User Tools

Site Tools


hardware:t4220:screen-rotate

Manually rotation screen

The following procedures allow to rotate the screen by means of two different methods:

  1. choose of the orientation, independently from the current one;
  2. clockwise rotation at intervals of $\frac{\pi}{2}$ .

The maximum resolution supported by Fujitsu Lifebook T4220 monitor is 1400×1050 px.

However, when the monitor is rotated in the portrait format, this resolution is no longer supported and it's necessary to come down at 1024×768 px.

Packages required

From Synaptic, install the packages::

  • xserver-xorg-input-synaptics
  • xserver-xorg-input-wacom

After that, restart the PC.

Get pointing system name

So that pointing systems, also called Virtual core pointer, are coordinated with the monitor rotation, it's necessary to refer to them, in the configuration script, with their right name.

To list them, give the command:

xinput

Script for rotate the monitor

Save in /usr/local/bin/rotate-screen/ the following two scripts:

gui.py
#!/usr/bin/env python3
 
import tkinter as tk
import rotate
 
 
class App(tk.Frame, rotate.Rotate):
    def __init__(self, master=None):
        """
        Initialize the class.
        """
        tk.Frame.__init__(self, master)
        self.grid()
        self.createWidgets()
        self.color_std = tk.Button.cget(self, "bg")  # get default colour name
        self.status(self.color_std)
 
    def createWidgets(self):
        """
        Create the buttons to choose the direction.
        """
        self.button_n = tk.Button(self)
        self.button_n["text"] = "NORMAL"
        self.button_n["command"] = self.call_normal
        self.button_n.grid(row=1, column=1, sticky=tk.E+tk.W)
 
        self.button_l = tk.Button(self)
        self.button_l["text"] = "LEFT"
        self.button_l["command"] = self.call_left
        self.button_l.grid(row=2, column=1, sticky=tk.E+tk.W)
 
        self.button_r = tk.Button(self)
        self.button_r["text"] = "RIGHT"
        self.button_r["command"] = self.call_right
        self.button_r.grid(row=3, column=1, sticky=tk.E+tk.W)
 
        self.button_i = tk.Button(self)
        self.button_i["text"] = "INVERTED"
        self.button_i["command"] = self.call_inverted
        self.button_i.grid(row=4, column=1, sticky=tk.E+tk.W)
 
    def status(self, color_std):
        """
        Depending on the current direction, highlight the relative button.
        """
        cur_dir = rotate.get_direction()
        if (cur_dir == "normal"):
            self.button_n["bg"] = "light green"
            self.button_l["bg"] = color_std
            self.button_r["bg"] = color_std
            self.button_i["bg"] = color_std
        elif (cur_dir == "left"):
            self.button_n["bg"] = color_std
            self.button_l["bg"] = "light green"
            self.button_r["bg"] = color_std
            self.button_i["bg"] = color_std
        elif (cur_dir == "right"):
            self.button_n["bg"] = color_std
            self.button_l["bg"] = color_std
            self.button_r["bg"] = "light green"
            self.button_i["bg"] = color_std
        elif (cur_dir == "inverted"):
            self.button_n["bg"] = color_std
            self.button_l["bg"] = color_std
            self.button_r["bg"] = color_std
            self.button_i["bg"] = "light green"
 
    def call_normal(self):
        """
        Invoke the methods that rotate the screen to normal.
        """
        cur_dir = rotate.get_direction()
        new_dir = "normal"
        rotate.choose(cur_dir, new_dir)
        self.status(self.color_std)
 
    def call_left(self):
        """
        Invoke the methods that rotate the screen to left.
        """
        cur_dir = rotate.get_direction()
        new_dir = "left"
        rotate.choose(cur_dir, new_dir)
        self.status(self.color_std)
 
    def call_right(self):
        """
        Invoke the methods that rotate the screen to right.
        """
        cur_dir = rotate.get_direction()
        new_dir = "right"
        rotate.choose(cur_dir, new_dir)
        self.status(self.color_std)
 
    def call_inverted(self):
        """
        Invoke the methods that rotate the screen to upside-down.
        """
        cur_dir = rotate.get_direction()
        new_dir = "inverted"
        rotate.choose(cur_dir, new_dir)
        self.status(self.color_std)
 
 
if __name__ == '__main__':
    root = tk.Tk()
    rotate = rotate.Rotate()
    app = App(master=root)
    app.master.title("")
    app.mainloop()
rotate.py
#!/usr/bin/env python3
 
