We have to change approaches based on whether your computer comes with an accelerometer or not. If your computer comes with an accelerometer (usually a 2-in-1 laptop or a tablet), this can be completely automated by detecting screen orientation changes. Otherwise, creating a bash script to change the orientation and dock location at the same time would give you enough convenience.
Laptop with an Accelerometer
I wrote a python3 code that gets run on boot. The script listens to the screen rotation changes and changes the dock position accordingly.
- Download Dash to Dock gnome extension.
In my case, it did not need to be enabled, but it might be required.
- With your favorite text editor, create a python3 file, put the following code, and save it. You can pick any name or location of the file as long as you remember where it is so that you can refer to it in Step 3 and 4. I called it "dock_rotator.py".
#!/usr/bin/env python3
import os
import subprocess
import dbus
from dbus.mainloop.glib import DBusGMainLoop
from gi.repository import GObject as gobject
def run_command(command, shell=False):
""" Takes a string to run a command on bash
"""
p = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
output = p.communicate()[0]
return output.decode()
def enable_view_rotation_lock():
os.environ["GIO_EXTRA_MODULES"] = "/usr/lib/x86_64-linux-gnu/gio/modules/"
def rotation_locked():
locked = run_command("gsettings get \
org.gnome.settings-daemon.peripherals.touchscreen \
orientation-lock")
return locked.strip() == "true"
def move_dash_top():
run_command("gsettings set org.gnome.shell.extensions.dash-to-dock dock-position TOP")
def move_dash_left():
run_command("gsettings set org.gnome.shell.extensions.dash-to-dock dock-position LEFT")
def sensor_handler(*args, **kwargs):
if len(args) < 2:
return
dbus_dict = args[1]
key = 'AccelerometerOrientation'
if key not in dbus_dict:
return
orientation = dbus_dict[key]
if not rotation_locked():
if orientation in ["left-up", "right-up"]:
move_dash_top()
else:
move_dash_left()
def main():
enable_view_rotation_lock()
global loop
DBusGMainLoop(set_as_default=True)
loop = gobject.MainLoop()
bus = dbus.SystemBus()
bus.add_signal_receiver(
sensor_handler,
path='/net/hadess/SensorProxy')
loop.run()
if __name__ == "__main__":
main()
- Make the code executable by
chmod +x <filename>
command. If you saved your file in a directory owned by root in Step 2, you need to run sudo chmod +x <filename>
instead.
- Open "Startup Application" and add a new entry to direct to the python file you just created.
Name: <any>
Command: <path to the file>
Comment: <any>
- Restart your computer.
Computer without an Accelerometer
This approach creates a bash script which needs to be executed when you want to change your display orientation.
Download Dash to Dock gnome extension.
In my case, it did not need to be enabled, but it might be required.
With your favorite text editor, create a bash file called "drotate" (for dash rotate), put the following code, and save it.
#!/bin/bash
show_help () {
echo "drotate <rotation> [<display number>]"
echo "<rotation> : inverted | left | normal | right "
echo "<display number> : any number between 1 to number of displays connected"
echo " defaults to 1"
}
set_gnome_orientation () {
case "$1" in
inverted)
GNOME_ORIENTATION=LEFT
;;
normal)
GNOME_ORIENTATION=LEFT
;;
left)
GNOME_ORIENTATION=TOP
;;
right)
GNOME_ORIENTATION=TOP
;;
*)
echo "Invalid orientation"
show_help
exit 1
;;
esac
}
if [ $# -eq 0 ] || [ $# -gt 2 ]; then
show_help
exit 1
fi
if [ $# -eq 1 ]; then
DISPLAY_INDEX=1 # any number between 1 to # of displays connected
else
DISPLAY_INDEX=$2
fi
DISPLAY=$( xrandr --current | grep -F connected |\
grep -Fv disconnected | cut -d" " -f 1 | head -${DISPLAY_INDEX} | tail -1 )
if [ -z "$DISPLAY" ]; then
echo Could not find display $DISPLAY_INDEX
exit 1
fi
echo $DISPLAY
ORIENTATION="$1"
GNOME_ORIENTATION=""
set_gnome_orientation $ORIENTATION
xrandr -d :0 --output ${DISPLAY} --rotate ${ORIENTATION}
export GIO_EXTRA_MODULES=/usr/lib/x86_64-linux-gnu/gio/modules/
gsettings set org.gnome.shell.extensions.dash-to-dock dock-position $GNOME_ORIENTATION
Make the code executable by chmod +x drotate
command. If you saved your file in a directory owned by root in Step 2, you need to run sudo chmod +x drotate
instead.
Open ~/.bash
. Create a new line in the end of the file and paste the following line. export PATH="<path/to/the/directory/with/your/script>:$PATH
. For example, if you saved your file in /usr/local/drotate
, the command becomes export PATH="/usr/local:$PATH
. Notice drotate
being omitted.
Save the file.
Either restart your terminal or run source .bashrc
. They do the same thing. Now your script should be able to run.
Usage
Open your terminal (Ctrl-Alt-t) and type drotate <rotation> [<display number>]
.
drotate <rotation> [<display number>]
<rotation> : inverted | left | normal | right
<display number> : any number between 1 to number of displays connected
defaults to 1
Examples
- Rotate the main display to left up:
drotate left
- Rotate the main display back to normal:
drotate normal
- Rotate your subdisplay to left up:
drotate left 2
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](https://i.stack.imgur.com/71Flw.png)
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
- Copy the code above into an empty file, save it as
watchout.py
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.
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.
Best Answer
CLI method:
You can disable the multiple monitors option for the dock by running the following command in Terminal:
Then you can set your preferred monitor by running
(Put
-1
in place of<monitor-number>
for the primary monitor)GUI method:
See the answer by singrium and the answer by Dan Dascalescu.
(GUI method may not work in special cases)