package bar // import "hhvn.uk/hbspbar/bar" import ( "fmt" "sync" "image" "errors" "hhvn.uk/hbspbar/config" "hhvn.uk/hbspbar/common" "hhvn.uk/hbspbar/bspc" "hhvn.uk/hbspbar/drw" "github.com/jezek/xgb" "github.com/jezek/xgb/xproto" "github.com/jezek/xgbutil" "github.com/jezek/xgbutil/icccm" "github.com/jezek/xgbutil/ewmh" "github.com/jezek/xgbutil/xgraphics" ) var bars map[int]*bar var stoploop chan bool type bar struct { Mon *bspc.Monitor w xproto.Window i *image.RGBA drawing sync.Mutex } func (b *bar) init() error { var err error go b.Mon.TopPadding(config.H) b.w, err = xproto.NewWindowId(drw.X) if err != nil { return common.Perror("xproto.NewWindowId", err) } geom := b.Mon.Rectangle xproto.CreateWindow(drw.X, drw.Screen.RootDepth, b.w, drw.Screen.Root, int16(geom.X), int16(geom.Y), uint16(geom.Width), uint16(config.H), 0, xproto.WindowClassInputOutput, drw.Screen.RootVisual, xproto.CwBackPixel|xproto.CwEventMask, []uint32{ config.Bg, xproto.EventMaskStructureNotify }) class := icccm.WmClass{"hbspbar", "hbspbar"} icccm.WmClassSet(drw.XU, b.w, &class) ewmh.WmWindowTypeSet(drw.XU, b.w, []string{"_NET_WM_WINDOW_TYPE_DOCK"}) ewmh.WmStateSet(drw.XU, b.w, []string{"_NET_WM_STATE_STICKY", "_NET_WM_STATE_ABOVE"}) err = xproto.MapWindowChecked(drw.X, b.w).Check() if err != nil { return common.Perror("xproto.MapWindow", err) } b.i = image.NewRGBA(b.rect()) return nil } func (b *bar) rect() image.Rectangle { return image.Rect(0, 0, int(b.Mon.Rectangle.Width), int(config.H)) } func (b *bar) drawText(x int, col uint32, text string) (int, error) { return drw.DrawText(b.i, x, col, text) } func (b *bar) drawRect(x, y, w, h int, c uint32, fill bool) { drw.DrawRect(b.i, x, y, w, h, c, fill) } func (b *bar) draw() { var bg uint32 var filled bool b.drawing.Lock() // Dimensions of drawing space cx := 0 w := int(b.Mon.Rectangle.Width) h := int(config.H) // Draw background b.drawRect(0, 0, w, h, config.Bg, true) // Monitor montext := b.Mon.Name monw := 6 + drw.TextWidth(montext) + 8 if b.Mon.Focused { bg = config.Sel } else { bg = config.Bg2 } b.drawRect(cx, 0, monw, h, bg, true) b.drawText(cx + 6, config.Fg, montext) cx += monw // Desktops boxw := 4 ds := len(b.Mon.Desktops) dw := 4 + (4 + boxw + 2 + 3) * ds + 5 for i := 0; i < ds; i++ { dw += drw.TextWidth(b.Mon.Desktops[i].Name) } cx += 4 for i := 0; i < ds; i++ { d := b.Mon.Desktops[i] cx += 5 if d.Focused { bg = config.Sel } else { bg = config.FgDark } filled = d.Root != nil b.drawRect(cx, 3, boxw, h - 7, bg, filled) cx += boxw + 2 ax, _ := b.drawText(cx, config.Fg, d.Name) cx += ax + 2 } cx += 5 xgr := xgraphics.NewConvert(drw.XU, b.i) xgr.XSurfaceSet(b.w) xgr.XDraw() xgr.XPaint(b.w) b.drawing.Unlock() } func (b *bar) destroy() { if (b.Mon != nil) { b.Mon.TopPadding(0) } xproto.DestroyWindow(drw.X, b.w) } type eventwrap struct { Ev xgb.Event Err error } // External interface type handle struct { Create chan int Destroy chan int NewState chan *bspc.State Err chan error } var Handle handle func init() { var state *bspc.State state = nil Handle.Create = make(chan int) Handle.Destroy = make(chan int) Handle.NewState = make(chan *bspc.State) Handle.Err = make(chan error) events := make(chan *eventwrap) stoploop = make(chan bool, 1) xstop := make(chan bool, 1) go func() { for { ev, err := drw.X.WaitForEvent() w := eventwrap{ev, err} select { case <- xstop: return default: events <- &w } }}() go func() { defer func() { xstop <- true close(Handle.Create) close(Handle.Destroy) close(Handle.NewState) close(Handle.Err) }() for { select { case e := <- events: if e.Ev == nil && e.Err == nil { Handle.Err <- fmt.Errorf("X connection clossed") return } if e.Err != nil { common.Perror("X.WaitForEvents", e.Err) continue } // switch e.Ev.(type) { // case xproto.DestroyNotifyEvent: // Handle.Err <- fmt.Errorf("Window destroyed") // return // default: // } case id := <- Handle.Destroy: bars[id].destroy() delete(bars, id) case id := <- Handle.Create: if state == nil { Handle.Err <- errors.New("attempted to create a bar with uninitialized state") return } if _, ok := bars[id]; ok { break } if err := create(drw.X, drw.XU, drw.Screen, state.GetMon(id)); err != nil { Handle.Err <- fmt.Errorf("Couldn't create window: %s\n", err) return } case state = <- Handle.NewState: for _, b := range bars { b.Mon = state.GetMon(b.Mon.ID) if b.Mon != nil { go b.draw() } } case <- stoploop: return } } }() } func create(x *xgb.Conn, xu *xgbutil.XUtil, scr *xproto.ScreenInfo, m *bspc.Monitor) (error) { var b bar b.Mon = m b.i = nil if err := b.init(); err != nil { return err } go b.draw() if bars == nil { bars = make(map[int]*bar) } bars[b.Mon.ID] = &b return nil } func Cleanup() { for _, b := range bars { b.destroy() } stoploop <- true }