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  }