Don't allow 2 processes of leanshot to run at the same time, using a
lockfile.
This commit is contained in:
parent
b75eeae98c
commit
b245c362c4
5 changed files with 87 additions and 4 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -172,7 +172,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "leanshot"
|
name = "leanshot"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"image",
|
"image",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "leanshot"
|
name = "leanshot"
|
||||||
description = "Screenshot capture for Linux."
|
description = "Screenshot capture for Linux."
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
repository = "https://git.mzhang.io/michael/leanshot"
|
repository = "https://git.mzhang.io/michael/leanshot"
|
||||||
license-file = "LICENSE"
|
license-file = "LICENSE"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
|
@ -223,7 +223,12 @@ impl Gui {
|
||||||
match evt.response_type() {
|
match evt.response_type() {
|
||||||
xcb::KEY_RELEASE => {
|
xcb::KEY_RELEASE => {
|
||||||
let evt = unsafe { xcb::cast_event::<xcb::KeyReleaseEvent>(&evt) };
|
let evt = unsafe { xcb::cast_event::<xcb::KeyReleaseEvent>(&evt) };
|
||||||
trace!("key released: root={} key={} state={}", evt.root(), evt.detail(), evt.state());
|
trace!(
|
||||||
|
"key released: root={} key={} state={}",
|
||||||
|
evt.root(),
|
||||||
|
evt.detail(),
|
||||||
|
evt.state()
|
||||||
|
);
|
||||||
|
|
||||||
if evt.detail() == 9 {
|
if evt.detail() == 9 {
|
||||||
if state.dragging {
|
if state.dragging {
|
||||||
|
@ -255,7 +260,7 @@ impl Gui {
|
||||||
|
|
||||||
// left mouse button
|
// left mouse button
|
||||||
if state.dragging && evt.detail() == 1 {
|
if state.dragging && evt.detail() == 1 {
|
||||||
state.dragging = false;
|
state.dragging = false;
|
||||||
if let Some(_) = state.rect() {
|
if let Some(_) = state.rect() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ extern crate log;
|
||||||
extern crate anyhow;
|
extern crate anyhow;
|
||||||
|
|
||||||
mod gui;
|
mod gui;
|
||||||
|
mod singleton;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
@ -31,6 +32,10 @@ fn main() -> Result<()> {
|
||||||
capture.save_to(&opt.outfile)?;
|
capture.save_to(&opt.outfile)?;
|
||||||
}
|
}
|
||||||
Region::Selection => {
|
Region::Selection => {
|
||||||
|
info!("Creating lockfile...");
|
||||||
|
let _lockfile = singleton::check_singleton()?;
|
||||||
|
info!("Lockfile: {:?}", _lockfile);
|
||||||
|
|
||||||
if let Some(rectangle) = gui.interactive_select(&capture)? {
|
if let Some(rectangle) = gui.interactive_select(&capture)? {
|
||||||
capture.save_cropped_to(&opt.outfile, Some(rectangle))?;
|
capture.save_cropped_to(&opt.outfile, Some(rectangle))?;
|
||||||
} else {
|
} else {
|
||||||
|
|
73
src/singleton.rs
Normal file
73
src/singleton.rs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
use std::fs::{File, self};
|
||||||
|
use std::io::{Write, Read};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::time::{SystemTime, Duration, UNIX_EPOCH};
|
||||||
|
use std::process;
|
||||||
|
|
||||||
|
use anyhow::{Error, Context, Result};
|
||||||
|
|
||||||
|
const PATHS: &[&str] = &["/var/run", "/tmp"];
|
||||||
|
|
||||||
|
pub fn check_singleton() -> Result<Lockfile> {
|
||||||
|
let (mut file, path) = get_lock_file()?;
|
||||||
|
|
||||||
|
let current_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis();
|
||||||
|
let pid = process::id();
|
||||||
|
let contents = format!("{}:{}", current_time, pid).as_bytes().to_vec();
|
||||||
|
file.write_all(&contents)?;
|
||||||
|
|
||||||
|
Ok(Lockfile(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Lockfile(PathBuf);
|
||||||
|
|
||||||
|
impl Drop for Lockfile {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
info!("Dropping lockfile...");
|
||||||
|
fs::remove_file(&self.0).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_lock_file() -> Result<(File, PathBuf)> {
|
||||||
|
for dir_path in PATHS.iter() {
|
||||||
|
use std::io::ErrorKind::*;
|
||||||
|
let dir_path = PathBuf::from(dir_path);
|
||||||
|
let lockfile_path = dir_path.join("leanshot.pid");
|
||||||
|
|
||||||
|
// check if it already exists
|
||||||
|
match File::open(&lockfile_path) {
|
||||||
|
Ok(mut f) => {
|
||||||
|
let mut contents = String::new();
|
||||||
|
f.read_to_string(&mut contents)?;
|
||||||
|
|
||||||
|
let mut parts = contents.split(":");
|
||||||
|
let timestamp = parts.next().unwrap().parse::<u64>()?;
|
||||||
|
let pid = parts.next().unwrap();
|
||||||
|
let time = UNIX_EPOCH + Duration::from_millis(timestamp);
|
||||||
|
|
||||||
|
// if it hasn't been 5 minutes since the last open, then bail
|
||||||
|
if SystemTime::now() - Duration::from_secs(60 * 5) < time {
|
||||||
|
bail!("existing lockfile found for pid {} at time {:?}", pid, time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ignore errors
|
||||||
|
Err(e) => match e.kind() {
|
||||||
|
NotFound => {},
|
||||||
|
_ => return Err(Error::from(e).context("could not open lockfile for reading")),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let file = match File::create(&lockfile_path) {
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(e) => match e.kind() {
|
||||||
|
PermissionDenied => continue,
|
||||||
|
_ => return Err(Error::from(e).context("could not open lockfile for writing")),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok((file, lockfile_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
bail!("could not find a suitable place to place lockfile");
|
||||||
|
}
|
Loading…
Reference in a new issue