~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Open Mash Cross Reference
mash/compat/console.cc

Component: ~ [ mash ] ~ [ apps ] ~ [ gsm ] ~ [ lib ] ~ [ otcl ] ~ [ srm ] ~ [ tcl8.3 ] ~ [ tclcl ] ~ [ tk8.3 ] ~ [ tutorials ] ~

  1 /*
  2  * console.cc --
  3  *
  4  *      FIXME: This file needs a description here.
  5  *
  6  * Copyright (c) 1998-2002 The Regents of the University of California.
  7  * All rights reserved.
  8  *
  9  * Redistribution and use in source and binary forms, with or without
 10  * modification, are permitted provided that the following conditions are met:
 11  *
 12  * A. Redistributions of source code must retain the above copyright notice,
 13  *    this list of conditions and the following disclaimer.
 14  * B. Redistributions in binary form must reproduce the above copyright notice,
 15  *    this list of conditions and the following disclaimer in the documentation
 16  *    and/or other materials provided with the distribution.
 17  * C. Neither the names of the copyright holders nor the names of its
 18  *    contributors may be used to endorse or promote products derived from this
 19  *    software without specific prior written permission.
 20  *
 21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
 22  * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 23  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
 25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 31  * POSSIBILITY OF SUCH DAMAGE.
 32  */
 33 
 34 #include <windows.h>
 35 #include <commctrl.h>
 36 #include <io.h>
 37 #include <fcntl.h>
 38 #include <stdio.h>
 39 #include <process.h>
 40 #include <errno.h>
 41 #include "console.h"
 42 
 43 
 44 #define M(str) MessageBox(NULL, str, NULL, MB_OK);
 45 #define MN(str, num) \
 46 do { \
 47         char buf[80]; \
 48         sprintf(buf, "%s%d", str, num); \
 49         M(buf); \
 50 } while (0)
 51 
 52 
 53 
 54 
 55 #define MC_OUTPUT WM_USER+1
 56 
 57 class ConsoleThread {
 58 public:
 59         ConsoleThread(int handle, HWND hwnd)
 60                 : outputReadHandle_(handle), consoleHwnd_(hwnd) { }
 61         ~ConsoleThread() { }
 62         void Main();
 63 
 64 private:
 65         int outputReadHandle_;
 66         HWND consoleHwnd_;
 67 };
 68 
 69 
 70 MashConsole::MashConsole(int bufferSize)
 71         : hInstance_(NULL), toplevel_(NULL), statusBar_(NULL), edit_(NULL),
 72           buffer_(NULL), len_(0), maxLen_(0)
 73 {
 74         SetBufferSize(bufferSize);
 75 }
 76 
 77 
 78 MashConsole::~MashConsole()
 79 {
 80         if (toplevel_) Destroy();
 81         if (buffer_) free(buffer_);
 82 }
 83 
 84 
 85 BOOL
 86 MashConsole::SetBufferSize(int size)
 87 {
 88         if (size < maxLen_ && len_ > size) {
 89                 // we need to clip some initial data
 90                 CopyTo(buffer_, size);
 91         }
 92 
 93         char *newBuffer = (char*)realloc(buffer_, (size+1) * sizeof(char));
 94         if (!newBuffer) return FALSE;
 95         buffer_ = newBuffer;
 96         maxLen_ = size;
 97         return TRUE;
 98 }
 99 
