Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,18 @@ objc2-app-kit = { version = "0.3.2", default-features = false, features = [
] }

[workspace]
members = ["examples/cursors", "examples/open_parented", "examples/open_window", "examples/plugin_clack", "examples/render_femtovg", "examples/render_wgpu", "examples/plugin_clack_femtovg", "examples/test-frame-pacing", "examples/external-wakeup"]
members = [
"examples/cursors",
"examples/open_parented",
"examples/open_window",
"examples/plugin_clack",
"examples/render_femtovg",
"examples/render_wgpu",
"examples/plugin_clack_femtovg",
"examples/test-frame-pacing",
"examples/external-wakeup",
"tests/partial-rendering"
]

[lints.clippy]
missing-safety-doc = "allow"
Expand Down
50 changes: 50 additions & 0 deletions src/damage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use super::*;
use crate::dpi::{PhysicalPosition, PhysicalSize};
use std::fmt::{Debug, Formatter};

/// Describes the area(s) of a window that have been damaged and need to be redrawn.
#[non_exhaustive]
#[derive(Debug, Copy, Clone)]
pub enum DamageArea<'a> {
/// The full window needs to be redrawn.
FullWindow,
/// Damages a single, specific [rectangle](DamageRect).
Rect(DamageRect),
/// Damages multiple, potentially overlapping [rectangles](DamageRect).
Rects(&'a [DamageRect]),
}

/// A damaged rectangle.
#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct DamageRect {
inner: platform::DamageRect,
}

impl DamageRect {
/// Returns the position of the top-left corner of the damaged rectangle, in physical pixels.
#[inline]
pub fn position(&self) -> PhysicalPosition<u32> {
self.inner.position()
}

/// Returns the size of the damaged rectangle, in physical pixels.
#[inline]
pub fn size(&self) -> PhysicalSize<u32> {
self.inner.size()
}
}

impl From<platform::DamageRect> for DamageRect {
#[inline]
fn from(value: platform::DamageRect) -> Self {
DamageRect { inner: value }
}
}

impl Debug for DamageRect {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
20 changes: 7 additions & 13 deletions src/handler.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
use super::*;
use crate::platform::Result;

#[non_exhaustive]
pub enum DamageArea {
FullWindow,
// Later: single rect, perhaps list of rects
}
use crate::platform::PlatformError;

pub trait WindowHandler: 'static {
/// Requests the handler to draw a new frame immediately.
Expand Down Expand Up @@ -43,7 +37,7 @@ pub trait WindowHandler: 'static {
/// [`draw`]: WindowHandler::draw
/// [`damage`]: WindowHandler::damage
/// [polling]: WindowHandler::poll
fn draw(&self) -> core::result::Result<(), HandlerError>;
fn draw(&self) -> Result<(), HandlerError>;

/// Notifies the handler that a given [`area`] of the window has been damaged by the platform
/// and needs to be redrawn.
Expand Down Expand Up @@ -111,27 +105,27 @@ pub trait WindowHandler: 'static {
///
/// It will also attempt to resize the underlying platform window and parent window back to the
/// previous size, but this is only a best-effort attempt since those operations can also fail.
fn resized(&self, new_size: WindowSize) -> core::result::Result<(), HandlerError>;
fn resized(&self, new_size: WindowSize) -> Result<(), HandlerError>;
fn on_event(&self, event: Event) -> EventStatus;
}

type DynBuilderResult = core::result::Result<Box<dyn WindowHandler>, HandlerError>;
type DynBuilderResult = Result<Box<dyn WindowHandler>, HandlerError>;

pub struct WindowHandlerBuilder {
inner: Box<dyn FnOnce(WindowContext) -> DynBuilderResult + Send + 'static>,
}

impl WindowHandlerBuilder {
pub fn new<H: WindowHandler>(
f: impl FnOnce(WindowContext) -> core::result::Result<H, HandlerError> + Send + 'static,
f: impl FnOnce(WindowContext) -> Result<H, HandlerError> + Send + 'static,
) -> WindowHandlerBuilder {
Self { inner: Box::new(|c| Ok(Box::new(f(c)?))) }
}

pub fn build(self, ctx: WindowContext) -> Result<Box<dyn WindowHandler>> {
pub fn build(self, ctx: WindowContext) -> Result<Box<dyn WindowHandler>, PlatformError> {
match (self.inner)(ctx) {
Ok(handle) => Ok(handle),
Err(e) => Err(platform::PlatformError::Handler(e)),
Err(e) => Err(PlatformError::Handler(e)),
}
}
}
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod clipboard;
mod context;
mod damage;
pub mod dpi;
mod error;
mod event;
Expand All @@ -19,9 +20,10 @@ pub mod gl;

pub use clipboard::*;
pub use context::{PlatformHandle, WindowContext};
pub use damage::{DamageArea, DamageRect};
pub use error::*;
pub use event::*;
pub use handler::{DamageArea, WindowHandler};
pub use handler::WindowHandler;
pub use mouse_cursor::MouseCursor;
pub use settings::*;
pub use waker::WindowWaker;
Expand Down
26 changes: 26 additions & 0 deletions src/platform/macos/damage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize};
use objc2_foundation::NSRect;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct DamageRect {
pos: PhysicalPosition<u32>,
size: PhysicalSize<u32>,
}

