misc fixes/tweaks/stuff intended to get the emscripten port working better.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4456 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2013-08-07 14:20:24 +00:00
parent cf893a1921
commit 3b51dae2c9
21 changed files with 1303 additions and 135 deletions

View File

@ -1118,20 +1118,24 @@ ifeq ($(FTE_TARGET),droid)
endif
ifeq ($(FTE_TARGET),web)
COMMON_OBJS+=sys_web.o fs_web.o
WEB_PREJS ?= --pre-js web/prejs.js
# WEB_MEMORY?=402653184 #384mb
ASMJS_MEMORY?=536870912 #512mb (required for asm.js)
# ASMJS_MEMORY?=268435456 #256mb (required for asm.js)
WEB_MEMORY?=$(ASMJS_MEMORY)
JSLIBS=--js-library web/ftejslib.js --js-library web/library_sdl.js -s DISABLE_GL_EMULATION=1
EMCC_ARGS=$(JSLIBS) $(WEB_PREJS) --shell-file web/fteshell.html -s ERROR_ON_UNDEFINED_SYMBOLS=1
RELEASE_CFLAGS=-DOMIT_QCC -DGL_STATIC -DFTE_TARGET_WEB
DEBUG_CFLAGS=-g --jcache -DOMIT_QCC -DGL_STATIC -DFTE_TARGET_WEB
# RELEASE_LDFLAGS=-s ASM_JS=1 -O2 $(WEB_PREJS) -s TOTAL_MEMORY=$(ASMJS_MEMORY) --js-library web/ftejslib.js --shell-file web/fteshell.html
RELEASE_LDFLAGS=-O1 $(WEB_PREJS) -s TOTAL_MEMORY=$(WEB_MEMORY) --js-library web/ftejslib.js --shell-file web/fteshell.html
DEBUG_LDLAGS=-O0 $(WEB_PREJS) -s TOTAL_MEMORY=$(WEB_MEMORY) --js-library web/ftejslib.js
RELEASE_LDFLAGS=-s ASM_JS=1 -O1 -s TOTAL_MEMORY=$(ASMJS_MEMORY) $(EMCC_ARGS)
# RELEASE_LDFLAGS=-s ASM_JS=0 -O1 -s TOTAL_MEMORY=$(WEB_MEMORY) $(EMCC_ARGS)
DEBUG_LDLAGS=-O0 -s TOTAL_MEMORY=$(WEB_MEMORY) $(EMCC_ARGS)
CC=emcc
#BASELDFLAGS=
#mostly we inherit the sdl defaults. because we can, however emscripten does not support sdl cd code.
GLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) gl_vidsdl.o snd_sdl.o cd_null.o sys_sdl.o in_sdl.o
GLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) gl_vidweb.o cd_null.o
SDL_INCLUDES=
SV_DIR=sv_web
@ -1200,7 +1204,7 @@ ifneq ($(OUT_DIR),)
endif
VPATH = $(BASE_DIR) : $(CLIENT_DIR) : $(GL_DIR) : $(COMMON_DIR) : $(SERVER_DIR) : $(HTTP_DIR) : $(BASE_DIR)/irc : $(BASE_DIR)/email : $(QUX_DIR) : $(PROGS_DIR) : $(NACL_DIR) : $(D3D_DIR) : $(BOTLIB_DIR) : $(BASE_DIR)/libs/speex/libspeex
VPATH = $(BASE_DIR) : $(CLIENT_DIR) : $(GL_DIR) : $(COMMON_DIR) : $(SERVER_DIR) : $(HTTP_DIR) : $(BASE_DIR)/irc : $(BASE_DIR)/email : $(QUX_DIR) : $(PROGS_DIR) : $(NACL_DIR) : $(D3D_DIR) : $(BOTLIB_DIR) : $(BASE_DIR)/libs/speex/libspeex : $(BASE_DIR)/web
ifneq ($(findstring -DSPEEX_STATIC, $(CFLAGS)),)
#add these to statically link libspeex

View File

@ -254,7 +254,7 @@ static texwadlump_t texwadlump[TEXWAD_MAXIMAGES];
typedef struct wadfile_s {
char name[64];
FILE *file;
vfsfile_t *file;
struct wadfile_s *next;
} wadfile_t;
@ -265,7 +265,7 @@ void Wads_Flush (void)
wadfile_t *wf;
while(openwadfiles)
{
fclose(openwadfiles->file);
VFS_CLOSE(openwadfiles->file);
wf = openwadfiles->next;
Z_Free(openwadfiles);

View File

@ -3796,7 +3796,7 @@ void COM_InitArgv (int argc, const char **argv) //not allowed to tprint
int i;
size_t result;
#ifndef NACL
#if !defined(NACL) && !defined(FTE_TARGET_WEB)
FILE *f;
if (argv && argv[0])

View File

@ -479,6 +479,11 @@ void Cvar_Reset_f (void)
if (gsearch)
Q_strlwr(gsearch);
if (!strcmp(search, "*"))
search = NULL;
if (!strcmp(gsearch, "*"))
gsearch = NULL;
for (grp=cvar_groups ; grp ; grp=grp->next)
{
if (gsearch)
@ -519,7 +524,7 @@ void Cvar_Reset_f (void)
}
}
if ((cmd->flags & CVAR_NOSET) && !strcmp(search, "*"))
if ((cmd->flags & CVAR_NOSET) && !search)
continue;
// reset cvar to default
Cvar_Set(cmd, cmd->defaultstr);

View File

@ -2,7 +2,7 @@
#include "fs.h"
#include "errno.h"
#ifndef NACL
#if !defined(NACL) && !defined(FTE_TARGET_WEB)
#ifdef WEBSVONLY
#define Z_Free free

View File

