new cairo backend! for now only tested on X11 on Linux
also includes a couple small changes everywhere
This commit is contained in:
parent
9b3969c6ad
commit
c41c2b9c69
|
@ -16,6 +16,7 @@ raw-window-handle = "0.4.3"
|
|||
[workspace]
|
||||
members = [
|
||||
"abletk-macros",
|
||||
"abletk-cairo",
|
||||
"abletk-common",
|
||||
"abletk-direct2d",
|
||||
]
|
||||
|
|
11
abletk-cairo/Cargo.toml
Normal file
11
abletk-cairo/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "abletk-cairo"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
raw-window-handle = "0.4.3"
|
||||
cairo-sys-rs = { version = "0.15.1", features = ["xlib"] }
|
||||
x11 = "2.19.1"
|
11
abletk-cairo/src/brush.rs
Normal file
11
abletk-cairo/src/brush.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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 enum Brush {
|
||||
Solid(f64, f64, f64, f64),
|
||||
}
|
211
abletk-cairo/src/lib.rs
Normal file
211
abletk-cairo/src/lib.rs
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* 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 {
|
||||
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(
|
||||
self.handle.display as _,
|
||||
self.handle.window.try_into().unwrap(),
|
||||
&mut root,
|
||||
&mut x,
|
||||
&mut y,
|
||||
&mut width,
|
||||
&mut height,
|
||||
&mut border_width,
|
||||
&mut depth,
|
||||
), 0);
|
||||
(width, height)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,3 +11,7 @@ raw-window-handle = "0.4.3"
|
|||
[target.'cfg(windows)'.dependencies]
|
||||
abletk-direct2d = { path = "../abletk-direct2d" }
|
||||
windows = { version = "0.36.1", features = ["Win32_Graphics_Direct2D_Common"] }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
abletk-cairo = { path = "../abletk-cairo" }
|
||||
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
#[cfg(windows)]
|
||||
use abletk_direct2d::brush::Brush as RawBrush;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use abletk_cairo::brush::Brush as RawBrush;
|
||||
|
||||
use crate::color::Color;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -16,6 +19,7 @@ pub enum Brush {
|
|||
Solid(Color),
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl From<Brush> for RawBrush {
|
||||
fn from(brush: Brush) -> Self {
|
||||
match brush {
|
||||
|
@ -23,3 +27,17 @@ impl From<Brush> for RawBrush {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
impl From<Brush> for RawBrush {
|
||||
fn from(brush: Brush) -> Self {
|
||||
match brush {
|
||||
Brush::Solid(color) => RawBrush::Solid(
|
||||
color.0.into(),
|
||||
color.1.into(),
|
||||
color.2.into(),
|
||||
color.3.into(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,12 @@ impl Color {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Color> for (f64, f64, f64, f64) {
|
||||
fn from(color: Color) -> Self {
|
||||
(color.0.into(), color.1.into(), color.2.into(), color.3.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl From<Color> for D2D1_COLOR_F {
|
||||
fn from(color: Color) -> Self {
|
||||
|
|
|
@ -18,6 +18,9 @@ use rect::Rect;
|
|||
#[cfg(windows)]
|
||||
use abletk_direct2d as backend;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use abletk_cairo as backend;
|
||||
|
||||
pub struct Renderer {
|
||||
renderer: backend::Renderer,
|
||||
x: u32,
|
||||
|
@ -27,10 +30,13 @@ pub struct Renderer {
|
|||
impl Renderer {
|
||||
pub fn new(handle: RawWindowHandle) -> Self {
|
||||
let handle = match handle {
|
||||
#[cfg(windows)]
|
||||
RawWindowHandle::Win32(handle) => handle,
|
||||
#[cfg(target_os = "linux")]
|
||||
RawWindowHandle::Xlib(handle) => handle,
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
|
||||
Self {
|
||||
renderer: backend::Renderer::new(handle),
|
||||
x: 0,
|
||||
|
@ -50,8 +56,8 @@ impl Renderer {
|
|||
self.renderer.draw_rect(Rect::new(
|
||||
self.x as f32,
|
||||
self.y as f32,
|
||||
(self.x + width) as f32,
|
||||
(self.y + height) as f32,
|
||||
width as f32,
|
||||
height as f32,
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -78,8 +84,8 @@ impl Renderer {
|
|||
self.renderer.fill_rect(Rect::new(
|
||||
self.x as f32,
|
||||
self.y as f32,
|
||||
(self.x + width) as f32,
|
||||
(self.y + height) as f32,
|
||||
width as f32,
|
||||
height as f32,
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -101,10 +107,10 @@ impl Renderer {
|
|||
}
|
||||
|
||||
pub fn set_brush(&mut self, brush: Brush) {
|
||||
self.renderer.set_brush(brush.into())
|
||||
self.renderer.set_brush(brush)
|
||||
}
|
||||
|
||||
pub fn size(&self) -> (f32, f32) {
|
||||
pub fn size(&self) -> (u32, u32) {
|
||||
self.renderer.size()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,12 @@ impl Rect {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Rect> for (f64, f64, f64, f64) {
|
||||
fn from(rect: Rect) -> Self {
|
||||
(rect.x.into(), rect.y.into(), rect.width.into(), rect.height.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl From<Rect> for RECT {
|
||||
fn from(rect: Rect) -> Self {
|
||||
|
|
|
@ -65,9 +65,9 @@ impl Renderer {
|
|||
unsafe {
|
||||
self.target.as_ref().unwrap()
|
||||
.DrawRectangle(
|
||||
&rect.into(),
|
||||
&xywh_to_ltrb_rect(rect.into()),
|
||||
self.brush.as_ref().unwrap(),
|
||||
1.0,
|
||||
1.0, // fixme
|
||||
&self.stroke_style)
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ impl Renderer {
|
|||
unsafe {
|
||||
self.target.as_ref().unwrap()
|
||||
.FillRectangle(
|
||||
&rect.into(),
|
||||
&xywh_to_ltrb_rect(rect.into()),
|
||||
self.brush.as_ref().unwrap())
|
||||
}
|
||||
}
|
||||
|
@ -128,15 +128,15 @@ impl Renderer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_brush(&mut self, brush: Brush) {
|
||||
self.brush = Some(brush.to_brush(self.target.as_ref().unwrap()))
|
||||
pub fn set_brush<B: Into<Brush>>(&mut self, brush: B) {
|
||||
self.brush = Some(brush.into().to_brush(self.target.as_ref().unwrap()))
|
||||
}
|
||||
|
||||
pub fn size(&self) -> (f32, f32) {
|
||||
pub fn size(&self) -> (u32, u32) {
|
||||
let size = unsafe {
|
||||
self.target.as_ref().unwrap().GetSize()
|
||||
};
|
||||
(size.width, size.height)
|
||||
(size.width as u32, size.height as u32)
|
||||
}
|
||||
|
||||
fn setup_target(&mut self) {
|
||||
|
@ -229,3 +229,14 @@ fn create_text_format(factory: &IDWriteFactory, fonts: &IDWriteFontCollection)
|
|||
format
|
||||
}
|
||||
}
|
||||
|
||||
/// D2D sometimes expects a rect to have LTRB (left, top, right, bottom) values
|
||||
/// but abletk-common uses XYWH (x, y, width, height) values.
|
||||
fn xywh_to_ltrb_rect(xywh: D2D_RECT_F) -> D2D_RECT_F {
|
||||
D2D_RECT_F {
|
||||
left: xywh.left,
|
||||
top: xywh.top,
|
||||
right: xywh.left + xywh.right,
|
||||
bottom: xywh.top + xywh.bottom,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ fn launch() -> _ {
|
|||
.add(Column::new()
|
||||
.add(Label::new("World!"))
|
||||
.add(Label::new("AbleTK!")))
|
||||
.add(Label::new("this is a label!")
|
||||
.add(Label::new("this is a label! jjjjyyy")
|
||||
.bg_color(rgb!(0xFF0000FF)))
|
||||
.padding_left(10))
|
||||
.on_event(WindowEvent::Closed, |_, window| {
|
||||
|
|
Loading…
Reference in a new issue