1use std::mem::MaybeUninit;
17
18use crate::{
19 alloc::{Allocator, Object},
20 error::{Error, Result, from_result, from_result_with_len},
21 ffi, key,
22 terminal::Terminal,
23};
24
25#[doc(inline)]
26pub use ffi::GhosttyMousePosition as Position;
27
28#[derive(Debug)]
31pub struct Encoder<'alloc>(Object<'alloc, ffi::GhosttyMouseEncoder>);
32
33impl<'alloc> Encoder<'alloc> {
34 pub fn new() -> Result<Self> {
36 unsafe { Self::new_inner(std::ptr::null()) }
38 }
39
40 pub fn new_with_alloc<'ctx: 'alloc, Ctx>(alloc: &'alloc Allocator<'ctx, Ctx>) -> Result<Self> {
45 unsafe { Self::new_inner(alloc.to_raw()) }
47 }
48
49 unsafe fn new_inner(alloc: *const ffi::GhosttyAllocator) -> Result<Self> {
50 let mut raw: ffi::GhosttyMouseEncoder_ptr = std::ptr::null_mut();
51 let result = unsafe { ffi::ghostty_mouse_encoder_new(alloc, &raw mut raw) };
52 from_result(result)?;
53 Ok(Self(Object::new(raw)?))
54 }
55
56 unsafe fn setopt(
57 &mut self,
58 option: ffi::GhosttyMouseEncoderOption,
59 value: *const std::ffi::c_void,
60 ) {
61 unsafe { ffi::ghostty_mouse_encoder_setopt(self.0.as_raw(), option, value) }
62 }
63
64 pub fn encode_to_vec(&mut self, event: &Event, vec: &mut Vec<u8>) -> Result<()> {
74 let remaining = vec.capacity() - vec.len();
75
76 let written = match self.encode_to_uninit_buf(event, vec.spare_capacity_mut()) {
77 Ok(v) => Ok(v),
78 Err(Error::OutOfSpace { required }) => {
79 vec.reserve(required - remaining);
81 self.encode_to_uninit_buf(event, vec.spare_capacity_mut())
82 }
83 Err(e) => Err(e),
84 };
85
86 unsafe { vec.set_len(vec.len() + written?) };
89 Ok(())
90 }
91
92 pub fn encode(&mut self, event: &Event, buf: &mut [u8]) -> Result<usize> {
99 self.encode_to_uninit_buf(event, unsafe {
101 std::slice::from_raw_parts_mut(buf.as_mut_ptr().cast(), buf.len())
102 })
103 }
104
105 fn encode_to_uninit_buf(
106 &mut self,
107 event: &Event,
108 buf: &mut [MaybeUninit<u8>],
109 ) -> Result<usize> {
110 let mut written: usize = 0;
111 let result = unsafe {
112 ffi::ghostty_mouse_encoder_encode(
113 self.0.as_raw(),
114 event.0.as_raw(),
115 buf.as_mut_ptr().cast(),
116 buf.len(),
117 &raw mut written,
118 )
119 };
120 from_result_with_len(result, written)
121 }
122
123 pub fn set_options_from_terminal(&mut self, terminal: &Terminal<'_, '_>) -> &mut Self {
128 unsafe {
129 ffi::ghostty_mouse_encoder_setopt_from_terminal(
130 self.0.as_raw(),
131 terminal.inner.as_raw(),
132 );
133 }
134 self
135 }
136 pub fn set_tracking_mode(&mut self, value: TrackingMode) -> &mut Self {
138 unsafe {
139 self.setopt(
140 ffi::GhosttyMouseEncoderOption_GHOSTTY_MOUSE_ENCODER_OPT_EVENT,
141 std::ptr::from_ref(&value).cast(),
142 );
143 }
144 self
145 }
146 pub fn set_format(&mut self, value: Format) -> &mut Self {
148 unsafe {
149 self.setopt(
150 ffi::GhosttyMouseEncoderOption_GHOSTTY_MOUSE_ENCODER_OPT_EVENT,
151 std::ptr::from_ref(&value).cast(),
152 );
153 }
154 self
155 }
156 pub fn set_size(&mut self, value: EncoderSize) -> &mut Self {
158 let raw: ffi::GhosttyMouseEncoderSize = value.into();
159 unsafe {
160 self.setopt(
161 ffi::GhosttyMouseEncoderOption_GHOSTTY_MOUSE_ENCODER_OPT_SIZE,
162 std::ptr::from_ref(&raw).cast(),
163 );
164 }
165 self
166 }
167 pub fn set_any_button_pressed(&mut self, value: bool) -> &mut Self {
169 unsafe {
170 self.setopt(
171 ffi::GhosttyMouseEncoderOption_GHOSTTY_MOUSE_ENCODER_OPT_ANY_BUTTON_PRESSED,
172 std::ptr::from_ref(&value).cast(),
173 );
174 }
175 self
176 }
177 pub fn set_track_last_cell(&mut self, value: bool) -> &mut Self {
179 unsafe {
180 self.setopt(
181 ffi::GhosttyMouseEncoderOption_GHOSTTY_MOUSE_ENCODER_OPT_TRACK_LAST_CELL,
182 std::ptr::from_ref(&value).cast(),
183 );
184 }
185 self
186 }
187
188 pub fn reset(&mut self) {
192 unsafe { ffi::ghostty_mouse_encoder_reset(self.0.as_raw()) }
193 }
194}
195
196impl Drop for Encoder<'_> {
197 fn drop(&mut self) {
198 unsafe { ffi::ghostty_mouse_encoder_free(self.0.as_raw()) }
199 }
200}
201
202#[derive(Debug)]
205pub struct Event<'alloc>(Object<'alloc, ffi::GhosttyMouseEvent>);
206
207impl<'alloc> Event<'alloc> {
208 pub fn new() -> Result<Self> {
210 unsafe { Self::new_inner(std::ptr::null()) }
212 }
213
214 pub fn new_with_alloc<'ctx: 'alloc, Ctx>(alloc: &'alloc Allocator<'ctx, Ctx>) -> Result<Self> {
219 unsafe { Self::new_inner(alloc.to_raw()) }
221 }
222
223 unsafe fn new_inner(alloc: *const ffi::GhosttyAllocator) -> Result<Self> {
224 let mut raw: ffi::GhosttyMouseEvent_ptr = std::ptr::null_mut();
225 let result = unsafe { ffi::ghostty_mouse_event_new(alloc, &raw mut raw) };
226 from_result(result)?;
227 Ok(Self(Object::new(raw)?))
228 }
229
230 pub fn set_action(&mut self, action: Action) -> &mut Self {
232 unsafe {
233 ffi::ghostty_mouse_event_set_action(self.0.as_raw(), action as ffi::GhosttyMouseAction);
234 }
235 self
236 }
237
238 #[must_use]
240 pub fn action(&self) -> Action {
241 unsafe { ffi::ghostty_mouse_event_get_action(self.0.as_raw()) }
242 .try_into()
243 .unwrap_or(Action::Press)
244 }
245
246 pub fn set_button(&mut self, button: Option<Button>) -> &mut Self {
248 if let Some(button) = button {
249 unsafe {
250 ffi::ghostty_mouse_event_set_button(
251 self.0.as_raw(),
252 button as ffi::GhosttyMouseButton,
253 );
254 }
255 } else {
256 unsafe { ffi::ghostty_mouse_event_clear_button(self.0.as_raw()) }
257 }
258 self
259 }
260
261 #[must_use]
263 pub fn button(&self) -> Option<Button> {
264 let mut button: ffi::GhosttyMouseButton = 0;
265 let has_button =
266 unsafe { ffi::ghostty_mouse_event_get_button(self.0.as_raw(), &raw mut button) };
267 if has_button {
268 Some(button.try_into().unwrap_or(Button::Unknown))
269 } else {
270 None
271 }
272 }
273
274 pub fn set_mods(&mut self, mods: key::Mods) -> &mut Self {
276 unsafe { ffi::ghostty_mouse_event_set_mods(self.0.as_raw(), mods.bits()) }
277 self
278 }
279
280 #[must_use]
282 pub fn mods(&self) -> key::Mods {
283 key::Mods::from_bits_retain(unsafe { ffi::ghostty_mouse_event_get_mods(self.0.as_raw()) })
284 }
285
286 pub fn set_position(&mut self, pos: Position) -> &mut Self {
288 unsafe { ffi::ghostty_mouse_event_set_position(self.0.as_raw(), pos) }
289 self
290 }
291
292 #[must_use]
294 pub fn position(&self) -> Position {
295 unsafe { ffi::ghostty_mouse_event_get_position(self.0.as_raw()) }
296 }
297}
298
299impl Drop for Event<'_> {
300 fn drop(&mut self) {
301 unsafe { ffi::ghostty_mouse_event_free(self.0.as_raw()) }
302 }
303}
304
305#[repr(u32)]
307#[derive(Clone, Copy, Debug, PartialEq, Eq, int_enum::IntEnum)]
308#[non_exhaustive]
309pub enum TrackingMode {
310 None = ffi::GhosttyMouseTrackingMode_GHOSTTY_MOUSE_TRACKING_NONE,
312 X10 = ffi::GhosttyMouseTrackingMode_GHOSTTY_MOUSE_TRACKING_X10,
314 Normal = ffi::GhosttyMouseTrackingMode_GHOSTTY_MOUSE_TRACKING_NORMAL,
316 Button = ffi::GhosttyMouseTrackingMode_GHOSTTY_MOUSE_TRACKING_BUTTON,
318 Any = ffi::GhosttyMouseTrackingMode_GHOSTTY_MOUSE_TRACKING_ANY,
320}
321
322#[repr(u32)]
324#[derive(Clone, Copy, Debug, PartialEq, Eq, int_enum::IntEnum)]
325#[non_exhaustive]
326#[expect(missing_docs, reason = "missing upstream docs")]
327pub enum Format {
328 X10 = ffi::GhosttyMouseFormat_GHOSTTY_MOUSE_FORMAT_X10,
329 Utf8 = ffi::GhosttyMouseFormat_GHOSTTY_MOUSE_FORMAT_UTF8,
330 Sgr = ffi::GhosttyMouseFormat_GHOSTTY_MOUSE_FORMAT_SGR,
331 Urxvt = ffi::GhosttyMouseFormat_GHOSTTY_MOUSE_FORMAT_URXVT,
332 SgrPixels = ffi::GhosttyMouseFormat_GHOSTTY_MOUSE_FORMAT_SGR_PIXELS,
333}
334
335#[derive(Clone, Copy, Debug, PartialEq, Eq)]
340pub struct EncoderSize {
341 pub screen_width: u32,
343 pub screen_height: u32,
345 pub cell_width: u32,
347 pub cell_height: u32,
349 pub padding_top: u32,
351 pub padding_bottom: u32,
353 pub padding_right: u32,
355 pub padding_left: u32,
357}
358
359impl From<EncoderSize> for ffi::GhosttyMouseEncoderSize {
360 fn from(value: EncoderSize) -> Self {
361 Self {
362 size: std::mem::size_of::<Self>(),
363 screen_width: value.screen_width,
364 screen_height: value.screen_height,
365 cell_width: value.cell_width,
366 cell_height: value.cell_height,
367 padding_top: value.padding_top,
368 padding_bottom: value.padding_bottom,
369 padding_right: value.padding_right,
370 padding_left: value.padding_left,
371 }
372 }
373}
374
375#[repr(u32)]
377#[derive(Clone, Copy, Debug, PartialEq, Eq, int_enum::IntEnum)]
378#[non_exhaustive]
379pub enum Action {
380 Press = ffi::GhosttyMouseAction_GHOSTTY_MOUSE_ACTION_PRESS,
382 Release = ffi::GhosttyMouseAction_GHOSTTY_MOUSE_ACTION_RELEASE,
384 Motion = ffi::GhosttyMouseAction_GHOSTTY_MOUSE_ACTION_MOTION,
386}
387
388#[repr(u32)]
390#[derive(Clone, Copy, Debug, PartialEq, Eq, int_enum::IntEnum)]
391#[non_exhaustive]
392#[expect(missing_docs, reason = "self-explanatory")]
393pub enum Button {
394 Unknown = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_UNKNOWN,
395 Left = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_LEFT,
396 Right = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_RIGHT,
397 Middle = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_MIDDLE,
398 Four = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_FOUR,
399 Five = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_FIVE,
400 Six = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_SIX,
401 Seven = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_SEVEN,
402 Eight = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_EIGHT,
403 Nine = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_NINE,
404 Ten = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_TEN,
405 Eleven = ffi::GhosttyMouseButton_GHOSTTY_MOUSE_BUTTON_ELEVEN,
406}