Files
ElyOffice/crates/ely-lok/src/ffi/document.rs
T

441 lines
15 KiB
Rust

use std::{ffi::c_char, ffi::c_void, io};
use super::{Callback, Doc, Document, cstring};
unsafe extern "C" {
fn free(ptr: *mut c_void);
}
impl Document {
/// Makes this document's view the kernel's current one.
pub fn activate(&self) {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, i32);
call!(class, 27, F, self.doc, self.view);
}
/// Page count or area in LOK twips; zeros when unset.
pub fn size(&self) -> (i64, i64) {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, *mut i64, *mut i64);
let (mut width, mut height) = (0i64, 0i64);
call!(class, 11, F, self.doc, &mut width, &mut height);
(width, height)
}
/// Paints the document into `buffer` at (x, y) twips.
pub fn paint_rect(&self, buffer: &mut [u8], pixels: (i32, i32), twips: (i32, i32, i32, i32)) {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, *mut u8, i32, i32, i32, i32, i32, i32);
let (pw, ph) = pixels;
let (x, y, w, h) = twips;
call!(
class,
9,
F,
self.doc,
buffer.as_mut_ptr(),
pw,
ph,
x,
y,
w,
h
);
}
/// Paints part `part` whole; `mode` 1 paints masters.
pub fn paint_part(&self, buffer: &mut [u8], (part, mode): (i32, i32), pixels: (i32, i32)) {
let (w, h) = self.size();
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, *mut u8, i32, i32, i32, i32, i32, i32, i32, i32);
call!(
class,
32,
F,
self.doc,
buffer.as_mut_ptr(),
part,
mode,
pixels.0,
pixels.1,
0,
0,
w as i32,
h as i32
);
}
/// Initializes rendering so callbacks flow (LOK-03).
pub fn initialize_for_rendering(&self) {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, *const c_char);
call!(class, 12, F, self.doc, std::ptr::null());
}
/// Registers `callback` with `data` (LOK-03).
pub fn set_callback(&self, callback: Option<Callback>, data: *mut c_void) {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, Option<Callback>, *mut c_void);
call!(class, 13, F, self.doc, callback, data);
}
/// Paints the document into `buffer` at `width` x `height` (R-01).
pub fn paint_tile(&self, buffer: &mut [u8], width: i32, height: i32) {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, *mut u8, i32, i32, i32, i32, i32, i32);
call!(
class,
9,
F,
self.doc,
buffer.as_mut_ptr(),
width,
height,
0,
0,
width * 15,
height * 15
);
}
/// Runs a UNO command with JSON `args` (LOK-08).
pub fn post_uno(&self, command: &str, args: Option<&str>) -> io::Result<()> {
self.dispatch(command, args, false)
}
/// Runs a UNO command and asks for its completion result.
pub fn post_uno_notify(&self, command: &str, args: Option<&str>) -> io::Result<()> {
self.dispatch(command, args, true)
}
fn dispatch(&self, command: &str, args: Option<&str>, notify: bool) -> io::Result<()> {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, *const c_char, *const c_char, bool);
let command = cstring(command)?;
let args = args.map(cstring).transpose()?;
let args = args.as_ref().map_or(std::ptr::null(), |args| args.as_ptr());
call!(class, 16, F, self.doc, command.as_ptr(), args, notify);
Ok(())
}
/// Posts a key event: 0 input, 1 up.
pub fn post_key(&self, event_type: i32, char_code: i32, key_code: i32) {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, i32, i32, i32);
call!(class, 14, F, self.doc, event_type, char_code, key_code);
}
/// Posts a mouse event: 0 down, 1 up, 2 move.
pub fn post_mouse(&self, event: [i32; 6]) {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, i32, i32, i32, i32, i32, i32);
let [kind, x, y, count, buttons, modifier] = event;
call!(class, 15, F, self.doc, kind, x, y, count, buttons, modifier);
}
/// Moves a selected object's handle: 0 start, 1 end.
pub fn set_graphic_selection(&self, kind: i32, x: i32, y: i32) {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, i32, i32, i32);
call!(class, 20, F, self.doc, kind, x, y);
}
/// Posts IME text: 0 composing, 2 commit (LOK-07).
pub fn post_text(&self, event_type: i32, text: &str) -> io::Result<()> {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, u32, i32, *const c_char);
let text = cstring(text)?;
call!(class, 40, F, self.doc, 0, event_type, text.as_ptr());
Ok(())
}
/// LOK_DOCTYPE: 0 text, 1 spreadsheet, 2 presentation.
pub fn kind(&self) -> i32 {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc) -> i32;
call!(class, 2, F, self.doc)
}
/// LOK_SELTYPE: 0 none, 1 text, 3 complex.
pub fn selection_type(&self) -> i32 {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc) -> i32;
call!(class, 54, F, self.doc)
}
/// The view clipboard in `mimes`; empty ones skipped (LOK-06).
pub fn clipboard(&self, mimes: &[&str]) -> io::Result<Vec<(String, Vec<u8>)>> {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(
*mut Doc,
*const *const c_char,
*mut usize,
*mut *mut *mut c_char,
*mut *mut usize,
*mut *mut *mut c_char,
) -> i32;
let owned = mimes
.iter()
.map(|mime| cstring(mime))
.collect::<io::Result<Vec<_>>>()?;
let mut list: Vec<*const c_char> = owned.iter().map(|mime| mime.as_ptr()).collect();
list.push(std::ptr::null());
let (mut count, mut names, mut sizes, mut streams) = (
0usize,
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut(),
);
let ok = call!(
class,
52,
F,
self.doc,
list.as_ptr(),
&mut count,
&mut names,
&mut sizes,
&mut streams
);
if ok == 0 {
return Err(io::Error::other("getClipboard failed"));
}
let mut found = Vec::new();
for i in 0..count {
let (name, size, stream) = unsafe { (*names.add(i), *sizes.add(i), *streams.add(i)) };
if size > 0 && !stream.is_null() {
let mime = unsafe { std::ffi::CStr::from_ptr(name) };
let data = unsafe { std::slice::from_raw_parts(stream.cast::<u8>(), size) };
found.push((mime.to_string_lossy().into_owned(), data.to_vec()));
}
unsafe {
free(name.cast());
free(stream.cast());
}
}
unsafe {
free(names.cast());
free(sizes.cast());
free(streams.cast());
}
Ok(found)
}
/// The JSON LOK keeps for `command` (getCommandValues).
pub fn command_values(&self, command: &str) -> io::Result<String> {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, *const c_char) -> *mut c_char;
let name = cstring(command)?;
let raw = call!(class, 22, F, self.doc, name.as_ptr());
if raw.is_null() {
return Err(io::Error::other(format!("no values for {command}")));
}
let text = unsafe { std::ffi::CStr::from_ptr(raw) }
.to_string_lossy()
.into_owned();
unsafe { free(raw.cast()) };
Ok(text)
}
/// Turns on the a11y callbacks for the current view.
pub fn enable_a11y(&self) {
let class = unsafe { (*self.doc).class };
type View = unsafe extern "C" fn(*mut Doc) -> i32;
type F = unsafe extern "C" fn(*mut Doc, i32, bool);
let view = call!(class, 28, View, self.doc);
call!(class, 69, F, self.doc, view, true);
}
/// Tells the kernel a tile's size in pixels and twips.
pub fn set_client_zoom(&self, pixels: i32, twips: i32) {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, i32, i32, i32, i32);
call!(class, 23, F, self.doc, pixels, pixels, twips, twips);
}
/// Tells the kernel the area the client shows, in twips.
pub fn set_visible_area(&self, x: i32, y: i32, width: i32, height: i32) {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, i32, i32, i32, i32);
call!(class, 24, F, self.doc, x, y, width, height);
}
/// The selection as plain text; Calc joins cells by tabs.
pub fn selection_text(&self) -> io::Result<String> {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, *const c_char, *mut *mut c_char) -> *mut c_char;
let mime = cstring("text/plain;charset=utf-8")?;
let mut used: *mut c_char = std::ptr::null_mut();
let text = owned(call!(class, 18, F, self.doc, mime.as_ptr(), &mut used));
owned(used);
text.ok_or_else(|| io::Error::other("the kernel gave no selection text"))
}
/// Completes the function name being typed (TABLE-03).
pub fn complete_function(&self, name: &str) -> io::Result<()> {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, *const c_char);
let name = cstring(name)?;
call!(class, 59, F, self.doc, name.as_ptr());
Ok(())
}
/// How many parts: sheets or slides.
pub fn parts(&self) -> i32 {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc) -> i32;
call!(class, 3, F, self.doc)
}
/// The part the view shows, from 0.
pub fn part(&self) -> i32 {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc) -> i32;
call!(class, 5, F, self.doc)
}
/// Shows part `part`.
pub fn set_part(&self, part: i32) {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, i32);
call!(class, 6, F, self.doc, part);
}
/// The name of part `part`.
pub fn part_name(&self, part: i32) -> io::Result<String> {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, i32) -> *mut c_char;
owned(call!(class, 7, F, self.doc, part))
.ok_or_else(|| io::Error::other(format!("part {part} has no name")))
}
/// LOK's JSON about part `part`: visible, protected.
pub fn part_info(&self, part: i32) -> io::Result<String> {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc, i32) -> *mut c_char;
owned(call!(class, 41, F, self.doc, part))
.ok_or_else(|| io::Error::other(format!("part {part} has no info")))
}
/// Page rectangles as "x, y, w, h; ...".
pub fn page_rects(&self) -> io::Result<String> {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc) -> *mut c_char;
let raw = call!(class, 4, F, self.doc);
if raw.is_null() {
return Ok(String::new());
}
let text = unsafe { std::ffi::CStr::from_ptr(raw) }
.to_string_lossy()
.into_owned();
unsafe { free(raw.cast()) };
Ok(text)
}
/// The focused paragraph as LOK's a11y JSON.
pub fn a11y_paragraph(&self) -> io::Result<String> {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc) -> *mut c_char;
let raw = call!(class, 70, F, self.doc);
if raw.is_null() {
return Err(io::Error::other("no focused paragraph"));
}
let text = unsafe { std::ffi::CStr::from_ptr(raw) }
.to_string_lossy()
.into_owned();
unsafe { free(raw.cast()) };
Ok(text)
}
/// Puts one `mime` entry on the view clipboard (LOK-06).
pub fn set_clipboard(&self, mime: &str, data: &[u8]) -> io::Result<()> {
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(
*mut Doc,
usize,
*const *const c_char,
*const usize,
*const *const c_char,
) -> i32;
let mime = cstring(mime)?;
let ok = call!(
class,
53,
F,
self.doc,
1,
&mime.as_ptr(),
&data.len(),
&data.as_ptr().cast::<c_char>()
);
if ok == 0 {
return Err(io::Error::other("setClipboard failed"));
}
Ok(())
}
/// Saves to `url` as `format`; false means LOK reported failure.
pub fn save_as(&self, url: &str, format: &str, options: &str) -> io::Result<()> {
let class = unsafe { (*self.doc).class };
let url_c = cstring(url)?;
let format_c = cstring(format)?;
let options_c = cstring(options)?;
type F = unsafe extern "C" fn(*mut Doc, *const i8, *const i8, *const i8) -> i32;
let ok = call!(
class,
1,
F,
self.doc,
url_c.as_ptr(),
format_c.as_ptr(),
options_c.as_ptr()
);
if ok == 0 {
return Err(io::Error::other(format!("saveAs failed for {url}")));
}
Ok(())
}
}
impl Drop for Document {
fn drop(&mut self) {
if self.doc.is_null() {
return;
}
let class = unsafe { (*self.doc).class };
type F = unsafe extern "C" fn(*mut Doc);
call!(class, 0, F, self.doc);
}
}
/// A document's native handle, for callbacks that cannot borrow it.
#[derive(Clone, Copy)]
pub struct Raw(*mut Doc);
impl Document {
pub fn raw(&self) -> Raw {
Raw(self.doc)
}
}
/// Closes kernel window `window` of document `doc` (LOK-08).
pub fn close_window(doc: Raw, window: u32) {
let doc = doc.0;
let class = unsafe { (*doc).class };
type F = unsafe extern "C" fn(*mut Doc, u32, i32, *const c_char);
call!(class, 36, F, doc, window, 0, std::ptr::null());
}
/// Takes a string LOK allocated; None for null.
fn owned(raw: *mut c_char) -> Option<String> {
if raw.is_null() {
return None;
}
let text = unsafe { std::ffi::CStr::from_ptr(raw) }
.to_string_lossy()
.into_owned();
unsafe { free(raw.cast()) };
Some(text)
}