Ubuntu – How to prevent misplaced new windows -top bar is hiding- in a multi-monitor setup

multiple-monitorswindowwindow-manager

I'm using Ubuntu 18.04 desktop and the default Image Viewer software.

There's a strange and really frustrating case when I open an image(s) with a specific size(960×960 for today), the window will always appear at the top-left of my secondary screen with the top bar(the one we use to drag it around, I don't remember the name) hiding like thisenter image description here
I use a pair of screens, the large as primary on the left and the small as secondary on the right. The sidebar is in the secondary screen.

What makes it strange is the other sizes like 400×400enter image description here or 1920×1200enter image description here are fine.

So my question is, is it possible to make it open in the current monitor without the top bar missing?

Best Answer

We can only guess what is the cause of the issue, but I have no clue of a real fix. We can however fix a bad window placement with a workaround.

The principle

The tiny daemon-like process will wait for creation of any window, on any of your monitors, with a "negative" placement (either x or y) on the monitor, and move it to a position of x=80px, y=80px on the corresponding monitor:

enter image description here

It does so by looking at the SE corner of newly created windows. SE is assumed to be the monitor it should have been fully placed on. The placement of the NW corner of the window is then the trigger to either act (move the window to a better position), or leave the window alone: it should not be at either the left or above the monitor SE is on.

In all other cases, the window is left alone completely. Note that:

  • this process is not a burden in any way, it is just waiting for the occasion to happen, only acts if a new window is misplaced (not a polling process).
  • I could not test it in exactly your setup, since I don't have three monitors. I don't expect any issues with it though.
  • The process is scaling savvy, counts with the "translation" between Wnck and Gdk in handling scaling.

The code

#!/usr/bin/env python3

import gi
gi.require_version("Gtk", "3.0")
gi.require_version("Wnck", "3.0")
from gi.repository import Gtk, Wnck, Gdk

class WatchOut:
    def __init__(self):
        self.wnckscr = Wnck.Screen.get_default()
        self.gdkdsp = Gdk.Display.get_default()
        self.wnckscr.connect("window_opened", self.checkonwin)
        Gtk.main()

    def actoncreate(self, window):
        wingeo = window.get_geometry();
        # get window geo, take SE as reference, abs numbers
        nw_x = wingeo.xp
        nw_y = wingeo.yp
        se_x = nw_x + wingeo.widthp
        se_y = nw_y + wingeo.heightp
        # get scale
        defmon = self.gdkdsp.get_primary_monitor()
        scale = defmon.get_scale_factor()
        # get monitor with window, gdk uses scaled(!)
        currmon = self.gdkdsp.get_monitor_at_point(se_x/scale, se_y/scale)
        # gdk uses scaled numbers!
        mongeo = currmon.get_geometry() 
        mondata = [n * scale for n in [
            mongeo.x, mongeo.y, mongeo.width, mongeo.height
        ]]
        # check if action is needed, calc in abs numbers!
        # if winx < monitorx -and/or- winy < monitory, move window
        if any([nw_x < mondata[0], nw_y < mondata[1]]):
            self.move(
                window, mondata[0] + 80, mondata[1] + 80, 700, 700
            )

    def move(self, win, x, y, w, h):
        g = Wnck.WindowGravity.NORTHWEST
        flags = Wnck.WindowMoveResizeMask.X | \
            Wnck.WindowMoveResizeMask.Y | \
            Wnck.WindowMoveResizeMask.WIDTH | \
            Wnck.WindowMoveResizeMask.HEIGHT
        win.set_geometry(g, flags, x, y, w, h)

    def checkonwin(self, scr, newwin):
        if newwin.get_window_type () == Wnck.WindowType.NORMAL:
            self.actoncreate(newwin)

WatchOut()

How to use

  1. Copy the code above into an empty file, save it as watchout.py
  2. Test-run it from a terminal by the command:

    python3 /path/to/watchout.py
    

    Try to open application with a misbehaving placement, which should be pretty predictable, looking at your question.

  3. If all works fine, add it to startup applications. Possibly you need to set a tiny break before starting up the process, to make sure it won't break on a uncompleted desktop-loading.

Related Question