hacksaw/src/main.rs

172 lines
4.6 KiB
Rust

extern crate xcb;
use xcb::shape;
fn main() {
let (conn, screen_num) = xcb::Connection::connect(None).unwrap();
let setup = conn.get_setup();
let screen = setup.roots().nth(screen_num as usize).unwrap();
let window = conn.generate_id();
// TODO event handling for expose/keypress
let values = [
// ?RGB. First 4 bytes appear to do nothing
(xcb::CW_BACK_PIXEL, 0x00_00_00_00),
(
xcb::CW_EVENT_MASK,
xcb::EVENT_MASK_EXPOSURE | xcb::EVENT_MASK_KEY_PRESS, // we'll need this later
),
(xcb::CW_OVERRIDE_REDIRECT, 1 as u32), // Don't be window managed
];
xcb::create_window(
&conn,
xcb::COPY_FROM_PARENT as u8,
window,
screen.root(),
0, // x
0, // y
0, // width
0, // height
0,
xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
screen.root_visual(),
&values,
);
xcb::map_window(&conn, window);
let title = "hacksaw";
// setting title
xcb::change_property(
&conn,
xcb::PROP_MODE_REPLACE as u8,
window,
xcb::ATOM_WM_NAME,
xcb::ATOM_STRING,
8,
title.as_bytes(),
);
let font = conn.generate_id();
xcb::open_font(&conn, font, "cursor");
// TODO: create cursor with a Pixmap
// https://stackoverflow.com/questions/40578969/how-to-create-a-cursor-in-x11-from-raw-data-c
let cursor = conn.generate_id();
xcb::create_glyph_cursor(&conn, cursor, font, font, 0, 30, 0, 0, 0, 0, 0, 0);
xcb::grab_pointer(
&conn,
true,
screen.root(),
(xcb::EVENT_MASK_BUTTON_RELEASE
| xcb::EVENT_MASK_BUTTON_PRESS
| xcb::EVENT_MASK_BUTTON_MOTION) as u16,
xcb::GRAB_MODE_ASYNC as u8,
xcb::GRAB_MODE_ASYNC as u8,
xcb::NONE,
cursor,
xcb::CURRENT_TIME,
).get_reply()
.unwrap();
conn.flush();
let temp_win = conn.generate_id();
xcb::create_window(
&conn,
xcb::COPY_FROM_PARENT as u8,
temp_win,
screen.root(),
0, // x
0, // y
200, // width
499, // height
0,
xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
screen.root_visual(),
&[(xcb::CW_BACK_PIXEL, screen.black_pixel())],
);
let rects = [xcb::Rectangle::new(300, 300, 400, 300)];
shape::rectangles(
&conn,
shape::SO_SET as u8,
shape::SK_BOUNDING as u8,
0,
temp_win,
0,
0,
&rects,
);
shape::combine(
&conn,
shape::SO_SET as u8,
shape::SK_BOUNDING as u8,
shape::SK_BOUNDING as u8,
window,
0,
0,
temp_win,
);
xcb::map_window(&conn, window);
conn.flush();
// TODO formalise the fact that motion comes after press?
let mut start_x = 0;
let mut start_y = 0;
let mut x = 0;
let mut y = 0;
loop {
let ev = conn.wait_for_event().unwrap();
match ev.response_type() {
xcb::BUTTON_PRESS => {
let button_press: &xcb::ButtonPressEvent = unsafe { xcb::cast_event(&ev) };
if button_press.detail() == 3 {
println!("Exiting due to right click");
return;
}
start_x = button_press.event_x();
start_y = button_press.event_y();
// For the case where there is no motion
x = start_x;
y = start_y;
}
xcb::KEY_PRESS => {
println!("Exiting due to key press");
return;
}
xcb::MOTION_NOTIFY => {
let motion: &xcb::MotionNotifyEvent = unsafe { xcb::cast_event(&ev) };
x = motion.event_x();
y = motion.event_y();
// TODO draw rectangles (and guides)
}
xcb::BUTTON_RELEASE => {
let motion: &xcb::ButtonReleaseEvent = unsafe { xcb::cast_event(&ev) };
match motion.detail() {
5 => continue, // Scroll wheel down
4 => continue, // Scroll wheel up
_ => break, // Move on after mouse released
}
}
xcb::EXPOSE => {
println!("expose!");
}
_ => continue,
};
}
// Now we have taken coordinates, we use them
let width = (x - start_x).abs();
let height = (y - start_y).abs();
println!("{}x{}+{}+{}", width, height, start_x, start_y);
}