From edcd59be26015cd971b9f09d092c74087c8d8a33 Mon Sep 17 00:00:00 2001 From: Sören Tempel Date: Sat, 22 May 2021 01:20:11 +0200 Subject: Generalize support for termios-keybindings --- saneterm/input.py | 15 ++++++++++++--- saneterm/terminal.py | 16 +++++++++------- saneterm/termview.py | 12 +++++++++--- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/saneterm/input.py b/saneterm/input.py index 7ca3e54..29658c3 100644 --- a/saneterm/input.py +++ b/saneterm/input.py @@ -10,7 +10,6 @@ class KeyBindings(): bind "u" { "kill-after-output" () }; bind "a" { "move-input-start" () }; bind "e" { "move-input-end" () }; - bind "c" { "interrupt" () }; } * { @@ -18,11 +17,21 @@ class KeyBindings(): } """ - def __init__(self): + def __init__(self, widget): self.provider = Gtk.CssProvider() self.provider.load_from_data(self.stylesheet) - def apply(self, widget): style_ctx = widget.get_style_context() style_ctx.add_provider(self.provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) + + def add_bind(self, key, signal, arg): + bindings = self.__binding_set() + # Using Gtk.BindingEntry.add_signall() would be preferable + # https://gitlab.gnome.org/GNOME/pygobject/-/issues/474 + Gtk.BindingEntry().add_signal_from_string(bindings, + F'bind "{key}" {{ "{signal}" ({arg}) }};') + + def __binding_set(self): + return Gtk.BindingSet.find("saneterm-key-bindings") + diff --git a/saneterm/terminal.py b/saneterm/terminal.py index 8e929de..bc19a88 100644 --- a/saneterm/terminal.py +++ b/saneterm/terminal.py @@ -11,6 +11,7 @@ import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk +from gi.repository import Gdk from gi.repository import GLib NAME = "saneterm" @@ -64,11 +65,11 @@ class Terminal(Gtk.Window): # Block-wise reading from the PTY requires an incremental decoder. self.decoder = codecs.getincrementaldecoder('UTF-8')() - bindings = input.KeyBindings() - bindings.apply(self.termview) - self.termview.connect("new-user-input", self.user_input) - self.termview.connect("interrupt", self.interrupt) + self.termview.connect("termios-ctrlkey", self.termios_ctrl) + + bindings = input.KeyBindings(self.termview) + bindings.add_bind("c", "termios-ctrlkey", termios.VINTR) self.add(self.termview) @@ -88,10 +89,11 @@ class Terminal(Gtk.Window): def user_input(self, termview, line): os.write(self.pty.master, line.encode("UTF-8")) - def interrupt(self, termview): + def termios_ctrl(self, termview, cidx): + # TODO: Employ some heuristic to cache tcgetattr result. cc = termios.tcgetattr(self.pty.master)[-1] - os.write(self.pty.master, cc[termios.VINTR]) + os.write(self.pty.master, cc[cidx]) # XXX: Clear line-based buffer here (i.e. update the # marks in TermView) in case the application doesn't - # write anything to the PTY on receiving VINTR. + # write anything to the PTY on receiving the CC. diff --git a/saneterm/termview.py b/saneterm/termview.py index 079939b..0728a83 100644 --- a/saneterm/termview.py +++ b/saneterm/termview.py @@ -21,6 +21,10 @@ class TermView(Gtk.TextView): where data from the backend source was last written. While the _last_mark constitues the position where user input would be added. + + Control characters are not line-buffered. Instead these are + intercepted through pre-defined key bindings by Gtk and communicated + to the application via the termios-ctrlkey signal. """ def __init__(self): @@ -37,7 +41,6 @@ class TermView(Gtk.TextView): "kill-after-output": self.__kill_after_output, "move-input-start": self.__move_input_start, "move-input-end": self.__move_input_end, - "interrupt": None } for signal in signals.items(): @@ -46,8 +49,11 @@ class TermView(Gtk.TextView): GObject.SIGNAL_ACTION, GObject.TYPE_NONE, ()) - if not func is None: - self.connect(name, func) + self.connect(name, func) + + GObject.signal_new("termios-ctrlkey", self, + GObject.SIGNAL_ACTION, GObject.TYPE_NONE, + (GObject.TYPE_LONG,)) GObject.signal_new("new-user-input", self, GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, -- cgit 1.4.1