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 | // See build.rs for how this file is generated. |
26 | #[cfg (feature = "dlopen" )] |
27 | include! { |
28 | concat!(env!("OUT_DIR" ) , "/libdir.rs" ) |
29 | } |
30 | |
31 | /// Base type for the display pointer. |
32 | pub(crate) enum Display {} |
33 | |
34 | /// The type of the error. |
35 | #[repr (C)] |
36 | #[derive (Clone, Copy)] |
37 | pub(crate) struct XErrorEvent { |
38 | pub type_: c_int, |
39 | pub display: *mut Display, |
40 | pub resourceid: c_ulong, |
41 | pub serial: c_ulong, |
42 | pub error_code: c_uchar, |
43 | pub request_code: c_uchar, |
44 | pub minor_code: c_uchar, |
45 | } |
46 | |
47 | // Function pointer types. |
48 | type XOpenDisplay = unsafe extern "C" fn(display_name: *const c_char) -> *mut Display; |
49 | type XCloseDisplay = unsafe extern "C" fn(display: *mut Display) -> c_int; |
50 | type XGetXCBConnection = unsafe extern "C" fn(display: *mut Display) -> *mut xcb_connection_t; |
51 | type XDefaultScreen = unsafe extern "C" fn(display: *mut Display) -> c_int; |
52 | pub(crate) type XErrorHook = |
53 | Option<unsafe extern "C" fn(display: *mut Display, error_event: *mut XErrorEvent) -> c_int>; |
54 | type XSetErrorHandler = unsafe extern "C" fn(handler: XErrorHook) -> XErrorHook; |
55 | type XInitThreads = unsafe extern "C" fn() -> c_int; |
56 | |
57 | /// Catalogue of functions offered by Xlib. |
58 | pub(crate) struct Xlib { |
59 | /// The currently loaded Xlib library. |
60 | #[cfg (feature = "dlopen" )] |
61 | _xlib_library: libloading::Library, |
62 | |
63 | /// The currently loaded XlibXcb library. |
64 | #[cfg (feature = "dlopen" )] |
65 | _xlib_xcb_library: libloading::Library, |
66 | |
67 | /// The XOpenDisplay function. |
68 | x_open_display: XOpenDisplay, |
69 | |
70 | /// The XCloseDisplay function. |
71 | x_close_display: XCloseDisplay, |
72 | |
73 | /// The XGetXCBConnection function. |
74 | x_get_xcb_connection: XGetXCBConnection, |
75 | |
76 | /// The XDefaultScreen function. |
77 | x_default_screen: XDefaultScreen, |
78 | |
79 | /// The XSetErrorHandler function. |
80 | x_set_error_handler: XSetErrorHandler, |
81 | |
82 | /// The XInitThreads function. |
83 | x_init_threads: XInitThreads, |
84 | } |
85 | |
86 | impl Xlib { |
87 | /// Open a new connection to the X server. |
88 | pub(crate) unsafe fn open_display(&self, display_name: *const c_char) -> *mut Display { |
89 | (self.x_open_display)(display_name) |
90 | } |
91 | |
92 | /// Close a connection to the X server. |
93 | pub(crate) unsafe fn close_display(&self, display: *mut Display) -> c_int { |
94 | (self.x_close_display)(display) |
95 | } |
96 | |
97 | /// Get the XCB connection from an Xlib display. |
98 | pub(crate) unsafe fn get_xcb_connection(&self, display: *mut Display) -> *mut xcb_connection_t { |
99 | (self.x_get_xcb_connection)(display) |
100 | } |
101 | |
102 | /// Get the default screen index. |
103 | pub(crate) unsafe fn default_screen(&self, display: *mut Display) -> c_int { |
104 | (self.x_default_screen)(display) |
105 | } |
106 | |
107 | /// Set the error handler. |
108 | pub(crate) unsafe fn set_error_handler(&self, handler: XErrorHook) -> XErrorHook { |
109 | (self.x_set_error_handler)(handler) |
110 | } |
111 | |
112 | /// Initialize threads. |
113 | pub(crate) unsafe fn init_threads(&self) -> c_int { |
114 | (self.x_init_threads)() |
115 | } |
116 | |
117 | /// Load the Xlib library at runtime. |
118 | #[cfg_attr (coverage, no_coverage)] |
119 | #[cfg (not(feature = "dlopen" ))] |
120 | pub(crate) fn load() -> Result<Self, std::io::Error> { |
121 | #[link (name = "X11" , kind = "dylib" )] |
122 | extern "C" { |
123 | fn XOpenDisplay(display_name: *const c_char) -> *mut Display; |
124 | fn XCloseDisplay(display: *mut Display) -> c_int; |
125 | fn XDefaultScreen(display: *mut Display) -> c_int; |
126 | fn XSetErrorHandler(handler: XErrorHook) -> XErrorHook; |
127 | fn XInitThreads() -> c_int; |
128 | } |
129 | |
130 | #[link (name = "X11-xcb" , kind = "dylib" )] |
131 | extern "C" { |
132 | fn XGetXCBConnection(display: *mut Display) -> *mut xcb_connection_t; |
133 | } |
134 | |
135 | Ok(Self { |
136 | x_open_display: XOpenDisplay, |
137 | x_close_display: XCloseDisplay, |
138 | x_get_xcb_connection: XGetXCBConnection, |
139 | x_default_screen: XDefaultScreen, |
140 | x_set_error_handler: XSetErrorHandler, |
141 | x_init_threads: XInitThreads, |
142 | }) |
143 | } |
144 | |
145 | /// Load the Xlib library at runtime. |
146 | #[cfg_attr (coverage, no_coverage)] |
147 | #[cfg (feature = "dlopen" )] |
148 | pub(crate) fn load() -> Result<Self, libloading::Error> { |
149 | let xlib_library = unsafe { load_library(XLIB_LIBDIR, &["libX11.so.6" , "libX11.so" ]) }?; |
150 | let xlib_xcb_library = |
151 | unsafe { load_library(XLIB_XCB_LIBDIR, &["libX11-xcb.so.1" , "libX11-xcb.so" ]) }?; |
152 | |
153 | let x_open_display = unsafe { xlib_library.get::<XOpenDisplay>(b"XOpenDisplay \0" )? }; |
154 | |
155 | let x_close_display = unsafe { xlib_library.get::<XCloseDisplay>(b"XCloseDisplay \0" )? }; |
156 | |
157 | let x_set_error_handler = |
158 | unsafe { xlib_library.get::<XSetErrorHandler>(b"XSetErrorHandler \0" )? }; |
159 | |
160 | let x_default_screen = unsafe { xlib_library.get::<XDefaultScreen>(b"XDefaultScreen \0" )? }; |
161 | |
162 | let x_get_xcb_connection = |
163 | unsafe { xlib_xcb_library.get::<XGetXCBConnection>(b"XGetXCBConnection \0" )? }; |
164 | |
165 | let x_init_threads = unsafe { xlib_library.get::<XInitThreads>(b"XInitThreads \0" )? }; |
166 | |
167 | Ok(Self { |
168 | x_open_display: *x_open_display, |
169 | x_close_display: *x_close_display, |
170 | x_get_xcb_connection: *x_get_xcb_connection, |
171 | x_default_screen: *x_default_screen, |
172 | x_set_error_handler: *x_set_error_handler, |
173 | x_init_threads: *x_init_threads, |
174 | _xlib_library: xlib_library, |
175 | _xlib_xcb_library: xlib_xcb_library, |
176 | }) |
177 | } |
178 | } |
179 | |
180 | #[cfg (feature = "dlopen" )] |
181 | #[cfg_attr (coverage, no_coverage)] |
182 | unsafe fn load_library( |
183 | prefix: Option<&str>, |
184 | names: &[&str], |
185 | ) -> Result<libloading::Library, libloading::Error> { |
186 | use std::path::{Path, PathBuf}; |
187 | |
188 | debug_assert!(!names.is_empty()); |
189 | let mut last_error = None; |
190 | |
191 | for name in names { |
192 | let realpath = match prefix { |
193 | Some(prefix) => Path::new(prefix).join(name), |
194 | None => PathBuf::from(name), |
195 | }; |
196 | match libloading::Library::new(realpath) { |
197 | Ok(lib) => return Ok(lib), |
198 | Err(err) => { |
199 | last_error = Some(err); |
200 | } |
201 | } |
202 | } |
203 | |
204 | if prefix.is_some() { |
205 | if let Ok(lib) = load_library(None, names) { |
206 | return Ok(lib); |
207 | } |
208 | } |
209 | |
210 | Err(last_error.unwrap()) |
211 | } |
212 | |