Add build output dependency file for mouse-test application

Add standard menu fore keybinds
This commit is contained in:
2026-03-28 03:18:41 +01:00
parent 4440ad79bd
commit 3a9e292088
203 changed files with 3797 additions and 29 deletions

View 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()
}