* 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 sys
import argparse
from terminatorlib.util import dbg, err
try:
@ -27,40 +28,60 @@ try:
except ImportError:
err('Unable to initialise Terminator remote library. This probably means dbus is not available')
sys.exit(1)
from terminatorlib.translation import _
APP_NAME='remotinator'
APP_VERSION='0.97'
COMMANDS={
'hsplit': ['terminal_hsplit', 'Split the current terminal horizontally'],
'vsplit': ['terminal_vsplit', 'Split the current terminal vertically'],
'terminals': ['get_terminals', 'Get a list of all terminals'],
'terminal_tab': ['get_terminal_tab', 'Get the UUID of a parent tab'],
'terminal_tab_title': ['get_terminal_tab_title', 'Get the title of a parent tab'],
# Command uuid req. Description
'new_window': [False, _('Open a new window')],
'new_tab': [True, _('Open a new tab')],
'hsplit': [True, _('Split the current terminal horizontally')],
'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__':
dbg ("%s starting up, version %s" % (APP_NAME, APP_VERSION))
if sys.argv[0].split('/')[-1] == APP_NAME:
if len(sys.argv) <2 or sys.argv[1] in ['-h', '--help']:
print "Usage: %s COMMAND" % sys.argv[0]
print " Commands:"
for command in COMMANDS:
print " %s : %s" % (command, COMMANDS[command][1])
sys.exit(1)
command = sys.argv[1]
command_desc=''
for command in sorted(COMMANDS.keys()):
command_desc += " %-*s %s %s\n" % (max([len(x) for x in COMMANDS.keys()]),
command,
COMMANDS[command][0] and '*' or ' ',
COMMANDS[command][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:
command = sys.argv[0].split('/')[-1]
if not command in COMMANDS or not hasattr(ipc, COMMANDS[command][0]):
err("Unknown command: %s" % command)
err("$TERMINATOR_UUID is not set, or passed as an option.")
sys.exit(1)
if not os.environ.has_key('TERMINATOR_UUID'):
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'])
else:
func(options)

View File

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

View File

@ -3,8 +3,10 @@
# GPL v2 only
"""ipc.py - DBus server and API calls"""
from gi.repository import Gtk
from gi.repository import DBus, DBusGLib # VERIFY/FIXME FOR GTK3: DBusException?? Replace all "dbus" with "DBus" in this file, and stuff??
from gi.repository import Gdk
import dbus.service
from dbus.exceptions import DBusException
import dbus.glib
from borg import Borg
from terminator import Terminator
from config import Config
@ -17,8 +19,8 @@ if not CONFIG['dbus']:
dbg('dbus disabled')
raise ImportError
BUS_BASE = 'net.tenshu.Terminator'
BUS_PATH = '/net/tenshu/Terminator'
BUS_BASE = 'net.tenshu.Terminator2'
BUS_PATH = '/net/tenshu/Terminator2'
try:
# Try and include the X11 display name in the dbus bus name
DISPLAY = hex(hash(Gdk.get_display())).replace('-', '_')
@ -58,7 +60,7 @@ class DBusService(Borg, dbus.service.Object):
self.terminator = Terminator()
@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"""
dbg('dbus method called: new_window with parameters %s'%(options))
oldopts = self.terminator.config.options_get()
@ -68,7 +70,7 @@ class DBusService(Borg, dbus.service.Object):
self.terminator.layout_done()
@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"""
dbg('dbus method called: new_tab with parameters %s'%(options))
oldopts = self.terminator.config.options_get()
@ -78,35 +80,78 @@ class DBusService(Borg, dbus.service.Object):
window.tab_new()
@dbus.service.method(BUS_NAME)
def terminal_hsplit(self, uuid=None):
"""Split a terminal horizontally, by UUID"""
return self.terminal_split(uuid, True)
def new_window(self):
"""Create a new Window"""
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)
def terminal_vsplit(self, uuid=None):
"""Split a terminal vertically, by UUID"""
return self.terminal_split(uuid, False)
def new_tab(self, uuid=None):
"""Create a new tab"""
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"""
dbg('dbus method called: terminal_hsplit')
dbg('dbus method called: %s' % type)
if not uuid:
return "ERROR: No UUID specified"
terminal = self.terminator.find_terminal_by_uuid(uuid)
terminals_before = set(self.get_terminals())
if not terminal:
return "ERROR: Terminal with supplied UUID not found"
if horiz:
elif type == 'tab':
terminal.key_new_tab()
elif type == 'hsplit':
terminal.key_split_horiz()
else:
elif type == 'vsplit':
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)
def get_terminals(self, uuid):
def get_terminals(self):
"""Return a list of all the terminals"""
return [x.uuid.urn for x in self.terminator.terminals]
@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"""
maker = Factory()
terminal = self.terminator.find_terminal_by_uuid(uuid)
@ -116,7 +161,7 @@ class DBusService(Borg, dbus.service.Object):
return root_widget.uuid.urn
@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"""
maker = Factory()
terminal = self.terminator.find_terminal_by_uuid(uuid)
@ -134,38 +179,58 @@ def with_proxy(func):
func(proxy, *args, **argd)
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
def new_window(session, options):
"""Call the dbus method to open a new window"""
session.new_window(options)
print session.new_window()
@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"""
session.new_tab(options)
print session.new_tab(uuid)
@with_proxy
def terminal_hsplit(session, uuid):
def hsplit(session, uuid, options):
"""Call the dbus method to horizontally split a terminal"""
session.terminal_hsplit(uuid)
print session.hsplit(uuid)
@with_proxy
def terminal_vsplit(session, uuid):
def vsplit(session, uuid, options):
"""Call the dbus method to vertically split a terminal"""
print session.terminal_vsplit(uuid)
print session.vsplit(uuid)
@with_proxy
def get_terminals(session, uuid):
def get_terminals(session, options):
"""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
def get_terminal_tab(session, uuid):
def get_window(session, uuid, options):
"""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
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"""
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)
if mapping and mapping not in ['close_window',
'full_screen',
'new_tab']:
'full_screen']:
dbg('Terminal::on_keypress: lookup found: %r' % mapping)
# 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
@ -1753,6 +1752,9 @@ class Terminal(Gtk.VBox):
def key_new_window(self):
self.terminator.new_window(self.get_cwd())
def key_new_tab(self):
self.get_toplevel().tab_new(self)
def key_new_terminator(self):
spawn_new_terminator(self.origcwd, ['-u'])

View File

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