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
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.