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