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/* 26 27background clear 28rendering 29turtle/net/ram icons 30sbar 31centerprint / slow centerprint 32notify lines 33intermission / finale overlay 34loading plaque 35console 36menu 37 38required background clears 39required update regions 40 41 42syncronous draw mode or async 43One off screen buffer, with updates either copied or xblited 44Need to double buffer? 45 46 47async draw will require the refresh area to be cleared, because it will be 48xblited, but sync draw can just ignore it. 49 50sync 51draw 52 53CenterPrint () 54SlowPrint () 55Screen_Update (); 56Con_Printf (); 57 58net 59turn off messages option 60 61the refresh is allways rendered, unless the console is full screen 62 63 64console is: 65 notify lines 66 half 67 full 68 69 70*/ 71 72 73int glx, gly, glwidth, glheight; 74 75// only the refresh window will be updated unless these variables are flagged 76int scr_copytop; 77int scr_copyeverything; 78 79float scr_con_current; 80float scr_conlines; // lines of console to display 81 82float oldscreensize, oldfov; 83cvar_t scr_viewsize = CVAR3("viewsize","100", true); 84cvar_t scr_fov = CVAR2("fov","90"); // 10 - 170 85cvar_t scr_conspeed = CVAR2("scr_conspeed","300"); 86cvar_t scr_centertime = CVAR2("scr_centertime","2"); 87cvar_t scr_showram = CVAR2("showram","1"); 88cvar_t scr_showturtle = CVAR2("showturtle","0"); 89cvar_t scr_showpause = CVAR2("showpause","1"); 90cvar_t scr_printspeed = CVAR2("scr_printspeed","8"); 91cvar_t scr_allowsnap = CVAR2("scr_allowsnap", "1"); 92cvar_t gl_triplebuffer = CVAR3("gl_triplebuffer", "1", true ); 93extern cvar_t crosshair; 94 95qboolean scr_initialized; // ready to draw 96 97qpic_t *scr_ram; 98qpic_t *scr_net; 99qpic_t *scr_turtle; 100 101int scr_fullupdate; 102 103int clearconsole; 104int clearnotify; 105 106// int sb_lines; 107 108viddef_t vid; // global video state 109 110vrect_t scr_vrect; 111 112qboolean scr_disabled_for_loading; 113qboolean scr_drawloading; 114float scr_disabled_time; 115 116qboolean block_drawing; 117 118void SCR_ScreenShot_f (void); 119 120/* 121=============================================================================== 122 123CENTER PRINTING 124 125=============================================================================== 126*/ 127 128char scr_centerstring[1024]; 129float scr_centertime_start; // for slow victory printing 130float scr_centertime_off; 131int scr_center_lines; 132int scr_erase_lines; 133int scr_erase_center; 134 135/* 136============== 137SCR_CenterPrint 138 139Called for important messages that should stay in the center of the screen 140for a few moments 141============== 142*/ 143void SCR_CenterPrint (char *str) 144{ 145 strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1); 146 scr_centertime_off = scr_centertime.value; 147 scr_centertime_start = cl.time; 148 149// count the number of lines for centering 150 scr_center_lines = 1; 151 while (*str) 152 { 153 if (*str == '\n') 154 scr_center_lines++; 155 str++; 156 } 157} 158 159 160void SCR_DrawCenterString (void) 161{ 162 char *start; 163 int l; 164 int j; 165 int x, y; 166 int remaining; 167 168// the finale prints the characters one at a time 169 if (cl.intermission) 170 remaining = (int) (scr_printspeed.value * (cl.time - scr_centertime_start)); 171 else 172 remaining = 9999; 173 174 scr_erase_center = 0; 175 start = scr_centerstring; 176 177 if (scr_center_lines <= 4) 178 y = (int)(vid.height*0.35); 179 else 180 y = 48; 181 182 do 183 { 184 // scan the width of the line 185 for (l=0 ; l<40 ; l++) 186 if (start[l] == '\n' || !start[l]) 187 break; 188 x = (vid.width - l*8)/2; 189 for (j=0 ; j<l ; j++, x+=8) 190 { 191 Draw_Character (x, y, start[j]); 192 if (!remaining--) 193 return; 194 } 195 196 y += 8; 197 198 while (*start && *start != '\n') 199 start++; 200 201 if (!*start) 202 break; 203 start++; // skip the \n 204 } while (1); 205} 206 207void SCR_CheckDrawCenterString (void) 208{ 209 scr_copytop = 1; 210 if (scr_center_lines > scr_erase_lines) 211 scr_erase_lines = scr_center_lines; 212 213 scr_centertime_off -= host_frametime; 214 215 if (scr_centertime_off <= 0 && !cl.intermission) 216 return; 217 if (key_dest != key_game) 218 return; 219 220 SCR_DrawCenterString (); 221} 222 223//============================================================================= 224 225/* 226==================== 227CalcFov 228==================== 229*/ 230float CalcFov (float fov_x, float width, float height) 231{ 232 float a; 233 float x; 234 235 if (fov_x < 1 || fov_x > 179) 236 Sys_Error ("Bad fov: %f", fov_x); 237 238 x = width/tan(fov_x/360*M_PI); 239 240 a = atan (height/x); 241 242 a = a*360/M_PI; 243 244 return a; 245} 246 247/* 248================= 249SCR_CalcRefdef 250 251Must be called whenever vid changes 252Internal use only 253================= 254*/ 255static void SCR_CalcRefdef (void) 256{ 257 vrect_t vrect; 258 float size; 259 int h; 260 qboolean full = false; 261 262 263 scr_fullupdate = 0; // force a background redraw 264 vid.recalc_refdef = 0; 265 266// force the status bar to redraw 267 Sbar_Changed (); 268 269//======================================== 270 271// bound viewsize 272 if (scr_viewsize.value < 30) 273 Cvar_Set ("viewsize","30"); 274 if (scr_viewsize.value > 120) 275 Cvar_Set ("viewsize","120"); 276 277// bound field of view 278 if (scr_fov.value < 10) 279 Cvar_Set ("fov","10"); 280 if (scr_fov.value > 170) 281 Cvar_Set ("fov","170"); 282 283// intermission is always full screen 284 if (cl.intermission) 285 size = 120; 286 else 287 size = scr_viewsize.value; 288 289 if (size >= 120) 290 sb_lines = 0; // no status bar at all 291 else if (size >= 110) 292 sb_lines = 24; // no inventory 293 else 294 sb_lines = 24+16+8; 295 296 if (scr_viewsize.value >= 100.0) { 297 full = true; 298 size = 100.0; 299 } else 300 size = scr_viewsize.value; 301 if (cl.intermission) 302 { 303 full = true; 304 size = 100; 305 sb_lines = 0; 306 } 307 size /= 100.0; 308 309 h = vid.height - sb_lines; 310 311 r_refdef.vrect.width = (int) (vid.width * size); 312 if (r_refdef.vrect.width < 96) 313 { 314 size = 96.0 / r_refdef.vrect.width; 315 r_refdef.vrect.width = 96; // min for icons 316 } 317 318 r_refdef.vrect.height = (int)(vid.height * size); 319 if ((int)(r_refdef.vrect.height) > (int)(vid.height - sb_lines)) 320 r_refdef.vrect.height = vid.height - sb_lines; 321 if ((int)(r_refdef.vrect.height) > (int)(vid.height)) 322 r_refdef.vrect.height = vid.height; 323 r_refdef.vrect.x = (vid.width - r_refdef.vrect.width)/2; 324 if (full) 325 r_refdef.vrect.y = 0; 326 else 327 r_refdef.vrect.y = (h - r_refdef.vrect.height)/2; 328 329 r_refdef.fov_x = scr_fov.value; 330 r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height); 331 332 scr_vrect = r_refdef.vrect; 333} 334 335 336/* 337================= 338SCR_SizeUp_f 339 340Keybinding command 341================= 342*/ 343void SCR_SizeUp_f (void) 344{ 345 Cvar_SetValue ("viewsize",scr_viewsize.value+10); 346 vid.recalc_refdef = 1; 347} 348 349 350/* 351================= 352SCR_SizeDown_f 353 354Keybinding command 355================= 356*/ 357void SCR_SizeDown_f (void) 358{ 359 Cvar_SetValue ("viewsize",scr_viewsize.value-10); 360 vid.recalc_refdef = 1; 361} 362 363//============================================================================ 364 365/* 366================== 367SCR_Init 368================== 369*/ 370void SCR_Init (void) 371{ 372 373 Cvar_RegisterVariable (&scr_fov); 374 Cvar_RegisterVariable (&scr_viewsize); 375 Cvar_RegisterVariable (&scr_conspeed); 376 Cvar_RegisterVariable (&scr_showram); 377 Cvar_RegisterVariable (&scr_showturtle); 378 Cvar_RegisterVariable (&scr_showpause); 379 Cvar_RegisterVariable (&scr_centertime); 380 Cvar_RegisterVariable (&scr_printspeed); 381 Cvar_RegisterVariable (&gl_triplebuffer); 382 383// 384// register our commands 385// 386 Cmd_AddCommand ("screenshot",SCR_ScreenShot_f); 387 Cmd_AddCommand ("sizeup",SCR_SizeUp_f); 388 Cmd_AddCommand ("sizedown",SCR_SizeDown_f); 389 390 scr_ram = Draw_PicFromWad ("ram"); 391 scr_net = Draw_PicFromWad ("net"); 392 scr_turtle = Draw_PicFromWad ("turtle"); 393 394 scr_initialized = true; 395} 396 397 398 399/* 400============== 401SCR_DrawRam 402============== 403*/ 404void SCR_DrawRam (void) 405{ 406 if (!scr_showram.value) 407 return; 408 409 if (!r_cache_thrash) 410 return; 411 412 Draw_Pic (scr_vrect.x+32, scr_vrect.y, scr_ram); 413} 414 415/* 416============== 417SCR_DrawTurtle 418============== 419*/ 420void SCR_DrawTurtle (void) 421{ 422 static int count; 423 424 if (!scr_showturtle.value) 425 return; 426 427 if (host_frametime < 0.1) 428 { 429 count = 0; 430 return; 431 } 432 433 count++; 434 if (count < 3) 435 return; 436 437 Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle); 438} 439 440/* 441============== 442SCR_DrawNet 443============== 444*/ 445void SCR_DrawNet (void) 446{ 447 if (realtime - cl.last_received_message < 0.3) 448 return; 449 if (cls.demoplayback) 450 return; 451 452 Draw_Pic (scr_vrect.x+64, scr_vrect.y, scr_net); 453} 454 455/* 456============== 457DrawPause 458============== 459*/ 460void SCR_DrawPause (void) 461{ 462 qpic_t *pic; 463 464 if (!scr_showpause.value) // turn off for screenshots 465 return; 466 467 if (!cl.paused) 468 return; 469 470 pic = Draw_CachePic ("gfx/pause.lmp"); 471 Draw_Pic ( (vid.width - pic->width)/2, 472 (vid.height - 48 - pic->height)/2, pic); 473} 474 475 476 477/* 478============== 479SCR_DrawLoading 480============== 481*/ 482void SCR_DrawLoading (void) 483{ 484 qpic_t *pic; 485 486 if (!scr_drawloading) 487 return; 488 489 pic = Draw_CachePic ("gfx/loading.lmp"); 490 Draw_Pic ( (vid.width - pic->width)/2, 491 (vid.height - 48 - pic->height)/2, pic); 492} 493 494 495 496//============================================================================= 497 498 499/* 500================== 501SCR_SetUpToDrawConsole 502================== 503*/ 504void SCR_SetUpToDrawConsole (void) 505{ 506 Con_CheckResize (); 507 508 if (scr_drawloading) 509 return; // never a console with loading plaque 510 511// decide on the height of the console 512 con_forcedup = !cl.worldmodel || cls.signon != SIGNONS; 513 514 if (con_forcedup) 515 { 516 scr_conlines = vid.height; // full screen 517 scr_con_current = scr_conlines; 518 } 519 else if (key_dest == key_console) 520 scr_conlines = vid.height/2; // half screen 521 else 522 scr_conlines = 0; // none visible 523 524 if (scr_conlines < scr_con_current) 525 { 526 scr_con_current -= scr_conspeed.value*host_frametime; 527 if (scr_conlines > scr_con_current) 528 scr_con_current = scr_conlines; 529 530 } 531 else if (scr_conlines > scr_con_current) 532 { 533 scr_con_current += scr_conspeed.value*host_frametime; 534 if (scr_conlines < scr_con_current) 535 scr_con_current = scr_conlines; 536 } 537 538 if (clearconsole++ < vid.numpages) 539 { 540 Sbar_Changed (); 541 } 542 else if (clearnotify++ < vid.numpages) 543 { 544 } 545 else 546 con_notifylines = 0; 547} 548 549/* 550================== 551SCR_DrawConsole 552================== 553*/ 554void SCR_DrawConsole (void) 555{ 556 if (scr_con_current) 557 { 558 scr_copyeverything = 1; 559 Con_DrawConsole ((int) scr_con_current, true); 560 clearconsole = 0; 561 } 562 else 563 { 564 if (key_dest == key_game || key_dest == key_message) 565 Con_DrawNotify (); // only draw notify in game 566 } 567} 568 569 570/* 571============================================================================== 572 573 SCREEN SHOTS 574 575============================================================================== 576*/ 577 578typedef struct _TargaHeader { 579 unsigned char id_length, colormap_type, image_type; 580 unsigned short colormap_index, colormap_length; 581 unsigned char colormap_size; 582 unsigned short x_origin, y_origin, width, height; 583 unsigned char pixel_size, attributes; 584} TargaHeader; 585 586 587/* 588================== 589SCR_ScreenShot_f 590================== 591*/ 592void SCR_ScreenShot_f (void) 593{ 594 byte *buffer; 595 char pcxname[80]; 596 char checkname[MAX_OSPATH]; 597 int i, c, temp; 598// 599// find a file name to save it to 600// 601 strcpy(pcxname,"quake00.tga"); 602 603 for (i=0 ; i<=99 ; i++) 604 { 605 pcxname[5] = i/10 + '0'; 606 pcxname[6] = i%10 + '0'; 607 sprintf (checkname, "%s/%s", com_gamedir, pcxname); 608 if (Sys_FileTime(checkname) == -1) 609 break; // file doesn't exist 610 } 611 if (i==100) 612 { 613 Con_Printf ("SCR_ScreenShot_f: Couldn't create a PCX file\n"); 614 return; 615 } 616 617 618 buffer = (byte*) malloc(glwidth*glheight*3 + 18); 619 memset (buffer, 0, 18); 620 buffer[2] = 2; // uncompressed type 621 buffer[12] = glwidth&255; 622 buffer[13] = glwidth>>8; 623 buffer[14] = glheight&255; 624 buffer[15] = glheight>>8; 625 buffer[16] = 24; // pixel size 626 627 glReadPixels (glx, gly, glwidth, glheight, GL_RGB, GL_UNSIGNED_BYTE, buffer+18 ); 628 629 // swap rgb to bgr 630 c = 18+glwidth*glheight*3; 631 for (i=18 ; i<c ; i+=3) 632 { 633 temp = buffer[i]; 634 buffer[i] = buffer[i+2]; 635 buffer[i+2] = temp; 636 } 637 COM_WriteFile (pcxname, buffer, glwidth*glheight*3 + 18 ); 638 639 free (buffer); 640 Con_Printf ("Wrote %s\n", pcxname); 641} 642 643 644//============================================================================= 645 646 647/* 648=============== 649SCR_BeginLoadingPlaque 650 651================ 652*/ 653void SCR_BeginLoadingPlaque (void) 654{ 655 S_StopAllSounds (true); 656 657 if (cls.state != ca_connected) 658 return; 659 if (cls.signon != SIGNONS) 660 return; 661 662// redraw with no console and the loading plaque 663 Con_ClearNotify (); 664 scr_centertime_off = 0; 665 scr_con_current = 0; 666 667 scr_drawloading = true; 668 scr_fullupdate = 0; 669 Sbar_Changed (); 670 SCR_UpdateScreen (); 671 scr_drawloading = false; 672 673 scr_disabled_for_loading = true; 674 scr_disabled_time = realtime; 675 scr_fullupdate = 0; 676} 677 678/* 679=============== 680SCR_EndLoadingPlaque 681 682================ 683*/ 684void SCR_EndLoadingPlaque (void) 685{ 686 scr_disabled_for_loading = false; 687 scr_fullupdate = 0; 688 Con_ClearNotify (); 689} 690 691//============================================================================= 692 693const char *scr_notifystring; 694qboolean scr_drawdialog; 695 696void SCR_DrawNotifyString (void) 697{ 698 const char *start; 699 int l; 700 int j; 701 int x, y; 702 703 start = scr_notifystring; 704 705 y = (int)(vid.height*0.35); 706 707 do 708 { 709 // scan the width of the line 710 for (l=0 ; l<40 ; l++) 711 if (start[l] == '\n' || !start[l]) 712 break; 713 x = (vid.width - l*8)/2; 714 for (j=0 ; j<l ; j++, x+=8) 715 Draw_Character (x, y, start[j]); 716 717 y += 8; 718 719 while (*start && *start != '\n') 720 start++; 721 722 if (!*start) 723 break; 724 start++; // skip the \n 725 } while (1); 726} 727 728/* 729================== 730SCR_ModalMessage 731 732Displays a text string in the center of the screen and waits for a Y or N 733keypress. 734================== 735*/ 736int SCR_ModalMessage (const char *text) 737{ 738 if (cls.state == ca_dedicated) 739 return true; 740 741#if 1 742 // On Android we can't do modal key events, so just say "yes" 743 return 1; 744#else 745 scr_notifystring = text; 746 747// draw a fresh screen 748 scr_fullupdate = 0; 749 scr_drawdialog = true; 750 SCR_UpdateScreen (); 751 scr_drawdialog = false; 752 753 S_ClearBuffer (); // so dma doesn't loop current sound 754 755 do 756 { 757 key_count = -1; // wait for a key down and up 758 Sys_SendKeyEvents (); 759 } while (key_lastpress != 'y' && key_lastpress != 'n' && key_lastpress != K_ESCAPE); 760 761 scr_fullupdate = 0; 762 SCR_UpdateScreen (); 763 764 return key_lastpress == 'y'; 765#endif 766} 767 768 769//============================================================================= 770 771/* 772=============== 773SCR_BringDownConsole 774 775Brings the console down and fades the palettes back to normal 776================ 777*/ 778void SCR_BringDownConsole (void) 779{ 780 int i; 781 782 scr_centertime_off = 0; 783 784 for (i=0 ; i<20 && scr_conlines != scr_con_current ; i++) 785 SCR_UpdateScreen (); 786 787 cl.cshifts[0].percent = 0; // no area contents palette on next frame 788 VID_SetPalette (host_basepal); 789} 790 791void SCR_TileClear (void) 792{ 793 if (r_refdef.vrect.x > 0) { 794 // left 795 Draw_TileClear (0, 0, r_refdef.vrect.x, vid.height - sb_lines); 796 // right 797 Draw_TileClear (r_refdef.vrect.x + r_refdef.vrect.width, 0, 798 vid.width - r_refdef.vrect.x + r_refdef.vrect.width, 799 vid.height - sb_lines); 800 } 801 if (r_refdef.vrect.y > 0) { 802 // top 803 Draw_TileClear (r_refdef.vrect.x, 0, 804 r_refdef.vrect.x + r_refdef.vrect.width, 805 r_refdef.vrect.y); 806 // bottom 807 Draw_TileClear (r_refdef.vrect.x, 808 r_refdef.vrect.y + r_refdef.vrect.height, 809 r_refdef.vrect.width, 810 vid.height - sb_lines - 811 (r_refdef.vrect.height + r_refdef.vrect.y)); 812 } 813} 814 815/* 816================== 817SCR_UpdateScreen 818 819This is called every frame, and can also be called explicitly to flush 820text to the screen. 821 822WARNING: be very careful calling this from elsewhere, because the refresh 823needs almost the entire 256k of stack space! 824================== 825*/ 826void SCR_UpdateScreen (void) 827{ 828 static float oldscr_viewsize; 829 vrect_t vrect; 830 831 if (block_drawing) 832 return; 833 834 vid.numpages = (int)(2 + gl_triplebuffer.value); 835 836 scr_copytop = 0; 837 scr_copyeverything = 0; 838 839 if (scr_disabled_for_loading) 840 { 841 if (realtime - scr_disabled_time > 60) 842 { 843 scr_disabled_for_loading = false; 844 Con_Printf ("load failed.\n"); 845 } 846 else 847 return; 848 } 849 850 if (!scr_initialized || !con_initialized) 851 return; // not initialized yet 852 853 854 GL_BeginRendering (&glx, &gly, &glwidth, &glheight); 855 856 // 857 // determine size of refresh window 858 // 859 if (oldfov != scr_fov.value) 860 { 861 oldfov = scr_fov.value; 862 vid.recalc_refdef = true; 863 } 864 865 if (oldscreensize != scr_viewsize.value) 866 { 867 oldscreensize = scr_viewsize.value; 868 vid.recalc_refdef = true; 869 } 870 871 if (vid.recalc_refdef) 872 SCR_CalcRefdef (); 873 874// 875// do 3D refresh drawing, and then update the screen 876// 877 SCR_SetUpToDrawConsole (); 878 879 V_RenderView (); 880 881 GL_Set2D (); 882 883 // 884 // draw any areas not covered by the refresh 885 // 886 SCR_TileClear (); 887 888 if (scr_drawdialog) 889 { 890 Sbar_Draw (); 891 Draw_FadeScreen (); 892 SCR_DrawNotifyString (); 893 scr_copyeverything = true; 894 } 895 else if (scr_drawloading) 896 { 897 SCR_DrawLoading (); 898 Sbar_Draw (); 899 } 900 else if (cl.intermission == 1 && key_dest == key_game) 901 { 902 Sbar_IntermissionOverlay (); 903 } 904 else if (cl.intermission == 2 && key_dest == key_game) 905 { 906 Sbar_FinaleOverlay (); 907 SCR_CheckDrawCenterString (); 908 } 909 else 910 { 911 if (crosshair.value) 912 Draw_Character (scr_vrect.x + scr_vrect.width/2, scr_vrect.y + scr_vrect.height/2, '+'); 913 914 SCR_DrawRam (); 915 SCR_DrawNet (); 916 SCR_DrawTurtle (); 917 SCR_DrawPause (); 918 SCR_CheckDrawCenterString (); 919 Sbar_Draw (); 920 SCR_DrawConsole (); 921 M_Draw (); 922 } 923 924 V_UpdatePalette (); 925 926 GL_EndRendering (); 927} 928