11

I'm not looking for a keyboard shortcut, rather I want a commands for:

  • New window
  • New Tab
  • Close Current tab or window
  • Maximise Shell Window
  • Minimise Shell Window
  • Move Shell to a different work-space
  • Switch Tab

And basically anything like this. Remember; I do not want shortcuts, but rather actual commands. The reason for this is so I can utilize alias functionality.

  • 1
    Is python ok with you ? – Sergiy Kolodyazhnyy Sep 18 '16 at 2:19
  • 4
    "Close current tab" – this command is called "exit" :D – egmont Sep 18 '16 at 9:09
  • "I do not want shortcuts [...] so I can utilize alias functionality" – could you please elaborate on this? What's the exact advantage you're hoping for instead of the well-known shortcuts? What's the problem or missing functionality in shortcuts? I think they are the right approach for what you're looking for. – egmont Sep 18 '16 at 9:14
  • @egmont I'm a Vim Addict, if that makes any sense. – Akiva Sep 18 '16 at 10:58
  • So, let's say, e.g. for Maximize, instead of having a window manager hotkey which works for all kinds of windows (browser, image editor, word processor etc.) in all states (i.e. whatever you're doing within them), you'd prefer to have a command that only works for the terminal and no other app, and only if it's not running any command inside (other than the default shell, of course). Nope, sorry, this idea still doesn't make too much sense to me :( – egmont Sep 18 '16 at 12:05
14

You cannot do this by default in Gnome-Terminal, at least with raw commands.

However, you can write scripts that call keyboard shortcuts that can do this. Note that you need xdotool for this: sudo apt install xdotool

  • New Window: Launch a new terminal window with nw
    We can do this with just gnome-terminal.
    Add to .bashrc:

    echo "alias nw=gnome-terminal" >> ~/.bashrc
    
  • New Tab: Launch a new tab with nt
    We can do this with xdotool getactivewindow $(xdotool key ctrl+shift+t)
    Add to .bashrc:

    echo "alias nt='xdotool getactivewindow $(xdotool key ctrl+shift+t)'" >> .bashrc
    
  • Close Tab: Close the current tab or window with ct
    xdotool strikes again: xdotool getactivewindow $(xdotool key ctrl+shift+w)
    Add to .bashrc:

    echo "alias ct='xdotool getactivewindow $(xdotool key ctrl+shift+w)'" >> .bashrc
    
  • Maximize Window: Maximize the entire window with maw
    We can use wmctrl here: wmctrl -r :ACTIVE: -b toggle,maximized_vert,maximized_horz
    Add to .bashrc:

    echo "alias maw='wmctrl -r :ACTIVE: -b toggle,maximized_vert,maximized_horz'" >> .bashrc
    
  • Minimize Window: Minimize the entire window with miw
    We can use xdotool again: xdotool windowminimize $(xdotool getactivewindow)
    Add to .bashrc:

    echo "alias miw='xdotool windowminimize $(xdotool getactivewindow)'" >> .bashrc
    
  • Move to Workspace: Move a window to another workspace with mtw <id>
    This would be just barely possible in shell scripting, and is way beyond my personal experience. I would recommend using Serg's script for this purpose, because it actually works as of now. Ah, the benefits of Compiz.

7

Introduction

The script presented in this answer allows user to control their terminal window via one single command and list of options. It is simple to use and compatible with any terminal emulator that has keybindings similar to gnome-terminal. Moving options can be used with other terminals as well, but tab opening is not guaranteed for those terminals.

The script covers tab opening, window opening, moving to workspace down, workspace right, specific workspace refereed to by integer number, minimizing, maximizing, and unmaximizing a window. The only thing the script does not cover is closing tab/window simply because each shell/terminal emulator already has a command for it - exit or alternatively via CtrlD shortcut.

!!! NOTE : you will need xdotool for workspace switching and tab opening. Install it via sudo apt-get install xdotool. If you prefer to not install extra packages, keep in mind that workspace and tab switching won't work, but other options will.

Usage:

All of the arguments to windowctrl.py are optional, so they can be used separately, or potentially together. As shown by -h option.

$ ./windowctrl.py -h                                                                               
usage: windowctrl.py [-h] [-w] [-t] [-m] [-M] [-u] [-v VIEWPORT] [-r] [-d]

Copyright 2016. Sergiy Kolodyazhnyy.

    Window control for terminal emulators. Originally written
    for gnome-terminal under Ubuntu with Unity desktop but can 
    be used with any other terminal emulator that conforms to 
    gnome-terminal keybindings. It can potentially be used for 
    controlling other windows as well via binding this script
    to a keyboard shortcut.

    Note that --viewport and --tab options require xdotool to be
    installed on the system. If you don't have it installed, you 
    can still use the other options. xdotool can be installed via
    sudo apt-get install xdotool.


optional arguments:
  -h, --help            show this help message and exit
  -w, --window          spawns new window
  -t, --tab             spawns new tab
  -m, --minimize        minimizes current window
  -M, --maximize        maximizes window
  -u, --unmaximize      unmaximizes window
  -v VIEWPORT, --viewport VIEWPORT
                        send window to workspace number
  -r, --right           send window to workspace right
  -d, --down            send window to workspace down

Script Source code:

The script source code is available on GitHub as well as here. Latest changes are likely to go into the GitHub rather than here, so I strongly suggest checking for latest version there. It is also suggested to post bug reports there as well.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Program name: windowctrl.py
Author: Sergiy Kolodyazhnyy
Date:  Sept 18, 2016
Written for: http://askubuntu.com/q/826310/295286
Tested on Ubuntu 16.04 LTS
"""
from __future__ import print_function
import gi
gi.require_version('Gdk', '3.0')
from gi.repository import Gio,Gdk
import sys
import dbus
import subprocess
import argparse

def gsettings_get(schema,path,key):
    """Get value of gsettings schema"""
    if path is None:
        gsettings = Gio.Settings.new(schema)
    else:
        gsettings = Gio.Settings.new_with_path(schema,path)
    return gsettings.get_value(key)

def run_cmd(cmdlist):
    """ Reusable function for running shell commands"""
    try:
        stdout = subprocess.check_output(cmdlist)
    except subprocess.CalledProcessError:
        print(">>> subprocess:",cmdlist)
        sys.exit(1)
    else:
        if stdout:
            return stdout

def get_dbus(bus_type,obj,path,interface,method,arg):
    # Reusable function for accessing dbus
    # This basically works the same as 
    # dbus-send or qdbus. Just give it
    # all the info, and it will spit out output
    if bus_type == "session":
        bus = dbus.SessionBus() 
    if bus_type == "system":
        bus = dbus.SystemBus()
    proxy = bus.get_object(obj,path)
    method = proxy.get_dbus_method(method,interface)
    if arg:
        return method(arg)
    else:
        return method() 

def new_window():
    screen = Gdk.Screen.get_default()
    active_xid = int(screen.get_active_window().get_xid())
    app_path = get_dbus( 'session',
                         'org.ayatana.bamf',
                         '/org/ayatana/bamf/matcher',
                         'org.ayatana.bamf.matcher',
                         'ApplicationForXid',
                         active_xid
                         )

    desk_file  = get_dbus('session',
                          'org.ayatana.bamf',
                          str(app_path),
                          'org.ayatana.bamf.application',
                          'DesktopFile',
                          None
                          )

    # Big credit to Six: http://askubuntu.com/a/664272/295286
    Gio.DesktopAppInfo.new_from_filename(desk_file).launch_uris(None)



def enumerate_viewports():
    """ generates enumerated dictionary of viewports and their
        indexes, counting left to right """
    schema="org.compiz.core"
    path="/org/compiz/profiles/unity/plugins/core/"
    keys=['hsize','vsize']
    screen = Gdk.Screen.get_default()
    screen_size=[ screen.get_width(),screen.get_height()]
    grid=[ int(str(gsettings_get(schema,path,key))) for key in keys]
    x_vals=[ screen_size[0]*x for x in range(0,grid[0]) ]
    y_vals=[screen_size[1]*x for x in range(0,grid[1]) ]

    viewports=[(x,y)  for y in y_vals for x in x_vals ]

    return {vp:ix for ix,vp in enumerate(viewports,1)}


def get_current_viewport():
    """returns tuple representing current viewport, 
       in format (width,height)"""
    vp_string = run_cmd(['xprop', '-root', 
                         '-notype', '_NET_DESKTOP_VIEWPORT'])
    vp_list=vp_string.decode().strip().split('=')[1].split(',')
    return tuple( int(i)  for i in vp_list )

def maximize():

    screen = Gdk.Screen.get_default()
    window = screen.get_active_window()
    window.maximize()
    screen.get_active_window()
    window.process_all_updates()

def unmaximize():

    screen = Gdk.Screen.get_default()
    window = screen.get_active_window()
    window.unmaximize()
    screen.get_active_window()
    window.process_all_updates()

def minimize():

    screen = Gdk.Screen.get_default()
    window = screen.get_active_window()
    window.iconify()
    window.process_all_updates()

def window_move(viewport):

    # 1. grab window object
    # 2. jump viewport 0 0 so we can move only
    #    in positive plane
    # 3. move the window.
    # 4. set viewport back to what it was

    # Step 1
    screen = Gdk.Screen.get_default()
    screen_size=[ screen.get_width(),screen.get_height()]
    window = screen.get_active_window()

    viewports = enumerate_viewports()
    current = get_current_viewport()
    current_num = viewports[current]
    destination = [ 
                   key for  key,val in viewports.items() 
                   if val == int(viewport)
                   ][0]
    # Step 2.
    run_cmd([
            'xdotool',
            'set_desktop_viewport',
            '0','0'
            ]) 
    # Step 3.
    window.move(destination[0],destination[1])
    window.process_all_updates()

    run_cmd([
            'xdotool',
            'set_desktop_viewport',
            str(current[0]),
            str(current[1])
            ]) 

def move_right():
    sc = Gdk.Screen.get_default()
    width = sc.get_width()
    win = sc.get_active_window()
    pos = win.get_origin()
    win.move(width,pos.y)
    win.process_all_updates()

def move_down():
    sc = Gdk.Screen.get_default()
    height = sc.get_height()
    win = sc.get_active_window()
    pos = win.get_origin()
    win.move(pos.x,height)
    win.process_all_updates()

def new_tab():
    run_cmd(['xdotool','key','ctrl+shift+t'])

def parse_args():
    """ Parse command line arguments"""

    info="""Copyright 2016. Sergiy Kolodyazhnyy.

    Window control for terminal emulators. Originally written
    for gnome-terminal under Ubuntu with Unity desktop but can 
    be used with any other terminal emulator that conforms to 
    gnome-terminal keybindings. It can potentially be used for 
    controlling other windows as well via binding this script
    to a keyboard shortcut.

    Note that --viewport and --tab options require xdotool to be
    installed on the system. If you don't have it installed, you 
    can still use the other options. xdotool can be installed via
    sudo apt-get install xdotool.
    """
    arg_parser = argparse.ArgumentParser(
                 description=info,
                 formatter_class=argparse.RawTextHelpFormatter)
    arg_parser.add_argument(
                '-w','--window', action='store_true',
                help='spawns new window',
                required=False)
    arg_parser.add_argument(
                '-t','--tab',action='store_true',
                help='spawns new tab',
                required=False)
    arg_parser.add_argument(
                '-m','--minimize',action='store_true',
                help='minimizes current window',
                required=False)
    arg_parser.add_argument(
                '-M','--maximize',action='store_true',
                help='maximizes window',
                required=False)
    arg_parser.add_argument(
                '-u','--unmaximize',action='store_true',
                help='unmaximizes window',
                required=False)
    arg_parser.add_argument(
               '-v','--viewport',action='store',
               type=int, help='send window to workspace number',
               required=False)
    arg_parser.add_argument(
               '-r','--right',action='store_true',
               help='send window to workspace right',
               required=False)
    arg_parser.add_argument(
               '-d','--down',action='store_true',
               help='send window to workspace down',
               required=False)
    return arg_parser.parse_args()

def main():

    args = parse_args()

    if args.window:
       new_window()
    if args.tab:
       new_tab()
    if args.down:
       move_down()
    if args.right:
       move_right()       
    if args.viewport:
       window_move(args.viewport)
    if args.minimize:
       minimize()
    if args.maximize:
       maximize()
    if args.unmaximize:
       unmaximize()

if __name__ == '__main__':
    main()

Side notes

  • You asked "Is there a command in Gnome-Terminal, or any tabbable shell to open a new tab?" Gnome Terminal manual doesn't list such option. The shells are command-line utilities. Tabs are feature of GUI applications. There are terminal multiplexers like screen or tmux which can have "tabs" or split windows , which sort of comes close to "tabbable shell" but this is not the same type of behavior you ask. Basically, answer to your question is "No". There are always alternatives, and my answer provides one of them. It treats terminal window according to its nature - X11 GUI window.

  • How does this answer relate to aliases ? Well, first of all aliases can be a bit messy, especially when it comes to quoting and parsing multiple outputs from multiple commands. This script gives you one , centralized command, with flags/switches to do a discrete task upon a window. It also makes aliases simpler. You could do alias nw='windowctrl.py --window'. Much shorter, much neater.

  • I am happy with split terminals – Akiva Sep 20 '16 at 22:49
  • 1
    @Akiva do you want me to link a question related to splitting terminal ? have you tried this script by the way ? what do you think ? – Sergiy Kolodyazhnyy Sep 20 '16 at 23:38
  • I will be trying your script, because the answer above is giving me trouble. However I may not have so much luck, due to the problem being chiefly with xdotool. – Akiva Sep 21 '16 at 1:44
  • @Akiva and what is the problem with xdotool ? Maybe I could fix it ? – Sergiy Kolodyazhnyy Sep 21 '16 at 2:10
  • I'll have to get back to you on that. I might have to do with my custom keyboard layout, or the fact that I'm on 16.10, or that I'm trying it on guake. – Akiva Sep 21 '16 at 2:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.