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)
39 #define WM_IGNORE_KEYMENU (WM_XUSER + 3)
41 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
42 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
43 static void cfgtopalette(void);
44 static void init_palette(void);
45 static void init_fonts(int);
47 static int extra_width, extra_height;
49 static int pending_netevent = 0;
50 static WPARAM pend_netevent_wParam = 0;
51 static LPARAM pend_netevent_lParam = 0;
52 static void enact_pending_netevent(void);
56 #define FONT_UNDERLINE 2
57 #define FONT_BOLDUND 3
59 #define FONT_OEMBOLD 5
60 #define FONT_OEMBOLDUND 6
62 static HFONT fonts[8];
64 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
72 static COLORREF colours[NCOLOURS];
74 static LPLOGPALETTE logpal;
75 static RGBTRIPLE defpal[NCOLOURS];
79 static int dbltime, lasttime, lastact;
80 static Mouse_Button lastbtn;
82 static char *window_name, *icon_name;
84 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
85 static char appname[] = "PuTTY";
90 int guess_width, guess_height;
93 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
95 winsock_ver = MAKEWORD(1, 1);
96 if (WSAStartup(winsock_ver, &wsadata)) {
97 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
98 MB_OK | MB_ICONEXCLAMATION);
101 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
102 MessageBox(NULL, "WinSock version is incompatible with 1.1",
103 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
107 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
109 InitCommonControls();
112 * Process the command line.
117 default_protocol = DEFAULT_PROTOCOL;
118 default_port = DEFAULT_PORT;
123 while (*p && isspace(*p)) p++;
126 * Process command line options first. Yes, this can be
127 * done better, and it will be as soon as I have the
131 char *q = p + strcspn(p, " \t");
134 tolower(p[0]) == 's' &&
135 tolower(p[1]) == 's' &&
136 tolower(p[2]) == 'h') {
137 default_protocol = cfg.protocol = PROT_SSH;
138 default_port = cfg.port = 22;
139 } else if (q == p + 3 &&
140 tolower(p[0]) == 'l' &&
141 tolower(p[1]) == 'o' &&
142 tolower(p[2]) == 'g') {
143 logfile = "putty.log";
145 p = q + strspn(q, " \t");
149 * An initial @ means to activate a saved session.
153 if (!*cfg.host && !do_config()) {
157 } else if (*p == '&') {
159 * An initial & means we've been given a command line
160 * containing the hex value of a HANDLE for a file
161 * mapping object, which we must then extract as a
166 if (sscanf(p+1, "%p", &filemap) == 1 &&
167 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
168 0, 0, sizeof(Config))) != NULL) {
171 CloseHandle(filemap);
172 } else if (!do_config()) {
179 * If the hostname starts with "telnet:", set the
180 * protocol to Telnet and process the string as a
183 if (!strncmp(q, "telnet:", 7)) {
187 if (q[0] == '/' && q[1] == '/')
189 cfg.protocol = PROT_TELNET;
191 while (*p && *p != ':' && *p != '/') p++;
199 strncpy (cfg.host, q, sizeof(cfg.host)-1);
200 cfg.host[sizeof(cfg.host)-1] = '\0';
202 while (*p && !isspace(*p)) p++;
205 strncpy (cfg.host, q, sizeof(cfg.host)-1);
206 cfg.host[sizeof(cfg.host)-1] = '\0';
207 while (*p && isspace(*p)) p++;
222 * Select protocol. This is farmed out into a table in a
223 * separate file to enable an ssh-free variant.
228 for (i = 0; backends[i].backend != NULL; i++)
229 if (backends[i].protocol == cfg.protocol) {
230 back = backends[i].backend;
234 MessageBox(NULL, "Unsupported protocol number found",
235 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
241 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
245 wndclass.lpfnWndProc = WndProc;
246 wndclass.cbClsExtra = 0;
247 wndclass.cbWndExtra = 0;
248 wndclass.hInstance = inst;
249 wndclass.hIcon = LoadIcon (inst,
250 MAKEINTRESOURCE(IDI_MAINICON));
251 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
252 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
253 wndclass.lpszMenuName = NULL;
254 wndclass.lpszClassName = appname;
256 RegisterClass (&wndclass);
261 savelines = cfg.savelines;
267 * Guess some defaults for the window size. This all gets
268 * updated later, so we don't really care too much. However, we
269 * do want the font width/height guesses to correspond to a
270 * large font rather than a small one...
277 term_size (cfg.height, cfg.width, cfg.savelines);
278 guess_width = extra_width + font_width * cols;
279 guess_height = extra_height + font_height * rows;
282 HWND w = GetDesktopWindow();
283 GetWindowRect (w, &r);
284 if (guess_width > r.right - r.left)
285 guess_width = r.right - r.left;
286 if (guess_height > r.bottom - r.top)
287 guess_height = r.bottom - r.top;
291 int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
292 if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
293 if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
294 hwnd = CreateWindow (appname, appname,
296 CW_USEDEFAULT, CW_USEDEFAULT,
297 guess_width, guess_height,
298 NULL, NULL, inst, NULL);
302 * Initialise the fonts, simultaneously correcting the guesses
303 * for font_{width,height}.
305 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
310 * Correct the guesses for extra_{width,height}.
314 GetWindowRect (hwnd, &wr);
315 GetClientRect (hwnd, &cr);
316 extra_width = wr.right - wr.left - cr.right + cr.left;
317 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
321 * Resize the window, now we know what size we _really_ want it
324 guess_width = extra_width + font_width * cols;
325 guess_height = extra_height + font_height * rows;
326 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
327 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
328 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
331 * Initialise the scroll bar.
336 si.cbSize = sizeof(si);
337 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
342 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
346 * Start up the telnet connection.
353 error = back->init (hwnd, cfg.host, cfg.port, &realhost);
355 sprintf(msg, "Unable to open connection:\n%s", error);
356 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
359 window_name = icon_name = NULL;
360 sprintf(msg, "%s - PuTTY", realhost);
365 session_closed = FALSE;
368 * Set up the input and output buffers.
371 outbuf_reap = outbuf_head = 0;
374 * Prepare the mouse handler.
376 lastact = MA_NOTHING;
377 lastbtn = MB_NOTHING;
378 dbltime = GetDoubleClickTime();
381 * Set up the session-control options on the system menu.
384 HMENU m = GetSystemMenu (hwnd, FALSE);
388 AppendMenu (m, MF_SEPARATOR, 0, 0);
389 if (cfg.protocol == PROT_TELNET) {
391 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
392 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
393 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
394 AppendMenu (p, MF_SEPARATOR, 0, 0);
395 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
396 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
397 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
398 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
399 AppendMenu (p, MF_SEPARATOR, 0, 0);
400 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
401 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
402 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
403 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
404 AppendMenu (p, MF_SEPARATOR, 0, 0);
405 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
406 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
407 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
408 AppendMenu (m, MF_SEPARATOR, 0, 0);
410 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
411 AppendMenu (m, MF_SEPARATOR, 0, 0);
412 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
413 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
416 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
417 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
418 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
419 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
420 AppendMenu (m, MF_SEPARATOR, 0, 0);
421 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
422 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
423 AppendMenu (m, MF_SEPARATOR, 0, 0);
424 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
428 * Finally show the window!
430 ShowWindow (hwnd, show);
433 * Set the palette up.
439 has_focus = (GetForegroundWindow() == hwnd);
443 int timer_id = 0, long_timer = 0;
445 while (GetMessage (&msg, NULL, 0, 0) == 1) {
446 /* Sometimes DispatchMessage calls routines that use their own
447 * GetMessage loop, setup this timer so we get some control back.
449 * Also call term_update() from the timer so that if the host
450 * is sending data flat out we still do redraws.
452 if(timer_id && long_timer) {
453 KillTimer(hwnd, timer_id);
454 long_timer = timer_id = 0;
457 timer_id = SetTimer(hwnd, 1, 20, NULL);
458 DispatchMessage (&msg);
460 /* This is too fast, but I'll leave it for now 'cause it shows
461 * how often term_update is called (far too often at times!)
465 /* Send the paste buffer if there's anything to send */
468 /* If there's nothing new in the queue then we can do everything
469 * we've delayed, reading the socket, writing, and repainting
472 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
473 if (pending_netevent) {
474 enact_pending_netevent();
479 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
481 KillTimer(hwnd, timer_id);
488 timer_id = SetTimer(hwnd, 1, 2000, NULL);
489 else if (cfg.blinktext)
490 timer_id = SetTimer(hwnd, 1, 250, NULL);
492 timer_id = SetTimer(hwnd, 1, 500, NULL);
505 DeleteObject(fonts[i]);
512 if (cfg.protocol == PROT_SSH) {
523 * Actually do the job requested by a WM_NETEVENT
525 static void enact_pending_netevent(void) {
527 static int reentering = 0;
530 return; /* don't unpend the pending */
532 pending_netevent = FALSE;
535 i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
540 switch (WSABASEERR + (-i) % 10000) {
542 sprintf(buf, "Connection reset by peer");
545 sprintf(buf, "Unexpected network error %d", -i);
548 MessageBox(hwnd, buf, "PuTTY Fatal Error",
549 MB_ICONERROR | MB_OK);
552 if (cfg.close_on_exit)
555 session_closed = TRUE;
556 MessageBox(hwnd, "Connection closed by remote host",
557 "PuTTY", MB_OK | MB_ICONINFORMATION);
558 SetWindowText (hwnd, "PuTTY (inactive)");
564 * Copy the colour palette from the configuration data into defpal.
565 * This is non-trivial because the colour indices are different.
567 static void cfgtopalette(void) {
569 static const int ww[] = {
570 6, 7, 8, 9, 10, 11, 12, 13,
571 14, 15, 16, 17, 18, 19, 20, 21,
572 0, 1, 2, 3, 4, 4, 5, 5
575 for (i=0; i<24; i++) {
577 defpal[i].rgbtRed = cfg.colours[w][0];
578 defpal[i].rgbtGreen = cfg.colours[w][1];
579 defpal[i].rgbtBlue = cfg.colours[w][2];
584 * Set up the colour palette.
586 static void init_palette(void) {
588 HDC hdc = GetDC (hwnd);
590 if (cfg.try_palette &&
591 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
592 logpal = smalloc(sizeof(*logpal)
593 - sizeof(logpal->palPalEntry)
594 + NCOLOURS * sizeof(PALETTEENTRY));
595 logpal->palVersion = 0x300;
596 logpal->palNumEntries = NCOLOURS;
597 for (i = 0; i < NCOLOURS; i++) {
598 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
599 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
600 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
601 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
603 pal = CreatePalette (logpal);
605 SelectPalette (hdc, pal, FALSE);
606 RealizePalette (hdc);
607 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
611 ReleaseDC (hwnd, hdc);
614 for (i=0; i<NCOLOURS; i++)
615 colours[i] = PALETTERGB(defpal[i].rgbtRed,
619 for(i=0; i<NCOLOURS; i++)
620 colours[i] = RGB(defpal[i].rgbtRed,
626 * Initialise all the fonts we will need. There may be as many as
627 * eight or as few as one. We also:
629 * - check the font width and height, correcting our guesses if
632 * - verify that the bold font is the same width as the ordinary
633 * one, and engage shadow bolding if not.
635 * - verify that the underlined font is the same width as the
636 * ordinary one (manual underlining by means of line drawing can
637 * be done in a pinch).
639 static void init_fonts(int pick_width) {
644 int fw_dontcare, fw_bold;
653 if (cfg.fontisbold) {
654 fw_dontcare = FW_BOLD;
657 fw_dontcare = FW_DONTCARE;
663 font_height = cfg.fontheight;
664 font_width = pick_width;
667 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
668 c, OUT_DEFAULT_PRECIS, \
669 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
670 FIXED_PITCH | FF_DONTCARE, cfg.font)
672 if (cfg.vtmode != VT_OEMONLY) {
673 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
675 SelectObject (hdc, fonts[FONT_NORMAL]);
676 GetTextMetrics(hdc, &tm);
677 font_height = tm.tmHeight;
678 font_width = tm.tmAveCharWidth;
680 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
682 if (bold_mode == BOLD_FONT) {
683 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
684 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
687 if (cfg.vtmode == VT_OEMANSI) {
688 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
689 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
691 if (bold_mode == BOLD_FONT) {
692 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
693 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
699 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
701 SelectObject (hdc, fonts[FONT_OEM]);
702 GetTextMetrics(hdc, &tm);
703 font_height = tm.tmHeight;
704 font_width = tm.tmAveCharWidth;
706 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
708 if (bold_mode == BOLD_FONT) {
709 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
710 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
715 descent = tm.tmAscent + 1;
716 if (descent >= font_height)
717 descent = font_height - 1;
718 firstchar = tm.tmFirstChar;
720 for (i=0; i<8; i++) {
722 if (SelectObject (hdc, fonts[i]) &&
723 GetTextMetrics(hdc, &tm) )
724 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
730 ReleaseDC (hwnd, hdc);
732 /* ... This is wrong in OEM only mode */
733 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
734 (bold_mode == BOLD_FONT &&
735 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
737 DeleteObject (fonts[FONT_UNDERLINE]);
738 if (bold_mode == BOLD_FONT)
739 DeleteObject (fonts[FONT_BOLDUND]);
742 if (bold_mode == BOLD_FONT &&
743 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
744 bold_mode = BOLD_SHADOW;
745 DeleteObject (fonts[FONT_BOLD]);
746 if (und_mode == UND_FONT)
747 DeleteObject (fonts[FONT_BOLDUND]);
751 /* With the fascist font painting it doesn't matter if the linedraw font
752 * isn't exactly the right size anymore so we don't have to check this.
754 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
755 if( cfg.fontcharset == OEM_CHARSET )
757 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
758 "different sizes. Using OEM-only mode instead",
759 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
760 cfg.vtmode = VT_OEMONLY;
762 else if( firstchar < ' ' )
764 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
765 "different sizes. Using XTerm mode instead",
766 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
767 cfg.vtmode = VT_XWINDOWS;
771 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
772 "different sizes. Using ISO8859-1 mode instead",
773 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
774 cfg.vtmode = VT_POORMAN;
779 DeleteObject (fonts[i]);
785 void request_resize (int w, int h, int refont) {
788 /* If the window is maximized supress resizing attempts */
789 if(IsZoomed(hwnd)) return;
792 /* Don't do this in OEMANSI, you may get disable messages */
793 if (refont && w != cols && (cols==80 || cols==132)
794 && cfg.vtmode != VT_OEMANSI)
796 if (refont && w != cols && (cols==80 || cols==132))
799 /* If font width too big for screen should we shrink the font more ? */
801 font_width = ((font_width*cols+w/2)/w);
808 DeleteObject(fonts[i]);
810 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
812 init_fonts(font_width);
816 static int first_time = 1;
822 /* Get the size of the screen */
823 if (GetClientRect(GetDesktopWindow(),&ss))
824 /* first_time = 0 */;
825 else { first_time = 2; break; }
827 /* Make sure the values are sane */
828 width = (ss.right-ss.left-extra_width ) / font_width;
829 height = (ss.bottom-ss.top-extra_height ) / font_height;
831 if (w>width) w=width;
832 if (h>height) h=height;
838 width = extra_width + font_width * w;
839 height = extra_height + font_height * h;
841 SetWindowPos (hwnd, NULL, 0, 0, width, height,
842 SWP_NOACTIVATE | SWP_NOCOPYBITS |
843 SWP_NOMOVE | SWP_NOZORDER);
846 static void click (Mouse_Button b, int x, int y) {
847 int thistime = GetMessageTime();
849 if (lastbtn == b && thistime - lasttime < dbltime) {
850 lastact = (lastact == MA_CLICK ? MA_2CLK :
851 lastact == MA_2CLK ? MA_3CLK :
852 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
857 if (lastact != MA_NOTHING)
858 term_mouse (b, lastact, x, y);
862 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
863 WPARAM wParam, LPARAM lParam) {
865 static int ignore_size = FALSE;
866 static int ignore_clip = FALSE;
867 static int ignore_keymenu = TRUE;
868 static int just_reconfigged = FALSE;
872 if (pending_netevent)
873 enact_pending_netevent();
881 if (!cfg.warn_on_close || session_closed ||
882 MessageBox(hwnd, "Are you sure you want to close this session?",
883 "PuTTY Exit Confirmation",
884 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
891 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
894 return 0; /* don't put up system menu on Alt */
907 PROCESS_INFORMATION pi;
908 HANDLE filemap = NULL;
910 if (wParam == IDM_DUPSESS) {
912 * Allocate a file-mapping memory chunk for the
915 SECURITY_ATTRIBUTES sa;
918 sa.nLength = sizeof(sa);
919 sa.lpSecurityDescriptor = NULL;
920 sa.bInheritHandle = TRUE;
921 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
928 p = (Config *)MapViewOfFile(filemap,
930 0, 0, sizeof(Config));
932 *p = cfg; /* structure copy */
936 sprintf(c, "putty &%p", filemap);
938 } else if (wParam == IDM_SAVEDSESS) {
939 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
940 cl = malloc(16 + strlen(session)); /* 8, but play safe */
942 cl = NULL; /* not a very important failure mode */
944 sprintf(cl, "putty @%s", session);
950 GetModuleFileName (NULL, b, sizeof(b)-1);
952 si.lpReserved = NULL;
957 si.lpReserved2 = NULL;
958 CreateProcess (b, cl, NULL, NULL, TRUE,
959 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
962 CloseHandle(filemap);
968 if (!do_reconfig(hwnd))
970 just_reconfigged = TRUE;
975 DeleteObject(fonts[i]);
977 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
981 /* Telnet will change local echo -> remote if the remote asks */
982 if (cfg.protocol != PROT_TELNET)
983 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
991 /* Enable or disable the scroll bar, etc */
993 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
996 if (cfg.scrollbar) nflg |= WS_VSCROLL;
997 else nflg &= ~WS_VSCROLL;
999 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1001 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1007 SetWindowLong(hwnd, GWL_STYLE, nflg);
1008 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1009 SetWindowPos(hwnd, NULL, 0,0,0,0,
1010 SWP_NOACTIVATE|SWP_NOCOPYBITS|
1011 SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER|
1014 GetWindowRect (hwnd, &wr);
1015 GetClientRect (hwnd, &cr);
1016 extra_width = wr.right - wr.left - cr.right + cr.left;
1017 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1021 term_size(cfg.height, cfg.width, cfg.savelines);
1022 InvalidateRect(hwnd, NULL, TRUE);
1023 SetWindowPos (hwnd, NULL, 0, 0,
1024 extra_width + font_width * cfg.width,
1025 extra_height + font_height * cfg.height,
1026 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1027 SWP_NOMOVE | SWP_NOZORDER);
1028 if (IsIconic(hwnd)) {
1029 SetWindowText (hwnd,
1030 cfg.win_name_always ? window_name : icon_name);
1039 case IDM_TEL_AYT: back->special (TS_AYT); break;
1040 case IDM_TEL_BRK: back->special (TS_BRK); break;
1041 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1042 case IDM_TEL_EC: back->special (TS_EC); break;
1043 case IDM_TEL_EL: back->special (TS_EL); break;
1044 case IDM_TEL_GA: back->special (TS_GA); break;
1045 case IDM_TEL_NOP: back->special (TS_NOP); break;
1046 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1047 case IDM_TEL_AO: back->special (TS_AO); break;
1048 case IDM_TEL_IP: back->special (TS_IP); break;
1049 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1050 case IDM_TEL_EOR: back->special (TS_EOR); break;
1051 case IDM_TEL_EOF: back->special (TS_EOF); break;
1056 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1057 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1062 #define X_POS(l) ((int)(short)LOWORD(l))
1063 #define Y_POS(l) ((int)(short)HIWORD(l))
1065 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1066 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1068 case WM_LBUTTONDOWN:
1069 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1070 TO_CHR_Y(Y_POS(lParam)));
1074 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1075 TO_CHR_Y(Y_POS(lParam)));
1078 case WM_MBUTTONDOWN:
1080 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1081 TO_CHR_X(X_POS(lParam)),
1082 TO_CHR_Y(Y_POS(lParam)));
1085 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1086 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1087 TO_CHR_Y(Y_POS(lParam)));
1090 case WM_RBUTTONDOWN:
1092 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1093 TO_CHR_X(X_POS(lParam)),
1094 TO_CHR_Y(Y_POS(lParam)));
1097 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1098 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1099 TO_CHR_Y(Y_POS(lParam)));
1104 * Add the mouse position and message time to the random
1105 * number noise, if we're using ssh.
1107 if (cfg.protocol == PROT_SSH)
1108 noise_ultralight(lParam);
1110 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1112 if (wParam & MK_LBUTTON)
1114 else if (wParam & MK_MBUTTON)
1115 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1117 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1118 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1119 TO_CHR_Y(Y_POS(lParam)));
1122 case WM_IGNORE_CLIP:
1123 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1125 case WM_IGNORE_KEYMENU:
1126 ignore_keymenu = wParam; /* do or don't ignore SC_KEYMENU */
1128 case WM_DESTROYCLIPBOARD:
1131 ignore_clip = FALSE;
1136 hdc = BeginPaint (hwnd, &p);
1138 SelectPalette (hdc, pal, TRUE);
1139 RealizePalette (hdc);
1141 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1142 p.rcPaint.right, p.rcPaint.bottom);
1143 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1144 SelectObject (hdc, GetStockObject(WHITE_PEN));
1145 EndPaint (hwnd, &p);
1149 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1150 * but the only one that's likely to try to overload us is FD_READ.
1151 * This means buffering just one is fine.
1153 if (pending_netevent)
1154 enact_pending_netevent();
1156 pending_netevent = TRUE;
1157 pend_netevent_wParam=wParam;
1158 pend_netevent_lParam=lParam;
1170 case WM_IGNORE_SIZE:
1171 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1173 case WM_ENTERSIZEMOVE:
1176 case WM_EXITSIZEMOVE:
1181 int width, height, w, h, ew, eh;
1182 LPRECT r = (LPRECT)lParam;
1184 width = r->right - r->left - extra_width;
1185 height = r->bottom - r->top - extra_height;
1186 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1187 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1188 UpdateSizeTip(hwnd, w, h);
1189 ew = width - w * font_width;
1190 eh = height - h * font_height;
1192 if (wParam == WMSZ_LEFT ||
1193 wParam == WMSZ_BOTTOMLEFT ||
1194 wParam == WMSZ_TOPLEFT)
1200 if (wParam == WMSZ_TOP ||
1201 wParam == WMSZ_TOPRIGHT ||
1202 wParam == WMSZ_TOPLEFT)
1212 /* break; (never reached) */
1214 if (wParam == SIZE_MINIMIZED) {
1215 SetWindowText (hwnd,
1216 cfg.win_name_always ? window_name : icon_name);
1219 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1220 SetWindowText (hwnd, window_name);
1222 int width, height, w, h;
1223 #if 0 /* we have fixed this using WM_SIZING now */
1227 width = LOWORD(lParam);
1228 height = HIWORD(lParam);
1229 w = width / font_width; if (w < 1) w = 1;
1230 h = height / font_height; if (h < 1) h = 1;
1231 #if 0 /* we have fixed this using WM_SIZING now */
1232 ew = width - w * font_width;
1233 eh = height - h * font_height;
1234 if (ew != 0 || eh != 0) {
1236 GetWindowRect (hwnd, &r);
1237 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1238 SetWindowPos (hwnd, NULL, 0, 0,
1239 r.right - r.left - ew, r.bottom - r.top - eh,
1240 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1243 if (w != cols || h != rows || just_reconfigged) {
1245 term_size (h, w, cfg.savelines);
1247 just_reconfigged = FALSE;
1250 ignore_size = FALSE;
1253 switch (LOWORD(wParam)) {
1254 case SB_BOTTOM: term_scroll(-1, 0); break;
1255 case SB_TOP: term_scroll(+1, 0); break;
1256 case SB_LINEDOWN: term_scroll (0, +1); break;
1257 case SB_LINEUP: term_scroll (0, -1); break;
1258 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1259 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1260 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1261 term_scroll (1, HIWORD(wParam)); break;
1264 case WM_PALETTECHANGED:
1265 if ((HWND) wParam != hwnd && pal != NULL) {
1266 HDC hdc = get_ctx();
1268 if (RealizePalette (hdc) > 0)
1274 case WM_QUERYNEWPALETTE:
1276 HDC hdc = get_ctx();
1278 if (RealizePalette (hdc) > 0)
1290 * Add the scan code and keypress timing to the random
1291 * number noise, if we're using ssh.
1293 if (cfg.protocol == PROT_SSH)
1294 noise_ultralight(lParam);
1297 * We don't do TranslateMessage since it disassociates the
1298 * resulting CHAR message from the KEYDOWN that sparked it,
1299 * which we occasionally don't want. Instead, we process
1300 * KEYDOWN, and call the Win32 translator functions so that
1301 * we get the translations under _our_ control.
1304 unsigned char buf[20];
1307 len = TranslateKey (message, wParam, lParam, buf);
1309 return DefWindowProc (hwnd, message, wParam, lParam);
1310 ldisc->send (buf, len);
1316 * Nevertheless, we are prepared to deal with WM_CHAR
1317 * messages, should they crop up. So if someone wants to
1318 * post the things to us as part of a macro manoeuvre,
1319 * we're ready to cope.
1322 char c = xlat_kbd2tty((unsigned char)wParam);
1323 ldisc->send (&c, 1);
1328 return DefWindowProc (hwnd, message, wParam, lParam);
1332 * Draw a line of text in the window, at given character
1333 * coordinates, in given attributes.
1335 * We are allowed to fiddle with the contents of `text'.
1337 void do_text (Context ctx, int x, int y, char *text, int len,
1338 unsigned long attr, int lattr) {
1340 int nfg, nbg, nfont;
1343 int force_manual_underline = 0;
1344 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1345 static int *IpDx = 0, IpDxLEN = 0;;
1347 if (len>IpDxLEN || IpDx[0] != fnt_width) {
1351 IpDx = smalloc((len+16)*sizeof(int));
1354 for(i=0; i<IpDxLEN; i++)
1355 IpDx[i] = fnt_width;
1361 if (attr & ATTR_ACTCURS) {
1362 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1363 attr ^= ATTR_CUR_XOR;
1367 if (cfg.vtmode == VT_OEMONLY)
1371 * Map high-half characters in order to approximate ISO using
1372 * OEM character set. No characters are missing if the OEM codepage
1375 if (nfont & FONT_OEM) {
1377 for (i=0; i<len; i++)
1378 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1380 /* This is CP850 ... perfect translation */
1381 static const char oemhighhalf[] =
1382 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1383 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1384 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1385 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1386 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1387 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1388 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1389 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1390 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1391 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1392 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1393 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1396 /* This is CP437 ... junk translation */
1397 static const unsigned char oemhighhalf[] = {
1398 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1399 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1400 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1401 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1402 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1403 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1404 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1405 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1406 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1407 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1408 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1409 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1412 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1416 if (attr & ATTR_GBCHR) {
1419 * GB mapping: map # to pound, and everything else stays
1422 for (i=0; i<len; i++)
1424 text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
1425 } else if (attr & ATTR_LINEDRW) {
1428 static const char poorman[] =
1429 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1432 static const char oemmap_437[] =
1433 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1434 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1437 static const char oemmap_850[] =
1438 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1439 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1441 /* Poor windows font ... eg: windows courier */
1442 static const char oemmap[] =
1443 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1444 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1447 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1448 * VT100 line drawing chars; everything else stays normal.
1450 switch (cfg.vtmode) {
1452 for (i=0; i<len; i++)
1453 if (text[i] >= '\x60' && text[i] <= '\x7E')
1454 text[i] += '\x01' - '\x60';
1457 /* Make sure we actually have an OEM font */
1458 if (fonts[nfont|FONT_OEM]) {
1461 for (i=0; i<len; i++)
1462 if (text[i] >= '\x60' && text[i] <= '\x7E')
1463 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1467 for (i=0; i<len; i++)
1468 if (text[i] >= '\x60' && text[i] <= '\x7E')
1469 text[i] = poorman[(unsigned char)text[i] - 0x60];
1474 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1475 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1476 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1478 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1479 nfont |= FONT_UNDERLINE;
1482 if (nfont&FONT_UNDERLINE)
1483 force_manual_underline = 1;
1484 /* Don't do the same for manual bold, it could be bad news. */
1486 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1488 if (attr & ATTR_REVERSE) {
1489 t = nfg; nfg = nbg; nbg = t;
1491 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1493 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1497 SelectObject (hdc, fonts[nfont]);
1498 SetTextColor (hdc, fg);
1499 SetBkColor (hdc, bg);
1500 SetBkMode (hdc, OPAQUE);
1503 line_box.right = x+fnt_width*len;
1504 line_box.bottom = y+font_height;
1505 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1506 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1507 SetBkMode (hdc, TRANSPARENT);
1509 /* GRR: This draws the character outside it's box and can leave
1510 * 'droppings' even with the clip box! I suppose I could loop it
1511 * one character at a time ... yuk.
1513 * Or ... I could do a test print with "W", and use +1 or -1 for this
1514 * shift depending on if the leftmost column is blank...
1516 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1518 if (force_manual_underline ||
1519 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1521 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1522 MoveToEx (hdc, x, y+descent, NULL);
1523 LineTo (hdc, x+len*fnt_width, y+descent);
1524 oldpen = SelectObject (hdc, oldpen);
1525 DeleteObject (oldpen);
1527 if (attr & ATTR_PASCURS) {
1530 pts[0].x = pts[1].x = pts[4].x = x;
1531 pts[2].x = pts[3].x = x+fnt_width-1;
1532 pts[0].y = pts[3].y = pts[4].y = y;
1533 pts[1].y = pts[2].y = y+font_height-1;
1534 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1535 Polyline (hdc, pts, 5);
1536 oldpen = SelectObject (hdc, oldpen);
1537 DeleteObject (oldpen);
1541 static int check_compose(int first, int second) {
1543 static char * composetbl[] = {
1544 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1545 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1546 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1547 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1548 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1549 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1550 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1551 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1552 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1553 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1554 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1555 "\"uü", "'yý", "htþ", "\"yÿ",
1559 static int recurse = 0;
1566 sprintf(buf, "cc(%d,%d)", first, second);
1571 for(c=composetbl; *c; c++) {
1572 if( (*c)[0] == first && (*c)[1] == second)
1574 return (*c)[2] & 0xFF;
1581 nc = check_compose(second, first);
1583 nc = check_compose(toupper(first), toupper(second));
1585 nc = check_compose(toupper(second), toupper(first));
1593 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1594 * codes. Returns number of bytes used or zero to drop the message
1595 * or -1 to forward the message to windows.
1597 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output) {
1599 int scan, left_alt = 0, key_down, shift_state;
1601 unsigned char * p = output;
1603 static WORD keys[3];
1604 static int compose_state = 0;
1605 static int compose_char = 0;
1606 static WPARAM compose_key = 0;
1608 r = GetKeyboardState(keystate);
1609 if (!r) memset(keystate, 0, sizeof(keystate));
1612 /* Note if AltGr was pressed and if it was used as a compose key */
1613 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
1615 keystate[VK_RMENU] = keystate[VK_MENU];
1616 if (!compose_state) compose_key = wParam;
1618 if (wParam == VK_APPS && !compose_state)
1619 compose_key = wParam;
1621 if (wParam == compose_key)
1623 if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1625 else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
1630 else if (compose_state==1 && wParam != VK_CONTROL)
1633 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1634 if ( (cfg.funky_type == 0 || (cfg.funky_type == 1 && app_keypad_keys))
1635 && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
1637 wParam = VK_EXECUTE;
1639 /* UnToggle NUMLock */
1640 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1641 keystate[VK_NUMLOCK] ^= 1;
1644 /* And write back the 'adjusted' state */
1645 SetKeyboardState (keystate);
1648 /* Disable Auto repeat if required */
1649 if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
1652 if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
1655 key_down = ((HIWORD(lParam)&KF_UP)==0);
1657 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1658 if (left_alt && (keystate[VK_CONTROL]&0x80))
1659 keystate[VK_MENU] = 0;
1661 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
1662 shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
1663 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
1666 * Record that we pressed key so the scroll window can be reset, but
1667 * be careful to avoid Shift-UP/Down
1669 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
1673 /* Make sure we're not pasting */
1674 if (key_down) term_nopaste();
1676 if (compose_state>1 && left_alt) compose_state = 0;
1678 /* Sanitize the number pad if not using a PC NumPad */
1679 if( left_alt || (app_keypad_keys && cfg.funky_type != 2)
1680 || cfg.nethack_keypad || compose_state )
1682 if ((HIWORD(lParam)&KF_EXTENDED) == 0)
1687 case VK_INSERT: nParam = VK_NUMPAD0; break;
1688 case VK_END: nParam = VK_NUMPAD1; break;
1689 case VK_DOWN: nParam = VK_NUMPAD2; break;
1690 case VK_NEXT: nParam = VK_NUMPAD3; break;
1691 case VK_LEFT: nParam = VK_NUMPAD4; break;
1692 case VK_CLEAR: nParam = VK_NUMPAD5; break;
1693 case VK_RIGHT: nParam = VK_NUMPAD6; break;
1694 case VK_HOME: nParam = VK_NUMPAD7; break;
1695 case VK_UP: nParam = VK_NUMPAD8; break;
1696 case VK_PRIOR: nParam = VK_NUMPAD9; break;
1697 case VK_DELETE: nParam = VK_DECIMAL; break;
1701 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
1707 /* If a key is pressed and AltGr is not active */
1708 if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
1710 /* Okay, prepare for most alts then ...*/
1711 if (left_alt) *p++ = '\033';
1713 /* Lets see if it's a pattern we know all about ... */
1714 if (wParam == VK_PRIOR && shift_state == 1) {
1715 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
1718 if (wParam == VK_NEXT && shift_state == 1) {
1719 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
1722 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
1725 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
1727 SendMessage (hwnd, WM_IGNORE_KEYMENU, FALSE, 0);
1728 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
1729 SendMessage (hwnd, WM_IGNORE_KEYMENU, TRUE, 0);
1733 /* Nethack keypad */
1734 if (cfg.nethack_keypad && !left_alt) {
1736 case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
1737 case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
1738 case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
1739 case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
1740 case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
1741 case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
1742 case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
1743 case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
1744 case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
1748 /* Application Keypad */
1752 if ( cfg.funky_type == 0 ||
1753 ( cfg.funky_type == 1 && app_keypad_keys)) switch(wParam) {
1754 case VK_EXECUTE: xkey = 'P'; break;
1755 case VK_DIVIDE: xkey = 'Q'; break;
1756 case VK_MULTIPLY:xkey = 'R'; break;
1757 case VK_SUBTRACT:xkey = 'S'; break;
1759 if(app_keypad_keys) switch(wParam) {
1760 case VK_NUMPAD0: xkey = 'p'; break;
1761 case VK_NUMPAD1: xkey = 'q'; break;
1762 case VK_NUMPAD2: xkey = 'r'; break;
1763 case VK_NUMPAD3: xkey = 's'; break;
1764 case VK_NUMPAD4: xkey = 't'; break;
1765 case VK_NUMPAD5: xkey = 'u'; break;
1766 case VK_NUMPAD6: xkey = 'v'; break;
1767 case VK_NUMPAD7: xkey = 'w'; break;
1768 case VK_NUMPAD8: xkey = 'x'; break;
1769 case VK_NUMPAD9: xkey = 'y'; break;
1771 case VK_DECIMAL: xkey = 'n'; break;
1772 case VK_ADD: if(shift_state) xkey = 'm';
1776 if (HIWORD(lParam)&KF_EXTENDED)
1784 if (xkey>='P' && xkey<='S')
1785 p += sprintf((char *)p, "\x1B%c", xkey);
1787 p += sprintf((char *)p, "\x1B?%c", xkey);
1790 p += sprintf((char *)p, "\x1BO%c", xkey);
1795 if (wParam == VK_BACK && shift_state == 0 ) /* Backspace */
1797 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
1800 if (wParam == VK_TAB && shift_state == 1 ) /* Shift tab */
1802 *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
1804 if (wParam == VK_SPACE && shift_state == 2 ) /* Ctrl-Space */
1806 *p++ = 0; return p - output;
1808 if (wParam == VK_SPACE && shift_state == 3 ) /* Ctrl-Shift-Space */
1810 *p++ = 160; return p - output;
1812 if (wParam == VK_CANCEL && shift_state == 2 ) /* Ctrl-Break */
1814 *p++ = 3; return p - output;
1816 /* Control-2 to Control-8 are special */
1817 if (shift_state == 2 && wParam >= '2' && wParam <= '8')
1819 *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
1822 if (shift_state == 2 && wParam == 0xBD) {
1826 if (shift_state == 2 && wParam == 0xDF) {
1830 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
1831 *p++ = '\r'; *p++ = '\n';
1836 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1837 * for integer decimal nn.)
1839 * We also deal with the weird ones here. Linux VCs replace F1
1840 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1841 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1846 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
1847 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
1848 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
1849 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
1850 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
1851 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
1852 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
1853 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
1854 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
1855 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
1856 case VK_F11: code = 23; break;
1857 case VK_F12: code = 24; break;
1858 case VK_F13: code = 25; break;
1859 case VK_F14: code = 26; break;
1860 case VK_F15: code = 28; break;
1861 case VK_F16: code = 29; break;
1862 case VK_F17: code = 31; break;
1863 case VK_F18: code = 32; break;
1864 case VK_F19: code = 33; break;
1865 case VK_F20: code = 34; break;
1866 case VK_HOME: code = 1; break;
1867 case VK_INSERT: code = 2; break;
1868 case VK_DELETE: code = 3; break;
1869 case VK_END: code = 4; break;
1870 case VK_PRIOR: code = 5; break;
1871 case VK_NEXT: code = 6; break;
1873 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
1874 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
1877 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
1878 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
1881 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
1882 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
1886 p += sprintf((char *)p, "\x1B[%d~", code);
1891 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
1892 * some reason seems to send VK_CLEAR to Windows...).
1897 case VK_UP: xkey = 'A'; break;
1898 case VK_DOWN: xkey = 'B'; break;
1899 case VK_RIGHT: xkey = 'C'; break;
1900 case VK_LEFT: xkey = 'D'; break;
1901 case VK_CLEAR: xkey = 'G'; break;
1906 p += sprintf((char *)p, "\x1B%c", xkey);
1907 else if (app_cursor_keys)
1908 p += sprintf((char *)p, "\x1BO%c", xkey);
1910 p += sprintf((char *)p, "\x1B[%c", xkey);
1916 /* Okay we've done everything interesting; let windows deal with
1917 * the boring stuff */
1919 BOOL capsOn=keystate[VK_CAPITAL] !=0;
1921 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
1922 if(cfg.xlat_capslockcyr)
1923 keystate[VK_CAPITAL] = 0;
1925 r = ToAscii (wParam, scan, keystate, keys, 0);
1931 unsigned char ch = (unsigned char)keys[i];
1933 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
1938 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
1942 if ((nc=check_compose(compose_char,ch)) == -1)
1947 *p++ = xlat_kbd2tty((unsigned char)nc);
1953 if( left_alt && key_down ) *p++ = '\033';
1959 ch = xlat_latkbd2win(ch);
1960 *p++ = xlat_kbd2tty(ch);
1964 /* This is so the ALT-Numpad and dead keys work correctly. */
1971 /* This stops ALT press-release doing a 'COMMAND MENU' function */
1973 if (message == WM_SYSKEYUP && wParam == VK_MENU)
1975 keystate[VK_MENU] = 0;
1983 void set_title (char *title) {
1984 sfree (window_name);
1985 window_name = smalloc(1+strlen(title));
1986 strcpy (window_name, title);
1987 if (cfg.win_name_always || !IsIconic(hwnd))
1988 SetWindowText (hwnd, title);
1991 void set_icon (char *title) {
1993 icon_name = smalloc(1+strlen(title));
1994 strcpy (icon_name, title);
1995 if (!cfg.win_name_always && IsIconic(hwnd))
1996 SetWindowText (hwnd, title);
1999 void set_sbar (int total, int start, int page) {
2002 if (!cfg.scrollbar) return;
2004 si.cbSize = sizeof(si);
2005 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2007 si.nMax = total - 1;
2011 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2014 Context get_ctx(void) {
2019 SelectPalette (hdc, pal, FALSE);
2025 void free_ctx (Context ctx) {
2026 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2027 ReleaseDC (hwnd, ctx);
2030 static void real_palette_set (int n, int r, int g, int b) {
2032 logpal->palPalEntry[n].peRed = r;
2033 logpal->palPalEntry[n].peGreen = g;
2034 logpal->palPalEntry[n].peBlue = b;
2035 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2036 colours[n] = PALETTERGB(r, g, b);
2037 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2039 colours[n] = RGB(r, g, b);
2042 void palette_set (int n, int r, int g, int b) {
2043 static const int first[21] = {
2044 0, 2, 4, 6, 8, 10, 12, 14,
2045 1, 3, 5, 7, 9, 11, 13, 15,
2048 real_palette_set (first[n], r, g, b);
2050 real_palette_set (first[n]+1, r, g, b);
2052 HDC hdc = get_ctx();
2053 UnrealizeObject (pal);
2054 RealizePalette (hdc);
2059 void palette_reset (void) {
2062 for (i = 0; i < NCOLOURS; i++) {
2064 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2065 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2066 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2067 logpal->palPalEntry[i].peFlags = 0;
2068 colours[i] = PALETTERGB(defpal[i].rgbtRed,
2069 defpal[i].rgbtGreen,
2070 defpal[i].rgbtBlue);
2072 colours[i] = RGB(defpal[i].rgbtRed,
2073 defpal[i].rgbtGreen,
2074 defpal[i].rgbtBlue);
2079 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2081 RealizePalette (hdc);
2086 void write_clip (void *data, int len) {
2090 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2093 lock = GlobalLock (clipdata);
2096 memcpy (lock, data, len);
2097 ((unsigned char *) lock) [len] = 0;
2098 GlobalUnlock (clipdata);
2100 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2101 if (OpenClipboard (hwnd)) {
2103 SetClipboardData (CF_TEXT, clipdata);
2106 GlobalFree (clipdata);
2107 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2110 void get_clip (void **p, int *len) {
2111 static HGLOBAL clipdata = NULL;
2115 GlobalUnlock (clipdata);
2119 if (OpenClipboard (NULL)) {
2120 clipdata = GetClipboardData (CF_TEXT);
2123 *p = GlobalLock (clipdata);
2137 * Move `lines' lines from position `from' to position `to' in the
2140 void optimised_move (int to, int from, int lines) {
2144 min = (to < from ? to : from);
2145 max = to + from - min;
2147 r.left = 0; r.right = cols * font_width;
2148 r.top = min * font_height; r.bottom = (max+lines) * font_height;
2149 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2153 * Print a message box and perform a fatal exit.
2155 void fatalbox(char *fmt, ...) {
2160 vsprintf(stuff, fmt, ap);
2162 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2169 void beep(int errorbeep) {
2170 static long last_beep = 0;
2171 long now, beep_diff;
2173 now = GetTickCount();
2174 beep_diff = now-last_beep;
2176 /* Make sure we only respond to one beep per packet or so */
2177 if (beep_diff>=0 && beep_diff<50)
2181 MessageBeep(MB_ICONHAND);
2185 last_beep = GetTickCount();