From 943cfb03422cffc97945a2137cfd157e8f0f80f7 Mon Sep 17 00:00:00 2001 From: lynnux Date: Wed, 16 Aug 2023 15:29:56 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0neovide=E7=9A=84=E5=85=89?= =?UTF-8?q?=E6=A0=87=E5=85=88=E5=8A=A8=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/beacon.rs | 395 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 380 insertions(+), 15 deletions(-) diff --git a/src/beacon.rs b/src/beacon.rs index 7419829..2b8774f 100644 --- a/src/beacon.rs +++ b/src/beacon.rs @@ -1,4 +1,5 @@ -use crate::to_wstring; +#![allow(non_upper_case_globals)] +use crate::to_wstring; use emacs::{defun, Result}; use winapi::ctypes::*; use winapi::shared::basetsd::*; @@ -34,10 +35,30 @@ const TIMER_BEACON_DELAY: UINT_PTR = 2; // cursor animation,参考https://github.com/manateelazycat/holo-layer/blob/master/plugin/cursor_animation.py const WM_SHOW_ANIMATION: UINT = WM_USER + 0x0003; static mut ANIMATION_WND: HWND = std::ptr::null_mut(); -static mut ANIMATION_ARG_X: usize = 0; -static mut ANIMATION_ARG_Y: usize = 0; static mut ANIMATION_ARG_DURATION: usize = 0; -// static mut BEACON_DURATION_LEFT_COUNT: isize = 0; // 百分比 +static mut ANIMATION_ARG_DURATION_STEP: usize = 100; + +const TIMER_ANIMATION_DURATION: UINT_PTR = 3; +static mut ANIMATION_DURATION_LEFT_COUNT: isize = 0; // 百分比 +static mut cursor_color: HBRUSH = std::ptr::null_mut(); +static mut cursor_color_r: u8 = 255; +static mut cursor_color_g: u8 = 255; +static mut cursor_color_b: u8 = 255; +static mut cursor_info_x: usize = 0; +static mut cursor_info_y: usize = 0; +static mut cursor_info_w: usize = 0; +static mut cursor_info_h: usize = 0; + +static mut cursor_info_prev_x: usize = 0; +static mut cursor_info_prev_y: usize = 0; +static mut cursor_info_prev_w: usize = 0; +static mut cursor_info_prev_h: usize = 0; +static mut cursor_animation_percent: f32 = 1.0; + +static mut cursor_start_x: usize = 0; +static mut cursor_start_y: usize = 0; +static mut cursor_end_x: usize = 0; +static mut cursor_end_y: usize = 0; pub unsafe extern "system" fn window_proc( hwnd: HWND, @@ -52,10 +73,10 @@ pub unsafe extern "system" fn window_proc( return DefWindowProcW(hwnd, msg, wparam, lparam); } -fn on_timer(left: isize) { +fn on_beacon_timer(left: isize) { unsafe { let left = (left as f64 / (BEACON_ARG_DURATION as f64 / TIMER_BEACON_DURATION_STEP as f64) - * BEACON_WIDTH as f64) as i32; + * BEACON_WIDTH as f64) as i32; #[cfg(debug_assertions)] { let right = BEACON_WIDTH as i32 - left; @@ -104,7 +125,8 @@ fn on_timer(left: isize) { } } } -fn show_wnd() { + +fn show_beacon_wnd() { unsafe { if BEACON_WND.is_null() { return; @@ -115,8 +137,14 @@ fn show_wnd() { } ShowWindow(BEACON_WND, SW_SHOW); - BEACON_DURATION_LEFT_COUNT = (BEACON_ARG_DURATION as f64 / TIMER_BEACON_DURATION_STEP as f64) as isize; - SetTimer(BEACON_WND, TIMER_BEACON_DURATION, TIMER_BEACON_DURATION_STEP as u32, None); + BEACON_DURATION_LEFT_COUNT = + (BEACON_ARG_DURATION as f64 / TIMER_BEACON_DURATION_STEP as f64) as isize; + SetTimer( + BEACON_WND, + TIMER_BEACON_DURATION, + TIMER_BEACON_DURATION_STEP as u32, + None, + ); // SetForegroundWindow(BEACON_WND); // 这个会发生焦点切换 SetWindowPos( BEACON_WND, @@ -127,11 +155,11 @@ fn show_wnd() { BEACON_HEIGHT as c_int, SWP_SHOWWINDOW, ); - on_timer(BEACON_DURATION_LEFT_COUNT); + on_beacon_timer(BEACON_DURATION_LEFT_COUNT); } } -fn create_wnd() { +fn create_beacon_wnd() { unsafe { if !BEACON_WND.is_null() { return; @@ -189,12 +217,282 @@ fn create_wnd() { RGB(0, 0, 0), 200, // LWA_COLORKEY这个就没效果,是全透明的 LWA_COLORKEY, // - // LWA_ALPHA, + // LWA_ALPHA, ); BEACON_WND = hwnd; } } +fn create_animation_wnd() { + unsafe { + if !ANIMATION_WND.is_null() { + return; + } + let cls_name = to_wstring("rust_animation_window_class"); + let wc = WNDCLASSEXW { + cbSize: std::mem::size_of::() as UINT, + style: CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, + lpfnWndProc: Some(window_proc), + cbClsExtra: 0, + cbWndExtra: 0, + hInstance: GetModuleHandleW(std::ptr::null_mut()) as HINSTANCE, + hIcon: std::ptr::null_mut(), + hCursor: LoadCursorW(std::ptr::null_mut(), IDC_ARROW), + hbrBackground: GetStockObject(BLACK_BRUSH as i32) as HBRUSH, + lpszMenuName: std::ptr::null_mut(), + lpszClassName: cls_name.as_ptr(), + hIconSm: std::ptr::null_mut(), + }; + if RegisterClassExW(&wc) == 0 { + #[cfg(debug_assertions)] + { + OutputDebugStringA(b"RegisterClassEx failed\0".as_ptr() as *const _); + } + } + + let wnd_name = to_wstring("Rust Window2"); + // 透明要带WS_EX_LAYERED + let hwnd = CreateWindowExW( + WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_NOACTIVATE | WS_EX_LAYERED, // 任务栏无标题,显示时无焦点切换 + wc.lpszClassName, + wnd_name.as_ptr(), + WS_POPUP, // 调试用| WS_BORDER + 0, + 0, + BEACON_WIDTH as i32, + BEACON_HEIGHT as i32, + std::ptr::null_mut(), + std::ptr::null_mut(), + wc.hInstance, + std::ptr::null_mut(), + ); + if hwnd == std::ptr::null_mut() { + #[cfg(debug_assertions)] + { + let err = GetLastError(); + let msg = format!("err:{}", err); + OutputDebugStringW(to_wstring(&msg).as_slice().as_ptr()); + } + return; + } + + // 指定透明色 + SetLayeredWindowAttributes( + hwnd, + RGB(0, 0, 0), + 200, // LWA_COLORKEY这个就没效果,是全透明的 + LWA_COLORKEY, // + // LWA_ALPHA, + ); + ANIMATION_WND = hwnd; + + // 直接覆盖整个屏幕 + let width = GetSystemMetrics(SM_CXSCREEN); + let height = GetSystemMetrics(SM_CYSCREEN); + SetWindowPos( + ANIMATION_WND, + HWND_TOPMOST, + 0, + 0, + width, + height, + SWP_SHOWWINDOW, + ); + } +} +fn show_animation_wnd() { + unsafe { + if ANIMATION_WND.is_null() { + return; + } + #[cfg(debug_assertions)] + { + OutputDebugStringA(b"WM_SHOW_ANIMATION!\0" as *const _ as *const _); + } + ShowWindow(ANIMATION_WND, SW_SHOW); + + ANIMATION_DURATION_LEFT_COUNT = + (ANIMATION_ARG_DURATION as f64 / ANIMATION_ARG_DURATION_STEP as f64) as isize; + SetTimer( + ANIMATION_WND, + TIMER_ANIMATION_DURATION, + TIMER_BEACON_DURATION_STEP as u32, + None, + ); + // TODO: 目前仅支持一次性设置 + if cursor_color.is_null() { + cursor_color = CreateSolidBrush(RGB(cursor_color_r, cursor_color_g, cursor_color_b)); + } + // SetForegroundWindow(ANIMATION_WND); // 这个会发生焦点切换 + on_animation_timer(ANIMATION_DURATION_LEFT_COUNT); + } +} + +// from https://docs.rs/qttypes/0.2.9/src/qttypes/lib.rs.html#923 +#[repr(C)] +#[derive(Default, Clone, Copy, PartialEq, Debug)] +pub struct QPointF { + pub x: f32, + pub y: f32, +} +impl QPointF { + fn new(x: f32, y: f32) -> QPointF { + QPointF { x, y } + } +} +impl std::ops::Add for QPointF { + type Output = QPointF; + fn add(self, other: QPointF) -> QPointF { + QPointF { + x: self.x + other.x, + y: self.y + other.y, + } + } +} +impl std::ops::Sub for QPointF { + type Output = QPointF; + fn sub(self, other: QPointF) -> QPointF { + QPointF { + x: self.x - other.x, + y: self.y - other.y, + } + } +} +impl std::ops::Mul for QPointF { + type Output = QPointF; + fn mul(self, other: f32) -> Self::Output { + QPointF { + x: self.x * other, + y: self.y * other, + } + } +} + +fn cursor_animation_draw_jelly_cursor( + cs: QPointF, + ce: QPointF, + w: f32, + h: f32, + p: f32, + points: &mut [QPointF; 4], +) { + let diff = cs - ce; + let diff_x = diff.x; + let diff_y = diff.y; + let w_point = QPointF::new(w, 0.0); + let h_point = QPointF::new(0.0, h); + let wh_point = QPointF::new(w, h); + if diff_x * diff_y > 0.0 { + points[0] = cs; + points[1] = cs + wh_point; + points[2] = ce + wh_point; + points[3] = ce; + } else if diff_x * diff_y < 0.0 { + points[0] = cs + h_point; + points[1] = cs + w_point; + points[2] = ce + w_point; + points[3] = ce + h_point; + } else if diff_x == 0.0 { + if diff_y >= 0.0 { + points[0] = cs + h_point; + points[1] = cs + wh_point; + points[2] = ce + w_point; + points[3] = ce; + } else { + points[0] = cs; + points[1] = cs + w_point; + points[2] = ce + wh_point; + points[3] = ce + h_point; + } + } else if diff_y == 0.0 { + if diff_x >= 0.0 { + points[0] = cs + w_point; + points[1] = cs + wh_point; + points[2] = ce + h_point; + points[3] = ce; + } else { + points[0] = cs; + points[1] = cs + h_point; + points[2] = ce + wh_point; + points[3] = ce + w_point; + } + } + + if p < 0.5 { + points[2] = points[2] * p * 2.0 + points[1] * (1.0 - p * 2.0); + points[3] = points[3] * p * 2.0 + points[0] * (1.0 - p * 2.0); + } else { + points[0] = points[3] * (p - 0.5) * 2.0 + points[0] * (1.0 - (p - 0.5) * 2.0); + points[1] = points[2] * (p - 0.5) * 2.0 + points[1] * (1.0 - (p - 0.5) * 2.0); + } +} + +fn on_animation_timer(left: isize) { + unsafe { + cursor_animation_percent = 1.0 + - (left as f32 / (ANIMATION_ARG_DURATION as f32 / ANIMATION_ARG_DURATION_STEP as f32)); + + #[cfg(debug_assertions)] + { + let msg = format!("cursor_animation_percent:{}", cursor_animation_percent); + OutputDebugStringW(to_wstring(&msg).as_slice().as_ptr()); + } + + // https://vc.zhizuobiao.com/vc-18112700241/ + let mut polygon: [QPointF; 4] = std::mem::zeroed(); + let p = cursor_animation_percent; + let cs = QPointF::new(cursor_start_x as f32, cursor_start_y as f32); + let ce = QPointF::new(cursor_end_x as f32, cursor_end_y as f32); + cursor_animation_draw_jelly_cursor( + cs, + ce, + cursor_info_w as f32, + cursor_info_h as f32, + p, + &mut polygon, + ); + + #[cfg(debug_assertions)] + { + let msg = format!( + "0:{:?}, 1:{:?}, 2:{:?}, 3: {:?}", + polygon[0], polygon[1], polygon[2], polygon[3] + ); + OutputDebugStringW(to_wstring(&msg).as_slice().as_ptr()); + } + + let hdc = GetDC(ANIMATION_WND); + + if !hdc.is_null() { + // 强制刷新,不然会有拖影 + let bb = GetStockObject(BLACK_BRUSH as i32); + let mut rc: RECT = std::mem::zeroed(); + GetClientRect(ANIMATION_WND, &mut rc); + FillRect(hdc, &rc, bb as *mut _); + + // let bb = GetStockObject(WHITE_BRUSH as i32); + let mut poly: [POINT; 4] = std::mem::zeroed(); + poly[0].x = polygon[0].x as i32; + poly[0].y = polygon[0].y as i32; + poly[1].x = polygon[1].x as i32; + poly[1].y = polygon[1].y as i32; + poly[2].x = polygon[2].x as i32; + poly[2].y = polygon[2].y as i32; + poly[3].x = polygon[3].x as i32; + poly[3].y = polygon[3].y as i32; + + let rgn = CreatePolygonRgn(&poly as *const _, 4, WINDING); + if !rgn.is_null() { + FillRgn(hdc, rgn, cursor_color as *mut _); + DeleteObject(rgn as *mut _); + } else { + // OutputDebugStringA(b"failed rng!\0".as_ptr() as *const _); + } + ReleaseDC(ANIMATION_WND, hdc); + } + } +} + pub fn becaon_init() { std::thread::spawn(move || unsafe { UI_THREAD_ID = GetCurrentThreadId(); @@ -214,7 +512,7 @@ pub fn becaon_init() { TranslateMessage(&msg); // 消息不能去window_proc里处理 if msg.message == WM_SHOW_BEACON { - create_wnd(); + create_beacon_wnd(); if !BEACON_WND.is_null() { KillTimer(BEACON_WND, TIMER_BEACON_DURATION); SetTimer(BEACON_WND, TIMER_BEACON_DELAY, msg.wParam as u32, None); @@ -224,6 +522,17 @@ pub fn becaon_init() { OutputDebugStringA(b"window is null!\0".as_ptr() as *const _); } } + } else if msg.message == WM_SHOW_ANIMATION { + create_animation_wnd(); + if !ANIMATION_WND.is_null() { + KillTimer(ANIMATION_WND, TIMER_ANIMATION_DURATION); + show_animation_wnd(); + } else { + #[cfg(debug_assertions)] + { + OutputDebugStringA(b"animation window is null!\0".as_ptr() as *const _); + } + } } else if msg.message == WM_BEACON_SET_SIZE { BEACON_WIDTH = msg.wParam as DWORD; BEACON_HEIGHT = msg.lParam as DWORD; @@ -242,11 +551,23 @@ pub fn becaon_init() { ShowWindow(BEACON_WND, SW_HIDE); KillTimer(BEACON_WND, TIMER_BEACON_DURATION); } else { - on_timer(BEACON_DURATION_LEFT_COUNT); + on_beacon_timer(BEACON_DURATION_LEFT_COUNT); } } else if msg.wParam == TIMER_BEACON_DELAY { KillTimer(BEACON_WND, TIMER_BEACON_DELAY); - show_wnd(); + show_beacon_wnd(); + } else if msg.wParam == TIMER_ANIMATION_DURATION { + ANIMATION_DURATION_LEFT_COUNT = ANIMATION_DURATION_LEFT_COUNT - 1; + if ANIMATION_DURATION_LEFT_COUNT < 0 { + #[cfg(debug_assertions)] + { + OutputDebugStringA(b"kill timer!\0" as *const _ as *const _); + } + ShowWindow(ANIMATION_WND, SW_HIDE); + KillTimer(ANIMATION_WND, TIMER_ANIMATION_DURATION); + } else { + on_animation_timer(ANIMATION_DURATION_LEFT_COUNT); + } } } DispatchMessageW(&msg); @@ -288,3 +609,47 @@ fn blink(x: usize, y: usize, timer: usize, delay: usize) -> Result<()> { } Ok(()) } + +#[defun] +fn animation( + x: usize, + y: usize, + w: usize, + h: usize, + timer: usize, + step: usize, + r: u8, + g: u8, + b: u8, +) -> Result<()> { + unsafe { + cursor_info_x = x; + cursor_info_y = y; + cursor_info_w = w; + cursor_info_h = h; + // 之前位置初始化,不用创建动画 + if cursor_info_prev_x == 0 && cursor_info_prev_y == 0 { + cursor_info_prev_x = x; + cursor_info_prev_y = y; + return Ok(()); + } + + if cursor_info_x != cursor_info_prev_x || cursor_info_y != cursor_info_prev_y { + cursor_start_x = cursor_info_prev_x; + cursor_start_y = cursor_info_prev_y; + cursor_end_x = cursor_info_x; + cursor_end_y = cursor_info_y; + cursor_color_r = r; + cursor_color_g = g; + cursor_color_b = b; + ANIMATION_ARG_DURATION = timer; + ANIMATION_ARG_DURATION_STEP = step; + PostThreadMessageW(UI_THREAD_ID, WM_SHOW_ANIMATION, 0 as WPARAM, 0 as LPARAM); + cursor_info_prev_x = x; + cursor_info_prev_y = y; + cursor_info_prev_w = w; + cursor_info_prev_h = h; + } + } + Ok(()) +}