hbspbar

bspwm status bar
git clone https://hhvn.uk/hbspbar
git clone git://hhvn.uk/hbspbar
Log | Files | Refs

bar.go (4838B)


      1 package bar // import "hhvn.uk/hbspbar/bar"
      2 
      3 import (
      4 	"fmt"
      5 	"image"
      6 	"errors"
      7 	"image/color"
      8 
      9 	"hhvn.uk/hbspbar/config"
     10 	"hhvn.uk/hbspbar/common"
     11 	"hhvn.uk/hbspbar/status"
     12 	"hhvn.uk/hbspbar/bspc"
     13 	"hhvn.uk/hbspbar/drw"
     14 )
     15 
     16 
     17 var bars     map[int]*bar
     18 
     19 type drawinfo struct {
     20 	State  *bspc.State
     21 	Blocks *status.Blocks
     22 }
     23 
     24 type bar struct {
     25 	id      int
     26 	w       *drw.Window
     27 	i       *image.RGBA
     28 	redraw  chan drawinfo
     29 }
     30 
     31 func create(state *bspc.State, id int) (error) {
     32 	var b bar
     33 
     34 	if err := b.init(id, state); err != nil {
     35 		return err
     36 	}
     37 
     38 	if bars == nil {
     39 		bars = make(map[int]*bar)
     40 	}
     41 
     42 	bars[b.id] = &b
     43 	return nil
     44 }
     45  
     46 func (b *bar) init(id int, state *bspc.State) error {
     47 	b.id = id
     48 	b.i  = nil
     49 
     50 	mon, err := b.getmon(state)
     51 	if err != nil { return err }
     52 
     53 	go mon.TopPadding(config.H)
     54 
     55 	geom := mon.Rectangle
     56 
     57 	b.w, err = drw.WindowCreate(geom.X, geom.Y, geom.Width, config.H)
     58 	if err != nil { return err }
     59 
     60 	rect := image.Rect(0, 0, int(mon.Rectangle.Width), int(config.H))
     61 	b.i = image.NewRGBA(rect)
     62 
     63 	b.redraw = make(chan drawinfo, 20)
     64 
     65 	go func(){
     66 		for d := range b.redraw {
     67 			// Gobble up excess redraws
     68 		gobble: for {
     69 				select {
     70 				case d = <- b.redraw:
     71 				default: break gobble
     72 				}
     73 			}
     74 
     75 			b.draw(d)
     76 		}
     77 	}()
     78 
     79 	return nil
     80 }
     81 
     82 func (b *bar) destroy(state *bspc.State) {
     83 	close(b.redraw)
     84 	mon, _ := b.getmon(state)
     85 	if (mon != nil) {
     86 		mon.TopPadding(0)
     87 	}
     88 }
     89 
     90 func (b bar) getmon(state *bspc.State) (*bspc.Monitor, error) {
     91 	return state.GetMon(b.id)
     92 }
     93 
     94 func (b *bar) drawText(x int, col color.Color, text string) (int, error) {
     95 	return drw.DrawText(b.i, x, col, text)
     96 }
     97 
     98 func (b *bar) drawRect(x, y, w, h int, c color.Color, fill bool) {
     99 	drw.DrawRect(b.i, x, y, w, h, c, fill)
    100 }
    101 
    102 func (b bar) draw(d drawinfo) {
    103 	var bg     color.Color
    104 	var filled bool
    105 
    106 	state  := d.State
    107 	blocks := d.Blocks
    108 
    109 	mon, err := b.getmon(state)
    110 	if err != nil {
    111 		return
    112 	}
    113 
    114 	// Dimensions of drawing space
    115 	cx := 0
    116 	w  := int(mon.Rectangle.Width)
    117 	h  := int(config.H)
    118 
    119 
    120 	// Draw background
    121 	b.drawRect(0, 0, w, h, config.Bg, true)
    122 
    123 	// Monitor
    124 	montext := mon.Name
    125 	monw    := 6 + drw.TextWidth(montext) + 8
    126 
    127 	if mon.Focused {
    128 		bg = config.Sel
    129 	} else {
    130 		bg = config.UnselMon
    131 	}
    132 
    133 	b.drawRect(cx, 0, monw, h, bg, true)
    134 	b.drawText(cx + 6, config.Fg, montext)
    135 	cx += monw
    136 
    137 	// Desktops
    138 	boxw := 4
    139 	ds   := len(mon.Desktops)
    140 	dw   := 4 + (4 + boxw + 2 + 3) * ds + 5
    141 
    142 	for i := 0; i < ds; i++ {
    143 		dw += drw.TextWidth(mon.Desktops[i].Name)
    144 	}
    145 
    146 	cx += 4
    147 	for i := 0; i < ds; i++ {
    148 		d := mon.Desktops[i]
    149 
    150 		cx += 5
    151 
    152 		if d.Focused {
    153 			bg = config.Sel
    154 		} else {
    155 			bg = config.FgDark
    156 		}
    157 
    158 		filled = d.Root != nil
    159 
    160 		b.drawRect(cx, 3, boxw, h - 7, bg, filled)
    161 		cx += boxw + 2
    162 		ax, _ := b.drawText(cx, config.Fg, d.Name)
    163 		cx += ax + 2
    164 	}
    165 
    166 	cx = int(mon.Rectangle.Width) - 6
    167 	for i := blocks.Len() - 1; i >= 0; i -= 1 {
    168 		s, ok := blocks.Get(i)
    169 		if ok {
    170 			cx -= s.W
    171 			cx -= config.StatusPad
    172 		}
    173 	}
    174 	cx += config.StatusPad
    175 	cx -= 6
    176 
    177 	b.drawRect(cx, 0, int(mon.Rectangle.Width) - cx, int(config.H),
    178 		config.Status, true)
    179 
    180 	cx = int(mon.Rectangle.Width) - 6
    181 	for i := blocks.Len() - 1; i >= 0; i -= 1 {
    182 		s, ok := blocks.Get(i)
    183 		if ok {
    184 			cx -= s.W
    185 
    186 			drw.AddImg(b.i, cx, s.W, s.I)
    187 
    188 			cx -= config.StatusPad
    189 		}
    190 	}
    191 
    192 	b.w.Paint(b.i)
    193 }
    194 
    195 var Create   = make(chan int)
    196 var Destroy  = make(chan int)
    197 var NewState = make(chan *bspc.State)
    198 var Err      = make(chan error)
    199 
    200 // Store the state for when bar.Cleanup() is called
    201 // This is a bit hacky but I don't really see any alternative
    202 var finalstate *bspc.State = nil
    203 
    204 func init() {
    205 	var state  *bspc.State    = nil
    206 	var blocks *status.Blocks = nil 
    207 
    208 	go func() {
    209 		defer func() {
    210 			close(Create)
    211 			close(Destroy)
    212 			close(NewState)
    213 			close(Err)
    214 		}()
    215 
    216 		for {
    217 			select {
    218 			case e := <- drw.Events:
    219 				if e.Ev == nil && e.Err == nil {
    220 					Err <- fmt.Errorf("X connection clossed")
    221 					return
    222 				}
    223 
    224 				if e.Err != nil {
    225 					common.Perror("X.WaitForEvents", e.Err)
    226 					continue
    227 				}
    228 
    229 				// switch e.Ev.(type) {
    230 				// case xproto.DestroyNotifyEvent:
    231 				// 	Err <- fmt.Errorf("Window destroyed")
    232 				// 	return
    233 				// default:
    234 				// }
    235 			case id := <- Destroy:
    236 				bars[id].destroy(state)
    237 				delete(bars, id)
    238 			case id := <- Create:
    239 				if state == nil {
    240 					Err <- errors.New("attempted to create a bar with uninitialized state")
    241 					return
    242 				}
    243 				if _, ok := bars[id]; ok { break }
    244 				if err := create(state, id); err != nil {
    245 					Err <- fmt.Errorf("Couldn't create window: %s\n", err)
    246 					return
    247 				}
    248 			case blocks = <- status.NewBlocks:
    249 				for _, b := range bars {
    250 					b.redraw <- drawinfo{state, blocks}
    251 				}
    252 			case state = <- NewState:
    253 				finalstate = state
    254 				for _, b := range bars {
    255 					b.redraw <- drawinfo{state, blocks}
    256 				}
    257 			}
    258 		}
    259 	}()
    260 }
    261 
    262 func Cleanup() {
    263 	if finalstate != nil {
    264 		for _, b := range bars {
    265 			b.destroy(finalstate)
    266 		}
    267 	}
    268 }