mirror of
				https://gitee.com/gfdgd-xi/deep-wine-runner
				synced 2025-11-04 15:32:23 +08:00 
			
		
		
		
	初步的虚拟机vnc连接
This commit is contained in:
		
							parent
							
								
									83fd8fffd4
								
							
						
					
					
						commit
						24131b0050
					
				@ -62,7 +62,7 @@ if [[ $? == 0 ]] && [[ -f "$HOME/Qemu/Windows/Windows.qcow2" ]]; then
 | 
			
		||||
                fi
 | 
			
		||||
            fi
 | 
			
		||||
        else
 | 
			
		||||
            qemuUEFI="-vga virtio -device nec-usb-xhci,id=xhci,addr=0x1b -device usb-tablet,id=tablet,bus=xhci.0,port=1 "
 | 
			
		||||
            qemuUEFI="-vga virtio -machine usb=on -device usb-tablet "
 | 
			
		||||
        fi
 | 
			
		||||
        echo $qemuUEFI
 | 
			
		||||
        ./VM/kvm-ok
 | 
			
		||||
@ -70,9 +70,10 @@ if [[ $? == 0 ]] && [[ -f "$HOME/Qemu/Windows/Windows.qcow2" ]]; then
 | 
			
		||||
            echo X86 架构,使用 kvm 加速
 | 
			
		||||
            $qemuMore qemu-system-x86_64 --enable-kvm -cpu host --hda "$HOME/Qemu/Windows/Windows.qcow2" \
 | 
			
		||||
                -smp $CpuCount,sockets=$CpuSocketNum,cores=$(($CpuCoreNum / $CpuSocketNum)),threads=$(($CpuCount / $CpuCoreNum / $CpuSocketNum)) \
 | 
			
		||||
                -m ${use}G  -display vnc=:5 -display gtk -usb -nic model=rtl8139 $qemuUEFI \
 | 
			
		||||
                -m ${use}G  -display vnc=:5 -display gtk -nic model=rtl8139 $qemuUEFI \
 | 
			
		||||
                -device AC97 -device ES1370 -device intel-hda -device hda-duplex  \
 | 
			
		||||
                --boot 'splash=VM/boot.jpg,menu=on,splash-time=2000' \
 | 
			
		||||
                -usb \
 | 
			
		||||
                > $TMPDIR/tmp/windows-virtual-machine-installer-for-wine-runner-run.log 2>&1 # 最新的 qemu 已经移除参数 -soundhw all 
 | 
			
		||||
            exit
 | 
			
		||||
        fi
 | 
			
		||||
@ -99,7 +100,7 @@ if [[ $? == 0 ]] && [[ -f "$HOME/Qemu/Windows/Windows.qcow2" ]]; then
 | 
			
		||||
        echo 不使用 kvm 加速
 | 
			
		||||
        $qemuPath --hda "$HOME/Qemu/Windows/Windows.qcow2" \
 | 
			
		||||
            -smp $CpuCount,sockets=$CpuSocketNum,cores=$(($CpuCoreNum / $CpuSocketNum)),threads=$(($CpuCount / $CpuCoreNum / $CpuSocketNum)) \
 | 
			
		||||
            -m ${use}G  -display vnc=:5 -display gtk -usb -nic model=rtl8139 $qemuUEFI \
 | 
			
		||||
            -m ${use}G  -display vnc=:5 -display gtk -nic model=rtl8139 $qemuUEFI \
 | 
			
		||||
            -device AC97 -device ES1370 -device intel-hda -device hda-duplex  \
 | 
			
		||||
            --boot 'splash=VM/boot.jpg,menu=on,splash-time=2000' \
 | 
			
		||||
            > $TMPDIR/tmp/windows-virtual-machine-installer-for-wine-runner-run.log 2>&1 # 最新的 qemu 已经移除参数 -soundhw all 
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										44
									
								
								novnc-client/main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								novnc-client/main.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
			
		||||
import sys
 | 
			
		||||
import logging
 | 
			
		||||
from PyQt5.QtWidgets import QApplication, QMainWindow
 | 
			
		||||
from qvncwidget import QVNCWidget
 | 
			
		||||
#logging.basicConfig(level=logging.DEBUG)  # DEBUG及以上的日志信息都会显示
 | 
			
		||||
class Window(QMainWindow):
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        super(Window, self).__init__()
 | 
			
		||||
 | 
			
		||||
        self.setWindowTitle("QVNCWidget")
 | 
			
		||||
 | 
			
		||||
        self.vnc = QVNCWidget(
 | 
			
		||||
            parent=self,
 | 
			
		||||
            host="127.0.0.1", port=5905,
 | 
			
		||||
            readOnly=False
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.setCentralWidget(self.vnc)
 | 
			
		||||
 | 
			
		||||
        # you can disable mouse tracking if desired
 | 
			
		||||
        self.vnc.setMouseTracking(True)
 | 
			
		||||
        self.setAutoFillBackground(True)
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        self.vnc.start()
 | 
			
		||||
 | 
			
		||||
    def keyPressEvent(self, ev):
 | 
			
		||||
        self.vnc.keyPressEvent(ev)
 | 
			
		||||
        return super().keyPressEvent(ev) # in case you need the signal somewhere else in the window
 | 
			
		||||
 | 
			
		||||
    def keyReleaseEvent(self, ev):
 | 
			
		||||
        self.vnc.keyReleaseEvent(ev)
 | 
			
		||||
        return super().keyReleaseEvent(ev) # in case you need the signal somewhere else in the window
 | 
			
		||||
 | 
			
		||||
    def closeEvent(self, ev):
 | 
			
		||||
        self.vnc.stop()
 | 
			
		||||
        return super().closeEvent(ev)
 | 
			
		||||
 | 
			
		||||
app = QApplication(sys.argv)
 | 
			
		||||
window = Window()
 | 
			
		||||
window.resize(800, 600)
 | 
			
		||||
window.show()
 | 
			
		||||
 | 
			
		||||
sys.exit(app.exec_())
 | 
			
		||||
							
								
								
									
										1
									
								
								novnc-client/qvncwidget/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								novnc-client/qvncwidget/__init__.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
from .qvncwidget import QVNCWidget
 | 
			
		||||
							
								
								
									
										237
									
								
								novnc-client/qvncwidget/easystruct.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								novnc-client/qvncwidget/easystruct.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,237 @@
 | 
			
		||||
#! /usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
import struct
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
# reading
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
def read_float_buff(buffer, big_endian=False) -> float:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">f", buffer.read(4))[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<f", buffer.read(4))[0]
 | 
			
		||||
 | 
			
		||||
def read_double_buff(buffer, big_endian=False) -> float:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">d", buffer.read(8))[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<d", buffer.read(8))[0]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def read_uint8_buff(buffer, big_endian=False) -> int:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">B", buffer.read(1))[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<B", buffer.read(1))[0]
 | 
			
		||||
 | 
			
		||||
def read_uint16_buff(buffer, big_endian=False) -> int:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">H", buffer.read(2))[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<H", buffer.read(2))[0]
 | 
			
		||||
 | 
			
		||||
def read_uint32_buff(buffer, big_endian=False) -> int:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">I", buffer.read(4))[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<I", buffer.read(4))[0]
 | 
			
		||||
 | 
			
		||||
def read_uint64_buff(buffer, big_endian=False) -> int:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">Q", buffer.read(8))[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<Q", buffer.read(8))[0]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def read_sint8_buff(buffer, big_endian=False) -> int:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">b", buffer.read(1))[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<b", buffer.read(1))[0]
 | 
			
		||||
 | 
			
		||||
