Skip to content

Commit

Permalink
chore: Implement "display-added" and "display-removed" for macOS
Browse files Browse the repository at this point in the history
  • Loading branch information
lijy91 committed Aug 18, 2024
1 parent 34f7259 commit 67831c9
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 11 deletions.
1 change: 1 addition & 0 deletions packages/screen_retriever/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## 0.2.0

* Convert to federated plugin
* Implement "display-added" and "display-removed" for macOS #5

## 0.1.9

Expand Down
29 changes: 20 additions & 9 deletions packages/screen_retriever/example/lib/pages/home.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ import 'package:hotkey_manager/hotkey_manager.dart';
import 'package:screen_retriever/screen_retriever.dart';
import 'package:screen_retriever_example/widgets/display_card.dart';

final hotKeyManager = HotKeyManager.instance;
final screenRetriever = ScreenRetriever.instance;

class _ListSection extends StatelessWidget {
const _ListSection({required this.title, required this.children});

Expand Down Expand Up @@ -42,25 +39,39 @@ class HomePage extends StatefulWidget {
State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
class _HomePageState extends State<HomePage> with ScreenListener {
Display? _primaryDisplay;
List<Display> _displayList = [];

@override
void initState() {
screenRetriever.addListener(this);
super.initState();
_init();
}

Future<void> _init() async {
// 初始化快捷键
hotKeyManager.unregisterAll();
hotKeyManager.register(
HotKey(KeyCode.keyD, modifiers: [KeyModifier.alt]),
keyDownHandler: (_) {
_handleGetCursorScreenPoint();
},
);
_getDisplays();
}

@override
void dispose() {
screenRetriever.removeListener(this);
super.dispose();
}

@override
void onScreenEvent(String eventName) {
BotToast.showText(
text: 'onScreenEvent: $eventName',
);
_getDisplays();
}

Future<void> _getDisplays() async {
_primaryDisplay = await screenRetriever.getPrimaryDisplay();
_displayList = await screenRetriever.getAllDisplays();
setState(() {});
Expand Down
32 changes: 32 additions & 0 deletions packages/screen_retriever/lib/src/screen_retriever.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:screen_retriever_platform_interface/screen_retriever_platform_interface.dart';

Expand All @@ -9,6 +10,35 @@ class ScreenRetriever {

ScreenRetrieverPlatform get _platform => ScreenRetrieverPlatform.instance;

/// Handle screen events from the platform side.
void _handleScreenEvent(event) {
String type = event['type'] as String;
for (var listener in _listeners) {
listener.onScreenEvent(type);
}
}

final ObserverList<ScreenListener> _listeners =
ObserverList<ScreenListener>();

bool get hasListeners {
return _listeners.isNotEmpty;
}

void addListener(ScreenListener listener) {
if (!hasListeners) {
_platform.onScreenEventReceiver.listen(_handleScreenEvent);
}
_listeners.add(listener);
}

void removeListener(ScreenListener listener) {
_listeners.remove(listener);
if (!hasListeners) {
_platform.onScreenEventReceiver.listen(null);
}
}

Future<Offset> getCursorScreenPoint() {
return _platform.getCursorScreenPoint();
}
Expand All @@ -21,3 +51,5 @@ class ScreenRetriever {
return _platform.getAllDisplays();
}
}

final ScreenRetriever screenRetriever = ScreenRetriever.instance;
4 changes: 4 additions & 0 deletions packages/screen_retriever/test/screen_retriever_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ class MockScreenRetrieverPlatform
with MockPlatformInterfaceMixin
implements ScreenRetrieverPlatform {
@override
Stream<Map<Object?, Object?>> get onScreenEventReceiver =>
throw UnimplementedError();

@override
Future<Offset> getCursorScreenPoint() {
return Future(() => const Offset(10.0, 10.0));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,64 @@ extension NSRect {
}
}

public class ScreenRetrieverMacosPlugin: NSObject, FlutterPlugin {
public class ScreenRetrieverMacosPlugin: NSObject, FlutterPlugin,FlutterStreamHandler {
private var _eventSink: FlutterEventSink?

var externalDisplayCount:Int = 0

public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "dev.leanflutter.plugins/screen_retriever", binaryMessenger: registrar.messenger)
let instance = ScreenRetrieverMacosPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
let eventChannel = FlutterEventChannel(name: "dev.leanflutter.plugins/screen_retriever_event", binaryMessenger: registrar.messenger)
eventChannel.setStreamHandler(instance)

instance.externalDisplayCount = NSScreen.screens.count
instance.setupNotificationCenter()
}

public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
self._eventSink = events
return nil;
}

public func onCancel(withArguments arguments: Any?) -> FlutterError? {
self._eventSink = nil
return nil
}

// public func handleDidFinishLaunching(_ notification: Notification) {
// externalDisplayCount = NSScreen.screens.count
// setupNotificationCenter()
// }

func setupNotificationCenter() {
NotificationCenter.default.addObserver(
self,
selector: #selector(handleDisplayConnection),
name: NSApplication.didChangeScreenParametersNotification,
object: nil)
}


@objc func handleDisplayConnection(notification: Notification) {
if externalDisplayCount < NSScreen.screens.count {
_emitEvent("display-added")
externalDisplayCount = NSScreen.screens.count
} else if externalDisplayCount > NSScreen.screens.count {
_emitEvent("display-removed")
externalDisplayCount = NSScreen.screens.count
}
}

public func _emitEvent(_ eventName: String) {
guard let eventSink = self._eventSink else {
return
}
let event: NSDictionary = [
"type": eventName,
]
eventSink(event)
}

public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/// Interface for listening to screen events.
abstract class ScreenListener {
abstract mixin class ScreenListener {
/// Called when a screen event occurs.
void onScreenEvent(String eventName) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ class MethodChannelScreenRetriever extends ScreenRetrieverPlatform {
'dev.leanflutter.plugins/screen_retriever',
);

/// The event channel used to receive events from the native platform.
@visibleForTesting
final eventChannel = const EventChannel(
'dev.leanflutter.plugins/screen_retriever_event',
);

@override
Stream<Map<Object?, Object?>> get onScreenEventReceiver {
return eventChannel.receiveBroadcastStream().cast<Map<Object?, Object?>>();
}

// The default arguments used for [methodChannel.invokeMethod].
Map<String, dynamic> get _defaultArguments {
MediaQueryData mediaQueryData = MediaQueryData.fromView(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ abstract class ScreenRetrieverPlatform extends PlatformInterface {
_instance = instance;
}

Stream<Map<Object?, Object?>> get onScreenEventReceiver {
throw UnimplementedError(
'onScreenEventReceiver() has not been implemented.',
);
}

Future<Offset> getCursorScreenPoint() {
throw UnimplementedError(
'getCursorScreenPoint() has not been implemented.',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ class MockScreenRetrieverPlatform
with MockPlatformInterfaceMixin
implements ScreenRetrieverPlatform {
@override
Stream<Map<Object?, Object?>> get onScreenEventReceiver =>
throw UnimplementedError();

@override
Future<Offset> getCursorScreenPoint() {
return Future(() => Offset.zero);
}
Expand Down

0 comments on commit 67831c9

Please sign in to comment.