Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide system clipboard handling API on Window #2156

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
- On X11, add mappings for numpad comma, numpad enter, numlock and pause.
- On macOS, fix Pinyin IME input by reverting a change that intended to improve IME.
- On Windows, fix a crash with transparent windows on Windows 11.
- Add `set_clipboard_content` and `request_clipboard_content` API on `Window`
- Add `set_primary_clipboard_content` and `request_primary_clipboard_content` API on `WindowExtUnix`

# 0.26.0 (2021-12-01)

Expand Down
1 change: 1 addition & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ Changes in the API that have been agreed upon but aren't implemented across all
|New API for HiDPI ([#315] [#319]) |✔️ |✔️ |✔️ |✔️ |▢[#721]|✔️ |❓ |
|Event Loop 2.0 ([#459]) |✔️ |✔️ |❌ |✔️ |❌ |✔️ |❓ |
|Keyboard Input ([#812]) |❌ |❌ |❌ |❌ |❌ |❌ |❓ |
|Clipboard handling |❌ |❌ |❌ |✔️ |❌ |❌ |❓ |

### Completed API Reworks
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |
Expand Down
26 changes: 26 additions & 0 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,9 @@ pub enum WindowEvent<'a> {
/// issue, and it should get fixed - but it's the current state of the API.
ModifiersChanged(ModifiersState),

/// Loaded clipboard content.
ClipboardContent(ClipboardContent),

/// The cursor has moved on the window.
CursorMoved {
device_id: DeviceId,
Expand Down Expand Up @@ -354,6 +357,27 @@ pub enum WindowEvent<'a> {
ThemeChanged(Theme),
}

/// Metadata which is passed along request to load clipboard.
pub type ClipboardMetadata = dyn std::any::Any + Send + Sync + 'static;

#[derive(Debug, Clone)]
pub struct ClipboardContent {
/// Clipboard data.
pub data: Vec<u8>,

/// Mime type which was picked to be loaded.
pub mime: String,

/// Metadata passed to `request_clipboard_content`.
pub metadata: Option<std::sync::Arc<ClipboardMetadata>>,
}
Comment on lines +363 to +373
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like there's no way to tell which clipboard request we're responding to. Is this supposed to be handled by the ClipboardMetadata in the consumer? I would have expected a serial.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. However we in alacritty need to apply function on certain loads. We could use u64 and decide what to do based on u64 value. But I've decided to go for Any and downcasting.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just feel like it would be useful to fix out-of-order clipboard replies, since they're asynchronous in nature. Either by filtering directly in winit or by making it obvious to the consumer that the replies are not in order?

Copy link
Member

@maroider maroider Jan 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My preference here would be for request_primary_clipboard_content to return a serial. Using Any + downcasting here doesn't feel like it's "pulling its weight" API-wise.


impl PartialEq for ClipboardContent {
fn eq(&self, other: &Self) -> bool {
self.data == other.data
}
}

impl Clone for WindowEvent<'static> {
fn clone(&self) -> Self {
use self::WindowEvent::*;
Expand All @@ -366,6 +390,7 @@ impl Clone for WindowEvent<'static> {
HoveredFile(file) => HoveredFile(file.clone()),
HoveredFileCancelled => HoveredFileCancelled,
ReceivedCharacter(c) => ReceivedCharacter(*c),
ClipboardContent(content) => ClipboardContent(content.clone()),
Focused(f) => Focused(*f),
KeyboardInput {
device_id,
Expand Down Expand Up @@ -456,6 +481,7 @@ impl<'a> WindowEvent<'a> {
DroppedFile(file) => Some(DroppedFile(file)),
HoveredFile(file) => Some(HoveredFile(file)),
HoveredFileCancelled => Some(HoveredFileCancelled),
ClipboardContent(content) => Some(ClipboardContent(content)),
ReceivedCharacter(c) => Some(ReceivedCharacter(c)),
Focused(focused) => Some(Focused(focused)),
KeyboardInput {
Expand Down
39 changes: 39 additions & 0 deletions src/platform/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
target_os = "openbsd"
))]

use std::collections::HashSet;
use std::os::raw;
#[cfg(feature = "x11")]
use std::{ptr, sync::Arc};

use crate::{
event::ClipboardMetadata,
event_loop::{EventLoop, EventLoopWindowTarget},
monitor::MonitorHandle,
window::{Window, WindowBuilder},
Expand Down Expand Up @@ -192,6 +194,18 @@ impl<T> EventLoopExtUnix for EventLoop<T> {

/// Additional methods on `Window` that are specific to Unix.
pub trait WindowExtUnix {
fn request_primary_clipboard_content(
&self,
mimes: HashSet<String>,
metadata: Option<std::sync::Arc<ClipboardMetadata>>,
);

fn set_primary_clipboard_content<C: AsRef<[u8]> + 'static>(
&self,
content: C,
mimes: HashSet<String>,
);

/// Returns the ID of the `Window` xlib object that is used by this window.
///
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
Expand Down Expand Up @@ -248,6 +262,31 @@ pub trait WindowExtUnix {
}

impl WindowExtUnix for Window {
/// Request primary system clipboard content.
///
/// This clipboard is usually present on middle mouse button.
///
/// The result of loading content with provided `mimes` mime types is delivered via
/// [`crate::event::WindowEvent::ClipboardContent`] with the given `metadata`.
fn request_primary_clipboard_content(
&self,
mimes: HashSet<String>,
metadata: Option<std::sync::Arc<ClipboardMetadata>>,
) {
self.window
.request_primary_clipboard_content(mimes, metadata);
}

/// Set primary system clipboard content to provided `content` and advertising it with given
/// `mimes` mime types.
fn set_primary_clipboard_content<C: AsRef<[u8]> + 'static>(
&self,
content: C,
mimes: HashSet<String>,
) {
self.window.set_primary_clipboard_content(content, mimes);
}

#[inline]
#[cfg(feature = "x11")]
fn xlib_window(&self) -> Option<raw::c_ulong> {
Expand Down
41 changes: 39 additions & 2 deletions src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
#[cfg(all(not(feature = "x11"), not(feature = "wayland")))]
compile_error!("Please select a feature to build for unix: `x11`, `wayland`");

use std::collections::{HashSet, VecDeque};
#[cfg(feature = "wayland")]
use std::error::Error;
use std::{collections::VecDeque, env, fmt};
use std::{env, fmt};
#[cfg(feature = "x11")]
use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Arc};

Expand All @@ -26,7 +27,7 @@ use self::x11::{ffi::XVisualInfo, util::WindowType as XWindowType, XConnection,
use crate::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
event::Event,
event::{ClipboardMetadata, Event},
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
icon::Icon,
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
Expand Down Expand Up @@ -451,6 +452,42 @@ impl Window {
x11_or_wayland!(match self; Window(w) => w.request_redraw())
}

#[inline]
pub fn request_clipboard_content(
&self,
mime: HashSet<String>,
metadata: Option<std::sync::Arc<ClipboardMetadata>>,
) {
x11_or_wayland!(match self; Window(w) => w.request_clipboard_content(mime, metadata))
}

#[inline]
pub fn set_clipboard_content<C: AsRef<[u8]> + 'static>(
&self,
content: C,
mimes: HashSet<String>,
) {
x11_or_wayland!(match self; Window(w) => w.set_clipboard_content(content, mimes))
}

#[inline]
pub fn request_primary_clipboard_content(
&self,
mimes: HashSet<String>,
metadata: Option<std::sync::Arc<ClipboardMetadata>>,
) {
x11_or_wayland!(match self; Window(w) => w.request_primary_clipboard_content(mimes, metadata))
}

#[inline]
pub fn set_primary_clipboard_content<C: AsRef<[u8]> + 'static>(
&self,
content: C,
mimes: HashSet<String>,
) {
x11_or_wayland!(match self; Window(w) => w.set_primary_clipboard_content(content, mimes))
}

#[inline]
pub fn current_monitor(&self) -> Option<RootMonitorHandle> {
match self {
Expand Down
Loading