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;
92 flags = FLAG_VERBOSE | FLAG_WINDOWED | FLAG_CONNECTION;
94 winsock_ver = MAKEWORD(1, 1);
95 if (WSAStartup(winsock_ver, &wsadata)) {
96 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
97 MB_OK | MB_ICONEXCLAMATION);
100 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
101 MessageBox(NULL, "WinSock version is incompatible with 1.1",
102 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
106 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
108 InitCommonControls();
111 * Process the command line.
116 default_protocol = DEFAULT_PROTOCOL;
117 default_port = DEFAULT_PORT;
122 while (*p && isspace(*p)) p++;
125 * Process command line options first. Yes, this can be
126 * done better, and it will be as soon as I have the
130 char *q = p + strcspn(p, " \t");
133 tolower(p[0]) == 's' &&
134 tolower(p[1]) == 's' &&
135 tolower(p[2]) == 'h') {
136 default_protocol = cfg.protocol = PROT_SSH;
137 default_port = cfg.port = 22;
138 } else if (q == p + 3 &&
139 tolower(p[0]) == 'l' &&
140 tolower(p[1]) == 'o' &&
141 tolower(p[2]) == 'g') {
142 logfile = "putty.log";
144 p = q + strspn(q, " \t");
148 * An initial @ means to activate a saved session.
152 if (!*cfg.host && !do_config()) {
156 } else if (*p == '&') {
158 * An initial & means we've been given a command line
159 * containing the hex value of a HANDLE for a file
160 * mapping object, which we must then extract as a
165 if (sscanf(p+1, "%p", &filemap) == 1 &&
166 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
167 0, 0, sizeof(Config))) != NULL) {
170 CloseHandle(filemap);
171 } else if (!do_config()) {
178 * If the hostname starts with "telnet:", set the
179 * protocol to Telnet and process the string as a
182 if (!strncmp(q, "telnet:", 7)) {
186 if (q[0] == '/' && q[1] == '/')
188 cfg.protocol = PROT_TELNET;
190 while (*p && *p != ':' && *p != '/') p++;
198 strncpy (cfg.host, q, sizeof(cfg.host)-1);
199 cfg.host[sizeof(cfg.host)-1] = '\0';
201 while (*p && !isspace(*p)) p++;
204 strncpy (cfg.host, q, sizeof(cfg.host)-1);
205 cfg.host[sizeof(cfg.host)-1] = '\0';
206 while (*p && isspace(*p)) p++;
221 * Select protocol. This is farmed out into a table in a
222 * separate file to enable an ssh-free variant.
227 for (i = 0; backends[i].backend != NULL; i++)
228 if (backends[i].protocol == cfg.protocol) {
229 back = backends[i].backend;
233 MessageBox(NULL, "Unsupported protocol number found",
234 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
240 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
244 wndclass.lpfnWndProc = WndProc;
245 wndclass.cbClsExtra = 0;
246 wndclass.cbWndExtra = 0;
247 wndclass.hInstance = inst;
248 wndclass.hIcon = LoadIcon (inst,
249 MAKEINTRESOURCE(IDI_MAINICON));
250 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
251 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
252 wndclass.lpszMenuName = NULL;
253 wndclass.lpszClassName = appname;
255 RegisterClass (&wndclass);
260 savelines = cfg.savelines;
266 * Guess some defaults for the window size. This all gets
267 * updated later, so we don't really care too much. However, we
268 * do want the font width/height guesses to correspond to a
269 * large font rather than a small one...
276 term_size (cfg.height, cfg.width, cfg.savelines);
277 guess_width = extra_width + font_width * cols;
278 guess_height = extra_height + font_height * rows;
281 HWND w = GetDesktopWindow();
282 GetWindowRect (w, &r);
283 if (guess_width > r.right - r.left)
284 guess_width = r.right - r.left;
285 if (guess_height > r.bottom - r.top)
286 guess_height = r.bottom - r.top;
290 int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
291 if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
292 if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
293 hwnd = CreateWindow (appname, appname,
295 CW_USEDEFAULT, CW_USEDEFAULT,
296 guess_width, guess_height,
297 NULL, NULL, inst, NULL);
301 * Initialise the fonts, simultaneously correcting the guesses
302 * for font_{width,height}.
304 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
309 * Correct the guesses for extra_{width,height}.
313 GetWindowRect (hwnd, &wr);
314 GetClientRect (hwnd, &cr);
315 extra_width = wr.right - wr.left - cr.right + cr.left;
316 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
320 * Resize the window, now we know what size we _really_ want it
323 guess_width = extra_width + font_width * cols;
324 guess_height = extra_height + font_height * rows;
325 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
326 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
327 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
330 * Initialise the scroll bar.
335 si.cbSize = sizeof(si);
336 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
341 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
345 * Start up the telnet connection.
352 error = back->init (hwnd, cfg.host, cfg.port, &realhost);
354 sprintf(msg, "Unable to open connection:\n%s", error);
355 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
358 window_name = icon_name = NULL;
359 sprintf(msg, "%s - PuTTY", realhost);
364 session_closed = FALSE;
367 * Set up the input and output buffers.
370 outbuf_reap = outbuf_head = 0;
373 * Prepare the mouse handler.
375 lastact = MA_NOTHING;
376 lastbtn = MB_NOTHING;
377 dbltime = GetDoubleClickTime();
380 * Set up the session-control options on the system menu.
383 HMENU m = GetSystemMenu (hwnd, FALSE);
387 AppendMenu (m, MF_SEPARATOR, 0, 0);
388 if (cfg.protocol == PROT_TELNET) {
390 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
391 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
392 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
393 AppendMenu (p, MF_SEPARATOR, 0, 0);
394 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
395 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
396 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
397 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
398 AppendMenu (p, MF_SEPARATOR, 0, 0);
399 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
400 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
401 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
402 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
403 AppendMenu (p, MF_SEPARATOR, 0, 0);
404 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
405 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
406 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
407 AppendMenu (m, MF_SEPARATOR, 0, 0);
409 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
410 AppendMenu (m, MF_SEPARATOR, 0, 0);
411 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
412 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
415 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
416 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
417 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
418 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
419 AppendMenu (m, MF_SEPARATOR, 0, 0);
420 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
421 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
422 AppendMenu (m, MF_SEPARATOR, 0, 0);
423 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
427 * Finally show the window!
429 ShowWindow (hwnd, show);
432 * Set the palette up.
438 has_focus = (GetForegroundWindow() == hwnd);
442 int timer_id = 0, long_timer = 0;
444 while (GetMessage (&msg, NULL, 0, 0) == 1) {
445 /* Sometimes DispatchMessage calls routines that use their own
446 * GetMessage loop, setup this timer so we get some control back.
448 * Also call term_update() from the timer so that if the host
449 * is sending data flat out we still do redraws.
451 if(timer_id && long_timer) {
452 KillTimer(hwnd, timer_id);
453 long_timer = timer_id = 0;
456 timer_id = SetTimer(hwnd, 1, 20, NULL);
457 DispatchMessage (&msg);
459 /* This is too fast, but I'll leave it for now 'cause it shows
460 * how often term_update is called (far too often at times!)
464 /* Send the paste buffer if there's anything to send */
467 /* If there's nothing new in the queue then we can do everything
468 * we've delayed, reading the socket, writing, and repainting
471 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
472 if (pending_netevent) {
473 enact_pending_netevent();
478 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
480 KillTimer(hwnd, timer_id);
487 timer_id = SetTimer(hwnd, 1, 2000, NULL);
488 else if (cfg.blinktext)
489 timer_id = SetTimer(hwnd, 1, 250, NULL);
491 timer_id = SetTimer(hwnd, 1, 500, NULL);
504 DeleteObject(fonts[i]);
511 if (cfg.protocol == PROT_SSH) {
522 * Actually do the job requested by a WM_NETEVENT
524 static void enact_pending_netevent(void) {
526 pending_netevent = FALSE;
527 i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
531 switch (WSABASEERR + (-i) % 10000) {
533 sprintf(buf, "Connection reset by peer");
536 sprintf(buf, "Unexpected network error %d", -i);
539 MessageBox(hwnd, buf, "PuTTY Fatal Error",
540 MB_ICONERROR | MB_OK);
543 if (cfg.close_on_exit)
546 session_closed = TRUE;
547 MessageBox(hwnd, "Connection closed by remote host",
548 "PuTTY", MB_OK | MB_ICONINFORMATION);
549 SetWindowText (hwnd, "PuTTY (inactive)");
555 * Copy the colour palette from the configuration data into defpal.
556 * This is non-trivial because the colour indices are different.
558 static void cfgtopalette(void) {
560 static const int ww[] = {
561 6, 7, 8, 9, 10, 11, 12, 13,
562 14, 15, 16, 17, 18, 19, 20, 21,
563 0, 1, 2, 3, 4, 4, 5, 5
566 for (i=0; i<24; i++) {
568 defpal[i].rgbtRed = cfg.colours[w][0];
569 defpal[i].rgbtGreen = cfg.colours[w][1];
570 defpal[i].rgbtBlue = cfg.colours[w][2];
575 * Set up the colour palette.
577 static void init_palette(void) {
579 HDC hdc = GetDC (hwnd);
581 if (cfg.try_palette &&
582 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
583 logpal = smalloc(sizeof(*logpal)
584 - sizeof(logpal->palPalEntry)
585 + NCOLOURS * sizeof(PALETTEENTRY));
586 logpal->palVersion = 0x300;
587 logpal->palNumEntries = NCOLOURS;
588 for (i = 0; i < NCOLOURS; i++) {
589 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
590 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
591 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
592 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
594 pal = CreatePalette (logpal);
596 SelectPalette (hdc, pal, FALSE);
597 RealizePalette (hdc);
598 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
602 ReleaseDC (hwnd, hdc);
605 for (i=0; i<NCOLOURS; i++)
606 colours[i] = PALETTERGB(defpal[i].rgbtRed,
610 for(i=0; i<NCOLOURS; i++)
611 colours[i] = RGB(defpal[i].rgbtRed,
617 * Initialise all the fonts we will need. There may be as many as
618 * eight or as few as one. We also:
620 * - check the font width and height, correcting our guesses if
623 * - verify that the bold font is the same width as the ordinary
624 * one, and engage shadow bolding if not.
626 * - verify that the underlined font is the same width as the
627 * ordinary one (manual underlining by means of line drawing can
628 * be done in a pinch).
630 static void init_fonts(int pick_width) {
635 int fw_dontcare, fw_bold;
644 if (cfg.fontisbold) {
645 fw_dontcare = FW_BOLD;
648 fw_dontcare = FW_DONTCARE;
654 font_height = cfg.fontheight;
655 font_width = pick_width;
658 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
659 c, OUT_DEFAULT_PRECIS, \
660 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
661 FIXED_PITCH | FF_DONTCARE, cfg.font)
663 if (cfg.vtmode != VT_OEMONLY) {
664 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
666 SelectObject (hdc, fonts[FONT_NORMAL]);
667 GetTextMetrics(hdc, &tm);
668 font_height = tm.tmHeight;
669 font_width = tm.tmAveCharWidth;
671 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
673 if (bold_mode == BOLD_FONT) {
674 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
675 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
678 if (cfg.vtmode == VT_OEMANSI) {
679 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
680 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
682 if (bold_mode == BOLD_FONT) {
683 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
684 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
690 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
692 SelectObject (hdc, fonts[FONT_OEM]);
693 GetTextMetrics(hdc, &tm);
694 font_height = tm.tmHeight;
695 font_width = tm.tmAveCharWidth;
697 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
699 if (bold_mode == BOLD_FONT) {
700 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
701 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
706 descent = tm.tmAscent + 1;
707 if (descent >= font_height)
708 descent = font_height - 1;
709 firstchar = tm.tmFirstChar;
711 for (i=0; i<8; i++) {
713 if (SelectObject (hdc, fonts[i]) &&
714 GetTextMetrics(hdc, &tm) )
715 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
721 ReleaseDC (hwnd, hdc);
723 /* ... This is wrong in OEM only mode */
724 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
725 (bold_mode == BOLD_FONT &&
726 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
728 DeleteObject (fonts[FONT_UNDERLINE]);
729 if (bold_mode == BOLD_FONT)
730 DeleteObject (fonts[FONT_BOLDUND]);
733 if (bold_mode == BOLD_FONT &&
734 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
735 bold_mode = BOLD_SHADOW;
736 DeleteObject (fonts[FONT_BOLD]);
737 if (und_mode == UND_FONT)
738 DeleteObject (fonts[FONT_BOLDUND]);
742 /* With the fascist font painting it doesn't matter if the linedraw font
743 * isn't exactly the right size anymore so we don't have to check this.
745 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
746 if( cfg.fontcharset == OEM_CHARSET )
748 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
749 "different sizes. Using OEM-only mode instead",
750 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
751 cfg.vtmode = VT_OEMONLY;
753 else if( firstchar < ' ' )
755 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
756 "different sizes. Using XTerm mode instead",
757 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
758 cfg.vtmode = VT_XWINDOWS;
762 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
763 "different sizes. Using ISO8859-1 mode instead",
764 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
765 cfg.vtmode = VT_POORMAN;
770 DeleteObject (fonts[i]);
776 void request_resize (int w, int h, int refont) {
779 /* If the window is maximized supress resizing attempts */
780 if(IsZoomed(hwnd)) return;
783 /* Don't do this in OEMANSI, you may get disable messages */
784 if (refont && w != cols && (cols==80 || cols==132)
785 && cfg.vtmode != VT_OEMANSI)
787 if (refont && w != cols && (cols==80 || cols==132))
790 /* If font width too big for screen should we shrink the font more ? */
792 font_width = ((font_width*cols+w/2)/w);
799 DeleteObject(fonts[i]);
801 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
803 init_fonts(font_width);
807 static int first_time = 1;
813 /* Get the size of the screen */
814 if (GetClientRect(GetDesktopWindow(),&ss))
815 /* first_time = 0 */;
816 else { first_time = 2; break; }
818 /* Make sure the values are sane */
819 width = (ss.right-ss.left-extra_width ) / font_width;
820 height = (ss.bottom-ss.top-extra_height ) / font_height;
822 if (w>width) w=width;
823 if (h>height) h=height;
829 width = extra_width + font_width * w;
830 height = extra_height + font_height * h;
832 SetWindowPos (hwnd, NULL, 0, 0, width, height,
833 SWP_NOACTIVATE | SWP_NOCOPYBITS |
834 SWP_NOMOVE | SWP_NOZORDER);
837 static void click (Mouse_Button b, int x, int y) {
838 int thistime = GetMessageTime();
840 if (lastbtn == b && thistime - lasttime < dbltime) {
841 lastact = (lastact == MA_CLICK ? MA_2CLK :
842 lastact == MA_2CLK ? MA_3CLK :
843 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
848 if (lastact != MA_NOTHING)
849 term_mouse (b, lastact, x, y);
853 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
854 WPARAM wParam, LPARAM lParam) {
856 static int ignore_size = FALSE;
857 static int ignore_clip = FALSE;
858 static int just_reconfigged = FALSE;
862 if (pending_netevent)
863 enact_pending_netevent();
871 if (!cfg.warn_on_close || session_closed ||
872 MessageBox(hwnd, "Are you sure you want to close this session?",
873 "PuTTY Exit Confirmation",
874 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
881 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
893 PROCESS_INFORMATION pi;
894 HANDLE filemap = NULL;
896 if (wParam == IDM_DUPSESS) {
898 * Allocate a file-mapping memory chunk for the
901 SECURITY_ATTRIBUTES sa;
904 sa.nLength = sizeof(sa);
905 sa.lpSecurityDescriptor = NULL;
906 sa.bInheritHandle = TRUE;
907 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
914 p = (Config *)MapViewOfFile(filemap,
916 0, 0, sizeof(Config));
918 *p = cfg; /* structure copy */
922 sprintf(c, "putty &%p", filemap);
924 } else if (wParam == IDM_SAVEDSESS) {
925 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
926 cl = malloc(16 + strlen(session)); /* 8, but play safe */
928 cl = NULL; /* not a very important failure mode */
930 sprintf(cl, "putty @%s", session);
936 GetModuleFileName (NULL, b, sizeof(b)-1);
938 si.lpReserved = NULL;
943 si.lpReserved2 = NULL;
944 CreateProcess (b, cl, NULL, NULL, TRUE,
945 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
948 CloseHandle(filemap);
954 if (!do_reconfig(hwnd))
956 just_reconfigged = TRUE;
961 DeleteObject(fonts[i]);
963 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
967 /* Telnet will change local echo -> remote if the remote asks */
968 if (cfg.protocol != PROT_TELNET)
969 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
977 /* Enable or disable the scroll bar, etc */
979 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
982 if (cfg.scrollbar) nflg |= WS_VSCROLL;
983 else nflg &= ~WS_VSCROLL;
985 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
987 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
993 SetWindowLong(hwnd, GWL_STYLE, nflg);
994 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
995 SetWindowPos(hwnd, NULL, 0,0,0,0,
996 SWP_NOACTIVATE|SWP_NOCOPYBITS|
997 SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER|
1000 GetWindowRect (hwnd, &wr);
1001 GetClientRect (hwnd, &cr);
1002 extra_width = wr.right - wr.left - cr.right + cr.left;
1003 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1007 term_size(cfg.height, cfg.width, cfg.savelines);
1008 InvalidateRect(hwnd, NULL, TRUE);
1009 SetWindowPos (hwnd, NULL, 0, 0,
1010 extra_width + font_width * cfg.width,
1011 extra_height + font_height * cfg.height,
1012 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1013 SWP_NOMOVE | SWP_NOZORDER);
1014 if (IsIconic(hwnd)) {
1015 SetWindowText (hwnd,
1016 cfg.win_name_always ? window_name : icon_name);
1025 case IDM_TEL_AYT: back->special (TS_AYT); break;
1026 case IDM_TEL_BRK: back->special (TS_BRK); break;
1027 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1028 case IDM_TEL_EC: back->special (TS_EC); break;
1029 case IDM_TEL_EL: back->special (TS_EL); break;
1030 case IDM_TEL_GA: back->special (TS_GA); break;
1031 case IDM_TEL_NOP: back->special (TS_NOP); break;
1032 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1033 case IDM_TEL_AO: back->special (TS_AO); break;
1034 case IDM_TEL_IP: back->special (TS_IP); break;
1035 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1036 case IDM_TEL_EOR: back->special (TS_EOR); break;
1037 case IDM_TEL_EOF: back->special (TS_EOF); break;
1042 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1043 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1048 #define X_POS(l) ((int)(short)LOWORD(l))
1049 #define Y_POS(l) ((int)(short)HIWORD(l))
1051 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1052 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1054 case WM_LBUTTONDOWN:
1055 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1056 TO_CHR_Y(Y_POS(lParam)));
1060 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1061 TO_CHR_Y(Y_POS(lParam)));
1064 case WM_MBUTTONDOWN:
1066 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1067 TO_CHR_X(X_POS(lParam)),
1068 TO_CHR_Y(Y_POS(lParam)));
1071 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1072 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1073 TO_CHR_Y(Y_POS(lParam)));
1076 case WM_RBUTTONDOWN:
1078 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1079 TO_CHR_X(X_POS(lParam)),
1080 TO_CHR_Y(Y_POS(lParam)));
1083 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1084 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1085 TO_CHR_Y(Y_POS(lParam)));
1090 * Add the mouse position and message time to the random
1091 * number noise, if we're using ssh.
1093 if (cfg.protocol == PROT_SSH)
1094 noise_ultralight(lParam);
1096 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1098 if (wParam & MK_LBUTTON)
1100 else if (wParam & MK_MBUTTON)
1101 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1103 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1104 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1105 TO_CHR_Y(Y_POS(lParam)));
1108 case WM_IGNORE_CLIP:
1109 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1111 case WM_DESTROYCLIPBOARD:
1114 ignore_clip = FALSE;
1119 hdc = BeginPaint (hwnd, &p);
1121 SelectPalette (hdc, pal, TRUE);
1122 RealizePalette (hdc);
1124 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1125 p.rcPaint.right, p.rcPaint.bottom);
1126 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1127 SelectObject (hdc, GetStockObject(WHITE_PEN));
1128 EndPaint (hwnd, &p);
1132 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1133 * but the only one that's likely to try to overload us is FD_READ.
1134 * This means buffering just one is fine.
1136 if (pending_netevent)
1137 enact_pending_netevent();
1139 pending_netevent = TRUE;
1140 pend_netevent_wParam=wParam;
1141 pend_netevent_lParam=lParam;
1153 case WM_IGNORE_SIZE:
1154 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1156 case WM_ENTERSIZEMOVE:
1159 case WM_EXITSIZEMOVE:
1164 int width, height, w, h, ew, eh;
1165 LPRECT r = (LPRECT)lParam;
1167 width = r->right - r->left - extra_width;
1168 height = r->bottom - r->top - extra_height;
1169 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1170 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1171 UpdateSizeTip(hwnd, w, h);
1172 ew = width - w * font_width;
1173 eh = height - h * font_height;
1175 if (wParam == WMSZ_LEFT ||
1176 wParam == WMSZ_BOTTOMLEFT ||
1177 wParam == WMSZ_TOPLEFT)
1183 if (wParam == WMSZ_TOP ||
1184 wParam == WMSZ_TOPRIGHT ||
1185 wParam == WMSZ_TOPLEFT)
1195 /* break; (never reached) */
1197 if (wParam == SIZE_MINIMIZED) {
1198 SetWindowText (hwnd,
1199 cfg.win_name_always ? window_name : icon_name);
1202 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1203 SetWindowText (hwnd, window_name);
1205 int width, height, w, h;
1206 #if 0 /* we have fixed this using WM_SIZING now */
1210 width = LOWORD(lParam);
1211 height = HIWORD(lParam);
1212 w = width / font_width; if (w < 1) w = 1;
1213 h = height / font_height; if (h < 1) h = 1;
1214 #if 0 /* we have fixed this using WM_SIZING now */
1215 ew = width - w * font_width;
1216 eh = height - h * font_height;
1217 if (ew != 0 || eh != 0) {
1219 GetWindowRect (hwnd, &r);
1220 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1221 SetWindowPos (hwnd, NULL, 0, 0,
1222 r.right - r.left - ew, r.bottom - r.top - eh,
1223 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1226 if (w != cols || h != rows || just_reconfigged) {
1228 term_size (h, w, cfg.savelines);
1230 just_reconfigged = FALSE;
1233 ignore_size = FALSE;
1236 switch (LOWORD(wParam)) {
1237 case SB_BOTTOM: term_scroll(-1, 0); break;
1238 case SB_TOP: term_scroll(+1, 0); break;
1239 case SB_LINEDOWN: term_scroll (0, +1); break;
1240 case SB_LINEUP: term_scroll (0, -1); break;
1241 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1242 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1243 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1244 term_scroll (1, HIWORD(wParam)); break;
1247 case WM_PALETTECHANGED:
1248 if ((HWND) wParam != hwnd && pal != NULL) {
1249 HDC hdc = get_ctx();
1251 if (RealizePalette (hdc) > 0)
1257 case WM_QUERYNEWPALETTE:
1259 HDC hdc = get_ctx();
1261 if (RealizePalette (hdc) > 0)
1273 * Add the scan code and keypress timing to the random
1274 * number noise, if we're using ssh.
1276 if (cfg.protocol == PROT_SSH)
1277 noise_ultralight(lParam);
1280 * We don't do TranslateMessage since it disassociates the
1281 * resulting CHAR message from the KEYDOWN that sparked it,
1282 * which we occasionally don't want. Instead, we process
1283 * KEYDOWN, and call the Win32 translator functions so that
1284 * we get the translations under _our_ control.
1287 unsigned char buf[20];
1290 len = TranslateKey (message, wParam, lParam, buf);
1292 return DefWindowProc (hwnd, message, wParam, lParam);
1293 ldisc->send (buf, len);
1299 * Nevertheless, we are prepared to deal with WM_CHAR
1300 * messages, should they crop up. So if someone wants to
1301 * post the things to us as part of a macro manoeuvre,
1302 * we're ready to cope.
1305 char c = xlat_kbd2tty((unsigned char)wParam);
1306 ldisc->send (&c, 1);
1311 return DefWindowProc (hwnd, message, wParam, lParam);
1315 * Draw a line of text in the window, at given character
1316 * coordinates, in given attributes.
1318 * We are allowed to fiddle with the contents of `text'.
1320 void do_text (Context ctx, int x, int y, char *text, int len,
1321 unsigned long attr, int lattr) {
1323 int nfg, nbg, nfont;
1326 int force_manual_underline = 0;
1327 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1328 static int *IpDx = 0, IpDxLEN = 0;;
1330 if (len>IpDxLEN || IpDx[0] != fnt_width) {
1334 IpDx = smalloc((len+16)*sizeof(int));
1337 for(i=0; i<IpDxLEN; i++)
1338 IpDx[i] = fnt_width;
1344 if (attr & ATTR_ACTCURS) {
1345 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1346 attr ^= ATTR_CUR_XOR;
1350 if (cfg.vtmode == VT_OEMONLY)
1354 * Map high-half characters in order to approximate ISO using
1355 * OEM character set. No characters are missing if the OEM codepage
1358 if (nfont & FONT_OEM) {
1360 for (i=0; i<len; i++)
1361 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1363 /* This is CP850 ... perfect translation */
1364 static const char oemhighhalf[] =
1365 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1366 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1367 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1368 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1369 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1370 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1371 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1372 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1373 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1374 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1375 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1376 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1379 /* This is CP437 ... junk translation */
1380 static const unsigned char oemhighhalf[] = {
1381 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1382 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1383 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1384 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1385 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1386 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1387 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1388 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1389 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1390 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1391 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1392 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1395 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1399 if (attr & ATTR_GBCHR) {
1402 * GB mapping: map # to pound, and everything else stays
1405 for (i=0; i<len; i++)
1407 text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
1408 } else if (attr & ATTR_LINEDRW) {
1411 static const char poorman[] =
1412 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1415 static const char oemmap_437[] =
1416 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1417 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1420 static const char oemmap_850[] =
1421 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1422 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1424 /* Poor windows font ... eg: windows courier */
1425 static const char oemmap[] =
1426 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1427 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1430 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1431 * VT100 line drawing chars; everything else stays normal.
1433 switch (cfg.vtmode) {
1435 for (i=0; i<len; i++)
1436 if (text[i] >= '\x60' && text[i] <= '\x7E')
1437 text[i] += '\x01' - '\x60';
1440 /* Make sure we actually have an OEM font */
1441 if (fonts[nfont|FONT_OEM]) {
1444 for (i=0; i<len; i++)
1445 if (text[i] >= '\x60' && text[i] <= '\x7E')
1446 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1450 for (i=0; i<len; i++)
1451 if (text[i] >= '\x60' && text[i] <= '\x7E')
1452 text[i] = poorman[(unsigned char)text[i] - 0x60];
1457 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1458 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1459 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1461 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1462 nfont |= FONT_UNDERLINE;
1465 if (nfont&FONT_UNDERLINE)
1466 force_manual_underline = 1;
1467 /* Don't do the same for manual bold, it could be bad news. */
1469 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1471 if (attr & ATTR_REVERSE) {
1472 t = nfg; nfg = nbg; nbg = t;
1474 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1476 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1480 SelectObject (hdc, fonts[nfont]);
1481 SetTextColor (hdc, fg);
1482 SetBkColor (hdc, bg);
1483 SetBkMode (hdc, OPAQUE);
1486 line_box.right = x+fnt_width*len;
1487 line_box.bottom = y+font_height;
1488 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1489 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1490 SetBkMode (hdc, TRANSPARENT);
1492 /* GRR: This draws the character outside it's box and can leave
1493 * 'droppings' even with the clip box! I suppose I could loop it
1494 * one character at a time ... yuk.
1496 * Or ... I could do a test print with "W", and use +1 or -1 for this
1497 * shift depending on if the leftmost column is blank...
1499 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1501 if (force_manual_underline ||
1502 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1504 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1505 MoveToEx (hdc, x, y+descent, NULL);
1506 LineTo (hdc, x+len*fnt_width, y+descent);
1507 oldpen = SelectObject (hdc, oldpen);
1508 DeleteObject (oldpen);
1510 if (attr & ATTR_PASCURS) {
1513 pts[0].x = pts[1].x = pts[4].x = x;
1514 pts[2].x = pts[3].x = x+fnt_width-1;
1515 pts[0].y = pts[3].y = pts[4].y = y;
1516 pts[1].y = pts[2].y = y+font_height-1;
1517 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1518 Polyline (hdc, pts, 5);
1519 oldpen = SelectObject (hdc, oldpen);
1520 DeleteObject (oldpen);
1524 static int check_compose(int first, int second) {
1526 static char * composetbl[] = {
1527 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1528 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1529 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1530 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1531 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1532 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1533 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1534 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1535 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1536 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1537 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1538 "\"uü", "'yý", "htþ", "\"yÿ",
1542 static int recurse = 0;
1549 sprintf(buf, "cc(%d,%d)", first, second);
1554 for(c=composetbl; *c; c++) {
1555 if( (*c)[0] == first && (*c)[1] == second)
1557 return (*c)[2] & 0xFF;
1564 nc = check_compose(second, first);
1566 nc = check_compose(toupper(first), toupper(second));
1568 nc = check_compose(toupper(second), toupper(first));
1576 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1577 * codes. Returns number of bytes used or zero to drop the message
1578 * or -1 to forward the message to windows.
1580 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output) {
1582 int scan, left_alt = 0, key_down, shift_state;
1584 unsigned char * p = output;
1586 static WORD keys[3];
1587 static int compose_state = 0;
1588 static int compose_char = 0;
1589 static WPARAM compose_key = 0;
1591 r = GetKeyboardState(keystate);
1592 if (!r) memset(keystate, 0, sizeof(keystate));
1595 /* Note if AltGr was pressed and if it was used as a compose key */
1596 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
1598 keystate[VK_RMENU] = keystate[VK_MENU];
1599 if (!compose_state) compose_key = wParam;
1601 if (wParam == VK_APPS && !compose_state)
1602 compose_key = wParam;
1604 if (wParam == compose_key)
1606 if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1608 else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
1613 else if (compose_state==1 && wParam != VK_CONTROL)
1616 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1617 if ( (cfg.funky_type == 0 || (cfg.funky_type == 1 && app_keypad_keys))
1618 && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
1620 wParam = VK_EXECUTE;
1622 /* UnToggle NUMLock */
1623 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1624 keystate[VK_NUMLOCK] ^= 1;
1627 /* And write back the 'adjusted' state */
1628 SetKeyboardState (keystate);
1631 /* Disable Auto repeat if required */
1632 if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
1635 if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
1638 key_down = ((HIWORD(lParam)&KF_UP)==0);
1640 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1641 if (left_alt && (keystate[VK_CONTROL]&0x80))
1642 keystate[VK_MENU] = 0;
1644 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
1645 shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
1646 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
1649 * Record that we pressed key so the scroll window can be reset, but
1650 * be careful to avoid Shift-UP/Down
1652 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
1656 /* Make sure we're not pasting */
1657 if (key_down) term_nopaste();
1659 if (compose_state>1 && left_alt) compose_state = 0;
1661 /* Sanitize the number pad if not using a PC NumPad */
1662 if( left_alt || (app_keypad_keys && cfg.funky_type != 2)
1663 || cfg.nethack_keypad || compose_state )
1665 if ((HIWORD(lParam)&KF_EXTENDED) == 0)
1670 case VK_INSERT: nParam = VK_NUMPAD0; break;
1671 case VK_END: nParam = VK_NUMPAD1; break;
1672 case VK_DOWN: nParam = VK_NUMPAD2; break;
1673 case VK_NEXT: nParam = VK_NUMPAD3; break;
1674 case VK_LEFT: nParam = VK_NUMPAD4; break;
1675 case VK_CLEAR: nParam = VK_NUMPAD5; break;
1676 case VK_RIGHT: nParam = VK_NUMPAD6; break;
1677 case VK_HOME: nParam = VK_NUMPAD7; break;
1678 case VK_UP: nParam = VK_NUMPAD8; break;
1679 case VK_PRIOR: nParam = VK_NUMPAD9; break;
1680 case VK_DELETE: nParam = VK_DECIMAL; break;
1684 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
1690 /* If a key is pressed and AltGr is not active */
1691 if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
1693 /* Okay, prepare for most alts then ...*/
1694 if (left_alt) *p++ = '\033';
1696 /* Lets see if it's a pattern we know all about ... */
1697 if (wParam == VK_PRIOR && shift_state == 1) {
1698 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
1701 if (wParam == VK_NEXT && shift_state == 1) {
1702 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
1705 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
1708 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
1709 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
1713 /* Nethack keypad */
1714 if (cfg.nethack_keypad && !left_alt) {
1716 case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
1717 case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
1718 case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
1719 case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
1720 case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
1721 case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
1722 case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
1723 case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
1724 case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
1728 /* Application Keypad */
1732 if ( cfg.funky_type == 0 ||
1733 ( cfg.funky_type == 1 && app_keypad_keys)) switch(wParam) {
1734 case VK_EXECUTE: xkey = 'P'; break;
1735 case VK_DIVIDE: xkey = 'Q'; break;
1736 case VK_MULTIPLY:xkey = 'R'; break;
1737 case VK_SUBTRACT:xkey = 'S'; break;
1739 if(app_keypad_keys) switch(wParam) {
1740 case VK_NUMPAD0: xkey = 'p'; break;
1741 case VK_NUMPAD1: xkey = 'q'; break;
1742 case VK_NUMPAD2: xkey = 'r'; break;
1743 case VK_NUMPAD3: xkey = 's'; break;
1744 case VK_NUMPAD4: xkey = 't'; break;
1745 case VK_NUMPAD5: xkey = 'u'; break;
1746 case VK_NUMPAD6: xkey = 'v'; break;
1747 case VK_NUMPAD7: xkey = 'w'; break;
1748 case VK_NUMPAD8: xkey = 'x'; break;
1749 case VK_NUMPAD9: xkey = 'y'; break;
1751 case VK_DECIMAL: xkey = 'n'; break;
1752 case VK_ADD: if(shift_state) xkey = 'm';
1756 if (HIWORD(lParam)&KF_EXTENDED)
1764 if (xkey>='P' && xkey<='S')
1765 p += sprintf((char *)p, "\x1B%c", xkey);
1767 p += sprintf((char *)p, "\x1B?%c", xkey);
1770 p += sprintf((char *)p, "\x1BO%c", xkey);
1775 if (wParam == VK_BACK && shift_state == 0 ) /* Backspace */
1777 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
1780 if (wParam == VK_TAB && shift_state == 1 ) /* Shift tab */
1782 *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
1784 if (wParam == VK_SPACE && shift_state == 2 ) /* Ctrl-Space */
1786 *p++ = 0; return p - output;
1788 if (wParam == VK_SPACE && shift_state == 3 ) /* Ctrl-Shift-Space */
1790 *p++ = 160; return p - output;
1792 if (wParam == VK_CANCEL && shift_state == 2 ) /* Ctrl-Break */
1794 *p++ = 3; return p - output;
1796 /* Control-2 to Control-8 are special */
1797 if (shift_state == 2 && wParam >= '2' && wParam <= '8')
1799 *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
1802 if (shift_state == 2 && wParam == 0xBD) {
1806 if (shift_state == 2 && wParam == 0xDF) {
1810 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
1811 *p++ = '\r'; *p++ = '\n';
1816 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1817 * for integer decimal nn.)
1819 * We also deal with the weird ones here. Linux VCs replace F1
1820 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1821 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1826 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
1827 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
1828 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
1829 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
1830 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
1831 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
1832 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
1833 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
1834 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
1835 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
1836 case VK_F11: code = 23; break;
1837 case VK_F12: code = 24; break;
1838 case VK_F13: code = 25; break;
1839 case VK_F14: code = 26; break;
1840 case VK_F15: code = 28; break;
1841 case VK_F16: code = 29; break;
1842 case VK_F17: code = 31; break;
1843 case VK_F18: code = 32; break;
1844 case VK_F19: code = 33; break;
1845 case VK_F20: code = 34; break;
1846 case VK_HOME: code = 1; break;
1847 case VK_INSERT: code = 2; break;
1848 case VK_DELETE: code = 3; break;
1849 case VK_END: code = 4; break;
1850 case VK_PRIOR: code = 5; break;
1851 case VK_NEXT: code = 6; break;
1853 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
1854 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
1857 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
1858 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
1861 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
1862 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
1866 p += sprintf((char *)p, "\x1B[%d~", code);
1871 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
1872 * some reason seems to send VK_CLEAR to Windows...).
1877 case VK_UP: xkey = 'A'; break;
1878 case VK_DOWN: xkey = 'B'; break;
1879 case VK_RIGHT: xkey = 'C'; break;
1880 case VK_LEFT: xkey = 'D'; break;
1881 case VK_CLEAR: xkey = 'G'; break;
1886 p += sprintf((char *)p, "\x1B%c", xkey);
1887 else if (app_cursor_keys)
1888 p += sprintf((char *)p, "\x1BO%c", xkey);
1890 p += sprintf((char *)p, "\x1B[%c", xkey);
1896 /* Okay we've done everything interesting; let windows deal with
1897 * the boring stuff */
1899 BOOL capsOn=keystate[VK_CAPITAL] !=0;
1901 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
1902 if(cfg.xlat_capslockcyr)
1903 keystate[VK_CAPITAL] = 0;
1905 r = ToAscii (wParam, scan, keystate, keys, 0);
1911 unsigned char ch = (unsigned char)keys[i];
1913 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
1918 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
1922 if ((nc=check_compose(compose_char,ch)) == -1)
1927 *p++ = xlat_kbd2tty((unsigned char)nc);
1933 if( left_alt && key_down ) *p++ = '\033';
1939 ch = xlat_latkbd2win(ch);
1940 *p++ = xlat_kbd2tty(ch);
1944 /* This is so the ALT-Numpad and dead keys work correctly. */
1951 /* This stops ALT press-release doing a 'COMMAND MENU' function */
1953 if (message == WM_SYSKEYUP && wParam == VK_MENU)
1955 keystate[VK_MENU] = 0;
1963 void set_title (char *title) {
1964 sfree (window_name);
1965 window_name = smalloc(1+strlen(title));
1966 strcpy (window_name, title);
1967 if (cfg.win_name_always || !IsIconic(hwnd))
1968 SetWindowText (hwnd, title);
1971 void set_icon (char *title) {
1973 icon_name = smalloc(1+strlen(title));
1974 strcpy (icon_name, title);
1975 if (!cfg.win_name_always && IsIconic(hwnd))
1976 SetWindowText (hwnd, title);
1979 void set_sbar (int total, int start, int page) {
1982 if (!cfg.scrollbar) return;
1984 si.cbSize = sizeof(si);
1985 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
1987 si.nMax = total - 1;
1991 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
1994 Context get_ctx(void) {
1999 SelectPalette (hdc, pal, FALSE);
2005 void free_ctx (Context ctx) {
2006 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2007 ReleaseDC (hwnd, ctx);
2010 static void real_palette_set (int n, int r, int g, int b) {
2012 logpal->palPalEntry[n].peRed = r;
2013 logpal->palPalEntry[n].peGreen = g;
2014 logpal->palPalEntry[n].peBlue = b;
2015 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2016 colours[n] = PALETTERGB(r, g, b);
2017 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2019 colours[n] = RGB(r, g, b);
2022 void palette_set (int n, int r, int g, int b) {
2023 static const int first[21] = {
2024 0, 2, 4, 6, 8, 10, 12, 14,
2025 1, 3, 5, 7, 9, 11, 13, 15,
2028 real_palette_set (first[n], r, g, b);
2030 real_palette_set (first[n]+1, r, g, b);
2032 HDC hdc = get_ctx();
2033 UnrealizeObject (pal);
2034 RealizePalette (hdc);
2039 void palette_reset (void) {
2042 for (i = 0; i < NCOLOURS; i++) {
2044 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2045 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2046 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2047 logpal->palPalEntry[i].peFlags = 0;
2048 colours[i] = PALETTERGB(defpal[i].rgbtRed,
2049 defpal[i].rgbtGreen,
2050 defpal[i].rgbtBlue);
2052 colours[i] = RGB(defpal[i].rgbtRed,
2053 defpal[i].rgbtGreen,
2054 defpal[i].rgbtBlue);
2059 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2061 RealizePalette (hdc);
2066 void write_clip (void *data, int len) {
2070 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2073 lock = GlobalLock (clipdata);
2076 memcpy (lock, data, len);
2077 ((unsigned char *) lock) [len] = 0;
2078 GlobalUnlock (clipdata);
2080 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2081 if (OpenClipboard (hwnd)) {
2083 SetClipboardData (CF_TEXT, clipdata);
2086 GlobalFree (clipdata);
2087 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2090 void get_clip (void **p, int *len) {
2091 static HGLOBAL clipdata = NULL;
2095 GlobalUnlock (clipdata);
2099 if (OpenClipboard (NULL)) {
2100 clipdata = GetClipboardData (CF_TEXT);
2103 *p = GlobalLock (clipdata);
2117 * Move `lines' lines from position `from' to position `to' in the
2120 void optimised_move (int to, int from, int lines) {
2124 min = (to < from ? to : from);
2125 max = to + from - min;
2127 r.left = 0; r.right = cols * font_width;
2128 r.top = min * font_height; r.bottom = (max+lines) * font_height;
2129 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2133 * Print a message box and perform a fatal exit.
2135 void fatalbox(char *fmt, ...) {
2140 vsprintf(stuff, fmt, ap);
2142 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2149 void beep(int errorbeep) {
2150 static long last_beep = 0;
2151 long now, beep_diff;
2153 now = GetTickCount();
2154 beep_diff = now-last_beep;
2156 /* Make sure we only respond to one beep per packet or so */
2157 if (beep_diff>=0 && beep_diff<50)
2161 MessageBeep(MB_ICONHAND);
2165 last_beep = GetTickCount();