1use std::collections::HashMap;
9use std::os::unix::io::RawFd;
10use std::sync::{Arc, Mutex};
11use std::time::{Duration, SystemTime};
12
13use crate::parser::WasmaConfig;
14use crate::uclient::SectionMemory;
15use crate::wasma_client_unix_posix_raw_app::{posix, RawAppSource, UClientEngine};
16use crate::window_client::WindowClient;
17use crate::window_handling::{WindowGeometry, WindowState, WindowType};
18
19pub const WASMA_CMD_MAGIC: [u8; 4] = [0x57, 0x41, 0x57, 0x4D]; pub const WASMA_RSP_MAGIC: [u8; 4] = [0x57, 0x52, 0x53, 0x50]; pub const WASMA_HDR_SIZE: usize = 16;
40
41#[repr(u32)]
43#[derive(Debug, Clone, Copy, PartialEq)]
44pub enum RawWindowCommand {
45 CreateWindow = 0x01,
46 DestroyWindow = 0x02,
47 SetGeometry = 0x03,
48 GetGeometry = 0x04,
49 SetState = 0x05,
50 GetState = 0x06,
51 SetFocus = 0x07,
52 GetFocus = 0x08,
53 SetTitle = 0x09,
54 SetParent = 0x0A,
55 ListWindows = 0x0B,
56 SetVisible = 0x0C,
57 SetType = 0x0D,
58 Ping = 0xFF,
59}
60
61impl RawWindowCommand {
62 pub fn from_u32(v: u32) -> Option<Self> {
63 match v {
64 0x01 => Some(Self::CreateWindow),
65 0x02 => Some(Self::DestroyWindow),
66 0x03 => Some(Self::SetGeometry),
67 0x04 => Some(Self::GetGeometry),
68 0x05 => Some(Self::SetState),
69 0x06 => Some(Self::GetState),
70 0x07 => Some(Self::SetFocus),
71 0x08 => Some(Self::GetFocus),
72 0x09 => Some(Self::SetTitle),
73 0x0A => Some(Self::SetParent),
74 0x0B => Some(Self::ListWindows),
75 0x0C => Some(Self::SetVisible),
76 0x0D => Some(Self::SetType),
77 0xFF => Some(Self::Ping),
78 _ => None,
79 }
80 }
81
82 pub fn to_u32(self) -> u32 {
83 self as u32
84 }
85}
86
87#[repr(u32)]
89#[derive(Debug, Clone, Copy, PartialEq)]
90pub enum RawWindowStatus {
91 Ok = 0x00,
92 ErrNotFound = 0x01,
93 ErrInvalidParam = 0x02,
94 ErrPermission = 0x03,
95 ErrInternal = 0x04,
96 Pong = 0xFF,
97}
98
99impl RawWindowStatus {
100 pub fn from_u32(v: u32) -> Self {
101 match v {
102 0x00 => Self::Ok,
103 0x01 => Self::ErrNotFound,
104 0x02 => Self::ErrInvalidParam,
105 0x03 => Self::ErrPermission,
106 0x04 => Self::ErrInternal,
107 0xFF => Self::Pong,
108 _ => Self::ErrInternal,
109 }
110 }
111}
112
113pub struct RawWindowFd {
119 ctrl_fd: Option<RawFd>,
121 source: RawAppSource,
123 connected: bool,
125}
126
127impl RawWindowFd {
128 pub fn new(source: RawAppSource) -> Self {
129 Self {
130 ctrl_fd: None,
131 source,
132 connected: false,
133 }
134 }
135
136 pub fn connect(&mut self) -> Result<(), std::io::Error> {
138 if self.connected {
139 return Ok(());
140 }
141
142 let fd = match &self.source {
143 RawAppSource::UnixSocket(path) => self.open_unix_socket(path)?,
144 RawAppSource::TcpSocket { ip, port } => self.open_tcp(ip, *port)?,
145 RawAppSource::NamedPipe(path) => posix::posix_open(path, libc::O_RDWR)?,
146 RawAppSource::CharDevice(path) => posix::posix_open(path, libc::O_RDWR)?,
147 RawAppSource::Stdin => 0,
148 };
149
150 self.ctrl_fd = Some(fd);
151 self.connected = true;
152 println!("🔗 RawWindowFd: Connected → {}", self.source.display_name());
153 Ok(())
154 }
155
156 fn open_unix_socket(&self, path: &str) -> Result<RawFd, std::io::Error> {
157 use std::os::unix::io::AsRawFd;
158 use std::os::unix::net::UnixStream;
159 let stream = UnixStream::connect(path)?;
160 let fd = stream.as_raw_fd();
161 let _ = std::mem::ManuallyDrop::new(stream);
162 Ok(fd)
163 }
164
165 fn open_tcp(&self, ip: &str, port: u16) -> Result<RawFd, std::io::Error> {
166 use std::net::TcpStream;
167 use std::os::unix::io::AsRawFd;
168 let stream = TcpStream::connect(format!("{}:{}", ip, port))?;
169 let fd = stream.as_raw_fd();
170 let _ = std::mem::ManuallyDrop::new(stream);
171 Ok(fd)
172 }
173
174 fn write_header(
176 &self,
177 fd: RawFd,
178 cmd: RawWindowCommand,
179 window_id: u32,
180 payload_len: u32,
181 ) -> Result<(), std::io::Error> {
182 let mut hdr = [0u8; WASMA_HDR_SIZE];
183 hdr[0..4].copy_from_slice(&WASMA_CMD_MAGIC);
184 hdr[4..8].copy_from_slice(&cmd.to_u32().to_le_bytes());
185 hdr[8..12].copy_from_slice(&window_id.to_le_bytes());
186 hdr[12..16].copy_from_slice(&payload_len.to_le_bytes());
187
188 let mut written = 0;
189 while written < WASMA_HDR_SIZE {
190 let n = unsafe {
191 libc::write(
192 fd,
193 hdr[written..].as_ptr() as *const libc::c_void,
194 WASMA_HDR_SIZE - written,
195 )
196 };
197 match n {
198 -1 => return Err(std::io::Error::last_os_error()),
199 0 => {
200 return Err(std::io::Error::new(
201 std::io::ErrorKind::WriteZero,
202 "write() sıfır byte yazdı",
203 ))
204 }
205 n => written += n as usize,
206 }
207 }
208 Ok(())
209 }
210
211 fn write_payload(&self, fd: RawFd, payload: &[u8]) -> Result<(), std::io::Error> {
213 let mut written = 0;
214 while written < payload.len() {
215 let n = unsafe {
216 libc::write(
217 fd,
218 payload[written..].as_ptr() as *const libc::c_void,
219 payload.len() - written,
220 )
221 };
222 match n {
223 -1 => return Err(std::io::Error::last_os_error()),
224 0 => {
225 return Err(std::io::Error::new(
226 std::io::ErrorKind::WriteZero,
227 "write() sıfır byte yazdı",
228 ))
229 }
230 n => written += n as usize,
231 }
232 }
233 Ok(())
234 }
235
236 fn read_response_header(
238 &self,
239 fd: RawFd,
240 ) -> Result<(RawWindowStatus, u32, u32), std::io::Error> {
241 let mut hdr = [0u8; WASMA_HDR_SIZE];
242 posix::posix_read_exact(fd, &mut hdr)?;
243
244 if hdr[0..4] != WASMA_RSP_MAGIC {
245 return Err(std::io::Error::new(
246 std::io::ErrorKind::InvalidData,
247 format!("Geçersiz yanıt magic: {:?}", &hdr[0..4]),
248 ));
249 }
250
251 let status = RawWindowStatus::from_u32(u32::from_le_bytes(hdr[4..8].try_into().unwrap()));
252 let window_id = u32::from_le_bytes(hdr[8..12].try_into().unwrap());
253 let payload_len = u32::from_le_bytes(hdr[12..16].try_into().unwrap());
254
255 Ok((status, window_id, payload_len))
256 }
257
258 pub fn send_command(
260 &self,
261 cmd: RawWindowCommand,
262 window_id: u32,
263 payload: &[u8],
264 ) -> Result<(RawWindowStatus, Vec<u8>), std::io::Error> {
265 let fd = self.ctrl_fd.ok_or_else(|| {
266 std::io::Error::new(
267 std::io::ErrorKind::NotConnected,
268 "Kontrol kanalı açık değil",
269 )
270 })?;
271
272 self.write_header(fd, cmd, window_id, payload.len() as u32)?;
274 if !payload.is_empty() {
275 self.write_payload(fd, payload)?;
276 }
277
278 let (status, _resp_wid, payload_len) = self.read_response_header(fd)?;
280 let mut resp_payload = vec![0u8; payload_len as usize];
281 if payload_len > 0 {
282 posix::posix_read_exact(fd, &mut resp_payload)?;
283 }
284
285 Ok((status, resp_payload))
286 }
287
288 pub fn is_connected(&self) -> bool {
289 self.connected
290 }
291}
292
293impl Drop for RawWindowFd {
294 fn drop(&mut self) {
295 if let Some(fd) = self.ctrl_fd.take() {
296 if fd != 0 {
297 let _ = posix::posix_close(fd);
298 }
299 }
300 }
301}
302
303#[derive(Debug, Clone)]
309pub struct RawWindowRecord {
310 pub id: u64,
311 pub title: String,
312 pub app_id: String,
313 pub geometry: WindowGeometry,
314 pub state: WindowState,
315 pub window_type: WindowType,
316 pub visible: bool,
317 pub focused: bool,
318 pub parent_id: Option<u64>,
319 pub children: Vec<u64>,
320 pub created_at: SystemTime,
321 pub last_modified: SystemTime,
322}
323
324impl RawWindowRecord {
325 pub fn new(id: u64, title: impl Into<String>, app_id: impl Into<String>) -> Self {
326 let now = SystemTime::now();
327 Self {
328 id,
329 title: title.into(),
330 app_id: app_id.into(),
331 geometry: WindowGeometry {
332 x: 0,
333 y: 0,
334 width: 800,
335 height: 600,
336 },
337 state: WindowState::Normal,
338 window_type: WindowType::Normal,
339 visible: true,
340 focused: false,
341 parent_id: None,
342 children: Vec::new(),
343 created_at: now,
344 last_modified: now,
345 }
346 }
347
348 pub fn touch(&mut self) {
350 self.last_modified = SystemTime::now();
351 }
352}
353
354pub mod geometry_codec {
359 use crate::window_handling::WindowGeometry;
360
361 pub const GEOMETRY_SIZE: usize = 16; pub fn encode(g: &WindowGeometry) -> [u8; GEOMETRY_SIZE] {
364 let mut buf = [0u8; GEOMETRY_SIZE];
365 buf[0..4].copy_from_slice(&g.x.to_le_bytes());
366 buf[4..8].copy_from_slice(&g.y.to_le_bytes());
367 buf[8..12].copy_from_slice(&g.width.to_le_bytes());
368 buf[12..16].copy_from_slice(&g.height.to_le_bytes());
369 buf
370 }
371
372 pub fn decode(buf: &[u8]) -> Option<WindowGeometry> {
373 if buf.len() < GEOMETRY_SIZE {
374 return None;
375 }
376 Some(WindowGeometry {
377 x: i32::from_le_bytes(buf[0..4].try_into().ok()?),
378 y: i32::from_le_bytes(buf[4..8].try_into().ok()?),
379 width: u32::from_le_bytes(buf[8..12].try_into().ok()?),
380 height: u32::from_le_bytes(buf[12..16].try_into().ok()?),
381 })
382 }
383}
384
385pub mod state_codec {
387 use crate::window_handling::WindowState;
388
389 pub fn encode(s: &WindowState) -> u8 {
390 match s {
391 WindowState::Normal => 0x00,
392 WindowState::Minimized => 0x01,
393 WindowState::Maximized => 0x02,
394 WindowState::Fullscreen => 0x03,
395 WindowState::Hidden => 0x04,
396 }
397 }
398
399 pub fn decode(v: u8) -> WindowState {
400 match v {
401 0x00 => WindowState::Normal,
402 0x01 => WindowState::Minimized,
403 0x02 => WindowState::Maximized,
404 0x03 => WindowState::Fullscreen,
405 0x04 => WindowState::Hidden,
406 _ => WindowState::Normal,
407 }
408 }
409}
410
411pub struct RawWindowClient {
427 config: Arc<WasmaConfig>,
428
429 fd_manager: RawWindowFd,
431
432 window_registry: Arc<Mutex<HashMap<u64, RawWindowRecord>>>,
434
435 next_id: Arc<Mutex<u64>>,
437
438 window_client: Option<Arc<Mutex<WindowClient>>>,
441
442 active: bool,
444
445 memory: SectionMemory,
447}
448
449impl RawWindowClient {
450 pub fn new(config: WasmaConfig) -> Self {
451 let level = config.resource_limits.scope_level;
452 let source = RawAppSource::UnixSocket("/run/wasma/window.sock".to_string());
453
454 Self {
455 fd_manager: RawWindowFd::new(source),
456 window_registry: Arc::new(Mutex::new(HashMap::new())),
457 next_id: Arc::new(Mutex::new(1)),
458 window_client: None,
459 active: false,
460 memory: SectionMemory::new(level),
461 config: Arc::new(config),
462 }
463 }
464
465 pub fn from_config(config: Arc<WasmaConfig>) -> Self {
466 let level = config.resource_limits.scope_level;
467 let source = RawAppSource::UnixSocket("/run/wasma/window.sock".to_string());
468
469 Self {
470 fd_manager: RawWindowFd::new(source),
471 window_registry: Arc::new(Mutex::new(HashMap::new())),
472 next_id: Arc::new(Mutex::new(1)),
473 window_client: None,
474 active: false,
475 memory: SectionMemory::new(level),
476 config,
477 }
478 }
479
480 pub fn attach_window_client(&mut self, wc: Arc<Mutex<WindowClient>>) {
482 self.window_client = Some(wc);
483 println!("🔗 RawWindowClient: WindowClient attached (rendering delegated)");
484 }
485
486 pub fn with_source(mut self, source: RawAppSource) -> Self {
488 self.fd_manager = RawWindowFd::new(source);
489 self
490 }
491
492 fn next_window_id(&self) -> u64 {
494 let mut id = self.next_id.lock().unwrap();
495 let current = *id;
496 *id += 1;
497 current
498 }
499
500 pub fn create_window(
506 &self,
507 title: impl Into<String>,
508 app_id: impl Into<String>,
509 geometry: WindowGeometry,
510 window_type: WindowType,
511 ) -> Result<u64, String> {
512 let title = title.into();
513 let app_id = app_id.into();
514 let window_id = self.next_window_id();
515
516 let title_bytes = title.as_bytes();
518 let app_id_bytes = app_id.as_bytes();
519 let mut payload = Vec::with_capacity(
520 geometry_codec::GEOMETRY_SIZE + 2 + title_bytes.len() + 2 + app_id_bytes.len() + 1,
521 );
522
523 payload.extend_from_slice(&geometry_codec::encode(&geometry));
524 payload.extend_from_slice(&(title_bytes.len() as u16).to_le_bytes());
525 payload.extend_from_slice(title_bytes);
526 payload.extend_from_slice(&(app_id_bytes.len() as u16).to_le_bytes());
527 payload.extend_from_slice(app_id_bytes);
528 payload.push(state_codec::encode(&WindowState::Normal));
529
530 let remote_ok = if self.fd_manager.is_connected() {
532 match self.fd_manager.send_command(
533 RawWindowCommand::CreateWindow,
534 window_id as u32,
535 &payload,
536 ) {
537 Ok((RawWindowStatus::Ok, _)) => true,
538 Ok((status, _)) => {
539 eprintln!("⚠️ CreateWindow server error: {:?}", status);
540 false
541 }
542 Err(e) => {
543 eprintln!("⚠️ CreateWindow communication error: {}", e);
544 false
545 }
546 }
547 } else {
548 true };
550
551 if remote_ok {
552 let mut record = RawWindowRecord::new(window_id, &title, &app_id);
554 record.geometry = geometry;
555 record.window_type = window_type;
556
557 let mut registry = self.window_registry.lock().unwrap();
558 registry.insert(window_id, record);
559
560 println!(
561 "🪟 RawWindowClient: Window {} created [{}x{} @ ({},{})]",
562 window_id, geometry.width, geometry.height, geometry.x, geometry.y
563 );
564 Ok(window_id)
565 } else {
566 Err(format!("Window {} could not be created", window_id))
567 }
568 }
569
570 pub fn destroy_window(&self, window_id: u64) -> Result<(), String> {
572 let children: Vec<u64> = {
574 let registry = self.window_registry.lock().unwrap();
575 registry
576 .get(&window_id)
577 .map(|r| r.children.clone())
578 .unwrap_or_default()
579 };
580 for child_id in children {
581 self.destroy_window(child_id)?;
582 }
583
584 let parent_id = {
586 let registry = self.window_registry.lock().unwrap();
587 registry.get(&window_id).and_then(|r| r.parent_id)
588 };
589 if let Some(pid) = parent_id {
590 let mut registry = self.window_registry.lock().unwrap();
591 if let Some(parent) = registry.get_mut(&pid) {
592 parent.children.retain(|&id| id != window_id);
593 parent.touch();
594 }
595 }
596
597 if self.fd_manager.is_connected() {
599 let _ = self.fd_manager.send_command(
600 RawWindowCommand::DestroyWindow,
601 window_id as u32,
602 &[],
603 );
604 }
605
606 let mut registry = self.window_registry.lock().unwrap();
608 if registry.remove(&window_id).is_some() {
609 println!("🗑️ RawWindowClient: Window {} closed", window_id);
610 Ok(())
611 } else {
612 Err(format!("Pencere {} bulunamadı", window_id))
613 }
614 }
615
616 pub fn set_geometry(&self, window_id: u64, geometry: WindowGeometry) -> Result<(), String> {
618 let payload = geometry_codec::encode(&geometry);
619
620 if self.fd_manager.is_connected() {
621 match self.fd_manager.send_command(
622 RawWindowCommand::SetGeometry,
623 window_id as u32,
624 &payload,
625 ) {
626 Ok((RawWindowStatus::Ok, _)) => {}
627 Ok((status, _)) => return Err(format!("SetGeometry hatası: {:?}", status)),
628 Err(e) => return Err(format!("SetGeometry iletişim hatası: {}", e)),
629 }
630 }
631
632 let mut registry = self.window_registry.lock().unwrap();
633 match registry.get_mut(&window_id) {
634 Some(record) => {
635 record.geometry = geometry;
636 record.touch();
637 println!(
638 "📐 Window {} geometry updated: {}x{} @ ({},{})",
639 window_id, geometry.width, geometry.height, geometry.x, geometry.y
640 );
641
642 if let Some(ref wc) = self.window_client {
644 let mut client = wc.lock().unwrap();
645 client.resize(geometry.width, geometry.height);
646 }
647 Ok(())
648 }
649 None => Err(format!("Pencere {} bulunamadı", window_id)),
650 }
651 }
652
653 pub fn get_geometry(&self, window_id: u64) -> Result<WindowGeometry, String> {
655 {
657 let registry = self.window_registry.lock().unwrap();
658 if let Some(record) = registry.get(&window_id) {
659 return Ok(record.geometry);
660 }
661 }
662
663 if self.fd_manager.is_connected() {
665 match self
666 .fd_manager
667 .send_command(RawWindowCommand::GetGeometry, window_id as u32, &[])
668 {
669 Ok((RawWindowStatus::Ok, payload)) => geometry_codec::decode(&payload)
670 .ok_or_else(|| "Geometry decode hatası".to_string()),
671 Ok((status, _)) => Err(format!("GetGeometry sunucu hatası: {:?}", status)),
672 Err(e) => Err(format!("GetGeometry iletişim hatası: {}", e)),
673 }
674 } else {
675 Err(format!("Pencere {} bulunamadı", window_id))
676 }
677 }
678
679 pub fn set_state(&self, window_id: u64, state: WindowState) -> Result<(), String> {
681 let payload = [state_codec::encode(&state)];
682
683 if self.fd_manager.is_connected() {
684 match self.fd_manager.send_command(
685 RawWindowCommand::SetState,
686 window_id as u32,
687 &payload,
688 ) {
689 Ok((RawWindowStatus::Ok, _)) => {}
690 Ok((status, _)) => return Err(format!("SetState hatası: {:?}", status)),
691 Err(e) => return Err(format!("SetState iletişim hatası: {}", e)),
692 }
693 }
694
695 let mut registry = self.window_registry.lock().unwrap();
696 match registry.get_mut(&window_id) {
697 Some(record) => {
698 record.state = state.clone();
699 record.touch();
700 println!("🔄 Window {} state: {:?}", window_id, state);
701 Ok(())
702 }
703 None => Err(format!("Pencere {} bulunamadı", window_id)),
704 }
705 }
706
707 pub fn get_state(&self, window_id: u64) -> Result<WindowState, String> {
709 let registry = self.window_registry.lock().unwrap();
710 match registry.get(&window_id) {
711 Some(record) => Ok(record.state.clone()),
712 None => Err(format!("Pencere {} bulunamadı", window_id)),
713 }
714 }
715
716 pub fn set_focus(&self, window_id: u64) -> Result<(), String> {
718 {
720 let mut registry = self.window_registry.lock().unwrap();
721 for record in registry.values_mut() {
722 record.focused = false;
723 }
724 }
725
726 if self.fd_manager.is_connected() {
727 let _ = self
728 .fd_manager
729 .send_command(RawWindowCommand::SetFocus, window_id as u32, &[]);
730 }
731
732 let mut registry = self.window_registry.lock().unwrap();
733 match registry.get_mut(&window_id) {
734 Some(record) => {
735 record.focused = true;
736 record.touch();
737 println!("👁️ Window {} focused", window_id);
738 Ok(())
739 }
740 None => Err(format!("Pencere {} bulunamadı", window_id)),
741 }
742 }
743
744 pub fn get_focused_window(&self) -> Option<u64> {
746 let registry = self.window_registry.lock().unwrap();
747 registry.values().find(|r| r.focused).map(|r| r.id)
748 }
749
750 pub fn set_title(&self, window_id: u64, title: impl Into<String>) -> Result<(), String> {
752 let title = title.into();
753 let title_bytes = title.as_bytes();
754
755 if self.fd_manager.is_connected() {
756 let mut payload = Vec::with_capacity(2 + title_bytes.len());
757 payload.extend_from_slice(&(title_bytes.len() as u16).to_le_bytes());
758 payload.extend_from_slice(title_bytes);
759 let _ = self.fd_manager.send_command(
760 RawWindowCommand::SetTitle,
761 window_id as u32,
762 &payload,
763 );
764 }
765
766 let mut registry = self.window_registry.lock().unwrap();
767 match registry.get_mut(&window_id) {
768 Some(record) => {
769 record.title = title.clone();
770 record.touch();
771 println!("📝 Window {} title: \"{}\"", window_id, title);
772 Ok(())
773 }
774 None => Err(format!("Pencere {} bulunamadı", window_id)),
775 }
776 }
777
778 pub fn set_visible(&self, window_id: u64, visible: bool) -> Result<(), String> {
780 if self.fd_manager.is_connected() {
781 let _ = self.fd_manager.send_command(
782 RawWindowCommand::SetVisible,
783 window_id as u32,
784 &[visible as u8],
785 );
786 }
787
788 let mut registry = self.window_registry.lock().unwrap();
789 match registry.get_mut(&window_id) {
790 Some(record) => {
791 record.visible = visible;
792 record.touch();
793 println!("👀 Window {} visibility: {}", window_id, visible);
794 Ok(())
795 }
796 None => Err(format!("Pencere {} bulunamadı", window_id)),
797 }
798 }
799
800 pub fn set_parent(&self, child_id: u64, parent_id: u64) -> Result<(), String> {
802 {
804 let registry = self.window_registry.lock().unwrap();
805 if !registry.contains_key(&parent_id) {
806 return Err(format!("Ebeveyn pencere {} bulunamadı", parent_id));
807 }
808 }
809
810 if self.fd_manager.is_connected() {
811 let payload = (parent_id as u32).to_le_bytes();
812 let _ = self.fd_manager.send_command(
813 RawWindowCommand::SetParent,
814 child_id as u32,
815 &payload,
816 );
817 }
818
819 let mut registry = self.window_registry.lock().unwrap();
820
821 if let Some(child) = registry.get_mut(&child_id) {
823 child.parent_id = Some(parent_id);
824 child.touch();
825 } else {
826 return Err(format!("Çocuk pencere {} bulunamadı", child_id));
827 }
828
829 if let Some(parent) = registry.get_mut(&parent_id) {
831 if !parent.children.contains(&child_id) {
832 parent.children.push(child_id);
833 }
834 parent.touch();
835 }
836
837 println!("👨👧 Window {} → parent {}", child_id, parent_id);
838 Ok(())
839 }
840
841 pub fn list_windows(&self) -> Vec<RawWindowRecord> {
843 let registry = self.window_registry.lock().unwrap();
844 let mut windows: Vec<RawWindowRecord> = registry.values().cloned().collect();
845 windows.sort_by_key(|w| w.id);
846 windows
847 }
848
849 pub fn get_window(&self, window_id: u64) -> Option<RawWindowRecord> {
851 let registry = self.window_registry.lock().unwrap();
852 registry.get(&window_id).cloned()
853 }
854
855 pub fn render_frame(&self, window_id: u64, _stream_id: u8, data: &[u8]) -> Result<(), String> {
858 {
859 let registry = self.window_registry.lock().unwrap();
860 match registry.get(&window_id) {
861 Some(record) => {
862 if !record.visible || record.state == WindowState::Hidden {
863 return Ok(());
864 }
865 }
866 None => return Err(format!("Window {} not found", window_id)),
867 }
868 }
869
870 self.dispatch_data(data);
873 Ok(())
874}
875 pub fn ping(&self) -> Result<Duration, String> {
877 if !self.fd_manager.is_connected() {
878 return Err("No connection".to_string());
879 }
880
881 let start = std::time::Instant::now();
882 match self.fd_manager.send_command(RawWindowCommand::Ping, 0, &[]) {
883 Ok((RawWindowStatus::Pong, _)) => Ok(start.elapsed()),
884 Ok((status, _)) => Err(format!("Ping hatası: {:?}", status)),
885 Err(e) => Err(format!("Ping iletişim hatası: {}", e)),
886 }
887 }
888}
889
890impl UClientEngine for RawWindowClient {
895 fn start_engine(&mut self) -> Result<(), Box<dyn std::error::Error>> {
896 println!("🔌 RawWindowClient: Opening control channel...");
897
898 if let Err(e) = self.fd_manager.connect() {
900 eprintln!("⚠️ Server connection failed, running in local mode: {}", e);
901 }
902
903 self.active = true;
904 println!("🟢 WASMA RawWindowClient: Engine Started");
905 println!(
906 "🎨 Renderer: {} (delegated to WindowClient)",
907 self.config.resource_limits.renderer
908 );
909 println!("📡 Mode: Window Management Engine");
910
911 if self.fd_manager.is_connected() {
913 let fd = self.fd_manager.ctrl_fd.unwrap();
914 let mut event_buf = vec![0u8; WASMA_HDR_SIZE + 256];
915
916 loop {
917 match posix::posix_poll_readable(fd, 50) {
919 Ok(false) => {
920 if !self.active {
921 break;
922 }
923 continue;
924 }
925 Ok(true) => {}
926 Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
927 Err(e) => {
928 eprintln!("⚠️ Poll error: {}", e);
929 break;
930 }
931 }
932
933 match posix::posix_read(fd, &mut event_buf[..WASMA_HDR_SIZE]) {
935 Ok(0) => {
936 println!("📭 Server connection closed.");
937 break;
938 }
939 Ok(n) if n < WASMA_HDR_SIZE => {
940 eprintln!("⚠️ Short header: {} bytes", n);
941 continue;
942 }
943 Ok(_) => {
944 self.handle_server_event(&event_buf[..WASMA_HDR_SIZE]);
946 }
947 Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => continue,
948 Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
949 Err(e) => {
950 eprintln!("⚠️ Read error: {}", e);
951 break;
952 }
953 }
954 }
955 }
956
957 self.active = false;
958 Ok(())
959 }
960
961 fn dispatch_data(&self, data: &[u8]) {
962 if let Some(focused_id) = self.get_focused_window() {
964 let _ = self.render_frame(focused_id, 0, data);
965 }
966 }
967
968 fn memory_usage(&self) -> (usize, usize, usize) {
969 (
970 self.memory.raw_storage.len(),
971 self.memory.cell_count,
972 self.memory.cell_size,
973 )
974 }
975
976 fn get_config(&self) -> &WasmaConfig {
977 &self.config
978 }
979
980 fn shutdown(&mut self) -> Result<(), Box<dyn std::error::Error>> {
981 self.active = false;
982
983 let ids: Vec<u64> = {
985 let registry = self.window_registry.lock().unwrap();
986 registry.keys().cloned().collect()
987 };
988 for id in ids {
989 let _ = self.destroy_window(id);
990 }
991
992 println!("🛑 RawWindowClient shutdown");
993 Ok(())
994 }
995
996 fn is_active(&self) -> bool {
997 self.active
998 }
999}
1000
1001impl RawWindowClient {
1006 fn handle_server_event(&self, hdr: &[u8]) {
1008 if hdr.len() < WASMA_HDR_SIZE {
1009 return;
1010 }
1011 if hdr[0..4] != WASMA_RSP_MAGIC {
1013 return;
1014 }
1015 let status =
1016 RawWindowStatus::from_u32(u32::from_le_bytes(hdr[4..8].try_into().unwrap_or([0; 4])));
1017 let window_id = u32::from_le_bytes(hdr[8..12].try_into().unwrap_or([0; 4]));
1018
1019 println!(
1020 "📨 Server event: window_id={} status={:?}",
1021 window_id, status
1022 );
1023 }
1025}
1026
1027pub struct RawWindowClientBuilder {
1032 config: Option<WasmaConfig>,
1033 source: Option<RawAppSource>,
1034 window_client: Option<Arc<Mutex<WindowClient>>>,
1035}
1036
1037impl RawWindowClientBuilder {
1038 pub fn new() -> Self {
1039 Self {
1040 config: None,
1041 source: None,
1042 window_client: None,
1043 }
1044 }
1045
1046 pub fn with_config(mut self, config: WasmaConfig) -> Self {
1047 self.config = Some(config);
1048 self
1049 }
1050
1051 pub fn with_source(mut self, source: RawAppSource) -> Self {
1052 self.source = Some(source);
1053 self
1054 }
1055
1056 pub fn with_window_client(mut self, wc: Arc<Mutex<WindowClient>>) -> Self {
1057 self.window_client = Some(wc);
1058 self
1059 }
1060
1061 pub fn build(self) -> Result<RawWindowClient, String> {
1062 let config = self.config.ok_or("Config required")?;
1063 let mut client = RawWindowClient::new(config);
1064
1065 if let Some(source) = self.source {
1066 client.fd_manager = RawWindowFd::new(source);
1067 }
1068
1069 if let Some(wc) = self.window_client {
1070 client.attach_window_client(wc);
1071 }
1072
1073 Ok(client)
1074 }
1075}
1076
1077impl Default for RawWindowClientBuilder {
1078 fn default() -> Self {
1079 Self::new()
1080 }
1081}
1082
1083#[cfg(test)]
1088mod tests {
1089 use super::*;
1090 use crate::parser::ConfigParser;
1091
1092 fn make_config() -> WasmaConfig {
1093 let parser = ConfigParser::new(None);
1094 let content = parser.generate_default_config();
1095 parser.parse(&content).unwrap()
1096 }
1097
1098 #[test]
1099 fn test_create_and_destroy_window() {
1100 let config = make_config();
1101 let client = RawWindowClient::new(config);
1102
1103 let geo = WindowGeometry {
1104 x: 100,
1105 y: 200,
1106 width: 1280,
1107 height: 720,
1108 };
1109 let id = client
1110 .create_window("Test Penceresi", "test.app", geo, WindowType::Normal)
1111 .unwrap();
1112
1113 assert!(client.get_window(id).is_some());
1114 let record = client.get_window(id).unwrap();
1115 assert_eq!(record.geometry.width, 1280);
1116 assert_eq!(record.geometry.height, 720);
1117 assert_eq!(record.title, "Test Penceresi");
1118
1119 client.destroy_window(id).unwrap();
1120 assert!(client.get_window(id).is_none());
1121
1122 println!("✅ Window create and destroy working");
1123 }
1124
1125 #[test]
1126 fn test_set_and_get_geometry() {
1127 let config = make_config();
1128 let client = RawWindowClient::new(config);
1129
1130 let id = client
1131 .create_window(
1132 "Geo Test",
1133 "geo.app",
1134 WindowGeometry {
1135 x: 0,
1136 y: 0,
1137 width: 800,
1138 height: 600,
1139 },
1140 WindowType::Normal,
1141 )
1142 .unwrap();
1143
1144 let new_geo = WindowGeometry {
1145 x: 50,
1146 y: 75,
1147 width: 1920,
1148 height: 1080,
1149 };
1150 client.set_geometry(id, new_geo).unwrap();
1151
1152 let fetched = client.get_geometry(id).unwrap();
1153 assert_eq!(fetched.width, 1920);
1154 assert_eq!(fetched.height, 1080);
1155 assert_eq!(fetched.x, 50);
1156 assert_eq!(fetched.y, 75);
1157
1158 println!("✅ Geometry management working");
1159 }
1160
1161 #[test]
1162 fn test_window_state_transitions() {
1163 let config = make_config();
1164 let client = RawWindowClient::new(config);
1165
1166 let id = client
1167 .create_window(
1168 "State Test",
1169 "state.app",
1170 WindowGeometry {
1171 x: 0,
1172 y: 0,
1173 width: 800,
1174 height: 600,
1175 },
1176 WindowType::Normal,
1177 )
1178 .unwrap();
1179
1180 for state in [
1181 WindowState::Minimized,
1182 WindowState::Maximized,
1183 WindowState::Fullscreen,
1184 WindowState::Hidden,
1185 WindowState::Normal,
1186 ] {
1187 client.set_state(id, state.clone()).unwrap();
1188 assert_eq!(client.get_state(id).unwrap(), state);
1189 }
1190
1191 println!("✅ Window state transitions working");
1192 }
1193
1194 #[test]
1195 fn test_focus_management() {
1196 let config = make_config();
1197 let client = RawWindowClient::new(config);
1198
1199 let geo = WindowGeometry {
1200 x: 0,
1201 y: 0,
1202 width: 800,
1203 height: 600,
1204 };
1205 let id1 = client
1206 .create_window("Win1", "app1", geo, WindowType::Normal)
1207 .unwrap();
1208 let id2 = client
1209 .create_window("Win2", "app2", geo, WindowType::Normal)
1210 .unwrap();
1211
1212 client.set_focus(id1).unwrap();
1213 assert_eq!(client.get_focused_window(), Some(id1));
1214 assert!(!client.get_window(id2).unwrap().focused);
1215
1216 client.set_focus(id2).unwrap();
1217 assert_eq!(client.get_focused_window(), Some(id2));
1218 assert!(!client.get_window(id1).unwrap().focused);
1219
1220 println!("✅ Focus management working");
1221 }
1222
1223 #[test]
1224 fn test_parent_child_relationship() {
1225 let config = make_config();
1226 let client = RawWindowClient::new(config);
1227
1228 let geo = WindowGeometry {
1229 x: 0,
1230 y: 0,
1231 width: 800,
1232 height: 600,
1233 };
1234 let parent_id = client
1235 .create_window("Parent", "parent.app", geo, WindowType::Normal)
1236 .unwrap();
1237 let child_id = client
1238 .create_window("Child", "child.app", geo, WindowType::Dialog)
1239 .unwrap();
1240
1241 client.set_parent(child_id, parent_id).unwrap();
1242
1243 let parent = client.get_window(parent_id).unwrap();
1244 assert!(parent.children.contains(&child_id));
1245
1246 let child = client.get_window(child_id).unwrap();
1247 assert_eq!(child.parent_id, Some(parent_id));
1248
1249 client.destroy_window(parent_id).unwrap();
1251 assert!(client.get_window(parent_id).is_none());
1252 assert!(client.get_window(child_id).is_none());
1253
1254 println!("✅ Parent-child relationship working");
1255 }
1256
1257 #[test]
1258 fn test_geometry_codec() {
1259 let geo = WindowGeometry {
1260 x: -100,
1261 y: 200,
1262 width: 1920,
1263 height: 1080,
1264 };
1265 let encoded = geometry_codec::encode(&geo);
1266 let decoded = geometry_codec::decode(&encoded).unwrap();
1267
1268 assert_eq!(decoded.x, geo.x);
1269 assert_eq!(decoded.y, geo.y);
1270 assert_eq!(decoded.width, geo.width);
1271 assert_eq!(decoded.height, geo.height);
1272
1273 println!("✅ Geometry codec working");
1274 }
1275
1276 #[test]
1277 fn test_state_codec() {
1278 for state in [
1279 WindowState::Normal,
1280 WindowState::Minimized,
1281 WindowState::Maximized,
1282 WindowState::Fullscreen,
1283 WindowState::Hidden,
1284 ] {
1285 let encoded = state_codec::encode(&state);
1286 let decoded = state_codec::decode(encoded);
1287 assert_eq!(decoded, state);
1288 }
1289 println!("✅ State codec working");
1290 }
1291
1292 #[test]
1293 fn test_list_windows() {
1294 let config = make_config();
1295 let client = RawWindowClient::new(config);
1296
1297 let geo = WindowGeometry {
1298 x: 0,
1299 y: 0,
1300 width: 800,
1301 height: 600,
1302 };
1303 let _id1 = client
1304 .create_window("W1", "a1", geo, WindowType::Normal)
1305 .unwrap();
1306 let _id2 = client
1307 .create_window("W2", "a2", geo, WindowType::Normal)
1308 .unwrap();
1309 let _id3 = client
1310 .create_window("W3", "a3", geo, WindowType::Utility)
1311 .unwrap();
1312
1313 let windows = client.list_windows();
1314 assert_eq!(windows.len(), 3);
1315 assert!(windows[0].id < windows[1].id);
1317 assert!(windows[1].id < windows[2].id);
1318
1319 println!("✅ Window listing working: {} windows", windows.len());
1320 }
1321
1322 #[test]
1323 fn test_uclient_engine_trait_object() {
1324 let config = make_config();
1325 let client: Box<dyn UClientEngine> = Box::new(RawWindowClient::new(config));
1326 assert!(!client.is_active());
1327 println!("✅ UClientEngine trait object (RawWindowClient) working");
1328 }
1329
1330 #[test]
1331 fn test_visibility() {
1332 let config = make_config();
1333 let client = RawWindowClient::new(config);
1334
1335 let geo = WindowGeometry {
1336 x: 0,
1337 y: 0,
1338 width: 800,
1339 height: 600,
1340 };
1341 let id = client
1342 .create_window("Vis Test", "vis.app", geo, WindowType::Normal)
1343 .unwrap();
1344
1345 assert!(client.get_window(id).unwrap().visible);
1346
1347 client.set_visible(id, false).unwrap();
1348 assert!(!client.get_window(id).unwrap().visible);
1349
1350 client.set_visible(id, true).unwrap();
1351 assert!(client.get_window(id).unwrap().visible);
1352
1353 println!("✅ Visibility management working");
1354 }
1355}