Merge heads
This commit is contained in:
commit
ef113c3313
1
README
1
README
|
@ -34,6 +34,7 @@ please report any success to the mailing lists
|
|||
gnome-python-desktop >= 2.15.4
|
||||
libxml2-python >= 2.6.23
|
||||
vte >= 0.12.2
|
||||
gtk-vnc >= 0.0.1
|
||||
python-virtinst >= 0.103.0
|
||||
|
||||
The latter is available from
|
||||
|
|
|
@ -30,7 +30,6 @@ AC_OUTPUT(Makefile
|
|||
po/Makefile.in
|
||||
src/Makefile
|
||||
src/virtManager/Makefile
|
||||
src/vncViewer/Makefile
|
||||
src/graphWidgets/Makefile
|
||||
man/Makefile
|
||||
virt-manager.spec
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
SUBDIRS = vncViewer graphWidgets virtManager
|
||||
SUBDIRS = graphWidgets virtManager
|
||||
|
||||
bin_SOURCES = virt-manager.in
|
||||
bin_SCRIPTS = virt-manager
|
||||
|
|
|
@ -25,9 +25,14 @@ import sys
|
|||
import logging
|
||||
import dbus
|
||||
import traceback
|
||||
import gtkvnc
|
||||
|
||||
from virtManager.error import vmmErrorDialog
|
||||
from vncViewer.vnc import GRFBViewer
|
||||
|
||||
PAGE_UNAVAILABLE = 0
|
||||
PAGE_SCREENSHOT = 1
|
||||
PAGE_AUTHENTICATE = 2
|
||||
PAGE_VNCVIEWER = 3
|
||||
|
||||
class vmmConsole(gobject.GObject):
|
||||
__gsignals__ = {
|
||||
|
@ -55,15 +60,19 @@ class vmmConsole(gobject.GObject):
|
|||
self.window.get_widget("control-shutdown").set_icon_widget(gtk.Image())
|
||||
self.window.get_widget("control-shutdown").get_icon_widget().set_from_file(config.get_icon_dir() + "/icon_shutdown.png")
|
||||
|
||||
self.vncViewer = gtkvnc.Display()
|
||||
if self.config.get_console_keygrab() == 2:
|
||||
self.vncViewer = GRFBViewer(topwin, autograbkey=True)
|
||||
else:
|
||||
self.vncViewer = GRFBViewer(topwin, autograbkey=False)
|
||||
self.vncViewer.connect("pointer-grabbed", self.notify_grabbed)
|
||||
self.vncViewer.connect("pointer-ungrabbed", self.notify_ungrabbed)
|
||||
self.vncViewer.set_keyboard_grab(True)
|
||||
|
||||
self.vncViewer.set_pointer_grab(True)
|
||||
#self.vncViewer.set_sticky_modifiers(True)
|
||||
|
||||
self.vncViewer.connect("vnc-pointer-grab", self.notify_grabbed)
|
||||
self.vncViewer.connect("vnc-pointer-ungrab", self.notify_ungrabbed)
|
||||
|
||||
self.window.get_widget("console-vnc-align").add(self.vncViewer)
|
||||
self.vncViewer.connect("size-request", self.autosize)
|
||||
self.vncViewer.realize()
|
||||
self.vncViewer.show()
|
||||
self.vncViewerFailures = 0
|
||||
self.vncViewerRetryDelay = 125
|
||||
|
@ -105,13 +114,15 @@ class vmmConsole(gobject.GObject):
|
|||
|
||||
"on_menu_vm_close_activate": self.close,
|
||||
|
||||
"on_console_auth_login_clicked": self.try_login,
|
||||
"on_console_auth_login_clicked": self.set_password,
|
||||
"on_console_help_activate": self.show_help,
|
||||
})
|
||||
|
||||
self.vm.connect("status-changed", self.update_widget_states)
|
||||
|
||||
self.vncViewer.connect("disconnected", self._vnc_disconnected)
|
||||
self.vncViewer.connect("vnc-auth-credential", self._vnc_auth_credential)
|
||||
self.vncViewer.connect("vnc-initialized", self._vnc_initialized)
|
||||
self.vncViewer.connect("vnc-disconnected", self._vnc_disconnected)
|
||||
|
||||
# Auto-increase the window size to fit the console - within reason
|
||||
# though, cos we don't want a min window size greater than the screen
|
||||
|
@ -168,18 +179,18 @@ class vmmConsole(gobject.GObject):
|
|||
|
||||
def keygrab_changed(self, src, ignore1=None,ignore2=None,ignore3=None):
|
||||
if self.config.get_console_keygrab() == 2:
|
||||
self.vncViewer.set_autograb_keyboard(True)
|
||||
self.vncViewer.set_keyboard_grab(True)
|
||||
else:
|
||||
self.vncViewer.set_autograb_keyboard(False)
|
||||
self.vncViewer.set_keyboard_grab(False)
|
||||
|
||||
def toggle_fullscreen(self, src):
|
||||
if src.get_active():
|
||||
self.window.get_widget("vmm-console").fullscreen()
|
||||
if self.config.get_console_keygrab() == 1:
|
||||
self.vncViewer.grab_keyboard()
|
||||
#if self.config.get_console_keygrab() == 1:
|
||||
# self.vncViewer.grab_keyboard()
|
||||
else:
|
||||
if self.config.get_console_keygrab() == 1:
|
||||
self.vncViewer.ungrab_keyboard()
|
||||
#if self.config.get_console_keygrab() == 1:
|
||||
# self.vncViewer.ungrab_keyboard()
|
||||
self.window.get_widget("vmm-console").unfullscreen()
|
||||
|
||||
def toggle_toolbar(self, src):
|
||||
|
@ -192,7 +203,6 @@ class vmmConsole(gobject.GObject):
|
|||
dialog = self.window.get_widget("vmm-console")
|
||||
dialog.show_all()
|
||||
dialog.present()
|
||||
self.try_login()
|
||||
self.update_widget_states(self.vm, self.vm.status())
|
||||
|
||||
def show_help(self, src):
|
||||
|
@ -206,9 +216,9 @@ class vmmConsole(gobject.GObject):
|
|||
fs.set_active(False)
|
||||
|
||||
self.window.get_widget("vmm-console").hide()
|
||||
if self.vncViewer.is_connected():
|
||||
if self.vncViewer.flags() & gtk.VISIBLE:
|
||||
try:
|
||||
self.vncViewer.disconnect_from_host()
|
||||
self.vncViewer.close()
|
||||
except:
|
||||
logging.error("Failure when disconnecting from VNC server")
|
||||
return 1
|
||||
|
@ -219,21 +229,45 @@ class vmmConsole(gobject.GObject):
|
|||
return 0
|
||||
|
||||
def _vnc_disconnected(self, src):
|
||||
if self.is_visible():
|
||||
self.try_login()
|
||||
logging.debug("VNC disconnected")
|
||||
self.vncViewerFailures = self.vncViewerFailures + 1
|
||||
self.activate_unavailable_page()
|
||||
if not self.is_visible():
|
||||
return
|
||||
|
||||
if self.vncViewerFailures < 10:
|
||||
self.schedule_retry()
|
||||
else:
|
||||
logging.error("Too many connection failures, not retrying again")
|
||||
|
||||
def _vnc_initialized(self, src):
|
||||
logging.debug("VNC initialized")
|
||||
self.activate_viewer_page()
|
||||
|
||||
# Had a succesfull connect, so reset counters now
|
||||
self.vncViewerFailures = 0
|
||||
self.vncViewerRetryDelay = 125
|
||||
|
||||
def schedule_retry(self):
|
||||
logging.warn("Retrying connection in %d ms", self.vncViewerRetryDelay)
|
||||
gobject.timeout_add(self.vncViewerRetryDelay, self.retry_login)
|
||||
if self.vncViewerRetryDelay < 2000:
|
||||
self.vncViewerRetryDelay = self.vncViewerRetryDelay * 2
|
||||
|
||||
def retry_login(self):
|
||||
gtk.gdk.threads_enter()
|
||||
try:
|
||||
logging.debug("Got timed retry")
|
||||
self.try_login()
|
||||
return False
|
||||
finally:
|
||||
gtk.gdk.threads_leave()
|
||||
|
||||
def try_login(self, src=None):
|
||||
if self.vm.get_id() == 0:
|
||||
if self.vm.get_id() <= 0:
|
||||
return
|
||||
|
||||
logging.debug("Trying console login")
|
||||
password = self.window.get_widget("console-auth-password").get_text()
|
||||
protocol, host, port = self.vm.get_graphics_console()
|
||||
|
||||
|
@ -244,56 +278,47 @@ class vmmConsole(gobject.GObject):
|
|||
uri = str(protocol) + "://" + str(host) + ":" + str(port)
|
||||
logging.debug("Graphics console configured at " + uri)
|
||||
|
||||
if int(port) == -1:
|
||||
self.schedule_retry()
|
||||
return
|
||||
|
||||
if protocol != "vnc":
|
||||
logging.debug("Not a VNC console, disabling")
|
||||
self.activate_unavailable_page()
|
||||
return
|
||||
|
||||
if not(self.vncViewer.is_connected()):
|
||||
logging.debug("Starting connect process for %s %s" % (host, str(port)))
|
||||
try:
|
||||
self.vncViewer.connect_to_host(host, port)
|
||||
self.vncViewer.open_host(host, str(port))
|
||||
except:
|
||||
self.vncViewerFailures = self.vncViewerFailures + 1
|
||||
logging.warn("Unable to activate console " + uri + ": " + str((sys.exc_info())[0]) + " " + str((sys.exc_info())[1]))
|
||||
self.activate_unavailable_page()
|
||||
if self.vncViewerFailures < 10:
|
||||
logging.warn("Retrying connection in %d ms", self.vncViewerRetryDelay)
|
||||
gobject.timeout_add(self.vncViewerRetryDelay, self.retry_login)
|
||||
if self.vncViewerRetryDelay < 2000:
|
||||
self.vncViewerRetryDelay = self.vncViewerRetryDelay * 2
|
||||
else:
|
||||
logging.error("Too many connection failures, not retrying again")
|
||||
(type, value, stacktrace) = sys.exc_info ()
|
||||
details = \
|
||||
"Unable to start virtual machine '%s'" % \
|
||||
(str(type) + " " + str(value) + "\n" + \
|
||||
traceback.format_exc (stacktrace))
|
||||
logging.error(details)
|
||||
|
||||
def set_password(self, src=None):
|
||||
logging.debug("Setting a password to " . str(src.get_text()))
|
||||
|
||||
self.vncViewer.set_credential(gtkvnc.CREDENTIAL_PASSWORD, src.get_text())
|
||||
|
||||
def _vnc_auth_credential(self, src, type):
|
||||
logging.debug("Got credential request %d", type)
|
||||
if type != gtkvnc.CREDENTIAL_PASSWORD:
|
||||
# Force it to stop re-trying
|
||||
self.vncViewerFailures = 10
|
||||
self.vncViewer.close()
|
||||
return
|
||||
|
||||
# Had a succesfull connect, so reset counters now
|
||||
self.vncViewerFailures = 0
|
||||
self.vncViewerRetryDelay = 125
|
||||
|
||||
if self.vncViewer.is_authenticated():
|
||||
self.activate_viewer_page()
|
||||
elif password or not(self.vncViewer.needs_password()):
|
||||
if self.vncViewer.authenticate(password) == 1:
|
||||
if self.window.get_widget("console-auth-remember").get_active():
|
||||
self.config.set_console_password(self.vm, password)
|
||||
else:
|
||||
self.config.clear_console_password(self.vm)
|
||||
self.activate_viewer_page()
|
||||
self.vncViewer.activate()
|
||||
else:
|
||||
# Our VNC console doesn't like it when password is
|
||||
# wrong and gets out of sync in its state machine
|
||||
# So we force disconnect
|
||||
self.vncViewer.disconnect_from_host()
|
||||
self.activate_auth_page()
|
||||
else:
|
||||
self.activate_auth_page()
|
||||
|
||||
|
||||
def activate_unavailable_page(self):
|
||||
self.window.get_widget("console-pages").set_current_page(0)
|
||||
self.window.get_widget("console-pages").set_current_page(PAGE_UNAVAILABLE)
|
||||
self.window.get_widget("menu-vm-screenshot").set_sensitive(False)
|
||||
|
||||
def activate_screenshot_page(self):
|
||||
self.window.get_widget("console-pages").set_current_page(1)
|
||||
self.window.get_widget("console-pages").set_current_page(PAGE_SCREENSHOT)
|
||||
self.window.get_widget("menu-vm-screenshot").set_sensitive(True)
|
||||
|
||||
def activate_auth_page(self):
|
||||
|
@ -309,10 +334,10 @@ class vmmConsole(gobject.GObject):
|
|||
self.window.get_widget("console-auth-remember").set_active(False)
|
||||
else:
|
||||
self.window.get_widget("console-auth-remember").set_sensitive(False)
|
||||
self.window.get_widget("console-pages").set_current_page(2)
|
||||
self.window.get_widget("console-pages").set_current_page(PAGE_AUTHENTICATE)
|
||||
|
||||
def activate_viewer_page(self):
|
||||
self.window.get_widget("console-pages").set_current_page(3)
|
||||
self.window.get_widget("console-pages").set_current_page(PAGE_VNCVIEWER)
|
||||
self.window.get_widget("menu-vm-screenshot").set_sensitive(True)
|
||||
self.vncViewer.grab_focus()
|
||||
|
||||
|
@ -467,13 +492,14 @@ class vmmConsole(gobject.GObject):
|
|||
self.window.get_widget("menu-vm-pause").set_active(False)
|
||||
|
||||
if status in [ libvirt.VIR_DOMAIN_SHUTOFF ,libvirt.VIR_DOMAIN_CRASHED ] or vm.is_management_domain():
|
||||
self.window.get_widget("console-pages").set_current_page(0)
|
||||
if self.window.get_widget("console-pages").get_current_page() != PAGE_UNAVAILABLE:
|
||||
self.vncViewer.close()
|
||||
self.window.get_widget("console-pages").set_current_page(PAGE_UNAVAILABLE)
|
||||
else:
|
||||
if status == libvirt.VIR_DOMAIN_PAUSED:
|
||||
screenshot = None
|
||||
if self.vncViewer.is_authenticated():
|
||||
if self.window.get_widget("console-pages").get_current_page() == PAGE_VNCVIEWER:
|
||||
screenshot = self.vncViewer.take_screenshot()
|
||||
if screenshot != None:
|
||||
cr = screenshot.cairo_create()
|
||||
width, height = screenshot.get_size()
|
||||
|
||||
|
@ -495,15 +521,14 @@ class vmmConsole(gobject.GObject):
|
|||
self.window.get_widget("console-screenshot").set_from_pixmap(screenshot, None)
|
||||
self.activate_screenshot_page()
|
||||
else:
|
||||
if self.window.get_widget("console-pages").get_current_page() != PAGE_UNAVAILABLE:
|
||||
self.vncViewer.close()
|
||||
self.activate_unavailable_page()
|
||||
else:
|
||||
# State changed, so better let it try connecting again
|
||||
if self.window.get_widget("console-pages").get_current_page() == PAGE_UNAVAILABLE:
|
||||
self.vncViewerFailures = 0
|
||||
self.vncViewerRetryDelay = 125
|
||||
try:
|
||||
self.try_login()
|
||||
except:
|
||||
logging.error("Couldn't open console " + str(sys.exc_info()[0]) + " " + str(sys.exc_info()[1]))
|
||||
self.ignorePause = False
|
||||
self.ignorePause = False
|
||||
|
||||
|
|
|
@ -209,8 +209,9 @@ class vmmManager(gobject.GObject):
|
|||
win.present()
|
||||
|
||||
def close(self, src=None, src2=None):
|
||||
for uri in self.connections:
|
||||
self.connections[uri].close()
|
||||
conns = self.connections.values()
|
||||
for conn in conns:
|
||||
conn.close()
|
||||
win = self.window.get_widget("vmm-manager")
|
||||
win.hide()
|
||||
return 1
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
|
||||
pythondir = $(pkgdatadir)/vncViewer
|
||||
python_DATA = $(wildcard $(srcdir)/*.py)
|
||||
|
||||
EXTRA_DIST = $(python_DATA)
|
|
@ -1,18 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2006 Red Hat, Inc.
|
||||
# Copyright (C) 2006 Daniel P. Berrange <berrange@redhat.com>
|
||||
#
|
||||
# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
#
|
|
@ -1,481 +0,0 @@
|
|||
##
|
||||
## pyvnc2swf - crippled_des.py
|
||||
##
|
||||
## $Id: crippled_des.py,v 1.3 2005/08/25 20:01:59 euske Exp $
|
||||
##
|
||||
## Copyright (C) 2005 by Yusuke Shinyama (yusuke at cs . nyu . edu)
|
||||
## All Rights Reserved.
|
||||
##
|
||||
## This 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 software 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 software; if not, write to the Free Software
|
||||
## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
## USA.
|
||||
##
|
||||
|
||||
## The following part of this file is taken from
|
||||
## pyvncviewer by Chris Liechti.
|
||||
## URL: http://homepage.hispeed.ch/py430/python/
|
||||
|
||||
# Modified DES encryption for VNC password authentication.
|
||||
# Ported from realvnc's java viewer by <cliechti@gmx.net>
|
||||
# I chose this package name because it is not compatible with the
|
||||
# original DES algorithm, e.g. found pycrypto.
|
||||
# Original notice following:
|
||||
|
||||
# This DES class has been extracted from package Acme.Crypto for use in VNC.
|
||||
# The bytebit[] array has been reversed so that the most significant bit
|
||||
# in each byte of the key is ignored, not the least significant. Also the
|
||||
# unnecessary odd parity code has been removed.
|
||||
#
|
||||
# These changes are:
|
||||
# Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||
#
|
||||
# This software 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.
|
||||
#
|
||||
|
||||
# DesCipher - the DES encryption method
|
||||
#
|
||||
# The meat of this code is by Dave Zimmerman <dzimm@widget.com>, and is:
|
||||
#
|
||||
# Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved.
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software
|
||||
# and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and
|
||||
# without fee is hereby granted, provided that this copyright notice is kept
|
||||
# intact.
|
||||
#
|
||||
# WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
|
||||
# OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
# TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
# PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE LIABLE
|
||||
# FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
|
||||
# DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
|
||||
#
|
||||
# THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
|
||||
# CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
|
||||
# PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
|
||||
# NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
|
||||
# SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
|
||||
# SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
|
||||
# PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). WIDGET WORKSHOP
|
||||
# SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
|
||||
# HIGH RISK ACTIVITIES.
|
||||
#
|
||||
#
|
||||
# The rest is:
|
||||
#
|
||||
# Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||
# fine Java utilities: http://www.acme.com/java/
|
||||
|
||||
|
||||
#/ The DES encryption method.
|
||||
# <P>
|
||||
# This is surprisingly fast, for pure Java. On a SPARC 20, wrapped
|
||||
# in Acme.Crypto.EncryptedOutputStream or Acme.Crypto.EncryptedInputStream,
|
||||
# it does around 7000 bytes/second.
|
||||
# <P>
|
||||
# Most of this code is by Dave Zimmerman <dzimm@widget.com>, and is
|
||||
# Copyright (c) 1996 Widget Workshop, Inc. See the source file for details.
|
||||
# <P>
|
||||
# <A HREF="/resources/classes/Acme/Crypto/DesCipher.java">Fetch the software.</A><BR>
|
||||
# <A HREF="/resources/classes/Acme.tar.Z">Fetch the entire Acme package.</A>
|
||||
# <P>
|
||||
# @see Des3Cipher
|
||||
# @see EncryptedOutputStream
|
||||
# @see EncryptedInputStream
|
||||
|
||||
import struct
|
||||
|
||||
class DesCipher:
|
||||
# Constructor, byte-array key.
|
||||
def __init__(self, key):
|
||||
self.setKey(key)
|
||||
|
||||
#/ Set the key.
|
||||
def setKey(self, key):
|
||||
self.encryptKeys = self.deskey([ord(x) for x in key], 1)
|
||||
self.decryptKeys = self.deskey([ord(x) for x in key], 0)
|
||||
|
||||
# Turn an 8-byte key into internal keys.
|
||||
def deskey(self, keyBlock, encrypting):
|
||||
#~ int i, j, l, m, n;
|
||||
pc1m = [0]*56 #new int[56];
|
||||
pcr = [0]*56 #new int[56];
|
||||
kn = [0]*32 #new int[32];
|
||||
|
||||
for j in range(56):
|
||||
l = pc1[j]
|
||||
m = l & 07
|
||||
pc1m[j] = ((keyBlock[l >> 3] & bytebit[m]) != 0)
|
||||
|
||||
for i in range(16):
|
||||
if encrypting:
|
||||
m = i << 1
|
||||
else:
|
||||
m = (15-i) << 1
|
||||
n = m + 1
|
||||
kn[m] = kn[n] = 0
|
||||
for j in range(28):
|
||||
l = j + totrot[i]
|
||||
if l < 28:
|
||||
pcr[j] = pc1m[l]
|
||||
else:
|
||||
pcr[j] = pc1m[l - 28]
|
||||
for j in range(28, 56):
|
||||
l = j + totrot[i]
|
||||
if l < 56:
|
||||
pcr[j] = pc1m[l]
|
||||
else:
|
||||
pcr[j] = pc1m[l - 28]
|
||||
for j in range(24):
|
||||
if pcr[pc2[j]] != 0:
|
||||
kn[m] |= bigbyte[j]
|
||||
if pcr[pc2[j+24]] != 0:
|
||||
kn[n] |= bigbyte[j]
|
||||
return self.cookey(kn)
|
||||
|
||||
def cookey(self, raw):
|
||||
#~ int raw0, raw1;
|
||||
#~ int rawi, KnLi;
|
||||
#~ int i;
|
||||
KnL = [0]*32
|
||||
|
||||
rawi = 0
|
||||
KnLi = 0
|
||||
for i in range(16):
|
||||
raw0 = raw[rawi]
|
||||
rawi += 1
|
||||
raw1 = raw[rawi]
|
||||
rawi += 1
|
||||
KnL[KnLi] = (raw0 & 0x00fc0000L) << 6
|
||||
KnL[KnLi] |= (raw0 & 0x00000fc0L) << 10
|
||||
KnL[KnLi] |= (raw1 & 0x00fc0000L) >> 10
|
||||
KnL[KnLi] |= (raw1 & 0x00000fc0L) >> 6
|
||||
KnLi += 1
|
||||
KnL[KnLi] = (raw0 & 0x0003f000L) << 12
|
||||
KnL[KnLi] |= (raw0 & 0x0000003fL) << 16
|
||||
KnL[KnLi] |= (raw1 & 0x0003f000L) >> 4
|
||||
KnL[KnLi] |= (raw1 & 0x0000003fL)
|
||||
KnLi += 1
|
||||
return KnL
|
||||
|
||||
# Block encryption routines.
|
||||
|
||||
#/ Encrypt a block of eight bytes.
|
||||
def encrypt(self, clearText):
|
||||
if len(clearText) != 8:
|
||||
raise TypeError, "length must be eight bytes"
|
||||
return struct.pack(">LL",
|
||||
*self.des(struct.unpack(">LL", clearText), self.encryptKeys)
|
||||
)
|
||||
|
||||
#/ Decrypt a block of eight bytes.
|
||||
def decrypt(self, cipherText):
|
||||
if len(cipherText) != 8:
|
||||
raise TypeError, "length must be eight bytes"
|
||||
return struct.pack(">LL",
|
||||
*self.des(struct.unpack(">LL", cipherText), self.decryptKeys)
|
||||
)
|
||||
|
||||
# The DES function.
|
||||
def des(self, (leftt, right), keys):
|
||||
#~ int fval, work, right, leftt;
|
||||
#~ int round
|
||||
keysi = 0
|
||||
|
||||
work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL
|
||||
right ^= work
|
||||
leftt ^= (work << 4) & 0xffffffffL
|
||||
|
||||
work = ((leftt >> 16) ^ right) & 0x0000ffffL
|
||||
right ^= work
|
||||
leftt ^= (work << 16) & 0xffffffffL
|
||||
|
||||
work = ((right >> 2) ^ leftt) & 0x33333333L
|
||||
leftt ^= work
|
||||
right ^= (work << 2) & 0xffffffffL
|
||||
|
||||
work = ((right >> 8) ^ leftt) & 0x00ff00ffL
|
||||
leftt ^= work
|
||||
right ^= (work << 8) & 0xffffffffL
|
||||
right = ((right << 1) | ((right >> 31) & 1)) & 0xffffffffL
|
||||
|
||||
work = (leftt ^ right) & 0xaaaaaaaaL
|
||||
leftt ^= work
|
||||
right ^= work
|
||||
leftt = ((leftt << 1) | ((leftt >> 31) & 1)) & 0xffffffffL
|
||||
|
||||
for round in range(8):
|
||||
work = ((right << 28) | (right >> 4)) & 0xffffffffL
|
||||
work ^= keys[keysi]
|
||||
keysi += 1
|
||||
fval = SP7[ work & 0x0000003fL ]
|
||||
fval |= SP5[(work >> 8) & 0x0000003fL ]
|
||||
fval |= SP3[(work >> 16) & 0x0000003fL ]
|
||||
fval |= SP1[(work >> 24) & 0x0000003fL ]
|
||||
work = right ^ keys[keysi]
|
||||
keysi += 1
|
||||
fval |= SP8[ work & 0x0000003fL ]
|
||||
fval |= SP6[(work >> 8) & 0x0000003fL ]
|
||||
fval |= SP4[(work >> 16) & 0x0000003fL ]
|
||||
fval |= SP2[(work >> 24) & 0x0000003fL ]
|
||||
leftt ^= fval
|
||||
work = ((leftt << 28) | (leftt >> 4)) & 0xffffffffL
|
||||
work ^= keys[keysi]
|
||||
keysi += 1
|
||||
fval = SP7[ work & 0x0000003fL ]
|
||||
fval |= SP5[(work >> 8) & 0x0000003fL ]
|
||||
fval |= SP3[(work >> 16) & 0x0000003fL ]
|
||||
fval |= SP1[(work >> 24) & 0x0000003fL ]
|
||||
work = leftt ^ keys[keysi]
|
||||
keysi += 1
|
||||
fval |= SP8[ work & 0x0000003fL ]
|
||||
fval |= SP6[(work >> 8) & 0x0000003fL ]
|
||||
fval |= SP4[(work >> 16) & 0x0000003fL ]
|
||||
fval |= SP2[(work >> 24) & 0x0000003fL ]
|
||||
right ^= fval
|
||||
|
||||
right = ((right << 31) | (right >> 1)) & 0xffffffffL
|
||||
work = (leftt ^ right) & 0xaaaaaaaaL
|
||||
leftt ^= work
|
||||
right ^= work
|
||||
leftt = ((leftt << 31) | (leftt >> 1)) & 0xffffffffL
|
||||
work = ((leftt >> 8) ^ right) & 0x00ff00ffL
|
||||
right ^= work
|
||||
leftt ^= (work << 8) & 0xffffffffL
|
||||
work = ((leftt >> 2) ^ right) & 0x33333333L
|
||||
right ^= work
|
||||
leftt ^= (work << 2) & 0xffffffffL
|
||||
work = ((right >> 16) ^ leftt) & 0x0000ffffL
|
||||
leftt ^= work
|
||||
right ^= (work << 16) & 0xffffffffL
|
||||
work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL
|
||||
leftt ^= work
|
||||
right ^= (work << 4) & 0xffffffffL
|
||||
return right, leftt
|
||||
|
||||
# Tables, permutations, S-boxes, etc.
|
||||
|
||||
bytebit = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]
|
||||
|
||||
bigbyte = [
|
||||
0x800000, 0x400000, 0x200000, 0x100000,
|
||||
0x080000, 0x040000, 0x020000, 0x010000,
|
||||
0x008000, 0x004000, 0x002000, 0x001000,
|
||||
0x000800, 0x000400, 0x000200, 0x000100,
|
||||
0x000080, 0x000040, 0x000020, 0x000010,
|
||||
0x000008, 0x000004, 0x000002, 0x000001
|
||||
]
|
||||
|
||||
pc1 = [
|
||||
56, 48, 40, 32, 24, 16, 8,
|
||||
0, 57, 49, 41, 33, 25, 17,
|
||||
9, 1, 58, 50, 42, 34, 26,
|
||||
18, 10, 2, 59, 51, 43, 35,
|
||||
62, 54, 46, 38, 30, 22, 14,
|
||||
6, 61, 53, 45, 37, 29, 21,
|
||||
13, 5, 60, 52, 44, 36, 28,
|
||||
20, 12, 4, 27, 19, 11, 3
|
||||
]
|
||||
|
||||
totrot = [
|
||||
1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28
|
||||
]
|
||||
|
||||
pc2 = [
|
||||
13, 16, 10, 23, 0, 4,
|
||||
2, 27, 14, 5, 20, 9,
|
||||
22, 18, 11, 3 , 25, 7,
|
||||
15, 6, 26, 19, 12, 1,
|
||||
40, 51, 30, 36, 46, 54,
|
||||
29, 39, 50, 44, 32, 47,
|
||||
43, 48, 38, 55, 33, 52,
|
||||
45, 41, 49, 35, 28, 31,
|
||||
]
|
||||
|
||||
SP1 = [
|
||||
0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
|
||||
0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
|
||||
0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
|
||||
0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
|
||||
0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
|
||||
0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
|
||||
0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
|
||||
0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
|
||||
0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
|
||||
0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
|
||||
0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
|
||||
0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
|
||||
0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
|
||||
0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
|
||||
0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
|
||||
0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L
|
||||
]
|
||||
SP2 = [
|
||||
0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
|
||||
0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
|
||||
0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
|
||||
0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
|
||||
0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
|
||||
0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
|
||||
0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
|
||||
0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
|
||||
0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
|
||||
0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
|
||||
0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
|
||||
0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
|
||||
0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
|
||||
0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
|
||||
0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
|
||||
0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L
|
||||
]
|
||||
SP3 = [
|
||||
0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
|
||||
0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
|
||||
0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
|
||||
0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
|
||||
0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
|
||||
0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
|
||||
0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
|
||||
0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
|
||||
0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
|
||||
0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
|
||||
0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
|
||||
0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
|
||||
0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
|
||||
0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
|
||||
0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
|
||||
0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L
|
||||
]
|
||||
SP4 = [
|
||||
0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
|
||||
0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
|
||||
0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
|
||||
0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
|
||||
0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
|
||||
0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
|
||||
0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
|
||||
0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
|
||||
0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
|
||||
0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
|
||||
0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
|
||||
0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
|
||||
0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
|
||||
0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
|
||||
0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
|
||||
0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L
|
||||
]
|
||||
SP5 = [
|
||||
0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
|
||||
0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
|
||||
0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
|
||||
0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
|
||||
0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
|
||||
0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
|
||||
0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
|
||||
0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
|
||||
0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
|
||||
0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
|
||||
0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
|
||||
0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
|
||||
0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
|
||||
0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
|
||||
0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
|
||||
0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L
|
||||
]
|
||||
SP6 = [
|
||||
0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
|
||||
0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
|
||||
0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
|
||||
0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
|
||||
0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
|
||||
0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
|
||||
0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
|
||||
0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
|
||||
0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
|
||||
0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
|
||||
0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
|
||||
0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
|
||||
0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
|
||||
0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
|
||||
0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
|
||||
0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L
|
||||
]
|
||||
SP7 = [
|
||||
0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
|
||||
0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
|
||||
0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
|
||||
0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
|
||||
0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
|
||||
0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
|
||||
0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
|
||||
0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
|
||||
0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
|
||||
0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
|
||||
0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
|
||||
0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
|
||||
0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
|
||||
0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
|
||||
0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
|
||||
0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L
|
||||
]
|
||||
SP8 = [
|
||||
0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
|
||||
0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
|
||||
0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
|
||||
0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
|
||||
0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
|
||||
0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
|
||||
0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
|
||||
0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
|
||||
0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
|
||||
0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
|
||||
0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
|
||||
0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
|
||||
0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
|
||||
0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
|
||||
0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
|
||||
0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L
|
||||
]
|
||||
|
||||
#test only:
|
||||
if __name__ == '__main__':
|
||||
des = DesCipher('test1234')
|
||||
print repr(des.encrypt("hello321"))
|
||||
print des.decrypt(des.encrypt("hello321"))
|
||||
print des.encrypt(des.decrypt("hello321"))
|
|
@ -1,566 +0,0 @@
|
|||
##
|
||||
## pyvnc2swf - rfb.py
|
||||
##
|
||||
## $Id: rfb.py,v 1.25 2005/11/27 00:04:18 euske Exp $
|
||||
##
|
||||
## Copyright (C) 2005 by Yusuke Shinyama (yusuke at cs . nyu . edu)
|
||||
## All Rights Reserved.
|
||||
##
|
||||
## Adapted for use as a VNC widget in virtmanager:
|
||||
##
|
||||
## Copyright (C) 2006 Red Hat Inc.
|
||||
##
|
||||
## This 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 software 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 software; if not, write to the Free Software
|
||||
## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
## USA.
|
||||
##
|
||||
|
||||
|
||||
import sys, time, socket
|
||||
from struct import pack, unpack
|
||||
from crippled_des import DesCipher
|
||||
stderr = sys.stderr
|
||||
lowerbound = max
|
||||
|
||||
|
||||
def byte2bit(s):
|
||||
return ''.join([ chr((ord(s[i>>3]) >> (7 - i&7)) & 1) for i in xrange(len(s)*8) ])
|
||||
|
||||
|
||||
# Exceptions
|
||||
class RFBError(Exception): pass
|
||||
class RFBAuthError(RFBError): pass
|
||||
class RFBProtocolError(RFBError): pass
|
||||
|
||||
ENCODING_RAW = 0
|
||||
ENCODING_COPY_RECT = 1
|
||||
ENCODING_RRE = 2
|
||||
ENCODING_CORRE = 4
|
||||
ENCODING_HEXTILE = 5
|
||||
ENCODING_ZRLE = 16
|
||||
ENCODING_DESKTOP_RESIZE = -223
|
||||
ENCODING_CURSOR_POS = -232
|
||||
ENCODING_RICH_CURSOR = -239
|
||||
ENCODING_XCURSOR = -240
|
||||
|
||||
AUTH_INVALID = 0
|
||||
AUTH_NONE = 1
|
||||
AUTH_VNCAUTH = 2
|
||||
AUTH_RA2 = 5
|
||||
AUTH_RA2NE = 6
|
||||
AUTH_TIGHT = 16
|
||||
AUTH_ULTRA = 17
|
||||
AUTH_TLS = 18
|
||||
|
||||
AUTH_VALID = [ AUTH_NONE, AUTH_VNCAUTH, AUTH_RA2, AUTH_RA2NE, AUTH_TIGHT, AUTH_ULTRA, AUTH_TLS ]
|
||||
AUTH_SUPPORTED = [ AUTH_NONE, AUTH_VNCAUTH ]
|
||||
|
||||
## RFBFrameBuffer
|
||||
##
|
||||
class RFBFrameBuffer:
|
||||
|
||||
def init_screen(self, width, height, name):
|
||||
#print >>stderr, 'init_screen: %dx%d, name=%r' % (width, height, name)
|
||||
raise NotImplementedError
|
||||
|
||||
def resize_screen(self, width, height):
|
||||
raise NotImplementedError
|
||||
|
||||
def set_converter(self, convert_pixels, convert_color1):
|
||||
self.convert_pixels = convert_pixels
|
||||
self.convert_color1 = convert_color1
|
||||
return
|
||||
|
||||
def process_pixels(self, x, y, width, height, data):
|
||||
#print >>stderr, 'process_pixels: %dx%d at (%d,%d)' % (width,height,x,y)
|
||||
raise NotImplementedError
|
||||
|
||||
def process_solid(self, x, y, width, height, data):
|
||||
#print >>stderr, 'process_solid: %dx%d at (%d,%d), color=%r' % (width,height,x,y, color)
|
||||
raise NotImplementedError
|
||||
|
||||
def update_screen(self, t):
|
||||
#print >>stderr, 'update_screen'
|
||||
raise NotImplementedError
|
||||
|
||||
# data is given as ARGB
|
||||
def change_cursor(self, width, height, x, y, data):
|
||||
#print >>stderr, 'change_cursor'
|
||||
raise NotImplementedError
|
||||
|
||||
def move_cursor(self, x, y):
|
||||
#print >>stderr, 'move_cursor'
|
||||
raise NotImplementedError
|
||||
|
||||
def close(self):
|
||||
return
|
||||
|
||||
|
||||
## RFBProxy
|
||||
##
|
||||
class RFBProxy:
|
||||
"Abstract class of RFB clients."
|
||||
|
||||
def __init__(self, fb=None, preferred_encoding=(ENCODING_RAW,ENCODING_HEXTILE), debug=0):
|
||||
self.fb = fb
|
||||
self.debug = debug
|
||||
self.preferred_encoding = preferred_encoding
|
||||
return
|
||||
|
||||
FASTEST_FORMAT = (32, 8, 1, 1, 255, 255, 255, 24, 16, 8)
|
||||
def preferred_format(self, bitsperpixel, depth, bigendian, truecolour,
|
||||
red_max, green_max, blue_max,
|
||||
red_shift, green_shift, blue_shift):
|
||||
# should return 10-tuple (bitsperpixel, depth, bigendian, truecolour,
|
||||
# red_max, green_max, blue_max, red_shift, green_shift, blue_shift)
|
||||
if self.fb:
|
||||
self.fb.set_converter(lambda data: data,
|
||||
lambda data: unpack('BBBx', data))
|
||||
return self.FASTEST_FORMAT
|
||||
|
||||
def send(self, s):
|
||||
"Send data s to the server."
|
||||
raise NotImplementedError
|
||||
|
||||
def recv(self, n):
|
||||
"Receive n-bytes data from the server."
|
||||
raise NotImplementedError
|
||||
|
||||
def recv_relay(self, n):
|
||||
"Same as recv() except the received data is also passed to self.relay.recv_framedata."
|
||||
return self.recv(n)
|
||||
|
||||
def recv_byte_with_timeout(self):
|
||||
return self.recv_relay(1)
|
||||
|
||||
def write(self, n):
|
||||
return
|
||||
|
||||
def request_update(self):
|
||||
"Send a request to the server."
|
||||
raise NotImplementedError
|
||||
def finish_update(self):
|
||||
if self.fb:
|
||||
self.fb.update_screen(time.time())
|
||||
return
|
||||
|
||||
def init(self):
|
||||
# recv: server protocol version
|
||||
server_version = self.recv(12)
|
||||
# send: client protocol version
|
||||
self.protocol_version = 3
|
||||
if server_version.startswith('RFB 003.007'):
|
||||
self.protocol_version = 7
|
||||
elif server_version.startswith('RFB 003.008'):
|
||||
self.protocol_version = 8
|
||||
self.send('RFB 003.%03d\x0a' % self.protocol_version)
|
||||
if self.debug:
|
||||
print >>stderr, 'protocol_version: 3.%d' % self.protocol_version
|
||||
|
||||
self.auth_types = []
|
||||
|
||||
if self.protocol_version == 3:
|
||||
# protocol 3.3 (or 3.6)
|
||||
# recv: server security
|
||||
(server_security,) = unpack('>L', self.recv(4))
|
||||
if self.debug:
|
||||
print >>stderr, 'server_security: %r' % server_security
|
||||
# server_security might be 0, 1 or 2.
|
||||
if int(server_security) == 0:
|
||||
(reason_length,) = unpack('>L', self.recv(4))
|
||||
reason = self.recv(reason_length)
|
||||
raise RFBAuthError('Auth Error: %s' % reason)
|
||||
elif int(server_security) in AUTH_VALID:
|
||||
self.auth_types = [ server_security ]
|
||||
else:
|
||||
raise "illegal auth type %d" % server_security
|
||||
elif self.protocol_version >= 7:
|
||||
(nsecurities,) = unpack('>B', self.recv(1))
|
||||
server_securities = self.recv(nsecurities)
|
||||
if self.debug:
|
||||
print >>stderr, 'server_securities: %r' % server_securities
|
||||
for type in server_securities:
|
||||
if ord(type) in AUTH_SUPPORTED:
|
||||
self.auth_types.append(ord(type))
|
||||
|
||||
if len(self.auth_types) == 0:
|
||||
raise "no valid auth types in " + str(server_securities)
|
||||
|
||||
return self.auth_types
|
||||
|
||||
def getpass(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def auth(self):
|
||||
|
||||
# vnc challange & response auth
|
||||
def crauth():
|
||||
p = self.getpass()
|
||||
if not p:
|
||||
raise RFBError('Auth cancelled')
|
||||
# from pyvncviewer
|
||||
des = DesCipher((p+'\x00'*8)[:8])
|
||||
challange = self.recv(16)
|
||||
if self.debug:
|
||||
print >>stderr, 'challange: %r' % challange
|
||||
response = des.encrypt(challange[:8]) + des.encrypt(challange[8:])
|
||||
if self.debug:
|
||||
print >>stderr, 'response: %r' % response
|
||||
self.send(response)
|
||||
# recv: security result
|
||||
(result,) = unpack('>L', self.recv(4))
|
||||
return result
|
||||
|
||||
server_result = 0
|
||||
if self.protocol_version == 3:
|
||||
if AUTH_NONE in self.auth_types:
|
||||
server_result = 0
|
||||
elif AUTH_VNCAUTH in self.auth_types:
|
||||
server_result = crauth()
|
||||
elif self.protocol_version >= 7:
|
||||
if AUTH_NONE in self.auth_types:
|
||||
self.send('\x01')
|
||||
if self.protocol_version == 8:
|
||||
# Protocol 3.8: must recv security result
|
||||
(server_result,) = unpack('>L', self.recv(4))
|
||||
else:
|
||||
server_result = 0
|
||||
elif AUTH_VNCAUTH in self.auth_types:
|
||||
self.send('\x02')
|
||||
server_result = crauth()
|
||||
else:
|
||||
raise "no supported auth types"
|
||||
# result returned.
|
||||
if self.debug:
|
||||
print >>stderr, 'server_result: %r' % server_result
|
||||
if server_result != 0:
|
||||
# auth failed.
|
||||
if self.protocol_version != 3:
|
||||
(reason_length,) = unpack('>L', self.recv(4))
|
||||
reason = self.recv(reason_length)
|
||||
else:
|
||||
reason = server_result
|
||||
raise RFBAuthError('Auth Error: %s' % reason)
|
||||
|
||||
def start(self, shared=True):
|
||||
if shared:
|
||||
self.send('\x01')
|
||||
else:
|
||||
self.send('\x00')
|
||||
|
||||
# server info.
|
||||
server_init = self.recv(24)
|
||||
(width, height, pixelformat, namelen) = unpack('>HH16sL', server_init)
|
||||
self.name = self.recv(namelen)
|
||||
(bitsperpixel, depth, bigendian, truecolour,
|
||||
red_max, green_max, blue_max,
|
||||
red_shift, green_shift, blue_shift) = unpack('>BBBBHHHBBBxxx', pixelformat)
|
||||
if self.debug:
|
||||
print >>stderr, 'Server Encoding:'
|
||||
print >>stderr, ' width=%d, height=%d, name=%r' % (width, height, self.name)
|
||||
print >>stderr, ' pixelformat=', (bitsperpixel, depth, bigendian, truecolour)
|
||||
print >>stderr, ' rgbmax=', (red_max, green_max, blue_max)
|
||||
print >>stderr, ' rgbshift=', (red_shift, green_shift, blue_shift)
|
||||
# setformat
|
||||
self.send('\x00\x00\x00\x00')
|
||||
# 32bit, 8bit-depth, big-endian(RGBX), truecolour, 255max
|
||||
(bitsperpixel, depth, bigendian, truecolour,
|
||||
red_max, green_max, blue_max,
|
||||
red_shift, green_shift, blue_shift) = self.preferred_format(bitsperpixel, depth, bigendian, truecolour,
|
||||
red_max, green_max, blue_max,
|
||||
red_shift, green_shift, blue_shift)
|
||||
self.bytesperpixel = bitsperpixel/8
|
||||
pixelformat = pack('>BBBBHHHBBBxxx', bitsperpixel, depth, bigendian, truecolour,
|
||||
red_max, green_max, blue_max,
|
||||
red_shift, green_shift, blue_shift)
|
||||
self.send(pixelformat)
|
||||
self.write(pack('>HH16sL', width, height, pixelformat, namelen))
|
||||
self.write(self.name)
|
||||
if self.fb:
|
||||
self.clipping = self.fb.init_screen(width, height, self.name)
|
||||
else:
|
||||
self.clipping = (0,0, width, height)
|
||||
self.send('\x02\x00' + pack('>H', len(self.preferred_encoding)))
|
||||
for e in self.preferred_encoding:
|
||||
self.send(pack('>l', e))
|
||||
return self
|
||||
|
||||
def loop1(self):
|
||||
self.request_update()
|
||||
c = self.recv_byte_with_timeout()
|
||||
if c == '':
|
||||
return False
|
||||
elif c == None:
|
||||
# timeout
|
||||
pass
|
||||
elif c == '\x00':
|
||||
(nrects,) = unpack('>xH', self.recv_relay(3))
|
||||
if self.debug:
|
||||
print >>stderr, 'FrameBufferUpdate: nrects=%d' % nrects
|
||||
for rectindex in xrange(nrects):
|
||||
(x0, y0, width, height, t) = unpack('>HHHHl', self.recv_relay(12))
|
||||
if self.debug:
|
||||
print >>stderr, ' %d: %d x %d at (%d,%d), type=%d' % (rectindex, width, height, x0, y0, t)
|
||||
|
||||
if t == ENCODING_RAW:
|
||||
l = width*height*self.bytesperpixel
|
||||
data = self.recv_relay(l)
|
||||
if self.debug:
|
||||
print >>stderr, ' RawEncoding: len=%d, received=%d' % (l, len(data))
|
||||
if self.fb:
|
||||
self.fb.process_pixels(x0, y0, width, height, data)
|
||||
|
||||
elif t == ENCODING_COPY_RECT:
|
||||
raise RFBProtocolError('unsupported: CopyRectEncoding')
|
||||
|
||||
elif t == ENCODING_RRE:
|
||||
(nsubrects,) = unpack('>L', self.recv_relay(4))
|
||||
bgcolor = self.recv_relay(self.bytesperpixel)
|
||||
if self.debug:
|
||||
print >>stderr, ' RREEncoding: subrects=%d, bgcolor=%r' % (nsubrects, bgcolor)
|
||||
if self.fb:
|
||||
self.fb.process_solid(x0, y0, width, height, bgcolor)
|
||||
for i in xrange(nsubrects):
|
||||
fgcolor = self.recv_relay(self.bytesperpixel)
|
||||
(x,y,w,h) = unpack('>HHHH', self.recv_relay(8))
|
||||
if self.fb:
|
||||
self.fb.process_solid(x0+x, y0+y, w, h, fgcolor)
|
||||
if 2 <= self.debug:
|
||||
print >>stderr, ' RREEncoding: ', (x,y,w,h,fgcolor)
|
||||
|
||||
elif t == ENCODING_CORRE:
|
||||
(nsubrects,) = unpack('>L', self.recv_relay(4))
|
||||
bgcolor = self.recv_relay(self.bytesperpixel)
|
||||
if self.debug:
|
||||
print >>stderr, ' CoRREEncoding: subrects=%d, bgcolor=%r' % (nsubrects, bgcolor)
|
||||
if self.fb:
|
||||
self.fb.process_solid(x0, y0, width, height, bgcolor)
|
||||
for i in xrange(nsubrects):
|
||||
fgcolor = self.recv_relay(self.bytesperpixel)
|
||||
(x,y,w,h) = unpack('>BBBB', self.recv_relay(4))
|
||||
if self.fb:
|
||||
self.fb.process_solid(x0+x, y0+y, w, h, fgcolor)
|
||||
if 2 <= self.debug:
|
||||
print >>stderr, ' CoRREEncoding: ', (x,y,w,h,fgcolor)
|
||||
|
||||
elif t == ENCODING_HEXTILE:
|
||||
if self.debug:
|
||||
print >>stderr, ' HextileEncoding'
|
||||
(fgcolor, bgcolor) = (None, None)
|
||||
for y in xrange(0, height, 16):
|
||||
for x in xrange(0, width, 16):
|
||||
w = min(width-x, 16)
|
||||
h = min(height-y, 16)
|
||||
c = ord(self.recv_relay(1))
|
||||
assert c < 32
|
||||
# Raw
|
||||
if c & 1:
|
||||
l = w*h*self.bytesperpixel
|
||||
data = self.recv_relay(l)
|
||||
if self.fb:
|
||||
self.fb.process_pixels(x0+x, y0+y, w, h, data)
|
||||
if 2 <= self.debug:
|
||||
print >>stderr, ' Raw:', l
|
||||
continue
|
||||
if c & 2:
|
||||
bgcolor = self.recv_relay(self.bytesperpixel)
|
||||
if c & 4:
|
||||
fgcolor = self.recv_relay(self.bytesperpixel)
|
||||
if self.fb:
|
||||
self.fb.process_solid(x0+x, y0+y, w, h, bgcolor)
|
||||
# Solid
|
||||
if not c & 8:
|
||||
if 2 <= self.debug:
|
||||
print >>stderr, ' Solid:', repr(bgcolor)
|
||||
continue
|
||||
nsubrects = ord(self.recv_relay(1))
|
||||
# SubrectsColoured
|
||||
if c & 16:
|
||||
if 2 <= self.debug:
|
||||
print >>stderr, ' SubrectsColoured:', nsubrects, repr(bgcolor)
|
||||
for i in xrange(nsubrects):
|
||||
color = self.recv_relay(self.bytesperpixel)
|
||||
(xy,wh) = unpack('>BB', self.recv_relay(2))
|
||||
if self.fb:
|
||||
self.fb.process_solid(x0+x+(xy>>4), y0+y+(xy&15), (wh>>4)+1, (wh&15)+1, color)
|
||||
if 3 <= self.debug:
|
||||
print >>stderr, ' ', repr(color), (xy,wh)
|
||||
# NoSubrectsColoured
|
||||
else:
|
||||
if 2 <= self.debug:
|
||||
print >>stderr, ' NoSubrectsColoured:', nsubrects, repr(bgcolor)
|
||||
for i in xrange(nsubrects):
|
||||
(xy,wh) = unpack('>BB', self.recv_relay(2))
|
||||
if self.fb:
|
||||
self.fb.process_solid(x0+x+(xy>>4), y0+y+(xy&15), (wh>>4)+1, (wh&15)+1, fgcolor)
|
||||
if 3 <= self.debug:
|
||||
print >>stderr, ' ', (xy,wh)
|
||||
|
||||
elif t == ENCODING_ZRLE:
|
||||
raise RFBProtocolError('unsupported: ZRLEEncoding')
|
||||
|
||||
elif t == ENCODING_DESKTOP_RESIZE:
|
||||
self.clipping = self.fb.resize_screen(width, height)
|
||||
|
||||
elif t == ENCODING_RICH_CURSOR:
|
||||
if width and height:
|
||||
rowbytes = (width + 7) / 8;
|
||||
# Cursor image RGB
|
||||
data = self.recv_relay(width * height * self.bytesperpixel)
|
||||
# Cursor mask -> 1 bit/pixel (1 -> image; 0 -> transparent)
|
||||
mask = self.recv_relay(rowbytes * height)
|
||||
# Set the alpha channel with maskData where bit=1 -> alpha = 255, bit=0 -> alpha=255
|
||||
if self.debug:
|
||||
print >>stderr, 'RichCursor: %dx%d at %d,%d' % (width,height,x0,y0)
|
||||
if self.fb:
|
||||
data = self.fb.convert_pixels(data)
|
||||
mask = ''.join([ byte2bit(mask[p:p+rowbytes]) for p in xrange(0, height*rowbytes, rowbytes) ])
|
||||
def conv1(i):
|
||||
if mask[i/4] == '\x01':
|
||||
return '\xff'+data[i]+data[i+1]+data[i+2]
|
||||
else:
|
||||
return '\x00\x00\x00\x00'
|
||||
data = ''.join([ conv1(i) for i in xrange(0, len(data), 4) ])
|
||||
self.fb.change_cursor(width, height, x0, y0, data)
|
||||
|
||||
elif t == ENCODING_XCURSOR:
|
||||
if width and height:
|
||||
rowbytes = (width + 7) / 8;
|
||||
# Foreground RGB
|
||||
fgcolor = self.recv_relay(3)
|
||||
# Background RGB
|
||||
bgcolor = self.recv_relay(3)
|
||||
# Cursor Data -> 1 bit/pixel
|
||||
data = self.recv_relay(rowbytes * height)
|
||||
# Cursor Mask -> 1 bit/pixel
|
||||
mask = self.recv_relay(rowbytes * height)
|
||||
# Create the image from cursordata and maskdata.
|
||||
print >>stderr, 'XCursor: %dx%d at %d,%d' % (width,height,x0,y0)
|
||||
if self.fb:
|
||||
data = byte2bit(data)
|
||||
mask = byte2bit(mask)
|
||||
def conv1(i):
|
||||
if mask[i] == '\x01':
|
||||
if data[i] == '\x01':
|
||||
return '\xff'+fgcolor
|
||||
else:
|
||||
return '\xff'+bgcolor
|
||||
else:
|
||||
return '\x00\x00\x00\x00'
|
||||
data = ''.join([ conv1(i) for i in xrange(len(data)) ])
|
||||
self.fb.change_cursor(width, height, x0, y0, data)
|
||||
|
||||
elif t == ENCODING_CURSOR_POS:
|
||||
if self.debug:
|
||||
print >>stderr, 'CursorPos: %d,%d' % (x0,y0)
|
||||
if self.fb:
|
||||
self.fb.move_cursor(x0, y0)
|
||||
else:
|
||||
raise RFBProtocolError('Illegal encoding: 0x%02x' % t)
|
||||
self.finish_update()
|
||||
elif c == '\x01':
|
||||
(first, ncolours) = unpack('>xHH', self.recv_relay(11))
|
||||
if self.debug:
|
||||
print >>stderr, 'SetColourMapEntries: first=%d, ncolours=%d' % (first, ncolours)
|
||||
for i in ncolours:
|
||||
self.recv_relay(6)
|
||||
|
||||
elif c == '\x02':
|
||||
if self.debug:
|
||||
print >>stderr, 'Bell'
|
||||
|
||||
elif c == '\x03':
|
||||
(length, ) = unpack('>3xL', self.recv_relay(7))
|
||||
data = self.recv_relay(length)
|
||||
if self.debug:
|
||||
print >>stderr, 'ServerCutText: %r' % data
|
||||
|
||||
else:
|
||||
raise RFBProtocolError('Unsupported msg: %d' % ord(c))
|
||||
|
||||
return True
|
||||
|
||||
def loop(self):
|
||||
while self.loop1():
|
||||
pass
|
||||
self.finish_update()
|
||||
return self
|
||||
|
||||
def close(self):
|
||||
if self.fb:
|
||||
self.fb.close()
|
||||
return
|
||||
|
||||
|
||||
## RFBNetworkClient
|
||||
##
|
||||
class RFBNetworkClient(RFBProxy):
|
||||
|
||||
def __init__(self, host, port, fb=None, pwdfile=None,
|
||||
preferred_encoding=(ENCODING_RAW,ENCODING_HEXTILE), debug=0):
|
||||
RFBProxy.__init__(self, fb=fb, preferred_encoding=preferred_encoding, debug=debug)
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.pwdfile = pwdfile
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
return
|
||||
|
||||
def init(self):
|
||||
self.sock.connect((self.host, self.port))
|
||||
return RFBProxy.init(self)
|
||||
|
||||
def recv(self, n):
|
||||
# MS-Windows doesn't have MSG_WAITALL, so we emulate it.
|
||||
buf = ''
|
||||
while n:
|
||||
x = self.sock.recv(n)
|
||||
if not x: break
|
||||
buf += x
|
||||
n -= len(x)
|
||||
return buf
|
||||
|
||||
def recv_byte_with_timeout(self):
|
||||
self.sock.settimeout(0.05)
|
||||
try:
|
||||
c = self.recv_relay(1)
|
||||
except socket.timeout:
|
||||
c = None
|
||||
self.sock.settimeout(None)
|
||||
return c
|
||||
|
||||
def send(self, s):
|
||||
return self.sock.send(s)
|
||||
|
||||
def getpass(self):
|
||||
import getpass
|
||||
if self.pwdfile:
|
||||
fp = file(self.pwdfile)
|
||||
s = fp.read().rstrip()
|
||||
fp.close()
|
||||
return s
|
||||
return getpass.getpass('Password for %s:%d: ' % (self.host, self.port))
|
||||
|
||||
def request_update(self):
|
||||
if self.debug:
|
||||
print >>stderr, 'FrameBufferUpdateRequest'
|
||||
self.send('\x03\x01' + pack('>HHHH', *self.clipping))
|
||||
return
|
||||
|
||||
def close(self):
|
||||
RFBProxy.close(self)
|
||||
self.sock.close()
|
||||
return
|
||||
|
||||
|
|
@ -1,659 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2006 Daniel Berrange
|
||||
# Copyright (C) 2006 Red Hat
|
||||
#
|
||||
## This 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 software 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 software; if not, write to the Free Software
|
||||
## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
## USA.
|
||||
|
||||
|
||||
import gobject
|
||||
import rfb
|
||||
import sys
|
||||
from struct import pack, unpack
|
||||
import pygtk
|
||||
import gtk
|
||||
import logging
|
||||
|
||||
stderr = sys.stderr
|
||||
|
||||
from time import time
|
||||
|
||||
class GRFBFrameBuffer(rfb.RFBFrameBuffer, gobject.GObject):
|
||||
__gsignals__= {
|
||||
"resize": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, [int,int]),
|
||||
"invalidate": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, [int,int,int,int])
|
||||
}
|
||||
|
||||
def __init__(self, canvas):
|
||||
self.__gobject_init__()
|
||||
self.canvas = canvas
|
||||
self.pixmap = None
|
||||
self.name = "VNC"
|
||||
self.dirtyregion = None
|
||||
|
||||
def get_name(self):
|
||||
return self.name
|
||||
|
||||
def get_pixmap(self):
|
||||
return self.pixmap
|
||||
|
||||
def clone_pixmap(self):
|
||||
if self.pixmap == None:
|
||||
return None
|
||||
width, height = self.pixmap.get_size()
|
||||
clone = gtk.gdk.Pixmap(self.canvas.window, width, height)
|
||||
gc = clone.new_gc()
|
||||
clone.draw_drawable(gc, self.pixmap, 0, 0, 0, 0, -1, -1)
|
||||
return clone
|
||||
|
||||
def init_screen(self, width, height, name):
|
||||
self.name = name
|
||||
return self.resize_screen(width, height)
|
||||
|
||||
def resize_screen(self, width, height):
|
||||
self.pixmap = gtk.gdk.Pixmap(self.canvas.window, width, height)
|
||||
self.gc = self.pixmap.new_gc()
|
||||
self.emit("resize", width, height)
|
||||
return (0, 0, width, height)
|
||||
|
||||
def process_pixels(self, x, y, width, height, data):
|
||||
if self.pixmap == None:
|
||||
return
|
||||
|
||||
self.pixmap.draw_rgb_32_image(self.gc, x, y, width, height, gtk.gdk.RGB_DITHER_NONE, data)
|
||||
self.dirty(x,y,width,height)
|
||||
|
||||
def dirty(self, x, y, width, height):
|
||||
if self.dirtyregion == None:
|
||||
self.dirtyregion = { "x1": x, "y1": y, "x2": x+width, "y2": y+height }
|
||||
else:
|
||||
if x < self.dirtyregion["x1"]:
|
||||
self.dirtyregion["x1"] = x
|
||||
if (x + width) > self.dirtyregion["x2"]:
|
||||
self.dirtyregion["x2"] = (x + width)
|
||||
if y < self.dirtyregion["y1"]:
|
||||
self.dirtyregion["y1"] = y
|
||||
if (y + height) > self.dirtyregion["y2"]:
|
||||
self.dirtyregion["y2"] = (y + height)
|
||||
|
||||
def process_solid(self, x, y, width, height, color):
|
||||
# XXX very very evil assumes pure 32-bit RGBA format
|
||||
(r,g,b,a) = unpack('BBBB', color)
|
||||
self.gc.set_rgb_fg_color(gtk.gdk.Color(red=r*255,green=g*255,blue=b*255))
|
||||
if width == 1 and height == 1:
|
||||
self.pixmap.draw_point(self.gc, x, y)
|
||||
else:
|
||||
self.pixmap.draw_rectangle(self.gc, True, x, y, width, height)
|
||||
self.dirty(x,y,width,height)
|
||||
|
||||
def update_screen(self, t):
|
||||
if self.dirtyregion != None:
|
||||
x1 = self.dirtyregion["x1"]
|
||||
x2 = self.dirtyregion["x2"]
|
||||
y1 = self.dirtyregion["y1"]
|
||||
y2 = self.dirtyregion["y2"]
|
||||
self.emit("invalidate", x1, y1, x2-x1, y2-y1)
|
||||
self.dirtyregion = None
|
||||
|
||||
def change_cursor(self, width, height, x, y, data):
|
||||
logging.error("Unsupported change_cursor operation requested")
|
||||
|
||||
def move_cursor(self, x, y):
|
||||
logging.error("Unsupported move_cursor operation requested")
|
||||
|
||||
gobject.type_register(GRFBFrameBuffer)
|
||||
|
||||
|
||||
class GRFBNetworkClient(rfb.RFBNetworkClient, gobject.GObject):
|
||||
__gsignals__= {
|
||||
"disconnected": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, [])
|
||||
}
|
||||
|
||||
def __init__(self, host, port, converter, debug=0, preferred_encoding=(rfb.ENCODING_RAW)):
|
||||
rfb.RFBNetworkClient.__init__(self, host, port, converter, debug=debug,preferred_encoding=preferred_encoding)
|
||||
self.__gobject_init__()
|
||||
|
||||
self.watch = None
|
||||
self.password = None
|
||||
|
||||
def init(self):
|
||||
return rfb.RFBNetworkClient.init(self)
|
||||
|
||||
def start(self):
|
||||
rfb.RFBNetworkClient.start(self)
|
||||
self.watch = gobject.io_add_watch(self.sock.fileno(), gobject.IO_IN | gobject.IO_ERR | gobject.IO_HUP, self.handle_io)
|
||||
|
||||
def handle_io(self, src, condition):
|
||||
gtk.gdk.threads_enter()
|
||||
try:
|
||||
return self._handle_io(src, condition)
|
||||
finally:
|
||||
gtk.gdk.threads_leave()
|
||||
|
||||
def _handle_io(self, src, condition):
|
||||
if self.watch == None:
|
||||
return 0
|
||||
|
||||
try:
|
||||
self.loop1()
|
||||
except Exception, e:
|
||||
logging.warn("Failure while handling VNC I/O, closing socket: " + str(e))
|
||||
self.close()
|
||||
self.emit("disconnected")
|
||||
return 0
|
||||
return 1
|
||||
|
||||
def close(self):
|
||||
rfb.RFBNetworkClient.close(self)
|
||||
|
||||
if self.watch != None:
|
||||
gobject.source_remove(self.watch)
|
||||
self.watch = None
|
||||
|
||||
def setpass(self, password):
|
||||
self.password = password
|
||||
|
||||
def getpass(self):
|
||||
return self.password
|
||||
|
||||
def update_key(self, down, key):
|
||||
self.send(pack('>BBHI', 4, down, 0, key))
|
||||
|
||||
def update_pointer(self, mask, x, y):
|
||||
if x < 0:
|
||||
x = 0
|
||||
if y < 0:
|
||||
y = 0
|
||||
self.send(pack('>BBHH', 5, mask, x, y))
|
||||
gobject.type_register(GRFBNetworkClient)
|
||||
|
||||
|
||||
class GRFBViewer(gtk.DrawingArea):
|
||||
__gsignals__= {
|
||||
"connected": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, [str, int]),
|
||||
"authenticated": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, []),
|
||||
"activated": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, []),
|
||||
"disconnected": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, []),
|
||||
"pointer-grabbed": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, []),
|
||||
"pointer-ungrabbed": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, []),
|
||||
"keyboard-grabbed": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, []),
|
||||
"keyboard-ungrabbed": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, []),
|
||||
}
|
||||
|
||||
def __init__(self, topwin, autograbkey=False):
|
||||
gtk.DrawingArea.__init__(self)
|
||||
|
||||
self.fb = GRFBFrameBuffer(self)
|
||||
self.client = None
|
||||
self.authenticated = False
|
||||
self.needpw = True
|
||||
self.autograbkey = autograbkey
|
||||
self.autograbptr = True
|
||||
self.topwin = topwin
|
||||
self.accel_groups = gtk.accel_groups_from_object(topwin)
|
||||
self.preferred_encoding = (rfb.ENCODING_RAW, rfb.ENCODING_DESKTOP_RESIZE)
|
||||
# Current impl of draw_solid is *far* too slow to be practical
|
||||
# for Hextile which likes lots of 1x1 pixels solid rectangles
|
||||
#self.preferred_encoding = (rfb.ENCODING_HEXTILE, rfb.ENCODING_RAW, rfb.ENCODING_DESKTOP_RESIZE)
|
||||
|
||||
self.fb.connect("resize", self.resize_display)
|
||||
self.fb.connect("invalidate", self.repaint_region)
|
||||
|
||||
self.connect("expose-event", self.expose_region)
|
||||
|
||||
self.connect("motion-notify-event", self.pointer_move)
|
||||
self.connect("button-press-event", self.pointer_press)
|
||||
self.connect("button-release-event", self.pointer_release)
|
||||
self.connect("scroll-event", self.pointer_scroll)
|
||||
self.connect("key-press-event", self.key_press)
|
||||
self.connect("key-release-event", self.key_release)
|
||||
self.connect("enter-notify-event", self.enter_notify)
|
||||
self.connect("leave-notify-event", self.leave_notify)
|
||||
self.connect("focus-in-event", self.focus_in)
|
||||
self.connect("focus-out-event", self.focus_out)
|
||||
|
||||
# We keep a big list of likely modifier keys, so when we get
|
||||
# a focus-out event while one of these is presed, we then
|
||||
# send a fake KeyUp event to VNC. This avoid trouble with
|
||||
# the guest having 'stuck' modifier keys.
|
||||
self.modifiers = (gtk.gdk.keyval_from_name("Shift_L"), \
|
||||
gtk.gdk.keyval_from_name("Shift_R"), \
|
||||
gtk.gdk.keyval_from_name("Control_L"), \
|
||||
gtk.gdk.keyval_from_name("Control_R"), \
|
||||
gtk.gdk.keyval_from_name("Caps_Lock"), \
|
||||
gtk.gdk.keyval_from_name("Shift_Lock"), \
|
||||
gtk.gdk.keyval_from_name("Meta_L"), \
|
||||
gtk.gdk.keyval_from_name("Meta_R"), \
|
||||
gtk.gdk.keyval_from_name("Alt_L"), \
|
||||
gtk.gdk.keyval_from_name("Alt_R"), \
|
||||
gtk.gdk.keyval_from_name("Super_L"), \
|
||||
gtk.gdk.keyval_from_name("Super_R"), \
|
||||
gtk.gdk.keyval_from_name("Hyper_L"), \
|
||||
gtk.gdk.keyval_from_name("Hyper_R"), \
|
||||
gtk.gdk.keyval_from_name("ISO_Lock"), \
|
||||
gtk.gdk.keyval_from_name("ISO_Level2_Latch"), \
|
||||
gtk.gdk.keyval_from_name("ISO_Level2_Shift"), \
|
||||
gtk.gdk.keyval_from_name("ISO_Level3_Latch"), \
|
||||
gtk.gdk.keyval_from_name("ISO_Level3_Lock"), \
|
||||
gtk.gdk.keyval_from_name("ISO_Group_Shift"), \
|
||||
gtk.gdk.keyval_from_name("ISO_Group_Latch"), \
|
||||
gtk.gdk.keyval_from_name("ISO_Group_Lock"), \
|
||||
gtk.gdk.keyval_from_name("ISO_Next_Group"), \
|
||||
gtk.gdk.keyval_from_name("ISO_Next_Group_Lock"), \
|
||||
gtk.gdk.keyval_from_name("ISO_Prev_Group"), \
|
||||
gtk.gdk.keyval_from_name("ISO_Prev_Group_Lock"), \
|
||||
gtk.gdk.keyval_from_name("ISO_First_Group"), \
|
||||
gtk.gdk.keyval_from_name("ISO_First_Group_Lock"), \
|
||||
gtk.gdk.keyval_from_name("ISO_Last_Group"), \
|
||||
gtk.gdk.keyval_from_name("ISO_Last_Group_Lock"), \
|
||||
gtk.gdk.keyval_from_name("Mode_switch"), \
|
||||
gtk.gdk.keyval_from_name("Num_Lock"), \
|
||||
)
|
||||
self.modifiersOn = {}
|
||||
|
||||
# If we press one of these keys 3 times in a row
|
||||
# its become sticky until a key outside this set
|
||||
# is pressed. This lets you do Ctrl-Alt-F1, eg
|
||||
# by "Ctrl Ctrl Ctrl Alt-F1"
|
||||
self.stickyMods = (gtk.gdk.keyval_from_name("Alt_L"), \
|
||||
gtk.gdk.keyval_from_name("Alt_R"), \
|
||||
gtk.gdk.keyval_from_name("Shift_L"), \
|
||||
gtk.gdk.keyval_from_name("Shift_R"), \
|
||||
gtk.gdk.keyval_from_name("Super_L"), \
|
||||
gtk.gdk.keyval_from_name("Super_R"), \
|
||||
gtk.gdk.keyval_from_name("Hyper_L"), \
|
||||
gtk.gdk.keyval_from_name("Hyper_R"), \
|
||||
gtk.gdk.keyval_from_name("Meta_L"), \
|
||||
gtk.gdk.keyval_from_name("Meta_R"), \
|
||||
gtk.gdk.keyval_from_name("Control_L"), \
|
||||
gtk.gdk.keyval_from_name("Control_R"))
|
||||
self.ctrlMods = (gtk.gdk.keyval_from_name("Control_L"), \
|
||||
gtk.gdk.keyval_from_name("Control_R"))
|
||||
self.altMods = (gtk.gdk.keyval_from_name("Alt_L"), \
|
||||
gtk.gdk.keyval_from_name("Alt_R"))
|
||||
self.lastKeyVal = None
|
||||
self.lastKeyRepeat = 0
|
||||
|
||||
empty = gtk.gdk.Pixmap(None, 1, 1, 1)
|
||||
clear = gtk.gdk.Color()
|
||||
self.nullcursor = gtk.gdk.Cursor(empty, empty, clear, clear, 0, 0)
|
||||
|
||||
self.set_events(gtk.gdk.EXPOSURE_MASK |
|
||||
gtk.gdk.LEAVE_NOTIFY_MASK |
|
||||
gtk.gdk.ENTER_NOTIFY_MASK |
|
||||
gtk.gdk.KEY_RELEASE_MASK |
|
||||
gtk.gdk.KEY_PRESS_MASK |
|
||||
gtk.gdk.BUTTON_RELEASE_MASK |
|
||||
gtk.gdk.BUTTON_PRESS_MASK |
|
||||
gtk.gdk.POINTER_MOTION_MASK |
|
||||
gtk.gdk.POINTER_MOTION_HINT_MASK)
|
||||
|
||||
self.set_property("can-focus", True)
|
||||
|
||||
def get_framebuffer_name(self):
|
||||
return self.fb.get_name()
|
||||
|
||||
def connect_to_host(self, host, port, debug=0):
|
||||
self.disconnect_from_host()
|
||||
|
||||
client = GRFBNetworkClient(host, port, self.fb, debug=debug, preferred_encoding=self.preferred_encoding)
|
||||
client.connect("disconnected", self._client_disconnected)
|
||||
|
||||
auth_types = client.init()
|
||||
|
||||
# NB we delibrately dont assign to self.client until
|
||||
# we're successfully connected.
|
||||
self.client = client
|
||||
self.authenticated = False
|
||||
self.emit("connected", host, port)
|
||||
if rfb.AUTH_NONE in auth_types:
|
||||
self.needpw = False
|
||||
else:
|
||||
self.needpw = True
|
||||
return self.needpw
|
||||
|
||||
def _client_disconnected(self, src):
|
||||
self.client = None
|
||||
self.emit("disconnected")
|
||||
|
||||
def disconnect_from_host(self):
|
||||
if self.client == None:
|
||||
return
|
||||
|
||||
# Reset server state in case we have modifiers pressed
|
||||
for key in self.modifiersOn.keys():
|
||||
if not(self.client is None):
|
||||
self.client.update_key(0, key)
|
||||
self.modifiersOn = {}
|
||||
|
||||
self.client.close()
|
||||
self.client = None
|
||||
self.emit("disconnected")
|
||||
|
||||
def authenticate(self, password):
|
||||
self.client.setpass(password)
|
||||
try:
|
||||
self.client.auth()
|
||||
except Exception, e:
|
||||
logging.warn("Failure while authenticating " + str(e))
|
||||
self.disconnect_from_host()
|
||||
return 0
|
||||
self.authenticated = True
|
||||
self.emit("authenticated")
|
||||
return 1
|
||||
|
||||
def activate(self):
|
||||
if self.client == None:
|
||||
return
|
||||
|
||||
self.client.start()
|
||||
self.client.request_update()
|
||||
self.emit("activated")
|
||||
|
||||
def is_authenticated(self):
|
||||
if not(self.is_connected()):
|
||||
return False
|
||||
return self.authenticated
|
||||
|
||||
def needs_password(self):
|
||||
return self.needpw
|
||||
|
||||
def is_connected(self):
|
||||
if self.client == None:
|
||||
return False
|
||||
return True
|
||||
|
||||
def state_to_mask(self, state):
|
||||
mask = 0
|
||||
if state & gtk.gdk.BUTTON1_MASK:
|
||||
mask = mask + 1
|
||||
if state & gtk.gdk.BUTTON2_MASK:
|
||||
mask = mask + 2
|
||||
if state & gtk.gdk.BUTTON3_MASK:
|
||||
mask = mask + 4
|
||||
if state & gtk.gdk.BUTTON4_MASK:
|
||||
mask = mask + 8
|
||||
if state & gtk.gdk.BUTTON5_MASK:
|
||||
mask = mask + 16
|
||||
return mask
|
||||
|
||||
def take_screenshot(self):
|
||||
return self.fb.clone_pixmap()
|
||||
|
||||
def pointer_move(self, win, event):
|
||||
self.update_pointer(win, event)
|
||||
|
||||
def pointer_press(self, win, event):
|
||||
if not gtk.gdk.pointer_is_grabbed() and self.will_autograb_pointer():
|
||||
self.grab_pointer()
|
||||
self.update_pointer(win, event)
|
||||
|
||||
def pointer_release(self, win, event):
|
||||
self.update_pointer(win, event)
|
||||
|
||||
def pointer_scroll(self, win, event):
|
||||
if self.client != None:
|
||||
x, y, state = event.window.get_pointer()
|
||||
newstate = state
|
||||
if event.direction == gtk.gdk.SCROLL_UP:
|
||||
newstate = newstate | gtk.gdk.BUTTON4_MASK
|
||||
else:
|
||||
newstate = newstate | gtk.gdk.BUTTON5_MASK
|
||||
self.client.update_pointer(self.state_to_mask(newstate), x, y)
|
||||
self.client.update_pointer(self.state_to_mask(state), x, y)
|
||||
|
||||
def update_pointer(self, win, event):
|
||||
if self.client != None:
|
||||
x, y, state = event.window.get_pointer()
|
||||
self.client.update_pointer(self.state_to_mask(state), x, y)
|
||||
return True
|
||||
|
||||
|
||||
def will_autograb_pointer(self):
|
||||
return self.autograbptr
|
||||
|
||||
def set_autograb_pointer(self, grab):
|
||||
self.autograbptr = grab
|
||||
if grab == False and gtk.gdk.pointer_is_grabbed():
|
||||
self.ungrab_pointer()
|
||||
|
||||
def has_grabbed_keyboard(self):
|
||||
return self.grabbedKeyboard
|
||||
|
||||
def will_autograb_keyboard(self):
|
||||
return self.autograbkey
|
||||
|
||||
def set_autograb_keyboard(self, grab):
|
||||
self.autograbkey = grab
|
||||
if grab == False and self.grabbedKeyboard:
|
||||
self.ungrab_keyboard()
|
||||
|
||||
|
||||
def grab_pointer(self):
|
||||
gtk.gdk.pointer_grab(self.window, False,
|
||||
gtk.gdk.LEAVE_NOTIFY_MASK |
|
||||
gtk.gdk.ENTER_NOTIFY_MASK |
|
||||
gtk.gdk.BUTTON_RELEASE_MASK |
|
||||
gtk.gdk.BUTTON_PRESS_MASK |
|
||||
gtk.gdk.POINTER_MOTION_MASK |
|
||||
gtk.gdk.POINTER_MOTION_HINT_MASK,
|
||||
self.window, self.nullcursor)
|
||||
self.emit("pointer-grabbed")
|
||||
|
||||
def ungrab_pointer(self):
|
||||
gtk.gdk.pointer_ungrab()
|
||||
self.emit("pointer-ungrabbed")
|
||||
|
||||
def grab_keyboard(self):
|
||||
gtk.gdk.keyboard_grab(self.window, False, long(0))
|
||||
for g in self.accel_groups:
|
||||
self.topwin.remove_accel_group(g)
|
||||
self.gtk_settings = gtk.settings_get_default()
|
||||
self.gtk_settings_accel = self.gtk_settings.get_property('gtk-menu-bar-accel')
|
||||
self.gtk_settings.set_property('gtk-menu-bar-accel', None)
|
||||
self.grabbedKeyboard = True
|
||||
self.emit("keyboard-grabbed")
|
||||
|
||||
def ungrab_keyboard(self):
|
||||
gtk.gdk.keyboard_ungrab()
|
||||
for g in self.accel_groups:
|
||||
self.topwin.add_accel_group(g)
|
||||
self.gtk_settings.set_property('gtk-menu-bar-accel', self.gtk_settings_accel)
|
||||
self.grabbedKeyboard = False
|
||||
self.emit("keyboard-ungrabbed")
|
||||
|
||||
def enter_notify(self, win, event):
|
||||
if self.autograbkey:
|
||||
self.grab_keyboard()
|
||||
|
||||
def leave_notify(self, win, event):
|
||||
if self.autograbkey:
|
||||
self.ungrab_keyboard()
|
||||
|
||||
def focus_in(self, win, event):
|
||||
self.modifiersOn = {}
|
||||
|
||||
def focus_out(self, win, event):
|
||||
# Forceably release any modifiers still on
|
||||
for key in self.modifiersOn.keys():
|
||||
if not(self.client is None):
|
||||
self.client.update_key(0, key)
|
||||
self.modifiersOn = {}
|
||||
|
||||
|
||||
def key_press(self, win, event):
|
||||
# Key handling in VNC is screwy. The event.keyval from GTK is
|
||||
# interpreted relative to modifier state. This really messes
|
||||
# up with VNC which has no concept of modifiers. If we interpret
|
||||
# at client end you can end up with 'Alt' key press generating
|
||||
# Alt_L, and key release generated ISO_Prev_Group. This really
|
||||
# really confuses the VNC server - 'Alt' gets stuck on.
|
||||
#
|
||||
# So we have to redo GTK's keycode -> keyval translation
|
||||
# using only the SHIFT modifier which explicitly has to be
|
||||
# interpreted at client end.
|
||||
map = gtk.gdk.keymap_get_default()
|
||||
maskedstate = event.state & (gtk.gdk.SHIFT_MASK | gtk.gdk.LOCK_MASK)
|
||||
(val,group,level,mod) = map.translate_keyboard_state(event.hardware_keycode, maskedstate, 0)
|
||||
|
||||
stickyVal = None
|
||||
|
||||
# Check modifiers for sticky keys, or pointer ungrab
|
||||
if val in self.stickyMods:
|
||||
# No previous mod pressed, start counting our presses
|
||||
if self.lastKeyVal == None:
|
||||
self.lastKeyVal = val
|
||||
self.lastKeyRepeat = 1
|
||||
else:
|
||||
# Check for Alt+Ctrl or Ctrl+Alt to release grab
|
||||
if ((self.lastKeyVal in self.ctrlMods and val in self.altMods) or \
|
||||
(self.lastKeyVal in self.altMods and val in self.ctrlMods)) and \
|
||||
gtk.gdk.pointer_is_grabbed():
|
||||
self.ungrab_pointer()
|
||||
|
||||
if self.lastKeyVal == val:
|
||||
# Match last key pressed, so increase count
|
||||
self.lastKeyRepeat = self.lastKeyRepeat + 1
|
||||
elif self.lastKeyRepeat < 3:
|
||||
# Different modifier & last one was not yet
|
||||
# sticky so reset it
|
||||
self.lastKeyVal = None
|
||||
else:
|
||||
# If the prev modifier was pressed 3 times in row its sticky
|
||||
if self.lastKeyVal != None and self.lastKeyRepeat >= 3:
|
||||
stickyVal = self.lastKeyVal
|
||||
|
||||
if self.client != None:
|
||||
# Send fake sticky modifier key
|
||||
if stickyVal != None:
|
||||
self.client.update_key(1, stickyVal)
|
||||
|
||||
self.client.update_key(1, val)
|
||||
#self.client.update_key(1, event.keyval)
|
||||
|
||||
if val in self.modifiers:
|
||||
self.modifiersOn[val] = 1
|
||||
|
||||
return True
|
||||
|
||||
def key_release(self, win, event):
|
||||
# Key handling in VNC is screwy. See above
|
||||
map = gtk.gdk.keymap_get_default()
|
||||
maskedstate = event.state & (gtk.gdk.SHIFT_MASK | gtk.gdk.LOCK_MASK)
|
||||
(val,group,level,mod) = map.translate_keyboard_state(event.hardware_keycode, maskedstate, 0)
|
||||
|
||||
stickyVal = None
|
||||
|
||||
if not(val in self.stickyMods):
|
||||
# If a sticky modifier is active, we must release it
|
||||
if self.lastKeyVal != None and self.lastKeyRepeat >= 3:
|
||||
stickyVal = self.lastKeyVal
|
||||
|
||||
# Release of any non-modifier clears stickyness
|
||||
self.lastKeyVal = None
|
||||
|
||||
if self.client != None:
|
||||
if val in self.modifiers and self.modifiersOn.has_key(val):
|
||||
del self.modifiersOn[val]
|
||||
|
||||
self.client.update_key(0, val)
|
||||
#self.client.update_key(0, event.keyval)
|
||||
|
||||
# Release the sticky modifier
|
||||
if stickyVal != None:
|
||||
self.client.update_key(0, stickyVal)
|
||||
|
||||
|
||||
return True
|
||||
|
||||
def get_frame_buffer(self):
|
||||
return self.fb
|
||||
|
||||
def resize_display(self, fb, width, height):
|
||||
self.set_size_request(width, height)
|
||||
|
||||
def repaint_region(self,fb, x, y, width, height):
|
||||
if self.fb.get_pixmap() == None:
|
||||
return
|
||||
gc = self.window.new_gc()
|
||||
self.window.draw_drawable(gc, self.fb.get_pixmap(), x, y, x, y, width, height)
|
||||
|
||||
def expose_region(self, win, event):
|
||||
if self.fb.get_pixmap() == None:
|
||||
return
|
||||
gc = self.window.new_gc()
|
||||
self.window.draw_drawable(gc, self.fb.get_pixmap(), event.area.x, event.area.y, event.area.x, event.area.y, event.area.width, event.area.height)
|
||||
|
||||
gobject.type_register(GRFBViewer)
|
||||
|
||||
|
||||
def main():
|
||||
host = sys.argv[1]
|
||||
port = int(sys.argv[2])
|
||||
password = None
|
||||
if len(sys.argv) == 4:
|
||||
password = sys.argv[3]
|
||||
|
||||
win = gtk.Window()
|
||||
win.set_name("VNC")
|
||||
win.connect("destroy", lambda w: gtk.main_quit())
|
||||
|
||||
pane = gtk.ScrolledWindow()
|
||||
pane.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
win.add(pane)
|
||||
|
||||
vp = gtk.Viewport()
|
||||
pane.add(vp)
|
||||
|
||||
vnc = GRFBViewer(win, autograbkey=True)
|
||||
vp.add(vnc)
|
||||
|
||||
win.show_all()
|
||||
win.present()
|
||||
|
||||
if vnc.connect_to_host(host, port, debug=0):
|
||||
print "Need password"
|
||||
if password == None:
|
||||
return 1
|
||||
else:
|
||||
print "No password needed"
|
||||
vnc.authenticate(password)
|
||||
vnc.activate()
|
||||
|
||||
win.set_title(vnc.get_framebuffer_name())
|
||||
|
||||
def autosize():
|
||||
rootWidth = gtk.gdk.screen_width()
|
||||
rootHeight = gtk.gdk.screen_height()
|
||||
|
||||
vncWidth, vncHeight = vnc.get_size_request()
|
||||
|
||||
if vncWidth > (rootWidth-200):
|
||||
vncWidth = rootWidth - 200
|
||||
if vncHeight > (rootHeight-200):
|
||||
vncHeight = rootHeight - 200
|
||||
|
||||
vp.set_size_request(vncWidth+3, vncHeight+3)
|
||||
|
||||
def resize(src, size):
|
||||
autosize()
|
||||
|
||||
vnc.connect('size-request', resize)
|
||||
|
||||
gtk.main()
|
||||
vnc.disconnect_from_host()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -44,6 +44,8 @@ Requires: librsvg2
|
|||
Requires: vte >= 0.12.2
|
||||
# For online help
|
||||
Requires: scrollkeeper
|
||||
# For console widget
|
||||
Requires: gtk-vnc
|
||||
|
||||
ExclusiveArch: %{ix86} x86_64 ia64
|
||||
|
||||
|
@ -129,10 +131,6 @@ fi
|
|||
%{_datadir}/%{name}/virtManager/*.pyc
|
||||
%{_datadir}/%{name}/virtManager/*.pyo
|
||||
|
||||
%{_datadir}/%{name}/vncViewer/*.py
|
||||
%{_datadir}/%{name}/vncViewer/*.pyc
|
||||
%{_datadir}/%{name}/vncViewer/*.pyo
|
||||
|
||||
%{_datadir}/omf/%{name}
|
||||
%{_datadir}/gnome/help
|
||||
|
||||
|
|
Loading…
Reference in New Issue