diff --git a/lib/init.dart b/lib/init.dart index 2841a03..88549d9 100644 --- a/lib/init.dart +++ b/lib/init.dart @@ -1,4 +1,7 @@ +import 'dart:async'; + import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_saf/flutter_saf.dart'; import 'package:rhttp/rhttp.dart'; import 'package:venera/foundation/app.dart'; @@ -51,6 +54,14 @@ Future init() async { FlutterError.onError = (details) { Log.error("Unhandled Exception", "${details.exception}\n${details.stack}"); }; + if (App.isWindows) { + // Report to the monitor thread that the app is running + // https://github.com/venera-app/venera/issues/343 + Timer.periodic(const Duration(seconds: 1), (_) { + const methodChannel = MethodChannel('venera/method_channel'); + methodChannel.invokeMethod("heartBeat"); + }); + } } void _checkOldConfigs() { diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp index f379ea8..fd0590a 100644 --- a/windows/runner/flutter_window.cpp +++ b/windows/runner/flutter_window.cpp @@ -10,11 +10,16 @@ #include #include #include "flutter/generated_plugin_registrant.h" +#include #define _CRT_SECURE_NO_WARNINGS std::unique_ptr>&& mouseEvents = nullptr; +std::atomic mainThreadAlive(true); +std::atomic lastHeartbeat(std::chrono::steady_clock::now()); +std::thread* monitorThread = nullptr; + char* wideCharToMultiByte(wchar_t* pWCStrKey) { size_t pSize = WideCharToMultiByte(CP_OEMCP, 0, pWCStrKey, wcslen(pWCStrKey), NULL, 0, NULL, NULL); @@ -45,6 +50,22 @@ FlutterWindow::FlutterWindow(const flutter::DartProject& project) FlutterWindow::~FlutterWindow() {} +void monitorUIThread() { + const auto timeout = std::chrono::seconds(5); + + while (mainThreadAlive.load()) { + auto now = std::chrono::steady_clock::now(); + auto duration = now - lastHeartbeat.load(); + + if (duration > timeout) { + std::cerr << "The UI thread is dead. Terminate the application."; + std::exit(0); + } + + std::this_thread::sleep_for(std::chrono::seconds(1)); + } +} + bool FlutterWindow::OnCreate() { if (!Win32Window::OnCreate()) { return false; @@ -78,6 +99,13 @@ bool FlutterWindow::OnCreate() { result->Success(flutter::EncodableValue("No Proxy")); delete(res); } + else if (call.method_name() == "heartBeat") { + if (monitorThread == nullptr) { + monitorThread = new std::thread{ monitorUIThread }; + } + lastHeartbeat = std::chrono::steady_clock::now(); + result->Success(); + } }); flutter::EventChannel<> channel2( @@ -163,6 +191,10 @@ void FlutterWindow::OnDestroy() { } Win32Window::OnDestroy(); + if (monitorThread != nullptr) { + mainThreadAlive = false; + monitorThread->join(); + } } void mouse_side_button_listener(unsigned int input)