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
This commit is contained in:
Spoike 2012-10-07 18:26:22 +00:00
parent b937bb8161
commit 459c99efec
4 changed files with 567 additions and 3 deletions

View file

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="berkelium"
ProjectGUID="{4877586B-E85B-4DF8-BCCE-59D31514D240}"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
EnableFunctionLevelLinking="true"
BrowseInformation="1"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="berkelium/berkelium.lib"
OutputFile="C:\Games\Quake\fte\plugins\$(ProjectName)x86.dll"
LinkIncremental="2"
GenerateManifest="false"
ModuleDefinitionFile="..\plugin.def"
GenerateDebugInformation="true"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
EmbedManifest="false"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\plugapi.cpp"
>
</File>
<File
RelativePath="..\plugin.c"
>
</File>
<File
RelativePath="..\qvm_api.c"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
<File
RelativePath="..\plugin.def"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View file

@ -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 <string>
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();
}

View file

@ -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'.

View file

@ -50,6 +50,11 @@ void BadBuiltin(void);
#include <stdlib.h>
#include <stdarg.h>
#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