package main import ( "fmt" "log" "os" "os/exec" "github.com/jezek/xgb" "github.com/jezek/xgb/xproto" ) // TODO: Make a separate keysym file and fully populate it. const ( XK_q = 0x0071 XK_Return = 0xff0d ) func main() { fmt.Println("ILLI: Execution begins") xconn := connectToXServer() attachedScreens := getAttachedScreens(xconn) xroot := getXRoot(xconn) keymap := getKeyboardMap(xconn) registerForKeyEvents(xconn, xroot, keymap) becomeWM(xconn, xroot) // Build a list of windows needing management ------------------------------ if len(attachedScreens) > 0 { fmt.Println("The Go compiler is waaaaay too picky about unused variables...") } tree, err := xproto.QueryTree(xconn, 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(xconn, 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) // } } // Main program loop reacts to X events ------------------------------------ eventloop: for { xevent, err := xconn.WaitForEvent() if err != nil { log.Println(err) continue } switch e := xevent.(type) { case xproto.KeyPressEvent: if err := handleKeyPressEvent(keymap, e); err != nil { break eventloop } default: log.Println(xevent) } } } // 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(conn *xgb.Conn, windowID xproto.Window) bool { reply, err := xproto.GetWindowAttributes(conn, windowID).Reply() if err != nil { log.Fatal(err) } 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(keymap [256][]xproto.Keysym, 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 // TODO: What do I actually want to return here? }