use crate::{
core::bindings::{KeyCode, MotionNotifyEvent, MouseEvent},
pure::geometry::{Point, Rect},
x::{Atom, XConn},
Result, Xid,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum XEvent {
ClientMessage(ClientMessage),
ConfigureNotify(ConfigureEvent),
ConfigureRequest(ConfigureEvent),
Enter(PointerChange),
Expose(ExposeEvent),
FocusIn(Xid),
Destroy(Xid),
KeyPress(KeyCode),
Leave(PointerChange),
MappingNotify,
MapRequest(Xid),
MouseEvent(MouseEvent),
MotionNotify(MotionNotifyEvent),
PropertyNotify(PropertyEvent),
RandrNotify,
ResizeRequest(ResizeRequestEvent),
ScreenChange,
UnmapNotify(Xid),
}
impl std::fmt::Display for XEvent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use XEvent::*;
match self {
ClientMessage(_) => write!(f, "ClientMessage"),
ConfigureNotify(_) => write!(f, "ConfigureNotify"),
ConfigureRequest(_) => write!(f, "ConfigureRequest"),
Enter(_) => write!(f, "Enter"),
Expose(_) => write!(f, "Expose"),
FocusIn(_) => write!(f, "FocusIn"),
Destroy(_) => write!(f, "Destroy"),
KeyPress(_) => write!(f, "KeyPress"),
Leave(_) => write!(f, "Leave"),
MappingNotify => write!(f, "MappingNotify"),
MapRequest(_) => write!(f, "MapRequest"),
MouseEvent(_) => write!(f, "MouseEvent"),
MotionNotify(_) => write!(f, "MotionNotify"),
PropertyNotify(_) => write!(f, "PropertyNotify"),
RandrNotify => write!(f, "RandrNotify"),
ResizeRequest(_) => write!(f, "ResizeRequest"),
ScreenChange => write!(f, "ScreenChange"),
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<X>(&self, q: &X) -> Result<ClientMessage>
where
X: XConn,
{
let proto_msg = |id: Xid, atom: Atom| {
let proto = Atom::WmProtocols.as_ref();
let data = &[*q.intern_atom(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.intern_atom(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,
StructureNotify,
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]),
}
macro_rules! cast_slice {
($s:expr, $t:ty) => {
$s.iter().map(|&v| v as $t).collect::<Vec<$t>>()
};
}
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,
pub 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,
}
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ConfigureEvent {
pub id: Xid,
pub r: Rect,
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: Rect,
pub count: usize,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PointerChange {
pub id: Xid,
pub abs: Point,
pub relative: Point,
pub same_screen: bool,
}
#[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,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ResizeRequestEvent {
pub id: Xid,
pub width: u32,
pub height: u32,
}