1use alloc::format;
31use alloc::string::String;
32use alloc::vec;
33use alloc::vec::Vec;
34use core::cell::RefCell;
35
36use rt::os::{syscall6, AT_FDCWD, O_CLOEXEC, O_PATH, SYS_CLOSE, SYS_CONNECT, SYS_GETUID, SYS_OPENAT, SYS_READ, SYS_SENDMSG, SYS_SOCKET, SYS_WRITE};
37
38use crate::{
39 BK_X11, E_CLOSE, E_DPI, E_FOCUS_IN, E_FOCUS_OUT, E_PAINT, E_RESIZE, Event, FD_EXT_SHIFT, FD_FOLDER, FD_SAVE, PAPER_N,
40 PJF_LANDSCAPE, PPage, PrintJob,
41};
42
43const AF_UNIX: u16 = 1;
44const SOCK_STREAM_CLOEXEC: usize = 1 | 0x80000;
45const POLLIN: u16 = 1;
46const EINTR: isize = -4;
47const MSG_NOSIGNAL: usize = 0x4000;
48
49const MB_CAP: usize = 32768; const MM_CAP: usize = MB_CAP + 4096; const RB_CAP: usize = 65536; const URI_CAP: usize = 2048;
53const UID_CAP: usize = 16;
54const HDR_CAP: usize = MM_CAP - MB_CAP - 32; const T_HELLO: i32 = 2000; const T_CALL: i32 = 20000; const KEEP_MAX: usize = 32; const MSG_CALL: u8 = 1;
59const MSG_RETURN: u8 = 2;
60const MSG_ERROR: u8 = 3;
61const MSG_SIGNAL: u8 = 4;
62const HF_PATH: u8 = 1;
63const HF_MEMBER: u8 = 3;
64const HF_REPLY: u8 = 5;
65const MP_NAME_END: usize = 200; const SKIP_DEPTH_MAX: u32 = 32;
67const OF_CAP: usize = 1024; const OF_WAIT: u32 = 1500; const PRN_T_DIALOG: u32 = 3_600_000; const PRN_T_PRINT: u32 = 600_000; pub const PATH_CAP: usize = 1040;
73
74const S_BUS_NAME: &[u8] = b"org.freedesktop.DBus";
75const S_BUS_PATH: &[u8] = b"/org/freedesktop/DBus";
76const S_HELLO: &[u8] = b"Hello";
77const S_ADDMATCH: &[u8] = b"AddMatch";
78const S_MATCH1: &[u8] = b"type='signal',interface='org.freedesktop.portal.Request',member='Response',path='";
79const S_MATCH_OWNER: &[u8] =
81 b"type='signal',sender='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.freedesktop.portal.Desktop'";
82const S_NAME_OWNER_CHANGED: &[u8] = b"NameOwnerChanged";
83const S_PORTAL_NAME: &[u8] = b"org.freedesktop.portal.Desktop";
84const S_PORTAL_PATH: &[u8] = b"/org/freedesktop/portal/desktop";
85const S_FILECHOOSER: &[u8] = b"org.freedesktop.portal.FileChooser";
86const S_RESPONSE: &[u8] = b"Response";
87const S_OPENURI: &[u8] = b"org.freedesktop.portal.OpenURI";
88const S_PRN_IFACE: &[u8] = b"org.freedesktop.portal.Print";
89const S_ALL_FILES: &[u8] = "Alle unterstützten Dateien".as_bytes();
90const S_HEXD: &[u8] = b"0123456789abcdef";
91const S_HEXU: &[u8] = b"0123456789ABCDEF";
92
93static PRN_PAPERS: [(f64, f64, &[u8], &[u8]); 5] = [
95 (210.0, 297.0, b"iso_a4", b"A4"),
96 (215.9, 279.4, b"na_letter", b"US Letter"),
97 (148.0, 210.0, b"iso_a5", b"A5"),
98 (215.9, 355.6, b"na_legal", b"US Legal"),
99 (297.0, 420.0, b"iso_a3", b"A3"),
100];
101static PRN_DKEYS: [&[u8]; 6] = [b"Width", b"Height", b"MarginTop", b"MarginBottom", b"MarginLeft", b"MarginRight"];
102const PRN_MM_PT: f64 = 2.834645669291339; enum Field {
106 None,
107 Str(usize, usize), U32(u32),
109}
110
111struct Bus {
112 fd: i32,
113 rbuf: Vec<u8>, rlen: usize,
115 mlen: usize, mhdr: usize, rp: usize, body: Vec<u8>,
119 b_over: bool, msg: Vec<u8>,
121 m_path: &'static [u8],
122 m_iface: &'static [u8],
123 m_member: &'static [u8],
124 m_dest: &'static [u8],
125 m_sig: Option<&'static [u8]>,
126 m_nfds: u32, m_sendfd: i32,
128 token_n: u32,
129 token: Vec<u8>,
130 req_path: Vec<u8>,
131 handle: Vec<u8>,
132 uri: Vec<u8>,
133 filter: Vec<u8>,
134 name: Vec<u8>,
135 folder: Vec<u8>,
136 keep: Vec<Event>,
137 skip_depth: u32,
138 neg_fd: bool, fd_ok: bool, of_keep: i32, prn_open: bool,
142 prn_uniq: Vec<u8>,
143}
144
145struct Global(RefCell<Option<Bus>>);
146unsafe impl Sync for Global {}
149static BUS: Global = Global(RefCell::new(None));
150
151fn with_bus<R>(f: impl FnOnce(&mut Bus) -> R) -> R {
152 let mut g = BUS.0.borrow_mut();
153 let b = g.get_or_insert_with(Bus::new);
154 f(b)
155}
156
157fn cstr(s: &[u8]) -> &[u8] {
161 s.iter().position(|&c| c == 0).map_or(s, |n| &s[..n])
162}
163
164fn copy_cap(s: &[u8], cap: usize) -> Vec<u8> {
166 let s = cstr(s);
167 if cap == 0 {
168 return Vec::new();
169 }
170 let mut n = s.len();
171 if n > cap - 1 {
172 n = cap - 1;
173 while n > 0 && s[n] & 0xC0 == 0x80 {
174 n -= 1;
175 }
176 }
177 s[..n].to_vec()
178}
179
180fn cat_cap(d: &mut Vec<u8>, s: &[u8], cap: usize) {
182 if d.len() >= cap {
183 return;
184 }
185 let t = copy_cap(s, cap - d.len());
186 d.extend_from_slice(&t);
187}
188
189fn sys_close(fd: i32) {
190 unsafe { syscall6(SYS_CLOSE, fd as usize, 0, 0, 0, 0, 0) };
192}
193
194fn env_is_1(name: &str) -> bool {
195 crate::env(name).is_some_and(|v| v.as_bytes().first() == Some(&b'1'))
196}
197
198fn parent() -> Vec<u8> {
200 if crate::disp_backend() == BK_X11 {
201 format!("x11:{:x}", crate::x11s::main_xid()).into_bytes()
202 } else {
203 Vec::new()
204 }
205}
206
207impl Bus {
208 fn new() -> Bus {
209 Bus {
210 fd: -1,
211 rbuf: vec![0; RB_CAP + 16],
212 rlen: 0,
213 mlen: 0,
214 mhdr: 0,
215 rp: 0,
216 body: Vec::with_capacity(MB_CAP),
217 b_over: false,
218 msg: Vec::with_capacity(MM_CAP),
219 m_path: S_BUS_PATH,
220 m_iface: S_BUS_NAME,
221 m_member: S_HELLO,
222 m_dest: S_BUS_NAME,
223 m_sig: None,
224 m_nfds: 0,
225 m_sendfd: -1,
226 token_n: 0,
227 token: Vec::new(),
228 req_path: Vec::new(),
229 handle: Vec::new(),
230 uri: Vec::new(),
231 filter: Vec::new(),
232 name: Vec::new(),
233 folder: Vec::new(),
234 keep: Vec::new(),
235 skip_depth: 0,
236 neg_fd: false,
237 fd_ok: false,
238 of_keep: 0,
239 prn_open: false,
240 prn_uniq: Vec::new(),
241 }
242 }
243
244 fn bus_open(&mut self) -> bool {
248 self.fd = -1;
249 self.rlen = 0;
250 let mut sa = [0u8; 112];
251 sa[..2].copy_from_slice(&AF_UNIX.to_le_bytes());
252 let mut alen = 0usize;
254 if let Some(a) = crate::env("DBUS_SESSION_BUS_ADDRESS").filter(|a| a.starts_with("unix:")) {
255 let b = cstr(a.as_bytes());
256 let mut i = 5;
257 loop {
258 let rest = &b[i.min(b.len())..];
259 let (abstr, skip) = if rest.starts_with(b"path=") {
260 (false, 5)
261 } else if rest.starts_with(b"abstract=") {
262 (true, 9)
263 } else {
264 match rest.iter().position(|&c| c == b',') {
266 Some(k) => {
267 i += k + 1;
268 continue;
269 }
270 None => break,
271 }
272 };
273 let mut o = if abstr { 3 } else { 2 };
274 for &c in &rest[skip..] {
275 if c == b',' {
276 break;
277 }
278 if o >= 108 {
279 return false;
280 }
281 sa[o] = c;
282 o += 1;
283 }
284 alen = o;
285 break;
286 }
287 }
288 if alen == 0 {
289 let uid = unsafe { syscall6(SYS_GETUID, 0, 0, 0, 0, 0, 0) } as u32;
291 let p = copy_cap(format!("/run/user/{uid}/bus").as_bytes(), 100);
292 sa[2..2 + p.len()].copy_from_slice(&p);
293 alen = p.len() + 2;
294 }
295 let fd = unsafe { syscall6(SYS_SOCKET, AF_UNIX as usize, SOCK_STREAM_CLOEXEC, 0, 0, 0, 0) };
297 if fd < 0 {
298 return false;
299 }
300 self.fd = fd as i32;
301 if unsafe { syscall6(SYS_CONNECT, fd as usize, sa.as_ptr() as usize, alen, 0, 0, 0) } != 0 {
303 self.bus_close();
304 return false;
305 }
306 let uid = unsafe { syscall6(SYS_GETUID, 0, 0, 0, 0, 0, 0) } as u32;
309 let digits = copy_cap(format!("{uid}").as_bytes(), UID_CAP);
310 let mut line = b"AUTH EXTERNAL ".to_vec();
311 for &d in &digits {
312 line.push(S_HEXD[(d >> 4) as usize]);
313 line.push(S_HEXD[(d & 15) as usize]);
314 }
315 line.extend_from_slice(b"\r\n");
316 if !self.bus_write(&[0]) || !self.bus_write(&line) || !self.read_line() || &self.rbuf[..2] != b"OK" {
317 self.bus_close();
318 return false;
319 }
320 self.rlen = 0;
321 self.fd_ok = false;
323 if self.neg_fd {
324 if !self.bus_write(b"NEGOTIATE_UNIX_FD\r\n") || !self.read_line() {
325 self.bus_close();
326 return false;
327 }
328 if &self.rbuf[..4] == b"AGRE" {
329 self.fd_ok = true;
330 }
331 self.rlen = 0;
332 }
333 if !self.bus_write(b"BEGIN\r\n") {
334 self.bus_close();
335 return false;
336 }
337 true
338 }
339
340 fn bus_close(&mut self) {
341 if self.fd >= 0 {
342 sys_close(self.fd);
343 self.fd = -1;
344 }
345 }
346
347 fn bus_write(&mut self, mut b: &[u8]) -> bool {
349 while !b.is_empty() {
350 let r = unsafe { syscall6(SYS_WRITE, self.fd as usize, b.as_ptr() as usize, b.len(), 0, 0, 0) };
352 if r == EINTR {
353 continue;
354 }
355 if r <= 0 {
356 return false;
357 }
358 b = &b[r as usize..];
359 }
360 true
361 }
362
363 fn bus_sendfd(&mut self, b: &[u8], fd: i32) -> bool {
365 loop {
366 let iov = [b.as_ptr() as usize, b.len()];
367 let mut ctl = [0u8; 24];
368 ctl[..8].copy_from_slice(&20u64.to_le_bytes()); ctl[8..12].copy_from_slice(&1u32.to_le_bytes()); ctl[12..16].copy_from_slice(&1u32.to_le_bytes()); ctl[16..20].copy_from_slice(&fd.to_le_bytes());
372 let mh = [0usize, 0, iov.as_ptr() as usize, 1, ctl.as_ptr() as usize, 24, 0];
374 let r = unsafe { syscall6(SYS_SENDMSG, self.fd as usize, mh.as_ptr() as usize, MSG_NOSIGNAL, 0, 0, 0) };
376 if r == EINTR {
377 continue;
378 }
379 if r <= 0 {
380 return false;
381 }
382 return self.bus_write(&b[r as usize..]);
384 }
385 }
386
387 fn bus_fill(&mut self, timeout_ms: i32) -> i32 {
389 loop {
390 let mut pfd = [0u8; 8];
391 pfd[..4].copy_from_slice(&self.fd.to_le_bytes());
392 pfd[4..6].copy_from_slice(&POLLIN.to_le_bytes());
393 let r = unsafe { rt::os::poll(pfd.as_mut_ptr() as usize, 1, timeout_ms) };
395 if r == EINTR {
396 continue;
397 }
398 if r < 0 {
399 return -1;
400 }
401 if r == 0 {
402 return 0;
403 }
404 if self.rlen >= RB_CAP {
405 return -1;
406 }
407 let room = RB_CAP - self.rlen;
408 let n = unsafe {
410 syscall6(SYS_READ, self.fd as usize, self.rbuf.as_mut_ptr().add(self.rlen) as usize, room, 0, 0, 0)
411 };
412 if n == EINTR {
413 continue;
414 }
415 if n <= 0 || n as usize > room {
416 return -1;
417 }
418 self.rlen += n as usize;
419 return 1;
420 }
421 }
422
423 fn read_line(&mut self) -> bool {
425 loop {
426 if self.rlen >= 2 && self.rbuf[..self.rlen].windows(2).any(|w| w == b"\r\n") {
427 return true;
428 }
429 if self.bus_fill(T_HELLO) != 1 {
430 return false;
431 }
432 }
433 }
434
435 fn body_reset(&mut self) {
440 self.body.clear();
441 self.b_over = false;
442 }
443
444 fn b_align(&mut self, n: usize) {
445 while self.body.len() & (n - 1) != 0 {
446 if self.body.len() >= MB_CAP {
447 self.b_over = true;
448 return;
449 }
450 self.body.push(0);
451 }
452 }
453
454 fn b_u8(&mut self, v: u8) {
455 if self.body.len() >= MB_CAP {
456 self.b_over = true;
457 return;
458 }
459 self.body.push(v);
460 }
461
462 fn b_u32(&mut self, v: u32) {
463 self.b_align(4);
464 if self.body.len() + 4 > MB_CAP {
465 self.b_over = true;
466 return;
467 }
468 self.body.extend_from_slice(&v.to_le_bytes());
469 }
470
471 fn b_f64(&mut self, v: f64) {
472 self.b_align(8);
473 if self.body.len() + 8 > MB_CAP {
474 self.b_over = true;
475 return;
476 }
477 self.body.extend_from_slice(&v.to_le_bytes());
478 }
479
480 fn b_str(&mut self, s: &[u8]) {
482 let s = cstr(s);
483 self.b_u32(s.len() as u32);
484 self.b_raw0(s);
485 }
486
487 fn b_sig(&mut self, s: &[u8]) {
489 let s = cstr(s);
490 self.b_u8(s.len() as u8);
491 self.b_raw0(s);
492 }
493
494 fn b_raw0(&mut self, s: &[u8]) {
496 if self.body.len() + s.len() + 1 > MB_CAP {
497 self.b_over = true;
498 return;
499 }
500 self.body.extend_from_slice(s);
501 self.body.push(0);
502 }
503
504 fn b_bytes(&mut self, s: &[u8]) {
505 if self.body.len() + s.len() > MB_CAP {
506 self.b_over = true;
507 return;
508 }
509 self.body.extend_from_slice(s);
510 }
511
512 fn b_array_begin(&mut self, elem_align: usize) -> (usize, usize) {
514 self.b_u32(0);
515 let slot = self.body.len().wrapping_sub(4);
516 self.b_align(elem_align);
517 (slot, self.body.len())
518 }
519
520 fn b_array_end(&mut self, (slot, start): (usize, usize)) {
522 let n = self.body.len().wrapping_sub(start) as u32;
523 if slot <= MB_CAP - 4 && slot + 4 <= self.body.len() {
524 self.body[slot..slot + 4].copy_from_slice(&n.to_le_bytes());
525 }
526 }
527
528 fn b_dict_key(&mut self, key: &[u8], sig: &[u8]) {
530 self.b_align(8);
531 self.b_str(key);
532 self.b_sig(sig);
533 }
534
535 fn h_field(&mut self, value: Option<&[u8]>, code: u8, typ: u8) {
538 let Some(v) = value else { return };
539 let v = cstr(v);
540 if self.msg.len() + v.len() + 24 > HDR_CAP {
541 self.b_over = true;
542 return;
543 }
544 while self.msg.len() & 7 != 0 {
545 self.msg.push(0);
546 }
547 self.msg.extend_from_slice(&[code, 1, typ, 0]);
548 if typ == b'g' {
549 self.msg.push(v.len() as u8);
550 } else {
551 while self.msg.len() & 3 != 0 {
552 self.msg.push(0);
553 }
554 self.msg.extend_from_slice(&(v.len() as u32).to_le_bytes());
555 }
556 self.msg.extend_from_slice(v);
557 self.msg.push(0);
558 }
559
560 fn msg_send(&mut self, serial: u32) -> bool {
562 if self.b_over {
563 return false;
564 }
565 let mut m = core::mem::take(&mut self.msg);
566 m.clear();
567 m.extend_from_slice(&[b'l', MSG_CALL, 0, 1]);
568 m.extend_from_slice(&(self.body.len() as u32).to_le_bytes());
569 m.extend_from_slice(&serial.to_le_bytes());
570 m.extend_from_slice(&[0; 4]); self.msg = m;
572 self.h_field(Some(self.m_path), HF_PATH, b'o');
573 self.h_field(Some(self.m_iface), 2, b's');
574 self.h_field(Some(self.m_member), HF_MEMBER, b's');
575 self.h_field(Some(self.m_dest), 6, b's');
576 self.h_field(self.m_sig, 8, b'g');
577 if self.m_nfds != 0 {
579 while self.msg.len() & 7 != 0 {
580 self.msg.push(0);
581 }
582 self.msg.extend_from_slice(&[9, 1, b'u', 0]);
583 self.msg.extend_from_slice(&self.m_nfds.to_le_bytes());
584 }
585 if self.b_over {
586 return false; }
588 let fl = (self.msg.len() - 16) as u32;
589 self.msg[12..16].copy_from_slice(&fl.to_le_bytes());
590 while self.msg.len() & 7 != 0 {
591 self.msg.push(0);
592 }
593 let mut m = core::mem::take(&mut self.msg);
594 m.extend_from_slice(&self.body);
595 let ok = if self.m_nfds != 0 { self.bus_sendfd(&m, self.m_sendfd) } else { self.bus_write(&m) };
596 self.msg = m;
597 ok
598 }
599
600 fn rd32(&self, o: usize) -> u32 {
603 u32::from_le_bytes([self.rbuf[o], self.rbuf[o + 1], self.rbuf[o + 2], self.rbuf[o + 3]])
604 }
605
606 fn msg_ready(&mut self) -> bool {
609 if self.rlen < 16 {
610 return false;
611 }
612 let hdr = (self.rd32(12) as u64 + 16 + 7) & !7;
613 let len = self.rd32(4) as u64 + hdr;
614 self.mhdr = hdr.min(RB_CAP as u64 + 1) as usize;
615 self.mlen = len.min(RB_CAP as u64 + 1) as usize;
616 self.mlen <= self.rlen
617 }
618
619 fn msg_next(&mut self, timeout_ms: i32) -> i32 {
621 loop {
622 if self.msg_ready() {
623 return 1;
624 }
625 if self.mlen > RB_CAP {
626 return -1;
627 }
628 let r = self.bus_fill(timeout_ms);
629 if r != 1 {
630 return r;
631 }
632 }
633 }
634
635 fn msg_consume(&mut self) {
637 let n = self.mlen.min(self.rlen);
638 self.rbuf.copy_within(n..self.rlen, 0);
639 self.rlen -= n;
640 }
641
642 fn msg_type(&self) -> u8 {
643 self.rbuf[1]
644 }
645
646 fn msg_field(&self, code: u8) -> Field {
648 let b = &self.rbuf;
649 let mut p: u64 = 16;
650 let end = (self.rd32(12) as u64 + 16).min(self.mhdr as u64);
651 loop {
652 p = (p + 7) & !7 & 0xFFFF_FFFF;
653 if p >= end {
654 return Field::None;
655 }
656 let c = b[p as usize];
657 let slen = b[p as usize + 1] as u64;
658 let typ = b[p as usize + 2];
659 p = (p + slen + 3) & 0xFFFF_FFFF;
660 match typ {
661 b'g' => {
662 if p >= end {
663 return Field::None;
664 }
665 let n = b[p as usize] as u64;
666 p += 1;
667 let e = p + n;
668 if e >= end || b[e as usize] != 0 {
669 return Field::None;
670 }
671 if c == code {
672 return Field::Str(p as usize, e as usize);
673 }
674 p = e + 1;
675 }
676 b'u' => {
677 p = (p + 3) & !3;
678 if p + 4 > end {
679 return Field::None;
680 }
681 let v = self.rd32(p as usize);
682 p += 4;
683 if c == code {
684 return Field::U32(v);
685 }
686 }
687 _ => {
688 p = (p + 3) & !3;
689 if p + 4 > end {
690 return Field::None;
691 }
692 let n = self.rd32(p as usize) as u64;
693 p += 4;
694 let e = p + n;
695 if e >= end || b[e as usize] != 0 {
696 return Field::None;
697 }
698 if c == code {
699 return Field::Str(p as usize, e as usize);
700 }
701 p = (e + 1) & 0xFFFF_FFFF;
702 }
703 }
704 }
705 }
706
707 fn field_is(&self, code: u8, s: &[u8]) -> bool {
709 match self.msg_field(code) {
710 Field::Str(a, e) => &self.rbuf[a..e] == cstr(s),
711 _ => false,
712 }
713 }
714
715 fn wait_reply(&mut self, serial: u32, timeout_ms: i32) -> bool {
717 loop {
718 if self.msg_next(timeout_ms) != 1 {
719 return false;
720 }
721 let t = self.msg_type();
722 if (t == MSG_RETURN || t == MSG_ERROR) && matches!(self.msg_field(HF_REPLY), Field::U32(v) if v == serial) {
723 return true;
724 }
725 self.msg_consume();
726 }
727 }
728
729 fn is_our_response(&self) -> bool {
731 if self.msg_type() != MSG_SIGNAL || !self.field_is(HF_MEMBER, S_RESPONSE) {
732 return false;
733 }
734 match self.msg_field(HF_PATH) {
735 Field::Str(a, e) => {
736 let p = &self.rbuf[a..e];
737 p == &self.req_path[..] || p == &self.handle[..]
738 }
739 _ => false,
740 }
741 }
742
743 fn keep_drain(&mut self) {
746 while let Some(e) = crate::evq_pop() {
747 if matches!(e.typ, E_RESIZE | E_DPI | E_FOCUS_IN | E_FOCUS_OUT | E_CLOSE | E_PAINT) && self.keep.len() < KEEP_MAX {
748 self.keep.push(e);
749 }
750 }
751 }
752
753 fn keep_restore(&mut self) {
755 for e in self.keep.drain(..) {
756 crate::evq_push(e);
757 }
758 }
759
760 fn breathe(&mut self) {
762 crate::wait(0);
763 self.keep_drain();
764 }
765
766 fn rd_body_begin(&mut self) {
769 self.rp = self.mhdr;
770 }
771
772 fn rd_align(&mut self, n: usize) {
773 self.rp = ((self.rp + n - 1) & !(n - 1)).min(self.mlen);
774 }
775
776 fn rd_skip(&mut self, n: u32) {
777 self.rp = (self.rp as u64 + n as u64).min(self.mlen as u64) as usize;
778 }
779
780 fn rd_u32(&mut self) -> u32 {
782 self.rd_align(4);
783 if self.rp + 4 > self.mlen {
784 self.rp = self.mlen;
785 return 0;
786 }
787 let v = self.rd32(self.rp);
788 self.rp += 4;
789 v
790 }
791
792 fn rd_f64(&mut self) -> f64 {
793 self.rd_align(8);
794 if self.rp + 8 > self.mlen {
795 self.rp = self.mlen;
796 return 0.0;
797 }
798 let mut b = [0u8; 8];
799 b.copy_from_slice(&self.rbuf[self.rp..self.rp + 8]);
800 self.rp += 8;
801 f64::from_le_bytes(b)
802 }
803
804 fn rd_str(&mut self) -> Option<Vec<u8>> {
807 let n = self.rd_u32() as u64;
808 let s = self.rp as u64;
809 let e = s + n + 1;
810 if e > self.mlen as u64 || self.rbuf[e as usize - 1] != 0 {
811 return None;
812 }
813 self.rp = e as usize;
814 Some(self.rbuf[s as usize..e as usize - 1].to_vec())
815 }
816
817 fn rd_sig(&mut self) -> Vec<u8> {
819 let p = self.rp;
820 if p >= self.mlen {
821 self.rp = self.mlen;
822 return Vec::new();
823 }
824 let n = self.rbuf[p] as usize;
825 let e = p + 1 + n + 1;
826 if e > self.mlen || self.rbuf[e - 1] != 0 {
827 self.rp = self.mlen;
828 return Vec::new();
829 }
830 self.rp = e;
831 self.rbuf[p + 1..e - 1].to_vec()
832 }
833
834 fn skip_value(&mut self, sig: &[u8], i: usize) -> usize {
836 self.skip_depth += 1;
837 let r = self.skip_value_in(sig, i);
838 self.skip_depth -= 1;
839 r
840 }
841
842 fn skip_value_in(&mut self, sig: &[u8], i: usize) -> usize {
843 let at = |k: usize| sig.get(k).copied().unwrap_or(0);
844 if self.skip_depth > SKIP_DEPTH_MAX {
845 self.rp = self.mlen;
847 return sig_end(sig, i);
848 }
849 let c = at(i);
850 self.rd_align(type_align(c));
851 match c {
852 b'y' => self.rd_skip(1),
853 b'n' | b'q' => self.rd_skip(2),
854 b'x' | b't' | b'd' => self.rd_skip(8),
855 b's' | b'o' => {
856 self.rd_str();
857 }
858 b'g' => {
859 self.rd_sig();
860 }
861 b'v' => {
862 let s = self.rd_sig();
863 self.skip_value(&s, 0);
864 }
865 b'a' => {
866 let n = self.rd_u32();
867 self.rd_align(type_align(at(i + 1)));
868 self.rd_skip(n);
869 return sig_end(sig, i + 1);
870 }
871 b'(' | b'{' => {
872 let mut k = i + 1;
873 loop {
874 match at(k) {
875 0 => return k,
876 b')' | b'}' => return k + 1,
877 _ => k = self.skip_value(sig, k),
878 }
879 }
880 }
881 _ => self.rd_skip(4), }
883 i + 1
884 }
885
886 fn set_bus_call(&mut self, member: &'static [u8], sig: Option<&'static [u8]>) {
889 self.m_path = S_BUS_PATH;
890 self.m_iface = S_BUS_NAME;
891 self.m_member = member;
892 self.m_dest = S_BUS_NAME;
893 self.m_sig = sig;
894 self.body_reset();
895 }
896
897 fn hello(&mut self) -> Option<Vec<u8>> {
899 self.set_bus_call(S_HELLO, None);
900 if !self.msg_send(1) || !self.wait_reply(1, T_HELLO) {
901 return None;
902 }
903 self.rd_body_begin();
904 let name = self.rd_str()?;
905 Some(name)
906 }
907
908 fn make_request_path(&mut self, uniq: &[u8]) {
910 self.token_n = self.token_n.wrapping_add(1);
911 self.token = copy_cap(format!("jfgui{}", self.token_n).as_bytes(), 32);
912 let mut p = b"/org/freedesktop/portal/desktop/request/".to_vec();
913 let mut s = cstr(uniq);
915 if s.first() == Some(&b':') {
916 s = &s[1..];
917 }
918 for &c in s {
919 if p.len() >= MP_NAME_END {
920 break;
921 }
922 p.push(if c == b'.' { b'_' } else { c });
923 }
924 p.push(b'/');
925 let tok = self.token.clone();
926 cat_cap(&mut p, &tok, 256);
927 self.req_path = p;
928 self.handle.clear();
929 }
930
931 fn add_match(&mut self, serial: u32) -> bool {
933 let mut t = copy_cap(S_MATCH1, 512);
934 let rp = self.req_path.clone();
935 cat_cap(&mut t, &rp, 512);
936 cat_cap(&mut t, b"'", 512);
937 self.set_bus_call(S_ADDMATCH, Some(b"s"));
938 self.b_str(&t);
939 self.msg_send(serial)
940 }
941
942 fn hello_match(&mut self) -> bool {
944 let Some(name) = self.hello() else { return false };
945 self.make_request_path(&name);
946 self.msg_consume();
947 self.add_match(2)
948 }
949
950 fn owner_match(&mut self, serial: u32) -> bool {
952 self.set_bus_call(S_ADDMATCH, Some(b"s"));
953 self.b_str(S_MATCH_OWNER);
954 self.msg_send(serial)
955 }
956
957 fn is_portal_gone(&mut self) -> bool {
959 if self.msg_type() != MSG_SIGNAL || !self.field_is(HF_MEMBER, S_NAME_OWNER_CHANGED) {
960 return false;
961 }
962 self.rd_body_begin();
963 let name = self.rd_str();
964 let _old = self.rd_str();
965 let new = self.rd_str();
966 name.as_deref() == Some(S_PORTAL_NAME) && new.is_some_and(|n| cstr(&n).is_empty())
967 }
968
969 fn file_dialog(&mut self, flags: u32, path: &mut String, exts: &[&[u8]]) -> (i32, u32) {
973 self.keep.clear();
974 if !self.bus_open() {
975 return (-1, 0);
976 }
977 let r = self.file_dialog_call(flags, path, exts);
978 self.bus_close();
979 self.keep_restore();
980 r
981 }
982
983 fn file_dialog_call(&mut self, flags: u32, path: &mut String, exts: &[&[u8]]) -> (i32, u32) {
984 if !self.hello_match() || !self.owner_match(9) {
985 return (-1, 0);
986 }
987 self.build_call(flags, path.as_bytes(), exts);
989 if !self.msg_send(3) || !self.wait_reply(3, T_CALL) || self.msg_type() == MSG_ERROR {
990 return (-1, 0);
991 }
992 self.rd_body_begin();
993 if let Some(h) = self.rd_str() {
994 self.handle = copy_cap(&h, 256);
995 }
996 self.msg_consume();
997 let r = self.wait_response();
999 if r <= 0 {
1000 return (r, 0);
1001 }
1002 let Some(p) = self.uri_to_path(PATH_CAP) else { return (0, 0) };
1004 let mut p = p;
1005 let index = if flags & FD_FOLDER != 0 { 0 } else { self.filter_index(exts) };
1006 if flags & (FD_SAVE | FD_FOLDER) == FD_SAVE {
1007 add_ext(&mut p, PATH_CAP, exts, index);
1008 }
1009 *path = String::from_utf8_lossy(&p).into_owned();
1010 (1, index)
1011 }
1012
1013 fn build_call(&mut self, flags: u32, path: &[u8], exts: &[&[u8]]) {
1015 let folder = flags & FD_FOLDER != 0;
1016 let save = !folder && flags & FD_SAVE != 0;
1017 self.m_path = S_PORTAL_PATH;
1018 self.m_iface = S_FILECHOOSER;
1019 self.m_member = if save { b"SaveFile" } else { b"OpenFile" };
1020 self.m_dest = S_PORTAL_NAME;
1021 self.m_sig = Some(b"ssa{sv}");
1022 self.body_reset();
1023 self.b_str(&parent());
1024 self.b_str(if save { b"Speichern unter" } else if folder { "Ordner auswählen".as_bytes() } else { "Öffnen".as_bytes() });
1025 let arr = self.b_array_begin(8);
1027 self.b_dict_key(b"handle_token", b"s");
1028 let tok = self.token.clone();
1029 self.b_str(&tok);
1030 self.b_dict_key(b"modal", b"b");
1031 self.b_u32(1);
1032 if folder {
1033 self.b_dict_key(b"directory", b"b");
1035 self.b_u32(1);
1036 if !path.is_empty() {
1037 self.b_dict_key(b"current_folder", b"ay");
1038 let a2 = self.b_array_begin(1);
1039 let mut f = path.to_vec();
1040 f.push(0);
1041 self.b_bytes(&f);
1042 self.b_array_end(a2);
1043 }
1044 self.b_array_end(arr);
1045 return;
1046 }
1047 self.split_path(path);
1049 if save && !self.name.is_empty() {
1050 self.b_dict_key(b"current_name", b"s");
1051 let n = self.name.clone();
1052 self.b_str(&n);
1053 }
1054 if !self.folder.is_empty() {
1055 self.b_dict_key(b"current_folder", b"ay");
1056 let a2 = self.b_array_begin(1);
1057 let mut f = self.folder.clone();
1058 f.push(0); self.b_bytes(&f);
1060 self.b_array_end(a2);
1061 }
1062 if !exts.is_empty() {
1063 self.b_dict_key(b"filters", b"a(sa(us))");
1065 let a2 = self.b_array_begin(8);
1066 if !save {
1067 self.b_filter(-1, exts); }
1069 for i in 0..exts.len() {
1070 self.b_filter(i as i32, exts);
1071 }
1072 self.b_array_end(a2);
1073 self.b_dict_key(b"current_filter", b"(sa(us))");
1075 let mut cur = -1;
1076 if save {
1077 cur = ((flags >> FD_EXT_SHIFT) & 0xFF) as i32;
1078 if cur as usize >= exts.len() {
1079 cur = 0;
1080 }
1081 }
1082 self.b_filter(cur, exts);
1083 }
1084 self.b_array_end(arr);
1085 }
1086
1087 fn b_filter(&mut self, i: i32, exts: &[&[u8]]) {
1090 let fname = filter_name(i, exts);
1091 self.b_align(8);
1092 self.b_str(&fname);
1093 let arr = self.b_array_begin(8);
1094 let mut j = if i == -1 { 0 } else { i as usize };
1095 while let Some(&e) = exts.get(j) {
1096 let mut p = 0;
1097 loop {
1098 let ext = ext_copy(&e[p..]);
1099 self.b_align(8);
1100 self.b_u32(0); let mut pat = copy_cap(b"*.", 128);
1104 for &c in cstr(&ext) {
1105 if pat.len() + 4 >= 512 {
1106 break;
1107 }
1108 if c.is_ascii_alphabetic() {
1109 pat.extend_from_slice(&[b'[', c.to_ascii_lowercase(), c.to_ascii_uppercase(), b']']);
1110 } else {
1111 pat.push(c);
1112 }
1113 }
1114 self.b_str(&pat);
1115 let mut k = p;
1117 let mut next = None;
1118 while k < e.len() && e[k] != 0 && e[k] != b'=' {
1119 k += 1;
1120 if e[k - 1] == b';' {
1121 next = Some(k);
1122 break;
1123 }
1124 }
1125 match next {
1126 Some(n) => p = n,
1127 None => break,
1128 }
1129 }
1130 if i != -1 {
1131 break;
1132 }
1133 j += 1;
1134 }
1135 self.b_array_end(arr);
1136 }
1137
1138 fn split_path(&mut self, path: &[u8]) {
1140 self.folder.clear();
1141 self.name.clear();
1142 let tmp = copy_cap(path, 512);
1143 match tmp.iter().rposition(|&c| c == b'/') {
1144 None => self.name = copy_cap(&tmp, 256),
1145 Some(k) => {
1146 self.name = copy_cap(&tmp[k + 1..], 256);
1147 let dir = &tmp[..k];
1148 if dir.first() == Some(&b'/') && crate::file_exists_raw(&String::from_utf8_lossy(dir)) == 2 {
1149 self.folder = copy_cap(dir, 512);
1150 }
1151 }
1152 }
1153 }
1154
1155 fn uri_to_path(&self, cap: usize) -> Option<Vec<u8>> {
1157 let u = &self.uri;
1158 if !u.starts_with(b"file://") {
1159 return None;
1160 }
1161 let at = |k: usize| u.get(k).copied().unwrap_or(0);
1162 let mut k = 7;
1163 if at(k) != b'/' {
1164 loop {
1166 if k >= u.len() || at(k) == 0 {
1167 return None;
1168 }
1169 if at(k) == b'/' {
1170 break;
1171 }
1172 k += 1;
1173 }
1174 }
1175 let mut out = Vec::new();
1176 while out.len() < cap - 1 && k < u.len() && at(k) != 0 {
1177 let mut c = at(k);
1178 k += 1;
1179 if c == b'%' {
1180 if k + 2 > URI_CAP || at(k) == 0 || at(k + 1) == 0 {
1182 break;
1183 }
1184 c = (hexval(at(k)) << 4 | hexval(at(k + 1))) as u8;
1185 k += 2;
1186 if c == 0 {
1187 break; }
1189 }
1190 out.push(c);
1191 }
1192 Some(out)
1193 }
1194
1195 fn filter_index(&self, exts: &[&[u8]]) -> u32 {
1197 if self.filter.is_empty() {
1198 return 0;
1199 }
1200 for i in 0..exts.len() {
1201 if filter_name(i as i32, exts) == self.filter {
1202 return i as u32;
1203 }
1204 }
1205 0
1206 }
1207
1208 fn wait_response(&mut self) -> i32 {
1211 loop {
1212 if self.msg_ready() {
1213 if self.is_our_response() {
1214 let r = self.parse_response();
1215 self.msg_consume();
1216 return r;
1217 }
1218 if self.is_portal_gone() {
1219 self.msg_consume();
1220 return -1; }
1222 self.msg_consume();
1223 continue;
1224 }
1225 let r = self.bus_fill(40);
1226 if r < 0 {
1227 return -1;
1228 }
1229 if r > 0 {
1230 continue;
1231 }
1232 self.breathe(); }
1234 }
1235
1236 fn parse_response(&mut self) -> i32 {
1239 self.uri.clear();
1240 self.filter.clear();
1241 self.rd_body_begin();
1242 if self.rd_u32() != 0 {
1243 return 0;
1244 }
1245 let len = self.rd_u32();
1246 self.rd_align(8);
1247 let end = (len as usize + self.rp) & 0xFFFF_FFFF;
1248 loop {
1249 self.rd_align(8);
1250 if self.rp >= end {
1251 break;
1252 }
1253 let Some(key) = self.rd_str() else { break };
1254 let sig = self.rd_sig();
1255 if key == b"uris" {
1256 if sig == b"as" {
1257 let n = self.rd_u32();
1258 let start = self.rp;
1259 if n != 0 {
1260 if let Some(s) = self.rd_str() {
1261 self.uri = copy_cap(&s, URI_CAP); }
1263 }
1264 self.rp = start; self.rd_skip(n);
1266 continue;
1267 }
1268 } else if key == b"current_filter" && sig == b"(sa(us))" {
1269 self.rd_align(8);
1270 let start = self.rp;
1271 if let Some(s) = self.rd_str() {
1272 self.filter = copy_cap(&s, 256); }
1274 self.rp = start;
1275 }
1276 self.skip_value(&sig, 0);
1277 }
1278 (!self.uri.is_empty()) as i32
1279 }
1280
1281 fn of_release(&mut self) {
1285 if self.of_keep > 0 {
1286 sys_close(self.of_keep);
1287 self.of_keep = 0;
1288 }
1289 }
1290
1291 fn open_folder(&mut self, path: &[u8]) -> i32 {
1293 self.of_release();
1294 let path = cstr(path);
1295 if path.is_empty() {
1296 return -1;
1297 }
1298 let mut of = Vec::new();
1300 if path[0] != b'/' {
1301 of = copy_cap(crate::cwd().as_bytes(), OF_CAP);
1302 cat_cap(&mut of, b"/", OF_CAP);
1303 }
1304 cat_cap(&mut of, path, OF_CAP);
1305 let mut z = of.clone();
1307 z.push(0);
1308 let fd = unsafe { syscall6(SYS_OPENAT, AT_FDCWD, z.as_ptr() as usize, O_PATH | O_CLOEXEC, 0, 0, 0) };
1310 if fd < 0 {
1311 return -1;
1312 }
1313 let fd = fd as i32;
1314 let mut r = -1;
1315 if of_uri(&mut self.uri, &of) {
1316 if env_is_1("jfgui_OPEN_DRYRUN") {
1317 let line = [
1318 &b"plat_open_folder: OpenURI.OpenDirectory(fd of "[..],
1319 &of,
1320 b"), fallback OpenURI.OpenURI(",
1321 &self.uri,
1322 b")\n",
1323 ]
1324 .concat();
1325 crate::write_stdout(&line);
1326 r = 0;
1327 } else {
1328 r = self.of_send(fd);
1329 }
1330 }
1331 sys_close(fd);
1332 r
1333 }
1334
1335 fn open_url(&mut self, url: &[u8]) -> i32 {
1337 self.of_release();
1338 let url = cstr(url);
1339 if url.is_empty() || url.len() - 1 > URI_CAP - 2 {
1340 return -1; }
1342 self.uri = copy_cap(url, URI_CAP);
1343 if env_is_1("jfgui_OPEN_DRYRUN") {
1344 let line = [&b"plat_open_url: OpenURI.OpenURI("[..], &self.uri, b")\n"].concat();
1345 crate::write_stdout(&line);
1346 return 0;
1347 }
1348 self.of_send(-1)
1349 }
1350
1351 fn of_send(&mut self, fd: i32) -> i32 {
1354 self.keep.clear();
1355 self.neg_fd = fd >= 0; let ok = self.bus_open();
1357 self.neg_fd = false;
1358 if !ok {
1359 return -1;
1360 }
1361 let r = self.of_send_call(fd);
1362 self.bus_close();
1363 self.keep_restore();
1364 r
1365 }
1366
1367 fn of_send_call(&mut self, fd: i32) -> i32 {
1368 if !self.hello_match() {
1369 return -1;
1370 }
1371 let mut done = false;
1372 if fd >= 0 && self.fd_ok {
1373 self.of_build(true);
1375 self.m_nfds = 1;
1376 self.m_sendfd = fd;
1377 let ok = self.msg_send(3);
1378 self.m_nfds = 0;
1379 if !ok {
1380 return -1;
1381 }
1382 done = self.of_answer(3);
1383 }
1384 if !done {
1385 self.of_build(false);
1387 if !self.msg_send(4) || !self.of_answer(4) {
1388 return -1;
1389 }
1390 }
1391 match self.of_wait(OF_WAIT) {
1393 -2 => {
1394 self.of_keep = self.fd;
1395 self.fd = -1;
1396 0
1397 }
1398 0 => 0,
1399 _ => -1,
1400 }
1401 }
1402
1403 fn of_answer(&mut self, serial: u32) -> bool {
1406 if !self.wait_reply(serial, T_CALL) {
1407 return false;
1408 }
1409 if self.msg_type() == MSG_ERROR {
1410 self.msg_consume();
1411 return false;
1412 }
1413 self.handle.clear();
1414 self.rd_body_begin();
1415 if let Some(h) = self.rd_str() {
1416 self.handle = copy_cap(&h, 256);
1417 }
1418 self.msg_consume();
1419 true
1420 }
1421
1422 fn of_build(&mut self, with_fd: bool) {
1425 self.m_path = S_PORTAL_PATH;
1426 self.m_iface = S_OPENURI;
1427 self.m_member = if with_fd { b"OpenDirectory" } else { b"OpenURI" };
1428 self.m_dest = S_PORTAL_NAME;
1429 self.m_sig = Some(if with_fd { b"sha{sv}" } else { b"ssa{sv}" });
1430 self.body_reset();
1431 self.b_str(&parent());
1432 if with_fd {
1433 self.b_u32(0); } else {
1435 let u = self.uri.clone();
1436 self.b_str(&u);
1437 }
1438 let arr = self.b_array_begin(8);
1439 self.b_dict_key(b"handle_token", b"s");
1440 let t = self.token.clone();
1441 self.b_str(&t);
1442 self.b_array_end(arr);
1443 }
1444
1445 fn resp_wait(&mut self, ms: u32, mut page: Option<&mut PPage>) -> i32 {
1449 let deadline = crate::time_us() + ms as u64 * 1000;
1450 loop {
1451 if self.msg_ready() {
1452 if self.is_our_response() {
1453 self.rd_body_begin();
1454 let code = self.rd_u32();
1455 if code == 0 {
1456 if let Some(p) = page.as_deref_mut() {
1457 self.prn_results(p);
1458 }
1459 }
1460 self.msg_consume();
1461 return code as i32;
1462 }
1463 self.msg_consume();
1464 continue;
1465 }
1466 if crate::time_us() >= deadline {
1467 return -2;
1468 }
1469 let r = self.bus_fill(20);
1470 if r < 0 {
1471 return -1;
1472 }
1473 if r > 0 {
1474 continue;
1475 }
1476 if crate::disp_backend() != 0 {
1477 self.breathe(); }
1479 }
1480 }
1481
1482 fn of_wait(&mut self, ms: u32) -> i32 {
1483 self.resp_wait(ms, None)
1484 }
1485
1486 fn prn_prepare(&mut self, job: &PrintJob, page: &mut PPage, title: &[u8]) -> i32 {
1492 self.prn_end();
1493 self.keep.clear();
1494 self.neg_fd = true;
1495 let ok = self.bus_open();
1496 self.neg_fd = false;
1497 self.prn_open = true;
1498 if !ok || !self.fd_ok {
1499 return -1; }
1501 let Some(u) = self.hello() else { return -1 };
1502 self.prn_uniq = copy_cap(&u, 128);
1503 self.msg_consume();
1504 if !self.prn_match(2) {
1505 return -1;
1506 }
1507 self.prn_build_prepare(job, title);
1508 if !self.msg_send(3) || !self.of_answer(3) {
1509 return -1;
1510 }
1511 match self.resp_wait(PRN_T_DIALOG, Some(page)) {
1512 r if r < 0 => -1,
1513 0 => 1,
1514 _ => 0, }
1516 }
1517
1518 fn prn_send(&mut self, title: &[u8], fd: i32, token: u32) -> i32 {
1521 if !self.prn_open || self.fd < 0 || !self.prn_match(4) {
1522 return -1;
1523 }
1524 self.prn_build_print(title, token);
1525 self.m_nfds = 1;
1526 self.m_sendfd = fd;
1527 let ok = self.msg_send(5);
1528 self.m_nfds = 0;
1529 if !ok || !self.of_answer(5) {
1530 return -1;
1531 }
1532 match self.resp_wait(PRN_T_PRINT, None) {
1533 r @ (0 | 1) => r,
1534 _ => -1, }
1536 }
1537
1538 fn prn_end(&mut self) {
1540 if self.prn_open {
1541 self.prn_open = false;
1542 self.bus_close();
1543 self.keep_restore();
1544 }
1545 }
1546
1547 fn prn_match(&mut self, serial: u32) -> bool {
1549 let u = self.prn_uniq.clone();
1550 self.make_request_path(&u);
1551 self.add_match(serial)
1552 }
1553
1554 fn prn_head(&mut self, member: &'static [u8], sig: &'static [u8]) {
1556 self.m_member = member;
1557 self.m_sig = Some(sig);
1558 self.m_path = S_PORTAL_PATH;
1559 self.m_iface = S_PRN_IFACE;
1560 self.m_dest = S_PORTAL_NAME;
1561 self.body_reset();
1562 self.b_str(&parent());
1563 }
1564
1565 fn prn_dict_s(&mut self, key: &[u8], s: &[u8]) {
1566 self.b_dict_key(key, b"s");
1567 self.b_str(s);
1568 }
1569
1570 fn prn_dict_d(&mut self, key: &[u8], v: f64) {
1571 self.b_dict_key(key, b"d");
1572 self.b_f64(v);
1573 }
1574
1575 fn prn_dict_u(&mut self, key: &[u8], sig: &[u8], v: u32) {
1576 self.b_dict_key(key, sig);
1577 self.b_u32(v);
1578 }
1579
1580 fn prn_build_prepare(&mut self, job: &PrintJob, title: &[u8]) {
1583 self.prn_head(b"PreparePrint", b"ssa{sv}a{sv}a{sv}");
1584 self.b_str(title);
1585 let p = PRN_PAPERS[if job.paper >= PAPER_N { 0 } else { job.paper as usize }];
1586 let orient: &[u8] = if job.flags & PJF_LANDSCAPE != 0 { b"landscape" } else { b"portrait" };
1587 let a = self.b_array_begin(8);
1589 self.prn_dict_s(b"orientation", orient);
1590 self.b_array_end(a);
1591 let a = self.b_array_begin(8);
1593 self.prn_dict_s(b"Name", p.2);
1594 self.prn_dict_s(b"DisplayName", p.3);
1595 self.prn_dict_d(b"Width", p.0);
1596 self.prn_dict_d(b"Height", p.1);
1597 self.prn_dict_s(b"Orientation", orient);
1598 self.b_array_end(a);
1599 let a = self.b_array_begin(8);
1601 let t = self.token.clone();
1602 self.prn_dict_s(b"handle_token", &t);
1603 self.prn_dict_u(b"modal", b"b", 1);
1604 self.b_array_end(a);
1605 }
1606
1607 fn prn_build_print(&mut self, title: &[u8], token: u32) {
1609 self.prn_head(b"Print", b"ssha{sv}");
1610 self.b_str(title);
1611 self.b_u32(0); let a = self.b_array_begin(8);
1613 let t = self.token.clone();
1614 self.prn_dict_s(b"handle_token", &t);
1615 self.prn_dict_u(b"modal", b"b", 1);
1616 self.prn_dict_u(b"token", b"u", token);
1617 self.b_array_end(a);
1618 }
1619
1620 fn prn_dict_end(&mut self) -> usize {
1623 let n = self.rd_u32() as usize;
1624 self.rd_align(8);
1625 let room = self.mlen.saturating_sub(self.rp);
1626 self.rp + n.min(room)
1627 }
1628
1629 fn prn_results(&mut self, page: &mut PPage) {
1631 let end = self.prn_dict_end();
1632 loop {
1633 self.rd_align(8);
1634 if self.rp >= end {
1635 return;
1636 }
1637 let Some(key) = self.rd_str() else { return };
1638 let sig = self.rd_sig();
1639 if key == b"token" {
1640 if sig == b"u" {
1641 page.token = self.rd_u32();
1642 continue;
1643 }
1644 } else if key == b"page-setup" && sig == b"a{sv}" {
1645 self.prn_setup(page);
1646 continue;
1647 }
1648 self.skip_value(&sig, 0);
1649 }
1650 }
1651
1652 fn prn_setup(&mut self, page: &mut PPage) {
1655 let mut mm = [0f64; 6];
1656 let mut land = 0u32;
1657 let end = self.prn_dict_end();
1658 loop {
1659 self.rd_align(8);
1660 if self.rp >= end {
1661 break;
1662 }
1663 let Some(key) = self.rd_str() else { break };
1664 let sig = self.rd_sig();
1665 if sig.first() == Some(&b'd') {
1666 if sig.len() == 1 {
1667 let v = self.rd_f64();
1668 if let Some(k) = PRN_DKEYS.iter().position(|&n| n == &key[..]) {
1669 mm[k] = v;
1670 }
1671 continue;
1672 }
1673 } else if key == b"Orientation" && sig == b"s" {
1674 if let Some(s) = self.rd_str() {
1675 land = s.windows(9).any(|w| w == b"landscape") as u32;
1676 }
1677 continue;
1678 }
1679 self.skip_value(&sig, 0);
1680 }
1681 let (mut w, mut h) = (mm[0], mm[1]);
1682 if !(w > 0.0) || !(h > 0.0) {
1683 return;
1684 }
1685 w *= PRN_MM_PT;
1686 h *= PRN_MM_PT;
1687 page.flags = land;
1688 if land != 0 {
1689 core::mem::swap(&mut w, &mut h);
1690 }
1691 page.w = w;
1692 page.h = h;
1693 page.t = mm[2] * PRN_MM_PT;
1694 page.b = mm[3] * PRN_MM_PT;
1695 page.l = mm[4] * PRN_MM_PT;
1696 page.r = mm[5] * PRN_MM_PT;
1697 }
1698}
1699
1700fn sig_end(sig: &[u8], mut i: usize) -> usize {
1704 let at = |k: usize| sig.get(k).copied().unwrap_or(0);
1705 while at(i) == b'a' {
1706 i += 1;
1707 }
1708 if at(i) != b'(' && at(i) != b'{' {
1709 return i + 1;
1710 }
1711 let mut depth = 0i32;
1712 loop {
1713 let c = at(i);
1714 if c == 0 {
1715 return i;
1716 }
1717 i += 1;
1718 match c {
1719 b'(' | b'{' => depth += 1,
1720 b')' | b'}' => {
1721 depth -= 1;
1722 if depth == 0 {
1723 return i;
1724 }
1725 }
1726 _ => {}
1727 }
1728 }
1729}
1730
1731fn type_align(c: u8) -> usize {
1733 match c {
1734 b'n' | b'q' => 2,
1735 b'x' | b't' | b'd' | b'(' | b'{' => 8,
1736 b'b' | b'i' | b'u' | b'h' | b's' | b'o' | b'a' => 4,
1737 _ => 1,
1738 }
1739}
1740
1741fn hexval(c: u8) -> u32 {
1743 let c = c as u32;
1744 let d = c.wrapping_sub(48);
1745 if d <= 9 {
1746 return d;
1747 }
1748 let x = (c | 32).wrapping_sub(87);
1749 if x <= 15 { x } else { 0 }
1750}
1751
1752fn ext_copy(e: &[u8]) -> Vec<u8> {
1754 let mut v = Vec::new();
1755 for &c in e {
1756 if c == 0 || c == b'=' || c == b';' || v.len() >= 30 {
1757 break;
1758 }
1759 v.push(c);
1760 }
1761 v
1762}
1763
1764fn filter_name(i: i32, exts: &[&[u8]]) -> Vec<u8> {
1767 if i == -1 {
1768 return copy_cap(S_ALL_FILES, 128);
1769 }
1770 let e = exts.get(i as usize).copied().unwrap_or(b"");
1771 match e.iter().position(|&c| c == b'=') {
1772 Some(k) => copy_cap(&e[k + 1..], 128),
1773 None => {
1774 let mut x = ext_copy(e);
1775 x.make_ascii_uppercase();
1776 let mut f = copy_cap(&x, 128);
1777 cat_cap(&mut f, b"-Datei", 128);
1778 f
1779 }
1780 }
1781}
1782
1783fn add_ext(path: &mut Vec<u8>, cap: usize, exts: &[&[u8]], index: u32) {
1785 let from = path.iter().rposition(|&c| c == b'/').unwrap_or(0);
1786 if path[from..].contains(&b'.') {
1787 return;
1788 }
1789 let Some(e) = exts.get(index as usize) else { return };
1790 let ext = ext_copy(e);
1791 cat_cap(path, b".", cap);
1792 cat_cap(path, &ext, cap);
1793}
1794
1795fn of_uri(uri: &mut Vec<u8>, path: &[u8]) -> bool {
1798 uri.clear();
1799 uri.extend_from_slice(b"file://");
1800 for &c in path {
1801 if uri.len() >= URI_CAP - 4 {
1802 return false;
1803 }
1804 if c.is_ascii_alphanumeric() || matches!(c, b'-' | b'.' | b'_' | b'~' | b'/') {
1805 uri.push(c);
1806 } else {
1807 uri.push(b'%');
1808 uri.push(S_HEXU[(c >> 4) as usize]);
1809 uri.push(S_HEXU[(c & 15) as usize]);
1810 }
1811 }
1812 uri.len() < URI_CAP - 4 }
1814
1815pub fn file_dialog(flags: u32, path: &mut String, exts: &[&str]) -> (i32, u32) {
1819 let list: Vec<&[u8]> = exts.iter().map(|e| cstr(e.as_bytes())).take_while(|e| !e.is_empty()).collect();
1820 with_bus(|b| b.file_dialog(flags, path, &list))
1821}
1822
1823pub fn open_folder(path: &str) -> i32 {
1825 if !crate::policy::fs_allowed(path, crate::policy::FS_READ) {
1826 return -1;
1827 }
1828 with_bus(|b| b.open_folder(path.as_bytes()))
1829}
1830
1831pub fn open_url(url: &str) -> i32 {
1833 if !crate::policy::url_allowed(url) {
1834 return -1;
1835 }
1836 with_bus(|b| b.open_url(url.as_bytes()))
1837}
1838
1839pub(crate) fn portal_print_prepare(job: &PrintJob, page: &mut PPage, title: &str) -> i32 {
1841 with_bus(|b| b.prn_prepare(job, page, title.as_bytes()))
1842}
1843
1844pub(crate) fn portal_print_send(title: &str, fd: i32, token: u32) -> i32 {
1846 with_bus(|b| b.prn_send(title.as_bytes(), fd, token))
1847}
1848
1849pub(crate) fn portal_print_end() {
1851 with_bus(|b| b.prn_end())
1852}