♻️ Refactor ICP server to make it available across platform
This commit is contained in:
@@ -12,8 +12,6 @@ import 'package:web_socket_channel/web_socket_channel.dart';
|
|||||||
|
|
||||||
// Conditional imports for IPC server - use web stubs on web platform
|
// Conditional imports for IPC server - use web stubs on web platform
|
||||||
import 'ipc_server.dart' if (dart.library.html) 'ipc_server.web.dart';
|
import 'ipc_server.dart' if (dart.library.html) 'ipc_server.web.dart';
|
||||||
import 'ipc_server.windows.dart' if (dart.library.html) 'ipc_server.web.dart';
|
|
||||||
import 'ipc_server.unix.dart' if (dart.library.html) 'ipc_server.web.dart';
|
|
||||||
|
|
||||||
const String kRpcLogPrefix = 'arRPC.websocket';
|
const String kRpcLogPrefix = 'arRPC.websocket';
|
||||||
const String kRpcIpcLogPrefix = 'arRPC.ipc';
|
const String kRpcIpcLogPrefix = 'arRPC.ipc';
|
||||||
@@ -112,11 +110,7 @@ class ActivityRpcServer {
|
|||||||
final shouldStartIpc = !Platform.isMacOS && !kIsWeb;
|
final shouldStartIpc = !Platform.isMacOS && !kIsWeb;
|
||||||
if (shouldStartIpc) {
|
if (shouldStartIpc) {
|
||||||
try {
|
try {
|
||||||
if (Platform.isWindows) {
|
_ipcServer = MultiPlatformIpcServer();
|
||||||
_ipcServer = WindowsIpcServer();
|
|
||||||
} else {
|
|
||||||
_ipcServer = UnixIpcServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up IPC handlers
|
// Set up IPC handlers
|
||||||
_ipcServer!.handlePacket = (socket, packet, _) {
|
_ipcServer!.handlePacket = (socket, packet, _) {
|
||||||
|
@@ -1,6 +1,10 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:developer' as developer;
|
||||||
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'package:dart_ipc/dart_ipc.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
const String kRpcIpcLogPrefix = 'arRPC.ipc';
|
const String kRpcIpcLogPrefix = 'arRPC.ipc';
|
||||||
|
|
||||||
@@ -116,3 +120,178 @@ abstract class IpcSocketWrapper {
|
|||||||
return packets;
|
return packets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Multiplatform IPC Server implementation using dart_ipc
|
||||||
|
class MultiPlatformIpcServer extends IpcServer {
|
||||||
|
StreamSubscription? _serverSubscription;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> start() async {
|
||||||
|
try {
|
||||||
|
final ipcPath = Platform.isWindows
|
||||||
|
? r'\\.\pipe\discord-ipc-0'
|
||||||
|
: await _findAvailableUnixIpcPath();
|
||||||
|
|
||||||
|
final serverSocket = await bind(ipcPath);
|
||||||
|
developer.log(
|
||||||
|
'IPC listening at $ipcPath',
|
||||||
|
name: kRpcIpcLogPrefix,
|
||||||
|
);
|
||||||
|
|
||||||
|
_serverSubscription = serverSocket.listen((socket) {
|
||||||
|
final socketWrapper = MultiPlatformIpcSocketWrapper(socket);
|
||||||
|
addSocket(socketWrapper);
|
||||||
|
developer.log(
|
||||||
|
'New IPC connection!',
|
||||||
|
name: kRpcIpcLogPrefix,
|
||||||
|
);
|
||||||
|
_handleIpcData(socketWrapper);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception('Failed to start IPC server: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> stop() async {
|
||||||
|
for (var socket in sockets) {
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
} catch (e) {
|
||||||
|
developer.log('Error closing IPC socket: $e', name: kRpcIpcLogPrefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sockets.clear();
|
||||||
|
_serverSubscription?.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle incoming IPC data
|
||||||
|
void _handleIpcData(MultiPlatformIpcSocketWrapper socket) {
|
||||||
|
final startTime = DateTime.now();
|
||||||
|
socket.socket.listen((data) {
|
||||||
|
final readStart = DateTime.now();
|
||||||
|
socket.addData(data);
|
||||||
|
final readDuration = DateTime.now().difference(readStart).inMicroseconds;
|
||||||
|
developer.log(
|
||||||
|
'Read data took $readDuration microseconds',
|
||||||
|
name: kRpcIpcLogPrefix,
|
||||||
|
);
|
||||||
|
|
||||||
|
final packets = socket.readPackets();
|
||||||
|
for (final packet in packets) {
|
||||||
|
handlePacket?.call(socket, packet, {});
|
||||||
|
}
|
||||||
|
}, onDone: () {
|
||||||
|
developer.log('IPC connection closed', name: kRpcIpcLogPrefix);
|
||||||
|
socket.close();
|
||||||
|
}, onError: (e) {
|
||||||
|
developer.log('IPC data error: $e', name: kRpcIpcLogPrefix);
|
||||||
|
socket.closeWithCode(IpcCloseCodes.closeUnsupported, e.toString());
|
||||||
|
});
|
||||||
|
final totalDuration = DateTime.now().difference(startTime).inMicroseconds;
|
||||||
|
developer.log(
|
||||||
|
'_handleIpcData took $totalDuration microseconds',
|
||||||
|
name: kRpcIpcLogPrefix,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> _getMacOsSystemTmpDir() async {
|
||||||
|
final result = await Process.run('getconf', ['DARWIN_USER_TEMP_DIR']);
|
||||||
|
return (result.stdout as String).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find available IPC socket path for Unix-like systems
|
||||||
|
Future<String> _findAvailableUnixIpcPath() async {
|
||||||
|
// Build list of directories to try, with macOS-specific handling
|
||||||
|
final baseDirs = <String>[];
|
||||||
|
|
||||||
|
if (Platform.isMacOS) {
|
||||||
|
try {
|
||||||
|
final macTempDir = await _getMacOsSystemTmpDir();
|
||||||
|
if (macTempDir.isNotEmpty) {
|
||||||
|
baseDirs.add(macTempDir);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
developer.log(
|
||||||
|
'Failed to get macOS system temp dir: $e',
|
||||||
|
name: kRpcIpcLogPrefix,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add other standard directories
|
||||||
|
final otherDirs = [
|
||||||
|
Platform.environment['XDG_RUNTIME_DIR'],
|
||||||
|
Platform.environment['TMPDIR'],
|
||||||
|
Platform.environment['TMP'],
|
||||||
|
Platform.environment['TEMP'],
|
||||||
|
'/tmp',
|
||||||
|
];
|
||||||
|
|
||||||
|
baseDirs.addAll(
|
||||||
|
otherDirs.where((dir) => dir != null && dir.isNotEmpty).cast<String>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (final baseDir in baseDirs) {
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
final socketPath = path.join(baseDir, 'discord-ipc-$i');
|
||||||
|
try {
|
||||||
|
final socket = await bind(socketPath);
|
||||||
|
socket.close();
|
||||||
|
try {
|
||||||
|
await File(socketPath).delete();
|
||||||
|
} catch (_) {}
|
||||||
|
developer.log(
|
||||||
|
'IPC socket will be created at: $socketPath',
|
||||||
|
name: kRpcIpcLogPrefix,
|
||||||
|
);
|
||||||
|
return socketPath;
|
||||||
|
} catch (e) {
|
||||||
|
if (i == 0) {
|
||||||
|
developer.log(
|
||||||
|
'IPC path $socketPath not available: $e',
|
||||||
|
name: kRpcIpcLogPrefix,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw Exception(
|
||||||
|
'No available IPC socket paths found in any temp directory',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiplatform IPC Socket Wrapper
|
||||||
|
class MultiPlatformIpcSocketWrapper extends IpcSocketWrapper {
|
||||||
|
final dynamic socket;
|
||||||
|
|
||||||
|
MultiPlatformIpcSocketWrapper(this.socket);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void send(Map<String, dynamic> msg) {
|
||||||
|
developer.log('IPC sending: $msg', name: kRpcIpcLogPrefix);
|
||||||
|
final packet = IpcServer.encodeIpcPacket(IpcTypes.frame, msg);
|
||||||
|
socket.add(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void sendPong(dynamic data) {
|
||||||
|
final packet = IpcServer.encodeIpcPacket(IpcTypes.pong, data ?? {});
|
||||||
|
socket.add(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void close() {
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void closeWithCode(int code, [String message = '']) {
|
||||||
|
final closeData = {'code': code, 'message': message};
|
||||||
|
final packet = IpcServer.encodeIpcPacket(IpcTypes.close, closeData);
|
||||||
|
socket.add(packet);
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,172 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:developer' as developer;
|
|
||||||
import 'dart:io';
|
|
||||||
import 'package:path/path.dart' as path;
|
|
||||||
import 'ipc_server.dart';
|
|
||||||
|
|
||||||
class UnixIpcServer extends IpcServer {
|
|
||||||
ServerSocket? _ipcServer;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> start() async {
|
|
||||||
final ipcPath = await _findAvailableIpcPath();
|
|
||||||
_ipcServer = await ServerSocket.bind(
|
|
||||||
InternetAddress(ipcPath, type: InternetAddressType.unix),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
developer.log('IPC listening at $ipcPath', name: kRpcIpcLogPrefix);
|
|
||||||
|
|
||||||
_ipcServer!.listen((Socket socket) {
|
|
||||||
_onIpcConnection(socket);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> stop() async {
|
|
||||||
for (var socket in sockets) {
|
|
||||||
try {
|
|
||||||
socket.close();
|
|
||||||
} catch (e) {
|
|
||||||
developer.log('Error closing IPC socket: $e', name: kRpcIpcLogPrefix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sockets.clear();
|
|
||||||
await _ipcServer?.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle new IPC connection
|
|
||||||
void _onIpcConnection(Socket socket) {
|
|
||||||
developer.log('New IPC connection!', name: kRpcIpcLogPrefix);
|
|
||||||
|
|
||||||
final socketWrapper = UnixIpcSocketWrapper(socket);
|
|
||||||
addSocket(socketWrapper);
|
|
||||||
|
|
||||||
socket.listen(
|
|
||||||
(data) => _onIpcData(socketWrapper, data),
|
|
||||||
onError: (e) {
|
|
||||||
developer.log('IPC socket error: $e', name: kRpcIpcLogPrefix);
|
|
||||||
socket.close();
|
|
||||||
},
|
|
||||||
onDone: () {
|
|
||||||
developer.log('IPC socket closed', name: kRpcIpcLogPrefix);
|
|
||||||
removeSocket(socketWrapper);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle incoming IPC data
|
|
||||||
void _onIpcData(UnixIpcSocketWrapper socket, List<int> data) {
|
|
||||||
try {
|
|
||||||
socket.addData(data);
|
|
||||||
final packets = socket.readPackets();
|
|
||||||
for (final packet in packets) {
|
|
||||||
handlePacket?.call(socket, packet, {});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
developer.log('IPC data error: $e', name: kRpcIpcLogPrefix);
|
|
||||||
socket.closeWithCode(IpcCloseCodes.closeUnsupported, e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> _getMacOsSystemTmpDir() async {
|
|
||||||
final result = await Process.run('getconf', ['DARWIN_USER_TEMP_DIR']);
|
|
||||||
return (result.stdout as String).trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find available IPC socket path
|
|
||||||
Future<String> _findAvailableIpcPath() async {
|
|
||||||
// Build list of directories to try, with macOS-specific handling
|
|
||||||
final baseDirs = <String>[];
|
|
||||||
|
|
||||||
if (Platform.isMacOS) {
|
|
||||||
try {
|
|
||||||
final macTempDir = await _getMacOsSystemTmpDir();
|
|
||||||
if (macTempDir.isNotEmpty) {
|
|
||||||
baseDirs.add(macTempDir);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
developer.log(
|
|
||||||
'Failed to get macOS system temp dir: $e',
|
|
||||||
name: kRpcIpcLogPrefix,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add other standard directories
|
|
||||||
final otherDirs = [
|
|
||||||
Platform.environment['XDG_RUNTIME_DIR'],
|
|
||||||
Platform.environment['TMPDIR'],
|
|
||||||
Platform.environment['TMP'],
|
|
||||||
Platform.environment['TEMP'],
|
|
||||||
'/tmp',
|
|
||||||
];
|
|
||||||
|
|
||||||
baseDirs.addAll(
|
|
||||||
otherDirs.where((dir) => dir != null && dir.isNotEmpty).cast<String>(),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (final baseDir in baseDirs) {
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
final socketPath = path.join(baseDir, 'discord-ipc-$i');
|
|
||||||
try {
|
|
||||||
final socket = await ServerSocket.bind(
|
|
||||||
InternetAddress(socketPath, type: InternetAddressType.unix),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
socket.close();
|
|
||||||
try {
|
|
||||||
await File(socketPath).delete();
|
|
||||||
} catch (_) {}
|
|
||||||
developer.log(
|
|
||||||
'IPC socket will be created at: $socketPath',
|
|
||||||
name: kRpcIpcLogPrefix,
|
|
||||||
);
|
|
||||||
return socketPath;
|
|
||||||
} catch (e) {
|
|
||||||
if (i == 0) {
|
|
||||||
developer.log(
|
|
||||||
'IPC path $socketPath not available: $e',
|
|
||||||
name: kRpcIpcLogPrefix,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw Exception(
|
|
||||||
'No available IPC socket paths found in any temp directory',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class UnixIpcSocketWrapper extends IpcSocketWrapper {
|
|
||||||
final Socket socket;
|
|
||||||
|
|
||||||
UnixIpcSocketWrapper(this.socket);
|
|
||||||
|
|
||||||
@override
|
|
||||||
void send(Map<String, dynamic> msg) {
|
|
||||||
developer.log('IPC sending: $msg', name: kRpcIpcLogPrefix);
|
|
||||||
final packet = IpcServer.encodeIpcPacket(IpcTypes.frame, msg);
|
|
||||||
socket.add(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void sendPong(dynamic data) {
|
|
||||||
final packet = IpcServer.encodeIpcPacket(IpcTypes.pong, data ?? {});
|
|
||||||
socket.add(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void close() {
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void closeWithCode(int code, [String message = '']) {
|
|
||||||
final closeData = {'code': code, 'message': message};
|
|
||||||
final packet = IpcServer.encodeIpcPacket(IpcTypes.close, closeData);
|
|
||||||
socket.add(packet);
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -56,8 +56,6 @@ class IpcSocketWrapper {
|
|||||||
List<dynamic> readPackets() => [];
|
List<dynamic> readPackets() => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
class WindowsIpcServer extends IpcServer {}
|
class MultiPlatformIpcServer extends IpcServer {}
|
||||||
|
|
||||||
class UnixIpcServer extends IpcServer {}
|
class MultiPlatformIpcSocketWrapper extends IpcSocketWrapper {}
|
||||||
|
|
||||||
class WindowsIpcSocketWrapper extends IpcSocketWrapper {}
|
|
||||||
|
@@ -1,243 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:developer' as developer;
|
|
||||||
import 'dart:ffi';
|
|
||||||
import 'dart:io';
|
|
||||||
import 'dart:isolate';
|
|
||||||
import 'package:ffi/ffi.dart';
|
|
||||||
import 'package:win32/win32.dart';
|
|
||||||
import 'ipc_server.dart';
|
|
||||||
|
|
||||||
class WindowsIpcServer extends IpcServer {
|
|
||||||
int? _pipeHandle;
|
|
||||||
Timer? _ipcTimer;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> start() async {
|
|
||||||
final pipeName = r'\\.\pipe\discord-ipc-0'.toNativeUtf16();
|
|
||||||
try {
|
|
||||||
_pipeHandle = CreateNamedPipe(
|
|
||||||
pipeName,
|
|
||||||
PIPE_ACCESS_DUPLEX,
|
|
||||||
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
|
||||||
PIPE_UNLIMITED_INSTANCES,
|
|
||||||
4096, // Output buffer size
|
|
||||||
4096, // Input buffer size
|
|
||||||
0, // Default timeout
|
|
||||||
nullptr, // Security attributes
|
|
||||||
);
|
|
||||||
|
|
||||||
if (_pipeHandle == INVALID_HANDLE_VALUE) {
|
|
||||||
final error = GetLastError();
|
|
||||||
throw Exception('Failed to create named pipe: error code $error');
|
|
||||||
}
|
|
||||||
|
|
||||||
developer.log(
|
|
||||||
r'IPC named pipe created at \\.\pipe\discord-ipc-0',
|
|
||||||
name: kRpcIpcLogPrefix,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Start listening for connections in a separate isolate
|
|
||||||
_listenWindowsIpc();
|
|
||||||
} finally {
|
|
||||||
free(pipeName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> stop() async {
|
|
||||||
for (var socket in sockets) {
|
|
||||||
try {
|
|
||||||
socket.close();
|
|
||||||
} catch (e) {
|
|
||||||
developer.log('Error closing IPC socket: $e', name: kRpcIpcLogPrefix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sockets.clear();
|
|
||||||
|
|
||||||
if (_pipeHandle != null) {
|
|
||||||
try {
|
|
||||||
CloseHandle(_pipeHandle!);
|
|
||||||
} catch (e) {
|
|
||||||
developer.log('Error closing named pipe: $e', name: kRpcIpcLogPrefix);
|
|
||||||
}
|
|
||||||
_pipeHandle = null;
|
|
||||||
}
|
|
||||||
_ipcTimer?.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listen for Windows IPC connections in an isolate
|
|
||||||
void _listenWindowsIpc() async {
|
|
||||||
final receivePort = ReceivePort();
|
|
||||||
await Isolate.spawn(_windowsIpcIsolate, receivePort.sendPort);
|
|
||||||
|
|
||||||
receivePort.listen((message) {
|
|
||||||
developer.log(message.toString(), name: kRpcIpcLogPrefix);
|
|
||||||
if (message is int) {
|
|
||||||
final socketWrapper = WindowsIpcSocketWrapper(message);
|
|
||||||
addSocket(socketWrapper);
|
|
||||||
developer.log(
|
|
||||||
'New IPC connection on named pipe',
|
|
||||||
name: kRpcIpcLogPrefix,
|
|
||||||
);
|
|
||||||
_handleWindowsIpcData(socketWrapper);
|
|
||||||
start(); // Create new pipe for next connection
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _windowsIpcIsolate(SendPort sendPort) {
|
|
||||||
while (true) {
|
|
||||||
final pipeHandle = CreateNamedPipe(
|
|
||||||
r'\\.\pipe\discord-ipc-0'.toNativeUtf16(),
|
|
||||||
PIPE_ACCESS_DUPLEX,
|
|
||||||
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
|
||||||
PIPE_UNLIMITED_INSTANCES,
|
|
||||||
4096,
|
|
||||||
4096,
|
|
||||||
0,
|
|
||||||
nullptr,
|
|
||||||
);
|
|
||||||
if (pipeHandle == INVALID_HANDLE_VALUE) {
|
|
||||||
developer.log(
|
|
||||||
'Failed to create named pipe: ${GetLastError()}',
|
|
||||||
name: kRpcIpcLogPrefix,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
final connected = ConnectNamedPipe(pipeHandle, nullptr);
|
|
||||||
if (connected != 0 || GetLastError() == ERROR_PIPE_CONNECTED) {
|
|
||||||
sendPort.send(pipeHandle);
|
|
||||||
}
|
|
||||||
// Avoid tight loop
|
|
||||||
sleep(Duration(milliseconds: 500));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle Windows IPC data
|
|
||||||
void _handleWindowsIpcData(WindowsIpcSocketWrapper socket) async {
|
|
||||||
final startTime = DateTime.now();
|
|
||||||
final buffer = malloc.allocate<BYTE>(4096);
|
|
||||||
final bytesRead = malloc.allocate<DWORD>(4);
|
|
||||||
try {
|
|
||||||
while (socket.pipeHandle != null) {
|
|
||||||
final readStart = DateTime.now();
|
|
||||||
final success = ReadFile(
|
|
||||||
socket.pipeHandle!,
|
|
||||||
buffer.cast(),
|
|
||||||
4096,
|
|
||||||
bytesRead,
|
|
||||||
nullptr,
|
|
||||||
);
|
|
||||||
final readDuration =
|
|
||||||
DateTime.now().difference(readStart).inMicroseconds;
|
|
||||||
developer.log(
|
|
||||||
'ReadFile took $readDuration microseconds',
|
|
||||||
name: kRpcIpcLogPrefix,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (success == FALSE && GetLastError() != ERROR_MORE_DATA) {
|
|
||||||
developer.log(
|
|
||||||
'IPC read error: ${GetLastError()}',
|
|
||||||
name: kRpcIpcLogPrefix,
|
|
||||||
);
|
|
||||||
socket.close();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
final data = buffer.asTypedList(0);
|
|
||||||
socket.addData(data);
|
|
||||||
final packets = socket.readPackets();
|
|
||||||
for (final packet in packets) {
|
|
||||||
handlePacket?.call(socket, packet, {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
developer.log('IPC data error: $e', name: kRpcIpcLogPrefix);
|
|
||||||
socket.closeWithCode(IpcCloseCodes.closeUnsupported, e.toString());
|
|
||||||
} finally {
|
|
||||||
malloc.free(buffer);
|
|
||||||
malloc.free(bytesRead);
|
|
||||||
final totalDuration = DateTime.now().difference(startTime).inMicroseconds;
|
|
||||||
developer.log(
|
|
||||||
'handleWindowsIpcData took $totalDuration microseconds',
|
|
||||||
name: kRpcIpcLogPrefix,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class WindowsIpcSocketWrapper extends IpcSocketWrapper {
|
|
||||||
final int? pipeHandle;
|
|
||||||
|
|
||||||
WindowsIpcSocketWrapper(this.pipeHandle);
|
|
||||||
|
|
||||||
@override
|
|
||||||
void send(Map<String, dynamic> msg) {
|
|
||||||
developer.log('IPC sending: $msg', name: kRpcIpcLogPrefix);
|
|
||||||
final packet = IpcServer.encodeIpcPacket(IpcTypes.frame, msg);
|
|
||||||
final buffer = malloc.allocate<BYTE>(packet.length);
|
|
||||||
buffer.asTypedList(packet.length).setAll(0, packet);
|
|
||||||
final bytesWritten = malloc.allocate<DWORD>(4); // DWORD is 4 bytes
|
|
||||||
try {
|
|
||||||
WriteFile(
|
|
||||||
pipeHandle!,
|
|
||||||
buffer.cast(),
|
|
||||||
packet.length,
|
|
||||||
bytesWritten,
|
|
||||||
nullptr,
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
malloc.free(buffer);
|
|
||||||
malloc.free(bytesWritten);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void sendPong(dynamic data) {
|
|
||||||
final packet = IpcServer.encodeIpcPacket(IpcTypes.pong, data ?? {});
|
|
||||||
final buffer = malloc.allocate<BYTE>(packet.length);
|
|
||||||
buffer.asTypedList(packet.length).setAll(0, packet);
|
|
||||||
final bytesWritten = malloc.allocate<DWORD>(4); // DWORD is 4 bytes
|
|
||||||
try {
|
|
||||||
WriteFile(
|
|
||||||
pipeHandle!,
|
|
||||||
buffer.cast(),
|
|
||||||
packet.length,
|
|
||||||
bytesWritten,
|
|
||||||
nullptr,
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
malloc.free(buffer);
|
|
||||||
malloc.free(bytesWritten);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void close() {
|
|
||||||
if (pipeHandle != null) {
|
|
||||||
CloseHandle(pipeHandle!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void closeWithCode(int code, [String message = '']) {
|
|
||||||
final closeData = {'code': code, 'message': message};
|
|
||||||
final packet = IpcServer.encodeIpcPacket(IpcTypes.close, closeData);
|
|
||||||
final buffer = malloc.allocate<BYTE>(packet.length);
|
|
||||||
buffer.asTypedList(packet.length).setAll(0, packet);
|
|
||||||
final bytesWritten = malloc.allocate<DWORD>(4); // DWORD is 4 bytes
|
|
||||||
try {
|
|
||||||
WriteFile(
|
|
||||||
pipeHandle!,
|
|
||||||
buffer.cast(),
|
|
||||||
packet.length,
|
|
||||||
bytesWritten,
|
|
||||||
nullptr,
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
malloc.free(buffer);
|
|
||||||
malloc.free(bytesWritten);
|
|
||||||
}
|
|
||||||
CloseHandle(pipeHandle!);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -30,7 +30,7 @@ class UserInfoNotifier extends StateNotifier<AsyncValue<SnAccount?>> {
|
|||||||
final user = SnAccount.fromJson(response.data);
|
final user = SnAccount.fromJson(response.data);
|
||||||
state = AsyncValue.data(user);
|
state = AsyncValue.data(user);
|
||||||
|
|
||||||
if (kIsWeb || !Platform.isLinux) {
|
if (kIsWeb || !(Platform.isLinux || Platform.isWindows)) {
|
||||||
FirebaseAnalytics.instance.setUserId(id: user.id);
|
FirebaseAnalytics.instance.setUserId(id: user.id);
|
||||||
}
|
}
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stackTrace) {
|
||||||
@@ -44,7 +44,7 @@ class UserInfoNotifier extends StateNotifier<AsyncValue<SnAccount?>> {
|
|||||||
: 'failedToLoadUserInfoNetwork')
|
: 'failedToLoadUserInfoNetwork')
|
||||||
.tr()
|
.tr()
|
||||||
.trim(),
|
.trim(),
|
||||||
'${error.response!.statusCode}\n${error.response?.headers}',
|
'${error.response?.statusCode ?? 'Network Error'}\n${error.response?.headers}',
|
||||||
jsonEncode(error.response?.data),
|
jsonEncode(error.response?.data),
|
||||||
].join('\n\n'),
|
].join('\n\n'),
|
||||||
iconStyle: IconStyle.error,
|
iconStyle: IconStyle.error,
|
||||||
@@ -87,7 +87,7 @@ class UserInfoNotifier extends StateNotifier<AsyncValue<SnAccount?>> {
|
|||||||
final prefs = _ref.read(sharedPreferencesProvider);
|
final prefs = _ref.read(sharedPreferencesProvider);
|
||||||
await prefs.remove(kTokenPairStoreKey);
|
await prefs.remove(kTokenPairStoreKey);
|
||||||
_ref.invalidate(tokenProvider);
|
_ref.invalidate(tokenProvider);
|
||||||
if (kIsWeb || !Platform.isLinux) {
|
if (kIsWeb || !(Platform.isLinux || Platform.isWindows)) {
|
||||||
FirebaseAnalytics.instance.setUserId(id: null);
|
FirebaseAnalytics.instance.setUserId(id: null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -401,6 +401,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0+7.7.0"
|
version: "1.0.0+7.7.0"
|
||||||
|
dart_ipc:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: dart_ipc
|
||||||
|
sha256: "6cad558cda5304017c1f581df4c96fd4f8e4ee212aae7bfa4357716236faa9ba"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
dart_style:
|
dart_style:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@@ -150,6 +150,7 @@ dependencies:
|
|||||||
windows_notification: ^1.3.0
|
windows_notification: ^1.3.0
|
||||||
win32: ^5.14.0
|
win32: ^5.14.0
|
||||||
ffi: ^2.1.4
|
ffi: ^2.1.4
|
||||||
|
dart_ipc: ^1.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <bitsdojo_window_windows/bitsdojo_window_plugin.h>
|
#include <bitsdojo_window_windows/bitsdojo_window_plugin.h>
|
||||||
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||||
|
#include <dart_ipc/dart_ipc_plugin_c_api.h>
|
||||||
#include <file_saver/file_saver_plugin.h>
|
#include <file_saver/file_saver_plugin.h>
|
||||||
#include <file_selector_windows/file_selector_windows.h>
|
#include <file_selector_windows/file_selector_windows.h>
|
||||||
#include <firebase_core/firebase_core_plugin_c_api.h>
|
#include <firebase_core/firebase_core_plugin_c_api.h>
|
||||||
@@ -38,6 +39,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||||||
registry->GetRegistrarForPlugin("BitsdojoWindowPlugin"));
|
registry->GetRegistrarForPlugin("BitsdojoWindowPlugin"));
|
||||||
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
||||||
|
DartIpcPluginCApiRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("DartIpcPluginCApi"));
|
||||||
FileSaverPluginRegisterWithRegistrar(
|
FileSaverPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FileSaverPlugin"));
|
registry->GetRegistrarForPlugin("FileSaverPlugin"));
|
||||||
FileSelectorWindowsRegisterWithRegistrar(
|
FileSelectorWindowsRegisterWithRegistrar(
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
bitsdojo_window_windows
|
bitsdojo_window_windows
|
||||||
connectivity_plus
|
connectivity_plus
|
||||||
|
dart_ipc
|
||||||
file_saver
|
file_saver
|
||||||
file_selector_windows
|
file_selector_windows
|
||||||
firebase_core
|
firebase_core
|
||||||
|
Reference in New Issue
Block a user