Try to improve the caret usage
[sgt/putty] / window.c
1 #include <windows.h>
2 #include <commctrl.h>
3 #ifndef AUTO_WINSOCK
4 #ifdef WINSOCK_TWO
5 #include <winsock2.h>
6 #else
7 #include <winsock.h>
8 #endif
9 #endif
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <ctype.h>
13
14 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
15 #include "putty.h"
16 #include "storage.h"
17 #include "win_res.h"
18
19 #define IDM_SHOWLOG 0x0010
20 #define IDM_NEWSESS 0x0020
21 #define IDM_DUPSESS 0x0030
22 #define IDM_RECONF 0x0040
23 #define IDM_CLRSB 0x0050
24 #define IDM_RESET 0x0060
25 #define IDM_TEL_AYT 0x0070
26 #define IDM_TEL_BRK 0x0080
27 #define IDM_TEL_SYNCH 0x0090
28 #define IDM_TEL_EC 0x00a0
29 #define IDM_TEL_EL 0x00b0
30 #define IDM_TEL_GA 0x00c0
31 #define IDM_TEL_NOP 0x00d0
32 #define IDM_TEL_ABORT 0x00e0
33 #define IDM_TEL_AO 0x00f0
34 #define IDM_TEL_IP 0x0100
35 #define IDM_TEL_SUSP 0x0110
36 #define IDM_TEL_EOR 0x0120
37 #define IDM_TEL_EOF 0x0130
38 #define IDM_ABOUT 0x0140
39 #define IDM_SAVEDSESS 0x0150
40
41 #define IDM_SAVED_MIN 0x1000
42 #define IDM_SAVED_MAX 0x2000
43
44 #define WM_IGNORE_SIZE (WM_XUSER + 1)
45 #define WM_IGNORE_CLIP (WM_XUSER + 2)
46
47 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
48 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
49 static void cfgtopalette(void);
50 static void init_palette(void);
51 static void init_fonts(int);
52
53 static int extra_width, extra_height;
54
55 static int pending_netevent = 0;
56 static WPARAM pend_netevent_wParam = 0;
57 static LPARAM pend_netevent_lParam = 0;
58 static void enact_pending_netevent(void);
59
60 #define FONT_NORMAL 0
61 #define FONT_BOLD 1
62 #define FONT_UNDERLINE 2
63 #define FONT_BOLDUND 3
64 #define FONT_OEM 4
65 #define FONT_OEMBOLD 5
66 #define FONT_OEMBOLDUND 6
67 #define FONT_OEMUND 7
68 static HFONT fonts[8];
69 static int font_needs_hand_underlining;
70 static enum {
71 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
72 } bold_mode;
73 static enum {
74 UND_LINE, UND_FONT
75 } und_mode;
76 static int descent;
77
78 #define NCOLOURS 24
79 static COLORREF colours[NCOLOURS];
80 static HPALETTE pal;
81 static LPLOGPALETTE logpal;
82 static RGBTRIPLE defpal[NCOLOURS];
83
84 static HWND hwnd;
85
86 static HBITMAP caretbm;
87
88 static int dbltime, lasttime, lastact;
89 static Mouse_Button lastbtn;
90
91 static char *window_name, *icon_name;
92
93 static Ldisc *real_ldisc;
94
95 void begin_session(void) {
96 ldisc = real_ldisc;
97 }
98
99 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
100 static char appname[] = "PuTTY";
101 WORD winsock_ver;
102 WSADATA wsadata;
103 WNDCLASS wndclass;
104 MSG msg;
105 int guess_width, guess_height;
106
107 putty_inst = inst;
108 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
109
110 winsock_ver = MAKEWORD(1, 1);
111 if (WSAStartup(winsock_ver, &wsadata)) {
112 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
113 MB_OK | MB_ICONEXCLAMATION);
114 return 1;
115 }
116 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
117 MessageBox(NULL, "WinSock version is incompatible with 1.1",
118 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
119 WSACleanup();
120 return 1;
121 }
122 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
123
124 InitCommonControls();
125
126 /*
127 * Process the command line.
128 */
129 {
130 char *p;
131
132 default_protocol = DEFAULT_PROTOCOL;
133 default_port = DEFAULT_PORT;
134
135 do_defaults(NULL);
136
137 p = cmdline;
138 while (*p && isspace(*p)) p++;
139
140 /*
141 * Process command line options first. Yes, this can be
142 * done better, and it will be as soon as I have the
143 * energy...
144 */
145 while (*p == '-') {
146 char *q = p + strcspn(p, " \t");
147 p++;
148 if (q == p + 3 &&
149 tolower(p[0]) == 's' &&
150 tolower(p[1]) == 's' &&
151 tolower(p[2]) == 'h') {
152 default_protocol = cfg.protocol = PROT_SSH;
153 default_port = cfg.port = 22;
154 } else if (q == p + 3 &&
155 tolower(p[0]) == 'l' &&
156 tolower(p[1]) == 'o' &&
157 tolower(p[2]) == 'g') {
158 logfile = "putty.log";
159 } else if (q == p + 7 &&
160 tolower(p[0]) == 'c' &&
161 tolower(p[1]) == 'l' &&
162 tolower(p[2]) == 'e' &&
163 tolower(p[3]) == 'a' &&
164 tolower(p[4]) == 'n' &&
165 tolower(p[5]) == 'u' &&
166 tolower(p[6]) == 'p') {
167 /*
168 * `putty -cleanup'. Remove all registry entries
169 * associated with PuTTY, and also find and delete
170 * the random seed file.
171 */
172 if (MessageBox(NULL,
173 "This procedure will remove ALL Registry\n"
174 "entries associated with PuTTY, and will\n"
175 "also remove the PuTTY random seed file.\n"
176 "\n"
177 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
178 "SESSIONS. Are you really sure you want\n"
179 "to continue?",
180 "PuTTY Warning",
181 MB_YESNO | MB_ICONWARNING) == IDYES) {
182 cleanup_all();
183 }
184 exit(0);
185 }
186 p = q + strspn(q, " \t");
187 }
188
189 /*
190 * An initial @ means to activate a saved session.
191 */
192 if (*p == '@') {
193 do_defaults (p+1);
194 if (!*cfg.host && !do_config()) {
195 WSACleanup();
196 return 0;
197 }
198 } else if (*p == '&') {
199 /*
200 * An initial & means we've been given a command line
201 * containing the hex value of a HANDLE for a file
202 * mapping object, which we must then extract as a
203 * config.
204 */
205 HANDLE filemap;
206 Config *cp;
207 if (sscanf(p+1, "%p", &filemap) == 1 &&
208 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
209 0, 0, sizeof(Config))) != NULL) {
210 cfg = *cp;
211 UnmapViewOfFile(cp);
212 CloseHandle(filemap);
213 } else if (!do_config()) {
214 WSACleanup();
215 return 0;
216 }
217 } else if (*p) {
218 char *q = p;
219 /*
220 * If the hostname starts with "telnet:", set the
221 * protocol to Telnet and process the string as a
222 * Telnet URL.
223 */
224 if (!strncmp(q, "telnet:", 7)) {
225 char c;
226
227 q += 7;
228 if (q[0] == '/' && q[1] == '/')
229 q += 2;
230 cfg.protocol = PROT_TELNET;
231 p = q;
232 while (*p && *p != ':' && *p != '/') p++;
233 c = *p;
234 if (*p)
235 *p++ = '\0';
236 if (c == ':')
237 cfg.port = atoi(p);
238 else
239 cfg.port = -1;
240 strncpy (cfg.host, q, sizeof(cfg.host)-1);
241 cfg.host[sizeof(cfg.host)-1] = '\0';
242 } else {
243 while (*p && !isspace(*p)) p++;
244 if (*p)
245 *p++ = '\0';
246 strncpy (cfg.host, q, sizeof(cfg.host)-1);
247 cfg.host[sizeof(cfg.host)-1] = '\0';
248 while (*p && isspace(*p)) p++;
249 if (*p)
250 cfg.port = atoi(p);
251 else
252 cfg.port = -1;
253 }
254 } else {
255 if (!do_config()) {
256 WSACleanup();
257 return 0;
258 }
259 }
260
261 /* See if host is of the form user@host */
262 if (cfg.host[0] != '\0') {
263 char *atsign = strchr(cfg.host, '@');
264 /* Make sure we're not overflowing the user field */
265 if (atsign) {
266 if (atsign-cfg.host < sizeof cfg.username) {
267 strncpy (cfg.username, cfg.host, atsign-cfg.host);
268 cfg.username[atsign-cfg.host] = '\0';
269 }
270 memmove(cfg.host, atsign+1, 1+strlen(atsign+1));
271 }
272 }
273 }
274
275 /*
276 * Select protocol. This is farmed out into a table in a
277 * separate file to enable an ssh-free variant.
278 */
279 {
280 int i;
281 back = NULL;
282 for (i = 0; backends[i].backend != NULL; i++)
283 if (backends[i].protocol == cfg.protocol) {
284 back = backends[i].backend;
285 break;
286 }
287 if (back == NULL) {
288 MessageBox(NULL, "Unsupported protocol number found",
289 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
290 WSACleanup();
291 return 1;
292 }
293 }
294
295 real_ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
296 /* To start with, we use the simple line discipline, so we can
297 * type passwords etc without fear of them being echoed... */
298 ldisc = &ldisc_simple;
299
300 if (!prev) {
301 wndclass.style = 0;
302 wndclass.lpfnWndProc = WndProc;
303 wndclass.cbClsExtra = 0;
304 wndclass.cbWndExtra = 0;
305 wndclass.hInstance = inst;
306 wndclass.hIcon = LoadIcon (inst,
307 MAKEINTRESOURCE(IDI_MAINICON));
308 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
309 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
310 wndclass.lpszMenuName = NULL;
311 wndclass.lpszClassName = appname;
312
313 RegisterClass (&wndclass);
314 }
315
316 hwnd = NULL;
317
318 savelines = cfg.savelines;
319 term_init();
320
321 cfgtopalette();
322
323 /*
324 * Guess some defaults for the window size. This all gets
325 * updated later, so we don't really care too much. However, we
326 * do want the font width/height guesses to correspond to a
327 * large font rather than a small one...
328 */
329
330 font_width = 10;
331 font_height = 20;
332 extra_width = 25;
333 extra_height = 28;
334 term_size (cfg.height, cfg.width, cfg.savelines);
335 guess_width = extra_width + font_width * cols;
336 guess_height = extra_height + font_height * rows;
337 {
338 RECT r;
339 HWND w = GetDesktopWindow();
340 GetWindowRect (w, &r);
341 if (guess_width > r.right - r.left)
342 guess_width = r.right - r.left;
343 if (guess_height > r.bottom - r.top)
344 guess_height = r.bottom - r.top;
345 }
346
347 {
348 int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
349 if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
350 if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
351 hwnd = CreateWindow (appname, appname,
352 winmode,
353 CW_USEDEFAULT, CW_USEDEFAULT,
354 guess_width, guess_height,
355 NULL, NULL, inst, NULL);
356 }
357
358 /*
359 * Initialise the fonts, simultaneously correcting the guesses
360 * for font_{width,height}.
361 */
362 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
363 und_mode = UND_FONT;
364 init_fonts(0);
365
366 /*
367 * Correct the guesses for extra_{width,height}.
368 */
369 {
370 RECT cr, wr;
371 GetWindowRect (hwnd, &wr);
372 GetClientRect (hwnd, &cr);
373 extra_width = wr.right - wr.left - cr.right + cr.left;
374 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
375 }
376
377 /*
378 * Resize the window, now we know what size we _really_ want it
379 * to be.
380 */
381 guess_width = extra_width + font_width * cols;
382 guess_height = extra_height + font_height * rows;
383 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
384 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
385 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
386
387 /*
388 * Set up a caret bitmap, with no content.
389 */
390 {
391 char *bits;
392 int size = (font_width+15)/16 * 2 * font_height;
393 bits = calloc(size, 1);
394 memset(bits, 0x55, size);
395 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
396 free(bits);
397 }
398
399 /*
400 * Initialise the scroll bar.
401 */
402 {
403 SCROLLINFO si;
404
405 si.cbSize = sizeof(si);
406 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
407 si.nMin = 0;
408 si.nMax = rows-1;
409 si.nPage = rows;
410 si.nPos = 0;
411 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
412 }
413
414 /*
415 * Start up the telnet connection.
416 */
417 {
418 char *error;
419 char msg[1024];
420 char *realhost;
421
422 error = back->init (hwnd, cfg.host, cfg.port, &realhost);
423 if (error) {
424 sprintf(msg, "Unable to open connection:\n%s", error);
425 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
426 return 0;
427 }
428 window_name = icon_name = NULL;
429 sprintf(msg, "%s - PuTTY", realhost);
430 set_title (msg);
431 set_icon (msg);
432 }
433
434 session_closed = FALSE;
435
436 /*
437 * Set up the input and output buffers.
438 */
439 inbuf_head = 0;
440 outbuf_reap = outbuf_head = 0;
441
442 /*
443 * Prepare the mouse handler.
444 */
445 lastact = MA_NOTHING;
446 lastbtn = MB_NOTHING;
447 dbltime = GetDoubleClickTime();
448
449 /*
450 * Set up the session-control options on the system menu.
451 */
452 {
453 HMENU m = GetSystemMenu (hwnd, FALSE);
454 HMENU p,s;
455 int i;
456
457 AppendMenu (m, MF_SEPARATOR, 0, 0);
458 if (cfg.protocol == PROT_TELNET) {
459 p = CreateMenu();
460 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
461 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
462 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
463 AppendMenu (p, MF_SEPARATOR, 0, 0);
464 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
465 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
466 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
467 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
468 AppendMenu (p, MF_SEPARATOR, 0, 0);
469 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
470 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
471 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
472 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
473 AppendMenu (p, MF_SEPARATOR, 0, 0);
474 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
475 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
476 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
477 AppendMenu (m, MF_SEPARATOR, 0, 0);
478 }
479 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
480 AppendMenu (m, MF_SEPARATOR, 0, 0);
481 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
482 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
483 s = CreateMenu();
484 get_sesslist(TRUE);
485 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
486 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
487 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
488 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
489 AppendMenu (m, MF_SEPARATOR, 0, 0);
490 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
491 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
492 AppendMenu (m, MF_SEPARATOR, 0, 0);
493 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
494 }
495
496 /*
497 * Finally show the window!
498 */
499 ShowWindow (hwnd, show);
500
501 /*
502 * Set the palette up.
503 */
504 pal = NULL;
505 logpal = NULL;
506 init_palette();
507
508 has_focus = (GetForegroundWindow() == hwnd);
509 UpdateWindow (hwnd);
510
511 {
512 int timer_id = 0, long_timer = 0;
513
514 while (GetMessage (&msg, NULL, 0, 0) == 1) {
515 /* Sometimes DispatchMessage calls routines that use their own
516 * GetMessage loop, setup this timer so we get some control back.
517 *
518 * Also call term_update() from the timer so that if the host
519 * is sending data flat out we still do redraws.
520 */
521 if(timer_id && long_timer) {
522 KillTimer(hwnd, timer_id);
523 long_timer = timer_id = 0;
524 }
525 if(!timer_id)
526 timer_id = SetTimer(hwnd, 1, 20, NULL);
527 DispatchMessage (&msg);
528
529 /* This is too fast, but I'll leave it for now 'cause it shows
530 * how often term_update is called (far too often at times!)
531 */
532 term_blink(0);
533
534 /* Send the paste buffer if there's anything to send */
535 term_paste();
536
537 /* If there's nothing new in the queue then we can do everything
538 * we've delayed, reading the socket, writing, and repainting
539 * the window.
540 */
541 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
542 if (pending_netevent) {
543 enact_pending_netevent();
544
545 term_blink(1);
546 }
547 } else continue;
548 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
549 if (timer_id) {
550 KillTimer(hwnd, timer_id);
551 timer_id = 0;
552 }
553 HideCaret(hwnd);
554 if (inbuf_head)
555 term_out();
556 term_update();
557 ShowCaret(hwnd);
558 if (!has_focus)
559 timer_id = SetTimer(hwnd, 1, 2000, NULL);
560 else if (cfg.blinktext)
561 timer_id = SetTimer(hwnd, 1, 250, NULL);
562 else
563 timer_id = SetTimer(hwnd, 1, 500, NULL);
564 long_timer = 1;
565 }
566 }
567 }
568
569 /*
570 * Clean up.
571 */
572 {
573 int i;
574 for (i=0; i<8; i++)
575 if (fonts[i])
576 DeleteObject(fonts[i]);
577 }
578 sfree(logpal);
579 if (pal)
580 DeleteObject(pal);
581 WSACleanup();
582
583 if (cfg.protocol == PROT_SSH) {
584 random_save_seed();
585 #ifdef MSCRYPTOAPI
586 crypto_wrapup();
587 #endif
588 }
589
590 return msg.wParam;
591 }
592
593 /*
594 * Print a message box and close the connection.
595 */
596 void connection_fatal(char *fmt, ...) {
597 va_list ap;
598 char stuff[200];
599
600 va_start(ap, fmt);
601 vsprintf(stuff, fmt, ap);
602 va_end(ap);
603 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
604 if (cfg.close_on_exit)
605 PostQuitMessage(1);
606 else {
607 session_closed = TRUE;
608 SetWindowText (hwnd, "PuTTY (inactive)");
609 }
610 }
611
612 /*
613 * Actually do the job requested by a WM_NETEVENT
614 */
615 static void enact_pending_netevent(void) {
616 int i;
617 static int reentering = 0;
618
619 if (reentering)
620 return; /* don't unpend the pending */
621
622 pending_netevent = FALSE;
623
624 reentering = 1;
625 i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
626 reentering = 0;
627
628 if (i < 0) {
629 char buf[1024];
630 switch (WSABASEERR + (-i) % 10000) {
631 case WSAECONNRESET:
632 sprintf(buf, "Connection reset by peer");
633 break;
634 default:
635 sprintf(buf, "Unexpected network error %d", -i);
636 break;
637 }
638 connection_fatal(buf);
639 }
640 if (i <= 0) {
641 if (cfg.close_on_exit)
642 PostQuitMessage(0);
643 else {
644 session_closed = TRUE;
645 MessageBox(hwnd, "Connection closed by remote host",
646 "PuTTY", MB_OK | MB_ICONINFORMATION);
647 SetWindowText (hwnd, "PuTTY (inactive)");
648 }
649 }
650 }
651
652 /*
653 * Copy the colour palette from the configuration data into defpal.
654 * This is non-trivial because the colour indices are different.
655 */
656 static void cfgtopalette(void) {
657 int i;
658 static const int ww[] = {
659 6, 7, 8, 9, 10, 11, 12, 13,
660 14, 15, 16, 17, 18, 19, 20, 21,
661 0, 1, 2, 3, 4, 4, 5, 5
662 };
663
664 for (i=0; i<24; i++) {
665 int w = ww[i];
666 defpal[i].rgbtRed = cfg.colours[w][0];
667 defpal[i].rgbtGreen = cfg.colours[w][1];
668 defpal[i].rgbtBlue = cfg.colours[w][2];
669 }
670 }
671
672 /*
673 * Set up the colour palette.
674 */
675 static void init_palette(void) {
676 int i;
677 HDC hdc = GetDC (hwnd);
678 if (hdc) {
679 if (cfg.try_palette &&
680 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
681 logpal = smalloc(sizeof(*logpal)
682 - sizeof(logpal->palPalEntry)
683 + NCOLOURS * sizeof(PALETTEENTRY));
684 logpal->palVersion = 0x300;
685 logpal->palNumEntries = NCOLOURS;
686 for (i = 0; i < NCOLOURS; i++) {
687 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
688 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
689 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
690 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
691 }
692 pal = CreatePalette (logpal);
693 if (pal) {
694 SelectPalette (hdc, pal, FALSE);
695 RealizePalette (hdc);
696 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
697 FALSE);
698 }
699 }
700 ReleaseDC (hwnd, hdc);
701 }
702 if (pal)
703 for (i=0; i<NCOLOURS; i++)
704 colours[i] = PALETTERGB(defpal[i].rgbtRed,
705 defpal[i].rgbtGreen,
706 defpal[i].rgbtBlue);
707 else
708 for(i=0; i<NCOLOURS; i++)
709 colours[i] = RGB(defpal[i].rgbtRed,
710 defpal[i].rgbtGreen,
711 defpal[i].rgbtBlue);
712 }
713
714 /*
715 * Initialise all the fonts we will need. There may be as many as
716 * eight or as few as one. We also:
717 *
718 * - check the font width and height, correcting our guesses if
719 * necessary.
720 *
721 * - verify that the bold font is the same width as the ordinary
722 * one, and engage shadow bolding if not.
723 *
724 * - verify that the underlined font is the same width as the
725 * ordinary one (manual underlining by means of line drawing can
726 * be done in a pinch).
727 */
728 static void init_fonts(int pick_width) {
729 TEXTMETRIC tm;
730 int i;
731 int fsize[8];
732 HDC hdc;
733 int fw_dontcare, fw_bold;
734 int firstchar = ' ';
735
736 #ifdef CHECKOEMFONT
737 font_messup:
738 #endif
739 for (i=0; i<8; i++)
740 fonts[i] = NULL;
741
742 if (cfg.fontisbold) {
743 fw_dontcare = FW_BOLD;
744 fw_bold = FW_BLACK;
745 } else {
746 fw_dontcare = FW_DONTCARE;
747 fw_bold = FW_BOLD;
748 }
749
750 hdc = GetDC(hwnd);
751
752 font_height = cfg.fontheight;
753 font_width = pick_width;
754
755 #define f(i,c,w,u) \
756 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
757 c, OUT_DEFAULT_PRECIS, \
758 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
759 FIXED_PITCH | FF_DONTCARE, cfg.font)
760
761 if (cfg.vtmode != VT_OEMONLY) {
762 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
763
764 SelectObject (hdc, fonts[FONT_NORMAL]);
765 GetTextMetrics(hdc, &tm);
766 font_height = tm.tmHeight;
767 font_width = tm.tmAveCharWidth;
768
769 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
770
771 /*
772 * Some fonts, e.g. 9-pt Courier, draw their underlines
773 * outside their character cell. We successfully prevent
774 * screen corruption by clipping the text output, but then
775 * we lose the underline completely. Here we try to work
776 * out whether this is such a font, and if it is, we set a
777 * flag that causes underlines to be drawn by hand.
778 *
779 * Having tried other more sophisticated approaches (such
780 * as examining the TEXTMETRIC structure or requesting the
781 * height of a string), I think we'll do this the brute
782 * force way: we create a small bitmap, draw an underlined
783 * space on it, and test to see whether any pixels are
784 * foreground-coloured. (Since we expect the underline to
785 * go all the way across the character cell, we only search
786 * down a single column of the bitmap, half way across.)
787 */
788 {
789 HDC und_dc;
790 HBITMAP und_bm, und_oldbm;
791 int i, gotit;
792 COLORREF c;
793
794 und_dc = CreateCompatibleDC(hdc);
795 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
796 und_oldbm = SelectObject(und_dc, und_bm);
797 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
798 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
799 SetTextColor (und_dc, RGB(255,255,255));
800 SetBkColor (und_dc, RGB(0,0,0));
801 SetBkMode (und_dc, OPAQUE);
802 ExtTextOut (und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
803 gotit = FALSE;
804 for (i = 0; i < font_height; i++) {
805 c = GetPixel(und_dc, font_width/2, i);
806 if (c != RGB(0,0,0))
807 gotit = TRUE;
808 }
809 SelectObject(und_dc, und_oldbm);
810 DeleteObject(und_bm);
811 DeleteDC(und_dc);
812 font_needs_hand_underlining = !gotit;
813 }
814
815 if (bold_mode == BOLD_FONT) {
816 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
817 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
818 }
819
820 if (cfg.vtmode == VT_OEMANSI) {
821 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
822 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
823
824 if (bold_mode == BOLD_FONT) {
825 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
826 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
827 }
828 }
829 }
830 else
831 {
832 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
833
834 SelectObject (hdc, fonts[FONT_OEM]);
835 GetTextMetrics(hdc, &tm);
836 font_height = tm.tmHeight;
837 font_width = tm.tmAveCharWidth;
838
839 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
840
841 if (bold_mode == BOLD_FONT) {
842 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
843 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
844 }
845 }
846 #undef f
847
848 descent = tm.tmAscent + 1;
849 if (descent >= font_height)
850 descent = font_height - 1;
851 firstchar = tm.tmFirstChar;
852
853 for (i=0; i<8; i++) {
854 if (fonts[i]) {
855 if (SelectObject (hdc, fonts[i]) &&
856 GetTextMetrics(hdc, &tm) )
857 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
858 else fsize[i] = -i;
859 }
860 else fsize[i] = -i;
861 }
862
863 ReleaseDC (hwnd, hdc);
864
865 /* ... This is wrong in OEM only mode */
866 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
867 (bold_mode == BOLD_FONT &&
868 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
869 und_mode = UND_LINE;
870 DeleteObject (fonts[FONT_UNDERLINE]);
871 if (bold_mode == BOLD_FONT)
872 DeleteObject (fonts[FONT_BOLDUND]);
873 }
874
875 if (bold_mode == BOLD_FONT &&
876 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
877 bold_mode = BOLD_SHADOW;
878 DeleteObject (fonts[FONT_BOLD]);
879 if (und_mode == UND_FONT)
880 DeleteObject (fonts[FONT_BOLDUND]);
881 }
882
883 #ifdef CHECKOEMFONT
884 /* With the fascist font painting it doesn't matter if the linedraw font
885 * isn't exactly the right size anymore so we don't have to check this.
886 */
887 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
888 if( cfg.fontcharset == OEM_CHARSET )
889 {
890 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
891 "different sizes. Using OEM-only mode instead",
892 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
893 cfg.vtmode = VT_OEMONLY;
894 }
895 else if( firstchar < ' ' )
896 {
897 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
898 "different sizes. Using XTerm mode instead",
899 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
900 cfg.vtmode = VT_XWINDOWS;
901 }
902 else
903 {
904 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
905 "different sizes. Using ISO8859-1 mode instead",
906 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
907 cfg.vtmode = VT_POORMAN;
908 }
909
910 for (i=0; i<8; i++)
911 if (fonts[i])
912 DeleteObject (fonts[i]);
913 goto font_messup;
914 }
915 #endif
916 }
917
918 void request_resize (int w, int h, int refont) {
919 int width, height;
920
921 /* If the window is maximized supress resizing attempts */
922 if(IsZoomed(hwnd)) return;
923
924 #ifdef CHECKOEMFONT
925 /* Don't do this in OEMANSI, you may get disable messages */
926 if (refont && w != cols && (cols==80 || cols==132)
927 && cfg.vtmode != VT_OEMANSI)
928 #else
929 if (refont && w != cols && (cols==80 || cols==132))
930 #endif
931 {
932 /* If font width too big for screen should we shrink the font more ? */
933 if (w==132)
934 font_width = ((font_width*cols+w/2)/w);
935 else
936 font_width = 0;
937 {
938 int i;
939 for (i=0; i<8; i++)
940 if (fonts[i])
941 DeleteObject(fonts[i]);
942 }
943 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
944 und_mode = UND_FONT;
945 init_fonts(font_width);
946 }
947 else
948 {
949 static int first_time = 1;
950 static RECT ss;
951
952 switch(first_time)
953 {
954 case 1:
955 /* Get the size of the screen */
956 if (GetClientRect(GetDesktopWindow(),&ss))
957 /* first_time = 0 */;
958 else { first_time = 2; break; }
959 case 0:
960 /* Make sure the values are sane */
961 width = (ss.right-ss.left-extra_width ) / font_width;
962 height = (ss.bottom-ss.top-extra_height ) / font_height;
963
964 if (w>width) w=width;
965 if (h>height) h=height;
966 if (w<15) w = 15;
967 if (h<1) w = 1;
968 }
969 }
970
971 width = extra_width + font_width * w;
972 height = extra_height + font_height * h;
973
974 SetWindowPos (hwnd, NULL, 0, 0, width, height,
975 SWP_NOACTIVATE | SWP_NOCOPYBITS |
976 SWP_NOMOVE | SWP_NOZORDER);
977 }
978
979 static void click (Mouse_Button b, int x, int y) {
980 int thistime = GetMessageTime();
981
982 if (lastbtn == b && thistime - lasttime < dbltime) {
983 lastact = (lastact == MA_CLICK ? MA_2CLK :
984 lastact == MA_2CLK ? MA_3CLK :
985 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
986 } else {
987 lastbtn = b;
988 lastact = MA_CLICK;
989 }
990 if (lastact != MA_NOTHING)
991 term_mouse (b, lastact, x, y);
992 lasttime = thistime;
993 }
994
995 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
996 WPARAM wParam, LPARAM lParam) {
997 HDC hdc;
998 static int ignore_size = FALSE;
999 static int ignore_clip = FALSE;
1000 static int just_reconfigged = FALSE;
1001 static int resizing = FALSE;
1002
1003 switch (message) {
1004 case WM_TIMER:
1005 if (pending_netevent)
1006 enact_pending_netevent();
1007 if (inbuf_head)
1008 term_out();
1009 HideCaret(hwnd);
1010 term_update();
1011 ShowCaret(hwnd);
1012 return 0;
1013 case WM_CREATE:
1014 break;
1015 case WM_CLOSE:
1016 if (!cfg.warn_on_close || session_closed ||
1017 MessageBox(hwnd, "Are you sure you want to close this session?",
1018 "PuTTY Exit Confirmation",
1019 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1020 DestroyWindow(hwnd);
1021 return 0;
1022 case WM_DESTROY:
1023 PostQuitMessage (0);
1024 return 0;
1025 case WM_SYSCOMMAND:
1026 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1027 case IDM_SHOWLOG:
1028 showeventlog(hwnd);
1029 break;
1030 case IDM_NEWSESS:
1031 case IDM_DUPSESS:
1032 case IDM_SAVEDSESS:
1033 {
1034 char b[2048];
1035 char c[30], *cl;
1036 int freecl = FALSE;
1037 STARTUPINFO si;
1038 PROCESS_INFORMATION pi;
1039 HANDLE filemap = NULL;
1040
1041 if (wParam == IDM_DUPSESS) {
1042 /*
1043 * Allocate a file-mapping memory chunk for the
1044 * config structure.
1045 */
1046 SECURITY_ATTRIBUTES sa;
1047 Config *p;
1048
1049 sa.nLength = sizeof(sa);
1050 sa.lpSecurityDescriptor = NULL;
1051 sa.bInheritHandle = TRUE;
1052 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
1053 &sa,
1054 PAGE_READWRITE,
1055 0,
1056 sizeof(Config),
1057 NULL);
1058 if (filemap) {
1059 p = (Config *)MapViewOfFile(filemap,
1060 FILE_MAP_WRITE,
1061 0, 0, sizeof(Config));
1062 if (p) {
1063 *p = cfg; /* structure copy */
1064 UnmapViewOfFile(p);
1065 }
1066 }
1067 sprintf(c, "putty &%p", filemap);
1068 cl = c;
1069 } else if (wParam == IDM_SAVEDSESS) {
1070 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
1071 cl = malloc(16 + strlen(session)); /* 8, but play safe */
1072 if (!cl)
1073 cl = NULL; /* not a very important failure mode */
1074 else {
1075 sprintf(cl, "putty @%s", session);
1076 freecl = TRUE;
1077 }
1078 } else
1079 cl = NULL;
1080
1081 GetModuleFileName (NULL, b, sizeof(b)-1);
1082 si.cb = sizeof(si);
1083 si.lpReserved = NULL;
1084 si.lpDesktop = NULL;
1085 si.lpTitle = NULL;
1086 si.dwFlags = 0;
1087 si.cbReserved2 = 0;
1088 si.lpReserved2 = NULL;
1089 CreateProcess (b, cl, NULL, NULL, TRUE,
1090 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1091
1092 if (filemap)
1093 CloseHandle(filemap);
1094 if (freecl)
1095 free(cl);
1096 }
1097 break;
1098 case IDM_RECONF:
1099 if (!do_reconfig(hwnd))
1100 break;
1101 just_reconfigged = TRUE;
1102 {
1103 int i;
1104 for (i=0; i<8; i++)
1105 if (fonts[i])
1106 DeleteObject(fonts[i]);
1107 }
1108 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1109 und_mode = UND_FONT;
1110 init_fonts(0);
1111 sfree(logpal);
1112 /* Telnet will change local echo -> remote if the remote asks */
1113 if (cfg.protocol != PROT_TELNET)
1114 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
1115 if (pal)
1116 DeleteObject(pal);
1117 logpal = NULL;
1118 pal = NULL;
1119 cfgtopalette();
1120 init_palette();
1121
1122 /* Enable or disable the scroll bar, etc */
1123 {
1124 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1125
1126 nflg = flag;
1127 if (cfg.scrollbar) nflg |= WS_VSCROLL;
1128 else nflg &= ~WS_VSCROLL;
1129 if (cfg.locksize)
1130 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1131 else
1132 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1133
1134 if (nflg != flag)
1135 {
1136 RECT cr, wr;
1137
1138 SetWindowLong(hwnd, GWL_STYLE, nflg);
1139 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1140 SetWindowPos(hwnd, NULL, 0,0,0,0,
1141 SWP_NOACTIVATE|SWP_NOCOPYBITS|
1142 SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER|
1143 SWP_FRAMECHANGED);
1144
1145 GetWindowRect (hwnd, &wr);
1146 GetClientRect (hwnd, &cr);
1147 extra_width = wr.right - wr.left - cr.right + cr.left;
1148 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1149 }
1150 }
1151
1152 term_size(cfg.height, cfg.width, cfg.savelines);
1153 InvalidateRect(hwnd, NULL, TRUE);
1154 SetWindowPos (hwnd, NULL, 0, 0,
1155 extra_width + font_width * cfg.width,
1156 extra_height + font_height * cfg.height,
1157 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1158 SWP_NOMOVE | SWP_NOZORDER);
1159 if (IsIconic(hwnd)) {
1160 SetWindowText (hwnd,
1161 cfg.win_name_always ? window_name : icon_name);
1162 }
1163 break;
1164 case IDM_CLRSB:
1165 term_clrsb();
1166 break;
1167 case IDM_RESET:
1168 term_pwron();
1169 break;
1170 case IDM_TEL_AYT: back->special (TS_AYT); break;
1171 case IDM_TEL_BRK: back->special (TS_BRK); break;
1172 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1173 case IDM_TEL_EC: back->special (TS_EC); break;
1174 case IDM_TEL_EL: back->special (TS_EL); break;
1175 case IDM_TEL_GA: back->special (TS_GA); break;
1176 case IDM_TEL_NOP: back->special (TS_NOP); break;
1177 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1178 case IDM_TEL_AO: back->special (TS_AO); break;
1179 case IDM_TEL_IP: back->special (TS_IP); break;
1180 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1181 case IDM_TEL_EOR: back->special (TS_EOR); break;
1182 case IDM_TEL_EOF: back->special (TS_EOF); break;
1183 case IDM_ABOUT:
1184 showabout (hwnd);
1185 break;
1186 default:
1187 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1188 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1189 }
1190 }
1191 break;
1192
1193 #define X_POS(l) ((int)(short)LOWORD(l))
1194 #define Y_POS(l) ((int)(short)HIWORD(l))
1195
1196 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1197 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1198
1199 case WM_LBUTTONDOWN:
1200 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1201 TO_CHR_Y(Y_POS(lParam)));
1202 SetCapture(hwnd);
1203 return 0;
1204 case WM_LBUTTONUP:
1205 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1206 TO_CHR_Y(Y_POS(lParam)));
1207 ReleaseCapture();
1208 return 0;
1209 case WM_MBUTTONDOWN:
1210 SetCapture(hwnd);
1211 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1212 TO_CHR_X(X_POS(lParam)),
1213 TO_CHR_Y(Y_POS(lParam)));
1214 return 0;
1215 case WM_MBUTTONUP:
1216 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1217 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1218 TO_CHR_Y(Y_POS(lParam)));
1219 ReleaseCapture();
1220 return 0;
1221 case WM_RBUTTONDOWN:
1222 SetCapture(hwnd);
1223 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1224 TO_CHR_X(X_POS(lParam)),
1225 TO_CHR_Y(Y_POS(lParam)));
1226 return 0;
1227 case WM_RBUTTONUP:
1228 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1229 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1230 TO_CHR_Y(Y_POS(lParam)));
1231 ReleaseCapture();
1232 return 0;
1233 case WM_MOUSEMOVE:
1234 /*
1235 * Add the mouse position and message time to the random
1236 * number noise, if we're using ssh.
1237 */
1238 if (cfg.protocol == PROT_SSH)
1239 noise_ultralight(lParam);
1240
1241 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1242 Mouse_Button b;
1243 if (wParam & MK_LBUTTON)
1244 b = MB_SELECT;
1245 else if (wParam & MK_MBUTTON)
1246 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1247 else
1248 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1249 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1250 TO_CHR_Y(Y_POS(lParam)));
1251 }
1252 return 0;
1253 case WM_IGNORE_CLIP:
1254 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1255 break;
1256 case WM_DESTROYCLIPBOARD:
1257 if (!ignore_clip)
1258 term_deselect();
1259 ignore_clip = FALSE;
1260 return 0;
1261 case WM_PAINT:
1262 {
1263 PAINTSTRUCT p;
1264 HideCaret(hwnd);
1265 hdc = BeginPaint (hwnd, &p);
1266 if (pal) {
1267 SelectPalette (hdc, pal, TRUE);
1268 RealizePalette (hdc);
1269 }
1270 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1271 p.rcPaint.right, p.rcPaint.bottom);
1272 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1273 SelectObject (hdc, GetStockObject(WHITE_PEN));
1274 EndPaint (hwnd, &p);
1275 ShowCaret(hwnd);
1276 }
1277 return 0;
1278 case WM_NETEVENT:
1279 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1280 * but the only one that's likely to try to overload us is FD_READ.
1281 * This means buffering just one is fine.
1282 */
1283 if (pending_netevent)
1284 enact_pending_netevent();
1285
1286 pending_netevent = TRUE;
1287 pend_netevent_wParam=wParam;
1288 pend_netevent_lParam=lParam;
1289 return 0;
1290 case WM_SETFOCUS:
1291 has_focus = TRUE;
1292 CreateCaret(hwnd, caretbm, 0, 0);
1293 ShowCaret(hwnd);
1294 term_out();
1295 term_update();
1296 break;
1297 case WM_KILLFOCUS:
1298 has_focus = FALSE;
1299 DestroyCaret();
1300 term_out();
1301 term_update();
1302 break;
1303 case WM_IGNORE_SIZE:
1304 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1305 break;
1306 case WM_ENTERSIZEMOVE:
1307 EnableSizeTip(1);
1308 resizing = TRUE;
1309 break;
1310 case WM_EXITSIZEMOVE:
1311 EnableSizeTip(0);
1312 resizing = FALSE;
1313 back->size();
1314 break;
1315 case WM_SIZING:
1316 {
1317 int width, height, w, h, ew, eh;
1318 LPRECT r = (LPRECT)lParam;
1319
1320 width = r->right - r->left - extra_width;
1321 height = r->bottom - r->top - extra_height;
1322 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1323 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1324 UpdateSizeTip(hwnd, w, h);
1325 ew = width - w * font_width;
1326 eh = height - h * font_height;
1327 if (ew != 0) {
1328 if (wParam == WMSZ_LEFT ||
1329 wParam == WMSZ_BOTTOMLEFT ||
1330 wParam == WMSZ_TOPLEFT)
1331 r->left += ew;
1332 else
1333 r->right -= ew;
1334 }
1335 if (eh != 0) {
1336 if (wParam == WMSZ_TOP ||
1337 wParam == WMSZ_TOPRIGHT ||
1338 wParam == WMSZ_TOPLEFT)
1339 r->top += eh;
1340 else
1341 r->bottom -= eh;
1342 }
1343 if (ew || eh)
1344 return 1;
1345 else
1346 return 0;
1347 }
1348 /* break; (never reached) */
1349 case WM_SIZE:
1350 if (wParam == SIZE_MINIMIZED) {
1351 SetWindowText (hwnd,
1352 cfg.win_name_always ? window_name : icon_name);
1353 break;
1354 }
1355 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1356 SetWindowText (hwnd, window_name);
1357 if (!ignore_size) {
1358 int width, height, w, h;
1359 #if 0 /* we have fixed this using WM_SIZING now */
1360 int ew, eh;
1361 #endif
1362
1363 width = LOWORD(lParam);
1364 height = HIWORD(lParam);
1365 w = width / font_width; if (w < 1) w = 1;
1366 h = height / font_height; if (h < 1) h = 1;
1367 #if 0 /* we have fixed this using WM_SIZING now */
1368 ew = width - w * font_width;
1369 eh = height - h * font_height;
1370 if (ew != 0 || eh != 0) {
1371 RECT r;
1372 GetWindowRect (hwnd, &r);
1373 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1374 SetWindowPos (hwnd, NULL, 0, 0,
1375 r.right - r.left - ew, r.bottom - r.top - eh,
1376 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1377 }
1378 #endif
1379 if (w != cols || h != rows || just_reconfigged) {
1380 term_invalidate();
1381 term_size (h, w, cfg.savelines);
1382 /*
1383 * Don't call back->size in mid-resize. (To prevent
1384 * massive numbers of resize events getting sent
1385 * down the connection during an NT opaque drag.)
1386 */
1387 if (!resizing)
1388 back->size();
1389 just_reconfigged = FALSE;
1390 }
1391 }
1392 ignore_size = FALSE;
1393 return 0;
1394 case WM_VSCROLL:
1395 switch (LOWORD(wParam)) {
1396 case SB_BOTTOM: term_scroll(-1, 0); break;
1397 case SB_TOP: term_scroll(+1, 0); break;
1398 case SB_LINEDOWN: term_scroll (0, +1); break;
1399 case SB_LINEUP: term_scroll (0, -1); break;
1400 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1401 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1402 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1403 term_scroll (1, HIWORD(wParam)); break;
1404 }
1405 break;
1406 case WM_PALETTECHANGED:
1407 if ((HWND) wParam != hwnd && pal != NULL) {
1408 HDC hdc = get_ctx();
1409 if (hdc) {
1410 if (RealizePalette (hdc) > 0)
1411 UpdateColors (hdc);
1412 free_ctx (hdc);
1413 }
1414 }
1415 break;
1416 case WM_QUERYNEWPALETTE:
1417 if (pal != NULL) {
1418 HDC hdc = get_ctx();
1419 if (hdc) {
1420 if (RealizePalette (hdc) > 0)
1421 UpdateColors (hdc);
1422 free_ctx (hdc);
1423 return TRUE;
1424 }
1425 }
1426 return FALSE;
1427 case WM_KEYDOWN:
1428 case WM_SYSKEYDOWN:
1429 case WM_KEYUP:
1430 case WM_SYSKEYUP:
1431 /*
1432 * Add the scan code and keypress timing to the random
1433 * number noise, if we're using ssh.
1434 */
1435 if (cfg.protocol == PROT_SSH)
1436 noise_ultralight(lParam);
1437
1438 /*
1439 * We don't do TranslateMessage since it disassociates the
1440 * resulting CHAR message from the KEYDOWN that sparked it,
1441 * which we occasionally don't want. Instead, we process
1442 * KEYDOWN, and call the Win32 translator functions so that
1443 * we get the translations under _our_ control.
1444 */
1445 {
1446 unsigned char buf[20];
1447 int len;
1448
1449 len = TranslateKey (message, wParam, lParam, buf);
1450 if (len == -1)
1451 return DefWindowProc (hwnd, message, wParam, lParam);
1452 ldisc->send (buf, len);
1453 }
1454 return 0;
1455 case WM_CHAR:
1456 case WM_SYSCHAR:
1457 /*
1458 * Nevertheless, we are prepared to deal with WM_CHAR
1459 * messages, should they crop up. So if someone wants to
1460 * post the things to us as part of a macro manoeuvre,
1461 * we're ready to cope.
1462 */
1463 {
1464 char c = xlat_kbd2tty((unsigned char)wParam);
1465 ldisc->send (&c, 1);
1466 }
1467 return 0;
1468 }
1469
1470 return DefWindowProc (hwnd, message, wParam, lParam);
1471 }
1472
1473 /*
1474 * Move the system caret. (We maintain one, even though it's
1475 * invisible, for the benefit of blind people: apparently some
1476 * helper software tracks the system caret, so we should arrange to
1477 * have one.)
1478 */
1479 void sys_cursor(int x, int y) {
1480 SetCaretPos(x * font_width, y * font_height);
1481 }
1482
1483 /*
1484 * Draw a line of text in the window, at given character
1485 * coordinates, in given attributes.
1486 *
1487 * We are allowed to fiddle with the contents of `text'.
1488 */
1489 void do_text (Context ctx, int x, int y, char *text, int len,
1490 unsigned long attr, int lattr) {
1491 COLORREF fg, bg, t;
1492 int nfg, nbg, nfont;
1493 HDC hdc = ctx;
1494 RECT line_box;
1495 int force_manual_underline = 0;
1496 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1497 static int *IpDx = 0, IpDxLEN = 0;;
1498
1499 if (len>IpDxLEN || IpDx[0] != fnt_width) {
1500 int i;
1501 if (len>IpDxLEN) {
1502 sfree(IpDx);
1503 IpDx = smalloc((len+16)*sizeof(int));
1504 IpDxLEN = (len+16);
1505 }
1506 for(i=0; i<IpDxLEN; i++)
1507 IpDx[i] = fnt_width;
1508 }
1509
1510 x *= fnt_width;
1511 y *= font_height;
1512
1513 if (attr & ATTR_ACTCURS) {
1514 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1515 attr ^= ATTR_CUR_XOR;
1516 }
1517
1518 nfont = 0;
1519 if (cfg.vtmode == VT_OEMONLY)
1520 nfont |= FONT_OEM;
1521
1522 /*
1523 * Map high-half characters in order to approximate ISO using
1524 * OEM character set. No characters are missing if the OEM codepage
1525 * is CP850.
1526 */
1527 if (nfont & FONT_OEM) {
1528 int i;
1529 for (i=0; i<len; i++)
1530 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1531 #if 0
1532 /* This is CP850 ... perfect translation */
1533 static const char oemhighhalf[] =
1534 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1535 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1536 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1537 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1538 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1539 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1540 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1541 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1542 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1543 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1544 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1545 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1546 ;
1547 #endif
1548 /* This is CP437 ... junk translation */
1549 static const unsigned char oemhighhalf[] = {
1550 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1551 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1552 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1553 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1554 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1555 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1556 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1557 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1558 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1559 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1560 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1561 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1562 };
1563
1564 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1565 }
1566 }
1567
1568 if (attr & ATTR_GBCHR) {
1569 int i;
1570 /*
1571 * GB mapping: map # to pound, and everything else stays
1572 * normal.
1573 */
1574 for (i=0; i<len; i++)
1575 if (text[i] == '#')
1576 text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
1577 } else if (attr & ATTR_LINEDRW) {
1578 int i;
1579 /* ISO 8859-1 */
1580 static const char poorman[] =
1581 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1582
1583 /* CP437 */
1584 static const char oemmap_437[] =
1585 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1586 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1587
1588 /* CP850 */
1589 static const char oemmap_850[] =
1590 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1591 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1592
1593 /* Poor windows font ... eg: windows courier */
1594 static const char oemmap[] =
1595 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1596 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1597
1598 /*
1599 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1600 * VT100 line drawing chars; everything else stays normal.
1601 */
1602 switch (cfg.vtmode) {
1603 case VT_XWINDOWS:
1604 for (i=0; i<len; i++)
1605 if (text[i] >= '\x60' && text[i] <= '\x7E')
1606 text[i] += '\x01' - '\x60';
1607 break;
1608 case VT_OEMANSI:
1609 /* Make sure we actually have an OEM font */
1610 if (fonts[nfont|FONT_OEM]) {
1611 case VT_OEMONLY:
1612 nfont |= FONT_OEM;
1613 for (i=0; i<len; i++)
1614 if (text[i] >= '\x60' && text[i] <= '\x7E')
1615 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1616 break;
1617 }
1618 case VT_POORMAN:
1619 for (i=0; i<len; i++)
1620 if (text[i] >= '\x60' && text[i] <= '\x7E')
1621 text[i] = poorman[(unsigned char)text[i] - 0x60];
1622 break;
1623 }
1624 }
1625
1626 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1627 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1628 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1629 nfont |= FONT_BOLD;
1630 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1631 nfont |= FONT_UNDERLINE;
1632 if (!fonts[nfont])
1633 {
1634 if (nfont&FONT_UNDERLINE)
1635 force_manual_underline = 1;
1636 /* Don't do the same for manual bold, it could be bad news. */
1637
1638 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1639 }
1640 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1641 force_manual_underline = 1;
1642 if (attr & ATTR_REVERSE) {
1643 t = nfg; nfg = nbg; nbg = t;
1644 }
1645 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1646 nfg++;
1647 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1648 nbg++;
1649 fg = colours[nfg];
1650 bg = colours[nbg];
1651 SelectObject (hdc, fonts[nfont]);
1652 SetTextColor (hdc, fg);
1653 SetBkColor (hdc, bg);
1654 SetBkMode (hdc, OPAQUE);
1655 line_box.left = x;
1656 line_box.top = y;
1657 line_box.right = x+fnt_width*len;
1658 line_box.bottom = y+font_height;
1659 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1660 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1661 SetBkMode (hdc, TRANSPARENT);
1662
1663 /* GRR: This draws the character outside it's box and can leave
1664 * 'droppings' even with the clip box! I suppose I could loop it
1665 * one character at a time ... yuk.
1666 *
1667 * Or ... I could do a test print with "W", and use +1 or -1 for this
1668 * shift depending on if the leftmost column is blank...
1669 */
1670 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1671 }
1672 if (force_manual_underline ||
1673 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1674 HPEN oldpen;
1675 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1676 MoveToEx (hdc, x, y+descent, NULL);
1677 LineTo (hdc, x+len*fnt_width, y+descent);
1678 oldpen = SelectObject (hdc, oldpen);
1679 DeleteObject (oldpen);
1680 }
1681 if (attr & ATTR_PASCURS) {
1682 POINT pts[5];
1683 HPEN oldpen;
1684 pts[0].x = pts[1].x = pts[4].x = x;
1685 pts[2].x = pts[3].x = x+fnt_width-1;
1686 pts[0].y = pts[3].y = pts[4].y = y;
1687 pts[1].y = pts[2].y = y+font_height-1;
1688 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1689 Polyline (hdc, pts, 5);
1690 oldpen = SelectObject (hdc, oldpen);
1691 DeleteObject (oldpen);
1692 }
1693 }
1694
1695 static int check_compose(int first, int second) {
1696
1697 static char * composetbl[] = {
1698 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1699 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1700 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--­", "RO®",
1701 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1702 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1703 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1704 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1705 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1706 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1707 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1708 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1709 "\"uü", "'yý", "htþ", "\"yÿ",
1710 0};
1711
1712 char ** c;
1713 static int recurse = 0;
1714 int nc = -1;
1715
1716 if(0)
1717 {
1718 char buf[256];
1719 char * p;
1720 sprintf(buf, "cc(%d,%d)", first, second);
1721 for(p=buf; *p; p++)
1722 c_write1(*p);
1723 }
1724
1725 for(c=composetbl; *c; c++) {
1726 if( (*c)[0] == first && (*c)[1] == second)
1727 {
1728 return (*c)[2] & 0xFF;
1729 }
1730 }
1731
1732 if(recurse==0)
1733 {
1734 recurse=1;
1735 nc = check_compose(second, first);
1736 if(nc == -1)
1737 nc = check_compose(toupper(first), toupper(second));
1738 if(nc == -1)
1739 nc = check_compose(toupper(second), toupper(first));
1740 recurse=0;
1741 }
1742 return nc;
1743 }
1744
1745
1746 /*
1747 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1748 * codes. Returns number of bytes used or zero to drop the message
1749 * or -1 to forward the message to windows.
1750 */
1751 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output) {
1752 BYTE keystate[256];
1753 int scan, left_alt = 0, key_down, shift_state;
1754 int r, i, code;
1755 unsigned char * p = output;
1756
1757 static WORD keys[3];
1758 static int compose_state = 0;
1759 static int compose_char = 0;
1760 static WPARAM compose_key = 0;
1761
1762 r = GetKeyboardState(keystate);
1763 if (!r) memset(keystate, 0, sizeof(keystate));
1764 else
1765 {
1766 /* Note if AltGr was pressed and if it was used as a compose key */
1767 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
1768 {
1769 keystate[VK_RMENU] = keystate[VK_MENU];
1770 if (!compose_state) compose_key = wParam;
1771 }
1772 if (wParam == VK_APPS && !compose_state)
1773 compose_key = wParam;
1774
1775 if (wParam == compose_key)
1776 {
1777 if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1778 compose_state = 1;
1779 else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
1780 compose_state = 2;
1781 else
1782 compose_state = 0;
1783 }
1784 else if (compose_state==1 && wParam != VK_CONTROL)
1785 compose_state = 0;
1786
1787 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1788 if ( (cfg.funky_type == 0 || (cfg.funky_type == 1 && app_keypad_keys))
1789 && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
1790
1791 wParam = VK_EXECUTE;
1792
1793 /* UnToggle NUMLock */
1794 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1795 keystate[VK_NUMLOCK] ^= 1;
1796 }
1797
1798 /* And write back the 'adjusted' state */
1799 SetKeyboardState (keystate);
1800 }
1801
1802 /* Disable Auto repeat if required */
1803 if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
1804 return 0;
1805
1806 if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
1807 left_alt = 1;
1808
1809 key_down = ((HIWORD(lParam)&KF_UP)==0);
1810
1811 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1812 if (left_alt && (keystate[VK_CONTROL]&0x80))
1813 keystate[VK_MENU] = 0;
1814
1815 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
1816 shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
1817 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
1818
1819 /*
1820 * Record that we pressed key so the scroll window can be reset, but
1821 * be careful to avoid Shift-UP/Down
1822 */
1823 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
1824 seen_key_event = 1;
1825 }
1826
1827 /* Make sure we're not pasting */
1828 if (key_down) term_nopaste();
1829
1830 if (compose_state>1 && left_alt) compose_state = 0;
1831
1832 /* Sanitize the number pad if not using a PC NumPad */
1833 if( left_alt || (app_keypad_keys && cfg.funky_type != 2)
1834 || cfg.nethack_keypad || compose_state )
1835 {
1836 if ((HIWORD(lParam)&KF_EXTENDED) == 0)
1837 {
1838 int nParam = 0;
1839 switch(wParam)
1840 {
1841 case VK_INSERT: nParam = VK_NUMPAD0; break;
1842 case VK_END: nParam = VK_NUMPAD1; break;
1843 case VK_DOWN: nParam = VK_NUMPAD2; break;
1844 case VK_NEXT: nParam = VK_NUMPAD3; break;
1845 case VK_LEFT: nParam = VK_NUMPAD4; break;
1846 case VK_CLEAR: nParam = VK_NUMPAD5; break;
1847 case VK_RIGHT: nParam = VK_NUMPAD6; break;
1848 case VK_HOME: nParam = VK_NUMPAD7; break;
1849 case VK_UP: nParam = VK_NUMPAD8; break;
1850 case VK_PRIOR: nParam = VK_NUMPAD9; break;
1851 case VK_DELETE: nParam = VK_DECIMAL; break;
1852 }
1853 if (nParam)
1854 {
1855 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
1856 wParam = nParam;
1857 }
1858 }
1859 }
1860
1861 /* If a key is pressed and AltGr is not active */
1862 if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
1863 {
1864 /* Okay, prepare for most alts then ...*/
1865 if (left_alt) *p++ = '\033';
1866
1867 /* Lets see if it's a pattern we know all about ... */
1868 if (wParam == VK_PRIOR && shift_state == 1) {
1869 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
1870 return 0;
1871 }
1872 if (wParam == VK_NEXT && shift_state == 1) {
1873 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
1874 return 0;
1875 }
1876 if (wParam == VK_INSERT && shift_state == 1) {
1877 term_mouse (MB_PASTE, MA_CLICK, 0, 0);
1878 term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
1879 return 0;
1880 }
1881 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
1882 return -1;
1883 }
1884 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
1885
1886 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
1887 return -1;
1888 }
1889
1890 /* Nethack keypad */
1891 if (cfg.nethack_keypad && !left_alt) {
1892 switch(wParam) {
1893 case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
1894 case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
1895 case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
1896 case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
1897 case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
1898 case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
1899 case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
1900 case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
1901 case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
1902 }
1903 }
1904
1905 /* Application Keypad */
1906 if (!left_alt) {
1907 int xkey = 0;
1908
1909 if ( cfg.funky_type == 0 ||
1910 ( cfg.funky_type == 1 && app_keypad_keys)) switch(wParam) {
1911 case VK_EXECUTE: if (app_keypad_keys) xkey = 'P'; break;
1912 case VK_DIVIDE: xkey = 'Q'; break;
1913 case VK_MULTIPLY:xkey = 'R'; break;
1914 case VK_SUBTRACT:xkey = 'S'; break;
1915 }
1916 if(app_keypad_keys) switch(wParam) {
1917 case VK_NUMPAD0: xkey = 'p'; break;
1918 case VK_NUMPAD1: xkey = 'q'; break;
1919 case VK_NUMPAD2: xkey = 'r'; break;
1920 case VK_NUMPAD3: xkey = 's'; break;
1921 case VK_NUMPAD4: xkey = 't'; break;
1922 case VK_NUMPAD5: xkey = 'u'; break;
1923 case VK_NUMPAD6: xkey = 'v'; break;
1924 case VK_NUMPAD7: xkey = 'w'; break;
1925 case VK_NUMPAD8: xkey = 'x'; break;
1926 case VK_NUMPAD9: xkey = 'y'; break;
1927
1928 case VK_DECIMAL: xkey = 'n'; break;
1929 case VK_ADD: if(shift_state) xkey = 'm';
1930 else xkey = 'l';
1931 break;
1932 case VK_RETURN:
1933 if (HIWORD(lParam)&KF_EXTENDED)
1934 xkey = 'M';
1935 break;
1936 }
1937 if(xkey)
1938 {
1939 if (vt52_mode)
1940 {
1941 if (xkey>='P' && xkey<='S')
1942 p += sprintf((char *)p, "\x1B%c", xkey);
1943 else
1944 p += sprintf((char *)p, "\x1B?%c", xkey);
1945 }
1946 else
1947 p += sprintf((char *)p, "\x1BO%c", xkey);
1948 return p - output;
1949 }
1950 }
1951
1952 if (wParam == VK_BACK && shift_state == 0 ) /* Backspace */
1953 {
1954 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
1955 return p-output;
1956 }
1957 if (wParam == VK_TAB && shift_state == 1 ) /* Shift tab */
1958 {
1959 *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
1960 }
1961 if (wParam == VK_SPACE && shift_state == 2 ) /* Ctrl-Space */
1962 {
1963 *p++ = 0; return p - output;
1964 }
1965 if (wParam == VK_SPACE && shift_state == 3 ) /* Ctrl-Shift-Space */
1966 {
1967 *p++ = 160; return p - output;
1968 }
1969 if (wParam == VK_CANCEL && shift_state == 2 ) /* Ctrl-Break */
1970 {
1971 *p++ = 3; return p - output;
1972 }
1973 /* Control-2 to Control-8 are special */
1974 if (shift_state == 2 && wParam >= '2' && wParam <= '8')
1975 {
1976 *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
1977 return p - output;
1978 }
1979 if (shift_state == 2 && wParam == 0xBD) {
1980 *p++ = 0x1F;
1981 return p - output;
1982 }
1983 if (shift_state == 2 && wParam == 0xDF) {
1984 *p++ = 0x1C;
1985 return p - output;
1986 }
1987 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
1988 *p++ = '\r'; *p++ = '\n';
1989 return p - output;
1990 }
1991
1992 /*
1993 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1994 * for integer decimal nn.)
1995 *
1996 * We also deal with the weird ones here. Linux VCs replace F1
1997 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1998 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1999 * respectively.
2000 */
2001 code = 0;
2002 switch (wParam) {
2003 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
2004 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
2005 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
2006 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
2007 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
2008 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
2009 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
2010 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
2011 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
2012 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
2013 case VK_F11: code = 23; break;
2014 case VK_F12: code = 24; break;
2015 case VK_F13: code = 25; break;
2016 case VK_F14: code = 26; break;
2017 case VK_F15: code = 28; break;
2018 case VK_F16: code = 29; break;
2019 case VK_F17: code = 31; break;
2020 case VK_F18: code = 32; break;
2021 case VK_F19: code = 33; break;
2022 case VK_F20: code = 34; break;
2023 case VK_HOME: code = 1; break;
2024 case VK_INSERT: code = 2; break;
2025 case VK_DELETE: code = 3; break;
2026 case VK_END: code = 4; break;
2027 case VK_PRIOR: code = 5; break;
2028 case VK_NEXT: code = 6; break;
2029 }
2030 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2031 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
2032 return p - output;
2033 }
2034 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2035 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
2036 return p - output;
2037 }
2038 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2039 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
2040 return p - output;
2041 }
2042 if (code) {
2043 p += sprintf((char *)p, "\x1B[%d~", code);
2044 return p - output;
2045 }
2046
2047 /*
2048 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2049 * some reason seems to send VK_CLEAR to Windows...).
2050 */
2051 {
2052 char xkey = 0;
2053 switch (wParam) {
2054 case VK_UP: xkey = 'A'; break;
2055 case VK_DOWN: xkey = 'B'; break;
2056 case VK_RIGHT: xkey = 'C'; break;
2057 case VK_LEFT: xkey = 'D'; break;
2058 case VK_CLEAR: xkey = 'G'; break;
2059 }
2060 if (xkey)
2061 {
2062 if (vt52_mode)
2063 p += sprintf((char *)p, "\x1B%c", xkey);
2064 else if (app_cursor_keys)
2065 p += sprintf((char *)p, "\x1BO%c", xkey);
2066 else
2067 p += sprintf((char *)p, "\x1B[%c", xkey);
2068 return p - output;
2069 }
2070 }
2071 }
2072
2073 /* Okay we've done everything interesting; let windows deal with
2074 * the boring stuff */
2075 {
2076 BOOL capsOn=keystate[VK_CAPITAL] !=0;
2077
2078 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2079 if(cfg.xlat_capslockcyr)
2080 keystate[VK_CAPITAL] = 0;
2081
2082 r = ToAscii (wParam, scan, keystate, keys, 0);
2083 if(r>0)
2084 {
2085 p = output;
2086 for(i=0; i<r; i++)
2087 {
2088 unsigned char ch = (unsigned char)keys[i];
2089
2090 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
2091 compose_char = ch;
2092 compose_state ++;
2093 continue;
2094 }
2095 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
2096 int nc;
2097 compose_state = 0;
2098
2099 if ((nc=check_compose(compose_char,ch)) == -1)
2100 {
2101 c_write1('\007');
2102 return 0;
2103 }
2104 *p++ = xlat_kbd2tty((unsigned char)nc);
2105 return p-output;
2106 }
2107
2108 compose_state = 0;
2109
2110 if( left_alt && key_down ) *p++ = '\033';
2111 if (!key_down)
2112 *p++ = ch;
2113 else
2114 {
2115 if(capsOn)
2116 ch = xlat_latkbd2win(ch);
2117 *p++ = xlat_kbd2tty(ch);
2118 }
2119 }
2120
2121 /* This is so the ALT-Numpad and dead keys work correctly. */
2122 keys[0] = 0;
2123
2124 return p-output;
2125 }
2126 }
2127
2128 /* This stops ALT press-release doing a 'COMMAND MENU' function */
2129 if (message == WM_SYSKEYUP && wParam == VK_MENU)
2130 {
2131 keystate[VK_MENU] = 0;
2132 return 0;
2133 }
2134
2135 return -1;
2136 }
2137
2138 void set_title (char *title) {
2139 sfree (window_name);
2140 window_name = smalloc(1+strlen(title));
2141 strcpy (window_name, title);
2142 if (cfg.win_name_always || !IsIconic(hwnd))
2143 SetWindowText (hwnd, title);
2144 }
2145
2146 void set_icon (char *title) {
2147 sfree (icon_name);
2148 icon_name = smalloc(1+strlen(title));
2149 strcpy (icon_name, title);
2150 if (!cfg.win_name_always && IsIconic(hwnd))
2151 SetWindowText (hwnd, title);
2152 }
2153
2154 void set_sbar (int total, int start, int page) {
2155 SCROLLINFO si;
2156
2157 if (!cfg.scrollbar) return;
2158
2159 si.cbSize = sizeof(si);
2160 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2161 si.nMin = 0;
2162 si.nMax = total - 1;
2163 si.nPage = page;
2164 si.nPos = start;
2165 if (hwnd)
2166 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2167 }
2168
2169 Context get_ctx(void) {
2170 HDC hdc;
2171 if (hwnd) {
2172 hdc = GetDC (hwnd);
2173 if (hdc && pal)
2174 SelectPalette (hdc, pal, FALSE);
2175 return hdc;
2176 } else
2177 return NULL;
2178 }
2179
2180 void free_ctx (Context ctx) {
2181 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2182 ReleaseDC (hwnd, ctx);
2183 }
2184
2185 static void real_palette_set (int n, int r, int g, int b) {
2186 if (pal) {
2187 logpal->palPalEntry[n].peRed = r;
2188 logpal->palPalEntry[n].peGreen = g;
2189 logpal->palPalEntry[n].peBlue = b;
2190 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2191 colours[n] = PALETTERGB(r, g, b);
2192 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2193 } else
2194 colours[n] = RGB(r, g, b);
2195 }
2196
2197 void palette_set (int n, int r, int g, int b) {
2198 static const int first[21] = {
2199 0, 2, 4, 6, 8, 10, 12, 14,
2200 1, 3, 5, 7, 9, 11, 13, 15,
2201 16, 17, 18, 20, 22
2202 };
2203 real_palette_set (first[n], r, g, b);
2204 if (first[n] >= 18)
2205 real_palette_set (first[n]+1, r, g, b);
2206 if (pal) {
2207 HDC hdc = get_ctx();
2208 UnrealizeObject (pal);
2209 RealizePalette (hdc);
2210 free_ctx (hdc);
2211 }
2212 }
2213
2214 void palette_reset (void) {
2215 int i;
2216
2217 for (i = 0; i < NCOLOURS; i++) {
2218 if (pal) {
2219 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2220 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2221 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2222 logpal->palPalEntry[i].peFlags = 0;
2223 colours[i] = PALETTERGB(defpal[i].rgbtRed,
2224 defpal[i].rgbtGreen,
2225 defpal[i].rgbtBlue);
2226 } else
2227 colours[i] = RGB(defpal[i].rgbtRed,
2228 defpal[i].rgbtGreen,
2229 defpal[i].rgbtBlue);
2230 }
2231
2232 if (pal) {
2233 HDC hdc;
2234 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2235 hdc = get_ctx();
2236 RealizePalette (hdc);
2237 free_ctx (hdc);
2238 }
2239 }
2240
2241 void write_clip (void *data, int len) {
2242 HGLOBAL clipdata;
2243 void *lock;
2244
2245 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2246 if (!clipdata)
2247 return;
2248 lock = GlobalLock (clipdata);
2249 if (!lock)
2250 return;
2251 memcpy (lock, data, len);
2252 ((unsigned char *) lock) [len] = 0;
2253 GlobalUnlock (clipdata);
2254
2255 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2256 if (OpenClipboard (hwnd)) {
2257 EmptyClipboard();
2258 SetClipboardData (CF_TEXT, clipdata);
2259 CloseClipboard();
2260 } else
2261 GlobalFree (clipdata);
2262 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2263 }
2264
2265 void get_clip (void **p, int *len) {
2266 static HGLOBAL clipdata = NULL;
2267
2268 if (!p) {
2269 if (clipdata)
2270 GlobalUnlock (clipdata);
2271 clipdata = NULL;
2272 return;
2273 } else {
2274 if (OpenClipboard (NULL)) {
2275 clipdata = GetClipboardData (CF_TEXT);
2276 CloseClipboard();
2277 if (clipdata) {
2278 *p = GlobalLock (clipdata);
2279 if (*p) {
2280 *len = strlen(*p);
2281 return;
2282 }
2283 }
2284 }
2285 }
2286
2287 *p = NULL;
2288 *len = 0;
2289 }
2290
2291 /*
2292 * Move `lines' lines from position `from' to position `to' in the
2293 * window.
2294 */
2295 void optimised_move (int to, int from, int lines) {
2296 RECT r;
2297 int min, max;
2298
2299 min = (to < from ? to : from);
2300 max = to + from - min;
2301
2302 r.left = 0; r.right = cols * font_width;
2303 r.top = min * font_height; r.bottom = (max+lines) * font_height;
2304 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2305 }
2306
2307 /*
2308 * Print a message box and perform a fatal exit.
2309 */
2310 void fatalbox(char *fmt, ...) {
2311 va_list ap;
2312 char stuff[200];
2313
2314 va_start(ap, fmt);
2315 vsprintf(stuff, fmt, ap);
2316 va_end(ap);
2317 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2318 exit(1);
2319 }
2320
2321 /*
2322 * Beep.
2323 */
2324 void beep(int errorbeep) {
2325 static long last_beep = 0;
2326 long now, beep_diff;
2327
2328 now = GetTickCount();
2329 beep_diff = now-last_beep;
2330
2331 /* Make sure we only respond to one beep per packet or so */
2332 if (beep_diff>=0 && beep_diff<50)
2333 return;
2334
2335 if(errorbeep)
2336 MessageBeep(MB_ICONHAND);
2337 else
2338 MessageBeep(MB_OK);
2339
2340 last_beep = GetTickCount();
2341 }