-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
841 additions
and
1,020 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/ | ||
|
@@ -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 | ||
|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) |
Oops, something went wrong.