github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/exp/shiny/driver/x11driver/screen.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  import (
     8  	"fmt"
     9  	"image"
    10  	"log"
    11  	"sync"
    12  
    13  	"github.com/BurntSushi/xgb"
    14  	"github.com/BurntSushi/xgb/render"
    15  	"github.com/BurntSushi/xgb/shm"
    16  	"github.com/BurntSushi/xgb/xproto"
    17  
    18  	"golang.org/x/exp/shiny/driver/internal/pump"
    19  	"golang.org/x/exp/shiny/screen"
    20  	"golang.org/x/mobile/event/key"
    21  	"golang.org/x/mobile/event/mouse"
    22  )
    23  
    24  // TODO: check that xgb is safe to use concurrently from multiple goroutines.
    25  // For example, its Conn.WaitForEvent concept is a method, not a channel, so
    26  // it's not obvious how to interrupt it to service a NewWindow request.
    27  
    28  type screenImpl struct {
    29  	xc      *xgb.Conn
    30  	xsi     *xproto.ScreenInfo
    31  	keysyms [256][2]xproto.Keysym
    32  
    33  	atomWMDeleteWindow xproto.Atom
    34  	atomWMProtocols    xproto.Atom
    35  	atomWMTakeFocus    xproto.Atom
    36  
    37  	pictformat24 render.Pictformat
    38  	pictformat32 render.Pictformat
    39  
    40  	// window32 and its related X11 resources is an unmapped window so that we
    41  	// have a depth-32 window to create depth-32 pixmaps from, i.e. pixmaps
    42  	// with an alpha channel. The root window isn't guaranteed to be depth-32.
    43  	gcontext32 xproto.Gcontext
    44  	window32   xproto.Window
    45  
    46  	mu              sync.Mutex
    47  	buffers         map[shm.Seg]*bufferImpl
    48  	uploads         map[uint16]chan struct{}
    49  	windows         map[xproto.Window]*windowImpl
    50  	nPendingUploads int
    51  	completionKeys  []uint16
    52  }
    53  
    54  func newScreenImpl(xc *xgb.Conn) (*screenImpl, error) {
    55  	s := &screenImpl{
    56  		xc:      xc,
    57  		xsi:     xproto.Setup(xc).DefaultScreen(xc),
    58  		buffers: map[shm.Seg]*bufferImpl{},
    59  		uploads: map[uint16]chan struct{}{},
    60  		windows: map[xproto.Window]*windowImpl{},
    61  	}
    62  	if err := s.initAtoms(); err != nil {
    63  		return nil, err
    64  	}
    65  	if err := s.initKeyboardMapping(); err != nil {
    66  		return nil, err
    67  	}
    68  	if err := s.initPictformats(); err != nil {
    69  		return nil, err
    70  	}
    71  	if err := s.initWindow32(); err != nil {
    72  		return nil, err
    73  	}
    74  	go s.run()
    75  	return s, nil
    76  }
    77  
    78  func (s *screenImpl) run() {
    79  	for {
    80  		ev, err := s.xc.WaitForEvent()
    81  		if err != nil {
    82  			log.Printf("x11driver: xproto.WaitForEvent: %v", err)
    83  			continue
    84  		}
    85  
    86  		noWindowFound := false
    87  		switch ev := ev.(type) {
    88  		case xproto.DestroyNotifyEvent:
    89  			s.mu.Lock()
    90  			delete(s.windows, ev.Window)
    91  			s.mu.Unlock()
    92  
    93  		case shm.CompletionEvent:
    94  			s.mu.Lock()
    95  			s.completionKeys = append(s.completionKeys, ev.Sequence)
    96  			s.handleCompletions()
    97  			s.mu.Unlock()
    98  
    99  		case xproto.ClientMessageEvent:
   100  			if ev.Type != s.atomWMProtocols || ev.Format != 32 {
   101  				break
   102  			}
   103  			switch xproto.Atom(ev.Data.Data32[0]) {
   104  			case s.atomWMDeleteWindow:
   105  				// TODO.
   106  			case s.atomWMTakeFocus:
   107  				xproto.SetInputFocus(s.xc, xproto.InputFocusParent, ev.Window, xproto.Timestamp(ev.Data.Data32[1]))
   108  			}
   109  
   110  		case xproto.ConfigureNotifyEvent:
   111  			if w := s.findWindow(ev.Window); w != nil {
   112  				w.handleConfigureNotify(ev)
   113  			} else {
   114  				noWindowFound = true
   115  			}
   116  
   117  		case xproto.ExposeEvent:
   118  			if w := s.findWindow(ev.Window); w != nil {
   119  				// A non-zero Count means that there are more expose events
   120  				// coming. For example, a non-rectangular exposure (e.g. from a
   121  				// partially overlapped window) will result in multiple expose
   122  				// events whose dirty rectangles combine to define the dirty
   123  				// region. Go's paint events do not provide dirty regions, so
   124  				// we only pass on the final X11 expose event.
   125  				if ev.Count == 0 {
   126  					w.handleExpose()
   127  				}
   128  			} else {
   129  				noWindowFound = true
   130  			}
   131  
   132  		case xproto.FocusInEvent:
   133  			// TODO: xw = ev.Event
   134  		case xproto.FocusOutEvent:
   135  			// TODO: xw = ev.Event
   136  
   137  		case xproto.KeyPressEvent:
   138  			if w := s.findWindow(ev.Event); w != nil {
   139  				w.handleKey(ev.Detail, ev.State, key.DirPress)
   140  			} else {
   141  				noWindowFound = true
   142  			}
   143  
   144  		case xproto.KeyReleaseEvent:
   145  			if w := s.findWindow(ev.Event); w != nil {
   146  				w.handleKey(ev.Detail, ev.State, key.DirRelease)
   147  			} else {
   148  				noWindowFound = true
   149  			}
   150  
   151  		case xproto.ButtonPressEvent:
   152  			if w := s.findWindow(ev.Event); w != nil {
   153  				w.handleMouse(ev.EventX, ev.EventY, ev.Detail, ev.State, mouse.DirPress)
   154  			} else {
   155  				noWindowFound = true
   156  			}
   157  
   158  		case xproto.ButtonReleaseEvent:
   159  			if w := s.findWindow(ev.Event); w != nil {
   160  				w.handleMouse(ev.EventX, ev.EventY, ev.Detail, ev.State, mouse.DirRelease)
   161  			} else {
   162  				noWindowFound = true
   163  			}
   164  
   165  		case xproto.MotionNotifyEvent:
   166  			if w := s.findWindow(ev.Event); w != nil {
   167  				w.handleMouse(ev.EventX, ev.EventY, 0, ev.State, mouse.DirNone)
   168  			} else {
   169  				noWindowFound = true
   170  			}
   171  		}
   172  
   173  		if noWindowFound {
   174  			log.Printf("x11driver: no window found for event %T", ev)
   175  		}
   176  	}
   177  }
   178  
   179  // TODO: is findBuffer and the s.buffers field unused? Delete?
   180  
   181  func (s *screenImpl) findBuffer(key shm.Seg) *bufferImpl {
   182  	s.mu.Lock()
   183  	b := s.buffers[key]
   184  	s.mu.Unlock()
   185  	return b
   186  }
   187  
   188  func (s *screenImpl) findWindow(key xproto.Window) *windowImpl {
   189  	s.mu.Lock()
   190  	w := s.windows[key]
   191  	s.mu.Unlock()
   192  	return w
   193  }
   194  
   195  // handleCompletions must only be called while holding s.mu.
   196  func (s *screenImpl) handleCompletions() {
   197  	if s.nPendingUploads != 0 {
   198  		return
   199  	}
   200  	for _, ck := range s.completionKeys {
   201  		completion, ok := s.uploads[ck]
   202  		if !ok {
   203  			log.Printf("x11driver: no matching upload for a SHM completion event")
   204  			continue
   205  		}
   206  		delete(s.uploads, ck)
   207  		close(completion)
   208  	}
   209  	s.completionKeys = s.completionKeys[:0]
   210  }
   211  
   212  const (
   213  	maxShmSide = 0x00007fff // 32,767 pixels.
   214  	maxShmSize = 0x10000000 // 268,435,456 bytes.
   215  )
   216  
   217  func (s *screenImpl) NewBuffer(size image.Point) (retBuf screen.Buffer, retErr error) {
   218  	// TODO: detect if the X11 server or connection cannot support SHM pixmaps,
   219  	// and fall back to regular pixmaps.
   220  
   221  	w, h := int64(size.X), int64(size.Y)
   222  	if w < 0 || maxShmSide < w || h < 0 || maxShmSide < h || maxShmSize < 4*w*h {
   223  		return nil, fmt.Errorf("x11driver: invalid buffer size %v", size)
   224  	}
   225  	xs, err := shm.NewSegId(s.xc)
   226  	if err != nil {
   227  		return nil, fmt.Errorf("x11driver: shm.NewSegId: %v", err)
   228  	}
   229  
   230  	bufLen := 4 * size.X * size.Y
   231  	shmid, addr, err := shmOpen(bufLen)
   232  	if err != nil {
   233  		return nil, fmt.Errorf("x11driver: shmOpen: %v", err)
   234  	}
   235  	defer func() {
   236  		if retErr != nil {
   237  			shmClose(addr)
   238  		}
   239  	}()
   240  	a := (*[maxShmSize]byte)(addr)
   241  	buf := (*a)[:bufLen:bufLen]
   242  
   243  	// readOnly is whether the shared memory is read-only from the X11 server's
   244  	// point of view. We need false to use SHM pixmaps.
   245  	const readOnly = false
   246  	shm.Attach(s.xc, xs, uint32(shmid), readOnly)
   247  
   248  	b := &bufferImpl{
   249  		s:    s,
   250  		addr: addr,
   251  		buf:  buf,
   252  		rgba: image.RGBA{
   253  			Pix:    buf,
   254  			Stride: 4 * size.X,
   255  			Rect:   image.Rectangle{Max: size},
   256  		},
   257  		size:     size,
   258  		xs:       xs,
   259  		reusable: true,
   260  	}
   261  
   262  	s.mu.Lock()
   263  	s.buffers[b.xs] = b
   264  	s.mu.Unlock()
   265  
   266  	return b, nil
   267  }
   268  
   269  func (s *screenImpl) NewTexture(size image.Point) (screen.Texture, error) {
   270  	w, h := int64(size.X), int64(size.Y)
   271  	if w < 0 || maxShmSide < w || h < 0 || maxShmSide < h || maxShmSize < 4*w*h {
   272  		return nil, fmt.Errorf("x11driver: invalid texture size %v", size)
   273  	}
   274  
   275  	xm, err := xproto.NewPixmapId(s.xc)
   276  	if err != nil {
   277  		return nil, fmt.Errorf("x11driver: xproto.NewPixmapId failed: %v", err)
   278  	}
   279  	xp, err := render.NewPictureId(s.xc)
   280  	if err != nil {
   281  		return nil, fmt.Errorf("x11driver: xproto.NewPictureId failed: %v", err)
   282  	}
   283  
   284  	t := &textureImpl{
   285  		s:    s,
   286  		size: size,
   287  		xm:   xm,
   288  		xp:   xp,
   289  	}
   290  
   291  	xproto.CreatePixmap(s.xc, textureDepth, xm, xproto.Drawable(s.window32), uint16(w), uint16(h))
   292  	render.CreatePicture(s.xc, xp, xproto.Drawable(xm), s.pictformat32, 0, nil)
   293  	render.SetPictureFilter(s.xc, xp, uint16(len("bilinear")), "bilinear", nil)
   294  	return t, nil
   295  }
   296  
   297  func (s *screenImpl) NewWindow(opts *screen.NewWindowOptions) (screen.Window, error) {
   298  	width, height := 1024, 768
   299  	if opts != nil {
   300  		if opts.Width > 0 {
   301  			width = opts.Width
   302  		}
   303  		if opts.Height > 0 {
   304  			height = opts.Height
   305  		}
   306  	}
   307  
   308  	xw, err := xproto.NewWindowId(s.xc)
   309  	if err != nil {
   310  		return nil, fmt.Errorf("x11driver: xproto.NewWindowId failed: %v", err)
   311  	}
   312  	xg, err := xproto.NewGcontextId(s.xc)
   313  	if err != nil {
   314  		return nil, fmt.Errorf("x11driver: xproto.NewGcontextId failed: %v", err)
   315  	}
   316  	xp, err := render.NewPictureId(s.xc)
   317  	if err != nil {
   318  		return nil, fmt.Errorf("x11driver: render.NewPictureId failed: %v", err)
   319  	}
   320  	pictformat := render.Pictformat(0)
   321  	switch s.xsi.RootDepth {
   322  	default:
   323  		return nil, fmt.Errorf("x11driver: unsupported root depth %d", s.xsi.RootDepth)
   324  	case 24:
   325  		pictformat = s.pictformat24
   326  	case 32:
   327  		pictformat = s.pictformat32
   328  	}
   329  
   330  	w := &windowImpl{
   331  		s:       s,
   332  		xw:      xw,
   333  		xg:      xg,
   334  		xp:      xp,
   335  		pump:    pump.Make(),
   336  		xevents: make(chan xgb.Event),
   337  	}
   338  
   339  	s.mu.Lock()
   340  	s.windows[xw] = w
   341  	s.mu.Unlock()
   342  
   343  	xproto.CreateWindow(s.xc, s.xsi.RootDepth, xw, s.xsi.Root,
   344  		0, 0, uint16(width), uint16(height), 0,
   345  		xproto.WindowClassInputOutput, s.xsi.RootVisual,
   346  		xproto.CwEventMask,
   347  		[]uint32{0 |
   348  			xproto.EventMaskKeyPress |
   349  			xproto.EventMaskKeyRelease |
   350  			xproto.EventMaskButtonPress |
   351  			xproto.EventMaskButtonRelease |
   352  			xproto.EventMaskPointerMotion |
   353  			xproto.EventMaskExposure |
   354  			xproto.EventMaskStructureNotify |
   355  			xproto.EventMaskFocusChange,
   356  		},
   357  	)
   358  	s.setProperty(xw, s.atomWMProtocols, s.atomWMDeleteWindow, s.atomWMTakeFocus)
   359  	xproto.CreateGC(s.xc, xg, xproto.Drawable(xw), 0, nil)
   360  	render.CreatePicture(s.xc, xp, xproto.Drawable(xw), pictformat, 0, nil)
   361  	xproto.MapWindow(s.xc, xw)
   362  
   363  	return w, nil
   364  }
   365  
   366  func (s *screenImpl) initAtoms() (err error) {
   367  	s.atomWMDeleteWindow, err = s.internAtom("WM_DELETE_WINDOW")
   368  	if err != nil {
   369  		return err
   370  	}
   371  	s.atomWMProtocols, err = s.internAtom("WM_PROTOCOLS")
   372  	if err != nil {
   373  		return err
   374  	}
   375  	s.atomWMTakeFocus, err = s.internAtom("WM_TAKE_FOCUS")
   376  	if err != nil {
   377  		return err
   378  	}
   379  	return nil
   380  }
   381  
   382  func (s *screenImpl) internAtom(name string) (xproto.Atom, error) {
   383  	r, err := xproto.InternAtom(s.xc, false, uint16(len(name)), name).Reply()
   384  	if err != nil {
   385  		return 0, fmt.Errorf("x11driver: xproto.InternAtom failed: %v", err)
   386  	}
   387  	if r == nil {
   388  		return 0, fmt.Errorf("x11driver: xproto.InternAtom failed")
   389  	}
   390  	return r.Atom, nil
   391  }
   392  
   393  func (s *screenImpl) initKeyboardMapping() error {
   394  	const keyLo, keyHi = 8, 255
   395  	km, err := xproto.GetKeyboardMapping(s.xc, keyLo, keyHi-keyLo+1).Reply()
   396  	if err != nil {
   397  		return err
   398  	}
   399  	n := int(km.KeysymsPerKeycode)
   400  	if n < 2 {
   401  		return fmt.Errorf("x11driver: too few keysyms per keycode: %d", n)
   402  	}
   403  	for i := keyLo; i <= keyHi; i++ {
   404  		s.keysyms[i][0] = km.Keysyms[(i-keyLo)*n+0]
   405  		s.keysyms[i][1] = km.Keysyms[(i-keyLo)*n+1]
   406  	}
   407  	return nil
   408  }
   409  
   410  func (s *screenImpl) initPictformats() error {
   411  	pformats, err := render.QueryPictFormats(s.xc).Reply()
   412  	if err != nil {
   413  		return fmt.Errorf("x11driver: render.QueryPictFormats failed: %v", err)
   414  	}
   415  	s.pictformat24, err = findPictformat(pformats.Formats, 24)
   416  	if err != nil {
   417  		return err
   418  	}
   419  	s.pictformat32, err = findPictformat(pformats.Formats, 32)
   420  	if err != nil {
   421  		return err
   422  	}
   423  	return nil
   424  }
   425  
   426  func findPictformat(fs []render.Pictforminfo, depth byte) (render.Pictformat, error) {
   427  	// This presumes little-endian BGRA.
   428  	want := render.Directformat{
   429  		RedShift:   16,
   430  		RedMask:    0xff,
   431  		GreenShift: 8,
   432  		GreenMask:  0xff,
   433  		BlueShift:  0,
   434  		BlueMask:   0xff,
   435  		AlphaShift: 24,
   436  		AlphaMask:  0xff,
   437  	}
   438  	if depth == 24 {
   439  		want.AlphaShift = 0
   440  		want.AlphaMask = 0x00
   441  	}
   442  	for _, f := range fs {
   443  		if f.Type == render.PictTypeDirect && f.Depth == depth && f.Direct == want {
   444  			return f.Id, nil
   445  		}
   446  	}
   447  	return 0, fmt.Errorf("x11driver: no matching Pictformat for depth %d", depth)
   448  }
   449  
   450  func (s *screenImpl) initWindow32() error {
   451  	visualid, err := findVisual(s.xsi, 32)
   452  	if err != nil {
   453  		return err
   454  	}
   455  	colormap, err := xproto.NewColormapId(s.xc)
   456  	if err != nil {
   457  		return fmt.Errorf("x11driver: xproto.NewColormapId failed: %v", err)
   458  	}
   459  	if err := xproto.CreateColormapChecked(
   460  		s.xc, xproto.ColormapAllocNone, colormap, s.xsi.Root, visualid).Check(); err != nil {
   461  		return fmt.Errorf("x11driver: xproto.CreateColormap failed: %v", err)
   462  	}
   463  	s.window32, err = xproto.NewWindowId(s.xc)
   464  	if err != nil {
   465  		return fmt.Errorf("x11driver: xproto.NewWindowId failed: %v", err)
   466  	}
   467  	s.gcontext32, err = xproto.NewGcontextId(s.xc)
   468  	if err != nil {
   469  		return fmt.Errorf("x11driver: xproto.NewGcontextId failed: %v", err)
   470  	}
   471  	const depth = 32
   472  	xproto.CreateWindow(s.xc, depth, s.window32, s.xsi.Root,
   473  		0, 0, 1, 1, 0,
   474  		xproto.WindowClassInputOutput, visualid,
   475  		// The CwBorderPixel attribute seems necessary for depth == 32. See
   476  		// http://stackoverflow.com/questions/3645632/how-to-create-a-window-with-a-bit-depth-of-32
   477  		xproto.CwBorderPixel|xproto.CwColormap,
   478  		[]uint32{0, uint32(colormap)},
   479  	)
   480  	xproto.CreateGC(s.xc, s.gcontext32, xproto.Drawable(s.window32), 0, nil)
   481  	return nil
   482  }
   483  
   484  func findVisual(xsi *xproto.ScreenInfo, depth byte) (xproto.Visualid, error) {
   485  	for _, d := range xsi.AllowedDepths {
   486  		if d.Depth != depth {
   487  			continue
   488  		}
   489  		for _, v := range d.Visuals {
   490  			if v.RedMask == 0xff0000 && v.GreenMask == 0xff00 && v.BlueMask == 0xff {
   491  				return v.VisualId, nil
   492  			}
   493  		}
   494  	}
   495  	return 0, fmt.Errorf("x11driver: no matching Visualid")
   496  }
   497  
   498  func (s *screenImpl) setProperty(xw xproto.Window, prop xproto.Atom, values ...xproto.Atom) {
   499  	b := make([]byte, len(values)*4)
   500  	for i, v := range values {
   501  		b[4*i+0] = uint8(v >> 0)
   502  		b[4*i+1] = uint8(v >> 8)
   503  		b[4*i+2] = uint8(v >> 16)
   504  		b[4*i+3] = uint8(v >> 24)
   505  	}
   506  	xproto.ChangeProperty(s.xc, xproto.PropModeReplace, xw, prop, xproto.AtomAtom, 32, uint32(len(values)), b)
   507  }