You need to sign in or sign up before continuing.
Commit 486cc6f5 authored by Ulrich Sibiller's avatar Ulrich Sibiller Committed by Mike Gabriel

add nxdialog for displaying dialogs

By adding NX_CLIENT=<path to nxdialog> to the envrionment certain actions in nx will open dialogs. If the options string contains "menu=1" rootless windows will show a popup menu when the mouse hits the center of the title bar offering session disconnect/terminatation.
parent 4538d213
#!/usr/bin/env python2
#
# Copyright (C) 2008 Google Inc.
# Copyright (C) 2019 Ulrich Sibiller <uli42@gmx.de>
#
# 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; either version 2 of the License, or
# (at your option) any later version.
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
# This version is based on the nxdialog.py of the long abandoned
# Google project "neatx" (https://code.google.com/archive/p/neatx/).
# List of changes:
# - pulled in the few parts of the neatx python modules that are actually
# to make it a standlone script
# - added usage output
# - dropped logging code, print errors to stderr
# - can handle the "yesno" dialog type
# - added missing docstrings
# - pylint improvements
# - removed neatx entry from the pulldoww menu
"""nxdialog program for handling dialog display."""
# If an "NX_CLIENT" environment variable is not provided to nxagent
# nxcomp library assumes this script is located in /usr/NX/bin/nxclient
import optparse
import os
import signal
import sys
import gtk
import pygtk
pygtk.require("2.0")
PROGRAM = "nxdialog"
DISCONNECT = 1
TERMINATE = 2
EXIT_SUCCESS = 0
EXIT_FAILURE = 1
CANCEL_TEXT = "Cancel"
DISCONNECT_TEXT = "Disconnect"
TERMINATE_TEXT = "Terminate"
YES_TEXT = "Yes"
NO_TEXT = "No"
DLG_TYPE_ERROR = "error"
DLG_TYPE_OK = "ok"
DLG_TYPE_PANIC = "panic"
DLG_TYPE_PULLDOWN = "pulldown"
DLG_TYPE_QUIT = "quit"
DLG_TYPE_YESNO = "yesno"
DLG_TYPE_YESNOSUSPEND = "yesnosuspend"
VALID_DLG_TYPES = frozenset([
DLG_TYPE_ERROR,
DLG_TYPE_OK,
DLG_TYPE_PANIC,
DLG_TYPE_PULLDOWN,
DLG_TYPE_QUIT,
DLG_TYPE_YESNO,
DLG_TYPE_YESNOSUSPEND,
])
class PullDownMenu(object):
""" Shows a popup menu to disconnect/terminate session. """
def __init__(self, window_id):
""" Initializes this class.
@type window_id: int
@param window_id: X11 window id of target window
"""
self._window_id = window_id
self._result = None
def Show(self):
""" Shows popup and returns result. """
win = gtk.gdk.window_foreign_new(self._window_id)
menu = gtk.Menu()
menu.connect("deactivate", self._MenuDeactivate)
# TODO: Show title item in bold font
title = gtk.MenuItem(label="Session control")
title.set_sensitive(False)
menu.append(title)
disconnect = gtk.MenuItem(label=DISCONNECT_TEXT)
disconnect.connect("activate", self._ItemActivate, DISCONNECT)
menu.append(disconnect)
terminate = gtk.MenuItem(label=TERMINATE_TEXT)
terminate.connect("activate", self._ItemActivate, TERMINATE)
menu.append(terminate)
menu.append(gtk.SeparatorMenuItem())
cancel = gtk.MenuItem(label=CANCEL_TEXT)
menu.append(cancel)
menu.show_all()
menu.popup(parent_menu_shell=None, parent_menu_item=None,
func=self._PosMenu, data=win,
button=0, activate_time=gtk.get_current_event_time())
gtk.main()
return self._result
def _ItemActivate(self, _, result):
""" called when a menu item is selected """
self._result = result
gtk.main_quit()
def _MenuDeactivate(self, _):
""" called when menu is deactivated """
gtk.main_quit()
def _PosMenu(self, menu, parent):
""" Positions menu at the top center of the parent window. """
# Get parent geometry and origin
(_, _, win_width, _, _) = parent.get_geometry()
(win_x, win_y) = parent.get_origin()
# Calculate width of menu
(menu_width, _) = menu.size_request()
# Calculate center
x = win_x + ((win_width - menu_width) / 2)
return (x, win_y, True)
def ShowYesNoSuspendBox(title, text):
""" Shows a message box to disconnect/terminate session.
@type title: str
@param title: Message box title
@type text: str
@param text: Message box text
@return: Choosen action
"""
dlg = gtk.MessageDialog(type=gtk.MESSAGE_QUESTION, flags=gtk.DIALOG_MODAL)
dlg.set_title(title)
dlg.set_markup(text)
dlg.add_button(DISCONNECT_TEXT, DISCONNECT)
dlg.add_button(TERMINATE_TEXT, TERMINATE)
dlg.add_button(CANCEL_TEXT, gtk.RESPONSE_CANCEL)
res = dlg.run()
if res in (DISCONNECT, TERMINATE):
return res
# Everything else is cancel
return None
def ShowYesNoBox(title, text):
""" Shows a message box with answers yes and no.
@type title: str
@param title: Message box title
@type text: str
@param text: Message box text
@return: Choosen action
"""
dlg = gtk.MessageDialog(type=gtk.MESSAGE_QUESTION, flags=gtk.DIALOG_MODAL)
dlg.set_title(title)
dlg.set_markup(text)
dlg.add_button(YES_TEXT, TERMINATE)
dlg.add_button(NO_TEXT, gtk.RESPONSE_CANCEL)
res = dlg.run()
if res == TERMINATE:
return res
# Everything else is cancel
return None
def HandleSessionAction(agentpid, action):
""" Execute session action choosen by user.
@type agentpid: int
@param agentpid: Nxagent process id as passed by command line
@type action: int or None
@param action: Choosen action
"""
if action == DISCONNECT:
print "Disconnecting from session, sending SIGHUP to %s" % (agentpid)
os.kill(agentpid, signal.SIGHUP)
elif action == TERMINATE:
print "Terminating session, sending SIGTERM to process %s" % (agentpid)
os.kill(agentpid, signal.SIGTERM)
elif action is None:
pass
else:
raise NotImplementedError()
def ShowSimpleMessageBox(icon, title, text):
""" Shows a simple message box.
@type icon: QMessageBox.Icon
@param icon: Icon for message box
@type title: str
@param title: Message box title
@type text: str
@param text: Message box text
"""
dlg = gtk.MessageDialog(type=icon, flags=gtk.DIALOG_MODAL,
buttons=gtk.BUTTONS_OK)
dlg.set_title(title)
dlg.set_markup(text)
dlg.run()
class NxDialogProgram(object):
""" the main program """
def __init__(self):
self.args = None
self.options = None
def Main(self):
""" let's do something """
try:
(self.options, self.args) = self.ParseArgs()
self.Run()
except (SystemExit, KeyboardInterrupt):
raise
except Exception, e:
sys.stderr.write("Caught exception: %s\n" % (e))
sys.exit(EXIT_FAILURE)
def ParseArgs(self):
""" init parser """
parser = optparse.OptionParser(option_list=self.BuildOptions(),
formatter=optparse.TitledHelpFormatter())
return parser.parse_args()
def BuildOptions(self):
""" build options for the parser """
return [
# nxagent 3.5.99.18 only uses yesno, ok, pulldown and yesnosuspend
# yesno dialogs will always kill the session if "yes" is selected
optparse.make_option("--dialog", type="string", dest="dialog_type",
help='type of dialog to show, one of "yesno", \
"ok", "error", "panic", "quit", "pulldown", \
"yesnosuspend"'),
optparse.make_option("--message", type="string", dest="text",
help="message text to display in the dialog"),
optparse.make_option("--caption", type="string", dest="caption",
help="window title of the dialog"),
optparse.make_option("--display", type="string", dest="display",
help="X11 display where the dialog should be \
shown"),
optparse.make_option("--parent", type="int", dest="agentpid",
help="pid of the nxagent"),
optparse.make_option("--window", type="int", dest="window",
help="id of window where to embed the \
pulldown dialog type"),
# -class, -local, -allowmultiple are unused in nxlibs 3.5.99.18
optparse.make_option("--class", type="string", dest="dlgclass",
default="info",
help="class of the message (info, warning, error) \
default: info) [currently unimplemented]"),
optparse.make_option("--local", action="store_true", dest="local",
help="specify that proxy mode is used \
[currently unimplemented]"),
optparse.make_option("--allowmultiple", action="store_true",
dest="allowmultiple",
help="allow launching more than one dialog with \
the same message [currently unimplemented]"),
]
def Run(self):
""" Disconnect/terminate NX session upon user's request. """
if not self.options.dialog_type:
sys.stderr.write("Dialog type not supplied via --type\n")
sys.exit(EXIT_FAILURE)
dlgtype = self.options.dialog_type
if dlgtype not in VALID_DLG_TYPES:
sys.stderr.write("Invalid dialog type '%s'\n" % (dlgtype))
sys.exit(EXIT_FAILURE)
if dlgtype in (DLG_TYPE_PULLDOWN,
DLG_TYPE_YESNOSUSPEND,
DLG_TYPE_YESNO) and not self.options.agentpid:
sys.stderr.write("Agent pid not supplied via --parent\n")
sys.exit(EXIT_FAILURE)
if dlgtype == DLG_TYPE_PULLDOWN and not self.options.window:
sys.stderr.write("Window id not supplied via --window\n")
sys.exit(EXIT_FAILURE)
if self.options.caption:
message_caption = self.options.caption
else:
message_caption = sys.argv[0]
if self.options.text:
message_text = self.options.text
else:
message_text = ""
if self.options.display:
os.environ["DISPLAY"] = self.options.display
if dlgtype == DLG_TYPE_OK:
ShowSimpleMessageBox(gtk.MESSAGE_INFO, message_caption, message_text)
elif dlgtype in (DLG_TYPE_ERROR, DLG_TYPE_PANIC):
ShowSimpleMessageBox(gtk.MESSAGE_ERROR, message_caption, message_text)
elif dlgtype == DLG_TYPE_PULLDOWN:
HandleSessionAction(self.options.agentpid,
PullDownMenu(self.options.window).Show())
elif dlgtype == DLG_TYPE_YESNOSUSPEND:
HandleSessionAction(self.options.agentpid,
ShowYesNoSuspendBox(message_caption, message_text))
elif dlgtype == DLG_TYPE_YESNO:
HandleSessionAction(self.options.agentpid,
ShowYesNoBox(message_caption, message_text))
else:
# TODO: Implement all dialog types
sys.stderr.write("Dialog type '%s' not implemented" % (dlgtype))
sys.exit(EXIT_FAILURE)
NxDialogProgram().Main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment