Refactored the some of the initialization code out of main() and into its own file...
[illi] / illi.go
diff --git a/illi.go b/illi.go
index 5cb1641..e535cc5 100644 (file)
--- a/illi.go
+++ b/illi.go
@@ -7,7 +7,6 @@ import (
     "os/exec"
 
     "github.com/jezek/xgb"
     "os/exec"
 
     "github.com/jezek/xgb"
-    "github.com/jezek/xgb/xinerama"
     "github.com/jezek/xgb/xproto"
 )
 
     "github.com/jezek/xgb/xproto"
 )
 
@@ -17,124 +16,28 @@ const (
     XK_Return = 0xff0d
 )
 
     XK_Return = 0xff0d
 )
 
+// TODO: These module-level global variables have arisen while establishing
+// basic functionality. Before proceeding much further, figure out what to do
+// with them.
 var xc *xgb.Conn
 var xc *xgb.Conn
-var attachedScreens []xinerama.ScreenInfo
-var xroot xproto.ScreenInfo
 var keymap [256][]xproto.Keysym
 
 func main() {
 var keymap [256][]xproto.Keysym
 
 func main() {
-    xConn, err := xgb.NewConn()
-    if err != nil {
-        log.Fatal(err)
-    }
-    xc = xConn
-    defer xc.Close()
-
-    setup := xproto.Setup(xc)
-    if setup == nil || len(setup.Roots) < 1 {
-        log.Fatal("ILLI: Unable to parse received SetupInfo from X11 server.")
-    }
-
-    if err := xinerama.Init(xc); err != nil {
-        log.Fatal(err)
-    }
-
-    if r, err := xinerama.QueryScreens(xc).Reply(); err != nil {
-        log.Fatal(err)
-    } else {
-        if len(r.ScreenInfo) == 0 {
-            attachedScreens = []xinerama.ScreenInfo{
-                xinerama.ScreenInfo{
-                    Width:  setup.Roots[0].WidthInPixels,
-                    Height: setup.Roots[0].HeightInPixels,
-                },
-            }
-        } else {
-            attachedScreens = r.ScreenInfo
-        }
-    }
-
-    connInfo := xproto.Setup(xc)
-    if connInfo == nil {
-        log.Fatal("ILLI: Unable to parse X connection information")
-    }
-    if len(connInfo.Roots) != 1 {
-        log.Fatal("ILLI: Inappropriate number of roots. Did xinerama initialize correctly?")
-    }
-    xroot = connInfo.Roots[0]
-
-    // Attempt to register as the window manager
-    if err := TakeWMOwnership(); err != nil {
-        if _, ok := err.(xproto.AccessError); ok {
-            log.Fatal("ILLI: Unable to register as window manager with X server. Perhaps another WM is already running?")
-        }
-        log.Fatal(err)
-    }
+    xconn := connectToXServer()
+    attachedScreens := getAttachedScreens(xconn)
+    xroot := getXRoot(xconn)
+    keymap = getKeyboardMap(xconn)
+    registerForKeyEvents(xconn, xroot, keymap)
 
 
-    fmt.Println("ILLI: Successfully registered as WM with X server.")
+    becomeWM(xconn, xroot)
 
 
-// -----------------------------------------------------------------------------
+    // Build a list of windows needing management ------------------------------
 
 
-    const (
-        loKey = 8
-        hiKey = 255
-    )
-
-    m := xproto.GetKeyboardMapping(xc, loKey, hiKey-loKey+1)
-    reply, err := m.Reply()
-    if err != nil {
-        log.Fatal(err)
-    }
-    if reply == nil {
-        log.Fatal("Could not load keyboard map")
-    }
-
-    for i := 0; i < hiKey-loKey+1; i++ {
-        keymap[loKey+i] = reply.Keysyms[i*int(reply.KeysymsPerKeycode) : (i+1)*int(reply.KeysymsPerKeycode)]
-    }
-    grabs := []struct {
-        sym       xproto.Keysym
-        modifiers uint16
-        codes     []xproto.Keycode
-    }{
-        {
-            sym:       XK_q,
-            modifiers: xproto.ModMask1 | xproto.ModMaskShift,
-        },
-        {
-            sym:       XK_Return,
-            modifiers: xproto.ModMask1 | xproto.ModMaskShift,
-        },
-    }
-
-    for i, syms := range keymap {
-        for _, sym := range syms {
-            for c := range grabs {
-                if grabs[c].sym == sym {
-                    grabs[c].codes = append(grabs[c].codes, xproto.Keycode(i))
-                }
-            }
-        }
-    }
-    for _, grabbed := range grabs {
-        for _, code := range grabbed.codes {
-            if err := xproto.GrabKeyChecked(
-                xc,
-                false,
-                xroot.Root,
-                grabbed.modifiers,
-                code,
-                xproto.GrabModeAsync,
-                xproto.GrabModeAsync,
-            ).Check(); err != nil {
-                log.Print(err)
-            }
-
-        }
+    xc = xconn
+    if len(attachedScreens) > 0 {
+        fmt.Println("The Go compiler is waaaaay too picky about unused variables...")
     }
 
     }
 
-// -----------------------------------------------------------------------------
-
     tree, err := xproto.QueryTree(xc, xroot.Root).Reply()
     if err != nil {
         log.Fatal(err)
     tree, err := xproto.QueryTree(xc, xroot.Root).Reply()
     if err != nil {
         log.Fatal(err)
@@ -165,6 +68,8 @@ func main() {
 
     }
 
 
     }
 
+    // Main program loop reacts to X events ------------------------------------
+
 eventloop:
     for {
         xevent, err := xc.WaitForEvent()
 eventloop:
     for {
         xevent, err := xc.WaitForEvent()
@@ -183,36 +88,26 @@ eventloop:
     }
 }
 
     }
 }
 
-func TakeWMOwnership() error {
-    return xproto.ChangeWindowAttributesChecked(
-        xc,
-        xroot.Root,
-        xproto.CwEventMask,
-        []uint32{
-            xproto.EventMaskKeyPress |
-                xproto.EventMaskKeyRelease |
-                xproto.EventMaskButtonPress |
-                xproto.EventMaskButtonRelease |
-                xproto.EventMaskStructureNotify |
-                xproto.EventMaskSubstructureRedirect,
-        }).Check()
-}
-
+// The client list appears to have some entries for windows that shouldn't
+// actually be viewable. For now, it appears that only windows with a
+// `MapState` value of 2 should be viewable. TODO: Verify this.
+//   - https://github.com/BurntSushi/xgb/blob/master/xproto/xproto.go#L3772
+//   - https://github.com/BurntSushi/xgb/blob/master/xproto/xproto.go#L10287
 func isMappedWindow(windowID xproto.Window) bool {
     reply, err := xproto.GetWindowAttributes(xc, windowID).Reply()
     if err != nil {
         log.Fatal(err)
     }
 func isMappedWindow(windowID xproto.Window) bool {
     reply, err := xproto.GetWindowAttributes(xc, windowID).Reply()
     if err != nil {
         log.Fatal(err)
     }
-    // TODO: Verify this.
-    // It appears that only windows with a `MapState` value of 2 should be 'viewable'.
-    //   - https://github.com/BurntSushi/xgb/blob/master/xproto/xproto.go#L3772
-    //   - https://github.com/BurntSushi/xgb/blob/master/xproto/xproto.go#L10287
     if reply != nil && reply.MapState == 2 {
         return true
     }
     return false
 }
 
     if reply != nil && reply.MapState == 2 {
         return true
     }
     return false
 }
 
+// TODO: Determine how I want to split the main event loop from the various
+// event handlers (like this keypress handler). It shouldn't grow too
+// fragmented, nor should it grow into a monolithic beast, but the balance
+// needs to be selected after the handlers are built out more completely.
 func handleKeyPressEvent(key xproto.KeyPressEvent) error {
     switch keymap[key.Detail][0] {
         case XK_q:
 func handleKeyPressEvent(key xproto.KeyPressEvent) error {
     switch keymap[key.Detail][0] {
         case XK_q:
@@ -238,5 +133,5 @@ func handleKeyPressEvent(key xproto.KeyPressEvent) error {
         default:
             return nil
     }
         default:
             return nil
     }
-    return nil // What do I actually want to return here?
+    return nil // TODO: What do I actually want to return here?
 }
 }