X-Git-Url: https://git.subgeniuskitty.com/illi/.git/blobdiff_plain/6c708f255a57437500f06919974c66011c911abf..HEAD:/illi.go diff --git a/illi.go b/illi.go index 5cb1641..e527d65 100644 --- a/illi.go +++ b/illi.go @@ -17,203 +17,138 @@ const ( 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 { @@ -223,7 +158,7 @@ func handleKeyPressEvent(key xproto.KeyPressEvent) error { // 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: @@ -238,5 +173,5 @@ func handleKeyPressEvent(key xproto.KeyPressEvent) error { default: return nil } - return nil // What do I actually want to return here? + return nil // TODO: What do I actually want to return here? }