aboutsummaryrefslogtreecommitdiff
path: root/main.go
diff options
context:
space:
mode:
authorcrecher <crecher@crecher.net>2026-10-21 22:32:22 +0000
committercrecher <crecher@crecher.net>2026-10-21 22:32:22 +0000
commit119f15531c28531ff879a5c532f4a44b04a19398 (patch)
tree30c5fae117523c68c4f240cf4b12fc948ba3e20a /main.go
parent5f2faec53cd88d723d8e7572e2f2a73949d6025a (diff)
downloadwm-119f15531c28531ff879a5c532f4a44b04a19398.tar.gz
wm-119f15531c28531ff879a5c532f4a44b04a19398.zip
fixes
Signed-off-by: crecher <crecher@crecher.net>
Diffstat (limited to 'main.go')
-rw-r--r--main.go200
1 files changed, 115 insertions, 85 deletions
diff --git a/main.go b/main.go
index f0f954c..93d5cf9 100644
--- a/main.go
+++ b/main.go
@@ -6,7 +6,6 @@ import (
"os"
"os/exec"
"strconv"
- "time"
"github.com/BurntSushi/xgb"
"github.com/BurntSushi/xgb/xproto"
@@ -68,58 +67,60 @@ func main() {
log.Fatal(err)
}
setup(conn)
- for {
- dirty := false
- drain:
+
+ events := make(chan xgb.Event)
+ go func() {
for {
- select {
- case s, ok := <-statusChan:
- if !ok {
- break drain
- }
- statusText = s
- dirty = true
- default:
- break drain
+ ev, xerr := conn.WaitForEvent()
+ if ev == nil && xerr == nil {
+ close(events)
+ return
+ }
+ if xerr != nil {
+ log.Println("x error:", xerr)
+ }
+ if ev != nil {
+ events <- ev
}
}
- if dirty {
+ }()
+
+ for {
+ select {
+ case s := <-statusChan:
+ statusText = s
drawBar(conn)
+ case ev, ok := <-events:
+ if !ok {
+ log.Fatal("lost connection to X server")
+ }
+ handleEvent(conn, ev)
}
+ }
+}
- ev, xerr := conn.PollForEvent()
- if xerr != nil {
- log.Fatal(xerr)
- }
- if ev == nil {
- time.Sleep(50 * time.Millisecond)
- continue
+func handleEvent(conn *xgb.Conn, ev xgb.Event) {
+ switch e := ev.(type) {
+ case xproto.KeyPressEvent:
+ handleKeyPress(conn, e)
+ case xproto.MapRequestEvent:
+ manageWindow(conn, e.Window)
+ case xproto.DestroyNotifyEvent:
+ unmanageWindow(conn, e.Window)
+ case xproto.PropertyNotifyEvent:
+ if e.Window == focused {
+ drawBar(conn)
}
-
- // event loop
- switch e := ev.(type) {
- case xproto.KeyPressEvent:
- handleKeyPress(conn, e)
- case xproto.MapRequestEvent:
- manageWindow(conn, e.Window)
- case xproto.DestroyNotifyEvent:
- unmanageWindow(conn, e.Window)
- case xproto.PropertyNotifyEvent:
- if e.Window == focused {
- drawBar(conn)
- }
- case xproto.EnterNotifyEvent: // change focused window event
- focused = e.Event
- setInputFocus(conn, focused)
+ case xproto.EnterNotifyEvent: // change focused window event
+ focused = e.Event
+ setInputFocus(conn, focused)
+ drawBar(conn)
+ case xproto.ExposeEvent:
+ if e.Window == barWindow {
drawBar(conn)
- case xproto.ExposeEvent:
- if e.Window == barWindow {
- drawBar(conn)
- }
- default:
- // log.Println("event: ", e)
}
-
+ default:
+ // log.Println("event: ", e)
}
}
@@ -151,8 +152,8 @@ func setup(conn *xgb.Conn) {
}
for _, cmd := range startup {
- exec.Command(cmd)
log.Println("Running startup command: " + cmd)
+ spawn("sh", "-c", cmd)
}
}
@@ -195,49 +196,71 @@ func startStatus() {
}
func killActiveWindow(conn *xgb.Conn, win xproto.Window) {
- if win == 0 {
+ if _, ok := clients[win]; !ok {
log.Println("no focused window to kill")
return
}
- // try to use WM_DELETE_WINDOW to close
- wmProtocols, err := xproto.InternAtom(conn, true,
- uint16(len("WM_PROTOCOLS")), "WM_PROTOCOLS").Reply()
- if err != nil {
- log.Println("failed to get WM_PROTOCOLS atom", err)
+ if supportsDeleteWindow(conn, win) {
+ if err := sendDeleteWindow(conn, win); err != nil {
+ log.Println("failed to send WM_DELETE_WINDOW:", err)
+ }
return
}
- wmDelete, err := xproto.InternAtom(conn, true,
- uint16(len("WM_DELETE_WINDOW")), "WM_DELETE_WINDOW").Reply()
+ xproto.KillClient(conn, uint32(win))
+}
+
+func internAtom(conn *xgb.Conn, name string) (xproto.Atom, error) {
+ reply, err := xproto.InternAtom(conn, false, uint16(len(name)), name).Reply()
if err != nil {
- log.Println("failed to get WM_DELETE_WINDOW atom", err)
- return
+ return 0, err
+ }
+ return reply.Atom, nil
+}
+
+func supportsDeleteWindow(conn *xgb.Conn, win xproto.Window) bool {
+ wmProtocols, err := internAtom(conn, "WM_PROTOCOLS")
+ if err != nil {
+ return false
+ }
+ wmDelete, err := internAtom(conn, "WM_DELETE_WINDOW")
+ if err != nil {
+ return false
+ }
+
+ prop, err := xproto.GetProperty(conn, false, win, wmProtocols, xproto.AtomAtom, 0, 64).Reply()
+ if err != nil || prop == nil || prop.Format != 32 {
+ return false
+ }
+ for i := 0; i+4 <= len(prop.Value); i += 4 {
+ if xproto.Atom(xgb.Get32(prop.Value[i:])) == wmDelete {
+ return true
+ }
+ }
+ return false
+}
+
+func sendDeleteWindow(conn *xgb.Conn, win xproto.Window) error {
+ wmProtocols, err := internAtom(conn, "WM_PROTOCOLS")
+ if err != nil {
+ return err
+ }
+ wmDelete, err := internAtom(conn, "WM_DELETE_WINDOW")
+ if err != nil {
+ return err
}
data := xproto.ClientMessageDataUnionData32New([]uint32{
- uint32(wmDelete.Atom), uint32(xproto.TimeCurrentTime), 0, 0, 0,
+ uint32(wmDelete), uint32(xproto.TimeCurrentTime), 0, 0, 0,
})
-
ev := xproto.ClientMessageEvent{
Format: 32,
Window: win,
- Type: wmProtocols.Atom,
+ Type: wmProtocols,
Data: data,
}
-
- // send event
- err = xproto.SendEventChecked(conn, false, win, xproto.EventMaskNoEvent, string(ev.Bytes())).Check()
- if err != nil {
- // force close if WM_DELETE_WINDOW doesnt work
- log.Println("force closing:", err)
- xproto.DestroyWindow(conn, win)
- }
-
- removeClient(win)
- delete(clients, win)
- focused = 0
- updateWindows(conn)
+ return xproto.SendEventChecked(conn, false, win, xproto.EventMaskNoEvent, string(ev.Bytes())).Check()
}
func grabKeys(conn *xgb.Conn) {
@@ -395,6 +418,13 @@ func switchTags(conn *xgb.Conn, tags BitMask) {
drawBar(conn)
}
+func dim(v int) uint32 {
+ if v < 1 {
+ return 1
+ }
+ return uint32(v)
+}
+
func tileVertical(conn *xgb.Conn) {
screen := xproto.Setup(conn).DefaultScreen(conn)
screenWidth := int(screen.WidthInPixels)
@@ -423,8 +453,8 @@ func tileVertical(conn *xgb.Conn) {
[]uint32{
0 + uint32(gapSize), // x
uint32(yOffset + gapSize), // y
- uint32(screenWidth - gapSize*2),
- uint32(height),
+ dim(screenWidth - gapSize*2),
+ dim(height),
})
i++
@@ -457,8 +487,8 @@ func tileHorizontal(conn *xgb.Conn) {
[]uint32{
uint32(xOffset + gapSize), // x
uint32(gapSize), // y
- uint32(width),
- uint32(screenHeight - int(barHeight) - gapSize*2),
+ dim(width),
+ dim(screenHeight - int(barHeight) - gapSize*2),
})
i++
}
@@ -491,10 +521,10 @@ func tileWithMaster(conn *xgb.Conn) {
xproto.ConfigWindowWidth|
xproto.ConfigWindowHeight,
[]uint32{
- uint32(0), // x
- uint32(0), // y
- uint32(screenWidth), // width
- uint32(screenHeight - int(barHeight)),
+ uint32(0), // x
+ uint32(0), // y
+ dim(screenWidth), // width
+ dim(screenHeight - int(barHeight)),
})
} else {
xproto.ConfigureWindow(conn, c.Win,
@@ -503,10 +533,10 @@ func tileWithMaster(conn *xgb.Conn) {
xproto.ConfigWindowWidth|
xproto.ConfigWindowHeight,
[]uint32{
- uint32(0 + gapSize), // x
- uint32(gapSize), // y
- uint32(screenWidth/2 - int(gapSize)), // width
- uint32(screenHeight - int(barHeight) - gapSize*2),
+ uint32(0 + gapSize), // x
+ uint32(gapSize), // y
+ dim(screenWidth/2 - gapSize), // width
+ dim(screenHeight - int(barHeight) - gapSize*2),
})
}
} else {
@@ -529,8 +559,8 @@ func tileWithMaster(conn *xgb.Conn) {
[]uint32{
xOffset, // x
uint32(yOffset), // y
- uint32(widthPerWin - gapSize*2),
- uint32(height),
+ dim(widthPerWin - gapSize*2),
+ dim(height),
})
i++