9fans.net/go@v0.0.5/draw/init.go (about)

     1  package draw
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"log"
     7  	"os"
     8  	"runtime"
     9  	"strings"
    10  	"sync"
    11  
    12  	"9fans.net/go/draw/drawfcall"
    13  )
    14  
    15  // Display locking:
    16  // The Exported methods of Display, being entry points for clients, lock the Display structure.
    17  // The unexported ones do not.
    18  // The methods for Font, Image and Screen also lock the associated display by the same rules.
    19  
    20  // A Display represents a connection to a graphics display,
    21  // holding all graphics resources associated with the connection,
    22  // including in particular raster image data in use by the client program.
    23  //
    24  // A Display d is created by calling Init.
    25  // Each Display corresponds to a single host window system window.
    26  // Multiple host windows can be created by calling Init multiple times,
    27  // although each allocated Image and Font is only valid for use with
    28  // the Display from which it was allocated.
    29  //
    30  // Historically, Plan 9 graphics programs have used fixed-size
    31  // graphics features that assume a narrow range of display densities,
    32  // around 100 dpi: pixels (or dots) per inch.  The new field DPI
    33  // contains the display's actual density if known, or else DefaultDPI (133).
    34  // The Display.Scale method scales a fixed pixel count n by DPI/DefaultDPI,
    35  // rounding appropriately. Note that the display DPI can change during
    36  // during Display.Attach.
    37  //
    38  // The mouse cursor is always shown. The initial cursor is the system arrow.
    39  // The SwitchCursor method changes the cursor image, and
    40  // MoveCursor moves the cursor.
    41  //
    42  // The various graphics operations are buffered and not guaranteed to
    43  // be made visible until a call to the Flush method.
    44  // Various routines flush automatically, notably Mousectl.Read.
    45  // Programs that receive directly from Mousectl.C should typically
    46  // Flush the display explicitly before the receive.
    47  //
    48  type Display struct {
    49  	Image       *Image
    50  	Screen      *Screen
    51  	ScreenImage *Image
    52  	Windows     *Image
    53  	DPI         int // display pixels-per-inch
    54  
    55  	White       *Image // pre-allocated color
    56  	Black       *Image // pre-allocated color
    57  	Opaque      *Image // pre-allocated color
    58  	Transparent *Image // pre-allocated color
    59  
    60  	Font *Font // default font for UI
    61  
    62  	defaultSubfont *subfont // fallback subfont
    63  
    64  	mu       sync.Mutex // See comment above.
    65  	conn     *drawfcall.Conn
    66  	errch    chan<- error
    67  	bufsize  int
    68  	buf      []byte
    69  	imageid  uint32
    70  	qmask    *Image
    71  	locking  bool
    72  	flushErr int
    73  
    74  	firstfont *Font
    75  	lastfont  *Font
    76  }
    77  
    78  // An Image represents an image on the server, possibly visible on the display.
    79  // It is a rectangular picture along with the methods to draw upon it.
    80  // It is also the building block for higher-level objects such as windows and fonts.
    81  // In particular, a window is represented as an Image; no special operators
    82  // are needed to draw on a window.
    83  //
    84  // Most of the graphics methods come in two forms: a basic form, and an
    85  // extended form that takes an extra Op to specify a Porter-Duff compositing
    86  // operator to use. The basic forms assume the operator is SoverD, which
    87  // suffices for the vast majority of applications.
    88  // The extended forms are named by adding an Op suffix to the basic form's name.
    89  type Image struct {
    90  	// Display is the display the image belongs to.
    91  	// All graphics operations must use images from a single display.
    92  	Display *Display
    93  
    94  	// R is the coordinates of the rectangle in the plane for
    95  	// which the Image has defined pixel values.
    96  	// It is read-only and should not be modified.
    97  	R Rectangle
    98  
    99  	// Clipr is the clipping rectangle: operations that read or write
   100  	// the image will not access pixels outside clipr.
   101  	// Frequently, clipr is the same as r, but it may differ.
   102  	// See in particular the comment for Repl.
   103  	// Clipr should not be modified directly; use the ReplClipr method instead.
   104  	Clipr Rectangle
   105  
   106  	// Pix is the pixel channel format descriptor.
   107  	// See the package documentation for details about pixel formats.
   108  	// It is read-only and should not be modified.
   109  	Pix Pix
   110  
   111  	// Depth is the number of bits per pixel in the picture.
   112  	// It is identical to Pix.Depth() and is provided as a convenience.
   113  	// It is read-only and should not be modified.
   114  	Depth int
   115  
   116  	// Repl is a boolean value specifying whether the image is tiled
   117  	// to cover the plane when used as a source for a drawing operation.
   118  	// If Repl is false, operations are restricted to the intersection of R and Clipr.
   119  	// If Repl is true, R defines the tile to be replicated and Clipr defines the
   120  	// portion of the plane covered by the tiling; in other words, R is replicated
   121  	// to cover Clipr. In this case, R and Clipr are independent.
   122  	//
   123  	// For example, a replicated image with R set to (0,0)-(1,1)
   124  	// and Clipr set to (0,0)-(100,100), with the single pixel of R set to blue,
   125  	// behaves identically to an image with R and Clipr both set
   126  	// to (0,0)-(100,100) and all pixels set to blue.
   127  	// However, the first image requires far less memory and enables
   128  	// more efficient operations.
   129  	// Repl should not be modified directly; use the ReplClipr method instead.
   130  	Repl bool // Whether the image is replicated (tiles the rectangle).
   131  
   132  	Screen *Screen // If non-nil, the associated screen; this is a window.
   133  
   134  	id   uint32
   135  	next *Image
   136  }
   137  
   138  // A Screen is a collection of windows that are visible on an image.
   139  type Screen struct {
   140  	Display *Display // Display connected to the server.
   141  	id      uint32
   142  	Fill    *Image // Background image behind the windows.
   143  }
   144  
   145  // Refresh algorithms to execute when a window is resized or uncovered.
   146  // RefMesg is almost always the correct one to use.
   147  const (
   148  	RefBackup = 0
   149  	RefNone   = 1
   150  	RefMesg   = 2
   151  )
   152  
   153  const deffontname = "*default*"
   154  
   155  // Init connects to a display server and creates a single host window.
   156  // The error channel is unused.
   157  //
   158  // The font specifies the font name.
   159  // If font is the empty string, Init uses the environment variable $font.
   160  // If $font is not set, Init uses a built-in minimal default font.
   161  // See the package documentation for a full discussion of font syntaxes.
   162  //
   163  // The label and size specify the initial window title and diemnsions.
   164  // The size takes the form "1000x500"; the units are pixels.
   165  //
   166  // Unlike the Plan 9 C library's initdraw, Init does not establish any global variables.
   167  // The C global variables display, font, and screen correspond to the
   168  // returned value d, d.Font, and d.ScreenImage.
   169  //
   170  // TODO: Use the error channel.
   171  func Init(errch chan<- error, font, label, size string) (d *Display, err error) {
   172  	c, err := drawfcall.New()
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  	d = &Display{
   177  		conn:    c,
   178  		errch:   errch,
   179  		bufsize: 10000,
   180  	}
   181  
   182  	// Lock Display so we maintain the contract within this library.
   183  	d.mu.Lock()
   184  	defer d.mu.Unlock()
   185  
   186  	d.buf = make([]byte, 0, d.bufsize+5) // 5 for final flush
   187  	if err := c.Init(label, size); err != nil {
   188  		c.Close()
   189  		return nil, err
   190  	}
   191  
   192  	i, err := d.getimage0(nil)
   193  	if err != nil {
   194  		c.Close()
   195  		return nil, err
   196  	}
   197  
   198  	d.Image = i
   199  	d.White, err = d.allocImage(Rect(0, 0, 1, 1), GREY1, true, White)
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  	d.Black, err = d.allocImage(Rect(0, 0, 1, 1), GREY1, true, Black)
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  	d.Opaque = d.White
   208  	d.Transparent = d.Black
   209  
   210  	/*
   211  	 * Set up default font
   212  	 */
   213  	df, err := getdefont(d)
   214  	if err != nil {
   215  		return nil, err
   216  	}
   217  	d.defaultSubfont = df
   218  
   219  	if font == "" {
   220  		font = os.Getenv("font")
   221  	}
   222  
   223  	/*
   224  	 * Build fonts with caches==depth of screen, for speed.
   225  	 * If conversion were faster, we'd use 0 and save memory.
   226  	 */
   227  	if font == "" {
   228  		buf := []byte(fmt.Sprintf("%d %d\n0 %d\t%s\n", df.Height, df.Ascent,
   229  			df.N-1, deffontname))
   230  		//fmt.Printf("%q\n", buf)
   231  		//BUG: Need something better for this	installsubfont("*default*", df);
   232  		d.Font, err = d.buildFont(buf, deffontname)
   233  	} else {
   234  		d.Font, err = d.openFont(font) // BUG: grey fonts
   235  	}
   236  	if err != nil {
   237  		fmt.Fprintf(os.Stderr, "imageinit: can't open default font %s: %v\n", font, err)
   238  		return nil, err
   239  	}
   240  
   241  	d.Screen, err = i.allocScreen(d.White, false)
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  	d.ScreenImage = d.Image // temporary, for d.ScreenImage.Pix
   246  	d.ScreenImage, err = allocwindow(nil, d.Screen, i.R, 0, White)
   247  	if err != nil {
   248  		return nil, err
   249  	}
   250  	if err := d.flush(true); err != nil {
   251  		log.Fatal("allocwindow flush: ", err)
   252  	}
   253  
   254  	screen := d.ScreenImage
   255  	screen.draw(screen.R, d.White, nil, ZP)
   256  	if err := d.flush(true); err != nil {
   257  		log.Fatal("draw flush: ", err)
   258  	}
   259  
   260  	return d, nil
   261  }
   262  
   263  func (d *Display) getimage0(i *Image) (*Image, error) {
   264  	if i != nil {
   265  		i.free()
   266  		*i = Image{}
   267  	}
   268  
   269  	a := d.bufimage(2)
   270  	a[0] = 'J'
   271  	a[1] = 'I'
   272  	if err := d.flush(false); err != nil {
   273  		fmt.Fprintf(os.Stderr, "cannot read screen info: %v\n", err)
   274  		return nil, err
   275  	}
   276  
   277  	info := make([]byte, 12*12)
   278  	n, err := d.conn.ReadDraw(info)
   279  	if err != nil {
   280  		return nil, err
   281  	}
   282  	if n < len(info) {
   283  		return nil, fmt.Errorf("short info from rddraw")
   284  	}
   285  
   286  	pix, _ := ParsePix(strings.TrimSpace(string(info[2*12 : 3*12])))
   287  	if i == nil {
   288  		i = new(Image)
   289  	}
   290  	*i = Image{
   291  		Display: d,
   292  		id:      0,
   293  		Pix:     pix,
   294  		Depth:   pix.Depth(),
   295  		Repl:    atoi(info[3*12:]) > 0,
   296  		R:       ator(info[4*12:]),
   297  		Clipr:   ator(info[8*12:]),
   298  	}
   299  
   300  	a = d.bufimage(3)
   301  	a[0] = 'q'
   302  	a[1] = 1
   303  	a[2] = 'd'
   304  	d.DPI = 100
   305  	if err := d.flush(false); err == nil {
   306  		if n, _ := d.conn.ReadDraw(info[:12]); n == 12 {
   307  			d.DPI = atoi(info)
   308  		}
   309  	}
   310  
   311  	return i, nil
   312  }
   313  
   314  // Attach reattaches to a display, after a resize, updating the
   315  // display's associated image, screen, and screen image data structures.
   316  // The images d.Image and d.ScreenImage and the screen d.Screen
   317  // are reallocated, so the caller must reinitialize any cached copies of
   318  // those fields.
   319  //
   320  // Any open Fonts associated with the Display may be updated in
   321  // response to a DPI change, meaning the caller should expect that
   322  // a Font's Height may be different after calling Attach as well.
   323  // The Font pointers themselves do not change.
   324  //
   325  func (d *Display) Attach(ref int) error {
   326  	d.mu.Lock()
   327  	defer d.mu.Unlock()
   328  	if runtime.GOOS != "plan9" {
   329  		oi := d.Image
   330  		i, err := d.getimage0(oi)
   331  		if err != nil {
   332  			return err
   333  		}
   334  		d.Image = i
   335  	}
   336  	i := d.Image
   337  	d.Screen.free()
   338  	var err error
   339  	d.Screen, err = i.allocScreen(d.White, false)
   340  	if err != nil {
   341  		return err
   342  	}
   343  	d.ScreenImage.free()
   344  	d.ScreenImage, err = allocwindow(d.ScreenImage, d.Screen, i.R, ref, White)
   345  	if err != nil {
   346  		log.Fatal("allocwindow: ", err)
   347  	}
   348  
   349  	if d.HiDPI() {
   350  		for f := d.firstfont; f != nil; f = f.next {
   351  			loadhidpi(f)
   352  		}
   353  	} else {
   354  		for f := d.firstfont; f != nil; f = f.next {
   355  			if f.lodpi != nil && f.lodpi != f {
   356  				swapfont(f, &f.hidpi, &f.lodpi)
   357  			}
   358  		}
   359  	}
   360  
   361  	return nil
   362  }
   363  
   364  // Close closes the Display.
   365  func (d *Display) Close() error {
   366  	if d == nil {
   367  		return nil
   368  	}
   369  	d.mu.Lock()
   370  	defer d.mu.Unlock()
   371  	return d.conn.Close()
   372  }
   373  
   374  // TODO: drawerror
   375  
   376  func (d *Display) flushBuffer() error {
   377  	if len(d.buf) == 0 {
   378  		return nil
   379  	}
   380  	_, err := d.conn.WriteDraw(d.buf)
   381  	d.buf = d.buf[:0]
   382  	if err != nil {
   383  		if d.flushErr++; d.flushErr > 100 {
   384  			panic("draw flush: error loop: " + err.Error())
   385  		}
   386  		if d.flushErr > 110 {
   387  			log.Fatalf("draw flush: error loop: " + err.Error())
   388  		}
   389  		fmt.Fprintf(os.Stderr, "draw flush: %v\n", err)
   390  		return err
   391  	}
   392  	d.flushErr = 0
   393  	return nil
   394  }
   395  
   396  // Flush flushes pending I/O to the server, making any drawing changes visible.
   397  func (d *Display) Flush() error {
   398  	d.mu.Lock()
   399  	defer d.mu.Unlock()
   400  	return d.flush(true)
   401  }
   402  
   403  func (d *Display) flush(visible bool) error {
   404  	if visible {
   405  		d.bufsize++
   406  		a := d.bufimage(1)
   407  		d.bufsize--
   408  		a[0] = 'v'
   409  	}
   410  
   411  	return d.flushBuffer()
   412  }
   413  
   414  func (d *Display) bufimage(n int) []byte {
   415  	if d == nil || n < 0 || n > d.bufsize {
   416  		panic("bad count in bufimage")
   417  	}
   418  	if len(d.buf)+n > d.bufsize {
   419  		if err := d.flushBuffer(); err != nil {
   420  			panic("bufimage flush: " + err.Error())
   421  		}
   422  	}
   423  	i := len(d.buf)
   424  	d.buf = d.buf[:i+n]
   425  	return d.buf[i:]
   426  }
   427  
   428  // DefaultDPI is the DPI assumed when the actual display DPI is unknown.
   429  // It is also the base DPI assumed for the Display.Scale method,
   430  // which scales fixed-size pixel counts for higher-resolution displays.
   431  // See the Display documentation for more information.
   432  const DefaultDPI = 133
   433  
   434  // Scale scales the fixed pixel count n by d.DPI / DefaultDPI,
   435  // rounding appropriately. It can help programs that historically
   436  // assumed fixed pixel counts (for example, a 4-pixel border)
   437  // scale gracefully to high-resolution displays.
   438  // See the Display documentation for more information.
   439  func (d *Display) Scale(n int) int {
   440  	if d == nil || d.DPI <= DefaultDPI {
   441  		return n
   442  	}
   443  	return (n*d.DPI + DefaultDPI/2) / DefaultDPI
   444  }
   445  
   446  func atoi(b []byte) int {
   447  	i := 0
   448  	for i < len(b) && b[i] == ' ' {
   449  		i++
   450  	}
   451  	n := 0
   452  	for ; i < len(b) && '0' <= b[i] && b[i] <= '9'; i++ {
   453  		n = n*10 + int(b[i]) - '0'
   454  	}
   455  	return n
   456  }
   457  
   458  func atop(b []byte) Point {
   459  	return Pt(atoi(b), atoi(b[12:]))
   460  }
   461  
   462  func ator(b []byte) Rectangle {
   463  	return Rectangle{atop(b), atop(b[2*12:])}
   464  }
   465  
   466  func bplong(b []byte, n uint32) {
   467  	binary.LittleEndian.PutUint32(b, n)
   468  }
   469  
   470  func bpshort(b []byte, n uint16) {
   471  	binary.LittleEndian.PutUint16(b, n)
   472  }
   473  
   474  func (d *Display) HiDPI() bool {
   475  	return d.DPI >= DefaultDPI*3/2
   476  }
   477  
   478  func (d *Display) ScaleSize(n int) int {
   479  	if d == nil || d.DPI <= DefaultDPI {
   480  		return n
   481  	}
   482  	return (n*d.DPI + DefaultDPI/2) / DefaultDPI
   483  }