package bar import ( "fmt" "container/list" "hhvn.uk/hbspbar/common" "hhvn.uk/hbspbar/bspc" "github.com/jezek/xgb" "github.com/jezek/xgb/xproto" "github.com/jezek/xgbutil" "github.com/jezek/xgbutil/icccm" "github.com/jezek/xgbutil/ewmh" ) var conf = struct { H uint Bg uint32 Fg uint32 Sel uint32 Urg uint32 } { 16, 0xff050a10, 0xffeeeeee, 0xff1b364b, 0xff90222b } var bars *list.List type bar struct { X *xgb.Conn Xu *xgbutil.XUtil Scr *xproto.ScreenInfo Mon *bspc.Monitor w xproto.Window } func (b *bar) Init() error { var err error b.w, err = xproto.NewWindowId(b.X) if err != nil { return common.Perror("xproto.NewWindowId", err) } geom := b.Mon.Rectangle xproto.CreateWindow(b.X, b.Scr.RootDepth, b.w, b.Scr.Root, int16(geom.X), int16(geom.Y), uint16(geom.Width), uint16(conf.H), 0, xproto.WindowClassInputOutput, b.Scr.RootVisual, xproto.CwBackPixel|xproto.CwEventMask, []uint32{ conf.Bg, xproto.EventMaskStructureNotify }) class := icccm.WmClass{"hbspbar", "hbspbar"} icccm.WmClassSet(b.Xu, b.w, &class) ewmh.WmWindowTypeSet(b.Xu, b.w, []string{"_NET_WM_WINDOW_TYPE_DOCK"}) ewmh.WmStateSet(b.Xu, b.w, []string{"_NET_WM_STATE_STICKY", "_NET_WM_STATE_ABOVE"}) err = xproto.MapWindowChecked(b.X, b.w).Check() if err != nil { return common.Perror("xproto.MapWindow", err) } go b.Mon.TopPadding(conf.H) return nil } func (b *bar) Destroy() { b.Mon.TopPadding(0) } 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 } func Init(state *bspc.State) (*Handle, error) { bars = list.New() x, err := xgb.NewConn() if err != nil { return nil, err } xu, err := xgbutil.NewConnXgb(x) if err != nil { return nil, err } setup := xproto.Setup(x) screen := setup.DefaultScreen(x) var h Handle h.Create = make(chan int) h.Destroy = make(chan int) h.NewState = make(chan *bspc.State) h.Err = make(chan error) events := make(chan *eventwrap) go func() { for { ev, err := x.WaitForEvent() w := eventwrap{ev, err} events <- &w select { case <- events: return default: } }}() go func() { defer func() { events <- nil }() for { select { case e := <- events: if e.Ev == nil && e.Err == nil { h.Err <- fmt.Errorf("X connection clossed") return } if e.Err != nil { common.Perror("x.WaitForEvents", err) continue } switch e.Ev.(type) { case xproto.DestroyNotifyEvent: h.Err <- fmt.Errorf("Window destroyed") return } case id := <- h.Destroy: destroy(id) case id := <- h.Create: if err := create(x, xu, screen, state.GetMon(id)); err != nil { h.Err <- fmt.Errorf("Couldn't create window: %s\n", err) return } case state := <- h.NewState: for e := bars.Front(); e != nil; e = e.Next() { v := e.Value.(*bar) v.Mon = state.GetMon(v.Mon.ID) } default: } } }() return &h, nil } func create(x *xgb.Conn, xu *xgbutil.XUtil, scr *xproto.ScreenInfo, m *bspc.Monitor) (error) { var b bar b.X = x b.Xu = xu b.Scr = scr b.Mon = m if err := b.Init(); err != nil { return err } bars.PushBack(&b) return nil } func destroy(monid int) (bool) { var next *list.Element for e := bars.Front(); e != nil; e = next { next = e.Next() v := e.Value.(*bar) /* A monid of -1 means that we're doing the bar.Cleanup schermoodle today */ if monid == -1 || monid > 0 && v.Mon.ID == int(monid) { v.Destroy() bars.Remove(e) if monid != -1 { return true } } } if monid == -1 { return true } return false } func Cleanup() { destroy(-1) }