Did I really write this 2 years ago??? Anyways, some small updates

This commit is contained in:
Camille Scholtz 2018-12-23 18:03:56 +01:00
parent a9daf23fbe
commit 67c68f8a98
7 changed files with 263 additions and 117 deletions

@ -55,8 +55,8 @@ specified before the `for` loop. For example setting up a connection
to `mpd`.
If you want something to only be done *after* the very first loop - an
example of this would be not waiting for a workspace chance event, but
immediately checking the current workspace. - use:
example of this would be not waiting for a workspace change event, but
immediately checking the current workspace - use:
```go
init := true
@ -75,8 +75,8 @@ the user changes his workspace for the first time.
---
When you've gathered all needed information you can update the block
values using for example `block.bg = value` and running
When you've gathered all information you can update the block values
using for example `block.bg = value` and running
`bar.redraw <- block`.

100
bar.go

@ -23,27 +23,26 @@ type Bar struct {
win *xwindow.Window
img *xgraphics.Image
// The font and fontsize that should be used.
font *truetype.Font
fsize float64
// The width and height of the bar.
w, h int
// This is a sum of all of the block widths, used to draw a block
// to the right of the last block.
// This is a sum of all of the block widths, used to draw a block to the
// right of the last block.
xsum int
// The font that should be used.
font *truetype.Font
size float64
// A map with information about the block, see the `Block` type.
block *sync.Map
// A channel where the block should be send to to once its ready
// to be redrawn.
// A channel where the block should be send to to once its ready to be
// redrawn.
redraw chan *Block
}
func initBar(x, y, w, h int, font string, fsize float64) (*Bar,
error) {
func initBar(x, y, w, h int, font string, size float64) (*Bar, error) {
bar := new(Bar)
var err error
@ -56,27 +55,26 @@ func initBar(x, y, w, h int, font string, fsize float64) (*Bar,
// Run the main X event loop, this is uses to catch events.
go xevent.Main(bar.xu)
// Listen to the root window for property change events, used to
// check if the user changed the focused window or active
// workspace for example.
if err := xwindow.New(bar.xu, bar.xu.RootWin()).Listen(
xproto.EventMaskPropertyChange); err != nil {
// Listen to the root window for property change events, used to check if
// the user changed the focused window or active workspace for example.
if err := xwindow.New(bar.xu, bar.xu.RootWin()).Listen(xproto.
EventMaskPropertyChange); err != nil {
return nil, err
}
// Create a window for the bar. This window listens to button
// press events in order to respond to them.
// Create a window for the bar. This window listens to button press events
// in order to respond to them.
bar.win, err = xwindow.Generate(bar.xu)
if err != nil {
return nil, err
}
bar.win.Create(bar.xu.RootWin(), x, y, w, h, xproto.CwBackPixel|
xproto.CwEventMask, 0x000000, xproto.EventMaskButtonPress)
bar.win.Create(bar.xu.RootWin(), x, y, w, h, xproto.CwBackPixel|xproto.
CwEventMask, 0x000000, xproto.EventMaskButtonPress)
// EWMH stuff to make the window behave like an actuale bar.
// TODO: `WmStateSet` and `WmDesktopSet` are basically here to
// keep OpenBox happy, can I somehow remove them and just use
// `_NET_WM_WINDOW_TYPE_DOCK` like I can with WindowChef?
// EWMH stuff to make the window behave like an actual bar.
// XXX: `WmStateSet` and `WmDesktopSet` are basically here to keep OpenBox
// happy, can I somehow remove them and just use `_NET_WM_WINDOW_TYPE_DOCK`
// like I can with WindowChef?
if err := ewmh.WmWindowTypeSet(bar.xu, bar.win.Id, []string{
"_NET_WM_WINDOW_TYPE_DOCK"}); err != nil {
return nil, err
@ -85,29 +83,34 @@ func initBar(x, y, w, h int, font string, fsize float64) (*Bar,
"_NET_WM_STATE_STICKY"}); err != nil {
return nil, err
}
if err := ewmh.WmDesktopSet(bar.xu, bar.win.Id, ^uint(0)); err !=
nil {
if err := ewmh.WmDesktopSet(bar.xu, bar.win.Id, ^uint(0)); err != nil {
return nil, err
}
if err := ewmh.WmNameSet(bar.xu, bar.win.Id, "melonbar"); err !=
nil {
if err := ewmh.WmNameSet(bar.xu, bar.win.Id, "melonbar"); err != nil {
return nil, err
}
// Map window.
bar.win.Map()
// TODO: Moving the window is again a hack to keep OpenBox happy.
// XXX: Moving the window is again a hack to keep OpenBox happy.
bar.win.Move(x, y)
// Create the bar image.
bar.img = xgraphics.New(bar.xu, image.Rect(0, 0, w, h))
bar.img.XSurfaceSet(bar.win.Id)
if err := bar.img.XSurfaceSet(bar.win.Id); err != nil {
return nil, err
}
bar.img.XDraw()
bar.w = w
bar.h = h
// Load font.
// TODO: I don't *really* want to use `ttf` fonts but there
// doesn't seem to be any `pcf` Go library at the moment.
// TODO: I don't *really* want to use `ttf` fonts but there doesn't seem to
// be any `pcf` Go library at the moment. I have tried the plan9 fonts,
// which do work, but honestly it's a pain in the ass (read: impossible) to
// convert muh cure font.
f, err := os.Open(font)
if err != nil {
return nil, err
@ -116,36 +119,30 @@ func initBar(x, y, w, h int, font string, fsize float64) (*Bar,
if err != nil {
return nil, err
}
bar.fsize = fsize
bar.w = w
bar.h = h
bar.size = size
bar.block = new(sync.Map)
bar.redraw = make(chan *Block)
// Listen to mouse events and execute the required function.
xevent.ButtonPressFun(func(_ *xgbutil.XUtil,
ev xevent.ButtonPressEvent) {
xevent.ButtonPressFun(func(_ *xgbutil.XUtil, ev xevent.ButtonPressEvent) {
// Determine what block the cursor is in.
// TODO: This feels a bit slow at the moment, can I improve
// it?
// TODO: This feels a bit slow at the moment, can I improve it?
var block *Block
bar.block.Range(func(name, i interface{}) bool {
block = i.(*Block)
// XXX: Hack for music block.
if name == "music" {
tw, _ := xgraphics.Extents(bar.font, bar.fsize,
block.txt)
if ev.EventX > int16(block.x+(block.w-tw+(block.xoff*
2))) && ev.EventX < int16(block.x+block.w) {
tw, _ := xgraphics.Extents(bar.font, bar.size, block.txt)
if ev.EventX > int16(block.x+(block.w-tw+(block.xoff*2))) && ev.
EventX < int16(block.x+block.w) {
return false
}
return true
}
if ev.EventX > int16(block.x) && ev.EventX < int16(
block.x+block.w) {
if ev.EventX > int16(block.x) && ev.EventX < int16(block.x+block.
w) {
return false
}
return true
@ -159,9 +156,8 @@ func initBar(x, y, w, h int, font string, fsize float64) (*Bar,
}
func (bar *Bar) draw(block *Block) error {
// Calculate the required x coordinate for the different
// aligments.
tw, _ := xgraphics.Extents(bar.font, bar.fsize, block.txt)
// Calculate the required x coordinate for the different aligments.
tw, _ := xgraphics.Extents(bar.font, bar.size, block.txt)
var x int
switch block.align {
case 'l':
@ -173,8 +169,7 @@ func (bar *Bar) draw(block *Block) error {
case 'a':
x = (bar.w / 2) - (tw / 2)
default:
return fmt.Errorf("draw %#U: Not a valid aligment rune",
block.align)
return fmt.Errorf("draw %#U: Not a valid aligment rune", block.align)
}
x += block.xoff
@ -193,12 +188,13 @@ func (bar *Bar) draw(block *Block) error {
// Draw the text.
// TODO: Center text vertically automatically.
if _, _, err := block.img.Text(x, 6, hexToBGRA(block.fg),
bar.fsize, bar.font, block.txt); err != nil {
if _, _, err := block.img.Text(x, 6, hexToBGRA(block.fg), bar.size, bar.
font, block.txt); err != nil {
return err
}
block.img.XDraw()
bar.img.XPaint(bar.win.Id)
return nil
}

@ -14,9 +14,9 @@ type Block struct {
// The x coordinate and width of the block.
x, w int
/// The aligment of the text, this can be `l` for left aligment,
// `c` for center aligment, `r` for right aligment and `a` for
// absolute center aligment.
// The aligment of the text, this can be `l` for left aligment, `c` for
// center aligment, `r` for right aligment and `a` for absolute center
// aligment.
align rune
// Additional x offset to further tweak the location of the text.
@ -25,25 +25,33 @@ type Block struct {
// The foreground and background colors in hex.
bg, fg string
// A map with functions to execute on button events. Accepted
// button strings are `button0` to `button5`
// A map with functions to execute on button events. Accepted button strings
// are `button0` to `button5`
actions map[string]func()
// The popup.
popup *Popup
// The sub-image that represents the block.
img *xgraphics.Image
}
func (bar *Bar) initBlock(name, txt string, w int, align rune,
xoff int, bg, fg string) *Block {
func (bar *Bar) initBlock(name, txt string, w int, align rune, xoff int, bg,
fg string) *Block {
block := new(Block)
block.txt = txt
block.x = bar.xsum
block.w = w
block.align = align
block.xoff = xoff
block.bg = bg
block.fg = fg
block.actions = map[string]func(){
"button1": func() {},
"button2": func() {},
@ -51,8 +59,9 @@ func (bar *Bar) initBlock(name, txt string, w int, align rune,
"button4": func() {},
"button5": func() {},
}
block.img = bar.img.SubImage(image.Rect(bar.xsum, 0, bar.xsum+w,
bar.h)).(*xgraphics.Image)
block.img = bar.img.SubImage(image.Rect(bar.xsum, 0, bar.xsum+w, bar.
h)).(*xgraphics.Image)
// Add the width of this block to the xsum.
bar.xsum += w

143
blocks.go

@ -17,8 +17,7 @@ import (
)
func (bar *Bar) clockFun() {
block := bar.initBlock("clock", "?", 800, 'a', 0, "#445967",
"#CCCCCC")
block := bar.initBlock("clock", "?", 800, 'a', 0, "#445967", "#CCCCCC")
init := true
for {
@ -38,23 +37,36 @@ func (bar *Bar) clockFun() {
}
func (bar *Bar) musicFun() error {
block := bar.initBlock("music", "?", 660, 'r', -10, "#3C4F5B",
"#CCCCCC")
block := bar.initBlock("music", "?", 660, 'r', -13, "#3C4F5B", "#CCCCCC")
block.actions["button1"] = func() {
if block.popup == nil {
var err error
block.popup, err = bar.initPopup(1920-304-29, 29, 304, 148,
"#3C4F5B", "#CCCCCC")
if err != nil {
log.Print(err)
}
//popup.draw()
} else {
block.popup = block.popup.destroy()
}
}
block.actions["button3"] = func() {
conn, err := mpd.Dial("tcp", ":6600")
if err != nil {
log.Print(err)
}
defer conn.Close()
status, err := conn.Status()
if err != nil {
log.Print(err)
}
if status["state"] == "pause" {
conn.Pause(false)
} else {
conn.Pause(true)
if err := conn.Pause(status["state"] != "pause"); err != nil {
log.Print(err)
}
}
block.actions["button4"] = func() {
@ -62,16 +74,22 @@ func (bar *Bar) musicFun() error {
if err != nil {
log.Print(err)
}
defer conn.Close()
conn.Previous()
if err := conn.Previous(); err != nil {
log.Print(err)
}
}
block.actions["button5"] = func() {
conn, err := mpd.Dial("tcp", ":6600")
if err != nil {
log.Print(err)
}
defer conn.Close()
conn.Next()
if err := conn.Next(); err != nil {
log.Print(err)
}
}
watcher, err := mpd.NewWatcher("tcp", ":6600", "", "player")
@ -87,8 +105,7 @@ func (bar *Bar) musicFun() error {
}
init = false
// TODO: Is it maybe possible to not create a new connection
// each loop?
// TODO: Is it maybe possible to not create a new connection each loop?
conn, err = mpd.Dial("tcp", ":6600")
if err != nil {
log.Print(err)
@ -106,6 +123,7 @@ func (bar *Bar) musicFun() error {
log.Print(err)
continue
}
var state string
if status["state"] == "pause" {
state = "[paused] "
@ -122,17 +140,16 @@ func (bar *Bar) musicFun() error {
}
func (bar *Bar) todoFun() {
block := bar.initBlock("todo", "?", 29, 'c', 0, "#5394C9",
"#FFFFFF")
block := bar.initBlock("todo", "?", 29, 'c', 0, "#5394C9", "#FFFFFF")
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
if err := watcher.Add("/home/onodera/todo"); err != nil {
if err := watcher.Add("/home/onodera/.todo"); err != nil {
log.Fatal(err)
}
file, err := os.Open("/home/onodera/todo")
file, err := os.Open("/home/onodera/.todo")
if err != nil {
log.Fatal(err)
}
@ -218,12 +235,11 @@ func (bar *Bar) weatherFun() {
*/
func (bar *Bar) windowFun() {
block := bar.initBlock("window", "?", 220, 'c', 0, "#37BF8D",
"#FFFFFF")
block := bar.initBlock("window", "?", 220, 'c', 0, "#37BF8D", "#FFFFFF")
// TODO: I'm not sure how I can use init here?
xevent.PropertyNotifyFun(func(_ *xgbutil.XUtil,
ev xevent.PropertyNotifyEvent) {
// TODO: I'm not sure how I can use init (to prevent a black bar) here?
xevent.PropertyNotifyFun(func(_ *xgbutil.XUtil, ev xevent.
PropertyNotifyEvent) {
atom, err := xprop.Atm(bar.xu, "_NET_ACTIVE_WINDOW")
if ev.Atom != atom {
return
@ -256,34 +272,32 @@ func (bar *Bar) windowFun() {
}
func (bar *Bar) workspaceFun() {
blockwww := bar.initBlock("www", "www", 74, 'c', 0, "#5394C9",
"#FFFFFF")
blockwww.actions["button1"] = func() {
blockWWW := bar.initBlock("www", "www", 74, 'c', 0, "#5394C9", "#FFFFFF")
blockWWW.actions["button1"] = func() {
if err := ewmh.CurrentDesktopReq(bar.xu, 0); err != nil {
log.Println(err)
}
}
blockirc := bar.initBlock("irc", "irc", 67, 'c', 0, "#5394C9",
"#FFFFFF")
blockirc.actions["button1"] = func() {
blockIRC := bar.initBlock("irc", "irc", 67, 'c', 0, "#5394C9", "#FFFFFF")
blockIRC.actions["button1"] = func() {
if err := ewmh.CurrentDesktopReq(bar.xu, 1); err != nil {
log.Println(err)
}
}
blocksrc := bar.initBlock("src", "src", 70, 'c', 0, "#5394C9",
"#FFFFFF")
blocksrc.actions["button1"] = func() {
blockSRC := bar.initBlock("src", "src", 70, 'c', 0, "#5394C9", "#FFFFFF")
blockSRC.actions["button1"] = func() {
if err := ewmh.CurrentDesktopReq(bar.xu, 2); err != nil {
log.Println(err)
}
}
// TODO: I'm not sure how I can use init here?
// TODO: I'm not sure how I can use init (to prevent a black bar) here?
var owsp uint
xevent.PropertyNotifyFun(func(_ *xgbutil.XUtil,
ev xevent.PropertyNotifyEvent) {
var pwsp, nwsp int
xevent.PropertyNotifyFun(func(_ *xgbutil.XUtil, ev xevent.
PropertyNotifyEvent) {
atom, err := xprop.Atm(bar.xu, "_NET_CURRENT_DESKTOP")
if ev.Atom != atom {
return
@ -298,27 +312,56 @@ func (bar *Bar) workspaceFun() {
log.Print(err)
return
}
switch wsp {
case 0:
blockWWW.bg = "#72A7D3"
blockIRC.bg = "#5394C9"
blockSRC.bg = "#5394C9"
pwsp = 2
nwsp = 1
case 1:
blockWWW.bg = "#5394C9"
blockIRC.bg = "#72A7D3"
blockSRC.bg = "#5394C9"
pwsp = 0
nwsp = 2
case 2:
blockWWW.bg = "#5394C9"
blockIRC.bg = "#5394C9"
blockSRC.bg = "#72A7D3"
pwsp = 1
nwsp = 0
}
if owsp == wsp {
return
}
owsp = wsp
switch wsp {
case 0:
blockwww.bg = "#72A7D3"
blockirc.bg = "#5394C9"
blocksrc.bg = "#5394C9"
case 1:
blockwww.bg = "#5394C9"
blockirc.bg = "#72A7D3"
blocksrc.bg = "#5394C9"
case 2:
blockwww.bg = "#5394C9"
blockirc.bg = "#5394C9"
blocksrc.bg = "#72A7D3"
}
bar.redraw <- blockwww
bar.redraw <- blockirc
bar.redraw <- blocksrc
bar.redraw <- blockWWW
bar.redraw <- blockIRC
bar.redraw <- blockSRC
}).Connect(bar.xu, bar.xu.RootWin())
prevFun := func() {
if err := ewmh.CurrentDesktopReq(bar.xu, pwsp); err != nil {
log.Println(err)
}
}
nextFun := func() {
if err := ewmh.CurrentDesktopReq(bar.xu, nwsp); err != nil {
log.Println(err)
}
}
blockWWW.actions["button4"] = prevFun
blockWWW.actions["button5"] = nextFun
blockIRC.actions["button4"] = prevFun
blockIRC.actions["button5"] = nextFun
blockSRC.actions["button4"] = prevFun
blockSRC.actions["button5"] = nextFun
}

@ -6,8 +6,7 @@ import (
)
func main() {
bar, err := initBar(0, 0, 1920, 29,
"/home/onodera/.fonts/cure.ttf", 11)
bar, err := initBar(0, 0, 1920, 29, "/home/onodera/.fonts/cure.tff.bak", 11)
if err != nil {
log.Fatal(err)
}

98
popup.go Normal file

@ -0,0 +1,98 @@
package main
import (
"image"
"github.com/BurntSushi/xgb/xproto"
"github.com/BurntSushi/xgbutil/ewmh"
"github.com/BurntSushi/xgbutil/xgraphics"
"github.com/BurntSushi/xgbutil/xwindow"
)
// Popup is a struct with information about the popup.
type Popup struct {
// The popup window and image.
win *xwindow.Window
img *xgraphics.Image
// The width and height of the popup.
w, h int
// The foreground and background colors in hex.
bg, fg string
// A channel where the popup should be send to to once its ready
// to be redrawn.
redraw chan *Popup
}
func (bar *Bar) initPopup(x, y, w, h int, bg, fg string) (*Popup,
error) {
popup := new(Popup)
var err error
// Create a window for the bar. This window listens to button
// press events in order to respond to them.
popup.win, err = xwindow.Generate(bar.xu)
if err != nil {
return nil, err
}
popup.win.Create(bar.xu.RootWin(), x, y, w, h, xproto.CwBackPixel|
xproto.CwEventMask, 0x000000, xproto.EventMaskButtonPress)
// EWMH stuff.
// TODO: `WmStateSet` and `WmDesktopSet` are basically here to
// keep OpenBox happy, can I somehow remove them and just use
// `_NET_WM_WINDOW_TYPE_DOCK` like I can with WindowChef?
if err := ewmh.WmWindowTypeSet(bar.xu, popup.win.Id, []string{
"_NET_WM_WINDOW_TYPE_DOCK"}); err != nil {
return nil, err
}
if err := ewmh.WmStateSet(bar.xu, popup.win.Id, []string{
"_NET_WM_STATE_STICKY"}); err != nil {
return nil, err
}
if err := ewmh.WmDesktopSet(bar.xu, popup.win.Id, ^uint(
0)); err != nil {
return nil, err
}
if err := ewmh.WmNameSet(bar.xu, popup.win.Id, "melonbar"); err !=
nil {
return nil, err
}
// Map window.
popup.win.Map()
// TODO: Moving the window is again a hack to keep OpenBox happy.
popup.win.Move(x, y)
// Create the bar image.
popup.img = xgraphics.New(bar.xu, image.Rect(0, 0, w, h))
popup.img.XSurfaceSet(popup.win.Id)
popup.img.XDraw()
popup.w = w
popup.h = h
popup.bg = bg
popup.fg = fg
popup.redraw = make(chan *Popup)
return popup, nil
}
//func (popup *Popup) draw() error {
//}
// TODO: I don't know if this actually frees memory and shit.
func (popup *Popup) destroy() *Popup {
popup.win.Destroy()
popup.img.Destroy()
close(popup.redraw)
return nil
}

1
popups.go Normal file

@ -0,0 +1 @@
package main