From e834a78ab2f565afb2d1759a771386b9b3e887e0 Mon Sep 17 00:00:00 2001 From: Denis Bernard Date: Tue, 29 Oct 2019 13:25:01 +0100 Subject: [PATCH] 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 --- app/internal/window/os_x11.go | 56 +++++++++++++++++------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/app/internal/window/os_x11.go b/app/internal/window/os_x11.go index 8f746b1a..9cbe2300 100644 --- a/app/internal/window/os_x11.go +++ b/app/internal/window/os_x11.go @@ -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})