From 459c99efec7376fbaae214275ab60180b7c8a4bd Mon Sep 17 00:00:00 2001 From: Spoike Date: Sun, 7 Oct 2012 18:26:22 +0000 Subject: [PATCH] FTE plugin for support to run berkelium. This is an in-game web browser, that can be used with playfilm or with custom shaders. CSQC also has some control over it, for feeding it mouse/keyboard events. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4109 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- plugins/berkelium/berkelium.vcproj | 189 ++++++++++++++++++ plugins/berkelium/plugapi.cpp | 309 +++++++++++++++++++++++++++++ plugins/berkelium/readme.txt | 52 +++++ plugins/plugin.h | 20 +- 4 files changed, 567 insertions(+), 3 deletions(-) create mode 100644 plugins/berkelium/berkelium.vcproj create mode 100644 plugins/berkelium/plugapi.cpp create mode 100644 plugins/berkelium/readme.txt diff --git a/plugins/berkelium/berkelium.vcproj b/plugins/berkelium/berkelium.vcproj new file mode 100644 index 000000000..bc11d9ff7 --- /dev/null +++ b/plugins/berkelium/berkelium.vcproj @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/berkelium/plugapi.cpp b/plugins/berkelium/plugapi.cpp new file mode 100644 index 000000000..0858baa8e --- /dev/null +++ b/plugins/berkelium/plugapi.cpp @@ -0,0 +1,309 @@ +#include "../plugin.h" +#include "../engine.h" + +#include "berkelium/Berkelium.hpp" +#include "berkelium/Window.hpp" +#include "berkelium/WindowDelegate.hpp" +#include "berkelium/Context.hpp" + +#include + +class decctx +{ +public: + Berkelium::Window *wnd; + int width; + int height; + unsigned int *buffer; + bool repainted; +}; + +class MyDelegate : public Berkelium::WindowDelegate +{ +private: + decctx *ctx; + + 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; + int t = scroll_rect.top(); + int l = scroll_rect.left(); + int w = scroll_rect.width(); + int h = scroll_rect.height(); + 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 = t+h-1; y >= t; y--) + { + if (y < 0 || y >= ctx->height) + continue; + if (y+dy < 0 || y+dy >= ctx->height) + continue; + memmove(ctx->buffer + ((l+dx) + (y+dy)*ctx->width), ctx->buffer + (l + y*ctx->width), w*4); + } + } + else + { + //moving upwards requires we write the top row first + for (y = t; y < t+h; y++) + { + if (y < 0 || y >= ctx->height) + continue; + if (y+dy < 0 || y+dy >= ctx->height) + continue; + + memmove(ctx->buffer + ((l+dx) + (y+dy)*ctx->width), ctx->buffer + (l + 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() - bitmap_rect.top(); + int l = copy_rects[i].left() - bitmap_rect.left(); + int r = copy_rects[i].width() + l; + int b = copy_rects[i].height() + t; + unsigned int instride = bitmap_rect.width() - (r - l); + unsigned int outstride = ctx->width - (r - l); + + out += copy_rects[i].left(); + out += copy_rects[i].top() * ctx->width; + + in += l; + in += t * bitmap_rect.width(); + + for (y = t; y < b; y++) + { + for (x = l; x < r; 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 = "about:blank"; + else if (!strncmp(medianame, "http:", 5)) + medianame = medianame; //and direct http requests. + else + return NULL; + + decctx *ctx = new decctx(); + + Berkelium::Context* context = Berkelium::Context::create(); + ctx->width = 1024; + 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->repainted = false; + return ctx->buffer; +} +static void Dec_Destroy(void *vctx) +{ + decctx *ctx = (decctx*)vctx; + ctx->wnd->destroy(); + delete ctx; +} +static void Dec_GetSize (void *vctx, int *width, int *height) +{ + decctx *ctx = (decctx*)vctx; + *width = ctx->width; + *height = ctx->height; +} +static qboolean Dec_SetSize (void *vctx, int width, int height) +{ + decctx *ctx = (decctx*)vctx; + 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) + ctx->wnd->mouseButton(code - 178, !isup); + else if (code == 188 || code == 189) + ctx->wnd->mouseWheel(0, (code==189)?-30:30); + else + { + if (code) + { + int mods = 0; + if (code == 127) + code = 0x2e; + 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, "opaque")) + ctx->wnd->setTransparent(true); + 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) +{ + if (!Berkelium::init(Berkelium::FileString::empty())) + { + Con_Printf("Couldn't initialize Berkelium.\n"); + return false; + } + return true; +} + +static int Dec_Tick(int *args) +{ + //need to keep it ticking over, if any work is to be done. + Berkelium::update(); + return 0; +} + +static int Dec_Shutdown(int *args) +{ + //force-kill all. + Berkelium::destroy(); + return 0; +} + +static media_decoder_funcs_t decoderfuncs = +{ + Dec_Create, + Dec_DisplayFrame, + NULL,//doneframe + Dec_Destroy, + NULL,//rewind + + Dec_CursorMove, + Dec_Key, + Dec_SetSize, + Dec_GetSize, + Dec_ChangeStream +}; + +int Plug_Init(int *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 warning: 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(); +} diff --git a/plugins/berkelium/readme.txt b/plugins/berkelium/readme.txt new file mode 100644 index 000000000..92ef035ac --- /dev/null +++ b/plugins/berkelium/readme.txt @@ -0,0 +1,52 @@ +simple media decoding plugin that decodes web pages instead of videos... +See installation instructions at the end of this file. + + +This means you can do: playfilm berkelium:http://google.com +And you'll get google.com displayed as if it were a cinematic or so. +The engine will pass mouse+keyboard events to such cinematics so you can interact with it. + + +You can also use the 'videomap' term within a shader, if you want to put some youtube video on a wall or something. Here's an example of such a shader: +muh_bad +{ + { + map $lightmap + } + { + videomap berkelium:http://google.com + blendfunc filter + } +} + + +CSQC is able to interact with videos by: +1: find the name of the shader + string texname = getsurfacetexture(trace_ent, getsurfacenearpoint(trace_ent, trace_endpos)); +2: send a key event + gecko_keyevent(texname, keycode, keydown); +3: move the mouse cursor + gecko_mousemove(texname, x, y); + note that the x and y values should be between 0 and 1. Surface resolution is not relevent here. +4: resize the image + gecko_resize(texname, 1024, 1024); + if you're using it in 2d, make sure it matches the pixel width of the screen where it'll be displayed. it'll get fuzzy otherwise. + Size matters. Beware of sites that do not resize images for different resolutions (ie: most of them). +5: you can query the image with: + vector v = gecko_get_texture_extent(texname); +6: you can purge resources with: + gecko_destroy(texname); + this will 'end' the video. when the shader is next displayed it'll reset from the original url. +7: you can change the url with: + gecko_navigate(texname, "http://fteqw.com"); + + + +Compiling the plugin for Windows: +In the berkelium 7z file that you should have already downloaded, there'll be an includes and a lib directory. Stick the contents of both of those in the empty doubled berkelium directory on the fte svn. +The msvc project file should now be usable. You will likely want to change the linker target path to get picked up by the engine automatically. + +Windows Installation instructions: +Download Berkelium from here: http://berkelium.org/ (http://github.com/sirikata/berkelium/downloads) +You should get a 7z file that contains a bin directory. Copy the contents of that bin directory to your quake directory. +The FTE-specific plugin 'berkeliumx86.dll' should be placed as 'quake/fte/plugins/berkeliumx86.dll'. diff --git a/plugins/plugin.h b/plugins/plugin.h index 4b6259040..06009a8fa 100644 --- a/plugins/plugin.h +++ b/plugins/plugin.h @@ -50,6 +50,11 @@ void BadBuiltin(void); #include #include #include "math.h" + +#ifdef __cplusplus +extern "C" { +#endif + //DLLs need a wrapper to add the extra parameter and call a boring function. #define EBUILTIN(t, n, args) extern int BUILTIN_##n; t n args #define TEST @@ -75,8 +80,13 @@ int snprintf(char *buffer, size_t maxlen, const char *format, ...); #endif #endif - -typedef enum {false, true} qboolean; +#ifdef __cplusplus +typedef enum {qfalse, qtrue} qboolean; +#else +typedef enum {qfalse, qtrue} qboolean; +#define false qfalse +#define true qtrue +#endif typedef void *qhandle_t; typedef float vec3_t[3]; typedef void* funcptr_t; @@ -109,7 +119,7 @@ EBUILTIN(qboolean, Plug_ExportNative, (char *funcname, void *func)); //set up in EBUILTIN(void, Con_Print, (char *text)); //on to main console. EBUILTIN(void, Con_SubPrint, (char *subname, char *text)); //on to sub console. -EBUILTIN(void, Con_RenameSub, (char *old, char *new)); //rename a console. +EBUILTIN(void, Con_RenameSub, (char *oldname, char *newname)); //rename a console. EBUILTIN(int, Con_IsActive, (char *conname)); EBUILTIN(void, Con_SetActive, (char *conname)); EBUILTIN(void, Con_Destroy, (char *conname)); @@ -256,4 +266,8 @@ void Info_RemoveNonStarKeys (char *start); void Info_SetValueForKey (char *s, char *key, char *value, int maxsize); void Info_SetValueForStarKey (char *s, char *key, char *value, int maxsize); +#ifdef __cplusplus +} +#endif + #endif