17 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
23 #define IDM_SHOWLOG 0x0010
24 #define IDM_NEWSESS 0x0020
25 #define IDM_DUPSESS 0x0030
26 #define IDM_RECONF 0x0040
27 #define IDM_CLRSB 0x0050
28 #define IDM_RESET 0x0060
29 #define IDM_TEL_AYT 0x0070
30 #define IDM_TEL_BRK 0x0080
31 #define IDM_TEL_SYNCH 0x0090
32 #define IDM_TEL_EC 0x00a0
33 #define IDM_TEL_EL 0x00b0
34 #define IDM_TEL_GA 0x00c0
35 #define IDM_TEL_NOP 0x00d0
36 #define IDM_TEL_ABORT 0x00e0
37 #define IDM_TEL_AO 0x00f0
38 #define IDM_TEL_IP 0x0100
39 #define IDM_TEL_SUSP 0x0110
40 #define IDM_TEL_EOR 0x0120
41 #define IDM_TEL_EOF 0x0130
42 #define IDM_ABOUT 0x0140
43 #define IDM_SAVEDSESS 0x0150
44 #define IDM_COPYALL 0x0160
46 #define IDM_SESSLGP 0x0250 /* log type printable */
47 #define IDM_SESSLGA 0x0260 /* log type all chars */
48 #define IDM_SESSLGE 0x0270 /* log end */
49 #define IDM_SAVED_MIN 0x1000
50 #define IDM_SAVED_MAX 0x2000
52 #define WM_IGNORE_SIZE (WM_XUSER + 1)
53 #define WM_IGNORE_CLIP (WM_XUSER + 2)
55 /* Needed for Chinese support and apparently not always defined. */
57 #define VK_PROCESSKEY 0xE5
60 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
61 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
62 static void cfgtopalette(void);
63 static void init_palette(void);
64 static void init_fonts(int);
66 static int extra_width, extra_height;
68 static int pending_netevent = 0;
69 static WPARAM pend_netevent_wParam = 0;
70 static LPARAM pend_netevent_lParam = 0;
71 static void enact_pending_netevent(void);
73 static time_t last_movement = 0;
77 #define FONT_UNDERLINE 2
78 #define FONT_BOLDUND 3
80 #define FONT_OEMBOLD 5
81 #define FONT_OEMBOLDUND 6
83 static HFONT fonts[8];
84 static int font_needs_hand_underlining;
86 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
94 static COLORREF colours[NCOLOURS];
96 static LPLOGPALETTE logpal;
97 static RGBTRIPLE defpal[NCOLOURS];
101 static HBITMAP caretbm;
103 static int dbltime, lasttime, lastact;
104 static Mouse_Button lastbtn;
106 static char *window_name, *icon_name;
108 static int compose_state = 0;
110 /* Dummy routine, only required in plink. */
111 void ldisc_update(int echo, int edit) {}
113 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
114 static char appname[] = "PuTTY";
119 int guess_width, guess_height;
122 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
124 winsock_ver = MAKEWORD(1, 1);
125 if (WSAStartup(winsock_ver, &wsadata)) {
126 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
127 MB_OK | MB_ICONEXCLAMATION);
130 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
131 MessageBox(NULL, "WinSock version is incompatible with 1.1",
132 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
136 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
139 InitCommonControls();
141 /* Ensure a Maximize setting in Explorer doesn't maximise the
146 * Process the command line.
151 default_protocol = DEFAULT_PROTOCOL;
152 default_port = DEFAULT_PORT;
153 cfg.logtype = LGTYP_NONE;
155 do_defaults(NULL, &cfg);
158 while (*p && isspace(*p)) p++;
161 * Process command line options first. Yes, this can be
162 * done better, and it will be as soon as I have the
166 char *q = p + strcspn(p, " \t");
169 tolower(p[0]) == 's' &&
170 tolower(p[1]) == 's' &&
171 tolower(p[2]) == 'h') {
172 default_protocol = cfg.protocol = PROT_SSH;
173 default_port = cfg.port = 22;
174 } else if (q == p + 7 &&
175 tolower(p[0]) == 'c' &&
176 tolower(p[1]) == 'l' &&
177 tolower(p[2]) == 'e' &&
178 tolower(p[3]) == 'a' &&
179 tolower(p[4]) == 'n' &&
180 tolower(p[5]) == 'u' &&
181 tolower(p[6]) == 'p') {
183 * `putty -cleanup'. Remove all registry entries
184 * associated with PuTTY, and also find and delete
185 * the random seed file.
188 "This procedure will remove ALL Registry\n"
189 "entries associated with PuTTY, and will\n"
190 "also remove the PuTTY random seed file.\n"
192 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
193 "SESSIONS. Are you really sure you want\n"
196 MB_YESNO | MB_ICONWARNING) == IDYES) {
201 p = q + strspn(q, " \t");
205 * An initial @ means to activate a saved session.
209 while (i > 1 && isspace(p[i-1]))
212 do_defaults (p+1, &cfg);
213 if (!*cfg.host && !do_config()) {
217 } else if (*p == '&') {
219 * An initial & means we've been given a command line
220 * containing the hex value of a HANDLE for a file
221 * mapping object, which we must then extract as a
226 if (sscanf(p+1, "%p", &filemap) == 1 &&
227 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
228 0, 0, sizeof(Config))) != NULL) {
231 CloseHandle(filemap);
232 } else if (!do_config()) {
239 * If the hostname starts with "telnet:", set the
240 * protocol to Telnet and process the string as a
243 if (!strncmp(q, "telnet:", 7)) {
247 if (q[0] == '/' && q[1] == '/')
249 cfg.protocol = PROT_TELNET;
251 while (*p && *p != ':' && *p != '/') p++;
259 strncpy (cfg.host, q, sizeof(cfg.host)-1);
260 cfg.host[sizeof(cfg.host)-1] = '\0';
262 while (*p && !isspace(*p)) p++;
265 strncpy (cfg.host, q, sizeof(cfg.host)-1);
266 cfg.host[sizeof(cfg.host)-1] = '\0';
267 while (*p && isspace(*p)) p++;
280 /* See if host is of the form user@host */
281 if (cfg.host[0] != '\0') {
282 char *atsign = strchr(cfg.host, '@');
283 /* Make sure we're not overflowing the user field */
285 if (atsign-cfg.host < sizeof cfg.username) {
286 strncpy (cfg.username, cfg.host, atsign-cfg.host);
287 cfg.username[atsign-cfg.host] = '\0';
289 memmove(cfg.host, atsign+1, 1+strlen(atsign+1));
295 * Select protocol. This is farmed out into a table in a
296 * separate file to enable an ssh-free variant.
301 for (i = 0; backends[i].backend != NULL; i++)
302 if (backends[i].protocol == cfg.protocol) {
303 back = backends[i].backend;
307 MessageBox(NULL, "Unsupported protocol number found",
308 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
314 /* Check for invalid Port number (i.e. zero) */
316 MessageBox(NULL, "Invalid Port Number",
317 "PuTTY Internal Error", MB_OK |MB_ICONEXCLAMATION);
324 wndclass.lpfnWndProc = WndProc;
325 wndclass.cbClsExtra = 0;
326 wndclass.cbWndExtra = 0;
327 wndclass.hInstance = inst;
328 wndclass.hIcon = LoadIcon (inst,
329 MAKEINTRESOURCE(IDI_MAINICON));
330 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
331 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
332 wndclass.lpszMenuName = NULL;
333 wndclass.lpszClassName = appname;
335 RegisterClass (&wndclass);
340 savelines = cfg.savelines;
346 * Guess some defaults for the window size. This all gets
347 * updated later, so we don't really care too much. However, we
348 * do want the font width/height guesses to correspond to a
349 * large font rather than a small one...
356 term_size (cfg.height, cfg.width, cfg.savelines);
357 guess_width = extra_width + font_width * cols;
358 guess_height = extra_height + font_height * rows;
361 HWND w = GetDesktopWindow();
362 GetWindowRect (w, &r);
363 if (guess_width > r.right - r.left)
364 guess_width = r.right - r.left;
365 if (guess_height > r.bottom - r.top)
366 guess_height = r.bottom - r.top;
370 int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
372 if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
373 if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
374 if (cfg.alwaysontop) exwinmode = WS_EX_TOPMOST;
375 hwnd = CreateWindowEx (exwinmode, appname, appname,
376 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
377 guess_width, guess_height,
378 NULL, NULL, inst, NULL);
382 * Initialise the fonts, simultaneously correcting the guesses
383 * for font_{width,height}.
385 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
390 * Correct the guesses for extra_{width,height}.
394 GetWindowRect (hwnd, &wr);
395 GetClientRect (hwnd, &cr);
396 extra_width = wr.right - wr.left - cr.right + cr.left;
397 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
401 * Resize the window, now we know what size we _really_ want it
404 guess_width = extra_width + font_width * cols;
405 guess_height = extra_height + font_height * rows;
406 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
407 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
408 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
411 * Set up a caret bitmap, with no content.
415 int size = (font_width+15)/16 * 2 * font_height;
416 bits = smalloc(size);
417 memset(bits, 0, size);
418 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
421 CreateCaret(hwnd, caretbm, font_width, font_height);
424 * Initialise the scroll bar.
429 si.cbSize = sizeof(si);
430 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
435 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
439 * Start up the telnet connection.
443 char msg[1024], *title;
446 error = back->init (cfg.host, cfg.port, &realhost);
448 sprintf(msg, "Unable to open connection to\n"
450 "%s", cfg.host, error);
451 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
454 window_name = icon_name = NULL;
456 title = cfg.wintitle;
458 sprintf(msg, "%s - PuTTY", realhost);
465 session_closed = FALSE;
468 * Set up the input and output buffers.
471 outbuf_reap = outbuf_head = 0;
474 * Prepare the mouse handler.
476 lastact = MA_NOTHING;
477 lastbtn = MB_NOTHING;
478 dbltime = GetDoubleClickTime();
481 * Set up the session-control options on the system menu.
484 HMENU m = GetSystemMenu (hwnd, FALSE);
488 AppendMenu (m, MF_SEPARATOR, 0, 0);
489 if (cfg.protocol == PROT_TELNET) {
491 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
492 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
493 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
494 AppendMenu (p, MF_SEPARATOR, 0, 0);
495 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
496 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
497 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
498 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
499 AppendMenu (p, MF_SEPARATOR, 0, 0);
500 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
501 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
502 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
503 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
504 AppendMenu (p, MF_SEPARATOR, 0, 0);
505 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
506 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
507 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
508 AppendMenu (m, MF_SEPARATOR, 0, 0);
510 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
511 AppendMenu (m, MF_SEPARATOR, 0, 0);
512 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
513 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
516 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
517 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
518 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
519 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
520 AppendMenu (m, MF_SEPARATOR, 0, 0);
521 AppendMenu (m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
522 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
523 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
524 AppendMenu (m, MF_SEPARATOR, 0, 0);
525 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
529 * Finally show the window!
531 ShowWindow (hwnd, show);
534 * Open the initial log file if there is one.
539 * Set the palette up.
545 has_focus = (GetForegroundWindow() == hwnd);
548 if (GetMessage (&msg, NULL, 0, 0) == 1)
550 int timer_id = 0, long_timer = 0;
552 while (msg.message != WM_QUIT) {
553 /* Sometimes DispatchMessage calls routines that use their own
554 * GetMessage loop, setup this timer so we get some control back.
556 * Also call term_update() from the timer so that if the host
557 * is sending data flat out we still do redraws.
559 if(timer_id && long_timer) {
560 KillTimer(hwnd, timer_id);
561 long_timer = timer_id = 0;
564 timer_id = SetTimer(hwnd, 1, 20, NULL);
565 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
566 DispatchMessage (&msg);
568 /* Make sure we blink everything that needs it. */
571 /* Send the paste buffer if there's anything to send */
574 /* If there's nothing new in the queue then we can do everything
575 * we've delayed, reading the socket, writing, and repainting
578 if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
581 if (pending_netevent) {
582 enact_pending_netevent();
584 /* Force the cursor blink on */
587 if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
591 /* Okay there is now nothing to do so we make sure the screen is
592 * completely up to date then tell windows to call us in a little
596 KillTimer(hwnd, timer_id);
605 /* Hmm, term_update didn't want to do an update too soon ... */
606 timer_id = SetTimer(hwnd, 1, 50, NULL);
608 timer_id = SetTimer(hwnd, 1, 59500, NULL);
610 timer_id = SetTimer(hwnd, 1, 100, NULL);
613 /* There's no point rescanning everything in the message queue
614 * so we do an apparently unnecessary wait here
617 if (GetMessage (&msg, NULL, 0, 0) != 1)
629 DeleteObject(fonts[i]);
636 if (cfg.protocol == PROT_SSH) {
647 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
649 char *do_select(SOCKET skt, int startup) {
653 events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE;
658 return "do_select(): internal error (hwnd==NULL)";
659 if (WSAAsyncSelect (skt, hwnd, msg, events) == SOCKET_ERROR) {
660 switch (WSAGetLastError()) {
661 case WSAENETDOWN: return "Network is down";
662 default: return "WSAAsyncSelect(): unknown error";
669 * Print a message box and close the connection.
671 void connection_fatal(char *fmt, ...) {
676 vsprintf(stuff, fmt, ap);
678 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
679 if (cfg.close_on_exit == COE_ALWAYS)
682 session_closed = TRUE;
683 SetWindowText (hwnd, "PuTTY (inactive)");
688 * Actually do the job requested by a WM_NETEVENT
690 static void enact_pending_netevent(void) {
691 static int reentering = 0;
692 extern int select_result(WPARAM, LPARAM);
696 return; /* don't unpend the pending */
698 pending_netevent = FALSE;
701 ret = select_result (pend_netevent_wParam, pend_netevent_lParam);
704 if (ret == 0 && !session_closed) {
705 /* Abnormal exits will already have set session_closed and taken
706 * appropriate action. */
707 if (cfg.close_on_exit == COE_ALWAYS ||
708 cfg.close_on_exit == COE_NORMAL)
711 session_closed = TRUE;
712 SetWindowText (hwnd, "PuTTY (inactive)");
713 MessageBox(hwnd, "Connection closed by remote host",
714 "PuTTY", MB_OK | MB_ICONINFORMATION);
720 * Copy the colour palette from the configuration data into defpal.
721 * This is non-trivial because the colour indices are different.
723 static void cfgtopalette(void) {
725 static const int ww[] = {
726 6, 7, 8, 9, 10, 11, 12, 13,
727 14, 15, 16, 17, 18, 19, 20, 21,
728 0, 1, 2, 3, 4, 4, 5, 5
731 for (i=0; i<24; i++) {
733 defpal[i].rgbtRed = cfg.colours[w][0];
734 defpal[i].rgbtGreen = cfg.colours[w][1];
735 defpal[i].rgbtBlue = cfg.colours[w][2];
740 * Set up the colour palette.
742 static void init_palette(void) {
744 HDC hdc = GetDC (hwnd);
746 if (cfg.try_palette &&
747 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
748 logpal = smalloc(sizeof(*logpal)
749 - sizeof(logpal->palPalEntry)
750 + NCOLOURS * sizeof(PALETTEENTRY));
751 logpal->palVersion = 0x300;
752 logpal->palNumEntries = NCOLOURS;
753 for (i = 0; i < NCOLOURS; i++) {
754 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
755 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
756 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
757 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
759 pal = CreatePalette (logpal);
761 SelectPalette (hdc, pal, FALSE);
762 RealizePalette (hdc);
763 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
767 ReleaseDC (hwnd, hdc);
770 for (i=0; i<NCOLOURS; i++)
771 colours[i] = PALETTERGB(defpal[i].rgbtRed,
775 for(i=0; i<NCOLOURS; i++)
776 colours[i] = RGB(defpal[i].rgbtRed,
782 * Initialise all the fonts we will need. There may be as many as
783 * eight or as few as one. We also:
785 * - check the font width and height, correcting our guesses if
788 * - verify that the bold font is the same width as the ordinary
789 * one, and engage shadow bolding if not.
791 * - verify that the underlined font is the same width as the
792 * ordinary one (manual underlining by means of line drawing can
793 * be done in a pinch).
795 static void init_fonts(int pick_width) {
800 int fw_dontcare, fw_bold;
809 if (cfg.fontisbold) {
810 fw_dontcare = FW_BOLD;
813 fw_dontcare = FW_DONTCARE;
819 font_height = cfg.fontheight;
820 if (font_height > 0) {
821 font_height = -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
823 font_width = pick_width;
826 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
827 c, OUT_DEFAULT_PRECIS, \
828 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
829 FIXED_PITCH | FF_DONTCARE, cfg.font)
831 if (cfg.vtmode != VT_OEMONLY) {
832 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
834 SelectObject (hdc, fonts[FONT_NORMAL]);
835 GetTextMetrics(hdc, &tm);
836 font_height = tm.tmHeight;
837 font_width = tm.tmAveCharWidth;
839 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
842 * Some fonts, e.g. 9-pt Courier, draw their underlines
843 * outside their character cell. We successfully prevent
844 * screen corruption by clipping the text output, but then
845 * we lose the underline completely. Here we try to work
846 * out whether this is such a font, and if it is, we set a
847 * flag that causes underlines to be drawn by hand.
849 * Having tried other more sophisticated approaches (such
850 * as examining the TEXTMETRIC structure or requesting the
851 * height of a string), I think we'll do this the brute
852 * force way: we create a small bitmap, draw an underlined
853 * space on it, and test to see whether any pixels are
854 * foreground-coloured. (Since we expect the underline to
855 * go all the way across the character cell, we only search
856 * down a single column of the bitmap, half way across.)
860 HBITMAP und_bm, und_oldbm;
864 und_dc = CreateCompatibleDC(hdc);
865 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
866 und_oldbm = SelectObject(und_dc, und_bm);
867 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
868 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
869 SetTextColor (und_dc, RGB(255,255,255));
870 SetBkColor (und_dc, RGB(0,0,0));
871 SetBkMode (und_dc, OPAQUE);
872 ExtTextOut (und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
874 for (i = 0; i < font_height; i++) {
875 c = GetPixel(und_dc, font_width/2, i);
879 SelectObject(und_dc, und_oldbm);
880 DeleteObject(und_bm);
882 font_needs_hand_underlining = !gotit;
885 if (bold_mode == BOLD_FONT) {
886 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
887 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
890 if (cfg.vtmode == VT_OEMANSI) {
891 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
892 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
894 if (bold_mode == BOLD_FONT) {
895 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
896 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
902 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
904 SelectObject (hdc, fonts[FONT_OEM]);
905 GetTextMetrics(hdc, &tm);
906 font_height = tm.tmHeight;
907 font_width = tm.tmAveCharWidth;
909 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
911 if (bold_mode == BOLD_FONT) {
912 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
913 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
918 descent = tm.tmAscent + 1;
919 if (descent >= font_height)
920 descent = font_height - 1;
921 firstchar = tm.tmFirstChar;
923 for (i=0; i<8; i++) {
925 if (SelectObject (hdc, fonts[i]) &&
926 GetTextMetrics(hdc, &tm) )
927 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
933 ReleaseDC (hwnd, hdc);
935 /* ... This is wrong in OEM only mode */
936 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
937 (bold_mode == BOLD_FONT &&
938 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
940 DeleteObject (fonts[FONT_UNDERLINE]);
941 if (bold_mode == BOLD_FONT)
942 DeleteObject (fonts[FONT_BOLDUND]);
945 if (bold_mode == BOLD_FONT &&
946 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
947 bold_mode = BOLD_SHADOW;
948 DeleteObject (fonts[FONT_BOLD]);
949 if (und_mode == UND_FONT)
950 DeleteObject (fonts[FONT_BOLDUND]);
954 /* With the fascist font painting it doesn't matter if the linedraw font
955 * isn't exactly the right size anymore so we don't have to check this.
957 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
958 if( cfg.fontcharset == OEM_CHARSET )
960 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
961 "different sizes. Using OEM-only mode instead",
962 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
963 cfg.vtmode = VT_OEMONLY;
965 else if( firstchar < ' ' )
967 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
968 "different sizes. Using XTerm mode instead",
969 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
970 cfg.vtmode = VT_XWINDOWS;
974 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
975 "different sizes. Using ISO8859-1 mode instead",
976 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
977 cfg.vtmode = VT_POORMAN;
982 DeleteObject (fonts[i]);
988 void request_resize (int w, int h, int refont) {
991 /* If the window is maximized supress resizing attempts */
992 if(IsZoomed(hwnd)) return;
995 /* Don't do this in OEMANSI, you may get disable messages */
996 if (refont && w != cols && (cols==80 || cols==132)
997 && cfg.vtmode != VT_OEMANSI)
999 if (refont && w != cols && (cols==80 || cols==132))
1002 /* If font width too big for screen should we shrink the font more ? */
1004 font_width = ((font_width*cols+w/2)/w);
1011 DeleteObject(fonts[i]);
1013 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1014 und_mode = UND_FONT;
1015 init_fonts(font_width);
1019 static int first_time = 1;
1025 /* Get the size of the screen */
1026 if (GetClientRect(GetDesktopWindow(),&ss))
1027 /* first_time = 0 */;
1028 else { first_time = 2; break; }
1030 /* Make sure the values are sane */
1031 width = (ss.right-ss.left-extra_width ) / font_width;
1032 height = (ss.bottom-ss.top-extra_height ) / font_height;
1034 if (w>width) w=width;
1035 if (h>height) h=height;
1041 width = extra_width + font_width * w;
1042 height = extra_height + font_height * h;
1044 SetWindowPos (hwnd, NULL, 0, 0, width, height,
1045 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1046 SWP_NOMOVE | SWP_NOZORDER);
1049 static void click (Mouse_Button b, int x, int y) {
1050 int thistime = GetMessageTime();
1052 if (lastbtn == b && thistime - lasttime < dbltime) {
1053 lastact = (lastact == MA_CLICK ? MA_2CLK :
1054 lastact == MA_2CLK ? MA_3CLK :
1055 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1060 if (lastact != MA_NOTHING)
1061 term_mouse (b, lastact, x, y);
1062 lasttime = thistime;
1065 static void show_mouseptr(int show) {
1066 static int cursor_visible = 1;
1067 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1069 if (cursor_visible && !show)
1071 else if (!cursor_visible && show)
1073 cursor_visible = show;
1076 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
1077 WPARAM wParam, LPARAM lParam) {
1079 static int ignore_size = FALSE;
1080 static int ignore_clip = FALSE;
1081 static int just_reconfigged = FALSE;
1082 static int resizing = FALSE;
1083 static int need_backend_resize = FALSE;
1087 if (pending_netevent)
1088 enact_pending_netevent();
1095 if (cfg.ping_interval > 0)
1099 if (now-last_movement > cfg.ping_interval)
1101 back->special(TS_PING);
1102 last_movement = now;
1110 if (!cfg.warn_on_close || session_closed ||
1111 MessageBox(hwnd, "Are you sure you want to close this session?",
1112 "PuTTY Exit Confirmation",
1113 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1114 DestroyWindow(hwnd);
1118 PostQuitMessage (0);
1121 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1133 PROCESS_INFORMATION pi;
1134 HANDLE filemap = NULL;
1136 if (wParam == IDM_DUPSESS) {
1138 * Allocate a file-mapping memory chunk for the
1141 SECURITY_ATTRIBUTES sa;
1144 sa.nLength = sizeof(sa);
1145 sa.lpSecurityDescriptor = NULL;
1146 sa.bInheritHandle = TRUE;
1147 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
1154 p = (Config *)MapViewOfFile(filemap,
1156 0, 0, sizeof(Config));
1158 *p = cfg; /* structure copy */
1162 sprintf(c, "putty &%p", filemap);
1164 } else if (wParam == IDM_SAVEDSESS) {
1165 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
1166 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1168 cl = NULL; /* not a very important failure mode */
1170 sprintf(cl, "putty @%s", session);
1176 GetModuleFileName (NULL, b, sizeof(b)-1);
1178 si.lpReserved = NULL;
1179 si.lpDesktop = NULL;
1183 si.lpReserved2 = NULL;
1184 CreateProcess (b, cl, NULL, NULL, TRUE,
1185 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1188 CloseHandle(filemap);
1195 int prev_alwaysontop = cfg.alwaysontop;
1196 char oldlogfile[FILENAME_MAX];
1198 int need_setwpos = FALSE;
1199 int old_fwidth, old_fheight;
1201 strcpy(oldlogfile, cfg.logfilename);
1202 oldlogtype = cfg.logtype;
1205 old_fwidth = font_width;
1206 old_fheight = font_height;
1207 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1209 if (!do_reconfig(hwnd))
1212 if (strcmp(oldlogfile, cfg.logfilename) ||
1213 oldlogtype != cfg.logtype) {
1214 logfclose(); /* reset logging */
1218 just_reconfigged = TRUE;
1223 DeleteObject(fonts[i]);
1225 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1226 und_mode = UND_FONT;
1230 * Flush the line discipline's edit buffer in the
1231 * case where local editing has just been disabled.
1233 ldisc_send(NULL, 0);
1241 /* Enable or disable the scroll bar, etc */
1243 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1244 LONG nexflag, exflag = GetWindowLong(hwnd, GWL_EXSTYLE);
1247 if (cfg.alwaysontop != prev_alwaysontop) {
1248 if (cfg.alwaysontop) {
1249 nexflag = WS_EX_TOPMOST;
1250 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1251 SWP_NOMOVE | SWP_NOSIZE);
1254 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1255 SWP_NOMOVE | SWP_NOSIZE);
1260 if (cfg.scrollbar) nflg |= WS_VSCROLL;
1261 else nflg &= ~WS_VSCROLL;
1263 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1265 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1267 if (nflg != flag || nexflag != exflag)
1272 SetWindowLong(hwnd, GWL_STYLE, nflg);
1273 if (nexflag != exflag)
1274 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1276 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1278 SetWindowPos(hwnd, NULL, 0,0,0,0,
1279 SWP_NOACTIVATE|SWP_NOCOPYBITS|
1280 SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|
1283 GetWindowRect (hwnd, &wr);
1284 GetClientRect (hwnd, &cr);
1285 extra_width = wr.right - wr.left - cr.right + cr.left;
1286 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1290 if (cfg.height != rows ||
1291 cfg.width != cols ||
1292 old_fwidth != font_width ||
1293 old_fheight != font_height ||
1294 cfg.savelines != savelines)
1295 need_setwpos = TRUE;
1296 term_size(cfg.height, cfg.width, cfg.savelines);
1297 InvalidateRect(hwnd, NULL, TRUE);
1300 SetWindowPos (hwnd, NULL, 0, 0,
1301 extra_width + font_width * cfg.width,
1302 extra_height + font_height * cfg.height,
1303 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1304 SWP_NOMOVE | SWP_NOZORDER);
1306 set_title(cfg.wintitle);
1307 if (IsIconic(hwnd)) {
1308 SetWindowText (hwnd,
1309 cfg.win_name_always ? window_name : icon_name);
1322 case IDM_TEL_AYT: back->special (TS_AYT); break;
1323 case IDM_TEL_BRK: back->special (TS_BRK); break;
1324 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1325 case IDM_TEL_EC: back->special (TS_EC); break;
1326 case IDM_TEL_EL: back->special (TS_EL); break;
1327 case IDM_TEL_GA: back->special (TS_GA); break;
1328 case IDM_TEL_NOP: back->special (TS_NOP); break;
1329 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1330 case IDM_TEL_AO: back->special (TS_AO); break;
1331 case IDM_TEL_IP: back->special (TS_IP); break;
1332 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1333 case IDM_TEL_EOR: back->special (TS_EOR); break;
1334 case IDM_TEL_EOF: back->special (TS_EOF); break;
1339 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1340 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1345 #define X_POS(l) ((int)(short)LOWORD(l))
1346 #define Y_POS(l) ((int)(short)HIWORD(l))
1348 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1349 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1351 case WM_LBUTTONDOWN:
1353 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1354 TO_CHR_Y(Y_POS(lParam)));
1359 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1360 TO_CHR_Y(Y_POS(lParam)));
1363 case WM_MBUTTONDOWN:
1366 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1367 TO_CHR_X(X_POS(lParam)),
1368 TO_CHR_Y(Y_POS(lParam)));
1372 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1373 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1374 TO_CHR_Y(Y_POS(lParam)));
1377 case WM_RBUTTONDOWN:
1380 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1381 TO_CHR_X(X_POS(lParam)),
1382 TO_CHR_Y(Y_POS(lParam)));
1386 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1387 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1388 TO_CHR_Y(Y_POS(lParam)));
1394 * Add the mouse position and message time to the random
1397 noise_ultralight(lParam);
1399 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1401 if (wParam & MK_LBUTTON)
1403 else if (wParam & MK_MBUTTON)
1404 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1406 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1407 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1408 TO_CHR_Y(Y_POS(lParam)));
1411 case WM_NCMOUSEMOVE:
1413 noise_ultralight(lParam);
1415 case WM_IGNORE_CLIP:
1416 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1418 case WM_DESTROYCLIPBOARD:
1421 ignore_clip = FALSE;
1427 hdc = BeginPaint (hwnd, &p);
1429 SelectPalette (hdc, pal, TRUE);
1430 RealizePalette (hdc);
1432 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1433 p.rcPaint.right, p.rcPaint.bottom);
1434 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1435 SelectObject (hdc, GetStockObject(WHITE_PEN));
1436 EndPaint (hwnd, &p);
1441 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1442 * but the only one that's likely to try to overload us is FD_READ.
1443 * This means buffering just one is fine.
1445 if (pending_netevent)
1446 enact_pending_netevent();
1448 pending_netevent = TRUE;
1449 pend_netevent_wParam=wParam;
1450 pend_netevent_lParam=lParam;
1451 time(&last_movement);
1455 CreateCaret(hwnd, caretbm, font_width, font_height);
1468 case WM_IGNORE_SIZE:
1469 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1471 case WM_ENTERSIZEMOVE:
1474 need_backend_resize = FALSE;
1476 case WM_EXITSIZEMOVE:
1479 if (need_backend_resize)
1484 int width, height, w, h, ew, eh;
1485 LPRECT r = (LPRECT)lParam;
1487 width = r->right - r->left - extra_width;
1488 height = r->bottom - r->top - extra_height;
1489 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1490 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1491 UpdateSizeTip(hwnd, w, h);
1492 ew = width - w * font_width;
1493 eh = height - h * font_height;
1495 if (wParam == WMSZ_LEFT ||
1496 wParam == WMSZ_BOTTOMLEFT ||
1497 wParam == WMSZ_TOPLEFT)
1503 if (wParam == WMSZ_TOP ||
1504 wParam == WMSZ_TOPRIGHT ||
1505 wParam == WMSZ_TOPLEFT)
1515 /* break; (never reached) */
1517 if (wParam == SIZE_MINIMIZED) {
1518 SetWindowText (hwnd,
1519 cfg.win_name_always ? window_name : icon_name);
1522 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1523 SetWindowText (hwnd, window_name);
1525 int width, height, w, h;
1526 #if 0 /* we have fixed this using WM_SIZING now */
1530 width = LOWORD(lParam);
1531 height = HIWORD(lParam);
1532 w = width / font_width; if (w < 1) w = 1;
1533 h = height / font_height; if (h < 1) h = 1;
1534 #if 0 /* we have fixed this using WM_SIZING now */
1535 ew = width - w * font_width;
1536 eh = height - h * font_height;
1537 if (ew != 0 || eh != 0) {
1539 GetWindowRect (hwnd, &r);
1540 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1541 SetWindowPos (hwnd, NULL, 0, 0,
1542 r.right - r.left - ew, r.bottom - r.top - eh,
1543 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1546 if (w != cols || h != rows || just_reconfigged) {
1548 term_size (h, w, cfg.savelines);
1550 * Don't call back->size in mid-resize. (To prevent
1551 * massive numbers of resize events getting sent
1552 * down the connection during an NT opaque drag.)
1557 need_backend_resize = TRUE;
1558 just_reconfigged = FALSE;
1561 ignore_size = FALSE;
1564 switch (LOWORD(wParam)) {
1565 case SB_BOTTOM: term_scroll(-1, 0); break;
1566 case SB_TOP: term_scroll(+1, 0); break;
1567 case SB_LINEDOWN: term_scroll (0, +1); break;
1568 case SB_LINEUP: term_scroll (0, -1); break;
1569 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1570 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1571 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1572 term_scroll (1, HIWORD(wParam)); break;
1575 case WM_PALETTECHANGED:
1576 if ((HWND) wParam != hwnd && pal != NULL) {
1577 HDC hdc = get_ctx();
1579 if (RealizePalette (hdc) > 0)
1585 case WM_QUERYNEWPALETTE:
1587 HDC hdc = get_ctx();
1589 if (RealizePalette (hdc) > 0)
1601 * Add the scan code and keypress timing to the random
1604 noise_ultralight(lParam);
1607 * We don't do TranslateMessage since it disassociates the
1608 * resulting CHAR message from the KEYDOWN that sparked it,
1609 * which we occasionally don't want. Instead, we process
1610 * KEYDOWN, and call the Win32 translator functions so that
1611 * we get the translations under _our_ control.
1614 unsigned char buf[20];
1617 if (wParam==VK_PROCESSKEY) {
1620 m.message = WM_KEYDOWN;
1622 m.lParam = lParam & 0xdfff;
1623 TranslateMessage(&m);
1625 len = TranslateKey (message, wParam, lParam, buf);
1627 return DefWindowProc (hwnd, message, wParam, lParam);
1628 ldisc_send (buf, len);
1637 unsigned char buf[2];
1640 buf[0] = wParam >> 8;
1641 ldisc_send (buf, 2);
1646 * Nevertheless, we are prepared to deal with WM_CHAR
1647 * messages, should they crop up. So if someone wants to
1648 * post the things to us as part of a macro manoeuvre,
1649 * we're ready to cope.
1652 char c = xlat_kbd2tty((unsigned char)wParam);
1658 return DefWindowProc (hwnd, message, wParam, lParam);
1662 * Move the system caret. (We maintain one, even though it's
1663 * invisible, for the benefit of blind people: apparently some
1664 * helper software tracks the system caret, so we should arrange to
1667 void sys_cursor(int x, int y) {
1668 SetCaretPos(x * font_width, y * font_height);
1672 * Draw a line of text in the window, at given character
1673 * coordinates, in given attributes.
1675 * We are allowed to fiddle with the contents of `text'.
1677 void do_text (Context ctx, int x, int y, char *text, int len,
1678 unsigned long attr, int lattr) {
1680 int nfg, nbg, nfont;
1683 int force_manual_underline = 0;
1684 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1685 static int *IpDx = 0, IpDxLEN = 0;;
1687 if (len>IpDxLEN || IpDx[0] != fnt_width) {
1691 IpDx = smalloc((len+16)*sizeof(int));
1694 for(i=0; i<IpDxLEN; i++)
1695 IpDx[i] = fnt_width;
1701 if ((attr & ATTR_ACTCURS) && cfg.cursor_type == 0) {
1702 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1703 attr ^= ATTR_CUR_XOR;
1707 if (cfg.vtmode == VT_OEMONLY)
1711 * Map high-half characters in order to approximate ISO using
1712 * OEM character set. No characters are missing if the OEM codepage
1715 if (nfont & FONT_OEM) {
1717 for (i=0; i<len; i++)
1718 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1720 /* This is CP850 ... perfect translation */
1721 static const char oemhighhalf[] =
1722 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1723 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1724 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1725 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1726 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1727 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1728 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1729 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1730 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1731 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1732 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1733 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1736 /* This is CP437 ... junk translation */
1737 static const unsigned char oemhighhalf[] = {
1738 0x20, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1739 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1740 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1741 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1742 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1743 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1744 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1745 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1746 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1747 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1748 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1749 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1752 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1756 if (attr & ATTR_LINEDRW) {
1759 static const char poorman[] =
1760 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1763 static const char oemmap_437[] =
1764 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1765 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1768 static const char oemmap_850[] =
1769 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1770 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1772 /* Poor windows font ... eg: windows courier */
1773 static const char oemmap[] =
1774 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1775 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1778 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1779 * VT100 line drawing chars; everything else stays normal.
1781 * Actually '_' maps to space too, but that's done before.
1783 switch (cfg.vtmode) {
1785 for (i=0; i<len; i++)
1786 if (text[i] >= '\x60' && text[i] <= '\x7E')
1787 text[i] += '\x01' - '\x60';
1790 /* Make sure we actually have an OEM font */
1791 if (fonts[nfont|FONT_OEM]) {
1794 for (i=0; i<len; i++)
1795 if (text[i] >= '\x60' && text[i] <= '\x7E')
1796 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1800 for (i=0; i<len; i++)
1801 if (text[i] >= '\x60' && text[i] <= '\x7E')
1802 text[i] = poorman[(unsigned char)text[i] - 0x60];
1807 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1808 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1809 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1811 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1812 nfont |= FONT_UNDERLINE;
1815 if (nfont&FONT_UNDERLINE)
1816 force_manual_underline = 1;
1817 /* Don't do the same for manual bold, it could be bad news. */
1819 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1821 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1822 force_manual_underline = 1;
1823 if (attr & ATTR_REVERSE) {
1824 t = nfg; nfg = nbg; nbg = t;
1826 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1828 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1832 SelectObject (hdc, fonts[nfont]);
1833 SetTextColor (hdc, fg);
1834 SetBkColor (hdc, bg);
1835 SetBkMode (hdc, OPAQUE);
1838 line_box.right = x+fnt_width*len;
1839 line_box.bottom = y+font_height;
1840 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1841 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1842 SetBkMode (hdc, TRANSPARENT);
1844 /* GRR: This draws the character outside it's box and can leave
1845 * 'droppings' even with the clip box! I suppose I could loop it
1846 * one character at a time ... yuk.
1848 * Or ... I could do a test print with "W", and use +1 or -1 for this
1849 * shift depending on if the leftmost column is blank...
1851 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1853 if (force_manual_underline ||
1854 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1856 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1857 MoveToEx (hdc, x, y+descent, NULL);
1858 LineTo (hdc, x+len*fnt_width, y+descent);
1859 oldpen = SelectObject (hdc, oldpen);
1860 DeleteObject (oldpen);
1862 if ((attr & ATTR_PASCURS) && cfg.cursor_type == 0) {
1865 pts[0].x = pts[1].x = pts[4].x = x;
1866 pts[2].x = pts[3].x = x+fnt_width-1;
1867 pts[0].y = pts[3].y = pts[4].y = y;
1868 pts[1].y = pts[2].y = y+font_height-1;
1869 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1870 Polyline (hdc, pts, 5);
1871 oldpen = SelectObject (hdc, oldpen);
1872 DeleteObject (oldpen);
1874 if ((attr & (ATTR_ACTCURS | ATTR_PASCURS)) && cfg.cursor_type != 0) {
1875 int startx, starty, dx, dy, length, i;
1876 if (cfg.cursor_type == 1) {
1877 startx = x; starty = y+descent;
1878 dx = 1; dy = 0; length = fnt_width;
1881 if (attr & ATTR_RIGHTCURS)
1882 xadjust = fnt_width-1;
1883 startx = x+xadjust; starty = y;
1884 dx = 0; dy = 1; length = font_height;
1886 if (attr & ATTR_ACTCURS) {
1888 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1889 MoveToEx (hdc, startx, starty, NULL);
1890 LineTo (hdc, startx+dx*length, starty+dy*length);
1891 oldpen = SelectObject (hdc, oldpen);
1892 DeleteObject (oldpen);
1894 for (i = 0; i < length; i++) {
1896 SetPixel(hdc, startx, starty, colours[23]);
1898 startx += dx; starty += dy;
1904 static int check_compose(int first, int second) {
1906 static char * composetbl[] = {
1907 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1908 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1909 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1910 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1911 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1912 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1913 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1914 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1915 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1916 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1917 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1918 "\"uü", "'yý", "htþ", "\"yÿ",
1922 static int recurse = 0;
1925 for(c=composetbl; *c; c++) {
1926 if( (*c)[0] == first && (*c)[1] == second)
1928 return (*c)[2] & 0xFF;
1935 nc = check_compose(second, first);
1937 nc = check_compose(toupper(first), toupper(second));
1939 nc = check_compose(toupper(second), toupper(first));
1947 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1948 * codes. Returns number of bytes used or zero to drop the message
1949 * or -1 to forward the message to windows.
1951 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
1952 unsigned char *output) {
1954 int scan, left_alt = 0, key_down, shift_state;
1956 unsigned char * p = output;
1957 static int alt_state = 0;
1959 HKL kbd_layout = GetKeyboardLayout(0);
1961 static WORD keys[3];
1962 static int compose_char = 0;
1963 static WPARAM compose_key = 0;
1965 r = GetKeyboardState(keystate);
1966 if (!r) memset(keystate, 0, sizeof(keystate));
1970 { /* Tell us all about key events */
1971 static BYTE oldstate[256];
1972 static int first = 1;
1975 if(first) memcpy(oldstate, keystate, sizeof(oldstate));
1978 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT) {
1980 } else if ((HIWORD(lParam)&KF_UP) && scan==(HIWORD(lParam) & 0xFF) ) {
1984 if (wParam >= VK_F1 && wParam <= VK_F20 )
1985 debug(("K_F%d", wParam+1-VK_F1));
1988 case VK_SHIFT: debug(("SHIFT")); break;
1989 case VK_CONTROL: debug(("CTRL")); break;
1990 case VK_MENU: debug(("ALT")); break;
1991 default: debug(("VK_%02x", wParam));
1993 if(message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
1995 debug((", S%02x", scan=(HIWORD(lParam) & 0xFF) ));
1997 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
1998 if (ch>=' ' && ch<='~') debug((", '%c'", ch));
1999 else if (ch) debug((", $%02x", ch));
2001 if (keys[0]) debug((", KB0=%02x", keys[0]));
2002 if (keys[1]) debug((", KB1=%02x", keys[1]));
2003 if (keys[2]) debug((", KB2=%02x", keys[2]));
2005 if ( (keystate[VK_SHIFT]&0x80)!=0) debug((", S"));
2006 if ( (keystate[VK_CONTROL]&0x80)!=0) debug((", C"));
2007 if ( (HIWORD(lParam)&KF_EXTENDED) ) debug((", E"));
2008 if ( (HIWORD(lParam)&KF_UP) ) debug((", U"));
2011 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
2013 else if ( (HIWORD(lParam)&KF_UP) )
2014 oldstate[wParam&0xFF] ^= 0x80;
2016 oldstate[wParam&0xFF] ^= 0x81;
2018 for(ch=0; ch<256; ch++)
2019 if (oldstate[ch] != keystate[ch])
2020 debug((", M%02x=%02x", ch, keystate[ch]));
2022 memcpy(oldstate, keystate, sizeof(oldstate));
2026 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED)) {
2027 keystate[VK_RMENU] = keystate[VK_MENU];
2031 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2032 if ( (cfg.funky_type == 3 ||
2033 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2034 && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
2036 wParam = VK_EXECUTE;
2038 /* UnToggle NUMLock */
2039 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
2040 keystate[VK_NUMLOCK] ^= 1;
2043 /* And write back the 'adjusted' state */
2044 SetKeyboardState (keystate);
2047 /* Disable Auto repeat if required */
2048 if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
2051 if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
2054 key_down = ((HIWORD(lParam)&KF_UP)==0);
2056 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2057 if (left_alt && (keystate[VK_CONTROL]&0x80)) {
2058 if (cfg.ctrlaltkeys)
2059 keystate[VK_MENU] = 0;
2061 keystate[VK_RMENU] = 0x80;
2066 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2067 shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
2068 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
2070 /* Note if AltGr was pressed and if it was used as a compose key */
2071 if (!compose_state) {
2072 compose_key = 0x100;
2073 if (cfg.compose_key) {
2074 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
2075 compose_key = wParam;
2077 if (wParam == VK_APPS)
2078 compose_key = wParam;
2081 if (wParam == compose_key)
2083 if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
2085 else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
2090 else if (compose_state==1 && wParam != VK_CONTROL)
2094 * Record that we pressed key so the scroll window can be reset, but
2095 * be careful to avoid Shift-UP/Down
2097 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
2101 /* Make sure we're not pasting */
2102 if (key_down) term_nopaste();
2104 if (compose_state>1 && left_alt) compose_state = 0;
2106 /* Sanitize the number pad if not using a PC NumPad */
2107 if( left_alt || (app_keypad_keys && !cfg.no_applic_k
2108 && cfg.funky_type != 2)
2109 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state )
2111 if ((HIWORD(lParam)&KF_EXTENDED) == 0)
2116 case VK_INSERT: nParam = VK_NUMPAD0; break;
2117 case VK_END: nParam = VK_NUMPAD1; break;
2118 case VK_DOWN: nParam = VK_NUMPAD2; break;
2119 case VK_NEXT: nParam = VK_NUMPAD3; break;
2120 case VK_LEFT: nParam = VK_NUMPAD4; break;
2121 case VK_CLEAR: nParam = VK_NUMPAD5; break;
2122 case VK_RIGHT: nParam = VK_NUMPAD6; break;
2123 case VK_HOME: nParam = VK_NUMPAD7; break;
2124 case VK_UP: nParam = VK_NUMPAD8; break;
2125 case VK_PRIOR: nParam = VK_NUMPAD9; break;
2126 case VK_DELETE: nParam = VK_DECIMAL; break;
2130 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
2136 /* If a key is pressed and AltGr is not active */
2137 if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
2139 /* Okay, prepare for most alts then ...*/
2140 if (left_alt) *p++ = '\033';
2142 /* Lets see if it's a pattern we know all about ... */
2143 if (wParam == VK_PRIOR && shift_state == 1) {
2144 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2147 if (wParam == VK_NEXT && shift_state == 1) {
2148 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2151 if (wParam == VK_INSERT && shift_state == 1) {
2152 term_mouse (MB_PASTE, MA_CLICK, 0, 0);
2153 term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
2156 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2159 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2161 PostMessage(hwnd, WM_CHAR, ' ', 0);
2162 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2165 /* Control-Numlock for app-keypad mode switch */
2166 if (wParam == VK_PAUSE && shift_state == 2) {
2167 app_keypad_keys ^= 1;
2171 /* Nethack keypad */
2172 if (cfg.nethack_keypad && !left_alt) {
2174 case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
2175 case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
2176 case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
2177 case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
2178 case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
2179 case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
2180 case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
2181 case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
2182 case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
2186 /* Application Keypad */
2190 if ( cfg.funky_type == 3 ||
2191 ( cfg.funky_type <= 1 &&
2192 app_keypad_keys && !cfg.no_applic_k)) switch(wParam) {
2193 case VK_EXECUTE: xkey = 'P'; break;
2194 case VK_DIVIDE: xkey = 'Q'; break;
2195 case VK_MULTIPLY:xkey = 'R'; break;
2196 case VK_SUBTRACT:xkey = 'S'; break;
2198 if(app_keypad_keys && !cfg.no_applic_k) switch(wParam) {
2199 case VK_NUMPAD0: xkey = 'p'; break;
2200 case VK_NUMPAD1: xkey = 'q'; break;
2201 case VK_NUMPAD2: xkey = 'r'; break;
2202 case VK_NUMPAD3: xkey = 's'; break;
2203 case VK_NUMPAD4: xkey = 't'; break;
2204 case VK_NUMPAD5: xkey = 'u'; break;
2205 case VK_NUMPAD6: xkey = 'v'; break;
2206 case VK_NUMPAD7: xkey = 'w'; break;
2207 case VK_NUMPAD8: xkey = 'x'; break;
2208 case VK_NUMPAD9: xkey = 'y'; break;
2210 case VK_DECIMAL: xkey = 'n'; break;
2211 case VK_ADD: if(cfg.funky_type==2) {
2212 if(shift_state) xkey = 'l';
2214 } else if(shift_state) xkey = 'm';
2218 case VK_DIVIDE: if(cfg.funky_type==2) xkey = 'o'; break;
2219 case VK_MULTIPLY:if(cfg.funky_type==2) xkey = 'j'; break;
2220 case VK_SUBTRACT:if(cfg.funky_type==2) xkey = 'm'; break;
2223 if (HIWORD(lParam)&KF_EXTENDED)
2231 if (xkey>='P' && xkey<='S')
2232 p += sprintf((char *)p, "\x1B%c", xkey);
2234 p += sprintf((char *)p, "\x1B?%c", xkey);
2237 p += sprintf((char *)p, "\x1BO%c", xkey);
2242 if (wParam == VK_BACK && shift_state == 0 ) /* Backspace */
2244 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2247 if (wParam == VK_TAB && shift_state == 1 ) /* Shift tab */
2249 *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
2251 if (wParam == VK_SPACE && shift_state == 2 ) /* Ctrl-Space */
2253 *p++ = 0; return p - output;
2255 if (wParam == VK_SPACE && shift_state == 3 ) /* Ctrl-Shift-Space */
2257 *p++ = 160; return p - output;
2259 if (wParam == VK_CANCEL && shift_state == 2 ) /* Ctrl-Break */
2261 *p++ = 3; return p - output;
2263 if (wParam == VK_PAUSE) /* Break/Pause */
2265 *p++ = 26; *p++ = 0; return -2;
2267 /* Control-2 to Control-8 are special */
2268 if (shift_state == 2 && wParam >= '2' && wParam <= '8')
2270 *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
2273 if (shift_state == 2 && wParam == 0xBD) {
2277 if (shift_state == 2 && wParam == 0xDF) {
2281 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2282 *p++ = '\r'; *p++ = '\n';
2287 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2288 * for integer decimal nn.)
2290 * We also deal with the weird ones here. Linux VCs replace F1
2291 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2292 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2297 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
2298 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
2299 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
2300 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
2301 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
2302 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
2303 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
2304 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
2305 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
2306 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
2307 case VK_F11: code = 23; break;
2308 case VK_F12: code = 24; break;
2309 case VK_F13: code = 25; break;
2310 case VK_F14: code = 26; break;
2311 case VK_F15: code = 28; break;
2312 case VK_F16: code = 29; break;
2313 case VK_F17: code = 31; break;
2314 case VK_F18: code = 32; break;
2315 case VK_F19: code = 33; break;
2316 case VK_F20: code = 34; break;
2317 case VK_HOME: code = 1; break;
2318 case VK_INSERT: code = 2; break;
2319 case VK_DELETE: code = 3; break;
2320 case VK_END: code = 4; break;
2321 case VK_PRIOR: code = 5; break;
2322 case VK_NEXT: code = 6; break;
2324 /* Reorder edit keys to physical order */
2325 if (cfg.funky_type == 3 && code <= 6 ) code = "\0\2\1\4\5\3\6"[code];
2327 if (vt52_mode && code > 0 && code <= 6) {
2328 p += sprintf((char *)p, "\x1B%c", " HLMEIG"[code]);
2332 if (cfg.funky_type == 5 && code >= 11 && code <= 24) {
2333 p += sprintf((char *)p, "\x1B[%c", code + 'M' - 11);
2336 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
2338 if (code>15) offt++; if (code>21) offt++;
2340 p += sprintf((char *)p, "\x1B%c", code + 'P' - 11 - offt);
2342 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11 - offt);
2345 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2346 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
2349 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2351 p += sprintf((char *)p, "\x1B%c", code + 'P' - 11);
2353 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
2356 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2357 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
2361 p += sprintf((char *)p, "\x1B[%d~", code);
2366 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2367 * some reason seems to send VK_CLEAR to Windows...).
2372 case VK_UP: xkey = 'A'; break;
2373 case VK_DOWN: xkey = 'B'; break;
2374 case VK_RIGHT: xkey = 'C'; break;
2375 case VK_LEFT: xkey = 'D'; break;
2376 case VK_CLEAR: xkey = 'G'; break;
2381 p += sprintf((char *)p, "\x1B%c", xkey);
2382 else if (app_cursor_keys && !cfg.no_applic_c)
2383 p += sprintf((char *)p, "\x1BO%c", xkey);
2385 p += sprintf((char *)p, "\x1B[%c", xkey);
2391 * Finally, deal with Return ourselves. (Win95 seems to
2392 * foul it up when Alt is pressed, for some reason.)
2394 if (wParam == VK_RETURN) /* Return */
2401 /* Okay we've done everything interesting; let windows deal with
2402 * the boring stuff */
2404 BOOL capsOn=keystate[VK_CAPITAL] !=0;
2406 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2407 if(cfg.xlat_capslockcyr)
2408 keystate[VK_CAPITAL] = 0;
2410 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
2416 unsigned char ch = (unsigned char)keys[i];
2418 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
2423 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
2427 if ((nc=check_compose(compose_char,ch)) == -1)
2429 MessageBeep(MB_ICONHAND);
2432 *p++ = xlat_kbd2tty((unsigned char)nc);
2438 if( left_alt && key_down ) *p++ = '\033';
2444 ch = xlat_latkbd2win(ch);
2445 *p++ = xlat_kbd2tty(ch);
2449 /* This is so the ALT-Numpad and dead keys work correctly. */
2454 /* If we're definitly not building up an ALT-54321 then clear it */
2455 if (!left_alt) keys[0] = 0;
2458 /* ALT alone may or may not want to bring up the System menu */
2459 if (wParam == VK_MENU) {
2461 if (message == WM_SYSKEYDOWN)
2463 else if (message == WM_SYSKEYUP && alt_state)
2464 PostMessage(hwnd, WM_CHAR, ' ', 0);
2465 if (message == WM_SYSKEYUP)
2475 void set_title (char *title) {
2476 sfree (window_name);
2477 window_name = smalloc(1+strlen(title));
2478 strcpy (window_name, title);
2479 if (cfg.win_name_always || !IsIconic(hwnd))
2480 SetWindowText (hwnd, title);
2483 void set_icon (char *title) {
2485 icon_name = smalloc(1+strlen(title));
2486 strcpy (icon_name, title);
2487 if (!cfg.win_name_always && IsIconic(hwnd))
2488 SetWindowText (hwnd, title);
2491 void set_sbar (int total, int start, int page) {
2494 if (!cfg.scrollbar) return;
2496 si.cbSize = sizeof(si);
2497 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2499 si.nMax = total - 1;
2503 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2506 Context get_ctx(void) {
2511 SelectPalette (hdc, pal, FALSE);
2517 void free_ctx (Context ctx) {
2518 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2519 ReleaseDC (hwnd, ctx);
2522 static void real_palette_set (int n, int r, int g, int b) {
2524 logpal->palPalEntry[n].peRed = r;
2525 logpal->palPalEntry[n].peGreen = g;
2526 logpal->palPalEntry[n].peBlue = b;
2527 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2528 colours[n] = PALETTERGB(r, g, b);
2529 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2531 colours[n] = RGB(r, g, b);
2534 void palette_set (int n, int r, int g, int b) {
2535 static const int first[21] = {
2536 0, 2, 4, 6, 8, 10, 12, 14,
2537 1, 3, 5, 7, 9, 11, 13, 15,
2540 real_palette_set (first[n], r, g, b);
2542 real_palette_set (first[n]+1, r, g, b);
2544 HDC hdc = get_ctx();
2545 UnrealizeObject (pal);
2546 RealizePalette (hdc);
2551 void palette_reset (void) {
2554 for (i = 0; i < NCOLOURS; i++) {
2556 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2557 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2558 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2559 logpal->palPalEntry[i].peFlags = 0;
2560 colours[i] = PALETTERGB(defpal[i].rgbtRed,
2561 defpal[i].rgbtGreen,
2562 defpal[i].rgbtBlue);
2564 colours[i] = RGB(defpal[i].rgbtRed,
2565 defpal[i].rgbtGreen,
2566 defpal[i].rgbtBlue);
2571 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2573 RealizePalette (hdc);
2578 void write_clip (void *data, int len, int must_deselect) {
2582 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2585 lock = GlobalLock (clipdata);
2588 memcpy (lock, data, len);
2589 ((unsigned char *) lock) [len] = 0;
2590 GlobalUnlock (clipdata);
2593 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2595 if (OpenClipboard (hwnd)) {
2597 SetClipboardData (CF_TEXT, clipdata);
2600 GlobalFree (clipdata);
2603 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2606 void get_clip (void **p, int *len) {
2607 static HGLOBAL clipdata = NULL;
2611 GlobalUnlock (clipdata);
2615 if (OpenClipboard (NULL)) {
2616 clipdata = GetClipboardData (CF_TEXT);
2619 *p = GlobalLock (clipdata);
2633 * Move `lines' lines from position `from' to position `to' in the
2636 void optimised_move (int to, int from, int lines) {
2640 min = (to < from ? to : from);
2641 max = to + from - min;
2643 r.left = 0; r.right = cols * font_width;
2644 r.top = min * font_height; r.bottom = (max+lines) * font_height;
2645 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2649 * Print a message box and perform a fatal exit.
2651 void fatalbox(char *fmt, ...) {
2656 vsprintf(stuff, fmt, ap);
2658 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2665 void beep(int mode) {
2666 if (mode == BELL_DEFAULT) {
2668 * For MessageBeep style bells, we want to be careful of
2669 * timing, because they don't have the nice property of
2670 * PlaySound bells that each one cancels the previous
2671 * active one. So we limit the rate to one per 50ms or so.
2673 static long lastbeep = 0;
2676 now = GetTickCount();
2677 beepdiff = now - lastbeep;
2678 if (beepdiff >= 0 && beepdiff < 50)
2682 } else if (mode == BELL_WAVEFILE) {
2683 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
2684 char buf[sizeof(cfg.bell_wavefile)+80];
2685 sprintf(buf, "Unable to play sound file\n%s\n"
2686 "Using default sound instead", cfg.bell_wavefile);
2687 MessageBox(hwnd, buf, "PuTTY Sound Error", MB_OK | MB_ICONEXCLAMATION);
2688 cfg.beep = BELL_DEFAULT;