impl DamageRect {
pub(crate) fn from_rect(rect: NSRect, scale_factor: f64) -> Self {
let pos = LogicalPosition { x: rect.origin.x, y: rect.origin.y };
let size = LogicalSize { width: rect.size.width, height: rect.size.height };

Self { pos: pos.to_physical(scale_factor), size: size.to_physical(scale_factor) }
}
#[inline]
pub fn position(&self) -> PhysicalPosition<u32> {
self.pos
}

#[inline]
pub fn size(&self) -> PhysicalSize<u32> {
self.size
}
}
2 changes: 2 additions & 0 deletions src/platform/macos/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod context;
mod cursor;
mod damage;
mod error;
mod keyboard;
mod view;
Expand All @@ -9,6 +10,7 @@ mod window;
use crate::platform::macos::view::BaseviewView;
use crate::wrappers::appkit::{extract_raw_window_handle, ParentWindowHandleError, View};
pub use context::WindowContext;
pub use damage::DamageRect;
use dispatch2::MainThreadBound;
pub use error::PlatformError;
use objc2::__framework_prelude::Retained;
Expand Down
31 changes: 29 additions & 2 deletions src/platform/macos/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use crate::window::WindowInitializer;
use crate::wrappers::appkit::*;
use crate::MouseEvent::{ButtonPressed, ButtonReleased};
use crate::{
DropData, DropEffect, Event, EventStatus, HandlerError, MouseButton, MouseEvent, ScrollDelta,
WindowEvent, WindowHandler, WindowSize,
DamageArea, DropData, DropEffect, Event, EventStatus, HandlerError, MouseButton, MouseEvent,
ScrollDelta, WindowEvent, WindowHandler, WindowSize,
};
use objc2::__framework_prelude::Retained;
use objc2::rc::Weak;
Expand All @@ -26,6 +26,7 @@ use objc2_app_kit::{
use objc2_foundation::{NSArray, NSNotification, NSPoint, NSPointInRect, NSRect, NSSize, NSString};
use objc2_quartz_core::CADisplayLink;
use std::cell::{Cell, OnceCell, RefCell};
use std::ptr::null;
use std::rc::Rc;

pub enum ViewParentingType {
Expand Down Expand Up @@ -385,6 +386,32 @@ impl ViewImpl for BaseviewView {
}

fn draw_rect(this: ViewRef<Self>, _rect: NSRect) {
this.window_handler.use_handler(|h| {
let mut rects_ptr = null();
let mut rects_count = 0;
unsafe { this.view.getRectsBeingDrawn_count(&mut rects_ptr, &mut rects_count) };

let rects_count: usize = rects_count.try_into().unwrap_or(0);
let rects;

let damage_area = if rects_count == 0 {
DamageArea::FullWindow
} else {
let scale_factor = this.view.backing_scale_factor();

let rects_raw = unsafe { core::slice::from_raw_parts(rects_ptr, rects_count) };

rects = rects_raw
.iter()
.map(|r| DamageRect::from_rect(*r, scale_factor).into())
.collect::<Vec<_>>();

DamageArea::Rects(&rects)
};

h.damage(damage_area);
});

this.set_next_frame_needed(true);
}

Expand Down
2 changes: 2 additions & 0 deletions src/platform/win/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod window_state;

use crate::wrappers::win32::h_instance::HInstance;
use crate::wrappers::win32::window::HWnd;
use crate::wrappers::win32::Rect;
pub(crate) use dpi::DpiScalingStrategy;
pub use error::{PlatformError, Result};
use raw_window_handle::{
Expand All @@ -25,6 +26,7 @@ pub use window::*;
pub mod gl;

pub type WindowContext = Rc<window_state::WindowState>;
pub type DamageRect = Rect;

#[derive(Clone)]
pub struct PlatformHandle {
Expand Down
8 changes: 7 additions & 1 deletion src/platform/win/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use windows_sys::Win32::{
};

use crate::dpi::{PhysicalPosition, PhysicalSize, Size};
use crate::{warn, EventStatus, HandlerError, WindowHandler};
use crate::{warn, DamageArea, EventStatus, HandlerError, WindowHandler};
use std::cell::{Cell, OnceCell};
use std::num::NonZeroU32;
use windows_sys::Win32::Foundation::POINT;
Expand Down Expand Up @@ -299,6 +299,11 @@ impl BaseviewWindow {
self.host.request_resize(new_size)
}

pub(crate) fn notify_damage(&self, damage: DamageArea) {
let Some(handler) = self.handler.get() else { return };
handler.damage(damage);
}

pub(crate) fn handle_draw(&self) {
let Some(handler) = self.handler.get() else { return };

Expand Down Expand Up @@ -544,6 +549,7 @@ unsafe fn wnd_proc_inner(
}
WM_PAINT => {
if let Some(rect) = window.get_update_rect() {
window_bv.notify_damage(DamageArea::Rect(rect.into()));
window_bv.handle_draw();
window.validate_rect(rect);
}
Expand Down
17 changes: 15 additions & 2 deletions src/platform/x11/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use crate::platform::x11::error::FatalError;
use crate::platform::x11::window_thread::{
HostCallback, WindowThreadRequest, WindowThreadResponseMessage,
};
use crate::warn;
use crate::wrappers::xkbcommon::XkbcommonState;
use crate::{warn, DamageArea};
use crate::{Event, MouseButton, MouseEvent, ScrollDelta, WindowEvent, WindowHandler, WindowSize};
use calloop::generic::Generic;
use calloop::timer::{TimeoutAction, Timer};
Expand Down Expand Up @@ -482,7 +482,20 @@ impl EventLoop {
}

XEvent::Expose(e) if e.window == self.window.raw_id() => {
self.window.present_notify_requested.set(true)
if e.count == 0 {
self.window.present_notify_requested.set(true);
}

let current_window_size = self.new_size.unwrap_or_else(|| self.window.get_size());

let damage_rect = DamageRect::new(&e);
let area = if damage_rect.fully_covers(current_window_size) {
DamageArea::FullWindow
} else {
DamageArea::Rect(damage_rect.into())
};

self.handler.damage(area);
}

////
Expand Down
35 changes: 35 additions & 0 deletions src/platform/x11/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
mod xcb_connection;

use dpi::{PhysicalPosition, PhysicalSize};
use raw_window_handle::{
DisplayHandle, HandleError, HasWindowHandle, RawWindowHandle, XcbWindowHandle,
};
use std::fmt::{Display, Formatter};
use std::num::{NonZero, NonZeroU32, TryFromIntError};
use std::rc::Rc;
use std::sync::Arc;
use x11rb::protocol::xproto::ExposeEvent;
pub(crate) use xcb_connection::X11Connection;

mod window;
Expand Down Expand Up @@ -124,3 +126,36 @@ impl Display for ParentWindowHandleError {
pub fn assume_standalone_in_process() {
// No-op on X11
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct DamageRect {
pos: PhysicalPosition<u16>,
size: PhysicalSize<u16>,
}

impl DamageRect {
#[inline]
pub fn new(event: &ExposeEvent) -> Self {
Self {
pos: PhysicalPosition::new(event.x, event.y),
size: PhysicalSize::new(event.width, event.height),
}
}

#[inline]
pub fn position(&self) -> PhysicalPosition<u32> {
PhysicalPosition { x: self.pos.x.into(), y: self.pos.y.into() }
}

#[inline]
pub fn size(&self) -> PhysicalSize<u32> {
PhysicalSize { height: self.size.height.into(), width: self.size.width.into() }
}

pub fn fully_covers(&self, window_size: PhysicalSize<u16>) -> bool {
self.pos.x == 0
&& self.pos.y == 0
&& window_size.width <= self.size.width
&& window_size.height <= self.size.height
}
}
2 changes: 1 addition & 1 deletion src/wrappers/egl/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl EglContext {
impl Drop for EglContext {
fn drop(&mut self) {
if let Err(e) = unsafe { self.display.egl().destroy_context(self) } {
crate::warn!("Failed to destroy EGL context: {e}");
crate::warn!("Failed to destroy EGL context: {}", e);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/wrappers/egl/surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ impl EglSurface {
impl Drop for EglSurface {
fn drop(&mut self) {
if let Err(e) = unsafe { self.display.egl().destroy_surface(self) } {
crate::warn!("Failed to destroy EGL surface: {e}");
crate::warn!("Failed to destroy EGL surface: {}", e);
}
}
}
Expand Down
Loading
Loading