Skip to content

Commit

Permalink
Rebase wez#2056 to main
Browse files Browse the repository at this point in the history
  • Loading branch information
swnakamura committed Jul 22, 2023
1 parent 94ab049 commit dae8ef1
Show file tree
Hide file tree
Showing 9 changed files with 395 additions and 34 deletions.
2 changes: 1 addition & 1 deletion wezterm-gui/src/scripting/guiwin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ impl UserData for GuiWin {
.notify(TermWindowNotif::Apply(Box::new(move |term_window| {
tx.try_send(match term_window.composition_status() {
DeadKeyStatus::None => None,
DeadKeyStatus::Composing(s) => Some(s.clone()),
DeadKeyStatus::Composing(s, ..) => Some(s.clone()),
})
.ok();
})));
Expand Down
1 change: 1 addition & 0 deletions wezterm-gui/src/termwindow/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub struct LineQuadCacheKey {
pub quad_generation: usize,
/// Only set if cursor.y == stable_row
pub composing: Option<String>,
pub composing_selection: Option<Range<usize>>,
pub selection: Range<usize>,
pub shape_hash: [u8; 16],
pub top_pixel_y: NotNan<f32>,
Expand Down
56 changes: 51 additions & 5 deletions wezterm-gui/src/termwindow/render/screen_line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,19 @@ impl crate::TermWindow {
};

// Referencing the text being composed, but only if it belongs to this pane
let composing = if cursor_idx.is_some() {
if let DeadKeyStatus::Composing(composing) = &self.dead_key_status {
Some(composing)
let (composing, selected_range) = if cursor_idx.is_some() {
if let DeadKeyStatus::Composing(composing, selected_range) = &self.dead_key_status {
(Some(composing), selected_range.as_ref())
} else {
None
(None, None)
}
} else {
None
(None, None)
};

let mut selected_start = 0;
let mut selected_width = 0;

let mut composition_width = 0;

let (_bidi_enabled, bidi_direction) = params.line.bidi_info();
Expand All @@ -93,6 +96,10 @@ impl crate::TermWindow {
// Do we need to shape immediately, or can we use the pre-shaped data?
if let Some(composing) = composing {
composition_width = unicode_column_width(composing, None);
if let Some(selected_range) = selected_range {
selected_start = unicode_column_width(&composing[..selected_range.start], None);
selected_width = unicode_column_width(&composing[selected_range.clone()], None);
}
}

let cursor_cell = if params.stable_line_idx == Some(params.cursor.y) {
Expand All @@ -109,6 +116,13 @@ impl crate::TermWindow {
0..0
};

let selected_cursor_range = if selected_width > 0 {
params.cursor.x + selected_start..params.cursor.x + selected_start + selected_width
} else {
0..0
};


let cursor_range_pixels = params.left_pixel_x + cursor_range.start as f32 * cell_width
..params.left_pixel_x + cursor_range.end as f32 * cell_width;

Expand Down Expand Up @@ -412,6 +426,38 @@ impl crate::TermWindow {

quad.set_fg_color(cursor_border_color);
quad.set_alt_color_and_mix_value(cursor_border_color_alt, cursor_border_mix);

if !selected_cursor_range.is_empty()
&& (cursor_range.start <= selected_cursor_range.start)
&& (selected_cursor_range.end <= cursor_range.end)
{
let mut quad = layers.allocate(0)?;
quad.set_position(
pos_x
+ (selected_cursor_range.start - cursor_range.start) as f32
* cell_width,
pos_y,
pos_x
+ (selected_cursor_range.end - cursor_range.start) as f32 * cell_width,
pos_y + cell_height,
);
quad.set_hsv(hsv);
quad.set_has_color(false);

quad.set_texture(
gl_state
.glyph_cache
.borrow_mut()
.cursor_sprite(
cursor_shape,
&params.render_metrics,
(selected_cursor_range.end - selected_cursor_range.start) as u8,
)?
.texture_coords(),
);

quad.set_fg_color(params.selection_bg);
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion window/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use bitflags::bitflags;
use config::{ConfigHandle, Dimension, GeometryOrigin};
use promise::Future;
use std::any::Any;
use std::ops::Range;
use std::path::PathBuf;
use std::rc::Rc;
use thiserror::Error;
Expand Down Expand Up @@ -135,7 +136,7 @@ pub enum DeadKeyStatus {
None,
/// Holding until composition is done; the string is the uncommitted
/// composition text to show as a placeholder
Composing(String),
Composing(String, Option<Range<usize>>),
}

#[derive(Debug)]
Expand Down
7 changes: 6 additions & 1 deletion window/src/os/macos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,16 @@ fn nsstring(s: &str) -> StrongPtr {
unsafe { StrongPtr::new(NSString::alloc(nil).init_str(s)) }
}

unsafe fn nsstring_to_str<'a>(mut ns: *mut Object) -> &'a str {
unsafe fn unattributed(mut ns: *mut Object) -> *mut Object {
let is_astring: bool = msg_send![ns, isKindOfClass: class!(NSAttributedString)];
if is_astring {
ns = msg_send![ns, string];
}
ns
}

unsafe fn nsstring_to_str<'a>(mut ns: *mut Object) -> &'a str {
ns = unattributed(ns);
let data = NSString::UTF8String(ns as id) as *const u8;
let len = NSString::len(ns as id);
let bytes = std::slice::from_raw_parts(data, len);
Expand Down
64 changes: 60 additions & 4 deletions window/src/os/macos/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#![allow(clippy::let_unit_value)]

use super::keycodes::*;
use super::{nsstring, nsstring_to_str};
use super::{nsstring, nsstring_to_str, unattributed};
use crate::clipboard::Clipboard as ClipboardContext;
use crate::connection::ConnectionOps;
use crate::os::macos::menu::{MenuItem, RepresentedItem};
Expand Down Expand Up @@ -44,6 +44,7 @@ use raw_window_handle::{
use std::any::Any;
use std::cell::RefCell;
use std::ffi::c_void;
use std::ops::Range;
use std::path::PathBuf;
use std::rc::Rc;
use std::str::FromStr;
Expand Down Expand Up @@ -489,6 +490,7 @@ impl Window {
ime_last_event: None,
live_resizing: false,
ime_text: String::new(),
selected_range: None,
}));

let window: id = msg_send![get_window_class(), alloc];
Expand Down Expand Up @@ -1372,6 +1374,7 @@ struct Inner {
live_resizing: bool,

ime_text: String,
selected_range: Option<Range<usize>>,
}

#[repr(C)]
Expand Down Expand Up @@ -1835,6 +1838,7 @@ impl WindowView {
if let Some(myself) = Self::get_this(this) {
let mut inner = myself.inner.borrow_mut();
inner.ime_text = s.to_string();
inner.selected_range = calc_str_selected_range(astring, selected_range, s.len());

/*
let key_is_down = inner.key_is_down.take().unwrap_or(true);
Expand Down Expand Up @@ -2361,7 +2365,7 @@ impl WindowView {
Ok(TranslateStatus::Composing(composing)) => {
// Next key press in dead key sequence is pending.
inner.events.dispatch(WindowEvent::AdviseDeadKeyStatus(
DeadKeyStatus::Composing(composing),
DeadKeyStatus::Composing(composing, None),
));

return;
Expand Down Expand Up @@ -2470,7 +2474,10 @@ impl WindowView {
// If it didn't generate an event, then a composition
// is pending.
let status = if inner.ime_last_event.is_none() {
DeadKeyStatus::Composing(inner.ime_text.clone())
DeadKeyStatus::Composing(
inner.ime_text.clone(),
inner.selected_range.clone(),
)
} else {
DeadKeyStatus::None
};
Expand Down Expand Up @@ -2500,7 +2507,10 @@ impl WindowView {
let status = if inner.ime_text.is_empty() {
DeadKeyStatus::None
} else {
DeadKeyStatus::Composing(inner.ime_text.clone())
DeadKeyStatus::Composing(
inner.ime_text.clone(),
inner.selected_range.clone(),
)
};
inner
.events
Expand Down Expand Up @@ -3189,3 +3199,49 @@ impl WindowView {
cls.register()
}
}


fn calc_str_selected_range(
astring: *mut Object,
selected_range: NSRange,
max: usize,
) -> Option<Range<usize>> {
unsafe fn sub_tostr_len(ns: *mut Object, range: NSRange, max: usize) -> usize {
let sub = msg_send![ns, substringWithRange: range];
let len = nsstring_to_str(sub).len();
std::cmp::min(len, max)
}

let ns;
let ns_length;
unsafe {
ns = unattributed(astring);
ns_length = msg_send![ns, length];
}

let a_start = selected_range.0.location;
let a_end = a_start + selected_range.0.length;
if ns_length < a_end {
return None;
}

let s_start;
let s_end;
unsafe {
s_start = if 0 < a_start {
sub_tostr_len(ns, NSRange::new(0, a_start), max)
} else {
0
};
s_end = if a_end < ns_length {
s_start + sub_tostr_len(ns, NSRange::new(a_start, a_end - a_start), max)
} else {
max
};
}
if s_end <= s_start {
return None;
}

Some(s_start..s_end)
}
85 changes: 66 additions & 19 deletions window/src/os/windows/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use std::collections::HashMap;
use std::convert::TryInto;
use std::ffi::OsString;
use std::io::{self, Error as IoError};
use std::ops::Range;
use std::os::windows::ffi::OsStringExt;
use std::path::PathBuf;
use std::ptr::{null, null_mut};
Expand Down Expand Up @@ -52,6 +53,8 @@ use winreg::RegKey;

const GCS_RESULTSTR: DWORD = 0x800;
const GCS_COMPSTR: DWORD = 0x8;
const GCS_COMPATTR: DWORD = 0x10;
const ATTR_TARGET_CONVERTED: u8 = 0x1;
const ISC_SHOWUICOMPOSITIONWINDOW: DWORD = 0x80000000;

#[allow(non_snake_case)]
Expand Down Expand Up @@ -200,6 +203,43 @@ unsafe impl HasRawDisplayHandle for WindowInner {
}
}

fn calc_str_selected_range(compstr: &[u16], compattr: &[u8], max: usize) -> Option<Range<usize>> {
fn selected(v: &u8) -> bool {
v & ATTR_TARGET_CONVERTED != 0
}
fn sub_tostr_len(compstr: &[u16], range: Range<usize>, max: usize) -> Option<usize> {
let sub = &compstr[range];
let len = OsString::from_wide(sub).into_string().ok()?.len();
Some(std::cmp::min(len, max))
}

if compstr.len() != compattr.len() {
return None;
}

let a_start = compattr.iter().position(selected)?;
let a_end = compattr.len() - compattr.iter().rev().position(selected)?;
if a_end <= a_start {
return None;
}

let s_start = if 0 < a_start {
sub_tostr_len(&compstr, 0..a_start, max)?
} else {
0
};
let s_end = if a_end < compattr.len() {
s_start + sub_tostr_len(&compstr, a_start..a_end, max)?
} else {
max
};
if s_end <= s_start {
return None;
}

Some(s_start..s_end)
}

unsafe impl HasRawWindowHandle for WindowInner {
fn raw_window_handle(&self) -> RawWindowHandle {
let mut handle = Win32WindowHandle::empty();
Expand Down Expand Up @@ -1976,26 +2016,27 @@ impl ImmContext {
}
}

pub fn get_str(&self, which: DWORD) -> Result<String, OsString> {
// This returns a size in bytes even though it is for a buffer of u16!
let byte_size =
unsafe { ImmGetCompositionStringW(self.imc, which, std::ptr::null_mut(), 0) };
if byte_size > 0 {
let word_size = byte_size as usize / 2;
let mut wide_buf = vec![0u16; word_size];
unsafe {
ImmGetCompositionStringW(
self.imc,
which,
wide_buf.as_mut_ptr() as *mut _,
byte_size as u32,
)
};
OsString::from_wide(&wide_buf).into_string()
pub fn get_raw<T: Clone>(&self, which: DWORD) -> Result<Vec<T>, i32> {
let size = unsafe { ImmGetCompositionStringW(self.imc, which, std::ptr::null_mut(), 0) };
if size < 0 {
Err(size)
} else if size == 0 {
Ok(vec![])
} else {
Ok(String::new())
let mut buf: Vec<T>;
let len = size as usize / std::mem::size_of::<T>();
buf = Vec::<T>::with_capacity(len);
unsafe {
buf.set_len(len);
ImmGetCompositionStringW(self.imc, which, buf.as_mut_ptr() as *mut _, size as u32);
}
Ok(buf)
}
}

pub fn get_str(&self, which: DWORD) -> Result<String, OsString> {
OsString::from_wide(&self.get_raw(which).unwrap_or_default()[..]).into_string()
}
}

impl Drop for ImmContext {
Expand Down Expand Up @@ -2073,11 +2114,17 @@ unsafe fn ime_composition(
if lparam & GCS_RESULTSTR == 0 {
// No finished result; continue with the default
// processing
if let Ok(composing) = imc.get_str(GCS_COMPSTR) {
let compstr = imc.get_raw::<u16>(GCS_COMPSTR).unwrap_or_default();
if let Ok(composing) = OsString::from_wide(&compstr[..]).into_string() {
let selected_range = match imc.get_raw::<u8>(GCS_COMPATTR) {
Ok(compattr) => calc_str_selected_range(&compstr, &compattr, composing.len()),
Err(_) => None,
};
inner
.events
.dispatch(WindowEvent::AdviseDeadKeyStatus(DeadKeyStatus::Composing(
composing,
selected_range,
)));
}
// We will show the composing string ourselves.
Expand Down Expand Up @@ -2698,7 +2745,7 @@ unsafe fn key(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<L
if inner.config.use_dead_keys {
inner.dead_pending.replace((modifiers, vk));
inner.events.dispatch(WindowEvent::AdviseDeadKeyStatus(
DeadKeyStatus::Composing(c.to_string()),
DeadKeyStatus::Composing(c.to_string(), None),
));
return Some(0);
}
Expand Down
Loading

0 comments on commit dae8ef1

Please sign in to comment.