Xpra: Ticket #811: Controling window placement with multiple monitors

Based on my post on the mailinglist [1]:

Motivation

I use xpra on a Windows with one (undocked laptop), two (docking station #1) or three (docking station #2) monitors. In the docking station cases, the *primary* display is/are always the external monitor and the secondary one the internal laptop display. Since the laptop is actually a convertible tablet, I use it nearly exclusively are notepad (having the display lying on the table with OneNote? open).

Although the internal laptop display is *not* the primary monitor, it is to the *left* of the other screens and hence, it has position (0,0) of the complete virtual display.

Now I have X window applications (again, proprietary, sorry) which in 90% of the cases place the windows on the *secondary* monitor. Since I don't even use that display as an actual monitor, it is so annoying to drag all the windows (and the applications really opens a lot of windows) to the actual primary screen. I asked in the forum of the proprietary application [2] and they claim the window manager would have the option to decide on which monitor new windows open etc.

Proposed enhancements

It would be great if some aspects on where new windows are placed in Windows in a multiple monitor setup can be controlled in xpra. Some suggestions which would overcome the problems above:

[1] http://lists.devloop.org.uk/pipermail/shifter-users/2015-February/001139.html [2] http://preview.tinyurl.com/m4k2l2f



Mon, 16 Feb 2015 04:54:24 GMT - Antoine Martin: owner changed; milestone set

The problem is that applications have many ways of requesting where they are placed on screen, from absolute coordinates on creation, gravity relative to their parent window, moving the window before showing it (and there is more than one way of doing that) - or even moving it after showing it (I've seen it done), specifying which screen to be on, etc..

Then when we create the window, we ask your client OS to place it (assuming we don't have any coordinates to use by that point) - and whatever it decides to do, we honour it.

So it isn't easy to start interfering with this process without breaking other well behaved applications and/or window managers.

Please post the -d window client output of when those windows are created so we can see what attributes are set, which will dictate what we can and can't tweak. The server log with -d window could also be useful, as long as it is limited to the moment of the window creation (otherwise it could be way too big to parse)


Wed, 18 Feb 2015 06:14:05 GMT - Lukas Haase: owner changed

I assume it's not easy. I'm also afraid it could break stuff. Particularly, I think about menus, which, in the end, are also windows. On the other hand, the current placement (placing always on a different minitor) is totally broken and drives me crazy. I don't know too much about X11 to understand if it is a window manager (and hence xpra) problem or the application itself.

In any case, wouldn't be an option to have a switch to turn on/off that behavior? So it can also be tested in a good manner.

What I could think of is a setting "Force window placement on same monitor". If this flag is set, xpra checks if the coordinate where the new window should be placed is on a different monitor. If this is the case, the placement is overwritten in some way (e.g. subtract/add coordinates in a way so that the window is placed at the same position but on the current monitor).

Here is the output of such a window when it opens on the leftmost display although it should land on the main/middle window:

2015-02-18 06:04:52,615 process_new_common: [425, 0, 0, 781, 657, {'opacity': -1, 'fullscreen': False, 'has-alpha': False, 'xid': '0x1000026', 'modal': False, 'title': 'Library Manager: initializing... ', 'icon-title': 'Cadence Library Manager', 'client-machine': 'server', 'pid': 14494, 'group-leader-xid': 16777256, 'iconic': False, 'window-type': ('NORMAL',), 'size-constraints': {'minimum-size': (300, 427), 'set-initial-position': True}, 'decorations': True, 'maximized': False, 'class-instance': ('Qt-subapplication', 'LibManager')}], OR=False
2015-02-18 06:04:52,615 make_new_window(..) client_window_classes=[<class 'xpra.client.gl.gtk2.gl_client_window.GLClientWindow'>, <class 'xpra.client.gtk2.border_client_window.BorderClientWindow'>], group_leader_window=None
2015-02-18 06:04:52,615 GLClientWindow(..)
2015-02-18 06:04:52,617 <class 'xpra.client.gl.gtk2.gl_client_window.GLClientWindow'>(gtk2.client, None, 425, 0, 0, 781, 657, {'opacity': -1, 'size-constraints': {'minimum-size': (300, 427), 'set-initial-position': True}, 'decorations': True, 'client-machine': 'r6cad-st130soi.stanford.edu', 'pid': 14494, 'group-leader-xid': 16777256, 'iconic': False, 'window-type': ('NORMAL',), 'fullscreen': False, 'has-alpha': False, 'xid': '0x1000026', 'title': 'Library Manager: initializing... ', 'icon-title': 'Cadence Library Manager', 'modal': False, 'maximized': False, 'class-instance': ('Qt-subapplication', 'LibManager')}, False, {}, (0, 0))
2015-02-18 06:04:52,617 set_window_type(['NORMAL']) hints=0
2015-02-18 06:04:52,617 setup_window() screen=-1, nscreens=1
2015-02-18 06:04:52,617 new_backing(781, 657) backing_class=<class 'xpra.client.gl.gtk2.gl_window_backing.GLPixmapBacking'>
2015-02-18 06:04:52,617 make_new_backing(<class 'xpra.client.gl.gtk2.gl_window_backing.GLPixmapBacking'>, 781, 657) effective backing class=<class 'xpra.client.gl.gtk2.gl_window_backing.GLPixmapBacking'>, server alpha=False, window alpha=False
2015-02-18 06:04:52,654 Win32Hooks: window frame size is 4x4
2015-02-18 06:04:52,710 GL do_configure_event(<gtk.gdk.Event at 036881E8: GDK_CONFIGURE x=4, y=757, width=781, height=657>)
2015-02-18 06:04:52,710 GLClientWindow(425 : gtk2.GLWindowBacking(425, (781, 657), None)).do_map_event(<gtk.gdk.Event at 036881E8: GDK_MAP>) OR=False
2015-02-18 06:04:52,711 GL do_configure_event(<gtk.gdk.Event at 036881E8: GDK_CONFIGURE x=4, y=757, width=781, height=657>)

As a different example, here is the output for such a menu, which of course, should be placed exactly where requested:

2015-02-18 06:07:12,286 process_new_common: [429, 2198, 681, 234, 523, {'opacity': -1, 'fullscreen': False, 'has-alpha': False, 'xid': '0xc0000b', 'pid': -1, 'window-type': ('DROPDOWN_MENU', 'POPUP_MENU', 'NORMAL'), 'maximized': False, 'transient-for': 418, 'override-redirect': True}], OR=True
2015-02-18 06:07:12,286 make_new_window(..) client_window_classes=[<class 'xpra.client.gl.gtk2.gl_client_window.GLClientWindow'>, <class 'xpra.client.gtk2.border_client_window.BorderClientWindow'>], group_leader_window=None
2015-02-18 06:07:12,286 GLClientWindow(..)
2015-02-18 06:07:12,286 <class 'xpra.client.gl.gtk2.gl_client_window.GLClientWindow'>(gtk2.client, None, 429, 2198, 681, 234, 523, {'opacity': -1, 'fullscreen': False, 'has-alpha': False, 'xid': '0xc0000b', 'pid': -1, 'window-type': ('DROPDOWN_MENU', 'POPUP_MENU', 'NORMAL'), 'maximized': False, 'transient-for': 418, 'override-redirect': True}, True, {}, (0, 0))
2015-02-18 06:07:12,286 GLClientWindow(429 : None).apply_transient_for(418) window=GLClientWindow(418 : gtk2.GLWindowBacking(418, (725, 193), YUV444P))
2015-02-18 06:07:12,286 set_window_type(['DROPDOWN_MENU', 'POPUP_MENU', 'NORMAL']) hints=9
2015-02-18 06:07:12,286 new_backing(234, 523) backing_class=<class 'xpra.client.gl.gtk2.gl_window_backing.GLPixmapBacking'>
2015-02-18 06:07:12,286 make_new_backing(<class 'xpra.client.gl.gtk2.gl_window_backing.GLPixmapBacking'>, 234, 523) effective backing class=<class 'xpra.client.gl.gtk2.gl_window_backing.GLPixmapBacking'>, server alpha=False, window alpha=False
2015-02-18 06:07:12,308 Win32Hooks: window frame size is 4x4
2015-02-18 06:07:12,326 GL do_configure_event(<gtk.gdk.Event at 036881D0: GDK_CONFIGURE x=2198, y=681, width=234, height=523>)
2015-02-18 06:07:12,326 GLClientWindow(429 : gtk2.GLWindowBacking(429, (234, 523), None)).do_map_event(<gtk.gdk.Event at 036881D0: GDK_MAP>) OR=True
2015-02-18 06:07:12,326 GL do_configure_event(<gtk.gdk.Event at 036881D0: GDK_CONFIGURE x=2198, y=681, width=234, height=523>)

I wait with posting server's "-d window" until it is necessary. Maybe this helps already.

Aside: I also tried ignoring the window with libfakeXinerama.

This is the content on my "~/.:1900-fakexinerama":

# 3 monitors:
3
# DISPLAY1 (677mm x 423mm)
1280 0 1920 1200
# DISPLAY2 (452mm x 271mm)
0 734 1280 768
# DISPLAY3 (677mm x 381mm)
3200 0 1920 1080

I tried several modifications, e.g.:

# 3 monitors:
3
# DISPLAY1 (677mm x 423mm)
1280 0 1920 1200
# DISPLAY2 (452mm x 271mm)
0 0 0 0
# DISPLAY3 (677mm x 381mm)
3200 0 1920 1080

But whatever I do, it's just ignored. (LD_PRELOAD is set appropriately). Also, this hack would have problems when I dynamically connect/disconnect monitors as it happens for laptops.


Wed, 18 Feb 2015 08:50:01 GMT - Antoine Martin: owner changed

From what I see, your application is requesting: 'set-initial-position': True whilst setting its position as (0,0). Please post the server log so we can see more details. Overriding application requested positioning could be tricky, it exists for a purpose..

As you figured out, the menu should be left alone - an important note: it isn't always possible to tell which windows are menus and which ones aren't, not all applications set the right hints.


Thu, 19 Feb 2015 00:33:24 GMT - alas:

With the way that the libfakexinerama file works, would it be possible to set a given display to be default for the positioning of new applications? (Allowing an application that wants to set a position as (0,0) to do so, but at (0,0) relative to that default display, rather than the Overall Desktop (0,0) coordinate?

(Just a thought, as the placement for some windows when I'm set up with multiple displays can be OCD taunting...)


Thu, 19 Feb 2015 02:42:40 GMT - Antoine Martin:

No, the fakexinerama stuff only allows us to expose monitor layouts, what applications decide to do with this information is completely out of our control.


Thu, 19 Feb 2015 04:40:28 GMT - Lukas Haase:

That's just so hard to believe because the placement is random (meaning: not always the same on just 80% of the time the wrong monitor). So it's random with a terrible bias actually.

I can imagine quite well how tricky this could be, hence the suggestion with a config option and placement on the requested position but the same monitor as the current focus.

It is very hard to capture anything useful because the server log fills MBs in seconds. I tried: I prepared a situation where, when I double click, the resulting window would open at the wrong monitor 80% of the time and tried to capture exactly the resulting logs.

Here is what appears on the client:

2015-02-19 04:26:32,269 process_new_common: [8, 120, 3221, 900, 690, {'opacity': -1, 'fullscreen': False, 'has-alpha': False, 'xid': '0xc004bf', 'modal': False, 'title': 'Virtuoso\xc2\xae Schematic Editor L Editing: test test_tia_mos schematic', 'icon-title': 'test_tia_mos', 'client-machine': 'server', 'pid': 6019, 'group-leader-xid': 12582961, 'iconic': False, 'window-type': ('NORMAL',), 'size-constraints': {'minimum-size': (100, 100), 'set-initial-position': True}, 'decorations': True, 'maximized': False, 'class-instance': ('Qt-subapplication', 'Virtuoso')}], OR=False
2015-02-19 04:26:32,270 make_new_window(..) client_window_classes=[<class 'xpra.client.gl.gtk2.gl_client_window.GLClientWindow'>, <class 'xpra.client.gtk2.border_client_window.BorderClientWindow'>], group_leader_window=None
2015-02-19 04:26:32,270 GLClientWindow(..)
2015-02-19 04:26:32,270 <class 'xpra.client.gl.gtk2.gl_client_window.GLClientWindow'>(gtk2.client, None, 8, 120, 3221, 900, 690, {'opacity': -1, 'size-constraints': {'minimum-size': (100, 100), 'set-initial-position': True}, 'decorations': True, 'client-machine': 'server', 'pid': 6019, 'group-leader-xid': 12582961, 'iconic': False, 'window-type': ('NORMAL',), 'fullscreen': False, 'has-alpha': False, 'xid': '0xc004bf', 'title': 'Virtuoso\xc2\xae Schematic Editor L Editing: test test_tia_mos schematic', 'icon-title': 'test_tia_mos', 'modal': False, 'maximized': False, 'class-instance': ('Qt-subapplication', 'Virtuoso')}, False, {}, (0, 0))
2015-02-19 04:26:32,270 set_window_type(['NORMAL']) hints=0
2015-02-19 04:26:32,270 setup_window() screen=-1, nscreens=1
2015-02-19 04:26:32,270 new_backing(900, 690) backing_class=<class 'xpra.client.gl.gtk2.gl_window_backing.GLPixmapBacking'>
2015-02-19 04:26:32,270 make_new_backing(<class 'xpra.client.gl.gtk2.gl_window_backing.GLPixmapBacking'>, 900, 690) effective backing class=<class 'xpra.client.gl.gtk2.gl_window_backing.GLPixmapBacking'>, server alpha=False, window alpha=False
2015-02-19 04:26:32,319 Win32Hooks: window frame size is 4x4
2015-02-19 04:26:32,352 GL do_configure_event(<gtk.gdk.Event at 037C16F8: GDK_CONFIGURE x=124, y=3244, width=900, height=690>)
2015-02-19 04:26:32,354 GL do_configure_event(<gtk.gdk.Event at 037C16F8: GDK_CONFIGURE x=124, y=808, width=900, height=690>)
2015-02-19 04:26:32,355 GLClientWindow(8 : gtk2.GLWindowBacking(8, (900, 690), None)).do_map_event(<gtk.gdk.Event at 037C16F8: GDK_MAP>) OR=False
2015-02-19 04:26:32,355 GL do_configure_event(<gtk.gdk.Event at 037C16F8: GDK_CONFIGURE x=124, y=808, width=900, height=690>)

The server part, although it contains only the relevant section (from the moment of clicking to the end when the window opened) is attached as server-window.txt because it is large.


Thu, 19 Feb 2015 04:41:05 GMT - Lukas Haase: attachment set


Thu, 19 Feb 2015 04:58:53 GMT - Antoine Martin:

The client gets a window of size 900x690 located at 120x3221, and honours the request.

From the server log, we can see that the position you get for the window has been requested by the application:

Found a potential client
new window 0xc004bf
XGetClassHint(0xc004bf) classhints: Qt-subapplication, Virtuoso
_update_client_geometry: using initial size=(900, 690) and position=(120, 3221)

And then this is what the server sees when the client honours the request:

client configured window 8 - WindowModel(0xc004bf - "Virtuoso® Schematic Editor L Editing: test test_tia_mos schematic"), at: (124, 808, 900, 690)

So I am very reluctant to second guess what the application requested.


Thu, 19 Feb 2015 05:14:09 GMT - Lukas Haase:

Hmm What do you mean specifically by that?


Thu, 19 Feb 2015 06:18:48 GMT - Antoine Martin:

I mean that it isn't xpra's job to override what applications have requested at random. Either fix the application to not request a specific location, or tell your client OS to not use a display you don't want to use.


Thu, 19 Feb 2015 17:32:28 GMT - alas:

Ahh, yes... this does make sense. I withdraw my request.


Tue, 14 Apr 2015 15:56:41 GMT - Antoine Martin: status changed; resolution set

Unless there's something I am missing, I don't think we're doing anything wrong here.


Sat, 23 Jan 2021 05:06:43 GMT - migration script:

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