use crate::wayland_subscription::{ToplevelRequest, ToplevelUpdate, WaylandRequest, WaylandUpdate}; use std::os::{ fd::{FromRawFd, RawFd}, unix::net::UnixStream, }; use cctk::{ sctk::{ self, activation::{RequestData, RequestDataExt}, reexports::{calloop, calloop_wayland_source::WaylandSource}, seat::{SeatHandler, SeatState}, }, toplevel_info::{ToplevelInfoHandler, ToplevelInfoState}, toplevel_management::{ToplevelManagerHandler, ToplevelManagerState}, wayland_client::{ self, protocol::{wl_seat::WlSeat, wl_surface::WlSurface}, WEnum, }, }; use cosmic_protocols::{ toplevel_info::v1::client::zcosmic_toplevel_handle_v1, toplevel_management::v1::client::zcosmic_toplevel_manager_v1, }; use futures::channel::mpsc::UnboundedSender; use sctk::{ activation::{ActivationHandler, ActivationState}, registry::{ProvidesRegistryState, RegistryState}, }; use wayland_client::{globals::registry_queue_init, Connection, QueueHandle}; struct AppData { exit: bool, tx: UnboundedSender, queue_handle: QueueHandle, registry_state: RegistryState, activation_state: Option, toplevel_info_state: ToplevelInfoState, toplevel_manager_state: ToplevelManagerState, seat_state: SeatState, } impl ProvidesRegistryState for AppData { fn registry(&mut self) -> &mut RegistryState { &mut self.registry_state } sctk::registry_handlers!(); } struct ExecRequestData { data: RequestData, exec: String, gpu_idx: Option, } impl RequestDataExt for ExecRequestData { fn app_id(&self) -> Option<&str> { self.data.app_id() } fn seat_and_serial(&self) -> Option<(&WlSeat, u32)> { self.data.seat_and_serial() } fn surface(&self) -> Option<&WlSurface> { self.data.surface() } } impl ActivationHandler for AppData { type RequestData = ExecRequestData; fn new_token(&mut self, token: String, data: &ExecRequestData) { let _ = self.tx.unbounded_send(WaylandUpdate::ActivationToken { token: Some(token), exec: data.exec.clone(), gpu_idx: data.gpu_idx, }); } } impl SeatHandler for AppData { fn seat_state(&mut self) -> &mut sctk::seat::SeatState { &mut self.seat_state } fn new_seat(&mut self, _: &Connection, _: &QueueHandle, _: WlSeat) {} fn new_capability( &mut self, _: &Connection, _: &QueueHandle, _: WlSeat, _: sctk::seat::Capability, ) { } fn remove_capability( &mut self, _: &Connection, _: &QueueHandle, _: WlSeat, _: sctk::seat::Capability, ) { } fn remove_seat(&mut self, _: &Connection, _: &QueueHandle, _: WlSeat) {} } impl ToplevelManagerHandler for AppData { fn toplevel_manager_state(&mut self) -> &mut cctk::toplevel_management::ToplevelManagerState { &mut self.toplevel_manager_state } fn capabilities( &mut self, _: &Connection, _: &QueueHandle, _: Vec>, ) { // TODO capabilities could affect the options in the applet } } impl ToplevelInfoHandler for AppData { fn toplevel_info_state(&mut self) -> &mut ToplevelInfoState { &mut self.toplevel_info_state } fn new_toplevel( &mut self, _conn: &Connection, _qh: &QueueHandle, toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, ) { if let Some(info) = self.toplevel_info_state.info(toplevel) { let _ = self .tx .unbounded_send(WaylandUpdate::Toplevel(ToplevelUpdate::Add( toplevel.clone(), info.clone(), ))); } } fn update_toplevel( &mut self, _conn: &Connection, _qh: &QueueHandle, toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, ) { if let Some(info) = self.toplevel_info_state.info(toplevel) { let _ = self .tx .unbounded_send(WaylandUpdate::Toplevel(ToplevelUpdate::Update( toplevel.clone(), info.clone(), ))); } } fn toplevel_closed( &mut self, _conn: &Connection, _qh: &QueueHandle, toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, ) { let _ = self .tx .unbounded_send(WaylandUpdate::Toplevel(ToplevelUpdate::Remove( toplevel.clone(), ))); } } pub(crate) fn wayland_handler( tx: UnboundedSender, rx: calloop::channel::Channel, ) { let socket = std::env::var("X_PRIVILEGED_WAYLAND_SOCKET") .ok() .and_then(|fd| { fd.parse::() .ok() .map(|fd| unsafe { UnixStream::from_raw_fd(fd) }) }); let conn = if let Some(socket) = socket { Connection::from_socket(socket).unwrap() } else { Connection::connect_to_env().unwrap() }; let (globals, event_queue) = registry_queue_init(&conn).unwrap(); let mut event_loop = calloop::EventLoop::::try_new().unwrap(); let qh = event_queue.handle(); let wayland_source = WaylandSource::new(conn, event_queue); let handle = event_loop.handle(); wayland_source .insert(handle.clone()) .expect("Failed to insert wayland source."); if handle .insert_source(rx, |event, _, state| match event { calloop::channel::Event::Msg(req) => match req { WaylandRequest::Toplevel(req) => match req { ToplevelRequest::Activate(handle) => { if let Some(seat) = state.seat_state.seats().next() { let manager = &state.toplevel_manager_state.manager; manager.activate(&handle, &seat); } } ToplevelRequest::Quit(handle) => { let manager = &state.toplevel_manager_state.manager; manager.close(&handle); } }, WaylandRequest::TokenRequest { app_id, exec, gpu_idx, } => { if let Some(activation_state) = state.activation_state.as_ref() { activation_state.request_token_with_data( &state.queue_handle, ExecRequestData { data: RequestData { app_id: Some(app_id), seat_and_serial: state .seat_state .seats() .next() .map(|seat| (seat, 0)), surface: None, }, exec, gpu_idx, }, ); } else { let _ = state.tx.unbounded_send(WaylandUpdate::ActivationToken { token: None, exec, gpu_idx, }); } } }, calloop::channel::Event::Closed => { state.exit = true; } }) .is_err() { return; } let registry_state = RegistryState::new(&globals); let mut app_data = AppData { exit: false, tx, queue_handle: qh.clone(), activation_state: ActivationState::bind::(&globals, &qh).ok(), seat_state: SeatState::new(&globals, &qh), toplevel_info_state: ToplevelInfoState::new(®istry_state, &qh), toplevel_manager_state: ToplevelManagerState::new(®istry_state, &qh), registry_state, }; loop { if app_data.exit { break; } event_loop.dispatch(None, &mut app_data).unwrap(); } } sctk::delegate_activation!(AppData, ExecRequestData); sctk::delegate_seat!(AppData); sctk::delegate_registry!(AppData); cctk::delegate_toplevel_info!(AppData); cctk::delegate_toplevel_manager!(AppData);