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_SAVED_MIN 0x1000
46 #define IDM_SAVED_MAX 0x2000
48 #define WM_IGNORE_SIZE (WM_XUSER + 1)
49 #define WM_IGNORE_CLIP (WM_XUSER + 2)
51 /* Needed for Chinese support and apparently not always defined. */
53 #define VK_PROCESSKEY 0xE5
56 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
57 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
58 static void cfgtopalette(void);
59 static void init_palette(void);
60 static void init_fonts(int);
62 static int extra_width, extra_height;
64 static int pending_netevent = 0;
65 static WPARAM pend_netevent_wParam = 0;
66 static LPARAM pend_netevent_lParam = 0;
67 static void enact_pending_netevent(void);
69 static time_t last_movement = 0;
73 #define FONT_UNDERLINE 2
74 #define FONT_BOLDUND 3
76 #define FONT_OEMBOLD 5
77 #define FONT_OEMBOLDUND 6
79 static HFONT fonts[8];
80 static int font_needs_hand_underlining;
82 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
90 static COLORREF colours[NCOLOURS];
92 static LPLOGPALETTE logpal;
93 static RGBTRIPLE defpal[NCOLOURS];
97 static HBITMAP caretbm;
99 static int dbltime, lasttime, lastact;
100 static Mouse_Button lastbtn;
102 static char *window_name, *icon_name;
104 static Ldisc *real_ldisc;
106 static int compose_state = 0;
108 void begin_session(void) {
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);
321 real_ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
322 /* To start with, we use the simple line discipline, so we can
323 * type passwords etc without fear of them being echoed... */
324 ldisc = &ldisc_simple;
328 wndclass.lpfnWndProc = WndProc;
329 wndclass.cbClsExtra = 0;
330 wndclass.cbWndExtra = 0;
331 wndclass.hInstance = inst;
332 wndclass.hIcon = LoadIcon (inst,
333 MAKEINTRESOURCE(IDI_MAINICON));
334 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
335 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
336 wndclass.lpszMenuName = NULL;
337 wndclass.lpszClassName = appname;
339 RegisterClass (&wndclass);
344 savelines = cfg.savelines;
350 * Guess some defaults for the window size. This all gets
351 * updated later, so we don't really care too much. However, we
352 * do want the font width/height guesses to correspond to a
353 * large font rather than a small one...
360 term_size (cfg.height, cfg.width, cfg.savelines);
361 guess_width = extra_width + font_width * cols;
362 guess_height = extra_height + font_height * rows;
365 HWND w = GetDesktopWindow();
366 GetWindowRect (w, &r);
367 if (guess_width > r.right - r.left)
368 guess_width = r.right - r.left;
369 if (guess_height > r.bottom - r.top)
370 guess_height = r.bottom - r.top;
374 int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
376 if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
377 if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
378 if (cfg.alwaysontop) exwinmode = WS_EX_TOPMOST;
379 hwnd = CreateWindowEx (exwinmode, appname, appname,
380 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
381 guess_width, guess_height,
382 NULL, NULL, inst, NULL);
386 * Initialise the fonts, simultaneously correcting the guesses
387 * for font_{width,height}.
389 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
394 * Correct the guesses for extra_{width,height}.
398 GetWindowRect (hwnd, &wr);
399 GetClientRect (hwnd, &cr);
400 extra_width = wr.right - wr.left - cr.right + cr.left;
401 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
405 * Resize the window, now we know what size we _really_ want it
408 guess_width = extra_width + font_width * cols;
409 guess_height = extra_height + font_height * rows;
410 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
411 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
412 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
415 * Set up a caret bitmap, with no content.
419 int size = (font_width+15)/16 * 2 * font_height;
420 bits = smalloc(size);
421 memset(bits, 0, size);
422 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
425 CreateCaret(hwnd, caretbm, font_width, font_height);
428 * Initialise the scroll bar.
433 si.cbSize = sizeof(si);
434 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
439 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
443 * Start up the telnet connection.
447 char msg[1024], *title;
450 error = back->init (cfg.host, cfg.port, &realhost);
452 sprintf(msg, "Unable to open connection:\n%s", error);
453 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
456 window_name = icon_name = NULL;
458 title = cfg.wintitle;
460 sprintf(msg, "%s - PuTTY", realhost);
467 session_closed = FALSE;
470 * Set up the input and output buffers.
473 outbuf_reap = outbuf_head = 0;
476 * Prepare the mouse handler.
478 lastact = MA_NOTHING;
479 lastbtn = MB_NOTHING;
480 dbltime = GetDoubleClickTime();
483 * Set up the session-control options on the system menu.
486 HMENU m = GetSystemMenu (hwnd, FALSE);
490 AppendMenu (m, MF_SEPARATOR, 0, 0);
491 if (cfg.protocol == PROT_TELNET) {
493 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
494 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
495 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
496 AppendMenu (p, MF_SEPARATOR, 0, 0);
497 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
498 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
499 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
500 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
501 AppendMenu (p, MF_SEPARATOR, 0, 0);
502 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
503 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
504 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
505 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
506 AppendMenu (p, MF_SEPARATOR, 0, 0);
507 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
508 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
509 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
510 AppendMenu (m, MF_SEPARATOR, 0, 0);
512 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
513 AppendMenu (m, MF_SEPARATOR, 0, 0);
514 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
515 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
518 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
519 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
520 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
521 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
522 AppendMenu (m, MF_SEPARATOR, 0, 0);
523 AppendMenu (m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
524 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
525 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
526 AppendMenu (m, MF_SEPARATOR, 0, 0);
527 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
531 * Finally show the window!
533 ShowWindow (hwnd, show);
536 * Open the initial log file if there is one.
541 * Set the palette up.
547 has_focus = (GetForegroundWindow() == hwnd);
550 if (GetMessage (&msg, NULL, 0, 0) == 1)
552 int timer_id = 0, long_timer = 0;
554 while (msg.message != WM_QUIT) {
555 /* Sometimes DispatchMessage calls routines that use their own
556 * GetMessage loop, setup this timer so we get some control back.
558 * Also call term_update() from the timer so that if the host
559 * is sending data flat out we still do redraws.
561 if(timer_id && long_timer) {
562 KillTimer(hwnd, timer_id);
563 long_timer = timer_id = 0;
566 timer_id = SetTimer(hwnd, 1, 20, NULL);
567 DispatchMessage (&msg);
569 /* Make sure we blink everything that needs it. */
572 /* Send the paste buffer if there's anything to send */
575 /* If there's nothing new in the queue then we can do everything
576 * we've delayed, reading the socket, writing, and repainting
579 if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
582 if (pending_netevent) {
583 enact_pending_netevent();
585 /* Force the cursor blink on */
588 if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
592 /* Okay there is now nothing to do so we make sure the screen is
593 * completely up to date then tell windows to call us in a little
597 KillTimer(hwnd, timer_id);
606 timer_id = SetTimer(hwnd, 1, 59500, NULL);
608 timer_id = SetTimer(hwnd, 1, 100, NULL);
611 /* There's no point rescanning everything in the message queue
612 * so we do an apperently unneccesary wait here
615 if (GetMessage (&msg, NULL, 0, 0) != 1)
627 DeleteObject(fonts[i]);
634 if (cfg.protocol == PROT_SSH) {
645 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
647 char *do_select(SOCKET skt, int startup) {
651 events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE;
656 return "do_select(): internal error (hwnd==NULL)";
657 if (WSAAsyncSelect (skt, hwnd, msg, events) == SOCKET_ERROR) {
658 switch (WSAGetLastError()) {
659 case WSAENETDOWN: return "Network is down";
660 default: return "WSAAsyncSelect(): unknown error";
667 * Print a message box and close the connection.
669 void connection_fatal(char *fmt, ...) {
674 vsprintf(stuff, fmt, ap);
676 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
677 if (cfg.close_on_exit)
680 session_closed = TRUE;
681 SetWindowText (hwnd, "PuTTY (inactive)");
686 * Actually do the job requested by a WM_NETEVENT
688 static void enact_pending_netevent(void) {
689 static int reentering = 0;
690 extern int select_result(WPARAM, LPARAM);
694 return; /* don't unpend the pending */
696 pending_netevent = FALSE;
699 ret = select_result (pend_netevent_wParam, pend_netevent_lParam);
703 if (cfg.close_on_exit)
706 session_closed = TRUE;
707 MessageBox(hwnd, "Connection closed by remote host",
708 "PuTTY", MB_OK | MB_ICONINFORMATION);
709 SetWindowText (hwnd, "PuTTY (inactive)");
715 * Copy the colour palette from the configuration data into defpal.
716 * This is non-trivial because the colour indices are different.
718 static void cfgtopalette(void) {
720 static const int ww[] = {
721 6, 7, 8, 9, 10, 11, 12, 13,
722 14, 15, 16, 17, 18, 19, 20, 21,
723 0, 1, 2, 3, 4, 4, 5, 5
726 for (i=0; i<24; i++) {
728 defpal[i].rgbtRed = cfg.colours[w][0];
729 defpal[i].rgbtGreen = cfg.colours[w][1];
730 defpal[i].rgbtBlue = cfg.colours[w][2];
735 * Set up the colour palette.
737 static void init_palette(void) {
739 HDC hdc = GetDC (hwnd);
741 if (cfg.try_palette &&
742 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
743 logpal = smalloc(sizeof(*logpal)
744 - sizeof(logpal->palPalEntry)
745 + NCOLOURS * sizeof(PALETTEENTRY));
746 logpal->palVersion = 0x300;
747 logpal->palNumEntries = NCOLOURS;
748 for (i = 0; i < NCOLOURS; i++) {
749 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
750 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
751 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
752 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
754 pal = CreatePalette (logpal);
756 SelectPalette (hdc, pal, FALSE);
757 RealizePalette (hdc);
758 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
762 ReleaseDC (hwnd, hdc);
765 for (i=0; i<NCOLOURS; i++)
766 colours[i] = PALETTERGB(defpal[i].rgbtRed,
770 for(i=0; i<NCOLOURS; i++)
771 colours[i] = RGB(defpal[i].rgbtRed,
777 * Initialise all the fonts we will need. There may be as many as
778 * eight or as few as one. We also:
780 * - check the font width and height, correcting our guesses if
783 * - verify that the bold font is the same width as the ordinary
784 * one, and engage shadow bolding if not.
786 * - verify that the underlined font is the same width as the
787 * ordinary one (manual underlining by means of line drawing can
788 * be done in a pinch).
790 static void init_fonts(int pick_width) {
795 int fw_dontcare, fw_bold;
804 if (cfg.fontisbold) {
805 fw_dontcare = FW_BOLD;
808 fw_dontcare = FW_DONTCARE;
814 font_height = cfg.fontheight;
815 font_width = pick_width;
818 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
819 c, OUT_DEFAULT_PRECIS, \
820 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
821 FIXED_PITCH | FF_DONTCARE, cfg.font)
823 if (cfg.vtmode != VT_OEMONLY) {
824 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
826 SelectObject (hdc, fonts[FONT_NORMAL]);
827 GetTextMetrics(hdc, &tm);
828 font_height = tm.tmHeight;
829 font_width = tm.tmAveCharWidth;
831 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
834 * Some fonts, e.g. 9-pt Courier, draw their underlines
835 * outside their character cell. We successfully prevent
836 * screen corruption by clipping the text output, but then
837 * we lose the underline completely. Here we try to work
838 * out whether this is such a font, and if it is, we set a
839 * flag that causes underlines to be drawn by hand.
841 * Having tried other more sophisticated approaches (such
842 * as examining the TEXTMETRIC structure or requesting the
843 * height of a string), I think we'll do this the brute
844 * force way: we create a small bitmap, draw an underlined
845 * space on it, and test to see whether any pixels are
846 * foreground-coloured. (Since we expect the underline to
847 * go all the way across the character cell, we only search
848 * down a single column of the bitmap, half way across.)
852 HBITMAP und_bm, und_oldbm;
856 und_dc = CreateCompatibleDC(hdc);
857 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
858 und_oldbm = SelectObject(und_dc, und_bm);
859 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
860 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
861 SetTextColor (und_dc, RGB(255,255,255));
862 SetBkColor (und_dc, RGB(0,0,0));
863 SetBkMode (und_dc, OPAQUE);
864 ExtTextOut (und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
866 for (i = 0; i < font_height; i++) {
867 c = GetPixel(und_dc, font_width/2, i);
871 SelectObject(und_dc, und_oldbm);
872 DeleteObject(und_bm);
874 font_needs_hand_underlining = !gotit;
877 if (bold_mode == BOLD_FONT) {
878 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
879 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
882 if (cfg.vtmode == VT_OEMANSI) {
883 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
884 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
886 if (bold_mode == BOLD_FONT) {
887 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
888 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
894 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
896 SelectObject (hdc, fonts[FONT_OEM]);
897 GetTextMetrics(hdc, &tm);
898 font_height = tm.tmHeight;
899 font_width = tm.tmAveCharWidth;
901 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
903 if (bold_mode == BOLD_FONT) {
904 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
905 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
910 descent = tm.tmAscent + 1;
911 if (descent >= font_height)
912 descent = font_height - 1;
913 firstchar = tm.tmFirstChar;
915 for (i=0; i<8; i++) {
917 if (SelectObject (hdc, fonts[i]) &&
918 GetTextMetrics(hdc, &tm) )
919 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
925 ReleaseDC (hwnd, hdc);
927 /* ... This is wrong in OEM only mode */
928 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
929 (bold_mode == BOLD_FONT &&
930 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
932 DeleteObject (fonts[FONT_UNDERLINE]);
933 if (bold_mode == BOLD_FONT)
934 DeleteObject (fonts[FONT_BOLDUND]);
937 if (bold_mode == BOLD_FONT &&
938 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
939 bold_mode = BOLD_SHADOW;
940 DeleteObject (fonts[FONT_BOLD]);
941 if (und_mode == UND_FONT)
942 DeleteObject (fonts[FONT_BOLDUND]);
946 /* With the fascist font painting it doesn't matter if the linedraw font
947 * isn't exactly the right size anymore so we don't have to check this.
949 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
950 if( cfg.fontcharset == OEM_CHARSET )
952 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
953 "different sizes. Using OEM-only mode instead",
954 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
955 cfg.vtmode = VT_OEMONLY;
957 else if( firstchar < ' ' )
959 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
960 "different sizes. Using XTerm mode instead",
961 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
962 cfg.vtmode = VT_XWINDOWS;
966 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
967 "different sizes. Using ISO8859-1 mode instead",
968 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
969 cfg.vtmode = VT_POORMAN;
974 DeleteObject (fonts[i]);
980 void request_resize (int w, int h, int refont) {
983 /* If the window is maximized supress resizing attempts */
984 if(IsZoomed(hwnd)) return;
987 /* Don't do this in OEMANSI, you may get disable messages */
988 if (refont && w != cols && (cols==80 || cols==132)
989 && cfg.vtmode != VT_OEMANSI)
991 if (refont && w != cols && (cols==80 || cols==132))
994 /* If font width too big for screen should we shrink the font more ? */
996 font_width = ((font_width*cols+w/2)/w);
1003 DeleteObject(fonts[i]);
1005 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1006 und_mode = UND_FONT;
1007 init_fonts(font_width);
1011 static int first_time = 1;
1017 /* Get the size of the screen */
1018 if (GetClientRect(GetDesktopWindow(),&ss))
1019 /* first_time = 0 */;
1020 else { first_time = 2; break; }
1022 /* Make sure the values are sane */
1023 width = (ss.right-ss.left-extra_width ) / font_width;
1024 height = (ss.bottom-ss.top-extra_height ) / font_height;
1026 if (w>width) w=width;
1027 if (h>height) h=height;
1033 width = extra_width + font_width * w;
1034 height = extra_height + font_height * h;
1036 SetWindowPos (hwnd, NULL, 0, 0, width, height,
1037 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1038 SWP_NOMOVE | SWP_NOZORDER);
1041 static void click (Mouse_Button b, int x, int y) {
1042 int thistime = GetMessageTime();
1044 if (lastbtn == b && thistime - lasttime < dbltime) {
1045 lastact = (lastact == MA_CLICK ? MA_2CLK :
1046 lastact == MA_2CLK ? MA_3CLK :
1047 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1052 if (lastact != MA_NOTHING)
1053 term_mouse (b, lastact, x, y);
1054 lasttime = thistime;
1057 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
1058 WPARAM wParam, LPARAM lParam) {
1060 static int ignore_size = FALSE;
1061 static int ignore_clip = FALSE;
1062 static int just_reconfigged = FALSE;
1063 static int resizing = FALSE;
1064 static int need_backend_resize = FALSE;
1068 if (pending_netevent)
1069 enact_pending_netevent();
1076 if (cfg.ping_interval > 0)
1080 if (now-last_movement > cfg.ping_interval * 60 - 10)
1082 back->special(TS_PING);
1083 last_movement = now;
1090 if (!cfg.warn_on_close || session_closed ||
1091 MessageBox(hwnd, "Are you sure you want to close this session?",
1092 "PuTTY Exit Confirmation",
1093 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1094 DestroyWindow(hwnd);
1097 PostQuitMessage (0);
1100 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1112 PROCESS_INFORMATION pi;
1113 HANDLE filemap = NULL;
1115 if (wParam == IDM_DUPSESS) {
1117 * Allocate a file-mapping memory chunk for the
1120 SECURITY_ATTRIBUTES sa;
1123 sa.nLength = sizeof(sa);
1124 sa.lpSecurityDescriptor = NULL;
1125 sa.bInheritHandle = TRUE;
1126 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
1133 p = (Config *)MapViewOfFile(filemap,
1135 0, 0, sizeof(Config));
1137 *p = cfg; /* structure copy */
1141 sprintf(c, "putty &%p", filemap);
1143 } else if (wParam == IDM_SAVEDSESS) {
1144 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
1145 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1147 cl = NULL; /* not a very important failure mode */
1149 sprintf(cl, "putty @%s", session);
1155 GetModuleFileName (NULL, b, sizeof(b)-1);
1157 si.lpReserved = NULL;
1158 si.lpDesktop = NULL;
1162 si.lpReserved2 = NULL;
1163 CreateProcess (b, cl, NULL, NULL, TRUE,
1164 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1167 CloseHandle(filemap);
1174 int prev_alwaysontop = cfg.alwaysontop;
1175 char oldlogfile[FILENAME_MAX];
1177 int need_setwpos = FALSE;
1178 int old_fwidth, old_fheight;
1180 strcpy(oldlogfile, cfg.logfilename);
1181 oldlogtype = cfg.logtype;
1184 old_fwidth = font_width;
1185 old_fheight = font_height;
1186 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1188 if (!do_reconfig(hwnd))
1191 if (strcmp(oldlogfile, cfg.logfilename) ||
1192 oldlogtype != cfg.logtype) {
1193 logfclose(); /* reset logging */
1197 just_reconfigged = TRUE;
1202 DeleteObject(fonts[i]);
1204 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1205 und_mode = UND_FONT;
1209 * Telnet will change local echo -> remote if the
1212 if (cfg.protocol != PROT_TELNET)
1213 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
1221 /* Enable or disable the scroll bar, etc */
1223 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1224 LONG nexflag, exflag = GetWindowLong(hwnd, GWL_EXSTYLE);
1227 if (cfg.alwaysontop != prev_alwaysontop) {
1228 if (cfg.alwaysontop) {
1229 nexflag = WS_EX_TOPMOST;
1230 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1231 SWP_NOMOVE | SWP_NOSIZE);
1234 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1235 SWP_NOMOVE | SWP_NOSIZE);
1240 if (cfg.scrollbar) nflg |= WS_VSCROLL;
1241 else nflg &= ~WS_VSCROLL;
1243 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1245 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1247 if (nflg != flag || nexflag != exflag)
1252 SetWindowLong(hwnd, GWL_STYLE, nflg);
1253 if (nexflag != exflag)
1254 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1256 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1258 SetWindowPos(hwnd, NULL, 0,0,0,0,
1259 SWP_NOACTIVATE|SWP_NOCOPYBITS|
1260 SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|
1263 GetWindowRect (hwnd, &wr);
1264 GetClientRect (hwnd, &cr);
1265 extra_width = wr.right - wr.left - cr.right + cr.left;
1266 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1270 if (cfg.height != rows ||
1271 cfg.width != cols ||
1272 old_fwidth != font_width ||
1273 old_fheight != font_height ||
1274 cfg.savelines != savelines)
1275 need_setwpos = TRUE;
1276 term_size(cfg.height, cfg.width, cfg.savelines);
1277 InvalidateRect(hwnd, NULL, TRUE);
1280 SetWindowPos (hwnd, NULL, 0, 0,
1281 extra_width + font_width * cfg.width,
1282 extra_height + font_height * cfg.height,
1283 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1284 SWP_NOMOVE | SWP_NOZORDER);
1286 set_title(cfg.wintitle);
1287 if (IsIconic(hwnd)) {
1288 SetWindowText (hwnd,
1289 cfg.win_name_always ? window_name : icon_name);
1302 case IDM_TEL_AYT: back->special (TS_AYT); break;
1303 case IDM_TEL_BRK: back->special (TS_BRK); break;
1304 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1305 case IDM_TEL_EC: back->special (TS_EC); break;
1306 case IDM_TEL_EL: back->special (TS_EL); break;
1307 case IDM_TEL_GA: back->special (TS_GA); break;
1308 case IDM_TEL_NOP: back->special (TS_NOP); break;
1309 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1310 case IDM_TEL_AO: back->special (TS_AO); break;
1311 case IDM_TEL_IP: back->special (TS_IP); break;
1312 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1313 case IDM_TEL_EOR: back->special (TS_EOR); break;
1314 case IDM_TEL_EOF: back->special (TS_EOF); break;
1319 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1320 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1325 #define X_POS(l) ((int)(short)LOWORD(l))
1326 #define Y_POS(l) ((int)(short)HIWORD(l))
1328 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1329 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1331 case WM_LBUTTONDOWN:
1332 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1333 TO_CHR_Y(Y_POS(lParam)));
1337 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1338 TO_CHR_Y(Y_POS(lParam)));
1341 case WM_MBUTTONDOWN:
1343 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1344 TO_CHR_X(X_POS(lParam)),
1345 TO_CHR_Y(Y_POS(lParam)));
1348 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1349 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1350 TO_CHR_Y(Y_POS(lParam)));
1353 case WM_RBUTTONDOWN:
1355 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1356 TO_CHR_X(X_POS(lParam)),
1357 TO_CHR_Y(Y_POS(lParam)));
1360 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1361 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1362 TO_CHR_Y(Y_POS(lParam)));
1367 * Add the mouse position and message time to the random
1370 noise_ultralight(lParam);
1372 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1374 if (wParam & MK_LBUTTON)
1376 else if (wParam & MK_MBUTTON)
1377 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1379 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1380 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1381 TO_CHR_Y(Y_POS(lParam)));
1384 case WM_IGNORE_CLIP:
1385 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1387 case WM_DESTROYCLIPBOARD:
1390 ignore_clip = FALSE;
1396 hdc = BeginPaint (hwnd, &p);
1398 SelectPalette (hdc, pal, TRUE);
1399 RealizePalette (hdc);
1401 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1402 p.rcPaint.right, p.rcPaint.bottom);
1403 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1404 SelectObject (hdc, GetStockObject(WHITE_PEN));
1405 EndPaint (hwnd, &p);
1410 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1411 * but the only one that's likely to try to overload us is FD_READ.
1412 * This means buffering just one is fine.
1414 if (pending_netevent)
1415 enact_pending_netevent();
1417 pending_netevent = TRUE;
1418 pend_netevent_wParam=wParam;
1419 pend_netevent_lParam=lParam;
1420 time(&last_movement);
1424 CreateCaret(hwnd, caretbm, font_width, font_height);
1436 case WM_IGNORE_SIZE:
1437 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1439 case WM_ENTERSIZEMOVE:
1442 need_backend_resize = FALSE;
1444 case WM_EXITSIZEMOVE:
1447 if (need_backend_resize)
1452 int width, height, w, h, ew, eh;
1453 LPRECT r = (LPRECT)lParam;
1455 width = r->right - r->left - extra_width;
1456 height = r->bottom - r->top - extra_height;
1457 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1458 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1459 UpdateSizeTip(hwnd, w, h);
1460 ew = width - w * font_width;
1461 eh = height - h * font_height;
1463 if (wParam == WMSZ_LEFT ||
1464 wParam == WMSZ_BOTTOMLEFT ||
1465 wParam == WMSZ_TOPLEFT)
1471 if (wParam == WMSZ_TOP ||
1472 wParam == WMSZ_TOPRIGHT ||
1473 wParam == WMSZ_TOPLEFT)
1483 /* break; (never reached) */
1485 if (wParam == SIZE_MINIMIZED) {
1486 SetWindowText (hwnd,
1487 cfg.win_name_always ? window_name : icon_name);
1490 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1491 SetWindowText (hwnd, window_name);
1493 int width, height, w, h;
1494 #if 0 /* we have fixed this using WM_SIZING now */
1498 width = LOWORD(lParam);
1499 height = HIWORD(lParam);
1500 w = width / font_width; if (w < 1) w = 1;
1501 h = height / font_height; if (h < 1) h = 1;
1502 #if 0 /* we have fixed this using WM_SIZING now */
1503 ew = width - w * font_width;
1504 eh = height - h * font_height;
1505 if (ew != 0 || eh != 0) {
1507 GetWindowRect (hwnd, &r);
1508 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1509 SetWindowPos (hwnd, NULL, 0, 0,
1510 r.right - r.left - ew, r.bottom - r.top - eh,
1511 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1514 if (w != cols || h != rows || just_reconfigged) {
1516 term_size (h, w, cfg.savelines);
1518 * Don't call back->size in mid-resize. (To prevent
1519 * massive numbers of resize events getting sent
1520 * down the connection during an NT opaque drag.)
1525 need_backend_resize = TRUE;
1526 just_reconfigged = FALSE;
1529 ignore_size = FALSE;
1532 switch (LOWORD(wParam)) {
1533 case SB_BOTTOM: term_scroll(-1, 0); break;
1534 case SB_TOP: term_scroll(+1, 0); break;
1535 case SB_LINEDOWN: term_scroll (0, +1); break;
1536 case SB_LINEUP: term_scroll (0, -1); break;
1537 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1538 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1539 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1540 term_scroll (1, HIWORD(wParam)); break;
1543 case WM_PALETTECHANGED:
1544 if ((HWND) wParam != hwnd && pal != NULL) {
1545 HDC hdc = get_ctx();
1547 if (RealizePalette (hdc) > 0)
1553 case WM_QUERYNEWPALETTE:
1555 HDC hdc = get_ctx();
1557 if (RealizePalette (hdc) > 0)
1569 * Add the scan code and keypress timing to the random
1572 noise_ultralight(lParam);
1575 * We don't do TranslateMessage since it disassociates the
1576 * resulting CHAR message from the KEYDOWN that sparked it,
1577 * which we occasionally don't want. Instead, we process
1578 * KEYDOWN, and call the Win32 translator functions so that
1579 * we get the translations under _our_ control.
1582 unsigned char buf[20];
1585 if (wParam==VK_PROCESSKEY) {
1588 m.message = WM_KEYDOWN;
1590 m.lParam = lParam & 0xdfff;
1591 TranslateMessage(&m);
1593 len = TranslateKey (message, wParam, lParam, buf);
1595 return DefWindowProc (hwnd, message, wParam, lParam);
1596 ldisc->send (buf, len);
1602 unsigned char buf[2];
1605 buf[0] = wParam >> 8;
1606 ldisc->send (buf, 2);
1611 * Nevertheless, we are prepared to deal with WM_CHAR
1612 * messages, should they crop up. So if someone wants to
1613 * post the things to us as part of a macro manoeuvre,
1614 * we're ready to cope.
1617 char c = xlat_kbd2tty((unsigned char)wParam);
1618 ldisc->send (&c, 1);
1623 return DefWindowProc (hwnd, message, wParam, lParam);
1627 * Move the system caret. (We maintain one, even though it's
1628 * invisible, for the benefit of blind people: apparently some
1629 * helper software tracks the system caret, so we should arrange to
1632 void sys_cursor(int x, int y) {
1633 SetCaretPos(x * font_width, y * font_height);
1637 * Draw a line of text in the window, at given character
1638 * coordinates, in given attributes.
1640 * We are allowed to fiddle with the contents of `text'.
1642 void do_text (Context ctx, int x, int y, char *text, int len,
1643 unsigned long attr, int lattr) {
1645 int nfg, nbg, nfont;
1648 int force_manual_underline = 0;
1649 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1650 static int *IpDx = 0, IpDxLEN = 0;;
1652 if (len>IpDxLEN || IpDx[0] != fnt_width) {
1656 IpDx = smalloc((len+16)*sizeof(int));
1659 for(i=0; i<IpDxLEN; i++)
1660 IpDx[i] = fnt_width;
1666 if ((attr & ATTR_ACTCURS) && cfg.cursor_type == 0) {
1667 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1668 attr ^= ATTR_CUR_XOR;
1672 if (cfg.vtmode == VT_OEMONLY)
1676 * Map high-half characters in order to approximate ISO using
1677 * OEM character set. No characters are missing if the OEM codepage
1680 if (nfont & FONT_OEM) {
1682 for (i=0; i<len; i++)
1683 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1685 /* This is CP850 ... perfect translation */
1686 static const char oemhighhalf[] =
1687 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1688 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1689 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1690 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1691 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1692 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1693 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1694 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1695 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1696 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1697 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1698 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1701 /* This is CP437 ... junk translation */
1702 static const unsigned char oemhighhalf[] = {
1703 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1704 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1705 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1706 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1707 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1708 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1709 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1710 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1711 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1712 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1713 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1714 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1717 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1721 if (attr & ATTR_LINEDRW) {
1724 static const char poorman[] =
1725 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1728 static const char oemmap_437[] =
1729 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1730 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1733 static const char oemmap_850[] =
1734 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1735 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1737 /* Poor windows font ... eg: windows courier */
1738 static const char oemmap[] =
1739 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1740 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1743 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1744 * VT100 line drawing chars; everything else stays normal.
1746 * Actually '_' maps to space too, but that's done before.
1748 switch (cfg.vtmode) {
1750 for (i=0; i<len; i++)
1751 if (text[i] >= '\x60' && text[i] <= '\x7E')
1752 text[i] += '\x01' - '\x60';
1755 /* Make sure we actually have an OEM font */
1756 if (fonts[nfont|FONT_OEM]) {
1759 for (i=0; i<len; i++)
1760 if (text[i] >= '\x60' && text[i] <= '\x7E')
1761 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1765 for (i=0; i<len; i++)
1766 if (text[i] >= '\x60' && text[i] <= '\x7E')
1767 text[i] = poorman[(unsigned char)text[i] - 0x60];
1772 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1773 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1774 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1776 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1777 nfont |= FONT_UNDERLINE;
1780 if (nfont&FONT_UNDERLINE)
1781 force_manual_underline = 1;
1782 /* Don't do the same for manual bold, it could be bad news. */
1784 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1786 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1787 force_manual_underline = 1;
1788 if (attr & ATTR_REVERSE) {
1789 t = nfg; nfg = nbg; nbg = t;
1791 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1793 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1797 SelectObject (hdc, fonts[nfont]);
1798 SetTextColor (hdc, fg);
1799 SetBkColor (hdc, bg);
1800 SetBkMode (hdc, OPAQUE);
1803 line_box.right = x+fnt_width*len;
1804 line_box.bottom = y+font_height;
1805 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1806 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1807 SetBkMode (hdc, TRANSPARENT);
1809 /* GRR: This draws the character outside it's box and can leave
1810 * 'droppings' even with the clip box! I suppose I could loop it
1811 * one character at a time ... yuk.
1813 * Or ... I could do a test print with "W", and use +1 or -1 for this
1814 * shift depending on if the leftmost column is blank...
1816 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1818 if (force_manual_underline ||
1819 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1821 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1822 MoveToEx (hdc, x, y+descent, NULL);
1823 LineTo (hdc, x+len*fnt_width, y+descent);
1824 oldpen = SelectObject (hdc, oldpen);
1825 DeleteObject (oldpen);
1827 if ((attr & ATTR_PASCURS) && cfg.cursor.type == 0) {
1830 pts[0].x = pts[1].x = pts[4].x = x;
1831 pts[2].x = pts[3].x = x+fnt_width-1;
1832 pts[0].y = pts[3].y = pts[4].y = y;
1833 pts[1].y = pts[2].y = y+font_height-1;
1834 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1835 Polyline (hdc, pts, 5);
1836 oldpen = SelectObject (hdc, oldpen);
1837 DeleteObject (oldpen);
1839 if ((attr & (ATTR_ACTCURS | ATTR_PASCURS)) && cfg.cursor_type != 0) {
1842 if (attr & ATTR_PASCURS)
1843 pentype = PS_DOTTED;
1846 oldpen = SelectObject (hdc, CreatePen(pentype, 0, colours[23]));
1847 if (cfg.cursor_type == 1) {
1848 MoveToEx (hdc, x, y+descent, NULL);
1849 LineTo (hdc, x+fnt_width-1, y+descent);
1852 if (attr & ATTR_RIGHTCURS)
1853 xadjust = fnt_width-1;
1854 MoveToEx (hdc, x+xadjust, y, NULL);
1855 LineTo (hdc, x+xadjust, y+font_height-1);
1857 oldpen = SelectObject (hdc, oldpen);
1858 DeleteObject (oldpen);
1862 static int check_compose(int first, int second) {
1864 static char * composetbl[] = {
1865 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1866 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1867 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1868 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1869 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1870 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1871 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1872 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1873 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1874 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1875 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1876 "\"uü", "'yý", "htþ", "\"yÿ",
1880 static int recurse = 0;
1883 for(c=composetbl; *c; c++) {
1884 if( (*c)[0] == first && (*c)[1] == second)
1886 return (*c)[2] & 0xFF;
1893 nc = check_compose(second, first);
1895 nc = check_compose(toupper(first), toupper(second));
1897 nc = check_compose(toupper(second), toupper(first));
1905 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1906 * codes. Returns number of bytes used or zero to drop the message
1907 * or -1 to forward the message to windows.
1909 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
1910 unsigned char *output) {
1912 int scan, left_alt = 0, key_down, shift_state;
1914 unsigned char * p = output;
1915 static int alt_state = 0;
1917 HKL kbd_layout = GetKeyboardLayout(0);
1919 static WORD keys[3];
1920 static int compose_char = 0;
1921 static WPARAM compose_key = 0;
1923 r = GetKeyboardState(keystate);
1924 if (!r) memset(keystate, 0, sizeof(keystate));
1928 { /* Tell us all about key events */
1929 static BYTE oldstate[256];
1930 static int first = 1;
1933 if(first) memcpy(oldstate, keystate, sizeof(oldstate));
1936 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT) {
1938 } else if ((HIWORD(lParam)&KF_UP) && scan==(HIWORD(lParam) & 0xFF) ) {
1942 if (wParam >= VK_F1 && wParam <= VK_F20 )
1943 debug(("K_F%d", wParam+1-VK_F1));
1946 case VK_SHIFT: debug(("SHIFT")); break;
1947 case VK_CONTROL: debug(("CTRL")); break;
1948 case VK_MENU: debug(("ALT")); break;
1949 default: debug(("VK_%02x", wParam));
1951 if(message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
1953 debug((", S%02x", scan=(HIWORD(lParam) & 0xFF) ));
1955 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
1956 if (ch>=' ' && ch<='~') debug((", '%c'", ch));
1957 else if (ch) debug((", $%02x", ch));
1959 if (keys[0]) debug((", KB0=%02x", keys[0]));
1960 if (keys[1]) debug((", KB1=%02x", keys[1]));
1961 if (keys[2]) debug((", KB2=%02x", keys[2]));
1963 if ( (keystate[VK_SHIFT]&0x80)!=0) debug((", S"));
1964 if ( (keystate[VK_CONTROL]&0x80)!=0) debug((", C"));
1965 if ( (HIWORD(lParam)&KF_EXTENDED) ) debug((", E"));
1966 if ( (HIWORD(lParam)&KF_UP) ) debug((", U"));
1969 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
1971 else if ( (HIWORD(lParam)&KF_UP) )
1972 oldstate[wParam&0xFF] ^= 0x80;
1974 oldstate[wParam&0xFF] ^= 0x81;
1976 for(ch=0; ch<256; ch++)
1977 if (oldstate[ch] != keystate[ch])
1978 debug((", M%02x=%02x", ch, keystate[ch]));
1980 memcpy(oldstate, keystate, sizeof(oldstate));
1984 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED)) {
1985 keystate[VK_RMENU] = keystate[VK_MENU];
1988 /* Note if AltGr was pressed and if it was used as a compose key */
1989 if (cfg.compose_key) {
1990 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
1992 if (!compose_state) compose_key = wParam;
1994 if (wParam == VK_APPS && !compose_state)
1995 compose_key = wParam;
1997 if (wParam == compose_key)
1999 if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
2001 else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
2006 else if (compose_state==1 && wParam != VK_CONTROL)
2012 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2013 if ( (cfg.funky_type == 3 ||
2014 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2015 && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
2017 wParam = VK_EXECUTE;
2019 /* UnToggle NUMLock */
2020 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
2021 keystate[VK_NUMLOCK] ^= 1;
2024 /* And write back the 'adjusted' state */
2025 SetKeyboardState (keystate);
2028 /* Disable Auto repeat if required */
2029 if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
2032 if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
2035 key_down = ((HIWORD(lParam)&KF_UP)==0);
2037 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
2038 if (left_alt && (keystate[VK_CONTROL]&0x80))
2039 keystate[VK_MENU] = 0;
2041 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2042 shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
2043 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
2046 * Record that we pressed key so the scroll window can be reset, but
2047 * be careful to avoid Shift-UP/Down
2049 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
2053 /* Make sure we're not pasting */
2054 if (key_down) term_nopaste();
2056 if (compose_state>1 && left_alt) compose_state = 0;
2058 /* Sanitize the number pad if not using a PC NumPad */
2059 if( left_alt || (app_keypad_keys && !cfg.no_applic_k
2060 && cfg.funky_type != 2)
2061 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state )
2063 if ((HIWORD(lParam)&KF_EXTENDED) == 0)
2068 case VK_INSERT: nParam = VK_NUMPAD0; break;
2069 case VK_END: nParam = VK_NUMPAD1; break;
2070 case VK_DOWN: nParam = VK_NUMPAD2; break;
2071 case VK_NEXT: nParam = VK_NUMPAD3; break;
2072 case VK_LEFT: nParam = VK_NUMPAD4; break;
2073 case VK_CLEAR: nParam = VK_NUMPAD5; break;
2074 case VK_RIGHT: nParam = VK_NUMPAD6; break;
2075 case VK_HOME: nParam = VK_NUMPAD7; break;
2076 case VK_UP: nParam = VK_NUMPAD8; break;
2077 case VK_PRIOR: nParam = VK_NUMPAD9; break;
2078 case VK_DELETE: nParam = VK_DECIMAL; break;
2082 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
2088 /* If a key is pressed and AltGr is not active */
2089 if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
2091 /* Okay, prepare for most alts then ...*/
2092 if (left_alt) *p++ = '\033';
2094 /* Lets see if it's a pattern we know all about ... */
2095 if (wParam == VK_PRIOR && shift_state == 1) {
2096 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2099 if (wParam == VK_NEXT && shift_state == 1) {
2100 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2103 if (wParam == VK_INSERT && shift_state == 1) {
2104 term_mouse (MB_PASTE, MA_CLICK, 0, 0);
2105 term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
2108 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2111 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2113 PostMessage(hwnd, WM_CHAR, ' ', 0);
2114 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2117 /* Control-Numlock for app-keypad mode switch */
2118 if (wParam == VK_PAUSE && shift_state == 2) {
2119 app_keypad_keys ^= 1;
2123 /* Nethack keypad */
2124 if (cfg.nethack_keypad && !left_alt) {
2126 case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
2127 case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
2128 case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
2129 case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
2130 case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
2131 case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
2132 case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
2133 case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
2134 case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
2138 /* Application Keypad */
2142 if ( cfg.funky_type == 3 ||
2143 ( cfg.funky_type <= 1 &&
2144 app_keypad_keys && !cfg.no_applic_k)) switch(wParam) {
2145 case VK_EXECUTE: xkey = 'P'; break;
2146 case VK_DIVIDE: xkey = 'Q'; break;
2147 case VK_MULTIPLY:xkey = 'R'; break;
2148 case VK_SUBTRACT:xkey = 'S'; break;
2150 if(app_keypad_keys && !cfg.no_applic_k) switch(wParam) {
2151 case VK_NUMPAD0: xkey = 'p'; break;
2152 case VK_NUMPAD1: xkey = 'q'; break;
2153 case VK_NUMPAD2: xkey = 'r'; break;
2154 case VK_NUMPAD3: xkey = 's'; break;
2155 case VK_NUMPAD4: xkey = 't'; break;
2156 case VK_NUMPAD5: xkey = 'u'; break;
2157 case VK_NUMPAD6: xkey = 'v'; break;
2158 case VK_NUMPAD7: xkey = 'w'; break;
2159 case VK_NUMPAD8: xkey = 'x'; break;
2160 case VK_NUMPAD9: xkey = 'y'; break;
2162 case VK_DECIMAL: xkey = 'n'; break;
2163 case VK_ADD: if(cfg.funky_type==2) {
2164 if(shift_state) xkey = 'l';
2166 } else if(shift_state) xkey = 'm';
2170 case VK_DIVIDE: if(cfg.funky_type==2) xkey = 'o'; break;
2171 case VK_MULTIPLY:if(cfg.funky_type==2) xkey = 'j'; break;
2172 case VK_SUBTRACT:if(cfg.funky_type==2) xkey = 'm'; break;
2175 if (HIWORD(lParam)&KF_EXTENDED)
2183 if (xkey>='P' && xkey<='S')
2184 p += sprintf((char *)p, "\x1B%c", xkey);
2186 p += sprintf((char *)p, "\x1B?%c", xkey);
2189 p += sprintf((char *)p, "\x1BO%c", xkey);
2194 if (wParam == VK_BACK && shift_state == 0 ) /* Backspace */
2196 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2199 if (wParam == VK_TAB && shift_state == 1 ) /* Shift tab */
2201 *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
2203 if (wParam == VK_SPACE && shift_state == 2 ) /* Ctrl-Space */
2205 *p++ = 0; return p - output;
2207 if (wParam == VK_SPACE && shift_state == 3 ) /* Ctrl-Shift-Space */
2209 *p++ = 160; return p - output;
2211 if (wParam == VK_CANCEL && shift_state == 2 ) /* Ctrl-Break */
2213 *p++ = 3; return p - output;
2215 /* Control-2 to Control-8 are special */
2216 if (shift_state == 2 && wParam >= '2' && wParam <= '8')
2218 *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
2221 if (shift_state == 2 && wParam == 0xBD) {
2225 if (shift_state == 2 && wParam == 0xDF) {
2229 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2230 *p++ = '\r'; *p++ = '\n';
2235 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2236 * for integer decimal nn.)
2238 * We also deal with the weird ones here. Linux VCs replace F1
2239 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2240 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2245 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
2246 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
2247 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
2248 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
2249 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
2250 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
2251 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
2252 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
2253 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
2254 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
2255 case VK_F11: code = 23; break;
2256 case VK_F12: code = 24; break;
2257 case VK_F13: code = 25; break;
2258 case VK_F14: code = 26; break;
2259 case VK_F15: code = 28; break;
2260 case VK_F16: code = 29; break;
2261 case VK_F17: code = 31; break;
2262 case VK_F18: code = 32; break;
2263 case VK_F19: code = 33; break;
2264 case VK_F20: code = 34; break;
2265 case VK_HOME: code = 1; break;
2266 case VK_INSERT: code = 2; break;
2267 case VK_DELETE: code = 3; break;
2268 case VK_END: code = 4; break;
2269 case VK_PRIOR: code = 5; break;
2270 case VK_NEXT: code = 6; break;
2272 /* Reorder edit keys to physical order */
2273 if (cfg.funky_type == 3 && code <= 6 ) code = "\0\2\1\4\5\3\6"[code];
2275 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2276 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
2279 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2281 p += sprintf((char *)p, "\x1B%c", code + 'P' - 11);
2283 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
2286 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2287 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
2291 p += sprintf((char *)p, "\x1B[%d~", code);
2296 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2297 * some reason seems to send VK_CLEAR to Windows...).
2302 case VK_UP: xkey = 'A'; break;
2303 case VK_DOWN: xkey = 'B'; break;
2304 case VK_RIGHT: xkey = 'C'; break;
2305 case VK_LEFT: xkey = 'D'; break;
2306 case VK_CLEAR: xkey = 'G'; break;
2311 p += sprintf((char *)p, "\x1B%c", xkey);
2312 else if (app_cursor_keys && !cfg.no_applic_c)
2313 p += sprintf((char *)p, "\x1BO%c", xkey);
2315 p += sprintf((char *)p, "\x1B[%c", xkey);
2321 * Finally, deal with Return ourselves. (Win95 seems to
2322 * foul it up when Alt is pressed, for some reason.)
2324 if (wParam == VK_RETURN) /* Return */
2331 /* Okay we've done everything interesting; let windows deal with
2332 * the boring stuff */
2334 BOOL capsOn=keystate[VK_CAPITAL] !=0;
2336 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2337 if(cfg.xlat_capslockcyr)
2338 keystate[VK_CAPITAL] = 0;
2340 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
2346 unsigned char ch = (unsigned char)keys[i];
2348 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
2353 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
2357 if ((nc=check_compose(compose_char,ch)) == -1)
2359 MessageBeep(MB_ICONHAND);
2362 *p++ = xlat_kbd2tty((unsigned char)nc);
2368 if( left_alt && key_down ) *p++ = '\033';
2374 ch = xlat_latkbd2win(ch);
2375 *p++ = xlat_kbd2tty(ch);
2379 /* This is so the ALT-Numpad and dead keys work correctly. */
2386 /* ALT alone may or may not want to bring up the System menu */
2387 if (wParam == VK_MENU) {
2389 if (message == WM_SYSKEYDOWN)
2391 else if (message == WM_SYSKEYUP && alt_state)
2392 PostMessage(hwnd, WM_CHAR, ' ', 0);
2393 if (message == WM_SYSKEYUP)
2402 void set_title (char *title) {
2403 sfree (window_name);
2404 window_name = smalloc(1+strlen(title));
2405 strcpy (window_name, title);
2406 if (cfg.win_name_always || !IsIconic(hwnd))
2407 SetWindowText (hwnd, title);
2410 void set_icon (char *title) {
2412 icon_name = smalloc(1+strlen(title));
2413 strcpy (icon_name, title);
2414 if (!cfg.win_name_always && IsIconic(hwnd))
2415 SetWindowText (hwnd, title);
2418 void set_sbar (int total, int start, int page) {
2421 if (!cfg.scrollbar) return;
2423 si.cbSize = sizeof(si);
2424 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2426 si.nMax = total - 1;
2430 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2433 Context get_ctx(void) {
2438 SelectPalette (hdc, pal, FALSE);
2444 void free_ctx (Context ctx) {
2445 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2446 ReleaseDC (hwnd, ctx);
2449 static void real_palette_set (int n, int r, int g, int b) {
2451 logpal->palPalEntry[n].peRed = r;
2452 logpal->palPalEntry[n].peGreen = g;
2453 logpal->palPalEntry[n].peBlue = b;
2454 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2455 colours[n] = PALETTERGB(r, g, b);
2456 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2458 colours[n] = RGB(r, g, b);
2461 void palette_set (int n, int r, int g, int b) {
2462 static const int first[21] = {
2463 0, 2, 4, 6, 8, 10, 12, 14,
2464 1, 3, 5, 7, 9, 11, 13, 15,
2467 real_palette_set (first[n], r, g, b);
2469 real_palette_set (first[n]+1, r, g, b);
2471 HDC hdc = get_ctx();
2472 UnrealizeObject (pal);
2473 RealizePalette (hdc);
2478 void palette_reset (void) {
2481 for (i = 0; i < NCOLOURS; i++) {
2483 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2484 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2485 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2486 logpal->palPalEntry[i].peFlags = 0;
2487 colours[i] = PALETTERGB(defpal[i].rgbtRed,
2488 defpal[i].rgbtGreen,
2489 defpal[i].rgbtBlue);
2491 colours[i] = RGB(defpal[i].rgbtRed,
2492 defpal[i].rgbtGreen,
2493 defpal[i].rgbtBlue);
2498 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2500 RealizePalette (hdc);
2505 void write_clip (void *data, int len, int must_deselect) {
2509 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2512 lock = GlobalLock (clipdata);
2515 memcpy (lock, data, len);
2516 ((unsigned char *) lock) [len] = 0;
2517 GlobalUnlock (clipdata);
2520 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2522 if (OpenClipboard (hwnd)) {
2524 SetClipboardData (CF_TEXT, clipdata);
2527 GlobalFree (clipdata);
2530 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2533 void get_clip (void **p, int *len) {
2534 static HGLOBAL clipdata = NULL;
2538 GlobalUnlock (clipdata);
2542 if (OpenClipboard (NULL)) {
2543 clipdata = GetClipboardData (CF_TEXT);
2546 *p = GlobalLock (clipdata);
2560 * Move `lines' lines from position `from' to position `to' in the
2563 void optimised_move (int to, int from, int lines) {
2567 min = (to < from ? to : from);
2568 max = to + from - min;
2570 r.left = 0; r.right = cols * font_width;
2571 r.top = min * font_height; r.bottom = (max+lines) * font_height;
2572 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2576 * Print a message box and perform a fatal exit.
2578 void fatalbox(char *fmt, ...) {
2583 vsprintf(stuff, fmt, ap);
2585 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2592 void beep(int errorbeep) {
2593 static long last_beep = 0;
2594 long now, beep_diff;
2596 now = GetTickCount();
2597 beep_diff = now-last_beep;
2599 /* Make sure we only respond to one beep per packet or so */
2600 if (beep_diff>=0 && beep_diff<50)
2604 MessageBeep(MB_ICONHAND);
2608 last_beep = GetTickCount();