Actually spawn multiple windows
Getty Ritter
5 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 |