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
16 changes: 13 additions & 3 deletions rust/limux-ghostty-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ pub const GHOSTTY_ACTION_NEW_WINDOW: c_int = 1;
pub const GHOSTTY_ACTION_NEW_TAB: c_int = 2;
pub const GHOSTTY_ACTION_CLOSE_TAB: c_int = 3;
pub const GHOSTTY_ACTION_NEW_SPLIT: c_int = 4;
pub const GHOSTTY_ACTION_SCROLLBAR: c_int = 26;
pub const GHOSTTY_ACTION_RENDER: c_int = 27;
pub const GHOSTTY_ACTION_DESKTOP_NOTIFICATION: c_int = 31;
pub const GHOSTTY_ACTION_SET_TITLE: c_int = 32;
Expand Down Expand Up @@ -293,13 +294,22 @@ pub struct ghostty_action_s {
// Must be exactly 24 bytes to match the C union.
#[repr(C)]
pub union ghostty_action_u {
pub scrollbar: ghostty_action_scrollbar_s,
pub desktop_notification: ghostty_action_desktop_notification_s,
pub set_title: ghostty_action_set_title_s,
pub pwd: ghostty_action_pwd_s,
pub child_exited: ghostty_surface_message_childexited_s,
_padding: [u8; 24],
}

#[repr(C)]
#[derive(Clone, Copy)]
pub struct ghostty_action_scrollbar_s {
pub total: u64,
pub offset: u64,
pub len: u64,
}

#[repr(C)]
#[derive(Clone, Copy)]
pub struct ghostty_action_desktop_notification_s {
Expand Down Expand Up @@ -368,15 +378,15 @@ extern "C" {
// Config
pub fn ghostty_config_new() -> ghostty_config_t;
pub fn ghostty_config_free(config: ghostty_config_t);
pub fn ghostty_config_load_default_files(config: ghostty_config_t);
pub fn ghostty_config_load_recursive_files(config: ghostty_config_t);
pub fn ghostty_config_finalize(config: ghostty_config_t);
pub fn ghostty_config_get(
config: ghostty_config_t,
out: *mut c_void,
key: *const c_char,
key_len: usize,
) -> bool;
pub fn ghostty_config_load_default_files(config: ghostty_config_t);
pub fn ghostty_config_load_recursive_files(config: ghostty_config_t);
pub fn ghostty_config_finalize(config: ghostty_config_t);

// App
pub fn ghostty_app_new(
Expand Down
2 changes: 1 addition & 1 deletion rust/limux-host-linux/src/pane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1070,7 +1070,7 @@ fn add_terminal_tab_inner(
},
term_callbacks,
);
let widget: gtk::Widget = term.overlay.clone().upcast();
let widget = term.root.clone();
internals.content_stack.add_named(&widget, Some(&tab_id));

{
Expand Down
83 changes: 81 additions & 2 deletions rust/limux-host-linux/src/terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ unsafe impl Sync for GhosttyState {}

static GHOSTTY: OnceLock<GhosttyState> = OnceLock::new();
static CURRENT_COLOR_SCHEME: AtomicI32 = AtomicI32::new(GHOSTTY_COLOR_SCHEME_LIGHT);
static CURRENT_SCROLLBAR_ENABLED: AtomicBool = AtomicBool::new(true);
static WAKEUP_IDLE_QUEUED: AtomicBool = AtomicBool::new(false);

type TitleChangedCallback = dyn Fn(&str);
Expand All @@ -43,6 +44,9 @@ type WidgetCallback = dyn Fn(&gtk::Widget);
struct SurfaceEntry {
gl_area: gtk::GLArea,
toast_overlay: gtk::Overlay,
scrollbar: gtk::Scrollbar,
scrollbar_adjustment: gtk::Adjustment,
scrollbar_syncing: Rc<Cell<bool>>,
on_title_changed: Option<Box<TitleChangedCallback>>,
on_pwd_changed: Option<Box<PwdChangedCallback>>,
on_desktop_notification: Option<Box<DesktopNotificationCallback>>,
Expand Down Expand Up @@ -262,7 +266,7 @@ impl TerminalHandle {
}

pub struct TerminalWidget {
pub overlay: gtk::Overlay,
pub root: gtk::Widget,
pub handle: TerminalHandle,
}

Expand Down Expand Up @@ -355,6 +359,7 @@ pub fn init_ghostty() {

let config = load_ghostty_config();
let background_opacity = load_background_opacity(config);
CURRENT_SCROLLBAR_ENABLED.store(load_scrollbar_enabled(config), Ordering::Relaxed);

let runtime_config = ghostty_runtime_config_s {
userdata: ptr::null_mut(),
Expand Down Expand Up @@ -418,6 +423,21 @@ fn load_background_opacity(config: ghostty_config_t) -> f64 {
}
}

fn load_scrollbar_enabled(config: ghostty_config_t) -> bool {
let mut value: *const c_char = ptr::null();
let key = b"scrollbar";
let loaded = unsafe {
ghostty_config_get(
config,
(&mut value as *mut *const c_char).cast::<c_void>(),
key.as_ptr().cast::<c_char>(),
key.len(),
)
};

!loaded || value.is_null() || unsafe { std::ffi::CStr::from_ptr(value) }.to_bytes() != b"never"
}

fn ghostty_color_scheme_for_dark_mode(dark: bool) -> c_int {
if dark {
GHOSTTY_COLOR_SCHEME_DARK
Expand Down Expand Up @@ -482,6 +502,31 @@ unsafe extern "C" fn ghostty_action_cb(
let tag = action.tag;

match tag {
GHOSTTY_ACTION_SCROLLBAR => {
if target.tag == GHOSTTY_TARGET_SURFACE {
let surface_key = unsafe { target.target.surface } as usize;
let scrollbar = unsafe { action.action.scrollbar };
SURFACE_MAP.with(|map| {
if let Some(entry) = map.borrow().get(&surface_key) {
entry.scrollbar_syncing.set(true);
entry.scrollbar_adjustment.configure(
scrollbar.offset as f64,
0.0,
scrollbar.total as f64,
1.0,
scrollbar.len as f64,
scrollbar.len as f64,
);
entry.scrollbar_syncing.set(false);
entry.scrollbar.set_visible(
CURRENT_SCROLLBAR_ENABLED.load(Ordering::Relaxed)
&& scrollbar.total > scrollbar.len,
);
}
});
}
true
}
GHOSTTY_ACTION_RENDER => {
if target.tag == GHOSTTY_TARGET_SURFACE {
let surface_key = unsafe { target.target.surface } as usize;
Expand Down Expand Up @@ -594,6 +639,7 @@ unsafe extern "C" fn ghostty_action_cb(
}
GHOSTTY_ACTION_RELOAD_CONFIG => {
let config = load_ghostty_config();
CURRENT_SCROLLBAR_ENABLED.store(load_scrollbar_enabled(config), Ordering::Relaxed);
match target.tag {
GHOSTTY_TARGET_APP => unsafe {
ghostty_app_update_config(app, config);
Expand Down Expand Up @@ -851,6 +897,7 @@ pub fn create_terminal(
let callbacks = Rc::new(RefCell::new(callbacks));
let surface_cell: Rc<RefCell<Option<ghostty_surface_t>>> = Rc::new(RefCell::new(None));
let had_focus = Rc::new(Cell::new(false));
let scrollbar_syncing = Rc::new(Cell::new(false));
let clipboard_context_cell: Rc<Cell<*mut ClipboardContext>> =
Rc::new(Cell::new(ptr::null_mut()));

Expand All @@ -860,6 +907,17 @@ pub fn create_terminal(
overlay.set_hexpand(true);
overlay.set_vexpand(true);

let scrollbar_adjustment = gtk::Adjustment::new(0.0, 0.0, 0.0, 1.0, 0.0, 0.0);
let scrollbar = gtk::Scrollbar::new(gtk::Orientation::Vertical, Some(&scrollbar_adjustment));
scrollbar.set_visible(false);
scrollbar.set_vexpand(true);

let root = gtk::Box::new(gtk::Orientation::Horizontal, 0);
root.set_hexpand(true);
root.set_vexpand(true);
root.append(&overlay);
root.append(&scrollbar);

let search_entry = gtk::SearchEntry::builder()
.hexpand(true)
.placeholder_text("Find in terminal")
Expand Down Expand Up @@ -938,15 +996,30 @@ pub fn create_terminal(
}
});
}
{
let surface_cell = surface_cell.clone();
let scrollbar_syncing = scrollbar_syncing.clone();
scrollbar_adjustment.connect_value_changed(move |adj| {
if scrollbar_syncing.get() {
return;
}

let row = adj.value().round() as usize;
surface_action(*surface_cell.borrow(), &format!("scroll_to_row:{row}"));
});
}

// On realize: create the Ghostty surface
{
let gl = gl_area.clone();
let overlay_for_map = overlay.clone();
let scrollbar_for_map = scrollbar.clone();
let scrollbar_adjustment_for_map = scrollbar_adjustment.clone();
let surface_cell = surface_cell.clone();
let callbacks = callbacks.clone();
let had_focus = had_focus.clone();
let clipboard_context_cell = clipboard_context_cell.clone();
let scrollbar_syncing = scrollbar_syncing.clone();
gl_area.connect_realize(move |gl_area| {
gl_area.make_current();
if let Some(err) = gl_area.error() {
Expand Down Expand Up @@ -1030,6 +1103,9 @@ pub fn create_terminal(
SurfaceEntry {
gl_area: gl.clone(),
toast_overlay: overlay_for_map.clone(),
scrollbar: scrollbar_for_map.clone(),
scrollbar_adjustment: scrollbar_adjustment_for_map.clone(),
scrollbar_syncing: scrollbar_syncing.clone(),
on_title_changed: Some(Box::new({
let cb = callbacks.clone();
move |title| {
Expand Down Expand Up @@ -1439,7 +1515,10 @@ pub fn create_terminal(
});
}

TerminalWidget { overlay, handle }
TerminalWidget {
root: root.upcast(),
handle,
}
}

// ---------------------------------------------------------------------------
Expand Down
Loading