@ -4066,11 +4066,7 @@ struct ftenet_generic_connection_s *FTENET_IRCConnect_EstablishConnection(qboole
#endif
#ifdef FTE_TARGET_WEB
int emscriptenfte_ws_connect(char *url);
int emscriptenfte_ws_close(int sock);
int emscriptenfte_ws_cansend(int sock, int extra, int maxpending);
int emscriptenfte_ws_send(int sock, void *data, int len);
int emscriptenfte_ws_recv(int sock, void *data, int len);
#include "web/ftejslib.h"
typedef struct
{

View File

@ -563,6 +563,9 @@ void TL_ParseLanguage (char *name, char *data, int num) //this is one of the fir
void TL_LoadLanguage (char *name, char *shortname, int num) //this is one of the first functions to be called.
{
#ifdef FTE_TARGET_WEB
return;
#else
FILE *f;
int size;
char *buffer;
@ -585,6 +588,7 @@ void TL_LoadLanguage (char *name, char *shortname, int num) //this is one of the
TL_ParseLanguage(name, buffer, num);
free(buffer);
#endif
}
#ifdef _DEBUG
#define CONVERTDEFAULT

View File

@ -1271,6 +1271,11 @@ void GL_Upload32_Int (char *name, unsigned *data, int width, int height, unsigne
}
else
GL_ResampleTexture (data, width, height, scaled, scaled_width, scaled_height);
if (scaled_width * scaled_height*4 > sizeofuploadmemorybufferintermediate)
{
sizeofuploadmemorybufferintermediate = scaled_width * scaled_height * 4;
uploadmemorybufferintermediate = BZ_Realloc(uploadmemorybufferintermediate, sizeofuploadmemorybufferintermediate);
}
if (scaled_width*scaled_height*4 > sizeofuploadmemorybufferintermediate)
{

View File

@ -158,6 +158,7 @@ void (APIENTRY *qglFogf) (GLenum pname, GLfloat param);
void (APIENTRY *qglFogi) (GLenum pname, GLint param);
void (APIENTRY *qglFogfv) (GLenum pname, const GLfloat *params);
#ifndef GL_STATIC
void (APIENTRY *qglGenBuffersARB)(GLsizei n, GLuint* ids);
void (APIENTRY *qglDeleteBuffersARB)(GLsizei n, GLuint* ids);
void (APIENTRY *qglBindBufferARB)(GLenum target, GLuint id);
@ -165,6 +166,7 @@ void (APIENTRY *qglBufferDataARB)(GLenum target, GLsizei size, const void* data,
void (APIENTRY *qglBufferSubDataARB)(GLenum target, GLint offset, GLsizei size, void* data);
void *(APIENTRY *qglMapBufferARB)(GLenum target, GLenum access);
GLboolean (APIENTRY *qglUnmapBufferARB)(GLenum target);
#endif
void (APIENTRY *qglGenVertexArrays)(GLsizei n, GLuint *arrays);
void (APIENTRY *qglBindVertexArray)(GLuint vaoarray);
@ -172,11 +174,13 @@ void (APIENTRY *qglBindVertexArray)(GLuint vaoarray);
const GLubyte * (APIENTRY * qglGetStringi) (GLenum name, GLuint index);
void (APIENTRY * qglGetPointerv) (GLenum pname, GLvoid **parms);
#ifndef GL_STATIC
void (APIENTRY *qglGenRenderbuffersEXT)(GLsizei n, GLuint* ids);
void (APIENTRY *qglBindRenderbufferEXT)(GLenum target, GLuint id);
void (APIENTRY *qglRenderbufferStorageEXT)(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height);
void (APIENTRY *qglFramebufferRenderbufferEXT)(GLenum target, GLenum attachmentPoint, GLenum textureTarget, GLuint textureId);
GLenum (APIENTRY *qglCheckFramebufferStatusEXT)(GLenum target);
#endif
void (APIENTRY *qglDepthBoundsEXT) (GLclampd zmin, GLclampd zmax);
/*
@ -191,7 +195,9 @@ FTEPFNGLUNLOCKARRAYSEXTPROC qglUnlockArraysEXT;
//extensions
//arb multitexture
#ifndef qglActiveTextureARB
qlpSelTexFUNC qglActiveTextureARB;
#endif
qlpSelTexFUNC qglClientActiveTextureARB;
qlpMTex3FUNC qglMultiTexCoord3fARB;
qlpMTex2FUNC qglMultiTexCoord2fARB;
@ -521,7 +527,9 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name))
//multitexture
gl_mtexable = false;
gl_mtexarbable = 0;
#ifndef qglActiveTextureARB
qglActiveTextureARB = NULL;
#endif
qglMultiTexCoord2fARB = NULL;
qglMultiTexCoord3fARB = NULL;
qglMTexCoord2fSGIS = NULL;
@ -587,7 +595,9 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name))
if (gl_config.gles)
{
#ifndef qglActiveTextureARB
qglActiveTextureARB = (void *) getglext("glActiveTexture");
#endif
qglClientActiveTextureARB = (void *) getglext("glClientActiveTexture");
qglSelectTextureSGIS = qglActiveTextureARB;
mtexid0 = GL_TEXTURE0_ARB;
@ -598,7 +608,9 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name))
}
else if (GL_CheckExtension("GL_ARB_multitexture") && !COM_CheckParm("-noamtex"))
{ //ARB multitexture is the popular choice.
#ifndef qglActiveTextureARB
qglActiveTextureARB = (void *) getglext("glActiveTextureARB");
#endif
qglClientActiveTextureARB = (void *) getglext("glClientActiveTextureARB");
qglMultiTexCoord2fARB = (void *) getglext("glMultiTexCoord2fARB");
qglMultiTexCoord3fARB = (void *) getglext("glMultiTexCoord3fARB");
@ -611,6 +623,7 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name))
mtexid0 = GL_TEXTURE0_ARB;
#ifndef qglActiveTextureARB
if (!qglActiveTextureARB || !qglClientActiveTextureARB || !qglMultiTexCoord2fARB)
{
qglActiveTextureARB = NULL;
@ -625,7 +638,7 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name))
{
Con_DPrintf("ARB Multitexture extensions found. Use -noamtex to disable.\n");
}
#endif
}
/*
else if (GL_CheckExtension("GL_SGIS_multitexture") && !COM_CheckParm("-nomtex"))
@ -712,6 +725,7 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name))
gl_config.arb_texture_cube_map = GL_CheckExtension("GL_ARB_texture_cube_map");
#if !defined(GL_STATIC)
/*vbos*/
if (gl_config.gles && gl_config.glversion >= 2)
{
@ -733,6 +747,7 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name))
qglMapBufferARB = (void *)getglext("glMapBufferARB");
qglUnmapBufferARB = (void *)getglext("glUnmapBufferARB");
}
#endif
#ifdef GL_STATIC
gl_config.arb_shader_objects = true;
@ -1608,11 +1623,13 @@ void GL_Init(void *(*getglfunction) (char *name))
qglEndList = (void*)getglcore("glEndList");
qglCallList = (void*)getglcore("glCallList");
#ifndef GL_STATIC
qglBindBufferARB = (void *)getglext("glBindBufferARB");
if (!qglBindBufferARB)
qglBindBufferARB = (void *)getglext("glBindBuffer");
if (!qglBindBufferARB)
qglBindBufferARB = GL_BindBufferARBStub;
#endif
gl_vendor = qglGetString (GL_VENDOR);
Con_SafePrintf ("GL_VENDOR: %s\n", gl_vendor);

View File

@ -440,12 +440,12 @@ void R_NetGraph (void);
#ifdef GL_STATIC
//these are the functions that are valid in gles2.
//other functions should never actually be used.
#define qglActiveTexture glActiveTexture
#define qglActiveTextureARB glActiveTexture
#define qglAttachShader glAttachShader
#define qglBindAttribLocation glBindAttribLocation
#define qglBindBuffer glBindBuffer
#define qglBindFramebuffer glBindFramebuffer
#define qglBindRenderbuffer glBindRenderbuffer
#define qglBindRenderbufferEXT glBindRenderbuffer
#define qglBindTexture glBindTexture
#define qglBlendColor glBlendColor
#define qglBlendEquation glBlendEquation
@ -454,7 +454,7 @@ void R_NetGraph (void);
#define qglBlendFuncSeparate glBlendFuncSeparate
#define qglBufferData glBufferData
#define qglBufferSubData glBufferSubData
#define qglCheckFramebufferStatus glCheckFramebufferStatus
#define qglCheckFramebufferStatusEXT glCheckFramebufferStatus
#define qglClear glClear
#define qglClearColor glClearColor
#define qglClearDepthf glClearDepthf
@ -486,13 +486,13 @@ void R_NetGraph (void);
#define qglEnableVertexAttribArray glEnableVertexAttribArray
#define qglFinish glFinish
#define qglFlush glFlush
#define qglFramebufferRenderbuffer glFramebufferRenderbuffer
#define qglFramebufferRenderbufferEXT glFramebufferRenderbuffer
#define qglFramebufferTexture2D glFramebufferTexture2D
#define qglFrontFace glFrontFace
#define qglGenBuffers glGenBuffers
#define qglGenerateMipmap glGenerateMipmap
#define qglGenFramebuffers glGenFramebuffers
#define qglGenRenderbuffers glGenRenderbuffers
#define qglGenRenderbuffersEXT glGenRenderbuffers
#define qglGenTextures glGenTextures
#define qglGetActiveAttrib glGetActiveAttrib
#define qglGetActiveUniform glGetActiveUniform
@ -534,7 +534,7 @@ void R_NetGraph (void);
#define qglPolygonOffset glPolygonOffset
#define qglReadPixels glReadPixels
#define qglReleaseShaderCompiler glReleaseShaderCompiler
#define qglRenderbufferStorage glRenderbufferStorage
#define qglRenderbufferStorageEXT glRenderbufferStorage
#define qglSampleCoverage glSampleCoverage
#define qglScissor glScissor
#define qglShaderBinary glShaderBinary
@ -616,6 +616,14 @@ void R_NetGraph (void);
#define qglUniform1iARB glUniform1i
#define qglUniform1fARB glUniform1f
#define qglGenBuffersARB glGenBuffers
#define qglDeleteBuffersARB glDeleteBuffers
#define qglBindBufferARB glBindBuffer
#define qglBufferDataARB glBufferData
#define qglBufferSubDataARB glBufferSubData
#define qglMapBufferARB glMapBuffer
#define qglUnmapBufferARB glUnmapBuffer
#else
extern void (APIENTRY *qglBindTexture) (GLenum target, GLuint texture);
extern void (APIENTRY *qglBlendFunc) (GLenum sfactor, GLenum dfactor);
@ -708,6 +716,14 @@ extern FTEPFNGLGETVERTEXATTRIBIV qglGetVertexAttribiv;
extern FTEPFNGLENABLEVERTEXATTRIBARRAY qglEnableVertexAttribArray;
extern FTEPFNGLDISABLEVERTEXATTRIBARRAY qglDisableVertexAttribArray;
extern void (APIENTRY *qglGenBuffersARB)(GLsizei n, GLuint* ids);
extern void (APIENTRY *qglDeleteBuffersARB)(GLsizei n, GLuint* ids);
extern void (APIENTRY *qglBindBufferARB)(GLenum target, GLuint id);
extern void (APIENTRY *qglBufferDataARB)(GLenum target, GLsizei size, const void* data, GLenum usage);
extern void (APIENTRY *qglBufferSubDataARB)(GLenum target, GLint offset, GLsizei size, void* data);
extern void *(APIENTRY *qglMapBufferARB)(GLenum target, GLenum access);
extern GLboolean (APIENTRY *qglUnmapBufferARB)(GLenum target);
#endif
//non-gles2 gl functions
@ -1045,14 +1061,6 @@ extern void (APIENTRY *qglDrawRangeElements) (GLenum, GLuint, GLuint, GLsizei, G
extern void (APIENTRY *qglEnableClientState) (GLenum array);
extern void (APIENTRY *qglVertexPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
extern void (APIENTRY *qglGenBuffersARB)(GLsizei n, GLuint* ids);
extern void (APIENTRY *qglDeleteBuffersARB)(GLsizei n, GLuint* ids);
extern void (APIENTRY *qglBindBufferARB)(GLenum target, GLuint id);
extern void (APIENTRY *qglBufferDataARB)(GLenum target, GLsizei size, const void* data, GLenum usage);
extern void (APIENTRY *qglBufferSubDataARB)(GLenum target, GLint offset, GLsizei size, void* data);
extern void *(APIENTRY *qglMapBufferARB)(GLenum target, GLenum access);
extern GLboolean (APIENTRY *qglUnmapBufferARB)(GLenum target);
extern void (APIENTRY *qglGenVertexArrays)(GLsizei n, GLuint *arrays);
extern void (APIENTRY *qglBindVertexArray)(GLuint vaoarray);

View File

@ -17,72 +17,6 @@
#include <emscripten/emscripten.h>
#endif
typedef struct
{
vfsfile_t funcs;
unsigned long offset;
unsigned long length;
char data[];
} mfile_t;
static int VFSMEM_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)
{
mfile_t *f = (mfile_t*)file;
if (f->offset >= f->length)
return -1; //eof!
if (bytestoread > f->length - f->offset)
bytestoread = f->length - f->offset; //eof!
memcpy(buffer, &f->data[f->offset], bytestoread);
f->offset += bytestoread;
return bytestoread;
}
static int VFSMEM_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestoread)
{
return 0;
}
static qboolean VFSMEM_Seek (struct vfsfile_s *file, unsigned long pos)
{
mfile_t *f = (mfile_t*)file;
f->offset = pos;
return true;
}
static unsigned long VFSMEM_Tell (struct vfsfile_s *file)
{
mfile_t *f = (mfile_t*)file;
return f->offset;
}
static unsigned long VFSMEM_GetSize (struct vfsfile_s *file)
{
mfile_t *f = (mfile_t*)file;
return f->length;
}
static void VFSMEM_Close(vfsfile_t *file)
{
free(file);
}
static void VFSMEM_Flush(struct vfsfile_s *file)
{
}
static vfsfile_t *VFSMEM_File(void *data, unsigned int datasize)
{
/*create a file which is already unlinked*/
mfile_t *f;
f = malloc(sizeof(*f) + datasize);
if (!f)
return NULL;
f->funcs.ReadBytes = VFSMEM_ReadBytes;
f->funcs.WriteBytes = VFSMEM_WriteBytes;
f->funcs.Seek = VFSMEM_Seek;
f->funcs.Tell = VFSMEM_Tell;
f->funcs.GetLen = VFSMEM_GetSize;
f->funcs.Close = VFSMEM_Close;
f->funcs.Flush = VFSMEM_Flush;
f->offset = 0;
f->length = datasize;
memcpy(f->data, data, datasize);
return &f->funcs;
}
static void DL_Abort(struct dl_download *dl)
{
dl->ctx = NULL;
@ -102,20 +36,23 @@ static void DL_OnLoad(void *c, void *data, int datasize)
else
{
//emscripten does not close the file. plus we seem to end up with infinite loops.
dl->file = VFSMEM_File(data, datasize);
dl->file = FS_OpenTemp();
}
}
if (dl->file)
{
VFS_WRITE(dl->file, data, datasize);
VFS_SEEK(dl->file, 0);
dl->status = DL_FINISHED;
}
else
dl->status = DL_FAILED;
dl->replycode = 200;
#if !MYJS
dl->completed += datasize;
#endif
dl->status = DL_FINISHED;
}
#if MYJS
static void DL_OnError(void *c, int ecode)
@ -124,13 +61,13 @@ static void DL_OnError(void *c)
#endif
{
struct dl_download *dl = c;
Con_Printf("download %p: error\n", dl);
#if MYJS
dl->replycode = ecode;
#else
dl->replycode = 404; //we don't actually know. should we not do this?
#endif
Con_Printf("download %p: error %i\n", dl, dl->replycode);
dl->status = DL_FAILED;
}
static void DL_OnProgress(void *c, int position, int totalsize)

View File

@ -1106,6 +1106,9 @@ static int PDECL qclib_null_printf(const char *s, ...)
{
return 0;
}
#ifdef FTE_TARGET_WEB
#define printf NULL //should be some null wrapper instead
#endif
//defs incase following structure is not passed.
struct edict_s *safesv_edicts;

View File

@ -1686,11 +1686,13 @@ void SV_AcceptMessage(int protocol)
#include <sys/stat.h>
static void SV_CheckRecentCrashes(client_t *tellclient)
{
#ifndef FTE_TARGET_WEB
struct stat sb;
if (-1 != stat("crash.log", &sb))
{
SV_ClientPrintf(tellclient, PRINT_HIGH, "\1WARNING: crash.log exists, dated %s\n", ctime(&sb.st_mtime));
}
#endif
}
#else
static void SV_CheckRecentCrashes(client_t *tellclient)

View File

@ -651,6 +651,9 @@ int Rank_Enumerate (unsigned int first, unsigned int last, void (*callback) (con
void Rank_RankingList_f (void)
{
#if 1
Con_Printf("Fixme\n");
#else
rankinfo_t ri;
int id;
int num;
@ -680,6 +683,7 @@ void Rank_RankingList_f (void)
}
fclose(outfile);
#endif
}
void Rank_Remove_f (void)

272
engine/web/fs_web.c Normal file
View File

@ -0,0 +1,272 @@
#include "quakedef.h"
#include "fs.h"
#if defined(FTE_TARGET_WEB)
#include "ftejslib.h"
#if !defined(_WIN32) || defined(_SDL)
#define FSWEB_OpenPath VFSOS_OpenPath
#endif
#define FSWEB_OpenTemp FS_OpenTemp
typedef struct {
searchpathfuncs_t pub;
int depth;
void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle);
char rootpath[1];
} webpath_t;
typedef struct {
vfsfile_t funcs;
int offset;
int handle;
} vfswebfile_t;
static int QDECL VFSWEB_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)
{
vfswebfile_t *intfile = (vfswebfile_t*)file;
int len = emscriptenfte_buf_read(intfile->handle, intfile->offset, buffer, bytestoread);
intfile->offset += len;
return len;
}
static int QDECL VFSWEB_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestoread)
{
vfswebfile_t *intfile = (vfswebfile_t*)file;
int len = emscriptenfte_buf_write(intfile->handle, intfile->offset, buffer, bytestoread);
intfile->offset += len;
return len;
}
static qboolean QDECL VFSWEB_Seek (struct vfsfile_s *file, unsigned long pos)
{
vfswebfile_t *intfile = (vfswebfile_t*)file;
if (pos < 0)
return 0;
intfile->offset = pos;
return true;
}
static unsigned long QDECL VFSWEB_Tell (struct vfsfile_s *file)
{
vfswebfile_t *intfile = (vfswebfile_t*)file;
return intfile->offset;
}
static void QDECL VFSWEB_Flush(struct vfsfile_s *file)
{
vfswebfile_t *intfile = (vfswebfile_t*)file;
}
static unsigned long QDECL VFSWEB_GetSize (struct vfsfile_s *file)
{
vfswebfile_t *intfile = (vfswebfile_t*)file;
unsigned long l;
l = emscriptenfte_buf_getsize(intfile->handle);
return l;
}
static void QDECL VFSWEB_Close(vfsfile_t *file)
{
vfswebfile_t *intfile = (vfswebfile_t*)file;
emscriptenfte_buf_release(intfile->handle);
Z_Free(file);
}
vfsfile_t *FSWEB_OpenTemp(void)
{
int f;
vfswebfile_t *file;
f = emscriptenfte_buf_create();
if (f == -1)
{
Con_Printf("FSWEB_OpenTemp failed\n");
return NULL;
}
file = Z_Malloc(sizeof(vfswebfile_t));
file->funcs.Close = VFSWEB_Close;
#ifdef _DEBUG
Q_strncpyz(file->funcs.dbgname, "FSWEB_OpenTemp", sizeof(file->funcs.dbgname));
#endif
file->funcs.ReadBytes = VFSWEB_ReadBytes;
file->funcs.WriteBytes = VFSWEB_WriteBytes;
file->funcs.Seek = VFSWEB_Seek;
file->funcs.Tell = VFSWEB_Tell;
file->funcs.GetLen = VFSWEB_GetSize;
file->funcs.Flush = VFSWEB_Flush;
file->handle = f;
return &file->funcs;
}
vfsfile_t *VFSWEB_Open(const char *osname, const char *mode, qboolean *needsflush)
{
int f;
vfswebfile_t *file;
//qboolean read = !!strchr(mode, 'r');
qboolean write = !!strchr(mode, 'w');
qboolean append = !!strchr(mode, 'a');
if (needsflush)
*needsflush = false;
f = emscriptenfte_buf_open(osname, write||append);
if (f == -1)
return NULL;
if (write || append)
{
if (needsflush)
*needsflush = true;
}
file = Z_Malloc(sizeof(vfswebfile_t));
#ifdef _DEBUG
Q_strncpyz(file->funcs.dbgname, osname, sizeof(file->funcs.dbgname));
#endif
file->funcs.ReadBytes = VFSWEB_ReadBytes;
file->funcs.WriteBytes = VFSWEB_WriteBytes;
file->funcs.Seek = VFSWEB_Seek;
file->funcs.Tell = VFSWEB_Tell;
file->funcs.GetLen = VFSWEB_GetSize;
file->funcs.Close = VFSWEB_Close;
file->funcs.Flush = VFSWEB_Flush;
file->handle = f;
if (append)
file->offset = VFSWEB_GetSize(&file->funcs);
return &file->funcs;
}
vfsfile_t *VFSOS_Open(const char *osname, const char *mode)
{
vfsfile_t *f;
qboolean needsflush;
f = VFSWEB_Open(osname, mode, &needsflush);
if (needsflush)
FS_FlushFSHashReally();
return f;
}
static vfsfile_t *QDECL FSWEB_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode)
{
vfsfile_t *f;
webpath_t *sp = (void*)handle;
qboolean needsflush;
f = VFSWEB_Open(loc->rawname, mode, &needsflush);
if (needsflush && sp->AddFileHash)
sp->AddFileHash(sp->depth, loc->rawname, NULL, sp);
return f;
}
static void QDECL FSWEB_ClosePath(searchpathfuncs_t *handle)
{
Z_Free(handle);
}
static qboolean QDECL FSWEB_PollChanges(searchpathfuncs_t *handle)
{
// webpath_t *np = handle;
return true; //can't verify that or not, so we have to assume the worst
}
static int QDECL FSWEB_RebuildFSHash(const char *filename, int filesize, void *data, searchpathfuncs_t *spath)
{
webpath_t *sp = (void*)spath;
void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle) = data;
if (filename[strlen(filename)-1] == '/')
{ //this is actually a directory
char childpath[256];
Q_snprintfz(childpath, sizeof(childpath), "%s*", filename);
Sys_EnumerateFiles(sp->rootpath, childpath, FSWEB_RebuildFSHash, data, spath);
return true;
}
AddFileHash(sp->depth, filename, NULL, sp);
return true;
}
static void QDECL FSWEB_BuildHash(searchpathfuncs_t *handle, int depth, void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle))
{
webpath_t *sp = (void*)handle;
sp->depth = depth;
sp->AddFileHash = AddFileHash;
Sys_EnumerateFiles(sp->rootpath, "*", FSWEB_RebuildFSHash, AddFileHash, handle);
}
static qboolean QDECL FSWEB_FLocate(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult)
{
webpath_t *sp = (void*)handle;
int len;
char netpath[MAX_OSPATH];
if (hashedresult && (void *)hashedresult != handle)
return false;
/*
if (!static_registered)
{ // if not a registered version, don't ever go beyond base
if ( strchr (filename, '/') || strchr (filename,'\\'))
continue;
}
*/
// check a file in the directory tree
snprintf (netpath, sizeof(netpath)-1, "%s/%s", sp->rootpath, filename);
{
vfsfile_t *f = VFSWEB_Open(netpath, "rb", NULL);
if (!f)
return false;
len = VFS_GETLEN(f);
VFS_CLOSE(f);
}
if (loc)
{
loc->len = len;
loc->offset = 0;
loc->index = 0;
Q_strncpyz(loc->rawname, netpath, sizeof(loc->rawname));
}
return true;
}
static void QDECL FSWEB_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer)
{
vfsfile_t *f;
size_t result;
f = VFSWEB_Open(loc->rawname, "rb", NULL);
if (!f) //err...
return;
VFS_SEEK(f, loc->offset);
result = VFS_READ(f, buffer, loc->len); // do soemthing with result
if (result != loc->len)
Con_Printf("FSWEB_ReadFile() fread: Filename: %s, expected %i, result was %u \n",loc->rawname,loc->len,(unsigned int)result);
VFS_CLOSE(f);
}
static int QDECL FSWEB_EnumerateFiles (searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *, int, void *, searchpathfuncs_t *spath), void *parm)
{
webpath_t *sp = (webpath_t*)handle;
return Sys_EnumerateFiles(sp->rootpath, match, func, parm, handle);
}
searchpathfuncs_t *QDECL FSWEB_OpenPath(vfsfile_t *mustbenull, const char *desc)
{
webpath_t *np;
int dlen = strlen(desc);
if (mustbenull)
return NULL;
np = Z_Malloc(sizeof(*np) + dlen);
if (np)
{
np->depth = 0;
memcpy(np->rootpath, desc, dlen+1);
}
np->pub.fsver = FSVER;
np->pub.ClosePath = FSWEB_ClosePath;
np->pub.BuildHash = FSWEB_BuildHash;
np->pub.FindFile = FSWEB_FLocate;
np->pub.ReadFile = FSWEB_ReadFile;
np->pub.EnumerateFiles = FSWEB_EnumerateFiles;
np->pub.OpenVFS = FSWEB_OpenVFS;
np->pub.PollChanges = FSWEB_PollChanges;
return &np->pub;
}
#endif

