xpra icon
Bug tracker and wiki

Opened 6 weeks ago

Closed 6 weeks ago

Last modified 6 weeks ago

#1975 closed defect (wontfix)

Java Window Drift

Reported by: mjharkin Owned by: Antoine Martin
Priority: major Milestone: 2.4
Component: server Version: trunk
Keywords: Cc:

Description

Server on Centos7 r20582
Client both Python and HTML on Windows r20582
Can't reproduce outside Xpra.

While looking for a good test case for #1974 I found this.

Install java and run:

java -jar windowDrift.jar

The window has set initial bounds (100,y=100,width=450,height=300) that don't change through the java code or user input but the window moves from these bounds. The only change in code is the window toggles visibility every 5 seconds.

sh-4.2$ java -jar windowDrift.jar
Window bounds: java.awt.Rectangle[x=100,y=100,width=450,height=300]
Window bounds: java.awt.Rectangle[x=92,y=69,width=450,height=300]
Window bounds: java.awt.Rectangle[x=84,y=38,width=450,height=300]
Window bounds: java.awt.Rectangle[x=84,y=38,width=450,height=300]
Window bounds: java.awt.Rectangle[x=76,y=7,width=450,height=300]
Window bounds: java.awt.Rectangle[x=76,y=7,width=450,height=300]

Expected output:

Window bounds: java.awt.Rectangle[x=100,y=100,width=450,height=300]
Window bounds: java.awt.Rectangle[x=100,y=100,width=450,height=300]
Window bounds: java.awt.Rectangle[x=100,y=100,width=450,height=300]
Window bounds: java.awt.Rectangle[x=100,y=100,width=450,height=300]
Window bounds: java.awt.Rectangle[x=100,y=100,width=450,height=300]
Window bounds: java.awt.Rectangle[x=100,y=100,width=450,height=300]
Window bounds: java.awt.Rectangle[x=100,y=100,width=450,height=300]
Window bounds: java.awt.Rectangle[x=100,y=100,width=450,height=300]

Attachments (1)

WindowDrift.zip (10.5 KB) - added by mjharkin 6 weeks ago.
Sample jar and source

Download all attachments as: .zip

Change History (3)

Changed 6 weeks ago by mjharkin

Attachment: WindowDrift.zip added

Sample jar and source

comment:1 Changed 6 weeks ago by Antoine Martin

Resolution: wontfix
Status: newclosed

-d geometry,metadata log output (edited):

  • window created and picked up by the server:
    Window.read_initial_X11_properties()
    WM_PROTOCOLS=['WM_DELETE_WINDOW', 'WM_TAKE_FOCUS']
    WM_CLASS=('sun-awt-X11-XFramePeer', 'MainWindow')
    wm_hints.input = 0
    WM_NORMAL_HINTS={u'position': (100, 100), u'win_gravity': 1, u'size': (440, 270)}
    initial X11 position and size: requested((100, 100, 440, 270), \
        {u'position': (100, 100), 'gravity': 1, u'size': (440, 270)})=(100, 100, 440, 270)
    setup() hints={u'position': (100, 100), 'gravity': 1, u'size': (440, 270)} size=440x270
    updateprop(geometry, (100, 100, 440, 270)) unchanged
    setup() resizing windows to 440x270
    
  • client receives the window packet:
    client @31.600 process_new_common: [2, 100, 100, 440, 270, {\
        b'size-constraints': {b'position': (100, 100), b'gravity': 1, b'size': (440, 270)}, \
        b'window-type': (b'NORMAL',), b'xid': b'0xe00007', b'decorations': 1, b'title': b' ', \
        b'icon-title': b'Java', b'class-instance': (b'sun-awt-X11-XFramePeer', b'MainWindow'), \
        b'client-machine': b'desktop', b'pid': 5660, b'set-initial-position': True
        }], \
        metadata={
            b'size-constraints': {b'position': (100, 100), b'gravity': 1, b'size': (440, 270)}, \
            b'window-type': (b'NORMAL',), b'xid': b'0xe00007', \
            b'decorations': 1, b'title': b' ', b'icon-title': b'Java', \
            b'class-instance': (b'sun-awt-X11-XFramePeer', b'MainWindow'), \
            b'client-machine': b'desktop', b'pid': 5660, b'set-initial-position': True}, \
        OR=False
    
  • java updates the window hints:
    Property changed on 0xe00007: WM_NORMAL_HINTS
    WM_NORMAL_HINTS={u'position': (100, 100), u'win_gravity': 1, u'size': (450, 263)}
    updateprop(size-hints, {u'position': (100, 100), 'gravity': 1, u'size': (450, 263)}) \
        previous value={u'position': (100, 100), 'gravity': 1, u'size': (440, 270)}
    XpraServer._window_resized_signaled(WindowModel(0xe00007),(<GParamBoxed 'geometry'>,)) \
        geometry=(100, 100, 450, 263), desktop manager geometry=[100, 100, 440, 270]
    

we ignore the update on the corral window since it is not visible yet:

