@@ -19,7 +19,7 @@ use std::cell::RefCell;
19
19
use std:: convert:: TryInto ;
20
20
use std:: rc:: { Rc , Weak } ;
21
21
22
- use cairo:: XCBSurface ;
22
+ use cairo:: { XCBConnection , XCBDrawable , XCBSurface , XCBVisualType } ;
23
23
use xcb:: {
24
24
Atom , Visualtype , ATOM_ATOM , ATOM_STRING , ATOM_WM_NAME , CONFIG_WINDOW_STACK_MODE ,
25
25
COPY_FROM_PARENT , CURRENT_TIME , CW_BACK_PIXEL , CW_EVENT_MASK , EVENT_MASK_BUTTON_PRESS ,
@@ -89,9 +89,16 @@ impl WindowBuilder {
89
89
// TODO(x11/menus): implement WindowBuilder::set_menu (currently a no-op)
90
90
}
91
91
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 > {
93
94
let conn = self . app . connection ( ) ;
94
95
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
95
102
let wm_protocols = match xcb:: intern_atom ( conn, false , "WM_PROTOCOLS" ) . get_reply ( ) {
96
103
Ok ( reply) => reply. atom ( ) ,
97
104
Err ( err) => {
@@ -102,6 +109,15 @@ impl WindowBuilder {
102
109
}
103
110
} ;
104
111
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
105
121
let wm_delete_window = match xcb:: intern_atom ( conn, false , "WM_DELETE_WINDOW" ) . get_reply ( ) {
106
122
Ok ( reply) => reply. atom ( ) ,
107
123
Err ( err) => {
@@ -112,12 +128,13 @@ impl WindowBuilder {
112
128
}
113
129
} ;
114
130
131
+ // Replace the window's WM_PROTOCOLS with the following.
115
132
let protocols = [ wm_delete_window] ;
116
133
// TODO(x11/errors): Check the response for errors?
117
134
xcb:: change_property (
118
135
conn,
119
136
PROP_MODE_REPLACE as u8 ,
120
- id ,
137
+ window_id ,
121
138
wm_protocols,
122
139
ATOM_ATOM ,
123
140
32 ,
@@ -130,34 +147,36 @@ impl WindowBuilder {
130
147
} )
131
148
}
132
149
133
- fn cairo_context (
150
+ /// Create a new cairo `Context`.
151
+ fn create_cairo_context (
134
152
& self ,
135
- id : u32 ,
153
+ window_id : u32 ,
136
154
visual_type : & mut Visualtype ,
137
155
) -> Result < RefCell < cairo:: Context > , Error > {
138
- // Create a draw surface
139
156
let conn = self . app . connection ( ) ;
140
157
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 )
144
159
} ;
145
- let cairo_drawable = cairo :: XCBDrawable ( id ) ;
160
+ let cairo_drawable = XCBDrawable ( window_id ) ;
146
161
let cairo_visual_type = unsafe {
147
- cairo :: XCBVisualType :: from_raw_none (
162
+ XCBVisualType :: from_raw_none (
148
163
& mut visual_type. base as * mut _ as * mut cairo_sys:: xcb_visualtype_t ,
149
164
)
150
165
} ;
151
- // TODO(x11/errors): Don't unwrap
152
166
let cairo_surface = XCBSurface :: create (
153
167
& cairo_xcb_connection,
154
168
& cairo_drawable,
155
169
& cairo_visual_type,
156
170
self . size . width as i32 ,
157
171
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
+ }
161
180
}
162
181
163
182
// TODO(x11/menus): make menus if requested
@@ -219,7 +238,7 @@ impl WindowBuilder {
219
238
220
239
// TODO(x11/errors): Should do proper cleanup (window destruction etc) in case of error
221
240
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) ?;
223
242
// Figure out the refresh rate of the current screen
224
243
let refresh_rate = util:: refresh_rate ( conn, id) ;
225
244
let state = RefCell :: new ( WindowState { size : self . size } ) ;
@@ -354,7 +373,12 @@ impl Window {
354
373
Ok ( ( ) )
355
374
}
356
375
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 ) {
358
382
// See: http://rtbo.github.io/rust-xcb/xcb/ffi/xproto/struct.xcb_expose_event_t.html
359
383
let expose_event = xcb:: ExposeEvent :: new (
360
384
self . id ,
@@ -371,13 +395,12 @@ impl Window {
371
395
EVENT_MASK_EXPOSURE ,
372
396
& expose_event,
373
397
) ;
374
- if flush {
375
- self . app . connection ( ) . flush ( ) ;
376
- }
377
398
}
378
399
379
400
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
+
381
404
// Figure out the window's current size
382
405
let geometry_cookie = xcb:: get_geometry ( self . app . connection ( ) , self . id ) ;
383
406
let reply = geometry_cookie. get_reply ( ) . unwrap ( ) ;
@@ -412,7 +435,7 @@ impl Window {
412
435
let sleep_amount_ms = ( 1000.0 / self . refresh_rate . unwrap ( ) ) as u64 ;
413
436
std:: thread:: sleep ( std:: time:: Duration :: from_millis ( sleep_amount_ms) ) ;
414
437
415
- self . request_redraw ( size. to_rect ( ) , false ) ;
438
+ self . request_redraw ( size. to_rect ( ) ) ;
416
439
}
417
440
self . app . connection ( ) . flush ( ) ;
418
441
Ok ( ( ) )
@@ -457,13 +480,14 @@ impl Window {
457
480
458
481
fn invalidate ( & self ) {
459
482
match self . size ( ) {
460
- Ok ( size) => self . request_redraw ( size. to_rect ( ) , true ) ,
483
+ Ok ( size) => self . invalidate_rect ( size. to_rect ( ) ) ,
461
484
Err ( err) => log:: error!( "Window::invalidate - failed to get size: {}" , err) ,
462
485
}
463
486
}
464
487
465
488
fn invalidate_rect ( & self , rect : Rect ) {
466
- self . request_redraw ( rect, true ) ;
489
+ self . request_redraw ( rect) ;
490
+ self . app . connection ( ) . flush ( ) ;
467
491
}
468
492
469
493
fn set_title ( & self , title : & str ) {
@@ -624,6 +648,7 @@ impl Window {
624
648
client_message : & xcb:: ClientMessageEvent ,
625
649
) -> Result < ( ) , Error > {
626
650
// 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
627
652
if client_message. type_ ( ) == self . atoms . wm_protocols && client_message. format ( ) == 32 {
628
653
let protocol = client_message. data ( ) . data32 ( ) [ 0 ] ;
629
654
if protocol == self . atoms . wm_delete_window {
0 commit comments