2641 lines
110 KiB
Plaintext
2641 lines
110 KiB
Plaintext
#![feature(prelude_import)]
|
|
#![warn(clippy::pedantic)]
|
|
#[prelude_import]
|
|
use std::prelude::rust_2021::*;
|
|
#[macro_use]
|
|
extern crate std;
|
|
use std::fmt::{Debug, Display};
|
|
use breadx::{
|
|
auto::xproto::{GrabKeyRequest, GrabMode, KeyButMask, ModMask},
|
|
keyboard::KeyboardState, prelude::{AsyncDisplay, AsyncDisplayXprotoExt, MapState},
|
|
traits::DisplayBase, AsyncDisplayConnection, AsyncDisplayExt, BreadError,
|
|
ConfigureWindowParameters, Event, EventMask, Window,
|
|
};
|
|
use lazy_static::lazy_static;
|
|
use tokio::sync::mpsc::unbounded_channel;
|
|
mod config {
|
|
#![allow(dead_code, clippy::module_name_repetitions)]
|
|
use crate::msg_listener::Action;
|
|
use crate::Result;
|
|
use breadx::auto::xproto::KeyButMask;
|
|
use serde::{
|
|
de::{Deserializer, Visitor},
|
|
Deserialize,
|
|
};
|
|
use std::collections::HashMap;
|
|
use std::path::PathBuf;
|
|
pub struct XcrabConfig {
|
|
border_color: Option<u32>,
|
|
focused_color: Option<u32>,
|
|
border_size: Option<u16>,
|
|
gap_size: Option<u16>,
|
|
outer_gap_size: Option<u16>,
|
|
pub msg: Option<XcrabMsgConfig>,
|
|
#[allow(clippy::zero_sized_map_values)]
|
|
#[serde(default)]
|
|
pub binds: HashMap<Keybind, Action>,
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::clone::Clone for XcrabConfig {
|
|
#[inline]
|
|
fn clone(&self) -> XcrabConfig {
|
|
XcrabConfig {
|
|
border_color: ::core::clone::Clone::clone(&self.border_color),
|
|
focused_color: ::core::clone::Clone::clone(&self.focused_color),
|
|
border_size: ::core::clone::Clone::clone(&self.border_size),
|
|
gap_size: ::core::clone::Clone::clone(&self.gap_size),
|
|
outer_gap_size: ::core::clone::Clone::clone(&self.outer_gap_size),
|
|
msg: ::core::clone::Clone::clone(&self.msg),
|
|
binds: ::core::clone::Clone::clone(&self.binds),
|
|
}
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::fmt::Debug for XcrabConfig {
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
|
let names: &'static _ = &[
|
|
"border_color",
|
|
"focused_color",
|
|
"border_size",
|
|
"gap_size",
|
|
"outer_gap_size",
|
|
"msg",
|
|
"binds",
|
|
];
|
|
let values: &[&dyn ::core::fmt::Debug] = &[
|
|
&self.border_color,
|
|
&self.focused_color,
|
|
&self.border_size,
|
|
&self.gap_size,
|
|
&self.outer_gap_size,
|
|
&self.msg,
|
|
&&self.binds,
|
|
];
|
|
::core::fmt::Formatter::debug_struct_fields_finish(
|
|
f,
|
|
"XcrabConfig",
|
|
names,
|
|
values,
|
|
)
|
|
}
|
|
}
|
|
#[doc(hidden)]
|
|
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
|
|
const _: () = {
|
|
#[allow(unused_extern_crates, clippy::useless_attribute)]
|
|
extern crate serde as _serde;
|
|
#[automatically_derived]
|
|
impl<'de> _serde::Deserialize<'de> for XcrabConfig {
|
|
fn deserialize<__D>(
|
|
__deserializer: __D,
|
|
) -> _serde::__private::Result<Self, __D::Error>
|
|
where
|
|
__D: _serde::Deserializer<'de>,
|
|
{
|
|
#[allow(non_camel_case_types)]
|
|
enum __Field {
|
|
__field0,
|
|
__field1,
|
|
__field2,
|
|
__field3,
|
|
__field4,
|
|
__field5,
|
|
__field6,
|
|
__ignore,
|
|
}
|
|
struct __FieldVisitor;
|
|
impl<'de> _serde::de::Visitor<'de> for __FieldVisitor {
|
|
type Value = __Field;
|
|
fn expecting(
|
|
&self,
|
|
__formatter: &mut _serde::__private::Formatter,
|
|
) -> _serde::__private::fmt::Result {
|
|
_serde::__private::Formatter::write_str(
|
|
__formatter,
|
|
"field identifier",
|
|
)
|
|
}
|
|
fn visit_u64<__E>(
|
|
self,
|
|
__value: u64,
|
|
) -> _serde::__private::Result<Self::Value, __E>
|
|
where
|
|
__E: _serde::de::Error,
|
|
{
|
|
match __value {
|
|
0u64 => _serde::__private::Ok(__Field::__field0),
|
|
1u64 => _serde::__private::Ok(__Field::__field1),
|
|
2u64 => _serde::__private::Ok(__Field::__field2),
|
|
3u64 => _serde::__private::Ok(__Field::__field3),
|
|
4u64 => _serde::__private::Ok(__Field::__field4),
|
|
5u64 => _serde::__private::Ok(__Field::__field5),
|
|
6u64 => _serde::__private::Ok(__Field::__field6),
|
|
_ => _serde::__private::Ok(__Field::__ignore),
|
|
}
|
|
}
|
|
fn visit_str<__E>(
|
|
self,
|
|
__value: &str,
|
|
) -> _serde::__private::Result<Self::Value, __E>
|
|
where
|
|
__E: _serde::de::Error,
|
|
{
|
|
match __value {
|
|
"border_color" => _serde::__private::Ok(__Field::__field0),
|
|
"focused_color" => _serde::__private::Ok(__Field::__field1),
|
|
"border_size" => _serde::__private::Ok(__Field::__field2),
|
|
"gap_size" => _serde::__private::Ok(__Field::__field3),
|
|
"outer_gap_size" => _serde::__private::Ok(__Field::__field4),
|
|
"msg" => _serde::__private::Ok(__Field::__field5),
|
|
"binds" => _serde::__private::Ok(__Field::__field6),
|
|
_ => _serde::__private::Ok(__Field::__ignore),
|
|
}
|
|
}
|
|
fn visit_bytes<__E>(
|
|
self,
|
|
__value: &[u8],
|
|
) -> _serde::__private::Result<Self::Value, __E>
|
|
where
|
|
__E: _serde::de::Error,
|
|
{
|
|
match __value {
|
|
b"border_color" => _serde::__private::Ok(__Field::__field0),
|
|
b"focused_color" => _serde::__private::Ok(__Field::__field1),
|
|
b"border_size" => _serde::__private::Ok(__Field::__field2),
|
|
b"gap_size" => _serde::__private::Ok(__Field::__field3),
|
|
b"outer_gap_size" => _serde::__private::Ok(__Field::__field4),
|
|
b"msg" => _serde::__private::Ok(__Field::__field5),
|
|
b"binds" => _serde::__private::Ok(__Field::__field6),
|
|
_ => _serde::__private::Ok(__Field::__ignore),
|
|
}
|
|
}
|
|
}
|
|
impl<'de> _serde::Deserialize<'de> for __Field {
|
|
#[inline]
|
|
fn deserialize<__D>(
|
|
__deserializer: __D,
|
|
) -> _serde::__private::Result<Self, __D::Error>
|
|
where
|
|
__D: _serde::Deserializer<'de>,
|
|
{
|
|
_serde::Deserializer::deserialize_identifier(
|
|
__deserializer,
|
|
__FieldVisitor,
|
|
)
|
|
}
|
|
}
|
|
struct __Visitor<'de> {
|
|
marker: _serde::__private::PhantomData<XcrabConfig>,
|
|
lifetime: _serde::__private::PhantomData<&'de ()>,
|
|
}
|
|
impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> {
|
|
type Value = XcrabConfig;
|
|
fn expecting(
|
|
&self,
|
|
__formatter: &mut _serde::__private::Formatter,
|
|
) -> _serde::__private::fmt::Result {
|
|
_serde::__private::Formatter::write_str(
|
|
__formatter,
|
|
"struct XcrabConfig",
|
|
)
|
|
}
|
|
#[inline]
|
|
fn visit_seq<__A>(
|
|
self,
|
|
mut __seq: __A,
|
|
) -> _serde::__private::Result<Self::Value, __A::Error>
|
|
where
|
|
__A: _serde::de::SeqAccess<'de>,
|
|
{
|
|
let __field0 = match match _serde::de::SeqAccess::next_element::<
|
|
Option<u32>,
|
|
>(&mut __seq) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
} {
|
|
_serde::__private::Some(__value) => __value,
|
|
_serde::__private::None => {
|
|
return _serde::__private::Err(
|
|
_serde::de::Error::invalid_length(
|
|
0usize,
|
|
&"struct XcrabConfig with 7 elements",
|
|
),
|
|
);
|
|
}
|
|
};
|
|
let __field1 = match match _serde::de::SeqAccess::next_element::<
|
|
Option<u32>,
|
|
>(&mut __seq) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
} {
|
|
_serde::__private::Some(__value) => __value,
|
|
_serde::__private::None => {
|
|
return _serde::__private::Err(
|
|
_serde::de::Error::invalid_length(
|
|
1usize,
|
|
&"struct XcrabConfig with 7 elements",
|
|
),
|
|
);
|
|
}
|
|
};
|
|
let __field2 = match match _serde::de::SeqAccess::next_element::<
|
|
Option<u16>,
|
|
>(&mut __seq) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
} {
|
|
_serde::__private::Some(__value) => __value,
|
|
_serde::__private::None => {
|
|
return _serde::__private::Err(
|
|
_serde::de::Error::invalid_length(
|
|
2usize,
|
|
&"struct XcrabConfig with 7 elements",
|
|
),
|
|
);
|
|
}
|
|
};
|
|
let __field3 = match match _serde::de::SeqAccess::next_element::<
|
|
Option<u16>,
|
|
>(&mut __seq) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
} {
|
|
_serde::__private::Some(__value) => __value,
|
|
_serde::__private::None => {
|
|
return _serde::__private::Err(
|
|
_serde::de::Error::invalid_length(
|
|
3usize,
|
|
&"struct XcrabConfig with 7 elements",
|
|
),
|
|
);
|
|
}
|
|
};
|
|
let __field4 = match match _serde::de::SeqAccess::next_element::<
|
|
Option<u16>,
|
|
>(&mut __seq) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
} {
|
|
_serde::__private::Some(__value) => __value,
|
|
_serde::__private::None => {
|
|
return _serde::__private::Err(
|
|
_serde::de::Error::invalid_length(
|
|
4usize,
|
|
&"struct XcrabConfig with 7 elements",
|
|
),
|
|
);
|
|
}
|
|
};
|
|
let __field5 = match match _serde::de::SeqAccess::next_element::<
|
|
Option<XcrabMsgConfig>,
|
|
>(&mut __seq) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
} {
|
|
_serde::__private::Some(__value) => __value,
|
|
_serde::__private::None => {
|
|
return _serde::__private::Err(
|
|
_serde::de::Error::invalid_length(
|
|
5usize,
|
|
&"struct XcrabConfig with 7 elements",
|
|
),
|
|
);
|
|
}
|
|
};
|
|
let __field6 = match match _serde::de::SeqAccess::next_element::<
|
|
HashMap<Keybind, Action>,
|
|
>(&mut __seq) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
} {
|
|
_serde::__private::Some(__value) => __value,
|
|
_serde::__private::None => {
|
|
_serde::__private::Default::default()
|
|
}
|
|
};
|
|
_serde::__private::Ok(XcrabConfig {
|
|
border_color: __field0,
|
|
focused_color: __field1,
|
|
border_size: __field2,
|
|
gap_size: __field3,
|
|
outer_gap_size: __field4,
|
|
msg: __field5,
|
|
binds: __field6,
|
|
})
|
|
}
|
|
#[inline]
|
|
fn visit_map<__A>(
|
|
self,
|
|
mut __map: __A,
|
|
) -> _serde::__private::Result<Self::Value, __A::Error>
|
|
where
|
|
__A: _serde::de::MapAccess<'de>,
|
|
{
|
|
let mut __field0: _serde::__private::Option<Option<u32>> = _serde::__private::None;
|
|
let mut __field1: _serde::__private::Option<Option<u32>> = _serde::__private::None;
|
|
let mut __field2: _serde::__private::Option<Option<u16>> = _serde::__private::None;
|
|
let mut __field3: _serde::__private::Option<Option<u16>> = _serde::__private::None;
|
|
let mut __field4: _serde::__private::Option<Option<u16>> = _serde::__private::None;
|
|
let mut __field5: _serde::__private::Option<
|
|
Option<XcrabMsgConfig>,
|
|
> = _serde::__private::None;
|
|
let mut __field6: _serde::__private::Option<
|
|
HashMap<Keybind, Action>,
|
|
> = _serde::__private::None;
|
|
while let _serde::__private::Some(__key)
|
|
= match _serde::de::MapAccess::next_key::<
|
|
__Field,
|
|
>(&mut __map) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
} {
|
|
match __key {
|
|
__Field::__field0 => {
|
|
if _serde::__private::Option::is_some(&__field0) {
|
|
return _serde::__private::Err(
|
|
<__A::Error as _serde::de::Error>::duplicate_field(
|
|
"border_color",
|
|
),
|
|
);
|
|
}
|
|
__field0 = _serde::__private::Some(
|
|
match _serde::de::MapAccess::next_value::<
|
|
Option<u32>,
|
|
>(&mut __map) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
},
|
|
);
|
|
}
|
|
__Field::__field1 => {
|
|
if _serde::__private::Option::is_some(&__field1) {
|
|
return _serde::__private::Err(
|
|
<__A::Error as _serde::de::Error>::duplicate_field(
|
|
"focused_color",
|
|
),
|
|
);
|
|
}
|
|
__field1 = _serde::__private::Some(
|
|
match _serde::de::MapAccess::next_value::<
|
|
Option<u32>,
|
|
>(&mut __map) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
},
|
|
);
|
|
}
|
|
__Field::__field2 => {
|
|
if _serde::__private::Option::is_some(&__field2) {
|
|
return _serde::__private::Err(
|
|
<__A::Error as _serde::de::Error>::duplicate_field(
|
|
"border_size",
|
|
),
|
|
);
|
|
}
|
|
__field2 = _serde::__private::Some(
|
|
match _serde::de::MapAccess::next_value::<
|
|
Option<u16>,
|
|
>(&mut __map) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
},
|
|
);
|
|
}
|
|
__Field::__field3 => {
|
|
if _serde::__private::Option::is_some(&__field3) {
|
|
return _serde::__private::Err(
|
|
<__A::Error as _serde::de::Error>::duplicate_field(
|
|
"gap_size",
|
|
),
|
|
);
|
|
}
|
|
__field3 = _serde::__private::Some(
|
|
match _serde::de::MapAccess::next_value::<
|
|
Option<u16>,
|
|
>(&mut __map) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
},
|
|
);
|
|
}
|
|
__Field::__field4 => {
|
|
if _serde::__private::Option::is_some(&__field4) {
|
|
return _serde::__private::Err(
|
|
<__A::Error as _serde::de::Error>::duplicate_field(
|
|
"outer_gap_size",
|
|
),
|
|
);
|
|
}
|
|
__field4 = _serde::__private::Some(
|
|
match _serde::de::MapAccess::next_value::<
|
|
Option<u16>,
|
|
>(&mut __map) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
},
|
|
);
|
|
}
|
|
__Field::__field5 => {
|
|
if _serde::__private::Option::is_some(&__field5) {
|
|
return _serde::__private::Err(
|
|
<__A::Error as _serde::de::Error>::duplicate_field("msg"),
|
|
);
|
|
}
|
|
__field5 = _serde::__private::Some(
|
|
match _serde::de::MapAccess::next_value::<
|
|
Option<XcrabMsgConfig>,
|
|
>(&mut __map) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
},
|
|
);
|
|
}
|
|
__Field::__field6 => {
|
|
if _serde::__private::Option::is_some(&__field6) {
|
|
return _serde::__private::Err(
|
|
<__A::Error as _serde::de::Error>::duplicate_field("binds"),
|
|
);
|
|
}
|
|
__field6 = _serde::__private::Some(
|
|
match _serde::de::MapAccess::next_value::<
|
|
HashMap<Keybind, Action>,
|
|
>(&mut __map) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
},
|
|
);
|
|
}
|
|
_ => {
|
|
let _ = match _serde::de::MapAccess::next_value::<
|
|
_serde::de::IgnoredAny,
|
|
>(&mut __map) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
};
|
|
}
|
|
}
|
|
}
|
|
let __field0 = match __field0 {
|
|
_serde::__private::Some(__field0) => __field0,
|
|
_serde::__private::None => {
|
|
match _serde::__private::de::missing_field("border_color") {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
let __field1 = match __field1 {
|
|
_serde::__private::Some(__field1) => __field1,
|
|
_serde::__private::None => {
|
|
match _serde::__private::de::missing_field(
|
|
"focused_color",
|
|
) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
let __field2 = match __field2 {
|
|
_serde::__private::Some(__field2) => __field2,
|
|
_serde::__private::None => {
|
|
match _serde::__private::de::missing_field("border_size") {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
let __field3 = match __field3 {
|
|
_serde::__private::Some(__field3) => __field3,
|
|
_serde::__private::None => {
|
|
match _serde::__private::de::missing_field("gap_size") {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
let __field4 = match __field4 {
|
|
_serde::__private::Some(__field4) => __field4,
|
|
_serde::__private::None => {
|
|
match _serde::__private::de::missing_field(
|
|
"outer_gap_size",
|
|
) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
let __field5 = match __field5 {
|
|
_serde::__private::Some(__field5) => __field5,
|
|
_serde::__private::None => {
|
|
match _serde::__private::de::missing_field("msg") {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
let __field6 = match __field6 {
|
|
_serde::__private::Some(__field6) => __field6,
|
|
_serde::__private::None => {
|
|
_serde::__private::Default::default()
|
|
}
|
|
};
|
|
_serde::__private::Ok(XcrabConfig {
|
|
border_color: __field0,
|
|
focused_color: __field1,
|
|
border_size: __field2,
|
|
gap_size: __field3,
|
|
outer_gap_size: __field4,
|
|
msg: __field5,
|
|
binds: __field6,
|
|
})
|
|
}
|
|
}
|
|
const FIELDS: &'static [&'static str] = &[
|
|
"border_color",
|
|
"focused_color",
|
|
"border_size",
|
|
"gap_size",
|
|
"outer_gap_size",
|
|
"msg",
|
|
"binds",
|
|
];
|
|
_serde::Deserializer::deserialize_struct(
|
|
__deserializer,
|
|
"XcrabConfig",
|
|
FIELDS,
|
|
__Visitor {
|
|
marker: _serde::__private::PhantomData::<XcrabConfig>,
|
|
lifetime: _serde::__private::PhantomData,
|
|
},
|
|
)
|
|
}
|
|
}
|
|
};
|
|
pub struct XcrabMsgConfig {
|
|
pub socket_path: PathBuf,
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::clone::Clone for XcrabMsgConfig {
|
|
#[inline]
|
|
fn clone(&self) -> XcrabMsgConfig {
|
|
XcrabMsgConfig {
|
|
socket_path: ::core::clone::Clone::clone(&self.socket_path),
|
|
}
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::fmt::Debug for XcrabMsgConfig {
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
|
::core::fmt::Formatter::debug_struct_field1_finish(
|
|
f,
|
|
"XcrabMsgConfig",
|
|
"socket_path",
|
|
&&self.socket_path,
|
|
)
|
|
}
|
|
}
|
|
#[doc(hidden)]
|
|
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
|
|
const _: () = {
|
|
#[allow(unused_extern_crates, clippy::useless_attribute)]
|
|
extern crate serde as _serde;
|
|
#[automatically_derived]
|
|
impl<'de> _serde::Deserialize<'de> for XcrabMsgConfig {
|
|
fn deserialize<__D>(
|
|
__deserializer: __D,
|
|
) -> _serde::__private::Result<Self, __D::Error>
|
|
where
|
|
__D: _serde::Deserializer<'de>,
|
|
{
|
|
#[allow(non_camel_case_types)]
|
|
enum __Field {
|
|
__field0,
|
|
__ignore,
|
|
}
|
|
struct __FieldVisitor;
|
|
impl<'de> _serde::de::Visitor<'de> for __FieldVisitor {
|
|
type Value = __Field;
|
|
fn expecting(
|
|
&self,
|
|
__formatter: &mut _serde::__private::Formatter,
|
|
) -> _serde::__private::fmt::Result {
|
|
_serde::__private::Formatter::write_str(
|
|
__formatter,
|
|
"field identifier",
|
|
)
|
|
}
|
|
fn visit_u64<__E>(
|
|
self,
|
|
__value: u64,
|
|
) -> _serde::__private::Result<Self::Value, __E>
|
|
where
|
|
__E: _serde::de::Error,
|
|
{
|
|
match __value {
|
|
0u64 => _serde::__private::Ok(__Field::__field0),
|
|
_ => _serde::__private::Ok(__Field::__ignore),
|
|
}
|
|
}
|
|
fn visit_str<__E>(
|
|
self,
|
|
__value: &str,
|
|
) -> _serde::__private::Result<Self::Value, __E>
|
|
where
|
|
__E: _serde::de::Error,
|
|
{
|
|
match __value {
|
|
"socket_path" => _serde::__private::Ok(__Field::__field0),
|
|
_ => _serde::__private::Ok(__Field::__ignore),
|
|
}
|
|
}
|
|
fn visit_bytes<__E>(
|
|
self,
|
|
__value: &[u8],
|
|
) -> _serde::__private::Result<Self::Value, __E>
|
|
where
|
|
__E: _serde::de::Error,
|
|
{
|
|
match __value {
|
|
b"socket_path" => _serde::__private::Ok(__Field::__field0),
|
|
_ => _serde::__private::Ok(__Field::__ignore),
|
|
}
|
|
}
|
|
}
|
|
impl<'de> _serde::Deserialize<'de> for __Field {
|
|
#[inline]
|
|
fn deserialize<__D>(
|
|
__deserializer: __D,
|
|
) -> _serde::__private::Result<Self, __D::Error>
|
|
where
|
|
__D: _serde::Deserializer<'de>,
|
|
{
|
|
_serde::Deserializer::deserialize_identifier(
|
|
__deserializer,
|
|
__FieldVisitor,
|
|
)
|
|
}
|
|
}
|
|
struct __Visitor<'de> {
|
|
marker: _serde::__private::PhantomData<XcrabMsgConfig>,
|
|
lifetime: _serde::__private::PhantomData<&'de ()>,
|
|
}
|
|
impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> {
|
|
type Value = XcrabMsgConfig;
|
|
fn expecting(
|
|
&self,
|
|
__formatter: &mut _serde::__private::Formatter,
|
|
) -> _serde::__private::fmt::Result {
|
|
_serde::__private::Formatter::write_str(
|
|
__formatter,
|
|
"struct XcrabMsgConfig",
|
|
)
|
|
}
|
|
#[inline]
|
|
fn visit_seq<__A>(
|
|
self,
|
|
mut __seq: __A,
|
|
) -> _serde::__private::Result<Self::Value, __A::Error>
|
|
where
|
|
__A: _serde::de::SeqAccess<'de>,
|
|
{
|
|
let __field0 = match match _serde::de::SeqAccess::next_element::<
|
|
PathBuf,
|
|
>(&mut __seq) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
} {
|
|
_serde::__private::Some(__value) => __value,
|
|
_serde::__private::None => {
|
|
return _serde::__private::Err(
|
|
_serde::de::Error::invalid_length(
|
|
0usize,
|
|
&"struct XcrabMsgConfig with 1 element",
|
|
),
|
|
);
|
|
}
|
|
};
|
|
_serde::__private::Ok(XcrabMsgConfig {
|
|
socket_path: __field0,
|
|
})
|
|
}
|
|
#[inline]
|
|
fn visit_map<__A>(
|
|
self,
|
|
mut __map: __A,
|
|
) -> _serde::__private::Result<Self::Value, __A::Error>
|
|
where
|
|
__A: _serde::de::MapAccess<'de>,
|
|
{
|
|
let mut __field0: _serde::__private::Option<PathBuf> = _serde::__private::None;
|
|
while let _serde::__private::Some(__key)
|
|
= match _serde::de::MapAccess::next_key::<
|
|
__Field,
|
|
>(&mut __map) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
} {
|
|
match __key {
|
|
__Field::__field0 => {
|
|
if _serde::__private::Option::is_some(&__field0) {
|
|
return _serde::__private::Err(
|
|
<__A::Error as _serde::de::Error>::duplicate_field(
|
|
"socket_path",
|
|
),
|
|
);
|
|
}
|
|
__field0 = _serde::__private::Some(
|
|
match _serde::de::MapAccess::next_value::<
|
|
PathBuf,
|
|
>(&mut __map) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
},
|
|
);
|
|
}
|
|
_ => {
|
|
let _ = match _serde::de::MapAccess::next_value::<
|
|
_serde::de::IgnoredAny,
|
|
>(&mut __map) {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
};
|
|
}
|
|
}
|
|
}
|
|
let __field0 = match __field0 {
|
|
_serde::__private::Some(__field0) => __field0,
|
|
_serde::__private::None => {
|
|
match _serde::__private::de::missing_field("socket_path") {
|
|
_serde::__private::Ok(__val) => __val,
|
|
_serde::__private::Err(__err) => {
|
|
return _serde::__private::Err(__err);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
_serde::__private::Ok(XcrabMsgConfig {
|
|
socket_path: __field0,
|
|
})
|
|
}
|
|
}
|
|
const FIELDS: &'static [&'static str] = &["socket_path"];
|
|
_serde::Deserializer::deserialize_struct(
|
|
__deserializer,
|
|
"XcrabMsgConfig",
|
|
FIELDS,
|
|
__Visitor {
|
|
marker: _serde::__private::PhantomData::<XcrabMsgConfig>,
|
|
lifetime: _serde::__private::PhantomData,
|
|
},
|
|
)
|
|
}
|
|
}
|
|
};
|
|
const DEFAULT_BORDER_COLOR: u32 = 0xff_00_00;
|
|
const DEFAULT_FOCUSED_COLOR: u32 = 0x00_00_ff;
|
|
const DEFAULT_BORDER_SIZE: u16 = 5;
|
|
const DEFAULT_GAP_SIZE: u16 = 20;
|
|
impl Default for XcrabConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
border_color: Some(DEFAULT_BORDER_COLOR),
|
|
focused_color: Some(DEFAULT_FOCUSED_COLOR),
|
|
border_size: Some(DEFAULT_BORDER_SIZE),
|
|
gap_size: Some(DEFAULT_GAP_SIZE),
|
|
outer_gap_size: None,
|
|
msg: Some(XcrabMsgConfig::default()),
|
|
binds: HashMap::new(),
|
|
}
|
|
}
|
|
}
|
|
impl Default for XcrabMsgConfig {
|
|
fn default() -> Self {
|
|
let home_dir = get_home().expect("Error: $HOME variable not set");
|
|
Self {
|
|
socket_path: {
|
|
let res = ::alloc::fmt::format(
|
|
format_args!("{0}/.config/xcrab/msg.sock", home_dir),
|
|
);
|
|
res
|
|
}
|
|
.into(),
|
|
}
|
|
}
|
|
}
|
|
impl XcrabConfig {
|
|
pub fn border_color(&self) -> u32 {
|
|
self.border_color.unwrap_or(DEFAULT_BORDER_COLOR)
|
|
}
|
|
pub fn focused_color(&self) -> u32 {
|
|
self.focused_color.unwrap_or(DEFAULT_FOCUSED_COLOR)
|
|
}
|
|
pub fn border_size(&self) -> u16 {
|
|
self.border_size.unwrap_or(DEFAULT_BORDER_SIZE)
|
|
}
|
|
pub fn gap_size(&self) -> u16 {
|
|
self.gap_size.unwrap_or(DEFAULT_GAP_SIZE)
|
|
}
|
|
pub fn outer_gap_size(&self) -> u16 {
|
|
self.outer_gap_size.unwrap_or_else(|| self.gap_size())
|
|
}
|
|
}
|
|
pub fn load_file() -> Result<XcrabConfig> {
|
|
let home_dir = get_home()?;
|
|
let contents = std::fs::read_to_string({
|
|
let res = ::alloc::fmt::format(
|
|
format_args!("{0}/.config/xcrab/config.toml", home_dir),
|
|
);
|
|
res
|
|
})?;
|
|
let config: XcrabConfig = toml::from_str(&contents)?;
|
|
Ok(config)
|
|
}
|
|
fn get_home() -> Result<String> {
|
|
Ok(std::env::var("HOME")?)
|
|
}
|
|
struct ActionVisitor;
|
|
impl<'de> Visitor<'de> for ActionVisitor {
|
|
type Value = Action;
|
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
formatter.write_str("a valid WM action")
|
|
}
|
|
fn visit_str<E: serde::de::Error>(
|
|
self,
|
|
value: &str,
|
|
) -> std::result::Result<Self::Value, E> {
|
|
value.parse().map_err(|s| E::custom(s))
|
|
}
|
|
}
|
|
impl<'de> Deserialize<'de> for Action {
|
|
fn deserialize<D: Deserializer<'de>>(
|
|
deserializer: D,
|
|
) -> std::result::Result<Self, D::Error> {
|
|
deserializer.deserialize_str(ActionVisitor)
|
|
}
|
|
}
|
|
pub struct Keybind {
|
|
pub key: char,
|
|
pub mods: KeyButMask,
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::clone::Clone for Keybind {
|
|
#[inline]
|
|
fn clone(&self) -> Keybind {
|
|
let _: ::core::clone::AssertParamIsClone<char>;
|
|
let _: ::core::clone::AssertParamIsClone<KeyButMask>;
|
|
*self
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::marker::Copy for Keybind {}
|
|
#[automatically_derived]
|
|
impl ::core::fmt::Debug for Keybind {
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
|
::core::fmt::Formatter::debug_struct_field2_finish(
|
|
f,
|
|
"Keybind",
|
|
"key",
|
|
&self.key,
|
|
"mods",
|
|
&&self.mods,
|
|
)
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::hash::Hash for Keybind {
|
|
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
|
|
::core::hash::Hash::hash(&self.key, state);
|
|
::core::hash::Hash::hash(&self.mods, state)
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::marker::StructuralPartialEq for Keybind {}
|
|
#[automatically_derived]
|
|
impl ::core::cmp::PartialEq for Keybind {
|
|
#[inline]
|
|
fn eq(&self, other: &Keybind) -> bool {
|
|
self.key == other.key && self.mods == other.mods
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::marker::StructuralEq for Keybind {}
|
|
#[automatically_derived]
|
|
impl ::core::cmp::Eq for Keybind {
|
|
#[inline]
|
|
#[doc(hidden)]
|
|
#[no_coverage]
|
|
fn assert_receiver_is_total_eq(&self) -> () {
|
|
let _: ::core::cmp::AssertParamIsEq<char>;
|
|
let _: ::core::cmp::AssertParamIsEq<KeyButMask>;
|
|
}
|
|
}
|
|
struct KeybindVisitor;
|
|
impl<'de> Visitor<'de> for KeybindVisitor {
|
|
type Value = Keybind;
|
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
formatter.write_str("a keybind in the form of 'M-x'")
|
|
}
|
|
fn visit_str<E: serde::de::Error>(
|
|
self,
|
|
value: &str,
|
|
) -> std::result::Result<Self::Value, E> {
|
|
let mut mask = KeyButMask::default();
|
|
for part in value.split('-') {
|
|
if part.len() > 1 {
|
|
return Err(E::custom("parts may only contain one character"));
|
|
}
|
|
let c = part
|
|
.chars()
|
|
.next()
|
|
.ok_or_else(|| E::custom(
|
|
"parts must contain at least one character",
|
|
))?;
|
|
if c.is_ascii_uppercase() {
|
|
match c {
|
|
'C' => mask.set_control(true),
|
|
'S' => mask.set_shift(true),
|
|
'A' => mask.set_mod1(true),
|
|
'W' => mask.set_mod4(true),
|
|
_ => {
|
|
return Err(
|
|
E::custom({
|
|
let res = ::alloc::fmt::format(
|
|
format_args!("no such modifier: {0}", c),
|
|
);
|
|
res
|
|
}),
|
|
);
|
|
}
|
|
};
|
|
}
|
|
}
|
|
let c = value
|
|
.split('-')
|
|
.flat_map(str::chars)
|
|
.find(char::is_ascii_lowercase)
|
|
.ok_or_else(|| E::custom("must specify one normal key"))?
|
|
.to_ascii_uppercase();
|
|
Ok(Keybind { key: c, mods: mask })
|
|
}
|
|
}
|
|
impl<'de> Deserialize<'de> for Keybind {
|
|
fn deserialize<D: Deserializer<'de>>(
|
|
deserializer: D,
|
|
) -> std::result::Result<Self, D::Error> {
|
|
deserializer.deserialize_str(KeybindVisitor)
|
|
}
|
|
}
|
|
}
|
|
mod msg_listener {
|
|
use crate::x11::client::XcrabWindowManager;
|
|
use crate::Result;
|
|
use breadx::AsyncDisplay;
|
|
use std::path::Path;
|
|
use std::str::FromStr;
|
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
use tokio::net::UnixListener;
|
|
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
|
|
use crate::XcrabError;
|
|
pub async fn listener_task<P: AsRef<Path>>(
|
|
socket_path: P,
|
|
sender: UnboundedSender<String>,
|
|
mut result_recv: UnboundedReceiver<Result<()>>,
|
|
) -> Result<()> {
|
|
let socket_path = socket_path.as_ref();
|
|
if socket_path.exists() {
|
|
std::fs::remove_file(socket_path)?;
|
|
}
|
|
let listener = UnixListener::bind(socket_path)?;
|
|
loop {
|
|
let (mut stream, _) = match listener.accept().await {
|
|
Ok(v) => v,
|
|
Err(_) => continue,
|
|
};
|
|
let mut buf = String::new();
|
|
stream.read_to_string(&mut buf).await?;
|
|
drop(sender.send(buf));
|
|
if let Err(e) = result_recv.recv().await.unwrap() {
|
|
stream
|
|
.write_all(
|
|
{
|
|
let res = ::alloc::fmt::format(format_args!("{0}", e));
|
|
res
|
|
}
|
|
.as_bytes(),
|
|
)
|
|
.await?;
|
|
} else {
|
|
stream.write_all(&[]).await?;
|
|
}
|
|
}
|
|
}
|
|
pub async fn on_recv<Dpy: AsyncDisplay + ?Sized>(
|
|
data: String,
|
|
manager: &mut XcrabWindowManager,
|
|
conn: &mut Dpy,
|
|
result_sender: &UnboundedSender<Result<()>>,
|
|
) -> Result<()> {
|
|
let res = { data.parse::<Action>() };
|
|
if let Ok(ref a) = res {
|
|
a.eval(manager, conn).await?;
|
|
}
|
|
drop(result_sender.send(res.map(|_| ())));
|
|
Ok(())
|
|
}
|
|
#[non_exhaustive]
|
|
pub enum Action {
|
|
Close,
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::fmt::Debug for Action {
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
|
::core::fmt::Formatter::write_str(f, "Close")
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::clone::Clone for Action {
|
|
#[inline]
|
|
fn clone(&self) -> Action {
|
|
Action::Close
|
|
}
|
|
}
|
|
impl FromStr for Action {
|
|
type Err = XcrabError;
|
|
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
|
#[allow(clippy::enum_glob_use)]
|
|
use Action::*;
|
|
let parts: Vec<String> = s
|
|
.split(' ')
|
|
.map(str::to_ascii_lowercase)
|
|
.filter(|s| !s.is_empty())
|
|
.collect();
|
|
if parts.is_empty() {
|
|
return Err(XcrabError::Custom(String::from("No action provided")));
|
|
}
|
|
if parts[0].eq_ignore_ascii_case("close") {
|
|
Ok(Close)
|
|
} else {
|
|
Err(
|
|
XcrabError::Custom({
|
|
let res = ::alloc::fmt::format(
|
|
format_args!("Unknown action: {0}", s),
|
|
);
|
|
res
|
|
}),
|
|
)
|
|
}
|
|
}
|
|
}
|
|
impl Action {
|
|
pub async fn eval<Dpy: AsyncDisplay + ?Sized>(
|
|
&self,
|
|
manager: &mut XcrabWindowManager,
|
|
conn: &mut Dpy,
|
|
) -> Result<()> {
|
|
#[allow(clippy::enum_glob_use)]
|
|
use Action::*;
|
|
match self {
|
|
Close => manager.destroy_focused_client(conn).await?,
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
mod x11 {
|
|
pub mod client {
|
|
use breadx::auto::xproto::{KeyButMask, Keycode, Keysym};
|
|
use breadx::{
|
|
auto::xproto::{ClientMessageEvent, InputFocus, SetInputFocusRequest},
|
|
client_message_data::ClientMessageData,
|
|
prelude::{AsByteSequence, AsyncDisplayXprotoExt, PropertyType, SetMode},
|
|
AsyncDisplay, AsyncDisplayExt, Atom, BreadError, ConfigureWindowParameters,
|
|
ErrorCode, Event, EventMask, KeyboardState, Window, WindowParameters, XidType,
|
|
};
|
|
use slotmap::{new_key_type, SlotMap};
|
|
use std::{collections::HashMap, future::Future, pin::Pin, slice};
|
|
use crate::{Result, XcrabError, CONFIG};
|
|
pub enum Direction {
|
|
Up,
|
|
Down,
|
|
Left,
|
|
Right,
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::fmt::Debug for Direction {
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
|
::core::fmt::Formatter::write_str(
|
|
f,
|
|
match self {
|
|
Direction::Up => "Up",
|
|
Direction::Down => "Down",
|
|
Direction::Left => "Left",
|
|
Direction::Right => "Right",
|
|
},
|
|
)
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::clone::Clone for Direction {
|
|
#[inline]
|
|
fn clone(&self) -> Direction {
|
|
*self
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::marker::Copy for Direction {}
|
|
pub enum Directionality {
|
|
Horizontal,
|
|
Vertical,
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::fmt::Debug for Directionality {
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
|
::core::fmt::Formatter::write_str(
|
|
f,
|
|
match self {
|
|
Directionality::Horizontal => "Horizontal",
|
|
Directionality::Vertical => "Vertical",
|
|
},
|
|
)
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::clone::Clone for Directionality {
|
|
#[inline]
|
|
fn clone(&self) -> Directionality {
|
|
*self
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::marker::Copy for Directionality {}
|
|
#[automatically_derived]
|
|
impl ::core::marker::StructuralPartialEq for Directionality {}
|
|
#[automatically_derived]
|
|
impl ::core::cmp::PartialEq for Directionality {
|
|
#[inline]
|
|
fn eq(&self, other: &Directionality) -> bool {
|
|
let __self_tag = ::core::intrinsics::discriminant_value(self);
|
|
let __arg1_tag = ::core::intrinsics::discriminant_value(other);
|
|
__self_tag == __arg1_tag
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::marker::StructuralEq for Directionality {}
|
|
#[automatically_derived]
|
|
impl ::core::cmp::Eq for Directionality {
|
|
#[inline]
|
|
#[doc(hidden)]
|
|
#[no_coverage]
|
|
fn assert_receiver_is_total_eq(&self) -> () {}
|
|
}
|
|
pub struct Dimensions {
|
|
x: u16,
|
|
y: u16,
|
|
width: u16,
|
|
height: u16,
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::fmt::Debug for Dimensions {
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
|
::core::fmt::Formatter::debug_struct_field4_finish(
|
|
f,
|
|
"Dimensions",
|
|
"x",
|
|
&self.x,
|
|
"y",
|
|
&self.y,
|
|
"width",
|
|
&self.width,
|
|
"height",
|
|
&&self.height,
|
|
)
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::clone::Clone for Dimensions {
|
|
#[inline]
|
|
fn clone(&self) -> Dimensions {
|
|
let _: ::core::clone::AssertParamIsClone<u16>;
|
|
*self
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::marker::Copy for Dimensions {}
|
|
#[automatically_derived]
|
|
impl ::core::default::Default for Dimensions {
|
|
#[inline]
|
|
fn default() -> Dimensions {
|
|
Dimensions {
|
|
x: ::core::default::Default::default(),
|
|
y: ::core::default::Default::default(),
|
|
width: ::core::default::Default::default(),
|
|
height: ::core::default::Default::default(),
|
|
}
|
|
}
|
|
}
|
|
impl Dimensions {
|
|
fn split(self, direction: Directionality, count: usize) -> Vec<Self> {
|
|
let count_u16 = u16::try_from(count).unwrap();
|
|
match direction {
|
|
Directionality::Horizontal => {
|
|
let amount_for_windows = self.width
|
|
- CONFIG.gap_size() * (count_u16 - 1);
|
|
let excess = amount_for_windows % count_u16;
|
|
let window_size = amount_for_windows / count_u16;
|
|
let window_stride = window_size + CONFIG.gap_size();
|
|
(0..count.try_into().unwrap())
|
|
.map(|i| Dimensions {
|
|
x: self.x + i * window_stride
|
|
+ if i < excess { 1 } else { 0 },
|
|
width: window_size,
|
|
..self
|
|
})
|
|
.collect()
|
|
}
|
|
Directionality::Vertical => {
|
|
let amount_for_windows = self.height
|
|
- CONFIG.gap_size() * (count_u16 - 1);
|
|
let excess = amount_for_windows % count_u16;
|
|
let window_size = amount_for_windows / count_u16;
|
|
let window_stride = window_size + CONFIG.gap_size();
|
|
(0..count.try_into().unwrap())
|
|
.map(|i| Dimensions {
|
|
y: self.y + i * window_stride
|
|
+ if i < excess { 1 } else { 0 },
|
|
height: window_size,
|
|
..self
|
|
})
|
|
.collect()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#[repr(transparent)]
|
|
struct XcrabKey(::slotmap::KeyData);
|
|
#[automatically_derived]
|
|
impl ::core::marker::Copy for XcrabKey {}
|
|
#[automatically_derived]
|
|
impl ::core::clone::Clone for XcrabKey {
|
|
#[inline]
|
|
fn clone(&self) -> XcrabKey {
|
|
let _: ::core::clone::AssertParamIsClone<::slotmap::KeyData>;
|
|
*self
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::default::Default for XcrabKey {
|
|
#[inline]
|
|
fn default() -> XcrabKey {
|
|
XcrabKey(::core::default::Default::default())
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::marker::StructuralEq for XcrabKey {}
|
|
#[automatically_derived]
|
|
impl ::core::cmp::Eq for XcrabKey {
|
|
#[inline]
|
|
#[doc(hidden)]
|
|
#[no_coverage]
|
|
fn assert_receiver_is_total_eq(&self) -> () {
|
|
let _: ::core::cmp::AssertParamIsEq<::slotmap::KeyData>;
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::marker::StructuralPartialEq for XcrabKey {}
|
|
#[automatically_derived]
|
|
impl ::core::cmp::PartialEq for XcrabKey {
|
|
#[inline]
|
|
fn eq(&self, other: &XcrabKey) -> bool {
|
|
self.0 == other.0
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::cmp::Ord for XcrabKey {
|
|
#[inline]
|
|
fn cmp(&self, other: &XcrabKey) -> ::core::cmp::Ordering {
|
|
::core::cmp::Ord::cmp(&self.0, &other.0)
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::cmp::PartialOrd for XcrabKey {
|
|
#[inline]
|
|
fn partial_cmp(
|
|
&self,
|
|
other: &XcrabKey,
|
|
) -> ::core::option::Option<::core::cmp::Ordering> {
|
|
::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0)
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::hash::Hash for XcrabKey {
|
|
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
|
|
::core::hash::Hash::hash(&self.0, state)
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::fmt::Debug for XcrabKey {
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
|
::core::fmt::Formatter::debug_tuple_field1_finish(
|
|
f,
|
|
"XcrabKey",
|
|
&&self.0,
|
|
)
|
|
}
|
|
}
|
|
impl From<::slotmap::KeyData> for XcrabKey {
|
|
fn from(k: ::slotmap::KeyData) -> Self {
|
|
XcrabKey(k)
|
|
}
|
|
}
|
|
unsafe impl ::slotmap::Key for XcrabKey {
|
|
fn data(&self) -> ::slotmap::KeyData {
|
|
self.0
|
|
}
|
|
}
|
|
pub struct XcrabWindowManager {
|
|
clients: HashMap<Window, XcrabKey>,
|
|
rects: SlotMap<XcrabKey, Rectangle>,
|
|
focused: Option<Window>,
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::fmt::Debug for XcrabWindowManager {
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
|
::core::fmt::Formatter::debug_struct_field3_finish(
|
|
f,
|
|
"XcrabWindowManager",
|
|
"clients",
|
|
&self.clients,
|
|
"rects",
|
|
&self.rects,
|
|
"focused",
|
|
&&self.focused,
|
|
)
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::clone::Clone for XcrabWindowManager {
|
|
#[inline]
|
|
fn clone(&self) -> XcrabWindowManager {
|
|
XcrabWindowManager {
|
|
clients: ::core::clone::Clone::clone(&self.clients),
|
|
rects: ::core::clone::Clone::clone(&self.rects),
|
|
focused: ::core::clone::Clone::clone(&self.focused),
|
|
}
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::default::Default for XcrabWindowManager {
|
|
#[inline]
|
|
fn default() -> XcrabWindowManager {
|
|
XcrabWindowManager {
|
|
clients: ::core::default::Default::default(),
|
|
rects: ::core::default::Default::default(),
|
|
focused: ::core::default::Default::default(),
|
|
}
|
|
}
|
|
}
|
|
struct Rectangle {
|
|
parent: XcrabKey,
|
|
cached_dimensions: Dimensions,
|
|
contents: RectangleContents,
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::fmt::Debug for Rectangle {
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
|
::core::fmt::Formatter::debug_struct_field3_finish(
|
|
f,
|
|
"Rectangle",
|
|
"parent",
|
|
&self.parent,
|
|
"cached_dimensions",
|
|
&self.cached_dimensions,
|
|
"contents",
|
|
&&self.contents,
|
|
)
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::clone::Clone for Rectangle {
|
|
#[inline]
|
|
fn clone(&self) -> Rectangle {
|
|
Rectangle {
|
|
parent: ::core::clone::Clone::clone(&self.parent),
|
|
cached_dimensions: ::core::clone::Clone::clone(
|
|
&self.cached_dimensions,
|
|
),
|
|
contents: ::core::clone::Clone::clone(&self.contents),
|
|
}
|
|
}
|
|
}
|
|
impl Rectangle {
|
|
fn unwrap_pane(&self) -> &Pane {
|
|
match &self.contents {
|
|
RectangleContents::Pane(pane) => pane,
|
|
RectangleContents::Client(_) => {
|
|
::core::panicking::panic(
|
|
"internal error: entered unreachable code",
|
|
)
|
|
}
|
|
}
|
|
}
|
|
fn unwrap_client(&self) -> &Client {
|
|
match &self.contents {
|
|
RectangleContents::Pane(_) => {
|
|
::core::panicking::panic(
|
|
"internal error: entered unreachable code",
|
|
)
|
|
}
|
|
RectangleContents::Client(client) => client,
|
|
}
|
|
}
|
|
fn unwrap_pane_mut(&mut self) -> &mut Pane {
|
|
match &mut self.contents {
|
|
RectangleContents::Pane(pane) => pane,
|
|
RectangleContents::Client(_) => {
|
|
::core::panicking::panic(
|
|
"internal error: entered unreachable code",
|
|
)
|
|
}
|
|
}
|
|
}
|
|
fn unwrap_client_mut(&mut self) -> &mut Client {
|
|
match &mut self.contents {
|
|
RectangleContents::Pane(_) => {
|
|
::core::panicking::panic(
|
|
"internal error: entered unreachable code",
|
|
)
|
|
}
|
|
RectangleContents::Client(client) => client,
|
|
}
|
|
}
|
|
}
|
|
enum RectangleContents {
|
|
Pane(Pane),
|
|
Client(Client),
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::fmt::Debug for RectangleContents {
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
|
match self {
|
|
RectangleContents::Pane(__self_0) => {
|
|
::core::fmt::Formatter::debug_tuple_field1_finish(
|
|
f,
|
|
"Pane",
|
|
&__self_0,
|
|
)
|
|
}
|
|
RectangleContents::Client(__self_0) => {
|
|
::core::fmt::Formatter::debug_tuple_field1_finish(
|
|
f,
|
|
"Client",
|
|
&__self_0,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::clone::Clone for RectangleContents {
|
|
#[inline]
|
|
fn clone(&self) -> RectangleContents {
|
|
match self {
|
|
RectangleContents::Pane(__self_0) => {
|
|
RectangleContents::Pane(::core::clone::Clone::clone(__self_0))
|
|
}
|
|
RectangleContents::Client(__self_0) => {
|
|
RectangleContents::Client(::core::clone::Clone::clone(__self_0))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
struct Pane {
|
|
children: Vec<XcrabKey>,
|
|
directionality: Directionality,
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::fmt::Debug for Pane {
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
|
::core::fmt::Formatter::debug_struct_field2_finish(
|
|
f,
|
|
"Pane",
|
|
"children",
|
|
&self.children,
|
|
"directionality",
|
|
&&self.directionality,
|
|
)
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::clone::Clone for Pane {
|
|
#[inline]
|
|
fn clone(&self) -> Pane {
|
|
Pane {
|
|
children: ::core::clone::Clone::clone(&self.children),
|
|
directionality: ::core::clone::Clone::clone(&self.directionality),
|
|
}
|
|
}
|
|
}
|
|
struct Client {
|
|
frame: FramedWindow,
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::fmt::Debug for Client {
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
|
::core::fmt::Formatter::debug_struct_field1_finish(
|
|
f,
|
|
"Client",
|
|
"frame",
|
|
&&self.frame,
|
|
)
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::clone::Clone for Client {
|
|
#[inline]
|
|
fn clone(&self) -> Client {
|
|
let _: ::core::clone::AssertParamIsClone<FramedWindow>;
|
|
*self
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::marker::Copy for Client {}
|
|
impl XcrabWindowManager {
|
|
pub fn new() -> Self {
|
|
XcrabWindowManager::default()
|
|
}
|
|
/// Given the `rect_key` from a `parent -> rect` relationship, makes A
|
|
/// `parent -> new_pane -> rect` relationship, then returns `new_pane_key`
|
|
fn insert_pane_above(
|
|
&mut self,
|
|
rect_key: XcrabKey,
|
|
directionality: Directionality,
|
|
) -> Option<XcrabKey> {
|
|
let rect = self.rects.get(rect_key)?;
|
|
let rect_dimensions = rect.cached_dimensions;
|
|
let parent_key = rect.parent;
|
|
let new_pane = Rectangle {
|
|
parent: parent_key,
|
|
cached_dimensions: rect_dimensions,
|
|
contents: RectangleContents::Pane(Pane {
|
|
children: <[_]>::into_vec(
|
|
#[rustc_box]
|
|
::alloc::boxed::Box::new([rect_key]),
|
|
),
|
|
directionality,
|
|
}),
|
|
};
|
|
let new_pane_key = if parent_key == rect_key {
|
|
self.rects
|
|
.insert_with_key(|key| Rectangle {
|
|
parent: key,
|
|
..new_pane
|
|
})
|
|
} else {
|
|
let new_pane_key = self.rects.insert(new_pane);
|
|
let parent_pane = self
|
|
.rects
|
|
.get_mut(parent_key)
|
|
.unwrap()
|
|
.unwrap_pane_mut();
|
|
let index = parent_pane
|
|
.children
|
|
.iter()
|
|
.copied()
|
|
.position(|v| v == rect_key)
|
|
.unwrap();
|
|
parent_pane.children[index] = new_pane_key;
|
|
new_pane_key
|
|
};
|
|
let rect = self.rects.get_mut(rect_key).unwrap();
|
|
rect.parent = new_pane_key;
|
|
Some(new_pane_key)
|
|
}
|
|
async fn focus_update_map<Dpy: AsyncDisplay + ?Sized>(
|
|
&mut self,
|
|
conn: &mut Dpy,
|
|
frame: FramedWindow,
|
|
parent_key: XcrabKey,
|
|
) -> Result<()> {
|
|
let win = frame.win;
|
|
self.focused = Some(win);
|
|
self.update_rectangle(conn, parent_key, None).await?;
|
|
frame.map(conn).await?;
|
|
self.update_focused(conn).await?;
|
|
Ok(())
|
|
}
|
|
/// Tells x about the currently focused window.
|
|
pub async fn update_focused<Dpy: AsyncDisplay + ?Sized>(
|
|
&mut self,
|
|
conn: &mut Dpy,
|
|
) -> Result<()> {
|
|
let mut req = SetInputFocusRequest {
|
|
req_type: 42,
|
|
revert_to: InputFocus::None,
|
|
length: 3,
|
|
focus: Window::from_xid(0),
|
|
time: 0,
|
|
};
|
|
if let Some(focus) = self.focused {
|
|
req.focus = focus;
|
|
}
|
|
conn.exchange_request_async(req).await?;
|
|
Ok(())
|
|
}
|
|
/// Adds a new client.
|
|
pub async fn add_client<Dpy: AsyncDisplay + ?Sized>(
|
|
&mut self,
|
|
conn: &mut Dpy,
|
|
win: Window,
|
|
) -> Result<()> {
|
|
self.add_client_direction(conn, win, Direction::Right).await
|
|
}
|
|
/// Adds a new client in the given direction from the focused window.
|
|
pub async fn add_client_direction<Dpy: AsyncDisplay + ?Sized>(
|
|
&mut self,
|
|
conn: &mut Dpy,
|
|
win: Window,
|
|
direction: Direction,
|
|
) -> Result<()> {
|
|
#[allow(clippy::enum_glob_use)]
|
|
use {Direction::*, Directionality::*};
|
|
let focused = match self.focused {
|
|
Some(v) => v,
|
|
None => return self.add_first_client(conn, win).await,
|
|
};
|
|
let frame = frame(conn, win).await?;
|
|
let focused_client_key = *self
|
|
.clients
|
|
.get(&focused)
|
|
.ok_or(XcrabError::ClientDoesntExist)?;
|
|
let target_directionality = match direction {
|
|
Up | Down => Vertical,
|
|
Left | Right => Horizontal,
|
|
};
|
|
let mut child_key = focused_client_key;
|
|
let parent_key = loop {
|
|
let parent_key = self.rects.get(child_key).unwrap().parent;
|
|
if parent_key == child_key {
|
|
break self
|
|
.insert_pane_above(child_key, target_directionality)
|
|
.unwrap();
|
|
}
|
|
let parent = self.rects.get(parent_key).unwrap();
|
|
if parent.unwrap_pane().directionality == target_directionality {
|
|
break parent_key;
|
|
}
|
|
child_key = parent_key;
|
|
};
|
|
let new_rect_key = self
|
|
.rects
|
|
.insert(Rectangle {
|
|
parent: parent_key,
|
|
cached_dimensions: Dimensions::default(),
|
|
contents: RectangleContents::Client(Client { frame }),
|
|
});
|
|
let parent_pane = self
|
|
.rects
|
|
.get_mut(parent_key)
|
|
.unwrap()
|
|
.unwrap_pane_mut();
|
|
let mut index = parent_pane
|
|
.children
|
|
.iter()
|
|
.copied()
|
|
.position(|v| v == child_key)
|
|
.unwrap();
|
|
if let Down | Right = direction {
|
|
index += 1;
|
|
}
|
|
parent_pane.children.insert(index, new_rect_key);
|
|
self.clients.insert(win, new_rect_key);
|
|
self.focus_update_map(conn, frame, parent_key).await?;
|
|
Ok(())
|
|
}
|
|
/// Adds a new client in the given direction directly adjacent to the focused window, creating a new pane if needed.
|
|
pub async fn add_client_direction_immediate<Dpy: AsyncDisplay + ?Sized>(
|
|
&mut self,
|
|
conn: &mut Dpy,
|
|
win: Window,
|
|
direction: Direction,
|
|
) -> Result<()> {
|
|
#[allow(clippy::enum_glob_use)]
|
|
use {Direction::*, Directionality::*};
|
|
let focused = match self.focused {
|
|
Some(v) => v,
|
|
None => return self.add_first_client(conn, win).await,
|
|
};
|
|
let frame = frame(conn, win).await?;
|
|
let focused_client_key = *self.clients.get(&focused).unwrap();
|
|
let focused_client = self.rects.get(focused_client_key).unwrap();
|
|
let mut parent_key = focused_client.parent;
|
|
let parent_pane_dir = match &self.rects.get(parent_key).unwrap().contents
|
|
{
|
|
RectangleContents::Pane(pane) => Some(pane.directionality),
|
|
RectangleContents::Client(_) => None,
|
|
};
|
|
let target_directionality = match direction {
|
|
Up | Down => Vertical,
|
|
Left | Right => Horizontal,
|
|
};
|
|
if parent_pane_dir.is_none()
|
|
|| parent_pane_dir.unwrap() != target_directionality
|
|
{
|
|
parent_key = self
|
|
.insert_pane_above(focused_client_key, target_directionality)
|
|
.unwrap();
|
|
}
|
|
let new_rect_key = self
|
|
.rects
|
|
.insert(Rectangle {
|
|
parent: parent_key,
|
|
cached_dimensions: Dimensions::default(),
|
|
contents: RectangleContents::Client(Client { frame }),
|
|
});
|
|
let parent_pane = self
|
|
.rects
|
|
.get_mut(parent_key)
|
|
.unwrap()
|
|
.unwrap_pane_mut();
|
|
let mut index = parent_pane
|
|
.children
|
|
.iter()
|
|
.copied()
|
|
.position(|v| v == focused_client_key)
|
|
.unwrap();
|
|
if let Down | Right = direction {
|
|
index += 1;
|
|
}
|
|
parent_pane.children.insert(index, new_rect_key);
|
|
self.clients.insert(win, new_rect_key);
|
|
self.focus_update_map(conn, frame, parent_key).await?;
|
|
Ok(())
|
|
}
|
|
async fn add_first_client<Dpy: AsyncDisplay + ?Sized>(
|
|
&mut self,
|
|
conn: &mut Dpy,
|
|
win: Window,
|
|
) -> Result<()> {
|
|
let frame = frame(conn, win).await?;
|
|
let root_geo = conn.default_root().geometry_immediate_async(conn).await?;
|
|
let outer_gap_size = CONFIG.outer_gap_size();
|
|
let key = self
|
|
.rects
|
|
.insert_with_key(|key| Rectangle {
|
|
parent: key,
|
|
cached_dimensions: Dimensions {
|
|
x: u16::try_from(root_geo.x).unwrap() + outer_gap_size,
|
|
y: u16::try_from(root_geo.y).unwrap() + outer_gap_size,
|
|
width: root_geo.width - 2 * outer_gap_size,
|
|
height: root_geo.height - 2 * outer_gap_size,
|
|
},
|
|
contents: RectangleContents::Client(Client { frame }),
|
|
});
|
|
self.clients.insert(win, key);
|
|
self.focus_update_map(conn, frame, key).await?;
|
|
Ok(())
|
|
}
|
|
#[must_use]
|
|
fn update_rectangle<'a, Dpy: AsyncDisplay + ?Sized>(
|
|
&'a mut self,
|
|
conn: &'a mut Dpy,
|
|
key: XcrabKey,
|
|
dimensions: Option<Dimensions>,
|
|
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a>> {
|
|
Box::pin(async move {
|
|
let rect = self
|
|
.rects
|
|
.get_mut(key)
|
|
.ok_or(XcrabError::ClientDoesntExist)?;
|
|
let dimensions = dimensions.unwrap_or(rect.cached_dimensions);
|
|
rect.cached_dimensions = dimensions;
|
|
match &mut rect.contents {
|
|
RectangleContents::Pane(pane) => {
|
|
if !pane.children.is_empty() {
|
|
let new_dimensions = dimensions
|
|
.split(pane.directionality, pane.children.len());
|
|
for (key, dimensions) in pane
|
|
.children
|
|
.clone()
|
|
.into_iter()
|
|
.zip(new_dimensions.into_iter())
|
|
{
|
|
self.update_rectangle(conn, key, Some(dimensions)).await?;
|
|
}
|
|
}
|
|
}
|
|
RectangleContents::Client(client) => {
|
|
client
|
|
.frame
|
|
.configure(
|
|
conn,
|
|
ConfigureWindowParameters {
|
|
x: Some(dimensions.x.into()),
|
|
y: Some(dimensions.y.into()),
|
|
width: Some(dimensions.width.into()),
|
|
height: Some(dimensions.height.into()),
|
|
..Default::default()
|
|
},
|
|
self.focused.unwrap(),
|
|
)
|
|
.await?;
|
|
}
|
|
}
|
|
Ok(())
|
|
})
|
|
}
|
|
pub fn has_client(&self, win: Window) -> bool {
|
|
self.clients.contains_key(&win)
|
|
}
|
|
pub async fn remove_client<Dpy: AsyncDisplay + ?Sized>(
|
|
&mut self,
|
|
conn: &mut Dpy,
|
|
win: Window,
|
|
) -> Result<()> {
|
|
let client_key = *self
|
|
.clients
|
|
.get(&win)
|
|
.ok_or(XcrabError::ClientDoesntExist)?;
|
|
let client = self.rects.get(client_key).unwrap();
|
|
client.unwrap_client().frame.unframe(conn).await?;
|
|
let parent_key = client.parent;
|
|
let parent = self.rects.get_mut(parent_key).unwrap();
|
|
parent.unwrap_pane_mut().children.retain(|&v| v != client_key);
|
|
self.clients.remove(&win);
|
|
self.rects.remove(client_key);
|
|
if self.focused.unwrap() == win {
|
|
self.focused = self.clients.keys().copied().next();
|
|
self.update_focused(conn).await?;
|
|
}
|
|
self.update_rectangle(conn, parent_key, None).await?;
|
|
Ok(())
|
|
}
|
|
pub async fn destroy_focused_client<Dpy: AsyncDisplay + ?Sized>(
|
|
&mut self,
|
|
conn: &mut Dpy,
|
|
) -> Result<()> {
|
|
if let Some(focused) = self.focused {
|
|
let client_key = *self
|
|
.clients
|
|
.get(&focused)
|
|
.ok_or(XcrabError::ClientDoesntExist)?;
|
|
let frame = self
|
|
.rects
|
|
.get(client_key)
|
|
.unwrap()
|
|
.unwrap_client()
|
|
.frame;
|
|
self.remove_client(conn, focused).await?;
|
|
frame.kill_client(conn).await?;
|
|
Ok(())
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
pub async fn set_focus<Dpy: AsyncDisplay + ?Sized>(
|
|
&mut self,
|
|
conn: &mut Dpy,
|
|
win: Window,
|
|
) -> Result<()> {
|
|
let client_key = *self
|
|
.clients
|
|
.get(&win)
|
|
.ok_or(XcrabError::ClientDoesntExist)?;
|
|
self.focused = Some(win);
|
|
self.update_focused(conn).await?;
|
|
self.update_rectangle(
|
|
conn,
|
|
self.rects.get(client_key).unwrap().parent,
|
|
None,
|
|
)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
pub async fn get_focused(&self) -> Option<Window> {
|
|
self.focused
|
|
}
|
|
pub async fn get_framed_window(&self, window: Window) -> FramedWindow {
|
|
let focused_key = self.clients.get(&window).unwrap();
|
|
let focused = self.rects.get(*focused_key).unwrap();
|
|
let focused_frame = focused.unwrap_client().frame;
|
|
focused_frame
|
|
}
|
|
}
|
|
pub fn may_not_exist(res: breadx::Result) -> breadx::Result {
|
|
match res {
|
|
Err(BreadError::XProtocol { error_code: ErrorCode(3), .. }) => Ok(()),
|
|
v => v,
|
|
}
|
|
}
|
|
pub struct FramedWindow {
|
|
pub frame: Window,
|
|
pub win: Window,
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::fmt::Debug for FramedWindow {
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
|
::core::fmt::Formatter::debug_struct_field2_finish(
|
|
f,
|
|
"FramedWindow",
|
|
"frame",
|
|
&self.frame,
|
|
"win",
|
|
&&self.win,
|
|
)
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::clone::Clone for FramedWindow {
|
|
#[inline]
|
|
fn clone(&self) -> FramedWindow {
|
|
let _: ::core::clone::AssertParamIsClone<Window>;
|
|
*self
|
|
}
|
|
}
|
|
#[automatically_derived]
|
|
impl ::core::marker::Copy for FramedWindow {}
|
|
impl FramedWindow {
|
|
async fn configure<Dpy: AsyncDisplay + ?Sized>(
|
|
self,
|
|
conn: &mut Dpy,
|
|
props: ConfigureWindowParameters,
|
|
focused_win: Window,
|
|
) -> Result<()> {
|
|
let inset = 2 * u32::from(CONFIG.border_size());
|
|
let width = props.width.map(|v| v - inset);
|
|
let height = props.height.map(|v| v - inset);
|
|
let focused = focused_win == self.win;
|
|
self.frame
|
|
.change_attributes_async(
|
|
conn,
|
|
WindowParameters {
|
|
border_pixel: Some(
|
|
if focused {
|
|
CONFIG.focused_color()
|
|
} else {
|
|
CONFIG.border_color()
|
|
},
|
|
),
|
|
..Default::default()
|
|
},
|
|
)
|
|
.await?;
|
|
self.frame
|
|
.configure_async(
|
|
conn,
|
|
ConfigureWindowParameters {
|
|
x: props.x,
|
|
y: props.y,
|
|
width,
|
|
height,
|
|
border_width: Some(CONFIG.border_size().into()),
|
|
..Default::default()
|
|
},
|
|
)
|
|
.await?;
|
|
may_not_exist(
|
|
self
|
|
.win
|
|
.configure_async(
|
|
conn,
|
|
ConfigureWindowParameters {
|
|
x: Some(-1),
|
|
y: Some(-1),
|
|
width,
|
|
height,
|
|
..Default::default()
|
|
},
|
|
)
|
|
.await,
|
|
)?;
|
|
Ok(())
|
|
}
|
|
async fn map<Dpy: AsyncDisplay + ?Sized>(
|
|
self,
|
|
conn: &mut Dpy,
|
|
) -> Result<()> {
|
|
may_not_exist(self.win.map_async(conn).await)?;
|
|
self.frame.map_async(conn).await?;
|
|
Ok(())
|
|
}
|
|
async fn unframe<Dpy: AsyncDisplay + ?Sized>(
|
|
self,
|
|
conn: &mut Dpy,
|
|
) -> Result<()> {
|
|
let root = conn.default_root();
|
|
self.frame.unmap_async(conn).await?;
|
|
may_not_exist(self.win.unmap_async(conn).await)?;
|
|
may_not_exist(self.win.reparent_async(conn, root, 0, 0).await)?;
|
|
may_not_exist(
|
|
self.win.change_save_set_async(conn, SetMode::Delete).await,
|
|
)?;
|
|
self.frame.free_async(conn).await?;
|
|
Ok(())
|
|
}
|
|
async fn kill_client<Dpy: AsyncDisplay + ?Sized>(
|
|
self,
|
|
conn: &mut Dpy,
|
|
) -> Result<()> {
|
|
struct ListOfAtom(Vec<Atom>);
|
|
impl AsByteSequence for ListOfAtom {
|
|
fn size(&self) -> usize {
|
|
::core::panicking::panic("not implemented")
|
|
}
|
|
fn as_bytes(&self, _: &mut [u8]) -> usize {
|
|
::core::panicking::panic("not implemented")
|
|
}
|
|
fn from_bytes(mut bytes: &[u8]) -> Option<(Self, usize)> {
|
|
let mut index = 0;
|
|
let mut vec = Vec::new();
|
|
while let Some((atom, index2)) = Atom::from_bytes(bytes) {
|
|
vec.push(atom);
|
|
index += index2;
|
|
bytes = &bytes[index2..];
|
|
}
|
|
Some((Self(vec), index))
|
|
}
|
|
}
|
|
fn as_u8_slice(data: &[u32]) -> &[u8] {
|
|
unsafe {
|
|
slice::from_raw_parts(
|
|
data.as_ptr().cast::<u8>(),
|
|
data.len().checked_mul(4).unwrap(),
|
|
)
|
|
}
|
|
}
|
|
let wm_protocols = conn
|
|
.intern_atom_immediate_async("WM_PROTOCOLS", true)
|
|
.await?;
|
|
if !(wm_protocols.xid != 0) {
|
|
::core::panicking::panic("assertion failed: wm_protocols.xid != 0")
|
|
}
|
|
let wm_delete_window = conn
|
|
.intern_atom_immediate_async("WM_DELETE_WINDOW", true)
|
|
.await?;
|
|
if !(wm_delete_window.xid != 0) {
|
|
::core::panicking::panic(
|
|
"assertion failed: wm_delete_window.xid != 0",
|
|
)
|
|
}
|
|
let prop = self
|
|
.win
|
|
.get_property_immediate_async::<
|
|
_,
|
|
ListOfAtom,
|
|
>(conn, wm_protocols, PropertyType::Atom, false)
|
|
.await?;
|
|
let protocols = prop.unwrap().0;
|
|
if protocols.contains(&wm_delete_window) {
|
|
let data = [wm_delete_window.xid, 0, 0, 0, 0];
|
|
let data_bytes = as_u8_slice(&data);
|
|
conn.send_event_async(
|
|
self.win,
|
|
EventMask::default(),
|
|
Event::ClientMessage(ClientMessageEvent {
|
|
event_type: 33,
|
|
format: 32,
|
|
sequence: 0,
|
|
window: self.win,
|
|
ty: wm_protocols,
|
|
data: ClientMessageData::from_bytes(data_bytes).unwrap().0,
|
|
}),
|
|
)
|
|
.await?;
|
|
} else {
|
|
self.win.free_async(conn).await?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
async fn frame<Dpy: AsyncDisplay + ?Sized>(
|
|
conn: &mut Dpy,
|
|
win: Window,
|
|
) -> Result<FramedWindow> {
|
|
let root = conn.default_root();
|
|
let geometry = win.geometry_immediate_async(conn).await?;
|
|
let frame = conn
|
|
.create_simple_window_async(
|
|
root,
|
|
geometry.x,
|
|
geometry.y,
|
|
geometry.width,
|
|
geometry.height,
|
|
CONFIG.border_size(),
|
|
CONFIG.border_color(),
|
|
0x00_00_00,
|
|
)
|
|
.await?;
|
|
frame
|
|
.set_event_mask_async(
|
|
conn,
|
|
EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY,
|
|
)
|
|
.await?;
|
|
win.set_event_mask_async(conn, EventMask::BUTTON_PRESS).await?;
|
|
may_not_exist(win.change_save_set_async(conn, SetMode::Insert).await)?;
|
|
may_not_exist(win.reparent_async(conn, frame, 0, 0).await)?;
|
|
Ok(FramedWindow { frame, win })
|
|
}
|
|
pub fn keymap(state: &mut KeyboardState) -> HashMap<Keysym, Keycode> {
|
|
let mut map: HashMap<Keysym, Keycode> = HashMap::new();
|
|
for keycode in 8..255_u8 {
|
|
let key = state.process_keycode(keycode, KeyButMask::default());
|
|
let keysyms = state.lookup_keysyms(keycode);
|
|
if key != None {
|
|
for keysym in keysyms {
|
|
map.insert(*keysym, keycode);
|
|
}
|
|
}
|
|
}
|
|
map
|
|
}
|
|
}
|
|
}
|
|
use x11::client::{may_not_exist, XcrabWindowManager};
|
|
#[non_exhaustive]
|
|
pub enum XcrabError {
|
|
#[error("breadx error: {0}")]
|
|
Bread(#[from] BreadError),
|
|
#[error("io error: {0}")]
|
|
Io(#[from] std::io::Error),
|
|
#[error("toml error: {0}")]
|
|
Toml(#[from] toml::de::Error),
|
|
#[error("env var error: {0}")]
|
|
Var(#[from] std::env::VarError),
|
|
#[error("client doesnt exist")]
|
|
ClientDoesntExist,
|
|
#[error("{0}")]
|
|
Custom(String),
|
|
}
|
|
#[allow(unused_qualifications)]
|
|
impl std::error::Error for XcrabError {
|
|
fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
|
|
use thiserror::__private::AsDynError;
|
|
#[allow(deprecated)]
|
|
match self {
|
|
XcrabError::Bread { 0: source, .. } => {
|
|
std::option::Option::Some(source.as_dyn_error())
|
|
}
|
|
XcrabError::Io { 0: source, .. } => {
|
|
std::option::Option::Some(source.as_dyn_error())
|
|
}
|
|
XcrabError::Toml { 0: source, .. } => {
|
|
std::option::Option::Some(source.as_dyn_error())
|
|
}
|
|
XcrabError::Var { 0: source, .. } => {
|
|
std::option::Option::Some(source.as_dyn_error())
|
|
}
|
|
XcrabError::ClientDoesntExist { .. } => std::option::Option::None,
|
|
XcrabError::Custom { .. } => std::option::Option::None,
|
|
}
|
|
}
|
|
}
|
|
#[allow(unused_qualifications)]
|
|
impl std::fmt::Display for XcrabError {
|
|
fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
#[allow(unused_imports)]
|
|
use thiserror::__private::{DisplayAsDisplay, PathAsDisplay};
|
|
#[allow(unused_variables, deprecated, clippy::used_underscore_binding)]
|
|
match self {
|
|
XcrabError::Bread(_0) => {
|
|
__formatter.write_fmt(format_args!("breadx error: {0}", _0.as_display()))
|
|
}
|
|
XcrabError::Io(_0) => {
|
|
__formatter.write_fmt(format_args!("io error: {0}", _0.as_display()))
|
|
}
|
|
XcrabError::Toml(_0) => {
|
|
__formatter.write_fmt(format_args!("toml error: {0}", _0.as_display()))
|
|
}
|
|
XcrabError::Var(_0) => {
|
|
__formatter
|
|
.write_fmt(format_args!("env var error: {0}", _0.as_display()))
|
|
}
|
|
XcrabError::ClientDoesntExist {} => {
|
|
__formatter.write_fmt(format_args!("client doesnt exist"))
|
|
}
|
|
XcrabError::Custom(_0) => {
|
|
__formatter.write_fmt(format_args!("{0}", _0.as_display()))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#[allow(unused_qualifications)]
|
|
impl std::convert::From<BreadError> for XcrabError {
|
|
#[allow(deprecated)]
|
|
fn from(source: BreadError) -> Self {
|
|
XcrabError::Bread { 0: source }
|
|
}
|
|
}
|
|
#[allow(unused_qualifications)]
|
|
impl std::convert::From<std::io::Error> for XcrabError {
|
|
#[allow(deprecated)]
|
|
fn from(source: std::io::Error) -> Self {
|
|
XcrabError::Io { 0: source }
|
|
}
|
|
}
|
|
#[allow(unused_qualifications)]
|
|
impl std::convert::From<toml::de::Error> for XcrabError {
|
|
#[allow(deprecated)]
|
|
fn from(source: toml::de::Error) -> Self {
|
|
XcrabError::Toml { 0: source }
|
|
}
|
|
}
|
|
#[allow(unused_qualifications)]
|
|
impl std::convert::From<std::env::VarError> for XcrabError {
|
|
#[allow(deprecated)]
|
|
fn from(source: std::env::VarError) -> Self {
|
|
XcrabError::Var { 0: source }
|
|
}
|
|
}
|
|
#[allow(missing_copy_implementations)]
|
|
#[allow(non_camel_case_types)]
|
|
#[allow(dead_code)]
|
|
pub struct CONFIG {
|
|
__private_field: (),
|
|
}
|
|
#[doc(hidden)]
|
|
pub static CONFIG: CONFIG = CONFIG { __private_field: () };
|
|
impl ::lazy_static::__Deref for CONFIG {
|
|
type Target = config::XcrabConfig;
|
|
fn deref(&self) -> &config::XcrabConfig {
|
|
#[inline(always)]
|
|
fn __static_ref_initialize() -> config::XcrabConfig {
|
|
config::load_file()
|
|
.unwrap_or_else(|e| {
|
|
{
|
|
::std::io::_print(
|
|
format_args!("[CONFIG] Error parsing config: {0}\n", e),
|
|
);
|
|
};
|
|
{
|
|
::std::io::_print(
|
|
format_args!("[CONFIG] Falling back to default config\n"),
|
|
);
|
|
};
|
|
config::XcrabConfig::default()
|
|
})
|
|
}
|
|
#[inline(always)]
|
|
fn __stability() -> &'static config::XcrabConfig {
|
|
static LAZY: ::lazy_static::lazy::Lazy<config::XcrabConfig> = ::lazy_static::lazy::Lazy::INIT;
|
|
LAZY.get(__static_ref_initialize)
|
|
}
|
|
__stability()
|
|
}
|
|
}
|
|
impl ::lazy_static::LazyStatic for CONFIG {
|
|
fn initialize(lazy: &Self) {
|
|
let _ = &**lazy;
|
|
}
|
|
}
|
|
impl Debug for XcrabError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
Display::fmt(self, f)
|
|
}
|
|
}
|
|
type Result<T> = std::result::Result<T, XcrabError>;
|
|
fn main() -> Result<()> {
|
|
let body = async {
|
|
let mut conn = AsyncDisplayConnection::create_async(None, None).await?;
|
|
let root = conn.default_root();
|
|
root.set_event_mask_async(
|
|
&mut conn,
|
|
EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY
|
|
| EventMask::KEY_PRESS,
|
|
)
|
|
.await?;
|
|
let mut manager = XcrabWindowManager::new();
|
|
conn.grab_server_async().await?;
|
|
let top_level_windows = root
|
|
.query_tree_immediate_async(&mut conn)
|
|
.await?
|
|
.children;
|
|
for &win in top_level_windows.iter() {
|
|
let attrs = win.window_attributes_immediate_async(&mut conn).await?;
|
|
if !attrs.override_redirect && attrs.map_state == MapState::Viewable {
|
|
manager.add_client(&mut conn, win).await?;
|
|
}
|
|
}
|
|
conn.ungrab_server_async().await?;
|
|
let mut mask = ModMask::new(
|
|
false,
|
|
false,
|
|
true,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
);
|
|
let mut keyboard_state = KeyboardState::new_async(&mut conn).await?;
|
|
let keymap = x11::client::keymap(&mut keyboard_state);
|
|
let mut request_key = *keymap
|
|
.get(&120)
|
|
.ok_or_else(|| {
|
|
XcrabError::Custom(
|
|
"At least one letter could not be found in the keymap".to_string(),
|
|
)
|
|
})?;
|
|
for &binds in CONFIG.binds.keys() {
|
|
for keysym in 97..122_u32 {
|
|
let keycode = keymap
|
|
.get(&keysym)
|
|
.ok_or_else(|| {
|
|
XcrabError::Custom(
|
|
"At least one letter could not be found in the keymap"
|
|
.to_string(),
|
|
)
|
|
})?;
|
|
let iter_char = keyboard_state
|
|
.process_keycode(*keycode, KeyButMask::default())
|
|
.ok_or_else(|| {
|
|
XcrabError::Custom(
|
|
"The keycode returned from the keymap could not be processed"
|
|
.to_string(),
|
|
)
|
|
})?
|
|
.as_char()
|
|
.ok_or_else(|| {
|
|
XcrabError::Custom(
|
|
"The processed Key could not be cast as a char".to_string(),
|
|
)
|
|
})?;
|
|
if iter_char == binds.key {
|
|
request_key = *keycode;
|
|
mask.inner = binds.mods.inner;
|
|
}
|
|
}
|
|
}
|
|
mask.set_Two(true);
|
|
conn.exchange_request_async(GrabKeyRequest {
|
|
req_type: 33,
|
|
owner_events: false,
|
|
length: 4,
|
|
grab_window: root,
|
|
modifiers: mask,
|
|
key: request_key,
|
|
pointer_mode: GrabMode::Async,
|
|
keyboard_mode: GrabMode::Async,
|
|
})
|
|
.await?;
|
|
mask.set_Two(false);
|
|
conn.exchange_request_async(GrabKeyRequest {
|
|
req_type: 33,
|
|
owner_events: false,
|
|
length: 4,
|
|
grab_window: root,
|
|
modifiers: mask,
|
|
key: request_key,
|
|
pointer_mode: GrabMode::Async,
|
|
keyboard_mode: GrabMode::Async,
|
|
})
|
|
.await?;
|
|
let (send, mut recv) = unbounded_channel();
|
|
let (result_send, result_recv) = unbounded_channel();
|
|
tokio::spawn(
|
|
msg_listener::listener_task(
|
|
CONFIG.msg.clone().unwrap_or_default().socket_path,
|
|
send,
|
|
result_recv,
|
|
),
|
|
);
|
|
loop {
|
|
{
|
|
#[doc(hidden)]
|
|
mod __tokio_select_util {
|
|
pub(super) enum Out<_0, _1> {
|
|
_0(_0),
|
|
_1(_1),
|
|
Disabled,
|
|
}
|
|
pub(super) type Mask = u8;
|
|
}
|
|
use ::tokio::macros::support::Future;
|
|
use ::tokio::macros::support::Pin;
|
|
use ::tokio::macros::support::Poll::{Ready, Pending};
|
|
const BRANCHES: u32 = 2;
|
|
let mut disabled: __tokio_select_util::Mask = Default::default();
|
|
if !true {
|
|
let mask: __tokio_select_util::Mask = 1 << 0;
|
|
disabled |= mask;
|
|
}
|
|
if !true {
|
|
let mask: __tokio_select_util::Mask = 1 << 1;
|
|
disabled |= mask;
|
|
}
|
|
let mut output = {
|
|
let mut futures = (recv.recv(), conn.wait_for_event_async());
|
|
::tokio::macros::support::poll_fn(|cx| {
|
|
let mut is_pending = false;
|
|
let start = 0;
|
|
for i in 0..BRANCHES {
|
|
let branch;
|
|
#[allow(clippy::modulo_one)]
|
|
{
|
|
branch = (start + i) % BRANCHES;
|
|
}
|
|
match branch {
|
|
#[allow(unreachable_code)]
|
|
0 => {
|
|
let mask = 1 << branch;
|
|
if disabled & mask == mask {
|
|
continue;
|
|
}
|
|
let (fut, ..) = &mut futures;
|
|
let mut fut = unsafe { Pin::new_unchecked(fut) };
|
|
let out = match Future::poll(fut, cx) {
|
|
Ready(out) => out,
|
|
Pending => {
|
|
is_pending = true;
|
|
continue;
|
|
}
|
|
};
|
|
disabled |= mask;
|
|
#[allow(unused_variables)] #[allow(unused_mut)]
|
|
match &out {
|
|
Some(s) => {}
|
|
_ => continue,
|
|
}
|
|
return Ready(__tokio_select_util::Out::_0(out));
|
|
}
|
|
#[allow(unreachable_code)]
|
|
1 => {
|
|
let mask = 1 << branch;
|
|
if disabled & mask == mask {
|
|
continue;
|
|
}
|
|
let (_, fut, ..) = &mut futures;
|
|
let mut fut = unsafe { Pin::new_unchecked(fut) };
|
|
let out = match Future::poll(fut, cx) {
|
|
Ready(out) => out,
|
|
Pending => {
|
|
is_pending = true;
|
|
continue;
|
|
}
|
|
};
|
|
disabled |= mask;
|
|
#[allow(unused_variables)] #[allow(unused_mut)]
|
|
match &out {
|
|
Ok(ev) => {}
|
|
_ => continue,
|
|
}
|
|
return Ready(__tokio_select_util::Out::_1(out));
|
|
}
|
|
_ => {
|
|
::core::panicking::unreachable_display(
|
|
&"reaching this means there probably is an off by one bug",
|
|
)
|
|
}
|
|
}
|
|
}
|
|
if is_pending {
|
|
Pending
|
|
} else {
|
|
Ready(__tokio_select_util::Out::Disabled)
|
|
}
|
|
})
|
|
.await
|
|
};
|
|
match output {
|
|
__tokio_select_util::Out::_0(Some(s)) => {
|
|
msg_listener::on_recv(s, &mut manager, &mut conn, &result_send)
|
|
.await?
|
|
}
|
|
__tokio_select_util::Out::_1(Ok(ev)) => {
|
|
process_event(
|
|
ev,
|
|
&mut manager,
|
|
&mut conn,
|
|
root,
|
|
&mut keyboard_state,
|
|
)
|
|
.await?
|
|
}
|
|
__tokio_select_util::Out::Disabled => {
|
|
::std::rt::begin_panic(
|
|
"all branches are disabled and there is no else branch",
|
|
)
|
|
}
|
|
_ => ::core::panicking::unreachable_display(&"failed to match bind"),
|
|
}
|
|
}
|
|
}
|
|
};
|
|
#[allow(clippy::expect_used, clippy::diverging_sub_expression)]
|
|
{
|
|
return tokio::runtime::Builder::new_multi_thread()
|
|
.enable_all()
|
|
.build()
|
|
.expect("Failed building the Runtime")
|
|
.block_on(body);
|
|
}
|
|
}
|
|
#[allow(clippy::too_many_lines)]
|
|
async fn process_event<Dpy: AsyncDisplay + ?Sized>(
|
|
ev: Event,
|
|
manager: &mut XcrabWindowManager,
|
|
conn: &mut Dpy,
|
|
root: Window,
|
|
keyboard_state: &mut KeyboardState,
|
|
) -> Result<()> {
|
|
match ev {
|
|
Event::MapRequest(ev) => {
|
|
manager.add_client(conn, ev.window).await?;
|
|
}
|
|
Event::ConfigureRequest(ev) => {
|
|
let mut params = ConfigureWindowParameters {
|
|
x: Some(ev.x.into()),
|
|
y: Some(ev.y.into()),
|
|
width: Some(ev.width.into()),
|
|
height: Some(ev.height.into()),
|
|
border_width: Some(ev.border_width.into()),
|
|
sibling: None,
|
|
stack_mode: Some(ev.stack_mode),
|
|
};
|
|
if manager.has_client(ev.window) {
|
|
params.x = None;
|
|
params.y = None;
|
|
params.width = None;
|
|
params.height = None;
|
|
}
|
|
may_not_exist(ev.window.configure_async(conn, params).await)?;
|
|
}
|
|
Event::UnmapNotify(ev) => {
|
|
if ev.event != root && manager.has_client(ev.window) {
|
|
manager.remove_client(conn, ev.window).await?;
|
|
}
|
|
}
|
|
Event::ButtonPress(ev) => {
|
|
if ev.detail == 1 {
|
|
manager.set_focus(conn, ev.event).await?;
|
|
}
|
|
}
|
|
Event::KeyPress(ev) => {
|
|
if let Some(k) = keyboard_state.process_keycode(ev.detail, ev.state) {
|
|
if let Some(c) = k.as_char() {
|
|
for (&bind, action) in &CONFIG.binds {
|
|
if bind.key == c {
|
|
action.eval(manager, conn).await?;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
Ok(())
|
|
}
|