New milestone: can launch and display an xterm
[illi] / initialization.go
CommitLineData
19cbf0a1
AT
1package main
2
3import (
4 "fmt"
5 "os"
6
7 "github.com/jezek/xgb"
8 "github.com/jezek/xgb/xinerama"
9 "github.com/jezek/xgb/xproto"
10)
11
12
13
14// -----------------------------------------------------------------------------
15// Note: Since functions in this file are related to initialization and thus
16// execute at a time when the program is still building toward a viable state,
17// these functions intentionally exit via a series of explicit commands,
18// cleaning up only what has already been initialized rather than simply
19// calling the normal illi die/restart functions.
20// -----------------------------------------------------------------------------
21
22
23
effccebe 24// Note: The caller is responsible for tracking the lifetime of the returned X
19cbf0a1
AT
25// connection and calling connection.Close() when appropriate.
26func connectToXServer() *xgb.Conn {
27 connection, err := xgb.NewConn()
28 if err != nil {
29 fmt.Fprintf(os.Stderr, "ILLI: Failed to connect to X server: %s\n", err.Error())
30 os.Exit(1)
31 }
32 return connection
33}
34
35func getAttachedScreens(conn *xgb.Conn) (screens []xinerama.ScreenInfo) {
effccebe 36 // Attempt to use xinerama to obtain screen information.
19cbf0a1
AT
37 err := xinerama.Init(conn)
38 if err != nil {
39 fmt.Fprintf(os.Stderr, "ILLI: Unable to initialize xinerama: %s\n", err.Error())
40 } else { // Xinerama successfully initialized.
41 reply, err := xinerama.QueryScreens(conn).Reply()
42 if err != nil {
43 fmt.Fprintf(os.Stderr, "ILLI: Unusable result when querying screens through xinerama: %s\n", err.Error())
44 } else {
45 screens = reply.ScreenInfo
46 return
47 }
48 }
49
50 // Since we were unable to obtain the screen information via xinerama, we
51 // fall back to requesting ScreenInfo directly from the X server.
52 setup := xproto.Setup(conn)
53 if setup == nil || len(setup.Roots) < 1 {
54 fmt.Fprintf(os.Stderr, "ILLI: Unusable ScreenInfo received from X server.\n")
55 conn.Close()
56 os.Exit(1)
57 } else {
58 screens = []xinerama.ScreenInfo{
59 xinerama.ScreenInfo{
60 Width: setup.Roots[0].WidthInPixels,
61 Height: setup.Roots[0].HeightInPixels,
62 },
63 }
64 return
65 }
66 return // Should be unreachable
67}
68
69func getXRoot(conn *xgb.Conn) xproto.ScreenInfo {
70 setup := xproto.Setup(conn)
71 if setup == nil || len(setup.Roots) < 1 {
72 fmt.Fprintf(os.Stderr, "ILLI: Failed to determine X root.\n")
73 conn.Close()
74 os.Exit(1)
75 }
76 return setup.Roots[0]
77}
78
79func getKeyboardMap(conn *xgb.Conn) (keymap [256][]xproto.Keysym) { // TODO: Why 256?
effccebe 80 const ( // TODO: Why? How does the keymap work under the hood?
19cbf0a1
AT
81 loKey = 8
82 hiKey = 255
83 )
84
85 m := xproto.GetKeyboardMapping(conn, loKey, hiKey-loKey+1)
86 r, err := m.Reply()
87 if err != nil || r == nil {
88 fmt.Fprintf(os.Stderr, "ILLI: Failed to load keymap from X server: %s\n.", err.Error())
89 conn.Close()
90 os.Exit(1)
91 }
92
93 for i := 0; i < hiKey-loKey+1; i++ {
94 keymap[loKey+i] = r.Keysyms[i*int(r.KeysymsPerKeycode) : (i+1)*int(r.KeysymsPerKeycode)]
95 }
96 return keymap
97}
98
99func registerForKeyEvents(conn *xgb.Conn, root xproto.ScreenInfo, keymap [256][]xproto.Keysym) {
100 // First we populate []grabs with the keysym and modifiers we seek.
101 grabs := []struct {
102 sym xproto.Keysym
103 modifiers uint16
104 codes []xproto.Keycode
105 }{
106 // TODO: Need to define a config table and do both keysym grabs and
107 // main event loop using the config table rather than hardcoding key
108 // assignments throughout the code.
109 {
110 sym: XK_q,
111 modifiers: xproto.ModMask1 | xproto.ModMaskShift,
112 },
113 {
114 sym: XK_Return,
115 modifiers: xproto.ModMask1 | xproto.ModMaskShift,
116 },
117 }
118
119 // Next we use the keymap provided by the X server to identify the keycodes
120 // corresponding to the keysyms in []grabs.
121 for i, syms := range keymap {
122 for _, sym := range syms {
123 for c := range grabs {
124 if grabs[c].sym == sym {
125 grabs[c].codes = append(grabs[c].codes, xproto.Keycode(i))
126 }
127 }
128 }
129 }
130
131 // Finally, we register with the X server for each combo of
132 // keycode+modifiers in []grabs.
133 for _, grabbed := range grabs {
134 for _, code := range grabbed.codes {
135 err := xproto.GrabKeyChecked(
136 conn,
137 false,
138 root.Root,
139 grabbed.modifiers,
140 code,
141 xproto.GrabModeAsync,
142 xproto.GrabModeAsync,
143 ).Check()
144 if err != nil {
145 fmt.Fprintf(os.Stderr,
146 "ILLI: Failed to register keycode 0x%X with modifier(s) 0x%X: %s\n",
147 code, grabbed.modifiers, err.Error())
148 }
149
150 }
151 }
152}
153
154// In addition to the basic key/button press/release events, by registering for
155// events like SubstructureRedirect, we are declaring our intent to act as
156// window manager for this X server. Only one WM is allowed per X server, a
157// policy enforced on the X server's side by refusing the registration request
158// for events like SubstructureRedirect if an existing process already receives
159// those events (i.e. is already the window manager).
160func becomeWM(conn *xgb.Conn, root xproto.ScreenInfo) {
161 err := xproto.ChangeWindowAttributesChecked(
162 conn,
163 root.Root,
164 xproto.CwEventMask,
165 []uint32{
166 xproto.EventMaskKeyPress |
167 xproto.EventMaskKeyRelease |
168 xproto.EventMaskButtonPress |
169 xproto.EventMaskButtonRelease |
170 xproto.EventMaskStructureNotify |
171 xproto.EventMaskSubstructureRedirect,
172 // TODO: Should we also register for EventMaskSubstructureNotify ?
173 // Where is the authoritative list of events located?
174 }).Check()
175 if err != nil {
176 fmt.Fprintf(os.Stderr, "ILLI: Unable to register as WM with X server: %s\n", err.Error())
177 conn.Close()
178 os.Exit(1)
179 }
180}
181