1 
2 
3 /* This version of microEmacs is based on the public domain C
4  * version written by Dave G. Conroy.
5  * The D programming language version is written by Walter Bright.
6  * http://www.digitalmars.com/d/
7  * This program is in the public domain.
8  */
9 
10 /*
11  * The routines in this file provide support for computers with
12  * WIN32 console I/O support.
13  */
14 
15 module console;
16 
17 version (Windows)
18 {
19 
20 import std.stdio;
21 import core.stdc.stdlib;
22 import core.stdc..string;
23 import core.sys.windows.windows;
24 import core.sys.windows.winuser;
25 
26 import ed;
27 import disp;
28 
29 
30 enum BEL = 0x07;                    /* BEL character.               */
31 enum ESC = 0x1B;                    /* ESC character.               */
32 
33 
34 static HANDLE hStdin;			// console input handle
35 static DWORD fdwSaveOldMode;
36 
37 static INPUT_RECORD lookaheadir;
38 static int lookahead;			// !=0 if data in lookaheadir
39 
40 /*
41  * Standard terminal interface dispatch table. Most of the fields point into
42  * "termio" code.
43  */
44 
45 
46 
47 struct TERM
48 {
49     int   t_nrow;              /* Number of rows.              */
50     int   t_ncol;              /* Number of columns.           */
51 
52     void t_open()             /* Open terminal at the start.  */
53     {
54 	hStdin  = GetStdHandle(STD_INPUT_HANDLE);
55 	if (hStdin == INVALID_HANDLE_VALUE)
56 	{   printf("getstdhandle\n");
57 	    exit(EXIT_FAILURE);
58 	}
59 
60 	if (!GetConsoleMode(hStdin,&fdwSaveOldMode))
61 	{   printf("getconsolemode\n");
62 	    exit(EXIT_FAILURE);
63 	}
64 
65 	if (!SetConsoleMode(hStdin,ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT))
66 	{   printf("setconsolemode\n");
67 	    exit(EXIT_FAILURE);
68 	}
69 
70 	disp_open();
71 	disp_setcursortype(DISP_CURSORBLOCK);
72 	t_ncol = disp_state.numcols;
73 	t_nrow = disp_state.numrows;
74     }
75 
76     void t_close()            /* Close terminal at end.       */
77     {
78 	disp_close();
79 
80 	if (!SetConsoleMode(hStdin,fdwSaveOldMode))
81 	{   printf("restore console mode\n");
82 	    exit(EXIT_FAILURE);
83 	}
84     }
85 
86     int t_getchar()          /* Get character from keyboard. */
87     {
88 	INPUT_RECORD buf;
89 	DWORD cNumRead;
90 	int c;
91 
92 	while (1)
93 	{
94 	    if (lookahead)
95 	    {   buf = lookaheadir;
96 		lookahead = 0;
97 	    }
98 	    else if (!ReadConsoleInputW(hStdin,&buf,1,&cNumRead))
99 	    {   c = 3;				// ^C
100 		goto Lret;
101 	    }
102 
103 	    switch (buf.EventType)
104 	    {
105 		case MOUSE_EVENT:
106 		    mstat_update(&buf.MouseEvent);
107 		    continue;
108 
109 		default:
110 		    continue;			// ignore
111 
112 		case KEY_EVENT:
113 		    c = win32_keytran(&buf.KeyEvent);
114 		    if (!c)
115 			continue;
116 		    goto Lret;
117 	    }
118 	}
119 
120     Lret:
121 	return c;
122     }
123 
124     void t_putchar(int c)          /* Put character to display.    */
125     {
126 	disp_putc(c);
127     }
128 
129     void t_flush()            /* Flush output buffers.        */
130     {
131 	disp_flush();
132     }
133 
134     void t_move(int row, int col)         /* Move the cursor, origin 0.   */
135     {
136 	disp_move(row, col);
137     }
138 
139     void t_eeol()             /* Erase to end of line.        */
140     {
141 	disp_eeol();
142     }
143 
144     void t_eeop()             /* Erase to end of page.        */
145     {
146 	disp_eeop();
147     }
148 
149     void t_beep()             /* Beep.                        */
150     {
151 	disp_putc(BEL);
152     }
153 
154     void t_standout()         /* Start standout mode          */
155     {
156 	disp_startstand();
157     }
158 
159     void t_standend()         /* End standout mode            */
160     {
161 	disp_endstand();
162     }
163 
164     void t_scrollup()         /* Scroll the screen up         */
165     {
166     }
167 
168     void t_scrolldn()         /* Scroll the screen down       */
169 				 /* Note: scrolling routines do  */
170 				 /*  not save cursor position.   */
171     {
172     }
173 
174     void t_setcursor(int insertmode)
175     {
176 	disp_setcursortype(insertmode ? DISP_CURSORBLOCK : DISP_CURSORUL);
177     }
178 }
179 
180 TERM term;
181 
182 /********************************************
183  */
184 
185 void updateline(int row,attchar_t[] buffer,attchar_t[] physical)
186 {
187     int col;
188     int numcols;
189     CHAR_INFO *psb;
190     CHAR_INFO[256] sbbuf;
191     CHAR_INFO *sb;
192     COORD sbsize;
193     static COORD sbcoord;
194     SMALL_RECT sdrect;
195 
196     sbsize.X = cast(short)disp_state.numcols;
197     sbsize.Y = 1;
198     sbcoord.X = 0;
199     sbcoord.Y = 0;
200     sdrect.Left = 0;
201     sdrect.Top = cast(short)row;
202     sdrect.Right = cast(short)(disp_state.numcols - 1);
203     sdrect.Bottom = cast(short)row;
204     numcols = disp_state.numcols;
205     sb = sbbuf.ptr;
206     if (numcols > sbbuf.length)
207     {
208 	sb = cast(CHAR_INFO *)alloca(numcols * CHAR_INFO.sizeof);
209     }
210     for (col = 0; col < numcols; col++)
211     {
212 	auto c = buffer[col].chr;
213 	sb[col].UnicodeChar = cast(WCHAR)c;
214 	sb[col].Attributes = buffer[col].attr;
215 	if (c >= 0x10000)
216 	{
217 	    /* Calculate surrogate pairs, but don't know yet how they
218 	     * work, if at all, with WriteConsoleOutput()
219 	     */
220 	    auto c0 = cast(wchar)((((c - 0x10000) >> 10) & 0x3FF) + 0xD800);
221 	    auto c1 = cast(wchar)(((c - 0x10000) & 0x3FF) + 0xDC00);
222 	}
223 	//printf("col = %2d, x%2x, '%c'\n",col,sb[col].AsciiChar,sb[col].AsciiChar);
224     }
225     if (!WriteConsoleOutputW(cast(HANDLE)disp_state.handle,sb,sbsize,sbcoord,&sdrect))
226     {
227 	// error
228     }
229     physical[] = buffer[];
230 }
231 
232 /*********************************
233  */
234 
235 extern (C) int msm_init()
236 {
237     return GetSystemMetrics(SM_MOUSEPRESENT);
238 }
239 
240 extern (C)
241 {
242     void	msm_term() { }
243     void	msm_showcursor() { }
244     void	msm_hidecursor() { }
245 }
246 
247 struct msm_status		// current state of mouse
248 {
249     uint row;
250     uint col;
251     int buttons;
252 }
253 
254 msm_status mstat;
255 
256 /*************************
257  * Fold MOUSE_EVENT into mstat.
258  */
259 
260 static void mstat_update(MOUSE_EVENT_RECORD *pme)
261 {
262     mstat.row = pme.dwMousePosition.Y;
263     mstat.col = pme.dwMousePosition.X;
264     mstat.buttons = pme.dwButtonState & 3;
265 }
266 
267 extern (C) int msm_getstatus(uint *pcol,uint *prow)
268 {
269     INPUT_RECORD buf;
270     DWORD cNumRead;
271 
272     if (lookahead)
273     {	buf = lookaheadir;
274 	cNumRead = 1;
275     }
276     else if (!PeekConsoleInputA(hStdin,&buf,1,&cNumRead))
277 	goto Lret;
278 
279     if (cNumRead)
280 	switch (buf.EventType)
281 	{
282 	    case MOUSE_EVENT:
283 		mstat_update(&buf.MouseEvent);
284 		goto default;
285 
286 	    default:
287 	    Ldiscard:
288 		if (lookahead)
289 		    lookahead = 0;
290 		else
291 		    ReadConsoleInputA(hStdin,&buf,1,&cNumRead);	// discard
292 		break;
293 
294 	    case KEY_EVENT:
295 		if (mstat.buttons & 3)
296 		    goto Ldiscard;
297 		break;
298 	}
299 
300 Lret:
301     *prow = mstat.row;
302     *pcol = mstat.col;
303     return mstat.buttons;
304 }
305 
306 /*************************************
307  * Translate key from WIN32 to IBM PC style.
308  * Params:
309  *	pkey = pointer to key data
310  * Returns:
311  *	0 if ignore it
312  * References:
313  *      https://github.com/dlang/druntime/blob/master/src/core/sys/windows/wincon.d
314  */
315 
316 static uint win32_keytran(KEY_EVENT_RECORD *pkey)
317 {
318     if (!pkey.bKeyDown)
319 	return 0;				// ignore button up events
320     uint c = pkey.UnicodeChar;
321 /+
322     printf("RepeatCount %x VirtualKeyCode %x VirtualScanCode %x UnicodeChar %x AsciiChar %x ControlKeyState %x\n",
323 	pkey.wRepeatCount, pkey.wVirtualKeyCode, pkey.wVirtualScanCode, pkey.UnicodeChar, pkey.AsciiChar,
324 	pkey.dwControlKeyState);
325 +/
326     if (c == 0)
327     {
328 	switch (pkey.wVirtualScanCode)
329 	{
330 	    case 0x1D:				// Ctrl
331 	    case 0x38:				// Alt
332 	    case 0x2A:				// Left Shift
333 	    case 0x36:				// Right Shift
334 		break;				// ignore
335 	    default:
336 		c = (pkey.wVirtualScanCode << 8) & 0xFF00;
337 		if (pkey.dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
338 		{
339 		    switch (c)
340 		    {   case 0x4700:	c = 0x7700;	break;	// Home
341 			case 0x4F00:	c = 0x7500;	break;	// End
342 			case 0x4900:	c = 0x8400;	break;	// PgUp
343 			case 0x5100:	c = 0x7600;	break;	// PgDn
344 			default:	c = 0;		break;
345 		    }
346 		}
347 		break;
348 	}
349     }
350     else if (pkey.dwControlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
351     {
352 	c = (pkey.wVirtualScanCode << 8) & 0xFF00;
353     }
354 Lret:
355     return c;
356 }
357 
358 /**************************
359  * Return when there are unread chars ready in the input.
360  */
361 
362 void ttwaitkeys()
363 {
364 } 
365 
366 /*************************************
367  * Wait for any input (yield to other processes).
368  */
369 
370 void ttyield()
371 {
372     if (!lookahead)
373     {
374 	DWORD cNumRead;
375 
376 	if (!ReadConsoleInputA(hStdin,&lookaheadir,1,&cNumRead))
377 	{   printf("readconsoleinput\n");
378 	    goto Lret;
379 	}
380     }
381     lookahead = 1;
382 Lret: ;
383 }
384 
385 /*************************************
386  */
387 
388 int ttkeysininput()
389 {
390     INPUT_RECORD buf;
391     DWORD cNumRead;
392 
393     if (lookahead)
394     {	buf = lookaheadir;
395 	cNumRead = 1;
396     }
397     else if (!PeekConsoleInputA(hStdin,&buf,1,&cNumRead))
398 	goto Lret;
399 
400     if (cNumRead)
401     {
402 	switch (buf.EventType)
403 	{
404 	    case MOUSE_EVENT:
405 		mstat_update(&buf.MouseEvent);
406 		goto default;
407 
408 	    default:
409 	    Ldiscard:
410 		if (lookahead)
411 		    lookahead = 0;
412 		else
413 		    ReadConsoleInputA(hStdin,&buf,1,&cNumRead);	// discard
414 		cNumRead = 0;
415 		break;
416 
417 	    case KEY_EVENT:
418 		if (!win32_keytran(&buf.KeyEvent))
419 		    goto Ldiscard;
420 		break;
421 	}
422     }
423 
424 Lret:
425     return cNumRead != 0;
426 }
427 
428 extern (C) void popen() { assert(0); }
429 
430 void setClipboard(const(char)[] s)
431 {
432     if (OpenClipboard(null))
433     {
434 	EmptyClipboard();
435 
436 	HGLOBAL hmem = GlobalAlloc(GMEM_MOVEABLE, (s.length + 1) * char.sizeof);
437 	if (hmem)
438 	{
439 	    auto p = cast(char*)GlobalLock(hmem);
440 	    memcpy(p, s.ptr, s.length * char.sizeof);
441 	    p[s.length] = 0;
442 	    GlobalUnlock(hmem);
443 
444 	    SetClipboardData(CF_TEXT, hmem);
445 	}
446 	CloseClipboard();
447     }
448 }
449 
450 char[] getClipboard()
451 {
452     char[] s = null;
453     if (IsClipboardFormatAvailable(CF_TEXT) &&
454         OpenClipboard(null))
455     { 
456 	HANDLE h = GetClipboardData(CF_TEXT);	// CF_UNICODETEXT is UTF-16
457 	if (h)
458 	{   
459 	    auto p = cast(char*)GlobalLock(h); 
460 	    if (p)
461 	    {
462 		size_t length = strlen(p);
463 		s = p[0 .. length].dup;
464 	    }
465 	    GlobalUnlock(h);
466 	} 
467 	CloseClipboard();
468     }
469     return s; 
470 }
471 
472 /***********************
473  * Open browser on help file.
474  */
475 
476 int help(bool f, int n)
477 {
478     printf("\nhelp \n");
479     char[MAX_PATH + 1] resolved_name = void;
480     if (GetModuleFileNameA(NULL, resolved_name.ptr, MAX_PATH + 1))
481     {
482 	size_t len = strlen(resolved_name.ptr);
483 	size_t i;
484 	for (i = len; i; --i)
485 	{
486 	    if (resolved_name[i] == '/' ||
487 		resolved_name[i] == '\\' ||
488 		resolved_name[i] == ':')
489 	    {
490 		++i;
491 		break;
492 	    }
493 	}
494 	immutable(char)[7] doc = "me.html";
495 	if (i + doc.sizeof <= MAX_PATH)
496 	{
497 	    import std.process;
498 	    memcpy(resolved_name.ptr + i, doc.ptr, doc.sizeof);
499     printf("\nhelp2 '%.*s'\n", cast(int)(i + doc.sizeof), resolved_name.ptr);
500 	    browse(cast(string)resolved_name[0 .. i + doc.sizeof]);
501 	}
502     }
503     return ed.FALSE;
504 }
505 
506 }
507 else
508 {
509 
510 import ed;
511 
512 /***********************
513  * Open browser on help file.
514  */
515 
516 int help(bool f, int n)
517 {
518     return ed.FALSE;
519 }
520 
521 }
522