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
+22 -22
View File
@@ -112,11 +112,6 @@ func (w *x11Window) wakeup() {
} }
func (w *x11Window) display() unsafe.Pointer { 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) return unsafe.Pointer(w.x)
} }
@@ -137,24 +132,40 @@ func (w *x11Window) loop() {
{Fd: int32(xfd), Events: syscall.POLLIN | syscall.POLLERR}, {Fd: int32(xfd), Events: syscall.POLLIN | syscall.POLLERR},
{Fd: int32(w.notify.read), 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. // Plenty of room for a backlog of notifications.
buf := make([]byte, 100) buf := make([]byte, 100)
var syn, redraw bool
loop: loop:
for !w.dead { for !w.dead {
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() w.mu.Lock()
animating := w.animating animating := w.animating
w.mu.Unlock() w.mu.Unlock()
// Clear poll events.
*dispEvents = 0
if animating { if animating {
redraw = true redraw = true
syn = h.handleEvents() } else {
} else if _, err := syscall.Ppoll(pollfds, nil, nil); err != nil && err != syscall.EINTR { // 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)) 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. // Clear notifications.
for { for {
_, err := syscall.Read(w.notify.read, buf) _, err := syscall.Read(w.notify.read, buf)
@@ -166,15 +177,6 @@ loop:
} }
redraw = true 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 { if redraw || syn {
w.cfg.now = time.Now() w.cfg.now = time.Now()
@@ -188,8 +190,6 @@ loop:
}, },
Sync: syn, Sync: syn,
}) })
redraw = false
syn = false
} }
} }
w.w.Event(system.DestroyEvent{Err: nil}) w.w.Event(system.DestroyEvent{Err: nil})