use std::mem::MaybeUninit;
use crate::{
alloc::{Allocator, Object},
error::{Error, Result, from_result, from_result_with_len},
ffi, key,
terminal::Terminal,
};
#[doc(inline)]
pub use ffi::GhosttyMousePosition as Position;
#[derive(Debug)]
pub struct Encoder<'alloc>(Object<'alloc, ffi::GhosttyMouseEncoder>);
impl<'alloc> Encoder<'alloc> {
pub fn new() -> Result<Self> {
unsafe { Self::new_inner(std::ptr::null()) }
}
pub fn new_with_alloc<'ctx: 'alloc, Ctx>(alloc: &'alloc Allocator<'ctx, Ctx>) -> Result<Self> {
unsafe { Self::new_inner(alloc.to_raw()) }
}
unsafe fn new_inner(alloc: *const ffi::GhosttyAllocator) -> Result<Self> {
let mut raw: ffi::GhosttyMouseEncoder_ptr = std::ptr::null_mut();
let result = unsafe { ffi::ghostty_mouse_encoder_new(alloc, &raw mut raw) };
from_result(result)?;
Ok(Self(Object::new(raw)?))
}
unsafe fn setopt(
&mut self,
option: ffi::GhosttyMouseEncoderOption,
value: *const std::ffi::c_void,
) {
unsafe { ffi::ghostty_mouse_encoder_setopt(self.0.as_raw(), option, value) }
}
pub fn encode_to_vec(&mut self, event: &Event, vec: &mut Vec<u8>) -> Result<()> {
let remaining = vec.capacity() - vec.len();
let written = match self.encode_to_uninit_buf(event, vec.spare_capacity_mut()) {
Ok(v) => Ok(v),
Err(Error::OutOfSpace { required }) => {
vec.reserve(required - remaining);
self.encode_to_uninit_buf(event, vec.spare_capacity_mut())
}
Err(e) => Err(e),
};
unsafe { vec.set_len(vec.len() + written?) };
Ok(())
}
pub fn encode(&mut self, event: &Event, buf: &mut [u8]) -> Result<usize> {
self.encode_to_uninit_buf(event, unsafe {
std::slice::from_raw_parts_mut(buf.as_mut_ptr().cast(), buf.len())
})
}
fn encode_to_uninit_buf(
&mut self,
event: &Event,
buf: &mut [MaybeUninit<u8>],
) -> Result<usize> {
let mut written: usize = 0;
let result = unsafe {
ffi::ghostty_mouse_encoder_encode(
self.0.as_raw(),
event.0.as_raw(),
buf.as_mut_ptr().cast(),
buf.len(),
&raw mut written,
)
};
from_result_with_len(result, written)
}
pub fn set_options_from_terminal(&mut self, terminal: &Terminal<'_, '_>) -> &mut Self {
unsafe {
ffi::ghostty_mouse_encoder_setopt_from_terminal(
self.0.as_raw(),
terminal.inner.as_raw(),
);
}
self
}
pub fn set_tracking_mode(&mut self, value: TrackingMode) -> &mut Self {
unsafe {
self.setopt(
ffi::GhosttyMouseEncoderOption_GHOSTTY_MOUSE_ENCODER_OPT_EVENT,
std::ptr::from_ref(&value).cast(),
);
}
self
}
pub fn set_format(&mut self, value: Format) -> &mut Self {
unsafe {
self.setopt(
ffi::GhosttyMouseEncoderOption_GHOSTTY_MOUSE_ENCODER_OPT_EVENT,
std::ptr::from_ref(&value).cast(),
);
}
self
}
pub fn set_size(&mut self, value: EncoderSize) -> &mut Self {
let raw: ffi::GhosttyMouseEncoderSize = value.into();
unsafe {
self.setopt(
ffi::GhosttyMouseEncoderOption_GHOSTTY_MOUSE_ENCODER_OPT_SIZE,
std::ptr::from_ref(&raw).cast(),
);
}
self
}
pub fn set_any_button_pressed(&mut self, value: bool) -> &mut Self {
unsafe {
self.setopt(
ffi::GhosttyMouseEncoderOption_GHOSTTY_MOUSE_ENCODER_OPT_ANY_BUTTON_PRESSED,
std::ptr::from_ref(&value).cast(),
);
}
self
}
pub fn set_track_last_cell(&mut self, value: bool) -> &mut Self {
unsafe {
self.setopt(
ffi::GhosttyMouseEncoderOption_GHOSTTY_MOUSE_ENCODER_OPT_TRACK_LAST_CELL,
std::ptr::from_ref(&value).cast(),
);
}
self
}
pub fn reset(&mut self) {
unsafe { ffi::ghostty_mouse_encoder_reset(self.0.as_raw()) }
}
}
impl Drop for Encoder<'_> {
fn drop(&mut self) {
unsafe { ffi::ghostty_mouse_encoder_free(self.0.as_raw()) }
}
}
#[derive(Debug)]
pub struct Event<'alloc>(Object<'alloc, ffi::GhosttyMouseEvent>);
impl<'alloc> Event<'alloc> {
pub fn new() -> Result<Self> {
unsafe { Self::new_inner(std::ptr::null()) }
}
pub fn new_with_alloc<'ctx: 'alloc, Ctx>(alloc: &'alloc Allocator<'ctx, Ctx>) -> Result<Self> {
unsafe { Self::new_inner(alloc.to_raw()) }
}
unsafe fn new_inner(alloc: *const ffi::GhosttyAllocator) -> Result<Self> {
let mut raw: ffi::GhosttyMouseEvent_ptr = std::ptr::null_mut();
let result = unsafe { ffi::ghostty_mouse_event_new(alloc, &raw mut raw) };
from_result(result)?;
Ok(Self(Object::new(raw)?))
}
pub fn set_action(&mut self, action: Action) -> &mut Self {
unsafe {
ffi::ghostty_mouse_event_set_action(self.0.as_raw(), action as ffi::GhosttyMouseAction);
}
self
}
#[must_use]
pub fn action(&self) -> Action {
unsafe { ffi::ghostty_mouse_event_get_action(self.0.as_raw()) }
.try_into()
.unwrap_or(Action::Press)
}
pub fn set_button(&mut self, button: Option<Button>) -> &mut Self {
if let Some(button) = button {
unsafe {
ffi::ghostty_mouse_event_set_button(
self.0.as_raw(),
button as ffi::GhosttyMouseButton,
);
}
} else {
unsafe { ffi::ghostty_mouse_event_clear_button(self.0.as_raw()) }
}
self
}
#[must_use]
pub fn button(&self) -> Option<Button> {
let mut button: ffi::GhosttyMouseButton = 0;
let has_button =
unsafe { ffi::ghostty_mouse_event_get_button(self.0.as_raw(), &raw mut button) };
if has_button {
Some(button.try_into().unwrap_or(Button::Unknown))
} else {
None
}
}
pub fn set_mods(&mut self, mods: key::Mods) -> &mut Self {
unsafe { ffi::ghostty_mouse_event_set_mods(self.0.as_raw(), mods.bits()) }
self
}
#[must_use]
pub fn mods(&self) -> key::Mods {
key::Mods::from_bits_retain(unsafe { ffi::ghostty_mouse_event_get_mods(self.0.as_raw()) })
}
pub fn set_position(&mut self, pos: Position) -> &mut Self {
unsafe { ffi::ghostty_mouse_event_set_position(self.0.as_raw(), pos) }
self
}
#[must_use]
pub fn position(&self) -> Position {
unsafe { ffi::ghostty_mouse_event_get_position(self.0.as_raw()) }
}
}
impl Drop for Event<'_> {
fn drop(&mut self) {
unsafe { ffi::ghostty_mouse_event_free(self.0.as_raw()) }
}
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, int_enum::IntEnum)]
#[non_exhaustive]
pub enum TrackingMode {
None = ffi::GhosttyMouseTrackingMode_GHOSTTY_MOUSE_TRACKING_NONE,
X10 = ffi::GhosttyMouseTrackingMode_GHOSTTY_MOUSE_TRACKING_X10,
Normal = ffi::GhosttyMouseTrackingMode_GHOSTTY_MOUSE_TRACKING_NORMAL,
Button = ffi::GhosttyMouseTrackingMode_GHOSTTY_MOUSE_TRACKING_BUTTON,
Any = ffi::GhosttyMouseTrackingMode_GHOSTTY_MOUSE_TRACKING_ANY,
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, int_enum::IntEnum)]
#[non_exhaustive]
#[expect(missing_docs, reason = "missing upstream docs")]
pub enum Format {
X10 = ffi::GhosttyMouseFormat_GHOSTTY_MOUSE_FORMAT_X10,
Utf8 = ffi::GhosttyMouseFormat_GHOSTTY_MOUSE_FORMAT_UTF8,
Sgr = ffi::GhosttyMouseFormat_GHOSTTY_MOUSE_FORMAT_SGR,
Urxvt = ffi::GhosttyMouseFormat_GHOSTTY_MOUSE_FORMAT_URXVT,
SgrPixels = ffi::GhosttyMouseFormat_GHOSTTY_MOUSE_FORMAT_SGR_PIXELS,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct EncoderSize {
pub screen_width: u32,
pub screen_height: u32,
pub cell_width: u32,
pub cell_height: u32,
pub padding_top: u32,
pub padding_bottom: u32,
pub padding_right: u32,
pub padding_left: u32,
}
impl From<EncoderSize> for ffi::GhosttyMouseEncoderSize {
fn from(value: EncoderSize) -> Self {
Self {
size: std::mem::size_of::<Self>(),
screen_width: value.screen_width,
screen_height: value.screen_height,
cell_width: value.cell_width,
cell_height: value.cell_height,
padding_top: value.padding_top,
padding_bottom: value.padding_bottom,
padding_right: value.padding_right,
padding_left: value.padding_left,
}
}
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, int_enum::IntEnum)]
#[non_exhaustive]
pub enum Action {
Press = ffi::GhosttyMouseAction_GHOSTTY_MOUSE_ACTION_PRESS,
Release = ffi::GhosttyMouseAction_GHOSTTY_MOUSE_ACTION_RELEASE,
Motion = ffi::GhosttyMouseAction_GHOSTTY_MOUSE_ACTION_MOTION,
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, int_enum::IntEnum)]
#[non_exhaustive]
#[expect(missing_docs, reason = "self-explanatory")]
pub enum Button {
Unknown = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_UNKNOWN,
Left = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_LEFT,
Right = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_RIGHT,
Middle = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_MIDDLE,
Four = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_FOUR,
Five = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_FIVE,
Six = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_SIX,
Seven = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_SEVEN,
Eight = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_EIGHT,
Nine = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_NINE,
Ten = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_TEN,
Eleven = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_ELEVEN,
}