Implement Kitty graphics protocol for background image rendering

- Added `kitty_gfx` module to handle loading, scaling, and displaying background images using the Kitty graphics protocol.
- Integrated background image handling into the main application loop, allowing dynamic updates based on configuration.
- Enhanced terminal rendering to support transparent backgrounds when a Kitty image is active.
- Updated IPC server to manage client connections and messages, including handling background image settings.
- Modified `PtyTerminal` to accept additional environment variables during shell spawning.
- Improved rendering logic to support popup dialogs and terminal background color customization.
This commit is contained in:
2026-03-29 04:28:31 +02:00
parent 3a9e292088
commit 339ad9530e
14 changed files with 2667 additions and 59 deletions

112
src/server.rs Normal file
View File

@@ -0,0 +1,112 @@
use crate::ipc::{self, ClientMessage, ClientRole, ServerMessage};
use std::io::{BufReader, BufWriter};
use std::os::unix::net::{UnixListener, UnixStream};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc;
use std::thread;
static NEXT_CLIENT_ID: AtomicUsize = AtomicUsize::new(1);
pub enum IpcEvent {
ClientConnected {
client_id: usize,
role: ClientRole,
width: u16,
height: u16,
tx: mpsc::SyncSender<ServerMessage>,
},
ClientDisconnected {
client_id: usize,
},
Message {
client_id: usize,
msg: ClientMessage,
},
}
pub fn start(socket_path: &str, event_tx: mpsc::Sender<IpcEvent>) {
let _ = std::fs::remove_file(socket_path);
let listener = match UnixListener::bind(socket_path) {
Ok(l) => l,
Err(e) => {
eprintln!("Kunde inte binda Unix socket {}: {}", socket_path, e);
return;
}
};
let socket_path = socket_path.to_string();
thread::spawn(move || {
for stream in listener.incoming() {
match stream {
Ok(stream) => {
let client_id = NEXT_CLIENT_ID.fetch_add(1, Ordering::SeqCst);
let event_tx = event_tx.clone();
let sp = socket_path.clone();
thread::spawn(move || {
handle_client(client_id, stream, event_tx, sp);
});
}
Err(_) => break,
}
}
});
}
fn handle_client(
client_id: usize,
stream: UnixStream,
event_tx: mpsc::Sender<IpcEvent>,
socket_path: String,
) {
let stream_write = match stream.try_clone() {
Ok(s) => s,
Err(_) => return,
};
let mut reader = BufReader::new(stream);
let (tx, rx) = mpsc::sync_channel::<ServerMessage>(64);
// Writer thread
thread::spawn(move || {
let mut writer = BufWriter::new(stream_write);
for msg in rx {
if ipc::write_message(&mut writer, &msg).is_err() {
break;
}
}
});
// Read Hello
let hello: ClientMessage = match ipc::read_message(&mut reader) {
Ok(m) => m,
Err(_) => return,
};
let (role, width, height) = match &hello {
ClientMessage::Hello { role, width, height, .. } => (role.clone(), *width, *height),
_ => return,
};
let _ = tx.send(ServerMessage::HelloOk {
version: ipc::VERSION.to_string(),
socket_path: socket_path.clone(),
});
let _ = event_tx.send(IpcEvent::ClientConnected {
client_id,
role,
width,
height,
tx: tx.clone(),
});
loop {
match ipc::read_message::<_, ClientMessage>(&mut reader) {
Ok(msg) => {
if event_tx.send(IpcEvent::Message { client_id, msg }).is_err() {
break;
}
}
Err(_) => break,
}
}
let _ = event_tx.send(IpcEvent::ClientDisconnected { client_id });
}