Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,7 @@ distcheck: all

clean:
rm -f $(OUT)

install:
sudo apt install -y libx11-dev libxdamage-dev libxfixes-dev libxtst-dev liblz4-dev g++

3 changes: 3 additions & 0 deletions PIwebVNC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "libs/input.hpp"
#include "libs/vncserver.hpp"
#include "libs/appConfigs.hpp"
#include "libs/clipboard.hpp"

using namespace std;

Expand Down Expand Up @@ -67,6 +68,8 @@ int main(int argc, char *argv[])
Display *display = vncServer.xdisplay.getDisplay();
Websocket ws;
XInputs input(display);
Clipboard clipboard(display);
input.clipboard = &clipboard;
vncServer.inputs = &input;
wss = &ws;
xinputs = &input;
Expand Down
Binary file added PiWebVNC
Binary file not shown.
5 changes: 5 additions & 0 deletions compile.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ fi
apt install -y libx11-dev libxdamage-dev libxfixes-dev libxtst-dev liblz4-dev g++

g++ PIwebVNC.cpp -lX11 -lXdamage -lXfixes -pthread -lXtst -llz4 -o /bin/PiWebVNC
g++ -static PIwebVNC.cpp -lX11 -lXdamage -lXfixes -lXtst -llz4 -pthread -o ../pvc
g++ -static PIwebVNC.cpp \
-L/usr/lib/x86_64-linux-gnu \
-lX11 -lXdamage -lXfixes -lXtst -lXext -lxcb -lXau -lXdmcp -lXrender


echo "[INFO] Compile Successful."

Expand Down
161 changes: 161 additions & 0 deletions libs/clipboard.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
clipboard.hpp - source code

=================================
clipboard class for PIwebVNC in C++
=================================

Free to use, free to modify, free to redistribute.
Created by : Jishan Ali Mondal

This is a header-only library.
created for only PIwebVNC
* This code was created entirely for the most optimized performance for
PIwebVNC *
* May not be suitable for other projects *
version 1.0.1
*/



#ifndef CLIPBOARD_HPP
#define CLIPBOARD_HPP

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <string>
#include <thread>
#include <atomic>
#include <iostream>
#include <cstring>
#include <chrono>

