/* Copyright (C) 1996-1997 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // r_screen.c -- master for refresh, status bar, console, chat, notify, etc #include "quakedef.h" #include "r_local.h" #ifdef _WIN32 #include "movie.h" #endif // only the refresh window will be updated unless these variables are flagged int scr_copytop; int scr_copyeverything; float scr_con_current; float scr_conlines; // lines of console to display float oldscreensize, oldfov; float oldsbar; // by joe cvar_t scr_viewsize = {"viewsize", "100", CVAR_ARCHIVE}; cvar_t scr_fov = {"fov", "90", CVAR_ARCHIVE}; // 10 - 170 cvar_t scr_consize = {"scr_consize", "0.5"}; // by joe cvar_t scr_conspeed = {"scr_conspeed", "1000"}; cvar_t scr_centertime = {"scr_centertime", "2"}; cvar_t scr_showram = {"showram", "1"}; cvar_t scr_showturtle = {"showturtle", "0"}; cvar_t scr_showpause = {"showpause", "1"}; cvar_t scr_printspeed = {"scr_printspeed", "8"}; cvar_t scr_sshot_format = {"scr_sshot_format", "png"}; extern cvar_t crosshair; extern cvar_t cl_crossx; extern cvar_t cl_crossy; qboolean scr_initialized; // ready to draw mpic_t *scr_ram; mpic_t *scr_net; mpic_t *scr_turtle; int scr_fullupdate; int clearconsole; int clearnotify; viddef_t vid; // global video state vrect_t scr_vrect; qboolean scr_disabled_for_loading; qboolean scr_drawloading; float scr_disabled_time; qboolean scr_skipupdate; qboolean block_drawing; void SCR_ScreenShot_f (void); /* =============================================================================== CENTER PRINTING =============================================================================== */ char scr_centerstring[1024]; float scr_centertime_start; // for slow victory printing float scr_centertime_off; int scr_center_lines; int scr_erase_lines; int scr_erase_center; /* ============== SCR_CenterPrint Called for important messages that should stay in the center of the screen for a few moments ============== */ void SCR_CenterPrint (char *str) { strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1); scr_centertime_off = scr_centertime.value; scr_centertime_start = cl.time; // count the number of lines for centering scr_center_lines = 1; while (*str) { if (*str == '\n') scr_center_lines++; str++; } } void SCR_EraseCenterString (void) { int y; if (scr_erase_center++ > vid.numpages) { scr_erase_lines = 0; return; } if (scr_center_lines <= 4) y = vid.height*0.35; else y = 48; scr_copytop = 1; Draw_TileClear (0, y,vid.width, 8*scr_erase_lines); } void SCR_DrawCenterString (void) { char *start; int l, j, x, y, remaining; // the finale prints the characters one at a time if (cl.intermission) remaining = scr_printspeed.value * (cl.time - scr_centertime_start); else remaining = 9999; scr_erase_center = 0; start = scr_centerstring; if (scr_center_lines <= 4) y = vid.height*0.35; else y = 48; do { // scan the width of the line for (l=0 ; l<40 ; l++) if (start[l] == '\n' || !start[l]) break; x = (vid.width - l*8)/2; for (j=0 ; j scr_erase_lines) scr_erase_lines = scr_center_lines; scr_centertime_off -= host_frametime; if (scr_centertime_off <= 0 && !cl.intermission) return; if (key_dest != key_game) return; SCR_DrawCenterString (); } //============================================================================= /* ==================== CalcFov ==================== */ float CalcFov (float fov_x, float width, float height) { float a, x; if (fov_x < 1 || fov_x > 179) Sys_Error ("Bad fov: %f", fov_x); x = width / tan(fov_x / 360*M_PI); a = atan(height/x); a = a * 360 / M_PI; return a; } /* ================= SCR_CalcRefdef Must be called whenever vid changes Internal use only ================= */ static void SCR_CalcRefdef (void) { vrect_t vrect; float size; scr_fullupdate = 0; // force a background redraw vid.recalc_refdef = 0; // force the status bar to redraw Sbar_Changed (); //======================================== // bound viewsize if (scr_viewsize.value < 30) Cvar_SetValue (&scr_viewsize, 30); if (scr_viewsize.value > 120) Cvar_SetValue (&scr_viewsize, 120); // bound field of view if (scr_fov.value < 10) Cvar_SetValue (&scr_fov, 10); if (scr_fov.value > 170) Cvar_SetValue (&scr_fov, 170); r_refdef.fov_x = scr_fov.value; r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height); // intermission is always full screen if (cl.intermission) size = 120; else size = scr_viewsize.value; if (size >= 120) sb_lines = 0; // no status bar at all else if (size >= 110) sb_lines = 24; // no inventory else sb_lines = 24+16+8; // these calculations mirror those in R_Init() for r_refdef, but take no // account of water warping vrect.x = 0; vrect.y = 0; vrect.width = vid.width; vrect.height = vid.height; R_SetVrect (&vrect, &scr_vrect, sb_lines); // guard against going from one mode to another that's less than half the // vertical resolution if (scr_con_current > vid.height) scr_con_current = vid.height; // notify the refresh of the change R_ViewChanged (&vrect, sb_lines, vid.aspect); } /* ================= SCR_SizeUp_f Keybinding command ================= */ void SCR_SizeUp_f (void) { Cvar_SetValue (&scr_viewsize, scr_viewsize.value + 10); vid.recalc_refdef = 1; } /* ================= SCR_SizeDown_f Keybinding command ================= */ void SCR_SizeDown_f (void) { Cvar_SetValue (&scr_viewsize, scr_viewsize.value - 10); vid.recalc_refdef = 1; } //============================================================================ /* ================== SCR_Init ================== */ void SCR_Init (void) { Cvar_Register (&scr_fov); Cvar_Register (&scr_viewsize); Cvar_Register (&scr_consize); Cvar_Register (&scr_conspeed); Cvar_Register (&scr_showram); Cvar_Register (&scr_showturtle); Cvar_Register (&scr_showpause); Cvar_Register (&scr_centertime); Cvar_Register (&scr_printspeed); Cvar_Register (&scr_sshot_format); // register our commands Cmd_AddCommand ("screenshot", SCR_ScreenShot_f); Cmd_AddCommand ("sizeup", SCR_SizeUp_f); Cmd_AddCommand ("sizedown", SCR_SizeDown_f); scr_ram = Draw_PicFromWad ("ram"); scr_net = Draw_PicFromWad ("net"); scr_turtle = Draw_PicFromWad ("turtle"); #ifdef _WIN32 Movie_Init (); #endif scr_initialized = true; } /* ============== SCR_DrawRam ============== */ void SCR_DrawRam (void) { if (!scr_showram.value) return; if (!r_cache_thrash) return; Draw_Pic (scr_vrect.x + 32, scr_vrect.y, scr_ram); } /* ============== SCR_DrawTurtle ============== */ void SCR_DrawTurtle (void) { static int count; if (!scr_showturtle.value) return; if (host_frametime < 0.1) { count = 0; return; } count++; if (count < 3) return; Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle); } /* ============== SCR_DrawNet ============== */ void SCR_DrawNet (void) { if (realtime - cl.last_received_message < 0.3) return; if (cls.demoplayback) return; Draw_Pic (scr_vrect.x + 64, scr_vrect.y, scr_net); } /* ============== SCR_DrawPause ============== */ void SCR_DrawPause (void) { mpic_t *pic; if (!scr_showpause.value) // turn off for screenshots return; if (!cl.paused) return; pic = Draw_CachePic ("gfx/pause.lmp"); Draw_Pic ((vid.width - pic->width)/2, (vid.height - 48 - pic->height)/2, pic); } /* ============== SCR_DrawLoading ============== */ void SCR_DrawLoading (void) { mpic_t *pic; if (!scr_drawloading) return; pic = Draw_CachePic ("gfx/loading.lmp"); Draw_Pic ((vid.width - pic->width)/2, (vid.height - 48 - pic->height)/2, pic); } //============================================================================= /* ================== SCR_SetUpToDrawConsole ================== */ void SCR_SetUpToDrawConsole (void) { Con_CheckResize (); if (scr_drawloading) return; // never a console with loading plaque // decide on the height of the console con_forcedup = !cl.worldmodel || cls.signon != SIGNONS; if (con_forcedup) { scr_conlines = vid.height; // full screen scr_con_current = scr_conlines; } else if (key_dest == key_console) // added by joe { scr_conlines = vid.height * scr_consize.value; if (scr_conlines < 30) scr_conlines = 30; if (scr_conlines > vid.height - 10) scr_conlines = vid.height - 10; } else { scr_conlines = 0; // none visible } if (scr_conlines < scr_con_current) { scr_con_current -= scr_conspeed.value*host_frametime; if (scr_conlines > scr_con_current) scr_con_current = scr_conlines; } else if (scr_conlines > scr_con_current) { scr_con_current += scr_conspeed.value*host_frametime; if (scr_conlines < scr_con_current) scr_con_current = scr_conlines; } if (clearconsole++ < vid.numpages) { scr_copytop = 1; Draw_TileClear (0, (int)scr_con_current, vid.width, vid.height - (int)scr_con_current); Sbar_Changed (); } else if (clearnotify++ < vid.numpages) { scr_copytop = 1; Draw_TileClear (0, 0, vid.width, con_notifylines); } else { con_notifylines = 0; } } /* ================== SCR_DrawConsole ================== */ void SCR_DrawConsole (void) { if (scr_con_current) { scr_copyeverything = 1; Con_DrawConsole (scr_con_current, true); clearconsole = 0; } else { if (key_dest == key_game || key_dest == key_message) Con_DrawNotify (); // only draw notify in game } } /* ============================================================================== SCREEN SHOTS ============================================================================== */ int SCR_ScreenShot (char *name) { qboolean ok = false; char *ext; D_EnableBackBufferAccess (); // enable direct drawing of console to back buffer ext = COM_FileExtension (name); if (!Q_strcasecmp(ext, "png")) ok = Image_WritePNGPLTE (name, png_compression_level.value, vid.buffer, vid.width, vid.height, vid.rowbytes, current_pal); else ok = Image_WritePCX (name, vid.buffer, vid.width, vid.height, vid.rowbytes, current_pal); D_DisableBackBufferAccess (); // for adapters that can't stay mapped in for linear writes all the time return ok; } /* ================== SCR_ScreenShot_f ================== */ void SCR_ScreenShot_f (void) { int i, success; char name[MAX_OSPATH], ext[4], sshot_dir[MAX_OSPATH]; snprintf(sshot_dir, sizeof(sshot_dir), "%s/shots", com_gamedir); if (Cmd_Argc() == 2) { Q_strncpyz (name, Cmd_Argv(1), sizeof(name)); } else if (Cmd_Argc() == 1) { // find a file name to save it to if (!Q_strcasecmp(scr_sshot_format.string, "png")) Q_strncpyz (ext, "png", 4); else Q_strncpyz (ext, "pcx", 4); for (i=0 ; i<999 ; i++) { Q_snprintfz (name, sizeof(name), "joequake%03i.%s", i, ext); if (Sys_FileTime(va("%s/%s", sshot_dir, name)) == -1) break; // file doesn't exist } if (i == 1000) { Con_Printf ("Error: Cannot create more than 1000 screenshots\n"); return; } } else { Con_Printf ("Usage: %s [filename]", Cmd_Argv(0)); return; } success = SCR_ScreenShot (va("%s/%s", sshot_dir, name)); Con_Printf ("%s %s\n", success ? "Wrote" : "Couldn't write", name); } //============================================================================= /* =============== SCR_BeginLoadingPlaque ================ */ void SCR_BeginLoadingPlaque (void) { S_StopAllSounds (true); if (cls.state != ca_connected || cls.signon != SIGNONS) return; // redraw with no console and the loading plaque Con_ClearNotify (); scr_centertime_off = 0; scr_con_current = 0; scr_drawloading = true; scr_fullupdate = 0; Sbar_Changed (); SCR_UpdateScreen (); scr_drawloading = false; scr_disabled_for_loading = true; scr_disabled_time = realtime; scr_fullupdate = 0; } /* =============== SCR_EndLoadingPlaque ================ */ void SCR_EndLoadingPlaque (void) { scr_disabled_for_loading = false; scr_fullupdate = 0; Con_ClearNotify (); } //============================================================================= char *scr_notifystring; qboolean scr_drawdialog; void SCR_DrawNotifyString (void) { char *start; int l, j, x, y; start = scr_notifystring; y = vid.height*0.35; do { // scan the width of the line for (l=0 ; l<40 ; l++) if (start[l] == '\n' || !start[l]) break; x = (vid.width - l*8)/2; for (j=0 ; j 60) scr_disabled_for_loading = false; else return; } #ifdef _WIN32 { // don't suck up any cpu if minimized extern int Minimized; if (Minimized) return; } #endif scr_copytop = 0; scr_copyeverything = 0; if (scr_viewsize.value != oldscr_viewsize) { oldscr_viewsize = scr_viewsize.value; vid.recalc_refdef = 1; } // check for vid changes if (oldfov != scr_fov.value) { oldfov = scr_fov.value; vid.recalc_refdef = true; } if (oldlcd_x != lcd_x.value) { oldlcd_x = lcd_x.value; vid.recalc_refdef = true; } if (oldscreensize != scr_viewsize.value) { oldscreensize = scr_viewsize.value; vid.recalc_refdef = true; } if (oldsbar != cl_sbar.value) { oldsbar = cl_sbar.value; vid.recalc_refdef = true; } if (vid.recalc_refdef) { // something changed, so reorder the screen SCR_CalcRefdef (); } // do 3D refresh drawing, and then update the screen D_EnableBackBufferAccess (); // of all overlay stuff if drawing directly if (scr_fullupdate++ < vid.numpages) { // clear the entire screen scr_copyeverything = 1; Draw_TileClear (0,0,vid.width,vid.height); Sbar_Changed (); } SCR_SetUpToDrawConsole (); SCR_EraseCenterString (); D_DisableBackBufferAccess (); // for adapters that can't stay mapped in for linear writes all the time VID_LockBuffer (); V_RenderView (); VID_UnlockBuffer (); D_EnableBackBufferAccess (); // of all overlay stuff if drawing directly if (scr_drawdialog) { Sbar_Draw (); Draw_FadeScreen (); SCR_DrawNotifyString (); scr_copyeverything = true; } else if (scr_drawloading) { SCR_DrawLoading (); Sbar_Draw (); } else if (cl.intermission == 1 && key_dest == key_game) { Sbar_IntermissionOverlay (); } else if (cl.intermission == 2 && key_dest == key_game) { Sbar_FinaleOverlay (); SCR_CheckDrawCenterString (); } else if (cl.intermission == 3 && key_dest == key_game) { SCR_CheckDrawCenterString (); } else { Draw_Crosshair (); SCR_DrawRam (); SCR_DrawNet (); SCR_DrawTurtle (); SCR_DrawPause (); SCR_CheckDrawCenterString (); SCR_DrawClock (); SCR_DrawFPS (); SCR_DrawSpeed (); SCR_DrawStats (); SCR_DrawVolume (); Sbar_Draw (); SCR_DrawConsole (); M_Draw (); } D_DisableBackBufferAccess (); // for adapters that can't stay mapped in for linear writes all the time V_UpdatePalette (); // update one of three areas if (scr_copyeverything) { vrect.x = 0; vrect.y = 0; vrect.width = vid.width; vrect.height = vid.height; vrect.pnext = 0; VID_Update (&vrect); } else if (scr_copytop) { vrect.x = 0; vrect.y = 0; vrect.width = vid.width; vrect.height = vid.height - sb_lines; vrect.pnext = 0; VID_Update (&vrect); } else { vrect.x = scr_vrect.x; vrect.y = scr_vrect.y; vrect.width = scr_vrect.width; vrect.height = scr_vrect.height; vrect.pnext = 0; VID_Update (&vrect); } #ifdef _WIN32 Movie_UpdateScreen (); #endif } /* ================== SCR_UpdateWholeScreen ================== */ void SCR_UpdateWholeScreen (void) { scr_fullupdate = 0; SCR_UpdateScreen (); }