From bcb704366cb5e333a626c18c308c7e0448a8e69f Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- krdc/Makefile.am | 42 + krdc/README | 18 + krdc/TODO | 17 + krdc/_rfb._tcp | 4 + krdc/close.png | Bin 0 -> 963 bytes krdc/configure.in.in | 14 + krdc/cr32-app-krdc.png | Bin 0 -> 2049 bytes krdc/cr48-app-krdc.png | Bin 0 -> 4335 bytes krdc/events.h | 204 +++++ krdc/hostpreferences.cpp | 192 +++++ krdc/hostpreferences.h | 85 +++ krdc/hostprofiles.ui | 168 ++++ krdc/hostprofiles.ui.h | 100 +++ krdc/iconify.png | Bin 0 -> 277 bytes krdc/keycapturedialog.cpp | 143 ++++ krdc/keycapturedialog.h | 52 ++ krdc/keycapturewidget.ui | 105 +++ krdc/kfullscreenpanel.cpp | 115 +++ krdc/kfullscreenpanel.h | 78 ++ krdc/krdc.cpp | 871 +++++++++++++++++++++ krdc/krdc.desktop | 63 ++ krdc/krdc.h | 175 +++++ krdc/kremoteview.cpp | 89 +++ krdc/kremoteview.h | 289 +++++++ krdc/kservicelocator.cpp | 760 ++++++++++++++++++ krdc/kservicelocator.h | 309 ++++++++ krdc/main.cpp | 224 ++++++ krdc/main.h | 60 ++ krdc/maindialog.cpp | 78 ++ krdc/maindialog.h | 48 ++ krdc/maindialogbase.ui | 351 +++++++++ krdc/maindialogwidget.cpp | 359 +++++++++ krdc/maindialogwidget.h | 74 ++ krdc/pindown.png | Bin 0 -> 322 bytes krdc/pinup.png | Bin 0 -> 320 bytes krdc/pointcursor.png | Bin 0 -> 143 bytes krdc/pointcursormask.png | Bin 0 -> 146 bytes krdc/preferencesdialog.cpp | 128 ++++ krdc/preferencesdialog.h | 57 ++ krdc/rdp.protocol | 12 + krdc/rdp/Makefile.am | 11 + krdc/rdp/README.patch | 12 + krdc/rdp/krdpview.cpp | 369 +++++++++ krdc/rdp/krdpview.h | 113 +++ krdc/rdp/rdesktop.patch | 76 ++ krdc/rdp/rdphostpref.cpp | 180 +++++ krdc/rdp/rdphostpref.h | 127 +++ krdc/rdp/rdpprefs.ui | 509 ++++++++++++ krdc/rdp/rdpprefs.ui.h | 163 ++++ krdc/smartptr.cpp | 17 + krdc/smartptr.h | 433 +++++++++++ krdc/smb2rdc.desktop | 56 ++ krdc/vidmode.cpp | 159 ++++ krdc/vidmode.h | 40 + krdc/vnc.protocol | 13 + krdc/vnc/Makefile.am | 16 + krdc/vnc/colour.c | 415 ++++++++++ krdc/vnc/d3des.c | 440 +++++++++++ krdc/vnc/d3des.h | 51 ++ krdc/vnc/desktop.c | 1613 +++++++++++++++++++++++++++++++++++++++ krdc/vnc/hextile.c | 129 ++++ krdc/vnc/kvncview.cpp | 828 ++++++++++++++++++++ krdc/vnc/kvncview.h | 121 +++ krdc/vnc/pointerlatencyometer.h | 83 ++ krdc/vnc/rfbproto.c | 1335 ++++++++++++++++++++++++++++++++ krdc/vnc/rfbproto.h | 957 +++++++++++++++++++++++ krdc/vnc/sockets.c | 325 ++++++++ krdc/vnc/threads.cpp | 392 ++++++++++ krdc/vnc/threads.h | 126 +++ krdc/vnc/tight.c | 610 +++++++++++++++ krdc/vnc/vncauth.c | 161 ++++ krdc/vnc/vncauth.h | 30 + krdc/vnc/vnchostpref.cpp | 127 +++ krdc/vnc/vnchostpref.h | 52 ++ krdc/vnc/vncprefs.ui | 165 ++++ krdc/vnc/vncprefs.ui.h | 53 ++ krdc/vnc/vnctypes.h | 73 ++ krdc/vnc/vncviewer.h | 186 +++++ krdc/vnc/zlib.c | 157 ++++ 79 files changed, 16007 insertions(+) create mode 100644 krdc/Makefile.am create mode 100644 krdc/README create mode 100644 krdc/TODO create mode 100644 krdc/_rfb._tcp create mode 100644 krdc/close.png create mode 100644 krdc/configure.in.in create mode 100644 krdc/cr32-app-krdc.png create mode 100644 krdc/cr48-app-krdc.png create mode 100644 krdc/events.h create mode 100644 krdc/hostpreferences.cpp create mode 100644 krdc/hostpreferences.h create mode 100644 krdc/hostprofiles.ui create mode 100644 krdc/hostprofiles.ui.h create mode 100644 krdc/iconify.png create mode 100644 krdc/keycapturedialog.cpp create mode 100644 krdc/keycapturedialog.h create mode 100644 krdc/keycapturewidget.ui create mode 100644 krdc/kfullscreenpanel.cpp create mode 100644 krdc/kfullscreenpanel.h create mode 100644 krdc/krdc.cpp create mode 100644 krdc/krdc.desktop create mode 100644 krdc/krdc.h create mode 100644 krdc/kremoteview.cpp create mode 100644 krdc/kremoteview.h create mode 100644 krdc/kservicelocator.cpp create mode 100644 krdc/kservicelocator.h create mode 100644 krdc/main.cpp create mode 100644 krdc/main.h create mode 100644 krdc/maindialog.cpp create mode 100644 krdc/maindialog.h create mode 100644 krdc/maindialogbase.ui create mode 100644 krdc/maindialogwidget.cpp create mode 100644 krdc/maindialogwidget.h create mode 100644 krdc/pindown.png create mode 100644 krdc/pinup.png create mode 100644 krdc/pointcursor.png create mode 100644 krdc/pointcursormask.png create mode 100644 krdc/preferencesdialog.cpp create mode 100644 krdc/preferencesdialog.h create mode 100644 krdc/rdp.protocol create mode 100644 krdc/rdp/Makefile.am create mode 100644 krdc/rdp/README.patch create mode 100644 krdc/rdp/krdpview.cpp create mode 100644 krdc/rdp/krdpview.h create mode 100644 krdc/rdp/rdesktop.patch create mode 100644 krdc/rdp/rdphostpref.cpp create mode 100644 krdc/rdp/rdphostpref.h create mode 100644 krdc/rdp/rdpprefs.ui create mode 100644 krdc/rdp/rdpprefs.ui.h create mode 100644 krdc/smartptr.cpp create mode 100644 krdc/smartptr.h create mode 100644 krdc/smb2rdc.desktop create mode 100644 krdc/vidmode.cpp create mode 100644 krdc/vidmode.h create mode 100644 krdc/vnc.protocol create mode 100644 krdc/vnc/Makefile.am create mode 100644 krdc/vnc/colour.c create mode 100644 krdc/vnc/d3des.c create mode 100644 krdc/vnc/d3des.h create mode 100644 krdc/vnc/desktop.c create mode 100644 krdc/vnc/hextile.c create mode 100644 krdc/vnc/kvncview.cpp create mode 100644 krdc/vnc/kvncview.h create mode 100644 krdc/vnc/pointerlatencyometer.h create mode 100644 krdc/vnc/rfbproto.c create mode 100644 krdc/vnc/rfbproto.h create mode 100644 krdc/vnc/sockets.c create mode 100644 krdc/vnc/threads.cpp create mode 100644 krdc/vnc/threads.h create mode 100644 krdc/vnc/tight.c create mode 100644 krdc/vnc/vncauth.c create mode 100644 krdc/vnc/vncauth.h create mode 100644 krdc/vnc/vnchostpref.cpp create mode 100644 krdc/vnc/vnchostpref.h create mode 100644 krdc/vnc/vncprefs.ui create mode 100644 krdc/vnc/vncprefs.ui.h create mode 100644 krdc/vnc/vnctypes.h create mode 100644 krdc/vnc/vncviewer.h create mode 100644 krdc/vnc/zlib.c (limited to 'krdc') diff --git a/krdc/Makefile.am b/krdc/Makefile.am new file mode 100644 index 00000000..bf24d823 --- /dev/null +++ b/krdc/Makefile.am @@ -0,0 +1,42 @@ +KDE_CXXFLAGS = $(USE_THREADS) + +SUBDIRS = vnc rdp + +METASOURCES = AUTO + +bin_PROGRAMS = krdc +krdc_SOURCES = main.cpp krdc.cpp vidmode.cpp kfullscreenpanel.cpp \ + hostprofiles.ui maindialogbase.ui keycapturewidget.ui kservicelocator.cpp \ + keycapturedialog.cpp kremoteview.cpp smartptr.cpp hostpreferences.cpp \ + preferencesdialog.cpp maindialogwidget.cpp maindialog.cpp + +noinst_HEADERS = main.h krdc.h vidmode.h kfullscreenpanel.h events.h \ + kservicelocator.h preferencesdialog.h \ + keycapturedialog.h kremoteview.h smartptr.h hostpreferences.h + +EXTRA_DIST = README TODO + +appdatadir = $(kde_datadir)/krdc/pics +appdata_DATA = pointcursor.png pointcursormask.png pinup.png pindown.png \ + iconify.png close.png + +dnssddatadir = $(kde_datadir)/zeroconf +dnssddata_DATA = _rfb._tcp + +krdc_LDADD = vnc/libvnc.la rdp/librdp.la $(LIB_KDEUI) $(LIBXF86VIDMODE) $(LIB_SLP) $(LIB_KDNSSD) $(X_LDFLAGS) $(LIB_X11) -lkwalletclient +krdc_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +xdg_apps_DATA = krdc.desktop + +servicedir = $(kde_datadir)/konqueror/servicemenus +service_DATA = smb2rdc.desktop + +kde_services_DATA = vnc.protocol rdp.protocol + +KDE_ICON = krdc + +INCLUDES= $(all_includes) + +messages: rc.cpp + $(EXTRACTRC) *.ui */*.ui >> rc.cpp + $(XGETTEXT) *.cpp */*.cpp *.h -o $(podir)/krdc.pot diff --git a/krdc/README b/krdc/README new file mode 100644 index 00000000..40b6db0d --- /dev/null +++ b/krdc/README @@ -0,0 +1,18 @@ +KDE Remote Desktop Connection +============================= + +KDE Remote Desktop Connection (krdc) is a small VNC-compatible client +that is based on the TightVNC Unix client. You can use it to access +VNC-compatible servers like KDE's Desktop Sharing (krfb). + +Its focus is on being small and as easy to use as possible. Therefore +it does not have many capabilities that may be useful in classrooms or +for tasks like system administration (like receiving 'broadcasts' of +a remote desktop, viewing several desktops at once, tabs to switch between +desktops). In the future I plan to make the backend a KPart so that +it is possible to write a more complex clients while sharing code with +krdc. + +tim@tjansen.de + + diff --git a/krdc/TODO b/krdc/TODO new file mode 100644 index 00000000..be7930a1 --- /dev/null +++ b/krdc/TODO @@ -0,0 +1,17 @@ +For 3.2: +- new preferences system +- merge SLP services with identical serviceid +- better scaling algorithm +- SASL authentication +- X11 support + - patch xnest to allow embedding it into a QXEmbed + - add exec feature to kio_fish or maybe use KSshProcess? + +Future: +- SSL/TLS? +- make KVncView a KPart +- possibly reduce flickering by bundling bitblts + + +Known bugs/problems: + diff --git a/krdc/_rfb._tcp b/krdc/_rfb._tcp new file mode 100644 index 00000000..ad7f6db3 --- /dev/null +++ b/krdc/_rfb._tcp @@ -0,0 +1,4 @@ +Name=Shared desktops +Type=_rfb._tcp +Protocol=vnc +Helper=krdc diff --git a/krdc/close.png b/krdc/close.png new file mode 100644 index 00000000..2415dfc7 Binary files /dev/null and b/krdc/close.png differ diff --git a/krdc/configure.in.in b/krdc/configure.in.in new file mode 100644 index 00000000..23a47a75 --- /dev/null +++ b/krdc/configure.in.in @@ -0,0 +1,14 @@ +KDE_CHECK_HEADER(X11/extensions/xf86vmode.h, [ + AC_DEFINE(HAVE_VIDMODE_EXTENSION, 1, [defined if XFree86's VidMode extension is supported]) + LIBXF86VIDMODE="-lXext -lXxf86vm" +], , +[ +#include +]) +AC_SUBST(LIBXF86VIDMODE) + +KDE_CHECK_SSL() + +if test "$have_ssl" != yes || test "x$kde_use_qt_mac" = "xyes"; then + DO_NOT_COMPILE="$DO_NOT_COMPILE krdc" +fi diff --git a/krdc/cr32-app-krdc.png b/krdc/cr32-app-krdc.png new file mode 100644 index 00000000..981267f1 Binary files /dev/null and b/krdc/cr32-app-krdc.png differ diff --git a/krdc/cr48-app-krdc.png b/krdc/cr48-app-krdc.png new file mode 100644 index 00000000..1e7d236e Binary files /dev/null and b/krdc/cr48-app-krdc.png differ diff --git a/krdc/events.h b/krdc/events.h new file mode 100644 index 00000000..7ac262a7 --- /dev/null +++ b/krdc/events.h @@ -0,0 +1,204 @@ +/*************************************************************************** + events.h - QCustomEvents + ------------------- + begin : Wed Jun 05 01:13:42 CET 2002 + copyright : (C) 2002 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include + +#ifndef EVENTS_H +#define EVENTS_H + +#include + +/** + * State of the connection. The state of the connection is returned + * by @ref KRemoteView::status(). + * + * Not every state transition is allowed. You are only allowed to transition + * a state to the following state, with three exceptions: + * @li You can move from every state directly to REMOTE_VIEW_DISCONNECTED + * @li You can move from every state except REMOTE_VIEW_DISCONNECTED to + * REMOTE_VIEW_DISCONNECTING + * @li You can move from REMOTE_VIEW_DISCONNECTED to REMOTE_VIEW_CONNECTING + * + * @ref KRemoteView::setStatus() will follow this rules for you. + * (If you add/remove a state here, you must adapt it) + */ +enum RemoteViewStatus { + REMOTE_VIEW_CONNECTING = 0, + REMOTE_VIEW_AUTHENTICATING = 1, + REMOTE_VIEW_PREPARING = 2, + REMOTE_VIEW_CONNECTED = 3, + REMOTE_VIEW_DISCONNECTING = -1, + REMOTE_VIEW_DISCONNECTED = -2 +}; + +enum ErrorCode { + ERROR_NONE = 0, + ERROR_INTERNAL, + ERROR_CONNECTION, + ERROR_PROTOCOL, + ERROR_IO, + ERROR_NAME, + ERROR_NO_SERVER, + ERROR_SERVER_BLOCKED, + ERROR_AUTHENTICATION +}; + +const int ScreenResizeEventType = 41001; + +class ScreenResizeEvent : public QCustomEvent +{ +private: + int m_width, m_height; +public: + ScreenResizeEvent(int w, int h) : + QCustomEvent(ScreenResizeEventType), + m_width(w), + m_height(h) + {}; + int width() const { return m_width; }; + int height() const { return m_height; }; +}; + +const int StatusChangeEventType = 41002; + +class StatusChangeEvent : public QCustomEvent +{ +private: + RemoteViewStatus m_status; +public: + StatusChangeEvent(RemoteViewStatus s) : + QCustomEvent(StatusChangeEventType), + m_status(s) + {}; + RemoteViewStatus status() const { return m_status; }; +}; + +const int PasswordRequiredEventType = 41003; + +class PasswordRequiredEvent : public QCustomEvent +{ +public: + PasswordRequiredEvent() : + QCustomEvent(PasswordRequiredEventType) + {}; +}; + +const int FatalErrorEventType = 41004; + +class FatalErrorEvent : public QCustomEvent +{ + ErrorCode m_error; +public: + FatalErrorEvent(ErrorCode e) : + QCustomEvent(FatalErrorEventType), + m_error(e) + {}; + + ErrorCode errorCode() const { return m_error; } +}; + +const int DesktopInitEventType = 41005; + +class DesktopInitEvent : public QCustomEvent +{ +public: + DesktopInitEvent() : + QCustomEvent(DesktopInitEventType) + {}; +}; + +const int ScreenRepaintEventType = 41006; + +class ScreenRepaintEvent : public QCustomEvent +{ +private: + int m_x, m_y, m_width, m_height; +public: + ScreenRepaintEvent(int x, int y, int w, int h) : + QCustomEvent(ScreenRepaintEventType), + m_x(x), + m_y(y), + m_width(w), + m_height(h) + {}; + int x() const { return m_x; }; + int y() const { return m_y; }; + int width() const { return m_width; }; + int height() const { return m_height; }; +}; + +const int BeepEventType = 41007; + +class BeepEvent : public QCustomEvent +{ +public: + BeepEvent() : + QCustomEvent(BeepEventType) + {}; +}; + +const int ServerCutEventType = 41008; + +class ServerCutEvent : public QCustomEvent +{ +private: + char *m_bytes; + int m_length; +public: + ServerCutEvent(char *bytes, int length) : + QCustomEvent(ServerCutEventType), + m_bytes(bytes), + m_length(length) + {}; + ~ServerCutEvent() { + free(m_bytes); + } + int length() const { return m_length; }; + char *bytes() const { return m_bytes; }; +}; + +const int MouseStateEventType = 41009; + +class MouseStateEvent : public QCustomEvent +{ +private: + int m_x, m_y, m_buttonMask; +public: + MouseStateEvent(int x, int y, int buttonMask) : + QCustomEvent(MouseStateEventType), + m_x(x), + m_y(y), + m_buttonMask(buttonMask) + {}; + ~MouseStateEvent() { + } + int x() const { return m_x; }; + int y() const { return m_y; }; + int buttonMask() const { return m_buttonMask; }; +}; + +const int WalletOpenEventType = 41010; + +class WalletOpenEvent : public QCustomEvent +{ +public: + WalletOpenEvent() : + QCustomEvent(WalletOpenEventType) + {}; +}; + +#endif diff --git a/krdc/hostpreferences.cpp b/krdc/hostpreferences.cpp new file mode 100644 index 00000000..4b02191b --- /dev/null +++ b/krdc/hostpreferences.cpp @@ -0,0 +1,192 @@ +/*************************************************************************** + hostpreferences.cpp - per host preferences + ------------------- + begin : Fri May 09 22:33 CET 2003 + copyright : (C) 2003 by Tim Jansen + : (C) 2004 Nadeem Hasan + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "hostpreferences.h" +#include "vnc/vnchostpref.h" +#include "rdp/rdphostpref.h" + +#include +#include +#include + +#include +#include + +HostPreferences *HostPreferences::m_instance = 0; +static KStaticDeleter sd; + +HostPreferences *HostPreferences::instance() +{ + if ( m_instance == 0 ) + sd.setObject( m_instance, new HostPreferences() ); + + return m_instance; +} + +HostPref::HostPref(KConfig *conf, const QString &host, const QString &type) : + m_host(host), + m_type(type), + m_config(conf) { +} + +HostPref::~HostPref() { + m_config->sync(); +} + +QString HostPref::host() const { + return m_host; +} + +QString HostPref::type() const { + return m_type; +} + +QString HostPref::prefix() const { + return prefix(m_host, m_type); +} + +QString HostPref::prefix(const QString &host, const QString &type) { + return QString("PerHost-%1-%2-").arg(type).arg(host); +} + + +HostPreferences::HostPreferences() { + m_config = kapp->config(); +} + +HostPreferences::~HostPreferences() { + if ( m_instance == this ) + sd.setObject( m_instance, 0, false ); +} + +HostPrefPtr HostPreferences::getHostPref(const QString &host, const QString &type) { + m_config->setGroup("PerHostSettings"); + if (!m_config->readBoolEntry(HostPref::prefix(host, type)+"exists")) + return 0; + + if (type == VncHostPref::VncType) { + HostPrefPtr hp = new VncHostPref(m_config, host, type); + hp->load(); + return hp; + } + else if(type == RdpHostPref::RdpType) { + HostPrefPtr hp = new RdpHostPref(m_config, host, type); + hp->load(); + return hp; + } + Q_ASSERT(true); + return 0; +} + +HostPrefPtr HostPreferences::createHostPref(const QString &host, const QString &type) { + HostPrefPtr hp = getHostPref(host, type); + if (hp) + return hp; + + if(type == VncHostPref::VncType) + { + hp = new VncHostPref(m_config, host, type); + } + else if(type == RdpHostPref::RdpType) + { + hp = new RdpHostPref(m_config, host, type); + } + hp->setDefaults(); + return hp; +} + +HostPrefPtr HostPreferences::vncDefaults() +{ + HostPrefPtr hp = new VncHostPref( m_config ); + hp->load(); + + return hp; +} + +HostPrefPtr HostPreferences::rdpDefaults() +{ + HostPrefPtr hp = new RdpHostPref( m_config ); + hp->load(); + + return hp; +} + +HostPrefPtrList HostPreferences::getAllHostPrefs() { + HostPrefPtrList r; + QMap map = m_config->entryMap("PerHostSettings"); + QStringList keys = map.keys(); + QStringList::iterator it = keys.begin(); + while (it != keys.end()) { + QString key = *it; + if (key.endsWith("-exists")) { + QRegExp re("PerHost-([^-]+)-(.*)-exists"); + if (re.exactMatch(key)) { + HostPrefPtr hp = getHostPref(re.cap(2), re.cap(1)); + if (hp) + r += hp; + } + + } + it++; + } + return r; +} + +void HostPreferences::removeHostPref(HostPref *hostPref) { + hostPref->remove(); +} + +void HostPreferences::setShowBrowsingPanel( bool b ) +{ + m_config->setGroup( QString::null ); + m_config->writeEntry( "browsingPanel", b ); +} + +void HostPreferences::setServerCompletions( const QStringList &list ) +{ + m_config->setGroup( QString::null ); + m_config->writeEntry( "serverCompletions", list ); +} + +void HostPreferences::setServerHistory( const QStringList &list ) +{ + m_config->setGroup( QString::null ); + m_config->writeEntry( "serverHistory", list ); +} + +bool HostPreferences::showBrowsingPanel() +{ + m_config->setGroup( QString::null ); + return m_config->readBoolEntry( "browsingPanel" ); +} + +QStringList HostPreferences::serverCompletions() +{ + m_config->setGroup( QString::null ); + return m_config->readListEntry( "serverCompletions" ); +} + +QStringList HostPreferences::serverHistory() +{ + m_config->setGroup( QString::null ); + return m_config->readListEntry( "serverHistory" ); +} + +void HostPreferences::sync() { + m_config->sync(); +} diff --git a/krdc/hostpreferences.h b/krdc/hostpreferences.h new file mode 100644 index 00000000..09b4abe6 --- /dev/null +++ b/krdc/hostpreferences.h @@ -0,0 +1,85 @@ +/*************************************************************************** + hostpreferences.h - per host preferences + ------------------- + begin : Fri May 09 19:02 CET 2003 + copyright : (C) 2003 by Tim Jansen + : (C) 2004 Nadeem Hasan + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef HOSTPREFERENCES_H +#define HOSTPREFERENCES_H + +#include +#include +#include "smartptr.h" + +class HostPreferences; +class KConfig; + +class HostPref { +protected: + friend class HostPreferences; + QString m_host; + QString m_type; + KConfig *m_config; + + HostPref(KConfig *conf, const QString &host, const QString &type); + + virtual void load() = 0; + virtual void setDefaults() = 0; + virtual void save() = 0; + virtual void remove() = 0; +public: + virtual ~HostPref(); + + virtual QString prefDescription() const = 0; + QString host() const; + QString type() const; + QString prefix() const; + static QString prefix(const QString &host, const QString &type); +}; + +typedef SmartPtr HostPrefPtr; +typedef QValueList HostPrefPtrList; + +class HostPreferences { +public: + static HostPreferences *instance(); + ~HostPreferences(); + + HostPrefPtr getHostPref(const QString &host, const QString &type); + HostPrefPtr createHostPref(const QString &host, const QString &type); + HostPrefPtrList getAllHostPrefs(); + HostPrefPtr vncDefaults(); + HostPrefPtr rdpDefaults(); + void removeHostPref(HostPref *hostPref); + + void setShowBrowsingPanel( bool b ); + void setServerCompletions( const QStringList &list ); + void setServerHistory( const QStringList &list ); + + bool showBrowsingPanel(); + QStringList serverCompletions(); + QStringList serverHistory(); + + void sync(); + +private: + HostPreferences(); + + KConfig *m_config; + static HostPreferences *m_instance; + +}; + +#endif diff --git a/krdc/hostprofiles.ui b/krdc/hostprofiles.ui new file mode 100644 index 00000000..2b36919b --- /dev/null +++ b/krdc/hostprofiles.ui @@ -0,0 +1,168 @@ + +HostProfiles + + + HostProfiles + + + + 0 + 0 + 451 + 222 + + + + + unnamed + + + 0 + + + + + Host + + + true + + + true + + + + + Type + + + true + + + true + + + + + Settings + + + true + + + true + + + + hostListView + + + Multi + + + true + + + true + + + false + + + This list shows all hosts that you have visited and a summary of your settings for them. If you want to reset the setting for a host, you can delete it using the buttons below. When you connect again you can then re-configure them. + + + + + spacer3 + + + Horizontal + + + Expanding + + + + 117 + 21 + + + + + + removeHostButton + + + &Remove Selected Host + + + Deletes the hosts that you have selected in the list above. + + + + + removeAllButton + + + Remove &All Hosts + + + Removes all hosts from the list. + + + + + + + removeHostButton + clicked() + HostProfiles + removeHost() + + + removeAllButton + clicked() + HostProfiles + removeAllHosts() + + + hostListView + selectionChanged() + HostProfiles + selectionChanged() + + + hostListView + doubleClicked(QListViewItem*,const QPoint&,int) + HostProfiles + slotHostDoubleClicked(QListViewItem*) + + + + kdialog.h + hostpreferences.h + hostprofiles.ui.h + + + HostPrefPtrList deletedHosts; + + + hostDoubleClicked(HostPrefPtr) + + + removeHost() + removeAllHosts() + selectionChanged() + slotHostDoubleClicked( QListViewItem * ) + + + load() + save() + + + + + klistview.h + + diff --git a/krdc/hostprofiles.ui.h b/krdc/hostprofiles.ui.h new file mode 100644 index 00000000..56c63f23 --- /dev/null +++ b/krdc/hostprofiles.ui.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you wish to add, delete or rename functions or slots use +** Qt Designer which will update this file, preserving your code. Create an +** init() function in place of a constructor, and a destroy() function in +** place of a destructor. +*****************************************************************************/ + +void HostProfiles::removeHost() +{ + HostPreferences *hps = HostPreferences::instance(); + + QListViewItemIterator it(hostListView); + while (it.current()) + { + QListViewItem *vi = it.current(); + if (vi->isSelected()) + { + HostPrefPtr hp = hps->getHostPref(vi->text(0), vi->text(1)); + if (hp) + deletedHosts += hp; + delete vi; + } + else + ++it; + } + removeAllButton->setEnabled(hostListView->childCount() > 0); +} + +void HostProfiles::removeAllHosts() +{ + HostPreferences *hps = HostPreferences::instance(); + + QListViewItemIterator it(hostListView); + while (it.current()) + { + QListViewItem *vi = it.current(); + HostPrefPtr hp = hps->getHostPref(vi->text(0), vi->text(1)); + if (hp) + deletedHosts += hp; + ++it; + } + hostListView->clear(); + removeAllButton->setEnabled(false); +} + + +void HostProfiles::selectionChanged() +{ + QListViewItemIterator it(hostListView); + while (it.current()) + { + if (it.current()->isSelected()) + { + removeHostButton->setEnabled(true); + return; + } + ++it; + } + removeHostButton->setEnabled(false); +} + + +void HostProfiles::load() +{ + HostPreferences *hps = HostPreferences::instance(); + + HostPrefPtrList hplist = hps->getAllHostPrefs(); + HostPrefPtrList::iterator it = hplist.begin(); + while ( it != hplist.end() ) + { + HostPref *hp = *it; + new KListViewItem( hostListView, hp->host(), hp->type(), + hp->prefDescription() ); + ++it; + } +} + + +void HostProfiles::save() +{ + HostPreferences *hps = HostPreferences::instance(); + + HostPrefPtrList::iterator it = deletedHosts.begin(); + while (it != deletedHosts.end()) + { + hps->removeHostPref(*it); + it++; + } + + hps->sync(); +} + +void HostProfiles::slotHostDoubleClicked( QListViewItem *vi ) +{ + HostPreferences *hps = HostPreferences::instance(); + HostPrefPtr hp = hps->getHostPref(vi->text(0), vi->text(1)); + emit( hostDoubleClicked( hp )); +} diff --git a/krdc/iconify.png b/krdc/iconify.png new file mode 100644 index 00000000..d9bd0550 Binary files /dev/null and b/krdc/iconify.png differ diff --git a/krdc/keycapturedialog.cpp b/krdc/keycapturedialog.cpp new file mode 100644 index 00000000..39a68304 --- /dev/null +++ b/krdc/keycapturedialog.cpp @@ -0,0 +1,143 @@ +/*************************************************************************** + keycapturedialog.cpp - KeyCaptureDialog + ------------------- + begin : Wed Dec 25 01:20:22 CET 2002 + copyright : (C) 2002-2003 by Tim Jansen + (C) unknown (whoever wrote kshortcutdialog.cpp) + (C) 2004 Nadeem Hasan + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +// +// based on key capture code from kdelibs/kdeui/kshortcutdialog.cpp +// + +#include "keycapturedialog.h" +#include "keycapturewidget.h" + +#include +#include + +#include + +#define XK_XKB_KEYS +#define XK_MISCELLANY +#include // For x11Event() +#include // For XK_... + +#ifdef KeyPress +const int XFocusOut = FocusOut; +const int XFocusIn = FocusIn; +const int XKeyPress = KeyPress; +const int XKeyRelease = KeyRelease; +#undef KeyRelease +#undef KeyPress +#undef FocusOut +#undef FocusIn +#endif + + +KeyCaptureDialog::KeyCaptureDialog(QWidget *parent, const char *name) + : KDialogBase(parent, name, true, i18n( "Enter Key Combination" ), + Cancel, Cancel, true), m_grabbed(false) { + QFrame *main = makeMainWidget(); + QVBoxLayout *layout = new QVBoxLayout( main, 0, KDialog::spacingHint() ); + m_captureWidget = new KeyCaptureWidget( main, "m_captureWidget" ); + layout->addWidget( m_captureWidget ); + layout->addStretch(); +} + +KeyCaptureDialog::~KeyCaptureDialog() { + if (m_grabbed) + releaseKeyboard(); +} + +void KeyCaptureDialog::execute() { + m_captureWidget->keyLabel->setText(""); + exec(); + if (m_grabbed) + releaseKeyboard(); +} + +bool KeyCaptureDialog::x11Event(XEvent *pEvent) +{ + switch( pEvent->type ) { + case XKeyPress: + case XKeyRelease: + x11EventKeyPress( pEvent ); + return true; + case XFocusIn: + if (!m_grabbed) + grabKeyboard(); + return true; + case XFocusOut: + if (m_grabbed) + releaseKeyboard(); + return true; + default: + break; + } + return QWidget::x11Event( pEvent ); +} + +void KeyCaptureDialog::x11EventKeyPress( XEvent *pEvent ) +{ + // taken from kshortcutdialog.h + KKeyNative keyNative( pEvent ); + uint keyModX = keyNative.mod(), keySymX = keyNative.sym(); + if ((keySymX == XK_Escape) && !keyModX) { + accept(); + return; + } + + switch( keySymX ) { + // Don't allow setting a modifier key as an accelerator. + // Also, don't release the focus yet. We'll wait until + // we get a 'normal' key. + case XK_Shift_L: case XK_Shift_R: keyModX = KKeyNative::modX(KKey::SHIFT); break; + case XK_Control_L: case XK_Control_R: keyModX = KKeyNative::modX(KKey::CTRL); break; + case XK_Alt_L: case XK_Alt_R: keyModX = KKeyNative::modX(KKey::ALT); break; + // FIXME: check whether the Meta or Super key are for the Win modifier + case XK_Meta_L: case XK_Meta_R: + case XK_Super_L: case XK_Super_R: keyModX = KKeyNative::modX(KKey::WIN); break; + case XK_Hyper_L: case XK_Hyper_R: + case XK_Mode_switch: + case XK_Num_Lock: + case XK_Caps_Lock: + break; + default: + if( pEvent->type == XKeyPress && keyNative.sym() ) { + emit keyPressed(pEvent); + reject(); + } + return; + } + + // If we are editing the first key in the sequence, + // display modifier keys which are held down + if( pEvent->type == XKeyPress ) + keyModX |= pEvent->xkey.state; + else + keyModX = pEvent->xkey.state & ~keyModX; + + QString keyModStr; + if( keyModX & KKeyNative::modX(KKey::WIN) ) keyModStr += KKey::modFlagLabel(KKey::WIN) + "+"; + if( keyModX & KKeyNative::modX(KKey::ALT) ) keyModStr += KKey::modFlagLabel(KKey::ALT) + "+"; + if( keyModX & KKeyNative::modX(KKey::CTRL) ) keyModStr += KKey::modFlagLabel(KKey::CTRL) + "+"; + if( keyModX & KKeyNative::modX(KKey::SHIFT) ) keyModStr += KKey::modFlagLabel(KKey::SHIFT) + "+"; + + // Display currently selected modifiers, or redisplay old key. + m_captureWidget->keyLabel->setText( keyModStr ); +} + +#include "keycapturedialog.moc" + diff --git a/krdc/keycapturedialog.h b/krdc/keycapturedialog.h new file mode 100644 index 00000000..6f3aa312 --- /dev/null +++ b/krdc/keycapturedialog.h @@ -0,0 +1,52 @@ +/*************************************************************************** + keycapturedialog2.h - KeyCaptureDialog + ------------------- + begin : Wed Dec 25 01:16:23 CET 2002 + copyright : (C) 2002-2003 by Tim Jansen + : (C) 2004 Nadeem Hasan + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KEYCAPTUREDIALOG_H +#define KEYCAPTUREDIALOG_H + +class KeyCaptureWidget; + +#include "keycapturedialog.h" + +#include +#include +#include + +class KeyCaptureDialog : public KDialogBase { + Q_OBJECT + + bool m_grabbed; +public: + KeyCaptureDialog(QWidget * parent= 0, const char *name = 0); + ~KeyCaptureDialog(); + +public slots: + void execute(); + +signals: + void keyPressed(XEvent *key); + +protected: + bool x11Event(XEvent*); + void x11EventKeyPress(XEvent*); + + KeyCaptureWidget *m_captureWidget; +}; + + +#endif diff --git a/krdc/keycapturewidget.ui b/krdc/keycapturewidget.ui new file mode 100644 index 00000000..aa2865ce --- /dev/null +++ b/krdc/keycapturewidget.ui @@ -0,0 +1,105 @@ + +KeyCaptureWidget + + + KeyCaptureWidget + + + + 0 + 0 + 358 + 119 + + + + + unnamed + + + 0 + + + + textLabel1 + + + Enter a special key or key combination to send to the remote side: + + + WordBreak|AlignVCenter + + + + + spacer2 + + + Horizontal + + + Expanding + + + + 112 + 21 + + + + + + keyLabel + + + + 100 + 0 + + + + Box + + + 2 + + + + + + + + spacer3 + + + Horizontal + + + Expanding + + + + 111 + 21 + + + + + + textLabel1_2 + + + This function allows you to send a key combination like Ctrl+Alt+Del to the remote side. Press Esc to cancel. + + + WordBreak|AlignVCenter + + + + + + kdialogbase.h + + + + diff --git a/krdc/kfullscreenpanel.cpp b/krdc/kfullscreenpanel.cpp new file mode 100644 index 00000000..84067359 --- /dev/null +++ b/krdc/kfullscreenpanel.cpp @@ -0,0 +1,115 @@ +/*************************************************************************** + kfullscreenpanel.cpp - auto-hideable toolbar + ------------------- + begin : Tue May 13 23:07:42 CET 2002 + copyright : (C) 2002 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kfullscreenpanel.h" +#include + +Counter::Counter(float start) : + m_currentValue(start) { + connect(&m_timer, SIGNAL(timeout()), SLOT(timeout())); +} + +void Counter::count(float stop, float stepSize, float frequency) { + m_timer.stop(); + m_stopValue = stop; + m_stepSize = stepSize; + m_timeoutMs = (int)(1000.0 / frequency); + timeout(); +} + +void Counter::timeout() { + if (m_stepSize < 0) { + if (m_currentValue <= m_stopValue) { + m_currentValue = m_stopValue; + emit counted(m_currentValue); + emit countingDownFinished(); + return; + } + } else { + if (m_currentValue >= m_stopValue) { + m_currentValue = m_stopValue; + emit counted(m_currentValue); + emit countingUpFinished(); + return; + } + } + emit counted(m_currentValue); + + m_currentValue += m_stepSize; + m_timer.start(m_timeoutMs, true); +} + + +KFullscreenPanel::KFullscreenPanel(QWidget* parent, + const char *name, + const QSize &resolution) : + QWidget(parent, name), + m_child(0), + m_layout(0), + m_fsResolution(resolution), + m_counter(0) +{ + connect(&m_counter, SIGNAL(countingDownFinished()), SLOT(hide())); + connect(&m_counter, SIGNAL(counted(float)), SLOT(movePanel(float))); +} + +KFullscreenPanel::~KFullscreenPanel() { +} + +void KFullscreenPanel::movePanel(float posY) { + move(x(), (int)posY); + if (!isVisible()) + show(); +} + +void KFullscreenPanel::setChild(QWidget *child) { + + if (m_layout) + delete m_layout; + m_child = child; + + m_layout = new QVBoxLayout(this); + m_layout->addWidget(child); + doLayout(); +} + +void KFullscreenPanel::doLayout() { + QSize s = sizeHint(); + setFixedSize(s); + setGeometry((m_fsResolution.width() - s.width())/2, 0, + s.width(), s.height()); +} + +void KFullscreenPanel::startShow() { + m_counter.count(0, height()/3.0, 24); +} + +void KFullscreenPanel::startHide() { + m_counter.count(-height(), -height()/12.0, 24); +} + +void KFullscreenPanel::enterEvent(QEvent*) { + emit mouseEnter(); +} + +void KFullscreenPanel::leaveEvent(QEvent*) { + emit mouseLeave(); +} + + +#include "kfullscreenpanel.moc" + diff --git a/krdc/kfullscreenpanel.h b/krdc/kfullscreenpanel.h new file mode 100644 index 00000000..43b4944d --- /dev/null +++ b/krdc/kfullscreenpanel.h @@ -0,0 +1,78 @@ +/*************************************************************************** + kfullscreenpanel.cpp - auto-hideable toolbar + ------------------- + begin : Tue May 13 23:07:42 CET 2002 + copyright : (C) 2002 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KFULLSCREENPANEL_H +#define KFULLSCREENPANEL_H + +#include +#include +#include +#include + +class Counter : public QObject { + Q_OBJECT +private: + QTimer m_timer; + float m_stopValue, m_currentValue, m_stepSize; + int m_timeoutMs; + +public: + Counter(float start); + + void count(float stop, float stepSize, float frequency); +private slots: + void timeout(); + +signals: + void countingDownFinished(); + void countingUpFinished(); + void counted(float value); +}; + + +class KFullscreenPanel : public QWidget { + Q_OBJECT +private: + QWidget *m_child; + QVBoxLayout *m_layout; + QSize m_fsResolution; + Counter m_counter; + + void doLayout(); + +public: + KFullscreenPanel(QWidget* parent, const char *name, + const QSize &resolution); + ~KFullscreenPanel(); + + void setChild(QWidget *child); + void startShow(); + void startHide(); + +protected: + void enterEvent(QEvent *e); + void leaveEvent(QEvent *e); + +private slots: + void movePanel(float posY); + +signals: + void mouseEnter(); + void mouseLeave(); +}; + +#endif diff --git a/krdc/krdc.cpp b/krdc/krdc.cpp new file mode 100644 index 00000000..d7ded0fe --- /dev/null +++ b/krdc/krdc.cpp @@ -0,0 +1,871 @@ +/*************************************************************************** + krdc.cpp - main window + ------------------- + begin : Tue May 13 23:07:42 CET 2002 + copyright : (C) 2002-2003 by Tim Jansen + (C) 2003 Nadeem Hasan + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "krdc.h" +#include "maindialog.h" +#include "hostpreferences.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define BUMP_SCROLL_CONSTANT (200) + +const int VIEW_ONLY_ID = 10; +const int SHOW_LOCAL_CURSOR_ID = 20; + +const int FS_AUTOHIDE_ID = 1; +const int FS_FULLSCREEN_ID = 2; +const int FS_SCALE_ID = 3; +const int FS_HOSTLABEL_ID = 4; +const int FS_ADVANCED_ID = 5; +const int FS_ICONIFY_ID = 6; +const int FS_CLOSE_ID = 7; + +const int KRDC::TOOLBAR_AUTOHIDE_TIMEOUT = 1000; +const int KRDC::TOOLBAR_FPS_1000 = 10000; +const int KRDC::TOOLBAR_SPEED_DOWN = 34; +const int KRDC::TOOLBAR_SPEED_UP = 20; + +QScrollView2::QScrollView2(QWidget *w, const char *name) : + QScrollView(w, name) { + setMouseTracking(true); + viewport()->setMouseTracking(true); + horizontalScrollBar()->setMouseTracking(true); + verticalScrollBar()->setMouseTracking(true); +} + +void QScrollView2::mouseMoveEvent( QMouseEvent *e ) +{ + e->ignore(); +} + + +QString KRDC::m_lastHost = ""; + +KRDC::KRDC(WindowMode wm, const QString &host, + Quality q, const QString &encodings, + const QString &password, + bool scale, + bool localCursor, + QSize initialWindowSize) : + QWidget(0, 0, Qt::WStyle_ContextHelp), + m_layout(0), + m_scrollView(0), + m_view(0), + m_fsToolbar(0), + m_toolbar(0), + m_dockArea(0), + m_popup(0), + m_ftAutoHide(false), + m_showProgress(false), + m_host(host), + m_protocol(PROTOCOL_AUTO), + m_quality(q), + m_encodings(encodings), + m_password(password), + m_isFullscreen(wm), + m_oldResolution(), + m_fullscreenMinimized(false), + m_windowScaling(scale), + m_localCursor(localCursor), + m_initialWindowSize(initialWindowSize) +{ + connect(&m_autoHideTimer, SIGNAL(timeout()), SLOT(hideFullscreenToolbarNow())); + connect(&m_bumpScrollTimer, SIGNAL(timeout()), SLOT(bumpScroll())); + + m_pindown = UserIcon("pindown"); + m_pinup = UserIcon("pinup"); + + m_keyCaptureDialog = new KeyCaptureDialog(0, 0); + + setMouseTracking(true); + + KStartupInfo::appStarted(); +} + +bool KRDC::start() +{ + QString userName, password; + QString serverHost; + int serverPort = 5900; + + if (!m_host.isNull() && + (m_host != "vnc:/") && + (m_host != "rdp:/")) { + if (m_host.startsWith("vnc:/")) + m_protocol = PROTOCOL_VNC; + if (m_host.startsWith("rdp:/")) + m_protocol = PROTOCOL_RDP; + if (!parseHost(m_host, m_protocol, serverHost, serverPort, + userName, password)) { + KMessageBox::error(0, + i18n("The entered host does not have the required form."), + i18n("Malformed URL or Host")); + emit disconnectedError(); + return true; + } + } else { + + MainDialog mainDlg(this, "MainDialog"); + mainDlg.setRemoteHost(m_lastHost); + + if (mainDlg.exec() == QDialog::Rejected) { + return false; + } + + QString m_host = mainDlg.remoteHost(); + m_lastHost = m_host; + if (m_host.startsWith("vnc:/")) + m_protocol = PROTOCOL_VNC; + if (m_host.startsWith("rdp:/")) + m_protocol = PROTOCOL_RDP; + if (!parseHost(m_host, m_protocol, serverHost, serverPort, + userName, password)) { + KMessageBox::error(0, + i18n("The entered host does not have the required form."), + i18n("Malformed URL or Host")); + emit disconnectedError(); + return true; + } + } + + setCaption(i18n("%1 - Remote Desktop Connection").arg(serverHost)); + + m_scrollView = new QScrollView2(this, "remote scrollview"); + m_scrollView->setFrameStyle(QFrame::NoFrame); + m_scrollView->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, + QSizePolicy::Expanding)); + + switch(m_protocol) + { + case PROTOCOL_AUTO: + // fall through + + case PROTOCOL_VNC: + m_view = new KVncView(this, 0, serverHost, serverPort, + m_password.isNull() ? password : m_password, + m_quality, + m_localCursor ? DOT_CURSOR_ON : DOT_CURSOR_AUTO, + m_encodings); + break; + + case PROTOCOL_RDP: + m_view = new KRdpView(this, 0, serverHost, serverPort, + userName, m_password.isNull() ? password : m_password); + break; + } + + m_view->setViewOnly(kapp->config()->readBoolEntry("viewOnly", false)); + + m_scrollView->addChild(m_view); + QWhatsThis::add(m_view, i18n("Here you can see the remote desktop. If the other side allows you to control it, you can also move the mouse, click or enter keystrokes. If the content does not fit your screen, click on the toolbar's full screen button or scale button. To end the connection, just close the window.")); + + connect(m_view, SIGNAL(changeSize(int,int)), SLOT(setSize(int,int))); + connect(m_view, SIGNAL(connected()), SLOT(show())); + connect(m_view, SIGNAL(disconnected()), SIGNAL(disconnected())); + // note that the disconnectedError() will be disconnected when kremoteview + // is completely initialized + connect(m_view, SIGNAL(disconnectedError()), SIGNAL(disconnectedError())); + connect(m_view, SIGNAL(statusChanged(RemoteViewStatus)), + SLOT(changeProgress(RemoteViewStatus))); + connect(m_view, SIGNAL(showingPasswordDialog(bool)), + SLOT(showingPasswordDialog(bool))); + connect(m_keyCaptureDialog, SIGNAL(keyPressed(XEvent*)), + m_view, SLOT(pressKey(XEvent*))); + + return m_view->start(); +} + +void KRDC::changeProgress(RemoteViewStatus s) { + if (!m_progressDialog) { + m_progressDialog = new KProgressDialog(0, 0, QString::null, + "1234567890", false); + m_progressDialog->showCancelButton(true); + m_progressDialog->setMinimumDuration(0x7fffffff);//disable effectively + m_progress = m_progressDialog->progressBar(); + m_progress->setTextEnabled(false); + m_progress->setTotalSteps(3); + connect(m_progressDialog, SIGNAL(cancelClicked()), + SIGNAL(disconnectedError())); + } + + if (s == REMOTE_VIEW_CONNECTING) { + m_progress->setValue(0); + m_progressDialog->setLabel(i18n("Establishing connection...")); + m_progressDialog->setAllowCancel(false); + showProgressDialog(); + } + else if (s == REMOTE_VIEW_AUTHENTICATING) { + m_progress->setValue(1); + m_progressDialog->setLabel(i18n("Authenticating...")); + m_progressDialog->setAllowCancel(true); + } + else if (s == REMOTE_VIEW_PREPARING) { + m_progress->setValue(2); + m_progressDialog->setLabel(i18n("Preparing desktop...")); + } + else if ((s == REMOTE_VIEW_CONNECTED) || + (s == REMOTE_VIEW_DISCONNECTED)) { + m_progress->setValue(3); + hideProgressDialog(); + if (s == REMOTE_VIEW_CONNECTED) { + QObject::disconnect(m_view, SIGNAL(disconnectedError()), + this, SIGNAL(disconnectedError())); + connect(m_view, SIGNAL(disconnectedError()), + SIGNAL(disconnected())); + } + else if (m_isFullscreen == WINDOW_MODE_FULLSCREEN) + switchToNormal(m_view->scaling()); + } +} + +void KRDC::showingPasswordDialog(bool b) { + if (!m_progressDialog) + return; + if (b) + hideProgressDialog(); + else + showProgressDialog(); +} + +void KRDC::showProgressDialog() { + m_showProgress = true; + QTimer::singleShot(400, this, SLOT(showProgressTimeout())); +} + +void KRDC::hideProgressDialog() { + m_showProgress = false; + m_progressDialog->hide(); +} + +void KRDC::showProgressTimeout() { + if (!m_showProgress) + return; + + m_progressDialog->setMinimumSize(300, 50); + m_progressDialog->show(); +} + +void KRDC::quit() { + m_view->releaseKeyboard(); + hide(); + vidmodeNormalSwitch(qt_xdisplay(), m_oldResolution); + if (m_view) + m_view->startQuitting(); + emit disconnected(); +} + +bool KRDC::parseHost(QString &str, Protocol &prot, QString &serverHost, int &serverPort, + QString &userName, QString &password) { + QString s = str; + userName = QString::null; + password = QString::null; + + if (prot == PROTOCOL_AUTO) { + if(s.startsWith("smb:/")>0) { //we know it's more likely to be windows.. + s = "rdp:/" + s.section("smb:/", 1); + prot = PROTOCOL_RDP; + } else if(s.contains("://") > 0) { + s = s.section("://",1); + } else if(s.contains(":/") > 0) { + s = s.section(":/", 1); + } + } + if (prot == PROTOCOL_AUTO || prot == PROTOCOL_VNC) { + if (s.startsWith(":")) + s = "localhost" + s; + if (!s.startsWith("vnc:/")) + s = "vnc://" + s; + else if (!s.startsWith("vnc://")) // TODO: fix this in KURL! + s.insert(4, '/'); + } + if (prot == PROTOCOL_RDP) { + if (!s.startsWith("rdp:/")) + s = "rdp://" + s; + else if (!s.startsWith("rdp://")) // TODO: fix this in KURL! + s.insert(4, '/'); + } + + KURL url(s); + if (!url.isValid()) + return false; + serverHost = url.host(); + if (serverHost.isEmpty()) + serverHost = "localhost"; + serverPort = url.port(); + if ((prot == PROTOCOL_AUTO || prot == PROTOCOL_VNC) && serverPort < 100) + serverPort += 5900; + if (url.hasUser()) + userName = url.user(); + if (url.hasPass()) + password = url.pass(); + + if (url.port()) { + if (url.hasUser()) + str = QString("%1@%2:%3").arg(userName).arg(serverHost).arg(url.port()); + else + str = QString("%1:%2").arg(serverHost).arg(url.port()); + } + else { + if (url.hasUser()) + str = QString("%1@%2").arg(userName).arg(serverHost); + else + str = QString("%1").arg(serverHost); + } + return true; +} + +KRDC::~KRDC() +{ + // kill explicitly to avoid xlib calls by the threads after closing the window! + if (m_view) + delete m_view; +} + +void KRDC::enableFullscreen(bool on) +{ + if (on) { + if (m_isFullscreen != WINDOW_MODE_FULLSCREEN) + switchToFullscreen(m_view->scaling()); + } + else { + if (m_isFullscreen != WINDOW_MODE_NORMAL) + switchToNormal(m_view->scaling() || m_windowScaling); + } + m_view->switchFullscreen(on); +} + +QSize KRDC::sizeHint() +{ + if ((m_isFullscreen != WINDOW_MODE_FULLSCREEN) && m_toolbar) { + int dockHint = m_dockArea->sizeHint().height(); + dockHint = dockHint < 1 ? 1 : dockHint; // fix wrong size hint + return QSize(m_view->framebufferSize().width(), + dockHint + m_view->framebufferSize().height()); + } + else + return m_view->framebufferSize(); +} + +QPopupMenu *KRDC::createPopupMenu(QWidget *parent) const { + KPopupMenu *pu = new KPopupMenu(parent); + pu->insertItem(i18n("View Only"), this, SLOT(viewOnlyToggled()), 0, VIEW_ONLY_ID); + pu->setCheckable(true); + pu->setItemChecked(VIEW_ONLY_ID, m_view->viewOnly()); + if (m_view->supportsLocalCursor()) { + pu->insertItem(i18n("Always Show Local Cursor"), this, + SLOT(showLocalCursorToggled()), 0, + SHOW_LOCAL_CURSOR_ID); + pu->setCheckable(true); + pu->setItemChecked(SHOW_LOCAL_CURSOR_ID, + m_view->dotCursorState() == DOT_CURSOR_ON); + } + return pu; +} + +void KRDC::switchToFullscreen(bool scaling) +{ + int x, y; + + bool fromFullscreen = (m_isFullscreen == WINDOW_MODE_FULLSCREEN); + + QWidget *desktop = QApplication::desktop(); + QSize ds = desktop->size(); + QSize fbs = m_view->framebufferSize(); + bool scalingPossible = m_view->supportsScaling() && + ((fbs.width() >= ds.width()) || (fbs.height() >= ds.height())); + + if (!fromFullscreen) { + hide(); + m_oldResolution = vidmodeFullscreenSwitch(qt_xdisplay(), + m_desktopWidget.screenNumber(this), + fbs.width(), + fbs.height(), + x, y); + if (m_oldResolution.valid) + m_fullscreenResolution = QSize(x, y); + else + m_fullscreenResolution = QApplication::desktop()->size(); + m_isFullscreen = WINDOW_MODE_FULLSCREEN; + if (!scalingPossible) + m_windowScaling = m_view->scaling(); + } + + if (m_toolbar) { + m_toolbar->hide(); + m_toolbar->deleteLater(); + m_toolbar = 0; + m_dockArea->hide(); + m_dockArea->deleteLater(); + m_dockArea = 0; + } + if (m_popup) { + m_popup->deleteLater(); + m_popup = 0; + } + if (m_fsToolbar) { + m_fsToolbar->hide(); + m_fsToolbar->deleteLater(); + m_fsToolbar = 0; + } + + if (m_layout) + delete m_layout; + m_layout = new QVBoxLayout(this); + m_layout->addWidget(m_scrollView); + + if (scalingPossible) { + m_view->enableScaling(scaling); + if (scaling) + m_view->resize(ds); + else + m_view->resize(fbs); + repositionView(true); + } + else + m_view->enableScaling(false); + + m_fsToolbar = new KFullscreenPanel(this, "fstoolbar", m_fullscreenResolution); + connect(m_fsToolbar, SIGNAL(mouseEnter()), SLOT(showFullscreenToolbar())); + connect(m_fsToolbar, SIGNAL(mouseLeave()), SLOT(hideFullscreenToolbarDelayed())); + + KToolBar *t = new KToolBar(m_fsToolbar); + m_fsToolbarWidget = t; + + QIconSet pinIconSet; + pinIconSet.setPixmap(m_pinup, QIconSet::Automatic, QIconSet::Normal, QIconSet::On); + pinIconSet.setPixmap(m_pindown, QIconSet::Automatic, QIconSet::Normal, QIconSet::Off); + t->insertButton("pinup", FS_AUTOHIDE_ID); + KToolBarButton *pinButton = t->getButton(FS_AUTOHIDE_ID); + pinButton->setIconSet(pinIconSet); + QToolTip::add(pinButton, i18n("Autohide on/off")); + t->setToggle(FS_AUTOHIDE_ID); + t->setButton(FS_AUTOHIDE_ID, false); + t->addConnection(FS_AUTOHIDE_ID, SIGNAL(clicked()), this, SLOT(toggleFsToolbarAutoHide())); + + t->insertButton("window_nofullscreen", FS_FULLSCREEN_ID); + KToolBarButton *fullscreenButton = t->getButton(FS_FULLSCREEN_ID); + QToolTip::add(fullscreenButton, i18n("Fullscreen")); + t->setToggle(FS_FULLSCREEN_ID); + t->setButton(FS_FULLSCREEN_ID, true); + t->addConnection(FS_FULLSCREEN_ID, SIGNAL(toggled(bool)), this, SLOT(enableFullscreen(bool))); + + m_popup = createPopupMenu(t); + t->insertButton("configure", FS_ADVANCED_ID, m_popup, true, i18n("Advanced options")); + KToolBarButton *advancedButton = t->getButton(FS_ADVANCED_ID); + QToolTip::add(advancedButton, i18n("Advanced options")); + //advancedButton->setPopupDelay(0); + + QLabel *hostLabel = new QLabel(t); + hostLabel->setName("kde toolbar widget"); + hostLabel->setAlignment(Qt::AlignCenter); + hostLabel->setText(" "+m_host+" "); + t->insertWidget(FS_HOSTLABEL_ID, 150, hostLabel); + t->setItemAutoSized(FS_HOSTLABEL_ID, true); + + if (scalingPossible) { + t->insertButton("viewmagfit", FS_SCALE_ID); + KToolBarButton *scaleButton = t->getButton(FS_SCALE_ID); + QToolTip::add(scaleButton, i18n("Scale view")); + t->setToggle(FS_SCALE_ID); + t->setButton(FS_SCALE_ID, scaling); + t->addConnection(FS_SCALE_ID, SIGNAL(toggled(bool)), this, SLOT(switchToFullscreen(bool))); + } + + t->insertButton("iconify", FS_ICONIFY_ID); + KToolBarButton *iconifyButton = t->getButton(FS_ICONIFY_ID); + QToolTip::add(iconifyButton, i18n("Minimize")); + t->addConnection(FS_ICONIFY_ID, SIGNAL(clicked()), this, SLOT(iconify())); + + t->insertButton("close", FS_CLOSE_ID); + KToolBarButton *closeButton = t->getButton(FS_CLOSE_ID); + QToolTip::add(closeButton, i18n("Close")); + t->addConnection(FS_CLOSE_ID, SIGNAL(clicked()), this, SLOT(quit())); + + m_fsToolbar->setChild(t); + + repositionView(true); + showFullScreen(); + + setMaximumSize(m_fullscreenResolution.width(), + m_fullscreenResolution.height()); + setGeometry(0, 0, m_fullscreenResolution.width(), + m_fullscreenResolution.height()); + + KWin::setState(winId(), NET::StaysOnTop); + + m_ftAutoHide = !m_ftAutoHide; + setFsToolbarAutoHide(!m_ftAutoHide); + + if (!fromFullscreen) { + if (m_oldResolution.valid) + grabInput(qt_xdisplay(), winId()); + m_view->grabKeyboard(); + } + + m_view->switchFullscreen( true ); +} + +void KRDC::switchToNormal(bool scaling) +{ + bool fromFullscreen = (m_isFullscreen == WINDOW_MODE_FULLSCREEN); + bool scalingChanged = (scaling != m_view->scaling()); + m_windowScaling = false; // delete remembered scaling value + if (fromFullscreen) { + KWin::clearState(winId(), NET::StaysOnTop); + hide(); + } + m_isFullscreen = WINDOW_MODE_NORMAL; + m_view->enableScaling(scaling); + + m_view->releaseKeyboard(); + if (m_oldResolution.valid) { + ungrabInput(qt_xdisplay()); + vidmodeNormalSwitch(qt_xdisplay(), m_oldResolution); + m_oldResolution = Resolution(); + } + + if (m_fsToolbar) { + m_fsToolbar->hide(); + m_fsToolbar->deleteLater(); + m_fsToolbar = 0; + } + + if (!m_toolbar) { + m_dockArea = new QDockArea(Qt::Horizontal, QDockArea::Normal, this); + m_dockArea->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, + QSizePolicy::Fixed)); + KToolBar *t = new KToolBar(m_dockArea); + m_toolbar = t; + t->setIconText(KToolBar::IconTextRight); + connect(t, SIGNAL(placeChanged(QDockWindow::Place)), SLOT(toolbarChanged())); + t->insertButton("window_fullscreen", 0, true, i18n("Fullscreen")); + KToolBarButton *fullscreenButton = t->getButton(0); + QToolTip::add(fullscreenButton, i18n("Fullscreen")); + QWhatsThis::add(fullscreenButton, i18n("Switches to full screen. If the remote desktop has a different screen resolution, Remote Desktop Connection will automatically switch to the nearest resolution.")); + t->setToggle(0); + t->setButton(0, false); + t->addConnection(0, SIGNAL(toggled(bool)), this, SLOT(enableFullscreen(bool))); + + if (m_view->supportsScaling()) { + t->insertButton("viewmagfit", 1, true, i18n("Scale")); + KToolBarButton *scaleButton = t->getButton(1); + QToolTip::add(scaleButton, i18n("Scale view")); + QWhatsThis::add(scaleButton, i18n("This option scales the remote screen to fit your window size.")); + t->setToggle(1); + t->setButton(1, scaling); + t->addConnection(1, SIGNAL(toggled(bool)), this, SLOT(switchToNormal(bool))); + } + + t->insertButton("key_enter", 2, true, i18n("Special Keys")); + KToolBarButton *skButton = t->getButton(2); + QToolTip::add(skButton, i18n("Enter special keys.")); + QWhatsThis::add(skButton, i18n("This option allows you to send special key combinations like Ctrl-Alt-Del to the remote host.")); + t->addConnection(2, SIGNAL(clicked()), m_keyCaptureDialog, SLOT(execute())); + + if (m_popup) { + m_popup->deleteLater(); + m_popup = 0; + } + + m_popup = createPopupMenu(t); + t->insertButton("configure", 3, m_popup, true, i18n("Advanced")); + KToolBarButton *advancedButton = t->getButton(3); + QToolTip::add(advancedButton, i18n("Advanced options")); + //advancedButton->setPopupDelay(0); + + if (m_layout) + delete m_layout; + m_layout = new QVBoxLayout(this); + m_layout->addWidget(m_dockArea); + m_layout->addWidget(m_scrollView); + m_layout->setGeometry(QRect(0, 0, m_scrollView->width(), + m_dockArea->height() + m_scrollView->height())); + } + + if (scaling) { + m_scrollView->installEventFilter(this); + m_view->resize(m_scrollView->size()); + } + else { + m_scrollView->removeEventFilter(this); + m_view->resize(m_view->framebufferSize()); + } + + setMaximumSize(sizeHint()); + + repositionView(false); + + + if (!fromFullscreen) { + if (m_initialWindowSize.isValid()) { + resize(m_initialWindowSize); + m_initialWindowSize = QSize(); + } + else if (!scalingChanged) + resize(sizeHint()); + show(); + if (scalingChanged) + m_view->update(); + } + else + showNormal(); +} + +void KRDC::viewOnlyToggled() { + bool s = !m_view->viewOnly(); + m_popup->setItemChecked(VIEW_ONLY_ID, s); + m_view->setViewOnly(s); +} + +void KRDC::showLocalCursorToggled() { + bool s = (m_view->dotCursorState() != DOT_CURSOR_ON); + m_popup->setItemChecked(SHOW_LOCAL_CURSOR_ID, s); + m_view->showDotCursor(s ? DOT_CURSOR_ON : DOT_CURSOR_AUTO); +} + +void KRDC::iconify() +{ + KWin::clearState(winId(), NET::StaysOnTop); + + m_view->releaseKeyboard(); + if (m_oldResolution.valid) + ungrabInput(qt_xdisplay()); + + vidmodeNormalSwitch(qt_xdisplay(), m_oldResolution); + m_oldResolution = Resolution(); + showNormal(); + showMinimized(); + m_fullscreenMinimized = true; +} + +void KRDC::toolbarChanged() { + setMaximumSize(sizeHint()); + + // resize window when toolbar is docked and it was maximized + QSize fs = m_view->framebufferSize(); + QSize cs = size(); + QSize cs1(cs.width(), cs.height()-1); // adjusted for QDockArea.height()==1 + if ((fs == cs) || (fs == cs1)) + resize(sizeHint()); +} + + +bool KRDC::event(QEvent *e) { +/* used to change resolution when fullscreen was minimized */ + if ((!m_fullscreenMinimized) || (e->type() != QEvent::WindowActivate)) + return QWidget::event(e); + + m_fullscreenMinimized = false; + int x, y; + m_oldResolution = vidmodeFullscreenSwitch(qt_xdisplay(), + m_desktopWidget.screenNumber(this), + m_view->width(), + m_view->height(), + x, y); + if (m_oldResolution.valid) + m_fullscreenResolution = QSize(x, y); + else + m_fullscreenResolution = QApplication::desktop()->size(); + + showFullScreen(); + setGeometry(0, 0, m_fullscreenResolution.width(), + m_fullscreenResolution.height()); + if (m_oldResolution.valid) + grabInput(qt_xdisplay(), winId()); + m_view->switchFullscreen( true ); + KWin::setState(winId(), NET::StaysOnTop); + + return QWidget::event(e); +} + +bool KRDC::eventFilter(QObject *watched, QEvent *e) { +/* used to get events from QScrollView on resize for scale mode*/ + if (watched != m_scrollView) + return false; + if (e->type() != QEvent::Resize) + return false; + + QResizeEvent *re = (QResizeEvent*) e; + m_view->resize(re->size()); + return false; +} + +void KRDC::setSize(int w, int h) +{ + int dw, dh; + + QWidget *desktop = QApplication::desktop(); + dw = desktop->width(); + dh = desktop->height(); + + switch (m_isFullscreen) { + case WINDOW_MODE_AUTO: + if ((w > dw) || (h > dh)) + switchToFullscreen(m_windowScaling); + else + switchToNormal(m_windowScaling); + break; + case WINDOW_MODE_NORMAL: + switchToNormal(m_windowScaling); + break; + case WINDOW_MODE_FULLSCREEN: + switchToFullscreen(m_windowScaling); + break; + } +} + +void KRDC::repositionView(bool fullscreen) { + int ox = 0; + int oy = 0; + + if (!m_scrollView) + return; + + QSize s = m_view->size(); + + if (fullscreen) { + QSize d = m_fullscreenResolution; + bool margin = false; + if (d.width() > s.width()) + ox = (d.width() - s.width()) / 2; + else if (d.width() < s.width()) + margin = true; + + if (d.height() > s.height()) + oy = (d.height() - s.height()) / 2; + else if (d.height() < s.height()) + margin = true; + + if (margin) + m_layout->setMargin(1); + } + + m_scrollView->moveChild(m_view, ox, oy); +} + +void KRDC::toggleFsToolbarAutoHide() { + setFsToolbarAutoHide(!m_ftAutoHide); +} + +void KRDC::setFsToolbarAutoHide(bool on) { + + if (on == m_ftAutoHide) + return; + if (m_isFullscreen != WINDOW_MODE_FULLSCREEN) + return; + + m_ftAutoHide = on; + if (!on) + showFullscreenToolbar(); +} + +void KRDC::hideFullscreenToolbarNow() { + if (m_fsToolbar && m_ftAutoHide) + m_fsToolbar->startHide(); +} + +void KRDC::bumpScroll() { + int x = QCursor::pos().x(); + int y = QCursor::pos().y(); + QSize s = m_view->size(); + QSize d = m_fullscreenResolution; + + if (d.width() < s.width()) { + if (x == 0) + m_scrollView->scrollBy(-BUMP_SCROLL_CONSTANT, 0); + else if (x == d.width()-1) + m_scrollView->scrollBy(BUMP_SCROLL_CONSTANT, 0); + } + if (d.height() < s.height()) { + if (y == 0) + m_scrollView->scrollBy(0, -BUMP_SCROLL_CONSTANT); + else if (y == d.height()-1) + m_scrollView->scrollBy(0, BUMP_SCROLL_CONSTANT); + } + + m_bumpScrollTimer.start(333, true); +} + +void KRDC::showFullscreenToolbar() { + m_fsToolbar->startShow(); + m_autoHideTimer.stop(); +} + +void KRDC::hideFullscreenToolbarDelayed() { + if (!m_autoHideTimer.isActive()) + m_autoHideTimer.start(TOOLBAR_AUTOHIDE_TIMEOUT, true); +} + +void KRDC::mouseMoveEvent(QMouseEvent *e) { + if (m_isFullscreen != WINDOW_MODE_FULLSCREEN) + return; + + int x = e->x(); + int y = e->y(); + + /* Bump Scrolling */ + + QSize s = m_view->size(); + QSize d = m_fullscreenResolution; + if ((d.width() < s.width()) || d.height() < s.height()) { + if ((x == 0) || (x >= d.width()-1) || + (y == 0) || (y >= d.height()-1)) + bumpScroll(); + else + m_bumpScrollTimer.stop(); + } + + /* Toolbar autohide */ + + if (!m_ftAutoHide) + return; + + int dw = d.width(); + int w = m_fsToolbar->width(); + int x1 = (dw - w)/4; + int x2 = dw - x1; + + if (((y <= 0) && (x >= x1) && (x <= x2))) + showFullscreenToolbar(); + else + hideFullscreenToolbarDelayed(); + e->accept(); +} + +void KRDC::setLastHost(const QString &lastHost) { + m_lastHost = lastHost; +} + +#include "krdc.moc" diff --git a/krdc/krdc.desktop b/krdc/krdc.desktop new file mode 100644 index 00000000..2d1a13cd --- /dev/null +++ b/krdc/krdc.desktop @@ -0,0 +1,63 @@ +# KDE Config File +[Desktop Entry] +Type=Application +Exec=krdc -caption "%c" %i %m %u +Icon=krdc +Terminal=false +Name=Krdc +Name[bn]=কে-আর-ডি-সি +Name[ne]=के आर डी सी +Name[zh_TW]=Krdc é ç«¯æ¡Œé¢é€£ç·š +GenericName=Remote Desktop Connection +GenericName[be]=ЗлучÑнне з аддаленым кампутарам +GenericName[bg]=Връзка Ñ Ð¾Ñ‚Ð´. раб. мÑÑто +GenericName[bn]=পà§à¦°à¦¤à§à¦¯à¦¨à§à¦¤ ডেসà§à¦•à¦Ÿà¦ª সংযোগ +GenericName[bs]=Veza na udaljeni desktop +GenericName[ca]=Connexió remota entre escriptoris +GenericName[cs]=Vzdálené pÅ™ipojení pracovní plochy +GenericName[cy]=Cysylltiad Penbwrdd Pell +GenericName[da]=Ekstern desktopforbindelse +GenericName[de]=Verbindung zu Fremdrechner +GenericName[el]=ΣÏνδεση σε απομακÏυσμένη επιφάνεια εÏγασίας +GenericName[eo]=Konektilo al foraj tabuloj +GenericName[es]=Conexión remota de escritorio +GenericName[et]=Kaugtöölaua ühendus +GenericName[eu]=Urruneko mahaigain konexioa +GenericName[fa]=اتصال رومیزی راه دور +GenericName[fi]=Etätyöpöytäyhteys +GenericName[fr]=Connexion à un bureau distant +GenericName[ga]=Nasc Deisce Cianda +GenericName[gl]=Conexión Remota de Escritorio +GenericName[he]=חיבור לשולחן עבודה מרוחק +GenericName[hu]=Távoli munkaasztal elérése +GenericName[is]=Tengingar við fjarlæg skjáborð +GenericName[it]=Connessione a desktop remoto +GenericName[ja]=リモートデスクトップ接続 +GenericName[ka]=დáƒáƒ¨áƒáƒ áƒ”ბული სáƒáƒ›áƒ£áƒ¨áƒáƒ მáƒáƒ’იდის კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜ +GenericName[kk]=Қашықтан Ò¯Ñтелге қоÑылу +GenericName[km]=ការ​ážâ€‹áž—្ជាប់​ផ្ទៃ​ážáž»â€‹áž–ី​ចម្ងាយ +GenericName[lt]=Nutolusio darbastalio prijungimas +GenericName[nb]=Tilkobling til annet skrivebord +GenericName[nds]=Schriefdisch-Feernverbinnen +GenericName[ne]=टाढाको डेसà¥à¤•à¤Ÿà¤ª जडान +GenericName[nl]=Verbinding met extern bureaublad +GenericName[nn]=Skrivebordssamband over nettverket +GenericName[pa]=ਰਿਮੋਟ ਡੈਸਕਟਾਪ ਕà©à¨¨à¨•à©ˆà¨¸à¨¼à¨¨ +GenericName[pl]=Zdalny dostÄ™p do pulpitu +GenericName[pt]=Ligação a Ecrãs Remotos +GenericName[pt_BR]=Conexões com Ambientes de Trabalho Remotos +GenericName[ru]=Общий рабочий Ñтол +GenericName[sk]=Pripojenie na vzdialenú pracovnú plochu +GenericName[sl]=Povezava z oddaljenim namizjem +GenericName[sr]=Удаљено повезивање на радну површину +GenericName[sr@Latn]=Udaljeno povezivanje na radnu povrÅ¡inu +GenericName[sv]=Fjärranslutning till skrivbord +GenericName[tr]=Uzak Masaüstü BaÄŸlantısı +GenericName[uk]=З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· віддаленою Ñтільницею +GenericName[uz]=Masofadagi ish stoliga ulanish +GenericName[uz@cyrillic]=МаÑофадаги иш Ñтолига уланиш +GenericName[zh_CN]=远程桌é¢è¿žæŽ¥ +GenericName[zh_HK]=é ç«¯æ¡Œé¢é€£ç·š +GenericName[zh_TW]=連接é ç«¯æ¡Œé¢ +DocPath=krdc/index.html +Categories=Qt;KDE;Network;RemoteAccess; diff --git a/krdc/krdc.h b/krdc/krdc.h new file mode 100644 index 00000000..5bf9a4bd --- /dev/null +++ b/krdc/krdc.h @@ -0,0 +1,175 @@ +/*************************************************************************** + krdc.h - main window + ------------------- + begin : Tue May 13 23:10:42 CET 2002 + copyright : (C) 2002-2003 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KRDC_H +#define KRDC_H + +#include +#include +#include +#include +#include +#include +#include + +#include "vnc/kvncview.h" +#include "rdp/krdpview.h" +#include "kfullscreenpanel.h" +#include "vidmode.h" +#include "smartptr.h" +#include "keycapturedialog.h" + +class QPixmap; +class KToolBar; +class QPopupMenu; +class QDockArea; + +enum WindowMode { + WINDOW_MODE_AUTO, + WINDOW_MODE_NORMAL, + WINDOW_MODE_FULLSCREEN +}; + +// known protocols +enum Protocol { + PROTOCOL_AUTO, + PROTOCOL_VNC, + PROTOCOL_RDP +}; + +// Overloaded QScrollView, to let mouse move events through to remote widget +class QScrollView2 : public QScrollView { +public: + QScrollView2(QWidget *w, const char *name); +protected: + virtual void mouseMoveEvent(QMouseEvent *e); +}; + + +class KRDC : public QWidget +{ + Q_OBJECT +private: + SmartPtr m_progressDialog; // dialog, displayed while connecting + QVBoxLayout *m_layout; // the layout for autosizing the scrollview + QScrollView *m_scrollView; // scrollview that contains the remote widget + KProgress *m_progress; // progress bar for the dialog + KRemoteView *m_view; // the remote widget (e.g. KVncView) + + SmartPtr m_keyCaptureDialog; // dialog for key capturing + KFullscreenPanel *m_fsToolbar; // toolbar for fullscreen (0 in normal mode) + QWidget *m_fsToolbarWidget; // qt designer widget for fs toolbar + // (invalid in normal mode) + QPixmap m_pinup, m_pindown; // fs toolbar imaged for autohide button + KToolBar *m_toolbar; // toolbar in normal mode (0 in fs mode) + QDockArea *m_dockArea; // dock area for toolbar in normal mode (0 in fs mode) + QPopupMenu *m_popup; // advanced options popup (0 in fs mode) + QDesktopWidget m_desktopWidget; + + static const int TOOLBAR_AUTOHIDE_TIMEOUT; + bool m_ftAutoHide; // if true auto hide in fs is activated + QTimer m_autoHideTimer; // timer for autohide + + QTimer m_bumpScrollTimer; // timer for bump scrolling (in fs, when res too large) + + bool m_showProgress; // can disable showing the progress dialog temporary + QString m_host; // host string as given from user + Protocol m_protocol; // the used protocol + Quality m_quality; // current quality setting + QString m_encodings; // string containing the encodings, space separated, + // used for config before connection + QString m_password; // if not null, contains the password to use + QString m_resolution;// contains an alternative resolution + QString m_keymap; // keymap on the terminal server + + WindowMode m_isFullscreen; // fs/normal state + Resolution m_oldResolution; // conatins encoded res before fs + bool m_fullscreenMinimized; // true if window is currently minimized from fs + QSize m_fullscreenResolution; // xvidmode size (valid only in fs) + bool m_windowScaling; // used in startup and fullscreen to determine + // whether scaling should be enabled in norm mode. + // The current state is m_view->scaled(). + bool m_localCursor; // show local cursor no matter what + QSize m_initialWindowSize; // initial window size (windowed mode only), + // invalid after first use + static QString m_lastHost; // remembers last value of host input + + bool parseHost(QString &s, Protocol &prot, QString &serverHost, int &serverPort, + QString &userName, QString &password); + + void repositionView(bool fullscreen); + + void showProgressDialog(); + void hideProgressDialog(); + + static const int TOOLBAR_FPS_1000; + static const int TOOLBAR_SPEED_DOWN; + static const int TOOLBAR_SPEED_UP; + void fsToolbarScheduleHidden(); + QPopupMenu *createPopupMenu(QWidget *parent) const; + +protected: + virtual void mouseMoveEvent(QMouseEvent *e); + virtual bool event(QEvent *e); + virtual bool eventFilter(QObject *watched, QEvent *e); + virtual QSize sizeHint(); + +public: + KRDC(WindowMode wm = WINDOW_MODE_AUTO, + const QString &host = QString::null, + Quality q = QUALITY_UNKNOWN, + const QString &encodings = QString::null, + const QString &password = QString::null, + bool scale = false, + bool localCursor = false, + QSize initialWindowSize = QSize()); + ~KRDC(); + + bool start(); + + static void setLastHost(const QString &host); + +private slots: + void changeProgress(RemoteViewStatus s); + void showingPasswordDialog(bool b); + void showProgressTimeout(); + + void setSize(int w, int h); + void iconify(); + void toolbarChanged(); + void bumpScroll(); + + void toggleFsToolbarAutoHide(); + void setFsToolbarAutoHide(bool on); + void showFullscreenToolbar(); + void hideFullscreenToolbarDelayed(); + void hideFullscreenToolbarNow(); + +public slots: + void quit(); + void enableFullscreen(bool full = false); + void switchToNormal(bool scaling = false); + void switchToFullscreen(bool scaling = false); + void viewOnlyToggled(); + void showLocalCursorToggled(); + +signals: + void disconnected(); + void disconnectedError(); +}; + +#endif diff --git a/krdc/kremoteview.cpp b/krdc/kremoteview.cpp new file mode 100644 index 00000000..fab9ed29 --- /dev/null +++ b/krdc/kremoteview.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** + kremoteview.cpp - widget that shows the remote framebuffer + ------------------- + begin : Wed Dec 26 00:21:14 CET 2002 + copyright : (C) 2002-2003 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kremoteview.h" + +KRemoteView::KRemoteView(QWidget *parent, + const char *name, + WFlags f) : + QWidget(parent, name, f), + m_status(REMOTE_VIEW_DISCONNECTED) { +} + +enum RemoteViewStatus KRemoteView::status() { + return m_status; +} + +void KRemoteView::setStatus(RemoteViewStatus s) { + if (m_status == s) + return; + + if (((1+(int)m_status) != (int)s) && + (s != REMOTE_VIEW_DISCONNECTED)) { + // follow state transition rules + + if (s == REMOTE_VIEW_DISCONNECTING) { + if (m_status == REMOTE_VIEW_DISCONNECTED) + return; + } + else { + Q_ASSERT(((int) s) >= 0); + if (((int)m_status) > ((int)s) ) { + m_status = REMOTE_VIEW_DISCONNECTED; + emit statusChanged(REMOTE_VIEW_DISCONNECTED); + } + // smooth state transition + int origState = (int)m_status; + for (int i = origState; i < (int)s; i++) { + m_status = (RemoteViewStatus) i; + emit statusChanged((RemoteViewStatus) i); + } + } + } + m_status = s; + emit statusChanged(m_status); +} + +KRemoteView::~KRemoteView() { +} + +bool KRemoteView::supportsScaling() const { + return false; +} + +bool KRemoteView::supportsLocalCursor() const { + return false; +} + +void KRemoteView::showDotCursor(DotCursorState) { +} + +DotCursorState KRemoteView::dotCursorState() const { + return DOT_CURSOR_OFF; +} + +bool KRemoteView::scaling() const { + return false; +} + +void KRemoteView::enableScaling(bool) { +} + +void KRemoteView::switchFullscreen(bool) { +} + +#include "kremoteview.moc" diff --git a/krdc/kremoteview.h b/krdc/kremoteview.h new file mode 100644 index 00000000..d07b82a1 --- /dev/null +++ b/krdc/kremoteview.h @@ -0,0 +1,289 @@ +/*************************************************************************** + kremoteview.h - widget that shows the remote framebuffer + ------------------- + begin : Wed Dec 25 23:58:12 CET 2002 + copyright : (C) 2002-2003 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KREMOTEVIEW_H +#define KREMOTEVIEW_H + + +#include +#include +#include "events.h" + +typedef enum { + QUALITY_UNKNOWN=0, + QUALITY_HIGH=1, + QUALITY_MEDIUM=2, + QUALITY_LOW=3 +} Quality; + +/** + * Describes the state of a local cursor, if there is such a concept in the backend. + * With local cursors, there are two cursors: the cursor on the local machine (client), + * and the cursor on the remote machine (server). Because there is usually some lag, + * some backends show both cursors simultanously. In the VNC backend the local cursor + * is a dot and the remote cursor is the 'real' cursor, usually an arrow. + */ +enum DotCursorState { + DOT_CURSOR_ON, ///< Always show local cursor (and the remote one). + DOT_CURSOR_OFF, ///< Never show local cursor, only the remote one. + /// Try to measure the lag and enable the local cursor if the latency is too high. + DOT_CURSOR_AUTO +}; + +/** + * Generic widget that displays a remote framebuffer. + * Implement this if you want to add another backend. + * + * Things to take care of: + * @li The KRemoteView is responsible for its size. In + * non-scaling mode, set the fixed size of the widget + * to the remote resolution. In scaling mode, set the + * maximum size to the remote size and minimum size to the + * smallest resolution that your scaler can handle. + * @li if you override mouseMoveEvent() + * you must ignore the QEvent, because the KRDC widget will + * need it for stuff like toolbar auto-hide and bump + * scrolling. If you use x11Event(), make sure that + * MotionNotify events will be forwarded. + * + */ +class KRemoteView : public QWidget +{ + Q_OBJECT +public: + KRemoteView(QWidget *parent = 0, + const char *name = 0, + WFlags f = 0); + + virtual ~KRemoteView(); + + /** + * Checks whether the backend supports scaling. The + * default implementation returns false. + * @return true if scaling is supported + * @see scaling() + */ + virtual bool supportsScaling() const; + + /** + * Checks whether the widget is in scale mode. The + * default implementation always returns false. + * @return true if scaling is activated. Must always be + * false if @ref supportsScaling() returns false + * @see supportsScaling() + */ + virtual bool scaling() const; + + /** + * Checks whether the backend supports the concept of local cursors. The + * default implementation returns false. + * @return true if local cursors are supported/known + * @see DotCursorState + * @see showDotCursor() + * @see dotCursorState() + */ + virtual bool supportsLocalCursor() const; + + /** + * Sets the state of the dot cursor, if supported by the backend. + * The default implementation does nothing. + * @param state the new state (DOT_CURSOR_ON, DOT_CURSOR_OFF or + * DOT_CURSOR_AUTO) + * @see dotCursorState() + * @see supportsLocalCursor() + */ + virtual void showDotCursor(DotCursorState state); + + /** + * Returns the state of the local cursor. The default implementation returns + * always DOT_CURSOR_OFF. + * @return true if local cursors are supported/known + * @see showDotCursor() + * @see supportsLocalCursor() + */ + virtual DotCursorState dotCursorState() const; + + /** + * Checks whether the view is in view-only mode. This means + * that all input is ignored. + */ + virtual bool viewOnly() = 0; + + /** + * Returns the resolution of the remote framebuffer. + * It should return a null @ref QSize when the size + * is not known. + * The backend must also emit a @ref changeSize() + * when the size of the framebuffer becomes available + * for the first time or the size changed. + * @return the remote framebuffer size, a null QSize + * if unknown + */ + virtual QSize framebufferSize() = 0; + + /** + * Initiate the disconnection. This doesn't need to happen + * immediately. The call must not block. + * @see isQuitting() + */ + virtual void startQuitting() = 0; + + /** + * Checks whether the view is currently quitting. + * @return true if it is quitting + * @see startQuitting() + * @see setStatus() + */ + virtual bool isQuitting() = 0; + + /** + * Returns the host the view is connected to. + * @return the host the view is connected to + */ + virtual QString host() = 0; + + /** + * Returns the port the view is connected to. + * @return the port the view is connected to + */ + virtual int port() = 0; + + /** + * Initialize the view (for example by showing configuration + * dialogs to the user) and start connecting. Should not block + * without running the event loop (so displaying a dialog is ok). + * When the view starts connecting the application must call + * @ref setStatus() with the status REMOTE_VIEW_CONNECTING. + * @return true if successful (so far), false + * otherwise + * @see connected() + * @see disconnected() + * @see disconnectedError() + * @see statusChanged() + */ + virtual bool start() = 0; + + /** + * Returns the current status of the connection. + * @return the status of the connection + * @see setStatus() + */ + enum RemoteViewStatus status(); + +public slots: + /** + * Called to enable or disable scaling. + * Ignored if @ref supportsScaling() is false. + * The default implementation does nothing. + * @param s true to enable, false to disable. + * @see supportsScaling() + * @see scaling() + */ + virtual void enableScaling(bool s); + + /** + * Enables/disables the view-only mode. + * Ignored if @ref supportsScaling() is false. + * The default implementation does nothing. + * @param s true to enable, false to disable. + * @see supportsScaling() + * @see viewOnly() + */ + virtual void setViewOnly(bool s) = 0; + + /** + * Called to let the backend know it when + * we switch from/to fullscreen. + * @param on true when switching to fullscreen, + * false when switching from fullscreen. + */ + virtual void switchFullscreen(bool on); + + /** + * Sends a key to the remote server. + * @param k the key to send + */ + virtual void pressKey(XEvent *k) = 0; + +signals: + /** + * Emitted when the size of the remote screen changes. Also + * called when the size is known for the first time. + * @param x the width of the screen + * @param y the height of the screen + */ + void changeSize(int w, int h); + + /** + * Emitted when the view connected successfully. + */ + void connected(); + + /** + * Emitted when the view disconnected without error. + */ + void disconnected(); + + /** + * Emitted when the view disconnected with error. + */ + void disconnectedError(); + + /** + * Emitted when the status of the view changed. + * @param s the new status + */ + void statusChanged(RemoteViewStatus s); + + /** + * Emitted when the password dialog is shown or hidden. + * @param b true when the dialog is shown, false when it has + * been hidden + */ + void showingPasswordDialog(bool b); + + /** + * Emitted when the mouse on the remote side has been moved. + * @param x the new x coordinate + * @param y the new y coordinate + * @param buttonMask the mask of mouse buttons (bit 0 for first mouse + * button, 1 for second button etc)a + */ + void mouseStateChanged(int x, int y, int buttonMask); + +protected: + /** + * The status of the remote view. + */ + enum RemoteViewStatus m_status; + + /** + * Set the status of the connection. + * Emits a statusChanged() signal. + * Note that the states need to be set in a certain order, + * see @ref RemoteViewStatus. setStatus() will try to do this + * transition automatically, so if you are in REMOTE_VIEW_CONNECTING + * and call setStatus(REMOTE_VIEW_PREPARING), setStatus() will + * emit a REMOTE_VIEW_AUTHENTICATING and then REMOTE_VIEW_PREPARING. + * If you transition backwards, it will emit a + * REMOTE_VIEW_DISCONNECTED before doing the transition. + * @param s the new status + */ + virtual void setStatus(RemoteViewStatus s); +}; + +#endif diff --git a/krdc/kservicelocator.cpp b/krdc/kservicelocator.cpp new file mode 100644 index 00000000..b8382db1 --- /dev/null +++ b/krdc/kservicelocator.cpp @@ -0,0 +1,760 @@ +/* + * Interface to find SLP services. + * Copyright (C) 2002 Tim Jansen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "kservicelocator.h" +#include +#include +#include + +#ifdef HAVE_SLP +#include +#include +#include +#endif + +const QString KServiceLocator::DEFAULT_AUTHORITY = ""; +const QString KServiceLocator::ALL_AUTHORITIES = QString::null; + +class AsyncThread; + +class KServiceLocatorPrivate { +public: + KServiceLocator *m_ksl; + bool m_opened; + volatile bool m_abort; + QString m_lang; + bool m_async; + + bool ensureOpen(); + + KServiceLocatorPrivate(KServiceLocator *ksl, + const QString &lang, + bool async); + ~KServiceLocatorPrivate(); + +#ifdef HAVE_SLP + SLPHandle m_handle; + AsyncThread *m_thread; + QString m_operationServiceUrl; // for findAttributes()/foundAttributes() + + friend class FindSrvTypesThread; + friend class FindSrvsThread; + friend class FindAttrsThread; + friend class FindScopesThread; + + friend SLPBoolean srvTypeCallback(SLPHandle hslp, + const char* srvtypes, + SLPError errcode, + void* cookie); + friend SLPBoolean srvTypeCallbackAsync(SLPHandle hslp, + const char* srvtypes, + SLPError errcode, + void* cookie); + friend SLPBoolean srvUrlCallback(SLPHandle hslp, + const char* srvurl, + unsigned short lifetime, + SLPError errcode, + void* cookie); + friend SLPBoolean srvUrlCallbackAsync(SLPHandle hslp, + const char* srvurl, + unsigned short lifetime, + SLPError errcode, + void* cookie); + friend SLPBoolean srvAttrCallback(SLPHandle hslp, + const char* attrlist, + SLPError errcode, + void* cookie); + friend SLPBoolean srvAttrCallbackAsync(SLPHandle hslp, + const char* attrlist, + SLPError errcode, + void* cookie); + + SLPBoolean handleSrvTypeCallback(const char* srvtypes, + SLPError errcode); + SLPBoolean handleSrvTypeCallbackAsync(const char* srvtypes, + SLPError errcode); + SLPBoolean handleSrvUrlCallback(const char* srvUrl, + unsigned short lifetime, + SLPError errcode); + SLPBoolean handleSrvUrlCallbackAsync(const char* srvUrl, + unsigned short lifetime, + SLPError errcode); + SLPBoolean handleAttrCallback(const char* attrlist, + SLPError errcode); + SLPBoolean handleAttrCallbackAsync(const char* attrlist, + SLPError errcode); +#endif +}; + +KServiceLocator::KServiceLocator(const QString &lang, bool async) : + QObject(0, "KServiceLocator") { + + d = new KServiceLocatorPrivate(this, lang, async); +} + +KServiceLocatorPrivate::KServiceLocatorPrivate(KServiceLocator *ksl, + const QString &lang, + bool async) : + m_ksl(ksl), + m_opened(false), + m_abort(false), + m_lang(lang), + m_async(async) { + +#ifdef HAVE_SLP + m_thread = 0; +#endif +} + + +#ifdef HAVE_SLP /** The real SLP implementations ********************** */ + + +/* ****** *** ****** *** ****** */ +/* Signals for async events */ +/* ****** *** ****** *** ****** */ + +const int MinLastSignalEventType = 45001; +const int LastServiceTypeSignalEventType = 45001; +const int LastServiceSignalEventType = 45002; +const int LastAttributesSignalEventType = 45003; +const int MaxLastSignalEventType = 45003; +class LastSignalEvent : public QCustomEvent +{ +private: + bool m_success; +public: + LastSignalEvent(int type, bool s) : + QCustomEvent(type), + m_success(s) + {}; + int success() const { return m_success; }; +}; + +const int FoundServiceTypesEventType = 45012; +class FoundServiceTypesEvent : public QCustomEvent +{ +private: + QString m_srvtypes; +public: + FoundServiceTypesEvent(const char *srvtypes) : + QCustomEvent(FoundServiceTypesEventType), + m_srvtypes(srvtypes) + {}; + QString srvtypes() const { return m_srvtypes; }; +}; + +const int FoundServiceEventType = 45013; +class FoundServiceEvent : public QCustomEvent +{ +private: + QString m_srvUrl; + unsigned short m_lifetime; +public: + FoundServiceEvent(const char *srvUrl, unsigned short lifetime) : + QCustomEvent(FoundServiceEventType), + m_srvUrl(srvUrl), + m_lifetime(lifetime) + {}; + QString srvUrl() const { return m_srvUrl; }; + unsigned short lifetime() const { return m_lifetime; }; +}; + +const int FoundAttributesEventType = 45014; +class FoundAttributesEvent : public QCustomEvent +{ +private: + QString m_attributes; +public: + FoundAttributesEvent(const char *attributes) : + QCustomEvent(FoundAttributesEventType), + m_attributes(attributes) + {}; + QString attributes() const { return m_attributes; }; +}; + +const int FoundScopesEventType = 45015; +class FoundScopesEvent : public QCustomEvent +{ +private: + QString m_scopes; +public: + FoundScopesEvent(const char *scopes) : + QCustomEvent(FoundScopesEventType), + m_scopes(scopes) + {}; + QString scopes() const { return m_scopes; }; +}; + + + +/* ****** *** ****** *** ****** */ +/* Callbacks */ +/* ****** *** ****** *** ****** */ + +SLPBoolean srvTypeCallback(SLPHandle, + const char* srvtypes, + SLPError errcode, + void* cookie) { + KServiceLocatorPrivate *ksl = (KServiceLocatorPrivate*) cookie; + return ksl->handleSrvTypeCallback(srvtypes, errcode); +} +SLPBoolean srvTypeCallbackAsync(SLPHandle, + const char* srvtypes, + SLPError errcode, + void* cookie) { + KServiceLocatorPrivate *ksl = (KServiceLocatorPrivate*) cookie; + if ((errcode != SLP_OK) || ksl->m_abort) { + QApplication::postEvent(ksl->m_ksl, new LastSignalEvent(LastServiceTypeSignalEventType, + (errcode == SLP_OK) || + (errcode == SLP_LAST_CALL) || + ksl->m_abort)); + return SLP_FALSE; + } + QApplication::postEvent(ksl->m_ksl, new FoundServiceTypesEvent(srvtypes)); + return SLP_TRUE; +} + +SLPBoolean srvUrlCallback(SLPHandle, + const char* srvurl, + unsigned short lifetime, + SLPError errcode, + void* cookie) { + KServiceLocatorPrivate *ksl = (KServiceLocatorPrivate*) cookie; + return ksl->handleSrvUrlCallback(srvurl, lifetime, errcode); +} +SLPBoolean srvUrlCallbackAsync(SLPHandle, + const char* srvurl, + unsigned short lifetime, + SLPError errcode, + void* cookie) { + KServiceLocatorPrivate *ksl = (KServiceLocatorPrivate*) cookie; + if ((errcode != SLP_OK) || ksl->m_abort) { + QApplication::postEvent(ksl->m_ksl, new LastSignalEvent(LastServiceSignalEventType, + (errcode == SLP_OK) || + (errcode == SLP_LAST_CALL) || + ksl->m_abort)); + return SLP_FALSE; + } + QApplication::postEvent(ksl->m_ksl, + new FoundServiceEvent(srvurl, lifetime)); + return SLP_TRUE; +} + +SLPBoolean srvAttrCallback(SLPHandle, + const char* attrlist, + SLPError errcode, + void* cookie) { + KServiceLocatorPrivate *ksl = (KServiceLocatorPrivate*) cookie; + return ksl->handleAttrCallback(attrlist, errcode); +} +SLPBoolean srvAttrCallbackAsync(SLPHandle, + const char* attrlist, + SLPError errcode, + void* cookie) { + KServiceLocatorPrivate *ksl = (KServiceLocatorPrivate*) cookie; + if ((errcode != SLP_OK) || ksl->m_abort) { + QApplication::postEvent(ksl->m_ksl, + new LastSignalEvent(LastAttributesSignalEventType, + (errcode == SLP_OK) || + (errcode == SLP_LAST_CALL) || + ksl->m_abort)); + return SLP_FALSE; + } + QApplication::postEvent(ksl->m_ksl, + new FoundAttributesEvent(attrlist)); + return SLP_TRUE; +} + + + +/* ****** *** ****** *** ****** */ +/* Threads for async events */ +/* ****** *** ****** *** ****** */ + +class AsyncThread : public QThread { +protected: + SLPHandle m_handle; + KServiceLocatorPrivate *m_parent; + AsyncThread(SLPHandle handle, KServiceLocatorPrivate *parent) : + m_handle(handle), + m_parent(parent) { + } +}; +class FindSrvTypesThread : public AsyncThread { + QString m_namingAuthority, m_scopeList; +public: + FindSrvTypesThread(SLPHandle handle, + KServiceLocatorPrivate *parent, + const char *namingAuthority, + const char *scopeList) : + AsyncThread(handle, parent), + m_namingAuthority(namingAuthority), + m_scopeList(scopeList){ + } + virtual void run() { + SLPError e; + e = SLPFindSrvTypes(m_handle, + m_namingAuthority.latin1(), + m_scopeList.latin1(), + srvTypeCallbackAsync, + m_parent); + if (e != SLP_OK) + QApplication::postEvent(m_parent->m_ksl, + new LastSignalEvent(LastServiceTypeSignalEventType, + false)); + } +}; +class FindSrvsThread : public AsyncThread { + QString m_srvUrl, m_scopeList, m_filter; +public: + FindSrvsThread(SLPHandle handle, + KServiceLocatorPrivate *parent, + const char *srvUrl, + const char *scopeList, + const char *filter) : + AsyncThread(handle, parent), + m_srvUrl(srvUrl), + m_scopeList(scopeList), + m_filter(filter) { + } + virtual void run() { + SLPError e; + + e = SLPFindSrvs(m_handle, + m_srvUrl.latin1(), + m_scopeList.isNull() ? "" : m_scopeList.latin1(), + m_filter.isNull() ? "" : m_filter.latin1(), + srvUrlCallbackAsync, + m_parent); + if (e != SLP_OK) + QApplication::postEvent(m_parent->m_ksl, + new LastSignalEvent(LastServiceSignalEventType, + false)); + } +}; +class FindAttrsThread : public AsyncThread { + QString m_srvUrl, m_scopeList, m_attrIds; +public: + FindAttrsThread(SLPHandle handle, + KServiceLocatorPrivate *parent, + const char *srvUrl, + const char *scopeList, + const char *attrIds) : + AsyncThread(handle, parent), + m_srvUrl(srvUrl), + m_scopeList(scopeList), + m_attrIds(attrIds) { + } + virtual void run() { + SLPError e; + e = SLPFindAttrs(m_handle, + m_srvUrl.latin1(), + m_scopeList.isNull() ? "" : m_scopeList.latin1(), + m_attrIds.isNull() ? "" : m_attrIds.latin1(), + srvAttrCallbackAsync, + m_parent); + if (e != SLP_OK) + QApplication::postEvent(m_parent->m_ksl, + new LastSignalEvent(LastAttributesSignalEventType, + false)); + } +}; +class FindScopesThread : public AsyncThread { +public: + FindScopesThread(SLPHandle handle, + KServiceLocatorPrivate *parent) : + AsyncThread(handle, parent){ + } + virtual void run() { + SLPError e; + char *_scopelist; + + e = SLPFindScopes(m_handle, &_scopelist); + if (e != SLP_OK) { + QApplication::postEvent(m_parent->m_ksl, + new FoundScopesEvent("")); + return; + } + + QString scopeList(_scopelist); + SLPFree(_scopelist); + QApplication::postEvent(m_parent->m_ksl, + new FoundScopesEvent(scopeList.latin1())); + } +}; + +KServiceLocatorPrivate::~KServiceLocatorPrivate() { + if (m_thread) { + m_abort = true; + m_thread->wait(); + delete m_thread; + m_thread = 0; // important, because event handler will run + } + if (m_opened) + SLPClose(m_handle); +} + +KServiceLocator::~KServiceLocator() { + delete d; +} + +bool KServiceLocator::available() { + return d->ensureOpen(); +} + +void KServiceLocator::abortOperation() { + d->m_abort = true; +} + +bool KServiceLocatorPrivate::ensureOpen() { + SLPError e; + + if (m_opened) + return true; + e = SLPOpen(m_lang.latin1(), SLP_FALSE, &m_handle); + if (e != SLP_OK) { + kdError() << "KServiceLocator: error while opening:" << e <ensureOpen()) + return false; + if (d->m_thread) + return false; + d->m_abort = false; + + if (d->m_async) { + d->m_thread = new FindSrvTypesThread(d->m_handle, + d, + namingAuthority.isNull() ? "*" : namingAuthority.latin1(), + scopeList.isNull() ? "" : scopeList.latin1()); + d->m_thread->start(); + return true; + } + else { + SLPError e; + e = SLPFindSrvTypes(d->m_handle, + namingAuthority.isNull() ? "*" : namingAuthority.latin1(), + scopeList.isNull() ? "" : scopeList.latin1(), + srvTypeCallback, + d); + return e == SLP_OK; + } +} + +bool KServiceLocator::findServices(const QString &srvtype, + const QString &filter, + const QString &scopeList) { + if (!d->ensureOpen()) + return false; + if (d->m_thread) + return false; + d->m_abort = false; + + if (d->m_async) { + d->m_thread = new FindSrvsThread(d->m_handle, + d, + srvtype.latin1(), + scopeList.isNull() ? "" : scopeList.latin1(), + filter.isNull() ? "" : filter.latin1()); + d->m_thread->start(); + return true; + } + else { + SLPError e; + e = SLPFindSrvs(d->m_handle, + srvtype.latin1(), + scopeList.isNull() ? "" : scopeList.latin1(), + filter.isNull() ? "" : filter.latin1(), + srvUrlCallback, + d); + return e == SLP_OK; + } +} + +bool KServiceLocator::findAttributes(const QString &serviceUrl, + const QString &attributeIds) { + if (!d->ensureOpen()) + return false; + if (d->m_thread) + return false; + d->m_abort = false; + + d->m_operationServiceUrl = serviceUrl; + if (d->m_async) { + d->m_thread = new FindAttrsThread(d->m_handle, + d, + serviceUrl.latin1(), + "", + attributeIds.isNull() ? "" : attributeIds.latin1()); + d->m_thread->start(); + return true; + } + else { + SLPError e; + e = SLPFindAttrs(d->m_handle, + serviceUrl.latin1(), + "", + attributeIds.isNull() ? "" : attributeIds.latin1(), + srvAttrCallback, + d); + return e == SLP_OK; + } +} + +bool KServiceLocator::findScopes() { + if (!d->ensureOpen()) + return false; + if (d->m_thread) + return false; + d->m_abort = false; + + if (d->m_async) { + d->m_thread = new FindScopesThread(d->m_handle, d); + d->m_thread->start(); + return true; + } + else { + SLPError e; + char *_scopelist; + QStringList scopeList; + e = SLPFindScopes(d->m_handle, &_scopelist); + if (e != SLP_OK) + return false; + scopeList = parseCommaList(_scopelist); + SLPFree(_scopelist); + emit foundScopes(scopeList); + return true; + } +} + +SLPBoolean KServiceLocatorPrivate::handleSrvTypeCallback(const char* srvtypes, + SLPError errcode) { + if ((errcode != SLP_OK) || m_abort) { + m_ksl->emitLastServiceTypeSignal((errcode == SLP_OK) || + (errcode == SLP_LAST_CALL) || + m_abort); + return SLP_FALSE; + } + m_ksl->emitFoundServiceTypes(srvtypes); + return SLP_TRUE; +} + +SLPBoolean KServiceLocatorPrivate::handleSrvUrlCallback(const char* srvurl, + unsigned short lifetime, + SLPError errcode) { + if ((errcode != SLP_OK) || m_abort) { + m_ksl->emitLastServiceSignal((errcode == SLP_OK) || + (errcode == SLP_LAST_CALL) || + m_abort); + return SLP_FALSE; + } + m_ksl->emitFoundService(srvurl, lifetime); + return SLP_TRUE; +} + +SLPBoolean KServiceLocatorPrivate::handleAttrCallback(const char* attrlist, + SLPError errcode) { + if ((errcode != SLP_OK) || m_abort) { + m_ksl->emitLastAttributesSignal((errcode == SLP_OK) || + (errcode == SLP_LAST_CALL) || + m_abort); + return SLP_FALSE; + } + m_ksl->emitFoundAttributes(m_operationServiceUrl, attrlist); + return SLP_TRUE; +} + + +void KServiceLocator::customEvent(QCustomEvent *e) { + if ((e->type() >= MinLastSignalEventType) && + (e->type() <= MaxLastSignalEventType)){ + bool s = true; + if (d->m_thread) { + d->m_thread->wait(); + delete d->m_thread; + d->m_thread = 0; + s = ((LastSignalEvent*)e)->success(); + } + if (e->type() == LastServiceTypeSignalEventType) + emit lastServiceTypeSignal(s); + else if (e->type() == LastServiceSignalEventType) + emit lastServiceSignal(s); + else if (e->type() == LastAttributesSignalEventType) + emit lastAttributesSignal(s); + else + kdFatal() << "unmapped last signal type " << e->type()<< endl; + } + else if (e->type() == FoundAttributesEventType) { + emit foundAttributes(d->m_operationServiceUrl, + ((FoundAttributesEvent*)e)->attributes()); + } + else if (e->type() == FoundServiceEventType) { + FoundServiceEvent *fse = (FoundServiceEvent*)e; + emit foundService(fse->srvUrl(), fse->lifetime()); + } + else if (e->type() == FoundServiceTypesEventType) { + emit foundServiceTypes(((FoundServiceTypesEvent*)e)->srvtypes()); + } + else if (e->type() == FoundScopesEventType) { + if (d->m_thread) { + d->m_thread->wait(); + delete d->m_thread; + d->m_thread = 0; + emit foundScopes(KServiceLocator::parseCommaList(((FoundScopesEvent*)e)->scopes())); + } + } +} + +QString KServiceLocator::decodeAttributeValue(const QString &value) { + char *n; + if (value.isNull()) + return value; + if (SLPUnescape(value.latin1(), &n, SLP_TRUE) != SLP_OK) + return QString::null; + QString r(n); + SLPFree(n); + return r; +} + +#else /** Empty dummy functions is SLP is not available ************************* */ + + +KServiceLocator::~KServiceLocator() { +} +bool KServiceLocator::available() { + return false; +} +void KServiceLocator::abortOperation() { +} +bool KServiceLocator::findServiceTypes(const QString &,const QString &) { + return false; +} +bool KServiceLocator::findServices(const QString &,const QString &,const QString &) { + return false; +} +bool KServiceLocator::findAttributes(const QString &,const QString &) { + return false; +} +bool KServiceLocator::findScopes() { + return false; +} +void KServiceLocator::customEvent(QCustomEvent *) { +} +QString KServiceLocator::decodeAttributeValue(const QString &value) { + return value; +} + +#endif + + +/*** Private emit-helpers ***/ + +void KServiceLocator::emitFoundServiceTypes(QString serviceTypes) { + emit foundServiceTypes(serviceTypes); +} +void KServiceLocator::emitFoundService(QString serviceUrl, + int lifetime) { + emit foundService(serviceUrl, lifetime); +} +void KServiceLocator::emitFoundAttributes(QString serviceUrl, + QString attributes) { + emit foundAttributes(serviceUrl, attributes); +} +void KServiceLocator::emitFoundScopes(QStringList scopeList) { + emit foundScopes(scopeList); +} +void KServiceLocator::emitLastServiceTypeSignal(bool success) { + emit lastServiceTypeSignal(success); +} +void KServiceLocator::emitLastServiceSignal(bool success) { + emit lastServiceSignal(success); +} +void KServiceLocator::emitLastAttributesSignal(bool success) { + emit lastAttributesSignal(success); +} + + + +/*** Static helpers ***/ + +void KServiceLocator::parseAttributeList(const QString &attributes, + QMap &attributeMap) { + QRegExp r("\\((.*)=(.*)\\)"); + r.setMinimal(true); + int pos = 0; + while (pos >= 0) { + pos = r.search(attributes, pos); + if (pos != -1) { + attributeMap[r.cap(1)] = r.cap(2); + pos += r.matchedLength(); + } + } +} + +QStringList KServiceLocator::parseCommaList(const QString &list) { + return QStringList::split(QChar(','), list); +} + +QString KServiceLocator::createCommaList(const QStringList &values) { + return values.join(","); +} + +QString KServiceLocator::escapeFilter(const QString &str) { + QString f; + int s = str.length(); + for (int i = 0; i < s; i++) { + char c = str[i]; + switch(c) { + case '*': + f.append("\2a"); + break; + case '(': + f.append("\28"); + break; + case ')': + f.append("\29"); + break; + case '\\': + f.append("\5c"); + break; + case 0: + f.append("\2a"); + break; + default: + f.append(c); + break; + } + } + return f; +} + +#include "kservicelocator.moc" + diff --git a/krdc/kservicelocator.h b/krdc/kservicelocator.h new file mode 100644 index 00000000..1177000f --- /dev/null +++ b/krdc/kservicelocator.h @@ -0,0 +1,309 @@ +/* + * Interface to find SLP services. + * Copyright (C) 2002 Tim Jansen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __KSERVICELOCATOR_H +#define __KSERVICELOCATOR_H + +#include +#include +#include + +class KServiceLocatorPrivate; + +/** + * KServiceLocator allows you to search the network for service types, + * services and their attributes using SLP. + * Note that most find methods can work in a synchronous and a asynchronous + * mode. In synchronous mode the find method emits a number of signals and + * returns. In asynchronous mode the method returns as soon as possible and + * will emit the signals either during or after the find method. You can have + * only one find search at a given time. + * + * @version $Id$ + * @author Tim Jansen, tim@tjansen.de + */ +class KServiceLocator : public QObject { + Q_OBJECT + private: + friend class KServiceLocatorPrivate; + KServiceLocatorPrivate *d; + + public: + /** + * Creates a new KServiceLocator. + * @param lang the language to search in, or QString::null for the + * default language + * @param async true to create the service locator in asynchronous + * mode, false otherwise + */ + KServiceLocator(const QString &lang = QString::null, + bool async = true); + + virtual ~KServiceLocator(); + + + /** + * Parses a list of attributes that has been encoded in a string, as + * returned by foundServiceAttributes() and foundAttributes(). + * The format of the encoded attributes is "(name=value), (name=value)". + * Note that some attributes contain lists that must be parsed using + * parseCommaList(), and that all values must be decoded using + * decodeAttributeValue(). + * @param attributes a list of encoded attributes + * @param attributeMap the attributes will be added to this map + */ + static void parseAttributeList(const QString &attributes, + QMap &attributeMap); + + /** + * Decodes the value of an attribute (removes the escape codes). This + * is neccessary even when you parsed an attribute list. + * This function requires the presence of the SLP library, otherwise it + * will return the original value. + * @param value the attribute value to decode + * @return the decoded value. If @p value was QString::null or decoding + * failed, QString::null will be returned + */ + static QString decodeAttributeValue(const QString &value); + + /** + * Parses a comma-separated string of lists, as returned by many signals. + * @param list the comma-separated list + * @return the items as a QStringList + */ + static QStringList parseCommaList(const QString &list); + + /** + * Creates a comma-separated string of lists, as required by many functions. + * @param map the items of this list will be converted + * @return the comma-separated list + */ + static QString createCommaList(const QStringList &values); + + /** + * Escapes a string for use as part of a filter, as described in + * RFC 2254. This will replace all occurrences of special + * characters like paranthesis, backslash and "*", so you can use + * the converted string as part of the query. Never escape the whole + * query because then even the neccessary paranthesis characters + * will be escaped. + * @param str the string to escape + * @return the escaped string + */ + static QString escapeFilter(const QString &str); + + /** + * Returns true if service location is generally possible. + * It will fail if SLP libraries are not installed. + * @return true if service location seems to be possible + */ + bool available(); + + /** + * Use this constant for findServiceTypes()'s namingAuthority argument + * to get only services from the default (IANA) naming authority. + */ + static const QString DEFAULT_AUTHORITY; + + /** + * Use this constant for findServiceTypes()'s namingAuthority argument + * to get all services, + */ + static const QString ALL_AUTHORITIES; + + /** + * Finds all service types in the given scope with the given naming + * authority. This function emits the signal foundServiceTypes() + * each time it discovered one or more service types. When the last + * service type has been found lastServiceTypeSignal() will be emitted. + * When KServiceLocator is in synchronous mode the function will not be + * returned before lastServiceTypeSignal() has been emitted, in + * asynchronous mode lastServiceTypeSignal() can be emitted later. If + * you call this function while another asynchronous operation is + * running it will fail. + * + * @param namingAuthority the naming authorities of the service + * types to be found. If DEFAULT_AUTHORITY + * only IANA service types will be returned, + * if it is ALL_AUTHORITIES or the + * argument has been omitted all service types + * will be returned. + * @param scopelist a comma-separated list of all scopes that will + * be searched, or QString:null to search in all + * scopes + * @return true if the operation was successful + */ + bool findServiceTypes(const QString &namingAuthority = QString::null, + const QString &scopelist = QString::null); + + /** + * Finds all services in the given scope with the given service type. + * Examples for service types are "service:ftp" to find all ftp servers + * or "service:remotedesktop:" to find all remote desktop services. + * You can also specify a filter to match services depending their + * attributes. The filter uses the LDAP Search Filter syntax as + * described in RFC 2254, "String Representation of LDAP Search + * Filters". + * The function emits the signal foundService() each time it + * discovered a service types. When the last service has been found + * lastServiceSignal() will be emitted. When KServiceLocator is in + * synchronous mode the function will not be returned before + * lastServiceSignal() has been emitted, in asynchronous mode + * lastServiceSignal() can be emitted later. If you call this function + * while another asynchronous operation is running it will fail. + * + * @param srvtype the type of the service to search. + * @param filter a filter in LDAP Search Filter syntax, as described + * in RFC 2254. + * @param scopelist a comma-separated list of all scopes that will + * be searched, or QString:null to search in all + * scopes + * @return true if the operation was successful + */ + bool findServices(const QString &srvtype, + const QString &filter = QString::null, + const QString &scopelist = QString::null); + + /** + * Finds the attributes of the service with the given URL. + * The function emits the signal foundAttributes() if the service + * has been found, followed by lastAttributesSignal(). When + * KServiceLocator is in synchronous mode the function will not be + * returned before lastAttributesSignal() has been emitted, in + * asynchronous mode lastAttributesSignal() can be emitted later. If + * you call this function while another asynchronous operation is + * running it will fail. + * + * @param serviceURL the URL of the service to search + * @param attributeIds a comma-separated list of attributes to + * retrieve, or QString::null to retrieve all + * attributes + * @return true if the operation was successful + */ + bool findAttributes(const QString &serviceUrl, + const QString &attributeIds = QString::null); + + /** + * Finds all scopes that can be searched. Always finds at least + * one scope (the default scope). + * The function emits the signal foundScopes() if the service + * has been found. When KServiceLocator is in synchronous mode + * the function will not be returned before foundScopes() has been + * emitted, in asynchronous mode it can be emitted later. If + * you call this function while another asynchronous operation is + * running it will fail. + * + * @return true if the operation was successful + */ + bool findScopes(); + + /** + * If there is a asynchronous operation running, it will aborted. + * It is not guaranteed that you dont receive any signals after + * calling abortOperation. You will always get a lastSignal call + * even after aborting. + */ + void abortOperation(); + + + signals: + /** + * Called by findServiceTypes() each time one or more service + * types have been discovered. + * @param serviceTypes a comma-separated list of service types + */ + void foundServiceTypes(QString serviceTypes); + + /** + * Called by findServices() each time a service has been + * found. + * @param serviceUrl the service url + * @param lifetime the lifetime of the service in seconds + */ + void foundService(QString serviceUrl, + int lifetime); + + /** + * Called by findAttributes() when the service's attributes + * have been found. + * @param serviceUrl the service url + * @param attributes an attribute map (see parseAttributeList() and + * decodeAttributeValue()) + */ + void foundAttributes(QString serviceUrl, + QString attributes); + + /** + * Called by findScopes() when the scopes have been discovered. + * @param scopeList a list of valid scopes, empty if an error + * occurred + */ + void foundScopes(QStringList scopeList); + + + /** + * Emitted when a service type discovery operation finished. If + * you are in async mode it is guaranteed that the operation is + * finished when you receive the signal, so you can start a new + * one. In synced mode the operation is still running, so you + * must wait until the find function returns. + * @param success true if all items have been found, false when + * an error occurred during the operation + */ + void lastServiceTypeSignal(bool success); + + /** + * Emitted when a service discovery operation finished. If you are + * in async mode it is guaranteed that the operation is finished + * when you receive the signal, so you can start a new one. In + * synced mode the operation is still running, so you must wait + * until the find function returns. + * @param success true if all items have been found, false when + * an error occurred during the operation + */ + void lastServiceSignal(bool success); + + /** + * Emitted when a attributes reqyest operation finished. If you are + * in async mode it is guaranteed that the operation is finished + * when you receive the signal, so you can start a new one. In synced + * mode the operation is still running, so you must wait until the + * find function returns. + * @param success true if all items have been found, false when + * an error occurred during the operation + */ + void lastAttributesSignal(bool success); + + protected: + virtual void customEvent(QCustomEvent *); + + private: + void emitFoundServiceTypes(QString serviceTypes); + void emitFoundService(QString serviceUrl, + int lifetime); + void emitFoundAttributes(QString serviceUrl, + QString attributes); + void emitFoundScopes(QStringList scopeList); + void emitLastServiceTypeSignal(bool success); + void emitLastServiceSignal(bool success); + void emitLastAttributesSignal(bool success); +}; + +#endif diff --git a/krdc/main.cpp b/krdc/main.cpp new file mode 100644 index 00000000..d0cd3e4c --- /dev/null +++ b/krdc/main.cpp @@ -0,0 +1,224 @@ +/*************************************************************************** + main.cpp - main control + ------------------- + begin : Thu Dec 20 15:11:42 CET 2001 + copyright : (C) 2001-2003 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../config.h" +#include "main.h" + +// NOTE: I'm not comfortable with the wallet being global data and this high up +// in the heirarchy, but there are 3 reasons for its current placement here: +// 1) There are some important threading issues where it comes to the password +// handling code, and a lot of it is done outside of the objects. +// 2) Different backends need access to the same wallet. so that it is not +// opened multiple times. +// 3) MainController is about the only thing that isn't deleted in between connection +// attempts. +KWallet::Wallet *wallet = 0; + +static const char description[] = I18N_NOOP("Remote desktop connection"); + +static KCmdLineOptions options[] = +{ + { "f", 0, 0 }, + { "fullscreen", I18N_NOOP("Start in fullscreen mode"), 0 }, + { "w", 0, 0 }, + { "window", I18N_NOOP("Start in regular window"), 0 }, + { "l", 0, 0 }, + { "low-quality", I18N_NOOP("Low quality mode (Tight Encoding, 8 bit color)"), 0 }, + { "m", 0, 0 }, + { "medium-quality", I18N_NOOP("Medium quality mode (Tight Encoding, lossy)"), 0 }, + { "h", 0, 0 }, + { "high-quality", I18N_NOOP("High quality mode, default (Hextile Encoding)"), 0 }, + { "s", 0, 0 }, + { "scale", I18N_NOOP("Start VNC in scaled mode"), 0 }, + { "c", 0, 0 }, + { "local-cursor", I18N_NOOP("Show local cursor (VNC only)"), 0 }, + { "e", 0, 0 }, + { "encodings ", I18N_NOOP("Override VNC encoding list (e.g. 'hextile raw')"), 0 }, + { "p", 0, 0 }, + { "password-file ", I18N_NOOP("Provide the password in a file"), 0 }, + { "+[host]", I18N_NOOP("The name of the host, e.g. 'localhost:1'"), 0 }, + KCmdLineLastOption +}; + + +int main(int argc, char *argv[]) +{ + KAboutData aboutData( "krdc", I18N_NOOP("Remote Desktop Connection"), + VERSION, description, KAboutData::License_GPL, + "(c) 2001-2003, Tim Jansen" + "(c) 2002-2003, Arend van Beelen jr." + "(c) 2000-2002, Const Kaplinsky\n" + "(c) 2000, Tridia Corporation\n" + "(c) 1999, AT&T Laboratories Cambridge\n" + "(c) 1999-2003, Matthew Chapman\n", 0, 0, + "tim@tjansen.de"); + aboutData.addAuthor("Tim Jansen",0, "tim@tjansen.de"); + aboutData.addAuthor("Arend van Beelen jr.", + I18N_NOOP("RDP backend"), "arend@auton.nl"); + aboutData.addCredit("AT&T Laboratories Cambridge", + I18N_NOOP("Original VNC viewer and protocol design")); + aboutData.addCredit("Const Kaplinsky", + I18N_NOOP("TightVNC encoding")); + aboutData.addCredit("Tridia Corporation", + I18N_NOOP("ZLib encoding")); + KCmdLineArgs::init( argc, argv, &aboutData ); + KCmdLineArgs::addCmdLineOptions( options ); + + KApplication a; + + QString host = QString::null; + Quality quality = QUALITY_UNKNOWN; + QString encodings = QString::null; + QString password = QString::null; + QString resolution = QString::null; + QString keymap = QString::null; + WindowMode wm = WINDOW_MODE_AUTO; + bool scale = false; + bool localCursor = kapp->config()->readBoolEntry("alwaysShowLocalCursor", false); + QSize initialWindowSize; + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + if (args->isSet("low-quality")) + quality = QUALITY_LOW; + else if (args->isSet("medium-quality")) + quality = QUALITY_MEDIUM; + else if (args->isSet("high-quality")) + quality = QUALITY_HIGH; + + if (args->isSet("fullscreen")) + wm = WINDOW_MODE_FULLSCREEN; + else if (args->isSet("window")) + wm = WINDOW_MODE_NORMAL; + + if (args->isSet("scale")) + scale = true; + + if (args->isSet("local-cursor")) + localCursor = true; + + if (args->isSet("encodings")) + encodings = args->getOption("encodings"); + + if (args->isSet("password-file")) { + QString passwordFile = args->getOption("password-file"); + QFile f(passwordFile); + if (!f.open(IO_ReadOnly)) { + KMessageBox::error(0, i18n("The password file '%1' does not exist.").arg(passwordFile)); + return 1; + } + password = QTextStream(&f).readLine(); + f.close(); + } + + if (args->count() > 0) + host = args->arg(0); + + QString is = a.geometryArgument(); + if (!is.isNull()) { + QRegExp re("([0-9]+)[xX]([0-9]+)"); + if (!re.exactMatch(is)) + args->usage(i18n("Wrong geometry format, must be widthXheight")); + initialWindowSize = QSize(re.cap(1).toInt(), re.cap(2).toInt()); + } + + MainController mc(&a, wm, host, quality, encodings, password, + scale, localCursor, initialWindowSize); + return mc.main(); +} + +MainController::MainController(KApplication *app, WindowMode wm, + const QString &host, + Quality quality, + const QString &encodings, + const QString &password, + bool scale, + bool localCursor, + QSize initialWindowSize) : + m_windowMode(wm), + m_host(host), + m_encodings(encodings), + m_password(password), + m_scale(scale), + m_localCursor(localCursor), + m_initialWindowSize(initialWindowSize), + m_quality(quality), + m_app(app) { +} + +MainController::~MainController() { + if ( wallet ) { + delete wallet; wallet = 0; + } +} + +int MainController::main() { + + if (start()) + return m_app->exec(); + else + return 0; +} + +void MainController::errorRestartRequested() { + QTimer::singleShot(0, this, SLOT(errorRestart())); +} + +bool MainController::start() { + m_krdc = new KRDC(m_windowMode, m_host, + m_quality, m_encodings, m_password, + m_scale, m_localCursor, m_initialWindowSize); + m_app->setMainWidget(m_krdc); + + QObject::connect(m_krdc, SIGNAL(disconnected()), + m_app, SLOT(quit())); + connect(m_krdc, SIGNAL(disconnectedError()), + SLOT(errorRestartRequested())); + + return m_krdc->start(); +} + +void MainController::errorRestart() { + if (!m_host.isEmpty()) + KRDC::setLastHost(m_host); + m_host = QString::null; // only auto-connect once + + // unset KRDC as main widget, to avoid quit on delete + m_app->setMainWidget(0); + + m_krdc = 0; + + if (!start()) + m_app->quit(); +} + +#include "main.moc" diff --git a/krdc/main.h b/krdc/main.h new file mode 100644 index 00000000..d95f079e --- /dev/null +++ b/krdc/main.h @@ -0,0 +1,60 @@ +/*************************************************************************** + main.h - controller object + ------------------- + begin : Sat Jun 15 02:12:00 CET 2002 + copyright : (C) 2002 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef MAIN_H +#define MAIN_H + +#include +#include "krdc.h" +#include "kremoteview.h" +#include "smartptr.h" + +class KApplication; + +class MainController : public QObject { + Q_OBJECT +private: + SmartPtr m_krdc; + WindowMode m_windowMode; + QString m_host, m_encodings, m_password, m_resolution; + bool m_scale; + bool m_localCursor; + QSize m_initialWindowSize; + QString m_keymap; + Quality m_quality; + + KApplication *m_app; + +public: + MainController(KApplication *app, WindowMode wm, + const QString &host, + Quality quality, + const QString &encodings, + const QString &password, + bool scale, + bool localCursor, + QSize initialWindowSize); + ~MainController(); + int main(); + bool start(); + +private slots: + void errorRestartRequested(); + void errorRestart(); +}; + +#endif diff --git a/krdc/maindialog.cpp b/krdc/maindialog.cpp new file mode 100644 index 00000000..61895c65 --- /dev/null +++ b/krdc/maindialog.cpp @@ -0,0 +1,78 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2004 Nadeem Hasan + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "maindialog.h" +#include "maindialogwidget.h" +#include "preferencesdialog.h" + +#include +#include +#include + +MainDialog::MainDialog( QWidget *parent, const char *name ) + : KDialogBase( parent, name, true, i18n( "Remote Desktop Connection" ), + Ok|Close|Help|User1, Ok, true, KGuiItem( i18n( "&Preferences" ), + "configure" ) ) +{ + m_dialogWidget = new MainDialogWidget( this, "m_dialogWidget" ); + setMainWidget( m_dialogWidget ); + + setButtonText( Ok, i18n( "Connect" ) ); + enableButtonOK( false ); + + connect( m_dialogWidget, SIGNAL( hostValid( bool ) ), + SLOT( enableButtonOK( bool ) ) ); +} + +void MainDialog::setRemoteHost( const QString &host ) +{ + m_dialogWidget->setRemoteHost( host ); +} + +QString MainDialog::remoteHost() +{ + return m_dialogWidget->remoteHost(); +} + +void MainDialog::slotHelp() +{ + kapp->invokeHelp(); +} + +void MainDialog::slotUser1() +{ + PreferencesDialog p( this ); + p.exec(); +} + +void MainDialog::slotOk() +{ + m_dialogWidget->save(); + + KDialogBase::accept(); +} + +void MainDialog::slotClose() +{ + m_dialogWidget->save(); + + KDialogBase::reject(); +} + +#include "maindialog.moc" diff --git a/krdc/maindialog.h b/krdc/maindialog.h new file mode 100644 index 00000000..2106e091 --- /dev/null +++ b/krdc/maindialog.h @@ -0,0 +1,48 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2004 Nadeem Hasan + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef MAINDIALOG_H +#define MAINDIALOG_H + +#include + +class MainDialogWidget; + +class MainDialog : public KDialogBase +{ + Q_OBJECT + public: + MainDialog( QWidget *parent, const char *name=0 ); + ~MainDialog() {} + + void setRemoteHost( const QString & ); + QString remoteHost(); + + protected slots: + virtual void slotOk(); + virtual void slotClose(); + virtual void slotUser1(); + virtual void slotHelp(); + + protected: + MainDialogWidget *m_dialogWidget; +}; + +#endif // MAINDIALOG_H + diff --git a/krdc/maindialogbase.ui b/krdc/maindialogbase.ui new file mode 100644 index 00000000..e2aab1fb --- /dev/null +++ b/krdc/maindialogbase.ui @@ -0,0 +1,351 @@ + +MainDialogBase + + + MainDialogBase + + + + 0 + 0 + 544 + 338 + + + + + unnamed + + + 0 + + + + m_serverLabel + + + + 0 + 5 + 0 + 0 + + + + MShape + + + MShadow + + + Remote &desktop: + + + m_serverInput + + + + + m_serverInput + + + + 250 + 0 + + + + 12 + + + false + + + Enter the hostname and display number + + + Enter the name and display number of the computer that you want to connect to, separated by a colon, e.g. 'mycomputer:1'. The address can be any valid Internet address. The display numbers usually start at 0. If you do not have a display number, try 0 or 1. +Remote Desktop Connection only supports systems that use VNC. + + + + + m_browseButton + + + + 0 + 0 + 0 + 0 + + + + &Browse << + + + Turn on/off the network browsing panel. + + + + + kActiveLabel2 + + + + 7 + 0 + 0 + 0 + + + + Enter the address of the computer to connect to, or browse the network and select one. VNC and RDP compatible servers will be supported. <a href="whatsthis:<h3>Examples</h3>for a computer called 'megan':<p><table><tr><td>megan:1</td><td>connect to the VNC server on 'megan' with display number 1</td></tr><tr><td>vnc:/megan:1</td><td>longer form for the same thing</td></tr><tr><td>rdp:/megan</td><td>connect to the RDP server on 'megan'</td></tr></table>">Examples</a> + + + + + m_browsingPanel + + + + 5 + 3 + 0 + 0 + + + + NoFrame + + + Plain + + + + unnamed + + + 0 + + + + m_rescanButton + + + &Rescan + + + Rescans the network. Depending on the network configuration this may take a few seconds until all systems have responded. + + + + + Spacer3 + + + Horizontal + + + Expanding + + + + 34 + 16 + + + + + + TextLabel1_2 + + + &Search: + + + m_searchInput + + + + + m_searchInput + + + Enter a search term + + + Enter a search term here if you want to search for a specific system, then press Enter or click Rescan. All systems, whose description matches the search term, will be displayed. The search is not case sensitive. If you leave the field empty all systems will be displayed. + + + + + Spacer4 + + + Horizontal + + + Expanding + + + + 34 + 16 + + + + + + TextLabel1 + + + + 0 + 5 + 0 + 0 + + + + Scop&e: + + + m_scopeCombo + + + + + m_scopeCombo + + + + 0 + 0 + 0 + 0 + + + + + 100 + 0 + + + + An administrator can configure the network to have several scopes. If this is the case, you can select the scope to scan here. + + + + + + Name + + + true + + + true + + + + + Type + + + true + + + true + + + + + Address + + + true + + + true + + + + + Protocol + + + true + + + true + + + + m_browsingView + + + true + + + false + + + AllColumns + + + Here you can see the systems on the network that allow you to connect. Note that an administrator can hide systems, so the list is not always complete. Click on an item to select it, double-click it to connect immediately. + + + + + + + + + + + m_browseButton + clicked() + MainDialogBase + toggleBrowsingArea() + + + m_rescanButton + clicked() + MainDialogBase + rescan() + + + m_serverInput + textChanged(const QString&) + MainDialogBase + hostChanged(const QString&) + + + m_searchInput + returnPressed() + MainDialogBase + rescan() + + + + kdialog.h + + + hostChanged( const QString & text ) + toggleBrowsingArea() + itemSelected( QListViewItem * item ) + itemDoubleClicked( QListViewItem * item ) + scopeSelected( const QString & scope ) + rescan() + + + enableBrowsingArea( bool enable ) + + + + + kcombobox.h + + diff --git a/krdc/maindialogwidget.cpp b/krdc/maindialogwidget.cpp new file mode 100644 index 00000000..8bc86c39 --- /dev/null +++ b/krdc/maindialogwidget.cpp @@ -0,0 +1,359 @@ +/* This file is part of the KDE project + Copyright (C) 2002-2003 Tim Jansen + Copyright (C) 2003-2004 Nadeem Hasan + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or ( at your option ) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "hostpreferences.h" +#include "maindialogwidget.h" + +static const QString DEFAULT_SCOPE = "default"; + +class UrlListViewItem : public KListViewItem +{ + public: + UrlListViewItem( QListView *v, const QString &url, const QString &host, + const QString &protocol, const QString &type, const QString &userid, + const QString &fullname, const QString &desc, + const QString &serviceid ) + : KListViewItem( v, host, i18n( "unknown" ), host, protocol ), + m_url( url ), m_serviceid( serviceid ) + { + if ( !type.isNull() ) + { + //User connects to somebody else's desktop, used for krfb + if ( type.lower() == "shared" ) + setText( 1, i18n( "Shared Desktop" ) ); + //User connects to desktop that exists only on the network + else if ( type.lower() == "private" ) + setText( 1, i18n( "Standalone Desktop" ) ); + } + if ( !desc.isNull() ) + setText( 0, desc ); + if ( ( !userid.isEmpty() ) && ( !fullname.isEmpty() ) ) + setText( 0, QString( "%1 (%2)" ).arg( fullname ).arg( userid ) ); + else if ( !userid.isNull() ) + setText( 0, userid ); + else if ( !fullname.isNull() ) + setText( 0, fullname ); + } + + QString url() + { + return m_url; + } + const QString& serviceid() const + { + return m_serviceid; + } + + protected: + QString m_url; + QString m_serviceid; +}; + +MainDialogWidget::MainDialogWidget( QWidget *parent, const char *name ) + : MainDialogBase( parent, name ), + m_scanning( false ) +{ + HostPreferences *hp = HostPreferences::instance(); + QStringList list; + + list = hp->serverCompletions(); + m_serverInput->completionObject()->setItems( list ); + list = hp->serverHistory(); + m_serverInput->setHistoryItems( list ); + + m_searchInput->setTrapReturnKey( true ); + + connect( m_browsingView, + SIGNAL( selectionChanged( QListViewItem * ) ), + SLOT( itemSelected( QListViewItem * ) ) ); + connect( m_browsingView, + SIGNAL( doubleClicked( QListViewItem *, const QPoint &, int ) ), + SLOT( itemDoubleClicked( QListViewItem * ) ) ); + connect( m_scopeCombo, + SIGNAL( activated( const QString & ) ), + SLOT( scopeSelected( const QString & ) ) ); + connect( m_serverInput, + SIGNAL( returnPressed( const QString & ) ), + SLOT( rescan() ) ); + + bool showBrowse = hp->showBrowsingPanel(); + enableBrowsingArea( showBrowse ); + + m_locator_dnssd = new DNSSD::ServiceBrowser(QStringList::split(',',"_rfb._tcp,_rdp._tcp"),0,DNSSD::ServiceBrowser::AutoResolve); + connect(m_locator_dnssd,SIGNAL(serviceAdded(DNSSD::RemoteService::Ptr)), + SLOT(addedService(DNSSD::RemoteService::Ptr))); + connect(m_locator_dnssd,SIGNAL(serviceRemoved(DNSSD::RemoteService::Ptr)), + SLOT(removedService(DNSSD::RemoteService::Ptr))); + m_locator_dnssd->startBrowse(); + + adjustSize(); +} + +MainDialogWidget::~MainDialogWidget() +{ + delete m_locator_dnssd; +} + +void MainDialogWidget::save() +{ + HostPreferences *hp = HostPreferences::instance(); + QStringList list; + + m_serverInput->addToHistory( m_serverInput->currentText() ); + list = m_serverInput->completionObject()->items(); + hp->setServerCompletions( list ); + list = m_serverInput->historyItems(); + hp->setServerHistory( list ); + + hp->setShowBrowsingPanel( m_browsingPanel->isVisible() ); +} + +void MainDialogWidget::setRemoteHost( const QString &host ) +{ + m_serverInput->setEditText( host ); +} + +QString MainDialogWidget::remoteHost() +{ + return m_serverInput->currentText(); +} + +void MainDialogWidget::hostChanged( const QString &text ) +{ + emit hostValid(text.contains(QRegExp(":[0-9]+$")) || + text.contains(QRegExp("^vnc:/.+")) || + text.contains(QRegExp("^rdp:/.+"))); +} + +void MainDialogWidget::toggleBrowsingArea() +{ + enableBrowsingArea(!m_browsingPanel->isVisible()); +} + +void MainDialogWidget::enableBrowsingArea( bool enable ) +{ + int hOffset = 0; + if (enable) + { + m_browsingPanel->show(); + m_browsingPanel->setMaximumSize(1000, 1000); + m_browsingPanel->setEnabled(true); + m_browseButton->setText(m_browseButton->text().replace(">>", "<<")); + } + else + { + hOffset = m_browsingPanel->height(); + m_browsingPanel->hide(); + m_browsingPanel->setMaximumSize(0, 0); + m_browsingPanel->setEnabled(false); + m_browseButton->setText(m_browseButton->text().replace("<<", ">>")); + int h = minimumSize().height()-hOffset; + setMinimumSize(minimumSize().width(), (h > 0) ? h : 0); + resize(width(), height()-hOffset); + + QTimer::singleShot( 0, parentWidget(), SLOT( adjustSize() ) ); + } + + if (enable) + rescan(); +} + +void MainDialogWidget::itemSelected( QListViewItem *item ) +{ + UrlListViewItem *u = ( UrlListViewItem* ) item; + QRegExp rx( "^service:remotedesktop\\.kde:([^;]*)" ); + if ( rx.search( u->url() ) < 0 ) + m_serverInput->setCurrentText( u->url()); + else m_serverInput->setCurrentText( rx.cap( 1 ) ); +} + +void MainDialogWidget::itemDoubleClicked( QListViewItem *item ) +{ + itemSelected( item ); + emit accept(); +} + +void MainDialogWidget::scopeSelected( const QString &scope ) +{ + QString s = scope; + if ( s == i18n( "default" ) ) + s = DEFAULT_SCOPE; + + if ( m_scope == s ) + return; + m_scope = s; + rescan(); +} + +void MainDialogWidget::rescan() +{ + QStringList scopeList; + + if ( m_scanning ) + return; + m_scanning = true; + m_rescanButton->setEnabled( false ); + m_scopeCombo->setEnabled( false ); + if ( !ensureLocatorOpen() ) + return; + + m_browsingView->clear(); + + QString filter; + if ( !m_searchInput->text().stripWhiteSpace().isEmpty() ) { + QString ef = KServiceLocator::escapeFilter( + m_searchInput->text().stripWhiteSpace() ); + filter = "(|(|(description=*"+ef+"*)(username=*"+ef+"*))(fullname=*"+ef+"*))"; + } + + if ( !m_locator->findServices( "service:remotedesktop.kde", + filter, m_scope ) ) { + kdWarning() << "Failure in findServices()" << endl; + errorScanning(); + return; + } +} + +bool MainDialogWidget::ensureLocatorOpen() +{ + if ( m_locator ) + return true; + + m_locator = new KServiceLocator(); + + if ( !m_locator->available() ) { +#ifdef HAVE_SLP + KMessageBox::error( 0, + i18n( "Browsing the network is not possible. You probably " + "did not install SLP support correctly." ), + i18n( "Browsing Not Possible" ), false ); +#endif + return false; + } + + connect( m_locator, SIGNAL( foundService( QString,int ) ), + SLOT( foundService( QString,int ) ) ); + connect( m_locator, SIGNAL( lastServiceSignal( bool ) ), + SLOT( lastSignalServices( bool ) ) ); + connect( m_locator, SIGNAL( foundScopes( QStringList ) ), + SLOT( foundScopes( QStringList ) ) ); + return true; +} + +void MainDialogWidget::errorScanning() +{ + KMessageBox::error( 0, + i18n( "An error occurred while scanning the network." ), + i18n( "Error While Scanning" ), false ); + finishScanning(); +} + +void MainDialogWidget::finishScanning() +{ + m_rescanButton->setEnabled( true ); + m_scopeCombo->setEnabled( true ); + m_scanning = false; +} + +void MainDialogWidget::foundService( QString url, int ) +{ + QRegExp rx( "^service:remotedesktop\\.kde:(\\w+)://([^;]+);(.*)$" ); + + if ( rx.search( url ) < 0 ) + { + rx = QRegExp( "^service:remotedesktop\\.kde:(\\w+)://(.*)$" ); + if ( rx.search( url ) < 0 ) + return; + } + + QMap map; + KServiceLocator::parseAttributeList( rx.cap( 3 ), map ); + + new UrlListViewItem( m_browsingView, url, rx.cap( 2 ), rx.cap( 1 ), + KServiceLocator::decodeAttributeValue( map[ "type" ] ), + KServiceLocator::decodeAttributeValue( map[ "username" ] ), + KServiceLocator::decodeAttributeValue( map[ "fullname" ] ), + KServiceLocator::decodeAttributeValue( map[ "description" ] ), + KServiceLocator::decodeAttributeValue( map[ "serviceid" ] ) ); +} + +void MainDialogWidget::addedService( DNSSD::RemoteService::Ptr service ) +{ +QString type = service->type().mid(1,3); +if (type == "rfb") type = "vnc"; +QString url = type+"://"+service->hostName()+":"+QString::number(service->port()); +new UrlListViewItem( m_browsingView, url, service->serviceName(), + type.upper(),service->textData()["type"], + service->textData()["u"],service->textData()["fullname"], + service->textData()["description"],service->serviceName()+service->domain()); +} + +void MainDialogWidget::removedService( DNSSD::RemoteService::Ptr service ) +{ + QListViewItemIterator it( m_browsingView ); + while ( it.current() ) { + if ( ((UrlListViewItem*)it.current())->serviceid() == service->serviceName()+service->domain() ) + delete it.current(); + else ++it; + } +} + + +void MainDialogWidget::lastSignalServices( bool success ) +{ + if ( !success ) + { + errorScanning(); + return; + } + + if ( !m_locator->findScopes() ) + { + kdWarning() << "Failure in findScopes()" << endl; + errorScanning(); + } +} + +void MainDialogWidget::foundScopes( QStringList scopeList ) +{ + int di = scopeList.findIndex( DEFAULT_SCOPE ); + if ( di >= 0 ) + scopeList[ di ] = i18n( "default" ); + + int ct = scopeList.findIndex( m_scopeCombo->currentText() ); + m_scopeCombo->clear(); + m_scopeCombo->insertStringList( scopeList ); + if ( ct >= 0 ) + m_scopeCombo->setCurrentItem( ct ); + finishScanning(); +} + +#include "maindialogwidget.moc" diff --git a/krdc/maindialogwidget.h b/krdc/maindialogwidget.h new file mode 100644 index 00000000..ec8acb01 --- /dev/null +++ b/krdc/maindialogwidget.h @@ -0,0 +1,74 @@ +/* This file is part of the KDE project + Copyright (C) 2002-2003 Tim Jansen + Copyright (C) 2003-2004 Nadeem Hasan + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or ( at your option ) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef MAINDIALOGWIDGET_H +#define MAINDIALOGWIDGET_H + +#include "kservicelocator.h" +#include "maindialogbase.h" +#include +#include +#include "smartptr.h" + +class MainDialogWidget : public MainDialogBase +{ + Q_OBJECT + + public: + MainDialogWidget( QWidget *parent, const char *name ); + ~MainDialogWidget(); + + void setRemoteHost( const QString & ); + QString remoteHost(); + void save(); + + protected: + void enableBrowsingArea( bool ); + bool ensureLocatorOpen(); + void errorScanning(); + void finishScanning(); + + signals: + void hostValid( bool b ); + void accept(); + + protected slots: + void hostChanged( const QString & ); + void toggleBrowsingArea(); + void itemSelected( QListViewItem * ); + void itemDoubleClicked( QListViewItem * ); + void scopeSelected( const QString & ); + void rescan(); + + void foundService( QString url, int ); + void lastSignalServices( bool success ); + void foundScopes( QStringList scopeList ); + void addedService( DNSSD::RemoteService::Ptr ); + void removedService( DNSSD::RemoteService::Ptr ); + + + protected: + QString m_scope; + bool m_scanning; + SmartPtr m_locator; + DNSSD::ServiceBrowser *m_locator_dnssd; +}; + +#endif // MAINDIALOGWIDGET_H diff --git a/krdc/pindown.png b/krdc/pindown.png new file mode 100644 index 00000000..20be0650 Binary files /dev/null and b/krdc/pindown.png differ diff --git a/krdc/pinup.png b/krdc/pinup.png new file mode 100644 index 00000000..e2256b27 Binary files /dev/null and b/krdc/pinup.png differ diff --git a/krdc/pointcursor.png b/krdc/pointcursor.png new file mode 100644 index 00000000..f0382ab3 Binary files /dev/null and b/krdc/pointcursor.png differ diff --git a/krdc/pointcursormask.png b/krdc/pointcursormask.png new file mode 100644 index 00000000..69306d08 Binary files /dev/null and b/krdc/pointcursormask.png differ diff --git a/krdc/preferencesdialog.cpp b/krdc/preferencesdialog.cpp new file mode 100644 index 00000000..bb1b9c97 --- /dev/null +++ b/krdc/preferencesdialog.cpp @@ -0,0 +1,128 @@ +/* This file is part of the KDE project + Copyright (C) 2002-2003 Nadeem Hasan + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "preferencesdialog.h" + +#include "hostprofiles.h" +#include "vnc/vncprefs.h" +#include "rdp/rdpprefs.h" + +#include "rdp/krdpview.h" +#include "vnc/kvncview.h" + +#include +#include + +#include +#include + +PreferencesDialog::PreferencesDialog( QWidget *parent, const char *name ) + : KDialogBase( Tabbed, i18n( "Preferences" ), Ok|Cancel, Ok, + parent, name, true ) +{ + QVBox *page; + QWidget *spacer; + + page = addVBoxPage( i18n( "&Host Profiles" ) ); + m_hostProfiles = new HostProfiles( page, "m_hostProfiles" ); + + connect( m_hostProfiles, SIGNAL( hostDoubleClicked(HostPrefPtr) ), this, SLOT( slotHostDoubleClicked(HostPrefPtr) )); + + page = addVBoxPage( i18n( "&VNC Defaults" ) ); + m_vncPrefs = new VncPrefs( page, "m_vncPrefs" ); + spacer = new QWidget( page ); + page->setStretchFactor( spacer, 10 ); + + m_vncPrefs->cbShowPrefs->setText( i18n( "Do not &show the preferences " + "dialog on new connections" ) ); + + page = addVBoxPage( i18n( "RD&P Defaults" ) ); + m_rdpPrefs = new RdpPrefs( page, "m_rdpPrefs" ); + spacer = new QWidget( page ); + page->setStretchFactor( spacer, 10 ); + + m_rdpPrefs->cbShowPrefs->setText( i18n( "Do not &show the preferences " + "dialog on new connections" ) ); + + HostPreferences *hp = HostPreferences::instance(); + m_vncDefaults = SmartPtr( hp->vncDefaults() ); + m_rdpDefaults = SmartPtr( hp->rdpDefaults() ); + + load(); +} + +void PreferencesDialog::load() +{ + m_hostProfiles->load(); + + m_vncPrefs->setQuality( m_vncDefaults->quality() ); + m_vncPrefs->setShowPrefs( m_vncDefaults->askOnConnect() ); + m_vncPrefs->setUseKWallet( m_vncDefaults->useKWallet() ); + + m_rdpPrefs->setRdpWidth( m_rdpDefaults->width() ); + m_rdpPrefs->setRdpHeight( m_rdpDefaults->height() ); + m_rdpPrefs->setShowPrefs( m_rdpDefaults->askOnConnect() ); + m_rdpPrefs->setUseKWallet( m_rdpDefaults->useKWallet() ); + m_rdpPrefs->setColorDepth( m_rdpDefaults->colorDepth() ); + m_rdpPrefs->setKbLayout( keymap2int( m_rdpDefaults->layout() )); + m_rdpPrefs->setResolution(); +} + +void PreferencesDialog::save() +{ + m_hostProfiles->save(); + + m_vncDefaults->setQuality( m_vncPrefs->quality() ); + m_vncDefaults->setAskOnConnect( m_vncPrefs->showPrefs() ); + m_vncDefaults->setUseKWallet( m_vncPrefs->useKWallet() ); + + m_rdpDefaults->setWidth( m_rdpPrefs->rdpWidth() ); + m_rdpDefaults->setHeight( m_rdpPrefs->rdpHeight() ); + m_rdpDefaults->setLayout( int2keymap( m_rdpPrefs->kbLayout() )); + m_rdpDefaults->setAskOnConnect( m_rdpPrefs->showPrefs() ); + m_rdpDefaults->setUseKWallet( m_rdpPrefs->useKWallet() ); + m_rdpDefaults->setColorDepth( m_rdpPrefs->colorDepth() ); + + HostPreferences *hp = HostPreferences::instance(); + hp->sync(); +} + +void PreferencesDialog::slotOk() +{ + save(); + accept(); +} + +void PreferencesDialog::slotHostDoubleClicked( HostPrefPtr hp ) +{ + bool hostChanged = false; + + if( hp->type() == RdpHostPref::RdpType ) + hostChanged = KRdpView::editPreferences( hp ); + else if( hp->type() == VncHostPref::VncType ) + hostChanged = KVncView::editPreferences( hp ); + + if( hostChanged ) + { + m_hostProfiles->hostListView->clear(); + m_hostProfiles->load(); + } +} + +#include "preferencesdialog.moc" diff --git a/krdc/preferencesdialog.h b/krdc/preferencesdialog.h new file mode 100644 index 00000000..e6d3ea8b --- /dev/null +++ b/krdc/preferencesdialog.h @@ -0,0 +1,57 @@ +/* This file is part of the KDE project + Copyright (C) 2002-2003 Nadeem Hasan + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef PREFERENCESDIALOG_H +#define PREFERENCESDIALOG_H + +#include + +#include "smartptr.h" +#include "vnc/vnchostpref.h" +#include "rdp/rdphostpref.h" + +class HostProfiles; +class VncPrefs; +class RdpPrefs; + +class PreferencesDialog : public KDialogBase +{ + Q_OBJECT + + public: + PreferencesDialog( QWidget *parent, const char *name=0 ); + ~PreferencesDialog() {}; + + protected slots: + void slotOk(); + void slotHostDoubleClicked( HostPrefPtr ); + + protected: + void load(); + void save(); + + HostProfiles *m_hostProfiles; + VncPrefs *m_vncPrefs; + RdpPrefs *m_rdpPrefs; + SmartPtr m_vncDefaults; + SmartPtr m_rdpDefaults; +}; + +#endif // PREFERENCESDIALOG_H + diff --git a/krdc/rdp.protocol b/krdc/rdp.protocol new file mode 100644 index 00000000..20a3c63e --- /dev/null +++ b/krdc/rdp.protocol @@ -0,0 +1,12 @@ +[Protocol] +exec=krdc '%u' +protocol=rdp +input=none +output=none +helper=true +listing=false +reading=false +writing=false +makedir=false +deleting=false +Icon=krdc diff --git a/krdc/rdp/Makefile.am b/krdc/rdp/Makefile.am new file mode 100644 index 00000000..51f35e89 --- /dev/null +++ b/krdc/rdp/Makefile.am @@ -0,0 +1,11 @@ +INCLUDES=-I$(top_srcdir)/krdc $(all_includes) + +METASOURCES = AUTO + +noinst_LTLIBRARIES = librdp.la + +librdp_la_SOURCES = krdpview.cpp rdphostpref.cpp rdpprefs.ui + +noinst_HEADERS = krdpview.h rdphostpref.h +librdp_la_LIBADD = $(LIB_KDEUI) $(LIBJPEG) +librdp_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) diff --git a/krdc/rdp/README.patch b/krdc/rdp/README.patch new file mode 100644 index 00000000..a3e705d8 --- /dev/null +++ b/krdc/rdp/README.patch @@ -0,0 +1,12 @@ +Read this for how to use the rdesktop patch: + +This patch is intended for rdesktop 1.3.1 only. It has not been tested +with other versions, and future versions should (hopefully) include it +by default. + +- Get a copy of the rdesktop 1.3.1 sources and unpack it. +- Place the rdesktop.patch file in the top directory of the unpacked + sources. +- Go to the directory where you placed the patch and type the following + command: patch -p 0 -i rdesktop.patch +- You can now compile the sources as usual. diff --git a/krdc/rdp/krdpview.cpp b/krdc/rdp/krdpview.cpp new file mode 100644 index 00000000..dc388e29 --- /dev/null +++ b/krdc/rdp/krdpview.cpp @@ -0,0 +1,369 @@ +/* + krdpview.h, implementation of the KRdpView class + Copyright (C) 2002 Arend van Beelen jr. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For any questions, comments or whatever, you may mail me at: arend@auton.nl +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#undef Bool + +#include "krdpview.h" +#include "rdphostpref.h" +#include "rdpprefs.h" + +bool rdpAppDataConfigured = false; +extern KWallet::Wallet *wallet; + +static KRdpView *krdpview; + +RdpContainer::RdpContainer(QWidget *parent, const char *name, WFlags f) : + QXEmbed(parent, name, f), + m_viewOnly(false) +{ +} + +RdpContainer::~RdpContainer() +{ +} + +void RdpContainer::windowChanged(WId window) +{ + if(window == 0) + { + emit embeddedWindowDestroyed(); + } + else + { + emit newEmbeddedWindow(window); + } +} + +bool RdpContainer::x11Event(XEvent *e) +{ + // FIXME: mouse events still get through in view-only + if(m_viewOnly && (e->type == KeyPress || e->type == KeyRelease || e->type == ButtonPress || + e->type == ButtonRelease || e->type == MotionNotify || e->type == FocusIn || + e->type == FocusOut || e->type == EnterNotify || e->type == LeaveNotify)) + { + return true; + } + + return QXEmbed::x11Event(e); +} + + +// constructor +KRdpView::KRdpView(QWidget *parent, const char *name, + const QString &host, int port, + const QString &user, const QString &password, + int flags, const QString &domain, + const QString &shell, const QString &directory) : + KRemoteView(parent, name, Qt::WResizeNoErase | Qt::WRepaintNoErase | Qt::WStaticContents), + m_name(name), + m_host(host), + m_port(port), + m_user(user), + m_password(password), + m_flags(flags), + m_domain(domain), + m_shell(shell), + m_directory(directory), + m_quitFlag(false), + m_process(NULL) +{ + krdpview = this; + setFixedSize(16, 16); + if(m_port == 0) + { + m_port = TCP_PORT_RDP; + } + + m_container = new RdpContainer(this); +} + +// destructor +KRdpView::~KRdpView() +{ + startQuitting(); + delete m_container; +} + +// returns the size of the framebuffer +QSize KRdpView::framebufferSize() +{ + return m_container->sizeHint(); +} + +// returns the suggested size +QSize KRdpView::sizeHint() +{ + return maximumSize(); +} + +// start closing the connection +void KRdpView::startQuitting() +{ + m_quitFlag = true; + if(m_process != NULL) + { + m_container->sendDelete(); + } +} + +// are we currently closing the connection? +bool KRdpView::isQuitting() +{ + return m_quitFlag; +} + +// return the host we're connected to +QString KRdpView::host() +{ + return m_host; +} + +// return the port number we're connected on +int KRdpView::port() +{ + return m_port; +} + +bool KRdpView::editPreferences( HostPrefPtr host ) +{ + SmartPtr hp( host ); + + int wv = hp->width(); + int hv = hp->height(); + int cd = hp->colorDepth(); + QString kl = hp->layout(); + bool kwallet = hp->useKWallet(); + + // show preferences dialog + KDialogBase *dlg = new KDialogBase( 0L, "dlg", true, + i18n( "RDP Host Preferences for %1" ).arg( host->host() ), + KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true ); + + QVBox *vbox = dlg->makeVBoxMainWidget(); + RdpPrefs *prefs = new RdpPrefs( vbox ); + QWidget *spacer = new QWidget( vbox ); + vbox->setStretchFactor( spacer, 10 ); + + prefs->setRdpWidth( wv ); + prefs->setRdpHeight( hv ); + prefs->setResolution(); + prefs->setColorDepth(cd); + prefs->setKbLayout( keymap2int( kl ) ); + prefs->setShowPrefs( true ); + prefs->setUseKWallet(kwallet); + + if ( dlg->exec() == QDialog::Rejected ) + return false; + + wv = prefs->rdpWidth(); + hv = prefs->rdpHeight(); + kl = int2keymap( prefs->kbLayout() ); + hp->setAskOnConnect( prefs->showPrefs() ); + hp->setWidth(wv); + hp->setHeight(hv); + hp->setColorDepth( prefs->colorDepth() ); + hp->setLayout(kl); + hp->setUseKWallet(prefs->useKWallet()); + + delete dlg; + return true; +} + +// open a connection +bool KRdpView::start() +{ + SmartPtr hp, rdpDefaults; + bool useKWallet = false; + + if(!rdpAppDataConfigured) + { + HostPreferences *hps = HostPreferences::instance(); + hp = SmartPtr(hps->createHostPref(m_host, + RdpHostPref::RdpType)); + if(hp->askOnConnect()) + { + if( !editPreferences( hp )) + return false; + hps->sync(); + } + + useKWallet = hp->useKWallet(); + } + + m_container->show(); + + m_process = new KProcess(m_container); + *m_process << "rdesktop"; + *m_process << "-g" << (QString::number(hp->width()) + "x" + QString::number(hp->height())); + *m_process << "-k" << hp->layout(); + if(!m_user.isEmpty()) { *m_process << "-u" << m_user; } + + if(m_password.isEmpty() && useKWallet ) { + QString krdc_folder = "KRDC-RDP"; + + // Bugfix: Check if wallet has been closed by an outside source + if ( wallet && !wallet->isOpen() ) { + delete wallet; wallet=0; + } + + // Do we need to open the wallet? + if ( !wallet ) { + QString walletName = KWallet::Wallet::NetworkWallet(); + wallet = KWallet::Wallet::openWallet(walletName); + } + + if (wallet && wallet->isOpen()) { + bool walletOK = wallet->hasFolder(krdc_folder); + if (walletOK == false) { + walletOK = wallet->createFolder(krdc_folder); + } + + if (walletOK == true) { + wallet->setFolder(krdc_folder); + if ( wallet->hasEntry(m_host) ) { + wallet->readPassword(m_host, m_password); + } + } + + if ( m_password.isEmpty() ) { + //There must not be an existing entry. Let's make one. + QCString newPassword; + if (KPasswordDialog::getPassword(newPassword, i18n("Please enter the password.")) == KPasswordDialog::Accepted) { + m_password = newPassword; + wallet->writePassword(m_host, m_password); + } + } + } + } + + if(!m_password.isEmpty()) { + *m_process << "-p" << m_password; + } + + *m_process << "-X" << ("0x" + QString::number(m_container->winId(), 16)); + *m_process << "-a" << QString::number(hp->colorDepth()); + *m_process << (m_host + ":" + QString::number(m_port)); + connect(m_process, SIGNAL(processExited(KProcess *)), SLOT(processDied(KProcess *))); + connect(m_process, SIGNAL(receivedStderr(KProcess *, char *, int)), SLOT(receivedStderr(KProcess *, char *, int))); + connect(m_container, SIGNAL(embeddedWindowDestroyed()), SLOT(connectionClosed())); + connect(m_container, SIGNAL(newEmbeddedWindow(WId)), SLOT(connectionOpened(WId))); + qDebug("Color depth: %d", hp->colorDepth()); + if(!m_process->start(KProcess::NotifyOnExit, KProcess::Stderr)) + { + KMessageBox::error(0, i18n("Could not start rdesktop; make sure rdesktop is properly installed."), + i18n("rdesktop Failure")); + return false; + } + + setStatus(REMOTE_VIEW_CONNECTING); + + return true; +} + +void KRdpView::switchFullscreen(bool on) +{ + if(on == true) + { + m_container->grabKeyboard(); + } +} + +// captures pressed keys +void KRdpView::pressKey(XEvent *e) +{ + m_container->x11Event(e); + m_container->grabKeyboard(); +} + +bool KRdpView::viewOnly() +{ + return m_container->m_viewOnly; +} + +void KRdpView::setViewOnly(bool s) +{ + m_container->m_viewOnly = s; +} + +void KRdpView::connectionOpened(WId /*window*/) +{ + QSize size = m_container->sizeHint(); + + setStatus(REMOTE_VIEW_CONNECTED); + setFixedSize(size); + m_container->setFixedSize(size); + emit changeSize(size.width(), size.height()); + emit connected(); + setFocus(); +} + +void KRdpView::connectionClosed() +{ + emit disconnected(); + setStatus(REMOTE_VIEW_DISCONNECTED); + m_quitFlag = true; +} + +void KRdpView::processDied(KProcess */*proc*/) +{ + if(m_status == REMOTE_VIEW_CONNECTING) + { + setStatus(REMOTE_VIEW_DISCONNECTED); + if(m_clientVersion.isEmpty()) + { + KMessageBox::error(0, i18n("Connection attempt to host failed."), + i18n("Connection Failure")); + } + emit disconnectedError(); + } +} + +void KRdpView::receivedStderr(KProcess */*proc*/, char *buffer, int /*buflen*/) +{ + QString output(buffer); + QString line; + int i = 0; + while(!(line = output.section('\n', i, i)).isEmpty()) + { + if(line.startsWith("Version ")) + { + m_clientVersion = line.section(' ', 1, 1); + m_clientVersion = m_clientVersion.left(m_clientVersion.length() - 1); + return; + } + i++; + } +} + +#include "krdpview.moc" diff --git a/krdc/rdp/krdpview.h b/krdc/rdp/krdpview.h new file mode 100644 index 00000000..a54c2d96 --- /dev/null +++ b/krdc/rdp/krdpview.h @@ -0,0 +1,113 @@ +/* + krdpview.h, declaration of the KRdpView class + Copyright (C) 2002 Arend van Beelen jr. + + This program is free software; you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if + not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + For any questions, comments or whatever, you may mail me at: arend@auton.nl +*/ + +#ifndef KRDPVIEW_H +#define KRDPVIEW_H + +#include + +#include "hostpreferences.h" +#include "kremoteview.h" + +#define TCP_PORT_RDP 3389 +#define RDP_LOGON_NORMAL 0x33 + +class KProcess; +class KRdpView; + +class RdpContainer : public QXEmbed +{ + Q_OBJECT + + friend class KRdpView; + + public: + RdpContainer(QWidget *parent = 0, const char *name = 0, WFlags f = 0); + ~RdpContainer(); + + signals: + void newEmbeddedWindow(WId window); + + protected: + virtual void windowChanged(WId window); + virtual bool x11Event(XEvent *e); + + private: + bool m_viewOnly; // if set: ignore all input +}; + +class KRdpView : public KRemoteView +{ + Q_OBJECT + + public: + // constructor and destructor + KRdpView(QWidget *parent = 0, const char *name = 0, + const QString &host = QString::null, int port = TCP_PORT_RDP, + const QString &user = QString::null, const QString &password = QString::null, + int flags = RDP_LOGON_NORMAL, const QString &domain = QString::null, + const QString &shell = QString::null, const QString &directory = QString::null); + virtual ~KRdpView(); + + // functions regarding the window + virtual QSize framebufferSize(); // returns the size of the remote view + QSize sizeHint(); // returns the suggested size + virtual bool viewOnly(); + + // functions regarding the connection + virtual void startQuitting(); // start closing the connection + virtual bool isQuitting(); // are we currently closing the connection? + virtual QString host(); // return the host we're connected to + virtual int port(); // return the port number we're connected on + virtual bool start(); // open a connection + + static bool editPreferences( HostPrefPtr ); + + public slots: + virtual void switchFullscreen(bool on); + virtual void pressKey(XEvent *k); // send a generated key to the server + virtual void setViewOnly(bool s); + + private: + // properties used for setting up the connection + QString m_name; // name of the connection + QString m_host; // the host to connect to + int m_port; // the port on the host + QString m_user; // the user to use to log in + QString m_password; // the password to use + int m_flags; // flags which determine how the connection is set up + QString m_domain; // the domain where the host is on + QString m_shell; // the shell to use + QString m_directory; // the working directory on the server + + // other properties + bool m_quitFlag; // if set: die + QString m_clientVersion; // version number returned by rdesktop + RdpContainer *m_container; // container for the rdesktop window + KProcess *m_process; // rdesktop process + + private slots: + void connectionOpened(WId window); // called if rdesktop started + void connectionClosed(); // called if rdesktop quits + void processDied(KProcess *); // called if rdesktop dies + void receivedStderr(KProcess *proc, char *buffer, int buflen); + // catches rdesktop debug output +}; + +#endif diff --git a/krdc/rdp/rdesktop.patch b/krdc/rdp/rdesktop.patch new file mode 100644 index 00000000..a027799c --- /dev/null +++ b/krdc/rdp/rdesktop.patch @@ -0,0 +1,76 @@ +--- rdesktop.c.orig 2004-03-08 18:02:58.000000000 +0100 ++++ rdesktop.c 2004-03-09 22:32:19.000000000 +0100 +@@ -27,6 +27,7 @@ + #include /* gettimeofday */ + #include /* times */ + #include ++#include /* Window */ + #include "rdesktop.h" + + #ifdef EGD_SOCKET +@@ -68,6 +69,7 @@ + BOOL g_console_session = False; + BOOL g_numlock_sync = False; + extern BOOL g_owncolmap; ++extern Window g_embed_wnd; + + #ifdef WITH_RDPSND + BOOL g_rdpsnd = False; +@@ -112,6 +114,7 @@ + fprintf(stderr, " -S: caption button size (single application mode)\n"); + fprintf(stderr, " -T: window title\n"); + fprintf(stderr, " -N: enable numlock synchronisation\n"); ++ fprintf(stderr, " -X: embed into another window with a given id.\n"); + fprintf(stderr, " -a: connection colour depth\n"); + fprintf(stderr, " -r: enable specified device redirection (currently: sound)\n"); + fprintf(stderr, " -0: attach to console\n"); +@@ -224,6 +227,7 @@ + prompt_password = False; + domain[0] = password[0] = shell[0] = directory[0] = 0; + strcpy(keymapname, "en-us"); ++ g_embed_wnd = 0; + + #ifdef RDP2VNC + #define VNCOPT "V:Q:" +@@ -231,7 +235,7 @@ + #define VNCOPT + #endif + +- while ((c = getopt(argc, argv, VNCOPT "u:d:s:c:p:n:k:g:fbeEmCDKS:T:Na:r:045h?")) != -1) ++ while ((c = getopt(argc, argv, VNCOPT "u:d:s:c:p:n:k:g:fbeEmCDKS:T:NX:a:r:045h?")) != -1) + { + switch (c) + { +@@ -374,6 +378,10 @@ + g_numlock_sync = True; + break; + ++ case 'X': ++ g_embed_wnd = strtod(optarg, NULL); ++ break; ++ + case 'a': + g_server_bpp = strtol(optarg, NULL, 10); + if (g_server_bpp != 8 && g_server_bpp != 16 && g_server_bpp != 15 +--- xwin.c.orig 2004-03-08 18:02:58.000000000 +0100 ++++ xwin.c 2004-03-10 16:17:59.000000000 +0100 +@@ -41,6 +41,7 @@ + static int g_x_socket; + static Screen *g_screen; + Window g_wnd; ++Window g_embed_wnd; + BOOL g_enable_compose = False; + static GC g_gc; + static BOOL g_gc_initialized = False; +@@ -929,6 +930,11 @@ + XFree(sizehints); + } + ++ if ( g_embed_wnd ) ++ { ++ XReparentWindow(g_display, g_wnd, g_embed_wnd, 0, 0); ++ } ++ + input_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | + VisibilityChangeMask | FocusChangeMask; + diff --git a/krdc/rdp/rdphostpref.cpp b/krdc/rdp/rdphostpref.cpp new file mode 100644 index 00000000..783c41e4 --- /dev/null +++ b/krdc/rdp/rdphostpref.cpp @@ -0,0 +1,180 @@ +/* + rdphostpref.cpp, handles preferences for RDP hosts + Copyright (C) 2003 Arend van Beelen jr. + + This program is free software; you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if + not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + For any questions, comments or whatever, you may mail me at: arend@auton.nl +*/ + +#include "rdphostpref.h" +#include +#include + +const QString RdpHostPref::RdpType = "RDP"; + +RdpHostPref::RdpHostPref(KConfig *conf, const QString &host, const QString &type) : + HostPref(conf, host, type), + m_width(800), + m_height(600), + m_colorDepth(8), + m_layout("en-us"), + m_askOnConnect(true), + m_useKWallet(true) +{ +} + +RdpHostPref::~RdpHostPref() +{ +} + +void RdpHostPref::save() +{ + if ( !m_host.isEmpty() && !m_type.isEmpty() ) + { + m_config->setGroup("PerHostSettings"); + QString p = prefix(); + m_config->writeEntry(p+"exists", true); + m_config->writeEntry(p+"width", m_width); + m_config->writeEntry(p+"height", m_height); + m_config->writeEntry(p+"colorDepth", m_colorDepth); + m_config->writeEntry(p+"layout", m_layout); + m_config->writeEntry(p+"askOnConnect", m_askOnConnect); + m_config->writeEntry(p+"useKWallet", m_useKWallet); + } + else + { + m_config->setGroup( "RdpDefaultSettings" ); + m_config->writeEntry( "rdpWidth", m_width ); + m_config->writeEntry( "rdpHeight", m_height ); + m_config->writeEntry( "rdpColorDepth", m_colorDepth); + m_config->writeEntry( "rdpKeyboardLayout", m_layout ); + m_config->writeEntry( "rdpShowHostPreferences", m_askOnConnect ); + m_config->writeEntry( "rdpUseKWallet", m_useKWallet ); + } +} + +void RdpHostPref::load() +{ + if ( !m_host.isEmpty() && !m_type.isEmpty() ) + { + m_config->setGroup("PerHostSettings"); + QString p = prefix(); + m_width = m_config->readNumEntry(p+"width", 800); + m_height = m_config->readNumEntry(p+"height", 600); + m_colorDepth = m_config->readNumEntry(p+"colorDepth", 8); + m_layout = m_config->readEntry(p+"layout", "en-us"); + m_askOnConnect = m_config->readBoolEntry(p+"askOnConnect", true); + m_useKWallet = m_config->readBoolEntry(p+"useKWallet", true); + } + else + { + setDefaults(); + } +} + +void RdpHostPref::remove() +{ + m_config->setGroup("PerHostSettings"); + QString p = prefix(); + m_config->deleteEntry(p+"exists"); + m_config->deleteEntry(p+"width"); + m_config->deleteEntry(p+"height"); + m_config->deleteEntry(p+"colorDepth"); + m_config->deleteEntry(p+"layout"); + m_config->deleteEntry(p+"askOnConnect"); + m_config->deleteEntry(p+"useKWallet"); +} + +void RdpHostPref::setDefaults() +{ + m_config->setGroup("RdpDefaultSettings"); + m_width = m_config->readNumEntry("rdpWidth", 800); + m_height = m_config->readNumEntry("rdpHeight", 600); + m_colorDepth = m_config->readNumEntry("rdpColorDepth", 8); + m_layout = m_config->readEntry("rdpKeyboardLayout", "en-us"); + m_askOnConnect = m_config->readBoolEntry("rdpShowHostPreferences", true); + m_useKWallet = m_config->readBoolEntry("rdpUseKWallet", true); +} + +QString RdpHostPref::prefDescription() const +{ + return i18n("Show Preferences: %1, Resolution: %2x%3, Color Depth: %4, Keymap: %5, KWallet: %6") + .arg(m_askOnConnect ? i18n("yes") : i18n("no")).arg(m_width).arg(m_height) + .arg(m_colorDepth).arg(m_layout).arg(m_useKWallet ? i18n("yes") : i18n("no")); +} + +void RdpHostPref::setWidth(int w) +{ + m_width = w; + save(); +} + +int RdpHostPref::width() const +{ + return m_width; +} + +void RdpHostPref::setHeight(int h) +{ + m_height = h; + save(); +} + +int RdpHostPref::height() const +{ + return m_height; +} + +void RdpHostPref::setColorDepth(int d) +{ + m_colorDepth = d; + save(); +} + +int RdpHostPref::colorDepth() const +{ + return m_colorDepth; +} + + +void RdpHostPref::setLayout(const QString &l) +{ + m_layout = l; + save(); +} + +QString RdpHostPref::layout() const +{ + return m_layout; +} + +void RdpHostPref::setAskOnConnect(bool ask) +{ + m_askOnConnect = ask; + save(); +} + +bool RdpHostPref::askOnConnect() const +{ + return m_askOnConnect; +} + +void RdpHostPref::setUseKWallet(bool use) { + m_useKWallet = use; + save(); +} + +bool RdpHostPref::useKWallet() const { + return m_useKWallet; +} diff --git a/krdc/rdp/rdphostpref.h b/krdc/rdp/rdphostpref.h new file mode 100644 index 00000000..355102f3 --- /dev/null +++ b/krdc/rdp/rdphostpref.h @@ -0,0 +1,127 @@ +/* + rdphostpref.h, handles preferences for RDP hosts + Copyright (C) 2003 Arend van Beelen jr. + + This program is free software; you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if + not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + For any questions, comments or whatever, you may mail me at: arend@auton.nl +*/ + +#ifndef RDPHOSTPREF_H +#define RDPHOSTPREF_H + +#include "hostpreferences.h" + +static const QString rdpKeymaps[] = { "ar", + "cs", + "da", + "de", + "de-ch", + "en-gb", + "en-us", + "es", + "et", + "fi", + "fr", + "fr-be", + "fr-ca", + "fr-ch", + "hr", + "hu", + "is", + "it", + "ja", + "lt", + "lv", + "mk", + "nl", + "nl-be", + "no", + "pl", + "pt", + "pt-br", + "ru", + "sl", + "sv", + "th", + "tr" }; +static const int rdpNumKeymaps = 33; +static const int rdpDefaultKeymap = 6; // en-us + +inline int keymap2int(const QString &keymap) +{ + int layout; + for(layout = 0; layout < rdpNumKeymaps; layout++) + { + if(keymap == rdpKeymaps[layout]) + { + break; + } + } + if(layout == rdpNumKeymaps) + { + layout = rdpDefaultKeymap; + } + return layout; +} + +inline QString int2keymap(int layout) +{ + if(layout < 0 || layout >= rdpNumKeymaps) + { + return rdpKeymaps[rdpDefaultKeymap]; + } + + return rdpKeymaps[layout]; +} + +class RdpHostPref : public HostPref +{ + protected: + friend class HostPreferences; + + int m_width; + int m_height; + int m_colorDepth; + QString m_layout; + bool m_askOnConnect; + bool m_useKWallet; + + virtual void load(); + virtual void setDefaults(); + virtual void save(); + virtual void remove(); + + public: + static const QString RdpType; + + RdpHostPref(KConfig *conf, const QString &host=QString::null, + const QString &type=QString::null); + virtual ~RdpHostPref(); + + virtual QString prefDescription() const; + void setWidth(int w); + int width() const; + void setHeight(int h); + int height() const; + void setColorDepth(int depth); + int colorDepth() const; + void setLayout(const QString &l); + QString layout() const; + void setAskOnConnect(bool ask); + bool askOnConnect() const; + bool useKWallet() const; + void setUseKWallet(bool); +}; + +#endif diff --git a/krdc/rdp/rdpprefs.ui b/krdc/rdp/rdpprefs.ui new file mode 100644 index 00000000..8e493d7a --- /dev/null +++ b/krdc/rdp/rdpprefs.ui @@ -0,0 +1,509 @@ + +RdpPrefs + + + RdpPrefs + + + + 0 + 0 + 449 + 177 + + + + + unnamed + + + 0 + + + + rdpGroupBox + + + Connection + + + + unnamed + + + + spacer8 + + + Horizontal + + + Fixed + + + + 70 + 21 + + + + + + + Small (640x480) + + + + + Medium (800x600) + + + + + Big (1024x768) + + + + + Custom (...) + + + + cmbResolution + + + + 5 + 0 + 0 + 0 + + + + + 280 + 0 + + + + 1 + + + Here you can specify the resolution of the remote desktop. This resolution determines the size of the desktop that will be presented to you. + + + + + spinWidth + + + false + + + 9999 + + + 800 + + + This is the width of the remote desktop. You can only change this value manually if you select Custom as desktop resolution above. + + + + + heightLabel + + + false + + + H&eight: + + + AlignVCenter|AlignRight + + + spinHeight + + + + + spinHeight + + + false + + + 9999 + + + 600 + + + This is the height of the remote desktop. You can only change this value manually if you select Custom as desktop resolution above. + + + + + + Arabic (ar) + + + + + Czech (cs) + + + + + Danish (da) + + + + + German (de) + + + + + Swiss German (de-ch) + + + + + British English (en-gb) + + + + + US English (en-us) + + + + + Spanish (es) + + + + + Estonian (et) + + + + + Finnish (fi) + + + + + French (fr) + + + + + Belgium (fr-be) + + + + + French Canadian (fr-ca) + + + + + Swiss French (fr-ch) + + + + + Croatian (hr) + + + + + Hungarian (hu) + + + + + Icelandic (is) + + + + + Italian (it) + + + + + Japanese (ja) + + + + + Lithuanian (lt) + + + + + Latvian (lv) + + + + + Macedonian (mk) + + + + + Dutch (nl) + + + + + Belgian Dutch (nl-be) + + + + + Norwegian (no) + + + + + Polish (pl) + + + + + Portuguese (pt) + + + + + Brazilian (pt-br) + + + + + Russian (ru) + + + + + Slovenian (sl) + + + + + Swedish (sv) + + + + + Thai (th) + + + + + Turkish (tr) + + + + cmbKbLayout + + + + 5 + 0 + 0 + 0 + + + + + 280 + 0 + + + + false + + + 4 + + + Use this to specify your keyboard layout. This layout setting is used to send the correct keyboard codes to the server. + + + + + layoutLabel + + + + 0 + 1 + 0 + 0 + + + + &Keyboard layout: + + + cmbKbLayout + + + + + cbUseKWallet + + + true + + + Use K&Wallet for passwords + + + Enable this option to store your passwords with KWallet. + + + + + resolutionLabel + + + + 0 + 1 + 0 + 0 + + + + Desktop &resolution: + + + cmbResolution + + + + + colorDepthLabel + + + + 0 + 1 + 0 + 0 + + + + Color &depth: + + + cmbKbLayout + + + + + widthLabel + + + false + + + &Width: + + + AlignVCenter|AlignRight + + + spinWidth + + + + + + Low Color (8 Bit) + + + + + High Color (16 Bit) + + + + + True Color (24 Bit) + + + + cmbColorDepth + + + + + + + cbShowPrefs + + + &Show this dialog again for this host + + + Select this option if you do not want to be asked for the settings when connecting to a host. For hosts with existing profiles these profiles will be taken. New hosts will be configured with the defaults. + + + + + + + cmbResolution + activated(int) + RdpPrefs + resolutionChanged(int) + + + cmbColorDepth + activated(int) + RdpPrefs + colorDepth() + + + + cmbResolution + spinWidth + spinHeight + cmbColorDepth + cmbKbLayout + cbShowPrefs + + + kdialog.h + rdpprefs.ui.h + + + resolutionChanged( int selection ) + colorDepth() + setColorDepth( int depth ) + + + setRdpWidth( int w ) + rdpWidth() + setRdpHeight( int h ) + rdpHeight() + setResolution() + resolution() + setKbLayout( int i ) + kbLayout() + setShowPrefs( bool b ) + showPrefs() + setUseKWallet( bool b ) + useKWallet() + + + + diff --git a/krdc/rdp/rdpprefs.ui.h b/krdc/rdp/rdpprefs.ui.h new file mode 100644 index 00000000..06efe7f4 --- /dev/null +++ b/krdc/rdp/rdpprefs.ui.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you wish to add, delete or rename functions or slots use +** Qt Designer which will update this file, preserving your code. Create an +** init() function in place of a constructor, and a destroy() function in +** place of a destructor. +*****************************************************************************/ + + +void RdpPrefs::resolutionChanged( int selection ) +{ + bool enable = (selection==3); + spinWidth->setEnabled(enable); + spinHeight->setEnabled(enable); + widthLabel->setEnabled(enable); + heightLabel->setEnabled(enable); + + switch(selection) + { + case 0: + spinWidth->setValue(640); + spinHeight->setValue(480); + break; + + case 1: + spinWidth->setValue(800); + spinHeight->setValue(600); + break; + + case 2: + spinWidth->setValue(1024); + spinHeight->setValue(768); + break; + + case 3: + default: + break; + } +} + + +void RdpPrefs::setRdpWidth( int w ) +{ + spinWidth->setValue(w); +} + + +int RdpPrefs::rdpWidth() +{ + return spinWidth->value(); +} + + +void RdpPrefs::setRdpHeight( int h ) +{ + spinHeight->setValue(h); +} + + +int RdpPrefs::rdpHeight() +{ + return spinHeight->value(); +} + + +int RdpPrefs::colorDepth() +{ + qDebug("current depth: %i", cmbColorDepth->currentItem() ); + switch (cmbColorDepth->currentItem()) + { + case 0: + return 8; + case 1: + return 16; + case 2: + return 24; + default: + // shouldn't happen, but who knows.. + return 8; + break; + } +} + + +void RdpPrefs::setColorDepth(int depth) +{ + switch (depth) + { + case 8: + cmbColorDepth->setCurrentItem(0); + break; + case 16: + cmbColorDepth->setCurrentItem(1); + break; + case 24: + cmbColorDepth->setCurrentItem(2); + break; + default: + break; + } +} + +void RdpPrefs::setResolution() +{ + if (rdpWidth()==640 && rdpHeight()==480) + { + cmbResolution->setCurrentItem(0); + } + else if (rdpWidth()==800 && rdpHeight()==600) + { + cmbResolution->setCurrentItem(1); + } + else if (rdpWidth()==1024 && rdpHeight()==768) + { + cmbResolution->setCurrentItem(2); + } + else + { + cmbResolution->setCurrentItem(3); + } + resolutionChanged( cmbResolution->currentItem() ); +} + + +int RdpPrefs::resolution() +{ + return cmbResolution->currentItem(); +} + + +void RdpPrefs::setKbLayout( int i ) +{ + cmbKbLayout->setCurrentItem( i ); +} + + +int RdpPrefs::kbLayout() +{ + return cmbKbLayout->currentItem(); +} + + +void RdpPrefs::setShowPrefs( bool b ) +{ + cbShowPrefs->setChecked( b ); +} + + +bool RdpPrefs::showPrefs() +{ + return cbShowPrefs->isChecked(); +} + +void RdpPrefs::setUseKWallet( bool b ) +{ + cbUseKWallet->setChecked(b); +} + +bool RdpPrefs::useKWallet() +{ + return cbUseKWallet->isChecked(); +} diff --git a/krdc/smartptr.cpp b/krdc/smartptr.cpp new file mode 100644 index 00000000..e11ffb41 --- /dev/null +++ b/krdc/smartptr.cpp @@ -0,0 +1,17 @@ +/*************************************************************************** + begin : Wed Jan 19 02:19 CET 2003 + copyright : (C) 2003 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include + diff --git a/krdc/smartptr.h b/krdc/smartptr.h new file mode 100644 index 00000000..d80f258e --- /dev/null +++ b/krdc/smartptr.h @@ -0,0 +1,433 @@ +/*************************************************************************** + begin : Wed Jan 1 17:56 CET 2003 + copyright : (C) 2003 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef SMARTPTR_H +#define SMARTPTR_H + +#include + +class WeakPtr; + +/** + * @internal + */ +struct SmartPtrRefCount { + SmartPtrRefCount(int toObj, int toThis) : + refsToObject(toObj), + refsToThis(toThis) { + } + int refsToObject; // number of pointers to the object, 0 if released + int refsToThis; // number of pointer to the ref count +}; + +/** + * SmartPtr is a reference counting smart pointer. When you create + * the first instance it will create a new counter for the pointee + * and it share it with all other SmartPtr instances for that pointee. + * The reference count can only be kept accurate when you do not create + * a second 'realm' of references by converting a SmartPtr into a + * regular pointer and then create a new SmartPtr from that pointer. + * When the last instance of a SmartPtr for the given object has been + * deleted the object itself will be deleted. You can stop the SmartPtr + * system to manage an object by calling @ref release() on any of + * the pointers pointing to that object. All SmartPtrs will then stop + * managing the object, and you can also safely create a second 'realm'. + * + * SmartPtr can be combined with @ref WeakPtr. A WeakPtr + * does not influence its life cycle, but notices when a SmartPtr + * deletes the object. + * + * The recommended way to use SmartPtr and @ref WeakPtr is to use SmartPtr + * for all aggregations and WeakPtr for associations. Unlike auto_ptr, + * SmartPtr can be used in collections. + * + * SmartPtr is not thread-safe. All instances of SmartPtrs pointing + * to a pointee must always be in the same thread, unless you break + * the 'realm' by calling @ref release() in one thread and give the + * original pointer the other thread. It can then create a new SmartPtr + * and control the lifecycle of the object. + * @see WeakPtr + */ +template +class SmartPtr +{ + public: // members are public because of problems with gcc 3.2 + friend class WeakPtr; + + /// @internal + T* ptr; + /// @internal + mutable SmartPtrRefCount *rc; // if !rc, refcount=1 is assumed + +protected: + void freePtr() { + if (!ptr) + return; + if (!rc) + delete ptr; + else { + if (rc->refsToObject > 0) { + Q_ASSERT(rc->refsToObject >= rc->refsToThis); + if (rc->refsToObject == 1) { + delete ptr; + rc->refsToObject = -1; + } + else + rc->refsToObject--; + } + rc->refsToThis--; + if (rc->refsToThis < 1) + delete rc; + } + } + + void init(T *sptr, SmartPtrRefCount *&orc) + { + ptr = sptr; + if (!sptr) + rc = 0; + else if (!orc) { + orc = new SmartPtrRefCount(2, 2); + rc = orc; + } + else { + rc = orc; + rc->refsToThis++; + if (rc->refsToObject) { + // prevent initialization from invalid WeakPtr + Q_ASSERT(rc->refsToObject > 0); + rc->refsToObject++; + } + } + } + + SmartPtr(T *p, SmartPtrRefCount *&orc) + { + init(p, orc); + } + +public: + /** + * Creates a SmartPtr that refers to the given pointer @p. + * SmartPtr will take control over the object and delete it + * when the last SmartPtr that referes to the object + * has been deleted. + * @param p the pointer to the object to manage, or the null pointer + */ + SmartPtr(T* p = 0) : + ptr(p), + rc(0) + { + } + + /** + * Copies the given SmartPtr, sharing ownership with the other + * pointer. Increases the reference count by 1 (if the object + * has not been @ref release()d). + * @param sptr the object pointer to copy + */ + SmartPtr(const SmartPtr &sptr) + { + init(sptr.ptr, sptr.rc); + } + + /** + * Copies the given SmartPtr, sharing ownership with the other + * pointer. Increases the reference count by 1 (if the object + * has not been @ref release()d). + * @param sptr the object pointer to copy + */ + template + SmartPtr(const SmartPtr &sptr) + { + init((T*)sptr.ptr, sptr.rc); + } + + /** + * Delete the pointer and, if the reference count is one and the object has not + * been released, deletes the object. + */ + ~SmartPtr() { + freePtr(); + } + + /** + * Copies the given SmartPtr, sharing ownership with the other + * pointer. Increases the reference count by 1 (if the object + * has not been @ref release()d). The original object will be dereferenced + * and thus deleted, if the reference count is 1. + * @param sptr the object pointer to copy + * @return this SmartPtr object + */ + SmartPtr &operator=(const SmartPtr &sptr) { + if (this == &sptr) + return *this; + + freePtr(); + init(sptr.ptr, sptr.rc); + return *this; + } + + /** + * Copies the given SmartPtr, sharing ownership with the other + * pointer. Increases the reference count by 1 (if the object + * has not been @ref release()d). The original object will be dereferenced + * and thus deleted, if the reference count is 1. + * @param sptr the object pointer to copy + * @return this SmartPtr object + */ + template + SmartPtr &operator=(const SmartPtr &sptr) { + if (this == static_cast >(&sptr)) + return *this; + + freePtr(); + init(static_cast(sptr.ptr), sptr.rc); + return *this; + } + + /** + * Sets the SmartPointer to the given value. The original object + * will be dereferenced and thus deleted, if the reference count is 1. + * @param p the value of the new pointer + */ + void set(T *p) { + if (ptr == p) + return; + freePtr(); + + ptr = p; + rc = 0; + } + + /** + * Releases the ptr. This means it will not be memory-managed + * anymore, neither by this SmartPtr nor by any other pointer that + * shares the object. The caller is responsible for freeing the + * object. It is possible to assign the plain pointer (but not the + * SmartPtr!) to another SmartPtr that will then start memory + * management. This may be useful, for example, to let another + * thread manage the lifecyle. + * @return the pointer, must be freed by the user + * @see data() + */ + T* release() { + if (!rc) + rc = new SmartPtrRefCount(0, 1); + else + rc->refsToObject = 0; + return ptr; + } + + /** + * Sets the SmartPointer to the given value. The original object + * will be dereferenced and thus deleted, if the reference count is 1. + * @param p the value of the new pointer + * @return this SmartPtr object + */ + SmartPtr &operator=(T *p) { + set(p); + return *this; + } + + /** + * Returns true if the SmartPtr points to an actual object, false + * if it is the null pointer. + * @return true for an actual pointer, false for the null pointer + */ + operator bool() const { + return ptr != 0; + } + + /** + * Returns the plain pointer to the pointed object. The object will + * still be managed by the SmartPtr. You must ensure that the pointer + * is valid (so don't delete the SmartPtr before you are done with the + * plain pointer). + * @return the plain pointer + * @see data() + * @see release() + * @see WeakPtr + */ + template + operator T2*() const { + return static_cast(ptr); + } + + /** + * Returns the plain pointer to the pointed object. The object will + * still be managed by the SmartPtr. You must ensure that the pointer + * is valid (so don't delete the SmartPtr before you are done with the + * plain pointer). + * @return the plain pointer + * @see data() + * @see release() + * @see WeakPtr + */ + template + operator const T2*() const { + return static_cast(ptr); + } + + /** + * Returns a reference to the pointed object. This works exactly + * like on a regular pointer. + * @return the pointer object + */ + T& operator*() { + return *ptr; + } + + /** + * Returns a reference to the pointed object. This works exactly + * like on a regular pointer. + * @return the pointer object + */ + const T& operator*() const { + return *ptr; + } + + /** + * Access a member of the pointed object. This works exactly + * like on a regular pointer. + * @return the pointer + */ + T* operator->() { + return ptr; + } + + /** + * Access a member of the pointed object. This works exactly + * like on a regular pointer. + * @return the pointer + */ + const T* operator->() const { + return ptr; + } + + /** + * Compares two SmartPtrs. They are equal if both point to the + * same object. + * @return true if both point to the same object + */ + bool operator==(const SmartPtr& sptr) const { + return ptr == sptr.ptr; + } + + /** + * Compares two SmartPtrs. They are unequal if both point to + * different objects. + * @return true if both point to different objects + */ + bool operator!=(const SmartPtr& sptr) const { + return ptr != sptr.ptr; + } + + /** + * Compares a SmartPtr with a plain pointer. They are equal if + * both point to the same object. + * @return true if both point to the same object + */ + bool operator==(const T* p) const { + return ptr == p; + } + + /** + * Compares a SmartPtr with a plain pointer. They are unequal if + * both point to different objects. + * @return true if both point to different objects + */ + bool operator!=(const T* p) const { + return ptr != p; + } + + /** + * Negates the pointer. True if the pointer is the null pointer + * @return true for the null pointer, false otherwise + */ + bool operator!() const { + return ptr == 0; + } + + /** + * Returns the pointer. The object will still be managed + * by the SmartPtr. You must ensure that the pointer + * is valid (so don't delete the SmartPtr before you are done with the + * plain pointer). + * @return the plain pointer + * @see release() + * @see WeakPtr + */ + T* data() { + return ptr; + } + + /** + * Returns the pointer. The object will still be managed + * by the SmartPtr. You must ensure that the pointer + * is valid (so don't delete the SmartPtr before you are done with the + * plain pointer). + * @return the plain pointer + * @see release() + * @see WeakPtr + */ + const T* data() const { + return ptr; + } + + /** + * Checks whether both SmartPtrs use the same pointer but two + * different reference counts. + * If yes, one of them must be 0 (object released), otherwise + * it is an error. + * @return true if the pointers are used correctly + */ + bool isRCCorrect(const SmartPtr &p2) const { + if (ptr == p2.ptr) + return true; + if (rc == p2.rc) + return true; + return (rc->refsToObject == 0) || (p2.rc->refsToObject == 0); + } + + /** + * Returns the reference count of the object. The count is 0 if + * the object has been released (@ref release()). For the null pointer + * the reference count is always 1. + * @return the reference count, or 0 for released objects + */ + int referenceCount() const { + return rc ? rc->refsToObject : 1; + } + + /** + * Returns a string representation of the pointer. + * @return a string representation + */ + QString toString() const { + int objrcount = 1; + int rcrcount = 0; + + if (rc) { + objrcount = rc->refsToObject; + rcrcount = rc->refsToThis; + } + return QString("SmartPtr: ptr=%1, refcounts=%2, ptrnum=%3") + .arg((int)ptr).arg(objrcount).arg(rcrcount); + } + +}; + +#endif diff --git a/krdc/smb2rdc.desktop b/krdc/smb2rdc.desktop new file mode 100644 index 00000000..0e6bd845 --- /dev/null +++ b/krdc/smb2rdc.desktop @@ -0,0 +1,56 @@ +# KDE Config File +[Desktop Entry] +ServiceTypes=application/x-smb-server +Actions=smb2rdc + +[Desktop Action smb2rdc] +Name=Open Remote Desktop Connection to This Machine +Name[be]=Ðдкрыць аддаленае злучÑнне з кампутарам +Name[bg]=Връзка Ñ Ð¾Ñ‚Ð´Ð°Ð»ÐµÑ‡ÐµÐ½ работно мÑÑто Ñ Ð¸Ð·Ð±Ñ€Ð°Ð½Ð¸Ñ ÐºÐ¾Ð¼Ð¿ÑŽÑ‚ÑŠÑ€ +Name[bn]=à¦à¦‡ মেশিনে পà§à¦°à¦¤à§à¦¯à¦¨à§à¦¤ ডেসà§à¦•à¦Ÿà¦ªà§‡à¦° সংযোগ খোলো +Name[bs]=Otvori Remote Desktop vezu na ovaj raÄunar +Name[ca]=Obre una connexió remota d'escriptori a aquesta màquina +Name[cs]=Otevřít vzdálené pÅ™ipojení plochy k tomuto poÄítaÄi +Name[da]=Ã…bn ekstern desktopforbindelse til denne maskine +Name[de]=Verbindung zur Arbeitsfläche dieses Rechners herstellen +Name[el]=ΔημιουÏγία σÏνδεσης σε απομακÏυσμένη επιφάνεια εÏγασίας σε αυτό το μηχάνημα +Name[es]=Abrir conexión remota de escritorio a este sistema +Name[et]=Kaugtöölaua ühenduse avamine sellesse arvutisse +Name[eu]=Open Remote Desktop konexioa makina honetara +Name[fa]=باز کردن اتصال رومیزی راه دور برای این ماشین +Name[fi]=Avaa etätyöpöytäyhteys tähän koneeseen +Name[fr]=Ouvrir une connexion distante au bureau de cette machine +Name[gl]=Abrir Conexión Remota de Escritorio a Esta Máquina +Name[he]=פתח חיבור שולחן עבודה מרוחק למכונה זו +Name[hu]=Távoli munkaasztal nyitása itt +Name[is]=Opna fjarlæga skjáborðtengingu til þessarar vélar +Name[it]=Apri connessione a desktop remoto a questa macchina +Name[ja]=ã“ã®ãƒ›ã‚¹ãƒˆã¸ãƒªãƒ¢ãƒ¼ãƒˆãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—接続を開ã +Name[ka]=დáƒáƒ¨áƒáƒ áƒ”ბული სáƒáƒ›áƒ£áƒ¨áƒáƒ მáƒáƒ’იდის კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜áƒ¡ გáƒáƒ®áƒ¡áƒœáƒ áƒáƒ› მáƒáƒœáƒ¥áƒáƒœáƒáƒ¡áƒ—áƒáƒœ +Name[kk]=ОÑÑ‹ компьютердегі Ò¯Ñтелге қашық қоÑылымды ашу +Name[km]=បើក​ការ​ážâ€‹áž—្ជាប​ផ្ទៃ​ážáž»â€‹áž–ី​ចម្ងាយ​ទៅ​ម៉ាស៊ីន​នáŸáŸ‡ +Name[lt]=Užmegzti nutolusio darbastalio prijungimÄ… prie Å¡io kompiuterio +Name[nb]=Ã…pne fjerntilkobling til skrivebord til denne maskinen +Name[nds]=Schriefdisch-Feernverbinnen na dissen Reekner opmaken +Name[ne]=यो मेशिनमा टाढाको डेसà¥à¤•à¤Ÿà¤ª जडान खोलà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Externe bureaubladverbinding met deze computer openen +Name[nn]=Opna samband til skrivebordet over nettverket til denne maskina +Name[pa]=ਇਹ ਮਸ਼ੀਨ ਲਈ ਰਿਮੋਟ ਡੈਸਕਟਾਪ ਕà©à¨¨à©ˆà¨•à¨¸à¨¼à¨¨ ਖੋਲà©à¨¹à©‹ +Name[pl]=Otwórz zdalne poÅ‚Ä…czenie z pulpitem na tej maszynie +Name[pt]=Abrir Ligação Remota a Ecrã para Este Computador +Name[pt_BR]=Abre Conexões Remotas para Essa Máquina +Name[ru]=Открыть Ñоединение Remote Desktop Ñ Ñтой машиной +Name[sk]=Otvorí spojenie na vzdialenú pracovnú plochu na tomto poÄítaÄi +Name[sl]=Odpri povezavo z oddaljenim namizjem na tem raÄunalniku +Name[sr]=Отвори удаљену везу Ñа радном површином на овој машини +Name[sr@Latn]=Otvori udaljenu vezu sa radnom povrÅ¡inom na ovoj maÅ¡ini +Name[sv]=Öppna fjärrskrivbordsanslutning till den här datorn +Name[tr]=Bu makinaya Uzak Masaüstü BaÄŸlantısı aç +Name[uk]=Відкрити з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· віддаленою Ñтільницею до цього комп'ютера +Name[zh_CN]=打开到此计算机的远程桌é¢è¿žæŽ¥ +Name[zh_HK]=開放é ç«¯æ¡Œé¢é€£ç·šåˆ°é€™é›»è…¦ +Name[zh_TW]=在此主機上開啟é ç«¯æ¡Œé¢ +Exec=krdc %u +Icon=krdc + + diff --git a/krdc/vidmode.cpp b/krdc/vidmode.cpp new file mode 100644 index 00000000..4c7762fb --- /dev/null +++ b/krdc/vidmode.cpp @@ -0,0 +1,159 @@ +/*************************************************************************** + vidmode.cpp - video mode switching + ------------------- + begin : Tue June 3 03:08:00 CET 2002 + copyright : (C) 2002 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include + +#ifdef HAVE_VIDMODE_EXTENSION +#include +#endif + +#include "vidmode.h" + +#ifdef HAVE_VIDMODE_EXTENSION + +void vidmodeNormalSwitch(Display *dpy, Resolution oldResolution) +{ + if (!oldResolution.valid) + return; + + XF86VidModeModeInfo **modes; + int modecount; + int eventB, errorB; + + if (!XF86VidModeQueryExtension(dpy, &eventB, &errorB)) + return; + + if (!XF86VidModeGetAllModeLines(dpy, oldResolution.screen, &modecount, &modes)) + return; + + for (int i = 0; i < modecount; i++) { + int w = (*modes[i]).hdisplay; + int h = (*modes[i]).vdisplay; + + if ((oldResolution.width == w) && + (oldResolution.height == h)) { + XF86VidModeSwitchToMode(dpy,oldResolution.screen,modes[i]); + XFlush(dpy); + XF86VidModeSetViewPort(dpy,DefaultScreen(dpy),0,0); + XFlush(dpy); + return; + } + } +} + +Resolution vidmodeFullscreenSwitch(Display *dpy, int screen, + int sw, int sh, int &nx, int &ny) +{ + XF86VidModeModeInfo **modes; + int modecount; + int bestmode = -1; + int bestw, besth; + int eventB, errorB; + + if (screen < 0) + return Resolution(); + + if (!XF86VidModeQueryExtension(dpy, &eventB, &errorB)) + return Resolution(); + + if (!XF86VidModeGetAllModeLines(dpy,screen,&modecount, &modes)) + return Resolution(); + + int cw = (*modes[0]).hdisplay; + int ch = (*modes[0]).vdisplay; + nx = cw; + ny = ch; + if ((cw == sw) && (ch == sh)) + return Resolution(); + bool foundLargeEnoughRes = (cw>=sw) && (ch>=sh); + bestw = cw; + besth = ch; + + for (int i = 1; i < modecount; i++) { + int w = (*modes[i]).hdisplay; + int h = (*modes[i]).vdisplay; + + if ((w == cw) && (h == ch)) + continue; + + /* If resolution matches framebuffer, take it */ + if ((w == sw) && (h == sh)) { + bestw = w; + besth = h; + bestmode = i; + break; + } + /* if resolution larger than framebuffer... */ + if ((w>=sw) && (h>=sh)) { + /* and no other previously found resoltion was smaller or + this is smaller than the best resolution so far, take it*/ + if ((!foundLargeEnoughRes) || + (w*h < bestw*besth)) { + bestw = w; + besth = h; + bestmode = i; + foundLargeEnoughRes = true; + } + } + /* If all resolutions so far were smaller than framebuffer... */ + else if (!foundLargeEnoughRes) { + /* take this one it it is bigger then they were */ + if (w*h > bestw*besth) { + bestw = w; + besth = h; + bestmode = i; + } + } + } + + if (bestmode == -1) + return Resolution(); + + nx = bestw; + ny = besth; + XF86VidModeSwitchToMode(dpy,screen,modes[bestmode]); + XF86VidModeSetViewPort(dpy,screen,0,0); + XFlush(dpy); + + return Resolution(cw, ch, screen); +} + +#else + +void vidmodeNormalSwitch(Display *dpy, Resolution oldResolution) +{ +} + +Resolution vidmodeFullscreenSwitch(Display *dpy, int screen, int sw, int sh, int &nx, int &ny) +{ + return Resolution(); +} + +#endif + +void grabInput(Display *dpy, unsigned int winId) { + XGrabPointer(dpy, winId, True, 0, + GrabModeAsync, GrabModeAsync, + winId, None, CurrentTime); + XFlush(dpy); +} + +void ungrabInput(Display *dpy) { + XUngrabPointer(dpy, CurrentTime); +} + diff --git a/krdc/vidmode.h b/krdc/vidmode.h new file mode 100644 index 00000000..ab17e1b5 --- /dev/null +++ b/krdc/vidmode.h @@ -0,0 +1,40 @@ +/*************************************************************************** + vidmode.h - video mode switching + ------------------- + begin : Tue June 3 03:11:00 CET 2002 + copyright : (C) 2002 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef VIDMODE_H +#define VIDMODE_H + +struct Resolution { + Resolution(int w, int h, int s) : + valid(true), width(w), height(h), screen(s) { + } + Resolution() : + valid(false), width(0), height(0), screen(0) { + } + bool valid; + int width; + int height; + int screen; +}; + +void vidmodeNormalSwitch(Display *dpy, Resolution oldResolution); +Resolution vidmodeFullscreenSwitch(Display *dpy, int screen, int sw, int sh, int &nx, int &ny); + +void grabInput(Display *dpy, unsigned int winId); +void ungrabInput(Display *dpy); + +#endif diff --git a/krdc/vnc.protocol b/krdc/vnc.protocol new file mode 100644 index 00000000..b34bd4ac --- /dev/null +++ b/krdc/vnc.protocol @@ -0,0 +1,13 @@ +[Protocol] +exec=krdc '%u' +protocol=vnc +input=none +output=none +helper=true +listing=false +reading=false +writing=false +makedir=false +deleting=false +Icon=vnc + diff --git a/krdc/vnc/Makefile.am b/krdc/vnc/Makefile.am new file mode 100644 index 00000000..23956297 --- /dev/null +++ b/krdc/vnc/Makefile.am @@ -0,0 +1,16 @@ +KDE_CXXFLAGS = $(USE_THREADS) + +METASOURCES = AUTO + +noinst_LTLIBRARIES = libvnc.la + +libvnc_la_SOURCES = kvncview.cpp threads.cpp colour.c d3des.c desktop.c \ + rfbproto.c sockets.c vncauth.c vncprefs.ui vnchostpref.cpp + +noinst_HEADERS = kvncview.h rfbproto.h vncviewer.h vnctypes.h vncauth.h \ + pointerlatencyometer.h threads.h d3des.h vnchostpref.h + +libvnc_la_LIBADD = $(LIB_KDEUI) $(LIBJPEG) +libvnc_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +INCLUDES= -I$(top_srcdir)/krdc -I.. $(all_includes) diff --git a/krdc/vnc/colour.c b/krdc/vnc/colour.c new file mode 100644 index 00000000..a51d6e61 --- /dev/null +++ b/krdc/vnc/colour.c @@ -0,0 +1,415 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * colour.c - functions to deal with colour - i.e. RFB pixel formats, X visuals + * and colormaps. Thanks to Grant McDorman for some of the ideas used here. + */ + +#include "vncviewer.h" +#include + + +#define INVALID_PIXEL 0xffffffff +#define MAX_CMAP_SIZE 256 +#define BGR233_SIZE 256 +unsigned long BGR233ToPixel[BGR233_SIZE]; + +Colormap cmap; +Visual *vis; +unsigned int visdepth, visbpp; +Bool allocColorFailed = False; + +static int nBGR233ColoursAllocated; + +static int GetBPPForDepth(int depth); +static void SetupBGR233Map(void); +static void AllocateExactBGR233Colours(void); +static Bool AllocateBGR233Colour(int r, int g, int b); + + +/* + * SetVisualAndCmap() deals with the wonderful world of X "visuals" (which are + * equivalent to the RFB protocol's "pixel format"). Having decided on the + * best visual, it also creates a colormap if necessary, sets the appropriate + * resources on the toplevel widget, and sets up the myFormat structure to + * describe the pixel format in terms that the RFB server will be able to + * understand. + * + * The algorithm for deciding which visual to use is as follows: + * + * If forceOwnCmap is true then we try to use a PseudoColor visual - we first + * see if there's one of the same depth as the RFB server, followed by an 8-bit + * deep one. + * + * If forceTrueColour is true then we try to use a TrueColor visual - if + * requestedDepth is set then it must be of that depth, otherwise any depth + * will be used. + * + * Otherwise, we use the X server's default visual and colormap. If this is + * TrueColor then we just ask the RFB server for this format. If the default + * isn't TrueColor, or if useBGR233 is true, then we ask the RFB server for + * BGR233 pixel format and use a lookup table to translate to the nearest + * colours provided by the X server. + */ + +void +SetVisualAndCmap() +{ + /* just use default visual and colormap */ + + vis = DefaultVisual(dpy,DefaultScreen(dpy)); + visdepth = DefaultDepth(dpy,DefaultScreen(dpy)); + visbpp = GetBPPForDepth(visdepth); + cmap = DefaultColormap(dpy,DefaultScreen(dpy)); + + if (!appData.useBGR233 && (vis->class == TrueColor)) { + + myFormat.bitsPerPixel = visbpp; + myFormat.depth = visdepth; + myFormat.trueColour = 1; + myFormat.bigEndian = (ImageByteOrder(dpy) == MSBFirst); + myFormat.redShift = ffs(vis->red_mask) - 1; + myFormat.greenShift = ffs(vis->green_mask) - 1; + myFormat.blueShift = ffs(vis->blue_mask) - 1; + myFormat.redMax = vis->red_mask >> myFormat.redShift; + myFormat.greenMax = vis->green_mask >> myFormat.greenShift; + myFormat.blueMax = vis->blue_mask >> myFormat.blueShift; + + fprintf(stderr, + "Using default colormap which is TrueColor. Pixel format:\n"); + PrintPixelFormat(&myFormat); + return; + } + + appData.useBGR233 = True; + + myFormat.bitsPerPixel = 8; + myFormat.depth = 8; + myFormat.trueColour = 1; + myFormat.bigEndian = 0; + myFormat.redMax = 7; + myFormat.greenMax = 7; + myFormat.blueMax = 3; + myFormat.redShift = 0; + myFormat.greenShift = 3; + myFormat.blueShift = 6; + + fprintf(stderr, + "Using default colormap and translating from BGR233. Pixel format:\n"); + PrintPixelFormat(&myFormat); + + SetupBGR233Map(); +} + + +/* + * GetBPPForDepth looks through the "pixmap formats" to find the bits-per-pixel + * for the given depth. + */ + +static int +GetBPPForDepth(int depth) +{ + XPixmapFormatValues *format; + int nformats; + int i; + int bpp; + + format = XListPixmapFormats(dpy, &nformats); + + for (i = 0; i < nformats; i++) { + if (format[i].depth == depth) + break; + } + + if (i == nformats) { + fprintf(stderr,"no pixmap format for depth %d???\n", depth); + exit(1); + } + + bpp = format[i].bits_per_pixel; + + XFree(format); + + if (bpp != 1 && bpp != 8 && bpp != 16 && bpp != 32) { + fprintf(stderr,"Can't cope with %d bits-per-pixel. Sorry.\n", bpp); + exit(1); + } + + return bpp; +} + + + +/* + * SetupBGR233Map() sets up the BGR233ToPixel array. + * + * It calls AllocateExactBGR233Colours to allocate some exact BGR233 colours + * (limited by space in the colormap and/or by the value of the nColours + * resource). If the number allocated is less than BGR233_SIZE then it fills + * the rest in using the "nearest" colours available. How this is done depends + * on the value of the useSharedColours resource. If it's false, we use only + * colours from the exact BGR233 colours we've just allocated. If it's true, + * then we also use other clients' "shared" colours available in the colormap. + */ + +static void +SetupBGR233Map(void) +{ + int r, g, b; + long i; + unsigned long nearestPixel = 0; + int cmapSize; + XColor cmapEntry[MAX_CMAP_SIZE]; + Bool exactBGR233[MAX_CMAP_SIZE]; + Bool shared[MAX_CMAP_SIZE]; + Bool usedAsNearest[MAX_CMAP_SIZE]; + int nSharedUsed = 0; + + if (visdepth > 8) { + appData.nColours = 256; /* ignore nColours setting for > 8-bit deep */ + } + + for (i = 0; i < BGR233_SIZE; i++) { + BGR233ToPixel[i] = INVALID_PIXEL; + } + + AllocateExactBGR233Colours(); + + fprintf(stderr,"Got %d exact BGR233 colours out of %d\n", + nBGR233ColoursAllocated, appData.nColours); + + if (nBGR233ColoursAllocated < BGR233_SIZE) { + + if (visdepth > 8) { /* shouldn't get here */ + fprintf(stderr,"Error: couldn't allocate BGR233 colours even though " + "depth is %d\n", visdepth); + exit(1); + } + + cmapSize = (1 << visdepth); + + for (i = 0; i < cmapSize; i++) { + cmapEntry[i].pixel = i; + exactBGR233[i] = False; + shared[i] = False; + usedAsNearest[i] = False; + } + + XQueryColors(dpy, cmap, cmapEntry, cmapSize); + + /* mark all our exact BGR233 pixels */ + + for (i = 0; i < BGR233_SIZE; i++) { + if (BGR233ToPixel[i] != INVALID_PIXEL) + exactBGR233[BGR233ToPixel[i]] = True; + } + + if (appData.useSharedColours) { + + /* Try to find existing shared colours. This is harder than it sounds + because XQueryColors doesn't tell us whether colours are shared, + private or unallocated. What we do is go through the colormap and for + each pixel try to allocate exactly its RGB values. If this returns a + different pixel then it's definitely either a private or unallocated + pixel, so no use to us. If it returns us the same pixel again, then + it's likely that it's a shared colour - however, it is possible that + it was actually an unallocated pixel, which we've now allocated. We + minimise this possibility by going through the pixels in reverse order + - this helps becuse the X server allocates new pixels from the lowest + number up, so it should only be a problem for the lowest unallocated + pixel. Got that? */ + + for (i = cmapSize-1; i >= 0; i--) { + if (!exactBGR233[i] && + XAllocColor(dpy, cmap, &cmapEntry[i])) { + + if (cmapEntry[i].pixel == (unsigned long) i) { + + shared[i] = True; /* probably shared */ + + } else { + + /* "i" is either unallocated or private. We have now unnecessarily + allocated cmapEntry[i].pixel. Free it. */ + + XFreeColors(dpy, cmap, &cmapEntry[i].pixel, 1, 0); + } + } + } + } + + /* Now fill in the nearest colours */ + + for (r = 0; r < 8; r++) { + for (g = 0; g < 8; g++) { + for (b = 0; b < 4; b++) { + if (BGR233ToPixel[(b<<6) | (g<<3) | r] == INVALID_PIXEL) { + + unsigned long minDistance = ULONG_MAX; + + for (i = 0; i < cmapSize; i++) { + if (exactBGR233[i] || shared[i]) { + unsigned long distance + = (abs(cmapEntry[i].red - r * 65535 / 7) + + abs(cmapEntry[i].green - g * 65535 / 7) + + abs(cmapEntry[i].blue - b * 65535 / 3)); + + if (distance < minDistance) { + minDistance = distance; + nearestPixel = i; + } + } + } + + BGR233ToPixel[(b<<6) | (g<<3) | r] = nearestPixel; + if (shared[nearestPixel] && !usedAsNearest[nearestPixel]) + nSharedUsed++; + usedAsNearest[nearestPixel] = True; + } + } + } + } + + /* Tidy up shared colours which we allocated but aren't going to use */ + + for (i = 0; i < cmapSize; i++) { + if (shared[i] && !usedAsNearest[i]) { + XFreeColors(dpy, cmap, (unsigned long *)&i, 1, 0); + } + } + + fprintf(stderr,"Using %d existing shared colours\n", nSharedUsed); + } +} + + +/* + * AllocateExactBGR233Colours() attempts to allocate each of the colours in the + * BGR233 colour cube, stopping when an allocation fails. The order it does + * this in is such that we should get a fairly well spread subset of the cube, + * however many allocations are made. There's probably a neater algorithm for + * doing this, but it's not obvious to me anyway. The way this algorithm works + * is: + * + * At each stage, we introduce a new value for one of the primaries, and + * allocate all the colours with the new value of that primary and all previous + * values of the other two primaries. We start with r=0 as the "new" value + * for r, and g=0, b=0 as the "previous" values of g and b. So we get: + * + * New primary value Previous values of other primaries Colours allocated + * ----------------- ---------------------------------- ----------------- + * r=0 g=0 b=0 r0 g0 b0 + * g=7 r=0 b=0 r0 g7 b0 + * b=3 r=0 g=0,7 r0 g0 b3 + * r0 g7 b3 + * r=7 g=0,7 b=0,3 r7 g0 b0 + * r7 g0 b3 + * r7 g7 b0 + * r7 g7 b3 + * g=3 r=0,7 b=0,3 r0 g3 b0 + * r0 g3 b3 + * r7 g3 b0 + * r7 g3 b3 + * ....etc. + * */ + +static void +AllocateExactBGR233Colours(void) +{ + int rv[] = {0,7,3,5,1,6,2,4}; + int gv[] = {0,7,3,5,1,6,2,4}; + int bv[] = {0,3,1,2}; + int rn = 0; + int gn = 1; + int bn = 1; + int ri, gi, bi; + + nBGR233ColoursAllocated = 0; + + while (1) { + if (rn == 8) + break; + + ri = rn; + for (gi = 0; gi < gn; gi++) { + for (bi = 0; bi < bn; bi++) { + if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi])) + return; + } + } + rn++; + + if (gn == 8) + break; + + gi = gn; + for (ri = 0; ri < rn; ri++) { + for (bi = 0; bi < bn; bi++) { + if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi])) + return; + } + } + gn++; + + if (bn < 4) { + + bi = bn; + for (ri = 0; ri < rn; ri++) { + for (gi = 0; gi < gn; gi++) { + if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi])) + return; + } + } + bn++; + } + } +} + + +/* + * AllocateBGR233Colour() attempts to allocate the given BGR233 colour as a + * shared colormap entry, storing its pixel value in the BGR233ToPixel array. + * r is from 0 to 7, g from 0 to 7 and b from 0 to 3. It fails either when the + * allocation fails or when we would exceed the number of colours specified in + * the nColours resource. + */ + +static Bool +AllocateBGR233Colour(int r, int g, int b) +{ + XColor c; + + if (nBGR233ColoursAllocated >= appData.nColours) + return False; + + c.red = r * 65535 / 7; + c.green = g * 65535 / 7; + c.blue = b * 65535 / 3; + + if (!XAllocColor(dpy, cmap, &c)) + return False; + + BGR233ToPixel[(b<<6) | (g<<3) | r] = c.pixel; + + nBGR233ColoursAllocated++; + + return True; +} diff --git a/krdc/vnc/d3des.c b/krdc/vnc/d3des.c new file mode 100644 index 00000000..8a358ce6 --- /dev/null +++ b/krdc/vnc/d3des.c @@ -0,0 +1,440 @@ +/* + * This is D3DES (V5.09) by Richard Outerbridge with the double and + * triple-length support removed for use in VNC. Also the bytebit[] array + * has been reversed so that the most significant bit in each byte of the + * key is ignored, not the least significant. + * + * These changes are: + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* D3DES (V5.09) - + * + * A portable, public domain, version of the Data Encryption Standard. + * + * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge. + * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation + * code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis + * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau, + * for humouring me on. + * + * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge. + * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. + */ + +#include "d3des.h" + +static void scrunch(unsigned char *, unsigned long *); +static void unscrun(unsigned long *, unsigned char *); +static void desfunc(unsigned long *, unsigned long *); +static void cookey(unsigned long *); + +static unsigned long KnL[32] = { 0L }; +static unsigned long KnR[32] = { 0L }; +static unsigned long Kn3[32] = { 0L }; +static unsigned char Df_Key[24] = { + 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, + 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10, + 0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67 }; + +static unsigned short bytebit[8] = { + 01, 02, 04, 010, 020, 040, 0100, 0200 }; + +static unsigned long bigbyte[24] = { + 0x800000L, 0x400000L, 0x200000L, 0x100000L, + 0x80000L, 0x40000L, 0x20000L, 0x10000L, + 0x8000L, 0x4000L, 0x2000L, 0x1000L, + 0x800L, 0x400L, 0x200L, 0x100L, + 0x80L, 0x40L, 0x20L, 0x10L, + 0x8L, 0x4L, 0x2L, 0x1L }; + +/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */ + +static unsigned char pc1[56] = { + 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, + 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, + 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, + 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 }; + +static unsigned char totrot[16] = { + 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 }; + +static unsigned char pc2[48] = { + 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, + 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, + 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, + 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 }; + +void deskey(key, edf) /* Thanks to James Gillogly & Phil Karn! */ +unsigned char *key; +int edf; +{ + register int i, j, l, m, n; + unsigned char pc1m[56], pcr[56]; + unsigned long kn[32]; + + for ( j = 0; j < 56; j++ ) { + l = pc1[j]; + m = l & 07; + pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0; + } + for( i = 0; i < 16; i++ ) { + if( edf == DE1 ) m = (15 - i) << 1; + else m = i << 1; + n = m + 1; + kn[m] = kn[n] = 0L; + for( j = 0; j < 28; j++ ) { + l = j + totrot[i]; + if( l < 28 ) pcr[j] = pc1m[l]; + else pcr[j] = pc1m[l - 28]; + } + for( j = 28; j < 56; j++ ) { + l = j + totrot[i]; + if( l < 56 ) pcr[j] = pc1m[l]; + else pcr[j] = pc1m[l - 28]; + } + for( j = 0; j < 24; j++ ) { + if( pcr[pc2[j]] ) kn[m] |= bigbyte[j]; + if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j]; + } + } + cookey(kn); + return; + } + +static void cookey(raw1) +register unsigned long *raw1; +{ + register unsigned long *cook, *raw0; + unsigned long dough[32]; + register int i; + + cook = dough; + for( i = 0; i < 16; i++, raw1++ ) { + raw0 = raw1++; + *cook = (*raw0 & 0x00fc0000L) << 6; + *cook |= (*raw0 & 0x00000fc0L) << 10; + *cook |= (*raw1 & 0x00fc0000L) >> 10; + *cook++ |= (*raw1 & 0x00000fc0L) >> 6; + *cook = (*raw0 & 0x0003f000L) << 12; + *cook |= (*raw0 & 0x0000003fL) << 16; + *cook |= (*raw1 & 0x0003f000L) >> 4; + *cook++ |= (*raw1 & 0x0000003fL); + } + usekey(dough); + return; + } + +void cpkey(into) +register unsigned long *into; +{ + register unsigned long *from, *endp; + + from = KnL, endp = &KnL[32]; + while( from < endp ) *into++ = *from++; + return; + } + +void usekey(from) +register unsigned long *from; +{ + register unsigned long *to, *endp; + + to = KnL, endp = &KnL[32]; + while( to < endp ) *to++ = *from++; + return; + } + +void des(inblock, outblock) +unsigned char *inblock, *outblock; +{ + unsigned long work[2]; + + scrunch(inblock, work); + desfunc(work, KnL); + unscrun(work, outblock); + return; + } + +static void scrunch(outof, into) +register unsigned char *outof; +register unsigned long *into; +{ + *into = (*outof++ & 0xffL) << 24; + *into |= (*outof++ & 0xffL) << 16; + *into |= (*outof++ & 0xffL) << 8; + *into++ |= (*outof++ & 0xffL); + *into = (*outof++ & 0xffL) << 24; + *into |= (*outof++ & 0xffL) << 16; + *into |= (*outof++ & 0xffL) << 8; + *into |= (*outof & 0xffL); + return; + } + +static void unscrun(outof, into) +register unsigned long *outof; +register unsigned char *into; +{ + *into++ = (*outof >> 24) & 0xffL; + *into++ = (*outof >> 16) & 0xffL; + *into++ = (*outof >> 8) & 0xffL; + *into++ = *outof++ & 0xffL; + *into++ = (*outof >> 24) & 0xffL; + *into++ = (*outof >> 16) & 0xffL; + *into++ = (*outof >> 8) & 0xffL; + *into = *outof & 0xffL; + return; + } + +static unsigned long SP1[64] = { + 0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L, + 0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L, + 0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L, + 0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L, + 0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L, + 0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L, + 0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L, + 0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L, + 0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L, + 0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L, + 0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L, + 0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L, + 0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L, + 0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L, + 0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L, + 0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L }; + +static unsigned long SP2[64] = { + 0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L, + 0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L, + 0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L, + 0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L, + 0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L, + 0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L, + 0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L, + 0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L, + 0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L, + 0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L, + 0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L, + 0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L, + 0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L, + 0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L, + 0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L, + 0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L }; + +static unsigned long SP3[64] = { + 0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L, + 0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L, + 0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L, + 0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L, + 0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L, + 0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L, + 0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L, + 0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L, + 0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L, + 0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L, + 0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L, + 0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L, + 0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L, + 0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L, + 0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L, + 0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L }; + +static unsigned long SP4[64] = { + 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, + 0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L, + 0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L, + 0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L, + 0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L, + 0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L, + 0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L, + 0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L, + 0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L, + 0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L, + 0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L, + 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, + 0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L, + 0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L, + 0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L, + 0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L }; + +static unsigned long SP5[64] = { + 0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L, + 0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L, + 0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L, + 0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L, + 0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L, + 0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L, + 0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L, + 0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L, + 0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L, + 0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L, + 0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L, + 0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L, + 0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L, + 0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L, + 0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L, + 0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L }; + +static unsigned long SP6[64] = { + 0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L, + 0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L, + 0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L, + 0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L, + 0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L, + 0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L, + 0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L, + 0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L, + 0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L, + 0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L, + 0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L, + 0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L, + 0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L, + 0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L, + 0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L, + 0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L }; + +static unsigned long SP7[64] = { + 0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L, + 0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L, + 0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L, + 0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L, + 0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L, + 0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L, + 0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L, + 0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L, + 0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L, + 0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L, + 0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L, + 0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L, + 0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L, + 0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L, + 0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L, + 0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L }; + +static unsigned long SP8[64] = { + 0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L, + 0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L, + 0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L, + 0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L, + 0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L, + 0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L, + 0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L, + 0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L, + 0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L, + 0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L, + 0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L, + 0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L, + 0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L, + 0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L, + 0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L, + 0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L }; + +static void desfunc(block, keys) +register unsigned long *block, *keys; +{ + register unsigned long fval, work, right, leftt; + register int round; + + leftt = block[0]; + right = block[1]; + work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL; + right ^= work; + leftt ^= (work << 4); + work = ((leftt >> 16) ^ right) & 0x0000ffffL; + right ^= work; + leftt ^= (work << 16); + work = ((right >> 2) ^ leftt) & 0x33333333L; + leftt ^= work; + right ^= (work << 2); + work = ((right >> 8) ^ leftt) & 0x00ff00ffL; + leftt ^= work; + right ^= (work << 8); + right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL; + work = (leftt ^ right) & 0xaaaaaaaaL; + leftt ^= work; + right ^= work; + leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL; + + for( round = 0; round < 8; round++ ) { + work = (right << 28) | (right >> 4); + work ^= *keys++; + fval = SP7[ work & 0x3fL]; + fval |= SP5[(work >> 8) & 0x3fL]; + fval |= SP3[(work >> 16) & 0x3fL]; + fval |= SP1[(work >> 24) & 0x3fL]; + work = right ^ *keys++; + fval |= SP8[ work & 0x3fL]; + fval |= SP6[(work >> 8) & 0x3fL]; + fval |= SP4[(work >> 16) & 0x3fL]; + fval |= SP2[(work >> 24) & 0x3fL]; + leftt ^= fval; + work = (leftt << 28) | (leftt >> 4); + work ^= *keys++; + fval = SP7[ work & 0x3fL]; + fval |= SP5[(work >> 8) & 0x3fL]; + fval |= SP3[(work >> 16) & 0x3fL]; + fval |= SP1[(work >> 24) & 0x3fL]; + work = leftt ^ *keys++; + fval |= SP8[ work & 0x3fL]; + fval |= SP6[(work >> 8) & 0x3fL]; + fval |= SP4[(work >> 16) & 0x3fL]; + fval |= SP2[(work >> 24) & 0x3fL]; + right ^= fval; + } + + right = (right << 31) | (right >> 1); + work = (leftt ^ right) & 0xaaaaaaaaL; + leftt ^= work; + right ^= work; + leftt = (leftt << 31) | (leftt >> 1); + work = ((leftt >> 8) ^ right) & 0x00ff00ffL; + right ^= work; + leftt ^= (work << 8); + work = ((leftt >> 2) ^ right) & 0x33333333L; + right ^= work; + leftt ^= (work << 2); + work = ((right >> 16) ^ leftt) & 0x0000ffffL; + leftt ^= work; + right ^= (work << 16); + work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL; + leftt ^= work; + right ^= (work << 4); + *block++ = right; + *block = leftt; + return; + } + +/* Validation sets: + * + * Single-length key, single-length plaintext - + * Key : 0123 4567 89ab cdef + * Plain : 0123 4567 89ab cde7 + * Cipher : c957 4425 6a5e d31d + * + * Double-length key, single-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 + * Plain : 0123 4567 89ab cde7 + * Cipher : 7f1d 0a77 826b 8aff + * + * Double-length key, double-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 + * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff + * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7 + * + * Triple-length key, single-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567 + * Plain : 0123 4567 89ab cde7 + * Cipher : de0b 7c06 ae5e 0ed5 + * + * Triple-length key, double-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567 + * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff + * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5 + * + * d3des V5.0a rwo 9208.07 18:44 Graven Imagery + **********************************************************************/ diff --git a/krdc/vnc/d3des.h b/krdc/vnc/d3des.h new file mode 100644 index 00000000..ea3da44c --- /dev/null +++ b/krdc/vnc/d3des.h @@ -0,0 +1,51 @@ +/* + * This is D3DES (V5.09) by Richard Outerbridge with the double and + * triple-length support removed for use in VNC. + * + * These changes are: + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* d3des.h - + * + * Headers and defines for d3des.c + * Graven Imagery, 1992. + * + * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge + * (GEnie : OUTER; CIS : [71755,204]) + */ + +#define EN0 0 /* MODE == encrypt */ +#define DE1 1 /* MODE == decrypt */ + +extern void deskey(unsigned char *, int); +/* hexkey[8] MODE + * Sets the internal key register according to the hexadecimal + * key contained in the 8 bytes of hexkey, according to the DES, + * for encryption or decryption according to MODE. + */ + +extern void usekey(unsigned long *); +/* cookedkey[32] + * Loads the internal key register with the data in cookedkey. + */ + +extern void cpkey(unsigned long *); +/* cookedkey[32] + * Copies the contents of the internal key register into the storage + * located at &cookedkey[0]. + */ + +extern void des(unsigned char *, unsigned char *); +/* from[8] to[8] + * Encrypts/Decrypts (according to the key currently loaded in the + * internal key register) one block of eight bytes at address 'from' + * into the block at address 'to'. They can be the same. + */ + +/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery + ********************************************************************/ diff --git a/krdc/vnc/desktop.c b/krdc/vnc/desktop.c new file mode 100644 index 00000000..f5a60966 --- /dev/null +++ b/krdc/vnc/desktop.c @@ -0,0 +1,1613 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * Copyright (C) 2002 Tim Jansen. All Rights Reserved. + * Copyright (C) 1999-2001 Anders Lindström + * + * + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * tim@tjansen.de: - removed stuff for krdc + * - merged with shm.c and misc.c + * - added FillRectangle and Sync methods to draw only on + * the image + * - added Zoom functionality, based on rotation funcs from + * SGE by Anders Lindström) + * - added support for softcursor encoding + * + */ + +/* + * desktop.c - functions to deal with "desktop" window. + */ + +#include +#include +#include +#include +#include +#include +#include "vncviewer.h" + +static XShmSegmentInfo shminfo; +static Bool caughtShmError = False; +static Bool needShmCleanup = False; + +static XShmSegmentInfo zoomshminfo; +static Bool caughtZoomShmError = False; +static Bool needZoomShmCleanup = False; + +static Bool gcInited = False; +GC gc; +GC srcGC, dstGC; /* used for debugging copyrect */ +Dimension dpyWidth, dpyHeight; + +static XImage *image = NULL; +Bool useShm = True; + +static Bool zoomActive = False; +static int zoomWidth, zoomHeight; +static XImage *zoomImage = NULL; +static Bool useZoomShm = True; + +/* for softcursor */ +static char *savedArea = NULL; + +typedef enum { + SOFTCURSOR_UNDER, + SOFTCURSOR_PART_UNDER, + SOFTCURSOR_UNAFFECTED +} SoftCursorState; + +typedef int Sint32; +typedef short Sint16; +typedef char Sint8; +typedef unsigned int Uint32; +typedef unsigned short Uint16; +typedef unsigned char Uint8; + +typedef struct { + int w, h; + unsigned int pitch; + void *pixels; + int BytesPerPixel; +} Surface; + +typedef struct { + Sint16 x, y; + Uint16 w, h; +} Rect; + +static void bgr233cpy(CARD8 *dst, CARD8 *src, int len); +static void CopyDataToScreenRaw(char *buf, int x, int y, int width, int height); +static void CopyBGR233ToScreen(CARD8 *buf, int x, int y, int width,int height); +static void FillRectangleBGR233(CARD8 buf, int x, int y, int width,int height); +static int CheckRectangle(int x, int y, int width, int height); +static SoftCursorState getSoftCursorState(int x, int y, int width, int height); +static void discardCursorSavedArea(void); +static void saveCursorSavedArea(void); + +static void ZoomInit(void); +static void transformZoomSrc(int six, int siy, int siw, int sih, + int *dix, int *diy, int *diw, int *dih, + int srcW, int dstW, int srcH, int dstH); +static void transformZoomDst(int *six, int *siy, int *siw, int *sih, + int dix, int diy, int diw, int dih, + int srcW, int dstW, int srcH, int dstH); +static void ZoomSurfaceSrcCoords(int x, int y, int w, int h, + int *dix, int *diy, int *diw, int *dih, + Surface * src, Surface * dst); +static void ZoomSurfaceCoords32(int sx, int sy, int sw, int sh, + int dx, int dy, Surface * src, Surface * dst); +static void sge_transform(Surface *src, Surface *dst, float xscale, float yscale, + Uint16 qx, Uint16 qy); + + +void +DesktopInit(Window win) +{ + XGCValues gcv; + + image = CreateShmImage(); + + if (!image) { + useShm = False; + image = XCreateImage(dpy, vis, visdepth, ZPixmap, 0, NULL, + si.framebufferWidth, si.framebufferHeight, + BitmapPad(dpy), 0); + + image->data = calloc(image->bytes_per_line * image->height, 1); + if (!image->data) { + fprintf(stderr,"malloc failed\n"); + exit(1); + } + } + + gc = XCreateGC(dpy,win,0,NULL); + + gcv.function = GXxor; + gcv.foreground = 0x0f0f0f0f; + srcGC = XCreateGC(dpy,win,GCFunction|GCForeground,&gcv); + gcv.foreground = 0xf0f0f0f0; + dstGC = XCreateGC(dpy,win,GCFunction|GCForeground,&gcv); + gcInited = True; +} + +/* + * DrawScreenRegionX11Thread + * Never call from any other desktop.c function, only for X11 thread + */ + +void +DrawScreenRegionX11Thread(Window win, int x, int y, int width, int height) { + zoomActive = False; + zoomWidth = 0; + zoomHeight = 0; + + if (!image) + return; + + if (useShm) + XShmPutImage(dpy, win, gc, image, x, y, x, y, width, height, False); + else + XPutImage(dpy, win, gc, image, x, y, x, y, width, height); +} + +/* + * CheckRectangle + */ + +static int CheckRectangle(int x, int y, int width, int height) { + if ((x < 0) || (y < 0)) + return 0; + + if (((x+width) > si.framebufferWidth) || ((y+height) > si.framebufferHeight)) + return 0; + + return 1; +} + +static +void bgr233cpy(CARD8 *dst, CARD8 *src, int len) { + int i; + CARD16 *d16; + CARD32 *d32; + + switch (visbpp) { + case 8: + for (i = 0; i < len; i++) + *(dst++) = (CARD8) BGR233ToPixel[*(src++)]; + break; + case 16: + d16 = (CARD16*) dst; + for (i = 0; i < len; i++) + *(d16++) = (CARD16) BGR233ToPixel[*(src++)]; + break; + case 32: + d32 = (CARD32*) dst; + for (i = 0; i < len; i++) + *(d32++) = (CARD32) BGR233ToPixel[*(src++)]; + break; + default: + fprintf(stderr, "Unsupported softcursor depth %d\n", visbpp); + } +} + + +/* + * CopyDataToScreen. + */ + +void +CopyDataToScreen(char *buf, int x, int y, int width, int height) +{ + SoftCursorState s; + + if (!CheckRectangle(x, y, width, height)) + return; + + LockFramebuffer(); + s = getSoftCursorState(x, y, width, height); + if (s == SOFTCURSOR_PART_UNDER) + undrawCursor(); + + if (!appData.useBGR233) + CopyDataToScreenRaw(buf, x, y, width, height); + else + CopyBGR233ToScreen((CARD8 *)buf, x, y, width, height); + + if (s != SOFTCURSOR_UNAFFECTED) + drawCursor(); + + UnlockFramebuffer(); + SyncScreenRegion(x, y, width, height); +} + +/* + * CopyDataToScreenRaw. + */ + +static void +CopyDataToScreenRaw(char *buf, int x, int y, int width, int height) +{ + int h; + int widthInBytes = width * visbpp / 8; + int scrWidthInBytes = image->bytes_per_line; + char *scr = (image->data + y * scrWidthInBytes + + x * visbpp / 8); + + for (h = 0; h < height; h++) { + memcpy(scr, buf, widthInBytes); + buf += widthInBytes; + scr += scrWidthInBytes; + } +} + +/* + * CopyBGR233ToScreen. + */ + +static void +CopyBGR233ToScreen(CARD8 *buf, int x, int y, int width, int height) +{ + int p, q; + int xoff = 7 - (x & 7); + int xcur; + int fbwb = si.framebufferWidth / 8; + CARD8 *scr1 = ((CARD8 *)image->data) + y * fbwb + x / 8; + CARD8 *scrt; + CARD8 *scr8 = ((CARD8 *)image->data) + y * si.framebufferWidth + x; + CARD16 *scr16 = ((CARD16 *)image->data) + y * si.framebufferWidth + x; + CARD32 *scr32 = ((CARD32 *)image->data) + y * si.framebufferWidth + x; + + switch (visbpp) { + + /* thanks to Chris Hooper for single bpp support */ + + case 1: + for (q = 0; q < height; q++) { + xcur = xoff; + scrt = scr1; + for (p = 0; p < width; p++) { + *scrt = ((*scrt & ~(1 << xcur)) + | (BGR233ToPixel[*(buf++)] << xcur)); + + if (xcur-- == 0) { + xcur = 7; + scrt++; + } + } + scr1 += fbwb; + } + break; + + case 8: + for (q = 0; q < height; q++) { + for (p = 0; p < width; p++) { + *(scr8++) = BGR233ToPixel[*(buf++)]; + } + scr8 += si.framebufferWidth - width; + } + break; + + case 16: + for (q = 0; q < height; q++) { + for (p = 0; p < width; p++) { + *(scr16++) = BGR233ToPixel[*(buf++)]; + } + scr16 += si.framebufferWidth - width; + } + break; + + case 32: + for (q = 0; q < height; q++) { + for (p = 0; p < width; p++) { + *(scr32++) = BGR233ToPixel[*(buf++)]; + } + scr32 += si.framebufferWidth - width; + } + break; + } +} + +/* + * FillRectangle8. + */ + +void +FillRectangle8(CARD8 fg, int x, int y, int width, int height) +{ + SoftCursorState s; + + if (!CheckRectangle(x, y, width, height)) + return; + + s = getSoftCursorState(x, y, width, height); + if (s == SOFTCURSOR_PART_UNDER) + undrawCursor(); + + if (!appData.useBGR233) { + int h; + int widthInBytes = width * visbpp / 8; + int scrWidthInBytes = image->bytes_per_line; + + char *scr = (image->data + y * scrWidthInBytes + + x * visbpp / 8); + + for (h = 0; h < height; h++) { + memset(scr, fg, widthInBytes); + scr += scrWidthInBytes; + } + } else { + FillRectangleBGR233(fg, x, y, width, height); + } + + if (s != SOFTCURSOR_UNAFFECTED) + drawCursor(); +} + +/* + * FillRectangleBGR233. + */ + +static void +FillRectangleBGR233(CARD8 fg, int x, int y, int width, int height) +{ + int p, q; + int xoff = 7 - (x & 7); + int xcur; + int fbwb = si.framebufferWidth / 8; + CARD8 *scr1 = ((CARD8 *)image->data) + y * fbwb + x / 8; + CARD8 *scrt; + CARD8 *scr8 = ((CARD8 *)image->data) + y * si.framebufferWidth + x; + CARD16 *scr16 = ((CARD16 *)image->data) + y * si.framebufferWidth + x; + CARD32 *scr32 = ((CARD32 *)image->data) + y * si.framebufferWidth + x; + + unsigned long fg233 = BGR233ToPixel[fg]; + + switch (visbpp) { + + /* thanks to Chris Hooper for single bpp support */ + + case 1: + for (q = 0; q < height; q++) { + xcur = xoff; + scrt = scr1; + for (p = 0; p < width; p++) { + *scrt = ((*scrt & ~(1 << xcur)) + | (fg233 << xcur)); + + if (xcur-- == 0) { + xcur = 7; + scrt++; + } + } + scr1 += fbwb; + } + break; + + case 8: + for (q = 0; q < height; q++) { + for (p = 0; p < width; p++) { + *(scr8++) = fg233; + } + scr8 += si.framebufferWidth - width; + } + break; + + case 16: + for (q = 0; q < height; q++) { + for (p = 0; p < width; p++) { + *(scr16++) = fg233; + } + scr16 += si.framebufferWidth - width; + } + break; + + case 32: + for (q = 0; q < height; q++) { + for (p = 0; p < width; p++) { + *(scr32++) = fg233; + } + scr32 += si.framebufferWidth - width; + } + break; + } +} + +/* + * FillRectangle16 + */ + +void +FillRectangle16(CARD16 fg, int x, int y, int width, int height) +{ + int i, h; + int scrWidthInBytes = image->bytes_per_line; + + char *scr = (image->data + y * scrWidthInBytes + + x * visbpp / 8); + CARD16 *scr16; + SoftCursorState s; + + if (!CheckRectangle(x, y, width, height)) + return; + + s = getSoftCursorState(x, y, width, height); + if (s == SOFTCURSOR_PART_UNDER) + undrawCursor(); + + for (h = 0; h < height; h++) { + scr16 = (CARD16*) scr; + for (i = 0; i < width; i++) + scr16[i] = fg; + scr += scrWidthInBytes; + } + + if (s != SOFTCURSOR_UNAFFECTED) + drawCursor(); +} + +/* + * FillRectangle32 + */ + +void +FillRectangle32(CARD32 fg, int x, int y, int width, int height) +{ + int i, h; + int scrWidthInBytes = image->bytes_per_line; + SoftCursorState s; + + char *scr = (image->data + y * scrWidthInBytes + + x * visbpp / 8); + CARD32 *scr32; + + if (!CheckRectangle(x, y, width, height)) + return; + + s = getSoftCursorState(x, y, width, height); + if (s == SOFTCURSOR_PART_UNDER) + undrawCursor(); + + for (h = 0; h < height; h++) { + scr32 = (CARD32*) scr; + for (i = 0; i < width; i++) + scr32[i] = fg; + scr += scrWidthInBytes; + } + + if (s != SOFTCURSOR_UNAFFECTED) + drawCursor(); +} + +/* + * CopyDataFromScreen. + */ + +void +CopyDataFromScreen(char *buf, int x, int y, int width, int height) +{ + int widthInBytes = width * visbpp / 8; + int scrWidthInBytes = image->bytes_per_line; + char *src = (image->data + y * scrWidthInBytes + + x * visbpp / 8); + int h; + + if (!CheckRectangle(x, y, width, height)) + return; + + for (h = 0; h < height; h++) { + memcpy(buf, src, widthInBytes); + src += scrWidthInBytes; + buf += widthInBytes; + } +} + +/* + * CopyArea + */ + +void +CopyArea(int srcX, int srcY, int width, int height, int x, int y) +{ + int widthInBytes = width * visbpp / 8; + SoftCursorState sSrc, sDst; + + LockFramebuffer(); + sSrc = getSoftCursorState(srcX, srcY, width, height); + sDst = getSoftCursorState(x, y, width, height); + if ((sSrc != SOFTCURSOR_UNAFFECTED) || + (sDst == SOFTCURSOR_PART_UNDER)) + undrawCursor(); + + if ((srcY+height < y) || (y+height < srcY) || + (srcX+width < x) || (x+width < srcX)) { + + int scrWidthInBytes = image->bytes_per_line; + char *src = (image->data + srcY * scrWidthInBytes + + srcX * visbpp / 8); + char *dst = (image->data + y * scrWidthInBytes + + x * visbpp / 8); + int h; + + if (!CheckRectangle(srcX, srcY, width, height)) { + UnlockFramebuffer(); + return; + } + if (!CheckRectangle(x, y, width, height)) { + UnlockFramebuffer(); + return; + } + + for (h = 0; h < height; h++) { + memcpy(dst, src, widthInBytes); + src += scrWidthInBytes; + dst += scrWidthInBytes; + } + } + else { + char *buf = malloc(widthInBytes*height); + if (!buf) { + UnlockFramebuffer(); + fprintf(stderr, "Out of memory, CopyArea impossible\n"); + return; + } + CopyDataFromScreen(buf, srcX, srcY, width, height); + CopyDataToScreenRaw(buf, x, y, width, height); + free(buf); + } + if ((sSrc != SOFTCURSOR_UNAFFECTED) || + (sDst != SOFTCURSOR_UNAFFECTED)) + drawCursor(); + UnlockFramebuffer(); + SyncScreenRegion(x, y, width, height); +} + +void SyncScreenRegion(int x, int y, int width, int height) { + int dx, dy, dw, dh; + + if (zoomActive) { + Surface src, dest; + src.w = si.framebufferWidth; + src.h = si.framebufferHeight; + src.pitch = image->bytes_per_line; + src.pixels = image->data; + src.BytesPerPixel = visbpp / 8; + dest.w = zoomWidth; + dest.h = zoomHeight; + dest.pitch = zoomImage->bytes_per_line; + dest.pixels = zoomImage->data; + dest.BytesPerPixel = visbpp / 8; + ZoomSurfaceSrcCoords(x, y, width, height, &dx, &dy, &dw, &dh, &src, &dest); + } + else { + dx = x; dy = y; + dw = width; dh = height; + } + DrawScreenRegion(dx, dy, dw, dh); +} + +void SyncScreenRegionX11Thread(int x, int y, int width, int height) { + int dx, dy, dw, dh; + + if (zoomActive) { + Surface src, dest; + src.w = si.framebufferWidth; + src.h = si.framebufferHeight; + src.pitch = image->bytes_per_line; + src.pixels = image->data; + src.BytesPerPixel = visbpp / 8; + dest.w = zoomWidth; + dest.h = zoomHeight; + dest.pitch = zoomImage->bytes_per_line; + dest.pixels = zoomImage->data; + dest.BytesPerPixel = visbpp / 8; + ZoomSurfaceSrcCoords(x, y, width, height, &dx, &dy, &dw, &dh, &src, &dest); + } + else { + dx = x; dy = y; + dw = width; dh = height; + } + DrawAnyScreenRegionX11Thread(dx, dy, dw, dh); +} + +/* + * ToplevelInitBeforeRealization sets the title, geometry and other resources + * on the toplevel window. + */ + +void +ToplevelInit() +{ + dpyWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy)); + dpyHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy)); +} + +/* + * Cleanup - perform shm cleanup operations prior to exiting. + */ + +void +Cleanup() +{ + if (useShm || useZoomShm) + ShmCleanup(); +} + +void +ShmCleanup() +{ + fprintf(stderr,"ShmCleanup called\n"); + if (needShmCleanup) { + shmdt(shminfo.shmaddr); + shmctl(shminfo.shmid, IPC_RMID, 0); + needShmCleanup = False; + } + if (needZoomShmCleanup) { + shmdt(zoomshminfo.shmaddr); + shmctl(zoomshminfo.shmid, IPC_RMID, 0); + needZoomShmCleanup = False; + } +} + +static int +ShmCreationXErrorHandler(Display *d, XErrorEvent *e) +{ + caughtShmError = True; + return 0; +} + +XImage * +CreateShmImage() +{ + XImage *_image; + XErrorHandler oldXErrorHandler; + + if (!XShmQueryExtension(dpy)) + return NULL; + + _image = XShmCreateImage(dpy, vis, visdepth, ZPixmap, NULL, &shminfo, + si.framebufferWidth, si.framebufferHeight); + if (!_image) return NULL; + + shminfo.shmid = shmget(IPC_PRIVATE, + _image->bytes_per_line * _image->height, + IPC_CREAT|0777); + + if (shminfo.shmid == -1) { + XDestroyImage(_image); + return NULL; + } + + shminfo.shmaddr = _image->data = shmat(shminfo.shmid, 0, 0); + + if (shminfo.shmaddr == (char *)-1) { + XDestroyImage(_image); + shmctl(shminfo.shmid, IPC_RMID, 0); + return NULL; + } + + shminfo.readOnly = True; + + oldXErrorHandler = XSetErrorHandler(ShmCreationXErrorHandler); + XShmAttach(dpy, &shminfo); + XSync(dpy, False); + XSetErrorHandler(oldXErrorHandler); + + if (caughtShmError) { + XDestroyImage(_image); + shmdt(shminfo.shmaddr); + shmctl(shminfo.shmid, IPC_RMID, 0); + return NULL; + } + + needShmCleanup = True; + + fprintf(stderr,"Using shared memory PutImage\n"); + + return _image; +} + +void undrawCursor() { + int x, y, w, h; + + if ((imageIndex < 0) || !savedArea) + return; + + getBoundingRectCursor(cursorX, cursorY, imageIndex, + &x, &y, &w, &h); + + if ((w < 1) || (h < 1)) + return; + + CopyDataToScreenRaw(savedArea, x, y, w, h); + discardCursorSavedArea(); +} + +static void drawCursorImage() { + int x, y, w, h, pw, pixelsLeft, processingMask; + int skipLeft, skipRight; + PointerImage *pi = &pointerImages[imageIndex]; + CARD8 *img = (CARD8*) pi->image; + CARD8 *imgEnd = &img[pi->len]; + CARD8 *fb; + + /* check whether the source image has ended (image broken) */ +#define CHECK_IMG(x) if (&img[x] > imgEnd) goto imgError + +/* check whether the end of the framebuffer has been reached (last line) */ +#define CHECK_END() if ((wl == 0) && (h == 1)) return + +/* skip x pixels in the source (x must be < pixelsLeft!) */ +#define SKIP_IMG(x) if ((x > 0) && !processingMask) { \ + CHECK_END(); \ + img += pw * x; \ + CHECK_IMG(0); \ + } + +/* skip x pixels in source and destination */ +#define SKIP_PIXELS(x) { int wl = x; \ + while (pixelsLeft <= wl) { \ + wl -= pixelsLeft; \ + SKIP_IMG(pixelsLeft); \ + CHECK_END(); \ + pixelsLeft = *(img++); \ + CHECK_IMG(0); \ + processingMask = processingMask ? 0 : 1; \ + } \ + pixelsLeft -= wl; \ + SKIP_IMG(wl); \ + } + + if (!img) + return; + + x = cursorX - pi->hotX; + y = cursorY - pi->hotY; + w = pi->w; + h = pi->h; + + if (!rectsIntersect(x, y, w, h, + 0, 0, si.framebufferWidth, si.framebufferHeight)) { + fprintf(stderr, "intersect abort\n"); + return; + } + + pw = myFormat.bitsPerPixel / 8; + processingMask = 1; + pixelsLeft = *(img++); + +/* at this point everything is initialized for the macros */ + + /* skip/clip bottom lines */ + if ((y+h) > si.framebufferHeight) + h = si.framebufferHeight - y; + + /* Skip invisible top lines */ + while (y < 0) { + SKIP_PIXELS(w); + y++; + h--; + } + + /* calculate left/right clipping */ + if (x < 0) { + skipLeft = -x; + w += x; + x = 0; + } + else + skipLeft = 0; + + if ((x+w) > si.framebufferWidth) { + skipRight = (x+w) - si.framebufferWidth; + w = si.framebufferWidth - x; + } + else + skipRight = 0; + + fb = (CARD8*) image->data + y * image->bytes_per_line + x * visbpp / 8; + + /* Paint the thing */ + while (h > 0) { + SKIP_PIXELS(skipLeft); + + { + CARD8 *fbx = fb; + int wl = w; + while (pixelsLeft <= wl) { + wl -= pixelsLeft; + if ((pixelsLeft > 0) && !processingMask) { + int pl = pw * pixelsLeft; + CHECK_IMG(pl); + if (!appData.useBGR233) + memcpy(fbx, img, pl); + else + bgr233cpy(fbx, img, pixelsLeft); + img += pl; + } + + CHECK_END(); + fbx += pixelsLeft * visbpp / 8; + pixelsLeft = *(img++); + + CHECK_IMG(0); + processingMask = processingMask ? 0 : 1; + } + pixelsLeft -= wl; + if ((wl > 0) && !processingMask) { + int pl = pw * wl; + CHECK_IMG(pl); + if (!appData.useBGR233) + memcpy(fbx, img, pl); + else + bgr233cpy(fbx, img, wl); + img += pl; + } + } + + SKIP_PIXELS(skipRight); + fb += image->bytes_per_line; + h--; + } + return; + +imgError: + fprintf(stderr, "Error in softcursor image %d\n", imageIndex); + pointerImages[imageIndex].set = 0; +} + +static void discardCursorSavedArea() { + if (savedArea) + free(savedArea); + savedArea = 0; +} + +static void saveCursorSavedArea() { + int x, y, w, h; + + if (imageIndex < 0) + return; + getBoundingRectCursor(cursorX, cursorY, imageIndex, + &x, &y, &w, &h); + if ((w < 1) || (h < 1)) + return; + discardCursorSavedArea(); + savedArea = malloc(h*image->bytes_per_line); + if (!savedArea) { + fprintf(stderr,"malloc failed, saving cursor not possible\n"); + exit(1); + } + CopyDataFromScreen(savedArea, x, y, w, h); +} + +void drawCursor() { + saveCursorSavedArea(); + drawCursorImage(); +} + +void getBoundingRectCursor(int cx, int cy, int _imageIndex, + int *x, int *y, int *w, int *h) { + int nx, ny, nw, nh; + + if ((_imageIndex < 0) || !pointerImages[_imageIndex].set) { + *x = 0; + *y = 0; + *w = 0; + *h = 0; + return; + } + + nx = cx - pointerImages[_imageIndex].hotX; + ny = cy - pointerImages[_imageIndex].hotY; + nw = pointerImages[_imageIndex].w; + nh = pointerImages[_imageIndex].h; + if (nx < 0) { + nw += nx; + nx = 0; + } + if (ny < 0) { + nh += ny; + ny = 0; + } + if ((nx+nw) > si.framebufferWidth) + nw = si.framebufferWidth - nx; + if ((ny+nh) > si.framebufferHeight) + nh = si.framebufferHeight - ny; + if ((nw <= 0) || (nh <= 0)) { + *x = 0; + *y = 0; + *w = 0; + *h = 0; + return; + } + + *x = nx; + *y = ny; + *w = nw; + *h = nh; +} + +static SoftCursorState getSoftCursorState(int x, int y, int w, int h) { + int cx, cy, cw, ch; + + if (imageIndex < 0) + return SOFTCURSOR_UNAFFECTED; + + getBoundingRectCursor(cursorX, cursorY, imageIndex, + &cx, &cy, &cw, &ch); + + if ((cw == 0) || (ch == 0)) + return SOFTCURSOR_UNAFFECTED; + + if (!rectsIntersect(x, y, w, h, cx, cy, cw, ch)) + return SOFTCURSOR_UNAFFECTED; + if (rectContains(x, y, w, h, cx, cy, cw, ch)) + return SOFTCURSOR_UNDER; + else + return SOFTCURSOR_PART_UNDER; +} + +int rectsIntersect(int x, int y, int w, int h, + int x2, int y2, int w2, int h2) { + if (x2 >= (x+w)) + return 0; + if (y2 >= (y+h)) + return 0; + if ((x2+w2) <= x) + return 0; + if ((y2+h2) <= y) + return 0; + return 1; +} + +int rectContains(int outX, int outY, int outW, int outH, + int inX, int inY, int inW, int inH) { + if (inX < outX) + return 0; + if (inY < outY) + return 0; + if ((inX+inW) > (outX+outW)) + return 0; + if ((inY+inH) > (outY+outH)) + return 0; + return 1; +} + +void rectsJoin(int *nx1, int *ny1, int *nw1, int *nh1, + int x2, int y2, int w2, int h2) { + int ox, oy, ow, oh; + ox = *nx1; + oy = *ny1; + ow = *nw1; + oh = *nh1; + + if (x2 < ox) { + ow += ox - x2; + ox = x2; + } + if (y2 < oy) { + oh += oy - y2; + oy = y2; + } + if ((x2+w2) > (ox+ow)) + ow = (x2+w2) - ox; + if ((y2+h2) > (oy+oh)) + oh = (y2+h2) - oy; + + *nx1 = ox; + *ny1 = oy; + *nw1 = ow; + *nh1 = oh; +} + +XImage * +CreateShmZoomImage() +{ + XImage *_image; + XErrorHandler oldXErrorHandler; + + if (!XShmQueryExtension(dpy)) + return NULL; + + _image = XShmCreateImage(dpy, vis, visdepth, ZPixmap, NULL, &zoomshminfo, + si.framebufferWidth, si.framebufferHeight); + if (!_image) return NULL; + + zoomshminfo.shmid = shmget(IPC_PRIVATE, + _image->bytes_per_line * _image->height, + IPC_CREAT|0777); + + if (zoomshminfo.shmid == -1) { + XDestroyImage(_image); + return NULL; + } + + zoomshminfo.shmaddr = _image->data = shmat(zoomshminfo.shmid, 0, 0); + + if (zoomshminfo.shmaddr == (char *)-1) { + XDestroyImage(_image); + shmctl(zoomshminfo.shmid, IPC_RMID, 0); + return NULL; + } + + zoomshminfo.readOnly = True; + + oldXErrorHandler = XSetErrorHandler(ShmCreationXErrorHandler); + XShmAttach(dpy, &zoomshminfo); + XSync(dpy, False); + XSetErrorHandler(oldXErrorHandler); + + if (caughtZoomShmError) { + XDestroyImage(_image); + shmdt(zoomshminfo.shmaddr); + shmctl(zoomshminfo.shmid, IPC_RMID, 0); + return NULL; + } + + needZoomShmCleanup = True; + + fprintf(stderr,"Using shared memory PutImage\n"); + + return _image; +} + + +/* + * DrawZoomedScreenRegionX11Thread + * Never call from any other desktop.c function, only for X11 thread + */ + +void +DrawZoomedScreenRegionX11Thread(Window win, int zwidth, int zheight, + int x, int y, int width, int height) { + if (!image) + return; + + if (zwidth > si.framebufferWidth) + zwidth = si.framebufferWidth; + if (zheight > si.framebufferHeight) + zheight = si.framebufferHeight; + + if (!zoomActive) { + ZoomInit(); + zoomActive = True; + } + + if ((zoomWidth != zwidth) || (zoomHeight != zheight)) { + Surface src, dest; + + zoomWidth = zwidth; + zoomHeight = zheight; + + src.w = si.framebufferWidth; + src.h = si.framebufferHeight; + src.pitch = image->bytes_per_line; + src.pixels = image->data; + src.BytesPerPixel = visbpp / 8; + dest.w = zwidth; + dest.h = zheight; + dest.pitch = zoomImage->bytes_per_line; + dest.pixels = zoomImage->data; + dest.BytesPerPixel = visbpp / 8; + sge_transform(&src, &dest, + (float)dest.w/(float)src.w, (float)dest.h/(float)src.h, + 0, 0); + + if (useZoomShm) + XShmPutImage(dpy, win, gc, zoomImage, 0, 0, 0, 0, zwidth, zheight, False); + else + XPutImage(dpy, win, gc, zoomImage, 0, 0, 0, 0, zwidth, zheight); + return; + } + + if (useZoomShm) + XShmPutImage(dpy, win, gc, zoomImage, x, y, x, y, width, height, False); + else + XPutImage(dpy, win, gc, zoomImage, x, y, x, y, width, height); +} + + +static void +ZoomInit() +{ + if (zoomImage) + return; + + zoomImage = CreateShmZoomImage(); + + if (!zoomImage) { + useZoomShm = False; + zoomImage = XCreateImage(dpy, vis, visdepth, ZPixmap, 0, NULL, + si.framebufferWidth, si.framebufferHeight, + BitmapPad(dpy), 0); + + zoomImage->data = calloc(zoomImage->bytes_per_line * zoomImage->height, 1); + if (!zoomImage->data) { + fprintf(stderr,"malloc failed\n"); + exit(1); + } + } +} + +static void transformZoomSrc(int six, int siy, int siw, int sih, + int *dix, int *diy, int *diw, int *dih, + int srcW, int dstW, int srcH, int dstH) { + double sx, sy, sw, sh; + double dx, dy, dw, dh; + double wq, hq; + + sx = six; sy = siy; + sw = siw; sh = sih; + + wq = ((double)dstW) / (double) srcW; + hq = ((double)dstH) / (double) srcH; + + dx = sx * wq; + dy = sy * hq; + dw = sw * wq; + dh = sh * hq; + + *dix = dx; + *diy = dy; + *diw = dw+(dx-(int)dx)+0.5; + *dih = dh+(dy-(int)dy)+0.5; +} + +static void transformZoomDst(int *six, int *siy, int *siw, int *sih, + int dix, int diy, int diw, int dih, + int srcW, int dstW, int srcH, int dstH) { + double sx, sy, sw, sh; + double dx, dy, dw, dh; + double wq, hq; + + dx = dix; dy = diy; + dw = diw; dh = dih; + + wq = ((double)dstW) / (double) srcW; + hq = ((double)dstH) / (double) srcH; + + sx = dx / wq; + sy = dy / hq; + sw = dw / wq; + sh = dh / hq; + + *six = sx; + *siy = sy; + *siw = sw+(sx-(int)sx)+0.5; + *sih = sh+(sy-(int)sy)+0.5; +} + + +static void ZoomSurfaceSrcCoords(int six, int siy, int siw, int sih, + int *dix, int *diy, int *diw, int *dih, + Surface * src, Surface * dst) +{ + int dx, dy, dw, dh; + int sx, sy, sw, sh; + + transformZoomSrc(six, siy, siw, sih, + &dx, &dy, &dw, &dh, + src->w, dst->w, src->h, dst->h); + dx-=2; + dy-=2; + dw+=4; + dh+=4; + + if (dx < 0) + dx = 0; + if (dy < 0) + dy = 0; + if (dx+dw > dst->w) + dw = dst->w - dx; + if (dy+dh > dst->h) + dh = dst->h - dy; + + transformZoomDst(&sx, &sy, &sw, &sh, + dx, dy, dw, dh, + src->w, dst->w, src->h, dst->h); + + if (sx+sw > src->w) + sw = src->w - sx; + if (sy+sh > src->h) + sh = src->h - sy; + + ZoomSurfaceCoords32(sx, sy, sw, sh, dx, dy, src, dst); + + *dix = dx; + *diy = dy; + *diw = dw; + *dih = dh; +} + +static void ZoomSurfaceCoords32(int sx, int sy, int sw, int sh, + int dx, int dy, + Surface * src, Surface * dst) +{ + Surface s2; + + s2 = *src; + s2.pixels = ((char*)s2.pixels) + (sx * s2.BytesPerPixel) + (sy * src->pitch); + s2.w = sw; + s2.h = sh; + sge_transform(&s2, dst, + (float)dst->w/(float)src->w, (float)dst->h/(float)src->h, + dx, dy); +} + + +#define sge_clip_xmin(pnt) 0 +#define sge_clip_xmax(pnt) pnt->w +#define sge_clip_ymin(pnt) 0 +#define sge_clip_ymax(pnt) pnt->h + +/*================================================================================== +// Helper function to sge_transform() +// Returns the bounding box +//================================================================================== +*/ +static void _calcRect(Surface *src, Surface *dst, float xscale, float yscale, + Uint16 qx, Uint16 qy, + Sint16 *xmin, Sint16 *ymin, Sint16 *xmax, Sint16 *ymax) +{ + Sint16 x, y, rx, ry; + int i; + + /* Clip to src surface */ + Sint16 sxmin = sge_clip_xmin(src); + Sint16 sxmax = sge_clip_xmax(src); + Sint16 symin = sge_clip_ymin(src); + Sint16 symax = sge_clip_ymax(src); + Sint16 sx[5]; + Sint16 sy[4]; + + /* We don't really need fixed-point here + * but why not? */ + Sint32 ictx = (Sint32) (xscale * 8192.0); + Sint32 icty = (Sint32) (yscale * 8192.0); + + sx[0] = sxmin; + sx[1] = sxmax; + sx[2] = sxmin; + sx[3] = sxmax; + sy[0] = symin; + sy[1] = symax; + sy[2] = symax; + sy[3] = symin; + + /* Calculate the four corner points */ + for(i=0; i<4; i++){ + rx = sx[i]; + ry = sy[i]; + + x = (Sint16)(((ictx*rx) >> 13) + qx); + y = (Sint16)(((icty*ry) >> 13) + qy); + + + if(i==0){ + *xmax = *xmin = x; + *ymax = *ymin = y; + }else{ + if(x>*xmax) + *xmax=x; + else if(x<*xmin) + *xmin=x; + + if(y>*ymax) + *ymax=y; + else if(y<*ymin) + *ymin=y; + } + } + + /* Better safe than sorry...*/ + *xmin -= 1; + *ymin -= 1; + *xmax += 1; + *ymax += 1; + + /* Clip to dst surface */ + if( !dst ) + return; + if( *xmin < sge_clip_xmin(dst) ) + *xmin = sge_clip_xmin(dst); + if( *xmax > sge_clip_xmax(dst) ) + *xmax = sge_clip_xmax(dst); + if( *ymin < sge_clip_ymin(dst) ) + *ymin = sge_clip_ymin(dst); + if( *ymax > sge_clip_ymax(dst) ) + *ymax = sge_clip_ymax(dst); +} + + +/*================================================================================== +** Scale by scale and place at position (qx,qy). +** +** +** Developed with the help from Terry Hancock (hancock@earthlink.net) +** +**==================================================================================*/ +/* First we need some macros to handle different bpp + * I'm sorry about this... + */ +#define TRANSFORM(UintXX, DIV) \ + Sint32 src_pitch=src->pitch/DIV; \ + Sint32 dst_pitch=dst->pitch/DIV; \ + UintXX *src_row = (UintXX *)src->pixels; \ + UintXX *dst_row; \ +\ + for (y=ymin; ypixels + y*dst_pitch; \ +\ + for (x=xmin; x> 13); /* Convert from fixed-point */ \ + ry=(Sint16)(sy >> 13); \ +\ + /* Make sure the source pixel is actually in the source image. */ \ + if( (rx>=sxmin) && (rx=symin) && (rypitch/DIV; \ + Sint32 dst_pitch=dst->pitch/DIV; \ + UintXX *src_row = (UintXX *)src->pixels; \ + UintXX *dst_row; \ + UintXX c1, c2, c3, c4;\ + Uint32 R, G, B, A=0; \ + UintXX Rmask = image->red_mask;\ + UintXX Gmask = image->green_mask;\ + UintXX Bmask = image->blue_mask;\ + UintXX Amask = 0;\ + Uint32 wx, wy;\ + Uint32 p1, p2, p3, p4;\ +\ + /* + * Interpolation: + * We calculate the distances from our point to the four nearest pixels, d1..d4. + * d(a,b) = sqrt(a²+b²) ~= 0.707(a+b) (Pythagoras (Taylor) expanded around (0.5;0.5)) + * + * 1 wx 2 + * *-|-* (+ = our point at (x,y)) + * | | | (* = the four nearest pixels) + * wy --+ | wx = float(x) - int(x) + * | | wy = float(y) - int(y) + * *---* + * 3 4 + * d1 = d(wx,wy) d2 = d(1-wx,wy) d3 = d(wx,1-wy) d4 = d(1-wx,1-wy) + * We now want to weight each pixels importance - it's vicinity to our point: + * w1=d4 w2=d3 w3=d2 w4=d1 (Yes it works... just think a bit about it) + * + * If the pixels have the colors c1..c4 then our point should have the color + * c = (w1*c1 + w2*c2 + w3*c3 + w4*c4)/(w1+w2+w3+w4) (the weighted average) + * but w1+w2+w3+w4 = 4*0.707 so we might as well write it as + * c = p1*c1 + p2*c2 + p3*c3 + p4*c4 where p1..p4 = (w1..w4)/(4*0.707) + * + * But p1..p4 are fixed point so we can just divide the fixed point constant! + * 8192/(4*0.71) = 2897 and we can skip 0.71 too (the division will cancel it everywhere) + * 8192/4 = 2048 + * + * 020102: I changed the fixed-point representation for the variables in the weighted average + * to 24.7 to avoid problems with 32bit colors. Everything else is still 18.13. This + * does however not solve the problem with 32bit RGBA colors... + */\ +\ + Sint32 one = 2048>>6; /* 1 in Fixed-point */ \ + Sint32 two = 2*2048>>6; /* 2 in Fixed-point */ \ +\ + for (y=ymin; ypixels + y*dst_pitch; \ +\ + for (x=xmin; x> 13); /* Convert from fixed-point */ \ + ry=(Sint16)(sy >> 13); \ +\ + /* Make sure the source pixel is actually in the source image. */ \ + if( (rx>=sxmin) && (rx+1=symin) && (ry+1>8; /* (float(x) - int(x)) / 4 */ \ + wy = (sy & 0x00001FFF) >>8;\ +\ + p4 = wx+wy;\ + p3 = one-wx+wy;\ + p2 = wx+one-wy;\ + p1 = two-wx-wy;\ +\ + c1 = *(src_row + ry*src_pitch + rx);\ + c2 = *(src_row + ry*src_pitch + rx+1);\ + c3 = *(src_row + (ry+1)*src_pitch + rx);\ + c4 = *(src_row + (ry+1)*src_pitch + rx+1);\ +\ + /* Calculate the average */\ + R = ((p1*(c1 & Rmask) + p2*(c2 & Rmask) + p3*(c3 & Rmask) + p4*(c4 & Rmask))>>7) & Rmask;\ + G = ((p1*(c1 & Gmask) + p2*(c2 & Gmask) + p3*(c3 & Gmask) + p4*(c4 & Gmask))>>7) & Gmask;\ + B = ((p1*(c1 & Bmask) + p2*(c2 & Bmask) + p3*(c3 & Bmask) + p4*(c4 & Bmask))>>7) & Bmask;\ + if(Amask)\ + A = ((p1*(c1 & Amask) + p2*(c2 & Amask) + p3*(c3 & Amask) + p4*(c4 & Amask))>>7) & Amask;\ + \ + *(dst_row + x) = R | G | B | A;\ + } \ + sx += ctx; /* Incremental transformations */ \ + } \ + } + +void sge_transform(Surface *src, Surface *dst, float xscale, float yscale, Uint16 qx, Uint16 qy) +{ + Sint32 dy, sx, sy; + Sint16 x, y, rx, ry; + Rect r; + + Sint32 ctx, cty; + Sint16 xmin, xmax, ymin, ymax; + Sint16 sxmin, sxmax, symin, symax; + Sint32 dx, ctdx; + + + /* Here we use 18.13 fixed point integer math + // Sint32 should have 31 usable bits and one for sign + // 2^13 = 8192 + */ + + /* Check scales */ + Sint32 maxint = (Sint32)(pow(2, sizeof(Sint32)*8 - 1 - 13)); /* 2^(31-13) */ + + r.x = r.y = r.w = r.h = 0; + + if( xscale == 0 || yscale == 0) + return; + + if( 8192.0/xscale > maxint ) + xscale = (float)(8192.0/maxint); + else if( 8192.0/xscale < -maxint ) + xscale = (float)(-8192.0/maxint); + + if( 8192.0/yscale > maxint ) + yscale = (float)(8192.0/maxint); + else if( 8192.0/yscale < -maxint ) + yscale = (float)(-8192.0/maxint); + + + /* Fixed-point equivalents */ + ctx = (Sint32)(8192.0/xscale); + cty = (Sint32)(8192.0/yscale); + + /* Compute a bounding rectangle */ + xmin=0; xmax=dst->w; ymin=0; ymax=dst->h; + _calcRect(src, dst, xscale, yscale, + qx, qy, &xmin, &ymin, &xmax, &ymax); + + /* Clip to src surface */ + sxmin = sge_clip_xmin(src); + sxmax = sge_clip_xmax(src); + symin = sge_clip_ymin(src); + symax = sge_clip_ymax(src); + + /* Some terms in the transform are constant */ + dx = xmin - qx; + ctdx = ctx*dx; + + /* Use the correct bpp */ + if( src->BytesPerPixel == dst->BytesPerPixel){ + switch( src->BytesPerPixel ){ + case 1: { /* Assuming 8-bpp */ + TRANSFORM(Uint8, 1) + } + break; + case 2: { /* Probably 15-bpp or 16-bpp */ + TRANSFORM_AA(Uint16, 2) + } + break; + case 4: { /* Probably 32-bpp */ + TRANSFORM_AA(Uint32, 4) + } + break; + } + } +} + +void freeDesktopResources() { + Cleanup(); + if (image) { + XDestroyImage(image); + } + if (zoomImage) { + XDestroyImage(zoomImage); + } + if (savedArea) + free(savedArea); + + if (gcInited) { + XFreeGC(dpy, gc); + XFreeGC(dpy, srcGC); + XFreeGC(dpy, dstGC); + } + + caughtShmError = False; + needShmCleanup = False; + caughtZoomShmError = False; + needZoomShmCleanup = False; + gcInited = False; + image = NULL; + useShm = True; + zoomActive = False; + zoomImage = NULL; + useZoomShm = True; + savedArea = NULL; +} + + +/* + * ColorRectangle32 + * Only used for debugging / visualizing output + */ +/* +static void +ColorRectangle32(XImage *img, CARD32 fg, int x, int y, int width, int height) +{ + int i, h; + int scrWidthInBytes = img->bytes_per_line; + char *scr; + CARD32 *scr32; + + if ((!img) || (!img->data)) + return; + + scr = (img->data + y * scrWidthInBytes + x * 4); + + if (!CheckRectangle(x, y, width, height)) + return; + + for (h = 0; h < height; h++) { + scr32 = (CARD32*) scr; + for (i = 0; i < width; i++) { + CARD32 n = 0; + CARD32 p = scr32[i]; + if (0xff & fg) + n |= ((( 0xff & p)+( 0xff & fg)) >> 2) & 0xff; + else + n |= (0xff & p); + if (0xff00 & fg) + n |= ((( 0xff00 & p)+( 0xff00 & fg)) >> 2) & 0xff00; + else + n |= (0xff00 & p); + if (0xff0000 & fg) + n |= (((0xff0000 & p)+(0xff0000 & fg)) >> 2) & 0xff0000; + else + n |= (0xff0000 & p); + scr32[i] = n; + } + scr += scrWidthInBytes; + } +} +*/ + diff --git a/krdc/vnc/hextile.c b/krdc/vnc/hextile.c new file mode 100644 index 00000000..002880af --- /dev/null +++ b/krdc/vnc/hextile.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * hextile.c - handle hextile encoding. + * + * This file shouldn't be compiled directly. It is included multiple times by + * rfbproto.c, each time with a different definition of the macro BPP. For + * each value of BPP, this file defines a function which handles a hextile + * encoded rectangle with BPP bits per pixel. + */ + +#define HandleHextileBPP CONCAT2E(HandleHextile,BPP) +#define CARDBPP CONCAT2E(CARD,BPP) +#define GET_PIXEL CONCAT2E(GET_PIXEL,BPP) +#define FillRectangleBPP CONCAT2E(FillRectangle,BPP) + +static Bool +HandleHextileBPP (int rx, int ry, int rw, int rh) +{ + CARDBPP bg, fg; + int i; + CARD8 *ptr; + int x, y, w, h; + int sx, sy, sw, sh; + CARD8 subencoding; + CARD8 nSubrects; + + for (y = ry; y < ry+rh; y += 16) { + for (x = rx; x < rx+rw; x += 16) { + w = h = 16; + if (rx+rw - x < 16) + w = rx+rw - x; + if (ry+rh - y < 16) + h = ry+rh - y; + + if (!ReadFromRFBServer((char *)&subencoding, 1)) + return False; + + if (subencoding & rfbHextileRaw) { + if (!ReadFromRFBServer(buffer, w * h * (BPP / 8))) + return False; + + CopyDataToScreen(buffer, x, y, w, h); + continue; + } + + if (subencoding & rfbHextileBackgroundSpecified) + if (!ReadFromRFBServer((char *)&bg, sizeof(bg))) + return False; + + LockFramebuffer(); + FillRectangleBPP(bg, x, y, w, h); + + if (subencoding & rfbHextileForegroundSpecified) + if (!ReadFromRFBServer((char *)&fg, sizeof(fg))) { + UnlockFramebuffer(); + return False; + } + + if (!(subencoding & rfbHextileAnySubrects)) { + UnlockFramebuffer(); + SyncScreenRegion(x, y, w, h); + continue; + } + + if (!ReadFromRFBServer((char *)&nSubrects, 1)) { + UnlockFramebuffer(); + return False; + } + + ptr = (CARD8 *)buffer; + + if (subencoding & rfbHextileSubrectsColoured) { + if (!ReadFromRFBServer(buffer, nSubrects * (2 + (BPP / 8)))) { + UnlockFramebuffer(); + return False; + } + + for (i = 0; i < nSubrects; i++) { + GET_PIXEL(fg, ptr); + sx = rfbHextileExtractX(*ptr); + sy = rfbHextileExtractY(*ptr); + ptr++; + sw = rfbHextileExtractW(*ptr); + sh = rfbHextileExtractH(*ptr); + ptr++; + FillRectangleBPP(fg, x+sx, y+sy, sw, sh); + } + + } else { + if (!ReadFromRFBServer(buffer, nSubrects * 2)) { + UnlockFramebuffer(); + return False; + } + + for (i = 0; i < nSubrects; i++) { + sx = rfbHextileExtractX(*ptr); + sy = rfbHextileExtractY(*ptr); + ptr++; + sw = rfbHextileExtractW(*ptr); + sh = rfbHextileExtractH(*ptr); + ptr++; + FillRectangleBPP(fg, x+sx, y+sy, sw, sh); + } + } + UnlockFramebuffer(); + SyncScreenRegion(x, y, w, h); + } + } + + return True; +} diff --git a/krdc/vnc/kvncview.cpp b/krdc/vnc/kvncview.cpp new file mode 100644 index 00000000..1b6a8de2 --- /dev/null +++ b/krdc/vnc/kvncview.cpp @@ -0,0 +1,828 @@ +/*************************************************************************** + kvncview.cpp - main widget + ------------------- + begin : Thu Dec 20 15:11:42 CET 2001 + copyright : (C) 2001-2003 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kvncview.h" +#include "vncprefs.h" +#include "vnchostpref.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "vncviewer.h" + +#include + +/* + * appData is our application-specific data which can be set by the user with + * application resource specs. The AppData structure is defined in the header + * file. + */ +AppData appData; +bool appDataConfigured = false; + +Display* dpy; + +static KVncView *kvncview; + +//Passwords and KWallet data +extern KWallet::Wallet *wallet; +bool useKWallet = false; +static QCString password; +static QMutex passwordLock; +static QWaitCondition passwordWaiter; + +const unsigned int MAX_SELECTION_LENGTH = 4096; + + +KVncView::KVncView(QWidget *parent, + const char *name, + const QString &_host, + int _port, + const QString &_password, + Quality quality, + DotCursorState dotCursorState, + const QString &encodings) : + KRemoteView(parent, name, Qt::WResizeNoErase | Qt::WRepaintNoErase | Qt::WStaticContents), + m_cthread(this, m_wthread, m_quitFlag), + m_wthread(this, m_quitFlag), + m_quitFlag(false), + m_enableFramebufferLocking(false), + m_scaling(false), + m_remoteMouseTracking(false), + m_viewOnly(false), + m_buttonMask(0), + m_host(_host), + m_port(_port), + m_dontSendCb(false), + m_cursorState(dotCursorState) +{ + kvncview = this; + password = _password.latin1(); + dpy = qt_xdisplay(); + setFixedSize(16,16); + setFocusPolicy(QWidget::StrongFocus); + + m_cb = QApplication::clipboard(); + connect(m_cb, SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); + connect(m_cb, SIGNAL(dataChanged()), this, SLOT(clipboardChanged())); + + KStandardDirs *dirs = KGlobal::dirs(); + QBitmap cursorBitmap(dirs->findResource("appdata", + "pics/pointcursor.png")); + QBitmap cursorMask(dirs->findResource("appdata", + "pics/pointcursormask.png")); + m_cursor = QCursor(cursorBitmap, cursorMask); + + if ((quality != QUALITY_UNKNOWN) || + !encodings.isNull()) + configureApp(quality, encodings); +} + +void KVncView::showDotCursor(DotCursorState state) { + if (state == m_cursorState) + return; + + m_cursorState = state; + showDotCursorInternal(); +} + +DotCursorState KVncView::dotCursorState() const { + return m_cursorState; +} + +void KVncView::showDotCursorInternal() { + switch (m_cursorState) { + case DOT_CURSOR_ON: + setCursor(m_cursor); + break; + case DOT_CURSOR_OFF: + setCursor(QCursor(Qt::BlankCursor)); + break; + case DOT_CURSOR_AUTO: + if (m_enableClientCursor) + setCursor(QCursor(Qt::BlankCursor)); + else + setCursor(m_cursor); + break; + } +} + +QString KVncView::host() { + return m_host; +} + +int KVncView::port() { + return m_port; +} + +void KVncView::startQuitting() { + m_quitFlag = true; + m_wthread.kick(); + m_cthread.kick(); +} + +bool KVncView::isQuitting() { + return m_quitFlag; +} + +void KVncView::configureApp(Quality q, const QString specialEncodings) { + appDataConfigured = true; + appData.shareDesktop = 1; + appData.viewOnly = 0; + + if (q == QUALITY_LOW) { + appData.useBGR233 = 1; + appData.encodingsString = "background copyrect softcursor tight zlib hextile raw"; + appData.compressLevel = -1; + appData.qualityLevel = 1; + appData.dotCursor = 1; + } + else if (q == QUALITY_MEDIUM) { + appData.useBGR233 = 0; + appData.encodingsString = "background copyrect softcursor tight zlib hextile raw"; + appData.compressLevel = -1; + appData.qualityLevel = 7; + appData.dotCursor = 1; + } + else if ((q == QUALITY_HIGH) || (q == QUALITY_UNKNOWN)) { + appData.useBGR233 = 0; + appData.encodingsString = "copyrect softcursor hextile raw"; + appData.compressLevel = -1; + appData.qualityLevel = 9; + appData.dotCursor = 1; + } + + if (!specialEncodings.isNull()) + appData.encodingsString = specialEncodings.latin1(); + + appData.nColours = 256; + appData.useSharedColours = 1; + appData.requestedDepth = 0; + + appData.rawDelay = 0; + appData.copyRectDelay = 0; + + if (!appData.dotCursor) + m_cursorState = DOT_CURSOR_OFF; + showDotCursorInternal(); +} + +bool KVncView::checkLocalKRfb() { + if ( m_host != "localhost" && !m_host.isEmpty() ) + return true; + DCOPClient *d = KApplication::dcopClient(); + + int portNum; + QByteArray sdata, rdata; + QCString replyType; + QDataStream arg(sdata, IO_WriteOnly); + arg << QString("krfb"); + if (!d->call ("kded", "kinetd", "port(QString)", sdata, replyType, rdata)) + return true; + + if (replyType != "int") + return true; + + QDataStream answer(rdata, IO_ReadOnly); + answer >> portNum; + + if (m_port != portNum) + return true; + + setStatus(REMOTE_VIEW_DISCONNECTED); + KMessageBox::error(0, + i18n("It is not possible to connect to a local desktop sharing service."), + i18n("Connection Failure")); + emit disconnectedError(); + return false; +} + +bool KVncView::editPreferences( HostPrefPtr host ) +{ + SmartPtr hp( host ); + + int ci = hp->quality(); + bool kwallet = hp->useKWallet(); + + // show preferences dialog + KDialogBase *dlg = new KDialogBase( 0L, "dlg", true, + i18n( "VNC Host Preferences for %1" ).arg( host->host() ), + KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true ); + + QVBox *vbox = dlg->makeVBoxMainWidget(); + VncPrefs *prefs = new VncPrefs( vbox ); + QWidget *spacer = new QWidget( vbox ); + vbox->setStretchFactor( spacer, 10 ); + + prefs->setQuality( ci ); + prefs->setShowPrefs(true); + prefs->setUseKWallet(kwallet); + + if ( dlg->exec() == QDialog::Rejected ) + return false; + + ci = prefs->quality(); + hp->setAskOnConnect(prefs->showPrefs()); + hp->setQuality(ci); + hp->setUseKWallet(prefs->useKWallet()); + + delete dlg; + return true; +} + +bool KVncView::start() { + + if (!checkLocalKRfb()) + return false; + + if (!appDataConfigured) { + + HostPreferences *hps = HostPreferences::instance(); + SmartPtr hp = + SmartPtr(hps->createHostPref(m_host, + VncHostPref::VncType)); + if (hp->askOnConnect()) { + if (!editPreferences(hp)) + return false; + hps->sync(); + } + + int ci = hp->quality(); + + Quality quality; + if (ci == 0) + quality = QUALITY_HIGH; + else if (ci == 1) + quality = QUALITY_MEDIUM; + else if (ci == 2) + quality = QUALITY_LOW; + else { + kdDebug() << "Unknown quality"; + return false; + } + + configureApp(quality); + useKWallet = hp->useKWallet(); + } + + setStatus(REMOTE_VIEW_CONNECTING); + + m_cthread.start(); + setBackgroundMode(Qt::NoBackground); + return true; +} + +KVncView::~KVncView() +{ + startQuitting(); + m_cthread.wait(); + m_wthread.wait(); + freeResources(); +} + +bool KVncView::supportsLocalCursor() const { + return true; +} + +bool KVncView::supportsScaling() const { + return true; +} + +bool KVncView::scaling() const { + return m_scaling; +} + +bool KVncView::viewOnly() { + return m_viewOnly; +} + +QSize KVncView::framebufferSize() { + return m_framebufferSize; +} + +void KVncView::setViewOnly(bool s) { + m_viewOnly = s; + + if (s) + setCursor(Qt::ArrowCursor); + else + showDotCursorInternal(); +} + +void KVncView::enableScaling(bool s) { + bool os = m_scaling; + m_scaling = s; + if (s != os) { + if (s) { + setMaximumSize(m_framebufferSize); + setMinimumSize(m_framebufferSize.width()/16, + m_framebufferSize.height()/16); + } + else + setFixedSize(m_framebufferSize); + } +} + +void KVncView::paintEvent(QPaintEvent *e) { + drawRegion(e->rect().x(), + e->rect().y(), + e->rect().width(), + e->rect().height()); +} + +void KVncView::drawRegion(int x, int y, int w, int h) { + if (m_scaling) + DrawZoomedScreenRegionX11Thread(winId(), width(), height(), + x, y, w, h); + else + DrawScreenRegionX11Thread(winId(), x, y, w, h); +} + +void KVncView::customEvent(QCustomEvent *e) +{ + if (e->type() == ScreenRepaintEventType) { + ScreenRepaintEvent *sre = (ScreenRepaintEvent*) e; + drawRegion(sre->x(), sre->y(),sre->width(), sre->height()); + } + else if (e->type() == ScreenResizeEventType) { + ScreenResizeEvent *sre = (ScreenResizeEvent*) e; + m_framebufferSize = QSize(sre->width(), sre->height()); + setFixedSize(m_framebufferSize); + emit changeSize(sre->width(), sre->height()); + } + else if (e->type() == DesktopInitEventType) { + m_cthread.desktopInit(); + } + else if (e->type() == StatusChangeEventType) { + StatusChangeEvent *sce = (StatusChangeEvent*) e; + setStatus(sce->status()); + if (m_status == REMOTE_VIEW_CONNECTED) { + emit connected(); + setFocus(); + setMouseTracking(true); + } + else if (m_status == REMOTE_VIEW_DISCONNECTED) { + setMouseTracking(false); + emit disconnected(); + } + else if (m_status == REMOTE_VIEW_PREPARING) { + //Login was successfull: Write KWallet password if necessary. + if ( useKWallet && !password.isNull() && wallet && wallet->isOpen() && !wallet->hasEntry(host())) { + wallet->writePassword(host(), password); + } + delete wallet; wallet=0; + } + } + else if (e->type() == PasswordRequiredEventType) { + emit showingPasswordDialog(true); + + if (KPasswordDialog::getPassword(password, i18n("Access to the system requires a password.")) != KPasswordDialog::Accepted) + password = QCString(); + + emit showingPasswordDialog(false); + + passwordLock.lock(); // to guarantee that thread is waiting + passwordWaiter.wakeAll(); + passwordLock.unlock(); + } + else if (e->type() == WalletOpenEventType) { + QString krdc_folder = "KRDC-VNC"; + emit showingPasswordDialog(true); //Bad things happen if you don't do this. + + // Bugfix: Check if wallet has been closed by an outside source + if ( wallet && !wallet->isOpen() ) { + delete wallet; wallet=0; + } + + // Do we need to open the wallet? + if ( !wallet ) { + QString walletName = KWallet::Wallet::NetworkWallet(); + wallet = KWallet::Wallet::openWallet(walletName); + } + + if (wallet && wallet->isOpen()) { + bool walletOK = wallet->hasFolder(krdc_folder); + if (walletOK == false) { + walletOK = wallet->createFolder(krdc_folder); + } + + if (walletOK == true) { + wallet->setFolder(krdc_folder); + QString newPass; + if ( wallet->hasEntry(kvncview->host()) && !wallet->readPassword(kvncview->host(), newPass) ) { + password=newPass.latin1(); + } + } + } + + passwordLock.lock(); // to guarantee that thread is waiting + passwordWaiter.wakeAll(); + passwordLock.unlock(); + + emit showingPasswordDialog(false); + } + else if (e->type() == FatalErrorEventType) { + FatalErrorEvent *fee = (FatalErrorEvent*) e; + setStatus(REMOTE_VIEW_DISCONNECTED); + switch (fee->errorCode()) { + case ERROR_CONNECTION: + KMessageBox::error(0, + i18n("Connection attempt to host failed."), + i18n("Connection Failure")); + break; + case ERROR_PROTOCOL: + KMessageBox::error(0, + i18n("Remote host is using an incompatible protocol."), + i18n("Connection Failure")); + break; + case ERROR_IO: + KMessageBox::error(0, + i18n("The connection to the host has been interrupted."), + i18n("Connection Failure")); + break; + case ERROR_SERVER_BLOCKED: + KMessageBox::error(0, + i18n("Connection failed. The server does not accept new connections."), + i18n("Connection Failure")); + break; + case ERROR_NAME: + KMessageBox::error(0, + i18n("Connection failed. A server with the given name cannot be found."), + i18n("Connection Failure")); + break; + case ERROR_NO_SERVER: + KMessageBox::error(0, + i18n("Connection failed. No server running at the given address and port."), + i18n("Connection Failure")); + break; + case ERROR_AUTHENTICATION: + //Login failed: Remove wallet entry if there is one. + if ( useKWallet && wallet && wallet->isOpen() && wallet->hasEntry(host()) ) { + wallet->removeEntry(host()); + } + KMessageBox::error(0, + i18n("Authentication failed. Connection aborted."), + i18n("Authentication Failure")); + break; + default: + KMessageBox::error(0, + i18n("Unknown error."), + i18n("Unknown Error")); + break; + } + emit disconnectedError(); + } + else if (e->type() == BeepEventType) { + QApplication::beep(); + } + else if (e->type() == ServerCutEventType) { + ServerCutEvent *sce = (ServerCutEvent*) e; + QString ctext = QString::fromUtf8(sce->bytes(), sce->length()); + m_dontSendCb = true; + m_cb->setText(ctext, QClipboard::Clipboard); + m_cb->setText(ctext, QClipboard::Selection); + m_dontSendCb = false; + } + else if (e->type() == MouseStateEventType) { + MouseStateEvent *mse = (MouseStateEvent*) e; + emit mouseStateChanged(mse->x(), mse->y(), mse->buttonMask()); + bool show = m_plom.handlePointerEvent(mse->x(), mse->y()); + if (m_cursorState != DOT_CURSOR_ON) + showDotCursor(show ? DOT_CURSOR_AUTO : DOT_CURSOR_OFF); + } +} + +void KVncView::mouseEvent(QMouseEvent *e) { + if (m_status != REMOTE_VIEW_CONNECTED) + return; + if (m_viewOnly) + return; + + if ( e->type() != QEvent::MouseMove ) { + if ( (e->type() == QEvent::MouseButtonPress) || + (e->type() == QEvent::MouseButtonDblClick)) { + if ( e->button() & LeftButton ) + m_buttonMask |= 0x01; + if ( e->button() & MidButton ) + m_buttonMask |= 0x02; + if ( e->button() & RightButton ) + m_buttonMask |= 0x04; + } + else if ( e->type() == QEvent::MouseButtonRelease ) { + if ( e->button() & LeftButton ) + m_buttonMask &= 0xfe; + if ( e->button() & MidButton ) + m_buttonMask &= 0xfd; + if ( e->button() & RightButton ) + m_buttonMask &= 0xfb; + } + } + + int x = e->x(); + int y = e->y(); + m_plom.registerPointerState(x, y); + if (m_scaling) { + x = (x * m_framebufferSize.width()) / width(); + y = (y * m_framebufferSize.height()) / height(); + } + m_wthread.queueMouseEvent(x, y, m_buttonMask); + + if (m_enableClientCursor) + DrawCursorX11Thread(x, y); // in rfbproto.c +} + +void KVncView::mousePressEvent(QMouseEvent *e) { + mouseEvent(e); + e->accept(); +} + +void KVncView::mouseDoubleClickEvent(QMouseEvent *e) { + mouseEvent(e); + e->accept(); +} + +void KVncView::mouseReleaseEvent(QMouseEvent *e) { + mouseEvent(e); + e->accept(); +} + +void KVncView::mouseMoveEvent(QMouseEvent *e) { + mouseEvent(e); + e->ignore(); +} + +void KVncView::wheelEvent(QWheelEvent *e) { + if (m_status != REMOTE_VIEW_CONNECTED) + return; + if (m_viewOnly) + return; + + int eb = 0; + if ( e->delta() < 0 ) + eb |= 0x10; + else + eb |= 0x8; + + int x = e->pos().x(); + int y = e->pos().y(); + if (m_scaling) { + x = (x * m_framebufferSize.width()) / width(); + y = (y * m_framebufferSize.height()) / height(); + } + m_wthread.queueMouseEvent(x, y, eb|m_buttonMask); + m_wthread.queueMouseEvent(x, y, m_buttonMask); + e->accept(); +} + +void KVncView::pressKey(XEvent *xe) { + KKeyNative k(xe); + uint mod = k.mod(); + if (mod & KKeyNative::modX(KKey::SHIFT)) + m_wthread.queueKeyEvent(XK_Shift_L, true); + if (mod & KKeyNative::modX(KKey::CTRL)) + m_wthread.queueKeyEvent(XK_Control_L, true); + if (mod & KKeyNative::modX(KKey::ALT)) + m_wthread.queueKeyEvent(XK_Alt_L, true); + if (mod & KKeyNative::modX(KKey::WIN)) + m_wthread.queueKeyEvent(XK_Meta_L, true); + + m_wthread.queueKeyEvent(k.sym(), true); + m_wthread.queueKeyEvent(k.sym(), false); + + if (mod & KKeyNative::modX(KKey::WIN)) + m_wthread.queueKeyEvent(XK_Meta_L, false); + if (mod & KKeyNative::modX(KKey::ALT)) + m_wthread.queueKeyEvent(XK_Alt_L, false); + if (mod & KKeyNative::modX(KKey::CTRL)) + m_wthread.queueKeyEvent(XK_Control_L, false); + if (mod & KKeyNative::modX(KKey::SHIFT)) + m_wthread.queueKeyEvent(XK_Shift_L, false); + + m_mods.clear(); +} + +bool KVncView::x11Event(XEvent *e) { + bool pressed; + if (e->type == KeyPress) + pressed = true; + else if (e->type == KeyRelease) + pressed = false; + else + return QWidget::x11Event(e); + + if (!m_viewOnly) { + unsigned int s = KKeyNative(e).sym(); + + switch (s) { + case XK_Meta_L: + case XK_Alt_L: + case XK_Control_L: + case XK_Shift_L: + case XK_Meta_R: + case XK_Alt_R: + case XK_Control_R: + case XK_Shift_R: + if (pressed) + m_mods[s] = true; + else if (m_mods.contains(s)) + m_mods.remove(s); + else + unpressModifiers(); + } + m_wthread.queueKeyEvent(s, pressed); + } + return true; +} + +void KVncView::unpressModifiers() { + QValueList keys = m_mods.keys(); + QValueList::const_iterator it = keys.begin(); + while (it != keys.end()) { + m_wthread.queueKeyEvent(*it, false); + it++; + } + m_mods.clear(); +} + +void KVncView::focusOutEvent(QFocusEvent *) { + unpressModifiers(); +} + +QSize KVncView::sizeHint() { + return maximumSize(); +} + +void KVncView::setRemoteMouseTracking(bool s) { + m_remoteMouseTracking = s; +} + +bool KVncView::remoteMouseTracking() { + return m_remoteMouseTracking; +} + +void KVncView::clipboardChanged() { + if (m_status != REMOTE_VIEW_CONNECTED) + return; + + if (m_cb->ownsClipboard() || m_dontSendCb) + return; + + QString text = m_cb->text(QClipboard::Clipboard); + if (text.length() > MAX_SELECTION_LENGTH) + return; + + m_wthread.queueClientCut(text); +} + +void KVncView::selectionChanged() { + if (m_status != REMOTE_VIEW_CONNECTED) + return; + + if (m_cb->ownsSelection() || m_dontSendCb) + return; + + QString text = m_cb->text(QClipboard::Selection); + if (text.length() > MAX_SELECTION_LENGTH) + return; + + m_wthread.queueClientCut(text); +} + + +void KVncView::lockFramebuffer() { + if (m_enableFramebufferLocking) + m_framebufferLock.lock(); +} + +void KVncView::unlockFramebuffer() { + if (m_enableFramebufferLocking) + m_framebufferLock.unlock(); +} + +void KVncView::enableClientCursor(bool enable) { + if (enable) { + m_enableFramebufferLocking = true; // cant be turned off + } + m_enableClientCursor = enable; + lockFramebuffer(); + showDotCursorInternal(); + unlockFramebuffer(); +} + +/*! + \brief Get a password for this host. + Tries to get a password from the url or wallet if at all possible. If + both of these fail, it then asks the user to enter a password. + \note Lots of dialogs can be popped up during this process. The thread + locks and signals are there to protect against deadlocks and other + horribleness. Be careful making changes here. +*/ +int getPassword(char *passwd, int pwlen) { + int retV = 0; + + //Prepare the system + passwordLock.lock(); + + //Try #1: Did the user give a password in the URL? + if (!password.isNull()) { + retV = 1; //got it! + } + + //Try #2: Is there something in the wallet? + if ( !retV && useKWallet ) { + QApplication::postEvent(kvncview, new WalletOpenEvent()); + passwordWaiter.wait(&passwordLock); //block + if (!password.isNull()) retV = 1; //got it! + } + + //Last try: Ask the user + if (!retV) { + QApplication::postEvent(kvncview, new PasswordRequiredEvent()); + passwordWaiter.wait(&passwordLock); //block + if (!password.isNull()) retV = 1; //got it! + } + + //Process the password if we got it, clear it if we didn't + if (retV) { + strncpy(passwd, (const char*)password, pwlen); + } else { + passwd[0] = 0; + } + + //Pack up and go home + passwordLock.unlock(); + if (!retV) kvncview->startQuitting(); + + return retV; +} + +extern int isQuitFlagSet() { + return kvncview->isQuitting() ? 1 : 0; +} + +extern void DrawScreenRegion(int x, int y, int width, int height) { +/* KApplication::kApplication()->lock(); + kvncview->drawRegion(x, y, width, height); + KApplication::kApplication()->unlock(); +*/ + QApplication::postEvent(kvncview, new ScreenRepaintEvent(x, y, width, height)); +} + +// call only from x11 thread! +extern void DrawAnyScreenRegionX11Thread(int x, int y, int width, int height) { + kvncview->drawRegion(x, y, width, height); +} + +extern void EnableClientCursor(int enable) { + kvncview->enableClientCursor(enable); +} + +extern void LockFramebuffer() { + kvncview->lockFramebuffer(); +} + +extern void UnlockFramebuffer() { + kvncview->unlockFramebuffer(); +} + +extern void beep() { + QApplication::postEvent(kvncview, new BeepEvent()); +} + +extern void newServerCut(char *bytes, int length) { + QApplication::postEvent(kvncview, new ServerCutEvent(bytes, length)); +} + +extern void postMouseEvent(int x, int y, int buttonMask) { + QApplication::postEvent(kvncview, new MouseStateEvent(x, y, buttonMask)); +} + +#include "kvncview.moc" diff --git a/krdc/vnc/kvncview.h b/krdc/vnc/kvncview.h new file mode 100644 index 00000000..1b961f3d --- /dev/null +++ b/krdc/vnc/kvncview.h @@ -0,0 +1,121 @@ +/*************************************************************************** + kvncview.h - widget that shows the vnc client + ------------------- + begin : Thu Dec 20 15:11:42 CET 2001 + copyright : (C) 2001-2003 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KVNCVIEW_H +#define KVNCVIEW_H + +#include "kremoteview.h" +#include +#include + +#include "pointerlatencyometer.h" +#include "hostpreferences.h" +#include "vnctypes.h" +#include "threads.h" + +class QClipBoard; + +class KVncView : public KRemoteView +{ + Q_OBJECT +private: + ControllerThread m_cthread; + WriterThread m_wthread; + volatile bool m_quitFlag; // if set: all threads should die ASAP + QMutex m_framebufferLock; + bool m_enableFramebufferLocking; + bool m_enableClientCursor; + + QSize m_framebufferSize; + bool m_scaling; + bool m_remoteMouseTracking; + bool m_viewOnly; + + int m_buttonMask; + QMap m_mods; + + QString m_host; + int m_port; + + QClipboard *m_cb; + bool m_dontSendCb; + QCursor m_cursor; + DotCursorState m_cursorState; + PointerLatencyOMeter m_plom; + + void mouseEvent(QMouseEvent*); + unsigned long toKeySym(QKeyEvent *k); + bool checkLocalKRfb(); + void paintMessage(const QString &msg); + void showDotCursorInternal(); + void unpressModifiers(); + +protected: + void paintEvent(QPaintEvent*); + void customEvent(QCustomEvent*); + void mousePressEvent(QMouseEvent*); + void mouseDoubleClickEvent(QMouseEvent*); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent*); + void wheelEvent(QWheelEvent *); + void focusOutEvent(QFocusEvent *); + bool x11Event(XEvent*); + +public: + KVncView(QWidget* parent=0, const char *name=0, + const QString &host = QString(""), int port = 5900, + const QString &password = QString::null, + Quality quality = QUALITY_UNKNOWN, + DotCursorState dotCursorState = DOT_CURSOR_AUTO, + const QString &encodings = QString::null); + ~KVncView(); + QSize sizeHint(); + void drawRegion(int x, int y, int w, int h); + void lockFramebuffer(); + void unlockFramebuffer(); + void enableClientCursor(bool enable); + virtual bool scaling() const; + virtual bool supportsScaling() const; + virtual bool supportsLocalCursor() const; + virtual QSize framebufferSize(); + void setRemoteMouseTracking(bool s); + bool remoteMouseTracking(); + void configureApp(Quality q, const QString specialEncodings = QString::null); + void showDotCursor(DotCursorState state); + DotCursorState dotCursorState() const; + virtual void startQuitting(); + virtual bool isQuitting(); + virtual QString host(); + virtual int port(); + virtual bool start(); + + virtual bool viewOnly(); + + static bool editPreferences( HostPrefPtr ); + +public slots: + virtual void enableScaling(bool s); + virtual void setViewOnly(bool s); + virtual void pressKey(XEvent *k); + + +private slots: + void clipboardChanged(); + void selectionChanged(); +}; + +#endif diff --git a/krdc/vnc/pointerlatencyometer.h b/krdc/vnc/pointerlatencyometer.h new file mode 100644 index 00000000..559536b2 --- /dev/null +++ b/krdc/vnc/pointerlatencyometer.h @@ -0,0 +1,83 @@ +/*************************************************************************** + pointerlatencyometer.h - measuring pointer latency + ------------------- + begin : Wed Jun 30 12:04:44 CET 2002 + copyright : (C) 2002-2003 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include + +struct PointerState { + int x, y; + QTime timestamp; +}; + +class PointerLatencyOMeter { +private: + enum { stateCapacity = 30, maximumLatency = 1000 }; + PointerState states[stateCapacity]; + int firstState, stateNum; + float last3Latency, last20Latency; + +public: + PointerLatencyOMeter() : + firstState(0), + stateNum(0), + last3Latency(125), + last20Latency(25) { + } + + // registers a client pointer state + void registerPointerState(int x, int y) { + if (stateNum == stateCapacity) + stateNum--; + if (firstState == 0) + firstState = stateCapacity-1; + else + firstState--; + states[firstState].x = x; + states[firstState].y = y; + states[firstState].timestamp.start(); + stateNum++; + } + + /* Returns true if pointer should be visible */ + bool registerLatency(int msecs) { + last3Latency = ((last3Latency * 2.0) + msecs) / 3.0; + last20Latency = ((last20Latency * 19.0) + msecs) / 20.0; + + if (msecs >= maximumLatency) + return true; + if (last3Latency > (1000/4)) + return true; + return last20Latency > (1000/12); + } + + // Called with server-side coordinates. + // Returns true if pointer should be visible + bool handlePointerEvent(int x, int y) { + for (int i = stateNum-1; i >= 0; i--) { + int idx = (i+firstState) % stateCapacity; + if ((states[idx].x != x) || + (states[idx].y != y)) + continue; + + stateNum = i; + int l = states[idx].timestamp.elapsed(); + return registerLatency((l > maximumLatency) ? maximumLatency : l); + } + return true; + } + +}; diff --git a/krdc/vnc/rfbproto.c b/krdc/vnc/rfbproto.c new file mode 100644 index 00000000..e9ed5764 --- /dev/null +++ b/krdc/vnc/rfbproto.c @@ -0,0 +1,1335 @@ +/* + * Copyright (C) 2002, Tim Jansen. All Rights Reserved. + * Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved. + * Copyright (C) 2000 Tridia Corporation. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * rfbproto.c - functions to deal with client side of RFB protocol. + * tim@tjansen.de: - added softcursor encoding + * - changed various things for krdc + */ + +#include +#include +#include +#include "vncviewer.h" +#include "vncauth.h" +#include +#include + +static Bool HandleHextile8(int rx, int ry, int rw, int rh); +static Bool HandleHextile16(int rx, int ry, int rw, int rh); +static Bool HandleHextile32(int rx, int ry, int rw, int rh); +static Bool HandleZlib8(int rx, int ry, int rw, int rh); +static Bool HandleZlib16(int rx, int ry, int rw, int rh); +static Bool HandleZlib32(int rx, int ry, int rw, int rh); +static Bool HandleTight8(int rx, int ry, int rw, int rh); +static Bool HandleTight16(int rx, int ry, int rw, int rh); +static Bool HandleTight32(int rx, int ry, int rw, int rh); + +static long ReadCompactLen (void); + +static void JpegInitSource(j_decompress_ptr cinfo); +static boolean JpegFillInputBuffer(j_decompress_ptr cinfo); +static void JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes); +static void JpegTermSource(j_decompress_ptr cinfo); +static void JpegSetSrcManager(j_decompress_ptr cinfo, CARD8 *compressedData, + int compressedLen); + + +#define RGB24_TO_PIXEL(bpp,r,g,b) \ + ((((CARD##bpp)(r) & 0xFF) * myFormat.redMax + 127) / 255 \ + << myFormat.redShift | \ + (((CARD##bpp)(g) & 0xFF) * myFormat.greenMax + 127) / 255 \ + << myFormat.greenShift | \ + (((CARD##bpp)(b) & 0xFF) * myFormat.blueMax + 127) / 255 \ + << myFormat.blueShift) + +int rfbsock; +char *desktopName; +rfbPixelFormat myFormat; +rfbServerInitMsg si; + +int endianTest = 1; + +/* + * Softcursor variables + */ + +int cursorX, cursorY; +int imageIndex = -1; + +PointerImage pointerImages[rfbSoftCursorMaxImages]; + + +/* Hextile assumes it is big enough to hold 16 * 16 * 32 bits. + Tight encoding assumes BUFFER_SIZE is at least 16384 bytes. */ + +#define BUFFER_SIZE (16384) +static char buffer[BUFFER_SIZE]; + + +/* The zlib encoding requires expansion/decompression/deflation of the + compressed data in the "buffer" above into another, result buffer. + However, the size of the result buffer can be determined precisely + based on the bitsPerPixel, height and width of the rectangle. We + allocate this buffer one time to be the full size of the buffer. */ + +static int raw_buffer_size = -1; +static char *raw_buffer = NULL; + +static z_stream decompStream; +static Bool decompStreamInited = False; + + +/* + * Variables for the ``tight'' encoding implementation. + */ + +/* Separate buffer for compressed data. */ +#define ZLIB_BUFFER_SIZE 512 +static char zlib_buffer[ZLIB_BUFFER_SIZE]; + +/* Four independent compression streams for zlib library. */ +static z_stream zlibStream[4]; +static Bool zlibStreamActive[4] = { + False, False, False, False +}; + +/* Filter stuff. Should be initialized by filter initialization code. */ +static Bool cutZeros; +static int rectWidth, rectColors; +static char tightPalette[256*4]; +static CARD8 tightPrevRow[2048*3*sizeof(CARD16)]; + +/* JPEG decoder state. */ +static Bool jpegError; + +/* Maximum length for the cut buffer (16 MB)*/ +#define MAX_CUTBUFFER (1024*1024*16) + +/* Maximum length for the strings (64 kB)*/ +#define MAX_STRING (1024*64) + +/* Maximum length for the strings (32 MB)*/ +#define MAX_JPEG_SIZE (1024*1024*32) + + +/* + * ConnectToRFBServer. + */ + +int +ConnectToRFBServer(const char *hostname, int port) +{ + unsigned int host; + + if (!StringToIPAddr(hostname, &host)) { + fprintf(stderr,"Couldn't convert '%s' to host address\n", hostname); + return -(int)INIT_NAME_RESOLUTION_FAILURE; + } + + rfbsock = ConnectToTcpAddr(host, port); + if (rfbsock < 0) { + fprintf(stderr,"Unable to connect to VNC server\n"); + } + + return rfbsock; +} + + +/* + * InitialiseRFBConnection. + */ + +enum InitStatus +InitialiseRFBConnection() +{ + rfbProtocolVersionMsg pv; + int major,minor; + CARD32 authScheme, reasonLen, authResult; + char *reason; + CARD8 challenge[CHALLENGESIZE]; + char passwd[9]; + int i; + rfbClientInitMsg ci; + + /* if the connection is immediately closed, don't report anything, so + that pmw's monitor can make test connections */ + + if (!ReadFromRFBServer(pv, sz_rfbProtocolVersionMsg)) return INIT_SERVER_BLOCKED; + + errorMessageOnReadFailure = True; + + pv[sz_rfbProtocolVersionMsg] = 0; + + if (sscanf(pv,rfbProtocolVersionFormat,&major,&minor) != 2) { + fprintf(stderr,"Not a valid VNC server\n"); + return INIT_PROTOCOL_FAILURE; + } + + fprintf(stderr,"VNC server supports protocol version %d.%d (viewer %d.%d)\n", + major, minor, rfbProtocolMajorVersion, rfbProtocolMinorVersion); + + major = rfbProtocolMajorVersion; + minor = rfbProtocolMinorVersion; + + sprintf(pv,rfbProtocolVersionFormat,major,minor); + + if (!WriteExact(rfbsock, pv, sz_rfbProtocolVersionMsg)) return INIT_CONNECTION_FAILED; + + if (!ReadFromRFBServer((char *)&authScheme, 4)) return INIT_CONNECTION_FAILED; + + authScheme = Swap32IfLE(authScheme); + + switch (authScheme) { + + case rfbConnFailed: + if (!ReadFromRFBServer((char *)&reasonLen, 4)) return INIT_CONNECTION_FAILED; + reasonLen = Swap32IfLE(reasonLen); + + if (reasonLen > MAX_STRING) { + fprintf(stderr, "Connection failure reason too long.\n"); + return INIT_CONNECTION_FAILED; + } + + reason = malloc(reasonLen); + if (!reason) + return INIT_CONNECTION_FAILED; + + if (!ReadFromRFBServer(reason, reasonLen)) return INIT_CONNECTION_FAILED; + + fprintf(stderr,"VNC connection failed: %.*s\n",(int)reasonLen, reason); + free(reason); + return INIT_CONNECTION_FAILED; + + case rfbNoAuth: + fprintf(stderr,"No authentication needed\n"); + break; + + case rfbVncAuth: + if (!ReadFromRFBServer((char *)challenge, CHALLENGESIZE)) return INIT_CONNECTION_FAILED; + + if (!getPassword(passwd, 8)) + return INIT_ABORTED; + + passwd[8] = '\0'; + + vncEncryptBytes(challenge, passwd); + + /* Lose the password from memory */ + for (i = strlen(passwd); i >= 0; i--) { + passwd[i] = '\0'; + } + + if (!WriteExact(rfbsock, (char *)challenge, CHALLENGESIZE)) return INIT_CONNECTION_FAILED; + + if (!ReadFromRFBServer((char *)&authResult, 4)) return INIT_CONNECTION_FAILED; + + authResult = Swap32IfLE(authResult); + + switch (authResult) { + case rfbVncAuthOK: + fprintf(stderr,"VNC authentication succeeded\n"); + break; + case rfbVncAuthFailed: + fprintf(stderr,"VNC authentication failed\n"); + return INIT_AUTHENTICATION_FAILED; + case rfbVncAuthTooMany: + fprintf(stderr,"VNC authentication failed - too many tries\n"); + return INIT_AUTHENTICATION_FAILED; + default: + fprintf(stderr,"Unknown VNC authentication result: %d\n", + (int)authResult); + return INIT_CONNECTION_FAILED; + } + break; + + default: + fprintf(stderr,"Unknown authentication scheme from VNC server: %d\n", + (int)authScheme); + return INIT_CONNECTION_FAILED; + } + + ci.shared = (appData.shareDesktop ? 1 : 0); + + if (!WriteExact(rfbsock, (char *)&ci, sz_rfbClientInitMsg)) return INIT_CONNECTION_FAILED; + + if (!ReadFromRFBServer((char *)&si, sz_rfbServerInitMsg)) return INIT_CONNECTION_FAILED; + + si.framebufferWidth = Swap16IfLE(si.framebufferWidth); + si.framebufferHeight = Swap16IfLE(si.framebufferHeight); + si.format.redMax = Swap16IfLE(si.format.redMax); + si.format.greenMax = Swap16IfLE(si.format.greenMax); + si.format.blueMax = Swap16IfLE(si.format.blueMax); + si.nameLength = Swap32IfLE(si.nameLength); + + if ((si.framebufferWidth*si.framebufferHeight) > (4096*4096)) + return INIT_CONNECTION_FAILED; + + if (si.nameLength > MAX_STRING) { + fprintf(stderr, "Display name too long.\n"); + return INIT_CONNECTION_FAILED; + } + + desktopName = malloc(si.nameLength + 1); + if (!desktopName) { + fprintf(stderr, "Error allocating memory for desktop name, %lu bytes\n", + (unsigned long)si.nameLength); + return INIT_CONNECTION_FAILED; + } + + if (!ReadFromRFBServer(desktopName, si.nameLength)) return INIT_CONNECTION_FAILED; + + desktopName[si.nameLength] = 0; + + fprintf(stderr,"Desktop name \"%s\"\n",desktopName); + + fprintf(stderr,"Connected to VNC server, using protocol version %d.%d\n", + rfbProtocolMajorVersion, rfbProtocolMinorVersion); + + fprintf(stderr,"VNC server default format:\n"); + PrintPixelFormat(&si.format); + + return INIT_OK; +} + + +/* + * SetFormatAndEncodings. + */ + +Bool +SetFormatAndEncodings() +{ + rfbSetPixelFormatMsg spf; + char buf[sz_rfbSetEncodingsMsg + MAX_ENCODINGS * 4]; + rfbSetEncodingsMsg *se = (rfbSetEncodingsMsg *)buf; + CARD32 *encs = (CARD32 *)(&buf[sz_rfbSetEncodingsMsg]); + int len = 0; + Bool requestCompressLevel = False; + Bool requestQualityLevel = False; + Bool requestLastRectEncoding = False; + + spf.type = rfbSetPixelFormat; + spf.pad1 = 0; + spf.pad2 = 0; + spf.format = myFormat; + spf.format.redMax = Swap16IfLE(spf.format.redMax); + spf.format.greenMax = Swap16IfLE(spf.format.greenMax); + spf.format.blueMax = Swap16IfLE(spf.format.blueMax); + + if (!WriteExact(rfbsock, (char *)&spf, sz_rfbSetPixelFormatMsg)) + return False; + + se->type = rfbSetEncodings; + se->pad = 0; + se->nEncodings = 0; + + if (appData.encodingsString) { + const char *encStr = appData.encodingsString; + int encStrLen; + do { + char *nextEncStr = strchr(encStr, ' '); + if (nextEncStr) { + encStrLen = nextEncStr - encStr; + nextEncStr++; + } else { + encStrLen = strlen(encStr); + } + + if (strncasecmp(encStr,"raw",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRaw); + } else if (strncasecmp(encStr,"copyrect",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCopyRect); + } else if (strncasecmp(encStr,"softcursor",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingSoftCursor); + /* if server supports SoftCursor, it will ignore X/RichCursor + * and PointerPos */ + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingXCursor); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRichCursor); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingPointerPos); + } else if (strncasecmp(encStr,"background",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingBackground); + } else if (strncasecmp(encStr,"tight",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingTight); + requestLastRectEncoding = True; + if (appData.compressLevel >= 0 && appData.compressLevel <= 9) + requestCompressLevel = True; + if (appData.qualityLevel >= 0 && appData.qualityLevel <= 9) + requestQualityLevel = True; + } else if (strncasecmp(encStr,"hextile",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingHextile); + } else if (strncasecmp(encStr,"zlib",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZlib); + if (appData.compressLevel >= 0 && appData.compressLevel <= 9) + requestCompressLevel = True; + } else { + fprintf(stderr,"Unknown encoding '%.*s'\n",encStrLen,encStr); + } + + encStr = nextEncStr; + } while (encStr && se->nEncodings < MAX_ENCODINGS); + + if (se->nEncodings < MAX_ENCODINGS && requestCompressLevel) { + encs[se->nEncodings++] = Swap32IfLE(appData.compressLevel + + rfbEncodingCompressLevel0); + } + + if (se->nEncodings < MAX_ENCODINGS && requestQualityLevel) { + encs[se->nEncodings++] = Swap32IfLE(appData.qualityLevel + + rfbEncodingQualityLevel0); + } + + if (se->nEncodings < MAX_ENCODINGS && requestLastRectEncoding) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingLastRect); + } + } + else { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCopyRect); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingTight); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingHextile); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZlib); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRaw); + + if (appData.compressLevel >= 0 && appData.compressLevel <= 9) { + encs[se->nEncodings++] = Swap32IfLE(appData.compressLevel + + rfbEncodingCompressLevel0); + } + + if (appData.qualityLevel >= 0 && appData.qualityLevel <= 9) { + encs[se->nEncodings++] = Swap32IfLE(appData.qualityLevel + + rfbEncodingQualityLevel0); + } + + if (si.format.depth >= 8) + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingSoftCursor); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingLastRect); + } + + len = sz_rfbSetEncodingsMsg + se->nEncodings * 4; + + se->nEncodings = Swap16IfLE(se->nEncodings); + + if (!WriteExact(rfbsock, buf, len)) return False; + + return True; +} + + +/* + * SendIncrementalFramebufferUpdateRequest. + * Note: this should only be called by the WriterThread + */ + +Bool +SendIncrementalFramebufferUpdateRequest() +{ + return SendFramebufferUpdateRequest(0, 0, si.framebufferWidth, + si.framebufferHeight, True); +} + + +/* + * SendFramebufferUpdateRequest. + * Note: this should only be called by the WriterThread + */ + +Bool +SendFramebufferUpdateRequest(int x, int y, int w, int h, Bool incremental) +{ + rfbFramebufferUpdateRequestMsg fur; + + fur.type = rfbFramebufferUpdateRequest; + fur.incremental = incremental ? 1 : 0; + fur.x = Swap16IfLE(x); + fur.y = Swap16IfLE(y); + fur.w = Swap16IfLE(w); + fur.h = Swap16IfLE(h); + + if (!WriteExact(rfbsock, (char *)&fur, sz_rfbFramebufferUpdateRequestMsg)) + return False; + + return True; +} + + +/* + * SendPointerEvent. + * Note: this should only be called by the WriterThread + */ + +Bool +SendPointerEvent(int x, int y, int buttonMask) +{ + rfbPointerEventMsg pe; + + pe.type = rfbPointerEvent; + pe.buttonMask = buttonMask; + if (x < 0) x = 0; + if (y < 0) y = 0; + pe.x = Swap16IfLE(x); + pe.y = Swap16IfLE(y); + return WriteExact(rfbsock, (char *)&pe, sz_rfbPointerEventMsg); +} + + +/* + * SendKeyEvent. + * Note: this should only be called by the WriterThread + */ + +Bool +SendKeyEvent(CARD32 key, Bool down) +{ + rfbKeyEventMsg ke; + + ke.type = rfbKeyEvent; + ke.down = down ? 1 : 0; + ke.key = Swap32IfLE(key); + return WriteExact(rfbsock, (char *)&ke, sz_rfbKeyEventMsg); +} + + +/* + * SendClientCutText. + * Note: this should only be called by the WriterThread + */ + +Bool +SendClientCutText(const char *str, int len) +{ + rfbClientCutTextMsg cct; + + cct.type = rfbClientCutText; + cct.length = Swap32IfLE((unsigned int)len); + return (WriteExact(rfbsock, (char *)&cct, sz_rfbClientCutTextMsg) && + WriteExact(rfbsock, str, len)); +} + + +static Bool +HandleSoftCursorSetImage(rfbSoftCursorSetImage *msg, rfbRectangle *rect) +{ + int iindex = msg->imageIndex - rfbSoftCursorSetIconOffset; + PointerImage *pi = &pointerImages[iindex]; + if (iindex >= rfbSoftCursorMaxImages) { + fprintf(stderr, "Received invalid soft cursor image index %d for SetImage\n", iindex); + return False; + } + EnableClientCursor(0); + + if (pi->set && pi->image) + free(pi->image); + + pi->w = rect->w; + pi->h = rect->h; + pi->hotX = rect->x; + pi->hotY = rect->y; + pi->len = Swap16IfLE(msg->imageLength); + pi->image = malloc(pi->len); + if (!pi->image) { + fprintf(stderr, "out of memory (size=%d)\n", pi->len); + return False; + } + + if (!ReadFromRFBServer(pi->image, pi->len)) + return False; + pi->set = 1; + return True; +} + +/* framebuffer must be locked when calling this!!! */ +static Bool +PointerMove(unsigned int x, unsigned int y, unsigned int mask, + int ox, int oy, int ow, int oh) +{ + int nx, ny, nw, nh; + + if (x >= si.framebufferWidth) + x = si.framebufferWidth - 1; + if (y >= si.framebufferHeight) + y = si.framebufferHeight - 1; + + cursorX = x; + cursorY = y; + drawCursor(); + UnlockFramebuffer(); + + getBoundingRectCursor(cursorX, cursorY, imageIndex, + &nx, &ny, &nw, &nh); + + if (rectsIntersect(ox, oy, ow, oh, nx, ny, nw, nh)) { + rectsJoin(&ox, &oy, &ow, &oh, nx, ny, nw, nh); + SyncScreenRegion(ox, oy, ow, oh); + } + else { + SyncScreenRegion(ox, oy, ow, oh); + SyncScreenRegion(nx, ny, nw, nh); + } + + postMouseEvent(cursorX, cursorY, mask); + + return True; +} + +static Bool +HandleSoftCursorMove(rfbSoftCursorMove *msg, rfbRectangle *rect) +{ + int ii, ox, oy, ow, oh; + + /* get old cursor rect to know what to update */ + getBoundingRectCursor(cursorX, cursorY, imageIndex, + &ox, &oy, &ow, &oh); + + ii = msg->imageIndex; + if (ii >= rfbSoftCursorMaxImages) { + fprintf(stderr, "Received invalid soft cursor image index %d for Move\n", ii); + return False; + } + + if (!pointerImages[ii].set) + return True; + + LockFramebuffer(); + undrawCursor(); + imageIndex = ii; + + return PointerMove(rect->w, rect->h, msg->buttonMask, ox, oy, ow, oh); +} + +static Bool +HandleCursorPos(unsigned int x, unsigned int y) +{ + int ox, oy, ow, oh; + + /* get old cursor rect to know what to update */ + getBoundingRectCursor(cursorX, cursorY, imageIndex, + &ox, &oy, &ow, &oh); + if (!pointerImages[0].set) + return True; + + LockFramebuffer(); + undrawCursor(); + imageIndex = 0; + return PointerMove(x, y, 0, ox, oy, ow, oh); +} + +/* call only from X11 thread. Only updates framebuffer, does not sync! */ +void DrawCursorX11Thread(int x, int y) { + int ox, oy, ow, oh, nx, ny, nw, nh; + if (!pointerImages[0].set) + return True; + imageIndex = 0; + + if (x >= si.framebufferWidth) + x = si.framebufferWidth - 1; + if (y >= si.framebufferHeight) + y = si.framebufferHeight - 1; + + LockFramebuffer(); + getBoundingRectCursor(cursorX, cursorY, imageIndex, + &ox, &oy, &ow, &oh); + undrawCursor(); + cursorX = x; + cursorY = y; + drawCursor(); + UnlockFramebuffer(); + + getBoundingRectCursor(cursorX, cursorY, imageIndex, + &nx, &ny, &nw, &nh); + if (rectsIntersect(ox, oy, ow, oh, nx, ny, nw, nh)) { + rectsJoin(&ox, &oy, &ow, &oh, nx, ny, nw, nh); + SyncScreenRegionX11Thread(ox, oy, ow, oh); + } + else { + SyncScreenRegionX11Thread(ox, oy, ow, oh); + SyncScreenRegionX11Thread(nx, ny, nw, nh); + } +} + +/** + * Create a softcursor in the "compressed alpha" format. + * Returns the softcursor, caller owns the object + */ +static void *MakeSoftCursor(int bpp, int cursorWidth, int cursorHeight, + CARD8 *cursorData, CARD8 *cursorMask, short *imageLen) +{ + int w = (cursorWidth+7)/8; + unsigned char *cp, *sp, *dstData; + int state; /* 0 = transparent, 1 otherwise */ + CARD8 *counter; + unsigned char bit; + int i,j; + + sp = (unsigned char*)cursorData; + dstData = cp = (unsigned char*)calloc(cursorWidth*(bpp+2),cursorHeight); + if (!dstData) + return 0; + + state = 0; + counter = cp++; + *counter = 0; + + for(j=0;j>1) + if(cursorMask[j*w+i/8]&bit) { + if (state) { + memcpy(cp,sp,bpp); + cp += bpp; + sp += bpp; + (*counter)++; + if (*counter == 255) { + state = 0; + counter = cp++; + *counter = 0; + } + } + else { + state = 1; + counter = cp++; + *counter = 1; + memcpy(cp,sp,bpp); + cp += bpp; + sp += bpp; + } + } + else { + if (!state) { + (*counter)++; + if (*counter == 255) { + state = 1; + counter = cp++; + *counter = 0; + } + } + else { + state = 0; + counter = cp++; + *counter = 1; + } + sp += bpp; + } + + *imageLen = cp - dstData; + return (void*) dstData; +} + + +/********************************************************************* + * HandleCursorShape(). Support for XCursor and RichCursor shape + * updates. We emulate cursor operating on the frame buffer (that is + * why we call it "software cursor"). + ********************************************************************/ + +static Bool HandleCursorShape(int xhot, int yhot, int width, int height, CARD32 enc) +{ + int bytesPerPixel; + size_t bytesPerRow, bytesMaskData; + rfbXCursorColors rgb; + CARD32 colors[2]; + CARD8 *ptr, *rcSource, *rcMask; + void *softCursor; + int x, y, b; + int ox, oy, ow, oh; + PointerImage *pi; + short imageLen; + + bytesPerPixel = myFormat.bitsPerPixel / 8; + bytesPerRow = (width + 7) / 8; + bytesMaskData = bytesPerRow * height; + + if (width * height == 0) + return True; + + /* Allocate memory for pixel data and temporary mask data. */ + + rcSource = malloc(width * height * bytesPerPixel); + if (rcSource == NULL) + return False; + + rcMask = malloc(bytesMaskData); + if (rcMask == NULL) { + free(rcSource); + return False; + } + + /* Read and decode cursor pixel data, depending on the encoding type. */ + + if (enc == rfbEncodingXCursor) { + /* Read and convert background and foreground colors. */ + if (!ReadFromRFBServer((char *)&rgb, sz_rfbXCursorColors)) { + free(rcSource); + free(rcMask); + return False; + } + colors[0] = RGB24_TO_PIXEL(32, rgb.backRed, rgb.backGreen, rgb.backBlue); + colors[1] = RGB24_TO_PIXEL(32, rgb.foreRed, rgb.foreGreen, rgb.foreBlue); + + /* Read 1bpp pixel data into a temporary buffer. */ + if (!ReadFromRFBServer((char*)rcMask, bytesMaskData)) { + free(rcSource); + free(rcMask); + return False; + } + + /* Convert 1bpp data to byte-wide color indices. */ + ptr = rcSource; + for (y = 0; y < height; y++) { + for (x = 0; x < width / 8; x++) { + for (b = 7; b >= 0; b--) { + *ptr = rcMask[y * bytesPerRow + x] >> b & 1; + ptr += bytesPerPixel; + } + } + for (b = 7; b > 7 - width % 8; b--) { + *ptr = rcMask[y * bytesPerRow + x] >> b & 1; + ptr += bytesPerPixel; + } + } + + /* Convert indices into the actual pixel values. */ + switch (bytesPerPixel) { + case 1: + for (x = 0; x < width * height; x++) + rcSource[x] = (CARD8)colors[rcSource[x]]; + break; + case 2: + for (x = 0; x < width * height; x++) + ((CARD16 *)rcSource)[x] = (CARD16)colors[rcSource[x * 2]]; + break; + case 4: + for (x = 0; x < width * height; x++) + ((CARD32 *)rcSource)[x] = colors[rcSource[x * 4]]; + break; + } + + + } else { + if (!ReadFromRFBServer((char *)rcSource, width * height * bytesPerPixel)) { + free(rcSource); + free(rcMask); + return False; + } + } + + /* Read mask data. */ + + if (!ReadFromRFBServer((char*)rcMask, bytesMaskData)) { + free(rcSource); + free(rcMask); + return False; + } + + + /* Set the soft cursor. */ + softCursor = MakeSoftCursor(bytesPerPixel, width, height, rcSource, rcMask, &imageLen); + if (!softCursor) { + free(rcMask); + free(rcSource); + return False; + } + + /* get old cursor rect to know what to update */ + EnableClientCursor(1); + LockFramebuffer(); + getBoundingRectCursor(cursorX, cursorY, imageIndex, + &ox, &oy, &ow, &oh); + undrawCursor(); + + pi = &pointerImages[0]; + if (pi->set && pi->image) + free(pi->image); + pi->w = width; + pi->h = height; + pi->hotX = xhot; + pi->hotY = yhot; + pi->len = imageLen; + pi->image = softCursor; + pi->set = 1; + + imageIndex = 0; + + free(rcMask); + free(rcSource); + + return PointerMove(cursorX, cursorY, 0, ox, oy, ow, oh); +} + + + +/* + * HandleRFBServerMessage. + */ + +Bool +HandleRFBServerMessage() +{ + rfbServerToClientMsg msg; + if (!ReadFromRFBServer((char *)&msg, 1)) + return False; + + switch (msg.type) { + + case rfbSetColourMapEntries: + { + int i; + CARD16 rgb[3]; + XColor xc; + + if (!ReadFromRFBServer(((char *)&msg) + 1, + sz_rfbSetColourMapEntriesMsg - 1)) + return False; + + msg.scme.firstColour = Swap16IfLE(msg.scme.firstColour); + msg.scme.nColours = Swap16IfLE(msg.scme.nColours); + + for (i = 0; i < msg.scme.nColours; i++) { + if (!ReadFromRFBServer((char *)rgb, 6)) + return False; + xc.pixel = msg.scme.firstColour + i; + xc.red = Swap16IfLE(rgb[0]); + xc.green = Swap16IfLE(rgb[1]); + xc.blue = Swap16IfLE(rgb[2]); + xc.flags = DoRed|DoGreen|DoBlue; + /* Disable colormaps + lockQt(); + XStoreColor(dpy, cmap, &xc); + unlockQt(); + */ + } + + break; + } + + case rfbFramebufferUpdate: + { + rfbFramebufferUpdateRectHeader rect; + int linesToRead; + int bytesPerLine; + int i; + + announceIncrementalUpdateRequest(); + + if (!ReadFromRFBServer(((char *)&msg.fu) + 1, + sz_rfbFramebufferUpdateMsg - 1)) + return False; + + msg.fu.nRects = Swap16IfLE(msg.fu.nRects); + + for (i = 0; i < msg.fu.nRects; i++) { + if (!ReadFromRFBServer((char *)&rect, sz_rfbFramebufferUpdateRectHeader)) + return False; + + rect.encoding = Swap32IfLE(rect.encoding); + if (rect.encoding == rfbEncodingLastRect) + break; + + rect.r.x = Swap16IfLE(rect.r.x); + rect.r.y = Swap16IfLE(rect.r.y); + rect.r.w = Swap16IfLE(rect.r.w); + rect.r.h = Swap16IfLE(rect.r.h); + + if (rect.encoding == rfbEncodingPointerPos) { + if (!HandleCursorPos(rect.r.x, rect.r.y)) { + return False; + } + continue; + } + + if (rect.encoding == rfbEncodingXCursor || + rect.encoding == rfbEncodingRichCursor) { + if (!HandleCursorShape(rect.r.x, rect.r.y, rect.r.w, rect.r.h, + rect.encoding)) { + return False; + } + continue; + } + + if ((rect.r.x + rect.r.w > si.framebufferWidth) || + (rect.r.y + rect.r.h > si.framebufferHeight)) + { + fprintf(stderr,"Rect too large: %dx%d at (%d, %d)\n", + rect.r.w, rect.r.h, rect.r.x, rect.r.y); + return False; + } + + if ((rect.r.h * rect.r.w == 0) && + (rect.encoding != rfbEncodingSoftCursor)) { + fprintf(stderr,"Zero size rect - ignoring\n"); + continue; + } + + switch (rect.encoding) { + + case rfbEncodingRaw: + + bytesPerLine = rect.r.w * myFormat.bitsPerPixel / 8; + linesToRead = BUFFER_SIZE / bytesPerLine; + + while (rect.r.h > 0) { + if (linesToRead > rect.r.h) + linesToRead = rect.r.h; + + if (!ReadFromRFBServer(buffer,bytesPerLine * linesToRead)) + return False; + + CopyDataToScreen(buffer, rect.r.x, rect.r.y, rect.r.w, + linesToRead); + + rect.r.h -= linesToRead; + rect.r.y += linesToRead; + + } + break; + + case rfbEncodingCopyRect: + { + rfbCopyRect cr; + + if (!ReadFromRFBServer((char *)&cr, sz_rfbCopyRect)) + return False; + + cr.srcX = Swap16IfLE(cr.srcX); + cr.srcY = Swap16IfLE(cr.srcY); + + CopyArea(cr.srcX, cr.srcY, rect.r.w, rect.r.h, rect.r.x, rect.r.y); + + break; + } + + case rfbEncodingHextile: + { + switch (myFormat.bitsPerPixel) { + case 8: + if (!HandleHextile8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return False; + break; + case 16: + if (!HandleHextile16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return False; + break; + case 32: + if (!HandleHextile32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return False; + break; + } + break; + } + + case rfbEncodingZlib: + { + switch (myFormat.bitsPerPixel) { + case 8: + if (!HandleZlib8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return False; + break; + case 16: + if (!HandleZlib16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return False; + break; + case 32: + if (!HandleZlib32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return False; + break; + } + break; + } + + case rfbEncodingTight: + { + switch (myFormat.bitsPerPixel) { + case 8: + if (!HandleTight8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return False; + break; + case 16: + if (!HandleTight16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return False; + break; + case 32: + if (!HandleTight32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return False; + break; + } + break; + } + + case rfbEncodingSoftCursor: + { + rfbSoftCursorMsg scmsg; + if (!ReadFromRFBServer((char *)&scmsg, 1)) + return False; + if (scmsg.type < rfbSoftCursorMaxImages) { + if (!ReadFromRFBServer(((char *)&scmsg)+1, + sizeof(rfbSoftCursorMove)- 1)) + return False; + if (!HandleSoftCursorMove(&scmsg.move, &rect.r)) + return False; + } + else if((scmsg.type >= rfbSoftCursorSetIconOffset) && + (scmsg.type < rfbSoftCursorSetIconOffset+rfbSoftCursorMaxImages)) { + if (!ReadFromRFBServer(((char *)&scmsg)+1, + sizeof(rfbSoftCursorSetImage)- 1)) + return False; + if (!HandleSoftCursorSetImage(&scmsg.setImage, &rect.r)) + return False; + } + else { + fprintf(stderr,"Unknown soft cursor image index %d\n", + (int)scmsg.type); + return False; + } + break; + } + + default: + fprintf(stderr,"Unknown rect encoding %d\n", + (int)rect.encoding); + return False; + } + + } + + queueIncrementalUpdateRequest(); + + break; + } + + case rfbBell: + { + beep(); + break; + } + + case rfbServerCutText: + { + char *serverCutText; + if (!ReadFromRFBServer(((char *)&msg) + 1, + sz_rfbServerCutTextMsg - 1)) + return False; + + msg.sct.length = Swap32IfLE(msg.sct.length); + + if (msg.sct.length > MAX_CUTBUFFER) { + fprintf(stderr, "Cutbuffer too long.\n"); + return False; + } + + serverCutText = malloc(msg.sct.length+1); + + if (!serverCutText) { + fprintf(stderr, "Out-of-memory, cutbuffer too long.\n"); + return False; + } + + if (!ReadFromRFBServer(serverCutText, msg.sct.length)) + return False; + + serverCutText[msg.sct.length] = 0; + newServerCut(serverCutText, msg.sct.length); /* takes ownership of serverCutText */ + + break; + } + + default: + fprintf(stderr,"Unknown message type %d from VNC server\n",msg.type); + return False; + } + + return True; +} + + +#define GET_PIXEL8(pix, ptr) ((pix) = *(ptr)++) + +#define GET_PIXEL16(pix, ptr) (((CARD8*)&(pix))[0] = *(ptr)++, \ + ((CARD8*)&(pix))[1] = *(ptr)++) + +#define GET_PIXEL32(pix, ptr) (((CARD8*)&(pix))[0] = *(ptr)++, \ + ((CARD8*)&(pix))[1] = *(ptr)++, \ + ((CARD8*)&(pix))[2] = *(ptr)++, \ + ((CARD8*)&(pix))[3] = *(ptr)++) + +/* CONCAT2 concatenates its two arguments. CONCAT2E does the same but also + expands its arguments if they are macros */ + +#define CONCAT2(a,b) a##b +#define CONCAT2E(a,b) CONCAT2(a,b) + +#define BPP 8 +#include "hextile.c" +#include "zlib.c" +#include "tight.c" +#undef BPP +#define BPP 16 +#include "hextile.c" +#include "zlib.c" +#include "tight.c" +#undef BPP +#define BPP 32 +#include "hextile.c" +#include "zlib.c" +#include "tight.c" +#undef BPP + + +/* + * PrintPixelFormat. + */ + +void +PrintPixelFormat(format) + rfbPixelFormat *format; +{ + if (format->bitsPerPixel == 1) { + fprintf(stderr," Single bit per pixel.\n"); + fprintf(stderr, + " %s significant bit in each byte is leftmost on the screen.\n", + (format->bigEndian ? "Most" : "Least")); + } else { + fprintf(stderr," %d bits per pixel.\n",format->bitsPerPixel); + if (format->bitsPerPixel != 8) { + fprintf(stderr," %s significant byte first in each pixel.\n", + (format->bigEndian ? "Most" : "Least")); + } + if (format->trueColour) { + fprintf(stderr," True colour: max red %d green %d blue %d", + format->redMax, format->greenMax, format->blueMax); + fprintf(stderr,", shift red %d green %d blue %d\n", + format->redShift, format->greenShift, format->blueShift); + } else { + fprintf(stderr," Colour map (not true colour).\n"); + } + } +} + +static long +ReadCompactLen (void) +{ + long len; + CARD8 b; + + if (!ReadFromRFBServer((char *)&b, 1)) + return -1; + len = (int)b & 0x7F; + if (b & 0x80) { + if (!ReadFromRFBServer((char *)&b, 1)) + return -1; + len |= ((int)b & 0x7F) << 7; + if (b & 0x80) { + if (!ReadFromRFBServer((char *)&b, 1)) + return -1; + len |= ((int)b & 0xFF) << 14; + } + } + return len; +} + +void freeRFBProtoResources() { + int i; + + if (desktopName) + free(desktopName); + if (raw_buffer) + free(raw_buffer); + for (i = 0; i < rfbSoftCursorMaxImages; i++) + if (pointerImages[i].set && pointerImages[i].image) + free(pointerImages[i].image); + + raw_buffer_size = -1; + raw_buffer = NULL; + decompStreamInited = False; + zlibStreamActive[0] = False; + zlibStreamActive[1] = False; + zlibStreamActive[2] = False; + zlibStreamActive[3] = False; + for (i = 0; i < rfbSoftCursorMaxImages; i++) + pointerImages[i].set = 0; + imageIndex = -1; +} + +void freeResources() { + freeSocketsResources(); + freeDesktopResources(); + freeRFBProtoResources(); +} + +/* + * JPEG source manager functions for JPEG decompression in Tight decoder. + */ + +static struct jpeg_source_mgr jpegSrcManager; +static JOCTET *jpegBufferPtr; +static size_t jpegBufferLen; + +static void +JpegInitSource(j_decompress_ptr cinfo) +{ + jpegError = False; +} + +static boolean +JpegFillInputBuffer(j_decompress_ptr cinfo) +{ + jpegError = True; + jpegSrcManager.bytes_in_buffer = jpegBufferLen; + jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr; + + return TRUE; +} + +static void +JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes) +{ + if (num_bytes < 0 || num_bytes > jpegSrcManager.bytes_in_buffer) { + jpegError = True; + jpegSrcManager.bytes_in_buffer = jpegBufferLen; + jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr; + } else { + jpegSrcManager.next_input_byte += (size_t) num_bytes; + jpegSrcManager.bytes_in_buffer -= (size_t) num_bytes; + } +} + +static void +JpegTermSource(j_decompress_ptr cinfo) +{ + /* No work necessary here. */ +} + +static void +JpegSetSrcManager(j_decompress_ptr cinfo, CARD8 *compressedData, + int compressedLen) +{ + jpegBufferPtr = (JOCTET *)compressedData; + jpegBufferLen = (size_t)compressedLen; + + jpegSrcManager.init_source = JpegInitSource; + jpegSrcManager.fill_input_buffer = JpegFillInputBuffer; + jpegSrcManager.skip_input_data = JpegSkipInputData; + jpegSrcManager.resync_to_restart = jpeg_resync_to_restart; + jpegSrcManager.term_source = JpegTermSource; + jpegSrcManager.next_input_byte = jpegBufferPtr; + jpegSrcManager.bytes_in_buffer = jpegBufferLen; + + cinfo->src = &jpegSrcManager; +} + diff --git a/krdc/vnc/rfbproto.h b/krdc/vnc/rfbproto.h new file mode 100644 index 00000000..61ceb903 --- /dev/null +++ b/krdc/vnc/rfbproto.h @@ -0,0 +1,957 @@ +/* + * Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved. + * Copyright (C) 2000 Tridia Corporation. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * rfbproto.h - header file for the RFB protocol version 3.3 + * + * Uses types CARD for an n-bit unsigned integer, INT for an n-bit signed + * integer (for n = 8, 16 and 32). + * + * All multiple byte integers are in big endian (network) order (most + * significant byte first). Unless noted otherwise there is no special + * alignment of protocol structures. + * + * + * Once the initial handshaking is done, all messages start with a type byte, + * (usually) followed by message-specific data. The order of definitions in + * this file is as follows: + * + * (1) Structures used in several types of message. + * (2) Structures used in the initial handshaking. + * (3) Message types. + * (4) Encoding types. + * (5) For each message type, the form of the data following the type byte. + * Sometimes this is defined by a single structure but the more complex + * messages have to be explained by comments. + */ + +#include "vnctypes.h" + +/***************************************************************************** + * + * Structures used in several messages + * + *****************************************************************************/ + +/*----------------------------------------------------------------------------- + * Structure used to specify a rectangle. This structure is a multiple of 4 + * bytes so that it can be interspersed with 32-bit pixel data without + * affecting alignment. + */ + +typedef struct { + CARD16 x; + CARD16 y; + CARD16 w; + CARD16 h; +} rfbRectangle; + +#define sz_rfbRectangle 8 + + +/*----------------------------------------------------------------------------- + * Structure used to specify pixel format. + */ + +typedef struct { + + CARD8 bitsPerPixel; /* 8,16,32 only */ + + CARD8 depth; /* 8 to 32 */ + + CARD8 bigEndian; /* True if multi-byte pixels are interpreted + as big endian, or if single-bit-per-pixel + has most significant bit of the byte + corresponding to first (leftmost) pixel. Of + course this is meaningless for 8 bits/pix */ + + CARD8 trueColour; /* If false then we need a "colour map" to + convert pixels to RGB. If true, xxxMax and + xxxShift specify bits used for red, green + and blue */ + + /* the following fields are only meaningful if trueColour is true */ + + CARD16 redMax; /* maximum red value (= 2^n - 1 where n is the + number of bits used for red). Note this + value is always in big endian order. */ + + CARD16 greenMax; /* similar for green */ + + CARD16 blueMax; /* and blue */ + + CARD8 redShift; /* number of shifts needed to get the red + value in a pixel to the least significant + bit. To find the red value from a given + pixel, do the following: + 1) Swap pixel value according to bigEndian + (e.g. if bigEndian is false and host byte + order is big endian, then swap). + 2) Shift right by redShift. + 3) AND with redMax (in host byte order). + 4) You now have the red value between 0 and + redMax. */ + + CARD8 greenShift; /* similar for green */ + + CARD8 blueShift; /* and blue */ + + CARD8 pad1; + CARD16 pad2; + +} rfbPixelFormat; + +#define sz_rfbPixelFormat 16 + + + +/***************************************************************************** + * + * Initial handshaking messages + * + *****************************************************************************/ + +/*----------------------------------------------------------------------------- + * Protocol Version + * + * The server always sends 12 bytes to start which identifies the latest RFB + * protocol version number which it supports. These bytes are interpreted + * as a string of 12 ASCII characters in the format "RFB xxx.yyy\n" where + * xxx and yyy are the major and minor version numbers (for version 3.3 + * this is "RFB 003.003\n"). + * + * The client then replies with a similar 12-byte message giving the version + * number of the protocol which should actually be used (which may be different + * to that quoted by the server). + * + * It is intended that both clients and servers may provide some level of + * backwards compatibility by this mechanism. Servers in particular should + * attempt to provide backwards compatibility, and even forwards compatibility + * to some extent. For example if a client demands version 3.1 of the + * protocol, a 3.0 server can probably assume that by ignoring requests for + * encoding types it doesn't understand, everything will still work OK. This + * will probably not be the case for changes in the major version number. + * + * The format string below can be used in sprintf or sscanf to generate or + * decode the version string respectively. + */ + +#define rfbProtocolVersionFormat "RFB %03d.%03d\n" +#define rfbProtocolMajorVersion 3 +#define rfbProtocolMinorVersion 3 + +typedef char rfbProtocolVersionMsg[13]; /* allow extra byte for null */ + +#define sz_rfbProtocolVersionMsg 12 + + +/*----------------------------------------------------------------------------- + * Authentication + * + * Once the protocol version has been decided, the server then sends a 32-bit + * word indicating whether any authentication is needed on the connection. + * The value of this word determines the authentication scheme in use. For + * version 3.0 of the protocol this may have one of the following values: + */ + +#define rfbConnFailed 0 +#define rfbNoAuth 1 +#define rfbVncAuth 2 + +/* + * rfbConnFailed: For some reason the connection failed (e.g. the server + * cannot support the desired protocol version). This is + * followed by a string describing the reason (where a + * string is specified as a 32-bit length followed by that + * many ASCII characters). + * + * rfbNoAuth: No authentication is needed. + * + * rfbVncAuth: The VNC authentication scheme is to be used. A 16-byte + * challenge follows, which the client encrypts as + * appropriate using the password and sends the resulting + * 16-byte response. If the response is correct, the + * server sends the 32-bit word rfbVncAuthOK. If a simple + * failure happens, the server sends rfbVncAuthFailed and + * closes the connection. If the server decides that too + * many failures have occurred, it sends rfbVncAuthTooMany + * and closes the connection. In the latter case, the + * server should not allow an immediate reconnection by + * the client. + */ + +#define rfbVncAuthOK 0 +#define rfbVncAuthFailed 1 +#define rfbVncAuthTooMany 2 + + +/*----------------------------------------------------------------------------- + * Client Initialisation Message + * + * Once the client and server are sure that they're happy to talk to one + * another, the client sends an initialisation message. At present this + * message only consists of a boolean indicating whether the server should try + * to share the desktop by leaving other clients connected, or give exclusive + * access to this client by disconnecting all other clients. + */ + +typedef struct { + CARD8 shared; +} rfbClientInitMsg; + +#define sz_rfbClientInitMsg 1 + + +/*----------------------------------------------------------------------------- + * Server Initialisation Message + * + * After the client initialisation message, the server sends one of its own. + * This tells the client the width and height of the server's framebuffer, + * its pixel format and the name associated with the desktop. + */ + +typedef struct { + CARD16 framebufferWidth; + CARD16 framebufferHeight; + rfbPixelFormat format; /* the server's preferred pixel format */ + CARD32 nameLength; + /* followed by char name[nameLength] */ +} rfbServerInitMsg; + +#define sz_rfbServerInitMsg (8 + sz_rfbPixelFormat) + + +/* + * Following the server initialisation message it's up to the client to send + * whichever protocol messages it wants. Typically it will send a + * SetPixelFormat message and a SetEncodings message, followed by a + * FramebufferUpdateRequest. From then on the server will send + * FramebufferUpdate messages in response to the client's + * FramebufferUpdateRequest messages. The client should send + * FramebufferUpdateRequest messages with incremental set to true when it has + * finished processing one FramebufferUpdate and is ready to process another. + * With a fast client, the rate at which FramebufferUpdateRequests are sent + * should be regulated to avoid hogging the network. + */ + + + +/***************************************************************************** + * + * Message types + * + *****************************************************************************/ + +/* server -> client */ + +#define rfbFramebufferUpdate 0 +#define rfbSetColourMapEntries 1 +#define rfbBell 2 +#define rfbServerCutText 3 + + +/* client -> server */ + +#define rfbSetPixelFormat 0 +#define rfbFixColourMapEntries 1 /* not currently supported */ +#define rfbSetEncodings 2 +#define rfbFramebufferUpdateRequest 3 +#define rfbKeyEvent 4 +#define rfbPointerEvent 5 +#define rfbClientCutText 6 + + + + +/***************************************************************************** + * + * Encoding types + * + *****************************************************************************/ + +#define rfbEncodingRaw 0 +#define rfbEncodingCopyRect 1 +#define rfbEncodingRRE 2 +#define rfbEncodingCoRRE 4 +#define rfbEncodingHextile 5 +#define rfbEncodingZlib 6 +#define rfbEncodingTight 7 +#define rfbEncodingZlibHex 8 + +/* + * Special encoding numbers: + * 0xFFFFFF00 .. 0xFFFFFF0F -- encoding-specific compression levels; + * 0xFFFFFF10 .. 0xFFFFFF1F -- mouse cursor shape data; + * 0xFFFFFF20 .. 0xFFFFFF2F -- various protocol extensions; + * 0xFFFFFF30 .. 0xFFFFFFDF -- not allocated yet; + * 0xFFFFFFE0 .. 0xFFFFFFEF -- quality level for JPEG compressor; + * 0xFFFFFFF0 .. 0xFFFFFFFF -- cross-encoding compression levels. + */ + +#define rfbEncodingCompressLevel0 0xFFFFFF00 +#define rfbEncodingCompressLevel1 0xFFFFFF01 +#define rfbEncodingCompressLevel2 0xFFFFFF02 +#define rfbEncodingCompressLevel3 0xFFFFFF03 +#define rfbEncodingCompressLevel4 0xFFFFFF04 +#define rfbEncodingCompressLevel5 0xFFFFFF05 +#define rfbEncodingCompressLevel6 0xFFFFFF06 +#define rfbEncodingCompressLevel7 0xFFFFFF07 +#define rfbEncodingCompressLevel8 0xFFFFFF08 +#define rfbEncodingCompressLevel9 0xFFFFFF09 + +#define rfbEncodingXCursor 0xFFFFFF10 +#define rfbEncodingRichCursor 0xFFFFFF11 +#define rfbEncodingSoftCursor 0xFFFFFF12 +#define rfbEncodingPointerPos 0xFFFFFF18 + +#define rfbEncodingLastRect 0xFFFFFF20 +#define rfbEncodingBackground 0xFFFFFF25 + +#define rfbEncodingQualityLevel0 0xFFFFFFE0 +#define rfbEncodingQualityLevel1 0xFFFFFFE1 +#define rfbEncodingQualityLevel2 0xFFFFFFE2 +#define rfbEncodingQualityLevel3 0xFFFFFFE3 +#define rfbEncodingQualityLevel4 0xFFFFFFE4 +#define rfbEncodingQualityLevel5 0xFFFFFFE5 +#define rfbEncodingQualityLevel6 0xFFFFFFE6 +#define rfbEncodingQualityLevel7 0xFFFFFFE7 +#define rfbEncodingQualityLevel8 0xFFFFFFE8 +#define rfbEncodingQualityLevel9 0xFFFFFFE9 + + +/***************************************************************************** + * + * Server -> client message definitions + * + *****************************************************************************/ + + +/*----------------------------------------------------------------------------- + * FramebufferUpdate - a block of rectangles to be copied to the framebuffer. + * + * This message consists of a header giving the number of rectangles of pixel + * data followed by the rectangles themselves. The header is padded so that + * together with the type byte it is an exact multiple of 4 bytes (to help + * with alignment of 32-bit pixels): + */ + +typedef struct { + CARD8 type; /* always rfbFramebufferUpdate */ + CARD8 pad; + CARD16 nRects; + /* followed by nRects rectangles */ +} rfbFramebufferUpdateMsg; + +#define sz_rfbFramebufferUpdateMsg 4 + +/* + * Each rectangle of pixel data consists of a header describing the position + * and size of the rectangle and a type word describing the encoding of the + * pixel data, followed finally by the pixel data. Note that if the client has + * not sent a SetEncodings message then it will only receive raw pixel data. + * Also note again that this structure is a multiple of 4 bytes. + */ + +typedef struct { + rfbRectangle r; + CARD32 encoding; /* one of the encoding types rfbEncoding... */ +} rfbFramebufferUpdateRectHeader; + +#define sz_rfbFramebufferUpdateRectHeader (sz_rfbRectangle + 4) + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * Raw Encoding. Pixels are sent in top-to-bottom scanline order, + * left-to-right within a scanline with no padding in between. + */ + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * CopyRect Encoding. The pixels are specified simply by the x and y position + * of the source rectangle. + */ + +typedef struct { + CARD16 srcX; + CARD16 srcY; +} rfbCopyRect; + +#define sz_rfbCopyRect 4 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * RRE - Rise-and-Run-length Encoding. We have an rfbRREHeader structure + * giving the number of subrectangles following. Finally the data follows in + * the form [...] where each is + * []. + */ + +typedef struct { + CARD32 nSubrects; +} rfbRREHeader; + +#define sz_rfbRREHeader 4 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * CoRRE - Compact RRE Encoding. We have an rfbRREHeader structure giving + * the number of subrectangles following. Finally the data follows in the form + * [...] where each is + * []. This means that + * the whole rectangle must be at most 255x255 pixels. + */ + +typedef struct { + CARD8 x; + CARD8 y; + CARD8 w; + CARD8 h; +} rfbCoRRERectangle; + +#define sz_rfbCoRRERectangle 4 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * Hextile Encoding. The rectangle is divided up into "tiles" of 16x16 pixels, + * starting at the top left going in left-to-right, top-to-bottom order. If + * the width of the rectangle is not an exact multiple of 16 then the width of + * the last tile in each row will be correspondingly smaller. Similarly if the + * height is not an exact multiple of 16 then the height of each tile in the + * final row will also be smaller. Each tile begins with a "subencoding" type + * byte, which is a mask made up of a number of bits. If the Raw bit is set + * then the other bits are irrelevant; w*h pixel values follow (where w and h + * are the width and height of the tile). Otherwise the tile is encoded in a + * similar way to RRE, except that the position and size of each subrectangle + * can be specified in just two bytes. The other bits in the mask are as + * follows: + * + * BackgroundSpecified - if set, a pixel value follows which specifies + * the background colour for this tile. The first non-raw tile in a + * rectangle must have this bit set. If this bit isn't set then the + * background is the same as the last tile. + * + * ForegroundSpecified - if set, a pixel value follows which specifies + * the foreground colour to be used for all subrectangles in this tile. + * If this bit is set then the SubrectsColoured bit must be zero. + * + * AnySubrects - if set, a single byte follows giving the number of + * subrectangles following. If not set, there are no subrectangles (i.e. + * the whole tile is just solid background colour). + * + * SubrectsColoured - if set then each subrectangle is preceded by a pixel + * value giving the colour of that subrectangle. If not set, all + * subrectangles are the same colour, the foreground colour; if the + * ForegroundSpecified bit wasn't set then the foreground is the same as + * the last tile. + * + * The position and size of each subrectangle is specified in two bytes. The + * Pack macros below can be used to generate the two bytes from x, y, w, h, + * and the Extract macros can be used to extract the x, y, w, h values from + * the two bytes. + */ + +#define rfbHextileRaw (1 << 0) +#define rfbHextileBackgroundSpecified (1 << 1) +#define rfbHextileForegroundSpecified (1 << 2) +#define rfbHextileAnySubrects (1 << 3) +#define rfbHextileSubrectsColoured (1 << 4) + +#define rfbHextilePackXY(x,y) (((x) << 4) | (y)) +#define rfbHextilePackWH(w,h) ((((w)-1) << 4) | ((h)-1)) +#define rfbHextileExtractX(byte) ((byte) >> 4) +#define rfbHextileExtractY(byte) ((byte) & 0xf) +#define rfbHextileExtractW(byte) (((byte) >> 4) + 1) +#define rfbHextileExtractH(byte) (((byte) & 0xf) + 1) + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * zlib - zlib compressed Encoding. We have an rfbZlibHeader structure + * giving the number of bytes following. Finally the data follows is + * zlib compressed version of the raw pixel data as negotiated. + */ + +typedef struct { + CARD32 nBytes; +} rfbZlibHeader; + +#define sz_rfbZlibHeader 4 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * Tight Encoding. + * + *-- The first byte of each Tight-encoded rectangle is a "compression control + * byte". Its format is as follows (bit 0 is the least significant one): + * + * bit 0: if 1, then compression stream 0 should be reset; + * bit 1: if 1, then compression stream 1 should be reset; + * bit 2: if 1, then compression stream 2 should be reset; + * bit 3: if 1, then compression stream 3 should be reset; + * bits 7-4: if 1000 (0x08), then the compression type is "fill", + * if 1001 (0x09), then the compression type is "jpeg", + * if 0xxx, then the compression type is "basic", + * values greater than 1001 are not valid. + * + * If the compression type is "basic", then bits 6..4 of the + * compression control byte (those xxx in 0xxx) specify the following: + * + * bits 5-4: decimal representation is the index of a particular zlib + * stream which should be used for decompressing the data; + * bit 6: if 1, then a "filter id" byte is following this byte. + * + *-- The data that follows after the compression control byte described + * above depends on the compression type ("fill", "jpeg" or "basic"). + * + *-- If the compression type is "fill", then the only pixel value follows, in + * client pixel format (see NOTE 1). This value applies to all pixels of the + * rectangle. + * + *-- If the compression type is "jpeg", the following data stream looks like + * this: + * + * 1..3 bytes: data size (N) in compact representation; + * N bytes: JPEG image. + * + * Data size is compactly represented in one, two or three bytes, according + * to the following scheme: + * + * 0xxxxxxx (for values 0..127) + * 1xxxxxxx 0yyyyyyy (for values 128..16383) + * 1xxxxxxx 1yyyyyyy zzzzzzzz (for values 16384..4194303) + * + * Here each character denotes one bit, xxxxxxx are the least significant 7 + * bits of the value (bits 0-6), yyyyyyy are bits 7-13, and zzzzzzzz are the + * most significant 8 bits (bits 14-21). For example, decimal value 10000 + * should be represented as two bytes: binary 10010000 01001110, or + * hexadecimal 90 4E. + * + *-- If the compression type is "basic" and bit 6 of the compression control + * byte was set to 1, then the next (second) byte specifies "filter id" which + * tells the decoder what filter type was used by the encoder to pre-process + * pixel data before the compression. The "filter id" byte can be one of the + * following: + * + * 0: no filter ("copy" filter); + * 1: "palette" filter; + * 2: "gradient" filter. + * + *-- If bit 6 of the compression control byte is set to 0 (no "filter id" + * byte), or if the filter id is 0, then raw pixel values in the client + * format (see NOTE 1) will be compressed. See below details on the + * compression. + * + *-- The "gradient" filter pre-processes pixel data with a simple algorithm + * which converts each color component to a difference between a "predicted" + * intensity and the actual intensity. Such a technique does not affect + * uncompressed data size, but helps to compress photo-like images better. + * Pseudo-code for converting intensities to differences is the following: + * + * P[i,j] := V[i-1,j] + V[i,j-1] - V[i-1,j-1]; + * if (P[i,j] < 0) then P[i,j] := 0; + * if (P[i,j] > MAX) then P[i,j] := MAX; + * D[i,j] := V[i,j] - P[i,j]; + * + * Here V[i,j] is the intensity of a color component for a pixel at + * coordinates (i,j). MAX is the maximum value of intensity for a color + * component. + * + *-- The "palette" filter converts true-color pixel data to indexed colors + * and a palette which can consist of 2..256 colors. If the number of colors + * is 2, then each pixel is encoded in 1 bit, otherwise 8 bits is used to + * encode one pixel. 1-bit encoding is performed such way that the most + * significant bits correspond to the leftmost pixels, and each raw of pixels + * is aligned to the byte boundary. When "palette" filter is used, the + * palette is sent before the pixel data. The palette begins with an unsigned + * byte which value is the number of colors in the palette minus 1 (i.e. 1 + * means 2 colors, 255 means 256 colors in the palette). Then follows the + * palette itself which consist of pixel values in client pixel format (see + * NOTE 1). + * + *-- The pixel data is compressed using the zlib library. But if the data + * size after applying the filter but before the compression is less then 12, + * then the data is sent as is, uncompressed. Four separate zlib streams + * (0..3) can be used and the decoder should read the actual stream id from + * the compression control byte (see NOTE 2). + * + * If the compression is not used, then the pixel data is sent as is, + * otherwise the data stream looks like this: + * + * 1..3 bytes: data size (N) in compact representation; + * N bytes: zlib-compressed data. + * + * Data size is compactly represented in one, two or three bytes, just like + * in the "jpeg" compression method (see above). + * + *-- NOTE 1. If the color depth is 24, and all three color components are + * 8-bit wide, then one pixel in Tight encoding is always represented by + * three bytes, where the first byte is red component, the second byte is + * green component, and the third byte is blue component of the pixel color + * value. This applies to colors in palettes as well. + * + *-- NOTE 2. The decoder must reset compression streams' states before + * decoding the rectangle, if some of bits 0,1,2,3 in the compression control + * byte are set to 1. Note that the decoder must reset zlib streams even if + * the compression type is "fill" or "jpeg". + * + *-- NOTE 3. The "gradient" filter and "jpeg" compression may be used only + * when bits-per-pixel value is either 16 or 32, not 8. + * + *-- NOTE 4. The width of any Tight-encoded rectangle cannot exceed 2048 + * pixels. If a rectangle is wider, it must be split into several rectangles + * and each one should be encoded separately. + * + */ + +#define rfbTightExplicitFilter 0x04 +#define rfbTightFill 0x08 +#define rfbTightJpeg 0x09 +#define rfbTightMaxSubencoding 0x09 + +/* Filters to improve compression efficiency */ +#define rfbTightFilterCopy 0x00 +#define rfbTightFilterPalette 0x01 +#define rfbTightFilterGradient 0x02 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * XCursor encoding. This is a special encoding used to transmit X-style + * cursor shapes from server to clients. Note that for this encoding, + * coordinates in rfbFramebufferUpdateRectHeader structure hold hotspot + * position (r.x, r.y) and cursor size (r.w, r.h). If (w * h != 0), two RGB + * samples are sent after header in the rfbXCursorColors structure. They + * denote foreground and background colors of the cursor. If a client + * supports only black-and-white cursors, it should ignore these colors and + * assume that foreground is black and background is white. Next, two bitmaps + * (1 bits per pixel) follow: first one with actual data (value 0 denotes + * background color, value 1 denotes foreground color), second one with + * transparency data (bits with zero value mean that these pixels are + * transparent). Both bitmaps represent cursor data in a byte stream, from + * left to right, from top to bottom, and each row is byte-aligned. Most + * significant bits correspond to leftmost pixels. The number of bytes in + * each row can be calculated as ((w + 7) / 8). If (w * h == 0), cursor + * should be hidden (or default local cursor should be set by the client). + */ + +typedef struct { + CARD8 foreRed; + CARD8 foreGreen; + CARD8 foreBlue; + CARD8 backRed; + CARD8 backGreen; + CARD8 backBlue; +} rfbXCursorColors; + +#define sz_rfbXCursorColors 6 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * RichCursor encoding. This is a special encoding used to transmit cursor + * shapes from server to clients. It is similar to the XCursor encoding but + * uses client pixel format instead of two RGB colors to represent cursor + * image. For this encoding, coordinates in rfbFramebufferUpdateRectHeader + * structure hold hotspot position (r.x, r.y) and cursor size (r.w, r.h). + * After header, two pixmaps follow: first one with cursor image in current + * client pixel format (like in raw encoding), second with transparency data + * (1 bit per pixel, exactly the same format as used for transparency bitmap + * in the XCursor encoding). If (w * h == 0), cursor should be hidden (or + * default local cursor should be set by the client). + */ + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * SoftCursor encoding. This encoding is used to transmit image and position + * of the remote cursor. It has two sub-messages: SetImage is used to upload + * one of 16 images, and Move selects the image and sets the position of the + * cursor. + * Each SoftCursor message starts with a CARD8. If it is in the 0-15 range + * it specifies the number of the cursor image and is followed by the + * rfbSoftCursorMove message. If the given cursor has not been set yet the + * message will be ignored. If the first CARD8 is in the 128-143 range it + * specifies the cursor that will be set in the following + * rfbSoftCursorSetImage message. To hide the cursor send a SetImage + * message with width and height 0 and imageLength 0. + * SetImage transmits the hotspot coordinates in the x/y fields of the + * rfbFramebufferUpdateRectHeader, width and height of the image are in the + * header's width and height fields. + * Move transmits the pointer coordinates in the w/h fields of the + * header, x/y are always 0. + */ + +typedef struct { + CARD8 imageIndex; + CARD8 buttonMask; /* bits 0-7 are buttons 1-8, 0=up, 1=down */ +} rfbSoftCursorMove; + +typedef struct { + CARD8 imageIndex; + CARD8 padding; + CARD16 imageLength; + /* + * Followed by an image of the cursor in the client's image format + * with the following RLE mask compression. It begins with CARD8 that + * specifies the number of mask'ed pixels that will be NOT transmitted. + * Then follows a CARD8 that specified by the number of unmask'd pixels + * that will be transmitted next. Then a CARD8 with the number of mask'd + * pixels and so on. + */ +} rfbSoftCursorSetImage; + +typedef union { + CARD8 type; + rfbSoftCursorMove move; + rfbSoftCursorSetImage setImage; +} rfbSoftCursorMsg; + +#define rfbSoftCursorMaxImages 16 +#define rfbSoftCursorSetIconOffset 128 + +/*----------------------------------------------------------------------------- + * SetColourMapEntries - these messages are only sent if the pixel + * format uses a "colour map" (i.e. trueColour false) and the client has not + * fixed the entire colour map using FixColourMapEntries. In addition they + * will only start being sent after the client has sent its first + * FramebufferUpdateRequest. So if the client always tells the server to use + * trueColour then it never needs to process this type of message. + */ + +typedef struct { + CARD8 type; /* always rfbSetColourMapEntries */ + CARD8 pad; + CARD16 firstColour; + CARD16 nColours; + + /* Followed by nColours * 3 * CARD16 + r1, g1, b1, r2, g2, b2, r3, g3, b3, ..., rn, bn, gn */ + +} rfbSetColourMapEntriesMsg; + +#define sz_rfbSetColourMapEntriesMsg 6 + + + +/*----------------------------------------------------------------------------- + * Bell - ring a bell on the client if it has one. + */ + +typedef struct { + CARD8 type; /* always rfbBell */ +} rfbBellMsg; + +#define sz_rfbBellMsg 1 + + + +/*----------------------------------------------------------------------------- + * ServerCutText - the server has new text in its cut buffer. + */ + +typedef struct { + CARD8 type; /* always rfbServerCutText */ + CARD8 pad1; + CARD16 pad2; + CARD32 length; + /* followed by char text[length] */ +} rfbServerCutTextMsg; + +#define sz_rfbServerCutTextMsg 8 + + +/*----------------------------------------------------------------------------- + * Union of all server->client messages. + */ + +typedef union { + CARD8 type; + rfbFramebufferUpdateMsg fu; + rfbSetColourMapEntriesMsg scme; + rfbBellMsg b; + rfbServerCutTextMsg sct; +} rfbServerToClientMsg; + + + +/***************************************************************************** + * + * Message definitions (client -> server) + * + *****************************************************************************/ + + +/*----------------------------------------------------------------------------- + * SetPixelFormat - tell the RFB server the format in which the client wants + * pixels sent. + */ + +typedef struct { + CARD8 type; /* always rfbSetPixelFormat */ + CARD8 pad1; + CARD16 pad2; + rfbPixelFormat format; +} rfbSetPixelFormatMsg; + +#define sz_rfbSetPixelFormatMsg (sz_rfbPixelFormat + 4) + + +/*----------------------------------------------------------------------------- + * FixColourMapEntries - when the pixel format uses a "colour map", fix + * read-only colour map entries. + * + * ***************** NOT CURRENTLY SUPPORTED ***************** + */ + +typedef struct { + CARD8 type; /* always rfbFixColourMapEntries */ + CARD8 pad; + CARD16 firstColour; + CARD16 nColours; + + /* Followed by nColours * 3 * CARD16 + r1, g1, b1, r2, g2, b2, r3, g3, b3, ..., rn, bn, gn */ + +} rfbFixColourMapEntriesMsg; + +#define sz_rfbFixColourMapEntriesMsg 6 + + +/*----------------------------------------------------------------------------- + * SetEncodings - tell the RFB server which encoding types we accept. Put them + * in order of preference, if we have any. We may always receive raw + * encoding, even if we don't specify it here. + */ + +typedef struct { + CARD8 type; /* always rfbSetEncodings */ + CARD8 pad; + CARD16 nEncodings; + /* followed by nEncodings * CARD32 encoding types */ +} rfbSetEncodingsMsg; + +#define sz_rfbSetEncodingsMsg 4 + + +/*----------------------------------------------------------------------------- + * FramebufferUpdateRequest - request for a framebuffer update. If incremental + * is true then the client just wants the changes since the last update. If + * false then it wants the whole of the specified rectangle. + */ + +typedef struct { + CARD8 type; /* always rfbFramebufferUpdateRequest */ + CARD8 incremental; + CARD16 x; + CARD16 y; + CARD16 w; + CARD16 h; +} rfbFramebufferUpdateRequestMsg; + +#define sz_rfbFramebufferUpdateRequestMsg 10 + + +/*----------------------------------------------------------------------------- + * KeyEvent - key press or release + * + * Keys are specified using the "keysym" values defined by the X Window System. + * For most ordinary keys, the keysym is the same as the corresponding ASCII + * value. Other common keys are: + * + * BackSpace 0xff08 + * Tab 0xff09 + * Return or Enter 0xff0d + * Escape 0xff1b + * Insert 0xff63 + * Delete 0xffff + * Home 0xff50 + * End 0xff57 + * Page Up 0xff55 + * Page Down 0xff56 + * Left 0xff51 + * Up 0xff52 + * Right 0xff53 + * Down 0xff54 + * F1 0xffbe + * F2 0xffbf + * ... ... + * F12 0xffc9 + * Shift 0xffe1 + * Control 0xffe3 + * Meta 0xffe7 + * Alt 0xffe9 + */ + +typedef struct { + CARD8 type; /* always rfbKeyEvent */ + CARD8 down; /* true if down (press), false if up */ + CARD16 pad; + CARD32 key; /* key is specified as an X keysym */ +} rfbKeyEventMsg; + +#define sz_rfbKeyEventMsg 8 + + +/*----------------------------------------------------------------------------- + * PointerEvent - mouse/pen move and/or button press. + */ + +typedef struct { + CARD8 type; /* always rfbPointerEvent */ + CARD8 buttonMask; /* bits 0-7 are buttons 1-8, 0=up, 1=down */ + CARD16 x; + CARD16 y; +} rfbPointerEventMsg; + +#define rfbButton1Mask 1 +#define rfbButton2Mask 2 +#define rfbButton3Mask 4 + +#define sz_rfbPointerEventMsg 6 + + + +/*----------------------------------------------------------------------------- + * ClientCutText - the client has new text in its cut buffer. + */ + +typedef struct { + CARD8 type; /* always rfbClientCutText */ + CARD8 pad1; + CARD16 pad2; + CARD32 length; + /* followed by char text[length] */ +} rfbClientCutTextMsg; + +#define sz_rfbClientCutTextMsg 8 + + + +/*----------------------------------------------------------------------------- + * Union of all client->server messages. + */ + +typedef union { + CARD8 type; + rfbSetPixelFormatMsg spf; + rfbFixColourMapEntriesMsg fcme; + rfbSetEncodingsMsg se; + rfbFramebufferUpdateRequestMsg fur; + rfbKeyEventMsg ke; + rfbPointerEventMsg pe; + rfbClientCutTextMsg cct; +} rfbClientToServerMsg; diff --git a/krdc/vnc/sockets.c b/krdc/vnc/sockets.c new file mode 100644 index 00000000..797dd22d --- /dev/null +++ b/krdc/vnc/sockets.c @@ -0,0 +1,325 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * 03-05-2002 tim@tjansen.de: removed Xt event processing for krdc + */ + +/* + * sockets.c - functions to deal with sockets. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vncviewer.h" + +void PrintInHex(char *buf, int len); + +Bool errorMessageOnReadFailure = True; + +#define BUF_SIZE 8192 +static char buf[BUF_SIZE]; +static char *bufoutptr = buf; +static unsigned int buffered = 0; + +/* Wait duration of select in seconds */ +#define SELECT_PERIOD 3 + + +/* + * ReadFromRFBServer is called whenever we want to read some data from the RFB + * server. + */ +Bool +ReadFromRFBServer(char *out, unsigned int n) +{ + fd_set fds; + int e; + struct timeval tx; + + if (isQuitFlagSet()) + return False; + + if (n <= buffered) { + memcpy(out, bufoutptr, n); + bufoutptr += n; + buffered -= n; + return True; + } + + memcpy(out, bufoutptr, buffered); + + out += buffered; + n -= buffered; + + bufoutptr = buf; + buffered = 0; + + if (n <= BUF_SIZE) { + + while (buffered < n) { + int i; + if (isQuitFlagSet()) + return False; + i = read(rfbsock, buf + buffered, BUF_SIZE - buffered); + + if (i <= 0) { + if (i < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + FD_ZERO(&fds); + FD_SET(rfbsock,&fds); + + tx.tv_sec = SELECT_PERIOD; + tx.tv_usec = 0; + if ((e=select(rfbsock+1, &fds, NULL, &fds, &tx)) < 0) { + perror("krdc: select read"); + return False; + } + i = 0; + } else { + perror("krdc: read"); + return False; + } + } else { + fprintf(stderr,"VNC server closed connection\n"); + return False; + } + } + buffered += i; + } + + memcpy(out, bufoutptr, n); + bufoutptr += n; + buffered -= n; + return isQuitFlagSet() ? False : True; + + } else { + + while (n > 0) { + int i; + if (isQuitFlagSet()) + return False; + i = read(rfbsock, out, n); + if (i <= 0) { + if (i < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + FD_ZERO(&fds); + FD_SET(rfbsock,&fds); + + tx.tv_sec = SELECT_PERIOD; + tx.tv_usec = 0; + if ((e=select(rfbsock+1, &fds, NULL, &fds, &tx)) < 0) { + perror("krdc: select"); + return False; + } + i = 0; + } else { + perror("krdc: read"); + return False; + } + } else { + fprintf(stderr,"VNC server closed connection\n"); + return False; + } + } + out += i; + n -= i; + } + + return isQuitFlagSet() ? False : True; + } +} + + +/* + * Write an exact number of bytes, and don't return until you've sent them. + * Note: this should only be called by the WriterThread + */ + +Bool +WriteExact(int sock, const char *_buf, int n) +{ + fd_set fds; + int i = 0; + int j; + int e; + struct timeval tx; + + while (i < n) { + if (isQuitFlagSet()) + return False; + j = write(sock, _buf + i, (n - i)); + if (j <= 0) { + if (j < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + FD_ZERO(&fds); + FD_SET(rfbsock,&fds); + + tx.tv_sec = SELECT_PERIOD; + tx.tv_usec = 0; + if ((e=select(rfbsock+1, NULL, &fds, NULL, &tx)) < 0) { + perror("krdc: select write"); + return False; + } + j = 0; + } else { + perror("krdc: write"); + return False; + } + } else { + fprintf(stderr,"write failed\n"); + return False; + } + } + i += j; + } + return True; +} + + +/* + * ConnectToTcpAddr connects to the given TCP port. + */ + +int +ConnectToTcpAddr(unsigned int host, int port) +{ + int sock; + struct sockaddr_in addr; + int one = 1; + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = host; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + perror("krdc: ConnectToTcpAddr: socket"); + return -(int)INIT_CONNECTION_FAILED; + } + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("krdc: ConnectToTcpAddr: connect"); + close(sock); + return -(int)INIT_NO_SERVER; + } + + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + (char *)&one, sizeof(one)) < 0) { + perror("krdc: ConnectToTcpAddr: setsockopt"); + close(sock); + return -(int)INIT_CONNECTION_FAILED; + } + + if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { + perror(": AcceptTcpConnection: fcntl"); + close(sock); + return -(int)INIT_CONNECTION_FAILED; + } + + return sock; +} + + +/* + * StringToIPAddr - convert a host string to an IP address. + */ + +Bool +StringToIPAddr(const char *str, unsigned int *addr) +{ + struct hostent *hp; + + if (strcmp(str,"") == 0) { + *addr = 0; /* local */ + return True; + } + + *addr = inet_addr(str); + + if (*addr != -1) + return True; + + hp = gethostbyname(str); + + if (hp) { + *addr = *(unsigned int *)hp->h_addr; + return True; + } + + return False; +} + + +/* + * Print out the contents of a packet for debugging. + */ + +void +PrintInHex(char *_buf, int len) +{ + int i, j; + char c, str[17]; + + str[16] = 0; + + fprintf(stderr,"ReadExact: "); + + for (i = 0; i < len; i++) + { + if ((i % 16 == 0) && (i != 0)) { + fprintf(stderr," "); + } + c = _buf[i]; + str[i % 16] = (((c > 31) && (c < 127)) ? c : '.'); + fprintf(stderr,"%02x ",(unsigned char)c); + if ((i % 4) == 3) + fprintf(stderr," "); + if ((i % 16) == 15) + { + fprintf(stderr,"%s\n",str); + } + } + if ((i % 16) != 0) + { + for (j = i % 16; j < 16; j++) + { + fprintf(stderr," "); + if ((j % 4) == 3) fprintf(stderr," "); + } + str[i % 16] = 0; + fprintf(stderr,"%s\n",str); + } + + fflush(stderr); +} + +void freeSocketsResources() { + close(rfbsock); + + errorMessageOnReadFailure = True; + bufoutptr = buf; + buffered = 0; +} + diff --git a/krdc/vnc/threads.cpp b/krdc/vnc/threads.cpp new file mode 100644 index 00000000..fe5a1d62 --- /dev/null +++ b/krdc/vnc/threads.cpp @@ -0,0 +1,392 @@ +/*************************************************************************** + threads.cpp - threads + ------------------- + begin : Thu May 09 17:01:44 CET 2002 + copyright : (C) 2002 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kvncview.h" + +#include +#include + +#include "vncviewer.h" +#include "threads.h" + +#include + +// Maximum idle time for writer thread in ms. When it timeouts, it will request +// another incremental update. Must be smaller than the timeout of the server +// (krfb's is 20s). +static const int MAXIMUM_WAIT_PERIOD = 8000; + +// time to postpone incremental updates that have not been requested explicitly +static const int POSTPONED_INCRRQ_WAIT_PERIOD = 110; + +static const int MOUSEPRESS_QUEUE_SIZE = 5; +static const int MOUSEMOVE_QUEUE_SIZE = 3; +static const int KEY_QUEUE_SIZE = 8192; + + +ControllerThread::ControllerThread(KVncView *v, WriterThread &wt, volatile bool &quitFlag) : + m_view(v), + m_status(REMOTE_VIEW_CONNECTING), + m_wthread(wt), + m_quitFlag(quitFlag), + m_desktopInitialized(false) +{ +} + +void ControllerThread::changeStatus(RemoteViewStatus s) { + m_status = s; + QApplication::postEvent(m_view, new StatusChangeEvent(s)); +} + +void ControllerThread::sendFatalError(ErrorCode s) { + m_quitFlag = true; + QApplication::postEvent(m_view, new FatalErrorEvent(s)); + m_wthread.kick(); +} + +/* + * Calls this from the X11 thread + */ +void ControllerThread::desktopInit() { + SetVisualAndCmap(); + ToplevelInit(); + DesktopInit(m_view->winId()); + m_desktopInitialized = true; + m_waiter.wakeAll(); +} + +void ControllerThread::kick() { + m_waiter.wakeAll(); +} + +void ControllerThread::run() { + int fd; + fd = ConnectToRFBServer(m_view->host().latin1(), m_view->port()); + if (fd < 0) { + if (fd == -(int)INIT_NO_SERVER) + sendFatalError(ERROR_NO_SERVER); + else if (fd == -(int)INIT_NAME_RESOLUTION_FAILURE) + sendFatalError(ERROR_NAME); + else + sendFatalError(ERROR_CONNECTION); + return; + } + if (m_quitFlag) { + changeStatus(REMOTE_VIEW_DISCONNECTED); + return; + } + + changeStatus(REMOTE_VIEW_AUTHENTICATING); + + enum InitStatus s = InitialiseRFBConnection(); + if (s != INIT_OK) { + if (s == INIT_CONNECTION_FAILED) + sendFatalError(ERROR_IO); + else if (s == INIT_SERVER_BLOCKED) + sendFatalError(ERROR_SERVER_BLOCKED); + else if (s == INIT_PROTOCOL_FAILURE) + sendFatalError(ERROR_PROTOCOL); + else if (s == INIT_AUTHENTICATION_FAILED) + sendFatalError(ERROR_AUTHENTICATION); + else if (s == INIT_ABORTED) + changeStatus(REMOTE_VIEW_DISCONNECTED); + else + sendFatalError(ERROR_INTERNAL); + return; + } + + QApplication::postEvent(m_view, + new ScreenResizeEvent(si.framebufferWidth, + si.framebufferHeight)); + m_wthread.queueUpdateRequest(QRegion(QRect(0,0,si.framebufferWidth, + si.framebufferHeight))); + + QApplication::postEvent(m_view, new DesktopInitEvent()); + while ((!m_quitFlag) && (!m_desktopInitialized)) + m_waiter.wait(1000); + + if (m_quitFlag) { + changeStatus(REMOTE_VIEW_DISCONNECTED); + return; + } + + changeStatus(REMOTE_VIEW_PREPARING); + + if (!SetFormatAndEncodings()) { + sendFatalError(ERROR_INTERNAL); + return; + } + + changeStatus(REMOTE_VIEW_CONNECTED); + + m_wthread.start(); + + while (!m_quitFlag) { + if ((!HandleRFBServerMessage()) && (!m_quitFlag)) { + sendFatalError(ERROR_IO); + return; + } + } + + m_quitFlag = true; + changeStatus(REMOTE_VIEW_DISCONNECTED); + m_wthread.kick(); +} + +enum RemoteViewStatus ControllerThread::status() { + return m_status; +} + + + + + +static WriterThread *writerThread; +void queueIncrementalUpdateRequest() { + writerThread->queueIncrementalUpdateRequest(); +} + +void announceIncrementalUpdateRequest() { + writerThread->announceIncrementalUpdateRequest(); +} + + +WriterThread::WriterThread(KVncView *v, volatile bool &quitFlag) : + m_quitFlag(quitFlag), + m_view(v), + m_lastIncrUpdatePostponed(false), + m_incrementalUpdateRQ(false), + m_incrementalUpdateAnnounced(false), + m_mouseEventNum(0), + m_keyEventNum(0), + m_clientCut(QString::null) +{ + writerThread = this; + m_lastIncrUpdate.start(); +} + +bool WriterThread::sendIncrementalUpdateRequest() { + m_lastIncrUpdate.restart(); + return SendIncrementalFramebufferUpdateRequest(); +} + +bool WriterThread::sendUpdateRequest(const QRegion ®ion) { + QMemArray r = region.rects(); + for (unsigned int i = 0; i < r.size(); i++) + if (!SendFramebufferUpdateRequest(r[i].x(), + r[i].y(), + r[i].width(), + r[i].height(), False)) + return false; + return true; +} + +bool WriterThread::sendInputEvents(const QValueList &events) { + QValueList::const_iterator it = events.begin(); + while (it != events.end()) { + if ((*it).type == KeyEventType) { + if (!SendKeyEvent((*it).e.k.k, (*it).e.k.down ? True : False)) + return false; + } + else + if (!SendPointerEvent((*it).e.m.x, (*it).e.m.y, (*it).e.m.buttons)) + return false; + it++; + } + return true; +} + +void WriterThread::queueIncrementalUpdateRequest() { + m_lock.lock(); + m_incrementalUpdateRQ = true; + m_waiter.wakeAll(); + m_lock.unlock(); +} + +void WriterThread::announceIncrementalUpdateRequest() { + m_lock.lock(); + m_incrementalUpdateAnnounced = true; + m_lock.unlock(); +} + + +void WriterThread::queueUpdateRequest(const QRegion &r) { + m_lock.lock(); + m_updateRegionRQ += r; + m_waiter.wakeAll(); + m_lock.unlock(); +} + +void WriterThread::queueMouseEvent(int x, int y, int buttonMask) { + InputEvent e; + e.type = MouseEventType; + e.e.m.x = x; + e.e.m.y = y; + e.e.m.buttons = buttonMask; + + m_lock.lock(); + if (m_mouseEventNum > 0) { + if ((e.e.m.x == m_lastMouseEvent.x) && + (e.e.m.y == m_lastMouseEvent.y) && + (e.e.m.buttons == m_lastMouseEvent.buttons)) { + m_lock.unlock(); + return; + } + if (m_mouseEventNum >= MOUSEPRESS_QUEUE_SIZE) { + m_lock.unlock(); + return; + } + if ((m_lastMouseEvent.buttons == buttonMask) && + (m_mouseEventNum >= MOUSEMOVE_QUEUE_SIZE)) { + m_lock.unlock(); + return; + } + } + + m_mouseEventNum++; + m_lastMouseEvent = e.e.m; + + m_inputEvents.push_back(e); + m_waiter.wakeAll(); + m_lock.unlock(); +} + +void WriterThread::queueKeyEvent(unsigned int k, bool down) { + InputEvent e; + e.type = KeyEventType; + e.e.k.k = k; + e.e.k.down = down; + + m_lock.lock(); + if (m_keyEventNum >= KEY_QUEUE_SIZE) { + m_lock.unlock(); + return; + } + + m_keyEventNum++; + m_inputEvents.push_back(e); + m_waiter.wakeAll(); + m_lock.unlock(); +} + +void WriterThread::queueClientCut(const QString &text) { + m_lock.lock(); + + m_clientCut = text; + + m_waiter.wakeAll(); + m_lock.unlock(); +} + +void WriterThread::kick() { + m_waiter.wakeAll(); +} + +void WriterThread::run() { + bool incrementalUpdateRQ = false; + bool incrementalUpdateAnnounced = false; + QRegion updateRegionRQ; + QValueList inputEvents; + QString clientCut; + + while (!m_quitFlag) { + m_lock.lock(); + incrementalUpdateRQ = m_incrementalUpdateRQ; + incrementalUpdateAnnounced = m_incrementalUpdateAnnounced; + updateRegionRQ = m_updateRegionRQ; + inputEvents = m_inputEvents; + clientCut = m_clientCut; + + if ((!incrementalUpdateRQ) && + (updateRegionRQ.isNull()) && + (inputEvents.size() == 0) && + (clientCut.isNull())) { + if (!m_waiter.wait(&m_lock, + m_lastIncrUpdatePostponed ? + POSTPONED_INCRRQ_WAIT_PERIOD : MAXIMUM_WAIT_PERIOD)) + m_incrementalUpdateRQ = true; + m_lock.unlock(); + } + else { + m_incrementalUpdateRQ = false; + m_incrementalUpdateAnnounced = false; + m_updateRegionRQ = QRegion(); + m_inputEvents.clear(); + m_keyEventNum = 0; + m_mouseEventNum = 0; + m_clientCut = QString::null; + m_lock.unlock(); + + // always send incremental update, unless + // a) a framebuffer update is done ATM and will do the request later, or + // b) the last unrequested update has been done less than 0.1s ago + // + // if the update has not been done because of b, postpone it. + if (incrementalUpdateRQ || !incrementalUpdateAnnounced) { + bool sendUpdate; + if (incrementalUpdateRQ) { + sendUpdate = true; + m_lastIncrUpdatePostponed = false; + } + else { + if (m_lastIncrUpdate.elapsed() < 100) { + sendUpdate = false; + m_lastIncrUpdatePostponed = true; + } + else { + sendUpdate = true; + m_lastIncrUpdatePostponed = false; + } + } + + if (sendUpdate) + if (!sendIncrementalUpdateRequest()) { + sendFatalError(ERROR_IO); + break; + } + } + else + m_lastIncrUpdatePostponed = false; + + if (!updateRegionRQ.isNull()) + if (!sendUpdateRequest(updateRegionRQ)) { + sendFatalError(ERROR_IO); + break; + } + if (inputEvents.size() != 0) + if (!sendInputEvents(inputEvents)) { + sendFatalError(ERROR_IO); + break; + } + if (!clientCut.isNull()) { + QCString cutTextUtf8(clientCut.utf8()); + if (!SendClientCutText(cutTextUtf8.data(), + (int)cutTextUtf8.length())) { + sendFatalError(ERROR_IO); + break; + } + } + } + } + m_quitFlag = true; +} + +void WriterThread::sendFatalError(ErrorCode s) { + m_quitFlag = true; + QApplication::postEvent(m_view, new FatalErrorEvent(s)); +} + diff --git a/krdc/vnc/threads.h b/krdc/vnc/threads.h new file mode 100644 index 00000000..5f38b71f --- /dev/null +++ b/krdc/vnc/threads.h @@ -0,0 +1,126 @@ +/*************************************************************************** + threads.h - threads for kvncview + ------------------- + begin : Thu May 09 16:01:42 CET 2002 + copyright : (C) 2002 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef THREADS_H +#define THREADS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "events.h" +#include "vnctypes.h" + +class KVncView; + +enum EventType { + MouseEventType, + KeyEventType +}; + + +struct MouseEvent { + int x, y, buttons; +}; + +struct KeyEvent { + unsigned int k; + bool down; +}; + +struct InputEvent { + EventType type; + union { + MouseEvent m; + KeyEvent k; + } e; +}; + + +class WriterThread : public QThread { +private: + QMutex m_lock; + QWaitCondition m_waiter; + volatile bool &m_quitFlag; + KVncView *m_view; + + QTime m_lastIncrUpdate; // start()ed when a incr update is sent + bool m_lastIncrUpdatePostponed; + + // all things that can be send follow: + bool m_incrementalUpdateRQ; // for sending an incremental request + bool m_incrementalUpdateAnnounced; // set when a RQ will come soon + QRegion m_updateRegionRQ; // for sending updates, null if it is done + QValueList m_inputEvents; // list of unsent input events + MouseEvent m_lastMouseEvent; + int m_mouseEventNum, m_keyEventNum; + QString m_clientCut; + + void sendFatalError(ErrorCode s); + +public: + WriterThread(KVncView *v, volatile bool &quitFlag); + + void queueIncrementalUpdateRequest(); + void announceIncrementalUpdateRequest(); + void queueUpdateRequest(const QRegion &r); + void queueMouseEvent(int x, int y, int buttonMask); + void queueKeyEvent(unsigned int k, bool down); + void queueClientCut(const QString &text); + void kick(); + +protected: + void run(); + bool sendIncrementalUpdateRequest(); + bool sendUpdateRequest(const QRegion &r); + bool sendInputEvents(const QValueList &events); +}; + + + +class ControllerThread : public QThread { +private: + KVncView *m_view; + enum RemoteViewStatus m_status; + WriterThread &m_wthread; + volatile bool &m_quitFlag; + volatile bool m_desktopInitialized; + QWaitCondition m_waiter; + + void changeStatus(RemoteViewStatus s); + void sendFatalError(ErrorCode s); + +public: + ControllerThread(KVncView *v, WriterThread &wt, volatile bool &quitFlag); + enum RemoteViewStatus status(); + void desktopInit(); + void kick(); + +protected: + void run(); +}; + + + +#endif diff --git a/krdc/vnc/tight.c b/krdc/vnc/tight.c new file mode 100644 index 00000000..2f08cfb0 --- /dev/null +++ b/krdc/vnc/tight.c @@ -0,0 +1,610 @@ +/* + * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * tight.c - handle ``tight'' encoding. + * + * This file shouldn't be compiled directly. It is included multiple + * times by rfbproto.c, each time with a different definition of the + * macro BPP. For each value of BPP, this file defines a function + * which handles a tight-encoded rectangle with BPP bits per pixel. + * + */ + +#define TIGHT_MIN_TO_COMPRESS 12 + +#define CARDBPP CONCAT2E(CARD,BPP) +#define filterPtrBPP CONCAT2E(filterPtr,BPP) + +#define HandleTightBPP CONCAT2E(HandleTight,BPP) +#define InitFilterCopyBPP CONCAT2E(InitFilterCopy,BPP) +#define InitFilterPaletteBPP CONCAT2E(InitFilterPalette,BPP) +#define InitFilterGradientBPP CONCAT2E(InitFilterGradient,BPP) +#define FilterCopyBPP CONCAT2E(FilterCopy,BPP) +#define FilterPaletteBPP CONCAT2E(FilterPalette,BPP) +#define FilterGradientBPP CONCAT2E(FilterGradient,BPP) +#define FillRectangleBPP CONCAT2E(FillRectangle,BPP) + +#if BPP != 8 +#define DecompressJpegRectBPP CONCAT2E(DecompressJpegRect,BPP) +#endif + +#ifndef RGB_TO_PIXEL + +#define RGB_TO_PIXEL(bpp,r,g,b) \ + (((CARD##bpp)(r) & myFormat.redMax) << myFormat.redShift | \ + ((CARD##bpp)(g) & myFormat.greenMax) << myFormat.greenShift | \ + ((CARD##bpp)(b) & myFormat.blueMax) << myFormat.blueShift) + +#define RGB24_TO_PIXEL(bpp,r,g,b) \ + ((((CARD##bpp)(r) & 0xFF) * myFormat.redMax + 127) / 255 \ + << myFormat.redShift | \ + (((CARD##bpp)(g) & 0xFF) * myFormat.greenMax + 127) / 255 \ + << myFormat.greenShift | \ + (((CARD##bpp)(b) & 0xFF) * myFormat.blueMax + 127) / 255 \ + << myFormat.blueShift) + +#define RGB24_TO_PIXEL32(r,g,b) \ + (((CARD32)(r) & 0xFF) << myFormat.redShift | \ + ((CARD32)(g) & 0xFF) << myFormat.greenShift | \ + ((CARD32)(b) & 0xFF) << myFormat.blueShift) + +#endif + +/* Type declarations */ + +typedef void (*filterPtrBPP)(int, CARDBPP *); + +/* Prototypes */ + +static int InitFilterCopyBPP (int rw, int rh); +static int InitFilterPaletteBPP (int rw, int rh); +static int InitFilterGradientBPP (int rw, int rh); +static void FilterCopyBPP (int numRows, CARDBPP *destBuffer); +static void FilterPaletteBPP (int numRows, CARDBPP *destBuffer); +static void FilterGradientBPP (int numRows, CARDBPP *destBuffer); + +static Bool DecompressJpegRectBPP(int x, int y, int w, int h); + +/* Definitions */ + +static Bool +HandleTightBPP (int rx, int ry, int rw, int rh) +{ + CARDBPP fill_colour; + XGCValues gcv; + CARD8 comp_ctl; + CARD8 filter_id; + filterPtrBPP filterFn; + z_streamp zs; + char *buffer2; + int err, stream_id, compressedLen, bitsPixel; + int bufferSize, rowSize, numRows, portionLen, rowsProcessed, extraBytes; + CARDBPP *rawData; + + if (!ReadFromRFBServer((char *)&comp_ctl, 1)) + return False; + + /* Flush zlib streams if we are told by the server to do so. */ + for (stream_id = 0; stream_id < 4; stream_id++) { + if ((comp_ctl & 1) && zlibStreamActive[stream_id]) { + if (inflateEnd (&zlibStream[stream_id]) != Z_OK && + zlibStream[stream_id].msg != NULL) + fprintf(stderr, "inflateEnd: %s\n", zlibStream[stream_id].msg); + zlibStreamActive[stream_id] = False; + } + comp_ctl >>= 1; + } + + /* Handle solid rectangles. */ + if (comp_ctl == rfbTightFill) { +#if BPP == 32 + if (myFormat.depth == 24 && myFormat.redMax == 0xFF && + myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) { + if (!ReadFromRFBServer(buffer, 3)) + return False; + fill_colour = RGB24_TO_PIXEL32(buffer[0], buffer[1], buffer[2]); + } else { + if (!ReadFromRFBServer((char*)&fill_colour, sizeof(fill_colour))) + return False; + } +#else + if (!ReadFromRFBServer((char*)&fill_colour, sizeof(fill_colour))) + return False; +#endif + + LockFramebuffer(); + FillRectangleBPP(fill_colour, rx, ry, rw, rh); + UnlockFramebuffer(); + SyncScreenRegion(rx, ry, rw, rh); + return True; + } + +#if BPP == 8 + if (comp_ctl == rfbTightJpeg) { + fprintf(stderr, "Tight encoding: JPEG is not supported in 8 bpp mode.\n"); + return False; + } +#else + if (comp_ctl == rfbTightJpeg) { + return DecompressJpegRectBPP(rx, ry, rw, rh); + } +#endif + + /* Quit on unsupported subencoding value. */ + if (comp_ctl > rfbTightMaxSubencoding) { + fprintf(stderr, "Tight encoding: bad subencoding value received.\n"); + return False; + } + + /* + * Here primary compression mode handling begins. + * Data was processed with optional filter + zlib compression. + */ + + /* First, we should identify a filter to use. */ + if ((comp_ctl & rfbTightExplicitFilter) != 0) { + if (!ReadFromRFBServer((char*)&filter_id, 1)) + return False; + + switch (filter_id) { + case rfbTightFilterCopy: + filterFn = FilterCopyBPP; + bitsPixel = InitFilterCopyBPP(rw, rh); + break; + case rfbTightFilterPalette: + filterFn = FilterPaletteBPP; + bitsPixel = InitFilterPaletteBPP(rw, rh); + break; + case rfbTightFilterGradient: + filterFn = FilterGradientBPP; + bitsPixel = InitFilterGradientBPP(rw, rh); + break; + default: + fprintf(stderr, "Tight encoding: unknown filter code received.\n"); + return False; + } + } else { + filterFn = FilterCopyBPP; + bitsPixel = InitFilterCopyBPP(rw, rh); + } + if (bitsPixel == 0) { + fprintf(stderr, "Tight encoding: error receiving palette.\n"); + return False; + } + + /* Determine if the data should be decompressed or just copied. */ + rowSize = (rw * bitsPixel + 7) / 8; + if (rh * rowSize < TIGHT_MIN_TO_COMPRESS) { + if (!ReadFromRFBServer((char*)buffer, rh * rowSize)) + return False; + + buffer2 = &buffer[TIGHT_MIN_TO_COMPRESS * 4]; + filterFn(rh, (CARDBPP *)buffer2); + CopyDataToScreen(buffer2, rx, ry, rw, rh); + + return True; + } + + /* Read the length (1..3 bytes) of compressed data following. */ + compressedLen = (int)ReadCompactLen(); + if (compressedLen <= 0) { + fprintf(stderr, "Incorrect data received from the server.\n"); + return False; + } + + /* Now let's initialize compression stream if needed. */ + stream_id = comp_ctl & 0x03; + zs = &zlibStream[stream_id]; + if (!zlibStreamActive[stream_id]) { + zs->zalloc = Z_NULL; + zs->zfree = Z_NULL; + zs->opaque = Z_NULL; + err = inflateInit(zs); + if (err != Z_OK) { + if (zs->msg != NULL) + fprintf(stderr, "InflateInit error: %s.\n", zs->msg); + return False; + } + zlibStreamActive[stream_id] = True; + } + + /* Read, decode and draw actual pixel data in a loop. */ + + bufferSize = BUFFER_SIZE * bitsPixel / (bitsPixel + BPP) & 0xFFFFFFFC; + buffer2 = &buffer[bufferSize]; + if (rowSize > bufferSize) { + /* Should be impossible when BUFFER_SIZE >= 16384 */ + fprintf(stderr, "Internal error: incorrect buffer size.\n"); + return False; + } + + rowsProcessed = 0; + extraBytes = 0; + + while (compressedLen > 0) { + if (compressedLen > ZLIB_BUFFER_SIZE) + portionLen = ZLIB_BUFFER_SIZE; + else + portionLen = compressedLen; + + if (!ReadFromRFBServer((char*)zlib_buffer, portionLen)) + return False; + + compressedLen -= portionLen; + + zs->next_in = (Bytef *)zlib_buffer; + zs->avail_in = portionLen; + + do { + zs->next_out = (Bytef *)&buffer[extraBytes]; + zs->avail_out = bufferSize - extraBytes; + + err = inflate(zs, Z_SYNC_FLUSH); + if (err == Z_BUF_ERROR) /* Input exhausted -- no problem. */ + break; + if (err != Z_OK && err != Z_STREAM_END) { + if (zs->msg != NULL) { + fprintf(stderr, "Inflate error: %s.\n", zs->msg); + } else { + fprintf(stderr, "Inflate error: %d.\n", err); + } + return False; + } + + numRows = (bufferSize - zs->avail_out) / rowSize; + + filterFn(numRows, (CARDBPP *)buffer2); + + extraBytes = bufferSize - zs->avail_out - numRows * rowSize; + if (extraBytes > 0) + memcpy(buffer, &buffer[numRows * rowSize], extraBytes); + + CopyDataToScreen(buffer2, rx, ry + rowsProcessed, rw, numRows); + rowsProcessed += numRows; + } + while (zs->avail_out == 0); + } + + if (rowsProcessed != rh) { + fprintf(stderr, "Incorrect number of scan lines after decompression.\n"); + return False; + } + + return True; +} + +/*---------------------------------------------------------------------------- + * + * Filter stuff. + * + */ + +/* + The following variables are defined in rfbproto.c: + static Bool cutZeros; + static int rectWidth, rectColors; + static CARD8 tightPalette[256*4]; + static CARD8 tightPrevRow[2048*3*sizeof(CARD16)]; +*/ + +static int +InitFilterCopyBPP (int rw, int rh) +{ + rectWidth = rw; + +#if BPP == 32 + if (myFormat.depth == 24 && myFormat.redMax == 0xFF && + myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) { + cutZeros = True; + return 24; + } else { + cutZeros = False; + } +#endif + + return BPP; +} + +static void +FilterCopyBPP (int numRows, CARDBPP *dst) +{ + +#if BPP == 32 + int x, y; + + if (cutZeros) { + for (y = 0; y < numRows; y++) { + for (x = 0; x < rectWidth; x++) { + dst[y*rectWidth+x] = + RGB24_TO_PIXEL32(buffer[(y*rectWidth+x)*3], + buffer[(y*rectWidth+x)*3+1], + buffer[(y*rectWidth+x)*3+2]); + } + } + return; + } +#endif + + memcpy (dst, buffer, numRows * rectWidth * (BPP / 8)); +} + +static int +InitFilterGradientBPP (int rw, int rh) +{ + int bits; + + bits = InitFilterCopyBPP(rw, rh); + if (cutZeros) + memset(tightPrevRow, 0, rw * 3); + else + memset(tightPrevRow, 0, rw * 3 * sizeof(CARD16)); + + return bits; +} + +#if BPP == 32 + +static void +FilterGradient24 (int numRows, CARD32 *dst) +{ + int x, y, c; + CARD8 thisRow[2048*3]; + CARD8 pix[3]; + int est[3]; + + for (y = 0; y < numRows; y++) { + + /* First pixel in a row */ + for (c = 0; c < 3; c++) { + pix[c] = tightPrevRow[c] + buffer[y*rectWidth*3+c]; + thisRow[c] = pix[c]; + } + dst[y*rectWidth] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]); + + /* Remaining pixels of a row */ + for (x = 1; x < rectWidth; x++) { + for (c = 0; c < 3; c++) { + est[c] = (int)tightPrevRow[x*3+c] + (int)pix[c] - + (int)tightPrevRow[(x-1)*3+c]; + if (est[c] > 0xFF) { + est[c] = 0xFF; + } else if (est[c] < 0x00) { + est[c] = 0x00; + } + pix[c] = (CARD8)est[c] + buffer[(y*rectWidth+x)*3+c]; + thisRow[x*3+c] = pix[c]; + } + dst[y*rectWidth+x] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]); + } + + memcpy(tightPrevRow, thisRow, rectWidth * 3); + } +} + +#endif + +static void +FilterGradientBPP (int numRows, CARDBPP *dst) +{ + int x, y, c; + CARDBPP *src = (CARDBPP *)buffer; + CARD16 *thatRow = (CARD16 *)tightPrevRow; + CARD16 thisRow[2048*3]; + CARD16 pix[3]; + CARD16 max[3]; + int shift[3]; + int est[3]; + +#if BPP == 32 + if (cutZeros) { + FilterGradient24(numRows, dst); + return; + } +#endif + + max[0] = myFormat.redMax; + max[1] = myFormat.greenMax; + max[2] = myFormat.blueMax; + + shift[0] = myFormat.redShift; + shift[1] = myFormat.greenShift; + shift[2] = myFormat.blueShift; + + for (y = 0; y < numRows; y++) { + + /* First pixel in a row */ + for (c = 0; c < 3; c++) { + pix[c] = (CARD16)((src[y*rectWidth] >> shift[c]) + thatRow[c] & max[c]); + thisRow[c] = pix[c]; + } + dst[y*rectWidth] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]); + + /* Remaining pixels of a row */ + for (x = 1; x < rectWidth; x++) { + for (c = 0; c < 3; c++) { + est[c] = (int)thatRow[x*3+c] + (int)pix[c] - (int)thatRow[(x-1)*3+c]; + if (est[c] > (int)max[c]) { + est[c] = (int)max[c]; + } else if (est[c] < 0) { + est[c] = 0; + } + pix[c] = (CARD16)((src[y*rectWidth+x] >> shift[c]) + est[c] & max[c]); + thisRow[x*3+c] = pix[c]; + } + dst[y*rectWidth+x] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]); + } + memcpy(thatRow, thisRow, rectWidth * 3 * sizeof(CARD16)); + } +} + +static int +InitFilterPaletteBPP (int rw, int rh) +{ + int i; + CARD8 numColors; + CARDBPP *palette = (CARDBPP *)tightPalette; + + rectWidth = rw; + + if (!ReadFromRFBServer((char*)&numColors, 1)) + return 0; + + rectColors = (int)numColors; + if (++rectColors < 2) + return 0; + +#if BPP == 32 + if (myFormat.depth == 24 && myFormat.redMax == 0xFF && + myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) { + if (!ReadFromRFBServer((char*)&tightPalette, rectColors * 3)) + return 0; + for (i = rectColors - 1; i >= 0; i--) { + palette[i] = RGB24_TO_PIXEL32(tightPalette[i*3], + tightPalette[i*3+1], + tightPalette[i*3+2]); + } + return (rectColors == 2) ? 1 : 8; + } +#endif + + if (!ReadFromRFBServer((char*)&tightPalette, rectColors * (BPP / 8))) + return 0; + + return (rectColors == 2) ? 1 : 8; +} + +static void +FilterPaletteBPP (int numRows, CARDBPP *dst) +{ + int x, y, b, w; + CARD8 *src = (CARD8 *)buffer; + CARDBPP *palette = (CARDBPP *)tightPalette; + + if (rectColors == 2) { + w = (rectWidth + 7) / 8; + for (y = 0; y < numRows; y++) { + for (x = 0; x < rectWidth / 8; x++) { + for (b = 7; b >= 0; b--) + dst[y*rectWidth+x*8+7-b] = palette[src[y*w+x] >> b & 1]; + } + for (b = 7; b >= 8 - rectWidth % 8; b--) { + dst[y*rectWidth+x*8+7-b] = palette[src[y*w+x] >> b & 1]; + } + } + } else { + for (y = 0; y < numRows; y++) + for (x = 0; x < rectWidth; x++) + dst[y*rectWidth+x] = palette[(int)src[y*rectWidth+x]]; + } +} + +#if BPP != 8 + +/*---------------------------------------------------------------------------- + * + * JPEG decompression. + * + */ + +/* + The following variables are defined in rfbproto.c: + static Bool jpegError; + static struct jpeg_source_mgr jpegSrcManager; + static JOCTET *jpegBufferPtr; + static size_t *jpegBufferLen; +*/ + +static Bool +DecompressJpegRectBPP(int x, int y, int w, int h) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + int compressedLen; + CARD8 *compressedData; + CARDBPP *pixelPtr; + JSAMPROW rowPointer[1]; + int dx, dy; + + compressedLen = (int)ReadCompactLen(); + if (compressedLen <= 0) { + fprintf(stderr, "Incorrect data received from the server.\n"); + return False; + } + + if (compressedLen > MAX_JPEG_SIZE) { + fprintf(stderr, "To large data announced by the server.\n"); + return False; + } + + compressedData = malloc(compressedLen); + if (compressedData == NULL) { + fprintf(stderr, "Memory allocation error.\n"); + return False; + } + + if (!ReadFromRFBServer((char*)compressedData, compressedLen)) { + free(compressedData); + return False; + } + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + + JpegSetSrcManager(&cinfo, compressedData, compressedLen); + + jpeg_read_header(&cinfo, TRUE); + cinfo.out_color_space = JCS_RGB; + + jpeg_start_decompress(&cinfo); + if (cinfo.output_width != w || cinfo.output_height != h || + cinfo.output_components != 3) { + fprintf(stderr, "Tight Encoding: Wrong JPEG data received.\n"); + jpeg_destroy_decompress(&cinfo); + free(compressedData); + return False; + } + + rowPointer[0] = (JSAMPROW)buffer; + dy = 0; + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, rowPointer, 1); + if (jpegError) { + break; + } + pixelPtr = (CARDBPP *)&buffer[BUFFER_SIZE / 2]; + for (dx = 0; dx < w; dx++) { + *pixelPtr++ = + RGB24_TO_PIXEL(BPP, buffer[dx*3], buffer[dx*3+1], buffer[dx*3+2]); + } + CopyDataToScreen(&buffer[BUFFER_SIZE / 2], x, y + dy, w, 1); + dy++; + } + + if (!jpegError) + jpeg_finish_decompress(&cinfo); + + jpeg_destroy_decompress(&cinfo); + free(compressedData); + + return !jpegError; +} + +#endif + diff --git a/krdc/vnc/vncauth.c b/krdc/vnc/vncauth.c new file mode 100644 index 00000000..b5d42836 --- /dev/null +++ b/krdc/vnc/vncauth.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * vncauth.c - Functions for VNC password management and authentication. + */ + +#include +#include +#include +#include +#include +#include +#include + + +/* + * We use a fixed key to store passwords, since we assume that our local + * file system is secure but nonetheless don't want to store passwords + * as plaintext. + */ + +unsigned char fixedkey[8] = {23,82,107,6,35,78,88,7}; + + +/* + * Encrypt a password and store it in a file. Returns 0 if successful, + * 1 if the file could not be written. + */ + +int +vncEncryptAndStorePasswd(char *passwd, char *fname) +{ + FILE *fp; + int i; + unsigned char encryptedPasswd[8]; + + if ((fp = fopen(fname,"w")) == NULL) return 1; + + chmod(fname, S_IRUSR|S_IWUSR); + + /* pad password with nulls */ + + for (i = 0; i < 8; i++) { + if (i < strlen(passwd)) { + encryptedPasswd[i] = passwd[i]; + } else { + encryptedPasswd[i] = 0; + } + } + + /* Do encryption in-place - this way we overwrite our copy of the plaintext + password */ + + deskey(fixedkey, EN0); + des(encryptedPasswd, encryptedPasswd); + + for (i = 0; i < 8; i++) { + putc(encryptedPasswd[i], fp); + } + + fclose(fp); + return 0; +} + + +/* + * Decrypt a password from a file. Returns a pointer to a newly allocated + * string containing the password or a null pointer if the password could + * not be retrieved for some reason. + */ + +char * +vncDecryptPasswdFromFile(char *fname) +{ + FILE *fp; + int i, ch; + unsigned char *passwd = (unsigned char *)malloc(9); + + if ((fp = fopen(fname,"r")) == NULL) return NULL; + + for (i = 0; i < 8; i++) { + ch = getc(fp); + if (ch == EOF) { + fclose(fp); + return NULL; + } + passwd[i] = ch; + } + + fclose(fp); + + deskey(fixedkey, DE1); + des(passwd, passwd); + + passwd[8] = 0; + + return (char *)passwd; +} + + +/* + * Generate CHALLENGESIZE random bytes for use in challenge-response + * authentication. + */ + +void +vncRandomBytes(unsigned char *bytes) +{ + int i; + unsigned int seed = (unsigned int) time(0); + + srandom(seed); + for (i = 0; i < CHALLENGESIZE; i++) { + bytes[i] = (unsigned char)(random() & 255); + } +} + + +/* + * Encrypt CHALLENGESIZE bytes in memory using a password. + */ + +void +vncEncryptBytes(unsigned char *bytes, char *passwd) +{ + unsigned char key[8]; + int i; + + /* key is simply password padded with nulls */ + + for (i = 0; i < 8; i++) { + if (i < strlen(passwd)) { + key[i] = passwd[i]; + } else { + key[i] = 0; + } + } + + deskey(key, EN0); + + for (i = 0; i < CHALLENGESIZE; i += 8) { + des(bytes+i, bytes+i); + } +} diff --git a/krdc/vnc/vncauth.h b/krdc/vnc/vncauth.h new file mode 100644 index 00000000..86dc455c --- /dev/null +++ b/krdc/vnc/vncauth.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * vncauth.h - describes the functions provided by the vncauth library. + */ + +#define MAXPWLEN 8 +#define CHALLENGESIZE 16 + +extern int vncEncryptAndStorePasswd(char *passwd, char *fname); +extern char *vncDecryptPasswdFromFile(char *fname); +extern void vncRandomBytes(unsigned char *bytes); +extern void vncEncryptBytes(unsigned char *bytes, char *passwd); diff --git a/krdc/vnc/vnchostpref.cpp b/krdc/vnc/vnchostpref.cpp new file mode 100644 index 00000000..a17f8ea1 --- /dev/null +++ b/krdc/vnc/vnchostpref.cpp @@ -0,0 +1,127 @@ +/*************************************************************************** + vnchostprefs.cpp - vnc host preferences + ------------------- + begin : Fri May 09 22:32 CET 2003 + copyright : (C) 2003 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "vnchostpref.h" +#include +#include + +const QString VncHostPref::VncType = "VNC"; + +VncHostPref::VncHostPref(KConfig *conf, const QString &host, const QString &type) : + HostPref(conf, host, type), + m_quality(0), + m_useKWallet(true), + m_askOnConnect(true) { +} + +VncHostPref::~VncHostPref() { +} + +void VncHostPref::save() { + if ( !m_host.isEmpty() && !m_type.isEmpty() ) + { + m_config->setGroup("PerHostSettings"); + QString p = prefix(); + m_config->writeEntry(p+"exists", true); + m_config->writeEntry(p+"quality", m_quality); + m_config->writeEntry(p+"askOnConnect", m_askOnConnect); + m_config->writeEntry(p+"useKWallet", m_useKWallet); + } + else + { + m_config->setGroup( "VncDefaultSettings" ); + m_config->writeEntry( "vncQuality", m_quality ); + m_config->writeEntry( "vncShowHostPreferences", m_askOnConnect ); + m_config->writeEntry( "vncUseKWallet", m_useKWallet ); + } +} + +void VncHostPref::load() { + if ( !m_host.isEmpty() && !m_type.isEmpty() ) + { + m_config->setGroup("PerHostSettings"); + QString p = prefix(); + m_quality = m_config->readNumEntry(p+"quality", 0); + m_askOnConnect = m_config->readBoolEntry(p+"askOnConnect", true); + m_useKWallet = m_config->readBoolEntry(p+"useKWallet", true); + } + else + { + setDefaults(); + } +} + +void VncHostPref::remove() { + m_config->setGroup("PerHostSettings"); + QString p = prefix(); + m_config->deleteEntry(p+"exists"); + m_config->deleteEntry(p+"quality"); + m_config->deleteEntry(p+"askOnConnect"); +} + +void VncHostPref::setDefaults() { + m_config->setGroup("VncDefaultSettings"); + m_quality = m_config->readNumEntry("vncQuality", 0); + m_askOnConnect = m_config->readBoolEntry("vncShowHostPreferences", true); + m_useKWallet = m_config->readBoolEntry("vncUseKWallet", true); +} + +QString VncHostPref::prefDescription() const { + QString q; + switch(m_quality) { + case 0: + q = i18n("High"); + break; + case 1: + q = i18n("Medium"); + break; + case 2: + q = i18n("Low"); + break; + default: + Q_ASSERT(true); + } + return i18n("Show Preferences: %1, Quality: %2, KWallet: %3") + .arg(m_askOnConnect ? i18n("yes") : i18n("no")).arg(q).arg(m_useKWallet ? i18n("yes") : i18n("no")); +} + +void VncHostPref::setQuality(int q) { + m_quality = q; + save(); +} + +int VncHostPref::quality() const { + return m_quality; +} + +void VncHostPref::setAskOnConnect(bool ask) { + m_askOnConnect = ask; + save(); +} + +bool VncHostPref::askOnConnect() const { + return m_askOnConnect; +} + +void VncHostPref::setUseKWallet(bool use) { + m_useKWallet = use; + save(); +} + +bool VncHostPref::useKWallet() const { + return m_useKWallet; +} diff --git a/krdc/vnc/vnchostpref.h b/krdc/vnc/vnchostpref.h new file mode 100644 index 00000000..33a20600 --- /dev/null +++ b/krdc/vnc/vnchostpref.h @@ -0,0 +1,52 @@ +/*************************************************************************** + vnchostprefs.h - vnc host preferences + ------------------- + begin : Fri May 09 22:32 CET 2003 + copyright : (C) 2003 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef VNCHOSTPREF_H +#define VNCHOSTPREF_H + +#include "hostpreferences.h" + +class VncHostPref : public HostPref { +protected: + friend class HostPreferences; + + int m_quality; + bool m_askOnConnect; + bool m_useKWallet; + + virtual void load(); + virtual void setDefaults(); + virtual void save(); + virtual void remove(); + +public: + static const QString VncType; + + VncHostPref(KConfig *conf, const QString &host=QString::null, + const QString &type=QString::null); + virtual ~VncHostPref(); + + virtual QString prefDescription() const; + void setQuality(int q); + int quality() const; + void setAskOnConnect(bool ask); + bool askOnConnect() const; + void setUseKWallet(bool); + bool useKWallet() const; +}; + +#endif diff --git a/krdc/vnc/vncprefs.ui b/krdc/vnc/vncprefs.ui new file mode 100644 index 00000000..034b2b1a --- /dev/null +++ b/krdc/vnc/vncprefs.ui @@ -0,0 +1,165 @@ + +VncPrefs + + + VncPrefs + + + + 0 + 0 + 472 + 126 + + + + + unnamed + + + 0 + + + + groupBox + + + Connection + + + + unnamed + + + + cbUseEncryption + + + false + + + &Enable encryption (secure, but slow and not always possible) + + + Enable this option to encrypt the connection. Only newer servers support this option. Encrypting prevents others from eavesdropping, but can slow down the connection considerably. + + + + + cbUseKWallet + + + true + + + Use K&Wallet for passwords + + + Enable this option to store your passwords with KWallet. + + + + + connectionTypeLabel + + + + 0 + 1 + 0 + 0 + + + + Connection &type: + + + cmbQuality + + + + + + High Quality (LAN, direct connection) + + + + + Medium Quality (DSL, Cable, fast Internet) + + + + + Low Quality (Modem, ISDN, slow Internet) + + + + cmbQuality + + + + 5 + 0 + 0 + 0 + + + + + 280 + 0 + + + + Use this to specify the performance of your connection. Note that you should select the speed of the weakest link - even if you have a high speed connection, it will not help you if the remote computer uses a slow modem. Choosing a level of quality that is too high on a slow link will cause slower response times. Choosing a lower quality will increase latencies in high speed connections and results in lower image quality, especially in 'Low Quality' mode. + + + + + Spacer + + + Horizontal + + + Expanding + + + + 84 + 16 + + + + + + + + cbShowPrefs + + + &Show this dialog again for this host + + + Select this option if you do not want to be asked for the settings when connecting to a host. For hosts with existing profiles these profiles will be taken. New hosts will be configured with the defaults. + + + + + + kdialog.h + vncprefs.ui.h + + + setQuality( int quality ) + quality() + setShowPrefs( bool b ) + showPrefs() + setUseEncryption( bool b ) + useEncryption() + setUseKWallet( bool b ) + useKWallet() + + + + diff --git a/krdc/vnc/vncprefs.ui.h b/krdc/vnc/vncprefs.ui.h new file mode 100644 index 00000000..a3438f05 --- /dev/null +++ b/krdc/vnc/vncprefs.ui.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you wish to add, delete or rename functions or slots use +** Qt Designer which will update this file, preserving your code. Create an +** init() function in place of a constructor, and a destroy() function in +** place of a destructor. +*****************************************************************************/ + +void VncPrefs::setQuality( int quality ) +{ + cmbQuality->setCurrentItem(quality); +} + + +int VncPrefs::quality() +{ + return cmbQuality->currentItem(); +} + + +void VncPrefs::setShowPrefs( bool b ) +{ + cbShowPrefs->setChecked(b); +} + + +int VncPrefs::showPrefs() +{ + return cbShowPrefs->isChecked(); +} + + +void VncPrefs::setUseEncryption( bool b ) +{ + cbUseEncryption->setChecked(b); +} + + +bool VncPrefs::useEncryption() +{ + return cbUseEncryption->isChecked(); +} + +void VncPrefs::setUseKWallet( bool b ) +{ + cbUseKWallet->setChecked(b); +} + +bool VncPrefs::useKWallet() +{ + return cbUseKWallet->isChecked(); +} diff --git a/krdc/vnc/vnctypes.h b/krdc/vnc/vnctypes.h new file mode 100644 index 00000000..15329ee2 --- /dev/null +++ b/krdc/vnc/vnctypes.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2002 Tim Jansen. All Rights Reserved. + * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. + * Copyright (C) 2000 Tridia Corporation. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef VNCTYPES_H +#define VNCTYPES_H + +#if(defined __cplusplus) +extern "C" +{ +#endif + +#include + + +typedef struct { + int shareDesktop; /* bool */ + int viewOnly; /* bool */ + + const char* encodingsString; + + int useBGR233; /* bool */ + int nColours; + int useSharedColours; /* bool */ + int requestedDepth; + + int rawDelay; + int copyRectDelay; + + int debug; /* bool */ + + int compressLevel; + int qualityLevel; + int dotCursor; /* bool */ + +} AppData; + + +enum InitStatus { + INIT_OK = 0, + INIT_NAME_RESOLUTION_FAILURE = 1, + INIT_PROTOCOL_FAILURE = 2, + INIT_CONNECTION_FAILED = 3, + INIT_AUTHENTICATION_FAILED = 4, + INIT_NO_SERVER = 5, + INIT_SERVER_BLOCKED = 6, + INIT_ABORTED = 7 +}; + + +#if(defined __cplusplus) +} +#endif + +#endif diff --git a/krdc/vnc/vncviewer.h b/krdc/vnc/vncviewer.h new file mode 100644 index 00000000..dcc66cde --- /dev/null +++ b/krdc/vnc/vncviewer.h @@ -0,0 +1,186 @@ +#ifndef VNCVIEWER_H +#define VNCVIEWER_H +/* + * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. + * Copyright (C) 2000 Tridia Corporation. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * vncviewer.h + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vnctypes.h" + +#if(defined __cplusplus) +extern "C" +{ +#endif + +#include "rfbproto.h" + +extern int endianTest; + +#define Swap16IfLE(s) \ + (*(char *)&endianTest ? ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) : (s)) + +#define Swap32IfLE(l) \ + (*(char *)&endianTest ? ((((l) & 0xff000000) >> 24) | \ + (((l) & 0x00ff0000) >> 8) | \ + (((l) & 0x0000ff00) << 8) | \ + (((l) & 0x000000ff) << 24)) : (l)) + +#define MAX_ENCODINGS 20 + + +/** kvncview.cpp **/ + +extern AppData appData; + +extern Display* dpy; +extern const char *vncServerHost; +extern int vncServerPort; + +extern int isQuitFlagSet(); +extern int getPassword(char *passwd, int pwlen); +extern void DrawScreenRegion(int x, int y, int width, int height); +extern void DrawAnyScreenRegionX11Thread(int x, int y, int width, int height); +extern void LockFramebuffer(); +extern void UnlockFramebuffer(); +extern void EnableClientCursor(int state); +extern void beep(); +extern void newServerCut(char *bytes, int len); +extern void postMouseEvent(int x, int y, int buttonMask); + +/** threads.cpp **/ + +extern void queueIncrementalUpdateRequest(); +extern void announceIncrementalUpdateRequest(); + +/* colour.c */ + +extern unsigned long BGR233ToPixel[]; + +extern Colormap cmap; +extern Visual *vis; +extern unsigned int visdepth, visbpp; + +extern void SetVisualAndCmap(void); + +/* desktop.c */ + +extern Widget form, viewport, desktop; +extern GC gc; +extern GC srcGC, dstGC; +extern Dimension dpyWidth, dpyHeight; + +extern void DesktopInit(Window win); +extern void ToplevelInit(void); +extern void SendRFBEvent(XEvent *event, String *params, Cardinal *num_params); +extern void CopyDataToScreen(char *buf, int x, int y, int width, int height); +extern void CopyDataFromScreen(char *buf, int x, int y, int width, int height); +extern void FillRectangle8(CARD8, int x, int y, int width, int height); +extern void FillRectangle16(CARD16, int x, int y, int width, int height); +extern void FillRectangle32(CARD32, int x, int y, int width, int height); +extern void CopyArea(int srcX, int srcY, int width, int height, int x, int y); +extern void SyncScreenRegion(int x, int y, int width, int height); +extern void SyncScreenRegionX11Thread(int x, int y, int width, int height); +extern void drawCursor(void); +extern void DrawCursorX11Thread(int x, int y); +extern void undrawCursor(void); +extern void getBoundingRectCursor(int cx, int cy, int _imageIndex, + int *x, int *y, int *w, int *h); +extern int rectsIntersect(int x, int y, int w, int h, + int x2, int y2, int w2, int h2); +extern int rectContains(int outX, int outY, int outW, int outH, + int inX, int inY, int inW, int inH); +extern void rectsJoin(int *nx1, int *ny1, int *nw1, int *nh1, + int x2, int y2, int w2, int h2); +extern void DrawZoomedScreenRegionX11Thread(Window win, int zwidth, + int zheight, + int x, int y, + int width, int height); +extern void DrawScreenRegionX11Thread(Window win, int x, int y, + int width, int height); +extern void Cleanup(void); +extern XImage *CreateShmZoomImage(void); +extern XImage *CreateShmImage(void); +extern void ShmCleanup(void); +extern void freeDesktopResources(void); + +/* rfbproto.c */ + +extern int rfbsock; +extern Bool canUseCoRRE; +extern Bool canUseHextile; +extern char *desktopName; +extern rfbPixelFormat myFormat; +extern rfbServerInitMsg si; + +extern int cursorX, cursorY; +extern int imageIndex; +typedef struct { + int set; + int w, h; + int hotX, hotY; + int len; + char *image; +} PointerImage; +extern PointerImage pointerImages[]; + +extern int ConnectToRFBServer(const char *hostname, int port); +extern enum InitStatus InitialiseRFBConnection(void); +extern Bool SetFormatAndEncodings(void); +extern Bool SendIncrementalFramebufferUpdateRequest(void); +extern Bool SendFramebufferUpdateRequest(int x, int y, int w, int h, + Bool incremental); +extern Bool SendPointerEvent(int x, int y, int buttonMask); +extern Bool SendKeyEvent(CARD32 key, Bool down); +extern Bool SendClientCutText(const char *str, int len); +extern Bool HandleRFBServerMessage(void); + +extern void PrintPixelFormat(rfbPixelFormat *format); +extern void freeRFBProtoResources(void); +extern void freeResources(void); + +/* sockets.c */ + +extern Bool errorMessageOnReadFailure; + +extern Bool ReadFromRFBServer(char *out, unsigned int n); +extern Bool WriteExact(int sock, const char *buf, int n); +extern int ConnectToTcpAddr(unsigned int host, int port); + +extern int StringToIPAddr(const char *str, unsigned int *addr); +extern void freeSocketsResources(void); + +#if(defined __cplusplus) +} +#endif +#endif diff --git a/krdc/vnc/zlib.c b/krdc/vnc/zlib.c new file mode 100644 index 00000000..80c4eeea --- /dev/null +++ b/krdc/vnc/zlib.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2000 Tridia Corporation. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * zlib.c - handle zlib encoding. + * + * This file shouldn't be compiled directly. It is included multiple times by + * rfbproto.c, each time with a different definition of the macro BPP. For + * each value of BPP, this file defines a function which handles an zlib + * encoded rectangle with BPP bits per pixel. + */ + +#define HandleZlibBPP CONCAT2E(HandleZlib,BPP) +#define CARDBPP CONCAT2E(CARD,BPP) + +static Bool +HandleZlibBPP (int rx, int ry, int rw, int rh) +{ + rfbZlibHeader hdr; + int remaining; + int inflateResult; + int toRead; + + /* First make sure we have a large enough raw buffer to hold the + * decompressed data. In practice, with a fixed BPP, fixed frame + * buffer size and the first update containing the entire frame + * buffer, this buffer allocation should only happen once, on the + * first update. + */ + if ( raw_buffer_size < (( rw * rh ) * ( BPP / 8 ))) { + + if ( raw_buffer != NULL ) { + + free( raw_buffer ); + + } + + raw_buffer_size = (( rw * rh ) * ( BPP / 8 )); + raw_buffer = (char*) malloc( raw_buffer_size ); + + } + + if (!ReadFromRFBServer((char *)&hdr, sz_rfbZlibHeader)) + return False; + + remaining = Swap32IfLE(hdr.nBytes); + + /* Need to initialize the decompressor state. */ + decompStream.next_in = ( Bytef * )buffer; + decompStream.avail_in = 0; + decompStream.next_out = ( Bytef * )raw_buffer; + decompStream.avail_out = raw_buffer_size; + decompStream.data_type = Z_BINARY; + + /* Initialize the decompression stream structures on the first invocation. */ + if ( decompStreamInited == False ) { + + inflateResult = inflateInit( &decompStream ); + + if ( inflateResult != Z_OK ) { + fprintf(stderr, + "inflateInit returned error: %d, msg: %s\n", + inflateResult, + decompStream.msg); + return False; + } + + decompStreamInited = True; + + } + + inflateResult = Z_OK; + + /* Process buffer full of data until no more to process, or + * some type of inflater error, or Z_STREAM_END. + */ + while (( remaining > 0 ) && + ( inflateResult == Z_OK )) { + + if ( remaining > BUFFER_SIZE ) { + toRead = BUFFER_SIZE; + } + else { + toRead = remaining; + } + + /* Fill the buffer, obtaining data from the server. */ + if (!ReadFromRFBServer(buffer,toRead)) + return False; + + decompStream.next_in = ( Bytef * )buffer; + decompStream.avail_in = toRead; + + /* Need to uncompress buffer full. */ + inflateResult = inflate( &decompStream, Z_SYNC_FLUSH ); + + /* We never supply a dictionary for compression. */ + if ( inflateResult == Z_NEED_DICT ) { + fprintf(stderr,"zlib inflate needs a dictionary!\n"); + return False; + } + if ( inflateResult < 0 ) { + fprintf(stderr, + "zlib inflate returned error: %d, msg: %s\n", + inflateResult, + decompStream.msg); + return False; + } + + /* Result buffer allocated to be at least large enough. We should + * never run out of space! + */ + if (( decompStream.avail_in > 0 ) && + ( decompStream.avail_out <= 0 )) { + fprintf(stderr,"zlib inflate ran out of space!\n"); + return False; + } + + remaining -= toRead; + + } /* while ( remaining > 0 ) */ + + if ( inflateResult == Z_OK ) { + + /* Put the uncompressed contents of the update on the screen. */ + CopyDataToScreen(raw_buffer, rx, ry, rw, rh); + + } + else { + + fprintf(stderr, + "zlib inflate returned error: %d, msg: %s\n", + inflateResult, + decompStream.msg); + return False; + + } + + return True; +} -- cgit v1.2.1