8 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
12 #define IDM_SHOWLOG 0x0010
13 #define IDM_NEWSESS 0x0020
14 #define IDM_DUPSESS 0x0030
15 #define IDM_RECONF 0x0040
16 #define IDM_CLRSB 0x0050
17 #define IDM_RESET 0x0060
18 #define IDM_TEL_AYT 0x0070
19 #define IDM_TEL_BRK 0x0080
20 #define IDM_TEL_SYNCH 0x0090
21 #define IDM_TEL_EC 0x00a0
22 #define IDM_TEL_EL 0x00b0
23 #define IDM_TEL_GA 0x00c0
24 #define IDM_TEL_NOP 0x00d0
25 #define IDM_TEL_ABORT 0x00e0
26 #define IDM_TEL_AO 0x00f0
27 #define IDM_TEL_IP 0x0100
28 #define IDM_TEL_SUSP 0x0110
29 #define IDM_TEL_EOR 0x0120
30 #define IDM_TEL_EOF 0x0130
31 #define IDM_ABOUT 0x0140
32 #define IDM_SAVEDSESS 0x0150
34 #define IDM_SAVED_MIN 0x1000
35 #define IDM_SAVED_MAX 0x2000
37 #define WM_IGNORE_SIZE (WM_XUSER + 1)
38 #define WM_IGNORE_CLIP (WM_XUSER + 2)
40 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
41 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
42 static void cfgtopalette(void);
43 static void init_palette(void);
44 static void init_fonts(int);
46 static int extra_width, extra_height;
48 static int pending_netevent = 0;
49 static WPARAM pend_netevent_wParam = 0;
50 static LPARAM pend_netevent_lParam = 0;
51 static void enact_pending_netevent(void);
55 #define FONT_UNDERLINE 2
56 #define FONT_BOLDUND 3
58 #define FONT_OEMBOLD 5
59 #define FONT_OEMBOLDUND 6
61 static HFONT fonts[8];
63 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
71 static COLORREF colours[NCOLOURS];
73 static LPLOGPALETTE logpal;
74 static RGBTRIPLE defpal[NCOLOURS];
78 static int dbltime, lasttime, lastact;
79 static Mouse_Button lastbtn;
81 static char *window_name, *icon_name;
83 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
84 static char appname[] = "PuTTY";
89 int guess_width, guess_height;
93 winsock_ver = MAKEWORD(1, 1);
94 if (WSAStartup(winsock_ver, &wsadata)) {
95 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
96 MB_OK | MB_ICONEXCLAMATION);
99 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
100 MessageBox(NULL, "WinSock version is incompatible with 1.1",
101 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
105 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
107 InitCommonControls();
110 * Process the command line.
115 default_protocol = DEFAULT_PROTOCOL;
116 default_port = DEFAULT_PORT;
121 while (*p && isspace(*p)) p++;
124 * Process command line options first. Yes, this can be
125 * done better, and it will be as soon as I have the
129 char *q = p + strcspn(p, " \t");
132 tolower(p[0]) == 's' &&
133 tolower(p[1]) == 's' &&
134 tolower(p[2]) == 'h') {
135 default_protocol = cfg.protocol = PROT_SSH;
136 default_port = cfg.port = 22;
137 } else if (q == p + 3 &&
138 tolower(p[0]) == 'l' &&
139 tolower(p[1]) == 'o' &&
140 tolower(p[2]) == 'g') {
141 logfile = "putty.log";
143 p = q + strspn(q, " \t");
147 * An initial @ means to activate a saved session.
151 if (!*cfg.host && !do_config()) {
155 } else if (*p == '&') {
157 * An initial & means we've been given a command line
158 * containing the hex value of a HANDLE for a file
159 * mapping object, which we must then extract as a
164 if (sscanf(p+1, "%p", &filemap) == 1 &&
165 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
166 0, 0, sizeof(Config))) != NULL) {
169 CloseHandle(filemap);
170 } else if (!do_config()) {
177 * If the hostname starts with "telnet://", set the
178 * protocol to Telnet and process the string as a
181 if (!strncmp(q, "telnet://", 9)) {
185 cfg.protocol = PROT_TELNET;
187 while (*p && *p != ':' && *p != '/') p++;
195 strncpy (cfg.host, q, sizeof(cfg.host)-1);
196 cfg.host[sizeof(cfg.host)-1] = '\0';
198 while (*p && !isspace(*p)) p++;
201 strncpy (cfg.host, q, sizeof(cfg.host)-1);
202 cfg.host[sizeof(cfg.host)-1] = '\0';
203 while (*p && isspace(*p)) p++;
218 * Select protocol. This is farmed out into a table in a
219 * separate file to enable an ssh-free variant.
224 for (i = 0; backends[i].backend != NULL; i++)
225 if (backends[i].protocol == cfg.protocol) {
226 back = backends[i].backend;
230 MessageBox(NULL, "Unsupported protocol number found",
231 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
237 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
241 wndclass.lpfnWndProc = WndProc;
242 wndclass.cbClsExtra = 0;
243 wndclass.cbWndExtra = 0;
244 wndclass.hInstance = inst;
245 wndclass.hIcon = LoadIcon (inst,
246 MAKEINTRESOURCE(IDI_MAINICON));
247 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
248 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
249 wndclass.lpszMenuName = NULL;
250 wndclass.lpszClassName = appname;
252 RegisterClass (&wndclass);
257 savelines = cfg.savelines;
263 * Guess some defaults for the window size. This all gets
264 * updated later, so we don't really care too much. However, we
265 * do want the font width/height guesses to correspond to a
266 * large font rather than a small one...
273 term_size (cfg.height, cfg.width, cfg.savelines);
274 guess_width = extra_width + font_width * cols;
275 guess_height = extra_height + font_height * rows;
278 HWND w = GetDesktopWindow();
279 GetWindowRect (w, &r);
280 if (guess_width > r.right - r.left)
281 guess_width = r.right - r.left;
282 if (guess_height > r.bottom - r.top)
283 guess_height = r.bottom - r.top;
287 int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
288 if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
289 if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
290 hwnd = CreateWindow (appname, appname,
292 CW_USEDEFAULT, CW_USEDEFAULT,
293 guess_width, guess_height,
294 NULL, NULL, inst, NULL);
298 * Initialise the fonts, simultaneously correcting the guesses
299 * for font_{width,height}.
301 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
306 * Correct the guesses for extra_{width,height}.
310 GetWindowRect (hwnd, &wr);
311 GetClientRect (hwnd, &cr);
312 extra_width = wr.right - wr.left - cr.right + cr.left;
313 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
317 * Resize the window, now we know what size we _really_ want it
320 guess_width = extra_width + font_width * cols;
321 guess_height = extra_height + font_height * rows;
322 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
323 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
324 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
327 * Initialise the scroll bar.
332 si.cbSize = sizeof(si);
333 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
338 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
342 * Start up the telnet connection.
349 error = back->init (hwnd, cfg.host, cfg.port, &realhost);
351 sprintf(msg, "Unable to open connection:\n%s", error);
352 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
355 window_name = icon_name = NULL;
356 sprintf(msg, "%s - PuTTY", realhost);
361 session_closed = FALSE;
364 * Set up the input and output buffers.
367 outbuf_reap = outbuf_head = 0;
370 * Prepare the mouse handler.
372 lastact = MA_NOTHING;
373 lastbtn = MB_NOTHING;
374 dbltime = GetDoubleClickTime();
377 * Set up the session-control options on the system menu.
380 HMENU m = GetSystemMenu (hwnd, FALSE);
384 AppendMenu (m, MF_SEPARATOR, 0, 0);
385 if (cfg.protocol == PROT_TELNET) {
387 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
388 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
389 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
390 AppendMenu (p, MF_SEPARATOR, 0, 0);
391 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
392 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
393 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
394 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
395 AppendMenu (p, MF_SEPARATOR, 0, 0);
396 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
397 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
398 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
399 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
400 AppendMenu (p, MF_SEPARATOR, 0, 0);
401 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
402 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
403 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
404 AppendMenu (m, MF_SEPARATOR, 0, 0);
406 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
407 AppendMenu (m, MF_SEPARATOR, 0, 0);
408 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
409 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
412 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
413 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
414 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
415 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
416 AppendMenu (m, MF_SEPARATOR, 0, 0);
417 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
418 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
419 AppendMenu (m, MF_SEPARATOR, 0, 0);
420 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
424 * Finally show the window!
426 ShowWindow (hwnd, show);
429 * Set the palette up.
435 has_focus = (GetForegroundWindow() == hwnd);
439 int timer_id = 0, long_timer = 0;
441 while (GetMessage (&msg, NULL, 0, 0) == 1) {
442 /* Sometimes DispatchMessage calls routines that use their own
443 * GetMessage loop, setup this timer so we get some control back.
445 * Also call term_update() from the timer so that if the host
446 * is sending data flat out we still do redraws.
448 if(timer_id && long_timer) {
449 KillTimer(hwnd, timer_id);
450 long_timer = timer_id = 0;
453 timer_id = SetTimer(hwnd, 1, 20, NULL);
454 DispatchMessage (&msg);
456 /* This is too fast, but I'll leave it for now 'cause it shows
457 * how often term_update is called (far too often at times!)
461 /* Send the paste buffer if there's anything to send */
464 /* If there's nothing new in the queue then we can do everything
465 * we've delayed, reading the socket, writing, and repainting
468 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
469 if (pending_netevent) {
470 enact_pending_netevent();
475 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
477 KillTimer(hwnd, timer_id);
484 timer_id = SetTimer(hwnd, 1, 2000, NULL);
485 else if (cfg.blinktext)
486 timer_id = SetTimer(hwnd, 1, 250, NULL);
488 timer_id = SetTimer(hwnd, 1, 500, NULL);
501 DeleteObject(fonts[i]);
508 if (cfg.protocol == PROT_SSH) {
519 * Actually do the job requested by a WM_NETEVENT
521 static void enact_pending_netevent(void) {
523 pending_netevent = FALSE;
524 i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
528 switch (WSABASEERR + (-i) % 10000) {
530 sprintf(buf, "Connection reset by peer");
533 sprintf(buf, "Unexpected network error %d", -i);
536 MessageBox(hwnd, buf, "PuTTY Fatal Error",
537 MB_ICONERROR | MB_OK);
540 if (cfg.close_on_exit)
543 session_closed = TRUE;
544 MessageBox(hwnd, "Connection closed by remote host",
545 "PuTTY", MB_OK | MB_ICONINFORMATION);
546 SetWindowText (hwnd, "PuTTY (inactive)");
552 * Copy the colour palette from the configuration data into defpal.
553 * This is non-trivial because the colour indices are different.
555 static void cfgtopalette(void) {
557 static const int ww[] = {
558 6, 7, 8, 9, 10, 11, 12, 13,
559 14, 15, 16, 17, 18, 19, 20, 21,
560 0, 1, 2, 3, 4, 4, 5, 5
563 for (i=0; i<24; i++) {
565 defpal[i].rgbtRed = cfg.colours[w][0];
566 defpal[i].rgbtGreen = cfg.colours[w][1];
567 defpal[i].rgbtBlue = cfg.colours[w][2];
572 * Set up the colour palette.
574 static void init_palette(void) {
576 HDC hdc = GetDC (hwnd);
578 if (cfg.try_palette &&
579 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
580 logpal = smalloc(sizeof(*logpal)
581 - sizeof(logpal->palPalEntry)
582 + NCOLOURS * sizeof(PALETTEENTRY));
583 logpal->palVersion = 0x300;
584 logpal->palNumEntries = NCOLOURS;
585 for (i = 0; i < NCOLOURS; i++) {
586 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
587 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
588 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
589 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
591 pal = CreatePalette (logpal);
593 SelectPalette (hdc, pal, FALSE);
594 RealizePalette (hdc);
595 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
599 ReleaseDC (hwnd, hdc);
602 for (i=0; i<NCOLOURS; i++)
603 colours[i] = PALETTERGB(defpal[i].rgbtRed,
607 for(i=0; i<NCOLOURS; i++)
608 colours[i] = RGB(defpal[i].rgbtRed,
614 * Initialise all the fonts we will need. There may be as many as
615 * eight or as few as one. We also:
617 * - check the font width and height, correcting our guesses if
620 * - verify that the bold font is the same width as the ordinary
621 * one, and engage shadow bolding if not.
623 * - verify that the underlined font is the same width as the
624 * ordinary one (manual underlining by means of line drawing can
625 * be done in a pinch).
627 static void init_fonts(int pick_width) {
632 int fw_dontcare, fw_bold;
641 if (cfg.fontisbold) {
642 fw_dontcare = FW_BOLD;
645 fw_dontcare = FW_DONTCARE;
651 font_height = cfg.fontheight;
652 font_width = pick_width;
655 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
656 c, OUT_DEFAULT_PRECIS, \
657 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
658 FIXED_PITCH | FF_DONTCARE, cfg.font)
660 if (cfg.vtmode != VT_OEMONLY) {
661 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
663 SelectObject (hdc, fonts[FONT_NORMAL]);
664 GetTextMetrics(hdc, &tm);
665 font_height = tm.tmHeight;
666 font_width = tm.tmAveCharWidth;
668 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
670 if (bold_mode == BOLD_FONT) {
671 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
672 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
675 if (cfg.vtmode == VT_OEMANSI) {
676 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
677 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
679 if (bold_mode == BOLD_FONT) {
680 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
681 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
687 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
689 SelectObject (hdc, fonts[FONT_OEM]);
690 GetTextMetrics(hdc, &tm);
691 font_height = tm.tmHeight;
692 font_width = tm.tmAveCharWidth;
694 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
696 if (bold_mode == BOLD_FONT) {
697 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
698 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
703 descent = tm.tmAscent + 1;
704 if (descent >= font_height)
705 descent = font_height - 1;
706 firstchar = tm.tmFirstChar;
708 for (i=0; i<8; i++) {
710 if (SelectObject (hdc, fonts[i]) &&
711 GetTextMetrics(hdc, &tm) )
712 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
718 ReleaseDC (hwnd, hdc);
720 /* ... This is wrong in OEM only mode */
721 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
722 (bold_mode == BOLD_FONT &&
723 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
725 DeleteObject (fonts[FONT_UNDERLINE]);
726 if (bold_mode == BOLD_FONT)
727 DeleteObject (fonts[FONT_BOLDUND]);
730 if (bold_mode == BOLD_FONT &&
731 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
732 bold_mode = BOLD_SHADOW;
733 DeleteObject (fonts[FONT_BOLD]);
734 if (und_mode == UND_FONT)
735 DeleteObject (fonts[FONT_BOLDUND]);
739 /* With the fascist font painting it doesn't matter if the linedraw font
740 * isn't exactly the right size anymore so we don't have to check this.
742 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
743 if( cfg.fontcharset == OEM_CHARSET )
745 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
746 "different sizes. Using OEM-only mode instead",
747 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
748 cfg.vtmode = VT_OEMONLY;
750 else if( firstchar < ' ' )
752 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
753 "different sizes. Using XTerm mode instead",
754 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
755 cfg.vtmode = VT_XWINDOWS;
759 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
760 "different sizes. Using ISO8859-1 mode instead",
761 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
762 cfg.vtmode = VT_POORMAN;
767 DeleteObject (fonts[i]);
773 void request_resize (int w, int h, int refont) {
776 /* If the window is maximized supress resizing attempts */
777 if(IsZoomed(hwnd)) return;
780 /* Don't do this in OEMANSI, you may get disable messages */
781 if (refont && w != cols && (cols==80 || cols==132)
782 && cfg.vtmode != VT_OEMANSI)
784 if (refont && w != cols && (cols==80 || cols==132))
787 /* If font width too big for screen should we shrink the font more ? */
789 font_width = ((font_width*cols+w/2)/w);
796 DeleteObject(fonts[i]);
798 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
800 init_fonts(font_width);
804 static int first_time = 1;
810 /* Get the size of the screen */
811 if (GetClientRect(GetDesktopWindow(),&ss))
812 /* first_time = 0 */;
813 else { first_time = 2; break; }
815 /* Make sure the values are sane */
816 width = (ss.right-ss.left-extra_width ) / font_width;
817 height = (ss.bottom-ss.top-extra_height ) / font_height;
819 if (w>width) w=width;
820 if (h>height) h=height;
826 width = extra_width + font_width * w;
827 height = extra_height + font_height * h;
829 SetWindowPos (hwnd, NULL, 0, 0, width, height,
830 SWP_NOACTIVATE | SWP_NOCOPYBITS |
831 SWP_NOMOVE | SWP_NOZORDER);
834 static void click (Mouse_Button b, int x, int y) {
835 int thistime = GetMessageTime();
837 if (lastbtn == b && thistime - lasttime < dbltime) {
838 lastact = (lastact == MA_CLICK ? MA_2CLK :
839 lastact == MA_2CLK ? MA_3CLK :
840 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
845 if (lastact != MA_NOTHING)
846 term_mouse (b, lastact, x, y);
850 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
851 WPARAM wParam, LPARAM lParam) {
853 static int ignore_size = FALSE;
854 static int ignore_clip = FALSE;
855 static int just_reconfigged = FALSE;
859 if (pending_netevent)
860 enact_pending_netevent();
868 if (!cfg.warn_on_close || session_closed ||
869 MessageBox(hwnd, "Are you sure you want to close this session?",
870 "PuTTY Exit Confirmation",
871 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
878 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
890 PROCESS_INFORMATION pi;
891 HANDLE filemap = NULL;
893 if (wParam == IDM_DUPSESS) {
895 * Allocate a file-mapping memory chunk for the
898 SECURITY_ATTRIBUTES sa;
901 sa.nLength = sizeof(sa);
902 sa.lpSecurityDescriptor = NULL;
903 sa.bInheritHandle = TRUE;
904 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
911 p = (Config *)MapViewOfFile(filemap,
913 0, 0, sizeof(Config));
915 *p = cfg; /* structure copy */
919 sprintf(c, "putty &%p", filemap);
921 } else if (wParam == IDM_SAVEDSESS) {
922 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
923 cl = malloc(16 + strlen(session)); /* 8, but play safe */
925 cl = NULL; /* not a very important failure mode */
927 sprintf(cl, "putty @%s", session);
933 GetModuleFileName (NULL, b, sizeof(b)-1);
935 si.lpReserved = NULL;
940 si.lpReserved2 = NULL;
941 CreateProcess (b, cl, NULL, NULL, TRUE,
942 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
945 CloseHandle(filemap);
951 if (!do_reconfig(hwnd))
953 just_reconfigged = TRUE;
958 DeleteObject(fonts[i]);
960 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
964 /* Telnet will change local echo -> remote if the remote asks */
965 if (cfg.protocol != PROT_TELNET)
966 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
974 /* Enable or disable the scroll bar, etc */
976 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
979 if (cfg.scrollbar) nflg |= WS_VSCROLL;
980 else nflg &= ~WS_VSCROLL;
982 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
984 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
990 SetWindowLong(hwnd, GWL_STYLE, nflg);
991 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
992 SetWindowPos(hwnd, NULL, 0,0,0,0,
993 SWP_NOACTIVATE|SWP_NOCOPYBITS|
994 SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER|
997 GetWindowRect (hwnd, &wr);
998 GetClientRect (hwnd, &cr);
999 extra_width = wr.right - wr.left - cr.right + cr.left;
1000 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1004 term_size(cfg.height, cfg.width, cfg.savelines);
1005 InvalidateRect(hwnd, NULL, TRUE);
1006 SetWindowPos (hwnd, NULL, 0, 0,
1007 extra_width + font_width * cfg.width,
1008 extra_height + font_height * cfg.height,
1009 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1010 SWP_NOMOVE | SWP_NOZORDER);
1011 if (IsIconic(hwnd)) {
1012 SetWindowText (hwnd,
1013 cfg.win_name_always ? window_name : icon_name);
1022 case IDM_TEL_AYT: back->special (TS_AYT); break;
1023 case IDM_TEL_BRK: back->special (TS_BRK); break;
1024 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1025 case IDM_TEL_EC: back->special (TS_EC); break;
1026 case IDM_TEL_EL: back->special (TS_EL); break;
1027 case IDM_TEL_GA: back->special (TS_GA); break;
1028 case IDM_TEL_NOP: back->special (TS_NOP); break;
1029 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1030 case IDM_TEL_AO: back->special (TS_AO); break;
1031 case IDM_TEL_IP: back->special (TS_IP); break;
1032 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1033 case IDM_TEL_EOR: back->special (TS_EOR); break;
1034 case IDM_TEL_EOF: back->special (TS_EOF); break;
1039 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1040 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1045 #define X_POS(l) ((int)(short)LOWORD(l))
1046 #define Y_POS(l) ((int)(short)HIWORD(l))
1048 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1049 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1051 case WM_LBUTTONDOWN:
1052 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1053 TO_CHR_Y(Y_POS(lParam)));
1057 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1058 TO_CHR_Y(Y_POS(lParam)));
1061 case WM_MBUTTONDOWN:
1063 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1064 TO_CHR_X(X_POS(lParam)),
1065 TO_CHR_Y(Y_POS(lParam)));
1068 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1069 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1070 TO_CHR_Y(Y_POS(lParam)));
1073 case WM_RBUTTONDOWN:
1075 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1076 TO_CHR_X(X_POS(lParam)),
1077 TO_CHR_Y(Y_POS(lParam)));
1080 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1081 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1082 TO_CHR_Y(Y_POS(lParam)));
1087 * Add the mouse position and message time to the random
1088 * number noise, if we're using ssh.
1090 if (cfg.protocol == PROT_SSH)
1091 noise_ultralight(lParam);
1093 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1095 if (wParam & MK_LBUTTON)
1097 else if (wParam & MK_MBUTTON)
1098 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1100 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1101 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1102 TO_CHR_Y(Y_POS(lParam)));
1105 case WM_IGNORE_CLIP:
1106 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1108 case WM_DESTROYCLIPBOARD:
1111 ignore_clip = FALSE;
1116 hdc = BeginPaint (hwnd, &p);
1118 SelectPalette (hdc, pal, TRUE);
1119 RealizePalette (hdc);
1121 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1122 p.rcPaint.right, p.rcPaint.bottom);
1123 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1124 SelectObject (hdc, GetStockObject(WHITE_PEN));
1125 EndPaint (hwnd, &p);
1129 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1130 * but the only one that's likely to try to overload us is FD_READ.
1131 * This means buffering just one is fine.
1133 if (pending_netevent)
1134 enact_pending_netevent();
1136 pending_netevent = TRUE;
1137 pend_netevent_wParam=wParam;
1138 pend_netevent_lParam=lParam;
1150 case WM_IGNORE_SIZE:
1151 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1153 case WM_ENTERSIZEMOVE:
1156 case WM_EXITSIZEMOVE:
1161 int width, height, w, h, ew, eh;
1162 LPRECT r = (LPRECT)lParam;
1164 width = r->right - r->left - extra_width;
1165 height = r->bottom - r->top - extra_height;
1166 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1167 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1168 UpdateSizeTip(hwnd, w, h);
1169 ew = width - w * font_width;
1170 eh = height - h * font_height;
1172 if (wParam == WMSZ_LEFT ||
1173 wParam == WMSZ_BOTTOMLEFT ||
1174 wParam == WMSZ_TOPLEFT)
1180 if (wParam == WMSZ_TOP ||
1181 wParam == WMSZ_TOPRIGHT ||
1182 wParam == WMSZ_TOPLEFT)
1192 /* break; (never reached) */
1194 if (wParam == SIZE_MINIMIZED) {
1195 SetWindowText (hwnd,
1196 cfg.win_name_always ? window_name : icon_name);
1199 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1200 SetWindowText (hwnd, window_name);
1202 int width, height, w, h;
1203 #if 0 /* we have fixed this using WM_SIZING now */
1207 width = LOWORD(lParam);
1208 height = HIWORD(lParam);
1209 w = width / font_width; if (w < 1) w = 1;
1210 h = height / font_height; if (h < 1) h = 1;
1211 #if 0 /* we have fixed this using WM_SIZING now */
1212 ew = width - w * font_width;
1213 eh = height - h * font_height;
1214 if (ew != 0 || eh != 0) {
1216 GetWindowRect (hwnd, &r);
1217 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1218 SetWindowPos (hwnd, NULL, 0, 0,
1219 r.right - r.left - ew, r.bottom - r.top - eh,
1220 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1223 if (w != cols || h != rows || just_reconfigged) {
1225 term_size (h, w, cfg.savelines);
1227 just_reconfigged = FALSE;
1230 ignore_size = FALSE;
1233 switch (LOWORD(wParam)) {
1234 case SB_BOTTOM: term_scroll(-1, 0); break;
1235 case SB_TOP: term_scroll(+1, 0); break;
1236 case SB_LINEDOWN: term_scroll (0, +1); break;
1237 case SB_LINEUP: term_scroll (0, -1); break;
1238 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1239 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1240 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1241 term_scroll (1, HIWORD(wParam)); break;
1244 case WM_PALETTECHANGED:
1245 if ((HWND) wParam != hwnd && pal != NULL) {
1246 HDC hdc = get_ctx();
1248 if (RealizePalette (hdc) > 0)
1254 case WM_QUERYNEWPALETTE:
1256 HDC hdc = get_ctx();
1258 if (RealizePalette (hdc) > 0)
1270 * Add the scan code and keypress timing to the random
1271 * number noise, if we're using ssh.
1273 if (cfg.protocol == PROT_SSH)
1274 noise_ultralight(lParam);
1277 * We don't do TranslateMessage since it disassociates the
1278 * resulting CHAR message from the KEYDOWN that sparked it,
1279 * which we occasionally don't want. Instead, we process
1280 * KEYDOWN, and call the Win32 translator functions so that
1281 * we get the translations under _our_ control.
1284 unsigned char buf[20];
1287 len = TranslateKey (message, wParam, lParam, buf);
1289 return DefWindowProc (hwnd, message, wParam, lParam);
1290 ldisc->send (buf, len);
1296 * Nevertheless, we are prepared to deal with WM_CHAR
1297 * messages, should they crop up. So if someone wants to
1298 * post the things to us as part of a macro manoeuvre,
1299 * we're ready to cope.
1302 char c = xlat_kbd2tty((unsigned char)wParam);
1303 ldisc->send (&c, 1);
1308 return DefWindowProc (hwnd, message, wParam, lParam);
1312 * Draw a line of text in the window, at given character
1313 * coordinates, in given attributes.
1315 * We are allowed to fiddle with the contents of `text'.
1317 void do_text (Context ctx, int x, int y, char *text, int len,
1318 unsigned long attr, int lattr) {
1320 int nfg, nbg, nfont;
1323 int force_manual_underline = 0;
1324 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1325 static int *IpDx = 0, IpDxLEN = 0;;
1327 if (len>IpDxLEN || IpDx[0] != fnt_width) {
1331 IpDx = smalloc((len+16)*sizeof(int));
1334 for(i=0; i<IpDxLEN; i++)
1335 IpDx[i] = fnt_width;
1341 if (attr & ATTR_ACTCURS) {
1342 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1343 attr ^= ATTR_CUR_XOR;
1347 if (cfg.vtmode == VT_OEMONLY)
1351 * Map high-half characters in order to approximate ISO using
1352 * OEM character set. No characters are missing if the OEM codepage
1355 if (nfont & FONT_OEM) {
1357 for (i=0; i<len; i++)
1358 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1360 /* This is CP850 ... perfect translation */
1361 static const char oemhighhalf[] =
1362 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1363 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1364 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1365 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1366 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1367 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1368 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1369 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1370 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1371 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1372 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1373 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1376 /* This is CP437 ... junk translation */
1377 static const unsigned char oemhighhalf[] = {
1378 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1379 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1380 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1381 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1382 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1383 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1384 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1385 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1386 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1387 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1388 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1389 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1392 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1396 if (attr & ATTR_GBCHR) {
1399 * GB mapping: map # to pound, and everything else stays
1402 for (i=0; i<len; i++)
1404 text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
1405 } else if (attr & ATTR_LINEDRW) {
1408 static const char poorman[] =
1409 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1412 static const char oemmap_437[] =
1413 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1414 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1417 static const char oemmap_850[] =
1418 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1419 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1421 /* Poor windows font ... eg: windows courier */
1422 static const char oemmap[] =
1423 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1424 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1427 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1428 * VT100 line drawing chars; everything else stays normal.
1430 switch (cfg.vtmode) {
1432 for (i=0; i<len; i++)
1433 if (text[i] >= '\x60' && text[i] <= '\x7E')
1434 text[i] += '\x01' - '\x60';
1437 /* Make sure we actually have an OEM font */
1438 if (fonts[nfont|FONT_OEM]) {
1441 for (i=0; i<len; i++)
1442 if (text[i] >= '\x60' && text[i] <= '\x7E')
1443 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1447 for (i=0; i<len; i++)
1448 if (text[i] >= '\x60' && text[i] <= '\x7E')
1449 text[i] = poorman[(unsigned char)text[i] - 0x60];
1454 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1455 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1456 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1458 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1459 nfont |= FONT_UNDERLINE;
1462 if (nfont&FONT_UNDERLINE)
1463 force_manual_underline = 1;
1464 /* Don't do the same for manual bold, it could be bad news. */
1466 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1468 if (attr & ATTR_REVERSE) {
1469 t = nfg; nfg = nbg; nbg = t;
1471 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1473 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1477 SelectObject (hdc, fonts[nfont]);
1478 SetTextColor (hdc, fg);
1479 SetBkColor (hdc, bg);
1480 SetBkMode (hdc, OPAQUE);
1483 line_box.right = x+fnt_width*len;
1484 line_box.bottom = y+font_height;
1485 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1486 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1487 SetBkMode (hdc, TRANSPARENT);
1489 /* GRR: This draws the character outside it's box and can leave
1490 * 'droppings' even with the clip box! I suppose I could loop it
1491 * one character at a time ... yuk.
1493 * Or ... I could do a test print with "W", and use +1 or -1 for this
1494 * shift depending on if the leftmost column is blank...
1496 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1498 if (force_manual_underline ||
1499 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1501 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1502 MoveToEx (hdc, x, y+descent, NULL);
1503 LineTo (hdc, x+len*fnt_width, y+descent);
1504 oldpen = SelectObject (hdc, oldpen);
1505 DeleteObject (oldpen);
1507 if (attr & ATTR_PASCURS) {
1510 pts[0].x = pts[1].x = pts[4].x = x;
1511 pts[2].x = pts[3].x = x+fnt_width-1;
1512 pts[0].y = pts[3].y = pts[4].y = y;
1513 pts[1].y = pts[2].y = y+font_height-1;
1514 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1515 Polyline (hdc, pts, 5);
1516 oldpen = SelectObject (hdc, oldpen);
1517 DeleteObject (oldpen);
1521 static int check_compose(int first, int second) {
1523 static char * composetbl[] = {
1524 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1525 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1526 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1527 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1528 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1529 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1530 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1531 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1532 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1533 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1534 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1535 "\"uü", "'yý", "htþ", "\"yÿ",
1539 static int recurse = 0;
1546 sprintf(buf, "cc(%d,%d)", first, second);
1551 for(c=composetbl; *c; c++) {
1552 if( (*c)[0] == first && (*c)[1] == second)
1554 return (*c)[2] & 0xFF;
1561 nc = check_compose(second, first);
1563 nc = check_compose(toupper(first), toupper(second));
1565 nc = check_compose(toupper(second), toupper(first));
1573 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1574 * codes. Returns number of bytes used or zero to drop the message
1575 * or -1 to forward the message to windows.
1577 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output) {
1579 int scan, left_alt = 0, key_down, shift_state;
1581 unsigned char * p = output;
1583 static WORD keys[3];
1584 static int compose_state = 0;
1585 static int compose_char = 0;
1586 static WPARAM compose_key = 0;
1588 r = GetKeyboardState(keystate);
1589 if (!r) memset(keystate, 0, sizeof(keystate));
1592 /* Note if AltGr was pressed and if it was used as a compose key */
1593 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
1595 keystate[VK_RMENU] = keystate[VK_MENU];
1596 if (!compose_state) compose_key = wParam;
1598 if (wParam == VK_APPS && !compose_state)
1599 compose_key = wParam;
1601 if (wParam == compose_key)
1603 if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1605 else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
1610 else if (compose_state==1 && wParam != VK_CONTROL)
1613 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1614 if ( (cfg.funky_type == 0 || (cfg.funky_type == 1 && app_keypad_keys))
1615 && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
1617 wParam = VK_EXECUTE;
1619 /* UnToggle NUMLock */
1620 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1621 keystate[VK_NUMLOCK] ^= 1;
1624 /* And write back the 'adjusted' state */
1625 SetKeyboardState (keystate);
1628 /* Disable Auto repeat if required */
1629 if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
1632 if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
1635 key_down = ((HIWORD(lParam)&KF_UP)==0);
1637 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1638 if (left_alt && (keystate[VK_CONTROL]&0x80))
1639 keystate[VK_MENU] = 0;
1641 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
1642 shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
1643 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
1646 * Record that we pressed key so the scroll window can be reset, but
1647 * be careful to avoid Shift-UP/Down
1649 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
1653 /* Make sure we're not pasting */
1654 if (key_down) term_nopaste();
1656 if (compose_state>1 && left_alt) compose_state = 0;
1658 /* Sanitize the number pad if not using a PC NumPad */
1659 if( left_alt || (app_keypad_keys && cfg.funky_type != 2)
1660 || cfg.nethack_keypad || compose_state )
1662 if ((HIWORD(lParam)&KF_EXTENDED) == 0)
1667 case VK_INSERT: nParam = VK_NUMPAD0; break;
1668 case VK_END: nParam = VK_NUMPAD1; break;
1669 case VK_DOWN: nParam = VK_NUMPAD2; break;
1670 case VK_NEXT: nParam = VK_NUMPAD3; break;
1671 case VK_LEFT: nParam = VK_NUMPAD4; break;
1672 case VK_CLEAR: nParam = VK_NUMPAD5; break;
1673 case VK_RIGHT: nParam = VK_NUMPAD6; break;
1674 case VK_HOME: nParam = VK_NUMPAD7; break;
1675 case VK_UP: nParam = VK_NUMPAD8; break;
1676 case VK_PRIOR: nParam = VK_NUMPAD9; break;
1677 case VK_DELETE: nParam = VK_DECIMAL; break;
1681 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
1687 /* If a key is pressed and AltGr is not active */
1688 if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
1690 /* Okay, prepare for most alts then ...*/
1691 if (left_alt) *p++ = '\033';
1693 /* Lets see if it's a pattern we know all about ... */
1694 if (wParam == VK_PRIOR && shift_state == 1) {
1695 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
1698 if (wParam == VK_NEXT && shift_state == 1) {
1699 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
1702 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
1705 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
1706 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
1710 /* Nethack keypad */
1711 if (cfg.nethack_keypad && !left_alt) {
1713 case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
1714 case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
1715 case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
1716 case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
1717 case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
1718 case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
1719 case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
1720 case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
1721 case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
1725 /* Application Keypad */
1729 if ( cfg.funky_type == 0 ||
1730 ( cfg.funky_type == 1 && app_keypad_keys)) switch(wParam) {
1731 case VK_EXECUTE: xkey = 'P'; break;
1732 case VK_DIVIDE: xkey = 'Q'; break;
1733 case VK_MULTIPLY:xkey = 'R'; break;
1734 case VK_SUBTRACT:xkey = 'S'; break;
1736 if(app_keypad_keys) switch(wParam) {
1737 case VK_NUMPAD0: xkey = 'p'; break;
1738 case VK_NUMPAD1: xkey = 'q'; break;
1739 case VK_NUMPAD2: xkey = 'r'; break;
1740 case VK_NUMPAD3: xkey = 's'; break;
1741 case VK_NUMPAD4: xkey = 't'; break;
1742 case VK_NUMPAD5: xkey = 'u'; break;
1743 case VK_NUMPAD6: xkey = 'v'; break;
1744 case VK_NUMPAD7: xkey = 'w'; break;
1745 case VK_NUMPAD8: xkey = 'x'; break;
1746 case VK_NUMPAD9: xkey = 'y'; break;
1748 case VK_DECIMAL: xkey = 'n'; break;
1749 case VK_ADD: if(shift_state) xkey = 'm';
1753 if (HIWORD(lParam)&KF_EXTENDED)
1761 if (xkey>='P' && xkey<='S')
1762 p += sprintf((char *)p, "\x1B%c", xkey);
1764 p += sprintf((char *)p, "\x1B?%c", xkey);
1767 p += sprintf((char *)p, "\x1BO%c", xkey);
1772 if (wParam == VK_BACK && shift_state == 0 ) /* Backspace */
1774 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
1777 if (wParam == VK_TAB && shift_state == 1 ) /* Shift tab */
1779 *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
1781 if (wParam == VK_SPACE && shift_state == 2 ) /* Ctrl-Space */
1783 *p++ = 0; return p - output;
1785 if (wParam == VK_SPACE && shift_state == 3 ) /* Ctrl-Shift-Space */
1787 *p++ = 160; return p - output;
1789 if (wParam == VK_CANCEL && shift_state == 2 ) /* Ctrl-Break */
1791 *p++ = 3; return p - output;
1793 /* Control-2 to Control-8 are special */
1794 if (shift_state == 2 && wParam >= '2' && wParam <= '8')
1796 *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
1799 if (shift_state == 2 && wParam == 0xBD) {
1803 if (shift_state == 2 && wParam == 0xDF) {
1807 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
1808 *p++ = '\r'; *p++ = '\n';
1813 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1814 * for integer decimal nn.)
1816 * We also deal with the weird ones here. Linux VCs replace F1
1817 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1818 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1823 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
1824 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
1825 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
1826 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
1827 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
1828 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
1829 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
1830 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
1831 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
1832 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
1833 case VK_F11: code = 23; break;
1834 case VK_F12: code = 24; break;
1835 case VK_F13: code = 25; break;
1836 case VK_F14: code = 26; break;
1837 case VK_F15: code = 28; break;
1838 case VK_F16: code = 29; break;
1839 case VK_F17: code = 31; break;
1840 case VK_F18: code = 32; break;
1841 case VK_F19: code = 33; break;
1842 case VK_F20: code = 34; break;
1843 case VK_HOME: code = 1; break;
1844 case VK_INSERT: code = 2; break;
1845 case VK_DELETE: code = 3; break;
1846 case VK_END: code = 4; break;
1847 case VK_PRIOR: code = 5; break;
1848 case VK_NEXT: code = 6; break;
1850 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
1851 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
1854 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
1855 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
1858 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
1859 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
1863 p += sprintf((char *)p, "\x1B[%d~", code);
1868 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
1869 * some reason seems to send VK_CLEAR to Windows...).
1874 case VK_UP: xkey = 'A'; break;
1875 case VK_DOWN: xkey = 'B'; break;
1876 case VK_RIGHT: xkey = 'C'; break;
1877 case VK_LEFT: xkey = 'D'; break;
1878 case VK_CLEAR: xkey = 'G'; break;
1883 p += sprintf((char *)p, "\x1B%c", xkey);
1884 else if (app_cursor_keys)
1885 p += sprintf((char *)p, "\x1BO%c", xkey);
1887 p += sprintf((char *)p, "\x1B[%c", xkey);
1893 /* Okay we've done everything interesting; let windows deal with
1894 * the boring stuff */
1896 BOOL capsOn=keystate[VK_CAPITAL] !=0;
1898 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
1899 if(cfg.xlat_capslockcyr)
1900 keystate[VK_CAPITAL] = 0;
1902 r = ToAscii (wParam, scan, keystate, keys, 0);
1908 unsigned char ch = (unsigned char)keys[i];
1910 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
1915 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
1919 if ((nc=check_compose(compose_char,ch)) == -1)
1924 *p++ = xlat_kbd2tty((unsigned char)nc);
1930 if( left_alt && key_down ) *p++ = '\033';
1936 ch = xlat_latkbd2win(ch);
1937 *p++ = xlat_kbd2tty(ch);
1941 /* This is so the ALT-Numpad and dead keys work correctly. */
1948 /* This stops ALT press-release doing a 'COMMAND MENU' function */
1950 if (message == WM_SYSKEYUP && wParam == VK_MENU)
1952 keystate[VK_MENU] = 0;
1960 void set_title (char *title) {
1961 sfree (window_name);
1962 window_name = smalloc(1+strlen(title));
1963 strcpy (window_name, title);
1964 if (cfg.win_name_always || !IsIconic(hwnd))
1965 SetWindowText (hwnd, title);
1968 void set_icon (char *title) {
1970 icon_name = smalloc(1+strlen(title));
1971 strcpy (icon_name, title);
1972 if (!cfg.win_name_always && IsIconic(hwnd))
1973 SetWindowText (hwnd, title);
1976 void set_sbar (int total, int start, int page) {
1979 if (!cfg.scrollbar) return;
1981 si.cbSize = sizeof(si);
1982 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
1984 si.nMax = total - 1;
1988 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
1991 Context get_ctx(void) {
1996 SelectPalette (hdc, pal, FALSE);
2002 void free_ctx (Context ctx) {
2003 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2004 ReleaseDC (hwnd, ctx);
2007 static void real_palette_set (int n, int r, int g, int b) {
2009 logpal->palPalEntry[n].peRed = r;
2010 logpal->palPalEntry[n].peGreen = g;
2011 logpal->palPalEntry[n].peBlue = b;
2012 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2013 colours[n] = PALETTERGB(r, g, b);
2014 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2016 colours[n] = RGB(r, g, b);
2019 void palette_set (int n, int r, int g, int b) {
2020 static const int first[21] = {
2021 0, 2, 4, 6, 8, 10, 12, 14,
2022 1, 3, 5, 7, 9, 11, 13, 15,
2025 real_palette_set (first[n], r, g, b);
2027 real_palette_set (first[n]+1, r, g, b);
2029 HDC hdc = get_ctx();
2030 UnrealizeObject (pal);
2031 RealizePalette (hdc);
2036 void palette_reset (void) {
2039 for (i = 0; i < NCOLOURS; i++) {
2041 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2042 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2043 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2044 logpal->palPalEntry[i].peFlags = 0;
2045 colours[i] = PALETTERGB(defpal[i].rgbtRed,
2046 defpal[i].rgbtGreen,
2047 defpal[i].rgbtBlue);
2049 colours[i] = RGB(defpal[i].rgbtRed,
2050 defpal[i].rgbtGreen,
2051 defpal[i].rgbtBlue);
2056 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2058 RealizePalette (hdc);
2063 void write_clip (void *data, int len) {
2067 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2070 lock = GlobalLock (clipdata);
2073 memcpy (lock, data, len);
2074 ((unsigned char *) lock) [len] = 0;
2075 GlobalUnlock (clipdata);
2077 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2078 if (OpenClipboard (hwnd)) {
2080 SetClipboardData (CF_TEXT, clipdata);
2083 GlobalFree (clipdata);
2084 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2087 void get_clip (void **p, int *len) {
2088 static HGLOBAL clipdata = NULL;
2092 GlobalUnlock (clipdata);
2096 if (OpenClipboard (NULL)) {
2097 clipdata = GetClipboardData (CF_TEXT);
2100 *p = GlobalLock (clipdata);
2114 * Move `lines' lines from position `from' to position `to' in the
2117 void optimised_move (int to, int from, int lines) {
2121 min = (to < from ? to : from);
2122 max = to + from - min;
2124 r.left = 0; r.right = cols * font_width;
2125 r.top = min * font_height; r.bottom = (max+lines) * font_height;
2126 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2130 * Print a message box and perform a fatal exit.
2132 void fatalbox(char *fmt, ...) {
2137 vsprintf(stuff, fmt, ap);
2139 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2146 void beep(int errorbeep) {
2147 static long last_beep = 0;
2148 long now, beep_diff;
2150 now = GetTickCount();
2151 beep_diff = now-last_beep;
2153 /* Make sure we only respond to one beep per packet or so */
2154 if (beep_diff>=0 && beep_diff<50)
2158 MessageBeep(MB_ICONHAND);
2162 last_beep = GetTickCount();