forked from BurntSushi/wingo
/
client_event.go
147 lines (131 loc) · 4.33 KB
/
client_event.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
package main
import (
"github.com/BurntSushi/xgb/xproto"
"github.com/BurntSushi/xgbutil"
"github.com/BurntSushi/xgbutil/ewmh"
"github.com/BurntSushi/xgbutil/icccm"
"github.com/BurntSushi/xgbutil/xevent"
"github.com/BurntSushi/xgbutil/xprop"
"github.com/BurntSushi/wingo/focus"
"github.com/BurntSushi/wingo/frame"
"github.com/BurntSushi/wingo/logger"
)
func (c *client) attachEventCallbacks() {
c.win.Listen(xproto.EventMaskPropertyChange |
xproto.EventMaskStructureNotify)
c.cbUnmapNotify().Connect(c.X, c.Id())
c.cbDestroyNotify().Connect(c.X, c.Id())
c.cbConfigureRequest().Connect(c.X, c.Id())
c.cbPropertyNotify().Connect(c.X, c.Id())
c.clientMouseConfig()
c.frameMouseConfig()
}
func (c *client) cbDestroyNotify() xevent.DestroyNotifyFun {
f := func(X *xgbutil.XUtil, ev xevent.DestroyNotifyEvent) {
c.unmanage()
}
return xevent.DestroyNotifyFun(f)
}
func (c *client) cbUnmapNotify() xevent.UnmapNotifyFun {
f := func(X *xgbutil.XUtil, ev xevent.UnmapNotifyEvent) {
// If a client has "iconified" set to true and we get an UnmapNotify
// event, then that means we need to switch focus to the next window
// in the focus stack.
if c.iconified && focus.Current().Id() == c.Id() {
wingo.focusFallback()
}
// When a client issues an Unmap request, the window manager should
// unmanage it. However, when wingo unmaps the window, we shouldn't
// unmanage it. Thus, every time wingo unmaps the window, the
// unmapIgnore counter is incremented. Only when it is zero does it mean
// that we should unmanage the client (i.e., the unmap request came
// from somewhere other than Wingo.)
if c.unmapIgnore > 0 {
c.unmapIgnore--
return
}
c.unmanage()
}
return xevent.UnmapNotifyFun(f)
}
func (c *client) cbConfigureRequest() xevent.ConfigureRequestFun {
f := func(X *xgbutil.XUtil, ev xevent.ConfigureRequestEvent) {
if c.frame.Moving() || c.frame.Resizing() || c.maximized {
logger.Lots.Printf("Denying ConfigureRequest from client because " +
"the client is in the processing of moving/resizing, or is " +
"maximized.")
// As per ICCCM 4.1.5, a window that has not been moved or resized
// must receive a synthetic ConfigureNotify event.
c.sendConfigureNotify()
return
}
flags := int(ev.ValueMask) &
^int(xproto.ConfigWindowStackMode) &
^int(xproto.ConfigWindowSibling)
x, y, w, h := frame.ClientToFrame(c.frame,
int(ev.X), int(ev.Y), int(ev.Width), int(ev.Height))
c.LayoutMROpt(flags, x, y, w, h)
}
return xevent.ConfigureRequestFun(f)
}
func (c *client) sendConfigureNotify() {
geom := c.Frame().Geom()
ev := xproto.ConfigureNotifyEvent{
Event: c.Id(),
Window: c.Id(),
AboveSibling: 0,
X: int16(geom.X()),
Y: int16(geom.Y()),
Width: uint16(c.win.Geom.Width()),
Height: uint16(c.win.Geom.Height()),
BorderWidth: 0,
OverrideRedirect: false,
}
xproto.SendEvent(c.X.Conn(), false, c.Id(),
xproto.EventMaskStructureNotify, string(ev.Bytes()))
}
func (c *client) cbPropertyNotify() xevent.PropertyNotifyFun {
// helper function to log property vals
showVals := func(o, n interface{}) {
logger.Lots.Printf("\tOld value: '%s', new value: '%s'", o, n)
}
f := func(X *xgbutil.XUtil, ev xevent.PropertyNotifyEvent) {
name, err := xprop.AtomName(c.X, ev.Atom)
if err != nil {
logger.Warning.Printf("Could not get property atom name for '%s' "+
"because: %s.", ev, err)
}
logger.Lots.Printf("Updating property %s with state %v on window %s",
name, ev.State, c)
switch name {
case "_NET_WM_VISIBLE_NAME":
fallthrough
case "_NET_WM_NAME":
fallthrough
case "WM_NAME":
c.refreshName()
case "_NET_WM_ICON":
case "WM_HINTS":
if hints, err := icccm.WmHintsGet(X, c.Id()); err == nil {
c.hints = hints
}
case "WM_NORMAL_HINTS":
if nhints, err := icccm.WmNormalHintsGet(X, c.Id()); err == nil {
c.nhints = nhints
}
case "WM_TRANSIENT_FOR":
if trans, err := icccm.WmTransientForGet(X, c.Id()); err == nil {
if transCli := wingo.findManagedClient(trans); transCli != nil {
c.transientFor = transCli
}
}
case "_NET_WM_USER_TIME":
if newTime, err := ewmh.WmUserTimeGet(X, c.Id()); err == nil {
showVals(c.time, newTime)
c.time = xproto.Timestamp(newTime)
}
case "_NET_WM_STRUT_PARTIAL":
}
}
return xevent.PropertyNotifyFun(f)
}