3 | 3 |
use std::ffi::CString;
|
4 | 4 |
use std::{mem,ptr};
|
5 | 5 |
use std::os::raw::{c_int,c_uchar};
|
| 6 |
|
| 7 |
#[derive(Debug,Clone,Copy)]
|
| 8 |
pub struct Size {
|
| 9 |
pub wd: i32,
|
| 10 |
pub ht: i32,
|
| 11 |
}
|
6 | 12 |
|
7 | 13 |
pub struct Display {
|
8 | 14 |
pub display: *mut xlib::_XDisplay,
|
|
24 | 30 |
}
|
25 | 31 |
}
|
26 | 32 |
|
| 33 |
/// All the state needed to keep around to run this sort of
|
| 34 |
/// application!
|
27 | 35 |
pub struct Window {
|
28 | 36 |
pub display: *mut xlib::_XDisplay,
|
29 | 37 |
pub screen: i32,
|
30 | 38 |
pub window: u64,
|
| 39 |
// these two are interned strings kept around because we want to
|
| 40 |
// check against them a _lot_, to find out if an event is a quit
|
| 41 |
// event
|
31 | 42 |
pub wm_protocols: u64,
|
32 | 43 |
pub wm_delete_window: u64,
|
| 44 |
// The width and height of the window
|
33 | 45 |
pub width: i32,
|
34 | 46 |
pub height: i32,
|
35 | 47 |
}
|
36 | 48 |
|
37 | 49 |
impl Window {
|
38 | |
pub fn create(d: Display, width: i32, height: i32) -> Window {
|
| 50 |
/// Create a new Window from a given Display and with the desire
|
| 51 |
/// width and height
|
| 52 |
pub fn create(
|
| 53 |
d: Display,
|
| 54 |
Size { wd: width, ht: height }: Size,
|
| 55 |
) -> Window {
|
39 | 56 |
unsafe {
|
40 | 57 |
let display = d.display;
|
41 | 58 |
let screen = d.screen;
|
|
44 | 61 |
xlib::XRootWindow(display, screen),
|
45 | 62 |
0,
|
46 | 63 |
0,
|
47 | |
3840,
|
48 | |
36,
|
| 64 |
width as u32,
|
| 65 |
height as u32,
|
49 | 66 |
1,
|
50 | 67 |
xlib::XBlackPixel(display, screen),
|
51 | 68 |
xlib::XWhitePixel(display, screen),
|
|
70 | 87 |
}
|
71 | 88 |
}
|
72 | 89 |
|
| 90 |
/// for this application, we might eventually care about the
|
| 91 |
/// mouse, so make sure we notify x11 that we care about those
|
73 | 92 |
pub fn set_input_masks(&mut self) {
|
74 | 93 |
let mut opcode = 0;
|
75 | 94 |
let mut event = 0;
|
|
126 | 145 |
}
|
127 | 146 |
}
|
128 | 147 |
|
| 148 |
/// Set the name of the window to the desired string
|
129 | 149 |
pub fn set_title(&mut self, name: &str) {
|
130 | 150 |
unsafe {
|
131 | 151 |
xlib::XStoreName(
|
|
136 | 156 |
}
|
137 | 157 |
}
|
138 | 158 |
|
| 159 |
/// Map the window to the screen
|
139 | 160 |
pub fn map(&mut self) {
|
140 | 161 |
unsafe {
|
141 | 162 |
xlib::XMapWindow(self.display, self.window);
|
142 | 163 |
}
|
143 | 164 |
}
|
144 | 165 |
|
| 166 |
/// Intern a string in the x server
|
145 | 167 |
pub fn intern(&mut self, s: &str) -> u64 {
|
146 | 168 |
unsafe {
|
147 | 169 |
let cstr = CString::new(s).unwrap();
|
|
149 | 171 |
}
|
150 | 172 |
}
|
151 | 173 |
|
| 174 |
/// Modify the supplied property to the noted value.
|
152 | 175 |
pub fn change_property<T: XProperty>(&mut self, prop: &str, val: &[T]) {
|
153 | 176 |
let prop = self.intern(prop);
|
154 | 177 |
unsafe {
|
|
168 | 191 |
}
|
169 | 192 |
}
|
170 | 193 |
|
| 194 |
/// Get the Cairo drawing surface corresponding to the whole
|
| 195 |
/// window
|
171 | 196 |
pub fn get_cairo_surface(&mut self) -> cairo::Surface {
|
172 | 197 |
unsafe {
|
173 | 198 |
let s = cairo_sys::cairo_xlib_surface_create(
|
174 | 199 |
self.display,
|
175 | 200 |
self.window,
|
176 | 201 |
xlib::XDefaultVisual(self.display, self.screen),
|
177 | |
3840,
|
178 | |
64,
|
| 202 |
self.width,
|
| 203 |
self.height,
|
179 | 204 |
);
|
180 | 205 |
cairo::Surface::from_raw_none(s)
|
181 | 206 |
}
|
182 | |
}
|
183 | |
|
184 | |
pub fn handle(&mut self) -> Event {
|
185 | |
// to find out if we're getting a delete window event
|
186 | |
|
| 207 |
}
|
| 208 |
|
| 209 |
/// handle a single event, wrapping it as an 'Event'. This is
|
| 210 |
/// pretty useless right now, but the plan is to make it easier to
|
| 211 |
/// handle things like keyboard input and mouse input later. This
|
| 212 |
/// will also only return values for events we care about
|
| 213 |
pub fn handle(&mut self) -> Option<Event> {
|
187 | 214 |
let mut e = unsafe { mem::uninitialized() };
|
188 | 215 |
unsafe { xlib::XNextEvent(self.display, &mut e) };
|
189 | 216 |
match e.get_type() {
|
| 217 |
// Is it a quit event? We gotta do some tedious string
|
| 218 |
// comparison to find out
|
190 | 219 |
xlib::ClientMessage => {
|
191 | 220 |
let xclient: xlib::XClientMessageEvent = From::from(e);
|
192 | 221 |
if xclient.message_type == self.wm_protocols && xclient.format == 32 {
|
193 | 222 |
let protocol = xclient.data.get_long(0) as xlib::Atom;
|
194 | 223 |
if protocol == self.wm_delete_window {
|
195 | |
return Event::QuitEvent;
|
| 224 |
return Some(Event::QuitEvent);
|
196 | 225 |
}
|
197 | 226 |
}
|
198 | 227 |
}
|
199 | 228 |
|
200 | |
xlib::Expose => return Event::ShowEvent,
|
201 | |
|
| 229 |
// Is it a show event?
|
| 230 |
xlib::Expose => return Some(Event::ShowEvent),
|
| 231 |
|
| 232 |
// otherwise, it might be a mouse press event
|
202 | 233 |
xlib::GenericEvent => {
|
203 | 234 |
let mut cookie: xlib::XGenericEventCookie = From::from(e);
|
204 | 235 |
unsafe { xlib::XGetEventData(self.display, &mut cookie) };
|
205 | 236 |
match cookie.evtype {
|
206 | 237 |
xinput2::XI_ButtonPress => {
|
207 | |
let data: &xinput2::XIDeviceEvent = unsafe { mem::transmute(cookie.data) };
|
208 | |
return Event::MouseEvent { x: data.event_x, y: data.event_y };
|
| 238 |
let data: &xinput2::XIDeviceEvent =
|
| 239 |
unsafe { mem::transmute(cookie.data) };
|
| 240 |
return Some(Event::MouseEvent { x: data.event_x, y: data.event_y });
|
209 | 241 |
}
|
210 | 242 |
_ => (),
|
211 | 243 |
}
|
|
213 | 245 |
_ => (),
|
214 | 246 |
}
|
215 | 247 |
|
216 | |
Event::Other
|
217 | |
}
|
218 | |
|
| 248 |
None
|
| 249 |
}
|
| 250 |
|
| 251 |
/// True if there are any pending events.
|
219 | 252 |
pub fn has_events(&mut self) -> bool {
|
220 | 253 |
unsafe {
|
221 | 254 |
xlib::XPending(self.display) != 0
|
222 | 255 |
}
|
223 | 256 |
}
|
224 | 257 |
|
| 258 |
/// Did you know that X11 uses a file descriptor underneath the
|
| 259 |
/// surface to wait on events? This lets us use select on it!
|
225 | 260 |
pub fn get_fd(&mut self) -> i32 {
|
226 | 261 |
unsafe {
|
227 | 262 |
xlib::XConnectionNumber(self.display)
|
|
229 | 264 |
}
|
230 | 265 |
}
|
231 | 266 |
|
| 267 |
/// Always close the display when we're done.
|
| 268 |
impl Drop for Window {
|
| 269 |
fn drop(&mut self) {
|
| 270 |
unsafe {
|
| 271 |
xlib::XCloseDisplay(self.display);
|
| 272 |
}
|
| 273 |
}
|
| 274 |
}
|
| 275 |
|
| 276 |
/// A trait for abstracting over different values which are allowed
|
| 277 |
/// for xlib properties
|
232 | 278 |
pub trait XProperty : Sized {
|
233 | |
fn with_ptr(xs: &[Self], w: &mut Window, f: impl FnOnce(&mut Window, u64, *const u8));
|
| 279 |
fn with_ptr(
|
| 280 |
xs: &[Self],
|
| 281 |
w: &mut Window,
|
| 282 |
f: impl FnOnce(&mut Window, u64, *const u8),
|
| 283 |
);
|
234 | 284 |
}
|
235 | 285 |
|
236 | 286 |
impl XProperty for i64 {
|
237 | |
fn with_ptr(xs: &[Self], w: &mut Window, f: impl FnOnce(&mut Window, u64, *const u8)) {
|
| 287 |
fn with_ptr(
|
| 288 |
xs: &[Self],
|
| 289 |
w: &mut Window,
|
| 290 |
f: impl FnOnce(&mut Window, u64, *const u8),
|
| 291 |
) {
|
238 | 292 |
f(w, xlib::XA_CARDINAL, unsafe { mem::transmute(xs.as_ptr()) })
|
239 | 293 |
}
|
240 | 294 |
}
|
241 | 295 |
|
242 | 296 |
impl XProperty for &str {
|
243 | |
fn with_ptr(xs: &[Self], w: &mut Window, f: impl FnOnce(&mut Window, u64, *const u8)) {
|
| 297 |
fn with_ptr(
|
| 298 |
xs: &[Self],
|
| 299 |
w: &mut Window,
|
| 300 |
f: impl FnOnce(&mut Window, u64, *const u8),
|
| 301 |
) {
|
244 | 302 |
let xs: Vec<u64> = xs.iter().map(|s| w.intern(s)).collect();
|
245 | 303 |
f(w, xlib::XA_ATOM, unsafe { mem::transmute(xs.as_ptr()) })
|
246 | 304 |
}
|
247 | 305 |
}
|
248 | 306 |
|
249 | |
impl Drop for Window {
|
250 | |
fn drop(&mut self) {
|
251 | |
unsafe {
|
252 | |
xlib::XCloseDisplay(self.display);
|
253 | |
}
|
254 | |
}
|
255 | |
}
|
256 | |
|
| 307 |
/// An ADT of only the events we care about, wrapped in a high-level
|
| 308 |
/// way
|
257 | 309 |
#[derive(Debug)]
|
258 | 310 |
pub enum Event {
|
259 | 311 |
MouseEvent { x:f64, y: f64 },
|
260 | 312 |
ShowEvent,
|
261 | 313 |
QuitEvent,
|
262 | |
Other,
|
263 | |
}
|
264 | |
|
265 | |
/*
|
266 | |
cairo_surface_t *cairo_create_x11_surface0(int x, int y)
|
267 | |
{
|
268 | |
Display *dsp;
|
269 | |
Drawable da;
|
270 | |
int screen;
|
271 | |
cairo_surface_t *sfc;
|
272 | |
|
273 | |
if ((dsp = XOpenDisplay(NULL)) == NULL)
|
274 | |
exit(1);
|
275 | |
screen = DefaultScreen(dsp);
|
276 | |
da = XCreateSimpleWindow(dsp, DefaultRootWindow(dsp),
|
277 | |
0, 0, x, y, 0, 0, 0);
|
278 | |
XSelectInput(dsp, da, ButtonPressMask | KeyPressMask);
|
279 | |
XMapWindow(dsp, da);
|
280 | |
|
281 | |
sfc = cairo_xlib_surface_create(dsp, da,
|
282 | |
DefaultVisual(dsp, screen), x, y);
|
283 | |
cairo_xlib_surface_set_size(sfc, x, y);
|
284 | |
|
285 | |
return sfc;
|
286 | |
}
|
287 | |
*/
|
| 314 |
}
|