github.com/as/shiny@v0.8.2/driver/x11driver/window.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package x11driver 6 7 // TODO: implement a back buffer. 8 9 import ( 10 "image" 11 "image/color" 12 "image/draw" 13 "sync" 14 15 "github.com/BurntSushi/xgb" 16 "github.com/BurntSushi/xgb/render" 17 "github.com/BurntSushi/xgb/xproto" 18 19 "github.com/as/shiny/driver/internal/drawer" 20 "github.com/as/shiny/driver/internal/x11key" 21 "github.com/as/shiny/event/key" 22 "github.com/as/shiny/event/mouse" 23 "github.com/as/shiny/event/paint" 24 "github.com/as/shiny/event/size" 25 "github.com/as/shiny/geom" 26 "github.com/as/shiny/math/f64" 27 "github.com/as/shiny/screen" 28 ) 29 30 type windowImpl struct { 31 s *screenImpl 32 33 xw xproto.Window 34 xg xproto.Gcontext 35 xp render.Picture 36 37 xevents chan xgb.Event 38 39 // This next group of variables are mutable, but are only modified in the 40 // screenImpl.run goroutine. 41 width, height int 42 43 mu sync.Mutex 44 released bool 45 } 46 47 func (w *windowImpl) Device() *screen.Device { 48 return screen.Dev // TODO(as): multiple windows? 49 } 50 51 func (w *windowImpl) Release() { 52 render.FreePicture(w.s.xc, w.xp) 53 xproto.FreeGC(w.s.xc, w.xg) 54 xproto.DestroyWindow(w.s.xc, w.xw) 55 } 56 57 func (w *windowImpl) Upload(dp image.Point, src screen.Buffer, sr image.Rectangle) { 58 src.(*bufferImpl).upload(xproto.Drawable(w.xw), w.xg, w.s.xsi.RootDepth, dp, sr) 59 } 60 61 func (w *windowImpl) Fill(dr image.Rectangle, src color.Color, op draw.Op) { 62 fill(w.s.xc, w.xp, dr, src, op) 63 } 64 65 func (w *windowImpl) DrawUniform(src2dst f64.Aff3, src color.Color, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) { 66 w.s.drawUniform(w.xp, &src2dst, src, sr, op, opts) 67 } 68 69 func (w *windowImpl) Draw(src2dst f64.Aff3, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) { 70 src.(*textureImpl).draw(w.xp, &src2dst, sr, op, opts) 71 } 72 73 func (w *windowImpl) Copy(dp image.Point, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) { 74 drawer.Copy(w, dp, src, sr, op, opts) 75 } 76 77 func (w *windowImpl) Scale(dr image.Rectangle, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) { 78 drawer.Scale(w, dr, src, sr, op, opts) 79 } 80 81 func (w *windowImpl) Publish() screen.PublishResult { 82 // TODO: implement a back buffer, and copy or flip that here to the front 83 // buffer. 84 85 // This sync isn't needed to flush the outgoing X11 requests. Instead, it 86 // acts as a form of flow control. Outgoing requests can be quite small on 87 // the wire, e.g. draw this texture ID (an integer) to this rectangle (four 88 // more integers), but much more expensive on the server (blending a 89 // million source and destination pixels). Without this sync, the Go X11 90 // client could easily end up sending work at a faster rate than the X11 91 // server can serve. 92 93 w.s.xc.Sync() 94 return screen.PublishResult{} 95 } 96 97 func (w *windowImpl) handleConfigureNotify(ev xproto.ConfigureNotifyEvent) { 98 // TODO: does the order of these lifecycle and size events matter? Should 99 // they really be a single, atomic event? 100 //w.lifecycler.SetVisible((int(ev.X)+int(ev.Width)) > 0 && (int(ev.Y)+int(ev.Height)) > 0) 101 102 newWidth, newHeight := int(ev.Width), int(ev.Height) 103 if w.width == newWidth && w.height == newHeight { 104 return 105 } 106 w.width, w.height = newWidth, newHeight 107 screen.SendSize(size.Event{ 108 WidthPx: newWidth, 109 HeightPx: newHeight, 110 WidthPt: geom.Pt(newWidth), 111 HeightPt: geom.Pt(newHeight), 112 PixelsPerPt: w.s.pixelsPerPt, 113 }) 114 } 115 116 func (w *windowImpl) handleExpose() { 117 screen.SendPaint(paint.Event{External: true}) 118 } 119 120 func (w *windowImpl) handleKey(detail xproto.Keycode, state uint16, dir key.Direction) { 121 r, c := w.s.keysyms.Lookup(uint8(detail), state) 122 screen.SendKey(key.Event{ 123 Rune: r, 124 Code: c, 125 Modifiers: x11key.KeyModifiers(state), 126 Direction: dir, 127 }) 128 } 129 130 func (w *windowImpl) handleMouse(x, y int16, b xproto.Button, state uint16, dir mouse.Direction) { 131 // TODO: should a mouse.Event have a separate MouseModifiers field, for 132 // which buttons are pressed during a mouse move? 133 btn := mouse.Button(b) 134 switch btn { 135 case 4: 136 btn = mouse.ButtonWheelUp 137 case 5: 138 btn = mouse.ButtonWheelDown 139 case 6: 140 btn = mouse.ButtonWheelLeft 141 case 7: 142 btn = mouse.ButtonWheelRight 143 } 144 if btn.IsWheel() { 145 if dir != mouse.DirPress { 146 return 147 } 148 dir = mouse.DirStep 149 screen.SendScroll(mouse.Event{ 150 X: float32(x), 151 Y: float32(y), 152 Button: btn, 153 Modifiers: x11key.KeyModifiers(state), 154 Direction: dir, 155 }) 156 } 157 screen.SendMouse(mouse.Event{ 158 X: float32(x), 159 Y: float32(y), 160 Button: btn, 161 Modifiers: x11key.KeyModifiers(state), 162 Direction: dir, 163 }) 164 }