16 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
22 #define IDM_SHOWLOG 0x0010
23 #define IDM_NEWSESS 0x0020
24 #define IDM_DUPSESS 0x0030
25 #define IDM_RECONF 0x0040
26 #define IDM_CLRSB 0x0050
27 #define IDM_RESET 0x0060
28 #define IDM_TEL_AYT 0x0070
29 #define IDM_TEL_BRK 0x0080
30 #define IDM_TEL_SYNCH 0x0090
31 #define IDM_TEL_EC 0x00a0
32 #define IDM_TEL_EL 0x00b0
33 #define IDM_TEL_GA 0x00c0
34 #define IDM_TEL_NOP 0x00d0
35 #define IDM_TEL_ABORT 0x00e0
36 #define IDM_TEL_AO 0x00f0
37 #define IDM_TEL_IP 0x0100
38 #define IDM_TEL_SUSP 0x0110
39 #define IDM_TEL_EOR 0x0120
40 #define IDM_TEL_EOF 0x0130
41 #define IDM_ABOUT 0x0140
42 #define IDM_SAVEDSESS 0x0150
43 #define IDM_COPYALL 0x0160
45 #define IDM_SESSLGP 0x0250 /* log type printable */
46 #define IDM_SESSLGA 0x0260 /* log type all chars */
47 #define IDM_SESSLGE 0x0270 /* log end */
48 #define IDM_SAVED_MIN 0x1000
49 #define IDM_SAVED_MAX 0x2000
51 #define WM_IGNORE_SIZE (WM_XUSER + 1)
52 #define WM_IGNORE_CLIP (WM_XUSER + 2)
54 /* Needed for Chinese support and apparently not always defined. */
56 #define VK_PROCESSKEY 0xE5
59 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
60 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
61 static void cfgtopalette(void);
62 static void init_palette(void);
63 static void init_fonts(int);
65 static int extra_width, extra_height;
67 static int pending_netevent = 0;
68 static WPARAM pend_netevent_wParam = 0;
69 static LPARAM pend_netevent_lParam = 0;
70 static void enact_pending_netevent(void);
72 static time_t last_movement = 0;
76 #define FONT_UNDERLINE 2
77 #define FONT_BOLDUND 3
79 #define FONT_OEMBOLD 5
80 #define FONT_OEMBOLDUND 6
82 static HFONT fonts[8];
83 static int font_needs_hand_underlining;
85 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
93 static COLORREF colours[NCOLOURS];
95 static LPLOGPALETTE logpal;
96 static RGBTRIPLE defpal[NCOLOURS];
100 static HBITMAP caretbm;
102 static int dbltime, lasttime, lastact;
103 static Mouse_Button lastbtn;
105 static char *window_name, *icon_name;
107 static int compose_state = 0;
109 /* Dummy routine, only required in plink. */
110 void ldisc_update(int echo, int edit) {}
112 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
113 static char appname[] = "PuTTY";
118 int guess_width, guess_height;
121 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
123 winsock_ver = MAKEWORD(1, 1);
124 if (WSAStartup(winsock_ver, &wsadata)) {
125 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
126 MB_OK | MB_ICONEXCLAMATION);
129 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
130 MessageBox(NULL, "WinSock version is incompatible with 1.1",
131 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
135 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
138 InitCommonControls();
140 /* Ensure a Maximize setting in Explorer doesn't maximise the
145 * Process the command line.
150 default_protocol = DEFAULT_PROTOCOL;
151 default_port = DEFAULT_PORT;
152 cfg.logtype = LGTYP_NONE;
154 do_defaults(NULL, &cfg);
157 while (*p && isspace(*p)) p++;
160 * Process command line options first. Yes, this can be
161 * done better, and it will be as soon as I have the
165 char *q = p + strcspn(p, " \t");
168 tolower(p[0]) == 's' &&
169 tolower(p[1]) == 's' &&
170 tolower(p[2]) == 'h') {
171 default_protocol = cfg.protocol = PROT_SSH;
172 default_port = cfg.port = 22;
173 } else if (q == p + 7 &&
174 tolower(p[0]) == 'c' &&
175 tolower(p[1]) == 'l' &&
176 tolower(p[2]) == 'e' &&
177 tolower(p[3]) == 'a' &&
178 tolower(p[4]) == 'n' &&
179 tolower(p[5]) == 'u' &&
180 tolower(p[6]) == 'p') {
182 * `putty -cleanup'. Remove all registry entries
183 * associated with PuTTY, and also find and delete
184 * the random seed file.
187 "This procedure will remove ALL Registry\n"
188 "entries associated with PuTTY, and will\n"
189 "also remove the PuTTY random seed file.\n"
191 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
192 "SESSIONS. Are you really sure you want\n"
195 MB_YESNO | MB_ICONWARNING) == IDYES) {
200 p = q + strspn(q, " \t");
204 * An initial @ means to activate a saved session.
208 while (i > 1 && isspace(p[i-1]))
211 do_defaults (p+1, &cfg);
212 if (!*cfg.host && !do_config()) {
216 } else if (*p == '&') {
218 * An initial & means we've been given a command line
219 * containing the hex value of a HANDLE for a file
220 * mapping object, which we must then extract as a
225 if (sscanf(p+1, "%p", &filemap) == 1 &&
226 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
227 0, 0, sizeof(Config))) != NULL) {
230 CloseHandle(filemap);
231 } else if (!do_config()) {
238 * If the hostname starts with "telnet:", set the
239 * protocol to Telnet and process the string as a
242 if (!strncmp(q, "telnet:", 7)) {
246 if (q[0] == '/' && q[1] == '/')
248 cfg.protocol = PROT_TELNET;
250 while (*p && *p != ':' && *p != '/') p++;
258 strncpy (cfg.host, q, sizeof(cfg.host)-1);
259 cfg.host[sizeof(cfg.host)-1] = '\0';
261 while (*p && !isspace(*p)) p++;
264 strncpy (cfg.host, q, sizeof(cfg.host)-1);
265 cfg.host[sizeof(cfg.host)-1] = '\0';
266 while (*p && isspace(*p)) p++;
279 /* See if host is of the form user@host */
280 if (cfg.host[0] != '\0') {
281 char *atsign = strchr(cfg.host, '@');
282 /* Make sure we're not overflowing the user field */
284 if (atsign-cfg.host < sizeof cfg.username) {
285 strncpy (cfg.username, cfg.host, atsign-cfg.host);
286 cfg.username[atsign-cfg.host] = '\0';
288 memmove(cfg.host, atsign+1, 1+strlen(atsign+1));
294 * Select protocol. This is farmed out into a table in a
295 * separate file to enable an ssh-free variant.
300 for (i = 0; backends[i].backend != NULL; i++)
301 if (backends[i].protocol == cfg.protocol) {
302 back = backends[i].backend;
306 MessageBox(NULL, "Unsupported protocol number found",
307 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
313 /* Check for invalid Port number (i.e. zero) */
315 MessageBox(NULL, "Invalid Port Number",
316 "PuTTY Internal Error", MB_OK |MB_ICONEXCLAMATION);
323 wndclass.lpfnWndProc = WndProc;
324 wndclass.cbClsExtra = 0;
325 wndclass.cbWndExtra = 0;
326 wndclass.hInstance = inst;
327 wndclass.hIcon = LoadIcon (inst,
328 MAKEINTRESOURCE(IDI_MAINICON));
329 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
330 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
331 wndclass.lpszMenuName = NULL;
332 wndclass.lpszClassName = appname;
334 RegisterClass (&wndclass);
339 savelines = cfg.savelines;
345 * Guess some defaults for the window size. This all gets
346 * updated later, so we don't really care too much. However, we
347 * do want the font width/height guesses to correspond to a
348 * large font rather than a small one...
355 term_size (cfg.height, cfg.width, cfg.savelines);
356 guess_width = extra_width + font_width * cols;
357 guess_height = extra_height + font_height * rows;
360 HWND w = GetDesktopWindow();
361 GetWindowRect (w, &r);
362 if (guess_width > r.right - r.left)
363 guess_width = r.right - r.left;
364 if (guess_height > r.bottom - r.top)
365 guess_height = r.bottom - r.top;
369 int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
371 if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
372 if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
373 if (cfg.alwaysontop) exwinmode = WS_EX_TOPMOST;
374 hwnd = CreateWindowEx (exwinmode, appname, appname,
375 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
376 guess_width, guess_height,
377 NULL, NULL, inst, NULL);
381 * Initialise the fonts, simultaneously correcting the guesses
382 * for font_{width,height}.
384 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
389 * Correct the guesses for extra_{width,height}.
393 GetWindowRect (hwnd, &wr);
394 GetClientRect (hwnd, &cr);
395 extra_width = wr.right - wr.left - cr.right + cr.left;
396 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
400 * Resize the window, now we know what size we _really_ want it
403 guess_width = extra_width + font_width * cols;
404 guess_height = extra_height + font_height * rows;
405 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
406 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
407 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
410 * Set up a caret bitmap, with no content.
414 int size = (font_width+15)/16 * 2 * font_height;
415 bits = smalloc(size);
416 memset(bits, 0, size);
417 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
420 CreateCaret(hwnd, caretbm, font_width, font_height);
423 * Initialise the scroll bar.
428 si.cbSize = sizeof(si);
429 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
434 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
438 * Start up the telnet connection.
442 char msg[1024], *title;
445 error = back->init (cfg.host, cfg.port, &realhost);
447 sprintf(msg, "Unable to open connection to\n"
449 "%s", cfg.host, error);
450 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
453 window_name = icon_name = NULL;
455 title = cfg.wintitle;
457 sprintf(msg, "%s - PuTTY", realhost);
464 session_closed = FALSE;
467 * Set up the input and output buffers.
470 outbuf_reap = outbuf_head = 0;
473 * Prepare the mouse handler.
475 lastact = MA_NOTHING;
476 lastbtn = MB_NOTHING;
477 dbltime = GetDoubleClickTime();
480 * Set up the session-control options on the system menu.
483 HMENU m = GetSystemMenu (hwnd, FALSE);
487 AppendMenu (m, MF_SEPARATOR, 0, 0);
488 if (cfg.protocol == PROT_TELNET) {
490 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
491 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
492 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
493 AppendMenu (p, MF_SEPARATOR, 0, 0);
494 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
495 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
496 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
497 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
498 AppendMenu (p, MF_SEPARATOR, 0, 0);
499 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
500 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
501 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
502 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
503 AppendMenu (p, MF_SEPARATOR, 0, 0);
504 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
505 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
506 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
507 AppendMenu (m, MF_SEPARATOR, 0, 0);
509 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
510 AppendMenu (m, MF_SEPARATOR, 0, 0);
511 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
512 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
515 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
516 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
517 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
518 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
519 AppendMenu (m, MF_SEPARATOR, 0, 0);
520 AppendMenu (m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
521 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
522 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
523 AppendMenu (m, MF_SEPARATOR, 0, 0);
524 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
528 * Finally show the window!
530 ShowWindow (hwnd, show);
533 * Open the initial log file if there is one.
538 * Set the palette up.
544 has_focus = (GetForegroundWindow() == hwnd);
547 if (GetMessage (&msg, NULL, 0, 0) == 1)
549 int timer_id = 0, long_timer = 0;
551 while (msg.message != WM_QUIT) {
552 /* Sometimes DispatchMessage calls routines that use their own
553 * GetMessage loop, setup this timer so we get some control back.
555 * Also call term_update() from the timer so that if the host
556 * is sending data flat out we still do redraws.
558 if(timer_id && long_timer) {
559 KillTimer(hwnd, timer_id);
560 long_timer = timer_id = 0;
563 timer_id = SetTimer(hwnd, 1, 20, NULL);
564 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
565 DispatchMessage (&msg);
567 /* Make sure we blink everything that needs it. */
570 /* Send the paste buffer if there's anything to send */
573 /* If there's nothing new in the queue then we can do everything
574 * we've delayed, reading the socket, writing, and repainting
577 if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
580 if (pending_netevent) {
581 enact_pending_netevent();
583 /* Force the cursor blink on */
586 if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
590 /* Okay there is now nothing to do so we make sure the screen is
591 * completely up to date then tell windows to call us in a little
595 KillTimer(hwnd, timer_id);
604 /* Hmm, term_update didn't want to do an update too soon ... */
605 timer_id = SetTimer(hwnd, 1, 50, NULL);
607 timer_id = SetTimer(hwnd, 1, 59500, NULL);
609 timer_id = SetTimer(hwnd, 1, 100, NULL);
612 /* There's no point rescanning everything in the message queue
613 * so we do an apparently unnecessary wait here
616 if (GetMessage (&msg, NULL, 0, 0) != 1)
628 DeleteObject(fonts[i]);
635 if (cfg.protocol == PROT_SSH) {
646 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
648 char *do_select(SOCKET skt, int startup) {
652 events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE;
657 return "do_select(): internal error (hwnd==NULL)";
658 if (WSAAsyncSelect (skt, hwnd, msg, events) == SOCKET_ERROR) {
659 switch (WSAGetLastError()) {
660 case WSAENETDOWN: return "Network is down";
661 default: return "WSAAsyncSelect(): unknown error";
668 * Print a message box and close the connection.
670 void connection_fatal(char *fmt, ...) {
675 vsprintf(stuff, fmt, ap);
677 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
678 if (cfg.close_on_exit == COE_ALWAYS)
681 session_closed = TRUE;
682 SetWindowText (hwnd, "PuTTY (inactive)");
687 * Actually do the job requested by a WM_NETEVENT
689 static void enact_pending_netevent(void) {
690 static int reentering = 0;
691 extern int select_result(WPARAM, LPARAM);
695 return; /* don't unpend the pending */
697 pending_netevent = FALSE;
700 ret = select_result (pend_netevent_wParam, pend_netevent_lParam);
703 if (ret == 0 && !session_closed) {
704 /* Abnormal exits will already have set session_closed and taken
705 * appropriate action. */
706 if (cfg.close_on_exit == COE_ALWAYS ||
707 cfg.close_on_exit == COE_NORMAL)
710 session_closed = TRUE;
711 SetWindowText (hwnd, "PuTTY (inactive)");
712 MessageBox(hwnd, "Connection closed by remote host",
713 "PuTTY", MB_OK | MB_ICONINFORMATION);
719 * Copy the colour palette from the configuration data into defpal.
720 * This is non-trivial because the colour indices are different.
722 static void cfgtopalette(void) {
724 static const int ww[] = {
725 6, 7, 8, 9, 10, 11, 12, 13,
726 14, 15, 16, 17, 18, 19, 20, 21,
727 0, 1, 2, 3, 4, 4, 5, 5
730 for (i=0; i<24; i++) {
732 defpal[i].rgbtRed = cfg.colours[w][0];
733 defpal[i].rgbtGreen = cfg.colours[w][1];
734 defpal[i].rgbtBlue = cfg.colours[w][2];
739 * Set up the colour palette.
741 static void init_palette(void) {
743 HDC hdc = GetDC (hwnd);
745 if (cfg.try_palette &&
746 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
747 logpal = smalloc(sizeof(*logpal)
748 - sizeof(logpal->palPalEntry)
749 + NCOLOURS * sizeof(PALETTEENTRY));
750 logpal->palVersion = 0x300;
751 logpal->palNumEntries = NCOLOURS;
752 for (i = 0; i < NCOLOURS; i++) {
753 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
754 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
755 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
756 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
758 pal = CreatePalette (logpal);
760 SelectPalette (hdc, pal, FALSE);
761 RealizePalette (hdc);
762 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
766 ReleaseDC (hwnd, hdc);
769 for (i=0; i<NCOLOURS; i++)
770 colours[i] = PALETTERGB(defpal[i].rgbtRed,
774 for(i=0; i<NCOLOURS; i++)
775 colours[i] = RGB(defpal[i].rgbtRed,
781 * Initialise all the fonts we will need. There may be as many as
782 * eight or as few as one. We also:
784 * - check the font width and height, correcting our guesses if
787 * - verify that the bold font is the same width as the ordinary
788 * one, and engage shadow bolding if not.
790 * - verify that the underlined font is the same width as the
791 * ordinary one (manual underlining by means of line drawing can
792 * be done in a pinch).
794 static void init_fonts(int pick_width) {
799 int fw_dontcare, fw_bold;
808 if (cfg.fontisbold) {
809 fw_dontcare = FW_BOLD;
812 fw_dontcare = FW_DONTCARE;
818 font_height = cfg.fontheight;
819 if (font_height > 0) {
820 font_height = -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
822 font_width = pick_width;
825 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
826 c, OUT_DEFAULT_PRECIS, \
827 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
828 FIXED_PITCH | FF_DONTCARE, cfg.font)
830 if (cfg.vtmode != VT_OEMONLY) {
831 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
833 SelectObject (hdc, fonts[FONT_NORMAL]);
834 GetTextMetrics(hdc, &tm);
835 font_height = tm.tmHeight;
836 font_width = tm.tmAveCharWidth;
838 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
841 * Some fonts, e.g. 9-pt Courier, draw their underlines
842 * outside their character cell. We successfully prevent
843 * screen corruption by clipping the text output, but then
844 * we lose the underline completely. Here we try to work
845 * out whether this is such a font, and if it is, we set a
846 * flag that causes underlines to be drawn by hand.
848 * Having tried other more sophisticated approaches (such
849 * as examining the TEXTMETRIC structure or requesting the
850 * height of a string), I think we'll do this the brute
851 * force way: we create a small bitmap, draw an underlined
852 * space on it, and test to see whether any pixels are
853 * foreground-coloured. (Since we expect the underline to
854 * go all the way across the character cell, we only search
855 * down a single column of the bitmap, half way across.)
859 HBITMAP und_bm, und_oldbm;
863 und_dc = CreateCompatibleDC(hdc);
864 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
865 und_oldbm = SelectObject(und_dc, und_bm);
866 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
867 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
868 SetTextColor (und_dc, RGB(255,255,255));
869 SetBkColor (und_dc, RGB(0,0,0));
870 SetBkMode (und_dc, OPAQUE);
871 ExtTextOut (und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
873 for (i = 0; i < font_height; i++) {
874 c = GetPixel(und_dc, font_width/2, i);
878 SelectObject(und_dc, und_oldbm);
879 DeleteObject(und_bm);
881 font_needs_hand_underlining = !gotit;
884 if (bold_mode == BOLD_FONT) {
885 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
886 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
889 if (cfg.vtmode == VT_OEMANSI) {
890 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
891 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
893 if (bold_mode == BOLD_FONT) {
894 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
895 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
901 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
903 SelectObject (hdc, fonts[FONT_OEM]);
904 GetTextMetrics(hdc, &tm);
905 font_height = tm.tmHeight;
906 font_width = tm.tmAveCharWidth;
908 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
910 if (bold_mode == BOLD_FONT) {
911 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
912 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
917 descent = tm.tmAscent + 1;
918 if (descent >= font_height)
919 descent = font_height - 1;
920 firstchar = tm.tmFirstChar;
922 for (i=0; i<8; i++) {
924 if (SelectObject (hdc, fonts[i]) &&
925 GetTextMetrics(hdc, &tm) )
926 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
932 ReleaseDC (hwnd, hdc);
934 /* ... This is wrong in OEM only mode */
935 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
936 (bold_mode == BOLD_FONT &&
937 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
939 DeleteObject (fonts[FONT_UNDERLINE]);
940 if (bold_mode == BOLD_FONT)
941 DeleteObject (fonts[FONT_BOLDUND]);
944 if (bold_mode == BOLD_FONT &&
945 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
946 bold_mode = BOLD_SHADOW;
947 DeleteObject (fonts[FONT_BOLD]);
948 if (und_mode == UND_FONT)
949 DeleteObject (fonts[FONT_BOLDUND]);
953 /* With the fascist font painting it doesn't matter if the linedraw font
954 * isn't exactly the right size anymore so we don't have to check this.
956 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
957 if( cfg.fontcharset == OEM_CHARSET )
959 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
960 "different sizes. Using OEM-only mode instead",
961 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
962 cfg.vtmode = VT_OEMONLY;
964 else if( firstchar < ' ' )
966 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
967 "different sizes. Using XTerm mode instead",
968 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
969 cfg.vtmode = VT_XWINDOWS;
973 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
974 "different sizes. Using ISO8859-1 mode instead",
975 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
976 cfg.vtmode = VT_POORMAN;
981 DeleteObject (fonts[i]);
987 void request_resize (int w, int h, int refont) {
990 /* If the window is maximized supress resizing attempts */
991 if(IsZoomed(hwnd)) return;
994 /* Don't do this in OEMANSI, you may get disable messages */
995 if (refont && w != cols && (cols==80 || cols==132)
996 && cfg.vtmode != VT_OEMANSI)
998 if (refont && w != cols && (cols==80 || cols==132))
1001 /* If font width too big for screen should we shrink the font more ? */
1003 font_width = ((font_width*cols+w/2)/w);
1010 DeleteObject(fonts[i]);
1012 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1013 und_mode = UND_FONT;
1014 init_fonts(font_width);
1018 static int first_time = 1;
1024 /* Get the size of the screen */
1025 if (GetClientRect(GetDesktopWindow(),&ss))
1026 /* first_time = 0 */;
1027 else { first_time = 2; break; }
1029 /* Make sure the values are sane */
1030 width = (ss.right-ss.left-extra_width ) / font_width;
1031 height = (ss.bottom-ss.top-extra_height ) / font_height;
1033 if (w>width) w=width;
1034 if (h>height) h=height;
1040 width = extra_width + font_width * w;
1041 height = extra_height + font_height * h;
1043 SetWindowPos (hwnd, NULL, 0, 0, width, height,
1044 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1045 SWP_NOMOVE | SWP_NOZORDER);
1048 static void click (Mouse_Button b, int x, int y) {
1049 int thistime = GetMessageTime();
1051 if (lastbtn == b && thistime - lasttime < dbltime) {
1052 lastact = (lastact == MA_CLICK ? MA_2CLK :
1053 lastact == MA_2CLK ? MA_3CLK :
1054 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1059 if (lastact != MA_NOTHING)
1060 term_mouse (b, lastact, x, y);
1061 lasttime = thistime;
1064 static void show_mouseptr(int show) {
1065 static int cursor_visible = 1;
1066 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1068 if (cursor_visible && !show)
1070 else if (!cursor_visible && show)
1072 cursor_visible = show;
1075 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
1076 WPARAM wParam, LPARAM lParam) {
1078 static int ignore_size = FALSE;
1079 static int ignore_clip = FALSE;
1080 static int just_reconfigged = FALSE;
1081 static int resizing = FALSE;
1082 static int need_backend_resize = FALSE;
1086 if (pending_netevent)
1087 enact_pending_netevent();
1094 if (cfg.ping_interval > 0)
1098 if (now-last_movement > cfg.ping_interval)
1100 back->special(TS_PING);
1101 last_movement = now;
1109 if (!cfg.warn_on_close || session_closed ||
1110 MessageBox(hwnd, "Are you sure you want to close this session?",
1111 "PuTTY Exit Confirmation",
1112 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1113 DestroyWindow(hwnd);
1117 PostQuitMessage (0);
1120 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1132 PROCESS_INFORMATION pi;
1133 HANDLE filemap = NULL;
1135 if (wParam == IDM_DUPSESS) {
1137 * Allocate a file-mapping memory chunk for the
1140 SECURITY_ATTRIBUTES sa;
1143 sa.nLength = sizeof(sa);
1144 sa.lpSecurityDescriptor = NULL;
1145 sa.bInheritHandle = TRUE;
1146 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
1153 p = (Config *)MapViewOfFile(filemap,
1155 0, 0, sizeof(Config));
1157 *p = cfg; /* structure copy */
1161 sprintf(c, "putty &%p", filemap);
1163 } else if (wParam == IDM_SAVEDSESS) {
1164 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
1165 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1167 cl = NULL; /* not a very important failure mode */
1169 sprintf(cl, "putty @%s", session);
1175 GetModuleFileName (NULL, b, sizeof(b)-1);
1177 si.lpReserved = NULL;
1178 si.lpDesktop = NULL;
1182 si.lpReserved2 = NULL;
1183 CreateProcess (b, cl, NULL, NULL, TRUE,
1184 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1187 CloseHandle(filemap);
1194 int prev_alwaysontop = cfg.alwaysontop;
1195 char oldlogfile[FILENAME_MAX];
1197 int need_setwpos = FALSE;
1198 int old_fwidth, old_fheight;
1200 strcpy(oldlogfile, cfg.logfilename);
1201 oldlogtype = cfg.logtype;
1204 old_fwidth = font_width;
1205 old_fheight = font_height;
1206 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1208 if (!do_reconfig(hwnd))
1211 if (strcmp(oldlogfile, cfg.logfilename) ||
1212 oldlogtype != cfg.logtype) {
1213 logfclose(); /* reset logging */
1217 just_reconfigged = TRUE;
1222 DeleteObject(fonts[i]);
1224 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1225 und_mode = UND_FONT;
1229 * Flush the line discipline's edit buffer in the
1230 * case where local editing has just been disabled.
1232 ldisc_send(NULL, 0);
1240 /* Enable or disable the scroll bar, etc */
1242 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1243 LONG nexflag, exflag = GetWindowLong(hwnd, GWL_EXSTYLE);
1246 if (cfg.alwaysontop != prev_alwaysontop) {
1247 if (cfg.alwaysontop) {
1248 nexflag = WS_EX_TOPMOST;
1249 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1250 SWP_NOMOVE | SWP_NOSIZE);
1253 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1254 SWP_NOMOVE | SWP_NOSIZE);
1259 if (cfg.scrollbar) nflg |= WS_VSCROLL;
1260 else nflg &= ~WS_VSCROLL;
1262 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1264 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1266 if (nflg != flag || nexflag != exflag)
1271 SetWindowLong(hwnd, GWL_STYLE, nflg);
1272 if (nexflag != exflag)
1273 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1275 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1277 SetWindowPos(hwnd, NULL, 0,0,0,0,
1278 SWP_NOACTIVATE|SWP_NOCOPYBITS|
1279 SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|
1282 GetWindowRect (hwnd, &wr);
1283 GetClientRect (hwnd, &cr);
1284 extra_width = wr.right - wr.left - cr.right + cr.left;
1285 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1289 if (cfg.height != rows ||
1290 cfg.width != cols ||
1291 old_fwidth != font_width ||
1292 old_fheight != font_height ||
1293 cfg.savelines != savelines)
1294 need_setwpos = TRUE;
1295 term_size(cfg.height, cfg.width, cfg.savelines);
1296 InvalidateRect(hwnd, NULL, TRUE);
1299 SetWindowPos (hwnd, NULL, 0, 0,
1300 extra_width + font_width * cfg.width,
1301 extra_height + font_height * cfg.height,
1302 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1303 SWP_NOMOVE | SWP_NOZORDER);
1305 set_title(cfg.wintitle);
1306 if (IsIconic(hwnd)) {
1307 SetWindowText (hwnd,
1308 cfg.win_name_always ? window_name : icon_name);
1321 case IDM_TEL_AYT: back->special (TS_AYT); break;
1322 case IDM_TEL_BRK: back->special (TS_BRK); break;
1323 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1324 case IDM_TEL_EC: back->special (TS_EC); break;
1325 case IDM_TEL_EL: back->special (TS_EL); break;
1326 case IDM_TEL_GA: back->special (TS_GA); break;
1327 case IDM_TEL_NOP: back->special (TS_NOP); break;
1328 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1329 case IDM_TEL_AO: back->special (TS_AO); break;
1330 case IDM_TEL_IP: back->special (TS_IP); break;
1331 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1332 case IDM_TEL_EOR: back->special (TS_EOR); break;
1333 case IDM_TEL_EOF: back->special (TS_EOF); break;
1338 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1339 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1344 #define X_POS(l) ((int)(short)LOWORD(l))
1345 #define Y_POS(l) ((int)(short)HIWORD(l))
1347 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1348 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1350 case WM_LBUTTONDOWN:
1352 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1353 TO_CHR_Y(Y_POS(lParam)));
1358 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1359 TO_CHR_Y(Y_POS(lParam)));
1362 case WM_MBUTTONDOWN:
1365 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1366 TO_CHR_X(X_POS(lParam)),
1367 TO_CHR_Y(Y_POS(lParam)));
1371 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1372 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1373 TO_CHR_Y(Y_POS(lParam)));
1376 case WM_RBUTTONDOWN:
1379 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1380 TO_CHR_X(X_POS(lParam)),
1381 TO_CHR_Y(Y_POS(lParam)));
1385 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1386 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1387 TO_CHR_Y(Y_POS(lParam)));
1393 * Add the mouse position and message time to the random
1396 noise_ultralight(lParam);
1398 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1400 if (wParam & MK_LBUTTON)
1402 else if (wParam & MK_MBUTTON)
1403 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1405 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1406 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1407 TO_CHR_Y(Y_POS(lParam)));
1410 case WM_NCMOUSEMOVE:
1412 noise_ultralight(lParam);
1414 case WM_IGNORE_CLIP:
1415 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1417 case WM_DESTROYCLIPBOARD:
1420 ignore_clip = FALSE;
1426 hdc = BeginPaint (hwnd, &p);
1428 SelectPalette (hdc, pal, TRUE);
1429 RealizePalette (hdc);
1431 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1432 p.rcPaint.right, p.rcPaint.bottom);
1433 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1434 SelectObject (hdc, GetStockObject(WHITE_PEN));
1435 EndPaint (hwnd, &p);
1440 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1441 * but the only one that's likely to try to overload us is FD_READ.
1442 * This means buffering just one is fine.
1444 if (pending_netevent)
1445 enact_pending_netevent();
1447 pending_netevent = TRUE;
1448 pend_netevent_wParam=wParam;
1449 pend_netevent_lParam=lParam;
1450 time(&last_movement);
1454 CreateCaret(hwnd, caretbm, font_width, font_height);
1467 case WM_IGNORE_SIZE:
1468 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1470 case WM_ENTERSIZEMOVE:
1473 need_backend_resize = FALSE;
1475 case WM_EXITSIZEMOVE:
1478 if (need_backend_resize)
1483 int width, height, w, h, ew, eh;
1484 LPRECT r = (LPRECT)lParam;
1486 width = r->right - r->left - extra_width;
1487 height = r->bottom - r->top - extra_height;
1488 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1489 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1490 UpdateSizeTip(hwnd, w, h);
1491 ew = width - w * font_width;
1492 eh = height - h * font_height;
1494 if (wParam == WMSZ_LEFT ||
1495 wParam == WMSZ_BOTTOMLEFT ||
1496 wParam == WMSZ_TOPLEFT)
1502 if (wParam == WMSZ_TOP ||
1503 wParam == WMSZ_TOPRIGHT ||
1504 wParam == WMSZ_TOPLEFT)
1514 /* break; (never reached) */
1516 if (wParam == SIZE_MINIMIZED) {
1517 SetWindowText (hwnd,
1518 cfg.win_name_always ? window_name : icon_name);
1521 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1522 SetWindowText (hwnd, window_name);
1524 int width, height, w, h;
1525 #if 0 /* we have fixed this using WM_SIZING now */
1529 width = LOWORD(lParam);
1530 height = HIWORD(lParam);
1531 w = width / font_width; if (w < 1) w = 1;
1532 h = height / font_height; if (h < 1) h = 1;
1533 #if 0 /* we have fixed this using WM_SIZING now */
1534 ew = width - w * font_width;
1535 eh = height - h * font_height;
1536 if (ew != 0 || eh != 0) {
1538 GetWindowRect (hwnd, &r);
1539 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1540 SetWindowPos (hwnd, NULL, 0, 0,
1541 r.right - r.left - ew, r.bottom - r.top - eh,
1542 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1545 if (w != cols || h != rows || just_reconfigged) {
1547 term_size (h, w, cfg.savelines);
1549 * Don't call back->size in mid-resize. (To prevent
1550 * massive numbers of resize events getting sent
1551 * down the connection during an NT opaque drag.)
1556 need_backend_resize = TRUE;
1557 just_reconfigged = FALSE;
1560 ignore_size = FALSE;
1563 switch (LOWORD(wParam)) {
1564 case SB_BOTTOM: term_scroll(-1, 0); break;
1565 case SB_TOP: term_scroll(+1, 0); break;
1566 case SB_LINEDOWN: term_scroll (0, +1); break;
1567 case SB_LINEUP: term_scroll (0, -1); break;
1568 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1569 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1570 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1571 term_scroll (1, HIWORD(wParam)); break;
1574 case WM_PALETTECHANGED:
1575 if ((HWND) wParam != hwnd && pal != NULL) {
1576 HDC hdc = get_ctx();
1578 if (RealizePalette (hdc) > 0)
1584 case WM_QUERYNEWPALETTE:
1586 HDC hdc = get_ctx();
1588 if (RealizePalette (hdc) > 0)
1600 * Add the scan code and keypress timing to the random
1603 noise_ultralight(lParam);
1606 * We don't do TranslateMessage since it disassociates the
1607 * resulting CHAR message from the KEYDOWN that sparked it,
1608 * which we occasionally don't want. Instead, we process
1609 * KEYDOWN, and call the Win32 translator functions so that
1610 * we get the translations under _our_ control.
1613 unsigned char buf[20];
1616 if (wParam==VK_PROCESSKEY) {
1619 m.message = WM_KEYDOWN;
1621 m.lParam = lParam & 0xdfff;
1622 TranslateMessage(&m);
1624 len = TranslateKey (message, wParam, lParam, buf);
1626 return DefWindowProc (hwnd, message, wParam, lParam);
1627 ldisc_send (buf, len);
1636 unsigned char buf[2];
1639 buf[0] = wParam >> 8;
1640 ldisc_send (buf, 2);
1645 * Nevertheless, we are prepared to deal with WM_CHAR
1646 * messages, should they crop up. So if someone wants to
1647 * post the things to us as part of a macro manoeuvre,
1648 * we're ready to cope.
1651 char c = xlat_kbd2tty((unsigned char)wParam);
1657 return DefWindowProc (hwnd, message, wParam, lParam);
1661 * Move the system caret. (We maintain one, even though it's
1662 * invisible, for the benefit of blind people: apparently some
1663 * helper software tracks the system caret, so we should arrange to
1666 void sys_cursor(int x, int y) {
1667 SetCaretPos(x * font_width, y * font_height);
1671 * Draw a line of text in the window, at given character
1672 * coordinates, in given attributes.
1674 * We are allowed to fiddle with the contents of `text'.
1676 void do_text (Context ctx, int x, int y, char *text, int len,
1677 unsigned long attr, int lattr) {
1679 int nfg, nbg, nfont;
1682 int force_manual_underline = 0;
1683 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1684 static int *IpDx = 0, IpDxLEN = 0;;
1686 if (len>IpDxLEN || IpDx[0] != fnt_width) {
1690 IpDx = smalloc((len+16)*sizeof(int));
1693 for(i=0; i<IpDxLEN; i++)
1694 IpDx[i] = fnt_width;
1700 if ((attr & ATTR_ACTCURS) && cfg.cursor_type == 0) {
1701 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1702 attr ^= ATTR_CUR_XOR;
1706 if (cfg.vtmode == VT_OEMONLY)
1710 * Map high-half characters in order to approximate ISO using
1711 * OEM character set. No characters are missing if the OEM codepage
1714 if (nfont & FONT_OEM) {
1716 for (i=0; i<len; i++)
1717 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1719 /* This is CP850 ... perfect translation */
1720 static const char oemhighhalf[] =
1721 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1722 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1723 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1724 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1725 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1726 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1727 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1728 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1729 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1730 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1731 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1732 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1735 /* This is CP437 ... junk translation */
1736 static const unsigned char oemhighhalf[] = {
1737 0x20, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1738 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1739 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1740 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1741 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1742 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1743 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1744 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1745 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1746 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1747 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1748 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1751 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1755 if (attr & ATTR_LINEDRW) {
1758 static const char poorman[] =
1759 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1762 static const char oemmap_437[] =
1763 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1764 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1767 static const char oemmap_850[] =
1768 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1769 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1771 /* Poor windows font ... eg: windows courier */
1772 static const char oemmap[] =
1773 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1774 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1777 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1778 * VT100 line drawing chars; everything else stays normal.
1780 * Actually '_' maps to space too, but that's done before.
1782 switch (cfg.vtmode) {
1784 for (i=0; i<len; i++)
1785 if (text[i] >= '\x60' && text[i] <= '\x7E')
1786 text[i] += '\x01' - '\x60';
1789 /* Make sure we actually have an OEM font */
1790 if (fonts[nfont|FONT_OEM]) {
1793 for (i=0; i<len; i++)
1794 if (text[i] >= '\x60' && text[i] <= '\x7E')
1795 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1799 for (i=0; i<len; i++)
1800 if (text[i] >= '\x60' && text[i] <= '\x7E')
1801 text[i] = poorman[(unsigned char)text[i] - 0x60];
1806 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1807 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1808 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1810 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1811 nfont |= FONT_UNDERLINE;
1814 if (nfont&FONT_UNDERLINE)
1815 force_manual_underline = 1;
1816 /* Don't do the same for manual bold, it could be bad news. */
1818 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1820 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1821 force_manual_underline = 1;
1822 if (attr & ATTR_REVERSE) {
1823 t = nfg; nfg = nbg; nbg = t;
1825 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1827 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1831 SelectObject (hdc, fonts[nfont]);
1832 SetTextColor (hdc, fg);
1833 SetBkColor (hdc, bg);
1834 SetBkMode (hdc, OPAQUE);
1837 line_box.right = x+fnt_width*len;
1838 line_box.bottom = y+font_height;
1839 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1840 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1841 SetBkMode (hdc, TRANSPARENT);
1843 /* GRR: This draws the character outside it's box and can leave
1844 * 'droppings' even with the clip box! I suppose I could loop it
1845 * one character at a time ... yuk.
1847 * Or ... I could do a test print with "W", and use +1 or -1 for this
1848 * shift depending on if the leftmost column is blank...
1850 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1852 if (force_manual_underline ||
1853 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1855 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1856 MoveToEx (hdc, x, y+descent, NULL);
1857 LineTo (hdc, x+len*fnt_width, y+descent);
1858 oldpen = SelectObject (hdc, oldpen);
1859 DeleteObject (oldpen);
1861 if ((attr & ATTR_PASCURS) && cfg.cursor_type == 0) {
1864 pts[0].x = pts[1].x = pts[4].x = x;
1865 pts[2].x = pts[3].x = x+fnt_width-1;
1866 pts[0].y = pts[3].y = pts[4].y = y;
1867 pts[1].y = pts[2].y = y+font_height-1;
1868 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1869 Polyline (hdc, pts, 5);
1870 oldpen = SelectObject (hdc, oldpen);
1871 DeleteObject (oldpen);
1873 if ((attr & (ATTR_ACTCURS | ATTR_PASCURS)) && cfg.cursor_type != 0) {
1874 int startx, starty, dx, dy, length, i;
1875 if (cfg.cursor_type == 1) {
1876 startx = x; starty = y+descent;
1877 dx = 1; dy = 0; length = fnt_width;
1880 if (attr & ATTR_RIGHTCURS)
1881 xadjust = fnt_width-1;
1882 startx = x+xadjust; starty = y;
1883 dx = 0; dy = 1; length = font_height;
1885 if (attr & ATTR_ACTCURS) {
1887 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1888 MoveToEx (hdc, startx, starty, NULL);
1889 LineTo (hdc, startx+dx*length, starty+dy*length);
1890 oldpen = SelectObject (hdc, oldpen);
1891 DeleteObject (oldpen);
1893 for (i = 0; i < length; i++) {
1895 SetPixel(hdc, startx, starty, colours[23]);
1897 startx += dx; starty += dy;
1903 static int check_compose(int first, int second) {
1905 static char * composetbl[] = {
1906 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1907 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1908 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1909 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1910 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1911 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1912 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1913 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1914 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1915 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1916 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1917 "\"uü", "'yý", "htþ", "\"yÿ",
1921 static int recurse = 0;
1924 for(c=composetbl; *c; c++) {
1925 if( (*c)[0] == first && (*c)[1] == second)
1927 return (*c)[2] & 0xFF;
1934 nc = check_compose(second, first);
1936 nc = check_compose(toupper(first), toupper(second));
1938 nc = check_compose(toupper(second), toupper(first));
1946 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1947 * codes. Returns number of bytes used or zero to drop the message
1948 * or -1 to forward the message to windows.
1950 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
1951 unsigned char *output) {
1953 int scan, left_alt = 0, key_down, shift_state;
1955 unsigned char * p = output;
1956 static int alt_state = 0;
1958 HKL kbd_layout = GetKeyboardLayout(0);
1960 static WORD keys[3];
1961 static int compose_char = 0;
1962 static WPARAM compose_key = 0;
1964 r = GetKeyboardState(keystate);
1965 if (!r) memset(keystate, 0, sizeof(keystate));
1969 { /* Tell us all about key events */
1970 static BYTE oldstate[256];
1971 static int first = 1;
1974 if(first) memcpy(oldstate, keystate, sizeof(oldstate));
1977 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT) {
1979 } else if ((HIWORD(lParam)&KF_UP) && scan==(HIWORD(lParam) & 0xFF) ) {
1983 if (wParam >= VK_F1 && wParam <= VK_F20 )
1984 debug(("K_F%d", wParam+1-VK_F1));
1987 case VK_SHIFT: debug(("SHIFT")); break;
1988 case VK_CONTROL: debug(("CTRL")); break;
1989 case VK_MENU: debug(("ALT")); break;
1990 default: debug(("VK_%02x", wParam));
1992 if(message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
1994 debug((", S%02x", scan=(HIWORD(lParam) & 0xFF) ));
1996 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
1997 if (ch>=' ' && ch<='~') debug((", '%c'", ch));
1998 else if (ch) debug((", $%02x", ch));
2000 if (keys[0]) debug((", KB0=%02x", keys[0]));
2001 if (keys[1]) debug((", KB1=%02x", keys[1]));
2002 if (keys[2]) debug((", KB2=%02x", keys[2]));
2004 if ( (keystate[VK_SHIFT]&0x80)!=0) debug((", S"));
2005 if ( (keystate[VK_CONTROL]&0x80)!=0) debug((", C"));
2006 if ( (HIWORD(lParam)&KF_EXTENDED) ) debug((", E"));
2007 if ( (HIWORD(lParam)&KF_UP) ) debug((", U"));
2010 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
2012 else if ( (HIWORD(lParam)&KF_UP) )
2013 oldstate[wParam&0xFF] ^= 0x80;
2015 oldstate[wParam&0xFF] ^= 0x81;
2017 for(ch=0; ch<256; ch++)
2018 if (oldstate[ch] != keystate[ch])
2019 debug((", M%02x=%02x", ch, keystate[ch]));
2021 memcpy(oldstate, keystate, sizeof(oldstate));
2025 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED)) {
2026 keystate[VK_RMENU] = keystate[VK_MENU];
2030 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2031 if ( (cfg.funky_type == 3 ||
2032 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2033 && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
2035 wParam = VK_EXECUTE;
2037 /* UnToggle NUMLock */
2038 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
2039 keystate[VK_NUMLOCK] ^= 1;
2042 /* And write back the 'adjusted' state */
2043 SetKeyboardState (keystate);
2046 /* Disable Auto repeat if required */
2047 if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
2050 if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
2053 key_down = ((HIWORD(lParam)&KF_UP)==0);
2055 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2056 if (left_alt && (keystate[VK_CONTROL]&0x80)) {
2057 if (cfg.ctrlaltkeys)
2058 keystate[VK_MENU] = 0;
2060 keystate[VK_RMENU] = 0x80;
2065 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2066 shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
2067 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
2069 /* Note if AltGr was pressed and if it was used as a compose key */
2070 if (!compose_state) {
2071 compose_key = 0x100;
2072 if (cfg.compose_key) {
2073 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
2074 compose_key = wParam;
2076 if (wParam == VK_APPS)
2077 compose_key = wParam;
2080 if (wParam == compose_key)
2082 if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
2084 else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
2089 else if (compose_state==1 && wParam != VK_CONTROL)
2093 * Record that we pressed key so the scroll window can be reset, but
2094 * be careful to avoid Shift-UP/Down
2096 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
2100 /* Make sure we're not pasting */
2101 if (key_down) term_nopaste();
2103 if (compose_state>1 && left_alt) compose_state = 0;
2105 /* Sanitize the number pad if not using a PC NumPad */
2106 if( left_alt || (app_keypad_keys && !cfg.no_applic_k
2107 && cfg.funky_type != 2)
2108 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state )
2110 if ((HIWORD(lParam)&KF_EXTENDED) == 0)
2115 case VK_INSERT: nParam = VK_NUMPAD0; break;
2116 case VK_END: nParam = VK_NUMPAD1; break;
2117 case VK_DOWN: nParam = VK_NUMPAD2; break;
2118 case VK_NEXT: nParam = VK_NUMPAD3; break;
2119 case VK_LEFT: nParam = VK_NUMPAD4; break;
2120 case VK_CLEAR: nParam = VK_NUMPAD5; break;
2121 case VK_RIGHT: nParam = VK_NUMPAD6; break;
2122 case VK_HOME: nParam = VK_NUMPAD7; break;
2123 case VK_UP: nParam = VK_NUMPAD8; break;
2124 case VK_PRIOR: nParam = VK_NUMPAD9; break;
2125 case VK_DELETE: nParam = VK_DECIMAL; break;
2129 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
2135 /* If a key is pressed and AltGr is not active */
2136 if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
2138 /* Okay, prepare for most alts then ...*/
2139 if (left_alt) *p++ = '\033';
2141 /* Lets see if it's a pattern we know all about ... */
2142 if (wParam == VK_PRIOR && shift_state == 1) {
2143 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2146 if (wParam == VK_NEXT && shift_state == 1) {
2147 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2150 if (wParam == VK_INSERT && shift_state == 1) {
2151 term_mouse (MB_PASTE, MA_CLICK, 0, 0);
2152 term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
2155 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2158 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2160 PostMessage(hwnd, WM_CHAR, ' ', 0);
2161 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2164 /* Control-Numlock for app-keypad mode switch */
2165 if (wParam == VK_PAUSE && shift_state == 2) {
2166 app_keypad_keys ^= 1;
2170 /* Nethack keypad */
2171 if (cfg.nethack_keypad && !left_alt) {
2173 case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
2174 case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
2175 case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
2176 case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
2177 case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
2178 case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
2179 case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
2180 case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
2181 case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
2185 /* Application Keypad */
2189 if ( cfg.funky_type == 3 ||
2190 ( cfg.funky_type <= 1 &&
2191 app_keypad_keys && !cfg.no_applic_k)) switch(wParam) {
2192 case VK_EXECUTE: xkey = 'P'; break;
2193 case VK_DIVIDE: xkey = 'Q'; break;
2194 case VK_MULTIPLY:xkey = 'R'; break;
2195 case VK_SUBTRACT:xkey = 'S'; break;
2197 if(app_keypad_keys && !cfg.no_applic_k) switch(wParam) {
2198 case VK_NUMPAD0: xkey = 'p'; break;
2199 case VK_NUMPAD1: xkey = 'q'; break;
2200 case VK_NUMPAD2: xkey = 'r'; break;
2201 case VK_NUMPAD3: xkey = 's'; break;
2202 case VK_NUMPAD4: xkey = 't'; break;
2203 case VK_NUMPAD5: xkey = 'u'; break;
2204 case VK_NUMPAD6: xkey = 'v'; break;
2205 case VK_NUMPAD7: xkey = 'w'; break;
2206 case VK_NUMPAD8: xkey = 'x'; break;
2207 case VK_NUMPAD9: xkey = 'y'; break;
2209 case VK_DECIMAL: xkey = 'n'; break;
2210 case VK_ADD: if(cfg.funky_type==2) {
2211 if(shift_state) xkey = 'l';
2213 } else if(shift_state) xkey = 'm';
2217 case VK_DIVIDE: if(cfg.funky_type==2) xkey = 'o'; break;
2218 case VK_MULTIPLY:if(cfg.funky_type==2) xkey = 'j'; break;
2219 case VK_SUBTRACT:if(cfg.funky_type==2) xkey = 'm'; break;
2222 if (HIWORD(lParam)&KF_EXTENDED)
2230 if (xkey>='P' && xkey<='S')
2231 p += sprintf((char *)p, "\x1B%c", xkey);
2233 p += sprintf((char *)p, "\x1B?%c", xkey);
2236 p += sprintf((char *)p, "\x1BO%c", xkey);
2241 if (wParam == VK_BACK && shift_state == 0 ) /* Backspace */
2243 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2246 if (wParam == VK_TAB && shift_state == 1 ) /* Shift tab */
2248 *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
2250 if (wParam == VK_SPACE && shift_state == 2 ) /* Ctrl-Space */
2252 *p++ = 0; return p - output;
2254 if (wParam == VK_SPACE && shift_state == 3 ) /* Ctrl-Shift-Space */
2256 *p++ = 160; return p - output;
2258 if (wParam == VK_CANCEL && shift_state == 2 ) /* Ctrl-Break */
2260 *p++ = 3; return p - output;
2262 if (wParam == VK_PAUSE) /* Break/Pause */
2264 *p++ = 26; *p++ = 0; return -2;
2266 /* Control-2 to Control-8 are special */
2267 if (shift_state == 2 && wParam >= '2' && wParam <= '8')
2269 *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
2272 if (shift_state == 2 && wParam == 0xBD) {
2276 if (shift_state == 2 && wParam == 0xDF) {
2280 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2281 *p++ = '\r'; *p++ = '\n';
2286 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2287 * for integer decimal nn.)
2289 * We also deal with the weird ones here. Linux VCs replace F1
2290 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2291 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2296 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
2297 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
2298 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
2299 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
2300 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
2301 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
2302 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
2303 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
2304 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
2305 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
2306 case VK_F11: code = 23; break;
2307 case VK_F12: code = 24; break;
2308 case VK_F13: code = 25; break;
2309 case VK_F14: code = 26; break;
2310 case VK_F15: code = 28; break;
2311 case VK_F16: code = 29; break;
2312 case VK_F17: code = 31; break;
2313 case VK_F18: code = 32; break;
2314 case VK_F19: code = 33; break;
2315 case VK_F20: code = 34; break;
2316 case VK_HOME: code = 1; break;
2317 case VK_INSERT: code = 2; break;
2318 case VK_DELETE: code = 3; break;
2319 case VK_END: code = 4; break;
2320 case VK_PRIOR: code = 5; break;
2321 case VK_NEXT: code = 6; break;
2323 /* Reorder edit keys to physical order */
2324 if (cfg.funky_type == 3 && code <= 6 ) code = "\0\2\1\4\5\3\6"[code];
2326 if (vt52_mode && code > 0 && code <= 6) {
2327 p += sprintf((char *)p, "\x1B%c", " HLMEIG"[code]);
2331 if (cfg.funky_type == 5 && code >= 11 && code <= 24) {
2332 p += sprintf((char *)p, "\x1B[%c", code + 'M' - 11);
2335 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
2337 if (code>15) offt++; if (code>21) offt++;
2339 p += sprintf((char *)p, "\x1B%c", code + 'P' - 11 - offt);
2341 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11 - offt);
2344 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2345 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
2348 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2350 p += sprintf((char *)p, "\x1B%c", code + 'P' - 11);
2352 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
2355 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2356 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
2360 p += sprintf((char *)p, "\x1B[%d~", code);
2365 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2366 * some reason seems to send VK_CLEAR to Windows...).
2371 case VK_UP: xkey = 'A'; break;
2372 case VK_DOWN: xkey = 'B'; break;
2373 case VK_RIGHT: xkey = 'C'; break;
2374 case VK_LEFT: xkey = 'D'; break;
2375 case VK_CLEAR: xkey = 'G'; break;
2380 p += sprintf((char *)p, "\x1B%c", xkey);
2381 else if (app_cursor_keys && !cfg.no_applic_c)
2382 p += sprintf((char *)p, "\x1BO%c", xkey);
2384 p += sprintf((char *)p, "\x1B[%c", xkey);
2390 * Finally, deal with Return ourselves. (Win95 seems to
2391 * foul it up when Alt is pressed, for some reason.)
2393 if (wParam == VK_RETURN) /* Return */
2400 /* Okay we've done everything interesting; let windows deal with
2401 * the boring stuff */
2403 BOOL capsOn=keystate[VK_CAPITAL] !=0;
2405 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2406 if(cfg.xlat_capslockcyr)
2407 keystate[VK_CAPITAL] = 0;
2409 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
2415 unsigned char ch = (unsigned char)keys[i];
2417 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
2422 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
2426 if ((nc=check_compose(compose_char,ch)) == -1)
2428 MessageBeep(MB_ICONHAND);
2431 *p++ = xlat_kbd2tty((unsigned char)nc);
2437 if( left_alt && key_down ) *p++ = '\033';
2443 ch = xlat_latkbd2win(ch);
2444 *p++ = xlat_kbd2tty(ch);
2448 /* This is so the ALT-Numpad and dead keys work correctly. */
2453 /* If we're definitly not building up an ALT-54321 then clear it */
2454 if (!left_alt) keys[0] = 0;
2457 /* ALT alone may or may not want to bring up the System menu */
2458 if (wParam == VK_MENU) {
2460 if (message == WM_SYSKEYDOWN)
2462 else if (message == WM_SYSKEYUP && alt_state)
2463 PostMessage(hwnd, WM_CHAR, ' ', 0);
2464 if (message == WM_SYSKEYUP)
2474 void set_title (char *title) {
2475 sfree (window_name);
2476 window_name = smalloc(1+strlen(title));
2477 strcpy (window_name, title);
2478 if (cfg.win_name_always || !IsIconic(hwnd))
2479 SetWindowText (hwnd, title);
2482 void set_icon (char *title) {
2484 icon_name = smalloc(1+strlen(title));
2485 strcpy (icon_name, title);
2486 if (!cfg.win_name_always && IsIconic(hwnd))
2487 SetWindowText (hwnd, title);
2490 void set_sbar (int total, int start, int page) {
2493 if (!cfg.scrollbar) return;
2495 si.cbSize = sizeof(si);
2496 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2498 si.nMax = total - 1;
2502 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2505 Context get_ctx(void) {
2510 SelectPalette (hdc, pal, FALSE);
2516 void free_ctx (Context ctx) {
2517 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2518 ReleaseDC (hwnd, ctx);
2521 static void real_palette_set (int n, int r, int g, int b) {
2523 logpal->palPalEntry[n].peRed = r;
2524 logpal->palPalEntry[n].peGreen = g;
2525 logpal->palPalEntry[n].peBlue = b;
2526 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2527 colours[n] = PALETTERGB(r, g, b);
2528 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2530 colours[n] = RGB(r, g, b);
2533 void palette_set (int n, int r, int g, int b) {
2534 static const int first[21] = {
2535 0, 2, 4, 6, 8, 10, 12, 14,
2536 1, 3, 5, 7, 9, 11, 13, 15,
2539 real_palette_set (first[n], r, g, b);
2541 real_palette_set (first[n]+1, r, g, b);
2543 HDC hdc = get_ctx();
2544 UnrealizeObject (pal);
2545 RealizePalette (hdc);
2550 void palette_reset (void) {
2553 for (i = 0; i < NCOLOURS; i++) {
2555 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2556 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2557 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2558 logpal->palPalEntry[i].peFlags = 0;
2559 colours[i] = PALETTERGB(defpal[i].rgbtRed,
2560 defpal[i].rgbtGreen,
2561 defpal[i].rgbtBlue);
2563 colours[i] = RGB(defpal[i].rgbtRed,
2564 defpal[i].rgbtGreen,
2565 defpal[i].rgbtBlue);
2570 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2572 RealizePalette (hdc);
2577 void write_clip (void *data, int len, int must_deselect) {
2581 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2584 lock = GlobalLock (clipdata);
2587 memcpy (lock, data, len);
2588 ((unsigned char *) lock) [len] = 0;
2589 GlobalUnlock (clipdata);
2592 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2594 if (OpenClipboard (hwnd)) {
2596 SetClipboardData (CF_TEXT, clipdata);
2599 GlobalFree (clipdata);
2602 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2605 void get_clip (void **p, int *len) {
2606 static HGLOBAL clipdata = NULL;
2610 GlobalUnlock (clipdata);
2614 if (OpenClipboard (NULL)) {
2615 clipdata = GetClipboardData (CF_TEXT);
2618 *p = GlobalLock (clipdata);
2632 * Move `lines' lines from position `from' to position `to' in the
2635 void optimised_move (int to, int from, int lines) {
2639 min = (to < from ? to : from);
2640 max = to + from - min;
2642 r.left = 0; r.right = cols * font_width;
2643 r.top = min * font_height; r.bottom = (max+lines) * font_height;
2644 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2648 * Print a message box and perform a fatal exit.
2650 void fatalbox(char *fmt, ...) {
2655 vsprintf(stuff, fmt, ap);
2657 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2664 void beep(int mode) {
2665 if (mode == BELL_DEFAULT) {
2667 } else if (mode == BELL_WAVEFILE) {
2668 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
2669 char buf[sizeof(cfg.bell_wavefile)+80];
2670 sprintf(buf, "Unable to play sound file\n%s\n"
2671 "Using default sound instead", cfg.bell_wavefile);
2672 MessageBox(hwnd, buf, "PuTTY Sound Error", MB_OK | MB_ICONEXCLAMATION);
2673 cfg.beep = BELL_DEFAULT;