92 lines
2.9 KiB
Rust
92 lines
2.9 KiB
Rust
use anyhow::Result;
|
|
use gpui::{AsyncApp, Pixels, Point, WindowHandle};
|
|
|
|
use crate::{capture::number, shell::Gallery};
|
|
|
|
/// A scroll's phase, as AppKit numbers them.
|
|
pub const BEGAN: i64 = 1;
|
|
pub const CHANGED: i64 = 2;
|
|
pub const ENDED: i64 = 4;
|
|
|
|
pub fn swipe(
|
|
window: WindowHandle<Gallery>,
|
|
at: Point<Pixels>,
|
|
across: f32,
|
|
phase: i64,
|
|
cx: &mut AsyncApp,
|
|
) -> Result<()> {
|
|
let (height, number) = window.update(cx, |_, window, _| {
|
|
(window.viewport_size().height, number(window))
|
|
})?;
|
|
post_scroll(
|
|
f64::from(at.x),
|
|
f64::from(height - at.y),
|
|
across,
|
|
phase,
|
|
number?,
|
|
)
|
|
}
|
|
|
|
/// Sends a real trackpad scroll to the window, sideways by `across` points, with its phase. Quartz scrolls carry no window, so AppKit reads their location as window points and the window takes them directly.
|
|
#[cfg(target_os = "macos")]
|
|
fn post_scroll(x: f64, y: f64, across: f32, phase: i64, number: u32) -> Result<()> {
|
|
use cocoa::{
|
|
appkit::NSApp,
|
|
base::{id, nil},
|
|
foundation::NSRect,
|
|
};
|
|
use objc::{class, msg_send, sel, sel_impl};
|
|
type Event = *mut std::ffi::c_void;
|
|
#[repr(C)]
|
|
struct Spot {
|
|
x: f64,
|
|
y: f64,
|
|
}
|
|
unsafe extern "C" {
|
|
fn CGEventCreateScrollWheelEvent2(
|
|
source: *const std::ffi::c_void,
|
|
units: u32,
|
|
count: u32,
|
|
wheel1: i32,
|
|
wheel2: i32,
|
|
wheel3: i32,
|
|
) -> Event;
|
|
fn CGEventSetIntegerValueField(event: Event, field: u32, value: i64);
|
|
fn CGEventSetLocation(event: Event, at: Spot);
|
|
fn CFRelease(object: *const std::ffi::c_void);
|
|
}
|
|
const PIXELS: u32 = 0;
|
|
const CONTINUOUS: u32 = 88;
|
|
const PHASE: u32 = 99;
|
|
unsafe {
|
|
let window: id = msg_send![NSApp(), windowWithWindowNumber: i64::from(number)];
|
|
anyhow::ensure!(window != nil, "no window numbered {number}");
|
|
let screens: id = msg_send![class!(NSScreen), screens];
|
|
let primary: id = msg_send![screens, objectAtIndex: 0usize];
|
|
let frame: NSRect = msg_send![primary, frame];
|
|
let event = CGEventCreateScrollWheelEvent2(
|
|
std::ptr::null(),
|
|
PIXELS,
|
|
2,
|
|
0,
|
|
across.round() as i32,
|
|
0,
|
|
);
|
|
anyhow::ensure!(!event.is_null(), "Quartz refused a scroll event");
|
|
CGEventSetLocation(
|
|
event,
|
|
Spot {
|
|
x,
|
|
y: frame.size.height - y,
|
|
},
|
|
);
|
|
CGEventSetIntegerValueField(event, CONTINUOUS, 1);
|
|
CGEventSetIntegerValueField(event, PHASE, phase);
|
|
let scroll: id = msg_send![class!(NSEvent), eventWithCGEvent: event];
|
|
CFRelease(event);
|
|
anyhow::ensure!(scroll != nil, "AppKit refused a scroll event");
|
|
let _: () = msg_send![window, sendEvent: scroll];
|
|
}
|
|
Ok(())
|
|
}
|