github.com/rajveermalviya/gamen@v0.1.2-0.20220930195403-9be15877c1aa/internal/web/display.go (about)

     1  //go:build js
     2  
     3  package web
     4  
     5  import (
     6  	"syscall/js"
     7  	"time"
     8  
     9  	"github.com/rajveermalviya/gamen/internal/common/atomicx"
    10  )
    11  
    12  type Display struct {
    13  	destroyRequested atomicx.Bool
    14  	destroyed        atomicx.Bool
    15  
    16  	windows map[uint64]*Window
    17  
    18  	eventCallbacksChan chan func()
    19  }
    20  
    21  func NewDisplay() (*Display, error) {
    22  	return &Display{
    23  		windows:            map[uint64]*Window{},
    24  		eventCallbacksChan: make(chan func()),
    25  	}, nil
    26  }
    27  
    28  func (d *Display) Poll() bool {
    29  	wait := make(chan struct{})
    30  	cb := js.FuncOf(func(this js.Value, args []js.Value) any {
    31  		if d.destroyed.Load() {
    32  			return nil
    33  		}
    34  
    35  		wait <- struct{}{}
    36  
    37  		return nil
    38  	})
    39  	defer cb.Release()
    40  	js.Global().Call("requestAnimationFrame", cb)
    41  
    42  outerloop:
    43  	for {
    44  		select {
    45  		case cb := <-d.eventCallbacksChan:
    46  			cb()
    47  
    48  		innerloop:
    49  			for {
    50  				select {
    51  				case cb := <-d.eventCallbacksChan:
    52  					cb()
    53  				default:
    54  					break innerloop
    55  				}
    56  			}
    57  
    58  		case <-wait:
    59  			break outerloop
    60  		}
    61  	}
    62  
    63  	if d.destroyRequested.Load() && !d.destroyed.Load() {
    64  		d.destroy()
    65  		return false
    66  	}
    67  
    68  	return !d.destroyed.Load()
    69  }
    70  
    71  func (d *Display) Wait() bool {
    72  	// wait for first event
    73  	cb := <-d.eventCallbacksChan
    74  	cb()
    75  
    76  	// then poll all pending events
    77  	return d.Poll()
    78  }
    79  
    80  func (d *Display) WaitTimeout(timeout time.Duration) bool {
    81  	timer := time.NewTimer(timeout)
    82  
    83  	select {
    84  	case <-timer.C:
    85  		return !d.destroyed.Load()
    86  
    87  	case cb := <-d.eventCallbacksChan:
    88  		if !timer.Stop() {
    89  			<-timer.C
    90  		}
    91  
    92  		cb()
    93  
    94  		// then poll all pending events
    95  		return d.Poll()
    96  	}
    97  }
    98  
    99  func (d *Display) Destroy() {
   100  	d.destroyRequested.Store(true)
   101  }
   102  
   103  func (d *Display) destroy() {
   104  	for id, w := range d.windows {
   105  		w.Destroy()
   106  
   107  		d.windows[id] = nil
   108  		delete(d.windows, id)
   109  	}
   110  
   111  	close(d.eventCallbacksChan)
   112  
   113  	d.destroyed.Store(true)
   114  }