Xpra: Ticket #1610: Repaint problems in urxvt

This problem only happens in urxvt as far as I know and it is only a problem if I disable OpenGL. I am using AwesomeWM on Debian Buster/Sid? and I am using xpra version 2.1 revision r16525, but I have seen this for a while. If I disable OpenGL and open a urxvt window, the whole window will refuse to repaint until I resize the window, which apparently triggers a refresh. Thus I cannot see what I am typing or the output of anything unless I constantly keep resizing the window. No other applications are affected (that I am aware of). I enabled logging for x11 and have attached the log here (truncated for the relevant part).



Sun, 30 Jul 2017 03:30:19 GMT - chaanakya: attachment set

Log output


Sun, 30 Jul 2017 07:07:28 GMT - Antoine Martin: owner, description changed

Since the screen updates are getting through with opengl enabled, it's very likely that the server is sending them in a timely manner and that it is the client that is somehow failing to either update its back buffer or failing to ask the local X11 server to repaint the window. Does the window refresh if you use the "refresh" menu entry in xpra's system tray?

Please include the "-d paint" client log. (small sample is enough) Please also try running the client with:


Sun, 30 Jul 2017 14:08:00 GMT - chaanakya:

Another interesting observation (not sure if it's related). If I try running xpra control :470 refresh, it errors out with

server returned error code 127
 error processing control command: full_quality_refresh() takes at most 2 arguments (3 given)

if I don't have OpenGL enabled (it works fine if I enable OpenGL - presumably it's calling different methods?). Looking at the log, I get:

Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/xpra/server/server_core.py", line 1158, in process_control_command
    v = command.run(*args[1:])
  File "/usr/lib/python2.7/dist-packages/xpra/server/control_command.py", line 67, in run
    return super(ArgsControlCommand, self).run(*args)
  File "/usr/lib/python2.7/dist-packages/xpra/server/control_command.py", line 32, in run
    return self.do_run(*args)
  File "/usr/lib/python2.7/dist-packages/xpra/server/server_base.py", line 1799, in control_command_refresh
    ws.full_quality_refresh(window, {})
TypeError: full_quality_refresh() takes at most 2 arguments (3 given)

Sun, 30 Jul 2017 14:52:33 GMT - chaanakya: attachment set

Relevant portion with all debugging enabled


Sun, 30 Jul 2017 15:01:05 GMT - Antoine Martin:

The fact that even the cairo backend fails to repaint is very suspicious. I think your window manager is up to no good. The full_quality_refresh bug has been fixed in r16565 - thanks for pointing that out! (and FYI: the message would have been exactly the same with or without opengl enabled)

Please attach the client "-d paint" log output, not the server's.

I get a message that there are no loggers matching paint

That's because there aren't any - it's a client logger.

Are you sure you tried those environment switches on the client?


Sun, 30 Jul 2017 15:06:11 GMT - chaanakya:

Yes, I used those environment variables when calling xpra attach. I didn't realize that xpra control controls the server (in retrospect, it should have been obvious). I'm attaching a portion of the client log now - I think it's the right portion, but I'm not sure.


Sun, 30 Jul 2017 15:06:41 GMT - chaanakya: attachment set

client debug output


Sun, 30 Jul 2017 15:11:19 GMT - chaanakya:

Even weirder. If I e.g. start mutt on an xpra display within the same terminal emulator, it works fine. If I then spawn an emacsclient from within that, that fails to repaint. Something really odd is going on. I'll attach the debug output from mutt since that may prove to be instructive.

[edit] That's interesting. It seems mutt doesn't generate any refresh events - I presume ncurses handles the redrawing or something...


Sun, 30 Jul 2017 15:14:17 GMT - Antoine Martin:

Nothing suspicious there, you're getting your screen updates through the mmap channel:

draw_region(0, 0, 1360, 732, mmap, 1 bytes, 4080, {'rgb_format': 'RGB'}, \
  [<function record_decode_time at 0x7f0578784f50>, <function after_draw_refresh at 0x7f0560917938>])

And then we fire the callbacks, in particular:

after_draw_refresh(True, ) 1360x732 at 0x0 encoding=mmap, options={'encoding': 'mmap', 'rgb_format': 'RGB'}

This will call gtk.Widget.queue_draw_area - the documentation says: the window will receive expose events for the union of all regions that have been invalidated. This should therefore cause the X11 server to call the window's do_expose_event method to repaint what had been invalidated. Please try the "-d events" switch client side to see if those events come through.


Sun, 30 Jul 2017 15:18:03 GMT - chaanakya:

