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) {
488 * Actually do the job requested by a WM_NETEVENT
490 static void enact_pending_netevent(void) {
492 pending_netevent = FALSE;
493 i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
497 switch (WSABASEERR + (-i) % 10000) {
499 sprintf(buf, "Connection reset by peer");
502 sprintf(buf, "Unexpected network error %d", -i);
505 MessageBox(hwnd, buf, "PuTTY Fatal Error",
506 MB_ICONERROR | MB_OK);
509 if (cfg.close_on_exit)
512 session_closed = TRUE;
513 MessageBox(hwnd, "Connection closed by remote host",
514 "PuTTY", MB_OK | MB_ICONINFORMATION);
515 SetWindowText (hwnd, "PuTTY (inactive)");
521 * Copy the colour palette from the configuration data into defpal.
522 * This is non-trivial because the colour indices are different.
524 static void cfgtopalette(void) {
526 static const int ww[] = {
527 6, 7, 8, 9, 10, 11, 12, 13,
528 14, 15, 16, 17, 18, 19, 20, 21,
529 0, 1, 2, 3, 4, 4, 5, 5
532 for (i=0; i<24; i++) {
534 defpal[i].rgbtRed = cfg.colours[w][0];
535 defpal[i].rgbtGreen = cfg.colours[w][1];
536 defpal[i].rgbtBlue = cfg.colours[w][2];
541 * Set up the colour palette.
543 static void init_palette(void) {
545 HDC hdc = GetDC (hwnd);
547 if (cfg.try_palette &&
548 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
549 logpal = smalloc(sizeof(*logpal)
550 - sizeof(logpal->palPalEntry)
551 + NCOLOURS * sizeof(PALETTEENTRY));
552 logpal->palVersion = 0x300;
553 logpal->palNumEntries = NCOLOURS;
554 for (i = 0; i < NCOLOURS; i++) {
555 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
556 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
557 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
558 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
560 pal = CreatePalette (logpal);
562 SelectPalette (hdc, pal, FALSE);
563 RealizePalette (hdc);
564 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
568 ReleaseDC (hwnd, hdc);
571 for (i=0; i<NCOLOURS; i++)
572 colours[i] = PALETTERGB(defpal[i].rgbtRed,
576 for(i=0; i<NCOLOURS; i++)
577 colours[i] = RGB(defpal[i].rgbtRed,
583 * Initialise all the fonts we will need. There may be as many as
584 * eight or as few as one. We also:
586 * - check the font width and height, correcting our guesses if
589 * - verify that the bold font is the same width as the ordinary
590 * one, and engage shadow bolding if not.
592 * - verify that the underlined font is the same width as the
593 * ordinary one (manual underlining by means of line drawing can
594 * be done in a pinch).
596 static void init_fonts(int pick_width) {
601 int fw_dontcare, fw_bold;
610 if (cfg.fontisbold) {
611 fw_dontcare = FW_BOLD;
614 fw_dontcare = FW_DONTCARE;
620 font_height = cfg.fontheight;
621 font_width = pick_width;
624 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
625 c, OUT_DEFAULT_PRECIS, \
626 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
627 FIXED_PITCH | FF_DONTCARE, cfg.font)
629 if (cfg.vtmode != VT_OEMONLY) {
630 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
632 SelectObject (hdc, fonts[FONT_NORMAL]);
633 GetTextMetrics(hdc, &tm);
634 font_height = tm.tmHeight;
635 font_width = tm.tmAveCharWidth;
637 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
639 if (bold_mode == BOLD_FONT) {
640 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
641 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
644 if (cfg.vtmode == VT_OEMANSI) {
645 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
646 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
648 if (bold_mode == BOLD_FONT) {
649 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
650 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
656 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
658 SelectObject (hdc, fonts[FONT_OEM]);
659 GetTextMetrics(hdc, &tm);
660 font_height = tm.tmHeight;
661 font_width = tm.tmAveCharWidth;
663 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
665 if (bold_mode == BOLD_FONT) {
666 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
667 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
672 descent = tm.tmAscent + 1;
673 if (descent >= font_height)
674 descent = font_height - 1;
675 firstchar = tm.tmFirstChar;
677 for (i=0; i<8; i++) {
679 if (SelectObject (hdc, fonts[i]) &&
680 GetTextMetrics(hdc, &tm) )
681 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
687 ReleaseDC (hwnd, hdc);
689 /* ... This is wrong in OEM only mode */
690 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
691 (bold_mode == BOLD_FONT &&
692 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
694 DeleteObject (fonts[FONT_UNDERLINE]);
695 if (bold_mode == BOLD_FONT)
696 DeleteObject (fonts[FONT_BOLDUND]);
699 if (bold_mode == BOLD_FONT &&
700 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
701 bold_mode = BOLD_SHADOW;
702 DeleteObject (fonts[FONT_BOLD]);
703 if (und_mode == UND_FONT)
704 DeleteObject (fonts[FONT_BOLDUND]);
708 /* With the fascist font painting it doesn't matter if the linedraw font
709 * isn't exactly the right size anymore so we don't have to check this.
711 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
712 if( cfg.fontcharset == OEM_CHARSET )
714 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
715 "different sizes. Using OEM-only mode instead",
716 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
717 cfg.vtmode = VT_OEMONLY;
719 else if( firstchar < ' ' )
721 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
722 "different sizes. Using XTerm mode instead",
723 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
724 cfg.vtmode = VT_XWINDOWS;
728 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
729 "different sizes. Using ISO8859-1 mode instead",
730 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
731 cfg.vtmode = VT_POORMAN;
736 DeleteObject (fonts[i]);
742 void request_resize (int w, int h, int refont) {
746 /* Don't do this in OEMANSI, you may get disable messages */
747 if (refont && w != cols && (cols==80 || cols==132)
748 && cfg.vtmode != VT_OEMANSI)
750 if (refont && w != cols && (cols==80 || cols==132))
753 /* If font width too big for screen should we shrink the font more ? */
755 font_width = ((font_width*cols+w/2)/w);
762 DeleteObject(fonts[i]);
764 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
766 init_fonts(font_width);
769 width = extra_width + font_width * w;
770 height = extra_height + font_height * h;
772 SetWindowPos (hwnd, NULL, 0, 0, width, height,
773 SWP_NOACTIVATE | SWP_NOCOPYBITS |
774 SWP_NOMOVE | SWP_NOZORDER);
777 static void click (Mouse_Button b, int x, int y) {
778 int thistime = GetMessageTime();
780 if (lastbtn == b && thistime - lasttime < dbltime) {
781 lastact = (lastact == MA_CLICK ? MA_2CLK :
782 lastact == MA_2CLK ? MA_3CLK :
783 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
788 if (lastact != MA_NOTHING)
789 term_mouse (b, lastact, x, y);
793 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
794 WPARAM wParam, LPARAM lParam) {
796 static int ignore_size = FALSE;
797 static int ignore_clip = FALSE;
798 static int just_reconfigged = FALSE;
802 if (pending_netevent)
803 enact_pending_netevent();
804 if (inbuf_reap != inbuf_head)
811 if (!cfg.warn_on_close || session_closed ||
812 MessageBox(hwnd, "Are you sure you want to close this session?",
813 "PuTTY Exit Confirmation",
814 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
821 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
833 PROCESS_INFORMATION pi;
834 HANDLE filemap = NULL;
836 if (wParam == IDM_DUPSESS) {
838 * Allocate a file-mapping memory chunk for the
841 SECURITY_ATTRIBUTES sa;
844 sa.nLength = sizeof(sa);
845 sa.lpSecurityDescriptor = NULL;
846 sa.bInheritHandle = TRUE;
847 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
854 p = (Config *)MapViewOfFile(filemap,
856 0, 0, sizeof(Config));
858 *p = cfg; /* structure copy */
862 sprintf(c, "putty &%p", filemap);
864 } else if (wParam == IDM_SAVEDSESS) {
865 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
866 cl = malloc(16 + strlen(session)); /* 8, but play safe */
868 cl = NULL; /* not a very important failure mode */
870 sprintf(cl, "putty @%s", session);
876 GetModuleFileName (NULL, b, sizeof(b)-1);
878 si.lpReserved = NULL;
883 si.lpReserved2 = NULL;
884 CreateProcess (b, cl, NULL, NULL, TRUE,
885 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
888 CloseHandle(filemap);
894 if (!do_reconfig(hwnd))
896 just_reconfigged = TRUE;
901 DeleteObject(fonts[i]);
903 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
907 /* Telnet will change local echo -> remote if the remote asks */
908 if (cfg.protocol != PROT_TELNET)
909 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
916 term_size(cfg.height, cfg.width, cfg.savelines);
917 InvalidateRect(hwnd, NULL, TRUE);
918 SetWindowPos (hwnd, NULL, 0, 0,
919 extra_width + font_width * cfg.width,
920 extra_height + font_height * cfg.height,
921 SWP_NOACTIVATE | SWP_NOCOPYBITS |
922 SWP_NOMOVE | SWP_NOZORDER);
923 if (IsIconic(hwnd)) {
925 cfg.win_name_always ? window_name : icon_name);
934 case IDM_TEL_AYT: back->special (TS_AYT); break;
935 case IDM_TEL_BRK: back->special (TS_BRK); break;
936 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
937 case IDM_TEL_EC: back->special (TS_EC); break;
938 case IDM_TEL_EL: back->special (TS_EL); break;
939 case IDM_TEL_GA: back->special (TS_GA); break;
940 case IDM_TEL_NOP: back->special (TS_NOP); break;
941 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
942 case IDM_TEL_AO: back->special (TS_AO); break;
943 case IDM_TEL_IP: back->special (TS_IP); break;
944 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
945 case IDM_TEL_EOR: back->special (TS_EOR); break;
946 case IDM_TEL_EOF: back->special (TS_EOF); break;
951 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
952 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
957 #define X_POS(l) ((int)(short)LOWORD(l))
958 #define Y_POS(l) ((int)(short)HIWORD(l))
960 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
961 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
964 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
965 TO_CHR_Y(Y_POS(lParam)));
969 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
970 TO_CHR_Y(Y_POS(lParam)));
975 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
976 TO_CHR_X(X_POS(lParam)),
977 TO_CHR_Y(Y_POS(lParam)));
980 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
981 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
982 TO_CHR_Y(Y_POS(lParam)));
987 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
988 TO_CHR_X(X_POS(lParam)),
989 TO_CHR_Y(Y_POS(lParam)));
992 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
993 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
994 TO_CHR_Y(Y_POS(lParam)));
999 * Add the mouse position and message time to the random
1000 * number noise, if we're using ssh.
1002 if (cfg.protocol == PROT_SSH)
1003 noise_ultralight(lParam);
1005 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1007 if (wParam & MK_LBUTTON)
1009 else if (wParam & MK_MBUTTON)
1010 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1012 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1013 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1014 TO_CHR_Y(Y_POS(lParam)));
1017 case WM_IGNORE_CLIP:
1018 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1020 case WM_DESTROYCLIPBOARD:
1023 ignore_clip = FALSE;
1028 hdc = BeginPaint (hwnd, &p);
1030 SelectPalette (hdc, pal, TRUE);
1031 RealizePalette (hdc);
1033 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1034 p.rcPaint.right, p.rcPaint.bottom);
1035 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1036 SelectObject (hdc, GetStockObject(WHITE_PEN));
1037 EndPaint (hwnd, &p);
1041 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1042 * but the only one that's likely to try to overload us is FD_READ.
1043 * This means buffering just one is fine.
1045 if (pending_netevent)
1046 enact_pending_netevent();
1048 pending_netevent = TRUE;
1049 pend_netevent_wParam=wParam;
1050 pend_netevent_lParam=lParam;
1062 case WM_IGNORE_SIZE:
1063 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1065 case WM_ENTERSIZEMOVE:
1068 case WM_EXITSIZEMOVE:
1073 int width, height, w, h, ew, eh;
1074 LPRECT r = (LPRECT)lParam;
1076 width = r->right - r->left - extra_width;
1077 height = r->bottom - r->top - extra_height;
1078 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1079 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1080 UpdateSizeTip(hwnd, w, h);
1081 ew = width - w * font_width;
1082 eh = height - h * font_height;
1084 if (wParam == WMSZ_LEFT ||
1085 wParam == WMSZ_BOTTOMLEFT ||
1086 wParam == WMSZ_TOPLEFT)
1092 if (wParam == WMSZ_TOP ||
1093 wParam == WMSZ_TOPRIGHT ||
1094 wParam == WMSZ_TOPLEFT)
1104 /* break; (never reached) */
1106 if (wParam == SIZE_MINIMIZED) {
1107 SetWindowText (hwnd,
1108 cfg.win_name_always ? window_name : icon_name);
1111 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1112 SetWindowText (hwnd, window_name);
1114 int width, height, w, h;
1115 #if 0 /* we have fixed this using WM_SIZING now */
1119 width = LOWORD(lParam);
1120 height = HIWORD(lParam);
1121 w = width / font_width; if (w < 1) w = 1;
1122 h = height / font_height; if (h < 1) h = 1;
1123 #if 0 /* we have fixed this using WM_SIZING now */
1124 ew = width - w * font_width;
1125 eh = height - h * font_height;
1126 if (ew != 0 || eh != 0) {
1128 GetWindowRect (hwnd, &r);
1129 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1130 SetWindowPos (hwnd, NULL, 0, 0,
1131 r.right - r.left - ew, r.bottom - r.top - eh,
1132 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1135 if (w != cols || h != rows || just_reconfigged) {
1137 term_size (h, w, cfg.savelines);
1139 just_reconfigged = FALSE;
1142 ignore_size = FALSE;
1145 switch (LOWORD(wParam)) {
1146 case SB_BOTTOM: term_scroll(-1, 0); break;
1147 case SB_TOP: term_scroll(+1, 0); break;
1148 case SB_LINEDOWN: term_scroll (0, +1); break;
1149 case SB_LINEUP: term_scroll (0, -1); break;
1150 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1151 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1152 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1153 term_scroll (1, HIWORD(wParam)); break;
1156 case WM_PALETTECHANGED:
1157 if ((HWND) wParam != hwnd && pal != NULL) {
1158 HDC hdc = get_ctx();
1160 if (RealizePalette (hdc) > 0)
1166 case WM_QUERYNEWPALETTE:
1168 HDC hdc = get_ctx();
1170 if (RealizePalette (hdc) > 0)
1180 * Add the scan code and keypress timing to the random
1181 * number noise, if we're using ssh.
1183 if (cfg.protocol == PROT_SSH)
1184 noise_ultralight(lParam);
1187 * We don't do TranslateMessage since it disassociates the
1188 * resulting CHAR message from the KEYDOWN that sparked it,
1189 * which we occasionally don't want. Instead, we process
1190 * KEYDOWN, and call the Win32 translator functions so that
1191 * we get the translations under _our_ control.
1194 unsigned char buf[20];
1197 len = TranslateKey (wParam, lParam, buf);
1199 return DefWindowProc (hwnd, message, wParam, lParam);
1200 ldisc->send (buf, len);
1206 * We handle KEYUP ourselves in order to distinghish left
1207 * and right Alt or Control keys, which Windows won't do
1208 * right if left to itself. See also the special processing
1209 * at the top of TranslateKey.
1213 int ret = GetKeyboardState(keystate);
1214 if (ret && wParam == VK_MENU) {
1215 if (lParam & 0x1000000) keystate[VK_RMENU] = 0;
1216 else keystate[VK_LMENU] = 0;
1217 SetKeyboardState (keystate);
1219 if (ret && wParam == VK_CONTROL) {
1220 if (lParam & 0x1000000) keystate[VK_RCONTROL] = 0;
1221 else keystate[VK_LCONTROL] = 0;
1222 SetKeyboardState (keystate);
1226 * We don't return here, in order to allow Windows to do
1227 * its own KEYUP processing as well.
1233 * Nevertheless, we are prepared to deal with WM_CHAR
1234 * messages, should they crop up. So if someone wants to
1235 * post the things to us as part of a macro manoeuvre,
1236 * we're ready to cope.
1239 char c = xlat_kbd2tty((unsigned char)wParam);
1240 ldisc->send (&c, 1);
1245 return DefWindowProc (hwnd, message, wParam, lParam);
1249 * Draw a line of text in the window, at given character
1250 * coordinates, in given attributes.
1252 * We are allowed to fiddle with the contents of `text'.
1254 void do_text (Context ctx, int x, int y, char *text, int len,
1255 unsigned long attr) {
1256 int lattr = 0; /* Will be arg later for line attribute type */
1258 int nfg, nbg, nfont;
1261 int force_manual_underline = 0;
1262 static int *IpDx = 0, IpDxLEN = 0;;
1264 if (len>IpDxLEN || IpDx[0] != font_width*(1+!lattr)) {
1268 IpDx = smalloc((len+16)*sizeof(int));
1271 for(i=0; i<len; i++)
1272 IpDx[i] = font_width;
1280 if (attr & ATTR_ACTCURS) {
1281 attr &= (bold_mode == BOLD_COLOURS ? 0x200 : 0x300);
1282 attr ^= ATTR_CUR_XOR;
1286 if (cfg.vtmode == VT_OEMONLY)
1290 * Map high-half characters in order to approximate ISO using
1291 * OEM character set. No characters are missing if the OEM codepage
1294 if (nfont & FONT_OEM) {
1296 for (i=0; i<len; i++)
1297 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1299 /* This is CP850 ... perfect translation */
1300 static const char oemhighhalf[] =
1301 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1302 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1303 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1304 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1305 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1306 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1307 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1308 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1309 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1310 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1311 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1312 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1315 /* This is CP437 ... junk translation */
1316 static const unsigned char oemhighhalf[] = {
1317 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1318 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1319 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1320 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1321 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1322 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1323 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1324 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1325 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1326 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1327 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1328 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1331 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1335 if (attr & ATTR_GBCHR) {
1338 * GB mapping: map # to pound, and everything else stays
1341 for (i=0; i<len; i++)
1343 text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
1344 } else if (attr & ATTR_LINEDRW) {
1347 static const char poorman[] =
1348 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1351 static const char oemmap_437[] =
1352 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1353 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1356 static const char oemmap_850[] =
1357 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1358 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1360 /* Poor windows font ... eg: windows courier */
1361 static const char oemmap[] =
1362 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1363 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1366 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1367 * VT100 line drawing chars; everything else stays normal.
1369 switch (cfg.vtmode) {
1371 for (i=0; i<len; i++)
1372 if (text[i] >= '\x60' && text[i] <= '\x7E')
1373 text[i] += '\x01' - '\x60';
1376 /* Make sure we actually have an OEM font */
1377 if (fonts[nfont|FONT_OEM]) {
1380 for (i=0; i<len; i++)
1381 if (text[i] >= '\x60' && text[i] <= '\x7E')
1382 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1386 for (i=0; i<len; i++)
1387 if (text[i] >= '\x60' && text[i] <= '\x7E')
1388 text[i] = poorman[(unsigned char)text[i] - 0x60];
1393 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1394 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1395 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1397 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1398 nfont |= FONT_UNDERLINE;
1401 if (nfont&FONT_UNDERLINE)
1402 force_manual_underline = 1;
1403 /* Don't do the same for manual bold, it could be bad news. */
1405 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1407 if (attr & ATTR_REVERSE) {
1408 t = nfg; nfg = nbg; nbg = t;
1410 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1412 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1416 SelectObject (hdc, fonts[nfont]);
1417 SetTextColor (hdc, fg);
1418 SetBkColor (hdc, bg);
1419 SetBkMode (hdc, OPAQUE);
1422 line_box.right = x+font_width*len;
1423 line_box.bottom = y+font_height;
1424 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1425 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1426 SetBkMode (hdc, TRANSPARENT);
1428 /* GRR: This draws the character outside it's box and can leave
1429 * 'droppings' even with the clip box! I suppose I could loop it
1430 * one character at a time ... yuk. */
1431 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1433 if (force_manual_underline ||
1434 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1436 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1437 MoveToEx (hdc, x, y+descent, NULL);
1438 LineTo (hdc, x+len*font_width, y+descent);
1439 oldpen = SelectObject (hdc, oldpen);
1440 DeleteObject (oldpen);
1442 if (attr & ATTR_PASCURS) {
1445 pts[0].x = pts[1].x = pts[4].x = x;
1446 pts[2].x = pts[3].x = x+font_width-1;
1447 pts[0].y = pts[3].y = pts[4].y = y;
1448 pts[1].y = pts[2].y = y+font_height-1;
1449 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1450 Polyline (hdc, pts, 5);
1451 oldpen = SelectObject (hdc, oldpen);
1452 DeleteObject (oldpen);
1457 * Translate a WM_(SYS)?KEYDOWN message into a string of ASCII
1458 * codes. Returns number of bytes used.
1460 static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output) {
1461 unsigned char *p = output;
1464 int cancel_alt = FALSE;
1467 * Get hold of the keyboard state, because we'll need it a few
1470 ret = GetKeyboardState(keystate);
1473 * Record that we pressed key so the scroll window can be reset, but
1474 * be careful to avoid Shift-UP/Down
1476 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
1481 * Windows does not always want to distinguish left and right
1482 * Alt or Control keys. Thus we keep track of them ourselves.
1483 * See also the WM_KEYUP handler.
1485 if (wParam == VK_MENU) {
1486 if (lParam & 0x1000000) keystate[VK_RMENU] = 0x80;
1487 else keystate[VK_LMENU] = 0x80;
1488 SetKeyboardState (keystate);
1491 if (wParam == VK_CONTROL) {
1492 if (lParam & 0x1000000) keystate[VK_RCONTROL] = 0x80;
1493 else keystate[VK_LCONTROL] = 0x80;
1494 SetKeyboardState (keystate);
1499 * Prepend ESC, and cancel ALT, if ALT was pressed at the time
1500 * and it wasn't AltGr.
1502 if (lParam & 0x20000000 && (keystate[VK_LMENU] & 0x80)) {
1508 * NetHack keypad mode. This may conflict with Shift-PgUp/PgDn,
1509 * so we do it first.
1511 if (cfg.nethack_keypad) {
1512 int shift = keystate[VK_SHIFT] & 0x80;
1514 * NB the shifted versions only work with numlock off.
1516 switch ( (lParam >> 16) & 0x1FF ) {
1517 case 0x047: *p++ = shift ? 'Y' : 'y'; return p - output;
1518 case 0x048: *p++ = shift ? 'K' : 'k'; return p - output;
1519 case 0x049: *p++ = shift ? 'U' : 'u'; return p - output;
1520 case 0x04B: *p++ = shift ? 'H' : 'h'; return p - output;
1521 case 0x04C: *p++ = '.'; return p - output;
1522 case 0x04D: *p++ = shift ? 'L' : 'l'; return p - output;
1523 case 0x04F: *p++ = shift ? 'B' : 'b'; return p - output;
1524 case 0x050: *p++ = shift ? 'J' : 'j'; return p - output;
1525 case 0x051: *p++ = shift ? 'N' : 'n'; return p - output;
1526 case 0x053: *p++ = '.'; return p - output;
1531 * Shift-PgUp, Shift-PgDn, and Alt-F4 all produce window
1532 * events: we'll deal with those now.
1534 if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_PRIOR) {
1535 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
1538 if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_NEXT) {
1539 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
1542 if ((lParam & 0x20000000) && wParam == VK_F4 && cfg.alt_f4) {
1545 if ((lParam & 0x20000000) && wParam == VK_SPACE && cfg.alt_space) {
1546 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
1551 * In general, the strategy is to see what the Windows keymap
1552 * translation has to say for itself, and then process function
1553 * keys and suchlike ourselves if that fails. But first we must
1554 * deal with the small number of special cases which the
1555 * Windows keymap translator thinks it can do but gets wrong.
1557 * First special case: we might want the Backspace key to send
1560 if (wParam == VK_BACK) {
1561 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
1566 * Control-Space should send ^@ (0x00), not Space.
1568 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == VK_SPACE) {
1573 if (app_keypad_keys) {
1575 * If we're in applications keypad mode, we have to process it
1576 * before char-map translation, because it will pre-empt lots
1577 * of stuff, even if NumLock is off.
1581 * Hack to ensure NumLock doesn't interfere with
1582 * perception of Shift for Keypad Plus. I don't pretend
1583 * to understand this, but it seems to work as is.
1584 * Leave it alone, or die.
1586 keystate[VK_NUMLOCK] = 0;
1587 SetKeyboardState (keystate);
1588 GetKeyboardState (keystate);
1590 switch ( (lParam >> 16) & 0x1FF ) {
1591 case 0x145: p += sprintf((char *)p, "\x1BOP"); return p - output;
1592 case 0x135: p += sprintf((char *)p, "\x1BOQ"); return p - output;
1593 case 0x037: p += sprintf((char *)p, "\x1BOR"); return p - output;
1594 case 0x047: p += sprintf((char *)p, "\x1BOw"); return p - output;
1595 case 0x048: p += sprintf((char *)p, "\x1BOx"); return p - output;
1596 case 0x049: p += sprintf((char *)p, "\x1BOy"); return p - output;
1597 case 0x04A: p += sprintf((char *)p, "\x1BOS"); return p - output;
1598 case 0x04B: p += sprintf((char *)p, "\x1BOt"); return p - output;
1599 case 0x04C: p += sprintf((char *)p, "\x1BOu"); return p - output;
1600 case 0x04D: p += sprintf((char *)p, "\x1BOv"); return p - output;
1601 case 0x04E: /* keypad + is ^[Ol, but ^[Om with Shift */
1602 p += sprintf((char *)p,
1603 (ret && (keystate[VK_SHIFT] & 0x80)) ?
1604 "\x1BOm" : "\x1BOl");
1606 case 0x04F: p += sprintf((char *)p, "\x1BOq"); return p - output;
1607 case 0x050: p += sprintf((char *)p, "\x1BOr"); return p - output;
1608 case 0x051: p += sprintf((char *)p, "\x1BOs"); return p - output;
1609 case 0x052: p += sprintf((char *)p, "\x1BOp"); return p - output;
1610 case 0x053: p += sprintf((char *)p, "\x1BOn"); return p - output;
1611 case 0x11C: p += sprintf((char *)p, "\x1BOM"); return p - output;
1616 * Shift-Tab should send ESC [ Z.
1618 if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_TAB) {
1619 *p++ = 0x1B; /* ESC */
1626 * Before doing Windows charmap translation, remove LeftALT
1627 * from the keymap, since its sole effect should be to prepend
1628 * ESC, which we've already done. Note that removal of LeftALT
1629 * has to happen _after_ the above call to SetKeyboardState, or
1630 * dire things will befall.
1633 keystate[VK_MENU] = keystate[VK_RMENU];
1634 keystate[VK_LMENU] = 0;
1638 * Attempt the Windows char-map translation.
1643 BOOL capsOn=keystate[VK_CAPITAL] !=0;
1645 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
1646 if(cfg.xlat_capslockcyr)
1647 keystate[VK_CAPITAL] = 0;
1649 r = ToAscii (wParam, (lParam >> 16) & 0xFF,
1653 chr = xlat_latkbd2win((unsigned char)(chr & 0xFF));
1655 *p++ = xlat_kbd2tty((unsigned char)(chr & 0xFF));
1661 * OK, we haven't had a key code from the keymap translation.
1662 * We'll try our various special cases and function keys, and
1663 * then give up. (There's nothing wrong with giving up:
1664 * Scrollock, Pause/Break, and of course the various buckybit
1665 * keys all produce KEYDOWN events that we really _do_ want to
1670 * Control-2 should return ^@ (0x00), Control-6 should return
1671 * ^^ (0x1E), and Control-Minus should return ^_ (0x1F). Since
1672 * the DOS keyboard handling did it, and we have nothing better
1673 * to do with the key combo in question, we'll also map
1674 * Control-Backquote to ^\ (0x1C).
1676 * In addition a real VT100 maps Ctrl-3/4/5/7 and 8.
1678 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '2') {
1682 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '3') {
1686 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '4') {
1690 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '5') {
1694 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '6') {
1698 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '7') {
1702 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '8') {
1706 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xBD) {
1710 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xDF) {
1716 * First, all the keys that do tilde codes. (ESC '[' nn '~',
1717 * for integer decimal nn.)
1719 * We also deal with the weird ones here. Linux VCs replace F1
1720 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1721 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1726 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
1727 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
1728 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
1729 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
1730 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
1731 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
1732 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
1733 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
1734 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
1735 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
1736 case VK_F11: code = 23; break;
1737 case VK_F12: code = 24; break;
1738 case VK_HOME: code = 1; break;
1739 case VK_INSERT: code = 2; break;
1740 case VK_DELETE: code = 3; break;
1741 case VK_END: code = 4; break;
1742 case VK_PRIOR: code = 5; break;
1743 case VK_NEXT: code = 6; break;
1745 if (cfg.linux_funkeys && code >= 11 && code <= 15) {
1746 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
1749 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
1750 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
1754 p += sprintf((char *)p, "\x1B[%d~", code);
1759 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
1760 * some reason seems to send VK_CLEAR to Windows...).
1764 p += sprintf((char *)p, app_cursor_keys ? "\x1BOA" : "\x1B[A");
1767 p += sprintf((char *)p, app_cursor_keys ? "\x1BOB" : "\x1B[B");
1770 p += sprintf((char *)p, app_cursor_keys ? "\x1BOC" : "\x1B[C");
1773 p += sprintf((char *)p, app_cursor_keys ? "\x1BOD" : "\x1B[D");
1775 case VK_CLEAR: p += sprintf((char *)p, "\x1B[G"); return p - output;
1781 void set_title (char *title) {
1782 sfree (window_name);
1783 window_name = smalloc(1+strlen(title));
1784 strcpy (window_name, title);
1785 if (cfg.win_name_always || !IsIconic(hwnd))
1786 SetWindowText (hwnd, title);
1789 void set_icon (char *title) {
1791 icon_name = smalloc(1+strlen(title));
1792 strcpy (icon_name, title);
1793 if (!cfg.win_name_always && IsIconic(hwnd))
1794 SetWindowText (hwnd, title);
1797 void set_sbar (int total, int start, int page) {
1799 si.cbSize = sizeof(si);
1800 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL;
1802 si.nMax = total - 1;
1806 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
1809 Context get_ctx(void) {
1814 SelectPalette (hdc, pal, FALSE);
1820 void free_ctx (Context ctx) {
1821 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
1822 ReleaseDC (hwnd, ctx);
1825 static void real_palette_set (int n, int r, int g, int b) {
1827 logpal->palPalEntry[n].peRed = r;
1828 logpal->palPalEntry[n].peGreen = g;
1829 logpal->palPalEntry[n].peBlue = b;
1830 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
1831 colours[n] = PALETTERGB(r, g, b);
1832 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
1834 colours[n] = RGB(r, g, b);
1837 void palette_set (int n, int r, int g, int b) {
1838 static const int first[21] = {
1839 0, 2, 4, 6, 8, 10, 12, 14,
1840 1, 3, 5, 7, 9, 11, 13, 15,
1843 real_palette_set (first[n], r, g, b);
1845 real_palette_set (first[n]+1, r, g, b);
1847 HDC hdc = get_ctx();
1848 UnrealizeObject (pal);
1849 RealizePalette (hdc);
1854 void palette_reset (void) {
1857 for (i = 0; i < NCOLOURS; i++) {
1859 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
1860 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
1861 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
1862 logpal->palPalEntry[i].peFlags = 0;
1863 colours[i] = PALETTERGB(defpal[i].rgbtRed,
1864 defpal[i].rgbtGreen,
1865 defpal[i].rgbtBlue);
1867 colours[i] = RGB(defpal[i].rgbtRed,
1868 defpal[i].rgbtGreen,
1869 defpal[i].rgbtBlue);
1874 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
1876 RealizePalette (hdc);
1881 void write_clip (void *data, int len) {
1885 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
1888 lock = GlobalLock (clipdata);
1891 memcpy (lock, data, len);
1892 ((unsigned char *) lock) [len] = 0;
1893 GlobalUnlock (clipdata);
1895 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
1896 if (OpenClipboard (hwnd)) {
1898 SetClipboardData (CF_TEXT, clipdata);
1901 GlobalFree (clipdata);
1902 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
1905 void get_clip (void **p, int *len) {
1906 static HGLOBAL clipdata = NULL;
1910 GlobalUnlock (clipdata);
1914 if (OpenClipboard (NULL)) {
1915 clipdata = GetClipboardData (CF_TEXT);
1918 *p = GlobalLock (clipdata);
1932 * Move `lines' lines from position `from' to position `to' in the
1935 void optimised_move (int to, int from, int lines) {
1939 min = (to < from ? to : from);
1940 max = to + from - min;
1942 r.left = 0; r.right = cols * font_width;
1943 r.top = min * font_height; r.bottom = (max+lines) * font_height;
1944 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
1948 * Print a message box and perform a fatal exit.
1950 void fatalbox(char *fmt, ...) {
1955 vsprintf(stuff, fmt, ap);
1957 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);