diff options
| author | crecher <crecher@crecher.net> | 2026-10-21 22:32:22 +0000 |
|---|---|---|
| committer | crecher <crecher@crecher.net> | 2026-10-21 22:32:22 +0000 |
| commit | 119f15531c28531ff879a5c532f4a44b04a19398 (patch) | |
| tree | 30c5fae117523c68c4f240cf4b12fc948ba3e20a | |
| parent | 5f2faec53cd88d723d8e7572e2f2a73949d6025a (diff) | |
| download | wm-119f15531c28531ff879a5c532f4a44b04a19398.tar.gz wm-119f15531c28531ff879a5c532f4a44b04a19398.zip | |
fixes
Signed-off-by: crecher <crecher@crecher.net>
| -rw-r--r-- | config.go | 14 | ||||
| -rw-r--r-- | main.go | 200 |
2 files changed, 119 insertions, 95 deletions
@@ -20,7 +20,9 @@ var tagCount = 9 // shows focused window title var windowTitle = 0 -var mod = uint16(xproto.ModMask1) +// xproto.ModMask1 = Alt +// xproto.ModMask4 = Super / Win +var mod = uint16(xproto.ModMask4) // commands that get executed on startup var startup = []string{"notify-send hello world!"} @@ -96,7 +98,7 @@ var keys = []Key{ Mod: mod, Sym: keysym.XK_0, Fn: func(conn *xgb.Conn) { - var tags BitMask = 2 ^ 32 - 1 + var tags BitMask = 1<<tagCount - 1 switchTags(conn, tags) }, }, @@ -172,12 +174,4 @@ var keys = []Key{ switchTags(conn, tags) }, }, - { - Mod: mod, - Sym: keysym.XK_0, - Fn: func(conn *xgb.Conn) { - var tags BitMask = 511 - switchTags(conn, tags) - }, - }, } @@ -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++ |