1 /*
2  * Uses the Win32 screen API.
3  *
4  * $Id: ntwinio.c,v 1.223 2021/01/01 10:17:51 tom Exp $
5  * Written by T.E.Dickey for vile (october 1997).
6  * -- improvements by Clark Morgan (see w32cbrd.c, w32pipe.c).
7  */
8 
9 #include "estruct.h"		/* includes <windows.h> */
10 
11 #include <windowsx.h>
12 #include <commdlg.h>
13 #include <shellapi.h>
14 #include <stdlib.h>
15 #include <math.h>
16 
17 #include "edef.h"
18 #include "pscreen.h"
19 #include "patchlev.h"
20 #include "winvile.h"
21 #include "nefsms.h"
22 #include "nefunc.h"
23 #include "chgdfunc.h"
24 #include "makeargv.h"
25 
26 #define ABS(x) (((x) < 0) ? -(x) : (x))
27 
28 #define WM_SYSTIMER 0x118	// undocumented http://support.microsoft.com/?id=108938
29 
30 #define MIN_ROWS MINWLNS
31 #define MIN_COLS 15
32 
33 #define OOPS (4)
34 
35 #define MAX_CURSOR_STYLE 2
36 
37 #if OPT_TRACE
38 #define IGN_PROC(tag,name) \
39 	case name: \
40 		TRACE((tag #name " (ignored)\n")); \
41 		break;
42 
43 #define DEF_PROC(tag,name) \
44 	case name: \
45 		TRACE((tag #name " (%s)\n", which_window(hWnd))); \
46 		return (DefWindowProc(hWnd, message, wParam, lParam))
47 #else
48 #define IGN_PROC(tag,name) case name: break;
49 #define DEF_PROC(tag,name)	/*nothing */
50 #endif
51 
52 #define MAIN_CLASS W32_STRING("VileMain")
53 #define TEXT_CLASS W32_STRING("VileText")
54 #define GRIP_CLASS W32_STRING("VileResize")
55 
56 #define MY_APPLE W32_STRING("Vile Application")
57 
58 #define MY_FONT  SYSTEM_FIXED_FONT	/* or ANSI_FIXED_FONT           */
59 
60 #define NROW	128		/* Max Screen size.             */
61 #define NCOL    256		/* Edit if you want to.         */
62 #define NOKYMAP (-1)
63 #define KYREDIR (-2)		/* sent keystroke elsewhere.    */
64 
65 #define RSZ_WDW_HGHT  18	/* pixels */
66 #define RSZ_WDW_WDTH 190
67 
68 #define SetCols(value) term.cols = cur_win->cols = value
69 #define SetRows(value) term.rows = cur_win->rows = value
70 
71 #define RowToPixel(n) ((n) * nLineHeight)
72 #define ColToPixel(n) ((n) * nCharWidth)
73 
74 #define PixelToRow(n) ((n) / nLineHeight)
75 #define PixelToCol(n) ((n) / nCharWidth)
76 
77 #define RectToCols(rect) (PixelToCol(rect.right - rect.left - SbWidth))
78 #define RectToRows(rect) (PixelToRow(rect.bottom - rect.top))
79 
80 #if OPT_SCROLLBARS
81 #define SbWidth   nLineHeight	// FIXME? GetSystemMetrics(SM_CXVSCROLL);
82 #else
83 #define SbWidth   0
84 #endif
85 
86 static DWORD default_bcolor;
87 static DWORD default_fcolor;
88 static ENC_CHOICES my_encoding = enc_DEFAULT;
89 static HANDLE hAccTable;	/* handle to accelerator table */
90 static HANDLE vile_hinstance;
91 static HCURSOR arrow_cursor;
92 static HCURSOR hglass_cursor;
93 static HCURSOR selection_cursor;
94 static HCURSOR wdwsize_cursor;
95 static HMENU vile_menu, popup_menu;
96 static LOGFONT vile_logfont;
97 static SETTIMER_RETVAL nIDTimer = 0;
98 static int ac_active = FALSE;	/* AutoColor active? */
99 static int caret_disabled = TRUE;
100 static int caret_exists = 0;
101 static int caret_visible = 0;
102 static int desired_wdw_state;
103 static int dont_update_sb = FALSE;
104 static int enable_popup = TRUE;
105 static int font_resize_in_progress;
106 static int gui_resize_in_progress;
107 static int initialized = FALSE;	/* winvile open for business */
108 static int mouse_captured = 0;
109 static int nCursorAdj = 0;
110 static int nCharWidth = 8;
111 static int nLineToFill = 0;
112 static int nLineHeight = 10;
113 static int vile_resizing = FALSE;	/* rely on repaint_window if true */
114 static int vile_selecting = FALSE;	/* true when using mouse to select */
115 static int icursor;		/* T -> enable insertion cursor   */
116 static int icursor_style;	/* 1 -> cmdmode = block,
117 				 *      insmode = vertical bar
118 				 * 2 -> cmdmode = vertical bar
119 				 *      insmode = block
120 				 */
121 
122 #ifdef VILE_OLE
123 static OLEAUTO_OPTIONS oa_opts;
124 static int redirect_keys;
125 #endif
126 
127 static int nfcolor = -1;	/* normal foreground color */
128 static int nbcolor = -1;	/* normal background color */
129 static int crow = -1;		/* current row */
130 static int ccol = -1;		/* current col */
131 
132 /* ansi to ibm color translation table */
133 static const char *initpalettestr = "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15";
134 /* black, red, green, yellow, blue, magenta, cyan, white   */
135 
136 static int cur_pos = 0;
137 static VIDEO_ATTR cur_atr = 0;
138 
139 typedef struct {
140     HWND w;
141     RECT r;
142     int shown;
143 } SBDATA;
144 
145 static struct gui_info {
146     HWND main_hwnd;		/* the top-level window */
147     HWND text_hwnd;		/* the enclosed text area */
148     int closed;
149     int cols;
150     int rows;
151     RECT xy_limit;
152     int x_border;
153     int y_border;
154     int y_titles;		/* height of caption/titles */
155     int nscrollbars;
156     int maxscrollbars;
157     SBDATA *scrollbars;
158     SBDATA size_box;
159     SBDATA size_grip;
160 } only_window, *cur_win = &only_window;
161 /* *INDENT-OFF* */
162 static struct my_font {
163     int used;
164     HFONT font;
165     VIDEO_ATTR attr;
166 } MyFonts[] = {
167     { FALSE, 0, 0 },
168     { FALSE, 0, VABOLD },
169     { FALSE, 0, VAUL },
170     { FALSE, 0, VAITAL },
171 };
172 /* *INDENT-ON* */
173 
174 #if OPT_SCROLLBARS
175 static int check_scrollbar_allocs(void);
176 static void update_scrollbar_sizes(void);
177 #endif
178 
179 #if OPT_TRACE
180 static char *
which_window(HWND hwnd)181 which_window(HWND hwnd)
182 {
183     if (hwnd == 0) {
184 	return "NULL";
185     }
186     if (hwnd == cur_win->main_hwnd) {
187 	return "main";
188     } else if (hwnd == cur_win->text_hwnd) {
189 	return "text";
190     } else {
191 	int n;
192 	static char temp[20];
193 	sprintf(temp, "h%p", hwnd);
194 	for (n = 0; n < cur_win->nscrollbars; n++) {
195 	    if (hwnd == cur_win->scrollbars[n].w) {
196 		sprintf(temp, "sb%d", n);
197 		break;
198 	    }
199 	}
200 	return temp;
201     }
202 }
203 
204 static char *
message2s(unsigned code)205 message2s(unsigned code)
206 {
207     static struct {
208 	WORD code;
209 	char *name;
210     } table[] = {
211 	/* *INDENT-OFF* */
212 	{ WM_ACTIVATE,		"WM_ACTIVATE" },
213 	{ WM_ACTIVATEAPP,	"WM_ACTIVATEAPP" },
214 	{ WM_CANCELMODE,	"WM_CANCELMODE" },
215 	{ WM_CAPTURECHANGED,	"WM_CAPTURECHANGED" },
216 	{ WM_CHAR,		"WM_CHAR" },
217 	{ WM_CLOSE,		"WM_CLOSE" },
218 	{ WM_CONTEXTMENU,	"WM_CONTEXTMENU" },
219 	{ WM_CREATE,		"WM_CREATE" },
220 	{ WM_CTLCOLORSCROLLBAR,	"WM_CTLCOLORSCROLLBAR" },
221 	{ WM_DROPFILES,		"WM_DROPFILES" },
222 	{ WM_ENABLE,		"WM_ENABLE" },
223 	{ WM_ENTERIDLE,		"WM_ENTERIDLE" },
224 	{ WM_ENTERMENULOOP,	"WM_ENTERMENULOOP" },
225 	{ WM_ENTERSIZEMOVE,	"WM_ENTERSIZEMOVE" },
226 	{ WM_ERASEBKGND,	"WM_ERASEBKGND" },
227 	{ WM_EXITMENULOOP,	"WM_EXITMENULOOP" },
228 	{ WM_EXITSIZEMOVE,	"WM_EXITSIZEMOVE" },
229 	{ WM_GETMINMAXINFO,	"WM_GETMINMAXINFO" },
230 	{ WM_GETTEXT,		"WM_GETTEXT" },
231 	{ WM_IME_NOTIFY,	"WM_IME_NOTIFY" },
232 	{ WM_IME_SETCONTEXT,	"WM_IME_SETCONTEXT" },
233 	{ WM_INITMENU,		"WM_INITMENU" },
234 	{ WM_INITMENUPOPUP,	"WM_INITMENUPOPUP" },
235 	{ WM_KEYDOWN,		"WM_KEYDOWN" },
236 	{ WM_KEYUP,		"WM_KEYUP" },
237 	{ WM_KILLFOCUS,		"WM_KILLFOCUS" },
238 	{ WM_MENUSELECT,	"WM_MENUSELECT" },
239 	{ WM_MOUSEACTIVATE,	"WM_MOUSEACTIVATE" },
240 	{ WM_MOUSEMOVE,		"WM_MOUSEMOVE" },
241 	{ WM_MOVE,		"WM_MOVE" },
242 	{ WM_MOVING,		"WM_MOVING" },
243 	{ WM_NCACTIVATE,	"WM_NCACTIVATE" },
244 	{ WM_NCCALCSIZE,	"WM_NCCALCSIZE" },
245 	{ WM_NCCREATE,		"WM_NCCREATE" },
246 	{ WM_NCHITTEST,		"WM_NCHITTEST" },
247 	{ WM_NCLBUTTONDOWN,	"WM_NCLBUTTONDOWN" },
248 	{ WM_NCMOUSEMOVE,	"WM_NCMOUSEMOVE" },
249 	{ WM_NCPAINT,		"WM_NCPAINT" },
250 	{ WM_PAINT,		"WM_PAINT" },
251 	{ WM_PARENTNOTIFY,	"WM_PARENTNOTIFY" },
252 	{ WM_QUERYNEWPALETTE,	"WM_QUERYNEWPALETTE" },
253 	{ WM_RBUTTONUP,		"WM_RBUTTONUP" },
254 	{ WM_SETCURSOR,		"WM_SETCURSOR" },
255 	{ WM_SETFOCUS,		"WM_SETFOCUS" },
256 	{ WM_SETTEXT,		"WM_SETTEXT" },
257 	{ WM_SHOWWINDOW,	"WM_SHOWWINDOW" },
258 	{ WM_SIZE,		"WM_SIZE" },
259 	{ WM_SIZING,		"WM_SIZING" },
260 	{ WM_STYLECHANGED,	"WM_STYLECHANGED" },
261 	{ WM_STYLECHANGING,	"WM_STYLECHANGING" },
262 	{ WM_SYSCOMMAND,	"WM_SYSCOMMAND" },
263 	{ WM_SYSKEYDOWN,	"WM_SYSKEYDOWN" },
264 	{ WM_SYSKEYUP,		"WM_SYSKEYUP" },
265 	{ WM_SYSTIMER,		"WM_SYSTIMER" },
266 	{ WM_TIMER,		"WM_TIMER" },
267 	{ WM_USER,		"WM_USER" },
268 	{ WM_WINDOWPOSCHANGED,	"WM_WINDOWPOSCHANGED" },
269 	{ WM_WINDOWPOSCHANGING,	"WM_WINDOWPOSCHANGING" },
270 	/* custom ntwinio WINDOWS messages */
271 	{ WM_WVILE_CURSOR_ON,	"WM_WVILE_CURSOR_ON" },
272 	{ WM_WVILE_CURSOR_OFF,	"WM_WVILE_CURSOR_OFF" },
273 	/* *INDENT-ON* */
274 
275     };
276     size_t n;
277     static char temp[20];
278 
279     for (n = 0; n < TABLESIZE(table); n++) {
280 	if (table[n].code == code) {
281 	    return table[n].name;
282 	}
283     }
284     sprintf(temp, "%#x", code);
285     return temp;
286 }
287 
288 static char *
syscommand2s(unsigned code)289 syscommand2s(unsigned code)
290 {
291     static struct {
292 	WORD code;
293 	char *name;
294     } table[] = {
295 	/* *INDENT-OFF* */
296 	{ SC_SIZE,		"SC_SIZE" },
297 	{ SC_MOVE,		"SC_MOVE" },
298 	{ SC_MINIMIZE,		"SC_MINIMIZE" },
299 	{ SC_MAXIMIZE,		"SC_MAXIMIZE" },
300 	{ SC_NEXTWINDOW,	"SC_NEXTWINDOW" },
301 	{ SC_PREVWINDOW,	"SC_PREVWINDOW" },
302 	{ SC_CLOSE,		"SC_CLOSE" },
303 	{ SC_VSCROLL,		"SC_VSCROLL" },
304 	{ SC_HSCROLL,		"SC_HSCROLL" },
305 	{ SC_MOUSEMENU,		"SC_MOUSEMENU" },
306 	{ SC_KEYMENU,		"SC_KEYMENU" },
307 	{ SC_ARRANGE,		"SC_ARRANGE" },
308 	{ SC_RESTORE,		"SC_RESTORE" },
309 	{ SC_TASKLIST,		"SC_TASKLIST" },
310 	{ SC_SCREENSAVE,	"SC_SCREENSAVE" },
311 	{ SC_HOTKEY,		"SC_HOTKEY" },
312 	/* *INDENT-ON* */
313 
314     };
315     size_t n;
316     static char temp[80];
317     unsigned remainder = code & 0xf;
318 
319     for (n = 0; n < TABLESIZE(table); n++) {
320 	if (table[n].code == (code - remainder)) {
321 	    sprintf(temp, "%s+%X", table[n].name, remainder);
322 	    return temp;
323 	}
324     }
325     sprintf(temp, "%#x", code);
326     return temp;
327 }
328 
329 static void
TraceWindowRect(HWND hwnd)330 TraceWindowRect(HWND hwnd)
331 {
332     RECT wrect;
333     int rows;
334     int cols;
335     GetWindowRect(hwnd, &wrect);
336     cols = RectToCols(wrect);
337     rows = RectToRows(wrect);
338     TRACE(("... (%3ld,%3ld) (%3ld,%3ld), window is %dx%d cells (%dx%d pixels)%s\n",
339 	   wrect.top, wrect.left,
340 	   wrect.bottom, wrect.right,
341 	   rows,
342 	   cols,
343 	   RowToPixel(rows),
344 	   ColToPixel(cols),
345 	   IsZoomed(hwnd) ? " zoomed" : ""
346 	  ));
347 }
348 
349 static void
TraceClientRect(HWND hwnd)350 TraceClientRect(HWND hwnd)
351 {
352     RECT crect;
353     int rows;
354     int cols;
355     GetClientRect(hwnd, &crect);
356     cols = RectToCols(crect);
357     rows = RectToRows(crect);
358     TRACE(("... (%3ld,%3ld) (%3ld,%3ld), client is %dx%d cells (%dx%d pixels)\n",
359 	   crect.top, crect.left,
360 	   crect.bottom, crect.right,
361 	   rows,
362 	   cols,
363 	   RowToPixel(rows),
364 	   ColToPixel(cols)
365 	  ));
366 }
367 
368 #else
369 #define TraceWindowRect(hwnd)	/* nothing */
370 #define TraceClientRect(hwnd)	/* nothing */
371 #endif
372 
373 static HBRUSH
Background(HDC hdc)374 Background(HDC hdc)
375 {
376     TRACE(("Background %#06lx\n", GetBkColor(hdc)));
377     return CreateSolidBrush(GetBkColor(hdc));
378 }
379 
380 void
gui_resize(int cols,int rows)381 gui_resize(int cols, int rows)
382 {
383     RECT crect;
384     RECT wrect;
385     int main_wide;
386     int main_high;
387     int text_wide;
388     int text_high;
389 
390     /*
391      * There's an undesirable feedback loop between gui_resize() and
392      * ResizeClient() when the user changes the font.  Here's how it
393      * goes:
394      *
395      * - user changes font
396      * - use_font() is eventually called
397      * - use_font() calls gui_resize()
398      * - gui_resize calls MoveWindow(cur_win->main_hwnd, ...) which triggers
399      *       a call to MainWndProc(), which calls ResizeClient()
400      * - ResizeClient() calls gui_resize()
401      *
402      * ResizeClient(), unfortunately, recomputes the editor's rows and
403      * cols and sometimes gets it wrong, especially when the number of
404      * rows exceeds a magic value (e.g., 37 rows on a 1024 x 768
405      * screen).
406      *
407      * To stop the feedback loop, check to see if an instance of
408      * gui_resize() is active, and if so, exit.
409      *
410      * Note:  even though "gui_resize_in_progress" breaks the
411      * aforementioned feedback loop, bear in mind that it's still not
412      * possible to define a screen geometry that's greater than the
413      * dimensions of the current desktop.  This restriction is enforced by
414      * Windows when the first ShowWindow() call is made from
415      * winvile_start().
416      */
417     if (gui_resize_in_progress && font_resize_in_progress)
418 	return;
419     gui_resize_in_progress = TRUE;
420 
421     TRACE(("gui_resize(%d x %d)\n", rows, cols));
422     TraceWindowRect(cur_win->main_hwnd);
423     TraceClientRect(cur_win->main_hwnd);
424 
425     GetClientRect(cur_win->main_hwnd, &crect);
426     GetWindowRect(cur_win->main_hwnd, &wrect);
427 
428     text_wide = ColToPixel(cols);
429     text_high = RowToPixel(rows);
430     wrect.right += text_wide - crect.right;
431     wrect.bottom += text_high - crect.bottom;
432     main_wide = text_wide + (2 * cur_win->x_border) + SbWidth;
433     main_high = text_high + (2 * cur_win->y_border) + cur_win->y_titles;
434 
435     TRACE(("... gui_resize -> (%ld,%ld) (%ld,%ld) main %dx%d, text %dx%d\n",
436 	   wrect.top,
437 	   wrect.left,
438 	   wrect.bottom,
439 	   wrect.right,
440 	   main_high,
441 	   main_wide,
442 	   text_high,
443 	   text_wide));
444 
445     MoveWindow(cur_win->main_hwnd,
446 	       wrect.left,
447 	       wrect.top,
448 	       main_wide,
449 	       main_high,
450 	       TRUE);
451     MoveWindow(cur_win->text_hwnd,
452 	       0,
453 	       0,
454 	       text_wide,
455 	       text_high,
456 	       TRUE);
457 
458     TraceWindowRect(cur_win->main_hwnd);
459     TraceClientRect(cur_win->main_hwnd);
460 #if OPT_SCROLLBARS
461     update_scrollbar_sizes();
462 #endif
463     TRACE(("... gui_resize finish\n"));
464     gui_resize_in_progress = FALSE;
465 }
466 
467 static int
AdjustedHeight(int high)468 AdjustedHeight(int high)
469 {
470     int extra = cur_win->y_titles + (2 * cur_win->y_border);
471     int rows;
472     if (high > cur_win->xy_limit.bottom)
473 	high = cur_win->xy_limit.bottom;
474     rows = PixelToRow(high - extra);
475     if (rows < MIN_ROWS)
476 	rows = MIN_ROWS;
477     return RowToPixel(rows) + extra;
478 }
479 
480 static int
AdjustedWidth(int wide)481 AdjustedWidth(int wide)
482 {
483     int extra = SbWidth + (2 * cur_win->x_border);
484     int cols;
485     if (wide > cur_win->xy_limit.right)
486 	wide = cur_win->xy_limit.right;
487     cols = PixelToCol(wide - extra);
488     if (cols < MIN_COLS)
489 	cols = MIN_COLS;
490     return ColToPixel(cols) + extra;
491 }
492 
493 static WINDOW_PROC_RETVAL
AdjustPosChanging(HWND hwnd,WINDOWPOS * pos)494 AdjustPosChanging(HWND hwnd, WINDOWPOS * pos)
495 {
496     (void) hwnd;
497 
498     if (!(pos->flags & SWP_NOSIZE)) {
499 	int wide = AdjustedWidth(pos->cx);
500 	int high = AdjustedHeight(pos->cy);
501 
502 	TRACE(("...%s position %d,%d, resize from %d,%d to %d,%d\n",
503 	       IsZoomed(hwnd) ? " zoomed" : "",
504 	       pos->y, pos->x,
505 	       pos->cy, pos->cx, high, wide));
506 	pos->cx = wide;
507 	pos->cy = high;
508     }
509     return 0;
510 }
511 
512 static HWND
sizing_window(void)513 sizing_window(void)
514 {
515     RECT crect;
516     int szw = RSZ_WDW_WDTH, szh = RSZ_WDW_HGHT, szx, szy;
517 
518     GetClientRect(cur_win->main_hwnd, &crect);
519     szx = crect.right / 2 - szw / 2;
520     szy = crect.bottom / 2 - szh / 2;
521     if (szx < 0 || szy < 0)
522 	szx = szy = CW_USEDEFAULT;
523 
524     return (CreateWindow(W32_STRING("STATIC"),
525 			 W32_STRING(""),
526 			 WS_CHILD | WS_VISIBLE | SS_CENTER,
527 			 szx,
528 			 szy,
529 			 szw,
530 			 szh,
531 			 cur_win->main_hwnd,
532 			 (HMENU) 0,
533 			 vile_hinstance,
534 			 (LPVOID) 0));
535 }
536 
537 /*
538  * Handle WM_SIZING, forcing the screen size to stay in multiples of a
539  * character cell.
540  */
541 static WINDOW_PROC_RETVAL
AdjustResizing(HWND hwnd,WPARAM fwSide,RECT * rect)542 AdjustResizing(HWND hwnd, WPARAM fwSide, RECT * rect)
543 {
544     int wide = rect->right - rect->left;
545     int high = rect->bottom - rect->top;
546     int adjX = wide - AdjustedWidth(wide);
547     int adjY = high - AdjustedHeight(high);
548 
549     (void) hwnd;
550 
551     TRACE(("AdjustResizing now (%ld,%ld) (%ld,%ld) (%ldx%ld pixels)\n",
552 	   rect->top, rect->left,
553 	   rect->bottom, rect->right,
554 	   (rect->bottom - rect->top),
555 	   (rect->right - rect->left)));
556 
557     TraceWindowRect(hwnd);
558     TraceClientRect(hwnd);
559 
560     if (fwSide == WMSZ_LEFT
561 	|| fwSide == WMSZ_TOPLEFT
562 	|| fwSide == WMSZ_BOTTOMLEFT)
563 	rect->left += adjX;
564     else if (fwSide == WMSZ_RIGHT
565 	     || fwSide == WMSZ_TOPRIGHT
566 	     || fwSide == WMSZ_BOTTOMRIGHT)
567 	rect->right -= adjX;
568 
569     if (fwSide == WMSZ_TOP
570 	|| fwSide == WMSZ_TOPLEFT
571 	|| fwSide == WMSZ_TOPRIGHT)
572 	rect->top += adjY;
573     else if (fwSide == WMSZ_BOTTOM
574 	     || fwSide == WMSZ_BOTTOMLEFT
575 	     || fwSide == WMSZ_BOTTOMRIGHT)
576 	rect->bottom -= adjY;
577 
578     TRACE(("... AdjustResizing (%ld,%ld) (%ld,%ld) adjY:%d, adjX:%d\n",
579 	   rect->top, rect->left,
580 	   rect->bottom, rect->right,
581 	   adjY, adjX));
582 
583     return TRUE;
584 }
585 
586 static void
ResizeClient(void)587 ResizeClient(void)
588 {
589     int h, w;
590     RECT crect;
591 
592     if (cur_win->closed) {
593 	TRACE(("ResizeClient ignored (closed)\n"));
594 	return;
595     }
596 
597     /*
598      * See comments in gui_resize() for an explanation of
599      * gui_resize_in_progress.
600      */
601     if (gui_resize_in_progress && font_resize_in_progress)
602 	return;
603     TRACE(("ResizeClient begin, currently %dx%d\n", term.rows, term.cols));
604     TraceWindowRect(cur_win->main_hwnd);
605     TraceClientRect(cur_win->main_hwnd);
606     GetClientRect(cur_win->main_hwnd, &crect);
607 
608     h = RectToRows(crect);
609     w = RectToCols(crect);
610 
611     /*
612      * The WM_WINDOWPOSCHANGING message is supposed to allow modification
613      * to keep a window in bounds.  But it doesn't work.  This does (by
614      * forcing the calls on MoveWindow to have a "good" value).
615      */
616     if (h < MIN_ROWS)
617 	h = MIN_ROWS;
618 
619     if (w < MIN_COLS)
620 	w = MIN_COLS;
621 
622     if ((h > 1 && h != term.rows) || (w > 1 && w != term.cols)) {
623 	TRACE(("...ResizeClient %dx%d\n", h, w));
624 	vile_resizing = TRUE;
625 	newscreensize(h, w);
626 	SetRows(h);
627 	SetCols(w);
628 #if OPT_SCROLLBARS
629 	if (check_scrollbar_allocs() == TRUE)	/* no allocation failure */
630 	    update_scrollbar_sizes();
631 #endif
632 	vile_resizing = FALSE;
633 	TRACE(("...ResizeClient %dx%d\n", h, w));
634     } else {
635 	TRACE(("ResizeClient ignored (h=%d, w=%d, vs %d x %d)\n",
636 	       h, w, term.rows, term.cols));
637     }
638 
639     gui_resize(w, h);
640     TRACE(("...ResizeClient finish\n"));
641 }
642 
643 static int RedValue[NCOLORS], GreenValue[NCOLORS], BlueValue[NCOLORS];
644 
645 /*
646  * Ask for a palette setting string.
647  * Format is "<entry> <red> <green> <blue>"
648  */
649 int
SetRGBPalette(int f,int n)650 SetRGBPalette(int f, int n)
651 {
652 #define RGB_DATA(name) { name, 0 }
653     static struct {
654 	char *name;
655 	int value;
656     } table[3] = {
657 	RGB_DATA("red"), RGB_DATA("green"), RGB_DATA("blue")
658     };
659 
660     char *next;
661     char prompt[NLINE + 1];
662     char tstring[NLINE + 1];	/* string to add */
663     FSM_BLIST *fp = name_to_choices("fcolor");
664     int code;
665     int count = 0;
666     int status;			/* status return code */
667     long value;
668 
669     (void) f;
670     (void) n;
671 
672     *tstring = EOS;
673     status = kbd_string("Color: ", tstring, sizeof(tstring), ' ',
674 			KBD_NORMAL, fsm_complete);
675     if (status != TRUE)
676 	return (status);
677 
678     code = choice_to_code(fp, tstring, strlen(tstring));
679     if (code < 0) {
680 	next = 0;
681 	code = strtol(tstring, &next, 0);
682 	if (code < 0 || code >= NCOLORS)
683 	    return FALSE;
684     }
685 
686     /* ask for setting string */
687     while (count < 3) {
688 	*tstring = EOS;
689 	sprintf(prompt, "Palette value (%s): ", table[count].name);
690 	status = mlreply(prompt, tstring, sizeof(tstring));
691 	if (status != TRUE)
692 	    return (status);
693 	next = 0;
694 	value = strtol(tstring, &next, 0);
695 	if (next == 0 || *next)
696 	    return FALSE;
697 	table[count++].value = value & 255;
698     }
699 
700     RedValue[code] = table[0].value;
701     GreenValue[code] = table[1].value;
702     BlueValue[code] = table[2].value;
703 
704     set_winflags(TRUE, WFHARD | WFCOLR);
705     vile_refresh(FALSE, 1);
706     return (TRUE);
707 }
708 
709 /*
710  * Set default color map palette
711  */
712 int
ResetRGBPalette(int f,int n)713 ResetRGBPalette(int f, int n)
714 {
715     int code;
716     int red, green, blue;
717     int first = f ? n : 0;
718     int last = f ? n + 1 : NCOLORS;
719 
720     for (code = first; code < last && code < NCOLORS; code++) {
721 	red = green = blue = 0;
722 	if (code & 1)
723 	    red = rgb_normal;
724 	if (code & 2)
725 	    green = rgb_normal;
726 	if (code & 4)
727 	    blue = rgb_normal;
728 	if (code & 8) {
729 	    if (red)
730 		red = rgb_bright;
731 	    if (green)
732 		green = rgb_bright;
733 	    if (blue)
734 		blue = rgb_bright;
735 	    if (code == 8) {
736 		red = rgb_gray;
737 		green = rgb_gray;
738 		blue = rgb_gray;
739 	    }
740 	}
741 	RedValue[code] = red;
742 	GreenValue[code] = green;
743 	BlueValue[code] = blue;
744     }
745     set_winflags(TRUE, WFHARD | WFCOLR);
746     vile_refresh(FALSE, 1);
747     return TRUE;
748 }
749 
750 static COLORREF
color_of(int code)751 color_of(int code)
752 {
753     code = ctrans[code & (NCOLORS - 1)];
754 
755     return PALETTERGB(RedValue[code], GreenValue[code], BlueValue[code]);
756 }
757 
758 /*
759  * Translate the attribute into indices into the color table.
760  */
761 static int
attr_to_colors(VIDEO_ATTR attr,int * fcolor,int * bcolor)762 attr_to_colors(VIDEO_ATTR attr, int *fcolor, int *bcolor)
763 {
764     int ninvert = FALSE;	/* normal colors inverted? */
765 
766     *fcolor = nfcolor;
767     *bcolor = nbcolor;
768 
769     attr &= (VASPCOL | VACOLOR | VABOLD | VAITAL | VASEL | VAREV);
770     if (attr) {
771 	ninvert = ((attr & (VASEL | VAREV)) == VASEL
772 		   || (attr & (VASEL | VAREV)) == VAREV);
773 
774 	if (attr & VASPCOL)
775 	    *fcolor = (VCOLORNUM(attr) & (NCOLORS - 1));
776 	else if (attr & VACOLOR)
777 	    *fcolor = ((VCOLORNUM(attr)) & (NCOLORS - 1));
778 
779 	if (ninvert) {
780 	    int temp = *bcolor;
781 	    *bcolor = *fcolor;
782 	    *fcolor = temp;
783 	}
784 	TRACE2(("attr_to_colors(%04x) fg=%2d, bg=%2d %s\n",
785 		attr, *fcolor, *bcolor, ninvert ? "INVERT" : ""));
786     }
787     return ninvert;
788 }
789 
790 static int
fake_color(int current,int nominal)791 fake_color(int current, int nominal)
792 {
793     int x = nominal;
794     if (current == ENUM_FCOLOR) {
795 	int r = GetRValue(nominal) ^ 0x40;
796 	int g = GetGValue(nominal) ^ 0x40;
797 	int b = GetBValue(nominal) ^ 0x40;
798 	x = PALETTERGB(r, g, b);
799     }
800     TRACE2(("fake_color(%d, %#x) = %#x\n", current, nominal, x));
801     nominal = x;
802     return nominal;
803 }
804 
805 /*
806  * Provide an array of inter-character spacing, needed to force bold and italic fonts
807  * to align with the normal font.
808  */
809 static INT *
intercharacter(int cols)810 intercharacter(int cols)
811 {
812     if (cols == 0 || nCharWidth == 0)
813 	return 0;
814     else {
815 	static INT *result;
816 	static unsigned length;
817 
818 	if (++cols >= (int) length) {
819 	    length = 1 + ((cols * 3) / 2);
820 	    safe_typereallocn(INT, result, length);
821 	}
822 	if (result != 0) {
823 	    while (--cols >= 0)
824 		result[cols] = nCharWidth;
825 	}
826 	return result;
827     }
828 }
829 
830 static HFONT
GetMyFont(VIDEO_ATTR attr)831 GetMyFont(VIDEO_ATTR attr)
832 {
833     unsigned n;
834 
835     if ((attr & (VAUL | VABOLD | VAITAL)) != 0) {
836 	for (n = 1; n < TABLESIZE(MyFonts); ++n) {
837 	    if ((attr & MyFonts[n].attr) == MyFonts[n].attr)
838 		break;
839 	}
840     } else {
841 	n = 0;
842     }
843 
844     if (n >= TABLESIZE(MyFonts)) {
845 	return GetMyFont(VAUL);
846     } else if (!MyFonts[n].used) {
847 	LOGFONT logfont = vile_logfont;
848 	logfont.lfItalic = (BYTE) ((attr & VAITAL) != 0);
849 	logfont.lfUnderline = (BYTE) ((attr & VAUL) != 0);
850 	if (attr & VABOLD)
851 	    logfont.lfWeight = FW_SEMIBOLD;
852 	if ((MyFonts[n].font = CreateFontIndirect(&logfont)) != 0) {
853 	    MyFonts[n].used = TRUE;
854 	} else if ((attr & (VABOLD | VAITAL)) == (VABOLD | VAITAL)) {
855 	    return GetMyFont(VABOLD);
856 	} else if ((attr & VAUL) != 0) {
857 	    return GetMyFont(VAUL);
858 	} else {
859 	    n = 0;
860 	}
861     } else if (MyFonts[n].font == 0) {
862 	n = 0;			/* live with previous failure */
863     }
864 
865     return MyFonts[n].font;
866 }
867 
868 static void
SetMyFont(HFONT font,LOGFONT * lf)869 SetMyFont(HFONT font, LOGFONT * lf)
870 {
871     unsigned n;
872 
873     for (n = 0; n < TABLESIZE(MyFonts); ++n) {
874 	if (MyFonts[n].used) {
875 	    DeleteObject(MyFonts[n].font);
876 	    MyFonts[n].used = FALSE;
877 	}
878     }
879     MyFonts[0].font = font;
880     MyFonts[0].used = TRUE;
881     if (lf != 0 && lf != &vile_logfont)
882 	vile_logfont = *lf;
883 }
884 
885 static HDC
get_DC_with_Font(HFONT font)886 get_DC_with_Font(HFONT font)
887 {
888     HDC hDC = GetDC(cur_win->text_hwnd);
889     if (hDC != 0) {
890 	if (SelectFont(hDC, font) == 0)
891 	    ReleaseDC(cur_win->text_hwnd, hDC);
892     }
893     return hDC;
894 }
895 
896 static void
nt_set_colors(HDC hdc,VIDEO_ATTR attr)897 nt_set_colors(HDC hdc, VIDEO_ATTR attr)
898 {
899     int fcolor;
900     int bcolor;
901     int ninvert;
902 
903     SelectFont(hdc, GetMyFont(attr));
904 #ifdef GVAL_VIDEO
905     attr ^= global_g_val(GVAL_VIDEO);
906 #endif
907     ninvert = attr_to_colors(attr, &fcolor, &bcolor);
908 
909     if (fcolor < 0)
910 	fcolor = ninvert
911 	    ? fake_color(fcolor, default_fcolor)
912 	    : fake_color(fcolor, default_bcolor);
913     else
914 	fcolor = color_of(fcolor);
915 
916     if (bcolor < 0)
917 	bcolor = ninvert
918 	    ? fake_color(bcolor, default_bcolor)
919 	    : fake_color(bcolor, default_fcolor);
920     else
921 	bcolor = color_of(bcolor);
922 
923     SetTextColor(hdc, fcolor);
924     SetBkColor(hdc, bcolor);
925 }
926 
927 static int
fhide_cursor(void)928 fhide_cursor(void)
929 {
930     TRACE(("fhide_cursor pos %#x,%#x (visible:%d, exists:%d)\n", ttrow,
931 	   ttcol, caret_visible, caret_exists));
932     if (!ac_active) {
933 	if (caret_visible) {
934 	    HideCaret(cur_win->text_hwnd);
935 	    caret_visible = 0;
936 	}
937 	if (caret_exists) {
938 	    DestroyCaret();
939 	    caret_exists = 0;
940 	}
941     }
942     return 0;
943 }
944 
945 static void
fshow_cursor(void)946 fshow_cursor(void)
947 {
948     int x, y, width;
949     POINT z;
950 
951     if (ac_active		/* reject while repainting autocolor */
952 	|| caret_disabled	/* reject display during font-dialog */
953 	|| ttrow > term.rows	/* reject bogus position in init */
954 	|| ttcol > term.cols)
955 	return;
956 
957     TRACE(("fshow_cursor pos %#x,%#x (visible:%d, exists:%d)\n", ttrow,
958 	   ttcol, caret_visible, caret_exists));
959     x = ColToPixel(ttcol) + 1 - nCursorAdj;
960     y = RowToPixel(ttrow) + 1;
961     if (caret_exists) {
962 	GetCaretPos(&z);
963 	if (x != z.x
964 	    || y != z.y)
965 	    fhide_cursor();
966     }
967 
968     if (!caret_exists) {
969 	width = nCharWidth;	/* assume block cursor */
970 #ifdef UNICODE
971 	/*
972 	 * If the current cell that we are pointing to contains a zero, then
973 	 * that is an extension of a multicolumn character.  Widen the cell to
974 	 * account for it.
975 	 */
976 	if (CELL_TEXT(ttrow, ttcol) == 0) {
977 	    width *= 2;
978 	    x -= (nCharWidth + nCursorAdj);
979 	}
980 #endif
981 #if OPT_ICURSOR
982 	if (icursor) {
983 	    int vertbar = FALSE;
984 
985 	    if (insertmode) {
986 		if (icursor_style == 1)
987 		    vertbar = TRUE;
988 	    } else {		/* command mode */
989 		if (icursor_style == 2)
990 		    vertbar = TRUE;
991 	    }
992 	    if (vertbar) {
993 		if (width < 5)
994 		    width = 2;
995 		else
996 		    width = 3;
997 	    }
998 	}
999 #endif
1000 	TRACE(("...CreateCaret(%d,%d)\n", ttrow, ttcol));
1001 	CreateCaret(cur_win->text_hwnd, (HBITMAP) 0, width, nLineHeight);
1002 	caret_exists = 1;
1003 	SetCaretPos(x, y);
1004 	ShowCaret(cur_win->text_hwnd);
1005 	caret_visible = 1;
1006     }
1007 }
1008 
1009 #if OPT_ICURSOR
1010 static void
ntwinio_icursor(int unused)1011 ntwinio_icursor(int unused)
1012 {
1013     (void) unused;
1014 
1015     if (icursor && caret_visible) {
1016 	fhide_cursor();		/* Kill the old caret        */
1017 	fshow_cursor();		/* And bring it back to life */
1018     }
1019 }
1020 #endif
1021 
1022 static void
get_borders(void)1023 get_borders(void)
1024 {
1025     SystemParametersInfo(SPI_GETWORKAREA, 0, &cur_win->xy_limit, 0);
1026     TRACE(("WORKAREA: %ld,%ld %ld,%ld\n",
1027 	   cur_win->xy_limit.top,
1028 	   cur_win->xy_limit.left,
1029 	   cur_win->xy_limit.right,
1030 	   cur_win->xy_limit.bottom));
1031     cur_win->x_border = GetSystemMetrics(SM_CXSIZEFRAME);
1032     cur_win->y_border = GetSystemMetrics(SM_CYSIZEFRAME);
1033     cur_win->y_titles = GetSystemMetrics(SM_CYCAPTION);
1034 
1035     TRACE(("--X border %d\n", GetSystemMetrics(SM_CXBORDER)));
1036     TRACE(("--X edge   %d\n", GetSystemMetrics(SM_CXEDGE)));
1037     TRACE(("--V scroll %d\n", GetSystemMetrics(SM_CXVSCROLL)));
1038     TRACE(("--H thumb  %d\n", GetSystemMetrics(SM_CXHTHUMB)));
1039 
1040     TRACE(("--Y border %d\n", GetSystemMetrics(SM_CYBORDER)));
1041     TRACE(("--Y edge   %d\n", GetSystemMetrics(SM_CYEDGE)));
1042     TRACE(("--H scroll %d\n", GetSystemMetrics(SM_CYHSCROLL)));
1043     TRACE(("--V thumb  %d\n", GetSystemMetrics(SM_CYVTHUMB)));
1044 
1045     TRACE(("X border: %d, Y border: %d\n", cur_win->x_border, cur_win->y_border));
1046     TRACE(("CYFRAME:   %d\n", GetSystemMetrics(SM_CYFRAME)));
1047     TRACE(("CYCAPTION: %d\n", cur_win->y_titles));
1048 
1049     /*
1050      * See
1051      * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2017
1052      *
1053      * The "1700" is Visual Studio 2012.
1054      *
1055      * This check might be applicable to a 32-bit Windows host, but I've only
1056      * 64-bit Windows 7 and 64-bit Windows 8.1 for testing.  The 32-bit target
1057      * with Visual Studio 2012 honors the APPVER setting, but the 64-bit
1058      * target does not, requiring this adjustment -TD
1059      */
1060 #if (_MSC_VER >= 1700) && defined(_WIN64)
1061     // https://winaero.com/blog/how-to-reduce-window-border-size-in-windows-8-windows-7-and-windows-vista/
1062     {
1063 	HKEY hkey;
1064 	if (RegOpenKeyEx(HKEY_CURRENT_USER,
1065 			 W32_STRING("Control Panel")
1066 			 W32_STRING("\\Desktop")
1067 			 W32_STRING("\\WindowMetrics"),
1068 			 0,
1069 			 KEY_READ,
1070 			 &hkey) == ERROR_SUCCESS) {
1071 	    char buffer[20];
1072 	    if (w32_get_reg_sz(hkey,
1073 			       "PaddedBorderWidth",
1074 			       buffer, sizeof(buffer)) == ERROR_SUCCESS) {
1075 		int padding;
1076 		if (sscanf(buffer, "%d", &padding) == 1 && padding < 0) {
1077 		    padding = (-padding) / 15;
1078 		    cur_win->x_border += padding;
1079 		    cur_win->y_border += padding;
1080 		}
1081 	    }
1082 	    (void) RegCloseKey(hkey);
1083 	}
1084     }
1085 #endif
1086 }
1087 
1088 /*
1089  * Note 1: lpntm is a pointer to a TEXTMETRIC struct if FontType does not
1090  * have TRUETYPE_FONTTYPE set, but we only need the tmPitchAndFamily member,
1091  * which has the same alignment as in NEWTEXTMETRIC.
1092  *
1093  * Note 2: enumerate_fonts() is an instance of FONTENUMPROC.  The parameter
1094  * types specified for this function do not match the windows docu I
1095  * have in my possession (MSDN JAN 1998), but they do match the
1096  * declarations in wingdi.h .  When in doubt, use the source :-) .
1097  */
1098 static int CALLBACK
enumerate_fonts(const LOGFONT * lpelf,const TEXTMETRIC * lpntm,DWORD FontType,LPARAM lParam)1099 enumerate_fonts(
1100 		   const LOGFONT * lpelf,
1101 		   const TEXTMETRIC * lpntm,
1102 		   DWORD FontType,
1103 		   LPARAM lParam)
1104 {
1105     int code = 2;
1106     const LOGFONT *src = lpelf;
1107     LOGFONT *dst = ((LOGFONT *) lParam);
1108 
1109     (void) lpntm;
1110     (void) FontType;
1111 
1112     if ((src->lfPitchAndFamily & 3) != FIXED_PITCH) {
1113 	code = 1;
1114     } else {
1115 	*dst = *src;
1116 	if (src->lfCharSet == ANSI_CHARSET) {
1117 	    code = 0;
1118 	    TRACE(("Found good font:%s\n", lpelf->lfFaceName));
1119 	}
1120 	TRACE(("Found ok font:%s\n", lpelf->lfFaceName));
1121 	TRACE(("Pitch/Family:   %#x\n", dst->lfPitchAndFamily));
1122     }
1123 
1124     return code;
1125 }
1126 
1127 static int
is_fixed_pitch(HFONT font)1128 is_fixed_pitch(HFONT font)
1129 {
1130     BOOL ok;
1131     HDC hDC;
1132     TEXTMETRIC metrics;
1133 
1134     hDC = get_DC_with_Font(font);
1135     ok = GetTextMetrics(hDC, &metrics);
1136     ReleaseDC(cur_win->text_hwnd, hDC);
1137 
1138     if (ok) {
1139 #ifdef UNICODE
1140 	int old_encoding = term.get_enc();
1141 
1142 	/*
1143 	 * FIXME - find how to (simply) determine the total number of glyphs
1144 	 * in a font.
1145 	 */
1146 	if ((metrics.tmFirstChar == 0x20) && (metrics.tmLastChar > 8000)) {
1147 	    term.set_enc(enc_UTF16);
1148 	    TRACE(("Assume font useful for UNICODE\n"));
1149 	} else {
1150 	    term.set_enc(enc_8BIT);	/* FIXME: how to do enc_LOCALE? */
1151 	}
1152 	/* if encoding changes, force recompute in display.c */
1153 	if (old_encoding != term.get_enc())
1154 	    set_winflags(TRUE, WFFORCE | WFHARD);
1155 #endif
1156 	ok = ((metrics.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0);
1157     }
1158 
1159     TRACE(("is_fixed_pitch: %d\n", ok));
1160     TRACE(("Ave Text width: %ld\n", metrics.tmAveCharWidth));
1161     TRACE(("Max Text width: %ld\n", metrics.tmMaxCharWidth));
1162     TRACE(("Pitch/Family:   %#x\n", metrics.tmPitchAndFamily));
1163     TRACE(("First char      %#x\n", metrics.tmFirstChar));
1164     TRACE(("Last char       %#x\n", metrics.tmLastChar));
1165 
1166     return (ok);
1167 }
1168 
1169 static int
new_font(LOGFONT * lf)1170 new_font(LOGFONT * lf)
1171 {
1172     HFONT font = CreateFontIndirect(lf);
1173 
1174     if (font != 0) {
1175 	if (is_fixed_pitch(font)) {
1176 	    SetMyFont(font, lf);
1177 	    TRACE(("created new font\n"));
1178 	    return TRUE;
1179 	} else
1180 	    DeleteObject(font);
1181     }
1182     return FALSE;
1183 }
1184 
1185 static void
get_font(LOGFONT * lf)1186 get_font(LOGFONT * lf)
1187 {
1188     HDC hDC;
1189 
1190     SetMyFont(GetStockObject(MY_FONT), 0);
1191     hDC = GetDC(cur_win->text_hwnd);
1192     if (EnumFontFamilies(hDC, NULL, enumerate_fonts, (LPARAM) lf) <= 0) {
1193 	TRACE(("Creating Pitch/Family: %#x\n", lf->lfPitchAndFamily));
1194 	new_font(lf);
1195     }
1196     ReleaseDC(cur_win->text_hwnd, hDC);
1197 }
1198 
1199 static void
use_font(HFONT my_font)1200 use_font(HFONT my_font)
1201 {
1202     HDC hDC;
1203     TEXTMETRIC textmetric;
1204     int oLineHeight = nLineHeight;
1205     int oCharWidth = nCharWidth;
1206 
1207     TRACE((T_CALLED "use_font %p\n", my_font));
1208 
1209     hDC = get_DC_with_Font(my_font);
1210     GetTextMetrics(hDC, &textmetric);
1211     ReleaseDC(cur_win->text_hwnd, hDC);
1212 
1213     TRACE(("Text height:      %ld\n", textmetric.tmHeight));
1214     TRACE(("Ave Text width:   %ld\n", textmetric.tmAveCharWidth));
1215     TRACE(("Max Text width:   %ld\n", textmetric.tmMaxCharWidth));
1216     TRACE(("Overhang:         %ld\n", textmetric.tmOverhang));
1217     TRACE(("Leading internal: %ld\n", textmetric.tmInternalLeading));
1218     TRACE(("Leading external: %ld\n", textmetric.tmExternalLeading));
1219     TRACE(("Pitch/Family:     %#x\n", textmetric.tmPitchAndFamily));
1220     TRACE(("First char:       %#x\n", textmetric.tmFirstChar));
1221     TRACE(("Last char:        %#x\n", textmetric.tmLastChar));
1222     TRACE(("Default char:     %#x\n", textmetric.tmDefaultChar));
1223 
1224     /*
1225      * We'll use the average text-width, since some fonts (e.g., Courier
1226      * New) have a bogus max text-width.
1227      */
1228     nLineToFill = textmetric.tmExternalLeading;
1229     nLineHeight = textmetric.tmExternalLeading + textmetric.tmHeight;
1230     nCharWidth = textmetric.tmAveCharWidth + textmetric.tmOverhang;
1231     nCursorAdj = 1 + textmetric.tmOverhang;
1232     get_borders();
1233 
1234     font_resize_in_progress = (oLineHeight != nLineHeight)
1235 	|| (oCharWidth != nCharWidth);
1236 
1237     gui_resize(term.cols, term.rows);
1238 
1239     font_resize_in_progress = FALSE;
1240 
1241     returnVoid();
1242 }
1243 
1244 static void
set_font(void)1245 set_font(void)
1246 {
1247     HDC hDC;
1248     CHOOSEFONT choose;
1249 
1250     TRACE((T_CALLED "set_font\n"));
1251     fhide_cursor();
1252     caret_disabled = TRUE;
1253     memset(&choose, 0, sizeof(choose));
1254     choose.lStructSize = sizeof(choose);
1255     choose.hwndOwner = cur_win->text_hwnd;
1256     choose.Flags = CF_SCREENFONTS
1257 	| CF_FIXEDPITCHONLY
1258 	| CF_FORCEFONTEXIST
1259 	| CF_NOSCRIPTSEL
1260 	| CF_INITTOLOGFONTSTRUCT;
1261     choose.lpLogFont = &vile_logfont;
1262 
1263     hDC = get_DC_with_Font(GetMyFont(0));
1264     GetTextFace(hDC, TABLESIZE(vile_logfont.lfFaceName), vile_logfont.lfFaceName);
1265     ReleaseDC(cur_win->text_hwnd, hDC);
1266 
1267     vile_logfont.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
1268     vile_logfont.lfCharSet = ANSI_CHARSET;
1269     TRACE(("LOGFONT Facename:%s\n", vile_logfont.lfFaceName));
1270 
1271     if (ChooseFont(&choose)) {
1272 	TRACE(("ChooseFont '%s'\n", vile_logfont.lfFaceName));
1273 	if (new_font(&vile_logfont)) {
1274 	    int saverow = ttrow;
1275 	    int savecol = ttcol;
1276 	    mlwrite("[Set font to %s]", vile_logfont.lfFaceName);
1277 	    movecursor(saverow, savecol);
1278 	    use_font(GetMyFont(0));
1279 	    vile_refresh(FALSE, 0);
1280 	    update(FALSE);
1281 	} else {
1282 	    mlforce("[Cannot create font]");
1283 	}
1284     } else {
1285 	mlforce("[No font selected]");
1286     }
1287 
1288     caret_disabled = FALSE;
1289     fshow_cursor();
1290     TRACE(("...set_font, LOGFONT Facename:%s\n", vile_logfont.lfFaceName));
1291     returnVoid();
1292 }
1293 
1294 static int
last_w32_error(int use_msg_box)1295 last_w32_error(int use_msg_box)
1296 {
1297     if (use_msg_box)
1298 	disp_win32_error(W32_SYS_ERROR, winvile_hwnd());
1299     else {
1300 	char *msg = NULL;
1301 
1302 	fmt_win32_error(W32_SYS_ERROR, &msg, 0);
1303 	mlforce(msg);
1304 	LocalFree(msg);
1305     }
1306     return (FALSE);
1307 }
1308 
1309 static void
show_ok_message(const char * msgtext)1310 show_ok_message(const char *msgtext)
1311 {
1312     W32_CHAR *msg = w32_charstring(msgtext);
1313     MessageBox(winvile_hwnd(), msg, w32_prognam(), MB_ICONSTOP | MB_OK);
1314     free(msg);
1315 }
1316 
1317 static void
show_font_message(const char * msgtext,int use_mb)1318 show_font_message(const char *msgtext, int use_mb)
1319 {
1320     if (use_mb) {
1321 	show_ok_message(msgtext);
1322     } else {
1323 	mlforce(msgtext);
1324     }
1325 }
1326 
1327 static void
w32_strcpy(W32_CHAR * dst,W32_CHAR * src)1328 w32_strcpy(W32_CHAR * dst, W32_CHAR * src)
1329 {
1330     while (((*dst++) = (*src++)) != 0) {
1331 	;
1332     }
1333 }
1334 
1335 /*
1336  * Set font from string specification.  See the function parse_font_str()
1337  * in file w32misc.c for acceptable font string syntax.
1338  *
1339  * Prerequistes before calling this function:
1340  *
1341  *    winvile's windows and default font created.
1342  *
1343  * Set use_mb (Boolean):
1344  *	T -> errors reported via MessageBox.
1345  *	F -> errors reported via message line.
1346  *
1347  * Returns: T -> all is well, F -> failure.
1348  */
1349 int
ntwinio_font_frm_str(const char * fontstr,int use_mb)1350 ntwinio_font_frm_str(const char *fontstr,
1351 		     int use_mb)
1352 {
1353     int rc = TRUE;
1354     int face_specified = FALSE;
1355     HDC hdc = 0;
1356     HFONT hfont = 0;
1357     HWND hwnd = cur_win->text_hwnd;
1358 
1359     LOGFONT logfont;
1360     FONTSTR_OPTIONS str_rslts;
1361     W32_CHAR font_mapper_face[LF_FACESIZE + 1];
1362     W32_CHAR current_face[sizeof(str_rslts.face)];
1363     TEXTMETRIC metrics;
1364 
1365     TRACE((T_CALLED "ntwinio_font_frm_str(%s)\n", fontstr));
1366 
1367     if (!parse_font_str(fontstr, &str_rslts)) {
1368 	show_font_message("Font syntax invalid", use_mb);
1369 	rc = FALSE;
1370     } else if ((hdc = get_DC_with_Font(GetMyFont(0))) == 0) {
1371 	(void) last_w32_error(use_mb);
1372 	rc = FALSE;
1373     } else if ((face_specified = (str_rslts.face[0] != '\0')) != FALSE) {
1374 	W32_CHAR *temp = w32_charstring(str_rslts.face);
1375 	w32_strcpy(current_face, temp);
1376 	free(temp);
1377     } else {
1378 	char *result_face = 0;
1379 
1380 	/* user didn't specify a face name, get current name. */
1381 
1382 	if ((GetTextFace(hdc, TABLESIZE(current_face), current_face)) == 0) {
1383 	    (void) last_w32_error(use_mb);
1384 	    rc = FALSE;
1385 	} else if ((result_face = asc_charstring(current_face)) == 0) {
1386 	    rc = FALSE;
1387 	} else {
1388 	    strcpy(str_rslts.face, result_face);
1389 	    free(result_face);
1390 	}
1391     }
1392 
1393     if (rc) {
1394 	/* Build up LOGFONT data structure. */
1395 	memset(&logfont, 0, sizeof(logfont));
1396 	logfont.lfWeight = (str_rslts.bold) ? FW_BOLD : FW_NORMAL;
1397 	logfont.lfHeight = -MulDiv(str_rslts.size,
1398 				   GetDeviceCaps(hdc, LOGPIXELSY),
1399 				   72);
1400 	if (str_rslts.italic)
1401 	    logfont.lfItalic = TRUE;
1402 	logfont.lfCharSet = DEFAULT_CHARSET;
1403 	logfont.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
1404 	w32_strcpy(logfont.lfFaceName, current_face);
1405 
1406 	if (!((hfont = CreateFontIndirect(&logfont)) != 0 && SelectFont(hdc, hfont))) {
1407 	    (void) last_w32_error(use_mb);
1408 	    rc = FALSE;
1409 	} else if (face_specified) {
1410 	    /*
1411 	     * The font mapper will substitute some other font for a font
1412 	     * request that cannot exactly be met.  Check first to see if the
1413 	     * face name matches the user chosen face (if applicable).
1414 	     */
1415 	    char *mapper_face = 0;
1416 
1417 	    if ((GetTextFace(hdc,
1418 			     TABLESIZE(font_mapper_face),
1419 			     font_mapper_face)) == 0) {
1420 		(void) last_w32_error(use_mb);
1421 		rc = FALSE;
1422 	    } else if ((mapper_face = asc_charstring(font_mapper_face)) == 0) {
1423 		rc = FALSE;
1424 	    } else if (stricmp(mapper_face, str_rslts.face) != 0) {
1425 		show_font_message("Font face unknown or size/style unsupported", use_mb);
1426 		rc = FALSE;
1427 	    }
1428 
1429 	    if (mapper_face != 0)
1430 		free(mapper_face);
1431 	}
1432     }
1433 
1434     if (rc) {
1435 	/* Next, font must be fixed pitch. */
1436 	if (!GetTextMetrics(hdc, &metrics)) {
1437 	    (void) last_w32_error(use_mb);
1438 	    rc = FALSE;
1439 	} else if ((metrics.tmPitchAndFamily & TMPF_FIXED_PITCH) != 0) {
1440 	    /* Misnamed constant! */
1441 
1442 	    show_font_message("Font not fixed pitch", use_mb);
1443 	    rc = FALSE;
1444 	}
1445     }
1446 
1447     if (hdc != 0) {
1448 	ReleaseDC(hwnd, hdc);	/* finally done with this */
1449     }
1450     if (rc) {
1451 	SetMyFont(hfont, &logfont);
1452 	use_font(GetMyFont(0));
1453 	vile_refresh(FALSE, 0);
1454     } else {
1455 	if (hfont)
1456 	    DeleteObject(hfont);
1457     }
1458 
1459     returnCode(rc);
1460 }
1461 
1462 char *
ntwinio_current_font(void)1463 ntwinio_current_font(void)
1464 {
1465     static char *buf;
1466     HDC hdc;
1467     LONG size;
1468     char *style;
1469     char *facename;
1470 
1471     if (!buf) {
1472 	buf = castalloc(char,
1473 			sizeof("bold-italic") +
1474 			LF_FACESIZE +
1475 			16);	/* space for delimiters and point size */
1476 	if (!buf)
1477 	    return (out_of_mem);
1478     }
1479     if ((hdc = get_DC_with_Font(GetMyFont(0))) == 0) {
1480 	char *msg = NULL;
1481 	fmt_win32_error(W32_SYS_ERROR, &msg, 0);
1482 	return (msg);
1483 	/* "msg" leaks here, but this code path should never be taken. */
1484     }
1485 
1486     if (vile_logfont.lfWeight == FW_BOLD && vile_logfont.lfItalic)
1487 	style = "bold-italic";
1488     else if (vile_logfont.lfWeight == FW_BOLD)
1489 	style = "bold";
1490     else if (vile_logfont.lfItalic)
1491 	style = "italic";
1492     else
1493 	style = NULL;
1494 
1495     size = MulDiv(labs(vile_logfont.lfHeight),
1496 		  72,
1497 		  GetDeviceCaps(hdc, LOGPIXELSY));
1498     facename = asc_charstring(vile_logfont.lfFaceName);
1499     sprintf(buf,
1500 	    "%s,%ld%s%s",
1501 	    (facename) ? facename : "?",
1502 	    size,
1503 	    (style) ? "," : "",
1504 	    (style) ? style : "");
1505     free(facename);
1506     return (buf);
1507 }
1508 
1509 static void
set_window_text(HWND handle,const char * text)1510 set_window_text(HWND handle, const char *text)
1511 {
1512     W32_CHAR *actual = w32_charstring(text);
1513     SetWindowText(handle, actual);
1514     free(actual);
1515 }
1516 
1517 #if OPT_TITLE
1518 static void
ntwinio_title(const char * title)1519 ntwinio_title(const char *title)
1520 {				/* set the current window title */
1521     if (title != 0)
1522 	set_window_text(winvile_hwnd(), title);
1523 }
1524 #endif
1525 
1526 static int
get_keyboard_state(void)1527 get_keyboard_state(void)
1528 {
1529     int result = 0;
1530 
1531     if (GetKeyState(VK_CONTROL) < 0)
1532 	result |= (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED);
1533     if (GetKeyState(VK_MENU) < 0)
1534 	result |= (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED);
1535     if (GetKeyState(VK_SHIFT) < 0)
1536 	result |= SHIFT_PRESSED;
1537     TRACE(("get_keyboard_state: %#x\n", result));
1538     return result;
1539 }
1540 
1541 #define KEY_FIFO struct key_fifo
1542 KEY_FIFO {
1543     KEY_FIFO *link;
1544     MSG data;
1545     DWORD state;
1546     long seqs;
1547 };
1548 
1549 static KEY_FIFO *key_fifo_head;
1550 static KEY_FIFO *key_fifo_tail;
1551 static long key_fifo_seqs;
1552 
1553 #if OPT_TRACE
1554 static int
fifo_size(void)1555 fifo_size(void)
1556 {
1557     KEY_FIFO *p = key_fifo_head;
1558     int result = 0;
1559     while (p != 0) {
1560 	++result;
1561 	p = p->link;
1562     }
1563     return result;
1564 }
1565 
1566 static char *
keyboard_state2s(int state)1567 keyboard_state2s(int state)
1568 {
1569     static char result[80];
1570 
1571     result[0] = 0;
1572     if (state & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
1573 	strcat(result, " ctrl");
1574     if (state & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
1575 	strcat(result, " alt");
1576     if (state & (SHIFT_PRESSED))
1577 	strcat(result, " shift");
1578     return result;
1579 }
1580 #endif
1581 
1582 static void
save_key_data(MSG * msg)1583 save_key_data(MSG * msg)
1584 {
1585     KEY_FIFO *entry = typealloc(KEY_FIFO);
1586 
1587     if (entry != 0) {
1588 	if (key_fifo_tail != 0)
1589 	    key_fifo_tail->link = entry;
1590 
1591 	entry->link = 0;
1592 	entry->data = *msg;
1593 	entry->state = get_keyboard_state();
1594 	entry->seqs = ++key_fifo_seqs;
1595 
1596 	if (key_fifo_head == 0)
1597 	    key_fifo_head = entry;
1598 
1599 	key_fifo_tail = entry;
1600 	TRACE(("FIFO%ld %d: save_key_data(%s) %p %s\n",
1601 	       entry->seqs,
1602 	       fifo_size(),
1603 	       message2s(msg->message),
1604 	       (void *) msg->wParam,
1605 	       keyboard_state2s(entry->state)));
1606     }
1607 }
1608 
1609 static int
restore_key_data(MSG * msg,DWORD * state)1610 restore_key_data(MSG * msg, DWORD * state)
1611 {
1612     int result = FALSE;
1613     KEY_FIFO *entry = key_fifo_head;
1614 
1615     if (entry != 0) {
1616 
1617 	if (msg != 0) {
1618 	    *msg = entry->data;
1619 	    *state = entry->state;
1620 
1621 	    TRACE(("FIFO%ld %d: restore_key_data(%s) %p %s\n",
1622 		   entry->seqs,
1623 		   fifo_size(),
1624 		   message2s(msg->message),
1625 		   (void *) msg->wParam,
1626 		   keyboard_state2s(*state)));
1627 
1628 	    key_fifo_head = entry->link;
1629 	    if (key_fifo_head == 0)
1630 		key_fifo_tail = 0;
1631 
1632 	    free(entry);
1633 	}
1634 
1635 	result = TRUE;
1636     }
1637     return result;
1638 }
1639 
1640 static void
nibble_queue(MSG * msg)1641 nibble_queue(MSG * msg)
1642 {
1643     TRACE((T_CALLED "nibble_queue %s\n", message2s(msg->message)));
1644     if (GetMessage(msg, (HWND) 0, 0, 0) != TRUE) {
1645 	PostQuitMessage(1);
1646 	quit(TRUE, 1);
1647     } else {
1648 	DispatchMessage(msg);
1649     }
1650     returnVoid();
1651 }
1652 
1653 static int
peek_at_queue(MSG * msg)1654 peek_at_queue(MSG * msg)
1655 {
1656     int rc = 0;
1657 
1658     if (PeekMessage(msg, (HWND) 0, 0, 0, PM_NOREMOVE)) {
1659 	if (msg->message != WM_SYSTIMER) {
1660 	    rc = 1;		/* A meaningful event--process it. */
1661 	} else {
1662 	    /*
1663 	     * This is the undocumented (as near as I can tell)
1664 	     * WM_SYSTIMER event that seems to occur whenever the caret
1665 	     * blinks.  This is a background "noise" event that can
1666 	     * be ignored once dispatched.
1667 	     */
1668 	    nibble_queue(msg);
1669 	}
1670     }
1671 
1672     return rc;
1673 }
1674 
1675 /*
1676  * Check if the message refers to a keyboard event.
1677  */
1678 static int
is_keyboard_message(MSG * msg)1679 is_keyboard_message(MSG * msg)
1680 {
1681     int rc = 0;
1682 
1683     switch (msg->message) {
1684     case WM_KEYDOWN:
1685     case WM_KEYUP:
1686     case WM_SYSKEYDOWN:
1687     case WM_SYSKEYUP:
1688     case WM_CHAR:
1689 	rc = 1;
1690 	break;
1691     }
1692     return rc;
1693 }
1694 
1695 /*
1696  * We cannot handle _all_ events in scflush(), since some must be handled,
1697  * e.g., in ntwinio_getch().  In particular, do not try to handle WM__CHAR
1698  * since that interferes with the OLE server wvwrap.exe's sending characters to
1699  * this process.
1700  */
1701 static int
is_drawing_message(MSG * msg)1702 is_drawing_message(MSG * msg)
1703 {
1704     int rc = 0;
1705 
1706     switch (msg->message) {
1707     case WM_KEYDOWN:
1708     case WM_SYSKEYDOWN:
1709     case WM_CHAR:
1710 	// TRACE(("NOT drawing_message:%s(%#x)\n", message2s(msg->message), msg->wParam));
1711 	break;
1712     default:
1713 	rc = 1;
1714 	break;
1715     }
1716     return rc;
1717 }
1718 
1719 static void
really_draw_text(HDC hdc,VIDEO_TEXT * text,VIDEO_ATTR attr,int length,int my_crow,int my_ccol)1720 really_draw_text(HDC hdc,
1721 		 VIDEO_TEXT * text,
1722 		 VIDEO_ATTR attr,
1723 		 int length,
1724 		 int my_crow,
1725 		 int my_ccol)
1726 {
1727     TRACE(("Draw [%3d,%3d]%s\n",
1728 	   my_crow, my_ccol, visible_video_text(text, length)));
1729 
1730     nt_set_colors(hdc, attr);
1731 
1732     ExtTextOut(hdc,
1733 	       ColToPixel(my_ccol),
1734 	       RowToPixel(my_crow),
1735 	       0,
1736 	       (RECT *) 0,
1737 	       (VIDEO_CHAR *) text, length,
1738 	       intercharacter(length));
1739 
1740     if (nLineToFill > 0) {
1741 	HBRUSH brush;
1742 	RECT rect;
1743 
1744 	rect.left = ColToPixel(my_ccol);
1745 	rect.top = RowToPixel(my_crow + 1) - nLineToFill;
1746 	rect.right = ColToPixel(my_ccol + length);
1747 	rect.bottom = RowToPixel(my_crow + 1);
1748 
1749 	brush = Background(hdc);
1750 	FillRect(hdc, &rect, brush);
1751 	DeleteObject(brush);
1752     }
1753 }
1754 
1755 static void
scflush(void)1756 scflush(void)
1757 {
1758     if (cur_pos && !vile_resizing) {
1759 	HDC hdc;
1760 	MSG msg;
1761 
1762 	/*
1763 	 * (try to) keep up with events for repainting the screen, e.g., when
1764 	 * reading into [Output].
1765 	 */
1766 	if (!vile_selecting && !(cur_atr & VAREV)) {
1767 	    int save_caret = caret_visible;
1768 	    while (peek_at_queue(&msg)) {
1769 		if (is_keyboard_message(&msg)) {
1770 		    if (GetMessage(&msg, (HWND) 0, 0, 0) == TRUE) {
1771 			switch (msg.message) {
1772 			case WM_KEYDOWN:
1773 			case WM_KEYUP:
1774 			case WM_SYSKEYDOWN:
1775 			case WM_SYSKEYUP:
1776 			    if (TranslateAccelerator(cur_win->main_hwnd,
1777 						     hAccTable, &msg)) {
1778 				TRACE(("GETC:no accelerator\n"));
1779 				continue;
1780 			    }
1781 			    TranslateMessage(&msg);
1782 			    break;
1783 			case WM_CHAR:
1784 			    save_key_data(&msg);
1785 			    break;
1786 			}
1787 		    }
1788 		} else if (is_drawing_message(&msg)) {
1789 		    fhide_cursor();
1790 		    nibble_queue(&msg);
1791 		}
1792 	    }
1793 	    if (save_caret && !caret_visible)
1794 		fshow_cursor();
1795 	}
1796 
1797 	TRACE2(("PUTC:flush %2d\n", cur_pos));
1798 
1799 	hdc = GetDC(cur_win->text_hwnd);
1800 	really_draw_text(hdc,
1801 			 &CELL_TEXT(crow, ccol),
1802 			 cur_atr,
1803 			 cur_pos,
1804 			 crow,
1805 			 ccol);
1806 	ReleaseDC(cur_win->text_hwnd, hdc);
1807     }
1808     ccol = ccol + cur_pos;
1809     cur_pos = 0;
1810 }
1811 
1812 #if OPT_COLOR
1813 static void
ntwinio_fcol(int color)1814 ntwinio_fcol(int color)
1815 {				/* set the current output color */
1816     scflush();
1817     nfcolor = color;
1818 }
1819 
1820 static void
ntwinio_bcol(int color)1821 ntwinio_bcol(int color)
1822 {				/* set the current background color */
1823     scflush();
1824     nbcolor = color;
1825 }
1826 #endif
1827 
1828 static void
ntwinio_flush(void)1829 ntwinio_flush(void)
1830 {
1831     scflush();
1832     SetCaretPos(ColToPixel(ccol), RowToPixel(crow));
1833 }
1834 
1835 static void
ntwinio_move(int row,int col)1836 ntwinio_move(int row, int col)
1837 {
1838     scflush();
1839     crow = (short) row;
1840     ccol = (short) col;
1841 }
1842 
1843 /* erase to the end of the line */
1844 static void
ntwinio_eeol(void)1845 ntwinio_eeol(void)
1846 {
1847     HDC hDC;
1848     HBRUSH brush;
1849     RECT rect;
1850     int x;
1851 
1852     scflush();
1853 
1854     TRACE(("NTEEOL %d,%d, atr %#x\n", crow, ccol, cur_atr));
1855     for (x = ccol; x < term.cols; x++) {
1856 	CELL_TEXT(crow, x) = ' ';
1857 	CELL_ATTR(crow, x) = cur_atr;
1858     }
1859 
1860     GetClientRect(cur_win->text_hwnd, &rect);
1861     rect.left = ColToPixel(ccol);
1862     rect.top = RowToPixel(crow);
1863     rect.right = ColToPixel(term.cols);
1864     rect.bottom = RowToPixel(crow + 1);
1865 
1866     hDC = GetDC(cur_win->text_hwnd);
1867     nt_set_colors(hDC, cur_atr);
1868     brush = Background(hDC);
1869     FillRect(hDC, &rect, brush);
1870     DeleteObject(brush);
1871     ReleaseDC(cur_win->text_hwnd, hDC);
1872 }
1873 
1874 #if	OPT_FLASH
1875 static void
flash_display(void)1876 flash_display(void)
1877 {
1878     PostMessage(cur_win->text_hwnd, WM_WVILE_FLASH_START, 0, 0);
1879     PostMessage(cur_win->text_hwnd, WM_WVILE_FLASH_STOP, 0, 0);
1880 }
1881 #endif
1882 
1883 static void
ntwinio_beep(void)1884 ntwinio_beep(void)
1885 {
1886 #if	OPT_FLASH
1887     if (global_g_val(GMDFLASH)) {
1888 	flash_display();
1889 	return;
1890     }
1891 #endif
1892     MessageBeep(0xffffffff);
1893 }
1894 
1895 /*
1896  * Move 'n' lines starting at 'from' to 'to'
1897  *
1898  * OPT_PRETTIER_SCROLL is prettier but slower -- it scrolls a line at a time
1899  *	instead of all at once.
1900  */
1901 
1902 /* move howmany lines starting at from to to */
1903 static void
ntwinio_scroll(int from,int to,int n)1904 ntwinio_scroll(int from, int to, int n)
1905 {
1906     HDC hDC;
1907     HBRUSH brush;
1908     RECT region;
1909     RECT tofill;
1910 
1911     scflush();
1912     if (to == from)
1913 	return;
1914 #if OPT_PRETTIER_SCROLL
1915     if (ABS(from - to) > 1) {
1916 	ntwinio_scroll(from, (from < to) ? to - 1 : to + 1, n);
1917 	if (from < to)
1918 	    from = to - 1;
1919 	else
1920 	    from = to + 1;
1921     }
1922 #endif
1923 
1924     region.left = 0;
1925     region.right = (SHORT) ColToPixel(term.cols);
1926 
1927     if (from > to) {
1928 	region.top = (SHORT) RowToPixel(to);
1929 	region.bottom = (SHORT) RowToPixel(from + n);
1930     } else {
1931 	region.top = (SHORT) RowToPixel(from);
1932 	region.bottom = (SHORT) RowToPixel(to + n);
1933     }
1934 
1935     TRACE(("ScrollWindowEx from=%d, to=%d, n=%d  (%ld,%ld)/(%ld,%ld)\n",
1936 	   from, to, n,
1937 	   region.left, region.top,
1938 	   region.right, region.bottom));
1939 
1940     ScrollWindowEx(
1941 		      cur_win->text_hwnd,	/* handle of window to scroll */
1942 		      0,	/* amount of horizontal scrolling */
1943 		      RowToPixel(to - from),	/* amount of vertical scrolling */
1944 		      &region,	/* address of structure with scroll rectangle */
1945 		      &region,	/* address of structure with clip rectangle */
1946 		      NULL,	/* handle of update region */
1947 		      &tofill,	/* invalidated rectangle */
1948 		      0		/* scrolling flags */
1949 	);
1950 
1951     /* Erase invalidated rectangle */
1952     TRACE(("ntwinio_scroll tofill: (%ld,%ld)/(%ld,%ld)\n",
1953 	   tofill.left, tofill.top,
1954 	   tofill.right, tofill.bottom));
1955     hDC = GetDC(cur_win->text_hwnd);
1956     nt_set_colors(hDC, cur_atr);
1957     brush = Background(hDC);
1958     FillRect(hDC, &tofill, brush);
1959     DeleteObject(brush);
1960     ReleaseDC(cur_win->text_hwnd, hDC);
1961 
1962     /*
1963      * force text_hwnd's WindProc to service a WM_PAINT message -- gives
1964      * faster screen updates.
1965      */
1966     UpdateWindow(cur_win->text_hwnd);
1967 }
1968 
1969 /*
1970  * vile very rarely generates any of the ASCII printing control characters
1971  * except for a few hand coded routines but we have to support them anyway.
1972  */
1973 
1974 /* put a character at the current position in the current colors */
1975 static void
ntwinio_putc(int ch)1976 ntwinio_putc(int ch)
1977 {
1978     /* This is an optimization for the most common case. */
1979     if (ch >= ' ') {
1980 	CELL_TEXT(crow, ccol + cur_pos) = (VIDEO_TEXT) ch;
1981 	CELL_ATTR(crow, ccol + cur_pos) = cur_atr;
1982 	cur_pos++;
1983     } else {
1984 
1985 	switch (ch) {
1986 
1987 	case '\b':
1988 	    scflush();
1989 	    if (ccol)
1990 		ccol--;
1991 	    break;
1992 
1993 	case '\a':
1994 	    ntwinio_beep();
1995 	    break;
1996 
1997 	case '\t':
1998 	    scflush();
1999 	    do {
2000 		CELL_TEXT(crow, ccol + cur_pos) = ' ';
2001 		CELL_ATTR(crow, ccol + cur_pos) = cur_atr;
2002 		cur_pos++;
2003 	    } while ((ccol + cur_pos) % 8 != 0);
2004 	    break;
2005 
2006 	case '\r':
2007 	    scflush();
2008 	    ccol = 0;
2009 	    break;
2010 
2011 	case '\n':
2012 	    scflush();
2013 	    if (crow < term.rows - 1)
2014 		crow++;
2015 	    else
2016 		ntwinio_scroll(1, 0, term.rows - 1);
2017 	    break;
2018 
2019 	default:
2020 	    CELL_TEXT(crow, ccol + cur_pos) = (VIDEO_TEXT) ch;
2021 	    CELL_ATTR(crow, ccol + cur_pos) = cur_atr;
2022 	    cur_pos++;
2023 	    break;
2024 	}
2025     }
2026 }
2027 
2028 static void
ntwinio_eeop(void)2029 ntwinio_eeop(void)
2030 {
2031     HDC hDC;
2032     HBRUSH brush;
2033     RECT rect;
2034     int y, x;
2035 
2036     scflush();
2037 
2038     TRACE(("NTEEOP %d,%d, atr %#x\n", crow, ccol, cur_atr));
2039     for (y = crow; y < term.rows; y++) {
2040 	for (x = 0; x < term.cols; x++) {
2041 	    CELL_TEXT(y, x) = ' ';
2042 	    CELL_ATTR(y, x) = cur_atr;
2043 	}
2044     }
2045 
2046     rect.left = ColToPixel(ccol);
2047     rect.top = RowToPixel(crow);
2048     rect.right = ColToPixel(term.cols);
2049     rect.bottom = RowToPixel(term.rows);
2050 
2051     if (!vile_resizing) {
2052 	hDC = GetDC(cur_win->text_hwnd);
2053 	nt_set_colors(hDC, cur_atr);
2054 	brush = Background(hDC);
2055 	FillRect(hDC, &rect, brush);
2056 	DeleteObject(brush);
2057 	ReleaseDC(cur_win->text_hwnd, hDC);
2058     }
2059 }
2060 
2061 static void
ntwinio_rev(UINT reverse)2062 ntwinio_rev(UINT reverse)
2063 {				/* change reverse video state */
2064     scflush();
2065     cur_atr = (VIDEO_ATTR) reverse;
2066 }
2067 
2068 static int
ntwinio_cres(const char * res)2069 ntwinio_cres(const char *res)
2070 {				/* change screen resolution */
2071     (void) res;
2072 
2073     scflush();
2074     return 0;
2075 }
2076 
2077 static void
ntwinio_set_encoding(ENC_CHOICES code)2078 ntwinio_set_encoding(ENC_CHOICES code)
2079 {
2080     my_encoding = code;
2081 }
2082 
2083 static ENC_CHOICES
ntwinio_get_encoding(void)2084 ntwinio_get_encoding(void)
2085 {
2086     return my_encoding;
2087 }
2088 
2089 static void
ntwinio_open(void)2090 ntwinio_open(void)
2091 {
2092     static int already_open = FALSE;
2093 
2094     TRACE(("ntwinio_open\n"));
2095 
2096     if (!already_open) {
2097 	already_open = TRUE;
2098 
2099 	set_colors(NCOLORS);
2100 	set_palette(initpalettestr);
2101 	ResetRGBPalette(FALSE, 1);
2102     }
2103 }
2104 
2105 static void
ntwinio_close(void)2106 ntwinio_close(void)
2107 {
2108     TRACE(("ntwinio_close\n"));
2109 
2110     scflush();
2111     ntwinio_move(term.rows - 1, 0);
2112     ntwinio_eeol();
2113     ntwinio_flush();
2114 }
2115 
2116 static void
ntwinio_kopen(void)2117 ntwinio_kopen(void)
2118 {				/* open the keyboard */
2119 }
2120 
2121 static void
ntwinio_kclose(void)2122 ntwinio_kclose(void)
2123 {				/* close the keyboard */
2124 }
2125 
2126 #define isModified(state) (state & \
2127 		(LEFT_CTRL_PRESSED \
2128 		 | RIGHT_CTRL_PRESSED \
2129 		 | LEFT_ALT_PRESSED \
2130 		 | RIGHT_ALT_PRESSED \
2131 		 | SHIFT_PRESSED))
2132 
2133 static int
modified_key(int key,DWORD state)2134 modified_key(int key, DWORD state)
2135 {
2136     key |= mod_KEY;
2137     if (state & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
2138 	key |= mod_CTRL;
2139     if (state & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
2140 	key |= mod_ALT;
2141     if (state & SHIFT_PRESSED)
2142 	key |= mod_SHIFT;
2143 
2144     return key;
2145 }
2146 
2147 /*
2148  * The so-called extended keys include the function keys, non-digit keys on
2149  * the numeric keypad, the cursor-keypad and editing keypad.  The treatment of
2150  * the tab key is inconsistent:  We can get a tab with control modifier, but a
2151  * shift-tab will still yield a WM_CHAR later.
2152  */
2153 static struct keyxlate_struct {
2154     int windows;
2155     int vile;
2156 } keyxlate[] = {
2157     /* *INDENT-OFF* */
2158     { VK_NEXT,		KEY_Next },
2159     { VK_PRIOR,		KEY_Prior },
2160     { VK_END,		KEY_End },
2161     { VK_HOME,		KEY_Home },
2162     { VK_LEFT,		KEY_Left },
2163     { VK_RIGHT,		KEY_Right },
2164     { VK_UP,		KEY_Up },
2165     { VK_DOWN,		KEY_Down },
2166     { VK_INSERT,	KEY_Insert },
2167     { VK_DELETE,	KEY_Delete },
2168     { VK_HELP,		KEY_Help },
2169     { VK_SELECT,	KEY_Select },
2170 #if 0
2171     /* Merely pressing the Alt key generates a VK_MENU key event. */
2172     { VK_MENU,		KEY_Menu },
2173 #endif
2174     { VK_F1,		KEY_F1 },
2175     { VK_F2,		KEY_F2 },
2176     { VK_F3,		KEY_F3 },
2177     { VK_F4,		KEY_F4 },
2178     { VK_F5,		KEY_F5 },
2179     { VK_F6,		KEY_F6 },
2180     { VK_F7,		KEY_F7 },
2181     { VK_F8,		KEY_F8 },
2182     { VK_F9,		KEY_F9 },
2183     { VK_F10,		KEY_F10 },
2184     { VK_F11,		KEY_F11 },
2185     { VK_F12,		KEY_F12 },
2186     { VK_F13,		KEY_F13 },
2187     { VK_F14,		KEY_F14 },
2188     { VK_F15,		KEY_F15 },
2189     { VK_F16,		KEY_F16 },
2190     { VK_F17,		KEY_F17 },
2191     { VK_F18,		KEY_F18 },
2192     { VK_F19,		KEY_F19 },
2193     { VK_F20,		KEY_F20 },
2194     { VK_F21,		KEY_F21 },
2195     { VK_F22,		KEY_F22 },
2196     { VK_F23,		KEY_F23 },
2197     { VK_F24,		KEY_F24 },
2198     /* winuser.h stops with VK_F24 */
2199     /* Allow ^-6 to invoke the alternate-buffer command, a la Unix.  */
2200     { '6',		'6' },
2201     /* Support recognition of ^@ */
2202     { '2',		'2' },
2203     /* *INDENT-ON* */
2204 
2205 };
2206 
2207 static int
decode_key_event(KEY_EVENT_RECORD * irp)2208 decode_key_event(KEY_EVENT_RECORD * irp)
2209 {
2210     int i, key;
2211     struct keyxlate_struct *keyp;
2212 
2213     if ((key = (UCHAR) irp->uChar.AsciiChar) != 0)
2214 	return key;
2215 
2216 #ifdef VILE_OLE
2217     if (redirect_keys &&
2218 	oleauto_redirected_key(irp->wVirtualKeyCode, irp->dwControlKeyState)) {
2219 	return (KYREDIR);	/* Key sent to another window. */
2220     }
2221 #endif
2222 
2223     key = NOKYMAP;
2224     for (i = 0, keyp = keyxlate; i < (int) TABLESIZE(keyxlate); i++, keyp++) {
2225 	if (keyp->windows == irp->wVirtualKeyCode) {
2226 	    DWORD state = irp->dwControlKeyState;
2227 
2228 	    /*
2229 	     * There are a few special keys that we must deal with here on an
2230 	     * ad hoc basis:
2231 	     *
2232 	     * ALT+F4 - This should _never_ be remapped by any user (nor
2233 	     * messed with by vile).
2234 	     *
2235 	     * SHIFT+6 - This is actually '^^' -- leave it alone.
2236 	     */
2237 	    if ((keyp->windows == VK_F4
2238 		 && (state & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)))
2239 		|| (keyp->windows == '6' && (state & SHIFT_PRESSED))) {
2240 		TRACE(("decode_key_event - special\n"));
2241 		break;
2242 	    }
2243 
2244 	    /*
2245 	     * Add the modifiers that we recognize.  Specifically, we don't
2246 	     * care about ENHANCED_KEY, since we already have portable
2247 	     * pageup/pagedown and arrow key bindings that would be lost if we
2248 	     * used the Win32-only definition.
2249 	     */
2250 	    if (isModified(state)) {
2251 		key = modified_key(keyp->vile, state);
2252 		if (keyxlate[i].vile == '2') {
2253 		    if ((key & mod_CTRL) && ((key & mod_ALT) == 0)) {
2254 			/* either ^2 or ^@, => nul char */
2255 
2256 			key = 0;
2257 		    } else if ((key & mod_SHIFT) &&
2258 			       ((key & (mod_ALT | mod_CTRL)) == 0)) {
2259 			/*
2260 			 * this will be mapped to '@' if we let Windows do
2261 			 * the translation
2262 			 */
2263 
2264 			key = NOKYMAP;
2265 		    }
2266 		}
2267 	    } else {
2268 		key = keyp->vile;
2269 	    }
2270 	    TRACE(("decode_key_event %#x (%#lx) -> %#x\n",
2271 		   irp->wVirtualKeyCode, state, key));
2272 	    break;
2273 	}
2274     }
2275 
2276     return key;
2277 }
2278 
2279 static void
enable_popup_menu(void)2280 enable_popup_menu(void)
2281 {
2282     enable_popup = !enable_popup;
2283     set_global_g_val(GMDPOPUPMENU, enable_popup);
2284     CheckMenuItem(vile_menu,
2285 		  IDM_MENU,
2286 		  MF_BYCOMMAND | (enable_popup ? MF_CHECKED : MF_UNCHECKED));
2287     DrawMenuBar(cur_win->main_hwnd);
2288 }
2289 
2290 typedef struct popup_menu_info_struct {
2291     int menu_id;
2292     char *menu_name;
2293     char *vile_cmdname;
2294 
2295 } POPUP_MENU_INFO;
2296 
2297 static POPUP_MENU_INFO popup_menu_tbl[] =
2298 {
2299     /* *INDENT-OFF* */
2300     {IDM_OPEN,		"&Open...",	"winopen"},
2301     {IDM_SAVE_AS,	"&Save As...",	"winsave"},
2302     {IDM_CHDIR,		"C&D...",	"wincd"},
2303     {IDM_UNDO,		"&Undo",	"undo-changes-backward"},
2304     {IDM_REDO,		"&Redo",	"redo-changes-forward"},
2305     {IDM_CUT,		"Cu&t",		"cut-to-clipboard"},
2306     {IDM_COPY,		"&Copy",	"copy-unnamed-reg-to-clipboard"},
2307     {IDM_PASTE,		"&Paste",	"paste-from-clipboard"},
2308     {IDM_DELETE,	"De&lete",	"delete-text-selection"},
2309     {IDM_SELECT_ALL,	"Select &All",	"select-all"},
2310     /* *INDENT-ON* */
2311 
2312 };
2313 
2314 static void
invoke_popup_menu(MSG msg)2315 invoke_popup_menu(MSG msg)
2316 {
2317     W32_CHAR *buf2;
2318     char accel[NSTRING], buf[NSTRING * 2];
2319     AREGION ar;
2320     BUFFER *bp;
2321     const CMDFUNC *cmd;
2322     int i, limit, flag, seltxt, samebuf;
2323     POINT point;
2324     POPUP_MENU_INFO *ptbl;
2325 
2326     TRACE(("invoke_popup_menu\n"));
2327     if (popup_menu == NULL) {
2328 	popup_menu = LoadMenu(vile_hinstance,
2329 			      W32_STRING("WinvilePopMenu"));
2330 	popup_menu = GetSubMenu(popup_menu, 0);
2331     }
2332     CheckMenuItem(popup_menu, IDM_MENU, MF_BYCOMMAND | MF_CHECKED);
2333 
2334     /* add accelerators (key bindings) to RMB */
2335     limit = sizeof(popup_menu_tbl) / sizeof(popup_menu_tbl[0]);
2336     for (i = 0, ptbl = popup_menu_tbl; i < limit; i++, ptbl++) {
2337 	if ((cmd = engl2fnc(ptbl->vile_cmdname)) != NULL) {
2338 
2339 	    if (fnc2kcod(cmd) != -1) {
2340 		/* cmd has a binding */
2341 
2342 		(void) kcod2prc(fnc2kcod(cmd), accel);
2343 		sprintf(buf, "%s\t%s", ptbl->menu_name, accel);
2344 	    } else {
2345 		/* rewrite menu text to clear out possible unmapped bindings */
2346 
2347 		vl_strncpy(buf, ptbl->menu_name, sizeof(buf));
2348 	    }
2349 	    buf2 = w32_charstring(buf);
2350 	    ModifyMenu(popup_menu,
2351 		       ptbl->menu_id,
2352 		       MF_BYCOMMAND | MF_STRING,
2353 		       ptbl->menu_id,
2354 		       buf2);
2355 	    free(buf2);
2356 	}
2357     }
2358 
2359     /* Can't undo/redo if not applicable. */
2360     flag = (undo_ok())? MF_ENABLED : MF_GRAYED;
2361     EnableMenuItem(popup_menu, IDM_UNDO, MF_BYCOMMAND | flag);
2362     flag = (redo_ok())? MF_ENABLED : MF_GRAYED;
2363     EnableMenuItem(popup_menu, IDM_REDO, MF_BYCOMMAND | flag);
2364 
2365     /* Can't "select all" in empty buffer. */
2366     flag = (curbp->b_bytecount > 0) ? MF_ENABLED : MF_GRAYED;
2367     EnableMenuItem(popup_menu, IDM_SELECT_ALL, MF_BYCOMMAND | flag);
2368 
2369     /* Can't paste data from clipboard if not TEXT. */
2370     flag = (IsClipboardFormatAvailable(CF_TEXT)) ? MF_ENABLED : MF_GRAYED;
2371     EnableMenuItem(popup_menu, IDM_PASTE, MF_BYCOMMAND | flag);
2372 
2373     /* Do we have a text selection? */
2374     if ((bp = get_selection_buffer_and_region(&ar)) != NULL)
2375 	seltxt = MF_ENABLED;
2376     else
2377 	seltxt = MF_GRAYED;
2378 
2379     /* Is the text selection in the current buffer? */
2380     if ((seltxt == MF_ENABLED) && (bp == curbp))
2381 	samebuf = MF_ENABLED;
2382     else
2383 	samebuf = MF_GRAYED;
2384 
2385     /* Modify menu items that depend on seltxt and samebuf flags. */
2386     EnableMenuItem(popup_menu, IDM_COPY, MF_BYCOMMAND | seltxt);
2387     EnableMenuItem(popup_menu, IDM_CUT, MF_BYCOMMAND | samebuf);
2388     EnableMenuItem(popup_menu, IDM_DELETE, MF_BYCOMMAND | samebuf);
2389 
2390     /* Show popup menu. */
2391     point.x = LOWORD(msg.lParam);
2392     point.y = HIWORD(msg.lParam);
2393     ClientToScreen(msg.hwnd, &point);
2394     TrackPopupMenu(popup_menu,
2395 		   0,
2396 		   point.x,
2397 		   point.y,
2398 		   0,
2399 		   msg.hwnd,
2400 		   NULL);
2401     TRACE(("...invoke_popup_menu\n"));
2402 }
2403 
2404 static DIALOG_PROC_RETVAL CALLBACK
AboutBoxProc(HWND hDlg,UINT iMsg,WPARAM wParam,LPARAM lParam)2405 AboutBoxProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
2406 {
2407     char buf[512];
2408     HWND hwnd;
2409 
2410     (void) lParam;
2411 
2412     switch (iMsg) {
2413     case WM_INITDIALOG:
2414 	/* set about box dialog title */
2415 	sprintf(buf, "About %s", prognam);
2416 	set_window_text(hDlg, buf);
2417 
2418 	/* announce program version */
2419 	hwnd = GetDlgItem(hDlg, IDM_ABOUT_PROGNAME);
2420 	sprintf(buf, "%s\n%s%s", prognam, version, VILE_PATCHLEVEL);
2421 	set_window_text(hwnd, buf);
2422 
2423 	/* talk about copyright */
2424 	hwnd = GetDlgItem(hDlg, IDM_ABOUT_COPYRIGHT);
2425 	sprintf(buf,
2426 		"\nCopyright \xA9 Thomas Dickey 1997-2020,2021\n\n"
2427 		"%s is free software, distributed under the terms of the GNU "
2428 		"Public License, Version 2 (see COPYING).",
2429 		prognam);
2430 	set_window_text(hwnd, buf);
2431 	w32_center_window(hDlg, cur_win->main_hwnd);
2432 	return (TRUE);
2433     case WM_COMMAND:
2434 	switch (LOWORD(wParam)) {
2435 	case IDOK:
2436 	case IDCANCEL:
2437 	    EndDialog(hDlg, 0);
2438 	    return (TRUE);
2439 	}
2440 	break;
2441     }
2442     return (FALSE);
2443 }
2444 
2445 static int
handle_builtin_menu(WPARAM code)2446 handle_builtin_menu(WPARAM code)
2447 {
2448     int result = TRUE, cmd = LOWORD(code);
2449 
2450     TRACE(("handle_builtin_menu code=%p\n", (void *) code));
2451     switch (cmd) {
2452     case IDM_ABOUT:
2453 	DialogBox(vile_hinstance, W32_STRING("AboutBox"),
2454 		  cur_win->main_hwnd, AboutBoxProc);
2455 	break;
2456     case IDM_OPEN:
2457 	winopen_dir(NULL);
2458 	update(FALSE);
2459 	break;
2460     case IDM_CHDIR:
2461 	wincd_dir(NULL);
2462 	update(FALSE);
2463 	break;
2464     case IDM_PRINT:
2465 	winprint(0, 0);
2466 	update(FALSE);
2467 	break;
2468     case IDM_PAGE_SETUP:
2469 	winpg_setup(0, 0);
2470 	update(FALSE);
2471 	break;
2472     case IDM_FAVORITES:
2473 	winopen_dir(get_favorites());
2474 	update(FALSE);
2475 	break;
2476     case IDM_SAVE_AS:
2477 	winsave_dir(NULL);
2478 	update(FALSE);
2479 	break;
2480     case IDM_SELECT_ALL:
2481 	sel_all(0, 0);
2482 	update(FALSE);
2483 	break;
2484     case IDM_FONT:
2485 	set_font();
2486 	break;
2487     case IDM_MENU:
2488 	enable_popup_menu();
2489 	update(FALSE);
2490 	break;
2491     case IDM_PASTE:
2492 	/* it's okay to paste into minibuffer if reading msg line data */
2493 	if ((!reading_msg_line) && b_val(curbp, MDVIEW))
2494 	    return rdonly();
2495 	mayneedundo();
2496 	fhide_cursor();
2497 	cbrdpaste(FALSE, FALSE);
2498 	update(FALSE);
2499 	fshow_cursor();
2500 	break;
2501     case IDM_REDO:
2502 	forwredo(FALSE, FALSE);
2503 	update(FALSE);
2504 	break;
2505     case IDM_UNDO:
2506 	backundo(FALSE, FALSE);
2507 	update(FALSE);
2508 	break;
2509     case IDM_COPY:
2510 	/*
2511 	 * Copy selected text to clipboard.  Start by first yanking selected
2512 	 * text to unnamed register.  In most cases this won't be necessary,
2513 	 * especially if mouse is used to navigate and make selections.
2514 	 * However, there are cases where the user might select some text
2515 	 * (causes automatic yank to unnamed register), do something to
2516 	 * clobber the unnamed register, and then use the menu system to
2517 	 * attempt to copy selected text.
2518 	 */
2519 
2520 	sel_yank(0);
2521 	cbrdcpy_unnamed(FALSE, FALSE);
2522 	update(FALSE);
2523 	break;
2524     case IDM_CUT:
2525     case IDM_DELETE:
2526 	if (b_val(curbp, MDVIEW))
2527 	    return rdonly();
2528 	mayneedundo();
2529 	fhide_cursor();
2530 	w32_del_selection(LOWORD(code) == IDM_CUT);
2531 	update(FALSE);
2532 	fshow_cursor();
2533 	break;
2534     default:
2535 	if (cmd >= IDM_RECENT_FILES && cmd < IDM_RECENT_FLDRS) {
2536 	    (void) edit_recent_file(cmd);
2537 	    update(TRUE);	/* force cursor out of mini-buffer */
2538 	} else if (cmd >= IDM_RECENT_FLDRS &&
2539 		   cmd < (IDM_RECENT_FLDRS + MAX_RECENT_FLDRS)) {
2540 	    (void) cd_recent_folder(cmd);
2541 	    update(TRUE);	/* force cursor out of mini-buffer */
2542 	} else
2543 	    result = FALSE;
2544 	break;
2545     }
2546     TRACE(("...handle_builtin_menu code ->%d\n", result));
2547     return result;
2548 }
2549 
2550 static void
GetMousePos(POINT * result)2551 GetMousePos(POINT * result)
2552 {
2553     DWORD dword;
2554     POINTS points;
2555 
2556     dword = GetMessagePos();
2557     points = MAKEPOINTS(dword);
2558     POINTSTOPOINT((*result), points);
2559     ScreenToClient(cur_win->main_hwnd, result);
2560     result->x /= nCharWidth;
2561     result->y /= nLineHeight;
2562 }
2563 
2564 // Get current mouse position.
2565 // If the mouse is above/below the current window
2566 // then scroll the window down/up proportinally
2567 // to the distance above/below.
2568 // This function is called from WM_TIMER when
2569 // the mouse is captured and a wipe selection is
2570 // active.
2571 static void
AutoScroll(WINDOW * wp)2572 AutoScroll(WINDOW *wp)
2573 {
2574 #define DVSR    10
2575 #define INCR    6
2576 #define TRIGGER (DVSR + INCR)
2577 
2578     POINT current;
2579     int Scroll = 0;
2580     static int ScrollCount = 0, Throttle = INCR;
2581     GetMousePos(&current);
2582 
2583     if (wp == 0)
2584 	return;
2585 
2586     // Determine if we are above or below the window,
2587     // and if so, how far...
2588     if (current.y < wp->w_toprow) {
2589 	// Above the window
2590 	// Scroll = wp->w_toprow - current.y;
2591 	Scroll = 1;
2592     }
2593     if (current.y > mode_row(wp)) {
2594 	// Below
2595 	// Scroll = current.y - mode_row(wp);
2596 	// Scroll *= -1;
2597 	Scroll = -1;
2598     }
2599 
2600     TRACE(("AutoScroll:(%d, %d, %d)\n", Scroll, ScrollCount, Throttle));
2601     if (Scroll) {
2602 	int row;
2603 	if (Scroll > 0) {
2604 	    row = wp->w_toprow;
2605 	} else {
2606 	    row = mode_row(wp) - 1;
2607 	}
2608 
2609 	// Scroll the pre-determined amount, ensuring at least one line of
2610 	// window movement per timer tick.  Note also that ScrollScale is
2611 	// signed, so it will be negative if we want to scroll down.
2612 	mvupwind(TRUE, Scroll * max(ScrollCount, TRIGGER) / (Throttle + DVSR));
2613 
2614 	// Set the cursor. Column doesn't really matter, it will
2615 	// get updated as soon as we get back into the window...
2616 	if (setcursor(row, 0)) {
2617 	    sel_extend(TRUE, TRUE);
2618 	}
2619 	(void) update(TRUE);
2620 	ScrollCount++;
2621 	if (ScrollCount > TRIGGER && Throttle > 0 && ScrollCount % INCR == 0)
2622 	    Throttle--;
2623     } else {
2624 	// Reset counters
2625 	Throttle = INCR;
2626 	ScrollCount = 0;
2627     }
2628 #undef DVSR
2629 #undef INCR
2630 #undef TRIGGER
2631 }
2632 
2633 static int
MouseClickSetPos(POINT * result,int * onmode)2634 MouseClickSetPos(POINT * result, int *onmode)
2635 {
2636     int code = FALSE;
2637     WINDOW *wp;
2638 
2639     GetMousePos(result);
2640 
2641     TRACE((T_CALLED "MouseClickSetPos(%ld, %ld)\n", result->y, result->x));
2642 
2643     /*
2644      * If we're getting a button-down in a window, allow it to maybe begin
2645      * a selection (if the mouse has actually moved).  A button-down on
2646      * its modeline will allow resizing the window.
2647      */
2648     *onmode = FALSE;
2649     if ((wp = row2window(result->y)) != 0) {
2650 	if (result->y == mode_row(wp)) {
2651 	    *onmode = TRUE;
2652 	    code = TRUE;
2653 	} else {
2654 	    code = setcursor(result->y, result->x);
2655 	}
2656     }
2657     returnCode(code);
2658 }
2659 
2660 /*
2661  * Shrink a window by dragging the modeline
2662  */
2663 static int
adjust_window(WINDOW * wp,POINT * current,POINT * latest)2664 adjust_window(WINDOW *wp, POINT * current, POINT * latest)
2665 {
2666     if (latest->y == mode_row(wp)) {
2667 	if (current->y != latest->y) {
2668 	    WINDOW *save_wp = curwp;
2669 	    set_curwp(wp);
2670 	    shrinkwind(FALSE, latest->y - current->y);
2671 	    set_curwp(save_wp);
2672 	    update(TRUE);
2673 	}
2674 	*latest = *current;
2675 	return TRUE;
2676     }
2677     return FALSE;
2678 }
2679 
2680 /*
2681  * FUNCTION
2682  *   mousemove(int    *sel_pending,
2683  *             int    onmode,
2684  *             POINT  *first,
2685  *             POINT  *latest,
2686  *             MARK   *lmbdn_mark,
2687  *             WINDOW *wp)
2688  *
2689  *   sel_pending - Boolean, T -> client has recorded a left mouse button (LMB)
2690  *                 click, and so, a selection is pending.
2691  *
2692  *   onmode      - Boolean, T -> left mouse button was initially clicked
2693  *                 down on a mode line.
2694  *
2695  *   first       - editor row/col coordinates where LMB was initially recorded.
2696  *
2697  *   latest      - during the mouse move, assuming the LMB is still down,
2698  *                 "latest" represents the editor's current row/col
2699  *                 position w/in the same window where "first" was recorded.
2700  *
2701  *   lmbdn_mark  - editor MARK when LMB was initially recorded.
2702  *
2703  *   wp          - pointer to editor window where LMB was initially recorded.
2704  *
2705  * DESCRIPTION
2706  *   Using several state variables, this function handles all the semantics
2707  *   of a left mouse button "MOVE" event.  The semantics are as follows:
2708  *
2709  *   1) This function will not be called unless that LMB is down (enforced
2710  *      by client).
2711  *   2) if the LMB was initially clicked down on a mode line, then the only
2712  *      operation that can occur is window resizing.
2713  *   3) if not #2 above, then a LMB move within the current editor window
2714  *      selects a region of text.  Later, when the user releases the LMB, that
2715  *      text is yanked to the unnamed register (the yank code is not handled
2716  *      in this function).
2717  *
2718  * RETURNS
2719  *   None
2720  */
2721 static void
mousemove(int * sel_pending,int onmode,POINT * first,POINT * latest,MARK * lmbdn_mark,WINDOW * wp)2722 mousemove(int *sel_pending,
2723 	  int onmode,
2724 	  POINT * first,
2725 	  POINT * latest,
2726 	  MARK *lmbdn_mark,
2727 	  WINDOW *wp)
2728 {
2729     POINT current;
2730     int dummy;
2731 
2732     fhide_cursor();
2733     GetMousePos(&current);
2734     TRACE(("GETC:MOUSEMOVE (%ld,%ld)\n", current.x, current.y));
2735 
2736     /* If on mode line, move window. */
2737     if (onmode) {
2738 	if (!adjust_window(wp, &current, latest)) {
2739 	    /*
2740 	     * left mouse button still down, but cursor moved off mode
2741 	     * line.  Update latest to keep track of cursor in case
2742 	     * it wanders back on the mode line.
2743 	     */
2744 
2745 	    *latest = current;
2746 	}
2747 	return;
2748     }
2749 
2750     if (*sel_pending) {
2751 	/*
2752 	 * Selection pending.  If the mouse has moved at least one char,
2753 	 * start a selection.
2754 	 */
2755 
2756 	if (MouseClickSetPos(latest, &dummy)) {
2757 	    /* ignore mouse jitter */
2758 
2759 	    if (latest->x != first->x || latest->y != first->y) {
2760 		*sel_pending = FALSE;
2761 		DOT = *lmbdn_mark;
2762 		(void) sel_begin();
2763 		(void) update(TRUE);
2764 	    } else
2765 		return;
2766 	}
2767     }
2768 
2769     if (wp != row2window(current.y)) {
2770 	/* mouse moved into a different editor window. */
2771 
2772 	return;
2773     }
2774     if (!setcursor(current.y, current.x))
2775 	return;
2776     if (get_keyboard_state() & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
2777 	(void) sel_setshape(rgn_RECTANGLE);
2778     if (!sel_extend(TRUE, TRUE))
2779 	return;
2780     (void) update(TRUE);
2781 }
2782 
2783 #if OPT_SCROLLBARS
2784 static int
check_scrollbar_allocs(void)2785 check_scrollbar_allocs(void)
2786 {
2787     int newmax = cur_win->rows / 2;
2788     int oldmax = cur_win->maxscrollbars;
2789 
2790     TRACE(("check_scrollbar_allocs %d > %d ?\n", oldmax, newmax));
2791     if (newmax > oldmax) {
2792 	GROW(cur_win->scrollbars, SBDATA, oldmax, newmax);
2793 	cur_win->maxscrollbars = newmax;
2794 	TRACE(("GROW scrollbars=%p, oldmax=%d, newmax=%d\n",
2795 	       cur_win->scrollbars, oldmax, newmax));
2796     }
2797     return TRUE;
2798 }
2799 
2800 static SBDATA
new_scrollbar(void)2801 new_scrollbar(void)
2802 {
2803     SBDATA result;
2804 
2805     result.shown = FALSE;
2806     result.r.top = -1;
2807     result.r.left = -1;
2808     result.r.bottom = -1;
2809     result.r.right = -1;
2810 
2811     result.w = CreateWindow(W32_STRING("SCROLLBAR"),
2812 			    W32_STRING("scrollbar"),
2813 			    WS_CHILD | SBS_VERT | WS_CLIPSIBLINGS,
2814 			    0,	/* x */
2815 			    0,	/* y */
2816 			    SbWidth,	/* width */
2817 			    1,	/* height */
2818 			    cur_win->main_hwnd,
2819 			    (HMENU) 0,
2820 			    vile_hinstance,
2821 			    (LPVOID) 0
2822 	);
2823     return result;
2824 }
2825 
2826 static int
show_scrollbar(int n,int flag)2827 show_scrollbar(int n, int flag)
2828 {
2829     if (cur_win->scrollbars[n].shown != flag) {
2830 	TRACE(("%s_scrollbar %s\n",
2831 	       flag ? "show" : "hide",
2832 	       which_window(cur_win->scrollbars[n].w)));
2833 	ShowScrollBar(cur_win->scrollbars[n].w, SB_CTL, flag);
2834 	cur_win->scrollbars[n].shown = flag;
2835 	return TRUE;
2836     }
2837     return FALSE;
2838 }
2839 
2840 static void
set_scrollbar_range(int n,WINDOW * wp)2841 set_scrollbar_range(int n, WINDOW *wp)
2842 {
2843     SCROLLINFO info;
2844     int lnum, lcnt;
2845 
2846     lnum = line_no(wp->w_bufp, wp->w_dot.l);
2847     lnum = max(lnum, 1);
2848 
2849     lcnt = vl_line_count(wp->w_bufp);
2850     lcnt = max(lcnt, 1) + wp->w_ntrows - 1;
2851 
2852     TRACE(("set_scrollbar_range(%d, %s) %d:%d\n",
2853 	   n, wp->w_bufp->b_bname, lnum, lcnt));
2854 
2855     info.cbSize = sizeof(info);
2856     info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
2857     info.nPos = lnum - 1;
2858     info.nMin = 0;
2859     info.nMax = lcnt - 1;
2860     info.nPage = wp->w_ntrows;
2861     SetScrollInfo(cur_win->scrollbars[n].w, SB_CTL, &info, TRUE);
2862 }
2863 
2864 /*
2865  * All we want to do here is to paint the gap between the real resize-grip and
2866  * the borders of the dummy window we're surrounding it with.  That's because
2867  * the resize-grip itself isn't resizable.
2868  */
2869 static WINDOW_PROC_RETVAL FAR PASCAL
GripWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)2870 GripWndProc(
2871 	       HWND hWnd,
2872 	       UINT message,
2873 	       WPARAM wParam,
2874 	       LPARAM lParam)
2875 {
2876     PAINTSTRUCT ps;
2877     HBRUSH brush;
2878 
2879     TRACE(("GRIP:%s\n", message2s(message)));
2880 
2881     switch (message) {
2882     case WM_PAINT:
2883 	BeginPaint(hWnd, &ps);
2884 	TRACE(("...painting (%ld,%ld) (%ld,%ld)\n",
2885 	       ps.rcPaint.top,
2886 	       ps.rcPaint.left,
2887 	       ps.rcPaint.bottom,
2888 	       ps.rcPaint.right));
2889 	brush = CreateSolidBrush(GetSysColor(COLOR_SCROLLBAR));
2890 	SelectObject(ps.hdc, brush);
2891 	Rectangle(ps.hdc,
2892 		  ps.rcPaint.left,
2893 		  ps.rcPaint.top,
2894 		  ps.rcPaint.right,
2895 		  ps.rcPaint.bottom);
2896 	DeleteObject(brush);
2897 	EndPaint(hWnd, &ps);
2898 	return (0);
2899     }
2900     return (DefWindowProc(hWnd, message, wParam, lParam));
2901 }
2902 
2903 static void
update_scrollbar_sizes(void)2904 update_scrollbar_sizes(void)
2905 {
2906     RECT crect;
2907     register WINDOW *wp;
2908     int i, top, left;
2909     int newsbcnt;
2910     int oldsbcnt = cur_win->nscrollbars;
2911 
2912     TRACE(("update_scrollbar_sizes\n"));
2913 
2914     i = 0;
2915     for_each_visible_window(wp)
2916 	i++;
2917     newsbcnt = i;
2918 
2919     for (i = cur_win->nscrollbars + 1; i <= newsbcnt; i++) {
2920 	if (cur_win->scrollbars[i].w == NULL) {
2921 	    cur_win->scrollbars[i] = new_scrollbar();
2922 	    TRACE(("... created sb%d=%p\n", i, cur_win->scrollbars[i].w));
2923 	}
2924     }
2925     cur_win->nscrollbars = newsbcnt;
2926 
2927     /* Set sizes and positions on scrollbars and sliders */
2928     i = 0;
2929     GetClientRect(cur_win->main_hwnd, &crect);
2930     top = crect.top;
2931     left = crect.right - SbWidth;
2932     for_each_visible_window(wp) {
2933 	int high = RowToPixel(wp->w_ntrows + 1);
2934 	int wide = SbWidth;
2935 	if (show_scrollbar(i, TRUE)
2936 	    || cur_win->scrollbars[i].r.top != top
2937 	    || cur_win->scrollbars[i].r.left != left
2938 	    || cur_win->scrollbars[i].r.bottom != high
2939 	    || cur_win->scrollbars[i].r.right != wide) {
2940 	    MoveWindow(cur_win->scrollbars[i].w,
2941 		       cur_win->scrollbars[i].r.left = left,
2942 		       cur_win->scrollbars[i].r.top = top,
2943 		       cur_win->scrollbars[i].r.right = wide,
2944 		       cur_win->scrollbars[i].r.bottom = high,
2945 		       TRUE);
2946 	    TRACE(("... adjusted %s to (%d,%d) (%d,%d)\n",
2947 		   which_window(cur_win->scrollbars[i].w),
2948 		   left,
2949 		   top,
2950 		   SbWidth,
2951 		   high));
2952 	}
2953 	if (cur_win->nscrollbars == i + 1)
2954 	    set_scrollbar_range(i, wp);
2955 	i++;
2956 	top += high;
2957     }
2958 
2959     while (i < oldsbcnt) {
2960 	(void) show_scrollbar(i, FALSE);
2961 	i++;
2962     }
2963 
2964     if (cur_win->size_box.w == 0) {
2965 	cur_win->size_box.w = CreateWindow(
2966 					      GRIP_CLASS,
2967 					      W32_STRING("sizebox"),
2968 					      WS_CHILD
2969 					      | WS_VISIBLE
2970 					      | WS_CLIPSIBLINGS,
2971 					      cur_win->size_box.r.left = left,
2972 					      cur_win->size_box.r.top = top,
2973 					      cur_win->size_box.r.right =
2974 					      SbWidth + 1,
2975 					      cur_win->size_box.r.bottom = nLineHeight,
2976 					      cur_win->main_hwnd,
2977 					      (HMENU) 0,
2978 					      vile_hinstance,
2979 					      (LPVOID) 0
2980 	    );
2981 	cur_win->size_grip.w = CreateWindow(
2982 					       W32_STRING("SCROLLBAR"),
2983 					       W32_STRING("sizebox"),
2984 					       WS_CHILD
2985 					       | WS_VISIBLE
2986 					       | SB_CTL
2987 					       | SBS_SIZEGRIP
2988 					       | WS_CLIPSIBLINGS
2989 					       | SBS_SIZEBOXBOTTOMRIGHTALIGN,
2990 					       cur_win->size_box.r.left = 0,
2991 					       cur_win->size_box.r.top = 0,
2992 					       cur_win->size_box.r.right = SbWidth,
2993 					       cur_win->size_box.r.bottom = nLineHeight,
2994 					       cur_win->size_box.w,
2995 					       (HMENU) 0,
2996 					       vile_hinstance,
2997 					       (LPVOID) 0
2998 	    );
2999 	TRACE(("... made SIZEGRIP %p at %d,%d\n", cur_win->size_box.w, left, top));
3000     } else {
3001 	int ok;
3002 
3003 	if (cur_win->size_box.r.left != left
3004 	    || cur_win->size_box.r.top != top
3005 	    || cur_win->size_box.r.right != SbWidth
3006 	    || cur_win->size_box.r.bottom != nLineHeight) {
3007 	    ok = MoveWindow(cur_win->size_box.w,
3008 			    cur_win->size_box.r.left = left,
3009 			    cur_win->size_box.r.top = top,
3010 			    cur_win->size_box.r.right = SbWidth,
3011 			    cur_win->size_box.r.bottom = nLineHeight,
3012 			    TRUE);
3013 	    TRACE(("... move SIZE_BOX %d:%p to %d,%d\n",
3014 		   ok, cur_win->size_box.w, left, top));
3015 	}
3016 
3017 	left = 0;
3018 	top = 0;
3019 	if (cur_win->size_grip.r.left != left
3020 	    || cur_win->size_grip.r.top != top
3021 	    || cur_win->size_grip.r.right != SbWidth
3022 	    || cur_win->size_grip.r.bottom != nLineHeight) {
3023 	    ok = MoveWindow(cur_win->size_grip.w,
3024 			    cur_win->size_grip.r.left = left,
3025 			    cur_win->size_grip.r.top = top,
3026 			    cur_win->size_grip.r.right = SbWidth,
3027 			    cur_win->size_grip.r.bottom = nLineHeight,
3028 			    TRUE);
3029 	    TRACE(("... move SIZEGRIP %d:%p to %d,%d\n",
3030 		   ok, cur_win->size_grip.w, left, top));
3031 	}
3032 	(void) ok;
3033     }
3034 }
3035 
3036 void
gui_update_scrollbar(WINDOW * uwp)3037 gui_update_scrollbar(WINDOW *uwp)
3038 {
3039     WINDOW *wp;
3040     int i;
3041 
3042     TRACE(("gui_update_scrollbar uwp=%p %s\n", uwp, uwp->w_bufp->b_bname));
3043     if (dont_update_sb)
3044 	return;
3045 
3046     i = 0;
3047     for_each_visible_window(wp) {
3048 	TRACE(("wp=%p name='%s'\n", wp, wp->w_bufp->b_bname));
3049 	if (wp == uwp)
3050 	    break;
3051 	i++;
3052     }
3053     if (wp == 0)
3054 	return;
3055 
3056     TRACE(("i=%d, nscrollbars=%d\n", i, cur_win->nscrollbars));
3057     if (i >= cur_win->nscrollbars || (wp->w_flag & WFSBAR)) {
3058 	/*
3059 	 * update_scrollbar_sizes will recursively invoke gui_update_scrollbar,
3060 	 * but with WFSBAR disabled.
3061 	 */
3062 	update_scrollbar_sizes();
3063 	return;
3064     }
3065 
3066     set_scrollbar_range(i, wp);
3067 }
3068 
3069 static int
find_scrollbar(HWND hWnd)3070 find_scrollbar(HWND hWnd)
3071 {
3072     WINDOW *wp;
3073     int i = 0;
3074 
3075     TRACE((T_CALLED "find_scrollbar(hWnd=%p)\n", hWnd));
3076 
3077     for_each_visible_window(wp) {
3078 	if (cur_win->scrollbars[i].w == hWnd
3079 	    || cur_win->main_hwnd == hWnd) {
3080 	    set_curwp(wp);
3081 	    if (wp->w_bufp != curbp) {
3082 		swbuffer(wp->w_bufp);
3083 	    }
3084 	    returnCode(i);
3085 	}
3086 	i++;
3087     }
3088     returnCode(-1);
3089 }
3090 
3091 /*
3092  * M$ is only capable of delivering an unsigned 16-bit number for nPos.  Infer
3093  * the "right" value by minimizing the distance we travel in a given jump.
3094  */
3095 static int
fix_scrollbar_tracking(int nPos)3096 fix_scrollbar_tracking(int nPos)
3097 {
3098     int this_line = line_no(curwp->w_bufp, curwp->w_line.l);
3099     int last_line = line_no(curwp->w_bufp, lback(buf_head(curwp->w_bufp)));
3100     int diff_line = 0x10000;
3101     int test_line;
3102 
3103     for (test_line = nPos; test_line < last_line; test_line += 0x10000) {
3104 	int check = ABS(this_line - test_line);
3105 	if (check < diff_line) {
3106 	    diff_line = check;
3107 	    nPos = test_line;
3108 	}
3109     }
3110     return nPos;
3111 }
3112 
3113 static void
handle_scrollbar(HWND hWnd,int msg,int nPos)3114 handle_scrollbar(HWND hWnd, int msg, int nPos)
3115 {
3116     int this_line;
3117     int snum = find_scrollbar(hWnd);
3118     int upd = TRUE;		/* do an update() if T */
3119 
3120     TRACE(("handle_scrollbar msg=%d, nPos=%d\n", msg, nPos));
3121 
3122     if (snum < 0) {
3123 	TRACE(("...could not find window for %s\n", which_window(hWnd)));
3124 	return;
3125     }
3126 
3127     fhide_cursor();
3128     switch (msg) {
3129     case SB_BOTTOM:
3130 	TRACE(("-> SB_BOTTOM\n"));
3131 	gotoeob(FALSE, 1);
3132 	break;
3133     case SB_LINEDOWN:
3134 	TRACE(("-> SB_LINEDOWN\n"));
3135 	mvdnwind(FALSE, 1);
3136 	break;
3137     case SB_LINEUP:
3138 	TRACE(("-> SB_LINEUP\n"));
3139 	mvupwind(FALSE, 1);
3140 	break;
3141     case SB_PAGEDOWN:
3142 	TRACE(("-> SB_PAGEDOWN\n"));
3143 	forwpage(FALSE, 1);
3144 	break;
3145     case SB_PAGEUP:
3146 	TRACE(("-> SB_PAGEUP\n"));
3147 	backpage(FALSE, 1);
3148 	break;
3149     case SB_THUMBPOSITION:
3150 	TRACE(("-> SB_THUMBPOSITION: %d\n", nPos));
3151 	vl_gotoline(fix_scrollbar_tracking(nPos) + 1);
3152 	break;
3153     case SB_THUMBTRACK:
3154 	TRACE(("-> SB_THUMBTRACK: %d\n", nPos));
3155 	/*
3156 	 * M$ is only capable of delivering an unsigned 16-bit number for nPos.
3157 	 * Infer the "right" value by minimizing the distance we travel in a
3158 	 * given jump.
3159 	 */
3160 	this_line = line_no(curwp->w_bufp, curwp->w_line.l);
3161 	mvupwind(TRUE, this_line - fix_scrollbar_tracking(nPos));
3162 	break;
3163     case SB_TOP:
3164 	TRACE(("-> SB_TOP\n"));
3165 	vl_gotoline(1);
3166 	break;
3167     case SB_ENDSCROLL:
3168 	TRACE(("-> SB_ENDSCROLL\n"));
3169 	/* Fall through */
3170     default:
3171 	upd = FALSE;
3172 	break;
3173     }
3174     if (upd) {
3175 	(void) update(TRUE);
3176 	set_scrollbar_range(snum, curwp);
3177     }
3178     fshow_cursor();
3179 }
3180 #endif /* OPT_SCROLLBARS */
3181 
3182 static int
ntwinio_getch(void)3183 ntwinio_getch(void)
3184 {
3185     static DWORD lastclick = 0;
3186     static int clicks = 0, onmode;
3187     // Save the timer ID so we can kill it.
3188 
3189     DWORD thisclick;
3190     DWORD keyboard_state = 0;
3191     int buttondown = FALSE;
3192     int sel_pending = FALSE;	/* Selection pending */
3193     MARK lmbdn_mark;		/* left mouse button down here */
3194     MARK last_dot = nullmark;	/* remember where dot was before selection */
3195     WINDOW *last_win = 0;	/* remember which window dot was in */
3196     int selecting = FALSE;	/* toggle between cut and paste */
3197     int result = -1;
3198     KEY_EVENT_RECORD ker;
3199     MSG msg;
3200     POINT first;
3201     POINT latest;
3202     UINT clicktime;
3203     WINDOW *that_wp = 0;
3204 #ifdef VAL_AUTOCOLOR
3205     int milli_ac, orig_milli_ac;
3206 #endif
3207 
3208     selecting = FALSE;		/* toggle between cut and paste */
3209 
3210     clicktime = GetDoubleClickTime();
3211 #ifdef VAL_AUTOCOLOR
3212     orig_milli_ac = global_b_val(VAL_AUTOCOLOR);
3213 #endif
3214     if (GetFocus() == cur_win->main_hwnd) {
3215 	fshow_cursor();
3216     } else {
3217 	fhide_cursor();
3218     }
3219     memset(&lmbdn_mark, 0, sizeof(lmbdn_mark));
3220     while (result < 0) {
3221 	if (!restore_key_data(&msg, &keyboard_state)) {
3222 #ifdef VAL_AUTOCOLOR
3223 	    milli_ac = orig_milli_ac;
3224 	    while (milli_ac > 0) {
3225 		if (peek_at_queue(&msg))
3226 		    break;	/* A meaningful event--process it. */
3227 		Sleep(20);	/* sleep a bit, but be responsive to all events */
3228 		milli_ac -= 20;
3229 	    }
3230 	    if (orig_milli_ac && milli_ac <= 0) {
3231 		ac_active = TRUE;
3232 		autocolor();
3233 		ac_active = FALSE;
3234 	    }
3235 #endif
3236 	    if (GetMessage(&msg, (HWND) 0, 0, 0) != TRUE) {
3237 		PostQuitMessage(1);
3238 		TRACE(("GETC:no message\n"));
3239 		quit(TRUE, 1);
3240 	    }
3241 	    if (is_keyboard_message(&msg))
3242 		keyboard_state = get_keyboard_state();
3243 	}
3244 
3245 	if (TranslateAccelerator(cur_win->main_hwnd, hAccTable, &msg)) {
3246 	    TRACE(("GETC:no accelerator\n"));
3247 	    continue;
3248 	}
3249 
3250 	TranslateMessage(&msg);
3251 
3252 	switch (msg.message) {
3253 	case WM_DESTROY:
3254 	    TRACE(("GETC:DESTROY\n"));
3255 	    PostQuitMessage(0);
3256 	    continue;
3257 
3258 	case WM_CHAR:
3259 	    TRACE(("GETC:CHAR:%p\n", (void *) msg.wParam));
3260 	    result = (int) msg.wParam;
3261 	    /*
3262 	     * Check for modifiers on control keys such as tab.
3263 	     */
3264 	    if ((result < 256) && isCntrl(result)) {
3265 		keyboard_state &= ~(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED);
3266 		if (isModified(keyboard_state))
3267 		    result = modified_key(result, keyboard_state);
3268 	    }
3269 	    if (result == ESC) {
3270 		sel_release();
3271 		(void) update(TRUE);
3272 	    }
3273 	    break;
3274 
3275 	case WM_KEYDOWN:
3276 	case WM_SYSKEYDOWN:
3277 	    ker.uChar.AsciiChar = 0;
3278 	    ker.wVirtualKeyCode = (SHORT) msg.wParam;
3279 	    ker.dwControlKeyState = keyboard_state;
3280 	    result = decode_key_event(&ker);
3281 	    TRACE(("GETC:%sKEYDOWN:%p %s ->%#x\n",
3282 		   (msg.message == WM_SYSKEYDOWN) ? "SYS" : "",
3283 		   (void *) msg.wParam,
3284 		   keyboard_state2s(keyboard_state),
3285 		   result));
3286 	    if (result == NOKYMAP) {
3287 		DispatchMessage(&msg);
3288 		result = -1;
3289 #ifdef VILE_OLE
3290 	    } else if (result == KYREDIR) {
3291 		result = -1;	/* keystroke sent elsewhere */
3292 #endif
3293 	    } else if (result == (int) msg.wParam) {
3294 		result = -1;	/* we'll get a WM_CHAR next */
3295 	    }
3296 	    break;
3297 
3298 	case WM_TIMER:
3299 	    TRACE(("Timer fired\n"));
3300 	    {
3301 		if (msg.wParam == nIDTimer) {
3302 		    /*
3303 		     * perform auto-scroll if the mouse cursor
3304 		     * is above/below the current window.
3305 		     */
3306 
3307 		    AutoScroll(that_wp);
3308 		} else {
3309 		    DispatchMessage(&msg);
3310 		}
3311 	    }
3312 	    break;
3313 	case WM_LBUTTONDOWN:
3314 	    TRACE(("GETC:LBUTTONDOWN %s\n", which_window(msg.hwnd)));
3315 	    if (msg.hwnd == cur_win->text_hwnd) {
3316 		/* Clear current selection, a la notepad. */
3317 		sel_release();
3318 		selecting = FALSE;
3319 		last_dot = DOT;
3320 		last_win = curwp;
3321 		/* Allow click to change window focus. */
3322 		if (MouseClickSetPos(&first, &onmode)) {
3323 		    fhide_cursor();
3324 		    lmbdn_mark = DOT;
3325 		    latest = first;
3326 		    that_wp = row2window(first.y);
3327 		    (void) update(TRUE);	/* for wdw change */
3328 
3329 		    /*
3330 		     * If clicking around on bottom mode line or in the
3331 		     * message line (aka mini-buffer), we're done.  In the
3332 		     * next computation, "term.rows - 1" is last usable
3333 		     * editor row, and additional "- 1" accounts for
3334 		     * bottom mode line.
3335 		     */
3336 		    if (first.y >= term.rows - 1 - 1) {
3337 			fshow_cursor();
3338 			vile_selecting = sel_pending = buttondown = FALSE;
3339 			break;
3340 		    }
3341 
3342 		    vile_selecting = sel_pending = buttondown = TRUE;
3343 		    SetCursor((onmode) ? wdwsize_cursor : selection_cursor);
3344 
3345 		    if (onmode) {
3346 			/* no mouse capture necessary if on a mode line */
3347 
3348 			break;
3349 		    }
3350 
3351 		    /* we _will_ own the mouse, if win32 API cooperates */
3352 		    SetCapture(cur_win->text_hwnd);
3353 		    mouse_captured = TRUE;
3354 
3355 		    /* Set a 25 msec timer for handling auto-scroll. */
3356 		    nIDTimer = SetTimer(cur_win->text_hwnd, 1, 25, 0);
3357 		    if (nIDTimer == 0) {
3358 			/* timer resources exhausted */
3359 
3360 			disp_win32_error(W32_SYS_ERROR, cur_win->text_hwnd);
3361 			ReleaseCapture();
3362 			mouse_captured = FALSE;
3363 		    } else {
3364 			AutoScroll(that_wp);
3365 		    }
3366 		}
3367 	    } else {
3368 		DispatchMessage(&msg);
3369 	    }
3370 	    break;
3371 
3372 	case WM_LBUTTONUP:
3373 	    TRACE(("GETC:LBUTTONUP %s\n", which_window(msg.hwnd)));
3374 
3375 	    /* If we captured the mouse, then we will release it. */
3376 	    if (mouse_captured) {
3377 		mouse_captured = FALSE;
3378 		ReleaseCapture();
3379 		KillTimer(cur_win->text_hwnd, nIDTimer);
3380 		nIDTimer = 0;
3381 	    }
3382 	    if (msg.hwnd == cur_win->text_hwnd) {
3383 		fhide_cursor();
3384 		vile_selecting = sel_pending = FALSE;
3385 		thisclick = GetTickCount();
3386 		TRACE(("CLICK %ld/%ld\n", lastclick, thisclick));
3387 		if (thisclick - lastclick < clicktime) {
3388 		    clicks++;
3389 		    TRACE(("MOUSE CLICKS %d\n", clicks));
3390 		} else {
3391 		    clicks = 0;
3392 		}
3393 		lastclick = thisclick;
3394 
3395 		switch (clicks) {
3396 		case 1:
3397 		    on_double_click();
3398 		    selecting = TRUE;
3399 		    break;
3400 		case 2:
3401 		    on_triple_click();
3402 		    selecting = TRUE;
3403 		    break;
3404 		}
3405 
3406 		if (buttondown) {
3407 		    int dummy;
3408 
3409 		    /*
3410 		     * Update editor's current cursor position, if that
3411 		     * position is still within cur_win->text_hwnd .
3412 		     */
3413 		    (void) MouseClickSetPos(&latest, &dummy);
3414 
3415 		    if (!onmode) {
3416 			/*
3417 			 * The LMB is down and non-modeline mouse movement
3418 			 * just terminated within a vile window.  Note
3419 			 * however that, due to autoscroll, the cursor
3420 			 * might not now be positioned within
3421 			 * cur_win->main_hwnd--but that doesn't matter.
3422 			 * If the user selected at least one char of
3423 			 * text, yank it to the unnamed register.
3424 			 */
3425 
3426 			sel_yank(0);
3427 		    }
3428 		    if (selecting) {
3429 			if (win2index(last_win) >= 0) {
3430 			    set_curwp(last_win);
3431 			    restore_dot(last_dot);
3432 			}
3433 			last_win = 0;
3434 			last_dot = nullmark;
3435 		    }
3436 		}
3437 		onmode = buttondown = FALSE;
3438 		(void) update(TRUE);
3439 		fshow_cursor();
3440 	    } else {
3441 		DispatchMessage(&msg);
3442 	    }
3443 	    break;
3444 
3445 	    /*
3446 	     * define _WIN32_WINNT=0x400 (or higher) to include WM_MOUSEWHEEL
3447 	     * code below.  Note that WM_MOUSEWHEEL is really only required
3448 	     * to support a mouse driver that doesn't emit WM_VMSCROLL
3449 	     * messages when the wheel mouse rotates.  Examples:
3450 	     *
3451 	     *    MS Intellimouse driver     -> emits WM_VMSCROLL
3452 	     *    Logitech mousewheel driver -> emits WM_VMSCROLL
3453 	     *    MS PS/2 compatible driver  -> emits WM_MOUSEWHEEL
3454 	     *
3455 	     * The latter driver is often installed when PNP can't
3456 	     * distinguish the native HW.
3457 	     */
3458 #ifdef WM_MOUSEWHEEL
3459 #if OPT_SCROLLBARS
3460 	case WM_MOUSEWHEEL:
3461 	    {
3462 		int c;
3463 
3464 		fhide_cursor();
3465 		if ((short) HIWORD(msg.wParam) > 0) {
3466 		    c = (HIWORD(msg.wParam) / WHEEL_DELTA);
3467 		    mvupwind(TRUE, c * 3);
3468 		} else {
3469 		    c = (-((short) HIWORD(msg.wParam)) / WHEEL_DELTA);
3470 		    mvdnwind(TRUE, c * 3);
3471 		}
3472 		update(TRUE);
3473 		fshow_cursor();
3474 	    }
3475 	    break;
3476 #endif
3477 #endif
3478 
3479 	case WM_MBUTTONDOWN:
3480 	    TRACE(("GETC:MBUTTONDOWN %s\n", which_window(msg.hwnd)));
3481 	    if (msg.hwnd == cur_win->text_hwnd) {
3482 		if (MouseClickSetPos(&latest, &onmode)
3483 		    && !onmode) {
3484 		    sel_yank(0);
3485 		    sel_release();
3486 		    paste_selection();
3487 		}
3488 		(void) update(TRUE);
3489 	    } else {
3490 		DispatchMessage(&msg);
3491 	    }
3492 	    break;
3493 
3494 	case WM_RBUTTONDOWN:
3495 	    TRACE(("GETC:RBUTTONDOWN %s\n", which_window(msg.hwnd)));
3496 	    if (msg.hwnd == cur_win->text_hwnd) {
3497 		if (enable_popup) {
3498 		    invoke_popup_menu(msg);
3499 		} else {
3500 		    if (selecting) {
3501 			sel_yank(0);
3502 			cbrdcpy_unnamed(FALSE, 1);
3503 			selecting = FALSE;
3504 			sel_release();
3505 		    } else {
3506 			mayneedundo();
3507 			cbrdpaste(FALSE, 1);
3508 		    }
3509 		    (void) update(TRUE);
3510 		}
3511 	    } else {
3512 		DispatchMessage(&msg);
3513 	    }
3514 	    break;
3515 
3516 	case WM_COMMAND:
3517 	    TRACE(("GETC:WM_COMMAND, popup:%d, wParam:%p\n",
3518 		   enable_popup, (void *) msg.wParam));
3519 	    if (enable_popup) {
3520 		handle_builtin_menu(msg.wParam);
3521 	    } else {
3522 		DispatchMessage(&msg);
3523 	    }
3524 	    break;
3525 
3526 	case WM_MOUSEMOVE:
3527 	    {
3528 		POINT pt;
3529 		WINDOW *wp;
3530 
3531 		GetMousePos(&pt);
3532 		if ((wp = row2window(pt.y)) != 0) {
3533 		    /*
3534 		     * In the next computation, "term.rows - 1" is last
3535 		     * usable editor row, and additional "- 1" accounts for
3536 		     * bottom mode line.
3537 		     */
3538 
3539 		    if (pt.y == mode_row(wp) &&
3540 			(pt.y < term.rows - 1 - 1) &&
3541 			(pt.x < term.cols)) {
3542 			/*
3543 			 * On movable mode line and cursor not in scrollbar
3544 			 * rectangle, so show appropriate cursor.
3545 			 */
3546 
3547 			SetCursor(wdwsize_cursor);
3548 		    }
3549 		}
3550 		if (buttondown) {
3551 		    mousemove(&sel_pending,
3552 			      onmode,
3553 			      &first,
3554 			      &latest,
3555 			      &lmbdn_mark,
3556 			      that_wp);
3557 		    selecting = !sel_pending;
3558 		} else {
3559 		    DispatchMessage(&msg);
3560 		}
3561 	    }
3562 	    break;
3563 
3564 	default:
3565 	    TRACE(("GETC:default(%s)\n", message2s(msg.message)));
3566 	    /* FALLTHRU */
3567 	case WM_KEYUP:		/* FALLTHRU */
3568 	case WM_NCACTIVATE:	/* FALLTHRU */
3569 	case WM_NCHITTEST:	/* FALLTHRU */
3570 	case WM_NCLBUTTONDOWN:	/* FALLTHRU */
3571 	case WM_NCMOUSEMOVE:	/* FALLTHRU */
3572 	case WM_PAINT:		/* FALLTHRU */
3573 	case WM_SETCURSOR:	/* FALLTHRU */
3574 	case WM_SYSKEYUP:	/* FALLTHRU */
3575 	case WM_SYSTIMER:
3576 	    DispatchMessage(&msg);
3577 	    break;
3578 	}
3579     }
3580     fhide_cursor();
3581     vile_selecting = FALSE;
3582 
3583     TRACE(("...ntwinio_getch %#x\n", result));
3584     return result;
3585 }
3586 
3587 static int
ntwinio_typahead(void)3588 ntwinio_typahead(void)
3589 {
3590     int rc = 0;
3591     MSG msg;
3592 
3593 #ifdef VAL_AUTOCOLOR
3594     if (ac_active) {
3595 	/*
3596 	 * Came here during an autocolor operation.  Do nothing, in an
3597 	 * attempt to avoid a keyboard lockup (editor loop) that occurs on
3598 	 * rare occasions (not reproducible).
3599 	 */
3600 
3601 	rc = 0;
3602     } else
3603 #endif
3604     if (restore_key_data((MSG *) 0, (DWORD *) 0)) {
3605 	rc = 1;
3606     } else if (PeekMessage(&msg, (HWND) 0, WM_KEYDOWN, WM_KEYDOWN, PM_NOREMOVE)) {
3607 	rc = 1;
3608     } else if (PeekMessage(&msg, (HWND) 0, WM_SYSKEYDOWN, WM_SYSKEYDOWN, PM_NOREMOVE)) {
3609 	rc = 1;
3610     } else {
3611 	rc = PeekMessage(&msg, (HWND) 0, WM_CHAR, WM_CHAR, PM_NOREMOVE);
3612     }
3613     return rc;
3614 }
3615 
3616 static void
repaint_window(HWND hWnd)3617 repaint_window(HWND hWnd)
3618 {
3619     PAINTSTRUCT ps;
3620     int x1, y1, x2, y2;
3621     int row, col;
3622 
3623     BeginPaint(hWnd, &ps);
3624     TRACE((T_CALLED "repaint_window (erase:%d) %s\n", ps.fErase,
3625 	   which_window(hWnd)));
3626 
3627     nt_set_colors(ps.hdc, cur_atr);
3628 
3629     TRACE(("...painting (%ld,%ld) (%ld,%ld)\n",
3630 	   ps.rcPaint.top,
3631 	   ps.rcPaint.left,
3632 	   ps.rcPaint.bottom,
3633 	   ps.rcPaint.right));
3634 
3635     y1 = PixelToRow(ps.rcPaint.top);
3636     x1 = PixelToCol(ps.rcPaint.left);
3637     y2 = PixelToRow(ps.rcPaint.bottom + nLineHeight);
3638     x2 = PixelToCol(ps.rcPaint.right + nCharWidth);
3639 
3640     if (y1 < 0)
3641 	y1 = 0;
3642     if (x1 < 0)
3643 	x1 = 0;
3644     if (y2 > term.rows)
3645 	y2 = term.rows;
3646     if (y2 > term.maxrows)
3647 	y2 = term.maxrows;
3648     if (x2 > term.cols)
3649 	x2 = term.cols;
3650     if (x2 > term.maxcols)
3651 	x2 = term.maxcols;
3652 
3653     TRACE(("...erase %d\n", ps.fErase));
3654     TRACE(("...cells (%d,%d) - (%d,%d)\n", y1, x1, y2, x2));
3655     TRACE(("...top:    %ld\n", RowToPixel(y1) - ps.rcPaint.top));
3656     TRACE(("...left:   %ld\n", ColToPixel(x1) - ps.rcPaint.left));
3657     TRACE(("...bottom: %ld\n", RowToPixel(y2) - ps.rcPaint.bottom));
3658     TRACE(("...right:  %ld\n", ColToPixel(x2) - ps.rcPaint.right));
3659 
3660     for (row = y1; row < y2; row++) {
3661 	if (pscreen != 0
3662 	    && VideoText(pscreen[row]) != 0
3663 	    && VideoAttr(pscreen[row]) != 0) {
3664 	    int old_col = x1;
3665 	    VIDEO_ATTR old_atr = CELL_ATTR(row, old_col);
3666 	    VIDEO_ATTR new_atr;
3667 
3668 	    for (col = x1 + 1; col < x2; col++) {
3669 		new_atr = CELL_ATTR(row, col);
3670 		if (new_atr != old_atr) {
3671 		    really_draw_text(ps.hdc,
3672 				     &CELL_TEXT(row, old_col),
3673 				     old_atr,
3674 				     col - old_col,
3675 				     row,
3676 				     old_col);
3677 		    old_atr = new_atr;
3678 		    old_col = col;
3679 		}
3680 	    }
3681 	    if (old_col < x2) {
3682 		really_draw_text(ps.hdc,
3683 				 &CELL_TEXT(row, old_col),
3684 				 old_atr,
3685 				 x2 - old_col,
3686 				 row,
3687 				 old_col);
3688 	    }
3689 	}
3690     }
3691 
3692     EndPaint(hWnd, &ps);
3693 
3694     returnVoid();
3695 }
3696 
3697 static void
receive_dropped_files(HDROP hDrop)3698 receive_dropped_files(HDROP hDrop)
3699 {
3700     W32_CHAR name2[NFILEN];
3701     UINT inx = 0xFFFFFFFF;
3702     UINT limit = DragQueryFile(hDrop, inx, name2, TABLESIZE(name2));
3703     BUFFER *bp = 0;
3704     char *name = 0;
3705 
3706     TRACE((T_CALLED "receive_dropped_files(hDrop=%p) %d dropped files\n",
3707 	   hDrop, limit));
3708 
3709     while (++inx < limit) {
3710 	DragQueryFile(hDrop, inx, name2, TABLESIZE(name2));
3711 	if ((name = asc_charstring(name2)) != 0) {
3712 	    TRACE(("...'%s'\n", name));
3713 	    if ((bp = getfile2bp(name, FALSE, FALSE)) != 0)
3714 		bp->b_flag |= BFARGS;	/* treat this as an argument */
3715 	    free(name);
3716 	}
3717     }
3718     if (bp != 0) {
3719 	if (swbuffer(bp)) {	/* editor switches to 1st buffer */
3720 	    set_directory_from_file(bp);
3721 	}
3722 	update(TRUE);
3723     }
3724     DragFinish(hDrop);
3725     returnVoid();
3726 }
3727 
3728 static void
HandleClose(HWND hWnd)3729 HandleClose(HWND hWnd)
3730 {
3731     (void) hWnd;
3732 
3733     quit(FALSE, 1);
3734 }
3735 
3736 static WINDOW_PROC_RETVAL FAR PASCAL
TextWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)3737 TextWndProc(
3738 	       HWND hWnd,
3739 	       UINT message,
3740 	       WPARAM wParam,
3741 	       LPARAM lParam)
3742 {
3743     RECT rect;
3744     HDC hDC;
3745 
3746     TRACE(("TEXT:%s, %s\n", message2s(message), which_window(hWnd)));
3747 
3748     switch (message) {
3749     case WM_PAINT:
3750 	if ((hWnd == cur_win->main_hwnd
3751 	     || hWnd == cur_win->text_hwnd)
3752 	    && GetUpdateRect(hWnd, (LPRECT) 0, FALSE)) {
3753 	    repaint_window(hWnd);
3754 	} else {
3755 	    /* scrollbars, etc. */
3756 	    TRACE(("...repaint %s\n", which_window(hWnd)));
3757 	    return (DefWindowProc(hWnd, message, wParam, lParam));
3758 	}
3759 	break;
3760     case WM_WVILE_FLASH_START:
3761 	GetClientRect(cur_win->text_hwnd, &rect);
3762 	hDC = GetDC(cur_win->text_hwnd);
3763 	InvertRect(hDC, &rect);
3764 	ReleaseDC(cur_win->text_hwnd, hDC);
3765 	break;
3766     case WM_WVILE_FLASH_STOP:
3767 	Sleep(200);
3768 	GetClientRect(cur_win->text_hwnd, &rect);
3769 	hDC = GetDC(cur_win->text_hwnd);
3770 	InvertRect(hDC, &rect);
3771 	ReleaseDC(cur_win->text_hwnd, hDC);
3772 	break;
3773     default:
3774 	return (DefWindowProc(hWnd, message, wParam, lParam));
3775 
3776 	IGN_PROC("TEXT:", WM_ERASEBKGND);
3777     }
3778     return (0);
3779 }
3780 
3781 static WINDOW_PROC_RETVAL FAR PASCAL
MainWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)3782 MainWndProc(
3783 	       HWND hWnd,
3784 	       UINT message,
3785 	       WPARAM wParam,
3786 	       LPARAM lParam)
3787 {
3788     static int resize_pending;	/* a resize, not a move */
3789     static HWND resize_hwnd;
3790     static int resize_wdw_up;
3791 
3792     TRACE(("MAIN:%s, %s\n", message2s(message), which_window(hWnd)));
3793 
3794     switch (message) {
3795 	HANDLE_MSG(hWnd, WM_CLOSE, HandleClose);
3796     case WM_COMMAND:
3797 #if 0				/* FIXME */
3798 	switch (wParam) {
3799 	case IDC_button:
3800 	    wParam = IDC_button_x;
3801 	    PostMessage(hWnd, message, wParam, lParam);
3802 	    break;
3803 	}
3804 #endif
3805 	break;
3806 
3807     case WM_SETFOCUS:
3808 	fshow_cursor();
3809 	break;
3810     case WM_WVILE_CURSOR_ON:
3811 	if (GetFocus() == hWnd)
3812 	    fshow_cursor();
3813 	break;
3814 
3815     case WM_KILLFOCUS:
3816     case WM_WVILE_CURSOR_OFF:
3817 	fhide_cursor();
3818 	break;
3819 
3820     case WM_DESTROY:
3821 	PostQuitMessage(0);
3822 	break;
3823 
3824     case WM_WINDOWPOSCHANGED:
3825 	if (!IsIconic(hWnd))
3826 	    ResizeClient();	/* ignore if window minimized */
3827 	return (DefWindowProc(hWnd, message, wParam, lParam));
3828 
3829     case WM_WINDOWPOSCHANGING:
3830 	if (wheadp != 0)
3831 	    return AdjustPosChanging(hWnd, (LPWINDOWPOS) lParam);
3832 	return (DefWindowProc(hWnd, message, wParam, lParam));
3833 
3834     case WM_SIZING:
3835 	if (initialized) {
3836 	    char buf[32];
3837 	    static int frame_w, frame_h;
3838 
3839 	    if (is_winnt()) {
3840 		/*
3841 		 * Win9x/ME GDI doesn't support supplementary GDI activity
3842 		 * while a window resize is in progress.
3843 		 */
3844 
3845 		if (!resize_hwnd)
3846 		    resize_hwnd = sizing_window();
3847 		if (resize_hwnd) {
3848 		    if (!resize_wdw_up) {
3849 			RECT crect, wrect;
3850 
3851 			resize_wdw_up = TRUE;
3852 			ShowWindow(resize_hwnd, SW_SHOWNORMAL);
3853 			sprintf(buf, "%d cols X %d rows", term.cols, term.rows);
3854 
3855 			/* compute real estate consumed by main window frame */
3856 			GetClientRect(cur_win->main_hwnd, &crect);
3857 			GetWindowRect(cur_win->main_hwnd, &wrect);
3858 			frame_w = (wrect.right - wrect.left) - crect.right;
3859 			frame_h = (wrect.bottom - wrect.top) - crect.bottom;
3860 		    } else {
3861 			RECT newrect, arect;
3862 			int h, w;
3863 
3864 			/*
3865 			 * The rectangle passed in with this message includes
3866 			 * the outer frame, which is worthless for editing
3867 			 * text.
3868 			 */
3869 			newrect = *((RECT *) lParam);
3870 			newrect.right -= frame_w;
3871 			newrect.bottom -= frame_h;
3872 			h = RectToRows(newrect);
3873 			w = RectToCols(newrect);
3874 			sprintf(buf, "%d cols X %d rows", w, h);
3875 
3876 			/*
3877 			 * This code is more complicated than it should be,
3878 			 * but it works (which is another way of saying:  I
3879 			 * have no idea what's happening).  Just too many hoops
3880 			 * to jump through to center one window over another.
3881 			 */
3882 			GetClientRect(GetForegroundWindow(), &arect);
3883 			MoveWindow(resize_hwnd,
3884 				   ((arect.right - arect.left) / 2 -
3885 				    RSZ_WDW_WDTH / 2),
3886 				   ((arect.bottom - arect.top) / 2 -
3887 				    RSZ_WDW_HGHT / 2),
3888 				   RSZ_WDW_WDTH,
3889 				   RSZ_WDW_HGHT,
3890 				   TRUE);
3891 		    }
3892 		    set_window_text(resize_hwnd, buf);
3893 		}
3894 	    }
3895 	}
3896 	return AdjustResizing(hWnd, wParam, (LPRECT) lParam);
3897 
3898     case WM_EXITSIZEMOVE:
3899 	if (resize_hwnd && resize_wdw_up) {
3900 	    resize_wdw_up = FALSE;
3901 	    SendMessage(resize_hwnd, WM_CLOSE, 0, 0);
3902 	    resize_hwnd = NULL;
3903 	}
3904 	ResizeClient();
3905 	if (resize_pending) {
3906 	    if (initialized) {
3907 		mlwrite("columns: %d, rows: %d", term.cols, term.rows);
3908 		update(TRUE);	/* Force cursor out of message line */
3909 	    }
3910 	    resize_pending = FALSE;
3911 	}
3912 	return (DefWindowProc(hWnd, message, wParam, lParam));
3913 
3914     case WM_DROPFILES:
3915 	receive_dropped_files((HDROP) wParam);
3916 	return 0;
3917 
3918     case WM_INITMENUPOPUP:
3919 	TRACE(("MAIN:WM_INITMENUPOPUP %s at %d,%d\n",
3920 	       syscommand2s(LOWORD(wParam)),
3921 	       HIWORD(lParam),
3922 	       LOWORD(lParam)));
3923 	build_recent_file_and_folder_menus();
3924 	return (DefWindowProc(hWnd, message, wParam, lParam));
3925 
3926     case WM_SYSCOMMAND:
3927 	TRACE(("MAIN:WM_SYSCOMMAND %s at %d,%d\n",
3928 	       syscommand2s(LOWORD(wParam)),
3929 	       HIWORD(lParam),
3930 	       LOWORD(lParam)));
3931 	handle_builtin_menu(wParam);
3932 	return (DefWindowProc(hWnd, message, wParam, lParam));
3933 
3934 #if OPT_SCROLLBARS
3935     case WM_VSCROLL:
3936 	handle_scrollbar((HWND) lParam, LOWORD(wParam), HIWORD(wParam));
3937 	return (0);
3938 #endif
3939 
3940     case WM_SIZE:
3941 	if (initialized)
3942 	    resize_pending = TRUE;
3943 	/* FALL THROUGH */
3944     default:
3945 	return (TextWndProc(hWnd, message, wParam, lParam));
3946 
3947 	IGN_PROC("MAIN:", WM_ERASEBKGND);
3948     }
3949     return (1);
3950 }
3951 
3952 static BOOL
InitInstance(HINSTANCE hInstance)3953 InitInstance(HINSTANCE hInstance)
3954 {
3955     WNDCLASS wc;
3956 
3957     hglass_cursor = LoadCursor((HINSTANCE) 0, IDC_WAIT);
3958     arrow_cursor = LoadCursor((HINSTANCE) 0, IDC_ARROW);
3959     selection_cursor = LoadCursor((HINSTANCE) 0, IDC_IBEAM);
3960     wdwsize_cursor = LoadCursor((HINSTANCE) 0, IDC_SIZENS);
3961 
3962     default_bcolor = GetSysColor(COLOR_WINDOWTEXT + 1);
3963     default_fcolor = GetSysColor(COLOR_WINDOW + 1);
3964 
3965     ZeroMemory(&wc, sizeof(wc));
3966     wc.style = CS_VREDRAW | CS_HREDRAW;
3967     wc.lpfnWndProc = MainWndProc;
3968     wc.cbClsExtra = 0;
3969     wc.cbWndExtra = 0;
3970     wc.hInstance = hInstance;
3971     wc.hIcon = LoadIcon(hInstance, W32_STRING("VilewIcon"));
3972     wc.hCursor = arrow_cursor;
3973     wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
3974     wc.lpszMenuName = W32_STRING("VileMenu");
3975     wc.lpszClassName = MAIN_CLASS;
3976 
3977     if (!RegisterClass(&wc))
3978 	return FALSE;
3979 
3980     TRACE(("Registered(%s)\n", asc_charstring(MAIN_CLASS)));
3981 
3982     vile_hinstance = hInstance;
3983     hAccTable = LoadAccelerators(vile_hinstance, W32_STRING("VileAcc"));
3984 
3985     cur_win->main_hwnd = CreateWindow(
3986 					 MAIN_CLASS,
3987 					 MY_APPLE,
3988 					 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
3989 					 CW_USEDEFAULT,
3990 					 CW_USEDEFAULT,
3991 					 1,
3992 					 1,
3993 					 (HWND) 0,
3994 					 (HMENU) 0,
3995 					 hInstance,
3996 					 (LPVOID) 0
3997 	);
3998     TRACE(("CreateWindow(main) -> %p\n", cur_win->main_hwnd));
3999     if (!cur_win->main_hwnd)
4000 	return (FALSE);
4001 
4002     ZeroMemory(&wc, sizeof(wc));
4003     wc.style = CS_VREDRAW | CS_HREDRAW;
4004     wc.lpfnWndProc = (WNDPROC) TextWndProc;
4005     wc.cbClsExtra = 0;
4006     wc.cbWndExtra = 0;
4007     wc.hInstance = hInstance;
4008     wc.hIcon = 0;
4009     wc.hCursor = arrow_cursor;
4010     wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
4011     wc.lpszMenuName = 0;
4012     wc.lpszClassName = TEXT_CLASS;
4013 
4014     if (!RegisterClass(&wc))
4015 	return FALSE;
4016 
4017     TRACE(("Registered(%s)\n", asc_charstring(TEXT_CLASS)));
4018 
4019     cur_win->text_hwnd = CreateWindow(
4020 					 TEXT_CLASS,
4021 					 W32_STRING("text"),
4022 					 WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
4023 					 CW_USEDEFAULT,
4024 					 CW_USEDEFAULT,
4025 					 1,
4026 					 1,
4027 					 cur_win->main_hwnd,
4028 					 (HMENU) 0,
4029 					 hInstance,
4030 					 (LPVOID) 0
4031 	);
4032     TRACE(("CreateWindow(text) -> %p\n", cur_win->text_hwnd));
4033     if (!cur_win->text_hwnd)
4034 	return (FALSE);
4035 
4036     /*
4037      * Register the GRIP_CLASS now also, otherwise it won't succeed when
4038      * we create the first scrollbars, until we resize the window.
4039      */
4040 #if OPT_SCROLLBARS
4041     ZeroMemory(&wc, sizeof(wc));
4042     wc.style = CS_VREDRAW | CS_HREDRAW;
4043     wc.lpfnWndProc = (WNDPROC) GripWndProc;
4044     wc.cbClsExtra = 0;
4045     wc.cbWndExtra = 0;
4046     wc.hInstance = vile_hinstance;
4047     wc.hCursor = arrow_cursor;
4048     wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
4049     wc.lpszMenuName = 0;
4050     wc.lpszClassName = GRIP_CLASS;
4051 
4052     if (!RegisterClass(&wc)) {
4053 	TRACE(("could not register class %s:%#lx\n",
4054 	       asc_charstring(wc.lpszClassName), GetLastError()));
4055 	return (FALSE);
4056     }
4057     TRACE(("Registered(%s)\n", asc_charstring(wc.lpszClassName)));
4058 #endif
4059 
4060     cur_win->nscrollbars = -1;
4061 
4062     /* Insert Winvile's menu items in the system menu. */
4063     vile_menu = GetSystemMenu(cur_win->main_hwnd, FALSE);
4064 
4065 #define VL_APPEND(type,code,text) AppendMenu(vile_menu, type, code, w32_charstring(text))
4066 
4067     VL_APPEND(MF_SEPARATOR, 0, NULL);
4068     VL_APPEND(MF_STRING, IDM_OPEN, "&Open...");
4069     VL_APPEND(MF_STRING, IDM_SAVE_AS, "&Save As...");
4070     VL_APPEND(MF_STRING, IDM_CHDIR, "C&D...");
4071     VL_APPEND(MF_STRING, IDM_FAVORITES, "Fa&vorites...");
4072     VL_APPEND(MF_STRING, IDM_FONT, "&Font...");
4073     VL_APPEND(MF_STRING, IDM_ABOUT, "&About...");
4074     VL_APPEND(MF_SEPARATOR, 0, NULL);
4075     VL_APPEND(MF_STRING, IDM_PAGE_SETUP, "Page Set&up...");
4076     VL_APPEND(MF_STRING, IDM_PRINT, "&Print...");
4077     VL_APPEND(MF_SEPARATOR, 0, NULL);
4078 
4079     /*
4080      * NB -- don't change the order of the next 3 menu items!
4081      *
4082      * The popup menus associated with the next two menu items are created
4083      * as necessary.
4084      */
4085     VL_APPEND(MF_POPUP, 0, "Recent Fi&les");
4086     VL_APPEND(MF_POPUP, 0, "Recent Fol&ders");
4087     VL_APPEND(MF_SEPARATOR, IDM_SEP_AFTER_RCNT_FLDRS, NULL);
4088     /*
4089      * NB -- don't change the order of the previous 3 menu items!
4090      */
4091 
4092     VL_APPEND(MF_STRING | MF_CHECKED, IDM_MENU, "&Menu");
4093 
4094 #if OPT_SCROLLBARS
4095     if (check_scrollbar_allocs() != TRUE)
4096 	return (FALSE);
4097 #endif
4098 
4099     get_font(&vile_logfont);
4100     use_font(GetMyFont(0));
4101 
4102     DragAcceptFiles(cur_win->main_hwnd, TRUE);
4103 
4104     return (TRUE);
4105 }
4106 
4107 #if OPT_TRACE
4108 static void
show_argv(int argc,char ** argv,const char * tag)4109 show_argv(int argc, char **argv, const char *tag)
4110 {
4111     int n;
4112 
4113     TRACE(("show_argv(%s)\n", tag));
4114     for (n = 0; n < argc; ++n)
4115 	TRACE(("argv[%d]%s\n", n, argv[n]));
4116 }
4117 #else
4118 #define show_argv(argc, argv, tag)	/* nothing */
4119 #endif
4120 
4121 /*
4122  * Check for an option and remove it, returning nonzero if found.
4123  */
4124 static int
had_option(char ** argv,int * argc,char * option)4125 had_option(char **argv, int *argc, char *option)
4126 {
4127     int passed = 0;
4128     int result = 0;
4129     int n;
4130 
4131     for (n = 1; n < *argc; ++n) {
4132 	if (!passed && is_option(argv[n])) {
4133 	    if (!strcmp(argv[n], option))
4134 		result++;
4135 	} else {
4136 	    passed = 1;
4137 	}
4138 	if (result)
4139 	    argv[n] = argv[n + result];
4140     }
4141     *argc -= result;
4142     return result;
4143 }
4144 
4145 /* SAL macros were introduced long after Win32 API was defined */
4146 #ifndef _In_
4147 #define _In_
4148 #endif
4149 #ifndef _In_opt_
4150 #define _In_opt_
4151 #endif
4152 
4153 int WINAPI
WinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPSTR lpCmdLine,_In_ int nCmdShow)4154 WinMain(
4155 	   _In_ HINSTANCE hInstance,
4156 	   _In_opt_ HINSTANCE hPrevInstance,
4157 	   _In_ LPSTR lpCmdLine,
4158 	   _In_ int nCmdShow)
4159 {
4160     int argc;
4161     int n;
4162     char **argv = 0;
4163     char *argend = 0;
4164     char *fontstr;
4165 #ifdef VILE_OLE
4166     int oa_invoke, oa_reg;
4167 
4168     memset(&oa_opts, 0, sizeof(oa_opts));
4169     oa_invoke = oa_reg = FALSE;
4170 #endif
4171 
4172     (void) hPrevInstance;
4173     TRACE(("Starting ntvile, CmdLine:%s\n", lpCmdLine));
4174 
4175     if (make_argv("VILE", lpCmdLine, &argv, &argc, &argend) < 0)
4176 	ExitProgram(BADEXIT);
4177 
4178     /*
4179      * Special case for "Send To".  The shortcut for winvile must have a "-i"
4180      * after the name of the executable to distinguish this case from running
4181      * from the command-line.
4182      */
4183     show_argv(argc, argv, "before parsing -i");
4184     TRACE(("argend '%s'\n", NonNull(argend)));
4185     if ((argend != 0)
4186 	&& had_option(argv, &argc, "-i")
4187 	&& ffaccess(argend, FL_READABLE)) {
4188 
4189 	argc = after_options(1, argc, argv);
4190 	argv[argc++] = argend;
4191 	argv[argc] = 0;
4192 
4193 	cd_on_open = -1;
4194     }
4195     show_argv(argc, argv, "after parsing -i");
4196 
4197 #if 0
4198     SetProcessDPIAware();
4199 #endif
4200     /*
4201      * Set default values for options that accept parameters.
4202      */
4203     fontstr = 0;
4204 
4205     SetCols(80);
4206     SetRows(24);
4207 
4208     /*
4209      * Get screen size and OLE options, if any.  Parsing logic is
4210      * messy, but must remain that way to handle the various command
4211      * line options available with and without OLE automation.
4212      */
4213     for (n = 1; n < argc; n++) {
4214 	int m = n, eat = 0;
4215 	if (n + 1 < argc) {
4216 	    if (strcmp(argv[n], "-geometry") == 0) {
4217 		char *src = argv[n + 1];
4218 		char *dst = 0;
4219 		int value = strtol(src, &dst, 0);
4220 		if (dst != src) {
4221 		    if (value > 2)
4222 			SetCols(value);
4223 		    if (*dst++ == 'x') {
4224 			src = dst;
4225 			value = strtol(src, &dst, 0);
4226 			if (value > 2) {
4227 			    SetRows(value);
4228 #ifdef VILE_OLE
4229 			    oa_opts.cols = term.cols;
4230 			    oa_opts.rows = term.rows;
4231 #endif
4232 			}
4233 		    }
4234 		    eat = 2;
4235 		}
4236 	    } else if (strcmp(argv[n], "-font") == 0 ||
4237 		       strcmp(argv[n], "-fn") == 0) {
4238 		fontstr = argv[n + 1];
4239 		eat = 2;
4240 	    }
4241 	}
4242 #ifdef VILE_OLE
4243 	if (eat == 0) {
4244 	    /* No valid options seen yet. */
4245 
4246 	    if (argv[n][0] == '-' && argv[n][1] == 'O') {
4247 		int which = argv[n][2];
4248 
4249 		if (which == 'r') {
4250 		    /*
4251 		     * Flag OLE registration request,
4252 		     * but don't eat argument.  Instead,
4253 		     * registration will be carried out
4254 		     * in main.c, so that the regular
4255 		     * cmdline parser has an opportunity
4256 		     * to flag misspelled OLE options.
4257 		     *
4258 		     * Ex:  winvile -Or -multiple
4259 		     */
4260 
4261 		    oa_reg = TRUE;
4262 		} else if (which == 'u')
4263 		    ExitProgram(oleauto_unregister());
4264 		else if (which == 'a') {
4265 		    oa_invoke = TRUE;
4266 		    eat = 1;
4267 		}
4268 	    } else if (strcmp(argv[n], "-invisible") == 0) {
4269 		oa_opts.invisible = TRUE;
4270 		eat = 1;
4271 	    } else if (strcmp(argv[n], "-multiple") == 0) {
4272 		oa_opts.multiple = TRUE;
4273 		eat = 1;
4274 	    }
4275 	}
4276 #endif
4277 	if (eat) {
4278 	    while (m + eat <= argc) {
4279 		argv[m] = argv[m + eat];
4280 		m++;
4281 	    }
4282 	    n--;
4283 	    argc -= eat;
4284 	}
4285     }
4286 
4287 #ifdef VILE_OLE
4288     if (oa_reg && oa_invoke) {
4289 	/* tsk tsk */
4290 
4291 	show_ok_message("-Oa and -Or are mutually exclusive");
4292 	ExitProgram(BADEXIT);
4293     }
4294     if (oa_reg) {
4295 	/*
4296 	 * The main program's command-line parser will eventually cause
4297 	 * OLE automation registration to occur, at which point
4298 	 * winvile exits.  So don't show a window.
4299 	 */
4300 
4301 	nCmdShow = SW_HIDE;
4302     }
4303     if (oa_opts.invisible)
4304 	nCmdShow = SW_HIDE;
4305 #endif
4306     desired_wdw_state = nCmdShow;
4307 
4308     if (!InitInstance(hInstance))
4309 	return (FALSE);
4310 
4311     /*
4312      * Vile window created and default font set.  It's now kosher to set
4313      * the font from a cmdline switch.
4314      */
4315     if (fontstr) {
4316 	int success = ntwinio_font_frm_str(fontstr, TRUE);
4317 
4318 #ifdef VILE_OLE
4319 	if (oa_reg) {
4320 	    if (!success) {
4321 		/*
4322 		 * That's it, game over -- crummy font spec detected during
4323 		 * OLE registration.
4324 		 */
4325 
4326 		ExitProgram(BADEXIT);
4327 	    } else
4328 		oa_opts.fontstr = fontstr;
4329 	}
4330 #else
4331 	(void) success;
4332 	default_font = strmalloc(fontstr);
4333 #endif
4334 	/* Regardless of success or failure, continue with new/default font. */
4335     }
4336 #ifdef VILE_OLE
4337     if (oa_invoke) {
4338 	/* Initialize OLE Automation */
4339 
4340 	if (!oleauto_init(&oa_opts))
4341 	    ExitProgram(BADEXIT);
4342     }
4343 #endif
4344 
4345     return MainProgram(argc, argv);
4346 }
4347 
4348 /* return winvile's main window handle */
4349 void *
winvile_hwnd(void)4350 winvile_hwnd(void)
4351 {
4352     return (cur_win->main_hwnd);
4353 }
4354 
4355 /*
4356  * To prevent drawing winvile's main frame twice during startup (once for
4357  * the default font/geometry and then again when the user specifies
4358  * his/her preferences), this entry point is provided to do the job just
4359  * once.  Called from main.c .
4360  */
4361 void
winvile_start(void)4362 winvile_start(void)
4363 {
4364     RECT desktop, vile, tray;
4365     int moved_window = 0;
4366     HWND tray_hwnd, desktop_hwnd;
4367 
4368     enable_popup = global_g_val(GMDPOPUPMENU);
4369 
4370     /*
4371      * Before displaying the main window, see if its bottom border lies
4372      * beneath the Win95/NT taskbar.  If so, move the frame up out of the
4373      * way (hope this code works for Win98, too :-) ).
4374      */
4375     tray_hwnd = FindWindow(W32_STRING("Shell_TrayWnd"), NULL);
4376     desktop_hwnd = GetDesktopWindow();
4377     GetWindowRect(desktop_hwnd, &desktop);
4378     GetWindowRect(cur_win->main_hwnd, &vile);
4379     if (tray_hwnd != NULL) {
4380 	GetWindowRect(tray_hwnd, &tray);
4381 	if (vile.bottom > tray.top) {
4382 	    /*
4383 	     * Could be a conflict...but only if the taskbar is parked
4384 	     * horizontally, at bottom of screen.
4385 	     */
4386 
4387 	    if ((tray.bottom + 10 >= desktop.bottom) &&
4388 		(tray.right - tray.left + 10 >= desktop.right - desktop.left)) {
4389 		int diff = vile.bottom - tray.top + 5;
4390 
4391 		if (vile.top - diff >= desktop.top) {
4392 		    /* editor's title bar won't be shifted offscreen. */
4393 
4394 		    MoveWindow(cur_win->main_hwnd,
4395 			       vile.left,
4396 			       vile.top - diff,
4397 			       vile.right - vile.left,
4398 			       vile.bottom - vile.top,
4399 			       FALSE);
4400 		    moved_window = 1;
4401 		}
4402 	    }
4403 	}
4404     }
4405     if (!moved_window) {
4406 	/*
4407 	 * no conflict with the task bar...but is the editor's bottom edge
4408 	 * below the desktop rect?
4409 	 */
4410 
4411 	if (vile.bottom > desktop.bottom) {
4412 	    int diff = vile.bottom - desktop.bottom + 5;
4413 
4414 	    if (vile.top - diff >= desktop.top) {
4415 		/* editor's title bar won't be shifted offscreen. */
4416 
4417 		MoveWindow(cur_win->main_hwnd,
4418 			   vile.left,
4419 			   vile.top - diff,
4420 			   vile.right - vile.left,
4421 			   vile.bottom - vile.top,
4422 			   FALSE);
4423 	    }
4424 	}
4425     }
4426     ShowWindow(cur_win->main_hwnd, desired_wdw_state);
4427     UpdateWindow(cur_win->main_hwnd);
4428     caret_disabled = FALSE;
4429     fshow_cursor();
4430     initialized = TRUE;
4431 }
4432 
4433 /*
4434  * Unfortunately, winvile cannot use vile's standard cursor visible/invisible
4435  * API.  Instead, winvile manages its cursor state internally (and does a
4436  * pretty good job, at that).  However, there are times when the editor
4437  * makes display changes that require an external "override".
4438  *
4439  * Set queue_change (Boolean) true to defer cursor change until winvile next
4440  * reads its message queue).
4441  *
4442  * Returns:  Boolean (previous cursor visibility state).
4443  */
4444 int
winvile_cursor_state(int visible,int queue_change)4445 winvile_cursor_state(
4446 			int visible,	/* Boolean, T -> cursor on */
4447 			int queue_change)
4448 {
4449     int rc = caret_visible;
4450 
4451     if (!queue_change) {
4452 	if (visible) {
4453 	    /* don't set turn on cursor unless editor has focus */
4454 	    if (GetFocus() == winvile_hwnd())
4455 		fshow_cursor();
4456 	} else
4457 	    fhide_cursor();
4458     } else {
4459 	PostMessage(winvile_hwnd(),
4460 		    (visible) ? WM_WVILE_CURSOR_ON : WM_WVILE_CURSOR_OFF,
4461 		    0,
4462 		    0);
4463     }
4464     return (rc);
4465 }
4466 
4467 /*
4468  * winvile doesn't exit via a traditional event loop.  provide a hook
4469  * to cleanup allocated resources.
4470  */
4471 void
winvile_cleanup(void)4472 winvile_cleanup(void)
4473 {
4474     if (cur_win->main_hwnd)
4475 	DragAcceptFiles(cur_win->main_hwnd, FALSE);
4476     if (popup_menu) {
4477 	DestroyMenu(popup_menu);
4478 	popup_menu = NULL;	/* be re-entrant */
4479     }
4480 }
4481 
4482 #ifdef VILE_OLE
4483 void
ntwinio_oleauto_reg(void)4484 ntwinio_oleauto_reg(void)
4485 {
4486     /* Pound a bunch of OLE registration data into the registry & exit. */
4487 
4488     ExitProgram(oleauto_register(&oa_opts));
4489 }
4490 
4491 void
ntwinio_redirect_hwnd(int redirect)4492 ntwinio_redirect_hwnd(int redirect)
4493 {
4494     redirect_keys = redirect;
4495 }
4496 #endif
4497 
4498 /*
4499  * Handle changes to "popup-menu" global setting.
4500  */
4501 int
chgd_popupmenu(BUFFER * bp,VALARGS * args,int glob_vals,int testing)4502 chgd_popupmenu(BUFFER *bp, VALARGS * args, int glob_vals, int testing)
4503 {
4504     (void) bp;
4505     (void) args;
4506     (void) glob_vals;
4507 
4508     if (!testing)
4509 	enable_popup_menu();
4510     return TRUE;
4511 }
4512 
4513 #if OPT_ICURSOR
4514 
4515 /* supported syntax is described in chgd_icursor() */
4516 static int
parse_icursor_string(char * str)4517 parse_icursor_string(char *str)
4518 {
4519     int failed, rc = TRUE;
4520     ULONG style;
4521 
4522     style = vl_atoul(str, 10, &failed);
4523     if (failed || style > MAX_CURSOR_STYLE)
4524 	rc = FALSE;
4525     else {
4526 	icursor_style = style;
4527 	icursor = (style > 0);
4528     }
4529     return (rc);
4530 }
4531 
4532 /*
4533  * user changed icursor mode
4534  *
4535  * Insertion cursor mode is a string that specifies one of three values:
4536  *
4537  *     0 -> disable insertion cursor
4538  *     1 -> insert mode displays vertical bar, cmd mode displays block cursor
4539  *     2 -> cmd mode displays vertical bar, insert mode displays block cursor
4540  */
4541 int
chgd_icursor(BUFFER * bp,VALARGS * args,int glob_vals,int testing)4542 chgd_icursor(BUFFER *bp, VALARGS * args, int glob_vals, int testing)
4543 {
4544     int rc = TRUE;
4545 
4546     (void) bp;
4547     (void) glob_vals;
4548 
4549     if (!testing) {
4550 	char *val = args->global->vp->p;
4551 
4552 	if (!parse_icursor_string(val)) {
4553 	    mlforce("[invalid icursor syntax]");
4554 	    rc = FALSE;
4555 	} else {
4556 	    if (caret_visible) {
4557 		fhide_cursor();	/* Kill the old caret        */
4558 		fshow_cursor();	/* And bring it back to life */
4559 	    }
4560 	}
4561     }
4562     return (rc);
4563 }
4564 #endif /* OPT_ICURSOR */
4565 
4566 /*
4567  * Split the version-message to allow us to format with tabs, so the
4568  * proportional font doesn't look ugly.
4569  */
4570 static size_t
option_size(const char * option)4571 option_size(const char *option)
4572 {
4573     if (*option == '-') {
4574 	const char *next = skip_ctext(option);
4575 	if (next[0] == ' '
4576 	    && next[1] != ' '
4577 	    && next[1] != 0) {
4578 	    next = skip_ctext(next + 1);
4579 	    return next - option;
4580 	}
4581 	return 14;		/* use embedded blanks to fix the tabs ... */
4582     }
4583     return 0;
4584 }
4585 
4586 void
gui_version(char * program)4587 gui_version(char *program)
4588 {
4589     (void) program;
4590 
4591     ShowWindow(cur_win->main_hwnd, SW_HIDE);
4592     show_ok_message(getversion());
4593 }
4594 
4595 void
gui_usage(char * program,const char * const * options,size_t length)4596 gui_usage(char *program, const char *const *options, size_t length)
4597 {
4598     char *buf, *s;
4599     size_t need, n;
4600     const char *fmt1 = "%s\n\nOptions:\n";
4601     const char *fmt2 = "    %s\t%s\n";
4602     const char *fmt3 = "%s\n";
4603 
4604     (void) program;
4605 
4606     /*
4607      * Hide the (partly-constructed) main window.  It'll flash (FIXME).
4608      */
4609     ShowWindow(cur_win->main_hwnd, SW_HIDE);
4610 
4611     need = strlen(fmt1) + strlen(prognam);
4612     for (n = 0; n < length; n++) {
4613 	if (option_size(options[n]))
4614 	    need += strlen(fmt2) + strlen(options[n]);
4615 	else
4616 	    need += strlen(fmt3) + strlen(options[n]);
4617     }
4618 
4619     if ((buf = typeallocn(char, need)) != 0) {
4620 
4621 	sprintf(s = buf, fmt1, prognam);
4622 	for (n = 0; n < length; n++) {
4623 	    char temp[80];
4624 
4625 	    s += strlen(s);
4626 	    if ((need = option_size(options[n])) != 0) {
4627 		strncpy(temp, options[n], need);
4628 		temp[need] = EOS;
4629 		sprintf(s, fmt2, temp, skip_cblanks(options[n] + need));
4630 	    } else {
4631 		sprintf(s, fmt3, options[n]);
4632 	    }
4633 	}
4634 
4635 	show_ok_message(buf);
4636 	free(buf);
4637     }
4638 }
4639 
4640 /*
4641  * Standard terminal interface dispatch table. None of the fields point into
4642  * "termio" code.
4643  */
4644 
4645 TERM term =
4646 {
4647     NROW,
4648     NROW,
4649     NCOL,
4650     NCOL,
4651     ntwinio_set_encoding,
4652     ntwinio_get_encoding,
4653     ntwinio_open,
4654     ntwinio_close,
4655     ntwinio_kopen,
4656     ntwinio_kclose,
4657     nullterm_clean,
4658     nullterm_unclean,
4659     nullterm_openup,
4660     ntwinio_getch,
4661     ntwinio_putc,
4662     ntwinio_typahead,
4663     ntwinio_flush,
4664     ntwinio_move,
4665     ntwinio_eeol,
4666     ntwinio_eeop,
4667     ntwinio_beep,
4668     ntwinio_rev,
4669     ntwinio_cres,
4670 #if OPT_COLOR
4671     ntwinio_fcol,
4672     ntwinio_bcol,
4673     set_ctrans,
4674 #else
4675     nullterm_setfore,
4676     nullterm_setback,
4677     nullterm_setpal,
4678 #endif
4679     nullterm_setccol,
4680     ntwinio_scroll,
4681     nullterm_pflush,
4682 #if OPT_ICURSOR
4683     ntwinio_icursor,
4684 #else
4685     nullterm_icursor,
4686 #endif
4687 #if OPT_TITLE
4688     ntwinio_title,
4689 #else
4690     nullterm_settitle,
4691 #endif
4692     nullterm_watchfd,
4693     nullterm_unwatchfd,
4694     nullterm_cursorvis,
4695     nullterm_mopen,
4696     nullterm_mclose,
4697     nullterm_mevent,
4698 };
4699