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 }