@@ -10,7 +10,7 @@ use garfield::ui::{AddressBar, AppPickerDialog, AppPickerResult, Breadcrumb, Con |
| 10 | 10 | use anyhow::Result; |
| 11 | 11 | use gartk_core::{InputEvent, Key, MouseButton, Point, Rect, Theme}; |
| 12 | 12 | use gartk_render::{Renderer, TextStyle}; |
| 13 | | -use gartk_x11::{Connection, EventLoop, EventLoopConfig, Window, WindowConfig}; |
| 13 | +use gartk_x11::{ClipboardManager, Connection, EventLoop, EventLoopConfig, Window, WindowConfig}; |
| 14 | 14 | use std::path::PathBuf; |
| 15 | 15 | use std::time::Instant; |
| 16 | 16 | use x11rb::protocol::xproto::{ConnectionExt, ImageFormat}; |
@@ -98,6 +98,8 @@ pub struct App { |
| 98 | 98 | preview_loader: PreviewLoader, |
| 99 | 99 | /// Async image preview loader. |
| 100 | 100 | image_preview_loader: ImagePreviewLoader, |
| 101 | + /// X11 clipboard manager for system clipboard integration. |
| 102 | + x11_clipboard: ClipboardManager, |
| 101 | 103 | } |
| 102 | 104 | |
| 103 | 105 | /// State for a paste operation with conflicts. |
@@ -140,6 +142,9 @@ impl App { |
| 140 | 142 | |
| 141 | 143 | window.focus()?; |
| 142 | 144 | |
| 145 | + // Create X11 clipboard manager for system clipboard integration |
| 146 | + let x11_clipboard = ClipboardManager::new(conn.clone(), window.id())?; |
| 147 | + |
| 143 | 148 | // Create graphics context for blitting |
| 144 | 149 | let gc = conn.generate_id()?; |
| 145 | 150 | conn.inner().create_gc(gc, window.id(), &Default::default())?; |
@@ -274,6 +279,7 @@ impl App { |
| 274 | 279 | pending_paste: None, |
| 275 | 280 | preview_loader: PreviewLoader::new(), |
| 276 | 281 | image_preview_loader: ImagePreviewLoader::new(), |
| 282 | + x11_clipboard, |
| 277 | 283 | }; |
| 278 | 284 | |
| 279 | 285 | app.update_status_bar(); |
@@ -349,6 +355,18 @@ impl App { |
| 349 | 355 | ev.request_redraw(); |
| 350 | 356 | } |
| 351 | 357 | } |
| 358 | + InputEvent::SelectionRequest(req) => { |
| 359 | + // Another application is requesting our clipboard data |
| 360 | + tracing::debug!("Received SelectionRequest event"); |
| 361 | + if let Err(e) = self.x11_clipboard.handle_selection_request(&req) { |
| 362 | + tracing::warn!("Failed to handle selection request: {}", e); |
| 363 | + } |
| 364 | + } |
| 365 | + InputEvent::SelectionClear => { |
| 366 | + // We lost clipboard ownership to another application |
| 367 | + tracing::debug!("Received SelectionClear event"); |
| 368 | + self.x11_clipboard.handle_selection_clear(); |
| 369 | + } |
| 352 | 370 | _ => {} |
| 353 | 371 | } |
| 354 | 372 | |
@@ -1493,7 +1511,15 @@ impl App { |
| 1493 | 1511 | let paths = self.get_selected_paths(); |
| 1494 | 1512 | if !paths.is_empty() { |
| 1495 | 1513 | let count = paths.len(); |
| 1496 | | - self.clipboard.copy(paths); |
| 1514 | + |
| 1515 | + // Update internal clipboard |
| 1516 | + self.clipboard.copy(paths.clone()); |
| 1517 | + |
| 1518 | + // Update X11 system clipboard so other apps can paste |
| 1519 | + if let Err(e) = self.x11_clipboard.set_files(&paths, false) { |
| 1520 | + tracing::warn!("Failed to set X11 clipboard: {}", e); |
| 1521 | + } |
| 1522 | + |
| 1497 | 1523 | let msg = if count == 1 { "1 item copied".to_string() } else { format!("{} items copied", count) }; |
| 1498 | 1524 | self.status_bar.set_status_message(msg); |
| 1499 | 1525 | self.update_status_bar(); |
@@ -1505,7 +1531,15 @@ impl App { |
| 1505 | 1531 | let paths = self.get_selected_paths(); |
| 1506 | 1532 | if !paths.is_empty() { |
| 1507 | 1533 | let count = paths.len(); |
| 1508 | | - self.clipboard.cut(paths); |
| 1534 | + |
| 1535 | + // Update internal clipboard |
| 1536 | + self.clipboard.cut(paths.clone()); |
| 1537 | + |
| 1538 | + // Update X11 system clipboard with cut flag so other apps know to move |
| 1539 | + if let Err(e) = self.x11_clipboard.set_files(&paths, true) { |
| 1540 | + tracing::warn!("Failed to set X11 clipboard: {}", e); |
| 1541 | + } |
| 1542 | + |
| 1509 | 1543 | let msg = if count == 1 { "1 item cut".to_string() } else { format!("{} items cut", count) }; |
| 1510 | 1544 | self.status_bar.set_status_message(msg); |
| 1511 | 1545 | self.update_status_bar(); |