"github.com/jezek/xgb/xinerama"
"github.com/jezek/xgb/xproto"
// TODO: Make a separate keysym file and fully populate it.
screen xinerama
.ScreenInfo
// Set ourselves up as the new window manager ------------------------------
xconn
:= connectToXServer()
keymap
:= getKeyboardMap(xconn
)
registerForKeyEvents(xconn
, xroot
, keymap
)
// Build a list of output devices ------------------------------------------
var attachedDisplays
[]display
for _
, screen
:= range getAttachedScreens(xconn
) {
attachedDisplays
= append(attachedDisplays
, display
{screen
: screen
})
// 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?
// Build a list of windows needing management ------------------------------
// TODO: The following should all be an initialization function. It should not be here.
tree
, err
:= xproto
.QueryTree(xconn
, xroot
.Root
).Reply()
for _
, c
:= range tree
.Children
{
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
)
// TODO: Try tiling/displaying/whatever with any existing windows before entering the event loop.
// Main program loop reacts to X events ------------------------------------
fmt
.Printf("ILLI: attachedDisplays: ")
fmt
.Println(attachedDisplays
)
xevent
, err
:= xconn
.WaitForEvent()
switch e
:= xevent
.(type) {
case xproto
.KeyPressEvent
:
fmt
.Printf("ILLI: Received xproto.KeyPressEvent\n")
if err
:= handleKeyPressEvent(keymap
, e
); err
!= nil {
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
{
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 (
attachedDisplays
[0].windows
[0],
xproto
.ConfigWindowWidth |
xproto
.ConfigWindowHeight
,
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.
fmt
.Println("ILLI: Failed to handle MapRequestEvent")
case xproto
.EnterNotifyEvent
:
fmt
.Printf("ILLI: Received xproto.EnterNotifyEvent\n")
fmt
.Printf("ILLI: Unknown X event received: %s\n", 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 reply
!= nil && reply
.MapState
== 2 {
// 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 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
case xproto
.ModMask1 | xproto
.ModMaskShift
:
cmd
:= exec
.Command("xterm")
return nil // TODO: What do I actually want to return here?