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 ®ion, /* address of structure with scroll rectangle */
1945 ®ion, /* 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(¤t);
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(¤t);
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, ¤t, 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