diff --git a/lib/app.dart b/lib/app.dart index 0a35093..a3b9008 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -23,6 +23,7 @@ import 'api/api.dart'; import 'common/helper/system_helper.dart'; import 'common/io/rs_http.dart'; import 'common/rust/frb_generated.dart'; +import 'common/rust/api/rs_process.dart' as rs_process; import 'data/app_version_data.dart'; import 'generated/no_l10n_strings.dart'; import 'ui/home/downloader/home_downloader_ui.dart'; @@ -157,7 +158,9 @@ class AppGlobalModel extends _$AppGlobalModel { } state = state.copyWith(deviceUUID: deviceUUID, appLocale: locale); } catch (e) { - exit(1); + await rs_process.setForegroundWindow(windowName: "SCToolBox"); + dPrint("exit: db is locking ..."); + exit(0); } // init powershell diff --git a/lib/common/rust/api/rs_process.dart b/lib/common/rust/api/rs_process.dart index 12b38c4..bb6bb14 100644 --- a/lib/common/rust/api/rs_process.dart +++ b/lib/common/rust/api/rs_process.dart @@ -23,6 +23,10 @@ Stream start( Future write({required int rsPid, required String data, dynamic hint}) => RustLib.instance.api.write(rsPid: rsPid, data: data, hint: hint); +Future setForegroundWindow({required String windowName, dynamic hint}) => + RustLib.instance.api + .setForegroundWindow(windowName: windowName, hint: hint); + class RsProcessStreamData { final RsProcessStreamDataType dataType; final String data; diff --git a/lib/common/rust/frb_generated.dart b/lib/common/rust/frb_generated.dart index f5abb6d..abcfe60 100644 --- a/lib/common/rust/frb_generated.dart +++ b/lib/common/rust/frb_generated.dart @@ -57,7 +57,7 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.0.0-dev.32'; @override - int get rustContentHash => 1067953400; + int get rustContentHash => -1186168522; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( @@ -90,6 +90,8 @@ abstract class RustLibApi extends BaseApi { String? appId, dynamic hint}); + Future setForegroundWindow({required String windowName, dynamic hint}); + Stream start( {required String executable, required List arguments, @@ -241,6 +243,29 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["summary", "body", "appName", "appId"], ); + @override + Future setForegroundWindow({required String windowName, dynamic hint}) { + return handler.executeNormal(NormalTask( + callFfi: (port_) { + var arg0 = cst_encode_String(windowName); + return wire.wire_set_foreground_window(port_, arg0); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_bool, + decodeErrorData: dco_decode_AnyhowException, + ), + constMeta: kSetForegroundWindowConstMeta, + argValues: [windowName], + apiImpl: this, + hint: hint, + )); + } + + TaskConstMeta get kSetForegroundWindowConstMeta => const TaskConstMeta( + debugName: "set_foreground_window", + argNames: ["windowName"], + ); + @override Stream start( {required String executable, @@ -323,6 +348,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw as String; } + @protected + bool dco_decode_bool(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw as bool; + } + @protected int dco_decode_box_autoadd_u_64(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -498,6 +529,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return utf8.decoder.convert(inner); } + @protected + bool sse_decode_bool(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return deserializer.buffer.getUint8() != 0; + } + @protected int sse_decode_box_autoadd_u_64(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -679,9 +716,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - bool sse_decode_bool(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - return deserializer.buffer.getUint8() != 0; + bool cst_encode_bool(bool raw) { + // Codec=Cst (C-struct based), see doc to use other codecs + return raw; } @protected @@ -765,6 +802,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_strict(utf8.encoder.convert(self), serializer); } + @protected + void sse_encode_bool(bool self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + serializer.buffer.putUint8(self ? 1 : 0); + } + @protected void sse_encode_box_autoadd_u_64(int self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -924,10 +967,4 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { void sse_encode_unit(void self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs } - - @protected - void sse_encode_bool(bool self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - serializer.buffer.putUint8(self ? 1 : 0); - } } diff --git a/lib/common/rust/frb_generated.io.dart b/lib/common/rust/frb_generated.io.dart index e38424c..2f05444 100644 --- a/lib/common/rust/frb_generated.io.dart +++ b/lib/common/rust/frb_generated.io.dart @@ -34,6 +34,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected String dco_decode_String(dynamic raw); + @protected + bool dco_decode_bool(dynamic raw); + @protected int dco_decode_box_autoadd_u_64(dynamic raw); @@ -109,6 +112,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected String sse_decode_String(SseDeserializer deserializer); + @protected + bool sse_decode_bool(SseDeserializer deserializer); + @protected int sse_decode_box_autoadd_u_64(SseDeserializer deserializer); @@ -174,9 +180,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_decode_unit(SseDeserializer deserializer); - @protected - bool sse_decode_bool(SseDeserializer deserializer); - @protected ffi.Pointer cst_encode_AnyhowException( AnyhowException raw) { @@ -306,6 +309,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { wireObj.data = cst_encode_opt_list_prim_u_8_strict(apiObj.data); } + @protected + bool cst_encode_bool(bool raw); + @protected int cst_encode_i_32(int raw); @@ -345,6 +351,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_String(String self, SseSerializer serializer); + @protected + void sse_encode_bool(bool self, SseSerializer serializer); + @protected void sse_encode_box_autoadd_u_64(int self, SseSerializer serializer); @@ -412,9 +421,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_unit(void self, SseSerializer serializer); - - @protected - void sse_encode_bool(bool self, SseSerializer serializer); } // Section: wire_class @@ -582,6 +588,25 @@ class RustLibWire implements BaseWire { ffi.Pointer, ffi.Pointer)>(); + void wire_set_foreground_window( + int port_, + ffi.Pointer window_name, + ) { + return _wire_set_foreground_window( + port_, + window_name, + ); + } + + late final _wire_set_foreground_windowPtr = _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Int64, ffi.Pointer)>>( + 'frbgen_starcitizen_doctor_wire_set_foreground_window'); + late final _wire_set_foreground_window = + _wire_set_foreground_windowPtr.asFunction< + void Function(int, ffi.Pointer)>(); + void wire_start( int port_, ffi.Pointer executable, diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 33c81c0..c5260eb 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -22,4 +22,5 @@ anyhow = "1.0" win32job = "2" lazy_static = "1.4" scopeguard = "1.2" -notify-rust = "4" \ No newline at end of file +notify-rust = "4" +windows = { version = "0.56.0", features = ["Win32_System_Services", "Win32_UI_WindowsAndMessaging"] } \ No newline at end of file diff --git a/rust/src/api/rs_process.rs b/rust/src/api/rs_process.rs index 4a5a27f..51309f3 100644 --- a/rust/src/api/rs_process.rs +++ b/rust/src/api/rs_process.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use std::sync::Arc; -use async_std::task::block_on; +use async_std::task::block_on; use lazy_static::lazy_static; use scopeguard::defer; use tokio::io::AsyncBufReadExt; @@ -9,6 +9,9 @@ use tokio::io::AsyncWriteExt; use tokio::io::BufReader; use tokio::process::ChildStdin; use tokio::sync::Mutex; +use windows::core::{HSTRING, PCWSTR}; +use windows::Win32::Foundation::HWND; +use windows::Win32::UI::WindowsAndMessaging; use crate::frb_generated::StreamSink; @@ -35,9 +38,9 @@ lazy_static! { } pub async fn start( - executable: String, + executable: &str, arguments: Vec, - working_directory: String, + working_directory: &str, stream_sink: StreamSink, ) { let stream_sink_arc = Arc::from(stream_sink); @@ -161,3 +164,17 @@ async fn _process_output( stream_sink.add(message).unwrap(); } } + +pub fn set_foreground_window(window_name: &str) -> anyhow::Result { + let window_name_p: PCWSTR = PCWSTR(HSTRING::from(window_name).as_ptr()); + let h = unsafe { WindowsAndMessaging::FindWindowW(PCWSTR::null(), window_name_p) }; + if h == HWND::default() { + return Ok(false); + } + let sr = unsafe { WindowsAndMessaging::ShowWindow(h, WindowsAndMessaging::SW_RESTORE) }; + if !sr.as_bool() { + return Ok(false); + } + let r = unsafe { WindowsAndMessaging::SetForegroundWindow(h) }; + Ok(r.as_bool()) +} diff --git a/rust/src/frb_generated.io.rs b/rust/src/frb_generated.io.rs index 5cdfaa7..999958d 100644 --- a/rust/src/frb_generated.io.rs +++ b/rust/src/frb_generated.io.rs @@ -215,6 +215,14 @@ pub extern "C" fn frbgen_starcitizen_doctor_wire_send_notify( wire_send_notify_impl(port_, summary, body, app_name, app_id) } +#[no_mangle] +pub extern "C" fn frbgen_starcitizen_doctor_wire_set_foreground_window( + port_: i64, + window_name: *mut wire_cst_list_prim_u_8_strict, +) { + wire_set_foreground_window_impl(port_, window_name) +} + #[no_mangle] pub extern "C" fn frbgen_starcitizen_doctor_wire_start( port_: i64, diff --git a/rust/src/frb_generated.rs b/rust/src/frb_generated.rs index aec255f..41a9628 100644 --- a/rust/src/frb_generated.rs +++ b/rust/src/frb_generated.rs @@ -31,7 +31,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueNom, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.32"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1067953400; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1186168522; // Section: executor @@ -170,6 +170,26 @@ fn wire_send_notify_impl( }, ) } +fn wire_set_foreground_window_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + window_name: impl CstDecode, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "set_foreground_window", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let api_window_name = window_name.cst_decode(); + move |context| { + transform_result_dco((move || { + crate::api::rs_process::set_foreground_window(&api_window_name) + })()) + } + }, + ) +} fn wire_start_impl( port_: flutter_rust_bridge::for_generated::MessagePort, executable: impl CstDecode, @@ -198,9 +218,9 @@ fn wire_start_impl( (move || async move { Result::<_, ()>::Ok( crate::api::rs_process::start( - api_executable, + &api_executable, api_arguments, - api_working_directory, + &api_working_directory, api_stream_sink, ) .await, @@ -242,6 +262,12 @@ fn wire_write_impl( // Section: dart2rust +impl CstDecode for bool { + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> bool { + self + } +} impl CstDecode for i32 { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> i32 { @@ -350,6 +376,13 @@ impl SseDecode for String { } } +impl SseDecode for bool { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_u8().unwrap() != 0 + } +} + impl SseDecode for i32 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -566,13 +599,6 @@ impl SseDecode for () { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {} } -impl SseDecode for bool { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - deserializer.cursor.read_u8().unwrap() != 0 - } -} - fn pde_ffi_dispatcher_primary_impl( func_id: i32, port: flutter_rust_bridge::for_generated::MessagePort, @@ -754,6 +780,13 @@ impl SseEncode for String { } } +impl SseEncode for bool { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer.cursor.write_u8(self as _).unwrap(); + } +} + impl SseEncode for i32 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -954,13 +987,6 @@ impl SseEncode for () { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {} } -impl SseEncode for bool { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - serializer.cursor.write_u8(self as _).unwrap(); - } -} - #[cfg(not(target_family = "wasm"))] #[path = "frb_generated.io.rs"] mod io; diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp index f4979eb..a5ac2b8 100644 --- a/windows/runner/main.cpp +++ b/windows/runner/main.cpp @@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, FlutterWindow window(project); Win32Window::Point origin(0, 0); Win32Window::Size size(1280, 720); - if (!window.Create(L"starcitizen_doctor", origin, size)) { + if (!window.Create(L"SCToolBox", origin, size)) { return EXIT_FAILURE; } window.SetQuitOnClose(true);