abletk/abletk-cairo/src/lib.rs

195 lines
5.5 KiB
Rust

/*
* Copyright (C) 2022 Umut İnan Erdoğan <umutinanerdogan@pm.me>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
pub mod brush;
use std::{mem, ffi::CString};
use brush::Brush;
use cairo_sys::*;
use raw_window_handle::XlibHandle;
use x11::xlib::{XDefaultScreen, XDefaultVisual, XGetGeometry};
pub struct Renderer {
handle: XlibHandle,
surface: *mut cairo_surface_t,
ctx: *mut cairo_t,
}
impl Renderer {
pub fn new(handle: XlibHandle) -> Self {
println!("{:#?}", handle);
let (width, height) = unsafe {
let mut root = mem::zeroed();
let mut x = mem::zeroed();
let mut y = mem::zeroed();
let mut width = mem::zeroed();
let mut height = mem::zeroed();
let mut border_width = mem::zeroed();
let mut depth = mem::zeroed();
assert_ne!(XGetGeometry(
handle.display as _,
handle.window.try_into().unwrap(),
&mut root,
&mut x,
&mut y,
&mut width,
&mut height,
&mut border_width,
&mut depth,
), 0);
(width.try_into().unwrap(), height.try_into().unwrap())
};
let surface = unsafe {
cairo_xlib_surface_create(handle.display as _,
handle.window,
XDefaultVisual(handle.display as _, XDefaultScreen(handle.display as _)),
width,
height
)
};
let ctx = unsafe {
cairo_create(surface)
};
Self {
handle,
surface,
ctx,
}
}
pub fn begin_draw(&mut self) {
unsafe {
// fixme
let font = CString::new("Georgia").unwrap();
cairo_select_font_face(
self.ctx,
font.as_ptr(),
FONT_SLANT_NORMAL,
FONT_WEIGHT_NORMAL,
);
cairo_set_font_size(self.ctx, 20.0);
cairo_set_line_width(self.ctx, 1.0)
}
}
pub fn clear<C: Into<(f64, f64, f64, f64)>>(&self, color: C) {
let (r, g, b, a) = color.into();
unsafe {
cairo_save(self.ctx);
cairo_set_source_rgba(self.ctx, r, g, b, a);
cairo_set_operator(self.ctx, OPERATOR_SOURCE);
cairo_paint(self.ctx);
cairo_restore(self.ctx)
}
}
pub fn draw_rect<R: Into<(f64, f64, f64, f64)>>(&self, rect: R) {
let (x, y, width, height) = rect.into();
unsafe {
cairo_rectangle(self.ctx, x, y, width, height);
cairo_stroke(self.ctx)
}
}
pub fn draw_text<R>(&self, text: &str, layout_rect: R)
where
R: Into<(f64, f64, f64, f64)>
{
let (x, y, _, _) = layout_rect.into();
let font_extents = self.font_extents();
let text_extents = self.text_extents(text);
let text = CString::new(text).unwrap();
unsafe {
cairo_move_to(
self.ctx,
x - text_extents.x_bearing,
y
+ font_extents.height
+ font_extents.descent
+ text_extents.y_bearing,
);
cairo_show_text(self.ctx, text.as_ptr())
}
}
pub fn end_draw(&self) {
unsafe {
cairo_surface_flush(self.surface)
}
}
pub fn fill_rect<R: Into<(f64, f64, f64, f64)>>(&self, rect: R) {
let (x, y, width, height) = rect.into();
unsafe {
cairo_rectangle(self.ctx, x, y, width, height);
cairo_fill(self.ctx)
}
}
// fixme: this and abletk-direct2d's get_text_size have small differences
pub fn get_text_size(&self, text: &str) -> (u32, u32) {
let text_extents = self.text_extents(text);
let font_extents = self.font_extents();
let result = (
text_extents.x_advance as u32,
(font_extents.ascent + font_extents.descent) as u32,
);
result
}
pub fn resized(&mut self, width: u32, height: u32) {
unsafe {
cairo_xlib_surface_set_size(
self.surface,
width.try_into().unwrap(),
height.try_into().unwrap(),
)
}
}
pub fn set_brush<B: Into<Brush>>(&mut self, brush: B) {
let brush = brush.into();
match brush {
Brush::Solid(r, g, b, a) => unsafe {
cairo_set_source_rgba(self.ctx, r, g, b, a)
},
}
}
pub fn size(&self) -> (u32, u32) {
unsafe {(
cairo_xlib_surface_get_width(self.surface).try_into().unwrap(),
cairo_xlib_surface_get_height(self.surface).try_into().unwrap(),
)}
}
fn text_extents(&self, text: &str) -> TextExtents {
unsafe {
let mut extents = mem::zeroed();
let text = CString::new(text).unwrap();
cairo_text_extents(
self.ctx,
text.as_ptr() as _,
&mut extents,
);
extents
}
}
fn font_extents(&self) -> FontExtents {
unsafe {
let mut extents = mem::zeroed();
cairo_font_extents(self.ctx, &mut extents);
extents
}
}
}