Actually spawn multiple windows
Getty Ritter
6 years ago
| 19 | 19 | ht: 36, |
| 20 | 20 | }; |
| 21 | 21 | let mut ws = Vec::new(); |
| 22 |
for (x_off, wd) in |
|
| 22 | for (x_off, wd) in d.get_widths()? { | |
| 23 | 23 | let size = Size { wd, ht: 36 }; |
| 24 |
let mut w = Window::create( |
|
| 24 | let mut w = Window::create(&d, size)?; | |
| 25 | 25 | // set some window-manager properties: this is a dock |
| 26 | 26 | w.change_property("_NET_WM_WINDOW_TYPE", &["_NET_WM_WINDOW_TYPE_DOCK"])?; |
| 27 | 27 | // ...and should push other windows out of the way |
| 44 | 44 | ws.push(w); |
| 45 | 45 | } |
| 46 | 46 | |
| 47 | // let's grab the cairo context here | |
| 48 | let surf = w.get_cairo_surface(); | |
| 49 | let ctx = cairo::Context::new(&surf); | |
| 50 | ||
| 51 | 47 | // we do some grossness with file descriptors later, so we need |
| 52 | 48 | // the file descriptors we care about here |
| 53 |
let window_fds |
|
| 49 | let window_fds: Vec<i32> = ws.iter_mut().map({ |w| w.get_fd() }).collect(); | |
| 54 | 50 | let stdin_fd = std::io::stdin().as_raw_fd(); |
| 51 | let mut fds = unsafe { std::mem::uninitialized() }; | |
| 55 | 52 | |
| 56 | let mut fds = unsafe { std::mem::uninitialized() }; | |
| 57 | 53 | // To begin with, our left-hand side---which normally is whatever |
| 58 | 54 | // was last passed in on stdin---will start as a generic |
| 59 | 55 | // message... |
| 60 | 56 | let mut input = format!("Loading..."); |
| 61 | 57 | // And let's get a buffered stdin handle now |
| 62 | 58 | let mut stdin = std::io::BufReader::new(std::io::stdin()); |
| 59 | ||
| 63 | 60 | // In the absence of other events, let's refresh every five |
| 64 | 61 | // seconds. Or whatever. |
| 65 | 62 | let mut timer = libc::timeval { |
| 67 | 64 | tv_usec: 0, |
| 68 | 65 | }; |
| 69 | 66 | |
| 70 | let layout = pangocairo::functions::create_layout(&ctx) | |
| 71 | .ok_or(format_err!("foo"))?; | |
| 72 | ||
| 73 | // allow for the whole width of the bar, minus a small fixed amount | |
| 74 | layout.set_width((size.wd - 20) * pango::SCALE); | |
| 75 | // this should also be configurable, but Fira Mono is a good font | |
| 76 | let mut font = pango::FontDescription::from_string("Fira Mono 18"); | |
| 77 | font.set_weight(pango::Weight::Bold); | |
| 78 | layout.set_font_description(&font); | |
| 79 | ||
| 80 | // do an initial pass at drawing the bar! | |
| 81 | draw(&ctx, &layout, &input, size)?; | |
| 67 | let mut ctxs = Vec::new(); | |
| 68 | for w in ws.iter_mut() { | |
| 69 | // let's grab the cairo context here | |
| 70 | let surf = w.get_cairo_surface(); | |
| 71 | let ctx = cairo::Context::new(&surf); | |
| 82 | 72 | |
| 83 | 73 | |
| 74 | let layout = pangocairo::functions::create_layout(&ctx) | |
| 75 | .ok_or(format_err!("unable to create layout"))?; | |
| 76 | ||
| 77 | // allow for the whole width of the bar, minus a small fixed amount | |
| 78 | layout.set_width((w.width - 20) * pango::SCALE); | |
| 79 | // this should also be configurable, but Fira Mono is a good font | |
| 80 | let mut font = pango::FontDescription::from_string("Fira Mono 18"); | |
| 81 | font.set_weight(pango::Weight::Bold); | |
| 82 | layout.set_font_description(&font); | |
| 83 | ||
| 84 | // do an initial pass at drawing the bar! | |
| 85 | draw(&ctx, &layout, &input, w.size())?; | |
| 86 | ||
| 87 | ctxs.push((ctx, layout)); | |
| 88 | } | |
| 89 | ||
| 90 | ||
| 91 | let max_fd = window_fds.iter().max().unwrap_or(&0) + 1; | |
| 84 | 92 | // we're gonna keep looping until we don't |
| 85 | 93 | loop { |
| 86 | 94 | unsafe { |
| 87 | 95 | // set up the FD set to be the X11 fd and the state of stdin |
| 88 | 96 | libc::FD_ZERO(&mut fds); |
| 89 | for fd in window_fds { | |
| 90 | libc::FD_SET(fd, &mut fds); | |
| 97 | for fd in window_fds.iter() { | |
| 98 | libc::FD_SET(*fd, &mut fds); | |
| 91 | 99 | } |
| 92 | 100 | libc::FD_SET(stdin_fd, &mut fds); |
| 93 | 101 | timer.tv_sec = 5; |
| 95 | 103 | // this will block until there's input on either of the |
| 96 | 104 | // above FDs or until five seconds have passed, whichever comes first |
| 97 | 105 | libc::select( |
| 98 |
|
|
| 106 | max_fd, | |
| 99 | 107 | &mut fds, |
| 100 | 108 | std::ptr::null_mut(), |
| 101 | 109 | std::ptr::null_mut(), |
| 112 | 120 | if input.len() == 0 { |
| 113 | 121 | break; |
| 114 | 122 | } |
| 115 |
|
|
| 123 | for (ctx, layout) in ctxs.iter() { | |
| 124 | draw(&ctx, &layout, &input, size)?; | |
| 125 | } | |
| 116 | 126 | } |
| 117 | 127 | |
| 118 | 128 | // if we have X11 events, handle them. If any one was a quit |
| 119 | 129 | // event, then just... quit. |
| 120 | while w.has_events() { | |
| 121 | match w.handle() { | |
| 122 | Some(Event::QuitEvent) => break, | |
| 123 | _e => (), | |
| 130 | for w in ws.iter_mut() { | |
| 131 | while w.has_events() { | |
| 132 | match w.handle() { | |
| 133 | Some(Event::QuitEvent) => break, | |
| 134 | _e => (), | |
| 135 | } | |
| 124 | 136 | } |
| 125 | 137 | } |
| 126 | 138 | |
| 127 | // otherwise, draw the thing! | |
| 128 | draw(&ctx, &layout, &input, size)?; | |
| 139 | for (ctx, layout) in ctxs.iter() { | |
| 140 | // otherwise, draw the thing! | |
| 141 | draw(&ctx, &layout, &input, size)?; | |
| 142 | } | |
| 129 | 143 | } |
| 130 | 144 | |
| 131 | 145 | Ok(()) |
| 50 | 50 | } |
| 51 | 51 | } |
| 52 | 52 | |
| 53 | impl Drop for Display { | |
| 54 | fn drop(&mut self) { | |
| 55 | unsafe { | |
| 56 | xlib::XCloseDisplay(self.display); | |
| 57 | } | |
| 58 | } | |
| 59 | } | |
| 60 | ||
| 53 | 61 | /// All the state needed to keep around to run this sort of |
| 54 | 62 | /// application! |
| 55 | pub struct Window { | |
| 56 | pub display: *mut xlib::_XDisplay, | |
| 63 | pub struct Window<'t> { | |
| 64 | pub display: &'t Display, | |
| 57 | 65 | pub screen: i32, |
| 58 | 66 | pub window: u64, |
| 59 | 67 | // these two are interned strings kept around because we want to |
| 66 | 74 | pub height: i32, |
| 67 | 75 | } |
| 68 | 76 | |
| 69 |
impl |
|
| 77 | impl<'t> Window<'t> { | |
| 70 | 78 | /// Create a new Window from a given Display and with the desire |
| 71 | 79 | /// width and height |
| 72 | 80 | pub fn create( |
| 73 |
d |
|
| 81 | display: &'t Display, | |
| 74 | 82 | Size { wd: width, ht: height }: Size, |
| 75 | ) -> Result<Window, failure::Error> { | |
| 76 | unsafe { | |
| 77 | let display = d.display; | |
| 78 | let screen = d.screen; | |
| 83 | ) -> Result<Window<'t>, failure::Error> { | |
| 84 | unsafe { | |
| 85 | let screen = display.screen; | |
| 79 | 86 | let window = xlib::XCreateSimpleWindow( |
| 80 | display, | |
| 81 | xlib::XRootWindow(display, screen), | |
| 87 | display.display, | |
| 88 | xlib::XRootWindow(display.display, screen), | |
| 82 | 89 | 0, |
| 83 | 90 | 0, |
| 84 | 91 | width as u32, |
| 85 | 92 | height as u32, |
| 86 | 93 | 1, |
| 87 | xlib::XBlackPixel(display, screen), | |
| 88 | xlib::XWhitePixel(display, screen), | |
| 94 | xlib::XBlackPixel(display.display, screen), | |
| 95 | xlib::XWhitePixel(display.display, screen), | |
| 89 | 96 | ); |
| 90 | 97 | let wm_protocols = { |
| 91 | 98 | let cstr = CString::new("WM_PROTOCOLS")?; |
| 92 |
xlib::XInternAtom(display |
|
| 99 | xlib::XInternAtom(display.display, cstr.as_ptr(), 0) | |
| 93 | 100 | }; |
| 94 | 101 | let wm_delete_window = { |
| 95 | 102 | let cstr = CString::new("WM_DELETE_WINDOW")?; |
| 96 |
xlib::XInternAtom(display |
|
| 103 | xlib::XInternAtom(display.display, cstr.as_ptr(), 0) | |
| 97 | 104 | }; |
| 98 | 105 | Ok(Window { |
| 99 | 106 | display, |
| 117 | 124 | let xinput_str = CString::new("XInputExtension")?; |
| 118 | 125 | unsafe { |
| 119 | 126 | xlib::XQueryExtension( |
| 120 |
self.display |
|
| 127 | self.display.display, | |
| 121 | 128 | xinput_str.as_ptr(), |
| 122 | 129 | &mut opcode, |
| 123 | 130 | &mut event, |
| 141 | 148 | |
| 142 | 149 | match unsafe { |
| 143 | 150 | xinput2::XISelectEvents( |
| 144 |
self.display |
|
| 151 | self.display.display, | |
| 145 | 152 | self.window, |
| 146 | 153 | &mut input_event_mask, |
| 147 | 154 | 1, |
| 158 | 165 | let mut protocols = [self.intern("WM_DELETE_WINDOW")?]; |
| 159 | 166 | unsafe { |
| 160 | 167 | xlib::XSetWMProtocols( |
| 161 |
self.display |
|
| 168 | self.display.display, | |
| 162 | 169 | self.window, |
| 163 | 170 | protocols.as_mut_ptr(), |
| 164 | 171 | protocols.len() as c_int, |
| 171 | 178 | pub fn set_title(&mut self, name: &str) -> Result<(), failure::Error> { |
| 172 | 179 | unsafe { |
| 173 | 180 | xlib::XStoreName( |
| 174 |
self.display |
|
| 181 | self.display.display, | |
| 175 | 182 | self.window, |
| 176 | 183 | CString::new(name)?.as_ptr(), |
| 177 | 184 | ); |
| 182 | 189 | /// Map the window to the screen |
| 183 | 190 | pub fn map(&mut self) { |
| 184 | 191 | unsafe { |
| 185 |
xlib::XMapWindow(self.display |
|
| 192 | xlib::XMapWindow(self.display.display, self.window); | |
| 186 | 193 | } |
| 187 | 194 | } |
| 188 | 195 | |
| 190 | 197 | pub fn intern(&mut self, s: &str) -> Result<u64, failure::Error> { |
| 191 | 198 | unsafe { |
| 192 | 199 | let cstr = CString::new(s)?; |
| 193 |
Ok(xlib::XInternAtom(self.display |
|
| 200 | Ok(xlib::XInternAtom(self.display.display, cstr.as_ptr(), 0)) | |
| 194 | 201 | } |
| 195 | 202 | } |
| 196 | 203 | |
| 206 | 213 | let len = val.len(); |
| 207 | 214 | T::with_ptr(val, self, |w, typ, ptr| { |
| 208 | 215 | xlib::XChangeProperty( |
| 209 |
w.display |
|
| 216 | w.display.display, | |
| 210 | 217 | w.window, |
| 211 | 218 | prop, |
| 212 | 219 | typ, |
| 225 | 232 | pub fn get_cairo_surface(&mut self) -> cairo::Surface { |
| 226 | 233 | unsafe { |
| 227 | 234 | let s = cairo_sys::cairo_xlib_surface_create( |
| 228 |
self.display |
|
| 235 | self.display.display, | |
| 229 | 236 | self.window, |
| 230 |
xlib::XDefaultVisual(self.display |
|
| 237 | xlib::XDefaultVisual(self.display.display, self.screen), | |
| 231 | 238 | self.width, |
| 232 | 239 | self.height, |
| 233 | 240 | ); |
| 241 | 248 | /// will also only return values for events we care about |
| 242 | 249 | pub fn handle(&mut self) -> Option<Event> { |
| 243 | 250 | let mut e = unsafe { mem::uninitialized() }; |
| 244 |
unsafe { xlib::XNextEvent(self.display |
|
| 251 | unsafe { xlib::XNextEvent(self.display.display, &mut e) }; | |
| 245 | 252 | match e.get_type() { |
| 246 | 253 | // Is it a quit event? We gotta do some tedious string |
| 247 | 254 | // comparison to find out |
| 261 | 268 | // otherwise, it might be a mouse press event |
| 262 | 269 | xlib::GenericEvent => { |
| 263 | 270 | let mut cookie: xlib::XGenericEventCookie = From::from(e); |
| 264 |
unsafe { xlib::XGetEventData(self.display |
|
| 271 | unsafe { xlib::XGetEventData(self.display.display, &mut cookie) }; | |
| 265 | 272 | match cookie.evtype { |
| 266 | 273 | xinput2::XI_ButtonPress => { |
| 267 | 274 | let data: &xinput2::XIDeviceEvent = |
| 280 | 287 | /// True if there are any pending events. |
| 281 | 288 | pub fn has_events(&mut self) -> bool { |
| 282 | 289 | unsafe { |
| 283 |
xlib::XPending(self.display |
|
| 290 | xlib::XPending(self.display.display) != 0 | |
| 284 | 291 | } |
| 285 | 292 | } |
| 286 | 293 | |
| 288 | 295 | /// surface to wait on events? This lets us use select on it! |
| 289 | 296 | pub fn get_fd(&mut self) -> i32 { |
| 290 | 297 | unsafe { |
| 291 | xlib::XConnectionNumber(self.display) | |
| 292 | } | |
| 293 | } | |
| 294 | } | |
| 295 | ||
| 296 | /// Always close the display when we're done. | |
| 297 | impl Drop for Window { | |
| 298 | fn drop(&mut self) { | |
| 299 | unsafe { | |
| 300 | xlib::XCloseDisplay(self.display); | |
| 301 |
|
|
| 298 | xlib::XConnectionNumber(self.display.display) | |
| 299 | } | |
| 300 | } | |
| 301 | ||
| 302 | pub fn size(&self) -> Size { | |
| 303 | Size { wd: self.width, ht: self.height } | |
| 302 | 304 | } |
| 303 | 305 | } |
| 304 | 306 | |