From cd337c53081837b4a54f12706f2ec390c3fd17da Mon Sep 17 00:00:00 2001
From: holby <justin.holby@aboutgolf.com>
Date: Wed, 20 Nov 2024 20:40:56 -0500
Subject: [PATCH] WIP

---
 .../focused_window/focused_window_provider.rs | 104 ++++++++----------
 .../desktop/src/providers/provider_manager.rs |   9 +-
 .../desktop/src/providers/provider_output.rs  |   9 +-
 3 files changed, 60 insertions(+), 62 deletions(-)

diff --git a/packages/desktop/src/providers/focused_window/focused_window_provider.rs b/packages/desktop/src/providers/focused_window/focused_window_provider.rs
index d241b76d..035764b9 100644
--- a/packages/desktop/src/providers/focused_window/focused_window_provider.rs
+++ b/packages/desktop/src/providers/focused_window/focused_window_provider.rs
@@ -1,13 +1,9 @@
-use std::{ffi::c_void, io::Cursor, sync::OnceLock};
+use std::{ffi::c_void, io::Cursor};
 
 use async_trait::async_trait;
 use base64::{prelude::BASE64_STANDARD, Engine as _};
 use image::{ImageBuffer, ImageFormat, Rgba};
 use serde::{Deserialize, Serialize};
