Skip to content

Commit

Permalink
Merge branch 'release/4.5.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
martin-ueding committed Oct 15, 2015
2 parents f6d78df + 15e05dc commit 247d601
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 14 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
Changelog
#########

v4.5.0
Released: 2015-10-15 16:50:57 +0200

- Add ``chvt`` workaround as suggested by Cody Christensen.

v4.4.2
Released: 2015-07-31 15:14:59 +0200

Expand Down
35 changes: 29 additions & 6 deletions doc/man/thinkpad-rotate.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,38 @@ You can set the following option:

``rotate.xrandr_bug_workaround``
On Ubuntu 15.04, XRandr has `a bug`__ which turns the screen black when
rotating with no external screen attached. If you set this to ``true``, the
rotate hook will only act when an external screen is attached. In the cases
you want to rotate the screen without external screens do the following:
Call ``thinkpad-rotate``, the screen will turn black. Then go to another
terminal with [Ctrl][Alt][F1] and back to the graphical one with
[Ctrl][Alt][F7]. *Default: false*.
rotating with no external screen attached.

__ https://bugs.launchpad.net/ubuntu/+source/x11-xserver-utils/+bug/1451798

This is problematic when the rotation is executed from a hardware event
hook. Then the screen is physically laying on the keyboard and one cannot
do anything. A workaround is to go to another terminal with [Ctrl][Alt][F1]
and back to the graphical one with [Ctrl][Alt][F7].

As contributed by Cody Christensen, that can be automated with ``chvt``.
This way the hook will work in a useful way for users with that XRandr bug.
However, this program needs superuser privileges. One can use ``sudo`` to
allow oneself to call this program without a password entry. Add the
following line in a file like ``/etc/sudoers.d/chvt``::

myuser ALL = NOPASSWD: /bin/chvt

Replace ``myuser`` with your username! Then check with ``visudo -c``
whether the syntax is fine.

|project| can figure out whether this line is implemented by querying
``sudo -l`` for a list of available commands with higher privileges. If you
set this option to ``true`` and the line is configured, it will call ``chvt
6; chvt 7`` after the rotation and before the hook.

If ``chvt`` cannot be used, the hook will be disabled by enabling this
option. That way you can manually rotate the contents of the display with
``thinkpad-rotate``, press [Ctrl][Alt][F1] and [Ctrl][Alt][F7] and only
then physically rotate the screen. The hook will not fire and rotate back.

*Default: false*.

``screen.internal``
The ``xrandr`` name for the internal monitor. *Default: LVDS1*

Expand Down
82 changes: 74 additions & 8 deletions tps/rotate.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def main():
config = tps.config.get_config()

if options.via_hook:
xrandr_bug_workaround(config)
xrandr_bug_fail_early(config)

try:
new_direction = new_rotation(
Expand Down Expand Up @@ -86,6 +86,9 @@ def rotate_to(direction, config):
logger.info('TouchPad was not found, could not be (de)activated.')
logger.debug('Exception was: “%s”', str(e))

if needs_xrandr_bug_workaround(config) and can_use_chvt():
toggle_virtual_terminal()

tps.hooks.postrotate(direction, config)


Expand All @@ -112,20 +115,83 @@ def new_rotation(current, desired_str, config):
return new


def xrandr_bug_workaround(config):
def can_use_chvt():
'''
Checks whether ``chvt`` can be called with ``sudo`` without a password.
The ``sudo`` command has the ``-n`` option which will just make the command
fail when the user does not have the appropriate permissions. The problem
with ``chvt`` is that it does not have any intelligent command line
argument parsing. If will return code 1 if no argument is given, the same
code that ``sudo`` gives when no permission is available. Therefore I chose
to use ``sudo -l` to get the whole list and see whether the full path to
``chvt`` is in there. This might break on Fedora where the ``usr``-merge
has been done now.
The following line is needed in a file like ``/etc/sudoers.d/chvt``::
myuser ALL = NOPASSWD: /bin/chvt
You have to replace ``myuser`` which your username. Giving too broad
permissions to every other user account is probably not a good idea.
:rtype: bool
'''
command = ['sudo', '-l']
output = tps.check_output(command, logger)

return b'/bin/chvt' in output


def toggle_virtual_terminal():
'''
'''
assert can_use_chvt()
tps.check_call(['sudo', '-n', 'chvt', '6'], logger)
tps.check_call(['sudo', '-n', 'chvt', '7'], logger)


def has_external_screens(config):
'''
Checks whether any external screens are attached.
'''
XRandr has a `bug in Ubuntu`__, maybe even in other distributions. This
functions will abort if there is no external screen attached.
externals = tps.screen.get_externals(config['screen']['internal'])
return len(externals) > 0


def needs_xrandr_bug_workaround(config):
'''
Determines whether xrandr bug needs to be worked around.
XRandr has a `bug in Ubuntu`__, maybe even in other distributions. In
Ubuntu 15.04 a workaround is to change the virtual terminal to a different
one and back to the seventh, the graphical one. This can be automated using
the ``chvt`` command which requires superuser privileges. An entry in the
sudo file can let the normal user execute this program.
__ https://bugs.launchpad.net/ubuntu/+source/x11-xserver-utils/+bug/1451798
'''
# Do nothing if workaround is not requested.
if not config['rotate'].getboolean('xrandr_bug_workaround'):
return
return False

externals = tps.screen.get_externals(config['screen']['internal'])
if (len(externals) == 0):
logger.debug('xrandr bug workaround requested')

# Do nothing if an external screen is attached. The bug does not appear
# then.
if has_external_screens(config):
return False

return True


def xrandr_bug_fail_early(config):
'''
Quits the program if xrandr bug cannot be coped with.
'''
if needs_xrandr_bug_workaround(config) and not can_use_chvt():
logger.warning('Aborting since there are no external screens attached '
'and XRandr bug workout is enabled.')
'and XRandr bug workaround is enabled.')
sys.exit(1)


Expand Down

0 comments on commit 247d601

Please sign in to comment.