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(WPARAM wParam, LPARAM lParam, unsigned char *output);
42 static void cfgtopalette(void);
43 static void init_palette(void);
44 static void init_fonts(int);
46 static int extra_width, extra_height;
48 static int pending_netevent = 0;
49 static WPARAM pend_netevent_wParam = 0;
50 static LPARAM pend_netevent_lParam = 0;
51 static void enact_pending_netevent(void);
55 #define FONT_UNDERLINE 2
56 #define FONT_BOLDUND 3
58 #define FONT_OEMBOLD 5
59 #define FONT_OEMBOLDUND 6
61 static HFONT fonts[8];
63 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
71 static COLORREF colours[NCOLOURS];
73 static LPLOGPALETTE logpal;
74 static RGBTRIPLE defpal[NCOLOURS];
78 static int dbltime, lasttime, lastact;
79 static Mouse_Button lastbtn;
81 static char *window_name, *icon_name;
83 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
84 static char appname[] = "PuTTY";
89 int guess_width, guess_height;
93 winsock_ver = MAKEWORD(1, 1);
94 if (WSAStartup(winsock_ver, &wsadata)) {
95 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
96 MB_OK | MB_ICONEXCLAMATION);
99 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
100 MessageBox(NULL, "WinSock version is incompatible with 1.1",
101 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
105 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
107 InitCommonControls();
110 * Process the command line.
115 default_protocol = DEFAULT_PROTOCOL;
116 default_port = DEFAULT_PORT;
121 while (*p && isspace(*p)) p++;
124 * Process command line options first. Yes, this can be
125 * done better, and it will be as soon as I have the
129 char *q = p + strcspn(p, " \t");
132 tolower(p[0]) == 's' &&
133 tolower(p[1]) == 's' &&
134 tolower(p[2]) == 'h') {
135 default_protocol = cfg.protocol = PROT_SSH;
136 default_port = cfg.port = 22;
137 } else if (q == p + 3 &&
138 tolower(p[0]) == 'l' &&
139 tolower(p[1]) == 'o' &&
140 tolower(p[2]) == 'g') {
141 logfile = "putty.log";
143 p = q + strspn(q, " \t");
147 * An initial @ means to activate a saved session.
151 if (!*cfg.host && !do_config()) {
155 } else if (*p == '&') {
157 * An initial & means we've been given a command line
158 * containing the hex value of a HANDLE for a file
159 * mapping object, which we must then extract as a
164 if (sscanf(p+1, "%p", &filemap) == 1 &&
165 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
166 0, 0, sizeof(Config))) != NULL) {
169 CloseHandle(filemap);
170 } else if (!do_config()) {
176 while (*p && !isspace(*p)) p++;
179 strncpy (cfg.host, q, sizeof(cfg.host)-1);
180 cfg.host[sizeof(cfg.host)-1] = '\0';
181 while (*p && isspace(*p)) p++;
195 * Select protocol. This is farmed out into a table in a
196 * separate file to enable an ssh-free variant.
201 for (i = 0; backends[i].backend != NULL; i++)
202 if (backends[i].protocol == cfg.protocol) {
203 back = backends[i].backend;
207 MessageBox(NULL, "Unsupported protocol number found",
208 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
214 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
218 wndclass.lpfnWndProc = WndProc;
219 wndclass.cbClsExtra = 0;
220 wndclass.cbWndExtra = 0;
221 wndclass.hInstance = inst;
222 wndclass.hIcon = LoadIcon (inst,
223 MAKEINTRESOURCE(IDI_MAINICON));
224 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
225 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
226 wndclass.lpszMenuName = NULL;
227 wndclass.lpszClassName = appname;
229 RegisterClass (&wndclass);
234 savelines = cfg.savelines;
240 * Guess some defaults for the window size. This all gets
241 * updated later, so we don't really care too much. However, we
242 * do want the font width/height guesses to correspond to a
243 * large font rather than a small one...
250 term_size (cfg.height, cfg.width, cfg.savelines);
251 guess_width = extra_width + font_width * cols;
252 guess_height = extra_height + font_height * rows;
255 HWND w = GetDesktopWindow();
256 GetWindowRect (w, &r);
257 if (guess_width > r.right - r.left)
258 guess_width = r.right - r.left;
259 if (guess_height > r.bottom - r.top)
260 guess_height = r.bottom - r.top;
263 hwnd = CreateWindow (appname, appname,
264 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
265 CW_USEDEFAULT, CW_USEDEFAULT,
266 guess_width, guess_height,
267 NULL, NULL, inst, NULL);
270 * Initialise the fonts, simultaneously correcting the guesses
271 * for font_{width,height}.
273 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
278 * Correct the guesses for extra_{width,height}.
282 GetWindowRect (hwnd, &wr);
283 GetClientRect (hwnd, &cr);
284 extra_width = wr.right - wr.left - cr.right + cr.left;
285 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
289 * Resize the window, now we know what size we _really_ want it
292 guess_width = extra_width + font_width * cols;
293 guess_height = extra_height + font_height * rows;
294 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
295 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
296 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
299 * Initialise the scroll bar.
304 si.cbSize = sizeof(si);
305 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL;
310 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
314 * Start up the telnet connection.
321 error = back->init (hwnd, cfg.host, cfg.port, &realhost);
323 sprintf(msg, "Unable to open connection:\n%s", error);
324 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
327 window_name = icon_name = NULL;
328 sprintf(msg, "%s - PuTTY", realhost);
333 session_closed = FALSE;
336 * Set up the input and output buffers.
338 inbuf_reap = inbuf_head = 0;
339 outbuf_reap = outbuf_head = 0;
342 * Choose unscroll method
344 unscroll_event = US_DISP;
347 * Prepare the mouse handler.
349 lastact = MA_NOTHING;
350 lastbtn = MB_NOTHING;
351 dbltime = GetDoubleClickTime();
354 * Set up the session-control options on the system menu.
357 HMENU m = GetSystemMenu (hwnd, FALSE);
361 AppendMenu (m, MF_SEPARATOR, 0, 0);
362 if (cfg.protocol == PROT_TELNET) {
364 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
365 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
366 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
367 AppendMenu (p, MF_SEPARATOR, 0, 0);
368 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
369 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
370 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
371 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
372 AppendMenu (p, MF_SEPARATOR, 0, 0);
373 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
374 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
375 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
376 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
377 AppendMenu (p, MF_SEPARATOR, 0, 0);
378 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
379 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
380 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
381 AppendMenu (m, MF_SEPARATOR, 0, 0);
383 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
384 AppendMenu (m, MF_SEPARATOR, 0, 0);
385 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
386 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
389 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
390 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
391 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
392 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
393 AppendMenu (m, MF_SEPARATOR, 0, 0);
394 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
395 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
396 AppendMenu (m, MF_SEPARATOR, 0, 0);
397 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
401 * Finally show the window!
403 ShowWindow (hwnd, show);
406 * Set the palette up.
412 has_focus = (GetForegroundWindow() == hwnd);
416 int timer_id = 0, long_timer = 0;
418 while (GetMessage (&msg, NULL, 0, 0) == 1) {
419 /* Sometimes DispatchMessage calls routines that use their own
420 * GetMessage loop, setup this timer so we get some control back.
422 * Also call term_update() from the timer so that if the host
423 * is sending data flat out we still do redraws.
425 if(timer_id && long_timer) {
426 KillTimer(hwnd, timer_id);
427 long_timer = timer_id = 0;
430 timer_id = SetTimer(hwnd, 1, 20, NULL);
431 DispatchMessage (&msg);
433 /* This is too fast, but I'll leave it for now 'cause it shows
434 * how often term_update is called (far too often at times!)
438 /* If there's nothing new in the queue then we can do everything
439 * we've delayed, reading the socket, writing, and repainting
442 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
443 if (pending_netevent) {
444 enact_pending_netevent();
449 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
451 KillTimer(hwnd, timer_id);
454 if (inbuf_reap != inbuf_head)
457 timer_id = SetTimer(hwnd, 1, 500, NULL);
470 DeleteObject(fonts[i]);
477 if (cfg.protocol == PROT_SSH)
484 * Actually do the job requested by a WM_NETEVENT
486 static void enact_pending_netevent(void) {
488 pending_netevent = FALSE;
489 i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
493 switch (WSABASEERR + (-i) % 10000) {
495 sprintf(buf, "Connection reset by peer");
498 sprintf(buf, "Unexpected network error %d", -i);
501 MessageBox(hwnd, buf, "PuTTY Fatal Error",
502 MB_ICONERROR | MB_OK);
505 if (cfg.close_on_exit)
508 session_closed = TRUE;
509 MessageBox(hwnd, "Connection closed by remote host",
510 "PuTTY", MB_OK | MB_ICONINFORMATION);
511 SetWindowText (hwnd, "PuTTY (inactive)");
517 * Copy the colour palette from the configuration data into defpal.
518 * This is non-trivial because the colour indices are different.
520 static void cfgtopalette(void) {
522 static const int ww[] = {
523 6, 7, 8, 9, 10, 11, 12, 13,
524 14, 15, 16, 17, 18, 19, 20, 21,
525 0, 1, 2, 3, 4, 4, 5, 5
528 for (i=0; i<24; i++) {
530 defpal[i].rgbtRed = cfg.colours[w][0];
531 defpal[i].rgbtGreen = cfg.colours[w][1];
532 defpal[i].rgbtBlue = cfg.colours[w][2];
537 * Set up the colour palette.
539 static void init_palette(void) {
541 HDC hdc = GetDC (hwnd);
543 if (cfg.try_palette &&
544 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
545 logpal = smalloc(sizeof(*logpal)
546 - sizeof(logpal->palPalEntry)
547 + NCOLOURS * sizeof(PALETTEENTRY));
548 logpal->palVersion = 0x300;
549 logpal->palNumEntries = NCOLOURS;
550 for (i = 0; i < NCOLOURS; i++) {
551 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
552 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
553 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
554 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
556 pal = CreatePalette (logpal);
558 SelectPalette (hdc, pal, FALSE);
559 RealizePalette (hdc);
560 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
564 ReleaseDC (hwnd, hdc);
567 for (i=0; i<NCOLOURS; i++)
568 colours[i] = PALETTERGB(defpal[i].rgbtRed,
572 for(i=0; i<NCOLOURS; i++)
573 colours[i] = RGB(defpal[i].rgbtRed,
579 * Initialise all the fonts we will need. There may be as many as
580 * eight or as few as one. We also:
582 * - check the font width and height, correcting our guesses if
585 * - verify that the bold font is the same width as the ordinary
586 * one, and engage shadow bolding if not.
588 * - verify that the underlined font is the same width as the
589 * ordinary one (manual underlining by means of line drawing can
590 * be done in a pinch).
592 static void init_fonts(int pick_width) {
597 int fw_dontcare, fw_bold;
606 if (cfg.fontisbold) {
607 fw_dontcare = FW_BOLD;
610 fw_dontcare = FW_DONTCARE;
616 font_height = cfg.fontheight;
617 font_width = pick_width;
620 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
621 c, OUT_DEFAULT_PRECIS, \
622 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
623 FIXED_PITCH | FF_DONTCARE, cfg.font)
625 if (cfg.vtmode != VT_OEMONLY) {
626 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
628 SelectObject (hdc, fonts[FONT_NORMAL]);
629 GetTextMetrics(hdc, &tm);
630 font_height = tm.tmHeight;
631 font_width = tm.tmAveCharWidth;
633 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
635 if (bold_mode == BOLD_FONT) {
636 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
637 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
640 if (cfg.vtmode == VT_OEMANSI) {
641 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
642 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
644 if (bold_mode == BOLD_FONT) {
645 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
646 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
652 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
654 SelectObject (hdc, fonts[FONT_OEM]);
655 GetTextMetrics(hdc, &tm);
656 font_height = tm.tmHeight;
657 font_width = tm.tmAveCharWidth;
659 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
661 if (bold_mode == BOLD_FONT) {
662 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
663 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
668 descent = tm.tmAscent + 1;
669 if (descent >= font_height)
670 descent = font_height - 1;
671 firstchar = tm.tmFirstChar;
673 for (i=0; i<8; i++) {
675 if (SelectObject (hdc, fonts[i]) &&
676 GetTextMetrics(hdc, &tm) )
677 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
683 ReleaseDC (hwnd, hdc);
685 /* ... This is wrong in OEM only mode */
686 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
687 (bold_mode == BOLD_FONT &&
688 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
690 DeleteObject (fonts[FONT_UNDERLINE]);
691 if (bold_mode == BOLD_FONT)
692 DeleteObject (fonts[FONT_BOLDUND]);
695 if (bold_mode == BOLD_FONT &&
696 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
697 bold_mode = BOLD_SHADOW;
698 DeleteObject (fonts[FONT_BOLD]);
699 if (und_mode == UND_FONT)
700 DeleteObject (fonts[FONT_BOLDUND]);
704 /* With the facist font painting it doesn't matter if the linedraw font
705 * isn't exactly the right size anymore so we don't have to check this.
707 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
708 if( cfg.fontcharset == OEM_CHARSET )
710 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
711 "different sizes. Using OEM-only mode instead",
712 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
713 cfg.vtmode = VT_OEMONLY;
715 else if( firstchar < ' ' )
717 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
718 "different sizes. Using XTerm mode instead",
719 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
720 cfg.vtmode = VT_XWINDOWS;
724 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
725 "different sizes. Using ISO8859-1 mode instead",
726 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
727 cfg.vtmode = VT_POORMAN;
732 DeleteObject (fonts[i]);
738 void request_resize (int w, int h, int refont) {
742 /* Don't do this in OEMANSI, you may get disable messages */
743 if (refont && w != cols && (cols==80 || cols==132)
744 && cfg.vtmode != VT_OEMANSI)
746 if (refont && w != cols && (cols==80 || cols==132))
749 /* If font width too big for screen should we shrink the font more ? */
751 font_width = ((font_width*cols+w/2)/w);
758 DeleteObject(fonts[i]);
760 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
762 init_fonts(font_width);
765 width = extra_width + font_width * w;
766 height = extra_height + font_height * h;
768 SetWindowPos (hwnd, NULL, 0, 0, width, height,
769 SWP_NOACTIVATE | SWP_NOCOPYBITS |
770 SWP_NOMOVE | SWP_NOZORDER);
773 static void click (Mouse_Button b, int x, int y) {
774 int thistime = GetMessageTime();
776 if (lastbtn == b && thistime - lasttime < dbltime) {
777 lastact = (lastact == MA_CLICK ? MA_2CLK :
778 lastact == MA_2CLK ? MA_3CLK :
779 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
784 if (lastact != MA_NOTHING)
785 term_mouse (b, lastact, x, y);
789 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
790 WPARAM wParam, LPARAM lParam) {
792 static int ignore_size = FALSE;
793 static int ignore_clip = FALSE;
794 static int just_reconfigged = FALSE;
798 if (pending_netevent)
799 enact_pending_netevent();
800 if (inbuf_reap != inbuf_head)
807 if (!cfg.warn_on_close || session_closed ||
808 MessageBox(hwnd, "Are you sure you want to close this session?",
809 "PuTTY Exit Confirmation",
810 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
817 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
829 PROCESS_INFORMATION pi;
830 HANDLE filemap = NULL;
832 if (wParam == IDM_DUPSESS) {
834 * Allocate a file-mapping memory chunk for the
837 SECURITY_ATTRIBUTES sa;
840 sa.nLength = sizeof(sa);
841 sa.lpSecurityDescriptor = NULL;
842 sa.bInheritHandle = TRUE;
843 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
850 p = (Config *)MapViewOfFile(filemap,
852 0, 0, sizeof(Config));
854 *p = cfg; /* structure copy */
858 sprintf(c, "putty &%p", filemap);
860 } else if (wParam == IDM_SAVEDSESS) {
861 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
862 cl = malloc(16 + strlen(session)); /* 8, but play safe */
864 cl = NULL; /* not a very important failure mode */
866 sprintf(cl, "putty @%s", session);
872 GetModuleFileName (NULL, b, sizeof(b)-1);
874 si.lpReserved = NULL;
879 si.lpReserved2 = NULL;
880 CreateProcess (b, cl, NULL, NULL, TRUE,
881 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
884 CloseHandle(filemap);
890 if (!do_reconfig(hwnd))
892 just_reconfigged = TRUE;
897 DeleteObject(fonts[i]);
899 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
903 /* Telnet will change local echo -> remote if the remote asks */
904 if (cfg.protocol != PROT_TELNET)
905 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
912 term_size(cfg.height, cfg.width, cfg.savelines);
913 InvalidateRect(hwnd, NULL, TRUE);
914 SetWindowPos (hwnd, NULL, 0, 0,
915 extra_width + font_width * cfg.width,
916 extra_height + font_height * cfg.height,
917 SWP_NOACTIVATE | SWP_NOCOPYBITS |
918 SWP_NOMOVE | SWP_NOZORDER);
919 if (IsIconic(hwnd)) {
921 cfg.win_name_always ? window_name : icon_name);
930 case IDM_TEL_AYT: back->special (TS_AYT); break;
931 case IDM_TEL_BRK: back->special (TS_BRK); break;
932 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
933 case IDM_TEL_EC: back->special (TS_EC); break;
934 case IDM_TEL_EL: back->special (TS_EL); break;
935 case IDM_TEL_GA: back->special (TS_GA); break;
936 case IDM_TEL_NOP: back->special (TS_NOP); break;
937 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
938 case IDM_TEL_AO: back->special (TS_AO); break;
939 case IDM_TEL_IP: back->special (TS_IP); break;
940 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
941 case IDM_TEL_EOR: back->special (TS_EOR); break;
942 case IDM_TEL_EOF: back->special (TS_EOF); break;
947 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
948 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
953 #define X_POS(l) ((int)(short)LOWORD(l))
954 #define Y_POS(l) ((int)(short)HIWORD(l))
956 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
957 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
960 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
961 TO_CHR_Y(Y_POS(lParam)));
965 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
966 TO_CHR_Y(Y_POS(lParam)));
971 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
972 TO_CHR_X(X_POS(lParam)),
973 TO_CHR_Y(Y_POS(lParam)));
976 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
977 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
978 TO_CHR_Y(Y_POS(lParam)));
983 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
984 TO_CHR_X(X_POS(lParam)),
985 TO_CHR_Y(Y_POS(lParam)));
988 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
989 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
990 TO_CHR_Y(Y_POS(lParam)));
995 * Add the mouse position and message time to the random
996 * number noise, if we're using ssh.
998 if (cfg.protocol == PROT_SSH)
999 noise_ultralight(lParam);
1001 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1003 if (wParam & MK_LBUTTON)
1005 else if (wParam & MK_MBUTTON)
1006 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1008 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1009 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1010 TO_CHR_Y(Y_POS(lParam)));
1013 case WM_IGNORE_CLIP:
1014 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1016 case WM_DESTROYCLIPBOARD:
1019 ignore_clip = FALSE;
1024 hdc = BeginPaint (hwnd, &p);
1026 SelectPalette (hdc, pal, TRUE);
1027 RealizePalette (hdc);
1029 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1030 p.rcPaint.right, p.rcPaint.bottom);
1031 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1032 SelectObject (hdc, GetStockObject(WHITE_PEN));
1033 EndPaint (hwnd, &p);
1037 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1038 * but the only one that's likely to try to overload us is FD_READ.
1039 * This means buffering just one is fine.
1041 if (pending_netevent)
1042 enact_pending_netevent();
1044 pending_netevent = TRUE;
1045 pend_netevent_wParam=wParam;
1046 pend_netevent_lParam=lParam;
1058 case WM_IGNORE_SIZE:
1059 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1061 case WM_ENTERSIZEMOVE:
1064 case WM_EXITSIZEMOVE:
1069 int width, height, w, h, ew, eh;
1070 LPRECT r = (LPRECT)lParam;
1072 width = r->right - r->left - extra_width;
1073 height = r->bottom - r->top - extra_height;
1074 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1075 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1076 UpdateSizeTip(hwnd, w, h);
1077 ew = width - w * font_width;
1078 eh = height - h * font_height;
1080 if (wParam == WMSZ_LEFT ||
1081 wParam == WMSZ_BOTTOMLEFT ||
1082 wParam == WMSZ_TOPLEFT)
1088 if (wParam == WMSZ_TOP ||
1089 wParam == WMSZ_TOPRIGHT ||
1090 wParam == WMSZ_TOPLEFT)
1100 /* break; (never reached) */
1102 if (wParam == SIZE_MINIMIZED) {
1103 SetWindowText (hwnd,
1104 cfg.win_name_always ? window_name : icon_name);
1107 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1108 SetWindowText (hwnd, window_name);
1110 int width, height, w, h;
1111 #if 0 /* we have fixed this using WM_SIZING now */
1115 width = LOWORD(lParam);
1116 height = HIWORD(lParam);
1117 w = width / font_width; if (w < 1) w = 1;
1118 h = height / font_height; if (h < 1) h = 1;
1119 #if 0 /* we have fixed this using WM_SIZING now */
1120 ew = width - w * font_width;
1121 eh = height - h * font_height;
1122 if (ew != 0 || eh != 0) {
1124 GetWindowRect (hwnd, &r);
1125 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1126 SetWindowPos (hwnd, NULL, 0, 0,
1127 r.right - r.left - ew, r.bottom - r.top - eh,
1128 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1131 if (w != cols || h != rows || just_reconfigged) {
1133 term_size (h, w, cfg.savelines);
1135 just_reconfigged = FALSE;
1138 ignore_size = FALSE;
1141 switch (LOWORD(wParam)) {
1142 case SB_BOTTOM: term_scroll(-1, 0); break;
1143 case SB_TOP: term_scroll(+1, 0); break;
1144 case SB_LINEDOWN: term_scroll (0, +1); break;
1145 case SB_LINEUP: term_scroll (0, -1); break;
1146 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1147 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1148 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1149 term_scroll (1, HIWORD(wParam)); break;
1152 case WM_PALETTECHANGED:
1153 if ((HWND) wParam != hwnd && pal != NULL) {
1154 HDC hdc = get_ctx();
1156 if (RealizePalette (hdc) > 0)
1162 case WM_QUERYNEWPALETTE:
1164 HDC hdc = get_ctx();
1166 if (RealizePalette (hdc) > 0)
1176 * Add the scan code and keypress timing to the random
1177 * number noise, if we're using ssh.
1179 if (cfg.protocol == PROT_SSH)
1180 noise_ultralight(lParam);
1183 * We don't do TranslateMessage since it disassociates the
1184 * resulting CHAR message from the KEYDOWN that sparked it,
1185 * which we occasionally don't want. Instead, we process
1186 * KEYDOWN, and call the Win32 translator functions so that
1187 * we get the translations under _our_ control.
1190 unsigned char buf[20];
1193 len = TranslateKey (wParam, lParam, buf);
1195 return DefWindowProc (hwnd, message, wParam, lParam);
1196 ldisc->send (buf, len);
1202 * We handle KEYUP ourselves in order to distinghish left
1203 * and right Alt or Control keys, which Windows won't do
1204 * right if left to itself. See also the special processing
1205 * at the top of TranslateKey.
1209 int ret = GetKeyboardState(keystate);
1210 if (ret && wParam == VK_MENU) {
1211 if (lParam & 0x1000000) keystate[VK_RMENU] = 0;
1212 else keystate[VK_LMENU] = 0;
1213 SetKeyboardState (keystate);
1215 if (ret && wParam == VK_CONTROL) {
1216 if (lParam & 0x1000000) keystate[VK_RCONTROL] = 0;
1217 else keystate[VK_LCONTROL] = 0;
1218 SetKeyboardState (keystate);
1222 * We don't return here, in order to allow Windows to do
1223 * its own KEYUP processing as well.
1229 * Nevertheless, we are prepared to deal with WM_CHAR
1230 * messages, should they crop up. So if someone wants to
1231 * post the things to us as part of a macro manoeuvre,
1232 * we're ready to cope.
1235 char c = xlat_kbd2tty((unsigned char)wParam);
1236 ldisc->send (&c, 1);
1241 return DefWindowProc (hwnd, message, wParam, lParam);
1245 * Draw a line of text in the window, at given character
1246 * coordinates, in given attributes.
1248 * We are allowed to fiddle with the contents of `text'.
1250 void do_text (Context ctx, int x, int y, char *text, int len,
1251 unsigned long attr) {
1252 int lattr = 0; /* Will be arg later for line attribute type */
1254 int nfg, nbg, nfont;
1257 int force_manual_underline = 0;
1258 static int *IpDx = 0, IpDxLEN = 0;;
1260 if (len>IpDxLEN || IpDx[0] != font_width*(1+!lattr)) {
1264 IpDx = smalloc((len+16)*sizeof(int));
1267 for(i=0; i<len; i++)
1268 IpDx[i] = font_width;
1276 if (attr & ATTR_ACTCURS) {
1277 attr &= (bold_mode == BOLD_COLOURS ? 0x200 : 0x300);
1278 attr ^= ATTR_CUR_XOR;
1282 if (cfg.vtmode == VT_OEMONLY)
1286 * Map high-half characters in order to approximate ISO using
1287 * OEM character set. No characters are missing if the OEM codepage
1290 if (nfont & FONT_OEM) {
1292 for (i=0; i<len; i++)
1293 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1295 /* This is CP850 ... perfect translation */
1296 static const char oemhighhalf[] =
1297 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1298 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1299 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1300 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1301 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1302 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1303 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1304 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1305 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1306 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1307 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1308 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1311 /* This is CP437 ... junk translation */
1312 static const unsigned char oemhighhalf[] = {
1313 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1314 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1315 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1316 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1317 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1318 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1319 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1320 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1321 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1322 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1323 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1324 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1327 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1331 if (attr & ATTR_GBCHR) {
1334 * GB mapping: map # to pound, and everything else stays
1337 for (i=0; i<len; i++)
1339 text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
1340 } else if (attr & ATTR_LINEDRW) {
1343 static const char poorman[] =
1344 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1347 static const char oemmap_437[] =
1348 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1349 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1352 static const char oemmap_850[] =
1353 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1354 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1356 /* Poor windows font ... eg: windows courier */
1357 static const char oemmap[] =
1358 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1359 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1362 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1363 * VT100 line drawing chars; everything else stays normal.
1365 switch (cfg.vtmode) {
1367 for (i=0; i<len; i++)
1368 if (text[i] >= '\x60' && text[i] <= '\x7E')
1369 text[i] += '\x01' - '\x60';
1372 /* Make sure we actually have an OEM font */
1373 if (fonts[nfont|FONT_OEM]) {
1376 for (i=0; i<len; i++)
1377 if (text[i] >= '\x60' && text[i] <= '\x7E')
1378 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1382 for (i=0; i<len; i++)
1383 if (text[i] >= '\x60' && text[i] <= '\x7E')
1384 text[i] = poorman[(unsigned char)text[i] - 0x60];
1389 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1390 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1391 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1393 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1394 nfont |= FONT_UNDERLINE;
1397 if (nfont&FONT_UNDERLINE)
1398 force_manual_underline = 1;
1399 /* Don't do the same for manual bold, it could be bad news. */
1401 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1403 if (attr & ATTR_REVERSE) {
1404 t = nfg; nfg = nbg; nbg = t;
1406 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1408 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1412 SelectObject (hdc, fonts[nfont]);
1413 SetTextColor (hdc, fg);
1414 SetBkColor (hdc, bg);
1415 SetBkMode (hdc, OPAQUE);
1418 line_box.right = x+font_width*len;
1419 line_box.bottom = y+font_height;
1420 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1421 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1422 SetBkMode (hdc, TRANSPARENT);
1424 /* GRR: This draws the character outside it's box and can leave
1425 * 'droppings' even with the clip box! I suppose I could loop it
1426 * one character at a time ... yuk. */
1427 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1429 if (force_manual_underline ||
1430 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1432 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1433 MoveToEx (hdc, x, y+descent, NULL);
1434 LineTo (hdc, x+len*font_width, y+descent);
1435 oldpen = SelectObject (hdc, oldpen);
1436 DeleteObject (oldpen);
1438 if (attr & ATTR_PASCURS) {
1441 pts[0].x = pts[1].x = pts[4].x = x;
1442 pts[2].x = pts[3].x = x+font_width-1;
1443 pts[0].y = pts[3].y = pts[4].y = y;
1444 pts[1].y = pts[2].y = y+font_height-1;
1445 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1446 Polyline (hdc, pts, 5);
1447 oldpen = SelectObject (hdc, oldpen);
1448 DeleteObject (oldpen);
1453 * Translate a WM_(SYS)?KEYDOWN message into a string of ASCII
1454 * codes. Returns number of bytes used.
1456 static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output) {
1457 unsigned char *p = output;
1460 int cancel_alt = FALSE;
1463 * Get hold of the keyboard state, because we'll need it a few
1466 ret = GetKeyboardState(keystate);
1469 * Record that we pressed key so the scroll window can be reset, but
1470 * be careful to avoid Shift-UP/Down
1472 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
1477 * Windows does not always want to distinguish left and right
1478 * Alt or Control keys. Thus we keep track of them ourselves.
1479 * See also the WM_KEYUP handler.
1481 if (wParam == VK_MENU) {
1482 if (lParam & 0x1000000) keystate[VK_RMENU] = 0x80;
1483 else keystate[VK_LMENU] = 0x80;
1484 SetKeyboardState (keystate);
1487 if (wParam == VK_CONTROL) {
1488 if (lParam & 0x1000000) keystate[VK_RCONTROL] = 0x80;
1489 else keystate[VK_LCONTROL] = 0x80;
1490 SetKeyboardState (keystate);
1495 * Prepend ESC, and cancel ALT, if ALT was pressed at the time
1496 * and it wasn't AltGr.
1498 if (lParam & 0x20000000 && (keystate[VK_LMENU] & 0x80)) {
1504 * NetHack keypad mode. This may conflict with Shift-PgUp/PgDn,
1505 * so we do it first.
1507 if (cfg.nethack_keypad) {
1508 int shift = keystate[VK_SHIFT] & 0x80;
1510 * NB the shifted versions only work with numlock off.
1512 switch ( (lParam >> 16) & 0x1FF ) {
1513 case 0x047: *p++ = shift ? 'Y' : 'y'; return p - output;
1514 case 0x048: *p++ = shift ? 'K' : 'k'; return p - output;
1515 case 0x049: *p++ = shift ? 'U' : 'u'; return p - output;
1516 case 0x04B: *p++ = shift ? 'H' : 'h'; return p - output;
1517 case 0x04C: *p++ = '.'; return p - output;
1518 case 0x04D: *p++ = shift ? 'L' : 'l'; return p - output;
1519 case 0x04F: *p++ = shift ? 'B' : 'b'; return p - output;
1520 case 0x050: *p++ = shift ? 'J' : 'j'; return p - output;
1521 case 0x051: *p++ = shift ? 'N' : 'n'; return p - output;
1522 case 0x053: *p++ = '.'; return p - output;
1527 * Shift-PgUp, Shift-PgDn, and Alt-F4 all produce window
1528 * events: we'll deal with those now.
1530 if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_PRIOR) {
1531 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
1534 if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_NEXT) {
1535 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
1538 if ((lParam & 0x20000000) && wParam == VK_F4 && cfg.alt_f4) {
1541 if ((lParam & 0x20000000) && wParam == VK_SPACE && cfg.alt_space) {
1542 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
1547 * In general, the strategy is to see what the Windows keymap
1548 * translation has to say for itself, and then process function
1549 * keys and suchlike ourselves if that fails. But first we must
1550 * deal with the small number of special cases which the
1551 * Windows keymap translator thinks it can do but gets wrong.
1553 * First special case: we might want the Backspace key to send
1556 if (wParam == VK_BACK) {
1557 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
1562 * Control-Space should send ^@ (0x00), not Space.
1564 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == VK_SPACE) {
1569 if (app_keypad_keys) {
1571 * If we're in applications keypad mode, we have to process it
1572 * before char-map translation, because it will pre-empt lots
1573 * of stuff, even if NumLock is off.
1577 * Hack to ensure NumLock doesn't interfere with
1578 * perception of Shift for Keypad Plus. I don't pretend
1579 * to understand this, but it seems to work as is.
1580 * Leave it alone, or die.
1582 keystate[VK_NUMLOCK] = 0;
1583 SetKeyboardState (keystate);
1584 GetKeyboardState (keystate);
1586 switch ( (lParam >> 16) & 0x1FF ) {
1587 case 0x145: p += sprintf((char *)p, "\x1BOP"); return p - output;
1588 case 0x135: p += sprintf((char *)p, "\x1BOQ"); return p - output;
1589 case 0x037: p += sprintf((char *)p, "\x1BOR"); return p - output;
1590 case 0x047: p += sprintf((char *)p, "\x1BOw"); return p - output;
1591 case 0x048: p += sprintf((char *)p, "\x1BOx"); return p - output;
1592 case 0x049: p += sprintf((char *)p, "\x1BOy"); return p - output;
1593 case 0x04A: p += sprintf((char *)p, "\x1BOS"); return p - output;
1594 case 0x04B: p += sprintf((char *)p, "\x1BOt"); return p - output;
1595 case 0x04C: p += sprintf((char *)p, "\x1BOu"); return p - output;
1596 case 0x04D: p += sprintf((char *)p, "\x1BOv"); return p - output;
1597 case 0x04E: /* keypad + is ^[Ol, but ^[Om with Shift */
1598 p += sprintf((char *)p,
1599 (ret && (keystate[VK_SHIFT] & 0x80)) ?
1600 "\x1BOm" : "\x1BOl");
1602 case 0x04F: p += sprintf((char *)p, "\x1BOq"); return p - output;
1603 case 0x050: p += sprintf((char *)p, "\x1BOr"); return p - output;
1604 case 0x051: p += sprintf((char *)p, "\x1BOs"); return p - output;
1605 case 0x052: p += sprintf((char *)p, "\x1BOp"); return p - output;
1606 case 0x053: p += sprintf((char *)p, "\x1BOn"); return p - output;
1607 case 0x11C: p += sprintf((char *)p, "\x1BOM"); return p - output;
1612 * Shift-Tab should send ESC [ Z.
1614 if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_TAB) {
1615 *p++ = 0x1B; /* ESC */
1622 * Before doing Windows charmap translation, remove LeftALT
1623 * from the keymap, since its sole effect should be to prepend
1624 * ESC, which we've already done. Note that removal of LeftALT
1625 * has to happen _after_ the above call to SetKeyboardState, or
1626 * dire things will befall.
1629 keystate[VK_MENU] = keystate[VK_RMENU];
1630 keystate[VK_LMENU] = 0;
1634 * Attempt the Windows char-map translation.
1639 BOOL capsOn=keystate[VK_CAPITAL] !=0;
1641 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
1642 if(cfg.xlat_capslockcyr)
1643 keystate[VK_CAPITAL] = 0;
1645 r = ToAscii (wParam, (lParam >> 16) & 0xFF,
1649 chr = xlat_latkbd2win((unsigned char)(chr & 0xFF));
1651 *p++ = xlat_kbd2tty((unsigned char)(chr & 0xFF));
1657 * OK, we haven't had a key code from the keymap translation.
1658 * We'll try our various special cases and function keys, and
1659 * then give up. (There's nothing wrong with giving up:
1660 * Scrollock, Pause/Break, and of course the various buckybit
1661 * keys all produce KEYDOWN events that we really _do_ want to
1666 * Control-2 should return ^@ (0x00), Control-6 should return
1667 * ^^ (0x1E), and Control-Minus should return ^_ (0x1F). Since
1668 * the DOS keyboard handling did it, and we have nothing better
1669 * to do with the key combo in question, we'll also map
1670 * Control-Backquote to ^\ (0x1C).
1672 * In addition a real VT100 maps Ctrl-3/4/5/7 and 8.
1674 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '2') {
1678 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '3') {
1682 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '4') {
1686 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '5') {
1690 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '6') {
1694 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '7') {
1698 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '8') {
1702 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xBD) {
1706 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xDF) {
1712 * First, all the keys that do tilde codes. (ESC '[' nn '~',
1713 * for integer decimal nn.)
1715 * We also deal with the weird ones here. Linux VCs replace F1
1716 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1717 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1722 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
1723 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
1724 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
1725 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
1726 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
1727 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
1728 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
1729 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
1730 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
1731 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
1732 case VK_F11: code = 23; break;
1733 case VK_F12: code = 24; break;
1734 case VK_HOME: code = 1; break;
1735 case VK_INSERT: code = 2; break;
1736 case VK_DELETE: code = 3; break;
1737 case VK_END: code = 4; break;
1738 case VK_PRIOR: code = 5; break;
1739 case VK_NEXT: code = 6; break;
1741 if (cfg.linux_funkeys && code >= 11 && code <= 15) {
1742 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
1745 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
1746 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
1750 p += sprintf((char *)p, "\x1B[%d~", code);
1755 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
1756 * some reason seems to send VK_CLEAR to Windows...).
1760 p += sprintf((char *)p, app_cursor_keys ? "\x1BOA" : "\x1B[A");
1763 p += sprintf((char *)p, app_cursor_keys ? "\x1BOB" : "\x1B[B");
1766 p += sprintf((char *)p, app_cursor_keys ? "\x1BOC" : "\x1B[C");
1769 p += sprintf((char *)p, app_cursor_keys ? "\x1BOD" : "\x1B[D");
1771 case VK_CLEAR: p += sprintf((char *)p, "\x1B[G"); return p - output;
1777 void set_title (char *title) {
1778 sfree (window_name);
1779 window_name = smalloc(1+strlen(title));
1780 strcpy (window_name, title);
1781 if (cfg.win_name_always || !IsIconic(hwnd))
1782 SetWindowText (hwnd, title);
1785 void set_icon (char *title) {
1787 icon_name = smalloc(1+strlen(title));
1788 strcpy (icon_name, title);
1789 if (!cfg.win_name_always && IsIconic(hwnd))
1790 SetWindowText (hwnd, title);
1793 void set_sbar (int total, int start, int page) {
1795 si.cbSize = sizeof(si);
1796 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL;
1798 si.nMax = total - 1;
1802 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
1805 Context get_ctx(void) {
1810 SelectPalette (hdc, pal, FALSE);
1816 void free_ctx (Context ctx) {
1817 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
1818 ReleaseDC (hwnd, ctx);
1821 static void real_palette_set (int n, int r, int g, int b) {
1823 logpal->palPalEntry[n].peRed = r;
1824 logpal->palPalEntry[n].peGreen = g;
1825 logpal->palPalEntry[n].peBlue = b;
1826 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
1827 colours[n] = PALETTERGB(r, g, b);
1828 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
1830 colours[n] = RGB(r, g, b);
1833 void palette_set (int n, int r, int g, int b) {
1834 static const int first[21] = {
1835 0, 2, 4, 6, 8, 10, 12, 14,
1836 1, 3, 5, 7, 9, 11, 13, 15,
1839 real_palette_set (first[n], r, g, b);
1841 real_palette_set (first[n]+1, r, g, b);
1843 HDC hdc = get_ctx();
1844 UnrealizeObject (pal);
1845 RealizePalette (hdc);
1850 void palette_reset (void) {
1853 for (i = 0; i < NCOLOURS; i++) {
1855 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
1856 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
1857 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
1858 logpal->palPalEntry[i].peFlags = 0;
1859 colours[i] = PALETTERGB(defpal[i].rgbtRed,
1860 defpal[i].rgbtGreen,
1861 defpal[i].rgbtBlue);
1863 colours[i] = RGB(defpal[i].rgbtRed,
1864 defpal[i].rgbtGreen,
1865 defpal[i].rgbtBlue);
1870 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
1872 RealizePalette (hdc);
1877 void write_clip (void *data, int len) {
1881 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
1884 lock = GlobalLock (clipdata);
1887 memcpy (lock, data, len);
1888 ((unsigned char *) lock) [len] = 0;
1889 GlobalUnlock (clipdata);
1891 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
1892 if (OpenClipboard (hwnd)) {
1894 SetClipboardData (CF_TEXT, clipdata);
1897 GlobalFree (clipdata);
1898 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
1901 void get_clip (void **p, int *len) {
1902 static HGLOBAL clipdata = NULL;
1906 GlobalUnlock (clipdata);
1910 if (OpenClipboard (NULL)) {
1911 clipdata = GetClipboardData (CF_TEXT);
1914 *p = GlobalLock (clipdata);
1928 * Move `lines' lines from position `from' to position `to' in the
1931 void optimised_move (int to, int from, int lines) {
1935 min = (to < from ? to : from);
1936 max = to + from - min;
1938 r.left = 0; r.right = cols * font_width;
1939 r.top = min * font_height; r.bottom = (max+lines) * font_height;
1940 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
1944 * Print a message box and perform a fatal exit.
1946 void fatalbox(char *fmt, ...) {
1951 vsprintf(stuff, fmt, ap);
1953 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);