import subprocess
import re
 
 
STYLUS = "Serial Wacom Tablet FUJ02e5 stylus"
TOUCH = "SynPS/2 Synaptics TouchPad"
RES_1 = "1400x1050"
RES_2 = "1024x768"
CMT_N = ["1", "0", "0", "0", "1", "0", "0", "0", "1"]
CMT_L = ["0", "-1", "1", "1", "0", "0", "0", "0", "1"]
CMT_R = ["0", "1", "0", "-1", "0", "1", "0", "0", "1"]
CMT_I = ["-1", "0", "1", "0", "-1", "1", "0", "0", "1"]
 
 
class Rotate(object):
 
    def get_direction(self):
        """
        Extract the actual direction of the screen.
 
        executing the bash command:
            xrandr | grep ' connected'
        we get a line of the form:
            "LVDS1 connected 1400x1050+0+0 <direction> 
            (normal left inverted right x axis y axis) 
            0mm x 0mm"
        where:
            <direction> = "left"|"inverted"|"right"
        but if:
            <direction> = ""
        the screen is in "normal"
 
        OUTPUT
        cur_dir: (str) the name of the current direction among
            "normal"|"left"|"inverted"|"right"
        """
        line = subprocess.check_output("xrandr | grep ' connected'",
                                       shell=True)
 
        p = re.compile(r'(left|right|inverted)(?=\s\()')
        m = p.search(str(line))
 
        if m:
            cur_dir = str(m.group())
        else:
            cur_dir = "normal"
 
        return cur_dir
 
    def choose(self, cur_dir, new_dir):
        """
        Depending on the current direction and the new direction, call the
        appropriate function which sets the new direction of the screen.
 
        INPUT
        cur_dir: (str) the name of the current direction among
            "normal"|"left"|"inverted"|"right"
        new_dir: (str) the name of the new direction among
            "normal"|"left"|"inverted"|"right"
        """
        if (cur_dir == "normal"):
            if (new_dir == "left"):
                self.set_left()
            elif (new_dir == "right"):
                self.set_right()
            elif (new_dir == "inverted"):
                self.set_inverted()
        if (cur_dir == "left"):
            if (new_dir == "normal"):
                self.set_normal()
            elif (new_dir == "right"):
                self.set_right()
            elif (new_dir == "inverted"):
                self.set_inverted()
        elif (cur_dir == "right"):
            if (new_dir == "normal"):
                self.set_normal()
            elif (new_dir == "left"):
                self.set_left()
            elif (new_dir == "inverted"):
                self.set_inverted()
        elif (cur_dir == "inverted"):
            if (new_dir == "normal"):
                self.set_normal()
            elif (new_dir == "left"):
                self.set_left()
            elif (new_dir == "right"):
                self.set_right()
 
    def set_normal(self):
        """
        Rotate the screen to normal.
        """
        subprocess.call(["xrandr", "-o", "normal"])
        subprocess.call(["xrandr", "-s", RES_1])
        subprocess.call(["xsetwacom", "set", STYLUS, "Rotate", "none"])
        subprocess.call(["xinput", "set-prop", TOUCH,
                         "Coordinate Transformation Matrix"] + CMT_N)
 
    def set_left(self):
        """
        Rotate the screen to left.
 
        In this direction, the screen can support only 1024x768 resolution.
        """
        subprocess.call(["xrandr", "-o", "left"])
        subprocess.call(["xrandr", "-s", RES_2])
        subprocess.call(["xsetwacom", "set", STYLUS, "Rotate", "ccw"])
        subprocess.call(["xinput", "set-prop", TOUCH,
                         "Coordinate Transformation Matrix"] + CMT_L)
 
    def set_right(self):
        """
        Rotate the screen to right.
 
        In this direction, the screen can support only 1024x768 resolution.
        """
        subprocess.call(["xrandr", "-o", "right"])
        subprocess.call(["xrandr", "-s", RES_2])
        subprocess.call(["xsetwacom", "set", STYLUS, "Rotate", "cw"])
        subprocess.call(["xinput", "set-prop", TOUCH,
                         "Coordinate Transformation Matrix"] + CMT_R)
 
    def set_inverted(self):
        """
        Rotate the screen upside-down.
        """
        subprocess.call(["xrandr", "-o", "inverted"])
        subprocess.call(["xrandr", "-s", RES_1])
        subprocess.call(["xsetwacom", "set", STYLUS, "Rotate", "half"])
        subprocess.call(["xinput", "set-prop", TOUCH,
                         "Coordinate Transformation Matrix"] + CMT_I)
 
    def bezel_button(self):
        """
        Depending on the current direction, perform rotation in counter
        clock wise in the followin order:
            left
            inverted
            right
            normal
        """
        cur_dir = self.get_direction()
        if (cur_dir == "normal"):
            self.set_left()
        elif (cur_dir == "left"):
            self.set_inverted()
        elif (cur_dir == "inverted"):
            self.set_right()
        elif (cur_dir == "right"):
            self.set_normal()
 
 
if __name__ == '__main__':
    rotate = Rotate()
    rotate.bezel_button()

Connect the script to a panel button

Add a launcher in the panel as follows:

Command python3 gui.py
Working Directory /usr/local/bin/rotate-screen

Connect the script to a bezel button

Bezel buttons are those ones around the monitor frame. Hereafter the steps to associate an action to a given bezel button using its keycode.

  1. Run the command
    xev
  2. Hit the button to use for the monitor rotation and write down its keycode.
    Figure 1: Bezel button choosen for the monitor rotation and relative output of the command xev.
  3. Edit the file /usr/share/X11/xkb/symbols/inet, replacing the lines:
    //  key <I161>   {      [ ]       }; // KEY_DIRECTION
        key <I162>   {      [ XF86RotateWindows     ]       };

    in:

        key <I161>   {      [ XF86RotateWindows     ]       };
    //  key <I161>   {      [ ]       }; // KEY_DIRECTION
    //  key <I162>   {      [ XF86RotateWindows     ]       };
  4. Eventually clean the cache in /var/lib/xkb/*.xkm.
  5. In Settings > Keyboard > Application Shortcuts, add a new shortcut:
    Shortcut XF86RotateWindows
    Command python3 /usr/local/bin/rotate-screen/rotate.py
  1. The original guide: forum.thinkpads.com
  2. List of symbolic codes: keytouch.sourceforge.net
hardware/t4220/screen-rotate.txt · Last modified: 2020/09/20 12:52 by tormec