#include "quakedef.h" #ifdef MENU_NATIVECODE static dllhandle_t *libmenu; menu_export_t *mn_entry; extern unsigned int r2d_be_flags; #include "pr_common.h" #include "shader.h" #include "cl_master.h" static int MN_CheckExtension(const char *extname) { unsigned int i; for (i = 0; i < QSG_Extensions_count; i++) { if (!strcmp(QSG_Extensions[i].name, extname)) return true; } return false; } static void MN_LocalCmd(const char *text) { Cbuf_AddText(text, RESTRICT_LOCAL); //menus are implicitly trusted. latching and other stuff would be a nightmare otherwise. } static const char *MN_Cvar_String(const char *cvarname, qboolean effective) { cvar_t *cv = Cvar_FindVar(cvarname); if (cv) { //some cvars don't change instantly, giving them (temporary) effective values that are different from their intended values. if (cv->latched_string && !effective) return cv->latched_string; return cv->string; } else return NULL; } static const char *MN_Cvar_GetDefault(const char *cvarname) { cvar_t *cv = Cvar_FindVar(cvarname); if (cv) return cv->defaultstr?cv->defaultstr:""; else return NULL; } static void MN_RegisterCvar(const char *cvarname, const char *defaulttext, unsigned int flags, const char *description) { Cvar_Get2(cvarname, defaulttext, flags, description, NULL); } void Cmd_DeleteAlias(const char *name); static void MN_RegisterCommand(const char *commandname, const char *description) { Cmd_DeleteAlias(commandname); //menuqc has no real way to register commands, so it has a nasty habit of creating loads of weird awkward aliases. Cmd_AddCommandD(commandname, NULL, description); } static int MN_GetServerState(void) { if (!sv.active) return 0; if (svs.allocated_client_slots <= 1) return 1; return 2; } static int MN_GetClientState(void) { if (cls.state >= ca_active) return 2; if (cls.state != ca_disconnected) return 1; return 0; } static void MN_fclose(vfsfile_t *f) { VFS_CLOSE(f); } static shader_t *MN_CachePic(const char *picname) { return R2D_SafeCachePic(picname); } static qboolean MN_DrawGetImageSize(struct shader_s *pic, int *w, int *h) { return R_GetShaderSizes(pic, w, h, true)>0; } static void MN_DrawQuad(const vec2_t position[4], const vec2_t texcoords[4], shader_t *pic, const vec4_t rgba, unsigned int be_flags) { extern shader_t *shader_draw_fill, *shader_draw_fill_trans; r2d_be_flags = be_flags; if (!pic) pic = rgba[3]==1?shader_draw_fill:shader_draw_fill_trans; R2D_ImageColours(rgba[0], rgba[1], rgba[2], rgba[3]); R2D_Image2dQuad(position, texcoords, pic); r2d_be_flags = 0; } static float MN_DrawString(const vec2_t position, const char *text, struct font_s *font, float height, const vec4_t rgba, unsigned int be_flags) { float px, py, ix; unsigned int codeflags, codepoint; conchar_t buffer[2048], *str = buffer; if (!font) font = font_default; COM_ParseFunString(CON_WHITEMASK, text, buffer, sizeof(buffer), false); R2D_ImageColours(rgba[0], rgba[1], rgba[2], rgba[3]); Font_BeginScaledString(font, position[0], position[1], height, height, &px, &py); ix=px; while(*str) { str = Font_Decode(str, &codeflags, &codepoint); px = Font_DrawScaleChar(px, py, codeflags, codepoint); } Font_EndString(font); return ((px-ix)*(float)vid.width)/(float)vid.rotpixelwidth; } static float MN_StringWidth(const char *text, struct font_s *font, float height) { float px, py; conchar_t buffer[2048], *end; if (!font) font = font_default; end = COM_ParseFunString(CON_WHITEMASK, text, buffer, sizeof(buffer), false); Font_BeginScaledString(font, 0, 0, height, height, &px, &py); px = Font_LineScaleWidth(buffer, end); Font_EndString(font); return (px * (float)vid.width) / (float)vid.rotpixelwidth; } static void MN_DrawSetClipArea(float x, float y, float width, float height) { srect_t srect; if (R2D_Flush) R2D_Flush(); srect.x = x / (float)vid.fbvwidth; srect.y = y / (float)vid.fbvheight; srect.width = width / (float)vid.fbvwidth; srect.height = height / (float)vid.fbvheight; srect.dmin = -99999; srect.dmax = 99999; srect.y = (1-srect.y) - srect.height; BE_Scissor(&srect); } static void MN_DrawResetClipArea(void) { if (R2D_Flush) R2D_Flush(); BE_Scissor(NULL); } static qboolean MN_SetKeyDest(qboolean focused) { qboolean ret = Key_Dest_Has(kdm_nmenu); if (ret == focused) return false; if (focused) { if (key_dest_absolutemouse & kdm_nmenu) { //we're activating the mouse cursor now... make sure the position is actually current. //FIXME: we should probably get the input code to do this for us when switching cursor modes. struct menu_inputevent_args_s ev = {MIE_MOUSEABS, -1}; ev.mouse.screen[0] = (mousecursor_x * vid.width) / vid.pixelwidth; ev.mouse.screen[1] = (mousecursor_y * vid.height) / vid.pixelheight; mn_entry->InputEvent(ev); } Key_Dest_Add(kdm_nmenu); } else Key_Dest_Remove(kdm_nmenu); return true; } static int MN_GetKeyDest(void) { if (Key_Dest_Has(kdm_nmenu)) { if (Key_Dest_Has_Higher(kdm_nmenu)) return -1; return 1; } return 0; } static int MN_SetMouseTarget(const char *cursorname, float hot_x, float hot_y, float scale) { if (cursorname) { struct key_cursor_s *m = &key_customcursor[kc_nmenu]; if (scale <= 0) scale = 1; if (!strcmp(m->name, cursorname) || m->hotspot[0] != hot_x || m->hotspot[1] != hot_y || m->scale != scale) { Q_strncpyz(m->name, cursorname, sizeof(m->name)); m->hotspot[0] = hot_x; m->hotspot[1] = hot_y; m->scale = scale; m->dirty = true; } key_dest_absolutemouse |= kdm_nmenu; } else key_dest_absolutemouse &= ~kdm_nmenu; return true; } static model_t *MN_CacheModel(const char *name) { return Mod_ForName(name, MLV_SILENT); } static qboolean MN_GetModelSize(model_t *model, vec3_t out_mins, vec3_t out_maxs) { if (model) { while(model->loadstate == MLS_LOADING) COM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING); VectorCopy(model->mins, out_mins); VectorCopy(model->maxs, out_maxs); return model->loadstate == MLS_LOADED; } VectorClear(out_mins); VectorClear(out_maxs); return false; } static void MN_RenderScene(menuscene_t *scene) { int i; entity_t ent; menuentity_t *e; if (R2D_Flush) R2D_Flush(); CL_ClearEntityLists(); memset(&ent, 0, sizeof(ent)); for (i = 0; i < scene->numentities; i++) { e = &scene->entlist[i]; ent.keynum = i; ent.model = scene->entlist[i].model; VectorCopy(e->matrix[0], ent.axis[0]); ent.origin[0] = e->matrix[0][3]; VectorCopy(e->matrix[1], ent.axis[1]); ent.origin[1] = e->matrix[1][3]; VectorCopy(e->matrix[2], ent.axis[2]); ent.origin[2] = e->matrix[2][3]; ent.scale = 1; ent.framestate.g[FS_REG].frame[0] = e->frame[0]; ent.framestate.g[FS_REG].frame[1] = e->frame[1]; ent.framestate.g[FS_REG].lerpweight[1] = e->frameweight[0]; ent.framestate.g[FS_REG].lerpweight[0] = e->frameweight[1]; ent.framestate.g[FS_REG].frametime[0] = e->frametime[0]; ent.framestate.g[FS_REG].frametime[1] = e->frametime[1]; ent.playerindex = -1; ent.topcolour = TOP_DEFAULT; ent.bottomcolour = BOTTOM_DEFAULT; Vector4Set(ent.shaderRGBAf, 1, 1, 1, 1); VectorSet(ent.glowmod, 1, 1, 1); #ifdef HEXEN2 ent.drawflags = SCALE_ORIGIN_ORIGIN; ent.abslight = 0; #endif ent.skinnum = 0; ent.fatness = 0; ent.forcedshader = NULL; ent.customskin = 0; V_AddAxisEntity(&ent); } VectorCopy(scene->viewmatrix[0], r_refdef.viewaxis[0]); r_refdef.vieworg[0] = scene->viewmatrix[0][3]; VectorCopy(scene->viewmatrix[1], r_refdef.viewaxis[1]); r_refdef.vieworg[1] = scene->viewmatrix[1][3]; VectorCopy(scene->viewmatrix[2], r_refdef.viewaxis[2]); r_refdef.vieworg[2] = scene->viewmatrix[2][3]; r_refdef.viewangles[0] = -(atan2(r_refdef.viewaxis[0][2], sqrt(r_refdef.viewaxis[0][1]*r_refdef.viewaxis[0][1]+r_refdef.viewaxis[0][0]*r_refdef.viewaxis[0][0])) * 180 / M_PI); r_refdef.viewangles[1] = (atan2(r_refdef.viewaxis[0][1], r_refdef.viewaxis[0][0]) * 180 / M_PI); r_refdef.viewangles[2] = 0; r_refdef.flags = 0; if (scene->worldmodel && scene->worldmodel == cl.worldmodel) r_refdef.flags &= ~RDF_NOWORLDMODEL; else r_refdef.flags |= RDF_NOWORLDMODEL; r_refdef.fovv_x = r_refdef.fov_x = scene->fov[0]; r_refdef.fovv_y = r_refdef.fov_y = scene->fov[1]; r_refdef.vrect.x = scene->pos[0]; r_refdef.vrect.y = scene->pos[1]; r_refdef.vrect.width = scene->size[0]; r_refdef.vrect.height = scene->size[1]; r_refdef.time = scene->time; r_refdef.useperspective = true; r_refdef.mindist = bound(0.1, gl_mindist.value, 4); r_refdef.maxdist = gl_maxdist.value; r_refdef.playerview = &cl.playerview[0]; memset(&r_refdef.globalfog, 0, sizeof(r_refdef.globalfog)); r_refdef.areabitsknown = false; R_RenderView(); r_refdef.playerview = NULL; r_refdef.time = 0; } void MN_Shutdown(void) { Key_Dest_Remove(kdm_nmenu); if (mn_entry) { mn_entry->Shutdown(MI_INIT); mn_entry = NULL; } if (libmenu) { Sys_CloseLibrary(libmenu); libmenu = NULL; } } qboolean MN_Init(void) { menu_export_t *(QDECL *pGetMenuAPI) ( menu_import_t *import ); static menu_import_t imports = { NATIVEMENU_API_VERSION_MAX, NULL, MN_CheckExtension, Host_Error, Con_Printf, Con_DPrintf, MN_LocalCmd, Cvar_VariableValue, MN_Cvar_String, MN_Cvar_GetDefault, Cvar_SetNamed, MN_RegisterCvar, MN_RegisterCommand, COM_ParseType, MN_GetServerState, MN_GetClientState, S_LocalSound2, // file input / search crap FS_OpenVFS, MN_fclose, VFS_GETS, VFS_PRINTF, COM_EnumerateFiles, // Drawing stuff MN_DrawSetClipArea, MN_DrawResetClipArea, //pics MN_CachePic, MN_DrawGetImageSize, MN_DrawQuad, //strings MN_DrawString, MN_StringWidth, Font_LoadFont, Font_Free, //3d stuff MN_CacheModel, MN_GetModelSize, MN_RenderScene, // Menu specific stuff MN_SetKeyDest, MN_GetKeyDest, MN_SetMouseTarget, Key_KeynumToString, Key_StringToKeynum, M_FindKeysForBind, // Server browser stuff Master_KeyForName, Master_SortedServer, Master_ReadKeyString, Master_ReadKeyFloat, Master_ClearMasks, Master_SetMaskString, Master_SetMaskInteger, Master_SetSortField, Master_SortServers, MasterInfo_Refresh, CL_QueryServers, }; dllfunction_t funcs[] = { {(void*)&pGetMenuAPI, "GetMenuAPI"}, {NULL} }; void *iterator = NULL; char syspath[MAX_OSPATH]; char gamepath[MAX_QPATH]; while(COM_IteratePaths(&iterator, syspath, sizeof(syspath), gamepath, sizeof(gamepath))) { if (!com_nogamedirnativecode.ival) libmenu = Sys_LoadLibrary(va("%smenu_"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, syspath), funcs); if (libmenu) break; if (host_parms.binarydir && !strchr(gamepath, '/') && !strchr(gamepath, '\\')) libmenu = Sys_LoadLibrary(va("%smenu_%s_"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, host_parms.binarydir, gamepath), funcs); if (libmenu) break; //some build systems don't really know the cpu type. if (host_parms.binarydir && !strchr(gamepath, '/') && !strchr(gamepath, '\\')) libmenu = Sys_LoadLibrary(va("%smenu_%s" ARCH_DL_POSTFIX, host_parms.binarydir, gamepath), funcs); if (libmenu) break; } if (libmenu) { imports.engine_version = version_string(); key_dest_absolutemouse |= kdm_nmenu; mn_entry = pGetMenuAPI (&imports); if (mn_entry && mn_entry->api_version >= NATIVEMENU_API_VERSION_MIN && mn_entry->api_version <= NATIVEMENU_API_VERSION_MAX) { mn_entry->Init(0, vid.width, vid.height, vid.pixelwidth, vid.pixelheight); return true; } else mn_entry = NULL; MN_Shutdown(); Sys_CloseLibrary(libmenu); libmenu = NULL; } return false; } #endif