aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.go22
-rw-r--r--main.go69
-rw-r--r--restart.go108
3 files changed, 156 insertions, 43 deletions
diff --git a/config.go b/config.go
index 5f8c4e2..67ef920 100644
--- a/config.go
+++ b/config.go
@@ -10,9 +10,9 @@ import (
// appearance
var barHeight uint16 = 20
var gapSize int = 10
-var barColor uint32 = 0x222222
-var barFg uint32 = 0xBBBBBB
-var selBg uint32 = 0x005577
+var barColor uint32 = 0x000000
+var barFg uint32 = 0x888888
+var selBg uint32 = 0x285577
var selFg uint32 = 0xEEEEEE
var fontName = "JetBrainsMono Nerd Font:size=16"
var tagCount = 9
@@ -30,7 +30,14 @@ var keys = []Key{
Mod: mod,
Sym: keysym.XK_Return,
Fn: func(conn *xgb.Conn) {
- spawn("kitty")
+ spawn("st")
+ },
+ },
+ {
+ Mod: mod,
+ Sym: keysym.XK_w,
+ Fn: func(conn *xgb.Conn) {
+ spawn("qutebrowser")
},
},
{
@@ -41,6 +48,13 @@ var keys = []Key{
},
},
{
+ Mod: mod | xproto.ModMaskShift,
+ Sym: keysym.XK_r,
+ Fn: func(conn *xgb.Conn) {
+ restart()
+ },
+ },
+ {
Mod: mod,
Sym: keysym.XK_q,
Fn: func(conn *xgb.Conn) {
diff --git a/main.go b/main.go
index 30c514f..f0f954c 100644
--- a/main.go
+++ b/main.go
@@ -146,6 +146,10 @@ func setup(conn *xgb.Conn) {
startStatus()
+ if restoreState(conn) {
+ return
+ }
+
for _, cmd := range startup {
exec.Command(cmd)
log.Println("Running startup command: " + cmd)
@@ -170,9 +174,11 @@ func startStatus() {
statusChan = make(chan string, 1)
go func() {
sc := bufio.NewScanner(os.Stdin)
- if sc.Err != nil {
- log.Println("scanner failed: ", sc.Err().Error)
- }
+ defer func() {
+ if err := sc.Err(); err != nil {
+ log.Println("scanner failed:", err)
+ }
+ }()
for sc.Scan() {
text := sc.Text()
select {
@@ -404,7 +410,7 @@ func tileVertical(conn *xgb.Conn) {
i := 0
for _, c := range ActiveClients {
- yOffset := int(barHeight) + (i * heightPerWin)
+ yOffset := i * heightPerWin
height := heightPerWin - gapSize
if i == len(ActiveClients)-1 {
height = heightPerWin - gapSize*2
@@ -449,8 +455,8 @@ func tileHorizontal(conn *xgb.Conn) {
xproto.ConfigWindowWidth|
xproto.ConfigWindowHeight,
[]uint32{
- uint32(xOffset + gapSize), // x
- uint32(int(barHeight) + gapSize), // y
+ uint32(xOffset + gapSize), // x
+ uint32(gapSize), // y
uint32(width),
uint32(screenHeight - int(barHeight) - gapSize*2),
})
@@ -485,9 +491,9 @@ func tileWithMaster(conn *xgb.Conn) {
xproto.ConfigWindowWidth|
xproto.ConfigWindowHeight,
[]uint32{
- uint32(0), // x
- uint32(int(barHeight)), // y
- uint32(screenWidth), // width
+ uint32(0), // x
+ uint32(0), // y
+ uint32(screenWidth), // width
uint32(screenHeight - int(barHeight)),
})
} else {
@@ -498,14 +504,14 @@ func tileWithMaster(conn *xgb.Conn) {
xproto.ConfigWindowHeight,
[]uint32{
uint32(0 + gapSize), // x
- uint32(int(barHeight) + gapSize), // y
+ uint32(gapSize), // y
uint32(screenWidth/2 - int(gapSize)), // width
uint32(screenHeight - int(barHeight) - gapSize*2),
})
}
} else {
heightPerWin := usableHeight / (n - 1)
- yOffset := int(barHeight) + (i * heightPerWin) + gapSize
+ yOffset := (i * heightPerWin) + gapSize
xOffset := uint32(widthPerWin + gapSize)
height := heightPerWin - gapSize*2
// log.Println(len(ActiveClients), i)
@@ -548,7 +554,7 @@ func createBar(conn *xgb.Conn) {
screen.RootDepth,
barWindow,
root,
- 0, 0, // x, y
+ 0, int16(int(screen.HeightInPixels)-int(barHeight)), // x, y
screen.WidthInPixels, barHeight,
0, // border
xproto.WindowClassInputOutput,
@@ -568,7 +574,8 @@ func createBar(conn *xgb.Conn) {
if h := fontAscent + fontDescent; h > 0 && uint16(h) != barHeight {
barHeight = uint16(h)
xproto.ConfigureWindow(conn, barWindow,
- xproto.ConfigWindowHeight, []uint32{uint32(barHeight)})
+ xproto.ConfigWindowY|xproto.ConfigWindowHeight,
+ []uint32{uint32(int(screen.HeightInPixels) - int(barHeight)), uint32(barHeight)})
}
}
@@ -677,42 +684,26 @@ func drawBar(conn *xgb.Conn) {
drawBarText(conn, barWidth-stw, 0, int(barHeight), statusText, barFg, barColor)
}
- var occ, focc BitMask
- if c, ok := clients[focused]; ok {
- focc = c.Tags
- }
+ var occ BitMask
for _, c := range clients {
occ |= c.Tags
}
- boxs := int(barHeight) / 9
- boxw := int(barHeight)/6 + 2
-
x := 0
for i := 0; i < tagCount; i++ {
var mask BitMask = 1 << uint(i)
- digit := strconv.Itoa(i + 1)
- w := textWidth(conn, digit) + int(barHeight)
sel := (GlobalTags & mask) != 0
- fg, bg := barFg, barColor
- if sel {
- fg, bg = selFg, selBg
- rect := xproto.Rectangle{X: int16(x), Y: 0, Width: uint16(w), Height: barHeight}
- xproto.ChangeGC(conn, barGC, xproto.GcForeground|xproto.GcBackground, []uint32{selBg, selBg})
- xproto.PolyFillRectangle(conn, xproto.Drawable(barWindow), barGC, []xproto.Rectangle{rect})
+ if !sel && occ&mask == 0 {
+ continue
}
- drawBarText(conn, x+int(barHeight)/2, 0, int(barHeight), digit, fg, bg)
- if occ&mask != 0 {
- rect := xproto.Rectangle{X: int16(x + boxs), Y: int16(boxs), Width: uint16(boxw), Height: uint16(boxw)}
- if focc&mask != 0 {
- xproto.ChangeGC(conn, barGC, xproto.GcForeground|xproto.GcBackground, []uint32{fg, fg})
- xproto.PolyFillRectangle(conn, xproto.Drawable(barWindow), barGC, []xproto.Rectangle{rect})
- } else {
- xproto.ChangeGC(conn, barGC, xproto.GcForeground|xproto.GcBackground, []uint32{fg, bg})
- xproto.PolyRectangle(conn, xproto.Drawable(barWindow), barGC, []xproto.Rectangle{rect})
- }
+ label := strconv.Itoa(i + 1)
+ fg := barFg
+ if sel {
+ label += "*"
+ fg = selFg
}
- x += w
+ drawBarText(conn, x+int(barHeight)/3, 0, int(barHeight), label, fg, barColor)
+ x += textWidth(conn, label) + int(barHeight)*2/3
}
if windowTitle != 0 {
diff --git a/restart.go b/restart.go
new file mode 100644
index 0000000..87c6c41
--- /dev/null
+++ b/restart.go
@@ -0,0 +1,108 @@
+package main
+
+import (
+ "encoding/json"
+ "log"
+ "os"
+ "strings"
+ "syscall"
+
+ "github.com/BurntSushi/xgb"
+ "github.com/BurntSushi/xgb/xproto"
+)
+
+const stateEnv = "WM_STATE"
+
+type savedClient struct {
+ Win xproto.Window
+ Floating bool
+ Tags BitMask
+}
+
+type savedState struct {
+ Clients []savedClient
+ GlobalTags BitMask
+ Layout string
+ Focused xproto.Window
+}
+
+func restart() {
+ st := savedState{GlobalTags: GlobalTags, Layout: layout, Focused: focused}
+ for _, c := range clientList {
+ st.Clients = append(st.Clients, savedClient{c.Win, c.Floating, c.Tags})
+ }
+ data, err := json.Marshal(st)
+ if err != nil {
+ log.Println("restart: failed to save state:", err)
+ return
+ }
+
+ exe, err := os.Executable()
+ if err != nil {
+ exe = os.Args[0]
+ }
+ exe = strings.TrimSuffix(exe, " (deleted)")
+
+ env := append(os.Environ(), stateEnv+"="+string(data))
+ log.Println("restarting", exe)
+ if err := syscall.Exec(exe, os.Args, env); err != nil {
+ log.Println("restart: exec failed:", err)
+ }
+}
+
+func restoreState(conn *xgb.Conn) bool {
+ raw, ok := os.LookupEnv(stateEnv)
+ if !ok {
+ return false
+ }
+ os.Unsetenv(stateEnv)
+
+ var st savedState
+ if err := json.Unmarshal([]byte(raw), &st); err != nil {
+ log.Println("restore: bad state:", err)
+ return false
+ }
+
+ GlobalTags = st.GlobalTags
+ if st.Layout != "" {
+ layout = st.Layout
+ }
+
+ for _, sc := range st.Clients {
+ geom, err := xproto.GetGeometry(conn, xproto.Drawable(sc.Win)).Reply()
+ if err != nil {
+ continue
+ }
+ client := &Client{
+ Win: sc.Win,
+ X: geom.X,
+ Y: geom.Y,
+ Width: geom.Width,
+ Height: geom.Height,
+ Floating: sc.Floating,
+ Tags: sc.Tags,
+ }
+ clients[sc.Win] = client
+ clientList = append(clientList, client)
+
+ xproto.ChangeWindowAttributes(conn, sc.Win,
+ xproto.CwEventMask,
+ []uint32{
+ xproto.EventMaskEnterWindow |
+ xproto.EventMaskFocusChange |
+ xproto.EventMaskPropertyChange,
+ })
+ }
+
+ if _, ok := clients[st.Focused]; ok {
+ focused = st.Focused
+ setInputFocus(conn, focused)
+ }
+
+ for _, c := range clients {
+ xproto.UnmapWindow(conn, c.Win)
+ }
+ updateWindows(conn)
+ drawBar(conn)
+ return true
+}