Skip to content

Commit 79e76ce

Browse files
committed
bump version to 1.4.8 in package.json and tauri.conf.json
2 parents a56d8ba + f468d5d commit 79e76ce

33 files changed

Lines changed: 337 additions & 12 deletions

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "clippy",
3-
"version": "1.4.7",
3+
"version": "1.4.8",
44
"description": "Clipboard Manager built with Rust & Typescript",
55
"license": "MIT",
66
"type": "module",

src-tauri/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "clippy"
3-
version = "1.4.7"
3+
version = "1.4.8"
44
description = "Clipboard Manager built with Rust & Typescript"
55
authors = ["0-don"]
66
license = "MIT"

src-tauri/entity/src/settings.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub struct Model {
3434
pub max_text_size: i32,
3535
pub max_rtf_size: i32,
3636
pub max_html_size: i32,
37+
pub suppress_hotkey_on_fullscreen: bool,
3738
}
3839

3940
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
@@ -56,6 +57,7 @@ pub enum Column {
5657
MaxTextSize,
5758
MaxRtfSize,
5859
MaxHtmlSize,
60+
SuppressHotkeyOnFullscreen,
5961
}
6062

6163
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
@@ -95,6 +97,7 @@ impl ColumnTrait for Column {
9597
Self::MaxTextSize => ColumnType::Integer.def(),
9698
Self::MaxRtfSize => ColumnType::Integer.def(),
9799
Self::MaxHtmlSize => ColumnType::Integer.def(),
100+
Self::SuppressHotkeyOnFullscreen => ColumnType::Boolean.def(),
98101
}
99102
}
100103
}

src-tauri/migration/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod m000006_create_clipboard_file;
99
mod m000007_create_settings;
1010
mod m000008_create_hotkey;
1111
mod m000009_seed;
12+
mod m000010_add_suppress_hotkey_on_fullscreen;
1213

1314
pub struct Migrator;
1415

@@ -25,6 +26,7 @@ impl MigratorTrait for Migrator {
2526
Box::new(m000007_create_settings::Migration),
2627
Box::new(m000008_create_hotkey::Migration),
2728
Box::new(m000009_seed::Migration),
29+
Box::new(m000010_add_suppress_hotkey_on_fullscreen::Migration),
2830
]
2931
}
3032
}

src-tauri/migration/src/m000009_seed.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use common::io::keyboard::get_keyboard_layout;
22
use common::types::enums::HotkeyEvent;
33
use common::types::types::KeyboardLayout;
4-
use entity::{hotkey, settings};
4+
use entity::hotkey;
55
use sea_orm_migration::prelude::*;
66
use sea_orm_migration::sea_orm::entity::*;
77

@@ -13,10 +13,12 @@ impl MigrationTrait for Migration {
1313
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
1414
let db = manager.get_connection();
1515

16-
settings::ActiveModel {
17-
..Default::default()
18-
}
19-
.insert(db)
16+
// Use raw SQL to insert settings to avoid dependency on the current entity definition
17+
// which might include columns that don't exist yet at this migration step.
18+
db.execute(sea_orm::Statement::from_string(
19+
manager.get_database_backend(),
20+
"INSERT INTO settings DEFAULT VALUES".to_owned(),
21+
))
2022
.await?;
2123

2224
let key = match get_keyboard_layout() {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use sea_orm_migration::{prelude::*, schema::boolean};
2+
3+
#[derive(Iden)]
4+
enum Settings {
5+
Table,
6+
SuppressHotkeyOnFullscreen,
7+
}
8+
9+
#[derive(DeriveMigrationName)]
10+
pub struct Migration;
11+
12+
#[async_trait::async_trait]
13+
impl MigrationTrait for Migration {
14+
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
15+
manager
16+
.alter_table(
17+
Table::alter()
18+
.table(Settings::Table)
19+
.add_column(boolean(Settings::SuppressHotkeyOnFullscreen).default(false))
20+
.to_owned(),
21+
)
22+
.await
23+
}
24+
25+
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
26+
manager
27+
.alter_table(
28+
Table::alter()
29+
.table(Settings::Table)
30+
.drop_column(Settings::SuppressHotkeyOnFullscreen)
31+
.to_owned(),
32+
)
33+
.await
34+
}
35+
}

src-tauri/src/config/setup.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::{
1111
tao::{config::setup_config, tao_constants::setup_globals},
1212
};
1313

