forked from BurntSushi/wingo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
root.go
149 lines (131 loc) · 4.47 KB
/
root.go
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
package main
import (
"github.com/BurntSushi/xgb/xproto"
"github.com/BurntSushi/xgbutil"
"github.com/BurntSushi/xgbutil/xevent"
"github.com/BurntSushi/xgbutil/xprop"
"github.com/BurntSushi/xgbutil/xrect"
"github.com/BurntSushi/xgbutil/xwindow"
"github.com/alexanderkyte/wingo/focus"
"github.com/alexanderkyte/wingo/logger"
"github.com/alexanderkyte/wingo/wm"
"github.com/alexanderkyte/wingo/xclient"
)
func rootInit(X *xgbutil.XUtil) {
var err error
// Listen to Root. It is all-important.
evMasks := xproto.EventMaskPropertyChange |
xproto.EventMaskFocusChange |
xproto.EventMaskButtonPress |
xproto.EventMaskButtonRelease |
xproto.EventMaskStructureNotify |
xproto.EventMaskSubstructureNotify |
xproto.EventMaskSubstructureRedirect
if wm.Config.FfmHead {
evMasks |= xproto.EventMaskPointerMotion
}
err = xwindow.New(X, X.RootWin()).Listen(evMasks)
if err != nil {
logger.Error.Fatalf("Could not listen to Root window events: %s", err)
}
// Update state when the root window changes size
wm.RootGeomChangeFun().Connect(X, wm.Root.Id)
// Oblige map request events
xevent.MapRequestFun(
func(X *xgbutil.XUtil, ev xevent.MapRequestEvent) {
xclient.New(ev.Window)
}).Connect(X, wm.Root.Id)
// Oblige configure requests from windows we don't manage.
xevent.ConfigureRequestFun(
func(X *xgbutil.XUtil, ev xevent.ConfigureRequestEvent) {
// Make sure we aren't managing this client.
if wm.FindManagedClient(ev.Window) != nil {
return
}
xwindow.New(X, ev.Window).Configure(int(ev.ValueMask),
int(ev.X), int(ev.Y), int(ev.Width), int(ev.Height),
ev.Sibling, ev.StackMode)
}).Connect(X, wm.Root.Id)
xevent.FocusInFun(
func(X *xgbutil.XUtil, ev xevent.FocusInEvent) {
if ignoreRootFocus(ev.Mode, ev.Detail) {
return
}
if len(wm.Workspace().Clients) == 0 {
return
}
wm.FocusFallback()
}).Connect(X, wm.Root.Id)
// Listen to Root client message events. This is how we handle all
// of the EWMH bullshit.
xevent.ClientMessageFun(handleClientMessages).Connect(X, wm.Root.Id)
// Check where the pointer is on motion events. If it's crossed a monitor
// boundary, switch the focus of the head.
if wm.Config.FfmHead {
xevent.MotionNotifyFun(handleMotionNotify).Connect(X, wm.Root.Id)
}
}
func handleClientMessages(X *xgbutil.XUtil, ev xevent.ClientMessageEvent) {
name, err := xprop.AtomName(X, ev.Type)
if err != nil {
logger.Warning.Printf("Could not get atom name for '%s': %s", ev, err)
return
}
switch name {
case "_NET_NUMBER_OF_DESKTOPS":
logger.Warning.Printf("Wingo does not support adding/removing " +
"desktops using the _NET_NUMBER_OF_DESKTOPS property. Please use " +
"the Wingo commands 'AddWorkspace' and 'RemoveWorkspace' to add " +
"or remove workspaces.")
case "_NET_DESKTOP_GEOMETRY":
logger.Warning.Printf("Wingo does not support the " +
"_NET_DESKTOP_GEOMETRY property. Namely, more than one workspace " +
"can be visible at a time, so different workspaces can have " +
"different geometries.")
case "_NET_DESKTOP_VIEWPORT":
logger.Warning.Printf("Wingo does not use viewports, and therefore " +
"does not support the _NET_DESKTOP_VIEWPORT property.")
case "_NET_CURRENT_DESKTOP":
index := int(ev.Data.Data32[0])
if wrk := wm.Heads.Workspaces.Get(index); wrk != nil {
wm.SetWorkspace(wrk, false)
wm.FocusFallback()
} else {
logger.Warning.Printf("Desktop index %d is not in the range "+
"[0, %d).", index, len(wm.Heads.Workspaces.Wrks))
}
default:
logger.Warning.Printf("Unknown root client message: %s", name)
}
}
func handleMotionNotify(X *xgbutil.XUtil, ev xevent.MotionNotifyEvent) {
qp, err := xproto.QueryPointer(X.Conn(), X.RootWin()).Reply()
if err != nil {
logger.Warning.Printf("Could not query pointer: %s", err)
return
}
geom := xrect.New(int(qp.RootX), int(qp.RootY), 1, 1)
if wrk := wm.Heads.FindMostOverlap(geom); wrk != nil {
if wrk != wm.Workspace() {
wm.SetWorkspace(wrk, false)
wm.FocusFallback()
}
}
}
func ignoreRootFocus(modeByte, detailByte byte) bool {
mode, detail := focus.Modes[modeByte], focus.Details[detailByte]
if mode == "NotifyGrab" || mode == "NotifyUngrab" {
return true
}
if detail == "NotifyAncestor" ||
detail == "NotifyInferior" ||
detail == "NotifyVirtual" ||
detail == "NotifyNonlinear" ||
detail == "NotifyNonlinearVirtual" ||
detail == "NotifyPointer" {
return true
}
// Only accept modes: NotifyNormal and NotifyWhileGrabbed
// Only accept details: NotifyPointerRoot, NotifyNone
return false
}