screen-selector 0.1.1

A reimplementation of Unity’s ScreenSelector.so plugin, using GTK 4
Documentation
// SPDX-License-Identifier: GPL-3.0-only

use core::mem::MaybeUninit;
use std::ffi::CStr;

use crate::{
    CONFIGURE_AXIS, GET_AXIS_DESCRIPTION, GET_DISPLAYS, GET_QUALITY_LEVELS, GET_RESOLUTIONS,
    GET_SELECTED_DISPLAY, GET_SELECTED_QUALITY_LEVEL, GET_SELECTED_RESOLUTION,
    SET_SELECTED_DISPLAY, SET_SELECTED_QUALITY_LEVEL, SET_SELECTED_RESOLUTION,
};

fn get_axis_description(axis: i32) -> Option<(String, String, String)> {
    let func = GET_AXIS_DESCRIPTION.get().unwrap();
    let mut control = MaybeUninit::<[i8; 8192]>::uninit();
    let mut primary = MaybeUninit::<[i8; 8192]>::uninit();
    let mut secondary = MaybeUninit::<[i8; 8200]>::uninit();
    let ret = func(
        axis,
        control.as_mut_ptr() as *mut i8,
        primary.as_mut_ptr() as *mut i8,
        secondary.as_mut_ptr() as *mut i8,
    );
    if ret == 0 {
        let control = unsafe { control.assume_init() };
        let primary = unsafe { primary.assume_init() };
        let secondary = unsafe { secondary.assume_init() };
        let control = unsafe { CStr::from_ptr(&control as *const i8) }
            .to_string_lossy()
            .into_owned();
        let primary = unsafe { CStr::from_ptr(&primary as *const i8) }
            .to_string_lossy()
            .into_owned();
        let secondary = unsafe { CStr::from_ptr(&secondary as *const i8) }
            .to_string_lossy()
            .into_owned();
        Some((control, primary, secondary))
    } else {
        None
    }
}

pub struct AxisIter {
    axis: i32,
}

impl Iterator for AxisIter {
    type Item = (String, String, String);

    fn next(&mut self) -> Option<Self::Item> {
        let ret = get_axis_description(self.axis);
        self.axis += 1;
        ret
    }
}

pub fn iter_axis_description() -> AxisIter {
    AxisIter { axis: 0 }
}

pub fn configure_axis(axis: i32, enable: i32) {
    let func = CONFIGURE_AXIS.get().unwrap();
    func(axis, enable);
}

pub fn get_resolutions(display: u32) -> Vec<(u32, u32)> {
    let func = GET_RESOLUTIONS.get().unwrap();
    let resolutions = unsafe { &*func(display) };
    resolutions.iter().cloned().collect()
}

pub fn get_selected_resolution() -> (u32, u32, bool) {
    let mut width = 0;
    let mut height = 0;
    let mut windowed = false;
    let func = GET_SELECTED_RESOLUTION.get().unwrap();
    func(&mut width, &mut height, &mut windowed);
    (width, height, windowed)
}

pub fn set_selected_resolution(width: u32, height: u32, windowed: bool) {
    let func = SET_SELECTED_RESOLUTION.get().unwrap();
    func(width, height, windowed);
}

pub fn get_quality_levels() -> Vec<String> {
    let func = GET_QUALITY_LEVELS.get().unwrap();
    let qualities = unsafe { &*func() };
    qualities
        .iter()
        .map(|name| {
            unsafe { CStr::from_ptr(*name) }
                .to_string_lossy()
                .into_owned()
        })
        .collect()
}

pub fn get_selected_quality_level() -> i32 {
    let func = GET_SELECTED_QUALITY_LEVEL.get().unwrap();
    func()
}

pub fn set_selected_quality_level(quality: i32) {
    let func = SET_SELECTED_QUALITY_LEVEL.get().unwrap();
    func(quality);
}

pub fn get_displays() -> Vec<String> {
    let func = GET_DISPLAYS.get().unwrap();
    let displays = unsafe { &*func() };
    displays
        .iter()
        .map(|name| {
            unsafe { CStr::from_ptr(*name) }
                .to_string_lossy()
                .into_owned()
        })
        .collect()
}

pub fn get_selected_display() -> u32 {
    let func = GET_SELECTED_DISPLAY.get().unwrap();
    func()
}

pub fn set_selected_display(display: u32) {
    let func = SET_SELECTED_DISPLAY.get().unwrap();
    func(display);
}