Skip to content

Commit

Permalink
Initial pyVNC Version
Browse files Browse the repository at this point in the history
  • Loading branch information
perara committed Aug 16, 2017
1 parent c4f4b6f commit 5f30d02
Show file tree
Hide file tree
Showing 9 changed files with 841 additions and 1,020 deletions.
1 change: 0 additions & 1 deletion .hgignore

This file was deleted.

100 changes: 100 additions & 0 deletions GUI.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
from twisted.internet import reactor
from constants import *


class GUI:

def __init__(self):
pygame.display.set_caption('pyVNC')
pygame.mouse.set_cursor(*POINTER)
pygame.key.set_repeat(500, 30)

# Variable Definitions
self.size = (400, 400)
self.clock = pygame.time.Clock()
self.alive = 1
self.loopcounter = 0
self.buttons = 0
self.protocol = None
self.buffer = None

self.set_rfb_size(*self.size)

def set_rfb_size(self, width, height, depth=32):
"""change screen size"""
self.width, self.height = width, height
self.area = Rect(0, 0, width, height)
winstyle = 0 # |FULLSCREEN
if depth == 32:
self.screen = pygame.display.set_mode(self.area.size, winstyle, 32)
elif depth == 8:
self.screen = pygame.display.set_mode(self.area.size, winstyle, 8)
else:
raise ValueError("color depth not supported")
self.background = pygame.Surface((self.width, self.height), depth)
self.background.fill(0) # black

def set_protocol(self, protocol):
"""attach a protocol instance to post the events to"""
self.protocol = protocol

def check_events(self):
"""process events from the queue"""
seen_events = 0
for e in pygame.event.get():
seen_events = 1

if e.type == QUIT:
self.alive = 0
reactor.stop()

if self.protocol is not None:

if e.type == KEYDOWN:
if e.key in MODIFIERS:
self.protocol.key_event(MODIFIERS[e.key], down=1)
elif e.key in KEYMAPPINGS:
self.protocol.key_event(KEYMAPPINGS[e.key], down=1)
elif e.unicode:
self.protocol.key_event(ord(e.unicode))
else:
print("warning: unknown key %r" % (e))

elif e.type == KEYUP:
if e.key in MODIFIERS:
self.protocol.key_event(MODIFIERS[e.key], down=0)
elif e.key in KEYMAPPINGS:
self.protocol.key_event(KEYMAPPINGS[e.key], down=0)
elif e.type == MOUSEMOTION:
self.buttons = e.buttons[0] and 1
self.buttons |= e.buttons[1] and 2
self.buttons |= e.buttons[2] and 4
self.protocol.pointer_event(e.pos[0], e.pos[1], self.buttons)
elif e.type == MOUSEBUTTONUP:
if e.button == 1: self.buttons &= ~1
if e.button == 2: self.buttons &= ~2
if e.button == 3: self.buttons &= ~4
if e.button == 4: self.buttons &= ~8
if e.button == 5: self.buttons &= ~16
self.protocol.pointer_event(e.pos[0], e.pos[1], self.buttons)

elif e.type == MOUSEBUTTONDOWN:
if e.button == 1: self.buttons |= 1
if e.button == 2: self.buttons |= 2
if e.button == 3: self.buttons |= 4
if e.button == 4: self.buttons |= 8
if e.button == 5: self.buttons |= 16
self.protocol.pointer_event(e.pos[0], e.pos[1], self.buttons)

return not seen_events
return not seen_events

def loop(self, dum=None):
"""gui 'mainloop', it is called repeated by twisteds mainloop
by using callLater"""
# ~ self.clock.tick()
no_work = self.check_events()
if self.alive:
self.buffer = pygame.surfarray.array3d(self.screen).swapaxes(0, 1)
reactor.callLater(no_work and 0.020, self.loop)

