/
window.go
170 lines (142 loc) · 4.71 KB
/
window.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x11driver
// TODO: implement a back buffer.
import (
"image"
"image/color"
"image/draw"
"sync"
"github.com/BurntSushi/xgb"
"github.com/BurntSushi/xgb/render"
"github.com/BurntSushi/xgb/xproto"
"github.com/niconan/shiny-plan9/shiny/driver/internal/drawer"
"github.com/niconan/shiny-plan9/shiny/driver/internal/event"
"github.com/niconan/shiny-plan9/shiny/driver/internal/lifecycler"
"github.com/niconan/shiny-plan9/shiny/driver/internal/x11key"
"github.com/niconan/shiny-plan9/shiny/screen"
"golang.org/x/image/math/f64"
"golang.org/x/mobile/event/key"
"golang.org/x/mobile/event/mouse"
"golang.org/x/mobile/event/paint"
"golang.org/x/mobile/event/size"
"golang.org/x/mobile/geom"
)
type windowImpl struct {
s *screenImpl
xw xproto.Window
xg xproto.Gcontext
xp render.Picture
event.Deque
xevents chan xgb.Event
// This next group of variables are mutable, but are only modified in the
// screenImpl.run goroutine.
width, height int
lifecycler lifecycler.State
mu sync.Mutex
released bool
}
func (w *windowImpl) Release() {
w.mu.Lock()
released := w.released
w.released = true
w.mu.Unlock()
// TODO: call w.lifecycler.SetDead and w.lifecycler.SendEvent, a la
// handling atomWMDeleteWindow?
if released {
return
}
render.FreePicture(w.s.xc, w.xp)
xproto.FreeGC(w.s.xc, w.xg)
xproto.DestroyWindow(w.s.xc, w.xw)
}
func (w *windowImpl) Upload(dp image.Point, src screen.Buffer, sr image.Rectangle) {
src.(*bufferImpl).upload(xproto.Drawable(w.xw), w.xg, w.s.xsi.RootDepth, dp, sr)
}
func (w *windowImpl) Fill(dr image.Rectangle, src color.Color, op draw.Op) {
fill(w.s.xc, w.xp, dr, src, op)
}
func (w *windowImpl) DrawUniform(src2dst f64.Aff3, src color.Color, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
w.s.drawUniform(w.xp, &src2dst, src, sr, op, opts)
}
func (w *windowImpl) Draw(src2dst f64.Aff3, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
src.(*textureImpl).draw(w.xp, &src2dst, sr, op, opts)
}
func (w *windowImpl) Copy(dp image.Point, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
drawer.Copy(w, dp, src, sr, op, opts)
}
func (w *windowImpl) Scale(dr image.Rectangle, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
drawer.Scale(w, dr, src, sr, op, opts)
}
func (w *windowImpl) Publish() screen.PublishResult {
// TODO: implement a back buffer, and copy or flip that here to the front
// buffer.
// This sync isn't needed to flush the outgoing X11 requests. Instead, it
// acts as a form of flow control. Outgoing requests can be quite small on
// the wire, e.g. draw this texture ID (an integer) to this rectangle (four
// more integers), but much more expensive on the server (blending a
// million source and destination pixels). Without this sync, the Go X11
// client could easily end up sending work at a faster rate than the X11
// server can serve.
w.s.xc.Sync()
return screen.PublishResult{}
}
func (w *windowImpl) handleConfigureNotify(ev xproto.ConfigureNotifyEvent) {
// TODO: does the order of these lifecycle and size events matter? Should
// they really be a single, atomic event?
w.lifecycler.SetVisible((int(ev.X)+int(ev.Width)) > 0 && (int(ev.Y)+int(ev.Height)) > 0)
w.lifecycler.SendEvent(w, nil)
newWidth, newHeight := int(ev.Width), int(ev.Height)
if w.width == newWidth && w.height == newHeight {
return
}
w.width, w.height = newWidth, newHeight
w.Send(size.Event{
WidthPx: newWidth,
HeightPx: newHeight,
WidthPt: geom.Pt(newWidth),
HeightPt: geom.Pt(newHeight),
PixelsPerPt: w.s.pixelsPerPt,
})
}
func (w *windowImpl) handleExpose() {
w.Send(paint.Event{})
}
func (w *windowImpl) handleKey(detail xproto.Keycode, state uint16, dir key.Direction) {
r, c := w.s.keysyms.Lookup(uint8(detail), state)
w.Send(key.Event{
Rune: r,
Code: c,
Modifiers: x11key.KeyModifiers(state),
Direction: dir,
})
}
func (w *windowImpl) handleMouse(x, y int16, b xproto.Button, state uint16, dir mouse.Direction) {
// TODO: should a mouse.Event have a separate MouseModifiers field, for
// which buttons are pressed during a mouse move?
btn := mouse.Button(b)
switch btn {
case 4:
btn = mouse.ButtonWheelUp
case 5:
btn = mouse.ButtonWheelDown
case 6:
btn = mouse.ButtonWheelLeft
case 7:
btn = mouse.ButtonWheelRight
}
if btn.IsWheel() {
if dir != mouse.DirPress {
return
}
dir = mouse.DirStep
}
w.Send(mouse.Event{
X: float32(x),
Y: float32(y),
Button: btn,
Modifiers: x11key.KeyModifiers(state),
Direction: dir,
})
}