11use std:: ffi:: CStr ;
2- use std:: fmt:: { Debug , Formatter } ;
2+ use std:: fmt:: { Debug , Display , Formatter } ;
33use x11:: xlib;
44
55use std:: cell:: RefCell ;
6+ use std:: error:: Error ;
7+ use std:: os:: raw:: { c_int, c_uchar, c_ulong} ;
68use std:: panic:: AssertUnwindSafe ;
79
810thread_local ! {
9- /// Used as part of [`XerrorHandler ::handle()`]. When an X11 error occurs during this function,
11+ /// Used as part of [`XErrorHandler ::handle()`]. When an X11 error occurs during this function,
1012 /// the error gets copied to this RefCell after which the program is allowed to resume. The
11- /// error can then be converted to a regular Rust Result value afterwards .
12- static CURRENT_X11_ERROR : RefCell <Option <xlib :: XErrorEvent >> = RefCell :: new( None ) ;
13+ /// error can then be converted to a regular Rust Result value afterward .
14+ static CURRENT_X11_ERROR : RefCell <Option <XLibError >> = const { RefCell :: new( None ) } ;
1315}
1416
1517/// A helper struct for safe X11 error handling
1618pub struct XErrorHandler < ' a > {
1719 display : * mut xlib:: Display ,
18- error : & ' a RefCell < Option < xlib :: XErrorEvent > > ,
20+ error : & ' a RefCell < Option < XLibError > > ,
1921}
2022
2123impl < ' a > XErrorHandler < ' a > {
22- /// Syncs and checks if any previous X11 calls returned an error
24+ /// Syncs and checks if any previous X11 calls from the given display returned an error
2325 pub fn check ( & mut self ) -> Result < ( ) , XLibError > {
2426 // Flush all possible previous errors
2527 unsafe {
@@ -29,20 +31,27 @@ impl<'a> XErrorHandler<'a> {
2931
3032 match error {
3133 None => Ok ( ( ) ) ,
32- Some ( inner) => Err ( XLibError { inner } ) ,
34+ Some ( inner) => Err ( inner) ,
3335 }
3436 }
3537
3638 /// Sets up a temporary X11 error handler for the duration of the given closure, and allows
37- /// that closure to check on the latest X11 error at any time
38- pub fn handle < T , F : FnOnce ( & mut XErrorHandler ) -> T > (
39+ /// that closure to check on the latest X11 error at any time.
40+ ///
41+ /// # Safety
42+ ///
43+ /// The given display pointer *must* be and remain valid for the duration of this function, as
44+ /// well as for the duration of the given `handler` closure.
45+ pub unsafe fn handle < T , F : FnOnce ( & mut XErrorHandler ) -> T > (
3946 display : * mut xlib:: Display , handler : F ,
4047 ) -> T {
48+ /// # Safety
49+ /// The given display and error pointers *must* be valid for the duration of this function.
4150 unsafe extern "C" fn error_handler (
4251 _dpy : * mut xlib:: Display , err : * mut xlib:: XErrorEvent ,
4352 ) -> i32 {
44- // SAFETY: the error pointer should be safe to copy
45- let err = * err;
53+ // SAFETY: the error pointer should be safe to access
54+ let err = & * err;
4655
4756 CURRENT_X11_ERROR . with ( |error| {
4857 let mut error = error. borrow_mut ( ) ;
@@ -51,7 +60,7 @@ impl<'a> XErrorHandler<'a> {
5160 // cause of the other errors
5261 Some ( _) => 1 ,
5362 None => {
54- * error = Some ( err) ;
63+ * error = Some ( XLibError :: from_event ( err) ) ;
5564 0
5665 }
5766 }
@@ -65,7 +74,9 @@ impl<'a> XErrorHandler<'a> {
6574
6675 CURRENT_X11_ERROR . with ( |error| {
6776 // Make sure to clear any errors from the last call to this function
68- * error. borrow_mut ( ) = None ;
77+ {
78+ * error. borrow_mut ( ) = None ;
79+ }
6980
7081 let old_handler = unsafe { xlib:: XSetErrorHandler ( Some ( error_handler) ) } ;
7182 let panic_result = std:: panic:: catch_unwind ( AssertUnwindSafe ( || {
@@ -84,39 +95,72 @@ impl<'a> XErrorHandler<'a> {
8495}
8596
8697pub struct XLibError {
87- inner : xlib:: XErrorEvent ,
98+ type_ : c_int ,
99+ resourceid : xlib:: XID ,
100+ serial : c_ulong ,
101+ error_code : c_uchar ,
102+ request_code : c_uchar ,
103+ minor_code : c_uchar ,
104+
105+ display_name : Box < str > ,
88106}
89107
90108impl XLibError {
91- pub fn get_display_name ( & self , buf : & mut [ u8 ] ) -> & CStr {
109+ /// # Safety
110+ /// The display pointer inside error must be valid for the duration of this call
111+ unsafe fn from_event ( error : & xlib:: XErrorEvent ) -> Self {
112+ Self {
113+ type_ : error. type_ ,
114+ resourceid : error. resourceid ,
115+ serial : error. serial ,
116+
117+ error_code : error. error_code ,
118+ request_code : error. request_code ,
119+ minor_code : error. minor_code ,
120+
121+ display_name : Self :: get_display_name ( error) ,
122+ }
123+ }
124+
125+ /// # Safety
126+ /// The display pointer inside error must be valid for the duration of this call
127+ unsafe fn get_display_name ( error : & xlib:: XErrorEvent ) -> Box < str > {
128+ let mut buf = [ 0 ; 255 ] ;
92129 unsafe {
93130 xlib:: XGetErrorText (
94- self . inner . display ,
95- self . inner . error_code . into ( ) ,
131+ error . display ,
132+ error . error_code . into ( ) ,
96133 buf. as_mut_ptr ( ) . cast ( ) ,
97134 ( buf. len ( ) - 1 ) as i32 ,
98135 ) ;
99136 }
100137
101138 * buf. last_mut ( ) . unwrap ( ) = 0 ;
102139 // SAFETY: whatever XGetErrorText did or not, we guaranteed there is a nul byte at the end of the buffer
103- unsafe { CStr :: from_ptr ( buf. as_mut_ptr ( ) . cast ( ) ) }
140+ let cstr = unsafe { CStr :: from_ptr ( buf. as_mut_ptr ( ) . cast ( ) ) } ;
141+
142+ cstr. to_string_lossy ( ) . into ( )
104143 }
105144}
106145
107146impl Debug for XLibError {
108147 fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
109- let mut buf = [ 0 ; 255 ] ;
110- let display_name = self . get_display_name ( & mut buf) . to_string_lossy ( ) ;
111-
112148 f. debug_struct ( "XLibError" )
113- . field ( "error_code" , & self . inner . error_code )
114- . field ( "error_message" , & display_name)
115- . field ( "minor_code" , & self . inner . minor_code )
116- . field ( "request_code" , & self . inner . request_code )
117- . field ( "type" , & self . inner . type_ )
118- . field ( "resource_id" , & self . inner . resourceid )
119- . field ( "serial" , & self . inner . serial )
149+ . field ( "error_code" , & self . error_code )
150+ . field ( "error_message" , & self . display_name )
151+ . field ( "minor_code" , & self . minor_code )
152+ . field ( "request_code" , & self . request_code )
153+ . field ( "type" , & self . type_ )
154+ . field ( "resource_id" , & self . resourceid )
155+ . field ( "serial" , & self . serial )
120156 . finish ( )
121157 }
122158}
159+
160+ impl Display for XLibError {
161+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
162+ write ! ( f, "XLib error: {} (error code {})" , & self . display_name, self . error_code)
163+ }
164+ }
165+
166+ impl Error for XLibError { }
0 commit comments