From 9988028bf03f97c48869d45304941c7670c56e30 Mon Sep 17 00:00:00 2001 From: IllusionMan1212 Date: Mon, 23 Oct 2023 04:51:59 +0200 Subject: [PATCH] only allow one instance of the program and use IPC to load models from other instances --- Cargo.lock | 62 +++++++++++++++++++++++++++++++++++++++++++------- Cargo.toml | 2 ++ TODO.md | 9 ++++---- src/ipc.rs | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/main.rs | 44 +++++++++++++++++++++++++++--------- 6 files changed, 160 insertions(+), 23 deletions(-) create mode 100644 src/ipc.rs diff --git a/Cargo.lock b/Cargo.lock index 6fcd940..08ac4b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,7 +37,7 @@ dependencies = [ "log 0.4.20", "parking", "polling", - "rustix", + "rustix 0.37.25", "slab", "socket2", "waker-fn", @@ -70,6 +70,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bit_field" version = "0.10.2" @@ -82,6 +91,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + [[package]] name = "block" version = "0.1.6" @@ -316,6 +331,16 @@ dependencies = [ "spin", ] +[[package]] +name = "fs4" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29f9df8a11882c4e3335eb2d18a0137c505d9ca927470b0cac9c6f0ae07d28f7" +dependencies = [ + "rustix 0.38.20", + "windows-sys", +] + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -433,7 +458,7 @@ version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "919ebc7bdbf60a529689a5e58f80d9dcde27e3d2ab4f2f7ce711ec439fe789c5" dependencies = [ - "bitflags", + "bitflags 1.3.2", "glfw-sys", "objc", "raw-window-handle", @@ -551,7 +576,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "122d677d0efcd64ca15f12907beaf46b26bbd2cdc855ee5b227f29cf50f75bb5" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "imgui-sys", "mint", @@ -646,6 +671,12 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +[[package]] +name = "linux-raw-sys" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" + [[package]] name = "lock_api" version = "0.4.10" @@ -909,7 +940,7 @@ version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", @@ -923,7 +954,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", - "bitflags", + "bitflags 1.3.2", "cfg-if", "concurrent-queue", "libc", @@ -1049,7 +1080,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1108,11 +1139,24 @@ version = "0.37.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4eb579851244c2c03e7c24f501c3432bed80b8f720af1d6e5b0e0f01555a035" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.8", + "windows-sys", +] + +[[package]] +name = "rustix" +version = "0.38.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys 0.4.10", "windows-sys", ] @@ -1245,6 +1289,8 @@ name = "threedobs" version = "0.1.1" dependencies = [ "anyhow", + "bincode", + "fs4", "glad-gl", "glfw", "glm", diff --git a/Cargo.toml b/Cargo.toml index 17b613a..d2b8e95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,8 @@ glm = "0.2.3" mint = "0.5.9" anyhow = "1.0.71" rfd = "0.11.3" +bincode = "1.3.3" +fs4 = "0.7.0" [dependencies.imgui] version = "0.11.0" diff --git a/TODO.md b/TODO.md index e909697..9c02009 100644 --- a/TODO.md +++ b/TODO.md @@ -19,7 +19,7 @@ - [x] Smaller console area - [ ] Better object rotation - [ ] Ability to change rotation pivot point (needs raycasting and shit) - - [ ] There might be memory leaks somewhere (tracked it down to russimp, will have to write my own .obj and .stl readers) + - [x] There might be memory leaks somewhere (tracked it down to russimp, will have to write my own .obj and .stl readers) - [x] Reset object rotation when changing active model - [x] Drag'n'Drop models - [ ] Config for consistent settings between restarts (home dir preferred) @@ -27,9 +27,10 @@ - only select the last one if we removed the selected model - [x] Use filename for the root node - [x] Ship shaders directly in the executable - - [ ] Have the program accept model files as CLI args - - [ ] Have it so that when an instance of 3dobs is open, any other file associated with 3dobs is opened in the same instance - - [ ] Show how many memory each loaded model is using + - [x] Have the program accept model files as CLI args + - [x] Have it so that when an instance of 3dobs is open, any other file associated with 3dobs is opened in the same instance + - [ ] Toggle for dis/allowing the one instance option + - [x] Show how much memory each loaded model is using - [ ] Move the invisible point lights to be around selected object from all sides so it's nicely illuminated - [ ] Bring back the regular camera movement (can maybe be a switch between them) (for later) diff --git a/src/ipc.rs b/src/ipc.rs new file mode 100644 index 0000000..d38f5e5 --- /dev/null +++ b/src/ipc.rs @@ -0,0 +1,65 @@ +use std::{os::unix::net::{UnixListener, UnixStream}, path::PathBuf, io::{Write, Read}, fs::File, process::exit, thread, sync::mpsc::Receiver}; + +use fs4::FileExt; + +fn create_named_pipe(pipe_path: PathBuf) -> UnixListener { + if pipe_path.exists() { + // Remove the existing pipe if it exists. + std::fs::remove_file(&pipe_path).expect("Failed to remove existing pipe"); + } + + let listener = UnixListener::bind(&pipe_path).expect("Failed to create named pipe"); + + listener +} + +fn send_args_to_existing_instance(pipe_path: PathBuf, args_paths: Vec) { + let mut stream = UnixStream::connect(pipe_path).expect("Failed to connect to named pipe"); + let data = bincode::serialize(&args_paths).expect("Failed to serialize arguments"); + + // Send the arguments to the first instance. + stream + .write_all(&data) + .expect("Failed to send arguments"); +} + +pub fn init(lock_file: &File, args_paths: Vec) -> Receiver> { + let pipe_name = "3dobs_pipe"; + + let pipe_path = std::env::temp_dir().join(pipe_name); + + match lock_file.try_lock_exclusive() { + Ok(_) => {}, + Err(_) => { + println!("An instance of the program is already running"); + send_args_to_existing_instance(pipe_path, args_paths); + exit(0); + } + } + + let pipe = create_named_pipe(pipe_path); + let (ipc_tx, ipc_rx) = std::sync::mpsc::channel::>(); + + // thread is not joined because it blocks anyway + // and there's no cleanup to do or anything + let _ = thread::spawn(move || { + for stream in pipe.incoming() { + match stream { + Ok(mut stream) => { + let mut serialized_paths = Vec::new(); + let _ = stream.read_to_end(&mut serialized_paths); + + let paths: Vec = bincode::deserialize(&serialized_paths).unwrap(); + + ipc_tx.send(paths).unwrap(); + } + Err(err) => { + println!("Error: {:?}", err); + break; + } + } + } + }); + + ipc_rx +} diff --git a/src/lib.rs b/src/lib.rs index 589b838..152380d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,3 +11,4 @@ pub mod ui; pub mod log; pub mod aabb; pub mod importer; +pub mod ipc; diff --git a/src/main.rs b/src/main.rs index c44239c..2d76703 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,25 @@ use glfw::{Action, Context, Key, Modifiers}; use glad_gl::gl; use anyhow; +use std::fs::File; use std::env; use std::path::PathBuf; -use threedobs::{shader, ui::ui, utils}; + +use threedobs::{shader, ui::ui, utils, ipc}; fn main() -> anyhow::Result<(), Box> { + let args: Vec = env::args().collect(); + let args_paths: Vec = args + .iter() + .skip(1) + .map(|arg| std::fs::canonicalize(PathBuf::from(arg)).unwrap()) + .collect(); + + let lock_file_name = "3dobs.lock"; + let lock_file_path = std::env::temp_dir().join(lock_file_name); + let lock_file = File::create(&lock_file_path)?; + let ipc_rx = ipc::init(&lock_file, args_paths.clone()); + let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS)?; glfw::WindowHint::ContextVersion(3, 3); @@ -184,15 +198,8 @@ fn main() -> anyhow::Result<(), Box> { let scene_fb = create_scene_framebuffer(); - // Import models from launch arguments - let args: Vec = env::args().collect(); if args.len() > 1 { - let paths: Vec = args - .iter() - .skip(1) - .map(|arg| PathBuf::from(arg)) - .collect(); - utils::import_models_from_paths(&paths, &mut state); + utils::import_models_from_paths(&args_paths, &mut state); } // main loop @@ -209,6 +216,21 @@ fn main() -> anyhow::Result<(), Box> { let view_mat = glm::ext::look_at(state.camera.position, state.camera.position + state.camera.front, state.camera.up); let projection_mat = glm::ext::perspective(glm::radians(state.camera.fov), state.viewport_size[0] / state.viewport_size[1], 0.01, 200.0); + match ipc_rx.try_recv() { + Ok(paths) => { + window.focus(); + utils::import_models_from_paths(&paths, &mut state); + }, + Err(e) => { + match e { + std::sync::mpsc::TryRecvError::Empty => {}, + std::sync::mpsc::TryRecvError::Disconnected => { + panic!("Error: IPC thread channel disconnected"); + } + } + } + } + for (_, event) in glfw::flush_messages(&events) { // order of handling events is important here // we need to handle window events first to have an updated @@ -236,10 +258,10 @@ fn main() -> anyhow::Result<(), Box> { state.camera.move_camera(xoffset, yoffset); } else { if let Some(active_model) = state.active_model { - let model = state.objects.iter_mut().find(|m| m.id == active_model).unwrap(); - // let x_rotation = glm::quat_angle_axis(xoffset * state.camera.sensitivity, &state.camera.up); let x_rotation = xoffset * state.camera.sensitivity * state.rotation_speed; let y_rotation = yoffset * state.camera.sensitivity * state.rotation_speed; + let model = state.objects.iter_mut().find(|m| m.id == active_model).unwrap(); + // let x_rotation = glm::quat_angle_axis(xoffset * state.camera.sensitivity, &state.camera.up); model.rotate(x_rotation, y_rotation); } }