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 = MaybeUninit::uninit(); let mut window: MaybeUninit = MaybeUninit::uninit(); let mut text: MaybeUninit = 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 { unsafe { let display: *mut xlib::Display = xlib::XOpenDisplay(std::ptr::null()); let mut revert_to_ret: MaybeUninit = MaybeUninit::uninit(); let mut window: MaybeUninit = MaybeUninit::uninit(); let mut text: MaybeUninit = 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 = MaybeUninit::uninit(); let mut format: MaybeUninit = MaybeUninit::uninit(); let mut n_items: MaybeUninit = MaybeUninit::uninit(); let mut bytes_after: MaybeUninit = 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 { 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 { let now: DateTime = 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 = 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::::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 { 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 = 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(); }