package main import ( "github.com/BurntSushi/xgb" "github.com/BurntSushi/xgb/render" "github.com/BurntSushi/xgb/xproto" "log" "math/rand" ) func F64ToFixed(f float64) render.Fixed { return render.Fixed(f * 65536) } func FixedToF64(f render.Fixed) float64 { return float64(f) / 65536 } func main() { X, err := xgb.NewConn() if err != nil { log.Fatalln(err) } if err := render.Init(X); err != nil { log.Fatalln(err) } setup := xproto.Setup(X) screen := setup.DefaultScreen(X) var visual xproto.Visualid var depth byte for _, i := range screen.AllowedDepths { if i.Depth == 32 { // TODO: Could/should check other parameters. for _, v := range i.Visuals { if v.Class == xproto.VisualClassTrueColor { visual = v.VisualId depth = i.Depth break } } } } if visual == 0 { log.Fatalln("cannot find an RGBA TrueColor visual") } mid, err := xproto.NewColormapId(X) if err != nil { log.Fatalln(err) } _ = xproto.CreateColormap( X, xproto.ColormapAllocNone, mid, screen.Root, visual) wid, err := xproto.NewWindowId(X) if err != nil { log.Fatalln(err) } // Border pixel and colormap are required when depth differs from parent. _ = xproto.CreateWindow(X, depth, wid, screen.Root, 0, 0, 500, 500, 0, xproto.WindowClassInputOutput, visual, xproto.CwBorderPixel|xproto.CwColormap, []uint32{0, uint32(mid)}) // This could be included in CreateWindow parameters. _ = xproto.ChangeWindowAttributes(X, wid, xproto.CwBackPixel|xproto.CwEventMask, []uint32{0x80808080, xproto.EventMaskStructureNotify | xproto.EventMaskKeyPress | xproto.EventMaskExposure}) title := []byte("Gradient") _ = xproto.ChangeProperty(X, xproto.PropModeReplace, wid, xproto.AtomWmName, xproto.AtomString, 8, uint32(len(title)), title) _ = xproto.MapWindow(X, wid) pformats, err := render.QueryPictFormats(X).Reply() if err != nil { log.Fatalln(err) } // Similar to XRenderFindVisualFormat. // The DefaultScreen is almost certain to be zero. var pformat render.Pictformat for _, pd := range pformats.Screens[X.DefaultScreen].Depths { // This check seems to be slightly extraneous. if pd.Depth != depth { continue } for _, pv := range pd.Visuals { if pv.Visual == visual { pformat = pv.Format } } } // ...or just scan through pformats.Formats and look for matches, which is // what XRenderFindStandardFormat in Xlib does as well as exp/shiny. pid, err := render.NewPictureId(X) if err != nil { log.Fatalln(err) } render.CreatePicture(X, pid, xproto.Drawable(wid), pformat, 0, []uint32{}) // Reserve an ID for the gradient. gid, err := render.NewPictureId(X) if err != nil { log.Fatalln(err) } var from, to render.Color recolor := func() { start := rand.Uint32() & 0xffffff from = render.Color{ Red: 0x101 * uint16((start>>16)&0xff), Green: 0x101 * uint16((start>>8)&0xff), Blue: 0x101 * uint16(start&0xff), Alpha: 0xffff, } end := rand.Uint32() & 0xffffff to = render.Color{ Red: 0x101 * uint16((end>>16)&0xff), Green: 0x101 * uint16((end>>8)&0xff), Blue: 0x101 * uint16(end&0xff), Alpha: 0xffff, } } var w, h uint16 gradient := func() { if w < 100 || h < 100 { return } _ = render.CreateLinearGradient(X, gid, render.Pointfix{F64ToFixed(0), F64ToFixed(0)}, render.Pointfix{F64ToFixed(0), F64ToFixed(float64(h) - 100)}, 2, []render.Fixed{F64ToFixed(0), F64ToFixed(1)}, []render.Color{from, to}) _ = render.Composite(X, render.PictOpSrc, gid, render.PictureNone, pid, 0, 0, 0, 0, 50, 50, w-100, h-100) _ = render.FreePicture(X, gid) } for { ev, xerr := X.WaitForEvent() if xerr != nil { log.Printf("Error: %s\n", xerr) return } if ev == nil { return } log.Printf("Event: %s\n", ev) switch e := ev.(type) { case xproto.UnmapNotifyEvent: return case xproto.ConfigureNotifyEvent: w, h = e.Width, e.Height recolor() case xproto.KeyPressEvent: recolor() gradient() case xproto.ExposeEvent: gradient() } } }