269 lines
8.6 KiB
Rust
269 lines
8.6 KiB
Rust
use dmenu_facade::*;
|
|
use x11::xlib;
|
|
use std::mem::MaybeUninit;
|
|
use std::os::raw::{c_int,c_ulong,c_uchar};
|
|
use image::{RgbaImage};
|
|
use std::convert::TryInto;
|
|
use std::process::{Stdio,Command};
|
|
use std::ffi::{CStr,CString};
|
|
use chrono::{DateTime, Local, SecondsFormat};
|
|
|
|
#[derive(Debug)]
|
|
struct Position {
|
|
origin_x: i32,
|
|
origin_y: i32,
|
|
height: u32,
|
|
width: u32,
|
|
}
|
|
|
|
fn get_focus_win_name() -> String {
|
|
unsafe {
|
|
let display: *mut xlib::Display = xlib::XOpenDisplay(std::ptr::null());
|
|
|
|
let mut revert_to_ret: MaybeUninit<c_int> = MaybeUninit::uninit();
|
|
let mut window: MaybeUninit<c_ulong> = MaybeUninit::uninit();
|
|
let mut text: MaybeUninit<xlib::XTextProperty> = MaybeUninit::uninit();
|
|
|
|
xlib::XGetInputFocus(display, window.as_mut_ptr(), revert_to_ret.as_mut_ptr());
|
|
let mut window = window.assume_init();
|
|
|
|
xlib::XGetWMName(display, window, text.as_mut_ptr());
|
|
|
|
let mut text = text.assume_init();
|
|
let c_str = CStr::from_ptr(text.value as *mut i8);
|
|
let str_slice = c_str.to_str().unwrap();
|
|
|
|
xlib::XCloseDisplay(display);
|
|
|
|
str_slice.to_owned()
|
|
}
|
|
}
|
|
|
|
fn get_wm_win_pid() -> Option<u64> {
|
|
|
|
unsafe {
|
|
let display: *mut xlib::Display = xlib::XOpenDisplay(std::ptr::null());
|
|
|
|
let mut revert_to_ret: MaybeUninit<c_int> = MaybeUninit::uninit();
|
|
let mut window: MaybeUninit<c_ulong> = MaybeUninit::uninit();
|
|
let mut text: MaybeUninit<xlib::XTextProperty> = MaybeUninit::uninit();
|
|
|
|
xlib::XGetInputFocus(display, window.as_mut_ptr(), revert_to_ret.as_mut_ptr());
|
|
let mut window = window.assume_init();
|
|
|
|
let pid_str = CString::new("_NET_WM_PID").unwrap();
|
|
let atom = xlib::XInternAtom(display, pid_str.as_ptr() as *const i8, 0);
|
|
|
|
let mut retrieved_atom: MaybeUninit<c_ulong> = MaybeUninit::uninit();
|
|
let mut format: MaybeUninit<c_int> = MaybeUninit::uninit();
|
|
let mut n_items: MaybeUninit<c_ulong> = MaybeUninit::uninit();
|
|
let mut bytes_after: MaybeUninit<c_ulong> = MaybeUninit::uninit();
|
|
let mut ptr: *mut c_uchar = 0 as *mut c_uchar;
|
|
|
|
if xlib::XGetWindowProperty(display, window, atom, 0, 1, 0, xlib::XA_CARDINAL,
|
|
retrieved_atom.as_mut_ptr(),
|
|
format.as_mut_ptr(),
|
|
n_items.as_mut_ptr(),
|
|
bytes_after.as_mut_ptr(),
|
|
&mut ptr) == 0 {
|
|
let t = ptr as *mut c_ulong;
|
|
Some(*t)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_process_name() -> Option<String> {
|
|
let pid = get_wm_win_pid()?;
|
|
let p = procfs::process::Process::new(pid as i32).unwrap();
|
|
Some(String::from(p.exe().unwrap().file_name()?.to_str()?))
|
|
}
|
|
|
|
fn get_name_proposals() -> Vec<String> {
|
|
let now: DateTime<Local> = Local::now();
|
|
let formatted_now = now.format("%F %T");
|
|
let mut window_name = get_focus_win_name();
|
|
let process_name = get_process_name().unwrap();
|
|
window_name.truncate(10);
|
|
|
|
vec!(format!("{}.png", formatted_now),
|
|
format!("{} ({}).png", formatted_now, window_name),
|
|
format!("{} ({}).png", formatted_now, process_name))
|
|
}
|
|
|
|
fn screenshot(pos: &Position) {
|
|
let len: usize = (pos.height as usize)*(pos.width as usize)*4;
|
|
|
|
let mut data: Vec<u8> = unsafe {
|
|
let display: *mut xlib::Display = xlib::XOpenDisplay(std::ptr::null());
|
|
let root_window = xlib::XDefaultRootWindow(display);
|
|
|
|
let ximg = xlib::XGetImage(display,
|
|
root_window,
|
|
pos.origin_x,
|
|
pos.origin_y,
|
|
pos.width,
|
|
pos.height,
|
|
xlib::XAllPlanes(),
|
|
xlib::ZPixmap);
|
|
|
|
Vec::<u8>::from_raw_parts((*ximg).data as *mut _, len, len)
|
|
};
|
|
|
|
let mut c = 0;
|
|
|
|
while c < len {
|
|
// for some reason, instead of storing RGBA data, x11 store BGRA data,
|
|
// we need to fix that before passing that to image lib
|
|
data.swap(c,c+2);
|
|
c+=4;
|
|
}
|
|
|
|
let img = RgbaImage::from_raw(pos.width, pos.height, data).unwrap();
|
|
img.save("empty.png").unwrap();
|
|
}
|
|
|
|
// https://bbs.archlinux.org/viewtopic.php?id=85378
|
|
fn select_zone() -> Option<Position> {
|
|
|
|
unsafe {
|
|
let display: *mut xlib::Display = xlib::XOpenDisplay(std::ptr::null());
|
|
let root_window = xlib::XDefaultRootWindow(display);
|
|
|
|
let c = xlib::XCreateFontCursor(display, 130); // xlib::XC_tcross = 130
|
|
|
|
let button_mask = xlib::ButtonMotionMask as u32 | xlib::ButtonPressMask as u32 | xlib::ButtonReleaseMask as u32;
|
|
|
|
if xlib::XGrabPointer(display, root_window, 0, button_mask, xlib::GrabModeAsync, xlib::GrabModeAsync, root_window, c, xlib::CurrentTime) != xlib::GrabSuccess {
|
|
panic!("could not grab");
|
|
}
|
|
|
|
if xlib::XGrabKeyboard(display, root_window, 0, xlib::GrabModeAsync, xlib::GrabModeAsync, xlib::CurrentTime) != xlib::GrabSuccess {
|
|
panic!("could not grab");
|
|
}
|
|
|
|
let mut pos = Position { origin_x: 0, origin_y: 0, width: 0, height: 0};
|
|
|
|
let mut gcv = xlib::XGCValues {
|
|
foreground: (255<<16),
|
|
subwindow_mode: xlib::IncludeInferiors,
|
|
plane_mask: 0,
|
|
function: xlib::GXxor,
|
|
background: 0,
|
|
line_width: 2,
|
|
line_style: 0,
|
|
cap_style: 0,
|
|
join_style: 0,
|
|
fill_style: 0,
|
|
fill_rule: 0,
|
|
arc_mode: 0,
|
|
tile: 0,
|
|
stipple: 0,
|
|
ts_x_origin: 0,
|
|
ts_y_origin: 0,
|
|
font: 0,
|
|
graphics_exposures: 0,
|
|
clip_x_origin: 0,
|
|
clip_y_origin: 0,
|
|
clip_mask: 0,
|
|
dash_offset: 0,
|
|
dashes: 0,
|
|
};
|
|
|
|
let gcv_mask = xlib::GCSubwindowMode | xlib::GCLineWidth | xlib::GCForeground | xlib::GCFunction;
|
|
|
|
let mut old_coord = (0,0);
|
|
let gc = xlib::XCreateGC(display, root_window, gcv_mask.into(), &mut gcv);
|
|
|
|
// https://stackoverflow.com/questions/38996916/x11-draw-overlay-remove-drawings-from-overlay
|
|
|
|
loop {
|
|
while xlib::XPending(display) > 0 {
|
|
let mut e: MaybeUninit<xlib::XEvent> = MaybeUninit::uninit();
|
|
|
|
xlib::XNextEvent(display, e.as_mut_ptr());
|
|
let event = e.assume_init();
|
|
|
|
match event.type_ {
|
|
xlib::MotionNotify => {
|
|
let event = xlib::XMotionEvent::from(event);
|
|
|
|
let width = event.x_root - pos.origin_x;
|
|
let height = event.y_root - pos.origin_y;
|
|
|
|
if height < 0 || width < 0 {
|
|
return None;
|
|
}
|
|
|
|
xlib::XDrawRectangle(display, root_window, gc, pos.origin_x, pos.origin_y, old_coord.0, old_coord.1);
|
|
xlib::XDrawRectangle(display, root_window, gc, pos.origin_x, pos.origin_y, width.try_into().unwrap(), height.try_into().unwrap());
|
|
|
|
old_coord = (width.try_into().unwrap(), height.try_into().unwrap());
|
|
},
|
|
xlib::KeyPress => {
|
|
let event = xlib::XKeyEvent::from(event);
|
|
|
|
if event.keycode == 9 { // escape keycode (xev)
|
|
return None;
|
|
}
|
|
},
|
|
xlib::ButtonPress => {
|
|
let event = xlib::XButtonEvent::from(event);
|
|
//println!("origin = ({},{})", event.x, event.y);
|
|
|
|
pos.origin_x = event.x_root.try_into().unwrap();
|
|
pos.origin_y = event.y_root.try_into().unwrap();
|
|
},
|
|
xlib::ButtonRelease => {
|
|
// assuming that the upper left corner is the origin
|
|
let event = xlib::XButtonEvent::from(event);
|
|
|
|
let x: u32 = event.x_root.try_into().unwrap();
|
|
let y: u32 = event.y_root.try_into().unwrap();
|
|
|
|
pos.width = x - pos.origin_x as u32;
|
|
pos.height = y - pos.origin_y as u32;
|
|
|
|
if pos.height == 0 || pos.width == 0 {
|
|
return None
|
|
}
|
|
|
|
xlib::XDrawRectangle(display, root_window, gc, pos.origin_x, pos.origin_y, pos.width, pos.height);
|
|
xlib::XFlush(display);
|
|
xlib::XCloseDisplay(display);
|
|
return Some(pos);
|
|
}
|
|
_ => { println!("type unknown: {}", event.type_); }
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
fn show_interactive_menu() {
|
|
let items = vec!["test", "test2"];
|
|
let chosen = DMenu::default()
|
|
.execute(&items);
|
|
//Prints selected item to stdout
|
|
if let Ok(item) = chosen {
|
|
println!("{}", item);
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
/*let a = get_focus_win_name();
|
|
println!("{:?}", get_name_proposals());
|
|
println!("{:?}", get_wm_win_pid());
|
|
println!("{:?}", get_process_name());*/
|
|
//let s = select_zone();
|
|
//println!("{:?}", s);
|
|
//if let Some(select) = s {
|
|
// screenshot(&select);
|
|
//}
|
|
show_interactive_menu();
|
|
}
|