]> icculus.org git repositories - taylor/freespace2.git/blob - src/osapi/outwnd.cpp
added copyright header
[taylor/freespace2.git] / src / osapi / outwnd.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/OsApi/OutWnd.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Routines for debugging output
16  *
17  * $Log$
18  * Revision 1.3  2002/06/09 04:41:25  relnev
19  * added copyright header
20  *
21  * Revision 1.2  2002/05/07 03:16:48  theoddone33
22  * The Great Newline Fix
23  *
24  * Revision 1.1.1.1  2002/05/03 03:28:10  root
25  * Initial import.
26  *
27  * 
28  * 6     6/03/99 6:37p Dave
29  * More TNT fun. Made perspective bitmaps more flexible.
30  * 
31  * 5     6/02/99 6:18p Dave
32  * Fixed TNT lockup problems! Wheeeee!
33  * 
34  * 4     12/14/98 12:13p Dave
35  * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
36  * Need to test now.
37  * 
38  * 3     10/08/98 2:38p Dave
39  * Cleanup up OsAPI code significantly. Removed old functions, centralized
40  * registry functions.
41  * 
42  * 2     10/07/98 10:53a Dave
43  * Initial checkin.
44  * 
45  * 1     10/07/98 10:50a Dave
46  * 
47  * 49    5/15/98 3:36p John
48  * Fixed bug with new graphics window code and standalone server.  Made
49  * hwndApp not be a global anymore.
50  * 
51  * 48    5/14/98 5:42p John
52  * Revamped the whole window position/mouse code for the graphics windows.
53  * 
54  * 47    5/14/98 11:24a Allender
55  * throw mprints to outtext regardless of gr mode
56  * 
57  * 46    4/30/98 4:53p John
58  * Restructured and cleaned up cfile code.  Added capability to read off
59  * of CD-ROM drive and out of multiple pack files.
60  * 
61  * 45    4/16/98 6:31p Dave
62  * Doubled MAX_FILTERS to 48
63  * 
64  * 44    3/31/98 5:18p John
65  * Removed demo/save/restore.  Made NDEBUG defined compile.  Removed a
66  * bunch of debug stuff out of player file.  Made model code be able to
67  * unload models and malloc out only however many models are needed.
68  *  
69  * 
70  * 43    3/11/98 12:06p John
71  * Made it so output window never gets focus upon startup
72  * 
73  * 42    2/16/98 9:47a John
74  * Made so only error, general, and warning are shown if no
75  * debug_filter.cfg and  .cfg file isn't saved if so.
76  * 
77  * 41    2/05/98 9:21p John
78  * Some new Direct3D code.   Added code to monitor a ton of stuff in the
79  * game.
80  * 
81  * 40    1/15/98 9:14p John
82  * Made it so general, error and warning can't be turned off.
83  * 
84  * 39    9/13/97 10:44a Lawrance
85  * allow logging of nprintf output to file
86  * 
87  * 38    8/23/97 11:32a Dave
88  * Made debug window size correctly in standalone mode.
89  * 
90  * 37    8/22/97 3:42p Hoffoss
91  * Lowered the filter limit to 24, and added support for filter recycling,
92  * instead of the rather nasty Assert if we should happen to exceed this
93  * limit.
94  * 
95  * 36    8/05/97 4:29p Dave
96  * Futzed with window sizes/placements for standalone mode.
97  * 
98  * 35    8/04/97 3:15p Dave
99  * Added an include for systemvars.h
100  * 
101  * 34    5/20/97 1:13p Allender
102  * fixes to make outwnd work better under Win95
103  * 
104  * 33    5/14/97 11:08a John
105  * fixed bug under 95 that occasionally hung program.
106  * 
107  * 32    5/07/97 3:01p Lawrance
108  * check for NT in mono_init
109  * 
110  * 31    5/07/97 3:06p John
111  * disabled new mono direct stuff under NT.
112  * 
113  * 30    5/07/97 2:59p John
114  * Made so mono driver works under '95.
115  * 
116  * 29    4/22/97 10:56a John
117  * fixed some resource leaks.
118  *
119  * $NoKeywords: $
120  */
121
122 #ifndef NDEBUG
123
124 #include <windows.h>
125 #include <stdio.h>
126 #include <stdarg.h>
127 #include <string.h>
128
129 // to disable otherwise well-lodged compiler warning
130 #pragma warning(disable: 4201)
131
132 #include <winioctl.h>
133 #include <conio.h>
134
135 #include "osapi.h"
136 #include "outwnd.h"
137 #include "cfile.h"
138 #include "monopub.h"
139 #include "2d.h"
140 #include "freespaceresource.h"
141 #include "systemvars.h"
142
143 #define MAX_FILTERS 48
144 #define SCROLL_BUFFER_SIZE      512
145 #define MAX_LINE_WIDTH  128
146 #define TASKBAR_HEIGHT 30
147
148 #define ID_COPY 32000
149 #define ID_FIND 32010
150 #define ID_FILTER 32100
151 #define TIMER1 1
152 #define UP_FAST 65537
153 #define DOWN_FAST 65538
154
155 HANDLE hOutputThread=NULL;
156 DWORD OutputThreadID;
157
158 HWND hOutputWnd;
159 char szOutputClass[] = "OutputWindow";
160 char spaces[MAX_LINE_WIDTH + 1];
161 int outwnd_filter_count = 0;
162 int outwnd_filter_loaded = 0;
163 char find_text[85];
164
165 struct outwnd_filter_struct {
166         char name[FILTER_NAME_LENGTH];
167         int state;
168 } *outwnd_filter[MAX_FILTERS], real_outwnd_filter[MAX_FILTERS];
169
170 int mprintf_last_line = -1;
171 char outtext[SCROLL_BUFFER_SIZE][MAX_LINE_WIDTH];
172 int old_scroll_pos = -32768;
173 int nTextHeight=0, nTextWidth=0, nCharRows=0;
174 int outwnd_inited = FALSE;
175 int outwnd_disabled = TRUE;
176 int max_scroll_pos = SCROLL_BUFFER_SIZE - 1;
177 static int marked = 0;
178 int marked_top, marked_bottom, marked_left, marked_right;
179 int old_marked = 0, old_marked_top, old_marked_bottom, old_marked_left, old_marked_right;
180 int marking_started_x, marking_started_y, client_top_yoffset, cur_line_index, marking_active = 0;
181 int scroll_wait = 1;
182 BOOL OutputActive = FALSE;
183 LPARAM last_mousemove;
184 int Outwnd_changed = 0;
185 int find_line = -1, find_pos;
186
187 // monochrome screen info
188 HANDLE  mono_driver=NULL;                               // handle to the monochrome driver
189
190 void outwnd_print(char *id, char *tmp);
191 BOOL CALLBACK find_dlg_handler(HWND hwnd,UINT msg,WPARAM wParam, LPARAM lParam);
192 void find_text_in_outwindow(int n, int p);
193 void outwnd_copy_marked_selection(HWND hwnd);
194 void outwnd_update_marking(LPARAM l_parm, HWND hwnd);
195 void outwnd_paint(HWND hwnd);
196
197 int Outwnd_no_filter_file = 0;          // 0 = .cfg file found, 1 = not found and warning not printed yet, 2 = not found and warning printed
198
199 // used for file logging
200 #ifndef NDEBUG
201         int Log_debug_output_to_file = 0;
202         FILE *Log_fp;
203         char *Freespace_logfilename = "fs.log";
204 #endif
205
206 inline void text_hilight(HDC &hdc)
207 {
208         SetBkColor(hdc, RGB(0, 0, 0));
209         SetTextColor(hdc, RGB(255, 255, 255));
210 }
211
212 inline void text_normal(HDC &hdc)
213 {
214         SetBkColor(hdc, RGB(255, 255, 255));
215         SetTextColor(hdc, RGB(0, 0, 0));
216 }
217
218 inline void fix_marking_coords(int &x, int &y, LPARAM l_parm)
219 {
220         x = (signed short) LOWORD(l_parm) / nTextWidth;
221         y = ((signed short) HIWORD(l_parm) - client_top_yoffset) / nTextHeight + cur_line_index;
222         
223         if (y < 0)
224                 y = x = 0;
225
226         if (y > SCROLL_BUFFER_SIZE)
227         {
228                 y = SCROLL_BUFFER_SIZE;
229                 x = 0;
230         }
231
232         if (x < 0)
233                 x = -1;
234
235         if (x > MAX_LINE_WIDTH)
236         {
237                 y++;
238                 x = 0;  // marks to end of line
239         }
240 }
241
242
243 //                      InvalidateRect(hOutputWnd,NULL,0);
244
245 void load_filter_info(void)
246 {
247         FILE *fp;
248         char pathname[256], inbuf[FILTER_NAME_LENGTH+4];
249         int z;
250
251         outwnd_filter_loaded = 1;
252         outwnd_filter_count = 0;
253         strcpy(pathname, "Debug_filter.cfg" );
254         fp = fopen(pathname, "rt");
255         if (!fp)        {
256                 Outwnd_no_filter_file = 1;
257
258                 outwnd_filter[outwnd_filter_count] = &real_outwnd_filter[outwnd_filter_count];
259                 strcpy( outwnd_filter[outwnd_filter_count]->name, "error" );
260                 outwnd_filter[outwnd_filter_count]->state = 1;
261                 outwnd_filter_count++;
262
263                 outwnd_filter[outwnd_filter_count] = &real_outwnd_filter[outwnd_filter_count];
264                 strcpy( outwnd_filter[outwnd_filter_count]->name, "general" );
265                 outwnd_filter[outwnd_filter_count]->state = 1;
266                 outwnd_filter_count++;
267
268                 outwnd_filter[outwnd_filter_count] = &real_outwnd_filter[outwnd_filter_count];
269                 strcpy( outwnd_filter[outwnd_filter_count]->name, "warning" );
270                 outwnd_filter[outwnd_filter_count]->state = 1;
271                 outwnd_filter_count++;
272
273                 return;
274         }
275
276         Outwnd_no_filter_file = 0;
277
278         while (fgets(inbuf, FILTER_NAME_LENGTH+3, fp))
279         {
280                 if (outwnd_filter_count == MAX_FILTERS)
281                         break;
282
283                 outwnd_filter[outwnd_filter_count] = &real_outwnd_filter[outwnd_filter_count];
284                 if (*inbuf == '+')
285                         outwnd_filter[outwnd_filter_count]->state = 1;
286                 else if (*inbuf == '-')
287                         outwnd_filter[outwnd_filter_count]->state = 0;
288                 else continue;  // skip everything else
289
290                 z = strlen(inbuf) - 1;
291                 if (inbuf[z] == '\n')
292                         inbuf[z] = 0;
293
294                 Assert(strlen(inbuf+1) < FILTER_NAME_LENGTH);
295                 strcpy(outwnd_filter[outwnd_filter_count]->name, inbuf + 1);
296
297                 if ( !stricmp( outwnd_filter[outwnd_filter_count]->name, "error" ) )    {
298                         outwnd_filter[outwnd_filter_count]->state = 1;
299                 } else if ( !stricmp( outwnd_filter[outwnd_filter_count]->name, "general" ) )   {
300                         outwnd_filter[outwnd_filter_count]->state = 1;
301                 } else if ( !stricmp( outwnd_filter[outwnd_filter_count]->name, "warning" ) )   {
302                         outwnd_filter[outwnd_filter_count]->state = 1;
303                 }
304
305                 outwnd_filter_count++;
306         }
307
308         if (ferror(fp) && !feof(fp))
309                 nprintf(("Error", "Error reading \"%s\"\n", pathname));
310
311         fclose(fp);
312 }
313
314 void save_filter_info(void)
315 {
316         FILE *fp;
317         int i;
318         char pathname[256];
319
320         if (!outwnd_filter_loaded)
321                 return;
322
323         if ( Outwnd_no_filter_file )    {
324                 return; // No file, don't save
325         }
326
327         strcpy(pathname, "Debug_filter.cfg" );
328         fp = fopen(pathname, "wt");
329         if (fp)
330         {
331                 for (i=0; i<outwnd_filter_count; i++)
332                         fprintf(fp, "%c%s\n", outwnd_filter[i]->state ? '+' : '-', outwnd_filter[i]->name);
333
334                 fclose(fp);
335         }
336 }
337
338 void outwnd_printf2(char *format, ...)
339 {
340         char tmp[MAX_LINE_WIDTH*4];
341         va_list args;
342         
343         va_start(args, format);
344         vsprintf(tmp, format, args);
345         va_end(args);
346         outwnd_print("General", tmp);
347 }
348
349
350 char mono_ram[80*25*2];
351 int mono_x, mono_y;
352 int mono_found = 0;
353
354 void mono_flush()
355 {
356         if ( !mono_found ) return;
357         memcpy( (void *)0xb0000, mono_ram, 80*25*2 );
358 }
359
360 void mono_init()
361 {
362         int i;
363
364         OSVERSIONINFO ver;
365         
366         ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
367         GetVersionEx(&ver);
368         if ( ver.dwPlatformId == VER_PLATFORM_WIN32_NT )        {
369                 mono_found = 0;
370                 return;
371         }
372
373         _outp( 0x3b4, 0x0f );
374         _outp( 0x3b4+1, 0x55 );
375
376         if ( _inp( 0x3b4+1 ) == 0x55 )  {
377                 mono_found = 1;
378                 _outp( 0x3b4+1, 0 );
379         } else {
380                 mono_found = 0;
381                 return;
382         }
383
384
385         for (i=0; i<80*25; i++ )        {
386                 mono_ram[i*2+0] = ' ';
387                 mono_ram[i*2+1] = 0x07;
388         }
389         mono_flush();
390         mono_x = mono_y = 0;
391 }
392
393
394 void mono_print( char * text, int len )
395 {
396         int i, j;
397
398         if ( !mono_found ) return;
399
400         for (i=0; i<len; i++ )  {
401                 int scroll_it = 0;
402
403                 switch( text[i] )       {
404                 case '\n':
405                         scroll_it = 1;
406                         break;
407                 default:
408                         mono_ram[mono_y*160+mono_x*2] = text[i];
409                         if ( mono_x < 79 )      {
410                                 mono_x++;
411                         } else {
412                                 scroll_it = 1;
413                         }
414                         break;
415                 }
416
417                 if ( scroll_it )        {
418                         if ( mono_y < 24 )      {
419                                 mono_y++;
420                                 mono_x = 0;
421                         } else {
422                                 memmove( mono_ram, mono_ram+160, 80*24*2 );
423                                 for (j=0; j<80; j++ )   {
424                                         mono_ram[24*160+j*2] = ' ';
425                                 }
426                                 mono_y = 24;
427                                 mono_x = 0;
428                         }
429                 }
430         }
431         mono_flush();
432 }
433
434 void outwnd_printf(char *id, char *format, ...)
435 {
436         char tmp[MAX_LINE_WIDTH*4];
437         va_list args;
438         
439         va_start(args, format);
440         vsprintf(tmp, format, args);
441         va_end(args);
442         outwnd_print(id, tmp);
443 }
444
445 void outwnd_print(char *id, char *tmp)
446 {
447         char *sptr;
448         char *dptr;
449         int i, nrows, ccol;
450         outwnd_filter_struct *temp;
451
452         if(gr_screen.mode == GR_DIRECT3D){
453                 return;
454         }
455
456         if (!outwnd_inited)
457                 return;
458
459         if ( Outwnd_no_filter_file == 1 )       {
460                 Outwnd_no_filter_file = 2;
461
462                 outwnd_print( "general", "==========================================================================\n" );
463                 outwnd_print( "general", "DEBUG SPEW: No debug_filter.cfg found, so only general, error, and warning\n" );
464                 outwnd_print( "general", "categories can be shown and no debug_filter.cfg info will be saved.\n" );
465                 outwnd_print( "general", "==========================================================================\n" );
466         }
467
468         if (!id)
469                 id = "General";
470
471         for (i=0; i<outwnd_filter_count; i++)
472                 if (!stricmp(id, outwnd_filter[i]->name))
473                         break;
474
475
476         if (i == outwnd_filter_count)  // new id found that's not yet in filter list
477         {
478                 // Only create new filters if there was a filter file
479                 if ( Outwnd_no_filter_file )    {
480                         return;
481                 }
482
483                 if (outwnd_filter_count >= MAX_FILTERS) {
484                         Assert(outwnd_filter_count == MAX_FILTERS);  // how did it get over the max?  Very bad..
485                         outwnd_printf("General", "Outwnd filter limit reached.  Recycling \"%s\" to add \"%s\"",
486                                 outwnd_filter[MAX_FILTERS - 1]->name, id);
487
488                         i--;  // overwrite the last element (oldest used filter in the list)
489                 }
490
491                 Assert(strlen(id) < FILTER_NAME_LENGTH);
492                 outwnd_filter[i] = &real_outwnd_filter[i];  // note: this assumes the list doesn't have gaps (from deleting an element for example)
493                 strcpy(outwnd_filter[i]->name, id);
494                 outwnd_filter[i]->state = 1;
495                 outwnd_filter_count = i + 1;
496                 save_filter_info();
497         }
498
499         // sort the filters from most recently used to oldest, so oldest ones will get recycled first
500         temp = outwnd_filter[i];
501         while (i--)
502                 outwnd_filter[i + 1] = outwnd_filter[i];
503
504         i++;
505         outwnd_filter[i] = temp;
506
507         if (!outwnd_filter[i]->state)
508                 return;
509
510         if (mprintf_last_line == -1 )   {
511                 for (i=0; i<SCROLL_BUFFER_SIZE;i++)     {
512                         outtext[i][0] = 0;
513                 }
514
515                 mprintf_last_line = 0;
516         }
517
518         // printf out to the monochrome screen first
519         if ( mono_driver != ((HANDLE)-1) ) {
520                 DWORD   cbReturned;
521
522                 DeviceIoControl (mono_driver, (DWORD)IOCTL_MONO_PRINT, tmp, strlen(tmp), NULL, 0, &cbReturned, 0 );
523         } else {
524                 mono_print(tmp, strlen(tmp) );
525         }
526
527         sptr = tmp;
528         ccol = strlen(outtext[mprintf_last_line] );
529         dptr = &outtext[mprintf_last_line][ccol];
530         nrows = 0;
531
532 #ifndef NDEBUG
533
534         if ( Log_debug_output_to_file ) {
535                 if ( Log_fp != NULL ) {
536                         fputs(tmp, Log_fp);     
537                         fflush(Log_fp);
538                 }
539         }
540
541 #endif
542
543         while(*sptr) {
544                 if ( (*sptr == '\n') || (ccol >= MAX_LINE_WIDTH-1 ) )   {
545                         nrows++;
546                         mprintf_last_line++;
547                         if (mprintf_last_line >= SCROLL_BUFFER_SIZE )
548                                 mprintf_last_line = 0;
549                         ccol = 0;
550                         if ( *sptr != '\n' )    {
551                                 outtext[mprintf_last_line][ccol]        = *sptr;
552                                 ccol++;
553                         }
554                         outtext[mprintf_last_line][ccol] = '\0';
555                         dptr = &outtext[mprintf_last_line][ccol];
556                 } else {
557                         *dptr++ = *sptr;
558                         *dptr = '\0';
559                         ccol++;
560                 }
561                 sptr++;
562         } 
563
564         if(gr_screen.mode == GR_DIRECT3D){
565                 return;
566         }
567 //      if ( D3D_enabled )      {
568 //              return;         // Direct3D seems to hang sometimes printing to window
569 //      }
570
571         if ( outwnd_disabled ){
572                 return;
573         }
574
575         if ( !OutputActive )    {
576                 int oldpos = GetScrollPos( hOutputWnd, SB_VERT );
577                 if ( oldpos != max_scroll_pos ) {
578                         SCROLLINFO si;
579                         si.cbSize = sizeof(SCROLLINFO);
580                         si.fMask = SIF_POS;
581                         si.nPos = max_scroll_pos;
582                         SetScrollInfo(hOutputWnd, SB_VERT, &si, 1 );
583                         InvalidateRect(hOutputWnd,NULL,0);
584                         UpdateWindow(hOutputWnd);
585                 } else {
586                         if ( nrows )    {
587                                 RECT client;
588                                 ScrollWindow(hOutputWnd,0,-nTextHeight*nrows,NULL,NULL);
589                                 GetClientRect(hOutputWnd, &client);
590                                 client.top = client.bottom - nTextHeight*(nrows+1);
591                                 InvalidateRect(hOutputWnd,&client,0);
592
593                                 UpdateWindow(hOutputWnd);
594                         } else {
595                                 Outwnd_changed++;
596                         }
597                 }
598         }
599 }
600
601 LRESULT CALLBACK outwnd_handler(HWND hwnd,UINT msg,WPARAM wParam, LPARAM lParam)
602 {
603         
604         switch(msg)     {
605         case WM_ACTIVATEAPP:
606                 // The application z-ordering has change
607                 // foreground application wParm will be
608                 OutputActive = (BOOL)wParam;
609                 break;
610
611         case WM_CREATE: {
612                         SCROLLINFO si;
613                         si.cbSize = sizeof(SCROLLINFO);
614                         si.fMask = SIF_RANGE | SIF_POS;
615                         si.nMin = 0;
616                         si.nMax = max_scroll_pos;
617                         si.nPos = max_scroll_pos;
618                         SetScrollInfo(hwnd, SB_VERT, &si, 1 );
619                 }
620                 break;
621
622         case WM_TIMER:
623                 if (scroll_wait)
624                         PostMessage(hwnd, WM_MOUSEMOVE, 0, last_mousemove);
625
626                 if ( Outwnd_changed )   {
627                         RECT client;
628                         GetClientRect(hOutputWnd, &client);
629                         client.top = client.bottom - nTextHeight;
630                         InvalidateRect(hOutputWnd,&client,0);
631                 }
632
633                 scroll_wait = 0;
634                 break;
635
636         case WM_COMMAND:        {
637                 int z;
638
639                 z = LOWORD(wParam);
640                 if (z >= ID_FILTER && z < ID_FILTER + outwnd_filter_count)
641                 {
642                         z -= ID_FILTER;
643                         outwnd_filter[z]->state = !outwnd_filter[z]->state;
644
645                         if ( !stricmp( outwnd_filter[z]->name, "error" ) )      {
646                                 outwnd_filter[z]->state = 1;
647                         } else if ( !stricmp( outwnd_filter[z]->name, "general" ) )     {
648                                 outwnd_filter[z]->state = 1;
649                         } else if ( !stricmp( outwnd_filter[z]->name, "warning" ) )     {
650                                 outwnd_filter[z]->state = 1;
651                         }
652                         save_filter_info();
653                         break;
654                 }
655
656                 switch (z)
657                 {
658                         case ID_COPY:
659                                 outwnd_copy_marked_selection(hwnd);
660                                 break;
661
662                         /*
663                         case ID_FIND:
664                                 if (DialogBox(GetModuleHandle(NULL), "FIND_DIALOG", hOutputWnd,
665                                         (int (__stdcall *)(void)) find_dlg_handler) == IDOK)
666                                 {
667                                         find_text_in_outwindow(mprintf_last_line, 0);
668                                 }
669
670                                 break;
671                                 */
672                 }
673                 break;
674         }
675
676         case WM_RBUTTONDOWN:    {
677                         HMENU h_menu = CreatePopupMenu();
678                         HMENU h_sub_menu = CreatePopupMenu();
679                         POINT pt;
680                         int i;
681
682                         for (i=0; i<outwnd_filter_count; i++)
683                         {
684                                 UINT flags = MFT_STRING;        //MF_GRAYED;
685
686                                 if ( !stricmp( outwnd_filter[i]->name, "error" ) )      {
687                                         flags |= MF_GRAYED;
688                                 } else if ( !stricmp( outwnd_filter[i]->name, "general" ) )     {
689                                         flags |= MF_GRAYED;
690                                 } else if ( !stricmp( outwnd_filter[i]->name, "warning" ) )     {
691                                         flags |= MF_GRAYED;
692                                 }
693
694                                 if (outwnd_filter[i]->state)
695                                         AppendMenu(h_sub_menu, flags | MF_CHECKED, ID_FILTER + i, outwnd_filter[i]->name);
696                                 else
697                                         AppendMenu(h_sub_menu, flags, ID_FILTER + i, outwnd_filter[i]->name);
698                         }
699
700                         AppendMenu(h_menu, MFT_STRING, ID_COPY, "&Copy\tEnter");
701                         AppendMenu(h_menu, MFT_STRING, ID_FIND, "&Find Text");
702                         AppendMenu(h_menu, MF_POPUP, (unsigned int) h_sub_menu, "Filter &Messages");
703                         pt.x = LOWORD(lParam);
704                         pt.y = HIWORD(lParam);
705                         ClientToScreen(hwnd, &pt);
706
707                         TrackPopupMenu(h_menu, 0, pt.x, pt.y, 0, hwnd, NULL);
708                         DestroyMenu(h_menu);
709                         break;
710                 }
711                 
712         case WM_LBUTTONDOWN:
713                 fix_marking_coords(marking_started_x, marking_started_y, lParam);
714                 SetCapture(hwnd);  // monopolize mouse
715                 marking_active = 1;
716                 outwnd_update_marking(lParam, hwnd);
717                 break;
718
719         case WM_MOUSEMOVE:
720                 last_mousemove = lParam;
721                 if (marking_active){
722                         outwnd_update_marking(lParam, hwnd);
723                 }
724                 break;
725
726         case WM_LBUTTONUP:
727                 if (marking_active)
728                 {
729                         ReleaseCapture();
730                         marking_active = 0;
731                         outwnd_update_marking(lParam, hwnd);
732                 }
733                 break;
734         
735         case WM_VSCROLL:        {
736                         SCROLLINFO si;
737                         int vpos = GetScrollPos( hwnd, SB_VERT );
738                         int old_vpos=vpos;
739                         switch (LOWORD(wParam)) {
740                         case SB_LINEDOWN:
741                                 vpos++;
742                                 break;
743                         case SB_LINEUP:
744                                 vpos--;
745                                 break;
746                         case SB_THUMBPOSITION:
747                                 vpos = HIWORD(wParam);
748                                 break;
749                         case SB_THUMBTRACK:
750                                 vpos = HIWORD(wParam);
751                                 break;
752                         case SB_PAGEDOWN:
753                                 vpos += nCharRows;
754                                 break;
755                         case SB_PAGEUP:
756                                 vpos -= nCharRows;
757                                 break;
758                         }
759                         if ( vpos < 0 ) vpos = 0;
760                         else if ( vpos > max_scroll_pos ) vpos = max_scroll_pos;
761                         si.cbSize = sizeof(SCROLLINFO);
762                         si.fMask = SIF_POS;
763                         si.nPos = vpos;
764                         SetScrollInfo(hwnd, SB_VERT, &si, 1 );
765                         ScrollWindow(hwnd,0,(old_vpos-vpos)*nTextHeight,NULL,NULL);
766                         UpdateWindow(hOutputWnd);
767                         //InvalidateRect(hOutputWnd,NULL,0);
768                 }
769                 break;
770
771         case WM_KEYDOWN:        {
772                         SCROLLINFO si;
773                         int vpos = GetScrollPos( hwnd, SB_VERT );
774                         int old_vpos=vpos;
775                         int nVirtKey = (int) wParam;  // virtual-key code
776                         switch(nVirtKey)        {
777                         case VK_DOWN:
778                         case VK_RIGHT:
779                                 vpos++;
780                                 break;
781                         case VK_UP:
782                         case VK_LEFT:
783                                 vpos--;
784                                 break;
785                         case VK_NEXT:
786                                 vpos += nCharRows;
787                                 break;
788                         case VK_PRIOR:
789                                 vpos -= nCharRows;
790                                 break;
791                         case VK_END:
792                                 vpos = 0;
793                                 break;
794                         case VK_HOME:
795                                 vpos = max_scroll_pos;
796                                 break;
797                         case VK_RETURN:
798                                 outwnd_copy_marked_selection(hwnd);
799                                 break;
800                         case UP_FAST:  // special value we define
801                                 vpos -= 5;
802                                 break;
803                         case DOWN_FAST:  // special value we define
804                                 vpos += 5;
805                                 break;
806                         }
807                         
808                         if ( vpos < 0 ) vpos = 0;
809                         else if ( vpos > max_scroll_pos ) vpos = max_scroll_pos;
810                         si.cbSize = sizeof(SCROLLINFO);
811                         si.fMask = SIF_POS;
812                         si.nPos = vpos;
813                         SetScrollInfo(hwnd, SB_VERT, &si, 1 );
814                         ScrollWindow(hwnd, 0, (old_vpos-vpos)*nTextHeight, NULL, NULL);
815                         UpdateWindow(hOutputWnd);
816                         //InvalidateRect(hOutputWnd,NULL,0);
817                 }
818                 break;
819
820         case WM_SIZE:
821                 InvalidateRect(hOutputWnd,NULL,0);
822                 break;
823
824         case WM_PAINT:
825                 outwnd_paint(hwnd);
826                 break;
827
828         case WM_DESTROY:
829                 outwnd_disabled = 1;
830                 PostQuitMessage(0);
831                 break;
832
833         default:
834                 return DefWindowProc(hwnd, msg, wParam, lParam);
835                 break;
836         }
837
838         return 0;
839 }
840
841 void outwnd_copy_marked_selection(HWND hwnd)
842 {
843         HGLOBAL h_text;
844         int i, size = 1;
845         char *ptr;
846
847         if (marked_top == marked_bottom)
848         {
849                 size += marked_right - marked_left;
850
851         } else {
852                 i = marked_top;
853                 size += strlen(outtext[i++]) - marked_left + 2;
854
855                 while (i < marked_bottom)
856                         size += strlen(outtext[i++]) + 2;
857
858                 size += marked_right;
859         }
860
861         h_text = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, size);
862         ptr = (char *) GlobalLock(h_text);
863
864         if (marked_top == marked_bottom)
865         {
866                 i = marked_right - marked_left;
867                 memcpy(ptr, outtext[marked_top] + marked_left, i);
868                 ptr[i] = 0;
869
870         } else {
871                 i = marked_top;
872                 strcpy(ptr, outtext[i] + marked_left);
873                 strcat(ptr, "\r\n");
874                 i++;
875
876                 while (i < marked_bottom)
877                 {
878                         strcat(ptr, outtext[i++]);
879                         strcat(ptr, "\r\n");
880                 }
881
882                 ptr[strlen(ptr) + marked_right] = 0;
883                 strncat(ptr, outtext[i], marked_right);
884         }
885
886         GlobalUnlock(h_text);
887         OpenClipboard(hwnd);
888         EmptyClipboard();
889         SetClipboardData(CF_TEXT, h_text);
890         CloseClipboard();
891
892         marked = 0;
893         InvalidateRect(hwnd, NULL, 0);
894 }
895
896 void outwnd_paint(HWND hwnd)
897 {
898         int i, n, x, y, len;
899         int old_nrows, scroll_pos;
900         HDC hdc;
901         PAINTSTRUCT ps; 
902         RECT client;
903         TEXTMETRIC tm;
904         HFONT newfont, oldfont;
905         HBRUSH newbrush, oldbrush;
906
907         Outwnd_changed = 0;
908
909         hdc = BeginPaint(hwnd, &ps);
910         GetClientRect(hOutputWnd, &client);
911         newfont = (HFONT)GetStockObject(ANSI_FIXED_FONT);
912         oldfont = (HFONT)SelectObject(hdc,newfont);
913         
914         GetTextMetrics(hdc, &tm);
915         nTextHeight = tm.tmHeight + tm.tmExternalLeading;
916         nTextWidth = tm.tmAveCharWidth;
917         old_nrows = nCharRows;
918         nCharRows = ((client.bottom-client.top)/nTextHeight)+1;
919
920         newbrush = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
921         oldbrush = (HBRUSH)SelectObject(hdc,newbrush);
922         
923         y = client.bottom - nTextHeight * nCharRows;  // starting y position at top
924         client_top_yoffset = y - nTextHeight;
925         scroll_pos = (max_scroll_pos - GetScrollPos( hOutputWnd, SB_VERT ));
926         cur_line_index = x = mprintf_last_line - scroll_pos - nCharRows;
927         if (x >= marked_top && x < marked_bottom)  // starting in marked area
928                 text_hilight(hdc);
929         else
930                 text_normal(hdc);
931         if (scroll_pos != old_scroll_pos) {
932                 if (!scroll_pos)        {
933                         char tmp[1024];
934                         sprintf( tmp, "Debug Spew");
935                         SetWindowText( hOutputWnd, tmp );
936
937                 } else {
938                         char tmp[1024];
939                         sprintf( tmp, "Debug Spew [Scrolled back %d lines]", scroll_pos );
940                         SetWindowText( hOutputWnd, tmp );
941                 }
942
943                 old_scroll_pos = scroll_pos;
944         }
945
946         i = nCharRows;
947         while (i--)
948         {
949                 n = mprintf_last_line - scroll_pos - i;
950                 if (n < 0)
951                         n += SCROLL_BUFFER_SIZE;
952         
953                 if (n >= 0 && n < SCROLL_BUFFER_SIZE)
954                 {
955                         len = strlen(outtext[n]);
956                         if (marked)
957                         {
958                                 if (n == marked_top && n == marked_bottom)  // special 1 line case
959                                 {
960                                         if (marked_left)
961                                                 TextOut(hdc, 0, y, outtext[n], marked_left);
962
963                                         text_hilight(hdc);
964                                         x = marked_left * nTextWidth;
965                                         TextOut(hdc, x, y, outtext[n] + marked_left, marked_right -
966                                                 marked_left);
967
968                                         text_normal(hdc);
969                                         x = marked_right * nTextWidth;
970                                         if (marked_right < len)
971                                                 TextOut(hdc, x, y, outtext[n] + marked_right, len - marked_right);
972
973                                         x = len * nTextWidth;
974                                         TextOut(hdc, x, y, spaces, MAX_LINE_WIDTH - len);
975
976                                 } else if (n == marked_top)     {  // start marked on this line
977                                         if (marked_left)
978                                                 TextOut(hdc, 0, y, outtext[n], marked_left);
979
980                                         text_hilight(hdc);
981                                         x = marked_left * nTextWidth;
982
983                                         TextOut(hdc, x, y, outtext[n] + marked_left, len - marked_left);
984
985                                         x = len * nTextWidth;
986                                         if (marked_left < MAX_LINE_WIDTH)
987                                                 TextOut(hdc, x, y, spaces, MAX_LINE_WIDTH - marked_left);
988
989                                 } else if (n == marked_bottom)  {  // end marked on this line
990                                         if (marked_right)
991                                                 TextOut(hdc, 0, y, outtext[n], marked_right);
992
993                                         text_normal(hdc);
994                                         x = marked_right * nTextWidth;
995                                         if (marked_right < len)
996                                                 TextOut(hdc, x, y, outtext[n] + marked_right, len - marked_right);
997
998                                         x = len * nTextWidth;
999                                         TextOut(hdc, x, y, spaces, MAX_LINE_WIDTH - len);
1000
1001                                 } else  {  // whole line marked
1002                                         TextOut(hdc, 0, y, outtext[n], len);
1003                                         x = len * nTextWidth;
1004                                         TextOut(hdc, x, y, spaces, MAX_LINE_WIDTH - len);
1005                                 }
1006
1007                         } else {
1008                                 TextOut(hdc, 0, y, outtext[n], len);
1009                                 x = len * nTextWidth;
1010                                 TextOut(hdc, x, y, spaces, MAX_LINE_WIDTH - len);
1011                         }
1012                 } else
1013                         TextOut(hdc, 0, y, spaces, MAX_LINE_WIDTH);
1014
1015                 y += nTextHeight;
1016         }
1017
1018         text_normal(hdc);
1019         SelectObject(hdc, oldfont);
1020         SelectObject(hdc, oldbrush);
1021         DeleteObject(newbrush);
1022
1023         if ( old_nrows != nCharRows )   {
1024                 SCROLLINFO si;
1025                 max_scroll_pos = SCROLL_BUFFER_SIZE-nCharRows - 1;
1026                 si.cbSize = sizeof(SCROLLINFO);
1027                 si.fMask = SIF_RANGE;
1028                 si.nMin = 0;
1029                 si.nMax = max_scroll_pos;
1030                 SetScrollInfo(hwnd, SB_VERT, &si, 1 );
1031         }
1032
1033         EndPaint(hwnd, &ps);
1034
1035 }
1036
1037 void outwnd_update_marking(LPARAM l_parm, HWND hwnd)
1038 {
1039         int x, y;
1040         RECT rect;
1041
1042         if (!scroll_wait)       {
1043                 y = (signed short) HIWORD(l_parm);
1044                 GetClientRect(hwnd, &rect);
1045                 
1046                 if (y < -150)   {
1047                         SendMessage(hwnd, WM_KEYDOWN, UP_FAST, 0);
1048                         scroll_wait = 1;
1049
1050                 } else if (y < 0)       {
1051                         SendMessage(hwnd, WM_KEYDOWN, VK_UP, 0);
1052                         scroll_wait = 1;
1053                 }
1054
1055                 if (y >= rect.bottom + 150)     {
1056                         SendMessage(hwnd, WM_KEYDOWN, DOWN_FAST, 0);
1057                         scroll_wait = 1;
1058
1059                 } else if (y >= rect.bottom)    {
1060                         SendMessage(hwnd, WM_KEYDOWN, VK_DOWN, 0);
1061                         scroll_wait = 1;
1062                 }
1063         }
1064
1065         fix_marking_coords(x, y, l_parm);
1066         marked = 1;
1067         marked_top = marked_bottom = marking_started_y;
1068         marked_left = marking_started_x;
1069         marked_right = marking_started_x + 1;
1070
1071         if (y < marked_top)
1072         {
1073                 marked_top = y;
1074                 marked_left = x;
1075
1076         } else if (y > marked_bottom)   {
1077                 marked_bottom = y;
1078                 marked_right = x + 1;
1079
1080         } else {  // must be single line case
1081                 if (x < marked_left)
1082                         marked_left = x;
1083                 if (x >= marked_right)
1084                         marked_right = x + 1;
1085
1086                 if (marked_left >= (signed int) strlen(outtext[y]))
1087                 {
1088                         marked = 0;  // this isn't going to even show up
1089                         return;
1090                 }
1091         }
1092
1093         if (marked_left >= (signed int) strlen(outtext[marked_top]))
1094         {
1095                 marked_top++;
1096                 marked_left = 0;
1097         }
1098
1099         if (marked_right > (signed int) strlen(outtext[marked_bottom]))
1100                 marked_right = strlen(outtext[marked_bottom]);
1101
1102         if (marked && (marked_top > marked_bottom || (marked_top == marked_bottom &&
1103                 marked_left >= marked_right)))
1104         {
1105                 char msg[1024];
1106
1107                 sprintf(msg, "Marking limits invalid!\n"
1108                         "(%d,%d) to (%d,%d)", marked_left, marked_top, marked_right, marked_bottom);
1109
1110                 MessageBox(hwnd, msg, "Error", MB_OK | MB_ICONERROR);
1111                 marked = 0;
1112         }
1113
1114         if (marked != old_marked || marked_top != old_marked_top || marked_bottom !=
1115                 old_marked_bottom || marked_left != old_marked_left || marked_right !=
1116                 old_marked_right)
1117         {
1118                 old_marked = marked;
1119                 old_marked_left = marked_left;
1120                 old_marked_right = marked_right;
1121                 old_marked_top = marked_top;
1122                 old_marked_bottom = marked_bottom;
1123                 InvalidateRect(hwnd, NULL, 0);
1124         }
1125 }
1126
1127 BOOL outwnd_create(int display_under_freespace_window)
1128 {
1129         int x;
1130         WNDCLASSEX wclass;
1131         HINSTANCE hInst = GetModuleHandle(NULL);
1132         RECT rect;
1133         DWORD style;
1134
1135         wclass.hInstance                = hInst;
1136         wclass.lpszClassName    = szOutputClass;
1137         wclass.lpfnWndProc              = (WNDPROC) outwnd_handler;
1138         wclass.style                    = CS_BYTEALIGNCLIENT | CS_OWNDC;        //CS_DBLCLKS | CS_PARENTDC| CS_VREDRAW | CS_HREDRAW |;
1139         wclass.cbSize                   = sizeof(WNDCLASSEX);
1140         wclass.hIcon                    = LoadIcon(NULL, IDI_APPLICATION);
1141         wclass.hIconSm                  = LoadIcon(NULL, IDI_APPLICATION);
1142         wclass.hCursor                  = LoadCursor(NULL, IDC_ARROW);
1143         wclass.lpszMenuName             = NULL; //"FreeSpaceMenu";
1144         wclass.cbClsExtra               = 0;
1145         wclass.cbWndExtra               = 0;
1146         wclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);     //(HBRUSH)NULL;
1147
1148         if (!RegisterClassEx(&wclass))
1149                 return FALSE;
1150
1151         if (display_under_freespace_window)     {
1152                 style = WS_OVERLAPPEDWINDOW;;
1153
1154                 RECT client_rect;
1155                 client_rect.left = client_rect.top = 0;
1156                 client_rect.right = 640;
1157                 client_rect.bottom = 480;
1158                 AdjustWindowRect(&client_rect,WS_CAPTION | WS_SYSMENU,FALSE);
1159
1160                 int x = (GetSystemMetrics( SM_CXSCREEN )-(client_rect.right - client_rect.left))/2;
1161                 int y = 0;
1162                 if ( x < 0 ) x = 0;
1163
1164                 rect.left = x;
1165                 rect.top = y;
1166                 rect.right = x + client_rect.right - client_rect.left - 1;
1167                 rect.bottom = y + client_rect.bottom - client_rect.top - 1;
1168
1169                 if(!Is_standalone){
1170                         rect.top = rect.bottom;
1171                         rect.bottom = GetSystemMetrics(SM_CYSCREEN) - TASKBAR_HEIGHT - rect.top;
1172                         rect.right -= rect.left;
1173                 } else {
1174                         rect.left += 350;
1175                         rect.right = 550;
1176                         rect.bottom = 400;
1177                 }       
1178         } else {
1179                 style = WS_OVERLAPPEDWINDOW | WS_MINIMIZE;;
1180                 rect.top = rect.bottom = rect.left = rect.right = CW_USEDEFAULT;
1181         }
1182         
1183         //      Create Game Window
1184         hOutputWnd = CreateWindow(szOutputClass, "Debug Spew", style, rect.left,
1185                 rect.top, rect.right, rect.bottom, NULL, NULL, hInst, NULL);
1186
1187                 // Show it, but don't activate it.  If you activate it, it cause problems
1188         // with fullscreen startups in main window.
1189         SetWindowPos( hOutputWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_SHOWWINDOW );
1190
1191         outwnd_disabled = FALSE;
1192         SetTimer(hOutputWnd, TIMER1, 50, NULL);
1193         for (x=0; x<MAX_LINE_WIDTH; x++)
1194                 spaces[x] = ' ';
1195
1196         spaces[MAX_LINE_WIDTH] = 0;
1197         return TRUE;
1198 }
1199
1200 DWORD outwnd_thread(int display_under_freespace_window)
1201 {
1202         MSG msg;
1203
1204         if (!outwnd_create(display_under_freespace_window))
1205                 return 0;
1206
1207         while (1)       {
1208                 if (WaitMessage())      {
1209                         while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))   {
1210                                 TranslateMessage(&msg);
1211                                 DispatchMessage(&msg);
1212                         }
1213                 }
1214
1215                 if (outwnd_disabled) break;
1216         }
1217         return 0;
1218 }
1219
1220 void close_mono()
1221 {
1222 //      DeviceIoControl (mono_driver, (DWORD) IOCTL_MONO_CLEAR_SCREEN, NULL, 0, NULL, 0, &cbReturned, 0);
1223         if (hOutputThread)      {
1224                 CloseHandle(hOutputThread);
1225                 hOutputThread = NULL;
1226         }
1227         if (mono_driver)        {
1228                 CloseHandle(mono_driver);
1229                 mono_driver = NULL;
1230         }
1231 }
1232
1233 void outwnd_init(int display_under_freespace_window)
1234 {
1235 #ifndef NMONO
1236         if (!outwnd_inited)     {
1237                 outwnd_inited = TRUE;
1238                 hOutputThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)outwnd_thread, (LPVOID)display_under_freespace_window, 0, &OutputThreadID);
1239                 //SetThreadPriority(hOutputThread, THREAD_PRIORITY_TIME_CRITICAL);
1240
1241                 // set up the monochrome drivers
1242     if ( (mono_driver = CreateFile("\\\\.\\MONO", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == ((HANDLE)-1))  {
1243                  outwnd_printf2("Cannot get handle to monochrome driver.\n");
1244                  mono_init();
1245                 }
1246
1247          atexit(close_mono);
1248         }
1249 #endif
1250
1251 #ifndef NDEBUG
1252         if ( Log_fp == NULL ) {
1253                 Log_fp = fopen(Freespace_logfilename, "wb");
1254         }
1255 #endif 
1256 }
1257
1258 BOOL CALLBACK find_dlg_handler(HWND hwnd,UINT msg,WPARAM wParam, LPARAM lParam)
1259 {
1260         switch (msg)
1261         {
1262                 case WM_COMMAND:
1263                         switch (LOWORD(wParam)) {
1264                                 case IDOK:
1265                                         GetDlgItemText(hwnd, IDC_TEXT, find_text, 82);  // get the text to find
1266                                         EndDialog(hwnd, IDOK);
1267                                         return 1;
1268
1269                                 case IDCANCEL:
1270                                         EndDialog(hwnd, 0);
1271                                         return 1;
1272                         }
1273                         break;
1274
1275                 case WM_INITDIALOG:
1276                         SendDlgItemMessage(hwnd, IDC_TEXT, EM_LIMITTEXT, 80, 0);  // limit text to 80 chars
1277                         if (GetDlgCtrlID((HWND) wParam) != IDC_TEXT) {
1278                                 SetFocus(GetDlgItem(hwnd, IDC_TEXT));
1279                                 return FALSE;
1280                         }
1281
1282                         return TRUE;
1283         }
1284
1285         return 0;
1286 }
1287
1288 void find_text_in_outwindow(int n, int p)
1289 {
1290         char *ptr, *str;
1291
1292         str = outtext[n] + p;
1293         while (1)
1294         {
1295                 ptr = strstr(str, find_text);
1296                 if (ptr)
1297                 {
1298                         int scroll_pos, pos;
1299
1300                         find_pos = ptr - str;
1301                         find_line = n;
1302                         marked = 1;
1303                         marked_top = marked_bottom = find_line;
1304                         marked_left = find_pos;
1305                         marked_right = find_pos + strlen(find_text);
1306
1307                         scroll_pos = (max_scroll_pos - GetScrollPos(hOutputWnd, SB_VERT));
1308                         pos = mprintf_last_line - scroll_pos;
1309                         if (pos < 0)
1310                                 pos += SCROLL_BUFFER_SIZE;
1311
1312                         pos -= n;
1313                         if (pos < 0)
1314                                 pos += SCROLL_BUFFER_SIZE;
1315
1316                         Assert(pos >= 0);
1317                         if (pos >= nCharRows - 1)  // outside of window viewport, so scroll
1318                         {
1319                                 SCROLLINFO si;
1320
1321                                 pos = mprintf_last_line - n - nCharRows / 2;
1322                                 if (pos < 0)
1323                                         pos += SCROLL_BUFFER_SIZE;
1324
1325                                 if (pos > max_scroll_pos)
1326                                         pos = max_scroll_pos;
1327
1328                                 scroll_pos = max_scroll_pos - GetScrollPos(hOutputWnd, SB_VERT);
1329                                 si.cbSize = sizeof(SCROLLINFO);
1330                                 si.fMask = SIF_POS;
1331                                 si.nPos = max_scroll_pos - pos;
1332                                 SetScrollInfo(hOutputWnd, SB_VERT, &si, 1);
1333                                 ScrollWindow(hOutputWnd, 0, (scroll_pos - pos) * nTextHeight, NULL, NULL);
1334                         }
1335
1336                         InvalidateRect(hOutputWnd, NULL, 0);
1337                         UpdateWindow(hOutputWnd);
1338                         return;
1339                 }
1340
1341                 n--;
1342                 if (n < 0)
1343                         n += SCROLL_BUFFER_SIZE;
1344
1345                 if (n == mprintf_last_line)
1346                 {
1347                         MessageBox(hOutputWnd, "Search text not found", "Find Error", MB_OK | MB_ICONERROR);
1348                         find_line = -1;
1349                         return;
1350                 }
1351
1352                 str = outtext[n];
1353         }
1354 }
1355
1356 void outwnd_close()
1357 {
1358 #ifndef NDEBUG
1359         if ( Log_fp != NULL ) {
1360                 fclose(Log_fp);
1361                 Log_fp = NULL;
1362         }
1363 #endif
1364
1365 }
1366
1367
1368 #endif //NDEBUG
1369