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  }