Skip to content

Commit

Permalink
Window shadows (#43)
Browse files Browse the repository at this point in the history
* Decouple border size from resize range

* Window shadows

* Resolve CI-specific clippy errors
  • Loading branch information
Friz64 authored Nov 27, 2023
1 parent 641824e commit 233b3e2
Show file tree
Hide file tree
Showing 5 changed files with 562 additions and 76 deletions.
5 changes: 3 additions & 2 deletions examples/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ fn main() {
registry_state,
seat_state,
output_state,
compositor_state,
compositor_state: Arc::new(compositor_state),
subcompositor_state: Arc::new(subcompositor_state),
shm_state,
_xdg_shell_state: xdg_shell_state,
Expand Down Expand Up @@ -120,7 +120,7 @@ struct SimpleWindow {
registry_state: RegistryState,
seat_state: SeatState,
output_state: OutputState,
compositor_state: CompositorState,
compositor_state: Arc<CompositorState>,
subcompositor_state: Arc<SubcompositorState>,
shm_state: Shm,
_xdg_shell_state: XdgShell,
Expand Down Expand Up @@ -226,6 +226,7 @@ impl WindowHandler for SimpleWindow {
let mut frame = AdwaitaFrame::new(
&self.window,
&self.shm_state,
self.compositor_state.clone(),
self.subcompositor_state.clone(),
qh.clone(),
FrameConfig::auto(),
Expand Down
81 changes: 55 additions & 26 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use smithay_client_toolkit::reexports::csd_frame::{
CursorIcon, DecorationsFrame, FrameAction, FrameClick, WindowManagerCapabilities, WindowState,
};

use smithay_client_toolkit::compositor::SurfaceData;
use smithay_client_toolkit::compositor::{CompositorState, Region, SurfaceData};
use smithay_client_toolkit::shell::WaylandSurface;
use smithay_client_toolkit::shm::{slot::SlotPool, Shm};
use smithay_client_toolkit::subcompositor::SubcompositorState;
Expand All @@ -28,6 +28,7 @@ mod buttons;
mod config;
mod parts;
mod pointer;
mod shadow;
pub mod theme;
mod title;
mod wl_typed;
Expand All @@ -41,6 +42,7 @@ use buttons::Buttons;
use config::get_button_layout_config;
use parts::DecorationParts;
use pointer::{Location, MouseState};
use shadow::Shadow;
use title::TitleText;
use wl_typed::WlTyped;

Expand All @@ -53,6 +55,8 @@ pub struct AdwaitaFrame<State> {
/// The base surface used to create the window.
base_surface: WlTyped<WlSurface, SurfaceData>,

compositor: Arc<CompositorState>,

/// Subcompositor to create/drop subsurfaces ondemand.
subcompositor: Arc<SubcompositorState>,

Expand Down Expand Up @@ -84,6 +88,7 @@ pub struct AdwaitaFrame<State> {
theme: ColorTheme,
title: Option<String>,
title_text: Option<TitleText>,
shadow: Shadow,
}

impl<State> AdwaitaFrame<State>
Expand All @@ -93,6 +98,7 @@ where
pub fn new(
base_surface: &impl WaylandSurface,
shm: &Shm,
compositor: Arc<CompositorState>,
subcompositor: Arc<SubcompositorState>,
queue_handle: QueueHandle<State>,
frame_config: FrameConfig,
Expand All @@ -113,6 +119,7 @@ where
base_surface,
decorations,
pool,
compositor,
subcompositor,
queue_handle,
dirty: true,
Expand All @@ -126,6 +133,7 @@ where
state: WindowState::empty(),
wm_capabilities: WindowManagerCapabilities::all(),
resizable: true,
shadow: Shadow::default(),
})
}

Expand All @@ -142,7 +150,7 @@ where
x: f64,
y: f64,
) -> Location {
let header_width = decoration.header().width;
let header_width = decoration.header().surface_rect.width;
let side_height = decoration.side_height();

let left_corner_x = BORDER_SIZE + RESIZE_HANDLE_CORNER_SIZE;
Expand Down Expand Up @@ -226,38 +234,46 @@ where
{
let scale = self.scale_factor;

let mut rect = part.surface_rect;
// XXX to perfectly align the visible borders we draw them with
// the header, otherwise rounded corners won't look 'smooth' at the
// start. To achieve that, we enlargen the width of the header by
// 2 * `VISIBLE_BORDER_SIZE`, and move `x` by `VISIBLE_BORDER_SIZE`
// to the left.
let (width, height, pos) = if idx == DecorationParts::HEADER && draw_borders {
(
(part.width + 2 * VISIBLE_BORDER_SIZE) * scale,
part.height * scale,
(part.pos.0 - VISIBLE_BORDER_SIZE as i32, part.pos.1),
)
} else {
(part.width * scale, part.height * scale, part.pos)
};
if idx == DecorationParts::HEADER && draw_borders {
rect.width += 2 * VISIBLE_BORDER_SIZE;
rect.x -= VISIBLE_BORDER_SIZE as i32;
}

rect.width *= scale;
rect.height *= scale;

let (buffer, canvas) = match self.pool.create_buffer(
width as i32,
height as i32,
width as i32 * 4,
rect.width as i32,
rect.height as i32,
rect.width as i32 * 4,
wl_shm::Format::Argb8888,
) {
Ok((buffer, canvas)) => (buffer, canvas),
Err(_) => continue,
};

// Create the pixmap and fill with transparent color.
let mut pixmap = PixmapMut::from_bytes(canvas, width, height)?;
let mut pixmap = PixmapMut::from_bytes(canvas, rect.width, rect.height)?;

// Fill everything with transparent background, since we draw rounded corners and
// do invisible borders to enlarge the input zone.
pixmap.fill(Color::TRANSPARENT);

if !self.state.intersects(WindowState::TILED) {
self.shadow.draw(
&mut pixmap,
scale,
self.state.contains(WindowState::ACTIVATED),
idx,
);
}

match idx {
DecorationParts::HEADER => {
if let Some(title_text) = self.title_text.as_mut() {
Expand All @@ -282,43 +298,43 @@ where

// XXX we do all the match using integral types and then convert to f32 in the
// end to ensure that result is finite.
let rect = match border {
let border_rect = match border {
DecorationParts::LEFT => {
let x = (pos.0.unsigned_abs() * scale) - visible_border_size;
let y = pos.1.unsigned_abs() * scale;
let x = (rect.x.unsigned_abs() * scale) - visible_border_size;
let y = rect.y.unsigned_abs() * scale;
Rect::from_xywh(
x as f32,
y as f32,
visible_border_size as f32,
(height - y) as f32,
(rect.height - y) as f32,
)
}
DecorationParts::RIGHT => {
let y = pos.1.unsigned_abs() * scale;
let y = rect.y.unsigned_abs() * scale;
Rect::from_xywh(
0.,
y as f32,
visible_border_size as f32,
(height - y) as f32,
(rect.height - y) as f32,
)
}
// We draw small visible border only bellow the window surface, no need to
// handle `TOP`.
DecorationParts::BOTTOM => {
let x = (pos.0.unsigned_abs() * scale) - visible_border_size;
let x = (rect.x.unsigned_abs() * scale) - visible_border_size;
Rect::from_xywh(
x as f32,
0.,
(width - 2 * x) as f32,
(rect.width - 2 * x) as f32,
visible_border_size as f32,
)
}
_ => None,
};

// Fill the visible border, if present.
if let Some(rect) = rect {
pixmap.fill_rect(rect, &border_paint, Transform::identity(), None);
if let Some(border_rect) = border_rect {
pixmap.fill_rect(border_rect, &border_paint, Transform::identity(), None);
}
}
};
Expand All @@ -331,7 +347,7 @@ where

part.surface.set_buffer_scale(scale as i32);

part.subsurface.set_position(pos.0, pos.1);
part.subsurface.set_position(rect.x, rect.y);
buffer.attach_to(&part.surface).ok()?;

if part.surface.version() >= 4 {
Expand All @@ -340,6 +356,19 @@ where
part.surface.damage(0, 0, i32::MAX, i32::MAX);
}

if let Some(input_rect) = part.input_rect {
let input_region = Region::new(&*self.compositor).ok()?;
input_region.add(
input_rect.x,
input_rect.y,
input_rect.width as i32,
input_rect.height as i32,
);

part.surface
.set_input_region(Some(input_region.wl_region()));
}

part.surface.commit();
}

Expand Down
Loading

0 comments on commit 233b3e2

Please sign in to comment.