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]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"abletk-macros",
|
"abletk-macros",
|
||||||
|
"abletk-cairo",
|
||||||
"abletk-common",
|
"abletk-common",
|
||||||
"abletk-direct2d",
|
"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]
|
[target.'cfg(windows)'.dependencies]
|
||||||
abletk-direct2d = { path = "../abletk-direct2d" }
|
abletk-direct2d = { path = "../abletk-direct2d" }
|
||||||
windows = { version = "0.36.1", features = ["Win32_Graphics_Direct2D_Common"] }
|
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)]
|
#[cfg(windows)]
|
||||||
use abletk_direct2d::brush::Brush as RawBrush;
|
use abletk_direct2d::brush::Brush as RawBrush;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use abletk_cairo::brush::Brush as RawBrush;
|
||||||
|
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
@ -16,6 +19,7 @@ pub enum Brush {
|
||||||
Solid(Color),
|
Solid(Color),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
impl From<Brush> for RawBrush {
|
impl From<Brush> for RawBrush {
|
||||||
fn from(brush: Brush) -> Self {
|
fn from(brush: Brush) -> Self {
|
||||||
match brush {
|
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)]
|
#[cfg(windows)]
|
||||||
impl From<Color> for D2D1_COLOR_F {
|
impl From<Color> for D2D1_COLOR_F {
|
||||||
fn from(color: Color) -> Self {
|
fn from(color: Color) -> Self {
|
||||||
|
|
|
@ -18,6 +18,9 @@ use rect::Rect;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use abletk_direct2d as backend;
|
use abletk_direct2d as backend;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use abletk_cairo as backend;
|
||||||
|
|
||||||
pub struct Renderer {
|
pub struct Renderer {
|
||||||
renderer: backend::Renderer,
|
renderer: backend::Renderer,
|
||||||
x: u32,
|
x: u32,
|
||||||
|
@ -27,7 +30,10 @@ pub struct Renderer {
|
||||||
impl Renderer {
|
impl Renderer {
|
||||||
pub fn new(handle: RawWindowHandle) -> Self {
|
pub fn new(handle: RawWindowHandle) -> Self {
|
||||||
let handle = match handle {
|
let handle = match handle {
|
||||||
|
#[cfg(windows)]
|
||||||
RawWindowHandle::Win32(handle) => handle,
|
RawWindowHandle::Win32(handle) => handle,
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
RawWindowHandle::Xlib(handle) => handle,
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -50,8 +56,8 @@ impl Renderer {
|
||||||
self.renderer.draw_rect(Rect::new(
|
self.renderer.draw_rect(Rect::new(
|
||||||
self.x as f32,
|
self.x as f32,
|
||||||
self.y as f32,
|
self.y as f32,
|
||||||
(self.x + width) as f32,
|
width as f32,
|
||||||
(self.y + height) as f32,
|
height as f32,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,8 +84,8 @@ impl Renderer {
|
||||||
self.renderer.fill_rect(Rect::new(
|
self.renderer.fill_rect(Rect::new(
|
||||||
self.x as f32,
|
self.x as f32,
|
||||||
self.y as f32,
|
self.y as f32,
|
||||||
(self.x + width) as f32,
|
width as f32,
|
||||||
(self.y + height) as f32,
|
height as f32,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,10 +107,10 @@ impl Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_brush(&mut self, brush: Brush) {
|
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()
|
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)]
|
#[cfg(windows)]
|
||||||
impl From<Rect> for RECT {
|
impl From<Rect> for RECT {
|
||||||
fn from(rect: Rect) -> Self {
|
fn from(rect: Rect) -> Self {
|
||||||
|
|
|
@ -65,9 +65,9 @@ impl Renderer {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.target.as_ref().unwrap()
|
self.target.as_ref().unwrap()
|
||||||
.DrawRectangle(
|
.DrawRectangle(
|
||||||
&rect.into(),
|
&xywh_to_ltrb_rect(rect.into()),
|
||||||
self.brush.as_ref().unwrap(),
|
self.brush.as_ref().unwrap(),
|
||||||
1.0,
|
1.0, // fixme
|
||||||
&self.stroke_style)
|
&self.stroke_style)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ impl Renderer {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.target.as_ref().unwrap()
|
self.target.as_ref().unwrap()
|
||||||
.FillRectangle(
|
.FillRectangle(
|
||||||
&rect.into(),
|
&xywh_to_ltrb_rect(rect.into()),
|
||||||
self.brush.as_ref().unwrap())
|
self.brush.as_ref().unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,15 +128,15 @@ impl Renderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_brush(&mut self, brush: Brush) {
|
pub fn set_brush<B: Into<Brush>>(&mut self, brush: B) {
|
||||||
self.brush = Some(brush.to_brush(self.target.as_ref().unwrap()))
|
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 {
|
let size = unsafe {
|
||||||
self.target.as_ref().unwrap().GetSize()
|
self.target.as_ref().unwrap().GetSize()
|
||||||
};
|
};
|
||||||
(size.width, size.height)
|
(size.width as u32, size.height as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_target(&mut self) {
|
fn setup_target(&mut self) {
|
||||||
|
@ -229,3 +229,14 @@ fn create_text_format(factory: &IDWriteFactory, fonts: &IDWriteFontCollection)
|
||||||
format
|
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(Column::new()
|
||||||
.add(Label::new("World!"))
|
.add(Label::new("World!"))
|
||||||
.add(Label::new("AbleTK!")))
|
.add(Label::new("AbleTK!")))
|
||||||
.add(Label::new("this is a label!")
|
.add(Label::new("this is a label! jjjjyyy")
|
||||||
.bg_color(rgb!(0xFF0000FF)))
|
.bg_color(rgb!(0xFF0000FF)))
|
||||||
.padding_left(10))
|
.padding_left(10))
|
||||||
.on_event(WindowEvent::Closed, |_, window| {
|
.on_event(WindowEvent::Closed, |_, window| {
|
||||||
|
|
Loading…
Reference in a new issue