User Tools

Site Tools


hardware:t4220:screen-rotate

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
hardware:t4220:screen-rotate [2021/04/29 09:16] – [Manually rotation screen] tormechardware:t4220:screen-rotate [2023/05/28 16:37] (current) – external edit 127.0.0.1
Line 1: Line 1:
 +====== Manually rotation screen ======
 +
 +The following procedures allow to rotate the screen by means of two different methods:
 +  - choose of the orientation, independently from the current one;
 +  - clockwise rotation at intervals of 90°.
 +
 +<WRAP important>
 +**Important:** the maximum resolution supported by Fujitsu Lifebook T4220 monitor is 1400x1050 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 1024x768 px.
 +</WRAP>
 +
 +
 +===== 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:
 +
 +<code bash>
 +xinput
 +</code>
 +
 +
 +===== Script for rotate the monitor =====
 +
 +Save in ''/usr/local/bin/rotate-screen/'' the following two scripts:  
 +
 +<code python 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()
 +
 +</code> <code python 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()
 +
 +</code>
 +
 +
 +===== 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''.
 +
 +  - Run the command <code bash>
 +xev
 +</code>
 +  - Hit the button to use for the monitor rotation and write down its ''keycode''.<WRAP>
 +<figure>
 +{{ hardware:t4220:keyboard-bezel-button.png?nolink |}}
 +<caption>
 +Bezel button choosen for the monitor rotation and relative output of the command ''xev''.</caption>
 +</figure>
 +</WRAP>
 +  - Edit the file ''/usr/share/X11/xkb/symbols/inet'', replacing the lines:<code>
 +//  key <I161>        [ ]       }; // KEY_DIRECTION
 +    key <I162>        [ XF86RotateWindows           };
 +</code> in: <code>
 +    key <I161>        [ XF86RotateWindows           };
 +//  key <I161>        [ ]       }; // KEY_DIRECTION
 +//  key <I162>        [ XF86RotateWindows           };
 +</code>
 +  - Eventually clean the cache in ''/var/lib/xkb/*.xkm''.
 +  - In ''Settings'' > ''Keyboard'' > ''Application Shortcuts'', add a new shortcut: <WRAP>
 +^ Shortcut | ''XF86RotateWindows'' |
 +^ Command | ''python3 /usr/local/bin/rotate-screen/rotate.py'' |
 +</WRAP>
 +
 +
 +===== Links =====
 +
 +  - The original guide: [[http://forum.thinkpads.com/viewtopic.php?t=108785| forum.thinkpads.com]]
 +  - List of symbolic codes: [[http://keytouch.sourceforge.net/howto_keyboard.html| keytouch.sourceforge.net]]
 +
  
hardware/t4220/screen-rotate.txt · Last modified: 2023/05/28 16:37 by 127.0.0.1