def read_sint16_buff(buffer, big_endian=False) -> int:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">h", buffer.read(2))[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<h", buffer.read(2))[0]
 | 
			
		||||
 | 
			
		||||
def read_sint32_buff(buffer, big_endian=False) -> int:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">i", buffer.read(4))[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<i", buffer.read(4))[0]
 | 
			
		||||
 | 
			
		||||
def read_sint64_buff(buffer, big_endian=False) -> int:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">q", buffer.read(8))[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<q", buffer.read(8))[0]
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
# writing
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
def write_float_buff(buffer, value: float, big_endian=False) -> None:
 | 
			
		||||
    buffer.write(return_float_bytes(value, big_endian))
 | 
			
		||||
 | 
			
		||||
def write_double_buff(buffer, value: float, big_endian=False) -> None:
 | 
			
		||||
    buffer.write(return_double_bytes(value, big_endian))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def write_uint8_buff(buffer, value: int, big_endian=False) -> None:
 | 
			
		||||
    buffer.write(return_uint8_bytes(value, big_endian))
 | 
			
		||||
 | 
			
		||||
def write_uint16_buff(buffer, value: int, big_endian=False) -> None:
 | 
			
		||||
    buffer.write(return_uint16_bytes(value, big_endian))
 | 
			
		||||
 | 
			
		||||
def write_uint32_buff(buffer, value: int, big_endian=False) -> None:
 | 
			
		||||
    buffer.write(return_uint32_bytes(value, big_endian))
 | 
			
		||||
 | 
			
		||||
def write_uint64_buff(buffer, value: int, big_endian=False) -> None:
 | 
			
		||||
    buffer.write(return_uint64_bytes(value, big_endian))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def write_sint8_buff(buffer, value: int, big_endian=False) -> None:
 | 
			
		||||
    buffer.write(return_sint8_bytes(value, big_endian))
 | 
			
		||||
 | 
			
		||||
def write_sint16_buff(buffer, value: int, big_endian=False) -> None:
 | 
			
		||||
    buffer.write(return_sint16_bytes(value, big_endian))
 | 
			
		||||
 | 
			
		||||
def write_sint32_buff(buffer, value: int, big_endian=False) -> None:
 | 
			
		||||
    buffer.write(return_sint32_bytes(value, big_endian))
 | 
			
		||||
 | 
			
		||||
def write_sint64_buff(buffer, value: int, big_endian=False) -> None:
 | 
			
		||||
    buffer.write(return_sint64_bytes(value, big_endian))
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
# return bytes
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
def return_float_bytes(value: float, big_endian=False) -> bytes:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.pack(">f", value)
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.pack("<f", value)
 | 
			
		||||
 | 
			
		||||
def return_double_bytes(value: float, big_endian=False) -> bytes:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.pack(">d", value)
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.pack("<d", value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def return_uint8_bytes(value: int, big_endian=False) -> bytes:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.pack(">B", value)
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.pack("<B", value)
 | 
			
		||||
 | 
			
		||||
def return_uint16_bytes(value: int, big_endian=False) -> bytes:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.pack(">H", value)
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.pack("<H", value)
 | 
			
		||||
 | 
			
		||||
def return_uint32_bytes(value: int, big_endian=False) -> bytes:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.pack(">I", value)
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.pack("<I", value)
 | 
			
		||||
 | 
			
		||||
def return_uint64_bytes(value: int, big_endian=False) -> bytes:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.pack(">Q", value)
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.pack("<Q", value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def return_sint8_bytes(value: int, big_endian=False) -> bytes:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.pack(">b", value)
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.pack("<b", value)
 | 
			
		||||
 | 
			
		||||
def return_sint16_bytes(value: int, big_endian=False) -> bytes:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.pack(">h", value)
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.pack("<h", value)
 | 
			
		||||
 | 
			
		||||
def return_sint32_bytes(value: int, big_endian=False) -> bytes:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.pack(">i", value)
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.pack("<i", value)
 | 
			
		||||
 | 
			
		||||
def return_sint64_bytes(value: int, big_endian=False) -> bytes:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.pack(">q", value)
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.pack("<q", value)
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
# return val
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
def return_float_val(data: bytes, big_endian=False) -> float:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">f", data)[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<f", data)[0]
 | 
			
		||||
 | 
			
		||||
def return_double_val(data: bytes, big_endian=False) -> float:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">d", data)[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<d", data)[0]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def return_uint8_val(data: bytes, big_endian=False) -> int:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">B", data)[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<B", data)[0]
 | 
			
		||||
 | 
			
		||||
def return_uint16_val(data: bytes, big_endian=False) -> int:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">H", data)[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<H", data)[0]
 | 
			
		||||
 | 
			
		||||
def return_uint32_val(data: bytes, big_endian=False) -> int:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">I", data)[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<I", data)[0]
 | 
			
		||||
 | 
			
		||||
def return_uint64_val(data: bytes, big_endian=False) -> int:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">Q", data)[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<Q", data)[0]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def return_sint8_val(data: bytes, big_endian=False) -> int:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">b", data)[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<b", data)[0]
 | 
			
		||||
 | 
			
		||||
def return_sint16_val(data: bytes, big_endian=False) -> int:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">h", data)[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<h", data)[0]
 | 
			
		||||
 | 
			
		||||
def return_sint32_val(data: bytes, big_endian=False) -> int:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">i", data)[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<i", data)[0]
 | 
			
		||||
 | 
			
		||||
def return_sint64_val(data: bytes, big_endian=False) -> int:
 | 
			
		||||
    if big_endian:
 | 
			
		||||
        return struct.unpack(">q", data)[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return struct.unpack("<q", data)[0]
 | 
			
		||||
							
								
								
									
										690
									
								
								novnc-client/qvncwidget/qvncwidget.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										690
									
								
								novnc-client/qvncwidget/qvncwidget.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,690 @@
 | 
			
		||||
"""
 | 
			
		||||
Qt Widget for displaying VNC framebuffer using RFB protocol
 | 
			
		||||
 | 
			
		||||
(c) zocker-160 2024
 | 
			
		||||
licensed under GPLv3
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
from PyQt5.QtCore import (
 | 
			
		||||
    QSize,
 | 
			
		||||
    Qt,
 | 
			
		||||
    pyqtSignal,
 | 
			
		||||
    QSemaphore
 | 
			
		||||
)
 | 
			
		||||
from PyQt5.QtGui import (
 | 
			
		||||
    QImage,
 | 
			
		||||
    QPaintEvent,
 | 
			
		||||
    QPainter,
 | 
			
		||||
    QColor,
 | 
			
		||||
    QBrush,
 | 
			
		||||
    QPixmap,
 | 
			
		||||
    QResizeEvent,
 | 
			
		||||
    QKeyEvent,
 | 
			
		||||
    QMouseEvent
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
from PyQt5.QtWidgets import (
 | 
			
		||||
    QWidget,
 | 
			
		||||
    QLabel,
 | 
			
		||||
    QWidget,
 | 
			
		||||
    QOpenGLWidget
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
from qvncwidget.rfb import RFBClient
 | 
			
		||||
from qvncwidget.rfbhelpers import RFBPixelformat, RFBInput
 | 
			
		||||
 | 
			
		||||
log = logging.getLogger("QVNCWidget")
 | 
			
		||||
 | 
			
		||||
class QVNCWidget(QWidget, RFBClient):
 | 
			
		||||
 | 
			
		||||
    onInitialResize = pyqtSignal(QSize)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent: QWidget,
 | 
			
		||||
                 host: str, port = 5900, password: str = None,
 | 
			
		||||
                 readOnly = False):
 | 
			
		||||
        super().__init__(
 | 
			
		||||
            parent=parent,
 | 
			
		||||
            host=host, port=port, password=password
 | 
			
		||||
        )
 | 
			
		||||
        self.readOnly = readOnly
 | 
			
		||||
 | 
			
		||||
        self.backbuffer: QImage = None
 | 
			
		||||
        self.frontbuffer: QImage = None
 | 
			
		||||
 | 
			
		||||
        self.setMouseTracking(not self.readOnly)
 | 
			
		||||
        self.setMinimumSize(1, 1) # make window scalable
 | 
			
		||||
 | 
			
		||||
        self.mouseButtonMask = 0
 | 
			
		||||
 | 
			
		||||
    def start(self):
 | 
			
		||||
        self.startConnection()
 | 
			
		||||
 | 
			
		||||
    def stop(self):
 | 
			
		||||
        self.closeConnection()
 | 
			
		||||
 | 
			
		||||
    def onConnectionMade(self):
 | 
			
		||||
        log.info("VNC handshake done")
 | 
			
		||||
 | 
			
		||||
        self.setPixelFormat(RFBPixelformat.getRGB32())
 | 
			
		||||
 | 
			
		||||
        self.PIX_FORMAT = QImage.Format.Format_RGB32
 | 
			
		||||
        self.backbuffer = QImage(self.vncWidth, self.vncHeight, self.PIX_FORMAT)
 | 
			
		||||
        self.onInitialResize.emit(QSize(self.vncWidth, self.vncHeight))
 | 
			
		||||
 | 
			
		||||
    def onRectangleUpdate(self,
 | 
			
		||||
            x: int, y: int, width: int, height: int, data: bytes):
 | 
			
		||||
 | 
			
		||||
        if self.backbuffer is None:
 | 
			
		||||
            log.warning("backbuffer is None")
 | 
			
		||||
            return
 | 
			
		||||
        else:
 | 
			
		||||
            log.debug("drawing backbuffer")
 | 
			
		||||
 | 
			
		||||
        #with open(f"{width}x{height}.data", "wb") as f:
 | 
			
		||||
        #    f.write(data)
 | 
			
		||||
 | 
			
		||||
        t1 = time.time()
 | 
			
		||||
 | 
			
		||||
        painter = QPainter(self.backbuffer)
 | 
			
		||||
        painter.drawImage(x, y, QImage(data, width, height, self.PIX_FORMAT))
 | 
			
		||||
        painter.end()
 | 
			
		||||
 | 
			
		||||
        log.debug(f"painting took: {(time.time() - t1)*1e3} ms")
 | 
			
		||||
 | 
			
		||||
        del painter
 | 
			
		||||
        del data
 | 
			
		||||
 | 
			
		||||
    def onFramebufferUpdateFinished(self):
 | 
			
		||||
        log.debug("FB Update finished")
 | 
			
		||||
        self.update()
 | 
			
		||||
 | 
			
		||||
    def paintEvent(self, a0: QPaintEvent):
 | 
			
		||||
        #log.debug("Paint event")
 | 
			
		||||
        painter = QPainter(self)
 | 
			
		||||
 | 
			
		||||
        if self.backbuffer is None:
 | 
			
		||||
            log.debug("backbuffer is None")
 | 
			
		||||
            painter.fillRect(0, 0, self.width(), self.height(), Qt.GlobalColor.black)
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            self.frontbuffer = self.backbuffer.scaled(
 | 
			
		||||
                    self.width(), self.height(),
 | 
			
		||||
                    Qt.AspectRatioMode.KeepAspectRatio,
 | 
			
		||||
                    Qt.TransformationMode.SmoothTransformation
 | 
			
		||||
                )
 | 
			
		||||
            painter.drawImage(0, 0, self.frontbuffer)
 | 
			
		||||
 | 
			
		||||
        painter.end()
 | 
			
		||||
 | 
			
		||||
    # Mouse events
 | 
			
		||||
 | 
			
		||||
    def mousePressEvent(self, ev: QMouseEvent):
 | 
			
		||||
        if self.readOnly or not self.frontbuffer: return
 | 
			
		||||
        self.mouseButtonMask = RFBInput.fromQMouseEvent(ev, True, self.mouseButtonMask)
 | 
			
		||||
        self.pointerEvent(*self._getRemoteRel(ev), self.mouseButtonMask)
 | 
			
		||||
 | 
			
		||||
    def mouseReleaseEvent(self, ev: QMouseEvent):
 | 
			
		||||
        if self.readOnly or not self.frontbuffer: return
 | 
			
		||||
        self.mouseButtonMask = RFBInput.fromQMouseEvent(ev, False, self.mouseButtonMask)
 | 
			
		||||
        self.pointerEvent(*self._getRemoteRel(ev), self.mouseButtonMask)
 | 
			
		||||
 | 
			
		||||
    def mouseMoveEvent(self, ev: QMouseEvent):
 | 
			
		||||
        if self.readOnly or not self.frontbuffer: return
 | 
			
		||||
        try:
 | 
			
		||||
            # 忽略拖动导致的问题
 | 
			
		||||
            self.pointerEvent(*self._getRemoteRel(ev), self.mouseButtonMask)
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    def _getRemoteRel(self, ev: QMouseEvent) -> tuple:
 | 
			
		||||
        xPos = (ev.localPos().x() / self.frontbuffer.width()) * self.vncWidth
 | 
			
		||||
        yPos = (ev.localPos().y() / self.frontbuffer.height()) * self.vncHeight
 | 
			
		||||
 | 
			
		||||
        return int(xPos), int(yPos)
 | 
			
		||||
 | 
			
		||||
    # Key events
 | 
			
		||||
 | 
			
		||||
    def keyPressEvent(self, ev: QKeyEvent):
 | 
			
		||||
        if self.readOnly: return
 | 
			
		||||
        self.keyEvent(RFBInput.fromQKeyEvent(ev.key(), ev.text()), down=1)
 | 
			
		||||
 | 
			
		||||
    def keyReleaseEvent(self, ev: QKeyEvent):
 | 
			
		||||
        if self.readOnly: return
 | 
			
		||||
        self.keyEvent(RFBInput.fromQKeyEvent(ev.key(), ev.text()), down=0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# other experimental implementations
 | 
			
		||||
 | 
			
		||||
class QVNCWidgetGL(QOpenGLWidget, RFBClient):
 | 
			
		||||
 | 
			
		||||
    IMG_FORMAT = QImage.Format_RGB32
 | 
			
		||||
 | 
			
		||||
    onInitialResize = pyqtSignal(QSize)
 | 
			
		||||
    #onUpdatePixmap = pyqtSignal(int, int, int, int, bytes)
 | 
			
		||||
    onUpdatePixmap = pyqtSignal()
 | 
			
		||||
    onSetPixmap = pyqtSignal()
 | 
			
		||||
 | 
			
		||||
    onKeyPress = pyqtSignal(QKeyEvent)
 | 
			
		||||
    onKeyRelease = pyqtSignal(QKeyEvent)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent, 
 | 
			
		||||
            host, port=5900, password: str=None,
 | 
			
		||||
            mouseTracking=False):
 | 
			
		||||
 | 
			
		||||
        #super(QOpenGLWidget, self).__init__()
 | 
			
		||||
        #super(RFBClient, self).__init__(
 | 
			
		||||
        super().__init__(
 | 
			
		||||
            parent=parent,
 | 
			
		||||
            host=host,
 | 
			
		||||
            port=port,
 | 
			
		||||
            password=password,
 | 
			
		||||
            daemonThread=True
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        #self.setAlignment(Qt.AlignCenter)
 | 
			
		||||
 | 
			
		||||
        #self.onUpdatePixmap.connect(self._updateImage)
 | 
			
		||||
        #self.onSetPixmap.connect(self._setImage)
 | 
			
		||||
        self.onSetPixmap.connect(self._updateImage)
 | 
			
		||||
 | 
			
		||||
        self.acceptMouseEvents = False # mouse events are not accepted at first
 | 
			
		||||
        self.setMouseTracking(mouseTracking)
 | 
			
		||||
        
 | 
			
		||||
        # Allow Resizing
 | 
			
		||||
        self.setMinimumSize(1, 1)
 | 
			
		||||
 | 
			
		||||
        self.data = list(tuple())
 | 
			
		||||
        self.dataMonitor = QSemaphore(0)
 | 
			
		||||
 | 
			
		||||
    def start(self):
 | 
			
		||||
        self.startConnection()
 | 
			
		||||
 | 
			
		||||
    def stop(self):
 | 
			
		||||
        self.closeConnection()
 | 
			
		||||
 | 
			
		||||
    def onConnectionMade(self):
 | 
			
		||||
        log.info("VNC handshake done")
 | 
			
		||||
 | 
			
		||||
        self.setPixelFormat(RFBPixelformat.getRGB32())
 | 
			
		||||
        self.onInitialResize.emit(QSize(self.vncWidth, self.vncHeight))
 | 
			
		||||
        self._initKeypress()
 | 
			
		||||
        self._initMouse()
 | 
			
		||||
 | 
			
		||||
    def onRectangleUpdate(self,
 | 
			
		||||
            x: int, y: int, width: int, height: int, data: bytes):
 | 
			
		||||
        #img = QImage(data, width, height, self.IMG_FORMAT)
 | 
			
		||||
        #self.onUpdatePixmap.emit(x, y, width, height, data)
 | 
			
		||||
 | 
			
		||||
        #self.dataMonitor.acquire(1)
 | 
			
		||||
 | 
			
		||||
        self.data.append((x, y, width, height, data))
 | 
			
		||||
        #self.data = (x, y, width, height, data)
 | 
			
		||||
        #self.dataMonitor.release(1)
 | 
			
		||||
 | 
			
		||||
        #self.onUpdatePixmap.emit()
 | 
			
		||||
        
 | 
			
		||||
        #else:
 | 
			
		||||
        #    print("AAAAAAAAAAAAAA", "MONITOR AQUIRE FAILED")
 | 
			
		||||
 | 
			
		||||
    def onFramebufferUpdateFinished(self):
 | 
			
		||||
        self.onSetPixmap.emit()
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
        if self.pixmap:
 | 
			
		||||
            #self.setPixmap(QPixmap.fromImage(self.image))
 | 
			
		||||
            self.resizeEvent(None)
 | 
			
		||||
 | 
			
		||||
    def onFatalError(self, error: Exception):
 | 
			
		||||
        log.error(str(error))
 | 
			
		||||
        #logging.exception(str(error))
 | 
			
		||||
        #self.reconnect()
 | 
			
		||||
 | 
			
		||||
    #def _updateImage(self, x: int, y: int, width: int, height: int, data: bytes):
 | 
			
		||||
    def _updateImage(self):
 | 
			
		||||
        print("update image")
 | 
			
		||||
        self.update()
 | 
			
		||||
 | 
			
		||||
        #if not self.screen:
 | 
			
		||||
        #    self.screen = QImage(width, height, self.IMG_FORMAT)
 | 
			
		||||
        #    self.screen.fill(Qt.red)
 | 
			
		||||
        #    self.screenPainter = QPainter(self.screen)
 | 
			
		||||
 | 
			
		||||
        #self.painter.beginNativePainting()
 | 
			
		||||
        #self.painter.drawPixmapFragments()
 | 
			
		||||
 | 
			
		||||
        #with open("/tmp/images/test.raw", "wb") as f:
 | 
			
		||||
        #    f.write(data)
 | 
			
		||||
        
 | 
			
		||||
        #p = QPainter(self.screen)
 | 
			
		||||
 | 
			
		||||
        #self.screenPainter.drawImage(
 | 
			
		||||
        #    x, y, QImage(data, width, height, self.IMG_FORMAT))
 | 
			
		||||
 | 
			
		||||
        #p.end()
 | 
			
		||||
 | 
			
		||||
        #self.repaint()
 | 
			
		||||
        #self.update()
 | 
			
		||||
 | 
			
		||||
    def _setPixmap(self):
 | 
			
		||||
        if self.pixmap:
 | 
			
		||||
            self.setPixmap(
 | 
			
		||||
                self.pixmap.scaled(
 | 
			
		||||
                    self.width(), self.height(),
 | 
			
		||||
                    Qt.KeepAspectRatio,
 | 
			
		||||
                    Qt.SmoothTransformation
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def _setImage(self):
 | 
			
		||||
        if self.screen:
 | 
			
		||||
            self.setPixmap(QPixmap.fromImage(
 | 
			
		||||
                self.screen.scaled(
 | 
			
		||||
                    self.width(), self.height(),
 | 
			
		||||
                    Qt.KeepAspectRatio,
 | 
			
		||||
                    Qt.SmoothTransformation
 | 
			
		||||
                )
 | 
			
		||||
            ))
 | 
			
		||||
        self.acceptMouseEvents = True  # mouse events are getting accepted
 | 
			
		||||
 | 
			
		||||
    # Passed events
 | 
			
		||||
 | 
			
		||||
    def _keyPress(self, ev: QKeyEvent):
 | 
			
		||||
        self.keyEvent(
 | 
			
		||||
            RFBInput.fromQKeyEvent(ev.key(), ev.text()), down=1)
 | 
			
		||||
 | 
			
		||||
    def _keyRelease(self, ev: QKeyEvent):
 | 
			
		||||
        self.keyEvent(
 | 
			
		||||
            RFBInput.fromQKeyEvent(ev.key(), ev.text()), down=0)
 | 
			
		||||
 | 
			
		||||
    # Window events
 | 
			
		||||
 | 
			
		||||
    def paintEvent(self, e: QPaintEvent):
 | 
			
		||||
        print("paint event")
 | 
			
		||||
 | 
			
		||||
        #self.dataMonitor.acquire(1)
 | 
			
		||||
 | 
			
		||||
        #while self.dataMonitor.tryAcquire(1):
 | 
			
		||||
        while len(self.data) > 0:
 | 
			
		||||
            x, y, w, h, data = self.data.pop(0)
 | 
			
		||||
 | 
			
		||||
            p = QPainter(self)
 | 
			
		||||
 | 
			
		||||
        #p.setPen(QColor(255, 0, 0))
 | 
			
		||||
        #p.drawText(e.rect(), Qt.AlignCenter, str(self.dataMonitor.available()))
 | 
			
		||||
        
 | 
			
		||||
            p.drawImage(x, y, QImage(data, w, h, self.IMG_FORMAT))
 | 
			
		||||
            p.end()
 | 
			
		||||
 | 
			
		||||
        #self.dataMonitor.release(1)
 | 
			
		||||
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
        p = QPainter(self)
 | 
			
		||||
        p.fillRect(e.rect(), QBrush(QColor(255, 255, 255)))
 | 
			
		||||
        p.end()
 | 
			
		||||
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
        if self.dataMonitor.tryAcquire(1):
 | 
			
		||||
            x, y, w, h, data = self.data
 | 
			
		||||
 | 
			
		||||
            p = QPainter(self)
 | 
			
		||||
            p.drawImage(x, y, QImage(data, w, h, self.IMG_FORMAT))
 | 
			
		||||
            p.end()
 | 
			
		||||
 | 
			
		||||
            self.dataMonitor.release(1)
 | 
			
		||||
 | 
			
		||||
            print("CCCCC", "Image painted diggah")
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            print("BBBBBBBBB", "aquire monitor failed")
 | 
			
		||||
 | 
			
		||||
        #return super().paintEvent(a0)
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
        if not self.screen:
 | 
			
		||||
            self.screen = QImage(self.size(), self.IMG_FORMAT)
 | 
			
		||||
            self.screen.fill(Qt.red)
 | 
			
		||||
            self.screenPainter = QPainter(self.screen)
 | 
			
		||||
 | 
			
		||||
        p = QPainter()
 | 
			
		||||
        p.begin(self)
 | 
			
		||||
        p.drawImage(0, 0,
 | 
			
		||||
            self.screen.scaled(
 | 
			
		||||
                self.width(), self.height(),
 | 
			
		||||
                Qt.KeepAspectRatio,
 | 
			
		||||
                Qt.SmoothTransformation
 | 
			
		||||
            ))
 | 
			
		||||
        p.end()
 | 
			
		||||
 | 
			
		||||
    def resizeEvent(self, e: QResizeEvent):
 | 
			
		||||
        return super().resizeEvent(e)
 | 
			
		||||
 | 
			
		||||
    def resizeGL(self, w: int, h: int):
 | 
			
		||||
        print("RESIZE THAT BITCH!!!", w, h)
 | 
			
		||||
        #return super().resizeGL(w, h)
 | 
			
		||||
 | 
			
		||||
    def resizeEvent_(self, a0: QResizeEvent):
 | 
			
		||||
        #print("RESIZE!", self.width(), self.height())
 | 
			
		||||
        #return super().resizeEvent(a0)
 | 
			
		||||
        if self.screen:
 | 
			
		||||
            self.setPixmap(QPixmap.fromImage(
 | 
			
		||||
                self.screen.scaled(
 | 
			
		||||
                    self.width(), self.height(),
 | 
			
		||||
                    Qt.KeepAspectRatio,
 | 
			
		||||
                    Qt.SmoothTransformation
 | 
			
		||||
                ))
 | 
			
		||||
            )
 | 
			
		||||
        return super().resizeEvent(a0)
 | 
			
		||||
 | 
			
		||||
    def mousePressEvent(self, ev: QMouseEvent):
 | 
			
		||||
        #print(ev.localPos(), ev.button())
 | 
			
		||||
        #print(self.height() - self.pixmap().height())
 | 
			
		||||
 | 
			
		||||
        if self.acceptMouseEvents: # need pixmap instance
 | 
			
		||||
            self.buttonMask = RFBInput.fromQMouseEvent(ev, True, self.buttonMask)
 | 
			
		||||
            self.pointerEvent(*self._getRemoteRel(ev), self.buttonMask)
 | 
			
		||||
 | 
			
		||||
        return super().mousePressEvent(ev)
 | 
			
		||||
 | 
			
		||||
    def mouseReleaseEvent(self, ev: QMouseEvent):
 | 
			
		||||
        if self.acceptMouseEvents: # need pixmap instance
 | 
			
		||||
            self.buttonMask = RFBInput.fromQMouseEvent(ev, False, self.buttonMask)
 | 
			
		||||
            self.pointerEvent(*self._getRemoteRel(ev), self.buttonMask)
 | 
			
		||||
 | 
			
		||||
        return super().mouseReleaseEvent(ev)
 | 
			
		||||
 | 
			
		||||
    def mouseMoveEvent(self, ev: QMouseEvent):
 | 
			
		||||
        if self.acceptMouseEvents: # need pixmap instance
 | 
			
		||||
            self.pointerEvent(*self._getRemoteRel(ev), self.buttonMask)
 | 
			
		||||
 | 
			
		||||
    # FIXME: The pixmap is assumed to be aligned center.
 | 
			
		||||
    def _getRemoteRel(self, ev: QMouseEvent) -> tuple:
 | 
			
		||||
        # FIXME: this code is ugly as fk
 | 
			
		||||
 | 
			
		||||
        # y coord is kinda fucked up
 | 
			
		||||
        yDiff = (self.height() - self.pixmap().height()) / 2
 | 
			
		||||
        yPos = ev.localPos().y() - yDiff
 | 
			
		||||
        if yPos < 0: yPos = 0
 | 
			
		||||
        if yPos > self.pixmap().height(): yPos = self.pixmap().height()
 | 
			
		||||
 | 
			
		||||
        yPos = self._calcRemoteRel(
 | 
			
		||||
            yPos, self.pixmap().height(), self.vncHeight)
 | 
			
		||||
 | 
			
		||||
        # x coord is kinda fucked up, too
 | 
			
		||||
        xDiff = (self.width() - self.pixmap().width()) / 2
 | 
			
		||||
        xPos = ev.localPos().x() - xDiff
 | 
			
		||||
        if xPos < 0: xPos = 0
 | 
			
		||||
        if xPos > self.pixmap().width(): xPos = self.pixmap().width()
 | 
			
		||||
 | 
			
		||||
        xPos = self._calcRemoteRel(
 | 
			
		||||
            xPos, self.pixmap().width(), self.vncWidth)
 | 
			
		||||
        
 | 
			
		||||
        return xPos, yPos
 | 
			
		||||
 | 
			
		||||
    def _calcRemoteRel(self, locRel, locMax, remoteMax) -> int:
 | 
			
		||||
        return int( (locRel / locMax) * remoteMax )
 | 
			
		||||
 | 
			
		||||
    def _initMouse(self):
 | 
			
		||||
        self.buttonMask = 0 # pressed buttons (bit fields)
 | 
			
		||||
 | 
			
		||||
    def _initKeypress(self):
 | 
			
		||||
        self.onKeyPress.connect(self._keyPress)
 | 
			
		||||
        self.onKeyRelease.connect(self._keyRelease)
 | 
			
		||||
 | 
			
		||||
    def __del__(self):
 | 
			
		||||
        self.stop()
 | 
			
		||||
    
 | 
			
		||||
    def __exit__(self, *args):
 | 
			
		||||
        self.stop()
 | 
			
		||||
        self.deleteLater()
 | 
			
		||||
 | 
			
		||||
class QVNCWidget_old(QLabel, RFBClient):
 | 
			
		||||
    
 | 
			
		||||
    IMG_FORMAT = QImage.Format_RGB32
 | 
			
		||||
 | 
			
		||||
    onInitialResize = pyqtSignal(QSize)
 | 
			
		||||
    onUpdatePixmap = pyqtSignal(int, int, int, int, bytes)
 | 
			
		||||
    onSetPixmap = pyqtSignal()
 | 
			
		||||
 | 
			
		||||
    onKeyPress = pyqtSignal(QKeyEvent)
 | 
			
		||||
    onKeyRelease = pyqtSignal(QKeyEvent)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent, 
 | 
			
		||||
                host, port=5900, password: str=None,
 | 
			
		||||
                mouseTracking=False):
 | 
			
		||||
        super().__init__(
 | 
			
		||||
            parent=parent,
 | 
			
		||||
            host=host,
 | 
			
		||||
            port=port,
 | 
			
		||||
            password=password,
 | 
			
		||||
            daemonThread=True
 | 
			
		||||
        )
 | 
			
		||||
        #import faulthandler
 | 
			
		||||
        #faulthandler.enable()
 | 
			
		||||
        self.screen: QImage = None
 | 
			
		||||
 | 
			
		||||
        # FIXME: The pixmap is assumed to be aligned center.
 | 
			
		||||
        self.setAlignment(Qt.AlignCenter)
 | 
			
		||||
 | 
			
		||||
        self.onUpdatePixmap.connect(self._updateImage)
 | 
			
		||||
        self.onSetPixmap.connect(self._setImage)
 | 
			
		||||
 | 
			
		||||
        self.acceptMouseEvents = False # mouse events are not accepted at first
 | 
			
		||||
        self.setMouseTracking(mouseTracking)
 | 
			
		||||
        
 | 
			
		||||
        # Allow Resizing
 | 
			
		||||
        self.setMinimumSize(1,1)
 | 
			
		||||
 | 
			
		||||
    def _initMouse(self):
 | 
			
		||||
        self.buttonMask = 0 # pressed buttons (bit fields)
 | 
			
		||||
 | 
			
		||||
    def _initKeypress(self):
 | 
			
		||||
        self.onKeyPress.connect(self._keyPress)
 | 
			
		||||
        self.onKeyRelease.connect(self._keyRelease)
 | 
			
		||||
 | 
			
		||||
    def start(self):
 | 
			
		||||
        self.startConnection()
 | 
			
		||||
 | 
			
		||||
    def stop(self):
 | 
			
		||||
        self.closeConnection()
 | 
			
		||||
        if self.screenPainter: self.screenPainter.end()
 | 
			
		||||
 | 
			
		||||
    def onConnectionMade(self):
 | 
			
		||||
        self.onInitialResize.emit(QSize(self.vncWidth, self.vncHeight))
 | 
			
		||||
        self.setPixelFormat(RFBPixelformat.getRGB32())
 | 
			
		||||
        self._initKeypress()
 | 
			
		||||
        self._initMouse()
 | 
			
		||||
 | 
			
		||||
    def onRectangleUpdate(self,
 | 
			
		||||
            x: int, y: int, width: int, height: int, data: bytes):
 | 
			
		||||
        #img = QImage(data, width, height, self.IMG_FORMAT)
 | 
			
		||||
        self.onUpdatePixmap.emit(x, y, width, height, data)
 | 
			
		||||
 | 
			
		||||
    def onFramebufferUpdateFinished(self):
 | 
			
		||||
        self.onSetPixmap.emit()
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
        if self.pixmap:
 | 
			
		||||
            #self.setPixmap(QPixmap.fromImage(self.image))
 | 
			
		||||
            self.resizeEvent(None)
 | 
			
		||||
 | 
			
		||||
    def onFatalError(self, error: Exception):
 | 
			
		||||
        log.error(str(error))
 | 
			
		||||
        #logging.exception(str(error))
 | 
			
		||||
        #self.reconnect()
 | 
			
		||||
 | 
			
		||||
    def _updateImage(self, x: int, y: int, width: int, height: int, data: bytes):
 | 
			
		||||
        if not self.screen:
 | 
			
		||||
            self.screen = QImage(width, height, self.IMG_FORMAT)
 | 
			
		||||
            self.screen.fill(Qt.red)
 | 
			
		||||
            self.screenPainter = QPainter(self.screen)
 | 
			
		||||
 | 
			
		||||
        #self.painter.beginNativePainting()
 | 
			
		||||
        #self.painter.drawPixmapFragments()
 | 
			
		||||
 | 
			
		||||
        #with open("/tmp/images/test.raw", "wb") as f:
 | 
			
		||||
        #    f.write(data)
 | 
			
		||||
        
 | 
			
		||||
        #p = QPainter(self.screen)
 | 
			
		||||
        self.screenPainter.drawImage(
 | 
			
		||||
            x, y, QImage(data, width, height, self.IMG_FORMAT))
 | 
			
		||||
        #p.end()
 | 
			
		||||
 | 
			
		||||
        #self.repaint()
 | 
			
		||||
        #self.update()
 | 
			
		||||
 | 
			
		||||
    def _drawPixmap(self, x: int, y: int, pix: QPixmap):
 | 
			
		||||
        #self.paintLock.acquire()
 | 
			
		||||
        self.pixmap = pix
 | 
			
		||||
 | 
			
		||||
        if not self.painter:
 | 
			
		||||
            self.painter = QPainter(self.pixmap)
 | 
			
		||||
        else:
 | 
			
		||||
            print("DRAW PIXMAP:", x, y, self.pixmap, self.painter, pix, pix.isNull())
 | 
			
		||||
            self.painter.drawPixmap(x, y, self.pixmap)
 | 
			
		||||
        #self.paintLock.release()
 | 
			
		||||
 | 
			
		||||
    def _drawPixmap2(self, x: int, y: int, pix: QPixmap, data: bytes):
 | 
			
		||||
        if not self.pixmap or (
 | 
			
		||||
            x == 0 and y == 0 and
 | 
			
		||||
            pix.width() == self.pixmap.width() and pix.height() == self.pixmap.height()):
 | 
			
		||||
 | 
			
		||||
            self.pixmap = pix.copy()
 | 
			
		||||
            self._setPixmap()
 | 
			
		||||
            return
 | 
			
		||||
        
 | 
			
		||||
        import time
 | 
			
		||||
        print("DRAW PIXMAP:", x, y, self.pixmap.width(), self.pixmap.height(), pix.width(), pix.height())
 | 
			
		||||
        _t = time.time()
 | 
			
		||||
        #self.pixmap.save(f"/tmp/images/imgP_{_t}", "jpg")
 | 
			
		||||
        #with open(f"/tmp/images/img_{_t}.raw", "wb") as f:
 | 
			
		||||
        #    f.write(data)
 | 
			
		||||
        #pix.save(f"/tmp/images/img_{_t}", "jpg")
 | 
			
		||||
 | 
			
		||||
        painter = QPainter(self.pixmap)
 | 
			
		||||
        painter.drawPixmap(x, y, pix)
 | 
			
		||||
        painter.end()
 | 
			
		||||
        #self._setPixmap()
 | 
			
		||||
 | 
			
		||||
    def _setPixmap(self):
 | 
			
		||||
        if self.pixmap:
 | 
			
		||||
            self.setPixmap(
 | 
			
		||||
                self.pixmap.scaled(
 | 
			
		||||
                    self.width(), self.height(),
 | 
			
		||||
                    Qt.KeepAspectRatio,
 | 
			
		||||
                    Qt.SmoothTransformation
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def _setImage(self):
 | 
			
		||||
        if self.screen:
 | 
			
		||||
            self.setPixmap(QPixmap.fromImage(
 | 
			
		||||
                self.screen.scaled(
 | 
			
		||||
                    self.width(), self.height(),
 | 
			
		||||
                    Qt.KeepAspectRatio,
 | 
			
		||||
                    Qt.SmoothTransformation
 | 
			
		||||
                )
 | 
			
		||||
            ))
 | 
			
		||||
        self.acceptMouseEvents = True  # mouse events are getting accepted
 | 
			
		||||
 | 
			
		||||
    # Passed events
 | 
			
		||||
 | 
			
		||||
    def _keyPress(self, ev: QKeyEvent):
 | 
			
		||||
        self.keyEvent(
 | 
			
		||||
            RFBInput.fromQKeyEvent(ev.key(), ev.text()), down=1)
 | 
			
		||||
 | 
			
		||||
    def _keyRelease(self, ev: QKeyEvent):
 | 
			
		||||
        self.keyEvent(
 | 
			
		||||
            RFBInput.fromQKeyEvent(ev.key(), ev.text()), down=0)
 | 
			
		||||
 | 
			
		||||
    # Window events
 | 
			
		||||
 | 
			
		||||
    def paintEvent(self, a0: QPaintEvent):
 | 
			
		||||
        return super().paintEvent(a0)
 | 
			
		||||
        if not self.screen:
 | 
			
		||||
            self.screen = QImage(self.size(), self.IMG_FORMAT)
 | 
			
		||||
            self.screen.fill(Qt.red)
 | 
			
		||||
            self.screenPainter = QPainter(self.screen)
 | 
			
		||||
 | 
			
		||||
        p = QPainter()
 | 
			
		||||
        p.begin(self)
 | 
			
		||||
        p.drawImage(0, 0,
 | 
			
		||||
            self.screen.scaled(
 | 
			
		||||
                self.width(), self.height(),
 | 
			
		||||
                Qt.KeepAspectRatio,
 | 
			
		||||
                Qt.SmoothTransformation
 | 
			
		||||
            ))
 | 
			
		||||
        p.end()
 | 
			
		||||
 | 
			
		||||
    def resizeEvent(self, a0: QResizeEvent):
 | 
			
		||||
        #print("RESIZE!", self.width(), self.height())
 | 
			
		||||
        #return super().resizeEvent(a0)
 | 
			
		||||
        if self.screen:
 | 
			
		||||
            self.setPixmap(QPixmap.fromImage(
 | 
			
		||||
                self.screen.scaled(
 | 
			
		||||
                    self.width(), self.height(),
 | 
			
		||||
                    Qt.KeepAspectRatio,
 | 
			
		||||
                    Qt.SmoothTransformation
 | 
			
		||||
                ))
 | 
			
		||||
            )
 | 
			
		||||
        return super().resizeEvent(a0)
 | 
			
		||||
 | 
			
		||||
    def mousePressEvent(self, ev: QMouseEvent):
 | 
			
		||||
        #print(ev.localPos(), ev.button())
 | 
			
		||||
        #print(self.height() - self.pixmap().height())
 | 
			
		||||
 | 
			
		||||
        if self.acceptMouseEvents: # need pixmap instance
 | 
			
		||||
            self.buttonMask = RFBInput.fromQMouseEvent(ev, True, self.buttonMask)
 | 
			
		||||
            self.pointerEvent(*self._getRemoteRel(ev), self.buttonMask)
 | 
			
		||||
 | 
			
		||||
        return super().mousePressEvent(ev)
 | 
			
		||||
 | 
			
		||||
    def mouseReleaseEvent(self, ev: QMouseEvent):
 | 
			
		||||
        if self.acceptMouseEvents: # need pixmap instance
 | 
			
		||||
            self.buttonMask = RFBInput.fromQMouseEvent(ev, False, self.buttonMask)
 | 
			
		||||
            self.pointerEvent(*self._getRemoteRel(ev), self.buttonMask)
 | 
			
		||||
 | 
			
		||||
        return super().mouseReleaseEvent(ev)
 | 
			
		||||
 | 
			
		||||
    def mouseMoveEvent(self, ev: QMouseEvent):
 | 
			
		||||
        if self.acceptMouseEvents: # need pixmap instance
 | 
			
		||||
            self.pointerEvent(*self._getRemoteRel(ev), self.buttonMask)
 | 
			
		||||
 | 
			
		||||
    # FIXME: The pixmap is assumed to be aligned center.
 | 
			
		||||
    def _getRemoteRel(self, ev: QMouseEvent) -> tuple:
 | 
			
		||||
        # FIXME: this code is ugly as fk
 | 
			
		||||
 | 
			
		||||
        # y coord is kinda fucked up
 | 
			
		||||
        yDiff = (self.height() - self.pixmap().height()) / 2
 | 
			
		||||
        yPos = ev.localPos().y() - yDiff
 | 
			
		||||
        if yPos < 0: yPos = 0
 | 
			
		||||
        if yPos > self.pixmap().height(): yPos = self.pixmap().height()
 | 
			
		||||
 | 
			
		||||
        yPos = self._calcRemoteRel(
 | 
			
		||||
            yPos, self.pixmap().height(), self.vncHeight)
 | 
			
		||||
 | 
			
		||||
        # x coord is kinda fucked up, too
 | 
			
		||||
        xDiff = (self.width() - self.pixmap().width()) / 2
 | 
			
		||||
        xPos = ev.localPos().x() - xDiff
 | 
			
		||||
        if xPos < 0: xPos = 0
 | 
			
		||||
        if xPos > self.pixmap().width(): xPos = self.pixmap().width()
 | 
			
		||||
 | 
			
		||||
        xPos = self._calcRemoteRel(
 | 
			
		||||
            xPos, self.pixmap().width(), self.vncWidth)
 | 
			
		||||
        
 | 
			
		||||
        return xPos, yPos
 | 
			
		||||
 | 
			
		||||
    def _calcRemoteRel(self, locRel, locMax, remoteMax) -> int:
 | 
			
		||||
        return int( (locRel / locMax) * remoteMax )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def __del__(self):
 | 
			
		||||
        self.stop()
 | 
			
		||||
    
 | 
			
		||||
    def __exit__(self, exc_type, exc_val, exc_tb):
 | 
			
		||||
        self.stop()
 | 
			
		||||
        self.deleteLater()
 | 
			
		||||
							
								
								
									
										485
									
								
								novnc-client/qvncwidget/rfb.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										485
									
								
								novnc-client/qvncwidget/rfb.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,485 @@
 | 
			
		||||
"""
 | 
			
		||||
RFB protocol implementation, client side
 | 
			
		||||
 | 
			
		||||
(c) zocker-160 2024
 | 
			
		||||
licensed under GPLv3
 | 
			
		||||
 | 
			
		||||
References:
 | 
			
		||||
- http://www.realvnc.com/docs/rfbproto.pdf
 | 
			
		||||
- https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from qvncwidget.rfbhelpers import RFBPixelformat, RFBRectangle
 | 
			
		||||
from qvncwidget.rfbdes import RFBDes
 | 
			
		||||
import qvncwidget.rfbconstants as c
 | 
			
		||||
import qvncwidget.easystruct as es
 | 
			
		||||
 | 
			
		||||
from threading import Thread
 | 
			
		||||
import logging
 | 
			
		||||
import socket
 | 
			
		||||
from socket import SHUT_RDWR
 | 
			
		||||
import struct as s
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
import threading
 | 
			
		||||
 | 
			
		||||
class RFBUnexpectedResponse(Exception):
 | 
			
		||||
    pass
 | 
			
		||||
class RFBNoResponse(Exception):
 | 
			
		||||
    pass
 | 
			
		||||
class RFBUnknownVersion(Exception):
 | 
			
		||||
    pass
 | 
			
		||||
class RFBHandshakeFailed(Exception):
 | 
			
		||||
    pass
 | 
			
		||||
class VNCAuthentificationFailed(Exception):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
SUPPORTED_VERSIONS = [
 | 
			
		||||
    (3,3)
 | 
			
		||||
]
 | 
			
		||||
KNOWN_VERSIONS = [
 | 
			
		||||
    (3,3), (3,6), (3,7), (3,8),
 | 
			
		||||
    (4,0), (4,1),
 | 
			
		||||
    (5,0)
 | 
			
		||||
]
 | 
			
		||||
"""
 | 
			
		||||
3.3: official minimum version
 | 
			
		||||
3.6: UltraVNC
 | 
			
		||||
3.7: official
 | 
			
		||||
3.8: official
 | 
			
		||||
4.0: Intel AMT KVM
 | 
			
		||||
4.1: RealVNC 4.6
 | 
			
		||||
5.0: RealVNC 5.3
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
SUPPORTED_ENCODINGS = [
 | 
			
		||||
    c.ENC_RAW
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
MAX_BUFF_SIZE: int = 10*1024*1024 # 10MB
 | 
			
		||||
 | 
			
		||||
class RFBClient:
 | 
			
		||||
 | 
			
		||||
    log = logging.getLogger("RFB Client")
 | 
			
		||||
    logc = logging.getLogger("RFB -> Server")
 | 
			
		||||
    logs = logging.getLogger("RFB Client <-")
 | 
			
		||||
 | 
			
		||||
    pixformat: RFBPixelformat
 | 
			
		||||
    numRectangles = 0
 | 
			
		||||
    #rectanglePositions = list() # list[RFBRectangle]
 | 
			
		||||
 | 
			
		||||
    _stop = False
 | 
			
		||||
    _connected = False
 | 
			
		||||
    _requestFrameBufferUpdate = False
 | 
			
		||||
    _incrementalFrameBufferUpdate = True
 | 
			
		||||
 | 
			
		||||
    def __init__(self, host, port = 5900,
 | 
			
		||||
                password: str = None, 
 | 
			
		||||
                sharedConnection = True,
 | 
			
		||||
                keepRequesting = True,
 | 
			
		||||
                requestIncremental = True):
 | 
			
		||||
        self.host = host
 | 
			
		||||
        self.port = port
 | 
			
		||||
        self.password = password
 | 
			
		||||
        self.sharedConn = sharedConnection
 | 
			
		||||
        self._requestFrameBufferUpdate = keepRequesting
 | 
			
		||||
        self._incrementalFrameBufferUpdate = requestIncremental
 | 
			
		||||
 | 
			
		||||
        self._mainLoop: Thread = None
 | 
			
		||||
 | 
			
		||||
    def __recv(self, expectedSize: int = None, maxSize=MAX_BUFF_SIZE) -> bytes:
 | 
			
		||||
        if not expectedSize:
 | 
			
		||||
            buffer = self.connection.recv(4096)
 | 
			
		||||
        else:
 | 
			
		||||
            buffer = self.connection.recv(expectedSize, socket.MSG_WAITALL)    
 | 
			
		||||
        
 | 
			
		||||
        if len(buffer) <= 50:
 | 
			
		||||
            self.logs.debug(f"len: {len(buffer)} | {buffer}")
 | 
			
		||||
        else:
 | 
			
		||||
            self.logs.debug(f"{len(buffer)} Bytes | {len(buffer)//1024} KB")
 | 
			
		||||
 | 
			
		||||
        return buffer
 | 
			
		||||
 | 
			
		||||
    def __send(self, data: bytes):
 | 
			
		||||
        self.connection.send(data)
 | 
			
		||||
        self.logc.debug(data)
 | 
			
		||||
 | 
			
		||||
    def __start(self):
 | 
			
		||||
        self.connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 | 
			
		||||
        self.connection.connect( (self.host, self.port) )
 | 
			
		||||
        self._handleInitial()
 | 
			
		||||
 | 
			
		||||
    def __close(self):
 | 
			
		||||
        self.log.debug("Closing connection")
 | 
			
		||||
 | 
			
		||||
        if self.connection:
 | 
			
		||||
            try:
 | 
			
		||||
                self.connection.shutdown(SHUT_RDWR)
 | 
			
		||||
                self.connection.close()
 | 
			
		||||
            except OSError:
 | 
			
		||||
                self.log.debug("TCP Connection already closed")
 | 
			
		||||
 | 
			
		||||
    def _handleInitial(self):
 | 
			
		||||
        buffer = self.__recv(12)
 | 
			
		||||
 | 
			
		||||
        if b'\n' in buffer and buffer.startswith(b'RFB'):
 | 
			
		||||
            maj, min = [int(x) for x in buffer[3:-1].split(b'.')]
 | 
			
		||||
            self.log.info(f"RFB from server: {maj}.{min}")
 | 
			
		||||
 | 
			
		||||
            if (maj, min) not in KNOWN_VERSIONS:
 | 
			
		||||
                raise RFBUnknownVersion(f"Unknown RFB version by server: {maj}.{min}")
 | 
			
		||||
 | 
			
		||||
            if (maj, min) not in SUPPORTED_VERSIONS:
 | 
			
		||||
                # request highest supported version
 | 
			
		||||
 | 
			
		||||
                # TODO: requested version must not be higher than
 | 
			
		||||
                # the one offered by the server
 | 
			
		||||
                maj, min = SUPPORTED_VERSIONS[-1]
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            self.__close()
 | 
			
		||||
            raise RFBUnknownVersion(buffer)
 | 
			
		||||
 | 
			
		||||
        self.version_maj, self.version_min = maj, min
 | 
			
		||||
 | 
			
		||||
        # request supported RFB version
 | 
			
		||||
        self.__send(f"RFB 00{maj}.00{min}\n".encode())
 | 
			
		||||
        self.log.info("VNC connected")
 | 
			
		||||
 | 
			
		||||
        if (maj, min) == (3,3):
 | 
			
		||||
            self._handleAuth33(self.__recv(4))
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            self.log.error(f"Missing AUTH implementation for {maj}.{min}")
 | 
			
		||||
 | 
			
		||||
    def _handleAuth33(self, data: bytes):
 | 
			
		||||
        """
 | 
			
		||||
        Handle security handshake for protocol version 3.3.
 | 
			
		||||
        In this version, the server decides the protocol (failed, none or VNCAuth)
 | 
			
		||||
        """
 | 
			
		||||
        auth = es.return_uint32_val(data, True)
 | 
			
		||||
 | 
			
		||||
        if auth == c.AUTH_FAIL:
 | 
			
		||||
            self._handleConnFailed(self.__recv(4))
 | 
			
		||||
        elif auth == c.AUTH_NONE:
 | 
			
		||||
            self._doClientInit()
 | 
			
		||||
        elif auth == c.AUTH_VNCAUTH:
 | 
			
		||||
            self._handleVNCAuth(self.__recv(16))
 | 
			
		||||
        else:
 | 
			
		||||
            self.__close()
 | 
			
		||||
            raise RFBUnexpectedResponse(f"Unknown auth response {auth}")
 | 
			
		||||
 | 
			
		||||
    def _doClientInit(self):
 | 
			
		||||
        shared = 1 if self.sharedConn else 0
 | 
			
		||||
        self.__send(es.return_uint8_bytes(shared, True))
 | 
			
		||||
        self._handleServerInit(self.__recv(24))
 | 
			
		||||
 | 
			
		||||
    def _handleServerInit(self, data: bytes):
 | 
			
		||||
        try:
 | 
			
		||||
            self.vncWidth, self.vncHeight, pixformat, namelen = s.unpack("!HH16sI", data)
 | 
			
		||||
        except s.error as e:
 | 
			
		||||
            self.log.error("Handshake failed")
 | 
			
		||||
            self.__close()
 | 
			
		||||
            raise RFBHandshakeFailed(e)
 | 
			
		||||
 | 
			
		||||
        threading.Thread(target=a).start()
 | 
			
		||||
        self.desktopname = self.__recv(namelen).decode()
 | 
			
		||||
        self.log.debug(f"Connecting to \"{self.desktopname}\"")
 | 
			
		||||
 | 
			
		||||
        pixformatData = s.unpack("!BBBBHHHBBBxxx", pixformat)
 | 
			
		||||
        self.pixformat = RFBPixelformat(*pixformatData)
 | 
			
		||||
 | 
			
		||||
        self.log.debug(f"Server Pixelformat: {self.pixformat}")
 | 
			
		||||
        self.log.debug(f"Resolution: {self.vncWidth}x{self.vncHeight}")
 | 
			
		||||
 | 
			
		||||
        # this should not be required, but some VNC servers (like QT QPA VNC)
 | 
			
		||||
        # require this to send FramebufferUpdate
 | 
			
		||||
        self.setEncodings(SUPPORTED_ENCODINGS)
 | 
			
		||||
 | 
			
		||||
        self.onConnectionMade()
 | 
			
		||||
        self._connected = True
 | 
			
		||||
 | 
			
		||||
        # enter main request loop
 | 
			
		||||
        self._mainRequestLoop()
 | 
			
		||||
 | 
			
		||||
    def _handleVNCAuth(self, data: bytes):
 | 
			
		||||
        self._VNCAuthChallenge = data
 | 
			
		||||
 | 
			
		||||
        self.log.info("Requesting password")
 | 
			
		||||
        self.vncRequestPassword()
 | 
			
		||||
        self._handleVNCAuthResult(self.__recv(4))
 | 
			
		||||
 | 
			
		||||
    def _handleVNCAuthResult(self, data: bytes):
 | 
			
		||||
        try:
 | 
			
		||||
            result = es.return_uint32_val(data)
 | 
			
		||||
        except s.error as e:
 | 
			
		||||
            raise VNCAuthentificationFailed(f"Authentication failed ({str(e)})")
 | 
			
		||||
        self.log.debug(f"Auth result {result}")
 | 
			
		||||
 | 
			
		||||
        if result == c.SMSG_AUTH_OK:
 | 
			
		||||
            self._doClientInit()
 | 
			
		||||
        elif result == c.SMSG_AUTH_FAIL:
 | 
			
		||||
            if self.version_min > 7:
 | 
			
		||||
                self._handleVNCAuthError(self.__recv(4))
 | 
			
		||||
            else:
 | 
			
		||||
                raise VNCAuthentificationFailed("Authentication failed")
 | 
			
		||||
        elif result == c.SMSG_AUTH_TOOMANY:
 | 
			
		||||
            raise VNCAuthentificationFailed("Too many login attempts")
 | 
			
		||||
        else:
 | 
			
		||||
            self.log.error(f"Unknown Auth response ({result})")
 | 
			
		||||
 | 
			
		||||
    def _handleVNCAuthError(self, data: bytes):
 | 
			
		||||
        waitfor = es.return_uint32_val(data)
 | 
			
		||||
        raise VNCAuthentificationFailed(
 | 
			
		||||
            f"Authentication failed ({self.__recv(waitfor)})")
 | 
			
		||||
 | 
			
		||||
    def _handleConnFailed(self, data: bytes):
 | 
			
		||||
        waitfor = es.return_uint32_val(data)
 | 
			
		||||
        resp = self.__recv(waitfor)
 | 
			
		||||
 | 
			
		||||
        self.__close()
 | 
			
		||||
        raise RFBHandshakeFailed(resp)
 | 
			
		||||
 | 
			
		||||
    # ------------------------------------------------------------------
 | 
			
		||||
    ## Main request loop
 | 
			
		||||
    # ------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    def _mainRequestLoop(self):
 | 
			
		||||
        time.sleep(0.2)
 | 
			
		||||
        # first request is non incremental
 | 
			
		||||
        self.framebufferUpdateRequest(incremental=False)
 | 
			
		||||
 | 
			
		||||
        while not self._stop and self.connection:
 | 
			
		||||
            try:
 | 
			
		||||
                dType = self.__recv(1)
 | 
			
		||||
 | 
			
		||||
                # when self.connection.close() is being called
 | 
			
		||||
                # dType will be empty with length of 0
 | 
			
		||||
                if len(dType) == 0:
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
                start = time.time()
 | 
			
		||||
                self._handleConnection(dType)
 | 
			
		||||
 | 
			
		||||
                self.log.debug(f"processing update took: {(time.time() - start)*1e3} ms")
 | 
			
		||||
            except socket.timeout:
 | 
			
		||||
                self.log.debug("timeout triggered")
 | 
			
		||||
                continue
 | 
			
		||||
            except s.error as e:
 | 
			
		||||
                self.log.exception(str(e))
 | 
			
		||||
                continue
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                self.onFatalError(e)
 | 
			
		||||
 | 
			
		||||
            #print("AAA")
 | 
			
		||||
            if self._requestFrameBufferUpdate:
 | 
			
		||||
                self.framebufferUpdateRequest(
 | 
			
		||||
                    incremental=self._incrementalFrameBufferUpdate)
 | 
			
		||||
            #print("BBB")
 | 
			
		||||
 | 
			
		||||
        self.log.debug("loop exit")
 | 
			
		||||
 | 
			
		||||
    # ------------------------------------------------------------------
 | 
			
		||||
    ## Server -> Client messages
 | 
			
		||||
    # ------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    def _handleConnection(self, data: bytes):
 | 
			
		||||
        msgid = es.return_uint8_val(data)
 | 
			
		||||
 | 
			
		||||
        if msgid == c.SMSG_FBUPDATE:
 | 
			
		||||
            # Framebuffer Update
 | 
			
		||||
            self._handleFramebufferUpdate(self.__recv(3))
 | 
			
		||||
        elif msgid == c.SMSG_BELL:
 | 
			
		||||
            # bell
 | 
			
		||||
            self.onBell()
 | 
			
		||||
        elif msgid == c.SMSG_SERVERCUTTEXT:
 | 
			
		||||
            # server cut text
 | 
			
		||||
            self._handleServerCutText(self.__recv(7))
 | 
			
		||||
        elif msgid == c.SMSG_SETCOLORMAP:
 | 
			
		||||
            # set color map entries
 | 
			
		||||
            pass
 | 
			
		||||
        else:
 | 
			
		||||
            self.log.warning(f"Unknown message type recieved (id {msgid})")
 | 
			
		||||
            raise RFBUnexpectedResponse
 | 
			
		||||
 | 
			
		||||
    def _handleServerCutText(self, data: bytes):
 | 
			
		||||
        datalength = s.unpack("!xxxI", data)[0]
 | 
			
		||||
        data = self.__recv(datalength)
 | 
			
		||||
 | 
			
		||||
        self.log.debug(f"Server clipboard: {data}")
 | 
			
		||||
        # TODO: create callback
 | 
			
		||||
 | 
			
		||||
    def _handleFramebufferUpdate(self, data: bytes):
 | 
			
		||||
        numRectangles = s.unpack("!xH", data)[0]
 | 
			
		||||
        self.log.debug(f"numRectangles: {numRectangles}")
 | 
			
		||||
 | 
			
		||||
        self.onBeginUpdate()
 | 
			
		||||
 | 
			
		||||
        for _ in range(numRectangles):
 | 
			
		||||
            self._handleRectangle(self.__recv(12))
 | 
			
		||||
 | 
			
		||||
        self.onFramebufferUpdateFinished()
 | 
			
		||||
 | 
			
		||||
    def _handleRectangle(self, data: bytes):
 | 
			
		||||
        xPos, yPos, width, height, encoding = s.unpack("!HHHHI", data)
 | 
			
		||||
 | 
			
		||||
        rect = RFBRectangle(xPos, yPos, width, height)
 | 
			
		||||
        self.log.debug(f"RECT: {rect}")
 | 
			
		||||
 | 
			
		||||
        if encoding == c.ENC_RAW:
 | 
			
		||||
            size = (width*height*self.pixformat.bitspp) // 8
 | 
			
		||||
            self.log.debug(f"expected size: {size}")
 | 
			
		||||
 | 
			
		||||
            start = time.time()
 | 
			
		||||
            data = self.__recv(expectedSize=size)
 | 
			
		||||
            self.log.debug(f"fetching data took: {(time.time() - start)*1e3} ms")
 | 
			
		||||
 | 
			
		||||
            self._decodeRAW(data, rect)
 | 
			
		||||
            del data
 | 
			
		||||
        else:
 | 
			
		||||
            raise TypeError(f"Unsupported encoding received ({encoding})")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # ------------------------------------------------------------------
 | 
			
		||||
    ## Image decoding stuff
 | 
			
		||||
    # ------------------------------------------------------------------        
 | 
			
		||||
 | 
			
		||||
    def _decodeRAW(self, data: bytes, rectangle: RFBRectangle):
 | 
			
		||||
        self.onRectangleUpdate(*rectangle.asTuple(), data)
 | 
			
		||||
 | 
			
		||||
    # ------------------------------------------------------------------
 | 
			
		||||
    ## Client -> Server messages
 | 
			
		||||
    # ------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    def setPixelFormat(self, pixelformat: RFBPixelformat):
 | 
			
		||||
        self.pixformat = pixelformat
 | 
			
		||||
        pformat = s.pack("!BBBBHHHBBBxxx", *pixelformat.asTuple())
 | 
			
		||||
        self.__send(s.pack("!Bxxx16s", c.CMSG_SETPIXELFORMAT, pformat))
 | 
			
		||||
 | 
			
		||||
    def setEncodings(self, encodings: list):
 | 
			
		||||
        self.__send(s.pack("!BxH", c.CMSG_SETENCODINGS, len(encodings)))
 | 
			
		||||
        for encoding in encodings:
 | 
			
		||||
            self.__send(es.return_sint32_bytes(encoding, True))
 | 
			
		||||
 | 
			
		||||
    def framebufferUpdateRequest(self,
 | 
			
		||||
        xPos=0, yPos=0, width=None, height=None,
 | 
			
		||||
        incremental=False):
 | 
			
		||||
 | 
			
		||||
        if not width: width = self.vncWidth - xPos
 | 
			
		||||
        if not height: height = self.vncHeight - yPos
 | 
			
		||||
        inc = 1 if incremental else 0
 | 
			
		||||
 | 
			
		||||
        self.__send(s.pack(
 | 
			
		||||
            "!BBHHHH",
 | 
			
		||||
            c.CMSG_FBUPDATEREQ, inc,
 | 
			
		||||
            xPos, yPos, width, height))
 | 
			
		||||
 | 
			
		||||
    def keyEvent(self, key, down=1):
 | 
			
		||||
        """
 | 
			
		||||
        For most ordinary keys, the "keysym" is the same as the corresponding ASCII value.
 | 
			
		||||
        Other common keys are shown in the KEY_ constants
 | 
			
		||||
        """
 | 
			
		||||
        self.log.debug(f'keyEvent: {key}, {"down" if down else "up"}')
 | 
			
		||||
 | 
			
		||||
        self.__send(s.pack(
 | 
			
		||||
            "!BBxxI",
 | 
			
		||||
            c.CMSG_KEYEVENT, down, key))
 | 
			
		||||
 | 
			
		||||
    def pointerEvent(self, x: int, y: int, buttommask=0):
 | 
			
		||||
        """
 | 
			
		||||
        Indicates either pointer movement or a pointer button press or release. The pointer is
 | 
			
		||||
           now at (x-position, y-position), and the current state of buttons 1 to 8 are represented
 | 
			
		||||
           by bits 0 to 7 of button-mask respectively, 0 meaning up, 1 meaning down (pressed)
 | 
			
		||||
        """
 | 
			
		||||
        if not self._connected: return
 | 
			
		||||
 | 
			
		||||
        self.log.debug(f"pointerEvent: {x}, {y}, {buttommask}")
 | 
			
		||||
 | 
			
		||||
        self.__send(s.pack(
 | 
			
		||||
            "!BBHH",
 | 
			
		||||
            c.CMSG_POINTEREVENT, buttommask, x, y))
 | 
			
		||||
 | 
			
		||||
    # ------------------------------------------------------------------
 | 
			
		||||
    ## Direct Calls
 | 
			
		||||
    # ------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    def startConnection(self):
 | 
			
		||||
        self._mainLoop = Thread(target=self.__start)
 | 
			
		||||
        self._mainLoop.start()
 | 
			
		||||
    
 | 
			
		||||
    def sendPassword(self, password):
 | 
			
		||||
        if type(password) is str:
 | 
			
		||||
            password = password.encode("ascii")
 | 
			
		||||
        password = (password + bytes(8))[:8]
 | 
			
		||||
        des = RFBDes(password)
 | 
			
		||||
        self.__send(des.encrypt(self._VNCAuthChallenge))
 | 
			
		||||
 | 
			
		||||
    def reconnect(self):
 | 
			
		||||
        self.closeConnection()
 | 
			
		||||
        self.startConnection()
 | 
			
		||||
 | 
			
		||||
    def closeConnection(self):
 | 
			
		||||
        self._stop = True
 | 
			
		||||
        self._connected = False
 | 
			
		||||
        self.__close()
 | 
			
		||||
 | 
			
		||||
        if self._mainLoop and self._mainLoop.is_alive():
 | 
			
		||||
            self.log.debug("waiting for main loop to exit")
 | 
			
		||||
            self._mainLoop.join()
 | 
			
		||||
 | 
			
		||||
    # ------------------------------------------------------------------
 | 
			
		||||
    ## Callbacks
 | 
			
		||||
    # ------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    def onConnectionMade(self):
 | 
			
		||||
        """
 | 
			
		||||
        connection is initialized and ready
 | 
			
		||||
        the pixel format and encodings can be set here using
 | 
			
		||||
 | 
			
		||||
        setPixelFormat() and setEncodings()
 | 
			
		||||
 | 
			
		||||
        the RFB main update loop will start after this function is done
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
    def onBeginUpdate(self):
 | 
			
		||||
        """
 | 
			
		||||
        called before a series of updateRectangle(),
 | 
			
		||||
        copyRectangle() or fillRectangle().
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
    def onRectangleUpdate(self,
 | 
			
		||||
            x: int, y: int, width: int, height: int, data: bytes):
 | 
			
		||||
        """
 | 
			
		||||
        new bitmap data. data are bytes in the pixel format set
 | 
			
		||||
        up earlier.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
    def onFramebufferUpdateFinished(self):
 | 
			
		||||
        """
 | 
			
		||||
        called after a series of updateRectangle(), copyRectangle()
 | 
			
		||||
        or fillRectangle() are finished.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
    def onBell(self):
 | 
			
		||||
        """
 | 
			
		||||
        a bell, yes that's right a BELL
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
    def vncRequestPassword(self):
 | 
			
		||||
        """
 | 
			
		||||
        a password is needed to log on, use sendPassword() to
 | 
			
		||||
        send one.
 | 
			
		||||
        """
 | 
			
		||||
        if not self.password:
 | 
			
		||||
            raise VNCAuthentificationFailed("No password specified")
 | 
			
		||||
        else:
 | 
			
		||||
            self.sendPassword(self.password)
 | 
			
		||||
 | 
			
		||||
    def onFatalError(self, error: Exception):
 | 
			
		||||
        """
 | 
			
		||||
        called when fatal error occurs
 | 
			
		||||
        which caused the main loop to crash
 | 
			
		||||
 | 
			
		||||
        you can try to reconnect here with reconnect()
 | 
			
		||||
        """
 | 
			
		||||
        raise error
 | 
			
		||||
							
								
								
									
										165
									
								
								novnc-client/qvncwidget/rfbconstants.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								novnc-client/qvncwidget/rfbconstants.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,165 @@
 | 
			
		||||
 | 
			
		||||
from PyQt5.QtCore import Qt
 | 
			
		||||
 | 
			
		||||
## Encoding Type for SetEncodings()
 | 
			
		||||
# publicly documented
 | 
			
		||||
ENC_RAW      = 0  # Raw
 | 
			
		||||
ENC_COPYRECT = 1  # CopyRect
 | 
			
		||||
ENC_RRE      = 2  # RRE
 | 
			
		||||
ENC_HEXTILE  = 5  # Hextile
 | 
			
		||||
ENC_TRLE     = 15 # TRLE
 | 
			
		||||
ENC_ZRLE     = 16 # ZRLE
 | 
			
		||||
 | 
			
		||||
# pseudo-encodings
 | 
			
		||||
ENC_CURSOR      = -239 # Cursor position pseudo-encoding
 | 
			
		||||
ENC_DESKTOPSIZE = -223 # DesktopSize pseudo-encoding
 | 
			
		||||
 | 
			
		||||
# additional
 | 
			
		||||
ENC_CORRE   = 4
 | 
			
		||||
ENC_ZLIB    = 6
 | 
			
		||||
ENC_TIGHT   = 7
 | 
			
		||||
ENC_ZLIBHEX = 8
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Keycodes for KeyEvent()
 | 
			
		||||
KEY_BackSpace = 0xff08
 | 
			
		||||
KEY_Tab =       0xff09
 | 
			
		||||
KEY_Return =    0xff0d
 | 
			
		||||
KEY_Escape =    0xff1b
 | 
			
		||||
KEY_Insert =    0xff63
 | 
			
		||||
KEY_Delete =    0xffff
 | 
			
		||||
KEY_Home =      0xff50
 | 
			
		||||
KEY_End =       0xff57
 | 
			
		||||
KEY_PageUp =    0xff55
 | 
			
		||||
KEY_PageDown =  0xff56
 | 
			
		||||
KEY_Left =      0xff51
 | 
			
		||||
KEY_Up =        0xff52
 | 
			
		||||
KEY_Right =     0xff53
 | 
			
		||||
KEY_Down =      0xff54
 | 
			
		||||
KEY_F1 =        0xffbe
 | 
			
		||||
KEY_F2 =        0xffbf
 | 
			
		||||
KEY_F3 =        0xffc0
 | 
			
		||||
KEY_F4 =        0xffc1
 | 
			
		||||
KEY_F5 =        0xffc2
 | 
			
		||||
KEY_F6 =        0xffc3
 | 
			
		||||
KEY_F7 =        0xffc4
 | 
			
		||||
KEY_F8 =        0xffc5
 | 
			
		||||
KEY_F9 =        0xffc6
 | 
			
		||||
KEY_F10 =       0xffc7
 | 
			
		||||
KEY_F11 =       0xffc8
 | 
			
		||||
KEY_F12 =       0xffc9
 | 
			
		||||
KEY_F13 =       0xFFCA
 | 
			
		||||
KEY_F14 =       0xFFCB
 | 
			
		||||
KEY_F15 =       0xFFCC
 | 
			
		||||
KEY_F16 =       0xFFCD
 | 
			
		||||
KEY_F17 =       0xFFCE
 | 
			
		||||
KEY_F18 =       0xFFCF
 | 
			
		||||
KEY_F19 =       0xFFD0
 | 
			
		||||
KEY_F20 =       0xFFD1
 | 
			
		||||
KEY_ShiftLeft = 0xffe1
 | 
			
		||||
KEY_ShiftRight = 0xffe2
 | 
			
		||||
KEY_ControlLeft = 0xffe3
 | 
			
		||||
KEY_ControlRight = 0xffe4
 | 
			
		||||
KEY_MetaLeft =  0xffe7
 | 
			
		||||
KEY_MetaRight = 0xffe8
 | 
			
		||||
KEY_AltLeft =   0xffe9
 | 
			
		||||
KEY_AltRight =  0xffea
 | 
			
		||||
 | 
			
		||||
KEY_Scroll_Lock = 0xFF14
 | 
			
		||||
KEY_Sys_Req =   0xFF15
 | 
			
		||||
KEY_Num_Lock =  0xFF7F
 | 
			
		||||
KEY_Caps_Lock = 0xFFE5
 | 
			
		||||
KEY_Pause =     0xFF13
 | 
			
		||||
KEY_Super_L =   0xFFEB
 | 
			
		||||
KEY_Super_R =   0xFFEC
 | 
			
		||||
KEY_Hyper_L =   0xFFED
 | 
			
		||||
KEY_Hyper_R =   0xFFEE
 | 
			
		||||
 | 
			
		||||
KEY_KP_0 =      0xFFB0
 | 
			
		||||
KEY_KP_1 =      0xFFB1
 | 
			
		||||
KEY_KP_2 =      0xFFB2
 | 
			
		||||
KEY_KP_3 =      0xFFB3
 | 
			
		||||
KEY_KP_4 =      0xFFB4
 | 
			
		||||
KEY_KP_5 =      0xFFB5
 | 
			
		||||
KEY_KP_6 =      0xFFB6
 | 
			
		||||
KEY_KP_7 =      0xFFB7
 | 
			
		||||
KEY_KP_8 =      0xFFB8
 | 
			
		||||
KEY_KP_9 =      0xFFB9
 | 
			
		||||
KEY_KP_Enter =  0xFF8D
 | 
			
		||||
 | 
			
		||||
# thanks to ken3 (https://github.com/ken3) for this
 | 
			
		||||
KEY_TRANSLATION_SPECIAL = {
 | 
			
		||||
    Qt.Key.Key_Backspace:  KEY_BackSpace,
 | 
			
		||||
    Qt.Key.Key_Tab:        KEY_Tab,
 | 
			
		||||
    Qt.Key.Key_Return:     KEY_Return,
 | 
			
		||||
    Qt.Key.Key_Escape:     KEY_Escape,
 | 
			
		||||
    Qt.Key.Key_Insert:     KEY_Insert,
 | 
			
		||||
    Qt.Key.Key_Delete:     KEY_Delete,
 | 
			
		||||
    Qt.Key.Key_Home:       KEY_Home,
 | 
			
		||||
    Qt.Key.Key_End:        KEY_End,
 | 
			
		||||
    Qt.Key.Key_PageUp:     KEY_PageUp,
 | 
			
		||||
    Qt.Key.Key_PageDown:   KEY_PageDown,
 | 
			
		||||
    Qt.Key.Key_Left:       KEY_Left,
 | 
			
		||||
    Qt.Key.Key_Up:         KEY_Up,
 | 
			
		||||
    Qt.Key.Key_Right:      KEY_Right,
 | 
			
		||||
    Qt.Key.Key_Down:       KEY_Down,
 | 
			
		||||
    Qt.Key.Key_F1:         KEY_F1,
 | 
			
		||||
    Qt.Key.Key_F2:         KEY_F2,
 | 
			
		||||
    Qt.Key.Key_F3:         KEY_F3,
 | 
			
		||||
    Qt.Key.Key_F4:         KEY_F4,
 | 
			
		||||
    Qt.Key.Key_F5:         KEY_F5,
 | 
			
		||||
    Qt.Key.Key_F6:         KEY_F6,
 | 
			
		||||
    Qt.Key.Key_F7:         KEY_F7,
 | 
			
		||||
    Qt.Key.Key_F8:         KEY_F8,
 | 
			
		||||
    Qt.Key.Key_F9:         KEY_F9,
 | 
			
		||||
    Qt.Key.Key_F10:        KEY_F10,
 | 
			
		||||
    Qt.Key.Key_F11:        KEY_F11,
 | 
			
		||||
    Qt.Key.Key_F12:        KEY_F12,
 | 
			
		||||
    Qt.Key.Key_F13:        KEY_F13,
 | 
			
		||||
    Qt.Key.Key_F14:        KEY_F14,
 | 
			
		||||
    Qt.Key.Key_F15:        KEY_F15,
 | 
			
		||||
    Qt.Key.Key_F16:        KEY_F16,
 | 
			
		||||
    Qt.Key.Key_F17:        KEY_F17,
 | 
			
		||||
    Qt.Key.Key_F18:        KEY_F18,
 | 
			
		||||
    Qt.Key.Key_F19:        KEY_F19,
 | 
			
		||||
    Qt.Key.Key_F20:        KEY_F20,
 | 
			
		||||
    Qt.Key.Key_Shift:      KEY_ShiftLeft,
 | 
			
		||||
    Qt.Key.Key_Control:    KEY_ControlLeft,
 | 
			
		||||
    Qt.Key.Key_Meta:       KEY_MetaLeft,
 | 
			
		||||
    Qt.Key.Key_Alt:        KEY_AltLeft,
 | 
			
		||||
    Qt.Key.Key_ScrollLock: KEY_Scroll_Lock,
 | 
			
		||||
    Qt.Key.Key_SysReq:     KEY_Sys_Req,
 | 
			
		||||
    Qt.Key.Key_NumLock:    KEY_Num_Lock,
 | 
			
		||||
    Qt.Key.Key_CapsLock:   KEY_Caps_Lock,
 | 
			
		||||
    Qt.Key.Key_Pause:      KEY_Pause,
 | 
			
		||||
    Qt.Key.Key_Super_L:    KEY_Super_L,
 | 
			
		||||
    Qt.Key.Key_Super_R:    KEY_Super_R,
 | 
			
		||||
    Qt.Key.Key_Hyper_L:    KEY_Hyper_L,
 | 
			
		||||
    Qt.Key.Key_Hyper_R:    KEY_Hyper_R,
 | 
			
		||||
    Qt.Key.Key_Enter:      KEY_KP_Enter,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Authentication protocol types
 | 
			
		||||
AUTH_FAIL =		0
 | 
			
		||||
AUTH_NONE = 	1
 | 
			
		||||
AUTH_VNCAUTH = 	2
 | 
			
		||||
 | 
			
		||||
# Authentication result types
 | 
			
		||||
SMSG_AUTH_OK        = 0
 | 
			
		||||
SMSG_AUTH_FAIL      = 1
 | 
			
		||||
SMSG_AUTH_TOOMANY   = 2
 | 
			
		||||
 | 
			
		||||
# Server message types
 | 
			
		||||
SMSG_FBUPDATE = 		0
 | 
			
		||||
SMSG_SETCOLORMAP = 		1
 | 
			
		||||
SMSG_BELL = 			2
 | 
			
		||||
SMSG_SERVERCUTTEXT = 	3
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Client message types
 | 
			
		||||
CMSG_SETPIXELFORMAT = 	0
 | 
			
		||||
CMSG_SETENCODINGS =		2
 | 
			
		||||
CMSG_FBUPDATEREQ = 		3
 | 
			
		||||
CMSG_KEYEVENT =			4
 | 
			
		||||
CMSG_POINTEREVENT =		5
 | 
			
		||||
CMSG_CLIENTCUTTEXT =	6
 | 
			
		||||
							
								
								
									
										19
									
								
								novnc-client/qvncwidget/rfbdes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								novnc-client/qvncwidget/rfbdes.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
 | 
			
		||||
import pyDes
 | 
			
		||||
 | 
			
		||||
class RFBDes(pyDes.des):
 | 
			
		||||
    def setKey(self, key):
 | 
			
		||||
        """
 | 
			
		||||
        RFB protocol for authentication requires client to encrypt
 | 
			
		||||
        challenge sent by server with password using DES method. However,
 | 
			
		||||
        bits in each byte of the password are put in reverse order before
 | 
			
		||||
        using it as encryption key.
 | 
			
		||||
        """
 | 
			
		||||
        newkey = list()
 | 
			
		||||
        for bsrc in key:
 | 
			
		||||
            btgt = 0
 | 
			
		||||
            for i in range(8):
 | 
			
		||||
                if bsrc & (1 << i):
 | 
			
		||||
                    btgt = btgt | (1 << 7-i)
 | 
			
		||||
            newkey.append(btgt)
 | 
			
		||||
        super(RFBDes, self).setKey(newkey)
 | 
			
		||||
							
								
								
									
										106
									
								
								novnc-client/qvncwidget/rfbhelpers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								novnc-client/qvncwidget/rfbhelpers.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,106 @@
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
import qvncwidget.rfbconstants as c
 | 
			
		||||
 | 
			
		||||
from PyQt5.QtGui import QMouseEvent
 | 
			
		||||
from PyQt5.QtCore import Qt
 | 
			
		||||
 | 
			
		||||
class RFBPixelformat:
 | 
			
		||||
    def __init__(self,
 | 
			
		||||
        bpp=32, depth=24, bigendian=False, truecolor=True,
 | 
			
		||||
        redmax=255, greenmax=255, bluemax=255,
 | 
			
		||||
        redshift=0, greenshift=0, blueshift=16):
 | 
			
		||||
 | 
			
		||||
        self.bitspp = bpp
 | 
			
		||||
        self.depth = depth
 | 
			
		||||
        self.bigendian = 1 if bigendian else 0
 | 
			
		||||
        self.truecolor = 1 if truecolor else 0
 | 
			
		||||
 | 
			
		||||
        self.redmax = redmax
 | 
			
		||||
        self.greenmax = greenmax
 | 
			
		||||
        self.bluemax = bluemax
 | 
			
		||||
        
 | 
			
		||||
        self.redshift = redshift
 | 
			
		||||
        self.greenshift = greenshift
 | 
			
		||||
        self.blueshift = blueshift
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def getRGB32():
 | 
			
		||||
        return RFBPixelformat(
 | 
			
		||||
            bpp=32, depth=32,
 | 
			
		||||
            redshift=16, greenshift=8, blueshift=0
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def getRGB16():
 | 
			
		||||
        return RFBPixelformat(
 | 
			
		||||
            bpp=16, depth=16,
 | 
			
		||||
            redmax=31, greenmax=63, bluemax=31,
 | 
			
		||||
            redshift=11, greenshift=5, blueshift=0
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def getRGB555():
 | 
			
		||||
        return RFBPixelformat(
 | 
			
		||||
            bpp=16, depth=15,
 | 
			
		||||
            redmax=31, greenmax=31, bluemax=31,
 | 
			
		||||
            redshift=10, greenshift=5, blueshift=0
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def asTuple(self) -> tuple:
 | 
			
		||||
        return (
 | 
			
		||||
            self.bitspp, self.depth, self.bigendian, self.truecolor,
 | 
			
		||||
            self.redmax, self.greenmax, self.bluemax,
 | 
			
		||||
            self.redshift, self.greenshift, self.blueshift
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def __str__(self) -> str:
 | 
			
		||||
        return ";".join(str(x) for x in self.asTuple())
 | 
			
		||||
 | 
			
		||||
class RFBRectangle:
 | 
			
		||||
    def __init__(self, xPos: int, yPos: int, width: int, height: int):
 | 
			
		||||
        self.xPos = xPos
 | 
			
		||||
        self.yPos = yPos
 | 
			
		||||
        self.width = width
 | 
			
		||||
        self.height = height
 | 
			
		||||
 | 
			
		||||
    def asTuple(self) -> tuple:
 | 
			
		||||
        return (self.xPos, self.yPos, self.width, self.height)
 | 
			
		||||
 | 
			
		||||
    def __str__(self) -> str:
 | 
			
		||||
        return f"x: {self.xPos} y: {self.yPos} width: {self.width} height: {self.height}"
 | 
			
		||||
 | 
			
		||||
class RFBInput:
 | 
			
		||||
 | 
			
		||||
    # thanks to ken3 (https://github.com/ken3) for this
 | 
			
		||||
    MOUSE_MAPPING = {
 | 
			
		||||
        Qt.LeftButton: 1 << 0,
 | 
			
		||||
        Qt.MidButton: 1 << 1,
 | 
			
		||||
        Qt.RightButton: 1 << 2,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def fromQKeyEvent(eventID: int, eventStr: str) -> int:
 | 
			
		||||
        rfbKey = c.KEY_TRANSLATION_SPECIAL.get(eventID)
 | 
			
		||||
 | 
			
		||||
        if not rfbKey:
 | 
			
		||||
            try:
 | 
			
		||||
                rfbKey = ord(eventStr)
 | 
			
		||||
            except TypeError:
 | 
			
		||||
                logging.warning(f"Unknown keytype: {eventID} | {eventStr}")
 | 
			
		||||
                return 0
 | 
			
		||||
 | 
			
		||||
        return rfbKey
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def fromQMouseEvent(eventID: QMouseEvent, pressEvent: bool, mask) -> int:
 | 
			
		||||
        _mask = RFBInput.MOUSE_MAPPING.get(eventID.button())
 | 
			
		||||
 | 
			
		||||
        # FIXME: return previous bitmask in case unknown key is pressed
 | 
			
		||||
        # TODO: implement all RFB supported buttons
 | 
			
		||||
        if not _mask: return mask
 | 
			
		||||
        
 | 
			
		||||
        if pressEvent:
 | 
			
		||||
            return mask | _mask
 | 
			
		||||
        else:
 | 
			
		||||
            return mask & ~_mask
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user