#!/usr/bin/env python """ pymux: Pure Python terminal multiplexer. Usage: pymux [(standalone|start-server|attach)] [-d] [--truecolor] [--ansicolor] [(-S )] [(-f )] [(--log )] [--] [] pymux list-sessions pymux -h | --help pymux 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[''] or os.environ.get('PYMUX') socket_name_from_env = not a[''] and os.environ.get('PYMUX') filename = a[''] command = a[''] 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['']: logging.basicConfig(filename=a[''], level=logging.DEBUG) if a['standalone']: mux.run_standalone(color_depth=color_depth) elif a['list-sessions'] or a[''] 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[''] and socket_name: create_client(socket_name).run_command(a[''], 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()