Skip to main content

wasma_sys/
wasma_client_unix_posix_raw_window.rs

1// WASMA - Windows Assignment System Monitoring Architecture
2// wasma_client_unix_posix_raw_window.rs
3// Raw POSIX Window Management Client
4// Window management through raw POSIX fd, rendering delegated to WindowClient
5// UClientEngine trait'ini implement eder (raw_app ile paralel)
6// Ocak 2026
7
8use 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
19// ============================================================================
20// POSIX WINDOW CONTROL PROTOCOL
21// ============================================================================
22// Communication protocol over POSIX fd for WASMA window management.
23// Each command has 16 byte fixed header + variable length data.
24//
25// Başlık yapısı:
26//   [0..4]   magic: 0x57 0x41 0x57 0x4D ("WAWM")
27//   [4..8]   command: RawWindowCommand (u32 LE)
28//   [8..12]  window_id: u32 LE
29//   [12..16] payload_len: u32 LE (sonraki byte sayısı)
30//
31// Yanıt yapısı:
32//   [0..4]   magic: 0x57 0x52 0x53 0x50 ("WRSP")
33//   [4..8]   status: RawWindowStatus (u32 LE)
34//   [8..12]  window_id: u32 LE
35//   [12..16] payload_len: u32 LE
36
37pub const WASMA_CMD_MAGIC: [u8; 4] = [0x57, 0x41, 0x57, 0x4D]; // "WAWM"
38pub const WASMA_RSP_MAGIC: [u8; 4] = [0x57, 0x52, 0x53, 0x50]; // "WRSP"
39pub const WASMA_HDR_SIZE: usize = 16;
40
41/// Pencere yönetimi komutları
42#[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/// Yanıt durum kodları
88#[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
113// ============================================================================
114// POSIX PENCERE FD YÖNETİCİSİ
115// ============================================================================
116
117/// POSIX fd üzerinden pencere kontrol kanalı
118pub struct RawWindowFd {
119    /// Kontrol kanalı fd'si (komut gönder / yanıt al)
120    ctrl_fd: Option<RawFd>,
121    /// Kaynak tanımı
122    source: RawAppSource,
123    /// Bağlı mı?
124    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    /// Kontrol kanalını aç
137    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    /// 16 byte komut başlığı yazar
175    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    /// payload byte'larını yazar
212    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    /// Reads response header: (status, window_id, payload_len)
237    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    /// Komut gönder + yanıt al
259    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        // Komut yaz
273        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        // Yanıt oku
279        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// ============================================================================
304// RAW PENCERE KAYDEDICI - Yerel pencere durumu
305// ============================================================================
306
307/// Yerel pencere kaydı — sunucuya gerek kalmadan hızlı sorgu
308#[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    /// Kaydı günceller ve last_modified'ı tazeler
349    pub fn touch(&mut self) {
350        self.last_modified = SystemTime::now();
351    }
352}
353
354// ============================================================================
355// GEOMETRY CODEC - WindowGeometry ↔ byte dizisi dönüşümü
356// ============================================================================
357
358pub mod geometry_codec {
359    use crate::window_handling::WindowGeometry;
360
361    pub const GEOMETRY_SIZE: usize = 16; // 4 × i32/u32 LE
362
363    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
385/// WindowState ↔ u8 dönüşümü
386pub 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
411// ============================================================================
412// RAW WINDOW CLIENT - Ana yapı
413// ============================================================================
414
415/// RawWindowClient
416///
417/// Pencere yönetimini POSIX fd üzerinden raw olarak yürütür.
418/// Rendering tamamen WindowClient'a devredilir.
419/// UClientEngine trait'ini implement eder → raw_app ile simetrik.
420///
421/// Katman hiyerarşisi:
422///   WasmaCore
423///     └── WindowHandler          (üst seviye yönetim)
424///           └── WindowClient     (rendering + viewport)
425///                 └── RawWindowClient  ← BU MODÜL (raw POSIX yönetim)
426pub struct RawWindowClient {
427    config: Arc<WasmaConfig>,
428
429    /// POSIX kontrol kanalı
430    fd_manager: RawWindowFd,
431
432    /// Yerel pencere kaydedici (hızlı sorgu için)
433    window_registry: Arc<Mutex<HashMap<u64, RawWindowRecord>>>,
434
435    /// Pencere ID sayacı
436    next_id: Arc<Mutex<u64>>,
437
438    /// Rendering için WindowClient referansı
439    /// None ise rendering atlanır (sadece yönetim modu)
440    window_client: Option<Arc<Mutex<WindowClient>>>,
441
442    /// Motor aktif mi?
443    active: bool,
444
445    /// SectionMemory — UClientEngine uyumluluğu için
446    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    /// WindowClient'ı bağlar (rendering için)
481    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    /// Özel kaynak adresi ayarlar
487    pub fn with_source(mut self, source: RawAppSource) -> Self {
488        self.fd_manager = RawWindowFd::new(source);
489        self
490    }
491
492    /// Sonraki pencere ID'sini üretir
493    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    // -------------------------------------------------------------------------
501    // PENCERE YÖNETİM İŞLEMLERİ
502    // -------------------------------------------------------------------------
503
504    /// Create new window
505    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        // Payload: geometry(16) + title_len(2) + title + appid_len(2) + appid + type(1)
517        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        // fd bağlı değilse yerel modda çalış
531        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 // connectionless mode: only local registration
549        };
550
551        if remote_ok {
552            // Yerel kayıt
553            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    /// Pencereyi kapat ve kaynakları serbest bırak
571    pub fn destroy_window(&self, window_id: u64) -> Result<(), String> {
572        // Önce çocuk pencereleri kapat
573        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        // Ebeveynden çıkar
585        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        // Sunucuya bildir
598        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        // Yerel kayıttan sil
607        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    /// Pencere geometrisini ayarla
617    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                // WindowClient'a bildir (rendering yeniden hesaplanır)
643                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    /// Pencere geometrisini sorgula
654    pub fn get_geometry(&self, window_id: u64) -> Result<WindowGeometry, String> {
655        // Önce yerel kayıta bak (önbellekleme)
656        {
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        // Yerel kayıt yok, sunucuya sor
664        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    /// Pencere durumunu ayarla (minimize, maximize, fullscreen, hidden, normal)
680    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    /// Pencere durumunu sorgula
708    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    /// Fokus ayarla
717    pub fn set_focus(&self, window_id: u64) -> Result<(), String> {
718        // Önce tüm pencerelerin fokusunu kaldır
719        {
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    /// Fokuslu pencereyi sorgula
745    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    /// Başlık ayarla
751    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    /// Görünürlük ayarla
779    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    /// Establish parent-child relationship
801    pub fn set_parent(&self, child_id: u64, parent_id: u64) -> Result<(), String> {
802        // parent var mı kontrol et
803        {
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        // Çocukta parent_id ayarla
822        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        // Ebeveynde children listesine ekle
830        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    /// Tüm pencereleri listele
842    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    /// Tek pencere kaydını sorgula
850    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    /// Rendering tetikle — WindowClient'a devredilir
856/// Rendering tetikle — wsdg-open modeli: uygulama kendi render eder
857pub 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    // wsdg-open modeli: spawned app renders itself
871    // WindowClient no longer handles rendering
872    self.dispatch_data(data);
873    Ok(())
874}
875    /// Ping — bağlantı sağlık kontrolü
876    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
890// ============================================================================
891// UCLİENT ENGINE TRAIT İMPLEMENTASYONU (raw_app ile paralel)
892// ============================================================================
893
894impl UClientEngine for RawWindowClient {
895    fn start_engine(&mut self) -> Result<(), Box<dyn std::error::Error>> {
896        println!("🔌 RawWindowClient: Opening control channel...");
897
898        // fd kanalını bağla (hata olursa yerel modda devam et)
899        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        // Olay döngüsü — sunucu gönderilen pencere olaylarını işler
912        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                // Olaya hazır mı? (50ms timeout)
918                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                // Başlık oku
934                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                        // Olay işle (ileride pencere olayları burada dispatch edilecek)
945                        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        // Raw window modunda dispatch: focused pencereye render et
963        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        // Tüm pencereleri kapat
984        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
1001// ============================================================================
1002// ÖZEL YARDIMCI METODLAR
1003// ============================================================================
1004
1005impl RawWindowClient {
1006    /// Sunucudan gelen olay başlığını işler
1007    fn handle_server_event(&self, hdr: &[u8]) {
1008        if hdr.len() < WASMA_HDR_SIZE {
1009            return;
1010        }
1011        // Magic kontrolü
1012        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        // Gelecekte: FocusChanged, GeometryChanged, CloseRequest vb. olaylar işlenecek
1024    }
1025}
1026
1027// ============================================================================
1028// BUILDER
1029// ============================================================================
1030
1031pub 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// ============================================================================
1084// TESTLER
1085// ============================================================================
1086
1087#[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        // Parent kapatılınca çocuk da kapanır
1250        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        // ID sırasına göre sıralanmış olmalı
1316        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}