mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
Turtle moved to UppHub
git-svn-id: svn://ultimatepp.org/upp/trunk@15649 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
c7e3f0ceb3
commit
3cc74e6760
16 changed files with 3 additions and 1976 deletions
|
|
@ -1,84 +0,0 @@
|
|||
template "U++ Basic Turtle application" main;
|
||||
|
||||
option "Create header" header;
|
||||
|
||||
text "Server URL" host = "localhost";
|
||||
text "Connection port" port = "8888";
|
||||
text "Connection limit" limit = "100";
|
||||
option "Debug mode" debugmode;
|
||||
option "Add GUI mode switch" guiflag;
|
||||
|
||||
@@<:PACKAGE:>.h
|
||||
??header
|
||||
#ifndef _<:PACKAGE:>_<:PACKAGE:>_h
|
||||
#define _<:PACKAGE:>_<:PACKAGE:>_h
|
||||
<:?guiflag:>
|
||||
#ifdef flagTURTLEGUI
|
||||
#include <Turtle/Turtle.h>
|
||||
#else
|
||||
#include <CtrlLib/CtrlLib.h>
|
||||
#endif
|
||||
<:/:>
|
||||
#include <Turtle/Turtle.h>
|
||||
<:.:>
|
||||
using namespace Upp {
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@<:PACKAGE:>.cpp
|
||||
<:? header :>#include "<:PACKAGE:>.h"<:.:><:? !header && !guiflag:>#include <Turtle/Turtle.h><:.:><:? !header && guiflag :>#ifdef flagTURTLEGUI
|
||||
#include <Turtle/Turtle.h>
|
||||
#else
|
||||
#include <CtrlLib/CtrlLib.h>
|
||||
#endif<:.:>
|
||||
|
||||
using namespace Upp;
|
||||
|
||||
void AppMainLoop()
|
||||
{
|
||||
// "Main" stuff should go in here...
|
||||
}
|
||||
<:?guiflag:>
|
||||
#ifdef flagTURTLEGUI
|
||||
<:.:>
|
||||
CONSOLE_APP_MAIN
|
||||
{
|
||||
<:?debugmode:>
|
||||
#ifdef _DEBUG
|
||||
TurtleServer::DebugMode();
|
||||
#endif
|
||||
<:.:>
|
||||
// MemoryLimitKb(100000000); // Can aid preventing DDoS attacks.
|
||||
|
||||
TurtleServer guiserver;
|
||||
guiserver.Host("<:host:>");
|
||||
guiserver.Port(<:port:>);
|
||||
guiserver.MaxConnections(<:limit:>);
|
||||
RunTurtleGui(guiserver, AppMainLoop);
|
||||
}
|
||||
<:?guiflag:>
|
||||
#else
|
||||
|
||||
GUI_APP_MAIN
|
||||
{
|
||||
AppMainLoop();
|
||||
}
|
||||
|
||||
#endif
|
||||
<:.:>
|
||||
@@<:PACKAGE:>.upp
|
||||
<:?guiflag:>
|
||||
uses (TURTLEGUI) Turtle;
|
||||
uses (!TURTLEGUI) CtrlLib;
|
||||
<:/:>
|
||||
uses Turtle;
|
||||
<:.:>
|
||||
|
||||
file<:?header:>
|
||||
<:PACKAGE:>.h,<:.:>
|
||||
<:PACKAGE:>.cpp;
|
||||
|
||||
mainconfig
|
||||
"" = "TURTLEGUI"<:?guiflag:>,<:/:>;<:.:>
|
||||
<:?guiflag:>"" = "GUI";<:.:>
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
template "U++ Turtle application with main window" main;
|
||||
|
||||
id "Main window class name" classname = PACKAGE;
|
||||
|
||||
select("No layouts", "Generate layout file", "Main window has layout", "Main window has OK/Cancel") "Layout" lay = 2;
|
||||
|
||||
option "Imagelist file" iml;
|
||||
|
||||
text "Server URL" host = "localhost";
|
||||
text "Connection port" port = "8888";
|
||||
text "Connection limit" limit = "100";
|
||||
|
||||
option "Debug mode" debugmode;
|
||||
option "Add GUI mode switch" guiflag;
|
||||
|
||||
@@<:PACKAGE:>.h
|
||||
#ifndef _<:PACKAGE:>_<:PACKAGE:>_h
|
||||
#define _<:PACKAGE:>_<:PACKAGE:>_h
|
||||
<:?guiflag:>
|
||||
#ifdef flagTURTLEGUI
|
||||
#include <Turtle/Turtle.h>
|
||||
#else
|
||||
#include <CtrlLib/CtrlLib.h>
|
||||
#endif
|
||||
<:/:>
|
||||
#include <Turtle/Turtle.h>
|
||||
<:.:>
|
||||
namespace Upp {<:?lay:>
|
||||
|
||||
#define LAYOUTFILE <<:PACKAGE:>/<:PACKAGE:>.lay>
|
||||
#include <CtrlCore/lay.h><:.:><:?iml:>
|
||||
|
||||
#define IMAGEFILE <<:PACKAGE:>/<:PACKAGE:>.iml>
|
||||
#include <Draw/iml_header.h><:.:>
|
||||
|
||||
class <:classname:> : public <:lay > 1 ? "With" + classname + "Layout<TopWindow>" : "TopWindow":> {
|
||||
public:
|
||||
<:classname:>();
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
@@main.cpp
|
||||
#include "<:PACKAGE:>.h"<:?iml:>
|
||||
|
||||
#define IMAGEFILE <<:PACKAGE:>/<:PACKAGE:>.iml>
|
||||
#include <Draw/iml_source.h><:.:>
|
||||
|
||||
using namespace Upp;
|
||||
|
||||
<:classname:>::<:classname:>()
|
||||
{<:?lay > 1:>
|
||||
CtrlLayout<:(lay == 3 ? "OKCancel" : ""):>(*this, "Window title");<:.:>
|
||||
}
|
||||
|
||||
void AppMainLoop()
|
||||
{
|
||||
// "Main" stuff should go in here...
|
||||
|
||||
<:classname:>().Run();
|
||||
}
|
||||
<:?guiflag:>
|
||||
#ifdef flagTURTLEGUI
|
||||
<:.:>
|
||||
CONSOLE_APP_MAIN
|
||||
{
|
||||
<:?debugmode:>
|
||||
#ifdef _DEBUG
|
||||
TurtleServer::DebugMode();
|
||||
#endif
|
||||
<:.:>
|
||||
// MemoryLimitKb(100000000); // Can aid preventing DDoS attacks.
|
||||
|
||||
TurtleServer guiserver;
|
||||
guiserver.Host("<:host:>");
|
||||
guiserver.Port(<:port:>);
|
||||
guiserver.MaxConnections(<:limit:>);
|
||||
RunTurtleGui(guiserver, AppMainLoop);
|
||||
}
|
||||
<:?guiflag:>
|
||||
#else
|
||||
|
||||
GUI_APP_MAIN
|
||||
{
|
||||
AppMainLoop();
|
||||
}
|
||||
|
||||
#endif
|
||||
<:.:>
|
||||
@@<:PACKAGE:>.lay
|
||||
??lay
|
||||
|
||||
LAYOUT(<:classname:>Layout, 200, 100)<:?lay == 3:>
|
||||
ITEM(Button, ok, SetLabel("OK").RightPosZ(90, 80).BottomPosZ(4, 22))
|
||||
ITEM(Button, cancel, SetLabel("Cancel").RightPosZ(4, 80).BottomPosZ(4, 22))<:.:>
|
||||
END_LAYOUT
|
||||
@@<:PACKAGE:>.iml
|
||||
??iml
|
||||
|
||||
@@<:PACKAGE:>.upp
|
||||
<:?guiflag:>
|
||||
uses (TURTLEGUI) Turtle;
|
||||
uses (!TURTLEGUI) CtrlLib;
|
||||
<:/:>
|
||||
uses Turtle;
|
||||
<:.:>
|
||||
|
||||
file
|
||||
<:PACKAGE:>.h,
|
||||
main.cpp<:?lay:>,
|
||||
<:PACKAGE:>.lay<:.:><:?iml:>,
|
||||
<:PACKAGE:>.iml<:.:>;
|
||||
|
||||
mainconfig
|
||||
"" = "TURTLEGUI"<:?guiflag:>,<:/:>;<:.:>
|
||||
<:?guiflag:>"" = "GUI";<:.:>
|
||||
|
||||
|
|
@ -1,185 +0,0 @@
|
|||
#include "Turtle.h"
|
||||
|
||||
#define LLOG(x) // LLOG(x)
|
||||
#define LDUMP(x) // RDUMP(x)
|
||||
#define LTIMING(x)
|
||||
|
||||
namespace Upp {
|
||||
|
||||
static int sHandleCount;
|
||||
static Vector<int> sFreeHandles;
|
||||
static TurtleServer::Draw sTurtleDraw;
|
||||
static LRUCache<TurtleServer::ImageSysData, int64> sImageCache;
|
||||
|
||||
struct ImageSysDataMaker : LRUCache<TurtleServer::ImageSysData, int64>::Maker {
|
||||
Image img;
|
||||
|
||||
int64 Key() const override
|
||||
{
|
||||
return img.GetSerialId();
|
||||
}
|
||||
|
||||
int Make(TurtleServer::ImageSysData& object) const override
|
||||
{
|
||||
object.Init(img);
|
||||
return img.GetLength();
|
||||
}
|
||||
};
|
||||
|
||||
static int sAllocImageHandle()
|
||||
{
|
||||
return sFreeHandles.GetCount() ? sFreeHandles.Pop() : sHandleCount++;
|
||||
}
|
||||
|
||||
static void sFreeImageHandle(int handle)
|
||||
{
|
||||
sFreeHandles.Add(handle);
|
||||
}
|
||||
|
||||
TurtleServer::ImageSysData::ImageSysData()
|
||||
{
|
||||
}
|
||||
|
||||
TurtleServer::ImageSysData::~ImageSysData()
|
||||
{
|
||||
SysImageReleased(image);
|
||||
sFreeImageHandle(handle);
|
||||
}
|
||||
|
||||
void TurtleServer::ImageSysData::Init(const Image& img)
|
||||
{
|
||||
image = img;
|
||||
handle = sAllocImageHandle();
|
||||
|
||||
LLOG(Format("SetImage %`, size: %`, cache size: %",
|
||||
handle, image.GetSize(), sImageCache.GetSize()));
|
||||
|
||||
TurtleServer::Put8(TurtleServer::SETIMAGE);
|
||||
TurtleServer::Put16(handle);
|
||||
TurtleServer::Put(image.GetSize());
|
||||
Image um = Unmultiply(image);
|
||||
const RGBA *end = ~um + um.GetLength();
|
||||
for(const RGBA *s = ~um; s < end; s++) {
|
||||
TurtleServer::Put8(s->r);
|
||||
TurtleServer::Put8(s->g);
|
||||
TurtleServer::Put8(s->b);
|
||||
TurtleServer::Put8(s->a);
|
||||
}
|
||||
TurtleServer::stat_setimage++;
|
||||
TurtleServer::stat_setimage_len += image.GetLength() * sizeof(RGBA);
|
||||
SysImageRealized(image);
|
||||
}
|
||||
|
||||
TurtleServer::Draw::Draw()
|
||||
{
|
||||
pos = { 0, 0 };
|
||||
PaintOnly();
|
||||
sysdraw.SetTarget(this);
|
||||
SDraw::Init(TurtleServer::desktopsize);
|
||||
}
|
||||
|
||||
void TurtleServer::Draw::Init(const Size& sz)
|
||||
{
|
||||
SDraw::Init(sz);
|
||||
}
|
||||
|
||||
void TurtleServer::Draw::PutImage(Point p, const Image& img, const Rect& src)
|
||||
{
|
||||
LLOG(Format("Turtle::Draw::PutImage %`, size: %`, src: %`, id: %",
|
||||
p, img.GetSize(), src, img.GetSerialId()));
|
||||
|
||||
TurtleServer::stat_putimage++;
|
||||
ImageSysDataMaker m;
|
||||
|
||||
LLOG(Format("SysImage cache pixels %`, count: %",
|
||||
sImageCache.GetSize(), sImageCache.GetCount()));
|
||||
|
||||
m.img = img;
|
||||
ImageSysData& sd = sImageCache.Get(m);
|
||||
if(Rect(img.GetSize()) == src) {
|
||||
Point dp = p - pos;
|
||||
if(abs(dp.x) < 256
|
||||
&& abs(dp.y) < 256) {
|
||||
TurtleServer::Put8(dp.x < 0
|
||||
? dp.y < 0 ? TurtleServer::IMAGENN : TurtleServer::IMAGENP
|
||||
: dp.y < 0 ? TurtleServer::IMAGEPN : TurtleServer::IMAGEPP);
|
||||
TurtleServer::Put8(abs(dp.x));
|
||||
TurtleServer::Put8(abs(dp.y));
|
||||
TurtleServer::Put16(sd.handle);
|
||||
pos = p;
|
||||
sImageCache.Shrink(25000000, 5000); // Cache must be after Paint because of PaintOnly!
|
||||
return;
|
||||
}
|
||||
}
|
||||
TurtleServer::Put8(TurtleServer::IMAGE);
|
||||
TurtleServer::Put16(sd.handle);
|
||||
TurtleServer::Put(p);
|
||||
TurtleServer::Put(src);
|
||||
pos = p;
|
||||
sImageCache.Shrink(25000000, 5000); // Cache must be after Paint because of PaintOnly!
|
||||
}
|
||||
|
||||
void TurtleServer::Draw::PutRect(const Rect& r, Color color)
|
||||
{
|
||||
LLOG("TurtleDraw::PutRect " << r << ", color " << color);
|
||||
|
||||
TurtleServer::stat_putrect++;
|
||||
|
||||
Point p = r.TopLeft();
|
||||
if(color == InvertColor()) {
|
||||
TurtleServer::Put8(TurtleServer::INVERTRECT);
|
||||
TurtleServer::Put(r);
|
||||
}
|
||||
else {
|
||||
Size sz = r.GetSize();
|
||||
Point dp = p - pos;
|
||||
if(abs(dp.x) < 256
|
||||
&& abs(dp.y) < 256
|
||||
&& sz.cx < 256
|
||||
&& sz.cy < 256
|
||||
&& 0) { // FIXME: ?
|
||||
TurtleServer::Put8(dp.x < 0
|
||||
? dp.y < 0 ? TurtleServer::RECTNN : TurtleServer::RECTNP
|
||||
: dp.y < 0 ? TurtleServer::RECTPN : TurtleServer::RECTPP);
|
||||
TurtleServer::Put8(abs(dp.x));
|
||||
TurtleServer::Put8(abs(dp.y));
|
||||
TurtleServer::Put8(sz.cx);
|
||||
TurtleServer::Put8(sz.cy);
|
||||
}
|
||||
else {
|
||||
TurtleServer::Put8(TurtleServer::RECT);
|
||||
TurtleServer::Put(r);
|
||||
}
|
||||
TurtleServer::Put8(color.GetR());
|
||||
TurtleServer::Put8(color.GetG());
|
||||
TurtleServer::Put8(color.GetB());
|
||||
}
|
||||
pos = p;
|
||||
}
|
||||
|
||||
SystemDraw& TurtleServer::BeginDraw()
|
||||
{
|
||||
return sTurtleDraw.sysdraw;
|
||||
}
|
||||
|
||||
void TurtleServer::CommitDraw()
|
||||
{
|
||||
if(recieved_update_serial < update_serial -1) // Falling behind, wait.
|
||||
SyncClient();
|
||||
Flush();
|
||||
}
|
||||
|
||||
void TurtleServer::SetCanvasSize(const Size& sz)
|
||||
{
|
||||
sTurtleDraw.Init(sz);
|
||||
}
|
||||
|
||||
void TurtleServer::ResetImageCache()
|
||||
{
|
||||
sImageCache.Clear();
|
||||
sFreeHandles.Clear();
|
||||
sHandleCount = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1,296 +0,0 @@
|
|||
#include "Turtle.h"
|
||||
|
||||
#define LLOG(x) // RLOG(x)
|
||||
#define LDUMP(x) // RDUMP(x)
|
||||
#define LTIMING(x)
|
||||
|
||||
namespace Upp {
|
||||
|
||||
static bool sQuit; // Ctrl::IsEndSession() would be much better to use had it been implemented.
|
||||
static BiVector<String> sEventQueue;
|
||||
|
||||
Point ReadPoint(CParser& p)
|
||||
{
|
||||
Point pt;
|
||||
pt.x = p.ReadInt();
|
||||
pt.y = p.ReadInt();
|
||||
return pt;
|
||||
}
|
||||
|
||||
bool TurtleServer::ProcessEvent(bool *quit)
|
||||
{
|
||||
if(!IsWaitingEvent())
|
||||
return false;
|
||||
|
||||
while(sEventQueue.GetCount() >= 2
|
||||
&& *sEventQueue[0] == 'M'
|
||||
&& *sEventQueue[1] == 'M')
|
||||
sEventQueue.DropHead(); // MouseMove compression
|
||||
|
||||
String event = sEventQueue[0];
|
||||
sEventQueue.DropHead();
|
||||
|
||||
LLOG("Processing event " << event);
|
||||
|
||||
CParser p(event);
|
||||
|
||||
try
|
||||
{
|
||||
if(p.Id("i"))
|
||||
{
|
||||
ResetImageCache();
|
||||
}
|
||||
else
|
||||
if(p.Id("R"))
|
||||
{
|
||||
Resize(p);
|
||||
}
|
||||
else
|
||||
if(p.Id("M"))
|
||||
{
|
||||
MouseMove(p);
|
||||
}
|
||||
else
|
||||
if(p.Id("W"))
|
||||
{
|
||||
MouseWheel(p);
|
||||
}
|
||||
else
|
||||
if(p.Id("I"))
|
||||
{
|
||||
mousein = true;
|
||||
}
|
||||
else
|
||||
if(p.Id("O"))
|
||||
{
|
||||
mousebuttons = 0;
|
||||
mousein = false;
|
||||
}
|
||||
else
|
||||
if(p.Id("D"))
|
||||
{
|
||||
MouseButton(Ctrl::DOWN, p);
|
||||
}
|
||||
else
|
||||
if(p.Id("U"))
|
||||
{
|
||||
MouseButton(Ctrl::UP, p);
|
||||
}
|
||||
else
|
||||
if(p.Id("K"))
|
||||
{
|
||||
KeyDown(event, p);
|
||||
}
|
||||
else
|
||||
if(p.Id("k"))
|
||||
{
|
||||
KeyUp(event, p);
|
||||
}
|
||||
else
|
||||
if(p.Id("C"))
|
||||
{
|
||||
KeyPress(event, p);
|
||||
}
|
||||
}
|
||||
catch(const CParser::Error& e)
|
||||
{
|
||||
LLOG("ProcessEvent() -> Parser error");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TurtleServer::WaitEvent(int ms)
|
||||
{
|
||||
websocket.Do();
|
||||
SocketWaitEvent we;
|
||||
websocket.AddTo(we);
|
||||
we.Wait(ms);
|
||||
}
|
||||
|
||||
bool TurtleServer::IsWaitingEvent()
|
||||
{
|
||||
websocket.Do();
|
||||
|
||||
String s = websocket.Receive();
|
||||
|
||||
if(websocket.IsClosed()) {
|
||||
Ctrl::EndSession();
|
||||
sQuit = true; // Ugly..
|
||||
return false;
|
||||
}
|
||||
|
||||
if(s.GetCount() == 0)
|
||||
return sEventQueue.GetCount();
|
||||
|
||||
LLOG("Received data " << s);
|
||||
|
||||
StringStream ss(s);
|
||||
while(!ss.IsEof()) {
|
||||
String s = ss.GetLine();
|
||||
CParser p(s);
|
||||
try
|
||||
{
|
||||
if(p.Id("S")) {
|
||||
uint32 l = p.ReadNumber();
|
||||
uint32 h = p.ReadNumber();
|
||||
recieved_update_serial = MAKEQWORD(l, h);
|
||||
stat_client_ms = p.ReadNumber();
|
||||
}
|
||||
else
|
||||
sEventQueue.AddTail(s);
|
||||
}
|
||||
catch(const CParser::Error& e)
|
||||
{
|
||||
LLOG("IsWaitingEvent() -> Parser error.");
|
||||
}
|
||||
}
|
||||
if(recieved_update_serial == serial_0) {
|
||||
serial_0 = 0;
|
||||
stat_roundtrip_ms = msecs() - serial_time0;
|
||||
serial_time0 = Null;
|
||||
}
|
||||
|
||||
if(websocket.IsError())
|
||||
LLOG("ERROR: " << websocket.GetErrorDesc());
|
||||
|
||||
return sEventQueue.GetCount();
|
||||
}
|
||||
|
||||
void TurtleServer::SyncClient()
|
||||
{
|
||||
while(recieved_update_serial < update_serial && !sQuit) {
|
||||
Ctrl::GuiSleep(10);
|
||||
IsWaitingEvent();
|
||||
}
|
||||
}
|
||||
|
||||
void TurtleServer::MouseButton(dword event, CParser& p)
|
||||
{
|
||||
auto MaxDistance = [](Point a, Point b) -> int
|
||||
{
|
||||
return IsNull(a) ? INT_MAX : max(abs(a.x - b.x), abs(a.y - b.y));
|
||||
};
|
||||
|
||||
static int64 sMouseDownTime;
|
||||
static Point sMouseDownPos;
|
||||
|
||||
int bt = p.ReadInt();
|
||||
Point pt = ReadPoint(p);
|
||||
int64 tm = p.ReadInt64();
|
||||
|
||||
dword down = (dword) event == Ctrl::DOWN;
|
||||
dword bt2 = decode(bt, 0, (1 << 0), 2, (1 << 1), (1 << 2));
|
||||
mousebuttons = (mousebuttons & ~bt2) | (-down & bt2); // Toggles button flags.
|
||||
|
||||
if(event == Ctrl::DOWN) {
|
||||
if(MaxDistance(sMouseDownPos, pt) < GUI_DragDistance() && tm - sMouseDownTime < 800) {
|
||||
event = Ctrl::DOUBLE;
|
||||
sMouseDownTime = 0;
|
||||
}
|
||||
else {
|
||||
sMouseDownPos = pt;
|
||||
sMouseDownTime = tm;
|
||||
}
|
||||
}
|
||||
|
||||
ReadModifierKeys(p);
|
||||
Ctrl::DoMouseFB(decode(bt, 0, Ctrl::LEFT, 2, Ctrl::RIGHT, Ctrl::MIDDLE) | event, pt, 0);
|
||||
}
|
||||
|
||||
void TurtleServer::MouseWheel(CParser& p)
|
||||
{
|
||||
double w = p.ReadDouble();
|
||||
Point pt = ReadPoint(p);
|
||||
ReadModifierKeys(p);
|
||||
Ctrl::DoMouseFB(Ctrl::MOUSEWHEEL, pt, w < 0 ? 120 : -120);
|
||||
}
|
||||
|
||||
void TurtleServer::MouseMove(CParser& p)
|
||||
{
|
||||
Point pt = ReadPoint(p);
|
||||
ReadModifierKeys(p);
|
||||
Ctrl::DoMouseFB(Ctrl::MOUSEMOVE, pt, 0);
|
||||
}
|
||||
|
||||
void TurtleServer::KeyDown(const String& event, CParser& p)
|
||||
{
|
||||
int count = 1;
|
||||
int code = p.ReadInt();
|
||||
int which = p.ReadInt();
|
||||
|
||||
for(;;) {
|
||||
if(sEventQueue.GetCount()
|
||||
&& sEventQueue[0] == event) { // Chrome autorepeat
|
||||
sEventQueue.DropHead();
|
||||
count++;
|
||||
}
|
||||
else
|
||||
if(sEventQueue.GetCount() >= 2
|
||||
&& *sEventQueue[0] == 'C'
|
||||
&& sEventQueue[1] == event) { // Firefox autorepeat
|
||||
String h = sEventQueue[0];
|
||||
sEventQueue.DropHead();
|
||||
sEventQueue.DropHead();
|
||||
sEventQueue.AddHead(h);
|
||||
count++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
ReadModifierKeys(p);
|
||||
Ctrl::DoKeyFB(TranslateWebKeyToK(which), count);
|
||||
}
|
||||
|
||||
void TurtleServer::KeyUp(const String& event, CParser& p)
|
||||
{
|
||||
int code = p.ReadInt();
|
||||
int which = p.ReadInt();
|
||||
|
||||
ReadModifierKeys(p);
|
||||
Ctrl::DoKeyFB(TranslateWebKeyToK(which) | K_KEYUP, 1);
|
||||
}
|
||||
|
||||
void TurtleServer::KeyPress(const String& event, CParser& p)
|
||||
{
|
||||
int code = p.ReadInt();
|
||||
int which = p.ReadInt();
|
||||
|
||||
ReadModifierKeys(p);
|
||||
|
||||
while(sEventQueue.GetCount() && sEventQueue[0] == event) // 'K_'s are not there anymore
|
||||
sEventQueue.DropHead();
|
||||
|
||||
if(which && !GetAlt() && !GetCtrl() && findarg(which, 0x09, 0x0D, 0x20) < 0)
|
||||
Ctrl::DoKeyFB(which, 1);
|
||||
}
|
||||
|
||||
void TurtleServer::Resize(CParser& p)
|
||||
{
|
||||
desktopsize = ReadPoint(p);
|
||||
SetCanvasSize(desktopsize);
|
||||
Ctrl::SetDesktopSize(desktopsize);
|
||||
}
|
||||
|
||||
void TurtleServer::SetMouseCursor(const Image& image)
|
||||
{
|
||||
int64 q = image.GetAuxData();
|
||||
if(q) {
|
||||
Put8(STD_CURSORIMAGE);
|
||||
Put8(clamp((int)q, 1, 16));
|
||||
}
|
||||
else {
|
||||
Point pt = image.GetHotSpot();
|
||||
String h;
|
||||
h << "url('data:image/png;base64,"
|
||||
<< Base64Encode(PNGEncoder().SaveString(image))
|
||||
<< "') " << pt.x << ' ' << pt.y << ", default";
|
||||
Put8(SETCURSORIMAGE);
|
||||
Put16(0); // TODO: Cursor cache
|
||||
Put(h);
|
||||
Put8(MOUSECURSOR);
|
||||
Put16(0); // TODO: Cursor cache
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
#include "Turtle.h"
|
||||
|
||||
#define LLOG(x) // LLOG(x)
|
||||
#define LDUMP(x) // RDUMP(x)
|
||||
#define LTIMING(x)
|
||||
|
||||
namespace Upp {
|
||||
String TurtleServer::host = "localhost";
|
||||
int TurtleServer::port = 8888;
|
||||
String TurtleServer::ip = "0.0.0.0";
|
||||
int TurtleServer::connection_limit = 100;
|
||||
bool TurtleServer::debugmode;
|
||||
WebSocket TurtleServer::websocket;
|
||||
int TurtleServer::mainpid;
|
||||
bool TurtleServer::quit;
|
||||
bool TurtleServer::mousein;
|
||||
dword TurtleServer::mousebuttons = 0;
|
||||
dword TurtleServer::modifierkeys = 0;
|
||||
Size TurtleServer::desktopsize = Size(1024, 1024);
|
||||
int64 TurtleServer::update_serial = 0;
|
||||
int64 TurtleServer::recieved_update_serial;
|
||||
int TurtleServer::serial_time0 = Null;
|
||||
int64 TurtleServer::serial_0 = 0;
|
||||
Time TurtleServer::stat_started;
|
||||
int64 TurtleServer::stat_data_send;
|
||||
int TurtleServer::stat_putrect;
|
||||
int TurtleServer::stat_putimage;
|
||||
int TurtleServer::stat_setimage;
|
||||
int64 TurtleServer::stat_setimage_len;
|
||||
int TurtleServer::stat_roundtrip_ms;
|
||||
int TurtleServer::stat_client_ms;
|
||||
|
||||
Event<int, String> TurtleServer::WhenConnect;
|
||||
Event<int> TurtleServer::WhenTerminate;
|
||||
Event<> TurtleServer::WhenDisconnect;
|
||||
|
||||
void RunTurtleGui(TurtleServer& gui, Event<> app_main)
|
||||
{
|
||||
if(TurtleServer::StartSession())
|
||||
RunVirtualGui((VirtualGui &) gui, app_main);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
#include "Turtle.h"
|
||||
|
||||
namespace Upp {
|
||||
|
||||
#define WEBKEY(x) x
|
||||
|
||||
const static VectorMap<dword, dword> sKeyCodeMap = {
|
||||
{ WEBKEY(3), K_BREAK },
|
||||
{ WEBKEY(8), K_BACKSPACE },
|
||||
{ WEBKEY(9), K_TAB },
|
||||
{ WEBKEY(13), K_RETURN },
|
||||
{ WEBKEY(16), K_SHIFT_KEY },
|
||||
{ WEBKEY(17), K_CTRL_KEY },
|
||||
{ WEBKEY(18), K_ALT_KEY },
|
||||
{ WEBKEY(20), K_CAPSLOCK },
|
||||
{ WEBKEY(27), K_ESCAPE },
|
||||
{ WEBKEY(32), K_SPACE },
|
||||
{ WEBKEY(33), K_PAGEUP },
|
||||
{ WEBKEY(34), K_PAGEDOWN },
|
||||
{ WEBKEY(35), K_END },
|
||||
{ WEBKEY(36), K_HOME },
|
||||
{ WEBKEY(37), K_LEFT },
|
||||
{ WEBKEY(38), K_UP },
|
||||
{ WEBKEY(39), K_RIGHT },
|
||||
{ WEBKEY(40), K_DOWN },
|
||||
{ WEBKEY(45), K_INSERT },
|
||||
{ WEBKEY(46), K_DELETE },
|
||||
{ WEBKEY('A'), K_A },
|
||||
{ WEBKEY('B'), K_B },
|
||||
{ WEBKEY('C'), K_C },
|
||||
{ WEBKEY('D'), K_D },
|
||||
{ WEBKEY('E'), K_E },
|
||||
{ WEBKEY('F'), K_F },
|
||||
{ WEBKEY('G'), K_G },
|
||||
{ WEBKEY('H'), K_H },
|
||||
{ WEBKEY('I'), K_I },
|
||||
{ WEBKEY('J'), K_J },
|
||||
{ WEBKEY('K'), K_K },
|
||||
{ WEBKEY('L'), K_L },
|
||||
{ WEBKEY('M'), K_M },
|
||||
{ WEBKEY('N'), K_N },
|
||||
{ WEBKEY('O'), K_O },
|
||||
{ WEBKEY('P'), K_P },
|
||||
{ WEBKEY('Q'), K_Q },
|
||||
{ WEBKEY('R'), K_R },
|
||||
{ WEBKEY('S'), K_S },
|
||||
{ WEBKEY('T'), K_T },
|
||||
{ WEBKEY('U'), K_U },
|
||||
{ WEBKEY('V'), K_V },
|
||||
{ WEBKEY('W'), K_W },
|
||||
{ WEBKEY('X'), K_X },
|
||||
{ WEBKEY('Y'), K_Y },
|
||||
{ WEBKEY('Z'), K_Z },
|
||||
{ WEBKEY('0'), K_0 },
|
||||
{ WEBKEY('1'), K_1 },
|
||||
{ WEBKEY('2'), K_2 },
|
||||
{ WEBKEY('3'), K_3 },
|
||||
{ WEBKEY('4'), K_4 },
|
||||
{ WEBKEY('5'), K_5 },
|
||||
{ WEBKEY('6'), K_6 },
|
||||
{ WEBKEY('7'), K_7 },
|
||||
{ WEBKEY('8'), K_8 },
|
||||
{ WEBKEY('9'), K_9 },
|
||||
{ WEBKEY(96), K_NUMPAD0 },
|
||||
{ WEBKEY(97), K_NUMPAD1 },
|
||||
{ WEBKEY(98), K_NUMPAD2 },
|
||||
{ WEBKEY(99), K_NUMPAD3 },
|
||||
{ WEBKEY(100), K_NUMPAD4 },
|
||||
{ WEBKEY(101), K_NUMPAD5 },
|
||||
{ WEBKEY(102), K_NUMPAD6 },
|
||||
{ WEBKEY(103), K_NUMPAD7 },
|
||||
{ WEBKEY(104), K_NUMPAD8 },
|
||||
{ WEBKEY(105), K_NUMPAD9 },
|
||||
{ WEBKEY(106), K_MULTIPLY },
|
||||
{ WEBKEY(107), K_ADD },
|
||||
{ WEBKEY(108), K_SEPARATOR },
|
||||
{ WEBKEY(109), K_SUBTRACT },
|
||||
{ WEBKEY(110), K_DECIMAL },
|
||||
{ WEBKEY(111), K_DIVIDE },
|
||||
{ WEBKEY(145), K_SCROLL },
|
||||
{ WEBKEY(112), K_F1 },
|
||||
{ WEBKEY(113), K_F2 },
|
||||
{ WEBKEY(114), K_F3 },
|
||||
{ WEBKEY(115), K_F4 },
|
||||
{ WEBKEY(116), K_F5 },
|
||||
{ WEBKEY(117), K_F6 },
|
||||
{ WEBKEY(118), K_F7 },
|
||||
{ WEBKEY(119), K_F8 },
|
||||
{ WEBKEY(120), K_F9 },
|
||||
{ WEBKEY(121), K_F10 },
|
||||
{ WEBKEY(122), K_F11 },
|
||||
{ WEBKEY(123), K_F12 },
|
||||
{ WEBKEY(219), K_CTRL_LBRACKET },
|
||||
{ WEBKEY(221), K_CTRL_RBRACKET },
|
||||
{ WEBKEY(173), K_CTRL_MINUS }, // Firefox specific.
|
||||
{ WEBKEY(189), K_CTRL_MINUS },
|
||||
{ WEBKEY(192), K_CTRL_GRAVE },
|
||||
{ WEBKEY(191), K_CTRL_SLASH },
|
||||
{ WEBKEY(220), K_CTRL_BACKSLASH },
|
||||
{ WEBKEY(188), K_CTRL_COMMA },
|
||||
{ WEBKEY(190), K_CTRL_PERIOD },
|
||||
{ WEBKEY(59) , K_CTRL_SEMICOLON }, // Firefox specific.
|
||||
{ WEBKEY(186), K_CTRL_SEMICOLON },
|
||||
{ WEBKEY(61) , K_CTRL_EQUAL }, // Firefox specific.
|
||||
{ WEBKEY(187), K_CTRL_EQUAL },
|
||||
{ WEBKEY(222), K_CTRL_APOSTROPHE },
|
||||
};
|
||||
|
||||
#undef WEBKEY
|
||||
|
||||
dword TurtleServer::TranslateWebKeyToK(dword key)
|
||||
{
|
||||
int i = sKeyCodeMap.Find(key);
|
||||
|
||||
if(i < 0)
|
||||
return key | K_DELTA;
|
||||
|
||||
key = sKeyCodeMap[i];
|
||||
|
||||
if(key == K_ALT_KEY || key == K_CTRL_KEY || key == K_SHIFT_KEY)
|
||||
return key;
|
||||
|
||||
if(GetAlt()) key |= K_ALT;
|
||||
if(GetCtrl()) key |= K_CTRL;
|
||||
if(GetShift()) key |= K_SHIFT;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
void TurtleServer::ReadModifierKeys(CParser& p)
|
||||
{
|
||||
const char *s = p.GetPtr();
|
||||
if(*s && *s++ == '1') modifierkeys |= KM_SHIFT; else modifierkeys &= ~KM_SHIFT;
|
||||
if(*s && *s++ == '1') modifierkeys |= KM_CTRL; else modifierkeys &= ~KM_CTRL;
|
||||
if(*s && *s++ == '1') modifierkeys |= KM_ALT; else modifierkeys &= ~KM_ALT;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
#include "Turtle.h"
|
||||
#include "Turtle.brc"
|
||||
|
||||
#ifdef PLATFORM_POSIX
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#define LLOG(x) // RLOG(x)
|
||||
#define LDUMP(x) // RDUMP(x)
|
||||
#define LTIMING(x)
|
||||
|
||||
namespace Upp {
|
||||
|
||||
static Vector<int> sChildPids;
|
||||
|
||||
static bool sSendTurtleHtml(TcpSocket& s, const String& host, int port)
|
||||
{
|
||||
HttpHeader h;
|
||||
if(!h.Read(s))
|
||||
return false;
|
||||
LLOG("Sending Turtle HTML");
|
||||
String html = String(turtle_html, turtle_html_length);
|
||||
html.Replace("%%host%%", Format("ws://%s:%d", host, port));
|
||||
return HttpResponse(s, h.scgi, 200, "OK", "text/html", html);
|
||||
}
|
||||
|
||||
static void sUpdateChildList()
|
||||
{
|
||||
#ifdef PLATFORM_POSIX
|
||||
int i = 0;
|
||||
while(i < sChildPids.GetCount()) {
|
||||
if(sChildPids[i] && waitpid(sChildPids[i], 0, WNOHANG | WUNTRACED) > 0) {
|
||||
TurtleServer::WhenTerminate(sChildPids[i]);
|
||||
sChildPids.Remove(i);
|
||||
}
|
||||
else ++i;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void TurtleServer::Broadcast(int signal)
|
||||
{
|
||||
#ifdef PLATFORM_POSIX
|
||||
if(getpid() == mainpid)
|
||||
for(int i = 0; i < sChildPids.GetCount(); i++)
|
||||
kill(sChildPids[i], signal);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool TurtleServer::StartSession()
|
||||
{
|
||||
// TODO: See if we can add secure websocket (wss://) support.
|
||||
|
||||
LLOG("Connect");
|
||||
|
||||
#ifdef PLATFORM_POSIX
|
||||
mainpid = getpid();
|
||||
#endif
|
||||
|
||||
IpAddrInfo ipinfo;
|
||||
ipinfo.Execute(ip, port, IpAddrInfo::FAMILY_IPV4);
|
||||
|
||||
TcpSocket server;
|
||||
|
||||
#ifdef _DEBUG
|
||||
int cnt = 0;
|
||||
while(!server.Listen(ipinfo, port, 5, false, true)) {
|
||||
LLOG("Trying to start listening (other process using the same port?) " << ++cnt);
|
||||
Sleep(1000);
|
||||
}
|
||||
#else
|
||||
if(!server.Listen(ipinfo, port, 5, false, true)) {
|
||||
LLOG("Cannot open server socket for listening!");
|
||||
Exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
LLOG("Starting to listen on " << port << ", pid: " << getpid());
|
||||
|
||||
for(;;) {
|
||||
sUpdateChildList();
|
||||
if(server.IsError())
|
||||
server.ClearError();
|
||||
TcpSocket socket;
|
||||
if(!socket.Accept(server))
|
||||
continue;
|
||||
if(!sSendTurtleHtml(socket, host, port))
|
||||
continue;
|
||||
websocket.NonBlocking();
|
||||
while(!websocket.Accept(server))
|
||||
Sleep(20);
|
||||
LLOG("Websocket connection accepted. IP: " << websocket.GetPeerAddr());
|
||||
#ifdef PLATFORM_POSIX
|
||||
if(sChildPids.GetCount() >= connection_limit)
|
||||
continue;
|
||||
if(debugmode)
|
||||
break;
|
||||
int newpid = fork();
|
||||
if(!newpid)
|
||||
break;
|
||||
else {
|
||||
LLOG("Process forked. Pid: " << newpid);
|
||||
sChildPids.Add(newpid);
|
||||
WhenConnect(newpid, websocket.GetPeerAddr());
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
server.Close();
|
||||
stat_started = GetSysTime();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
#include "Turtle.h"
|
||||
|
||||
#define LLOG(x) // LLOG(x)
|
||||
#define LDUMP(x) // RDUMP(x)
|
||||
#define LTIMING(x)
|
||||
|
||||
namespace Upp {
|
||||
|
||||
static TurtleServer::Stream sTurtleStream;
|
||||
|
||||
TurtleServer::Stream::Stream()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void TurtleServer::Stream::Out(const void *data, dword size)
|
||||
{
|
||||
zlib.Put(data, (int) size);
|
||||
}
|
||||
|
||||
String TurtleServer::Stream::FlushStream()
|
||||
{
|
||||
Flush();
|
||||
zlib.End();
|
||||
String s = zlib.Get();
|
||||
Reset();
|
||||
return s;
|
||||
}
|
||||
|
||||
void TurtleServer::Stream::Reset()
|
||||
{
|
||||
zlib.Clear();
|
||||
zlib.Compress();
|
||||
hasdata = false;
|
||||
}
|
||||
|
||||
void TurtleServer::Put8(int x)
|
||||
{
|
||||
sTurtleStream.hasdata = true;
|
||||
sTurtleStream.Put(x);
|
||||
}
|
||||
|
||||
void TurtleServer::Put16(int x)
|
||||
{
|
||||
Put8(LOBYTE(x));
|
||||
Put8(HIBYTE(x));
|
||||
}
|
||||
|
||||
void TurtleServer::Put32(int x)
|
||||
{
|
||||
Put16(LOWORD(x));
|
||||
Put16(HIWORD(x));
|
||||
}
|
||||
|
||||
void TurtleServer::Put(Point p)
|
||||
{
|
||||
// TODO: Clamp?
|
||||
Put16(p.x);
|
||||
Put16(p.y);
|
||||
}
|
||||
|
||||
void TurtleServer::Put(Size sz)
|
||||
{
|
||||
Put((Point)sz);
|
||||
}
|
||||
|
||||
void TurtleServer::Put(const Rect& r)
|
||||
{
|
||||
Put(r.TopLeft());
|
||||
Put(r.GetSize());
|
||||
}
|
||||
|
||||
void TurtleServer::Put(const String& s)
|
||||
{
|
||||
Put32(s.GetLength());
|
||||
sTurtleStream.hasdata = true;
|
||||
sTurtleStream.Put(s);
|
||||
}
|
||||
|
||||
//void Turtle_PutLink(const String& link) // FIXME
|
||||
//{
|
||||
// TurtleServer::Put8(OPENLINK);
|
||||
// TurtleServer::Put(link);
|
||||
//}
|
||||
|
||||
void TurtleServer::Flush()
|
||||
{
|
||||
if(!sTurtleStream.hasdata || websocket.IsClosed())
|
||||
return;
|
||||
|
||||
websocket.SendBinary(ZCompress(String(DISABLESENDING, 1))); // Do not send events until data transfered and processed
|
||||
int64 x = ++update_serial;
|
||||
if(IsNull(serial_time0)) {
|
||||
serial_time0 = msecs();
|
||||
serial_0 = update_serial;
|
||||
}
|
||||
Put8(UPDATESERIAL);
|
||||
Put32(LODWORD(x));
|
||||
Put32(HIDWORD(x));
|
||||
String s = sTurtleStream.FlushStream();
|
||||
stat_data_send += s.GetCount();
|
||||
LLOG("Sending " << s.GetLength());
|
||||
websocket.SendBinary(s);
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
BINARY(turtle_html, "Turtle.html")
|
||||
|
|
@ -1,169 +0,0 @@
|
|||
#ifndef _VirtualGui_Turtle_h
|
||||
#define _VirtualGui_Turtle_h
|
||||
|
||||
#include <CtrlLib/CtrlLib.h>
|
||||
|
||||
namespace Upp {
|
||||
|
||||
class TurtleServer : public VirtualGui {
|
||||
public:
|
||||
TurtleServer() {}
|
||||
TurtleServer(const String& host, int port) { Host(host).Port(port); }
|
||||
TurtleServer(const String& ip, String& host, int port) { Bind(ip).Host(host).Port(port); }
|
||||
|
||||
TurtleServer& Bind(const String& addr) { TurtleServer::ip = addr; return *this; }
|
||||
TurtleServer& Host(const String& host) { TurtleServer::host = host; return *this; }
|
||||
TurtleServer& Port(int port) { TurtleServer::port = port; return *this; }
|
||||
TurtleServer& MaxConnections(int limit) { TurtleServer::connection_limit = max(1, limit); return *this; }
|
||||
|
||||
static void DebugMode(bool b = true) { TurtleServer::debugmode = b; }
|
||||
|
||||
static Event<int, String> WhenConnect;
|
||||
static Event<int> WhenTerminate;
|
||||
static Event<> WhenDisconnect;
|
||||
|
||||
private:
|
||||
virtual dword GetOptions() { return GUI_SETMOUSECURSOR; }
|
||||
virtual Size GetSize() { return desktopsize; }
|
||||
virtual dword GetMouseButtons() { return mousebuttons; }
|
||||
virtual dword GetModKeys() { return modifierkeys; }
|
||||
virtual bool IsMouseIn() { return mousein; }
|
||||
virtual bool ProcessEvent(bool *quit);
|
||||
virtual void WaitEvent(int ms);
|
||||
virtual bool IsWaitingEvent();
|
||||
virtual void SetMouseCursor(const Image& image);
|
||||
virtual void SetCaret(const Rect& caret) {}
|
||||
virtual SystemDraw& BeginDraw();
|
||||
virtual void CommitDraw();
|
||||
virtual void WakeUpGuiThread() {}
|
||||
virtual void Quit() { WhenDisconnect(); }
|
||||
|
||||
private:
|
||||
void MouseButton(dword event, CParser& p);
|
||||
void MouseWheel(CParser& p);
|
||||
void MouseMove(CParser& p);
|
||||
void KeyDown(const String& event, CParser& p);
|
||||
void KeyUp(const String& event, CParser& p);
|
||||
void KeyPress(const String& event, CParser& p);
|
||||
void Resize(CParser& p);
|
||||
|
||||
void ReadModifierKeys(CParser& p);
|
||||
dword TranslateWebKeyToK(dword key);
|
||||
|
||||
static void Broadcast(int signal);
|
||||
void SyncClient();
|
||||
|
||||
public:
|
||||
struct Stream : OutStream
|
||||
{
|
||||
Stream();
|
||||
void Out(const void *data, dword size) final;
|
||||
String FlushStream();
|
||||
void Reset();
|
||||
Zlib zlib;
|
||||
bool hasdata;
|
||||
};
|
||||
|
||||
struct ImageSysData
|
||||
{
|
||||
ImageSysData();
|
||||
~ImageSysData();
|
||||
void Init(const Image& img);
|
||||
Image image;
|
||||
int handle;
|
||||
};
|
||||
|
||||
struct Draw : SDraw
|
||||
{
|
||||
Draw();
|
||||
void Init(const Size& sz);
|
||||
void PutImage(Point p, const Image& img, const Rect& src) final;
|
||||
void PutRect(const Rect& r, Color color) final;
|
||||
Point pos;
|
||||
SystemDraw sysdraw;
|
||||
};
|
||||
|
||||
friend struct TurtleServer::Draw;
|
||||
friend struct TurtleServer::ImageSysData;
|
||||
|
||||
public:
|
||||
enum Commands {
|
||||
RECT = 0,
|
||||
IMAGE = 1,
|
||||
SETIMAGE = 2,
|
||||
INVERTRECT = 3,
|
||||
STD_CURSORIMAGE = 4,
|
||||
SETCURSORIMAGE = 5,
|
||||
MOUSECURSOR = 6,
|
||||
DISABLESENDING = 7,
|
||||
UPDATESERIAL = 8,
|
||||
|
||||
IMAGEPP = 9,
|
||||
IMAGENP = 10,
|
||||
IMAGEPN = 11,
|
||||
IMAGENN = 12,
|
||||
|
||||
RECTPP = 13,
|
||||
RECTNP = 14,
|
||||
RECTPN = 15,
|
||||
RECTNN = 16,
|
||||
|
||||
SETCARET = 17,
|
||||
|
||||
HORZDRAGLINE = 18,
|
||||
VERTDRAGLINE = 19,
|
||||
|
||||
OPENLINK = 20,
|
||||
};
|
||||
|
||||
private:
|
||||
static void Put8(int x);
|
||||
static void Put16(int x);
|
||||
static void Put32(int x);
|
||||
static void Put(Point p);
|
||||
static void Put(Size sz);
|
||||
static void Put(const Rect& r);
|
||||
static void Put(const String& s);
|
||||
static void Flush();
|
||||
|
||||
static void SetCanvasSize(const Size& sz);
|
||||
static void ResetImageCache();
|
||||
|
||||
private:
|
||||
static WebSocket websocket;
|
||||
static dword mousebuttons;
|
||||
static dword modifierkeys;
|
||||
static Size desktopsize;
|
||||
static int mainpid;
|
||||
static int64 update_serial;
|
||||
static int64 recieved_update_serial;
|
||||
static int64 serial_0;
|
||||
static int serial_time0;
|
||||
static bool quit;
|
||||
static String host;
|
||||
static int port;
|
||||
static String ip;
|
||||
static int connection_limit;
|
||||
static bool debugmode;
|
||||
static bool mousein;
|
||||
|
||||
public:
|
||||
// Statistics.
|
||||
static Time stat_started;
|
||||
static int64 stat_data_send;
|
||||
static int stat_putrect;
|
||||
static int stat_putimage;
|
||||
static int stat_setimage;
|
||||
static int64 stat_setimage_len;
|
||||
static int stat_roundtrip_ms;
|
||||
static int stat_client_ms;
|
||||
|
||||
private:
|
||||
static bool StartSession();
|
||||
friend void RunTurtleGui(TurtleServer&, Event<>);
|
||||
};
|
||||
|
||||
void RunTurtleGui(TurtleServer& gui, Event<> app_main);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,587 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<style>
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0px;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<canvas id="myCanvas" width="1000" height="1000" style="border:0px" tabindex="1" oncontextmenu="return false;">
|
||||
Your browser does not support the HTML5 canvas tag.
|
||||
</canvas>
|
||||
|
||||
<script>
|
||||
|
||||
// zlib.js for inflate
|
||||
/** @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License */(function() {'use strict';function m(b){throw b;}var n=void 0,r=this;function s(b,d){var a=b.split("."),c=r;!(a[0]in c)&&c.execScript&&c.execScript("var "+a[0]);for(var f;a.length&&(f=a.shift());)!a.length&&d!==n?c[f]=d:c=c[f]?c[f]:c[f]={}};var u="undefined"!==typeof Uint8Array&&"undefined"!==typeof Uint16Array&&"undefined"!==typeof Uint32Array;function v(b){var d=b.length,a=0,c=Number.POSITIVE_INFINITY,f,e,g,h,k,l,q,p,t;for(p=0;p<d;++p)b[p]>a&&(a=b[p]),b[p]<c&&(c=b[p]);f=1<<a;e=new (u?Uint32Array:Array)(f);g=1;h=0;for(k=2;g<=a;){for(p=0;p<d;++p)if(b[p]===g){l=0;q=h;for(t=0;t<g;++t)l=l<<1|q&1,q>>=1;for(t=l;t<f;t+=k)e[t]=g<<16|p;++h}++g;h<<=1;k<<=1}return[e,a,c]};function w(b,d){this.g=[];this.h=32768;this.d=this.f=this.a=this.l=0;this.input=u?new Uint8Array(b):b;this.m=!1;this.i=x;this.r=!1;if(d||!(d={}))d.index&&(this.a=d.index),d.bufferSize&&(this.h=d.bufferSize),d.bufferType&&(this.i=d.bufferType),d.resize&&(this.r=d.resize);switch(this.i){case y:this.b=32768;this.c=new (u?Uint8Array:Array)(32768+this.h+258);break;case x:this.b=0;this.c=new (u?Uint8Array:Array)(this.h);this.e=this.z;this.n=this.v;this.j=this.w;break;default:m(Error("invalid inflate mode"))}}
|
||||
var y=0,x=1,z={t:y,s:x};
|
||||
w.prototype.k=function(){for(;!this.m;){var b=A(this,3);b&1&&(this.m=!0);b>>>=1;switch(b){case 0:var d=this.input,a=this.a,c=this.c,f=this.b,e=n,g=n,h=n,k=c.length,l=n;this.d=this.f=0;e=d[a++];e===n&&m(Error("invalid uncompressed block header: LEN (first byte)"));g=e;e=d[a++];e===n&&m(Error("invalid uncompressed block header: LEN (second byte)"));g|=e<<8;e=d[a++];e===n&&m(Error("invalid uncompressed block header: NLEN (first byte)"));h=e;e=d[a++];e===n&&m(Error("invalid uncompressed block header: NLEN (second byte)"));h|=
|
||||
e<<8;g===~h&&m(Error("invalid uncompressed block header: length verify"));a+g>d.length&&m(Error("input buffer is broken"));switch(this.i){case y:for(;f+g>c.length;){l=k-f;g-=l;if(u)c.set(d.subarray(a,a+l),f),f+=l,a+=l;else for(;l--;)c[f++]=d[a++];this.b=f;c=this.e();f=this.b}break;case x:for(;f+g>c.length;)c=this.e({p:2});break;default:m(Error("invalid inflate mode"))}if(u)c.set(d.subarray(a,a+g),f),f+=g,a+=g;else for(;g--;)c[f++]=d[a++];this.a=a;this.b=f;this.c=c;break;case 1:this.j(B,C);break;case 2:aa(this);
|
||||
break;default:m(Error("unknown BTYPE: "+b))}}return this.n()};
|
||||
var D=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],E=u?new Uint16Array(D):D,F=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,258,258],G=u?new Uint16Array(F):F,H=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0],I=u?new Uint8Array(H):H,J=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],K=u?new Uint16Array(J):J,L=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,
|
||||
13],M=u?new Uint8Array(L):L,N=new (u?Uint8Array:Array)(288),O,P;O=0;for(P=N.length;O<P;++O)N[O]=143>=O?8:255>=O?9:279>=O?7:8;var B=v(N),Q=new (u?Uint8Array:Array)(30),R,S;R=0;for(S=Q.length;R<S;++R)Q[R]=5;var C=v(Q);function A(b,d){for(var a=b.f,c=b.d,f=b.input,e=b.a,g;c<d;)g=f[e++],g===n&&m(Error("input buffer is broken")),a|=g<<c,c+=8;g=a&(1<<d)-1;b.f=a>>>d;b.d=c-d;b.a=e;return g}
|
||||
function T(b,d){for(var a=b.f,c=b.d,f=b.input,e=b.a,g=d[0],h=d[1],k,l,q;c<h;){k=f[e++];if(k===n)break;a|=k<<c;c+=8}l=g[a&(1<<h)-1];q=l>>>16;b.f=a>>q;b.d=c-q;b.a=e;return l&65535}
|
||||
function aa(b){function d(a,b,c){var d,e,f,g;for(g=0;g<a;)switch(d=T(this,b),d){case 16:for(f=3+A(this,2);f--;)c[g++]=e;break;case 17:for(f=3+A(this,3);f--;)c[g++]=0;e=0;break;case 18:for(f=11+A(this,7);f--;)c[g++]=0;e=0;break;default:e=c[g++]=d}return c}var a=A(b,5)+257,c=A(b,5)+1,f=A(b,4)+4,e=new (u?Uint8Array:Array)(E.length),g,h,k,l;for(l=0;l<f;++l)e[E[l]]=A(b,3);g=v(e);h=new (u?Uint8Array:Array)(a);k=new (u?Uint8Array:Array)(c);b.j(v(d.call(b,a,g,h)),v(d.call(b,c,g,k)))}
|
||||
w.prototype.j=function(b,d){var a=this.c,c=this.b;this.o=b;for(var f=a.length-258,e,g,h,k;256!==(e=T(this,b));)if(256>e)c>=f&&(this.b=c,a=this.e(),c=this.b),a[c++]=e;else{g=e-257;k=G[g];0<I[g]&&(k+=A(this,I[g]));e=T(this,d);h=K[e];0<M[e]&&(h+=A(this,M[e]));c>=f&&(this.b=c,a=this.e(),c=this.b);for(;k--;)a[c]=a[c++-h]}for(;8<=this.d;)this.d-=8,this.a--;this.b=c};
|
||||
w.prototype.w=function(b,d){var a=this.c,c=this.b;this.o=b;for(var f=a.length,e,g,h,k;256!==(e=T(this,b));)if(256>e)c>=f&&(a=this.e(),f=a.length),a[c++]=e;else{g=e-257;k=G[g];0<I[g]&&(k+=A(this,I[g]));e=T(this,d);h=K[e];0<M[e]&&(h+=A(this,M[e]));c+k>f&&(a=this.e(),f=a.length);for(;k--;)a[c]=a[c++-h]}for(;8<=this.d;)this.d-=8,this.a--;this.b=c};
|
||||
w.prototype.e=function(){var b=new (u?Uint8Array:Array)(this.b-32768),d=this.b-32768,a,c,f=this.c;if(u)b.set(f.subarray(32768,b.length));else{a=0;for(c=b.length;a<c;++a)b[a]=f[a+32768]}this.g.push(b);this.l+=b.length;if(u)f.set(f.subarray(d,d+32768));else for(a=0;32768>a;++a)f[a]=f[d+a];this.b=32768;return f};
|
||||
w.prototype.z=function(b){var d,a=this.input.length/this.a+1|0,c,f,e,g=this.input,h=this.c;b&&("number"===typeof b.p&&(a=b.p),"number"===typeof b.u&&(a+=b.u));2>a?(c=(g.length-this.a)/this.o[2],e=258*(c/2)|0,f=e<h.length?h.length+e:h.length<<1):f=h.length*a;u?(d=new Uint8Array(f),d.set(h)):d=h;return this.c=d};
|
||||
w.prototype.n=function(){var b=0,d=this.c,a=this.g,c,f=new (u?Uint8Array:Array)(this.l+(this.b-32768)),e,g,h,k;if(0===a.length)return u?this.c.subarray(32768,this.b):this.c.slice(32768,this.b);e=0;for(g=a.length;e<g;++e){c=a[e];h=0;for(k=c.length;h<k;++h)f[b++]=c[h]}e=32768;for(g=this.b;e<g;++e)f[b++]=d[e];this.g=[];return this.buffer=f};
|
||||
w.prototype.v=function(){var b,d=this.b;u?this.r?(b=new Uint8Array(d),b.set(this.c.subarray(0,d))):b=this.c.subarray(0,d):(this.c.length>d&&(this.c.length=d),b=this.c);return this.buffer=b};function U(b,d){var a,c;this.input=b;this.a=0;if(d||!(d={}))d.index&&(this.a=d.index),d.verify&&(this.A=d.verify);a=b[this.a++];c=b[this.a++];switch(a&15){case V:this.method=V;break;default:m(Error("unsupported compression method"))}0!==((a<<8)+c)%31&&m(Error("invalid fcheck flag:"+((a<<8)+c)%31));c&32&&m(Error("fdict flag is not supported"));this.q=new w(b,{index:this.a,bufferSize:d.bufferSize,bufferType:d.bufferType,resize:d.resize})}
|
||||
U.prototype.k=function(){var b=this.input,d,a;d=this.q.k();this.a=this.q.a;if(this.A){a=(b[this.a++]<<24|b[this.a++]<<16|b[this.a++]<<8|b[this.a++])>>>0;var c=d;if("string"===typeof c){var f=c.split(""),e,g;e=0;for(g=f.length;e<g;e++)f[e]=(f[e].charCodeAt(0)&255)>>>0;c=f}for(var h=1,k=0,l=c.length,q,p=0;0<l;){q=1024<l?1024:l;l-=q;do h+=c[p++],k+=h;while(--q);h%=65521;k%=65521}a!==(k<<16|h)>>>0&&m(Error("invalid adler-32 checksum"))}return d};var V=8;s("Zlib.Inflate",U);s("Zlib.Inflate.prototype.decompress",U.prototype.k);var W={ADAPTIVE:z.s,BLOCK:z.t},X,Y,Z,$;if(Object.keys)X=Object.keys(W);else for(Y in X=[],Z=0,W)X[Z++]=Y;Z=0;for($=X.length;Z<$;++Z)Y=X[Z],s("Zlib.Inflate.BufferType."+Y,W[Y]);}).call(this);
|
||||
|
||||
function Log(msg)
|
||||
{
|
||||
if (window.console && console.log)
|
||||
console.log(msg); //for firebug
|
||||
}
|
||||
|
||||
var lastCommand;
|
||||
|
||||
function Char(p, ch)
|
||||
{
|
||||
if(p.pos < p.data.length && (255 & p.data[p.pos]) == ch) {
|
||||
lastCommand = ch;
|
||||
p.pos++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function Get8(p)
|
||||
{
|
||||
return p.pos < p.data.length ? (255 & p.data[p.pos++]) : 0;
|
||||
}
|
||||
|
||||
function Get16(p)
|
||||
{
|
||||
var l = Get8(p);
|
||||
var h = Get8(p);
|
||||
return (h << 8) | l;
|
||||
}
|
||||
|
||||
function Get32(p)
|
||||
{
|
||||
var l = Get16(p);
|
||||
var h = Get16(p);
|
||||
return (h << 16) | l;
|
||||
}
|
||||
|
||||
var lastLen; // TODO: remove
|
||||
var lastString;
|
||||
|
||||
function GetString(p)
|
||||
{
|
||||
var n = Get32(p);
|
||||
lastLen = n;
|
||||
var s = "";
|
||||
for(var i = 0; i < n; i++)
|
||||
s += String.fromCharCode(Get8(p));
|
||||
lastString = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
var SendingEnabled = true;
|
||||
|
||||
var posx, posy;
|
||||
var ctx;
|
||||
|
||||
var caretx = 0, carety = 0, caretcx = 0, caretcy = 0, caretshown = false;
|
||||
var caretTimerID;
|
||||
|
||||
function InvertRect(x, y, cx, cy)
|
||||
{
|
||||
if(cx <= 0 || cy <= 0)
|
||||
return;
|
||||
var imageData = ctx.getImageData(x, y, cx, cy);
|
||||
var data = imageData.data;
|
||||
for(var i = 0; i < data.length; i += 4) {
|
||||
data[i] = 255 - data[i];
|
||||
data[i + 1] = 255 - data[i + 1];
|
||||
data[i + 2] = 255 - data[i + 2];
|
||||
}
|
||||
ctx.putImageData(imageData, x, y);
|
||||
}
|
||||
|
||||
function DrawDragLine(x, y, cx, cy, anim)
|
||||
{
|
||||
if(cx <= 0 || cy <= 0)
|
||||
return;
|
||||
var imageData = ctx.getImageData(x, y, cx, cy);
|
||||
var data = imageData.data;
|
||||
for(var i = 0; i < data.length; i += 4) {
|
||||
if(((i / 4 + anim) / 4) & 1) {
|
||||
data[i] = 255 - data[i];
|
||||
data[i + 1] = 255 - data[i + 1];
|
||||
data[i + 2] = 255 - data[i + 2];
|
||||
}
|
||||
}
|
||||
ctx.putImageData(imageData, x, y);
|
||||
}
|
||||
|
||||
function InvertCaret()
|
||||
{
|
||||
// Log("InvertCaret " + caretx + " " + carety + " " + caretcx + " " + caretcy);
|
||||
if(caretcx >= 0 || caretcy >= 0)
|
||||
InvertRect(caretx, carety, caretcx, caretcy);
|
||||
}
|
||||
|
||||
function ClearCaretTimer()
|
||||
{
|
||||
if(caretTimerID != undefined)
|
||||
clearTimeout(caretTimerID);
|
||||
}
|
||||
|
||||
function CaretTimer()
|
||||
{
|
||||
ClearCaretTimer();
|
||||
caretTimerID = setTimeout(DoCaret, 500);
|
||||
}
|
||||
|
||||
function DoCaret()
|
||||
{
|
||||
if(caretcx || caretcy) {
|
||||
InvertCaret();
|
||||
caretshown = !caretshown;
|
||||
CaretTimer();
|
||||
}
|
||||
}
|
||||
|
||||
function InvertShownCaret()
|
||||
{
|
||||
if(caretshown)
|
||||
InvertCaret();
|
||||
}
|
||||
|
||||
function HideCaret(x, y, cx, cy)
|
||||
{
|
||||
return;
|
||||
if(caretHidden)
|
||||
return;
|
||||
if(Math.max(x, caretx) < Math.min(x + cx, caretx + caretcx) &&
|
||||
Math.max(y, carety) < Math.min(y + cy, carety + caretcy)) {
|
||||
// Log("Hiding caret");
|
||||
InvertShownCaret();
|
||||
caretHidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
var lastR, lastG, lastB;
|
||||
|
||||
function SetFillColor(r, g, b)
|
||||
{
|
||||
if(r == lastR && g == lastG && b == lastB)
|
||||
return;
|
||||
lastR = r;
|
||||
lastG = g;
|
||||
lastB = b;
|
||||
var c = "rgb(" + r + "," + g + "," + b + ")";
|
||||
ctx.fillStyle = c;
|
||||
}
|
||||
|
||||
function ProcessDraw(s)
|
||||
{
|
||||
// console.clear();
|
||||
|
||||
// Log("Painting started " + s.length);
|
||||
|
||||
// window.document.title = "Processing draw";
|
||||
|
||||
var time0 = (new Date).getTime();
|
||||
|
||||
var p = new Object;
|
||||
p.data = s;
|
||||
p.pos = 0;
|
||||
|
||||
var canvas = document.getElementById("myCanvas");
|
||||
|
||||
ctx = canvas.getContext("2d");
|
||||
|
||||
SendingEnabled = true;
|
||||
|
||||
posx = 0;
|
||||
posy = 0;
|
||||
|
||||
InvertShownCaret();
|
||||
|
||||
while(p.pos < p.data.length) {
|
||||
// Log(p.pos + ": " + p.data[p.pos]);
|
||||
if(Char(p, 0)) { // RECT
|
||||
var x = Get16(p);
|
||||
var y = Get16(p);
|
||||
var cx = Get16(p);
|
||||
var cy = Get16(p);
|
||||
var r = Get8(p);
|
||||
var g = Get8(p);
|
||||
var b = Get8(p);
|
||||
SetFillColor(r, g, b);
|
||||
// Log("color: " + c);
|
||||
HideCaret(x, y, cx, cy);
|
||||
ctx.fillRect(x, y, cx, cy);
|
||||
posx = x;
|
||||
posy = y;
|
||||
}
|
||||
else
|
||||
if(Char(p, 3)) { // INVERTRECT
|
||||
var x = Get16(p);
|
||||
var y = Get16(p);
|
||||
var cx = Get16(p);
|
||||
var cy = Get16(p);
|
||||
// Log("irect: " + x + ", " + y + ", " + cx + ", " + cy);
|
||||
HideCaret(x, y, cx, cy);
|
||||
InvertRect(x, y, cx, cy);
|
||||
posx = x;
|
||||
posy = y;
|
||||
}
|
||||
else
|
||||
if(Char(p, 2)) { // SETIMAGE
|
||||
var r = Get16(p);
|
||||
var cx = Get16(p);
|
||||
var cy = Get16(p);
|
||||
var n = cx * cy * 4;
|
||||
var imgData = ctx.createImageData(cx, cy);
|
||||
for(i = 0; i < n; i++)
|
||||
imgData.data[i] = Get8(p);
|
||||
var img = document.createElement('canvas');
|
||||
img.width = cx;
|
||||
img.height = cy;
|
||||
img.getContext("2d").putImageData(imgData, 0, 0);
|
||||
window.img_cache[r] = img;
|
||||
// Log("Set image: " + r);
|
||||
if(img == undefined) // TODO: Remove after debugging this
|
||||
alert("Undefined in set: " + n);
|
||||
if(window.img_cache[r] == undefined)
|
||||
alert("Undefined 2: " + n);
|
||||
}
|
||||
else
|
||||
if(Char(p, 1)) { // IMAGE
|
||||
var n = Get16(p);
|
||||
|
||||
var px = Get16(p);
|
||||
var py = Get16(p);
|
||||
var x = Get16(p);
|
||||
var y = Get16(p);
|
||||
var cx = Get16(p);
|
||||
var cy = Get16(p);
|
||||
|
||||
// Log("Draw image: " + n);
|
||||
if(window.img_cache[n] == undefined)
|
||||
alert("Undefined image: " + n);
|
||||
HideCaret(x, y, cx, cy);
|
||||
ctx.drawImage(window.img_cache[n], x, y, cx, cy, px, py, cx, cy);
|
||||
|
||||
posx = px;
|
||||
posy = py;
|
||||
}
|
||||
else
|
||||
if(Char(p, 4)) { // STD_CURSORIMAGE
|
||||
canvas.style.cursor = [
|
||||
"default", // should not happen
|
||||
"default", // Arrow
|
||||
"wait", // Wait
|
||||
"text", // IBeam
|
||||
"not-allowed", // No
|
||||
"move", // SizeAll
|
||||
"ew-resize", // SizeHorz
|
||||
"ns-resize", // SizeVert
|
||||
"nw-resize", // SizeTopLeft
|
||||
"n-resize", // SizeTop
|
||||
"ne-resize", // SizeTopRight
|
||||
"w-resize", // SizeLeft
|
||||
"e-resize", // SizeRight
|
||||
"sw-resize", // SizeBottomLeft
|
||||
"s-resize", // SizeBottom
|
||||
"se-resize", // SizeBottomRight
|
||||
"pointer" // Hand
|
||||
][Get8(p)];
|
||||
}
|
||||
else
|
||||
if(Char(p, 5)) { // SETCURSORIMAGE
|
||||
i = Get16(p);
|
||||
window.cursor_cache[i] = GetString(p);
|
||||
}
|
||||
else
|
||||
if(Char(p, 6)) { // CURSORIMAGE
|
||||
i = Get16(p);
|
||||
canvas.style.cursor = window.cursor_cache[i];
|
||||
// Log(cursor_cache[i]);
|
||||
}
|
||||
else
|
||||
if(Char(p, 7)) { // DISABLESENDING
|
||||
SendingEnabled = true;
|
||||
}
|
||||
else
|
||||
if(Char(p, 8)) { // UPDATESERIAL
|
||||
update_serial_l = Get32(p);
|
||||
update_serial_h = Get32(p);
|
||||
ws.send("S " + update_serial_l + " " + update_serial_h + " " + ((new Date).getTime() - time0) + "\n");
|
||||
}
|
||||
else
|
||||
if(Char(p, 9)) // IMAGEPP
|
||||
SImage(p, 1, 1);
|
||||
else
|
||||
if(Char(p, 10)) // IMAGENP
|
||||
SImage(p, -1, 1);
|
||||
else
|
||||
if(Char(p, 11)) // IMAGEPN
|
||||
SImage(p, 1, -1);
|
||||
else
|
||||
if(Char(p, 12)) // IMAGENN
|
||||
SImage(p, -1, -1);
|
||||
else
|
||||
if(Char(p, 13)) // RECTPP
|
||||
SRect(p, 1, 1);
|
||||
else
|
||||
if(Char(p, 14)) // RECTNP
|
||||
SRect(p, -1, 1);
|
||||
else
|
||||
if(Char(p, 15)) // RECTPN
|
||||
SRect(p, 1, -1);
|
||||
else
|
||||
if(Char(p, 16)) // RECTNN
|
||||
SRect(p, -1, -1);
|
||||
else
|
||||
if(Char(p, 17)) { // SETCARET
|
||||
caretx = Get16(p);
|
||||
carety = Get16(p);
|
||||
caretcx = Get16(p);
|
||||
caretcy = Get16(p);
|
||||
caretshown = true;
|
||||
CaretTimer();
|
||||
// Log("SET CARET " + caretx + " " + carety + " " + caretcx + " " + caretcy);
|
||||
}
|
||||
else
|
||||
if(Char(p, 18)) { // HORZDRAGLINE,
|
||||
var x = Get16(p);
|
||||
var y = Get16(p);
|
||||
var l = Get16(p);
|
||||
var anim = Get16(p);
|
||||
DrawDragLine(x, y, l, 1, anim);
|
||||
}
|
||||
else
|
||||
if(Char(p, 19)) { // VERTDRAGLINE,
|
||||
var x = Get16(p);
|
||||
var y = Get16(p);
|
||||
var l = Get16(p);
|
||||
var anim = Get16(p);
|
||||
DrawDragLine(x, y, 1, l, anim);
|
||||
}
|
||||
else
|
||||
if(Char(p, 20)) { // OPENLINK,
|
||||
var s = GetString(p);
|
||||
window.open(s);
|
||||
}
|
||||
else {
|
||||
alert("Unrecognized code: " + p.data[p.pos] + " after " + lastCommand + " lastLen " + lastLen + " lastString " + lastString);
|
||||
break;
|
||||
}
|
||||
}
|
||||
InvertShownCaret();
|
||||
// window.document.title = "OK";
|
||||
if(SendingEnabled && event_queue.length)
|
||||
ScheduleSend();
|
||||
// Log("Painting finished");
|
||||
}
|
||||
|
||||
function SImage(p, sx, sy)
|
||||
{
|
||||
var px = posx + sx * Get8(p);
|
||||
var py = posy + sy * Get8(p);
|
||||
var n = Get16(p);
|
||||
// Log("Draw opt image: " + px + " " + py + " " + n);
|
||||
var img = window.img_cache[n];
|
||||
if(img == undefined)
|
||||
alert("Undefined image: " + n);
|
||||
HideCaret(px, py, img.width, img.height);
|
||||
ctx.drawImage(window.img_cache[n], px, py);
|
||||
posx = px;
|
||||
posy = py;
|
||||
}
|
||||
|
||||
function SRect(p, sx, sy)
|
||||
{
|
||||
var px = posx + sx * Get8(p);
|
||||
var py = posy + sy * Get8(p);
|
||||
var cx = Get8(p);
|
||||
var cy = Get8(p);
|
||||
var r = Get8(p);
|
||||
var g = Get8(p);
|
||||
var b = Get8(p);
|
||||
// Log("rect: " + x + ", " + y + ", " + cx + ", " + cy);
|
||||
SetFillColor(r, g, b);
|
||||
// Log("color: " + c);
|
||||
HideCaret(x, y, cx, cy);
|
||||
ctx.fillRect(x, y, cx, cy);
|
||||
posx = px;
|
||||
posy = py;
|
||||
}
|
||||
|
||||
|
||||
window.img_cache = {};
|
||||
window.event_queue = "i\n";
|
||||
window.cursor_cache = {};
|
||||
window.update_serial_l = 0;
|
||||
window.update_serial_h = 0;
|
||||
|
||||
var canvas = document.getElementById("myCanvas");
|
||||
|
||||
function key_flags(event)
|
||||
{
|
||||
return " " + 1*event.shiftKey + 1*event.ctrlKey + 1*event.altKey + "\n";
|
||||
}
|
||||
|
||||
function mouse_event(event)
|
||||
{
|
||||
return " " + event.clientX + " " + event.clientY + " " + (new Date).getTime() + key_flags(event);
|
||||
}
|
||||
|
||||
canvas.onmousemove = function(event)
|
||||
{
|
||||
event_queue += "M" + mouse_event(event);
|
||||
ScheduleSend();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
canvas.onmousedown = function(event)
|
||||
{
|
||||
event_queue += "D " + event.button + mouse_event(event);
|
||||
ScheduleSend();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
canvas.onmouseout = function(event)
|
||||
{
|
||||
event_queue += "O\n";
|
||||
ScheduleSend();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
canvas.onmouseover = function(event)
|
||||
{
|
||||
event_queue += "I\n";
|
||||
ScheduleSend();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
canvas.onmouseup = function(event)
|
||||
{
|
||||
event_queue += "U " + event.button + mouse_event(event);
|
||||
ScheduleSend();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
function MouseWheel(event)
|
||||
{
|
||||
event_queue += "W " + event.deltaY + mouse_event(event);
|
||||
}
|
||||
|
||||
canvas.addEventListener("wheel", MouseWheel); // IE9 needs addEventListener...
|
||||
|
||||
document.onkeydown = function(event)
|
||||
{
|
||||
event_queue += "K " + event.keyCode + " " + event.which + key_flags(event);
|
||||
ScheduleSend();
|
||||
if(event.ctrlKey || event.altKey || event.keyCode == 8 || event.keyCode == 9) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
document.onkeypress = function(event)
|
||||
{
|
||||
event_queue += "C " + event.keyCode + " " + event.which + key_flags(event);
|
||||
event.preventDefault();
|
||||
ScheduleSend();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
document.onkeyup = function(event)
|
||||
{
|
||||
event_queue += "k " + event.keyCode + " " + event.which + key_flags(event);
|
||||
event.preventDefault();
|
||||
ScheduleSend();
|
||||
if(event.ctrlKey || event.altKey) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function ResizeCanvas()
|
||||
{
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
event_queue += "R " + canvas.width + ' ' + canvas.height + "\n";
|
||||
ScheduleSend();
|
||||
}
|
||||
|
||||
window.onresize = ResizeCanvas;
|
||||
|
||||
var timerID;
|
||||
|
||||
function ConnectWebsocket()
|
||||
{
|
||||
return new WebSocket("%%host%%");
|
||||
}
|
||||
|
||||
var ws = ConnectWebsocket();
|
||||
|
||||
ws.binaryType = "arraybuffer";
|
||||
|
||||
function ScheduleSend()
|
||||
{
|
||||
if(SendingEnabled) {
|
||||
if(timerID != undefined)
|
||||
clearTimeout(timerID);
|
||||
timerID = setTimeout(SendEvents, 0);
|
||||
}
|
||||
}
|
||||
|
||||
function SendEvents()
|
||||
{
|
||||
if(SendingEnabled) {
|
||||
if(event_queue.length) {
|
||||
// Log(event_queue);
|
||||
ws.send(event_queue);
|
||||
}
|
||||
else
|
||||
ws.send("PING");
|
||||
event_queue = "";
|
||||
if(timerID != undefined)
|
||||
clearTimeout(timerID);
|
||||
timerID = setTimeout(SendEvents, 20);
|
||||
}
|
||||
}
|
||||
|
||||
ws.onopen = function()
|
||||
{
|
||||
// Log("websocket connection opened");
|
||||
ResizeCanvas();
|
||||
};
|
||||
|
||||
ws.onmessage = function(event)
|
||||
{
|
||||
// Log("onmessage");
|
||||
if(event.data instanceof ArrayBuffer) {
|
||||
var inflate = new Zlib.Inflate(new Uint8Array(event.data));
|
||||
ProcessDraw(inflate.decompress());
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = function(ev)
|
||||
{
|
||||
ClearCaretTimer();
|
||||
alert("Connection closed.");
|
||||
};
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
description "HTML5 thin client backend\377B128,0,255";
|
||||
|
||||
uses
|
||||
CtrlLib,
|
||||
VirtualGui;
|
||||
|
||||
uses(WIN32 & MSC) plugin/DroidFonts;
|
||||
|
||||
file
|
||||
Turtle.h,
|
||||
Init.cpp,
|
||||
Stream.cpp,
|
||||
Server.cpp,
|
||||
Keys.cpp,
|
||||
Event.cpp,
|
||||
Draw.cpp,
|
||||
Info readonly separator,
|
||||
Turtle.brc,
|
||||
Turtle.html,
|
||||
Docs readonly separator,
|
||||
src.tpp,
|
||||
issues;
|
||||
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
Known Issues
|
||||
------------
|
||||
|
||||
1) LaunchWebBrowser function doesn't work, at the moment.
|
||||
|
||||
Hopefully, this is a temporary regression.
|
||||
Maybe it should be handled generally for VirtualGUi-based stuff, using a callback?
|
||||
|
||||
2) uppsrc contains various proprocessor definitons for Turtle.
|
||||
|
||||
They are obsolete now and better be removed. (VIRTUALGUI is sufficient)
|
||||
|
||||
3) The VirtualGui-based Turtle package inherits the problems of the old Turtle package.
|
||||
|
||||
One such major problem is that on windows platform, closing the remote app throws an
|
||||
exception on the server side. This requires further investigation.
|
||||
|
||||
4) KeyboardEvent.KeyCode and KeyboardEvent.which properties are deprecated and are going
|
||||
to be emoved from the standard.
|
||||
See: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode
|
||||
See: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/which
|
||||
Recommended alternatives: KeyboardEvent.key and KeyboardEvent.code.
|
||||
See: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code
|
||||
See: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
topic "TurtleServer";
|
||||
[i448;a25;kKO9;2 $$1,0#37138531426314131252341829483380:class]
|
||||
[l288;2 $$2,2#27521748481378242620020725143825:desc]
|
||||
[0 $$3,0#96390100711032703541132217272105:end]
|
||||
[H6;0 $$4,0#05600065144404261032431302351956:begin]
|
||||
[i448;a25;kKO9;2 $$5,0#37138531426314131252341829483370:item]
|
||||
[l288;a4;*@5;1 $$6,6#70004532496200323422659154056402:requirement]
|
||||
[l288;i1121;b17;O9;~~~.1408;2 $$7,0#10431211400427159095818037425705:param]
|
||||
[i448;b42;O9;2 $$8,8#61672508125594000341940100500538:tparam]
|
||||
[b42;2 $$9,9#13035079074754324216151401829390:normal]
|
||||
[2 $$0,0#00000000000000000000000000000000:Default]
|
||||
[{_}
|
||||
[ {{10000@(113.42.0) [s0;%% [*@7;4 TurtleServer]]}}&]
|
||||
[s2; &]
|
||||
[s1;:Upp`:`:TurtleServer`:`:class: [@(0.0.255)3 class][3 _][*3 TurtleServer][3 _:_][@(0.0.255)3 p
|
||||
ublic][3 _][*@3;3 VirtualGui]&]
|
||||
[s2;#%% This class implements a remote gui virtualization server
|
||||
for U`+`+ applications. By utilizing the modern web technologies
|
||||
such as HTML`-5 canvas and websockets, TurtleServer allows U`+`+
|
||||
gui applications to be accessed remotely via modern web browsers,
|
||||
or, possibly, via specialized client software that understands
|
||||
the Turtle wire protocol.&]
|
||||
[s3;%% &]
|
||||
[ {{10000F(128)G(128)@1 [s0;%% [* Public Method List]]}}&]
|
||||
[s3; &]
|
||||
[s5;:Upp`:`:TurtleServer`:`:Bind`(const Upp`:`:String`&`): [_^Upp`:`:TurtleServer^ Turt
|
||||
leServer][@(0.0.255) `&]_[* Bind]([@(0.0.255) const]_[_^Upp`:`:String^ String][@(0.0.255) `&
|
||||
]_[*@3 addr])&]
|
||||
[s2;%% Sets the server bind address to [%-*@3 addr]. Default is `"0.0.0.0`".
|
||||
Returns `*this for method chaining.&]
|
||||
[s3;%% &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:TurtleServer`:`:Host`(const Upp`:`:String`&`): [_^Upp`:`:TurtleServer^ Turt
|
||||
leServer][@(0.0.255) `&]_[* Host]([@(0.0.255) const]_[_^Upp`:`:String^ String][@(0.0.255) `&
|
||||
]_[*@3 host])&]
|
||||
[s2;%% Sets the host URL to [%-*@3 host]. Default URL is `"localhost`".
|
||||
Returns `*this for method chaining.&]
|
||||
[s3;%% &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:TurtleServer`:`:Port`(int`): [_^Upp`:`:TurtleServer^ TurtleServer][@(0.0.255) `&
|
||||
]_[* Port]([@(0.0.255) int]_[*@3 port])&]
|
||||
[s2;%% Sets the connection port number. Default port number is 8888.
|
||||
Returns `*this for method chaining.&]
|
||||
[s3;%% &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:TurtleServer`:`:MaxConnections`(int`): [_^Upp`:`:TurtleServer^ TurtleServer
|
||||
][@(0.0.255) `&]_[* MaxConnections]([@(0.0.255) int]_[*@3 limit])&]
|
||||
[s2;%% Sets a limit to the maximum number of concurrent client conntections.
|
||||
Default max. connection limit is 100. Returns `*this for method
|
||||
chaining.&]
|
||||
[s3;%% &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:TurtleServer`:`:DebugMode`(bool`): [@(0.0.255) static]
|
||||
[@(0.0.255) void]_[* DebugMode]([@(0.0.255) bool]_[*@3 b]_`=_[@(0.0.255) true])&]
|
||||
[s6;%% POSIX only&]
|
||||
[s2;%% If true, the server will not spawn child processes (no forking).
|
||||
Useful for debugging purposes.&]
|
||||
[s3;%% &]
|
||||
[ {{10000F(128)G(128)@1 [s0;%% [* Constructor detail]]}}&]
|
||||
[s3; &]
|
||||
[s5;:Upp`:`:TurtleServer`:`:TurtleServer`(`): [* TurtleServer]()&]
|
||||
[s2;%% Default constructor. Initializes the server bind address to
|
||||
`"0.0.0.0`", the host URL to `"localhost`", and the connection
|
||||
port number to 8888.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:TurtleServer`:`:TurtleServer`(const Upp`:`:String`&`,int`): [* TurtleServer
|
||||
]([@(0.0.255) const]_[_^Upp`:`:String^ String][@(0.0.255) `&]_[*@3 host],
|
||||
[@(0.0.255) int]_[*@3 port])&]
|
||||
[s2;%% Constructor overload. Initializes the host URL and the connection
|
||||
port number to provided values.&]
|
||||
[s3;%% &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:TurtleServer`:`:TurtleServer`(const Upp`:`:String`&`,Upp`:`:String`&`,int`): [* T
|
||||
urtleServer]([@(0.0.255) const]_[_^Upp`:`:String^ String][@(0.0.255) `&]_[*@3 ip],
|
||||
[_^Upp`:`:String^ String][@(0.0.255) `&]_[*@3 host], [@(0.0.255) int]_[*@3 port])&]
|
||||
[s2;%% Constructor overload. Initializes the server bind adrress,
|
||||
the host URL and the connection port number to provided values.&]
|
||||
[s3;%% &]
|
||||
[ {{10000F(128)G(128)@1 [s0;%% [* Function List]]}}&]
|
||||
[s3;%% &]
|
||||
[s5;:Upp`:`:RunTurtleGui`(Upp`:`:TurtleServer`&`,Upp`:`:Event`<`>`): [@(0.0.255) void]_
|
||||
[* RunTurtleGui]([_^Upp`:`:TurtleServer^ TurtleServer][@(0.0.255) `&]_[*@3 gui],
|
||||
[_^Upp`:`:Event^ Event]<>_[*@3 app`_main])&]
|
||||
[s2;%% Starts the Turtle GUI virtualization server and runs a U`+`+
|
||||
GUI application over it.&]
|
||||
[s3;%% &]
|
||||
[s0;%% ]]
|
||||
|
|
@ -94,7 +94,7 @@ struct GetPasskeyDlg : WithGetPasskeyLayout<TopWindow> {
|
|||
|
||||
GetPasskeyDlg::GetPasskeyDlg()
|
||||
{
|
||||
CtrlLayoutOKCancel(*this, "Passkey");
|
||||
CtrlLayoutOK(*this, "Passkey");
|
||||
|
||||
show_passkey << [=] { Sync(); };
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
description "FreeType based Draw FONTSYS replacement\3770,128,0";
|
||||
|
||||
options(BUILDER_OPTION) NOWARNINGS;
|
||||
|
||||
uses
|
||||
Draw;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue