9fans.net/go@v0.0.7/cmd/devdraw/devdraw.go (about)

     1  /*
     2   * /dev/draw simulator -- handles the messages prepared by the draw library.
     3   * Doesn't simulate the file system part, just the messages.
     4   */
     5  
     6  package main
     7  
     8  import (
     9  	"bytes"
    10  	"encoding/binary"
    11  	"fmt"
    12  	"sync"
    13  	"unsafe"
    14  
    15  	"9fans.net/go/draw"
    16  	"9fans.net/go/draw/memdraw"
    17  )
    18  
    19  var drawdebug int
    20  
    21  var drawlk sync.Mutex
    22  
    23  func draw_initdisplaymemimage(c *Client, m *memdraw.Image) {
    24  	c.screenimage = m
    25  	m.ScreenRef = 1
    26  	c.slot = 0
    27  	c.clientid = 1
    28  	c.op = draw.SoverD
    29  }
    30  
    31  // gfx_replacescreenimage replaces c's screen image with m.
    32  // It is called by the host driver on the main host thread.
    33  func gfx_replacescreenimage(c *Client, m *memdraw.Image) {
    34  	drawlk.Lock()
    35  	om := c.screenimage
    36  	c.screenimage = m
    37  	m.ScreenRef = 1
    38  	if om != nil {
    39  		om.ScreenRef--
    40  		if om.ScreenRef == 0 {
    41  			memdraw.Free(om)
    42  		}
    43  	}
    44  	drawlk.Unlock()
    45  	gfx_mouseresized(c)
    46  }
    47  
    48  func drawrefreshscreen(l *DImage, client *Client) {
    49  	for l != nil && l.dscreen == nil {
    50  		l = l.fromname
    51  	}
    52  	if l != nil && l.dscreen.owner != client {
    53  		l.dscreen.owner.refreshme = 1
    54  	}
    55  }
    56  
    57  func drawrefresh(m *memdraw.Image, r draw.Rectangle, v interface{}) {
    58  	if v == nil {
    59  		return
    60  	}
    61  	x := v.(*Refx)
    62  	c := x.client
    63  	d := x.dimage
    64  	var ref *Refresh
    65  	for ref = c.refresh; ref != nil; ref = ref.next {
    66  		if ref.dimage == d {
    67  			draw.CombineRect(&ref.r, r)
    68  			return
    69  		}
    70  	}
    71  	ref = new(Refresh)
    72  	if ref != nil {
    73  		ref.dimage = d
    74  		ref.r = r
    75  		ref.next = c.refresh
    76  		c.refresh = ref
    77  	}
    78  }
    79  
    80  func addflush(c *Client, r draw.Rectangle) {
    81  	if !draw.RectClip(&r, c.screenimage.R) {
    82  		return
    83  	}
    84  
    85  	if c.flushrect.Min.X >= c.flushrect.Max.X {
    86  		c.flushrect = r
    87  		c.waste = 0
    88  		return
    89  	}
    90  	nbb := c.flushrect
    91  	draw.CombineRect(&nbb, r)
    92  	ar := r.Dx() * r.Dy()
    93  	abb := c.flushrect.Dx() * c.flushrect.Dy()
    94  	anbb := nbb.Dx() * nbb.Dy()
    95  	/*
    96  	 * Area of new waste is area of new bb minus area of old bb,
    97  	 * less the area of the new segment, which we assume is not waste.
    98  	 * This could be negative, but that's OK.
    99  	 */
   100  	c.waste += anbb - abb - ar
   101  	if c.waste < 0 {
   102  		c.waste = 0
   103  	}
   104  	/*
   105  	 * absorb if:
   106  	 *	total area is small
   107  	 *	waste is less than half total area
   108  	 * 	rectangles touch
   109  	 */
   110  	if anbb <= 1024 || c.waste*2 < anbb || draw.RectXRect(c.flushrect, r) {
   111  		c.flushrect = nbb
   112  		return
   113  	}
   114  	/* emit current state */
   115  	fr := c.flushrect
   116  	c.flushrect = r
   117  	c.waste = 0
   118  	if fr.Min.X < fr.Max.X {
   119  		// Unlock drawlk because rpc_flush may want to run on gfx thread,
   120  		// and gfx thread might be blocked on drawlk trying to install a new screen
   121  		// during a resize.
   122  		rpc_gfxdrawunlock()
   123  		drawlk.Unlock()
   124  		c.impl.rpc_flush(c, fr)
   125  		drawlk.Lock()
   126  		rpc_gfxdrawlock()
   127  	}
   128  }
   129  
   130  func dstflush(c *Client, dstid int, dst *memdraw.Image, r draw.Rectangle) {
   131  	if dstid == 0 {
   132  		draw.CombineRect(&c.flushrect, r)
   133  		return
   134  	}
   135  	/* how can this happen? -rsc, dec 12 2002 */
   136  	if dst == nil {
   137  		// fmt.Fprintf(os.Stderr, "nil dstflush\n")
   138  		return
   139  	}
   140  	l := dst.Layer
   141  	if l == nil {
   142  		return
   143  	}
   144  	for {
   145  		if l.Screen.Image.Data != c.screenimage.Data {
   146  			return
   147  		}
   148  		r = r.Add(l.Delta)
   149  		l = l.Screen.Image.Layer
   150  		if l == nil {
   151  			break
   152  		}
   153  	}
   154  	addflush(c, r)
   155  }
   156  
   157  func drawflush(c *Client) {
   158  	r := c.flushrect
   159  	c.flushrect = draw.Rect(10000, 10000, -10000, -10000)
   160  	if r.Min.X < r.Max.X {
   161  		// Unlock drawlk because rpc_flush may want to run on gfx thread,
   162  		// and gfx thread might be blocked on drawlk trying to install a new screen
   163  		// during a resize.
   164  		rpc_gfxdrawunlock()
   165  		drawlk.Unlock()
   166  		c.impl.rpc_flush(c, r)
   167  		drawlk.Lock()
   168  		rpc_gfxdrawlock()
   169  	}
   170  }
   171  
   172  func drawlookupname(client *Client, str string) *DName {
   173  	for i := 0; i < len(client.name); i++ {
   174  		name := &client.name[i]
   175  		if name.name == str {
   176  			return name
   177  		}
   178  	}
   179  	return nil
   180  }
   181  
   182  func drawgoodname(client *Client, d *DImage) int {
   183  	/* if window, validate the screen's own images */
   184  	if d.dscreen != nil {
   185  		if drawgoodname(client, d.dscreen.dimage) == 0 || drawgoodname(client, d.dscreen.dfill) == 0 {
   186  			return 0
   187  		}
   188  	}
   189  	if d.name == "" {
   190  		return 1
   191  	}
   192  	n := drawlookupname(client, d.name)
   193  	if n == nil || n.vers != d.vers {
   194  		return 0
   195  	}
   196  	return 1
   197  }
   198  
   199  func drawlookup(client *Client, id int, checkname int) *DImage {
   200  	d := client.dimage[id&HASHMASK]
   201  	for d != nil {
   202  		if d.id == id {
   203  			/*
   204  			 * BUG: should error out but too hard.
   205  			 * Return 0 instead.
   206  			 */
   207  			if checkname != 0 && drawgoodname(client, d) == 0 {
   208  				return nil
   209  			}
   210  			return d
   211  		}
   212  		d = d.next
   213  	}
   214  	return nil
   215  }
   216  
   217  func drawlookupdscreen(c *Client, id int) *DScreen {
   218  	s := c.dscreen
   219  	for s != nil {
   220  		if s.id == id {
   221  			return s
   222  		}
   223  		s = s.next
   224  	}
   225  	return nil
   226  }
   227  
   228  func drawlookupscreen(client *Client, id int, cs **CScreen) *DScreen {
   229  	s := client.cscreen
   230  	for s != nil {
   231  		if s.dscreen.id == id {
   232  			*cs = s
   233  			return s.dscreen
   234  		}
   235  		s = s.next
   236  	}
   237  	/* caller must check! */
   238  	return nil
   239  }
   240  
   241  func drawinstall(client *Client, id int, i *memdraw.Image, dscreen *DScreen) *memdraw.Image {
   242  	d := new(DImage)
   243  	if d == nil {
   244  		return nil
   245  	}
   246  	d.id = id
   247  	d.ref = 1
   248  	d.name = ""
   249  	d.vers = 0
   250  	d.image = i
   251  	if i.ScreenRef != 0 {
   252  		i.ScreenRef++
   253  	}
   254  	d.fchar = nil
   255  	d.fromname = nil
   256  	d.dscreen = dscreen
   257  	d.next = client.dimage[id&HASHMASK]
   258  	client.dimage[id&HASHMASK] = d
   259  	return i
   260  }
   261  
   262  func drawinstallscreen(client *Client, d *DScreen, id int, dimage *DImage, dfill *DImage, public int) *memdraw.Screen {
   263  	c := new(CScreen)
   264  	if dimage != nil && dimage.image != nil && dimage.image.Pix == 0 {
   265  		// fmt.Fprintf(os.Stderr, "bad image %p in drawinstallscreen", dimage.image)
   266  		panic("drawinstallscreen")
   267  	}
   268  
   269  	if c == nil {
   270  		return nil
   271  	}
   272  	if d == nil {
   273  		d = new(DScreen)
   274  		if d == nil {
   275  			return nil
   276  		}
   277  		s := new(memdraw.Screen)
   278  		if s == nil {
   279  			return nil
   280  		}
   281  		s.Frontmost = nil
   282  		s.Rearmost = nil
   283  		d.dimage = dimage
   284  		if dimage != nil {
   285  			s.Image = dimage.image
   286  			dimage.ref++
   287  		}
   288  		d.dfill = dfill
   289  		if dfill != nil {
   290  			s.Fill = dfill.image
   291  			dfill.ref++
   292  		}
   293  		d.ref = 0
   294  		d.id = id
   295  		d.screen = s
   296  		d.public = public
   297  		d.next = client.dscreen
   298  		d.owner = client
   299  		client.dscreen = d
   300  	}
   301  	c.dscreen = d
   302  	d.ref++
   303  	c.next = client.cscreen
   304  	client.cscreen = c
   305  	return d.screen
   306  }
   307  
   308  func drawdelname(client *Client, name *DName) {
   309  	i := 0
   310  	for &client.name[i] != name {
   311  		i++
   312  	}
   313  	copy(client.name[i:], client.name[i+1:])
   314  	client.name = client.name[:len(client.name)-1]
   315  }
   316  
   317  func drawfreedscreen(client *Client, this *DScreen) {
   318  	this.ref--
   319  	if this.ref < 0 {
   320  		// fmt.Fprintf(os.Stderr, "negative ref in drawfreedscreen\n")
   321  	}
   322  	if this.ref > 0 {
   323  		return
   324  	}
   325  	ds := client.dscreen
   326  	if ds == this {
   327  		client.dscreen = this.next
   328  		goto Found
   329  	}
   330  	for {
   331  		next := ds.next
   332  		if next == nil {
   333  			break
   334  		} /* assign = */
   335  		if next == this {
   336  			ds.next = this.next
   337  			goto Found
   338  		}
   339  		ds = next
   340  	}
   341  	/*
   342  	 * Should signal Enodrawimage, but too hard.
   343  	 */
   344  	return
   345  
   346  Found:
   347  	if this.dimage != nil {
   348  		drawfreedimage(client, this.dimage)
   349  	}
   350  	if this.dfill != nil {
   351  		drawfreedimage(client, this.dfill)
   352  	}
   353  }
   354  
   355  func drawfreedimage(client *Client, dimage *DImage) {
   356  	dimage.ref--
   357  	if dimage.ref < 0 {
   358  		// fmt.Fprintf(os.Stderr, "negative ref in drawfreedimage\n")
   359  	}
   360  	if dimage.ref > 0 {
   361  		return
   362  	}
   363  
   364  	/* any names? */
   365  	for i := 0; i < len(client.name); {
   366  		if client.name[i].dimage == dimage {
   367  			drawdelname(client, &client.name[i])
   368  		} else {
   369  			i++
   370  		}
   371  	}
   372  	if dimage.fromname != nil { /* acquired by name; owned by someone else*/
   373  		drawfreedimage(client, dimage.fromname)
   374  		return
   375  	}
   376  	ds := dimage.dscreen
   377  	l := dimage.image
   378  	dimage.dscreen = nil /* paranoia */
   379  	dimage.image = nil
   380  	if ds != nil {
   381  		if l.Data == client.screenimage.Data {
   382  			addflush(client, l.Layer.Screenr)
   383  		}
   384  		l.Layer.Refreshptr = nil
   385  		if drawgoodname(client, dimage) != 0 {
   386  			memdraw.LDelete(l)
   387  		} else {
   388  			memdraw.LFree(l)
   389  		}
   390  		drawfreedscreen(client, ds)
   391  	} else {
   392  		if l.ScreenRef == 0 {
   393  			memdraw.Free(l)
   394  		} else {
   395  			l.ScreenRef--
   396  			if l.ScreenRef == 0 {
   397  				memdraw.Free(l)
   398  			}
   399  		}
   400  	}
   401  }
   402  
   403  func drawuninstallscreen(client *Client, this *CScreen) {
   404  	cs := client.cscreen
   405  	if cs == this {
   406  		client.cscreen = this.next
   407  		drawfreedscreen(client, this.dscreen)
   408  		return
   409  	}
   410  	for {
   411  		next := cs.next
   412  		if next == nil {
   413  			break
   414  		} /* assign = */
   415  		if next == this {
   416  			cs.next = this.next
   417  			drawfreedscreen(client, this.dscreen)
   418  			return
   419  		}
   420  		cs = next
   421  	}
   422  }
   423  
   424  func drawuninstall(client *Client, id int) int {
   425  	var d *DImage
   426  	for l := &client.dimage[id&HASHMASK]; ; l = &d.next {
   427  		d = *l
   428  		if d == nil {
   429  			break
   430  		}
   431  		if d.id == id {
   432  			*l = d.next
   433  			drawfreedimage(client, d)
   434  			return 0
   435  		}
   436  	}
   437  	return -1
   438  }
   439  
   440  func drawaddname(client *Client, di *DImage, str string) error {
   441  	for i := range client.name {
   442  		name := &client.name[i]
   443  		if name.name == str {
   444  			return fmt.Errorf("image name in use")
   445  		}
   446  	}
   447  	client.name = append(client.name, DName{})
   448  	new := &client.name[len(client.name)-1]
   449  	new.name = str
   450  	new.dimage = di
   451  	new.client = client
   452  	client.namevers++
   453  	new.vers = client.namevers
   454  	return nil
   455  }
   456  
   457  func drawclientop(cl *Client) draw.Op {
   458  	op := cl.op
   459  	cl.op = draw.SoverD
   460  	return op
   461  }
   462  
   463  func drawimage(client *Client, a []uint8) *memdraw.Image {
   464  	d := drawlookup(client, rd4(a), 1)
   465  	if d == nil {
   466  		return nil /* caller must check! */
   467  	}
   468  	return d.image
   469  }
   470  
   471  func rd4(b []byte) int {
   472  	return int(int32(binary.LittleEndian.Uint32(b)))
   473  }
   474  
   475  func drawrectangle(r *draw.Rectangle, a []uint8) {
   476  	r.Min.X = rd4(a[0*4:])
   477  	r.Min.Y = rd4(a[1*4:])
   478  	r.Max.X = rd4(a[2*4:])
   479  	r.Max.Y = rd4(a[3*4:])
   480  }
   481  
   482  func drawpoint(p *draw.Point, a []uint8) {
   483  	p.X = rd4(a[0*4:])
   484  	p.Y = rd4(a[1*4:])
   485  }
   486  
   487  func drawchar(dst *memdraw.Image, p draw.Point, src *memdraw.Image, sp *draw.Point, font *DImage, index int, op draw.Op) draw.Point {
   488  	fc := &font.fchar[index]
   489  	var r draw.Rectangle
   490  	r.Min.X = p.X + int(fc.left)
   491  	r.Min.Y = p.Y - (font.ascent - int(fc.miny))
   492  	r.Max.X = r.Min.X + (int(fc.maxx) - int(fc.minx))
   493  	r.Max.Y = r.Min.Y + (int(fc.maxy) - int(fc.miny))
   494  	var sp1 draw.Point
   495  	sp1.X = sp.X + int(fc.left)
   496  	sp1.Y = sp.Y + int(fc.miny)
   497  	memdraw.Draw(dst, r, src, sp1, font.image, draw.Pt(fc.minx, int(fc.miny)), op)
   498  	p.X += int(fc.width)
   499  	sp.X += int(fc.width)
   500  	return p
   501  }
   502  
   503  func drawcoord(p []uint8, oldx int, newx *int) []uint8 {
   504  	if len(p) == 0 {
   505  		return nil
   506  	}
   507  	b := p[0]
   508  	p = p[1:]
   509  	x := int(b & 0x7F)
   510  	if b&0x80 != 0 {
   511  		if len(p) < 1 {
   512  			return nil
   513  		}
   514  		x |= int(p[0]) << 7
   515  		x |= int(p[1]) << 15
   516  		p = p[2:]
   517  		if x&(1<<22) != 0 {
   518  			x |= ^0 << 23
   519  		}
   520  	} else {
   521  		if b&0x40 != 0 {
   522  			x |= ^0 << 7
   523  		}
   524  		x += oldx
   525  	}
   526  	*newx = x
   527  	return p
   528  }
   529  
   530  func draw_dataread(cl *Client, a []byte) (int, error) {
   531  	drawlk.Lock()
   532  	defer drawlk.Unlock()
   533  
   534  	if cl.readdata == nil {
   535  		return 0, fmt.Errorf("no draw data")
   536  	}
   537  	if len(a) < len(cl.readdata) {
   538  		return 0, fmt.Errorf("short read")
   539  	}
   540  
   541  	// TODO(rsc) reuse cl.readdata
   542  	n := copy(a, cl.readdata)
   543  	cl.readdata = nil
   544  	return n, nil
   545  }
   546  
   547  func draw_datawrite(client *Client, v []byte) (int, error) {
   548  	drawlk.Lock()
   549  	rpc_gfxdrawlock()
   550  	a := v
   551  	m := 0
   552  	oldn := len(v)
   553  	var err error
   554  
   555  	for {
   556  		a = a[m:]
   557  		if len(a) == 0 {
   558  			break
   559  		}
   560  		// fmt.Fprintf(os.Stderr, "msgwrite %d(%c)...", len(a), a[0])
   561  		var refx *Refx
   562  		var reffn memdraw.Refreshfn
   563  		var r draw.Rectangle
   564  		var clipr draw.Rectangle
   565  		var sp draw.Point
   566  		var q draw.Point
   567  		var pp []draw.Point
   568  		var p draw.Point
   569  		var scrn *memdraw.Screen
   570  		var src *memdraw.Image
   571  		var mask *memdraw.Image
   572  		var lp []*memdraw.Image
   573  		var l *memdraw.Image
   574  		var i *memdraw.Image
   575  		var dst *memdraw.Image
   576  		var fc *FChar
   577  		var dscrn *DScreen
   578  		var dn *DName
   579  		var ll *DImage
   580  		var font *DImage
   581  		var dsrc *DImage
   582  		var ddst *DImage
   583  		var di *DImage
   584  		var cs *CScreen
   585  		var value draw.Color
   586  		var chan_ draw.Pix
   587  		var refresh uint8
   588  		var y int
   589  		var scrnid int
   590  		var repl int
   591  		var oy int
   592  		var ox int
   593  		var oesize int
   594  		var nw int
   595  		var ni int
   596  		var j int
   597  		var esize int
   598  		var dstid int
   599  		var doflush int
   600  		var ci int
   601  		var c int
   602  		switch a[0] {
   603  		/*fmt.Fprintf(os.Stderr, "bad command %d\n", a[0]); */
   604  		default:
   605  			err = fmt.Errorf("bad draw command")
   606  			goto error
   607  
   608  		/* allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1]
   609  		R[4*4] clipR[4*4] rrggbbaa[4]
   610  		*/
   611  		case 'b':
   612  			m = 1 + 4 + 4 + 1 + 4 + 1 + 4*4 + 4*4 + 4
   613  			if len(a) < m {
   614  				goto Eshortdraw
   615  			}
   616  			dstid = rd4(a[1:])
   617  			scrnid = int(binary.LittleEndian.Uint16(a[5:]))
   618  			refresh = a[9]
   619  			chan_ = draw.Pix(binary.LittleEndian.Uint32(a[10:]))
   620  			repl = int(a[14])
   621  			drawrectangle(&r, a[15:])
   622  			drawrectangle(&clipr, a[31:])
   623  			value = draw.Color(binary.LittleEndian.Uint32(a[47:]))
   624  			if drawlookup(client, dstid, 0) != nil {
   625  				goto Eimageexists
   626  			}
   627  			if scrnid != 0 {
   628  				dscrn = drawlookupscreen(client, scrnid, &cs)
   629  				if dscrn == nil {
   630  					goto Enodrawscreen
   631  				}
   632  				scrn = dscrn.screen
   633  				if repl != 0 || chan_ != scrn.Image.Pix {
   634  					err = fmt.Errorf("image parameters incompatibile with screen")
   635  					goto error
   636  				}
   637  				reffn = nil
   638  				switch refresh {
   639  				case draw.RefBackup:
   640  					break
   641  				case draw.RefNone:
   642  					reffn = memdraw.LNoRefresh
   643  				case draw.RefMesg:
   644  					reffn = drawrefresh
   645  				default:
   646  					err = fmt.Errorf("unknown refresh method")
   647  					goto error
   648  				}
   649  				l, err = memdraw.LAlloc(scrn, r, reffn, 0, value)
   650  				if err != nil {
   651  					goto Edrawmem
   652  				}
   653  				addflush(client, l.Layer.Screenr)
   654  				l.Clipr = clipr
   655  				draw.RectClip(&l.Clipr, r)
   656  				if drawinstall(client, dstid, l, dscrn) == nil {
   657  					memdraw.LDelete(l)
   658  					goto Edrawmem
   659  				}
   660  				dscrn.ref++
   661  				if reffn != nil {
   662  					refx = nil
   663  					if funcPC(reffn) == funcPC(drawrefresh) {
   664  						refx = new(Refx)
   665  						if refx == nil {
   666  							if drawuninstall(client, dstid) < 0 {
   667  								goto Enodrawimage
   668  							}
   669  							goto Edrawmem
   670  						}
   671  						refx.client = client
   672  						refx.dimage = drawlookup(client, dstid, 1)
   673  					}
   674  					memdraw.LSetRefresh(l, reffn, refx)
   675  				}
   676  				continue
   677  			}
   678  			i, err = memdraw.AllocImage(r, chan_)
   679  			if err != nil {
   680  				goto Edrawmem
   681  			}
   682  			if repl != 0 {
   683  				i.Flags |= memdraw.Frepl
   684  			}
   685  			i.Clipr = clipr
   686  			if repl == 0 {
   687  				draw.RectClip(&i.Clipr, r)
   688  			}
   689  			if drawinstall(client, dstid, i, nil) == nil {
   690  				memdraw.Free(i)
   691  				goto Edrawmem
   692  			}
   693  			// fmt.Fprintf(os.Stderr, "ALLOC %p %v %v %x\n", i, r, i.Clipr, value)
   694  			memdraw.FillColor(i, value)
   695  			continue
   696  
   697  		/* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */
   698  		case 'A':
   699  			m = 1 + 4 + 4 + 4 + 1
   700  			if len(a) < m {
   701  				goto Eshortdraw
   702  			}
   703  			dstid = rd4(a[1:])
   704  			if dstid == 0 {
   705  				goto Ebadarg
   706  			}
   707  			if drawlookupdscreen(client, dstid) != nil {
   708  				goto Escreenexists
   709  			}
   710  			ddst = drawlookup(client, rd4(a[5:]), 1)
   711  			dsrc = drawlookup(client, rd4(a[9:]), 1)
   712  			if ddst == nil || dsrc == nil {
   713  				goto Enodrawimage
   714  			}
   715  			if drawinstallscreen(client, nil, dstid, ddst, dsrc, int(a[13])) == nil {
   716  				goto Edrawmem
   717  			}
   718  			continue
   719  
   720  		/* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */
   721  		case 'c':
   722  			m = 1 + 4 + 1 + 4*4
   723  			if len(a) < m {
   724  				goto Eshortdraw
   725  			}
   726  			ddst = drawlookup(client, rd4(a[1:]), 1)
   727  			if ddst == nil {
   728  				goto Enodrawimage
   729  			}
   730  			if ddst.name != "" {
   731  				err = fmt.Errorf("can't change repl/clipr of shared image")
   732  				goto error
   733  			}
   734  			dst = ddst.image
   735  			if a[5] != 0 {
   736  				dst.Flags |= memdraw.Frepl
   737  			}
   738  			drawrectangle(&dst.Clipr, a[6:])
   739  			continue
   740  
   741  		/* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */
   742  		case 'd':
   743  			m = 1 + 4 + 4 + 4 + 4*4 + 2*4 + 2*4
   744  			if len(a) < m {
   745  				goto Eshortdraw
   746  			}
   747  			dst = drawimage(client, a[1:])
   748  			dstid = rd4(a[1:])
   749  			src = drawimage(client, a[5:])
   750  			mask = drawimage(client, a[9:])
   751  			if dst == nil || src == nil || mask == nil {
   752  				goto Enodrawimage
   753  			}
   754  			drawrectangle(&r, a[13:])
   755  			drawpoint(&p, a[29:])
   756  			drawpoint(&q, a[37:])
   757  			op := drawclientop(client)
   758  			// fmt.Fprintf(os.Stderr, "DRAW %p %v %p %v %p %v %v\n", dst, r, src, p, mask, q, op)
   759  			memdraw.Draw(dst, r, src, p, mask, q, op)
   760  			dstflush(client, dstid, dst, r)
   761  			continue
   762  
   763  		/* toggle debugging: 'D' val[1] */
   764  		case 'D':
   765  			m = 1 + 1
   766  			if len(a) < m {
   767  				goto Eshortdraw
   768  			}
   769  			drawdebug = int(a[1])
   770  			continue
   771  
   772  		/* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/
   773  		case 'e',
   774  			'E':
   775  			m = 1 + 4 + 4 + 2*4 + 4 + 4 + 4 + 2*4 + 2*4
   776  			if len(a) < m {
   777  				goto Eshortdraw
   778  			}
   779  			dst := drawimage(client, a[1:])
   780  			dstid := rd4(a[1:])
   781  			src := drawimage(client, a[5:])
   782  			if dst == nil || src == nil {
   783  				goto Enodrawimage
   784  			}
   785  			drawpoint(&p, a[9:])
   786  			e0 := rd4(a[17:])
   787  			e1 := rd4(a[21:])
   788  			if e0 < 0 || e1 < 0 {
   789  				err = fmt.Errorf("invalid ellipse semidiameter")
   790  				goto error
   791  			}
   792  			j := rd4(a[25:])
   793  			if j < 0 {
   794  				err = fmt.Errorf("negative ellipse thickness")
   795  				goto error
   796  			}
   797  
   798  			drawpoint(&sp, a[29:])
   799  			c = j
   800  			if a[0] == 'E' {
   801  				c = -1
   802  			}
   803  			ox := rd4(a[37:])
   804  			oy := rd4(a[41:])
   805  			op := drawclientop(client)
   806  			/* high bit indicates arc angles are present */
   807  			if ox&(1<<31) != 0 {
   808  				if ox&(1<<30) == 0 {
   809  					ox &= ^(1 << 31)
   810  				}
   811  				memdraw.Arc(dst, p, e0, e1, c, src, sp, ox, oy, op)
   812  			} else {
   813  				memdraw.Ellipse(dst, p, e0, e1, c, src, sp, op)
   814  			}
   815  			dstflush(client, dstid, dst, draw.Rect(p.X-e0-j, p.Y-e1-j, p.X+e0+j+1, p.Y+e1+j+1))
   816  			continue
   817  
   818  		/* free: 'f' id[4] */
   819  		case 'f':
   820  			m = 1 + 4
   821  			if len(a) < m {
   822  				goto Eshortdraw
   823  			}
   824  			ll = drawlookup(client, rd4(a[1:]), 0)
   825  			if ll != nil && ll.dscreen != nil && ll.dscreen.owner != client {
   826  				ll.dscreen.owner.refreshme = 1
   827  			}
   828  			if drawuninstall(client, rd4(a[1:])) < 0 {
   829  				goto Enodrawimage
   830  			}
   831  			continue
   832  
   833  		/* free screen: 'F' id[4] */
   834  		case 'F':
   835  			m = 1 + 4
   836  			if len(a) < m {
   837  				goto Eshortdraw
   838  			}
   839  			if drawlookupscreen(client, rd4(a[1:]), &cs) == nil {
   840  				goto Enodrawscreen
   841  			}
   842  			drawuninstallscreen(client, cs)
   843  			continue
   844  
   845  		/* initialize font: 'i' fontid[4] nchars[4] ascent[1] */
   846  		case 'i':
   847  			m = 1 + 4 + 4 + 1
   848  			if len(a) < m {
   849  				goto Eshortdraw
   850  			}
   851  			dstid = rd4(a[1:])
   852  			if dstid == 0 {
   853  				err = fmt.Errorf("can't use display as font")
   854  				goto error
   855  			}
   856  			font = drawlookup(client, dstid, 1)
   857  			if font == nil {
   858  				goto Enodrawimage
   859  			}
   860  			if font.image.Layer != nil {
   861  				err = fmt.Errorf("can't use window as font")
   862  				goto error
   863  			}
   864  			ni = rd4(a[5:])
   865  			if ni <= 0 || ni > 4096 {
   866  				err = fmt.Errorf("bad font size (4096 chars max)")
   867  				goto error
   868  			}
   869  			font.fchar = make([]FChar, ni)
   870  			font.ascent = int(a[9])
   871  			continue
   872  
   873  		/* set image 0 to screen image */
   874  		case 'J':
   875  			m = 1
   876  			if len(a) < m {
   877  				goto Eshortdraw
   878  			}
   879  			if drawlookup(client, 0, 0) != nil {
   880  				goto Eimageexists
   881  			}
   882  			drawinstall(client, 0, client.screenimage, nil)
   883  			client.infoid = 0
   884  			continue
   885  
   886  		/* get image info: 'I' */
   887  		case 'I':
   888  			m = 1
   889  			if len(a) < m {
   890  				goto Eshortdraw
   891  			}
   892  			if client.infoid < 0 {
   893  				goto Enodrawimage
   894  			}
   895  			if client.infoid == 0 {
   896  				i = client.screenimage
   897  				if i == nil {
   898  					goto Enodrawimage
   899  				}
   900  			} else {
   901  				di = drawlookup(client, client.infoid, 1)
   902  				if di == nil {
   903  					goto Enodrawimage
   904  				}
   905  				i = di.image
   906  			}
   907  			repl := 0
   908  			if i.Flags&memdraw.Frepl != 0 {
   909  				repl = 1
   910  			}
   911  			client.readdata = []byte(fmt.Sprintf("%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ",
   912  				client.clientid, client.infoid, i.Pix.String(), repl,
   913  				i.R.Min.X, i.R.Min.Y, i.R.Max.X, i.R.Max.Y,
   914  				i.Clipr.Min.X, i.Clipr.Min.Y, i.Clipr.Max.X, i.Clipr.Max.Y))
   915  			client.infoid = -1
   916  			continue
   917  
   918  		/* query: 'Q' n[1] queryspec[n] */
   919  		case 'q':
   920  			if len(a) < 2 {
   921  				goto Eshortdraw
   922  			}
   923  			m = 1 + 1 + int(a[1])
   924  			if len(a) < m {
   925  				goto Eshortdraw
   926  			}
   927  			var buf bytes.Buffer
   928  			for c = 0; c < int(a[1]); c++ {
   929  				switch a[2+c] {
   930  				default:
   931  					err = fmt.Errorf("unknown query")
   932  					goto error
   933  				case 'd': /* dpi */
   934  					if client.forcedpi != 0 {
   935  						fmt.Fprintf(&buf, "%11d ", client.forcedpi)
   936  					} else {
   937  						fmt.Fprintf(&buf, "%11d ", client.displaydpi)
   938  					}
   939  				}
   940  			}
   941  			client.readdata = buf.Bytes()
   942  			continue
   943  
   944  		/* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */
   945  		case 'l':
   946  			m = 1 + 4 + 4 + 2 + 4*4 + 2*4 + 1 + 1
   947  			if len(a) < m {
   948  				goto Eshortdraw
   949  			}
   950  			font = drawlookup(client, rd4(a[1:]), 1)
   951  			if font == nil {
   952  				goto Enodrawimage
   953  			}
   954  			if len(font.fchar) == 0 {
   955  				goto Enotfont
   956  			}
   957  			src = drawimage(client, a[5:])
   958  			if src == nil {
   959  				goto Enodrawimage
   960  			}
   961  			ci = int(binary.LittleEndian.Uint16(a[9:]))
   962  			if ci >= len(font.fchar) {
   963  				goto Eindex
   964  			}
   965  			drawrectangle(&r, a[11:])
   966  			drawpoint(&p, a[27:])
   967  			memdraw.Draw(font.image, r, src, p, memdraw.Opaque, p, draw.S)
   968  			fc = &font.fchar[ci]
   969  			fc.minx = r.Min.X
   970  			fc.maxx = r.Max.X
   971  			fc.miny = uint8(r.Min.Y)
   972  			fc.maxy = uint8(r.Max.Y)
   973  			fc.left = int8(a[35])
   974  			fc.width = a[36]
   975  			continue
   976  
   977  		/* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */
   978  		case 'L':
   979  			m = 1 + 4 + 2*4 + 2*4 + 4 + 4 + 4 + 4 + 2*4
   980  			if len(a) < m {
   981  				goto Eshortdraw
   982  			}
   983  			dst = drawimage(client, a[1:])
   984  			dstid = rd4(a[1:])
   985  			drawpoint(&p, a[5:])
   986  			drawpoint(&q, a[13:])
   987  			e0 := draw.End(rd4(a[21:]))
   988  			e1 := draw.End(rd4(a[25:]))
   989  			j = rd4(a[29:])
   990  			if j < 0 {
   991  				err = fmt.Errorf("negative line width")
   992  				goto error
   993  			}
   994  			src = drawimage(client, a[33:])
   995  			if dst == nil || src == nil {
   996  				goto Enodrawimage
   997  			}
   998  			drawpoint(&sp, a[37:])
   999  			op := drawclientop(client)
  1000  			memdraw.Line(dst, p, q, e0, e1, j, src, sp, op)
  1001  			/* avoid memlinebbox if possible */
  1002  			if dstid == 0 || dst.Layer != nil {
  1003  				/* BUG: this is terribly inefficient: update maximal containing rect*/
  1004  				r = memdraw.LineBBox(p, q, e0, e1, j)
  1005  				dstflush(client, dstid, dst, r.Inset(-(1 + 1 + j)))
  1006  			}
  1007  			continue
  1008  
  1009  		/* create image mask: 'm' newid[4] id[4] */
  1010  		/*
  1011  			 *
  1012  					case 'm':
  1013  						m = 4+4;
  1014  						if(len(a) < m)
  1015  							goto Eshortdraw;
  1016  						break;
  1017  			 *
  1018  		*/
  1019  
  1020  		/* attach to a named image: 'n' dstid[4] j[1] name[j] */
  1021  		case 'n':
  1022  			m = 1 + 4 + 1
  1023  			if len(a) < m {
  1024  				goto Eshortdraw
  1025  			}
  1026  			j = int(a[5])
  1027  			if j == 0 { /* give me a non-empty name please */
  1028  				goto Eshortdraw
  1029  			}
  1030  			m += j
  1031  			if len(a) < m {
  1032  				goto Eshortdraw
  1033  			}
  1034  			dstid = rd4(a[1:])
  1035  			if drawlookup(client, dstid, 0) != nil {
  1036  				goto Eimageexists
  1037  			}
  1038  			s := string(a[6 : 6+j])
  1039  			dn = drawlookupname(client, s)
  1040  			if dn == nil {
  1041  				goto Enoname
  1042  			}
  1043  			if drawinstall(client, dstid, dn.dimage.image, nil) == nil {
  1044  				goto Edrawmem
  1045  			}
  1046  			di = drawlookup(client, dstid, 0)
  1047  			if di == nil {
  1048  				goto Eoldname
  1049  			}
  1050  			di.vers = dn.vers
  1051  			di.name = s
  1052  			di.fromname = dn.dimage
  1053  			di.fromname.ref++
  1054  			client.infoid = dstid
  1055  			continue
  1056  
  1057  		/* name an image: 'N' dstid[4] in[1] j[1] name[j] */
  1058  		case 'N':
  1059  			m = 1 + 4 + 1 + 1
  1060  			if len(a) < m {
  1061  				goto Eshortdraw
  1062  			}
  1063  			c = int(a[5])
  1064  			j = int(a[6])
  1065  			if j == 0 { /* give me a non-empty name please */
  1066  				goto Eshortdraw
  1067  			}
  1068  			m += j
  1069  			if len(a) < m {
  1070  				goto Eshortdraw
  1071  			}
  1072  			di = drawlookup(client, rd4(a[1:]), 0)
  1073  			if di == nil {
  1074  				goto Enodrawimage
  1075  			}
  1076  			if di.name != "" {
  1077  				goto Enamed
  1078  			}
  1079  			if c != 0 {
  1080  				s := string(a[7 : 7+j])
  1081  				if err = drawaddname(client, di, s); err != nil {
  1082  					goto error
  1083  				}
  1084  				dn = drawlookupname(client, s)
  1085  				if dn == nil {
  1086  					goto Enoname
  1087  				}
  1088  				if dn.dimage != di {
  1089  					goto Ewrongname
  1090  				}
  1091  				drawdelname(client, dn)
  1092  			}
  1093  			continue
  1094  
  1095  		/* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */
  1096  		case 'o':
  1097  			m = 1 + 4 + 2*4 + 2*4
  1098  			if len(a) < m {
  1099  				goto Eshortdraw
  1100  			}
  1101  			dst = drawimage(client, a[1:])
  1102  			if dst == nil {
  1103  				goto Enodrawimage
  1104  			}
  1105  			if dst.Layer != nil {
  1106  				drawpoint(&p, a[5:])
  1107  				drawpoint(&q, a[13:])
  1108  				r = dst.Layer.Screenr
  1109  				ni, err = memdraw.LOrigin(dst, p, q)
  1110  				if err != nil {
  1111  					goto error
  1112  				}
  1113  				if ni > 0 {
  1114  					addflush(client, r)
  1115  					addflush(client, dst.Layer.Screenr)
  1116  					ll = drawlookup(client, rd4(a[1:]), 1)
  1117  					drawrefreshscreen(ll, client)
  1118  				}
  1119  			}
  1120  			continue
  1121  
  1122  		/* set compositing operator for next draw operation: 'O' op */
  1123  		case 'O':
  1124  			m = 1 + 1
  1125  			if len(a) < m {
  1126  				goto Eshortdraw
  1127  			}
  1128  			client.op = draw.Op(a[1])
  1129  			continue
  1130  
  1131  		/* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
  1132  		/* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
  1133  		case 'p',
  1134  			'P':
  1135  			m = 1 + 4 + 2 + 4 + 4 + 4 + 4 + 2*4
  1136  			if len(a) < m {
  1137  				goto Eshortdraw
  1138  			}
  1139  			dstid = rd4(a[1:])
  1140  			dst = drawimage(client, a[1:])
  1141  			ni = int(binary.LittleEndian.Uint16(a[5:]))
  1142  			if ni < 0 {
  1143  				err = fmt.Errorf("negative cout in polygon")
  1144  				goto error
  1145  			}
  1146  			e0 := draw.End(rd4(a[7:]))
  1147  			e1 := draw.End(rd4(a[11:]))
  1148  			j = 0
  1149  			if a[0] == 'p' {
  1150  				j = rd4(a[15:])
  1151  				if j < 0 {
  1152  					err = fmt.Errorf("negative polygon line width")
  1153  					goto error
  1154  				}
  1155  			}
  1156  			src = drawimage(client, a[19:])
  1157  			if dst == nil || src == nil {
  1158  				goto Enodrawimage
  1159  			}
  1160  			drawpoint(&sp, a[23:])
  1161  			drawpoint(&p, a[31:])
  1162  			ni++
  1163  			pp = make([]draw.Point, ni)
  1164  			doflush = 0
  1165  			if dstid == 0 || (dst.Layer != nil && dst.Layer.Screen.Image.Data == client.screenimage.Data) {
  1166  				doflush = 1 /* simplify test in loop */
  1167  			}
  1168  			oy = 0
  1169  			ox = oy
  1170  			esize = 0
  1171  			u := a[m:]
  1172  			for y = 0; y < ni; y++ {
  1173  				q = p
  1174  				oesize = esize
  1175  				u = drawcoord(u, ox, &p.X)
  1176  				if u == nil {
  1177  					goto Eshortdraw
  1178  				}
  1179  				u = drawcoord(u, oy, &p.Y)
  1180  				if u == nil {
  1181  					goto Eshortdraw
  1182  				}
  1183  				ox = p.X
  1184  				oy = p.Y
  1185  				if doflush != 0 {
  1186  					esize = j
  1187  					if a[0] == 'p' {
  1188  						if y == 0 {
  1189  							c = memdraw.LineEndSize(e0)
  1190  							if c > esize {
  1191  								esize = c
  1192  							}
  1193  						}
  1194  						if y == ni-1 {
  1195  							c = memdraw.LineEndSize(e1)
  1196  							if c > esize {
  1197  								esize = c
  1198  							}
  1199  						}
  1200  					}
  1201  					if a[0] == 'P' && e0 != 1 && e0 != ^0 {
  1202  						r = dst.Clipr
  1203  					} else if y > 0 {
  1204  						r = draw.Rect(q.X-oesize, q.Y-oesize, q.X+oesize+1, q.Y+oesize+1)
  1205  						draw.CombineRect(&r, draw.Rect(p.X-esize, p.Y-esize, p.X+esize+1, p.Y+esize+1))
  1206  					}
  1207  					if draw.RectClip(&r, dst.Clipr) { /* should perhaps be an arg to dstflush */
  1208  						dstflush(client, dstid, dst, r)
  1209  					}
  1210  				}
  1211  				pp[y] = p
  1212  			}
  1213  			if y == 1 {
  1214  				dstflush(client, dstid, dst, draw.Rect(p.X-esize, p.Y-esize, p.X+esize+1, p.Y+esize+1))
  1215  			}
  1216  			op := drawclientop(client)
  1217  			if a[0] == 'p' {
  1218  				memdraw.Poly(dst, pp, e0, e1, j, src, sp, op)
  1219  			} else {
  1220  				memdraw.FillPoly(dst, pp, int(e0), src, sp, op)
  1221  			}
  1222  			m = len(a) - len(u)
  1223  			continue
  1224  
  1225  		/* read: 'r' id[4] R[4*4] */
  1226  		case 'r':
  1227  			m = 1 + 4 + 4*4
  1228  			if len(a) < m {
  1229  				goto Eshortdraw
  1230  			}
  1231  			i = drawimage(client, a[1:])
  1232  			if i == nil {
  1233  				goto Enodrawimage
  1234  			}
  1235  			drawrectangle(&r, a[5:])
  1236  			if !draw.RectInRect(r, i.R) {
  1237  				goto Ereadoutside
  1238  			}
  1239  			c = draw.BytesPerLine(r, i.Depth)
  1240  			c *= r.Dy()
  1241  			client.readdata = make([]byte, c)
  1242  			n, e := memdraw.Unload(i, r, client.readdata)
  1243  			if e != nil {
  1244  				client.readdata = nil
  1245  				err = fmt.Errorf("bad readimage call")
  1246  				goto error
  1247  			}
  1248  			client.readdata = client.readdata[:n]
  1249  			continue
  1250  
  1251  		/* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */
  1252  		/* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] bgid[4] bgpt[2*4] ni*(index[2]) */
  1253  		case 's',
  1254  			'x':
  1255  			m = 1 + 4 + 4 + 4 + 2*4 + 4*4 + 2*4 + 2
  1256  			if a[0] == 'x' {
  1257  				m += 4 + 2*4
  1258  			}
  1259  			if len(a) < m {
  1260  				goto Eshortdraw
  1261  			}
  1262  
  1263  			dst = drawimage(client, a[1:])
  1264  			dstid = rd4(a[1:])
  1265  			src = drawimage(client, a[5:])
  1266  			if dst == nil || src == nil {
  1267  				goto Enodrawimage
  1268  			}
  1269  			font = drawlookup(client, rd4(a[9:]), 1)
  1270  			if font == nil {
  1271  				goto Enodrawimage
  1272  			}
  1273  			if len(font.fchar) == 0 {
  1274  				goto Enotfont
  1275  			}
  1276  			drawpoint(&p, a[13:])
  1277  			drawrectangle(&r, a[21:])
  1278  			drawpoint(&sp, a[37:])
  1279  			ni = int(binary.LittleEndian.Uint16(a[45:]))
  1280  			u := a[m:]
  1281  			m += ni * 2
  1282  			if len(a) < m {
  1283  				goto Eshortdraw
  1284  			}
  1285  			clipr = dst.Clipr
  1286  			dst.Clipr = r
  1287  			op := drawclientop(client)
  1288  			if a[0] == 'x' {
  1289  				/* paint background */
  1290  				l = drawimage(client, a[47:])
  1291  				if l == nil {
  1292  					goto Enodrawimage
  1293  				}
  1294  				drawpoint(&q, a[51:])
  1295  				r.Min.X = p.X
  1296  				r.Min.Y = p.Y - font.ascent
  1297  				r.Max.X = p.X
  1298  				r.Max.Y = r.Min.Y + font.image.R.Dy()
  1299  				u := u // local copy
  1300  				j = ni
  1301  				for {
  1302  					j--
  1303  					if j < 0 {
  1304  						break
  1305  					}
  1306  					ci = int(binary.LittleEndian.Uint16(u))
  1307  					if ci < 0 || ci >= len(font.fchar) {
  1308  						dst.Clipr = clipr
  1309  						goto Eindex
  1310  					}
  1311  					r.Max.X += int(font.fchar[ci].width)
  1312  					u = u[2:]
  1313  				}
  1314  				memdraw.Draw(dst, r, l, q, memdraw.Opaque, draw.ZP, op)
  1315  			}
  1316  			q = p
  1317  			for {
  1318  				ni--
  1319  				if ni < 0 {
  1320  					break
  1321  				}
  1322  				ci = int(binary.LittleEndian.Uint16(u))
  1323  				if ci < 0 || ci >= len(font.fchar) {
  1324  					dst.Clipr = clipr
  1325  					goto Eindex
  1326  				}
  1327  				q = drawchar(dst, q, src, &sp, font, ci, op)
  1328  				u = u[2:]
  1329  			}
  1330  			dst.Clipr = clipr
  1331  			p.Y -= font.ascent
  1332  			dstflush(client, dstid, dst, draw.Rect(p.X, p.Y, q.X, p.Y+font.image.R.Dy()))
  1333  			continue
  1334  
  1335  		/* use public screen: 'S' id[4] chan[4] */
  1336  		case 'S':
  1337  			m = 1 + 4 + 4
  1338  			if len(a) < m {
  1339  				goto Eshortdraw
  1340  			}
  1341  			dstid = rd4(a[1:])
  1342  			if dstid == 0 {
  1343  				goto Ebadarg
  1344  			}
  1345  			dscrn = drawlookupdscreen(client, dstid)
  1346  			if dscrn == nil || (dscrn.public == 0 && dscrn.owner != client) {
  1347  				goto Enodrawscreen
  1348  			}
  1349  			if dscrn.screen.Image.Pix != draw.Pix(binary.LittleEndian.Uint32(a[5:])) {
  1350  				err = fmt.Errorf("inconsistent chan")
  1351  				goto error
  1352  			}
  1353  			if drawinstallscreen(client, dscrn, 0, nil, nil, 0) == nil {
  1354  				goto Edrawmem
  1355  			}
  1356  			continue
  1357  
  1358  		/* top or bottom windows: 't' top[1] nw[2] n*id[4] */
  1359  		case 't':
  1360  			m = 1 + 1 + 2
  1361  			if len(a) < m {
  1362  				goto Eshortdraw
  1363  			}
  1364  			nw = int(binary.LittleEndian.Uint16(a[2:]))
  1365  			if nw < 0 {
  1366  				goto Ebadarg
  1367  			}
  1368  			if nw == 0 {
  1369  				continue
  1370  			}
  1371  			m += nw * 4
  1372  			if len(a) < m {
  1373  				goto Eshortdraw
  1374  			}
  1375  			lp = make([]*memdraw.Image, nw)
  1376  			for j = 0; j < nw; j++ {
  1377  				lp[j] = drawimage(client, a[1+1+2+j*4:])
  1378  				if lp[j] == nil {
  1379  					goto Enodrawimage
  1380  				}
  1381  			}
  1382  			if lp[0].Layer == nil {
  1383  				err = fmt.Errorf("images are not windows")
  1384  				goto error
  1385  			}
  1386  			for j = 1; j < nw; j++ {
  1387  				if lp[j].Layer.Screen != lp[0].Layer.Screen {
  1388  					err = fmt.Errorf("images not on same screen")
  1389  					goto error
  1390  				}
  1391  			}
  1392  			if a[1] != 0 {
  1393  				memdraw.LToFrontN(lp, nw)
  1394  			} else {
  1395  				memdraw.LToRearN(lp, nw)
  1396  			}
  1397  			if lp[0].Layer.Screen.Image.Data == client.screenimage.Data {
  1398  				for j = 0; j < nw; j++ {
  1399  					addflush(client, lp[j].Layer.Screenr)
  1400  				}
  1401  			}
  1402  			ll = drawlookup(client, rd4(a[1+1+2:]), 1)
  1403  			drawrefreshscreen(ll, client)
  1404  			continue
  1405  
  1406  		/* visible: 'v' */
  1407  		case 'v':
  1408  			m = 1
  1409  			drawflush(client)
  1410  			continue
  1411  
  1412  		/* write: 'y' id[4] R[4*4] data[x*1] */
  1413  		/* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */
  1414  		case 'y',
  1415  			'Y':
  1416  			m = 1 + 4 + 4*4
  1417  			if len(a) < m {
  1418  				goto Eshortdraw
  1419  			}
  1420  			dstid = rd4(a[1:])
  1421  			dst = drawimage(client, a[1:])
  1422  			if dst == nil {
  1423  				goto Enodrawimage
  1424  			}
  1425  			drawrectangle(&r, a[5:])
  1426  			if !draw.RectInRect(r, dst.R) {
  1427  				goto Ewriteoutside
  1428  			}
  1429  			y, err = memdraw.Load(dst, r, a[m:], a[0] == 'Y')
  1430  			if err != nil {
  1431  				err = fmt.Errorf("bad writeimage call")
  1432  				goto error
  1433  			}
  1434  			dstflush(client, dstid, dst, r)
  1435  			m += y
  1436  			continue
  1437  		}
  1438  	}
  1439  	rpc_gfxdrawunlock()
  1440  	drawlk.Unlock()
  1441  	return oldn - len(a), nil
  1442  
  1443  Enodrawimage:
  1444  	err = fmt.Errorf("unknown id for draw image")
  1445  	goto error
  1446  Enodrawscreen:
  1447  	err = fmt.Errorf("unknown id for draw screen")
  1448  	goto error
  1449  Eshortdraw:
  1450  	err = fmt.Errorf("short draw message")
  1451  	goto error
  1452  	/*
  1453  	   Eshortread:
  1454  	   	err = fmt.Errorf("draw read too short");
  1455  	   	goto error;
  1456  	*/
  1457  Eimageexists:
  1458  	err = fmt.Errorf("image id in use")
  1459  	goto error
  1460  Escreenexists:
  1461  	err = fmt.Errorf("screen id in use")
  1462  	goto error
  1463  Edrawmem:
  1464  	err = fmt.Errorf("image memory allocation failed")
  1465  	goto error
  1466  Ereadoutside:
  1467  	err = fmt.Errorf("readimage outside image")
  1468  	goto error
  1469  Ewriteoutside:
  1470  	err = fmt.Errorf("writeimage outside image")
  1471  	goto error
  1472  Enotfont:
  1473  	err = fmt.Errorf("image not a font")
  1474  	goto error
  1475  Eindex:
  1476  	err = fmt.Errorf("character index out of range")
  1477  	goto error
  1478  	/*
  1479  	   Enoclient:
  1480  	   	err = fmt.Errorf("no such draw client");
  1481  	   	goto error;
  1482  	   Edepth:
  1483  	   	err = fmt.Errorf("image has bad depth");
  1484  	   	goto error;
  1485  	   Enameused:
  1486  	   	err = fmt.Errorf("image name in use");
  1487  	   	goto error;
  1488  	*/
  1489  Enoname:
  1490  	err = fmt.Errorf("no image with that name")
  1491  	goto error
  1492  Eoldname:
  1493  	err = fmt.Errorf("named image no longer valid")
  1494  	goto error
  1495  Enamed:
  1496  	err = fmt.Errorf("image already has name")
  1497  	goto error
  1498  Ewrongname:
  1499  	err = fmt.Errorf("wrong name for image")
  1500  	goto error
  1501  Ebadarg:
  1502  	err = fmt.Errorf("bad argument in draw message")
  1503  	goto error
  1504  
  1505  error:
  1506  	rpc_gfxdrawunlock()
  1507  	drawlk.Unlock()
  1508  	return 0, err
  1509  }
  1510  
  1511  type eface struct {
  1512  	_type unsafe.Pointer
  1513  	data  unsafe.Pointer
  1514  }
  1515  
  1516  func funcPC(f interface{}) uintptr {
  1517  	return *(*uintptr)(efaceOf(&f).data)
  1518  }
  1519  func efaceOf(ep *interface{}) *eface {
  1520  	return (*eface)(unsafe.Pointer(ep))
  1521  }