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()) {
177 * If the hostname starts with "telnet://", set the
178 * protocol to Telnet and process the string as a
181 if (!strncmp(q, "telnet://", 9)) {
183 cfg.protocol = PROT_TELNET;
185 while (*p && *p != ':') p++;
191 strncpy (cfg.host, q, sizeof(cfg.host)-1);
192 cfg.host[sizeof(cfg.host)-1] = '\0';
194 while (*p && !isspace(*p)) p++;
197 strncpy (cfg.host, q, sizeof(cfg.host)-1);
198 cfg.host[sizeof(cfg.host)-1] = '\0';
199 while (*p && isspace(*p)) p++;
214 * Select protocol. This is farmed out into a table in a
215 * separate file to enable an ssh-free variant.
220 for (i = 0; backends[i].backend != NULL; i++)
221 if (backends[i].protocol == cfg.protocol) {
222 back = backends[i].backend;
226 MessageBox(NULL, "Unsupported protocol number found",
227 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
233 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
237 wndclass.lpfnWndProc = WndProc;
238 wndclass.cbClsExtra = 0;
239 wndclass.cbWndExtra = 0;
240 wndclass.hInstance = inst;
241 wndclass.hIcon = LoadIcon (inst,
242 MAKEINTRESOURCE(IDI_MAINICON));
243 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
244 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
245 wndclass.lpszMenuName = NULL;
246 wndclass.lpszClassName = appname;
248 RegisterClass (&wndclass);
253 savelines = cfg.savelines;
259 * Guess some defaults for the window size. This all gets
260 * updated later, so we don't really care too much. However, we
261 * do want the font width/height guesses to correspond to a
262 * large font rather than a small one...
269 term_size (cfg.height, cfg.width, cfg.savelines);
270 guess_width = extra_width + font_width * cols;
271 guess_height = extra_height + font_height * rows;
274 HWND w = GetDesktopWindow();
275 GetWindowRect (w, &r);
276 if (guess_width > r.right - r.left)
277 guess_width = r.right - r.left;
278 if (guess_height > r.bottom - r.top)
279 guess_height = r.bottom - r.top;
282 hwnd = CreateWindow (appname, appname,
283 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
284 CW_USEDEFAULT, CW_USEDEFAULT,
285 guess_width, guess_height,
286 NULL, NULL, inst, NULL);
289 * Initialise the fonts, simultaneously correcting the guesses
290 * for font_{width,height}.
292 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
297 * Correct the guesses for extra_{width,height}.
301 GetWindowRect (hwnd, &wr);
302 GetClientRect (hwnd, &cr);
303 extra_width = wr.right - wr.left - cr.right + cr.left;
304 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
308 * Resize the window, now we know what size we _really_ want it
311 guess_width = extra_width + font_width * cols;
312 guess_height = extra_height + font_height * rows;
313 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
314 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
315 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
318 * Initialise the scroll bar.
323 si.cbSize = sizeof(si);
324 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL;
329 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
333 * Start up the telnet connection.
340 error = back->init (hwnd, cfg.host, cfg.port, &realhost);
342 sprintf(msg, "Unable to open connection:\n%s", error);
343 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
346 window_name = icon_name = NULL;
347 sprintf(msg, "%s - PuTTY", realhost);
352 session_closed = FALSE;
355 * Set up the input and output buffers.
357 inbuf_reap = inbuf_head = 0;
358 outbuf_reap = outbuf_head = 0;
361 * Choose unscroll method
363 unscroll_event = US_DISP;
366 * Prepare the mouse handler.
368 lastact = MA_NOTHING;
369 lastbtn = MB_NOTHING;
370 dbltime = GetDoubleClickTime();
373 * Set up the session-control options on the system menu.
376 HMENU m = GetSystemMenu (hwnd, FALSE);
380 AppendMenu (m, MF_SEPARATOR, 0, 0);
381 if (cfg.protocol == PROT_TELNET) {
383 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
384 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
385 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
386 AppendMenu (p, MF_SEPARATOR, 0, 0);
387 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
388 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
389 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
390 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
391 AppendMenu (p, MF_SEPARATOR, 0, 0);
392 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
393 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
394 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
395 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
396 AppendMenu (p, MF_SEPARATOR, 0, 0);
397 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
398 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
399 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
400 AppendMenu (m, MF_SEPARATOR, 0, 0);
402 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
403 AppendMenu (m, MF_SEPARATOR, 0, 0);
404 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
405 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
408 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
409 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
410 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
411 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
412 AppendMenu (m, MF_SEPARATOR, 0, 0);
413 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
414 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
415 AppendMenu (m, MF_SEPARATOR, 0, 0);
416 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
420 * Finally show the window!
422 ShowWindow (hwnd, show);
425 * Set the palette up.
431 has_focus = (GetForegroundWindow() == hwnd);
435 int timer_id = 0, long_timer = 0;
437 while (GetMessage (&msg, NULL, 0, 0) == 1) {
438 /* Sometimes DispatchMessage calls routines that use their own
439 * GetMessage loop, setup this timer so we get some control back.
441 * Also call term_update() from the timer so that if the host
442 * is sending data flat out we still do redraws.
444 if(timer_id && long_timer) {
445 KillTimer(hwnd, timer_id);
446 long_timer = timer_id = 0;
449 timer_id = SetTimer(hwnd, 1, 20, NULL);
450 DispatchMessage (&msg);
452 /* This is too fast, but I'll leave it for now 'cause it shows
453 * how often term_update is called (far too often at times!)
457 /* If there's nothing new in the queue then we can do everything
458 * we've delayed, reading the socket, writing, and repainting
461 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
462 if (pending_netevent) {
463 enact_pending_netevent();
468 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
470 KillTimer(hwnd, timer_id);
473 if (inbuf_reap != inbuf_head)
476 timer_id = SetTimer(hwnd, 1, 500, NULL);
489 DeleteObject(fonts[i]);
496 if (cfg.protocol == PROT_SSH) {
507 * Actually do the job requested by a WM_NETEVENT
509 static void enact_pending_netevent(void) {
511 pending_netevent = FALSE;
512 i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
516 switch (WSABASEERR + (-i) % 10000) {
518 sprintf(buf, "Connection reset by peer");
521 sprintf(buf, "Unexpected network error %d", -i);
524 MessageBox(hwnd, buf, "PuTTY Fatal Error",
525 MB_ICONERROR | MB_OK);
528 if (cfg.close_on_exit)
531 session_closed = TRUE;
532 MessageBox(hwnd, "Connection closed by remote host",
533 "PuTTY", MB_OK | MB_ICONINFORMATION);
534 SetWindowText (hwnd, "PuTTY (inactive)");
540 * Copy the colour palette from the configuration data into defpal.
541 * This is non-trivial because the colour indices are different.
543 static void cfgtopalette(void) {
545 static const int ww[] = {
546 6, 7, 8, 9, 10, 11, 12, 13,
547 14, 15, 16, 17, 18, 19, 20, 21,
548 0, 1, 2, 3, 4, 4, 5, 5
551 for (i=0; i<24; i++) {
553 defpal[i].rgbtRed = cfg.colours[w][0];
554 defpal[i].rgbtGreen = cfg.colours[w][1];
555 defpal[i].rgbtBlue = cfg.colours[w][2];
560 * Set up the colour palette.
562 static void init_palette(void) {
564 HDC hdc = GetDC (hwnd);
566 if (cfg.try_palette &&
567 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
568 logpal = smalloc(sizeof(*logpal)
569 - sizeof(logpal->palPalEntry)
570 + NCOLOURS * sizeof(PALETTEENTRY));
571 logpal->palVersion = 0x300;
572 logpal->palNumEntries = NCOLOURS;
573 for (i = 0; i < NCOLOURS; i++) {
574 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
575 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
576 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
577 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
579 pal = CreatePalette (logpal);
581 SelectPalette (hdc, pal, FALSE);
582 RealizePalette (hdc);
583 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
587 ReleaseDC (hwnd, hdc);
590 for (i=0; i<NCOLOURS; i++)
591 colours[i] = PALETTERGB(defpal[i].rgbtRed,
595 for(i=0; i<NCOLOURS; i++)
596 colours[i] = RGB(defpal[i].rgbtRed,
602 * Initialise all the fonts we will need. There may be as many as
603 * eight or as few as one. We also:
605 * - check the font width and height, correcting our guesses if
608 * - verify that the bold font is the same width as the ordinary
609 * one, and engage shadow bolding if not.
611 * - verify that the underlined font is the same width as the
612 * ordinary one (manual underlining by means of line drawing can
613 * be done in a pinch).
615 static void init_fonts(int pick_width) {
620 int fw_dontcare, fw_bold;
629 if (cfg.fontisbold) {
630 fw_dontcare = FW_BOLD;
633 fw_dontcare = FW_DONTCARE;
639 font_height = cfg.fontheight;
640 font_width = pick_width;
643 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
644 c, OUT_DEFAULT_PRECIS, \
645 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
646 FIXED_PITCH | FF_DONTCARE, cfg.font)
648 if (cfg.vtmode != VT_OEMONLY) {
649 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
651 SelectObject (hdc, fonts[FONT_NORMAL]);
652 GetTextMetrics(hdc, &tm);
653 font_height = tm.tmHeight;
654 font_width = tm.tmAveCharWidth;
656 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
658 if (bold_mode == BOLD_FONT) {
659 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
660 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
663 if (cfg.vtmode == VT_OEMANSI) {
664 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
665 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
667 if (bold_mode == BOLD_FONT) {
668 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
669 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
675 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
677 SelectObject (hdc, fonts[FONT_OEM]);
678 GetTextMetrics(hdc, &tm);
679 font_height = tm.tmHeight;
680 font_width = tm.tmAveCharWidth;
682 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
684 if (bold_mode == BOLD_FONT) {
685 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
686 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
691 descent = tm.tmAscent + 1;
692 if (descent >= font_height)
693 descent = font_height - 1;
694 firstchar = tm.tmFirstChar;
696 for (i=0; i<8; i++) {
698 if (SelectObject (hdc, fonts[i]) &&
699 GetTextMetrics(hdc, &tm) )
700 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
706 ReleaseDC (hwnd, hdc);
708 /* ... This is wrong in OEM only mode */
709 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
710 (bold_mode == BOLD_FONT &&
711 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
713 DeleteObject (fonts[FONT_UNDERLINE]);
714 if (bold_mode == BOLD_FONT)
715 DeleteObject (fonts[FONT_BOLDUND]);
718 if (bold_mode == BOLD_FONT &&
719 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
720 bold_mode = BOLD_SHADOW;
721 DeleteObject (fonts[FONT_BOLD]);
722 if (und_mode == UND_FONT)
723 DeleteObject (fonts[FONT_BOLDUND]);
727 /* With the fascist font painting it doesn't matter if the linedraw font
728 * isn't exactly the right size anymore so we don't have to check this.
730 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
731 if( cfg.fontcharset == OEM_CHARSET )
733 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
734 "different sizes. Using OEM-only mode instead",
735 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
736 cfg.vtmode = VT_OEMONLY;
738 else if( firstchar < ' ' )
740 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
741 "different sizes. Using XTerm mode instead",
742 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
743 cfg.vtmode = VT_XWINDOWS;
747 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
748 "different sizes. Using ISO8859-1 mode instead",
749 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
750 cfg.vtmode = VT_POORMAN;
755 DeleteObject (fonts[i]);
761 void request_resize (int w, int h, int refont) {
765 /* Don't do this in OEMANSI, you may get disable messages */
766 if (refont && w != cols && (cols==80 || cols==132)
767 && cfg.vtmode != VT_OEMANSI)
769 if (refont && w != cols && (cols==80 || cols==132))
772 /* If font width too big for screen should we shrink the font more ? */
774 font_width = ((font_width*cols+w/2)/w);
781 DeleteObject(fonts[i]);
783 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
785 init_fonts(font_width);
788 width = extra_width + font_width * w;
789 height = extra_height + font_height * h;
791 SetWindowPos (hwnd, NULL, 0, 0, width, height,
792 SWP_NOACTIVATE | SWP_NOCOPYBITS |
793 SWP_NOMOVE | SWP_NOZORDER);
796 static void click (Mouse_Button b, int x, int y) {
797 int thistime = GetMessageTime();
799 if (lastbtn == b && thistime - lasttime < dbltime) {
800 lastact = (lastact == MA_CLICK ? MA_2CLK :
801 lastact == MA_2CLK ? MA_3CLK :
802 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
807 if (lastact != MA_NOTHING)
808 term_mouse (b, lastact, x, y);
812 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
813 WPARAM wParam, LPARAM lParam) {
815 static int ignore_size = FALSE;
816 static int ignore_clip = FALSE;
817 static int just_reconfigged = FALSE;
821 if (pending_netevent)
822 enact_pending_netevent();
823 if (inbuf_reap != inbuf_head)
830 if (!cfg.warn_on_close || session_closed ||
831 MessageBox(hwnd, "Are you sure you want to close this session?",
832 "PuTTY Exit Confirmation",
833 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
840 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
852 PROCESS_INFORMATION pi;
853 HANDLE filemap = NULL;
855 if (wParam == IDM_DUPSESS) {
857 * Allocate a file-mapping memory chunk for the
860 SECURITY_ATTRIBUTES sa;
863 sa.nLength = sizeof(sa);
864 sa.lpSecurityDescriptor = NULL;
865 sa.bInheritHandle = TRUE;
866 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
873 p = (Config *)MapViewOfFile(filemap,
875 0, 0, sizeof(Config));
877 *p = cfg; /* structure copy */
881 sprintf(c, "putty &%p", filemap);
883 } else if (wParam == IDM_SAVEDSESS) {
884 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
885 cl = malloc(16 + strlen(session)); /* 8, but play safe */
887 cl = NULL; /* not a very important failure mode */
889 sprintf(cl, "putty @%s", session);
895 GetModuleFileName (NULL, b, sizeof(b)-1);
897 si.lpReserved = NULL;
902 si.lpReserved2 = NULL;
903 CreateProcess (b, cl, NULL, NULL, TRUE,
904 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
907 CloseHandle(filemap);
913 if (!do_reconfig(hwnd))
915 just_reconfigged = TRUE;
920 DeleteObject(fonts[i]);
922 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
926 /* Telnet will change local echo -> remote if the remote asks */
927 if (cfg.protocol != PROT_TELNET)
928 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
935 term_size(cfg.height, cfg.width, cfg.savelines);
936 InvalidateRect(hwnd, NULL, TRUE);
937 SetWindowPos (hwnd, NULL, 0, 0,
938 extra_width + font_width * cfg.width,
939 extra_height + font_height * cfg.height,
940 SWP_NOACTIVATE | SWP_NOCOPYBITS |
941 SWP_NOMOVE | SWP_NOZORDER);
942 if (IsIconic(hwnd)) {
944 cfg.win_name_always ? window_name : icon_name);
953 case IDM_TEL_AYT: back->special (TS_AYT); break;
954 case IDM_TEL_BRK: back->special (TS_BRK); break;
955 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
956 case IDM_TEL_EC: back->special (TS_EC); break;
957 case IDM_TEL_EL: back->special (TS_EL); break;
958 case IDM_TEL_GA: back->special (TS_GA); break;
959 case IDM_TEL_NOP: back->special (TS_NOP); break;
960 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
961 case IDM_TEL_AO: back->special (TS_AO); break;
962 case IDM_TEL_IP: back->special (TS_IP); break;
963 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
964 case IDM_TEL_EOR: back->special (TS_EOR); break;
965 case IDM_TEL_EOF: back->special (TS_EOF); break;
970 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
971 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
976 #define X_POS(l) ((int)(short)LOWORD(l))
977 #define Y_POS(l) ((int)(short)HIWORD(l))
979 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
980 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
983 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
984 TO_CHR_Y(Y_POS(lParam)));
988 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
989 TO_CHR_Y(Y_POS(lParam)));
994 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
995 TO_CHR_X(X_POS(lParam)),
996 TO_CHR_Y(Y_POS(lParam)));
999 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1000 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1001 TO_CHR_Y(Y_POS(lParam)));
1004 case WM_RBUTTONDOWN:
1006 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1007 TO_CHR_X(X_POS(lParam)),
1008 TO_CHR_Y(Y_POS(lParam)));
1011 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1012 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1013 TO_CHR_Y(Y_POS(lParam)));
1018 * Add the mouse position and message time to the random
1019 * number noise, if we're using ssh.
1021 if (cfg.protocol == PROT_SSH)
1022 noise_ultralight(lParam);
1024 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1026 if (wParam & MK_LBUTTON)
1028 else if (wParam & MK_MBUTTON)
1029 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1031 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1032 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1033 TO_CHR_Y(Y_POS(lParam)));
1036 case WM_IGNORE_CLIP:
1037 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1039 case WM_DESTROYCLIPBOARD:
1042 ignore_clip = FALSE;
1047 hdc = BeginPaint (hwnd, &p);
1049 SelectPalette (hdc, pal, TRUE);
1050 RealizePalette (hdc);
1052 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1053 p.rcPaint.right, p.rcPaint.bottom);
1054 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1055 SelectObject (hdc, GetStockObject(WHITE_PEN));
1056 EndPaint (hwnd, &p);
1060 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1061 * but the only one that's likely to try to overload us is FD_READ.
1062 * This means buffering just one is fine.
1064 if (pending_netevent)
1065 enact_pending_netevent();
1067 pending_netevent = TRUE;
1068 pend_netevent_wParam=wParam;
1069 pend_netevent_lParam=lParam;
1081 case WM_IGNORE_SIZE:
1082 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1084 case WM_ENTERSIZEMOVE:
1087 case WM_EXITSIZEMOVE:
1092 int width, height, w, h, ew, eh;
1093 LPRECT r = (LPRECT)lParam;
1095 width = r->right - r->left - extra_width;
1096 height = r->bottom - r->top - extra_height;
1097 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1098 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1099 UpdateSizeTip(hwnd, w, h);
1100 ew = width - w * font_width;
1101 eh = height - h * font_height;
1103 if (wParam == WMSZ_LEFT ||
1104 wParam == WMSZ_BOTTOMLEFT ||
1105 wParam == WMSZ_TOPLEFT)
1111 if (wParam == WMSZ_TOP ||
1112 wParam == WMSZ_TOPRIGHT ||
1113 wParam == WMSZ_TOPLEFT)
1123 /* break; (never reached) */
1125 if (wParam == SIZE_MINIMIZED) {
1126 SetWindowText (hwnd,
1127 cfg.win_name_always ? window_name : icon_name);
1130 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1131 SetWindowText (hwnd, window_name);
1133 int width, height, w, h;
1134 #if 0 /* we have fixed this using WM_SIZING now */
1138 width = LOWORD(lParam);
1139 height = HIWORD(lParam);
1140 w = width / font_width; if (w < 1) w = 1;
1141 h = height / font_height; if (h < 1) h = 1;
1142 #if 0 /* we have fixed this using WM_SIZING now */
1143 ew = width - w * font_width;
1144 eh = height - h * font_height;
1145 if (ew != 0 || eh != 0) {
1147 GetWindowRect (hwnd, &r);
1148 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1149 SetWindowPos (hwnd, NULL, 0, 0,
1150 r.right - r.left - ew, r.bottom - r.top - eh,
1151 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1154 if (w != cols || h != rows || just_reconfigged) {
1156 term_size (h, w, cfg.savelines);
1158 just_reconfigged = FALSE;
1161 ignore_size = FALSE;
1164 switch (LOWORD(wParam)) {
1165 case SB_BOTTOM: term_scroll(-1, 0); break;
1166 case SB_TOP: term_scroll(+1, 0); break;
1167 case SB_LINEDOWN: term_scroll (0, +1); break;
1168 case SB_LINEUP: term_scroll (0, -1); break;
1169 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1170 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1171 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1172 term_scroll (1, HIWORD(wParam)); break;
1175 case WM_PALETTECHANGED:
1176 if ((HWND) wParam != hwnd && pal != NULL) {
1177 HDC hdc = get_ctx();
1179 if (RealizePalette (hdc) > 0)
1185 case WM_QUERYNEWPALETTE:
1187 HDC hdc = get_ctx();
1189 if (RealizePalette (hdc) > 0)
1199 * Add the scan code and keypress timing to the random
1200 * number noise, if we're using ssh.
1202 if (cfg.protocol == PROT_SSH)
1203 noise_ultralight(lParam);
1206 * We don't do TranslateMessage since it disassociates the
1207 * resulting CHAR message from the KEYDOWN that sparked it,
1208 * which we occasionally don't want. Instead, we process
1209 * KEYDOWN, and call the Win32 translator functions so that
1210 * we get the translations under _our_ control.
1213 unsigned char buf[20];
1216 len = TranslateKey (wParam, lParam, buf);
1218 return DefWindowProc (hwnd, message, wParam, lParam);
1219 ldisc->send (buf, len);
1225 * We handle KEYUP ourselves in order to distinghish left
1226 * and right Alt or Control keys, which Windows won't do
1227 * right if left to itself. See also the special processing
1228 * at the top of TranslateKey.
1232 int ret = GetKeyboardState(keystate);
1233 if (ret && wParam == VK_MENU) {
1234 if (lParam & 0x1000000) keystate[VK_RMENU] = 0;
1235 else keystate[VK_LMENU] = 0;
1236 SetKeyboardState (keystate);
1238 if (ret && wParam == VK_CONTROL) {
1239 if (lParam & 0x1000000) keystate[VK_RCONTROL] = 0;
1240 else keystate[VK_LCONTROL] = 0;
1241 SetKeyboardState (keystate);
1245 * We don't return here, in order to allow Windows to do
1246 * its own KEYUP processing as well.
1252 * Nevertheless, we are prepared to deal with WM_CHAR
1253 * messages, should they crop up. So if someone wants to
1254 * post the things to us as part of a macro manoeuvre,
1255 * we're ready to cope.
1258 char c = xlat_kbd2tty((unsigned char)wParam);
1259 ldisc->send (&c, 1);
1264 return DefWindowProc (hwnd, message, wParam, lParam);
1268 * Draw a line of text in the window, at given character
1269 * coordinates, in given attributes.
1271 * We are allowed to fiddle with the contents of `text'.
1273 void do_text (Context ctx, int x, int y, char *text, int len,
1274 unsigned long attr) {
1275 int lattr = 0; /* Will be arg later for line attribute type */
1277 int nfg, nbg, nfont;
1280 int force_manual_underline = 0;
1281 static int *IpDx = 0, IpDxLEN = 0;;
1283 if (len>IpDxLEN || IpDx[0] != font_width*(1+!lattr)) {
1287 IpDx = smalloc((len+16)*sizeof(int));
1290 for(i=0; i<len; i++)
1291 IpDx[i] = font_width;
1299 if (attr & ATTR_ACTCURS) {
1300 attr &= (bold_mode == BOLD_COLOURS ? 0x200 : 0x300);
1301 attr ^= ATTR_CUR_XOR;
1305 if (cfg.vtmode == VT_OEMONLY)
1309 * Map high-half characters in order to approximate ISO using
1310 * OEM character set. No characters are missing if the OEM codepage
1313 if (nfont & FONT_OEM) {
1315 for (i=0; i<len; i++)
1316 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1318 /* This is CP850 ... perfect translation */
1319 static const char oemhighhalf[] =
1320 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1321 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1322 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1323 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1324 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1325 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1326 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1327 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1328 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1329 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1330 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1331 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1334 /* This is CP437 ... junk translation */
1335 static const unsigned char oemhighhalf[] = {
1336 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1337 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1338 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1339 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1340 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1341 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1342 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1343 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1344 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1345 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1346 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1347 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1350 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1354 if (attr & ATTR_GBCHR) {
1357 * GB mapping: map # to pound, and everything else stays
1360 for (i=0; i<len; i++)
1362 text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
1363 } else if (attr & ATTR_LINEDRW) {
1366 static const char poorman[] =
1367 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1370 static const char oemmap_437[] =
1371 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1372 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1375 static const char oemmap_850[] =
1376 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1377 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1379 /* Poor windows font ... eg: windows courier */
1380 static const char oemmap[] =
1381 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1382 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1385 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1386 * VT100 line drawing chars; everything else stays normal.
1388 switch (cfg.vtmode) {
1390 for (i=0; i<len; i++)
1391 if (text[i] >= '\x60' && text[i] <= '\x7E')
1392 text[i] += '\x01' - '\x60';
1395 /* Make sure we actually have an OEM font */
1396 if (fonts[nfont|FONT_OEM]) {
1399 for (i=0; i<len; i++)
1400 if (text[i] >= '\x60' && text[i] <= '\x7E')
1401 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1405 for (i=0; i<len; i++)
1406 if (text[i] >= '\x60' && text[i] <= '\x7E')
1407 text[i] = poorman[(unsigned char)text[i] - 0x60];
1412 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1413 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1414 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1416 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1417 nfont |= FONT_UNDERLINE;
1420 if (nfont&FONT_UNDERLINE)
1421 force_manual_underline = 1;
1422 /* Don't do the same for manual bold, it could be bad news. */
1424 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1426 if (attr & ATTR_REVERSE) {
1427 t = nfg; nfg = nbg; nbg = t;
1429 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1431 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1435 SelectObject (hdc, fonts[nfont]);
1436 SetTextColor (hdc, fg);
1437 SetBkColor (hdc, bg);
1438 SetBkMode (hdc, OPAQUE);
1441 line_box.right = x+font_width*len;
1442 line_box.bottom = y+font_height;
1443 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1444 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1445 SetBkMode (hdc, TRANSPARENT);
1447 /* GRR: This draws the character outside it's box and can leave
1448 * 'droppings' even with the clip box! I suppose I could loop it
1449 * one character at a time ... yuk. */
1450 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1452 if (force_manual_underline ||
1453 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1455 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1456 MoveToEx (hdc, x, y+descent, NULL);
1457 LineTo (hdc, x+len*font_width, y+descent);
1458 oldpen = SelectObject (hdc, oldpen);
1459 DeleteObject (oldpen);
1461 if (attr & ATTR_PASCURS) {
1464 pts[0].x = pts[1].x = pts[4].x = x;
1465 pts[2].x = pts[3].x = x+font_width-1;
1466 pts[0].y = pts[3].y = pts[4].y = y;
1467 pts[1].y = pts[2].y = y+font_height-1;
1468 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1469 Polyline (hdc, pts, 5);
1470 oldpen = SelectObject (hdc, oldpen);
1471 DeleteObject (oldpen);
1476 * Translate a WM_(SYS)?KEYDOWN message into a string of ASCII
1477 * codes. Returns number of bytes used.
1479 static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output) {
1480 unsigned char *p = output;
1483 int cancel_alt = FALSE;
1486 * Get hold of the keyboard state, because we'll need it a few
1489 ret = GetKeyboardState(keystate);
1492 * Record that we pressed key so the scroll window can be reset, but
1493 * be careful to avoid Shift-UP/Down
1495 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
1500 * Windows does not always want to distinguish left and right
1501 * Alt or Control keys. Thus we keep track of them ourselves.
1502 * See also the WM_KEYUP handler.
1504 if (wParam == VK_MENU) {
1505 if (lParam & 0x1000000) keystate[VK_RMENU] = 0x80;
1506 else keystate[VK_LMENU] = 0x80;
1507 SetKeyboardState (keystate);
1510 if (wParam == VK_CONTROL) {
1511 if (lParam & 0x1000000) keystate[VK_RCONTROL] = 0x80;
1512 else keystate[VK_LCONTROL] = 0x80;
1513 SetKeyboardState (keystate);
1518 * Prepend ESC, and cancel ALT, if ALT was pressed at the time
1519 * and it wasn't AltGr.
1521 if (lParam & 0x20000000 && (keystate[VK_LMENU] & 0x80)) {
1527 * NetHack keypad mode. This may conflict with Shift-PgUp/PgDn,
1528 * so we do it first.
1530 if (cfg.nethack_keypad) {
1531 int shift = keystate[VK_SHIFT] & 0x80;
1533 * NB the shifted versions only work with numlock off.
1535 switch ( (lParam >> 16) & 0x1FF ) {
1536 case 0x047: *p++ = shift ? 'Y' : 'y'; return p - output;
1537 case 0x048: *p++ = shift ? 'K' : 'k'; return p - output;
1538 case 0x049: *p++ = shift ? 'U' : 'u'; return p - output;
1539 case 0x04B: *p++ = shift ? 'H' : 'h'; return p - output;
1540 case 0x04C: *p++ = '.'; return p - output;
1541 case 0x04D: *p++ = shift ? 'L' : 'l'; return p - output;
1542 case 0x04F: *p++ = shift ? 'B' : 'b'; return p - output;
1543 case 0x050: *p++ = shift ? 'J' : 'j'; return p - output;
1544 case 0x051: *p++ = shift ? 'N' : 'n'; return p - output;
1545 case 0x053: *p++ = '.'; return p - output;
1550 * Shift-PgUp, Shift-PgDn, and Alt-F4 all produce window
1551 * events: we'll deal with those now.
1553 if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_PRIOR) {
1554 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
1557 if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_NEXT) {
1558 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
1561 if ((lParam & 0x20000000) && wParam == VK_F4 && cfg.alt_f4) {
1564 if ((lParam & 0x20000000) && wParam == VK_SPACE && cfg.alt_space) {
1565 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
1570 * In general, the strategy is to see what the Windows keymap
1571 * translation has to say for itself, and then process function
1572 * keys and suchlike ourselves if that fails. But first we must
1573 * deal with the small number of special cases which the
1574 * Windows keymap translator thinks it can do but gets wrong.
1576 * First special case: we might want the Backspace key to send
1579 if (wParam == VK_BACK) {
1580 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
1585 * Control-Space should send ^@ (0x00), not Space.
1587 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == VK_SPACE) {
1592 if (app_keypad_keys) {
1594 * If we're in applications keypad mode, we have to process it
1595 * before char-map translation, because it will pre-empt lots
1596 * of stuff, even if NumLock is off.
1600 * Hack to ensure NumLock doesn't interfere with
1601 * perception of Shift for Keypad Plus. I don't pretend
1602 * to understand this, but it seems to work as is.
1603 * Leave it alone, or die.
1605 keystate[VK_NUMLOCK] = 0;
1606 SetKeyboardState (keystate);
1607 GetKeyboardState (keystate);
1609 switch ( (lParam >> 16) & 0x1FF ) {
1610 case 0x145: p += sprintf((char *)p, "\x1BOP"); return p - output;
1611 case 0x135: p += sprintf((char *)p, "\x1BOQ"); return p - output;
1612 case 0x037: p += sprintf((char *)p, "\x1BOR"); return p - output;
1613 case 0x047: p += sprintf((char *)p, "\x1BOw"); return p - output;
1614 case 0x048: p += sprintf((char *)p, "\x1BOx"); return p - output;
1615 case 0x049: p += sprintf((char *)p, "\x1BOy"); return p - output;
1616 case 0x04A: p += sprintf((char *)p, "\x1BOS"); return p - output;
1617 case 0x04B: p += sprintf((char *)p, "\x1BOt"); return p - output;
1618 case 0x04C: p += sprintf((char *)p, "\x1BOu"); return p - output;
1619 case 0x04D: p += sprintf((char *)p, "\x1BOv"); return p - output;
1620 case 0x04E: /* keypad + is ^[Ol, but ^[Om with Shift */
1621 p += sprintf((char *)p,
1622 (ret && (keystate[VK_SHIFT] & 0x80)) ?
1623 "\x1BOm" : "\x1BOl");
1625 case 0x04F: p += sprintf((char *)p, "\x1BOq"); return p - output;
1626 case 0x050: p += sprintf((char *)p, "\x1BOr"); return p - output;
1627 case 0x051: p += sprintf((char *)p, "\x1BOs"); return p - output;
1628 case 0x052: p += sprintf((char *)p, "\x1BOp"); return p - output;
1629 case 0x053: p += sprintf((char *)p, "\x1BOn"); return p - output;
1630 case 0x11C: p += sprintf((char *)p, "\x1BOM"); return p - output;
1635 * Shift-Tab should send ESC [ Z.
1637 if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_TAB) {
1638 *p++ = 0x1B; /* ESC */
1645 * Before doing Windows charmap translation, remove LeftALT
1646 * from the keymap, since its sole effect should be to prepend
1647 * ESC, which we've already done. Note that removal of LeftALT
1648 * has to happen _after_ the above call to SetKeyboardState, or
1649 * dire things will befall.
1652 keystate[VK_MENU] = keystate[VK_RMENU];
1653 keystate[VK_LMENU] = 0;
1657 * Attempt the Windows char-map translation.
1662 BOOL capsOn=keystate[VK_CAPITAL] !=0;
1664 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
1665 if(cfg.xlat_capslockcyr)
1666 keystate[VK_CAPITAL] = 0;
1668 r = ToAscii (wParam, (lParam >> 16) & 0xFF,
1672 chr = xlat_latkbd2win((unsigned char)(chr & 0xFF));
1674 *p++ = xlat_kbd2tty((unsigned char)(chr & 0xFF));
1680 * OK, we haven't had a key code from the keymap translation.
1681 * We'll try our various special cases and function keys, and
1682 * then give up. (There's nothing wrong with giving up:
1683 * Scrollock, Pause/Break, and of course the various buckybit
1684 * keys all produce KEYDOWN events that we really _do_ want to
1689 * Control-2 should return ^@ (0x00), Control-6 should return
1690 * ^^ (0x1E), and Control-Minus should return ^_ (0x1F). Since
1691 * the DOS keyboard handling did it, and we have nothing better
1692 * to do with the key combo in question, we'll also map
1693 * Control-Backquote to ^\ (0x1C).
1695 * In addition a real VT100 maps Ctrl-3/4/5/7 and 8.
1697 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '2') {
1701 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '3') {
1705 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '4') {
1709 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '5') {
1713 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '6') {
1717 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '7') {
1721 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '8') {
1725 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xBD) {
1729 if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xDF) {
1735 * First, all the keys that do tilde codes. (ESC '[' nn '~',
1736 * for integer decimal nn.)
1738 * We also deal with the weird ones here. Linux VCs replace F1
1739 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1740 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1745 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
1746 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
1747 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
1748 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
1749 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
1750 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
1751 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
1752 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
1753 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
1754 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
1755 case VK_F11: code = 23; break;
1756 case VK_F12: code = 24; break;
1757 case VK_HOME: code = 1; break;
1758 case VK_INSERT: code = 2; break;
1759 case VK_DELETE: code = 3; break;
1760 case VK_END: code = 4; break;
1761 case VK_PRIOR: code = 5; break;
1762 case VK_NEXT: code = 6; break;
1764 if (cfg.linux_funkeys && code >= 11 && code <= 15) {
1765 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
1768 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
1769 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
1773 p += sprintf((char *)p, "\x1B[%d~", code);
1778 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
1779 * some reason seems to send VK_CLEAR to Windows...).
1783 p += sprintf((char *)p, app_cursor_keys ? "\x1BOA" : "\x1B[A");
1786 p += sprintf((char *)p, app_cursor_keys ? "\x1BOB" : "\x1B[B");
1789 p += sprintf((char *)p, app_cursor_keys ? "\x1BOC" : "\x1B[C");
1792 p += sprintf((char *)p, app_cursor_keys ? "\x1BOD" : "\x1B[D");
1794 case VK_CLEAR: p += sprintf((char *)p, "\x1B[G"); return p - output;
1800 void set_title (char *title) {
1801 sfree (window_name);
1802 window_name = smalloc(1+strlen(title));
1803 strcpy (window_name, title);
1804 if (cfg.win_name_always || !IsIconic(hwnd))
1805 SetWindowText (hwnd, title);
1808 void set_icon (char *title) {
1810 icon_name = smalloc(1+strlen(title));
1811 strcpy (icon_name, title);
1812 if (!cfg.win_name_always && IsIconic(hwnd))
1813 SetWindowText (hwnd, title);
1816 void set_sbar (int total, int start, int page) {
1818 si.cbSize = sizeof(si);
1819 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL;
1821 si.nMax = total - 1;
1825 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
1828 Context get_ctx(void) {
1833 SelectPalette (hdc, pal, FALSE);
1839 void free_ctx (Context ctx) {
1840 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
1841 ReleaseDC (hwnd, ctx);
1844 static void real_palette_set (int n, int r, int g, int b) {
1846 logpal->palPalEntry[n].peRed = r;
1847 logpal->palPalEntry[n].peGreen = g;
1848 logpal->palPalEntry[n].peBlue = b;
1849 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
1850 colours[n] = PALETTERGB(r, g, b);
1851 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
1853 colours[n] = RGB(r, g, b);
1856 void palette_set (int n, int r, int g, int b) {
1857 static const int first[21] = {
1858 0, 2, 4, 6, 8, 10, 12, 14,
1859 1, 3, 5, 7, 9, 11, 13, 15,
1862 real_palette_set (first[n], r, g, b);
1864 real_palette_set (first[n]+1, r, g, b);
1866 HDC hdc = get_ctx();
1867 UnrealizeObject (pal);
1868 RealizePalette (hdc);
1873 void palette_reset (void) {
1876 for (i = 0; i < NCOLOURS; i++) {
1878 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
1879 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
1880 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
1881 logpal->palPalEntry[i].peFlags = 0;
1882 colours[i] = PALETTERGB(defpal[i].rgbtRed,
1883 defpal[i].rgbtGreen,
1884 defpal[i].rgbtBlue);
1886 colours[i] = RGB(defpal[i].rgbtRed,
1887 defpal[i].rgbtGreen,
1888 defpal[i].rgbtBlue);
1893 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
1895 RealizePalette (hdc);
1900 void write_clip (void *data, int len) {
1904 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
1907 lock = GlobalLock (clipdata);
1910 memcpy (lock, data, len);
1911 ((unsigned char *) lock) [len] = 0;
1912 GlobalUnlock (clipdata);
1914 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
1915 if (OpenClipboard (hwnd)) {
1917 SetClipboardData (CF_TEXT, clipdata);
1920 GlobalFree (clipdata);
1921 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
1924 void get_clip (void **p, int *len) {
1925 static HGLOBAL clipdata = NULL;
1929 GlobalUnlock (clipdata);
1933 if (OpenClipboard (NULL)) {
1934 clipdata = GetClipboardData (CF_TEXT);
1937 *p = GlobalLock (clipdata);
1951 * Move `lines' lines from position `from' to position `to' in the
1954 void optimised_move (int to, int from, int lines) {
1958 min = (to < from ? to : from);
1959 max = to + from - min;
1961 r.left = 0; r.right = cols * font_width;
1962 r.top = min * font_height; r.bottom = (max+lines) * font_height;
1963 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
1967 * Print a message box and perform a fatal exit.
1969 void fatalbox(char *fmt, ...) {
1974 vsprintf(stuff, fmt, ap);
1976 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);