17 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
23 #define IDM_SHOWLOG 0x0010
24 #define IDM_NEWSESS 0x0020
25 #define IDM_DUPSESS 0x0030
26 #define IDM_RECONF 0x0040
27 #define IDM_CLRSB 0x0050
28 #define IDM_RESET 0x0060
29 #define IDM_TEL_AYT 0x0070
30 #define IDM_TEL_BRK 0x0080
31 #define IDM_TEL_SYNCH 0x0090
32 #define IDM_TEL_EC 0x00a0
33 #define IDM_TEL_EL 0x00b0
34 #define IDM_TEL_GA 0x00c0
35 #define IDM_TEL_NOP 0x00d0
36 #define IDM_TEL_ABORT 0x00e0
37 #define IDM_TEL_AO 0x00f0
38 #define IDM_TEL_IP 0x0100
39 #define IDM_TEL_SUSP 0x0110
40 #define IDM_TEL_EOR 0x0120
41 #define IDM_TEL_EOF 0x0130
42 #define IDM_ABOUT 0x0140
43 #define IDM_SAVEDSESS 0x0150
44 #define IDM_COPYALL 0x0160
46 #define IDM_SESSLGP 0x0250 /* log type printable */
47 #define IDM_SESSLGA 0x0260 /* log type all chars */
48 #define IDM_SESSLGE 0x0270 /* log end */
49 #define IDM_SAVED_MIN 0x1000
50 #define IDM_SAVED_MAX 0x2000
52 #define WM_IGNORE_SIZE (WM_XUSER + 1)
53 #define WM_IGNORE_CLIP (WM_XUSER + 2)
55 /* Needed for Chinese support and apparently not always defined. */
57 #define VK_PROCESSKEY 0xE5
60 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
61 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
62 unsigned char *output);
63 static void cfgtopalette(void);
64 static void init_palette(void);
65 static void init_fonts(int);
67 static int extra_width, extra_height;
69 static int pending_netevent = 0;
70 static WPARAM pend_netevent_wParam = 0;
71 static LPARAM pend_netevent_lParam = 0;
72 static void enact_pending_netevent(void);
74 static time_t last_movement = 0;
78 #define FONT_UNDERLINE 2
79 #define FONT_BOLDUND 3
81 #define FONT_OEMBOLD 5
82 #define FONT_OEMBOLDUND 6
84 static HFONT fonts[8];
85 static int font_needs_hand_underlining;
87 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
95 static COLORREF colours[NCOLOURS];
97 static LPLOGPALETTE logpal;
98 static RGBTRIPLE defpal[NCOLOURS];
102 static HBITMAP caretbm;
104 static int dbltime, lasttime, lastact;
105 static Mouse_Button lastbtn;
107 /* this allows xterm-style mouse handling. */
108 static int send_raw_mouse = 0;
109 static int wheel_accumulator = 0;
111 static char *window_name, *icon_name;
113 static int compose_state = 0;
115 /* Dummy routine, only required in plink. */
116 void ldisc_update(int echo, int edit)
120 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
122 static char appname[] = "PuTTY";
127 int guess_width, guess_height;
130 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
132 winsock_ver = MAKEWORD(1, 1);
133 if (WSAStartup(winsock_ver, &wsadata)) {
134 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
135 MB_OK | MB_ICONEXCLAMATION);
138 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
139 MessageBox(NULL, "WinSock version is incompatible with 1.1",
140 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
144 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
147 InitCommonControls();
149 /* Ensure a Maximize setting in Explorer doesn't maximise the
154 * Process the command line.
159 default_protocol = DEFAULT_PROTOCOL;
160 default_port = DEFAULT_PORT;
161 cfg.logtype = LGTYP_NONE;
163 do_defaults(NULL, &cfg);
166 while (*p && isspace(*p))
170 * Process command line options first. Yes, this can be
171 * done better, and it will be as soon as I have the
175 char *q = p + strcspn(p, " \t");
178 tolower(p[0]) == 's' &&
179 tolower(p[1]) == 's' && tolower(p[2]) == 'h') {
180 default_protocol = cfg.protocol = PROT_SSH;
181 default_port = cfg.port = 22;
182 } else if (q == p + 7 &&
183 tolower(p[0]) == 'c' &&
184 tolower(p[1]) == 'l' &&
185 tolower(p[2]) == 'e' &&
186 tolower(p[3]) == 'a' &&
187 tolower(p[4]) == 'n' &&
188 tolower(p[5]) == 'u' && tolower(p[6]) == 'p') {
190 * `putty -cleanup'. Remove all registry entries
191 * associated with PuTTY, and also find and delete
192 * the random seed file.
195 "This procedure will remove ALL Registry\n"
196 "entries associated with PuTTY, and will\n"
197 "also remove the PuTTY random seed file.\n"
199 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
200 "SESSIONS. Are you really sure you want\n"
203 MB_YESNO | MB_ICONWARNING) == IDYES) {
208 p = q + strspn(q, " \t");
212 * An initial @ means to activate a saved session.
216 while (i > 1 && isspace(p[i - 1]))
219 do_defaults(p + 1, &cfg);
220 if (!*cfg.host && !do_config()) {
224 } else if (*p == '&') {
226 * An initial & means we've been given a command line
227 * containing the hex value of a HANDLE for a file
228 * mapping object, which we must then extract as a
233 if (sscanf(p + 1, "%p", &filemap) == 1 &&
234 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
235 0, 0, sizeof(Config))) != NULL) {
238 CloseHandle(filemap);
239 } else if (!do_config()) {
246 * If the hostname starts with "telnet:", set the
247 * protocol to Telnet and process the string as a
250 if (!strncmp(q, "telnet:", 7)) {
254 if (q[0] == '/' && q[1] == '/')
256 cfg.protocol = PROT_TELNET;
258 while (*p && *p != ':' && *p != '/')
267 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
268 cfg.host[sizeof(cfg.host) - 1] = '\0';
270 while (*p && !isspace(*p))
274 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
275 cfg.host[sizeof(cfg.host) - 1] = '\0';
276 while (*p && isspace(*p))
290 /* See if host is of the form user@host */
291 if (cfg.host[0] != '\0') {
292 char *atsign = strchr(cfg.host, '@');
293 /* Make sure we're not overflowing the user field */
295 if (atsign - cfg.host < sizeof cfg.username) {
296 strncpy(cfg.username, cfg.host, atsign - cfg.host);
297 cfg.username[atsign - cfg.host] = '\0';
299 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
304 * Trim a colon suffix off the hostname if it's there.
306 cfg.host[strcspn(cfg.host, ":")] = '\0';
310 * Select protocol. This is farmed out into a table in a
311 * separate file to enable an ssh-free variant.
316 for (i = 0; backends[i].backend != NULL; i++)
317 if (backends[i].protocol == cfg.protocol) {
318 back = backends[i].backend;
322 MessageBox(NULL, "Unsupported protocol number found",
323 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
329 /* Check for invalid Port number (i.e. zero) */
331 MessageBox(NULL, "Invalid Port Number",
332 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
339 wndclass.lpfnWndProc = WndProc;
340 wndclass.cbClsExtra = 0;
341 wndclass.cbWndExtra = 0;
342 wndclass.hInstance = inst;
343 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
344 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
345 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
346 wndclass.lpszMenuName = NULL;
347 wndclass.lpszClassName = appname;
349 RegisterClass(&wndclass);
354 savelines = cfg.savelines;
360 * Guess some defaults for the window size. This all gets
361 * updated later, so we don't really care too much. However, we
362 * do want the font width/height guesses to correspond to a
363 * large font rather than a small one...
370 term_size(cfg.height, cfg.width, cfg.savelines);
371 guess_width = extra_width + font_width * cols;
372 guess_height = extra_height + font_height * rows;
375 HWND w = GetDesktopWindow();
376 GetWindowRect(w, &r);
377 if (guess_width > r.right - r.left)
378 guess_width = r.right - r.left;
379 if (guess_height > r.bottom - r.top)
380 guess_height = r.bottom - r.top;
384 int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
387 winmode &= ~(WS_VSCROLL);
389 winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
391 exwinmode |= WS_EX_TOPMOST;
393 exwinmode |= WS_EX_CLIENTEDGE;
394 hwnd = CreateWindowEx(exwinmode, appname, appname,
395 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
396 guess_width, guess_height,
397 NULL, NULL, inst, NULL);
401 * Initialise the fonts, simultaneously correcting the guesses
402 * for font_{width,height}.
404 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
409 * Correct the guesses for extra_{width,height}.
413 GetWindowRect(hwnd, &wr);
414 GetClientRect(hwnd, &cr);
415 extra_width = wr.right - wr.left - cr.right + cr.left;
416 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
420 * Resize the window, now we know what size we _really_ want it
423 guess_width = extra_width + font_width * cols;
424 guess_height = extra_height + font_height * rows;
425 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
426 SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
427 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
430 * Set up a caret bitmap, with no content.
434 int size = (font_width + 15) / 16 * 2 * font_height;
435 bits = smalloc(size);
436 memset(bits, 0, size);
437 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
440 CreateCaret(hwnd, caretbm, font_width, font_height);
443 * Initialise the scroll bar.
448 si.cbSize = sizeof(si);
449 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
454 SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
458 * Start up the telnet connection.
462 char msg[1024], *title;
465 error = back->init(cfg.host, cfg.port, &realhost);
467 sprintf(msg, "Unable to open connection to\n"
468 "%.800s\n" "%s", cfg.host, error);
469 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
472 window_name = icon_name = NULL;
474 title = cfg.wintitle;
476 sprintf(msg, "%s - PuTTY", realhost);
483 session_closed = FALSE;
486 * Set up the input and output buffers.
489 outbuf_reap = outbuf_head = 0;
492 * Prepare the mouse handler.
494 lastact = MA_NOTHING;
495 lastbtn = MBT_NOTHING;
496 dbltime = GetDoubleClickTime();
499 * Set up the session-control options on the system menu.
502 HMENU m = GetSystemMenu(hwnd, FALSE);
506 AppendMenu(m, MF_SEPARATOR, 0, 0);
507 if (cfg.protocol == PROT_TELNET) {
509 AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
510 AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
511 AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
512 AppendMenu(p, MF_SEPARATOR, 0, 0);
513 AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
514 AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
515 AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
516 AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
517 AppendMenu(p, MF_SEPARATOR, 0, 0);
518 AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
519 AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
520 AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
521 AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
522 AppendMenu(p, MF_SEPARATOR, 0, 0);
523 AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
524 AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
525 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
527 AppendMenu(m, MF_SEPARATOR, 0, 0);
529 AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
530 AppendMenu(m, MF_SEPARATOR, 0, 0);
531 AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
532 AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
535 for (i = 1; i < ((nsessions < 256) ? nsessions : 256); i++)
536 AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
538 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
539 AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
540 AppendMenu(m, MF_SEPARATOR, 0, 0);
541 AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
542 AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
543 AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
544 AppendMenu(m, MF_SEPARATOR, 0, 0);
545 AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
549 * Finally show the window!
551 ShowWindow(hwnd, show);
554 * Open the initial log file if there is one.
559 * Set the palette up.
565 has_focus = (GetForegroundWindow() == hwnd);
568 if (GetMessage(&msg, NULL, 0, 0) == 1) {
569 int timer_id = 0, long_timer = 0;
571 while (msg.message != WM_QUIT) {
572 /* Sometimes DispatchMessage calls routines that use their own
573 * GetMessage loop, setup this timer so we get some control back.
575 * Also call term_update() from the timer so that if the host
576 * is sending data flat out we still do redraws.
578 if (timer_id && long_timer) {
579 KillTimer(hwnd, timer_id);
580 long_timer = timer_id = 0;
583 timer_id = SetTimer(hwnd, 1, 20, NULL);
584 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
585 DispatchMessage(&msg);
587 /* Make sure we blink everything that needs it. */
590 /* Send the paste buffer if there's anything to send */
593 /* If there's nothing new in the queue then we can do everything
594 * we've delayed, reading the socket, writing, and repainting
597 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
600 if (pending_netevent) {
601 enact_pending_netevent();
603 /* Force the cursor blink on */
606 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
610 /* Okay there is now nothing to do so we make sure the screen is
611 * completely up to date then tell windows to call us in a little
615 KillTimer(hwnd, timer_id);
624 /* Hmm, term_update didn't want to do an update too soon ... */
625 timer_id = SetTimer(hwnd, 1, 50, NULL);
627 timer_id = SetTimer(hwnd, 1, 2000, NULL);
629 timer_id = SetTimer(hwnd, 1, 100, NULL);
632 /* There's no point rescanning everything in the message queue
633 * so we do an apparently unnecessary wait here
636 if (GetMessage(&msg, NULL, 0, 0) != 1)
646 for (i = 0; i < 8; i++)
648 DeleteObject(fonts[i]);
655 if (cfg.protocol == PROT_SSH) {
666 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
668 char *do_select(SOCKET skt, int startup)
673 events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE;
678 return "do_select(): internal error (hwnd==NULL)";
679 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
680 switch (WSAGetLastError()) {
682 return "Network is down";
684 return "WSAAsyncSelect(): unknown error";
691 * set or clear the "raw mouse message" mode
693 void set_raw_mouse_mode(int activate)
695 send_raw_mouse = activate;
696 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
700 * Print a message box and close the connection.
702 void connection_fatal(char *fmt, ...)
708 vsprintf(stuff, fmt, ap);
710 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
711 if (cfg.close_on_exit == COE_ALWAYS)
714 session_closed = TRUE;
715 SetWindowText(hwnd, "PuTTY (inactive)");
720 * Actually do the job requested by a WM_NETEVENT
722 static void enact_pending_netevent(void)
724 static int reentering = 0;
725 extern int select_result(WPARAM, LPARAM);
729 return; /* don't unpend the pending */
731 pending_netevent = FALSE;
734 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
737 if (ret == 0 && !session_closed) {
738 /* Abnormal exits will already have set session_closed and taken
739 * appropriate action. */
740 if (cfg.close_on_exit == COE_ALWAYS ||
741 cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
743 session_closed = TRUE;
744 SetWindowText(hwnd, "PuTTY (inactive)");
745 MessageBox(hwnd, "Connection closed by remote host",
746 "PuTTY", MB_OK | MB_ICONINFORMATION);
752 * Copy the colour palette from the configuration data into defpal.
753 * This is non-trivial because the colour indices are different.
755 static void cfgtopalette(void)
758 static const int ww[] = {
759 6, 7, 8, 9, 10, 11, 12, 13,
760 14, 15, 16, 17, 18, 19, 20, 21,
761 0, 1, 2, 3, 4, 4, 5, 5
764 for (i = 0; i < 24; i++) {
766 defpal[i].rgbtRed = cfg.colours[w][0];
767 defpal[i].rgbtGreen = cfg.colours[w][1];
768 defpal[i].rgbtBlue = cfg.colours[w][2];
773 * Set up the colour palette.
775 static void init_palette(void)
778 HDC hdc = GetDC(hwnd);
780 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
781 logpal = smalloc(sizeof(*logpal)
782 - sizeof(logpal->palPalEntry)
783 + NCOLOURS * sizeof(PALETTEENTRY));
784 logpal->palVersion = 0x300;
785 logpal->palNumEntries = NCOLOURS;
786 for (i = 0; i < NCOLOURS; i++) {
787 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
788 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
789 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
790 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
792 pal = CreatePalette(logpal);
794 SelectPalette(hdc, pal, FALSE);
796 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
799 ReleaseDC(hwnd, hdc);
802 for (i = 0; i < NCOLOURS; i++)
803 colours[i] = PALETTERGB(defpal[i].rgbtRed,
807 for (i = 0; i < NCOLOURS; i++)
808 colours[i] = RGB(defpal[i].rgbtRed,
809 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
813 * Initialise all the fonts we will need. There may be as many as
814 * eight or as few as one. We also:
816 * - check the font width and height, correcting our guesses if
819 * - verify that the bold font is the same width as the ordinary
820 * one, and engage shadow bolding if not.
822 * - verify that the underlined font is the same width as the
823 * ordinary one (manual underlining by means of line drawing can
824 * be done in a pinch).
826 static void init_fonts(int pick_width)
832 int fw_dontcare, fw_bold;
838 for (i = 0; i < 8; i++)
841 if (cfg.fontisbold) {
842 fw_dontcare = FW_BOLD;
845 fw_dontcare = FW_DONTCARE;
851 font_height = cfg.fontheight;
852 if (font_height > 0) {
854 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
856 font_width = pick_width;
859 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
860 c, OUT_DEFAULT_PRECIS, \
861 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
862 FIXED_PITCH | FF_DONTCARE, cfg.font)
864 if (cfg.vtmode != VT_OEMONLY) {
865 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
867 SelectObject(hdc, fonts[FONT_NORMAL]);
868 GetTextMetrics(hdc, &tm);
869 font_height = tm.tmHeight;
870 font_width = tm.tmAveCharWidth;
872 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
875 * Some fonts, e.g. 9-pt Courier, draw their underlines
876 * outside their character cell. We successfully prevent
877 * screen corruption by clipping the text output, but then
878 * we lose the underline completely. Here we try to work
879 * out whether this is such a font, and if it is, we set a
880 * flag that causes underlines to be drawn by hand.
882 * Having tried other more sophisticated approaches (such
883 * as examining the TEXTMETRIC structure or requesting the
884 * height of a string), I think we'll do this the brute
885 * force way: we create a small bitmap, draw an underlined
886 * space on it, and test to see whether any pixels are
887 * foreground-coloured. (Since we expect the underline to
888 * go all the way across the character cell, we only search
889 * down a single column of the bitmap, half way across.)
893 HBITMAP und_bm, und_oldbm;
897 und_dc = CreateCompatibleDC(hdc);
898 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
899 und_oldbm = SelectObject(und_dc, und_bm);
900 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
901 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
902 SetTextColor(und_dc, RGB(255, 255, 255));
903 SetBkColor(und_dc, RGB(0, 0, 0));
904 SetBkMode(und_dc, OPAQUE);
905 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
907 for (i = 0; i < font_height; i++) {
908 c = GetPixel(und_dc, font_width / 2, i);
909 if (c != RGB(0, 0, 0))
912 SelectObject(und_dc, und_oldbm);
913 DeleteObject(und_bm);
915 font_needs_hand_underlining = !gotit;
918 if (bold_mode == BOLD_FONT) {
919 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
920 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
923 if (cfg.vtmode == VT_OEMANSI) {
924 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
925 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
927 if (bold_mode == BOLD_FONT) {
928 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
929 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
933 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
935 SelectObject(hdc, fonts[FONT_OEM]);
936 GetTextMetrics(hdc, &tm);
937 font_height = tm.tmHeight;
938 font_width = tm.tmAveCharWidth;
940 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
942 if (bold_mode == BOLD_FONT) {
943 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
944 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
949 descent = tm.tmAscent + 1;
950 if (descent >= font_height)
951 descent = font_height - 1;
952 firstchar = tm.tmFirstChar;
954 for (i = 0; i < 8; i++) {
956 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
957 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
964 ReleaseDC(hwnd, hdc);
966 /* ... This is wrong in OEM only mode */
967 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
968 (bold_mode == BOLD_FONT &&
969 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
971 DeleteObject(fonts[FONT_UNDERLINE]);
972 if (bold_mode == BOLD_FONT)
973 DeleteObject(fonts[FONT_BOLDUND]);
976 if (bold_mode == BOLD_FONT && fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
977 bold_mode = BOLD_SHADOW;
978 DeleteObject(fonts[FONT_BOLD]);
979 if (und_mode == UND_FONT)
980 DeleteObject(fonts[FONT_BOLDUND]);
983 /* With the fascist font painting it doesn't matter if the linedraw font
984 * isn't exactly the right size anymore so we don't have to check this.
986 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL]) {
987 if (cfg.fontcharset == OEM_CHARSET) {
988 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
989 "different sizes. Using OEM-only mode instead",
990 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
991 cfg.vtmode = VT_OEMONLY;
992 } else if (firstchar < ' ') {
993 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
994 "different sizes. Using XTerm mode instead",
995 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
996 cfg.vtmode = VT_XWINDOWS;
998 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
999 "different sizes. Using ISO8859-1 mode instead",
1000 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
1001 cfg.vtmode = VT_POORMAN;
1004 for (i = 0; i < 8; i++)
1006 DeleteObject(fonts[i]);
1012 void request_resize(int w, int h, int refont)
1016 /* If the window is maximized supress resizing attempts */
1021 /* Don't do this in OEMANSI, you may get disable messages */
1022 if (refont && w != cols && (cols == 80 || cols == 132)
1023 && cfg.vtmode != VT_OEMANSI)
1025 if (refont && w != cols && (cols == 80 || cols == 132))
1028 /* If font width too big for screen should we shrink the font more ? */
1030 font_width = ((font_width * cols + w / 2) / w);
1035 for (i = 0; i < 8; i++)
1037 DeleteObject(fonts[i]);
1039 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1040 und_mode = UND_FONT;
1041 init_fonts(font_width);
1043 static int first_time = 1;
1046 switch (first_time) {
1048 /* Get the size of the screen */
1049 if (GetClientRect(GetDesktopWindow(), &ss))
1050 /* first_time = 0 */ ;
1056 /* Make sure the values are sane */
1057 width = (ss.right - ss.left - extra_width) / font_width;
1058 height = (ss.bottom - ss.top - extra_height) / font_height;
1071 width = extra_width + font_width * w;
1072 height = extra_height + font_height * h;
1074 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1075 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1076 SWP_NOMOVE | SWP_NOZORDER);
1079 static void click(Mouse_Button b, int x, int y, int shift, int ctrl)
1081 int thistime = GetMessageTime();
1083 if (send_raw_mouse) {
1084 term_mouse(b, MA_CLICK, x, y, shift, ctrl);
1088 if (lastbtn == b && thistime - lasttime < dbltime) {
1089 lastact = (lastact == MA_CLICK ? MA_2CLK :
1090 lastact == MA_2CLK ? MA_3CLK :
1091 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1096 if (lastact != MA_NOTHING)
1097 term_mouse(b, lastact, x, y, shift, ctrl);
1098 lasttime = thistime;
1102 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1103 * into a cooked one (SELECT, EXTEND, PASTE).
1105 Mouse_Button translate_button(Mouse_Button button)
1107 if (button == MBT_LEFT)
1109 if (button == MBT_MIDDLE)
1110 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1111 if (button == MBT_RIGHT)
1112 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1115 static void show_mouseptr(int show)
1117 static int cursor_visible = 1;
1118 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1120 if (cursor_visible && !show)
1122 else if (!cursor_visible && show)
1124 cursor_visible = show;
1127 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1128 WPARAM wParam, LPARAM lParam)
1131 static int ignore_size = FALSE;
1132 static int ignore_clip = FALSE;
1133 static int just_reconfigged = FALSE;
1134 static int resizing = FALSE;
1135 static int need_backend_resize = FALSE;
1136 static int defered_resize = FALSE;
1140 if (pending_netevent)
1141 enact_pending_netevent();
1148 if (cfg.ping_interval > 0) {
1151 if (now - last_movement > cfg.ping_interval) {
1152 back->special(TS_PING);
1153 last_movement = now;
1161 if (!cfg.warn_on_close || session_closed ||
1163 "Are you sure you want to close this session?",
1164 "PuTTY Exit Confirmation",
1165 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1166 DestroyWindow(hwnd);
1173 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1185 PROCESS_INFORMATION pi;
1186 HANDLE filemap = NULL;
1188 if (wParam == IDM_DUPSESS) {
1190 * Allocate a file-mapping memory chunk for the
1193 SECURITY_ATTRIBUTES sa;
1196 sa.nLength = sizeof(sa);
1197 sa.lpSecurityDescriptor = NULL;
1198 sa.bInheritHandle = TRUE;
1199 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1202 0, sizeof(Config), NULL);
1204 p = (Config *) MapViewOfFile(filemap,
1206 0, 0, sizeof(Config));
1208 *p = cfg; /* structure copy */
1212 sprintf(c, "putty &%p", filemap);
1214 } else if (wParam == IDM_SAVEDSESS) {
1216 sessions[(lParam - IDM_SAVED_MIN) / 16];
1217 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1219 cl = NULL; /* not a very important failure mode */
1221 sprintf(cl, "putty @%s", session);
1227 GetModuleFileName(NULL, b, sizeof(b) - 1);
1229 si.lpReserved = NULL;
1230 si.lpDesktop = NULL;
1234 si.lpReserved2 = NULL;
1235 CreateProcess(b, cl, NULL, NULL, TRUE,
1236 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1239 CloseHandle(filemap);
1246 int prev_alwaysontop = cfg.alwaysontop;
1247 int prev_sunken_edge = cfg.sunken_edge;
1248 char oldlogfile[FILENAME_MAX];
1250 int need_setwpos = FALSE;
1251 int old_fwidth, old_fheight;
1253 strcpy(oldlogfile, cfg.logfilename);
1254 oldlogtype = cfg.logtype;
1255 old_fwidth = font_width;
1256 old_fheight = font_height;
1257 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1259 if (!do_reconfig(hwnd))
1262 if (strcmp(oldlogfile, cfg.logfilename) ||
1263 oldlogtype != cfg.logtype) {
1264 logfclose(); /* reset logging */
1268 just_reconfigged = TRUE;
1271 for (i = 0; i < 8; i++)
1273 DeleteObject(fonts[i]);
1275 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1276 und_mode = UND_FONT;
1280 * Flush the line discipline's edit buffer in the
1281 * case where local editing has just been disabled.
1283 ldisc_send(NULL, 0);
1291 /* Enable or disable the scroll bar, etc */
1293 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1294 LONG nexflag, exflag =
1295 GetWindowLong(hwnd, GWL_EXSTYLE);
1298 if (cfg.alwaysontop != prev_alwaysontop) {
1299 if (cfg.alwaysontop) {
1300 nexflag |= WS_EX_TOPMOST;
1301 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1302 SWP_NOMOVE | SWP_NOSIZE);
1304 nexflag &= ~(WS_EX_TOPMOST);
1305 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1306 SWP_NOMOVE | SWP_NOSIZE);
1309 if (cfg.sunken_edge)
1310 nexflag |= WS_EX_CLIENTEDGE;
1312 nexflag &= ~(WS_EX_CLIENTEDGE);
1318 nflg &= ~WS_VSCROLL;
1320 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1322 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1324 if (nflg != flag || nexflag != exflag) {
1328 SetWindowLong(hwnd, GWL_STYLE, nflg);
1329 if (nexflag != exflag)
1330 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1332 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1334 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1335 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1336 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER
1337 | SWP_FRAMECHANGED);
1339 GetWindowRect(hwnd, &wr);
1340 GetClientRect(hwnd, &cr);
1342 wr.right - wr.left - cr.right + cr.left;
1344 wr.bottom - wr.top - cr.bottom + cr.top;
1345 need_setwpos = TRUE;
1349 if (cfg.height != rows ||
1350 cfg.width != cols ||
1351 old_fwidth != font_width ||
1352 old_fheight != font_height ||
1353 cfg.savelines != savelines ||
1354 cfg.sunken_edge != prev_sunken_edge)
1355 need_setwpos = TRUE;
1357 if (IsZoomed(hwnd)) {
1361 defered_resize = TRUE;
1363 GetClientRect(hwnd, &cr);
1364 w = cr.right - cr.left;
1365 h = cr.bottom - cr.top;
1369 h = h / font_height;
1373 term_size(h, w, cfg.savelines);
1374 InvalidateRect(hwnd, NULL, TRUE);
1377 term_size(cfg.height, cfg.width, cfg.savelines);
1378 InvalidateRect(hwnd, NULL, TRUE);
1380 SetWindowPos(hwnd, NULL, 0, 0,
1381 extra_width + font_width * cfg.width,
1383 font_height * cfg.height,
1384 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1385 SWP_NOMOVE | SWP_NOZORDER);
1389 if (cfg.locksize && IsZoomed(hwnd))
1391 set_title(cfg.wintitle);
1392 if (IsIconic(hwnd)) {
1394 cfg.win_name_always ? window_name :
1409 back->special(TS_AYT);
1412 back->special(TS_BRK);
1415 back->special(TS_SYNCH);
1418 back->special(TS_EC);
1421 back->special(TS_EL);
1424 back->special(TS_GA);
1427 back->special(TS_NOP);
1430 back->special(TS_ABORT);
1433 back->special(TS_AO);
1436 back->special(TS_IP);
1439 back->special(TS_SUSP);
1442 back->special(TS_EOR);
1445 back->special(TS_EOF);
1451 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1452 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1457 #define X_POS(l) ((int)(short)LOWORD(l))
1458 #define Y_POS(l) ((int)(short)HIWORD(l))
1460 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1461 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1462 #define WHEEL_DELTA 120
1465 wheel_accumulator += (short) HIWORD(wParam);
1466 wParam = LOWORD(wParam);
1468 /* process events when the threshold is reached */
1469 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1472 /* reduce amount for next time */
1473 if (wheel_accumulator > 0) {
1475 wheel_accumulator -= WHEEL_DELTA;
1476 } else if (wheel_accumulator < 0) {
1478 wheel_accumulator += WHEEL_DELTA;
1482 if (send_raw_mouse) {
1483 /* send a mouse-down followed by a mouse up */
1486 TO_CHR_X(X_POS(lParam)),
1487 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1488 wParam & MK_CONTROL);
1489 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1490 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1491 wParam & MK_CONTROL);
1493 /* trigger a scroll */
1495 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1500 case WM_LBUTTONDOWN:
1501 case WM_MBUTTONDOWN:
1502 case WM_RBUTTONDOWN:
1509 case WM_LBUTTONDOWN:
1513 case WM_MBUTTONDOWN:
1514 button = MBT_MIDDLE;
1517 case WM_RBUTTONDOWN:
1526 button = MBT_MIDDLE;
1537 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1538 wParam & MK_SHIFT, wParam & MK_CONTROL);
1541 term_mouse(button, MA_RELEASE,
1542 TO_CHR_X(X_POS(lParam)),
1543 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1544 wParam & MK_CONTROL);
1552 * Add the mouse position and message time to the random
1555 noise_ultralight(lParam);
1557 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1559 if (wParam & MK_LBUTTON)
1561 else if (wParam & MK_MBUTTON)
1562 b = cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1564 b = cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1565 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1566 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1567 wParam & MK_CONTROL);
1570 case WM_NCMOUSEMOVE:
1572 noise_ultralight(lParam);
1574 case WM_IGNORE_CLIP:
1575 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1577 case WM_DESTROYCLIPBOARD:
1580 ignore_clip = FALSE;
1586 hdc = BeginPaint(hwnd, &p);
1588 SelectPalette(hdc, pal, TRUE);
1589 RealizePalette(hdc);
1591 term_paint(hdc, p.rcPaint.left, p.rcPaint.top,
1592 p.rcPaint.right, p.rcPaint.bottom);
1593 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1594 SelectObject(hdc, GetStockObject(WHITE_PEN));
1600 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1601 * but the only one that's likely to try to overload us is FD_READ.
1602 * This means buffering just one is fine.
1604 if (pending_netevent)
1605 enact_pending_netevent();
1607 pending_netevent = TRUE;
1608 pend_netevent_wParam = wParam;
1609 pend_netevent_lParam = lParam;
1610 time(&last_movement);
1614 CreateCaret(hwnd, caretbm, font_width, font_height);
1627 case WM_IGNORE_SIZE:
1628 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1630 case WM_ENTERSIZEMOVE:
1633 need_backend_resize = FALSE;
1635 case WM_EXITSIZEMOVE:
1638 if (need_backend_resize)
1643 int width, height, w, h, ew, eh;
1644 LPRECT r = (LPRECT) lParam;
1646 width = r->right - r->left - extra_width;
1647 height = r->bottom - r->top - extra_height;
1648 w = (width + font_width / 2) / font_width;
1651 h = (height + font_height / 2) / font_height;
1654 UpdateSizeTip(hwnd, w, h);
1655 ew = width - w * font_width;
1656 eh = height - h * font_height;
1658 if (wParam == WMSZ_LEFT ||
1659 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1665 if (wParam == WMSZ_TOP ||
1666 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1676 /* break; (never reached) */
1678 if (wParam == SIZE_MINIMIZED) {
1680 cfg.win_name_always ? window_name : icon_name);
1683 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1684 SetWindowText(hwnd, window_name);
1686 int width, height, w, h;
1687 #if 0 /* we have fixed this using WM_SIZING now */
1691 width = LOWORD(lParam);
1692 height = HIWORD(lParam);
1693 w = width / font_width;
1696 h = height / font_height;
1699 #if 0 /* we have fixed this using WM_SIZING now */
1700 ew = width - w * font_width;
1701 eh = height - h * font_height;
1702 if (ew != 0 || eh != 0) {
1704 GetWindowRect(hwnd, &r);
1705 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1706 SetWindowPos(hwnd, NULL, 0, 0,
1707 r.right - r.left - ew, r.bottom - r.top - eh,
1708 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1711 if (w != cols || h != rows || just_reconfigged) {
1713 term_size(h, w, cfg.savelines);
1715 * Don't call back->size in mid-resize. (To prevent
1716 * massive numbers of resize events getting sent
1717 * down the connection during an NT opaque drag.)
1722 need_backend_resize = TRUE;
1726 just_reconfigged = FALSE;
1729 if (wParam == SIZE_RESTORED && defered_resize) {
1730 defered_resize = FALSE;
1731 SetWindowPos(hwnd, NULL, 0, 0,
1732 extra_width + font_width * cfg.width,
1733 extra_height + font_height * cfg.height,
1734 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1735 SWP_NOMOVE | SWP_NOZORDER);
1737 ignore_size = FALSE;
1740 switch (LOWORD(wParam)) {
1754 term_scroll(0, +rows / 2);
1757 term_scroll(0, -rows / 2);
1759 case SB_THUMBPOSITION:
1761 term_scroll(1, HIWORD(wParam));
1765 case WM_PALETTECHANGED:
1766 if ((HWND) wParam != hwnd && pal != NULL) {
1767 HDC hdc = get_ctx();
1769 if (RealizePalette(hdc) > 0)
1775 case WM_QUERYNEWPALETTE:
1777 HDC hdc = get_ctx();
1779 if (RealizePalette(hdc) > 0)
1791 * Add the scan code and keypress timing to the random
1794 noise_ultralight(lParam);
1797 * We don't do TranslateMessage since it disassociates the
1798 * resulting CHAR message from the KEYDOWN that sparked it,
1799 * which we occasionally don't want. Instead, we process
1800 * KEYDOWN, and call the Win32 translator functions so that
1801 * we get the translations under _our_ control.
1804 unsigned char buf[20];
1807 if (wParam == VK_PROCESSKEY) {
1810 m.message = WM_KEYDOWN;
1812 m.lParam = lParam & 0xdfff;
1813 TranslateMessage(&m);
1815 len = TranslateKey(message, wParam, lParam, buf);
1817 return DefWindowProc(hwnd, message, wParam, lParam);
1818 ldisc_send(buf, len);
1827 unsigned char buf[2];
1830 buf[0] = wParam >> 8;
1836 * Nevertheless, we are prepared to deal with WM_CHAR
1837 * messages, should they crop up. So if someone wants to
1838 * post the things to us as part of a macro manoeuvre,
1839 * we're ready to cope.
1842 char c = xlat_kbd2tty((unsigned char) wParam);
1847 if (send_raw_mouse) {
1848 SetCursor(LoadCursor(NULL, IDC_ARROW));
1853 return DefWindowProc(hwnd, message, wParam, lParam);
1857 * Move the system caret. (We maintain one, even though it's
1858 * invisible, for the benefit of blind people: apparently some
1859 * helper software tracks the system caret, so we should arrange to
1862 void sys_cursor(int x, int y)
1865 SetCaretPos(x * font_width, y * font_height);
1869 * Draw a line of text in the window, at given character
1870 * coordinates, in given attributes.
1872 * We are allowed to fiddle with the contents of `text'.
1874 void do_text(Context ctx, int x, int y, char *text, int len,
1875 unsigned long attr, int lattr)
1878 int nfg, nbg, nfont;
1881 int force_manual_underline = 0;
1882 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
1883 static int *IpDx = 0, IpDxLEN = 0;;
1885 if (len > IpDxLEN || IpDx[0] != fnt_width) {
1887 if (len > IpDxLEN) {
1889 IpDx = smalloc((len + 16) * sizeof(int));
1890 IpDxLEN = (len + 16);
1892 for (i = 0; i < IpDxLEN; i++)
1893 IpDx[i] = fnt_width;
1899 if ((attr & ATTR_ACTCURS) && cfg.cursor_type == 0) {
1900 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1901 attr ^= ATTR_CUR_XOR;
1905 if (cfg.vtmode == VT_OEMONLY)
1909 * Map high-half characters in order to approximate ISO using
1910 * OEM character set. No characters are missing if the OEM codepage
1913 if (nfont & FONT_OEM) {
1915 for (i = 0; i < len; i++)
1916 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1918 /* This is CP850 ... perfect translation */
1919 static const char oemhighhalf[] = "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1920 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1921 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1922 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1923 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1924 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1925 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1926 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1927 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1928 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1929 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1930 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1933 /* This is CP437 ... junk translation */
1934 static const unsigned char oemhighhalf[] = {
1935 0x20, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1936 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1937 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1938 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1939 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1940 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1941 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1942 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1943 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1944 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1945 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1946 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1949 text[i] = oemhighhalf[(unsigned char) text[i] - 0xA0];
1953 if (attr & ATTR_LINEDRW) {
1956 static const char poorman[] =
1957 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1960 static const char oemmap_437[] =
1961 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1962 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1965 static const char oemmap_850[] =
1966 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1967 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1969 /* Poor windows font ... eg: windows courier */
1970 static const char oemmap[] =
1971 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1972 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1975 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1976 * VT100 line drawing chars; everything else stays normal.
1978 * Actually '_' maps to space too, but that's done before.
1980 switch (cfg.vtmode) {
1982 for (i = 0; i < len; i++)
1983 if (text[i] >= '\x60' && text[i] <= '\x7E')
1984 text[i] += '\x01' - '\x60';
1987 /* Make sure we actually have an OEM font */
1988 if (fonts[nfont | FONT_OEM]) {
1991 for (i = 0; i < len; i++)
1992 if (text[i] >= '\x60' && text[i] <= '\x7E')
1993 text[i] = oemmap[(unsigned char) text[i] - 0x60];
1997 for (i = 0; i < len; i++)
1998 if (text[i] >= '\x60' && text[i] <= '\x7E')
1999 text[i] = poorman[(unsigned char) text[i] - 0x60];
2004 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2005 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2006 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2008 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2009 nfont |= FONT_UNDERLINE;
2010 if (!fonts[nfont]) {
2011 if (nfont & FONT_UNDERLINE)
2012 force_manual_underline = 1;
2013 /* Don't do the same for manual bold, it could be bad news. */
2015 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2017 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
2018 force_manual_underline = 1;
2019 if (attr & ATTR_REVERSE) {
2024 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2026 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2030 SelectObject(hdc, fonts[nfont]);
2031 SetTextColor(hdc, fg);
2032 SetBkColor(hdc, bg);
2033 SetBkMode(hdc, OPAQUE);
2036 line_box.right = x + fnt_width * len;
2037 line_box.bottom = y + font_height;
2038 ExtTextOut(hdc, x, y, ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len,
2040 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2041 SetBkMode(hdc, TRANSPARENT);
2043 /* GRR: This draws the character outside it's box and can leave
2044 * 'droppings' even with the clip box! I suppose I could loop it
2045 * one character at a time ... yuk.
2047 * Or ... I could do a test print with "W", and use +1 or -1 for this
2048 * shift depending on if the leftmost column is blank...
2050 ExtTextOut(hdc, x - 1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
2052 if (force_manual_underline ||
2053 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
2055 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2056 MoveToEx(hdc, x, y + descent, NULL);
2057 LineTo(hdc, x + len * fnt_width, y + descent);
2058 oldpen = SelectObject(hdc, oldpen);
2059 DeleteObject(oldpen);
2061 if ((attr & ATTR_PASCURS) && cfg.cursor_type == 0) {
2064 pts[0].x = pts[1].x = pts[4].x = x;
2065 pts[2].x = pts[3].x = x + fnt_width - 1;
2066 pts[0].y = pts[3].y = pts[4].y = y;
2067 pts[1].y = pts[2].y = y + font_height - 1;
2068 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2069 Polyline(hdc, pts, 5);
2070 oldpen = SelectObject(hdc, oldpen);
2071 DeleteObject(oldpen);
2073 if ((attr & (ATTR_ACTCURS | ATTR_PASCURS)) && cfg.cursor_type != 0) {
2074 int startx, starty, dx, dy, length, i;
2075 if (cfg.cursor_type == 1) {
2077 starty = y + descent;
2083 if (attr & ATTR_RIGHTCURS)
2084 xadjust = fnt_width - 1;
2085 startx = x + xadjust;
2089 length = font_height;
2091 if (attr & ATTR_ACTCURS) {
2094 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2095 MoveToEx(hdc, startx, starty, NULL);
2096 LineTo(hdc, startx + dx * length, starty + dy * length);
2097 oldpen = SelectObject(hdc, oldpen);
2098 DeleteObject(oldpen);
2100 for (i = 0; i < length; i++) {
2102 SetPixel(hdc, startx, starty, colours[23]);
2111 static int check_compose(int first, int second)
2114 static char *composetbl[] = {
2115 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡",
2117 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§",
2119 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
2120 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·",
2122 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ",
2124 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
2125 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
2126 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
2127 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ",
2129 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð",
2131 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú",
2133 "\"uü", "'yý", "htþ", "\"yÿ",
2138 static int recurse = 0;
2141 for (c = composetbl; *c; c++) {
2142 if ((*c)[0] == first && (*c)[1] == second) {
2143 return (*c)[2] & 0xFF;
2149 nc = check_compose(second, first);
2151 nc = check_compose(toupper(first), toupper(second));
2153 nc = check_compose(toupper(second), toupper(first));
2161 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2162 * codes. Returns number of bytes used or zero to drop the message
2163 * or -1 to forward the message to windows.
2165 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2166 unsigned char *output)
2169 int scan, left_alt = 0, key_down, shift_state;
2171 unsigned char *p = output;
2172 static int alt_state = 0;
2174 HKL kbd_layout = GetKeyboardLayout(0);
2176 static WORD keys[3];
2177 static int compose_char = 0;
2178 static WPARAM compose_key = 0;
2180 r = GetKeyboardState(keystate);
2182 memset(keystate, 0, sizeof(keystate));
2185 { /* Tell us all about key events */
2186 static BYTE oldstate[256];
2187 static int first = 1;
2191 memcpy(oldstate, keystate, sizeof(oldstate));
2194 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2196 } else if ((HIWORD(lParam) & KF_UP)
2197 && scan == (HIWORD(lParam) & 0xFF)) {
2201 if (wParam >= VK_F1 && wParam <= VK_F20)
2202 debug(("K_F%d", wParam + 1 - VK_F1));
2215 debug(("VK_%02x", wParam));
2217 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2219 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2221 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2222 if (ch >= ' ' && ch <= '~')
2223 debug((", '%c'", ch));
2225 debug((", $%02x", ch));
2228 debug((", KB0=%02x", keys[0]));
2230 debug((", KB1=%02x", keys[1]));
2232 debug((", KB2=%02x", keys[2]));
2234 if ((keystate[VK_SHIFT] & 0x80) != 0)
2236 if ((keystate[VK_CONTROL] & 0x80) != 0)
2238 if ((HIWORD(lParam) & KF_EXTENDED))
2240 if ((HIWORD(lParam) & KF_UP))
2244 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2245 else if ((HIWORD(lParam) & KF_UP))
2246 oldstate[wParam & 0xFF] ^= 0x80;
2248 oldstate[wParam & 0xFF] ^= 0x81;
2250 for (ch = 0; ch < 256; ch++)
2251 if (oldstate[ch] != keystate[ch])
2252 debug((", M%02x=%02x", ch, keystate[ch]));
2254 memcpy(oldstate, keystate, sizeof(oldstate));
2258 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2259 keystate[VK_RMENU] = keystate[VK_MENU];
2263 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2264 if ((cfg.funky_type == 3 ||
2265 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2266 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2268 wParam = VK_EXECUTE;
2270 /* UnToggle NUMLock */
2271 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2272 keystate[VK_NUMLOCK] ^= 1;
2275 /* And write back the 'adjusted' state */
2276 SetKeyboardState(keystate);
2279 /* Disable Auto repeat if required */
2280 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2283 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2286 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2288 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2289 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2290 if (cfg.ctrlaltkeys)
2291 keystate[VK_MENU] = 0;
2293 keystate[VK_RMENU] = 0x80;
2298 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2299 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2300 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2302 /* Note if AltGr was pressed and if it was used as a compose key */
2303 if (!compose_state) {
2304 compose_key = 0x100;
2305 if (cfg.compose_key) {
2306 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2307 compose_key = wParam;
2309 if (wParam == VK_APPS)
2310 compose_key = wParam;
2313 if (wParam == compose_key) {
2314 if (compose_state == 0
2315 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2317 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2321 } else if (compose_state == 1 && wParam != VK_CONTROL)
2325 * Record that we pressed key so the scroll window can be reset, but
2326 * be careful to avoid Shift-UP/Down
2328 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2332 /* Make sure we're not pasting */
2336 if (compose_state > 1 && left_alt)
2339 /* Sanitize the number pad if not using a PC NumPad */
2340 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2341 && cfg.funky_type != 2)
2342 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2343 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2347 nParam = VK_NUMPAD0;
2350 nParam = VK_NUMPAD1;
2353 nParam = VK_NUMPAD2;
2356 nParam = VK_NUMPAD3;
2359 nParam = VK_NUMPAD4;
2362 nParam = VK_NUMPAD5;
2365 nParam = VK_NUMPAD6;
2368 nParam = VK_NUMPAD7;
2371 nParam = VK_NUMPAD8;
2374 nParam = VK_NUMPAD9;
2377 nParam = VK_DECIMAL;
2381 if (keystate[VK_NUMLOCK] & 1)
2388 /* If a key is pressed and AltGr is not active */
2389 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2390 /* Okay, prepare for most alts then ... */
2394 /* Lets see if it's a pattern we know all about ... */
2395 if (wParam == VK_PRIOR && shift_state == 1) {
2396 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2399 if (wParam == VK_NEXT && shift_state == 1) {
2400 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2403 if (wParam == VK_INSERT && shift_state == 1) {
2404 term_mouse(MBT_PASTE, MA_CLICK, 0, 0, 0, 0);
2405 term_mouse(MBT_PASTE, MA_RELEASE, 0, 0, 0, 0);
2408 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2411 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2413 PostMessage(hwnd, WM_CHAR, ' ', 0);
2414 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2417 /* Control-Numlock for app-keypad mode switch */
2418 if (wParam == VK_PAUSE && shift_state == 2) {
2419 app_keypad_keys ^= 1;
2423 /* Nethack keypad */
2424 if (cfg.nethack_keypad && !left_alt) {
2427 *p++ = shift_state ? 'B' : 'b';
2430 *p++ = shift_state ? 'J' : 'j';
2433 *p++ = shift_state ? 'N' : 'n';
2436 *p++ = shift_state ? 'H' : 'h';
2439 *p++ = shift_state ? '.' : '.';
2442 *p++ = shift_state ? 'L' : 'l';
2445 *p++ = shift_state ? 'Y' : 'y';
2448 *p++ = shift_state ? 'K' : 'k';
2451 *p++ = shift_state ? 'U' : 'u';
2456 /* Application Keypad */
2460 if (cfg.funky_type == 3 ||
2461 (cfg.funky_type <= 1 &&
2462 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
2476 if (app_keypad_keys && !cfg.no_applic_k)
2513 if (cfg.funky_type == 2) {
2518 } else if (shift_state)
2525 if (cfg.funky_type == 2)
2529 if (cfg.funky_type == 2)
2533 if (cfg.funky_type == 2)
2538 if (HIWORD(lParam) & KF_EXTENDED)
2544 if (xkey >= 'P' && xkey <= 'S')
2545 p += sprintf((char *) p, "\x1B%c", xkey);
2547 p += sprintf((char *) p, "\x1B?%c", xkey);
2549 p += sprintf((char *) p, "\x1BO%c", xkey);
2554 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
2555 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2558 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
2564 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
2568 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
2572 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
2576 if (wParam == VK_PAUSE) { /* Break/Pause */
2581 /* Control-2 to Control-8 are special */
2582 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
2583 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
2586 if (shift_state == 2 && wParam == 0xBD) {
2590 if (shift_state == 2 && wParam == 0xDF) {
2594 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2601 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2602 * for integer decimal nn.)
2604 * We also deal with the weird ones here. Linux VCs replace F1
2605 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2606 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2612 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
2615 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
2618 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
2621 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
2624 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
2627 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
2630 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
2633 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
2636 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
2639 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
2690 /* Reorder edit keys to physical order */
2691 if (cfg.funky_type == 3 && code <= 6)
2692 code = "\0\2\1\4\5\3\6"[code];
2694 if (vt52_mode && code > 0 && code <= 6) {
2695 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
2699 if (cfg.funky_type == 5 && code >= 11 && code <= 24) {
2700 p += sprintf((char *) p, "\x1B[%c", code + 'M' - 11);
2703 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
2710 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
2713 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
2716 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2717 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
2720 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2722 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
2724 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
2727 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2728 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
2732 p += sprintf((char *) p, "\x1B[%d~", code);
2737 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2738 * some reason seems to send VK_CLEAR to Windows...).
2761 p += sprintf((char *) p, "\x1B%c", xkey);
2762 else if (app_cursor_keys && !cfg.no_applic_c)
2763 p += sprintf((char *) p, "\x1BO%c", xkey);
2765 p += sprintf((char *) p, "\x1B[%c", xkey);
2771 * Finally, deal with Return ourselves. (Win95 seems to
2772 * foul it up when Alt is pressed, for some reason.)
2774 if (wParam == VK_RETURN) { /* Return */
2780 /* Okay we've done everything interesting; let windows deal with
2781 * the boring stuff */
2783 BOOL capsOn = keystate[VK_CAPITAL] != 0;
2785 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2786 if (cfg.xlat_capslockcyr)
2787 keystate[VK_CAPITAL] = 0;
2789 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
2792 for (i = 0; i < r; i++) {
2793 unsigned char ch = (unsigned char) keys[i];
2795 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
2800 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
2804 if ((nc = check_compose(compose_char, ch)) == -1) {
2805 MessageBeep(MB_ICONHAND);
2808 *p++ = xlat_kbd2tty((unsigned char) nc);
2814 if (left_alt && key_down)
2820 ch = xlat_latkbd2win(ch);
2821 *p++ = xlat_kbd2tty(ch);
2825 /* This is so the ALT-Numpad and dead keys work correctly. */
2830 /* If we're definitly not building up an ALT-54321 then clear it */
2835 /* ALT alone may or may not want to bring up the System menu */
2836 if (wParam == VK_MENU) {
2838 if (message == WM_SYSKEYDOWN)
2840 else if (message == WM_SYSKEYUP && alt_state)
2841 PostMessage(hwnd, WM_CHAR, ' ', 0);
2842 if (message == WM_SYSKEYUP)
2852 void set_title(char *title)
2855 window_name = smalloc(1 + strlen(title));
2856 strcpy(window_name, title);
2857 if (cfg.win_name_always || !IsIconic(hwnd))
2858 SetWindowText(hwnd, title);
2861 void set_icon(char *title)
2864 icon_name = smalloc(1 + strlen(title));
2865 strcpy(icon_name, title);
2866 if (!cfg.win_name_always && IsIconic(hwnd))
2867 SetWindowText(hwnd, title);
2870 void set_sbar(int total, int start, int page)
2877 si.cbSize = sizeof(si);
2878 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2880 si.nMax = total - 1;
2884 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
2887 Context get_ctx(void)
2893 SelectPalette(hdc, pal, FALSE);
2899 void free_ctx(Context ctx)
2901 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
2902 ReleaseDC(hwnd, ctx);
2905 static void real_palette_set(int n, int r, int g, int b)
2908 logpal->palPalEntry[n].peRed = r;
2909 logpal->palPalEntry[n].peGreen = g;
2910 logpal->palPalEntry[n].peBlue = b;
2911 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2912 colours[n] = PALETTERGB(r, g, b);
2913 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
2915 colours[n] = RGB(r, g, b);
2918 void palette_set(int n, int r, int g, int b)
2920 static const int first[21] = {
2921 0, 2, 4, 6, 8, 10, 12, 14,
2922 1, 3, 5, 7, 9, 11, 13, 15,
2925 real_palette_set(first[n], r, g, b);
2927 real_palette_set(first[n] + 1, r, g, b);
2929 HDC hdc = get_ctx();
2930 UnrealizeObject(pal);
2931 RealizePalette(hdc);
2936 void palette_reset(void)
2940 for (i = 0; i < NCOLOURS; i++) {
2942 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2943 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2944 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2945 logpal->palPalEntry[i].peFlags = 0;
2946 colours[i] = PALETTERGB(defpal[i].rgbtRed,
2947 defpal[i].rgbtGreen,
2948 defpal[i].rgbtBlue);
2950 colours[i] = RGB(defpal[i].rgbtRed,
2951 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
2956 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
2958 RealizePalette(hdc);
2963 void write_clip(void *data, int len, int must_deselect)
2968 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2971 lock = GlobalLock(clipdata);
2974 memcpy(lock, data, len);
2975 ((unsigned char *) lock)[len] = 0;
2976 GlobalUnlock(clipdata);
2979 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
2981 if (OpenClipboard(hwnd)) {
2983 SetClipboardData(CF_TEXT, clipdata);
2986 GlobalFree(clipdata);
2989 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
2992 void get_clip(void **p, int *len)
2994 static HGLOBAL clipdata = NULL;
2998 GlobalUnlock(clipdata);
3002 if (OpenClipboard(NULL)) {
3003 clipdata = GetClipboardData(CF_TEXT);
3006 *p = GlobalLock(clipdata);
3020 * Move `lines' lines from position `from' to position `to' in the
3023 void optimised_move(int to, int from, int lines)
3028 min = (to < from ? to : from);
3029 max = to + from - min;
3032 r.right = cols * font_width;
3033 r.top = min * font_height;
3034 r.bottom = (max + lines) * font_height;
3035 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3039 * Print a message box and perform a fatal exit.
3041 void fatalbox(char *fmt, ...)
3047 vsprintf(stuff, fmt, ap);
3049 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3058 if (mode == BELL_DEFAULT) {
3060 * For MessageBeep style bells, we want to be careful of
3061 * timing, because they don't have the nice property of
3062 * PlaySound bells that each one cancels the previous
3063 * active one. So we limit the rate to one per 50ms or so.
3065 static long lastbeep = 0;
3068 beepdiff = GetTickCount() - lastbeep;
3069 if (beepdiff >= 0 && beepdiff < 50)
3073 * The above MessageBeep call takes time, so we record the
3074 * time _after_ it finishes rather than before it starts.
3076 lastbeep = GetTickCount();
3077 } else if (mode == BELL_WAVEFILE) {
3078 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3079 char buf[sizeof(cfg.bell_wavefile) + 80];
3080 sprintf(buf, "Unable to play sound file\n%s\n"
3081 "Using default sound instead", cfg.bell_wavefile);
3082 MessageBox(hwnd, buf, "PuTTY Sound Error",
3083 MB_OK | MB_ICONEXCLAMATION);
3084 cfg.beep = BELL_DEFAULT;