restore KeyCodesFilter, somehow dropped during refactoring

This commit is contained in:
Michael Hudson-Doyle 2020-09-17 11:18:52 +12:00
parent 01e9c04ab5
commit 19178979fc
2 changed files with 121 additions and 0 deletions

View File

@ -31,6 +31,7 @@ from subiquitycore.async_helpers import (
run_in_thread, run_in_thread,
schedule_task, schedule_task,
) )
from subiquitycore.screen import is_linux_tty
from subiquitycore.tuicontroller import Skip from subiquitycore.tuicontroller import Skip
from subiquitycore.tui import TuiApplication from subiquitycore.tui import TuiApplication
from subiquitycore.snapd import ( from subiquitycore.snapd import (
@ -45,6 +46,10 @@ from subiquity.common.errorreport import (
ErrorReportKind, ErrorReportKind,
) )
from subiquity.journald import journald_listener from subiquity.journald import journald_listener
from subiquity.keycodes import (
DummyKeycodesFilter,
KeyCodesFilter,
)
from subiquity.lockfile import Lockfile from subiquity.lockfile import Lockfile
from subiquity.models.subiquity import SubiquityModel from subiquity.models.subiquity import SubiquityModel
from subiquity.ui.frame import SubiquityUI from subiquity.ui.frame import SubiquityUI
@ -124,6 +129,11 @@ class Subiquity(TuiApplication):
if not opts.bootloader == 'none' and platform.machine() != 's390x': if not opts.bootloader == 'none' and platform.machine() != 's390x':
self.controllers.remove("Zdev") self.controllers.remove("Zdev")
if is_linux_tty():
self.input_filter = KeyCodesFilter()
else:
self.input_filter = DummyKeycodesFilter()
self.journal_fd, self.journal_watcher = journald_listener( self.journal_fd, self.journal_watcher = journald_listener(
["subiquity"], self.subiquity_event, seek=True) ["subiquity"], self.subiquity_event, seek=True)
self.help_menu = HelpMenu(self) self.help_menu = HelpMenu(self)
@ -266,6 +276,9 @@ class Subiquity(TuiApplication):
super().new_event_loop() super().new_event_loop()
self.aio_loop.add_reader(self.journal_fd, self.journal_watcher) self.aio_loop.add_reader(self.journal_fd, self.journal_watcher)
def extra_urwid_loop_args(self):
return dict(input_filter=self.input_filter.filter)
def run(self): def run(self):
try: try:
if self.opts.autoinstall is not None: if self.opts.autoinstall is not None:

108
subiquity/keycodes.py Normal file
View File

@ -0,0 +1,108 @@
# Copyright 2020 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import fcntl
import logging
import os
import struct
import sys
log = logging.getLogger('subiquity.keycodes')
# /usr/include/linux/kd.h
K_RAW = 0x00
K_XLATE = 0x01
K_MEDIUMRAW = 0x02
K_UNICODE = 0x03
K_OFF = 0x04
KDGKBMODE = 0x4B44 # gets current keyboard mode
KDSKBMODE = 0x4B45 # sets current keyboard mode
class KeyCodesFilter:
"""input_filter that can pass (medium) raw keycodes to the application
See http://lct.sourceforge.net/lct/x60.html for terminology.
Call enter_keycodes_mode()/exit_keycodes_mode() to switch into and
out of keycodes mode. In keycodes mode, the only events passed to
the application are "press $N" / "release $N" where $N is the
keycode the user pressed or released.
Much of this is cribbed from the source of the "showkeys" utility.
"""
def __init__(self):
self._fd = os.open("/proc/self/fd/"+str(sys.stdin.fileno()), os.O_RDWR)
self.filtering = False
def enter_keycodes_mode(self):
log.debug("enter_keycodes_mode")
self.filtering = True
# Read the old keyboard mode (it will proably always be K_UNICODE but
# well).
o = bytearray(4)
fcntl.ioctl(self._fd, KDGKBMODE, o)
self._old_mode = struct.unpack('i', o)[0]
# Set the keyboard mode to K_MEDIUMRAW, which causes the keyboard
# driver in the kernel to pass us keycodes.
fcntl.ioctl(self._fd, KDSKBMODE, K_MEDIUMRAW)
def exit_keycodes_mode(self):
log.debug("exit_keycodes_mode")
self.filtering = False
fcntl.ioctl(self._fd, KDSKBMODE, self._old_mode)
def filter(self, keys, codes):
# Luckily urwid passes us the raw results from read() we can
# turn into keycodes.
if self.filtering:
i = 0
r = []
n = len(codes)
while i < len(codes):
# This is straight from showkeys.c.
if codes[i] & 0x80:
p = 'release '
else:
p = 'press '
if i + 2 < n and (codes[i] & 0x7f) == 0:
if (codes[i + 1] & 0x80) != 0:
if (codes[i + 2] & 0x80) != 0:
kc = (((codes[i + 1] & 0x7f) << 7) |
(codes[i + 2] & 0x7f))
i += 3
else:
kc = codes[i] & 0x7f
i += 1
r.append(p + str(kc))
return r
else:
return keys
class DummyKeycodesFilter:
# A dummy implementation of the same interface as KeyCodesFilter
# we can use when not running in a linux tty.
def enter_keycodes_mode(self):
pass
def exit_keycodes_mode(self):
pass
def filter(self, keys, codes):
return keys