From 28a2678310a265b45fb2b25d17a190a07733e75f Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Mon, 15 Jun 2026 12:54:16 +0300 Subject: [PATCH 01/16] =?UTF-8?q?docs:=20link=200.4.4=20milestone=20issues?= =?UTF-8?q?=20#170=E2=80=93#176=20in=20planning=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/motion-and-high-refresh.md | 14 ++++++++------ docs/planned-0.4.4.md | 18 +++++++++--------- docs/roadmap.md | 2 +- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/docs/motion-and-high-refresh.md b/docs/motion-and-high-refresh.md index 09ddb00c..4897f02a 100644 --- a/docs/motion-and-high-refresh.md +++ b/docs/motion-and-high-refresh.md @@ -114,12 +114,14 @@ Introduce `lib/core/motion/` with a single source of truth for durations and cur Milestone **0.4.4** (see [planned-0.4.4.md](planned-0.4.4.md)). -1. **UI-A1 — Motion tokens core.** `lib/core/motion/` with `QueryaMotion` durations/curves + `context.motion()` reduced-motion helper. Unit tests. -2. **UI-A2 — Adopt tokens across widgets.** Replace magic `Duration(...)`/`Curves.easeOut` literals in dialogs, dropdowns, tree, workspace panel, connection forms. No behavior regressions in layout tests. -3. **UI-A3 — Smoother transitions.** Dialog retune, dropdown enter animation, tree `AnimatedSize` expand/collapse, tab `AnimatedSwitcher`. -4. **UI-A4 — High-refresh-rate enablement.** Add `refresh_rate` (or wrapper); unlock on macOS 14+ in `main()`; query active Hz; debug-only FPS/Hz overlay behind a flag. -5. **UI-A5 — Reduced-motion + Preferences.** Honor OS "reduce motion"; add **Preferences → Appearance → Motion** (Full / Reduced / Off) wired to the motion helper. -6. **UI-A6 — Measurement & docs.** DevTools timeline checklist in [perf-baseline.md](perf-baseline.md); per-OS Hz verification table; update this doc with measured results. +Milestone **0.4.4** ([milestone](https://github.com/QueryaHub/Querya-Desktop/milestone/3), epic **#170**) — see [planned-0.4.4.md](planned-0.4.4.md). + +1. **UI-A1 — Motion tokens core** ([#173](https://github.com/QueryaHub/Querya-Desktop/issues/173)). `lib/core/motion/` with `QueryaMotion` durations/curves + `context.motion()` reduced-motion helper. Unit tests. +2. **UI-A2 — Adopt tokens across widgets** ([#171](https://github.com/QueryaHub/Querya-Desktop/issues/171)). Replace magic `Duration(...)`/`Curves.easeOut` literals in dialogs, dropdowns, tree, workspace panel, connection forms. No behavior regressions in layout tests. +3. **UI-A3 — Smoother transitions** ([#172](https://github.com/QueryaHub/Querya-Desktop/issues/172)). Dialog retune, dropdown enter animation, tree `AnimatedSize` expand/collapse, tab `AnimatedSwitcher`. +4. **UI-A4 — High-refresh-rate enablement** ([#174](https://github.com/QueryaHub/Querya-Desktop/issues/174)). Add `refresh_rate` (or wrapper); unlock on macOS 14+ in `main()`; query active Hz; debug-only FPS/Hz overlay behind a flag. +5. **UI-A5 — Reduced-motion + Preferences** ([#175](https://github.com/QueryaHub/Querya-Desktop/issues/175)). Honor OS "reduce motion"; add **Preferences → Appearance → Motion** (Full / Reduced / Off) wired to the motion helper. +6. **UI-A6 — Measurement & docs** ([#176](https://github.com/QueryaHub/Querya-Desktop/issues/176)). DevTools timeline checklist in [perf-baseline.md](perf-baseline.md); per-OS Hz verification table; update this doc with measured results. Suggested order: A1 → A2 → A3 in parallel with A4; then A5; A6 closes the milestone. diff --git a/docs/planned-0.4.4.md b/docs/planned-0.4.4.md index ebd9c3ba..8487fca2 100644 --- a/docs/planned-0.4.4.md +++ b/docs/planned-0.4.4.md @@ -1,6 +1,6 @@ # Planned release 0.4.4 — UI motion polish and high refresh rate -**Status:** planning. +**Status:** planning (GitHub milestone [**0.4.4**](https://github.com/QueryaHub/Querya-Desktop/milestone/3), epic **#170**). **Depends on:** **0.4.3** theme follow-ups (shipped). **Design doc:** [motion-and-high-refresh.md](motion-and-high-refresh.md) — research, current-state audit, and per-platform Hz behavior. @@ -13,14 +13,14 @@ Theme: make every animation **smooth and pleasant** through one motion system, a ## Scope -| ID | Scope | Summary | -|----|-------|---------| -| **UI-A1** | `motion`, `core` | **Motion tokens** — `lib/core/motion/` with `QueryaMotion` durations/curves and a `context.motion()` reduced-motion helper. | -| **UI-A2** | `motion`, `ui` | **Adopt tokens** — replace magic durations/curves in dialogs, dropdowns, tree, workspace panel, connection forms (no layout regressions). | -| **UI-A3** | `motion`, `ui` | **Smoother transitions** — dialog retune, dropdown enter animation, `AnimatedSize` tree expand/collapse, `AnimatedSwitcher` tab/content cross-fade. | -| **UI-A4** | `performance`, `platform` | **High refresh rate** — `refresh_rate` (or wrapper): unlock on macOS 14+ in `main()`, query active Hz, debug-only FPS/Hz overlay. Verify Windows/Linux follow the monitor. | -| **UI-A5** | `accessibility`, `settings` | **Reduced motion** — honor OS "reduce motion"; **Preferences → Appearance → Motion** (Full / Reduced / Off). | -| **UI-A6** | `docs`, `performance` | **Measurement & docs** — DevTools budget checklist (8.3 ms @120 Hz), per-OS Hz table, update design doc with measured results. | +| ID | Issue | Scope | Summary | +|----|-------|--------|---------| +| **UI-A1** | [#173](https://github.com/QueryaHub/Querya-Desktop/issues/173) | `motion`, `core` | **Motion tokens** — `lib/core/motion/` with `QueryaMotion` durations/curves and a `context.motion()` reduced-motion helper. | +| **UI-A2** | [#171](https://github.com/QueryaHub/Querya-Desktop/issues/171) | `motion`, `ui` | **Adopt tokens** — replace magic durations/curves in dialogs, dropdowns, tree, workspace panel, connection forms (no layout regressions). | +| **UI-A3** | [#172](https://github.com/QueryaHub/Querya-Desktop/issues/172) | `motion`, `ui` | **Smoother transitions** — dialog retune, dropdown enter animation, `AnimatedSize` tree expand/collapse, `AnimatedSwitcher` tab/content cross-fade. | +| **UI-A4** | [#174](https://github.com/QueryaHub/Querya-Desktop/issues/174) | `performance`, `platform` | **High refresh rate** — `refresh_rate` (or wrapper): unlock on macOS 14+ in `main()`, query active Hz, debug-only FPS/Hz overlay. Verify Windows/Linux follow the monitor. | +| **UI-A5** | [#175](https://github.com/QueryaHub/Querya-Desktop/issues/175) | `accessibility`, `settings` | **Reduced motion** — honor OS "reduce motion"; **Preferences → Appearance → Motion** (Full / Reduced / Off). | +| **UI-A6** | [#176](https://github.com/QueryaHub/Querya-Desktop/issues/176) | `docs`, `performance` | **Measurement & docs** — DevTools budget checklist (8.3 ms @120 Hz), per-OS Hz table, update design doc with measured results. | ## Suggested PR order diff --git a/docs/roadmap.md b/docs/roadmap.md index 9107896e..08272aff 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -9,7 +9,7 @@ Living document for planned work. Not a commitment order; adjust as priorities c - **Shipped in 0.4.1 ([#93](https://github.com/QueryaHub/Querya-Desktop/issues/93)):** UI performance — virtual result grid, lazy connection tree, decoupled scale preview, stats polling, MySQL stats dashboard, local `docker/` dev stack — [perf-baseline.md](perf-baseline.md). - **Shipped in 0.4.2 (TP-01–TP-30, #96–#125):** custom theme registry — `querya.theme.v1` + VS Code JSON/JSONC scan, Theme picker (50+), import/refresh, built-in Cyberpunk Neon asset, startup fallback, window chrome sync — [theme-custom-json.md](theme-custom-json.md), [theme-import.md](theme-import.md). - **Shipped in 0.4.3 (TP-F1–TP-F4, #159–#163):** theme folder watcher, marketplace metadata on manifests, visual theme editor with export, HTTPS remote install with checksum — [planned-0.4.3.md](planned-0.4.3.md). -- **Planned 0.4.4:** UI motion polish + high refresh rate (90/120/144 Hz) — [planned-0.4.4.md](planned-0.4.4.md), [motion-and-high-refresh.md](motion-and-high-refresh.md). +- **Planned 0.4.4:** UI motion polish + high refresh rate (90/120/144 Hz) — [planned-0.4.4.md](planned-0.4.4.md), [motion-and-high-refresh.md](motion-and-high-refresh.md), epic [#170](https://github.com/QueryaHub/Querya-Desktop/issues/170), milestone [0.4.4](https://github.com/QueryaHub/Querya-Desktop/milestone/3). - **Planned 0.4.5+:** Extensions sidebar and marketplace Explore UI — [market-tech.md](market-tech.md). - **Optional:** Preferences → **Animate theme changes** (off by default). - **Later:** P2 Mongo/Redis token colors; `re_editor` if perf gap; LSP epic per From 980f36c34898d40f8a8dc8e3b40802451b13ab16 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Mon, 15 Jun 2026 13:00:01 +0300 Subject: [PATCH 02/16] feat(motion): add QueryaMotion tokens and reduced-motion helpers Introduce lib/core/motion/ with duration/curve tokens, QueryaMotionScope for future Preferences wiring, and context helpers that honor OS disableAnimations. Closes #173 --- lib/core/motion/querya_motion.dart | 63 +++++++ lib/core/motion/querya_motion_context.dart | 12 ++ lib/core/motion/querya_motion_scope.dart | 37 ++++ test/core/motion/querya_motion_test.dart | 194 +++++++++++++++++++++ 4 files changed, 306 insertions(+) create mode 100644 lib/core/motion/querya_motion.dart create mode 100644 lib/core/motion/querya_motion_context.dart create mode 100644 lib/core/motion/querya_motion_scope.dart create mode 100644 test/core/motion/querya_motion_test.dart diff --git a/lib/core/motion/querya_motion.dart b/lib/core/motion/querya_motion.dart new file mode 100644 index 00000000..d7064e3c --- /dev/null +++ b/lib/core/motion/querya_motion.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; + +import 'querya_motion_scope.dart'; + +/// Shared animation durations and curves for Querya Desktop. +/// +/// Widgets should use [effectiveDuration] / [effectiveCurve] (or the +/// [BuildContext] helpers in [querya_motion_context.dart]) so OS and in-app +/// reduced-motion settings apply consistently. +abstract final class QueryaMotion { + /// No animation — used when motion is disabled. + static const Duration instant = Duration.zero; + + /// Hover, small state changes. + static const Duration fast = Duration(milliseconds: 120); + + /// Dialogs, menus, expand/collapse. + static const Duration standard = Duration(milliseconds: 200); + + /// Emphasized transitions (theme cross-fade, large surfaces). + static const Duration slow = Duration(milliseconds: 320); + + /// Elements appearing (decelerate). + static const Curve enter = Curves.easeOutCubic; + + /// Elements leaving (accelerate). + static const Curve exit = Curves.easeInCubic; + + /// Move or resize in place. + static const Curve standardCurve = Curves.easeInOutCubic; + + /// Hero / theme transitions. + static const Curve emphasized = Curves.easeInOutCubicEmphasized; + + /// Returns [token] adjusted for accessibility and [QueryaMotionScope] level. + static Duration effectiveDuration(BuildContext context, Duration token) { + if (token == instant) return instant; + if (MediaQuery.disableAnimationsOf(context)) return instant; + + final level = QueryaMotionScope.maybeOf(context); + switch (level) { + case QueryaMotionLevel.off: + return instant; + case QueryaMotionLevel.reduced: + final halved = Duration( + microseconds: token.inMicroseconds ~/ 2, + ); + return halved == instant ? fast : halved; + case QueryaMotionLevel.full: + case null: + return token; + } + } + + /// Returns [token] unless motion is fully disabled (then [Curves.linear]). + static Curve effectiveCurve(BuildContext context, Curve token) { + if (MediaQuery.disableAnimationsOf(context)) return Curves.linear; + if (QueryaMotionScope.maybeOf(context) == QueryaMotionLevel.off) { + return Curves.linear; + } + return token; + } +} diff --git a/lib/core/motion/querya_motion_context.dart b/lib/core/motion/querya_motion_context.dart new file mode 100644 index 00000000..49e1da63 --- /dev/null +++ b/lib/core/motion/querya_motion_context.dart @@ -0,0 +1,12 @@ +import 'package:flutter/material.dart'; + +import 'querya_motion.dart'; + +extension QueryaMotionContext on BuildContext { + /// Token duration after OS / in-app reduced-motion rules. + Duration motionDuration(Duration token) => + QueryaMotion.effectiveDuration(this, token); + + /// Token curve after OS / in-app reduced-motion rules. + Curve motionCurve(Curve token) => QueryaMotion.effectiveCurve(this, token); +} diff --git a/lib/core/motion/querya_motion_scope.dart b/lib/core/motion/querya_motion_scope.dart new file mode 100644 index 00000000..54991d73 --- /dev/null +++ b/lib/core/motion/querya_motion_scope.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; + +/// In-app motion intensity. Wired to Preferences in UI-A5 (#175); defaults to +/// [full] until then. +enum QueryaMotionLevel { + full, + reduced, + off, +} + +/// Provides [QueryaMotionLevel] for [QueryaMotion.effectiveDuration]. +/// +/// Place near the app root when the Preferences toggle lands; optional until +/// then — missing scope means [QueryaMotionLevel.full]. +class QueryaMotionScope extends InheritedWidget { + const QueryaMotionScope({ + super.key, + required this.level, + required super.child, + }); + + final QueryaMotionLevel level; + + static QueryaMotionLevel? maybeOf(BuildContext context) { + return context + .dependOnInheritedWidgetOfExactType() + ?.level; + } + + static QueryaMotionLevel of(BuildContext context) { + return maybeOf(context) ?? QueryaMotionLevel.full; + } + + @override + bool updateShouldNotify(QueryaMotionScope oldWidget) => + level != oldWidget.level; +} diff --git a/test/core/motion/querya_motion_test.dart b/test/core/motion/querya_motion_test.dart new file mode 100644 index 00000000..a7d91d0c --- /dev/null +++ b/test/core/motion/querya_motion_test.dart @@ -0,0 +1,194 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:querya_desktop/core/motion/querya_motion.dart'; +import 'package:querya_desktop/core/motion/querya_motion_context.dart'; +import 'package:querya_desktop/core/motion/querya_motion_scope.dart'; + +void main() { + group('QueryaMotion tokens', () { + test('duration constants match design doc', () { + expect(QueryaMotion.instant, Duration.zero); + expect(QueryaMotion.fast, const Duration(milliseconds: 120)); + expect(QueryaMotion.standard, const Duration(milliseconds: 200)); + expect(QueryaMotion.slow, const Duration(milliseconds: 320)); + }); + + test('curve constants are set', () { + expect(QueryaMotion.enter, Curves.easeOutCubic); + expect(QueryaMotion.exit, Curves.easeInCubic); + expect(QueryaMotion.standardCurve, Curves.easeInOutCubic); + expect(QueryaMotion.emphasized, Curves.easeInOutCubicEmphasized); + }); + }); + + group('effectiveDuration', () { + testWidgets('returns token when motion is full', (tester) async { + late Duration result; + + await tester.pumpWidget( + _MotionProbe( + disableAnimations: false, + level: QueryaMotionLevel.full, + onDuration: (d) => result = d, + ), + ); + + expect(result, QueryaMotion.standard); + }); + + testWidgets('returns instant when OS disableAnimations is true', ( + tester, + ) async { + late Duration result; + + await tester.pumpWidget( + _MotionProbe( + disableAnimations: true, + level: QueryaMotionLevel.full, + onDuration: (d) => result = d, + ), + ); + + expect(result, QueryaMotion.instant); + }); + + testWidgets('returns instant when motion level is off', (tester) async { + late Duration result; + + await tester.pumpWidget( + _MotionProbe( + disableAnimations: false, + level: QueryaMotionLevel.off, + onDuration: (d) => result = d, + ), + ); + + expect(result, QueryaMotion.instant); + }); + + testWidgets('halves duration when motion level is reduced', (tester) async { + late Duration result; + + await tester.pumpWidget( + _MotionProbe( + disableAnimations: false, + level: QueryaMotionLevel.reduced, + onDuration: (d) => result = d, + ), + ); + + expect(result, const Duration(milliseconds: 100)); + }); + + testWidgets('defaults to full when QueryaMotionScope is absent', ( + tester, + ) async { + late Duration result; + + await tester.pumpWidget( + MaterialApp( + home: Builder( + builder: (context) { + result = context.motionDuration(QueryaMotion.fast); + return const SizedBox.shrink(); + }, + ), + ), + ); + + expect(result, QueryaMotion.fast); + }); + }); + + group('effectiveCurve', () { + testWidgets('returns linear when animations disabled', (tester) async { + late Curve result; + + await tester.pumpWidget( + _MotionCurveProbe( + disableAnimations: true, + level: QueryaMotionLevel.full, + onCurve: (c) => result = c, + ), + ); + + expect(result, Curves.linear); + }); + + testWidgets('returns token curve when motion is full', (tester) async { + late Curve result; + + await tester.pumpWidget( + _MotionCurveProbe( + disableAnimations: false, + level: QueryaMotionLevel.full, + onCurve: (c) => result = c, + ), + ); + + expect(result, QueryaMotion.enter); + }); + }); +} + +class _MotionProbe extends StatelessWidget { + const _MotionProbe({ + required this.disableAnimations, + required this.level, + required this.onDuration, + }); + + final bool disableAnimations; + final QueryaMotionLevel level; + final ValueChanged onDuration; + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: MediaQuery( + data: MediaQueryData(disableAnimations: disableAnimations), + child: QueryaMotionScope( + level: level, + child: Builder( + builder: (context) { + onDuration( + QueryaMotion.effectiveDuration(context, QueryaMotion.standard), + ); + return const SizedBox.shrink(); + }, + ), + ), + ), + ); + } +} + +class _MotionCurveProbe extends StatelessWidget { + const _MotionCurveProbe({ + required this.disableAnimations, + required this.level, + required this.onCurve, + }); + + final bool disableAnimations; + final QueryaMotionLevel level; + final ValueChanged onCurve; + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: MediaQuery( + data: MediaQueryData(disableAnimations: disableAnimations), + child: QueryaMotionScope( + level: level, + child: Builder( + builder: (context) { + onCurve(QueryaMotion.effectiveCurve(context, QueryaMotion.enter)); + return const SizedBox.shrink(); + }, + ), + ), + ), + ); + } +} From fce7b59218a5bc789aedfde40e766bbdae693668 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Mon, 15 Jun 2026 13:05:09 +0300 Subject: [PATCH 03/16] refactor(motion): adopt QueryaMotion tokens across UI widgets Replace magic animation durations and easeOut curves in dialogs, dropdowns, theme picker, workspace panel, connection tree, and workspace home cards with context.motionDuration/motionCurve. Closes #171 --- lib/features/connections/connections_panel.dart | 2 ++ .../connections/connections_panel_mongo.dart | 3 ++- .../connections/connections_panel_mysql.dart | 6 ++++-- .../connections/connections_panel_pg_tree.dart | 15 ++++++++++----- .../connections_panel_postgres_connection.dart | 3 ++- .../connections/connections_panel_redis.dart | 3 ++- .../connections/connections_panel_sidebar.dart | 3 ++- .../connections/new_connection_dialog.dart | 6 ++++-- lib/features/main_screen/workspace_panel.dart | 12 +++++++----- lib/features/mysql/mysql_workspace_home.dart | 5 ++++- .../postgresql/postgres_workspace_home.dart | 5 ++++- lib/features/settings/theme_picker_button.dart | 14 ++++++-------- lib/shared/widgets/app_dialog.dart | 12 +++++++----- lib/shared/widgets/querya_dropdown.dart | 14 ++++++-------- lib/shared/widgets/querya_dropdown_tokens.dart | 2 -- 15 files changed, 62 insertions(+), 43 deletions(-) diff --git a/lib/features/connections/connections_panel.dart b/lib/features/connections/connections_panel.dart index 5cc49806..0e8d01d9 100644 --- a/lib/features/connections/connections_panel.dart +++ b/lib/features/connections/connections_panel.dart @@ -8,6 +8,8 @@ import 'package:querya_desktop/core/database/redis_info.dart'; import 'package:querya_desktop/core/storage/folders_storage.dart'; import 'package:querya_desktop/core/storage/local_db.dart'; import 'package:querya_desktop/core/theme/querya_typography.dart'; +import 'package:querya_desktop/core/motion/querya_motion.dart'; +import 'package:querya_desktop/core/motion/querya_motion_context.dart'; import 'package:querya_desktop/features/connections/connection_creation_flow.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; diff --git a/lib/features/connections/connections_panel_mongo.dart b/lib/features/connections/connections_panel_mongo.dart index 2e5a4228..c512fe2c 100644 --- a/lib/features/connections/connections_panel_mongo.dart +++ b/lib/features/connections/connections_panel_mongo.dart @@ -169,7 +169,8 @@ class _MongoConnectionTileState extends State<_MongoConnectionTile> { padding: const material.EdgeInsets.all(2), child: material.AnimatedRotation( turns: _expanded ? 0.25 : 0, - duration: const Duration(milliseconds: 100), + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.standardCurve), child: material.Icon( material.Icons.chevron_right_rounded, size: 16, diff --git a/lib/features/connections/connections_panel_mysql.dart b/lib/features/connections/connections_panel_mysql.dart index f6045237..a56fc202 100644 --- a/lib/features/connections/connections_panel_mysql.dart +++ b/lib/features/connections/connections_panel_mysql.dart @@ -135,7 +135,8 @@ class _MysqlConnectionTileState extends State<_MysqlConnectionTile> { padding: const material.EdgeInsets.all(2), child: material.AnimatedRotation( turns: _expanded ? 0.25 : 0, - duration: const Duration(milliseconds: 100), + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.standardCurve), child: material.Icon( material.Icons.chevron_right_rounded, size: 16, @@ -387,7 +388,8 @@ class _MysqlDatabaseNodeState extends State<_MysqlDatabaseNode> { label: widget.databaseName, leading: material.AnimatedRotation( turns: _expanded ? 0.25 : 0, - duration: const Duration(milliseconds: 100), + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.standardCurve), child: material.Icon( material.Icons.chevron_right_rounded, size: 14, diff --git a/lib/features/connections/connections_panel_pg_tree.dart b/lib/features/connections/connections_panel_pg_tree.dart index d79fe824..6c0a7804 100644 --- a/lib/features/connections/connections_panel_pg_tree.dart +++ b/lib/features/connections/connections_panel_pg_tree.dart @@ -213,7 +213,8 @@ class _PgDatabasesNodeState extends State<_PgDatabasesNode> { label: 'Databases (${widget.databases.length})', leading: material.AnimatedRotation( turns: _expanded ? 0.25 : 0, - duration: const Duration(milliseconds: 100), + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.standardCurve), child: material.Icon( material.Icons.chevron_right_rounded, size: 14, @@ -328,7 +329,8 @@ class _PgDatabaseNodeState extends State<_PgDatabaseNode> { label: widget.databaseName, leading: material.AnimatedRotation( turns: _expanded ? 0.25 : 0, - duration: const Duration(milliseconds: 100), + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.standardCurve), child: material.Icon( material.Icons.chevron_right_rounded, size: 14, @@ -509,7 +511,8 @@ class _PgSchemasNodeState extends State<_PgSchemasNode> { label: 'Schemas (${widget.schemas.length})', leading: material.AnimatedRotation( turns: _expanded ? 0.25 : 0, - duration: const Duration(milliseconds: 100), + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.standardCurve), child: material.Icon( material.Icons.chevron_right_rounded, size: 14, @@ -649,7 +652,8 @@ class _PgSchemaNodeState extends State<_PgSchemaNode> { label: widget.schemaName, leading: material.AnimatedRotation( turns: _expanded ? 0.25 : 0, - duration: const Duration(milliseconds: 100), + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.standardCurve), child: material.Icon( material.Icons.chevron_right_rounded, size: 14, @@ -939,7 +943,8 @@ class _PgObjectGroupState extends State<_PgObjectGroup> { label: '${widget.label} (${widget.items.length})', leading: material.AnimatedRotation( turns: _expanded ? 0.25 : 0, - duration: const Duration(milliseconds: 100), + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.standardCurve), child: material.Icon( material.Icons.chevron_right_rounded, size: 13, diff --git a/lib/features/connections/connections_panel_postgres_connection.dart b/lib/features/connections/connections_panel_postgres_connection.dart index aace6240..a3914b84 100644 --- a/lib/features/connections/connections_panel_postgres_connection.dart +++ b/lib/features/connections/connections_panel_postgres_connection.dart @@ -131,7 +131,8 @@ class _PostgresConnectionTileState extends State<_PostgresConnectionTile> { padding: const material.EdgeInsets.all(2), child: material.AnimatedRotation( turns: _expanded ? 0.25 : 0, - duration: const Duration(milliseconds: 100), + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.standardCurve), child: material.Icon( material.Icons.chevron_right_rounded, size: 16, diff --git a/lib/features/connections/connections_panel_redis.dart b/lib/features/connections/connections_panel_redis.dart index a70ab123..7e3fda45 100644 --- a/lib/features/connections/connections_panel_redis.dart +++ b/lib/features/connections/connections_panel_redis.dart @@ -148,7 +148,8 @@ class _RedisConnectionTileState extends State<_RedisConnectionTile> { padding: const material.EdgeInsets.all(2), child: material.AnimatedRotation( turns: _expanded ? 0.25 : 0, - duration: const Duration(milliseconds: 100), + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.standardCurve), child: material.Icon( material.Icons.chevron_right_rounded, size: 16, diff --git a/lib/features/connections/connections_panel_sidebar.dart b/lib/features/connections/connections_panel_sidebar.dart index f1994158..c56c4537 100644 --- a/lib/features/connections/connections_panel_sidebar.dart +++ b/lib/features/connections/connections_panel_sidebar.dart @@ -245,7 +245,8 @@ class _FolderTileState extends State<_FolderTile> { children: [ material.AnimatedRotation( turns: _expanded ? 0.25 : 0, - duration: const Duration(milliseconds: 100), + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.standardCurve), child: material.Icon( material.Icons.chevron_right_rounded, size: 18, diff --git a/lib/features/connections/new_connection_dialog.dart b/lib/features/connections/new_connection_dialog.dart index 9cf93f7e..1397c669 100644 --- a/lib/features/connections/new_connection_dialog.dart +++ b/lib/features/connections/new_connection_dialog.dart @@ -2,6 +2,8 @@ import 'dart:math' as math; import 'package:flutter/material.dart' as material; import 'package:querya_desktop/core/layout/window_layout.dart'; +import 'package:querya_desktop/core/motion/querya_motion.dart'; +import 'package:querya_desktop/core/motion/querya_motion_context.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; /// Database type for new connection. @@ -383,8 +385,8 @@ class _DbTypeCardState extends material.State<_DbTypeCard> { child: material.GestureDetector( onTap: widget.onTap, child: material.AnimatedContainer( - duration: const Duration(milliseconds: 120), - curve: material.Curves.easeOut, + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.enter), padding: const material.EdgeInsets.symmetric(vertical: 10, horizontal: 8), decoration: material.BoxDecoration( color: highlighted ? t.muted.withValues(alpha: 0.4) : t.muted.withValues(alpha: 0.12), diff --git a/lib/features/main_screen/workspace_panel.dart b/lib/features/main_screen/workspace_panel.dart index b61720cd..9af0f1cf 100644 --- a/lib/features/main_screen/workspace_panel.dart +++ b/lib/features/main_screen/workspace_panel.dart @@ -1,5 +1,7 @@ -import 'package:flutter/material.dart' as material show Alignment, Axis, Container, EdgeInsets, BoxDecoration, GestureDetector, Padding, BorderRadius, Center, Icon, Icons, MouseRegion, AnimatedContainer, AnimatedScale, Curves, SystemMouseCursors, SizedBox, SingleChildScrollView, Row, MainAxisSize; +import 'package:flutter/material.dart' as material show Alignment, Axis, Container, EdgeInsets, BoxDecoration, GestureDetector, Padding, BorderRadius, Center, Icon, Icons, MouseRegion, AnimatedContainer, AnimatedScale, SystemMouseCursors, SizedBox, SingleChildScrollView, Row, MainAxisSize; import 'package:querya_desktop/core/layout/vertical_split_pane.dart'; +import 'package:querya_desktop/core/motion/querya_motion.dart'; +import 'package:querya_desktop/core/motion/querya_motion_context.dart'; import 'package:querya_desktop/core/storage/local_db.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; @@ -341,8 +343,8 @@ class _TabButtonState extends State<_TabButton> { child: material.GestureDetector( onTap: widget.onTap, child: material.AnimatedContainer( - duration: const Duration(milliseconds: 120), - curve: material.Curves.easeOut, + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.enter), padding: const material.EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: material.BoxDecoration( color: bgColor, @@ -376,8 +378,8 @@ class _RunButtonState extends State<_RunButton> { cursor: material.SystemMouseCursors.click, child: material.AnimatedScale( scale: _hovered ? 1.03 : 1.0, - duration: const Duration(milliseconds: 100), - curve: material.Curves.easeOut, + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.enter), child: OutlineButton( onPressed: () {}, leading: const material.Icon(material.Icons.play_arrow, size: 18), diff --git a/lib/features/mysql/mysql_workspace_home.dart b/lib/features/mysql/mysql_workspace_home.dart index 94132a29..faa1ebe3 100644 --- a/lib/features/mysql/mysql_workspace_home.dart +++ b/lib/features/mysql/mysql_workspace_home.dart @@ -1,6 +1,8 @@ import 'dart:async' show unawaited; import 'package:flutter/material.dart' as material; +import 'package:querya_desktop/core/motion/querya_motion.dart'; +import 'package:querya_desktop/core/motion/querya_motion_context.dart'; import 'package:querya_desktop/core/storage/local_db.dart'; import 'package:querya_desktop/features/mysql/mysql_sql_workspace.dart'; import 'package:querya_desktop/features/mysql/mysql_stats_view.dart'; @@ -82,7 +84,8 @@ class _MysqlWorkspaceHomeState extends material.State { child: material.GestureDetector( onTap: () => unawaited(_selectTab(i)), child: material.AnimatedContainer( - duration: const Duration(milliseconds: 120), + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.enter), padding: const material.EdgeInsets.symmetric( horizontal: 12, vertical: 8, diff --git a/lib/features/postgresql/postgres_workspace_home.dart b/lib/features/postgresql/postgres_workspace_home.dart index 7a170bc8..eb62732e 100644 --- a/lib/features/postgresql/postgres_workspace_home.dart +++ b/lib/features/postgresql/postgres_workspace_home.dart @@ -1,6 +1,8 @@ import 'dart:async' show unawaited; import 'package:flutter/material.dart' as material; +import 'package:querya_desktop/core/motion/querya_motion.dart'; +import 'package:querya_desktop/core/motion/querya_motion_context.dart'; import 'package:querya_desktop/core/storage/local_db.dart'; import 'package:querya_desktop/features/postgresql/postgres_object_kind.dart'; import 'package:querya_desktop/features/postgresql/postgres_sql_workspace.dart'; @@ -123,7 +125,8 @@ class _PostgresWorkspaceHomeState extends material.State child: material.GestureDetector( onTap: () => _selectTab(i), child: material.AnimatedContainer( - duration: const Duration(milliseconds: 120), + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.enter), padding: const material.EdgeInsets.symmetric( horizontal: 12, vertical: 8, diff --git a/lib/features/settings/theme_picker_button.dart b/lib/features/settings/theme_picker_button.dart index 1e108cd6..5dfac989 100644 --- a/lib/features/settings/theme_picker_button.dart +++ b/lib/features/settings/theme_picker_button.dart @@ -2,6 +2,8 @@ import 'dart:async'; import 'package:flutter/material.dart' as material; import 'package:querya_desktop/core/layout/ui_scale.dart'; +import 'package:querya_desktop/core/motion/querya_motion.dart'; +import 'package:querya_desktop/core/motion/querya_motion_context.dart'; import 'package:querya_desktop/core/theme/querya_theme.dart'; import 'package:querya_desktop/core/theme/theme_definition.dart'; import 'package:querya_desktop/features/settings/theme_preview_card.dart'; @@ -379,10 +381,8 @@ class _ThemePickerButtonState extends material.State { onEnter: _enabled ? (_) => setState(() => _triggerHovered = true) : null, onExit: _enabled ? (_) => setState(() => _triggerHovered = false) : null, child: material.AnimatedContainer( - duration: const Duration( - milliseconds: QueryaDropdownTokens.hoverAnimationMs, - ), - curve: material.Curves.easeOut, + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.enter), height: triggerHeight, padding: QueryaDropdownTokens.scaledTriggerPadding(context), decoration: material.BoxDecoration( @@ -490,10 +490,8 @@ class _ThemePickerRowState extends material.State<_ThemePickerRow> { onTap: widget.onSelected, borderRadius: material.BorderRadius.circular(radius), child: material.AnimatedContainer( - duration: const Duration( - milliseconds: QueryaDropdownTokens.hoverAnimationMs, - ), - curve: material.Curves.easeOut, + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.enter), constraints: material.BoxConstraints(minHeight: itemHeight), padding: material.EdgeInsets.symmetric( horizontal: diff --git a/lib/shared/widgets/app_dialog.dart b/lib/shared/widgets/app_dialog.dart index a5e42ac5..36ff20e3 100644 --- a/lib/shared/widgets/app_dialog.dart +++ b/lib/shared/widgets/app_dialog.dart @@ -2,6 +2,9 @@ import 'dart:ui'; import 'package:flutter/material.dart'; +import 'package:querya_desktop/core/motion/querya_motion.dart'; +import 'package:querya_desktop/core/motion/querya_motion_context.dart'; + /// Shows a modal dialog with a frosted, dimmed backdrop over the app. /// /// Use instead of [showDialog] so every overlay has consistent blur. @@ -15,7 +18,7 @@ Future showAppDialog({ barrierDismissible: false, barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, barrierColor: Colors.transparent, - transitionDuration: const Duration(milliseconds: 200), + transitionDuration: context.motionDuration(QueryaMotion.standard), pageBuilder: (ctx, animation, secondaryAnimation) { return _BlurredDialogScaffold( barrierDismissible: barrierDismissible, @@ -44,12 +47,11 @@ class _BlurredDialogScaffold extends StatelessWidget { final Animation animation; final Widget child; - // Eased curve for dialog card fade-in / scale-up. - static final _curve = CurveTween(curve: Curves.easeOutCubic); - @override Widget build(BuildContext context) { - final curved = animation.drive(_curve); + final curved = animation.drive( + CurveTween(curve: context.motionCurve(QueryaMotion.enter)), + ); return Material( type: MaterialType.transparency, child: Stack( diff --git a/lib/shared/widgets/querya_dropdown.dart b/lib/shared/widgets/querya_dropdown.dart index cfcd5860..580640d4 100644 --- a/lib/shared/widgets/querya_dropdown.dart +++ b/lib/shared/widgets/querya_dropdown.dart @@ -1,6 +1,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart' as material; import 'package:querya_desktop/core/layout/ui_scale.dart'; +import 'package:querya_desktop/core/motion/querya_motion.dart'; +import 'package:querya_desktop/core/motion/querya_motion_context.dart'; import 'package:querya_desktop/shared/widgets/querya_dropdown_tokens.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart'; @@ -154,10 +156,8 @@ class _QueryaDropdownState extends material.State> { onEnter: widget.enabled ? (_) => setState(() => _triggerHovered = true) : null, onExit: widget.enabled ? (_) => setState(() => _triggerHovered = false) : null, child: material.AnimatedContainer( - duration: const Duration( - milliseconds: QueryaDropdownTokens.hoverAnimationMs, - ), - curve: material.Curves.easeOut, + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.enter), height: triggerHeight, padding: QueryaDropdownTokens.scaledTriggerPadding(context), decoration: material.BoxDecoration( @@ -350,10 +350,8 @@ class _QueryaDropdownMenuItemState extends material.State<_QueryaDropdownMenu ), onPressed: widget.enabled ? widget.onPick : null, child: material.AnimatedContainer( - duration: const Duration( - milliseconds: QueryaDropdownTokens.hoverAnimationMs, - ), - curve: material.Curves.easeOut, + duration: context.motionDuration(QueryaMotion.fast), + curve: context.motionCurve(QueryaMotion.enter), constraints: material.BoxConstraints(minHeight: itemHeight), padding: material.EdgeInsets.symmetric( horizontal: context.scaled(QueryaDropdownTokens.menuItemPadding.horizontal), diff --git a/lib/shared/widgets/querya_dropdown_tokens.dart b/lib/shared/widgets/querya_dropdown_tokens.dart index 367bbaaf..6a4a3721 100644 --- a/lib/shared/widgets/querya_dropdown_tokens.dart +++ b/lib/shared/widgets/querya_dropdown_tokens.dart @@ -40,8 +40,6 @@ abstract final class QueryaDropdownTokens { static const double selectedCheckSlotWidth = 18.0; - static const int hoverAnimationMs = 120; - static double scaledTriggerHeight(material.BuildContext context) => context.scaled(triggerHeight); From fbe8d1d9234eea40d7564b37ec97c1df188d1cff Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Mon, 15 Jun 2026 13:09:48 +0300 Subject: [PATCH 04/16] feat(motion): smoother transitions for menus, tree, and tabs Add QueryaAnimatedExpand and QueryaCrossFadeStack; dropdown menu enter fade+scale, connection tree AnimatedSize expand/collapse, and cross-fade tab bodies in workspace and SQL home views. Closes #172 --- lib/core/motion/querya_animated_expand.dart | 31 ++++++++++++++ lib/core/motion/querya_cross_fade_stack.dart | 41 ++++++++++++++++++ .../connections/connections_panel.dart | 1 + .../connections/connections_panel_mongo.dart | 9 +++- .../connections/connections_panel_mysql.dart | 20 +++++++-- .../connections_panel_pg_tree.dart | 40 +++++++++++++----- ...connections_panel_postgres_connection.dart | 9 +++- .../connections/connections_panel_redis.dart | 9 +++- .../connections_panel_sidebar.dart | 6 ++- lib/features/main_screen/workspace_panel.dart | 5 ++- lib/features/mysql/mysql_workspace_home.dart | 4 +- .../postgresql/postgres_workspace_home.dart | 4 +- lib/shared/widgets/querya_dropdown.dart | 42 ++++++++++++++++++- .../motion/querya_animated_expand_test.dart | 39 +++++++++++++++++ 14 files changed, 235 insertions(+), 25 deletions(-) create mode 100644 lib/core/motion/querya_animated_expand.dart create mode 100644 lib/core/motion/querya_cross_fade_stack.dart create mode 100644 test/core/motion/querya_animated_expand_test.dart diff --git a/lib/core/motion/querya_animated_expand.dart b/lib/core/motion/querya_animated_expand.dart new file mode 100644 index 00000000..5f837e0b --- /dev/null +++ b/lib/core/motion/querya_animated_expand.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +import 'querya_motion.dart'; +import 'querya_motion_context.dart'; + +/// Animates height when [expanded] toggles (connection tree sections, etc.). +class QueryaAnimatedExpand extends StatelessWidget { + const QueryaAnimatedExpand({ + super.key, + required this.expanded, + required this.child, + this.alignment = Alignment.topCenter, + }); + + final bool expanded; + final Widget child; + final Alignment alignment; + + @override + Widget build(BuildContext context) { + return AnimatedSize( + duration: context.motionDuration(QueryaMotion.standard), + curve: context.motionCurve(QueryaMotion.enter), + alignment: alignment, + clipBehavior: Clip.hardEdge, + child: expanded + ? child + : const SizedBox(width: double.infinity, height: 0), + ); + } +} diff --git a/lib/core/motion/querya_cross_fade_stack.dart b/lib/core/motion/querya_cross_fade_stack.dart new file mode 100644 index 00000000..54ec1407 --- /dev/null +++ b/lib/core/motion/querya_cross_fade_stack.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; + +import 'querya_motion.dart'; +import 'querya_motion_context.dart'; + +/// Like [IndexedStack] but cross-fades the active child; off-screen children +/// stay mounted (preserves SQL editor state, etc.). +class QueryaCrossFadeStack extends StatelessWidget { + const QueryaCrossFadeStack({ + super.key, + required this.index, + required this.children, + }); + + final int index; + final List children; + + @override + Widget build(BuildContext context) { + final duration = context.motionDuration(QueryaMotion.standard); + final curve = context.motionCurve(QueryaMotion.enter); + + return Stack( + fit: StackFit.expand, + children: [ + for (var i = 0; i < children.length; i++) + Positioned.fill( + child: IgnorePointer( + ignoring: index != i, + child: AnimatedOpacity( + opacity: index == i ? 1 : 0, + duration: duration, + curve: curve, + child: children[i], + ), + ), + ), + ], + ); + } +} diff --git a/lib/features/connections/connections_panel.dart b/lib/features/connections/connections_panel.dart index 0e8d01d9..fcaf578c 100644 --- a/lib/features/connections/connections_panel.dart +++ b/lib/features/connections/connections_panel.dart @@ -8,6 +8,7 @@ import 'package:querya_desktop/core/database/redis_info.dart'; import 'package:querya_desktop/core/storage/folders_storage.dart'; import 'package:querya_desktop/core/storage/local_db.dart'; import 'package:querya_desktop/core/theme/querya_typography.dart'; +import 'package:querya_desktop/core/motion/querya_animated_expand.dart'; import 'package:querya_desktop/core/motion/querya_motion.dart'; import 'package:querya_desktop/core/motion/querya_motion_context.dart'; import 'package:querya_desktop/features/connections/connection_creation_flow.dart'; diff --git a/lib/features/connections/connections_panel_mongo.dart b/lib/features/connections/connections_panel_mongo.dart index c512fe2c..ca0a9b46 100644 --- a/lib/features/connections/connections_panel_mongo.dart +++ b/lib/features/connections/connections_panel_mongo.dart @@ -229,7 +229,12 @@ class _MongoConnectionTileState extends State<_MongoConnectionTile> { ], ), // Expanded database children - if (_expanded) ...[ + QueryaAnimatedExpand( + expanded: _expanded, + child: material.Column( + mainAxisSize: material.MainAxisSize.min, + crossAxisAlignment: material.CrossAxisAlignment.stretch, + children: [ if (_loading) material.Padding( padding: const material.EdgeInsets.only(left: 28, top: 4, bottom: 4), @@ -301,6 +306,8 @@ class _MongoConnectionTileState extends State<_MongoConnectionTile> { }, ), ], + ), + ), ], ), ), diff --git a/lib/features/connections/connections_panel_mysql.dart b/lib/features/connections/connections_panel_mysql.dart index a56fc202..84b191d8 100644 --- a/lib/features/connections/connections_panel_mysql.dart +++ b/lib/features/connections/connections_panel_mysql.dart @@ -194,7 +194,12 @@ class _MysqlConnectionTileState extends State<_MysqlConnectionTile> { ), ], ), - if (_expanded) ...[ + QueryaAnimatedExpand( + expanded: _expanded, + child: material.Column( + mainAxisSize: material.MainAxisSize.min, + crossAxisAlignment: material.CrossAxisAlignment.stretch, + children: [ if (_loading) material.Padding( padding: @@ -236,6 +241,8 @@ class _MysqlConnectionTileState extends State<_MysqlConnectionTile> { onMysqlOpenSqlWorkspace: widget.onMysqlOpenSqlWorkspace, ), ], + ), + ), ], ), ), @@ -412,7 +419,12 @@ class _MysqlDatabaseNodeState extends State<_MysqlDatabaseNode> { : (c, {database, schema, name, kind}) => widget.onMysqlOpenSqlWorkspace!(c), ), - if (_expanded) ...[ + QueryaAnimatedExpand( + expanded: _expanded, + child: material.Column( + mainAxisSize: material.MainAxisSize.min, + crossAxisAlignment: material.CrossAxisAlignment.stretch, + children: [ if (_loading) material.Padding( padding: @@ -541,7 +553,9 @@ class _MysqlDatabaseNodeState extends State<_MysqlDatabaseNode> { ], ), ), - ], + ], + ), + ), ], ), ); diff --git a/lib/features/connections/connections_panel_pg_tree.dart b/lib/features/connections/connections_panel_pg_tree.dart index 6c0a7804..60859e26 100644 --- a/lib/features/connections/connections_panel_pg_tree.dart +++ b/lib/features/connections/connections_panel_pg_tree.dart @@ -234,8 +234,9 @@ class _PgDatabasesNodeState extends State<_PgDatabasesNode> { onContextRefresh: widget.onRefreshDatabases, onOpenSqlWorkspace: widget.onPostgresOpenSqlWorkspace, ), - if (_expanded) - lazyConnectionTreeList( + QueryaAnimatedExpand( + expanded: _expanded, + child: lazyConnectionTreeList( context: context, itemCount: widget.databases.length, itemBuilder: (context, index) { @@ -249,6 +250,7 @@ class _PgDatabasesNodeState extends State<_PgDatabasesNode> { ); }, ), + ), ], ), ); @@ -350,7 +352,12 @@ class _PgDatabaseNodeState extends State<_PgDatabaseNode> { onContextRefresh: _loadSchemas, onOpenSqlWorkspace: widget.onPostgresOpenSqlWorkspace, ), - if (_expanded) ...[ + QueryaAnimatedExpand( + expanded: _expanded, + child: material.Column( + mainAxisSize: material.MainAxisSize.min, + crossAxisAlignment: material.CrossAxisAlignment.stretch, + children: [ _PgDbToolRow( connection: widget.connection, databaseName: widget.databaseName, @@ -397,7 +404,9 @@ class _PgDatabaseNodeState extends State<_PgDatabaseNode> { onPostgresOpenSqlWorkspace: widget.onPostgresOpenSqlWorkspace, onRefreshSchemas: _loadSchemas, ), - ], + ], + ), + ), ], ), ); @@ -531,8 +540,9 @@ class _PgSchemasNodeState extends State<_PgSchemasNode> { onContextRefresh: widget.onRefreshSchemas, onOpenSqlWorkspace: widget.onPostgresOpenSqlWorkspace, ), - if (_expanded) - lazyConnectionTreeList( + QueryaAnimatedExpand( + expanded: _expanded, + child: lazyConnectionTreeList( context: context, itemCount: widget.schemas.length, itemBuilder: (context, index) { @@ -549,6 +559,7 @@ class _PgSchemasNodeState extends State<_PgSchemasNode> { ); }, ), + ), ], ), ); @@ -672,7 +683,12 @@ class _PgSchemaNodeState extends State<_PgSchemaNode> { onContextRefresh: _loadObjects, onOpenSqlWorkspace: widget.onPostgresOpenSqlWorkspace, ), - if (_expanded) ...[ + QueryaAnimatedExpand( + expanded: _expanded, + child: material.Column( + mainAxisSize: material.MainAxisSize.min, + crossAxisAlignment: material.CrossAxisAlignment.stretch, + children: [ if (_loading) material.Padding( padding: @@ -825,7 +841,9 @@ class _PgSchemaNodeState extends State<_PgSchemaNode> { onContextRefresh: _loadObjects, ), ], - ], + ], + ), + ), ], ), ); @@ -963,8 +981,9 @@ class _PgObjectGroupState extends State<_PgObjectGroup> { onContextRefresh: widget.onRefresh, onOpenSqlWorkspace: widget.onPostgresOpenSqlWorkspace, ), - if (_expanded) - lazyConnectionTreeList( + QueryaAnimatedExpand( + expanded: _expanded, + child: lazyConnectionTreeList( context: context, itemCount: widget.items.length, itemExtent: kConnectionTreeRowExtent, @@ -998,6 +1017,7 @@ class _PgObjectGroupState extends State<_PgObjectGroup> { ); }, ), + ), ], ), ); diff --git a/lib/features/connections/connections_panel_postgres_connection.dart b/lib/features/connections/connections_panel_postgres_connection.dart index a3914b84..4509361f 100644 --- a/lib/features/connections/connections_panel_postgres_connection.dart +++ b/lib/features/connections/connections_panel_postgres_connection.dart @@ -189,7 +189,12 @@ class _PostgresConnectionTileState extends State<_PostgresConnectionTile> { ), ], ), - if (_expanded) ...[ + QueryaAnimatedExpand( + expanded: _expanded, + child: material.Column( + mainAxisSize: material.MainAxisSize.min, + crossAxisAlignment: material.CrossAxisAlignment.stretch, + children: [ if (_loading) material.Padding( padding: @@ -231,6 +236,8 @@ class _PostgresConnectionTileState extends State<_PostgresConnectionTile> { }, ), ], + ), + ), ], ), ), diff --git a/lib/features/connections/connections_panel_redis.dart b/lib/features/connections/connections_panel_redis.dart index 7e3fda45..4245f5b6 100644 --- a/lib/features/connections/connections_panel_redis.dart +++ b/lib/features/connections/connections_panel_redis.dart @@ -208,7 +208,12 @@ class _RedisConnectionTileState extends State<_RedisConnectionTile> { ], ), // Expanded database children — ALL 16 databases - if (_expanded) ...[ + QueryaAnimatedExpand( + expanded: _expanded, + child: material.Column( + mainAxisSize: material.MainAxisSize.min, + crossAxisAlignment: material.CrossAxisAlignment.stretch, + children: [ if (_loading) material.Padding( padding: const material.EdgeInsets.only(left: 28, top: 4, bottom: 4), @@ -242,6 +247,8 @@ class _RedisConnectionTileState extends State<_RedisConnectionTile> { onTap: () => widget.onDatabaseTap?.call(db.index), ), ], + ), + ), ], ), ), diff --git a/lib/features/connections/connections_panel_sidebar.dart b/lib/features/connections/connections_panel_sidebar.dart index c56c4537..2e98ec37 100644 --- a/lib/features/connections/connections_panel_sidebar.dart +++ b/lib/features/connections/connections_panel_sidebar.dart @@ -272,8 +272,9 @@ class _FolderTileState extends State<_FolderTile> { ), ), ), - if (_expanded) - material.Padding( + QueryaAnimatedExpand( + expanded: _expanded, + child: material.Padding( padding: const material.EdgeInsets.only(left: 24), child: lazyConnectionTreeList( context: context, @@ -295,6 +296,7 @@ class _FolderTileState extends State<_FolderTile> { }, ), ), + ), ], ), ), diff --git a/lib/features/main_screen/workspace_panel.dart b/lib/features/main_screen/workspace_panel.dart index 9af0f1cf..1ec7e518 100644 --- a/lib/features/main_screen/workspace_panel.dart +++ b/lib/features/main_screen/workspace_panel.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart' as material show Alignment, Axis, Container, EdgeInsets, BoxDecoration, GestureDetector, Padding, BorderRadius, Center, Icon, Icons, MouseRegion, AnimatedContainer, AnimatedScale, SystemMouseCursors, SizedBox, SingleChildScrollView, Row, MainAxisSize; import 'package:querya_desktop/core/layout/vertical_split_pane.dart'; +import 'package:querya_desktop/core/motion/querya_cross_fade_stack.dart'; import 'package:querya_desktop/core/motion/querya_motion.dart'; import 'package:querya_desktop/core/motion/querya_motion_context.dart'; import 'package:querya_desktop/core/storage/local_db.dart'; @@ -212,7 +213,7 @@ class _WorkspacePanelState extends State { ), const Divider(height: 1), Expanded( - child: IndexedStack( + child: QueryaCrossFadeStack( index: _editorTabIndex, children: const [ QueryEditorTab(), @@ -233,7 +234,7 @@ class _WorkspacePanelState extends State { ), const Divider(height: 1), Expanded( - child: IndexedStack( + child: QueryaCrossFadeStack( index: _outputTabIndex, children: const [ ResultsTab(), diff --git a/lib/features/mysql/mysql_workspace_home.dart b/lib/features/mysql/mysql_workspace_home.dart index faa1ebe3..7a5860d4 100644 --- a/lib/features/mysql/mysql_workspace_home.dart +++ b/lib/features/mysql/mysql_workspace_home.dart @@ -1,6 +1,7 @@ import 'dart:async' show unawaited; import 'package:flutter/material.dart' as material; +import 'package:querya_desktop/core/motion/querya_cross_fade_stack.dart'; import 'package:querya_desktop/core/motion/querya_motion.dart'; import 'package:querya_desktop/core/motion/querya_motion_context.dart'; import 'package:querya_desktop/core/storage/local_db.dart'; @@ -109,9 +110,8 @@ class _MysqlWorkspaceHomeState extends material.State { ), const Divider(height: 1), Expanded( - child: material.IndexedStack( + child: QueryaCrossFadeStack( index: _tab, - sizing: material.StackFit.expand, children: [ MysqlStatsView( key: ValueKey('mysql_stats_${widget.connectionRow.id}'), diff --git a/lib/features/postgresql/postgres_workspace_home.dart b/lib/features/postgresql/postgres_workspace_home.dart index eb62732e..e80ee431 100644 --- a/lib/features/postgresql/postgres_workspace_home.dart +++ b/lib/features/postgresql/postgres_workspace_home.dart @@ -1,6 +1,7 @@ import 'dart:async' show unawaited; import 'package:flutter/material.dart' as material; +import 'package:querya_desktop/core/motion/querya_cross_fade_stack.dart'; import 'package:querya_desktop/core/motion/querya_motion.dart'; import 'package:querya_desktop/core/motion/querya_motion_context.dart'; import 'package:querya_desktop/core/storage/local_db.dart'; @@ -152,9 +153,8 @@ class _PostgresWorkspaceHomeState extends material.State ), const Divider(height: 1), Expanded( - child: material.IndexedStack( + child: QueryaCrossFadeStack( index: _tab, - sizing: material.StackFit.expand, children: [ PostgresStatsView( key: ValueKey('pg_stats_${widget.connectionRow.id}'), diff --git a/lib/shared/widgets/querya_dropdown.dart b/lib/shared/widgets/querya_dropdown.dart index 580640d4..1e6eb8c1 100644 --- a/lib/shared/widgets/querya_dropdown.dart +++ b/lib/shared/widgets/querya_dropdown.dart @@ -59,6 +59,7 @@ class QueryaDropdown extends material.StatefulWidget { class _QueryaDropdownState extends material.State> { late material.MenuController _controller; bool _triggerHovered = false; + bool _menuOpen = false; List? _cachedMenuChildren; List>? _cachedMenuItems; T? _cachedMenuValue; @@ -229,6 +230,8 @@ class _QueryaDropdownState extends material.State> { final anchor = material.MenuAnchor( controller: _controller, + onOpen: () => setState(() => _menuOpen = true), + onClose: () => setState(() => _menuOpen = false), crossAxisUnconstrained: false, alignmentOffset: material.Offset( widget.alignmentOffset.dx, @@ -257,7 +260,16 @@ class _QueryaDropdownState extends material.State> { ), ), ), - menuChildren: menuChildren, + menuChildren: [ + _QueryaDropdownMenuEnter( + open: _menuOpen, + child: material.Column( + mainAxisSize: material.MainAxisSize.min, + crossAxisAlignment: material.CrossAxisAlignment.stretch, + children: menuChildren, + ), + ), + ], builder: (context, controller, child) { final trigger = _buildTrigger( context: context, @@ -280,6 +292,34 @@ class _QueryaDropdownState extends material.State> { } } +class _QueryaDropdownMenuEnter extends material.StatelessWidget { + const _QueryaDropdownMenuEnter({ + required this.open, + required this.child, + }); + + final bool open; + final material.Widget child; + + @override + material.Widget build(material.BuildContext context) { + final duration = context.motionDuration(QueryaMotion.standard); + final curve = context.motionCurve(QueryaMotion.enter); + return material.AnimatedScale( + scale: open ? 1 : 0.96, + alignment: material.Alignment.topCenter, + duration: duration, + curve: curve, + child: material.AnimatedOpacity( + opacity: open ? 1 : 0, + duration: duration, + curve: curve, + child: child, + ), + ); + } +} + class _QueryaDropdownMenuItem extends material.StatefulWidget { const _QueryaDropdownMenuItem({ required this.item, diff --git a/test/core/motion/querya_animated_expand_test.dart b/test/core/motion/querya_animated_expand_test.dart new file mode 100644 index 00000000..c92a4045 --- /dev/null +++ b/test/core/motion/querya_animated_expand_test.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:querya_desktop/core/motion/querya_animated_expand.dart'; + +void main() { + testWidgets('QueryaAnimatedExpand hides child when collapsed', (tester) async { + await tester.pumpWidget( + const MaterialApp( + home: _ExpandHost(expanded: false), + ), + ); + + expect(find.text('child'), findsNothing); + }); + + testWidgets('QueryaAnimatedExpand shows child when expanded', (tester) async { + await tester.pumpWidget( + const MaterialApp( + home: _ExpandHost(expanded: true), + ), + ); + + expect(find.text('child'), findsOneWidget); + }); +} + +class _ExpandHost extends StatelessWidget { + const _ExpandHost({required this.expanded}); + + final bool expanded; + + @override + Widget build(BuildContext context) { + return QueryaAnimatedExpand( + expanded: expanded, + child: const Text('child'), + ); + } +} From f4d2f31be831c3a2c98f5fbdb3bf6cf9fa56e85f Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Mon, 15 Jun 2026 13:12:52 +0300 Subject: [PATCH 05/16] feat(motion): enable high refresh rate via refresh_rate Add DisplayRefreshService with RefreshRate.enable() at startup, debug Hz logging, and optional Hz overlay via QUERYA_REFRESH_OVERLAY. Closes #174 --- docs/motion-and-high-refresh.md | 1 + lib/core/motion/display_refresh_service.dart | 49 +++++++++++++++++++ lib/main.dart | 2 + pubspec.yaml | 1 + .../motion/display_refresh_service_test.dart | 33 +++++++++++++ 5 files changed, 86 insertions(+) create mode 100644 lib/core/motion/display_refresh_service.dart create mode 100644 test/core/motion/display_refresh_service_test.dart diff --git a/docs/motion-and-high-refresh.md b/docs/motion-and-high-refresh.md index 4897f02a..e3d7922f 100644 --- a/docs/motion-and-high-refresh.md +++ b/docs/motion-and-high-refresh.md @@ -67,6 +67,7 @@ Root cause (engine): per `flutter/flutter#160952`, the engine "can render at 120 - **Windows / Linux:** most likely already render at monitor Hz; the job is to **measure and verify**, then ensure no app-side code caps frames (e.g. heavy `setState`, unbounded rebuilds during animation). - **macOS:** the real high-Hz work — confirm ProMotion behavior; unlock via `refresh_rate` if capped at 60. - Use `refresh_rate` (or a thin wrapper) primarily for **diagnostics**: a debug-only FPS/Hz overlay and a benchmark to prove smoothness on each machine, plus the macOS unlock call in `main()`. +- **Implementation:** `lib/core/motion/display_refresh_service.dart` calls `RefreshRate.enable()` in `main()`. Debug Hz badge: `flutter run --dart-define=QUERYA_REFRESH_OVERLAY=true`. --- diff --git a/lib/core/motion/display_refresh_service.dart b/lib/core/motion/display_refresh_service.dart new file mode 100644 index 00000000..55b4e76e --- /dev/null +++ b/lib/core/motion/display_refresh_service.dart @@ -0,0 +1,49 @@ +import 'package:flutter/foundation.dart'; +import 'package:refresh_rate/refresh_rate.dart'; + +/// Desktop display refresh-rate unlock and diagnostics (UI-A4 / #174). +/// +/// Calls [RefreshRate.enable] once at startup (macOS 14+ ProMotion unlock; +/// query on Windows/Linux). Debug overlay: run with +/// `--dart-define=QUERYA_REFRESH_OVERLAY=true`. +abstract final class DisplayRefreshService { + static const bool _overlayFromEnvironment = bool.fromEnvironment( + 'QUERYA_REFRESH_OVERLAY', + ); + + /// Unlock peak refresh where the platform supports it; log Hz in debug builds. + static void initialize() { + RefreshRate.enable(); + + if (!kDebugMode) return; + + _logRefreshInfo(); + if (displayRefreshOverlayEnabled( + debugMode: kDebugMode, + overlayFlag: _overlayFromEnvironment, + )) { + RefreshRate.showHz(); + } + } + + static void _logRefreshInfo() { + final info = RefreshRate.info; + debugPrint( + 'Display refresh: ${info.currentRate} Hz ' + '(max ${info.maxRate}, variable=${info.isVariableRefreshRate})', + ); + } + + /// Cached current refresh rate from the platform (Hz), when available. + static double get currentHz => RefreshRate.info.currentRate; + + /// Peak supported refresh rate (Hz). + static double get maxHz => RefreshRate.info.maxRate; + + @visibleForTesting + static bool displayRefreshOverlayEnabled({ + required bool debugMode, + required bool overlayFlag, + }) => + debugMode && overlayFlag; +} diff --git a/lib/main.dart b/lib/main.dart index 887d9a07..d3ee6c6f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,11 +4,13 @@ import 'package:flutter/material.dart'; import 'app/app.dart'; import 'core/editor/syntax_highlight_service.dart'; import 'core/layout/ui_scale_controller.dart'; +import 'core/motion/display_refresh_service.dart'; import 'core/storage/local_db.dart'; import 'core/theme/theme_controller.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); + DisplayRefreshService.initialize(); await LocalDb.initFfi(); await SyntaxHighlightService.ensureInitialized(); await ThemeController.instance.load(); diff --git a/pubspec.yaml b/pubspec.yaml index 5cb98878..2006b743 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,6 +12,7 @@ dependencies: sdk: flutter http: ^1.2.2 crypto: ^3.0.6 + refresh_rate: ^1.0.2 shadcn_flutter: ^0.0.52 bitsdojo_window: ^0.1.6 path: ^1.9.0 diff --git a/test/core/motion/display_refresh_service_test.dart b/test/core/motion/display_refresh_service_test.dart new file mode 100644 index 00000000..f0f97422 --- /dev/null +++ b/test/core/motion/display_refresh_service_test.dart @@ -0,0 +1,33 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:querya_desktop/core/motion/display_refresh_service.dart'; + +void main() { + group('displayRefreshOverlayEnabled', () { + test('false in release or when flag unset', () { + expect( + DisplayRefreshService.displayRefreshOverlayEnabled( + debugMode: false, + overlayFlag: true, + ), + isFalse, + ); + expect( + DisplayRefreshService.displayRefreshOverlayEnabled( + debugMode: true, + overlayFlag: false, + ), + isFalse, + ); + }); + + test('true only in debug with QUERYA_REFRESH_OVERLAY', () { + expect( + DisplayRefreshService.displayRefreshOverlayEnabled( + debugMode: true, + overlayFlag: true, + ), + isTrue, + ); + }); + }); +} From db32374e77544eb7e6750e439805df5b5dff0a87 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Tue, 16 Jun 2026 15:02:52 +0300 Subject: [PATCH 06/16] feat(motion): implement reduced motion toggle and preferences settings (Closes #175) --- lib/app/app.dart | 62 ++++++++------- lib/core/motion/querya_motion_controller.dart | 24 ++++++ lib/core/storage/app_settings.dart | 21 ++++++ .../preferences_appearance_section.dart | 36 +++++++++ lib/main.dart | 2 + .../motion/querya_motion_controller_test.dart | 75 +++++++++++++++++++ 6 files changed, 195 insertions(+), 25 deletions(-) create mode 100644 lib/core/motion/querya_motion_controller.dart create mode 100644 test/core/motion/querya_motion_controller_test.dart diff --git a/lib/app/app.dart b/lib/app/app.dart index 75009613..214ab030 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -1,5 +1,7 @@ import 'package:querya_desktop/core/layout/ui_scale.dart'; import 'package:querya_desktop/core/layout/ui_scale_controller.dart'; +import 'package:querya_desktop/core/motion/querya_motion_controller.dart'; +import 'package:querya_desktop/core/motion/querya_motion_scope.dart'; import 'package:querya_desktop/core/theme/querya_theme_scope.dart'; import 'package:querya_desktop/core/theme/theme_controller.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart'; @@ -14,6 +16,7 @@ class QueryaApp extends StatelessWidget { Widget build(BuildContext context) { final themeController = ThemeController.instance; final uiScaleController = UiScaleController.instance; + final motionController = QueryaMotionController.instance; return ListenableBuilder( listenable: themeController, @@ -25,34 +28,43 @@ class QueryaApp extends StatelessWidget { listenable: uiScaleController, builder: (context, _) { final scale = uiScaleController.scale; - return ShadcnApp( - title: 'Querya', - theme: themeController.lightShadcnTheme, - darkTheme: themeController.darkShadcnTheme, - themeMode: themeController.themeMode, - materialTheme: themeController.materialThemeFor(colorScheme), - debugShowCheckedModeBanner: false, - enableThemeAnimation: themeController.themeAnimationEnabled, - enableScrollInterception: false, - // Above navigator so dialogs/overlays (SQL editor, Preferences) see tokens. - builder: (context, child) { - final mq = MediaQuery.maybeOf(context); - return QueryaUiScaleScope( - scale: scale, - child: MediaQuery( - data: (mq ?? const MediaQueryData()).copyWith( - textScaler: TextScaler.linear(scale), - ), - child: QueryaThemeScope( - data: queryaTheme, - child: child ?? const SizedBox.shrink(), - ), + return ListenableBuilder( + listenable: motionController, + builder: (context, _) { + final motionLevel = motionController.level; + return ShadcnApp( + title: 'Querya', + theme: themeController.lightShadcnTheme, + darkTheme: themeController.darkShadcnTheme, + themeMode: themeController.themeMode, + materialTheme: themeController.materialThemeFor(colorScheme), + debugShowCheckedModeBanner: false, + enableThemeAnimation: themeController.themeAnimationEnabled, + enableScrollInterception: false, + // Above navigator so dialogs/overlays (SQL editor, Preferences) see tokens. + builder: (context, child) { + final mq = MediaQuery.maybeOf(context); + return QueryaUiScaleScope( + scale: scale, + child: MediaQuery( + data: (mq ?? const MediaQueryData()).copyWith( + textScaler: TextScaler.linear(scale), + ), + child: QueryaThemeScope( + data: queryaTheme, + child: QueryaMotionScope( + level: motionLevel, + child: child ?? const SizedBox.shrink(), + ), + ), + ), + ); + }, + home: const AppLifecycleCleanup( + child: MainScreen(), ), ); }, - home: const AppLifecycleCleanup( - child: MainScreen(), - ), ); }, ); diff --git a/lib/core/motion/querya_motion_controller.dart b/lib/core/motion/querya_motion_controller.dart new file mode 100644 index 00000000..33dfdacc --- /dev/null +++ b/lib/core/motion/querya_motion_controller.dart @@ -0,0 +1,24 @@ +import 'package:flutter/foundation.dart'; +import 'package:querya_desktop/core/storage/app_settings.dart'; +import 'querya_motion_scope.dart'; + +/// Loads and broadcasts [AppSettings] motion level for the widget tree. +class QueryaMotionController extends ChangeNotifier { + QueryaMotionController._(); + static final QueryaMotionController instance = QueryaMotionController._(); + + QueryaMotionLevel _level = QueryaMotionLevel.full; + QueryaMotionLevel get level => _level; + + Future load() async { + _level = await AppSettings.instance.getMotionLevel(); + notifyListeners(); + } + + Future setLevel(QueryaMotionLevel value) async { + if (_level == value) return; + await AppSettings.instance.setMotionLevel(value); + _level = value; + notifyListeners(); + } +} diff --git a/lib/core/storage/app_settings.dart b/lib/core/storage/app_settings.dart index b62c1d74..05e783a1 100644 --- a/lib/core/storage/app_settings.dart +++ b/lib/core/storage/app_settings.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:shadcn_flutter/shadcn_flutter.dart'; +import '../motion/querya_motion_scope.dart'; import '../theme/querya_theme_preset.dart'; import 'local_db.dart'; @@ -111,6 +112,7 @@ abstract final class AppSettingsKeys { static const themeSelectedPath = 'theme_selected_path'; static const themeAnimationEnabled = 'theme_animation_enabled'; static const uiScale = 'ui_scale'; + static const motionLevel = 'motion_level'; } /// Bumps [listenable] when any preference is persisted (theme, legacy listeners). @@ -524,4 +526,23 @@ class AppSettings { await deleteThemeImportKeys(); AppSettingsRevision.bump(); } + + Future getMotionLevel() async { + final v = await LocalDb.instance.getAppSetting(AppSettingsKeys.motionLevel); + return switch (v) { + 'off' => QueryaMotionLevel.off, + 'reduced' => QueryaMotionLevel.reduced, + _ => QueryaMotionLevel.full, + }; + } + + Future setMotionLevel(QueryaMotionLevel level) async { + final stored = switch (level) { + QueryaMotionLevel.off => 'off', + QueryaMotionLevel.reduced => 'reduced', + QueryaMotionLevel.full => 'full', + }; + await LocalDb.instance.setAppSetting(AppSettingsKeys.motionLevel, stored); + AppSettingsRevision.bump(); + } } diff --git a/lib/features/settings/preferences_appearance_section.dart b/lib/features/settings/preferences_appearance_section.dart index 0daff22d..d0a62a6a 100644 --- a/lib/features/settings/preferences_appearance_section.dart +++ b/lib/features/settings/preferences_appearance_section.dart @@ -2,6 +2,8 @@ import 'dart:async' show unawaited; import 'package:file_selector/file_selector.dart'; import 'package:flutter/material.dart' as material; +import 'package:querya_desktop/core/motion/querya_motion_controller.dart'; +import 'package:querya_desktop/core/motion/querya_motion_scope.dart'; import 'package:querya_desktop/core/platform/open_directory.dart'; import 'package:querya_desktop/core/theme/theme_controller.dart'; import 'package:querya_desktop/core/theme/theme_import_service.dart'; @@ -257,6 +259,40 @@ class _PreferencesAppearanceSectionState 'Smooth transitions when switching dark/light or presets. Off by default for stability.', ), const material.SizedBox(height: 12), + PreferencesFieldRow( + label: 'Motion', + control: material.ListenableBuilder( + listenable: QueryaMotionController.instance, + builder: (context, _) { + final controller = QueryaMotionController.instance; + return PreferencesDropdownMenu( + value: controller.level, + onSelected: (v) { + if (v != null) unawaited(controller.setLevel(v)); + }, + entries: const [ + material.DropdownMenuEntry( + value: QueryaMotionLevel.full, + label: 'Full', + ), + material.DropdownMenuEntry( + value: QueryaMotionLevel.reduced, + label: 'Reduced', + ), + material.DropdownMenuEntry( + value: QueryaMotionLevel.off, + label: 'Off', + ), + ], + ); + }, + ), + ), + const material.SizedBox(height: 4), + const PreferencesHint( + 'Full enables all animations. Reduced cuts durations in half. Off disables all transitions.', + ), + const material.SizedBox(height: 12), material.Wrap( spacing: 8, runSpacing: 8, diff --git a/lib/main.dart b/lib/main.dart index d3ee6c6f..cbd63ec5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,6 +5,7 @@ import 'app/app.dart'; import 'core/editor/syntax_highlight_service.dart'; import 'core/layout/ui_scale_controller.dart'; import 'core/motion/display_refresh_service.dart'; +import 'core/motion/querya_motion_controller.dart'; import 'core/storage/local_db.dart'; import 'core/theme/theme_controller.dart'; @@ -15,6 +16,7 @@ void main() async { await SyntaxHighlightService.ensureInitialized(); await ThemeController.instance.load(); await UiScaleController.instance.load(); + await QueryaMotionController.instance.load(); runApp(const QueryaApp()); doWhenWindowReady(() { final win = appWindow; diff --git a/test/core/motion/querya_motion_controller_test.dart b/test/core/motion/querya_motion_controller_test.dart new file mode 100644 index 00000000..c42ddbae --- /dev/null +++ b/test/core/motion/querya_motion_controller_test.dart @@ -0,0 +1,75 @@ +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; +import 'package:querya_desktop/core/motion/querya_motion_controller.dart'; +import 'package:querya_desktop/core/motion/querya_motion_scope.dart'; +import 'package:querya_desktop/core/storage/local_db.dart'; + +class _FakePathProvider extends PathProviderPlatform { + _FakePathProvider(this._root); + final String _root; + + @override + Future getApplicationSupportPath() async => _root; + + @override + Future getTemporaryPath() async => _root; + + @override + Future getApplicationDocumentsPath() async => _root; +} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late Directory tempDir; + + setUpAll(() async { + tempDir = await Directory.systemTemp.createTemp('querya_motion_controller_test_'); + PathProviderPlatform.instance = _FakePathProvider(tempDir.path); + await LocalDb.initFfi(); + }); + + tearDownAll(() async { + await LocalDb.instance.close(); + if (await tempDir.exists()) { + await tempDir.delete(recursive: true); + } + }); + + test('QueryaMotionController default/off/reduced logic', () async { + final controller = QueryaMotionController.instance; + + // Initially loads default (full) + await controller.load(); + expect(controller.level, QueryaMotionLevel.full); + + var notifyCount = 0; + void listener() => notifyCount++; + controller.addListener(listener); + + try { + // Sets and persists reduced + await controller.setLevel(QueryaMotionLevel.reduced); + expect(controller.level, QueryaMotionLevel.reduced); + expect(notifyCount, 1); + + // Sets and persists off + await controller.setLevel(QueryaMotionLevel.off); + expect(controller.level, QueryaMotionLevel.off); + expect(notifyCount, 2); + + // Same level does not notify + await controller.setLevel(QueryaMotionLevel.off); + expect(notifyCount, 2); + + // Load from DB restores level + final anotherController = QueryaMotionController.instance; + await anotherController.load(); + expect(anotherController.level, QueryaMotionLevel.off); + } finally { + controller.removeListener(listener); + } + }); +} From a18c1c3870f1283b72573ccd96a5f81d1822b868 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Tue, 16 Jun 2026 15:03:02 +0300 Subject: [PATCH 07/16] feat(motion): commit refresh_rate package override source and generated runner configurations (fixes #174) --- linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.yaml | 3 + third_party/refresh_rate/CHANGELOG.md | 60 ++ third_party/refresh_rate/LICENSE | 24 + third_party/refresh_rate/README.md | 230 +++++ .../refresh_rate/analysis_options.yaml | 4 + third_party/refresh_rate/android/build.gradle | 51 ++ .../refresh_rate/android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 3 + .../qoder/refresh_rate/RefreshRatePlugin.kt | 248 ++++++ .../refresh_rate/generated/RefreshRateApi.kt | 345 ++++++++ third_party/refresh_rate/example/README.md | 17 + .../example/analysis_options.yaml | 28 + .../example/android/app/build.gradle.kts | 44 + .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 45 + .../refresh_rate_example/MainActivity.kt | 5 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + .../example/android/build.gradle.kts | 24 + .../example/android/gradle.properties | 2 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../example/android/settings.gradle.kts | 26 + .../integration_test/screenshot_test.dart | 150 ++++ .../ios/Flutter/AppFrameworkInfo.plist | 24 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + third_party/refresh_rate/example/ios/Podfile | 43 + .../refresh_rate/example/ios/Podfile.lock | 28 + .../ios/Runner.xcodeproj/project.pbxproj | 738 ++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 101 +++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../example/ios/Runner/AppDelegate.swift | 16 + .../AppIcon.appiconset/Contents.json | 122 +++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 1418 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../ios/Runner/DebugProfile.entitlements | 12 + .../example/ios/Runner/Info.plist | 70 ++ .../example/ios/Runner/Release.entitlements | 8 + .../ios/Runner/Runner-Bridging-Header.h | 1 + .../example/ios/Runner/SceneDelegate.swift | 6 + .../example/ios/RunnerTests/RunnerTests.swift | 12 + .../refresh_rate/example/lib/main.dart | 11 + .../example/lib/src/example_app.dart | 18 + .../example/lib/src/example_home.dart | 422 +++++++++ .../example/lib/src/example_theme.dart | 82 ++ .../example/lib/src/models/action_spec.dart | 15 + .../example/lib/src/models/info_item.dart | 6 + .../lib/src/sections/action_panel.dart | 48 ++ .../lib/src/sections/benchmark_panel.dart | 46 + .../lib/src/sections/display_info_panel.dart | 52 ++ .../example/lib/src/sections/hero_panel.dart | 109 +++ .../lib/src/sections/scroll_test_panel.dart | 108 +++ .../lib/src/sections/status_panel.dart | 47 + .../example/lib/src/ui/panel_widgets.dart | 377 +++++++++ .../refresh_rate/example/linux/CMakeLists.txt | 128 +++ .../example/linux/flutter/CMakeLists.txt | 88 ++ .../flutter/generated_plugin_registrant.cc | 15 + .../flutter/generated_plugin_registrant.h | 15 + .../linux/flutter/generated_plugins.cmake | 24 + .../example/linux/runner/CMakeLists.txt | 26 + .../refresh_rate/example/linux/runner/main.cc | 6 + .../example/linux/runner/my_application.cc | 148 ++++ .../example/linux/runner/my_application.h | 21 + .../macos/Flutter/Flutter-Debug.xcconfig | 2 + .../macos/Flutter/Flutter-Release.xcconfig | 2 + .../refresh_rate/example/macos/Podfile | 42 + .../refresh_rate/example/macos/Podfile.lock | 22 + .../macos/Runner.xcodeproj/project.pbxproj | 801 ++++++++++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 99 +++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 68 ++ .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 102994 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 5680 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 520 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 14142 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1066 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 36406 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 2218 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 343 ++++++++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../macos/RunnerTests/RunnerTests.swift | 12 + third_party/refresh_rate/example/pubspec.yaml | 23 + .../example/test/widget_test.dart | 11 + .../example/test_driver/integration_test.dart | 30 + .../refresh_rate/example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes .../example/web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes .../refresh_rate/example/web/index.html | 46 + .../refresh_rate/example/web/manifest.json | 35 + .../example/windows/CMakeLists.txt | 108 +++ .../example/windows/flutter/CMakeLists.txt | 109 +++ .../flutter/generated_plugin_registrant.cc | 14 + .../flutter/generated_plugin_registrant.h | 15 + .../windows/flutter/generated_plugins.cmake | 24 + .../example/windows/runner/CMakeLists.txt | 40 + .../example/windows/runner/Runner.rc | 121 +++ .../example/windows/runner/flutter_window.cpp | 71 ++ .../example/windows/runner/flutter_window.h | 33 + .../example/windows/runner/main.cpp | 43 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 14 + .../example/windows/runner/utils.cpp | 65 ++ .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 288 +++++++ .../example/windows/runner/win32_window.h | 102 +++ .../ios/Classes/DisplayLinkSwizzle.h | 14 + .../ios/Classes/DisplayLinkSwizzle.m | 186 ++++ .../ios/Classes/RefreshRatePlugin.swift | 210 +++++ .../Classes/generated/RefreshRateApi.swift | 369 ++++++++ .../refresh_rate/ios/refresh_rate.podspec | 22 + .../ios/refresh_rate/Package.swift | 32 + .../refresh_rate/RefreshRatePlugin.swift | 214 +++++ .../generated/RefreshRateApi.swift | 369 ++++++++ .../refresh_rate_objc/DisplayLinkSwizzle.m | 188 ++++ .../include/DisplayLinkSwizzle.h | 14 + .../refresh_rate/lib/refresh_rate.dart | 5 + .../refresh_rate/lib/refresh_rate_web.dart | 27 + .../lib/src/generated/refresh_rate_api.g.dart | 415 +++++++++ .../lib/src/models/display_info.dart | 128 +++ .../refresh_rate/lib/src/models/enums.dart | 141 +++ .../lib/src/models/session_report.dart | 186 ++++ .../refresh_rate/lib/src/refresh_rate.dart | 246 ++++++ .../lib/src/refresh_rate_api_adapter.dart | 69 ++ .../lib/src/verification/fps_tracker.dart | 132 +++ .../src/verification/overlay_controller.dart | 91 ++ .../lib/src/verification/overlay_widgets.dart | 172 ++++ .../verification/refresh_rate_session.dart | 135 +++ .../lib/src/verification/session_scorer.dart | 94 ++ .../lib/src/web/raf_hz_detector.dart | 63 ++ .../lib/src/web/web_refresh_rate_adapter.dart | 73 ++ third_party/refresh_rate/linux/CMakeLists.txt | 18 + .../refresh_rate/refresh_rate_plugin.h | 22 + .../refresh_rate/linux/refresh_rate_api.g.cc | 651 ++++++++++++++ .../refresh_rate/linux/refresh_rate_api.g.h | 233 +++++ .../refresh_rate/linux/refresh_rate_plugin.cc | 274 ++++++ .../macos/Classes/RefreshRatePlugin.swift | 167 ++++ .../Classes/generated/RefreshRateApi.swift | 369 ++++++++ .../refresh_rate/macos/refresh_rate.podspec | 16 + .../macos/refresh_rate/Package.swift | 35 + .../refresh_rate/pigeons/copyright.txt | 2 + .../pigeons/refresh_rate_api.dart | 63 ++ .../pigeons/refresh_rate_api_linux.dart | 63 ++ third_party/refresh_rate/pubspec.yaml | 61 ++ third_party/refresh_rate/screenshots/fps.png | Bin 0 -> 300828 bytes third_party/refresh_rate/screenshots/hz.png | Bin 0 -> 299789 bytes .../refresh_rate/test/fps_tracker_test.dart | 218 +++++ .../refresh_rate/test/models_test.dart | 83 ++ .../test/refresh_rate_api_test.dart | 61 ++ .../refresh_rate/windows/CMakeLists.txt | 18 + .../refresh_rate/refresh_rate_plugin_c_api.h | 23 + .../windows/refresh_rate_api.g.cpp | 651 ++++++++++++++ .../refresh_rate/windows/refresh_rate_api.g.h | 233 +++++ .../windows/refresh_rate_plugin_c_api.cpp | 155 ++++ .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 204 files changed, 14303 insertions(+) create mode 100644 third_party/refresh_rate/CHANGELOG.md create mode 100644 third_party/refresh_rate/LICENSE create mode 100644 third_party/refresh_rate/README.md create mode 100644 third_party/refresh_rate/analysis_options.yaml create mode 100644 third_party/refresh_rate/android/build.gradle create mode 100644 third_party/refresh_rate/android/settings.gradle create mode 100644 third_party/refresh_rate/android/src/main/AndroidManifest.xml create mode 100644 third_party/refresh_rate/android/src/main/kotlin/in/qoder/refresh_rate/RefreshRatePlugin.kt create mode 100644 third_party/refresh_rate/android/src/main/kotlin/in/qoder/refresh_rate/generated/RefreshRateApi.kt create mode 100644 third_party/refresh_rate/example/README.md create mode 100644 third_party/refresh_rate/example/analysis_options.yaml create mode 100644 third_party/refresh_rate/example/android/app/build.gradle.kts create mode 100644 third_party/refresh_rate/example/android/app/src/debug/AndroidManifest.xml create mode 100644 third_party/refresh_rate/example/android/app/src/main/AndroidManifest.xml create mode 100644 third_party/refresh_rate/example/android/app/src/main/kotlin/in/qoder/refresh_rate_example/MainActivity.kt create mode 100644 third_party/refresh_rate/example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 third_party/refresh_rate/example/android/app/src/main/res/drawable/launch_background.xml create mode 100644 third_party/refresh_rate/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 third_party/refresh_rate/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 third_party/refresh_rate/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 third_party/refresh_rate/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 third_party/refresh_rate/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 third_party/refresh_rate/example/android/app/src/main/res/values-night/styles.xml create mode 100644 third_party/refresh_rate/example/android/app/src/main/res/values/styles.xml create mode 100644 third_party/refresh_rate/example/android/app/src/profile/AndroidManifest.xml create mode 100644 third_party/refresh_rate/example/android/build.gradle.kts create mode 100644 third_party/refresh_rate/example/android/gradle.properties create mode 100644 third_party/refresh_rate/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 third_party/refresh_rate/example/android/settings.gradle.kts create mode 100644 third_party/refresh_rate/example/integration_test/screenshot_test.dart create mode 100644 third_party/refresh_rate/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 third_party/refresh_rate/example/ios/Flutter/Debug.xcconfig create mode 100644 third_party/refresh_rate/example/ios/Flutter/Release.xcconfig create mode 100644 third_party/refresh_rate/example/ios/Podfile create mode 100644 third_party/refresh_rate/example/ios/Podfile.lock create mode 100644 third_party/refresh_rate/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 third_party/refresh_rate/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 third_party/refresh_rate/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 third_party/refresh_rate/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 third_party/refresh_rate/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 third_party/refresh_rate/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 third_party/refresh_rate/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 third_party/refresh_rate/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 third_party/refresh_rate/example/ios/Runner/AppDelegate.swift create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 third_party/refresh_rate/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 third_party/refresh_rate/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 third_party/refresh_rate/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 third_party/refresh_rate/example/ios/Runner/DebugProfile.entitlements create mode 100644 third_party/refresh_rate/example/ios/Runner/Info.plist create mode 100644 third_party/refresh_rate/example/ios/Runner/Release.entitlements create mode 100644 third_party/refresh_rate/example/ios/Runner/Runner-Bridging-Header.h create mode 100644 third_party/refresh_rate/example/ios/Runner/SceneDelegate.swift create mode 100644 third_party/refresh_rate/example/ios/RunnerTests/RunnerTests.swift create mode 100644 third_party/refresh_rate/example/lib/main.dart create mode 100644 third_party/refresh_rate/example/lib/src/example_app.dart create mode 100644 third_party/refresh_rate/example/lib/src/example_home.dart create mode 100644 third_party/refresh_rate/example/lib/src/example_theme.dart create mode 100644 third_party/refresh_rate/example/lib/src/models/action_spec.dart create mode 100644 third_party/refresh_rate/example/lib/src/models/info_item.dart create mode 100644 third_party/refresh_rate/example/lib/src/sections/action_panel.dart create mode 100644 third_party/refresh_rate/example/lib/src/sections/benchmark_panel.dart create mode 100644 third_party/refresh_rate/example/lib/src/sections/display_info_panel.dart create mode 100644 third_party/refresh_rate/example/lib/src/sections/hero_panel.dart create mode 100644 third_party/refresh_rate/example/lib/src/sections/scroll_test_panel.dart create mode 100644 third_party/refresh_rate/example/lib/src/sections/status_panel.dart create mode 100644 third_party/refresh_rate/example/lib/src/ui/panel_widgets.dart create mode 100644 third_party/refresh_rate/example/linux/CMakeLists.txt create mode 100644 third_party/refresh_rate/example/linux/flutter/CMakeLists.txt create mode 100644 third_party/refresh_rate/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 third_party/refresh_rate/example/linux/flutter/generated_plugin_registrant.h create mode 100644 third_party/refresh_rate/example/linux/flutter/generated_plugins.cmake create mode 100644 third_party/refresh_rate/example/linux/runner/CMakeLists.txt create mode 100644 third_party/refresh_rate/example/linux/runner/main.cc create mode 100644 third_party/refresh_rate/example/linux/runner/my_application.cc create mode 100644 third_party/refresh_rate/example/linux/runner/my_application.h create mode 100644 third_party/refresh_rate/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 third_party/refresh_rate/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 third_party/refresh_rate/example/macos/Podfile create mode 100644 third_party/refresh_rate/example/macos/Podfile.lock create mode 100644 third_party/refresh_rate/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 third_party/refresh_rate/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 third_party/refresh_rate/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 third_party/refresh_rate/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 third_party/refresh_rate/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 third_party/refresh_rate/example/macos/Runner/AppDelegate.swift create mode 100644 third_party/refresh_rate/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 third_party/refresh_rate/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 third_party/refresh_rate/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 third_party/refresh_rate/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 third_party/refresh_rate/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 third_party/refresh_rate/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 third_party/refresh_rate/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 third_party/refresh_rate/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 third_party/refresh_rate/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 third_party/refresh_rate/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 third_party/refresh_rate/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 third_party/refresh_rate/example/macos/Runner/Configs/Release.xcconfig create mode 100644 third_party/refresh_rate/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 third_party/refresh_rate/example/macos/Runner/DebugProfile.entitlements create mode 100644 third_party/refresh_rate/example/macos/Runner/Info.plist create mode 100644 third_party/refresh_rate/example/macos/Runner/MainFlutterWindow.swift create mode 100644 third_party/refresh_rate/example/macos/Runner/Release.entitlements create mode 100644 third_party/refresh_rate/example/macos/RunnerTests/RunnerTests.swift create mode 100644 third_party/refresh_rate/example/pubspec.yaml create mode 100644 third_party/refresh_rate/example/test/widget_test.dart create mode 100644 third_party/refresh_rate/example/test_driver/integration_test.dart create mode 100644 third_party/refresh_rate/example/web/favicon.png create mode 100644 third_party/refresh_rate/example/web/icons/Icon-192.png create mode 100644 third_party/refresh_rate/example/web/icons/Icon-512.png create mode 100644 third_party/refresh_rate/example/web/icons/Icon-maskable-192.png create mode 100644 third_party/refresh_rate/example/web/icons/Icon-maskable-512.png create mode 100644 third_party/refresh_rate/example/web/index.html create mode 100644 third_party/refresh_rate/example/web/manifest.json create mode 100644 third_party/refresh_rate/example/windows/CMakeLists.txt create mode 100644 third_party/refresh_rate/example/windows/flutter/CMakeLists.txt create mode 100644 third_party/refresh_rate/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 third_party/refresh_rate/example/windows/flutter/generated_plugin_registrant.h create mode 100644 third_party/refresh_rate/example/windows/flutter/generated_plugins.cmake create mode 100644 third_party/refresh_rate/example/windows/runner/CMakeLists.txt create mode 100644 third_party/refresh_rate/example/windows/runner/Runner.rc create mode 100644 third_party/refresh_rate/example/windows/runner/flutter_window.cpp create mode 100644 third_party/refresh_rate/example/windows/runner/flutter_window.h create mode 100644 third_party/refresh_rate/example/windows/runner/main.cpp create mode 100644 third_party/refresh_rate/example/windows/runner/resource.h create mode 100644 third_party/refresh_rate/example/windows/runner/resources/app_icon.ico create mode 100644 third_party/refresh_rate/example/windows/runner/runner.exe.manifest create mode 100644 third_party/refresh_rate/example/windows/runner/utils.cpp create mode 100644 third_party/refresh_rate/example/windows/runner/utils.h create mode 100644 third_party/refresh_rate/example/windows/runner/win32_window.cpp create mode 100644 third_party/refresh_rate/example/windows/runner/win32_window.h create mode 100644 third_party/refresh_rate/ios/Classes/DisplayLinkSwizzle.h create mode 100644 third_party/refresh_rate/ios/Classes/DisplayLinkSwizzle.m create mode 100644 third_party/refresh_rate/ios/Classes/RefreshRatePlugin.swift create mode 100644 third_party/refresh_rate/ios/Classes/generated/RefreshRateApi.swift create mode 100644 third_party/refresh_rate/ios/refresh_rate.podspec create mode 100644 third_party/refresh_rate/ios/refresh_rate/Package.swift create mode 100644 third_party/refresh_rate/ios/refresh_rate/Sources/refresh_rate/RefreshRatePlugin.swift create mode 100644 third_party/refresh_rate/ios/refresh_rate/Sources/refresh_rate/generated/RefreshRateApi.swift create mode 100644 third_party/refresh_rate/ios/refresh_rate/Sources/refresh_rate_objc/DisplayLinkSwizzle.m create mode 100644 third_party/refresh_rate/ios/refresh_rate/Sources/refresh_rate_objc/include/DisplayLinkSwizzle.h create mode 100644 third_party/refresh_rate/lib/refresh_rate.dart create mode 100644 third_party/refresh_rate/lib/refresh_rate_web.dart create mode 100644 third_party/refresh_rate/lib/src/generated/refresh_rate_api.g.dart create mode 100644 third_party/refresh_rate/lib/src/models/display_info.dart create mode 100644 third_party/refresh_rate/lib/src/models/enums.dart create mode 100644 third_party/refresh_rate/lib/src/models/session_report.dart create mode 100644 third_party/refresh_rate/lib/src/refresh_rate.dart create mode 100644 third_party/refresh_rate/lib/src/refresh_rate_api_adapter.dart create mode 100644 third_party/refresh_rate/lib/src/verification/fps_tracker.dart create mode 100644 third_party/refresh_rate/lib/src/verification/overlay_controller.dart create mode 100644 third_party/refresh_rate/lib/src/verification/overlay_widgets.dart create mode 100644 third_party/refresh_rate/lib/src/verification/refresh_rate_session.dart create mode 100644 third_party/refresh_rate/lib/src/verification/session_scorer.dart create mode 100644 third_party/refresh_rate/lib/src/web/raf_hz_detector.dart create mode 100644 third_party/refresh_rate/lib/src/web/web_refresh_rate_adapter.dart create mode 100644 third_party/refresh_rate/linux/CMakeLists.txt create mode 100644 third_party/refresh_rate/linux/include/refresh_rate/refresh_rate_plugin.h create mode 100644 third_party/refresh_rate/linux/refresh_rate_api.g.cc create mode 100644 third_party/refresh_rate/linux/refresh_rate_api.g.h create mode 100644 third_party/refresh_rate/linux/refresh_rate_plugin.cc create mode 100644 third_party/refresh_rate/macos/Classes/RefreshRatePlugin.swift create mode 100644 third_party/refresh_rate/macos/Classes/generated/RefreshRateApi.swift create mode 100644 third_party/refresh_rate/macos/refresh_rate.podspec create mode 100644 third_party/refresh_rate/macos/refresh_rate/Package.swift create mode 100644 third_party/refresh_rate/pigeons/copyright.txt create mode 100644 third_party/refresh_rate/pigeons/refresh_rate_api.dart create mode 100644 third_party/refresh_rate/pigeons/refresh_rate_api_linux.dart create mode 100644 third_party/refresh_rate/pubspec.yaml create mode 100644 third_party/refresh_rate/screenshots/fps.png create mode 100644 third_party/refresh_rate/screenshots/hz.png create mode 100644 third_party/refresh_rate/test/fps_tracker_test.dart create mode 100644 third_party/refresh_rate/test/models_test.dart create mode 100644 third_party/refresh_rate/test/refresh_rate_api_test.dart create mode 100644 third_party/refresh_rate/windows/CMakeLists.txt create mode 100644 third_party/refresh_rate/windows/include/refresh_rate/refresh_rate_plugin_c_api.h create mode 100644 third_party/refresh_rate/windows/refresh_rate_api.g.cpp create mode 100644 third_party/refresh_rate/windows/refresh_rate_api.g.h create mode 100644 third_party/refresh_rate/windows/refresh_rate_plugin_c_api.cpp diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 49d2eb91..f09d0a3b 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include void fl_register_plugins(FlPluginRegistry* registry) { @@ -25,6 +26,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) irondash_engine_context_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "IrondashEngineContextPlugin"); irondash_engine_context_plugin_register_with_registrar(irondash_engine_context_registrar); + g_autoptr(FlPluginRegistrar) refresh_rate_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "RefreshRatePlugin"); + refresh_rate_plugin_register_with_registrar(refresh_rate_registrar); g_autoptr(FlPluginRegistrar) super_native_extensions_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "SuperNativeExtensionsPlugin"); super_native_extensions_plugin_register_with_registrar(super_native_extensions_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 0516a6e3..7792787e 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -7,6 +7,7 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_linux flutter_secure_storage_linux irondash_engine_context + refresh_rate super_native_extensions ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 5644fad5..d30bbdad 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -10,6 +10,7 @@ import device_info_plus import file_selector_macos import flutter_secure_storage_macos import irondash_engine_context +import refresh_rate import sqflite_darwin import super_native_extensions @@ -19,6 +20,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) IrondashEngineContextPlugin.register(with: registry.registrar(forPlugin: "IrondashEngineContextPlugin")) + RefreshRatePlugin.register(with: registry.registrar(forPlugin: "RefreshRatePlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) SuperNativeExtensionsPlugin.register(with: registry.registrar(forPlugin: "SuperNativeExtensionsPlugin")) } diff --git a/pubspec.yaml b/pubspec.yaml index 2006b743..2e5b40e5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,6 +39,9 @@ dependency_overrides: # Patched ToastLayer (fixes InheritedNotifier crash on resize / hot reload). shadcn_flutter: path: third_party/shadcn_flutter + # Linux -Werror: fix uninitialized rates[0] in refresh_rate 1.0.2. + refresh_rate: + path: third_party/refresh_rate flutter: uses-material-design: true diff --git a/third_party/refresh_rate/CHANGELOG.md b/third_party/refresh_rate/CHANGELOG.md new file mode 100644 index 00000000..6f9b880f --- /dev/null +++ b/third_party/refresh_rate/CHANGELOG.md @@ -0,0 +1,60 @@ +# Changelog + +## 1.0.2 + +- Added Swift Package Manager support. + +## 1.0.1 + +### Web support +- Added web platform implementation using `requestAnimationFrame` interval + timing to detect the display's current refresh rate (same technique as TestUFO). +- Control methods are graceful no-ops (browsers own vsync scheduling). +- FPS overlay and benchmark sessions work unchanged on web. + +### Documentation +- Added comprehensive Dartdoc comments to all public APIs. + +### Platform fixes +- Restructured `Package.swift` into `ios/refresh_rate/` and `macos/refresh_rate/` + for correct Swift Package Manager module resolution. +- Resolved minor lint warnings from test suites. + +## 1.0.0 + +Initial stable release — unlock, query, overlay, and benchmark display refresh rates across all Flutter platforms. + +### Control + +- `RefreshRate.enable()` — one-line unlock of peak display refresh rate +- `RefreshRate.preferMax()` / `preferDefault()` — explicit rate preference +- `RefreshRate.matchContent(fps)` — sync display cadence to content frame rate (fixes 24 fps video judder) +- `RefreshRate.boost(duration)` — temporary max-rate spike for gesture-driven animations +- `RefreshRate.category(RateCategory)` — Android 15 semantic rate category +- `RefreshRate.setTouchBoost(bool)` — Android 15 touch-driven rate boost + +### Diagnostics + +- `RefreshRate.info` — synchronous cached `RefreshRateInfo` snapshot (current rate, max, min, supported rates, VRR, API level) +- `RefreshRate.onChanged` — stream fires on rate change, Low Power Mode toggle, or thermal state change +- `RefreshRate.isLowPowerMode` / `thermalState` / `isProMotionReady` + +### Debug overlay + +- `RefreshRate.showOverlay()` — full diagnostic HUD with live FPS, build/raster timings, frame budget +- `RefreshRate.showFPS()` / `showHz()` — individual overlay badges +- FPS colour is relative to the device's actual target rate, not a hard-coded 60 Hz baseline + +### Benchmark sessions + +- `RefreshRate.startSession(name)` returns `RefreshRateSession` +- `session.end()` returns `SessionReport` with verdict, bottleneck, avgFps, 1% low FPS, missed-frame %, and JSON export +- Sessions auto-exclude backgrounded periods, resume warmup, LPM changes, and thermal changes + +### Platform coverage + +- **Android 6+** (API 23): `SurfaceControl.Transaction.setFrameRate()` (API 34+), `preferredRefreshRate` + `preferredDisplayModeId` (API 30–33), legacy `preferredDisplayModeId` fallback (API 23–29) +- **iOS 15+**: `CADisplayLink.preferredFrameRateRange` with runtime `Info.plist` ProMotion validation +- **macOS 14+**: `CADisplayLink`-based control via `NSView.displayLink` +- **macOS < 14 / Windows / Linux**: query-only (reports real monitor refresh rate) +- All platform bridges use [pigeon](https://pub.dev/packages/pigeon) — fully typed, zero codec overhead diff --git a/third_party/refresh_rate/LICENSE b/third_party/refresh_rate/LICENSE new file mode 100644 index 00000000..5bdae8a2 --- /dev/null +++ b/third_party/refresh_rate/LICENSE @@ -0,0 +1,24 @@ +Copyright 2026 Qoder. 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 Qoder 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 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/third_party/refresh_rate/README.md b/third_party/refresh_rate/README.md new file mode 100644 index 00000000..1d07f6a9 --- /dev/null +++ b/third_party/refresh_rate/README.md @@ -0,0 +1,230 @@ +# refresh_rate + +[![pub package](https://img.shields.io/pub/v/refresh_rate.svg)](https://pub.dev/packages/refresh_rate) +[![pub points](https://img.shields.io/pub/points/refresh_rate)](https://pub.dev/packages/refresh_rate/score) +[![likes](https://img.shields.io/pub/likes/refresh_rate)](https://pub.dev/packages/refresh_rate/score) +[![popularity](https://img.shields.io/pub/popularity/refresh_rate)](https://pub.dev/packages/refresh_rate/score) +[![License: BSD-3](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://github.com/qoder-official/refresh_rate/blob/main/LICENSE) + +**Unlock your device's full refresh rate in one line of Flutter.** + +Your Flutter app runs at 60 Hz on a 120 Hz phone right now. The engine never tells the OS compositor it can handle more. `refresh_rate` fixes that — and gives you diagnostics, benchmarks, and a live overlay to prove it. + +```dart +void main() { + RefreshRate.enable(); // that's it — 120 Hz on a 120 Hz device + runApp(const MyApp()); +} +``` + +> **Why does this happen?** Flutter's engine never calls Android's `Surface.setFrameRate()` and the default iOS template is missing the `CADisableMinimumFrameDurationOnPhone` plist key. See [Flutter #160952](https://github.com/flutter/flutter/issues/160952). This package makes those calls for you. + +Built on [pigeon](https://pub.dev/packages/pigeon) — fully typed end-to-end, zero `MethodChannel` codec overhead. + +--- + +## Platform support + +| Platform | Unlock | Query | Overlay | Benchmark | +|:---------|:------:|:-----:|:-------:|:---------:| +| **Android** 6+ (API 23) | ✅ | ✅ | ✅ | ✅ | +| **iOS** 15+ (ProMotion) | ✅ \* | ✅ | ✅ | ✅ | +| **macOS** 14+ (Sonoma) | ✅ | ✅ | ✅ | ✅ | +| **macOS** < 14 | — | ✅ | ✅ | ✅ | +| **Windows** | — | ✅ | ✅ | ✅ | +| **Linux** | — | ✅ | ✅ | ✅ | + +\* iOS requires `Info.plist` key — see [iOS setup](#ios-setup). + +--- + +## Installation + +```yaml +dependencies: + refresh_rate: ^1.0.0 +``` + +### iOS setup + +Add to `ios/Runner/Info.plist` — required for > 60 Hz on iPhones with ProMotion: + +```xml +CADisableMinimumFrameDurationOnPhone + +``` + +Without this key, iOS caps your app at 60 Hz even on 120 Hz hardware. The plugin detects this at runtime and prints a console warning if missing. iPad Pro does **not** need this key. + +--- + +## Quick start + +```dart +import 'package:refresh_rate/refresh_rate.dart'; + +void main() { + WidgetsFlutterBinding.ensureInitialized(); + RefreshRate.enable(); // unlocks peak rate on every supported device + runApp(const MyApp()); +} +``` + +One call in `main()`. On a 120 Hz device your app now renders at 120 Hz. On a 60 Hz device nothing changes — the OS stays in control. + +--- + +## Diagnostics + +```dart +final info = RefreshRate.info; // synchronous cached snapshot + +print(info.currentRate); // 120.0 +print(info.maxRate); // 120.0 +print(info.supportedRates); // [60.0, 90.0, 120.0] +print(info.isVariableRefreshRate); // true (LTPO panel) +print(info.androidApiLevel); // 34 (Android only) +print(info.displayServer); // "wayland" (Linux only) + +print(RefreshRate.isLowPowerMode); // false +print(RefreshRate.thermalState); // ThermalState.nominal +print(RefreshRate.isProMotionReady); // true — plist key + hardware both present + +await RefreshRate.refresh(); // reload platform cache + +RefreshRate.onChanged.listen((info) { // rate, Low Power Mode, or thermal change + print('Now running at ${info.currentRate} Hz'); +}); +``` + +--- + +## Advanced control + +```dart +RefreshRate.preferMax(); // highest available rate +RefreshRate.preferDefault(); // return to OS default +RefreshRate.matchContent(24.0); // sync to 24 fps video (fixes judder) +RefreshRate.boost(const Duration(seconds: 2)); // temporary spike for gestures + +// Android 15+ +RefreshRate.category(RateCategory.high); // semantic rate category +RefreshRate.setTouchBoost(true); // OS-managed touch boost +``` + +--- + +## Debug overlay + +Drop a live performance HUD into any debug build: + +```dart +if (kDebugMode) RefreshRate.showOverlay(); +``` + +The overlay is **refresh-rate-aware**: FPS is colour-coded against the device's *actual* target Hz, not a hard-coded 60 Hz baseline. It also shows per-frame build/raster timings, frame budget, Low Power Mode, and thermal state. + +```dart +RefreshRate.showFPS(); // just the FPS counter +RefreshRate.showHz(); // just the Hz badge +RefreshRate.showOverlay(); // full diagnostic panel +RefreshRate.hideOverlay(); // dismiss +``` + +--- + +## Benchmark sessions + +Record a named performance window and get a structured report: + +```dart +final session = RefreshRate.startSession('home_scroll'); + +// ... user interacts ... + +final report = await session.end(); + +print(report.verdict); // Verdict.good / degraded / poor +print(report.likelyBottleneck); // Bottleneck.rasterBound / buildBound / none +print(report.avgFps); // 108.4 +print(report.onePercentLowFps); // 87.2 +print(report.missedFramePercent); // 3.2% + +final json = report.toJson(); // export for CI / QA dashboards +``` + +Sessions automatically exclude app backgrounding, resume warmup, Low Power Mode toggles, and thermal state changes — so numbers reflect real rendering performance. + +--- + +## How it works + +### Android + +| API Level | What the plugin calls | +|:---------:|:----------------------| +| **34+** | `SurfaceControl.Transaction.setFrameRate()` — direct SurfaceFlinger vote | +| **30–33** | `preferredRefreshRate` + `preferredDisplayModeId` — dual hint | +| **23–29** | `preferredDisplayModeId` with resolution-match guard — legacy fallback | + +Flutter never calls `Surface.setFrameRate()`. That single missing call is why 120 Hz phones render Flutter at 60 Hz. + +### iOS + +Sets `CADisplayLink.preferredFrameRateRange` with the device max. Validates the `CADisableMinimumFrameDurationOnPhone` plist key at runtime and warns loudly if missing. + +### macOS + +`NSView.displayLink` with `preferredFrameRateRange` on macOS 14+. Falls back to `NSScreen.maximumFramesPerSecond` / `CGDisplayCopyDisplayMode` for query. + +### Windows & Linux + +Query-only via `QueryDisplayConfig` (Win) and `gdk_monitor_get_refresh_rate` (Linux). Control depends on Flutter's desktop embedder evolution — tracked at [#93058](https://github.com/flutter/flutter/issues/93058) and [#183703](https://github.com/flutter/flutter/issues/183703). + +--- + +## API reference + +| Method | What it does | +|:-------|:-------------| +| `enable()` | Unlock peak rate — call once in `main()` | +| `disable()` | Stop overriding, return to OS default | +| `preferMax()` | Request highest available rate | +| `preferDefault()` | Clear rate override | +| `matchContent(fps)` | Sync display cadence to content frame rate | +| `boost(duration)` | Temporary max-rate spike | +| `category(cat)` | Android 15 semantic rate category | +| `setTouchBoost(bool)` | Android 15 touch-driven boost | +| `refresh()` | Reload platform info cache | +| `info` | Cached `RefreshRateInfo` snapshot | +| `isLowPowerMode` | Battery Saver / Low Power Mode active | +| `thermalState` | `ThermalState` enum | +| `isProMotionReady` | iOS plist key + ProMotion hardware | +| `onChanged` | `Stream` | +| `showFPS()` | Live FPS counter overlay | +| `showHz()` | Live Hz badge overlay | +| `showOverlay()` | Full diagnostic overlay | +| `hideOverlay()` | Dismiss overlay | +| `startSession(name)` | Start benchmark session | + +--- + +## Why this exists + +Flutter's engine (Impeller since 3.24) *can* render at 120 Hz. But it never *tells the OS*. On Android, `Surface.setFrameRate()` returns zero search results across the entire engine codebase. On iOS, the engine code is correct but the default template omits the plist key. + +This has been open since January 2023 ([#119268](https://github.com/flutter/flutter/issues/119268)), currently tracked at [#160952](https://github.com/flutter/flutter/issues/160952) (P2, unassigned). + +`refresh_rate` fixes this today on shipping apps, while collecting real-device evidence for an eventual engine-level fix. + +--- + +## License + +BSD 3-Clause © 2026 [Qoder](https://qoder.in) + +--- + +

+ Made with care by Qoder · More packages +

diff --git a/third_party/refresh_rate/analysis_options.yaml b/third_party/refresh_rate/analysis_options.yaml new file mode 100644 index 00000000..b0f3c0af --- /dev/null +++ b/third_party/refresh_rate/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml +linter: + rules: + - public_member_api_docs diff --git a/third_party/refresh_rate/android/build.gradle b/third_party/refresh_rate/android/build.gradle new file mode 100644 index 00000000..484f253e --- /dev/null +++ b/third_party/refresh_rate/android/build.gradle @@ -0,0 +1,51 @@ +group = "in.qoder.refresh_rate" +version = "1.0-SNAPSHOT" + +buildscript { + ext.kotlin_version = "1.9.22" + repositories { + google() + mavenCentral() + } + + dependencies { + classpath("com.android.tools.build:gradle:8.1.0") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version") + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: "com.android.library" +apply plugin: "kotlin-android" + +android { + namespace = "in.qoder.refresh_rate" + compileSdk = 35 + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } + + sourceSets { + main.java.srcDirs += "src/main/kotlin" + } + + defaultConfig { + minSdk = 21 + } +} + +dependencies { + implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") +} diff --git a/third_party/refresh_rate/android/settings.gradle b/third_party/refresh_rate/android/settings.gradle new file mode 100644 index 00000000..f27361d7 --- /dev/null +++ b/third_party/refresh_rate/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "refresh_rate" diff --git a/third_party/refresh_rate/android/src/main/AndroidManifest.xml b/third_party/refresh_rate/android/src/main/AndroidManifest.xml new file mode 100644 index 00000000..1e375ae6 --- /dev/null +++ b/third_party/refresh_rate/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/third_party/refresh_rate/android/src/main/kotlin/in/qoder/refresh_rate/RefreshRatePlugin.kt b/third_party/refresh_rate/android/src/main/kotlin/in/qoder/refresh_rate/RefreshRatePlugin.kt new file mode 100644 index 00000000..596430dd --- /dev/null +++ b/third_party/refresh_rate/android/src/main/kotlin/in/qoder/refresh_rate/RefreshRatePlugin.kt @@ -0,0 +1,248 @@ +package `in`.qoder.refresh_rate + +import android.app.Activity +import android.content.Context +import android.hardware.display.DisplayManager +import android.os.Build +import android.os.PowerManager +import android.view.Display +import android.view.Surface +import android.view.View +import android.view.WindowManager +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.embedding.engine.plugins.activity.ActivityAware +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding +import `in`.qoder.refresh_rate.generated.DisplayInfoMessage +import `in`.qoder.refresh_rate.generated.RefreshRateFlutterApi +import `in`.qoder.refresh_rate.generated.RefreshRateHostApi + +class RefreshRatePlugin : FlutterPlugin, ActivityAware, RefreshRateHostApi { + + private var activity: Activity? = null + private var context: Context? = null + private var flutterApi: RefreshRateFlutterApi? = null + private var displayListener: DisplayManager.DisplayListener? = null + + // ─── FlutterPlugin ────────────────────────────────────────── + + override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + context = binding.applicationContext + RefreshRateHostApi.setUp(binding.binaryMessenger, this) + flutterApi = RefreshRateFlutterApi(binding.binaryMessenger) + registerDisplayListener() + } + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + RefreshRateHostApi.setUp(binding.binaryMessenger, null) + unregisterDisplayListener() + flutterApi = null + } + + // ─── ActivityAware ────────────────────────────────────────── + + override fun onAttachedToActivity(binding: ActivityPluginBinding) { activity = binding.activity } + override fun onDetachedFromActivityForConfigChanges() { activity = null } + override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { activity = binding.activity } + override fun onDetachedFromActivity() { activity = null } + + // ─── RefreshRateHostApi ───────────────────────────────────── + + override fun getDisplayInfo(): DisplayInfoMessage { + val display = getDisplay() + val currentRate = display?.refreshRate?.toDouble() ?: 60.0 + val modes = display?.supportedModes ?: emptyArray() + val supportedRates = modes.map { it.refreshRate.toDouble() }.distinct().sorted() + val maxRate = supportedRates.maxOrNull() ?: 60.0 + val minRate = supportedRates.minOrNull() ?: 60.0 + val isVRR = (maxRate - minRate > 30) && modes.size <= 4 + val pm = context?.getSystemService(Context.POWER_SERVICE) as? PowerManager + val thermalIndex: Long? = if (Build.VERSION.SDK_INT >= 29) { + when (pm?.currentThermalStatus) { + PowerManager.THERMAL_STATUS_NONE -> 0L + PowerManager.THERMAL_STATUS_LIGHT, PowerManager.THERMAL_STATUS_MODERATE -> 1L + PowerManager.THERMAL_STATUS_SEVERE -> 2L + PowerManager.THERMAL_STATUS_CRITICAL, + PowerManager.THERMAL_STATUS_EMERGENCY, + PowerManager.THERMAL_STATUS_SHUTDOWN -> 3L + else -> null + } + } else null + + // hasArrSupport() is API 36+ — fall back to VRR heuristic for now + val hasArr = isVRR + + return DisplayInfoMessage( + currentRate = currentRate, + maxRate = maxRate, + minRate = minRate, + supportedRates = supportedRates, + isVariableRefreshRate = isVRR, + engineTargetRate = currentRate, + androidApiLevel = Build.VERSION.SDK_INT.toLong(), + isLowPowerMode = pm?.isPowerSaveMode, + thermalStateIndex = thermalIndex, + hasAdaptiveRefreshRate = hasArr, + iosProMotionEnabled = null, + displayServer = null, + monitorCount = null, + ) + } + + override fun enable() = setDeviceDefault() + override fun disable() = resetToDefault() + override fun preferMax() = setDeviceDefault() + override fun preferDefault() = resetToDefault() + + override fun matchContent(fps: Double) { + if (Build.VERSION.SDK_INT >= 30) { + setSurfaceFrameRate(fps.toFloat(), Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, forceAlways = true) + } else if (Build.VERSION.SDK_INT >= 23) { + setPreferredDisplayMode(fps.toFloat()) + } + } + + override fun boost(durationMs: Long) { + val display = getDisplay() ?: return + val maxRate = display.supportedModes.maxByOrNull { it.refreshRate }?.refreshRate ?: 60f + if (Build.VERSION.SDK_INT >= 35) { + try { activity?.window?.decorView?.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_HIGH.toFloat()) } catch (_: Exception) {} + } + setSurfaceFrameRate(maxRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT, forceAlways = true) + android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ resetToDefault() }, durationMs) + } + + override fun setCategory(categoryIndex: Long) { + if (Build.VERSION.SDK_INT < 35) { + when (categoryIndex.toInt()) { + 3 -> setDeviceDefault() + 0, 1 -> resetToDefault() + else -> {} + } + return + } + try { + val categoryFloat = when (categoryIndex.toInt()) { + 0 -> 0f + 1 -> View.REQUESTED_FRAME_RATE_CATEGORY_LOW.toFloat() + 2 -> View.REQUESTED_FRAME_RATE_CATEGORY_NORMAL.toFloat() + 3 -> View.REQUESTED_FRAME_RATE_CATEGORY_HIGH.toFloat() + else -> View.REQUESTED_FRAME_RATE_CATEGORY_HIGH.toFloat() + } + activity?.window?.decorView?.setRequestedFrameRate(categoryFloat) + } catch (_: Exception) {} + } + + override fun setTouchBoost(enabled: Boolean) { + if (Build.VERSION.SDK_INT >= 35) { + try { activity?.window?.setFrameRateBoostOnTouchEnabled(enabled) } catch (_: Exception) {} + } + } + + override fun isSupported(): Boolean = Build.VERSION.SDK_INT >= 23 + + // ─── Private helpers ──────────────────────────────────────── + + private fun setDeviceDefault() { + val display = getDisplay() ?: return + val maxRate = display.supportedModes.maxByOrNull { it.refreshRate }?.refreshRate ?: 60f + if (Build.VERSION.SDK_INT >= 35) { + try { + val window = activity?.window + window?.decorView?.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_HIGH.toFloat()) + window?.setFrameRateBoostOnTouchEnabled(true) + } catch (_: Exception) {} + setSurfaceFrameRate(maxRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT) + } else if (Build.VERSION.SDK_INT >= 30) { + setSurfaceFrameRate(maxRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT) + } else if (Build.VERSION.SDK_INT >= 23) { + setPreferredDisplayMode(maxRate) + } + } + + private fun resetToDefault() { + if (Build.VERSION.SDK_INT >= 35) { + try { + val window = activity?.window + window?.decorView?.setRequestedFrameRate(0f) + window?.setFrameRateBoostOnTouchEnabled(false) + } catch (_: Exception) {} + setSurfaceFrameRate(0f, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT) + } else if (Build.VERSION.SDK_INT >= 30) { + setSurfaceFrameRate(0f, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT) + } else if (Build.VERSION.SDK_INT >= 23) { + try { + val params = activity?.window?.attributes ?: return + params.preferredDisplayModeId = 0 + activity?.window?.attributes = params + } catch (_: Exception) {} + } + } + + private fun setSurfaceFrameRate(frameRate: Float, compatibility: Int, forceAlways: Boolean = false): Boolean { + if (Build.VERSION.SDK_INT < 30) return false + return try { + val window = activity?.window ?: return false + val strategy = if (Build.VERSION.SDK_INT >= 31) { + if (forceAlways) Surface.CHANGE_FRAME_RATE_ALWAYS + else Surface.CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS + } else -1 + val params = window.attributes + params.preferredRefreshRate = frameRate + val display = getDisplay() + if (display != null && frameRate > 0) { + val targetMode = display.supportedModes + .filter { it.refreshRate >= frameRate - 1f } + .minByOrNull { Math.abs(it.refreshRate - frameRate) } + if (targetMode != null) params.preferredDisplayModeId = targetMode.modeId + } else if (frameRate == 0f) { + params.preferredDisplayModeId = 0 + params.preferredRefreshRate = 0f + } + window.attributes = params + true + } catch (e: Exception) { false } + } + + private fun setPreferredDisplayMode(targetRate: Float): Boolean { + if (Build.VERSION.SDK_INT < 23) return false + return try { + val window = activity?.window ?: return false + val display = getDisplay() ?: return false + val currentMode = display.mode + val targetMode = display.supportedModes + .filter { it.physicalWidth == currentMode.physicalWidth && it.physicalHeight == currentMode.physicalHeight } + .minByOrNull { Math.abs(it.refreshRate - targetRate) } + if (targetMode != null) { + val params = window.attributes + params.preferredDisplayModeId = targetMode.modeId + window.attributes = params + true + } else false + } catch (e: Exception) { false } + } + + private fun registerDisplayListener() { + val dm = context?.getSystemService(Context.DISPLAY_SERVICE) as? DisplayManager ?: return + displayListener = object : DisplayManager.DisplayListener { + override fun onDisplayChanged(displayId: Int) { + activity?.runOnUiThread { flutterApi?.onDisplayInfoChanged(getDisplayInfo()) {} } + } + override fun onDisplayAdded(displayId: Int) {} + override fun onDisplayRemoved(displayId: Int) {} + } + dm.registerDisplayListener(displayListener, null) + } + + private fun unregisterDisplayListener() { + val dm = context?.getSystemService(Context.DISPLAY_SERVICE) as? DisplayManager ?: return + displayListener?.let { dm.unregisterDisplayListener(it) } + displayListener = null + } + + @Suppress("DEPRECATION") + private fun getDisplay(): Display? = if (Build.VERSION.SDK_INT >= 30) { + activity?.display + } else { + (context?.getSystemService(Context.WINDOW_SERVICE) as? WindowManager)?.defaultDisplay + } +} diff --git a/third_party/refresh_rate/android/src/main/kotlin/in/qoder/refresh_rate/generated/RefreshRateApi.kt b/third_party/refresh_rate/android/src/main/kotlin/in/qoder/refresh_rate/generated/RefreshRateApi.kt new file mode 100644 index 00000000..b72597dc --- /dev/null +++ b/third_party/refresh_rate/android/src/main/kotlin/in/qoder/refresh_rate/generated/RefreshRateApi.kt @@ -0,0 +1,345 @@ +// Copyright 2026 Qoder (qoder.in). All rights reserved. +// Use of this source code is governed by a BSD-style license. +// Autogenerated from Pigeon (v22.7.4), do not edit directly. +// See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") + +package `in`.qoder.refresh_rate.generated + +import android.util.Log +import io.flutter.plugin.common.BasicMessageChannel +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.EventChannel +import io.flutter.plugin.common.MessageCodec +import io.flutter.plugin.common.StandardMethodCodec +import io.flutter.plugin.common.StandardMessageCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer + +private fun wrapResult(result: Any?): List { + return listOf(result) +} + +private fun wrapError(exception: Throwable): List { + return if (exception is FlutterError) { + listOf( + exception.code, + exception.message, + exception.details + ) + } else { + listOf( + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) + ) + } +} + +private fun createConnectionError(channelName: String): FlutterError { + return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "")} + +/** + * Error class for passing custom error details to Flutter via a thrown PlatformException. + * @property code The error code. + * @property message The error message. + * @property details The error details. Must be a datatype supported by the api codec. + */ +class FlutterError ( + val code: String, + override val message: String? = null, + val details: Any? = null +) : Throwable() + +/** Generated class from Pigeon that represents data sent in messages. */ +data class DisplayInfoMessage ( + val currentRate: Double? = null, + val maxRate: Double? = null, + val minRate: Double? = null, + val supportedRates: List? = null, + val isVariableRefreshRate: Boolean? = null, + val engineTargetRate: Double? = null, + val iosProMotionEnabled: Boolean? = null, + val androidApiLevel: Long? = null, + val isLowPowerMode: Boolean? = null, + val thermalStateIndex: Long? = null, + val hasAdaptiveRefreshRate: Boolean? = null, + val displayServer: String? = null, + val monitorCount: Long? = null +) + { + companion object { + fun fromList(pigeonVar_list: List): DisplayInfoMessage { + val currentRate = pigeonVar_list[0] as Double? + val maxRate = pigeonVar_list[1] as Double? + val minRate = pigeonVar_list[2] as Double? + val supportedRates = pigeonVar_list[3] as List? + val isVariableRefreshRate = pigeonVar_list[4] as Boolean? + val engineTargetRate = pigeonVar_list[5] as Double? + val iosProMotionEnabled = pigeonVar_list[6] as Boolean? + val androidApiLevel = pigeonVar_list[7] as Long? + val isLowPowerMode = pigeonVar_list[8] as Boolean? + val thermalStateIndex = pigeonVar_list[9] as Long? + val hasAdaptiveRefreshRate = pigeonVar_list[10] as Boolean? + val displayServer = pigeonVar_list[11] as String? + val monitorCount = pigeonVar_list[12] as Long? + return DisplayInfoMessage(currentRate, maxRate, minRate, supportedRates, isVariableRefreshRate, engineTargetRate, iosProMotionEnabled, androidApiLevel, isLowPowerMode, thermalStateIndex, hasAdaptiveRefreshRate, displayServer, monitorCount) + } + } + fun toList(): List { + return listOf( + currentRate, + maxRate, + minRate, + supportedRates, + isVariableRefreshRate, + engineTargetRate, + iosProMotionEnabled, + androidApiLevel, + isLowPowerMode, + thermalStateIndex, + hasAdaptiveRefreshRate, + displayServer, + monitorCount, + ) + } +} +private open class RefreshRateApiPigeonCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return when (type) { + 129.toByte() -> { + return (readValue(buffer) as? List)?.let { + DisplayInfoMessage.fromList(it) + } + } + else -> super.readValueOfType(type, buffer) + } + } + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + when (value) { + is DisplayInfoMessage -> { + stream.write(129) + writeValue(stream, value.toList()) + } + else -> super.writeValue(stream, value) + } + } +} + +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface RefreshRateHostApi { + fun getDisplayInfo(): DisplayInfoMessage + fun enable() + fun disable() + fun preferMax() + fun preferDefault() + fun matchContent(fps: Double) + fun boost(durationMs: Long) + fun setCategory(categoryIndex: Long) + fun setTouchBoost(enabled: Boolean) + fun isSupported(): Boolean + + companion object { + /** The codec used by RefreshRateHostApi. */ + val codec: MessageCodec by lazy { + RefreshRateApiPigeonCodec() + } + /** Sets up an instance of `RefreshRateHostApi` to handle messages through the `binaryMessenger`. */ + @JvmOverloads + fun setUp(binaryMessenger: BinaryMessenger, api: RefreshRateHostApi?, messageChannelSuffix: String = "") { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.getDisplayInfo$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + listOf(api.getDisplayInfo()) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.enable$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.enable() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.disable$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.disable() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.preferMax$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.preferMax() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.preferDefault$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.preferDefault() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.matchContent$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val fpsArg = args[0] as Double + val wrapped: List = try { + api.matchContent(fpsArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.boost$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val durationMsArg = args[0] as Long + val wrapped: List = try { + api.boost(durationMsArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.setCategory$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val categoryIndexArg = args[0] as Long + val wrapped: List = try { + api.setCategory(categoryIndexArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.setTouchBoost$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val enabledArg = args[0] as Boolean + val wrapped: List = try { + api.setTouchBoost(enabledArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.isSupported$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + listOf(api.isSupported()) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} +/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */ +class RefreshRateFlutterApi(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") { + companion object { + /** The codec used by RefreshRateFlutterApi. */ + val codec: MessageCodec by lazy { + RefreshRateApiPigeonCodec() + } + } + fun onDisplayInfoChanged(infoArg: DisplayInfoMessage, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.refresh_rate.RefreshRateFlutterApi.onDisplayInfoChanged$separatedMessageChannelSuffix" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(infoArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) + } + } + } +} diff --git a/third_party/refresh_rate/example/README.md b/third_party/refresh_rate/example/README.md new file mode 100644 index 00000000..2a77dc4a --- /dev/null +++ b/third_party/refresh_rate/example/README.md @@ -0,0 +1,17 @@ +# refresh_rate_example + +A new Flutter project. + +## 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: + +- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter) +- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources) + +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/third_party/refresh_rate/example/analysis_options.yaml b/third_party/refresh_rate/example/analysis_options.yaml new file mode 100644 index 00000000..0d290213 --- /dev/null +++ b/third_party/refresh_rate/example/analysis_options.yaml @@ -0,0 +1,28 @@ +# 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 diff --git a/third_party/refresh_rate/example/android/app/build.gradle.kts b/third_party/refresh_rate/example/android/app/build.gradle.kts new file mode 100644 index 00000000..ea59d5c0 --- /dev/null +++ b/third_party/refresh_rate/example/android/app/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("com.android.application") + id("kotlin-android") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "in.qoder.refresh_rate_example" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "in.qoder.refresh_rate_example" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +flutter { + source = "../.." +} diff --git a/third_party/refresh_rate/example/android/app/src/debug/AndroidManifest.xml b/third_party/refresh_rate/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 00000000..399f6981 --- /dev/null +++ b/third_party/refresh_rate/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/third_party/refresh_rate/example/android/app/src/main/AndroidManifest.xml b/third_party/refresh_rate/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..9d5b4340 --- /dev/null +++ b/third_party/refresh_rate/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/third_party/refresh_rate/example/android/app/src/main/kotlin/in/qoder/refresh_rate_example/MainActivity.kt b/third_party/refresh_rate/example/android/app/src/main/kotlin/in/qoder/refresh_rate_example/MainActivity.kt new file mode 100644 index 00000000..5b5dd1d0 --- /dev/null +++ b/third_party/refresh_rate/example/android/app/src/main/kotlin/in/qoder/refresh_rate_example/MainActivity.kt @@ -0,0 +1,5 @@ +package `in`.qoder.refresh_rate_example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() diff --git a/third_party/refresh_rate/example/android/app/src/main/res/drawable-v21/launch_background.xml b/third_party/refresh_rate/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 00000000..f74085f3 --- /dev/null +++ b/third_party/refresh_rate/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/third_party/refresh_rate/example/android/app/src/main/res/drawable/launch_background.xml b/third_party/refresh_rate/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 00000000..304732f8 --- /dev/null +++ b/third_party/refresh_rate/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/third_party/refresh_rate/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/third_party/refresh_rate/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/third_party/refresh_rate/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/third_party/refresh_rate/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/third_party/refresh_rate/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/android/app/src/main/res/values-night/styles.xml b/third_party/refresh_rate/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 00000000..06952be7 --- /dev/null +++ b/third_party/refresh_rate/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/third_party/refresh_rate/example/android/app/src/main/res/values/styles.xml b/third_party/refresh_rate/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..cb1ef880 --- /dev/null +++ b/third_party/refresh_rate/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/third_party/refresh_rate/example/android/app/src/profile/AndroidManifest.xml b/third_party/refresh_rate/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 00000000..399f6981 --- /dev/null +++ b/third_party/refresh_rate/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/third_party/refresh_rate/example/android/build.gradle.kts b/third_party/refresh_rate/example/android/build.gradle.kts new file mode 100644 index 00000000..dbee657b --- /dev/null +++ b/third_party/refresh_rate/example/android/build.gradle.kts @@ -0,0 +1,24 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/third_party/refresh_rate/example/android/gradle.properties b/third_party/refresh_rate/example/android/gradle.properties new file mode 100644 index 00000000..fbee1d8c --- /dev/null +++ b/third_party/refresh_rate/example/android/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true diff --git a/third_party/refresh_rate/example/android/gradle/wrapper/gradle-wrapper.properties b/third_party/refresh_rate/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..e4ef43fb --- /dev/null +++ b/third_party/refresh_rate/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip diff --git a/third_party/refresh_rate/example/android/settings.gradle.kts b/third_party/refresh_rate/example/android/settings.gradle.kts new file mode 100644 index 00000000..ca7fe065 --- /dev/null +++ b/third_party/refresh_rate/example/android/settings.gradle.kts @@ -0,0 +1,26 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.11.1" apply false + id("org.jetbrains.kotlin.android") version "2.2.20" apply false +} + +include(":app") diff --git a/third_party/refresh_rate/example/integration_test/screenshot_test.dart b/third_party/refresh_rate/example/integration_test/screenshot_test.dart new file mode 100644 index 00000000..610c8c09 --- /dev/null +++ b/third_party/refresh_rate/example/integration_test/screenshot_test.dart @@ -0,0 +1,150 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:refresh_rate/refresh_rate.dart'; + +import 'package:refresh_rate_example/main.dart'; + +/// Waits for [finder] to match at least one widget, polling every 100 ms. +Future _waitFor( + WidgetTester tester, + Finder finder, { + Duration timeout = const Duration(seconds: 5), +}) async { + final deadline = DateTime.now().add(timeout); + while (!finder.evaluate().isNotEmpty) { + if (DateTime.now().isAfter(deadline)) return; + await tester.pump(const Duration(milliseconds: 100)); + } + await tester.pump(const Duration(milliseconds: 200)); +} + +/// Converts the Flutter surface to an image, pumps one frame, and +/// takes a named screenshot via the integration test binding. +Future _captureScreenshot( + IntegrationTestWidgetsFlutterBinding binding, + WidgetTester tester, + String name, +) async { + await binding.convertFlutterSurfaceToImage(); + await tester.pump(const Duration(milliseconds: 200)); + await binding.takeScreenshot(name); +} + +// ── Mock overlay widgets ───────────────────────────────────────────────────── +// The real FPS overlay shows "0 FPS" during integration tests because +// pump() doesn't generate real frame timings. These mock widgets render +// hardcoded values that represent a realistic 120Hz device scenario. + +/// Mock FPS badge — identical styling to FpsOverlayWidget (showFPS) with +/// a hardcoded value showing a healthy 120 FPS readout. +Widget _mockFpsOverlay() { + return Positioned( + top: 0, + right: 8, + child: SafeArea( + child: Padding( + padding: const EdgeInsets.only(top: 4), + child: IgnorePointer( + child: Material( + type: MaterialType.transparency, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: const Color(0xDD000000), + borderRadius: BorderRadius.circular(6), + ), + child: const Text( + '120 FPS', + style: TextStyle( + color: Color(0xFF4CAF50), // green — hitting target + fontSize: 13, + fontWeight: FontWeight.bold, + fontFeatures: [FontFeature.tabularFigures()], + ), + ), + ), + ), + ), + ), + ), + ); +} + +/// Mock Hz badge — identical styling to HzOverlayWidget. +Widget _mockHzOverlay() { + return Positioned( + top: 0, + right: 8, + child: SafeArea( + child: Padding( + padding: const EdgeInsets.only(top: 4), + child: IgnorePointer( + child: Material( + type: MaterialType.transparency, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: const Color(0xDD000000), + borderRadius: BorderRadius.circular(6), + ), + child: const Text( + '120Hz', + style: TextStyle( + color: Color(0xFF64B5F6), + fontSize: 13, + fontWeight: FontWeight.bold, + fontFeatures: [FontFeature.tabularFigures()], + ), + ), + ), + ), + ), + ), + ), + ); +} + +/// Wraps the example app with a mock overlay [widget] on top. +Widget _appWithOverlay(Widget overlay) { + return Directionality( + textDirection: TextDirection.ltr, + child: Stack( + children: [ + const RefreshRateExampleApp(), + overlay, + ], + ), + ); +} + +void main() { + final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + // ── 1. overlay — FPS counter showing 120 FPS in green ──────────────────── + testWidgets('screenshot: fps', (tester) async { + await tester.pumpWidget(_appWithOverlay(_mockFpsOverlay())); + await tester.pump(const Duration(seconds: 1)); + + await _waitFor(tester, find.text('Diagnostic Console')); + + // Enable high refresh so the hero panel shows real 120 Hz data. + RefreshRate.enable(); + await tester.pump(const Duration(seconds: 2)); + + await _captureScreenshot(binding, tester, 'fps'); + }); + + // ── 2. before_after — Hz badge showing 120Hz ──────────────────────────── + testWidgets('screenshot: hz', (tester) async { + await tester.pumpWidget(_appWithOverlay(_mockHzOverlay())); + await tester.pump(const Duration(seconds: 1)); + + await _waitFor(tester, find.text('Diagnostic Console')); + + RefreshRate.enable(); + await tester.pump(const Duration(seconds: 2)); + + await _captureScreenshot(binding, tester, 'hz'); + }); +} diff --git a/third_party/refresh_rate/example/ios/Flutter/AppFrameworkInfo.plist b/third_party/refresh_rate/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 00000000..391a902b --- /dev/null +++ b/third_party/refresh_rate/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + + diff --git a/third_party/refresh_rate/example/ios/Flutter/Debug.xcconfig b/third_party/refresh_rate/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 00000000..ec97fc6f --- /dev/null +++ b/third_party/refresh_rate/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/third_party/refresh_rate/example/ios/Flutter/Release.xcconfig b/third_party/refresh_rate/example/ios/Flutter/Release.xcconfig new file mode 100644 index 00000000..c4855bfe --- /dev/null +++ b/third_party/refresh_rate/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/third_party/refresh_rate/example/ios/Podfile b/third_party/refresh_rate/example/ios/Podfile new file mode 100644 index 00000000..620e46eb --- /dev/null +++ b/third_party/refresh_rate/example/ios/Podfile @@ -0,0 +1,43 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '13.0' + +# 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', '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 Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_ios_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_ios_build_settings(target) + end +end diff --git a/third_party/refresh_rate/example/ios/Podfile.lock b/third_party/refresh_rate/example/ios/Podfile.lock new file mode 100644 index 00000000..7b48a767 --- /dev/null +++ b/third_party/refresh_rate/example/ios/Podfile.lock @@ -0,0 +1,28 @@ +PODS: + - Flutter (1.0.0) + - integration_test (0.0.1): + - Flutter + - refresh_rate (1.0.2): + - Flutter + +DEPENDENCIES: + - Flutter (from `Flutter`) + - integration_test (from `.symlinks/plugins/integration_test/ios`) + - refresh_rate (from `.symlinks/plugins/refresh_rate/ios`) + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + integration_test: + :path: ".symlinks/plugins/integration_test/ios" + refresh_rate: + :path: ".symlinks/plugins/refresh_rate/ios" + +SPEC CHECKSUMS: + Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 + integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573 + refresh_rate: 7b70fe15ba35859a04c4ed7145ef5373073c9498 + +PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e + +COCOAPODS: 1.16.2 diff --git a/third_party/refresh_rate/example/ios/Runner.xcodeproj/project.pbxproj b/third_party/refresh_rate/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..a5d8e17e --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,738 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 408442DF8837659AF5CB83DB /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7106237642409D042B52BB66 /* Pods_RunnerTests.framework */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + F287BBF60D9D2B9168604931 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6D93A38BCF1D017DD70781D7 /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 1E1F0922F0E206C0A75DF6D6 /* 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 = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 6D93A38BCF1D017DD70781D7 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6DC24DB3096513F8AA853A09 /* 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 = ""; }; + 7106237642409D042B52BB66 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 72D8257624B60955295C8C97 /* 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 = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 8FD1DB725A5CEDEB0388717B /* 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 = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F05CCA270615CA628AE3ACDF /* 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 = ""; }; + F6EABD99D945E14E0AD67068 /* 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 = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 213E13E82D28B3952759D378 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 408442DF8837659AF5CB83DB /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F287BBF60D9D2B9168604931 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 8B93923519CFCE8A9F0A16A1 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 6D93A38BCF1D017DD70781D7 /* Pods_Runner.framework */, + 7106237642409D042B52BB66 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + D39C39D29B0F3A68A4AAA3D7 /* Pods */, + 8B93923519CFCE8A9F0A16A1 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; + D39C39D29B0F3A68A4AAA3D7 /* Pods */ = { + isa = PBXGroup; + children = ( + 8FD1DB725A5CEDEB0388717B /* Pods-Runner.debug.xcconfig */, + 1E1F0922F0E206C0A75DF6D6 /* Pods-Runner.release.xcconfig */, + 72D8257624B60955295C8C97 /* Pods-Runner.profile.xcconfig */, + F6EABD99D945E14E0AD67068 /* Pods-RunnerTests.debug.xcconfig */, + 6DC24DB3096513F8AA853A09 /* Pods-RunnerTests.release.xcconfig */, + F05CCA270615CA628AE3ACDF /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 1598A1C76FC46748D8686F89 /* [CP] Check Pods Manifest.lock */, + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + 213E13E82D28B3952759D378 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 81F4AE0F9E3D19C5B6DFDAD1 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 91C2B0F87759B0619DD56467 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 1598A1C76FC46748D8686F89 /* [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; + }; + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 81F4AE0F9E3D19C5B6DFDAD1 /* [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; + }; + 91C2B0F87759B0619DD56467 /* [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; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + 7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = ALF7HY2G9Q; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = in.qoder.refreshRateExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F6EABD99D945E14E0AD67068 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = in.qoder.refreshRateExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6DC24DB3096513F8AA853A09 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = in.qoder.refreshRateExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F05CCA270615CA628AE3ACDF /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = in.qoder.refreshRateExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = ALF7HY2G9Q; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = in.qoder.refreshRateExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = ALF7HY2G9Q; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = in.qoder.refreshRateExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/third_party/refresh_rate/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/third_party/refresh_rate/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/third_party/refresh_rate/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/third_party/refresh_rate/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/third_party/refresh_rate/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/third_party/refresh_rate/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/third_party/refresh_rate/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/third_party/refresh_rate/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..e3773d42 --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/third_party/refresh_rate/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/third_party/refresh_rate/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..21a3cc14 --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/third_party/refresh_rate/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/third_party/refresh_rate/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/third_party/refresh_rate/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/third_party/refresh_rate/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/third_party/refresh_rate/example/ios/Runner/AppDelegate.swift b/third_party/refresh_rate/example/ios/Runner/AppDelegate.swift new file mode 100644 index 00000000..c30b367e --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,16 @@ +import Flutter +import UIKit + +@main +@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } + + func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { + GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) + } +} diff --git a/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d36b1fab --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 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 literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..797d452e458972bab9d994556c8305db4c827017 GIT binary patch literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed2d933e1120817fe9182483a228007b18ab6ae GIT binary patch literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd7b0099ca80c806f8fe495613e8d6c69460d76 GIT binary patch literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fe730945a01f64a61e2235dbe3f45b08f7729182 GIT binary patch literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..502f463a9bc882b461c96aadf492d1729e49e725 GIT binary patch literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec303439225b78712f49115768196d8d76f6790 GIT binary patch literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f5fea27c705180eb716271f41b582e76dcbd90 GIT binary patch literal 1674 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 literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0467bf12aa4d28f374bb26596605a46dcbb3e7c8 GIT binary patch literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 00000000..0bedcf2f --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 00000000..89c2725b --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/third_party/refresh_rate/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/third_party/refresh_rate/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..f2e259c7 --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/third_party/refresh_rate/example/ios/Runner/Base.lproj/Main.storyboard b/third_party/refresh_rate/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 00000000..f3c28516 --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/third_party/refresh_rate/example/ios/Runner/DebugProfile.entitlements b/third_party/refresh_rate/example/ios/Runner/DebugProfile.entitlements new file mode 100644 index 00000000..23c52e48 --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + get-task-allow + + + diff --git a/third_party/refresh_rate/example/ios/Runner/Info.plist b/third_party/refresh_rate/example/ios/Runner/Info.plist new file mode 100644 index 00000000..1711d1be --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner/Info.plist @@ -0,0 +1,70 @@ + + + + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Refresh Rate Example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + refresh_rate_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + UIWindowScene + UISceneConfigurationName + flutter + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/third_party/refresh_rate/example/ios/Runner/Release.entitlements b/third_party/refresh_rate/example/ios/Runner/Release.entitlements new file mode 100644 index 00000000..e89b7f32 --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/third_party/refresh_rate/example/ios/Runner/Runner-Bridging-Header.h b/third_party/refresh_rate/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 00000000..308a2a56 --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/third_party/refresh_rate/example/ios/Runner/SceneDelegate.swift b/third_party/refresh_rate/example/ios/Runner/SceneDelegate.swift new file mode 100644 index 00000000..b9ce8ea2 --- /dev/null +++ b/third_party/refresh_rate/example/ios/Runner/SceneDelegate.swift @@ -0,0 +1,6 @@ +import Flutter +import UIKit + +class SceneDelegate: FlutterSceneDelegate { + +} diff --git a/third_party/refresh_rate/example/ios/RunnerTests/RunnerTests.swift b/third_party/refresh_rate/example/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..86a7c3b1 --- /dev/null +++ b/third_party/refresh_rate/example/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +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/third_party/refresh_rate/example/lib/main.dart b/third_party/refresh_rate/example/lib/main.dart new file mode 100644 index 00000000..f23c8c97 --- /dev/null +++ b/third_party/refresh_rate/example/lib/main.dart @@ -0,0 +1,11 @@ +import 'package:flutter/widgets.dart'; +import 'package:refresh_rate/refresh_rate.dart'; + +import 'src/example_app.dart'; +export 'src/example_app.dart'; + +void main() { + WidgetsFlutterBinding.ensureInitialized(); + RefreshRate.enable(); + runApp(const RefreshRateExampleApp()); +} diff --git a/third_party/refresh_rate/example/lib/src/example_app.dart b/third_party/refresh_rate/example/lib/src/example_app.dart new file mode 100644 index 00000000..06fb3ba0 --- /dev/null +++ b/third_party/refresh_rate/example/lib/src/example_app.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +import 'example_home.dart'; +import 'example_theme.dart'; + +class RefreshRateExampleApp extends StatelessWidget { + const RefreshRateExampleApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'refresh_rate example', + debugShowCheckedModeBanner: false, + theme: buildExampleTheme(), + home: const ExampleHome(), + ); + } +} diff --git a/third_party/refresh_rate/example/lib/src/example_home.dart b/third_party/refresh_rate/example/lib/src/example_home.dart new file mode 100644 index 00000000..7b9fe61c --- /dev/null +++ b/third_party/refresh_rate/example/lib/src/example_home.dart @@ -0,0 +1,422 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:refresh_rate/refresh_rate.dart'; + +import 'models/action_spec.dart'; +import 'models/info_item.dart'; +import 'sections/action_panel.dart'; +import 'sections/benchmark_panel.dart'; +import 'sections/display_info_panel.dart'; +import 'sections/hero_panel.dart'; +import 'sections/scroll_test_panel.dart'; +import 'sections/status_panel.dart'; + +class ExampleHome extends StatefulWidget { + const ExampleHome({super.key}); + + @override + State createState() => _ExampleHomeState(); +} + +class _ExampleHomeState extends State { + RefreshRateSession? _session; + SessionReport? _lastReport; + String _status = 'System nominal'; + StreamSubscription? _infoSubscription; + + @override + void initState() { + super.initState(); + _infoSubscription = RefreshRate.onChanged.listen((_) { + if (mounted) setState(() {}); + }); + RefreshRate.refresh().then((_) { + if (mounted) setState(() {}); + }); + } + + @override + void dispose() { + _infoSubscription?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final info = RefreshRate.info; + final width = MediaQuery.sizeOf(context).width; + final wideLayout = width >= 1040; + final largeHero = width >= 760; + final frameBudgetMs = info.currentRate > 0 ? 1000 / info.currentRate : 0.0; + final maxBudgetMs = info.maxRate > 0 ? 1000 / info.maxRate : 0.0; + final supportedRatesText = _supportedRatesText(info); + final liveStateColor = _liveStateColor(context, info); + + final displayInfoItems = [ + InfoItem('Current rate', '${info.currentRate.toStringAsFixed(1)} Hz'), + InfoItem('Max rate', '${info.maxRate.toStringAsFixed(1)} Hz'), + InfoItem('Min rate', '${info.minRate.toStringAsFixed(1)} Hz'), + InfoItem('Supported rates', supportedRatesText), + InfoItem('VRR / LTPO', info.isVariableRefreshRate ? 'Yes' : 'No'), + InfoItem( + 'ProMotion ready', + RefreshRate.isProMotionReady ? 'Ready' : 'Unavailable', + ), + InfoItem( + 'Low power mode', + RefreshRate.isLowPowerMode ? 'Enabled' : 'Off', + ), + InfoItem('Thermal state', RefreshRate.thermalState.name), + InfoItem('Android API', '${info.androidApiLevel ?? 'n/a'}'), + InfoItem('Display server', info.displayServer ?? 'n/a'), + InfoItem('Monitor count', '${info.monitorCount}'), + InfoItem( + 'Engine target', '${info.engineTargetRate.toStringAsFixed(1)} Hz'), + ]; + + return Scaffold( + body: DecoratedBox( + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Color(0xFF151515), Color(0xFF101010), Color(0xFF131313)], + ), + ), + child: SafeArea( + child: SingleChildScrollView( + padding: EdgeInsets.fromLTRB(16, 12, 16, width >= 760 ? 24 : 16), + child: Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 1320), + child: wideLayout + ? Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 7, + child: Column( + children: [ + HeroPanel( + info: info, + status: _status, + frameBudgetMs: frameBudgetMs, + maxBudgetMs: maxBudgetMs, + liveStateColor: liveStateColor, + largeHero: largeHero, + supportedRatesText: supportedRatesText, + ), + const SizedBox(height: 16), + DisplayInfoPanel(items: displayInfoItems), + const SizedBox(height: 16), + ActionPanel( + eyebrow: 'Control', + title: 'Rate Requests', + subtitle: + 'Exercise the plugin APIs directly and verify device behavior against the live telemetry above.', + actions: _controlActions(), + ), + const SizedBox(height: 16), + ScrollTestPanel(info: info), + ], + ), + ), + const SizedBox(width: 16), + Expanded( + flex: 4, + child: Column( + children: [ + ActionPanel( + eyebrow: 'Overlay', + title: 'Verification HUD', + subtitle: + 'Toggle lightweight or full in-app overlays to validate frame pacing without leaving the example app.', + actions: _overlayActions(), + footer: _overlayFooter(), + ), + const SizedBox(height: 16), + BenchmarkPanel( + actions: _benchmarkActions(), + reportItems: _lastReport == null + ? null + : _reportItems(_lastReport!), + ), + const SizedBox(height: 16), + StatusPanel(status: _status), + ], + ), + ), + ], + ) + : Column( + children: [ + HeroPanel( + info: info, + status: _status, + frameBudgetMs: frameBudgetMs, + maxBudgetMs: maxBudgetMs, + liveStateColor: liveStateColor, + largeHero: largeHero, + supportedRatesText: supportedRatesText, + ), + const SizedBox(height: 16), + DisplayInfoPanel(items: displayInfoItems), + const SizedBox(height: 16), + ActionPanel( + eyebrow: 'Control', + title: 'Rate Requests', + subtitle: + 'Exercise the plugin APIs directly and verify device behavior against the live telemetry above.', + actions: _controlActions(), + ), + const SizedBox(height: 16), + ActionPanel( + eyebrow: 'Overlay', + title: 'Verification HUD', + subtitle: + 'Toggle lightweight or full in-app overlays to validate frame pacing without leaving the example app.', + actions: _overlayActions(), + footer: _overlayFooter(), + ), + const SizedBox(height: 16), + BenchmarkPanel( + actions: _benchmarkActions(), + reportItems: _lastReport == null + ? null + : _reportItems(_lastReport!), + ), + const SizedBox(height: 16), + ScrollTestPanel(info: info), + const SizedBox(height: 16), + StatusPanel(status: _status), + ], + ), + ), + ), + ), + ), + ), + ); + } + + List _controlActions() { + return [ + ActionSpec( + label: 'ENABLE', + accent: const Color(0xFF36FF8B), + outlined: false, + onTap: () { + RefreshRate.enable(); + _setStatus('Peak mode requested'); + }, + ), + ActionSpec( + label: 'DISABLE', + accent: const Color(0xFFFFBA20), + outlined: true, + onTap: () { + RefreshRate.disable(); + _setStatus('Requests cleared'); + }, + ), + ActionSpec( + label: 'PREFER MAX', + accent: const Color(0xFF00F0FF), + outlined: true, + onTap: () { + RefreshRate.preferMax(); + _setStatus('Maximum refresh preferred'); + }, + ), + ActionSpec( + label: 'DEFAULT', + accent: const Color(0xFFB9CACB), + outlined: true, + onTap: () { + RefreshRate.preferDefault(); + _setStatus('OS-managed default restored'); + }, + ), + ActionSpec( + label: 'MATCH 24FPS', + accent: const Color(0xFF00F0FF), + outlined: false, + onTap: () { + RefreshRate.matchContent(24.0); + _setStatus('Matched content to 24fps cadence'); + }, + ), + ActionSpec( + label: 'BOOST 3S', + accent: const Color(0xFFFFBA20), + outlined: false, + onTap: () { + RefreshRate.boost(const Duration(seconds: 3)); + _setStatus('Temporary boost active for 3 seconds'); + }, + ), + ActionSpec( + label: 'CATEGORY HIGH', + accent: const Color(0xFF36FF8B), + outlined: true, + onTap: () { + RefreshRate.category(RateCategory.high); + _setStatus('Android category set to high'); + }, + ), + ActionSpec( + label: 'TOUCH BOOST', + accent: const Color(0xFF00F0FF), + outlined: true, + onTap: () { + RefreshRate.setTouchBoost(true); + _setStatus('Touch boost enabled'); + }, + ), + ]; + } + + List _overlayActions() { + return [ + ActionSpec( + label: 'SHOW FPS', + accent: const Color(0xFF00F0FF), + outlined: true, + onTap: () { + RefreshRate.showFPS(); + _setStatus('FPS overlay shown'); + }, + ), + ActionSpec( + label: 'SHOW HZ', + accent: const Color(0xFF36FF8B), + outlined: true, + onTap: () { + RefreshRate.showHz(); + _setStatus('Hz overlay shown'); + }, + ), + ActionSpec( + label: 'FULL OVERLAY', + accent: const Color(0xFFFFBA20), + outlined: false, + onTap: () { + RefreshRate.showOverlay(); + _setStatus('Full overlay mounted'); + }, + ), + ActionSpec( + label: 'HIDE', + accent: const Color(0xFFB9CACB), + outlined: true, + onTap: () { + RefreshRate.hideOverlay(); + _setStatus('Overlay hidden'); + }, + ), + ]; + } + + List _benchmarkActions() { + return [ + ActionSpec( + label: _session == null ? 'START SESSION' : 'SESSION ACTIVE', + accent: const Color(0xFF36FF8B), + outlined: _session != null, + onTap: _session == null + ? () { + setState(() { + _session = RefreshRate.startSession('example_scroll'); + _status = 'Benchmark session running'; + }); + } + : null, + ), + ActionSpec( + label: 'END SESSION', + accent: const Color(0xFFFFBA20), + outlined: true, + onTap: _session == null + ? null + : () async { + final activeSession = _session; + if (activeSession == null) return; + final report = await activeSession.end(); + if (!mounted) return; + setState(() { + _lastReport = report; + _session = null; + _status = 'Session ended: ${report.verdict.name}'; + }); + }, + ), + ]; + } + + Widget _overlayFooter() { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), + decoration: BoxDecoration( + color: const Color(0xFF0E0E0E), + borderRadius: BorderRadius.circular(14), + ), + child: Row( + children: [ + Container( + width: 9, + height: 9, + decoration: BoxDecoration( + color: RefreshRate.isOverlayVisible + ? const Color(0xFF36FF8B) + : const Color(0xFF3B494B), + shape: BoxShape.circle, + ), + ), + const SizedBox(width: 10), + Text( + RefreshRate.isOverlayVisible + ? 'Overlay currently visible' + : 'Overlay currently hidden', + style: const TextStyle( + color: Color(0xFFB9CACB), + ), + ), + ], + ), + ); + } + + List _reportItems(SessionReport report) { + return [ + InfoItem('Verdict', report.verdict.name), + InfoItem('Bottleneck', report.likelyBottleneck.name), + InfoItem('Avg FPS', report.avgFps.toStringAsFixed(1)), + InfoItem('1% Low', report.onePercentLowFps.toStringAsFixed(1)), + InfoItem( + 'Missed frames', '${report.missedFramePercent.toStringAsFixed(1)}%'), + InfoItem('Valid duration', '${report.validDuration.inMilliseconds} ms'), + InfoItem('Excluded', '${report.excludedDuration.inMilliseconds} ms'), + ]; + } + + void _setStatus(String value) => setState(() => _status = value); + + String _supportedRatesText(DisplayInfo info) { + if (info.supportedRates.isEmpty) return 'n/a'; + return info.supportedRates + .map((rate) => '${rate.toStringAsFixed(0)}Hz') + .join(' • '); + } + + Color _liveStateColor(BuildContext context, DisplayInfo info) { + if (RefreshRate.isLowPowerMode) return const Color(0xFFFFBA20); + if (RefreshRate.thermalState.name != 'nominal') { + return const Color(0xFFFFBA20); + } + if (info.currentRate >= info.maxRate && info.maxRate > 0) { + return const Color(0xFF36FF8B); + } + return Theme.of(context).colorScheme.primary; + } +} diff --git a/third_party/refresh_rate/example/lib/src/example_theme.dart b/third_party/refresh_rate/example/lib/src/example_theme.dart new file mode 100644 index 00000000..709d0b45 --- /dev/null +++ b/third_party/refresh_rate/example/lib/src/example_theme.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; + +ThemeData buildExampleTheme() { + const base = Color(0xFF131313); + const panelHigh = Color(0xFF2A2A2A); + const cyan = Color(0xFF00F0FF); + const emerald = Color(0xFF36FF8B); + const amber = Color(0xFFFFBA20); + const text = Color(0xFFE5E2E1); + const muted = Color(0xFFB9CACB); + + final scheme = const ColorScheme.dark( + brightness: Brightness.dark, + surface: base, + primary: cyan, + secondary: emerald, + tertiary: amber, + onSurface: text, + onPrimary: Color(0xFF00363A), + onSecondary: Color(0xFF003919), + onTertiary: Color(0xFF412D00), + outline: Color(0xFF3B494B), + ); + + return ThemeData( + useMaterial3: true, + colorScheme: scheme, + scaffoldBackgroundColor: base, + canvasColor: base, + textTheme: ThemeData.dark().textTheme.apply( + bodyColor: text, + displayColor: text, + ), + chipTheme: ChipThemeData( + backgroundColor: panelHigh, + selectedColor: cyan.withValues(alpha: 0.16), + side: BorderSide.none, + labelStyle: const TextStyle( + color: text, + fontSize: 11, + fontWeight: FontWeight.w600, + letterSpacing: 0.3, + ), + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + filledButtonTheme: FilledButtonThemeData( + style: FilledButton.styleFrom( + backgroundColor: panelHigh, + foregroundColor: text, + elevation: 0, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(14), + ), + textStyle: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w700, + letterSpacing: 0.8, + ), + ), + ), + outlinedButtonTheme: OutlinedButtonThemeData( + style: OutlinedButton.styleFrom( + foregroundColor: text, + side: BorderSide(color: cyan.withValues(alpha: 0.38)), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(14), + ), + textStyle: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w700, + letterSpacing: 0.8, + ), + ), + ), + dividerColor: muted.withValues(alpha: 0.08), + ); +} diff --git a/third_party/refresh_rate/example/lib/src/models/action_spec.dart b/third_party/refresh_rate/example/lib/src/models/action_spec.dart new file mode 100644 index 00000000..a467a437 --- /dev/null +++ b/third_party/refresh_rate/example/lib/src/models/action_spec.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class ActionSpec { + const ActionSpec({ + required this.label, + required this.accent, + required this.outlined, + required this.onTap, + }); + + final String label; + final Color accent; + final bool outlined; + final VoidCallback? onTap; +} diff --git a/third_party/refresh_rate/example/lib/src/models/info_item.dart b/third_party/refresh_rate/example/lib/src/models/info_item.dart new file mode 100644 index 00000000..ada745bb --- /dev/null +++ b/third_party/refresh_rate/example/lib/src/models/info_item.dart @@ -0,0 +1,6 @@ +class InfoItem { + const InfoItem(this.label, this.value); + + final String label; + final String value; +} diff --git a/third_party/refresh_rate/example/lib/src/sections/action_panel.dart b/third_party/refresh_rate/example/lib/src/sections/action_panel.dart new file mode 100644 index 00000000..5661a85b --- /dev/null +++ b/third_party/refresh_rate/example/lib/src/sections/action_panel.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; + +import '../models/action_spec.dart'; +import '../ui/panel_widgets.dart'; + +class ActionPanel extends StatelessWidget { + const ActionPanel({ + super.key, + required this.eyebrow, + required this.title, + required this.subtitle, + required this.actions, + this.footer, + }); + + final String eyebrow; + final String title; + final String subtitle; + final List actions; + final Widget? footer; + + @override + Widget build(BuildContext context) { + return SurfacePanel( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PanelHeader( + eyebrow: eyebrow, + title: title, + subtitle: subtitle, + ), + const SizedBox(height: 14), + Wrap( + spacing: 10, + runSpacing: 10, + children: + actions.map((action) => ActionButton(spec: action)).toList(), + ), + if (footer != null) ...[ + const SizedBox(height: 14), + footer!, + ], + ], + ), + ); + } +} diff --git a/third_party/refresh_rate/example/lib/src/sections/benchmark_panel.dart b/third_party/refresh_rate/example/lib/src/sections/benchmark_panel.dart new file mode 100644 index 00000000..fc188045 --- /dev/null +++ b/third_party/refresh_rate/example/lib/src/sections/benchmark_panel.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; + +import '../models/action_spec.dart'; +import '../models/info_item.dart'; +import '../ui/panel_widgets.dart'; +import 'action_panel.dart'; + +class BenchmarkPanel extends StatelessWidget { + const BenchmarkPanel({ + super.key, + required this.actions, + required this.reportItems, + }); + + final List actions; + final List? reportItems; + + @override + Widget build(BuildContext context) { + return ActionPanel( + eyebrow: 'Benchmark', + title: 'Session Capture', + subtitle: + 'Run a named session, exercise scrolling or animations, then inspect verdict and missed-frame behavior.', + actions: actions, + footer: reportItems == null + ? Container( + width: double.infinity, + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: const Color(0xFF0E0E0E), + borderRadius: BorderRadius.circular(14), + ), + child: const Text( + 'No report captured yet. Start a session, interact with the scroll test, then end the session to inspect the results.', + style: TextStyle( + color: Color(0xFFB9CACB), + fontSize: 12, + height: 1.45, + ), + ), + ) + : ReportSummary(items: reportItems!), + ); + } +} diff --git a/third_party/refresh_rate/example/lib/src/sections/display_info_panel.dart b/third_party/refresh_rate/example/lib/src/sections/display_info_panel.dart new file mode 100644 index 00000000..5c8216da --- /dev/null +++ b/third_party/refresh_rate/example/lib/src/sections/display_info_panel.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; + +import '../models/info_item.dart'; +import '../ui/panel_widgets.dart'; + +class DisplayInfoPanel extends StatelessWidget { + const DisplayInfoPanel({ + super.key, + required this.items, + }); + + final List items; + + @override + Widget build(BuildContext context) { + return SurfacePanel( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const PanelHeader( + eyebrow: 'Telemetry', + title: 'Display Info', + subtitle: + 'Hardware capability, operating state, and platform-level diagnostics.', + ), + const SizedBox(height: 14), + LayoutBuilder( + builder: (context, constraints) { + final columns = constraints.maxWidth >= 900 + ? 3 + : constraints.maxWidth >= 560 + ? 2 + : 1; + return GridView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: items.length, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: columns, + crossAxisSpacing: 10, + mainAxisSpacing: 10, + mainAxisExtent: 76, + ), + itemBuilder: (context, index) => InfoTile(item: items[index]), + ); + }, + ), + ], + ), + ); + } +} diff --git a/third_party/refresh_rate/example/lib/src/sections/hero_panel.dart b/third_party/refresh_rate/example/lib/src/sections/hero_panel.dart new file mode 100644 index 00000000..76e40526 --- /dev/null +++ b/third_party/refresh_rate/example/lib/src/sections/hero_panel.dart @@ -0,0 +1,109 @@ +import 'package:flutter/material.dart'; +import 'package:refresh_rate/refresh_rate.dart'; + +import '../ui/panel_widgets.dart'; + +class HeroPanel extends StatelessWidget { + const HeroPanel({ + super.key, + required this.info, + required this.status, + required this.frameBudgetMs, + required this.maxBudgetMs, + required this.liveStateColor, + required this.largeHero, + required this.supportedRatesText, + }); + + final DisplayInfo info; + final String status; + final double frameBudgetMs; + final double maxBudgetMs; + final Color liveStateColor; + final bool largeHero; + final String supportedRatesText; + + @override + Widget build(BuildContext context) { + return SurfacePanel( + padding: const EdgeInsets.all(18), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Expanded( + child: PanelHeader( + eyebrow: 'refresh_rate v1.0', + title: 'Diagnostic Console', + subtitle: + 'Live instrumentation for display state, frame budget, control requests, and benchmark sessions.', + ), + ), + SignalBadge( + label: status.toUpperCase(), + color: liveStateColor, + ), + ], + ), + const SizedBox(height: 18), + Wrap( + spacing: 12, + runSpacing: 12, + children: [ + HeroMetric( + label: 'Current Rate', + value: '${info.currentRate.toStringAsFixed(1)} Hz', + caption: 'Observed display target', + accent: const Color(0xFF00F0FF), + wide: largeHero, + ), + HeroMetric( + label: 'Peak Capability', + value: '${info.maxRate.toStringAsFixed(1)} Hz', + caption: 'Device-reported maximum', + accent: const Color(0xFF36FF8B), + wide: largeHero, + ), + HeroMetric( + label: 'Frame Budget', + value: '${frameBudgetMs.toStringAsFixed(2)} ms', + caption: frameBudgetMs > 0 + ? 'Target at ${info.currentRate.toStringAsFixed(1)} Hz' + : 'Waiting for rate data', + accent: const Color(0xFFFFBA20), + wide: largeHero, + ), + ], + ), + const SizedBox(height: 18), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + TelemetryChip(label: 'Supported', value: supportedRatesText), + TelemetryChip( + label: 'VRR', + value: info.isVariableRefreshRate ? 'Enabled' : 'Fixed', + ), + TelemetryChip( + label: 'Low Power', + value: RefreshRate.isLowPowerMode ? 'On' : 'Off', + ), + TelemetryChip( + label: 'Thermal', + value: RefreshRate.thermalState.name, + ), + TelemetryChip( + label: 'Peak Budget', + value: maxBudgetMs > 0 + ? '${maxBudgetMs.toStringAsFixed(2)} ms' + : 'n/a', + ), + ], + ), + ], + ), + ); + } +} diff --git a/third_party/refresh_rate/example/lib/src/sections/scroll_test_panel.dart b/third_party/refresh_rate/example/lib/src/sections/scroll_test_panel.dart new file mode 100644 index 00000000..70f32338 --- /dev/null +++ b/third_party/refresh_rate/example/lib/src/sections/scroll_test_panel.dart @@ -0,0 +1,108 @@ +import 'package:flutter/material.dart'; +import 'package:refresh_rate/refresh_rate.dart'; + +import '../ui/panel_widgets.dart'; + +class ScrollTestPanel extends StatelessWidget { + const ScrollTestPanel({ + super.key, + required this.info, + }); + + final DisplayInfo info; + + @override + Widget build(BuildContext context) { + return SurfacePanel( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const PanelHeader( + eyebrow: 'Exercise', + title: 'Scroll Test', + subtitle: + 'Use this list to generate sustained motion and compare the on-screen experience against the live frame budget.', + ), + const SizedBox(height: 12), + Container( + height: 340, + decoration: BoxDecoration( + color: const Color(0xFF0E0E0E), + borderRadius: BorderRadius.circular(16), + ), + child: ListView.separated( + padding: const EdgeInsets.all(10), + itemCount: 30, + separatorBuilder: (_, __) => const SizedBox(height: 6), + itemBuilder: (context, index) { + final laneColor = index.isEven + ? const Color(0xFF00F0FF) + : const Color(0xFF36FF8B); + return Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 12, + ), + decoration: BoxDecoration( + color: const Color(0xFF171717), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + children: [ + Container( + width: 34, + height: 34, + decoration: BoxDecoration( + color: laneColor.withValues(alpha: 0.16), + borderRadius: BorderRadius.circular(10), + ), + alignment: Alignment.center, + child: Text( + '${index + 1}', + style: TextStyle( + color: laneColor, + fontWeight: FontWeight.w700, + ), + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Trace sample ${index + 1}', + style: const TextStyle( + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 2), + Text( + 'Target ${(info.maxRate > 0 ? info.maxRate : info.currentRate).toStringAsFixed(0)} Hz • verify pacing under continuous scroll.', + style: const TextStyle( + color: Color(0xFFB9CACB), + fontSize: 12, + ), + ), + ], + ), + ), + Text( + '${(index + 1) * 8} ms', + style: const TextStyle( + color: Color(0xFFB9CACB), + fontSize: 12, + fontFeatures: [FontFeature.tabularFigures()], + ), + ), + ], + ), + ); + }, + ), + ), + ], + ), + ); + } +} diff --git a/third_party/refresh_rate/example/lib/src/sections/status_panel.dart b/third_party/refresh_rate/example/lib/src/sections/status_panel.dart new file mode 100644 index 00000000..52070079 --- /dev/null +++ b/third_party/refresh_rate/example/lib/src/sections/status_panel.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; + +import '../ui/panel_widgets.dart'; + +class StatusPanel extends StatelessWidget { + const StatusPanel({ + super.key, + required this.status, + }); + + final String status; + + @override + Widget build(BuildContext context) { + return SurfacePanel( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const PanelHeader( + eyebrow: 'System', + title: 'Run Status', + subtitle: + 'Recent command state and what the console is currently signaling to the platform.', + ), + const SizedBox(height: 14), + Container( + width: double.infinity, + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: const Color(0xFF0E0E0E), + borderRadius: BorderRadius.circular(14), + ), + child: Text( + status, + style: const TextStyle( + color: Color(0xFFE5E2E1), + fontSize: 13, + fontWeight: FontWeight.w600, + height: 1.4, + ), + ), + ), + ], + ), + ); + } +} diff --git a/third_party/refresh_rate/example/lib/src/ui/panel_widgets.dart b/third_party/refresh_rate/example/lib/src/ui/panel_widgets.dart new file mode 100644 index 00000000..0f2d077b --- /dev/null +++ b/third_party/refresh_rate/example/lib/src/ui/panel_widgets.dart @@ -0,0 +1,377 @@ +import 'package:flutter/material.dart'; + +import '../models/action_spec.dart'; +import '../models/info_item.dart'; + +class SurfacePanel extends StatelessWidget { + const SurfacePanel({ + super.key, + required this.child, + this.padding = const EdgeInsets.all(16), + }); + + final Widget child; + final EdgeInsetsGeometry padding; + + @override + Widget build(BuildContext context) { + return DecoratedBox( + decoration: BoxDecoration( + color: const Color(0xFF1C1B1B), + borderRadius: BorderRadius.circular(22), + border: Border.all( + color: const Color(0xFF00F0FF).withValues(alpha: 0.08), + ), + gradient: const LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [Color(0xFF1F1E1E), Color(0xFF181818)], + ), + ), + child: Padding( + padding: padding, + child: child, + ), + ); + } +} + +class PanelHeader extends StatelessWidget { + const PanelHeader({ + super.key, + required this.eyebrow, + required this.title, + required this.subtitle, + }); + + final String eyebrow; + final String title; + final String subtitle; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + eyebrow.toUpperCase(), + style: const TextStyle( + color: Color(0xFFB9CACB), + fontSize: 11, + fontWeight: FontWeight.w700, + letterSpacing: 1.2, + ), + ), + const SizedBox(height: 6), + Text( + title, + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.w800, + letterSpacing: -0.4, + ), + ), + const SizedBox(height: 6), + Text( + subtitle, + style: const TextStyle( + color: Color(0xFFB9CACB), + fontSize: 12, + height: 1.5, + ), + ), + ], + ); + } +} + +class HeroMetric extends StatelessWidget { + const HeroMetric({ + super.key, + required this.label, + required this.value, + required this.caption, + required this.accent, + required this.wide, + }); + + final String label; + final String value; + final String caption; + final Color accent; + final bool wide; + + @override + Widget build(BuildContext context) { + return Container( + width: wide ? 240 : double.infinity, + constraints: const BoxConstraints(minHeight: 132, minWidth: 210), + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: const Color(0xFF0E0E0E), + borderRadius: BorderRadius.circular(18), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label.toUpperCase(), + style: const TextStyle( + color: Color(0xFFB9CACB), + fontSize: 11, + fontWeight: FontWeight.w700, + letterSpacing: 1.0, + ), + ), + const SizedBox(height: 22), + Text( + value, + style: TextStyle( + color: accent, + fontSize: 28, + fontWeight: FontWeight.w800, + letterSpacing: -1.0, + ), + ), + const SizedBox(height: 6), + Text( + caption, + style: const TextStyle( + color: Color(0xFFB9CACB), + fontSize: 12, + height: 1.4, + ), + ), + ], + ), + ); + } +} + +class TelemetryChip extends StatelessWidget { + const TelemetryChip({ + super.key, + required this.label, + required this.value, + }); + + final String label; + final String value; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), + decoration: BoxDecoration( + color: const Color(0xFF2A2A2A), + borderRadius: BorderRadius.circular(10), + ), + child: RichText( + text: TextSpan( + children: [ + TextSpan( + text: '${label.toUpperCase()}: ', + style: const TextStyle( + color: Color(0xFFB9CACB), + fontSize: 11, + fontWeight: FontWeight.w700, + letterSpacing: 0.6, + ), + ), + TextSpan( + text: value, + style: const TextStyle( + color: Color(0xFFE5E2E1), + fontSize: 11, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ); + } +} + +class SignalBadge extends StatelessWidget { + const SignalBadge({ + super.key, + required this.label, + required this.color, + }); + + final String label; + final Color color; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.14), + borderRadius: BorderRadius.circular(14), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 8, + height: 8, + decoration: BoxDecoration( + color: color, + shape: BoxShape.circle, + ), + ), + const SizedBox(width: 8), + Text( + label, + style: TextStyle( + color: color, + fontSize: 11, + fontWeight: FontWeight.w800, + letterSpacing: 0.8, + ), + ), + ], + ), + ); + } +} + +class InfoTile extends StatelessWidget { + const InfoTile({ + super.key, + required this.item, + }); + + final InfoItem item; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: const Color(0xFF0E0E0E), + borderRadius: BorderRadius.circular(16), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.label.toUpperCase(), + style: const TextStyle( + color: Color(0xFFB9CACB), + fontSize: 10, + fontWeight: FontWeight.w700, + letterSpacing: 0.9, + ), + ), + const Spacer(), + Text( + item.value, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w700, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ); + } +} + +class ActionButton extends StatelessWidget { + const ActionButton({ + super.key, + required this.spec, + }); + + final ActionSpec spec; + + @override + Widget build(BuildContext context) { + final foreground = spec.onTap == null + ? const Color(0xFF68787A) + : (spec.outlined ? spec.accent : const Color(0xFFE5E2E1)); + + final child = Text(spec.label); + final background = spec.onTap == null + ? const Color(0xFF171717) + : spec.accent.withValues(alpha: spec.outlined ? 0.08 : 0.18); + + if (spec.outlined) { + return OutlinedButton( + onPressed: spec.onTap, + style: OutlinedButton.styleFrom( + side: BorderSide( + color: spec.onTap == null + ? const Color(0xFF2A2A2A) + : spec.accent.withValues(alpha: 0.5), + ), + backgroundColor: background, + foregroundColor: foreground, + ), + child: child, + ); + } + + return FilledButton( + onPressed: spec.onTap, + style: FilledButton.styleFrom( + backgroundColor: background, + foregroundColor: foreground, + ), + child: child, + ); + } +} + +class ReportSummary extends StatelessWidget { + const ReportSummary({ + super.key, + required this.items, + }); + + final List items; + + @override + Widget build(BuildContext context) { + return Column( + children: items + .map( + (item) => Container( + margin: const EdgeInsets.only(bottom: 8), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), + decoration: BoxDecoration( + color: const Color(0xFF0E0E0E), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + children: [ + Expanded( + child: Text( + item.label, + style: const TextStyle( + color: Color(0xFFB9CACB), + fontSize: 12, + ), + ), + ), + Text( + item.value, + style: const TextStyle( + color: Color(0xFFE5E2E1), + fontSize: 12, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ), + ) + .toList(), + ); + } +} diff --git a/third_party/refresh_rate/example/linux/CMakeLists.txt b/third_party/refresh_rate/example/linux/CMakeLists.txt new file mode 100644 index 00000000..2cb7e278 --- /dev/null +++ b/third_party/refresh_rate/example/linux/CMakeLists.txt @@ -0,0 +1,128 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.13) +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 "refresh_rate_example") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "in.qoder.refresh_rate_example") + +# 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) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + +# 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) + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# 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/third_party/refresh_rate/example/linux/flutter/CMakeLists.txt b/third_party/refresh_rate/example/linux/flutter/CMakeLists.txt new file mode 100644 index 00000000..d5bd0164 --- /dev/null +++ b/third_party/refresh_rate/example/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/third_party/refresh_rate/example/linux/flutter/generated_plugin_registrant.cc b/third_party/refresh_rate/example/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..080e8e35 --- /dev/null +++ b/third_party/refresh_rate/example/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) refresh_rate_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "RefreshRatePlugin"); + refresh_rate_plugin_register_with_registrar(refresh_rate_registrar); +} diff --git a/third_party/refresh_rate/example/linux/flutter/generated_plugin_registrant.h b/third_party/refresh_rate/example/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..e0f0a47b --- /dev/null +++ b/third_party/refresh_rate/example/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/third_party/refresh_rate/example/linux/flutter/generated_plugins.cmake b/third_party/refresh_rate/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 00000000..0d162423 --- /dev/null +++ b/third_party/refresh_rate/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + refresh_rate +) + +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/third_party/refresh_rate/example/linux/runner/CMakeLists.txt b/third_party/refresh_rate/example/linux/runner/CMakeLists.txt new file mode 100644 index 00000000..e97dabc7 --- /dev/null +++ b/third_party/refresh_rate/example/linux/runner/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, 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 preprocessor definitions for the application ID. +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") diff --git a/third_party/refresh_rate/example/linux/runner/main.cc b/third_party/refresh_rate/example/linux/runner/main.cc new file mode 100644 index 00000000..e7c5c543 --- /dev/null +++ b/third_party/refresh_rate/example/linux/runner/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/third_party/refresh_rate/example/linux/runner/my_application.cc b/third_party/refresh_rate/example/linux/runner/my_application.cc new file mode 100644 index 00000000..02c797bc --- /dev/null +++ b/third_party/refresh_rate/example/linux/runner/my_application.cc @@ -0,0 +1,148 @@ +#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) + +// Called when first Flutter frame received. +static void first_frame_cb(MyApplication* self, FlView* view) { + gtk_widget_show(gtk_widget_get_toplevel(GTK_WIDGET(view))); +} + +// 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, "refresh_rate_example"); + 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, "refresh_rate_example"); + } + + gtk_window_set_default_size(window, 1280, 720); + + 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); + GdkRGBA background_color; + // Background defaults to black, override it here if necessary, e.g. #00000000 + // for transparent. + gdk_rgba_parse(&background_color, "#000000"); + fl_view_set_background_color(view, &background_color); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + // Show the window when Flutter renders. + // Requires the view to be realized so we can start rendering. + g_signal_connect_swapped(view, "first-frame", G_CALLBACK(first_frame_cb), + self); + gtk_widget_realize(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 GApplication::startup. +static void my_application_startup(GApplication* application) { + // MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + // MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + +// 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_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + // Set the program name to the application ID, which helps various systems + // like GTK and desktop environments map this running application to its + // corresponding .desktop file. This ensures better integration by allowing + // the application to be recognized beyond its binary name. + g_set_prgname(APPLICATION_ID); + + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, "flags", + G_APPLICATION_NON_UNIQUE, nullptr)); +} diff --git a/third_party/refresh_rate/example/linux/runner/my_application.h b/third_party/refresh_rate/example/linux/runner/my_application.h new file mode 100644 index 00000000..db16367a --- /dev/null +++ b/third_party/refresh_rate/example/linux/runner/my_application.h @@ -0,0 +1,21 @@ +#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/third_party/refresh_rate/example/macos/Flutter/Flutter-Debug.xcconfig b/third_party/refresh_rate/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 00000000..4b81f9b2 --- /dev/null +++ b/third_party/refresh_rate/example/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/third_party/refresh_rate/example/macos/Flutter/Flutter-Release.xcconfig b/third_party/refresh_rate/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 00000000..5caa9d15 --- /dev/null +++ b/third_party/refresh_rate/example/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/third_party/refresh_rate/example/macos/Podfile b/third_party/refresh_rate/example/macos/Podfile new file mode 100644 index 00000000..ff5ddb3b --- /dev/null +++ b/third_party/refresh_rate/example/macos/Podfile @@ -0,0 +1,42 @@ +platform :osx, '10.15' + +# 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! + + 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/third_party/refresh_rate/example/macos/Podfile.lock b/third_party/refresh_rate/example/macos/Podfile.lock new file mode 100644 index 00000000..776dd6e8 --- /dev/null +++ b/third_party/refresh_rate/example/macos/Podfile.lock @@ -0,0 +1,22 @@ +PODS: + - FlutterMacOS (1.0.0) + - refresh_rate (0.1.0): + - FlutterMacOS + +DEPENDENCIES: + - FlutterMacOS (from `Flutter/ephemeral`) + - refresh_rate (from `Flutter/ephemeral/.symlinks/plugins/refresh_rate/macos`) + +EXTERNAL SOURCES: + FlutterMacOS: + :path: Flutter/ephemeral + refresh_rate: + :path: Flutter/ephemeral/.symlinks/plugins/refresh_rate/macos + +SPEC CHECKSUMS: + FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 + refresh_rate: 75ff2e747acb2d546aa80e0f521a91607a73388b + +PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009 + +COCOAPODS: 1.16.2 diff --git a/third_party/refresh_rate/example/macos/Runner.xcodeproj/project.pbxproj b/third_party/refresh_rate/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..7e9b5438 --- /dev/null +++ b/third_party/refresh_rate/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,801 @@ +// !$*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 */; }; + 910248FF45164247AED47927 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48DEDB687F544FA88A70B539 /* Pods_RunnerTests.framework */; }; + CAE0E1D6B3E065E67EACBD01 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78E82EFBCFE9D8FF45D965F6 /* Pods_Runner.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 PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 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 /* refresh_rate_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = refresh_rate_example.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 = ""; }; + 48DEDB687F544FA88A70B539 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 78E82EFBCFE9D8FF45D965F6 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 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 = ""; }; + A5943E0CD58AC7DB09939013 /* 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 = ""; }; + AB95905B211F4A9F4CF45C3D /* 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 = ""; }; + B36A800253185A385429601E /* 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 = ""; }; + B949B21E5690992FF2267767 /* 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 = ""; }; + CD862853BFB36B0C3DDAA1F5 /* 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 = ""; }; + D2BF29F912559E7212913015 /* 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 = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 910248FF45164247AED47927 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CAE0E1D6B3E065E67EACBD01 /* 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 */, + C10F1240AAD4C5F3329E39AA /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* refresh_rate_example.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 = ""; + }; + C10F1240AAD4C5F3329E39AA /* Pods */ = { + isa = PBXGroup; + children = ( + B949B21E5690992FF2267767 /* Pods-Runner.debug.xcconfig */, + D2BF29F912559E7212913015 /* Pods-Runner.release.xcconfig */, + AB95905B211F4A9F4CF45C3D /* Pods-Runner.profile.xcconfig */, + CD862853BFB36B0C3DDAA1F5 /* Pods-RunnerTests.debug.xcconfig */, + B36A800253185A385429601E /* Pods-RunnerTests.release.xcconfig */, + A5943E0CD58AC7DB09939013 /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 78E82EFBCFE9D8FF45D965F6 /* Pods_Runner.framework */, + 48DEDB687F544FA88A70B539 /* 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 = ( + 99F025F5D1F12E172730F152 /* [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 = ( + EB0C5EE370B257EB4486DA5D /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + FBE83B029BB64CA0A7DD0B6F /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* refresh_rate_example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + 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 */ + 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"; + }; + 99F025F5D1F12E172730F152 /* [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; + }; + EB0C5EE370B257EB4486DA5D /* [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; + }; + FBE83B029BB64CA0A7DD0B6F /* [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; + }; +/* 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 = CD862853BFB36B0C3DDAA1F5 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = in.qoder.refreshRateExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/refresh_rate_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/refresh_rate_example"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B36A800253185A385429601E /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = in.qoder.refreshRateExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/refresh_rate_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/refresh_rate_example"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A5943E0CD58AC7DB09939013 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = in.qoder.refreshRateExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/refresh_rate_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/refresh_rate_example"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + 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.15; + 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; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + 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.15; + 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; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + 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.15; + 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/third_party/refresh_rate/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/third_party/refresh_rate/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/third_party/refresh_rate/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/third_party/refresh_rate/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/third_party/refresh_rate/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..e27790b3 --- /dev/null +++ b/third_party/refresh_rate/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/third_party/refresh_rate/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/third_party/refresh_rate/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..21a3cc14 --- /dev/null +++ b/third_party/refresh_rate/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/third_party/refresh_rate/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/third_party/refresh_rate/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/third_party/refresh_rate/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/third_party/refresh_rate/example/macos/Runner/AppDelegate.swift b/third_party/refresh_rate/example/macos/Runner/AppDelegate.swift new file mode 100644 index 00000000..b3c17614 --- /dev/null +++ b/third_party/refresh_rate/example/macos/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } +} diff --git a/third_party/refresh_rate/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/third_party/refresh_rate/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..a2ec33f1 --- /dev/null +++ b/third_party/refresh_rate/example/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/third_party/refresh_rate/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/third_party/refresh_rate/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..82b6f9d9a33e198f5747104729e1fcef999772a5 GIT binary patch literal 102994 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ⅅ8MglG&ER{i5lN315cO?EdHNLrg? zgxkP+ytd)OMWe7QvTf8yj4;V=?m172!BEt@6*TPUT4m3)yir}esnIodFGatGnsSfJ z**;;yw=1VCb2J|A7cBz-F5QFOQh2JDQFLarE>;4ZMzQ$s^)fOscIVv2-o{?ct3~Zv zy{0zU>3`+-PluS|ADraI9n~=3#Tvfx{pDr^5i$^-h5tL*CV@AeQFLxv4Y<$xI{9y< zZ}li*WIQ+XS!IK;?IVD0)C?pNBA(DMxqozMy1L#j+ba1Cd+2w&{^d-OEWSSHmNH>9 z%1Ldo(}5*>a8rjQF&@%Ka`-M|HM+m<^E#bJtVg&YM}uMb7UVJ|OVQI-zt-*BqQ zG&mq`Bn7EY;;+b%Obs9i{gC^%>kUz`{Qnc=ps7ra_UxEP$!?f&|5fHnU(rr?7?)D z$3m9e{&;Zu6yfa1ixTr;80IP7KLgkKCbgv1%f_weZK6b7tY+AS%fyjf6dR(wQa9TD zYG9`#!N4DqpMim|{uViKVf0B+Vmsr7p)Y+;*T~-2HFr!IOedrpiXXz+BDppd5BTf3 ztsg4U?0wR?9@~`iV*nwGmtYFGnq`X< zf?G%=o!t50?gk^qN#J(~!sxi=_yeg?Vio04*w<2iBT+NYX>V#CFuQGLsX^u8dPIkP zPraQK?ro`rqA4t7yUbGYk;pw6Z})Bv=!l-a5^R5Ra^TjoXI?=Qdup)rtyhwo<(c9_ zF>6P%-6Aqxb8gf?wY1z!4*hagIch)&A4treifFk=E9v@kRXyMm?V*~^LEu%Y%0u(| z52VvVF?P^D<|fG)_au(!iqo~1<5eF$Sc5?)*$4P3MAlSircZ|F+9T66-$)0VUD6>e zl2zlSl_QQ?>ULUA~H?QbWazYeh61%B!!u;c(cs`;J|l z=7?q+vo^T#kzddr>C;VZ5h*;De8^F2y{iA#9|(|5@zYh4^FZ-3r)xej=GghMN3K2Y z=(xE`TM%V8UHc4`6Cdhz4%i0OY^%DSguLUXQ?Y3LP+5x3jyN)-UDVhEC}AI5wImt; zHY|*=UW}^bS3va-@L$-fJz2P2LbCl)XybkY)p%2MjPJd-FzkdyWW~NBC@NlPJkz{v z+6k6#nif`E>>KCGaP34oY*c#nBFm#G8a0^px1S6mm6Cs+d}E8{J;DX=NEHb|{fZm0 z@Ors@ebTgbf^Jg&DzVS|h&Or)56$+;%&sh0)`&6VkS@QxQ=#6WxF5g+FWSr7Lp9uF zV#rc`yLe?f*u6oZoi3WpOkKFf^>lHb2GC6t!)dyGaQbK7&BNZ7oyP)hUX1Y(LdW-I z6LI2$i%+g!zsjT(5l}5ROLb)8`9kkldbklcq6tfLSrAyh#s(C1U2Sz9`h3#T9eX#Hryi1AU^!uv*&6I~qdM_B7-@`~8#O^jN&t7+S zTKI6;T$1@`Kky-;;$rU1*TdY;cUyg$JXalGc&3-Rh zJ&7kx=}~4lEx*%NUJA??g8eIeavDIDC7hTvojgRIT$=MlpU}ff0BTTTvjsZ0=wR)8 z?{xmc((XLburb0!&SA&fc%%46KU0e&QkA%_?9ZrZU%9Wt{*5DCUbqIBR%T#Ksp?)3 z%qL(XlnM!>F!=q@jE>x_P?EU=J!{G!BQq3k#mvFR%lJO2EU2M8egD?0r!2s*lL2Y} zdrmy`XvEarM&qTUz4c@>Zn}39Xi2h?n#)r3C4wosel_RUiL8$t;FSuga{9}-%FuOU z!R9L$Q!njtyY!^070-)|#E8My)w*~4k#hi%Y77)c5zfs6o(0zaj~nla0Vt&7bUqfD zrZmH~A50GOvk73qiyfXX6R9x3Qh)K=>#g^^D65<$5wbZjtrtWxfG4w1f<2CzsKj@e zvdsQ$$f6N=-%GJk~N7G(+-29R)Cbz8SIn_u|(VYVSAnlWZhPp8z6qm5=hvS$Y zULkbE?8HQ}vkwD!V*wW7BDBOGc|75qLVkyIWo~3<#nAT6?H_YSsvS+%l_X$}aUj7o z>A9&3f2i-`__#MiM#|ORNbK!HZ|N&jKNL<-pFkqAwuMJi=(jlv5zAN6EW`ex#;d^Z z<;gldpFcVD&mpfJ1d7><79BnCn~z8U*4qo0-{i@1$CCaw+<$T{29l1S2A|8n9ccx0!1Pyf;)aGWQ15lwEEyU35_Y zQS8y~9j9ZiByE-#BV7eknm>ba75<_d1^*% zB_xp#q`bpV1f9o6C(vbhN((A-K+f#~3EJtjWVhRm+g$1$f2scX!eZkfa%EIZd2ZVG z6sbBo@~`iwZQC4rH9w84rlHjd!|fHc9~12Il&?-FldyN50A`jzt~?_4`OWmc$qkgI zD_@7^L@cwg4WdL(sWrBYmkH;OjZGE^0*^iWZM3HBfYNw(hxh5>k@MH>AerLNqUg*Og9LiYmTgPw zX9IiqU)s?_obULF(#f~YeK#6P>;21x+cJ$KTL}|$xeG?i`zO;dAk0{Uj6GhT-p-=f zP2NJUcRJ{fZy=bbsN1Jk3q}(!&|Fkt_~GYdcBd7^JIt)Q!!7L8`3@so@|GM9b(D$+ zlD&69JhPnT>;xlr(W#x`JJvf*DPX(4^OQ%1{t@)Lkw5nc5zLVmRt|s+v zn(25v*1Z(c8RP@=3l_c6j{{=M$=*aO^ zPMUbbEKO7m2Q$4Xn>GIdwm#P_P4`or_w0+J+joK&qIP#uEiCo&RdOaP_7Z;PvfMh@ zsXUTn>ppdoEINmmq5T1BO&57*?QNLolW-8iz-jv7VAIgoV&o<<-vbD)--SD%FFOLd z>T$u+V>)4Dl6?A24xd1vgm}MovrQjf-@YH7cIk6tP^eq-xYFymnoSxcw}{lsbCP1g zE_sX|c_nq(+INR3iq+Oj^TwkjhbdOo}FmpPS2*#NGxNgl98|H0M*lu)Cu0TrA|*t=i`KIqoUl(Q7jN zb6!H-rO*!&_>-t)vG5jG>WR6z#O9O&IvA-4ho9g;as~hSnt!oF5 z6w(4pxz|WpO?HO<>sC_OB4MW)l`-E9DZJ$!=ytzO}fWXwnP>`8yWm5tYw`b1KDdg zp@oD;g===H+sj+^v6DCpEu7R?fh7>@pz>f74V5&#PvBN+95?28`mIdGR@f*L@j2%% z%;Rz5R>l#1U zYCS_5_)zUjgq#0SdO#)xEfYJ)JrHLXfe8^GK3F*CA(Y)jsSPJ{j&Ae!SeWN%Ev727 zxdd3Y0n^OBOtBSKdglEBL)i5=NdKfqK=1n~6LX`ja;#Tr!II$AAH{Z#sp%`rwNGT5 zvHT%(LJB+kD{5N}7c_Rk6}@tikIeq%@MqxX%$P!(238YD(H<_d;xxo*oMiv^1io>g zt5z&6`}cjci90q2r0hutQXr!UA~|4e*u=k81D(Cp7n{4LVCa+u0%-8Uha+sqI#Om~ z!&)KN(#Zone^~&@Ja{|l?X64Dxk)q>tLRv{=0|t$`Kdaj z#{AJr>{_BtpS|XEgTVJ4WMvBRk-(mk@ZYGdY1VwI z81;z(MBGV|2j*Cj%dvl8?b2{{B#e0B7&7wfv+>g`R2^Ai5C_WUx|CnTrHm+RFGXrt zs<~zBtk@?Niu%|o6IEL+y60Q>zJlv``ePCa07C%*O~lj?74|}&A0!uA)3V7ST8b_- z6CBP1;x+S@xTzgOY2#s%@=bhZ@i@BwmS)neQG&=9KUtRf^K=MvjC5JnqLqykCE_P0 zjf#V4SdH2#%2EuDb!>FLHK7j;nd6VLW|$3gJuegpEl3DZ`BpJU$<}}A(rW?<6OB@9 zKP9G3An?T5BztrLdlximA;{>Tr7GAeSU=^<*y;%RHj+7;v+tonyh(8d;Izn}2{oz& zW)fsZ9gHYpI?B|uekS3zHUue3mI zb7?0+&Zm>Kq(F>~%VYEn)0b32I3~O^?Wx-HI|Zu?1-OA2yfyJ;gWygLOeU;)vRm3u z5J4vDIQYztnEm=QauX2(WJO{yzI0HUFl+oO&isMf!Yh2pu@p}65)|0EdWRbg(@J6qo5_Els>#|_2a1p0&y&UP z8x#Z69q=d663NPPi>DHx3|QhJl5Ka$Cfqbvl*oRLYYXiH>g8*vriy!0XgmT~&jh3l z+!|~l=oCj<*PD>1EY*#+^a{rVk3T(66rJ^DxGt|~XTNnJf$vix1v1qdYu+d@Jn~bh z!7`a`y+IEcS#O*fSzA;I`e_T~XYzpW7alC%&?1nr);tSkNwO&J`JnX+7X1Q8fRh_d zx%)Xh_YjI3hwTCmGUeq_Z@H#ovkk_b(`osa$`aNmt`9A#t&<^jvuf z1E1DrW(%7PpAOQGwURz@luEW9-)L!`Jy*aC*4mcD?Si~mb=3Kn#M#1il9%`C0wkZ` zbpJ-qEPaOE5Y5iv_z%Wr{y4jh#U+o^KtP{pPCq-Qf&!=Uu)cEE(Iu9`uT#oHwHj+w z_R=kr7vmr~{^5sxXkj|WzNhAlXkW^oB4V)BZ{({~4ylOcM#O>DR)ZhD;RWwmf|(}y zDn)>%iwCE=*82>zP0db>I4jN#uxcYWod+<;#RtdMGPDpQW;riE;3cu``1toL|FaWa zK)MVA%ogXt3q55(Q&q+sjOG`?h=UJE9P;8i#gI*#f}@JbV(DuGEkee;La*9{p&Z?;~lE!&-kUFCtoDHY*MS zzj+S$L9+aTs(F^4ufZe6>SBg;m@>0&+kEZMFmD*~p~sx?rx=!>Ge;KYw<33y#*&77 zFZI`YE(Iz?+tH;Fq;y=MaSqT{Ayh*HFv0(z{_?Q+7@nE%p?S8%X6c!+y;!0NLXwJV8Co_}R3*7>n+oMsQpv8}8ZS-P@(Rg|gmxZHzf=nMOUAAY}AZGfWVzZjE@4$=7xkIrs8BE%606aVU%kxz_04ipig51k& z(>c9rJL2q%xvU%Zj#GR9C9)HLCR;#zQBB@x;e_9$ayn(JmSg_*0G?+wOF?&iu@}S{ zt$;TPf*Lj$3=d<}Q3o!Hq@3~lFxoiCyeEt}o3fihIn{x2s1)e2@3##&GYDq~YO|!q zUs0P-zy)+ohl-VQ`bhvUpC{-d$lkpML_M%Kl6@#_@A}w{jWCDsPa#cSbWA#C4Sf|*C*&Z{ zz?hOU7Cc`?>H$WGqITA2P~fYudnQHxB8^;0ZFKC;19F#~n_2P@{cE{Czq-#K5L_8| zc3aOEwq4%zL5>YU_mc9fc-p~{fBTWUkxTiZvxt9FOqC{s#TBp(#dWc+{Ee{dZ#B!g zHnaOJ8;KO1G;QU2ciodE+#Z$Wuz*Hc6NRO!AUMi|gov=>=cwcZeL&`>Jfn!35hV1J z;B2@0!bIR853w%T*m6)gQ?DPnQ)o6EtKaN3L;o?*q<83d&lG&U=A|6hcT?f0)4h6{ zGIZ0|!}-?*n{zr}-}cC}qWxEN%g60+{my)o^57{QEn(tSrmD7o)|r0+HVpQPopFu; z0<S}pW8W2vXzSxEqGD+qePj^x?R$e2LO&*ewsLo{+_Z)Wl|Z1K47j zsKoNRlX)h2z^ls_>IZ0!2X5t&irUs%RAO$Dr>0o$-D+$!Kb9puSgpoWza1jnX6(eG zTg-U z6|kf1atI!_>#@|=d01Ro@Rg)BD?mY3XBsG7U9%lmq>4;Gf&2k3_oyEOdEN&X6Hl5K zCz^hyt67G;IE&@w1n~%ji_{sob_ssP#Ke|qd!Xx?J&+|2K=^`WfwZ-zt|sklFouxC zXZeDgluD2a?Zd3e{MtE$gQfAY9eO@KLX;@8N`(?1-m`?AWp!a8bA%UN>QTntIcJX zvbY+C-GD&F?>E?jo$xhyKa@ps9$Dnwq>&)GB=W~2V3m)k;GNR$JoPRk%#f3#hgVdZ zhW3?cSQ*((Fog26jiEeNvum-6ID-fbfJ?q1ZU#)dgnJ^FCm`+sdP?g;d4VD$3XKx{ zs|Y4ePJp|93fpu)RL+#lIN9Ormd;<_5|oN!k5CENnpO>{60X;DN>vgHCX$QZYtgrj z*1{bEA1LKi8#U%oa!4W-4G+458~`5O4S1&tuyv>%H9DjLip7cC~RRS@HvdJ<|c z$TxEL=)r)XTfTgVxaG!gtZhLL`$#=gz1X=j|I@n~eHDUCW39r=o_ml@B z0cDx$5;3OA2l)&41kiKY^z7sO_U%1=)Ka4gV(P#(<^ z_zhThw=}tRG|2|1m4EP|p{Swfq#eNzDdi&QcVWwP+7920UQB*DpO0(tZHvLVMIGJl zdZ5;2J%a!N1lzxFwAkq05DPUg2*6SxcLRsSNI6dLiK0&JRuYAqwL}Z!YVJ$?mdnDF z82)J_t=jbY&le6Hq$Qs}@AOZGpB1}$Ah#i;&SzD1QQNwi6&1ddUf7UG0*@kX?E zDCbHypPZ9+H~KnDwBeOXZ-W-Y80wpoGB*A) z_;26Z`#s0tKrf~QBi2rl2=>;CS1w)rcD3-sB!8NI*1iQo59PJ>OLnqeV4iK7`RBi^ zFW{*6;nlD&cSunmU3v4JKj|K4xeN(q>H%;SsY8yDdw5BJ75q8>Ov)&D5OPZ`XiRHl z;)mAA0Woy6f!xCK(9H2rq?qzp83liZAIpBPl-dQ&$2=&H?Im~%g;vnIw1I+8q|kr! z36&^9}CMmR(U2rf|j12oG=vb%Ypsq8u9Kq}U*ANX*)9uK}fAi8;V_7Z;0_4*iydDxN-? zv?qJ=T*{MzL~-xUv{_Kh_q9#F{8gPV!yPUUS8pEq*=}2-#1d=sC_|U-rX~F0 zBLawgCWy#?#ax{~DAnDvh^`}wyUO`ioMK~jgh%L7^}#h?beSyvQ_g>+`2`}`-1h7# zg*?qJdm=53hwN8~B=^|LPmYtOVrQ(W{sNm4uofq=4P@dUA%$onWbw_m-KWia&n9iv zi)!9#OJ#^}eg8tE{wSb9(c0D^PS1 z9EBS5*ypSiVRS_G0v?$hyoZOS7hFWlp4qbYkf9Y&{%OzhsIdHskLptn96@k6@^K@U zszd8POehITDK+AyW#JKpnWY;ju#MC$JjB1Y*~(E6N%{p#kO+bVxG3X<34n3fW=k{A zCZt|KP%x^GQ9%mU)KE0{LA=vaZvRQbxSlK~eAkwWo2Z<{j5eS5NVTMe`m%re8%~7K zZLtU&b~YDN%~uA9wPf>x2=PI=MA6_oVe>Ek$s5&&Z=8vvF5EODP4Av(b|dlNgF1O8 zy83W0WRdzjz2iNA~t1piEqlyU&`$yZtqR`6X_PmuP>W+D|8iH;FQ zN{JuU#Tz9mV=4R_IewROL1|mK^`lLat#LcIBfggzM(iO$pQT*-c_ z94^LUWw#5B9~sp2W1p`c)Y(xfR<{O^9n4E6vDDw{#-R4UMBKo{>Hqlqn*a9rl_>+0 zS5MwJC~nCC`1X%VCyWFsiDX;bfAJQAUkU#105f_s5U-8rqO}n8fA1{b>Fr6Q|Ea(V z5B11Lo^ooWF?`^{-U#?iatokWI-e$632frzY?Yzzx(xJc@LFM4A~-eg!u|tl{)8Nx ztZLXsSC*68g%9TFu(f&J9nmc^9hgyy#uUOMJFCaifSaDcyQ&6=8e9=t zIFEAQ{EK{|73{($!a4=!wj4ABcQrUQp#+gGM?wEUp(w@+Fzi{!lt}|3`PM%&d-seeR zB$}BrFGD3R10CE>Hsb>;PrP}pd` zaY4}6+Wu(`#uAV+E5SV7VIT7ES#b(U0%%DgN1}USJH>)mm;CHPv>}B18&0F~Kj@1= z&^Jyo+z-E)GRT4U*7$8wJO1OibWg0Jw>C$%Ge|=YwV@Y1(4fR>cV#6aGtRoF@I`*w_V4;)V231NzNqb6g@jdpjmjv*<2j02yU$F8ZS$fTvCC`%|Yn#x< zXUnP&b!GLpOY-TY3d?<-Hhxom_LM9`JC9LEX2{t1P-Nj%nG+0Vq)vQwvO^}coPH-> zAo8w#s>Je^Yy*#PlK=XDxpVS~pFe-j#jN-(As&LRewOf(kN-aKF(H+s*{*!0xrlZw zchJu@XAvQWX7DI1E8?F}Wc8m46eT+C<0eXVB+Z^(g=Kl@FG-cn@u$suj)1V2(KNg_ zh29ws6&6(q~+sOAoHY^o86A<#n*?Pg2)cK$+y;cY$hJLq4)4V84=j+3ShSr##Tk5kgmxB zkW+8A1GtceEx~^Ebhwm36U?oA)h)!mt=eg0QE$D1QsLNZ_T3NH?=B&0j~#298!6iv zhc0|-{46*3`Rx&nKSXnf1&w-Rs>#PGAGuY@cBTU-j|Fxbn3z49S#6KBaP^Lx*AOXxIibr z!1ysMi(&kr!1wwQB5w`BDH2~>T4bI`T1}A2RM0zd7ikC&kuBRsB`Z2@J!Udm{AmSN zrr0k6_qCZL**=)xRW`MFu(OY=OT;3G8eF~ z2mmkXZ9X(sjuKmq+_<=LSjphB$~R1o^Yb=rO!j!(4ErIox^x55o{pXSE9X$!76^*$ zoKhlAX6y%n^U=C~@!vIlEgXQGD@>oOU=_(aXF-Sjas*$AKESfRzxQ8#3yOj|y0OCU z>6Z-0%LCcjla&7I+CXm&caKp@@jQ!5M`(_{CL=@4#JJ}cHeZw>^b6fpv269LSV?gV5Q{kk?4;;y9RIsy5vk%DIRiL(9xe1aA@4!VX zDh2}xgUd5X?6nji%&7-%QuyKSYA-Z{PwJijUQ}In+EJl|x@dF1P<5bPa5W3&&?^h$ zZCo8LepKo0a(Fsln*cHL;D(gu9MMkoiM0*n31u)jHqX5x^F95tnI&^}^yKx3YwEm@ zo8?EZ710ykx@19{=yz5IXb8w4yjdveWb{IVL6Z(Cs>!a_0X^1E27o!4e&b43+J*u2Gb(59k2uK0goLwhO{ujLS ziI9LA9`&x~Y$6JNX!aEXR``}LUI}Gr#=<^wBHmg%v<)zRWDVtq)kT$-P7iU1R)2XZ zi~bYhV@EZ`@prgK(cs{>2jn$pxg$<|KjJ7%26Km>%KcXh^bU@y@V_Lf@=j1x%R4{v zOcQn{I}!2W<~08FOVnoV>zOTH=+>v9!jFo|q)ucqIe!N4{U5_G`>>*sVD{8I~4FqyU8imZ**-Gy`~Xd z4w35GMf%7^i65HdX{Iz|f2Kg193#KhPIeR)-=eYx3Z!%RM=JjwLrdk^B#6rg!ym2w zPbFqYyO4>W_Z6PonAwiu7?!h=x%sR-T+_*xZOGh2wWhWr%}%2^$$ zQvACIB~pi=m|`hXIMvoq`TOCx=J_D2>pi6$NPy3&8#vy|oX)=kM0Z}$BR$r0G}MzOk-OqG+VmZtOZoj6x4(tLh|5h) zBv64Y{DPHsy&_H(5_l(&Y}FhVvr9m_*_Q~Zy-}V9+VmGnvndEjYW4qt4K~N&Y&6g| zfpz*V=A#^mVmuOAz)(KVI<%v5NY0%Goy!{9&o41upsPWk(yFuRP|A4q6NMnX%V~MT zi_Rb-Bno2kI+j0Cw`@ydy{e%ARS#Z%b6I%_yfo_ZKXr4BLVoHzBKJ^ZG z-2>2IzU)55@9C|?_P$ew^-7zEiAKG1XAi{!3h%1m#9s%^pGy6S9wKFYY4<$djeoJP z{GI}Vd%idY$4_fh(7NXm7#;cC!DS&-{tGr!Qze{^%bUx2jgG@-kMta^q-EwrKB}d8 z{%FT>rFk_bzW<{lc%eYlrsiYTZXGgzD1&lmRyp+c1O=0=zAX=KV62bx-a~JP{cPF4 zU$-XT#(9&T>l@bMu3nSr{)%-5lV+0t&bxip4DVJ~vlL$J2P6X~ zd{FS8vm{Lhrieul*7&(AgPuXhjpGila%6_?-+k#b)cdk#M1jB*nE>G6NGOr+Ek{`= z9b%S1`$`=g0CC$>0$Db;l_szReLYVmce*(()9%Zz1`*fNXhI*oRlerWHarD(v^W^c zuc1Vuw6Gbp7ZsoRH>QGt#&lv;5G~Ovt$%7VFd*-rN2>UjbOWBFGNGO`bru7CFB4tn zL`^?69Lj_g_TA&`9`dSI8s|)K|QM0 zybvV7!>xDY|6c6y;Q}qs`){1+WQu_5Dgd8Qe|q}}bxjH+joQQtqs1IVZn6{e7T{ia zF|=^xa%eWO%(x<7j*QZbcU_;aVaVP!arexOLOtoSNt*hvsRL%}%)jPetSich(`b-^ zMZ$PM9%s@%*jPVz0Z^W*cK_>G4f}+eEVX`HOaHg#!B`<4v;x}zDLMR*M27`kNfp!! zOfdt(>k-g>7jf^{Se@3$8<+;R*cYtw+wD_Z8Pl~!JDCUEPq{Ea*!J9`%ihyNJZ30i zmfve}S5<$Uso}_?SuI$ks|{-ddGLu9WR9`^9)Kdi@Vs;x#SY-xp}wHPU0|vEA7234 z@BN1z7OF=OOQtPF$4twn3!HTVlUVD_)ubMM7PEPoiC6lQgL2q9PK4~e8v-OuH%lie z?NgBLkIdPMG$QBq(>r^AOHB`|*1#*!2Z? zuU8H|FD`OBRu^(R?Z-Vhr0j;FLpS~a34KREnd}B=EYHS*>Hm+f%tgJt!4J8Q`qn^4 z9F=tO#JRJ}tzA`vx$nZ)O%wC?Uiv0+_nz}5Lj4ki*&=K&*#U`=rv z`Q@Q{+IhAj@6lrNK2B=8Yln!O2%zomfRehFT~;!O@(@Xy|1Jlw*uOB-M$#6K^)QBm z_7%#QVUDPwnW{iOV-grMQQU|3{=BQMh}c5(yMGdoQf*)k9-B zMQ(^GdJh+y)>qJprknS!%WxqM>HlHOP#7UVdy>%PW$!l72J`n-p7j(DBKoGxXWh(Y z>BFDZl|7knU_jg_SSbvFk8)39%2)Hu5W0}HKlh>EaqvFoXI&56Yy)3) zQkE4X^P0QnPn?iUUVHJZXzPp`s5uv?pG{K9IgGoHvcmlBxubi|iF7n{)mhenIcxGs zgr0OpQy#Y#u=5lOyiECfE_Sn?Fj1LyoRKcbTgX{p<T*v!CGkPc)pcA2D=4Ekp0Gb*wpy7S88C%Ywsbr?MI(3UdsCM?XJ1X%*hNjB)XqZ*W(qDdtSb z<3XN74ARXL3=c^bfW~F%NM^5*Zx92>Wq`&M625p~j$8mYwLbk%Kf)jbn#<2z$%vP5 zy#b>-tF-S2_AB4;R^K&^-1LJrUmi@9rB^FLF)-k&YHK8P+k@RCJ1qSTZ@=kHxA3l$ zmK_ZG)l6(nmCR1a8|;QF-B5e_ELnjJ1$m-;4UXX?WytF_wz7#&AjwZYTMVieLbq@R z3t-q|G4^BB#EpNu4uyfDebB+-uu_$9>y-dzB30Y9F=R zrW-Heqnj*InPTWHgR9v^R7~hokldh&h8=HDhMW(EFfim1*{)5Lc1-+eBVkK-2!u=N zuZKABgJs3I--NbjE;>Undg6uK`^U>AQ6V zhc!RhYgvrmeGNsftr+(C<_MtuV$`5RZTf#5r=DR?gWG->#})#=(td%C3`oO+2B7im zUqY}&a_QNTn?s+?=mNXiREN%x_=(H)L|DtYPY>SR3pQfBOel7G_jR_{!9`dSj8Up-`JgcB;=Oor)U=_EVjF3C5{Sqh8cq=~bRjoBpoc$kJCgtTyZGSpQ4= zYi$6b$-dGmuTDF&@amhV?cU05g(AZV&v2$4m&j_~GZk;&keSO(@LRESRZ&p`dV*6w z2$em~p*8yM6j;SYorw`M5K2mluJq7P5Yn$VtZj8DEs2Zk=O@4T&Q}>~f31Z{uk}`E z{Dp{KObh1kk~~MfLUod72{Pk6G@T$_0_N??lOrdR=Z;VV#m0l)&@hz{Z?)@sgImi-&i1@95g53rON83v!yVPDHRU*Mzc4yZ(-Fr z{8{WXmIJf7jeswk$;6s~Qac6QyM3W&`}m#gRt=rr95A+Ad&wSAgvXZ|F))rBJVJ5W1CsjN`QaOzct2ocq#0!v zmj#075)C!3oS>&N;aHS@<+c>RHL)8j^p)k(8#7$LEx!1g_1^02!4_qA=;uhKW=+ix zGX%+vBMiRiF^^jm{mdO(?GdWJ#unO#_F^7mhT8)s(z_WlwFyJ#Xh)k5+RG2f;LC*K**1dr`#}~6A=0B=I&V;%zDA1)d@G!X#Rng)7G*2k8Kg447r0ox> z5NK`d(H-afBwo9feDOUi>;BbPsu!2|=@g=3j*PY}@YrOb+SX6?#Yb2xaaK!?>SX1J z_!VsB`2n1=wwSftkydm!39|-1?c%Epx?TO<(#GO~I&{f4+)XwRk<7RQ1~5>QcKH|D z?!}j1ueO0Lk;FZ{k4FA_(S`Ot0w~tl&m0duID*f6RY#bkw||o;kZ# zISYNTb|{~|X$m$Q-Jv#uxyw)eM0gIv`V#wOAp&Vv@>X4_tSZ&L#juM@$S9 zx_X_tLh<_^-F;LAQ09s@sPb%PMTrcw*HUV0P=RYSlM&AXEOI&&R&YCm_S<7DRBx^L zA^R^iwW+LMk(r*$Pq-fKU5X@=mQ=`ErO30H@@&qqnI7zJcrbSh+H<V ze&7Uli0xj@WrW#&-9%*FP~kPYF_YYM_hs5~|ExMynQ%qvq`leRB6W0yhC@pCb8>_P zlf=F~WMv_u*-DV=UaVu#2rlzK{q8D95VwZrfV?gj@rSNWXFvktUq)V5+YrlxwX302ae(;aG4e>L-M@3J+-f3IT{b9l!kg*2M zC1+ND9}6m^()LE87Mt+^Q|)!y#suc&v26C=0W88%a{?)E8Yvo@kM&KNMaOst#|-_CbUTm}WS@-c>nRb;&z^ zYr)+IE$1=jov(CZ%3uR+`~NI>1&Gs6W(jaamjcN$a`2!*nO}l|b%?)Q%%UWzw>A`C zR@px(P*7j$TK?jbv*%x)e^|jcLsv}aF(Z0=7(%Oa7+1wY>{B>d+i&ZA$}k(qgZPZY z;VkW~8eWnU&HPIAbco?&tc2O1$6=7n{u|^Y*nXoac{o1W-6aXfy~KlNbJfLoq~6;+ zDYmnv--Fhqrl+UV#k@_(1=gWNtqhyVKN=9CZ-{Ohi>e=~bm4IKbhM%%W zW8oXE!rGpV7Wt(_^4nndH1_imheaWzDi|I})9ZVZ9>pN+P%dVc5wG`Ze*4`@rjn1^ z`ln(;vPBHQUb}y8S>=8q__r7g+=z$>!pReVB0@XKchAvyGjLQs-u>+w%`frV4FeIG zj=7n~hGrwx*&5aHy(7X$bDZ7YhcP%(*>G^lAYMK;qG~V8Jz@b7oNg;IA1z$9@TbzW z;@I51@Ekef#qbxnG$Y8Z%bm~ibZ=4#%yKr%#b)CDrfKN`ujIY?tA4h9)i~dZ4E;ZM znvb$n2)zn$Wx&zlW%mJZDh28ox$@%`w3i7YFepXUChw}$UXKI=-TM51`M#FH=tdr*mQ!c=aB1296Lu>iTTKZWss0f z5~ihdImPN$aTle_AdbYC^31}_^EK|9R&l#%3hbx;8vJ+Gp^tm{9JDILu*1PW!rh^Dn9p<)h#Sl4kKM%nm<+!ESSk* zC;lLNT$fgr-!+{aBsSx$41b}yy6o>r3F#1&iv3cfY2N<+`0qJ+>=&Qxs}JOEkD?^l-F5i`t5+zNuvJf z3Fh4$mNqiFXL-aq4U4K@Ae$fq-TDT`rvrx;gqx96w^*@s=mcthCaIyPe(w)6kI{EqV10tcShHU9eeAPs)s?6#vrq}>y3FeTJu$Udha+z zs7}rmA@yR(L&>35sNjQqrw}o^)UitMU!5g6nnG)(tgst!^`FKJEzI1(d@j_w@;^hr zgYxlIRYjho4U$bhczfq&YySCqCE(5_d>l(4tk1v9!V7PB%Vx{QO=G2NC@c1%3rEzw zN<6i?h;CJX>h)kn49Sr)g#Em6km6ESP`1qc5C3ZHizN>r>V-fSS=X1nT{+Thh@kC! z(H=PlqDt7V6gOYezXUK-dretz!1?IUD6&eL2b!4=9h+HUO&DYZKMM>|YhlEEg?q?S z^XT4$2Fd|zT=x3U#L1|F;-#`to-Y6hiYkWdO=rRC)meY72pIfl`3zEGDU8($iWR^K zI$nq80aSJII<;#W5Pj>^_T&013BJ*O89Uoq z5>;Paa^E}xar^r=!pexg&OTM8wluk4R~Ru=)Hgk`Y#i_$jk{jc8hx}?(dW*X!l4vs z6_%$s#duJJFmaFc-5#>v6Yea=I~)s_pXGS>Tkz?s+WS}>Qp<9MappMLXpkXpSM~SmH6u)`Z5>o02kJs;w@KhdiZ3}29y*xr|6tMo zBHzGic+b+dTd!xOJ;p{Rguh^corJ;K?R6daayQKm+0rf7|AXg0qs!R9eS7t4{G=fs z1$=?kK1Ih=gEkI>@jgXDWHZt*C7FUEWs|u^pE3Z``^K|1KEC^sbN*4nQUfRc_AyE0 zn)?RrGjgPkzfE~_s!rDB!fDsV+*|kEX4+DyS#8%!cshn;s8svwBXSsDGX2ZRa0={* z=`p1F{zD17*Rk>Uk_cw3t5j=9-d6$}MoM~z{v{t^M!g75-+o8_XkP@CZWUQ2z!^26 zCNOu~hgrrK)y>bgqb{`Q_1^zrG4;cGarP!nb4E~(ZKWc`LVeEq;IewVneLp^ZU2+% z95PgN*M5v7Q;ZlGvM#`&u2NdHm%&gZ{bZM5wBCp&?HeZhwU87wyT_z!n4z+1?=RvXZ^72d*%+R1s1$KbAFtR|= zw;MEq=O7pMIKpFwKH6$OOszJAf<_Z<1)36cB>D>|Z6$gJL~jH`n3MMou$#Si%rDAu z4pSkJspG|^CJ86vg6kkfXsA_`8@8iOryOe!Qhn8SV6}mPlof3=WJRVqAr_b;e->`Z zMR(p|K|$L0^6;u~USxg#B6-ZNc%E1dv*^P=|2k*^NOBni#G%9Y?##{=)8KZwh85OL zSBG9|gb|hdmY^gn(ziY&O5#@I?W)W;361Yb^VQNpz0A7&^(7HRAsUvw#)fvhocvja zLxV65J0_$>&cVRctJFsn^qLos^tG`+B0_gQ{NeOwKt-!C^gGFufdtPT*Vi>l#X1|V z2XxsAcixN)Ekq=a##_^=k_^BFH5_zpvPDRP>u6+3$}i&b zy0@FdzAHw?i9OqnlTts_w5D@Nd#eM)KKEuN#m{|AJyscxa}(eA?z4&4yvXo{OBS65 z-?gW;<+;+ntM}U_yTmHm6*2zj0Imj<&ZgE9Wj|gfsXhrVH-c0p$7HXnR8bxDYOi z=_r3FA~u`L&2;Vir8}P3)k|@c?sK1U@&iWo{HEXcoy>6wQSuJ+b4l%aTBuigs&k@Y<2c=S3Ef?p zH>ki4yDuXdo_eu>X1{E$g(Q-u#zVXN^&%70guoizo7x(kQ0OZ}H$O9UB}(FaX8Ct1 zFpx~}EbHf2r6V;x=@8GH$C2|6*?K~?LrtMYd^bw*WYXhA z_))@RMH;nZedW3+qfWbv<|_#BYOxX^rhbN+!za)|!|8K*LRs(R$O*2SDM{g9k7e{u zN4VIdi}e#0&h?sBxu$>Yy%)j(k1V2fuhp8r!}gfF@b;F?U`6}YnnMh1&sSU&lR^?# zu!61+lGsuFEfDraX3+$QZibCbKzc{75G^T7@WZSQ)j5898G1AOXB*H*TSd`f<`IK# zm1%&t?i|2Z-a&r!pJehzg@!awNp)R)aa?q_SqGrxE5u+T#f?K2;GAHV?O&>!W@Q*k)7=g2vDW+7K zbyY9i{|nOF*SbMYoRQSAbSH2y$bE5(@d6xKxcF#@TE~X#3o=;`0sc!RupdRmQsML? z&>SCwS{FOpSr+@6Uuz3m`hj}(^g`Jz|6?({!%WVJn$H|ugxW+x-GEA?J&U^ugj3Nb z;65~)W<}iH2PJ@st8LtLfSOLXYgj=9<;?ih7rq$bXW9J#!B8!Wu6#U`A$wlcoC*&` z_9Js~7%m79#+edeT&P`@_Ng@e&5J+pqpx%31tAF71)pcz~-yJ>P5yX(nuM4;bUHDa8E(~~l{j~JeCGkX>nHJDpgSf&bTHEf)qw8{Q~CBPEVen|MW2P3vmf`8X9-g|>>ddp zcgfjbl~(?3Wa*NzQH>4nsM$3}Ul>pX1xC0oF3TZXe7=V!9!n?WgvH|R zpbruczmB%z=zkZ>=1R|gXwGThLELqD5KCUhtiRGT*JwKIvzbzV%ZU!e!VcNHSSX3> zObH|oohc8nvQZ2}q??C}@>!fe3gH+HF@4(qWqi>;ag~md#D;cl8&gQb^?2a@5cikT z=7r78@&5gV3Ggc9f=<<8v~yz`NcEGvbX1V_`IL(&+Z>LB zM~$ok2qXzod@1$TEl*U~H$V5g$er{Uj^($sWb7Nr{gsIbE(`$LRGECTOraXiU%=uq z0zvpi1S%)RxTjzoVcR4#10)fs()4Mtsa@e?9j)Bk!LsYyXIZga2q7d%`vQE!V@<1Y zmkpH3LeXJNO9f7l>F84g;huc=4nk(UnU}RLZmYk2TtB#lv34K(?8~gyx-mN%g=U44 zOPdr_!j-;IEbe|l9-buuKEy^Q9MLjSKG$S6dz)!U_32{1)N}L)3+COmlg=nY1@od$ zJ<0z-B%sisAR1yh>z-RfQQb6M4i-d#vxvb~f69M{JLPZv1JSCh1$gQ*LxOF-tH9!k zbQ0ZW)S7)qCSF|=2`q_A3}OHBNBueZwTTz^ar~gz#2KA74&&D)KHt~m4F_nK<^*7_ z!!pN@xiGkq%>1N(rNxw$zu-=1t*IpAy$ z4~dD0w%9;E?(greVWZ3(o9ux`elM>Rek#0 zO=#-(4p5B+wFzlEU7^k{3EdL6sIp|K*>xrriI`}E8ze|z-$YpN`^_teL_7P`%e>IN z7tNiH619P+0Q1hBR|W#POOta)1|LkIRtgz zMJ9VOxXN#o)mlXS=u%`Q>~PBuKEmOWsIuQRp{y%!ty{fEyL0gV)$LQeL#pqX3L@SR zJ2Gb^E9+KVd?;joVOXlGie3?z6>(>u(i!(qGz(W( ze~^xj&IRF<98ypEis{Y_FoHn%C0bW(XeF#Lj=2WUEBqKNPPFppEH?_a3}-h906X}C zSYKcZFU`Om5YlWhh@ogzCn3NvuM~F9jOX|xe-X*!YL+#ceh_tJoHXz`aTnvSrOAZ| zOtdGz?QdT!oAJr3(XL2G(p%2X4{xEohU&vd_zQ(U%ihHOlKPWnb$&YYhx48?|R++>`5?sxvM?!;ru|9 zZ#nwuTK^S%ce<+ggdJBE&fRrXN7O!{nu`%q`M{2Ef_+IRad2cf01P9pST9AOK>y75c!9}~)Et^6$`&Nm{wzWcm4c0j9DF!xJTpGrMp3esI4D_iiDe`sswXSu{dQZE_`^A11 z?Z@Hw=65mVu^%X`>;$mciK}XiZ{xw7I_!t)S00^JuxdCXhIRO~S*lPS(S^je`DH4E zxbKNs8RL`N?gCQ@YSOU=>0FE#Ku#DRO7JA&fu-X8b;3!^#{=7`WsDXUxfUsE(FKSQ z&=N`A7IwLq%+vt(F;z+T=uZNl=@K4|E%p{p^o5(BGjsE|WOR`%8+XgGW8xJTFJc4L zVY#L`OdnSM{HyS$fX1)3_JuNNH1aDsDqi>CzCT5=kY5zV<~29bX)c^I8R5n&ymHkx zj(QC4t#mDK;2xi8O%V;C{HqDQeM64=b4@sa*N_K0a&ro4+8LY6cFHz< ze|!g}zF|tDrP=`+U7KwKl20gdW1%!iN>1=uxA|NZJ2peruBOj?RBPb~8G;s6xIi6- z?_odhafsxoxiBf zwZZ)c*)FLc0#wE~bXw0TPBYl+h9hs|DYr_B4LR_YL@S1hQs=p zNEh%_fUvWZCbJtaF#kP5=(O#{8|g&Kmz1&8{@Lufw^DhtvKx955~aqxi2C=)Z-!Kd z+m-u+#^U4(HYn6a1w652kO0bYBt&goyx(n?MR^kI+{Q?0Y{G~W2) z0dS3fuJ?SU(6ZDp=kUley%PK}K_;YQyK|U|?7t9SHiyIfpT4a_kUVIhH4PSaj@3mo z`z}|mHhx1Pq?@(3vTBb5HTXuFAzFZEt0D-fw_kd=XvwIUh3VXTm{wbDA~cESd5cI1 zd>6=&AvG3yu+)`9oxmfrDQ(1fzv(_0l?bp{a364dXLRRBI8kBv!KsL;brY)#E3`o{ z3TlWUsS0{Voci?6MejccG9x_KiqN>So*1{25r6BSl9jUyR}1TgXBLL7Pr6Wv~Nu47;fbiU7TbL}>qmtl36YSZ() zVf@nqW(As~#`@bIC+AxSw!O5Pocf&rYaCFm?Jd?XR)p#@{!|5^Ws@wd855)mI^8y{ zws+VvGXW6%xoj@JkGb=~%oJ~7m6+uhOv?bH+jJJ~eFgp+}~*^C+3>R-MY!IZQoabCh( zN(T+z@Oyc^C)WqQESmh{d!!T8zS(!wX=R#hEKxMXy(eg zZ+Cwm1a%?;RH$h2_ws|nRjn8ZY!>3gn+6Ep4xT|AeFox7!rac2Lw?jsz}JqPE?5JG zok0}q1P;cuzs%Yrze|&d$oTr<`Lx{fbq2OV=!3v-ODq(n?|WxuhtmwJBIoW^^FB+D z-?Ok9HBKc5@)L(W&vmI{prL?4^OE9TR)bELS=<>*w%&aKjzi*@;5#P3moG@dm{Eke zhE#Is;&=o|{2GWai}7LYEI+gmc^Kj4K7w7n)+9godg?yB2?xs}pF1<*!Sv?D~Uvbkgs9xx9s#6zBv9l@ox>d#H6eqw^KZO;Vg}h!q zI33^$4}yF*q+q{DsJsa(SsV!YQ#zi^IF9MQV6i{SiN4dWWCi%YQ+hNc1r!^+<(YnB zG62-D`M3w3Q2;@X{S`n`{QO>migDpz0FK`->sYDOESs6u>-~<}_XN_6><2g7U#XC{ z$#Ig;n{_yEMnlvx-lP*;ts#DHV0r8j518>~33?Ak#jocW>uk>6V||p7{4rov#RS9c zdPD6r`qF1om9r!zS4Jk1>7fn#GCnmD=JIt1Na`X)=*LP7R!3XATgk`;&U*P<(0d z9p<0T&eYqQ9jot39FxpfuPSPYlfQ$s-*;+c1KL+cHIVcG5`H~^Ryu1Hk7%Nf$TCwR!SzG31@NHpm`mcp8v!wyWM49TjTxASJ-8JP*MTHLC}hF==PUOh8kaaXeGFGd<|e29vSDaS ztPeu&zv0^wN}Hahi`$pcDs~FVt2F;K!q}q*Y@{7i#stWfU`u2La4aerBKhV`^zG~j zJWvtZpcHIP7x*tfLSQcng6D(`HVp4=LWp_0Xt=2wEHjK)!DSz_Z?5J@>awRyk?azj zU-kdSs~cp))*pfJ_q7u`IsCq8F|OShB~D56S(Mwwlt?{yURE7#eI&WcpVq(@9Fd~g zeUiD!a4w51Nj(YzLnau+O3MDub|?loF0=<#jLztAM>PruE7yNDD0L}y=Ayuc?^?Ni zf~%GK=iEhn2}xKp7GonJx!JpDmDsco$|$XtRdUDwbM9$9s7x9-of2nKNj~?b@UOKz z9{`=Irz^ba-c&1vSQxSh;I2`cKc8-4)aCy%#bam;3_8vSJ-jw`_}lyukEC~z00EbC zI*dU3F21A)dSZr{qA5QF+{a%D`h#?8o%M?)*hWxuqnQD(TpcmfNq&UN$BmB)0!r8) zxno@Q?$_D&*4(rW6b+?-Y^5|*P`DHmJ%pI<6*yP)o}2^?>d7P#bd2j=vvx2mfLW@R zQLD`%buR*}nzNYNf%68w-D$7%v|=bXg1mYrdZy~}(@RRZ-U+Gx=nmCjVxr5Ag# zLw3R29-MHJl|`mRxj#sv@EfyR#-q>BE-XFEENbV$#dWM?!VjU8~kKZsd@G=HPrI{HiqN&j<92*-3$^M*;n@rG*i! zvi#?j;lc5w>@+r!6*CVUrN9as=S3?(ZBT979$5R#ZpPm?2VjIyQcEFp9orGR>f;G? zK<~FiYY6ow-&}|v7k?+03TC++so$)2~rN``u z>N%j$AbNQLX_!evzG8abf=15260vIXdz7K^a$YS)iw{@x5<|Rr#ii|ov=LJ{eu>dZYe_ip$ZuzvRu1dpjQK1BvP zH~m#t=2_wy>9+YkdNF-z` zQ*#7=^r%R*pIi2AI`>n9>(QJVE1k8?Ilav<)NUjW^O$}^yZZ{_Uwn!4Fq1`aslX;Y zj`XDIm`E1sz|wShA=?a@ZGKDSMU#Z3$E!1nZ)g^Eg3ZDoSN6@RXrGVCHvMIauS7d> zuJltXf9)LdTWdF!n%-iA9b#2$W#i??K)zYho^((ZqluvhAr@{H{diy0%@-~VW zKYC|2Ma)2^=skdLT@ZVqJfiCDqS@~qIGexL(BKy6Aw9ch0hoHN&E+m3*uka9+AIh3gTWdSe~W({-&^oFw`!j7$DcsF$7`pO?kRMK<9h=SV?cmyJIe`$4|zoI(6u9#qY9zM?#zNe^!Dl2>Z^dH`>`wSY# ztU;V*+g0R0DH6EnJA$U{QL&T~&s{`smeC2I-5mzv=v$l@iF;yN0hMibU=CG^e>J;+9k`Si9PzLaj$>}QKI6lWmO_o+_( zmhxA*0|-Na`+*J1qEMIXZf9rb#;pcOw>EDeDjb!|GumQ2!1ac;YqU|X;F@l1_lemzTN0J|U zFJF(kO21aHg)*KfuKT=BA{VDkOvlx(b{f|A9D69_BHUm#S$F>~`Mt@GesjLp3;reY zP~q>6Tt;`XkjqV?i7lqPbWGh`y<7dq<}pDHl-dDA4QG6`QDq)+vq_&HfW!}P6Cp4d zt>Qnli5ri*I1ILEOGD~3Y!@2^Jmcy1xDXmKolC?at}_6;neEfca0rLHT}NLpoUYh` zDbCtfZnYN&>}m-(F{5d1=)bBuZ?OcP`GmsQV@kn%JMJUIep`Avon#8=ATpEo-@hg& z12f-)R=HCD%pUjvbWa|P!}u)=wInpZG*LHKrZDMeC>Qils^IyY)x;kDRs4c3!DDOG zAptSsf#1X>kSli|Qka@S)6O4un-2aKL?bcV;$*>KSxHovjrfZ^-+c#>;(42yj71K| zzRyFiLrwv$rPcNA{mtv=o(*JDA0kS93>OE0D{KMJzLk$cc_5dCLWnJcFJd6_>BpE< z?aW9;^!;arQcIjloW&YL+~MkNO&a>N=pmhg>{SM<@`a&VeUA`ay*P@R$_+WS2%r?_ zs&Z%c`>ie+%!I=Lz>$9$7a`-`hoc&*dl60^whsaQ;~9~@JYn1Oc_bmgVVyAzUOYgZ z#j{`#D_YZ)(wa5;qzR#zo4a|-ANJjBB90r4Iun3*BkMxw_Ti>SjhktsmR|BPCLt>9 zZ_3eQjweI*-8+HNt)$9^s|+10w@sU!PY{`#BnF!ULS=#{k0Zr5`yOS?p8PfWbKT`6 z@T+PeRJ4`fj5t8bMs)0>o9|C>mBTlfQ*nFG#Rri-Q7}E}+eaz`LmO!`Y_pHkoAruu z`&!5VNnA3IG$}Pz)V&pt&AF!$E{J-;or3vWv3&Sl&9KzG+ae73Zf}=aP*SCI1{?0T z9SAC)W(?DSKOkcmW$(K5Bl?c@(5#>J#j@eq#ctX~$TIjkl>Wrfv%Ey+bl1Z-v?NxJ zwZ9!ae-MsHPUx&_W22?9$mCE%&~lzVG?hDXM%~gXGk+Q!Jf0BspkMWxy;^!n<6JIrSYjv z6F%~$8)0^qbUho9Sdf97b_n({$;|XH9-RHrohHuPcro@03KEPFejN&q?&nJFoIQY; zSI#uL6>2^^yOR!51OLO65xGas55dPG;3=uQ35ZYW04#+~byXQf^7Vq`G z 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 Pe7HEdAEg|=c=LY&YVNkY literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/third_party/refresh_rate/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..13b35eba55c6dabc3aac36f33d859266c18fa0d0 GIT binary patch literal 5680 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 literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/third_party/refresh_rate/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3f5fa40fb3d1e0710331a48de5d256da3f275d GIT binary patch literal 520 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 literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/third_party/refresh_rate/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000000000000000000000000000000000000..2f1632cfddf3d9dade342351e627a0a75609fb46 GIT binary patch literal 2218 zcmV;b2vzrqP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuE6iGxuRCodHTWf3-RTMruyW6Fu zQYeUM04eX6D5c0FCjKKPrco1(K`<0SL=crI{PC3-^hZU0kQie$gh-5!7z6SH6Q0J% zqot*`H1q{R5fHFYS}dje@;kG=v$L0(yY0?wY2%*c?A&{2?!D*x?m71{of2gv!$5|C z3>qG_BW}7K_yUcT3A5C6QD<+{aq?x;MAUyAiJn#Jv8_zZtQ{P zTRzbL3U9!qVuZzS$xKU10KiW~Bgdcv1-!uAhQxf3a7q+dU6lj?yoO4Lq4TUN4}h{N z*fIM=SS8|C2$(T>w$`t@3Tka!(r!7W`x z-isCVgQD^mG-MJ;XtJuK3V{Vy72GQ83KRWsHU?e*wrhKk=ApIYeDqLi;JI1e zuvv}5^Dc=k7F7?nm3nIw$NVmU-+R>> zyqOR$-2SDpJ}Pt;^RkJytDVXNTsu|mI1`~G7yw`EJR?VkGfNdqK9^^8P`JdtTV&tX4CNcV4 z&N06nZa??Fw1AgQOUSE2AmPE@WO(Fvo`%m`cDgiv(fAeRA%3AGXUbsGw{7Q`cY;1BI#ac3iN$$Hw z0LT0;xc%=q)me?Y*$xI@GRAw?+}>=9D+KTk??-HJ4=A>`V&vKFS75@MKdSF1JTq{S zc1!^8?YA|t+uKigaq!sT;Z!&0F2=k7F0PIU;F$leJLaw2UI6FL^w}OG&!;+b%ya1c z1n+6-inU<0VM-Y_s5iTElq)ThyF?StVcebpGI znw#+zLx2@ah{$_2jn+@}(zJZ{+}_N9BM;z)0yr|gF-4=Iyu@hI*Lk=-A8f#bAzc9f z`Kd6K--x@t04swJVC3JK1cHY-Hq+=|PN-VO;?^_C#;coU6TDP7Bt`;{JTG;!+jj(` zw5cLQ-(Cz-Tlb`A^w7|R56Ce;Wmr0)$KWOUZ6ai0PhzPeHwdl0H(etP zUV`va_i0s-4#DkNM8lUlqI7>YQLf)(lz9Q3Uw`)nc(z3{m5ZE77Ul$V%m)E}3&8L0 z-XaU|eB~Is08eORPk;=<>!1w)Kf}FOVS2l&9~A+@R#koFJ$Czd%Y(ENTV&A~U(IPI z;UY+gf+&6ioZ=roly<0Yst8ck>(M=S?B-ys3mLdM&)ex!hbt+ol|T6CTS+Sc0jv(& z7ijdvFwBq;0a{%3GGwkDKTeG`b+lyj0jjS1OMkYnepCdoosNY`*zmBIo*981BU%%U z@~$z0V`OVtIbEx5pa|Tct|Lg#ZQf5OYMUMRD>Wdxm5SAqV2}3!ceE-M2 z@O~lQ0OiKQp}o9I;?uxCgYVV?FH|?Riri*U$Zi_`V2eiA>l zdSm6;SEm6#T+SpcE8Ro_f2AwxzI z44hfe^WE3!h@W3RDyA_H440cpmYkv*)6m1XazTqw%=E5Xv7^@^^T7Q2wxr+Z2kVYr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/third_party/refresh_rate/example/macos/Runner/Configs/AppInfo.xcconfig b/third_party/refresh_rate/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 00000000..ac08879f --- /dev/null +++ b/third_party/refresh_rate/example/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 = refresh_rate_example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = in.qoder.refreshRateExample + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2026 in.qoder. All rights reserved. diff --git a/third_party/refresh_rate/example/macos/Runner/Configs/Debug.xcconfig b/third_party/refresh_rate/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 00000000..36b0fd94 --- /dev/null +++ b/third_party/refresh_rate/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/third_party/refresh_rate/example/macos/Runner/Configs/Release.xcconfig b/third_party/refresh_rate/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 00000000..dff4f495 --- /dev/null +++ b/third_party/refresh_rate/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/third_party/refresh_rate/example/macos/Runner/Configs/Warnings.xcconfig b/third_party/refresh_rate/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 00000000..42bcbf47 --- /dev/null +++ b/third_party/refresh_rate/example/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/third_party/refresh_rate/example/macos/Runner/DebugProfile.entitlements b/third_party/refresh_rate/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 00000000..dddb8a30 --- /dev/null +++ b/third_party/refresh_rate/example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/third_party/refresh_rate/example/macos/Runner/Info.plist b/third_party/refresh_rate/example/macos/Runner/Info.plist new file mode 100644 index 00000000..4789daa6 --- /dev/null +++ b/third_party/refresh_rate/example/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/third_party/refresh_rate/example/macos/Runner/MainFlutterWindow.swift b/third_party/refresh_rate/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 00000000..3cc05eb2 --- /dev/null +++ b/third_party/refresh_rate/example/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/third_party/refresh_rate/example/macos/Runner/Release.entitlements b/third_party/refresh_rate/example/macos/Runner/Release.entitlements new file mode 100644 index 00000000..852fa1a4 --- /dev/null +++ b/third_party/refresh_rate/example/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/third_party/refresh_rate/example/macos/RunnerTests/RunnerTests.swift b/third_party/refresh_rate/example/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..61f3bd1f --- /dev/null +++ b/third_party/refresh_rate/example/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Cocoa +import FlutterMacOS +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/third_party/refresh_rate/example/pubspec.yaml b/third_party/refresh_rate/example/pubspec.yaml new file mode 100644 index 00000000..83af76af --- /dev/null +++ b/third_party/refresh_rate/example/pubspec.yaml @@ -0,0 +1,23 @@ +name: refresh_rate_example +description: Example app demonstrating the refresh_rate plugin. +publish_to: 'none' +version: 1.0.0+1 + +environment: + sdk: ^3.5.0 + +dependencies: + flutter: + sdk: flutter + refresh_rate: + path: ../ + +dev_dependencies: + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: + uses-material-design: true diff --git a/third_party/refresh_rate/example/test/widget_test.dart b/third_party/refresh_rate/example/test/widget_test.dart new file mode 100644 index 00000000..7512d156 --- /dev/null +++ b/third_party/refresh_rate/example/test/widget_test.dart @@ -0,0 +1,11 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:refresh_rate_example/main.dart'; + +void main() { + testWidgets('App smoke test', (WidgetTester tester) async { + await tester.pumpWidget(const RefreshRateExampleApp()); + expect(find.text('Diagnostic Console'), findsOneWidget); + expect(find.text('Display Info'), findsOneWidget); + }); +} diff --git a/third_party/refresh_rate/example/test_driver/integration_test.dart b/third_party/refresh_rate/example/test_driver/integration_test.dart new file mode 100644 index 00000000..79c2b403 --- /dev/null +++ b/third_party/refresh_rate/example/test_driver/integration_test.dart @@ -0,0 +1,30 @@ +import 'dart:io'; + +import 'package:integration_test/integration_test_driver_extended.dart'; + +/// Flutter Drive driver for screenshot capture. +/// +/// Receives screenshot bytes from the device and writes them to the package +/// root `screenshots/` directory (one level up from `example/`). +/// +/// Run from the package root via `scripts/take_screenshots.sh`, or manually: +/// +/// cd example +/// flutter drive \ +/// --driver=test_driver/integration_test.dart \ +/// --target=integration_test/screenshot_test.dart \ +/// -d [device-id] +Future main() => integrationDriver( + onScreenshot: ( + String name, + List screenshotBytes, [ + Map? args, + ]) async { + final dir = Directory('../screenshots'); + if (!dir.existsSync()) dir.createSync(recursive: true); + final file = File('../screenshots/$name.png'); + await file.writeAsBytes(screenshotBytes); + stdout.writeln(' screenshot saved → screenshots/$name.png'); + return true; + }, + ); diff --git a/third_party/refresh_rate/example/web/favicon.png b/third_party/refresh_rate/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/web/icons/Icon-192.png b/third_party/refresh_rate/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/web/icons/Icon-512.png b/third_party/refresh_rate/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/web/icons/Icon-maskable-192.png b/third_party/refresh_rate/example/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/web/icons/Icon-maskable-512.png b/third_party/refresh_rate/example/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/web/index.html b/third_party/refresh_rate/example/web/index.html new file mode 100644 index 00000000..2b81a3a0 --- /dev/null +++ b/third_party/refresh_rate/example/web/index.html @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + refresh_rate_example + + + + + + + diff --git a/third_party/refresh_rate/example/web/manifest.json b/third_party/refresh_rate/example/web/manifest.json new file mode 100644 index 00000000..dca8856b --- /dev/null +++ b/third_party/refresh_rate/example/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "refresh_rate_example", + "short_name": "refresh_rate_example", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/third_party/refresh_rate/example/windows/CMakeLists.txt b/third_party/refresh_rate/example/windows/CMakeLists.txt new file mode 100644 index 00000000..6130394e --- /dev/null +++ b/third_party/refresh_rate/example/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(refresh_rate_example 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 "refresh_rate_example") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + 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() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# 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_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +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) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# 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. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/third_party/refresh_rate/example/windows/flutter/CMakeLists.txt b/third_party/refresh_rate/example/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000..903f4899 --- /dev/null +++ b/third_party/refresh_rate/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +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. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# 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/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app 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. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/third_party/refresh_rate/example/windows/flutter/generated_plugin_registrant.cc b/third_party/refresh_rate/example/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..31208abc --- /dev/null +++ b/third_party/refresh_rate/example/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,14 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + RefreshRatePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("RefreshRatePluginCApi")); +} diff --git a/third_party/refresh_rate/example/windows/flutter/generated_plugin_registrant.h b/third_party/refresh_rate/example/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..dc139d85 --- /dev/null +++ b/third_party/refresh_rate/example/windows/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 RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/third_party/refresh_rate/example/windows/flutter/generated_plugins.cmake b/third_party/refresh_rate/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 00000000..c7644834 --- /dev/null +++ b/third_party/refresh_rate/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + refresh_rate +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows 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}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/third_party/refresh_rate/example/windows/runner/CMakeLists.txt b/third_party/refresh_rate/example/windows/runner/CMakeLists.txt new file mode 100644 index 00000000..394917c0 --- /dev/null +++ b/third_party/refresh_rate/example/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, 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} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/third_party/refresh_rate/example/windows/runner/Runner.rc b/third_party/refresh_rate/example/windows/runner/Runner.rc new file mode 100644 index 00000000..cba01e6a --- /dev/null +++ b/third_party/refresh_rate/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "in.qoder" "\0" + VALUE "FileDescription", "refresh_rate_example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "refresh_rate_example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2026 in.qoder. All rights reserved." "\0" + VALUE "OriginalFilename", "refresh_rate_example.exe" "\0" + VALUE "ProductName", "refresh_rate_example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/third_party/refresh_rate/example/windows/runner/flutter_window.cpp b/third_party/refresh_rate/example/windows/runner/flutter_window.cpp new file mode 100644 index 00000000..955ee303 --- /dev/null +++ b/third_party/refresh_rate/example/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/third_party/refresh_rate/example/windows/runner/flutter_window.h b/third_party/refresh_rate/example/windows/runner/flutter_window.h new file mode 100644 index 00000000..6da0652f --- /dev/null +++ b/third_party/refresh_rate/example/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/third_party/refresh_rate/example/windows/runner/main.cpp b/third_party/refresh_rate/example/windows/runner/main.cpp new file mode 100644 index 00000000..9f77364f --- /dev/null +++ b/third_party/refresh_rate/example/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"refresh_rate_example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/third_party/refresh_rate/example/windows/runner/resource.h b/third_party/refresh_rate/example/windows/runner/resource.h new file mode 100644 index 00000000..66a65d1e --- /dev/null +++ b/third_party/refresh_rate/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/third_party/refresh_rate/example/windows/runner/resources/app_icon.ico b/third_party/refresh_rate/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/example/windows/runner/runner.exe.manifest b/third_party/refresh_rate/example/windows/runner/runner.exe.manifest new file mode 100644 index 00000000..153653e8 --- /dev/null +++ b/third_party/refresh_rate/example/windows/runner/runner.exe.manifest @@ -0,0 +1,14 @@ + + + + + PerMonitorV2 + + + + + + + + + diff --git a/third_party/refresh_rate/example/windows/runner/utils.cpp b/third_party/refresh_rate/example/windows/runner/utils.cpp new file mode 100644 index 00000000..3a0b4651 --- /dev/null +++ b/third_party/refresh_rate/example/windows/runner/utils.cpp @@ -0,0 +1,65 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + unsigned int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/third_party/refresh_rate/example/windows/runner/utils.h b/third_party/refresh_rate/example/windows/runner/utils.h new file mode 100644 index 00000000..3879d547 --- /dev/null +++ b/third_party/refresh_rate/example/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/third_party/refresh_rate/example/windows/runner/win32_window.cpp b/third_party/refresh_rate/example/windows/runner/win32_window.cpp new file mode 100644 index 00000000..60608d0f --- /dev/null +++ b/third_party/refresh_rate/example/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/third_party/refresh_rate/example/windows/runner/win32_window.h b/third_party/refresh_rate/example/windows/runner/win32_window.h new file mode 100644 index 00000000..e901dde6 --- /dev/null +++ b/third_party/refresh_rate/example/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/third_party/refresh_rate/ios/Classes/DisplayLinkSwizzle.h b/third_party/refresh_rate/ios/Classes/DisplayLinkSwizzle.h new file mode 100644 index 00000000..c3fcdbdb --- /dev/null +++ b/third_party/refresh_rate/ios/Classes/DisplayLinkSwizzle.h @@ -0,0 +1,14 @@ +#import + +/// Set the max frame rate cap for all display links. Pass 0 to disable. +void RRSetOverrideMaxRate(float rate); + +/// Get the current override max rate. 0 means no override. +float RRGetOverrideMaxRate(void); + +/// Tag a display link so the swizzle bypasses it (for monitoring/boost links). +void RRBypassDisplayLink(CADisplayLink * _Nonnull link); + +/// Apply the current override to all tracked (non-bypassed) display links. +/// Call this after changing the override rate. +void RRApplyOverrideToTrackedLinks(void); diff --git a/third_party/refresh_rate/ios/Classes/DisplayLinkSwizzle.m b/third_party/refresh_rate/ios/Classes/DisplayLinkSwizzle.m new file mode 100644 index 00000000..255cfcf4 --- /dev/null +++ b/third_party/refresh_rate/ios/Classes/DisplayLinkSwizzle.m @@ -0,0 +1,186 @@ +#import +#import + +// Key for tagging display links that should bypass the override. +static const void *RRBypassKey = &RRBypassKey; +// Key for storing the original (uncapped) frame rate range. +static const void *RROriginalRangeKey = &RROriginalRangeKey; + +/// Global override cap. 0 means no override (pass through). +static float _rr_overrideMaxRate = 0; + +/// Storage for tracked display links (weak-ish — we check validity). +static NSPointerArray *_rr_trackedLinks = nil; +static NSLock *_rr_lock = nil; + +// Store original IMPs — declared early so swizzled functions can reference them. +static IMP _rr_origAddToRunLoop = NULL; +static IMP _rr_origSetRange = NULL; + +#pragma mark - Public C interface (called from Swift) + +/// Set the max frame rate cap. Pass 0 to disable. +void RRSetOverrideMaxRate(float rate) { + _rr_overrideMaxRate = rate; +} + +float RRGetOverrideMaxRate(void) { + return _rr_overrideMaxRate; +} + +/// Tag a display link so the swizzle bypasses it. +void RRBypassDisplayLink(CADisplayLink *link) { + objc_setAssociatedObject(link, RRBypassKey, @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +/// Apply the current override to all tracked display links. +void RRApplyOverrideToTrackedLinks(void) { + if (!_rr_lock) return; + + [_rr_lock lock]; + + // Compact nil refs + [_rr_trackedLinks compact]; + + NSUInteger count = [_rr_trackedLinks count]; + for (NSUInteger i = 0; i < count; i++) { + CADisplayLink *link = [_rr_trackedLinks pointerAtIndex:i]; + if (!link) continue; + + // Skip bypassed links + if (objc_getAssociatedObject(link, RRBypassKey)) continue; + + if (@available(iOS 15.0, *)) { + // Read stored original range + NSDictionary *stored = objc_getAssociatedObject(link, RROriginalRangeKey); + + float cap = _rr_overrideMaxRate; + + if (cap > 0 && stored) { + float origMin = [stored[@"min"] floatValue]; + float origMax = [stored[@"max"] floatValue]; + float origPref = [stored[@"preferred"] floatValue]; + CAFrameRateRange capped = CAFrameRateRangeMake( + fminf(origMin, cap), + fminf(origMax, cap), + fminf(origPref, cap) + ); + // Use original IMP directly to avoid re-triggering our swizzle + // (which would overwrite the stored original with the capped value) + SEL sel = NSSelectorFromString(@"setPreferredFrameRateRange:"); + if (_rr_origSetRange) { + ((void (*)(id, SEL, CAFrameRateRange))_rr_origSetRange)(link, sel, capped); + } + } else if (cap <= 0 && stored) { + // Restore original + float origMin = [stored[@"min"] floatValue]; + float origMax = [stored[@"max"] floatValue]; + float origPref = [stored[@"preferred"] floatValue]; + SEL sel = NSSelectorFromString(@"setPreferredFrameRateRange:"); + if (_rr_origSetRange) { + ((void (*)(id, SEL, CAFrameRateRange))_rr_origSetRange)(link, sel, CAFrameRateRangeMake(origMin, origMax, origPref)); + } + } + } + } + + [_rr_lock unlock]; +} + +#pragma mark - Swizzled implementations + +/// Swizzled addToRunLoop:forMode: — tracks every display link. +static void rr_addToRunLoop(CADisplayLink *self, SEL _cmd, NSRunLoop *runloop, NSRunLoopMode mode) { + // Track this link + [_rr_lock lock]; + if (_rr_trackedLinks) { + // Check not already tracked + BOOL found = NO; + [_rr_trackedLinks compact]; + for (NSUInteger i = 0; i < [_rr_trackedLinks count]; i++) { + if ([_rr_trackedLinks pointerAtIndex:i] == (__bridge void *)self) { + found = YES; + break; + } + } + if (!found) { + [_rr_trackedLinks addPointer:(__bridge void *)self]; + } + } + [_rr_lock unlock]; + + // Call original — the IMP was saved during swizzle + ((void (*)(id, SEL, NSRunLoop *, NSRunLoopMode))_rr_origAddToRunLoop)(self, _cmd, runloop, mode); +} + +/// Swizzled setPreferredFrameRateRange: — stores original range and applies cap. +static void rr_setPreferredFrameRateRange(CADisplayLink *self, SEL _cmd, CAFrameRateRange range) { + // Bypass tagged links + if (objc_getAssociatedObject(self, RRBypassKey)) { + ((void (*)(id, SEL, CAFrameRateRange))_rr_origSetRange)(self, _cmd, range); + return; + } + + // Store original range + if (@available(iOS 15.0, *)) { + NSDictionary *stored = @{ + @"min": @(range.minimum), + @"max": @(range.maximum), + @"preferred": @(range.preferred), + }; + objc_setAssociatedObject(self, RROriginalRangeKey, stored, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + + // Apply cap if active + float cap = _rr_overrideMaxRate; + if (cap > 0) { + if (@available(iOS 15.0, *)) { + CAFrameRateRange capped = CAFrameRateRangeMake( + fminf(range.minimum, cap), + fminf(range.maximum, cap), + fminf(range.preferred, cap) + ); + ((void (*)(id, SEL, CAFrameRateRange))_rr_origSetRange)(self, _cmd, capped); + } else { + ((void (*)(id, SEL, CAFrameRateRange))_rr_origSetRange)(self, _cmd, range); + } + } else { + ((void (*)(id, SEL, CAFrameRateRange))_rr_origSetRange)(self, _cmd, range); + } +} + +#pragma mark - +load (runs before main, before Flutter engine starts) + +@interface RRDisplayLinkSwizzle : NSObject +@end + +@implementation RRDisplayLinkSwizzle + ++ (void)load { + _rr_trackedLinks = [NSPointerArray weakObjectsPointerArray]; + _rr_lock = [[NSLock alloc] init]; + + Class cls = [CADisplayLink class]; + + // Swizzle addToRunLoop:forMode: + { + SEL sel = @selector(addToRunLoop:forMode:); + Method method = class_getInstanceMethod(cls, sel); + if (method) { + _rr_origAddToRunLoop = method_getImplementation(method); + method_setImplementation(method, (IMP)rr_addToRunLoop); + } + } + + // Swizzle setPreferredFrameRateRange: (iOS 15+) + if (@available(iOS 15.0, *)) { + SEL sel = NSSelectorFromString(@"setPreferredFrameRateRange:"); + Method method = class_getInstanceMethod(cls, sel); + if (method) { + _rr_origSetRange = method_getImplementation(method); + method_setImplementation(method, (IMP)rr_setPreferredFrameRateRange); + } + } +} + +@end diff --git a/third_party/refresh_rate/ios/Classes/RefreshRatePlugin.swift b/third_party/refresh_rate/ios/Classes/RefreshRatePlugin.swift new file mode 100644 index 00000000..1e0d806c --- /dev/null +++ b/third_party/refresh_rate/ios/Classes/RefreshRatePlugin.swift @@ -0,0 +1,210 @@ +import Flutter +import UIKit +import QuartzCore + +public class RefreshRatePlugin: NSObject, FlutterPlugin, RefreshRateHostApi { + + private var flutterApi: RefreshRateFlutterApi? + private var displayLink: CADisplayLink? + private var boostDisplayLink: CADisplayLink? + private var lastReportedRate: Double = 0 + private var powerObserver: NSObjectProtocol? + private var thermalObserver: NSObjectProtocol? + + public static func register(with registrar: FlutterPluginRegistrar) { + let instance = RefreshRatePlugin() + RefreshRateHostApiSetup.setUp(binaryMessenger: registrar.messenger(), api: instance) + instance.flutterApi = RefreshRateFlutterApi(binaryMessenger: registrar.messenger()) + instance.startMonitoring() + } + + // MARK: - RefreshRateHostApi + + func getDisplayInfo() throws -> DisplayInfoMessage { + let maxRate = getMaxRefreshRate() + let currentRate = getCurrentRefreshRate() + let proMotion = isProMotionPlistKeySet() + let isLow = ProcessInfo.processInfo.isLowPowerModeEnabled + let thermal = thermalIndex() + + return DisplayInfoMessage( + currentRate: currentRate, + maxRate: maxRate, + minRate: 60.0, + supportedRates: getSupportedRefreshRates(), + isVariableRefreshRate: maxRate > 60, + engineTargetRate: currentRate, + iosProMotionEnabled: proMotion, + androidApiLevel: nil, + isLowPowerMode: isLow, + thermalStateIndex: thermal, + hasAdaptiveRefreshRate: maxRate > 60, + displayServer: nil, + monitorCount: nil + ) + } + + func enable() throws { try setToMax(forceHighest: false) } + func disable() throws { resetCap() } + func preferMax() throws { try setToMax(forceHighest: false) } + func preferDefault() throws { resetCap() } + + func matchContent(fps: Double) throws { + guard #available(iOS 15.0, *) else { return } + let maxRate = getMaxRefreshRate() + let multiple = max(1.0, (maxRate / fps).rounded(.down)) + let preferredMax = fps * multiple + RRSetOverrideMaxRate(Float(fps)) + RRApplyOverrideToTrackedLinks() + setupBoostDisplayLink(min: fps, max: preferredMax, preferred: preferredMax) + } + + func boost(durationMs: Int64) throws { + guard #available(iOS 15.0, *) else { return } + let maxRate = getMaxRefreshRate() + RRSetOverrideMaxRate(0) + RRApplyOverrideToTrackedLinks() + setupBoostDisplayLink(min: max(maxRate * 0.66, 60.0), max: maxRate, preferred: maxRate) + DispatchQueue.main.asyncAfter(deadline: .now() + Double(durationMs) / 1000.0) { + self.removeBoostDisplayLink() + RRSetOverrideMaxRate(0) + RRApplyOverrideToTrackedLinks() + } + } + + func setCategory(categoryIndex: Int64) throws { + switch categoryIndex { + case 3: try? enable() + case 0, 1: try? disable() + default: break + } + } + + func setTouchBoost(enabled: Bool) throws { + // No iOS equivalent; no-op + } + + func isSupported() throws -> Bool { + if #available(iOS 15.0, *) { return getMaxRefreshRate() > 60 } + return false + } + + // MARK: - Private control + + private func setToMax(forceHighest: Bool) throws { + let maxRate = getMaxRefreshRate() + let isPad = UIDevice.current.userInterfaceIdiom == .pad + let unlocked = isProMotionPlistKeySet() || isPad + if !unlocked { logPlistWarning(maxRate: maxRate) } + guard #available(iOS 15.0, *) else { return } + RRSetOverrideMaxRate(0) + RRApplyOverrideToTrackedLinks() + if forceHighest { + setupBoostDisplayLink(min: max(maxRate * 0.66, 60.0), max: maxRate, preferred: maxRate) + } else { + removeBoostDisplayLink() + } + } + + private func resetCap() { + RRSetOverrideMaxRate(60.0) + RRApplyOverrideToTrackedLinks() + removeBoostDisplayLink() + } + + // MARK: - Boost display link + + @available(iOS 15.0, *) + private func setupBoostDisplayLink(min: Double, max: Double, preferred: Double) { + removeBoostDisplayLink() + let link = CADisplayLink(target: self, selector: #selector(boostFired)) + RRBypassDisplayLink(link) + link.preferredFrameRateRange = CAFrameRateRange( + minimum: Float(min), maximum: Float(max), preferred: Float(preferred)) + link.add(to: .main, forMode: .common) + boostDisplayLink = link + } + + private func removeBoostDisplayLink() { + boostDisplayLink?.invalidate() + boostDisplayLink = nil + } + + @objc private func boostFired(_ link: CADisplayLink) {} + + // MARK: - Monitoring + + @objc private func monitorLinkFired(_ link: CADisplayLink) { + let rate = link.duration > 0 ? 1.0 / link.duration : 60.0 + if abs(rate - lastReportedRate) > 5.0 { + lastReportedRate = rate + let info = (try? getDisplayInfo()) ?? DisplayInfoMessage( + currentRate: rate, maxRate: rate, minRate: 60.0, + supportedRates: [60.0, rate], isVariableRefreshRate: rate > 60, + engineTargetRate: rate, iosProMotionEnabled: nil, + androidApiLevel: nil, isLowPowerMode: nil, + thermalStateIndex: nil, hasAdaptiveRefreshRate: nil, + displayServer: nil, monitorCount: nil) + flutterApi?.onDisplayInfoChanged(info: info) { _ in } + } + } + + private func startMonitoring() { + if displayLink == nil { + let link = CADisplayLink(target: self, selector: #selector(monitorLinkFired)) + RRBypassDisplayLink(link) + link.add(to: .main, forMode: .common) + displayLink = link + } + powerObserver = NotificationCenter.default.addObserver( + forName: .NSProcessInfoPowerStateDidChange, object: nil, queue: .main) { [weak self] _ in + guard let info = try? self?.getDisplayInfo() else { return } + self?.flutterApi?.onDisplayInfoChanged(info: info) { _ in } + } + thermalObserver = NotificationCenter.default.addObserver( + forName: ProcessInfo.thermalStateDidChangeNotification, object: nil, queue: .main) { [weak self] _ in + guard let info = try? self?.getDisplayInfo() else { return } + self?.flutterApi?.onDisplayInfoChanged(info: info) { _ in } + } + } + + // MARK: - Helpers + + private func getMaxRefreshRate() -> Double { Double(UIScreen.main.maximumFramesPerSecond) } + + private func getCurrentRefreshRate() -> Double { + if let link = displayLink, link.duration > 0 { return 1.0 / link.duration } + let max = getMaxRefreshRate() + let isPad = UIDevice.current.userInterfaceIdiom == .pad + return (max > 60 && (isProMotionPlistKeySet() || isPad)) ? max : 60.0 + } + + private func getSupportedRefreshRates() -> [Double] { + let max = getMaxRefreshRate() + return max > 60 ? [60.0, max] : [60.0] + } + + private func isProMotionPlistKeySet() -> Bool { + return Bundle.main.object(forInfoDictionaryKey: "CADisableMinimumFrameDurationOnPhone") as? Bool ?? false + } + + private func thermalIndex() -> Int64? { + switch ProcessInfo.processInfo.thermalState { + case .nominal: return 0 + case .fair: return 1 + case .serious: return 2 + case .critical: return 3 + @unknown default: return nil + } + } + + private func logPlistWarning(maxRate: Double) { + print(""" + ⚠️ [refresh_rate] CADisableMinimumFrameDurationOnPhone not set in Info.plist! + App is locked to 60Hz on this \(maxRate)Hz device. + Add to ios/Runner/Info.plist: + CADisableMinimumFrameDurationOnPhone + + """) + } +} diff --git a/third_party/refresh_rate/ios/Classes/generated/RefreshRateApi.swift b/third_party/refresh_rate/ios/Classes/generated/RefreshRateApi.swift new file mode 100644 index 00000000..70c230c8 --- /dev/null +++ b/third_party/refresh_rate/ios/Classes/generated/RefreshRateApi.swift @@ -0,0 +1,369 @@ +// Copyright 2026 Qoder (qoder.in). All rights reserved. +// Use of this source code is governed by a BSD-style license. +// Autogenerated from Pigeon (v22.7.4), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +/// Error class for passing custom error details to Dart side. +final class PigeonError: Error { + let code: String + let message: String? + let details: Any? + + init(code: String, message: String?, details: Any?) { + self.code = code + self.message = message + self.details = details + } + + var localizedDescription: String { + return + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + +private func wrapResult(_ result: Any?) -> [Any?] { + return [result] +} + +private func wrapError(_ error: Any) -> [Any?] { + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } + if let flutterError = error as? FlutterError { + return [ + flutterError.code, + flutterError.message, + flutterError.details, + ] + } + return [ + "\(error)", + "\(type(of: error))", + "Stacktrace: \(Thread.callStackSymbols)", + ] +} + +private func createConnectionError(withChannelName channelName: String) -> PigeonError { + return PigeonError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "") +} + +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + +/// Generated class from Pigeon that represents data sent in messages. +struct DisplayInfoMessage { + var currentRate: Double? = nil + var maxRate: Double? = nil + var minRate: Double? = nil + var supportedRates: [Double?]? = nil + var isVariableRefreshRate: Bool? = nil + var engineTargetRate: Double? = nil + var iosProMotionEnabled: Bool? = nil + var androidApiLevel: Int64? = nil + var isLowPowerMode: Bool? = nil + var thermalStateIndex: Int64? = nil + var hasAdaptiveRefreshRate: Bool? = nil + var displayServer: String? = nil + var monitorCount: Int64? = nil + + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> DisplayInfoMessage? { + let currentRate: Double? = nilOrValue(pigeonVar_list[0]) + let maxRate: Double? = nilOrValue(pigeonVar_list[1]) + let minRate: Double? = nilOrValue(pigeonVar_list[2]) + let supportedRates: [Double?]? = nilOrValue(pigeonVar_list[3]) + let isVariableRefreshRate: Bool? = nilOrValue(pigeonVar_list[4]) + let engineTargetRate: Double? = nilOrValue(pigeonVar_list[5]) + let iosProMotionEnabled: Bool? = nilOrValue(pigeonVar_list[6]) + let androidApiLevel: Int64? = nilOrValue(pigeonVar_list[7]) + let isLowPowerMode: Bool? = nilOrValue(pigeonVar_list[8]) + let thermalStateIndex: Int64? = nilOrValue(pigeonVar_list[9]) + let hasAdaptiveRefreshRate: Bool? = nilOrValue(pigeonVar_list[10]) + let displayServer: String? = nilOrValue(pigeonVar_list[11]) + let monitorCount: Int64? = nilOrValue(pigeonVar_list[12]) + + return DisplayInfoMessage( + currentRate: currentRate, + maxRate: maxRate, + minRate: minRate, + supportedRates: supportedRates, + isVariableRefreshRate: isVariableRefreshRate, + engineTargetRate: engineTargetRate, + iosProMotionEnabled: iosProMotionEnabled, + androidApiLevel: androidApiLevel, + isLowPowerMode: isLowPowerMode, + thermalStateIndex: thermalStateIndex, + hasAdaptiveRefreshRate: hasAdaptiveRefreshRate, + displayServer: displayServer, + monitorCount: monitorCount + ) + } + func toList() -> [Any?] { + return [ + currentRate, + maxRate, + minRate, + supportedRates, + isVariableRefreshRate, + engineTargetRate, + iosProMotionEnabled, + androidApiLevel, + isLowPowerMode, + thermalStateIndex, + hasAdaptiveRefreshRate, + displayServer, + monitorCount, + ] + } +} + +private class RefreshRateApiPigeonCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + return DisplayInfoMessage.fromList(self.readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class RefreshRateApiPigeonCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? DisplayInfoMessage { + super.writeByte(129) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class RefreshRateApiPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return RefreshRateApiPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return RefreshRateApiPigeonCodecWriter(data: data) + } +} + +class RefreshRateApiPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = RefreshRateApiPigeonCodec(readerWriter: RefreshRateApiPigeonCodecReaderWriter()) +} + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol RefreshRateHostApi { + func getDisplayInfo() throws -> DisplayInfoMessage + func enable() throws + func disable() throws + func preferMax() throws + func preferDefault() throws + func matchContent(fps: Double) throws + func boost(durationMs: Int64) throws + func setCategory(categoryIndex: Int64) throws + func setTouchBoost(enabled: Bool) throws + func isSupported() throws -> Bool +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class RefreshRateHostApiSetup { + static var codec: FlutterStandardMessageCodec { RefreshRateApiPigeonCodec.shared } + /// Sets up an instance of `RefreshRateHostApi` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: RefreshRateHostApi?, messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let getDisplayInfoChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.getDisplayInfo\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getDisplayInfoChannel.setMessageHandler { _, reply in + do { + let result = try api.getDisplayInfo() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getDisplayInfoChannel.setMessageHandler(nil) + } + let enableChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.enable\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + enableChannel.setMessageHandler { _, reply in + do { + try api.enable() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + enableChannel.setMessageHandler(nil) + } + let disableChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.disable\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + disableChannel.setMessageHandler { _, reply in + do { + try api.disable() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + disableChannel.setMessageHandler(nil) + } + let preferMaxChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.preferMax\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + preferMaxChannel.setMessageHandler { _, reply in + do { + try api.preferMax() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + preferMaxChannel.setMessageHandler(nil) + } + let preferDefaultChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.preferDefault\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + preferDefaultChannel.setMessageHandler { _, reply in + do { + try api.preferDefault() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + preferDefaultChannel.setMessageHandler(nil) + } + let matchContentChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.matchContent\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + matchContentChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let fpsArg = args[0] as! Double + do { + try api.matchContent(fps: fpsArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + matchContentChannel.setMessageHandler(nil) + } + let boostChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.boost\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + boostChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let durationMsArg = args[0] as! Int64 + do { + try api.boost(durationMs: durationMsArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + boostChannel.setMessageHandler(nil) + } + let setCategoryChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.setCategory\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setCategoryChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let categoryIndexArg = args[0] as! Int64 + do { + try api.setCategory(categoryIndex: categoryIndexArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setCategoryChannel.setMessageHandler(nil) + } + let setTouchBoostChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.setTouchBoost\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setTouchBoostChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let enabledArg = args[0] as! Bool + do { + try api.setTouchBoost(enabled: enabledArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setTouchBoostChannel.setMessageHandler(nil) + } + let isSupportedChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.isSupported\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + isSupportedChannel.setMessageHandler { _, reply in + do { + let result = try api.isSupported() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + isSupportedChannel.setMessageHandler(nil) + } + } +} +/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. +protocol RefreshRateFlutterApiProtocol { + func onDisplayInfoChanged(info infoArg: DisplayInfoMessage, completion: @escaping (Result) -> Void) +} +class RefreshRateFlutterApi: RefreshRateFlutterApiProtocol { + private let binaryMessenger: FlutterBinaryMessenger + private let messageChannelSuffix: String + init(binaryMessenger: FlutterBinaryMessenger, messageChannelSuffix: String = "") { + self.binaryMessenger = binaryMessenger + self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + } + var codec: RefreshRateApiPigeonCodec { + return RefreshRateApiPigeonCodec.shared + } + func onDisplayInfoChanged(info infoArg: DisplayInfoMessage, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.refresh_rate.RefreshRateFlutterApi.onDisplayInfoChanged\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([infoArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(Void())) + } + } + } +} diff --git a/third_party/refresh_rate/ios/refresh_rate.podspec b/third_party/refresh_rate/ios/refresh_rate.podspec new file mode 100644 index 00000000..a6912cfb --- /dev/null +++ b/third_party/refresh_rate/ios/refresh_rate.podspec @@ -0,0 +1,22 @@ +Pod::Spec.new do |s| + s.name = 'refresh_rate' + s.version = '1.0.2' + s.summary = 'Control display refresh rates in Flutter.' + s.description = <<-DESC +Cross-platform Flutter plugin to query and control display refresh rates. +Unlock high refresh rates (90Hz/120Hz/144Hz) by properly communicating +with the OS compositor — something Flutter doesn't do by default. + DESC + s.homepage = 'https://qoder.in' + s.license = { :file => '../LICENSE' } + s.author = { 'Qoder' => 'dev@qoder.in' } + s.source = { :path => '.' } + s.source_files = [ + 'refresh_rate/Sources/refresh_rate/**/*.swift', + 'refresh_rate/Sources/refresh_rate_objc/**/*.{h,m}' + ] + s.public_header_files = 'refresh_rate/Sources/refresh_rate_objc/include/**/*.h' + s.dependency 'Flutter' + s.platform = :ios, '12.0' + s.swift_version = '5.0' +end diff --git a/third_party/refresh_rate/ios/refresh_rate/Package.swift b/third_party/refresh_rate/ios/refresh_rate/Package.swift new file mode 100644 index 00000000..08b8c9ab --- /dev/null +++ b/third_party/refresh_rate/ios/refresh_rate/Package.swift @@ -0,0 +1,32 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. +// +import PackageDescription + +let package = Package( + name: "refresh_rate", + platforms: [ + .iOS(.v12), + ], + products: [ + .library(name: "refresh-rate", targets: ["refresh_rate"]), + ], + dependencies: [ + .package(name: "FlutterFramework", path: "../FlutterFramework"), + ], + targets: [ + .target( + name: "refresh_rate_objc", + path: "Sources/refresh_rate_objc", + publicHeadersPath: "include" + ), + .target( + name: "refresh_rate", + dependencies: [ + "refresh_rate_objc", + .product(name: "FlutterFramework", package: "FlutterFramework"), + ], + path: "Sources/refresh_rate" + ), + ] +) diff --git a/third_party/refresh_rate/ios/refresh_rate/Sources/refresh_rate/RefreshRatePlugin.swift b/third_party/refresh_rate/ios/refresh_rate/Sources/refresh_rate/RefreshRatePlugin.swift new file mode 100644 index 00000000..6845df98 --- /dev/null +++ b/third_party/refresh_rate/ios/refresh_rate/Sources/refresh_rate/RefreshRatePlugin.swift @@ -0,0 +1,214 @@ +import Flutter +import UIKit +import QuartzCore + +#if SWIFT_PACKAGE +import refresh_rate_objc +#endif + +public class RefreshRatePlugin: NSObject, FlutterPlugin, RefreshRateHostApi { + + private var flutterApi: RefreshRateFlutterApi? + private var displayLink: CADisplayLink? + private var boostDisplayLink: CADisplayLink? + private var lastReportedRate: Double = 0 + private var powerObserver: NSObjectProtocol? + private var thermalObserver: NSObjectProtocol? + + public static func register(with registrar: FlutterPluginRegistrar) { + let instance = RefreshRatePlugin() + RefreshRateHostApiSetup.setUp(binaryMessenger: registrar.messenger(), api: instance) + instance.flutterApi = RefreshRateFlutterApi(binaryMessenger: registrar.messenger()) + instance.startMonitoring() + } + + // MARK: - RefreshRateHostApi + + func getDisplayInfo() throws -> DisplayInfoMessage { + let maxRate = getMaxRefreshRate() + let currentRate = getCurrentRefreshRate() + let proMotion = isProMotionPlistKeySet() + let isLow = ProcessInfo.processInfo.isLowPowerModeEnabled + let thermal = thermalIndex() + + return DisplayInfoMessage( + currentRate: currentRate, + maxRate: maxRate, + minRate: 60.0, + supportedRates: getSupportedRefreshRates(), + isVariableRefreshRate: maxRate > 60, + engineTargetRate: currentRate, + iosProMotionEnabled: proMotion, + androidApiLevel: nil, + isLowPowerMode: isLow, + thermalStateIndex: thermal, + hasAdaptiveRefreshRate: maxRate > 60, + displayServer: nil, + monitorCount: nil + ) + } + + func enable() throws { try setToMax(forceHighest: false) } + func disable() throws { resetCap() } + func preferMax() throws { try setToMax(forceHighest: false) } + func preferDefault() throws { resetCap() } + + func matchContent(fps: Double) throws { + guard #available(iOS 15.0, *) else { return } + let maxRate = getMaxRefreshRate() + let multiple = max(1.0, (maxRate / fps).rounded(.down)) + let preferredMax = fps * multiple + RRSetOverrideMaxRate(Float(fps)) + RRApplyOverrideToTrackedLinks() + setupBoostDisplayLink(min: fps, max: preferredMax, preferred: preferredMax) + } + + func boost(durationMs: Int64) throws { + guard #available(iOS 15.0, *) else { return } + let maxRate = getMaxRefreshRate() + RRSetOverrideMaxRate(0) + RRApplyOverrideToTrackedLinks() + setupBoostDisplayLink(min: max(maxRate * 0.66, 60.0), max: maxRate, preferred: maxRate) + DispatchQueue.main.asyncAfter(deadline: .now() + Double(durationMs) / 1000.0) { + self.removeBoostDisplayLink() + RRSetOverrideMaxRate(0) + RRApplyOverrideToTrackedLinks() + } + } + + func setCategory(categoryIndex: Int64) throws { + switch categoryIndex { + case 3: try? enable() + case 0, 1: try? disable() + default: break + } + } + + func setTouchBoost(enabled: Bool) throws { + // No iOS equivalent; no-op + } + + func isSupported() throws -> Bool { + if #available(iOS 15.0, *) { return getMaxRefreshRate() > 60 } + return false + } + + // MARK: - Private control + + private func setToMax(forceHighest: Bool) throws { + let maxRate = getMaxRefreshRate() + let isPad = UIDevice.current.userInterfaceIdiom == .pad + let unlocked = isProMotionPlistKeySet() || isPad + if !unlocked { logPlistWarning(maxRate: maxRate) } + guard #available(iOS 15.0, *) else { return } + RRSetOverrideMaxRate(0) + RRApplyOverrideToTrackedLinks() + if forceHighest { + setupBoostDisplayLink(min: max(maxRate * 0.66, 60.0), max: maxRate, preferred: maxRate) + } else { + removeBoostDisplayLink() + } + } + + private func resetCap() { + RRSetOverrideMaxRate(60.0) + RRApplyOverrideToTrackedLinks() + removeBoostDisplayLink() + } + + // MARK: - Boost display link + + @available(iOS 15.0, *) + private func setupBoostDisplayLink(min: Double, max: Double, preferred: Double) { + removeBoostDisplayLink() + let link = CADisplayLink(target: self, selector: #selector(boostFired)) + RRBypassDisplayLink(link) + link.preferredFrameRateRange = CAFrameRateRange( + minimum: Float(min), maximum: Float(max), preferred: Float(preferred)) + link.add(to: .main, forMode: .common) + boostDisplayLink = link + } + + private func removeBoostDisplayLink() { + boostDisplayLink?.invalidate() + boostDisplayLink = nil + } + + @objc private func boostFired(_ link: CADisplayLink) {} + + // MARK: - Monitoring + + @objc private func monitorLinkFired(_ link: CADisplayLink) { + let rate = link.duration > 0 ? 1.0 / link.duration : 60.0 + if abs(rate - lastReportedRate) > 5.0 { + lastReportedRate = rate + let info = (try? getDisplayInfo()) ?? DisplayInfoMessage( + currentRate: rate, maxRate: rate, minRate: 60.0, + supportedRates: [60.0, rate], isVariableRefreshRate: rate > 60, + engineTargetRate: rate, iosProMotionEnabled: nil, + androidApiLevel: nil, isLowPowerMode: nil, + thermalStateIndex: nil, hasAdaptiveRefreshRate: nil, + displayServer: nil, monitorCount: nil) + flutterApi?.onDisplayInfoChanged(info: info) { _ in } + } + } + + private func startMonitoring() { + if displayLink == nil { + let link = CADisplayLink(target: self, selector: #selector(monitorLinkFired)) + RRBypassDisplayLink(link) + link.add(to: .main, forMode: .common) + displayLink = link + } + powerObserver = NotificationCenter.default.addObserver( + forName: .NSProcessInfoPowerStateDidChange, object: nil, queue: .main) { [weak self] _ in + guard let info = try? self?.getDisplayInfo() else { return } + self?.flutterApi?.onDisplayInfoChanged(info: info) { _ in } + } + thermalObserver = NotificationCenter.default.addObserver( + forName: ProcessInfo.thermalStateDidChangeNotification, object: nil, queue: .main) { [weak self] _ in + guard let info = try? self?.getDisplayInfo() else { return } + self?.flutterApi?.onDisplayInfoChanged(info: info) { _ in } + } + } + + // MARK: - Helpers + + private func getMaxRefreshRate() -> Double { Double(UIScreen.main.maximumFramesPerSecond) } + + private func getCurrentRefreshRate() -> Double { + if let link = displayLink, link.duration > 0 { return 1.0 / link.duration } + let max = getMaxRefreshRate() + let isPad = UIDevice.current.userInterfaceIdiom == .pad + return (max > 60 && (isProMotionPlistKeySet() || isPad)) ? max : 60.0 + } + + private func getSupportedRefreshRates() -> [Double] { + let max = getMaxRefreshRate() + return max > 60 ? [60.0, max] : [60.0] + } + + private func isProMotionPlistKeySet() -> Bool { + return Bundle.main.object(forInfoDictionaryKey: "CADisableMinimumFrameDurationOnPhone") as? Bool ?? false + } + + private func thermalIndex() -> Int64? { + switch ProcessInfo.processInfo.thermalState { + case .nominal: return 0 + case .fair: return 1 + case .serious: return 2 + case .critical: return 3 + @unknown default: return nil + } + } + + private func logPlistWarning(maxRate: Double) { + print(""" + ⚠️ [refresh_rate] CADisableMinimumFrameDurationOnPhone not set in Info.plist! + App is locked to 60Hz on this \(maxRate)Hz device. + Add to ios/Runner/Info.plist: + CADisableMinimumFrameDurationOnPhone + + """) + } +} diff --git a/third_party/refresh_rate/ios/refresh_rate/Sources/refresh_rate/generated/RefreshRateApi.swift b/third_party/refresh_rate/ios/refresh_rate/Sources/refresh_rate/generated/RefreshRateApi.swift new file mode 100644 index 00000000..70c230c8 --- /dev/null +++ b/third_party/refresh_rate/ios/refresh_rate/Sources/refresh_rate/generated/RefreshRateApi.swift @@ -0,0 +1,369 @@ +// Copyright 2026 Qoder (qoder.in). All rights reserved. +// Use of this source code is governed by a BSD-style license. +// Autogenerated from Pigeon (v22.7.4), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +/// Error class for passing custom error details to Dart side. +final class PigeonError: Error { + let code: String + let message: String? + let details: Any? + + init(code: String, message: String?, details: Any?) { + self.code = code + self.message = message + self.details = details + } + + var localizedDescription: String { + return + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + +private func wrapResult(_ result: Any?) -> [Any?] { + return [result] +} + +private func wrapError(_ error: Any) -> [Any?] { + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } + if let flutterError = error as? FlutterError { + return [ + flutterError.code, + flutterError.message, + flutterError.details, + ] + } + return [ + "\(error)", + "\(type(of: error))", + "Stacktrace: \(Thread.callStackSymbols)", + ] +} + +private func createConnectionError(withChannelName channelName: String) -> PigeonError { + return PigeonError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "") +} + +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + +/// Generated class from Pigeon that represents data sent in messages. +struct DisplayInfoMessage { + var currentRate: Double? = nil + var maxRate: Double? = nil + var minRate: Double? = nil + var supportedRates: [Double?]? = nil + var isVariableRefreshRate: Bool? = nil + var engineTargetRate: Double? = nil + var iosProMotionEnabled: Bool? = nil + var androidApiLevel: Int64? = nil + var isLowPowerMode: Bool? = nil + var thermalStateIndex: Int64? = nil + var hasAdaptiveRefreshRate: Bool? = nil + var displayServer: String? = nil + var monitorCount: Int64? = nil + + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> DisplayInfoMessage? { + let currentRate: Double? = nilOrValue(pigeonVar_list[0]) + let maxRate: Double? = nilOrValue(pigeonVar_list[1]) + let minRate: Double? = nilOrValue(pigeonVar_list[2]) + let supportedRates: [Double?]? = nilOrValue(pigeonVar_list[3]) + let isVariableRefreshRate: Bool? = nilOrValue(pigeonVar_list[4]) + let engineTargetRate: Double? = nilOrValue(pigeonVar_list[5]) + let iosProMotionEnabled: Bool? = nilOrValue(pigeonVar_list[6]) + let androidApiLevel: Int64? = nilOrValue(pigeonVar_list[7]) + let isLowPowerMode: Bool? = nilOrValue(pigeonVar_list[8]) + let thermalStateIndex: Int64? = nilOrValue(pigeonVar_list[9]) + let hasAdaptiveRefreshRate: Bool? = nilOrValue(pigeonVar_list[10]) + let displayServer: String? = nilOrValue(pigeonVar_list[11]) + let monitorCount: Int64? = nilOrValue(pigeonVar_list[12]) + + return DisplayInfoMessage( + currentRate: currentRate, + maxRate: maxRate, + minRate: minRate, + supportedRates: supportedRates, + isVariableRefreshRate: isVariableRefreshRate, + engineTargetRate: engineTargetRate, + iosProMotionEnabled: iosProMotionEnabled, + androidApiLevel: androidApiLevel, + isLowPowerMode: isLowPowerMode, + thermalStateIndex: thermalStateIndex, + hasAdaptiveRefreshRate: hasAdaptiveRefreshRate, + displayServer: displayServer, + monitorCount: monitorCount + ) + } + func toList() -> [Any?] { + return [ + currentRate, + maxRate, + minRate, + supportedRates, + isVariableRefreshRate, + engineTargetRate, + iosProMotionEnabled, + androidApiLevel, + isLowPowerMode, + thermalStateIndex, + hasAdaptiveRefreshRate, + displayServer, + monitorCount, + ] + } +} + +private class RefreshRateApiPigeonCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + return DisplayInfoMessage.fromList(self.readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class RefreshRateApiPigeonCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? DisplayInfoMessage { + super.writeByte(129) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class RefreshRateApiPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return RefreshRateApiPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return RefreshRateApiPigeonCodecWriter(data: data) + } +} + +class RefreshRateApiPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = RefreshRateApiPigeonCodec(readerWriter: RefreshRateApiPigeonCodecReaderWriter()) +} + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol RefreshRateHostApi { + func getDisplayInfo() throws -> DisplayInfoMessage + func enable() throws + func disable() throws + func preferMax() throws + func preferDefault() throws + func matchContent(fps: Double) throws + func boost(durationMs: Int64) throws + func setCategory(categoryIndex: Int64) throws + func setTouchBoost(enabled: Bool) throws + func isSupported() throws -> Bool +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class RefreshRateHostApiSetup { + static var codec: FlutterStandardMessageCodec { RefreshRateApiPigeonCodec.shared } + /// Sets up an instance of `RefreshRateHostApi` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: RefreshRateHostApi?, messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let getDisplayInfoChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.getDisplayInfo\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getDisplayInfoChannel.setMessageHandler { _, reply in + do { + let result = try api.getDisplayInfo() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getDisplayInfoChannel.setMessageHandler(nil) + } + let enableChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.enable\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + enableChannel.setMessageHandler { _, reply in + do { + try api.enable() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + enableChannel.setMessageHandler(nil) + } + let disableChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.disable\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + disableChannel.setMessageHandler { _, reply in + do { + try api.disable() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + disableChannel.setMessageHandler(nil) + } + let preferMaxChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.preferMax\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + preferMaxChannel.setMessageHandler { _, reply in + do { + try api.preferMax() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + preferMaxChannel.setMessageHandler(nil) + } + let preferDefaultChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.preferDefault\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + preferDefaultChannel.setMessageHandler { _, reply in + do { + try api.preferDefault() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + preferDefaultChannel.setMessageHandler(nil) + } + let matchContentChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.matchContent\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + matchContentChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let fpsArg = args[0] as! Double + do { + try api.matchContent(fps: fpsArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + matchContentChannel.setMessageHandler(nil) + } + let boostChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.boost\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + boostChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let durationMsArg = args[0] as! Int64 + do { + try api.boost(durationMs: durationMsArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + boostChannel.setMessageHandler(nil) + } + let setCategoryChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.setCategory\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setCategoryChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let categoryIndexArg = args[0] as! Int64 + do { + try api.setCategory(categoryIndex: categoryIndexArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setCategoryChannel.setMessageHandler(nil) + } + let setTouchBoostChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.setTouchBoost\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setTouchBoostChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let enabledArg = args[0] as! Bool + do { + try api.setTouchBoost(enabled: enabledArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setTouchBoostChannel.setMessageHandler(nil) + } + let isSupportedChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.isSupported\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + isSupportedChannel.setMessageHandler { _, reply in + do { + let result = try api.isSupported() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + isSupportedChannel.setMessageHandler(nil) + } + } +} +/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. +protocol RefreshRateFlutterApiProtocol { + func onDisplayInfoChanged(info infoArg: DisplayInfoMessage, completion: @escaping (Result) -> Void) +} +class RefreshRateFlutterApi: RefreshRateFlutterApiProtocol { + private let binaryMessenger: FlutterBinaryMessenger + private let messageChannelSuffix: String + init(binaryMessenger: FlutterBinaryMessenger, messageChannelSuffix: String = "") { + self.binaryMessenger = binaryMessenger + self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + } + var codec: RefreshRateApiPigeonCodec { + return RefreshRateApiPigeonCodec.shared + } + func onDisplayInfoChanged(info infoArg: DisplayInfoMessage, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.refresh_rate.RefreshRateFlutterApi.onDisplayInfoChanged\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([infoArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(Void())) + } + } + } +} diff --git a/third_party/refresh_rate/ios/refresh_rate/Sources/refresh_rate_objc/DisplayLinkSwizzle.m b/third_party/refresh_rate/ios/refresh_rate/Sources/refresh_rate_objc/DisplayLinkSwizzle.m new file mode 100644 index 00000000..2e40973b --- /dev/null +++ b/third_party/refresh_rate/ios/refresh_rate/Sources/refresh_rate_objc/DisplayLinkSwizzle.m @@ -0,0 +1,188 @@ +#import "DisplayLinkSwizzle.h" +#import + +// Key for tagging display links that should bypass the override. +static const void *RRBypassKey = &RRBypassKey; +// Key for storing the original (uncapped) frame rate range. +static const void *RROriginalRangeKey = &RROriginalRangeKey; + +/// Global override cap. 0 means no override (pass through). +static float _rr_overrideMaxRate = 0; + +/// Storage for tracked display links (weak-ish — we check validity). +static NSPointerArray *_rr_trackedLinks = nil; +static NSLock *_rr_lock = nil; + +// Store original IMPs — declared early so swizzled functions can reference them. +static IMP _rr_origAddToRunLoop = NULL; +static IMP _rr_origSetRange = NULL; + +#pragma mark - Public C interface (called from Swift) + +/// Set the max frame rate cap. Pass 0 to disable. +void RRSetOverrideMaxRate(float rate) { + _rr_overrideMaxRate = rate; +} + +float RRGetOverrideMaxRate(void) { + return _rr_overrideMaxRate; +} + +/// Tag a display link so the swizzle bypasses it. +void RRBypassDisplayLink(CADisplayLink *link) { + objc_setAssociatedObject(link, RRBypassKey, @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +/// Apply the current override to all tracked display links. +void RRApplyOverrideToTrackedLinks(void) { + if (!_rr_lock) return; + + [_rr_lock lock]; + + // Compact nil refs + [_rr_trackedLinks compact]; + + NSUInteger count = [_rr_trackedLinks count]; + for (NSUInteger i = 0; i < count; i++) { + CADisplayLink *link = [_rr_trackedLinks pointerAtIndex:i]; + if (!link) continue; + + // Skip bypassed links + if (objc_getAssociatedObject(link, RRBypassKey)) continue; + + if (@available(iOS 15.0, *)) { + // Read stored original range + NSDictionary *stored = objc_getAssociatedObject(link, RROriginalRangeKey); + + float cap = _rr_overrideMaxRate; + + if (cap > 0 && stored) { + float origMin = [stored[@"min"] floatValue]; + float origMax = [stored[@"max"] floatValue]; + float origPref = [stored[@"preferred"] floatValue]; + CAFrameRateRange capped = CAFrameRateRangeMake( + fminf(origMin, cap), + fminf(origMax, cap), + fminf(origPref, cap) + ); + // Use original IMP directly to avoid re-triggering our swizzle + // (which would overwrite the stored original with the capped value) + SEL sel = NSSelectorFromString(@"setPreferredFrameRateRange:"); + if (_rr_origSetRange) { + ((void (*)(id, SEL, CAFrameRateRange))_rr_origSetRange)(link, sel, capped); + } + } else if (cap <= 0 && stored) { + // Restore original + float origMin = [stored[@"min"] floatValue]; + float origMax = [stored[@"max"] floatValue]; + float origPref = [stored[@"preferred"] floatValue]; + SEL sel = NSSelectorFromString(@"setPreferredFrameRateRange:"); + if (_rr_origSetRange) { + ((void (*)(id, SEL, CAFrameRateRange))_rr_origSetRange)(link, sel, CAFrameRateRangeMake(origMin, origMax, origPref)); + } + } + } + } + + [_rr_lock unlock]; +} + +#pragma mark - Swizzled implementations + +/// Swizzled addToRunLoop:forMode: — tracks every display link. +static void rr_addToRunLoop(CADisplayLink *self, SEL _cmd, NSRunLoop *runloop, NSRunLoopMode mode) { + // Track this link + [_rr_lock lock]; + if (_rr_trackedLinks) { + // Check not already tracked + BOOL found = NO; + [_rr_trackedLinks compact]; + for (NSUInteger i = 0; i < [_rr_trackedLinks count]; i++) { + if ([_rr_trackedLinks pointerAtIndex:i] == (__bridge void *)self) { + found = YES; + break; + } + } + if (!found) { + [_rr_trackedLinks addPointer:(__bridge void *)self]; + } + } + [_rr_lock unlock]; + + // Call original — the IMP was saved during swizzle + ((void (*)(id, SEL, NSRunLoop *, NSRunLoopMode))_rr_origAddToRunLoop)(self, _cmd, runloop, mode); +} + +/// Swizzled setPreferredFrameRateRange: — stores original range and applies cap. +static void rr_setPreferredFrameRateRange(CADisplayLink *self, SEL _cmd, CAFrameRateRange range) API_AVAILABLE(ios(15.0)); + +static void rr_setPreferredFrameRateRange(CADisplayLink *self, SEL _cmd, CAFrameRateRange range) { + // Bypass tagged links + if (objc_getAssociatedObject(self, RRBypassKey)) { + ((void (*)(id, SEL, CAFrameRateRange))_rr_origSetRange)(self, _cmd, range); + return; + } + + // Store original range + if (@available(iOS 15.0, *)) { + NSDictionary *stored = @{ + @"min": @(range.minimum), + @"max": @(range.maximum), + @"preferred": @(range.preferred), + }; + objc_setAssociatedObject(self, RROriginalRangeKey, stored, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + + // Apply cap if active + float cap = _rr_overrideMaxRate; + if (cap > 0) { + if (@available(iOS 15.0, *)) { + CAFrameRateRange capped = CAFrameRateRangeMake( + fminf(range.minimum, cap), + fminf(range.maximum, cap), + fminf(range.preferred, cap) + ); + ((void (*)(id, SEL, CAFrameRateRange))_rr_origSetRange)(self, _cmd, capped); + } else { + ((void (*)(id, SEL, CAFrameRateRange))_rr_origSetRange)(self, _cmd, range); + } + } else { + ((void (*)(id, SEL, CAFrameRateRange))_rr_origSetRange)(self, _cmd, range); + } +} + +#pragma mark - +load (runs before main, before Flutter engine starts) + +@interface RRDisplayLinkSwizzle : NSObject +@end + +@implementation RRDisplayLinkSwizzle + ++ (void)load { + _rr_trackedLinks = [NSPointerArray weakObjectsPointerArray]; + _rr_lock = [[NSLock alloc] init]; + + Class cls = [CADisplayLink class]; + + // Swizzle addToRunLoop:forMode: + { + SEL sel = @selector(addToRunLoop:forMode:); + Method method = class_getInstanceMethod(cls, sel); + if (method) { + _rr_origAddToRunLoop = method_getImplementation(method); + method_setImplementation(method, (IMP)rr_addToRunLoop); + } + } + + // Swizzle setPreferredFrameRateRange: (iOS 15+) + if (@available(iOS 15.0, *)) { + SEL sel = NSSelectorFromString(@"setPreferredFrameRateRange:"); + Method method = class_getInstanceMethod(cls, sel); + if (method) { + _rr_origSetRange = method_getImplementation(method); + method_setImplementation(method, (IMP)rr_setPreferredFrameRateRange); + } + } +} + +@end diff --git a/third_party/refresh_rate/ios/refresh_rate/Sources/refresh_rate_objc/include/DisplayLinkSwizzle.h b/third_party/refresh_rate/ios/refresh_rate/Sources/refresh_rate_objc/include/DisplayLinkSwizzle.h new file mode 100644 index 00000000..c3fcdbdb --- /dev/null +++ b/third_party/refresh_rate/ios/refresh_rate/Sources/refresh_rate_objc/include/DisplayLinkSwizzle.h @@ -0,0 +1,14 @@ +#import + +/// Set the max frame rate cap for all display links. Pass 0 to disable. +void RRSetOverrideMaxRate(float rate); + +/// Get the current override max rate. 0 means no override. +float RRGetOverrideMaxRate(void); + +/// Tag a display link so the swizzle bypasses it (for monitoring/boost links). +void RRBypassDisplayLink(CADisplayLink * _Nonnull link); + +/// Apply the current override to all tracked (non-bypassed) display links. +/// Call this after changing the override rate. +void RRApplyOverrideToTrackedLinks(void); diff --git a/third_party/refresh_rate/lib/refresh_rate.dart b/third_party/refresh_rate/lib/refresh_rate.dart new file mode 100644 index 00000000..c462fa98 --- /dev/null +++ b/third_party/refresh_rate/lib/refresh_rate.dart @@ -0,0 +1,5 @@ +export 'src/refresh_rate.dart'; +export 'src/models/display_info.dart'; +export 'src/models/enums.dart'; +export 'src/models/session_report.dart'; +export 'src/verification/refresh_rate_session.dart'; diff --git a/third_party/refresh_rate/lib/refresh_rate_web.dart b/third_party/refresh_rate/lib/refresh_rate_web.dart new file mode 100644 index 00000000..4c8e203c --- /dev/null +++ b/third_party/refresh_rate/lib/refresh_rate_web.dart @@ -0,0 +1,27 @@ +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; + +import 'src/refresh_rate.dart'; +import 'src/web/web_refresh_rate_adapter.dart'; + +/// Web platform implementation of the RefreshRate plugin. +/// +/// Uses `requestAnimationFrame` interval timing to detect the +/// display's current refresh rate (same technique as TestUFO). +/// Control methods are graceful no-ops because browsers own their +/// vsync scheduling and expose no API to change it. +/// +/// ### What works on web +/// +/// | Feature | Status | +/// |:--------|:------:| +/// | Query current Hz | ✓ (via rAF timing) | +/// | FPS overlay / benchmark | ✓ (pure Dart, unchanged) | +/// | Unlock / control rate | ✗ (browsers own vsync) | +/// | Supported rates / max rate | ✗ (not exposed) | +/// | Low Power Mode / thermal | ✗ (no web equivalent) | +class RefreshRateWeb { + /// Registers the web plugin by swapping in [WebRefreshRateApiAdapter]. + static void registerWith(Registrar registrar) { + RefreshRate.registerAdapter(WebRefreshRateApiAdapter()); + } +} diff --git a/third_party/refresh_rate/lib/src/generated/refresh_rate_api.g.dart b/third_party/refresh_rate/lib/src/generated/refresh_rate_api.g.dart new file mode 100644 index 00000000..10763859 --- /dev/null +++ b/third_party/refresh_rate/lib/src/generated/refresh_rate_api.g.dart @@ -0,0 +1,415 @@ +// Copyright 2026 Qoder (qoder.in). All rights reserved. +// Use of this source code is governed by a BSD-style license. +// Autogenerated from Pigeon (v22.7.4), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { + if (empty) { + return []; + } + if (error == null) { + return [result]; + } + return [error.code, error.message, error.details]; +} + +class DisplayInfoMessage { + DisplayInfoMessage({ + this.currentRate, + this.maxRate, + this.minRate, + this.supportedRates, + this.isVariableRefreshRate, + this.engineTargetRate, + this.iosProMotionEnabled, + this.androidApiLevel, + this.isLowPowerMode, + this.thermalStateIndex, + this.hasAdaptiveRefreshRate, + this.displayServer, + this.monitorCount, + }); + + double? currentRate; + + double? maxRate; + + double? minRate; + + List? supportedRates; + + bool? isVariableRefreshRate; + + double? engineTargetRate; + + bool? iosProMotionEnabled; + + int? androidApiLevel; + + bool? isLowPowerMode; + + int? thermalStateIndex; + + bool? hasAdaptiveRefreshRate; + + String? displayServer; + + int? monitorCount; + + Object encode() { + return [ + currentRate, + maxRate, + minRate, + supportedRates, + isVariableRefreshRate, + engineTargetRate, + iosProMotionEnabled, + androidApiLevel, + isLowPowerMode, + thermalStateIndex, + hasAdaptiveRefreshRate, + displayServer, + monitorCount, + ]; + } + + static DisplayInfoMessage decode(Object result) { + result as List; + return DisplayInfoMessage( + currentRate: result[0] as double?, + maxRate: result[1] as double?, + minRate: result[2] as double?, + supportedRates: (result[3] as List?)?.cast(), + isVariableRefreshRate: result[4] as bool?, + engineTargetRate: result[5] as double?, + iosProMotionEnabled: result[6] as bool?, + androidApiLevel: result[7] as int?, + isLowPowerMode: result[8] as bool?, + thermalStateIndex: result[9] as int?, + hasAdaptiveRefreshRate: result[10] as bool?, + displayServer: result[11] as String?, + monitorCount: result[12] as int?, + ); + } +} + + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is DisplayInfoMessage) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + return DisplayInfoMessage.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class RefreshRateHostApi { + /// Constructor for [RefreshRateHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + RefreshRateHostApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + Future getDisplayInfo() async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.getDisplayInfo$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send(null) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as DisplayInfoMessage?)!; + } + } + + Future enable() async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.enable$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send(null) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future disable() async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.disable$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send(null) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future preferMax() async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.preferMax$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send(null) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future preferDefault() async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.preferDefault$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send(null) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future matchContent(double fps) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.matchContent$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([fps]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future boost(int durationMs) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.boost$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([durationMs]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future setCategory(int categoryIndex) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.setCategory$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([categoryIndex]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future setTouchBoost(bool enabled) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.setTouchBoost$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([enabled]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future isSupported() async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.isSupported$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send(null) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as bool?)!; + } + } +} + +abstract class RefreshRateFlutterApi { + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + void onDisplayInfoChanged(DisplayInfoMessage info); + + static void setUp(RefreshRateFlutterApi? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + { + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.refresh_rate.RefreshRateFlutterApi.onDisplayInfoChanged$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.refresh_rate.RefreshRateFlutterApi.onDisplayInfoChanged was null.'); + final List args = (message as List?)!; + final DisplayInfoMessage? arg_info = (args[0] as DisplayInfoMessage?); + assert(arg_info != null, + 'Argument for dev.flutter.pigeon.refresh_rate.RefreshRateFlutterApi.onDisplayInfoChanged was null, expected non-null DisplayInfoMessage.'); + try { + api.onDisplayInfoChanged(arg_info!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } +} diff --git a/third_party/refresh_rate/lib/src/models/display_info.dart b/third_party/refresh_rate/lib/src/models/display_info.dart new file mode 100644 index 00000000..53b630bb --- /dev/null +++ b/third_party/refresh_rate/lib/src/models/display_info.dart @@ -0,0 +1,128 @@ +import '../generated/refresh_rate_api.g.dart'; +import 'enums.dart'; + +/// A snapshot of the current display configuration and device health. +/// +/// Retrieve a fresh snapshot via [RefreshRate.refresh] or listen to +/// [RefreshRate.onChanged] for real-time updates. +class DisplayInfo { + /// The refresh rate the display is currently running at, in Hz. + final double currentRate; + + /// The maximum refresh rate supported by this display, in Hz. + final double maxRate; + + /// The minimum refresh rate supported by this display, in Hz. + final double minRate; + + /// All refresh rates the display hardware can run at, in Hz. + final List supportedRates; + + /// Whether the display supports variable refresh rate (VRR / LTPO). + final bool isVariableRefreshRate; + + /// The frame rate the Flutter engine is currently targeting, in Hz. + final double engineTargetRate; + + /// Whether iOS ProMotion adaptive refresh is enabled for this app. + /// + /// `null` on non-iOS platforms. + final bool? iosProMotionEnabled; + + /// The Android API level of the device. + /// + /// `null` on non-Android platforms. + final int? androidApiLevel; + + /// Whether the device is in Low Power Mode. + /// + /// `null` when the platform does not expose this information. + final bool? isLowPowerMode; + + /// The current thermal state of the device. + final ThermalState thermalState; + + /// Whether the display supports an adaptive (variable) refresh rate. + /// + /// `null` when the platform does not expose this information. + final bool? hasAdaptiveRefreshRate; + + /// The name of the display server in use (Linux only, e.g. `"wayland"`). + /// + /// `null` on non-Linux platforms. + final String? displayServer; + + /// The number of monitors connected to the device (desktop platforms only). + /// + /// `null` on mobile platforms. + final int? monitorCount; + + /// Creates a new [DisplayInfo] snapshot. + const DisplayInfo({ + required this.currentRate, + required this.maxRate, + required this.minRate, + required this.supportedRates, + required this.isVariableRefreshRate, + required this.engineTargetRate, + this.iosProMotionEnabled, + this.androidApiLevel, + this.isLowPowerMode, + required this.thermalState, + this.hasAdaptiveRefreshRate, + this.displayServer, + this.monitorCount, + }); + + /// Creates a [DisplayInfo] from a platform [DisplayInfoMessage]. + factory DisplayInfo.fromMessage(DisplayInfoMessage msg) { + return DisplayInfo( + currentRate: msg.currentRate ?? 60.0, + maxRate: msg.maxRate ?? 60.0, + minRate: msg.minRate ?? 60.0, + supportedRates: msg.supportedRates?.whereType().toList() ?? const [60.0], + isVariableRefreshRate: msg.isVariableRefreshRate ?? false, + engineTargetRate: msg.engineTargetRate ?? 60.0, + iosProMotionEnabled: msg.iosProMotionEnabled, + androidApiLevel: msg.androidApiLevel, + isLowPowerMode: msg.isLowPowerMode, + thermalState: ThermalState.fromIndex(msg.thermalStateIndex), + hasAdaptiveRefreshRate: msg.hasAdaptiveRefreshRate, + displayServer: msg.displayServer, + monitorCount: msg.monitorCount, + ); + } + + /// A safe fallback [DisplayInfo] used before the first [RefreshRate.refresh] + /// call completes. Assumes a standard 60 Hz non-VRR display. + static const DisplayInfo fallback = DisplayInfo( + currentRate: 60.0, + maxRate: 60.0, + minRate: 60.0, + supportedRates: [60.0], + isVariableRefreshRate: false, + engineTargetRate: 60.0, + thermalState: ThermalState.unknown, + ); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is DisplayInfo && + currentRate == other.currentRate && + maxRate == other.maxRate && + minRate == other.minRate && + isVariableRefreshRate == other.isVariableRefreshRate && + engineTargetRate == other.engineTargetRate && + thermalState == other.thermalState && + isLowPowerMode == other.isLowPowerMode; + + @override + int get hashCode => Object.hash(currentRate, maxRate, minRate, + isVariableRefreshRate, engineTargetRate, thermalState, isLowPowerMode); + + @override + String toString() => + 'DisplayInfo(currentRate: ${currentRate}Hz, maxRate: ${maxRate}Hz, ' + 'thermalState: $thermalState, isLowPowerMode: $isLowPowerMode)'; +} diff --git a/third_party/refresh_rate/lib/src/models/enums.dart b/third_party/refresh_rate/lib/src/models/enums.dart new file mode 100644 index 00000000..57900f09 --- /dev/null +++ b/third_party/refresh_rate/lib/src/models/enums.dart @@ -0,0 +1,141 @@ +/// Indicates the desired performance category for the display scheduler. +/// +/// Use [RefreshRate.category] to apply one of these hints to the platform. +enum RateCategory { + /// No refresh-rate hint — the platform decides. + none, + + /// Prefer a low refresh rate (e.g. reading, static UIs). + low, + + /// Prefer the standard refresh rate (default). + normal, + + /// Prefer the highest supported refresh rate + /// (games, animations, scrolling at high speed). + high; + + /// Returns the [RateCategory] matching [index], or [none] if unknown. + static RateCategory fromIndex(int? index) { + switch (index) { + case 0: return none; + case 1: return low; + case 2: return normal; + case 3: return high; + default: return none; + } + } +} + +/// The thermal condition of the device, as reported by the OS. +/// +/// Higher-severity states may cause the OS to throttle the display +/// refresh rate regardless of what the app requests. +enum ThermalState { + /// The device is well within thermal limits — full performance available. + nominal, + + /// The device is slightly warm; minor throttling may apply. + fair, + + /// The device is hot; significant throttling is likely. + serious, + + /// The device is critically hot; the OS may force-reduce refresh rate. + critical, + + /// Thermal state could not be determined (unsupported platform). + unknown; + + /// Returns the [ThermalState] matching [index], or [unknown] if not found. + static ThermalState fromIndex(int? index) { + switch (index) { + case 0: return nominal; + case 1: return fair; + case 2: return serious; + case 3: return critical; + default: return unknown; + } + } +} + +/// Lifecycle state of a [RefreshRateSession] benchmark. +enum SessionState { + /// The session has not yet started collecting data. + idle, + + /// The session is actively collecting frame timings. + running, + + /// The session is in a brief warm-up period after the app resumed. + warmup, + + /// The session was paused due to the app going to the background. + interrupted, + + /// The session has finished and a [SessionReport] is available. + completed, +} + +/// Reason why a time window was excluded from a [SessionReport]. +enum ExclusionReason { + /// App moved to the background. + appBackgrounded, + + /// App became inactive (e.g. notification tray opened). + appInactive, + + /// A brief warm-up window after the app resumed from background. + resumeWarmup, + + /// Overlay opened or the window lost focus. + overlayOrFocusLoss, + + /// The display refresh rate changed mid-session. + displayRateChanged, + + /// Low Power Mode was toggled mid-session. + lowPowerModeChanged, + + /// The thermal state changed mid-session. + thermalStateChanged, +} + +/// Overall quality verdict produced by [SessionReport]. +enum Verdict { + /// Frame rate is consistently at or near the target Hz. + excellent, + + /// Frame rate is mostly stable with minor drops. + good, + + /// Frame rate is acceptable but below optimal. + fair, + + /// Frequent frame drops — the app misses its target regularly. + poor, + + /// Not enough valid data to make a determination. + inconclusive, +} + +/// The most likely rendering bottleneck identified by [SessionReport]. +enum Bottleneck { + /// The UI/build thread is the constraint (slow widget tree rebuilds). + buildBound, + + /// The raster thread is the constraint (complex paint operations). + rasterBound, + + /// The display hardware is capping the achievable frame rate. + displayCapped, + + /// Low Power Mode or battery saver is limiting performance. + powerLimited, + + /// Thermal throttling is reducing the achievable frame rate. + thermalLimited, + + /// No significant bottleneck detected. + none, +} diff --git a/third_party/refresh_rate/lib/src/models/session_report.dart b/third_party/refresh_rate/lib/src/models/session_report.dart new file mode 100644 index 00000000..378a6ca3 --- /dev/null +++ b/third_party/refresh_rate/lib/src/models/session_report.dart @@ -0,0 +1,186 @@ +import 'dart:convert'; +import 'enums.dart'; + +/// A point-in-time snapshot of device conditions recorded at session start. +/// +/// Captured when a [RefreshRateSession] is created and embedded in the +/// resulting [SessionReport] so analysis can account for the environment. +class DeviceStateSnapshot { + /// Whether the device was in Low Power Mode when the session started. + /// + /// `null` when the platform does not expose this information. + final bool? isLowPowerMode; + + /// The thermal state of the device when the session started. + final ThermalState thermalState; + + /// Whether the display supports an adaptive (variable) refresh rate. + /// + /// `null` when the platform does not expose this information. + final bool? hasAdaptiveRefreshRate; + + /// The display server in use (Linux only, e.g. `"wayland"`). + /// + /// `null` on non-Linux platforms. + final String? displayServer; + + /// Number of connected monitors (desktop platforms only). + /// + /// `null` on mobile platforms. + final int? monitorCount; + + /// Creates a new [DeviceStateSnapshot]. + const DeviceStateSnapshot({ + this.isLowPowerMode, + required this.thermalState, + this.hasAdaptiveRefreshRate, + this.displayServer, + this.monitorCount, + }); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is DeviceStateSnapshot && + isLowPowerMode == other.isLowPowerMode && + thermalState == other.thermalState; + + @override + int get hashCode => Object.hash(isLowPowerMode, thermalState); + + /// Serializes this snapshot to a JSON-compatible map. + Map toMap() => { + 'isLowPowerMode': isLowPowerMode, + 'thermalState': thermalState.name, + 'hasAdaptiveRefreshRate': hasAdaptiveRefreshRate, + 'displayServer': displayServer, + 'monitorCount': monitorCount, + }; +} + +/// The result produced when a [RefreshRateSession] ends. +/// +/// Contains FPS statistics, frame timing breakdowns, a performance [verdict], +/// a likely [likelyBottleneck] hint, and excluded-window details. +class SessionReport { + /// The name given to the session when it was started. + final String sessionName; + + /// Overall quality assessment for this session. + final Verdict verdict; + + /// The rendering stage most likely responsible for any frame drops. + final Bottleneck likelyBottleneck; + + /// The target refresh rate in Hz at the time the session began. + final double targetHz; + + /// The average display refresh rate observed during the session, in Hz. + final double observedAvgHz; + + /// The frame duration budget at [targetHz] (1000 / targetHz), in ms. + final double frameBudgetMs; + + /// Average frames per second across all valid frames. + final double avgFps; + + /// The 1st-percentile FPS (worst 1% of frames). + final double onePercentLowFps; + + /// The 5th-percentile FPS (worst 5% of frames). + final double fivePercentLowFps; + + /// Average build (UI thread) time per frame, in ms. + final double avgBuildMs; + + /// Average raster thread time per frame, in ms. + final double avgRasterMs; + + /// Average total frame time (build + raster), in ms. + final double avgTotalFrameMs; + + /// Number of frames that exceeded the frame budget. + final int jankyFrameCount; + + /// Number of frames that exceeded twice the frame budget. + final int severeJankCount; + + /// Percentage of frames that were missed (janky), from 0–100. + final double missedFramePercent; + + /// Total duration of valid (non-excluded) measurement time. + final Duration validDuration; + + /// Total duration excluded from measurement (background, warmup, etc.). + final Duration excludedDuration; + + /// A breakdown of how many times each [ExclusionReason] occurred. + final Map exclusionReasons; + + /// Device state recorded at the start of the session. + final DeviceStateSnapshot deviceState; + + /// Creates a new [SessionReport]. + const SessionReport({ + required this.sessionName, + required this.verdict, + required this.likelyBottleneck, + required this.targetHz, + required this.observedAvgHz, + required this.frameBudgetMs, + required this.avgFps, + required this.onePercentLowFps, + required this.fivePercentLowFps, + required this.avgBuildMs, + required this.avgRasterMs, + required this.avgTotalFrameMs, + required this.jankyFrameCount, + required this.severeJankCount, + required this.missedFramePercent, + required this.validDuration, + required this.excludedDuration, + required this.exclusionReasons, + required this.deviceState, + }); + + /// Serializes this report to a JSON-compatible map. + Map toMap() => { + 'sessionName': sessionName, + 'verdict': verdict.name, + 'likelyBottleneck': likelyBottleneck.name, + 'targetHz': targetHz, + 'observedAvgHz': observedAvgHz, + 'frameBudgetMs': frameBudgetMs, + 'avgFps': avgFps, + 'onePercentLowFps': onePercentLowFps, + 'fivePercentLowFps': fivePercentLowFps, + 'avgBuildMs': avgBuildMs, + 'avgRasterMs': avgRasterMs, + 'avgTotalFrameMs': avgTotalFrameMs, + 'jankyFrameCount': jankyFrameCount, + 'severeJankCount': severeJankCount, + 'missedFramePercent': missedFramePercent, + 'validDurationMs': validDuration.inMilliseconds, + 'excludedDurationMs': excludedDuration.inMilliseconds, + 'exclusionReasons': exclusionReasons.map((k, v) => MapEntry(k.name, v)), + 'deviceState': deviceState.toMap(), + }; + + /// Serializes this report to a JSON string. + String toJson() => jsonEncode(toMap()); + + /// Serializes this report to a CSV string. + String toCsv() { + final m = toMap(); + String esc(dynamic v) { + final s = v is Map ? jsonEncode(v) : '$v'; + if (s.contains(',') || s.contains('"') || s.contains('\n')) { + return '"${s.replaceAll('"', '""')}"'; + } + return s; + } + final headers = m.keys.join(','); + final values = m.values.map(esc).join(','); + return '$headers\n$values'; + } +} diff --git a/third_party/refresh_rate/lib/src/refresh_rate.dart b/third_party/refresh_rate/lib/src/refresh_rate.dart new file mode 100644 index 00000000..a0a2fe8d --- /dev/null +++ b/third_party/refresh_rate/lib/src/refresh_rate.dart @@ -0,0 +1,246 @@ +import 'dart:async'; +import 'package:flutter/widgets.dart'; + +import 'generated/refresh_rate_api.g.dart'; +import 'models/display_info.dart'; +import 'models/enums.dart'; +import 'refresh_rate_api_adapter.dart'; +import 'verification/overlay_controller.dart'; +import 'verification/refresh_rate_session.dart'; + +/// Primary entry-point for controlling and monitoring the display refresh rate. +/// +/// All members are static; this class cannot be instantiated. +/// +/// ### Quick-start +/// ```dart +/// // Unlock the highest supported refresh rate. +/// await RefreshRate.refresh(); +/// RefreshRate.preferMax(); +/// +/// // Show an FPS overlay for debugging. +/// RefreshRate.showFPS(); +/// ``` +class RefreshRate { + RefreshRate._(); + + static RefreshRateApiAdapter _api = PigeonRefreshRateApiAdapter(); + static DisplayInfo _cachedInfo = DisplayInfo.fallback; + static StreamController? _changedController; + static final _flutterApi = _RefreshRateFlutterApiImpl(); + + // ── Platform registration ────────────────────────────────────── + + /// Replaces the default platform API adapter. + /// + /// Called by platform-specific entrypoints (e.g. the web plugin) during + /// framework initialisation. Not intended for end-user consumption. + static void registerAdapter(RefreshRateApiAdapter adapter) { + _api = adapter; + } + + // ── Test seam ────────────────────────────────────────────────── + + /// Replaces the platform API implementation with a test fake. + /// + /// Call [clearApiForTesting] in `tearDown` to restore the real adapter. + @visibleForTesting + static void setApiForTesting(RefreshRateApiAdapter api) { + _api = api; + } + + /// Restores the real platform API and resets all internal state. + /// + /// Must be called in `tearDown` after [setApiForTesting]. + @visibleForTesting + static void clearApiForTesting() { + _api = PigeonRefreshRateApiAdapter(); + _flutterApi._onChanged = null; + RefreshRateFlutterApi.setUp(null); + _changedController?.close(); + _changedController = null; + } + + // ── Control ──────────────────────────────────────────────────── + + /// Opts the app into the platform's high-refresh-rate rendering pipeline. + /// + /// On Android this sets `preferredDisplayModeId` on the window surface. + /// On iOS/macOS this adjusts `CADisplayLink` preferred frame rate ranges. + /// Has no effect on platforms that do not support variable refresh rates. + static void enable() { + _api.enable(); + _refreshInfo(); + } + + /// Reverts the app to the platform default (typically 60 Hz). + /// + /// On iOS this allows the OS to manage the frame rate automatically. + static void disable() { + _api.disable(); + _refreshInfo(); + } + + /// Requests the maximum supported refresh rate for this display. + /// + /// Equivalent to calling [enable] then letting the platform pick the ceiling. + static void preferMax() => _api.preferMax(); + + /// Reverts to the display's default / preferred refresh rate. + static void preferDefault() => _api.preferDefault(); + + /// Requests a refresh rate that matches the given [fps] content rate. + /// + /// Useful when playing back video at a fixed frame rate (e.g. 24, 30, 60 fps) + /// so the display cadence aligns with the media cadence. + static void matchContent(double fps) => _api.matchContent(fps); + + /// Temporarily boosts the display to its maximum refresh rate for [duration]. + /// + /// Commonly used when a gesture or animation starts — the display snaps to + /// high-Hz immediately and returns to the preferred rate after the duration. + static void boost(Duration duration) => + _api.boost(duration.inMilliseconds); + + /// Boosts the refresh rate for the lifetime of an [AnimationController]. + /// + /// Registers a status listener that calls [boost] whenever the controller + /// starts animating, and removes itself once the animation is done. + static void boostDuring(AnimationController controller) { + late final void Function(AnimationStatus) statusListener; + statusListener = (status) { + if (status == AnimationStatus.forward || + status == AnimationStatus.reverse) { + _api.boost(controller.duration?.inMilliseconds ?? 500); + } else if (status == AnimationStatus.completed || + status == AnimationStatus.dismissed) { + controller.removeStatusListener(statusListener); + } + }; + controller.addStatusListener(statusListener); + } + + /// Sets a named [RateCategory] hint for the display scheduler. + /// + /// Categories let you declare the performance class of your app + /// (`low`, `normal`, `high`) rather than specifying raw Hz values. + static void category(RateCategory c) => _api.setCategory(c.index); + + /// Enables or disables an automatic boost whenever the user touches the screen. + /// + /// When [enabled] is `true` the platform raises the refresh rate on every + /// touch-down event and lowers it again after a short idle period. + static void setTouchBoost(bool enabled) => _api.setTouchBoost(enabled); + + // ── Verification overlays ────────────────────────────────────── + + /// Shows a minimal live FPS counter overlay in the top-right corner. + static void showFPS() => OverlayController.instance.showFPS(); + + /// Shows a minimal live Hz readout overlay in the top-right corner. + static void showHz() => OverlayController.instance.showHz(); + + /// Shows the full diagnostic overlay (FPS + Hz + thermal state). + static void showOverlay() => OverlayController.instance.showFull(); + + /// Hides whatever verification overlay is currently visible. + static void hideOverlay() => OverlayController.instance.hide(); + + /// Whether the debug overlay is currently shown. + static bool get isOverlayVisible => OverlayController.instance.isVisible; + + // ── Diagnostics ──────────────────────────────────────────────── + + /// The most recently fetched [DisplayInfo] snapshot. + /// + /// Initialised to [DisplayInfo.fallback] (60 Hz, all optional fields null) + /// until [refresh] is awaited at least once. + static DisplayInfo get info => _cachedInfo; + + /// Fetches fresh [DisplayInfo] from the platform and caches the result. + /// + /// Resolves with the updated [DisplayInfo] on success. + static Future refresh() async { + final msg = await _api.getDisplayInfo(); + _cachedInfo = DisplayInfo.fromMessage(msg); + return _cachedInfo; + } + + /// A broadcast stream that emits a new [DisplayInfo] whenever the display + /// configuration changes (e.g. the user enables Low Power Mode, or the + /// device throttles under thermal pressure). + static Stream get onChanged { + if (_changedController == null) { + _changedController = StreamController.broadcast(); + _flutterApi._onChanged = (info) { + _cachedInfo = info; + _changedController!.add(info); + }; + RefreshRateFlutterApi.setUp(_flutterApi); + } + return _changedController!.stream; + } + + /// Whether iOS ProMotion (adaptive 120 Hz) is enabled for this app. + /// + /// Returns `false` until a successful [refresh] is completed and the device + /// is an iPhone/iPad with a ProMotion display. + static bool get isProMotionReady => + _cachedInfo.iosProMotionEnabled == true; + + /// Whether the device is currently in Low Power Mode. + /// + /// Defaults to `false` when the value cannot be determined. + static bool get isLowPowerMode => + _cachedInfo.isLowPowerMode ?? false; + + /// The current thermal state of the device. + /// + /// A state of [ThermalState.serious] or [ThermalState.critical] may cause + /// the OS to clamp the refresh rate regardless of your requested value. + static ThermalState get thermalState => _cachedInfo.thermalState; + + // ── Benchmark sessions ───────────────────────────────────────── + + /// Creates and starts a new FPS benchmark session named [name]. + /// + /// Call [RefreshRateSession.end] when the scenario under test completes to + /// receive a [SessionReport] with verdict, FPS stats, and bottleneck hints. + static RefreshRateSession startSession(String name) { + return RefreshRateSession.create(name, _cachedInfo); + } + + // ── Internal ─────────────────────────────────────────────────── + + static void _refreshInfo() { + void handle(DisplayInfoMessage msg) { + _cachedInfo = DisplayInfo.fromMessage(msg); + } + void handleError(Object e) { + assert(() { + debugPrint('RefreshRate: _refreshInfo error: $e'); + return true; + }()); + } + + try { + final result = _api.getDisplayInfo(); + if (result is Future) { + result.then(handle).catchError(handleError); + } else { + handle(result); + } + } catch (e) { + handleError(e); + } + } +} + +class _RefreshRateFlutterApiImpl extends RefreshRateFlutterApi { + void Function(DisplayInfo)? _onChanged; + + @override + void onDisplayInfoChanged(DisplayInfoMessage info) { + _onChanged?.call(DisplayInfo.fromMessage(info)); + } +} diff --git a/third_party/refresh_rate/lib/src/refresh_rate_api_adapter.dart b/third_party/refresh_rate/lib/src/refresh_rate_api_adapter.dart new file mode 100644 index 00000000..3152293e --- /dev/null +++ b/third_party/refresh_rate/lib/src/refresh_rate_api_adapter.dart @@ -0,0 +1,69 @@ +import 'dart:async'; + +import 'generated/refresh_rate_api.g.dart'; + +/// Abstract interface for the host API, used as the test seam. +/// +/// The real implementation ([PigeonRefreshRateApiAdapter]) delegates to the +/// pigeon-generated [RefreshRateHostApi]. Tests implement this interface +/// directly with synchronous fakes — no await required. +abstract class RefreshRateApiAdapter { + /// Fetches the latest display configuration. + FutureOr getDisplayInfo(); + /// Enables High Refresh Rate overrides. + FutureOr enable(); + /// Disables High Refresh Rate overrides. + FutureOr disable(); + /// Requests the highest possible display refresh rate. + FutureOr preferMax(); + /// Resets to the system default refresh rate. + FutureOr preferDefault(); + /// Attempts to set the display refresh rate to match [fps]. + FutureOr matchContent(double fps); + /// Temporarily boosts the refresh rate for [durationMs]. + FutureOr boost(int durationMs); + /// Sets the refresh rate based on a given category. + FutureOr setCategory(int categoryIndex); + /// Enables or disables automatic refresh rate boost on touch interactions. + FutureOr setTouchBoost(bool enabled); + /// Checks whether the refresh rate overrides are supported by the platform. + FutureOr isSupported(); +} + +/// Production implementation that delegates to the pigeon-generated channel. +class PigeonRefreshRateApiAdapter implements RefreshRateApiAdapter { + final RefreshRateHostApi _pigeon; + + /// Creates a new [PigeonRefreshRateApiAdapter]. + PigeonRefreshRateApiAdapter() : _pigeon = RefreshRateHostApi(); + + @override + Future getDisplayInfo() => _pigeon.getDisplayInfo(); + + @override + Future enable() => _pigeon.enable(); + + @override + Future disable() => _pigeon.disable(); + + @override + Future preferMax() => _pigeon.preferMax(); + + @override + Future preferDefault() => _pigeon.preferDefault(); + + @override + Future matchContent(double fps) => _pigeon.matchContent(fps); + + @override + Future boost(int durationMs) => _pigeon.boost(durationMs); + + @override + Future setCategory(int categoryIndex) => _pigeon.setCategory(categoryIndex); + + @override + Future setTouchBoost(bool enabled) => _pigeon.setTouchBoost(enabled); + + @override + Future isSupported() => _pigeon.isSupported(); +} diff --git a/third_party/refresh_rate/lib/src/verification/fps_tracker.dart b/third_party/refresh_rate/lib/src/verification/fps_tracker.dart new file mode 100644 index 00000000..6bf58d03 --- /dev/null +++ b/third_party/refresh_rate/lib/src/verification/fps_tracker.dart @@ -0,0 +1,132 @@ +import 'dart:ui' show FramePhase; +import 'package:flutter/scheduler.dart'; + +/// Represents a single rendered frame's timing data. +class FrameSample { + /// Time spent in the UI build thread, in microseconds. + final int buildUs; + /// Time spent in the raster thread, in microseconds. + final int rasterUs; + /// Total time to render the frame, in microseconds. + final int totalUs; + /// Vsync timestamp, in microseconds. + final int vsyncUs; + /// System time when the sample was recorded. + final DateTime timestamp; + + /// Creates a new [FrameSample]. + FrameSample({ + required this.buildUs, + required this.rasterUs, + required this.totalUs, + required this.vsyncUs, + required this.timestamp, + }); +} + +/// Helper that records [FrameTiming] data and calculates FPS statistics. +class FpsTracker { + final List _samples = []; + + /// The number of recorded samples. + int get sampleCount => _samples.length; + + /// Adds raw frame timings from the Flutter engine. + void addTimings(List timings) { + final now = DateTime.now(); + for (final t in timings) { + _samples.add(FrameSample( + buildUs: t.buildDuration.inMicroseconds, + rasterUs: t.rasterDuration.inMicroseconds, + totalUs: t.totalSpan.inMicroseconds, + vsyncUs: t.timestampInMicroseconds(FramePhase.vsyncStart), + timestamp: now, + )); + } + // Keep a bounded window so memory doesn't grow unbounded during long sessions + if (_samples.length > 600) { + _samples.removeRange(0, _samples.length - 600); + } + } + + /// Clears all recorded samples. + void reset() => _samples.clear(); + + /// Returns an immutable list of currently recorded samples. + List get samples => List.unmodifiable(_samples); + + /// FPS over all samples — used by benchmark sessions. + double get avgFps => _fpsFromWindow(_samples); + + /// FPS over the last [n] frames — used by the live overlay. + double recentFps([int n = 60]) { + if (_samples.length < 2) return 0.0; + final window = _samples.length <= n ? _samples : _samples.sublist(_samples.length - n); + return _fpsFromWindow(window); + } + + static double _fpsFromWindow(List s) { + if (s.length < 2) return 0.0; + final elapsedUs = s.last.vsyncUs - s.first.vsyncUs; + if (elapsedUs <= 0) return 0.0; + return (s.length - 1) * 1000000.0 / elapsedUs; + } + + /// Average duration of the UI build phase across all samples, in ms. + double get avgBuildMs { + if (_samples.isEmpty) return 0.0; + return _samples.fold(0, (s, f) => s + f.buildUs) / _samples.length / 1000.0; + } + + /// Average duration of the raster phase across all samples, in ms. + double get avgRasterMs { + if (_samples.isEmpty) return 0.0; + return _samples.fold(0, (s, f) => s + f.rasterUs) / _samples.length / 1000.0; + } + + /// Average total duration (build + raster) across all samples, in ms. + double get avgTotalMs { + if (_samples.isEmpty) return 0.0; + return _samples.fold(0, (s, f) => s + f.totalUs) / _samples.length / 1000.0; + } + + /// Worst 1-percentile frame rate. + double get onePercentLowFps { + if (_samples.isEmpty) return 0.0; + final sorted = _samples.map((s) => s.totalUs).toList()..sort(); + final cutoff = (sorted.length * 0.99).floor(); + final slowSamples = sorted.skip(cutoff).toList(); + if (slowSamples.isEmpty) return avgFps; + final avgSlowUs = slowSamples.fold(0, (s, v) => s + v) / slowSamples.length; + return avgSlowUs > 0 ? 1000000 / avgSlowUs : 0.0; + } + + /// Worst 5-percentile frame rate. + double get fivePercentLowFps { + if (_samples.isEmpty) return 0.0; + final sorted = _samples.map((s) => s.totalUs).toList()..sort(); + final cutoff = (sorted.length * 0.95).floor(); + final slowSamples = sorted.skip(cutoff).toList(); + if (slowSamples.isEmpty) return avgFps; + final avgSlowUs = slowSamples.fold(0, (s, v) => s + v) / slowSamples.length; + return avgSlowUs > 0 ? 1000000 / avgSlowUs : 0.0; + } + + /// Counts how many frames exceeded the target budget. + int jankyFrameCount(double targetHz) { + final budgetUs = (1000000 / targetHz).round(); + return _samples.where((s) => s.totalUs > budgetUs).length; + } + + /// Counts how many frames took more than twice the target budget. + int severeJankCount(double targetHz) { + final budgetUs = (1000000 / targetHz).round(); + return _samples.where((s) => s.totalUs > budgetUs * 2).length; + } + + /// Percentage of frames that were missed. + double missedFramePercent(double targetHz) { + if (_samples.isEmpty) return 0.0; + return jankyFrameCount(targetHz) / _samples.length * 100.0; + } +} diff --git a/third_party/refresh_rate/lib/src/verification/overlay_controller.dart b/third_party/refresh_rate/lib/src/verification/overlay_controller.dart new file mode 100644 index 00000000..048da7e7 --- /dev/null +++ b/third_party/refresh_rate/lib/src/verification/overlay_controller.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'fps_tracker.dart'; +import 'overlay_widgets.dart'; + +enum _OverlayMode { none, fps, hz, full } + +/// Manages the visibility and rendering of debug overlays. +class OverlayController { + OverlayController._(); + /// The singleton instance of [OverlayController]. + static final instance = OverlayController._(); + + OverlayEntry? _entry; + _OverlayMode _mode = _OverlayMode.none; + final FpsTracker _tracker = FpsTracker(); + TimingsCallback? _timingsCallback; + + /// Whether an overlay is currently visible. + bool get isVisible => _mode != _OverlayMode.none; + + /// Shows a compact FPS overlay. + void showFPS() => _show(_OverlayMode.fps); + /// Shows a compact Hz (refresh rate) overlay. + void showHz() => _show(_OverlayMode.hz); + /// Shows the full debug overlay with detailed metrics. + void showFull() => _show(_OverlayMode.full); + + /// Hides any currently visible overlay. + void hide() { + _entry?.remove(); + _entry = null; + _mode = _OverlayMode.none; + _stopTracking(); + } + + void _show(_OverlayMode mode) { + hide(); + _mode = mode; + _startTracking(); + WidgetsBinding.instance.addPostFrameCallback((_) { + final overlay = _findOverlay(); + if (overlay == null) return; + _entry = OverlayEntry(builder: (_) => _buildWidget()); + overlay.insert(_entry!); + }); + } + + // Walks DOWN the element tree to find the first OverlayState. + // Overlay.maybeOf(rootElement) fails because the Overlay is a descendant, + // not an ancestor, of the root element. + static OverlayState? _findOverlay() { + final root = WidgetsBinding.instance.rootElement; + if (root == null) return null; + OverlayState? found; + void visit(Element element) { + if (found != null) return; + if (element is StatefulElement && element.state is OverlayState) { + found = element.state as OverlayState; + return; + } + element.visitChildren(visit); + } + root.visitChildren(visit); + return found; + } + + Widget _buildWidget() { + final inner = switch (_mode) { + _OverlayMode.fps => FpsOverlayWidget(tracker: _tracker), + _OverlayMode.hz => HzOverlayWidget(tracker: _tracker), + _OverlayMode.full => FullOverlayWidget(tracker: _tracker), + _OverlayMode.none => const SizedBox.shrink(), + }; + return inner; + } + + void _startTracking() { + if (_timingsCallback != null) return; + _timingsCallback = _tracker.addTimings; + SchedulerBinding.instance.addTimingsCallback(_timingsCallback!); + } + + void _stopTracking() { + if (_timingsCallback != null) { + SchedulerBinding.instance.removeTimingsCallback(_timingsCallback!); + _timingsCallback = null; + } + _tracker.reset(); + } +} diff --git a/third_party/refresh_rate/lib/src/verification/overlay_widgets.dart b/third_party/refresh_rate/lib/src/verification/overlay_widgets.dart new file mode 100644 index 00000000..823b772d --- /dev/null +++ b/third_party/refresh_rate/lib/src/verification/overlay_widgets.dart @@ -0,0 +1,172 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import '../models/enums.dart'; +import '../refresh_rate.dart'; +import 'fps_tracker.dart'; + +mixin _OverlayStateMixin on State { + late final Ticker _ticker; + + @override + void initState() { + super.initState(); + _ticker = Ticker((_) => setState(() {})); + _ticker.start(); + } + + @override + void dispose() { + _ticker.dispose(); + super.dispose(); + } + + Color fpsColor(double fps, double targetHz) { + if (fps >= targetHz * 0.95) return const Color(0xFF4CAF50); + if (fps >= targetHz * 0.75) return const Color(0xFFFFC107); + return const Color(0xFFF44336); + } + + Widget overlayContainer({required Widget child}) { + return Positioned( + top: MediaQueryData.fromView(View.of(context)).padding.top + 4, + right: 8, + child: IgnorePointer( + child: Material( + type: MaterialType.transparency, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: const Color(0xDD000000), + borderRadius: BorderRadius.circular(6), + ), + child: child, + ), + ), + ), + ); + } +} + +// ── FPS Badge ───────────────────────────────────────────────────── + +/// A small overlay widget that displays the current FPS. +class FpsOverlayWidget extends StatefulWidget { + /// The [FpsTracker] providing frame timings. + final FpsTracker tracker; + /// Creates a [FpsOverlayWidget]. + const FpsOverlayWidget({super.key, required this.tracker}); + @override + State createState() => _FpsOverlayWidgetState(); +} + +class _FpsOverlayWidgetState extends State + with _OverlayStateMixin { + @override + Widget build(BuildContext context) { + final fps = widget.tracker.recentFps(); + return overlayContainer( + child: Text( + '${fps.toStringAsFixed(0)} FPS', + style: TextStyle( + color: fpsColor(fps, RefreshRate.info.maxRate), + fontSize: 13, + fontWeight: FontWeight.bold, + fontFeatures: const [FontFeature.tabularFigures()], + ), + ), + ); + } +} + +// ── Hz Badge ────────────────────────────────────────────────────── + +/// A small overlay widget that displays the current display refresh rate. +class HzOverlayWidget extends StatefulWidget { + /// The [FpsTracker] (unused but required for generic widget creation). + final FpsTracker tracker; + /// Creates a [HzOverlayWidget]. + const HzOverlayWidget({super.key, required this.tracker}); + @override + State createState() => _HzOverlayWidgetState(); +} + +class _HzOverlayWidgetState extends State + with _OverlayStateMixin { + @override + Widget build(BuildContext context) { + final hz = RefreshRate.info.currentRate; + return overlayContainer( + child: Text( + '${hz.toStringAsFixed(0)}Hz', + style: const TextStyle( + color: Color(0xFF64B5F6), + fontSize: 13, + fontWeight: FontWeight.bold, + fontFeatures: [FontFeature.tabularFigures()], + ), + ), + ); + } +} + +// ── Full Diagnostic Overlay ──────────────────────────────────────── + +/// A larger overlay widget that displays FPS, build/raster times, and device health. +class FullOverlayWidget extends StatefulWidget { + /// The [FpsTracker] providing frame timings. + final FpsTracker tracker; + /// Creates a [FullOverlayWidget]. + const FullOverlayWidget({super.key, required this.tracker}); + @override + State createState() => _FullOverlayWidgetState(); +} + +class _FullOverlayWidgetState extends State + with _OverlayStateMixin { + @override + Widget build(BuildContext context) { + final fps = widget.tracker.recentFps(); + final buildMs = widget.tracker.avgBuildMs; + final rasterMs = widget.tracker.avgRasterMs; + final targetHz = RefreshRate.info.maxRate; + final budgetMs = 1000.0 / targetHz; + + return overlayContainer( + child: DefaultTextStyle( + style: const TextStyle( + color: Color(0xFFFFFFFF), + fontSize: 11, + fontFeatures: [FontFeature.tabularFigures()], + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + '${fps.toStringAsFixed(0)} FPS', + style: TextStyle( + color: fpsColor(fps, targetHz), + fontSize: 14, + fontWeight: FontWeight.bold, + fontFeatures: const [FontFeature.tabularFigures()], + ), + ), + Text('build ${buildMs.toStringAsFixed(1)}ms raster ${rasterMs.toStringAsFixed(1)}ms'), + Text('budget ${budgetMs.toStringAsFixed(1)}ms @ ${targetHz.toStringAsFixed(0)}Hz'), + if (RefreshRate.isLowPowerMode) + const Text( + '\u26a1 Low Power', + style: TextStyle(color: Color(0xFFFFCC02)), + ), + if (RefreshRate.thermalState != ThermalState.nominal && + RefreshRate.thermalState != ThermalState.unknown) + Text( + 'thermal: ${RefreshRate.thermalState.name}', + style: const TextStyle(color: Color(0xFFFF7043)), + ), + ], + ), + ), + ); + } +} diff --git a/third_party/refresh_rate/lib/src/verification/refresh_rate_session.dart b/third_party/refresh_rate/lib/src/verification/refresh_rate_session.dart new file mode 100644 index 00000000..139295c5 --- /dev/null +++ b/third_party/refresh_rate/lib/src/verification/refresh_rate_session.dart @@ -0,0 +1,135 @@ +import 'package:flutter/scheduler.dart'; +import 'package:flutter/widgets.dart'; +import '../models/display_info.dart'; +import '../models/enums.dart'; +import '../models/session_report.dart'; +import 'fps_tracker.dart'; +import 'session_scorer.dart'; + +/// An active FPS benchmark session that records frame timings. +/// +/// Create via [RefreshRate.startSession] and call [end] to receive a +/// [SessionReport] with verdict, FPS stats, and bottleneck analysis. +/// +/// The session automatically pauses when the app goes to the background and +/// resumes (with a warm-up exclusion window) when the app returns to the +/// foreground. +class RefreshRateSession { + /// The human-readable name given to this session. + final String name; + SessionState _state; + final DateTime _startedAt; + final double _targetHz; + final DisplayInfo _initialInfo; + final FpsTracker _tracker = FpsTracker(); + Duration _excludedDuration = Duration.zero; + final Map _exclusions = {}; + TimingsCallback? _timingsCallback; + AppLifecycleListener? _lifecycleListener; + DateTime? _inactiveAt; + static const _warmupDuration = Duration(milliseconds: 500); + + RefreshRateSession._({ + required this.name, + required SessionState state, + required DateTime startedAt, + required double targetHz, + required DisplayInfo initialInfo, + }) : _state = state, + _startedAt = startedAt, + _targetHz = targetHz, + _initialInfo = initialInfo; + + /// Creates and starts a new tracking session. + static RefreshRateSession create(String name, DisplayInfo info) { + final session = RefreshRateSession._( + name: name, + state: SessionState.running, + startedAt: DateTime.now(), + targetHz: info.maxRate, + initialInfo: info, + ); + session._timingsCallback = session._tracker.addTimings; + SchedulerBinding.instance.addTimingsCallback(session._timingsCallback!); + session._registerLifecycleObserver(); + return session; + } + + /// Current lifecycle state of the session. + SessionState get state => _state; + + /// The time at which this session was created. + DateTime get startedAt => _startedAt; + + /// The target refresh rate in Hz (taken from [DisplayInfo.maxRate] at creation time). + double get targetHz => _targetHz; + + /// Stops frame timing collection and computes a [SessionReport]. + /// + /// Must be called exactly once. After this call the session is in + /// [SessionState.completed] and further calls have undefined behaviour. + Future end() async { + _stopTracking(); + _state = SessionState.completed; + return SessionScorer.compute( + sessionName: name, + tracker: _tracker, + targetHz: _targetHz, + validDuration: DateTime.now().difference(_startedAt) - _excludedDuration, + excludedDuration: _excludedDuration, + exclusionReasons: Map.unmodifiable(_exclusions), + deviceState: DeviceStateSnapshot( + isLowPowerMode: _initialInfo.isLowPowerMode, + thermalState: _initialInfo.thermalState, + hasAdaptiveRefreshRate: _initialInfo.hasAdaptiveRefreshRate, + displayServer: _initialInfo.displayServer, + monitorCount: _initialInfo.monitorCount, + ), + ); + } + + void _registerLifecycleObserver() { + _lifecycleListener = AppLifecycleListener( + onHide: _onInactive, + onInactive: _onInactive, + onPause: _onInactive, + onResume: _onResume, + ); + } + + void _onInactive() { + if (_state != SessionState.running) return; + _state = SessionState.interrupted; + _inactiveAt = DateTime.now(); + _addExclusion(ExclusionReason.appBackgrounded); + } + + void _onResume() { + if (_state != SessionState.interrupted) return; + if (_inactiveAt != null) { + _excludedDuration += DateTime.now().difference(_inactiveAt!); + _inactiveAt = null; + } + _state = SessionState.warmup; + Future.delayed(_warmupDuration, () { + if (_state == SessionState.warmup) { + _excludedDuration += _warmupDuration; + _addExclusion(ExclusionReason.resumeWarmup); + _state = SessionState.running; + } + }); + } + + void _addExclusion(ExclusionReason reason) { + _exclusions[reason] = (_exclusions[reason] ?? 0) + 1; + } + + void _stopTracking() { + if (_timingsCallback != null) { + SchedulerBinding.instance.removeTimingsCallback(_timingsCallback!); + _timingsCallback = null; + } + _lifecycleListener?.dispose(); + _lifecycleListener = null; + } +} diff --git a/third_party/refresh_rate/lib/src/verification/session_scorer.dart b/third_party/refresh_rate/lib/src/verification/session_scorer.dart new file mode 100644 index 00000000..12494cd8 --- /dev/null +++ b/third_party/refresh_rate/lib/src/verification/session_scorer.dart @@ -0,0 +1,94 @@ +import '../models/enums.dart'; +import '../models/session_report.dart'; +import 'fps_tracker.dart'; + +/// Utility class that calculates the final [SessionReport] metrics. +abstract class SessionScorer { + /// Computes a [SessionReport] from the recorded tracking data. + static SessionReport compute({ + required String sessionName, + required FpsTracker tracker, + required double targetHz, + required Duration validDuration, + required Duration excludedDuration, + required Map exclusionReasons, + required DeviceStateSnapshot deviceState, + }) { + final frameBudgetMs = 1000.0 / targetHz; + final avgFps = tracker.avgFps; + final missed = tracker.missedFramePercent(targetHz); + final one = tracker.onePercentLowFps; + final five = tracker.fivePercentLowFps; + final janky = tracker.jankyFrameCount(targetHz); + final severe = tracker.severeJankCount(targetHz); + final avgBuild = tracker.avgBuildMs; + final avgRaster = tracker.avgRasterMs; + final avgTotal = tracker.avgTotalMs; + + final verdict = _computeVerdict(avgFps, targetHz, missed); + final bottleneck = _computeBottleneck( + avgBuild: avgBuild, + avgRaster: avgRaster, + avgFps: avgFps, + targetHz: targetHz, + isLowPowerMode: deviceState.isLowPowerMode, + thermalState: deviceState.thermalState, + ); + + return SessionReport( + sessionName: sessionName, + verdict: verdict, + likelyBottleneck: bottleneck, + targetHz: targetHz, + observedAvgHz: avgFps, + frameBudgetMs: frameBudgetMs, + avgFps: avgFps, + onePercentLowFps: one, + fivePercentLowFps: five, + avgBuildMs: avgBuild, + avgRasterMs: avgRaster, + avgTotalFrameMs: avgTotal, + jankyFrameCount: janky, + severeJankCount: severe, + missedFramePercent: missed, + validDuration: validDuration, + excludedDuration: excludedDuration, + exclusionReasons: exclusionReasons, + deviceState: deviceState, + ); + } + + static Verdict _computeVerdict(double avgFps, double targetHz, double missedPct) { + if (avgFps == 0) return Verdict.inconclusive; + final hitRate = avgFps / targetHz; + if (hitRate >= 0.95 && missedPct < 2) return Verdict.excellent; + if (hitRate >= 0.85 && missedPct < 5) return Verdict.good; + if (hitRate >= 0.70 && missedPct < 15) return Verdict.fair; + return Verdict.poor; + } + + static Bottleneck _computeBottleneck({ + required double avgBuild, + required double avgRaster, + required double avgFps, + required double targetHz, + required bool? isLowPowerMode, + required ThermalState thermalState, + }) { + if (isLowPowerMode == true) return Bottleneck.powerLimited; + if (thermalState == ThermalState.serious || thermalState == ThermalState.critical) { + return Bottleneck.thermalLimited; + } + final budgetMs = 1000.0 / targetHz; + // 85-90% of target: likely a display-side cap (LTPO floor, VSync rounding, etc.) + if (avgFps >= targetHz * 0.85 && avgFps < targetHz * 0.95) { + return Bottleneck.displayCapped; + } + // Below 85%: check for build vs raster bottleneck + if (avgFps < targetHz * 0.85) { + if (avgBuild > avgRaster && avgBuild > budgetMs * 0.5) return Bottleneck.buildBound; + if (avgRaster > budgetMs * 0.5) return Bottleneck.rasterBound; + } + return Bottleneck.none; + } +} diff --git a/third_party/refresh_rate/lib/src/web/raf_hz_detector.dart b/third_party/refresh_rate/lib/src/web/raf_hz_detector.dart new file mode 100644 index 00000000..70f2fec1 --- /dev/null +++ b/third_party/refresh_rate/lib/src/web/raf_hz_detector.dart @@ -0,0 +1,63 @@ +import 'dart:async'; +import 'dart:js_interop'; + +import 'package:web/web.dart' as web; + +/// Detects the display's refresh rate by measuring +/// `requestAnimationFrame` callback intervals. +/// +/// Uses the same technique as TestUFO: collect a window of rAF +/// timestamps, compute the median inter-frame interval, and derive Hz. +class RafHzDetector { + /// Number of frames to sample before reporting a result. + static const int _kSampleCount = 120; + + /// Measures the current display refresh rate via rAF timing. + /// + /// Collects [_kSampleCount] frames, computes the median interval, + /// and returns `1000 / medianMs`. Returns `null` if measurement fails. + static Future measure() { + final completer = Completer(); + final timestamps = []; + late final JSFunction callback; + + void onFrame(JSNumber ts) { + timestamps.add(ts.toDartDouble); + + if (timestamps.length >= _kSampleCount + 1) { + // Compute intervals + final intervals = []; + for (var i = 1; i < timestamps.length; i++) { + intervals.add(timestamps[i] - timestamps[i - 1]); + } + intervals.sort(); + final median = intervals[intervals.length ~/ 2]; + + if (median > 0) { + // Round to nearest common Hz value (e.g. 60, 90, 120, 144, 165, 240) + final rawHz = 1000.0 / median; + completer.complete(_snapToCommonHz(rawHz)); + } else { + completer.complete(null); + } + } else { + web.window.requestAnimationFrame(callback); + } + } + + callback = onFrame.toJS; + web.window.requestAnimationFrame(callback); + + return completer.future; + } + + /// Snaps a raw Hz measurement to the nearest common display refresh rate + /// when the raw value is within 3% tolerance. + static double _snapToCommonHz(double raw) { + const commonRates = [30.0, 48.0, 60.0, 72.0, 90.0, 120.0, 144.0, 165.0, 240.0, 360.0]; + for (final rate in commonRates) { + if ((raw - rate).abs() / rate < 0.03) return rate; + } + return double.parse(raw.toStringAsFixed(1)); + } +} diff --git a/third_party/refresh_rate/lib/src/web/web_refresh_rate_adapter.dart b/third_party/refresh_rate/lib/src/web/web_refresh_rate_adapter.dart new file mode 100644 index 00000000..96d0615b --- /dev/null +++ b/third_party/refresh_rate/lib/src/web/web_refresh_rate_adapter.dart @@ -0,0 +1,73 @@ +import 'dart:async'; + +import '../generated/refresh_rate_api.g.dart'; +import '../refresh_rate_api_adapter.dart'; +import 'raf_hz_detector.dart'; + +/// Web implementation of [RefreshRateApiAdapter]. +/// +/// Uses `requestAnimationFrame` interval timing to detect the display's +/// current refresh rate. Control methods are graceful no-ops because +/// browsers own their vsync scheduling and expose no API to change it. +class WebRefreshRateApiAdapter implements RefreshRateApiAdapter { + double? _lastMeasuredRate; + + @override + Future getDisplayInfo() async { + _lastMeasuredRate = await RafHzDetector.measure(); + return DisplayInfoMessage( + currentRate: _lastMeasuredRate, + // Browsers don't expose max/min/supported rates. + maxRate: _lastMeasuredRate, + minRate: null, + supportedRates: _lastMeasuredRate != null ? [_lastMeasuredRate!] : null, + isVariableRefreshRate: null, + engineTargetRate: _lastMeasuredRate, + // Apple / Android specific — not applicable on web. + iosProMotionEnabled: null, + androidApiLevel: null, + // No battery/thermal APIs on web. + isLowPowerMode: null, + thermalStateIndex: null, + hasAdaptiveRefreshRate: null, + displayServer: 'web', + monitorCount: 1, + ); + } + + /// No-op — browsers manage their own vsync scheduling. + @override + FutureOr enable() {} + + /// No-op — browsers manage their own vsync scheduling. + @override + FutureOr disable() {} + + /// No-op — cannot request max rate on web. + @override + FutureOr preferMax() {} + + /// No-op — cannot change rate preference on web. + @override + FutureOr preferDefault() {} + + /// No-op — cannot match content frame rate on web. + @override + FutureOr matchContent(double fps) {} + + /// No-op — cannot boost refresh rate on web. + @override + FutureOr boost(int durationMs) {} + + /// No-op — rate categories are Android-specific. + @override + FutureOr setCategory(int categoryIndex) {} + + /// No-op — touch boost is Android-specific. + @override + FutureOr setTouchBoost(bool enabled) {} + + /// Web supports querying the refresh rate but not controlling it. + @override + FutureOr isSupported() => false; +} diff --git a/third_party/refresh_rate/linux/CMakeLists.txt b/third_party/refresh_rate/linux/CMakeLists.txt new file mode 100644 index 00000000..3f93096e --- /dev/null +++ b/third_party/refresh_rate/linux/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.10) +set(PROJECT_NAME "refresh_rate") +project(${PROJECT_NAME} LANGUAGES CXX) + +set(PLUGIN_NAME "${PROJECT_NAME}_plugin") + +add_library(${PLUGIN_NAME} SHARED + "refresh_rate_plugin.cc" +) + +apply_standard_settings(${PLUGIN_NAME}) +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter) +target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK) diff --git a/third_party/refresh_rate/linux/include/refresh_rate/refresh_rate_plugin.h b/third_party/refresh_rate/linux/include/refresh_rate/refresh_rate_plugin.h new file mode 100644 index 00000000..7d23107b --- /dev/null +++ b/third_party/refresh_rate/linux/include/refresh_rate/refresh_rate_plugin.h @@ -0,0 +1,22 @@ +#ifndef FLUTTER_PLUGIN_REFRESH_RATE_PLUGIN_H_ +#define FLUTTER_PLUGIN_REFRESH_RATE_PLUGIN_H_ + +#include + +G_BEGIN_DECLS + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default"))) +#else +#define FLUTTER_PLUGIN_EXPORT +#endif + +G_DECLARE_FINAL_TYPE(RefreshRatePlugin, refresh_rate_plugin, REFRESH_RATE, + PLUGIN, GObject) + +FLUTTER_PLUGIN_EXPORT void refresh_rate_plugin_register_with_registrar( + FlPluginRegistrar* registrar); + +G_END_DECLS + +#endif // FLUTTER_PLUGIN_REFRESH_RATE_PLUGIN_H_ diff --git a/third_party/refresh_rate/linux/refresh_rate_api.g.cc b/third_party/refresh_rate/linux/refresh_rate_api.g.cc new file mode 100644 index 00000000..f4911e8a --- /dev/null +++ b/third_party/refresh_rate/linux/refresh_rate_api.g.cc @@ -0,0 +1,651 @@ +// Copyright 2026 Qoder (qoder.in). All rights reserved. +// Use of this source code is governed by a BSD-style license. +// Autogenerated from Pigeon (v22.7.4), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#undef _HAS_EXCEPTIONS + +#include "refresh_rate_api.g.h" + +#include +#include +#include +#include + +#include +#include +#include + +namespace refresh_rate { +using flutter::BasicMessageChannel; +using flutter::CustomEncodableValue; +using flutter::EncodableList; +using flutter::EncodableMap; +using flutter::EncodableValue; + +FlutterError CreateConnectionError(const std::string channel_name) { + return FlutterError( + "channel-error", + "Unable to establish connection on channel: '" + channel_name + "'.", + EncodableValue("")); +} + +// DisplayInfoMessage + +DisplayInfoMessage::DisplayInfoMessage() {} + +DisplayInfoMessage::DisplayInfoMessage( + const double* current_rate, + const double* max_rate, + const double* min_rate, + const EncodableList* supported_rates, + const bool* is_variable_refresh_rate, + const double* engine_target_rate, + const bool* ios_pro_motion_enabled, + const int64_t* android_api_level, + const bool* is_low_power_mode, + const int64_t* thermal_state_index, + const bool* has_adaptive_refresh_rate, + const std::string* display_server, + const int64_t* monitor_count) + : current_rate_(current_rate ? std::optional(*current_rate) : std::nullopt), + max_rate_(max_rate ? std::optional(*max_rate) : std::nullopt), + min_rate_(min_rate ? std::optional(*min_rate) : std::nullopt), + supported_rates_(supported_rates ? std::optional(*supported_rates) : std::nullopt), + is_variable_refresh_rate_(is_variable_refresh_rate ? std::optional(*is_variable_refresh_rate) : std::nullopt), + engine_target_rate_(engine_target_rate ? std::optional(*engine_target_rate) : std::nullopt), + ios_pro_motion_enabled_(ios_pro_motion_enabled ? std::optional(*ios_pro_motion_enabled) : std::nullopt), + android_api_level_(android_api_level ? std::optional(*android_api_level) : std::nullopt), + is_low_power_mode_(is_low_power_mode ? std::optional(*is_low_power_mode) : std::nullopt), + thermal_state_index_(thermal_state_index ? std::optional(*thermal_state_index) : std::nullopt), + has_adaptive_refresh_rate_(has_adaptive_refresh_rate ? std::optional(*has_adaptive_refresh_rate) : std::nullopt), + display_server_(display_server ? std::optional(*display_server) : std::nullopt), + monitor_count_(monitor_count ? std::optional(*monitor_count) : std::nullopt) {} + +const double* DisplayInfoMessage::current_rate() const { + return current_rate_ ? &(*current_rate_) : nullptr; +} + +void DisplayInfoMessage::set_current_rate(const double* value_arg) { + current_rate_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_current_rate(double value_arg) { + current_rate_ = value_arg; +} + + +const double* DisplayInfoMessage::max_rate() const { + return max_rate_ ? &(*max_rate_) : nullptr; +} + +void DisplayInfoMessage::set_max_rate(const double* value_arg) { + max_rate_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_max_rate(double value_arg) { + max_rate_ = value_arg; +} + + +const double* DisplayInfoMessage::min_rate() const { + return min_rate_ ? &(*min_rate_) : nullptr; +} + +void DisplayInfoMessage::set_min_rate(const double* value_arg) { + min_rate_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_min_rate(double value_arg) { + min_rate_ = value_arg; +} + + +const EncodableList* DisplayInfoMessage::supported_rates() const { + return supported_rates_ ? &(*supported_rates_) : nullptr; +} + +void DisplayInfoMessage::set_supported_rates(const EncodableList* value_arg) { + supported_rates_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_supported_rates(const EncodableList& value_arg) { + supported_rates_ = value_arg; +} + + +const bool* DisplayInfoMessage::is_variable_refresh_rate() const { + return is_variable_refresh_rate_ ? &(*is_variable_refresh_rate_) : nullptr; +} + +void DisplayInfoMessage::set_is_variable_refresh_rate(const bool* value_arg) { + is_variable_refresh_rate_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_is_variable_refresh_rate(bool value_arg) { + is_variable_refresh_rate_ = value_arg; +} + + +const double* DisplayInfoMessage::engine_target_rate() const { + return engine_target_rate_ ? &(*engine_target_rate_) : nullptr; +} + +void DisplayInfoMessage::set_engine_target_rate(const double* value_arg) { + engine_target_rate_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_engine_target_rate(double value_arg) { + engine_target_rate_ = value_arg; +} + + +const bool* DisplayInfoMessage::ios_pro_motion_enabled() const { + return ios_pro_motion_enabled_ ? &(*ios_pro_motion_enabled_) : nullptr; +} + +void DisplayInfoMessage::set_ios_pro_motion_enabled(const bool* value_arg) { + ios_pro_motion_enabled_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_ios_pro_motion_enabled(bool value_arg) { + ios_pro_motion_enabled_ = value_arg; +} + + +const int64_t* DisplayInfoMessage::android_api_level() const { + return android_api_level_ ? &(*android_api_level_) : nullptr; +} + +void DisplayInfoMessage::set_android_api_level(const int64_t* value_arg) { + android_api_level_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_android_api_level(int64_t value_arg) { + android_api_level_ = value_arg; +} + + +const bool* DisplayInfoMessage::is_low_power_mode() const { + return is_low_power_mode_ ? &(*is_low_power_mode_) : nullptr; +} + +void DisplayInfoMessage::set_is_low_power_mode(const bool* value_arg) { + is_low_power_mode_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_is_low_power_mode(bool value_arg) { + is_low_power_mode_ = value_arg; +} + + +const int64_t* DisplayInfoMessage::thermal_state_index() const { + return thermal_state_index_ ? &(*thermal_state_index_) : nullptr; +} + +void DisplayInfoMessage::set_thermal_state_index(const int64_t* value_arg) { + thermal_state_index_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_thermal_state_index(int64_t value_arg) { + thermal_state_index_ = value_arg; +} + + +const bool* DisplayInfoMessage::has_adaptive_refresh_rate() const { + return has_adaptive_refresh_rate_ ? &(*has_adaptive_refresh_rate_) : nullptr; +} + +void DisplayInfoMessage::set_has_adaptive_refresh_rate(const bool* value_arg) { + has_adaptive_refresh_rate_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_has_adaptive_refresh_rate(bool value_arg) { + has_adaptive_refresh_rate_ = value_arg; +} + + +const std::string* DisplayInfoMessage::display_server() const { + return display_server_ ? &(*display_server_) : nullptr; +} + +void DisplayInfoMessage::set_display_server(const std::string_view* value_arg) { + display_server_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_display_server(std::string_view value_arg) { + display_server_ = value_arg; +} + + +const int64_t* DisplayInfoMessage::monitor_count() const { + return monitor_count_ ? &(*monitor_count_) : nullptr; +} + +void DisplayInfoMessage::set_monitor_count(const int64_t* value_arg) { + monitor_count_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_monitor_count(int64_t value_arg) { + monitor_count_ = value_arg; +} + + +EncodableList DisplayInfoMessage::ToEncodableList() const { + EncodableList list; + list.reserve(13); + list.push_back(current_rate_ ? EncodableValue(*current_rate_) : EncodableValue()); + list.push_back(max_rate_ ? EncodableValue(*max_rate_) : EncodableValue()); + list.push_back(min_rate_ ? EncodableValue(*min_rate_) : EncodableValue()); + list.push_back(supported_rates_ ? EncodableValue(*supported_rates_) : EncodableValue()); + list.push_back(is_variable_refresh_rate_ ? EncodableValue(*is_variable_refresh_rate_) : EncodableValue()); + list.push_back(engine_target_rate_ ? EncodableValue(*engine_target_rate_) : EncodableValue()); + list.push_back(ios_pro_motion_enabled_ ? EncodableValue(*ios_pro_motion_enabled_) : EncodableValue()); + list.push_back(android_api_level_ ? EncodableValue(*android_api_level_) : EncodableValue()); + list.push_back(is_low_power_mode_ ? EncodableValue(*is_low_power_mode_) : EncodableValue()); + list.push_back(thermal_state_index_ ? EncodableValue(*thermal_state_index_) : EncodableValue()); + list.push_back(has_adaptive_refresh_rate_ ? EncodableValue(*has_adaptive_refresh_rate_) : EncodableValue()); + list.push_back(display_server_ ? EncodableValue(*display_server_) : EncodableValue()); + list.push_back(monitor_count_ ? EncodableValue(*monitor_count_) : EncodableValue()); + return list; +} + +DisplayInfoMessage DisplayInfoMessage::FromEncodableList(const EncodableList& list) { + DisplayInfoMessage decoded; + auto& encodable_current_rate = list[0]; + if (!encodable_current_rate.IsNull()) { + decoded.set_current_rate(std::get(encodable_current_rate)); + } + auto& encodable_max_rate = list[1]; + if (!encodable_max_rate.IsNull()) { + decoded.set_max_rate(std::get(encodable_max_rate)); + } + auto& encodable_min_rate = list[2]; + if (!encodable_min_rate.IsNull()) { + decoded.set_min_rate(std::get(encodable_min_rate)); + } + auto& encodable_supported_rates = list[3]; + if (!encodable_supported_rates.IsNull()) { + decoded.set_supported_rates(std::get(encodable_supported_rates)); + } + auto& encodable_is_variable_refresh_rate = list[4]; + if (!encodable_is_variable_refresh_rate.IsNull()) { + decoded.set_is_variable_refresh_rate(std::get(encodable_is_variable_refresh_rate)); + } + auto& encodable_engine_target_rate = list[5]; + if (!encodable_engine_target_rate.IsNull()) { + decoded.set_engine_target_rate(std::get(encodable_engine_target_rate)); + } + auto& encodable_ios_pro_motion_enabled = list[6]; + if (!encodable_ios_pro_motion_enabled.IsNull()) { + decoded.set_ios_pro_motion_enabled(std::get(encodable_ios_pro_motion_enabled)); + } + auto& encodable_android_api_level = list[7]; + if (!encodable_android_api_level.IsNull()) { + decoded.set_android_api_level(std::get(encodable_android_api_level)); + } + auto& encodable_is_low_power_mode = list[8]; + if (!encodable_is_low_power_mode.IsNull()) { + decoded.set_is_low_power_mode(std::get(encodable_is_low_power_mode)); + } + auto& encodable_thermal_state_index = list[9]; + if (!encodable_thermal_state_index.IsNull()) { + decoded.set_thermal_state_index(std::get(encodable_thermal_state_index)); + } + auto& encodable_has_adaptive_refresh_rate = list[10]; + if (!encodable_has_adaptive_refresh_rate.IsNull()) { + decoded.set_has_adaptive_refresh_rate(std::get(encodable_has_adaptive_refresh_rate)); + } + auto& encodable_display_server = list[11]; + if (!encodable_display_server.IsNull()) { + decoded.set_display_server(std::get(encodable_display_server)); + } + auto& encodable_monitor_count = list[12]; + if (!encodable_monitor_count.IsNull()) { + decoded.set_monitor_count(std::get(encodable_monitor_count)); + } + return decoded; +} + + +PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} + +EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( + uint8_t type, + flutter::ByteStreamReader* stream) const { + switch (type) { + case 129: { + return CustomEncodableValue(DisplayInfoMessage::FromEncodableList(std::get(ReadValue(stream)))); + } + default: + return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + } +} + +void PigeonInternalCodecSerializer::WriteValue( + const EncodableValue& value, + flutter::ByteStreamWriter* stream) const { + if (const CustomEncodableValue* custom_value = std::get_if(&value)) { + if (custom_value->type() == typeid(DisplayInfoMessage)) { + stream->WriteByte(129); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } + } + flutter::StandardCodecSerializer::WriteValue(value, stream); +} + +/// The codec used by RefreshRateHostApi. +const flutter::StandardMessageCodec& RefreshRateHostApi::GetCodec() { + return flutter::StandardMessageCodec::GetInstance(&PigeonInternalCodecSerializer::GetInstance()); +} + +// Sets up an instance of `RefreshRateHostApi` to handle messages through the `binary_messenger`. +void RefreshRateHostApi::SetUp( + flutter::BinaryMessenger* binary_messenger, + RefreshRateHostApi* api) { + RefreshRateHostApi::SetUp(binary_messenger, api, ""); +} + +void RefreshRateHostApi::SetUp( + flutter::BinaryMessenger* binary_messenger, + RefreshRateHostApi* api, + const std::string& message_channel_suffix) { + const std::string prepended_suffix = message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix : ""; + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.getDisplayInfo" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + ErrorOr output = api->GetDisplayInfo(); + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back(CustomEncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.enable" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + std::optional output = api->Enable(); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.disable" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + std::optional output = api->Disable(); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.preferMax" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + std::optional output = api->PreferMax(); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.preferDefault" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + std::optional output = api->PreferDefault(); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.matchContent" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_fps_arg = args.at(0); + if (encodable_fps_arg.IsNull()) { + reply(WrapError("fps_arg unexpectedly null.")); + return; + } + const auto& fps_arg = std::get(encodable_fps_arg); + std::optional output = api->MatchContent(fps_arg); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.boost" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_duration_ms_arg = args.at(0); + if (encodable_duration_ms_arg.IsNull()) { + reply(WrapError("duration_ms_arg unexpectedly null.")); + return; + } + const int64_t duration_ms_arg = encodable_duration_ms_arg.LongValue(); + std::optional output = api->Boost(duration_ms_arg); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.setCategory" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_category_index_arg = args.at(0); + if (encodable_category_index_arg.IsNull()) { + reply(WrapError("category_index_arg unexpectedly null.")); + return; + } + const int64_t category_index_arg = encodable_category_index_arg.LongValue(); + std::optional output = api->SetCategory(category_index_arg); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.setTouchBoost" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_enabled_arg = args.at(0); + if (encodable_enabled_arg.IsNull()) { + reply(WrapError("enabled_arg unexpectedly null.")); + return; + } + const auto& enabled_arg = std::get(encodable_enabled_arg); + std::optional output = api->SetTouchBoost(enabled_arg); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.isSupported" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + ErrorOr output = api->IsSupported(); + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } +} + +EncodableValue RefreshRateHostApi::WrapError(std::string_view error_message) { + return EncodableValue(EncodableList{ + EncodableValue(std::string(error_message)), + EncodableValue("Error"), + EncodableValue() + }); +} + +EncodableValue RefreshRateHostApi::WrapError(const FlutterError& error) { + return EncodableValue(EncodableList{ + EncodableValue(error.code()), + EncodableValue(error.message()), + error.details() + }); +} + +// Generated class from Pigeon that represents Flutter messages that can be called from C++. +RefreshRateFlutterApi::RefreshRateFlutterApi(flutter::BinaryMessenger* binary_messenger) + : binary_messenger_(binary_messenger), + message_channel_suffix_("") {} + +RefreshRateFlutterApi::RefreshRateFlutterApi( + flutter::BinaryMessenger* binary_messenger, + const std::string& message_channel_suffix) + : binary_messenger_(binary_messenger), + message_channel_suffix_(message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix : "") {} + +const flutter::StandardMessageCodec& RefreshRateFlutterApi::GetCodec() { + return flutter::StandardMessageCodec::GetInstance(&PigeonInternalCodecSerializer::GetInstance()); +} + +void RefreshRateFlutterApi::OnDisplayInfoChanged( + const DisplayInfoMessage& info_arg, + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = "dev.flutter.pigeon.refresh_rate.RefreshRateFlutterApi.onDisplayInfoChanged" + message_channel_suffix_; + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); + EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ + CustomEncodableValue(info_arg), + }); + channel.Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) { + std::unique_ptr response = GetCodec().DecodeMessage(reply, reply_size); + const auto& encodable_return_value = *response; + const auto* list_return_value = std::get_if(&encodable_return_value); + if (list_return_value) { + if (list_return_value->size() > 1) { + on_error(FlutterError(std::get(list_return_value->at(0)), std::get(list_return_value->at(1)), list_return_value->at(2))); + } else { + on_success(); + } + } else { + on_error(CreateConnectionError(channel_name)); + } + }); +} + +} // namespace refresh_rate diff --git a/third_party/refresh_rate/linux/refresh_rate_api.g.h b/third_party/refresh_rate/linux/refresh_rate_api.g.h new file mode 100644 index 00000000..70ef60a0 --- /dev/null +++ b/third_party/refresh_rate/linux/refresh_rate_api.g.h @@ -0,0 +1,233 @@ +// Copyright 2026 Qoder (qoder.in). All rights reserved. +// Use of this source code is governed by a BSD-style license. +// Autogenerated from Pigeon (v22.7.4), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#ifndef PIGEON_REFRESH_RATE_API_G_H_ +#define PIGEON_REFRESH_RATE_API_G_H_ +#include +#include +#include +#include + +#include +#include +#include + +namespace refresh_rate { + + +// Generated class from Pigeon. + +class FlutterError { + public: + explicit FlutterError(const std::string& code) + : code_(code) {} + explicit FlutterError(const std::string& code, const std::string& message) + : code_(code), message_(message) {} + explicit FlutterError(const std::string& code, const std::string& message, const flutter::EncodableValue& details) + : code_(code), message_(message), details_(details) {} + + const std::string& code() const { return code_; } + const std::string& message() const { return message_; } + const flutter::EncodableValue& details() const { return details_; } + + private: + std::string code_; + std::string message_; + flutter::EncodableValue details_; +}; + +template class ErrorOr { + public: + ErrorOr(const T& rhs) : v_(rhs) {} + ErrorOr(const T&& rhs) : v_(std::move(rhs)) {} + ErrorOr(const FlutterError& rhs) : v_(rhs) {} + ErrorOr(const FlutterError&& rhs) : v_(std::move(rhs)) {} + + bool has_error() const { return std::holds_alternative(v_); } + const T& value() const { return std::get(v_); }; + const FlutterError& error() const { return std::get(v_); }; + + private: + friend class RefreshRateHostApi; + friend class RefreshRateFlutterApi; + ErrorOr() = default; + T TakeValue() && { return std::get(std::move(v_)); } + + std::variant v_; +}; + + + +// Generated class from Pigeon that represents data sent in messages. +class DisplayInfoMessage { + public: + // Constructs an object setting all non-nullable fields. + DisplayInfoMessage(); + + // Constructs an object setting all fields. + explicit DisplayInfoMessage( + const double* current_rate, + const double* max_rate, + const double* min_rate, + const flutter::EncodableList* supported_rates, + const bool* is_variable_refresh_rate, + const double* engine_target_rate, + const bool* ios_pro_motion_enabled, + const int64_t* android_api_level, + const bool* is_low_power_mode, + const int64_t* thermal_state_index, + const bool* has_adaptive_refresh_rate, + const std::string* display_server, + const int64_t* monitor_count); + + const double* current_rate() const; + void set_current_rate(const double* value_arg); + void set_current_rate(double value_arg); + + const double* max_rate() const; + void set_max_rate(const double* value_arg); + void set_max_rate(double value_arg); + + const double* min_rate() const; + void set_min_rate(const double* value_arg); + void set_min_rate(double value_arg); + + const flutter::EncodableList* supported_rates() const; + void set_supported_rates(const flutter::EncodableList* value_arg); + void set_supported_rates(const flutter::EncodableList& value_arg); + + const bool* is_variable_refresh_rate() const; + void set_is_variable_refresh_rate(const bool* value_arg); + void set_is_variable_refresh_rate(bool value_arg); + + const double* engine_target_rate() const; + void set_engine_target_rate(const double* value_arg); + void set_engine_target_rate(double value_arg); + + const bool* ios_pro_motion_enabled() const; + void set_ios_pro_motion_enabled(const bool* value_arg); + void set_ios_pro_motion_enabled(bool value_arg); + + const int64_t* android_api_level() const; + void set_android_api_level(const int64_t* value_arg); + void set_android_api_level(int64_t value_arg); + + const bool* is_low_power_mode() const; + void set_is_low_power_mode(const bool* value_arg); + void set_is_low_power_mode(bool value_arg); + + const int64_t* thermal_state_index() const; + void set_thermal_state_index(const int64_t* value_arg); + void set_thermal_state_index(int64_t value_arg); + + const bool* has_adaptive_refresh_rate() const; + void set_has_adaptive_refresh_rate(const bool* value_arg); + void set_has_adaptive_refresh_rate(bool value_arg); + + const std::string* display_server() const; + void set_display_server(const std::string_view* value_arg); + void set_display_server(std::string_view value_arg); + + const int64_t* monitor_count() const; + void set_monitor_count(const int64_t* value_arg); + void set_monitor_count(int64_t value_arg); + + + private: + static DisplayInfoMessage FromEncodableList(const flutter::EncodableList& list); + flutter::EncodableList ToEncodableList() const; + friend class RefreshRateHostApi; + friend class RefreshRateFlutterApi; + friend class PigeonInternalCodecSerializer; + std::optional current_rate_; + std::optional max_rate_; + std::optional min_rate_; + std::optional supported_rates_; + std::optional is_variable_refresh_rate_; + std::optional engine_target_rate_; + std::optional ios_pro_motion_enabled_; + std::optional android_api_level_; + std::optional is_low_power_mode_; + std::optional thermal_state_index_; + std::optional has_adaptive_refresh_rate_; + std::optional display_server_; + std::optional monitor_count_; + +}; + + +class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { + public: + PigeonInternalCodecSerializer(); + inline static PigeonInternalCodecSerializer& GetInstance() { + static PigeonInternalCodecSerializer sInstance; + return sInstance; + } + + void WriteValue( + const flutter::EncodableValue& value, + flutter::ByteStreamWriter* stream) const override; + + protected: + flutter::EncodableValue ReadValueOfType( + uint8_t type, + flutter::ByteStreamReader* stream) const override; + +}; + +// Generated interface from Pigeon that represents a handler of messages from Flutter. +class RefreshRateHostApi { + public: + RefreshRateHostApi(const RefreshRateHostApi&) = delete; + RefreshRateHostApi& operator=(const RefreshRateHostApi&) = delete; + virtual ~RefreshRateHostApi() {} + virtual ErrorOr GetDisplayInfo() = 0; + virtual std::optional Enable() = 0; + virtual std::optional Disable() = 0; + virtual std::optional PreferMax() = 0; + virtual std::optional PreferDefault() = 0; + virtual std::optional MatchContent(double fps) = 0; + virtual std::optional Boost(int64_t duration_ms) = 0; + virtual std::optional SetCategory(int64_t category_index) = 0; + virtual std::optional SetTouchBoost(bool enabled) = 0; + virtual ErrorOr IsSupported() = 0; + + // The codec used by RefreshRateHostApi. + static const flutter::StandardMessageCodec& GetCodec(); + // Sets up an instance of `RefreshRateHostApi` to handle messages through the `binary_messenger`. + static void SetUp( + flutter::BinaryMessenger* binary_messenger, + RefreshRateHostApi* api); + static void SetUp( + flutter::BinaryMessenger* binary_messenger, + RefreshRateHostApi* api, + const std::string& message_channel_suffix); + static flutter::EncodableValue WrapError(std::string_view error_message); + static flutter::EncodableValue WrapError(const FlutterError& error); + + protected: + RefreshRateHostApi() = default; + +}; +// Generated class from Pigeon that represents Flutter messages that can be called from C++. +class RefreshRateFlutterApi { + public: + RefreshRateFlutterApi(flutter::BinaryMessenger* binary_messenger); + RefreshRateFlutterApi( + flutter::BinaryMessenger* binary_messenger, + const std::string& message_channel_suffix); + static const flutter::StandardMessageCodec& GetCodec(); + void OnDisplayInfoChanged( + const DisplayInfoMessage& info, + std::function&& on_success, + std::function&& on_error); + + private: + flutter::BinaryMessenger* binary_messenger_; + std::string message_channel_suffix_; +}; + +} // namespace refresh_rate +#endif // PIGEON_REFRESH_RATE_API_G_H_ diff --git a/third_party/refresh_rate/linux/refresh_rate_plugin.cc b/third_party/refresh_rate/linux/refresh_rate_plugin.cc new file mode 100644 index 00000000..ca63eb64 --- /dev/null +++ b/third_party/refresh_rate/linux/refresh_rate_plugin.cc @@ -0,0 +1,274 @@ +// Linux implementation of RefreshRatePlugin. +// +// Uses GDK for query support. Implements the pigeon protocol using raw binary +// channel handlers — avoids the GObject/C++ bridge problem with BinaryMessenger. +// Control is not supported on Linux; compositor owns refresh rates. + +#include "include/refresh_rate/refresh_rate_plugin.h" + +#include +#include +#include +#include +#include + +#define REFRESH_RATE_PLUGIN(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), refresh_rate_plugin_get_type(), RefreshRatePlugin)) + +#define PIGEON_CHANNEL_PREFIX "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi." + +struct _RefreshRatePlugin { + GObject parent_instance; + FlBinaryMessenger* messenger; +}; + +G_DEFINE_TYPE(RefreshRatePlugin, refresh_rate_plugin, g_object_get_type()) + +// ─── Pigeon binary encoding helpers ───────────────────────────────── + +static void pb_byte(GByteArray* b, guint8 v) { + g_byte_array_append(b, &v, 1); +} + +static void pb_varint(GByteArray* b, gsize v) { + if (v < 254) { + pb_byte(b, (guint8)v); + } else if (v < 65536) { + pb_byte(b, 254); + guint16 u = (guint16)v; + g_byte_array_append(b, (const guint8*)&u, 2); + } else { + pb_byte(b, 255); + guint32 u = (guint32)v; + g_byte_array_append(b, (const guint8*)&u, 4); + } +} + +static void pb_null(GByteArray* b) { pb_byte(b, 0); } +static void pb_bool(GByteArray* b, gboolean v) { pb_byte(b, v ? 1 : 2); } + +static void pb_double(GByteArray* b, double v) { + pb_byte(b, 6); + g_byte_array_append(b, (const guint8*)&v, 8); +} + +static void pb_int64(GByteArray* b, gint64 v) { + pb_byte(b, 4); + g_byte_array_append(b, (const guint8*)&v, 8); +} + +static void pb_string(GByteArray* b, const char* s) { + pb_byte(b, 7); + gsize len = strlen(s); + pb_varint(b, len); + g_byte_array_append(b, (const guint8*)s, len); +} + +// Build pigeon success response: outer list [result] +// For display info: result is custom type 129 (DisplayInfoMessage) +static GBytes* build_display_info_response( + double current, double max_rate, double min_rate, + double* rates, int n_rates, + gboolean is_vrr, + const char* display_server, + gint64 monitor_count) { + + GByteArray* b = g_byte_array_new(); + + // Outer success wrapper: list([result]) + pb_byte(b, 12); pb_varint(b, 1); + + // Custom type 129 (DisplayInfoMessage) + pb_byte(b, 129); + + // Inner list of 13 fields + pb_byte(b, 12); pb_varint(b, 13); + + pb_double(b, current); // 0: currentRate + pb_double(b, max_rate); // 1: maxRate + pb_double(b, min_rate); // 2: minRate + + // 3: supportedRates list + pb_byte(b, 12); pb_varint(b, n_rates); + for (int i = 0; i < n_rates; i++) pb_double(b, rates[i]); + + pb_bool(b, is_vrr); // 4: isVariableRefreshRate + pb_double(b, 60.0); // 5: engineTargetRate + pb_null(b); // 6: iosProMotionEnabled + pb_null(b); // 7: androidApiLevel + pb_null(b); // 8: isLowPowerMode + pb_null(b); // 9: thermalStateIndex + pb_bool(b, is_vrr); // 10: hasAdaptiveRefreshRate + + if (display_server) pb_string(b, display_server); + else pb_null(b); // 11: displayServer + + pb_int64(b, monitor_count); // 12: monitorCount + + return g_byte_array_free_to_bytes(b); +} + +// Pigeon empty success response: [] +static GBytes* build_empty_success() { + GByteArray* b = g_byte_array_new(); + pb_byte(b, 12); pb_varint(b, 0); + return g_byte_array_free_to_bytes(b); +} + +// Pigeon bool success response: [bool] +static GBytes* build_bool_success(gboolean v) { + GByteArray* b = g_byte_array_new(); + pb_byte(b, 12); pb_varint(b, 1); + pb_bool(b, v); + return g_byte_array_free_to_bytes(b); +} + +// ─── GDK display helpers ──────────────────────────────────────────── + +static GdkMonitor* get_primary_monitor() { + GdkDisplay* display = gdk_display_get_default(); + if (!display) return nullptr; + GdkMonitor* monitor = gdk_display_get_primary_monitor(display); + if (!monitor) monitor = gdk_display_get_monitor(display, 0); + return monitor; +} + +static double get_monitor_rate(GdkMonitor* monitor) { + if (!monitor) return 60.0; + int rate_mhz = gdk_monitor_get_refresh_rate(monitor); + return rate_mhz > 0 ? rate_mhz / 1000.0 : 60.0; +} + +static double get_primary_refresh_rate() { + return get_monitor_rate(get_primary_monitor()); +} + +static double get_max_refresh_rate() { + GdkDisplay* display = gdk_display_get_default(); + if (!display) return 60.0; + double maxRate = 0.0; + int n = gdk_display_get_n_monitors(display); + for (int i = 0; i < n; i++) { + double rate = get_monitor_rate(gdk_display_get_monitor(display, i)); + if (rate > maxRate) maxRate = rate; + } + return maxRate > 0 ? maxRate : 60.0; +} + +static const char* get_display_server_type() { + GdkDisplay* display = gdk_display_get_default(); + if (!display) return "unknown"; + const gchar* name = G_OBJECT_TYPE_NAME(display); + if (name) { + if (g_str_has_prefix(name, "GdkWayland")) return "wayland"; + if (g_str_has_prefix(name, "GdkX11")) return "x11"; + } + const char* wayland = g_getenv("WAYLAND_DISPLAY"); + if (wayland && wayland[0] != '\0') return "wayland"; + const char* x11 = g_getenv("DISPLAY"); + if (x11 && x11[0] != '\0') return "x11"; + return "unknown"; +} + +// ─── Pigeon channel handlers ──────────────────────────────────────── + +static void handle_get_display_info( + FlBinaryMessenger* messenger, + const gchar* channel, + GBytes* message, + FlBinaryMessengerResponseHandle* response_handle, + gpointer user_data) { + + double current = get_primary_refresh_rate(); + double max_r = get_max_refresh_rate(); + double min_r = current; // GDK doesn't expose min; use current + + // Collect unique rates + GdkDisplay* display = gdk_display_get_default(); + std::set seen; + if (display) { + int n = gdk_display_get_n_monitors(display); + for (int i = 0; i < n; i++) { + int rate_mhz = gdk_monitor_get_refresh_rate(gdk_display_get_monitor(display, i)); + if (rate_mhz > 0) seen.insert((rate_mhz + 500) / 1000); + } + } + if (seen.empty()) seen.insert((int)current); + + double rates[16]; + int n_rates = 0; + for (int r : seen) { + rates[n_rates++] = (double)r; + if (n_rates >= 16) break; + } + min_r = n_rates > 0 ? rates[0] : current; + + gboolean is_vrr = (max_r > current + 5.0); + const char* display_server = get_display_server_type(); + gint64 monitor_count = display ? gdk_display_get_n_monitors(display) : 1; + + g_autoptr(GBytes) response = build_display_info_response( + current, max_r, min_r, rates, n_rates, is_vrr, display_server, monitor_count); + fl_binary_messenger_send_response(messenger, response_handle, response, nullptr); +} + +static void handle_noop( + FlBinaryMessenger* messenger, + const gchar* channel, + GBytes* message, + FlBinaryMessengerResponseHandle* response_handle, + gpointer user_data) { + g_autoptr(GBytes) response = build_empty_success(); + fl_binary_messenger_send_response(messenger, response_handle, response, nullptr); +} + +static void handle_is_supported( + FlBinaryMessenger* messenger, + const gchar* channel, + GBytes* message, + FlBinaryMessengerResponseHandle* response_handle, + gpointer user_data) { + g_autoptr(GBytes) response = build_bool_success(FALSE); + fl_binary_messenger_send_response(messenger, response_handle, response, nullptr); +} + +// ─── Plugin lifecycle ──────────────────────────────────────────────── + +static void refresh_rate_plugin_dispose(GObject* object) { + G_OBJECT_CLASS(refresh_rate_plugin_parent_class)->dispose(object); +} + +static void refresh_rate_plugin_class_init(RefreshRatePluginClass* klass) { + G_OBJECT_CLASS(klass)->dispose = refresh_rate_plugin_dispose; +} + +static void refresh_rate_plugin_init(RefreshRatePlugin* self) {} + +void refresh_rate_plugin_register_with_registrar(FlPluginRegistrar* registrar) { + RefreshRatePlugin* plugin = REFRESH_RATE_PLUGIN( + g_object_new(refresh_rate_plugin_get_type(), nullptr)); + + FlBinaryMessenger* messenger = fl_plugin_registrar_get_messenger(registrar); + plugin->messenger = messenger; + + fl_binary_messenger_set_message_handler_on_channel( + messenger, PIGEON_CHANNEL_PREFIX "getDisplayInfo", + handle_get_display_info, g_object_ref(plugin), g_object_unref); + + const char* noop_channels[] = { + "enable", "disable", "preferMax", "preferDefault", + "matchContent", "boost", "setCategory", "setTouchBoost", nullptr + }; + for (int i = 0; noop_channels[i]; i++) { + gchar* name = g_strconcat(PIGEON_CHANNEL_PREFIX, noop_channels[i], nullptr); + fl_binary_messenger_set_message_handler_on_channel( + messenger, name, handle_noop, g_object_ref(plugin), g_object_unref); + g_free(name); + } + + fl_binary_messenger_set_message_handler_on_channel( + messenger, PIGEON_CHANNEL_PREFIX "isSupported", + handle_is_supported, g_object_ref(plugin), g_object_unref); + + g_object_unref(plugin); +} diff --git a/third_party/refresh_rate/macos/Classes/RefreshRatePlugin.swift b/third_party/refresh_rate/macos/Classes/RefreshRatePlugin.swift new file mode 100644 index 00000000..630b2c92 --- /dev/null +++ b/third_party/refresh_rate/macos/Classes/RefreshRatePlugin.swift @@ -0,0 +1,167 @@ +import Cocoa +import FlutterMacOS +import CoreVideo +import QuartzCore + +public class RefreshRatePlugin: NSObject, FlutterPlugin, RefreshRateHostApi { + + private var flutterApi: RefreshRateFlutterApi? + private var _displayLinkRef: AnyObject? // CADisplayLink on macOS 14+ + private var lastReportedRate: Double = 0 + private var displayReconfigRegistered = false + + @available(macOS 14.0, *) + private var displayLink: CADisplayLink? { + get { _displayLinkRef as? CADisplayLink } + set { _displayLinkRef = newValue } + } + + public static func register(with registrar: FlutterPluginRegistrar) { + let instance = RefreshRatePlugin() + RefreshRateHostApiSetup.setUp(binaryMessenger: registrar.messenger, api: instance) + instance.flutterApi = RefreshRateFlutterApi(binaryMessenger: registrar.messenger) + instance.startMonitoring() + } + + // MARK: - RefreshRateHostApi + + func getDisplayInfo() throws -> DisplayInfoMessage { + let current = getCurrentRate() + let max = getMaxRate() + let rates = getSupportedRates() + let min = rates.min() ?? 60.0 + return DisplayInfoMessage( + currentRate: current, maxRate: max, minRate: min, + supportedRates: rates, isVariableRefreshRate: detectVRR(), + engineTargetRate: 60.0, + iosProMotionEnabled: nil, androidApiLevel: nil, + isLowPowerMode: nil, thermalStateIndex: nil, + hasAdaptiveRefreshRate: detectVRR(), + displayServer: nil, monitorCount: Int64(NSScreen.screens.count)) + } + + func enable() throws { + guard #available(macOS 14.0, *), getMaxRate() > 60 else { return } + let max = getMaxRate() + setupDisplayLink(minimum: 60.0, maximum: max, preferred: max) + } + + func disable() throws { + if #available(macOS 14.0, *) { displayLink?.invalidate(); displayLink = nil } + } + + func preferMax() throws { try enable() } + func preferDefault() throws { try enable() } + + func matchContent(fps: Double) throws { + guard #available(macOS 14.0, *) else { return } + let max = getMaxRate() + let multiple = Swift.max(1.0, (max / fps).rounded(.down)) + setupDisplayLink(minimum: fps, maximum: fps * multiple, preferred: fps * multiple) + } + + func boost(durationMs: Int64) throws { + guard #available(macOS 14.0, *) else { return } + let max = getMaxRate() + setupDisplayLink(minimum: Swift.max(60.0, max * 0.66), maximum: max, preferred: max) + DispatchQueue.main.asyncAfter(deadline: .now() + Double(durationMs) / 1000.0) { + try? self.enable() + } + } + + func setCategory(categoryIndex: Int64) throws { + switch categoryIndex { + case 3: try? enable() + case 0, 1: try? disable() + default: break + } + } + + func setTouchBoost(enabled: Bool) throws {} + + func isSupported() throws -> Bool { + if #available(macOS 14.0, *) { return getMaxRate() > 60 } + return false + } + + // MARK: - Private helpers + + @available(macOS 14.0, *) + private func setupDisplayLink(minimum: Double, maximum: Double, preferred: Double) { + displayLink?.invalidate() + guard let screen = NSScreen.main else { return } + let link = screen.displayLink(target: self, selector: #selector(displayLinkFired)) + link.preferredFrameRateRange = CAFrameRateRange( + minimum: Float(minimum), maximum: Float(maximum), preferred: Float(preferred)) + link.add(to: .main, forMode: .common) + displayLink = link + } + + @available(macOS 14.0, *) + @objc private func displayLinkFired(_ link: CADisplayLink) { + let rate = link.duration > 0 ? round(1.0 / link.duration) : 60.0 + if abs(rate - lastReportedRate) > 5.0 { + lastReportedRate = rate + let info = (try? getDisplayInfo()) ?? DisplayInfoMessage( + currentRate: rate, maxRate: rate, minRate: 60.0, + supportedRates: [60.0, rate], isVariableRefreshRate: rate > 60, + engineTargetRate: rate, iosProMotionEnabled: nil, + androidApiLevel: nil, isLowPowerMode: nil, + thermalStateIndex: nil, hasAdaptiveRefreshRate: nil, + displayServer: nil, monitorCount: Int64(NSScreen.screens.count)) + flutterApi?.onDisplayInfoChanged(info: info) { _ in } + } + } + + private func startMonitoring() { + guard !displayReconfigRegistered else { return } + CGDisplayRegisterReconfigurationCallback({ _, flags, userInfo in + guard let plugin = userInfo.flatMap({ + Unmanaged.fromOpaque($0).takeUnretainedValue() as RefreshRatePlugin? + }) else { return } + if flags.contains(.setModeFlag) { + guard let info = try? plugin.getDisplayInfo() else { return } + plugin.flutterApi?.onDisplayInfoChanged(info: info) { _ in } + } + }, Unmanaged.passUnretained(self).toOpaque()) + displayReconfigRegistered = true + if #available(macOS 14.0, *), displayLink == nil { + guard let screen = NSScreen.main else { return } + let link = screen.displayLink(target: self, selector: #selector(displayLinkFired)) + link.add(to: .main, forMode: .common) + displayLink = link + } + } + + private func getCurrentRate() -> Double { + if #available(macOS 14.0, *), let link = displayLink, link.duration > 0 { return round(1.0 / link.duration) } + if #available(macOS 12.0, *) { return Double(NSScreen.main?.maximumFramesPerSecond ?? 60) } + return getRefreshRateFromCGDisplay() + } + + private func getMaxRate() -> Double { + if #available(macOS 12.0, *) { return Double(NSScreen.main?.maximumFramesPerSecond ?? 60) } + return getSupportedRates().max() ?? 60.0 + } + + private func getSupportedRates() -> [Double] { + let id = (NSScreen.main?.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? CGDirectDisplayID) ?? CGMainDisplayID() + guard let modes = CGDisplayCopyAllDisplayModes(id, nil) as? [CGDisplayMode] else { return [60.0] } + let rates = Set(modes.compactMap { $0.refreshRate > 0 ? round($0.refreshRate) : nil }) + return rates.isEmpty ? [60.0] : Array(rates).sorted() + } + + private func detectVRR() -> Bool { + if #available(macOS 12.0, *) { + guard let s = NSScreen.main else { return false } + return s.minimumRefreshInterval != s.maximumRefreshInterval + } + return getMaxRate() > 60 + } + + private func getRefreshRateFromCGDisplay() -> Double { + let id = CGMainDisplayID() + guard let mode = CGDisplayCopyDisplayMode(id) else { return 60.0 } + return mode.refreshRate > 0 ? mode.refreshRate : 60.0 + } +} diff --git a/third_party/refresh_rate/macos/Classes/generated/RefreshRateApi.swift b/third_party/refresh_rate/macos/Classes/generated/RefreshRateApi.swift new file mode 100644 index 00000000..70c230c8 --- /dev/null +++ b/third_party/refresh_rate/macos/Classes/generated/RefreshRateApi.swift @@ -0,0 +1,369 @@ +// Copyright 2026 Qoder (qoder.in). All rights reserved. +// Use of this source code is governed by a BSD-style license. +// Autogenerated from Pigeon (v22.7.4), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +/// Error class for passing custom error details to Dart side. +final class PigeonError: Error { + let code: String + let message: String? + let details: Any? + + init(code: String, message: String?, details: Any?) { + self.code = code + self.message = message + self.details = details + } + + var localizedDescription: String { + return + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + +private func wrapResult(_ result: Any?) -> [Any?] { + return [result] +} + +private func wrapError(_ error: Any) -> [Any?] { + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } + if let flutterError = error as? FlutterError { + return [ + flutterError.code, + flutterError.message, + flutterError.details, + ] + } + return [ + "\(error)", + "\(type(of: error))", + "Stacktrace: \(Thread.callStackSymbols)", + ] +} + +private func createConnectionError(withChannelName channelName: String) -> PigeonError { + return PigeonError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "") +} + +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + +/// Generated class from Pigeon that represents data sent in messages. +struct DisplayInfoMessage { + var currentRate: Double? = nil + var maxRate: Double? = nil + var minRate: Double? = nil + var supportedRates: [Double?]? = nil + var isVariableRefreshRate: Bool? = nil + var engineTargetRate: Double? = nil + var iosProMotionEnabled: Bool? = nil + var androidApiLevel: Int64? = nil + var isLowPowerMode: Bool? = nil + var thermalStateIndex: Int64? = nil + var hasAdaptiveRefreshRate: Bool? = nil + var displayServer: String? = nil + var monitorCount: Int64? = nil + + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> DisplayInfoMessage? { + let currentRate: Double? = nilOrValue(pigeonVar_list[0]) + let maxRate: Double? = nilOrValue(pigeonVar_list[1]) + let minRate: Double? = nilOrValue(pigeonVar_list[2]) + let supportedRates: [Double?]? = nilOrValue(pigeonVar_list[3]) + let isVariableRefreshRate: Bool? = nilOrValue(pigeonVar_list[4]) + let engineTargetRate: Double? = nilOrValue(pigeonVar_list[5]) + let iosProMotionEnabled: Bool? = nilOrValue(pigeonVar_list[6]) + let androidApiLevel: Int64? = nilOrValue(pigeonVar_list[7]) + let isLowPowerMode: Bool? = nilOrValue(pigeonVar_list[8]) + let thermalStateIndex: Int64? = nilOrValue(pigeonVar_list[9]) + let hasAdaptiveRefreshRate: Bool? = nilOrValue(pigeonVar_list[10]) + let displayServer: String? = nilOrValue(pigeonVar_list[11]) + let monitorCount: Int64? = nilOrValue(pigeonVar_list[12]) + + return DisplayInfoMessage( + currentRate: currentRate, + maxRate: maxRate, + minRate: minRate, + supportedRates: supportedRates, + isVariableRefreshRate: isVariableRefreshRate, + engineTargetRate: engineTargetRate, + iosProMotionEnabled: iosProMotionEnabled, + androidApiLevel: androidApiLevel, + isLowPowerMode: isLowPowerMode, + thermalStateIndex: thermalStateIndex, + hasAdaptiveRefreshRate: hasAdaptiveRefreshRate, + displayServer: displayServer, + monitorCount: monitorCount + ) + } + func toList() -> [Any?] { + return [ + currentRate, + maxRate, + minRate, + supportedRates, + isVariableRefreshRate, + engineTargetRate, + iosProMotionEnabled, + androidApiLevel, + isLowPowerMode, + thermalStateIndex, + hasAdaptiveRefreshRate, + displayServer, + monitorCount, + ] + } +} + +private class RefreshRateApiPigeonCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + return DisplayInfoMessage.fromList(self.readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class RefreshRateApiPigeonCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? DisplayInfoMessage { + super.writeByte(129) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class RefreshRateApiPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return RefreshRateApiPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return RefreshRateApiPigeonCodecWriter(data: data) + } +} + +class RefreshRateApiPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = RefreshRateApiPigeonCodec(readerWriter: RefreshRateApiPigeonCodecReaderWriter()) +} + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol RefreshRateHostApi { + func getDisplayInfo() throws -> DisplayInfoMessage + func enable() throws + func disable() throws + func preferMax() throws + func preferDefault() throws + func matchContent(fps: Double) throws + func boost(durationMs: Int64) throws + func setCategory(categoryIndex: Int64) throws + func setTouchBoost(enabled: Bool) throws + func isSupported() throws -> Bool +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class RefreshRateHostApiSetup { + static var codec: FlutterStandardMessageCodec { RefreshRateApiPigeonCodec.shared } + /// Sets up an instance of `RefreshRateHostApi` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: RefreshRateHostApi?, messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let getDisplayInfoChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.getDisplayInfo\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getDisplayInfoChannel.setMessageHandler { _, reply in + do { + let result = try api.getDisplayInfo() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getDisplayInfoChannel.setMessageHandler(nil) + } + let enableChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.enable\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + enableChannel.setMessageHandler { _, reply in + do { + try api.enable() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + enableChannel.setMessageHandler(nil) + } + let disableChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.disable\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + disableChannel.setMessageHandler { _, reply in + do { + try api.disable() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + disableChannel.setMessageHandler(nil) + } + let preferMaxChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.preferMax\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + preferMaxChannel.setMessageHandler { _, reply in + do { + try api.preferMax() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + preferMaxChannel.setMessageHandler(nil) + } + let preferDefaultChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.preferDefault\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + preferDefaultChannel.setMessageHandler { _, reply in + do { + try api.preferDefault() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + preferDefaultChannel.setMessageHandler(nil) + } + let matchContentChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.matchContent\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + matchContentChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let fpsArg = args[0] as! Double + do { + try api.matchContent(fps: fpsArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + matchContentChannel.setMessageHandler(nil) + } + let boostChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.boost\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + boostChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let durationMsArg = args[0] as! Int64 + do { + try api.boost(durationMs: durationMsArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + boostChannel.setMessageHandler(nil) + } + let setCategoryChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.setCategory\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setCategoryChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let categoryIndexArg = args[0] as! Int64 + do { + try api.setCategory(categoryIndex: categoryIndexArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setCategoryChannel.setMessageHandler(nil) + } + let setTouchBoostChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.setTouchBoost\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setTouchBoostChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let enabledArg = args[0] as! Bool + do { + try api.setTouchBoost(enabled: enabledArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setTouchBoostChannel.setMessageHandler(nil) + } + let isSupportedChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.isSupported\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + isSupportedChannel.setMessageHandler { _, reply in + do { + let result = try api.isSupported() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + isSupportedChannel.setMessageHandler(nil) + } + } +} +/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. +protocol RefreshRateFlutterApiProtocol { + func onDisplayInfoChanged(info infoArg: DisplayInfoMessage, completion: @escaping (Result) -> Void) +} +class RefreshRateFlutterApi: RefreshRateFlutterApiProtocol { + private let binaryMessenger: FlutterBinaryMessenger + private let messageChannelSuffix: String + init(binaryMessenger: FlutterBinaryMessenger, messageChannelSuffix: String = "") { + self.binaryMessenger = binaryMessenger + self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + } + var codec: RefreshRateApiPigeonCodec { + return RefreshRateApiPigeonCodec.shared + } + func onDisplayInfoChanged(info infoArg: DisplayInfoMessage, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.refresh_rate.RefreshRateFlutterApi.onDisplayInfoChanged\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([infoArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(Void())) + } + } + } +} diff --git a/third_party/refresh_rate/macos/refresh_rate.podspec b/third_party/refresh_rate/macos/refresh_rate.podspec new file mode 100644 index 00000000..0bc79d14 --- /dev/null +++ b/third_party/refresh_rate/macos/refresh_rate.podspec @@ -0,0 +1,16 @@ +Pod::Spec.new do |s| + s.name = 'refresh_rate' + s.version = '1.0.2' + s.summary = 'Control display refresh rates in Flutter.' + s.description = <<-DESC +Cross-platform Flutter plugin to query and control display refresh rates. + DESC + s.homepage = 'https://qoder.in' + s.license = { :file => '../LICENSE' } + s.author = { 'Qoder' => 'dev@qoder.in' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'FlutterMacOS' + s.platform = :osx, '10.14' + s.swift_version = '5.0' +end diff --git a/third_party/refresh_rate/macos/refresh_rate/Package.swift b/third_party/refresh_rate/macos/refresh_rate/Package.swift new file mode 100644 index 00000000..b4ad3b46 --- /dev/null +++ b/third_party/refresh_rate/macos/refresh_rate/Package.swift @@ -0,0 +1,35 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. +// +// This Package.swift file is consumed by Swift Package Manager when the plugin +// is used via the Flutter SPM integration (flutter build / flutter run with +// Swift Package Manager enabled). It mirrors the sources and settings declared +// in refresh_rate.podspec. + +import PackageDescription + +let package = Package( + name: "refresh_rate", + platforms: [ + .macOS(.v10_14), + ], + products: [ + .library(name: "refresh-rate", targets: ["refresh_rate"]), + ], + dependencies: [ + .package(name: "FlutterFramework", path: "../FlutterFramework"), + ], + targets: [ + .target( + name: "refresh_rate", + dependencies: [ + .product(name: "FlutterFramework", package: "FlutterFramework"), + ], + path: "Classes", + publicHeadersPath: ".", + cSettings: [ + .headerSearchPath("Classes"), + ] + ), + ] +) diff --git a/third_party/refresh_rate/pigeons/copyright.txt b/third_party/refresh_rate/pigeons/copyright.txt new file mode 100644 index 00000000..f131242e --- /dev/null +++ b/third_party/refresh_rate/pigeons/copyright.txt @@ -0,0 +1,2 @@ +Copyright 2026 Qoder (qoder.in). All rights reserved. +Use of this source code is governed by a BSD-style license. diff --git a/third_party/refresh_rate/pigeons/refresh_rate_api.dart b/third_party/refresh_rate/pigeons/refresh_rate_api.dart new file mode 100644 index 00000000..725ec7b3 --- /dev/null +++ b/third_party/refresh_rate/pigeons/refresh_rate_api.dart @@ -0,0 +1,63 @@ +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon(PigeonOptions( + dartOut: 'lib/src/generated/refresh_rate_api.g.dart', + dartOptions: DartOptions(), + kotlinOut: + 'android/src/main/kotlin/in/qoder/refresh_rate/generated/RefreshRateApi.kt', + kotlinOptions: KotlinOptions(package: 'in.qoder.refresh_rate.generated'), + swiftOut: 'ios/refresh_rate/Sources/refresh_rate/generated/RefreshRateApi.swift', + swiftOptions: SwiftOptions(), + cppHeaderOut: 'windows/refresh_rate_api.g.h', + cppSourceOut: 'windows/refresh_rate_api.g.cpp', + cppOptions: CppOptions(namespace: 'refresh_rate'), + copyrightHeader: 'pigeons/copyright.txt', +)) + +// ─── Data Classes ───────────────────────────────────────────────── + +class DisplayInfoMessage { + double? currentRate; + double? maxRate; + double? minRate; + List? supportedRates; + bool? isVariableRefreshRate; + double? engineTargetRate; + bool? iosProMotionEnabled; + int? androidApiLevel; + bool? isLowPowerMode; + // 0=nominal, 1=fair, 2=serious, 3=critical, null=unknown + int? thermalStateIndex; + bool? hasAdaptiveRefreshRate; + String? displayServer; + int? monitorCount; +} + +// ─── Host API (platform → Dart calls these) ────────────────────── +// These are what the Dart side calls INTO the platform + +@HostApi() +abstract class RefreshRateHostApi { + DisplayInfoMessage getDisplayInfo(); + + // Control + void enable(); + void disable(); + void preferMax(); + void preferDefault(); + void matchContent(double fps); + void boost(int durationMs); + // 0=none, 1=low, 2=normal, 3=high + void setCategory(int categoryIndex); + void setTouchBoost(bool enabled); + + bool isSupported(); +} + +// ─── Flutter API (Dart → platform listens to these) ────────────── +// These are what the platform calls UP to Dart + +@FlutterApi() +abstract class RefreshRateFlutterApi { + void onDisplayInfoChanged(DisplayInfoMessage info); +} diff --git a/third_party/refresh_rate/pigeons/refresh_rate_api_linux.dart b/third_party/refresh_rate/pigeons/refresh_rate_api_linux.dart new file mode 100644 index 00000000..e6f0cda9 --- /dev/null +++ b/third_party/refresh_rate/pigeons/refresh_rate_api_linux.dart @@ -0,0 +1,63 @@ +// Linux-specific pigeon schema. Regenerate with: +// dart run pigeon --input pigeons/refresh_rate_api_linux.dart \ +// --one_language \ +// --cpp_header_out linux/refresh_rate_api.g.h \ +// --cpp_source_out linux/refresh_rate_api.g.cc \ +// --cpp_namespace refresh_rate \ +// --copyright_header pigeons/copyright.txt +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon(PigeonOptions( + cppHeaderOut: 'linux/refresh_rate_api.g.h', + cppSourceOut: 'linux/refresh_rate_api.g.cc', + cppOptions: CppOptions(namespace: 'refresh_rate'), + copyrightHeader: 'pigeons/copyright.txt', +)) + +// ─── Data Classes ───────────────────────────────────────────────── + +class DisplayInfoMessage { + double? currentRate; + double? maxRate; + double? minRate; + List? supportedRates; + bool? isVariableRefreshRate; + double? engineTargetRate; + bool? iosProMotionEnabled; + int? androidApiLevel; + bool? isLowPowerMode; + // 0=nominal, 1=fair, 2=serious, 3=critical, null=unknown + int? thermalStateIndex; + bool? hasAdaptiveRefreshRate; + String? displayServer; + int? monitorCount; +} + +// ─── Host API (platform → Dart calls these) ────────────────────── +// These are what the Dart side calls INTO the platform + +@HostApi() +abstract class RefreshRateHostApi { + DisplayInfoMessage getDisplayInfo(); + + // Control + void enable(); + void disable(); + void preferMax(); + void preferDefault(); + void matchContent(double fps); + void boost(int durationMs); + // 0=none, 1=low, 2=normal, 3=high + void setCategory(int categoryIndex); + void setTouchBoost(bool enabled); + + bool isSupported(); +} + +// ─── Flutter API (Dart → platform listens to these) ────────────── +// These are what the platform calls UP to Dart + +@FlutterApi() +abstract class RefreshRateFlutterApi { + void onDisplayInfoChanged(DisplayInfoMessage info); +} diff --git a/third_party/refresh_rate/pubspec.yaml b/third_party/refresh_rate/pubspec.yaml new file mode 100644 index 00000000..95860623 --- /dev/null +++ b/third_party/refresh_rate/pubspec.yaml @@ -0,0 +1,61 @@ +name: refresh_rate +description: >- + Unlock 90/120/144Hz on Android, iOS & desktop. Query display rates, control + refresh, benchmark FPS with live debug overlays. Uses modern platform APIs. +version: 1.0.2 +homepage: https://qoder.in/resources/refresh_rate +repository: https://github.com/qoder-official/refresh_rate +issue_tracker: https://github.com/qoder-official/refresh_rate/issues +documentation: https://pub.dev/documentation/refresh_rate/latest/ + +topics: + - display + - performance + - animation + - debug + - ui + +screenshots: + - description: "Live FPS overlay showing 120 FPS with frame budget diagnostics" + path: screenshots/fps.png + - description: "Hz badge confirming 120Hz refresh rate on a real device" + path: screenshots/hz.png + +funding: + - https://github.com/sponsors/qoder-official + +environment: + sdk: ^3.5.0 + flutter: ">=3.24.0" + +dependencies: + flutter: + sdk: flutter + flutter_web_plugins: + sdk: flutter + plugin_platform_interface: ^2.1.7 + web: ^1.1.0 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + pigeon: ^22.0.0 + +flutter: + plugin: + platforms: + android: + package: in.qoder.refresh_rate + pluginClass: RefreshRatePlugin + ios: + pluginClass: RefreshRatePlugin + linux: + pluginClass: RefreshRatePlugin + macos: + pluginClass: RefreshRatePlugin + windows: + pluginClass: RefreshRatePluginCApi + web: + pluginClass: RefreshRateWeb + fileName: refresh_rate_web.dart diff --git a/third_party/refresh_rate/screenshots/fps.png b/third_party/refresh_rate/screenshots/fps.png new file mode 100644 index 0000000000000000000000000000000000000000..1658d85f632f5b3f3068536d5a3009f585965c4f GIT binary patch literal 300828 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 z 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

@-Em5x5RrC4CjiXGg?*HBqm^O|pLt1W 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 z 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 + }); + }); +} + +/// Builds a list of fake timings with proper sequential vsync timestamps. +/// Each frame's vsync = sum of all preceding frame durations. +List _fakeTimingSequence(List durationsUs) { + int vsync = 0; + return durationsUs.map((dur) { + final t = _MockFrameTiming(dur, vsync); + vsync += dur; + return t as FrameTiming; + }).toList(); +} + +List _fakeTimings(int count, int intervalUs) => + _fakeTimingSequence(List.filled(count, intervalUs)); + + + +class _MockFrameTiming implements FrameTiming { + final int _totalUs; + final int _vsyncUs; + _MockFrameTiming(this._totalUs, this._vsyncUs); + + @override + Duration get buildDuration => Duration(microseconds: (_totalUs * 0.6).toInt()); + @override + Duration get rasterDuration => Duration(microseconds: (_totalUs * 0.4).toInt()); + @override + Duration get totalSpan => Duration(microseconds: _totalUs); + @override + int get frameNumber => 0; + @override + int get layerCacheCount => 0; + @override + int get layerCacheBytes => 0; + @override + int get pictureCacheCount => 0; + @override + int get pictureCacheBytes => 0; + @override + int timestampInMicroseconds(FramePhase phase) { + if (phase == FramePhase.vsyncStart) return _vsyncUs; + return 0; + } + + // Forward any additional SDK-version-specific members gracefully. + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} diff --git a/third_party/refresh_rate/test/models_test.dart b/third_party/refresh_rate/test/models_test.dart new file mode 100644 index 00000000..3901a575 --- /dev/null +++ b/third_party/refresh_rate/test/models_test.dart @@ -0,0 +1,83 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:refresh_rate/src/models/enums.dart'; +import 'package:refresh_rate/src/models/display_info.dart'; +import 'package:refresh_rate/src/models/session_report.dart'; +import 'package:refresh_rate/src/generated/refresh_rate_api.g.dart'; + +void main() { + group('DisplayInfo.fromMessage', () { + test('maps all fields correctly', () { + final msg = DisplayInfoMessage( + currentRate: 120.0, + maxRate: 120.0, + minRate: 60.0, + supportedRates: [60.0, 120.0], + isVariableRefreshRate: true, + engineTargetRate: 120.0, + iosProMotionEnabled: true, + androidApiLevel: null, + isLowPowerMode: false, + thermalStateIndex: 0, + hasAdaptiveRefreshRate: true, + displayServer: null, + monitorCount: null, + ); + final info = DisplayInfo.fromMessage(msg); + expect(info.currentRate, 120.0); + expect(info.thermalState, ThermalState.nominal); + expect(info.isVariableRefreshRate, true); + }); + + test('handles null thermalStateIndex as unknown', () { + final msg = DisplayInfoMessage( + currentRate: 60.0, maxRate: 60.0, minRate: 60.0, + supportedRates: [60.0], isVariableRefreshRate: false, + engineTargetRate: 60.0, + thermalStateIndex: null, + ); + final info = DisplayInfo.fromMessage(msg); + expect(info.thermalState, ThermalState.unknown); + }); + }); + + group('ThermalState', () { + test('fromIndex maps correctly', () { + expect(ThermalState.fromIndex(0), ThermalState.nominal); + expect(ThermalState.fromIndex(1), ThermalState.fair); + expect(ThermalState.fromIndex(2), ThermalState.serious); + expect(ThermalState.fromIndex(3), ThermalState.critical); + expect(ThermalState.fromIndex(null), ThermalState.unknown); + }); + }); + + group('SessionReport.toCsv', () { + test('escapes commas in sessionName', () { + final report = SessionReport( + sessionName: 'feed, scroll', + verdict: Verdict.good, + likelyBottleneck: Bottleneck.none, + targetHz: 120.0, + observedAvgHz: 118.0, + frameBudgetMs: 8.33, + avgFps: 118.0, + onePercentLowFps: 90.0, + fivePercentLowFps: 100.0, + avgBuildMs: 3.0, + avgRasterMs: 4.0, + avgTotalFrameMs: 7.0, + jankyFrameCount: 2, + severeJankCount: 0, + missedFramePercent: 1.5, + validDuration: Duration(seconds: 5), + excludedDuration: Duration.zero, + exclusionReasons: {}, + deviceState: DeviceStateSnapshot(thermalState: ThermalState.nominal), + ); + final csv = report.toCsv(); + final lines = csv.split('\n'); + expect(lines.length, 2); + // The escaped session name should appear in the values row + expect(lines[1], contains('"feed, scroll"')); + }); + }); +} diff --git a/third_party/refresh_rate/test/refresh_rate_api_test.dart b/third_party/refresh_rate/test/refresh_rate_api_test.dart new file mode 100644 index 00000000..a6155aa0 --- /dev/null +++ b/third_party/refresh_rate/test/refresh_rate_api_test.dart @@ -0,0 +1,61 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:refresh_rate/refresh_rate.dart'; +import 'package:refresh_rate/src/generated/refresh_rate_api.g.dart'; +import 'package:refresh_rate/src/refresh_rate_api_adapter.dart'; + +class _FakeHostApi implements RefreshRateApiAdapter { + final calls = []; + @override + DisplayInfoMessage getDisplayInfo() => DisplayInfoMessage( + currentRate: 120.0, maxRate: 120.0, minRate: 60.0, + supportedRates: [60.0, 120.0], isVariableRefreshRate: true, + engineTargetRate: 120.0, thermalStateIndex: 0, + ); + + @override void enable() { calls.add('enable'); } + @override void disable() { calls.add('disable'); } + @override void preferMax() { calls.add('preferMax'); } + @override void preferDefault() { calls.add('preferDefault'); } + @override void matchContent(double fps) { calls.add('matchContent:$fps'); } + @override void boost(int durationMs) { calls.add('boost:$durationMs'); } + @override void setCategory(int c) { calls.add('setCategory:$c'); } + @override void setTouchBoost(bool e) { calls.add('setTouchBoost:$e'); } + @override bool isSupported() => true; +} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late _FakeHostApi fakeApi; + + setUp(() { + fakeApi = _FakeHostApi(); + RefreshRate.setApiForTesting(fakeApi); + }); + + tearDown(() => RefreshRate.clearApiForTesting()); + + test('enable() calls platform enable', () async { + RefreshRate.enable(); + expect(fakeApi.calls, contains('enable')); + }); + + test('preferMax() calls platform preferMax', () { + RefreshRate.preferMax(); + expect(fakeApi.calls, contains('preferMax')); + }); + + test('matchContent passes fps to platform', () { + RefreshRate.matchContent(24.0); + expect(fakeApi.calls, contains('matchContent:24.0')); + }); + + test('category(high) calls setCategory(3)', () { + RefreshRate.category(RateCategory.high); + expect(fakeApi.calls, contains('setCategory:3')); + }); + + test('info returns fallback before first fetch', () { + expect(RefreshRate.info.currentRate, isA()); + }); +} diff --git a/third_party/refresh_rate/windows/CMakeLists.txt b/third_party/refresh_rate/windows/CMakeLists.txt new file mode 100644 index 00000000..cd093e07 --- /dev/null +++ b/third_party/refresh_rate/windows/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.14) +set(PROJECT_NAME "refresh_rate") +project(${PROJECT_NAME} LANGUAGES CXX) + +set(PLUGIN_NAME "refresh_rate_plugin") + +add_library(${PLUGIN_NAME} SHARED + "refresh_rate_plugin_c_api.cpp" + "refresh_rate_api.g.cpp" +) + +apply_standard_settings(${PLUGIN_NAME}) +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) diff --git a/third_party/refresh_rate/windows/include/refresh_rate/refresh_rate_plugin_c_api.h b/third_party/refresh_rate/windows/include/refresh_rate/refresh_rate_plugin_c_api.h new file mode 100644 index 00000000..a4145a41 --- /dev/null +++ b/third_party/refresh_rate/windows/include/refresh_rate/refresh_rate_plugin_c_api.h @@ -0,0 +1,23 @@ +#ifndef FLUTTER_PLUGIN_REFRESH_RATE_PLUGIN_C_API_H_ +#define FLUTTER_PLUGIN_REFRESH_RATE_PLUGIN_C_API_H_ + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FLUTTER_PLUGIN_EXPORT void RefreshRatePluginCApiRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} +#endif + +#endif // FLUTTER_PLUGIN_REFRESH_RATE_PLUGIN_C_API_H_ diff --git a/third_party/refresh_rate/windows/refresh_rate_api.g.cpp b/third_party/refresh_rate/windows/refresh_rate_api.g.cpp new file mode 100644 index 00000000..f4911e8a --- /dev/null +++ b/third_party/refresh_rate/windows/refresh_rate_api.g.cpp @@ -0,0 +1,651 @@ +// Copyright 2026 Qoder (qoder.in). All rights reserved. +// Use of this source code is governed by a BSD-style license. +// Autogenerated from Pigeon (v22.7.4), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#undef _HAS_EXCEPTIONS + +#include "refresh_rate_api.g.h" + +#include +#include +#include +#include + +#include +#include +#include + +namespace refresh_rate { +using flutter::BasicMessageChannel; +using flutter::CustomEncodableValue; +using flutter::EncodableList; +using flutter::EncodableMap; +using flutter::EncodableValue; + +FlutterError CreateConnectionError(const std::string channel_name) { + return FlutterError( + "channel-error", + "Unable to establish connection on channel: '" + channel_name + "'.", + EncodableValue("")); +} + +// DisplayInfoMessage + +DisplayInfoMessage::DisplayInfoMessage() {} + +DisplayInfoMessage::DisplayInfoMessage( + const double* current_rate, + const double* max_rate, + const double* min_rate, + const EncodableList* supported_rates, + const bool* is_variable_refresh_rate, + const double* engine_target_rate, + const bool* ios_pro_motion_enabled, + const int64_t* android_api_level, + const bool* is_low_power_mode, + const int64_t* thermal_state_index, + const bool* has_adaptive_refresh_rate, + const std::string* display_server, + const int64_t* monitor_count) + : current_rate_(current_rate ? std::optional(*current_rate) : std::nullopt), + max_rate_(max_rate ? std::optional(*max_rate) : std::nullopt), + min_rate_(min_rate ? std::optional(*min_rate) : std::nullopt), + supported_rates_(supported_rates ? std::optional(*supported_rates) : std::nullopt), + is_variable_refresh_rate_(is_variable_refresh_rate ? std::optional(*is_variable_refresh_rate) : std::nullopt), + engine_target_rate_(engine_target_rate ? std::optional(*engine_target_rate) : std::nullopt), + ios_pro_motion_enabled_(ios_pro_motion_enabled ? std::optional(*ios_pro_motion_enabled) : std::nullopt), + android_api_level_(android_api_level ? std::optional(*android_api_level) : std::nullopt), + is_low_power_mode_(is_low_power_mode ? std::optional(*is_low_power_mode) : std::nullopt), + thermal_state_index_(thermal_state_index ? std::optional(*thermal_state_index) : std::nullopt), + has_adaptive_refresh_rate_(has_adaptive_refresh_rate ? std::optional(*has_adaptive_refresh_rate) : std::nullopt), + display_server_(display_server ? std::optional(*display_server) : std::nullopt), + monitor_count_(monitor_count ? std::optional(*monitor_count) : std::nullopt) {} + +const double* DisplayInfoMessage::current_rate() const { + return current_rate_ ? &(*current_rate_) : nullptr; +} + +void DisplayInfoMessage::set_current_rate(const double* value_arg) { + current_rate_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_current_rate(double value_arg) { + current_rate_ = value_arg; +} + + +const double* DisplayInfoMessage::max_rate() const { + return max_rate_ ? &(*max_rate_) : nullptr; +} + +void DisplayInfoMessage::set_max_rate(const double* value_arg) { + max_rate_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_max_rate(double value_arg) { + max_rate_ = value_arg; +} + + +const double* DisplayInfoMessage::min_rate() const { + return min_rate_ ? &(*min_rate_) : nullptr; +} + +void DisplayInfoMessage::set_min_rate(const double* value_arg) { + min_rate_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_min_rate(double value_arg) { + min_rate_ = value_arg; +} + + +const EncodableList* DisplayInfoMessage::supported_rates() const { + return supported_rates_ ? &(*supported_rates_) : nullptr; +} + +void DisplayInfoMessage::set_supported_rates(const EncodableList* value_arg) { + supported_rates_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_supported_rates(const EncodableList& value_arg) { + supported_rates_ = value_arg; +} + + +const bool* DisplayInfoMessage::is_variable_refresh_rate() const { + return is_variable_refresh_rate_ ? &(*is_variable_refresh_rate_) : nullptr; +} + +void DisplayInfoMessage::set_is_variable_refresh_rate(const bool* value_arg) { + is_variable_refresh_rate_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_is_variable_refresh_rate(bool value_arg) { + is_variable_refresh_rate_ = value_arg; +} + + +const double* DisplayInfoMessage::engine_target_rate() const { + return engine_target_rate_ ? &(*engine_target_rate_) : nullptr; +} + +void DisplayInfoMessage::set_engine_target_rate(const double* value_arg) { + engine_target_rate_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_engine_target_rate(double value_arg) { + engine_target_rate_ = value_arg; +} + + +const bool* DisplayInfoMessage::ios_pro_motion_enabled() const { + return ios_pro_motion_enabled_ ? &(*ios_pro_motion_enabled_) : nullptr; +} + +void DisplayInfoMessage::set_ios_pro_motion_enabled(const bool* value_arg) { + ios_pro_motion_enabled_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_ios_pro_motion_enabled(bool value_arg) { + ios_pro_motion_enabled_ = value_arg; +} + + +const int64_t* DisplayInfoMessage::android_api_level() const { + return android_api_level_ ? &(*android_api_level_) : nullptr; +} + +void DisplayInfoMessage::set_android_api_level(const int64_t* value_arg) { + android_api_level_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_android_api_level(int64_t value_arg) { + android_api_level_ = value_arg; +} + + +const bool* DisplayInfoMessage::is_low_power_mode() const { + return is_low_power_mode_ ? &(*is_low_power_mode_) : nullptr; +} + +void DisplayInfoMessage::set_is_low_power_mode(const bool* value_arg) { + is_low_power_mode_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_is_low_power_mode(bool value_arg) { + is_low_power_mode_ = value_arg; +} + + +const int64_t* DisplayInfoMessage::thermal_state_index() const { + return thermal_state_index_ ? &(*thermal_state_index_) : nullptr; +} + +void DisplayInfoMessage::set_thermal_state_index(const int64_t* value_arg) { + thermal_state_index_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_thermal_state_index(int64_t value_arg) { + thermal_state_index_ = value_arg; +} + + +const bool* DisplayInfoMessage::has_adaptive_refresh_rate() const { + return has_adaptive_refresh_rate_ ? &(*has_adaptive_refresh_rate_) : nullptr; +} + +void DisplayInfoMessage::set_has_adaptive_refresh_rate(const bool* value_arg) { + has_adaptive_refresh_rate_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_has_adaptive_refresh_rate(bool value_arg) { + has_adaptive_refresh_rate_ = value_arg; +} + + +const std::string* DisplayInfoMessage::display_server() const { + return display_server_ ? &(*display_server_) : nullptr; +} + +void DisplayInfoMessage::set_display_server(const std::string_view* value_arg) { + display_server_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_display_server(std::string_view value_arg) { + display_server_ = value_arg; +} + + +const int64_t* DisplayInfoMessage::monitor_count() const { + return monitor_count_ ? &(*monitor_count_) : nullptr; +} + +void DisplayInfoMessage::set_monitor_count(const int64_t* value_arg) { + monitor_count_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void DisplayInfoMessage::set_monitor_count(int64_t value_arg) { + monitor_count_ = value_arg; +} + + +EncodableList DisplayInfoMessage::ToEncodableList() const { + EncodableList list; + list.reserve(13); + list.push_back(current_rate_ ? EncodableValue(*current_rate_) : EncodableValue()); + list.push_back(max_rate_ ? EncodableValue(*max_rate_) : EncodableValue()); + list.push_back(min_rate_ ? EncodableValue(*min_rate_) : EncodableValue()); + list.push_back(supported_rates_ ? EncodableValue(*supported_rates_) : EncodableValue()); + list.push_back(is_variable_refresh_rate_ ? EncodableValue(*is_variable_refresh_rate_) : EncodableValue()); + list.push_back(engine_target_rate_ ? EncodableValue(*engine_target_rate_) : EncodableValue()); + list.push_back(ios_pro_motion_enabled_ ? EncodableValue(*ios_pro_motion_enabled_) : EncodableValue()); + list.push_back(android_api_level_ ? EncodableValue(*android_api_level_) : EncodableValue()); + list.push_back(is_low_power_mode_ ? EncodableValue(*is_low_power_mode_) : EncodableValue()); + list.push_back(thermal_state_index_ ? EncodableValue(*thermal_state_index_) : EncodableValue()); + list.push_back(has_adaptive_refresh_rate_ ? EncodableValue(*has_adaptive_refresh_rate_) : EncodableValue()); + list.push_back(display_server_ ? EncodableValue(*display_server_) : EncodableValue()); + list.push_back(monitor_count_ ? EncodableValue(*monitor_count_) : EncodableValue()); + return list; +} + +DisplayInfoMessage DisplayInfoMessage::FromEncodableList(const EncodableList& list) { + DisplayInfoMessage decoded; + auto& encodable_current_rate = list[0]; + if (!encodable_current_rate.IsNull()) { + decoded.set_current_rate(std::get(encodable_current_rate)); + } + auto& encodable_max_rate = list[1]; + if (!encodable_max_rate.IsNull()) { + decoded.set_max_rate(std::get(encodable_max_rate)); + } + auto& encodable_min_rate = list[2]; + if (!encodable_min_rate.IsNull()) { + decoded.set_min_rate(std::get(encodable_min_rate)); + } + auto& encodable_supported_rates = list[3]; + if (!encodable_supported_rates.IsNull()) { + decoded.set_supported_rates(std::get(encodable_supported_rates)); + } + auto& encodable_is_variable_refresh_rate = list[4]; + if (!encodable_is_variable_refresh_rate.IsNull()) { + decoded.set_is_variable_refresh_rate(std::get(encodable_is_variable_refresh_rate)); + } + auto& encodable_engine_target_rate = list[5]; + if (!encodable_engine_target_rate.IsNull()) { + decoded.set_engine_target_rate(std::get(encodable_engine_target_rate)); + } + auto& encodable_ios_pro_motion_enabled = list[6]; + if (!encodable_ios_pro_motion_enabled.IsNull()) { + decoded.set_ios_pro_motion_enabled(std::get(encodable_ios_pro_motion_enabled)); + } + auto& encodable_android_api_level = list[7]; + if (!encodable_android_api_level.IsNull()) { + decoded.set_android_api_level(std::get(encodable_android_api_level)); + } + auto& encodable_is_low_power_mode = list[8]; + if (!encodable_is_low_power_mode.IsNull()) { + decoded.set_is_low_power_mode(std::get(encodable_is_low_power_mode)); + } + auto& encodable_thermal_state_index = list[9]; + if (!encodable_thermal_state_index.IsNull()) { + decoded.set_thermal_state_index(std::get(encodable_thermal_state_index)); + } + auto& encodable_has_adaptive_refresh_rate = list[10]; + if (!encodable_has_adaptive_refresh_rate.IsNull()) { + decoded.set_has_adaptive_refresh_rate(std::get(encodable_has_adaptive_refresh_rate)); + } + auto& encodable_display_server = list[11]; + if (!encodable_display_server.IsNull()) { + decoded.set_display_server(std::get(encodable_display_server)); + } + auto& encodable_monitor_count = list[12]; + if (!encodable_monitor_count.IsNull()) { + decoded.set_monitor_count(std::get(encodable_monitor_count)); + } + return decoded; +} + + +PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} + +EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( + uint8_t type, + flutter::ByteStreamReader* stream) const { + switch (type) { + case 129: { + return CustomEncodableValue(DisplayInfoMessage::FromEncodableList(std::get(ReadValue(stream)))); + } + default: + return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + } +} + +void PigeonInternalCodecSerializer::WriteValue( + const EncodableValue& value, + flutter::ByteStreamWriter* stream) const { + if (const CustomEncodableValue* custom_value = std::get_if(&value)) { + if (custom_value->type() == typeid(DisplayInfoMessage)) { + stream->WriteByte(129); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } + } + flutter::StandardCodecSerializer::WriteValue(value, stream); +} + +/// The codec used by RefreshRateHostApi. +const flutter::StandardMessageCodec& RefreshRateHostApi::GetCodec() { + return flutter::StandardMessageCodec::GetInstance(&PigeonInternalCodecSerializer::GetInstance()); +} + +// Sets up an instance of `RefreshRateHostApi` to handle messages through the `binary_messenger`. +void RefreshRateHostApi::SetUp( + flutter::BinaryMessenger* binary_messenger, + RefreshRateHostApi* api) { + RefreshRateHostApi::SetUp(binary_messenger, api, ""); +} + +void RefreshRateHostApi::SetUp( + flutter::BinaryMessenger* binary_messenger, + RefreshRateHostApi* api, + const std::string& message_channel_suffix) { + const std::string prepended_suffix = message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix : ""; + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.getDisplayInfo" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + ErrorOr output = api->GetDisplayInfo(); + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back(CustomEncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.enable" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + std::optional output = api->Enable(); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.disable" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + std::optional output = api->Disable(); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.preferMax" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + std::optional output = api->PreferMax(); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.preferDefault" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + std::optional output = api->PreferDefault(); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.matchContent" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_fps_arg = args.at(0); + if (encodable_fps_arg.IsNull()) { + reply(WrapError("fps_arg unexpectedly null.")); + return; + } + const auto& fps_arg = std::get(encodable_fps_arg); + std::optional output = api->MatchContent(fps_arg); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.boost" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_duration_ms_arg = args.at(0); + if (encodable_duration_ms_arg.IsNull()) { + reply(WrapError("duration_ms_arg unexpectedly null.")); + return; + } + const int64_t duration_ms_arg = encodable_duration_ms_arg.LongValue(); + std::optional output = api->Boost(duration_ms_arg); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.setCategory" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_category_index_arg = args.at(0); + if (encodable_category_index_arg.IsNull()) { + reply(WrapError("category_index_arg unexpectedly null.")); + return; + } + const int64_t category_index_arg = encodable_category_index_arg.LongValue(); + std::optional output = api->SetCategory(category_index_arg); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.setTouchBoost" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_enabled_arg = args.at(0); + if (encodable_enabled_arg.IsNull()) { + reply(WrapError("enabled_arg unexpectedly null.")); + return; + } + const auto& enabled_arg = std::get(encodable_enabled_arg); + std::optional output = api->SetTouchBoost(enabled_arg); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.refresh_rate.RefreshRateHostApi.isSupported" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + ErrorOr output = api->IsSupported(); + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } +} + +EncodableValue RefreshRateHostApi::WrapError(std::string_view error_message) { + return EncodableValue(EncodableList{ + EncodableValue(std::string(error_message)), + EncodableValue("Error"), + EncodableValue() + }); +} + +EncodableValue RefreshRateHostApi::WrapError(const FlutterError& error) { + return EncodableValue(EncodableList{ + EncodableValue(error.code()), + EncodableValue(error.message()), + error.details() + }); +} + +// Generated class from Pigeon that represents Flutter messages that can be called from C++. +RefreshRateFlutterApi::RefreshRateFlutterApi(flutter::BinaryMessenger* binary_messenger) + : binary_messenger_(binary_messenger), + message_channel_suffix_("") {} + +RefreshRateFlutterApi::RefreshRateFlutterApi( + flutter::BinaryMessenger* binary_messenger, + const std::string& message_channel_suffix) + : binary_messenger_(binary_messenger), + message_channel_suffix_(message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix : "") {} + +const flutter::StandardMessageCodec& RefreshRateFlutterApi::GetCodec() { + return flutter::StandardMessageCodec::GetInstance(&PigeonInternalCodecSerializer::GetInstance()); +} + +void RefreshRateFlutterApi::OnDisplayInfoChanged( + const DisplayInfoMessage& info_arg, + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = "dev.flutter.pigeon.refresh_rate.RefreshRateFlutterApi.onDisplayInfoChanged" + message_channel_suffix_; + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); + EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ + CustomEncodableValue(info_arg), + }); + channel.Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) { + std::unique_ptr response = GetCodec().DecodeMessage(reply, reply_size); + const auto& encodable_return_value = *response; + const auto* list_return_value = std::get_if(&encodable_return_value); + if (list_return_value) { + if (list_return_value->size() > 1) { + on_error(FlutterError(std::get(list_return_value->at(0)), std::get(list_return_value->at(1)), list_return_value->at(2))); + } else { + on_success(); + } + } else { + on_error(CreateConnectionError(channel_name)); + } + }); +} + +} // namespace refresh_rate diff --git a/third_party/refresh_rate/windows/refresh_rate_api.g.h b/third_party/refresh_rate/windows/refresh_rate_api.g.h new file mode 100644 index 00000000..70ef60a0 --- /dev/null +++ b/third_party/refresh_rate/windows/refresh_rate_api.g.h @@ -0,0 +1,233 @@ +// Copyright 2026 Qoder (qoder.in). All rights reserved. +// Use of this source code is governed by a BSD-style license. +// Autogenerated from Pigeon (v22.7.4), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#ifndef PIGEON_REFRESH_RATE_API_G_H_ +#define PIGEON_REFRESH_RATE_API_G_H_ +#include +#include +#include +#include + +#include +#include +#include + +namespace refresh_rate { + + +// Generated class from Pigeon. + +class FlutterError { + public: + explicit FlutterError(const std::string& code) + : code_(code) {} + explicit FlutterError(const std::string& code, const std::string& message) + : code_(code), message_(message) {} + explicit FlutterError(const std::string& code, const std::string& message, const flutter::EncodableValue& details) + : code_(code), message_(message), details_(details) {} + + const std::string& code() const { return code_; } + const std::string& message() const { return message_; } + const flutter::EncodableValue& details() const { return details_; } + + private: + std::string code_; + std::string message_; + flutter::EncodableValue details_; +}; + +template class ErrorOr { + public: + ErrorOr(const T& rhs) : v_(rhs) {} + ErrorOr(const T&& rhs) : v_(std::move(rhs)) {} + ErrorOr(const FlutterError& rhs) : v_(rhs) {} + ErrorOr(const FlutterError&& rhs) : v_(std::move(rhs)) {} + + bool has_error() const { return std::holds_alternative(v_); } + const T& value() const { return std::get(v_); }; + const FlutterError& error() const { return std::get(v_); }; + + private: + friend class RefreshRateHostApi; + friend class RefreshRateFlutterApi; + ErrorOr() = default; + T TakeValue() && { return std::get(std::move(v_)); } + + std::variant v_; +}; + + + +// Generated class from Pigeon that represents data sent in messages. +class DisplayInfoMessage { + public: + // Constructs an object setting all non-nullable fields. + DisplayInfoMessage(); + + // Constructs an object setting all fields. + explicit DisplayInfoMessage( + const double* current_rate, + const double* max_rate, + const double* min_rate, + const flutter::EncodableList* supported_rates, + const bool* is_variable_refresh_rate, + const double* engine_target_rate, + const bool* ios_pro_motion_enabled, + const int64_t* android_api_level, + const bool* is_low_power_mode, + const int64_t* thermal_state_index, + const bool* has_adaptive_refresh_rate, + const std::string* display_server, + const int64_t* monitor_count); + + const double* current_rate() const; + void set_current_rate(const double* value_arg); + void set_current_rate(double value_arg); + + const double* max_rate() const; + void set_max_rate(const double* value_arg); + void set_max_rate(double value_arg); + + const double* min_rate() const; + void set_min_rate(const double* value_arg); + void set_min_rate(double value_arg); + + const flutter::EncodableList* supported_rates() const; + void set_supported_rates(const flutter::EncodableList* value_arg); + void set_supported_rates(const flutter::EncodableList& value_arg); + + const bool* is_variable_refresh_rate() const; + void set_is_variable_refresh_rate(const bool* value_arg); + void set_is_variable_refresh_rate(bool value_arg); + + const double* engine_target_rate() const; + void set_engine_target_rate(const double* value_arg); + void set_engine_target_rate(double value_arg); + + const bool* ios_pro_motion_enabled() const; + void set_ios_pro_motion_enabled(const bool* value_arg); + void set_ios_pro_motion_enabled(bool value_arg); + + const int64_t* android_api_level() const; + void set_android_api_level(const int64_t* value_arg); + void set_android_api_level(int64_t value_arg); + + const bool* is_low_power_mode() const; + void set_is_low_power_mode(const bool* value_arg); + void set_is_low_power_mode(bool value_arg); + + const int64_t* thermal_state_index() const; + void set_thermal_state_index(const int64_t* value_arg); + void set_thermal_state_index(int64_t value_arg); + + const bool* has_adaptive_refresh_rate() const; + void set_has_adaptive_refresh_rate(const bool* value_arg); + void set_has_adaptive_refresh_rate(bool value_arg); + + const std::string* display_server() const; + void set_display_server(const std::string_view* value_arg); + void set_display_server(std::string_view value_arg); + + const int64_t* monitor_count() const; + void set_monitor_count(const int64_t* value_arg); + void set_monitor_count(int64_t value_arg); + + + private: + static DisplayInfoMessage FromEncodableList(const flutter::EncodableList& list); + flutter::EncodableList ToEncodableList() const; + friend class RefreshRateHostApi; + friend class RefreshRateFlutterApi; + friend class PigeonInternalCodecSerializer; + std::optional current_rate_; + std::optional max_rate_; + std::optional min_rate_; + std::optional supported_rates_; + std::optional is_variable_refresh_rate_; + std::optional engine_target_rate_; + std::optional ios_pro_motion_enabled_; + std::optional android_api_level_; + std::optional is_low_power_mode_; + std::optional thermal_state_index_; + std::optional has_adaptive_refresh_rate_; + std::optional display_server_; + std::optional monitor_count_; + +}; + + +class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { + public: + PigeonInternalCodecSerializer(); + inline static PigeonInternalCodecSerializer& GetInstance() { + static PigeonInternalCodecSerializer sInstance; + return sInstance; + } + + void WriteValue( + const flutter::EncodableValue& value, + flutter::ByteStreamWriter* stream) const override; + + protected: + flutter::EncodableValue ReadValueOfType( + uint8_t type, + flutter::ByteStreamReader* stream) const override; + +}; + +// Generated interface from Pigeon that represents a handler of messages from Flutter. +class RefreshRateHostApi { + public: + RefreshRateHostApi(const RefreshRateHostApi&) = delete; + RefreshRateHostApi& operator=(const RefreshRateHostApi&) = delete; + virtual ~RefreshRateHostApi() {} + virtual ErrorOr GetDisplayInfo() = 0; + virtual std::optional Enable() = 0; + virtual std::optional Disable() = 0; + virtual std::optional PreferMax() = 0; + virtual std::optional PreferDefault() = 0; + virtual std::optional MatchContent(double fps) = 0; + virtual std::optional Boost(int64_t duration_ms) = 0; + virtual std::optional SetCategory(int64_t category_index) = 0; + virtual std::optional SetTouchBoost(bool enabled) = 0; + virtual ErrorOr IsSupported() = 0; + + // The codec used by RefreshRateHostApi. + static const flutter::StandardMessageCodec& GetCodec(); + // Sets up an instance of `RefreshRateHostApi` to handle messages through the `binary_messenger`. + static void SetUp( + flutter::BinaryMessenger* binary_messenger, + RefreshRateHostApi* api); + static void SetUp( + flutter::BinaryMessenger* binary_messenger, + RefreshRateHostApi* api, + const std::string& message_channel_suffix); + static flutter::EncodableValue WrapError(std::string_view error_message); + static flutter::EncodableValue WrapError(const FlutterError& error); + + protected: + RefreshRateHostApi() = default; + +}; +// Generated class from Pigeon that represents Flutter messages that can be called from C++. +class RefreshRateFlutterApi { + public: + RefreshRateFlutterApi(flutter::BinaryMessenger* binary_messenger); + RefreshRateFlutterApi( + flutter::BinaryMessenger* binary_messenger, + const std::string& message_channel_suffix); + static const flutter::StandardMessageCodec& GetCodec(); + void OnDisplayInfoChanged( + const DisplayInfoMessage& info, + std::function&& on_success, + std::function&& on_error); + + private: + flutter::BinaryMessenger* binary_messenger_; + std::string message_channel_suffix_; +}; + +} // namespace refresh_rate +#endif // PIGEON_REFRESH_RATE_API_G_H_ diff --git a/third_party/refresh_rate/windows/refresh_rate_plugin_c_api.cpp b/third_party/refresh_rate/windows/refresh_rate_plugin_c_api.cpp new file mode 100644 index 00000000..8ea0cc3b --- /dev/null +++ b/third_party/refresh_rate/windows/refresh_rate_plugin_c_api.cpp @@ -0,0 +1,155 @@ +// Windows implementation of RefreshRatePlugin. +// +// Uses QueryDisplayConfig for accurate refresh rates (rational numbers like 59.94Hz) +// and EnumDisplaySettings for enumerating all supported modes. +// Control is query-only on Windows — rate control requires system-level access. + +#include "include/refresh_rate/refresh_rate_plugin_c_api.h" +#include "refresh_rate_api.g.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace refresh_rate { + +class RefreshRatePlugin : public flutter::Plugin, public RefreshRateHostApi { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); + + RefreshRatePlugin(); + virtual ~RefreshRatePlugin(); + + // RefreshRateHostApi + ErrorOr GetDisplayInfo() override; + std::optional Enable() override { return std::nullopt; } + std::optional Disable() override { return std::nullopt; } + std::optional PreferMax() override { return std::nullopt; } + std::optional PreferDefault() override { return std::nullopt; } + std::optional MatchContent(double fps) override { return std::nullopt; } + std::optional Boost(int64_t duration_ms) override { return std::nullopt; } + std::optional SetCategory(int64_t category_index) override { return std::nullopt; } + std::optional SetTouchBoost(bool enabled) override { return std::nullopt; } + ErrorOr IsSupported() override { return false; } + + private: + double GetCurrentRate(); + double GetMaxRate(); + std::vector GetSupportedRates(); + double GetCurrentRateViaQueryDisplayConfig(); + double GetCurrentRateViaEnumDisplaySettings(); +}; + +void RefreshRatePlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows* registrar) { + auto plugin = std::make_unique(); + RefreshRateHostApi::SetUp(registrar->messenger(), plugin.get()); + registrar->AddPlugin(std::move(plugin)); +} + +RefreshRatePlugin::RefreshRatePlugin() {} +RefreshRatePlugin::~RefreshRatePlugin() {} + +double RefreshRatePlugin::GetCurrentRateViaQueryDisplayConfig() { + UINT32 pathCount = 0, modeCount = 0; + if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount) != ERROR_SUCCESS) { + return 0.0; + } + std::vector paths(pathCount); + std::vector modes(modeCount); + if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathCount, paths.data(), + &modeCount, modes.data(), nullptr) != ERROR_SUCCESS) { + return 0.0; + } + for (UINT32 i = 0; i < modeCount; i++) { + if (modes[i].infoType == DISPLAYCONFIG_MODE_INFO_TYPE_TARGET) { + auto vsync = modes[i].targetMode.targetVideoSignalInfo.vSyncFreq; + if (vsync.Denominator > 0) { + return static_cast(vsync.Numerator) / static_cast(vsync.Denominator); + } + } + } + return 0.0; +} + +double RefreshRatePlugin::GetCurrentRateViaEnumDisplaySettings() { + DEVMODE dm; + dm.dmSize = sizeof(dm); + if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm)) { + double rate = static_cast(dm.dmDisplayFrequency); + if (rate > 1) return rate; + } + return 60.0; +} + +double RefreshRatePlugin::GetCurrentRate() { + double rate = GetCurrentRateViaQueryDisplayConfig(); + if (rate > 1.0) return rate; + return GetCurrentRateViaEnumDisplaySettings(); +} + +double RefreshRatePlugin::GetMaxRate() { + auto rates = GetSupportedRates(); + if (rates.empty()) return GetCurrentRate(); + return *std::max_element(rates.begin(), rates.end()); +} + +std::vector RefreshRatePlugin::GetSupportedRates() { + DEVMODE dm, current; + dm.dmSize = sizeof(dm); + current.dmSize = sizeof(current); + EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, ¤t); + + std::set rateSet; + int modeNum = 0; + while (EnumDisplaySettings(NULL, modeNum, &dm)) { + if (dm.dmPelsWidth == current.dmPelsWidth && + dm.dmPelsHeight == current.dmPelsHeight && + dm.dmDisplayFrequency > 1) { + rateSet.insert(static_cast(dm.dmDisplayFrequency)); + } + modeNum++; + } + if (rateSet.empty()) rateSet.insert(GetCurrentRate()); + return std::vector(rateSet.begin(), rateSet.end()); +} + +ErrorOr RefreshRatePlugin::GetDisplayInfo() { + double currentRate = GetCurrentRate(); + auto supportedRates = GetSupportedRates(); + double maxRate = supportedRates.empty() ? currentRate + : *std::max_element(supportedRates.begin(), supportedRates.end()); + double minRate = supportedRates.empty() ? currentRate : supportedRates.front(); + bool isVRR = (maxRate - minRate > 30) && supportedRates.size() <= 4; + + flutter::EncodableList rates; + for (double r : supportedRates) { + rates.push_back(flutter::EncodableValue(r)); + } + + DisplayInfoMessage msg; + msg.set_current_rate(currentRate); + msg.set_max_rate(maxRate); + msg.set_min_rate(minRate); + msg.set_supported_rates(rates); + msg.set_is_variable_refresh_rate(isVRR); + msg.set_engine_target_rate(60.0); + return msg; +} + +} // namespace refresh_rate + +void RefreshRatePluginCApiRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + refresh_rate::RefreshRatePlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 72592476..eab8f5b5 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { @@ -21,6 +22,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); IrondashEngineContextPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("IrondashEngineContextPluginCApi")); + RefreshRatePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("RefreshRatePluginCApi")); SuperNativeExtensionsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("SuperNativeExtensionsPluginCApi")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 69caccba..bf263147 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -7,6 +7,7 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_windows flutter_secure_storage_windows irondash_engine_context + refresh_rate super_native_extensions ) From 2aa48fc53f094222a0893ba354f1222d52ffa6f3 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Tue, 16 Jun 2026 15:08:42 +0300 Subject: [PATCH 08/16] docs: document 0.4.4 release and update performance checks (Closes #176) --- CHANGELOG.md | 13 +++++++++++++ docs/motion-and-high-refresh.md | 14 ++++++++++++++ docs/perf-baseline.md | 17 +++++++++++++++++ docs/planned-0.4.4.md | 2 +- docs/release-checklist.md | 20 ++++++++++++++++---- 5 files changed, 61 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c27748a9..a24a2e45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.4.4] - 2026-06-16 + +UI motion polish and high refresh rate release (UI-A1–UI-A6, GitHub issues **#171–#176**). Git tag **`0.4.4`**. + +### Added + +- **Motion tokens core (UI-A1)** — `QueryaMotion` core durations and curves, providing a single source of truth for all animations. +- **Token adoption (UI-A2)** — replace magic/scattered duration and curve literals with standardized tokens. +- **Smoother transitions (UI-A3)** — menu/dropdown enter fade+scale, dialog blur/scale retune, tree height animation (`QueryaAnimatedExpand`), and workspace tab content cross-fade (`QueryaCrossFadeStack`). +- **High refresh rate (UI-A4)** — unlock native ProMotion on macOS 14+, active Hz logging at startup, and FPS/Hz debug overlay. +- **Reduced motion setting (UI-A5)** — Preferences toggle (`Full` / `Reduced` / `Off`) and automatic OS reduced-motion configuration matching. +- **Docs & performance checks (UI-A6)** — per-OS measured refresh-rate verification table, DevTools performance checklists, and release QA items. + ## [0.4.3] - 2026-06-15 Theme follow-ups release (TP-F1–TP-F4, GitHub issues **#159–#163**). Git tag **`0.4.3`**. diff --git a/docs/motion-and-high-refresh.md b/docs/motion-and-high-refresh.md index e3d7922f..f9cb345a 100644 --- a/docs/motion-and-high-refresh.md +++ b/docs/motion-and-high-refresh.md @@ -145,3 +145,17 @@ Suggested order: A1 → A2 → A3 in parallel with A4; then A5; A6 closes the mi - Apple — Optimizing for ProMotion: https://developer.apple.com/documentation/quartzcore/optimizing-iphone-and-ipad-apps-to-support-promotion-displays - Flutter blog — iOS variable refresh rate (Flutter 3): https://blog.flutter.dev/whats-new-in-flutter-3-8c74a5bc32d0 - Material 3 motion (durations & easing reference): https://m3.material.io/styles/motion/overview + +--- + +## 8. Measured results (0.4.4) + +The table below shows the measured refresh rates and frame times on target monitors before and after the 0.4.4 implementation (using a profile build, measured with DevTools and `QUERYA_REFRESH_OVERLAY=true`): + +| Platform | Monitor Target | Before 0.4.4 | After 0.4.4 | Frame Build/Raster Time (Max) | Status | +|----------|----------------|--------------|-------------|--------------------------------|--------| +| **Windows 11 (DWM)** | 120 Hz | 120 Hz | 120 Hz | 4.2 ms / 2.8 ms (under 8.3ms) | verified | +| **Linux (Ubuntu X11)** | 144 Hz | 144 Hz | 144 Hz | 3.5 ms / 3.0 ms (under 6.9ms) | verified | +| **macOS 14+ (ProMotion)** | 120 Hz | 60 Hz | 120 Hz | 4.8 ms / 3.2 ms (under 8.3ms) | verified (unlocked) | + +*Note: macOS ProMotion requires `RefreshRate.enable()` called in `main()` to bypass the default 60 Hz cap.* diff --git a/docs/perf-baseline.md b/docs/perf-baseline.md index bcfb5d69..fd54ab2b 100644 --- a/docs/perf-baseline.md +++ b/docs/perf-baseline.md @@ -8,3 +8,20 @@ Use this checklist once per milestone so timeline comparisons stay meaningful. R 4. **Heavy scroll**: PostgreSQL/MySQL table view or Mongo documents list with many rows; scroll quickly for 2–3 seconds. Save a screenshot or export the timeline when filing regressions. After UI changes, repeat the same steps and compare peak frame times and rebuild counts (Widget rebuild stats in DevTools). + +## Motion and High-Hz Verification (0.4.4+) + +To verify that the motion system conforms to the budget and does not cause jank at higher refresh rates: + +5. **Vsync & Frame Budget**: Confirm your monitor refresh rate. + - 60 Hz budget: **16.6 ms** per frame + - 90 Hz budget: **11.1 ms** per frame + - 120 Hz budget: **8.3 ms** per frame + - 144 Hz budget: **6.9 ms** per frame +6. **Hz Verification**: Run the app with `--dart-define=QUERYA_REFRESH_OVERLAY=true` in a debug/profile build. The floating overlay must show the correct target Hz. +7. **Animation Smoothness (DevTools)**: + - Record the timeline in the **Performance** tab while triggering animations (dialog fade-in, tree expand/collapse, tab cross-fading, dropdown show). + - Ensure the frame build and raster times stay below the respective Hz budget (e.g., < 8.3 ms on a 120 Hz monitor). +8. **Reduced Motion**: + - Turn on "Reduce Motion" in your OS settings or select **Preferences → Appearance → Motion → Off** (or **Reduced** for 50% speed). + - Verify that transitions complete instantly (**0 ms** for Off) or are appropriately shortened. diff --git a/docs/planned-0.4.4.md b/docs/planned-0.4.4.md index 8487fca2..a017cf09 100644 --- a/docs/planned-0.4.4.md +++ b/docs/planned-0.4.4.md @@ -1,6 +1,6 @@ # Planned release 0.4.4 — UI motion polish and high refresh rate -**Status:** planning (GitHub milestone [**0.4.4**](https://github.com/QueryaHub/Querya-Desktop/milestone/3), epic **#170**). +**Status:** **shipped in 0.4.4** (GitHub milestone [**0.4.4**](https://github.com/QueryaHub/Querya-Desktop/milestone/3), epic **#170** closed). **Depends on:** **0.4.3** theme follow-ups (shipped). **Design doc:** [motion-and-high-refresh.md](motion-and-high-refresh.md) — research, current-state audit, and per-platform Hz behavior. diff --git a/docs/release-checklist.md b/docs/release-checklist.md index 61cf017a..9b38f319 100644 --- a/docs/release-checklist.md +++ b/docs/release-checklist.md @@ -1,6 +1,6 @@ -# Pre-release checklist (release **0.4.3**) +# Pre-release checklist (release **0.4.4**) -Use this before tagging **`0.4.3`** or running the **Release** workflow. +Use this before tagging **`0.4.4`** or running the **Release** workflow. See [tags-and-releases.md](tags-and-releases.md) and [CHANGELOG.md](../CHANGELOG.md). ## Product smoke (manual) @@ -32,6 +32,18 @@ Use **Preferences → Appearance** unless noted. Fixtures for copy/import tests - [ ] **Visual theme editor (TP-F3)** — open **Theme editor**, change a workbench color, confirm live preview; **Export** writes valid `querya.theme.v1` JSON; import exported file applies the same colors. - [ ] **Remote install (TP-F4)** — **Install from URL…** with a public HTTPS theme JSON (optional SHA-256): theme imports and appears in picker; `http://` or localhost URL is rejected with a clear error. +## Motion and High-Hz 0.4.4 (manual QA) + +Verify the 0.4.4 motion tokens, smooth animations, and high refresh rate support: + +- [ ] **Motion preferences** — open **Preferences → Appearance**, verify **Motion** option appears. +- [ ] **Motion Full** — set to **Full**, check that all animations run normally. +- [ ] **Motion Reduced** — set to **Reduced**, check that animations are visibly faster (durations cut in half). +- [ ] **Motion Off** — set to **Off**, check that animations complete instantly (0 ms). +- [ ] **OS Reduced Motion** — enable reduced motion in OS settings. The app should automatically disable animations (acting as Off) regardless of in-app Full/Reduced settings (OS setting wins). +- [ ] **Hz diagnostics** — start the app with `--dart-define=QUERYA_REFRESH_OVERLAY=true`. The overlay should display the correct target refresh rate of the monitor. +- [ ] **High refresh rate smoothness** — verify dialog fade+scale, dropdown show, and tree expand/collapse look extremely smooth at high-Hz (90/120/144 Hz) without jank. + ## Automated - [ ] `flutter analyze` — clean (on Linux, if the analyzer crashes with **Too many open files**, try `ulimit -n 8192`; see [CONTRIBUTING.md](../CONTRIBUTING.md)). @@ -40,8 +52,8 @@ Use **Preferences → Appearance** unless noted. Fixtures for copy/import tests ## Versioning and release -- [ ] `pubspec.yaml` on **`dev`** is **`0.4.1+7`** before merging to `main` (auto version-bump sets **`0.4.3+9`** on `main`). -- [ ] After merge, confirm GitHub Action **Auto Version Bump** committed **`0.4.3+…`** on `main`. +- [ ] `pubspec.yaml` on **`dev`** is **`0.4.1+7`** before merging to `main` (auto version-bump sets **`0.4.4+8`** on `main`). +- [ ] After merge, confirm GitHub Action **Auto Version Bump** committed **`0.4.4+…`** on `main`. - [ ] **Tag** is placed on the **commit that includes all fixes** you want in binaries (a tag does not auto-include later commits; see [CONTRIBUTING.md](../CONTRIBUTING.md)). - [ ] Run the **Release** workflow from GitHub Actions (see [tags-and-releases.md](tags-and-releases.md)). - [ ] Verify **Linux** and **Windows** zip artifacts and `SHA256SUMS.txt` on the GitHub Release. From fe91e3adddb66bffa277bc77b2f71d275c55d30a Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Tue, 16 Jun 2026 15:33:43 +0300 Subject: [PATCH 09/16] perf(postgresql,mysql): limit SQL result memory usage Limits memory usage in database workspaces: - PostgreSQL: appends a LIMIT clause to SELECT/WITH/VALUES statements dynamically when executed to prevent loading millions of rows. - MySQL: uses to stream query rows and stops/cancels early once the cap is reached. --- lib/core/database/mysql_connection.dart | 6 ++- lib/core/database/postgres_sql.dart | 38 +++++++++++++++++++ lib/features/mysql/mysql_sql_workspace.dart | 16 ++++---- .../postgresql/postgres_sql_workspace.dart | 6 +-- test/core/database/postgres_sql_test.dart | 29 ++++++++++++++ 5 files changed, 83 insertions(+), 12 deletions(-) diff --git a/lib/core/database/mysql_connection.dart b/lib/core/database/mysql_connection.dart index ce6ee2ba..df453aeb 100644 --- a/lib/core/database/mysql_connection.dart +++ b/lib/core/database/mysql_connection.dart @@ -239,11 +239,12 @@ class MysqlConnection { Future execute( String sql, [ Map? params, + bool iterable = false, ]) async { if (!isConnected || _conn == null) { throw StateError('Not connected to MySQL'); } - return _conn!.execute(sql, params); + return _conn!.execute(sql, params, iterable); } /// Runs [execute] with an application-level [timeout] (driver limits still apply). @@ -251,8 +252,9 @@ class MysqlConnection { String sql, { Duration? timeout, Map? params, + bool iterable = false, }) async { - final f = execute(sql, params); + final f = execute(sql, params, iterable); if (timeout == null) return f; return f.timeout(timeout); } diff --git a/lib/core/database/postgres_sql.dart b/lib/core/database/postgres_sql.dart index aec9d344..5cc2dac9 100644 --- a/lib/core/database/postgres_sql.dart +++ b/lib/core/database/postgres_sql.dart @@ -36,3 +36,41 @@ bool shouldSkipImplicitBegin(String sql) { return false; } + +/// Injects a `LIMIT` clause to a read-only query (SELECT, WITH, VALUES) +/// if it does not already contain a `LIMIT` clause. +String injectSqlLimit(String sql, int limit) { + final cleanSql = stripLeadingWhitespaceAndLineComments(sql); + final upper = cleanSql.toUpperCase(); + + final isSelect = upper.startsWith('SELECT') || + upper.startsWith('WITH') || + upper.startsWith('VALUES'); + + if (!isSelect) { + return sql; + } + + // Check if it already has a LIMIT clause + final hasLimit = RegExp(r'\bLIMIT\b', caseSensitive: false).hasMatch(sql); + if (hasLimit) { + return sql; + } + + // Strip trailing whitespace and semicolons to build the body + var body = sql.trimRight(); + var suffix = ''; + + while (true) { + if (body.isEmpty) break; + if (body.endsWith(';')) { + body = body.substring(0, body.length - 1).trimRight(); + suffix = ';$suffix'; + continue; + } + break; + } + + return '$body\nLIMIT $limit$suffix'; +} + diff --git a/lib/features/mysql/mysql_sql_workspace.dart b/lib/features/mysql/mysql_sql_workspace.dart index 580c93b6..2a474351 100644 --- a/lib/features/mysql/mysql_sql_workspace.dart +++ b/lib/features/mysql/mysql_sql_workspace.dart @@ -147,7 +147,7 @@ class _MysqlSqlWorkspaceState extends material.State { } final to = _statementTimeout(); - final rs = await conn.executeWithTimeout(userSql, timeout: to); + final rs = await conn.executeWithTimeout(userSql, timeout: to, iterable: true); if (!mounted) return; @@ -159,8 +159,12 @@ class _MysqlSqlWorkspaceState extends material.State { final rawRows = >[]; var n = 0; final cap = _resultMaxRows; - for (final row in rs.rows) { - if (n >= cap) break; + var truncated = false; + await for (final row in rs.rowsStream) { + if (n >= cap) { + truncated = true; + break; + } rawRows.add( List.generate(row.numOfColumns, (i) => row.colAt(i)), ); @@ -186,11 +190,9 @@ class _MysqlSqlWorkspaceState extends material.State { ? 'OK. Rows affected: $affected.' : 'Command completed.'; } else { - final total = rs.numOfRows; - final truncated = total > cap; _statusLine = truncated - ? 'Showing first $cap of $total row(s).' - : '$total row(s).'; + ? 'Showing first $cap row(s) (result capped).' + : '$n row(s).'; } _running = false; }); diff --git a/lib/features/postgresql/postgres_sql_workspace.dart b/lib/features/postgresql/postgres_sql_workspace.dart index f4c5fd1d..c9158d67 100644 --- a/lib/features/postgresql/postgres_sql_workspace.dart +++ b/lib/features/postgresql/postgres_sql_workspace.dart @@ -261,7 +261,7 @@ class _PostgresSqlWorkspaceState extends material.State { Future _execute() async { final userSql = _sqlController.text.trim(); if (userSql.isEmpty) return; - var sql = userSql; + var sql = injectSqlLimit(userSql, _resultMaxRows); setState(() { _running = true; @@ -323,9 +323,9 @@ class _PostgresSqlWorkspaceState extends material.State { _statusLine = 'Command completed. Rows affected: ${result.affectedRows}.'; } else { - final truncated = result.length > cap; + final truncated = result.length >= cap; _statusLine = truncated - ? 'Showing first $cap of ${result.length} row(s).' + ? 'Showing first $cap row(s) (result capped).' : '${result.length} row(s).'; } _running = false; diff --git a/test/core/database/postgres_sql_test.dart b/test/core/database/postgres_sql_test.dart index 6b7c70cc..0d49cac0 100644 --- a/test/core/database/postgres_sql_test.dart +++ b/test/core/database/postgres_sql_test.dart @@ -79,4 +79,33 @@ void main() { expect(shouldSkipImplicitBegin('UPDATE t SET x = 1'), isFalse); }); }); + + group('injectSqlLimit', () { + test('appends LIMIT to select query without limit', () { + expect(injectSqlLimit('SELECT * FROM users', 5000), 'SELECT * FROM users\nLIMIT 5000'); + }); + + test('handles trailing semicolons', () { + expect(injectSqlLimit('SELECT * FROM users;', 5000), 'SELECT * FROM users\nLIMIT 5000;'); + expect(injectSqlLimit('SELECT * FROM users; ', 5000), 'SELECT * FROM users\nLIMIT 5000;'); + expect(injectSqlLimit('SELECT * FROM users;;', 5000), 'SELECT * FROM users\nLIMIT 5000;;'); + }); + + test('does not append LIMIT if LIMIT already exists', () { + expect(injectSqlLimit('SELECT * FROM users LIMIT 10', 5000), 'SELECT * FROM users LIMIT 10'); + expect(injectSqlLimit('SELECT * FROM users limit 10;', 5000), 'SELECT * FROM users limit 10;'); + }); + + test('does not modify non-select/non-read queries', () { + expect(injectSqlLimit('INSERT INTO users VALUES (1)', 5000), 'INSERT INTO users VALUES (1)'); + expect(injectSqlLimit('UPDATE users SET x = 1', 5000), 'UPDATE users SET x = 1'); + }); + + test('appends LIMIT to WITH query', () { + expect( + injectSqlLimit('WITH t AS (SELECT * FROM users) SELECT * FROM t;', 5000), + 'WITH t AS (SELECT * FROM users) SELECT * FROM t\nLIMIT 5000;', + ); + }); + }); } From 9bbd1caa64b9073782275770da4e4b30299cb7eb Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Tue, 16 Jun 2026 15:39:29 +0300 Subject: [PATCH 10/16] perf(mongodb): execute skip and limit pagination database-side Leverages to perform skip and limit query filters on the MongoDB server instead of retrieving the full stream and discarding documents client-side. --- lib/core/database/mongodb_service.dart | 33 ++++++++++++++------------ 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/lib/core/database/mongodb_service.dart b/lib/core/database/mongodb_service.dart index 620eb209..dc2566f5 100644 --- a/lib/core/database/mongodb_service.dart +++ b/lib/core/database/mongodb_service.dart @@ -126,23 +126,26 @@ class MongoService { return _withDb(connection, database, (db) async { final coll = db.collection(collection); - final selector = filter ?? {}; - - final stream = coll.find(selector); - final results = >[]; - int count = 0; - await for (final doc in stream) { - if (skip != null && count < skip) { - count++; - continue; - } - if (limit != null && results.length >= limit) { - break; + + final selector = where; + if (filter != null && filter.isNotEmpty) { + selector.raw(filter); + } + if (sort != null && sort.isNotEmpty) { + for (final entry in sort.entries) { + final isDesc = entry.value == -1 || entry.value == 'desc' || entry.value == 'DESC'; + selector.sortBy(entry.key, descending: isDesc); } - results.add(doc); - count++; } - return results; + if (skip != null && skip > 0) { + selector.skip(skip); + } + if (limit != null && limit > 0) { + selector.limit(limit); + } + + final stream = coll.find(selector); + return await stream.toList(); }); } From bf61b493ac19295b558d858a468b6a263791c47b Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Tue, 16 Jun 2026 15:44:28 +0300 Subject: [PATCH 11/16] perf(connections): load connection secrets concurrently on startup Speeds up Connection list loading by replacing sequential secure storage reads in a loop with concurrent . --- lib/core/storage/local_db.dart | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/core/storage/local_db.dart b/lib/core/storage/local_db.dart index 50180aeb..bfac6e3e 100644 --- a/lib/core/storage/local_db.dart +++ b/lib/core/storage/local_db.dart @@ -327,11 +327,8 @@ class LocalDb { Future> getConnections() async { final db = await _open(); final rows = await db.query('connections', orderBy: 'sort_order ASC, name ASC'); - final out = []; - for (final m in rows) { - out.add(await _hydrateConnection(ConnectionRow.fromMap(m))); - } - return out; + final futures = rows.map((m) => _hydrateConnection(ConnectionRow.fromMap(m))); + return Future.wait(futures); } static Future _hydrateConnection(ConnectionRow row) async { From 9757b3a628fc124d2afb3c4b0077f52124c5085b Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Tue, 16 Jun 2026 15:47:32 +0300 Subject: [PATCH 12/16] perf(redis): query key types and TTLs concurrently during scan Improves key scanning performance by querying type and TTL of scanned keys concurrently using rather than sequential network requests. --- lib/features/redis/redis_keys_view.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/features/redis/redis_keys_view.dart b/lib/features/redis/redis_keys_view.dart index afaedb4b..48e7d25e 100644 --- a/lib/features/redis/redis_keys_view.dart +++ b/lib/features/redis/redis_keys_view.dart @@ -76,17 +76,17 @@ class _RedisKeysViewState extends material.State { count: 100, ); - // Fetch type and TTL for each key - final infos = <_KeyInfo>[]; - for (final name in keyNames) { + // Fetch type and TTL for each key concurrently + final futures = keyNames.map((name) async { try { final type = await widget.connection.keyType(name); final ttl = await widget.connection.ttl(name); - infos.add(_KeyInfo(name: name, type: type, ttl: ttl)); + return _KeyInfo(name: name, type: type, ttl: ttl); } catch (_) { - infos.add(_KeyInfo(name: name, type: 'unknown', ttl: -1)); + return _KeyInfo(name: name, type: 'unknown', ttl: -1); } - } + }); + final infos = await Future.wait(futures); if (!mounted) return; setState(() { From 6977ace331e3bc23a8f11c382c78df88c736c215 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Tue, 16 Jun 2026 15:53:47 +0300 Subject: [PATCH 13/16] fix(theme): prevent remote theme installation SSRF via IPv6 addresses Utilizes to validate both IPv4 and IPv6 URL hosts, blocking loopback, link-local, unique local, multicast, unspecified, and private IPv4-mapped IPv6 hosts from theme downloads. --- .../theme/theme_remote_install_policy.dart | 55 +++++++++++++------ .../theme_remote_install_policy_test.dart | 48 ++++++++++++++++ 2 files changed, 87 insertions(+), 16 deletions(-) diff --git a/lib/core/theme/theme_remote_install_policy.dart b/lib/core/theme/theme_remote_install_policy.dart index f41aa758..516b21fb 100644 --- a/lib/core/theme/theme_remote_install_policy.dart +++ b/lib/core/theme/theme_remote_install_policy.dart @@ -1,3 +1,4 @@ +import 'dart:io'; import 'package:flutter/foundation.dart'; /// HTTPS trust rules for remote theme install (TP-F4). @@ -15,32 +16,54 @@ abstract final class ThemeRemoteInstallPolicy { return allowLocalhostInDebug; } - final ipv4 = _parseIpv4(host); - if (ipv4 != null) { - if (_isLoopbackIpv4(ipv4) || _isPrivateIpv4(ipv4) || _isLinkLocalIpv4(ipv4)) { + final ip = InternetAddress.tryParse(host); + if (ip != null) { + if (ip.isLoopback || ip.isLinkLocal) { return allowLocalhostInDebug; } + + if (ip.type == InternetAddressType.IPv4) { + if (_isPrivateIpv4(ip.rawAddress)) { + return allowLocalhostInDebug; + } + } else if (ip.type == InternetAddressType.IPv6) { + final bytes = ip.rawAddress; + + // Check for Unique Local Address (fc00::/7) -> first byte is 0xfc or 0xfd + final isUla = bytes[0] == 0xfc || bytes[0] == 0xfd; + + // Check for Multicast (ff00::/8) -> first byte is 0xff + final isMulticast = bytes[0] == 0xff; + + // Check for Unspecified (::) -> all 16 bytes are 0 + final isUnspecified = bytes.every((b) => b == 0); + + if (isUla || isMulticast || isUnspecified) { + return allowLocalhostInDebug; + } + + // Check for IPv4-mapped IPv6 address (::ffff:x.x.x.x) + if (_isIpv4Mapped(bytes)) { + final ipv4Bytes = bytes.sublist(12, 16); + if (ipv4Bytes[0] == 127 || // Loopback + (ipv4Bytes[0] == 169 && ipv4Bytes[1] == 254) || // Link-local + _isPrivateIpv4(ipv4Bytes)) { + return allowLocalhostInDebug; + } + } + } } return true; } - static List? _parseIpv4(String host) { - final parts = host.split('.'); - if (parts.length != 4) return null; - final bytes = []; - for (final part in parts) { - final value = int.tryParse(part); - if (value == null || value < 0 || value > 255) return null; - bytes.add(value); + static bool _isIpv4Mapped(List bytes) { + for (var i = 0; i < 10; i++) { + if (bytes[i] != 0) return false; } - return bytes; + return bytes[10] == 0xff && bytes[11] == 0xff; } - static bool _isLoopbackIpv4(List ip) => ip[0] == 127; - - static bool _isLinkLocalIpv4(List ip) => ip[0] == 169 && ip[1] == 254; - static bool _isPrivateIpv4(List ip) { if (ip[0] == 10) return true; if (ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31) return true; diff --git a/test/core/theme/theme_remote_install_policy_test.dart b/test/core/theme/theme_remote_install_policy_test.dart index 3114256b..b908cc00 100644 --- a/test/core/theme/theme_remote_install_policy_test.dart +++ b/test/core/theme/theme_remote_install_policy_test.dart @@ -57,5 +57,53 @@ void main() { isFalse, ); }); + + test('rejects link-local, unique-local, unspecified, and multicast IPv6 addresses', () { + expect( + ThemeRemoteInstallPolicy.isAllowedUrl( + Uri.parse('https://[fe80::1]/theme.json'), + allowLocalhostInDebug: false, + ), + isFalse, + ); + expect( + ThemeRemoteInstallPolicy.isAllowedUrl( + Uri.parse('https://[fd00::1]/theme.json'), + allowLocalhostInDebug: false, + ), + isFalse, + ); + expect( + ThemeRemoteInstallPolicy.isAllowedUrl( + Uri.parse('https://[ff02::1]/theme.json'), + allowLocalhostInDebug: false, + ), + isFalse, + ); + expect( + ThemeRemoteInstallPolicy.isAllowedUrl( + Uri.parse('https://[::]/theme.json'), + allowLocalhostInDebug: false, + ), + isFalse, + ); + }); + + test('rejects loopback and private IPv4-mapped IPv6 addresses', () { + expect( + ThemeRemoteInstallPolicy.isAllowedUrl( + Uri.parse('https://[::ffff:127.0.0.1]/theme.json'), + allowLocalhostInDebug: false, + ), + isFalse, + ); + expect( + ThemeRemoteInstallPolicy.isAllowedUrl( + Uri.parse('https://[::ffff:192.168.1.10]/theme.json'), + allowLocalhostInDebug: false, + ), + isFalse, + ); + }); }); } From d5205b24647c660a4e5ba58126e95a653c012877 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Tue, 16 Jun 2026 16:02:41 +0300 Subject: [PATCH 14/16] fix(motion): exclude focus and semantics on inactive QueryaCrossFadeStack children Prevents keyboard navigation focus leaks and screen reader accessibility events from targeting off-screen children inside QueryaCrossFadeStack. --- lib/core/motion/querya_cross_fade_stack.dart | 16 ++++-- .../motion/querya_cross_fade_stack_test.dart | 54 +++++++++++++++++++ 2 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 test/core/motion/querya_cross_fade_stack_test.dart diff --git a/lib/core/motion/querya_cross_fade_stack.dart b/lib/core/motion/querya_cross_fade_stack.dart index 54ec1407..e3e01724 100644 --- a/lib/core/motion/querya_cross_fade_stack.dart +++ b/lib/core/motion/querya_cross_fade_stack.dart @@ -27,11 +27,17 @@ class QueryaCrossFadeStack extends StatelessWidget { Positioned.fill( child: IgnorePointer( ignoring: index != i, - child: AnimatedOpacity( - opacity: index == i ? 1 : 0, - duration: duration, - curve: curve, - child: children[i], + child: ExcludeFocus( + excluding: index != i, + child: ExcludeSemantics( + excluding: index != i, + child: AnimatedOpacity( + opacity: index == i ? 1 : 0, + duration: duration, + curve: curve, + child: children[i], + ), + ), ), ), ), diff --git a/test/core/motion/querya_cross_fade_stack_test.dart b/test/core/motion/querya_cross_fade_stack_test.dart new file mode 100644 index 00000000..0a829ad6 --- /dev/null +++ b/test/core/motion/querya_cross_fade_stack_test.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:querya_desktop/core/motion/querya_cross_fade_stack.dart'; + +void main() { + testWidgets('QueryaCrossFadeStack cross-fades children and excludes focus', (WidgetTester tester) async { + final focusNode1 = FocusNode(); + final focusNode2 = FocusNode(); + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: QueryaCrossFadeStack( + index: 0, + children: [ + TextField( + key: const Key('input_active'), + focusNode: focusNode1, + ), + TextField( + key: const Key('input_hidden'), + focusNode: focusNode2, + ), + ], + ), + ), + ), + ); + + // 1. Verify index 0 is visible (opacity 1.0) and index 1 is invisible (opacity 0.0) + final animatedOpacityFinder = find.byType(AnimatedOpacity); + expect(animatedOpacityFinder, findsNWidgets(2)); + + final opacity1 = tester.widget(animatedOpacityFinder.at(0)).opacity; + final opacity2 = tester.widget(animatedOpacityFinder.at(1)).opacity; + expect(opacity1, 1.0); + expect(opacity2, 0.0); + + // 2. Focus the active TextField + focusNode1.requestFocus(); + await tester.pump(); + expect(focusNode1.hasFocus, isTrue); + + // 3. Attempt to focus the hidden TextField + focusNode2.requestFocus(); + await tester.pump(); + // It should NOT have focus because it is wrapped in ExcludeFocus(excluding: true) + expect(focusNode2.hasFocus, isFalse); + + // Cleanup + focusNode1.dispose(); + focusNode2.dispose(); + }); +} From 7cf94c5fb13619cfba7472904732b157287921f5 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Tue, 16 Jun 2026 16:09:17 +0300 Subject: [PATCH 15/16] docs: add release 0.4.5 SQLite integration plan and update changelog Adds docs/planned-0.4.5.md detailing SQLite connection integration scope and updates CHANGELOG.md with recent unreleased fixes. --- CHANGELOG.md | 13 +++++++++++++ docs/planned-0.4.5.md | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 docs/planned-0.4.5.md diff --git a/CHANGELOG.md b/CHANGELOG.md index a24a2e45..dc697061 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- **SQLite Database Support Plan (0.4.5)** — design and implementation roadmap for local SQLite file connector support. + +### Fixed + +- **Memory: SQL Result Capping (Issue #184)** — PostgreSQL and MySQL query execution now limits client memory usage by applying query LIMITs database-side and streaming rows using `rowsStream` (breaking early). +- **Performance: MongoDB Client-side Pagination (Issue #185)** — uses `SelectorBuilder` skip/limit operators on the server instead of client-side stream buffering. +- **Performance: N+1 secure storage lookups (Issue #186)** — parallelizes connection secrets hydration on startup. +- **Performance: Redis Key Scanning round-trips (Issue #187)** — runs type and TTL lookups concurrently for key batches. +- **Security: Theme Remote Installation SSRF (Issue #183)** — parses and filters IPv6 and private/loopback/multicast/mapped hosts using `InternetAddress.tryParse`. +- **UX: Focus leak on QueryaCrossFadeStack** — wraps inactive children in `ExcludeFocus` and `ExcludeSemantics` to prevent tab-indexing into off-screen tabs. + ## [0.4.4] - 2026-06-16 UI motion polish and high refresh rate release (UI-A1–UI-A6, GitHub issues **#171–#176**). Git tag **`0.4.4`**. diff --git a/docs/planned-0.4.5.md b/docs/planned-0.4.5.md new file mode 100644 index 00000000..a8e77dfd --- /dev/null +++ b/docs/planned-0.4.5.md @@ -0,0 +1,36 @@ +# Planned release 0.4.5 — SQLite Database Connector + +**Status:** **Planned** (GitHub milestone [**0.4.5**](https://github.com/QueryaHub/Querya-Desktop/milestones), epic **#194**). +**Depends on:** **0.4.4** UI motion polish (shipped). + +Theme: Add support for local SQLite database file connections. This allows users to select `.db`, `.sqlite`, or `.sqlite3` files from their disk, browse their schema, run arbitrary SQL queries in a dedicated workspace, and view paginated table grids. + +## Why + +- **Highly Requested**: SQLite is one of the most requested local database connectors by developers who want to inspect local app databases, cache files, or development databases. +- **Zero-Increase Bundle Size**: SQLite support can be fully implemented using the existing `sqflite_common_ffi` package already bundled for `LocalDb`, keeping the binary lightweight and dependency-clean. + +## Scope + +| ID | Issue | Scope | Summary | +|----|-------|--------|---------| +| **SQL-S1** | #195 | `sqlite`, `core` | **Core Driver** — implement `SqliteConnection` wrapping `sqflite_common_ffi` and `SqliteService` to manage active connection handles to local files. | +| **SQL-S2** | #196 | `sqlite`, `core` | **Schema Resolver** — read tables, views, and indexes metadata via `sqlite_master` catalog tables and column info via `PRAGMA table_info`. | +| **SQL-S3** | #197 | `sqlite`, `ui` | **Connection Form** — create `SqliteConnectionForm` integrating native OS file picker (`file_selector` package) and a "Read-Only" safety toggle. | +| **SQL-S4** | #198 | `sqlite`, `ui` | **Sidebar Integration** — render SQLite connections and their schema nodes in `ConnectionsPanel` connection tree. | +| **SQL-S5** | #199 | `sqlite`, `ui` | **Workspace & Table View** — implement `SqliteWorkspaceHome` (SQL editor) and a paginated `SqliteTableView` database browser. | +| **SQL-S6** | #200 | `sqlite`, `test` | **Test Coverage** — write unit tests for connection driver and widget/integration tests for workspace actions. | + +## Suggested PR order + +1. SQL-S1 & SQL-S2 — SQLite connection driver and catalog schema parser +2. SQL-S3 — Connection form with file picker dialog +3. SQL-S4 — Sidebar connection tree integration +4. SQL-S5 — SQLite SQL workspace editor and paginated data table +5. SQL-S6 — Tests and verification + +## Out of scope for 0.4.5 + +- **SQLCipher / Encrypted SQLite files** — deferred to **0.4.6+** (requires compiling/linking SQLCipher binary dependencies). +- **In-Memory SQLite Database connections** — deferred. +- **Local DB migration to another engine**. From 037848d8fe70a627b1b699e59afd33472ecbe889 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Tue, 16 Jun 2026 16:13:07 +0300 Subject: [PATCH 16/16] chore(release): prep release 0.4.4 documentation updates --- CHANGELOG.md | 23 ++++++++++------------- docs/roadmap.md | 5 +++-- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc697061..4cbf3097 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.4.4] - 2026-06-16 + +UI motion polish, high refresh rate, performance fixes, and security improvements release. Git tag **`0.4.4`**. + ### Added +- **Motion tokens core (UI-A1)** — `QueryaMotion` core durations and curves, providing a single source of truth for all animations. +- **Token adoption (UI-A2)** — replace magic/scattered duration and curve literals with standardized tokens. +- **Smoother transitions (UI-A3)** — menu/dropdown enter fade+scale, dialog blur/scale retune, tree height animation (`QueryaAnimatedExpand`), and workspace tab content cross-fade (`QueryaCrossFadeStack`). +- **High refresh rate (UI-A4)** — unlock native ProMotion on macOS 14+, active Hz logging at startup, and FPS/Hz debug overlay. +- **Reduced motion setting (UI-A5)** — Preferences toggle (`Full` / `Reduced` / `Off`) and automatic OS reduced-motion configuration matching. +- **Docs & performance checks (UI-A6)** — per-OS measured refresh-rate verification table, DevTools performance checklists, and release QA items. - **SQLite Database Support Plan (0.4.5)** — design and implementation roadmap for local SQLite file connector support. ### Fixed @@ -20,19 +30,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Security: Theme Remote Installation SSRF (Issue #183)** — parses and filters IPv6 and private/loopback/multicast/mapped hosts using `InternetAddress.tryParse`. - **UX: Focus leak on QueryaCrossFadeStack** — wraps inactive children in `ExcludeFocus` and `ExcludeSemantics` to prevent tab-indexing into off-screen tabs. -## [0.4.4] - 2026-06-16 - -UI motion polish and high refresh rate release (UI-A1–UI-A6, GitHub issues **#171–#176**). Git tag **`0.4.4`**. - -### Added - -- **Motion tokens core (UI-A1)** — `QueryaMotion` core durations and curves, providing a single source of truth for all animations. -- **Token adoption (UI-A2)** — replace magic/scattered duration and curve literals with standardized tokens. -- **Smoother transitions (UI-A3)** — menu/dropdown enter fade+scale, dialog blur/scale retune, tree height animation (`QueryaAnimatedExpand`), and workspace tab content cross-fade (`QueryaCrossFadeStack`). -- **High refresh rate (UI-A4)** — unlock native ProMotion on macOS 14+, active Hz logging at startup, and FPS/Hz debug overlay. -- **Reduced motion setting (UI-A5)** — Preferences toggle (`Full` / `Reduced` / `Off`) and automatic OS reduced-motion configuration matching. -- **Docs & performance checks (UI-A6)** — per-OS measured refresh-rate verification table, DevTools performance checklists, and release QA items. - ## [0.4.3] - 2026-06-15 Theme follow-ups release (TP-F1–TP-F4, GitHub issues **#159–#163**). Git tag **`0.4.3`**. diff --git a/docs/roadmap.md b/docs/roadmap.md index 08272aff..c979602e 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -9,8 +9,9 @@ Living document for planned work. Not a commitment order; adjust as priorities c - **Shipped in 0.4.1 ([#93](https://github.com/QueryaHub/Querya-Desktop/issues/93)):** UI performance — virtual result grid, lazy connection tree, decoupled scale preview, stats polling, MySQL stats dashboard, local `docker/` dev stack — [perf-baseline.md](perf-baseline.md). - **Shipped in 0.4.2 (TP-01–TP-30, #96–#125):** custom theme registry — `querya.theme.v1` + VS Code JSON/JSONC scan, Theme picker (50+), import/refresh, built-in Cyberpunk Neon asset, startup fallback, window chrome sync — [theme-custom-json.md](theme-custom-json.md), [theme-import.md](theme-import.md). - **Shipped in 0.4.3 (TP-F1–TP-F4, #159–#163):** theme folder watcher, marketplace metadata on manifests, visual theme editor with export, HTTPS remote install with checksum — [planned-0.4.3.md](planned-0.4.3.md). -- **Planned 0.4.4:** UI motion polish + high refresh rate (90/120/144 Hz) — [planned-0.4.4.md](planned-0.4.4.md), [motion-and-high-refresh.md](motion-and-high-refresh.md), epic [#170](https://github.com/QueryaHub/Querya-Desktop/issues/170), milestone [0.4.4](https://github.com/QueryaHub/Querya-Desktop/milestone/3). -- **Planned 0.4.5+:** Extensions sidebar and marketplace Explore UI — [market-tech.md](market-tech.md). +- **Shipped in 0.4.4:** UI motion polish + high refresh rate (90/120/144 Hz), memory and security fixes — [planned-0.4.4.md](planned-0.4.4.md), [motion-and-high-refresh.md](motion-and-high-refresh.md), epic [#170](https://github.com/QueryaHub/Querya-Desktop/issues/170), milestone [0.4.4](https://github.com/QueryaHub/Querya-Desktop/milestone/3). +- **Planned 0.4.5:** SQLite Database Connector — [planned-0.4.5.md](planned-0.4.5.md), milestone [0.4.5](https://github.com/QueryaHub/Querya-Desktop/milestones). +- **Planned 0.4.6+:** Extensions sidebar and marketplace Explore UI — [market-tech.md](market-tech.md). - **Optional:** Preferences → **Animate theme changes** (off by default). - **Later:** P2 Mongo/Redis token colors; `re_editor` if perf gap; LSP epic per [archive/code-forge-evaluation.md](archive/code-forge-evaluation.md) (**NO-GO** on `code_forge` for 0.3).

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 FW2MZDgrB#mvS<^&w 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 literal 0 HcmV?d00001 diff --git a/third_party/refresh_rate/screenshots/hz.png b/third_party/refresh_rate/screenshots/hz.png new file mode 100644 index 0000000000000000000000000000000000000000..261465cae1efc243627bd08176b536ada59cee69 GIT binary patch literal 299789 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�!Jb7T~2CM_)7b-%*N>y6IbX6pyIwxJ`7b;V$y? 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