func (s *screenImpl) NewTexture(size image.Point) (screen.Texture, error) { w, h := int64(size.X), int64(size.Y) if w < 0 || maxShmSide < w || h < 0 || maxShmSide < h || maxShmSize < 4*w*h { return nil, fmt.Errorf("x11driver: invalid texture size %v", size) } xm, err := xproto.NewPixmapId(s.xc) if err != nil { return nil, fmt.Errorf("x11driver: xproto.NewPixmapId failed: %v", err) } xp, err := render.NewPictureId(s.xc) if err != nil { return nil, fmt.Errorf("x11driver: xproto.NewPictureId failed: %v", err) } t := &textureImpl{ s: s, size: size, xm: xm, xp: xp, } xproto.CreatePixmap(s.xc, textureDepth, xm, xproto.Drawable(s.window32), uint16(w), uint16(h)) render.CreatePicture(s.xc, xp, xproto.Drawable(xm), s.pictformat32, 0, nil) render.SetPictureFilter(s.xc, xp, uint16(len("bilinear")), "bilinear", nil) return t, nil }
// adjustSize takes a client and dimensions, and adjust them so that they'll // account for window decorations. For example, if you want a window to be // 200 pixels wide, a window manager will typically determine that as // you wanting the *client* to be 200 pixels wide. The end result is that // the client plus decorations ends up being // (200 + left decor width + right decor width) pixels wide. Which is probably // not what you want. Therefore, transform 200 into // 200 - decoration window width - client window width. // Similarly for height. func adjustSize(xu *xgbutil.XUtil, win xproto.Window, w, h int) (int, int, error) { // raw client geometry cGeom, err := RawGeometry(xu, xproto.Drawable(win)) if err != nil { return 0, 0, err } // geometry with decorations pGeom, err := RawGeometry(xu, xproto.Drawable(win)) if err != nil { return 0, 0, err } neww := w - (pGeom.Width() - cGeom.Width()) newh := h - (pGeom.Height() - cGeom.Height()) if neww < 1 { neww = 1 } if newh < 1 { newh = 1 } return neww, newh, nil }
// NewIcccmIcon converts two pixmap ids (icon_pixmap and icon_mask in the // WM_HINTS properts) to a single xgraphics.Image. // It is okay for one of iconPixmap or iconMask to be 0, but not both. // You should probably use xgraphics.FindIcon instead of this directly. func NewIcccmIcon(X *xgbutil.XUtil, iconPixmap, iconMask xproto.Pixmap) (*Image, error) { if iconPixmap == 0 && iconMask == 0 { return nil, fmt.Errorf("NewIcccmIcon: At least one of iconPixmap or " + "iconMask must be non-zero, but both are 0.") } var pximg, mximg *Image var err error // Get the xgraphics.Image for iconPixmap. if iconPixmap != 0 { pximg, err = NewDrawable(X, xproto.Drawable(iconPixmap)) if err != nil { return nil, err } } // Now get the xgraphics.Image for iconMask. if iconMask != 0 { mximg, err = NewDrawable(X, xproto.Drawable(iconMask)) if err != nil { return nil, err } } // Now merge them together if both were specified. switch { case pximg != nil && mximg != nil: r := pximg.Bounds() var x, y int var bgra, maskBgra BGRA for x = r.Min.X; x < r.Max.X; x++ { for y = r.Min.Y; y < r.Max.Y; y++ { maskBgra = mximg.At(x, y).(BGRA) bgra = pximg.At(x, y).(BGRA) if maskBgra.A == 0 { pximg.SetBGRA(x, y, BGRA{ B: bgra.B, G: bgra.G, R: bgra.R, A: 0, }) } } } return pximg, nil case pximg != nil: return pximg, nil case mximg != nil: return mximg, nil } panic("unreachable") }
// XExpPaint achieves a similar result as XPaint and XSurfaceSet, but // uses CopyArea instead of setting a background pixmap and using ClearArea. // CreatePixmap must be called before using XExpPaint. // XExpPaint can be called on sub-images. // x and y correspond to the destination x and y to copy the image to. // // This should not be used on the same image with XSurfaceSet and XPaint. func (im *Image) XExpPaint(wid xproto.Window, x, y int) { if im.Pixmap == 0 { return } xproto.CopyArea(im.X.Conn(), xproto.Drawable(im.Pixmap), xproto.Drawable(wid), im.X.GC(), int16(im.Rect.Min.X), int16(im.Rect.Min.Y), int16(x), int16(y), uint16(im.Rect.Dx()), uint16(im.Rect.Dy())) }
func main() { X, err := xgbutil.NewConn() if err != nil { log.Fatal(err) } // Use the "NewDrawable" constructor to create an xgraphics.Image value // from a drawable. (Usually this is done with pixmaps, but drawables // can also be windows.) ximg, err := xgraphics.NewDrawable(X, xproto.Drawable(X.RootWin())) if err != nil { log.Fatal(err) } // Shows the screenshot in a window. ximg.XShowExtra("Screenshot", true) // If you'd like to save it as a png, use: // err = ximg.SavePng("screenshot.png") // if err != nil { // log.Fatal(err) // } xevent.Main(X) }
func (m *MotionRecorder) update() { geo, _ := xproto.GetGeometry(m.Window.Conn, xproto.Drawable(m.root)).Reply() if geo != nil { m.Resize(geo.Width, geo.Height) } }
func (m *TrayManager) addTrayIcon(xid xproto.Window) { m.checkValid() for _, id := range m.TrayIcons { if xproto.Window(id) == xid { return } } if d, err := damage.NewDamageId(TrayXU.Conn()); err != nil { return } else { m.dmageInfo[xid] = d if err := damage.CreateChecked(TrayXU.Conn(), d, xproto.Drawable(xid), damage.ReportLevelRawRectangles).Check(); err != nil { logger.Debug("DamageCreate Failed:", err) return } } composite.RedirectWindow(TrayXU.Conn(), xid, composite.RedirectAutomatic) m.TrayIcons = append(m.TrayIcons, uint32(xid)) icon := xwindow.New(TrayXU, xid) icon.Listen(xproto.EventMaskVisibilityChange | damage.Notify | xproto.EventMaskStructureNotify) icon.Change(xproto.CwBackPixel, 0) name, err := ewmh.WmNameGet(TrayXU, xid) if err != nil { logger.Debug("WmNameGet failed:", err, xid) } m.nameInfo[xid] = name m.notifyInfo[xid] = true dbus.Emit(m, "Added", uint32(xid)) logger.Infof("Added try icon: \"%s\"(%d)", name, uint32(xid)) }
func (s *screenImpl) initWindow32() error { visualid, err := findVisual(s.xsi, 32) if err != nil { return err } colormap, err := xproto.NewColormapId(s.xc) if err != nil { return fmt.Errorf("x11driver: xproto.NewColormapId failed: %v", err) } if err := xproto.CreateColormapChecked( s.xc, xproto.ColormapAllocNone, colormap, s.xsi.Root, visualid).Check(); err != nil { return fmt.Errorf("x11driver: xproto.CreateColormap failed: %v", err) } s.window32, err = xproto.NewWindowId(s.xc) if err != nil { return fmt.Errorf("x11driver: xproto.NewWindowId failed: %v", err) } s.gcontext32, err = xproto.NewGcontextId(s.xc) if err != nil { return fmt.Errorf("x11driver: xproto.NewGcontextId failed: %v", err) } const depth = 32 xproto.CreateWindow(s.xc, depth, s.window32, s.xsi.Root, 0, 0, 1, 1, 0, xproto.WindowClassInputOutput, visualid, // The CwBorderPixel attribute seems necessary for depth == 32. See // http://stackoverflow.com/questions/3645632/how-to-create-a-window-with-a-bit-depth-of-32 xproto.CwBorderPixel|xproto.CwColormap, []uint32{0, uint32(colormap)}, ) xproto.CreateGC(s.xc, s.gcontext32, xproto.Drawable(s.window32), 0, nil) return nil }
// RootGeometry gets the geometry of the root window. It will panic on failure. func RootGeometry(xu *xgbutil.XUtil) xrect.Rect { geom, err := RawGeometry(xu, xproto.Drawable(xu.RootWin())) if err != nil { panic(err) } return geom }
// BufferSwapCompleteEventNew constructs a BufferSwapCompleteEvent value that implements xgb.Event from a byte slice. func BufferSwapCompleteEventNew(buf []byte) xgb.Event { v := BufferSwapCompleteEvent{} b := 1 // don't read event number b += 1 // padding v.Sequence = xgb.Get16(buf[b:]) b += 2 v.EventType = xgb.Get16(buf[b:]) b += 2 b += 2 // padding v.Drawable = xproto.Drawable(xgb.Get32(buf[b:])) b += 4 v.UstHi = xgb.Get32(buf[b:]) b += 4 v.UstLo = xgb.Get32(buf[b:]) b += 4 v.MscHi = xgb.Get32(buf[b:]) b += 4 v.MscLo = xgb.Get32(buf[b:]) b += 4 v.Sbc = xgb.Get32(buf[b:]) b += 4 return v }
// NewDrawable converts an X drawable into a xgraphics.Image. // This is used in NewIcccmIcon. func NewDrawable(X *xgbutil.XUtil, did xproto.Drawable) (*Image, error) { // Get the geometry of the pixmap for use in the GetImage request. pgeom, err := xwindow.RawGeometry(X, xproto.Drawable(did)) if err != nil { return nil, err } // Get the image data for each pixmap. pixmapData, err := xproto.GetImage(X.Conn(), xproto.ImageFormatZPixmap, did, 0, 0, uint16(pgeom.Width()), uint16(pgeom.Height()), (1<<32)-1).Reply() if err != nil { return nil, err } // Now create the xgraphics.Image and populate it with data from // pixmapData and maskData. ximg := New(X, image.Rect(0, 0, pgeom.Width(), pgeom.Height())) // We'll try to be a little flexible with the image format returned, // but not completely flexible. err = readDrawableData(X, ximg, did, pixmapData, pgeom.Width(), pgeom.Height()) if err != nil { return nil, err } return ximg, nil }
// CompletionEventNew constructs a CompletionEvent value that implements xgb.Event from a byte slice. func CompletionEventNew(buf []byte) xgb.Event { v := CompletionEvent{} b := 1 // don't read event number b += 1 // padding v.Sequence = xgb.Get16(buf[b:]) b += 2 v.Drawable = xproto.Drawable(xgb.Get32(buf[b:])) b += 4 v.MinorEvent = xgb.Get16(buf[b:]) b += 2 v.MajorEvent = buf[b] b += 1 b += 1 // padding v.Shmseg = Seg(xgb.Get32(buf[b:])) b += 4 v.Offset = xgb.Get32(buf[b:]) b += 4 return v }
// NotifyEventNew constructs a NotifyEvent value that implements xgb.Event from a byte slice. func NotifyEventNew(buf []byte) xgb.Event { v := NotifyEvent{} b := 1 // don't read event number v.Level = buf[b] b += 1 v.Sequence = xgb.Get16(buf[b:]) b += 2 v.Drawable = xproto.Drawable(xgb.Get32(buf[b:])) b += 4 v.Damage = Damage(xgb.Get32(buf[b:])) b += 4 v.Timestamp = xproto.Timestamp(xgb.Get32(buf[b:])) b += 4 v.Area = xproto.Rectangle{} b += xproto.RectangleRead(buf[b:], &v.Area) v.Geometry = xproto.Rectangle{} b += xproto.RectangleRead(buf[b:], &v.Geometry) return v }
// Geometry retrieves an up-to-date version of the this window's geometry. // It also loads the geometry into the Geom member of Window. func (w *Window) Geometry() (xrect.Rect, error) { geom, err := RawGeometry(w.X, xproto.Drawable(w.Id)) if err != nil { return nil, err } w.Geom = geom return geom, err }
func (k *workspace) drawFrameBorders() { if k.fullscreen || k.listing == listWorkspaces { return } setForeground(colorUnfocused) rects := k.mainFrame.appendRectangles(nil) check(xp.PolyRectangleChecked(xConn, xp.Drawable(desktopXWin), desktopXGC, rects)) setForeground(colorFocused) k.focusedFrame.drawBorder() }
func NewScreenSaver() *ScreenSaver { s := &ScreenSaver{inhibitors: make(map[uint32]inhibitor)} s.xu, _ = xgbutil.NewConn() screensaver.Init(s.xu.Conn()) screensaver.QueryVersion(s.xu.Conn(), 1, 0) screensaver.SelectInput(s.xu.Conn(), xproto.Drawable(s.xu.RootWin()), screensaver.EventNotifyMask|screensaver.EventCycleMask) dpms.Init(s.xu.Conn()) go s.loop() return s }
// DecorGeometry retrieves the client's width and height including decorations. // This can be tricky. In a non-parenting window manager, the width/height of // a client can be found by inspecting the client directly. In a reparenting // window manager like Openbox, the parent of the client reflects the true // width/height. Still yet, in KWin, it's the parent of the parent of the // client that reflects the true width/height. // The idea then is to traverse up the tree until we hit the root window. // Therefore, we're at a top-level window which should accurately reflect // the width/height. func (w *Window) DecorGeometry() (xrect.Rect, error) { parent := w for { tempParent, err := parent.Parent() if err != nil || tempParent.Id == w.X.RootWin() { break } parent = tempParent } return RawGeometry(w.X, xproto.Drawable(parent.Id)) }
// InvalidateBuffersEventNew constructs a InvalidateBuffersEvent value that implements xgb.Event from a byte slice. func InvalidateBuffersEventNew(buf []byte) xgb.Event { v := InvalidateBuffersEvent{} b := 1 // don't read event number b += 1 // padding v.Sequence = xgb.Get16(buf[b:]) b += 2 v.Drawable = xproto.Drawable(xgb.Get32(buf[b:])) b += 4 return v }
func (w *Window) Redraw() { /* Hide X11 borders */ var mask uint16 = xproto.ConfigWindowBorderWidth values := make([]uint32, 1) values[0] = 0 xproto.ConfigureWindow(w.conn, w.id, mask, values) wdt, hgh := int16(w.geom.W), int16(w.geom.H) /* Drawing background */ bg := xproto.Rectangle{0, 0, uint16(wdt), uint16(hgh)} bgs := make([]xproto.Rectangle, 1) bgs[0] = bg xproto.PolyFillRectangle(w.conn, xproto.Drawable(w.id), w.gc.bg, bgs) /* Drawing borders */ vertices := make([]xproto.Point, 5) vertices[0].X = 0 vertices[0].Y = 0 vertices[1].X = wdt vertices[1].Y = 0 vertices[2].X = wdt vertices[2].Y = hgh vertices[3].X = 0 vertices[3].Y = hgh vertices[4].X = 0 vertices[4].Y = 0 xproto.PolyLine(w.conn, xproto.CoordModeOrigin, xproto.Drawable(w.id), w.gc.bc, vertices) /* Drawing text */ hline := int16(w.gc.fontHeight) x, y := int16(w.gc.border), int16(w.gc.border+w.gc.fontUp) for _, line := range w.lines { xproto.ImageText8(w.conn, byte(len(line)), xproto.Drawable(w.id), w.gc.fg, x, y, line) y += hline } }
func (self *SessionModule) WriteWindowImage(window_id string, w io.Writer) error { if id, err := self.toX11WindowId(window_id); err == nil { drawable := xproto.Drawable(id) if image, err := xgraphics.NewDrawable(self.X, drawable); err == nil { image.XSurfaceSet(id) image.XDraw() return image.WritePng(w) } else { return err } } else { return err } }
func icon2md5(xid xproto.Window) []byte { pixmap, _ := xproto.NewPixmapId(TrayXU.Conn()) defer xproto.FreePixmap(TrayXU.Conn(), pixmap) if err := composite.NameWindowPixmapChecked(TrayXU.Conn(), xid, pixmap).Check(); err != nil { logger.Warning("NameWindowPixmap failed:", err, xid) return nil } im, err := xgraphics.NewDrawable(TrayXU, xproto.Drawable(pixmap)) if err != nil { logger.Warning("Create xgraphics.Image failed:", err, pixmap) return nil } buf := bytes.NewBuffer(nil) im.WritePng(buf) hasher := md5.New() hasher.Write(buf.Bytes()) return hasher.Sum(nil) }
func main() { X, _ := xgbutil.NewConn() heads, err := xinerama.PhysicalHeads(X) if err != nil { fmt.Printf("ERROR: %v\n", err) } // Test intersection r1 := xrect.New(0, 0, 100, 100) r2 := xrect.New(100, 100, 100, 100) fmt.Println(xrect.IntersectArea(r1, r2)) // Test largest overlap window := xrect.New(1800, 0, 300, 200) fmt.Println(xrect.LargestOverlap(window, heads)) // Test ApplyStrut rgeom, _ := xwindow.RawGeometry(X, xproto.Drawable(X.RootWin())) fmt.Println("---------------------------") for i, head := range heads { fmt.Printf("%d - %v\n", i, head) } // Let's actually apply struts to the current environment clients, _ := ewmh.ClientListGet(X) for _, client := range clients { strut, err := ewmh.WmStrutPartialGet(X, client) if err == nil { xrect.ApplyStrut(heads, rgeom.Width(), rgeom.Height(), strut.Left, strut.Right, strut.Top, strut.Bottom, strut.LeftStartY, strut.LeftEndY, strut.RightStartY, strut.RightEndY, strut.TopStartX, strut.TopEndX, strut.BottomStartX, strut.BottomEndX) } } fmt.Println("---------------------------") fmt.Println("After applying struts...") for i, head := range heads { fmt.Printf("%d - %v\n", i, head) } }
func main() { // os.Setenv("DISPLAY", "localhost:6080") c, err := xgb.NewConn() if err != nil { fmt.Println(err) return } screen := xproto.Setup(c).DefaultScreen(c) rect := image.Rect(0, 0, int(screen.WidthInPixels), int(screen.HeightInPixels)) x, y := rect.Dx(), rect.Dy() xImg, err := xproto.GetImage(c, xproto.ImageFormatZPixmap, xproto.Drawable(screen.Root), int16(rect.Min.X), int16(rect.Min.Y), uint16(x), uint16(y), 0xffffffff).Reply() if err != nil { fmt.Println("Error: %s\r\n", err) } data := xImg.Data for i := 0; i < len(data); i += 4 { data[i], data[i+2], data[i+3] = data[i+2], data[i], 255 } img := &image.RGBA{ data, 4 * x, image.Rect(0, 0, x, y)} z.FcheckParents("screen") f := z.FileW("screen") defer f.Close() png := jpeg.Encode(f, img, &jpeg.Options{90}) fmt.Printf("End with png: %v", png) }
// Draws a single point on the window in the window (rather then plot) // coordinate system. // This routine is slow because it is essentially performing a buffer flip for // each point. // // This routine should be avoided. This is here as an assistance function rather // then the intended routine to be used for plotting. Prefer the 'Plot' data // functions over this function in almost all circumstances. func (plot *PlotWindow) DrawPoint(x, y int, pixelColor color.Color) { if plot == nil { return } ximg, err := xgraphics.NewDrawable(system.X, xproto.Drawable(plot.window.Id)) if err != nil { return } defer ximg.Destroy() ximg.Set(x, y, pixelColor) ximg.XSurfaceSet(plot.window.Id) ximg.XDraw() ximg.XPaint(plot.window.Id) }
// CreatePixmap allocates an X resource identifier for a pixmap. (It does not // do any drawing.) You only need to call this if you're using XDraw/XExpPaint. // If you're using XSurfaceSet/XDraw/XPaint, then CreatePixmap is called for // you automatically. func (im *Image) CreatePixmap() error { // Generate the pixmap id. pid, err := xproto.NewPixmapId(im.X.Conn()) if err != nil { return err } // Now actually create the pixmap. err = xproto.CreatePixmapChecked(im.X.Conn(), im.X.Screen().RootDepth, pid, xproto.Drawable(im.X.RootWin()), uint16(im.Bounds().Dx()), uint16(im.Bounds().Dy())).Check() if err != nil { return err } // Now give it to the image. im.Pixmap = pid return nil }
// Draws a array of points on the window in the window (rather then plot) // coordinate system. // This routine is faster then the single point system because it performs only // one buffer flip per Point array rather then per point. // // This routine should be avoided. This is here as an assistance function rather // then the intended routine to be used for plotting. Prefer the 'Plot' data // functions over this function in almost all circumstances. func (plot *PlotWindow) DrawPoints(points []image.Point, pixelColor color.Color) { if plot == nil { return } ximg, err := xgraphics.NewDrawable(system.X, xproto.Drawable(plot.window.Id)) if err != nil { return } defer ximg.Destroy() // for index, _ := range points { ximg.Set(points[index].X, points[index].Y, pixelColor) } ximg.XSurfaceSet(plot.window.Id) ximg.XDraw() ximg.XPaint(plot.window.Id) }
func CaptureRect(rect image.Rectangle) (*image.RGBA, error) { c, err := xgb.NewConn() if err != nil { return nil, err } defer c.Close() screen := xproto.Setup(c).DefaultScreen(c) x, y := rect.Dx(), rect.Dy() xImg, err := xproto.GetImage(c, xproto.ImageFormatZPixmap, xproto.Drawable(screen.Root), int16(rect.Min.X), int16(rect.Min.Y), uint16(x), uint16(y), 0xffffffff).Reply() if err != nil { return nil, err } data := xImg.Data for i := 0; i < len(data); i += 4 { data[i], data[i+2], data[i+3] = data[i+2], data[i], 255 } img := &image.RGBA{data, 4 * x, image.Rect(0, 0, x, y)} return img, nil }
func (s *screenImpl) NewWindow(opts *screen.NewWindowOptions) (screen.Window, error) { // TODO: look at opts. const width, height = 1024, 768 xw, err := xproto.NewWindowId(s.xc) if err != nil { return nil, fmt.Errorf("x11driver: xproto.NewWindowId failed: %v", err) } xg, err := xproto.NewGcontextId(s.xc) if err != nil { return nil, fmt.Errorf("x11driver: xproto.NewGcontextId failed: %v", err) } xp, err := render.NewPictureId(s.xc) if err != nil { return nil, fmt.Errorf("x11driver: render.NewPictureId failed: %v", err) } pictformat := render.Pictformat(0) switch s.xsi.RootDepth { default: return nil, fmt.Errorf("x11driver: unsupported root depth %d", s.xsi.RootDepth) case 24: pictformat = s.pictformat24 case 32: pictformat = s.pictformat32 } w := &windowImpl{ s: s, xw: xw, xg: xg, xp: xp, pump: pump.Make(), xevents: make(chan xgb.Event), } s.mu.Lock() s.windows[xw] = w s.mu.Unlock() xproto.CreateWindow(s.xc, s.xsi.RootDepth, xw, s.xsi.Root, 0, 0, width, height, 0, xproto.WindowClassInputOutput, s.xsi.RootVisual, xproto.CwEventMask, []uint32{0 | xproto.EventMaskKeyPress | xproto.EventMaskKeyRelease | xproto.EventMaskButtonPress | xproto.EventMaskButtonRelease | xproto.EventMaskPointerMotion | xproto.EventMaskExposure | xproto.EventMaskStructureNotify | xproto.EventMaskFocusChange, }, ) s.setProperty(xw, s.atomWMProtocols, s.atomWMDeleteWindow, s.atomWMTakeFocus) xproto.CreateGC(s.xc, xg, xproto.Drawable(xw), 0, nil) render.CreatePicture(s.xc, xp, xproto.Drawable(xw), pictformat, 0, nil) xproto.MapWindow(s.xc, xw) return w, nil }
func (f *frame) drawBorder() { check(xp.PolyRectangleChecked(xConn, xp.Drawable(desktopXWin), desktopXGC, []xp.Rectangle{f.rect})) }
func main() { X, err := xgb.NewConn() if err != nil { fmt.Println(err) return } wid, _ := xproto.NewWindowId(X) screen := xproto.Setup(X).DefaultScreen(X) xproto.CreateWindow(X, screen.RootDepth, wid, screen.Root, 0, 0, 240, 240, 0, xproto.WindowClassInputOutput, screen.RootVisual, xproto.CwBackPixel|xproto.CwEventMask, []uint32{ // values must be in the order defined by the protocol 0xffffffff, xproto.EventMaskStructureNotify | xproto.EventMaskKeyPress | xproto.EventMaskKeyRelease}) xproto.MapWindow(X, wid) fmt.Printf("%d %d\n", screen.AllowedDepths[0].Visuals[0].VisualId, screen.RootVisual) var ( ux, uy float64 = 1, 1 fe cairo.FontExtents te cairo.TextExtents text = "joy" x, y, px, dashlength float64 ) surface := cairo.NewSurfaceFromXCB(xproto.Drawable(wid), screen.AllowedDepths[0].Visuals[0], 240, 240) surface.Scale(240, 240) surface.SetFontSize(0.5) /* Drawing code goes here */ surface.SetSourceRGB(0.0, 0.0, 0.0) surface.SelectFontFace("Georgia", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) surface.FontExtents(&fe) ux, uy = surface.DeviceToUserDistance(ux, uy) if ux > uy { px = ux } else { px = uy } surface.FontExtents(&fe) surface.TextExtents(text, &te) x = 0.5 - te.X_bearing - te.Width/2 y = 0.5 - fe.Descent + fe.Height/2 /* baseline, descent, ascent, height */ surface.SetLineWidth(4 * px) dashlength = 9 * px surface.SetDash([]float64{dashlength}, 0) surface.SetSourceRGBA(0, 0.6, 0, 0.5) surface.MoveTo(x+te.X_bearing, y) surface.RelLineTo(te.Width, 0) surface.MoveTo(x+te.X_bearing, y+fe.Descent) surface.RelLineTo(te.Width, 0) surface.MoveTo(x+te.X_bearing, y-fe.Ascent) surface.RelLineTo(te.Width, 0) surface.MoveTo(x+te.X_bearing, y-fe.Height) surface.RelLineTo(te.Width, 0) surface.Stroke() /* extents: width & height */ surface.SetSourceRGBA(0, 0, 0.75, 0.5) surface.SetLineWidth(px) dashlength = 3 * px surface.SetDash([]float64{dashlength}, 0) surface.Rectangle(x+te.X_bearing, y+te.Y_bearing, te.Width, te.Height) surface.Stroke() /* text */ surface.MoveTo(x, y) surface.SetSourceRGB(0, 0, 0) surface.ShowText(text) /* bearing */ surface.SetDash(nil, 0) surface.SetLineWidth(2 * px) surface.SetSourceRGBA(0, 0, 0.75, 0.5) surface.MoveTo(x, y) surface.RelLineTo(te.X_bearing, te.Y_bearing) surface.Stroke() /* text's advance */ surface.SetSourceRGBA(0, 0, 0.75, 0.5) surface.Arc(x+te.X_advance, y+te.Y_advance, 5*px, 0, 2*math.Pi) surface.Fill() /* reference point */ surface.Arc(x, y, 5*px, 0, 2*math.Pi) surface.SetSourceRGBA(0.75, 0, 0, 0.5) surface.Fill() surface.Finish() surface.Destroy() for { ev, xerr := X.WaitForEvent() if ev == nil && xerr == nil { // fmt.Println("Both event and error are nil. Exiting...") return } if ev != nil { // fmt.Printf("Event: %s\n", ev) } if xerr != nil { // fmt.Printf("Error: %s\n", xerr) } } }