Skip to content

Commit

Permalink
place window using vpinball ini settings
Browse files Browse the repository at this point in the history
  • Loading branch information
francisdb committed Oct 31, 2024
1 parent d90085e commit 58fc059
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 16 deletions.
88 changes: 85 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize};
use std::fs::File;
use std::{env, io};

use log::info;
use std::io::Write;

const CONFIGURATION_FILE_NAME: &str = "vpxtool.cfg";
Expand Down Expand Up @@ -38,14 +39,95 @@ pub struct ResolvedConfig {
pub editor: Option<String>,
}

/// FullScreen = 0
/// PlayfieldFullScreen = 0
/// WindowPosX =
/// PlayfieldWindowPosX =
/// WindowPosY =
/// PlayfieldWindowPosY =
/// Width = 540
/// PlayfieldWidth = 540
/// Height = 960
/// PlayfieldHeight = 960
///
/// Note: For macOS with hidpi screen this these are logical sizes/locations, not pixel sizes
pub(crate) struct PlayfieldInfo {
pub(crate) fullscreen: bool,
pub(crate) x: Option<u32>,
pub(crate) y: Option<u32>,
pub(crate) width: Option<u32>,
pub(crate) height: Option<u32>,
}

pub(crate) struct VPinballConfig {
ini: ini::Ini,
}

impl VPinballConfig {
pub fn read(ini_path: &Path) -> Result<Self, ini::Error> {
info!("Reading vpinball ini file: {:?}", ini_path);
let ini = ini::Ini::load_from_file(ini_path)?;
Ok(VPinballConfig { ini })
}

pub fn get_pinmame_path(&self) -> Option<String> {
if let Some(standalone_section) = self.ini.section(Some("Standalone")) {
standalone_section.get("PinMAMEPath").map(|s| s.to_string())
} else {
None
}
}

pub fn get_playfield_info(&self) -> Option<PlayfieldInfo> {
if let Some(standalone_section) = self.ini.section(Some("Player")) {
// get all the values from PlayfieldXXX and fall back to the normal values
let fullscreen = match standalone_section.get("PlayfieldFullScreen") {
Some(value) => value == "1",
None => match standalone_section.get("FullScreen") {
Some(value) => value == "1",
None => true, // not sure if this is the correct default value for every os
},
};
let x = standalone_section
.get("PlayfieldWndX")
.or_else(|| standalone_section.get("WindowPosX"))
.and_then(|s| s.parse::<u32>().ok());

let y = standalone_section
.get("PlayfieldWndY")
.or_else(|| standalone_section.get("WindowPosY"))
.and_then(|s| s.parse::<u32>().ok());

let width = standalone_section
.get("PlayfieldWidth")
.or_else(|| standalone_section.get("Width"))
.and_then(|s| s.parse::<u32>().ok());

let height = standalone_section
.get("PlayfieldHeight")
.or_else(|| standalone_section.get("Height"))
.and_then(|s| s.parse::<u32>().ok());

Some(PlayfieldInfo {
fullscreen,
x,
y,
width,
height,
})
} else {
None
}
}
}

