1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//! Debugging helpers to handle `WAYLAND_DEBUG` env variable.

use std::{
    fmt::Display,
    os::unix::io::AsRawFd,
    time::{SystemTime, UNIX_EPOCH},
};

use crate::protocol::Argument;

/// The `WAYLAND_DEBUG` env variable is set to debug client.
pub fn has_debug_client_env() -> bool {
    matches!(std::env::var_os("WAYLAND_DEBUG"), Some(str) if str == "1" || str == "client")
}

/// Print the dispatched message to stderr in a following format:
///
/// `[timestamp] <- [email protected]_name(args)`
#[cfg_attr(coverage, coverage(off))]
pub fn print_dispatched_message<Id: Display, Fd: AsRawFd>(
    interface: &str,
    id: u32,
    msg_name: &str,
    args: &[Argument<Id, Fd>],
) {
    // Add timestamp to output.
    print_timestamp();

    eprint!(" <- {}@{}.{}, ({})", interface, id, msg_name, DisplaySlice(args));

    // Add a new line.
    eprintln!();
}

/// Print the send message to stderr in a following format:
///
/// `[timestamp] -> [email protected]_name(args)`
#[cfg_attr(coverage, coverage(off))]
pub fn print_send_message<Id: Display, Fd: AsRawFd>(
    interface: &str,
    id: u32,
    msg_name: &str,
    args: &[Argument<Id, Fd>],
    discarded: bool,
) {
    // Add timestamp to output.
    print_timestamp();

    if discarded {
        eprint!("[discarded]");
    }

    eprint!(" -> {}@{}.{}({})", interface, id, msg_name, DisplaySlice(args));

    // Add a new line.
    eprintln!();
}

pub(crate) struct DisplaySlice<'a, D>(pub &'a [D]);

impl<'a, D: Display> Display for DisplaySlice<'a, D> {
    #[cfg_attr(coverage, coverage(off))]
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut it = self.0.iter();
        if let Some(val) = it.next() {
            write!(f, "{}", val)?;
        }
        for val in it {
            write!(f, ", {}", val)?;
        }
        Ok(())
    }
}

/// Print timestamp in seconds.microseconds format.
#[cfg_attr(coverage, coverage(off))]
fn print_timestamp() {
    if let Ok(timestamp) = SystemTime::now().duration_since(UNIX_EPOCH) {
        // NOTE this is all to make timestamps the same with libwayland, so the log doesn't look
        // out of place when sys tries to log on their own.
        let time = (timestamp.as_secs() * 1000000 + timestamp.subsec_nanos() as u64 / 1000) as u32;
        // NOTE annotate timestamp so we know which library emmited the log entry.
        eprint!("[{:7}.{:03}][rs]", time / 1000, time % 1000);
    }
}