class Clipboard {
private:
Display *display;
Window window;
Atom XA_CLIPBOARD_;
Atom XA_UTF8_;
std::string offeredText_;
std::thread eventThread;
std::atomic<bool> running;

void eventLoop() {
while (running) {
while (XPending(display)) {
XEvent ev;
XNextEvent(display, &ev);

if (ev.type == SelectionRequest) {
XSelectionRequestEvent *req = &ev.xselectionrequest;

XEvent respond;
memset(&respond, 0, sizeof(respond));
respond.xselection.type = SelectionNotify;
respond.xselection.display = req->display;
respond.xselection.requestor = req->requestor;
respond.xselection.selection = req->selection;
respond.xselection.target = req->target;
respond.xselection.time = req->time;
respond.xselection.property = None;

// Handle TARGETS request (list formats we support)
if (req->target == XInternAtom(display, "TARGETS", False)) {
Atom supported[2] = {
XA_UTF8_,
XInternAtom(display, "STRING", False)
};
XChangeProperty(display,
req->requestor,
req->property,
XA_ATOM,
32,
PropModeReplace,
(unsigned char*)supported,
2);
respond.xselection.property = req->property;
}
// Serve actual text
else if (req->target == XA_UTF8_ ||
req->target == XInternAtom(display, "STRING", False)) {
XChangeProperty(display,
req->requestor,
req->property,
req->target,
8,
PropModeReplace,
(unsigned char*)offeredText_.c_str(),
offeredText_.size());
respond.xselection.property = req->property;
}

XSendEvent(display, req->requestor, True, 0, &respond);
XFlush(display);
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}

public:
Clipboard(Display *dpy) {
if (!XInitThreads()) {
std::cerr << "Failed to initialize X11 threads!" << std::endl;
}

display = dpy;
window = XCreateSimpleWindow(display, DefaultRootWindow(display),
0, 0, 1, 1, 0, 0, 0);
XA_CLIPBOARD_ = XInternAtom(display, "CLIPBOARD", False);
XA_UTF8_ = XInternAtom(display, "UTF8_STRING", False);

running = true;
eventThread = std::thread(&Clipboard::eventLoop, this);
}

~Clipboard() {
running = false;
if (eventThread.joinable()) eventThread.join();
if (window) XDestroyWindow(display, window);
}

void setText(const char *text) {
if (!text) return;
offeredText_ = text;

// Claim ownership of CLIPBOARD
XSetSelectionOwner(display, XA_CLIPBOARD_, window, CurrentTime);
XFlush(display);

if (XGetSelectionOwner(display, XA_CLIPBOARD_) != window) {
std::cerr << "Clipboard: failed to become owner" << std::endl;
}
}

std::string getText() {
Atom prop = XInternAtom(display, "XSEL_DATA", False);
XConvertSelection(display, XA_CLIPBOARD_, XA_UTF8_, prop, window, CurrentTime);
XFlush(display);

XEvent ev;
XNextEvent(display, &ev);

if (ev.type == SelectionNotify && ev.xselection.property != None) {
Atom type;
int format;
unsigned long nitems, bytes_after;
unsigned char *data = nullptr;

XGetWindowProperty(display, window, prop, 0, ~0, False, AnyPropertyType,
&type, &format, &nitems, &bytes_after, &data);
if (data) {
std::string result((char*)data, nitems);
XFree(data);
return result;
}
}
return "";
}
};

#endif
13 changes: 7 additions & 6 deletions libs/httpPage.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9641,12 +9641,13 @@ void parseHttpPage()
var text = e.clipboardData.getData('text/plain') || window.clipboardData.getData('Text');
//console.log(text);
if (webSocket != null && webSocket.readyState == 1) {
webSocket.send("P" + text);
//update as soon as possible [make it server side]
for (var i = 0; i < text.length; i++) {
let key = getXkeyName(text[i]);
webSocket.send("K1" + key + "\0");
await sleep(80);
}
// for (var i = 0; i < text.length; i++) {
// let key = getXkeyName(text[i]);
// webSocket.send("K1" + key + "\0");
// await sleep(80);
// }
}
}
function drawCanvas(data) {
Expand Down Expand Up @@ -9692,7 +9693,7 @@ void parseHttpPage()
return;
}
statusdiv.innerText = "connecting...";
let ws = new WebSocket("ws://" + location.host);
let ws = new WebSocket(`${location.protocol == 'https:' ? 'wss' : 'ws'}://` + location.host);
webSocket = ws;
ws.onerror = function (evt) {
//console.log("WebSocket Error: ", e);
Expand Down
50 changes: 34 additions & 16 deletions libs/input.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#ifndef INPUT_HPP
#define INPUT_HPP

#include "clipboard.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
Expand All @@ -26,6 +27,7 @@
#include <X11/Xutil.h>
#include <X11/extensions/XTest.h>
#include <X11/extensions/Xdamage.h>
#include <iostream>

class XInputs
{
Expand All @@ -34,9 +36,10 @@ class XInputs
void processInputs(char *data, int clinetSD);
void queueInputs(char *data, int clinetSD);
void dispatchEvents();
Clipboard *clipboard;
private:
Display *display;
char events[30][100] = {0};
char events[30][1024] = {0};
int eventCount = 0;
};

Expand All @@ -49,6 +52,7 @@ void XInputs::queueInputs(char *data, int clinetSD)
{
if (this->eventCount < 30)
{
std::cout << data << std::endl;
strcpy(this->events[this->eventCount], data);
this->eventCount++;
}
Expand All @@ -69,7 +73,7 @@ void XInputs::processInputs(char *data, int clientSD)
{
int len = strlen(data);
int x = 0, y = 0, i = 1, x2 = 0, y2 = 0;
if (data[0] == 'C')
if (data[0] == 'C') //for click to x y pos
{
while (data[i] != 32 && i < len)
x = x * 10 + data[i++] - 48;
Expand All @@ -78,12 +82,12 @@ void XInputs::processInputs(char *data, int clientSD)
y = y * 10 + data[i++] - 48;
if (this->display == 0)
return;
XTestFakeMotionEvent(this->display, -1, x, y, CurrentTime);
XTestFakeMotionEvent(this->display, DefaultScreen(this->display), x, y, CurrentTime);
XTestFakeButtonEvent(this->display, 1, True, CurrentTime);
XTestFakeButtonEvent(this->display, 1, False, CurrentTime);
XFlush(this->display);
}
else if (data[0] == 'M')
else if (data[0] == 'M') //only mouse pointer move to x y pos
{
while (data[i] != 32 && i < len)
x = x * 10 + data[i++] - 48;
Expand All @@ -92,10 +96,10 @@ void XInputs::processInputs(char *data, int clientSD)
y = y * 10 + data[i++] - 48;
if (this->display == 0)
return;
XTestFakeMotionEvent(this->display, -1, x, y, CurrentTime);
XTestFakeMotionEvent(this->display,DefaultScreen(this->display), x, y, CurrentTime);
XFlush(this->display);
}
else if (data[0] == 'R')
else if (data[0] == 'R') // right click at x y pos
{
while (data[i] != 32 && i < len)
x = x * 10 + data[i++] - 48;
Expand All @@ -104,12 +108,12 @@ void XInputs::processInputs(char *data, int clientSD)
y = y * 10 + data[i++] - 48;
if (this->display == 0)
return;
XTestFakeMotionEvent(this->display, -1, x, y, CurrentTime);
XTestFakeMotionEvent(this->display, DefaultScreen(this->display), x, y, CurrentTime);
XTestFakeButtonEvent(this->display, 3, True, CurrentTime);
XTestFakeButtonEvent(this->display, 3, False, CurrentTime);
XFlush(this->display);
}
else if (data[0] == 'D')
else if (data[0] == 'D') // drag from x y pos to x2 y2 pos
{
while (data[i] != 32 && i < len)
x = x * 10 + data[i++] - 48;
Expand All @@ -124,34 +128,34 @@ void XInputs::processInputs(char *data, int clientSD)
y2 = y2 * 10 + data[i++] - 48;
if (this->display == 0)
return;
XTestFakeMotionEvent(this->display, -1, x, y, CurrentTime);
XTestFakeMotionEvent(this->display, DefaultScreen(this->display), x, y, CurrentTime);
XTestFakeButtonEvent(this->display, 1, True, CurrentTime);
XTestFakeMotionEvent(this->display, -1, x2, y2, CurrentTime);
XTestFakeMotionEvent(this->display, DefaultScreen(this->display), x2, y2, CurrentTime);
XFlush(this->display);
}
else if (data[0] == 'S')
else if (data[0] == 'S') // mouse scroll
{
if (data[1] == 'U')
if (data[1] == 'U') // scroll uo
{
XTestFakeButtonEvent(this->display, 4, 1, 0);
XTestFakeButtonEvent(this->display, 4, 0, 70);
}
else
else //scroll down
{
XTestFakeButtonEvent(this->display, 5, 1, 0);
XTestFakeButtonEvent(this->display, 5, 0, 70);
}
}
else if (data[0] == 'K')
else if (data[0] == 'K') // keyboard key press
{
if (data[1] == 49)
if (data[1] == 49) // single key press
{
int keycode = XKeysymToKeycode(this->display, XStringToKeysym(data + 2));
XTestFakeKeyEvent(this->display, keycode, True, CurrentTime);
XTestFakeKeyEvent(this->display, keycode, False, CurrentTime);
XFlush(this->display);
}
else if (data[1] == 50)
else if (data[1] == 50) // combo key press
{
char buffer[15] = {0};
int i = 2;
Expand All @@ -170,6 +174,20 @@ void XInputs::processInputs(char *data, int clientSD)
XFlush(this->display);
}
}
else if (data[0] == 'P') // paste (ctrl + shift + v)
{
this->clipboard->setText(&data[1]);
int keycode = XKeysymToKeycode(this->display, XStringToKeysym("Control_L"));
int keycode2 = XKeysymToKeycode(this->display, XStringToKeysym("Shift_L"));
int keycode3 = XKeysymToKeycode(this->display, XStringToKeysym("v"));
XTestFakeKeyEvent(this->display, keycode, True, CurrentTime);
XTestFakeKeyEvent(this->display, keycode2, True, CurrentTime);
XTestFakeKeyEvent(this->display, keycode3, True, CurrentTime);
XTestFakeKeyEvent(this->display, keycode3, False, CurrentTime);
XTestFakeKeyEvent(this->display, keycode2, False, CurrentTime);
XTestFakeKeyEvent(this->display, keycode, False, CurrentTime);
XFlush(this->display);
}
XFlush(this->display);
}

Expand Down
2 changes: 1 addition & 1 deletion libs/websocket.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ void Websocket::connections()
{
if (callBackMsg != NULL)
{
char inputData[200]={0};
char inputData[1024]={0};
decode(buffer , inputData);
callBackMsg(inputData, i);
}
Expand Down