screen_selector/
lib.rs

1// SPDX-License-Identifier: GPL-3.0-only
2
3//! A Unity plugin replacing the proprietary ScreenSelector.so.
4//!
5//! It is built using GTK 4, and released under the GNU GPL 3.0 only license.
6//!
7//! Once built, you should rename the shared object to ScreenSelector.so, and either drop it in
8//! your game’s plugins directory, or install it system-wide and symlink it in each game you want
9//! to use it in.
10
11use core::ffi::c_void;
12use once_cell::sync::OnceCell;
13use std::ffi::CStr;
14
15mod callbacks;
16mod cpp_vector;
17
18#[cfg(not(feature = "gui"))]
19mod cli;
20#[cfg(feature = "gui")]
21mod gtk;
22
23use cpp_vector::CppVector;
24
25type GetAxisDescription = extern "C" fn(i32, *mut i8, *mut i8, *mut i8) -> i32;
26type ConfigureAxis = extern "C" fn(i32, i32);
27type GetResolutions = extern "C" fn(u32) -> *mut CppVector<(u32, u32)>;
28type GetSelectedResolution = extern "C" fn(*mut u32, *mut u32, *mut bool);
29type SetSelectedResolution = extern "C" fn(u32, u32, bool);
30type GetQualityLevels = extern "C" fn() -> *mut CppVector<*const i8>;
31type GetSelectedQualityLevel = extern "C" fn() -> i32;
32type SetSelectedQualityLevel = extern "C" fn(i32);
33type GetDisplays = extern "C" fn() -> *mut CppVector<*const i8>;
34type GetSelectedDisplay = extern "C" fn() -> u32;
35type SetSelectedDisplay = extern "C" fn(u32);
36
37static GET_AXIS_DESCRIPTION: OnceCell<GetAxisDescription> = OnceCell::new();
38static CONFIGURE_AXIS: OnceCell<ConfigureAxis> = OnceCell::new();
39static GET_RESOLUTIONS: OnceCell<GetResolutions> = OnceCell::new();
40static GET_SELECTED_RESOLUTION: OnceCell<GetSelectedResolution> = OnceCell::new();
41static SET_SELECTED_RESOLUTION: OnceCell<SetSelectedResolution> = OnceCell::new();
42static GET_QUALITY_LEVELS: OnceCell<GetQualityLevels> = OnceCell::new();
43static GET_SELECTED_QUALITY_LEVEL: OnceCell<GetSelectedQualityLevel> = OnceCell::new();
44static SET_SELECTED_QUALITY_LEVEL: OnceCell<SetSelectedQualityLevel> = OnceCell::new();
45static GET_DISPLAYS: OnceCell<GetDisplays> = OnceCell::new();
46static GET_SELECTED_DISPLAY: OnceCell<GetSelectedDisplay> = OnceCell::new();
47static SET_SELECTED_DISPLAY: OnceCell<SetSelectedDisplay> = OnceCell::new();
48
49/// Sets the callbacks from Unity.
50#[no_mangle]
51pub extern "C" fn SetupUnityCallbacks(
52    get_axis_description: GetAxisDescription,
53    configure_axis: ConfigureAxis,
54    get_resolutions: GetResolutions,
55    get_selected_resolution: GetSelectedResolution,
56    set_selected_resolution: SetSelectedResolution,
57    get_quality_levels: GetQualityLevels,
58    get_selected_quality_level: GetSelectedQualityLevel,
59    set_selected_quality_level: SetSelectedQualityLevel,
60    get_displays: GetDisplays,
61    get_selected_display: GetSelectedDisplay,
62    set_selected_display: SetSelectedDisplay,
63) {
64    println!(
65        "SetupUnityCallbacks({:p}, {:p}, {:p}, {:p}, {:p}, {:p}, {:p}, {:p}, {:p}, {:p}, {:p})",
66        get_axis_description,
67        configure_axis,
68        get_resolutions,
69        get_selected_resolution,
70        set_selected_resolution,
71        get_quality_levels,
72        get_selected_quality_level,
73        set_selected_quality_level,
74        get_displays,
75        get_selected_display,
76        set_selected_display
77    );
78    GET_AXIS_DESCRIPTION.set(get_axis_description).unwrap();
79    CONFIGURE_AXIS.set(configure_axis).unwrap();
80    GET_RESOLUTIONS.set(get_resolutions).unwrap();
81    GET_SELECTED_RESOLUTION
82        .set(get_selected_resolution)
83        .unwrap();
84    SET_SELECTED_RESOLUTION
85        .set(set_selected_resolution)
86        .unwrap();
87    GET_QUALITY_LEVELS.set(get_quality_levels).unwrap();
88    GET_SELECTED_QUALITY_LEVEL
89        .set(get_selected_quality_level)
90        .unwrap();
91    SET_SELECTED_QUALITY_LEVEL
92        .set(set_selected_quality_level)
93        .unwrap();
94    GET_DISPLAYS.set(get_displays).unwrap();
95    GET_SELECTED_DISPLAY.set(get_selected_display).unwrap();
96    SET_SELECTED_DISPLAY.set(set_selected_display).unwrap();
97}
98
99/// Main entrypoint of this plugin.
100#[no_mangle]
101pub extern "C" fn LoadScreenSelectorWindow(
102    _module: *const c_void,
103    title: *const i8,
104    icon: *const i8,
105    pixbuf_path: *const i8,
106) -> i32 {
107    let title = unsafe { CStr::from_ptr(title) }
108        .to_string_lossy()
109        .to_string();
110    let icon = unsafe { CStr::from_ptr(icon) }
111        .to_string_lossy()
112        .to_string();
113    let pixbuf_path = unsafe { CStr::from_ptr(pixbuf_path) }
114        .to_string_lossy()
115        .to_string();
116
117    #[cfg(not(feature = "gui"))]
118    let ret = cli::load_screen_selector_window(title, icon, pixbuf_path);
119    #[cfg(feature = "gui")]
120    let ret = gtk::load_screen_selector_window(title, icon, pixbuf_path);
121    ret
122}