View File

@ -1,2 +1,32 @@
void emscriptenfte_async_wget_data2(const char *url, void *ctx, void (*onload)(void*ctx,void*buf,int sz), void (*onerror)(void*ctx,int code), void (*onprogress)(void*ctx,int prog,int total));
//filesystem buffers are implemented in javascript so that we are not bound by power-of-two heap limitations quite so much.
//also, we can't use emscripten because it reserves 16m file handles or something.
int emscriptenfte_buf_create(void);
int emscriptenfte_buf_open(const char *name, int createifneeded);
int emscriptenfte_buf_rename(const char *oldname, const char *newname);
int emscriptenfte_buf_delete(const char *fname);
void emscriptenfte_buf_release(int handle);
unsigned int emscriptenfte_buf_getsize(int handle);
int emscriptenfte_buf_read(int handle, int offset, void *data, int len);
int emscriptenfte_buf_write(int handle, int offset, const void *data, int len);
//websocket is implemented in javascript because there is no usable C api (emscripten's javascript implementation is shite).
int emscriptenfte_ws_connect(const char *url);
void emscriptenfte_ws_close(int sockid);
int emscriptenfte_ws_cansend(int sockid, int extra, int maxpending);
int emscriptenfte_ws_send(int sockid, const void *data, int len);
int emscriptenfte_ws_recv(int sockid, void *data, int len);
void Sys_Print(const char *msg);
unsigned long emscriptenfte_ticks_ms(void);
int emscriptenfte_setupcanvas(
int width,
int height,
void(*Resized)(int newwidth, int newheight),
void(*Mouse)(int devid,int abs,float x,float y,float z,float size),
void(*Button)(int devid, int down, int mbutton),
void(*Keyboard)(int devid, int down, int keycode, int unicode)
);