-use tokio::{
-  sync::mpsc::{self, Sender},
-  task,
-};
 use windows::{
   core::{Error, Interface, HSTRING, PWSTR},
   Win32::{
@@ -40,12 +36,11 @@ use windows::{
   },
 };
 
-use crate::providers::{Provider, ProviderOutput, ProviderResult};
-
-static PROVIDER_TX: OnceLock<mpsc::Sender<ProviderResult>> =
-  OnceLock::new();
+use crate::providers::{
+  CommonProviderState, Provider, ProviderEmitter, RuntimeType,
+};
 
-// This doesn't always work.. 
+// This doesn't always work..
 const ICON_SIZE: i32 = 32;
 
 #[derive(Deserialize, Debug)]
@@ -60,31 +55,15 @@ pub struct FocusedWindowOutput {
 }
 
 pub struct FocusedWindowProvider {
-  _config: FocusedWindowProviderConfig,
-}
-
-#[async_trait]
-impl Provider for FocusedWindowProvider {
-  async fn run(&self, emit_result_tx: Sender<ProviderResult>) {
-    PROVIDER_TX
-      .set(emit_result_tx.clone())
-      .expect("Error setting provider tx in focused window provider");
-
-    task::spawn_blocking(move || {
-      if let Err(err) = Self::create_focused_window_hook() {
-        emit_result_tx
-          .blocking_send(Err(err).into())
-          .expect("Error with focused window provider");
-      }
-    });
-  }
+  common: CommonProviderState,
 }
 
 impl FocusedWindowProvider {
   pub fn new(
-    config: FocusedWindowProviderConfig,
+    _config: FocusedWindowProviderConfig,
+    common: CommonProviderState,
   ) -> FocusedWindowProvider {
-    FocusedWindowProvider { _config: config }
+    FocusedWindowProvider { common }
   }
 
   unsafe fn get_foreground_window_icon(
@@ -287,30 +266,34 @@ impl FocusedWindowProvider {
     Some(BASE64_STANDARD.encode(&png_data))
   }
 
-  unsafe fn emit_window_info(hwnd: HWND) {
+  unsafe fn emit_window_info(hwnd: HWND, emitter: &ProviderEmitter) {
+    println!("Emitting window info");
     if !IsWindow(hwnd).as_bool() {
       return;
     }
+    let output = Self::focused_window_output(hwnd);
+    emitter.emit_output(output);
+  }
 
+  unsafe fn focused_window_output(
+    hwnd: HWND,
+  ) -> anyhow::Result<FocusedWindowOutput> {
     if let Some(title) = Self::get_foreground_window_title(hwnd) {
       if title.trim().is_empty() {
-        return;
+        return Err(anyhow::Error::msg("Empty title"));
       }
 
       if let Ok(icon) = Self::get_foreground_window_icon(hwnd) {
-        if let Some(tx) = PROVIDER_TX.get() {
-          let output = FocusedWindowOutput { title, icon };
-          if let Err(err) = tx.blocking_send(
-            Ok(ProviderOutput::FocusedWindow(output)).into(),
-          ) {
-            println!("Error sending result: {:?}", err);
-          }
-        }
+        Ok(FocusedWindowOutput { title, icon })
+      } else {
+        Err(anyhow::Error::msg("Failed to extract icon"))
       }
+    } else {
+      Err(anyhow::Error::msg("Failed to get window title"))
     }
   }
 
-  fn create_focused_window_hook() -> anyhow::Result<()> {
+  fn create_focused_window_hook(&self) -> anyhow::Result<()> {
     unsafe {
       let focus_hook = SetWinEventHook(
         EVENT_SYSTEM_FOREGROUND,
@@ -352,21 +335,15 @@ impl FocusedWindowProvider {
         WINEVENT_OUTOFCONTEXT,
       );
 
-      if focus_hook.is_invalid()
-        || title_hook.is_invalid()
-        || create_hook.is_invalid()
-        || switch_hook.is_invalid()
-      {
+      if focus_hook.is_invalid() || title_hook.is_invalid() {
         return Err(anyhow::Error::msg("Failed to set event hooks"));
       }
 
       // Initialize with current foreground window
       if let Some(current_hwnd) = Some(GetForegroundWindow()) {
-        Self::emit_window_info(current_hwnd);
+        Self::emit_window_info(current_hwnd, &self.common.emitter);
       }
 
-      println!("Monitoring window focus, creation, and title changes...");
-
       let mut msg = MSG::default();
       while GetMessageW(&mut msg, HWND::default(), 0, 0).as_bool() {
         let _ = TranslateMessage(&msg);
@@ -390,26 +367,25 @@ unsafe extern "system" fn win_event_proc(
   if !IsWindow(hwnd).as_bool() {
     return;
   }
-
   match event {
     EVENT_SYSTEM_FOREGROUND => {
-      FocusedWindowProvider::emit_window_info(hwnd);
+      FocusedWindowProvider::emit_window_info(hwnd, emitter);
     }
     EVENT_OBJECT_CREATE => {
       let foreground = GetForegroundWindow();
       if hwnd == foreground {
-        FocusedWindowProvider::emit_window_info(hwnd);
+        FocusedWindowProvider::emit_window_info(hwnd, emitter);
       }
     }
     EVENT_OBJECT_NAMECHANGE => {
       if id_object == 0 && hwnd == GetForegroundWindow() {
-        FocusedWindowProvider::emit_window_info(hwnd);
+        FocusedWindowProvider::emit_window_info(hwnd, emitter);
       }
     }
     EVENT_SYSTEM_SWITCHSTART => {
-      let foreground = GetForegroundWindow();
-      if !foreground.is_invalid() {
-        FocusedWindowProvider::emit_window_info(foreground);
+      let hwnd = GetForegroundWindow();
+      if !hwnd.is_invalid() {
+        FocusedWindowProvider::emit_window_info(hwnd, emitter);
       }
     }
     _ => {}
@@ -448,3 +424,19 @@ unsafe extern "system" fn enum_child_proc(
   }
   BOOL(1)
 }
+
+#[async_trait]
+impl Provider for FocusedWindowProvider {
+  fn runtime_type(&self) -> RuntimeType {
+    RuntimeType::Sync
+  }
+
+  fn start_sync(&mut self) {
+    if let Err(err) = self.create_focused_window_hook() {
+      self
+        .common
+        .emitter
+        .emit_output::<FocusedWindowOutput>(Err(err));
+    }
+  }
+}
diff --git a/packages/desktop/src/providers/provider_manager.rs b/packages/desktop/src/providers/provider_manager.rs
index 67aa541d..e888f218 100644
--- a/packages/desktop/src/providers/provider_manager.rs
+++ b/packages/desktop/src/providers/provider_manager.rs
@@ -11,8 +11,9 @@ use tracing::info;
 
 #[cfg(windows)]
 use super::{
-  audio::AudioProvider, keyboard::KeyboardProvider,
-  komorebi::KomorebiProvider, media::MediaProvider,
+  audio::AudioProvider, focused_window::FocusedWindowProvider,
+  keyboard::KeyboardProvider, komorebi::KomorebiProvider,
+  media::MediaProvider,
 };
 use super::{
   battery::BatteryProvider, cpu::CpuProvider, disk::DiskProvider,
@@ -227,6 +228,10 @@ impl ProviderManager {
       ProviderConfig::Cpu(config) => {
         Box::new(CpuProvider::new(config, common))
       }
+      #[cfg(windows)]
+      ProviderConfig::FocusedWindow(config) => {
+        Box::new(FocusedWindowProvider::new(config, common))
+      }
       ProviderConfig::Host(config) => {
         Box::new(HostProvider::new(config, common))
       }
diff --git a/packages/desktop/src/providers/provider_output.rs b/packages/desktop/src/providers/provider_output.rs
index 6aeb3d02..da772c01 100644
--- a/packages/desktop/src/providers/provider_output.rs
+++ b/packages/desktop/src/providers/provider_output.rs
@@ -2,13 +2,12 @@ use serde::Serialize;
 
 #[cfg(windows)]
 use super::{
-  audio::AudioOutput, keyboard::KeyboardOutput, komorebi::KomorebiOutput,
-  media::MediaOutput,
+  audio::AudioOutput, focused_window::FocusedWindowOutput,
+  keyboard::KeyboardOutput, komorebi::KomorebiOutput, media::MediaOutput,
 };
 use super::{
   battery::BatteryOutput, cpu::CpuOutput, disk::DiskOutput,
- 
-  focused_window::FocusedWindowOutput, host::HostOutput, ip::IpOutput, memory::MemoryOutput,
+  host::HostOutput, ip::IpOutput, memory::MemoryOutput,
   network::NetworkOutput, weather::WeatherOutput,
 };
 
@@ -32,6 +31,7 @@ pub enum ProviderOutput {
   Audio(AudioOutput),
   Battery(BatteryOutput),
   Cpu(CpuOutput),
+  #[cfg(windows)]
   FocusedWindow(FocusedWindowOutput),
   Host(HostOutput),
   Ip(IpOutput),
@@ -61,6 +61,7 @@ impl_provider_output! {
 #[cfg(windows)]
 impl_provider_output! {
   Audio(AudioOutput),
+  FocusedWindow(FocusedWindowOutput),
   Komorebi(KomorebiOutput),
   Media(MediaOutput),
   Keyboard(KeyboardOutput)