--- /dev/null
+package main
+
+import (
+ "fmt"
+ "log"
+ "os"
+ "os/exec"
+
+ "github.com/jezek/xgb"
+ "github.com/jezek/xgb/xinerama"
+ "github.com/jezek/xgb/xproto"
+)
+
+// TODO: Make a separate keysym file and fully populate it.
+const (
+ XK_q = 0x0071
+ XK_Return = 0xff0d
+)
+
+var xc *xgb.Conn
+var attachedScreens []xinerama.ScreenInfo
+var xroot xproto.ScreenInfo
+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)
+ }
+
+ fmt.Println("ILLI: Successfully registered as WM with X server.")
+
+// -----------------------------------------------------------------------------
+
+ 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)
+ }
+
+ }
+ }
+
+// -----------------------------------------------------------------------------
+
+ tree, err := xproto.QueryTree(xc, xroot.Root).Reply()
+ if err != nil {
+ log.Fatal(err)
+ }
+ if tree != nil {
+ //workspaces = make(map[string]*Workspace)
+ //defaultw := &Workspace{mu: &sync.Mutex{}}
+ for _, c := range tree.Children {
+ if isMappedWindow(c) {
+// err := defaultw.Add(c)
+// if err != nil {
+// log.Println(err)
+// }
+ fmt.Println("ILLI: Found a client.")
+ }
+
+ }
+
+// if len(attachedScreens) > 0 {
+// defaultw.Screen = &attachedScreens[0]
+// }
+//
+// workspaces["default"] = defaultw
+//
+// if err := defaultw.TileWindows(); err != nil {
+// log.Println(err)
+// }
+
+ }
+
+eventloop:
+ for {
+ xevent, err := xc.WaitForEvent()
+ if err != nil {
+ log.Println(err)
+ continue
+ }
+ switch e := xevent.(type) {
+ case xproto.KeyPressEvent:
+ if err := handleKeyPressEvent(e); err != nil {
+ break eventloop
+ }
+ default:
+ log.Println(xevent)
+ }
+ }
+}
+
+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()
+}
+
+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
+}
+
+func handleKeyPressEvent(key xproto.KeyPressEvent) error {
+ switch keymap[key.Detail][0] {
+ case XK_q:
+ switch key.State {
+ case xproto.ModMask1 | xproto.ModMaskShift:
+ // TODO: Where and how do I want to handle exit/restart?
+ // -------------------------
+ // We must manually close the X connection since we used
+ // `defer` when setting it up and os.Exit() short-circuits
+ // that deferral.
+ xc.Close()
+ os.Exit(0)
+ }
+ case XK_Return:
+ switch key.State {
+ case xproto.ModMask1 | xproto.ModMaskShift:
+ cmd := exec.Command("xterm")
+ err := cmd.Start()
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+ default:
+ return nil
+ }
+ return nil // What do I actually want to return here?
+}