cf893a1921
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4455 fc73d0e0-1445-4013-8a0c-d673dee63da5
449 lines
10 KiB
C++
449 lines
10 KiB
C++
#include "../plugin.h"
|
|
#include "../engine.h"
|
|
|
|
#include "berkelium/Berkelium.hpp"
|
|
#include "berkelium/Window.hpp"
|
|
#include "berkelium/WindowDelegate.hpp"
|
|
#include "berkelium/Context.hpp"
|
|
|
|
#include <string>
|
|
|
|
qboolean inited;
|
|
|
|
class decctx
|
|
{
|
|
public:
|
|
Berkelium::Window *wnd;
|
|
int width;
|
|
int height;
|
|
unsigned int *buffer;
|
|
bool repainted;
|
|
|
|
int paintedwidth;
|
|
int paintedheight;
|
|
};
|
|
|
|
class MyDelegate : public Berkelium::WindowDelegate
|
|
{
|
|
private:
|
|
decctx *ctx;
|
|
|
|
virtual void onCrashedWorker(Berkelium::Window *win)
|
|
{
|
|
int i;
|
|
Con_Printf("Berkelium worker crashed\n");
|
|
|
|
/*black it out*/
|
|
for (i = 0; i < ctx->width*ctx->height; i++)
|
|
{
|
|
ctx->buffer[i] = 0xff000000;
|
|
}
|
|
ctx->repainted = true;
|
|
}
|
|
|
|
virtual void onCrashed(Berkelium::Window *win)
|
|
{
|
|
int i;
|
|
Con_Printf("Berkelium window crashed\n");
|
|
|
|
/*black it out*/
|
|
for (i = 0; i < ctx->width*ctx->height; i++)
|
|
{
|
|
ctx->buffer[i] = 0xff000000;
|
|
}
|
|
ctx->repainted = true;
|
|
}
|
|
virtual void onUnresponsive(Berkelium::Window *win)
|
|
{
|
|
Con_Printf("Berkelium window unresponsive\n");
|
|
}
|
|
virtual void onResponsive(Berkelium::Window *win)
|
|
{
|
|
Con_Printf("Berkelium window responsive again, yay\n");
|
|
}
|
|
|
|
virtual void onPaint(Berkelium::Window *wini, const unsigned char *bitmap_in, const Berkelium::Rect &bitmap_rect, size_t num_copy_rects, const Berkelium::Rect *copy_rects, int dx, int dy, const Berkelium::Rect& scroll_rect)
|
|
{
|
|
int i;
|
|
// handle paint events...
|
|
if (dx || dy)
|
|
{
|
|
int y, m;
|
|
int dt = scroll_rect.top();
|
|
int dl = scroll_rect.left();
|
|
int w = scroll_rect.width();
|
|
int h = scroll_rect.height();
|
|
int st = dt - dy;
|
|
int sl = dl - dx;
|
|
|
|
/*bound the output rect*/
|
|
if (dt < 0)
|
|
{
|
|
st -= dt;
|
|
h += dt;
|
|
dt = 0;
|
|
}
|
|
if (dl < 0)
|
|
{
|
|
sl -= dl;
|
|
w += dl;
|
|
dl = 0;
|
|
}
|
|
/*bound the source rect*/
|
|
if (st < 0)
|
|
{
|
|
dt -= st;
|
|
h += st;
|
|
st = 0;
|
|
}
|
|
if (sl < 0)
|
|
{
|
|
dl -= sl;
|
|
w += sl;
|
|
sl = 0;
|
|
}
|
|
/*bound the width*/
|
|
m = (dl>sl)?dl:sl;
|
|
if (m + w > ctx->width)
|
|
w = ctx->width - m;
|
|
m = (dt>st)?dt:st;
|
|
if (m + h > ctx->height)
|
|
h = ctx->height - m;
|
|
|
|
if (w > 0 && h > 0)
|
|
{
|
|
if (dy > 0)
|
|
{
|
|
//if we're moving downwards, we need to write the bottom before the top (so we don't overwrite the data before its copied)
|
|
for (y = h-1; y >= 0; y--)
|
|
{
|
|
memmove(ctx->buffer + (dl + (dt+y)*ctx->width), ctx->buffer + (sl + (st+y)*ctx->width), w*4);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//moving upwards requires we write the top row first
|
|
for (y = 0; y < h; y++)
|
|
{
|
|
memmove(ctx->buffer + (dl + (dt+y)*ctx->width), ctx->buffer + (sl + (st+y)*ctx->width), w*4);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < num_copy_rects; i++)
|
|
{
|
|
unsigned int *out = ctx->buffer;
|
|
const unsigned int *in = (const unsigned int*)bitmap_in;
|
|
int x, y;
|
|
int t = copy_rects[i].top();
|
|
int l = copy_rects[i].left();
|
|
int r = copy_rects[i].width() + l;
|
|
int b = copy_rects[i].height() + t;
|
|
int w, h;
|
|
|
|
//Clip the rect to the display. This should generally happen anyway, but resizes can be lagged a bit with the whole multi-process/thread thing.
|
|
//don't need to clip to the bitmap rect, that should be correct.
|
|
if (l < 0)
|
|
l = 0;
|
|
if (t < 0)
|
|
t = 0;
|
|
if (r > ctx->width)
|
|
r = ctx->width;
|
|
if (b > ctx->height)
|
|
b = ctx->height;
|
|
w = r - l;
|
|
h = b - t;
|
|
|
|
unsigned int instride = bitmap_rect.width() - (w);
|
|
unsigned int outstride = ctx->width - (w);
|
|
|
|
out += l;
|
|
out += t * ctx->width;
|
|
|
|
in += (l-bitmap_rect.left());
|
|
in += (t-bitmap_rect.top()) * bitmap_rect.width();
|
|
|
|
for (y = 0; y < h; y++)
|
|
{
|
|
for (x = 0; x < w; x++)
|
|
{
|
|
*out++ = *in++;
|
|
}
|
|
in += instride;
|
|
out += outstride;
|
|
}
|
|
}
|
|
|
|
ctx->repainted = true;
|
|
}
|
|
|
|
public:
|
|
MyDelegate(decctx *_ctx) : ctx(_ctx) {};
|
|
};
|
|
|
|
static void *Dec_Create(char *medianame)
|
|
{
|
|
/*only respond to berkelium: media prefixes*/
|
|
if (!strncmp(medianame, "berkelium:", 10))
|
|
medianame = medianame + 10;
|
|
else if (!strcmp(medianame, "berkelium"))
|
|
medianame = (char*)"about:blank";
|
|
else if (!strncmp(medianame, "http:", 5) || !strncmp(medianame, "https:", 6))
|
|
medianame = medianame; //and direct http requests.
|
|
else
|
|
return NULL;
|
|
|
|
if (!inited)
|
|
{
|
|
//linux lags behind and apparently returns void, so don't bother checking return values on windows, cos I'm lazy.
|
|
Berkelium::init(Berkelium::FileString::empty());
|
|
inited = qtrue;
|
|
}
|
|
|
|
decctx *ctx = new decctx();
|
|
|
|
Berkelium::Context* context = Berkelium::Context::create();
|
|
ctx->paintedwidth = ctx->width = 1024;
|
|
ctx->paintedheight = ctx->height = 1024;
|
|
ctx->repainted = false;
|
|
ctx->buffer = (unsigned int*)malloc(ctx->width * ctx->height * 4);
|
|
ctx->wnd = Berkelium::Window::create(context);
|
|
delete context;
|
|
|
|
ctx->wnd->setDelegate(new MyDelegate(ctx));
|
|
|
|
|
|
ctx->wnd->resize(ctx->width, ctx->height);
|
|
std::string url = medianame;
|
|
ctx->wnd->navigateTo(Berkelium::URLString::point_to(url.data(), url.length()));
|
|
|
|
return ctx;
|
|
}
|
|
|
|
static void *Dec_DisplayFrame(void *vctx, qboolean nosound, enum uploadfmt_e *fmt, int *width, int *height)
|
|
{
|
|
decctx *ctx = (decctx*)vctx;
|
|
*fmt = TF_BGRA32;
|
|
*width = ctx->width;
|
|
*height = ctx->height;
|
|
|
|
if (!ctx->repainted)
|
|
return NULL;
|
|
ctx->paintedwidth = ctx->width;
|
|
ctx->paintedheight = ctx->height;
|
|
ctx->repainted = false;
|
|
return ctx->buffer;
|
|
}
|
|
static void Dec_Destroy(void *vctx)
|
|
{
|
|
decctx *ctx = (decctx*)vctx;
|
|
if (inited) //make sure things don't happen in the wrong order. we can still leak though
|
|
ctx->wnd->destroy();
|
|
delete ctx;
|
|
}
|
|
static void Dec_GetSize (void *vctx, int *width, int *height)
|
|
{
|
|
decctx *ctx = (decctx*)vctx;
|
|
if (ctx->repainted)
|
|
{
|
|
*width = ctx->width;
|
|
*height = ctx->height;
|
|
}
|
|
else
|
|
{
|
|
*width = ctx->paintedwidth;
|
|
*height = ctx->paintedheight;
|
|
}
|
|
}
|
|
static qboolean Dec_SetSize (void *vctx, int width, int height)
|
|
{
|
|
decctx *ctx = (decctx*)vctx;
|
|
if (width < 4)
|
|
width = 4;
|
|
if (height < 4)
|
|
height = 4;
|
|
if (ctx->width == width && ctx->height == height)
|
|
return qtrue; //no point
|
|
|
|
//there's no resize notification. apparently javascript cannot resize windows. yay.
|
|
unsigned int *newbuf = (unsigned int*)realloc(ctx->buffer, width * height * sizeof(*newbuf));
|
|
if (!newbuf)
|
|
return qfalse; //failed?!?
|
|
ctx->width = width;
|
|
ctx->height = height;
|
|
ctx->buffer = newbuf;
|
|
ctx->repainted = false;
|
|
|
|
ctx->wnd->resize(ctx->width, ctx->height);
|
|
|
|
return qtrue;
|
|
}
|
|
static void Dec_CursorMove (void *vctx, float posx, float posy)
|
|
{
|
|
decctx *ctx = (decctx*)vctx;
|
|
ctx->wnd->mouseMoved((int)(posx * ctx->width), (int)(posy * ctx->height));
|
|
}
|
|
static void Dec_Key (void *vctx, int code, int unicode, int isup)
|
|
{
|
|
decctx *ctx = (decctx*)vctx;
|
|
wchar_t wchr = unicode;
|
|
|
|
if (code >= 178 && code < 178+6)
|
|
{
|
|
code = code - 178;
|
|
//swap mouse2+3
|
|
if (code == 1)
|
|
code = 2;
|
|
else if (code == 2)
|
|
code = 1;
|
|
ctx->wnd->mouseButton(code, !isup);
|
|
}
|
|
else if (code == 188 || code == 189)
|
|
{
|
|
if (!isup)
|
|
ctx->wnd->mouseWheel(0, (code==189)?-30:30);
|
|
}
|
|
else
|
|
{
|
|
int mods = 0;
|
|
if (code == 127)
|
|
code = 0x08;
|
|
else if (code == 140) //del
|
|
code = 0x2e;
|
|
else if (code == 143) //home
|
|
code = 0x24;
|
|
else if (code == 144) //end
|
|
code = 0x23;
|
|
else if (code == 141) //pgdn
|
|
code = 0x22;
|
|
else if (code == 142) //pgup
|
|
code = 0x21;
|
|
else if (code == 139) //ins
|
|
code = 0x2d;
|
|
else if (code == 132) //up
|
|
code = 0x26;
|
|
else if (code == 133) //down
|
|
code = 0x28;
|
|
else if (code == 134) //left
|
|
code = 0x25;
|
|
else if (code == 135) //right
|
|
code = 0x27;
|
|
if (code)
|
|
ctx->wnd->keyEvent(!isup, mods, code, 0);
|
|
if (unicode && !isup)
|
|
{
|
|
wchar_t chars[2] = {unicode};
|
|
if (unicode == 127 || unicode == 8 || unicode == 9 || unicode == 27)
|
|
return;
|
|
ctx->wnd->textEvent(chars, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void Dec_ChangeStream(void *vctx, char *newstream)
|
|
{
|
|
decctx *ctx = (decctx*)vctx;
|
|
|
|
if (!strncmp(newstream, "cmd:", 4))
|
|
{
|
|
if (!strcmp(newstream+4, "refresh"))
|
|
ctx->wnd->refresh();
|
|
else if (!strcmp(newstream+4, "transparent"))
|
|
ctx->wnd->setTransparent(true);
|
|
else if (!strcmp(newstream+4, "focus"))
|
|
ctx->wnd->focus();
|
|
else if (!strcmp(newstream+4, "unfocus"))
|
|
ctx->wnd->unfocus();
|
|
else if (!strcmp(newstream+4, "opaque"))
|
|
ctx->wnd->setTransparent(false);
|
|
else if (!strcmp(newstream+4, "stop"))
|
|
ctx->wnd->stop();
|
|
else if (!strcmp(newstream+4, "back"))
|
|
ctx->wnd->goBack();
|
|
else if (!strcmp(newstream+4, "forward"))
|
|
ctx->wnd->goForward();
|
|
else if (!strcmp(newstream+4, "cut"))
|
|
ctx->wnd->cut();
|
|
else if (!strcmp(newstream+4, "copy"))
|
|
ctx->wnd->copy();
|
|
else if (!strcmp(newstream+4, "paste"))
|
|
ctx->wnd->paste();
|
|
else if (!strcmp(newstream+4, "del"))
|
|
ctx->wnd->del();
|
|
else if (!strcmp(newstream+4, "selectall"))
|
|
ctx->wnd->selectAll();
|
|
}
|
|
else if (!strncmp(newstream, "javascript:", 11))
|
|
{
|
|
newstream+=11;
|
|
int len = mblen(newstream, MB_CUR_MAX);
|
|
wchar_t *wchrs = (wchar_t *)malloc((len+1)*2);
|
|
len = mbstowcs(wchrs, newstream, len);
|
|
ctx->wnd->executeJavascript(Berkelium::WideString::point_to(wchrs, len));
|
|
free(wchrs);
|
|
}
|
|
else
|
|
{
|
|
std::string url = newstream;
|
|
ctx->wnd->navigateTo(Berkelium::URLString::point_to(url.data(), url.length()));
|
|
}
|
|
}
|
|
|
|
static bool Dec_Init(void)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static qintptr_t Dec_Tick(qintptr_t *args)
|
|
{
|
|
//need to keep it ticking over, if any work is to be done.
|
|
if (inited)
|
|
Berkelium::update();
|
|
return 0;
|
|
}
|
|
|
|
static qintptr_t Dec_Shutdown(qintptr_t *args)
|
|
{
|
|
//force-kill all.
|
|
if (inited)
|
|
Berkelium::destroy();
|
|
inited = qfalse;
|
|
return 0;
|
|
}
|
|
|
|
static media_decoder_funcs_t decoderfuncs =
|
|
{
|
|
"berkelium",
|
|
Dec_Create,
|
|
Dec_DisplayFrame,
|
|
NULL,//doneframe
|
|
Dec_Destroy,
|
|
NULL,//rewind
|
|
|
|
Dec_CursorMove,
|
|
Dec_Key,
|
|
Dec_SetSize,
|
|
Dec_GetSize,
|
|
Dec_ChangeStream
|
|
};
|
|
|
|
extern "C" qintptr_t Plug_Init(qintptr_t *args)
|
|
{
|
|
if (!Plug_Export("Tick", Dec_Tick))
|
|
{
|
|
Con_Printf("Berkelium plugin failed: Engine doesn't support Tick feature\n");
|
|
return false;
|
|
}
|
|
if (!Plug_Export("Shutdown", Dec_Shutdown))
|
|
{
|
|
Con_Printf("Berkelium plugin failed: Engine doesn't support Shutdown feature\n");
|
|
return false;
|
|
}
|
|
if (!Plug_ExportNative("Media_VideoDecoder", &decoderfuncs))
|
|
{
|
|
Con_Printf("Berkelium plugin failed: Engine doesn't support media decoder plugins\n");
|
|
return false;
|
|
}
|
|
return Dec_Init();
|
|
}
|
|
|