Skip to main content

rdesktop_core/
app.rs

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