Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Logic for improved guake like terminal functionality #579

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions doc/terminator.1
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,30 @@ Specify the preferred size and position of Terminator\(cqs window;
see \fBX\fP(7).
.RE
.sp
\fB\-\-guake\-key\fP=\fIGUAKE_KEY\fP
.RS 4
A Guake like terminal mode where terminal can be toggled by a key.
Usage Example: terminator \-\-guake\-key="F8" \-\-guake\-side left \-\-guake\-width 700 \-\-guake\-height 800
.RE
.sp
\fB\-\-guake\-side\fP=\fIGUAKE_SIDE\fP
.RS 4
Set the preferred screen edge position of a terminal in Guake like mode.
Options: top, bottom, left, right
Default: top
.RE
.sp
\fB\-\-guake\-width\fP=\fIGUAKE_WIDTH\fP
.RS 4
Set the preferred width when using Guake like mode.
Default: 800
.RE
.sp
\fB\-\-guake\-height\fP=\fIGUAKE_HEIGHT\fP
.RS 4
Set the preferred height when using Guake like mode.
Default: 600
.sp
\fB\-e\fP \fICOMMAND\fP, \fB\-\-command\fP=\fICOMMAND\fP
.RS 4
Run the specified command instead of the default shell or profile
Expand Down
13 changes: 12 additions & 1 deletion terminatorlib/optionparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"""Terminator.optionparse - Parse commandline options"""

import argparse
from argparse import RawTextHelpFormatter
import sys
import os

Expand All @@ -37,7 +38,7 @@ def parse_options():
"""Parse the command line options"""
is_x_terminal_emulator = os.path.basename(sys.argv[0]) == 'x-terminal-emulator'

parser = argparse.ArgumentParser()
parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter)

parser.add_argument('-R', '--reload', action='store_true', dest='reload',
help=_('Reload terminator configuration'))
Expand All @@ -59,6 +60,16 @@ def parse_options():
parser.add_argument('--geometry', dest='geometry', type=str,
help=_('Set the preferred size and position of the window'
'(see X man page)'))
parser.add_argument('--guake-key', dest='guake_key', type=str,
help=_('A Guake like terminal mode where terminal can be toggled by a key.\nUsage Example: terminator --guake-key="F8" --guake-side left --guake-width 700 --guake-height 800'))
parser.add_argument('--guake-side', dest='guake_side', default="top", type=str,
help=_('Set the preferred screen edge position of a terminal in Guake like mode.\nOptions: top, bottom, left, right\nDefault: top'))
parser.add_argument('--guake-width', dest='guake_width', default="800", type=str,
help=_('Set the preferred width when using Guake like mode. Default: 800'
'(see X man page)'))
parser.add_argument('--guake-height', dest='guake_height', default="600", type=str,
help=_('Set the preferred height when using Guake like mode. Default: 600'
'(see X man page)'))
if not is_x_terminal_emulator:
parser.add_argument('-e', '--command', dest='command',
help=_('Specify a command to execute inside the terminal'))
Expand Down
125 changes: 97 additions & 28 deletions terminatorlib/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,17 @@ def __init__(self):
# self.set_property('allow-shrink', True) # FIXME FOR GTK3, or do we need this actually?
icon_to_apply=''

self.register_callbacks()

self.apply_config()

self.title = WindowTitle(self)
self.title.update()

self.preventHide = False

self.preventHide = False

self.display = Gdk.Display().get_default()
self.mouse = self.display.get_default_seat().get_pointer()
self.guake_key = None

options = self.config.options_get()
if options:
Expand All @@ -92,15 +96,44 @@ def __init__(self):

if options.role:
self.set_role(options.role)

if options.forcedicon is not None:
icon_to_apply = options.forcedicon

