1 /* con_nt.cpp
2 *
3 * Copyright (c) 1994-1996, Marko Macek
4 *
5 * You may distribute under the terms of either the GNU General Public
6 * License or the Artistic License, as specified in the README file.
7 *
8 */
9
10 /* some functionality is now shared with g_text.cpp and g_draw.cpp */
11 /* new os/2 code needs to be merged with this */
12 /* the console APIs on win'95 seem to be really screwed up */
13
14 /*
15 * 10/13/96 Jal:
16 * Rebuilt for Windows NT, generic; no port/Watcom code should
17 * be needed to compile (jal).
18 * Removed most mouse handling code (unnecessary), added pipe handler by
19 * porting the OS/2 version to NT.
20 * Solved some bugs with regard to the TCell problem.
21 *
22 * 10/28/96 Jal:
23 * Started to replace threaded pipe code with nonthreaded code, using
24 * overlapped I/O.
25 *
26 * Wed Jan 15 1997 (Jal):
27 * - The Grey-Alt-+ key and some other keys were not recognised. This
28 * was because NT didn't mark these as "enhanced" keys. Now the code
29 * translates some (scancode,ascii) pairs to the enhanced keyboard.
30 * The table was already present!
31 * - To solve the "flashing cursor" problem: now doesn't enter FTE
32 * mainloop when console returns empty action..
33 *
34 */
35 #define WIN32_LEAN_AND_MEAN
36 #include <windows.h>
37 #include <process.h>
38 #include <signal.h>
39
40 #include <wincon.h>
41
42 #include <ctype.h>
43 #include <stdio.h>
44 #include "sysdep.h"
45 #include "console.h"
46 #include "gui.h"
47 #include "s_files.h"
48 #include "s_string.h"
49 #include "c_config.h"
50
51 #define True 1
52 #define False 0
53
54
55 static int Initialized = 0;
56 static int MousePresent = 0;
57 static int CursorVisible = 1; /* 1 means visible */
58 static int MouseVisible = 0; /* 0 means hidden */
59 static TEvent MouseEv = { evNone };
60 static TEvent EventBuf = { evNone };
61 //static TEventMask EventMask;
62
63 static HANDLE ConOut;
64 static HANDLE ConIn;
65 static HANDLE OurConOut;
66 static DWORD OldConsoleMode;
67
68 static int LastMouseX = 0;
69 static int LastMouseY = 0;
70 //static int isWin95 = 0;
71
72 static char winTitle[256] = "FTE";
73 static char winSTitle[256] = "FTE";
74
75 int codepage;
76
77 #define dbm(x) //printf(x), Sleep(3000)
78
79
80 #if 0
81 void dbg(const char* s, ...) /*FOLD00*/
82 {
83 }
84 #else
85
dbg(const char * s,...)86 void dbg(const char* s, ...) /*FOLD00*/
87 {
88 char buf[256];
89 va_list args;
90
91 va_start(args, s);
92 vsprintf(buf, s, args);
93 va_end(args);
94 OutputDebugString(buf);
95 }
96 #endif
97
98
DrawCursor(int Show)99 static void DrawCursor(int Show) { /*FOLD00*/
100 CONSOLE_CURSOR_INFO cci;
101
102 GetConsoleCursorInfo(OurConOut, &cci);
103 cci.bVisible = Show ? TRUE : FALSE;
104 SetConsoleCursorInfo(OurConOut, &cci);
105 }
106
107 #define NUMITEMS(x) (sizeof(x) / sizeof(x[0]))
108
109 #if 1
110 /*
111 * Translation table 1: translate (scan,asciicode) of the input event to a
112 * valid FTE keystroke. This is used because NT sometimes "forgets" to flag
113 * special keys as "enhanced" (grey) keys..
114 */
115 static struct {
116 USHORT CharScan;
117 TKeyCode KeyCode;
118 } TransCharScan[] = {
119 { 0x0100, kbEsc }, { 0x011B, kbEsc },
120 { 0x1C0D, kbEnter }, { 0x1C0A, kbEnter },
121 { 0x1C00, kbEnter }, { 0xE00D, kbEnter | kfGray },
122 { 0xA600, kbEnter | kfGray }, { 0xE00A, kbEnter | kfGray },
123 { 0x0E08, kbBackSp }, { 0x0E7F, kbBackSp },
124 { 0x0E00, kbBackSp }, { 0x0F09, kbTab },
125 { 0x9400, kbTab }, { 0xA500, kbTab },
126 { 0x0F00, kbTab }, { 0x4E00, '+' | kfGray },
127 { 0x9000, '+' | kfGray }, { 0x4E2B, '+' | kfGray },
128 { 0x4A00, '-' | kfGray }, { 0x8E00, '-' | kfGray },
129 { 0x4A2D, '-' | kfGray }, { 0x3700, '*' | kfGray },
130 { 0x9600, '*' | kfGray }, { 0x372A, '*' | kfGray },
131 { 0xE02F, '/' | kfGray }, { 0xA400, '/' | kfGray },
132 { 0x9500, '/' | kfGray }, { 0x0300, 0 }
133 };
134 #endif
135
136 #if 0
137 static struct {
138 int ScanCode;
139 TKeyCode KeyCode;
140 } TransScan[] = {
141 { 0x78, '1' }, { 0x79, '2' }, { 0x7A, '3' }, { 0x7B, '4' }, { 0x7C, '5' },
142 { 0x7D, '6' }, { 0x7E, '7' }, { 0x7F, '8' }, { 0x80, '9' }, { 0x81, '0' },
143
144 { 0x10, 'Q' }, { 0x11, 'W' }, { 0x12, 'E' }, { 0x13, 'R' }, { 0x14, 'T' },
145 { 0x15, 'Y' }, { 0x16, 'U' }, { 0x17, 'I' }, { 0x18, 'O' }, { 0x19, 'P' },
146
147 { 0x1E, 'A' }, { 0x1F, 'S' }, { 0x20, 'D' }, { 0x21, 'F' }, { 0x22, 'G' },
148 { 0x23, 'H' }, { 0x24, 'J' }, { 0x25, 'K' }, { 0x26, 'L' },
149
150 { 0x2C, 'Z' }, { 0x2D, 'X' }, { 0x2E, 'C' }, { 0x2F, 'V' }, { 0x30, 'B' },
151 { 0x31, 'N' }, { 0x32, 'M' },
152
153 { 0x29, '`' }, { 0x82, '-' }, { 0x83, '=' }, { 0x2B, '\\' }, { 0x1A, '[' },
154 { 0x1B, ']' }, { 0x27, ';' }, { 0x28, '\'' }, { 0x33, ',' }, { 0x34, '.' },
155 { 0x35, '/' }, { 0x37, '*' }, { 0x4E, '+' }, { 0x4A, '-' },
156
157 { 0x3B, kbF1 }, { 0x3C, kbF2 }, { 0x3D, kbF3 },
158 { 0x3E, kbF4 }, { 0x3F, kbF5 }, { 0x40, kbF6 },
159 { 0x41, kbF7 }, { 0x42, kbF8 }, { 0x43, kbF9 },
160 { 0x44, kbF10 }, { 0x85, kbF11 }, { 0x86, kbF12 },
161
162 { 0x54, kbF1 }, { 0x55, kbF2 }, { 0x56, kbF3 },
163 { 0x57, kbF4 }, { 0x58, kbF5 }, { 0x59, kbF6 },
164 { 0x5A, kbF7 }, { 0x5B, kbF8 }, { 0x5C, kbF9 },
165 { 0x5D, kbF10 }, { 0x87, kbF11 }, { 0x88, kbF12 },
166
167 { 0x5E, kbF1 }, { 0x5F, kbF2 }, { 0x60, kbF3 },
168 { 0x61, kbF4 }, { 0x62, kbF5 }, { 0x63, kbF6 },
169 { 0x64, kbF7 }, { 0x65, kbF8 }, { 0x66, kbF9 },
170 { 0x67, kbF10 }, { 0x89, kbF11 }, { 0x8A, kbF12 },
171
172 { 0x68, kbF1 }, { 0x69, kbF2 }, { 0x6A, kbF3 },
173 { 0x6B, kbF4 }, { 0x6C, kbF5 }, { 0x6D, kbF6 },
174 { 0x6E, kbF7 }, { 0x6F, kbF8 }, { 0x70, kbF9 },
175 { 0x71, kbF10 }, { 0x8B, kbF11 }, { 0x8C, kbF12 },
176
177 { 0x47, kbHome }, { 0x48, kbUp }, { 0x49, kbPgUp },
178 { 0x4B, kbLeft }, { 0x4C, kbCenter}, { 0x4D, kbRight },
179 { 0x4F, kbEnd }, { 0x50, kbDown }, { 0x51, kbPgDn },
180 { 0x52, kbIns }, { 0x53, kbDel },
181
182 { 0x77, kbHome }, { 0x8D, kbUp }, { 0x84, kbPgUp },
183 { 0x73, kbLeft }, { 0x74, kbRight },
184 { 0x75, kbEnd }, { 0x91, kbDown }, { 0x76, kbPgDn },
185 { 0x92, kbIns }, { 0x93, kbDel },
186
187 { 0x97, kbHome | kfGray }, { 0x98, kbUp | kfGray }, { 0x99, kbPgUp | kfGray },
188 { 0x9B, kbLeft | kfGray }, { 0x9D, kbRight | kfGray },
189 { 0x9F, kbEnd | kfGray }, { 0xA0, kbDown | kfGray }, { 0xA1, kbPgDn | kfGray },
190 { 0xA2, kbIns | kfGray }, { 0xA3, kbDel | kfGray }
191 };
192 #endif
193
194
195 struct {
196 SHORT VirtCode;
197 unsigned long KeyCode;
198 } VirtTab[] =
199 {
200 { 112, kbF1 },
201 { 113, kbF2 },
202 { 114, kbF3 },
203 { 115, kbF4 },
204 { 116, kbF5 },
205 { 117, kbF6 },
206 { 118, kbF7 },
207 { 119, kbF8 },
208 { 120, kbF9 },
209 { 121, kbF10 },
210 { 122, kbF11 },
211 { 123, kbF12 },
212
213 { 35, kbEnd },
214 { 36, kbHome },
215 { 33, kbPgUp },
216 { 34, kbPgDn },
217 { 38, kbUp },
218 { 37, kbLeft },
219 { 39, kbRight },
220 { 40, kbDown },
221 { 45, kbIns },
222 { 46, kbDel },
223
224 { 27, kbEsc },
225 { 13, kbEnter },
226 { 8, kbBackSp },
227 { 32, kbSpace },
228 { 9, kbTab },
229
230 { 0, 0 }
231 };
232
233 static const char shftwrng[] = "~!@#$%^&*()_+{}|:\"<>?";
234 static const char shftright[] = "`1234567890-=[]\\;',./";
235
ReadConsoleEvent(TEvent * E)236 int ReadConsoleEvent(TEvent *E) /*FOLD00*/
237 {
238 /*
239 * Reads and interprets the console event. It is called when console input
240 * handle is signalled. To prevent flashing cursors this routine returns
241 * F if there's nothing to do; this causes the caller to loop without
242 * returning to the FTE mainloop.
243 */
244 INPUT_RECORD inp;
245 DWORD nread;
246 TKeyCode Ch = 0;
247 TKeyCode flg = 0;
248 ULONG flags;
249 int I, i;
250
251 ReadConsoleInput(ConIn, &inp, 1, &nread);
252 if (nread != 1) return False; // Nothing read after signal??
253
254 switch (inp.EventType)
255 {
256 case WINDOW_BUFFER_SIZE_EVENT:
257 //** Resized the window. Make FTE use the new size..
258 frames->Resize(inp.Event.WindowBufferSizeEvent.dwSize.X, inp.Event.WindowBufferSizeEvent.dwSize.Y);
259 frames->Repaint();
260 return True;
261
262 case KEY_EVENT:
263 if (inp.Event.KeyEvent.bKeyDown) {
264 if ((inp.Event.KeyEvent.dwControlKeyState & CAPSLOCK_ON) &&
265 inp.Event.KeyEvent.wVirtualKeyCode != 106 &&
266 inp.Event.KeyEvent.wVirtualKeyCode != 109 &&
267 inp.Event.KeyEvent.wVirtualKeyCode != 107)
268 {
269 if (!(inp.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)) {
270 for (i=0; shftwrng[i]; i++) {
271 if (inp.Event.KeyEvent.uChar.AsciiChar == shftwrng[i]) {
272 inp.Event.KeyEvent.uChar.AsciiChar = shftright[i];
273 break;
274 }
275 }
276 }
277 else {
278 for (i=0; shftright[i]; i++) {
279 if (inp.Event.KeyEvent.uChar.AsciiChar == shftright[i]) {
280 inp.Event.KeyEvent.uChar.AsciiChar = shftwrng[i];
281 break;
282 }
283 }
284 }
285 }
286 }
287 //** Skip shift, control and alt key stuff.
288 switch(inp.Event.KeyEvent.wVirtualKeyCode)
289 { case VK_SHIFT: case VK_CONTROL: case VK_MENU: case VK_PAUSE:
290 case VK_CAPITAL: case VK_LWIN: case VK_RWIN: case VK_APPS:
291 return False;
292 }
293
294 //** Distill FTE flags from the NT flags. This fails for some keys
295 //** because NT has an oddity with enhanced keys (Alt-Grey-+ etc).
296
297 // from chaac: Please do not toutch RIGHT_ALT_PRESSED handling,
298 // in some keyboard ALT-GR is used for special characters.
299 flags = inp.Event.KeyEvent.dwControlKeyState;
300 if (flags & (/*RIGHT_ALT_PRESSED |*/ LEFT_ALT_PRESSED)) flg |= kfAlt;
301 if (flags & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) flg |= kfCtrl;
302 if (flags & (RIGHT_ALT_PRESSED)) flg &= ~kfCtrl;
303 if (flags & SHIFT_PRESSED) flg |= kfShift;
304 if (flags & ENHANCED_KEY) flg |= kfGray;
305
306 #if 0
307 dbg("key1: %s, vk=%x, vscan=%x, flags=%x, rep=%d, ascii=%x (%c).\n",
308 inp.Event.KeyEvent.bKeyDown ? "down" : "up",
309 inp.Event.KeyEvent.wVirtualKeyCode,
310 inp.Event.KeyEvent.wVirtualScanCode,
311 flags,
312 inp.Event.KeyEvent.wRepeatCount,
313 inp.Event.KeyEvent.uChar.AsciiChar,
314 inp.Event.KeyEvent.uChar.AsciiChar);
315 #endif
316 Ch = 0;
317
318 // handle special case when user with scandinavian keyboard presses
319 // alt-gr + special key and then spacebar
320 if (inp.Event.KeyEvent.bKeyDown) {
321 if ((inp.Event.KeyEvent.wVirtualKeyCode == 0x20) &&
322 (inp.Event.KeyEvent.wVirtualScanCode == 0x39))
323 {
324 switch(inp.Event.KeyEvent.uChar.AsciiChar)
325 {
326 case '~': Ch = '~'; break;
327 case '^': Ch = '^'; break;
328 case '`': Ch = '`'; break;
329 case '�': Ch = '�'; break;
330 }
331 }
332 }
333
334 //** Translate VK codes to FTE codes,
335 if (Ch == 0)
336 {
337 for (I = 0; I < sizeof(VirtTab)/sizeof(VirtTab[0]); I++)
338 if (VirtTab[I].VirtCode == inp.Event.KeyEvent.wVirtualKeyCode)
339 {
340 Ch = VirtTab[I].KeyCode;
341 break;
342 }
343 }
344
345 //** Not a virtual key-> do charscan translation, if needed;
346 if(Ch == 0)
347 {
348 unsigned int cc = ((inp.Event.KeyEvent.wVirtualScanCode << 8) | (unsigned char)inp.Event.KeyEvent.uChar.AsciiChar);
349 for(I = 0; I < NUMITEMS(TransCharScan); I++)
350 {
351 if(cc == TransCharScan[I].CharScan)
352 {
353 Ch = TransCharScan[I].KeyCode;
354 break;
355 }
356 }
357 }
358 if (Ch == 0)
359 {
360 if ((Ch = (TKeyCode) (unsigned char)inp.Event.KeyEvent.uChar.AsciiChar) != 0)
361 {
362 if (flg & kfAlt) Ch = toupper(Ch);
363 }
364 }
365
366 if (Ch == 0) //** Odd: cannot distill keycode.
367 return False;
368
369 if (flg & kfCtrl)
370 if (Ch < 32)
371 Ch += 64;
372
373 E->Key.Code = Ch | flg; // Set FTE keycode,
374 E->What = inp.Event.KeyEvent.bKeyDown ? evKeyDown : evKeyUp;
375 return True;
376
377 case MOUSE_EVENT:
378 LastMouseX = E->Mouse.X = inp.Event.MouseEvent.dwMousePosition.X;
379 LastMouseY = E->Mouse.Y = inp.Event.MouseEvent.dwMousePosition.Y;
380 flags = inp.Event.MouseEvent.dwControlKeyState;
381 if (flags & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) flg |= kfAlt;
382 if (flags & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) flg |= kfCtrl;
383 if (flags & SHIFT_PRESSED) flg |= kfShift;
384 E->Mouse.KeyMask = flg;
385 E->Mouse.Buttons = (unsigned short)inp.Event.MouseEvent.dwButtonState;
386 E->Mouse.Count = 1;
387 if (inp.Event.MouseEvent.dwEventFlags & DOUBLE_CLICK)
388 E->Mouse.Count = 2;
389
390 if ((inp.Event.MouseEvent.dwEventFlags & MOUSE_WHEELED) == MOUSE_WHEELED)
391 {
392 E->What = evCommand;
393 E->Msg.View = 0;
394 E->Msg.Model = 0;
395 E->Msg.Param2 = 0;
396
397 // Scroll:
398 // (The SDK does not tell how to determine whether the wheel
399 // was scrolled up or down. Found an example on:
400 // http://www.adrianxw.dk/SoftwareSite/Consoles/Consoles5.html
401 if (inp.Event.MouseEvent.dwButtonState & 0xFF000000) { // Wheel down
402 if (flg & kfShift) { // Translate to horizontal scroll.
403 E->Msg.Command = (flg & kfCtrl) ? cmHScrollPgRt : cmHScrollRight;
404 } else { // Translate to vertical scroll.
405 E->Msg.Command = (flg & kfCtrl) ? cmVScrollPgDn : cmVScrollDown;
406 }
407 } else { // Wheel up
408 if (flg & kfShift) { // Translate to horizontal scroll.
409 E->Msg.Command = (flg & kfCtrl) ? cmHScrollPgLt : cmHScrollLeft;
410 } else { // Translate to vertical scroll.
411 E->Msg.Command = (flg & kfCtrl) ? cmVScrollPgUp : cmVScrollUp;
412 }
413 }
414
415 E->Msg.Param1 = (flg & kfCtrl) ? 1 : 3; // 1 page / 3 lines
416
417 return True;
418 }
419
420 if (inp.Event.MouseEvent.dwEventFlags == MOUSE_MOVED)
421 {
422 E->What = evMouseMove;
423 //puts("Move");
424 }
425 else
426 {
427 static unsigned short mb = 0;
428
429 if (inp.Event.MouseEvent.dwButtonState & ~mb)
430 {
431 E->What = evMouseDown;
432 E->Mouse.Buttons = ((unsigned short)inp.Event.MouseEvent.dwButtonState) & ~mb;
433 //puts("Down");
434 }
435 else
436 {
437 E->What = evMouseUp;
438 E->Mouse.Buttons = mb & ~((unsigned short)inp.Event.MouseEvent.dwButtonState);
439 //puts("Up");
440 }
441 mb = (unsigned short)inp.Event.MouseEvent.dwButtonState;
442 }
443 return True;
444 }
445 return False;
446 }
447
448
ConInit(int,int)449 int ConInit(int /*XSize*/, int /*YSize*/) { /*FOLD00*/
450 if (Initialized) return 1;
451
452 EventBuf.What = evNone;
453 MousePresent = 0; //MOUSInit();
454
455 //** Get NT/Win95 flag,
456 OSVERSIONINFO oi;
457
458 oi.dwOSVersionInfoSize = sizeof(oi);
459 GetVersionEx((LPOSVERSIONINFO) &oi);
460 //isWin95 = (oi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
461
462 ConOut = GetStdHandle(STD_OUTPUT_HANDLE);
463 ConIn = GetStdHandle(STD_INPUT_HANDLE);
464 codepage = GetConsoleCP();
465 GetConsoleMode(ConIn, &OldConsoleMode);
466 SetConsoleMode(ConIn,
467 ENABLE_WINDOW_INPUT |
468 ENABLE_MOUSE_INPUT);
469 OurConOut = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
470 0, NULL,
471 CONSOLE_TEXTMODE_BUFFER, NULL);
472 ConContinue();
473
474 Initialized = 1;
475
476 return 1;
477 }
478
ConDone()479 int ConDone() { /*FOLD00*/
480 ConSuspend();
481 CloseHandle(OurConOut);
482
483 return 1;
484 }
485
ConSuspend()486 int ConSuspend() { /*FOLD00*/
487 SetConsoleActiveScreenBuffer(ConOut);
488 SetConsoleMode(ConIn, OldConsoleMode);
489
490 return 1;
491 }
492
ConContinue()493 int ConContinue() { /*FOLD00*/
494 SetConsoleActiveScreenBuffer(OurConOut);
495 GetConsoleMode(ConIn, &OldConsoleMode);
496 SetConsoleMode(ConIn, ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
497
498 SetConsoleOutputCP(codepage);
499 SetConsoleCP(codepage);
500
501 return 1;
502 }
503
ConClear()504 int ConClear() { /*FOLD00*/
505 int W, H;
506 TDrawBuffer B;
507
508 MoveChar(B, 0, ConMaxCols, ' ', 0x07, 1);
509 if (!ConQuerySize(&W, &H)
510 || !ConSetBox(0, 0, W, H, B[0]))
511 return 0;
512
513 return 1;
514 }
515
516
517 #if 0 // Mouse control not necessary when using console functions.
518 /*--------------------------------------------------------------------------*/
519 /* CLASS: tMouHelp is used to control mouse cursor visibility during */
520 /* screen updates. */
521 /*--------------------------------------------------------------------------*/
522 class tMouHelp
523 {
524 protected:
525 int mh_x, mh_y; // Current mouse position / 0
526 int mh_valid;
527 int mh_disabled; // T if mouse should be re-enabled.
528
529 public:
530 tMouHelp() : mh_x(0), mh_y(0), mh_valid(FALSE), mh_disabled(FALSE) {}
531 ~tMouHelp()
532 { if(MouseVisible && mh_disabled) DrawMouse(1);
533 }
534
535 void disIfLine(int x, int w, int y)
536 {
537 if(mh_disabled) return;
538 if(! mh_valid)
539 {
540 ConQueryMousePos(&mh_x, &mh_y);
541 mh_valid = TRUE;
542 }
543 if(y == mh_y && mh_x >= x && mh_x < x+y)
544 {
545 mh_disabled= TRUE;
546 DrawMouse(0);
547 }
548 }
549 };
550 #endif
551
ConPutBox(int X,int Y,int W,int H,PCell Cell)552 int ConPutBox(int X, int Y, int W, int H, PCell Cell) /*FOLD00*/
553 {
554 int I;
555 PCell p = Cell;
556 COORD corg, csize;
557 SMALL_RECT rcl;
558 BOOL rc;
559
560 for (I = 0; I < H; I++)
561 {
562 corg.X = corg.Y = 0;
563 csize.X = W;
564 csize.Y = 1;
565 rcl.Left= X;
566 rcl.Top = I + Y;
567 rcl.Bottom = I + Y;// + (isWin95 ? 1 : 0);
568 rcl.Right = X + W - 1;// + (isWin95 ? 1 : 0);
569
570 rc = WriteConsoleOutput(OurConOut, (PCHAR_INFO)p, csize, corg, &rcl);
571 if (rc != TRUE) {
572 //("WriteConsoleOutput %d\n", rc);
573 }
574 p += W;
575 }
576 return 1;
577 }
578
ConGetBox(int X,int Y,int W,int H,PCell Cell)579 int ConGetBox(int X, int Y, int W, int H, PCell Cell) /*FOLD00*/
580 {
581 int I;
582 USHORT WW = W << 1;
583 PCell p = Cell;
584 COORD corg, csize;
585 SMALL_RECT rcl;
586
587 for (I = 0; I < H; I++)
588 {
589 corg.X = corg.Y = 0;
590 csize.X = W;
591 csize.Y = 1;
592 rcl.Left = X;
593 rcl.Top = I + Y;
594 rcl.Bottom = I + Y;// + (isWin95 ? 1 : 0);
595 rcl.Right = X + W - 1;// + (isWin95 ? 1 : 0);
596
597 ReadConsoleOutput(OurConOut, (PCHAR_INFO)p, csize, corg, &rcl);
598 p += W;
599 }
600 return 1;
601 }
602
ConPutLine(int X,int Y,int W,int H,PCell Cell)603 int ConPutLine(int X, int Y, int W, int H, PCell Cell) /*FOLD00*/
604 {
605 int I;
606 COORD corg, csize;
607 SMALL_RECT rcl;
608 BOOL rc;
609
610 for (I = 0; I < H; I++)
611 {
612 corg.X = corg.Y = 0;
613 csize.X = W;
614 csize.Y = 1;
615 rcl.Left = X;
616 rcl.Top = I + Y;
617 rcl.Bottom = I + Y;// + (isWin95 ? 1 : 0);
618 rcl.Right = X + W - 1;// + (isWin95 ? 1 : 0);
619
620 rc = WriteConsoleOutput(OurConOut, (PCHAR_INFO)Cell, csize, corg, &rcl);
621 if (rc != TRUE) {
622 //printf("WriteConsoleOutput %d\n", rc);
623 }
624 }
625
626 return 1;
627 }
628
ConSetBox(int X,int Y,int W,int H,TCell Cell)629 int ConSetBox(int X, int Y, int W, int H, TCell Cell) /*FOLD00*/
630 {
631 int I;
632 COORD corg, csize;
633 SMALL_RECT rcl;
634 TDrawBuffer B;
635
636 I = W;
637 while (I-- > 0) B[I] = Cell;
638
639 for (I = 0; I < H; I++)
640 {
641 corg.X = corg.Y = 0;
642 csize.X = W;
643 csize.Y = 1;
644 rcl.Left = X;
645 rcl.Top = I + Y;
646 rcl.Bottom = I + Y;// - (isWin95 ? 1 : 0);
647 rcl.Right = X + W - 1;// - (isWin95 ? 1 : 0);
648
649 WriteConsoleOutput(OurConOut, (PCHAR_INFO)B, csize, corg, &rcl);
650 }
651
652 return 1;
653 }
654
ConScroll(int Way,int X,int Y,int W,int H,TAttr Fill,int Count)655 int ConScroll(int Way, int X, int Y, int W, int H, TAttr Fill, int Count) /*FOLD00*/
656 {
657 TCell FillCell;
658 SMALL_RECT rect, clip;
659 COORD dest;
660
661 MoveCh(&FillCell, ' ', Fill, 1);
662
663 clip.Left = X;
664 clip.Top = Y;
665 clip.Right = X + W - 1;
666 clip.Bottom = Y + H - 1;
667
668 rect = clip;
669 dest.X = X;
670 dest.Y = Y;
671
672 switch (Way) {
673 case csUp:
674 rect.Top += Count;
675 break;
676 case csDown:
677 rect.Bottom -= Count;
678 dest.Y += Count;
679 break;
680 case csLeft:
681 rect.Left += Count;
682 break;
683 case csRight:
684 rect.Right += Count;
685 dest.X += Count;
686 break;
687 }
688
689 ScrollConsoleScreenBuffer(OurConOut, &rect, &clip, dest, (PCHAR_INFO)&FillCell);
690
691 return 1;
692 }
693
ConSetSize(int X,int Y)694 int ConSetSize(int X, int Y) { /*FOLD00*/
695 return 0;
696 }
697
ConQuerySize(int * X,int * Y)698 int ConQuerySize(int *X, int *Y) { /*FOLD00*/
699 CONSOLE_SCREEN_BUFFER_INFO csbi;
700
701 GetConsoleScreenBufferInfo(OurConOut, &csbi);
702 *X = csbi.dwSize.X;
703 *Y = csbi.dwSize.Y;
704
705 dbg("Console size (%u,%u)\n", *X, *Y);
706
707 return 1;
708 }
709
ConSetCursorPos(int X,int Y)710 int ConSetCursorPos(int X, int Y) { /*FOLD00*/
711 COORD xy;
712
713 xy.X = X;
714 xy.Y = Y;
715 SetConsoleCursorPosition(OurConOut, xy);
716
717 return 1;
718 }
719
ConQueryCursorPos(int * X,int * Y)720 int ConQueryCursorPos(int *X, int *Y) { /*FOLD00*/
721 CONSOLE_SCREEN_BUFFER_INFO csbi;
722
723 GetConsoleScreenBufferInfo(OurConOut, &csbi);
724 *X = csbi.dwCursorPosition.X;
725 *Y = csbi.dwCursorPosition.Y;
726
727 return 1;
728 }
729
ConShowCursor()730 int ConShowCursor() { /*FOLD00*/
731 CursorVisible = 1;
732 DrawCursor(1);
733
734 return 1;
735 }
736
ConHideCursor()737 int ConHideCursor() { /*FOLD00*/
738 CursorVisible = 0;
739 DrawCursor(0);
740
741 return 1;
742 }
743
ConCursorVisible()744 int ConCursorVisible() { /*FOLD00*/
745 return (CursorVisible == 1);
746 }
747
ConSetCursorSize(int Start,int End)748 int ConSetCursorSize(int Start, int End) { /*FOLD00*/
749 return 0;
750 }
751
ConSetMousePos(int X,int Y)752 int ConSetMousePos(int X, int Y) { /*FOLD00*/
753 return 0;
754 }
755
ConQueryMousePos(int * X,int * Y)756 int ConQueryMousePos(int *X, int *Y) { /*FOLD00*/
757 *X = LastMouseX;
758 *Y = LastMouseY;
759
760 // NT does not have this ? (not needed anyway, but check mouse hiding above).
761 return 1;
762 }
763
ConShowMouse()764 int ConShowMouse() { /*FOLD00*/
765 MouseVisible = 1;
766
767 return (MousePresent != 0);
768 }
769
ConHideMouse()770 int ConHideMouse() { /*FOLD00*/
771 MouseVisible = 0;
772
773 return (MousePresent != 0);
774 }
775
ConMouseVisible()776 int ConMouseVisible() { /*FOLD00*/
777 return (MouseVisible == 1);
778 }
779
ConQueryMouseButtons(int * ButtonCount)780 int ConQueryMouseButtons(int *ButtonCount) { /*FOLD00*/
781 return 1;
782 }
783
ConPutEvent(const TEvent & Event)784 int ConPutEvent(const TEvent& Event) { /*FOLD00*/
785 EventBuf = Event;
786
787 return 1;
788 }
789
ConFlush()790 int ConFlush() { /*FOLD00*/
791 return 1;
792 }
793
ConGrabEvents(TEventMask EventMask)794 int ConGrabEvents(TEventMask EventMask) { /*FOLD00*/
795 return 1;
796 }
797
798
799 static PCell SavedScreen = 0;
800 static int SavedX, SavedY, SaveCursorPosX, SaveCursorPosY;
801
SaveScreen()802 int SaveScreen() { /*FOLD00*/
803 if (SavedScreen)
804 free(SavedScreen);
805
806 ConQuerySize(&SavedX, &SavedY);
807
808 SavedScreen = (PCell) malloc(SavedX * SavedY * sizeof(TCell));
809
810 if (SavedScreen)
811 ConGetBox(0, 0, SavedX, SavedY, SavedScreen);
812
813 ConQueryCursorPos(&SaveCursorPosX, &SaveCursorPosY);
814
815 return 1;
816 }
817
RestoreScreen()818 int RestoreScreen() { /*FOLD00*/
819 if (SavedScreen) {
820 ConPutBox(0, 0, SavedX, SavedY, SavedScreen);
821 ConSetCursorPos(SaveCursorPosX, SaveCursorPosY);
822 }
823 return 1;
824 }
825
826
GUI(int & argc,char ** argv,int XSize,int YSize)827 GUI::GUI(int &argc, char **argv, int XSize, int YSize) { /*FOLD00*/
828 fArgc = argc;
829 fArgv = argv;
830 ::ConInit(-1, -1);
831 SaveScreen();
832 ::ConSetSize(XSize, YSize);
833 gui = this;
834 }
835
~GUI()836 GUI::~GUI() { /*FOLD00*/
837 RestoreScreen();
838
839 if (SavedScreen)
840 free(SavedScreen);
841
842 ::ConDone();
843 gui = 0;
844 }
845
ConSuspend()846 int GUI::ConSuspend() { /*FOLD00*/
847 RestoreScreen();
848 return ::ConSuspend();
849 }
850
ConContinue()851 int GUI::ConContinue() { /*FOLD00*/
852 SaveScreen();
853 return ::ConContinue();
854 }
855
ShowEntryScreen()856 int GUI::ShowEntryScreen() { /*FOLD00*/
857 TEvent E;
858
859 ConHideMouse();
860 RestoreScreen();
861 SetConsoleActiveScreenBuffer(ConOut);
862 do { gui->ConGetEvent(evKeyDown, &E, -1, 1, 0); } while (E.What != evKeyDown);
863 SetConsoleActiveScreenBuffer(OurConOut);
864 ConShowMouse();
865 if (frames)
866 frames->Repaint();
867 return 1;
868 }
869
ConGetDrawChar(unsigned int index)870 char ConGetDrawChar(unsigned int index) { /*FOLD00*/
871 static const char *tab=NULL;
872
873 if (!tab) {
874 tab=GetGUICharacters ("WindowsNT","ڿ��ij�ô��\x1A����\x1B\x1A");
875 }
876 assert(index < strlen(tab));
877
878 return tab[index];
879 }
880
881
RunProgram(int mode,char * Command)882 int GUI::RunProgram(int mode, char *Command) { /*FOLD00*/
883 int rc, W, H, W1, H1;
884
885 ConQuerySize(&W, &H);
886 ConHideMouse();
887 ConSuspend();
888
889 if (*Command == 0) // empty string = shell
890 Command = getenv(
891 "COMSPEC"
892 );
893
894 // we don't want ctrl-c or ctrl-break to exit our editor...
895 signal(SIGBREAK, SIG_IGN);
896 signal(SIGINT, SIG_IGN);
897
898 rc = system(Command);
899
900 // restore handlers back to default handlers
901 signal(SIGBREAK, SIG_DFL);
902 signal(SIGINT, SIG_DFL);
903
904 ConContinue();
905 ConShowMouse();
906 ConQuerySize(&W1, &H1);
907
908 if (W != W1 || H != H1) {
909 frames->Resize(W1, H1);
910 }
911 frames->Repaint();
912 return rc;
913 }
914
ConSetTitle(const char * Title,const char * STitle)915 int ConSetTitle(const char *Title, const char *STitle) { /*FOLD00*/
916 char buf[sizeof(winTitle)] = {0};
917 JustFileName(Title, buf, sizeof(buf));
918 if (buf[0] == '\0') // if there is no filename, try the directory name.
919 JustLastDirectory(Title, buf, sizeof(buf));
920
921 strlcpy(winTitle, "FTE - ", sizeof(winTitle));
922 if (buf[0] != 0) // if there is a file/dir name, stick it in here.
923 {
924 strlcat(winTitle, buf, sizeof(winTitle));
925 strlcat(winTitle, " - ", sizeof(winTitle));
926 }
927 strlcat(winTitle, Title, sizeof(winTitle));
928 strlcpy(winSTitle, STitle, sizeof(winSTitle));
929 SetConsoleTitle (winTitle);
930
931 return 1;
932 }
933
ConGetTitle(char * Title,size_t MaxLen,char * STitle,size_t SMaxLen)934 int ConGetTitle(char *Title, size_t MaxLen, char *STitle, size_t SMaxLen) { /*FOLD00*/
935 strlcpy(Title, winTitle, MaxLen);
936 strlcpy(STitle, winSTitle, SMaxLen);
937 return 1;
938 }
939
940
941 #if 0
942 /****************************************************************************/
943 /* */
944 /* CODING: Pipe handler. */
945 /* */
946 /****************************************************************************/
947 /*--------------------------------------------------------------------------*/
948 /* STATIC GLOBALS. */
949 /*--------------------------------------------------------------------------*/
950 #define MAX_PIPES 4
951 #define PIPE_BUFLEN 4096
952 #define PIPEBUF_SZ 4096
953
954 class NTHandle
955 {
956 protected:
957 HANDLE nth_h;
958
959 public:
960 operator HANDLE()
961 { return nth_h;
962 }
963
964 void close()
965 { if(nth_h != INVALID_HANDLE_VALUE)
966 {
967 CloseHandle(nth_h);
968 nth_h = INVALID_HANDLE_VALUE;
969 }
970 }
971
972
973 NTHandle() { nth_h = INVALID_HANDLE_VALUE; }
974
975 ~NTHandle()
976 { close();
977 }
978
979 NTHandle(const HANDLE& h) : nth_h(h) {}
980 NTHandle(const NTHandle& nth); // UNDEFINED (no assgn)
981 NTHandle& operator =(const NTHandle& nth); // UNDEFINED (no assgn)
982 NTHandle& operator =(const HANDLE nth)
983 { close();
984 nth_h = nth;
985 return *this;
986 }
987 };
988
989
990 class GPipe
991 {
992 public:
993 int p_used;
994 int p_id;
995 char* p_buffer;
996 int p_buflen;
997 int p_bufused;
998 int p_bufpos;
999 EModel* p_notify;
1000 char* p_command;
1001 int p_retcode;
1002 int p_doterm;
1003
1004 //** NT specific.
1005 HANDLE p_proc_h; // Handle of spawned process,
1006 HANDLE pipeDataRead;
1007 HANDLE pipeStartRead;
1008 HANDLE pipeMutex;
1009 HANDLE p_pipe_ph; // Input pipe (read by FTE)
1010 HANDLE p_child_ph; // Client side's handle (written to by spawned)
1011 DWORD p_read_len; // #bytes read in overlapped I/O
1012 int p_io_posted; // T when overlapped I/O is posted,
1013 int p_completed; // T when client process closed down.
1014 int p_has_data; // T when OVERLAPPED completed.
1015
1016 static GPipe pipe_ar[MAX_PIPES];
1017
1018
1019 public:
1020 int open(char *Command, EModel *notify);
1021 int close();
1022 int read(void *buffer, int len);
1023 int getEvent(TEvent* event);
1024
1025
1026 protected:
1027 int createPipe();
1028 void releasePipe();
1029 int runCommand();
1030 void closeProc();
1031 int handlePost();
1032 int postRead();
1033
1034
1035
1036 public:
1037 static GPipe* getFreePipe();
1038 static GPipe* getPipe(int id);
1039
1040 };
1041
1042 GPipe GPipe::pipe_ar[MAX_PIPES];
1043
1044
1045 /*
1046 * getFreePipe() locates an unused GPipe structure. It also assigns it's ID.
1047 */
1048 GPipe* GPipe::getFreePipe() /*FOLD00*/
1049 {
1050 int i;
1051
1052 for(i = 0; i < MAX_PIPES; i++)
1053 {
1054 if(! pipe_ar[i].p_used)
1055 {
1056 pipe_ar[i].p_id = i; // Set pipenr,
1057 return pipe_ar + i;
1058 }
1059 }
1060 return NULL; // No free pipe
1061 }
1062
1063
1064 GPipe* GPipe::getPipe(int id) /*FOLD00*/
1065 {
1066 if (id < 0 || id > MAX_PIPES) return NULL;
1067 if(! pipe_ar[id].p_used) return NULL;
1068 return pipe_ar + id;
1069 }
1070
1071
1072 int GPipe::createPipe() /*FOLD00*/
1073 {
1074 /*
1075 * Called from open() to create and open the server and the client pipes.
1076 */
1077 static int PCount = 0;
1078 //HANDLE hchild;
1079 char pipename[50];
1080 int ok;
1081 SECURITY_ATTRIBUTES sa;
1082
1083 sa.nLength = sizeof(sa); // Security descriptor for INHERIT.
1084 sa.lpSecurityDescriptor = 0;
1085 sa.bInheritHandle = 1;
1086
1087 #if 1
1088 if (CreatePipe(&p_pipe_ph, &p_child_ph, &sa, 0) == FALSE)
1089 return FALSE;
1090
1091 Pipes[i].tid = _beginthread(PipeThread,
1092 FAKE_BEGINTHREAD_NULL
1093 16384, &Pipes[i]);
1094 #else
1095
1096 //** Create the named pipe, and handle the SERVER (edit)'s end...
1097 sprintf(pipename, "\\\\.\\pipe\\fte%d\\child%d", getpid(), PCount);
1098 p_pipe_ph = CreateNamedPipe(pipename,
1099 PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
1100 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
1101 1, // nMaxInstances,
1102 0, PIPEBUF_SZ,
1103 1000,
1104 0);
1105 if(p_pipe_ph == INVALID_HANDLE_VALUE)
1106 return FALSE;
1107 PCount++;
1108
1109 /*
1110 * Client side: get a connection to the server's pipe. Do this before the
1111 * call to ConnectNamedPipe() to prevent it from blocking.
1112 */
1113
1114 #if 1
1115 p_child_ph = CreateFile(pipename, GENERIC_WRITE, 0, &sa,
1116 OPEN_EXISTING, 0, 0);
1117 #else
1118 p_child_ph = CreateFile("_test", GENERIC_WRITE|GENERIC_READ, 0, &sa,
1119 CREATE_ALWAYS, 0, 0);
1120 #endif
1121 if(p_child_ph == INVALID_HANDLE_VALUE)
1122 dbm("CreateFile(client_side_pipe) has failed");
1123 else
1124 {
1125 //** Server side: aquire connection..
1126 ok = TRUE;
1127 if(! ConnectNamedPipe(p_pipe_ph, 0)) // Get connect;
1128 {
1129 if(GetLastError() != ERROR_PIPE_CONNECTED)
1130 ok = FALSE;
1131 }
1132
1133 //** Connect worked?
1134 if(!ok)
1135 dbm("ConnectNmPipe() has failed");
1136 else
1137 return TRUE; // All opened & ready for action!
1138
1139 //** Something went wrong.
1140 CloseHandle(p_child_ph); // Close child: was inh.
1141 DisconnectNamedPipe(p_pipe_ph); // Force disconnection of client (-)
1142 CloseHandle(p_child_ph);
1143 }
1144 CloseHandle(p_pipe_ph);
1145 #endif
1146 return FALSE; // Something has failed.
1147 }
1148
1149
1150 void GPipe::releasePipe() /*FOLD00*/
1151 {
1152 /*
1153 * releasePipe() releases all that createPipe() allocates. It's usually
1154 * called when an error causes the process to abort.
1155 */
1156 if(p_child_ph != INVALID_HANDLE_VALUE)
1157 {
1158 CloseHandle(p_child_ph);
1159 p_child_ph = INVALID_HANDLE_VALUE;
1160 }
1161
1162 if(p_pipe_ph != 0)
1163 {
1164 //DisconnectNamedPipe(p_pipe_ph);
1165 CloseHandle(p_pipe_ph);
1166 p_pipe_ph = INVALID_HANDLE_VALUE;
1167 }
1168 }
1169
1170
1171 int GPipe::runCommand() /*FOLD00*/
1172 {
1173 /*
1174 * runCommand() takes the child pipe, dups it onto stdout and stderr while
1175 * saving their old assignments, then it spawns
1176 */
1177 int ok;
1178 char* comspec, *args, tbuf[256];
1179 HANDLE errh;
1180 PROCESS_INFORMATION pi;
1181 STARTUPINFO si;
1182 const char nt4[] = "4nt.exe";
1183
1184 ok = FALSE;
1185 comspec = getenv("COMSPEC");
1186
1187 /*
1188 * BUG workaround: When using 4NT, it doesn't properly reassign stderr!
1189 * This is a bug in 4NT, so if comspec *is* 4nt use cmd.exe instead...
1190 */
1191 if(comspec == 0) return -1;
1192 int l = strlen(comspec);
1193 if(strnicmp(comspec + (l- sizeof(nt4)+1), nt4, sizeof(nt4)-1) == 0)
1194 {
1195 //** It's 4DOS all right..
1196 args = getenv("SystemRoot");
1197 if(args== 0) return -1;
1198 strlcpy(tbuf, args, sizeof(tbuf)); // Get to c:\winnt
1199 strlcat(tbuf, "\\system32\\cmd.exe", sizeof(tbuf));
1200 comspec = tbuf;
1201 }
1202
1203 int argslen = strlen(comspec) + strlen(p_command) + 120;
1204 args = (char *)malloc(argslen);
1205 if(args == 0)
1206 dbm("malloc() failed for command line..");
1207 else
1208 {
1209 //** Form a command line for the process;
1210 strlcpy(args, comspec, argslen);
1211 strlcat(args, " /c ", argslen);
1212 strlcat(args, p_command, argslen);
1213
1214 //** Dup the child handle to get separate handles for stdout and err,
1215 if (DuplicateHandle(GetCurrentProcess(), p_child_ph, // Source,
1216 GetCurrentProcess(), &errh, // Destination,
1217 0, True, // Same access, inheritable
1218 DUPLICATE_SAME_ACCESS));
1219
1220 {
1221 /* Set up members of STARTUPINFO structure. */
1222 memset(&si, 0, sizeof(si));
1223 si.cb = sizeof(STARTUPINFO);
1224 si.lpReserved = NULL;
1225 si.lpReserved2 = NULL;
1226 si.cbReserved2 = 0;
1227 si.lpDesktop = NULL;
1228 /* si.dwFlags = STARTF_USESTDHANDLES;
1229 #if 1
1230 si.hStdOutput = p_child_ph;
1231 si.hStdError = errh;
1232 si.hStdInput = INVALID_HANDLE_VALUE;
1233 #else
1234 si.hStdOutput = errh;
1235 si.hStdError = p_child_ph;
1236 si.hStdInput = INVALID_HANDLE_VALUE;
1237 #endif*/
1238 if(CreateProcess(NULL, args, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
1239 {
1240 ok = TRUE;
1241 CloseHandle(pi.hThread); // Thread handle not needed
1242 p_proc_h = pi.hProcess; // Return process handle (to get RC)
1243 }
1244 CloseHandle(errh); // Close error handle,
1245 }
1246 else
1247 dbm("DupHandle for stderr failed.");
1248
1249 free(args); // Release command line.
1250 }
1251
1252 // SetConsoleMode(horgout, ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
1253
1254 //** And last but not least: close the child handle.
1255 CloseHandle(p_child_ph);
1256 p_child_ph = INVALID_HANDLE_VALUE;
1257 return ok;
1258 }
1259
1260
1261 void GPipe::closeProc() /*FOLD00*/
1262 {
1263 /*
1264 * closeProc() gets called when a read fails. It assumes the process has
1265 * ended, retrieves the process return code, then it closes all handles.
1266 * The state is set to p_completed.
1267 */
1268 DWORD ec;
1269
1270 dbg("[closeProc] ");
1271
1272 if(! GetExitCodeProcess(p_proc_h, &ec)) ec = 0xabcd;
1273 p_retcode = ec; // Save return code of process,
1274 if(p_proc_h != INVALID_HANDLE_VALUE) // Close process,
1275 {
1276 CloseHandle(p_proc_h);
1277 p_proc_h = INVALID_HANDLE_VALUE;
1278 }
1279
1280 //** Close the main pipe,
1281 if(p_pipe_ph != INVALID_HANDLE_VALUE)
1282 {
1283 CloseHandle(p_pipe_ph);
1284 p_pipe_ph = INVALID_HANDLE_VALUE;
1285 }
1286 p_completed = TRUE;
1287 p_has_data = TRUE;
1288 }
1289
1290
1291 int GPipe::startRead() /*FOLD00*/
1292 {
1293 /*
1294 * postRead() checks if an overlapped READ needs to be posted by checking
1295 * the io_posted flag. If that's clear and no termination or closed flag
1296 * is set then a new overlapped I/O request is issued.
1297 */
1298 p_has_data = FALSE;
1299 dbg("[postRead ");
1300 if(p_io_posted || p_completed)
1301 dbg("no action: %s] ", p_io_posted ? "posted" : "complete");
1302 else
1303 {
1304 p_ovl.hEvent = p_data_evh; // Signal this when done,
1305 if(!ReadFile(p_pipe_ph, p_buffer, p_buflen, &p_read_len, NULL))
1306 {
1307 DWORD ec = GetLastError();
1308 if(ec != ERROR_IO_PENDING)
1309 {
1310 //** Something's wrong. Treat as closed pipe for now.
1311 closeProc(); // Close pipe, complete stuff...
1312 dbg("postfail] ");
1313 return FALSE; // And return failure.
1314 }
1315 }
1316 p_io_posted = TRUE; // Handle pending ioresult.
1317 dbg("posted] ");
1318 }
1319 return TRUE;
1320 }
1321
1322
1323 int GPipe::open(char* command, EModel* notify) /*FOLD00*/
1324 {
1325 memset(&p_ovl, 0, sizeof(p_ovl)); // Clear overlapped,
1326 p_bufused = 0;
1327 p_bufpos = 0;
1328 p_io_posted = FALSE;
1329 p_has_data = FALSE;
1330 p_completed = FALSE; // T if client closed.
1331 p_doterm = FALSE;
1332 p_buflen = PIPE_BUFLEN;
1333 p_notify = notify;
1334 p_doterm = FALSE;
1335
1336 p_pipe_ph = INVALID_HANDLE_VALUE;
1337 p_child_ph = INVALID_HANDLE_VALUE;
1338 if( (p_command = strdup(command)) == 0)
1339 return -1;
1340
1341 //** Allocate the read buffer;
1342 if ((p_buffer = (char*) malloc(p_buflen)) != 0) {
1343 if ((pipeDataRead = CreateEvent(0, 1, 0, 0)) == 0) {
1344 dbm("CreateEvent(data_evh) failed.");
1345 goto fail;
1346 }
1347 if ((pipeStartRead = CreateEvent(0, 1, 0, 0)) == 0) {
1348 dbm("CreateEvent(data_evh) failed.");
1349 goto fail;
1350 }
1351 if ((pipeMutex = CreateMutex(NULL, FALSE, NULL)) == NULL) {
1352 dbm("Failed pipe mutex");
1353 goto fail;
1354 }
1355
1356
1357 else
1358 {
1359 if(createPipe()) // Create server & client pipe.
1360 {
1361 if(! postRead())
1362 dbm("postRead() initial failed.");
1363 else
1364 {
1365 if(runCommand())
1366 {
1367 p_used = TRUE;
1368 return p_id;
1369 }
1370 }
1371 releasePipe(); // Release pipes,
1372 }
1373 CloseHandle(p_data_evh);
1374 }
1375 free(p_buffer);
1376 }
1377 free(p_command);
1378 return -1;
1379 }
1380
1381
1382 int GPipe::close() /*FOLD00*/
1383 {
1384 /*
1385 * close() disconnects from the spawned task, closes the pipe and releases
1386 * all stuff.
1387 */
1388 if(! p_used) return -1;
1389 if(! p_completed) // Overlapped I/O not complete yet?
1390 {
1391 //** We *must* wait till the overlapped I/O completes,
1392 if(p_io_posted)
1393 {
1394 GetOverlappedResult(p_pipe_ph, &p_ovl, &p_read_len, TRUE);
1395 p_io_posted = FALSE;
1396 }
1397 }
1398 p_completed= TRUE;
1399
1400 //** Now close all that might be pending,
1401 free(p_buffer);
1402 free(p_command);
1403
1404 releasePipe(); // Close all pipe stuff,
1405 if(p_proc_h != INVALID_HANDLE_VALUE)
1406 {
1407 CloseHandle(p_proc_h);
1408 p_proc_h = INVALID_HANDLE_VALUE;
1409 }
1410
1411 CloseHandle(pipeStartRead);
1412 CloseHandle(pipeDataRead);
1413 CloseHandle(pipeMutex);
1414
1415 p_used = FALSE;
1416 return p_retcode;
1417 }
1418
1419
1420 int GPipe::read(void *buffer, int len) /*FOLD00*/
1421 {
1422 /*
1423 * read() is called to get the current data from the pipe. It takes the
1424 * #bytes read and returns them. It returns data till the buffer is
1425 * exhausted. If the process is completed it returns -1; else it returns
1426 * the #bytes read. It returns 0 if the buffer's empty.
1427 */
1428 dbg("[read ");
1429 if(p_has_data)
1430 {
1431 if(p_bufpos < p_read_len) // Data in buffer?
1432 {
1433 unsigned l;
1434
1435 l = p_read_len - p_bufpos; // Try to output all,
1436 if(l > len) l = len;
1437 memcpy(buffer, p_buffer+p_bufpos, l); // Copy data from the buffer,
1438 p_bufpos += l;
1439 dbg("%u data] ", l);
1440 return l; // Data returned,
1441 }
1442
1443 //** There's nothing left in the buffer. Is the task complete?
1444 if(p_completed)
1445 {
1446 dbg("no data, complete] ");
1447 return -1;
1448 }
1449
1450 if(! postRead())
1451 {
1452 dbg("post failed-> complete] ");
1453 return -1;
1454 }
1455
1456 dbg("nodata, post] ");
1457 return 0;
1458 }
1459 else if(p_completed)
1460 {
1461 dbg("completed] ");
1462 return -1;
1463 }
1464
1465 dbg("nothing] ");
1466 return 0;
1467 }
1468
1469
1470 int GPipe::getEvent(TEvent* event) /*FOLD00*/
1471 {
1472 dbg("[getpipeevent: ");
1473 event->What = evNone;
1474
1475 if(! p_used || p_notify == 0) return 0; // No data.
1476 if(! handlePost()) return 0; // Again: no data,
1477 //** This pipe has data!
1478 event->What = evNotify;
1479 event->Msg.View = 0;
1480 event->Msg.Model = p_notify;
1481 event->Msg.Command = cmPipeRead;
1482 event->Msg.Param1 = p_id;
1483 dbg("ok] ");
1484 return 1;
1485 }
1486
1487
1488 /*
1489 * NT Pipe handler - overview
1490 * ==========================
1491 * The NT pipe handler uses overlapped I/O to read console events.
1492 *
1493 * OpenPipe():
1494 * When the pipe is opened, one of the pipe structures is allocated and set
1495 * to used. Then an event semaphore (reset_manual) is created. This semaphore
1496 * will be signalled when data is available on the input pipe which gathers
1497 * the spawned tasks's output.
1498 *
1499 * Then a pipe is created, opened for the client side and stdout and stderr
1500 * are redirected therein. After that the client task is spawned.
1501 *
1502 * If the spawn succeeds an overlapped READ is posted for the pipe; then the
1503 * OpenPipe function returns.
1504 *
1505 * ConGetEvent():
1506 * The ConGetEvent() handler does a WaitForMultipleObjects() on the console
1507 * handle and all pipe handles currently active. If a pipe has data the
1508 * overlapped result is gotten, and the output is sent to the message window.
1509 * Then, if the thread didn't finish, a new overlapped read is posted.
1510 *
1511 *
1512 */
1513 int GUI::OpenPipe(const char *Command, EModel *notify) /*FOLD00*/
1514 {
1515 GPipe* gp;
1516
1517 if( (gp = GPipe::getFreePipe()) == 0)
1518 return -1; // Out of pipes.
1519 return gp->open(Command, notify); // And ask the pipe to init.
1520 }
1521
1522
1523 int GUI::SetPipeView(int id, EModel *notify) /*FOLD00*/
1524 {
1525 GPipe* p;
1526
1527 if( (p = GPipe::getPipe(id)) == 0) return -1;
1528 p->lock();
1529 p->p_notify = notify;
1530 p->unlock();
1531 return 0;
1532 }
1533
1534
1535 ssize_t GUI::ReadPipe(int id, void *buffer, size_t len) /*FOLD00*/
1536 {
1537 //int l;
1538 GPipe* p;
1539
1540 if( (p = GPipe::getPipe(id)) == 0) return -1;
1541 return p->read(buffer, len);
1542 }
1543
1544
1545 int GUI::ClosePipe(int id) /*FOLD00*/
1546 {
1547 GPipe* p;
1548
1549 if( (p = GPipe::getPipe(id)) == 0) return -1;
1550 return p->close();
1551 }
1552
1553
1554 static int GetPipeEvent(int id, TEvent *Event) /*FOLD00*/
1555 {
1556 //int i;
1557 GPipe* p;
1558
1559 if( (p = GPipe::getPipe(id)) == 0) return -1;
1560 return p->getEvent(Event);
1561 }
1562
1563 #else
1564
1565 #define MAX_PIPES 4
1566 #define PIPE_BUFLEN 4096
1567
1568 typedef struct {
1569 int used;
1570 int id;
1571 int reading, stopped;
1572 HANDLE Thread;
1573 HANDLE Access;
1574 HANDLE ResumeRead;
1575 HANDLE NewData;
1576 char *buffer;
1577 int buflen;
1578 int bufused;
1579 int bufpos;
1580 EModel *notify;
1581 char *Command;
1582 DWORD RetCode;
1583 int DoTerm;
1584 } GPipe;
1585
1586 static GPipe Pipes[MAX_PIPES] = {
1587 { 0 }, { 0 }, { 0 }, { 0 }
1588 };
1589
CreatePipeChild(HANDLE & child,HANDLE & hPipe,char * Command)1590 static int CreatePipeChild(HANDLE &child, HANDLE &hPipe, char *Command) {
1591 //static int PCount = 0;
1592 int arglen = 0;
1593 HANDLE hChildPipe;
1594 BOOL rc;
1595
1596 SECURITY_ATTRIBUTES sa;
1597
1598 sa.nLength = sizeof(sa); // Security descriptor for INHERIT.
1599 sa.lpSecurityDescriptor = 0;
1600 sa.bInheritHandle = 1;
1601
1602 rc = CreatePipe(&hPipe, &hChildPipe, &sa, 0);
1603 if (rc != TRUE)
1604 return -1;
1605
1606 int ok;
1607 char* comspec, *args, tbuf[256];
1608 //HANDLE errh;
1609 PROCESS_INFORMATION pi;
1610 STARTUPINFO si;
1611 HANDLE hNul;
1612 const char nt4[] = "4nt.exe";
1613
1614 ok = FALSE;
1615 comspec = getenv("COMSPEC");
1616
1617 /*
1618 * BUG workaround: When using 4NT, it doesn't properly reassign stderr!
1619 * This is a bug in 4NT, so if comspec *is* 4nt use cmd.exe instead...
1620 */
1621 if (comspec == 0)
1622 return -1;
1623 int l = strlen(comspec);
1624 if (strnicmp(comspec + (l- sizeof(nt4)+1), nt4, sizeof(nt4)-1) == 0)
1625 {
1626 //** It's 4DOS all right..
1627 args = getenv("SystemRoot");
1628 if(args== 0) return -1;
1629 strlcpy(tbuf, args, sizeof(tbuf)); // Get to c:\winnt
1630 strlcat(tbuf, "\\system32\\cmd.exe", sizeof(tbuf));
1631 comspec = tbuf;
1632 }
1633
1634 int argslen = strlen(comspec) + strlen(Command) + 120;
1635 args = (char *)malloc(argslen);
1636 if(args == 0)
1637 dbm("malloc() failed for command line..");
1638 else
1639 {
1640 //** Form a command line for the process;
1641 strlcpy(args, comspec, argslen);
1642 strlcat(args, " /c ", argslen);
1643 strlcat(args, Command, argslen);
1644
1645 //** Dup the child handle to get separate handles for stdout and err,
1646 /*if (DuplicateHandle(GetCurrentProcess(), hChildPipe,
1647 GetCurrentProcess(), &errh,
1648 0, True, DUPLICATE_SAME_ACCESS))*/
1649 //fprintf(stderr, "open NUL\n");
1650
1651 hNul = CreateFile("NUL",
1652 GENERIC_READ | GENERIC_WRITE,
1653 FILE_SHARE_READ | FILE_SHARE_WRITE,
1654 NULL, OPEN_EXISTING,
1655 0,
1656 NULL);
1657 //fprintf(stderr, "starting %s\n", args);
1658
1659 if (hNul != NULL) {
1660 /* Set up members of STARTUPINFO structure. */
1661 memset(&si, 0, sizeof(si));
1662 si.cb = sizeof(STARTUPINFO);
1663 si.lpReserved = NULL;
1664 si.lpReserved2 = NULL;
1665 si.cbReserved2 = 0;
1666 si.lpDesktop = NULL;
1667 si.dwFlags = STARTF_USESTDHANDLES;
1668 #if 1
1669 si.hStdOutput = hChildPipe;
1670 si.hStdError = hChildPipe;
1671 si.hStdInput = hNul;//INVALID_HANDLE_VALUE;
1672 #else
1673 si.hStdOutput = errh;
1674 si.hStdError = hChildPipe;
1675 si.hStdInput = INVALID_HANDLE_VALUE;
1676 #endif
1677 if (CreateProcess(NULL, args, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) == TRUE)
1678 {
1679 ok = TRUE;
1680 CloseHandle(pi.hThread); // Thread handle not needed
1681 //fprintf(stderr, "create process success\n");
1682 child = pi.hProcess; // Return process handle (to get RC)
1683 } else
1684 //fprintf(stderr, "create process failed %d\n" + GetLastError());
1685 CloseHandle(hNul); // Close error handle,
1686 }
1687 else
1688 dbm("DupHandle for stderr failed.");
1689
1690 free(args);
1691 }
1692
1693 CloseHandle(hChildPipe);
1694 return 0;
1695 }
1696
PipeThread(void * p)1697 static DWORD __stdcall PipeThread(void *p) {
1698 GPipe *pipe = (GPipe *)p;
1699 BOOL rc;
1700 DWORD used;
1701 HANDLE child;
1702 HANDLE hfPipe;
1703
1704 rc = CreatePipeChild(child, hfPipe, pipe->Command);
1705
1706 if (rc != 0) {
1707 //fprintf(stderr, "Failed CreatePipeChild\n");
1708 WaitForSingleObject(pipe->Access, INFINITE);
1709 pipe->reading = 0;
1710 SetEvent(pipe->NewData);
1711 ReleaseMutex(pipe->Access);
1712 return 0;
1713 }
1714
1715 //fprintf(stderr, "Pipe: Begin: %d %s\n", pipe->id, pipe->Command);
1716 while (1) {
1717 //fprintf(stderr, "Waiting on pipe\n");
1718 rc = ReadFile(hfPipe, pipe->buffer, pipe->buflen, &used, NULL);
1719 if (rc != TRUE) {
1720 //fprintf(stderr, "ReadFile failed %d %ld", GetLastError(), used);
1721 used = 0;
1722 }
1723
1724 WaitForSingleObject(pipe->Access, INFINITE);
1725 //fprintf(stderr, "Waiting on mutex\n");
1726 pipe->bufused = used;
1727 //fprintf(stderr, "Pipe: fread: %d %d\n", pipe->id, pipe->bufused);
1728 ResetEvent(pipe->ResumeRead);
1729 if (pipe->bufused == 0)
1730 break;
1731 if (pipe->notify) {
1732 SetEvent(pipe->NewData);
1733 pipe->stopped = 0;
1734 }
1735 ReleaseMutex(pipe->Access);
1736 if (pipe->DoTerm)
1737 break;
1738 //fprintf(stderr, "Waiting on sem\n");
1739 WaitForSingleObject(pipe->ResumeRead, INFINITE);
1740 //fprintf(stderr, "Read: Released mutex\n");
1741 if (pipe->DoTerm)
1742 break;
1743 }
1744 CloseHandle(hfPipe);
1745 //fprintf(stderr, "Pipe: pClose: %d\n", pipe->id);
1746 rc = WaitForSingleObject(child, INFINITE);
1747 GetExitCodeProcess(child, &pipe->RetCode);
1748 CloseHandle(child);
1749 pipe->reading = 0;
1750 SetEvent(pipe->NewData);
1751 ReleaseMutex(pipe->Access);
1752 //fprintf(stderr, "Read: Released mutex\n");
1753 return 1;
1754 }
1755
OpenPipe(const char * Command,EModel * notify)1756 int GUI::OpenPipe(const char *Command, EModel *notify) {
1757 int i;
1758
1759 for (i = 0; i < MAX_PIPES; i++) {
1760 if (Pipes[i].used == 0) {
1761 Pipes[i].reading = 1;
1762 Pipes[i].stopped = 1;
1763 Pipes[i].id = i;
1764 Pipes[i].bufused = 0;
1765 Pipes[i].bufpos = 0;
1766 Pipes[i].buflen = PIPE_BUFLEN;
1767 Pipes[i].Command = strdup(Command);
1768 Pipes[i].notify = notify;
1769 Pipes[i].DoTerm = 0;
1770 if ((Pipes[i].buffer = (char *)malloc(PIPE_BUFLEN)) == 0)
1771 return -1;
1772
1773 if ((Pipes[i].Access = CreateMutex(NULL, FALSE, NULL)) == NULL) {
1774 free(Pipes[i].Command);
1775 free(Pipes[i].buffer);
1776 return -1;
1777 }
1778
1779 if ((Pipes[i].ResumeRead = CreateEvent(0, 1, 0, 0)) == NULL) {
1780 free(Pipes[i].Command);
1781 free(Pipes[i].buffer);
1782 CloseHandle(Pipes[i].Access);
1783 return -1;
1784 }
1785
1786 if ((Pipes[i].NewData = CreateEvent(0, 1, 0, 0)) == NULL) {
1787 free(Pipes[i].Command);
1788 free(Pipes[i].buffer);
1789 CloseHandle(Pipes[i].ResumeRead);
1790 CloseHandle(Pipes[i].Access);
1791 return -1;
1792 }
1793
1794 DWORD tid;
1795
1796 if ((Pipes[i].Thread = CreateThread(NULL, 32768,
1797 &PipeThread, &Pipes[i],
1798 0, &tid)) == NULL)
1799 {
1800 free(Pipes[i].Command);
1801 free(Pipes[i].buffer);
1802 CloseHandle(Pipes[i].ResumeRead);
1803 CloseHandle(Pipes[i].Access);
1804 CloseHandle(Pipes[i].NewData);
1805 return -1;
1806 }
1807 Pipes[i].used = 1;
1808 //fprintf(stderr, "Pipe Open: %d\n", i);
1809 return i;
1810 }
1811 }
1812 return -1;
1813 }
1814
SetPipeView(int id,EModel * notify)1815 int GUI::SetPipeView(int id, EModel *notify) {
1816 if (id < 0 || id > MAX_PIPES)
1817 return 0;
1818 if (Pipes[id].used == 0)
1819 return 0;
1820 WaitForSingleObject(Pipes[id].Access, INFINITE);
1821 //fprintf(stderr, "Pipe View: %d %08X\n", id, notify);
1822 Pipes[id].notify = notify;
1823 ReleaseMutex(Pipes[id].Access);
1824 return 1;
1825 }
1826
ReadPipe(int id,void * buffer,size_t len)1827 ssize_t GUI::ReadPipe(int id, void *buffer, size_t len) {
1828 ssize_t l;
1829 //ULONG ulPostCount;
1830
1831 if (id < 0 || id > MAX_PIPES || Pipes[id].used == 0)
1832 return -1;
1833
1834 //fprintf(stderr, "Read: Waiting on mutex\n");
1835 //ConContinue();
1836 WaitForSingleObject(Pipes[id].Access, INFINITE);
1837 //fprintf(stderr, "Pipe Read: Get %d %d\n", id, len);
1838 if (Pipes[id].bufused - Pipes[id].bufpos > 0) {
1839 l = len;
1840 if (l > Pipes[id].bufused - Pipes[id].bufpos) {
1841 l = Pipes[id].bufused - Pipes[id].bufpos;
1842 }
1843 memcpy(buffer,
1844 Pipes[id].buffer + Pipes[id].bufpos,
1845 l);
1846 Pipes[id].bufpos += l;
1847 if (Pipes[id].bufpos == Pipes[id].bufused) {
1848 Pipes[id].bufused = 0;
1849 Pipes[id].bufpos = 0;
1850 //fprintf(stderr, "Pipe Resume Read: %d\n", id);
1851 Pipes[id].stopped = 1;
1852 //fprintf(stderr, "Read: posting sem\n");
1853 SetEvent(Pipes[id].ResumeRead);
1854 }
1855 } else if (Pipes[id].reading == 0)
1856 l = -1;
1857 else {
1858 l = 0;
1859 // DosBeep(200, 200);
1860 }
1861 //fprintf(stderr, "Pipe Read: Got %d %d\n", id, l);
1862 ReleaseMutex(Pipes[id].Access);
1863 //fprintf(stderr, "Read: Released mutex\n");
1864 return l;
1865 }
1866
ClosePipe(int id)1867 int GUI::ClosePipe(int id) {
1868 if (id < 0 || id > MAX_PIPES)
1869 return 0;
1870 if (Pipes[id].used == 0)
1871 return 0;
1872 if (Pipes[id].reading == 1) {
1873 Pipes[id].DoTerm = 1;
1874 SetEvent(Pipes[id].ResumeRead);
1875 WaitForSingleObject(&Pipes[id].Thread, INFINITE);
1876 }
1877 free(Pipes[id].buffer);
1878 free(Pipes[id].Command);
1879 CloseHandle(Pipes[id].NewData);
1880 CloseHandle(Pipes[id].ResumeRead);
1881 CloseHandle(Pipes[id].Access);
1882 CloseHandle(Pipes[id].Thread);
1883 //fprintf(stderr, "Pipe Close: %d\n", id);
1884 Pipes[id].used = 0;
1885 //ConContinue();
1886 return (Pipes[id].RetCode == 0);
1887 }
1888
GetPipeEvent(int i,TEvent * Event)1889 int GetPipeEvent(int i, TEvent *Event) {
1890 Event->What = evNone;
1891 if (Pipes[i].used == 0) return 0;
1892 if (Pipes[i].notify == 0) return 0;
1893 ResetEvent(Pipes[i].NewData);
1894 //fprintf(stderr, "Pipe New Data: %d\n", i);
1895 Event->What = evNotify;
1896 Event->Msg.View = 0;
1897 Event->Msg.Model = Pipes[i].notify;
1898 Event->Msg.Command = cmPipeRead;
1899 Event->Msg.Param1 = i;
1900 return 1;
1901 }
1902
1903 #endif
1904
ConGetEvent(TEventMask EventMask,TEvent * Event,int WaitTime,int Delete)1905 int ConGetEvent(TEventMask EventMask, TEvent *Event, int WaitTime, int Delete) /*FOLD00*/
1906 {
1907 //** Any saved events left?
1908 if (EventBuf.What != evNone) {
1909 *Event = EventBuf;
1910 if (Delete) EventBuf.What = evNone;
1911 return 1;
1912 }
1913 if (MouseEv.What != evNone) {
1914 *Event = MouseEv;
1915 if (Delete) MouseEv.What = evNone;
1916 return 1;
1917 }
1918
1919 //** Now block and wait for a new event on the console handle and all pipes,
1920 HANDLE o_ar[1 + MAX_PIPES];
1921 DWORD rc;
1922 int i, nh;
1923
1924 EventBuf.What = evNone;
1925 Event->What = evNone;
1926
1927 //** Fill the handle array with all active handles for pipes && console,
1928 o_ar[0] = ConIn;
1929 for(i = 0, nh = 1; i < MAX_PIPES; i++) { // For all possible pipes
1930 if (Pipes[i].used)
1931 o_ar[nh++] = Pipes[i].NewData;
1932 }
1933
1934 for(;;) {
1935 rc = WaitForMultipleObjects(nh, o_ar, FALSE, WaitTime);
1936 if (rc != WAIT_FAILED && (rc >= WAIT_OBJECT_0 && rc < WAIT_OBJECT_0+nh)) {
1937 i = rc - WAIT_OBJECT_0; // Get item that signalled new data
1938 if (i == 0) { // Was console?
1939 if (ReadConsoleEvent(Event)) // Get console,
1940 return 1; // And exit if valid,
1941 } else {
1942 GetPipeEvent(i - 1, Event); // Read data from pipe.
1943 return 1;
1944 }
1945 }
1946 else
1947 return 0; // Something's wrong!
1948 }
1949 }
1950
1951 #include "clip.h"
1952
GetClipText(ClipData * cd)1953 int GetClipText(ClipData *cd) {
1954 int rc = 0;
1955 cd->fLen = 0;
1956 cd->fChar = NULL;
1957 if (OpenClipboard(NULL)) {
1958 HANDLE hmem;
1959
1960 if ((hmem = GetClipboardData(CF_TEXT)) != NULL) {
1961 LPVOID data;
1962
1963 if ((data = GlobalLock(hmem)) != NULL) {
1964 int len = strlen((char *)data);
1965
1966 cd->fChar = (char *)malloc(len);
1967 if (cd->fChar != NULL) {
1968 cd->fLen = len;
1969 memcpy(cd->fChar, data, len);
1970 rc = 1;
1971 }
1972 GlobalUnlock(hmem);
1973 }
1974 }
1975 CloseClipboard();
1976 }
1977 return rc;
1978 }
1979
PutClipText(ClipData * cd)1980 int PutClipText(ClipData *cd) {
1981 int rc = 0;
1982 if (OpenClipboard(NULL)) {
1983 if (EmptyClipboard()) {
1984 HGLOBAL hmem;
1985
1986 if ((hmem = GlobalAlloc(GMEM_MOVEABLE, cd->fLen + 1)) != NULL) {
1987 LPVOID data;
1988
1989 if ((data = GlobalLock(hmem)) != NULL) {
1990 memcpy(data, cd->fChar, cd->fLen);
1991 ((char *)data)[cd->fLen] = 0;
1992 GlobalUnlock(hmem);
1993 if (SetClipboardData(CF_TEXT, hmem))
1994 rc = 1;
1995 }
1996 }
1997 }
1998 CloseClipboard();
1999 }
2000 return rc;
2001 }
2002