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