main.rs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. #[macro_use]
  2. extern crate failure;
  3. mod widgets;
  4. mod window;
  5. use std::os::unix::io::AsRawFd;
  6. use pango::LayoutExt;
  7. use widgets::Widget;
  8. use window::{Display,Event,Size,Window};
  9. fn main() -> Result<(), failure::Error> {
  10. // set up the display and the window
  11. let mut d = Display::create()?;
  12. let mut ws = Vec::new();
  13. for (x_off, wd) in d.get_widths()? {
  14. let size = Size { wd, ht: 36, xo: x_off, yo: 0 };
  15. let mut w = Window::create(&d, size)?;
  16. // set some window-manager properties: this is a dock
  17. w.change_property("_NET_WM_WINDOW_TYPE", &["_NET_WM_WINDOW_TYPE_DOCK"])?;
  18. // ...and should push other windows out of the way
  19. w.change_property("_NET_WM_STRUT", &[x_off as i64, 0, size.ht as i64, 0])?;
  20. w.change_property(
  21. "_NET_WM_STRUT_PARTIAL",
  22. &[ 0, 0, size.ht as i64, 0,
  23. 0, 0, 0, 0,
  24. 0, size.wd as i64, 0, 0,
  25. ],
  26. )?;
  27. // we won't ever see this, but for good measure.
  28. w.set_title("rbar")?;
  29. // we care about some input events!
  30. w.set_input_masks()?;
  31. w.set_protocols()?;
  32. // and now show it!
  33. w.map();
  34. ws.push(w);
  35. }
  36. // we do some grossness with file descriptors later, so we need
  37. // the file descriptors we care about here
  38. let window_fds: Vec<i32> = ws.iter_mut().map({ |w| w.get_fd() }).collect();
  39. let stdin_fd = std::io::stdin().as_raw_fd();
  40. let mut fds = unsafe { std::mem::uninitialized() };
  41. // To begin with, our left-hand side---which normally is whatever
  42. // was last passed in on stdin---will start as a generic
  43. // message...
  44. let mut input = format!("Loading...");
  45. // And let's get a buffered stdin handle now
  46. let mut stdin = std::io::BufReader::new(std::io::stdin());
  47. // In the absence of other events, let's refresh every five
  48. // seconds. Or whatever.
  49. let mut timer = libc::timeval {
  50. tv_sec: 5,
  51. tv_usec: 0,
  52. };
  53. let mut ctxs = Vec::new();
  54. for w in ws.iter_mut() {
  55. // let's grab the cairo context here
  56. let surf = w.get_cairo_surface();
  57. let ctx = cairo::Context::new(&surf);
  58. let layout = pangocairo::functions::create_layout(&ctx)
  59. .ok_or(format_err!("unable to create layout"))?;
  60. // allow for the whole width of the bar, minus a small fixed amount
  61. layout.set_width((w.width - 20) * pango::SCALE);
  62. // this should also be configurable, but Fira Mono is a good font
  63. let mut font = pango::FontDescription::from_string("Fira Mono 18");
  64. font.set_weight(pango::Weight::Bold);
  65. layout.set_font_description(&font);
  66. // do an initial pass at drawing the bar!
  67. draw(&ctx, &layout, &input, w.size())?;
  68. ctxs.push((ctx, layout, w.size()));
  69. }
  70. let max_fd = window_fds.iter().max().unwrap_or(&0) + 1;
  71. // we're gonna keep looping until we don't
  72. loop {
  73. unsafe {
  74. // set up the FD set to be the X11 fd and the state of stdin
  75. libc::FD_ZERO(&mut fds);
  76. for fd in window_fds.iter() {
  77. libc::FD_SET(*fd, &mut fds);
  78. }
  79. libc::FD_SET(stdin_fd, &mut fds);
  80. timer.tv_sec = 5;
  81. // this will block until there's input on either of the
  82. // above FDs or until five seconds have passed, whichever comes first
  83. libc::select(
  84. max_fd,
  85. &mut fds,
  86. std::ptr::null_mut(),
  87. std::ptr::null_mut(),
  88. &mut timer,
  89. );
  90. }
  91. // if we _did_ have input on stdin, then read it in: that'll
  92. // be our new left-hand text
  93. if unsafe { libc::FD_ISSET(stdin_fd, &mut fds) } {
  94. use std::io::BufRead;
  95. input = String::new();
  96. stdin.read_line(&mut input)?;
  97. if input.len() == 0 {
  98. break;
  99. }
  100. for (ctx, layout, sz) in ctxs.iter() {
  101. draw(&ctx, &layout, &input, *sz)?;
  102. }
  103. }
  104. // if we have X11 events, handle them. If any one was a quit
  105. // event, then just... quit.
  106. for w in ws.iter_mut() {
  107. while w.has_events() {
  108. match w.handle() {
  109. Some(Event::QuitEvent) => break,
  110. _e => (),
  111. }
  112. }
  113. }
  114. for (ctx, layout, sz) in ctxs.iter() {
  115. // otherwise, draw the thing!
  116. draw(&ctx, &layout, &input, *sz)?;
  117. }
  118. }
  119. Ok(())
  120. }
  121. /// Do our Cairo drawing. This needs to be refactored to allow for
  122. /// more configurability in terms of what gets written!
  123. fn draw(
  124. ctx: &cairo::Context,
  125. layout: &pango::Layout,
  126. left: &str,
  127. size: Size)
  128. -> Result<(), failure::Error>
  129. {
  130. // the background is... gray-ish? this'll be configurable eventually
  131. ctx.set_source_rgb(0.1, 0.1, 0.1);
  132. ctx.paint();
  133. // and the text is white
  134. ctx.set_source_rgb(1.0, 1.0, 1.0);
  135. // set up a struct with everything that widgets need to draw
  136. let drawing = widgets::Drawing {
  137. ctx: ctx,
  138. lyt: &layout,
  139. size,
  140. };
  141. // set up our widgets
  142. let text = widgets::Text::new(left);
  143. let time = widgets::Time::new();
  144. // let bat = widgets::Battery::new()?;
  145. // and create a 'config' which tells us which widgets to draw from
  146. // the left, and which from the right
  147. let config = widgets::Config {
  148. left: vec![
  149. &text as &Widget,
  150. ],
  151. right: vec![
  152. // &bat as &Widget,
  153. &time as &Widget,
  154. ],
  155. };
  156. // and draw them!
  157. config.draw(&drawing);
  158. Ok(())
  159. }