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