Skip to main content

rdesktop_core/
app.rs

1use crate::config::{AppConfig, WindowConfig};
2use crate::ipc::IpcHandler;
3use crate::renderer::Renderer;
4
5type SetupFn = Box<dyn FnOnce(&mut dyn Renderer) -> crate::Result<()>>;
6
7/// Content to load in the main window.
8pub enum WindowContent {
9    /// Load a URL.
10    Url(String),
11    /// Load HTML directly.
12    Html(String),
13}
14
15/// Builder for creating an rdesktop application.
16pub struct AppBuilder {
17    config: AppConfig,
18    renderer: Option<Box<dyn Renderer>>,
19    ipc_handler: Option<Box<dyn IpcHandler>>,
20    content: Option<WindowContent>,
21    setup_fn: Option<SetupFn>,
22}
23
24impl AppBuilder {
25    /// Create a new AppBuilder with the given configuration.
26    pub fn new(config: AppConfig) -> Self {
27        Self {
28            config,
29            renderer: None,
30            ipc_handler: None,
31            content: None,
32            setup_fn: None,
33        }
34    }
35
36    /// Set the renderer to use.
37    pub fn with_renderer(mut self, renderer: Box<dyn Renderer>) -> Self {
38        self.renderer = Some(renderer);
39        self
40    }
41
42    /// Set the IPC handler for frontend-to-backend communication.
43    pub fn with_ipc_handler(mut self, handler: Box<dyn IpcHandler>) -> Self {
44        self.ipc_handler = Some(handler);
45        self
46    }
47
48    /// Set the URL to load in the main window.
49    pub fn with_url(mut self, url: impl Into<String>) -> Self {
50        self.content = Some(WindowContent::Url(url.into()));
51        self
52    }
53
54    /// Set the HTML to load in the main window.
55    pub fn with_html(mut self, html: impl Into<String>) -> Self {
56        self.content = Some(WindowContent::Html(html.into()));
57        self
58    }
59
60    /// Set a setup function that runs after renderer initialization.
61    pub fn with_setup<F>(mut self, setup: F) -> Self
62    where
63        F: FnOnce(&mut dyn Renderer) -> crate::Result<()> + 'static,
64    {
65        self.setup_fn = Some(Box::new(setup));
66        self
67    }
68
69    /// Build the application.
70    pub fn build(self) -> crate::Result<App> {
71        let renderer = self.renderer.ok_or_else(|| {
72            crate::RdesktopError::RendererInit(
73                "No renderer provided. Use with_renderer().".to_string(),
74            )
75        })?;
76
77        Ok(App {
78            config: self.config,
79            renderer,
80            ipc_handler: self.ipc_handler,
81            content: self.content,
82            setup_fn: self.setup_fn,
83        })
84    }
85}
86
87/// The main application struct.
88pub struct App {
89    config: AppConfig,
90    renderer: Box<dyn Renderer>,
91    ipc_handler: Option<Box<dyn IpcHandler>>,
92    content: Option<WindowContent>,
93    setup_fn: Option<SetupFn>,
94}
95
96impl App {
97    /// Create a new AppBuilder.
98    pub fn builder(config: AppConfig) -> AppBuilder {
99        AppBuilder::new(config)
100    }
101
102    /// Run the application.
103    pub fn run(mut self) -> crate::Result<()> {
104        // Initialize the renderer
105        self.renderer.init()?;
106
107        // Set IPC handler if provided
108        if let Some(handler) = self.ipc_handler {
109            self.renderer.set_ipc_handler(handler);
110        }
111
112        // Create the main window
113        let window_config = WindowConfig {
114            title: self.config.name.clone(),
115            ..self.config.window.clone()
116        };
117        let handle = self.renderer.create_window(&window_config)?;
118
119        // Load content
120        match self.content {
121            Some(WindowContent::Url(url)) => {
122                self.renderer.load_url(handle, &url)?;
123            }
124            Some(WindowContent::Html(html)) => {
125                self.renderer.load_html(handle, &html)?;
126            }
127            None => {
128                // Load about:blank by default
129                self.renderer.load_url(handle, "about:blank")?;
130            }
131        }
132
133        // Run setup function if provided
134        if let Some(setup) = self.setup_fn {
135            setup(self.renderer.as_mut())?;
136        }
137
138        // Run the event loop (blocks until all windows are closed)
139        self.renderer.run()
140    }
141}