github.com/jmigpin/editor@v1.6.0/driver/xdriver/wimage/shmwimage.go (about) 1 package wimage 2 3 import ( 4 "fmt" 5 "image" 6 "image/draw" 7 "log" 8 "time" 9 10 "github.com/BurntSushi/xgb" 11 "github.com/BurntSushi/xgb/shm" 12 "github.com/BurntSushi/xgb/xproto" 13 "github.com/jmigpin/editor/util/syncutil" 14 ) 15 16 type ShmWImage struct { 17 opt *Options 18 segId shm.Seg 19 imgWrap *ShmImgWrap 20 putCompleted *syncutil.WaitForSet 21 } 22 23 func NewShmWImage(opt *Options) (*ShmWImage, error) { 24 //// init shared memory extension 25 //if err := shm.Init(opt.Conn); err != nil { 26 // return nil, err 27 //} 28 // get error from early init 29 if initErr != nil { 30 return nil, initErr 31 } 32 33 wi := &ShmWImage{opt: opt} 34 wi.putCompleted = syncutil.NewWaitForSet() 35 36 // server segment id 37 segId, err := shm.NewSegId(wi.opt.Conn) 38 if err != nil { 39 return nil, err 40 } 41 wi.segId = segId 42 43 // initial image 44 r := image.Rect(0, 0, 1, 1) 45 if err := wi.Resize(r); err != nil { 46 return nil, err 47 } 48 49 return wi, nil 50 } 51 func (wi *ShmWImage) Close() error { 52 return wi.imgWrap.Close() 53 } 54 55 func (wi *ShmWImage) Resize(r image.Rectangle) error { 56 imgWrap, err := NewShmImgWrap(r) 57 if err != nil { 58 return err 59 } 60 old := wi.imgWrap 61 wi.imgWrap = imgWrap 62 // clean old img 63 if old != nil { 64 // need to detach to attach a new img id later 65 _ = shm.Detach(wi.opt.Conn, wi.segId) 66 67 err := old.Close() 68 if err != nil { 69 return err 70 } 71 } 72 // attach to segId 73 readOnly := false 74 shmId := uint32(wi.imgWrap.shmId) 75 cookie := shm.AttachChecked(wi.opt.Conn, wi.segId, shmId, readOnly) 76 if err := cookie.Check(); err != nil { 77 return fmt.Errorf("shmwimage.resize.attach: %w", err) 78 } 79 80 return nil 81 } 82 83 func (wi *ShmWImage) Image() draw.Image { 84 return wi.imgWrap.Img 85 } 86 87 func (wi *ShmWImage) PutImage(r image.Rectangle) error { 88 wi.putCompleted.Start(500 * time.Millisecond) 89 if err := wi.putImage2(r); err != nil { 90 wi.putCompleted.Cancel() 91 return err 92 } 93 // wait for shm.CompletionEvent that should call PutImageCompleted() 94 // Returns early if the server fails to send the msg (failsafe) 95 _, err := wi.putCompleted.WaitForSet() 96 if err != nil { 97 err = fmt.Errorf("shm putCompleted: get, %w", err) 98 } 99 return err 100 } 101 102 func (wi *ShmWImage) putImage2(r image.Rectangle) error { 103 gctx := wi.opt.GCtx 104 img := wi.imgWrap.Img 105 drawable := xproto.Drawable(wi.opt.Window) 106 depth := wi.opt.ScreenInfo.RootDepth 107 b := img.Bounds() 108 c1 := shm.PutImageChecked( 109 wi.opt.Conn, 110 drawable, 111 gctx, 112 uint16(b.Dx()), uint16(b.Dy()), // total width/height 113 uint16(r.Min.X), uint16(r.Min.Y), uint16(r.Dx()), uint16(r.Dy()), // src x,y,w,h 114 int16(r.Min.X), int16(r.Min.Y), // dst x,y 115 depth, 116 xproto.ImageFormatZPixmap, 117 1, // send shm.CompletionEvent when done 118 wi.segId, 119 0) // offset 120 return c1.Check() 121 } 122 123 func (wi *ShmWImage) PutImageCompleted() { 124 err := wi.putCompleted.Set(nil) 125 if err != nil { 126 err = fmt.Errorf("shm putCompleted: set, %w", err) 127 log.Println(err) 128 } 129 } 130 131 //---------- 132 133 var initErr error 134 135 // initialize early to avoid concurrent map read/write (XGB library issue) 136 func Init(conn *xgb.Conn) { 137 initErr = shm.Init(conn) 138 }