123 changes: 44 additions & 79 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,40 @@
Simple VNC viewer that is built with
# pyVNC
pyVNC Client is a client library for interacting programatically (and physically) with a VNC session.
pyVNC Client that is built with
[Twisted-Python](https://twistedmatrix.com/trac/) and
[PyGame](http://www.pygame.org/). Originally written by
[Chris Liechti](http://homepage.hispeed.ch/py430/python/).

The viewer supports the following encodings:
`Hextile, CoRRE, RRE, RAW, CopyRect`

The display is done using pygame because of it's good graphics
performance, but any GUI system can be used as the code is
modular and can be easily adapted. Two good examples of code
reuse are
[VNC client in browser](http://arkaitzj.wordpress.com/2011/11/12/vnc-in-your-browser-through-websockets-handled-by-gevent/)
by Arkaitz Jimenez, and most recent
[vncdotool](https://github.com/sibson/vncdotool) by Marc Sibson.
Pygame version supports clipboard transfer, but it's not used in
the sample application.

Usage
-----
You can simply start `vncviewer.py` and it will ask for a hostname
and password if required. Hostnames are in the "host:display"
form, where "display" is the VNC dispaly number.

These settings can be passed through command line, but note
it's a bad idea to pass the password in this way as it can be
snooped by other users with a simple process listing!
Try `-h` or `--help` to see supported options.

Please keep in mind that VNC transimts keypresses in cleartext,
so don't type in passwords on non-encrypted connection over
insecure networks.

With "--depth" a display depth can be gived, use "8" for
slower connections.

The "--fast" option uses only `RAW` and `CopyRect` encodings
and is thus only suitable for fast connections. But it delivers
better performance than other encodings.

What is it good for?
--------------------
Nothing ;-) Use the original VNC viewer for better performance.

However it works very well and I think with a good speed.
It could be embedded in other Python applications or it can
server as a base of various supervision or remote desktop
applications, automated tests of GUIs or be embedded in a tool
for remote support...

Bugs, etc
---------
- Properties dialog is missing. Like for specifying encodings etc.
- Listen mode not implemented.
- Key repetition does not work (pygame?)
- It does not reduce update requests when minimized.
- Screen cannot be scolled, impractical if remote desktop is larger
than local
- The password dialog blocks twisted and everthing else

References:
-----------
[PyGame](http://www.pygame.org/).

The client supports the following encodings: `Hextile, CoRRE, RRE, RAW, CopyRect`

pyVNC is tested for `Python >= 3.5`

#Usage

## Example 1
```py
vnc = VNCClient(host="127.0.0.1",
password=None,
port=5902,
depth=32,
fast=False,
shared=True) # Default parameters

vnc.start() # Starts the vnc client (Threaded)

vnc.send_key("a") # Sends the key "a"
vnc.send_mouse("Left", (200, 200)) # Left Clicks at x=200, y=200
vnc.send_mouse("Right", (200, 200)) # Right Clicks at x=200, y=200
vnc.get_screen() # Get a array representation of the screen shape: (?, ?, 3)
vnc.join() # Exit
```

## Parameters
`pyVNC.py --host=127.0.0.1 --password=None --depth=32 --fast=False, shared=False`

# What is it good for?
pyVNC is excellent for automating tasks inside a VNC session.

# References:
- http://homepage.hispeed.ch/py430/python/
- http://code.google.com/p/vnc2flv/
- http://arkaitzj.wordpress.com/2011/11/12/vnc-in-your-browser-through-websockets-handled-by-gevent/
Expand All @@ -69,10 +44,15 @@ References:
- http://www.pygame.org
- http://www.realvnc.org

-------
## Copyright Notice
Thanks to the original authors for providing an excellent implemenation of the VNC protocol in python.
This project would not have been possible with their work:
- (c) 2003 chris <[email protected]>
- (c) 2009 techtonik <[email protected]>

And pyVNC author:
- (c) 2017 Per-Arne Andersen <[email protected]>

Released under the MIT License.

You're free to use it for commercial and noncommercial
Expand All @@ -81,20 +61,5 @@ copyright notices are intact. There are no warranties, not
even that it does what it says to do ;-)


Changes:
--------
2015.08.29 - expored to Github
2009.12.14 - 4. another update
* replaced crippled_des.py with pyDes
* TAB and BACKSPACE keys now work
2009.12.3 - 3. update
* changed license to MIT with Chris consent as Python license
is not supported by Google Code
* works with twisted 8.2.0
* works with pygame 1.9.1 (blit failed on locked surfaces)
* don't refuse to connect to 3.7 and 3.8 VNC servers
2003.3.4 - 2. release
* improved performance with RRE, CoRRE
* color depth can be choosen (32, 8)
* added "fast" option
2003.3.3 - 1. public release
## Changes
16.08.17 - Forked and reworked as a client library
103 changes: 103 additions & 0 deletions RFBToGUI.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import pygame
import struct
import rfb


class RFBToGUI(rfb.RFBClient):
"""RFBClient protocol that talks to the GUI app"""
def __init__(self):
super().__init__()

def vnc_connection_made(self):
"""choose appropriate color depth, resize screen"""
# ~ print "Screen format: depth=%d bytes_per_pixel=%r" % (self.depth, self.bpp)
# ~ print "Desktop name: %r" % self.name

# ~ print "redmax=%r, greenmax=%r, bluemax=%r" % (self.redmax, self.greenmax, self.bluemax)
# ~ print "redshift=%r, greenshift=%r, blueshift=%r" % (self.redshift, self.greenshift, self.blueshift)
self.frame_buffer = self.factory.remoteframebuffer
self.screen = self.frame_buffer.screen
self.frame_buffer.set_protocol(self)
self.frame_buffer.set_rfb_size(self.width, self.height, 32)
self.set_encodings(self.factory.encodings)
self.set_pixel_format() # set up pixel format to 32 bits
self.framebuffer_update_request() # request initial screen update

def vnc_request_password(self):
if self.factory.password is not None:
self.send_password(self.factory.password)
"""else:
# XXX hack, this is blocking twisted!!!!!!!
screen = pygame.display.set_mode((220, 40))
screen.fill((255, 100, 0)) # redish bg
self.send_password(inputbox.ask(screen, "Password", password=1))
"""
# ~ def beginUpdate(self):
# ~ """start with a new series of display updates"""

def begin_update(self):
"""begin series of display updates"""
# ~ log.msg("screen lock")

def commit_update(self, rectangles=None):
"""finish series of display updates"""
# ~ log.msg("screen unlock")
pygame.display.update(rectangles)
self.framebuffer_update_request(incremental=1)
#self.remoteframebuffer.data = np.array(self.remoteframebuffer._data, copy=True)

def update_rectangle(self, x, y, width, height, data):
"""new bitmap data"""
# print("%s " * 5 % (x, y, width, height, len(data)))
# ~ log.msg("screen update")
self.screen.blit(
pygame.image.fromstring(data, (width, height), 'RGBX'), # TODO color format
(x, y)
)

def copy_rectangle(self, srcx, srcy, x, y, width, height):
"""copy src rectangle -> destinantion"""
# ~ print "copyrect", (srcx, srcy, x, y, width, height)
self.screen.blit(self.screen,
(x, y),
(srcx, srcy, width, height)
)

def fill_rectangle(self, x, y, width, height, color):
"""fill rectangle with one color"""
# ~ remoteframebuffer.CopyRect(srcx, srcy, x, y, width, height)
color = struct.unpack("BBBB", color)
rect = (x, y, width, height)

#self.remoteframebuffer._data[y:y+width, x:x+width] = [color[0], color[1], color[2]]
self.screen.fill(color, rect)

def bell(self):
print("katsching")

def copy_text(self, text):
print("Clipboard: %r" % text)


class RFBToGUIeightbits(RFBToGUI):
def vnc_connection_made(self):
"""choose appropriate color depth, resize screen"""
self.remoteframebuffer = self.factory.remoteframebuffer
self.screen = self.remoteframebuffer.screen
self.remoteframebuffer.set_protocol(self)
self.remoteframebuffer.set_rfb_size(self.width, self.height, 8)
self.set_encodings(self.factory.encodings)
self.set_pixel_format(bpp=8, depth=8, bigendian=0, truecolor=1,
redmax=7, greenmax=7, bluemax=3,
redshift=5, greenshift=2, blueshift=0
)
self.palette = self.screen.get_palette()
self.framebuffer_update_request()

def update_rectangle(self, x, y, width, height, data):
bmp = pygame.image.fromstring(data, (width, height), 'P')
bmp.set_palette(self.palette)
self.screen.blit(bmp, (x, y))

def fill_rectangle(self, x, y, width, height, color):
self.screen.fill(ord(color), (x, y, width, height))
Loading

0 comments on commit 5f30d02

Please sign in to comment.