initial work - making config file
This commit is contained in:
parent
ec0b46bf85
commit
aa195431d6
45
README.md
45
README.md
|
@ -2,20 +2,13 @@
|
|||
|
||||
melonbar - A concurrent, hackable bar/panel for X written in Go.
|
||||
|
||||
![](https://punpun.xyz/54c7.png)
|
||||
![](https:/camille.sh/LGxO.png)
|
||||
|
||||
|
||||
## INSTALLATION
|
||||
|
||||
`go get github.com/onodera-punpun/melonbar`
|
||||
|
||||
Or for a binary that includes embedded static files:
|
||||
|
||||
`packr2 get github.com/onodera-punpun/melonbar`
|
||||
|
||||
`melonbar` depends on Go 1.9 or newer, gnuplot, and
|
||||
[packr2](https://github.com/gobuffalo/packr/tree/master/v2).
|
||||
|
||||
|
||||
## USAGE
|
||||
|
||||
|
@ -23,40 +16,8 @@ The idea is that this bar is very "simple" to configure by just modifying the
|
|||
source code, à la suckless.
|
||||
|
||||
Users can configure the position, width, height and font of the bar in
|
||||
`main.go`. A bar consist of various blocks that display info, these blocks are
|
||||
functions definded in `blocks.go` and exectured in a goroutine in `main.go`.
|
||||
|
||||
|
||||
## CREATING BLOCK FUNCTIONS
|
||||
|
||||
On top of each block function you should run `bar.initBlock()`, this generates a
|
||||
block object, and is where most of the configuration happens. Here is a short
|
||||
explanation of each parameter from left to right:
|
||||
|
||||
* The name of the block, this is gets used as the name of the block map key.
|
||||
(`string`)
|
||||
* The initial string the block should display. (`string`)
|
||||
* The width of the block. (`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. (`rune`)
|
||||
* Additional x offset to further tweak the location of the text. (`int`)
|
||||
* The foreground color of the block in hexadecimal. (`string`)
|
||||
* The background color of the block in hexadecimal. (`string`)
|
||||
|
||||
It is possible to bind mousebindings to a block using using:
|
||||
|
||||
```go
|
||||
block.actions["buttonN"] = func() {
|
||||
// Do stuff.
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
When you've gathered all information you can update the block values using for
|
||||
example `block.bg = "#FF0000"` or `block.txt = "Hello World!"` and executing
|
||||
`bar.redraw <- block`.
|
||||
`main.go`. A bar consist of various blocks that display info, these blocks are
|
||||
definded in `blocks.go`.
|
||||
|
||||
|
||||
## AUTHORS
|
||||
|
|
3
TODO.md
3
TODO.md
|
@ -2,3 +2,6 @@
|
|||
* Fix weird `'` characters.
|
||||
* Fix `?` character.
|
||||
* Fix `\n` character.
|
||||
|
||||
* Check if redrawing is necaserry.
|
||||
* Test how many times a redraw is called
|
||||
|
|
100
bar.go
100
bar.go
|
@ -4,14 +4,11 @@ import (
|
|||
"fmt"
|
||||
"image"
|
||||
"log"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
"github.com/BurntSushi/xgbutil/xevent"
|
||||
"github.com/BurntSushi/xgbutil/xgraphics"
|
||||
"github.com/BurntSushi/xgbutil/xwindow"
|
||||
"github.com/elliotchance/orderedmap"
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
@ -32,16 +29,19 @@ type Bar struct {
|
|||
// Text drawer.
|
||||
drawer *font.Drawer
|
||||
|
||||
// A map with information about the block, see the `Block` type.
|
||||
blocks *sync.Map
|
||||
// A map that stores the various blocks.
|
||||
blocks *orderedmap.OrderedMap
|
||||
|
||||
// A map that stores the various popups.
|
||||
popups *orderedmap.OrderedMap
|
||||
|
||||
// Store is an interface to store variables and objects to be used by other
|
||||
// blocks or popups.
|
||||
store map[string]interface{}
|
||||
|
||||
// A channel where the block should be send to to once its ready to be
|
||||
// redrawn.
|
||||
redraw chan *Block
|
||||
|
||||
// A channel where a boolean should be send once a block has initizalized,
|
||||
// notifying that the next block can intialize.
|
||||
ready chan bool
|
||||
}
|
||||
|
||||
func initBar(x, y, w, h int) (*Bar, error) {
|
||||
|
@ -75,70 +75,33 @@ func initBar(x, y, w, h int) (*Bar, error) {
|
|||
}
|
||||
bar.img.XDraw()
|
||||
|
||||
// Set bar width and height.
|
||||
bar.w = w
|
||||
bar.h = h
|
||||
|
||||
// Set bar font face.
|
||||
bar.drawer = &font.Drawer{
|
||||
Dst: bar.img,
|
||||
Face: face,
|
||||
}
|
||||
|
||||
bar.blocks = new(sync.Map)
|
||||
// Creat blocks and popups map.
|
||||
bar.blocks = orderedmap.NewOrderedMap()
|
||||
bar.popups = orderedmap.NewOrderedMap()
|
||||
|
||||
// Create store map.
|
||||
bar.store = make(map[string]interface{})
|
||||
|
||||
// Create redraw channel.
|
||||
bar.redraw = make(chan *Block)
|
||||
|
||||
// Listen to mouse events and execute the required function.
|
||||
xevent.ButtonPressFun(func(_ *xgbutil.XUtil, ev xevent.ButtonPressEvent) {
|
||||
// Determine what block the cursor is in.
|
||||
var block *Block
|
||||
bar.blocks.Range(func(name, i interface{}) bool {
|
||||
block = i.(*Block)
|
||||
|
||||
// XXX: Hack for music block.
|
||||
if name == "music" {
|
||||
tw := font.MeasureString(face, block.txt).Ceil()
|
||||
if ev.EventX >= int16(block.x+(block.w-tw+(block.xoff*2))) &&
|
||||
ev.EventX < int16(block.x+block.w) {
|
||||
return false
|
||||
}
|
||||
block = nil
|
||||
return true
|
||||
}
|
||||
|
||||
// XXX: Hack for clock block.
|
||||
if name == "clock" {
|
||||
tw := bar.drawer.MeasureString(block.txt).Ceil()
|
||||
if ev.EventX >= int16(((bar.w/2)-(tw/2))-13) && ev.
|
||||
EventX < int16(((bar.w/2)+(tw/2))+13) {
|
||||
return false
|
||||
}
|
||||
block = nil
|
||||
return true
|
||||
}
|
||||
|
||||
if ev.EventX >= int16(block.x) && ev.EventX < int16(block.x+block.
|
||||
w) {
|
||||
return false
|
||||
}
|
||||
block = nil
|
||||
return true
|
||||
})
|
||||
|
||||
// Execute the function as specified.
|
||||
if block != nil {
|
||||
if err := block.actions["button"+strconv.Itoa(int(ev.
|
||||
Detail))](); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
}).Connect(X, bar.win.Id)
|
||||
|
||||
return bar, nil
|
||||
}
|
||||
|
||||
func (bar *Bar) draw(block *Block) error {
|
||||
// Calculate the required x coordinate for the different aligments.
|
||||
var x int
|
||||
tw := bar.drawer.MeasureString(block.txt).Ceil()
|
||||
tw := bar.drawer.MeasureString(block.txt).Round()
|
||||
switch block.align {
|
||||
case 'l':
|
||||
x = block.x
|
||||
|
@ -152,22 +115,22 @@ func (bar *Bar) draw(block *Block) error {
|
|||
return fmt.Errorf("draw %#U: Not a valid aligment rune", block.align)
|
||||
}
|
||||
x += block.xoff
|
||||
x += 2
|
||||
|
||||
// Color the background.
|
||||
block.img.For(func(cx, cy int) xgraphics.BGRA {
|
||||
// XXX: Hack for music block.
|
||||
if block.w == 660 {
|
||||
if cx < x+block.xoff {
|
||||
return hexToBGRA("#445967")
|
||||
return xgraphics.BGRA{B: 103, G: 89, R: 68, A: 0xFF}
|
||||
}
|
||||
return hexToBGRA(block.bg)
|
||||
}
|
||||
|
||||
return hexToBGRA(block.bg)
|
||||
return block.bg
|
||||
})
|
||||
|
||||
// Set text color.
|
||||
bar.drawer.Src = image.NewUniform(hexToBGRA(block.fg))
|
||||
// Set foreground color.
|
||||
bar.drawer.Src = image.NewUniform(block.fg)
|
||||
|
||||
// Draw the text.
|
||||
bar.drawer.Dot = fixed.P(x, 18)
|
||||
|
@ -180,17 +143,6 @@ func (bar *Bar) draw(block *Block) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (bar *Bar) initBlocks(blocks []func()) {
|
||||
bar.ready = make(chan bool)
|
||||
|
||||
for _, f := range blocks {
|
||||
go f()
|
||||
<-bar.ready
|
||||
}
|
||||
|
||||
close(bar.ready)
|
||||
}
|
||||
|
||||
func (bar *Bar) listen() {
|
||||
for {
|
||||
if err := bar.draw(<-bar.redraw); err != nil {
|
||||
|
|
114
block.go
114
block.go
|
@ -3,7 +3,11 @@ package main
|
|||
import (
|
||||
"image"
|
||||
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
"github.com/BurntSushi/xgbutil/xevent"
|
||||
"github.com/BurntSushi/xgbutil/xgraphics"
|
||||
"golang.org/x/image/font"
|
||||
)
|
||||
|
||||
// Block is a struct with information about a block.
|
||||
|
@ -25,56 +29,84 @@ type Block struct {
|
|||
// aligment.
|
||||
align rune
|
||||
|
||||
// The foreground and background colors in hex.
|
||||
bg, fg string
|
||||
// The foreground and background colors.
|
||||
bg, fg xgraphics.BGRA
|
||||
|
||||
// A map with functions to execute on button events. Accepted button strings
|
||||
// are `button0` to `button5`
|
||||
actions map[string]func() error
|
||||
// This boolean decides if the block is an invisible "script block", that
|
||||
// doesn't draw anything to the bar, only executes the `update` function.
|
||||
script bool
|
||||
|
||||
// Block popup..
|
||||
popup *Popup
|
||||
// The fuction that updates the block, this will be executes as a goroutine.
|
||||
update func()
|
||||
|
||||
// A map with functions to execute on button events.
|
||||
actions map[xproto.Button]func() error
|
||||
}
|
||||
|
||||
func (bar *Bar) initBlock(name, txt string, w int, align rune, xoff int, bg,
|
||||
fg string) *Block {
|
||||
block := new(Block)
|
||||
func (bar *Bar) drawBlocks() {
|
||||
for _, key := range bar.blocks.Keys() {
|
||||
block := bar.block(key.(string))
|
||||
|
||||
block.img = bar.img.SubImage(image.Rect(bar.xsum, 0, bar.xsum+w, bar.
|
||||
h)).(*xgraphics.Image)
|
||||
block.x = bar.xsum
|
||||
block.w = w
|
||||
block.xoff = xoff
|
||||
block.txt = txt
|
||||
block.align = align
|
||||
block.bg = bg
|
||||
block.fg = fg
|
||||
block.actions = map[string]func() error{
|
||||
"button1": func() error { return nil },
|
||||
"button2": func() error { return nil },
|
||||
"button3": func() error { return nil },
|
||||
"button4": func() error { return nil },
|
||||
"button5": func() error { return nil },
|
||||
// Run update function.
|
||||
go block.update()
|
||||
|
||||
// Check if the block is a script block, if that is the case, we only
|
||||
// execute the `upload` function.
|
||||
if block.script {
|
||||
continue
|
||||
}
|
||||
|
||||
// Initialize block image.
|
||||
block.img = bar.img.SubImage(image.Rect(bar.xsum, 0, bar.xsum+block.
|
||||
w, bar.h)).(*xgraphics.Image)
|
||||
|
||||
// set the block location.
|
||||
block.x = bar.xsum
|
||||
|
||||
// Add the width of this block to the xsum.
|
||||
bar.xsum += block.w
|
||||
|
||||
// Draw block.
|
||||
bar.redraw <- block
|
||||
}
|
||||
|
||||
// Add the width of this block to the xsum.
|
||||
bar.xsum += w
|
||||
// Listen to mouse events and execute the required function.
|
||||
xevent.ButtonPressFun(func(_ *xgbutil.XUtil, ev xevent.ButtonPressEvent) {
|
||||
for _, k := range bar.blocks.Keys() {
|
||||
block := bar.block(k.(string))
|
||||
|
||||
// Store the block in map.
|
||||
bar.blocks.Store(name, block)
|
||||
// Check if clicked inside the block, if not, return.
|
||||
switch k {
|
||||
// XXX: Hack for music block.
|
||||
case "music":
|
||||
tw := font.MeasureString(face, block.txt).Round()
|
||||
if ev.EventX < int16(block.x+(block.w-tw+(block.xoff*2))) || ev.
|
||||
EventX > int16(block.x+block.w) {
|
||||
continue
|
||||
}
|
||||
// XXX: Hack for clock block.
|
||||
case "clock":
|
||||
tw := bar.drawer.MeasureString(block.txt).Ceil()
|
||||
if ev.EventX < int16(((bar.w/2)-(tw/2))-13) || ev.
|
||||
EventX > int16(((bar.w/2)+(tw/2))+13) {
|
||||
continue
|
||||
}
|
||||
default:
|
||||
if ev.EventX < int16(block.x) || ev.EventX > int16(block.x+block.
|
||||
w) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Draw block.
|
||||
bar.redraw <- block
|
||||
|
||||
return block
|
||||
// Execute the function as specified.
|
||||
if _, ok := block.actions[ev.Detail]; ok {
|
||||
go block.actions[ev.Detail]()
|
||||
}
|
||||
}
|
||||
}).Connect(X, bar.win.Id)
|
||||
}
|
||||
|
||||
// TODO: Make this function more versatile by allowing different and multiple
|
||||
// properties to be checked.
|
||||
func (block *Block) diff(txt string) bool {
|
||||
if block.txt == txt {
|
||||
return false
|
||||
}
|
||||
block.txt = txt
|
||||
return true
|
||||
func (bar *Bar) block(key string) *Block {
|
||||
i, _ := bar.blocks.Get(key)
|
||||
return i.(*Block)
|
||||
}
|
||||
|
|
652
blocks.go
652
blocks.go
|
@ -4,317 +4,409 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
"github.com/BurntSushi/xgbutil/ewmh"
|
||||
"github.com/BurntSushi/xgbutil/icccm"
|
||||
"github.com/BurntSushi/xgbutil/xevent"
|
||||
"github.com/BurntSushi/xgbutil/xgraphics"
|
||||
"github.com/BurntSushi/xgbutil/xprop"
|
||||
"github.com/fhs/gompd/mpd"
|
||||
"github.com/rkoesters/xdg/basedir"
|
||||
"github.com/BurntSushi/xgbutil/xwindow"
|
||||
// "github.com/fhs/gompd/mpd"
|
||||
)
|
||||
|
||||
func (bar *Bar) clock() {
|
||||
// Initialize block.
|
||||
block := bar.initBlock("clock", "?", 799, 'a', 0, "#445967", "#CCCCCC")
|
||||
func (bar *Bar) initBlocks() {
|
||||
bar.blocks.Set("window", &Block{
|
||||
txt: "?",
|
||||
w: activeWinSize,
|
||||
align: 'c',
|
||||
xoff: 0,
|
||||
bg: xgraphics.BGRA{B: 65, G: 0, R: 0, A: 0xFF},
|
||||
fg: xgraphics.BGRA{B: 255, G: 255, R: 255, A: 0xFF},
|
||||
|
||||
// Notify that the next block can be initialized.
|
||||
bar.ready <- true
|
||||
update: func() {
|
||||
block := bar.block("window")
|
||||
|
||||
// Show popup on clicking the left mouse button.
|
||||
block.actions["button1"] = func() error {
|
||||
if block.popup != nil {
|
||||
block.popup = block.popup.destroy()
|
||||
return nil
|
||||
}
|
||||
// Redraw block function.
|
||||
t := func(id xproto.Window) {
|
||||
// Set new block text.
|
||||
txt, err := ewmh.WmNameGet(X, id)
|
||||
if err != nil || len(txt) == 0 {
|
||||
txt, err = icccm.WmNameGet(X, id)
|
||||
if err != nil || len(txt) == 0 {
|
||||
txt = "?"
|
||||
}
|
||||
}
|
||||
txt = trim(txt, 34)
|
||||
|
||||
var err error
|
||||
block.popup, err = initPopup((bar.w/2)-(178/2), bar.h, 178, 129,
|
||||
"#EEEEEE", "#021B21")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Return if the text is the same.
|
||||
if txt == block.txt {
|
||||
return
|
||||
}
|
||||
block.txt = txt
|
||||
|
||||
return block.popup.clock()
|
||||
}
|
||||
// Redraw block.
|
||||
bar.redraw <- block
|
||||
}
|
||||
|
||||
for {
|
||||
// Compose block text.
|
||||
txt := time.Now().Format("Monday, January 2th 03:04 PM")
|
||||
// Variable where we store the (previous) xwindow.
|
||||
var xid *xwindow.Window
|
||||
|
||||
// Redraw block.
|
||||
if block.diff(txt) {
|
||||
bar.redraw <- block
|
||||
}
|
||||
|
||||
// Update every 45 seconds.
|
||||
time.Sleep(45 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func (bar *Bar) music() {
|
||||
// Initialize block.
|
||||
block := bar.initBlock("music", "» ", 660, 'r', -12, "#3C4F5B", "#CCCCCC")
|
||||
|
||||
// Notify that the next block can be initialized.
|
||||
bar.ready <- true
|
||||
|
||||
// Connect to MPD.
|
||||
c, err := mpd.Dial("tcp", ":6600")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// Keep connection alive by pinging ever 45 seconds.
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(time.Second * 45)
|
||||
|
||||
if err := c.Ping(); err != nil {
|
||||
c, err = mpd.Dial("tcp", ":6600")
|
||||
// Get window ID function.
|
||||
f := func() {
|
||||
// Get active window.
|
||||
id, err := ewmh.ActiveWindowGet(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Show popup on clicking the left mouse button.
|
||||
block.actions["button1"] = func() error {
|
||||
if block.popup != nil {
|
||||
block.popup = block.popup.destroy()
|
||||
return nil
|
||||
}
|
||||
|
||||
block.popup, err = initPopup(bar.w-304-29, bar.h, 304, 148, "#EEEEEE",
|
||||
"#021B21")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return block.popup.music(c)
|
||||
}
|
||||
|
||||
// Toggle play/pause on clicking the right mouse button.
|
||||
block.actions["button3"] = func() error {
|
||||
status, err := c.Status()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Pause(status["state"] != "pause")
|
||||
}
|
||||
|
||||
// Previous song on scrolling up.
|
||||
block.actions["button4"] = func() error {
|
||||
return c.Previous()
|
||||
}
|
||||
|
||||
// Next song on on scrolling down..
|
||||
block.actions["button5"] = func() error {
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
// Watch MPD for events.
|
||||
w, err := mpd.NewWatcher("tcp", ":6600", "", "player")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
for {
|
||||
cur, err := c.CurrentSong()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
sts, err := c.Status()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
// Compose text.
|
||||
var s string
|
||||
if sts["state"] == "pause" {
|
||||
s = "[paused] "
|
||||
}
|
||||
txt := "» " + s + cur["Artist"] + " - " + cur["Title"]
|
||||
|
||||
// Redraw block.
|
||||
if block.diff(txt) {
|
||||
bar.redraw <- block
|
||||
|
||||
// Redraw popup.
|
||||
if block.popup != nil {
|
||||
if err := block.popup.music(c); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if id == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Stop listening to the previous window.
|
||||
if xid != nil {
|
||||
xid.Detach()
|
||||
}
|
||||
|
||||
// Create xwindow from active window.
|
||||
xid = xwindow.New(X, id)
|
||||
|
||||
// Listen to this window for window name changes.
|
||||
xid.Listen(xproto.EventMaskPropertyChange)
|
||||
xevent.PropertyNotifyFun(func(_ *xgbutil.XUtil, ev xevent.
|
||||
PropertyNotifyEvent) {
|
||||
// Only listen to `_NET_WM_NAME` events.
|
||||
atom, err := xprop.Atm(X, "_NET_WM_NAME")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if ev.Atom != atom {
|
||||
return
|
||||
}
|
||||
|
||||
t(id)
|
||||
}).Connect(X, id)
|
||||
|
||||
t(id)
|
||||
}
|
||||
}
|
||||
|
||||
<-w.Event
|
||||
}
|
||||
}
|
||||
// Listen for window change event, execute `f()` accordingly.
|
||||
xevent.PropertyNotifyFun(func(_ *xgbutil.XUtil, ev xevent.
|
||||
PropertyNotifyEvent) {
|
||||
// Only listen to `_NET_ACTIVE_WINDOW` events.
|
||||
//atom, err := xprop.Atm(X, "_NET_ACTIVE_WINDOW")
|
||||
//if err != nil {
|
||||
// log.Println(err)
|
||||
// return
|
||||
//}
|
||||
//if ev.Atom != atom {
|
||||
// return
|
||||
//}
|
||||
|
||||
func (bar *Bar) todo() {
|
||||
// Initialize block.
|
||||
block := bar.initBlock("todo", "¢", bar.h, 'c', 0, "#5394C9", "#FFFFFF")
|
||||
f()
|
||||
}).Connect(X, X.RootWin())
|
||||
|
||||
// Notify that the next block can be initialized.
|
||||
bar.ready <- true
|
||||
// Execute `f()` one time initially.
|
||||
f()
|
||||
},
|
||||
})
|
||||
|
||||
// Show popup on clicking the left mouse button.
|
||||
block.actions["button1"] = func() error {
|
||||
cmd := exec.Command("st", "micro", "-savecursor", "false", path.Join(
|
||||
basedir.Home, ".todo"))
|
||||
cmd.Stdout = os.Stdout
|
||||
return cmd.Run()
|
||||
}
|
||||
bar.blocks.Set("workspace-1", &Block{
|
||||
txt: "1",
|
||||
w: workspaceWidth,
|
||||
align: 'l',
|
||||
xoff: 12,
|
||||
bg: xgraphics.BGRA{B: 0, G: 0, R: 0, A: 0xFF},
|
||||
fg: xgraphics.BGRA{B: 255, G: 255, R: 255, A: 0xFF},
|
||||
|
||||
// Show count popup.
|
||||
var err error
|
||||
block.popup, err = initPopup(bar.w-19-16, bar.h-8, 16, 12, "#EB6084",
|
||||
"#FFFFFF")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
if err := block.popup.todo(); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
update: func() {},
|
||||
|
||||
func (bar *Bar) window() {
|
||||
// Initialize blocks.
|
||||
bar.initBlock("windowIcon", "¶", 21, 'l', 12, "#37BF8D", "#FFFFFF")
|
||||
block := bar.initBlock("window", "?", 200, 'c', 0, "#37BF8D", "#FFFFFF")
|
||||
actions: map[xproto.Button]func() error{
|
||||
1: func() error {
|
||||
return ewmh.CurrentDesktopReq(X, 0)
|
||||
},
|
||||
4: func() error {
|
||||
return ewmh.CurrentDesktopReq(X, bar.store["prev"].(int))
|
||||
},
|
||||
5: func() error {
|
||||
return ewmh.CurrentDesktopReq(X, bar.store["next"].(int))
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Notify that the next block can be initialized.
|
||||
bar.ready <- true
|
||||
bar.blocks.Set("workspace-2", &Block{
|
||||
txt: "2",
|
||||
w: workspaceWidth,
|
||||
align: 'l',
|
||||
xoff: 12,
|
||||
bg: xgraphics.BGRA{B: 0, G: 0, R: 0, A: 0xFF},
|
||||
fg: xgraphics.BGRA{B: 255, G: 255, R: 255, A: 0xFF},
|
||||
|
||||
// TODO: This doesn't check for window title changes.
|
||||
xevent.PropertyNotifyFun(func(_ *xgbutil.XUtil, ev xevent.
|
||||
PropertyNotifyEvent) {
|
||||
// Only listen to `_NET_ACTIVE_WINDOW` events.
|
||||
atom, err := xprop.Atm(X, "_NET_ACTIVE_WINDOW")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if ev.Atom != atom {
|
||||
return
|
||||
}
|
||||
update: func() {},
|
||||
|
||||
// Get active window.
|
||||
id, err := ewmh.ActiveWindowGet(X)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if id == 0 {
|
||||
return
|
||||
}
|
||||
actions: map[xproto.Button]func() error{
|
||||
1: func() error {
|
||||
return ewmh.CurrentDesktopReq(X, 1)
|
||||
},
|
||||
4: func() error {
|
||||
return ewmh.CurrentDesktopReq(X, bar.store["prev"].(int))
|
||||
},
|
||||
5: func() error {
|
||||
return ewmh.CurrentDesktopReq(X, bar.store["next"].(int))
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Compose block text.
|
||||
txt, err := ewmh.WmNameGet(X, id)
|
||||
if err != nil || len(txt) == 0 {
|
||||
txt, err = icccm.WmNameGet(X, id)
|
||||
if err != nil || len(txt) == 0 {
|
||||
txt = "?"
|
||||
bar.blocks.Set("workspace-3", &Block{
|
||||
txt: "3",
|
||||
w: workspaceWidth,
|
||||
align: 'l',
|
||||
xoff: 12,
|
||||
bg: xgraphics.BGRA{B: 0, G: 0, R: 0, A: 0xFF},
|
||||
fg: xgraphics.BGRA{B: 255, G: 255, R: 255, A: 0xFF},
|
||||
|
||||
update: func() {},
|
||||
|
||||
actions: map[xproto.Button]func() error{
|
||||
1: func() error {
|
||||
return ewmh.CurrentDesktopReq(X, 2)
|
||||
},
|
||||
4: func() error {
|
||||
return ewmh.CurrentDesktopReq(X, bar.store["prev"].(int))
|
||||
},
|
||||
5: func() error {
|
||||
return ewmh.CurrentDesktopReq(X, bar.store["next"].(int))
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
bar.blocks.Set("workspace", &Block{
|
||||
script: true,
|
||||
|
||||
update: func() {
|
||||
one := bar.block("workspace-1")
|
||||
two := bar.block("workspace-2")
|
||||
three := bar.block("workspace-3")
|
||||
|
||||
f := func() {
|
||||
// Get the current active desktop.
|
||||
wsp, err := ewmh.CurrentDesktopGet(X)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// KAYOS FIX THIS MAKE IT A CONFIG
|
||||
var chosenbg xgraphics.BGRA = xgraphics.BGRA{B: 255, G: 255, R: 255, A: 0xFF}
|
||||
var chosenfg xgraphics.BGRA = xgraphics.BGRA{B: 0, G: 0, R: 0, A: 0xFF}
|
||||
|
||||
// Set new block colors and the previous/next workspaces.
|
||||
switch wsp {
|
||||
case 0:
|
||||
one.bg = chosenbg
|
||||
one.fg = chosenfg
|
||||
two.bg = xgraphics.BGRA{B: 0, G: 0, R: 0, A: 0xFF}
|
||||
three.bg = xgraphics.BGRA{B: 0, G: 0, R: 0, A: 0xFF}
|
||||
|
||||
bar.store["prev"] = 2
|
||||
bar.store["next"] = 1
|
||||
case 1:
|
||||
one.bg = xgraphics.BGRA{B: 0, G: 0, R: 0, A: 0xFF}
|
||||
two.bg = chosenbg
|
||||
two.fg = chosenfg
|
||||
three.bg = xgraphics.BGRA{B: 0, G: 0, R: 0, A: 0xFF}
|
||||
|
||||
bar.store["prev"] = 0
|
||||
bar.store["next"] = 2
|
||||
case 2:
|
||||
one.bg = xgraphics.BGRA{B: 0, G: 0, R: 0, A: 0xFF}
|
||||
two.bg = xgraphics.BGRA{B: 0, G: 0, R: 0, A: 0xFF}
|
||||
three.bg =chosenbg
|
||||
three.fg =chosenfg
|
||||
|
||||
bar.store["prev"] = 1
|
||||
bar.store["next"] = 0
|
||||
}
|
||||
|
||||
// Redraw block.
|
||||
bar.redraw <- one
|
||||
bar.redraw <- two
|
||||
bar.redraw <- three
|
||||
}
|
||||
}
|
||||
txt = trim(txt, 34)
|
||||
|
||||
// Redraw block.
|
||||
if block.diff(txt) {
|
||||
bar.redraw <- block
|
||||
}
|
||||
}).Connect(X, X.RootWin())
|
||||
// Listen for workspace change event, execute `f()` accordingly.
|
||||
xevent.PropertyNotifyFun(func(_ *xgbutil.XUtil, ev xevent.
|
||||
PropertyNotifyEvent) {
|
||||
// Only listen to `_NET_ACTIVE_WINDOW` events.
|
||||
atom, err := xprop.Atm(X, "_NET_CURRENT_DESKTOP")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if ev.Atom != atom {
|
||||
return
|
||||
}
|
||||
|
||||
f()
|
||||
}).Connect(X, X.RootWin())
|
||||
|
||||
// Execute `f()` one time initially.
|
||||
f()
|
||||
},
|
||||
})
|
||||
|
||||
bar.blocks.Set("clock", &Block{
|
||||
txt: "?",
|
||||
w: 799,
|
||||
align: 'a',
|
||||
xoff: 0,
|
||||
bg: xgraphics.BGRA{B: 0, G: 0, R: 0, A: 0xFF},
|
||||
fg: xgraphics.BGRA{B: 255, G: 247, R: 91, A: 0xFF},
|
||||
|
||||
update: func() {
|
||||
for {
|
||||
block := bar.block("clock")
|
||||
|
||||
// Set new block text.
|
||||
block.txt = time.Now().Format("Monday, January 2th 03:04 PM")
|
||||
|
||||
// Redraw block.
|
||||
bar.redraw <- block
|
||||
|
||||
// Update every 45 seconds.
|
||||
time.Sleep(45 * time.Second)
|
||||
}
|
||||
},
|
||||
|
||||
/* actions: map[xproto.Button]func() error{
|
||||
1: func() error {
|
||||
return bar.drawPopup("clock")
|
||||
},
|
||||
},*/
|
||||
})
|
||||
|
||||
/* bar.blocks.Set("music", &Block{
|
||||
txt: " Ƅ ",
|
||||
w: 660,
|
||||
align: 'r',
|
||||
xoff: -12,
|
||||
bg: xgraphics.BGRA{B: 91, G: 79, R: 60, A: 0xFF},
|
||||
fg: xgraphics.BGRA{B: 204, G: 204, R: 204, A: 0xFF},
|
||||
|
||||
update: func() {
|
||||
block := bar.block("music")
|
||||
popup := bar.popup("music")
|
||||
|
||||
// Connect to MPD.
|
||||
var err error
|
||||
bar.store["mpd"], err = mpd.Dial("tcp", ":6600")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// Keep connection alive by pinging ever 45 seconds.
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(time.Second * 45)
|
||||
|
||||
if err := bar.store["mpd"].(*mpd.Client).
|
||||
Ping(); err != nil {
|
||||
bar.store["mpd"], err = mpd.Dial("tcp", ":6600")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Watch MPD for events.
|
||||
w, err := mpd.NewWatcher("tcp", ":6600", "", "player")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
for {
|
||||
cur, err := bar.store["mpd"].(*mpd.Client).CurrentSong()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
sts, err := bar.store["mpd"].(*mpd.Client).Status()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Set new block text.
|
||||
var s string
|
||||
if sts["state"] == "pause" {
|
||||
s = "[paused] "
|
||||
}
|
||||
block.txt = " Ƅ " + s + cur["Artist"] + " - " + cur["Title"]
|
||||
|
||||
// Redraw block.
|
||||
bar.redraw <- block
|
||||
|
||||
// Update popup if open.
|
||||
if popup.open {
|
||||
popup.update()
|
||||
}
|
||||
|
||||
// Wait for next event.
|
||||
<-w.Event
|
||||
}
|
||||
},*/
|
||||
|
||||
/* actions: map[xproto.Button]func() error{
|
||||
1: func() error {
|
||||
return bar.drawPopup("music")
|
||||
},
|
||||
3: func() error {
|
||||
s, err := bar.store["mpd"].(*mpd.Client).Status()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bar.store["mpd"].(*mpd.Client).
|
||||
Pause(s["state"] != "pause")
|
||||
},
|
||||
4: func() error {
|
||||
return bar.store["mpd"].(*mpd.Client).Previous()
|
||||
},
|
||||
5: func() error {
|
||||
return bar.store["mpd"].(*mpd.Client).Next()
|
||||
},
|
||||
},
|
||||
})*/
|
||||
|
||||
bar.blocks.Set("todo", &Block{
|
||||
txt: "ƅ",
|
||||
w: bar.h,
|
||||
align: 'c',
|
||||
xoff: 1,
|
||||
bg: xgraphics.BGRA{B: 0, G: 0, R: 0, A: 0xFF},
|
||||
fg: xgraphics.BGRA{B: 255, G: 255, R: 255, A: 0xFF},
|
||||
|
||||
update: func() {},
|
||||
|
||||
actions: map[xproto.Button]func() error{
|
||||
1: func() error {
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.Command("st", "nano", "-savecursor", "false", path.
|
||||
Join(u.HomeDir, ".todo"))
|
||||
cmd.Stdout = os.Stdout
|
||||
return cmd.Start()
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (bar *Bar) workspace() {
|
||||
// Initialize block.
|
||||
blockWWW := bar.initBlock("www", "¼ www", 74, 'l', 10, "#5394C9",
|
||||
"#FFFFFF")
|
||||
blockIRC := bar.initBlock("irc", "½ irc", 67, 'l', 10, "#5394C9",
|
||||
"#FFFFFF")
|
||||
blockSRC := bar.initBlock("src", "¾ src", 70, 'l', 10, "#5394C9",
|
||||
"#FFFFFF")
|
||||
|
||||
// Notify that the next block can be initialized.
|
||||
bar.ready <- true
|
||||
|
||||
// Change active workspace on clicking on one of the blocks.
|
||||
blockWWW.actions["button1"] = func() error {
|
||||
return ewmh.CurrentDesktopReq(X, 0)
|
||||
}
|
||||
blockIRC.actions["button1"] = func() error {
|
||||
return ewmh.CurrentDesktopReq(X, 1)
|
||||
}
|
||||
blockSRC.actions["button1"] = func() error {
|
||||
return ewmh.CurrentDesktopReq(X, 2)
|
||||
}
|
||||
|
||||
var owsp uint
|
||||
var pwsp, nwsp int
|
||||
xevent.PropertyNotifyFun(func(_ *xgbutil.XUtil, ev xevent.
|
||||
PropertyNotifyEvent) {
|
||||
// Only listen to `_NET_ACTIVE_WINDOW` events.
|
||||
atom, err := xprop.Atm(X, "_NET_CURRENT_DESKTOP")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if ev.Atom != atom {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the current active desktop.
|
||||
wsp, err := ewmh.CurrentDesktopGet(X)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
// Set colors accordingly.
|
||||
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 {
|
||||
bar.redraw <- blockWWW
|
||||
bar.redraw <- blockIRC
|
||||
bar.redraw <- blockSRC
|
||||
|
||||
owsp = wsp
|
||||
}
|
||||
}).Connect(X, X.RootWin())
|
||||
|
||||
prevFun := func() error {
|
||||
return ewmh.CurrentDesktopReq(X, pwsp)
|
||||
}
|
||||
nextFun := func() error {
|
||||
return ewmh.CurrentDesktopReq(X, nwsp)
|
||||
}
|
||||
|
||||
blockWWW.actions["button4"] = prevFun
|
||||
blockWWW.actions["button5"] = nextFun
|
||||
blockIRC.actions["button4"] = prevFun
|
||||
blockIRC.actions["button5"] = nextFun
|
||||
blockSRC.actions["button4"] = prevFun
|
||||
blockSRC.actions["button5"] = nextFun
|
||||
}
|
||||
|
|
31
main.go
31
main.go
|
@ -2,20 +2,21 @@ package main
|
|||
|
||||
import (
|
||||
"log"
|
||||
"embed"
|
||||
|
||||
"github.com/AndreKR/multiface"
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"golang.org/x/image/font"
|
||||
)
|
||||
|
||||
var (
|
||||
box = packr.New("box", "./box")
|
||||
//go:embed runtime/*
|
||||
runtime embed.FS
|
||||
|
||||
// Connection to the X server.
|
||||
X *xgbutil.XUtil
|
||||
|
||||
// The font that should be used.
|
||||
face font.Face
|
||||
// The multifont face that should be used.
|
||||
face *multiface.Face
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -24,25 +25,23 @@ func main() {
|
|||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// Initialize font.
|
||||
if err := initFont(); err != nil {
|
||||
// Initialize font face.
|
||||
if err := initFace(); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// Initialize bar.
|
||||
bar, err := initBar(0, 0, 1920, 29)
|
||||
bar, err := initBar(0, 965, 1366, 29)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// Initialize blocks.
|
||||
go bar.initBlocks([]func(){
|
||||
bar.window,
|
||||
bar.workspace,
|
||||
bar.clock,
|
||||
bar.music,
|
||||
bar.todo,
|
||||
})
|
||||
// Initialize blocks and popups.
|
||||
bar.initBlocks()
|
||||
//bar.initPopups()
|
||||
|
||||
// Draw blocks.
|
||||
go bar.drawBlocks()
|
||||
|
||||
// Listen for redraw events.
|
||||
bar.listen()
|
||||
|
|
75
popup.go
75
popup.go
|
@ -15,79 +15,86 @@ type Popup struct {
|
|||
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
|
||||
// The position, width and height of the popup.
|
||||
x, y, w, h int
|
||||
|
||||
// Text drawer.
|
||||
drawer *font.Drawer
|
||||
|
||||
// If the popup is currently open or not.
|
||||
open bool
|
||||
|
||||
// The fuction that updates the block, this will be executes as a goroutine.
|
||||
update func()
|
||||
}
|
||||
|
||||
func initPopup(x, y, w, h int, bg, fg string) (*Popup, error) {
|
||||
popup := new(Popup)
|
||||
var err error
|
||||
func (bar *Bar) drawPopup(key string) error {
|
||||
popup := bar.popup(key)
|
||||
|
||||
// Create a window for the bar. This window listens to button press events
|
||||
// in order to respond to them.
|
||||
// If the popup is already open, we destroy it.
|
||||
if popup.open {
|
||||
popup.destroy()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a window for the popup. This window listens to button press
|
||||
// events in order to respond to them.
|
||||
var err error
|
||||
popup.win, err = xwindow.Generate(X)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
popup.win.Create(X.RootWin(), x, y, w, h, xproto.CwBackPixel|xproto.
|
||||
CwEventMask, 0x000000, xproto.EventMaskButtonPress)
|
||||
popup.win.Create(X.RootWin(), popup.x, popup.y, popup.w, popup.h, xproto.
|
||||
CwBackPixel|xproto.CwEventMask, 0x000000, xproto.EventMaskButtonPress)
|
||||
|
||||
// EWMH stuff.
|
||||
if err := initEWMH(popup.win.Id); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
// Map window.
|
||||
popup.win.Map()
|
||||
|
||||
// TODO: Moving the window is again a hack to keep OpenBox happy.
|
||||
popup.win.Move(x, y)
|
||||
// XXX: Moving the window is again a hack to keep OpenBox happy.
|
||||
popup.win.Move(popup.x, popup.y)
|
||||
|
||||
// Create the bar image.
|
||||
popup.img = xgraphics.New(X, image.Rect(0, 0, w, h))
|
||||
// Create the popup image.
|
||||
popup.img = xgraphics.New(X, image.Rect(0, 0, popup.w, popup.h))
|
||||
if err := popup.img.XSurfaceSet(popup.win.Id); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
popup.img.XDraw()
|
||||
|
||||
popup.w = w
|
||||
popup.h = h
|
||||
|
||||
popup.bg = bg
|
||||
popup.fg = fg
|
||||
|
||||
// Set popup font face.
|
||||
popup.drawer = &font.Drawer{
|
||||
Dst: popup.img,
|
||||
Face: face,
|
||||
}
|
||||
|
||||
// Color the background.
|
||||
popup.img.For(func(cx, cy int) xgraphics.BGRA {
|
||||
return hexToBGRA(popup.bg)
|
||||
})
|
||||
// Run update function.
|
||||
popup.update()
|
||||
|
||||
// Draw the popup.
|
||||
popup.draw()
|
||||
return nil
|
||||
}
|
||||
|
||||
return popup, nil
|
||||
func (bar *Bar) popup(key string) *Popup {
|
||||
i, _ := bar.popups.Get(key)
|
||||
return i.(*Popup)
|
||||
}
|
||||
|
||||
func (popup *Popup) draw() {
|
||||
popup.img.XDraw()
|
||||
popup.img.XPaint(popup.win.Id)
|
||||
|
||||
// Set popup status to open.
|
||||
popup.open = true
|
||||
}
|
||||
|
||||
// TODO: I don't know if this actually frees memory and shit.
|
||||
func (popup *Popup) destroy() *Popup {
|
||||
func (popup *Popup) destroy() {
|
||||
popup.win.Destroy()
|
||||
popup.img.Destroy()
|
||||
popup = nil
|
||||
|
||||
return popup
|
||||
// Set popup status to closed.
|
||||
popup.open = false
|
||||
}
|
||||
|
|
600
popups.go
600
popups.go
|
@ -1,272 +1,398 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strconv"
|
||||
"time"
|
||||
// "image"
|
||||
// "log"
|
||||
// "math"
|
||||
// "os"
|
||||
// "path"
|
||||
// "strconv"
|
||||
// "time"
|
||||
|
||||
"github.com/BurntSushi/xgbutil/xgraphics"
|
||||
"github.com/fhs/gompd/mpd"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/rkoesters/xdg/basedir"
|
||||
"github.com/rkoesters/xdg/userdirs"
|
||||
"github.com/thedevsaddam/gojsonq"
|
||||
"golang.org/x/image/math/fixed"
|
||||
// "github.com/BurntSushi/xgbutil/xgraphics"
|
||||
// "github.com/IvanMenshykov/MoonPhase"
|
||||
// "github.com/RadhiFadlillah/go-prayer"
|
||||
// "github.com/elliotchance/orderedmap"
|
||||
// "github.com/fhs/gompd/mpd"
|
||||
// "github.com/rkoesters/xdg/userdirs"
|
||||
// "golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
func (popup *Popup) clock() error {
|
||||
// Color the background.
|
||||
f, err := box.Open("images/clock-popup-bg.png")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
func (bar *Bar) initPopups() {
|
||||
/*bar.popups.Set("clock", &Popup{
|
||||
x: (bar.w / 2) - (184 / 2),
|
||||
y: bar.h,
|
||||
w: 184,
|
||||
h: 118,
|
||||
|
||||
// Draw the background.
|
||||
bg, _, err := image.Decode(f.(io.Reader))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
xgraphics.Blend(popup.img, xgraphics.NewConvert(X, bg), image.Point{0, 0})
|
||||
update: func() {
|
||||
popup := bar.popup("clock")
|
||||
|
||||
// Redraw the popup.
|
||||
popup.draw()
|
||||
// Color the background.
|
||||
popup.img.For(func(cx, cy int) xgraphics.BGRA {
|
||||
return xgraphics.BGRA{B: 238, G: 238, R: 238, A: 0xFF}
|
||||
})
|
||||
|
||||
// Creat http client with a timeout.
|
||||
c := &http.Client{Timeout: time.Duration(2 * time.Second)}
|
||||
// Set foreground color.
|
||||
popup.drawer.Src = image.NewUniform(xgraphics.BGRA{B: 33, G: 27,
|
||||
R: 2, A: 0xFF})
|
||||
|
||||
// Get weather information.
|
||||
r, err := c.Get("https://api.buienradar.nl/data/public/2.0/jsonfeed")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
jq := gojsonq.New().Reader(r.Body).From("actual.stationmeasurements.[0]").
|
||||
WhereEqual("stationid", "6260")
|
||||
// Get the current time. Present Day, heh... Present Time! Hahahaha!
|
||||
n := time.Now()
|
||||
|
||||
// Draw weather information.
|
||||
popup.drawer.Src = image.NewUniform(hexToBGRA(popup.fg))
|
||||
popup.drawer.Dot = fixed.P(11, 101)
|
||||
popup.drawer.DrawString("Rainfall graph, it's " + fmt.Sprint(jq.Find(
|
||||
"feeltemperature")) + "°C.")
|
||||
// Get moon phase.
|
||||
/* mc := MoonPhase.New(n)
|
||||
mn := map[int]string{
|
||||
0: "Ɔ",
|
||||
1: "Ƈ",
|
||||
2: "ƈ",
|
||||
3: "Ɖ",
|
||||
4: "Ɗ",
|
||||
5: "Ƌ",
|
||||
6: "ƌ",
|
||||
7: "ƍ",
|
||||
8: "Ƈ",
|
||||
}
|
||||
|
||||
// Redraw the popup.
|
||||
popup.draw()
|
||||
// Draw moon text.
|
||||
popup.drawer.Dot = fixed.P(19, 48)
|
||||
popup.drawer.DrawString("The moon currently looks like: " + mn[int(
|
||||
math.Floor((mc.Phase()+0.0625)*8))])
|
||||
|
||||
// Set location.
|
||||
lat := "52.0646"
|
||||
lon := "5.2065"
|
||||
// Get the prayers.
|
||||
pm := (&prayer.Calculator{
|
||||
Latitude: 52.1277,
|
||||
Longitude: 5.6686,
|
||||
Elevation: 21,
|
||||
CalculationMethod: prayer.MWL,
|
||||
AsrConvention: prayer.Hanafi,
|
||||
PreciseToSeconds: false,
|
||||
}).Init().SetDate(n).Calculate()
|
||||
|
||||
// Get rainfall information.
|
||||
r, err = c.Get("https://gpsgadget.buienradar.nl/data/raintext?lat=" + lat +
|
||||
"&lon=" + lon)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
// The prayers we want to track.
|
||||
pom := orderedmap.NewOrderedMap()
|
||||
pom.Set("Fajr", pm[prayer.Fajr])
|
||||
pom.Set("Zuhr", pm[prayer.Zuhr])
|
||||
pom.Set("Asr", pm[prayer.Asr])
|
||||
pom.Set("Maghrib", pm[prayer.Maghrib])
|
||||
pom.Set("Isha", pm[prayer.Isha])
|
||||
|
||||
// Create rainfall tmp files.
|
||||
td, err := ioutil.TempFile(os.TempDir(), "melonbar-rain-*.dat")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(td.Name())
|
||||
ti, err := ioutil.TempFile(os.TempDir(), "melonbar-rain-*.png")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(ti.Name())
|
||||
// Calculate the dot lenght, this is the length of the line (164px)
|
||||
// divided by 2400 (minutes in a day).
|
||||
d := 164.00 / 2400.00
|
||||
|
||||
// Compose rainfall data tmp file contents.
|
||||
var d []byte
|
||||
s := bufio.NewScanner(r.Body)
|
||||
for s.Scan() {
|
||||
d = append(d, bytes.Split(s.Bytes(), []byte("|"))[0]...)
|
||||
d = append(d, []byte("\n")...)
|
||||
}
|
||||
// Calculate elapsed line length.
|
||||
e := int(math.Round(d*float64(n.Hour()*100+n.Minute()))) + 10
|
||||
|
||||
// Write rainfall data tmp file.
|
||||
if _, err = td.Write(d); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := td.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Draw line.
|
||||
popup.img.SubImage(image.Rect(10, 101, 10+164, 102)).(*xgraphics.
|
||||
Image).For(func(x, y int) xgraphics.BGRA {
|
||||
// Make the line look dashed.
|
||||
if x%5 == 4 {
|
||||
return xgraphics.BGRA{B: 238, G: 238, R: 238, A: 0xFF}
|
||||
}
|
||||
|
||||
// Create rainfall graph.
|
||||
cmd := exec.Command("gnuplot", "-e", `
|
||||
set terminal png transparent size 211,107;
|
||||
set output '`+ti.Name()+`';
|
||||
set yrange [0:255];
|
||||
set noborder;
|
||||
set nolabel;
|
||||
set nokey;
|
||||
set notics;
|
||||
set notitle;
|
||||
set style fill solid border rgb '#5394C9';
|
||||
plot '`+td.Name()+`' smooth csplines with filledcurve x1 lc rgb '#72A7D3'
|
||||
`)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
if x < e {
|
||||
return xgraphics.BGRA{B: 33, G: 27, R: 2, A: 0xFF}
|
||||
}
|
||||
return xgraphics.BGRA{B: 211, G: 167, R: 114, A: 0xFF}
|
||||
})
|
||||
|
||||
// Draw rainfall graph.
|
||||
img, _, err := image.Decode(ti)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
xgraphics.Blend(popup.img, xgraphics.NewConvert(X, img), image.Point{14, 0})
|
||||
// Loop over these prayers and draw stuff for each one.
|
||||
tm := false
|
||||
np := false
|
||||
for p := pom.Front(); p != nil; p = p.Next() {
|
||||
k := p.Key.(string)
|
||||
v := p.Value.(time.Time)
|
||||
|
||||
// Redraw the popup.
|
||||
popup.draw()
|
||||
// Calculate arrow position.
|
||||
pd := int(math.Round(d*float64(v.Hour()*100+v.Minute()))) + 9
|
||||
|
||||
return nil
|
||||
}
|
||||
// Set arrow color.
|
||||
popup.drawer.Src = image.NewUniform(xgraphics.BGRA{B: 211,
|
||||
G: 167, R: 114, A: 0xFF})
|
||||
|
||||
// TODO: Make progressbar clickable.
|
||||
// TODO: Make progressbar update every X milliseconds.
|
||||
func (popup *Popup) music(c *mpd.Client) error {
|
||||
// Color the background.
|
||||
popup.img.For(func(cx, cy int) xgraphics.BGRA {
|
||||
return hexToBGRA(popup.bg)
|
||||
if tm || (!np && v.Unix() > n.Add(-time.Hour).Unix()) {
|
||||
np = true
|
||||
|
||||
// Set arrow color for next prayer.
|
||||
popup.drawer.Src = image.NewUniform(xgraphics.BGRA{B: 33,
|
||||
G: 27, R: 2, A: 0xFF})
|
||||
|
||||
// Compose arrow text.
|
||||
s := k + ", " + v.Format("03:04 PM")
|
||||
|
||||
// Calculate X offset, we use some smart logic in order to
|
||||
// always have nice padding, even with longer strings.
|
||||
sl := popup.drawer.MeasureString(s).Round()
|
||||
x := pd + 2 - (sl / 2)
|
||||
if x < 10 {
|
||||
x = 10
|
||||
} else if x > 184-8-sl {
|
||||
x = 184 - 8 - sl
|
||||
}
|
||||
|
||||
// Draw arrow text.
|
||||
popup.drawer.Dot = fixed.P(x, 85)
|
||||
popup.drawer.DrawString(s)
|
||||
}
|
||||
|
||||
// Workaround if the next prayer is tomorrow.
|
||||
if p.Next() == nil && !np {
|
||||
tm = true
|
||||
|
||||
pom.Delete("Fajr")
|
||||
pom.Set("Fajr", pm[prayer.Fajr])
|
||||
}
|
||||
|
||||
// Draw arrow.
|
||||
popup.drawer.Dot = fixed.P(pd+2, 98)
|
||||
popup.drawer.DrawString("↓")
|
||||
}
|
||||
|
||||
// Redraw the popup.
|
||||
popup.draw()
|
||||
},
|
||||
})
|
||||
|
||||
cur, err := c.CurrentSong()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sts, err := c.Status()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bar.popups.Set("music", &Popup{
|
||||
x: bar.w - 327 - bar.h,
|
||||
y: bar.h,
|
||||
w: 327,
|
||||
h: 148,
|
||||
|
||||
// Set text color.
|
||||
popup.drawer.Src = image.NewUniform(hexToBGRA(popup.fg))
|
||||
update: func() {
|
||||
popup := bar.popup("music")
|
||||
|
||||
// Draw album text.
|
||||
album := trim(cur["Album"], 32)
|
||||
popup.drawer.Dot = fixed.P(-(popup.drawer.MeasureString(album).Ceil()/2)+82,
|
||||
48)
|
||||
popup.drawer.DrawString(album)
|
||||
cur, err := bar.store["mpd"].(*mpd.Client).CurrentSong()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
sts, err := bar.store["mpd"].(*mpd.Client).Status()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Draw artist text.
|
||||
artist := trim("Artist: "+cur["AlbumArtist"], 32)
|
||||
popup.drawer.Dot = fixed.P(-(popup.drawer.MeasureString(artist).Ceil()/2)+
|
||||
82, 58+16)
|
||||
popup.drawer.DrawString(artist)
|
||||
// Color the background.
|
||||
popup.img.For(func(cx, cy int) xgraphics.BGRA {
|
||||
return xgraphics.BGRA{B: 238, G: 238, R: 238, A: 0xFF}
|
||||
})
|
||||
|
||||
// Draw rlease date text.
|
||||
date := trim("Release date: "+cur["Date"], 32)
|
||||
popup.drawer.Dot = fixed.P(-(popup.drawer.MeasureString(date).Ceil()/2)+82,
|
||||
58+16+16)
|
||||
popup.drawer.DrawString(date)
|
||||
// Set foreground color.
|
||||
popup.drawer.Src = image.NewUniform(xgraphics.BGRA{B: 33, G: 27,
|
||||
R: 2, A: 0xFF})
|
||||
|
||||
// Check if album art file exists.
|
||||
fp := path.Join(userdirs.Music, path.Dir(cur["file"]), "cover_popup.png")
|
||||
if _, err := os.Stat(fp); !os.IsNotExist(err) {
|
||||
f, err := os.Open(fp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
// Draw album text.
|
||||
album := trim(cur["Album"], 32)
|
||||
popup.drawer.Dot = fixed.P(-(popup.drawer.MeasureString(album).
|
||||
Ceil()/2)+90, 48)
|
||||
popup.drawer.DrawString(album)
|
||||
|
||||
// Draw album art.
|
||||
img, _, err := image.Decode(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
xgraphics.Blend(popup.img, xgraphics.NewConvert(X, img), image.Point{
|
||||
-166, -10})
|
||||
} else {
|
||||
popup.drawer.Dot = fixed.P(200, 78)
|
||||
popup.drawer.DrawString("No cover found!")
|
||||
}
|
||||
// Draw artist text.
|
||||
artist := trim("Artist: "+cur["AlbumArtist"], 32)
|
||||
popup.drawer.Dot = fixed.P(-(popup.drawer.MeasureString(artist).
|
||||
Ceil()/2)+90, 58+16)
|
||||
popup.drawer.DrawString(artist)
|
||||
|
||||
// Calculate progressbar lengths.
|
||||
e, err := strconv.ParseFloat(sts["elapsed"], 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t, err := strconv.ParseFloat(sts["duration"], 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pf := int(math.Round(e / t * 29))
|
||||
pu := 29 - pf
|
||||
// Draw rlease date text.
|
||||
date := trim("Release date: "+cur["Date"], 32)
|
||||
popup.drawer.Dot = fixed.P(-(popup.drawer.MeasureString(date).
|
||||
Ceil()/2)+90, 58+16+16)
|
||||
popup.drawer.DrawString(date)
|
||||
|
||||
// Draw progressbar.
|
||||
popup.drawer.Dot = fixed.P(10, 132)
|
||||
for i := 1; i <= pf; i++ {
|
||||
popup.drawer.DrawString("-")
|
||||
}
|
||||
popup.drawer.Src = image.NewUniform(hexToBGRA("#72A7D3"))
|
||||
for i := 1; i <= pu; i++ {
|
||||
popup.drawer.DrawString("-")
|
||||
}
|
||||
// Check if the cover art file exists.
|
||||
fp := path.Join(userdirs.Music, path.Dir(cur["file"]),
|
||||
"cover_popup.png")
|
||||
if _, err := os.Stat(fp); !os.IsNotExist(err) {
|
||||
f, err := os.Open(fp)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Redraw the popup.
|
||||
popup.draw()
|
||||
// Draw cover art.
|
||||
img, _, err := image.Decode(f)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
xgraphics.Blend(popup.img, xgraphics.NewConvert(X, img), image.
|
||||
Point{-179, -0})
|
||||
} else {
|
||||
popup.drawer.Dot = fixed.P(218, 78)
|
||||
popup.drawer.DrawString("No cover found!")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (popup *Popup) todo() error {
|
||||
// Watch file for events.
|
||||
w, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.Add(path.Join(basedir.Home, ".todo")); err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.Open(path.Join(basedir.Home, ".todo"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set text color.
|
||||
popup.drawer.Src = image.NewUniform(hexToBGRA(popup.fg))
|
||||
|
||||
for {
|
||||
// Count file lines.
|
||||
s := bufio.NewScanner(f)
|
||||
var c int
|
||||
for s.Scan() {
|
||||
c++
|
||||
}
|
||||
|
||||
// Rewind file.
|
||||
if _, err := f.Seek(0, 0); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
// Color the background.
|
||||
popup.img.For(func(cx, cy int) xgraphics.BGRA {
|
||||
return hexToBGRA(popup.bg)
|
||||
})
|
||||
|
||||
// Draw text.
|
||||
popup.drawer.Dot = fixed.P(5, 11)
|
||||
popup.drawer.DrawString(strconv.Itoa(c))
|
||||
|
||||
// Redraw block.
|
||||
popup.draw()
|
||||
|
||||
// Listen for next write event.
|
||||
ev := <-w.Events
|
||||
if ev.Op&fsnotify.Write != fsnotify.Write {
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Get elapsed and duration times.
|
||||
se, err := strconv.ParseFloat(sts["elapsed"], 32)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
//e := int(math.Round(se))
|
||||
|
||||
// Calculate the dot lenght, this is the length of the line divided
|
||||
// by the length of the song.
|
||||
sd, err := strconv.ParseFloat(sts["duration"], 32)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
d := 159.00 / sd
|
||||
|
||||
// Calculate elapsed line length.
|
||||
e := int(math.Round(d*se)) + 10
|
||||
|
||||
// Draw line.
|
||||
popup.img.SubImage(image.Rect(10, 131, 10+159, 132)).(*xgraphics.
|
||||
Image).For(func(x, y int) xgraphics.BGRA {
|
||||
// Make the line look dashed.
|
||||
if x%5 == 4 {
|
||||
return xgraphics.BGRA{B: 238, G: 238, R: 238, A: 0xFF}
|
||||
}
|
||||
|
||||
if x < e {
|
||||
return xgraphics.BGRA{B: 33, G: 27, R: 2, A: 0xFF}
|
||||
}
|
||||
return xgraphics.BGRA{B: 211, G: 167, R: 114, A: 0xFF}
|
||||
})
|
||||
|
||||
// Redraw the popup.
|
||||
popup.draw()
|
||||
},
|
||||
})
|
||||
|
||||
bar.popups.Set("clock", &Popup{
|
||||
x: (bar.w / 2) - (178 / 2),
|
||||
y: bar.h,
|
||||
w: 178,
|
||||
h: 129,
|
||||
|
||||
update: func() {
|
||||
popup := bar.popup("clock")
|
||||
|
||||
// Color the background.
|
||||
f, err := runtime.Open("/images/clock-popup-bg.png")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
bg, _, err := image.Decode(f.(io.Reader))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
xgraphics.Blend(popup.img, xgraphics.NewConvert(X, bg), image.Point{
|
||||
0, 0})
|
||||
|
||||
// Redraw the popup.
|
||||
popup.draw()
|
||||
|
||||
// Set foreground color.
|
||||
popup.drawer.Src = image.NewUniform(xgraphics.BGRA{B: 33, G: 27,
|
||||
R: 2, A: 0xFF})
|
||||
|
||||
// Create http client with a timeout.
|
||||
/*c := &http.Client{Timeout: time.Duration(2 * time.Second)}
|
||||
|
||||
// Get weather information.
|
||||
r, err := c.Get(
|
||||
"https://api.buienradar.nl/data/public/2.0/jsonfeed")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
jq := gojsonq.New().Reader(r.Body).From(
|
||||
"actual.stationmeasurements.[0]").WhereEqual("stationid",
|
||||
"6260")
|
||||
|
||||
// Draw weather information.
|
||||
popup.drawer.Dot = fixed.P(11, 101)
|
||||
popup.drawer.DrawString("Rainfall graph, it's " + fmt.Sprint(jq.
|
||||
Find("feeltemperature")) + "°C.")
|
||||
|
||||
// Redraw the popup.
|
||||
popup.draw()
|
||||
|
||||
// Set location.
|
||||
lat := "52.0646"
|
||||
lon := "5.2065"
|
||||
|
||||
// Get rainfall information.
|
||||
r, err = c.Get(
|
||||
"https://gpsgadget.buienradar.nl/data/raintext?lat=" + lat +
|
||||
"&lon=" + lon)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
// Create rainfall tmp files.
|
||||
td, err := ioutil.TempFile(os.TempDir(), "melonbar-rain-*.dat")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer os.Remove(td.Name())
|
||||
ti, err := ioutil.TempFile(os.TempDir(), "melonbar-rain-*.png")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer os.Remove(ti.Name())
|
||||
|
||||
// Compose rainfall data tmp file contents.
|
||||
var d []byte
|
||||
s := bufio.NewScanner(r.Body)
|
||||
for s.Scan() {
|
||||
d = append(d, bytes.Split(s.Bytes(), []byte("|"))[0]...)
|
||||
d = append(d, []byte("\n")...)
|
||||
}
|
||||
|
||||
// Write rainfall data tmp file.
|
||||
if _, err = td.Write(d); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if err := td.Close(); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Create rainfall graph.
|
||||
cmd := exec.Command("gnuplot", "-e", `
|
||||
set terminal png transparent size 205,107;
|
||||
set output '`+ti.Name()+`';
|
||||
set yrange [0:255];
|
||||
set noborder;
|
||||
set nolabel;
|
||||
set nokey;
|
||||
set notics;
|
||||
set notitle;
|
||||
plot '`+td.Name()+`' smooth csplines w filledcu x1 fc rgb '#72A7D3', '`+td.Name()+`' smooth csplines w line lc rgb '#5394C9';
|
||||
`)
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Draw rainfall graph.
|
||||
img, _, err := image.Decode(ti)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
xgraphics.Blend(popup.img, xgraphics.NewConvert(X, img), image.
|
||||
Point{11, 2})
|
||||
|
||||
// Redraw the popup.
|
||||
popup.draw()
|
||||
},
|
||||
})*/
|
||||
}
|
||||
|
|
45
util.go
45
util.go
|
@ -1,19 +1,5 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
|
||||
"github.com/BurntSushi/xgbutil/xgraphics"
|
||||
)
|
||||
|
||||
func hexToBGRA(h string) xgraphics.BGRA {
|
||||
h = strings.Replace(h, "#", "", 1)
|
||||
d, _ := hex.DecodeString(h)
|
||||
|
||||
return xgraphics.BGRA{B: d[2], G: d[1], R: d[0], A: 0xFF}
|
||||
}
|
||||
|
||||
// TODO: Instead of doing this using rune-count, do this using pixel-count.
|
||||
func trim(txt string, l int) string {
|
||||
if len(txt) > l {
|
||||
|
@ -21,3 +7,34 @@ func trim(txt string, l int) string {
|
|||
}
|
||||
return txt
|
||||
}
|
||||
|
||||
type Hex string
|
||||
|
||||
type RGB struct {
|
||||
Red uint8
|
||||
Green uint8
|
||||
Blue uint8
|
||||
}
|
||||
|
||||
func (h Hex) toRGB() (RGB, error) {
|
||||
return Hex2RGB(h)
|
||||
}
|
||||
|
||||
func Hex2RGB(hex Hex) (RGB, error) {
|
||||
var rgb RGB
|
||||
values, err := strconv.ParseUint(string(hex), 16, 32)
|
||||
|
||||
if err != nil {
|
||||
return RGB{}, err
|
||||
}
|
||||
|
||||
rgb = RGB{
|
||||
Red: uint8(values >> 16),
|
||||
Green: uint8((values >> 8) & 0xFF),
|
||||
Blue: uint8(values & 0xFF),
|
||||
}
|
||||
|
||||
return rgb, nil
|
||||
}
|
||||
|
||||
|
||||
|
|
39
x.go
39
x.go
|
@ -1,17 +1,23 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"path"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
||||
"github.com/AndreKR/multiface"
|
||||
"github.com/BurntSushi/xgb"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
"github.com/BurntSushi/xgbutil/ewmh"
|
||||
"github.com/BurntSushi/xgbutil/xevent"
|
||||
"github.com/BurntSushi/xgbutil/xwindow"
|
||||
"golang.org/x/image/font/plan9font"
|
||||
"github.com/zachomedia/go-bdf"
|
||||
)
|
||||
|
||||
func initX() error {
|
||||
// Disable logging messages.
|
||||
xgb.Logger = log.New(ioutil.Discard, "", 0)
|
||||
|
||||
// Set up a connection to the X server.
|
||||
var err error
|
||||
X, err = xgbutil.NewConn()
|
||||
|
@ -45,17 +51,26 @@ func initEWMH(w xproto.Window) error {
|
|||
return ewmh.WmNameSet(X, w, "melonbar")
|
||||
}
|
||||
|
||||
func initFont() error {
|
||||
fr := func(name string) ([]byte, error) {
|
||||
return box.Find(path.Join("fonts", name))
|
||||
func initFace() error {
|
||||
face = new(multiface.Face)
|
||||
|
||||
fpl := []string{
|
||||
"runtime/fonts/cure.punpun.bdf",
|
||||
"runtime/fonts/kochi.small.bdf",
|
||||
"runtime/fonts/baekmuk.small.bdf",
|
||||
}
|
||||
fp, err := box.Find("fonts/cure.font")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
face, err = plan9font.ParseFont(fp, fr)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
for _, fp := range fpl {
|
||||
fb, err := runtime.ReadFile(fp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ff, err := bdf.Parse(fb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
face.AddFace(ff.NewFace())
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
Loading…
Reference in New Issue