* Fix the DBus interface (gtk2-gtk3)

* Disable the wm_class feature. Seems not possible in GTK3, and breaks
  the DBus call for new_window.
(trunk-1651)
* As part of GTK3 fixup, some improvements to the DBus interface, and
  remotinator (Steve Boddy)
  * Can now open a window or tab using remotinator
  * Can get the window uuid, or title using remotinator
  * Moved new tab key handling into the terminal for consistency
  * Standardise response when a new term is created (split, win
    or tab) to reply with new terms uuid
  * For GTK3 gave the DBus a slightly different name so they it
    can run at same time as GTK2
  * remotinator now uses argparse for commandline option handling,
    vastly improving the option handling
  * remotinator help strings are translatable now
This commit is contained in:
Stephen Boddy 2015-09-19 05:25:25 +02:00
parent e4c235dcd6
commit 7b769ae1d8
5 changed files with 154 additions and 70 deletions

View File

@ -20,6 +20,7 @@
import os import os
import sys import sys
import argparse
from terminatorlib.util import dbg, err from terminatorlib.util import dbg, err
try: try:
@ -27,40 +28,60 @@ try:
except ImportError: except ImportError:
err('Unable to initialise Terminator remote library. This probably means dbus is not available') err('Unable to initialise Terminator remote library. This probably means dbus is not available')
sys.exit(1) sys.exit(1)
from terminatorlib.translation import _
APP_NAME='remotinator' APP_NAME='remotinator'
APP_VERSION='0.97' APP_VERSION='0.97'
COMMANDS={ COMMANDS={
'hsplit': ['terminal_hsplit', 'Split the current terminal horizontally'], # Command uuid req. Description
'vsplit': ['terminal_vsplit', 'Split the current terminal vertically'], 'new_window': [False, _('Open a new window')],
'terminals': ['get_terminals', 'Get a list of all terminals'], 'new_tab': [True, _('Open a new tab')],
'terminal_tab': ['get_terminal_tab', 'Get the UUID of a parent tab'], 'hsplit': [True, _('Split the current terminal horizontally')],
'terminal_tab_title': ['get_terminal_tab_title', 'Get the title of a parent tab'], 'vsplit': [True, _('Split the current terminal vertically')],
'get_terminals': [False, _('Get a list of all terminals')],
'get_window': [True, _('Get the UUID of a parent window')],
'get_window_title': [True, _('Get the title of a parent window')],
'get_tab': [True, _('Get the UUID of a parent tab')],
'get_tab_title': [True, _('Get the title of a parent tab')],
} }
if __name__ == '__main__': if __name__ == '__main__':
dbg ("%s starting up, version %s" % (APP_NAME, APP_VERSION)) dbg ("%s starting up, version %s" % (APP_NAME, APP_VERSION))
if sys.argv[0].split('/')[-1] == APP_NAME: command_desc=''
if len(sys.argv) <2 or sys.argv[1] in ['-h', '--help']: for command in sorted(COMMANDS.keys()):
print "Usage: %s COMMAND" % sys.argv[0] command_desc += " %-*s %s %s\n" % (max([len(x) for x in COMMANDS.keys()]),
print " Commands:" command,
for command in COMMANDS: COMMANDS[command][0] and '*' or ' ',
print " %s : %s" % (command, COMMANDS[command][1]) COMMANDS[command][1])
sys.exit(1)
command = sys.argv[1] # Parse args
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
usage='%(prog)s command [options]',
description=_('Run one of the following Terminator DBus commands:\n\n%s') % (command_desc),
epilog=_('* These entries require either TERMINATOR_UUID environment var,\n or the --uuid option must be used.'))
parser.add_argument('-u', '--uuid', dest='uuid', type=str, metavar='UUID', default=argparse.SUPPRESS,
help=_('Terminal UUID for when not in env var TERMINATOR_UUID'))
parser.add_argument('command', type=str, nargs=1, choices=sorted(COMMANDS.keys()),
help=argparse.SUPPRESS)
parser.add_argument('-v', '--version', action='version', version='%%(prog)s %s' %(APP_VERSION))
options = vars(parser.parse_args()) # Straight to dict
# Pull out the command
command = options['command'][0]
del options['command']
func = getattr(ipc, command)
uuid_required = COMMANDS[command][0]
if uuid_required:
uuid = options.get('uuid', os.environ.get('TERMINATOR_UUID'))
if uuid:
func(uuid, options)
else: else:
command = sys.argv[0].split('/')[-1] err("$TERMINATOR_UUID is not set, or passed as an option.")
if not command in COMMANDS or not hasattr(ipc, COMMANDS[command][0]):
err("Unknown command: %s" % command)
sys.exit(1) sys.exit(1)
else:
if not os.environ.has_key('TERMINATOR_UUID'): func(options)
err("$TERMINATOR_UUID is not set. Are you definitely running inside Terminator?")
sys.exit(1)
func = getattr(ipc, COMMANDS[command][0])
func(os.environ['TERMINATOR_UUID'])

