| 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 | |