diff --git a/README b/README index abfb75e0..4cb1af76 100644 --- a/README +++ b/README @@ -18,7 +18,7 @@ install locally, using the following instructions... cd byobu* ./configure --prefix="$HOME/byobu" 4) OPTIONAL: Use python from your environment, rather than from your distro - sed -i -e "s:/usr/bin/python:/usr/bin/env python" usr/bin/* usr/lib/byobu/include/* + echo "export BYOBU_PYTHON='/usr/bin/env python'" >> $HOME/.bashrc 5) Build: make 6) Install: diff --git a/debian/changelog b/debian/changelog index 3f8d8bb8..9a39c4b8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -6,6 +6,15 @@ byobu (5.68) unreleased; urgency=low - add a new status item, to show the session name * usr/lib/byobu/include/icons, usr/lib/byobu/session: - use an icon for the session status item + * debian/control, debian/rules, README, usr/bin/byobu-config, + usr/bin/byobu-config => usr/lib/byobu/include/config.py, + usr/bin/byobu-select-session, usr/bin/byobu-select-session => + usr/lib/byobu/include/select-session.py, + usr/lib/byobu/include/constants, usr/lib/byobu/include/Makefile.am: + - LP: #1253458 + - introduce a new BYOBU_PYTHON environment variable, that defines + your desired python interpreter + - prefer python, then python2, then python3 -- Dustin Kirkland Mon, 02 Dec 2013 18:46:28 -0600 diff --git a/debian/control b/debian/control index ef7ef052..2526a887 100644 --- a/debian/control +++ b/debian/control @@ -14,8 +14,8 @@ Depends: ${misc:Depends}, ${perl:Depends}, ${python:Depends}, debconf (>= 0.5) | debconf-2.0, gettext-base, - python, - python-newt (>= 0.52.2-11), + python3|python, + python3-newt|python-newt, tmux (>= 1.5) | screen, gawk Recommends: diff --git a/debian/rules b/debian/rules index 42908d06..ff8e705b 100755 --- a/debian/rules +++ b/debian/rules @@ -4,7 +4,7 @@ override_dh_auto_build: # Check python syntax - pep8 --verbose --repeat --ignore W191,E501 usr/bin/byobu-config usr/bin/byobu-select-session + pep8 --verbose --repeat --ignore W191,E501 usr/lib/byobu/include/config.py usr/lib/byobu/include/select-session.py # Check shell syntax sh -n `find . -type f -exec grep -l "^\#\!/bin/sh" '{}' \;` # Check for bashisms in shell scripts diff --git a/usr/bin/byobu-config b/usr/bin/byobu-config index 834be4a6..9fd810f2 100755 --- a/usr/bin/byobu-config +++ b/usr/bin/byobu-config @@ -1,12 +1,9 @@ -#!/usr/bin/python +#!/bin/sh -e # -# byobu-config -# Copyright (C) 2008 Canonical Ltd. -# Copyright (C) 2008-2013 Dustin Kirkland - +# byobu-config - interactive byobu configuration wrapper script +# Copyright (C) 2013 Dustin Kirkland # -# Authors: Nick Barcet -# Dustin Kirkland +# Authors: Dustin Kirkland # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -20,366 +17,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# If you change any strings, please generate localization information with: -# ./debian/rules get-po +PKG="byobu" +[ -z "${BYOBU_PREFIX}" ] && export BYOBU_PREFIX="/usr" || export BYOBU_PREFIX +. "${BYOBU_PREFIX}/lib/${PKG}/include/common" -import sys -import os -import os.path -import time -import string -import subprocess -import gettext -import glob - - -def error(msg): - print("ERROR: %s" % msg) - sys.exit(1) - - -try: - import snack - from snack import * -except: - error("Could not import the python snack module") - - -PKG = "byobu" -HOME = os.getenv("HOME") -USER = os.getenv("USER") -BYOBU_CONFIG_DIR = os.getenv("BYOBU_CONFIG_DIR", HOME + "/.byobu") -BYOBU_RUN_DIR = os.getenv("BYOBU_RUN_DIR", HOME + "/.cache/byobu") -BYOBU_BACKEND = os.getenv("BYOBU_BACKEND", "tmux") -BYOBU_SOCKETDIR = os.getenv("SOCKETDIR", "/var/run/screen") -BYOBU_PREFIX = os.getenv("BYOBU_PREFIX", "/usr") -SHARE = BYOBU_PREFIX + '/share/' + PKG -DOC = BYOBU_PREFIX + '/share/doc/' + PKG -if not os.path.exists(SHARE): - SHARE = BYOBU_CONFIG_DIR + "/" + SHARE -if not os.path.exists(DOC): - DOC = BYOBU_CONFIG_DIR + "/" + DOC -DEF_ESC = "A" -RELOAD = "If you are using the default set of keybindings, press\n or to activate these changes.\n\nOtherwise, exit this session and start a new one." -RELOAD_FLAG = "%s/reload-required" % (BYOBU_RUN_DIR) -ESC = '' -snack.hotkeys[ESC] = ord(ESC) -snack.hotkeys[ord(ESC)] = ESC -gettext.bindtextdomain(PKG, SHARE + '/po') -gettext.textdomain(PKG) -_ = gettext.gettext - - -def ioctl_GWINSZ(fd): - # Discover terminal width - try: - import fcntl - import termios - import struct - import os - cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) - except: - return None - return cr - - -def reload_required(): - try: - if not os.path.exists(BYOBU_CONFIG_DIR): - # 493 (decimal) is 0755 (octal) - # Use decimal for portability across all python versions - os.makedirs(BYOBU_CONFIG_DIR, 493) - f = open(RELOAD_FLAG, 'w') - f.close() - if BYOBU_BACKEND == "screen": - subprocess.call([BYOBU_BACKEND, "-X", "at", "0", "source", "%s/profile" % BYOBU_CONFIG_DIR]) - except: - True - - -def terminal_size(): - # decide on some terminal size - cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) - # try open fds - if not cr: - # ...then ctty - try: - fd = os.open(os.ctermid(), os.O_RDONLY) - cr = ioctl_GWINSZ(fd) - os.close(fd) - except: - pass - if not cr: - # env vars or finally defaults - try: - cr = (env['LINES'], env['COLUMNS']) - except: - cr = (25, 80) - # reverse rows, cols - return int(cr[1] - 5), int(cr[0] - 5) - - -def menu(snackScreen, size, isInstalled): - if isInstalled: - installtext = _("Byobu currently launches at login (toggle off)") - else: - installtext = _("Byobu currently does not launch at login (toggle on)") - li = Listbox(height=6, width=60, returnExit=1) - li.append(_("Help -- Quick Start Guide"), 1) - li.append(_("Toggle status notifications"), 2) - li.append(_("Change escape sequence"), 3) - li.append(installtext, 4) - bb = ButtonBar(snackScreen, (("Exit", "exit", ESC),), compact=1) - g = GridForm(snackScreen, _(" Byobu Configuration Menu"), 1, 2) - g.add(li, 0, 0, padding=(4, 2, 4, 2)) - g.add(bb, 0, 1, padding=(1, 1, 0, 0)) - if bb.buttonPressed(g.runOnce()) == "exit": - return 0 - else: - return li.current() - - -def messagebox(snackScreen, width, height, title, text, scroll=0, buttons=((_("Okay"), "okay"), (_("Cancel"), "cancel", ESC))): - t = Textbox(width, height, text, scroll=scroll) - bb = ButtonBar(snackScreen, buttons, compact=1) - g = GridForm(snackScreen, title, 1, 2) - g.add(t, 0, 0, padding=(0, 0, 0, 0)) - g.add(bb, 0, 1, padding=(1, 1, 0, 0)) - return bb.buttonPressed(g.runOnce()) - - -def help(snackScreen, size): - f = open(DOC + '/help.' + BYOBU_BACKEND + '.txt') - text = f.read() - f.close() - text = text.replace("", getesckey(), 1) - t = Textbox(67, 16, text, scroll=1, wrap=1) - bb = ButtonBar(snackScreen, ((_("Menu"), "menu", ESC),), compact=1) - g = GridForm(snackScreen, _("Byobu Help"), 2, 4) - g.add(t, 1, 0) - g.add(bb, 1, 1, padding=(1, 1, 0, 0)) - button = bb.buttonPressed(g.runOnce()) - return 100 - - -def readstatus(): - status = {} - glo = {} - loc = {} - for f in [SHARE + '/status/status', BYOBU_CONFIG_DIR + '/status']: - if os.path.exists(f): - try: - exec(open(f).read(), glo, loc) - except: - error("Invalid configuration [%s]" % f) - if BYOBU_BACKEND == "tmux": - items = "%s %s" % (loc["tmux_left"], loc["tmux_right"]) - else: - items = "%s %s %s %s" % (loc["screen_upper_left"], loc["screen_upper_right"], loc["screen_lower_left"], loc["screen_lower_right"]) - for i in items.split(): - if i.startswith("#"): - i = i.replace("#", "") - status[i] = "0" - else: - status[i] = "1" - li = [] - keys = status.keys() - for i in sorted(keys): - window = [int(status[i]), i] - li.append(window) - return li - - -def genstatusstring(s, status): - new = "" - glo = {} - loc = {} - exec(open(SHARE + '/status/status').read(), glo, loc) - for i in loc[s].split(): - if i.startswith("#"): - i = i.replace("#", "") - if status[i] == 1: - new += " " + i - else: - new += " #" + i - return new - - -def writestatus(items): - status = {} - path = BYOBU_CONFIG_DIR + '/status' - for i in items: - status[i[1]] = i[0] - for key in ["tmux_left", "tmux_right", "screen_upper_left", "screen_upper_right", "screen_lower_left", "screen_lower_right"]: - if key.startswith(BYOBU_BACKEND): - f = open(path, "r") - lines = f.readlines() - f.close() - f = open(path, "w") - for l in lines: - if l.startswith("%s=" % key): - val = genstatusstring(key, status) - f.write("%s=\"%s\"\n" % (key, val)) - else: - f.write(l) - f.close - - -def togglestatus(snackScreen, size): - itemlist = readstatus() - rl = Label("") - r = CheckboxTree(12, scroll=1) - count = 0 - for item in itemlist: - if item[0] != -1: - r.append(item[1], count, selected=item[0]) - count = count + 1 - bb = ButtonBar(snackScreen, ((_("Apply"), "apply"), (_("Cancel"), "cancel", ESC)), compact=1) - g = GridForm(snackScreen, _("Toggle status notifications"), 2, 4) - g.add(rl, 0, 0, anchorLeft=1, anchorTop=1, padding=(4, 0, 0, 1)) - g.add(r, 1, 0) - g.add(bb, 1, 1, padding=(4, 1, 0, 0)) - if bb.buttonPressed(g.runOnce()) != "cancel": - count = 0 - for item in itemlist: - if item[0] != -1: - item[0] = r.getEntryValue(count)[1] - count = count + 1 - writestatus(itemlist) - reload_required() - return 100 - - -def install(snackScreen, size, isInstalled): - out = "" - if isInstalled: - if subprocess.call(["byobu-launcher-uninstall"]) == 0: - out = _("Byobu will not be launched next time you login.") - button = messagebox(snackScreen, 60, 2, _("Message"), out, buttons=((_("Menu"), ))) - return 101 - else: - if subprocess.call(["byobu-launcher-install"]) == 0: - out = _("Byobu will be launched automatically next time you login.") - button = messagebox(snackScreen, 60, 2, "Message", out, buttons=((_("Menu"), ))) - return 100 - - -def appendtofile(p, s): - f = open(p, 'a') - try: - f.write(s) - except IOError: - f.close() - return - f.close() - return - - -def getesckey(): - line = "" - if BYOBU_BACKEND == "tmux": - path = BYOBU_CONFIG_DIR + '/keybindings.tmux' - if os.path.exists(path): - for l in open(path): - if l.startswith("set -g prefix "): - line = l - else: - return DEF_ESC - else: - path = BYOBU_CONFIG_DIR + '/keybindings' - if os.path.exists(path): - for l in open(path): - if l.startswith("escape "): - line = l - else: - return DEF_ESC - if line == "": - return DEF_ESC - esc = line[line.find('^') + 1] - if esc == "`": - esc = " " - return esc - - -def setesckey(key): - if key.isalpha(): - subprocess.call(["byobu-ctrl-a", "screen", key]) - - -def chgesc(snackScreen, size): - esc = Entry(2, text=getesckey(), returnExit=1) - escl = Label(_("Escape key: ctrl-")) - bb = ButtonBar(snackScreen, ((_("Apply"), "apply"), (_("Cancel"), "cancel", ESC)), compact=1) - g = GridForm(snackScreen, _("Change escape sequence"), 2, 4) - g.add(escl, 0, 0, anchorLeft=1, padding=(1, 0, 0, 1)) - g.add(esc, 1, 0, anchorLeft=1) - g.add(bb, 1, 1) - g.setTimer(100) - loop = 1 - while loop: - which = g.run() - if which == "TIMER": - val = esc.value() - if len(val) > 1: - esc.set(val[1]) - # Ensure that escape sequence is not \ or / - if val == '/' or val == '\\': - esc.set(DEF_ESC) - # Ensure that the escape sequence is not set to a number - try: - dummy = int(esc.value()) - esc.set(DEF_ESC) - except: - # do nothing - dummy = "foo" - else: - loop = 0 - snackScreen.popWindow() - button = bb.buttonPressed(which) - if button != "cancel": - setesckey(esc.value()) - reload_required() - if button == "exit": - return 0 - return 100 - - -def autolaunch(): - if os.path.exists(BYOBU_CONFIG_DIR + "/disable-autolaunch"): - return 0 - try: - for line in open("%s/.profile" % HOME): - if "byobu-launch" in line: - return 1 - except: - return 0 - if os.path.exists("/etc/profile.d/Z98-%s.sh" % PKG): - return 1 - return 0 - - -def main(): - """This is the main loop of our utility""" - size = terminal_size() - snackScreen = SnackScreen() - snackScreen.drawRootText(1, 0, _('Byobu Configuration Menu')) - snackScreen.pushHelpLine(_(' between elements | selects | exits')) - isInstalled = autolaunch() - tag = 100 - while tag > 0: - tag = menu(snackScreen, size, isInstalled) - if tag == 1: - tag = help(snackScreen, size) - elif tag == 2: - tag = togglestatus(snackScreen, size) - elif tag == 3: - tag = chgesc(snackScreen, size) - elif tag == 4: - tag = install(snackScreen, size, isInstalled) - isInstalled = autolaunch() - snackScreen.finish() - sys.exit(0) - - -if __name__ == "__main__": - main() +${BYOBU_PYTHON} "${BYOBU_PREFIX}/lib/${PKG}/include/config.py" diff --git a/usr/bin/byobu-select-session b/usr/bin/byobu-select-session index 6a939ce4..34bc552f 100755 --- a/usr/bin/byobu-select-session +++ b/usr/bin/byobu-select-session @@ -1,11 +1,9 @@ -#!/usr/bin/python +#!/bin/sh -e # -# byobu-select-session -# Copyright (C) 2010 Canonical Ltd. -# Copyright (C) 2012-2013 Dustin Kirkland +# byobu-select-session - interactive session select wrapper script +# Copyright (C) 2013 Dustin Kirkland # # Authors: Dustin Kirkland -# Ryan C. Thompson # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -19,156 +17,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +PKG="byobu" +[ -z "${BYOBU_PREFIX}" ] && export BYOBU_PREFIX="/usr" || export BYOBU_PREFIX +. "${BYOBU_PREFIX}/lib/${PKG}/include/common" -import os -import re -import sys -import subprocess - -PKG = "byobu" -SHELL = os.getenv("SHELL", "/bin/bash") -HOME = os.getenv("HOME") -BYOBU_CONFIG_DIR = os.getenv("BYOBU_CONFIG_DIR", HOME + "/.byobu") -BYOBU_BACKEND = os.getenv("BYOBU_BACKEND", "tmux") -choice = -1 -sessions = [] -text = [] - -BYOBU_UPDATE_ENVVARS = ["DISPLAY", "DBUS_SESSION_BUS_ADDRESS", "SESSION_MANAGER", "GPG_AGENT_INFO", "XDG_SESSION_COOKIE", "XDG_SESSION_PATH", "GNOME_KEYRING_CONTROL", "GNOME_KEYRING_PID", "GPG_AGENT_INFO", "SSH_ASKPASS", "SSH_AUTH_SOCK", "SSH_AGENT_PID", "WINDOWID", "UPSTART_JOB", "UPSTART_EVENTS", "UPSTART_SESSION", "UPSTART_INSTANCE"] - - -def get_sessions(): - sessions = [] - i = 0 - output = False - if BYOBU_BACKEND == "screen": - try: - output = subprocess.Popen(["screen", "-ls"], stdout=subprocess.PIPE).communicate()[0] - except subprocess.CalledProcessError as cpe: - # screen -ls seems to always return 1 - if cpe.returncode != 1: - raise - else: - output = cpe.output - if not sys.stdout.encoding is None: - output = output.decode(sys.stdout.encoding) - if output: - for s in output.splitlines(): - s = re.sub(r'\s+', ' ', s) - # Ignore hidden sessions (named sessions that start with a ".") - if s and s != " " and (s.find(" ") == 0 and len(s) > 1 and s.count("..") == 0): - text.append("screen: %s" % s.strip()) - items = s.split(" ") - sessions.append("screen____%s" % items[1]) - i += 1 - if BYOBU_BACKEND == "tmux": - output = subprocess.Popen(["tmux", "list-sessions"], stdout=subprocess.PIPE).communicate()[0] - output = output.decode(sys.stdout.encoding) - if output: - for s in output.splitlines(): - if s: - text.append("tmux: %s" % s.strip()) - sessions.append("tmux____%s" % s.split(":")[0]) - i += 1 - return sessions - - -def update_environment(session): - backend, session_name = session.split("____", 2) - for var in BYOBU_UPDATE_ENVVARS: - value = os.getenv(var) - if value: - if backend == "tmux": - cmd = ["tmux", "setenv", "-t", session_name, var, value] - else: - cmd = ["screen", "-S", session_name, "-X", "setenv", var, value] - print("Sending variable: %s" % (cmd, )) - subprocess.call(cmd, stdout=open(os.devnull, "w")) - - -def attach_session(session): - print("Attaching: [%s]\n" % session) - update_environment(session) - backend, session_name = session.split("____", 2) - # must use the binary, not the wrapper! - if backend == "tmux": - os.execvp("tmux", ["", "-2", "attach", "-t", session_name]) - else: - os.execvp("screen", ["", "-AOxRR", session_name]) - -# Confirm nested session, if necessary -if os.getenv("BYOBU_NESTING", "0") != "1": - if (BYOBU_BACKEND == "tmux" and os.getenv("TMUX")) or (BYOBU_BACKEND == "screen" and "screen" in os.getenv("TERM")): - sys.stderr.write("WARNING: Sessions should be nested with care.\n") - try: - nest = raw_input("Are you sure you want to run Byobu inside another session? [y/N]: ") - if nest != "Y" and nest != "y": - sys.exit(1) - else: - if os.getenv("TMUX"): - os.unsetenv("TMUX") - except KeyboardInterrupt: - sys.stdout.write("\n") - sys.exit(1) - except: - sys.stdout.write("\n") - sys.exit(1) - -sessions = get_sessions() - -show_shell = os.path.exists("%s/.always-select" % (BYOBU_CONFIG_DIR)) -if len(sessions) > 1 or show_shell: - sessions.append("NEW") - text.append("Create a new Byobu session (%s)" % BYOBU_BACKEND) - sessions.append("SHELL") - text.append("Run a shell without Byobu (%s)" % SHELL) - -if len(sessions) > 1: - sys.stdout.write("\nByobu sessions...\n\n") - tries = 0 - while tries < 3: - i = 1 - for s in text: - sys.stdout.write(" %d. %s\n" % (i, s)) - i += 1 - try: - choice = int(input("\nChoose 1-%d [1]: " % (i - 1))) - if choice >= 1 and choice < i: - break - else: - tries += 1 - choice = -1 - sys.stderr.write("\nERROR: Invalid input\n") - except KeyboardInterrupt: - sys.stdout.write("\n") - sys.exit(0) - except: - if choice == "" or choice == -1: - choice = 1 - break - tries += 1 - choice = -1 - sys.stderr.write("\nERROR: Invalid input\n") -elif len(sessions) == 1: - # Auto-select the only session - choice = 1 - -if choice >= 1: - if sessions[choice - 1] == "NEW": - # Create a new session - if BYOBU_BACKEND == "tmux": - os.execvp("byobu", ["", "new-session", SHELL]) - else: - os.execvp("byobu", ["", SHELL]) - elif sessions[choice - 1] == "SHELL": - os.execvp(SHELL, [SHELL]) - else: - # Attach to the chosen session; must use the binary, not the wrapper! - attach_session(sessions[choice - 1]) - -# No valid selection, default to the youngest session, create if necessary -if BYOBU_BACKEND == "tmux": - args = "" -else: - args = "-AOxRR" -os.execvp("byobu", ["", args]) +${BYOBU_PYTHON} "${BYOBU_PREFIX}/lib/${PKG}/include/select-session.py" diff --git a/usr/lib/byobu/include/Makefile.am b/usr/lib/byobu/include/Makefile.am index 5b3beedb..51b9c607 100644 --- a/usr/lib/byobu/include/Makefile.am +++ b/usr/lib/byobu/include/Makefile.am @@ -1,2 +1,2 @@ inclibdirdir = $(prefix)/lib/@PACKAGE@/include -inclibdir_SCRIPTS = common constants cycle-status dirs icons mondrian notify_osd shutil ec2instancespricing.py tmux-detach-all-but-current-client toggle-utf8 +inclibdir_SCRIPTS = common config.py constants cycle-status dirs icons mondrian notify_osd shutil ec2instancespricing.py select-session.py tmux-detach-all-but-current-client toggle-utf8 diff --git a/usr/lib/byobu/include/config.py b/usr/lib/byobu/include/config.py new file mode 100755 index 00000000..a51efba8 --- /dev/null +++ b/usr/lib/byobu/include/config.py @@ -0,0 +1,385 @@ +#!/usr/bin/python +# +# config.py +# Copyright (C) 2008 Canonical Ltd. +# Copyright (C) 2008-2013 Dustin Kirkland + +# +# Authors: Nick Barcet +# Dustin Kirkland +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# If you change any strings, please generate localization information with: +# ./debian/rules get-po + +import sys +import os +import os.path +import time +import string +import subprocess +import gettext +import glob + + +def error(msg): + print("ERROR: %s" % msg) + sys.exit(1) + + +try: + import snack + from snack import * +except: + error("Could not import the python snack module") + + +PKG = "byobu" +HOME = os.getenv("HOME") +USER = os.getenv("USER") +BYOBU_CONFIG_DIR = os.getenv("BYOBU_CONFIG_DIR", HOME + "/.byobu") +BYOBU_RUN_DIR = os.getenv("BYOBU_RUN_DIR", HOME + "/.cache/byobu") +BYOBU_BACKEND = os.getenv("BYOBU_BACKEND", "tmux") +BYOBU_SOCKETDIR = os.getenv("SOCKETDIR", "/var/run/screen") +BYOBU_PREFIX = os.getenv("BYOBU_PREFIX", "/usr") +SHARE = BYOBU_PREFIX + '/share/' + PKG +DOC = BYOBU_PREFIX + '/share/doc/' + PKG +if not os.path.exists(SHARE): + SHARE = BYOBU_CONFIG_DIR + "/" + SHARE +if not os.path.exists(DOC): + DOC = BYOBU_CONFIG_DIR + "/" + DOC +DEF_ESC = "A" +RELOAD = "If you are using the default set of keybindings, press\n or to activate these changes.\n\nOtherwise, exit this session and start a new one." +RELOAD_FLAG = "%s/reload-required" % (BYOBU_RUN_DIR) +ESC = '' +snack.hotkeys[ESC] = ord(ESC) +snack.hotkeys[ord(ESC)] = ESC +gettext.bindtextdomain(PKG, SHARE + '/po') +gettext.textdomain(PKG) +_ = gettext.gettext + + +def ioctl_GWINSZ(fd): + # Discover terminal width + try: + import fcntl + import termios + import struct + import os + cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) + except: + return None + return cr + + +def reload_required(): + try: + if not os.path.exists(BYOBU_CONFIG_DIR): + # 493 (decimal) is 0755 (octal) + # Use decimal for portability across all python versions + os.makedirs(BYOBU_CONFIG_DIR, 493) + f = open(RELOAD_FLAG, 'w') + f.close() + if BYOBU_BACKEND == "screen": + subprocess.call([BYOBU_BACKEND, "-X", "at", "0", "source", "%s/profile" % BYOBU_CONFIG_DIR]) + except: + True + + +def terminal_size(): + # decide on some terminal size + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) + # try open fds + if not cr: + # ...then ctty + try: + fd = os.open(os.ctermid(), os.O_RDONLY) + cr = ioctl_GWINSZ(fd) + os.close(fd) + except: + pass + if not cr: + # env vars or finally defaults + try: + cr = (env['LINES'], env['COLUMNS']) + except: + cr = (25, 80) + # reverse rows, cols + return int(cr[1] - 5), int(cr[0] - 5) + + +def menu(snackScreen, size, isInstalled): + if isInstalled: + installtext = _("Byobu currently launches at login (toggle off)") + else: + installtext = _("Byobu currently does not launch at login (toggle on)") + li = Listbox(height=6, width=60, returnExit=1) + li.append(_("Help -- Quick Start Guide"), 1) + li.append(_("Toggle status notifications"), 2) + li.append(_("Change escape sequence"), 3) + li.append(installtext, 4) + bb = ButtonBar(snackScreen, (("Exit", "exit", ESC),), compact=1) + g = GridForm(snackScreen, _(" Byobu Configuration Menu"), 1, 2) + g.add(li, 0, 0, padding=(4, 2, 4, 2)) + g.add(bb, 0, 1, padding=(1, 1, 0, 0)) + if bb.buttonPressed(g.runOnce()) == "exit": + return 0 + else: + return li.current() + + +def messagebox(snackScreen, width, height, title, text, scroll=0, buttons=((_("Okay"), "okay"), (_("Cancel"), "cancel", ESC))): + t = Textbox(width, height, text, scroll=scroll) + bb = ButtonBar(snackScreen, buttons, compact=1) + g = GridForm(snackScreen, title, 1, 2) + g.add(t, 0, 0, padding=(0, 0, 0, 0)) + g.add(bb, 0, 1, padding=(1, 1, 0, 0)) + return bb.buttonPressed(g.runOnce()) + + +def help(snackScreen, size): + f = open(DOC + '/help.' + BYOBU_BACKEND + '.txt') + text = f.read() + f.close() + text = text.replace("", getesckey(), 1) + t = Textbox(67, 16, text, scroll=1, wrap=1) + bb = ButtonBar(snackScreen, ((_("Menu"), "menu", ESC),), compact=1) + g = GridForm(snackScreen, _("Byobu Help"), 2, 4) + g.add(t, 1, 0) + g.add(bb, 1, 1, padding=(1, 1, 0, 0)) + button = bb.buttonPressed(g.runOnce()) + return 100 + + +def readstatus(): + status = {} + glo = {} + loc = {} + for f in [SHARE + '/status/status', BYOBU_CONFIG_DIR + '/status']: + if os.path.exists(f): + try: + exec(open(f).read(), glo, loc) + except: + error("Invalid configuration [%s]" % f) + if BYOBU_BACKEND == "tmux": + items = "%s %s" % (loc["tmux_left"], loc["tmux_right"]) + else: + items = "%s %s %s %s" % (loc["screen_upper_left"], loc["screen_upper_right"], loc["screen_lower_left"], loc["screen_lower_right"]) + for i in items.split(): + if i.startswith("#"): + i = i.replace("#", "") + status[i] = "0" + else: + status[i] = "1" + li = [] + keys = status.keys() + for i in sorted(keys): + window = [int(status[i]), i] + li.append(window) + return li + + +def genstatusstring(s, status): + new = "" + glo = {} + loc = {} + exec(open(SHARE + '/status/status').read(), glo, loc) + for i in loc[s].split(): + if i.startswith("#"): + i = i.replace("#", "") + if status[i] == 1: + new += " " + i + else: + new += " #" + i + return new + + +def writestatus(items): + status = {} + path = BYOBU_CONFIG_DIR + '/status' + for i in items: + status[i[1]] = i[0] + for key in ["tmux_left", "tmux_right", "screen_upper_left", "screen_upper_right", "screen_lower_left", "screen_lower_right"]: + if key.startswith(BYOBU_BACKEND): + f = open(path, "r") + lines = f.readlines() + f.close() + f = open(path, "w") + for l in lines: + if l.startswith("%s=" % key): + val = genstatusstring(key, status) + f.write("%s=\"%s\"\n" % (key, val)) + else: + f.write(l) + f.close + + +def togglestatus(snackScreen, size): + itemlist = readstatus() + rl = Label("") + r = CheckboxTree(12, scroll=1) + count = 0 + for item in itemlist: + if item[0] != -1: + r.append(item[1], count, selected=item[0]) + count = count + 1 + bb = ButtonBar(snackScreen, ((_("Apply"), "apply"), (_("Cancel"), "cancel", ESC)), compact=1) + g = GridForm(snackScreen, _("Toggle status notifications"), 2, 4) + g.add(rl, 0, 0, anchorLeft=1, anchorTop=1, padding=(4, 0, 0, 1)) + g.add(r, 1, 0) + g.add(bb, 1, 1, padding=(4, 1, 0, 0)) + if bb.buttonPressed(g.runOnce()) != "cancel": + count = 0 + for item in itemlist: + if item[0] != -1: + item[0] = r.getEntryValue(count)[1] + count = count + 1 + writestatus(itemlist) + reload_required() + return 100 + + +def install(snackScreen, size, isInstalled): + out = "" + if isInstalled: + if subprocess.call(["byobu-launcher-uninstall"]) == 0: + out = _("Byobu will not be launched next time you login.") + button = messagebox(snackScreen, 60, 2, _("Message"), out, buttons=((_("Menu"), ))) + return 101 + else: + if subprocess.call(["byobu-launcher-install"]) == 0: + out = _("Byobu will be launched automatically next time you login.") + button = messagebox(snackScreen, 60, 2, "Message", out, buttons=((_("Menu"), ))) + return 100 + + +def appendtofile(p, s): + f = open(p, 'a') + try: + f.write(s) + except IOError: + f.close() + return + f.close() + return + + +def getesckey(): + line = "" + if BYOBU_BACKEND == "tmux": + path = BYOBU_CONFIG_DIR + '/keybindings.tmux' + if os.path.exists(path): + for l in open(path): + if l.startswith("set -g prefix "): + line = l + else: + return DEF_ESC + else: + path = BYOBU_CONFIG_DIR + '/keybindings' + if os.path.exists(path): + for l in open(path): + if l.startswith("escape "): + line = l + else: + return DEF_ESC + if line == "": + return DEF_ESC + esc = line[line.find('^') + 1] + if esc == "`": + esc = " " + return esc + + +def setesckey(key): + if key.isalpha(): + subprocess.call(["byobu-ctrl-a", "screen", key]) + + +def chgesc(snackScreen, size): + esc = Entry(2, text=getesckey(), returnExit=1) + escl = Label(_("Escape key: ctrl-")) + bb = ButtonBar(snackScreen, ((_("Apply"), "apply"), (_("Cancel"), "cancel", ESC)), compact=1) + g = GridForm(snackScreen, _("Change escape sequence"), 2, 4) + g.add(escl, 0, 0, anchorLeft=1, padding=(1, 0, 0, 1)) + g.add(esc, 1, 0, anchorLeft=1) + g.add(bb, 1, 1) + g.setTimer(100) + loop = 1 + while loop: + which = g.run() + if which == "TIMER": + val = esc.value() + if len(val) > 1: + esc.set(val[1]) + # Ensure that escape sequence is not \ or / + if val == '/' or val == '\\': + esc.set(DEF_ESC) + # Ensure that the escape sequence is not set to a number + try: + dummy = int(esc.value()) + esc.set(DEF_ESC) + except: + # do nothing + dummy = "foo" + else: + loop = 0 + snackScreen.popWindow() + button = bb.buttonPressed(which) + if button != "cancel": + setesckey(esc.value()) + reload_required() + if button == "exit": + return 0 + return 100 + + +def autolaunch(): + if os.path.exists(BYOBU_CONFIG_DIR + "/disable-autolaunch"): + return 0 + try: + for line in open("%s/.profile" % HOME): + if "byobu-launch" in line: + return 1 + except: + return 0 + if os.path.exists("/etc/profile.d/Z98-%s.sh" % PKG): + return 1 + return 0 + + +def main(): + """This is the main loop of our utility""" + size = terminal_size() + snackScreen = SnackScreen() + snackScreen.drawRootText(1, 0, _('Byobu Configuration Menu')) + snackScreen.pushHelpLine(_(' between elements | selects | exits')) + isInstalled = autolaunch() + tag = 100 + while tag > 0: + tag = menu(snackScreen, size, isInstalled) + if tag == 1: + tag = help(snackScreen, size) + elif tag == 2: + tag = togglestatus(snackScreen, size) + elif tag == 3: + tag = chgesc(snackScreen, size) + elif tag == 4: + tag = install(snackScreen, size, isInstalled) + isInstalled = autolaunch() + snackScreen.finish() + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/usr/lib/byobu/include/constants b/usr/lib/byobu/include/constants index afefc54f..34b45f0d 100755 --- a/usr/lib/byobu/include/constants +++ b/usr/lib/byobu/include/constants @@ -53,6 +53,17 @@ $BYOBU_SED --follow-symlinks "s///" /dev/null && BYOBU_SED="$BYOBU_ # Determine if we have ulimit support $BYOBU_TEST ulimit >/dev/null 2>&1 && export BYOBU_ULIMIT="ulimit" || export BYOBU_ULIMIT="false" +# Find a suitable python interpreter, if undefined +if [ -z "$BYOBU_PYTHON" ]; then + if $BYOBU_TEST python >/dev/null 2>&1; then + BYOBU_PYTHON="python" + elif $BYOBU_TEST python2 >/dev/null 2>&1; then + BYOBU_PYTHON="python2" + elif $BYOBU_TEST python3 >/dev/null 2>&1; then + BYOBU_PYTHON="python3" + fi +fi + # Default colors export BYOBU_DARK="black" export BYOBU_LIGHT="white" diff --git a/usr/lib/byobu/include/select-session.py b/usr/lib/byobu/include/select-session.py new file mode 100755 index 00000000..47f75b9e --- /dev/null +++ b/usr/lib/byobu/include/select-session.py @@ -0,0 +1,174 @@ +#!/usr/bin/python +# +# select-session.py +# Copyright (C) 2010 Canonical Ltd. +# Copyright (C) 2012-2013 Dustin Kirkland +# +# Authors: Dustin Kirkland +# Ryan C. Thompson +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +import os +import re +import sys +import subprocess + +PKG = "byobu" +SHELL = os.getenv("SHELL", "/bin/bash") +HOME = os.getenv("HOME") +BYOBU_CONFIG_DIR = os.getenv("BYOBU_CONFIG_DIR", HOME + "/.byobu") +BYOBU_BACKEND = os.getenv("BYOBU_BACKEND", "tmux") +choice = -1 +sessions = [] +text = [] + +BYOBU_UPDATE_ENVVARS = ["DISPLAY", "DBUS_SESSION_BUS_ADDRESS", "SESSION_MANAGER", "GPG_AGENT_INFO", "XDG_SESSION_COOKIE", "XDG_SESSION_PATH", "GNOME_KEYRING_CONTROL", "GNOME_KEYRING_PID", "GPG_AGENT_INFO", "SSH_ASKPASS", "SSH_AUTH_SOCK", "SSH_AGENT_PID", "WINDOWID", "UPSTART_JOB", "UPSTART_EVENTS", "UPSTART_SESSION", "UPSTART_INSTANCE"] + + +def get_sessions(): + sessions = [] + i = 0 + output = False + if BYOBU_BACKEND == "screen": + try: + output = subprocess.Popen(["screen", "-ls"], stdout=subprocess.PIPE).communicate()[0] + except subprocess.CalledProcessError as cpe: + # screen -ls seems to always return 1 + if cpe.returncode != 1: + raise + else: + output = cpe.output + if not sys.stdout.encoding is None: + output = output.decode(sys.stdout.encoding) + if output: + for s in output.splitlines(): + s = re.sub(r'\s+', ' ', s) + # Ignore hidden sessions (named sessions that start with a ".") + if s and s != " " and (s.find(" ") == 0 and len(s) > 1 and s.count("..") == 0): + text.append("screen: %s" % s.strip()) + items = s.split(" ") + sessions.append("screen____%s" % items[1]) + i += 1 + if BYOBU_BACKEND == "tmux": + output = subprocess.Popen(["tmux", "list-sessions"], stdout=subprocess.PIPE).communicate()[0] + output = output.decode(sys.stdout.encoding) + if output: + for s in output.splitlines(): + if s: + text.append("tmux: %s" % s.strip()) + sessions.append("tmux____%s" % s.split(":")[0]) + i += 1 + return sessions + + +def update_environment(session): + backend, session_name = session.split("____", 2) + for var in BYOBU_UPDATE_ENVVARS: + value = os.getenv(var) + if value: + if backend == "tmux": + cmd = ["tmux", "setenv", "-t", session_name, var, value] + else: + cmd = ["screen", "-S", session_name, "-X", "setenv", var, value] + print("Sending variable: %s" % (cmd, )) + subprocess.call(cmd, stdout=open(os.devnull, "w")) + + +def attach_session(session): + print("Attaching: [%s]\n" % session) + update_environment(session) + backend, session_name = session.split("____", 2) + # must use the binary, not the wrapper! + if backend == "tmux": + os.execvp("tmux", ["", "-2", "attach", "-t", session_name]) + else: + os.execvp("screen", ["", "-AOxRR", session_name]) + +# Confirm nested session, if necessary +if os.getenv("BYOBU_NESTING", "0") != "1": + if (BYOBU_BACKEND == "tmux" and os.getenv("TMUX")) or (BYOBU_BACKEND == "screen" and "screen" in os.getenv("TERM")): + sys.stderr.write("WARNING: Sessions should be nested with care.\n") + try: + nest = raw_input("Are you sure you want to run Byobu inside another session? [y/N]: ") + if nest != "Y" and nest != "y": + sys.exit(1) + else: + if os.getenv("TMUX"): + os.unsetenv("TMUX") + except KeyboardInterrupt: + sys.stdout.write("\n") + sys.exit(1) + except: + sys.stdout.write("\n") + sys.exit(1) + +sessions = get_sessions() + +show_shell = os.path.exists("%s/.always-select" % (BYOBU_CONFIG_DIR)) +if len(sessions) > 1 or show_shell: + sessions.append("NEW") + text.append("Create a new Byobu session (%s)" % BYOBU_BACKEND) + sessions.append("SHELL") + text.append("Run a shell without Byobu (%s)" % SHELL) + +if len(sessions) > 1: + sys.stdout.write("\nByobu sessions...\n\n") + tries = 0 + while tries < 3: + i = 1 + for s in text: + sys.stdout.write(" %d. %s\n" % (i, s)) + i += 1 + try: + choice = int(input("\nChoose 1-%d [1]: " % (i - 1))) + if choice >= 1 and choice < i: + break + else: + tries += 1 + choice = -1 + sys.stderr.write("\nERROR: Invalid input\n") + except KeyboardInterrupt: + sys.stdout.write("\n") + sys.exit(0) + except: + if choice == "" or choice == -1: + choice = 1 + break + tries += 1 + choice = -1 + sys.stderr.write("\nERROR: Invalid input\n") +elif len(sessions) == 1: + # Auto-select the only session + choice = 1 + +if choice >= 1: + if sessions[choice - 1] == "NEW": + # Create a new session + if BYOBU_BACKEND == "tmux": + os.execvp("byobu", ["", "new-session", SHELL]) + else: + os.execvp("byobu", ["", SHELL]) + elif sessions[choice - 1] == "SHELL": + os.execvp(SHELL, [SHELL]) + else: + # Attach to the chosen session; must use the binary, not the wrapper! + attach_session(sessions[choice - 1]) + +# No valid selection, default to the youngest session, create if necessary +if BYOBU_BACKEND == "tmux": + args = "" +else: + args = "-AOxRR" +os.execvp("byobu", ["", args])