100 
101 void
102 MashConsole::Init(HINSTANCE hInstance)
103 {
104         WNDCLASSEX consoleClass;
105         hInstance_ = hInstance;
106 
107         consoleClass.cbSize        = sizeof(consoleClass);
108         consoleClass.style         = CS_HREDRAW | CS_VREDRAW | CS_CLASSDC;
109         consoleClass.lpfnWndProc   = MainWindowProc_Static;
110         consoleClass.cbClsExtra    = 0;
111         consoleClass.cbWndExtra    = 0;
112         consoleClass.hInstance     = hInstance_;
113         consoleClass.hIcon         = LoadIcon(hInstance_, "MashIcon");
114         consoleClass.hIconSm       = LoadIcon(hInstance_, "MashIcon");
115         consoleClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
116         consoleClass.hbrBackground = NULL;
117         consoleClass.lpszMenuName  = "MashConsoleMenu";
118         consoleClass.lpszClassName = MashConsoleWindow_Class;
119         RegisterClassEx(&consoleClass);
120 
121         InitCommonControls();
122 }
123 
124 
125 void
126 MashConsole::Create(int x, int y, int width, int height)
127 {
128         toplevel_ = CreateWindow(MashConsoleWindow_Class, "Mash Console",
129                                  WS_OVERLAPPEDWINDOW, x, y, width, height,
130                                  NULL, NULL, hInstance_, this);
131         ShowWindow(toplevel_, SW_HIDE) ;
132         UpdateWindow(toplevel_);
133 }
134 
135 
136 void
137 MashConsole::Show(BOOL show)
138 {
139         if (!toplevel_) return;
140 
141         if (show) {
142                 /* auto scroll to the end */
143                 ShowWindow(toplevel_, SW_SHOW);
144                 SendMessage(edit_, EM_SETSEL, len_, len_);
145                 SendMessage(edit_, EM_SCROLLCARET, 0, 0);
146         } else {
147                 ShowWindow(toplevel_, SW_HIDE);
148         }
149 }
150 
151 void
152 MashConsole::DoModal()
153 {
154         MSG message;
155         while (GetMessage(&message, NULL, 0, 0)) {
156                 TranslateMessage(&message);
157                 DispatchMessage(&message);
158         }
159 }
160 
161 
162 BOOL
163 MashConsole::Toggle()
164 {
165         if (IsVisible()) {
166                 Show(FALSE);
167                 return FALSE;
168         } else {
169                 Show(TRUE);
170                 return TRUE;
171         }
172 }
173 
174 
175 static void ShowError(const char *msg, BOOL isWindowsError)
176 {
177         char buffer[256];
178         if (isWindowsError) {
179                 sprintf(buffer, "%s:\n", msg);
180                 int len = sizeof(buffer) - strlen(buffer) - 1;
181                 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
182                               FORMAT_MESSAGE_IGNORE_INSERTS,
183                               NULL,
184                               GetLastError(),
185                               MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
186                               // Default language
187                               buffer, len, NULL);
188         } else {
189                 sprintf(buffer, "%s:\n%s", msg, strerror(errno));
190         }
191 
192         // Display the string.
193         MessageBox(NULL, msg, "Console Error", MB_OK | MB_ICONERROR);
194 }
195 
196 
197 BOOL
198 MashConsole::SavedFileHandle::Set(DWORD nStdHandle, int newHandle, FILE *file)
199 {
200         int fileno = _fileno(stdout);
201 
202         FILE *fp = _fdopen(newHandle, "w");
203         if (fp==NULL || setvbuf(fp, NULL, _IONBF, 0)!=0) {
204                 ShowError("Could not create FILE object for output "
205                           "handle", FALSE);
206                 if (fp) fclose(fp);
207                 else close(newHandle);
208                 return FALSE;
209         }
210 
211         if (SetStdHandle(nStdHandle,
212                          (HANDLE)_get_osfhandle(newHandle))==FALSE) {
213                 fclose(fp);
214                 ShowError("Could not set the system standard handle", TRUE);
215                 return FALSE;
216         }
217 
218         file_ = *file;
219         *file = *fp;
220         osf_handle_ = GetStdHandle(nStdHandle);
221         return TRUE;
222 }
223 
224 
225 void
226 MashConsole::SavedFileHandle::Restore(DWORD nStdHandle, FILE *file)
227 {
228         if (osf_handle_ != INVALID_HANDLE_VALUE) {
229 
230                 // for some reason, the other end of the pipe does not
231                 // seem to detect EOF unless I invoke both fclose and close!
232                 // may have something to do with the way _fd_open is
233                 // implemented on Windows.
234 
235                 int fileno = _fileno(file);
236                 fclose(file);
237                 close(fileno);
238                 SetStdHandle(nStdHandle, osf_handle_);
239                 *file = file_;
240 
241                 osf_handle_ = INVALID_HANDLE_VALUE;
242         }
243 }
244 
245 
246 BOOL
247 MashConsole::CreateOutputPipe(int &outputReadHandle)
248 {
249         enum { READ_HANDLE, WRITE_HANDLE };
250         int hOutputPipe[2], fileno;
251 
252         // Create the pipe
253         if(_pipe(hOutputPipe, 512, O_BINARY) == -1) {
254                 ShowError("Could not create output pipe", FALSE);
255                 return FALSE;
256                 // don't goto error here, coz there is nothing to cleanup!
257         }
258 
259         fileno = _dup(hOutputPipe[WRITE_HANDLE]);
260         if (fileno < 0) {
261                 ShowError("Could not duplicate output file handle for stdout",
262                           FALSE);
263                 goto error;
264         }
265         if (savedStdOut_.Set(STD_OUTPUT_HANDLE, fileno, stdout)==FALSE)
266                 goto error;
267 
268         fileno = _dup(hOutputPipe[WRITE_HANDLE]);
269         if (fileno < 0) {
270                 ShowError("Could not duplicate output file handle for stderr",
271                           FALSE);
272                 goto error;
273         }
274         if (savedStdErr_.Set(STD_ERROR_HANDLE, fileno, stderr)==FALSE)
275                 goto error;
276 
277         // Close original write end of pipe
278         close(hOutputPipe[WRITE_HANDLE]);
279 
280         outputReadHandle = hOutputPipe[READ_HANDLE];
281         return TRUE;
282 
283 error:
284         savedStdOut_.Restore(STD_OUTPUT_HANDLE, stdout);
285         savedStdErr_.Restore(STD_ERROR_HANDLE,  stderr);
286         close(hOutputPipe[WRITE_HANDLE]);
287         close(hOutputPipe[READ_HANDLE]);
288         return FALSE;
289 }
290 
291 
292 void
293 MashConsole::CloseOutputPipe()
294 {
295         savedStdOut_.Restore(STD_OUTPUT_HANDLE, stdout);
296         savedStdErr_.Restore(STD_ERROR_HANDLE,  stderr);
297 }
298 
299 
300 
301 #if 0
302 {
303         fileno = _fileno(stdout);
304         if (fileno < 0) {
305                 // we don't currently have a stdout
306                 fp = _fdopen(hOutputPipe[WRITE_HANDLE], "w");
307                 if (fp==NULL || setvbuf(fp, NULL, _IONBF, 0)!=0) {
308                         ShowError("Could not create FILE object for output "
309                                   "handle");
310                         if (fp) fclose(fp);
311                         return FALSE;
312                 }
313 
314                 savedStdOut_.Save(stdout);
315                 *stdout = *fp;
316                 SetStdHandle(_get_osfhandle(hOutputPipe[WRITE_HANDLE]));
317         }
318         else {
319                 // we do have a stdout already; do a complicated
320                 // dup procedure to map it to a new file descriptor
321 
322                 savedStdOut_.Save(fileno);
323 
324                 // duplicate the write end of the pipe to the stdout handle
325 
326         }
327 
328         outputReadHandle = hOutputPipe[READ_HANDLE];
329         return TRUE;
330 
331 #if 0
332         // Duplicate stdout handle (next line will close original)
333         savedStdOut_ = _dup(_fileno(stdout));
334 
335         MN("stdout is ", _fileno(stdout));
336         // Duplicate write end of pipe to stdout handle
337         if(_dup2(hOutputPipe[WRITE_HANDLE], _fileno(stdout)) != 0) {
338                 M("stdout dup2 failed");
339                 return FALSE;
340         }
341 
342         // Duplicate stderr handle (next line will close original)
343         savedStdErr_ = _dup(_fileno(stderr));
344 
345         // Duplicate write end of pipe to stderr handle
346         if(_dup2(hOutputPipe[WRITE_HANDLE], _fileno(stderr)) != 0)
347                 return FALSE;
348 
349         // Close original write end of pipe
350         close(hOutputPipe[WRITE_HANDLE]);
351 
352         outputReadHandle = hOutputPipe[READ_HANDLE];
353         return TRUE;
354 #endif
355 }
356 #endif
357 
358 
359 
360 #if 0
361 BOOL
362 MashConsole::CloseOutputPipe()
363 {
364 #if 0
365         // Duplicate copy of original stdout back into stdout
366         if (_dup2(savedStdOut_, _fileno(stdout)) != 0) return FALSE;
367         // Close duplicate copy of original stdout
368         close(savedStdOut_);
369 
370         // Duplicate copy of original stderr back into stderr
371         if (_dup2(savedStdErr_, _fileno(stderr)) != 0) return FALSE;
372         // Close duplicate copy of original stderr
373         close(savedStdErr_);
374 
375         savedStdOut_ = savedStdErr_ = -1;
376 #endif
377         return TRUE;
378 }
379 #endif
380 
381 
382 void
383 MashConsole::Destroy()
384 {
385         CloseOutputPipe();
386         DestroyWindow(toplevel_);
387         toplevel_ = statusBar_ = edit_ = NULL;
388 }
389 
390 
391 LRESULT CALLBACK MashConsole::MainWindowProc_Static(HWND hwnd, UINT msg,
392                                                     WPARAM wParam,
393                                                     LPARAM lParam)
394 {
395         MashConsole *This=NULL;
396         CREATESTRUCT *info;
397 
398         switch (msg) {
399         case WM_CREATE:
400                 info = (CREATESTRUCT *) lParam;
401                 This = (MashConsole*) info->lpCreateParams;
402                 SetWindowLong(hwnd, GWL_USERDATA, (LONG) This);
403                 //MessageBox(NULL, "Building window", NULL, MB_OK);
404                 This->BuildWindow(hwnd);
405                 return 0;
406 
407         default:
408                 This = (MashConsole*)GetWindowLong(hwnd, GWL_USERDATA);
409                 if (This)
410                         return This->MainWindowProc(hwnd, msg, wParam, lParam);
411                 else return DefWindowProc(hwnd, msg, wParam, lParam);
412         }
413 }
414 
415 
416 LRESULT MashConsole::MainWindowProc(HWND hwnd, UINT msg,
417                                     WPARAM wParam, LPARAM lParam)
418 {
419         BOOL  isSysMenu;
420         HMENU hmenu;
421         UINT  uPos;
422 
423         switch(msg)
424         {
425         case WM_SIZE:
426                 if (edit_ && statusBar_) {
427                         int width, height, statusHeight;
428                         RECT rect;
429                         width = LOWORD(lParam);
430                         height= HIWORD(lParam);
431                         GetWindowRect(statusBar_, &rect);
432                         statusHeight = rect.bottom - rect.top + 1;
433                         MoveWindow(edit_, 0, 0, width, height-statusHeight+1,
434                                    TRUE);
435                         MoveWindow(statusBar_, 0, height-statusHeight,
436                                    width, statusHeight, TRUE);
437                         return 0;
438                 }
439                 break;
440 
441         case MC_OUTPUT:
442                 // got more data to write
443         {
444                 char *buffer;
445                 int len;
446                 buffer = (char*)lParam;
447                 len    = (int)wParam;
448                 Output(buffer, len);
449                 delete [] buffer;
450                 break;
451         }
452 
453         case WM_INITMENUPOPUP:
454                 isSysMenu = (BOOL) HIWORD(lParam); // window menu flag
455                 hmenu = (HMENU) wParam;            // handle of submenu
456                 uPos  = (UINT) LOWORD(lParam);     // submenu item position
457                 if (!isSysMenu && uPos==1) {
458                         int select, enable;
459 
460                         select = SendMessage(edit_, EM_GETSEL, 0, 0);
461                         if (HIWORD(select)==LOWORD(select))
462                                 enable = MF_GRAYED;
463                         else
464                                 enable = MF_ENABLED;
465                         EnableMenuItem(hmenu, IDM_COPY, enable);
466                 }
467                 break;
468 
469         case WM_COMMAND:
470                 switch(LOWORD(wParam))
471                 {
472                 case IDM_HIDE:
473                         Show(FALSE);
474                         return 0;
475 
476                 case IDM_EXIT:
477                         exit(0);
478                         return 0;
479 
480                 case IDM_COPY:
481                         SendMessage(edit_, WM_COPY, 0, 0);
482                         return 0;
483 
484                 case IDM_SELECTALL:
485                         SendMessage(edit_, EM_SETSEL, 0, -1);
486                         return 0;
487 
488                 case IDM_ABOUT:
489                         DialogBox(hInstance_, "MashConsoleAboutDialog",
490                                   toplevel_, (DLGPROC) AboutDialogProc);
491                         break;
492                 }
493 
494                 break;
495 
496         case WM_CLOSE:
497                 // just hide the window; don't actually close it!
498                 Show(FALSE);
499                 return 0;
500         }
501 
502         return DefWindowProc(hwnd, msg, wParam, lParam);
503 }
504 
505 
506 BOOL CALLBACK
507 MashConsole::AboutDialogProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
508 {
509         switch (msg) {
510         case WM_COMMAND:
511                 if (LOWORD(wParam)==IDOK) {
512                         EndDialog(hdlg, 0);
513                         return TRUE;
514                 }
515                 break;
516         }
517 
518         return FALSE;
519 }
520 
521 
522 void
523 MashConsole::BuildWindow(HWND toplevel)
524 {
525         toplevel_ = toplevel;
526         edit_ = CreateWindow("edit", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER |
527                              WS_EX_CLIENTEDGE | WS_HSCROLL | WS_VSCROLL |
528                              ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL |
529                              ES_AUTOHSCROLL | ES_READONLY, 0, 0, 0, 0,toplevel,
530                              (HMENU) ID_EDIT, hInstance_, NULL);
531         statusBar_ = CreateStatusWindow(WS_CHILD | WS_VISIBLE |
532                                         WS_CLIPSIBLINGS | CCS_BOTTOM,
533                                         "Application running...",
534                                         toplevel, ID_STATUS);
535         SendMessage(edit_, WM_SETFONT,
536                     (WPARAM)GetStockObject(DEFAULT_GUI_FONT),
537                     MAKELPARAM(TRUE, 0));
538 
539         HDC hdc = GetDC(edit_);
540         SIZE size;
541         GetTextExtentPoint32(hdc, "a", 1, &size);
542         ReleaseDC(edit_, hdc);
543         size.cx *= 60;
544         size.cy *= 25;
545 
546         // add something for the statusbar, menu, title, borders
547         RECT rect;
548         GetClientRect(toplevel_, &rect);
549         size.cx += rect.left * 2;
550         size.cy += rect.top * 2;
551         GetWindowRect(toplevel_, &rect);
552         MoveWindow(toplevel_, rect.left, rect.top, size.cx, size.cy, TRUE);
553 
554         int outputReadHandle;
555         if (CreateOutputPipe(outputReadHandle)==FALSE) {
556                 MessageBox(NULL, "error in creating output pipe", NULL, MB_OK);
557                 return;
558         }
559 
560         ConsoleThread *thread = new ConsoleThread(outputReadHandle, toplevel_);
561         _beginthread(ThreadMain, 0, (void*)thread);
562 }
563 
564 
565 void
566 MashConsole::Output(const char *bytes, int len)
567 {
568         // first count any "\n" characters that are not preceded by "\r"
569         const char *ptr;
570         char *dst;
571         int extra=0;
572         for (ptr=bytes; ptr < (bytes+len); ptr++) {
573                 if (*ptr=='\n') {
574                         if ( ! (ptr > bytes && *(ptr-1)=='\r') ) extra++;
575                 }
576         }
577 
578         // make sure we have enough space in the buffer
579         if ((len+extra) > maxLen_) {
580                 int needToCut = len - maxLen_;
581                 bytes += needToCut;
582                 len   -= needToCut;
583         }
584 
585         if (len_ + len + extra > maxLen_) {
586                 CreateSpace(len_ + len + extra);
587         }
588 
589         // now copy the bytes over
590         for (ptr=bytes, dst=buffer_+len_; ptr < (bytes+len); ptr++) {
591                 if (*ptr=='\n') {
592                         if ( ! (ptr > bytes && *(ptr-1)=='\r') ) *dst++ = '\r';
593                 }
594                 *dst++ = *ptr;
595         }
596 
597         *dst = '\0';
598         len_ = (dst-buffer_);
599 
600         if (edit_) {
601                 SetWindowText(edit_, buffer_);
602                 if (IsVisible()) {
603                         /* auto scroll to the end */
604                         SendMessage(edit_, EM_SETSEL, len_, len_);
605                         SendMessage(edit_, EM_SCROLLCARET, 0, 0);
606                 }
607         }
608 }
609 
610 
611 void
612 MashConsole::CreateSpace(int need)
613 {
614         int size = maxLen_ - (need - maxLen_);
615 
616         if (size < len_) {
617                 // we have too much data; we should throw away some...
618                 // shrink the buffer to size
619                 CopyTo(buffer_, size);
620                 len_ = size;
621         }
622 }
623 
624 
625 void
626 MashConsole::CopyTo(char *newBuffer, int &size)
627 {
628         int curSize = len_;
629         char *curBuf= buffer_, *newline;
630 
631         while (curSize > size) {
632                 newline = strchr(curBuf, '\n');
633                 if (newline==NULL) {
634                         curSize = 0;
635                         curBuf  = buffer_ + maxLen_;
636                         break;
637                 } else {
638                         curSize -= (newline+1 - curBuf);
639                         curBuf   = newline+1;
640                 }
641         }
642 
643         if (curSize <= 0) {
644                 size = 0;
645                 *newBuffer = '\0';
646         } else {
647                 // remember to copy the trailing '\0'
648                 memmove(newBuffer, curBuf, curSize+1);
649                 size = curSize;
650         }
651 }
652 
653 
654 void
655 MashConsole::ThreadMain(void *args)
656 {
657         ConsoleThread *thread = (ConsoleThread*)args;
658         thread->Main();
659         delete thread;
660 
661         // implicit _endThread
662 }
663 
664 
665 void
666 ConsoleThread::Main()
667 {
668         char *buffer;
669         const int  bytesAtATime=80;
670         int bytesRead;
671 
672         while (1) {
673                 buffer = new char[bytesAtATime+1];
674                 bytesRead = _read(outputReadHandle_, buffer, bytesAtATime);
675                 if (bytesRead <= 0) {
676                         if (bytesRead < 0) {
677                                 MessageBox(NULL, strerror(errno),
678                                            "Console error",
679                                            MB_ICONERROR | MB_OK);
680                         }
681                         close(outputReadHandle_);
682                         break;
683                 }
684 
685                 PostMessage(consoleHwnd_, MC_OUTPUT, (WPARAM)bytesRead,
686                             (LPARAM)buffer);
687         }
688 }
689 
690 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.