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
22use as_raw_xcb_connection::xcb_connection_t;
23use std::os::raw::{c_char, c_int, c_uchar, c_ulong};
24
25/// Base type for the display pointer.
26pub(crate) enum Display {}
27
28/// The type of the error.
29#[repr(C)]
30#[derive(Clone, Copy)]
31pub(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.
42type XOpenDisplay = unsafe extern "C" fn(display_name: *const c_char) -> *mut Display;
43type XCloseDisplay = unsafe extern "C" fn(display: *mut Display) -> c_int;
44type XGetXCBConnection = unsafe extern "C" fn(display: *mut Display) -> *mut xcb_connection_t;
45pub(crate) type XErrorHook =
46 Option<unsafe extern "C" fn(display: *mut Display, error_event: *mut XErrorEvent) -> c_int>;
47type XSetErrorHandler = unsafe extern "C" fn(handler: XErrorHook) -> XErrorHook;
48type XInitThreads = unsafe extern "C" fn() -> c_int;
49
50/// Catalogue of functions offered by Xlib.
51pub(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
76impl 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)]
161unsafe 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