mirror of https://github.com/wayvr-org/wayvr.git
whisper working
This commit is contained in:
parent
dd9838f5ff
commit
6dd624e046
|
|
@ -6608,6 +6608,8 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ash",
|
"ash",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"calloop 0.14.4",
|
||||||
|
"calloop-wayland-source 0.4.1",
|
||||||
"chrono",
|
"chrono",
|
||||||
"chrono-tz",
|
"chrono-tz",
|
||||||
"clap",
|
"clap",
|
||||||
|
|
@ -6655,6 +6657,7 @@ dependencies = [
|
||||||
"vulkano",
|
"vulkano",
|
||||||
"vulkano-shaders",
|
"vulkano-shaders",
|
||||||
"wayland-client",
|
"wayland-client",
|
||||||
|
"wayland-protocols",
|
||||||
"wayland-protocols-misc",
|
"wayland-protocols-misc",
|
||||||
"wayvr-ipc",
|
"wayvr-ipc",
|
||||||
"wgui",
|
"wgui",
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,8 @@ vulkano = { version = "0.35.2", default-features = false, features = [
|
||||||
"macros",
|
"macros",
|
||||||
] }
|
] }
|
||||||
vulkano-shaders = "0.35.0"
|
vulkano-shaders = "0.35.0"
|
||||||
wayland-client = { version = "0.31.11" }
|
wayland-client = { version = "0.31" }
|
||||||
|
wayland-protocols = { version = "0.32" }
|
||||||
wayland-protocols-misc = { version = "0.3.12" }
|
wayland-protocols-misc = { version = "0.3.12" }
|
||||||
xdg = "3.0.0"
|
xdg = "3.0.0"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,7 @@ tracing = "0.1.43"
|
||||||
tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
wayland-client = { workspace = true }
|
wayland-client = { workspace = true }
|
||||||
|
wayland-protocols = { workspace = true }
|
||||||
wayland-protocols-misc = { workspace = true }
|
wayland-protocols-misc = { workspace = true }
|
||||||
winit = { version = "0.30.12", optional = true }
|
winit = { version = "0.30.12", optional = true }
|
||||||
xcb = { version = "1.6.0", features = [
|
xcb = { version = "1.6.0", features = [
|
||||||
|
|
@ -95,7 +96,11 @@ xkbcommon = { version = "0.8.0" } # 0.9.0 breaks keymap import on some distros
|
||||||
whisper-rs = { version = "0.16.0", features = ["vulkan"], optional = true }
|
whisper-rs = { version = "0.16.0", features = ["vulkan"], optional = true }
|
||||||
smithay-clipboard = "0.7.3"
|
smithay-clipboard = "0.7.3"
|
||||||
|
|
||||||
|
calloop = { version = "0.14", optional = true }
|
||||||
|
calloop-wayland-source = { version = "0.4", optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|
||||||
regex.workspace = true
|
regex.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
@ -108,6 +113,6 @@ osc = ["dep:rosc"]
|
||||||
pipewire = ["wlx-capture/pipewire"]
|
pipewire = ["wlx-capture/pipewire"]
|
||||||
uidev = ["dep:winit"]
|
uidev = ["dep:winit"]
|
||||||
wayland = ["pipewire", "wlx-capture/wlr", "xkbcommon/wayland"]
|
wayland = ["pipewire", "wlx-capture/wlr", "xkbcommon/wayland"]
|
||||||
whisper = ["dep:pipewire", "dep:whisper-rs"]
|
whisper = ["dep:pipewire", "dep:whisper-rs", "dep:calloop", "dep:calloop-wayland-source"]
|
||||||
x11 = ["dep:xcb", "wlx-capture/xshm", "xkbcommon/x11"]
|
x11 = ["dep:xcb", "wlx-capture/xshm", "xkbcommon/x11"]
|
||||||
xcb = ["dep:xcb"]
|
xcb = ["dep:xcb"]
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{path::Path, rc::Rc, time::Duration};
|
use std::{rc::Rc, time::Duration};
|
||||||
|
|
||||||
use glam::{Affine3A, Quat, Vec3, vec3};
|
use glam::{Affine3A, Quat, Vec3, vec3};
|
||||||
use wgui::{
|
use wgui::{
|
||||||
|
|
@ -27,7 +27,7 @@ use crate::{
|
||||||
overlays::toast::Toast,
|
overlays::toast::Toast,
|
||||||
state::AppState,
|
state::AppState,
|
||||||
subsystem::{
|
subsystem::{
|
||||||
clipboard::{ClipboardProvider, wl::WlClipboardProvider, x11::X11ClipboardProvider},
|
clipboard::{self, ClipboardProvider},
|
||||||
hid::VirtualKey,
|
hid::VirtualKey,
|
||||||
input::KeyboardFocus,
|
input::KeyboardFocus,
|
||||||
whisper_stt::WhisperStt,
|
whisper_stt::WhisperStt,
|
||||||
|
|
@ -76,20 +76,22 @@ pub fn create_whisper(
|
||||||
headless: bool,
|
headless: bool,
|
||||||
wayland: bool,
|
wayland: bool,
|
||||||
) -> anyhow::Result<OverlayWindowConfig> {
|
) -> anyhow::Result<OverlayWindowConfig> {
|
||||||
// let clipboard_provider: Option<Box<dyn ClipboardProvider>> = match (headless, wayland) {
|
let clipboard_provider: Option<Box<dyn ClipboardProvider>> = match (headless, wayland) {
|
||||||
// (true, _) => None,
|
#[cfg(feature = "wayland")]
|
||||||
// (false, true) => WlClipboardProvider::new()
|
(false, true) => clipboard::wl::Provider::new()
|
||||||
// .log_err("Could not create Wayland clipboard provider")
|
.log_err("Could not create Wayland clipboard provider")
|
||||||
// .ok()
|
.ok()
|
||||||
// .map(|p| Box::new(p) as Box<dyn ClipboardProvider>),
|
.map(|p| Box::new(p) as Box<dyn ClipboardProvider>),
|
||||||
// (false, false) => X11ClipboardProvider::new()
|
#[cfg(feature = "x11")]
|
||||||
// .log_err("Could not create X11 clipboard provider")
|
(false, false) => clipboard::x11::Provider::new()
|
||||||
// .ok()
|
.log_err("Could not create X11 clipboard provider")
|
||||||
// .map(|p| Box::new(p) as Box<dyn ClipboardProvider>),
|
.ok()
|
||||||
// };
|
.map(|p| Box::new(p) as Box<dyn ClipboardProvider>),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
let state = WhisperState {
|
let state = WhisperState {
|
||||||
clipboard_provider: None,
|
clipboard_provider,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let xml = "gui/whisper.xml";
|
let xml = "gui/whisper.xml";
|
||||||
|
|
@ -203,6 +205,11 @@ pub fn create_whisper(
|
||||||
VirtualKey::V,
|
VirtualKey::V,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
app.hid_provider.send_key_routed(
|
||||||
|
app.wvr_server.as_mut(),
|
||||||
|
VirtualKey::V,
|
||||||
|
false,
|
||||||
|
);
|
||||||
app.hid_provider.send_key_routed(
|
app.hid_provider.send_key_routed(
|
||||||
app.wvr_server.as_mut(),
|
app.wvr_server.as_mut(),
|
||||||
VirtualKey::RCtrl,
|
VirtualKey::RCtrl,
|
||||||
|
|
@ -310,16 +317,19 @@ pub fn create_whisper(
|
||||||
|
|
||||||
panel.update_layout(app)?;
|
panel.update_layout(app)?;
|
||||||
|
|
||||||
|
let transform = Affine3A::from_cols_array_2d(&[
|
||||||
|
[0.49993715, -0.00020921684, -0.008030709],
|
||||||
|
[-0.0021463279, 0.47818363, -0.14607349],
|
||||||
|
[0.007741399, 0.1460891, 0.478121],
|
||||||
|
[-0.021562248, -0.40786624, -0.3346647],
|
||||||
|
]);
|
||||||
|
|
||||||
Ok(OverlayWindowConfig {
|
Ok(OverlayWindowConfig {
|
||||||
name: WHISPER_NAME.into(),
|
name: WHISPER_NAME.into(),
|
||||||
default_state: OverlayWindowState {
|
default_state: OverlayWindowState {
|
||||||
interactable: true,
|
interactable: true,
|
||||||
grabbable: true,
|
grabbable: true,
|
||||||
transform: Affine3A::from_scale_rotation_translation(
|
transform,
|
||||||
Vec3::ONE * 0.5,
|
|
||||||
Quat::IDENTITY,
|
|
||||||
vec3(0.0, 0.0, -0.6),
|
|
||||||
),
|
|
||||||
positioning: Positioning::Anchored,
|
positioning: Positioning::Anchored,
|
||||||
..OverlayWindowState::default()
|
..OverlayWindowState::default()
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,349 @@
|
||||||
use smithay_clipboard::Clipboard;
|
use std::{
|
||||||
use wlx_capture::wayland::smithay_client_toolkit::reexports::client::{Connection, Proxy};
|
fs::File,
|
||||||
|
io::Write,
|
||||||
|
sync::mpsc::{self, SyncSender},
|
||||||
|
thread::{self, JoinHandle},
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use calloop::{
|
||||||
|
EventLoop,
|
||||||
|
channel::{Channel, Event as ChannelEvent, Sender, channel},
|
||||||
|
};
|
||||||
|
use calloop_wayland_source::WaylandSource;
|
||||||
|
use wayland_client::{
|
||||||
|
Connection, Dispatch, QueueHandle,
|
||||||
|
globals::{GlobalListContents, registry_queue_init},
|
||||||
|
protocol::{
|
||||||
|
wl_registry,
|
||||||
|
wl_seat::{self, WlSeat},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use wayland_protocols::ext::data_control::v1::client::{
|
||||||
|
ext_data_control_device_v1::{self, ExtDataControlDeviceV1},
|
||||||
|
ext_data_control_manager_v1::{self, ExtDataControlManagerV1},
|
||||||
|
ext_data_control_offer_v1::{self, ExtDataControlOfferV1},
|
||||||
|
ext_data_control_source_v1::{self, ExtDataControlSourceV1},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::subsystem::clipboard::ClipboardProvider;
|
use crate::subsystem::clipboard::ClipboardProvider;
|
||||||
|
|
||||||
pub struct WlClipboardProvider {
|
const TEXT_MIME_TYPES: &[&str] = &[
|
||||||
clipboard: Clipboard,
|
"text/plain;charset=utf-8",
|
||||||
|
"text/plain;charset=UTF-8",
|
||||||
|
"text/plain",
|
||||||
|
"UTF8_STRING",
|
||||||
|
"TEXT",
|
||||||
|
"STRING",
|
||||||
|
];
|
||||||
|
|
||||||
|
pub struct Provider {
|
||||||
|
tx: Sender<Command>,
|
||||||
|
_thread: JoinHandle<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WlClipboardProvider {
|
enum Command {
|
||||||
|
SetText(String),
|
||||||
|
Shutdown,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ActiveSource {
|
||||||
|
source: ExtDataControlSourceV1,
|
||||||
|
bytes: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
conn: Connection,
|
||||||
|
qh: QueueHandle<State>,
|
||||||
|
manager: ExtDataControlManagerV1,
|
||||||
|
device: ExtDataControlDeviceV1,
|
||||||
|
active_sources: Vec<ActiveSource>,
|
||||||
|
ignored_offers: Vec<ExtDataControlOfferV1>,
|
||||||
|
stop: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Provider {
|
||||||
pub fn new() -> anyhow::Result<Self> {
|
pub fn new() -> anyhow::Result<Self> {
|
||||||
let connection = Connection::connect_to_env()?;
|
let (tx, rx) = channel::<Command>();
|
||||||
let clipboard = unsafe { Clipboard::new(connection.display().id().as_ptr() as *mut _) };
|
let (ready_tx, ready_rx) = mpsc::sync_channel::<Result<(), String>>(1);
|
||||||
|
|
||||||
Ok(Self { clipboard })
|
let thread = thread::Builder::new()
|
||||||
|
.name("wayland-clipboard-ext-data-control".to_owned())
|
||||||
|
.spawn(move || run_worker(rx, ready_tx))
|
||||||
|
.context("failed to spawn Wayland clipboard worker thread")?;
|
||||||
|
|
||||||
|
match ready_rx
|
||||||
|
.recv()
|
||||||
|
.context("Wayland clipboard worker exited before reporting readiness")?
|
||||||
|
{
|
||||||
|
Ok(()) => Ok(Self {
|
||||||
|
tx,
|
||||||
|
_thread: thread,
|
||||||
|
}),
|
||||||
|
Err(err) => anyhow::bail!("{}", err),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClipboardProvider for WlClipboardProvider {
|
impl ClipboardProvider for Provider {
|
||||||
fn set_clipboard_utf8(&mut self, content: &str) {
|
fn set_clipboard_utf8(&mut self, content: &str) {
|
||||||
self.clipboard.store(content.to_owned());
|
let _ = self.tx.send(Command::SetText(content.to_owned()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Provider {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = self.tx.send(Command::Shutdown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_worker(rx: Channel<Command>, ready: SyncSender<Result<(), String>>) {
|
||||||
|
macro_rules! init_or_report {
|
||||||
|
($expr:expr, $context:literal) => {
|
||||||
|
match $expr {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(err) => {
|
||||||
|
let _ = ready.send(Err(format!("{}: {}", $context, err)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let conn = init_or_report!(
|
||||||
|
Connection::connect_to_env(),
|
||||||
|
"connect to Wayland compositor"
|
||||||
|
);
|
||||||
|
|
||||||
|
let (globals, event_queue) =
|
||||||
|
init_or_report!(registry_queue_init::<State>(&conn), "read Wayland globals");
|
||||||
|
|
||||||
|
let qh = event_queue.handle();
|
||||||
|
|
||||||
|
let mut event_loop =
|
||||||
|
init_or_report!(EventLoop::<State>::try_new(), "create calloop event loop");
|
||||||
|
|
||||||
|
init_or_report!(
|
||||||
|
WaylandSource::new(conn.clone(), event_queue).insert(event_loop.handle()),
|
||||||
|
"attach Wayland queue to event loop"
|
||||||
|
);
|
||||||
|
|
||||||
|
let manager: ExtDataControlManagerV1 = init_or_report!(
|
||||||
|
globals.bind(&qh, 1..=1, ()),
|
||||||
|
"bind ext_data_control_manager_v1"
|
||||||
|
);
|
||||||
|
|
||||||
|
let seat: WlSeat = init_or_report!(globals.bind(&qh, 1..=9, ()), "bind wl_seat");
|
||||||
|
|
||||||
|
let device = manager.get_data_device(&seat, &qh, ());
|
||||||
|
|
||||||
|
let mut state = State {
|
||||||
|
conn,
|
||||||
|
qh,
|
||||||
|
manager,
|
||||||
|
device,
|
||||||
|
active_sources: Vec::new(),
|
||||||
|
ignored_offers: Vec::new(),
|
||||||
|
stop: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
init_or_report!(
|
||||||
|
event_loop
|
||||||
|
.handle()
|
||||||
|
.insert_source(rx, |event, _metadata, state| match event {
|
||||||
|
ChannelEvent::Msg(Command::SetText(text)) => {
|
||||||
|
state.set_clipboard_text(text);
|
||||||
|
}
|
||||||
|
ChannelEvent::Msg(Command::Shutdown) | ChannelEvent::Closed => {
|
||||||
|
state.stop = true;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"attach clipboard command channel to event loop"
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Err(err) = state.conn.flush() {
|
||||||
|
let _ = ready.send(Err(format!("flush initial Wayland requests: {err}")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = ready.send(Ok(()));
|
||||||
|
|
||||||
|
while !state.stop {
|
||||||
|
if let Err(err) = event_loop.dispatch(None, &mut state) {
|
||||||
|
eprintln!("Wayland clipboard event loop failed: {err}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(err) = state.conn.flush() {
|
||||||
|
eprintln!("Wayland clipboard flush failed: {err}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn set_clipboard_text(&mut self, content: String) {
|
||||||
|
let source = self.manager.create_data_source(&self.qh, ());
|
||||||
|
|
||||||
|
for mime_type in TEXT_MIME_TYPES {
|
||||||
|
source.offer((*mime_type).to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.device.set_selection(Some(&source));
|
||||||
|
|
||||||
|
let previous_sources = std::mem::take(&mut self.active_sources);
|
||||||
|
|
||||||
|
self.active_sources.push(ActiveSource {
|
||||||
|
source,
|
||||||
|
bytes: content.into_bytes(),
|
||||||
|
});
|
||||||
|
|
||||||
|
for old in previous_sources {
|
||||||
|
old.source.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = self.conn.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for State {
|
||||||
|
fn event(
|
||||||
|
_state: &mut Self,
|
||||||
|
_registry: &wl_registry::WlRegistry,
|
||||||
|
_event: wl_registry::Event,
|
||||||
|
_data: &GlobalListContents,
|
||||||
|
_conn: &Connection,
|
||||||
|
_qh: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<WlSeat, ()> for State {
|
||||||
|
fn event(
|
||||||
|
_state: &mut Self,
|
||||||
|
_seat: &WlSeat,
|
||||||
|
_event: wl_seat::Event,
|
||||||
|
_data: &(),
|
||||||
|
_conn: &Connection,
|
||||||
|
_qh: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<ExtDataControlManagerV1, ()> for State {
|
||||||
|
fn event(
|
||||||
|
_state: &mut Self,
|
||||||
|
_manager: &ExtDataControlManagerV1,
|
||||||
|
_event: ext_data_control_manager_v1::Event,
|
||||||
|
_data: &(),
|
||||||
|
_conn: &Connection,
|
||||||
|
_qh: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<ExtDataControlDeviceV1, ()> for State {
|
||||||
|
fn event(
|
||||||
|
state: &mut Self,
|
||||||
|
device: &ExtDataControlDeviceV1,
|
||||||
|
event: ext_data_control_device_v1::Event,
|
||||||
|
_data: &(),
|
||||||
|
_conn: &Connection,
|
||||||
|
_qh: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
ext_data_control_device_v1::Event::DataOffer { id } => {
|
||||||
|
state.ignored_offers.push(id);
|
||||||
|
}
|
||||||
|
ext_data_control_device_v1::Event::Selection { id }
|
||||||
|
| ext_data_control_device_v1::Event::PrimarySelection { id } => {
|
||||||
|
if let Some(offer) = id {
|
||||||
|
if let Some(pos) = state
|
||||||
|
.ignored_offers
|
||||||
|
.iter()
|
||||||
|
.position(|stored| *stored == offer)
|
||||||
|
{
|
||||||
|
state.ignored_offers.swap_remove(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
offer.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ext_data_control_device_v1::Event::Finished => {
|
||||||
|
device.destroy();
|
||||||
|
state.stop = true;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event_created_child(
|
||||||
|
opcode: u16,
|
||||||
|
qh: &QueueHandle<Self>,
|
||||||
|
) -> std::sync::Arc<dyn wayland_client::backend::ObjectData> {
|
||||||
|
if opcode == ext_data_control_device_v1::EVT_DATA_OFFER_OPCODE {
|
||||||
|
qh.make_data::<ExtDataControlOfferV1, _>(())
|
||||||
|
} else {
|
||||||
|
unreachable!("unknown ext_data_control_device_v1 child event opcode: {opcode}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<ExtDataControlOfferV1, ()> for State {
|
||||||
|
fn event(
|
||||||
|
_state: &mut Self,
|
||||||
|
_offer: &ExtDataControlOfferV1,
|
||||||
|
_event: ext_data_control_offer_v1::Event,
|
||||||
|
_data: &(),
|
||||||
|
_conn: &Connection,
|
||||||
|
_qh: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<ExtDataControlSourceV1, ()> for State {
|
||||||
|
fn event(
|
||||||
|
state: &mut Self,
|
||||||
|
source: &ExtDataControlSourceV1,
|
||||||
|
event: ext_data_control_source_v1::Event,
|
||||||
|
_data: &(),
|
||||||
|
_conn: &Connection,
|
||||||
|
_qh: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
ext_data_control_source_v1::Event::Send { mime_type, fd } => {
|
||||||
|
if !TEXT_MIME_TYPES.contains(&mime_type.as_str()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(active) = state
|
||||||
|
.active_sources
|
||||||
|
.iter()
|
||||||
|
.find(|active| active.source == *source)
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut file = File::from(fd);
|
||||||
|
|
||||||
|
if let Err(err) = file.write_all(&active.bytes) {
|
||||||
|
eprintln!("failed to write clipboard data to Wayland fd: {err}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = file.flush();
|
||||||
|
}
|
||||||
|
ext_data_control_source_v1::Event::Cancelled => {
|
||||||
|
if let Some(pos) = state
|
||||||
|
.active_sources
|
||||||
|
.iter()
|
||||||
|
.position(|active| active.source == *source)
|
||||||
|
{
|
||||||
|
let active = state.active_sources.swap_remove(pos);
|
||||||
|
active.source.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,12 @@ use xcb::{Xid, x};
|
||||||
|
|
||||||
use crate::{RUNNING, subsystem::clipboard::ClipboardProvider};
|
use crate::{RUNNING, subsystem::clipboard::ClipboardProvider};
|
||||||
|
|
||||||
pub struct X11ClipboardProvider {
|
pub struct Provider {
|
||||||
tx: Sender<Command>,
|
tx: Sender<Command>,
|
||||||
worker: Option<JoinHandle<()>>,
|
worker: Option<JoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl X11ClipboardProvider {
|
impl Provider {
|
||||||
pub fn new() -> Result<Self, Box<dyn Error + Send + Sync + 'static>> {
|
pub fn new() -> Result<Self, Box<dyn Error + Send + Sync + 'static>> {
|
||||||
let (conn, screen_num) = xcb::Connection::connect(None)?;
|
let (conn, screen_num) = xcb::Connection::connect(None)?;
|
||||||
let runtime = ClipboardRuntime::new(conn, screen_num)?;
|
let runtime = ClipboardRuntime::new(conn, screen_num)?;
|
||||||
|
|
@ -35,7 +35,7 @@ impl X11ClipboardProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClipboardProvider for X11ClipboardProvider {
|
impl ClipboardProvider for Provider {
|
||||||
fn set_clipboard_utf8(&mut self, content: &str) {
|
fn set_clipboard_utf8(&mut self, content: &str) {
|
||||||
let (ack_tx, ack_rx) = mpsc::channel();
|
let (ack_tx, ack_rx) = mpsc::channel();
|
||||||
|
|
||||||
|
|
@ -50,7 +50,7 @@ impl ClipboardProvider for X11ClipboardProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for X11ClipboardProvider {
|
impl Drop for Provider {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let _ = self.tx.send(Command::Shutdown);
|
let _ = self.tx.send(Command::Shutdown);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use wlx_capture::wayland::wayland_client::{
|
use wayland_client::{
|
||||||
Connection, Dispatch, Proxy, QueueHandle,
|
Connection, Dispatch, Proxy, QueueHandle,
|
||||||
globals::{GlobalListContents, registry_queue_init},
|
globals::{GlobalListContents, registry_queue_init},
|
||||||
protocol::{
|
protocol::{
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,6 @@ use smithay_client_toolkit::reexports::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use smithay_client_toolkit;
|
|
||||||
pub use wayland_client;
|
|
||||||
use wayland_client::{
|
use wayland_client::{
|
||||||
Connection, Dispatch, EventQueue, Proxy, QueueHandle,
|
Connection, Dispatch, EventQueue, Proxy, QueueHandle,
|
||||||
backend::WaylandError,
|
backend::WaylandError,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue