164 lines
4.9 KiB
Python
164 lines
4.9 KiB
Python
|
#!/usr/bin/env python
|
||
|
"""
|
||
|
pymux: Pure Python terminal multiplexer.
|
||
|
Usage:
|
||
|
pymux [(standalone|start-server|attach)] [-d]
|
||
|
[--truecolor] [--ansicolor] [(-S <socket>)] [(-f <file>)]
|
||
|
[(--log <logfile>)]
|
||
|
[--] [<command>]
|
||
|
pymux list-sessions
|
||
|
pymux -h | --help
|
||
|
pymux <command>
|
||
|
|
||
|
Options:
|
||
|
standalone : Run as a standalone process. (for debugging, detaching is
|
||
|
not possible.
|
||
|
start-server : Run a server daemon that can be attached later on.
|
||
|
attach : Attach to a running session.
|
||
|
|
||
|
-f : Path to configuration file. By default: '~/.pymux.conf'.
|
||
|
-S : Unix socket path.
|
||
|
-d : Detach all other clients, when attaching.
|
||
|
--log : Logfile.
|
||
|
--truecolor : Render true color (24 bit) instead of 256 colors.
|
||
|
(Each client can set this separately.)
|
||
|
"""
|
||
|
from __future__ import unicode_literals, absolute_import
|
||
|
|
||
|
from prompt_toolkit.output import ColorDepth
|
||
|
from pymux.main import Pymux
|
||
|
from pymux.client import create_client, list_clients
|
||
|
from pymux.utils import daemonize
|
||
|
|
||
|
import docopt
|
||
|
import getpass
|
||
|
import logging
|
||
|
import os
|
||
|
import sys
|
||
|
import tempfile
|
||
|
|
||
|
__all__ = (
|
||
|
'run',
|
||
|
)
|
||
|
|
||
|
|
||
|
def run():
|
||
|
a = docopt.docopt(__doc__)
|
||
|
socket_name = a['<socket>'] or os.environ.get('PYMUX')
|
||
|
socket_name_from_env = not a['<socket>'] and os.environ.get('PYMUX')
|
||
|
filename = a['<file>']
|
||
|
command = a['<command>']
|
||
|
true_color = a['--truecolor']
|
||
|
ansi_colors_only = a['--ansicolor'] or \
|
||
|
bool(os.environ.get('PROMPT_TOOLKIT_ANSI_COLORS_ONLY', False))
|
||
|
|
||
|
# Parse pane_id from socket_name. It looks like "socket_name,pane_id".
|
||
|
if socket_name and ',' in socket_name:
|
||
|
socket_name, pane_id = socket_name.rsplit(',', 1)
|
||
|
else:
|
||
|
pane_id = None
|
||
|
|
||
|
# Color depth.
|
||
|
if ansi_colors_only:
|
||
|
color_depth = ColorDepth.DEPTH_4_BIT
|
||
|
elif true_color:
|
||
|
color_depth = ColorDepth.DEPTH_24_BIT
|
||
|
else:
|
||
|
color_depth = ColorDepth.DEPTH_8_BIT
|
||
|
|
||
|
# Expand socket name. (Make it possible to just accept numbers.)
|
||
|
if socket_name and socket_name.isdigit():
|
||
|
socket_name = '%s/pymux.sock.%s.%s' % (
|
||
|
tempfile.gettempdir(), getpass.getuser(), socket_name)
|
||
|
|
||
|
# Configuration filename.
|
||
|
default_config = os.path.abspath(os.path.expanduser('~/.pymux.conf'))
|
||
|
if not filename and os.path.exists(default_config):
|
||
|
filename = default_config
|
||
|
|
||
|
if filename:
|
||
|
filename = os.path.abspath(os.path.expanduser(filename))
|
||
|
|
||
|
# Create 'Pymux'.
|
||
|
mux = Pymux(source_file=filename, startup_command=command)
|
||
|
|
||
|
# Setup logging.
|
||
|
if a['<logfile>']:
|
||
|
logging.basicConfig(filename=a['<logfile>'], level=logging.DEBUG)
|
||
|
|
||
|
if a['standalone']:
|
||
|
mux.run_standalone(color_depth=color_depth)
|
||
|
|
||
|
elif a['list-sessions'] or a['<command>'] in ('ls', 'list-sessions'):
|
||
|
for c in list_clients():
|
||
|
print(c.socket_name)
|
||
|
|
||
|
elif a['start-server']:
|
||
|
if socket_name_from_env:
|
||
|
_socket_from_env_warning()
|
||
|
sys.exit(1)
|
||
|
|
||
|
# Log to stdout.
|
||
|
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
|
||
|
|
||
|
# Run server.
|
||
|
socket_name = mux.listen_on_socket()
|
||
|
try:
|
||
|
mux.run_server()
|
||
|
except KeyboardInterrupt:
|
||
|
sys.exit(1)
|
||
|
|
||
|
elif a['attach']:
|
||
|
if socket_name_from_env:
|
||
|
_socket_from_env_warning()
|
||
|
sys.exit(1)
|
||
|
|
||
|
detach_other_clients = a['-d']
|
||
|
|
||
|
if socket_name:
|
||
|
create_client(socket_name).attach(
|
||
|
detach_other_clients=detach_other_clients,
|
||
|
color_depth=color_depth)
|
||
|
else:
|
||
|
# Connect to the first server.
|
||
|
for c in list_clients():
|
||
|
c.attach(detach_other_clients=detach_other_clients,
|
||
|
color_depth=color_depth)
|
||
|
break
|
||
|
else: # Nobreak.
|
||
|
print('No pymux instance found.')
|
||
|
sys.exit(1)
|
||
|
|
||
|
elif a['<command>'] and socket_name:
|
||
|
create_client(socket_name).run_command(a['<command>'], pane_id)
|
||
|
|
||
|
elif not socket_name:
|
||
|
# Run client/server combination.
|
||
|
socket_name = mux.listen_on_socket(socket_name)
|
||
|
pid = daemonize()
|
||
|
|
||
|
if pid > 0:
|
||
|
# Create window. It is important that this happens in the daemon,
|
||
|
# because the parent of the process running inside should be this
|
||
|
# daemon. (Otherwise the `waitpid` call won't work.)
|
||
|
mux.run_server()
|
||
|
else:
|
||
|
create_client(socket_name).attach(color_depth=color_depth)
|
||
|
|
||
|
else:
|
||
|
if socket_name_from_env:
|
||
|
_socket_from_env_warning()
|
||
|
sys.exit(1)
|
||
|
else:
|
||
|
print('Invalid command.')
|
||
|
sys.exit(1)
|
||
|
|
||
|
|
||
|
def _socket_from_env_warning():
|
||
|
print('Please be careful nesting pymux sessions.')
|
||
|
print('Unset PYMUX environment variable first.')
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
run()
|