1/* 2Copyright (C) 1996-1997 Id Software, Inc. 3 4This program is free software; you can redistribute it and/or 5modify it under the terms of the GNU General Public License 6as published by the Free Software Foundation; either version 2 7of the License, or (at your option) any later version. 8 9This program is distributed in the hope that it will be useful, 10but WITHOUT ANY WARRANTY; without even the implied warranty of 11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13See the GNU General Public License for more details. 14 15You should have received a copy of the GNU General Public License 16along with this program; if not, write to the Free Software 17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19*/ 20 21// screen.c -- master for refresh, status bar, console, chat, notify, etc 22 23#include "quakedef.h" 24 25#include <time.h> 26 27/* 28 29background clear 30rendering 31turtle/net/ram icons 32sbar 33centerprint / slow centerprint 34notify lines 35intermission / finale overlay 36loading plaque 37console 38menu 39 40required background clears 41required update regions 42 43 44syncronous draw mode or async 45One off screen buffer, with updates either copied or xblited 46Need to double buffer? 47 48 49async draw will require the refresh area to be cleared, because it will be 50xblited, but sync draw can just ignore it. 51 52sync 53draw 54 55CenterPrint () 56SlowPrint () 57Screen_Update (); 58Con_Printf (); 59 60net 61turn off messages option 62 63the refresh is allways rendered, unless the console is full screen 64 65 66console is: 67 notify lines 68 half 69 full 70 71 72*/ 73 74 75int glx, gly, glwidth, glheight; 76 77// only the refresh window will be updated unless these variables are flagged 78int scr_copytop; 79int scr_copyeverything; 80 81float scr_con_current; 82float scr_conlines; // lines of console to display 83 84float oldscreensize, oldfov; 85cvar_t scr_viewsize = CVAR3("viewsize","100", true); 86cvar_t scr_fov = CVAR2("fov","90"); // 10 - 170 87cvar_t scr_conspeed = CVAR2("scr_conspeed","300"); 88cvar_t scr_centertime = CVAR2("scr_centertime","2"); 89cvar_t scr_showram = CVAR2("showram","1"); 90cvar_t scr_showturtle = CVAR2("showturtle","0"); 91cvar_t scr_showpause = CVAR2("showpause","1"); 92cvar_t scr_printspeed = CVAR2("scr_printspeed","8"); 93cvar_t scr_allowsnap = CVAR2("scr_allowsnap", "1"); 94cvar_t gl_triplebuffer = CVAR3("gl_triplebuffer", "1", true ); 95extern cvar_t crosshair; 96 97qboolean scr_initialized; // ready to draw 98 99qpic_t *scr_ram; 100qpic_t *scr_net; 101qpic_t *scr_turtle; 102 103int scr_fullupdate; 104 105int clearconsole; 106int clearnotify; 107 108int sb_lines; 109 110viddef_t vid; // global video state 111 112vrect_t scr_vrect; 113 114qboolean scr_disabled_for_loading; 115qboolean scr_drawloading; 116float scr_disabled_time; 117 118qboolean block_drawing; 119 120void SCR_ScreenShot_f (void); 121void SCR_RSShot_f (void); 122 123/* 124=============================================================================== 125 126CENTER PRINTING 127 128=============================================================================== 129*/ 130 131char scr_centerstring[1024]; 132float scr_centertime_start; // for slow victory printing 133float scr_centertime_off; 134int scr_center_lines; 135int scr_erase_lines; 136int scr_erase_center; 137 138/* 139============== 140SCR_CenterPrint 141 142Called for important messages that should stay in the center of the screen 143for a few moments 144============== 145*/ 146void SCR_CenterPrint (char *str) 147{ 148 strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1); 149 scr_centertime_off = scr_centertime.value; 150 scr_centertime_start = cl.time; 151 152// count the number of lines for centering 153 scr_center_lines = 1; 154 while (*str) 155 { 156 if (*str == '\n') 157 scr_center_lines++; 158 str++; 159 } 160} 161 162 163void SCR_DrawCenterString (void) 164{ 165 char *start; 166 int l; 167 int j; 168 int x, y; 169 int remaining; 170 171// the finale prints the characters one at a time 172 if (cl.intermission) 173 remaining = scr_printspeed.value * (cl.time - scr_centertime_start); 174 else 175 remaining = 9999; 176 177 scr_erase_center = 0; 178 start = scr_centerstring; 179 180 if (scr_center_lines <= 4) 181 y = vid.height*0.35; 182 else 183 y = 48; 184 185 do 186 { 187 // scan the width of the line 188 for (l=0 ; l<40 ; l++) 189 if (start[l] == '\n' || !start[l]) 190 break; 191 x = (vid.width - l*8)/2; 192 for (j=0 ; j<l ; j++, x+=8) 193 { 194 Draw_Character (x, y, start[j]); 195 if (!remaining--) 196 return; 197 } 198 199 y += 8; 200 201 while (*start && *start != '\n') 202 start++; 203 204 if (!*start) 205 break; 206 start++; // skip the \n 207 } while (1); 208} 209 210void SCR_CheckDrawCenterString (void) 211{ 212 scr_copytop = 1; 213 if (scr_center_lines > scr_erase_lines) 214 scr_erase_lines = scr_center_lines; 215 216 scr_centertime_off -= host_frametime; 217 218 if (scr_centertime_off <= 0 && !cl.intermission) 219 return; 220 if (key_dest != key_game) 221 return; 222 223 SCR_DrawCenterString (); 224} 225 226//============================================================================= 227 228/* 229==================== 230CalcFov 231==================== 232*/ 233float CalcFov (float fov_x, float width, float height) 234{ 235 float a; 236 float x; 237 238 if (fov_x < 1 || fov_x > 179) 239 Sys_Error ("Bad fov: %f", fov_x); 240 241 x = width/tan(fov_x/360*M_PI); 242 243 a = atan (height/x); 244 245 a = a*360/M_PI; 246 247 return a; 248} 249 250/* 251================= 252SCR_CalcRefdef 253 254Must be called whenever vid changes 255Internal use only 256================= 257*/ 258static void SCR_CalcRefdef (void) 259{ 260 float size; 261 int h; 262 qboolean full = false; 263 264 265 scr_fullupdate = 0; // force a background redraw 266 vid.recalc_refdef = 0; 267 268// force the status bar to redraw 269 Sbar_Changed (); 270 271//======================================== 272 273// bound viewsize 274 if (scr_viewsize.value < 30) 275 Cvar_Set ("viewsize","30"); 276 if (scr_viewsize.value > 120) 277 Cvar_Set ("viewsize","120"); 278 279// bound field of view 280 if (scr_fov.value < 10) 281 Cvar_Set ("fov","10"); 282 if (scr_fov.value > 170) 283 Cvar_Set ("fov","170"); 284 285// intermission is always full screen 286 if (cl.intermission) 287 size = 120; 288 else 289 size = scr_viewsize.value; 290 291 if (size >= 120) 292 sb_lines = 0; // no status bar at all 293 else if (size >= 110) 294 sb_lines = 24; // no inventory 295 else 296 sb_lines = 24+16+8; 297 298 if (scr_viewsize.value >= 100.0) { 299 full = true; 300 size = 100.0; 301 } else 302 size = scr_viewsize.value; 303 if (cl.intermission) 304 { 305 full = true; 306 size = 100.0; 307 sb_lines = 0; 308 } 309 size /= 100.0; 310 311 if (!cl_sbar.value && full) 312 h = vid.height; 313 else 314 h = vid.height - sb_lines; 315 316 r_refdef.vrect.width = vid.width * size; 317 if (r_refdef.vrect.width < 96) 318 { 319 size = 96.0 / r_refdef.vrect.width; 320 r_refdef.vrect.width = 96; // min for icons 321 } 322 323 r_refdef.vrect.height = vid.height * size; 324 if (cl_sbar.value || !full) { 325 if (r_refdef.vrect.height > (int) (vid.height - sb_lines)) 326 r_refdef.vrect.height = vid.height - sb_lines; 327 } else if (r_refdef.vrect.height > (int) vid.height) 328 r_refdef.vrect.height = vid.height; 329 r_refdef.vrect.x = (vid.width - r_refdef.vrect.width)/2; 330 if (full) 331 r_refdef.vrect.y = 0; 332 else 333 r_refdef.vrect.y = (h - r_refdef.vrect.height)/2; 334 335 r_refdef.fov_x = scr_fov.value; 336 r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height); 337 338 scr_vrect = r_refdef.vrect; 339} 340 341 342/* 343================= 344SCR_SizeUp_f 345 346Keybinding command 347================= 348*/ 349void SCR_SizeUp_f (void) 350{ 351 Cvar_SetValue ("viewsize",scr_viewsize.value+10); 352 vid.recalc_refdef = 1; 353} 354 355 356/* 357================= 358SCR_SizeDown_f 359 360Keybinding command 361================= 362*/ 363void SCR_SizeDown_f (void) 364{ 365 Cvar_SetValue ("viewsize",scr_viewsize.value-10); 366 vid.recalc_refdef = 1; 367} 368 369//============================================================================ 370 371/* 372================== 373SCR_Init 374================== 375*/ 376void SCR_Init (void) 377{ 378 Cvar_RegisterVariable (&scr_fov); 379 Cvar_RegisterVariable (&scr_viewsize); 380 Cvar_RegisterVariable (&scr_conspeed); 381 Cvar_RegisterVariable (&scr_showram); 382 Cvar_RegisterVariable (&scr_showturtle); 383 Cvar_RegisterVariable (&scr_showpause); 384 Cvar_RegisterVariable (&scr_centertime); 385 Cvar_RegisterVariable (&scr_printspeed); 386 Cvar_RegisterVariable (&scr_allowsnap); 387 Cvar_RegisterVariable (&gl_triplebuffer); 388 389// 390// register our commands 391// 392 Cmd_AddCommand ("screenshot",SCR_ScreenShot_f); 393 Cmd_AddCommand ("snap",SCR_RSShot_f); 394 Cmd_AddCommand ("sizeup",SCR_SizeUp_f); 395 Cmd_AddCommand ("sizedown",SCR_SizeDown_f); 396 397 scr_ram = Draw_PicFromWad ("ram"); 398 scr_net = Draw_PicFromWad ("net"); 399 scr_turtle = Draw_PicFromWad ("turtle"); 400 401 scr_initialized = true; 402} 403 404 405 406/* 407============== 408SCR_DrawRam 409============== 410*/ 411void SCR_DrawRam (void) 412{ 413 if (!scr_showram.value) 414 return; 415 416 if (!r_cache_thrash) 417 return; 418 419 Draw_Pic (scr_vrect.x+32, scr_vrect.y, scr_ram); 420} 421 422/* 423============== 424SCR_DrawTurtle 425============== 426*/ 427void SCR_DrawTurtle (void) 428{ 429 static int count; 430 431 if (!scr_showturtle.value) 432 return; 433 434 if (host_frametime < 0.1) 435 { 436 count = 0; 437 return; 438 } 439 440 count++; 441 if (count < 3) 442 return; 443 444 Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle); 445} 446 447/* 448============== 449SCR_DrawNet 450============== 451*/ 452void SCR_DrawNet (void) 453{ 454 if (cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged < UPDATE_BACKUP-1) 455 return; 456 if (cls.demoplayback) 457 return; 458 459 Draw_Pic (scr_vrect.x+64, scr_vrect.y, scr_net); 460} 461 462void SCR_DrawFPS (void) 463{ 464 extern cvar_t show_fps; 465 static double lastframetime; 466 double t; 467 extern int fps_count; 468 static int lastfps; 469 int x, y; 470 char st[80]; 471 472 if (!show_fps.value) 473 return; 474 475 t = Sys_DoubleTime(); 476 if ((t - lastframetime) >= 1.0) { 477 lastfps = fps_count; 478 fps_count = 0; 479 lastframetime = t; 480 } 481 482 sprintf(st, "%3d FPS", lastfps); 483 x = vid.width - strlen(st) * 8 - 8; 484 y = vid.height - sb_lines - 8; 485// Draw_TileClear(x, y, strlen(st) * 8, 8); 486 Draw_String(x, y, st); 487} 488 489 490/* 491============== 492DrawPause 493============== 494*/ 495void SCR_DrawPause (void) 496{ 497 qpic_t *pic; 498 499 if (!scr_showpause.value) // turn off for screenshots 500 return; 501 502 if (!cl.paused) 503 return; 504 505 pic = Draw_CachePic ("gfx/pause.lmp"); 506 Draw_Pic ( (vid.width - pic->width)/2, 507 (vid.height - 48 - pic->height)/2, pic); 508} 509 510 511 512/* 513============== 514SCR_DrawLoading 515============== 516*/ 517void SCR_DrawLoading (void) 518{ 519 qpic_t *pic; 520 521 if (!scr_drawloading) 522 return; 523 524 pic = Draw_CachePic ("gfx/loading.lmp"); 525 Draw_Pic ( (vid.width - pic->width)/2, 526 (vid.height - 48 - pic->height)/2, pic); 527} 528 529 530 531//============================================================================= 532 533 534/* 535================== 536SCR_SetUpToDrawConsole 537================== 538*/ 539void SCR_SetUpToDrawConsole (void) 540{ 541 Con_CheckResize (); 542 543 if (scr_drawloading) 544 return; // never a console with loading plaque 545 546// decide on the height of the console 547 if (cls.state != ca_active) 548 { 549 scr_conlines = vid.height; // full screen 550 scr_con_current = scr_conlines; 551 } 552 else if (key_dest == key_console) 553 scr_conlines = vid.height/2; // half screen 554 else 555 scr_conlines = 0; // none visible 556 557 if (scr_conlines < scr_con_current) 558 { 559 scr_con_current -= scr_conspeed.value*host_frametime; 560 if (scr_conlines > scr_con_current) 561 scr_con_current = scr_conlines; 562 563 } 564 else if (scr_conlines > scr_con_current) 565 { 566 scr_con_current += scr_conspeed.value*host_frametime; 567 if (scr_conlines < scr_con_current) 568 scr_con_current = scr_conlines; 569 } 570 571 if (clearconsole++ < vid.numpages) 572 { 573 Sbar_Changed (); 574 } 575 else if (clearnotify++ < vid.numpages) 576 { 577 } 578 else 579 con_notifylines = 0; 580} 581 582/* 583================== 584SCR_DrawConsole 585================== 586*/ 587void SCR_DrawConsole (void) 588{ 589 if (scr_con_current) 590 { 591 scr_copyeverything = 1; 592 Con_DrawConsole (scr_con_current); 593 clearconsole = 0; 594 } 595 else 596 { 597 if (key_dest == key_game || key_dest == key_message) 598 Con_DrawNotify (); // only draw notify in game 599 } 600} 601 602 603/* 604============================================================================== 605 606 SCREEN SHOTS 607 608============================================================================== 609*/ 610 611typedef struct _TargaHeader { 612 unsigned char id_length, colormap_type, image_type; 613 unsigned short colormap_index, colormap_length; 614 unsigned char colormap_size; 615 unsigned short x_origin, y_origin, width, height; 616 unsigned char pixel_size, attributes; 617} TargaHeader; 618 619 620/* 621================== 622SCR_ScreenShot_f 623================== 624*/ 625void SCR_ScreenShot_f (void) 626{ 627 byte *buffer; 628 char pcxname[80]; 629 char checkname[MAX_OSPATH]; 630 int i, c, temp; 631// 632// find a file name to save it to 633// 634 strcpy(pcxname,"quake00.tga"); 635 636 for (i=0 ; i<=99 ; i++) 637 { 638 pcxname[5] = i/10 + '0'; 639 pcxname[6] = i%10 + '0'; 640 sprintf (checkname, "%s/%s", com_gamedir, pcxname); 641 if (Sys_FileTime(checkname) == -1) 642 break; // file doesn't exist 643 } 644 if (i==100) 645 { 646 Con_Printf ("SCR_ScreenShot_f: Couldn't create a PCX file\n"); 647 return; 648 } 649 650 651 buffer = malloc(glwidth*glheight*3 + 18); 652 memset (buffer, 0, 18); 653 buffer[2] = 2; // uncompressed type 654 buffer[12] = glwidth&255; 655 buffer[13] = glwidth>>8; 656 buffer[14] = glheight&255; 657 buffer[15] = glheight>>8; 658 buffer[16] = 24; // pixel size 659 660 glReadPixels (glx, gly, glwidth, glheight, GL_RGB, GL_UNSIGNED_BYTE, buffer+18 ); 661 662 // swap rgb to bgr 663 c = 18+glwidth*glheight*3; 664 for (i=18 ; i<c ; i+=3) 665 { 666 temp = buffer[i]; 667 buffer[i] = buffer[i+2]; 668 buffer[i+2] = temp; 669 } 670 COM_WriteFile (pcxname, buffer, glwidth*glheight*3 + 18 ); 671 672 free (buffer); 673 Con_Printf ("Wrote %s\n", pcxname); 674} 675 676/* 677============== 678WritePCXfile 679============== 680*/ 681void WritePCXfile (char *filename, byte *data, int width, int height, 682 int rowbytes, byte *palette, qboolean upload) 683{ 684 int i, j, length; 685 pcx_t *pcx; 686 byte *pack; 687 688 pcx = Hunk_TempAlloc (width*height*2+1000); 689 if (pcx == NULL) 690 { 691 Con_Printf("SCR_ScreenShot_f: not enough memory\n"); 692 return; 693 } 694 695 pcx->manufacturer = 0x0a; // PCX id 696 pcx->version = 5; // 256 color 697 pcx->encoding = 1; // uncompressed 698 pcx->bits_per_pixel = 8; // 256 color 699 pcx->xmin = 0; 700 pcx->ymin = 0; 701 pcx->xmax = LittleShort((short)(width-1)); 702 pcx->ymax = LittleShort((short)(height-1)); 703 pcx->hres = LittleShort((short)width); 704 pcx->vres = LittleShort((short)height); 705 Q_memset (pcx->palette,0,sizeof(pcx->palette)); 706 pcx->color_planes = 1; // chunky image 707 pcx->bytes_per_line = LittleShort((short)width); 708 pcx->palette_type = LittleShort(2); // not a grey scale 709 Q_memset (pcx->filler,0,sizeof(pcx->filler)); 710 711// pack the image 712 pack = &pcx->data; 713 714 data += rowbytes * (height - 1); 715 716 for (i=0 ; i<height ; i++) 717 { 718 for (j=0 ; j<width ; j++) 719 { 720 if ( (*data & 0xc0) != 0xc0) 721 *pack++ = *data++; 722 else 723 { 724 *pack++ = 0xc1; 725 *pack++ = *data++; 726 } 727 } 728 729 data += rowbytes - width; 730 data -= rowbytes * 2; 731 } 732 733// write the palette 734 *pack++ = 0x0c; // palette ID byte 735 for (i=0 ; i<768 ; i++) 736 *pack++ = *palette++; 737 738// write output file 739 length = pack - (byte *)pcx; 740 741 if (upload) 742 CL_StartUpload((void *)pcx, length); 743 else 744 COM_WriteFile (filename, pcx, length); 745} 746 747 748 749/* 750Find closest color in the palette for named color 751*/ 752int MipColor(int r, int g, int b) 753{ 754 int i; 755 float dist; 756 int best = 0; 757 float bestdist; 758 int r1, g1, b1; 759 static int lr = -1, lg = -1, lb = -1; 760 static int lastbest; 761 762 if (r == lr && g == lg && b == lb) 763 return lastbest; 764 765 bestdist = 256*256*3; 766 767 for (i = 0; i < 256; i++) { 768 r1 = host_basepal[i*3] - r; 769 g1 = host_basepal[i*3+1] - g; 770 b1 = host_basepal[i*3+2] - b; 771 dist = r1*r1 + g1*g1 + b1*b1; 772 if (dist < bestdist) { 773 bestdist = dist; 774 best = i; 775 } 776 } 777 lr = r; lg = g; lb = b; 778 lastbest = best; 779 return best; 780} 781 782// from gl_draw.c 783byte *draw_chars; // 8*8 graphic characters 784 785void SCR_DrawCharToSnap (int num, byte *dest, int width) 786{ 787 int row, col; 788 byte *source; 789 int drawline; 790 int x; 791 792 row = num>>4; 793 col = num&15; 794 source = draw_chars + (row<<10) + (col<<3); 795 796 drawline = 8; 797 798 while (drawline--) 799 { 800 for (x=0 ; x<8 ; x++) 801 if (source[x]) 802 dest[x] = source[x]; 803 else 804 dest[x] = 98; 805 source += 128; 806 dest -= width; 807 } 808 809} 810 811void SCR_DrawStringToSnap (const char *s, byte *buf, int x, int y, int width) 812{ 813 byte *dest; 814 const unsigned char *p; 815 816 dest = buf + ((y * width) + x); 817 818 p = (const unsigned char *)s; 819 while (*p) { 820 SCR_DrawCharToSnap(*p++, dest, width); 821 dest += 8; 822 } 823} 824 825 826/* 827================== 828SCR_RSShot_f 829================== 830*/ 831void SCR_RSShot_f (void) 832{ 833 int i, x, y; 834 unsigned char *src, *dest; 835 char pcxname[80]; 836 char checkname[MAX_OSPATH]; 837 unsigned char *newbuf, *srcbuf; 838 int srcrowbytes; 839 int w, h; 840 int dx, dy, dex, dey, nx; 841 int r, b, g; 842 int count; 843 float fracw, frach; 844 char st[80]; 845 time_t now; 846 847 if (CL_IsUploading()) 848 return; // already one pending 849 850 if (cls.state < ca_onserver) 851 return; // gotta be connected 852 853 Con_Printf("Remote screen shot requested.\n"); 854 855#if 0 856// 857// find a file name to save it to 858// 859 strcpy(pcxname,"mquake00.pcx"); 860 861 for (i=0 ; i<=99 ; i++) 862 { 863 pcxname[6] = i/10 + '0'; 864 pcxname[7] = i%10 + '0'; 865 sprintf (checkname, "%s/%s", com_gamedir, pcxname); 866 if (Sys_FileTime(checkname) == -1) 867 break; // file doesn't exist 868 } 869 if (i==100) 870 { 871 Con_Printf ("SCR_ScreenShot_f: Couldn't create a PCX"); 872 return; 873 } 874#endif 875 876// 877// save the pcx file 878// 879 newbuf = malloc(glheight * glwidth * 3); 880 881 glReadPixels (glx, gly, glwidth, glheight, GL_RGB, GL_UNSIGNED_BYTE, newbuf ); 882 883 w = (vid.width < RSSHOT_WIDTH) ? glwidth : RSSHOT_WIDTH; 884 h = (vid.height < RSSHOT_HEIGHT) ? glheight : RSSHOT_HEIGHT; 885 886 fracw = (float)glwidth / (float)w; 887 frach = (float)glheight / (float)h; 888 889 for (y = 0; y < h; y++) { 890 dest = newbuf + (w*3 * y); 891 892 for (x = 0; x < w; x++) { 893 r = g = b = 0; 894 895 dx = x * fracw; 896 dex = (x + 1) * fracw; 897 if (dex == dx) dex++; // at least one 898 dy = y * frach; 899 dey = (y + 1) * frach; 900 if (dey == dy) dey++; // at least one 901 902 count = 0; 903 for (/* */; dy < dey; dy++) { 904 src = newbuf + (glwidth * 3 * dy) + dx * 3; 905 for (nx = dx; nx < dex; nx++) { 906 r += *src++; 907 g += *src++; 908 b += *src++; 909 count++; 910 } 911 } 912 r /= count; 913 g /= count; 914 b /= count; 915 *dest++ = r; 916 *dest++ = b; 917 *dest++ = g; 918 } 919 } 920 921 // convert to eight bit 922 for (y = 0; y < h; y++) { 923 src = newbuf + (w * 3 * y); 924 dest = newbuf + (w * y); 925 926 for (x = 0; x < w; x++) { 927 *dest++ = MipColor(src[0], src[1], src[2]); 928 src += 3; 929 } 930 } 931 932 time(&now); 933 strcpy(st, ctime(&now)); 934 st[strlen(st) - 1] = 0; 935 SCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, h - 1, w); 936 937 strncpy(st, cls.servername, sizeof(st)); 938 st[sizeof(st) - 1] = 0; 939 SCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, h - 11, w); 940 941 strncpy(st, name.string, sizeof(st)); 942 st[sizeof(st) - 1] = 0; 943 SCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, h - 21, w); 944 945 WritePCXfile (pcxname, newbuf, w, h, w, host_basepal, true); 946 947 free(newbuf); 948 949 Con_Printf ("Wrote %s\n", pcxname); 950} 951 952 953 954 955//============================================================================= 956 957 958//============================================================================= 959 960char *scr_notifystring; 961qboolean scr_drawdialog; 962 963void SCR_DrawNotifyString (void) 964{ 965 char *start; 966 int l; 967 int j; 968 int x, y; 969 970 start = scr_notifystring; 971 972 y = vid.height*0.35; 973 974 do 975 { 976 // scan the width of the line 977 for (l=0 ; l<40 ; l++) 978 if (start[l] == '\n' || !start[l]) 979 break; 980 x = (vid.width - l*8)/2; 981 for (j=0 ; j<l ; j++, x+=8) 982 Draw_Character (x, y, start[j]); 983 984 y += 8; 985 986 while (*start && *start != '\n') 987 start++; 988 989 if (!*start) 990 break; 991 start++; // skip the \n 992 } while (1); 993} 994 995/* 996================== 997SCR_ModalMessage 998 999Displays a text string in the center of the screen and waits for a Y or N 1000keypress. 1001================== 1002*/ 1003int SCR_ModalMessage (char *text) 1004{ 1005 scr_notifystring = text; 1006 1007// draw a fresh screen 1008 scr_fullupdate = 0; 1009 scr_drawdialog = true; 1010 SCR_UpdateScreen (); 1011 scr_drawdialog = false; 1012 1013 S_ClearBuffer (); // so dma doesn't loop current sound 1014 1015 do 1016 { 1017 key_count = -1; // wait for a key down and up 1018 Sys_SendKeyEvents (); 1019 } while (key_lastpress != 'y' && key_lastpress != 'n' && key_lastpress != K_ESCAPE); 1020 1021 scr_fullupdate = 0; 1022 SCR_UpdateScreen (); 1023 1024 return key_lastpress == 'y'; 1025} 1026 1027 1028//============================================================================= 1029 1030/* 1031=============== 1032SCR_BringDownConsole 1033 1034Brings the console down and fades the palettes back to normal 1035================ 1036*/ 1037void SCR_BringDownConsole (void) 1038{ 1039 int i; 1040 1041 scr_centertime_off = 0; 1042 1043 for (i=0 ; i<20 && scr_conlines != scr_con_current ; i++) 1044 SCR_UpdateScreen (); 1045 1046 cl.cshifts[0].percent = 0; // no area contents palette on next frame 1047 VID_SetPalette (host_basepal); 1048} 1049 1050void SCR_TileClear (void) 1051{ 1052 if (r_refdef.vrect.x > 0) { 1053 // left 1054 Draw_TileClear (0, 0, r_refdef.vrect.x, vid.height - sb_lines); 1055 // right 1056 Draw_TileClear (r_refdef.vrect.x + r_refdef.vrect.width, 0, 1057 vid.width - r_refdef.vrect.x + r_refdef.vrect.width, 1058 vid.height - sb_lines); 1059 } 1060 if (r_refdef.vrect.y > 0) { 1061 // top 1062 Draw_TileClear (r_refdef.vrect.x, 0, 1063 r_refdef.vrect.x + r_refdef.vrect.width, 1064 r_refdef.vrect.y); 1065 // bottom 1066 Draw_TileClear (r_refdef.vrect.x, 1067 r_refdef.vrect.y + r_refdef.vrect.height, 1068 r_refdef.vrect.width, 1069 vid.height - sb_lines - 1070 (r_refdef.vrect.height + r_refdef.vrect.y)); 1071 } 1072} 1073 1074float oldsbar = 0; 1075 1076/* 1077================== 1078SCR_UpdateScreen 1079 1080This is called every frame, and can also be called explicitly to flush 1081text to the screen. 1082 1083WARNING: be very careful calling this from elsewhere, because the refresh 1084needs almost the entire 256k of stack space! 1085================== 1086*/ 1087void SCR_UpdateScreen (void) 1088{ 1089 if (block_drawing) 1090 return; 1091 1092 vid.numpages = 2 + gl_triplebuffer.value; 1093 1094 scr_copytop = 0; 1095 scr_copyeverything = 0; 1096 1097 if (scr_disabled_for_loading) 1098 { 1099 if (realtime - scr_disabled_time > 60) 1100 { 1101 scr_disabled_for_loading = false; 1102 Con_Printf ("load failed.\n"); 1103 } 1104 else 1105 return; 1106 } 1107 1108 if (!scr_initialized || !con_initialized) 1109 return; // not initialized yet 1110 1111 1112 if (oldsbar != cl_sbar.value) { 1113 oldsbar = cl_sbar.value; 1114 vid.recalc_refdef = true; 1115 } 1116 1117 GL_BeginRendering (&glx, &gly, &glwidth, &glheight); 1118 1119 // 1120 // determine size of refresh window 1121 // 1122 if (oldfov != scr_fov.value) 1123 { 1124 oldfov = scr_fov.value; 1125 vid.recalc_refdef = true; 1126 } 1127 1128 if (vid.recalc_refdef) 1129 SCR_CalcRefdef (); 1130 1131// 1132// do 3D refresh drawing, and then update the screen 1133// 1134 SCR_SetUpToDrawConsole (); 1135 1136 V_RenderView (); 1137 1138 GL_Set2D (); 1139 1140 // 1141 // draw any areas not covered by the refresh 1142 // 1143 SCR_TileClear (); 1144 1145 if (r_netgraph.value) 1146 R_NetGraph (); 1147 1148 if (scr_drawdialog) 1149 { 1150 Sbar_Draw (); 1151 Draw_FadeScreen (); 1152 SCR_DrawNotifyString (); 1153 scr_copyeverything = true; 1154 } 1155 else if (scr_drawloading) 1156 { 1157 SCR_DrawLoading (); 1158 Sbar_Draw (); 1159 } 1160 else if (cl.intermission == 1 && key_dest == key_game) 1161 { 1162 Sbar_IntermissionOverlay (); 1163 } 1164 else if (cl.intermission == 2 && key_dest == key_game) 1165 { 1166 Sbar_FinaleOverlay (); 1167 SCR_CheckDrawCenterString (); 1168 } 1169 else 1170 { 1171 if (crosshair.value) 1172 Draw_Crosshair(); 1173 1174 SCR_DrawRam (); 1175 SCR_DrawNet (); 1176 SCR_DrawFPS (); 1177 SCR_DrawTurtle (); 1178 SCR_DrawPause (); 1179 SCR_CheckDrawCenterString (); 1180 Sbar_Draw (); 1181 SCR_DrawConsole (); 1182 M_Draw (); 1183 } 1184 1185 V_UpdatePalette (); 1186 1187 GL_EndRendering (); 1188} 1189