Skip to content

Commit 887de64

Browse files
committed
Add more X11 docs.
1 parent c70831a commit 887de64

File tree

1 file changed

+49
-24
lines changed

1 file changed

+49
-24
lines changed

druid-shell/src/platform/x11/window.rs

+49-24
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use std::cell::RefCell;
1919
use std::convert::TryInto;
2020
use std::rc::{Rc, Weak};
2121

22-
use cairo::XCBSurface;
22+
use cairo::{XCBConnection, XCBDrawable, XCBSurface, XCBVisualType};
2323
use xcb::{
2424
Atom, Visualtype, ATOM_ATOM, ATOM_STRING, ATOM_WM_NAME, CONFIG_WINDOW_STACK_MODE,
2525
COPY_FROM_PARENT, CURRENT_TIME, CW_BACK_PIXEL, CW_EVENT_MASK, EVENT_MASK_BUTTON_PRESS,
@@ -89,9 +89,16 @@ impl WindowBuilder {
8989
// TODO(x11/menus): implement WindowBuilder::set_menu (currently a no-op)
9090
}
9191

92-
fn atoms(&self, id: u32) -> Result<WindowAtoms, Error> {
92+
/// Registers and returns all the atoms that the window will need.
93+
fn atoms(&self, window_id: u32) -> Result<WindowAtoms, Error> {
9394
let conn = self.app.connection();
9495

96+
// WM_PROTOCOLS
97+
//
98+
// List of atoms that identify the communications protocols between
99+
// the client and window manager in which the client is willing to participate.
100+
//
101+
// https://www.x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html#wm_protocols_property
95102
let wm_protocols = match xcb::intern_atom(conn, false, "WM_PROTOCOLS").get_reply() {
96103
Ok(reply) => reply.atom(),
97104
Err(err) => {
@@ -102,6 +109,15 @@ impl WindowBuilder {
102109
}
103110
};
104111

112+
// WM_DELETE_WINDOW
113+
//
114+
// Including this atom in the WM_PROTOCOLS property on each window makes sure that
115+
// if the window manager respects WM_DELETE_WINDOW it will send us the event.
116+
//
117+
// The WM_DELETE_WINDOW event is sent when there is a request to close the window.
118+
// Registering for but ignoring this event means that the window will remain open.
119+
//
120+
// https://www.x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html#window_deletion
105121
let wm_delete_window = match xcb::intern_atom(conn, false, "WM_DELETE_WINDOW").get_reply() {
106122
Ok(reply) => reply.atom(),
107123
Err(err) => {
@@ -112,12 +128,13 @@ impl WindowBuilder {
112128
}
113129
};
114130

131+
// Replace the window's WM_PROTOCOLS with the following.
115132
let protocols = [wm_delete_window];
116133
// TODO(x11/errors): Check the response for errors?
117134
xcb::change_property(
118135
conn,
119136
PROP_MODE_REPLACE as u8,
120-
id,
137+
window_id,
121138
wm_protocols,
122139
ATOM_ATOM,
123140
32,
@@ -130,34 +147,36 @@ impl WindowBuilder {
130147
})
131148
}
132149

133-
fn cairo_context(
150+
/// Create a new cairo `Context`.
151+
fn create_cairo_context(
134152
&self,
135-
id: u32,
153+
window_id: u32,
136154
visual_type: &mut Visualtype,
137155
) -> Result<RefCell<cairo::Context>, Error> {
138-
// Create a draw surface
139156
let conn = self.app.connection();
140157
let cairo_xcb_connection = unsafe {
141-
cairo::XCBConnection::from_raw_none(
142-
conn.get_raw_conn() as *mut cairo_sys::xcb_connection_t
143-
)
158+
XCBConnection::from_raw_none(conn.get_raw_conn() as *mut cairo_sys::xcb_connection_t)
144159
};
145-
let cairo_drawable = cairo::XCBDrawable(id);
160+
let cairo_drawable = XCBDrawable(window_id);
146161
let cairo_visual_type = unsafe {
147-
cairo::XCBVisualType::from_raw_none(
162+
XCBVisualType::from_raw_none(
148163
&mut visual_type.base as *mut _ as *mut cairo_sys::xcb_visualtype_t,
149164
)
150165
};
151-
// TODO(x11/errors): Don't unwrap
152166
let cairo_surface = XCBSurface::create(
153167
&cairo_xcb_connection,
154168
&cairo_drawable,
155169
&cairo_visual_type,
156170
self.size.width as i32,
157171
self.size.height as i32,
158-
)
159-
.expect("couldn't create a cairo surface");
160-
Ok(RefCell::new(cairo::Context::new(&cairo_surface)))
172+
);
173+
match cairo_surface {
174+
Ok(cairo_surface) => Ok(RefCell::new(cairo::Context::new(&cairo_surface))),
175+
Err(err) => Err(Error::Generic(format!(
176+
"Failed to create cairo surface: {}",
177+
err
178+
))),
179+
}
161180
}
162181

163182
// TODO(x11/menus): make menus if requested
@@ -219,7 +238,7 @@ impl WindowBuilder {
219238

220239
// TODO(x11/errors): Should do proper cleanup (window destruction etc) in case of error
221240
let atoms = self.atoms(id)?;
222-
let cairo_context = self.cairo_context(id, &mut visual_type)?;
241+
let cairo_context = self.create_cairo_context(id, &mut visual_type)?;
223242
// Figure out the refresh rate of the current screen
224243
let refresh_rate = util::refresh_rate(conn, id);
225244
let state = RefCell::new(WindowState { size: self.size });
@@ -354,7 +373,12 @@ impl Window {
354373
Ok(())
355374
}
356375

357-
fn request_redraw(&self, rect: Rect, flush: bool) {
376+
/// Tell the X server to mark the specified `rect` as needing redraw.
377+
///
378+
/// ### Connection
379+
///
380+
/// Does not flush the connection.
381+
fn request_redraw(&self, rect: Rect) {
358382
// See: http://rtbo.github.io/rust-xcb/xcb/ffi/xproto/struct.xcb_expose_event_t.html
359383
let expose_event = xcb::ExposeEvent::new(
360384
self.id,
@@ -371,13 +395,12 @@ impl Window {
371395
EVENT_MASK_EXPOSURE,
372396
&expose_event,
373397
);
374-
if flush {
375-
self.app.connection().flush();
376-
}
377398
}
378399

379400
fn render(&self, invalid_rect: Rect) -> Result<(), Error> {
380-
// TODO(x11/errors): this function should return a proper error
401+
// TODO(x11/errors): this function should return a an error
402+
// instead of panicking or logging if the error isn't recoverable.
403+
381404
// Figure out the window's current size
382405
let geometry_cookie = xcb::get_geometry(self.app.connection(), self.id);
383406
let reply = geometry_cookie.get_reply().unwrap();
@@ -412,7 +435,7 @@ impl Window {
412435
let sleep_amount_ms = (1000.0 / self.refresh_rate.unwrap()) as u64;
413436
std::thread::sleep(std::time::Duration::from_millis(sleep_amount_ms));
414437

415-
self.request_redraw(size.to_rect(), false);
438+
self.request_redraw(size.to_rect());
416439
}
417440
self.app.connection().flush();
418441
Ok(())
@@ -457,13 +480,14 @@ impl Window {
457480

458481
fn invalidate(&self) {
459482
match self.size() {
460-
Ok(size) => self.request_redraw(size.to_rect(), true),
483+
Ok(size) => self.invalidate_rect(size.to_rect()),
461484
Err(err) => log::error!("Window::invalidate - failed to get size: {}", err),
462485
}
463486
}
464487

465488
fn invalidate_rect(&self, rect: Rect) {
466-
self.request_redraw(rect, true);
489+
self.request_redraw(rect);
490+
self.app.connection().flush();
467491
}
468492

469493
fn set_title(&self, title: &str) {
@@ -624,6 +648,7 @@ impl Window {
624648
client_message: &xcb::ClientMessageEvent,
625649
) -> Result<(), Error> {
626650
// https://www.x.org/releases/X11R7.7/doc/libX11/libX11/libX11.html#id2745388
651+
// https://www.x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html#window_deletion
627652
if client_message.type_() == self.atoms.wm_protocols && client_message.format() == 32 {
628653
let protocol = client_message.data().data32()[0];
629654
if protocol == self.atoms.wm_delete_window {

0 commit comments

Comments
 (0)