1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
|
-----------------------------------------------------------------------------
-- |
-- Module : Main.hs
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : sjanssen@cse.unl.edu
-- Stability : unstable
-- Portability : not portable, uses mtl, X11, posix
--
-----------------------------------------------------------------------------
--
-- xmonad, a minimal window manager for X11
--
import Data.Bits
import qualified Data.Map as M
import Graphics.X11.Xlib hiding (refreshKeyboardMapping)
import Graphics.X11.Xlib.Extras
import Graphics.X11.Xinerama
import Control.Monad.State
import qualified StackSet as W
import XMonad
import Operations
import Config
--
-- The main entry point
--
main :: IO ()
main = do
dpy <- openDisplay ""
let dflt = defaultScreen dpy
rootw <- rootWindow dpy dflt
wmdelt <- internAtom dpy "WM_DELETE_WINDOW" False
wmprot <- internAtom dpy "WM_PROTOCOLS" False
xinesc <- getScreenInfo dpy
let st = XState
{ display = dpy
, xineScreens = xinesc
, theRoot = rootw
, wmdelete = wmdelt
, wmprotocols = wmprot
-- fromIntegral needed for X11 versions that use Int instead of CInt.
, dimensions = (fromIntegral (displayWidth dpy dflt),
fromIntegral (displayHeight dpy dflt))
, workspace = W.empty workspaces (length xinesc)
, defaultLayoutDesc = startingLayoutDesc
, layoutDescs = M.empty
}
xSetErrorHandler -- in C, I'm too lazy to write the binding
-- setup initial X environment
sync dpy False
selectInput dpy rootw $ substructureRedirectMask
.|. substructureNotifyMask
.|. enterWindowMask
.|. leaveWindowMask
grabKeys dpy rootw
sync dpy False
ws <- scan dpy rootw
allocaXEvent $ \e ->
runX st $ do
mapM_ manage ws
forever $ handle =<< xevent dpy e
where
xevent d e = io (nextEvent d e >> getEvent e)
forever a = a >> forever a
-- ---------------------------------------------------------------------
-- IO stuff. Doesn't require any X state
-- Most of these things run only on startup (bar grabkeys)
-- | scan for any initial windows to manage
scan :: Display -> Window -> IO [Window]
scan dpy rootw = do
(_, _, ws) <- queryTree dpy rootw
filterM ok ws
where
ok w = do wa <- getWindowAttributes dpy w
return $ not (wa_override_redirect wa)
&& wa_map_state wa == waIsViewable
-- | Grab the keys back
grabKeys :: Display -> Window -> IO ()
grabKeys dpy rootw = do
ungrabKey dpy '\0' {-AnyKey-} anyModifier rootw
flip mapM_ (M.keys keys) $ \(mask,sym) -> do
kc <- keysymToKeycode dpy sym
-- "If the specified KeySym is not defined for any KeyCode,
-- XKeysymToKeycode() returns zero."
when (kc /= '\0') $ mapM_ (grab kc . (mask .|.)) $ [0, numlockMask, lockMask, numlockMask .|. lockMask]
where
grab kc m = grabKey dpy kc m rootw True grabModeAsync grabModeAsync
-- ---------------------------------------------------------------------
-- Event handler
--
-- | handle. Handle X events
--
-- Events dwm handles that we don't:
--
-- [ButtonPress] = buttonpress,
-- [Expose] = expose,
-- [PropertyNotify] = propertynotify,
--
-- Todo: seperate IO from X monad stuff. We want to be able to test the
-- handler, and client functions, with dummy X interface ops, in QuickCheck
--
-- Will require an abstract interpreter from Event -> X Action, which
-- modifies the internal X state, and then produces an IO action to
-- evaluate.
--
-- XCreateWindowEvent(3X11)
-- Window manager clients normally should ignore this window if the
-- override_redirect member is True.
--
handle :: Event -> X ()
-- run window manager command
handle (KeyEvent {ev_event_type = t, ev_state = m, ev_keycode = code})
| t == keyPress
= withDisplay $ \dpy -> do
s <- io $ keycodeToKeysym dpy code 0
whenJust (M.lookup (complement (numlockMask .|. lockMask) .&. m,s) keys) id
-- manage a new window
handle (MapRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
wa <- io $ getWindowAttributes dpy w -- ignore override windows
when (not (wa_override_redirect wa)) $ manage w
-- window destroyed, unmanage it
handle (DestroyWindowEvent {ev_window = w}) = do b <- isClient w; when b $ unmanage w
-- window gone, unmanage it
handle (UnmapEvent {ev_window = w}) = do b <- isClient w; when b $ unmanage w
-- set keyboard mapping
handle e@(MappingNotifyEvent {ev_window = w}) = do
-- this fromIntegral is only necessary with the old X11 version that uses
-- Int instead of CInt. TODO delete it when there is a new release of X11
let m = (ev_request e, ev_first_keycode e, fromIntegral $ ev_count e)
withDisplay $ \d -> io $ refreshKeyboardMapping d m
when (ev_request e == mappingKeyboard) $ withDisplay $ io . flip grabKeys w
-- click on an unfocussed window
handle (ButtonEvent {ev_window = w, ev_event_type = t})
| t == buttonPress
= safeFocus w
-- entered a normal window
handle e@(CrossingEvent {ev_window = w, ev_event_type = t})
| t == enterNotify && ev_mode e == notifyNormal && ev_detail e /= notifyInferior
= safeFocus w
-- left a window, check if we need to focus root
handle e@(CrossingEvent {ev_event_type = t})
| t == leaveNotify
= do rootw <- gets theRoot
when (ev_window e == rootw && not (ev_same_screen e)) $ setFocus rootw
-- configure a window
handle e@(ConfigureRequestEvent {ev_window = w}) = do
XState { display = dpy, workspace = ws } <- get
when (W.member w ws) $ -- already managed, reconfigure (see client:configure()
trace ("Reconfigure already managed window: " ++ show w)
io $ configureWindow dpy (ev_window e) (ev_value_mask e) $ WindowChanges
{ wc_x = ev_x e
, wc_y = ev_y e
, wc_width = ev_width e
, wc_height = ev_height e
, wc_border_width = ev_border_width e
, wc_sibling = ev_above e
-- this fromIntegral is only necessary with the old X11 version that uses
-- Int instead of CInt. TODO delete it when there is a new release of X11
, wc_stack_mode = fromIntegral $ ev_detail e
}
io $ sync dpy False
handle e = trace (eventName e) -- ignoring
|