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
2 changes: 1 addition & 1 deletion .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ erase_line="\r\033[2K"
IFS=""
commands=(
"cargo fmt --check --manifest-path src-tauri/Cargo.toml"
"cargo clippy --all-targets --manifest-path src-tauri/Cargo.toml"
"cargo clippy --all-targets --manifest-path src-tauri/Cargo.toml -- -Dwarnings"
"npx eslint . --color"
)
steps=(
Expand Down
18 changes: 18 additions & 0 deletions src-tauri/src/commands/window.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use tauri::window::{ProgressBarState, ProgressBarStatus};

#[tauri::command]
pub fn window_close(window: tauri::Window) {
window.destroy().ok();
Expand All @@ -14,6 +16,22 @@ pub fn window_set_title(title: &str, window: tauri::Window) {
window.set_title(&format!("Tess - {title}")).ok();
}

#[tauri::command]
pub fn window_set_overall_progress(progress: u8, windows: tauri::Window) {
let progress_state = if progress > 0 && progress < 100 {
ProgressBarState {
progress: Some(progress.into()),
status: Some(ProgressBarStatus::Normal),
}
} else {
ProgressBarState {
progress: None,
status: Some(ProgressBarStatus::None),
}
};
windows.set_progress_bar(progress_state).ok();
}

#[tauri::command]
pub fn window_request_attention(window: tauri::Window) {
window
Expand Down
5 changes: 3 additions & 2 deletions src-tauri/src/ipc/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct Client {
}

impl Client {
#[allow(clippy::unused_async)]
pub async fn new(addr: impl AsRef<OsStr>) -> Result<Self, Error> {
Ok(Self {
sender: ClientOptions::new().open(addr)?,
Expand Down Expand Up @@ -58,14 +59,14 @@ impl Server {
buf.clear();
match self.listener.connect().await {
Err(e) => callback(Err(Box::from(e))),
Ok(_) => callback(
Ok(()) => callback(
self.listener
.read_to_end(&mut buf)
.await
.map_err(Box::from)
.and_then(|_| bitcode::decode(&buf).map_err(Box::from)),
),
};
}
drop(self.listener);
self.listener = match ServerOptions::new()
.first_pipe_instance(true)
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ async fn main() {
commands::window_close,
commands::window_focus,
commands::window_set_title,
commands::window_set_overall_progress,
commands::window_request_attention
])
.build(tauri::generate_context!())
Expand Down
36 changes: 19 additions & 17 deletions src-tauri/src/pty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Mutex;

#[cfg(target_os = "windows")]
use std::ffi::c_void;
#[cfg(target_os = "windows")]
use std::os::windows::ffi::OsStringExt;
#[cfg(target_os = "windows")]
Expand All @@ -43,7 +41,11 @@ unsafe impl Send for Pty {}
unsafe impl Sync for Pty {}

impl Pty {
#[allow(clippy::too_many_arguments, clippy::too_many_lines)]
#[allow(
clippy::too_many_arguments,
clippy::too_many_lines,
clippy::similar_names
)]
pub fn build_and_run(
command: &str,
workdir: Option<impl AsRef<OsStr>>,
Expand Down Expand Up @@ -91,16 +93,13 @@ impl Pty {
}

let built_command = CommandBuilder::from_argv(
#[allow(clippy::cast_sign_loss)]
unsafe { core::slice::from_raw_parts(argv, argc as usize) }
.iter()
.map(|s| OsString::from_wide(unsafe { s.as_wide() }))
.collect(),
);
unsafe {
LocalFree(Some(windows::Win32::Foundation::HLOCAL(
argv as *mut c_void,
)))
};
unsafe { LocalFree(Some(windows::Win32::Foundation::HLOCAL(argv.cast()))) };

built_command
};
Expand Down Expand Up @@ -189,33 +188,36 @@ impl Pty {
continue;
}

buf[remaining..].fill(0);
let read = match reader.read(&mut buf[remaining..]) {
Ok(0) => break,
Ok(n) => n,
Err(_) => continue,
};
let mut pre_parser = pre_parser.lock().unwrap();
let previous_cached_content = pre_parser.screen().contents();
let mut consummed = 0;
let mut processed_buf = std::io::Cursor::new([0; PTY_BUFFER_SIZE]);
let chunks = buf[..remaining + read].utf8_chunks();
for chunk in chunks {
pre_parser.process(chunk.valid().as_bytes());
let filled = read + remaining;
for chunk in buf[..filled].utf8_chunks() {
processed_buf.write_all(chunk.valid().as_bytes()).ok();
consummed += chunk.valid().len();

if !chunk.invalid().is_empty() && (read + remaining) - consummed >= 4 {
if !chunk.invalid().is_empty() && filled - consummed >= 4 {
consummed += chunk.invalid().len();
processed_buf.write_all("\u{FFFD}".as_bytes()).ok();
}
}

#[allow(clippy::cast_possible_truncation)]
let processed = &processed_buf.get_ref()[..processed_buf.position() as usize];
unsafe {
on_read(std::str::from_utf8_unchecked(processed_buf.get_ref()));
on_read(std::str::from_utf8_unchecked(processed));
}
remaining = (remaining + read) - consummed;
remaining = filled - consummed;
buf.rotate_left(consummed);

let mut pre_parser = pre_parser.lock().unwrap();
let previous_cached_content = pre_parser.screen().contents();
pre_parser.process(processed);

let cached_content = pre_parser.screen().contents();
if cached_content != previous_cached_content {
if notify {
Expand Down
13 changes: 8 additions & 5 deletions src-tauri/src/pty/process/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use windows::Win32::System::Threading::{
};
use windows_native::ntrtl::RTL_USER_PROCESS_PARAMETERS;

#[must_use]
pub fn get_leader_pid(shell_pid: u32) -> u32 {
let mut leader_pid = shell_pid;

Expand Down Expand Up @@ -64,6 +65,7 @@ pub async fn get_title(pid: u32, fetched_title: &mut Option<String>) {
.unwrap_or(None);
}

#[allow(clippy::unused_async)]
pub async fn get_working_dir(pid: u32, fetched_pwd: &mut Option<String>) {
if let Ok(handle) = unsafe {
windows::Win32::System::Threading::OpenProcess(
Expand All @@ -75,10 +77,11 @@ pub async fn get_working_dir(pid: u32, fetched_pwd: &mut Option<String>) {
let pbi = PROCESS_BASIC_INFORMATION::default();

*fetched_pwd = unsafe {
#[allow(clippy::cast_possible_truncation)]
NtQueryInformationProcess(
handle,
ProcessBasicInformation,
&pbi as *const _ as *mut c_void,
&raw const pbi as *mut c_void,
size_of::<PROCESS_BASIC_INFORMATION>() as u32,
ptr::null_mut(),
)
Expand All @@ -92,7 +95,7 @@ pub async fn get_working_dir(pid: u32, fetched_pwd: &mut Option<String>) {
ReadProcessMemory(
handle,
pbi.PebBaseAddress as *const c_void,
&peb as *const _ as *mut c_void,
&raw const peb as *mut c_void,
size_of::<PEB>(),
None,
)
Expand All @@ -106,7 +109,7 @@ pub async fn get_working_dir(pid: u32, fetched_pwd: &mut Option<String>) {
ReadProcessMemory(
handle,
peb.ProcessParameters as *const c_void,
&upp as *const _ as *mut c_void,
&raw const upp as *mut c_void,
size_of::<RTL_USER_PROCESS_PARAMETERS>(),
None,
)
Expand All @@ -119,8 +122,8 @@ pub async fn get_working_dir(pid: u32, fetched_pwd: &mut Option<String>) {
unsafe {
ReadProcessMemory(
handle,
upp.CurrentDirectory.DosPath.Buffer.as_ptr() as *mut c_void,
path.as_mut_ptr() as *mut c_void,
upp.CurrentDirectory.DosPath.Buffer.as_ptr().cast(),
path.as_mut_ptr().cast(),
upp.CurrentDirectory.DosPath.Length as usize,
None,
)
Expand Down
22 changes: 14 additions & 8 deletions src-tauri/src/settings/deserialized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,18 +656,17 @@ impl Default for CloseConfirmation {
pub struct DesktopIntegration {
pub custom_titlebar: bool,
pub dynamic_title: bool,
pub taskbar_progress: bool,
#[cfg(target_family = "unix")]
pub intercept_signals: bool,
}

impl Default for DesktopIntegration {
fn default() -> Self {
Self {
#[cfg(target_family = "unix")]
custom_titlebar: false,
#[cfg(target_os = "windows")]
custom_titlebar: true,
custom_titlebar: default_custom_title(),
dynamic_title: true,
taskbar_progress: true,
#[cfg(target_family = "unix")]
intercept_signals: true,
}
Expand All @@ -683,6 +682,7 @@ impl<'de> Deserialize<'de> for DesktopIntegration {
struct PartialDesktopIntegration {
custom_titlebar: Option<bool>,
dynamic_title: Option<bool>,
taskbar_progress: Option<bool>,
#[cfg(target_family = "unix")]
intercept_signals: Option<bool>,
}
Expand All @@ -698,15 +698,16 @@ impl<'de> Deserialize<'de> for DesktopIntegration {
Wrapper::Simple(enable) => Self {
custom_titlebar: enable,
dynamic_title: enable,
taskbar_progress: enable,
#[cfg(target_family = "unix")]
intercept_signals: enable,
},
Wrapper::Complex(partial_desktop_integration) => Self {
custom_titlebar: partial_desktop_integration
.custom_titlebar
.unwrap_or(default_custom_title()),
dynamic_title: partial_desktop_integration.dynamic_title.unwrap_or(true),
#[cfg(target_family = "unix")]
custom_titlebar: partial_desktop_integration.custom_titlebar.unwrap_or(false),
#[cfg(target_os = "windows")]
custom_titlebar: partial_desktop_integration.custom_titlebar.unwrap_or(true),
taskbar_progress: partial_desktop_integration.taskbar_progress.unwrap_or(true),
#[cfg(target_family = "unix")]
intercept_signals: partial_desktop_integration
.intercept_signals
Expand Down Expand Up @@ -747,6 +748,11 @@ const fn default_to_true() -> bool {
true
}

#[inline]
const fn default_custom_title() -> bool {
cfg!(target_os = "windows")
}

#[inline]
fn default_hyperlink_modifier() -> String {
String::from("CTRL")
Expand Down
9 changes: 5 additions & 4 deletions src-tauri/src/utils/windows/jumplist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ fn new_shell_link(
}

fn new_encoded_indirect_string(path: impl Display, resource_index: u16) -> Vec<u16> {
format!("@{},-{}", path, resource_index)
format!("@{path},-{resource_index}")
.encode_utf16()
.chain(std::iter::once(0))
.collect::<Vec<u16>>()
Expand Down Expand Up @@ -94,16 +94,17 @@ fn jumplist_tasks() -> Result<IObjectCollection, windows::core::Error> {
pub fn update() -> Result<(), windows::core::Error> {
unsafe {
CoInitialize(None).and_then(|| {
CoCreateInstance(&DestinationList, None, CLSCTX_INPROC_SERVER)
let res = CoCreateInstance(&DestinationList, None, CLSCTX_INPROC_SERVER)
.and_then(|jumplist: ICustomDestinationList| {
jumplist.BeginList::<IObjectArray>(&mut 0).and(Ok(jumplist))
})
.and_then(|jumplist| {
jumplist_tasks()
.and_then(|tasks| jumplist.AddUserTasks(&tasks).and(Ok(jumplist)))
})
.and_then(|jumplist| jumplist.CommitList())
.and(Ok(CoUninitialize()))
.and_then(|jumplist| jumplist.CommitList());
CoUninitialize();
res
})
}
}
22 changes: 17 additions & 5 deletions src/ts/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export default class App {
"tabTitleChange",
(e: CustomEventInit) => App.refreshWindowTitle(e.detail)
);
this.tabsManager.addEventListener(
"overallProgressUpdate",
(e: CustomEventInit) => App.refreshOverallProgress(e.detail)
);
this.tabsManager.addEventListener(
"widgetFocusRequest",
(e: CustomEventInit) => {
Expand Down Expand Up @@ -79,9 +83,9 @@ export default class App {
this.shortcutsManager.onKeyPress(e, target)
);

popupManager.addEventListener("popupClosed", () => {
this.focusedView!.focus();
});
popupManager.addEventListener("popupClosed", () =>
this.focusedView!.focus()
);

// eslint-disable-next-line @typescript-eslint/no-floating-promises
webviewWindow.listen("js_window_request_closing", () =>
Expand All @@ -100,8 +104,7 @@ export default class App {
if (settings.appBehavior.focusMode === "requestAttention") {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
invoke("window_request_attention");
}
if (settings.appBehavior.focusMode === "focus") {
} else if (settings.appBehavior.focusMode === "focus") {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
invoke("window_focus");
}
Expand Down Expand Up @@ -450,4 +453,13 @@ export default class App {
invoke("window_set_title", { title });
}
}

private static refreshOverallProgress(progress: number) {
if (settings.desktopIntegration.taskbarProgress) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
invoke("window_set_overall_progress", {
progress: Math.floor(progress),
});
}
}
}
Loading