forked from joejulian/gio
app,app/internal/wm: introduce app.Window.Run and use it internally
app.Window implements a method for safely running functions against the underlying native window through the driverFuncs channel. However, the functions still run in a different goroutine than the one driving the native event loop, which forces the implementations in package wm to do complicated synchronization. A previous change added a mechanism to run functions in the native event loop thread. The macOS port needed this functionality, but with some care it can be generalized. That's what this change does through the new Run method. The advantage is that the thread switch dance is now confined to app.Window, with the help of a generic wm.Driver.Wakeup method. All other Driver methods can then assume they run on their event loop threads. Run is exported because it is also needed for programs that use Windows configured with CustomRenderer to control their own rendering. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+17
-52
@@ -87,59 +87,34 @@ type x11Window struct {
|
||||
}
|
||||
dead bool
|
||||
|
||||
mu sync.Mutex
|
||||
animating bool
|
||||
opts *Options
|
||||
|
||||
pointerBtns pointer.Buttons
|
||||
|
||||
clipboard struct {
|
||||
read bool
|
||||
write *string
|
||||
content []byte
|
||||
}
|
||||
cursor pointer.CursorName
|
||||
mode WindowMode
|
||||
|
||||
wakeups chan struct{}
|
||||
}
|
||||
|
||||
func (w *x11Window) SetAnimating(anim bool) {
|
||||
w.mu.Lock()
|
||||
w.animating = anim
|
||||
w.mu.Unlock()
|
||||
if anim {
|
||||
w.wakeup()
|
||||
}
|
||||
}
|
||||
|
||||
func (w *x11Window) ReadClipboard() {
|
||||
w.mu.Lock()
|
||||
w.clipboard.read = true
|
||||
w.mu.Unlock()
|
||||
w.wakeup()
|
||||
C.XDeleteProperty(w.x, w.xw, w.atoms.clipboardContent)
|
||||
C.XConvertSelection(w.x, w.atoms.clipboard, w.atoms.utf8string, w.atoms.clipboardContent, w.xw, C.CurrentTime)
|
||||
}
|
||||
|
||||
func (w *x11Window) WriteClipboard(s string) {
|
||||
w.mu.Lock()
|
||||
w.clipboard.write = &s
|
||||
w.mu.Unlock()
|
||||
w.wakeup()
|
||||
w.clipboard.content = []byte(s)
|
||||
C.XSetSelectionOwner(w.x, w.atoms.clipboard, w.xw, C.CurrentTime)
|
||||
}
|
||||
|
||||
func (w *x11Window) Option(opts *Options) {
|
||||
w.mu.Lock()
|
||||
w.opts = opts
|
||||
w.mu.Unlock()
|
||||
w.wakeup()
|
||||
}
|
||||
|
||||
func (w *x11Window) setOptions() {
|
||||
w.mu.Lock()
|
||||
opts := w.opts
|
||||
w.opts = nil
|
||||
w.mu.Unlock()
|
||||
if opts == nil {
|
||||
return
|
||||
}
|
||||
var shints C.XSizeHints
|
||||
if o := opts.MinSize; o != nil {
|
||||
shints.min_width = C.int(w.cfg.Px(o.Width))
|
||||
@@ -250,9 +225,6 @@ func (w *x11Window) ShowTextInput(show bool) {}
|
||||
|
||||
// Close the window.
|
||||
func (w *x11Window) Close() {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
var xev C.XEvent
|
||||
ev := (*C.XClientMessageEvent)(unsafe.Pointer(&xev))
|
||||
*ev = C.XClientMessageEvent{
|
||||
@@ -270,7 +242,11 @@ func (w *x11Window) Close() {
|
||||
|
||||
var x11OneByte = make([]byte, 1)
|
||||
|
||||
func (w *x11Window) wakeup() {
|
||||
func (w *x11Window) Wakeup() {
|
||||
select {
|
||||
case w.wakeups <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
if _, err := syscall.Write(w.notify.write, x11OneByte); err != nil && err != syscall.EAGAIN {
|
||||
panic(fmt.Errorf("failed to write to pipe: %v", err))
|
||||
}
|
||||
@@ -312,9 +288,7 @@ loop:
|
||||
// This fixes an issue on Xephyr where on startup XPending() > 0 but
|
||||
// poll will still block. This also prevents no-op calls to poll.
|
||||
if syn = h.handleEvents(); !syn {
|
||||
w.mu.Lock()
|
||||
anim = w.animating
|
||||
w.mu.Unlock()
|
||||
if !anim {
|
||||
// Clear poll events.
|
||||
*xEvents = 0
|
||||
@@ -333,7 +307,6 @@ loop:
|
||||
}
|
||||
}
|
||||
}
|
||||
w.setOptions()
|
||||
// Clear notifications.
|
||||
for {
|
||||
_, err := syscall.Read(w.notify.read, buf)
|
||||
@@ -344,6 +317,11 @@ loop:
|
||||
panic(fmt.Errorf("x11 loop: read from notify pipe failed: %w", err))
|
||||
}
|
||||
}
|
||||
select {
|
||||
case <-w.wakeups:
|
||||
w.w.Event(WakeupEvent{})
|
||||
default:
|
||||
}
|
||||
|
||||
if anim || syn {
|
||||
w.w.Event(FrameEvent{
|
||||
@@ -358,20 +336,6 @@ loop:
|
||||
Sync: syn,
|
||||
})
|
||||
}
|
||||
w.mu.Lock()
|
||||
readClipboard := w.clipboard.read
|
||||
writeClipboard := w.clipboard.write
|
||||
w.clipboard.read = false
|
||||
w.clipboard.write = nil
|
||||
w.mu.Unlock()
|
||||
if readClipboard {
|
||||
C.XDeleteProperty(w.x, w.xw, w.atoms.clipboardContent)
|
||||
C.XConvertSelection(w.x, w.atoms.clipboard, w.atoms.utf8string, w.atoms.clipboardContent, w.xw, C.CurrentTime)
|
||||
}
|
||||
if writeClipboard != nil {
|
||||
w.clipboard.content = []byte(*writeClipboard)
|
||||
C.XSetSelectionOwner(w.x, w.atoms.clipboard, w.xw, C.CurrentTime)
|
||||
}
|
||||
}
|
||||
w.w.Event(system.DestroyEvent{Err: nil})
|
||||
}
|
||||
@@ -681,6 +645,7 @@ func newX11Window(gioWin Callbacks, opts *Options) error {
|
||||
cfg: cfg,
|
||||
xkb: xkb,
|
||||
xkbEventBase: xkbEventBase,
|
||||
wakeups: make(chan struct{}, 1),
|
||||
}
|
||||
w.notify.read = pipe[0]
|
||||
w.notify.write = pipe[1]
|
||||
|
||||
Reference in New Issue
Block a user