Skip to content

Commit 646efe8

Browse files
Merge branch 'master' into desktop-flatpak-wip
2 parents bea86f6 + bd1f7ee commit 646efe8

14 files changed

Lines changed: 322 additions & 148 deletions

File tree

desktop/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ windows = { version = "0.58.0", features = [
5555
"Win32_Graphics_Gdi",
5656
"Win32_System_LibraryLoader",
5757
"Win32_System_Com",
58+
"Win32_System_Console",
5859
"Win32_UI_Controls",
5960
"Win32_UI_WindowsAndMessaging",
6061
"Win32_UI_HiDpi",

desktop/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,14 @@ pub fn start() {
106106

107107
// Explicitly drop the instance lock
108108
drop(lock);
109+
110+
// Workaround for a Windows-specific exception that occurs when `app` is dropped.
111+
// The issue causes the window to hang for a few seconds before closing.
112+
// Appears to be related to CEF object destruction order.
113+
// Calling `exit` bypasses rust teardown and lets Windows perform process cleanup.
114+
// TODO: Identify and fix the underlying CEF shutdown issue so this workaround can be removed.
115+
#[cfg(target_os = "windows")]
116+
exit(0);
109117
}
110118

111119
pub fn start_helper() {

desktop/src/window.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,13 @@ pub(crate) struct Window {
4141
#[allow(dead_code)]
4242
native_handle: native::NativeWindowImpl,
4343
custom_cursors: HashMap<CustomCursorSource, CustomCursor>,
44-
clipboard: window_clipboard::Clipboard,
44+
clipboard: Option<window_clipboard::Clipboard>,
45+
}
46+
impl Drop for Window {
47+
fn drop(&mut self) {
48+
// Clipboard must be dropped before `winit_window`
49+
drop(self.clipboard.take());
50+
}
4551
}
4652

4753
impl Window {
@@ -62,7 +68,7 @@ impl Window {
6268

6369
let winit_window = event_loop.create_window(attributes).unwrap();
6470
let native_handle = native::NativeWindowImpl::new(winit_window.as_ref(), app_event_scheduler);
65-
let clipboard = unsafe { window_clipboard::Clipboard::connect(&winit_window) }.expect("failed to create clipboard");
71+
let clipboard = unsafe { window_clipboard::Clipboard::connect(&winit_window) }.ok();
6672
Self {
6773
winit_window: winit_window.into(),
6874
native_handle,
@@ -158,7 +164,11 @@ impl Window {
158164
}
159165

160166
pub(crate) fn clipboard_read(&self) -> Option<String> {
161-
match self.clipboard.read() {
167+
let Some(clipboard) = &self.clipboard else {
168+
tracing::error!("Clipboard not available");
169+
return None;
170+
};
171+
match clipboard.read() {
162172
Ok(data) => Some(data),
163173
Err(e) => {
164174
tracing::error!("Failed to read from clipboard: {e}");
@@ -168,7 +178,11 @@ impl Window {
168178
}
169179

170180
pub(crate) fn clipboard_write(&mut self, data: String) {
171-
if let Err(e) = self.clipboard.write(data) {
181+
let Some(clipboard) = &mut self.clipboard else {
182+
tracing::error!("Clipboard not available");
183+
return;
184+
};
185+
if let Err(e) = clipboard.write(data) {
172186
tracing::error!("Failed to write to clipboard: {e}")
173187
}
174188
}

desktop/src/window/win.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use windows::Win32::System::Com::{COINIT_APARTMENTTHREADED, CoInitializeEx};
2+
use windows::Win32::System::Console::{ATTACH_PARENT_PROCESS, AttachConsole};
23
use windows::Win32::UI::Shell::SetCurrentProcessExplicitAppUserModelID;
34
use windows::core::HSTRING;
45
use winit::event_loop::ActiveEventLoop;
@@ -13,6 +14,12 @@ pub(super) struct NativeWindowImpl {
1314

1415
impl super::NativeWindow for NativeWindowImpl {
1516
fn init() {
17+
// Attach to parent console if launched from a terminal (no-op otherwise)
18+
unsafe {
19+
let _ = AttachConsole(ATTACH_PARENT_PROCESS);
20+
}
21+
22+
// Set stable app ID
1623
let app_id = HSTRING::from(APP_ID);
1724
unsafe {
1825
let _ = CoInitializeEx(None, COINIT_APARTMENTTHREADED).ok();

desktop/src/window/win/native_handle.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ impl NativeWindowHandle {
6161
0,
6262
0,
6363
0,
64-
None,
64+
main,
6565
None,
6666
HINSTANCE(std::ptr::null_mut()),
6767
// Pass the main window's HWND to WM_NCCREATE so the helper can store it.
@@ -118,7 +118,7 @@ impl NativeWindowHandle {
118118
}
119119

120120
// Force window update
121-
let _ = unsafe { SetWindowPos(main, None, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER) };
121+
let _ = unsafe { SetWindowPos(main, None, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE) };
122122

123123
native_handle
124124
}
@@ -325,20 +325,20 @@ unsafe fn position_helper(main: HWND, helper: HWND) {
325325
let w = (r.right - r.left) + RESIZE_BAND_THICKNESS * 2;
326326
let h = (r.bottom - r.top) + RESIZE_BAND_THICKNESS * 2;
327327

328-
let _ = unsafe { SetWindowPos(helper, main, x, y, w, h, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING) };
328+
let _ = unsafe { SetWindowPos(helper, main, x, y, w, h, SWP_NOACTIVATE | SWP_NOSENDCHANGING) };
329329
}
330330

331331
unsafe fn calculate_hit(helper: HWND, lparam: LPARAM) -> u32 {
332-
let x = (lparam.0 & 0xFFFF) as i16 as u32;
333-
let y = ((lparam.0 >> 16) & 0xFFFF) as i16 as u32;
332+
let x = (lparam.0 & 0xFFFF) as i16 as i32;
333+
let y = ((lparam.0 >> 16) & 0xFFFF) as i16 as i32;
334334

335335
let mut r = RECT::default();
336336
let _ = unsafe { GetWindowRect(helper, &mut r) };
337337

338-
let on_top = y < (r.top + RESIZE_BAND_THICKNESS) as u32;
339-
let on_right = x >= (r.right - RESIZE_BAND_THICKNESS) as u32;
340-
let on_bottom = y >= (r.bottom - RESIZE_BAND_THICKNESS) as u32;
341-
let on_left = x < (r.left + RESIZE_BAND_THICKNESS) as u32;
338+
let on_top = y < (r.top + RESIZE_BAND_THICKNESS) as i32;
339+
let on_right = x >= (r.right - RESIZE_BAND_THICKNESS) as i32;
340+
let on_bottom = y >= (r.bottom - RESIZE_BAND_THICKNESS) as i32;
341+
let on_left = x < (r.left + RESIZE_BAND_THICKNESS) as i32;
342342

343343
match (on_top, on_right, on_bottom, on_left) {
344344
(true, _, _, true) => HTTOPLEFT,

node-graph/libraries/no-std-types/src/color/color_types.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ impl Pixel for Luma {}
216216
/// The other components (RGB) are stored as `f32` that range from `0.0` up to `f32::MAX`,
217217
/// the values encode the brightness of each channel proportional to the light intensity in cd/m² (nits) in HDR, and `0.0` (black) to `1.0` (white) in SDR color.
218218
#[repr(C)]
219-
#[derive(Debug, Default, Clone, Copy, PartialEq, Pod, Zeroable, BufferStruct)]
219+
#[derive(Debug, Default, Clone, Copy, Pod, Zeroable, BufferStruct)]
220220
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
221221
pub struct Color {
222222
red: f32,
@@ -225,6 +225,14 @@ pub struct Color {
225225
alpha: f32,
226226
}
227227

228+
impl PartialEq for Color {
229+
fn eq(&self, other: &Self) -> bool {
230+
self.red == other.red && self.green == other.green && self.blue == other.blue && self.alpha == other.alpha
231+
}
232+
}
233+
234+
impl Eq for Color {}
235+
228236
#[allow(clippy::derived_hash_with_manual_eq)]
229237
impl Hash for Color {
230238
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {

node-graph/libraries/raster-types/src/image.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ mod base64_serde {
3939
}
4040
}
4141

42-
#[derive(Clone, PartialEq, Default, specta::Type, serde::Serialize, serde::Deserialize)]
42+
#[derive(Clone, Eq, Default, specta::Type, serde::Serialize, serde::Deserialize)]
4343
pub struct Image<P: Pixel> {
4444
pub width: u32,
4545
pub height: u32,
@@ -53,6 +53,12 @@ pub struct Image<P: Pixel> {
5353
// TODO: Currently it is always anchored at the top left corner at (0, 0). The bottom right corner of the new origin field would correspond to (1, 1).
5454
}
5555

56+
impl<P: Pixel + PartialEq> PartialEq for Image<P> {
57+
fn eq(&self, other: &Self) -> bool {
58+
self.width == other.width && self.height == other.height && self.data == other.data
59+
}
60+
}
61+
5662
#[derive(Debug, Clone, dyn_any::DynAny, Default, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
5763
pub struct TransformImage(pub DAffine2);
5864

node-graph/libraries/rendering/src/renderer.rs

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use kurbo::Shape;
2626
use num_traits::Zero;
2727
use std::collections::{HashMap, HashSet};
2828
use std::fmt::Write;
29-
use std::hash::{DefaultHasher, Hash, Hasher};
29+
use std::hash::{Hash, Hasher};
3030
use std::ops::Deref;
3131
use std::sync::{Arc, LazyLock};
3232
use vello::*;
@@ -59,7 +59,7 @@ pub struct SvgRender {
5959
pub svg: Vec<SvgSegment>,
6060
pub svg_defs: String,
6161
pub transform: DAffine2,
62-
pub image_data: Vec<(u64, Image<Color>)>,
62+
pub image_data: HashMap<Image<Color>, u64>,
6363
indent: usize,
6464
}
6565

@@ -69,7 +69,7 @@ impl SvgRender {
6969
svg: Vec::default(),
7070
svg_defs: String::new(),
7171
transform: DAffine2::IDENTITY,
72-
image_data: Vec::new(),
72+
image_data: HashMap::new(),
7373
indent: 0,
7474
}
7575
}
@@ -1239,23 +1239,18 @@ impl Render for Table<Raster<CPU>> {
12391239
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
12401240
for row in self.iter() {
12411241
let image = row.element;
1242+
12421243
let transform = *row.transform;
12431244

12441245
if image.data.is_empty() {
12451246
continue;
12461247
}
12471248

12481249
if render_params.to_canvas() {
1249-
let id = row.source_node_id.map(|x| x.0).unwrap_or_else(|| {
1250-
let mut state = DefaultHasher::new();
1251-
image.data().hash(&mut state);
1252-
state.finish()
1253-
});
1254-
if !render.image_data.iter().any(|(old_id, _)| *old_id == id) {
1255-
let mut image = image.data().clone();
1256-
image.map_pixels(|p| p.to_unassociated_alpha());
1257-
render.image_data.push((id, image));
1258-
}
1250+
let mut image_copy = image.clone();
1251+
image_copy.data_mut().map_pixels(|p| p.to_unassociated_alpha());
1252+
let id = *render.image_data.entry(image_copy.into_data()).or_insert_with(generate_uuid);
1253+
12591254
render.parent_tag(
12601255
"foreignObject",
12611256
|attributes| {

0 commit comments

Comments
 (0)