WindowModel.do_xpra_configure_event: corral window is not visible
_do_update_client_geometry: allocated 440x270 (from <function window_size at 0x7ff7083ce6e0>)
_do_update_client_geometry: size({u'position': (100, 100), 'gravity': 1, u'size': (450, 263)})=440x270
_do_update_client_geometry: position=100x100 (from <function window_position at 0x7ff7083ce848>)
updateprop(geometry, (100, 100, 440, 270)) unchanged

anyway, we process the resize (450x263) on the client window:

do_child_configure_request_event(<X11:ConfigureRequest {\
    'delivered_to': u'0x40003c', 'send_event': '0', 'type': '23', 'detail': '0', \
    'height': '263', 'width': '450', 'window': u'0xe00007', 'above': '0', 'y': '0', 'x': '0', \
    'serial': '0xb48', 'border_width': '0', 'value_mask': '12', 'display': ':15'}>) \
    client=0xe00007, corral=0x40003c, value_mask=Width|Height, \
    size-hints={u'position': (100, 100), 'gravity': 1, u'size': (450, 263)}
updateprop(requested-size, (450, 263)) previous value=(440, 270)
updateprop(geometry, (100, 100, 450, 263)) previous value=(100, 100, 440, 270)
  • meanwhile the client has set up the window with the original size (440x270), adjusting the position for the window frame size:
    client @31.610 setup_window() position=(100, 100), set_initial_position=True, OR=False, decorated=True
    client @31.610 setup_window() window frame sizes={'frame': (0, 0, 37, 0), 'offset': (0, 37)}
    client @31.611 setup_window() adjusted initial position=(100, 63)
    

and it sends back a configure packet to the server with this size (440x270):

_process_configure_window([2, 100, 100, 440, 270, {\
    'encoding.scrolling': True, 'encoding.transparency': False, \
    'encodings.rgb_formats': ('YUV420P', 'YUV422P', 'YUV444P', 'GBRP', 'BGRA', 'BGRX', 'RGBA', 'RGBX', 'RGB', 'BGR'), \
    'encoding.full_csc_modes': {'mpeg1': ('YUV420P',), 'mpeg2': ('YUV420P',), '...',)}, \
    'encoding.bit-depth': 24
    }, 0, {}, False, 2, (1776, 1368), ('mod2',)]) old window geometry: (100, 100, 450, 263)

(edited: first run returns 100,100 for position)
And the server honours this (older) size value:

updateprop(geometry, (100, 100, 440, 270)) previous value=(100, 100, 450, 263)

The server figures out that the client already uses this value and doesn't send another resize to it. (which would create a resize loop)

  • meanwhile the client receives the changed size:
    _process_window_move_resize[2, 100, 100, 450, 263, 1] moving / resizing window \
        GLClientWindow(2 : gtk3.GLDrawingArea(2, (440, 270), None)) (id=2) to (100, 100, 450, 263)
    window 2 move_resize(100, 100, 450, 263, 450)
    unchanged position 100x100, using resize(450, 263)
    resize(450, 263, 0) current size=(440, 270), fullscreen=None, maximized=False
    

And tells the server when it's done:

_process_configure_window([2, 100, 100, 450, 263, {}, 0, {}, False, 2, (1777, 1367), ('mod2',)]) old window geometry: (100, 100, 440, 270)

By this (convoluted) point, the position and size are "correct" on both the client and server.


  • window gets unmapped, we clean it up:
    scrub_x11() x11 properties=['WM_STATE', '_NET_FRAME_EXTENTS', '_NET_WM_ALLOWED_ACTIONS']
    

  • it gets mapped again, this time the hints contain:
    WM_NORMAL_HINTS={u'position': (100, 63), u'win_gravity': 1, u'size': (450, 263)}
    initial X11 position and size: requested((100, 63, 450, 263), \
        {u'position': (100, 63), 'gravity': 1, u'size': (450, 263)})=(100, 63, 450, 263)
    

Java is clearly trying to adjust for the window frame size which is set at 27 for my client's DE.

And sure enough, by making the _NET_FRAME_EXTENTS (#919) feature disabled using r20594 then Java maps the window at a different offset:

Window bounds: java.awt.Rectangle[x=100,y=100,width=450,height=300]
Window bounds: java.awt.Rectangle[x=95,y=75,width=450,height=300]
Window bounds: java.awt.Rectangle[x=95,y=75,width=440,height=270]
Window bounds: java.awt.Rectangle[x=95,y=75,width=440,height=270]
Window bounds: java.awt.Rectangle[x=95,y=75,width=440,height=270]
Window bounds: java.awt.Rectangle[x=95,y=75,width=440,height=270]

It is probably guessing the window offsets, using something like (5,25,5,5).

And that's all I've got time for.
Java is a mess, full of ugly hacks: http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/solaris/classes/sun/awt/X11/XWM.java and it's not the first time it gets it wrong: #725 (in particular ticket:705#comment:8).

Last edited 6 weeks ago by Antoine Martin (previous) (diff)

comment:2 Changed 6 weeks ago by mjharkin

Thanks.That explains a lot, had no idea how ugly this was.
The link to the java source helped, after a quick test setting:

wm-name=NO WM
env=XPRA_FRAME_EXTENTS=False

It improves the result but still moving after some time.
I'll look into it further if I've time.

Note: See TracTickets for help on using tickets.