1
1
use std:: ffi:: CStr ;
2
- use std:: fmt:: { Debug , Formatter } ;
2
+ use std:: fmt:: { Debug , Display , Formatter } ;
3
3
use x11:: xlib;
4
4
5
5
use std:: cell:: RefCell ;
6
+ use std:: error:: Error ;
7
+ use std:: os:: raw:: { c_int, c_uchar, c_ulong} ;
6
8
use std:: panic:: AssertUnwindSafe ;
7
9
8
10
thread_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,
10
12
/// 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 ) } ;
13
15
}
14
16
15
17
/// A helper struct for safe X11 error handling
16
18
pub struct XErrorHandler < ' a > {
17
19
display : * mut xlib:: Display ,
18
- error : & ' a RefCell < Option < xlib :: XErrorEvent > > ,
20
+ error : & ' a RefCell < Option < XLibError > > ,
19
21
}
20
22
21
23
impl < ' 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
23
25
pub fn check ( & mut self ) -> Result < ( ) , XLibError > {
24
26
// Flush all possible previous errors
25
27
unsafe {
@@ -29,20 +31,27 @@ impl<'a> XErrorHandler<'a> {
29
31
30
32
match error {
31
33
None => Ok ( ( ) ) ,
32
- Some ( inner) => Err ( XLibError { inner } ) ,
34
+ Some ( inner) => Err ( inner) ,
33
35
}
34
36
}
35
37
36
38
/// 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 > (
39
46
display : * mut xlib:: Display , handler : F ,
40
47
) -> T {
48
+ /// # Safety
49
+ /// The given display and error pointers *must* be valid for the duration of this function.
41
50
unsafe extern "C" fn error_handler (
42
51
_dpy : * mut xlib:: Display , err : * mut xlib:: XErrorEvent ,
43
52
) -> 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;
46
55
47
56
CURRENT_X11_ERROR . with ( |error| {
48
57
let mut error = error. borrow_mut ( ) ;
@@ -51,7 +60,7 @@ impl<'a> XErrorHandler<'a> {
51
60
// cause of the other errors
52
61
Some ( _) => 1 ,
53
62
None => {
54
- * error = Some ( err) ;
63
+ * error = Some ( XLibError :: from_event ( err) ) ;
55
64
0
56
65
}
57
66
}
@@ -65,7 +74,9 @@ impl<'a> XErrorHandler<'a> {
65
74
66
75
CURRENT_X11_ERROR . with ( |error| {
67
76
// 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
+ }
69
80
70
81
let old_handler = unsafe { xlib:: XSetErrorHandler ( Some ( error_handler) ) } ;
71
82
let panic_result = std:: panic:: catch_unwind ( AssertUnwindSafe ( || {
@@ -84,39 +95,72 @@ impl<'a> XErrorHandler<'a> {
84
95
}
85
96
86
97
pub 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 > ,
88
106
}
89
107
90
108
impl 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 ] ;
92
129
unsafe {
93
130
xlib:: XGetErrorText (
94
- self . inner . display ,
95
- self . inner . error_code . into ( ) ,
131
+ error . display ,
132
+ error . error_code . into ( ) ,
96
133
buf. as_mut_ptr ( ) . cast ( ) ,
97
134
( buf. len ( ) - 1 ) as i32 ,
98
135
) ;
99
136
}
100
137
101
138
* buf. last_mut ( ) . unwrap ( ) = 0 ;
102
139
// 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 ( )
104
143
}
105
144
}
106
145
107
146
impl Debug for XLibError {
108
147
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
-
112
148
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 )
120
156
. finish ( )
121
157
}
122
158
}
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