#!/usr/bin/python # -*- coding: utf-8 -*- import os import gi gi.require_version("Gtk", "3.0") gi.require_version('Vte', '2.91') from gi.repository import Gtk, Gdk, GLib, Vte class IntegratedTerminal(Gtk.Window): def __init__(self, command, title=None, shell=None, cwd=None, width=600, height=500): try: self.terminal=Vte.Terminal() self.terminal.set_scrollback_lines(-1) # Setting the font doesn't work for some reason, let's leave it # self.terminal.set_font(Pango.FontDescription(string='Monospace')) self.command = command self.ready = False self.output_handler = self.terminal.connect("cursor-moved", self.on_cursor_moved) # apparently Vte.Terminal.spawn_sync() is deprecated in favour of # the non-existent Vte.Terminal.spawn_async()... self.terminal.spawn_sync( Vte.PtyFlags.DEFAULT, # pty_flags cwd or os.environ.get("HOME"), # working_directory shell or [os.environ.get("SHELL")], # argv [], # envv GLib.SpawnFlags.DO_NOT_REAP_CHILD, # spawn_flags None, # child_setup None, # child_setup_data None # cancellable ) except Exception as e: self.close() raise Exception(e) Gtk.Window.__init__(self, title=title or "mintMenu Integrated Terminal") self.set_icon_name("utilities-terminal") box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.scrolled_window = Gtk.ScrolledWindow() self.scrolled_window.set_hexpand(True) self.scrolled_window.set_vexpand(True) self.scrolled_window.set_size_request(width, height) self.scrolled_window.add(self.terminal) box.pack_start(self.scrolled_window, False, True, 0) self.add(box) self.connect("key-press-event", self.on_key_press_event) self.terminal.connect("child-exited", self.exit) self.terminal.connect("eof", self.exit) self.terminal.set_rewrap_on_resize(True) def on_cursor_moved(self, terminal): if not self.ready: # we have to run the command on a callback because the # spawn_sync() method doesn't wait for the shell to load, # so instead we have to wait for the shell to create a prompt self.ready = True # if we don't show the terminal once after this it won't # always receive output, so we show it and hide it again right # away. Whatever works... self.show_all() self.hide() # Now we can go: command = "%s\n" % self.command self.terminal.feed_child(command, len(command)) return # Unfortunately we cannot guarantee that the command is on the first # line (the shell may display somethinge else first), so we have to # search for it: x, y = terminal.get_cursor_position() contents, dummy = terminal.get_text_range(0, 0, x, y, None, None) lines = contents.split("\n") prefix = "" command_found = False for line in lines: if not line: continue if command_found: if not line.lstrip(prefix): # we got a command prompt, exit self.exit() return # the command generated output, show the terminal terminal.disconnect(self.output_handler) self.show_all() return if self.command in line: # this is our command line prefix = line.split(self.command, 1)[0] command_found = True def on_key_press_event(self, widget, event): if event.keyval == Gdk.KEY_Escape: self.exit() def exit(self, *args): # Gtk.main_quit() self.close()