use crate::core::{
bindings::{KeyCode, MouseEvent},
data_types::{Point, Region},
xconnection::{Atom, Result, XAtomQuerier, Xid},
};
use std::convert::TryFrom;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum XEvent {
ClientMessage(ClientMessage),
ConfigureNotify(ConfigureEvent),
ConfigureRequest(ConfigureEvent),
Enter(PointerChange),
Expose(ExposeEvent),
FocusIn(Xid),
Destroy(Xid),
KeyPress(KeyCode),
Leave(PointerChange),
MapRequest(Xid, bool),
MouseEvent(MouseEvent),
PropertyNotify(PropertyEvent),
RandrNotify,
ScreenChange,
UnmapNotify(Xid),
}
impl std::fmt::Display for XEvent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
XEvent::ClientMessage(_) => write!(f, "ClientMessage"),
XEvent::ConfigureNotify(_) => write!(f, "ConfigureNotify"),
XEvent::ConfigureRequest(_) => write!(f, "ConfigureRequest"),
XEvent::Enter(_) => write!(f, "Enter"),
XEvent::Expose(_) => write!(f, "Expose"),
XEvent::FocusIn(_) => write!(f, "FocusIn"),
XEvent::Destroy(_) => write!(f, "Destroy"),
XEvent::KeyPress(_) => write!(f, "KeyPress"),
XEvent::Leave(_) => write!(f, "Leave"),
XEvent::MapRequest(_, _) => write!(f, "MapRequest"),
XEvent::MouseEvent(_) => write!(f, "MouseEvent"),
XEvent::PropertyNotify(_) => write!(f, "PropertyNotify"),
XEvent::RandrNotify => write!(f, "RandrNotify"),
XEvent::ScreenChange => write!(f, "ScreenChange"),
XEvent::UnmapNotify(_) => write!(f, "UnmapNotify"),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ClientMessageKind {
DeleteWindow(Xid),
TakeFocus(Xid),
TakeSystrayOwnership(Xid, Xid),
XEmbedFocusIn(Xid, Xid),
XEmbedModalityOn(Xid, Xid),
XEmbedNotify(Xid, Xid),
XEmbedWindowActivate(Xid, Xid),
}
impl ClientMessageKind {
pub fn as_message<Q>(&self, q: &Q) -> Result<ClientMessage>
where
Q: XAtomQuerier,
{
let proto_msg = |id: Xid, atom: Atom| {
let proto = Atom::WmProtocols.as_ref();
let data = &[q.atom_id(atom.as_ref())?, 0, 0, 0, 0];
let mask = ClientEventMask::NoEventMask;
Ok(ClientMessage::new(id, mask, proto, data.into()))
};
let xembed_version = 0;
let notify = 0;
let activate = 1;
let focus_in = 4;
let modality_on = 10;
let xembed_msg = |id: Xid, embedder: Xid, kind: u32| {
let atom = Atom::XEmbed.as_ref();
let data = &[0, kind, 0, embedder, xembed_version];
let mask = ClientEventMask::SubstructureNotify;
Ok(ClientMessage::new(id, mask, atom, data.into()))
};
match self {
ClientMessageKind::DeleteWindow(id) => proto_msg(*id, Atom::WmDeleteWindow),
ClientMessageKind::TakeFocus(id) => proto_msg(*id, Atom::WmTakeFocus),
ClientMessageKind::TakeSystrayOwnership(root_id, systray_id) => {
let atom = Atom::Manager.as_ref();
let systray = q.atom_id(Atom::NetSystemTrayS0.as_ref())?;
let data = &[0, systray, *systray_id, 0, 0];
let mask = ClientEventMask::SubstructureNotify;
Ok(ClientMessage::new(*root_id, mask, atom, data.into()))
}
ClientMessageKind::XEmbedFocusIn(id, other) => xembed_msg(*id, *other, focus_in),
ClientMessageKind::XEmbedModalityOn(id, other) => xembed_msg(*id, *other, modality_on),
ClientMessageKind::XEmbedNotify(id, other) => xembed_msg(*id, *other, notify),
ClientMessageKind::XEmbedWindowActivate(id, other) => xembed_msg(*id, *other, activate),
}
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ClientEventMask {
SubstructureNotify,
NoEventMask,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ClientMessageData {
U8([u8; 20]),
U16([u16; 10]),
U32([u32; 5]),
}
impl ClientMessageData {
pub fn as_usize(&self) -> Vec<usize> {
match self {
Self::U8(data) => cast_slice!(data, usize),
Self::U16(data) => cast_slice!(data, usize),
Self::U32(data) => cast_slice!(data, usize),
}
}
}
macro_rules! __impl_client_message_data(
{ $t:ty; $count:expr, $variant:expr, $method:ident } => {
impl ClientMessageData {
pub fn $method(&self) -> Vec<$t> {
match self {
Self::U8(data) => cast_slice!(data, $t),
Self::U16(data) => cast_slice!(data, $t),
Self::U32(data) => cast_slice!(data, $t),
}
}
}
impl From<[$t; $count]> for ClientMessageData {
fn from(data: [$t; $count]) -> Self {
$variant(data)
}
}
impl From<&[$t; $count]> for ClientMessageData {
fn from(data: &[$t; $count]) -> Self {
$variant(*data)
}
}
impl TryFrom<&[$t]> for ClientMessageData {
type Error = std::array::TryFromSliceError;
fn try_from(data: &[$t]) -> std::result::Result<Self, Self::Error> {
Ok($variant(<[$t; $count]>::try_from(data)?))
}
}
}
);
__impl_client_message_data!(u8; 20, ClientMessageData::U8, as_u8);
__impl_client_message_data!(u16; 10, ClientMessageData::U16, as_u16);
__impl_client_message_data!(u32; 5, ClientMessageData::U32, as_u32);
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ClientMessage {
pub id: Xid,
pub mask: ClientEventMask,
pub dtype: String,
data: ClientMessageData,
}
impl ClientMessage {
pub fn new(
id: Xid,
mask: ClientEventMask,
dtype: impl Into<String>,
data: ClientMessageData,
) -> Self {
Self {
id,
mask,
dtype: dtype.into(),
data,
}
}
pub fn data(&self) -> &ClientMessageData {
&self.data
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ConfigureEvent {
pub id: Xid,
pub r: Region,
pub is_root: bool,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ExposeEvent {
pub id: Xid,
pub r: Region,
pub count: usize,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PointerChange {
pub id: Xid,
pub abs: Point,
pub relative: Point,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PropertyEvent {
pub id: Xid,
pub atom: String,
pub is_root: bool,
}