View File

@ -70,13 +70,11 @@ if __name__ == '__main__':
# being False will cause us to continue without the dbus server and open a # being False will cause us to continue without the dbus server and open a
# window. # window.
try: try:
dbg('dbus not fixed for GTK3 yet')
raise ImportError
if OPTIONS.nodbus: if OPTIONS.nodbus:
dbg('dbus disabled by command line') dbg('dbus disabled by command line')
raise ImportError raise ImportError
from terminatorlib import ipc from terminatorlib import ipc
from gi import DBus # VERIFY FOR GTK3 import dbus
try: try:
dbus_service = ipc.DBusService() dbus_service = ipc.DBusService()
except ipc.DBusException: except ipc.DBusException:
@ -91,13 +89,13 @@ if __name__ == '__main__':
if val == True: if val == True:
val = 'True' val = 'True'
optionslist[opt] = val and '%s'%val or '' optionslist[opt] = val and '%s'%val or ''
optionslist = DBus.Dictionary(optionslist, signature='ss') # VERIFY FOR GTK3 optionslist = dbus.Dictionary(optionslist, signature='ss')
if OPTIONS.new_tab: if OPTIONS.new_tab:
dbg('Requesting a new tab') dbg('Requesting a new tab')
ipc.new_tab(optionslist) ipc.new_tab_cmdline(optionslist)
else: else:
dbg('Requesting a new window') dbg('Requesting a new window')
ipc.new_window(optionslist) ipc.new_window_cmdline(optionslist)
sys.exit() sys.exit()
except ImportError: except ImportError:
dbg('dbus not imported') dbg('dbus not imported')

View File

