1 | // SPDX-License-Identifier: MIT OR Apache-2.0 OR Zlib |
2 | |
3 | // Copyright 2023 John Nunley |
4 | // |
5 | // Licensed under the Apache License, Version 2.0, the MIT License, and |
6 | // the Zlib license. You may not use this software except in compliance |
7 | // with at least one of these licenses. You should have received a copy |
8 | // of these licenses with this software. You may also find them at: |
9 | // |
10 | // http://www.apache.org/licenses/LICENSE-2.0 |
11 | // https://opensource.org/licenses/MIT |
12 | // https://opensource.org/licenses/Zlib |
13 | // |
14 | // Unless required by applicable law or agreed to in writing, software |
15 | // distributed under these licenses is distributed on an "AS IS" BASIS, |
16 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
17 | // See the licenses for the specific language governing permissions and |
18 | // limitations under the licenses. |
19 | |
20 | //! FFI bindings to Xlib and XlibXCB. |
21 | |
22 | use as_raw_xcb_connection::xcb_connection_t; |
23 | use std::os::raw::{c_char, c_int, c_uchar, c_ulong}; |
24 | |
25 | /// Base type for the display pointer. |
26 | pub(crate) enum Display {} |
27 | |
28 | /// The type of the error. |
29 | #[repr (C)] |
30 | #[derive (Clone, Copy)] |
31 | pub(crate) struct XErrorEvent { |
32 | pub type_: c_int, |
33 | pub display: *mut Display, |
34 | pub resourceid: c_ulong, |
35 | pub serial: c_ulong, |
36 | pub error_code: c_uchar, |
37 | pub request_code: c_uchar, |
38 | pub minor_code: c_uchar, |
39 | } |
40 | |
41 | // Function pointer types. |
42 | type XOpenDisplay = unsafe extern "C" fn(display_name: *const c_char) -> *mut Display; |
43 | type XCloseDisplay = unsafe extern "C" fn(display: *mut Display) -> c_int; |
44 | type XGetXCBConnection = unsafe extern "C" fn(display: *mut Display) -> *mut xcb_connection_t; |
45 | pub(crate) type XErrorHook = |
46 | Option<unsafe extern "C" fn(display: *mut Display, error_event: *mut XErrorEvent) -> c_int>; |
47 | type XSetErrorHandler = unsafe extern "C" fn(handler: XErrorHook) -> XErrorHook; |
48 | type XInitThreads = unsafe extern "C" fn() -> c_int; |
49 | |
50 | /// Catalogue of functions offered by Xlib. |
51 | pub(crate) struct Xlib { |
52 | /// The currently loaded Xlib library. |
53 | #[cfg (feature = "dlopen" )] |
54 | _xlib_library: libloading::Library, |
55 | |
56 | /// The currently loaded XlibXcb library. |
57 | #[cfg (feature = "dlopen" )] |
58 | _xlib_xcb_library: libloading::Library, |
59 | |
60 | /// The XOpenDisplay function. |
61 | x_open_display: XOpenDisplay, |
62 | |
63 | /// The XCloseDisplay function. |
64 | x_close_display: XCloseDisplay, |
65 | |
66 | /// The XGetXCBConnection function. |
67 | x_get_xcb_connection: XGetXCBConnection, |
68 | |
69 | /// The XSetErrorHandler function. |
70 | x_set_error_handler: XSetErrorHandler, |
71 | |
72 | /// The XInitThreads function. |
73 | x_init_threads: XInitThreads, |
74 | } |
75 | |
76 | impl Xlib { |
77 | /// Open a new connection to the X server. |
78 | pub(crate) unsafe fn open_display(&self, display_name: *const c_char) -> *mut Display { |
79 | (self.x_open_display)(display_name) |
80 | } |
81 | |
82 | /// Close a connection to the X server. |
83 | pub(crate) unsafe fn close_display(&self, display: *mut Display) -> c_int { |
84 | (self.x_close_display)(display) |
85 | } |
86 | |
87 | /// Get the XCB connection from an Xlib display. |
88 | pub(crate) unsafe fn get_xcb_connection(&self, display: *mut Display) -> *mut xcb_connection_t { |
89 | (self.x_get_xcb_connection)(display) |
90 | } |
91 | |
92 | /// Set the error handler. |
93 | pub(crate) unsafe fn set_error_handler(&self, handler: XErrorHook) -> XErrorHook { |
94 | (self.x_set_error_handler)(handler) |
95 | } |
96 | |
97 | /// Initialize threads. |
98 | pub(crate) unsafe fn init_threads(&self) -> c_int { |
99 | (self.x_init_threads)() |
100 | } |
101 | |
102 | /// Load the Xlib library at runtime. |
103 | #[cfg_attr (coverage, no_coverage)] |
104 | #[cfg (not(feature = "dlopen" ))] |
105 | pub(crate) fn load() -> Result<Self, std::io::Error> { |
106 | #[link (name = "X11" , kind = "dylib" )] |
107 | extern "C" { |
108 | fn XOpenDisplay(display_name: *const c_char) -> *mut Display; |
109 | fn XCloseDisplay(display: *mut Display) -> c_int; |
110 | fn XSetErrorHandler(handler: XErrorHook) -> XErrorHook; |
111 | fn XInitThreads() -> c_int; |
112 | } |
113 | |
114 | #[link (name = "X11-xcb" , kind = "dylib" )] |
115 | extern "C" { |
116 | fn XGetXCBConnection(display: *mut Display) -> *mut xcb_connection_t; |
117 | } |
118 | |
119 | Ok(Self { |
120 | x_open_display: XOpenDisplay, |
121 | x_close_display: XCloseDisplay, |
122 | x_get_xcb_connection: XGetXCBConnection, |
123 | x_set_error_handler: XSetErrorHandler, |
124 | x_init_threads: XInitThreads, |
125 | }) |
126 | } |
127 | |
128 | /// Load the Xlib library at runtime. |
129 | #[cfg_attr (coverage, no_coverage)] |
130 | #[cfg (feature = "dlopen" )] |
131 | pub(crate) fn load() -> Result<Self, libloading::Error> { |
132 | let xlib_library = unsafe { load_library(&["libX11.so.6" , "libX11.so" ]) }?; |
133 | let xlib_xcb_library = unsafe { load_library(&["libX11-xcb.so.1" , "libX11-xcb.so" ]) }?; |
134 | |
135 | let x_open_display = unsafe { xlib_library.get::<XOpenDisplay>(b"XOpenDisplay \0" )? }; |
136 | |
137 | let x_close_display = unsafe { xlib_library.get::<XCloseDisplay>(b"XCloseDisplay \0" )? }; |
138 | |
139 | let x_set_error_handler = |
140 | unsafe { xlib_library.get::<XSetErrorHandler>(b"XSetErrorHandler \0" )? }; |
141 | |
142 | let x_get_xcb_connection = |
143 | unsafe { xlib_xcb_library.get::<XGetXCBConnection>(b"XGetXCBConnection \0" )? }; |
144 | |
145 | let x_init_threads = unsafe { xlib_library.get::<XInitThreads>(b"XInitThreads \0" )? }; |
146 | |
147 | Ok(Self { |
148 | x_open_display: *x_open_display, |
149 | x_close_display: *x_close_display, |
150 | x_get_xcb_connection: *x_get_xcb_connection, |
151 | x_set_error_handler: *x_set_error_handler, |
152 | x_init_threads: *x_init_threads, |
153 | _xlib_library: xlib_library, |
154 | _xlib_xcb_library: xlib_xcb_library, |
155 | }) |
156 | } |
157 | } |
158 | |
159 | #[cfg (feature = "dlopen" )] |
160 | #[cfg_attr (coverage, no_coverage)] |
161 | unsafe fn load_library(names: &[&str]) -> Result<libloading::Library, libloading::Error> { |
162 | debug_assert!(!names.is_empty()); |
163 | let mut last_error: Option = None; |
164 | |
165 | for name: &&str in names { |
166 | match libloading::Library::new(filename:name) { |
167 | Ok(lib: Library) => return Ok(lib), |
168 | Err(err: Error) => { |
169 | last_error = Some(err); |
170 | } |
171 | } |
172 | } |
173 | |
174 | Err(last_error.unwrap()) |
175 | } |
176 | |