View File

@ -1,57 +1,335 @@
mergeInto(LibraryManager.library,
{
$FTESockets__deps: [],
$FTESockets: {
socks: []
//generic handles array
//yeah, I hope you don't use-after-free. hopefully that sort of thing will be detected on systems with easier non-mangled debuggers.
$FTEH__deps: [],
$FTEH: {
h: [],
f: {}
},
emscriptenfte_ws_connect__deps: ['$FTESockets'],
$FTEC:
{
w: -1,
h: -1,
donecb:0,
evcb: {
resize:0,
mouse:0,
key:0,
},
handleevent : function(event)
{
switch(event.type)
{
case 'resize':
if (FTEC.evcb.resize != 0)
Runtime.dynCall('vii', FTEC.evcb.resize, [Module['canvas'].width, Module['canvas'].height]);
break;
case 'mousemove':
if (FTEC.evcb.mouse != 0)
{
if (Browser.pointerLock)
{
if (typeof event.movementX === 'undefined')
{
event.movementX = event.mozMovementX;
event.movementY = event.mozMovementY;
}
if (typeof event.movementX === 'undefined')
{
event.movementX = event.webkitMovementX;
event.movementY = event.webkitMovementY;
}
Runtime.dynCall('viiffff', FTEC.evcb.mouse, [0, false, event.movementX, event.movementY, 0, 0]);
}
else
Runtime.dynCall('viiffff', FTEC.evcb.mouse, [0, true, event.pageX, event.pageY, 0, 0]);
}
break;
case 'mousedown':
case 'mouseup':
Browser.requestFullScreen(true, true);
Module['canvas'].requestPointerLock();
if (FTEC.evcb.button != 0)
{
Runtime.dynCall('viii', FTEC.evcb.button, [0, event.type=='mousedown', event.button]);
event.preventDefault();
}
break;
case 'mousewheel':
case 'wheel':
if (FTEC.evcb.button != 0)
{
Runtime.dynCall('viii', FTEC.evcb.button, [0, 3, event.deltaY]);
event.preventDefault();
}
break;
case 'mouseout':
if (FTEC.evcb.button != 0)
{
for (var i = 0; i < 8; i++)
Runtime.dynCall('viii', FTEC.evcb.button, [0, false, i]);
}
break;
case 'keypress':
if (FTEC.evcb.key != 0)
{
Runtime.dynCall('viiii', FTEC.evcb.key, [0, 1, 0, event.charCode]);
Runtime.dynCall('viiii', FTEC.evcb.key, [0, 0, 0, event.charCode]);
event.preventDefault();
}
break;
case 'keydown':
case 'keyup':
//122 is 'toggle fullscreen'.
//we don't steal that because its impossible to leave it again once used.
if (FTEC.evcb.key != 0 && event.keyCode != 122)
{
Runtime.dynCall('viiii', FTEC.evcb.key, [0, event.type=='keydown', event.keyCode, 0]);
event.preventDefault();
}
break;
case 'keydown':
default:
console.log(event);
}
}
},
emscriptenfte_setupcanvas__deps: ['$FTEC', '$Browser'],
emscriptenfte_setupcanvas : function(nw,nh,evresz,evm,evb,evk)
{
FTEC.evcb.resize = evresz;
FTEC.evcb.mouse = evm;
FTEC.evcb.button = evb;
FTEC.evcb.key = evk;
if (!FTEC.donecb)
{
FTEC.donecb = 1;
['mousedown', 'mouseup', 'mousemove', 'wheel', 'mousewheel', 'mouseout', 'keydown', 'keyup'].forEach(function(event)
{
Module['canvas'].addEventListener(event, FTEC.handleevent, true);
});
['keydown', 'keyup', 'keypress'].forEach(function(event)
{
document.addEventListener(event, FTEC.handleevent, true);
});
Browser.resizeListeners.push(function(w, h) {
FTEC.handleevent({
type: 'resize',
});
});
}
var ctx = Browser.createContext(Module['canvas'], true, true);
if (!ctx)
return 0;
Browser.setCanvasSize(nw, nh, false);
window.onresize = function()
{
//emscripten's browser library will revert sizes wrongly or something when we're fullscreen, so make sure that doesn't happen.
if (Browser.isFullScreen)
{
Browser.windowedWidth = window.innerWidth;
Browser.windowedHeight = window.innerHeight;
}
else
Browser.setCanvasSize(window.innerWidth, window.innerHeight, false);
};
window.onresize();
return 1;
},
Sys_Print : function(msg)
{
console.log(Pointer_stringify(msg));
},
emscriptenfte_ticks_ms : function()
{
return Date.now();
},
emscriptenfte_handle_alloc__deps : ['$FTEH'],
emscriptenfte_handle_alloc : function(h)
{
for (var i = 0; FTEH.h.length; i+=1)
{
if (FTEH.h[i] == null)
{
FTEH.h[i] = h;
return i;
}
}
i = FTEH.h.length;
FTEH.h[i] = h;
return i;
},
//temp files
emscriptenfte_buf_create__deps : ['emscriptenfte_handle_alloc'],
emscriptenfte_buf_create : function()
{
var b = {h:-1, r:1, l:0,m:4096,d:new Uint8Array(4096), n:null};
b.h = _emscriptenfte_handle_alloc(b);
return b.h;
},
//temp files
emscriptenfte_buf_open__deps : ['emscriptenfte_buf_create'],
emscriptenfte_buf_open : function(name, createifneeded)
{
name = Pointer_stringify(name);
var f = FTEH.f[name];
var r = -1;
if (f == null)
{
if (createifneeded)
r = _emscriptenfte_buf_create();
if (r != -1)
{
f = FTEH.h[r];
f.r+=1;
f.n = name;
FTEH.f[name] = f;
if (FTEH.f[name] != f || f.n != name)
console.log('error creating file '+name);
}
}
else
{
f.r+=1;
r = f.h;
}
return r;
},
emscriptenfte_buf_rename : function(oldname, newname)
{
oldname = Pointer_stringify(oldname);
newname = Pointer_stringify(newname);
var f = FTEH.f[oldname];
if (f == null)
return 0;
if (FTEH.f[newname] != null)
return 0;
FTEH.f[newname] = f;
delete FTEH.f[oldname];
f.n = newname;
return 1;
},
emscriptenfte_buf_delete : function(name)
{
name = Pointer_stringify(name);
var f = FTEH.f[name];
if (f)
{
delete FTEH.f[name];
f.n = null;
emscriptenfte_buf_release(f.h);
console.log('deleted '+name);
return 1;
}
return 0;
},
emscriptenfte_buf_release : function(handle)
{
var b = FTEH.h[handle];
if (b == null)
{
Module.printError('emscriptenfte_buf_release with invalid handle');
return;
}
b.r -= 1;
if (b.r == 0)
{
if (b.n != null)
delete FTEH.f[b.n];
delete FTEH.h[handle];
b.d = null;
}
},
emscriptenfte_buf_getsize : function(handle)
{
var b = FTEH.h[handle];
return b.l;
},
emscriptenfte_buf_read : function(handle, offset, data, len)
{
var b = FTEH.h[handle];
if (offset+len > b.l) //clamp the read
len = b.l - offset;
if (len < 0)
{
len = 0;
if (offset+len >= b.l)
return -1;
}
HEAPU8.set(b.d.subarray(offset, offset+len), data);
return len;
},
emscriptenfte_buf_write : function(handle, offset, data, len)
{
var b = FTEH.h[handle];
if (offset+len > b.m)
{ //extend it if needed.
b.m = offset + len + 4095;
b.m = b.m & ~4095;
var nd = new Uint8Array(b.m);
nd.set(b.d, 0);
b.d = nd;
}
if (len < 0)
len = 0;
console.log('deleted '+name);
b.d.set(HEAPU8.subarray(data, data+len), offset);
if (offset + len > b.l)
b.l = offset + len;
return len;
},
emscriptenfte_ws_connect__deps: ['emscriptenfte_handle_alloc'],
emscriptenfte_ws_connect : function(url)
{
var _url = Pointer_stringify(url);
var s = {ws:null, inq:[], err:0};
for (var i = 0; ; i+=1)
{
if (!FTESockets.socks[i])
s.ws = new WebSocket(_url, 'binary');
if (!s.ws)
return -1;
s.ws.onerror = function(event) {s.err = 1;};
s.ws.onclose = function(event) {s.err = 1;};
// s.ws.onopen = function(event) {};
s.ws.onmessage = function(event)
{
s.ws = new WebSocket(_url, 'binary');
if (!s.ws)
return -1;
FTESockets.socks[i] = s;
s.ws.onerror = function(event) {s.err = 1;};
s.ws.onclose = function(event) {s.err = 1;};
// s.ws.onopen = function(event) {};
s.ws.onmessage = function(event)
{
assert(typeof event.data !== 'string' && event.data.byteLength);
s.inq.push(new Uint8Array(event.data));
};
return i;
}
}
return -1;
assert(typeof event.data !== 'string' && event.data.byteLength);
s.inq.push(new Uint8Array(event.data));
};
return _emscriptenfte_handle_alloc(s);
},
emscriptenfte_ws_close : function(sockid)
{
var s = FTESockets.socks[sockid];
var s = FTEH.h[sockid];
if (!s)
return -1;
s.ws.close();
s.ws = null; //make sure to avoid circular references
FTESockets.socks[sockid] = null; //socked is no longer accessible.
delete FTEH.h[sockid]; //socked is no longer accessible.
return 0;
},
//separate call allows for more sane flood control when fragmentation is involved.
emscriptenfte_ws_cansend : function(sockid, extra, maxpending)
{
var s = FTESockets.socks[sockid];
var s = FTEH.h[sockid];
if (!s)
return 1; //go on punk, make my day.
return ((s.ws.bufferedAmount+extra) < maxpending);
},
emscriptenfte_ws_send : function(sockid, data, len)
{
var s = FTESockets.socks[sockid];
var s = FTEH.h[sockid];
if (!s)
return -1;
s.s.send(HEAPU8.subarray(data, data+len).buffer);
@ -59,7 +337,7 @@ mergeInto(LibraryManager.library,
},
emscriptenfte_ws_recv : function(sockid, data, len)
{
var s = FTESockets.socks[sockid];
var s = FTEH.h[sockid];
if (!s)
return -1;
var inp = s.inq.shift();
@ -80,13 +358,14 @@ mergeInto(LibraryManager.library,
emscriptenfte_async_wget_data2 : function(url, ctx, onload, onerror, onprogress)
{
var _url = Pointer_stringify(url);
console.log("Attempting download of " + _url);
var http = new XMLHttpRequest();
http.open('GET', _url, true);
http.responseType = 'arraybuffer';
http.onload = function(e)
{
console.log("onload: " + _url + " status " + http.status);
if (http.status == 200)
{
var bar = new Uint8Array(http.response);
@ -104,6 +383,7 @@ mergeInto(LibraryManager.library,
http.onerror = function(e)
{
console.log("onerror: " + _url + " status " + http.status);
if (onerror)
Runtime.dynCall('vii', onerror, [ctx, http.status]);
};

View File

@ -5,12 +5,12 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>FTE QuakeWorld</title>
<style>
body { background-color:#000000; color:#808080; }
body { background-color:#000000; color:#808080; height:100%;width:100%;margin:0;padding:0;}
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
div.emscripten { text-align: center; }
div.emscripten_border { border: 1px solid black; }
div.emscripten { text-align: center; padding:0; margin: 0;}
div.emscripten_border { padding:0; margin: 0; }
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
canvas.emscripten { border: 0px none; }
canvas.emscripten { border: 0px none; width:100%; height:100%; padding:0; margin: 0;}
</style>
</head>
<body>
@ -21,14 +21,14 @@
<div class="emscripten_border">
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
</div>
<div class="emscripten">
<!-- <div class="emscripten">
<input type="checkbox" id="resize">Resize canvas
<input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer
&nbsp;&nbsp;&nbsp;
<input type="button" value="Fullscreen" onclick="Module.requestFullScreen(document.getElementById('pointerLock').checked,
document.getElementById('resize').checked)">
</div>
-->
<script type='text/javascript'>
// connect to canvas
var Module = {

214
engine/web/gl_vidweb.c Normal file
View File

@ -0,0 +1,214 @@
#include "quakedef.h"
#include "glquake.h"
#include "web/ftejslib.h"
extern cvar_t vid_hardwaregamma;
extern cvar_t gl_lateswap;
extern int gammaworks;
extern qboolean vid_isfullscreen;
qboolean ActiveApp;
qboolean mouseactive;
extern qboolean mouseusedforgui;
static void *GLVID_getsdlglfunction(char *functionname)
{
return NULL;
}
static void VID_Resized(int width, int height)
{
extern cvar_t vid_conautoscale, vid_conwidth;
vid.pixelwidth = width;
vid.pixelheight = height;
Con_Printf("Resized: %i %i\n", vid.pixelwidth, vid.pixelheight);
Cvar_ForceCallback(&vid_conautoscale);
Cvar_ForceCallback(&vid_conwidth);
}
static unsigned int domkeytoquake(unsigned int code)
{
unsigned int tab[256] =
{
/* 0*/ 0,0,0,0,0,0,0,0, K_BACKSPACE,K_TAB,0,0,0,K_ENTER,0,0,
/* 16*/ K_SHIFT,K_CTRL,K_ALT,K_PAUSE,K_CAPSLOCK,0,0,0,0,0,0,K_ESCAPE,0,0,0,0,
/* 32*/ ' ',K_PGUP,K_PGDN,K_END,K_HOME,K_LEFTARROW,K_UPARROW,K_RIGHTARROW, K_DOWNARROW,0,0,0,K_PRINTSCREEN,K_INS,K_DEL,0,
/* 48*/ '0','1','2','3','4','5','6','7', '8','9',0,0,0,0,0,0,
/* 64*/ 0,'a','b','c','d','e','f','g', 'h','i','j','k','l','m','n','o',
/* 80*/ 'p','q','r','s','t','u','v','w', 'x','y','z',K_LWIN,K_RWIN,K_APP,0,0,
/* 96*/ K_KP_INS,K_KP_END,K_KP_DOWNARROW,K_KP_PGDN,K_KP_LEFTARROW,K_KP_5,K_KP_RIGHTARROW,K_KP_HOME, K_KP_UPARROW,K_KP_PGDN,K_KP_STAR,K_KP_PLUS,0,K_KP_MINUS,K_KP_DEL,K_KP_SLASH,
/*112*/ K_F1,K_F2,K_F3,K_F4,K_F5,K_F6,K_F7,K_F8,K_F9,K_F10,K_F11,K_F12,0,0,0,0,
/*128*/ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
/*144*/ K_KP_NUMLOCK,K_SCRLCK,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
/*160*/ 0,0,0,'#',0,0,0,0, 0,0,0,0,0,0,0,0,
/*176*/ 0,0,0,0,0,0,0,0, 0,0,';','=',',','-','.','/',
/*192*/ '`',0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
/*208*/ 0,0,0,0,0,0,0,0, 0,0,0,'[','\\',']','\'','`',
/*224*/ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
/*240*/ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
};
if (!code)
return 0;
if (code >= sizeof(tab)/sizeof(tab[0]))
{
Con_DPrintf("You just pressed key %u, but I don't know what its meant to be\n", code);
return 0;
}
if (!tab[code])
Con_DPrintf("You just pressed key %u, but I don't know what its meant to be\n", code);
Con_DPrintf("You just pressed dom key %u, which is quake key %u\n", code, tab[code]);
return tab[code];
}
static void DOM_KeyEvent(int devid, int down, int scan, int uni)
{
IN_KeyEvent(0, down, domkeytoquake(scan), uni);
}
static void DOM_ButtonEvent(int devid, int down, int button)
{
if (down == 2)
{
//fixme: the event is a float. we ignore that.
while(button < 0)
{
IN_KeyEvent(0, true, K_MWHEELUP, 0);
button += 1;
}
while(button > 0)
{
IN_KeyEvent(0, true, K_MWHEELDOWN, 0);
button -= 1;
}
}
else
{
//swap buttons 2 and 3, so rmb is still +forward by default and not +mlook.
if (button == 2)
button = 1;
else if (button == 1)
button = 2;
IN_KeyEvent(0, down, K_MOUSE1+button, 0);
}
}
qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
{
int flags;
vid_isfullscreen = true;
if (!emscriptenfte_setupcanvas(
info->width,
info->height,
VID_Resized,
IN_MouseMove,
DOM_ButtonEvent,
DOM_KeyEvent
))
{
Con_Printf("Couldn't set up canvas\n");
return false;
}
ActiveApp = true;
GL_Init(GLVID_getsdlglfunction);
qglViewport (0, 0, vid.pixelwidth, vid.pixelheight);
mouseactive = false;
return true;
}
void GLVID_DeInit (void)
{
ActiveApp = false;
emscriptenfte_setupcanvas(-1, -1, NULL, NULL, NULL, NULL);
}
void GL_BeginRendering (void)
{
}
qboolean screenflush;
void GL_DoSwap (void)
{
if (!screenflush)
return;
screenflush = 0;
//webgl doesn't support swapbuffers.
//you can't use it for loading screens.
//such things must result in waiting until the following frame.
//although there IS a swapped-buffers event, which we should probably use in preference to requestanimationframe or whatever the call is.
/*
if (!vid_isfullscreen)
{
if (!_windowed_mouse.value)
{
if (mouseactive)
{
IN_DeactivateMouse ();
}
}
else
{
if ((key_dest == key_game||mouseusedforgui) && ActiveApp)
IN_ActivateMouse ();
else if (!(key_dest == key_game || mouseusedforgui) || !ActiveApp)
IN_DeactivateMouse ();
}
}
*/
}
void GL_EndRendering (void)
{
screenflush = true;
if (!gl_lateswap.value)
GL_DoSwap();
}
qboolean GLVID_ApplyGammaRamps (unsigned short *ramps)
{
gammaworks = false;
return gammaworks;
}
void GLVID_SetCaption(char *text)
{
// SDL_WM_SetCaption( text, NULL );
}
void Sys_SendKeyEvents(void)
{
/*callbacks happen outside our code, we don't need to poll for events*/
}
/*various stuff for joysticks, which we don't support in this port*/
void INS_Shutdown (void)
{
}
void INS_ReInit (void)
{
}
void INS_Move(float *movements, int pnum)
{
}
void INS_Init (void)
{
}
void INS_Accumulate(void)
{
}
void INS_Commands (void)
{
}

View File

@ -1,6 +1,6 @@
Module['preRun'] = function()
{
FS.createPath('/', 'id1', true, true);
//FS.createPath('/', 'id1', true, true);
//FS.createPath('/', 'qw', true, true);
//FS.createPath('/', 'fte', true, true);

387
engine/web/sys_web.c Normal file
View File

@ -0,0 +1,387 @@
#include "quakedef.h"
#include <SDL.h>
#ifdef MULTITHREAD
#include <SDL_thread.h>
#endif
#include <SDL_loadso.h>
#include <emscripten/emscripten.h>
#include "ftejslib.h"
#ifndef isDedicated
qboolean isDedicated;
#endif
void Sys_Error (const char *error, ...)
{
va_list argptr;
char string[1024];
va_start (argptr,error);
vsnprintf (string, sizeof (string), error, argptr);
va_end (argptr);
Sys_Printf("Error: %s\n", string);
Con_Print ("Quake Error: ");
Con_Print (string);
Con_Print ("\n");
if (COM_CheckParm("-crashonerror"))
*(int*)-3 = 0;
Host_Shutdown ();
exit (1);
}
void Sys_RecentServer(char *command, char *target, char *title, char *desc)
{
}
qboolean Sys_RandomBytes(qbyte *string, int len)
{
return false;
}
//print into stdout
void Sys_Printf (char *fmt, ...)
{
va_list argptr;
char buf[1024];
va_start (argptr,fmt);
vsnprintf (buf, sizeof(buf), fmt, argptr);
Sys_Print(buf);
va_end (argptr);
}
unsigned int Sys_Milliseconds(void)
{
static int first = true;
static unsigned long oldtime = 0, curtime = 0;
unsigned long newtime;
newtime = emscriptenfte_ticks_ms(); //return Date.now()
if (first)
{
first = false;
oldtime = newtime;
}
if (newtime < oldtime)
Con_Printf("Sys_Milliseconds stepped backwards!\n");
else
curtime += newtime - oldtime;
oldtime = newtime;
return curtime;
}
//return the current time, in the form of a double
double Sys_DoubleTime (void)
{
return Sys_Milliseconds() / 1000.0;
}
//create a directory
void Sys_mkdir (char *path)
{
}
//unlink a file
qboolean Sys_remove (char *path)
{
emscriptenfte_buf_delete(path);
return true;
}
qboolean Sys_Rename (char *oldfname, char *newfname)
{
return emscriptenfte_buf_rename(oldfname, newfname);
return false;
}
//someone used the 'quit' command
void Sys_Quit (void)
{
Host_Shutdown();
exit (0);
}
int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, int, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath)
{
Con_DPrintf("Warning: Sys_EnumerateFiles not implemented\n");
return false;
}
//blink window if possible (it's not)
void Sys_ServerActivity(void)
{
}
void Sys_CloseLibrary(dllhandle_t *lib)
{
}
dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)
{
return NULL;
}
void *Sys_GetAddressForName(dllhandle_t *module, const char *exportname)
{
return NULL;
}
void Sys_Init(void)
{
}
void Sys_Shutdown(void)
{
}
int VARGS Sys_DebugLog(char *file, char *fmt, ...)
{
};
qboolean Sys_InitTerminal(void)
{
return true;
}
char *Sys_ConsoleInput(void)
{
return NULL;
}
void Sys_CloseTerminal (void)
{
}
void Sys_MainLoop(void)
{
static float oldtime;
float newtime, time;
newtime = Sys_DoubleTime ();
if (!oldtime)
oldtime = newtime;
time = newtime - oldtime;
Host_Frame (time);
oldtime = newtime;
}
int QDECL main(int argc, char **argv)
{
float time, newtime, oldtime;
quakeparms_t parms;
int t;
int delay = 1;
memset(&parms, 0, sizeof(parms));
parms.basedir = "";
parms.argc = argc;
parms.argv = (const char**)argv;
COM_InitArgv (parms.argc, parms.argv);
TL_InitLanguages();
Sys_Printf ("Host_Init\n");
Host_Init (&parms);
oldtime = Sys_DoubleTime ();
//-1 fps should give vsync
emscripten_set_main_loop(Sys_MainLoop, -1, false);
return 0;
}
qboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate)
{
return false;
}
static char *clipboard_buffer;
char *Sys_GetClipboard(void)
{
return clipboard_buffer;
}
void Sys_CloseClipboard(char *bf)
{
}
void Sys_SaveClipboard(char *text)
{
free(clipboard_buffer);
clipboard_buffer = strdup(text);
}
#ifdef MULTITHREAD
/* Thread creation calls */
void *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority, int stacksize)
{
// SDL threads do not support setting thread stack size
return (void *)SDL_CreateThread(func, args);
}
void Sys_WaitOnThread(void *thread)
{
SDL_WaitThread((SDL_Thread *)thread, NULL);
}
/* Mutex calls */
// SDL mutexes don't have try-locks for mutexes in the spec so we stick with 1-value semaphores
void *Sys_CreateMutex(void)
{
return (void *)SDL_CreateSemaphore(1);
}
qboolean Sys_TryLockMutex(void *mutex)
{
return !SDL_SemTryWait(mutex);
}
qboolean Sys_LockMutex(void *mutex)
{
return !SDL_SemWait(mutex);
}
qboolean Sys_UnlockMutex(void *mutex)
{
return !SDL_SemPost(mutex);
}
void Sys_DestroyMutex(void *mutex)
{
SDL_DestroySemaphore(mutex);
}
/* Conditional wait calls */
typedef struct condvar_s
{
SDL_mutex *mutex;
SDL_cond *cond;
} condvar_t;
void *Sys_CreateConditional(void)
{
condvar_t *condv;
SDL_mutex *mutex;
SDL_cond *cond;
condv = (condvar_t *)malloc(sizeof(condvar_t));
if (!condv)
return NULL;
mutex = SDL_CreateMutex();
cond = SDL_CreateCond();
if (mutex)
{
if (cond)
{
condv->cond = cond;
condv->mutex = mutex;
return (void *)condv;
}
else
SDL_DestroyMutex(mutex);
}
free(condv);
return NULL;
}
qboolean Sys_LockConditional(void *condv)
{
return !SDL_mutexP(((condvar_t *)condv)->mutex);
}
qboolean Sys_UnlockConditional(void *condv)
{
return !SDL_mutexV(((condvar_t *)condv)->mutex);
}
qboolean Sys_ConditionWait(void *condv)
{
return !SDL_CondWait(((condvar_t *)condv)->cond, ((condvar_t *)condv)->mutex);
}
qboolean Sys_ConditionSignal(void *condv)
{
return !SDL_CondSignal(((condvar_t *)condv)->cond);
}
qboolean Sys_ConditionBroadcast(void *condv)
{
return !SDL_CondBroadcast(((condvar_t *)condv)->cond);
}
void Sys_DestroyConditional(void *condv)
{
condvar_t *cv = (condvar_t *)condv;
SDL_DestroyCond(cv->cond);
SDL_DestroyMutex(cv->mutex);
free(cv);
}
#endif
void Sys_Sleep (double seconds)
{
SDL_Delay(seconds * 1000);
}
//emscripten does not support the full set of sdl functions, so we stub the extras.
int SDL_GetGammaRamp(Uint16 *redtable, Uint16 *greentable, Uint16 *bluetable)
{
return -1;
}
int SDL_SetGammaRamp(const Uint16 *redtable, const Uint16 *greentable, const Uint16 *bluetable)
{
return -1;
}
//SDL_GL_GetAttribute
void SDL_UnloadObject(void *object)
{
}
void *SDL_LoadObject(const char *sofile)
{
return NULL;
}
void *SDL_LoadFunction(void *handle, const char *name)
{
return NULL;
}
Uint8 SDL_GetAppState(void)
{
return SDL_APPACTIVE;
}
#define socklen_t int
int getsockname(int socket, struct sockaddr *address, socklen_t *address_len)
{
return -1;
}
int getpeername(int socket, struct sockaddr *address, socklen_t *address_len)
{
return -1;
}
ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len)
{
return -1;
}