2017-07-30 11:15:47,657 on_realize(ClientWindow(5)) gdk window=<gtk.gdk.Window object at 0x7f917055aaa0 (GdkWindow at 0x5455b1db40)>
2017-07-30 11:15:47,673 ClientWindow(5).do_configure_event(<gtk.gdk.Event at 0x7f9170554378: GDK_CONFIGURE x=0, y=0, width=1366, height=768>) OR=False, iconified=False
2017-07-30 11:15:47,674 ClientWindow(5).do_configure_event(<gtk.gdk.Event at 0x7f9170554378: GDK_CONFIGURE x=0, y=0, width=1366, height=768>) OR=False, iconified=False
2017-07-30 11:15:47,678 do_expose_event(<gtk.gdk.Event at 0x7f9170554378: GDK_EXPOSE area=[0, 0, 1366, 768]>) area=gtk.gdk.Rectangle(0, 0, 1366, 768)
2017-07-30 11:15:48,560 ClientWindow(5).do_configure_event(<gtk.gdk.Event at 0x7f9170554378: GDK_CONFIGURE x=0, y=20, width=1366, height=748>) OR=False, iconified=False
2017-07-30 11:15:48,561 ClientWindow(5).do_configure_event(<gtk.gdk.Event at 0x7f9170554378: GDK_CONFIGURE x=0, y=20, width=1366, height=748>) OR=False, iconified=False
2017-07-30 11:15:48,571 do_expose_event(<gtk.gdk.Event at 0x7f9170554378: GDK_EXPOSE area=[0, 0, 1360, 748]>) area=gtk.gdk.Rectangle(0, 0, 1360, 748)
2017-07-30 11:15:48,804 ClientWindow(5).do_configure_event(<gtk.gdk.Event at 0x7f9170554378: GDK_CONFIGURE x=0, y=0, width=1366, height=768>) OR=False, iconified=False
2017-07-30 11:15:48,811 ClientWindow(5).do_configure_event(<gtk.gdk.Event at 0x7f9170554378: GDK_CONFIGURE x=0, y=0, width=1366, height=768>) OR=False, iconified=False
2017-07-30 11:15:48,814 do_expose_event(<gtk.gdk.Event at 0x7f9170554378: GDK_EXPOSE area=[0, 0, 1366, 768]>) area=gtk.gdk.Rectangle(0, 0, 1366, 768)
2017-07-30 11:15:56,197 ClientWindow(5).do_configure_event(<gtk.gdk.Event at 0x7f91705543c8: GDK_CONFIGURE x=0, y=20, width=1366, height=748>) OR=False, iconified=False
2017-07-30 11:15:56,198 ClientWindow(5).do_configure_event(<gtk.gdk.Event at 0x7f91705543c8: GDK_CONFIGURE x=0, y=20, width=1366, height=748>) OR=False, iconified=False
2017-07-30 11:15:56,207 do_expose_event(<gtk.gdk.Event at 0x7f91705543c8: GDK_EXPOSE area=[0, 0, 1360, 748]>) area=gtk.gdk.Rectangle(0, 0, 1360, 748)
2017-07-30 11:15:57,232 ClientWindow(5).do_configure_event(<gtk.gdk.Event at 0x7f91705543c8: GDK_CONFIGURE x=0, y=0, width=1366, height=768>) OR=False, iconified=False
2017-07-30 11:15:57,233 ClientWindow(5).do_configure_event(<gtk.gdk.Event at 0x7f91705543c8: GDK_CONFIGURE x=0, y=0, width=1366, height=768>) OR=False, iconified=False
2017-07-30 11:15:57,240 do_expose_event(<gtk.gdk.Event at 0x7f91705543c8: GDK_EXPOSE area=[0, 0, 1366, 768]>) area=gtk.gdk.Rectangle(0, 0, 1366, 768)
2017-07-30 11:16:06,127 ClientWindow(5).do_configure_event(<gtk.gdk.Event at 0x7f9170554378: GDK_CONFIGURE x=0, y=20, width=1366, height=748>) OR=False, iconified=False
2017-07-30 11:16:06,129 ClientWindow(5).do_configure_event(<gtk.gdk.Event at 0x7f9170554378: GDK_CONFIGURE x=0, y=20, width=1366, height=748>) OR=False, iconified=False

Sun, 30 Jul 2017 17:12:34 GMT - Antoine Martin:

The only expose events shown in the log sample follow the window resize (configure event). My guess is that your window manager is unhelpfully intercepting the expose events. Not much we can do about this.


Sun, 30 Jul 2017 17:34:40 GMT - chaanakya:

Hmm okay I'll post a bug report in AwesomeWM's bug tracker. Thanks!


Sun, 30 Jul 2017 18:13:39 GMT - Antoine Martin:

If I am right, this may workaround the issue in your window manager:

--- xpra/client/client_window_base.py	(revision 16553)
+++ xpra/client/client_window_base.py	(working copy)
@@ -589,7 +589,13 @@
                     rect = 0, 0, w, h
                 else:
                     rect = self._client.srect(x, y, width, height)
