mirror of
https://github.com/osmarks/random-stuff
synced 2026-11-14 03:34:30 +00:00
make not totally broken (GPT-6)
This commit is contained in:
1 parent
f17f6ddb9e
commit
cc1f10ef7a
2 files changed
+154
-12
No files matched your search
+25
-12
@@ -1,5 +1,8 @@
|
||||
// based on https://github.com/josephdunn/idlers/blob/main/src/main.rs
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::process::{Command, Stdio};
|
||||
use std::time::{Instant, Duration};
|
||||
use std::sync::{Mutex, Arc};
|
||||
@@ -7,7 +10,7 @@ use std::io::Write;
|
||||
use std::thread;
|
||||
use anyhow::Result;
|
||||
use chrono::{DateTime, Utc};
|
||||
use swayipc::{EventType, Node, NodeType, Workspace};
|
||||
use swayipc::{EventType, Node, NodeType};
|
||||
use wayland_client::{Connection, Dispatch, QueueHandle, delegate_noop, event_created_child, globals::{GlobalListContents, registry_queue_init}, protocol::{wl_registry, wl_seat}};
|
||||
use wayland_protocols::ext::idle_notify::v1::client::{ext_idle_notification_v1::{self, ExtIdleNotificationV1}, ext_idle_notifier_v1::ExtIdleNotifierV1};
|
||||
use smol_str::SmolStr;
|
||||
@@ -26,7 +29,7 @@ impl Dispatch<ExtIdleNotificationV1, ()> for WrState {
|
||||
state: &mut Self,
|
||||
_proxy: &ExtIdleNotificationV1,
|
||||
event: ext_idle_notification_v1::Event,
|
||||
data: &(),
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
) {
|
||||
@@ -100,12 +103,19 @@ fn process_tree<'a>(node: &'a Node, windows: &mut Vec<ArbttWindow>, focused: &mu
|
||||
}
|
||||
|
||||
fn sway_thread(wr_state: WrState) -> Result<()> {
|
||||
let mut connection = swayipc::Connection::new()?;
|
||||
watch_sway(wr_state, swayipc::Connection::new()?, swayipc::Connection::new()?)
|
||||
}
|
||||
|
||||
let tree = connection.get_tree()?;
|
||||
fn watch_sway(
|
||||
wr_state: WrState,
|
||||
mut connection: swayipc::Connection,
|
||||
events: swayipc::Connection,
|
||||
) -> Result<()> {
|
||||
let events = events.subscribe([EventType::Window, EventType::Workspace])?;
|
||||
let mut windows = vec![];
|
||||
|
||||
let mut read_windows = || {
|
||||
let mut read_windows = || -> Result<()> {
|
||||
let tree = connection.get_tree()?;
|
||||
windows.clear();
|
||||
let mut focused = SmolStr::new("");
|
||||
process_tree(&tree, &mut windows, &mut focused, None);
|
||||
@@ -113,17 +123,17 @@ fn sway_thread(wr_state: WrState) -> Result<()> {
|
||||
//println!("emit tree {:?}", windows);
|
||||
std::mem::swap(&mut state.windows, &mut windows);
|
||||
state.focused_desktop = focused;
|
||||
Ok(())
|
||||
};
|
||||
|
||||
read_windows();
|
||||
read_windows()?;
|
||||
|
||||
let st = connection.subscribe([EventType::Window])?;
|
||||
for _s in st {
|
||||
//println!("reread");
|
||||
read_windows();
|
||||
for event in events {
|
||||
event?;
|
||||
read_windows()?;
|
||||
}
|
||||
|
||||
std::process::exit(0);
|
||||
anyhow::bail!("Sway event stream ended")
|
||||
}
|
||||
|
||||
const INTERVAL: Duration = Duration::from_secs(60);
|
||||
@@ -170,7 +180,10 @@ fn main() -> Result<()> {
|
||||
let wr_state_ = wr_state.clone();
|
||||
let wr_state__ = wr_state.clone();
|
||||
thread::spawn(|| {
|
||||
sway_thread(wr_state_).unwrap()
|
||||
if let Err(error) = sway_thread(wr_state_) {
|
||||
eprintln!("Sway window tracking failed: {error:#}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
});
|
||||
thread::spawn(|| {
|
||||
arbtt_thread(wr_state__).unwrap()
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
use super::*;
|
||||
use serde_json::{Value, json};
|
||||
use std::io::Read;
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
||||
fn socket_pair() -> (swayipc::Connection, UnixStream) {
|
||||
let (client, server) = UnixStream::pair().unwrap();
|
||||
for socket in [&client, &server] {
|
||||
socket
|
||||
.set_read_timeout(Some(Duration::from_secs(5)))
|
||||
.unwrap();
|
||||
socket
|
||||
.set_write_timeout(Some(Duration::from_secs(5)))
|
||||
.unwrap();
|
||||
}
|
||||
(client.into(), server)
|
||||
}
|
||||
|
||||
fn receive(socket: &mut UnixStream, expected_type: u32) -> Vec<u8> {
|
||||
let mut header = [0; 14];
|
||||
socket.read_exact(&mut header).unwrap();
|
||||
assert_eq!(&header[..6], b"i3-ipc");
|
||||
assert_eq!(
|
||||
u32::from_ne_bytes(header[10..14].try_into().unwrap()),
|
||||
expected_type
|
||||
);
|
||||
let mut payload = vec![0; u32::from_ne_bytes(header[6..10].try_into().unwrap()) as usize];
|
||||
socket.read_exact(&mut payload).unwrap();
|
||||
payload
|
||||
}
|
||||
|
||||
fn send(socket: &mut UnixStream, message_type: u32, payload: &Value) {
|
||||
let payload = serde_json::to_vec(payload).unwrap();
|
||||
socket.write_all(b"i3-ipc").unwrap();
|
||||
socket
|
||||
.write_all(&(payload.len() as u32).to_ne_bytes())
|
||||
.unwrap();
|
||||
socket.write_all(&message_type.to_ne_bytes()).unwrap();
|
||||
socket.write_all(&payload).unwrap();
|
||||
}
|
||||
|
||||
fn node(kind: &str, name: &str, focused: bool, children: Vec<Value>) -> Value {
|
||||
let rect = json!({"x": 0, "y": 0, "width": 100, "height": 100});
|
||||
json!({
|
||||
"id": 1, "name": name, "type": kind, "border": "none",
|
||||
"current_border_width": 0, "layout": "splith", "orientation": "none",
|
||||
"rect": rect, "window_rect": rect, "deco_rect": rect, "geometry": rect,
|
||||
"urgent": false, "focused": focused, "focus": [], "nodes": children,
|
||||
"floating_nodes": [], "sticky": false
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn refreshes_empty_startup_tree_on_window_and_workspace_events() {
|
||||
let state = WrState(Arc::new(Mutex::new(State {
|
||||
idle_since: None,
|
||||
windows: vec![],
|
||||
focused_desktop: SmolStr::new(""),
|
||||
})));
|
||||
let (queries, mut query_server) = socket_pair();
|
||||
let (events, mut event_server) = socket_pair();
|
||||
let worker_state = state.clone();
|
||||
let worker = thread::spawn(move || watch_sway(worker_state, queries, events));
|
||||
|
||||
let subscriptions: Value = serde_json::from_slice(&receive(&mut event_server, 2)).unwrap();
|
||||
assert_eq!(subscriptions, json!(["window", "workspace"]));
|
||||
// The subscription must complete before the first tree query.
|
||||
query_server.set_nonblocking(true).unwrap();
|
||||
assert_eq!(
|
||||
query_server.read(&mut [0]).unwrap_err().kind(),
|
||||
std::io::ErrorKind::WouldBlock
|
||||
);
|
||||
query_server.set_nonblocking(false).unwrap();
|
||||
send(&mut event_server, 2, &json!({"success": true}));
|
||||
|
||||
receive(&mut query_server, 4);
|
||||
send(&mut query_server, 4, &node("workspace", "1", true, vec![]));
|
||||
|
||||
let opened = node("con", "First title", true, vec![]);
|
||||
send(
|
||||
&mut event_server,
|
||||
0x80000003,
|
||||
&json!({"change": "new", "container": opened}),
|
||||
);
|
||||
receive(&mut query_server, 4);
|
||||
assert!(state.0.lock().unwrap().windows.is_empty());
|
||||
send(
|
||||
&mut query_server,
|
||||
4,
|
||||
&node("workspace", "1", false, vec![opened]),
|
||||
);
|
||||
|
||||
let renamed = node("con", "Updated title", true, vec![]);
|
||||
send(
|
||||
&mut event_server,
|
||||
0x80000003,
|
||||
&json!({"change": "title", "container": renamed}),
|
||||
);
|
||||
receive(&mut query_server, 4);
|
||||
// Receiving the next query proves the previous snapshot has been published.
|
||||
assert_eq!(state.0.lock().unwrap().windows[0].title, "First title");
|
||||
send(
|
||||
&mut query_server,
|
||||
4,
|
||||
&node("workspace", "1", false, vec![renamed]),
|
||||
);
|
||||
|
||||
let empty_workspace = node("workspace", "2", true, vec![]);
|
||||
send(
|
||||
&mut event_server,
|
||||
0x80000000,
|
||||
&json!({"change": "focus", "current": empty_workspace}),
|
||||
);
|
||||
receive(&mut query_server, 4);
|
||||
{
|
||||
let state = state.0.lock().unwrap();
|
||||
assert_eq!(state.windows[0].title, "Updated title");
|
||||
assert!(state.windows[0].active);
|
||||
assert_eq!(state.focused_desktop, "1");
|
||||
}
|
||||
send(&mut query_server, 4, &empty_workspace);
|
||||
|
||||
// A disconnected event socket must return an error, not keep logging stale data.
|
||||
drop(event_server);
|
||||
assert!(worker.join().unwrap().is_err());
|
||||
let state = state.0.lock().unwrap();
|
||||
assert!(state.windows.is_empty());
|
||||
assert_eq!(state.focused_desktop, "2");
|
||||
}
|
||||
Reference in new issue
Block a user