impl ResolvedConfig {
pub fn global_pinmame_folder(&self) -> PathBuf {
// first we try to read the ini file
let ini_file = self.vpinball_ini_file();
if ini_file.exists() {
let ini = ini::Ini::load_from_file(ini_file).unwrap();
let standalone_section = ini.section(Some("Standalone")).unwrap();
if let Some(value) = standalone_section.get("PinMAMEPath") {
let vpinball_config = VPinballConfig::read(&ini_file).unwrap();
if let Some(value) = vpinball_config.get_pinmame_path() {
// if the path exists we return it
let path = PathBuf::from(value);
if path.exists() {
Expand Down
90 changes: 77 additions & 13 deletions src/guifrontend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,19 @@ use bevy::sprite::{MaterialMesh2dBundle, Mesh2dHandle};
// use bevy_asset_loader::prelude::*;
// use bevy_egui::{egui, EguiContexts, EguiPlugin};
// use image::ImageReader;
use std::collections::HashSet;
use std::{
io,
path::{Path, PathBuf},
process::{exit, ExitStatus},
};

use crate::config::ResolvedConfig;
use crate::config::{ResolvedConfig, VPinballConfig};
use crate::indexer;
use crate::indexer::IndexedTable;
use bevy::window::*;
use colored::Colorize;
use console::Emoji;
use is_executable::IsExecutable;
use std::collections::HashSet;
use std::{
io,
path::{Path, PathBuf},
process::{exit, ExitStatus},
};

// enum Orientation {
// Horizontal,
Expand Down Expand Up @@ -49,6 +48,11 @@ pub struct Config {
pub config: ResolvedConfig,
}

#[derive(Resource)]
pub struct VpxConfig {
pub config: VPinballConfig,
}

#[derive(Resource)]
pub struct VpxTables {
pub indexed_tables: Vec<IndexedTable>,
Expand All @@ -60,6 +64,35 @@ pub struct InfoBox {
// info_string: String,
}

fn correct_mac_window_size(
mut window_query: Query<&mut Window, With<PrimaryWindow>>,
vpx_config: Res<VpxConfig>,
) {
let mut window = window_query.single_mut();
#[cfg(target_os = "macos")]
{
if window.resolution.scale_factor() != 1.0 {
info!(
"Resizing window for macOS with scale factor {}",
window.resolution.scale_factor(),
);
let vpinball_config = &vpx_config.config;
if let Some(playfield) = vpinball_config.get_playfield_info() {
if let (Some(logical_x), Some(logical_y)) = (playfield.x, playfield.y) {
// For macOS with scales factor > 1 this is not correct but we don't know the scale
// factor before the window is created.
let physical_x = logical_x as f32 * window.resolution.scale_factor();
let physical_y = logical_y as f32 * window.resolution.scale_factor();
// this will apply the width as if it was set in logical pixels
window.position =
WindowPosition::At(IVec2::new(physical_x as i32, physical_y as i32));
window.set_changed();
}
}
}
}
}

fn create_wheel(
mut commands: Commands,
asset_server: Res<AssetServer>,
Expand Down Expand Up @@ -381,7 +414,7 @@ fn gui_update(
tx: Res<StreamSender>,
config: Res<Config>,
) {
let mut window = window_query.single_mut();
let window = window_query.single_mut();

let width = window.width();
let height = window.height();
Expand Down Expand Up @@ -553,7 +586,7 @@ fn gui_update(
wheel.launch_path.clone().into_os_string().to_string_lossy()
);
println!("Hide window");
window.visible = false;
//window.visible = false;

let tx = tx.clone();
let path = wheel.launch_path.clone();
Expand Down Expand Up @@ -592,23 +625,54 @@ pub fn guifrontend(
// viewport: egui::ViewportBuilder::default().with_inner_size([400.0, 800.0]),
// ..Default::default()
// };

let vpinball_ini_path = config.vpinball_ini_file();
let vpinball_config = VPinballConfig::read(&vpinball_ini_path).unwrap();
let mut position = WindowPosition::default();
let mut mode = WindowMode::Fullscreen;
let mut resolution = WindowResolution::default();
if let Some(playfield) = vpinball_config.get_playfield_info() {
if let (Some(x), Some(y)) = (playfield.x, playfield.y) {
// For macOS with scale factor > 1 this is not correct but we don't know the scale
// factor before the window is created. We will correct the position later using the
// system "correct_mac_window_size".
let physical_x = x as i32;
let physical_y = y as i32;
position = WindowPosition::At(IVec2::new(physical_x, physical_y));
}
if let (Some(width), Some(height)) = (playfield.width, playfield.height) {
resolution = WindowResolution::new(width as f32, height as f32);
}
mode = if playfield.fullscreen {
WindowMode::Fullscreen
} else {
WindowMode::Windowed
};
}

App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "VPXTOOL".to_string(),
window_level: WindowLevel::AlwaysOnTop,
// window_level: WindowLevel::AlwaysOnTop,
resolution,
mode,
position,
..Default::default()
}),
..Default::default()
}))
.insert_resource(Config { config })
.insert_resource(VpxConfig {
config: vpinball_config,
})
.insert_resource(VpxTables {
indexed_tables: vpx_files_with_tableinfo,
})
.insert_resource(ClearColor(Color::srgb(0.1, 0.1, 0.1)))
// .insert_resource(ClearColor(Color::srgb(0.9, 0.3, 0.6)))
.add_event::<StreamEvent>()
.add_systems(Startup, setup)
.add_systems(Startup, (correct_mac_window_size, setup))
.add_systems(Startup, (create_wheel, create_flippers))
.add_systems(Startup, play_background_audio)
.add_systems(Update, gui_update)
Expand Down Expand Up @@ -687,7 +751,7 @@ fn read_stream(
println!("Showing window");
window.visible = true;
// bring window to front
window.window_level = WindowLevel::AlwaysOnTop;
// window.window_level = WindowLevel::AlwaysOnTop;
// request focus
window.focused = true;
events.send(StreamEvent(from_stream));
Expand Down

0 comments on commit 58fc059

Please sign in to comment.