gdritter repos knurling / 538d887
Some cleanup and lots of comments Getty Ritter 5 years ago
2 changed file(s) with 144 addition(s) and 85 deletion(s). Collapse all Expand all
33 use std::os::unix::io::AsRawFd;
44 use pango::LayoutExt;
55
6 use window::{Display,Event,Window};
6 use window::{Display,Event,Size,Window};
77
88 fn main() {
9 // set up the display and the window
910 let mut d = Display::create();
10 let width = d.get_width();
11 let mut w = Window::create(d, width, 36);
11 let size = Size {
12 wd: d.get_width(),
13 // TODO: this should be a function of font size
14 ht: 36,
15 };
16 let mut w = Window::create(d, size);
17 // set some window-manager properties: this is a dock
18 w.change_property("_NET_WM_WINDOW_TYPE", &["_NET_WM_WINDOW_TYPE_DOCK"]);
19 // ...and should push other windows out of the way
20 w.change_property("_NET_WM_STRUT", &[0i64, 0, size.ht as i64, 0],);
1221 w.change_property(
13 "_NET_WM_WINDOW_TYPE",
14 &["_NET_WM_WINDOW_TYPE_DOCK"],
22 "_NET_WM_STRUT_PARTIAL",
23 &[ 0, 0, size.ht as i64, 0,
24 0, 0, 0, 0,
25 0, size.wd as i64, 0, 0,
26 ],
1527 );
1628
17 w.change_property(
18 "_NET_WM_STRUT_PARTIAL",
19 &[
20 0, 0, 36, 0,
21 0, 0, 0, 0,
22 0, width as i64, 0, 0,
23 ],
24 );
25 w.change_property(
26 "_NET_WM_STRUT",
27 &[0i64, 0, 36, 0],
28 );
29
29 // we won't ever see this, but for good measure.
3030 w.set_title("rbar");
31
31 // we care about some input events!
3232 w.set_input_masks();
33
3433 w.set_protocols();
34 // and now show it!
3535 w.map();
3636
37 // let's grab the cairo context here
3738 let surf = w.get_cairo_surface();
3839 let ctx = cairo::Context::new(&surf);
3940
41 // we do some grossness with file descriptors later, so we need
42 // the file descriptors we care about here
4043 let window_fd = w.get_fd();
44 let stdin_fd = std::io::stdin().as_raw_fd();
4145
4246 let mut fds = unsafe { std::mem::uninitialized() };
47 // To begin with, our left-hand side---which normally is whatever
48 // was last passed in on stdin---will start as a generic
49 // message...
4350 let mut input = format!("Loading...");
44 let stdin_fd = std::io::stdin().as_raw_fd();
51 // And let's get a buffered stdin handle now
4552 let mut stdin = std::io::BufReader::new(std::io::stdin());
53 // In the absence of other events, let's refresh every five
54 // seconds. Or whatever.
4655 let mut timer = libc::timeval {
4756 tv_sec: 5,
4857 tv_usec: 0,
4958 };
50 draw(&ctx, "[1]", width);
59 // do an initial pass at drawing the bar!
60 draw(&ctx, &input, size);
5161
62 // we're gonna keep looping until we don't
5263 loop {
53 use std::io::BufRead;
54
5564 unsafe {
65 // set up the FD set to be the X11 fd and the state of stdin
5666 libc::FD_ZERO(&mut fds);
5767 libc::FD_SET(window_fd, &mut fds);
5868 libc::FD_SET(stdin_fd, &mut fds);
5969
70 // this will block until there's input on either of the
71 // above FDs or until five seconds have passed, whichever comes first
6072 libc::select(
6173 window_fd + 1,
6274 &mut fds,
6678 );
6779 }
6880
81 // if we _did_ have input on stdin, then read it in: that'll
82 // be our new left-hand text
6983 if unsafe { libc::FD_ISSET(stdin_fd, &mut fds) } {
84 use std::io::BufRead;
7085 input = String::new();
7186 stdin.read_line(&mut input).unwrap();
7287 if input == "" {
7388 break;
7489 }
75 draw(&ctx, &input, width);
90 draw(&ctx, &input, size);
7691 }
7792
93 // if we have X11 events, handle them. If any one was a quit
94 // event, then just... quit.
7895 while w.has_events() {
79 draw(&ctx, &input, width);
8096 match w.handle() {
81 Event::QuitEvent => break,
82 Event::ShowEvent =>
83 draw(&ctx, &input, width),
97 Some(Event::QuitEvent) => break,
8498 _e => (),
8599 }
86100 }
87101
102 // otherwise, draw the thing!
103 draw(&ctx, &input, size);
88104 }
89105 }
90106
91107
92 fn draw(ctx: &cairo::Context, left: &str, width: i32) {
108 /// Do our Cairo drawing. This needs to be refactored to allow for
109 /// more configurability in terms of what gets written!
110 fn draw(ctx: &cairo::Context, left: &str, Size { wd: width, ..}: Size) {
111 // for the current time on the right
93112 let now = time::now();
94113
114 // the background is... gray-ish? this'll be configurable eventually
95115 ctx.set_source_rgb(0.1, 0.1, 0.1);
96116 ctx.paint();
117
118 // and the text is white
97119 ctx.set_source_rgb(1.0, 1.0, 1.0);
98120
121 // Our pango layout handles placing and drawing text
99122 let layout = pangocairo::functions::create_layout(&ctx).unwrap();
123 // for the time, we want to align it to the right
100124 layout.set_alignment(pango::Alignment::Right);
125 // allow for the whole width of the bar, minus a small fixed amount
101126 layout.set_width((width - 20) * pango::SCALE);
127 // this should also be configurable, but Fira Mono is a good font
102128 let mut font = pango::FontDescription::from_string("Fira Mono 18");
103129 font.set_weight(pango::Weight::Bold);
104130 layout.set_font_description(&font);
131 // start drawing in the top-left
105132 ctx.move_to(10.0, 4.0);
133 //The text here is just the nicely-rendered current time
106134 layout.set_text(&time::strftime("%a %b %d %H:%M", &now).unwrap());
135 // and draw it
107136 pangocairo::functions::show_layout(&ctx, &layout);
108137
138 // We can reuse the same layout, but starting from the left
109139 layout.set_alignment(pango::Alignment::Left);
140 // and now we write whatever the "current text" is...
110141 layout.set_text(left);
142 // and draw that!
111143 pangocairo::functions::show_layout(&ctx, &layout);
112144 }
33 use std::ffi::CString;
44 use std::{mem,ptr};
55 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 }
612
713 pub struct Display {
814 pub display: *mut xlib::_XDisplay,
2430 }
2531 }
2632
33 /// All the state needed to keep around to run this sort of
34 /// application!
2735 pub struct Window {
2836 pub display: *mut xlib::_XDisplay,
2937 pub screen: i32,
3038 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
3142 pub wm_protocols: u64,
3243 pub wm_delete_window: u64,
44 // The width and height of the window
3345 pub width: i32,
3446 pub height: i32,
3547 }
3648
3749 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 {
3956 unsafe {
4057 let display = d.display;
4158 let screen = d.screen;
4461 xlib::XRootWindow(display, screen),
4562 0,
4663 0,
47 3840,
48 36,
64 width as u32,
65 height as u32,
4966 1,
5067 xlib::XBlackPixel(display, screen),
5168 xlib::XWhitePixel(display, screen),
7087 }
7188 }
7289
90 /// for this application, we might eventually care about the
91 /// mouse, so make sure we notify x11 that we care about those
7392 pub fn set_input_masks(&mut self) {
7493 let mut opcode = 0;
7594 let mut event = 0;
126145 }
127146 }
128147
148 /// Set the name of the window to the desired string
129149 pub fn set_title(&mut self, name: &str) {
130150 unsafe {
131151 xlib::XStoreName(
136156 }
137157 }
138158
159 /// Map the window to the screen
139160 pub fn map(&mut self) {
140161 unsafe {
141162 xlib::XMapWindow(self.display, self.window);
142163 }
143164 }
144165
166 /// Intern a string in the x server
145167 pub fn intern(&mut self, s: &str) -> u64 {
146168 unsafe {
147169 let cstr = CString::new(s).unwrap();
149171 }
150172 }
151173
174 /// Modify the supplied property to the noted value.
152175 pub fn change_property<T: XProperty>(&mut self, prop: &str, val: &[T]) {
153176 let prop = self.intern(prop);
154177 unsafe {
168191 }
169192 }
170193
194 /// Get the Cairo drawing surface corresponding to the whole
195 /// window
171196 pub fn get_cairo_surface(&mut self) -> cairo::Surface {
172197 unsafe {
173198 let s = cairo_sys::cairo_xlib_surface_create(
174199 self.display,
175200 self.window,
176201 xlib::XDefaultVisual(self.display, self.screen),
177 3840,
178 64,
202 self.width,
203 self.height,
179204 );
180205 cairo::Surface::from_raw_none(s)
181206 }
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> {
187214 let mut e = unsafe { mem::uninitialized() };
188215 unsafe { xlib::XNextEvent(self.display, &mut e) };
189216 match e.get_type() {
217 // Is it a quit event? We gotta do some tedious string
218 // comparison to find out
190219 xlib::ClientMessage => {
191220 let xclient: xlib::XClientMessageEvent = From::from(e);
192221 if xclient.message_type == self.wm_protocols && xclient.format == 32 {
193222 let protocol = xclient.data.get_long(0) as xlib::Atom;
194223 if protocol == self.wm_delete_window {
195 return Event::QuitEvent;
224 return Some(Event::QuitEvent);
196225 }
197226 }
198227 }
199228
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
202233 xlib::GenericEvent => {
203234 let mut cookie: xlib::XGenericEventCookie = From::from(e);
204235 unsafe { xlib::XGetEventData(self.display, &mut cookie) };
205236 match cookie.evtype {
206237 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 });
209241 }
210242 _ => (),
211243 }
213245 _ => (),
214246 }
215247
216 Event::Other
217 }
218
248 None
249 }
250
251 /// True if there are any pending events.
219252 pub fn has_events(&mut self) -> bool {
220253 unsafe {
221254 xlib::XPending(self.display) != 0
222255 }
223256 }
224257
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!
225260 pub fn get_fd(&mut self) -> i32 {
226261 unsafe {
227262 xlib::XConnectionNumber(self.display)
229264 }
230265 }
231266
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
232278 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 );
234284 }
235285
236286 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 ) {
238292 f(w, xlib::XA_CARDINAL, unsafe { mem::transmute(xs.as_ptr()) })
239293 }
240294 }
241295
242296 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 ) {
244302 let xs: Vec<u64> = xs.iter().map(|s| w.intern(s)).collect();
245303 f(w, xlib::XA_ATOM, unsafe { mem::transmute(xs.as_ptr()) })
246304 }
247305 }
248306
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
257309 #[derive(Debug)]
258310 pub enum Event {
259311 MouseEvent { x:f64, y: f64 },
260312 ShowEvent,
261313 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 }