14-
pub fn setup(app: &mut tauri::App) -> Result<(), Box<(dyn std::error::Error + 'static)>> {
14+
pub fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error + 'static>> {
1515
setup_globals(app);
1616
setup_config();
1717

src-tauri/src/events/hotkey_events.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use crate::commands::sync::sync_authenticate_toggle;
22
use crate::prelude::*;
33
use crate::service::clipboard::init_clipboards;
4+
use crate::service::settings::get_global_settings;
45
use crate::service::window::open_window;
56
use crate::tao::global::{
67
get_app, get_global_hotkey_store, get_hotkey_running, get_hotkey_stop_tx, get_main_window,
78
get_window_hotkey_store,
89
};
10+
use crate::utils::fullscreen_detector::is_other_window_fullscreen;
911
use crate::{
1012
service::{
1113
clipboard::copy_clipboard_from_index,
@@ -49,7 +51,14 @@ pub fn setup_hotkey_listener() {
4951
loop {
5052
if let Ok(event) = receiver.try_recv() {
5153
if event.state == HotKeyState::Pressed {
52-
// Check both global and window hotkey stores
54+
let settings = get_global_settings();
55+
if settings.suppress_hotkey_on_fullscreen && is_other_window_fullscreen() {
56+
if get_main_window().is_visible().unwrap_or(false) {
57+
get_main_window().hide().expect("Failed to hide window");
58+
}
59+
continue;
60+
}
61+
5362
let hotkey: Option<Key> = get_global_hotkey_store()
5463
.get(&event.id)
5564
.cloned()
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
pub fn is_other_window_fullscreen() -> bool {
2+
#[cfg(target_os = "windows")]
3+
return is_fullscreen_windows();
4+
5+
#[cfg(target_os = "macos")]
6+
return is_fullscreen_macos();
7+
8+
#[cfg(target_os = "linux")]
9+
return is_fullscreen_linux();
10+
}
11+
12+
#[cfg(target_os = "windows")]
13+
fn is_fullscreen_windows() -> bool {
14+
use std::ffi::c_void;
15+
use std::mem::zeroed;
16+
17+
#[repr(C)]
18+
struct RECT {
19+
left: i32,
20+
top: i32,
21+
right: i32,
22+
bottom: i32,
23+
}
24+
25+
#[repr(C)]
26+
struct MONITORINFO {
27+
cb_size: u32,
28+
rc_monitor: RECT,
29+
rc_work: RECT,
30+
dw_flags: u32,
31+
}
32+
33+
type HWND = *mut c_void;
34+
type HMONITOR = *mut c_void;
35+
36+
#[link(name = "user32")]
37+
extern "system" {
38+
fn GetForegroundWindow() -> HWND;
39+
fn GetWindowRect(hwnd: HWND, lp_rect: *mut RECT) -> i32;
40+
fn MonitorFromWindow(hwnd: HWND, dw_flags: u32) -> HMONITOR;
41+
fn GetMonitorInfoW(h_monitor: HMONITOR, lpmi: *mut MONITORINFO) -> i32;
42+
fn GetDesktopWindow() -> HWND;
43+
fn GetShellWindow() -> HWND;
44+
}
45+
46+
unsafe {
47+
let foreground = GetForegroundWindow();
48+
if foreground.is_null() {
49+
return false;
50+
}
51+
52+
let desktop = GetDesktopWindow();
53+
let shell = GetShellWindow();
54+
if foreground == desktop || foreground == shell {
55+
return false;
56+
}
57+
58+
let mut window_rect: RECT = zeroed();
59+
if GetWindowRect(foreground, &mut window_rect) == 0 {
60+
return false;
61+
}
62+
63+
let monitor = MonitorFromWindow(foreground, 0x00000002);
64+
if monitor.is_null() {
65+
return false;
66+
}
67+
68+
let mut monitor_info: MONITORINFO = zeroed();
69+
monitor_info.cb_size = std::mem::size_of::<MONITORINFO>() as u32;
70+
if GetMonitorInfoW(monitor, &mut monitor_info) == 0 {
71+
return false;
72+
}
73+
74+
let screen = &monitor_info.rc_monitor;
75+
window_rect.left <= screen.left
76+
&& window_rect.top <= screen.top
77+
&& window_rect.right >= screen.right
78+
&& window_rect.bottom >= screen.bottom
79+
}
80+
}
81+
82+
#[cfg(target_os = "macos")]
83+
fn is_fullscreen_macos() -> bool {
84+
use std::ffi::c_void;
85+
use std::ptr::null;
86+
87+
type CFTypeRef = *const c_void;
88+
89+
#[link(name = "CoreGraphics", kind = "framework")]
90+
extern "C" {
91+
fn CGWindowListCopyWindowInfo(option: u32, relative_to_window: u32) -> CFTypeRef;
92+
}
93+
94+
#[link(name = "CoreFoundation", kind = "framework")]
95+
extern "C" {
96+
fn CFArrayGetCount(the_array: CFTypeRef) -> isize;
97+
fn CFArrayGetValueAtIndex(the_array: CFTypeRef, idx: isize) -> CFTypeRef;
98+
fn CFDictionaryGetValue(the_dict: CFTypeRef, key: CFTypeRef) -> CFTypeRef;
99+
fn CFNumberGetValue(number: CFTypeRef, the_type: isize, value_ptr: *mut c_void) -> u8;
100+
fn CFRelease(cf: CFTypeRef);
101+
fn CFStringCreateWithCString(alloc: CFTypeRef, c_str: *const i8, encoding: u32) -> CFTypeRef;
102+
}
103+
104+
unsafe {
105+
let window_list = CGWindowListCopyWindowInfo(1, 0);
106+
if window_list.is_null() {
107+
return false;
108+
}
109+
110+
let count = CFArrayGetCount(window_list);
111+
let layer_key = CFStringCreateWithCString(null(), b"kCGWindowLayer\0".as_ptr() as *const i8, 0x08000100);
112+
113+
let mut result = false;
114+
for i in 0..count {
115+
let window_info = CFArrayGetValueAtIndex(window_list, i);
116+
if window_info.is_null() {
117+
continue;
118+
}
119+
120+
let layer_value = CFDictionaryGetValue(window_info, layer_key);
121+
if !layer_value.is_null() {
122+
let mut layer: i32 = 0;
123+
if CFNumberGetValue(layer_value, 9, &mut layer as *mut i32 as *mut c_void) != 0 && layer == 0 && i == 0 {
124+
result = true;
125+
break;
126+
}
127+
}
128+
}
129+
130+
CFRelease(layer_key);
131+
CFRelease(window_list);
132+
result
133+
}
134+
}
135+
136+
#[cfg(target_os = "linux")]
137+
fn is_fullscreen_linux() -> bool {
138+
use std::ffi::{c_char, c_int, c_long, c_uchar, c_ulong, c_void};
139+
use std::ptr::null_mut;
140+
141+
type Display = c_void;
142+
type Window = c_ulong;
143+
type Atom = c_ulong;
144+
145+
#[link(name = "X11")]
146+
extern "C" {
147+
fn XOpenDisplay(display_name: *const c_char) -> *mut Display;
148+
fn XCloseDisplay(display: *mut Display) -> c_int;
149+
fn XDefaultRootWindow(display: *mut Display) -> Window;
150+
fn XInternAtom(display: *mut Display, atom_name: *const c_char, only_if_exists: c_int) -> Atom;
151+
fn XGetWindowProperty(
152+
display: *mut Display, w: Window, property: Atom, long_offset: c_long, long_length: c_long,
153+
delete: c_int, req_type: Atom, actual_type_return: *mut Atom, actual_format_return: *mut c_int,
154+
nitems_return: *mut c_ulong, bytes_after_return: *mut c_ulong, prop_return: *mut *mut c_uchar,
155+
) -> c_int;
156+
fn XFree(data: *mut c_void) -> c_int;
157+
}
158+
159+
unsafe {
160+
let display = XOpenDisplay(null_mut());
161+
if display.is_null() {
162+
return false;
163+
}
164+
165+
let root = XDefaultRootWindow(display);
166+
let net_active_window = XInternAtom(display, b"_NET_ACTIVE_WINDOW\0".as_ptr() as *const c_char, 1);
167+
let net_wm_state = XInternAtom(display, b"_NET_WM_STATE\0".as_ptr() as *const c_char, 1);
168+
let net_wm_state_fullscreen = XInternAtom(display, b"_NET_WM_STATE_FULLSCREEN\0".as_ptr() as *const c_char, 1);
169+
170+
if net_active_window == 0 || net_wm_state == 0 || net_wm_state_fullscreen == 0 {
171+
XCloseDisplay(display);
172+
return false;
173+
}
174+
175+
let mut actual_type: Atom = 0;
176+
let mut actual_format: c_int = 0;
177+
let mut nitems: c_ulong = 0;
178+
let mut bytes_after: c_ulong = 0;
179+
let mut prop: *mut c_uchar = null_mut();
180+
181+
let status = XGetWindowProperty(display, root, net_active_window, 0, 1, 0, 0, &mut actual_type, &mut actual_format, &mut nitems, &mut bytes_after, &mut prop);
182+
183+
if status != 0 || nitems == 0 || prop.is_null() {
184+
if !prop.is_null() { XFree(prop as *mut c_void); }
185+
XCloseDisplay(display);
186+
return false;
187+
}
188+
189+
let active_window = *(prop as *const Window);
190+
XFree(prop as *mut c_void);
191+
192+
if active_window == 0 {
193+
XCloseDisplay(display);
194+
return false;
195+
}
196+
197+
let status = XGetWindowProperty(display, active_window, net_wm_state, 0, 1024, 0, 0, &mut actual_type, &mut actual_format, &mut nitems, &mut bytes_after, &mut prop);
198+
199+
let mut is_fullscreen = false;
200+
if status == 0 && nitems > 0 && !prop.is_null() {
201+
let atoms = std::slice::from_raw_parts(prop as *const Atom, nitems as usize);
202+
is_fullscreen = atoms.contains(&net_wm_state_fullscreen);
203+
XFree(prop as *mut c_void);
204+
}
205+
206+
XCloseDisplay(display);
207+
is_fullscreen
208+
}
209+
}

0 commit comments

Comments
 (0)