mirror of
https://github.com/IllusionMan1212/3dobs.git
synced 2026-10-24 10:12:35 +00:00
only allow one instance of the program and use IPC to load models from other instances
This commit is contained in:
+65
@@ -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<PathBuf>) {
|
||||
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<PathBuf>) -> Receiver<Vec<PathBuf>> {
|
||||
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::<Vec<PathBuf>>();
|
||||
|
||||
// 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<PathBuf> = bincode::deserialize(&serialized_paths).unwrap();
|
||||
|
||||
ipc_tx.send(paths).unwrap();
|
||||
}
|
||||
Err(err) => {
|
||||
println!("Error: {:?}", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ipc_rx
|
||||
}
|
||||
@@ -11,3 +11,4 @@ pub mod ui;
|
||||
pub mod log;
|
||||
pub mod aabb;
|
||||
pub mod importer;
|
||||
pub mod ipc;
|
||||
|
||||
+33
-11
@@ -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<dyn std::error::Error>> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let args_paths: Vec<PathBuf> = 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<dyn std::error::Error>> {
|
||||
|
||||
let scene_fb = create_scene_framebuffer();
|
||||
|
||||
// Import models from launch arguments
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() > 1 {
|
||||
let paths: Vec<PathBuf> = 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<dyn std::error::Error>> {
|
||||
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<dyn std::error::Error>> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user