@ -3,8 +3,10 @@
# GPL v2 only # GPL v2 only
"""ipc.py - DBus server and API calls""" """ipc.py - DBus server and API calls"""
from gi.repository import Gtk from gi.repository import Gdk
from gi.repository import DBus, DBusGLib # VERIFY/FIXME FOR GTK3: DBusException?? Replace all "dbus" with "DBus" in this file, and stuff?? import dbus.service
from dbus.exceptions import DBusException
import dbus.glib
from borg import Borg from borg import Borg
from terminator import Terminator from terminator import Terminator
from config import Config from config import Config
@ -17,8 +19,8 @@ if not CONFIG['dbus']:
dbg('dbus disabled') dbg('dbus disabled')
raise ImportError raise ImportError
BUS_BASE = 'net.tenshu.Terminator' BUS_BASE = 'net.tenshu.Terminator2'
BUS_PATH = '/net/tenshu/Terminator' BUS_PATH = '/net/tenshu/Terminator2'
try: try:
# Try and include the X11 display name in the dbus bus name # Try and include the X11 display name in the dbus bus name
DISPLAY = hex(hash(Gdk.get_display())).replace('-', '_') DISPLAY = hex(hash(Gdk.get_display())).replace('-', '_')
@ -58,7 +60,7 @@ class DBusService(Borg, dbus.service.Object):
self.terminator = Terminator() self.terminator = Terminator()
@dbus.service.method(BUS_NAME, in_signature='a{ss}') @dbus.service.method(BUS_NAME, in_signature='a{ss}')
def new_window(self, options=dbus.Dictionary()): def new_window_cmdline(self, options=dbus.Dictionary()):
"""Create a new Window""" """Create a new Window"""
dbg('dbus method called: new_window with parameters %s'%(options)) dbg('dbus method called: new_window with parameters %s'%(options))
oldopts = self.terminator.config.options_get() oldopts = self.terminator.config.options_get()
@ -68,7 +70,7 @@ class DBusService(Borg, dbus.service.Object):
self.terminator.layout_done() self.terminator.layout_done()
@dbus.service.method(BUS_NAME, in_signature='a{ss}') @dbus.service.method(BUS_NAME, in_signature='a{ss}')
def new_tab(self, options=dbus.Dictionary()): def new_tab_cmdline(self, options=dbus.Dictionary()):
"""Create a new tab""" """Create a new tab"""
dbg('dbus method called: new_tab with parameters %s'%(options)) dbg('dbus method called: new_tab with parameters %s'%(options))
oldopts = self.terminator.config.options_get() oldopts = self.terminator.config.options_get()
@ -78,35 +80,78 @@ class DBusService(Borg, dbus.service.Object):
window.tab_new() window.tab_new()
@dbus.service.method(BUS_NAME) @dbus.service.method(BUS_NAME)
def terminal_hsplit(self, uuid=None): def new_window(self):
"""Split a terminal horizontally, by UUID""" """Create a new Window"""
return self.terminal_split(uuid, True) terminals_before = set(self.get_terminals())
self.terminator.new_window()
terminals_after = set(self.get_terminals())
new_terminal_set = list(terminals_after - terminals_before)
if len(new_terminal_set) != 1:
"ERROR: Cannot determine the UUID of the added terminal"
else:
return new_terminal_set[0]
@dbus.service.method(BUS_NAME) @dbus.service.method(BUS_NAME)
def terminal_vsplit(self, uuid=None): def new_tab(self, uuid=None):
"""Split a terminal vertically, by UUID""" """Create a new tab"""
return self.terminal_split(uuid, False) return self.new_terminal(uuid, 'tab')
def terminal_split(self, uuid, horiz): @dbus.service.method(BUS_NAME)
def hsplit(self, uuid=None):
"""Split a terminal horizontally, by UUID"""
return self.new_terminal(uuid, 'hsplit')
@dbus.service.method(BUS_NAME)
def vsplit(self, uuid=None):
"""Split a terminal vertically, by UUID"""
return self.new_terminal(uuid, 'vsplit')
def new_terminal(self, uuid, type):
"""Split a terminal horizontally or vertically, by UUID""" """Split a terminal horizontally or vertically, by UUID"""
dbg('dbus method called: terminal_hsplit') dbg('dbus method called: %s' % type)
if not uuid: if not uuid:
return "ERROR: No UUID specified" return "ERROR: No UUID specified"
terminal = self.terminator.find_terminal_by_uuid(uuid) terminal = self.terminator.find_terminal_by_uuid(uuid)
terminals_before = set(self.get_terminals())
if not terminal: if not terminal:
return "ERROR: Terminal with supplied UUID not found" return "ERROR: Terminal with supplied UUID not found"
if horiz: elif type == 'tab':
terminal.key_new_tab()
elif type == 'hsplit':
terminal.key_split_horiz() terminal.key_split_horiz()
else: elif type == 'vsplit':
terminal.key_split_vert() terminal.key_split_vert()
else:
return "ERROR: Unknown type \"%s\" specified" % (type)
terminals_after = set(self.get_terminals())
# Detect the new terminal UUID
new_terminal_set = list(terminals_after - terminals_before)
if len(new_terminal_set) != 1:
return "ERROR: Cannot determine the UUID of the added terminal"
else:
return new_terminal_set[0]
@dbus.service.method(BUS_NAME) @dbus.service.method(BUS_NAME)
def get_terminals(self, uuid): def get_terminals(self):
"""Return a list of all the terminals""" """Return a list of all the terminals"""
return [x.uuid.urn for x in self.terminator.terminals] return [x.uuid.urn for x in self.terminator.terminals]
@dbus.service.method(BUS_NAME) @dbus.service.method(BUS_NAME)
def get_terminal_tab(self, uuid): def get_window(self, uuid=None):
"""Return the UUID of the parent window of a given terminal"""
terminal = self.terminator.find_terminal_by_uuid(uuid)
window = terminal.get_toplevel()
return window.uuid.urn
@dbus.service.method(BUS_NAME)
def get_window_title(self, uuid=None):
"""Return the title of a parent window of a given terminal"""
terminal = self.terminator.find_terminal_by_uuid(uuid)
window = terminal.get_toplevel()
return window.get_title()
@dbus.service.method(BUS_NAME)
def get_tab(self, uuid=None):
"""Return the UUID of the parent tab of a given terminal""" """Return the UUID of the parent tab of a given terminal"""
maker = Factory() maker = Factory()
terminal = self.terminator.find_terminal_by_uuid(uuid) terminal = self.terminator.find_terminal_by_uuid(uuid)
@ -116,7 +161,7 @@ class DBusService(Borg, dbus.service.Object):
return root_widget.uuid.urn return root_widget.uuid.urn
@dbus.service.method(BUS_NAME) @dbus.service.method(BUS_NAME)
def get_terminal_tab_title(self, uuid): def get_tab_title(self, uuid=None):
"""Return the title of a parent tab of a given terminal""" """Return the title of a parent tab of a given terminal"""
maker = Factory() maker = Factory()
terminal = self.terminator.find_terminal_by_uuid(uuid) terminal = self.terminator.find_terminal_by_uuid(uuid)
@ -134,38 +179,58 @@ def with_proxy(func):
func(proxy, *args, **argd) func(proxy, *args, **argd)
return _exec return _exec
@with_proxy
def new_window_cmdline(session, options):
"""Call the dbus method to open a new window"""
session.new_window_cmdline(options)
@with_proxy
def new_tab_cmdline(session, options):
"""Call the dbus method to open a new tab in the first window"""
session.new_tab_cmdline(options)
@with_proxy @with_proxy
def new_window(session, options): def new_window(session, options):
"""Call the dbus method to open a new window""" """Call the dbus method to open a new window"""
session.new_window(options) print session.new_window()
@with_proxy @with_proxy
def new_tab(session, options): def new_tab(session, uuid, options):
"""Call the dbus method to open a new tab in the first window""" """Call the dbus method to open a new tab in the first window"""
session.new_tab(options) print session.new_tab(uuid)
@with_proxy @with_proxy
def terminal_hsplit(session, uuid): def hsplit(session, uuid, options):
"""Call the dbus method to horizontally split a terminal""" """Call the dbus method to horizontally split a terminal"""
session.terminal_hsplit(uuid) print session.hsplit(uuid)
@with_proxy @with_proxy
def terminal_vsplit(session, uuid): def vsplit(session, uuid, options):
"""Call the dbus method to vertically split a terminal""" """Call the dbus method to vertically split a terminal"""
print session.terminal_vsplit(uuid) print session.vsplit(uuid)
@with_proxy @with_proxy
def get_terminals(session, uuid): def get_terminals(session, options):
"""Call the dbus method to return a list of all terminals""" """Call the dbus method to return a list of all terminals"""
print '\n'.join(session.get_terminals(uuid)) print '\n'.join(session.get_terminals())
@with_proxy @with_proxy
def get_terminal_tab(session, uuid): def get_window(session, uuid, options):
"""Call the dbus method to return the toplevel tab for a terminal""" """Call the dbus method to return the toplevel tab for a terminal"""
print session.get_terminal_tab(uuid) print session.get_window(uuid)
@with_proxy @with_proxy
def get_terminal_tab_title(session, uuid): def get_window_title(session, uuid, options):
"""Call the dbus method to return the title of a tab""" """Call the dbus method to return the title of a tab"""
print session.get_terminal_tab_title(uuid) print session.get_window_title(uuid)
@with_proxy
def get_tab(session, uuid, options):
"""Call the dbus method to return the toplevel tab for a terminal"""
print session.get_tab(uuid)
@with_proxy
def get_tab_title(session, uuid, options):
"""Call the dbus method to return the title of a tab"""
print session.get_tab_title(uuid)