if options.geometry:
if not self.parse_geometry(options.geometry):
err('Window::__init__: Unable to parse geometry: %s' %
err('Window::__init__: Unable to parse geometry: %s' %
options.geometry)

if options.guake_key:
self.guake_key = options.guake_key

if options.guake_side and options.guake_width and options.guake_height:
proceed_undecorated = True

if options.guake_side in ["top", "bottom", "left", "right"]:
self.guake_side = options.guake_side
else:
proceed_undecorated = False
err('Window::__init__: Unable to parse guake_side: %s' %
options.guake_side)

try:
self.guake_width = int(options.guake_width)
self.guake_height = int(options.guake_height)
except Exception as e:
proceed_undecorated = False
err('Window::__init__: Unable to parse guake_width: %s and/or guake_height: %s' %
options.guake_width, options.guake_height)

if proceed_undecorated:
self.set_decorated(False)
self.set_default_size(self.guake_width, self.guake_height)
else:
self.guake_key = None

self.register_callbacks()

self.apply_icon(icon_to_apply)
self.pending_set_rough_geometry_hint = False
self.hidefunc = self.hide
Expand All @@ -119,6 +152,35 @@ def do_set_property(self, prop, value):
else:
raise AttributeError('unknown property %s' % prop.name)

# NOTE: Gdk.VisibilityState.UNOBSCURED presumably isn't reliable due to transparency in moddern wms.
# Seems to work okay for our needs but should be kept in mind for future changes....
def _bind_window_to_position(self, widget, eve):
if eve.state == Gdk.VisibilityState.UNOBSCURED:
screen, mouse_x, mouse_y = self.mouse.get_position()
monitor = self.display.get_monitor_at_point(mouse_x, mouse_y)
window_w, window_h = self.guake_width, self.guake_height
geom_rect = monitor.get_geometry()
x, y, w, h = geom_rect.x, geom_rect.y, geom_rect.width, geom_rect.height

if not self.is_maximized() and not self.isfullscreen:
if self.guake_side == "top":
new_x = (w - (window_w + ((w - window_w)/2) )) + x
new_y = y
if self.guake_side == "bottom":
new_x = (w - (window_w + ((w - window_w)/2) )) + x
new_y = (h - window_h) + y
if self.guake_side == "left":
new_x = x
new_y = (h - (window_h + ((h - window_h)/2) )) + y
if self.guake_side == "right":
new_x = (w - window_w) + x
new_y = (h - (window_h + ((h - window_h)/2) )) + y
else:
new_x = x
new_y = y

self.move(new_x, new_y)

def register_callbacks(self):
"""Connect the GTK+ signals we care about"""
self.connect('key-press-event', self.on_key_press)
Expand All @@ -129,22 +191,29 @@ def register_callbacks(self):
self.connect('focus-out-event', self.on_focus_out)
self.connect('focus-in-event', self.on_focus_in)

if self.guake_key not in ('', None):
guake_unload_id = self.connect('visibility-notify-event', self._bind_window_to_position)

_hide_key = self.config['keybindings']['hide_window']
toggle_key = self.guake_key if self.guake_key not in ('', None) else _hide_key if _hide_key not in ('', None) else None

# Attempt to grab a global hotkey for hiding the window.
# If we fail, we'll never hide the window, iconifying instead.
if self.config['keybindings']['hide_window'] not in ('', None):
if display_manager() == 'X11':
try:
self.hidebound = Keybinder.bind(
self.config['keybindings']['hide_window'],
self.on_hide_window)
except (KeyError, NameError):
pass

if not self.hidebound:
err('Unable to bind hide_window key, another instance/window has it.')
self.hidefunc = self.iconify
else:
self.hidefunc = self.hide
if toggle_key and display_manager() == 'X11':
try:
self.hidebound = Keybinder.bind(toggle_key, self.on_hide_window)
except (KeyError, NameError):
...

if not self.hidebound:
err('Unable to bind hide_window key, another instance/window has it.')
self.hidefunc = self.iconify

if self.guake_key not in ('', None):
GObject.signal_handler_disconnect(self, guake_unload_id)
self.set_decorated(True)
else:
self.hidefunc = self.hide

def apply_config(self):
"""Apply various configuration options"""
Expand Down Expand Up @@ -335,7 +404,7 @@ def on_hide_window(self, data=None):
# pylint: disable-msg=W0613
def on_window_state_changed(self, window, event):
"""Handle the state of the window changing"""
self.isfullscreen = bool(event.new_window_state &
self.isfullscreen = bool(event.new_window_state &
Gdk.WindowState.FULLSCREEN)
self.ismaximised = bool(event.new_window_state &
Gdk.WindowState.MAXIMIZED)
Expand Down Expand Up @@ -394,7 +463,7 @@ def set_real_transparency(self, value=True):
visual = screen.get_rgba_visual()
if visual:
self.set_visual(visual)

def show(self, startup=False):
"""Undo the startup show request if started in hidden mode"""
#Present is necessary to grab focus when window is hidden from taskbar.
Expand Down Expand Up @@ -484,7 +553,7 @@ def split_axis(self, widget, vertical=True, cwd=None, sibling=None, widgetfirst=
container = maker.make('VPaned')
else:
container = maker.make('HPaned')

self.set_pos_by_ratio = True

if not sibling:
Expand All @@ -508,7 +577,7 @@ def split_axis(self, widget, vertical=True, cwd=None, sibling=None, widgetfirst=
for term in order:
container.add(term)
container.show_all()

while Gtk.events_pending():
Gtk.main_iteration_do(False)
sibling.grab_focus()
Expand Down Expand Up @@ -556,7 +625,7 @@ def zoom(self, widget, font_scale=True):
self.set_property('term_zoomed', True)

if font_scale:
widget.cnxids.new(widget, 'size-allocate',
widget.cnxids.new(widget, 'size-allocate',
widget.zoom_scale, self.zoom_data)

widget.grab_focus()
Expand Down Expand Up @@ -629,7 +698,7 @@ def rotate(self, widget, clockwise):

def get_terminals(self):
return(util.enumerate_descendants(self)[1])

def get_visible_terminals(self):
"""Walk down the widget tree to find all of the visible terminals.
Mostly using Container::get_visible_terminals()"""
Expand Down Expand Up @@ -719,7 +788,7 @@ def set_rough_geometry_hints(self):
extra_height = win_height - total_font_height

dbg('setting geometry hints: (ewidth:%s)(eheight:%s),\
(fwidth:%s)(fheight:%s)' % (extra_width, extra_height,
(fwidth:%s)(fheight:%s)' % (extra_width, extra_height,
font_width, font_height))
geometry = Gdk.Geometry()
geometry.base_width = extra_width
Expand Down Expand Up @@ -843,7 +912,7 @@ def ungroup_tab(self, widget):
if not maker.isinstance(notebook, 'Notebook'):
dbg('note in a notebook, refusing to ungroup tab')
return

self.set_groups(None, self.get_visible_terminals())

def move_tab(self, widget, direction):
Expand Down Expand Up @@ -876,7 +945,7 @@ def move_tab(self, widget, direction):
else:
err('unknown direction: %s' % direction)
return

notebook.reorder_child(child, page)

def navigate_terminal(self, terminal, direction):
Expand Down
Loading