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