mangolog/
mangolog.rs

1// Copyright (c) Meta Platforms, Inc. and affiliates.
2
3// This software may be used and distributed according to the terms of the
4// GNU General Public License version 2.
5use scx_utils::mangoapp::{MANGOAPP_PROJ_ID, mangoapp_msg_v1};
6
7use anyhow::Result;
8use anyhow::bail;
9use libc::{IPC_NOWAIT, msgget, msgrcv};
10
11use std::mem;
12use std::path::Path;
13use std::thread;
14use std::time::Duration;
15use std::{ffi::CString, io};
16
17fn main() -> Result<()> {
18    // NOTE: the mangoapp file has to be present when gamescope launches in order to read from the
19    // message queue. For example when launching Counter Strike 2 the mangoapp file needs to be in
20    // $HOME/.local/share/Steam/steamapps/common/Counter-String Global Offensive/mangoapp and the
21    // program needs to run in the same directory as well.
22    let file_path = Path::new("mangoapp");
23    if !file_path.exists() {
24        bail!("mangoapp file does not exists");
25    }
26
27    // Create the key for msgget reads using the mangoapp file
28    let path = CString::new("mangoapp").unwrap();
29    let key = unsafe { libc::ftok(path.as_ptr(), MANGOAPP_PROJ_ID) };
30    if key == -1 {
31        bail!("failed to ftok: {}", io::Error::last_os_error());
32    }
33
34    // Get the key from the queue
35    let msgid = unsafe { msgget(key.try_into().unwrap(), 0) };
36    if msgid == -1 {
37        bail!("msgget failed: {}", std::io::Error::last_os_error());
38    }
39
40    let mut raw_msg: mangoapp_msg_v1 = unsafe { mem::zeroed() };
41
42    loop {
43        let msg_size = unsafe {
44            msgrcv(
45                msgid,
46                &mut raw_msg as *mut _ as *mut libc::c_void,
47                mem::size_of::<mangoapp_msg_v1>() - mem::size_of::<i64>(),
48                0,
49                IPC_NOWAIT, // XXX: this should probably use MSG_COPY as it pulls messages off the
50                            // queue and may mess with mangohud or other mangoapp uses.
51            )
52        };
53
54        if msg_size as isize != -1 {
55            let frametime = raw_msg.visible_frametime_ns;
56            let upscale = raw_msg.fsr_upscale;
57            let sharpness = raw_msg.fsr_sharpness;
58            let app_frametime_ns = raw_msg.app_frametime_ns;
59            let pid = raw_msg.pid;
60            let latency_ns = raw_msg.latency_ns;
61            let output_width = raw_msg.output_width;
62            let output_height = raw_msg.output_height;
63            let wants_hdr = raw_msg.wants_hdr();
64            let steam_focused = raw_msg.steam_focused();
65
66            println!("Received MangoApp data:");
67            println!("  Visible Frametime: {}", frametime);
68            println!("  FSR Upscale: {}", upscale);
69            println!("  FSR Sharpness: {}", sharpness);
70            println!("  App Frametime: {}", app_frametime_ns);
71            println!("  Latency: {}", latency_ns);
72            println!("  PID: {}", pid);
73            println!("  Output Width: {}", output_width);
74            println!("  Output Height: {}", output_height);
75            println!("  App Wants HDR: {}", wants_hdr);
76            println!("  Steam Focused: {}", steam_focused);
77            println!(
78                "  Engine Name: {}",
79                String::from_utf8_lossy(unsafe {
80                    &*(raw_msg.engine_name.as_ptr() as *const [u8; 40])
81                })
82            );
83        } else {
84            bail!(
85                "mangoapp: msgrcv returned -1 with error {}",
86                io::Error::last_os_error()
87            );
88        }
89        thread::sleep(Duration::from_secs(1));
90    }
91}