-                self.idle_add(self.queue_draw, *rect)
+                #self.idle_add(self.queue_draw, *rect)
+                from xpra.util import AdHocStruct
+                event = AdHocStruct()
+                from gtk.gdk import  Rectangle
+                w, h = self.get_size()
+                event.area = Rectangle(0, 0, w, h)
+                self.do_expose_event(event)
         #only register this callback if we actually need it:
         if backing.draw_needs_refresh:
             callbacks.append(after_draw_refresh)

This mimics the missing expose event.

Another thing worth trying:

--- xpra/client/gtk2/gtk2_window_base.py	(revision 16553)
+++ xpra/client/gtk2/gtk2_window_base.py	(working copy)
@@ -405,7 +405,7 @@
     def queue_draw(self, x, y, width, height):
         window = self.get_window()
         if window:
-            window.invalidate_rect(gdk.Rectangle(x, y, width, height), False)
+            window.invalidate_rect(gdk.Rectangle(x, y, width, height), True)
         else:
             log.warn("ignoring draw received for a window which is not realized yet!")

(this one would make more sense)


Sun, 30 Jul 2017 18:43:43 GMT - chaanakya:

Your first fix worked! I wonder how common this is...if it's common enough, it may be worth it to keep this as the default?


Sun, 30 Jul 2017 18:50:24 GMT - chaanakya: status changed; resolution set


Mon, 31 Jul 2017 05:21:06 GMT - Antoine Martin: status changed; resolution deleted

This is not "fixed": the first fix cannot be merged as it is (it only confirms that the expose events are not being generated because of your window manager), the second fix could be merged, please try that one.


Mon, 31 Jul 2017 05:31:12 GMT - chaanakya:

Unfortunately the second fix did not work.


Mon, 31 Jul 2017 06:06:15 GMT - Antoine Martin:

The patch below uses a better place to fix things. Only penalizing non-compositing window managers with the workaround - but since you reported that running a compositor didn't help, this would not fix all cases either.

--- xpra/client/gtk2/gtk2_window_base.py	(revision 16553)
+++ xpra/client/gtk2/gtk2_window_base.py	(working copy)
@@ -22,7 +22,7 @@
 from xpra.client.gtk_base.gtk_client_window_base import GTKClientWindowBase, HAS_X11_BINDINGS
 from xpra.gtk_common.gtk_util import WINDOW_NAME_TO_HINT, WINDOW_EVENT_MASK, BUTTON_MASK
 from xpra.gtk_common.gobject_util import one_arg_signal
-from xpra.util import WORKSPACE_UNSET, WORKSPACE_NAMES, csv, envbool, envint
+from xpra.util import WORKSPACE_UNSET, WORKSPACE_NAMES, csv, envbool, envint, AdHocStruct
 from xpra.os_util import strtobytes
@@ -404,10 +404,18 @@
     def queue_draw(self, x, y, width, height):
         window = self.get_window()
-        if window:
-            window.invalidate_rect(gdk.Rectangle(x, y, width, height), False)
+        if not window:
+            log.warn("ignoring draw received for a window which is not realized yet!")
+            return
+        rect = gdk.Rectangle(x, y, width, height)
+        if self.is_composited():
+            window.invalidate_rect(rect, False)
         else:
-            log.warn("ignoring draw received for a window which is not realized yet!")
+            #draw directly (bad) to workaround buggy window managers:
+            #see: https://github.com/Xpra-org/xpra/issues/1610
+            event = AdHocStruct()
+            event.area = rect
+            self.do_expose_event(event)
     def do_expose_event(self, event):
         #cannot use self

I will probably close this ticket as wontfix. The paint code in xpra is very very well tested and works with GTK2, GTK3, on 40+ platforms (including things as varied as macos, win32, bsd, etc). This is also the only recommended way of painting the screen in all the GTK documentation.

Something in your window manager is swallowing those important expose events and we shouldn't be trying to workaround that.


Mon, 31 Jul 2017 09:03:14 GMT - Antoine Martin:

Based on the upstream ticket: Depth=32 not redrawn correctly (CopyArea has no effect).


Mon, 31 Jul 2017 12:41:23 GMT - chaanakya:

Interestingly, the latest patch happened to work in my case. I have absolutely no idea what's going on as the fix suggested in the upstream ticket did not work.


Mon, 31 Jul 2017 15:18:13 GMT - Antoine Martin: status changed; resolution set

Based on the upstream ticket (see comment:16), there seems to be an issue in awesome - fixed in newer versions, but not fixed in Debian (...) Either a missing configure notify: size_hints_honor issue #1340

So I am closing this ticket as invalid.

I've partially merged the workaround in r16571: we can enable "immediate paint" mode using XPRA_FORCE_IMMEDIATE_PAINT=1, which could be useful for testing / debugging in the future, or as a workaround for those stuck with buggy versions of awesome.


Sat, 23 Jan 2021 05:29:03 GMT - migration script:

this ticket has been moved to: https://github.com/Xpra-org/xpra/issues/1610