- Added cursor pseudo encoding support. - Added Windows support for cursor image capturing in `get_cursor_image` method. Implemented Windows-specific logic using the `win32gui`, `win32ui`, and related libraries to capture the current cursor image, enhancing the cross-platform capability of the application. - Fixed issues in `get_bitmap` method for handling different bpp formats. Specifically addressed the processing logic for 16 bpp (BGR565) format, ensuring that the image conversion and rendering are handled correctly for VNC clients expecting this format. - Added initial Tight encoding support. - Updated the `send_image` method in the Tight encoding class to correctly handle JPEG and ZLIB compression. This includes proper signaling to the client about the type of compression used (JPEG or ZLIB) and ensuring that the data is formatted and sent according to the Tight encoding specifications. - Added checks and conversions in `send_image` to handle different image modes (like RGBX and RGBA) and convert them to the appropriate format (RGB) before compression and transmission. - Implemented a more robust and accurate method for determining when to use JPEG compression in Tight encoding based on the unique color count and image characteristics. These updates significantly improve the functionality, stability, and compatibility of the VNC server, particularly in handling different pixel formats and encoding methods.
103 lines
4.0 KiB
Python
103 lines
4.0 KiB
Python
# coding=utf-8
|
|
# pyvncs
|
|
# Copyright (C) 2017-2018 Matias Fernandez
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it under
|
|
# the terms of the GNU Lesser General Public License as published by the Free
|
|
# Software Foundation, either version 3 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 Lesser General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import os
|
|
import struct
|
|
import ctypes
|
|
import ctypes.util
|
|
import platform
|
|
from PIL import Image, ImageChops, ImageDraw, ImagePalette
|
|
import base64
|
|
from io import BytesIO
|
|
|
|
from . import common
|
|
from struct import *
|
|
from lib import log
|
|
|
|
OS_NAME = platform.system()
|
|
|
|
if OS_NAME == 'Linux':
|
|
import lib.oshelpers.x11 as x11
|
|
Xcursor = x11.XCursor
|
|
|
|
if OS_NAME == 'Windows':
|
|
import lib.oshelpers.windows_cursor as windows_cursor
|
|
|
|
|
|
class Encoding:
|
|
name = 'Cursor'
|
|
id = -239
|
|
description = 'Cursor pseudo encoding'
|
|
enabled = True
|
|
pseudoEncoding = True
|
|
|
|
cursor_sent = False
|
|
|
|
def __init__(self):
|
|
log.debug("Initialized", __name__)
|
|
self.default_cursor_data = 'iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAACc0lEQVR4nK2Wv0sbYRjHP/fDyNUGalKitBDI4FAojg5NF8M7BFIPUkvSopuQoYMgFrl/wbWbndrFuBocAroVHCIdJAaxzdJuNYXGBpIgsU8H7zSpGo3mhS/3Hvfe9/N837t7OI3zobXNhT4Nre2otZ3/7RdEbwOYSqk4MACYbdfuPDTXcKherzeUUklgyAX1BaK5ZkERkUql0lBKpYD7/YJogA94LCJi27YcHh42lVLpfkE0YBAIi4iEw2FJJpNSqVSaSqnX/YB0ACKRiEQiEZmenu4bpAMwNjZ2pnQ6fWfIhcWGYZxpd3eX+fn5wWw2+1Ep9cItxOgFcmGhaZodKhaLLCws3BrSNYGnYrHI4uKiB0n0Ark2gadSqcTS0tLg2traJxficyHaBddeE3gqlUo4juNB4m0proR4Dc4HjIjI92g0ekrWdQzDoNVqsbq6Sjgc7rix0Wg0bdt+tbW1ladLczQvS6DrOo7jsLe3Ry6XY319nUAg8GV2dvY90ASqwFfg51XG/6c4+w5isZjk83nZ39//XS6XZXJyUqampqRarR6HQqEo8Ah4CFj08AzEq8RxHCzL+jExMfGu1Wr9Gh8fp9FosL29PTA3N/cSqLkJmsDJdQnaAScAmqZ9i8fjb2u12ueVlZWcbduYpsnGxgaZTOYNp9vaterLtsgAApubmwXLsp4BIcA/PDz8vFqtHs/MzEgikZCjoyMJBoNPOG0ZPUN8lmWNct5zBoDR5eXl3MHBgezs7EihUCgDI7cBtCfxXl0duKfr+tNUKvUhk8lk/X5/DPDTQy/qVoUH8QEP3PkfoE4PPwU3iemBcI25qTnAP3ZG9LuVtmFhAAAAAElFTkSuQmCC'
|
|
self.default_cursor_img = Image.open(BytesIO(base64.b64decode(self.default_cursor_data)))
|
|
|
|
def get_cursor_image(self):
|
|
if OS_NAME == 'Windows':
|
|
return windows_cursor.get_cursor_image()
|
|
|
|
elif OS_NAME == 'Linux':
|
|
cursor = Xcursor()
|
|
return cursor.get_cursor_image()
|
|
|
|
elif OS_NAME == 'Darwin':
|
|
if self.default_cursor_img.mode != 'RGBA':
|
|
self.default_cursor_img = self.default_cursor_img.convert('RGBA')
|
|
return self.default_cursor_img
|
|
|
|
else:
|
|
return None
|
|
|
|
|
|
def send_cursor(self, x, y, cursor):
|
|
sendbuff = bytearray()
|
|
sendbuff.extend(pack("!B", 0)) # message type 0 == SetCursorPosition
|
|
sendbuff.extend(pack("!H", x))
|
|
sendbuff.extend(pack("!H", y))
|
|
self.cursor_sent = True
|
|
|
|
if cursor is not None:
|
|
w, h = cursor.size
|
|
cursor_bytes = cursor.convert("RGBA").tobytes("raw", "BGRA")
|
|
|
|
# Invert alpha channel if needed
|
|
pixels = bytearray(cursor_bytes)
|
|
for i in range(0, len(pixels), 4):
|
|
pixels[i + 3] = 255 - pixels[i + 3]
|
|
|
|
sendbuff.extend(pack("!B", 1)) # message type 1 == SetCursorShape
|
|
sendbuff.extend(pack("!H", w)) # width
|
|
sendbuff.extend(pack("!H", h)) # height
|
|
sendbuff.extend(pixels)
|
|
|
|
else:
|
|
sendbuff.extend(pack("!B", 0)) # message type 0 == SetCursorPosition
|
|
sendbuff.extend(pack("!H", x))
|
|
sendbuff.extend(pack("!H", y))
|
|
|
|
return sendbuff
|
|
|
|
|
|
common.encodings[common.ENCODINGS.cursor] = Encoding
|
|
log.debug("Loaded encoding: %s (%s)" % (__name__, Encoding.id))
|