View File

@ -849,8 +849,7 @@ class Terminal(Gtk.VBox):
return(False) return(False)
if mapping and mapping not in ['close_window', if mapping and mapping not in ['close_window',
'full_screen', 'full_screen']:
'new_tab']:
dbg('Terminal::on_keypress: lookup found: %r' % mapping) dbg('Terminal::on_keypress: lookup found: %r' % mapping)
# handle the case where user has re-bound copy to ctrl+<key> # handle the case where user has re-bound copy to ctrl+<key>
# we only copy if there is a selection otherwise let it fall through # we only copy if there is a selection otherwise let it fall through
@ -1753,6 +1752,9 @@ class Terminal(Gtk.VBox):
def key_new_window(self): def key_new_window(self):
self.terminator.new_window(self.get_cwd()) self.terminator.new_window(self.get_cwd())
def key_new_tab(self):
self.get_toplevel().tab_new(self)
def key_new_terminator(self): def key_new_terminator(self):
spawn_new_terminator(self.origcwd, ['-u']) spawn_new_terminator(self.origcwd, ['-u'])

View File

@ -79,8 +79,8 @@ class Window(Container, Gtk.Window):
if options.role: if options.role:
self.set_role(options.role) self.set_role(options.role)
if options.classname is not None: # if options.classname is not None:
self.set_wmclass(options.classname, self.wmclass_class) # self.set_wmclass(options.classname, self.wmclass_class)
if options.forcedicon is not None: if options.forcedicon is not None:
icon_to_apply = options.forcedicon icon_to_apply = options.forcedicon
@ -208,8 +208,6 @@ class Window(Container, Gtk.Window):
Gdk.Event(Gdk.DELETE)): Gdk.Event(Gdk.DELETE)):
self.on_destroy_event(window, self.on_destroy_event(window,
Gdk.Event(Gdk.DESTROY)) Gdk.Event(Gdk.DESTROY))
elif mapping == 'new_tab':
self.tab_new(self.get_focussed_terminal())
else: else:
return(False) return(False)
return(True) return(True)