package main import ( "github.com/BurntSushi/xgb" "github.com/BurntSushi/xgb/xproto" "log" "math" "math/rand" ) func main() { X, err := xgb.NewConn() if 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 := "Gradient" _ = xproto.ChangeProperty(X, xproto.PropModeReplace, wid, xproto.AtomWmName, xproto.AtomString, 8, uint32(len(title)), []byte(title)) _ = xproto.MapWindow(X, wid) cid, err := xproto.NewGcontextId(X) if err != nil { log.Fatalln(err) } _ = xproto.CreateGC(X, cid, xproto.Drawable(wid), xproto.GcGraphicsExposures, []uint32{0}) blend := func(a, b uint32, ratio, gamma float64) uint32 { iratio := 1 - ratio fa := math.Pow(float64(a)/255, gamma) fb := math.Pow(float64(b)/255, gamma) return uint32(math.Pow(ratio*fa+iratio*fb, 1/gamma)*255) & 0xff } // TODO: We could show some text just like we intend to with xgb-xrender.go. var w, h uint16 var start, end uint32 = 0xabcdef, 0x32ab54 gradient := func() { ra, ga, ba := (start>>16)&0xff, (start>>8)&0xff, start&0xff rb, gb, bb := (end>>16)&0xff, (end>>8)&0xff, end&0xff var low, high uint16 = 50, h - 50 if high > h { return } for y := low; y < high; y++ { ratio := float64(y-low) / (float64(high) - float64(low)) rR := blend(ra, rb, ratio, 2.2) gG := blend(ga, gb, ratio, 2.2) bB := blend(ba, bb, ratio, 2.2) _ = xproto.ChangeGC(X, cid, xproto.GcForeground, []uint32{0xff000000 | rR<<16 | gG<<8 | bB}) _ = xproto.PolyLine(X, xproto.CoordModeOrigin, xproto.Drawable(wid), cid, []xproto.Point{ {X: 50, Y: int16(y)}, {X: int16(w / 2), Y: int16(y)}, }) rR = blend(ra, rb, ratio, 1) gG = blend(ga, gb, ratio, 1) bB = blend(ba, bb, ratio, 1) _ = xproto.ChangeGC(X, cid, xproto.GcForeground, []uint32{0xff000000 | rR<<16 | gG<<8 | bB}) _ = xproto.PolyLine(X, xproto.CoordModeOrigin, xproto.Drawable(wid), cid, []xproto.Point{ {X: int16(w / 2), Y: int16(y)}, {X: int16(w - 50), Y: int16(y)}, }) } } 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 case xproto.KeyPressEvent: start = rand.Uint32() & 0xffffff end = rand.Uint32() & 0xffffff gradient() case xproto.ExposeEvent: gradient() } } }