diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9756a0768..553b73c34 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -92,6 +92,11 @@ jobs: path: ${{ env.PIP_CACHE_DIR }} key: ${{ runner.os }}-pyproject-toml-${{ secrets.CACHE_VERSION }}-${{ hashFiles('pyproject.toml') }} + - name: Install ImageMagick + shell: bash + run: | + cargo make install-imagemagick + - name: Set up python builder shell: bash run: | @@ -160,6 +165,11 @@ jobs: path: ${{ env.PIP_CACHE_DIR }} key: ${{ runner.os }}-pyproject-toml-${{ secrets.CACHE_VERSION }}-${{ hashFiles('pyproject.toml') }} + - name: Install ImageMagick + shell: bash + run: | + cargo make install-imagemagick + - name: Set up python builder run: | cargo make setup-builder diff --git a/Cargo.lock b/Cargo.lock index 31f98e83b..6fce66bdf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -900,14 +900,12 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39522e96686d38f4bc984b9198e3a0613264abaebaff2c5c918bfa6b6da09af" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" dependencies = [ - "cfg-if 1.0.0", "crc32fast", - "libc", - "miniz_oxide 0.5.1", + "miniz_oxide 0.5.3", ] [[package]] @@ -1218,9 +1216,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jpeg-decoder" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be7ef4b99870f0c9f2fc2f20dbef72707e2bcca675bb9196734cf433e999b0c5" +checksum = "9478aa10f73e7528198d75109c8be5cd7d15fb530238040148d5f9a22d4c5b3b" dependencies = [ "rayon", ] @@ -1451,9 +1449,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" dependencies = [ "adler", ] @@ -1919,7 +1917,7 @@ dependencies = [ "bitflags", "crc32fast", "deflate", - "miniz_oxide 0.5.1", + "miniz_oxide 0.5.3", ] [[package]] @@ -1986,7 +1984,7 @@ dependencies = [ "cfg-if 1.0.0", "indoc", "libc", - "parking_lot 0.11.2", + "parking_lot 0.12.1", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", diff --git a/Makefile.toml b/Makefile.toml index b4c621a89..135551065 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -122,14 +122,11 @@ exec --fail-on-error ${python} -m py2many --rust=1 /tmp/piksi_tools_constants.py ''' [tasks.generate-resources] -command = "${PYSIDE2_RCC}" -args = [ - "resources/console_resources.qrc", - "-o", - "swiftnav_console/console_resources.py", - "-g", - "python", -] +dependencies = ["build-splash-version"] +script_runner = "@duckscript" +script = ''' + exec --fail-on-error "${PYSIDE2_RCC}" "resources/console_resources.qrc" "-o" "swiftnav_console/console_resources.py" "-g" "python" +''' [tasks.check-capnp] script_runner = "@shell" @@ -357,7 +354,6 @@ script = "sudo apt-get install gsfonts imagemagick" script = "brew install imagemagick" [tasks.build-splash-version] -private = true script_runner = "@duckscript" script = ''' output = exec --fail-on-error git describe --always --tags --dirty @@ -370,8 +366,7 @@ else end ''' -[tasks.build-splash-bin] -private = true +[tasks.build-windowpos-bin] dependencies = ["build-splash-version"] command = "cargo" args = [ @@ -379,21 +374,31 @@ args = [ "--release", "-vv", "--features", - "splash", + "winit", "--bin", - "swift-console-splash", + "windowpos", ] [tasks.build-console-bin] -private = true -dependencies = ["build-splash-bin"] +command = "cargo" +args = [ + "build", + "--release", + "-vv", + "--features=entrypoint", + "--bin", + "${APP_NAME}", +] + +[tasks.build-console-bin.linux] +dependencies = ["build-windowpos-bin"] command = "cargo" args = [ "build", "--release", "-vv", "--features", - "entrypoint", + "entrypoint,splash", "--bin", "${APP_NAME}", ] @@ -412,19 +417,21 @@ script_runner = "@duckscript" script = ''' app_name = get_env APP_NAME cp target/release/${app_name} py39-dist/${app_name} -cp target/release/swift-console-splash py39-dist/swift-console-splash os = os_family if eq ${os} mac exec --fail-on-error install_name_tool -change /install/lib/libpython3.9.dylib @executable_path/lib/libpython3.9.dylib py39-dist/${app_name} +elseif eq ${os} linux + cp target/release/windowpos py39-dist/windowpos end ''' [tasks.build-dist-install-console.windows] script = ''' -cp target/release/swift-console-splash.exe py39-dist/swift-console-splash.exe cp target/release/${APP_NAME}.exe py39-dist/${APP_NAME}.exe cp target/release/${APP_NAME}.d py39-dist/${APP_NAME}.d -cp target/release/swift_console.pdb py39-dist/swift_console.pdb +if is_path_exists target/release/swift_console.pdb + cp target/release/swift_console.pdb py39-dist/swift_console.pdb +end cp target/x86_64-pc-windows-msvc/release/console_backend.pdb py39-dist/Lib/site-packages/console_backend/console_backend.pdb ''' @@ -688,7 +695,6 @@ mkdir ${contents_dir} mkdir ${contents_mac_os} exec --fail-on-error cp -r py39-dist "${contents_resources_dir}" exec --fail-on-error mv ./${contents_resources_dir}/${app_original_name} "./${contents_mac_os}/${app_file_prefix}" -exec --fail-on-error mv ./${contents_resources_dir}/swift-console-splash "./${contents_mac_os}/swift-console-splash" exec --fail-on-error ln -s "../Resources/lib" ${contents_mac_os}/lib exec --fail-on-error ln -s "../Resources/.frozen" ${contents_mac_os}/.frozen exec --fail-on-error ln -s "../Resources/resources" ${contents_mac_os}/resources @@ -885,6 +891,20 @@ dependencies = ["store-version", "copy-capnp"] command = "cargo" args = ["check"] +[tasks.watch-entrypoint] +dependencies = ["store-version", "copy-capnp"] +command = "cargo" +args = [ + "watch", + "--exec", + "check --bin swift-console --features=entrypoint,splash", +] + +[tasks.watch-windowpos] +dependencies = ["store-version", "copy-capnp"] +command = "cargo" +args = ["watch", "--exec", "check --bin windowpos --features=winit"] + [tasks.python-type-check] dependencies = ["set-python-files"] command = "${PYTHON}" diff --git a/console_backend/src/common_constants.rs b/console_backend/src/common_constants.rs index 3290b187e..4e451f8d5 100644 --- a/console_backend/src/common_constants.rs +++ b/console_backend/src/common_constants.rs @@ -335,6 +335,6 @@ pub enum ApplicationMetadata { ORGANIZATION_NAME, #[strum(serialize = "swiftnav.com")] ORGANIZATION_DOMAIN, - #[strum(serialize = "swift-toolbox")] + #[strum(serialize = "Swift Console")] APPLICATION_NAME, } diff --git a/entrypoint/Cargo.toml b/entrypoint/Cargo.toml index cc66a1093..5a7ddc6e5 100644 --- a/entrypoint/Cargo.toml +++ b/entrypoint/Cargo.toml @@ -17,37 +17,31 @@ required-features = ["entrypoint"] [[bin]] bench = false -name = "swift-console-splash" -path = "src/splash.rs" -required-features = ["splash"] +name = "windowpos" +path = "src/windowpos.rs" +required-features = ["winit"] [dependencies.lazy_static] -optional = true version = "1.4.0" +[dependencies.image] +version = "0.24.2" + [dependencies.winit] -optional = true version = "0.26.1" - -[dependencies.image] optional = true -version = "0.24.2" [dependencies.minifb] -optional = true default-features = false features = ["x11"] version = "0.23" +optional = true [dependencies.pyo3] default-features = false features = ["auto-initialize"] -optional = true version = "0.16" - -[features] -entrypoint = ["pyo3", "windows"] -splash = ["minifb", "image", "winit", "windows", "lazy_static"] +optional = true [target] [target."cfg(target_os = \"windows\")"] @@ -58,5 +52,10 @@ version = "0.1" [target."cfg(target_os = \"windows\")".dependencies] [target."cfg(target_os = \"windows\")".dependencies.windows] features = ["Win32_System_Console", "Win32_Foundation"] -optional = true version = ">=0.24" +optional = true + +[features] +default = [] +entrypoint = ["pyo3", "windows", "minifb"] +splash = [] diff --git a/entrypoint/build.rs b/entrypoint/build.rs index 24220adf7..9dfd3ceb0 100644 --- a/entrypoint/build.rs +++ b/entrypoint/build.rs @@ -1,4 +1,4 @@ -#[cfg(target_os = "windows")] +#[cfg(target_os = "windows")] use winres::WindowsResource; type Result = std::result::Result>; diff --git a/entrypoint/src/lib.rs b/entrypoint/src/lib.rs index 8d55d5b2a..ae998fc5b 100644 --- a/entrypoint/src/lib.rs +++ b/entrypoint/src/lib.rs @@ -1,3 +1,26 @@ +use std::io::Cursor; + +use lazy_static::lazy_static; + +#[cfg(feature = "splash")] +pub mod splash; + +pub type Error = Box; +pub type Result = std::result::Result; + +lazy_static! { + pub static ref SPLASH_IMAGE: image::DynamicImage = { + let splash_image_buf = include_bytes!(concat!("../../", env!("CONSOLE_SPLASH_IMAGE"))); + image::io::Reader::with_format( + std::io::BufReader::new(Cursor::new(splash_image_buf)), + image::ImageFormat::Jpeg, + ) + .decode() + .expect("could not decode splash image") + }; +} + +#[cfg(feature = "entrypoint")] pub fn attach_console() { #[cfg(target_os = "windows")] { diff --git a/entrypoint/src/main.rs b/entrypoint/src/main.rs index 384ef10e3..aec3e363f 100644 --- a/entrypoint/src/main.rs +++ b/entrypoint/src/main.rs @@ -1,13 +1,11 @@ -#![cfg_attr(target_os = "windows", windows_subsystem = "windows")] - -type Result = std::result::Result>; +#![cfg_attr(target_os = "windows", windows_subsystem = "windows")] use pyo3::prelude::*; use pyo3::types::PyTuple; use entrypoint::attach_console; -const SWIFT_CONSOLE_PID: &str = "SWIFT_CONSOLE_PID"; +type Result = std::result::Result>; fn handle_wayland() { #[cfg(target_os = "linux")] @@ -25,37 +23,31 @@ fn handle_wayland() { } } +fn handle_splash() { + #[cfg(feature = "splash")] + { + std::env::set_var( + "SWIFTNAV_CONSOLE_SPLASH", + entrypoint::splash::marker_filepath(), + ); + entrypoint::splash::spawn(); + } +} + fn main() -> Result<()> { attach_console(); handle_wayland(); let current_exe = std::env::current_exe()?; let parent = current_exe.parent().ok_or("no parent directory")?; let args: Vec<_> = std::env::args().collect(); - let helper_found = args - .iter() - .any(|arg| matches!(arg.as_ref(), "--help" | "-h" | "--version" | "-V")); - if !helper_found { - let mut command = std::process::Command::new(parent.join("swift-console-splash")); - match command.spawn() { - Ok(child) => { - let pid = child.id(); - std::env::set_var(SWIFT_CONSOLE_PID, format!("{pid}")); - } - Err(e) => { - eprintln!("Starting splash screen failed: {e}"); - } - }; - } - std::env::set_var("SWIFTNAV_CONSOLE_FROZEN", parent); - std::env::set_var("PYTHONHOME", parent); std::env::set_var("PYTHONDONTWRITEBYTECODE", "1"); + handle_splash(); let exit_code = Python::with_gil(|py| { let args = PyTuple::new(py, args); let snav = py.import("swiftnav_console.main")?; snav.call_method("main", (args,), None)?.extract::() })?; - std::process::exit(exit_code); } diff --git a/entrypoint/src/splash.rs b/entrypoint/src/splash.rs index 0257c2d33..9866c7d08 100644 --- a/entrypoint/src/splash.rs +++ b/entrypoint/src/splash.rs @@ -1,22 +1,18 @@ -#![cfg_attr(target_os = "windows", windows_subsystem = "windows")] - use std::{ - io::Cursor, path::PathBuf, + process::Command, time::{Duration, Instant}, }; use lazy_static::lazy_static; use minifb::{Window, WindowOptions}; -use entrypoint::attach_console; +use crate::Result; +const STARTUP_TIMEOUT_DURATION: Duration = Duration::from_secs(2); const TIMEOUT_DURATION: Duration = Duration::from_secs(30); const TEMP_FILENAME: &str = "swiftnav_console"; -type Error = Box; -type Result = std::result::Result; - lazy_static! { static ref PID_FILE: PathBuf = { let pid = std::process::id(); @@ -30,57 +26,25 @@ fn rgb8_3_to_rgb32(r: u8, g: u8, b: u8) -> u32 { ((r as u32) << 16) | ((g as u32) << 8) | (b as u32) } -fn create_temp_file() -> Result { - std::fs::File::create(&*PID_FILE)?; - Ok(PID_FILE.clone()) +pub fn marker_filepath() -> PathBuf { + PID_FILE.clone() } -fn launch_splash() -> Result<()> { - // Attach to console in windows so logs go somewhere - attach_console(); +fn marker_exists() -> bool { + std::fs::metadata(&*PID_FILE).is_ok() +} + +fn ensure_no_marker() { + let _ = std::fs::remove_file(marker_filepath()); +} - let logo = include_bytes!(concat!("../../", env!("CONSOLE_SPLASH_IMAGE"))); - let image = image::io::Reader::with_format( - std::io::BufReader::new(Cursor::new(logo)), - image::ImageFormat::Jpeg, - ) - .decode()?; - let u32_buffer: Vec = image +fn launch_splash(pos_x: isize, pos_y: isize) -> Result<()> { + let image = &crate::SPLASH_IMAGE; + let image_buffer: Vec = image .as_bytes() .chunks(3) .map(|v| rgb8_3_to_rgb32(v[0], v[1], v[2])) .collect(); - - let monitor = { - let init_pos = winit::dpi::Position::Logical(winit::dpi::LogicalPosition::new(0.0, 0.0)); - let init_size = winit::dpi::Size::Logical(winit::dpi::LogicalSize::new(1.0, 1.0)); - let event_loop = winit::event_loop::EventLoop::new(); - winit::window::WindowBuilder::new() - .with_visible(false) - .with_decorations(false) - .with_inner_size(init_size) - .with_position(init_pos) - .build(&event_loop)? - .current_monitor() - .or(event_loop.primary_monitor()) - .or(event_loop.available_monitors().take(1).next()) - }; - - let (pos_x, pos_y) = if let Some(monitor) = monitor { - let size = monitor.size(); - let (width, height) = if cfg!(target_os = "macos") { - let size = size.to_logical::(monitor.scale_factor()); - (size.width, size.height) - } else { - (size.width as f64, size.height as f64) - }; - let pos_x = ((width - image.width() as f64) / 2.0) as isize; - let pos_y = ((height - image.height() as f64) / 2.0) as isize; - (pos_x, pos_y) - } else { - (20, 20) - }; - let mut window = Window::new( "", image.width() as usize, @@ -92,23 +56,55 @@ fn launch_splash() -> Result<()> { ..WindowOptions::default() }, )?; - - let temp_filename = create_temp_file()?; let now = Instant::now(); - while window.is_open() && now.elapsed() < TIMEOUT_DURATION && temp_filename.exists() { - window.update_with_buffer(&u32_buffer, image.width() as usize, image.height() as usize)?; + while window.is_open() && now.elapsed() < TIMEOUT_DURATION && marker_exists() { + window.update_with_buffer( + &image_buffer, + image.width() as usize, + image.height() as usize, + )?; window.set_position(pos_x, pos_y); } - Ok(()) } -fn main() -> Result<()> { - let result = launch_splash(); - if let Err(ref err) = result { - eprint!("Error launching splash screen: {err}"); - } - // Try to remove the file, don't care about the result - let _result = std::fs::remove_file(&*PID_FILE); - result +fn splash_position() -> Result<(isize, isize)> { + let current_exe = std::env::current_exe()?; + let parent = current_exe.parent().ok_or("no parent directory")?; + let stdout = Command::new(parent.join("windowpos")).output()?.stdout; + let stdout = String::from_utf8_lossy(&stdout); + let xy: Vec<&str> = stdout.split_whitespace().collect(); + let (x, y) = (xy[0].parse::()?, xy[1].parse::()?); + Ok((x, y)) +} + +pub fn spawn() { + ensure_no_marker(); + std::thread::spawn(|| { + let now = Instant::now(); + while !marker_exists() { + std::thread::sleep(Duration::from_millis(100)); + if now.elapsed() > STARTUP_TIMEOUT_DURATION { + eprintln!("splash: marker never existed, exiting..."); + return; + } + } + eprintln!("splash: launching"); + let (pos_x, pos_y) = match splash_position() { + Ok((pos_x, pos_y)) => (pos_x, pos_y), + Err(err) => { + eprint!("splash: error launching: {err}"); + (20, 20) + } + }; + let result = launch_splash(pos_x, pos_y); + if let Err(ref err) = result { + eprint!("splash: error launching: {err}"); + } + // Try to remove the file, don't care about the result + let _result = std::fs::remove_file(&*PID_FILE); + if let Err(ref err) = result { + eprintln!("splash: error launching: {err}"); + } + }); } diff --git a/entrypoint/src/windowpos.rs b/entrypoint/src/windowpos.rs new file mode 100644 index 000000000..12aee8940 --- /dev/null +++ b/entrypoint/src/windowpos.rs @@ -0,0 +1,39 @@ +use entrypoint::Result; + +fn splash_position() -> Result<(isize, isize)> { + let monitor = { + let init_pos = winit::dpi::Position::Logical(winit::dpi::LogicalPosition::new(0.0, 0.0)); + let init_size = winit::dpi::Size::Logical(winit::dpi::LogicalSize::new(1.0, 1.0)); + let event_loop = winit::event_loop::EventLoop::new(); + winit::window::WindowBuilder::new() + .with_visible(false) + .with_decorations(false) + .with_inner_size(init_size) + .with_position(init_pos) + .build(&event_loop)? + .current_monitor() + .or(event_loop.primary_monitor()) + .or(event_loop.available_monitors().take(1).next()) + }; + let image = &entrypoint::SPLASH_IMAGE; + let (pos_x, pos_y) = if let Some(monitor) = monitor { + let size = monitor.size(); + let (width, height) = if cfg!(target_os = "macos") { + let size = size.to_logical::(monitor.scale_factor()); + (size.width, size.height) + } else { + (size.width as f64, size.height as f64) + }; + let pos_x = ((width - image.width() as f64) / 2.0) as isize; + let pos_y = ((height - image.height() as f64) / 2.0) as isize; + (pos_x, pos_y) + } else { + (20, 20) + }; + Ok((pos_x, pos_y)) +} + +fn main() { + let (pos_x, pos_y) = splash_position().unwrap(); + print!("{pos_x} {pos_y}"); +} diff --git a/resources/ConnectionScreen.qml b/resources/ConnectionScreen.qml index e8a2865f6..1b67a23fb 100644 --- a/resources/ConnectionScreen.qml +++ b/resources/ConnectionScreen.qml @@ -26,7 +26,6 @@ Item { property string connectingConstant: Constants.connection.connecting.toUpperCase() property string disconnectedConstant: Constants.connection.disconnected.toUpperCase() property string disconnectingConstant: Constants.connection.disconnecting.toUpperCase() - property bool appVisible: false function backend_request_broker_ready() { return (typeof (backend_request_broker) !== "undefined"); @@ -487,10 +486,6 @@ Item { if (!connectionData.available_baudrates.length) return ; - if (!appVisible && backend_request_broker_ready()) { - backend_request_broker.app_visible(); - appVisible = true; - } if (!available_baudrates.length || !available_flows.length) { Globals.consoleVersion = connectionData.console_version; available_baudrates = connectionData.available_baudrates; diff --git a/resources/console_resources.qrc b/resources/console_resources.qrc index b422da5ba..4cf148dc6 100644 --- a/resources/console_resources.qrc +++ b/resources/console_resources.qrc @@ -115,6 +115,7 @@ images/swift-nav/Solution.svg images/swift-nav/Tracking.svg images/swift-nav/Update.svg + images/splash-version.jpg TableComponents/SortableColumnHeading.qml TableComponents/TableCellDelegate.qml TableComponents/SwiftTableView.qml diff --git a/resources/images/fontawesome/power27.svg b/resources/images/fontawesome/power27.svg index 1b59d5db8..813d1a498 100755 --- a/resources/images/fontawesome/power27.svg +++ b/resources/images/fontawesome/power27.svg @@ -1,22 +1,22 @@ - - - - - - - - - + + + + + + + + + diff --git a/resources/images/iconic/fullscreen.svg b/resources/images/iconic/fullscreen.svg index 22a66907a..d711fb320 100644 --- a/resources/images/iconic/fullscreen.svg +++ b/resources/images/iconic/fullscreen.svg @@ -1,16 +1,16 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/resources/images/iconic/move.svg b/resources/images/iconic/move.svg index ae629fa48..a90a6e75f 100644 --- a/resources/images/iconic/move.svg +++ b/resources/images/iconic/move.svg @@ -1,16 +1,16 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/resources/images/iconic/pause.svg b/resources/images/iconic/pause.svg index 88fa7691d..def6dc700 100644 --- a/resources/images/iconic/pause.svg +++ b/resources/images/iconic/pause.svg @@ -1,14 +1,14 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/resources/images/iconic/play.svg b/resources/images/iconic/play.svg index adb25d880..83eedd3da 100644 --- a/resources/images/iconic/play.svg +++ b/resources/images/iconic/play.svg @@ -1,11 +1,11 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/resources/images/iconic/stop.svg b/resources/images/iconic/stop.svg index bd1beff39..0526b5525 100644 --- a/resources/images/iconic/stop.svg +++ b/resources/images/iconic/stop.svg @@ -1,11 +1,11 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/resources/images/iconic/target.svg b/resources/images/iconic/target.svg index ddb0cb3a2..ea6f7ec15 100644 --- a/resources/images/iconic/target.svg +++ b/resources/images/iconic/target.svg @@ -1,35 +1,35 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/iconic/x.svg b/resources/images/iconic/x.svg index c68a44d80..ecf0b20ef 100644 --- a/resources/images/iconic/x.svg +++ b/resources/images/iconic/x.svg @@ -1,14 +1,14 @@ - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/swiftnav_console/backend_request_broker.py b/swiftnav_console/backend_request_broker.py index b33ce5cea..64a53bd8c 100644 --- a/swiftnav_console/backend_request_broker.py +++ b/swiftnav_console/backend_request_broker.py @@ -1,5 +1,3 @@ -import os -import tempfile from typing import Any, List, Optional from PySide2.QtCore import QObject, Slot @@ -61,17 +59,6 @@ def disconnect(self) -> None: buffer = msg.to_bytes() self.endpoint.send_message(buffer) - @Slot() # type: ignore - def app_visible(self) -> None: # pylint: disable=no-self-use - pid = os.getenv("SWIFT_CONSOLE_PID") - if pid is not None: - - splash_filename = os.path.join(tempfile.gettempdir(), f"swiftnav_console.{pid}") - try: - os.remove(splash_filename) - except FileNotFoundError: - pass - @Slot() # type: ignore def serial_refresh(self) -> None: Message = self.messages.Message diff --git a/swiftnav_console/constants.py b/swiftnav_console/constants.py index d55d019f1..037a240c0 100644 --- a/swiftnav_console/constants.py +++ b/swiftnav_console/constants.py @@ -164,4 +164,4 @@ class QTKeys(str, Enum): class ApplicationMetadata(str, Enum): ORGANIZATION_NAME = "Swift Navigation" ORGANIZATION_DOMAIN = "swiftnav.com" - APPLICATION_NAME = "swift-toolbox" + APPLICATION_NAME = "Swift Console" diff --git a/swiftnav_console/main.py b/swiftnav_console/main.py index fbf9aa9d2..186e998bf 100644 --- a/swiftnav_console/main.py +++ b/swiftnav_console/main.py @@ -4,6 +4,7 @@ from datetime import datetime import os import pickle +import platform import sys import time @@ -11,14 +12,14 @@ import capnp # type: ignore -from PySide2.QtWidgets import QApplication # type: ignore +from PySide2.QtWidgets import QApplication, QSplashScreen # type: ignore from PySide2.QtCore import QObject, QUrl, QPointF, QThread, QTimer, Slot from PySide2.QtCharts import QtCharts # pylint: disable=unused-import from PySide2 import QtQml, QtCore -from PySide2.QtGui import QFontDatabase, QIcon +from PySide2.QtGui import QFontDatabase, QIcon, QPixmap from PySide2.QtQml import QQmlComponent, qmlRegisterType @@ -625,6 +626,46 @@ def handle_cli_arguments(args: argparse.Namespace, globals_: QObject): globals_.setProperty("showFileConnection", True) # type: ignore +def start_splash_linux(): + splash_filename = os.getenv("SWIFTNAV_CONSOLE_SPLASH") + if not splash_filename: + return + try: + with open(splash_filename, "wb"): + pass + except FileNotFoundError: + pass + + +def start_splash() -> Optional[QSplashScreen]: + if platform.system() == "Linux": + start_splash_linux() + return None + pixmap = QPixmap(":/images/splash-version.jpg") + splash = QSplashScreen(pixmap) + splash.show() + return splash + + +def stop_splash_linux(): + splash_filename = os.getenv("SWIFTNAV_CONSOLE_SPLASH") + if not splash_filename: + return + try: + os.remove(splash_filename) + time.sleep(0.200) + except FileNotFoundError: + pass + + +def stop_splash(splash: Optional[QSplashScreen]): + if platform.system() == "Linux": + stop_splash_linux() + else: + assert splash is not None + splash.close() + + def main(passed_args: Optional[Tuple[str, ...]] = None) -> int: parser = argparse.ArgumentParser(add_help=False, usage=argparse.SUPPRESS) parser.add_argument("--exit-after-timeout", type=int, default=None) @@ -711,6 +752,7 @@ def handle_qml_load_errors(obj, _url): if found_help_arg: return 0 + # Unfortunately it is not possible to access singletons directly using the PySide2 API. # This approach stores the globals somwhere that can be grabbed and manipulated. component = QQmlComponent(engine) @@ -722,6 +764,7 @@ def handle_qml_load_errors(obj, _url): globals_main = globals_main.property("globals") # type: ignore handle_cli_arguments(args_main, globals_main) + splash = start_splash() engine.addImportPath("PySide2") engine.addImportPath(":/") @@ -779,6 +822,7 @@ def handle_qml_load_errors(obj, _url): ) backend_msg_receiver.start() + stop_splash(splash) app.exec_() endpoint_main.shutdown()