Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions rust_indicator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ windows = { version = "0.58", features = [
"Win32_Foundation",
"Win32_UI_WindowsAndMessaging",
"Win32_UI_Input_Ime",
"Win32_UI_Input_KeyboardAndMouse",
"Win32_UI_Accessibility",
"Win32_Graphics_Gdi",
"Win32_Graphics_GdiPlus",
Expand Down
41 changes: 32 additions & 9 deletions rust_indicator/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ pub struct Config {
pub poll_track_interval_ms: u64,

pub tray_enable: bool,
pub debug_console: bool,

pub caret_enable: bool,
pub caret_color_cn: u32,
pub caret_color_en: u32,
pub caret_color_en_upper: u32,
pub caret_size: i32,
pub caret_offset_x: i32,
pub caret_offset_y: i32,
Expand All @@ -27,6 +29,7 @@ pub struct Config {
pub mouse_enable: bool,
pub mouse_color_cn: u32,
pub mouse_color_en: u32,
pub mouse_color_en_upper: u32,
pub mouse_size: i32,
pub mouse_offset_x: i32,
pub mouse_offset_y: i32,
Expand All @@ -40,16 +43,19 @@ impl Default for Config {
poll_state_interval_ms: 100,
poll_track_interval_ms: 10,
tray_enable: true,
debug_console: false,
caret_enable: true,
caret_color_cn: parse_color("#FF7800A0"),
caret_color_en: parse_color("#0078FF30"),
caret_color_cn: parse_color("#FF0000A0"),
caret_color_en: parse_color("#0000FFA0"),
caret_color_en_upper: parse_color("#008000A0"),
caret_size: 8,
caret_offset_x: 0,
caret_offset_y: 0,
caret_show_en: true,
mouse_enable: true,
mouse_color_cn: parse_color("#FF7800A0"),
mouse_color_en: parse_color("#0078FF30"),
mouse_color_cn: parse_color("#FF0000A0"),
mouse_color_en: parse_color("#0000FFA0"),
mouse_color_en_upper: parse_color("#008000A0"),
mouse_size: 8,
mouse_offset_x: 2,
mouse_offset_y: 18,
Expand Down Expand Up @@ -121,13 +127,20 @@ fn load_config() -> Config {
if let Some(v) = get("poll", "state_interval_ms") { if let Ok(n) = v.parse() { config.poll_state_interval_ms = n; } }
if let Some(v) = get("poll", "track_interval_ms") { if let Ok(n) = v.parse() { config.poll_track_interval_ms = n; } }

if let Some(v) = get("tray", "enable") {
if let Some(v) = get("tray", "enable") {
match v.as_str() {
"true" => config.tray_enable = true,
"false" => config.tray_enable = false,
_ => {} // 保持默认值
}
}
if let Some(v) = get("tray", "debug_console") {
match v.as_str() {
"true" => config.debug_console = true,
"false" => config.debug_console = false,
_ => {}
}
}

if let Some(v) = get("caret", "enable") {
match v.as_str() {
Expand All @@ -138,6 +151,7 @@ fn load_config() -> Config {
}
if let Some(v) = get("caret", "color_cn") { config.caret_color_cn = v.parse_color(); }
if let Some(v) = get("caret", "color_en") { config.caret_color_en = v.parse_color(); }
if let Some(v) = get("caret", "color_en_upper") { config.caret_color_en_upper = v.parse_color(); }
if let Some(v) = get("caret", "size") { if let Ok(n) = v.parse() { config.caret_size = n; } }
if let Some(v) = get("caret", "offset_x") { if let Ok(n) = v.parse() { config.caret_offset_x = n; } }
if let Some(v) = get("caret", "offset_y") { if let Ok(n) = v.parse() { config.caret_offset_y = n; } }
Expand All @@ -158,6 +172,7 @@ fn load_config() -> Config {
}
if let Some(v) = get("mouse", "color_cn") { config.mouse_color_cn = v.parse_color(); }
if let Some(v) = get("mouse", "color_en") { config.mouse_color_en = v.parse_color(); }
if let Some(v) = get("mouse", "color_en_upper") { config.mouse_color_en_upper = v.parse_color(); }
if let Some(v) = get("mouse", "size") { if let Ok(n) = v.parse() { config.mouse_size = n; } }
if let Some(v) = get("mouse", "offset_x") { if let Ok(n) = v.parse() { config.mouse_offset_x = n; } }
if let Some(v) = get("mouse", "offset_y") { if let Ok(n) = v.parse() { config.mouse_offset_y = n; } }
Expand Down Expand Up @@ -188,20 +203,25 @@ track_interval_ms = 10 # 位置追踪间隔 (ms)

[tray]
enable = true # 是否显示托盘图标 (false 时完全后台运行,只能通过任务管理器结束)
debug_console = false # 是否显示调试控制台 (用于开发调试)

[caret]
# 颜色格式:#RRGGBBAA
# RR=红, GG=绿, BB=蓝, AA=透明度(00=透明, FF=不透明, A0=63%不透明)
enable = true # 是否启用文本光标提示
color_cn = "#FF7800A0" # 中文状态颜色 (#RRGGBBAA)
color_en = "#0078FF30" # 英文状态颜色
color_cn = "#FF0000A0" # 中文状态颜色(红)
color_en = "#0000FFA0" # 英文小写状态颜色(蓝)
color_en_upper = "#008000A0" # 英文大写状态颜色(绿)
size = 8 # 提示球大小
offset_x = 0
offset_y = 0
show_en = true # 英文状态下是否显示

[mouse]
enable = true # 是否开启鼠标提示
color_cn = "#FF7800A0" # 中文状态颜色
color_en = "#0078FF30" # 英文状态颜色
color_cn = "#FF0000A0" # 中文状态颜色(红)
color_en = "#0000FFA0" # 英文小写状态颜色(蓝)
color_en_upper = "#008000A0" # 英文大写状态颜色(绿)
size = 8 # 提示球大小
offset_x = 2
offset_y = 18
Expand All @@ -220,16 +240,19 @@ pub fn get() -> &'static Config { CONFIG.get_or_init(load_config) }
pub fn state_poll_interval_ms() -> u64 { get().poll_state_interval_ms }
pub fn track_poll_interval_ms() -> u64 { get().poll_track_interval_ms }
pub fn tray_enable() -> bool { get().tray_enable }
pub fn debug_console() -> bool { get().debug_console }
pub fn caret_enable() -> bool { get().caret_enable }
pub fn caret_color_cn() -> u32 { get().caret_color_cn }
pub fn caret_color_en() -> u32 { get().caret_color_en }
pub fn caret_color_en_upper() -> u32 { get().caret_color_en_upper }
pub fn caret_size() -> i32 { get().caret_size }
pub fn caret_offset_x() -> i32 { get().caret_offset_x }
pub fn caret_offset_y() -> i32 { get().caret_offset_y }
pub fn caret_show_en() -> bool { get().caret_show_en }
pub fn mouse_enable() -> bool { get().mouse_enable }
pub fn mouse_color_cn() -> u32 { get().mouse_color_cn }
pub fn mouse_color_en() -> u32 { get().mouse_color_en }
pub fn mouse_color_en_upper() -> u32 { get().mouse_color_en_upper }
pub fn mouse_size() -> i32 { get().mouse_size }
pub fn mouse_offset_x() -> i32 { get().mouse_offset_x }
pub fn mouse_offset_y() -> i32 { get().mouse_offset_y }
Expand Down
80 changes: 80 additions & 0 deletions rust_indicator/src/fullscreen_detector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//! 全屏应用检测模块
//!
//! 用于检测是否有应用程序处于全屏状态(如视频播放、游戏等),
//! 在全屏时自动隐藏 IME 指示器,避免遮挡视频内容。

use windows::Win32::Foundation::RECT;
use windows::Win32::UI::WindowsAndMessaging::{
GetForegroundWindow, GetSystemMetrics, GetWindowRect, SM_CXSCREEN, SM_CYSCREEN,
};

/// 检测是否有应用程序处于全屏状态
///
/// 判断逻辑:
/// 1. 获取当前活动窗口
/// 2. 检查窗口是否覆盖整个屏幕
/// 3. 排除桌面窗口(Progman)
///
/// # Returns
/// - `true` - 检测到全屏应用
/// - `false` - 无全屏应用
pub fn is_fullscreen_app() -> bool {
unsafe {
// 获取当前活动窗口
let hwnd = GetForegroundWindow();

if hwnd.is_invalid() {
return false;
}

// 获取窗口矩形
let mut rect = RECT::default();
if GetWindowRect(hwnd, &mut rect).is_err() {
return false;
}

// 获取屏幕尺寸
let screen_width = GetSystemMetrics(SM_CXSCREEN);
let screen_height = GetSystemMetrics(SM_CYSCREEN);

// 检查窗口是否覆盖整个屏幕
// 允许小幅偏差(某些播放器可能会有几像素的边框)
let tolerance = 2;
let covers_screen = (rect.left <= tolerance)
&& (rect.top <= tolerance)
&& (rect.right >= screen_width - tolerance)
&& (rect.bottom >= screen_height - tolerance);

if !covers_screen {
return false;
}

// 排除桌面窗口
// 获取窗口类名来判断是否是桌面
let mut class_name = [0u16; 256];
let _ = windows::Win32::UI::WindowsAndMessaging::GetClassNameW(hwnd, &mut class_name);

let class_name_str = String::from_utf16_lossy(
&class_name[..class_name.iter().position(|&c| c == 0).unwrap_or(0)],
);

// 排除桌面和某些系统窗口
if class_name_str.contains("Progman") || class_name_str.contains("WorkerW") {
return false;
}

true
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_fullscreen_detection() {
// 基本的功能测试
let result = is_fullscreen_app();
println!("Fullscreen status: {}", result);
}
}
Loading