app/internal/window: X11 fix black window on Xephyr

On Xephyr, when fitst entering x11Window.loop(), syscall.Ppoll would
block forever while XPending() would still return a non zero value.
This commit refactors the loop so that XPending() gets called first,
then fallback to waiting in ppoll if there are no pending draw events.

This has the added benefit of reducing the number of calls to ppoll when
receiving a lot of events.

Signed-off-by: Denis Bernard <db047h@gmail.com>
This commit is contained in:
Denis Bernard
2019-10-29 13:25:01 +01:00
committed by Elias Naur
parent 2eb233dda6
commit e834a78ab2
+28 -28
View File
@@ -112,11 +112,6 @@ func (w *x11Window) wakeup() {
}
func (w *x11Window) display() unsafe.Pointer {
// TODO(dennwc): We have an awesome X library written in pure Go, however,
// we can't use it because of this specific function.
// The *C.Display pointer is required to call eglGetDisplay,
// so we can't really implement the call in pure Go.
// Thus, we have to use Xlib for everything.
return unsafe.Pointer(w.x)
}
@@ -137,23 +132,39 @@ func (w *x11Window) loop() {
{Fd: int32(xfd), Events: syscall.POLLIN | syscall.POLLERR},
{Fd: int32(w.notify.read), Events: syscall.POLLIN | syscall.POLLERR},
}
dispEvents := &pollfds[0].Revents
xEvents := &pollfds[0].Revents
// Plenty of room for a backlog of notifications.
buf := make([]byte, 100)
var syn, redraw bool
loop:
for !w.dead {
w.mu.Lock()
animating := w.animating
w.mu.Unlock()
// Clear poll events.
*dispEvents = 0
if animating {
redraw = true
syn = h.handleEvents()
} else if _, err := syscall.Ppoll(pollfds, nil, nil); err != nil && err != syscall.EINTR {
panic(fmt.Errorf("x11 loop: poll failed: %w", err))
var syn, redraw bool
// Check for pending draw events before checking animation or blocking.
// This fixes an issue on Xephyr where on startup XPending() > 0 but
// Ppoll will still block. This also prevents no-op calls to Ppoll.
if syn = h.handleEvents(); !syn {
w.mu.Lock()
animating := w.animating
w.mu.Unlock()
if animating {
redraw = true
} else {
// Clear poll events.
*xEvents = 0
// Wait for X event or gio notification.
if _, err := syscall.Ppoll(pollfds, nil, nil); err != nil && err != syscall.EINTR {
panic(fmt.Errorf("x11 loop: poll failed: %w", err))
}
switch {
case *xEvents&syscall.POLLIN != 0:
syn = h.handleEvents()
if w.dead {
break loop
}
case *xEvents&(syscall.POLLERR|syscall.POLLHUP) != 0:
break loop
}
}
}
// Clear notifications.
for {
@@ -166,15 +177,6 @@ loop:
}
redraw = true
}
switch {
case *dispEvents&syscall.POLLIN != 0:
syn = h.handleEvents() || syn
if w.dead {
break loop
}
case *dispEvents&(syscall.POLLERR|syscall.POLLHUP) != 0:
break loop
}
if redraw || syn {
w.cfg.now = time.Now()
@@ -188,8 +190,6 @@ loop:
},
Sync: syn,
})
redraw = false
syn = false
}
}
w.w.Event(system.DestroyEvent{Err: nil})