Skip to content

Commit 3556058

Browse files
authored
x11: Implement LinuxApplicationExt (#1867)
This allows interacting with the primary selection in the X11 backend. Signed-off-by: Uli Schlachter <psychon@znc.in>
1 parent b1bbbbb commit 3556058

File tree

4 files changed

+56
-9
lines changed

4 files changed

+56
-9
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ You can find its changes [documented below](#070---2021-01-01).
4848
- `has_focus` method on `WidgetPod` ([#1825] by [@ForLoveOfCats])
4949
- x11: Add support for getting and setting clipboard contents ([#1805], [#1851], and [#1866] by [@psychon])
5050
- Linux extension: primary_clipboard ([#1843] by [@Maan2003])
51+
- x11: Implement primary_clipboard ([#1867] by [@psychon])
5152

5253
### Changed
5354

@@ -752,6 +753,7 @@ Last release without a changelog :(
752753
[#1863]: https://github.com/linebender/druid/pull/1863
753754
[#1865]: https://github.com/linebender/druid/pull/1865
754755
[#1866]: https://github.com/linebender/druid/pull/1866
756+
[#1867]: https://github.com/linebender/druid/pull/1867
755757

756758
[Unreleased]: https://github.com/linebender/druid/compare/v0.7.0...master
757759
[0.7.0]: https://github.com/linebender/druid/compare/v0.6.0...v0.7.0

druid-shell/src/backend/x11/application.rs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ use super::window::Window;
7979
//
8080
// The name of the clipboard selection; used for implementing copy&paste
8181
//
82+
// PRIMARY
83+
//
84+
// The name of the primary selection; used for implementing "paste the currently selected text"
85+
//
8286
// TARGETS
8387
//
8488
// A target for getting the selection contents that answers with a list of supported targets
@@ -99,6 +103,7 @@ x11rb::atom_manager! {
99103
_NET_WM_WINDOW_TYPE_TOOLTIP,
100104
_NET_WM_WINDOW_TYPE_DIALOG,
101105
CLIPBOARD,
106+
PRIMARY,
102107
TARGETS,
103108
INCR,
104109
}
@@ -135,6 +140,8 @@ pub(crate) struct Application {
135140
pub(crate) cursors: Cursors,
136141
/// The clipboard implementation
137142
clipboard: Clipboard,
143+
/// The clipboard implementation for the primary selection
144+
primary: Clipboard,
138145
/// The default screen of the connected display.
139146
///
140147
/// The connected display may also have additional screens.
@@ -292,6 +299,15 @@ impl Application {
292299
Rc::clone(&connection),
293300
screen_num,
294301
Rc::clone(&atoms),
302+
atoms.CLIPBOARD,
303+
Rc::clone(&pending_events),
304+
Rc::clone(&timestamp),
305+
);
306+
let primary = Clipboard::new(
307+
Rc::clone(&connection),
308+
screen_num,
309+
Rc::clone(&atoms),
310+
atoms.PRIMARY,
295311
Rc::clone(&pending_events),
296312
Rc::clone(&timestamp),
297313
);
@@ -305,6 +321,7 @@ impl Application {
305321
idle_read,
306322
cursors,
307323
clipboard,
324+
primary,
308325
idle_write,
309326
present_opcode,
310327
root_visual_type,
@@ -592,17 +609,26 @@ impl Application {
592609
Event::SelectionClear(ev) => {
593610
self.clipboard
594611
.handle_clear(*ev)
595-
.context("SELECTION_CLEAR event handling")?;
612+
.context("SELECTION_CLEAR event handling for clipboard")?;
613+
self.primary
614+
.handle_clear(*ev)
615+
.context("SELECTION_CLEAR event handling for primary")?;
596616
}
597617
Event::SelectionRequest(ev) => {
598618
self.clipboard
599619
.handle_request(ev)
600-
.context("SELECTION_REQUEST event handling")?;
620+
.context("SELECTION_REQUEST event handling for clipboard")?;
621+
self.primary
622+
.handle_request(ev)
623+
.context("SELECTION_REQUEST event handling for primary")?;
601624
}
602625
Event::PropertyNotify(ev) => {
603626
self.clipboard
604627
.handle_property_notify(*ev)
605-
.context("PROPERTY_NOTIFY event handling")?;
628+
.context("PROPERTY_NOTIFY event handling for clipboard")?;
629+
self.primary
630+
.handle_property_notify(*ev)
631+
.context("PROPERTY_NOTIFY event handling for primary")?;
606632
}
607633
Event::Error(e) => {
608634
// TODO: if an error is caused by the present extension, disable it and fall back
@@ -767,6 +793,12 @@ impl Application {
767793
}
768794
}
769795

796+
impl crate::platform::linux::LinuxApplicationExt for crate::Application {
797+
fn primary_clipboard(&self) -> crate::Clipboard {
798+
self.backend_app.primary.clone().into()
799+
}
800+
}
801+
770802
/// Clears out our idle pipe; `idle_read` should be the reading end of a pipe that was opened with
771803
/// O_NONBLOCK.
772804
fn drain_idle_pipe(idle_read: RawFd) -> Result<(), Error> {

druid-shell/src/backend/x11/clipboard.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,15 @@ impl Clipboard {
5555
connection: Rc<XCBConnection>,
5656
screen_num: usize,
5757
atoms: Rc<AppAtoms>,
58+
selection_name: Atom,
5859
event_queue: Rc<RefCell<VecDeque<Event>>>,
5960
timestamp: Rc<Cell<Timestamp>>,
6061
) -> Self {
6162
Self(Rc::new(RefCell::new(ClipboardState::new(
6263
connection,
6364
screen_num,
6465
atoms,
66+
selection_name,
6567
event_queue,
6668
timestamp,
6769
))))
@@ -122,6 +124,7 @@ struct ClipboardState {
122124
connection: Rc<XCBConnection>,
123125
screen_num: usize,
124126
atoms: Rc<AppAtoms>,
127+
selection_name: Atom,
125128
event_queue: Rc<RefCell<VecDeque<Event>>>,
126129
timestamp: Rc<Cell<Timestamp>>,
127130
contents: Option<ClipboardContents>,
@@ -133,13 +136,15 @@ impl ClipboardState {
133136
connection: Rc<XCBConnection>,
134137
screen_num: usize,
135138
atoms: Rc<AppAtoms>,
139+
selection_name: Atom,
136140
event_queue: Rc<RefCell<VecDeque<Event>>>,
137141
timestamp: Rc<Cell<Timestamp>>,
138142
) -> Self {
139143
Self {
140144
connection,
141145
screen_num,
142146
atoms,
147+
selection_name,
143148
event_queue,
144149
timestamp,
145150
contents: None,
@@ -153,16 +158,16 @@ impl ClipboardState {
153158
// Create a window for selection ownership and save the necessary state
154159
let contents = ClipboardContents::new(conn, self.screen_num, formats)?;
155160

156-
// Become CLIPBOARD selection owner
161+
// Become selection owner of our selection
157162
conn.set_selection_owner(
158163
contents.owner_window,
159-
self.atoms.CLIPBOARD,
164+
self.selection_name,
160165
self.timestamp.get(),
161166
)?;
162167

163168
// Check if we really are the selection owner; this might e.g. fail if our timestamp is too
164169
// old and some other program became the selection owner with a newer timestamp
165-
let owner = conn.get_selection_owner(self.atoms.CLIPBOARD)?.reply()?;
170+
let owner = conn.get_selection_owner(self.selection_name)?.reply()?;
166171
if owner.owner == contents.owner_window {
167172
// We are the new selection owner! Remember our contents for later.
168173
debug!("put_formats(): became selection owner");
@@ -269,7 +274,7 @@ impl ClipboardState {
269274

270275
conn.convert_selection(
271276
window.window,
272-
self.atoms.CLIPBOARD,
277+
self.selection_name,
273278
format_atom,
274279
TRANSFER_ATOM,
275280
self.timestamp.get(),
@@ -351,6 +356,11 @@ impl ClipboardState {
351356
}
352357

353358
fn handle_clear(&mut self, event: SelectionClearEvent) -> Result<(), ConnectionError> {
359+
if event.selection != self.selection_name {
360+
// This event is meant for another Clipboard instance
361+
return Ok(());
362+
}
363+
354364
let window = self.contents.as_ref().map(|c| c.owner_window);
355365
if Some(event.owner) == window {
356366
// We lost ownership of the selection, clean up
@@ -362,6 +372,11 @@ impl ClipboardState {
362372
}
363373

364374
fn handle_request(&mut self, event: &SelectionRequestEvent) -> Result<(), ReplyOrIdError> {
375+
if event.selection != self.selection_name {
376+
// This request is meant for another Clipboard instance
377+
return Ok(());
378+
}
379+
365380
let conn = &*self.connection;
366381
let contents = match &self.contents {
367382
Some(contents) if contents.owner_window == event.owner => contents,

druid-shell/src/platform/linux.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,5 @@ mod test {
3232

3333
use super::*;
3434
use static_assertions as sa;
35-
// TODO(shell/x11): implement LinuxApplicationExt
36-
#[cfg(not(feature = "x11"))]
3735
sa::assert_impl_all!(Application: LinuxApplicationExt);
3836
}

0 commit comments

Comments
 (0)