aboutsummaryrefslogtreecommitdiff
path: root/restart.go
diff options
context:
space:
mode:
authorcrecher <crecher@crecher.net>2026-10-21 22:16:49 +0000
committercrecher <crecher@crecher.net>2026-10-21 22:16:49 +0000
commit5f2faec53cd88d723d8e7572e2f2a73949d6025a (patch)
treee7d5402a51cdac1262d843088067ef2a0e7e4268 /restart.go
parent4539e476b6644e961a4a96e3253c615bc4673b0c (diff)
downloadwm-5f2faec53cd88d723d8e7572e2f2a73949d6025a.tar.gz
wm-5f2faec53cd88d723d8e7572e2f2a73949d6025a.zip
fix: crash on startup, feat: hot reload
Signed-off-by: crecher <crecher@onyx.home>
Diffstat (limited to 'restart.go')
-rw-r--r--restart.go108
1 files changed, 108 insertions, 0 deletions
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
+}