Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
+17
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
ADD_SUBDIRECTORY(Gui)
|
||||
ADD_SUBDIRECTORY(Common)
|
||||
|
||||
#
|
||||
# Gui Plugin Interface
|
||||
#
|
||||
ADD_INTERFACE_LIBRARY(PwsGuiPluginInterface
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/pws_gui_plugin_interface.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||
GuiPluginInterface
|
||||
)
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
SET(pws_common_SRCS
|
||||
pws_rfb_protocol.cpp
|
||||
pws_rfb_protocol.h
|
||||
pws_des_encryption.h
|
||||
pws_des_encryption.cpp
|
||||
pws_vnc_handler.h
|
||||
pws_vnc_handler.cpp
|
||||
)
|
||||
|
||||
NGL_ADD_LIBRARY2(WS PwsCommon SHARED ${pws_common_SRCS} )
|
||||
NGL_TARGET_LINK_LIBRARIES(PwsCommon
|
||||
PRIVATE
|
||||
NGLLogger
|
||||
ngltools
|
||||
LtsSession
|
||||
LtsProjectionWorkstation
|
||||
PUBLIC
|
||||
Qt5::Core
|
||||
Qt5::Network
|
||||
)
|
||||
@@ -0,0 +1,318 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "pws_des_encryption.h"
|
||||
|
||||
namespace ProjectionWorkstation
|
||||
{
|
||||
|
||||
constexpr quint32 LB32_MASK = 0x00000001;
|
||||
constexpr quint64 LB64_MASK = 0x0000000000000001;
|
||||
constexpr quint64 L64_MASK = 0x00000000ffffffff;
|
||||
|
||||
// Permuted Choice 1 Table [7*8]
|
||||
constexpr char PC1[] =
|
||||
{
|
||||
57, 49, 41, 33, 25, 17, 9,
|
||||
1, 58, 50, 42, 34, 26, 18,
|
||||
10, 2, 59, 51, 43, 35, 27,
|
||||
19, 11, 3, 60, 52, 44, 36,
|
||||
|
||||
63, 55, 47, 39, 31, 23, 15,
|
||||
7, 62, 54, 46, 38, 30, 22,
|
||||
14, 6, 61, 53, 45, 37, 29,
|
||||
21, 13, 5, 28, 20, 12, 4
|
||||
};
|
||||
|
||||
// Permuted Choice 2 Table [6*8]
|
||||
constexpr char PC2[] =
|
||||
{
|
||||
14, 17, 11, 24, 1, 5,
|
||||
3, 28, 15, 6, 21, 10,
|
||||
23, 19, 12, 4, 26, 8,
|
||||
16, 7, 27, 20, 13, 2,
|
||||
41, 52, 31, 37, 47, 55,
|
||||
30, 40, 51, 45, 33, 48,
|
||||
44, 49, 39, 56, 34, 53,
|
||||
46, 42, 50, 36, 29, 32
|
||||
};
|
||||
|
||||
// Iteration Shift Array
|
||||
constexpr char ITERATION_SHIFT[] =
|
||||
{
|
||||
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
||||
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
|
||||
};
|
||||
|
||||
// Initial Permutation Table [8*8]
|
||||
constexpr char IP[] =
|
||||
{
|
||||
58, 50, 42, 34, 26, 18, 10, 2,
|
||||
60, 52, 44, 36, 28, 20, 12, 4,
|
||||
62, 54, 46, 38, 30, 22, 14, 6,
|
||||
64, 56, 48, 40, 32, 24, 16, 8,
|
||||
57, 49, 41, 33, 25, 17, 9, 1,
|
||||
59, 51, 43, 35, 27, 19, 11, 3,
|
||||
61, 53, 45, 37, 29, 21, 13, 5,
|
||||
63, 55, 47, 39, 31, 23, 15, 7
|
||||
};
|
||||
|
||||
// Inverse Initial Permutation Table [8*8]
|
||||
constexpr char FP[] =
|
||||
{
|
||||
40, 8, 48, 16, 56, 24, 64, 32,
|
||||
39, 7, 47, 15, 55, 23, 63, 31,
|
||||
38, 6, 46, 14, 54, 22, 62, 30,
|
||||
37, 5, 45, 13, 53, 21, 61, 29,
|
||||
36, 4, 44, 12, 52, 20, 60, 28,
|
||||
35, 3, 43, 11, 51, 19, 59, 27,
|
||||
34, 2, 42, 10, 50, 18, 58, 26,
|
||||
33, 1, 41, 9, 49, 17, 57, 25
|
||||
};
|
||||
|
||||
// Expansion table [6*8]
|
||||
constexpr char EXPANSION[] =
|
||||
{
|
||||
32, 1, 2, 3, 4, 5,
|
||||
4, 5, 6, 7, 8, 9,
|
||||
8, 9, 10, 11, 12, 13,
|
||||
12, 13, 14, 15, 16, 17,
|
||||
16, 17, 18, 19, 20, 21,
|
||||
20, 21, 22, 23, 24, 25,
|
||||
24, 25, 26, 27, 28, 29,
|
||||
28, 29, 30, 31, 32, 1
|
||||
};
|
||||
|
||||
// The S-Box tables [8*16*4]
|
||||
constexpr char SBOX[8][64] =
|
||||
{
|
||||
{
|
||||
// S1
|
||||
14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
|
||||
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
|
||||
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
|
||||
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13
|
||||
},
|
||||
{
|
||||
// S2
|
||||
15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
|
||||
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
|
||||
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
|
||||
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
|
||||
},
|
||||
{
|
||||
// S3
|
||||
10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
|
||||
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
|
||||
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
|
||||
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
|
||||
},
|
||||
{
|
||||
// S4
|
||||
7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
|
||||
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
|
||||
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
|
||||
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
|
||||
},
|
||||
{
|
||||
// S5
|
||||
2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
|
||||
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
|
||||
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
|
||||
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
|
||||
},
|
||||
{
|
||||
// S6
|
||||
12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
|
||||
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
|
||||
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
|
||||
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
|
||||
},
|
||||
{
|
||||
// S7
|
||||
4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
|
||||
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
|
||||
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
|
||||
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
|
||||
},
|
||||
{
|
||||
// S8
|
||||
13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
|
||||
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
|
||||
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
|
||||
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
|
||||
}
|
||||
};
|
||||
|
||||
// Post S-Box permutation [4*8]
|
||||
constexpr char PBOX[] =
|
||||
{
|
||||
16, 7, 20, 21,
|
||||
29, 12, 28, 17,
|
||||
1, 15, 23, 26,
|
||||
5, 18, 31, 10,
|
||||
2, 8, 24, 14,
|
||||
32, 27, 3, 9,
|
||||
19, 13, 30, 6,
|
||||
22, 11, 4, 25
|
||||
};
|
||||
|
||||
DES::DES(quint64 key)
|
||||
{
|
||||
keygen(key);
|
||||
}
|
||||
|
||||
quint64 DES::encrypt(quint64 block)
|
||||
{
|
||||
return des(block, false);
|
||||
}
|
||||
|
||||
quint64 DES::decrypt(quint64 block)
|
||||
{
|
||||
return des(block, true);
|
||||
}
|
||||
|
||||
quint64 DES::encrypt(quint64 block, quint64 key)
|
||||
{
|
||||
DES des(key);
|
||||
return des.des(block, false);
|
||||
}
|
||||
|
||||
quint64 DES::decrypt(quint64 block, quint64 key)
|
||||
{
|
||||
DES des(key);
|
||||
return des.des(block, true);
|
||||
}
|
||||
|
||||
void DES::keygen(quint64 key)
|
||||
{
|
||||
// initial key schedule calculation
|
||||
quint64 permuted_choice_1 = 0; // 56 bits
|
||||
for (quint8 i = 0; i < 56; i++)
|
||||
{
|
||||
permuted_choice_1 <<= 1;
|
||||
permuted_choice_1 |= (key >> (64 - PC1[i])) & LB64_MASK;
|
||||
}
|
||||
|
||||
// 28 bits
|
||||
quint32 C = (quint32)((permuted_choice_1 >> 28) & 0x000000000fffffff);
|
||||
quint32 D = (quint32)(permuted_choice_1 & 0x000000000fffffff);
|
||||
|
||||
// Calculation of the 16 keys
|
||||
for (quint8 i = 0; i < 16; i++)
|
||||
{
|
||||
// key schedule, shifting Ci and Di
|
||||
for (quint8 j = 0; j < ITERATION_SHIFT[i]; j++)
|
||||
{
|
||||
C = (0x0fffffff & (C << 1)) | (0x00000001 & (C >> 27));
|
||||
D = (0x0fffffff & (D << 1)) | (0x00000001 & (D >> 27));
|
||||
}
|
||||
|
||||
quint64 permuted_choice_2 = (((quint64) C) << 28) | (quint64) D;
|
||||
|
||||
m_subKey[i] = 0; // 48 bits (2*24)
|
||||
for (quint8 j = 0; j < 48; j++)
|
||||
{
|
||||
m_subKey[i] <<= 1;
|
||||
m_subKey[i] |= (permuted_choice_2 >> (56 - PC2[j])) & LB64_MASK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
quint64 DES::des(quint64 block, bool mode)
|
||||
{
|
||||
// applying initial permutation
|
||||
block = ip(block);
|
||||
|
||||
// dividing T' into two 32-bit parts
|
||||
quint32 L = (quint32)(block >> 32) & L64_MASK;
|
||||
quint32 R = (quint32)(block & L64_MASK);
|
||||
|
||||
// 16 rounds
|
||||
for (quint8 i = 0; i < 16; i++)
|
||||
{
|
||||
quint32 F = mode ? f(R, m_subKey[15 - i]) : f(R, m_subKey[i]);
|
||||
feistel(L, R, F);
|
||||
}
|
||||
|
||||
// swapping the two parts
|
||||
block = (((quint64) R) << 32) | (quint64) L;
|
||||
// applying final permutation
|
||||
return fp(block);
|
||||
}
|
||||
|
||||
quint64 DES::ip(quint64 block) // initial Permutation
|
||||
{
|
||||
// initial permutation
|
||||
quint64 result = 0;
|
||||
for (quint8 i = 0; i < 64; i++)
|
||||
{
|
||||
result <<= 1;
|
||||
result |= (block >> (64 - IP[i])) & LB64_MASK;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
quint64 DES::fp(quint64 block) // final Permutation
|
||||
{
|
||||
// inverse initial permutation
|
||||
quint64 result = 0;
|
||||
for (quint8 i = 0; i < 64; i++)
|
||||
{
|
||||
result <<= 1;
|
||||
result |= (block >> (64 - FP[i])) & LB64_MASK;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void DES::feistel(quint32 &L, quint32 &R, quint32 F)
|
||||
{
|
||||
quint32 temp = R;
|
||||
R = L ^ F;
|
||||
L = temp;
|
||||
}
|
||||
|
||||
quint32 DES::f(quint32 R, quint64 k) // f(R,k) function
|
||||
{
|
||||
// applying expansion permutation and returning 48-bit data
|
||||
quint64 s_input = 0;
|
||||
for (quint8 i = 0; i < 48; i++)
|
||||
{
|
||||
s_input <<= 1;
|
||||
s_input |= (quint64)((R >> (32 - EXPANSION[i])) & LB32_MASK);
|
||||
}
|
||||
|
||||
// XORing expanded Ri with Ki, the round key
|
||||
s_input = s_input ^ k;
|
||||
|
||||
// applying S-Boxes function and returning 32-bit data
|
||||
quint32 s_output = 0;
|
||||
for (quint8 i = 0; i < 8; i++)
|
||||
{
|
||||
// Outer bits
|
||||
char row = (char)((s_input & (0x0000840000000000 >> 6 * i)) >> (42 - 6 * i));
|
||||
row = (row >> 4) | (row & 0x01);
|
||||
|
||||
// Middle 4 bits of input
|
||||
char column = (char)((s_input & (0x0000780000000000 >> 6 * i)) >> (43 - 6 * i));
|
||||
|
||||
s_output <<= 4;
|
||||
s_output |= (quint32)(SBOX[i][16 * row + column] & 0x0f);
|
||||
}
|
||||
|
||||
// applying the round permutation
|
||||
quint32 f_result = 0;
|
||||
for (quint8 i = 0; i < 32; i++)
|
||||
{
|
||||
f_result <<= 1;
|
||||
f_result |= (s_output >> (32 - PBOX[i])) & LB32_MASK;
|
||||
}
|
||||
|
||||
return f_result;
|
||||
}
|
||||
|
||||
} // ProjectionWorkstation
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtGlobal>
|
||||
#include "pwscommon_export.h"
|
||||
|
||||
namespace ProjectionWorkstation
|
||||
{
|
||||
|
||||
class PWSCOMMON_EXPORT DES
|
||||
{
|
||||
public:
|
||||
explicit DES(quint64 key);
|
||||
|
||||
quint64 des(quint64 block, bool mode);
|
||||
|
||||
quint64 encrypt(quint64 block);
|
||||
quint64 decrypt(quint64 block);
|
||||
|
||||
static quint64 encrypt(quint64 block, quint64 key);
|
||||
static quint64 decrypt(quint64 block, quint64 key);
|
||||
|
||||
protected:
|
||||
void keygen(quint64 key);
|
||||
|
||||
static quint64 ip(quint64 block);
|
||||
static quint64 fp(quint64 block);
|
||||
|
||||
static void feistel(quint32 &L, quint32 &R, quint32 F);
|
||||
static quint32 f(quint32 R, quint64 k);
|
||||
|
||||
private:
|
||||
quint64 m_subKey[16]; // 48 bits each
|
||||
};
|
||||
|
||||
} // ProjectionWorkstation
|
||||
@@ -0,0 +1,226 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "pws_rfb_protocol.h"
|
||||
|
||||
// see https://vncdotool.readthedocs.io/en/0.8.0/rfbproto.html
|
||||
namespace ProjectionWorkstation
|
||||
{
|
||||
|
||||
namespace RfbProtocol
|
||||
{
|
||||
namespace STC
|
||||
{
|
||||
static constexpr int s_bytePerPixel = 4;
|
||||
|
||||
enum HextileSubEncoding : uint8_t
|
||||
{
|
||||
Raw = 0x01,
|
||||
BackgroundSpecified = 0x02,
|
||||
ForegroundSpecified = 0x04,
|
||||
AnySubrects = 0x08,
|
||||
SubrectsColoured = 0x10,
|
||||
};
|
||||
|
||||
static inline int highNibble(uint8_t b)
|
||||
{
|
||||
return b >> 4;
|
||||
}
|
||||
static inline int lowNibble(uint8_t b)
|
||||
{
|
||||
return b & 0x0f;
|
||||
}
|
||||
|
||||
static inline bool testBit(uint8_t bit, HextileSubEncoding enc)
|
||||
{
|
||||
return (bit & enc) == enc;
|
||||
}
|
||||
|
||||
static inline uint32_t swap(uint32_t in)
|
||||
{
|
||||
// compiler: bswap $eax ...
|
||||
uint32_t out = 0;
|
||||
out |= ((in >> 24) & 0x000000ff);
|
||||
out |= ((in >> 8) & 0x0000ff00);
|
||||
out |= ((in << 8) & 0x00ff0000);
|
||||
out |= ((in << 24) & 0xff000000);
|
||||
return out;
|
||||
}
|
||||
|
||||
static inline void fillHextileRect(RfbRectangle &rect, int xOfs, int yOfs, int w, int h, uint32_t col)
|
||||
{
|
||||
uint32_t *dst = reinterpret_cast<uint32_t *>(rect.pixelData.data());
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
Q_ASSERT(x + xOfs < rect.xpos + rect.width);
|
||||
Q_ASSERT(y + yOfs < rect.ypos + rect.height);
|
||||
dst[rect.width * (y + yOfs) + (x + xOfs)] = col;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void copyHextileRect(RfbRectangle &rect, int xOfs, int yOfs, int w, int h, const QByteArray &srcBa)
|
||||
{
|
||||
uint32_t *dst = reinterpret_cast<uint32_t *>(rect.pixelData.data());
|
||||
const uint32_t *src = reinterpret_cast<const uint32_t *>(srcBa.data());
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
Q_ASSERT(x + xOfs < rect.xpos + rect.width);
|
||||
Q_ASSERT(y + yOfs < rect.ypos + rect.height);
|
||||
dst[rect.width * (y + yOfs) + (x + xOfs)] = src[w * y + x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool decodeHextile(RfbRectangle &rect, QDataStream &ds)
|
||||
{
|
||||
uint32_t bg = 0;
|
||||
uint32_t fg = 0;
|
||||
|
||||
for (int yRect = rect.ypos; yRect < rect.ypos + rect.height; yRect += 16)
|
||||
{
|
||||
for (int xRect = rect.xpos; xRect < rect.xpos + rect.width; xRect += 16)
|
||||
{
|
||||
int w = 16;
|
||||
int h = 16;
|
||||
if (rect.xpos + rect.width - xRect < 16)
|
||||
w = rect.xpos + rect.width - xRect;
|
||||
if (rect.ypos + rect.height - yRect < 16)
|
||||
h = rect.ypos + rect.height - yRect;
|
||||
|
||||
const int x = xRect - rect.xpos;
|
||||
const int y = yRect - rect.ypos;
|
||||
|
||||
uint8_t subencoding = 0;
|
||||
ds >> subencoding;
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return false;
|
||||
|
||||
if (testBit(subencoding, HextileSubEncoding::Raw))
|
||||
{
|
||||
QByteArray buf(w * h * s_bytePerPixel, Qt::Uninitialized);
|
||||
ds.readRawData(buf.data(), buf.size());
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return false;
|
||||
copyHextileRect(rect, x, y, w, h, buf);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (testBit(subencoding, HextileSubEncoding::BackgroundSpecified))
|
||||
{
|
||||
ds >> bg;
|
||||
bg = swap(bg);
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return false;
|
||||
}
|
||||
|
||||
fillHextileRect(rect, x, y, w, h, bg);
|
||||
|
||||
if (testBit(subencoding, HextileSubEncoding::ForegroundSpecified))
|
||||
{
|
||||
ds >> fg;
|
||||
fg = swap(fg);
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!testBit(subencoding, HextileSubEncoding::AnySubrects))
|
||||
continue;
|
||||
|
||||
uint8_t nSubrects;
|
||||
ds >> nSubrects;
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return false;
|
||||
|
||||
if (testBit(subencoding, HextileSubEncoding::SubrectsColoured))
|
||||
{
|
||||
uint8_t xy = 0;
|
||||
uint8_t wh = 0;
|
||||
for (int i = 0; i < nSubrects; i++)
|
||||
{
|
||||
ds >> fg >> xy >> wh;
|
||||
fg = swap(fg);
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return false;
|
||||
int xOfs = highNibble(xy);
|
||||
int yOfs = lowNibble(xy);
|
||||
int wOfs = highNibble(wh) + 1;
|
||||
int hOfs = lowNibble(wh) + 1;
|
||||
fillHextileRect(rect, x + xOfs, y + yOfs, wOfs, hOfs, fg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t xy = 0;
|
||||
uint8_t wh = 0;
|
||||
for (int i = 0; i < nSubrects; i++)
|
||||
{
|
||||
ds >> xy >> wh;
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return false;
|
||||
int xOfs = highNibble(xy);
|
||||
int yOfs = lowNibble(xy);
|
||||
int wOfs = highNibble(wh) + 1;
|
||||
int hOfs = lowNibble(wh) + 1;
|
||||
fillHextileRect(rect, x + xOfs, y + yOfs, wOfs, hOfs, fg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RfbFramebufferUpdate::fillDataFromStream(QDataStream &ds)
|
||||
{
|
||||
quint8 padding;
|
||||
ds >> padding;
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
|
||||
quint16 numberOfRectangles;
|
||||
ds >> numberOfRectangles;
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
|
||||
RfbRectangle rect;
|
||||
for (int count = 0; count < numberOfRectangles; ++count)
|
||||
{
|
||||
ds >> rect.xpos >> rect.ypos >> rect.width >> rect.height >> rect.encoding;
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
|
||||
const int calculatedPixelData = rect.width * rect.height * s_bytePerPixel;
|
||||
if (calculatedPixelData <= 0)
|
||||
continue;
|
||||
rect.pixelData.resize(calculatedPixelData);
|
||||
if (rect.encoding == RAW_ENCODING)
|
||||
{
|
||||
ds.readRawData(rect.pixelData.data(), calculatedPixelData);
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
m_rectangles.append(rect);
|
||||
}
|
||||
else if (rect.encoding == HEXTILE_ENCODING)
|
||||
{
|
||||
decodeHextile(rect, ds);
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
m_rectangles.append(rect);
|
||||
}
|
||||
else if (rect.encoding == LAST_RECT_PSEUDO_ENCODING)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace STC
|
||||
} // namespace RfbProtocol
|
||||
} // namespace ProjectionWorkstation
|
||||
+393
@@ -0,0 +1,393 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QVector>
|
||||
#include <QDataStream>
|
||||
|
||||
namespace ProjectionWorkstation
|
||||
{
|
||||
|
||||
namespace RfbProtocol
|
||||
{
|
||||
|
||||
struct RfbPixelFormat
|
||||
{
|
||||
quint8 bitsPerPixel = 0;
|
||||
quint8 depth = 0;
|
||||
quint8 bigEndianFlag = 0;
|
||||
quint8 trueColourFlag = 0;
|
||||
quint8 redShift = 0;
|
||||
quint8 greenShift = 0;
|
||||
quint8 blueShift = 0;
|
||||
quint16 redMax = 0;
|
||||
quint16 greenMax = 0;
|
||||
quint16 blueMax = 0;
|
||||
};
|
||||
|
||||
struct RfbRectangle
|
||||
{
|
||||
QByteArray pixelData;
|
||||
quint16 xpos = 0;
|
||||
quint16 ypos = 0;
|
||||
quint16 width = 0;
|
||||
quint16 height = 0;
|
||||
qint32 encoding = 0;
|
||||
};
|
||||
|
||||
static constexpr int RAW_ENCODING = 0;
|
||||
static constexpr int HEXTILE_ENCODING = 5;
|
||||
static constexpr int LAST_RECT_PSEUDO_ENCODING = -224;
|
||||
|
||||
namespace STC
|
||||
{
|
||||
|
||||
enum Type
|
||||
{
|
||||
// Official Values v
|
||||
RFB_FRAMEBUFFER_UPDATE = 0,
|
||||
RFB_SET_COLORMAP = 1,
|
||||
RFB_RING_BELL = 2,
|
||||
RFB_SERVER_CUTTEXT = 3,
|
||||
|
||||
// User Defined Values v
|
||||
RFB_VERSION = 256,
|
||||
RFB_SUP_SECURITY_TYPES = 257,
|
||||
RFB_VNC_AUTHENTICATION = 258,
|
||||
RFB_SECURITY_RESULT = 259,
|
||||
RFB_SERVER_INIT = 260
|
||||
};
|
||||
|
||||
class ServerToClient
|
||||
{
|
||||
public:
|
||||
virtual ~ServerToClient() = default;
|
||||
friend QDataStream &operator>>(QDataStream &ds, ServerToClient &stc)
|
||||
{
|
||||
stc.fillDataFromStream(ds);
|
||||
return ds;
|
||||
}
|
||||
protected:
|
||||
explicit ServerToClient(Type type) : m_type(type) {}
|
||||
virtual void fillDataFromStream(QDataStream &ds) = 0;
|
||||
Type m_type;
|
||||
}; // ServerToClient
|
||||
|
||||
// --------------------------------------------------
|
||||
|
||||
class RfbVersion final : public ServerToClient
|
||||
{
|
||||
quint8 m_minor = 0;
|
||||
quint8 m_major = 0;
|
||||
|
||||
public:
|
||||
constexpr static Type TYPE = RFB_VERSION;
|
||||
|
||||
RfbVersion() : ServerToClient(TYPE) {}
|
||||
|
||||
quint8 majorVersion() const { return m_major; }
|
||||
quint8 minorVersion() const { return m_minor; }
|
||||
|
||||
void fillDataFromStream(QDataStream &ds) override final
|
||||
{
|
||||
QVarLengthArray<char, 12> data(12);
|
||||
ds.readRawData(data.data(), 12);
|
||||
if (ds.status() == QDataStream::Ok)
|
||||
{
|
||||
m_major = data[6] - '0';
|
||||
m_minor = data[10] - '0';
|
||||
}
|
||||
}
|
||||
}; // RfbVersion
|
||||
|
||||
// --------------------------------------------------
|
||||
|
||||
class RfbSupportedSecurityTypes final : public ServerToClient
|
||||
{
|
||||
QVector<quint8> m_secTypes;
|
||||
|
||||
public:
|
||||
constexpr static Type TYPE = RFB_SUP_SECURITY_TYPES;
|
||||
|
||||
RfbSupportedSecurityTypes() : ServerToClient(TYPE) {}
|
||||
|
||||
const QVector<quint8> &types() const { return m_secTypes; }
|
||||
|
||||
void fillDataFromStream(QDataStream &ds) override final
|
||||
{
|
||||
quint8 amountOfSupportedTypes;
|
||||
ds >> amountOfSupportedTypes;
|
||||
for (int count = 0; count < amountOfSupportedTypes; ++count)
|
||||
{
|
||||
quint8 type;
|
||||
ds >> type;
|
||||
m_secTypes.append(type);
|
||||
}
|
||||
}
|
||||
|
||||
}; // RfbSupportedSecurityTypes
|
||||
|
||||
// --------------------------------------------------
|
||||
|
||||
class RfbVncAuthentication final : public ServerToClient
|
||||
{
|
||||
quint64 m_challengeBlockA = 0;
|
||||
quint64 m_challengeBlockB = 0;
|
||||
|
||||
public:
|
||||
constexpr static Type TYPE = RFB_VNC_AUTHENTICATION;
|
||||
|
||||
RfbVncAuthentication() : ServerToClient(TYPE) {}
|
||||
|
||||
quint64 challengeBlockA() const { return m_challengeBlockA; }
|
||||
quint64 challengeBlockB() const { return m_challengeBlockB; }
|
||||
|
||||
void fillDataFromStream(QDataStream &ds) override final
|
||||
{
|
||||
ds >> m_challengeBlockA;
|
||||
ds >> m_challengeBlockB;
|
||||
}
|
||||
|
||||
}; // RfbVncAuthentication
|
||||
|
||||
// --------------------------------------------------
|
||||
|
||||
class RfbSecurityResult : public ServerToClient
|
||||
{
|
||||
QByteArray m_reason;
|
||||
quint32 m_resultCode = 0;
|
||||
|
||||
public:
|
||||
constexpr static Type TYPE = RFB_SECURITY_RESULT;
|
||||
|
||||
RfbSecurityResult() : ServerToClient(TYPE) {}
|
||||
|
||||
quint32 resultCode() const { return m_resultCode; }
|
||||
const QByteArray &reason() const { return m_reason; }
|
||||
|
||||
void fillDataFromStream(QDataStream &ds) override final
|
||||
{
|
||||
ds >> m_resultCode;
|
||||
if (ds.status() == QDataStream::Ok)
|
||||
{
|
||||
if (m_resultCode == 0)
|
||||
return;
|
||||
else
|
||||
{
|
||||
quint32 reasonLength;
|
||||
ds >> reasonLength;
|
||||
QByteArray reason(reasonLength, Qt::Uninitialized);
|
||||
ds.readRawData(reason.data(), reasonLength);
|
||||
m_reason = reason;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}; // RfbSecurityResult
|
||||
|
||||
// --------------------------------------------------
|
||||
|
||||
class RfbServerInit final : public ServerToClient
|
||||
{
|
||||
QByteArray m_name;
|
||||
RfbPixelFormat m_pixelFormat;
|
||||
quint16 m_frameBufferWidth = 0;
|
||||
quint16 m_frameBufferHeight = 0;
|
||||
|
||||
public:
|
||||
constexpr static Type TYPE = RFB_SERVER_INIT;
|
||||
|
||||
RfbServerInit() : ServerToClient(TYPE) {}
|
||||
|
||||
quint16 frameBufferWidth() const { return m_frameBufferWidth; }
|
||||
quint16 frameBufferHeight() const { return m_frameBufferHeight; }
|
||||
const RfbPixelFormat &pixelFormat() const { return m_pixelFormat; }
|
||||
const QByteArray &name() const { return m_name; }
|
||||
|
||||
void fillDataFromStream(QDataStream &ds) override final
|
||||
{
|
||||
ds >> m_frameBufferWidth >> m_frameBufferHeight;
|
||||
|
||||
ds >> m_pixelFormat.bitsPerPixel >> m_pixelFormat.depth >> m_pixelFormat.bigEndianFlag >> m_pixelFormat.trueColourFlag
|
||||
>> m_pixelFormat.redMax >> m_pixelFormat.greenMax >> m_pixelFormat.blueMax
|
||||
>> m_pixelFormat.redShift >> m_pixelFormat.greenShift >> m_pixelFormat.blueShift;
|
||||
|
||||
ds.skipRawData(3); // padding
|
||||
|
||||
quint32 nameLength;
|
||||
ds >> nameLength;
|
||||
QByteArray nameData(nameLength, Qt::Uninitialized);
|
||||
ds.readRawData(nameData.data(), nameLength);
|
||||
m_name = nameData;
|
||||
}
|
||||
|
||||
}; // RfbServerInit
|
||||
|
||||
// --------------------------------------------------
|
||||
|
||||
class RfbFramebufferUpdate final : public ServerToClient
|
||||
{
|
||||
QVector<RfbRectangle> m_rectangles;
|
||||
|
||||
public:
|
||||
RfbFramebufferUpdate() : ServerToClient(RFB_FRAMEBUFFER_UPDATE) {}
|
||||
|
||||
const QVector<RfbRectangle> &rectangles() const { return m_rectangles; }
|
||||
|
||||
void fillDataFromStream(QDataStream &ds) override final;
|
||||
|
||||
}; // RfbFramebufferUpdate
|
||||
|
||||
// --------------------------------------------------
|
||||
|
||||
class RfbSetColormap final : public ServerToClient
|
||||
{
|
||||
public:
|
||||
RfbSetColormap() : ServerToClient(RFB_SET_COLORMAP) {}
|
||||
|
||||
void fillDataFromStream(QDataStream &ds) override final
|
||||
{
|
||||
if (ds.status() == QDataStream::Ok)
|
||||
{
|
||||
quint8 padding;
|
||||
ds >> padding;
|
||||
quint16 firstColor, numberOfColors;
|
||||
ds >> firstColor >> numberOfColors;
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
qint32 payloadLen = numberOfColors + 3 * sizeof(quint16); // rgb, 16 bit
|
||||
// we don't care
|
||||
ds.skipRawData(payloadLen);
|
||||
}
|
||||
else
|
||||
ds.setStatus(QDataStream::ReadPastEnd);
|
||||
}
|
||||
}; // RfbSetColormap
|
||||
|
||||
// --------------------------------------------------
|
||||
|
||||
class RfbRingBell final : public ServerToClient
|
||||
{
|
||||
public:
|
||||
RfbRingBell() : ServerToClient(RFB_RING_BELL) {}
|
||||
|
||||
void fillDataFromStream(QDataStream &) override final
|
||||
{
|
||||
// nothing to do here
|
||||
}
|
||||
}; // RfbRingBell
|
||||
|
||||
// --------------------------------------------------
|
||||
|
||||
class RfbServerCutText final : public ServerToClient
|
||||
{
|
||||
QByteArray m_text;
|
||||
|
||||
public:
|
||||
RfbServerCutText() : ServerToClient(RFB_SERVER_CUTTEXT) {}
|
||||
|
||||
const QByteArray &text() const { return m_text; }
|
||||
|
||||
void fillDataFromStream(QDataStream &ds) override final
|
||||
{
|
||||
if (ds.status() == QDataStream::Ok)
|
||||
{
|
||||
ds.skipRawData(3); //padding
|
||||
quint32 length;
|
||||
ds >> length;
|
||||
QByteArray data(length, Qt::Uninitialized);
|
||||
ds.readRawData(data.data(), length);
|
||||
m_text = data;
|
||||
}
|
||||
else ds.setStatus(QDataStream::ReadPastEnd);
|
||||
}
|
||||
}; // RfbServerCutText
|
||||
|
||||
// --------------------------------------------------
|
||||
|
||||
} // STC
|
||||
|
||||
namespace CTS
|
||||
{
|
||||
|
||||
inline QByteArray RfbVersion(quint8 majorVersion, quint8 minorVersion)
|
||||
{
|
||||
QByteArray data;
|
||||
QString version = QStringLiteral("RFB 00%1.00%2\n");
|
||||
data.append(version.arg(majorVersion).arg(minorVersion).toLatin1());
|
||||
return data;
|
||||
}
|
||||
|
||||
inline QByteArray RfbSecurityType(quint8 type)
|
||||
{
|
||||
QByteArray data;
|
||||
data.append(type);
|
||||
return data;
|
||||
}
|
||||
|
||||
inline QByteArray RfbVncAuthentication(quint64 blockA, quint64 blockB)
|
||||
{
|
||||
QByteArray data;
|
||||
QDataStream stream(&data, QIODevice::WriteOnly);
|
||||
stream << blockA << blockB;
|
||||
return data;
|
||||
}
|
||||
|
||||
inline QByteArray RfbClientInit(bool sharedConnection)
|
||||
{
|
||||
quint8 share = sharedConnection ? 1 : 0;
|
||||
QByteArray data;
|
||||
data.append(share);
|
||||
return data;
|
||||
}
|
||||
|
||||
inline QByteArray RfbFrameBufferUpdateRequest(bool isIncremental, quint16 xPos, quint16 yPos, quint16 width, quint16 height)
|
||||
{
|
||||
quint8 type = 3;
|
||||
quint8 incremental = isIncremental ? 1 : 0;
|
||||
QByteArray data;
|
||||
QDataStream stream(&data, QIODevice::WriteOnly);
|
||||
stream << type << incremental << xPos << yPos << width << height;
|
||||
return data;
|
||||
}
|
||||
|
||||
inline QByteArray RfbPointerEvent(quint8 buttonMask, quint16 xpos, quint16 ypos)
|
||||
{
|
||||
quint8 type = 5;
|
||||
QByteArray data;
|
||||
QDataStream stream(&data, QIODevice::WriteOnly);
|
||||
stream << type << buttonMask << xpos << ypos;
|
||||
return data;
|
||||
}
|
||||
|
||||
inline QByteArray RfbKeyEvent(quint32 key, bool isPressed)
|
||||
{
|
||||
quint8 type = 4;
|
||||
quint8 pressed = isPressed ? 1 : 0;
|
||||
QByteArray data;
|
||||
QDataStream stream(&data, QIODevice::WriteOnly);
|
||||
stream << type << pressed << quint16(0) << key;
|
||||
return data;
|
||||
}
|
||||
|
||||
inline QByteArray RfbSetEncodings(const QVector<qint32> &encodings)
|
||||
{
|
||||
quint8 type = 2;
|
||||
QByteArray data;
|
||||
QDataStream stream(&data, QIODevice::WriteOnly);
|
||||
stream << type << quint8(0) << quint16(encodings.length());
|
||||
for (qint32 encoding : encodings)
|
||||
stream << encoding;
|
||||
return data;
|
||||
}
|
||||
|
||||
} // CTS
|
||||
|
||||
} // RfbProtocol
|
||||
|
||||
} // ProjectionWorkstation
|
||||
@@ -0,0 +1,345 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "pws_vnc_handler.h"
|
||||
#include "pws_des_encryption.h"
|
||||
|
||||
namespace ProjectionWorkstation
|
||||
{
|
||||
|
||||
|
||||
VncHandler::VncHandler(const QHostAddress &host, quint16 port, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_logger(LtsProjectionWorkstation::getLogger("VncHandler"))
|
||||
, m_nextType(RfbProtocol::STC::RFB_VERSION)
|
||||
, m_socket(new QTcpSocket(this))
|
||||
, m_host(host)
|
||||
, m_port(port)
|
||||
{
|
||||
connect(m_socket, &QTcpSocket::readyRead, this, &VncHandler::onReadyRead);
|
||||
connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &VncHandler::error);
|
||||
connect(m_socket, &QTcpSocket::disconnected, this, &VncHandler::slotOnDisconnected);
|
||||
}
|
||||
|
||||
void VncHandler::connectToHost()
|
||||
{
|
||||
m_socket->connectToHost(m_host, m_port);
|
||||
}
|
||||
|
||||
void VncHandler::connectToHost(const QHostAddress &host, quint16 port)
|
||||
{
|
||||
m_socket->connectToHost(host, port);
|
||||
}
|
||||
|
||||
void VncHandler::onReadyRead()
|
||||
{
|
||||
m_buffer += m_socket->readAll();
|
||||
|
||||
QDataStream stream(m_buffer);
|
||||
stream.setByteOrder(QDataStream::BigEndian);
|
||||
qint64 readPos = 0;
|
||||
while (handleData(stream))
|
||||
readPos = stream.device()->pos();
|
||||
|
||||
// TODO: squeeze m_buffer only when needed (e.g is larger than x MB)
|
||||
if (readPos > 0)
|
||||
m_buffer = m_buffer.mid(readPos);
|
||||
}
|
||||
|
||||
bool VncHandler::handleData(QDataStream &stream)
|
||||
{
|
||||
switch (m_nextType)
|
||||
{
|
||||
// -------------------------------------------------------------------------------------------
|
||||
case RfbProtocol::STC::RFB_VERSION:
|
||||
{
|
||||
RfbProtocol::STC::RfbVersion version;
|
||||
stream.startTransaction();
|
||||
stream >> version;
|
||||
if (!stream.commitTransaction())
|
||||
return false;
|
||||
if (handleVersion(version))
|
||||
{
|
||||
setNextExpectedMessageType(RfbProtocol::STC::RFB_SUP_SECURITY_TYPES);
|
||||
DEBUG_LOG(m_logger) << "RfbVersion handled Successfully!";
|
||||
return true;
|
||||
}
|
||||
ERROR_LOG(m_logger) << "RfbVersion handshake Error!";
|
||||
break;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------
|
||||
case RfbProtocol::STC::RFB_SUP_SECURITY_TYPES:
|
||||
{
|
||||
RfbProtocol::STC::RfbSupportedSecurityTypes supTypes;
|
||||
stream.startTransaction();
|
||||
stream >> supTypes;
|
||||
if (!stream.commitTransaction())
|
||||
return false;
|
||||
if (handleSupportedSecurityTypes(supTypes))
|
||||
{
|
||||
setNextExpectedMessageType(RfbProtocol::STC::RFB_VNC_AUTHENTICATION);
|
||||
DEBUG_LOG(m_logger) << "RfbSecurityType handled Successfully!";
|
||||
return true;
|
||||
}
|
||||
ERROR_LOG(m_logger) << "RfbSecurityType handshake Error!";
|
||||
break;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------
|
||||
case RfbProtocol::STC::RFB_VNC_AUTHENTICATION:
|
||||
{
|
||||
RfbProtocol::STC::RfbVncAuthentication auth;
|
||||
stream.startTransaction();
|
||||
stream >> auth;
|
||||
if (!stream.commitTransaction())
|
||||
return false;
|
||||
if (handleVncAuthentication(auth))
|
||||
{
|
||||
setNextExpectedMessageType(RfbProtocol::STC::RFB_SECURITY_RESULT);
|
||||
DEBUG_LOG(m_logger) << "RfbVncAuthentication handled Successfully!";
|
||||
return true;
|
||||
}
|
||||
ERROR_LOG(m_logger) << "RfbVncAuthentication Error!";
|
||||
break;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------
|
||||
case RfbProtocol::STC::RFB_SECURITY_RESULT:
|
||||
{
|
||||
RfbProtocol::STC::RfbSecurityResult result;
|
||||
stream.startTransaction();
|
||||
stream >> result;
|
||||
if (!stream.commitTransaction())
|
||||
return false;
|
||||
if (handleSecurityResult(result))
|
||||
{
|
||||
setNextExpectedMessageType(RfbProtocol::STC::RFB_SERVER_INIT);
|
||||
DEBUG_LOG(m_logger) << "RfbSecurityResult handled Successfully!";
|
||||
return true;
|
||||
}
|
||||
ERROR_LOG(m_logger) << "RfbSecurityResult Error!";
|
||||
break;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------
|
||||
case RfbProtocol::STC::RFB_SERVER_INIT:
|
||||
{
|
||||
RfbProtocol::STC::RfbServerInit init;
|
||||
stream.startTransaction();
|
||||
stream >> init;
|
||||
if (!stream.commitTransaction())
|
||||
return false;
|
||||
if (handleServerInit(init))
|
||||
{
|
||||
QByteArray data = RfbProtocol::CTS::RfbSetEncodings({qint32(RfbProtocol::HEXTILE_ENCODING),
|
||||
qint32(RfbProtocol::RAW_ENCODING),
|
||||
qint32(RfbProtocol::LAST_RECT_PSEUDO_ENCODING),
|
||||
qint32(-247), // Compression Level Pseudo-encoding (-247 high .. -255 low)
|
||||
qint32(-23), // JPEG Quality Level Pseudo-encoding (-23 high quality .. -32 low quality)
|
||||
});
|
||||
|
||||
m_socket->write(data);
|
||||
|
||||
emit connected();
|
||||
m_socket->write(RfbProtocol::CTS::RfbFrameBufferUpdateRequest(false, 0, 0, 1920, 1200));
|
||||
setNextExpectedMessageType(RfbProtocol::STC::RFB_FRAMEBUFFER_UPDATE);
|
||||
DEBUG_LOG(m_logger) << "RfbServerInit handled Successfully!";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------
|
||||
case RfbProtocol::STC::RFB_FRAMEBUFFER_UPDATE:
|
||||
{
|
||||
// The handling of 'next expected type is flawed' - the order and next type is only defined during setup,
|
||||
// not during runtime - here we can get any Server to Client Message defined in the rfb protocol. So we
|
||||
// have to take a look at the protocol byte and handle accordingly
|
||||
// by default we must only be able to decode 0 (Framebuffer update), 1 (SetColourMapEntries), 2 (Bell) and 3 (ServerCutText)
|
||||
// also using QDataStream here only works by accident... but we won't change it now (never touch a running system)
|
||||
stream.startTransaction();
|
||||
quint8 msgType;
|
||||
stream >> msgType;
|
||||
DEBUG_LOG(m_logger) << "got message from server: " << QString::number(msgType, 16);
|
||||
switch (msgType)
|
||||
{
|
||||
case RfbProtocol::STC::RFB_FRAMEBUFFER_UPDATE:
|
||||
return handleFrameBufferUpdate(stream);
|
||||
case RfbProtocol::STC::RFB_SET_COLORMAP:
|
||||
return handleSetColormap(stream);
|
||||
case RfbProtocol::STC::RFB_RING_BELL:
|
||||
return handleRingBell(stream);
|
||||
case RfbProtocol::STC::RFB_SERVER_CUTTEXT:
|
||||
return handleServerCuttext(stream);
|
||||
default:
|
||||
WARNING_LOG(m_logger) << "Unknown message type from server (" << QString::number(msgType, 16) << ") - reconnecting";
|
||||
stream.abortTransaction();
|
||||
slotOnDisconnected();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
Q_ASSERT(false);
|
||||
stream.abortTransaction();
|
||||
slotOnDisconnected();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VncHandler::handleFrameBufferUpdate(QDataStream &stream)
|
||||
{
|
||||
RfbProtocol::STC::RfbFramebufferUpdate update;
|
||||
stream.startTransaction();
|
||||
stream >> update;
|
||||
bool bRet = stream.commitTransaction();
|
||||
if (bRet)
|
||||
{
|
||||
if (!update.rectangles().isEmpty())
|
||||
emit frameBufferUpdateReceived(update.rectangles());
|
||||
m_socket->write(RfbProtocol::CTS::RfbFrameBufferUpdateRequest(true, 0, 0, 1920, 1200));
|
||||
}
|
||||
return bRet;
|
||||
}
|
||||
|
||||
bool VncHandler::handleSetColormap(QDataStream &stream)
|
||||
{
|
||||
RfbProtocol::STC::RfbSetColormap setColormap;
|
||||
stream.startTransaction();
|
||||
stream >> setColormap;
|
||||
return stream.commitTransaction();
|
||||
}
|
||||
|
||||
bool VncHandler::handleRingBell(QDataStream &stream)
|
||||
{
|
||||
RfbProtocol::STC::RfbRingBell cutText;
|
||||
stream.startTransaction();
|
||||
stream >> cutText;
|
||||
bool bRet = stream.commitTransaction();
|
||||
emit beepReceived();
|
||||
return bRet;
|
||||
}
|
||||
|
||||
bool VncHandler::handleServerCuttext(QDataStream &stream)
|
||||
{
|
||||
RfbProtocol::STC::RfbServerCutText cutText;
|
||||
stream.startTransaction();
|
||||
stream >> cutText;
|
||||
bool bRet = stream.commitTransaction();
|
||||
if (bRet)
|
||||
emit cutTextReceived(QString::fromLatin1(cutText.text()));
|
||||
return bRet;
|
||||
}
|
||||
|
||||
void VncHandler::slotOnDisconnected()
|
||||
{
|
||||
resetHandler();
|
||||
emit disconnected();
|
||||
}
|
||||
|
||||
void VncHandler::writeData(const QByteArray &data)
|
||||
{
|
||||
m_socket->write(data);
|
||||
m_socket->flush();
|
||||
}
|
||||
|
||||
void VncHandler::error()
|
||||
{
|
||||
ERROR_LOG(m_logger) << "SocketError " << m_socket->errorString();
|
||||
}
|
||||
|
||||
void VncHandler::resetHandler()
|
||||
{
|
||||
m_buffer.clear();
|
||||
setNextExpectedMessageType(RfbProtocol::STC::RFB_VERSION);
|
||||
m_serverInit = RfbProtocol::STC::RfbServerInit();
|
||||
}
|
||||
|
||||
// Data Handlers
|
||||
|
||||
bool VncHandler::handleVersion(const RfbProtocol::STC::RfbVersion &version)
|
||||
{
|
||||
if (version.majorVersion() == 3 && version.minorVersion() == 8)
|
||||
{
|
||||
m_socket->write(RfbProtocol::CTS::RfbVersion(version.majorVersion(),
|
||||
version.minorVersion()));
|
||||
return true;
|
||||
}
|
||||
ERROR_LOG(m_logger) << "Unsupported Version: " << version.majorVersion() << "." << version.minorVersion();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VncHandler::handleSupportedSecurityTypes(const RfbProtocol::STC::RfbSupportedSecurityTypes &supportedTypes)
|
||||
{
|
||||
if (supportedTypes.types().contains(2) && !supportedTypes.types().contains(0))
|
||||
{
|
||||
m_socket->write(RfbProtocol::CTS::RfbSecurityType(2));
|
||||
return true;
|
||||
}
|
||||
QString typesString;
|
||||
for (quint8 type : supportedTypes.types())
|
||||
typesString.append(QString::number(type)).append(QLatin1Char(' '));
|
||||
if (typesString.isEmpty()) typesString = QStringLiteral("EMPTY");
|
||||
ERROR_LOG(m_logger) << "Unsupported Security Types: " << typesString;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VncHandler::handleVncAuthentication(const RfbProtocol::STC::RfbVncAuthentication &authentication)
|
||||
{
|
||||
quint64 key = convertPassword("ctc11de");
|
||||
quint64 blockA = 0, blockB = 0;
|
||||
DES des(key);
|
||||
blockA = des.encrypt(authentication.challengeBlockA());
|
||||
blockB = des.encrypt(authentication.challengeBlockB());
|
||||
m_socket->write(RfbProtocol::CTS::RfbVncAuthentication(blockA, blockB));
|
||||
return true;
|
||||
}
|
||||
|
||||
// VNC Servers require the password with reversed bytes
|
||||
// and a length of 8bytes (truncated or padded if longer/smaller)
|
||||
quint64 VncHandler::convertPassword(const char *password)
|
||||
{
|
||||
quint64 result = 0;
|
||||
const size_t len = strlen(password);
|
||||
for (quint8 count = 0; count < 8; ++count)
|
||||
{
|
||||
quint8 data;
|
||||
result <<= 8;
|
||||
if (count < len)
|
||||
data = reverseByte(quint8(password[count]));
|
||||
else
|
||||
data = 0;
|
||||
result = (result | data);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
quint8 VncHandler::reverseByte(quint8 b)
|
||||
{
|
||||
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
|
||||
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
|
||||
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
|
||||
return b;
|
||||
}
|
||||
|
||||
bool VncHandler::handleSecurityResult(const RfbProtocol::STC::RfbSecurityResult &result)
|
||||
{
|
||||
if (result.resultCode() == 0)
|
||||
{
|
||||
m_socket->write(RfbProtocol::CTS::RfbClientInit(true));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(m_logger) << result.reason();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool VncHandler::handleServerInit(const RfbProtocol::STC::RfbServerInit &serverInit)
|
||||
{
|
||||
m_serverInit = serverInit;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // ProjectionWorkstation
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "pws_rfb_protocol.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QTcpSocket>
|
||||
#include <QDataStream>
|
||||
#include <QHostAddress>
|
||||
|
||||
#include <Plugins/ProjectionWorkstation/Common/pwscommon_export.h>
|
||||
#include <LtsProjectionWorkstation/logger.h>
|
||||
|
||||
namespace ProjectionWorkstation
|
||||
{
|
||||
|
||||
class PWSCOMMON_EXPORT VncHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
VncHandler(const QHostAddress &host, quint16 port, QObject *parent = nullptr);
|
||||
void setNextExpectedMessageType(RfbProtocol::STC::Type type) { m_nextType = type; }
|
||||
void connectToHost();
|
||||
void connectToHost(const QHostAddress &host, quint16 port);
|
||||
void writeData(const QByteArray &data);
|
||||
|
||||
signals:
|
||||
void connected();
|
||||
void disconnected();
|
||||
void frameBufferUpdateReceived(const QVector<RfbProtocol::RfbRectangle> &rectangles);
|
||||
void beepReceived();
|
||||
void cutTextReceived(const QString &txt);
|
||||
|
||||
private slots:
|
||||
void onReadyRead();
|
||||
void slotOnDisconnected();
|
||||
void error();
|
||||
|
||||
private:
|
||||
bool handleData(QDataStream &stream);
|
||||
bool handleFrameBufferUpdate(QDataStream &stream);
|
||||
bool handleSetColormap(QDataStream &stream);
|
||||
bool handleRingBell(QDataStream &stream);
|
||||
bool handleServerCuttext(QDataStream &stream);
|
||||
void resetHandler();
|
||||
bool handleVersion(const RfbProtocol::STC::RfbVersion &version);
|
||||
bool handleSupportedSecurityTypes(const RfbProtocol::STC::RfbSupportedSecurityTypes &supportedTypes);
|
||||
bool handleVncAuthentication(const RfbProtocol::STC::RfbVncAuthentication &authentication);
|
||||
static quint64 convertPassword(const char *password);
|
||||
static quint8 reverseByte(quint8 b);
|
||||
bool handleSecurityResult(const RfbProtocol::STC::RfbSecurityResult &result);
|
||||
bool handleServerInit(const RfbProtocol::STC::RfbServerInit &serverInit);
|
||||
|
||||
LtsProjectionWorkstation::LOGGER_T m_logger;
|
||||
RfbProtocol::STC::Type m_nextType;
|
||||
RfbProtocol::STC::RfbServerInit m_serverInit;
|
||||
QTcpSocket *m_socket;
|
||||
QByteArray m_buffer;
|
||||
QHostAddress m_host;
|
||||
quint16 m_port;
|
||||
|
||||
};
|
||||
|
||||
} // ProjectionWorkstation
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
SET(pws_gui_SRCS
|
||||
pws_gui_plugin.h
|
||||
pws_gui_plugin.cpp
|
||||
|
||||
pws_control_dialog.h
|
||||
pws_control_dialog.cpp
|
||||
|
||||
pws_vnc_viewer_dialog.h
|
||||
pws_vnc_viewer_dialog.cpp
|
||||
|
||||
pws_operator_settings_loader.h
|
||||
pws_operator_settings_loader.cpp
|
||||
|
||||
WebSocket/pws_web_socket_client.h
|
||||
WebSocket/pws_web_socket_client.cpp
|
||||
)
|
||||
|
||||
NGL_WRAP_UI(pws_gui_SRCS
|
||||
pws_control_dialog.ui
|
||||
pws_vnc_viewer_dialog.ui
|
||||
)
|
||||
|
||||
NGL_ADD_LIBRARY(WS PwsGuiPlugin MODULE ${pws_gui_SRCS})
|
||||
NGL_TARGET_LINK_LIBRARIES(PwsGuiPlugin
|
||||
ngltools
|
||||
NGLLogger
|
||||
nglsettings
|
||||
Qt5::Widgets
|
||||
Qt5::Core
|
||||
Qt5::Network
|
||||
Qt5::OpenGL
|
||||
LtsSession
|
||||
LtsWidgets
|
||||
LtsUtilsGuiDds
|
||||
LtsUtilsSettings
|
||||
LtsSessionData
|
||||
LtsProjectionWorkstation
|
||||
LtsWebSocketServer
|
||||
PwsCommon
|
||||
svcPwsCommon
|
||||
AsmDataPluginInterface
|
||||
FltDataPluginInterface
|
||||
OpmDataPluginInterface
|
||||
OpmSettingsGuiPluginInterface
|
||||
)
|
||||
|
||||
NGL_INSTALL_TARGET(PwsGuiPlugin)
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "pws_web_socket_client.h"
|
||||
#include <LtsProjectionWorkstation/WebSocket/message_factory.h>
|
||||
|
||||
namespace ProjectionWorkstation
|
||||
{
|
||||
|
||||
WebSocketClient::WebSocketClient(LtsProjectionWorkstation::MessageFactory *messageFactory, QObject *parent)
|
||||
: LtsWebSocket::ClientConnection(nullptr, parent)
|
||||
, m_messageFactory(messageFactory)
|
||||
{
|
||||
connect(this, &LtsWebSocket::ClientConnection::newTextMessage, this, &WebSocketClient::onNewTextMessage);
|
||||
init();
|
||||
}
|
||||
|
||||
void WebSocketClient::onNewTextMessage(LtsWebSocket::ClientConnection *client, const QString &message)
|
||||
{
|
||||
LtsWebSocket::AbstractClientMessage *clientMessage = nullptr;
|
||||
auto result = m_messageFactory->parseMessage(message);
|
||||
if (QJsonParseError::NoError == result.error)
|
||||
clientMessage = m_messageFactory->createMessage(client, message);
|
||||
else
|
||||
{
|
||||
// WARNING_LOG(m_logger) << "Could not parse incoming message: '" << message << "', error: " << result.errorString();
|
||||
clientMessage = new LtsWebSocket::InvalidMessage(client, QString(), QStringLiteral("Got message which is not JSON conform"));
|
||||
}
|
||||
|
||||
if (clientMessage)
|
||||
clientMessage->process();
|
||||
|
||||
delete clientMessage;
|
||||
}
|
||||
|
||||
} // ProjectionWorkstation
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <LtsWebSocket/client_connection.h>
|
||||
|
||||
namespace LtsProjectionWorkstation { class MessageFactory; }
|
||||
|
||||
namespace ProjectionWorkstation
|
||||
{
|
||||
|
||||
class WebSocketClient : public LtsWebSocket::ClientConnection
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WebSocketClient(LtsProjectionWorkstation::MessageFactory *messageFactory, QObject *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void onNewTextMessage(ClientConnection *client, const QString &message);
|
||||
|
||||
private:
|
||||
LtsProjectionWorkstation::MessageFactory *m_messageFactory;
|
||||
};
|
||||
|
||||
} // ProjectionWorstation
|
||||
@@ -0,0 +1,416 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#include "pws_control_dialog.h"
|
||||
#include "ui_pws_control_dialog.h"
|
||||
|
||||
#include "pws_vnc_viewer_dialog.h"
|
||||
|
||||
#include <LtsUtilsSettings/settings.h>
|
||||
#include <LtsSession/lts_session.h>
|
||||
#include <LtsSession/lts_session_data.h>
|
||||
#include <LtsProjectionWorkstation/vnc_server_manager.h>
|
||||
|
||||
#include <nglsettings/nglsettings_connections.h>
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QUuid>
|
||||
#include <QHostInfo>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace ProjectionWorkstation
|
||||
{
|
||||
|
||||
ControlDialog::ControlDialog(const QString &menuGroup, QWidget *parent)
|
||||
: LtsWidgets::LtsDialog(parent)
|
||||
, m_ui(new Ui::ControlDialog)
|
||||
, m_logger(LtsProjectionWorkstation::getLogger(QStringLiteral("ControlDialog")))
|
||||
, m_messageFactory(nullptr)
|
||||
, m_webSocketClient(nullptr)
|
||||
, m_session(nullptr)
|
||||
, m_menuGroup(menuGroup)
|
||||
, m_exerciseId(-1)
|
||||
, m_locationId(-1)
|
||||
, m_isConnected(false)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
connect(m_ui->pbnConnect, &QPushButton::clicked, this, &ControlDialog::slotOnConnectClicked);
|
||||
connect(m_ui->pbnLogout, &QPushButton::clicked, this, &ControlDialog::slotOnLogoutClicked);
|
||||
connect(m_ui->chbInThisLocationOnly, &QCheckBox::toggled, this, &ControlDialog::fillCombobox);
|
||||
connect(m_ui->chbProjectionWSOnly, &QCheckBox::toggled, this, &ControlDialog::fillCombobox);
|
||||
|
||||
connect(m_ui->cbxWorkstations, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, &ControlDialog::slotCbxWorkstationsChanged);
|
||||
|
||||
m_ui->pbnLogout->setEnabled(m_isConnected);
|
||||
m_ui->chbProjectionWSOnly->setVisible(false);
|
||||
|
||||
#ifdef LTS_DEBUG
|
||||
m_ui->chbProjectionWSOnly->setVisible(true);
|
||||
#endif
|
||||
|
||||
setOnlineHelpFilename(QStringLiteral("Situation-Projection_Control"));
|
||||
}
|
||||
|
||||
|
||||
ControlDialog::~ControlDialog()
|
||||
{
|
||||
if (m_webSocketClient)
|
||||
{
|
||||
// NLDEVCTRL-808: OpApp stürzt ab beim beenden (ProjWS)
|
||||
// at initWebSocketHandler the underlying QWebSocket is used for several
|
||||
// connections. But the disconnect was missing. Cleanup a little bit...
|
||||
|
||||
// disconnect in reverse order:
|
||||
// 1) disconnect from underlying QWebSocket
|
||||
if (m_webSocketClient->webSocket())
|
||||
m_webSocketClient->webSocket()->disconnect(); // to avoid our slots to be called
|
||||
|
||||
// 2) disconnect from m_webSocketClient
|
||||
m_webSocketClient->disconnect(this);
|
||||
|
||||
// add reply while destroying?
|
||||
const int userId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
|
||||
m_webSocketClient->addReply((new LtsProjectionWorkstation::CTS::DisconnectRequest(QUuid::createUuid().toString(), userId))->toReplyMessage());
|
||||
|
||||
// m_webSocketClient->deleteLater(); // ControlDialog is parent of WebSocketClient, therefore useless!?!
|
||||
}
|
||||
delete m_messageFactory;
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
|
||||
void ControlDialog::initWebSocketHandler()
|
||||
{
|
||||
if (!m_messageFactory)
|
||||
m_messageFactory = new LtsProjectionWorkstation::MessageFactory(this, m_logger);
|
||||
delete m_webSocketClient;
|
||||
m_webSocketClient = new WebSocketClient(m_messageFactory, this);
|
||||
auto s = m_webSocketClient->webSocket();
|
||||
connect(s, &QWebSocket::connected, this, &ControlDialog::slotOnWebSocketConnected);
|
||||
connect(s, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), this, &ControlDialog::slotOnWebSocketError);
|
||||
connect(s, QOverload<const QList<QSslError>&>::of(&QWebSocket::sslErrors), this, &ControlDialog::slotOnWebSocketSslError);
|
||||
connect(s, &QWebSocket::disconnected, this, &ControlDialog::slotOnWebSocketDisconnected);
|
||||
}
|
||||
|
||||
|
||||
bool ControlDialog::init(LtsSession::LtsSession *session,
|
||||
const std::shared_ptr<OperatorManagement::Interface::Dtc> &opmDtc,
|
||||
const std::shared_ptr<OperatorManagement::ForeignSettingsGuiInterface> &opmIfc,
|
||||
const std::shared_ptr<AssetManagement::Interface::Dtc> &asmDtc, int exerciseId)
|
||||
{
|
||||
if (!session)
|
||||
return false;
|
||||
|
||||
m_session = session;
|
||||
|
||||
m_opmDtc = opmDtc;
|
||||
m_opmIfc = opmIfc;
|
||||
m_asmDtc = asmDtc;
|
||||
|
||||
if (exerciseId < 0) return false;
|
||||
m_exerciseId = exerciseId;
|
||||
|
||||
LtsSessionData::LtsSessionData *sessionData = LtsSessionData::LtsSessionData::getInstance();
|
||||
auto getLocationIdFunc =
|
||||
[sessionData, this](const AssetManagement::Asset & data) -> bool
|
||||
{
|
||||
if (data.id() == sessionData->getWorkstationId())
|
||||
{
|
||||
m_locationId = data.locationId();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
m_asmDtc->assets(std::function<bool(const AssetManagement::Asset &data)>(getLocationIdFunc));
|
||||
|
||||
fillCombobox();
|
||||
|
||||
QString txt = m_ui->cbxWorkstations->currentText();
|
||||
|
||||
if (!txt.isEmpty())
|
||||
{
|
||||
QHostInfo info = QHostInfo::fromName(txt);
|
||||
if (info.addresses().size() > 0)
|
||||
m_host = info.addresses().at(0);
|
||||
else
|
||||
ERROR_LOG(m_logger) << "Address for host " << m_ui->cbxWorkstations->currentText() << " not found!";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ControlDialog::aboutToClose()
|
||||
{
|
||||
for (VncViewerDialog *diag : qAsConst(m_viewers))
|
||||
diag->closeRequest();
|
||||
m_viewers.clear();
|
||||
|
||||
if (m_webSocketClient)
|
||||
{
|
||||
const int userId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
|
||||
m_webSocketClient->addReply((new LtsProjectionWorkstation::CTS::DisconnectRequest(QUuid::createUuid().toString(), userId))->toReplyMessage());
|
||||
m_webSocketClient->webSocket()->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ControlDialog::slotOnConnectClicked()
|
||||
{
|
||||
TRACE_LOG(m_logger) << LOGFUNCTION << " START, this=" << this;
|
||||
if (!m_isConnected)
|
||||
{
|
||||
initWebSocketHandler();
|
||||
int port = NGL::Connections::port(NGL::Connections::eConnection::PWS_WEBSOCKET);
|
||||
if (m_webSocketClient)
|
||||
m_webSocketClient->webSocket()->open(QUrl(QStringLiteral("ws://%1:%2/").arg(m_host.toString()).arg(port)));
|
||||
m_ui->pbnConnect->setEnabled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_webSocketClient)
|
||||
{
|
||||
const int userId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
|
||||
m_webSocketClient->addReply((new LtsProjectionWorkstation::CTS::DisconnectRequest(QUuid::createUuid().toString(), userId))->toReplyMessage());
|
||||
m_webSocketClient->webSocket()->close();
|
||||
}
|
||||
}
|
||||
DEBUG_LOG(m_logger) << LOGFUNCTION << " connect clicked, this=" << this;
|
||||
updateConnectButton();
|
||||
TRACE_LOG(m_logger) << LOGFUNCTION << " END, this=" << this;
|
||||
}
|
||||
|
||||
void ControlDialog::slotOnLogoutClicked()
|
||||
{
|
||||
if (m_webSocketClient)
|
||||
{
|
||||
m_webSocketClient->addReply((new LtsProjectionWorkstation::CTS::LogoutRequest(QUuid::createUuid().toString()))->toReplyMessage());
|
||||
m_webSocketClient->webSocket()->close();
|
||||
}
|
||||
DEBUG_LOG(m_logger) << LOGFUNCTION << " logout clicked, this=" << this;
|
||||
updateConnectButton();
|
||||
}
|
||||
|
||||
|
||||
void ControlDialog::slotOnWebSocketConnected()
|
||||
{
|
||||
LtsSessionData::LtsSessionData *sessioData = LtsSessionData::LtsSessionData::getInstance();
|
||||
int userId = sessioData->getOperatorId();
|
||||
int exerciseId = m_exerciseId;
|
||||
OperatorManagement::User user = m_opmDtc->user(userId);
|
||||
if (user.id() > 0)
|
||||
{
|
||||
// NLDEVCTRL-445: Projektion Filter und Kartenausschnitt
|
||||
// very new app 'Lage', workspace isn`t saved yet and therefore no workspaceId.
|
||||
// But callWorkspaceSaveUnsaved needs an workspaceId for transfering the workspace with filter:
|
||||
QString workspaceIdToUse = m_session->workspaceId();
|
||||
if (workspaceIdToUse.isEmpty())
|
||||
{
|
||||
workspaceIdToUse = QUuid::createUuid().toString();
|
||||
INFO_LOG(m_logger) << "workspaceIdToUse " << workspaceIdToUse;
|
||||
}
|
||||
m_opmIfc->callWorkspaceSaveUnsaved(workspaceIdToUse);
|
||||
LtsUtils::OperatorSettings* opSettings = LtsUtils::OperatorSettings::getInstance();
|
||||
opSettings->setValue(QStringLiteral("PwsWorkspace"), QStringLiteral("CurrentId"), workspaceIdToUse);
|
||||
QByteArray opSettingsData = LtsUtils::OperatorSettings::getInstance()->exportSettings();
|
||||
|
||||
// This does not work since m_session->setWorkspaceId() triggers loading of filters on controlling WS :-(
|
||||
// if (m_session->workspaceId().isEmpty())
|
||||
// {
|
||||
// const QString newId(QUuid::createUuid().toString());
|
||||
// INFO_LOG(m_logger) << "new WsId" << newId;
|
||||
// m_session->setWorkspaceId(newId);
|
||||
// }
|
||||
// m_opmIfc->callWorkspaceSaveUnsaved();
|
||||
// LtsUtils::OperatorSettings* opSettings = LtsUtils::OperatorSettings::getInstance();
|
||||
// opSettings->setValue(QStringLiteral("PwsWorkspace"), QStringLiteral("CurrentId"), m_session->workspaceId());
|
||||
// QByteArray opSettingsData = LtsUtils::OperatorSettings::getInstance()->exportSettings();
|
||||
|
||||
INFO_LOG(m_logger) << " connect userId=" << userId
|
||||
<< ", with prio=" << user.projectionPriority()
|
||||
<< ", PwsWorkspace/Id='" << workspaceIdToUse << "'";
|
||||
|
||||
LtsProjectionWorkstation::CTS::LoginRequest req(QUuid::createUuid().toString(),
|
||||
userId,
|
||||
user.projectionPriority(),
|
||||
exerciseId, opSettingsData);
|
||||
m_webSocketClient->addReply(req.toReplyMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ControlDialog::slotOnVncDisconnected()
|
||||
{
|
||||
INFO_LOG(m_logger) << " slotOnVncDisconnected";
|
||||
|
||||
if (m_webSocketClient)
|
||||
{
|
||||
const int userId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
|
||||
m_webSocketClient->addReply((new LtsProjectionWorkstation::CTS::DisconnectRequest(QUuid::createUuid().toString(), userId))->toReplyMessage());
|
||||
}
|
||||
m_isConnected = false;
|
||||
if (!m_ui || !m_ui->pbnConnect)
|
||||
return;
|
||||
m_ui->pbnConnect->setEnabled(true);
|
||||
DEBUG_LOG(m_logger) << LOGFUNCTION << " VNC disconnected, this=" << this;
|
||||
updateConnectButton();
|
||||
}
|
||||
|
||||
void ControlDialog::slotOnWebSocketDisconnected()
|
||||
{
|
||||
for (VncViewerDialog *diag : qAsConst(m_viewers))
|
||||
diag->closeRequest(); // will be deleted automatically
|
||||
m_viewers.clear();
|
||||
m_isConnected = false;
|
||||
if (m_ui && m_ui->pbnConnect)
|
||||
m_ui->pbnConnect->setEnabled(true);
|
||||
DEBUG_LOG(m_logger) << LOGFUNCTION << " WEBSOCKET disconnected, this=" << this;
|
||||
updateConnectButton();
|
||||
}
|
||||
|
||||
|
||||
void ControlDialog::slotOnWebSocketError()
|
||||
{
|
||||
ERROR_LOG(m_logger) << m_webSocketClient->webSocket()->errorString();
|
||||
LtsWidgets::LtsMessageDialog::showMessage(this, m_webSocketClient->webSocket()->errorString(), tr("Connection to the presentation workstation failed!"));
|
||||
m_isConnected = false;
|
||||
m_ui->pbnConnect->setEnabled(true);
|
||||
DEBUG_LOG(m_logger) << LOGFUNCTION << " VWEBSOCKET ERROR, this=" << this;
|
||||
updateConnectButton();
|
||||
}
|
||||
|
||||
|
||||
void ControlDialog::slotOnWebSocketSslError()
|
||||
{
|
||||
if (m_webSocketClient)
|
||||
m_webSocketClient->webSocket()->ignoreSslErrors();
|
||||
}
|
||||
|
||||
|
||||
void ControlDialog::slotCbxWorkstationsChanged()
|
||||
{
|
||||
QString txt = m_ui->cbxWorkstations->currentText();
|
||||
if (txt.isEmpty())
|
||||
return;
|
||||
QHostInfo info = QHostInfo::fromName(txt);
|
||||
if (info.addresses().size() > 0)
|
||||
m_host = info.addresses().at(0);
|
||||
else
|
||||
ERROR_LOG(m_logger) << "Address for host " << m_ui->cbxWorkstations->currentText() << " not found!";
|
||||
}
|
||||
|
||||
|
||||
void ControlDialog::fillCombobox()
|
||||
{
|
||||
m_ui->cbxWorkstations->clear();
|
||||
|
||||
m_asmDtc->assets([this](const AssetManagement::Asset & data)
|
||||
{
|
||||
if (m_ui->chbProjectionWSOnly->isChecked() &&
|
||||
data.assetTypeId() != AssetManagement::AssetType_def::ProjectionWorkstation)
|
||||
return;
|
||||
if (m_ui->chbInThisLocationOnly->isChecked() &&
|
||||
data.locationId() != m_locationId)
|
||||
return;
|
||||
|
||||
m_ui->cbxWorkstations->addItem(QString::fromStdString(data.name()));
|
||||
});
|
||||
m_ui->cbxWorkstations->model()->sort(0);
|
||||
|
||||
}
|
||||
|
||||
void ControlDialog::updateConnectButton()
|
||||
{
|
||||
TRACE_LOG(m_logger) << LOGFUNCTION << " START, this=" << this
|
||||
<< ", m_isConnected=" << m_isConnected
|
||||
<< ", pbnConnect->text()=" << m_ui->pbnConnect->text();
|
||||
if (!m_ui)
|
||||
return;
|
||||
m_ui->pbnConnect->setText(m_isConnected ? tr("Disonnect") : tr("Connect"));
|
||||
m_ui->pbnLogout->setEnabled(m_isConnected);
|
||||
TRACE_LOG(m_logger) << LOGFUNCTION << " END, this=" << this
|
||||
<< ", m_isConnected=" << m_isConnected
|
||||
<< ", pbnConnect->text()=" << m_ui->pbnConnect->text();
|
||||
}
|
||||
|
||||
|
||||
void ControlDialog::handleMessage(LtsWebSocket::AbstractClientMessage *message)
|
||||
{
|
||||
using namespace LtsProjectionWorkstation::STC;
|
||||
LoginRequestReply *login = dynamic_cast<LtsProjectionWorkstation::STC::LoginRequestReply *>(message);
|
||||
if (login)
|
||||
{
|
||||
if (login->reason() == LoginRequestReply::LoginSuccessful)
|
||||
{
|
||||
for (const LtsProjectionWorkstation::VncScreen &screen : login->screens())
|
||||
{
|
||||
VncViewerDialog *vnc = new VncViewerDialog(screen.name, this);
|
||||
if (vnc->init(m_host, screen.port, screen.geometry.size()))
|
||||
{
|
||||
m_viewers.append(vnc);
|
||||
connect(vnc, &VncViewerDialog::disconnected, this, &ControlDialog::slotOnVncDisconnected);
|
||||
QString name = screen.name;
|
||||
name.remove(QLatin1Char(' '));
|
||||
addDialog(vnc, m_menuGroup, name,
|
||||
LtsWidgets::LayoutArea::eDialogTab, false, false);
|
||||
}
|
||||
}
|
||||
DEBUG_LOG(m_logger) << LOGFUNCTION << " LoginSuccessful YES, this=" << this;
|
||||
m_isConnected = true;
|
||||
m_ui->pbnConnect->setEnabled(true);
|
||||
updateConnectButton();
|
||||
}
|
||||
else
|
||||
{
|
||||
QString errMsg;
|
||||
switch (login->reason())
|
||||
{
|
||||
case LoginRequestReply::LoginSuccessful:
|
||||
break;
|
||||
case LoginRequestReply::OtherUser:
|
||||
errMsg = tr("The connection is blocked by user %1")
|
||||
.arg(QString::fromStdString(m_opmDtc->user(login->reasonUserId()).name()));
|
||||
break;
|
||||
}
|
||||
|
||||
LtsWidgets::LtsMessageDialog::showMessage(this, errMsg, tr("Login failed!"));
|
||||
DEBUG_LOG(m_logger) << LOGFUNCTION << " LoginSuccessful NO, this=" << this;
|
||||
m_isConnected = false;
|
||||
m_ui->pbnConnect->setEnabled(true);
|
||||
updateConnectButton();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
LtsProjectionWorkstation::STC::DedicatedDisconnect *disconnect = dynamic_cast<LtsProjectionWorkstation::STC::DedicatedDisconnect *>(message);
|
||||
if (disconnect)
|
||||
{
|
||||
INFO_LOG(m_logger) << " DedicatedDisconnect.. "
|
||||
<< ", getOperatorId=" << LtsSessionData::LtsSessionData::getInstance()->getOperatorId()
|
||||
<< ", disconnect->userid()=" << disconnect->userid();
|
||||
if ( (disconnect->userid() != LtsSessionData::LtsSessionData::getInstance()->getOperatorId()) && m_isConnected)
|
||||
{
|
||||
slotOnWebSocketDisconnected();
|
||||
INFO_LOG(m_logger) << " DedicatedDisconnect.. done";
|
||||
|
||||
// NLDEVCTRL-445: Projektion Filter und Kartenausschnitt:
|
||||
// LtsWidgets::LtsMessageDialog::showMessage(this, tr("There is already a user with a higher priority connected. Please wait until the user has ended the session."), tr("Disconnected"));
|
||||
// LtsMessageDialog is blocking ; but we need unblocking:
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("There is already a user with a higher priority connected. Please wait until the user has ended the session."), tr("Disconnected"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ControlDialog::retranslate()
|
||||
{
|
||||
translate(m_ui, tr("Control Projection"));
|
||||
}
|
||||
|
||||
} // ProjectionWorkstation
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "WebSocket/pws_web_socket_client.h"
|
||||
|
||||
#include <QWebSocket>
|
||||
|
||||
#include <LtsWidgets/lts_dialog.h>
|
||||
#include <LtsProjectionWorkstation/logger.h>
|
||||
|
||||
#include <LtsProjectionWorkstation/WebSocket/messages.h>
|
||||
#include <LtsProjectionWorkstation/WebSocket/message_factory.h>
|
||||
|
||||
#include <Plugins/OperatorManagement/opm_settings_gui_plugin_interface.h>
|
||||
#include <Plugins/OperatorManagement/Data/opm_data_plugin.h>
|
||||
#include <Plugins/AssetManagement/Data/asm_data_plugin.h>
|
||||
|
||||
namespace Ui { class ControlDialog; }
|
||||
namespace LtsSession { class LtsSession; }
|
||||
|
||||
namespace ProjectionWorkstation
|
||||
{
|
||||
|
||||
class VncViewerDialog;
|
||||
class ControlDialog : public LtsWidgets::LtsDialog, public LtsProjectionWorkstation::AbstractMessageHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ControlDialog(const QString &menuGroup, QWidget *parent = nullptr);
|
||||
~ControlDialog();
|
||||
|
||||
// AbstractMessageHandler interface
|
||||
void handleMessage(LtsWebSocket::AbstractClientMessage *message) override final;
|
||||
|
||||
// LtsDialog interface
|
||||
void retranslate() override final;
|
||||
|
||||
bool init(LtsSession::LtsSession *session,
|
||||
const std::shared_ptr<OperatorManagement::Interface::Dtc> &opmDtc,
|
||||
const std::shared_ptr<OperatorManagement::ForeignSettingsGuiInterface> &opmIfc,
|
||||
const std::shared_ptr<AssetManagement::Interface::Dtc> &asmDtc, int exerciseId);
|
||||
|
||||
protected:
|
||||
// LtsDialog interface
|
||||
void aboutToClose() override final;
|
||||
void initWebSocketHandler();
|
||||
|
||||
private slots:
|
||||
void slotOnConnectClicked();
|
||||
void slotOnLogoutClicked();
|
||||
|
||||
void slotOnWebSocketConnected();
|
||||
void slotOnVncDisconnected();
|
||||
void slotOnWebSocketDisconnected();
|
||||
void slotOnWebSocketError();
|
||||
void slotOnWebSocketSslError();
|
||||
|
||||
void slotCbxWorkstationsChanged();
|
||||
|
||||
void fillCombobox();
|
||||
private:
|
||||
|
||||
void updateConnectButton();
|
||||
void closeViewers();
|
||||
|
||||
Ui::ControlDialog *m_ui;
|
||||
LtsProjectionWorkstation::LOGGER_T m_logger;
|
||||
LtsProjectionWorkstation::MessageFactory *m_messageFactory;
|
||||
QVector<VncViewerDialog *> m_viewers;
|
||||
WebSocketClient *m_webSocketClient;
|
||||
LtsSession::LtsSession *m_session;
|
||||
QHostAddress m_host;
|
||||
QString m_menuGroup;
|
||||
int m_exerciseId;
|
||||
int m_locationId;
|
||||
bool m_isConnected;
|
||||
|
||||
std::shared_ptr<OperatorManagement::Interface::Dtc> m_opmDtc;
|
||||
std::shared_ptr<OperatorManagement::ForeignSettingsGuiInterface> m_opmIfc;
|
||||
std::shared_ptr<AssetManagement::Interface::Dtc> m_asmDtc;
|
||||
|
||||
};
|
||||
|
||||
} // ProjectionWorkstation
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ControlDialog</class>
|
||||
<widget class="QWidget" name="ControlDialog">
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Control Connection</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="horizontalSpacing">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<item row="0" column="0" rowspan="2">
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<property name="spacing">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<item row="1" column="0" colspan="3">
|
||||
<widget class="QComboBox" name="cbxWorkstations">
|
||||
<property name="currentText">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" rowspan="2" colspan="4">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbnConnect">
|
||||
<property name="text">
|
||||
<string>Connect</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbnLogout">
|
||||
<property name="text">
|
||||
<string>Logout</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Available Workstations:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="4">
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QCheckBox" name="chbInThisLocationOnly">
|
||||
<property name="text">
|
||||
<string>Show only in this location</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" rowspan="2" colspan="3">
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Preferred</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="3" rowspan="2">
|
||||
<widget class="QCheckBox" name="chbProjectionWSOnly">
|
||||
<property name="text">
|
||||
<string>Show Projection-Workstations only</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="autoExclusive">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>46</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "pws_gui_plugin.h"
|
||||
#include "pws_control_dialog.h"
|
||||
#include "pws_operator_settings_loader.h"
|
||||
|
||||
#include <LtsSession/lts_session.h>
|
||||
#include <LtsUtilsSettings/settings.h>
|
||||
#include <LtsProjectionWorkstation/lts_projection_workstation.h>
|
||||
#include <Services/ProjectionWorkstation/pws_operator_rights.h>
|
||||
|
||||
namespace ProjectionWorkstation
|
||||
{
|
||||
|
||||
std::shared_ptr<GuiPluginBase::Interface> GuiPlugin::createGuiInterface(LtsSession::LtsSession *session)
|
||||
{
|
||||
return std::make_shared<InterfaceImpl>(session);
|
||||
}
|
||||
|
||||
|
||||
InterfaceImpl::InterfaceImpl(LtsSession::LtsSession *session)
|
||||
: GuiPluginBase::Interface(session, LtsProjectionWorkstation::getLogger("Plugins.Gui.PWS"))
|
||||
, m_isProjectionWorkstation(LtsProjectionWorkstation::LtsProjectionWorkstation::hasInstance())
|
||||
{}
|
||||
|
||||
|
||||
QVector<LtsWidgets::PluginData> InterfaceImpl::getActions()
|
||||
{
|
||||
QVector<LtsWidgets::PluginData> ret;
|
||||
if (m_session->isReplayPartition())
|
||||
return ret;
|
||||
|
||||
LtsUtils::ProjectSettings *ps = LtsUtils::ProjectSettings::getInstance();
|
||||
const bool enableProjection = ps->value(QStringLiteral("pws"),
|
||||
QStringLiteral("enableProjection")).toBool();
|
||||
|
||||
if (!m_isProjectionWorkstation && enableProjection)
|
||||
{
|
||||
LtsWidgets::PluginData plData;
|
||||
plData.menuGroup = menuGroup();
|
||||
plData.menuText = tr("Projection Control");
|
||||
plData.identifier = QLatin1String("pwsControl");
|
||||
plData.menuIcon = QIcon(QLatin1String("icon:projection.svg"));
|
||||
plData.operatorRight = OperatorRights::ProjectionStart;
|
||||
plData.exercisePart = LtsWidgets::ExercisePart::eSpecial;
|
||||
plData.createWidgetFunc = [this](QWidget * parent)
|
||||
{
|
||||
return LtsWidgets::initDialog(new ControlDialog(menuGroup(), parent),
|
||||
m_session, m_opmDtc, m_opmIfc, m_asmDtc, m_session->exerciseId());
|
||||
};
|
||||
ret.append(plData);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool InterfaceImpl::fetchInterfaces()
|
||||
{
|
||||
if (m_isProjectionWorkstation)
|
||||
{
|
||||
LtsProjectionWorkstation::LoginInformation request = LtsProjectionWorkstation::LtsProjectionWorkstation::getInstance()->lastLoginRequest();
|
||||
OperatorSettingsLoader loader(request.m_userId);
|
||||
if (!loader.init(m_session))
|
||||
return false;
|
||||
loader.execute();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_session->getDtc<OperatorManagement::DataInterface>(m_opmDtc))
|
||||
return false;
|
||||
if (!m_session->getForeignGuiInterface<OperatorManagement::ForeignSettingsGuiInterface>(m_opmIfc))
|
||||
return false;
|
||||
if (!m_session->getDtc<AssetManagement::DataInterface>(m_asmDtc))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void InterfaceImpl::clearInterfaces()
|
||||
{
|
||||
m_opmDtc = nullptr;
|
||||
m_opmIfc = nullptr;
|
||||
m_asmDtc = nullptr;
|
||||
}
|
||||
|
||||
} // namespace ProjectionWorkstation
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <PluginInterfaces/gui_plugin_interface.h>
|
||||
#include <LtsProjectionWorkstation/logger.h>
|
||||
#include <Plugins/OperatorManagement/Data/opm_data_plugin.h>
|
||||
#include <Plugins/OperatorManagement/opm_settings_gui_plugin_interface.h>
|
||||
#include <Plugins/AssetManagement/Data/asm_data_plugin.h>
|
||||
#include <memory>
|
||||
|
||||
namespace ProjectionWorkstation
|
||||
{
|
||||
|
||||
class GuiPlugin final : public QObject, public GuiPluginBase::Plugin
|
||||
{
|
||||
Q_PLUGIN_METADATA(IID "gui:3:pws")
|
||||
Q_INTERFACES(GuiPluginBase::Plugin)
|
||||
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GuiPlugin() = default;
|
||||
~GuiPlugin() = default;
|
||||
|
||||
QString pluginName() override { return QLatin1String("ProjectionWorkstationGuiPlugin"); }
|
||||
std::shared_ptr<GuiPluginBase::Interface> createGuiInterface(LtsSession::LtsSession *session) override;
|
||||
bool availableInPartition(IDL::PartitionType partition) const override
|
||||
{
|
||||
return partition == IDL::PartitionType::Exercise ||
|
||||
partition == IDL::PartitionType::Replay;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class InterfaceImpl final : public QObject, public GuiPluginBase::Interface
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit InterfaceImpl(LtsSession::LtsSession *session);
|
||||
|
||||
QVector<LtsWidgets::PluginData> getActions() override;
|
||||
QString menuGroup() const override { return tr("Projection"); }
|
||||
bool fetchInterfaces() override;
|
||||
void clearInterfaces() override;
|
||||
|
||||
private:
|
||||
bool m_isProjectionWorkstation;
|
||||
|
||||
std::shared_ptr<OperatorManagement::Interface::Dtc> m_opmDtc;
|
||||
std::shared_ptr<OperatorManagement::ForeignSettingsGuiInterface> m_opmIfc;
|
||||
std::shared_ptr<AssetManagement::Interface::Dtc> m_asmDtc;
|
||||
|
||||
};
|
||||
|
||||
} // namespace ProjectionWorkstation
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "pws_operator_settings_loader.h"
|
||||
|
||||
#include <LtsUtilsGuiDds/request_helper.h>
|
||||
#include <LtsSession/lts_session_data.h>
|
||||
#include <LtsSession/lts_session.h>
|
||||
|
||||
#include <PluginInterfaces/gui_plugin_interface.h>
|
||||
|
||||
|
||||
namespace ProjectionWorkstation
|
||||
{
|
||||
|
||||
|
||||
OperatorSettingsLoader::OperatorSettingsLoader(int userId)
|
||||
: m_logger(LtsProjectionWorkstation::getLogger(QStringLiteral("OperatorSettingsLoader")))
|
||||
, m_opmFtc(nullptr)
|
||||
, m_requestHelper(new LtsUtils::RequestHelper())
|
||||
, m_userId(userId)
|
||||
{
|
||||
}
|
||||
|
||||
OperatorSettingsLoader::~OperatorSettingsLoader()
|
||||
{
|
||||
delete m_requestHelper;
|
||||
}
|
||||
|
||||
|
||||
bool OperatorSettingsLoader::init(LtsSession::LtsSession *session)
|
||||
{
|
||||
if (!session->getFtc<OperatorManagement::DataInterface>(m_opmFtc))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Failed to load OperatorManagement FTC!";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool OperatorSettingsLoader::loadOperatorRights()
|
||||
{
|
||||
LtsSessionData::LtsSessionData *sessionData = LtsSessionData::LtsSessionData::getInstance();
|
||||
|
||||
int localUserId = sessionData ? sessionData->getOperatorId() : 0;
|
||||
|
||||
if (m_userId != 0 && localUserId != 0)
|
||||
{
|
||||
OperatorManagement::OperatorApplyForeignAclsRequest req;
|
||||
|
||||
req.id(localUserId);
|
||||
req.remoteOperatorId(m_userId);
|
||||
|
||||
auto cb = [this, &req]() -> bool
|
||||
{
|
||||
return m_opmFtc->operatorApplyForeignAclsRequest(req, this);
|
||||
};
|
||||
|
||||
m_requestHelper->addRequest(cb);
|
||||
bool ret = m_requestHelper->startRequests();
|
||||
|
||||
return ret;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void OperatorSettingsLoader::onOperatorApplyForeignAclsReply(const Core::FunctionReplyObject &reply)
|
||||
{
|
||||
m_requestHelper->requestReply(reply);
|
||||
}
|
||||
|
||||
|
||||
void OperatorSettingsLoader::execute()
|
||||
{
|
||||
loadOperatorRights();
|
||||
}
|
||||
|
||||
|
||||
} // ProjectionWorkstation
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <OperatorManagement/opm_data_plugin_interface.h>
|
||||
|
||||
#include <LtsProjectionWorkstation/logger.h>
|
||||
|
||||
namespace LtsSession { class LtsSession; }
|
||||
namespace LtsUtils { class RequestHelper; }
|
||||
|
||||
namespace ProjectionWorkstation
|
||||
{
|
||||
|
||||
class OperatorSettingsLoader
|
||||
: public OperatorManagement::Interface::Ftc::OperatorApplyForeignAclsReplyHandler
|
||||
{
|
||||
public:
|
||||
explicit OperatorSettingsLoader(int userId);
|
||||
~OperatorSettingsLoader();
|
||||
|
||||
bool init(LtsSession::LtsSession *session);
|
||||
|
||||
// OperatorCreateModifyReplyHandler interface
|
||||
void onOperatorApplyForeignAclsReply(const Core::FunctionReplyObject &reply) override final;
|
||||
|
||||
void execute();
|
||||
|
||||
private:
|
||||
|
||||
bool loadOperatorRights();
|
||||
|
||||
LtsProjectionWorkstation::LOGGER_T m_logger;
|
||||
|
||||
std::shared_ptr<OperatorManagement::Interface::Ftc> m_opmFtc;
|
||||
|
||||
LtsUtils::RequestHelper *m_requestHelper;
|
||||
|
||||
int m_userId;
|
||||
|
||||
};
|
||||
|
||||
} // ProjectionWorkstation
|
||||
@@ -0,0 +1,398 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#include "pws_vnc_viewer_dialog.h"
|
||||
#include "ui_pws_vnc_viewer_dialog.h"
|
||||
|
||||
#include <LtsProjectionWorkstation/logger.h>
|
||||
|
||||
#include <QMutexLocker>
|
||||
#include <QPainter>
|
||||
#include <QOpenGLPaintDevice>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
|
||||
namespace ProjectionWorkstation
|
||||
{
|
||||
|
||||
// Dialog
|
||||
|
||||
VncViewerDialog::VncViewerDialog(const QString &name, QWidget *parent)
|
||||
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::SeamlessDialog, false)
|
||||
, m_ui(new Ui::VncViewerDialog)
|
||||
, m_handler(nullptr)
|
||||
, m_renderArea(nullptr)
|
||||
, m_mouseButtonMask(0)
|
||||
, m_mouseScrollReleaseTimer(new QTimer(this))
|
||||
, m_name(name)
|
||||
, m_isConnected(false)
|
||||
, m_logger(LtsProjectionWorkstation::getLogger(QStringLiteral("VncViewerDialog")))
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
}
|
||||
|
||||
VncViewerDialog::~VncViewerDialog()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
bool VncViewerDialog::init(const QHostAddress &host, quint16 port, const QSize &remoteResolution)
|
||||
{
|
||||
m_handler = new VncHandler(host, port, this);
|
||||
m_renderArea = new VncRenderArea(remoteResolution, this);
|
||||
m_remoteResolution = remoteResolution;
|
||||
m_ui->gridLayout->addWidget(m_renderArea);
|
||||
|
||||
connect(m_handler, &VncHandler::frameBufferUpdateReceived, m_renderArea, &VncRenderArea::frameBufferUpdateReceived);
|
||||
connect(m_handler, &VncHandler::cutTextReceived, this, &VncViewerDialog::handleServerCuttext);
|
||||
connect(m_handler, &VncHandler::beepReceived, this, &QApplication::beep);
|
||||
connect(m_handler, &VncHandler::connected, this, &VncViewerDialog::show);
|
||||
connect(m_handler, &VncHandler::disconnected, this, &VncViewerDialog::slotOnDisconnected);
|
||||
connect(m_mouseScrollReleaseTimer, &QTimer::timeout, this, &VncViewerDialog::onScrollRelease);
|
||||
setMouseTracking(true);
|
||||
|
||||
m_mouseScrollReleaseTimer->setSingleShot(true);
|
||||
m_mouseScrollReleaseTimer->setInterval(5);
|
||||
|
||||
m_handler->connectToHost();
|
||||
|
||||
hide();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VncViewerDialog::retranslate()
|
||||
{
|
||||
translate(m_ui, m_name);
|
||||
}
|
||||
|
||||
void VncViewerDialog::handleServerCuttext(const QString &cutText)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Got cuttext: " << cutText;
|
||||
}
|
||||
|
||||
void VncViewerDialog::slotOnDisconnected()
|
||||
{
|
||||
// closeRequest();
|
||||
// m_renderArea->clear();
|
||||
emit disconnected();
|
||||
}
|
||||
|
||||
void VncViewerDialog::focusInEvent(QFocusEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
grabKeyboard();
|
||||
// setCursor(Qt::BlankCursor);
|
||||
if (m_renderArea)
|
||||
{
|
||||
QByteArray data = RfbProtocol::CTS::RfbFrameBufferUpdateRequest(false, 0, 0, m_renderArea->frameSize().width(), m_renderArea->frameSize().height());
|
||||
m_handler->writeData(data);
|
||||
}
|
||||
}
|
||||
|
||||
void VncViewerDialog::focusOutEvent(QFocusEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
releaseKeyboard();
|
||||
// unsetCursor();
|
||||
}
|
||||
|
||||
void VncViewerDialog::onScrollRelease()
|
||||
{
|
||||
m_mouseButtonMask &= ~(3 << 3);
|
||||
|
||||
QByteArray data = RfbProtocol::CTS::RfbPointerEvent(m_mouseButtonMask, m_lastScrollPos.x(), m_lastScrollPos.y());
|
||||
m_handler->writeData(data);
|
||||
}
|
||||
|
||||
void VncViewerDialog::onSlotConnected()
|
||||
{
|
||||
m_isConnected = true;
|
||||
}
|
||||
|
||||
void VncViewerDialog::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
if (event->button() == Qt::LeftButton)
|
||||
m_mouseButtonMask |= (1 << 0);
|
||||
else if (event->button() == Qt::MidButton)
|
||||
m_mouseButtonMask |= (1 << 1);
|
||||
else if (event->button() == Qt::RightButton)
|
||||
m_mouseButtonMask |= (1 << 2);
|
||||
|
||||
QPoint mapped = mapMousePosition(event->pos());
|
||||
|
||||
QByteArray data = RfbProtocol::CTS::RfbPointerEvent(m_mouseButtonMask, mapped.x(), mapped.y());
|
||||
m_handler->writeData(data);
|
||||
}
|
||||
|
||||
void VncViewerDialog::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
if (event->button() == Qt::LeftButton)
|
||||
m_mouseButtonMask &= ~(1 << 0);
|
||||
else if (event->button() == Qt::MidButton)
|
||||
m_mouseButtonMask &= ~(1 << 1);
|
||||
else if (event->button() == Qt::RightButton)
|
||||
m_mouseButtonMask &= ~(1 << 2);
|
||||
|
||||
QPoint mapped = mapMousePosition(event->pos());
|
||||
|
||||
QByteArray data = RfbProtocol::CTS::RfbPointerEvent(m_mouseButtonMask, mapped.x(), mapped.y());
|
||||
m_handler->writeData(data);
|
||||
}
|
||||
|
||||
void VncViewerDialog::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
QPoint mapped = mapMousePosition(event->pos());
|
||||
QByteArray data = RfbProtocol::CTS::RfbPointerEvent(m_mouseButtonMask, mapped.x(), mapped.y());
|
||||
m_handler->writeData(data);
|
||||
}
|
||||
|
||||
void VncViewerDialog::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
QByteArray data = RfbProtocol::CTS::RfbKeyEvent(event->nativeVirtualKey(), true);
|
||||
m_handler->writeData(data);
|
||||
}
|
||||
|
||||
void VncViewerDialog::keyReleaseEvent(QKeyEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
QByteArray data = RfbProtocol::CTS::RfbKeyEvent(event->nativeVirtualKey(), false);
|
||||
m_handler->writeData(data);
|
||||
}
|
||||
|
||||
void VncViewerDialog::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
if (!m_mouseScrollReleaseTimer->isActive())
|
||||
{
|
||||
if (event->angleDelta().y() > 0)
|
||||
m_mouseButtonMask |= (1 << 3);
|
||||
else if (event->angleDelta().y() < 0)
|
||||
m_mouseButtonMask |= (1 << 4);
|
||||
|
||||
QPoint mapped = mapMousePosition(event->pos());
|
||||
|
||||
QByteArray data = RfbProtocol::CTS::RfbPointerEvent(m_mouseButtonMask, mapped.x(), mapped.y());
|
||||
m_handler->writeData(data);
|
||||
m_lastScrollPos = mapped;
|
||||
m_mouseScrollReleaseTimer->start();
|
||||
}
|
||||
}
|
||||
|
||||
void VncViewerDialog::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
if (m_isConnected && m_renderArea)
|
||||
{
|
||||
emit m_renderArea->resized();
|
||||
QByteArray data = RfbProtocol::CTS::RfbFrameBufferUpdateRequest(false, 0, 0, m_renderArea->frameSize().width(), m_renderArea->frameSize().height());
|
||||
m_handler->writeData(data);
|
||||
}
|
||||
}
|
||||
|
||||
void VncViewerDialog::showEvent(QShowEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
if (m_isConnected && m_renderArea)
|
||||
{
|
||||
QByteArray data = RfbProtocol::CTS::RfbFrameBufferUpdateRequest(false, 0, 0, m_renderArea->frameSize().width(), m_renderArea->frameSize().height());
|
||||
m_handler->writeData(data);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int mapValue(double val, double maxIn, double maxOut)
|
||||
{
|
||||
val = qBound(0., val, maxIn);
|
||||
return qRound(val * (maxOut / maxIn));
|
||||
}
|
||||
|
||||
QPoint VncViewerDialog::mapMousePosition(const QPoint &point) const
|
||||
{
|
||||
QPoint clienPoint = m_renderArea->mapFromParent(point);
|
||||
int x = mapValue(clienPoint.x(), m_renderArea->scaledSize().width(), m_renderArea->frameSize().width());
|
||||
int y = mapValue(clienPoint.y(), m_renderArea->scaledSize().height(), m_renderArea->frameSize().height());
|
||||
return QPoint(x, y);
|
||||
}
|
||||
|
||||
|
||||
// Render Area Worker
|
||||
VncRenderAreaWorker::VncRenderAreaWorker()
|
||||
: m_logger(LtsProjectionWorkstation::getLogger(QStringLiteral("VncRenderArea")))
|
||||
{
|
||||
qRegisterMetaType<QVector<RfbProtocol::RfbRectangle>>("QVector<RfbProtocol::RfbRectangle>");
|
||||
}
|
||||
|
||||
void VncRenderAreaWorker::onRfbFrameBufferUpdate(const QVector<RfbProtocol::RfbRectangle> &rectangles)
|
||||
{
|
||||
if (rectangles.isEmpty())
|
||||
return;
|
||||
|
||||
QPainter painter(&m_renderedImage);
|
||||
// no dequeue here otherwise rectangles() is dangling
|
||||
for (const RfbProtocol::RfbRectangle &rect : rectangles)
|
||||
{
|
||||
if (rect.pixelData.isEmpty())
|
||||
{
|
||||
WARNING_LOG(m_logger) << "No valid vnc pixel data... skipping frame";
|
||||
continue;
|
||||
}
|
||||
QImage img(reinterpret_cast<const unsigned char *>(rect.pixelData.data()),
|
||||
rect.width,
|
||||
rect.height,
|
||||
QImage::Format_RGB32);
|
||||
if (img.isNull())
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Could not decode image data... skipping vnc frame";
|
||||
continue;
|
||||
}
|
||||
painter.drawImage(QPoint(rect.xpos, rect.ypos), img);
|
||||
}
|
||||
painter.end();
|
||||
|
||||
QImage img = m_renderedImage.scaled(m_scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
if (img.size() != m_scaledSizeAspectRatio)
|
||||
{
|
||||
m_scaledSizeAspectRatio = img.size();
|
||||
emit newScaleSizeAspectRatio(m_scaledSizeAspectRatio);
|
||||
}
|
||||
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
std::swap(img, m_scaledImage);
|
||||
}
|
||||
|
||||
emit newImageAvailable();
|
||||
}
|
||||
|
||||
void VncRenderAreaWorker::setImageSize(QSize s)
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
m_renderedImage = QImage(s, QImage::Format_RGB32);
|
||||
}
|
||||
|
||||
void VncRenderAreaWorker::setScaledSize(QSize s)
|
||||
{
|
||||
if (m_scaledSize == s)
|
||||
return;
|
||||
|
||||
QMutexLocker l(&m_mutex);
|
||||
m_scaledSize = s;
|
||||
if (!m_renderedImage.isNull())
|
||||
{
|
||||
m_scaledImage = m_renderedImage.scaled(m_scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
m_scaledSizeAspectRatio = m_scaledImage.size();
|
||||
emit newScaleSizeAspectRatio(m_scaledSizeAspectRatio);
|
||||
}
|
||||
}
|
||||
|
||||
QImage VncRenderAreaWorker::renderedImage() const
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
return m_scaledImage;
|
||||
}
|
||||
|
||||
|
||||
//Render Area
|
||||
|
||||
VncRenderArea::VncRenderArea(const QSize remoteResolution, QWidget *parent)
|
||||
: QOpenGLWidget(parent)
|
||||
, m_imageRenderThread(new QThread(this))
|
||||
, m_renderAreaWorker(new VncRenderAreaWorker)
|
||||
, m_frameSize(remoteResolution)
|
||||
, m_fbo(nullptr)
|
||||
, m_logger(LtsProjectionWorkstation::getLogger(QStringLiteral("VncRenderArea")))
|
||||
{
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
connect(this, &VncRenderArea::frameBufferUpdateReceived,
|
||||
m_renderAreaWorker, &VncRenderAreaWorker::onRfbFrameBufferUpdate);
|
||||
connect(m_renderAreaWorker, &VncRenderAreaWorker::newImageAvailable,
|
||||
this, &VncRenderArea::prepareRender);
|
||||
connect(m_renderAreaWorker, &VncRenderAreaWorker::newScaleSizeAspectRatio,
|
||||
this, [this](QSize s) { m_scaledSizeAspectRatio = s; });
|
||||
m_renderAreaWorker->setImageSize(remoteResolution);
|
||||
m_renderAreaWorker->moveToThread(m_imageRenderThread);
|
||||
m_imageRenderThread->start();
|
||||
}
|
||||
|
||||
VncRenderArea::~VncRenderArea()
|
||||
{
|
||||
delete m_fbo;
|
||||
|
||||
m_imageRenderThread->quit();
|
||||
m_imageRenderThread->wait(2000);
|
||||
delete m_imageRenderThread;
|
||||
delete m_renderAreaWorker;
|
||||
}
|
||||
|
||||
void VncRenderArea::clear()
|
||||
{
|
||||
if (context())
|
||||
{
|
||||
glClearColor(1, 1, 1, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void VncRenderArea::initializeGL()
|
||||
{
|
||||
initializeOpenGLFunctions();
|
||||
m_blitter.create();
|
||||
}
|
||||
|
||||
void VncRenderArea::paintGL()
|
||||
{
|
||||
if (context() && m_fbo)
|
||||
{
|
||||
m_blitter.bind();
|
||||
const QRect targetRect(QPoint(0, 0), m_fbo->size());
|
||||
const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(targetRect, targetRect); // no scaling here, is not anti-aliased
|
||||
m_blitter.blit(m_fbo->texture(), target, QOpenGLTextureBlitter::OriginBottomLeft);
|
||||
m_blitter.release();
|
||||
}
|
||||
}
|
||||
|
||||
void VncRenderArea::maybeResized()
|
||||
{
|
||||
rebuildFbo(size());
|
||||
m_renderAreaWorker->setScaledSize(size());
|
||||
}
|
||||
|
||||
void VncRenderArea::prepareRender()
|
||||
{
|
||||
if (context())
|
||||
{
|
||||
if (!m_fbo || m_fbo->size() != size())
|
||||
maybeResized();
|
||||
m_fbo->bind();
|
||||
QOpenGLPaintDevice device(size());
|
||||
QPainter painter(&device);
|
||||
painter.drawImage(QPoint(0, 0), m_renderAreaWorker->renderedImage());
|
||||
painter.end();
|
||||
m_fbo->release();
|
||||
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void VncRenderArea::rebuildFbo(QSize newSize)
|
||||
{
|
||||
delete m_fbo;
|
||||
QOpenGLFramebufferObjectFormat format;
|
||||
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
|
||||
m_fbo = new QOpenGLFramebufferObject(newSize, format);
|
||||
}
|
||||
|
||||
} // ProjectionWorkstation
|
||||
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <LtsWidgets/lts_dialog.h>
|
||||
#include <Plugins/ProjectionWorkstation/Common/pws_rfb_protocol.h>
|
||||
#include <Plugins/ProjectionWorkstation/Common/pws_vnc_handler.h>
|
||||
|
||||
#include <QMutex>
|
||||
#include <QOpenGLFramebufferObject>
|
||||
#include <QOpenGLTextureBlitter>
|
||||
#include <QOpenGLWidget>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QHostAddress>
|
||||
|
||||
namespace Ui { class VncViewerDialog; }
|
||||
|
||||
namespace ProjectionWorkstation
|
||||
{
|
||||
|
||||
class VncRenderAreaWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VncRenderAreaWorker();
|
||||
|
||||
void setImageSize(QSize s);
|
||||
void setScaledSize(QSize s);
|
||||
QImage renderedImage() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void onRfbFrameBufferUpdate(const QVector<RfbProtocol::RfbRectangle> &rectangles);
|
||||
Q_SIGNALS:
|
||||
void newImageAvailable();
|
||||
void newScaleSizeAspectRatio(QSize s);
|
||||
private:
|
||||
QImage m_renderedImage; // store pixeldata
|
||||
QImage m_scaledImage;
|
||||
mutable QMutex m_mutex;
|
||||
QSize m_scaledSize;
|
||||
QSize m_scaledSizeAspectRatio;
|
||||
LtsProjectionWorkstation::LOGGER_T m_logger;
|
||||
};
|
||||
|
||||
class VncRenderArea : public QOpenGLWidget, protected QOpenGLFunctions
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VncRenderArea(const QSize remoteResolution, QWidget *parent);
|
||||
~VncRenderArea();
|
||||
// QOpenGLWidget interface
|
||||
|
||||
void clear();
|
||||
|
||||
QSize frameSize() const { return m_frameSize; }
|
||||
QSize scaledSize() const { return m_scaledSizeAspectRatio; }
|
||||
void maybeResized();
|
||||
|
||||
protected:
|
||||
void initializeGL() override final;
|
||||
void paintGL() override final;
|
||||
|
||||
Q_SIGNALS:
|
||||
void frameBufferUpdateReceived(const QVector<RfbProtocol::RfbRectangle> &rectangles);
|
||||
public slots:
|
||||
void prepareRender();
|
||||
private:
|
||||
void rebuildFbo(QSize newSize);
|
||||
private:
|
||||
QThread *m_imageRenderThread;
|
||||
VncRenderAreaWorker *m_renderAreaWorker;
|
||||
QSize m_scaledSizeAspectRatio;
|
||||
QSize m_frameSize;
|
||||
QOpenGLFramebufferObject *m_fbo;
|
||||
QOpenGLTextureBlitter m_blitter;
|
||||
|
||||
LtsProjectionWorkstation::LOGGER_T m_logger;
|
||||
}; // VncRenderArea
|
||||
|
||||
|
||||
class VncViewerDialog : public LtsWidgets::LtsDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VncViewerDialog(const QString &name, QWidget *parent = nullptr);
|
||||
~VncViewerDialog();
|
||||
|
||||
bool init(const QHostAddress &host, quint16 port, const QSize &remoteResolution);
|
||||
|
||||
// LtsDialog interface
|
||||
void retranslate() override final;
|
||||
|
||||
signals:
|
||||
void disconnected();
|
||||
|
||||
private slots:
|
||||
void handleServerCuttext(const QString &cutText);
|
||||
void slotOnDisconnected();
|
||||
void onScrollRelease();
|
||||
void onSlotConnected();
|
||||
|
||||
protected:
|
||||
// QWidget interface
|
||||
void focusInEvent(QFocusEvent *event) override final;
|
||||
void focusOutEvent(QFocusEvent *event) override final;
|
||||
void mousePressEvent(QMouseEvent *event) override final;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override final;
|
||||
void mouseMoveEvent(QMouseEvent *event) override final;
|
||||
void keyPressEvent(QKeyEvent *event) override final;
|
||||
void keyReleaseEvent(QKeyEvent *event) override final;
|
||||
void wheelEvent(QWheelEvent *event) override final;
|
||||
void resizeEvent(QResizeEvent *event) override final;
|
||||
void showEvent(QShowEvent *event) override final;
|
||||
|
||||
private:
|
||||
QPoint mapMousePosition(const QPoint &point) const;
|
||||
|
||||
Ui::VncViewerDialog *m_ui;
|
||||
|
||||
VncHandler *m_handler;
|
||||
VncRenderArea *m_renderArea;
|
||||
|
||||
quint8 m_mouseButtonMask;
|
||||
QTimer *m_mouseScrollReleaseTimer;
|
||||
QPoint m_lastScrollPos;
|
||||
QSize m_remoteResolution;
|
||||
QString m_name;
|
||||
bool m_isConnected;
|
||||
|
||||
LtsProjectionWorkstation::LOGGER_T m_logger;
|
||||
}; // VncViewerDialog
|
||||
|
||||
} // ProjectionWorkstation
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>VncViewerDialog</class>
|
||||
<widget class="QWidget" name="VncViewerDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace ProjectionWorkstation
|
||||
{
|
||||
class ForeignGuiInterface
|
||||
{
|
||||
public:
|
||||
ForeignGuiInterface() = default;
|
||||
virtual ~ForeignGuiInterface() = default;
|
||||
};
|
||||
|
||||
} // namespace ProjectionWorkstation
|
||||
Reference in new issue
Block a user