XK_Return = 0xff0d
)
-var xc *xgb.Conn
-var attachedScreens []xinerama.ScreenInfo
-var xroot xproto.ScreenInfo
-var keymap [256][]xproto.Keysym
+type display struct {
+ screen xinerama.ScreenInfo
+ windows []xproto.Window
+}
func main() {
- xConn, err := xgb.NewConn()
- if err != nil {
- log.Fatal(err)
- }
- xc = xConn
- defer xc.Close()
+ // Set ourselves up as the new window manager ------------------------------
- setup := xproto.Setup(xc)
- if setup == nil || len(setup.Roots) < 1 {
- log.Fatal("ILLI: Unable to parse received SetupInfo from X11 server.")
- }
+ xconn := connectToXServer()
+ xroot := getXRoot(xconn)
+ keymap := getKeyboardMap(xconn)
+ registerForKeyEvents(xconn, xroot, keymap)
- if err := xinerama.Init(xc); err != nil {
- log.Fatal(err)
- }
+ becomeWM(xconn, xroot)
- 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
- }
- }
+ // Build a list of output devices ------------------------------------------
- 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?")
+ var attachedDisplays []display
+ for _, screen := range getAttachedScreens(xconn) {
+ attachedDisplays = append(attachedDisplays, display{screen: screen})
}
- xroot = connInfo.Roots[0]
+ // TODO: Verify some displays actually exist and we don't have an empty array. Otherwise, die here.
+ // TODO: Should this all go in its own function and simply return attachedDisplays[] here instead? What will my fundamental data structure be?
- // 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
- )
+ // Build a list of windows needing management ------------------------------
- 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()
+ // TODO: The following should all be an initialization function. It should not be here.
+ tree, err := xproto.QueryTree(xconn, xroot.Root).Reply()
if err != nil {
+ // TODO: Better error
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 isMappedWindow(xconn, c) {
+ // For now, dump any pre-existing windows into the first
+ // display. Later we can add functionality for tracking window
+ // attributes across window manager restarts.
+ attachedDisplays[0].windows = append(attachedDisplays[0].windows, c)
}
}
-
-// if len(attachedScreens) > 0 {
-// defaultw.Screen = &attachedScreens[0]
-// }
-//
-// workspaces["default"] = defaultw
-//
-// if err := defaultw.TileWindows(); err != nil {
-// log.Println(err)
-// }
-
+ // TODO: Try tiling/displaying/whatever with any existing windows before entering the event loop.
}
+ // Main program loop reacts to X events ------------------------------------
+
eventloop:
for {
- xevent, err := xc.WaitForEvent()
+ fmt.Printf("ILLI: attachedDisplays: ")
+ fmt.Println(attachedDisplays)
+ xevent, err := xconn.WaitForEvent()
if err != nil {
log.Println(err)
continue
}
switch e := xevent.(type) {
case xproto.KeyPressEvent:
- if err := handleKeyPressEvent(e); err != nil {
+ fmt.Printf("ILLI: Received xproto.KeyPressEvent\n")
+ if err := handleKeyPressEvent(keymap, e); err != nil {
break eventloop
}
+ case xproto.KeyReleaseEvent:
+ fmt.Printf("ILLI: Received xproto.KeyReleaseEvent\n")
+ case xproto.DestroyNotifyEvent:
+ fmt.Printf("ILLI: Received xproto.DestroyNotifyEvent\n")
+ case xproto.ConfigureRequestEvent:
+ fmt.Printf("ILLI: Received xproto.ConfigureRequestEvent\n")
+ rewrittenEvent := xproto.ConfigureNotifyEvent{
+ Event: e.Window,
+ Window: e.Window,
+ AboveSibling: 0,
+ X: e.X,
+ Y: e.Y,
+ Width: e.Width,
+ Height: e.Height,
+ BorderWidth: 0,
+ OverrideRedirect: false,
+ }
+ xproto.SendEventChecked(xconn, false, e.Window, xproto.EventMaskStructureNotify, string(rewrittenEvent.Bytes()))
+ case xproto.MapRequestEvent:
+ fmt.Printf("ILLI: Received xproto.MapRequestEvent\n")
+ if windowAttributes, err := xproto.GetWindowAttributes(xconn, e.Window).Reply(); err != nil || !windowAttributes.OverrideRedirect {
+ xproto.MapWindowChecked(xconn, e.Window)
+ attachedDisplays[0].windows = append(attachedDisplays[0].windows, e.Window)
+
+ if err := xproto.ConfigureWindowChecked (
+ xconn,
+ attachedDisplays[0].windows[0],
+ xproto.ConfigWindowX |
+ xproto.ConfigWindowY |
+ xproto.ConfigWindowWidth |
+ xproto.ConfigWindowHeight,
+ []uint32{
+ uint32(100), // TODO: Temporarily hardcoding for testing.
+ uint32(100), // TODO: Temporarily hardcoding for testing.
+ uint32(1720), // TODO: Temporarily hardcoding for testing.
+ uint32(1000), // TODO: Temporarily hardcoding for testing.
+ }).Check(); err != nil {
+ fmt.Println("ILLI: Failed to handle MapRequestEvent")
+ }
+
+ }
+ case xproto.EnterNotifyEvent:
+ fmt.Printf("ILLI: Received xproto.EnterNotifyEvent\n")
default:
- log.Println(xevent)
+ fmt.Printf("ILLI: Unknown X event received: %s\n", 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()
+// 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)
}
- // 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 {
+// 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 {
// We must manually close the X connection since we used
// `defer` when setting it up and os.Exit() short-circuits
// that deferral.
- xc.Close()
+ //xc.Close()
os.Exit(0)
}
case XK_Return:
default:
return nil
}
- return nil // What do I actually want to return here?
+ return nil // TODO: What do I actually want to return here?
}