mirror of
https://github.com/kasmtech/KasmVNC.git
synced 2024-11-25 01:24:04 +01:00
Merge branch 'feature/KASM-2117_watermark' into 'master'
Resolve KASM-2117 "Feature/ watermark" Closes KASM-2117 See merge request kasm-technologies/internal/KasmVNC!91
This commit is contained in:
commit
059bec7ddf
@ -148,6 +148,9 @@ endif()
|
||||
# Check for zlib
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
# Check for libpng
|
||||
find_package(PNG REQUIRED)
|
||||
|
||||
# Check for libjpeg
|
||||
find_package(JPEG REQUIRED)
|
||||
|
||||
|
@ -24,6 +24,8 @@ RUN zypper install -ny \
|
||||
libgnutls-devel \
|
||||
libopenssl-devel \
|
||||
libpng16-devel \
|
||||
libpnglite0 \
|
||||
png++-devel \
|
||||
libtiff-devel \
|
||||
libXfont2-devel \
|
||||
libxkbcommon-x11-devel \
|
||||
|
@ -1,4 +1,4 @@
|
||||
include_directories(${CMAKE_SOURCE_DIR}/common ${JPEG_INCLUDE_DIR}
|
||||
include_directories(${CMAKE_SOURCE_DIR}/common ${JPEG_INCLUDE_DIR} ${PNG_INCLUDE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/unix/kasmvncpasswd)
|
||||
|
||||
set(RFB_SOURCES
|
||||
@ -65,6 +65,7 @@ set(RFB_SOURCES
|
||||
VNCServerST.cxx
|
||||
ZRLEEncoder.cxx
|
||||
ZRLEDecoder.cxx
|
||||
Watermark.cxx
|
||||
cpuid.cxx
|
||||
encodings.cxx
|
||||
util.cxx
|
||||
@ -79,7 +80,7 @@ if(WIN32)
|
||||
set(RFB_SOURCES ${RFB_SOURCES} WinPasswdValidator.cxx)
|
||||
endif(WIN32)
|
||||
|
||||
set(RFB_LIBRARIES ${JPEG_LIBRARIES} os rdr Xregion)
|
||||
set(RFB_LIBRARIES ${JPEG_LIBRARIES} ${PNG_LIBRARIES} os rdr Xregion)
|
||||
|
||||
if(HAVE_PAM)
|
||||
set(RFB_SOURCES ${RFB_SOURCES} UnixPasswordValidator.cxx
|
||||
|
@ -285,6 +285,8 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
|
||||
// QOI-specific overrides
|
||||
if (supportsQOI)
|
||||
useCopyRect = false;
|
||||
if (Server::DLP_WatermarkImage[0])
|
||||
useCopyRect = false;
|
||||
}
|
||||
|
||||
void ConnParams::setLEDState(unsigned int state)
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <rfb/UpdateTracker.h>
|
||||
#include <rfb/LogWriter.h>
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/Watermark.h>
|
||||
|
||||
#include <rfb/RawEncoder.h>
|
||||
#include <rfb/RREEncoder.h>
|
||||
@ -162,6 +163,7 @@ static void updateMaxVideoRes(uint16_t *x, uint16_t *y) {
|
||||
EncodeManager::EncodeManager(SConnection* conn_, EncCache *encCache_) : conn(conn_),
|
||||
dynamicQualityMin(-1), dynamicQualityOff(-1),
|
||||
areaCur(0), videoDetected(false), videoTimer(this),
|
||||
watermarkStats(0),
|
||||
maxEncodingTime(0), framesSinceEncPrint(0),
|
||||
encCache(encCache_)
|
||||
{
|
||||
@ -299,6 +301,11 @@ void EncodeManager::logStats()
|
||||
vlog.info(" Total: %s, %s", a, b);
|
||||
iecPrefix(bytes, "B", a, sizeof(a));
|
||||
vlog.info(" %s (1:%g ratio)", a, ratio);
|
||||
|
||||
if (watermarkData) {
|
||||
siPrefix(watermarkStats, "B", a, sizeof(a));
|
||||
vlog.info(" Watermark data sent: %s", a);
|
||||
}
|
||||
}
|
||||
|
||||
bool EncodeManager::supported(int encoding)
|
||||
@ -408,8 +415,14 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
|
||||
nRects += copypassed.size();
|
||||
nRects += computeNumRects(changed);
|
||||
nRects += computeNumRects(cursorRegion);
|
||||
|
||||
if (watermarkData)
|
||||
nRects++;
|
||||
}
|
||||
|
||||
if (watermarkData)
|
||||
packWatermark(changed);
|
||||
|
||||
conn->writer()->writeFramebufferUpdateStart(nRects);
|
||||
|
||||
writeCopyRects(copied, copyDelta);
|
||||
@ -427,6 +440,23 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
|
||||
if (!videoDetected) // In case detection happened between the calls
|
||||
writeRects(cursorRegion, renderedCursor);
|
||||
|
||||
if (watermarkData) {
|
||||
beforeLength = conn->getOutStream(conn->cp.supportsUdp)->length();
|
||||
|
||||
const Rect rect(0, 0, pb->width(), pb->height());
|
||||
TightEncoder *encoder = ((TightEncoder *) encoders[encoderTight]);
|
||||
|
||||
conn->writer()->startRect(rect, encoder->encoding);
|
||||
encoder->writeWatermarkRect(watermarkData, watermarkDataLen,
|
||||
watermarkInfo.r,
|
||||
watermarkInfo.g,
|
||||
watermarkInfo.b,
|
||||
watermarkInfo.a);
|
||||
conn->writer()->endRect();
|
||||
|
||||
watermarkStats += conn->getOutStream(conn->cp.supportsUdp)->length() - beforeLength;
|
||||
}
|
||||
|
||||
updateQualities();
|
||||
|
||||
conn->writer()->writeFramebufferUpdateEnd();
|
||||
|
@ -193,6 +193,7 @@ namespace rfb {
|
||||
unsigned updates;
|
||||
EncoderStats copyStats;
|
||||
StatsVector stats;
|
||||
unsigned long long watermarkStats;
|
||||
int activeType;
|
||||
int beforeLength;
|
||||
size_t curMaxUpdateSize;
|
||||
|
@ -239,3 +239,8 @@ void rfb::Region::debug_print(const char* prefix) const
|
||||
xrgn->rects[i].y2-xrgn->rects[i].y1);
|
||||
}
|
||||
}
|
||||
|
||||
bool rfb::Region::contains(int x, int y) const
|
||||
{
|
||||
return XPointInRegion(xrgn, x, y);
|
||||
}
|
||||
|
@ -73,6 +73,8 @@ namespace rfb {
|
||||
|
||||
void debug_print(const char *prefix) const;
|
||||
|
||||
bool contains(int x, int y) const;
|
||||
|
||||
protected:
|
||||
|
||||
struct _XRegion* xrgn;
|
||||
|
@ -185,6 +185,23 @@ rfb::BoolParameter rfb::Server::DLP_RegionAllowRelease
|
||||
"Allow click releases inside the blacked-out region",
|
||||
true);
|
||||
|
||||
rfb::IntParameter rfb::Server::DLP_WatermarkRepeatSpace
|
||||
("DLP_WatermarkRepeatSpace",
|
||||
"Number of pixels between repeats of the watermark",
|
||||
0, 0, 4096);
|
||||
rfb::StringParameter rfb::Server::DLP_WatermarkImage
|
||||
("DLP_WatermarkImage",
|
||||
"PNG file to use as a watermark",
|
||||
"");
|
||||
rfb::StringParameter rfb::Server::DLP_WatermarkLocation
|
||||
("DLP_WatermarkLocation",
|
||||
"Place the watermark at this position from the corner.",
|
||||
"");
|
||||
rfb::StringParameter rfb::Server::DLP_WatermarkTint
|
||||
("DLP_WatermarkTint",
|
||||
"Tint the greyscale watermark by this color.",
|
||||
"255,255,255,255");
|
||||
|
||||
rfb::StringParameter rfb::Server::maxVideoResolution
|
||||
("MaxVideoResolution",
|
||||
"When in video mode, downscale the screen to max this size.",
|
||||
|
@ -48,9 +48,13 @@ namespace rfb {
|
||||
static IntParameter DLP_ClipAcceptMax;
|
||||
static IntParameter DLP_ClipDelay;
|
||||
static IntParameter DLP_KeyRateLimit;
|
||||
static IntParameter DLP_WatermarkRepeatSpace;
|
||||
static StringParameter DLP_ClipLog;
|
||||
static StringParameter DLP_Region;
|
||||
static StringParameter DLP_Clip_Types;
|
||||
static StringParameter DLP_WatermarkImage;
|
||||
static StringParameter DLP_WatermarkLocation;
|
||||
static StringParameter DLP_WatermarkTint;
|
||||
static BoolParameter DLP_RegionAllowClick;
|
||||
static BoolParameter DLP_RegionAllowRelease;
|
||||
static IntParameter jpegVideoQuality;
|
||||
|
@ -25,7 +25,8 @@ namespace rfb {
|
||||
const unsigned int tightPng = 0x0a;
|
||||
const unsigned int tightWebp = 0x0b;
|
||||
const unsigned int tightQoi = 0x0c;
|
||||
const unsigned int tightMaxSubencoding = 0x0c;
|
||||
const unsigned int tightIT = 0x0d;
|
||||
const unsigned int tightMaxSubencoding = 0x0d;
|
||||
|
||||
// Filters to improve compression efficiency
|
||||
const unsigned int tightFilterCopy = 0x00;
|
||||
|
@ -277,6 +277,28 @@ void TightEncoder::resetZlib()
|
||||
zlibNeedsReset = true;
|
||||
}
|
||||
|
||||
void TightEncoder::writeWatermarkRect(const rdr::U8 *data, const unsigned len,
|
||||
const rdr::U8 r,
|
||||
const rdr::U8 g,
|
||||
const rdr::U8 b,
|
||||
const rdr::U8 a)
|
||||
{
|
||||
rdr::OutStream* os;
|
||||
|
||||
os = conn->getOutStream(conn->cp.supportsUdp);
|
||||
|
||||
os->writeU8(tightIT << 4);
|
||||
|
||||
writeCompact(os, len + 4);
|
||||
|
||||
os->writeU8(r);
|
||||
os->writeU8(g);
|
||||
os->writeU8(b);
|
||||
os->writeU8(a);
|
||||
|
||||
os->writeBytes(data, len);
|
||||
}
|
||||
|
||||
//
|
||||
// Including BPP-dependent implementation of the encoder.
|
||||
//
|
||||
|
@ -39,6 +39,11 @@ namespace rfb {
|
||||
virtual void writeSolidRect(int width, int height,
|
||||
const PixelFormat& pf,
|
||||
const rdr::U8* colour);
|
||||
void writeWatermarkRect(const rdr::U8 *data, const unsigned len,
|
||||
const rdr::U8 r,
|
||||
const rdr::U8 g,
|
||||
const rdr::U8 b,
|
||||
const rdr::U8 a);
|
||||
void resetZlib();
|
||||
|
||||
protected:
|
||||
|
@ -1783,6 +1783,9 @@ void VNCSConnectionST::udpDowngrade(const bool byServer)
|
||||
cp.useCopyRect = true;
|
||||
encodeManager.resetZlib();
|
||||
|
||||
if (Server::DLP_WatermarkImage[0])
|
||||
cp.useCopyRect = false;
|
||||
|
||||
vlog.info("Client %s downgrading from udp by %s", sock->getPeerAddress(),
|
||||
byServer ? "the server" : "its own request");
|
||||
}
|
||||
|
@ -62,6 +62,7 @@
|
||||
#include <rfb/ServerCore.h>
|
||||
#include <rfb/VNCServerST.h>
|
||||
#include <rfb/VNCSConnectionST.h>
|
||||
#include <rfb/Watermark.h>
|
||||
#include <rfb/util.h>
|
||||
#include <rfb/ledStates.h>
|
||||
|
||||
@ -1048,6 +1049,9 @@ void VNCServerST::writeUpdate()
|
||||
memset(&jpegstats, 0, sizeof(EncodeManager::codecstats_t));
|
||||
memset(&webpstats, 0, sizeof(EncodeManager::codecstats_t));
|
||||
|
||||
if (watermarkData)
|
||||
updateWatermark();
|
||||
|
||||
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
|
||||
ci_next = ci; ci_next++;
|
||||
|
||||
|
@ -256,6 +256,8 @@ namespace rfb {
|
||||
|
||||
bool getComparerState();
|
||||
|
||||
void updateWatermark();
|
||||
|
||||
QueryConnectionHandler* queryConnectionHandler;
|
||||
KeyRemapper* keyRemapper;
|
||||
|
||||
|
248
common/rfb/Watermark.cxx
Normal file
248
common/rfb/Watermark.cxx
Normal file
@ -0,0 +1,248 @@
|
||||
/* Copyright (C) 2023 Kasm
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#include <png.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
#include <rfb/LogWriter.h>
|
||||
#include <rfb/ServerCore.h>
|
||||
#include <rfb/VNCServerST.h>
|
||||
|
||||
#include "Watermark.h"
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
static LogWriter vlog("watermark");
|
||||
|
||||
watermarkInfo_t watermarkInfo;
|
||||
|
||||
uint8_t *watermarkData, *watermarkUnpacked, *watermarkTmp;
|
||||
uint32_t watermarkDataLen;
|
||||
static uint16_t rw, rh;
|
||||
|
||||
#define MAXW 4096
|
||||
#define MAXH 4096
|
||||
|
||||
static bool loadimage(const char path[]) {
|
||||
|
||||
FILE *f = fopen(path, "r");
|
||||
if (!f) {
|
||||
vlog.error("Can't open %s", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
|
||||
if (!png_ptr) return false;
|
||||
png_infop info = png_create_info_struct(png_ptr);
|
||||
if (!info) return false;
|
||||
if (setjmp(png_jmpbuf(png_ptr))) return false;
|
||||
|
||||
png_init_io(png_ptr, f);
|
||||
png_read_png(png_ptr, info,
|
||||
PNG_TRANSFORM_PACKING |
|
||||
PNG_TRANSFORM_STRIP_16 |
|
||||
PNG_TRANSFORM_STRIP_ALPHA |
|
||||
PNG_TRANSFORM_EXPAND, NULL);
|
||||
|
||||
uint8_t **rows = png_get_rows(png_ptr, info);
|
||||
const unsigned imgw = png_get_image_width(png_ptr, info);
|
||||
const unsigned imgh = png_get_image_height(png_ptr, info);
|
||||
|
||||
watermarkInfo.w = imgw;
|
||||
watermarkInfo.h = imgh;
|
||||
watermarkInfo.src = (uint8_t *) calloc(imgw, imgh);
|
||||
|
||||
unsigned x, y;
|
||||
for (y = 0; y < imgh; y++) {
|
||||
for (x = 0; x < imgw; x++) {
|
||||
const uint8_t r = rows[y][x * 3 + 0];
|
||||
const uint8_t g = rows[y][x * 3 + 1];
|
||||
const uint8_t b = rows[y][x * 3 + 2];
|
||||
|
||||
const uint8_t grey = r * .2126f +
|
||||
g * .7152f +
|
||||
b * .0722f;
|
||||
|
||||
const uint8_t out = (grey + 8) >> 4;
|
||||
watermarkInfo.src[y * imgw + x] = out < 16 ? out : 15;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
png_destroy_info_struct(png_ptr, &info);
|
||||
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool watermarkInit() {
|
||||
memset(&watermarkInfo, 0, sizeof(watermarkInfo_t));
|
||||
watermarkData = watermarkUnpacked = watermarkTmp = NULL;
|
||||
rw = rh = 0;
|
||||
|
||||
if (!Server::DLP_WatermarkImage[0])
|
||||
return true;
|
||||
|
||||
if (!loadimage(Server::DLP_WatermarkImage))
|
||||
return false;
|
||||
|
||||
if (Server::DLP_WatermarkRepeatSpace && Server::DLP_WatermarkLocation[0]) {
|
||||
vlog.error("Repeat and location can't be used together");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sscanf(Server::DLP_WatermarkTint, "%hhu,%hhu,%hhu,%hhu",
|
||||
&watermarkInfo.r,
|
||||
&watermarkInfo.g,
|
||||
&watermarkInfo.b,
|
||||
&watermarkInfo.a) != 4) {
|
||||
vlog.error("Invalid tint");
|
||||
return false;
|
||||
}
|
||||
|
||||
watermarkInfo.repeat = Server::DLP_WatermarkRepeatSpace;
|
||||
|
||||
if (Server::DLP_WatermarkLocation[0]) {
|
||||
if (sscanf(Server::DLP_WatermarkLocation, "%hd,%hd",
|
||||
&watermarkInfo.x,
|
||||
&watermarkInfo.y) != 2) {
|
||||
vlog.error("Invalid location");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
watermarkUnpacked = (uint8_t *) calloc(MAXW, MAXH);
|
||||
watermarkTmp = (uint8_t *) calloc(MAXW, MAXH / 2);
|
||||
watermarkData = (uint8_t *) calloc(MAXW, MAXH / 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// update the screen-size rendered watermark whenever the screen is resized
|
||||
void VNCServerST::updateWatermark() {
|
||||
if (rw == pb->width() &&
|
||||
rh == pb->height())
|
||||
return;
|
||||
|
||||
rw = pb->width();
|
||||
rh = pb->height();
|
||||
|
||||
memset(watermarkUnpacked, 0, rw * rh);
|
||||
|
||||
uint16_t x, y, srcy;
|
||||
|
||||
if (watermarkInfo.repeat) {
|
||||
for (y = 0, srcy = 0; y < rh; y++) {
|
||||
for (x = 0; x < rw;) {
|
||||
if (x + watermarkInfo.w < rw)
|
||||
memcpy(&watermarkUnpacked[y * rw + x],
|
||||
&watermarkInfo.src[srcy * watermarkInfo.w],
|
||||
watermarkInfo.w);
|
||||
else
|
||||
memcpy(&watermarkUnpacked[y * rw + x],
|
||||
&watermarkInfo.src[srcy * watermarkInfo.w],
|
||||
rw - x);
|
||||
|
||||
x += watermarkInfo.w + watermarkInfo.repeat;
|
||||
}
|
||||
|
||||
srcy++;
|
||||
if (srcy == watermarkInfo.h) {
|
||||
srcy = 0;
|
||||
y += watermarkInfo.repeat;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int16_t sx, sy;
|
||||
|
||||
if (!watermarkInfo.x)
|
||||
sx = (rw - watermarkInfo.w) / 2;
|
||||
else if (watermarkInfo.x > 0)
|
||||
sx = watermarkInfo.x;
|
||||
else
|
||||
sx = rw - watermarkInfo.w + watermarkInfo.x;
|
||||
|
||||
if (sx < 0)
|
||||
sx = 0;
|
||||
|
||||
if (!watermarkInfo.y)
|
||||
sy = (rh - watermarkInfo.h) / 2;
|
||||
else if (watermarkInfo.y > 0)
|
||||
sy = watermarkInfo.y;
|
||||
else
|
||||
sy = rh - watermarkInfo.h + watermarkInfo.y;
|
||||
|
||||
if (sy < 0)
|
||||
sy = 0;
|
||||
|
||||
for (y = 0; y < watermarkInfo.h; y++) {
|
||||
if (sx + watermarkInfo.w < rw)
|
||||
memcpy(&watermarkUnpacked[(sy + y) * rw + sx],
|
||||
&watermarkInfo.src[y * watermarkInfo.w],
|
||||
watermarkInfo.w);
|
||||
else
|
||||
memcpy(&watermarkUnpacked[(sy + y) * rw + sx],
|
||||
&watermarkInfo.src[y * watermarkInfo.w],
|
||||
rw - sx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void packWatermark(const Region &changed) {
|
||||
// Take the expanded 4-bit data, filter it by the changed rects, pack
|
||||
// to shared bytes, and compress with zlib
|
||||
|
||||
uint16_t x, y;
|
||||
uint8_t pix[2], cur = 0;
|
||||
uint8_t *dst = watermarkTmp;
|
||||
|
||||
const Rect &bounding = changed.get_bounding_rect();
|
||||
|
||||
for (y = 0; y < rh; y++) {
|
||||
// Is the entire line outside the changed area?
|
||||
if (bounding.tl.y > y || bounding.br.y < y) {
|
||||
for (x = 0; x < rw; x++) {
|
||||
pix[cur] = 0;
|
||||
|
||||
if (cur || (y == rh - 1 && x == rw - 1))
|
||||
*dst++ = pix[0] | (pix[1] << 4);
|
||||
|
||||
cur ^= 1;
|
||||
}
|
||||
} else {
|
||||
for (x = 0; x < rw; x++) {
|
||||
pix[cur] = 0;
|
||||
if (bounding.contains(Point(x, y)) && changed.contains(x, y))
|
||||
pix[cur] = watermarkUnpacked[y * rw + x];
|
||||
|
||||
if (cur || (y == rh - 1 && x == rw - 1))
|
||||
*dst++ = pix[0] | (pix[1] << 4);
|
||||
|
||||
cur ^= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uLong destLen = MAXW * MAXH / 2;
|
||||
if (compress2(watermarkData, &destLen, watermarkTmp, rw * rh / 2 + 1, 1) != Z_OK)
|
||||
vlog.error("Zlib compression error");
|
||||
|
||||
watermarkDataLen = destLen;
|
||||
}
|
43
common/rfb/Watermark.h
Normal file
43
common/rfb/Watermark.h
Normal file
@ -0,0 +1,43 @@
|
||||
/* Copyright (C) 2023 Kasm
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#ifndef WATERMARK_H
|
||||
#define WATERMARK_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <rfb/Region.h>
|
||||
|
||||
struct watermarkInfo_t {
|
||||
uint8_t *src;
|
||||
uint16_t w, h;
|
||||
|
||||
int16_t x, y;
|
||||
uint16_t repeat;
|
||||
|
||||
uint8_t r, g, b, a;
|
||||
};
|
||||
|
||||
extern watermarkInfo_t watermarkInfo;
|
||||
|
||||
bool watermarkInit();
|
||||
void packWatermark(const rfb::Region &changed); // filter and pack the watermark for sending
|
||||
|
||||
extern uint8_t *watermarkData;
|
||||
extern uint32_t watermarkDataLen;
|
||||
|
||||
#endif
|
2
kasmweb
2
kasmweb
@ -1 +1 @@
|
||||
Subproject commit 31b1a93335c1cb4947d4eac06dd1311bb18f5022
|
||||
Subproject commit f17512ee88b80ea7ee4e5b94c74963a4729a6c45
|
5
spec/fixtures/defaults_config.yaml
vendored
5
spec/fixtures/defaults_config.yaml
vendored
@ -45,6 +45,11 @@ data_loss_prevention:
|
||||
keyboard:
|
||||
enabled: true
|
||||
rate_limit: unlimited
|
||||
watermark:
|
||||
# image: /etc/kasmvnc/picture.png
|
||||
# location: 10,10
|
||||
# tint: 255,20,20,128
|
||||
# repeat_spacing: 10
|
||||
logging:
|
||||
level: off
|
||||
|
||||
|
@ -87,9 +87,13 @@ data_loss_prevention:
|
||||
keyboard:
|
||||
enabled: true
|
||||
rate_limit: unlimited
|
||||
# "verbose" SETTING LOGS YOUR PRIVATE INFORMATION. Keypresses and clipboard
|
||||
# content.
|
||||
watermark:
|
||||
# image: /etc/kasmvnc/picture.png
|
||||
# location: 10,10
|
||||
# tint: 255,20,20,128
|
||||
# repeat_spacing: 10
|
||||
logging:
|
||||
# "verbose" SETTING LOGS YOUR PRIVATE INFORMATION. Keypresses and clipboard content
|
||||
level: off
|
||||
|
||||
encoding:
|
||||
|
@ -1721,6 +1721,50 @@ sub DefineConfigToCLIConversion {
|
||||
$value;
|
||||
}
|
||||
}),
|
||||
KasmVNC::CliOption->new({
|
||||
name => 'DLP_WatermarkImage',
|
||||
configKeys => [
|
||||
KasmVNC::ConfigKey->new({
|
||||
name => "data_loss_prevention.watermark.image",
|
||||
type => KasmVNC::ConfigKey::ANY
|
||||
})
|
||||
]
|
||||
}),
|
||||
KasmVNC::CliOption->new({
|
||||
name => 'DLP_WatermarkLocation',
|
||||
configKeys => [
|
||||
KasmVNC::ConfigKey->new({
|
||||
name => "data_loss_prevention.watermark.location",
|
||||
type => KasmVNC::ConfigKey::ANY,
|
||||
validator => KasmVNC::PatternValidator->new({
|
||||
pattern => qr/^\d+,\d+$/,
|
||||
errorMessage => "Must be an x and y offset separated by a comma: 10,10"
|
||||
})
|
||||
})
|
||||
]
|
||||
}),
|
||||
KasmVNC::CliOption->new({
|
||||
name => 'DLP_WatermarkTint',
|
||||
configKeys => [
|
||||
KasmVNC::ConfigKey->new({
|
||||
name => "data_loss_prevention.watermark.tint",
|
||||
type => KasmVNC::ConfigKey::ANY,
|
||||
validator => KasmVNC::PatternValidator->new({
|
||||
pattern => qr/^\d{1,3},\d{1,3},\d{1,3},\d{1,3}$/,
|
||||
errorMessage => "Must be RBGA formatted: 255,255,255,128"
|
||||
})
|
||||
})
|
||||
]
|
||||
}),
|
||||
KasmVNC::CliOption->new({
|
||||
name => 'DLP_WatermarkRepeatSpace',
|
||||
configKeys => [
|
||||
KasmVNC::ConfigKey->new({
|
||||
name => "data_loss_prevention.watermark.repeat_spacing",
|
||||
type => KasmVNC::ConfigKey::INT
|
||||
})
|
||||
]
|
||||
}),
|
||||
KasmVNC::CliOption->new({
|
||||
name => 'DLP_Log',
|
||||
configKeys => [
|
||||
|
@ -350,6 +350,28 @@ Log clipboard and keyboard actions. Info logs just clipboard direction and size,
|
||||
verbose adds the contents for both.
|
||||
.
|
||||
.TP
|
||||
.B \-DLP_WatermarkImage \fIpath/to/file.png\fP
|
||||
Add a watermark. The PNG file should be greyscale, black is treated as transparent
|
||||
and white as opaque.
|
||||
.
|
||||
.TP
|
||||
.B \-DLP_WatermarkLocation \fIx,y\fP
|
||||
Place the watermark at this position from the corner. Positive numbers are from top-left,
|
||||
negative from bottom-right. Negative numbers count from the bottom-right edge of the image.
|
||||
If not set, the watermark will be centered. Cannot be used together with repeat.
|
||||
.
|
||||
.TP
|
||||
.B \-DLP_WatermarkRepeatSpace \fInum\fP
|
||||
If set, repeat the watermark over the entire image, with \fBnum\fP pixels between
|
||||
repetitions. Cannot be used together with location.
|
||||
.
|
||||
.TP
|
||||
.B \-DLP_WatermarkTint \fIr,g,b,a\fP
|
||||
Tint the greyscale watermark by this color. Default is 255,255,255,255 - full white.
|
||||
The color components can be used to colorize the greyscale watermark, and the alpha
|
||||
can be used to make it fainter.
|
||||
.
|
||||
.TP
|
||||
.B \-selfBench
|
||||
Run a set of self-benchmarks and exit.
|
||||
.
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <rfb/Hostname.h>
|
||||
#include <rfb/Region.h>
|
||||
#include <rfb/ledStates.h>
|
||||
#include <rfb/Watermark.h>
|
||||
#include <network/iceip.h>
|
||||
#include <network/TcpSocket.h>
|
||||
#include <network/UnixSocket.h>
|
||||
@ -232,6 +233,9 @@ void vncExtensionInit(void)
|
||||
dummyY < 16)
|
||||
vncFatalError("Invalid value to %s", Server::maxVideoResolution.getName());
|
||||
|
||||
if (!watermarkInit())
|
||||
vncFatalError("Invalid watermark params");
|
||||
|
||||
pipe(wakeuppipe);
|
||||
const int flags = fcntl(wakeuppipe[0], F_GETFL, 0);
|
||||
fcntl(wakeuppipe[0], F_SETFL, flags | O_NONBLOCK);
|
||||
|
Loading…
Reference in New Issue
Block a user