Add build output dependency file for mouse-test application
Add standard menu fore keybinds
This commit is contained in:
262
testapps/mouse-test/src/main.rs
Normal file
262
testapps/mouse-test/src/main.rs
Normal file
@@ -0,0 +1,262 @@
|
||||
use crossterm::{
|
||||
cursor,
|
||||
event::{
|
||||
self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, MouseButton, MouseEventKind,
|
||||
},
|
||||
execute, queue,
|
||||
style::{Color, Print, ResetColor, SetBackgroundColor, SetForegroundColor},
|
||||
terminal::{
|
||||
self, disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen,
|
||||
LeaveAlternateScreen,
|
||||
},
|
||||
};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{self, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
// ─── Logging ─────────────────────────────────────────────────────────────────
|
||||
|
||||
fn log_path() -> PathBuf {
|
||||
let exe = std::env::current_exe().unwrap_or_else(|_| PathBuf::from("."));
|
||||
let dir = exe.parent().unwrap_or(std::path::Path::new(".")).to_path_buf();
|
||||
let filename = today_filename();
|
||||
dir.join(filename)
|
||||
}
|
||||
|
||||
fn today_filename() -> String {
|
||||
let secs = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.map(|d| d.as_secs())
|
||||
.unwrap_or(0);
|
||||
let (y, mo, d) = unix_to_ymd(secs);
|
||||
format!("mouse-test-{:04}-{:02}-{:02}.log", y, mo, d)
|
||||
}
|
||||
|
||||
fn timestamp() -> String {
|
||||
let dur = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap_or_default();
|
||||
let secs = dur.as_secs();
|
||||
let ms = dur.subsec_millis();
|
||||
let h = (secs % 86400) / 3600;
|
||||
let m = (secs % 3600) / 60;
|
||||
let s = secs % 60;
|
||||
format!("{:02}:{:02}:{:02}.{:03}", h, m, s, ms)
|
||||
}
|
||||
|
||||
fn unix_to_ymd(secs: u64) -> (u32, u32, u32) {
|
||||
let z = (secs / 86400) as i64 + 719468;
|
||||
let era = if z >= 0 { z } else { z - 146096 } / 146097;
|
||||
let doe = (z - era * 146097) as u32;
|
||||
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
||||
let y = yoe as i64 + era * 400;
|
||||
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
||||
let mp = (5 * doy + 2) / 153;
|
||||
let d = doy - (153 * mp + 2) / 5 + 1;
|
||||
let m = if mp < 10 { mp + 3 } else { mp - 9 };
|
||||
let y = if m <= 2 { y + 1 } else { y };
|
||||
(y as u32, m, d)
|
||||
}
|
||||
|
||||
fn write_log(log_file: &mut std::fs::File, msg: &str) {
|
||||
let line = format!("[{}] {}\n", timestamp(), msg);
|
||||
let _ = log_file.write_all(line.as_bytes());
|
||||
let _ = log_file.flush();
|
||||
}
|
||||
|
||||
// ─── Shapes ──────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Shape {
|
||||
col: u16,
|
||||
row: u16,
|
||||
color: ShapeColor,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum ShapeColor {
|
||||
Blue, // left click
|
||||
Red, // right click
|
||||
}
|
||||
|
||||
// ─── Main ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
// Öppna loggfil
|
||||
let path = log_path();
|
||||
let _ = fs::create_dir_all(path.parent().unwrap());
|
||||
let mut log_file = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&path)?;
|
||||
write_log(&mut log_file, &format!("=== mouse-test startad, logg: {:?} ===", path));
|
||||
|
||||
enable_raw_mode()?;
|
||||
let mut stdout = io::stdout();
|
||||
execute!(
|
||||
stdout,
|
||||
EnterAlternateScreen,
|
||||
EnableMouseCapture,
|
||||
cursor::Hide,
|
||||
)?;
|
||||
|
||||
// Aktivera utökad mus-tracking: button events + drag + any motion + SGR encoding
|
||||
// ?1002 = button-motion, ?1003 = any motion, ?1006 = SGR encoding
|
||||
execute!(stdout, Print("\x1b[?1003h\x1b[?1006h"))?;
|
||||
write_log(&mut log_file, "Aktiverade mus-tracking: ?1003h ?1006h");
|
||||
|
||||
let result = run(&mut stdout, &mut log_file);
|
||||
|
||||
// Stäng av allt
|
||||
execute!(stdout, Print("\x1b[?1003l\x1b[?1006l"))?;
|
||||
execute!(stdout, cursor::Show, LeaveAlternateScreen, DisableMouseCapture)?;
|
||||
disable_raw_mode()?;
|
||||
|
||||
write_log(&mut log_file, "=== mouse-test avslutad ===");
|
||||
result
|
||||
}
|
||||
|
||||
fn run(stdout: &mut io::Stdout, log_file: &mut std::fs::File) -> io::Result<()> {
|
||||
let mut shapes: Vec<Shape> = Vec::new();
|
||||
let mut log_lines: Vec<String> = Vec::new();
|
||||
|
||||
// Initialdragning
|
||||
redraw(stdout, &shapes, &log_lines)?;
|
||||
|
||||
loop {
|
||||
if event::poll(Duration::from_millis(50))? {
|
||||
let ev = event::read()?;
|
||||
match &ev {
|
||||
Event::Key(k) => {
|
||||
let msg = format!("KEY {:?} modifiers={:?}", k.code, k.modifiers);
|
||||
write_log(log_file, &msg);
|
||||
if k.code == KeyCode::Char('q') || k.code == KeyCode::Esc {
|
||||
break;
|
||||
}
|
||||
if k.code == KeyCode::Char('c') {
|
||||
shapes.clear();
|
||||
log_lines.push("Rensat alla shapes".to_string());
|
||||
cap_log(&mut log_lines);
|
||||
}
|
||||
}
|
||||
Event::Mouse(m) => {
|
||||
let col = m.column;
|
||||
let row = m.row;
|
||||
let kind = m.kind;
|
||||
|
||||
let msg = format!("MOUSE {:?} col={} row={} mods={:?}", kind, col, row, m.modifiers);
|
||||
write_log(log_file, &msg);
|
||||
log_lines.push(msg.clone());
|
||||
cap_log(&mut log_lines);
|
||||
|
||||
match kind {
|
||||
MouseEventKind::Down(MouseButton::Left) => {
|
||||
shapes.push(Shape { col, row, color: ShapeColor::Blue });
|
||||
}
|
||||
MouseEventKind::Down(MouseButton::Right) => {
|
||||
shapes.push(Shape { col, row, color: ShapeColor::Red });
|
||||
}
|
||||
MouseEventKind::ScrollUp => {
|
||||
for s in &mut shapes {
|
||||
s.row = s.row.saturating_sub(1);
|
||||
}
|
||||
}
|
||||
MouseEventKind::ScrollDown => {
|
||||
for s in &mut shapes {
|
||||
s.row = s.row.saturating_add(1);
|
||||
}
|
||||
}
|
||||
// Crossterm skickar horisontell scroll som ScrollLeft/ScrollRight
|
||||
MouseEventKind::ScrollLeft => {
|
||||
for s in &mut shapes {
|
||||
s.col = s.col.saturating_sub(1);
|
||||
}
|
||||
}
|
||||
MouseEventKind::ScrollRight => {
|
||||
for s in &mut shapes {
|
||||
s.col = s.col.saturating_add(1);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Event::Resize(w, h) => {
|
||||
let msg = format!("RESIZE {}x{}", w, h);
|
||||
write_log(log_file, &msg);
|
||||
log_lines.push(msg);
|
||||
cap_log(&mut log_lines);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
redraw(stdout, &shapes, &log_lines)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const MAX_LOG_LINES: usize = 20;
|
||||
|
||||
fn cap_log(lines: &mut Vec<String>) {
|
||||
while lines.len() > MAX_LOG_LINES {
|
||||
lines.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
fn redraw(stdout: &mut io::Stdout, shapes: &[Shape], log_lines: &[String]) -> io::Result<()> {
|
||||
let (cols, rows) = terminal::size()?;
|
||||
|
||||
queue!(stdout, cursor::MoveTo(0, 0), Clear(ClearType::All))?;
|
||||
|
||||
// Rubrik
|
||||
queue!(
|
||||
stdout,
|
||||
cursor::MoveTo(0, 0),
|
||||
SetForegroundColor(Color::Cyan),
|
||||
Print("mouse-test | vänsterklick=blå ruta högerklick=röd ruta scroll=flytta q=avsluta c=rensa"),
|
||||
ResetColor,
|
||||
)?;
|
||||
|
||||
// Separator
|
||||
let sep: String = "─".repeat(cols as usize);
|
||||
queue!(stdout, cursor::MoveTo(0, 1), SetForegroundColor(Color::DarkGrey), Print(&sep), ResetColor)?;
|
||||
|
||||
// Rita shapes (2x4 tecken block)
|
||||
for s in shapes {
|
||||
let bg = match s.color {
|
||||
ShapeColor::Blue => Color::Blue,
|
||||
ShapeColor::Red => Color::Red,
|
||||
};
|
||||
// Gör ett 2-radshögt, 4-kolumnsbredt block
|
||||
for dr in 0u16..2 {
|
||||
let r = s.row.saturating_add(dr);
|
||||
if r < 2 || r >= rows { continue; }
|
||||
queue!(
|
||||
stdout,
|
||||
cursor::MoveTo(s.col, r),
|
||||
SetBackgroundColor(bg),
|
||||
Print(" "),
|
||||
ResetColor,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Logg-panel längst ner
|
||||
let log_start_row = rows.saturating_sub(MAX_LOG_LINES as u16 + 1);
|
||||
queue!(stdout, cursor::MoveTo(0, log_start_row), SetForegroundColor(Color::DarkGrey), Print(&sep), ResetColor)?;
|
||||
for (i, line) in log_lines.iter().enumerate() {
|
||||
let r = log_start_row + 1 + i as u16;
|
||||
if r >= rows { break; }
|
||||
// Trunkera om för lång
|
||||
let display: &str = if line.len() > cols as usize { &line[..cols as usize] } else { line };
|
||||
queue!(
|
||||
stdout,
|
||||
cursor::MoveTo(0, r),
|
||||
SetForegroundColor(Color::Grey),
|
||||
Print(display),
|
||||
ResetColor,
|
||||
)?;
|
||||
}
|
||||
|
||||
stdout.flush()
|
||||
}
|
||||
Reference in New Issue
Block a user