17 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
23 #define IDM_SHOWLOG 0x0010
24 #define IDM_NEWSESS 0x0020
25 #define IDM_DUPSESS 0x0030
26 #define IDM_RECONF 0x0040
27 #define IDM_CLRSB 0x0050
28 #define IDM_RESET 0x0060
29 #define IDM_TEL_AYT 0x0070
30 #define IDM_TEL_BRK 0x0080
31 #define IDM_TEL_SYNCH 0x0090
32 #define IDM_TEL_EC 0x00a0
33 #define IDM_TEL_EL 0x00b0
34 #define IDM_TEL_GA 0x00c0
35 #define IDM_TEL_NOP 0x00d0
36 #define IDM_TEL_ABORT 0x00e0
37 #define IDM_TEL_AO 0x00f0
38 #define IDM_TEL_IP 0x0100
39 #define IDM_TEL_SUSP 0x0110
40 #define IDM_TEL_EOR 0x0120
41 #define IDM_TEL_EOF 0x0130
42 #define IDM_ABOUT 0x0140
43 #define IDM_SAVEDSESS 0x0150
44 #define IDM_COPYALL 0x0160
46 #define IDM_SESSLGP 0x0250 /* log type printable */
47 #define IDM_SESSLGA 0x0260 /* log type all chars */
48 #define IDM_SESSLGE 0x0270 /* log end */
49 #define IDM_SAVED_MIN 0x1000
50 #define IDM_SAVED_MAX 0x2000
52 #define WM_IGNORE_SIZE (WM_XUSER + 1)
53 #define WM_IGNORE_CLIP (WM_XUSER + 2)
55 /* Needed for Chinese support and apparently not always defined. */
57 #define VK_PROCESSKEY 0xE5
60 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
61 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
62 static void cfgtopalette(void);
63 static void init_palette(void);
64 static void init_fonts(int);
66 static int extra_width, extra_height;
68 static int pending_netevent = 0;
69 static WPARAM pend_netevent_wParam = 0;
70 static LPARAM pend_netevent_lParam = 0;
71 static void enact_pending_netevent(void);
73 static time_t last_movement = 0;
77 #define FONT_UNDERLINE 2
78 #define FONT_BOLDUND 3
80 #define FONT_OEMBOLD 5
81 #define FONT_OEMBOLDUND 6
83 static HFONT fonts[8];
84 static int font_needs_hand_underlining;
86 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
94 static COLORREF colours[NCOLOURS];
96 static LPLOGPALETTE logpal;
97 static RGBTRIPLE defpal[NCOLOURS];
101 static HBITMAP caretbm;
103 static int dbltime, lasttime, lastact;
104 static Mouse_Button lastbtn;
106 /* this allows xterm-style mouse handling. */
107 static int send_raw_mouse = 0;
108 static int wheel_accumulator = 0;
110 static char *window_name, *icon_name;
112 static int compose_state = 0;
114 /* Dummy routine, only required in plink. */
115 void ldisc_update(int echo, int edit) {}
117 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
118 static char appname[] = "PuTTY";
123 int guess_width, guess_height;
126 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
128 winsock_ver = MAKEWORD(1, 1);
129 if (WSAStartup(winsock_ver, &wsadata)) {
130 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
131 MB_OK | MB_ICONEXCLAMATION);
134 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
135 MessageBox(NULL, "WinSock version is incompatible with 1.1",
136 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
140 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
143 InitCommonControls();
145 /* Ensure a Maximize setting in Explorer doesn't maximise the
150 * Process the command line.
155 default_protocol = DEFAULT_PROTOCOL;
156 default_port = DEFAULT_PORT;
157 cfg.logtype = LGTYP_NONE;
159 do_defaults(NULL, &cfg);
162 while (*p && isspace(*p)) p++;
165 * Process command line options first. Yes, this can be
166 * done better, and it will be as soon as I have the
170 char *q = p + strcspn(p, " \t");
173 tolower(p[0]) == 's' &&
174 tolower(p[1]) == 's' &&
175 tolower(p[2]) == 'h') {
176 default_protocol = cfg.protocol = PROT_SSH;
177 default_port = cfg.port = 22;
178 } else if (q == p + 7 &&
179 tolower(p[0]) == 'c' &&
180 tolower(p[1]) == 'l' &&
181 tolower(p[2]) == 'e' &&
182 tolower(p[3]) == 'a' &&
183 tolower(p[4]) == 'n' &&
184 tolower(p[5]) == 'u' &&
185 tolower(p[6]) == 'p') {
187 * `putty -cleanup'. Remove all registry entries
188 * associated with PuTTY, and also find and delete
189 * the random seed file.
192 "This procedure will remove ALL Registry\n"
193 "entries associated with PuTTY, and will\n"
194 "also remove the PuTTY random seed file.\n"
196 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
197 "SESSIONS. Are you really sure you want\n"
200 MB_YESNO | MB_ICONWARNING) == IDYES) {
205 p = q + strspn(q, " \t");
209 * An initial @ means to activate a saved session.
213 while (i > 1 && isspace(p[i-1]))
216 do_defaults (p+1, &cfg);
217 if (!*cfg.host && !do_config()) {
221 } else if (*p == '&') {
223 * An initial & means we've been given a command line
224 * containing the hex value of a HANDLE for a file
225 * mapping object, which we must then extract as a
230 if (sscanf(p+1, "%p", &filemap) == 1 &&
231 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
232 0, 0, sizeof(Config))) != NULL) {
235 CloseHandle(filemap);
236 } else if (!do_config()) {
243 * If the hostname starts with "telnet:", set the
244 * protocol to Telnet and process the string as a
247 if (!strncmp(q, "telnet:", 7)) {
251 if (q[0] == '/' && q[1] == '/')
253 cfg.protocol = PROT_TELNET;
255 while (*p && *p != ':' && *p != '/') p++;
263 strncpy (cfg.host, q, sizeof(cfg.host)-1);
264 cfg.host[sizeof(cfg.host)-1] = '\0';
266 while (*p && !isspace(*p)) p++;
269 strncpy (cfg.host, q, sizeof(cfg.host)-1);
270 cfg.host[sizeof(cfg.host)-1] = '\0';
271 while (*p && isspace(*p)) p++;
284 /* See if host is of the form user@host */
285 if (cfg.host[0] != '\0') {
286 char *atsign = strchr(cfg.host, '@');
287 /* Make sure we're not overflowing the user field */
289 if (atsign-cfg.host < sizeof cfg.username) {
290 strncpy (cfg.username, cfg.host, atsign-cfg.host);
291 cfg.username[atsign-cfg.host] = '\0';
293 memmove(cfg.host, atsign+1, 1+strlen(atsign+1));
299 * Select protocol. This is farmed out into a table in a
300 * separate file to enable an ssh-free variant.
305 for (i = 0; backends[i].backend != NULL; i++)
306 if (backends[i].protocol == cfg.protocol) {
307 back = backends[i].backend;
311 MessageBox(NULL, "Unsupported protocol number found",
312 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
318 /* Check for invalid Port number (i.e. zero) */
320 MessageBox(NULL, "Invalid Port Number",
321 "PuTTY Internal Error", MB_OK |MB_ICONEXCLAMATION);
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 if (cfg.sunken_edge) exwinmode |= WS_EX_CLIENTEDGE;
380 hwnd = CreateWindowEx (exwinmode, appname, appname,
381 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
382 guess_width, guess_height,
383 NULL, NULL, inst, NULL);
387 * Initialise the fonts, simultaneously correcting the guesses
388 * for font_{width,height}.
390 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
395 * Correct the guesses for extra_{width,height}.
399 GetWindowRect (hwnd, &wr);
400 GetClientRect (hwnd, &cr);
401 extra_width = wr.right - wr.left - cr.right + cr.left;
402 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
406 * Resize the window, now we know what size we _really_ want it
409 guess_width = extra_width + font_width * cols;
410 guess_height = extra_height + font_height * rows;
411 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
412 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
413 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
416 * Set up a caret bitmap, with no content.
420 int size = (font_width+15)/16 * 2 * font_height;
421 bits = smalloc(size);
422 memset(bits, 0, size);
423 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
426 CreateCaret(hwnd, caretbm, font_width, font_height);
429 * Initialise the scroll bar.
434 si.cbSize = sizeof(si);
435 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
440 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
444 * Start up the telnet connection.
448 char msg[1024], *title;
451 error = back->init (cfg.host, cfg.port, &realhost);
453 sprintf(msg, "Unable to open connection to\n"
455 "%s", cfg.host, error);
456 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
459 window_name = icon_name = NULL;
461 title = cfg.wintitle;
463 sprintf(msg, "%s - PuTTY", realhost);
470 session_closed = FALSE;
473 * Set up the input and output buffers.
476 outbuf_reap = outbuf_head = 0;
479 * Prepare the mouse handler.
481 lastact = MA_NOTHING;
482 lastbtn = MBT_NOTHING;
483 dbltime = GetDoubleClickTime();
486 * Set up the session-control options on the system menu.
489 HMENU m = GetSystemMenu (hwnd, FALSE);
493 AppendMenu (m, MF_SEPARATOR, 0, 0);
494 if (cfg.protocol == PROT_TELNET) {
496 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
497 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
498 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
499 AppendMenu (p, MF_SEPARATOR, 0, 0);
500 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
501 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
502 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
503 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
504 AppendMenu (p, MF_SEPARATOR, 0, 0);
505 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
506 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
507 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
508 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
509 AppendMenu (p, MF_SEPARATOR, 0, 0);
510 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
511 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
512 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
513 AppendMenu (m, MF_SEPARATOR, 0, 0);
515 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
516 AppendMenu (m, MF_SEPARATOR, 0, 0);
517 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
518 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
521 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
522 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
523 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
524 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
525 AppendMenu (m, MF_SEPARATOR, 0, 0);
526 AppendMenu (m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
527 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
528 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
529 AppendMenu (m, MF_SEPARATOR, 0, 0);
530 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
534 * Finally show the window!
536 ShowWindow (hwnd, show);
539 * Open the initial log file if there is one.
544 * Set the palette up.
550 has_focus = (GetForegroundWindow() == hwnd);
553 if (GetMessage (&msg, NULL, 0, 0) == 1)
555 int timer_id = 0, long_timer = 0;
557 while (msg.message != WM_QUIT) {
558 /* Sometimes DispatchMessage calls routines that use their own
559 * GetMessage loop, setup this timer so we get some control back.
561 * Also call term_update() from the timer so that if the host
562 * is sending data flat out we still do redraws.
564 if(timer_id && long_timer) {
565 KillTimer(hwnd, timer_id);
566 long_timer = timer_id = 0;
569 timer_id = SetTimer(hwnd, 1, 20, NULL);
570 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
571 DispatchMessage (&msg);
573 /* Make sure we blink everything that needs it. */
576 /* Send the paste buffer if there's anything to send */
579 /* If there's nothing new in the queue then we can do everything
580 * we've delayed, reading the socket, writing, and repainting
583 if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
586 if (pending_netevent) {
587 enact_pending_netevent();
589 /* Force the cursor blink on */
592 if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
596 /* Okay there is now nothing to do so we make sure the screen is
597 * completely up to date then tell windows to call us in a little
601 KillTimer(hwnd, timer_id);
610 /* Hmm, term_update didn't want to do an update too soon ... */
611 timer_id = SetTimer(hwnd, 1, 50, NULL);
613 timer_id = SetTimer(hwnd, 1, 2000, NULL);
615 timer_id = SetTimer(hwnd, 1, 100, NULL);
618 /* There's no point rescanning everything in the message queue
619 * so we do an apparently unnecessary wait here
622 if (GetMessage (&msg, NULL, 0, 0) != 1)
634 DeleteObject(fonts[i]);
641 if (cfg.protocol == PROT_SSH) {
652 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
654 char *do_select(SOCKET skt, int startup) {
658 events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE;
663 return "do_select(): internal error (hwnd==NULL)";
664 if (WSAAsyncSelect (skt, hwnd, msg, events) == SOCKET_ERROR) {
665 switch (WSAGetLastError()) {
666 case WSAENETDOWN: return "Network is down";
667 default: return "WSAAsyncSelect(): unknown error";
674 * set or clear the "raw mouse message" mode
676 void set_raw_mouse_mode(int activate)
678 send_raw_mouse = activate;
679 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
683 * Print a message box and close the connection.
685 void connection_fatal(char *fmt, ...) {
690 vsprintf(stuff, fmt, ap);
692 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
693 if (cfg.close_on_exit == COE_ALWAYS)
696 session_closed = TRUE;
697 SetWindowText (hwnd, "PuTTY (inactive)");
702 * Actually do the job requested by a WM_NETEVENT
704 static void enact_pending_netevent(void) {
705 static int reentering = 0;
706 extern int select_result(WPARAM, LPARAM);
710 return; /* don't unpend the pending */
712 pending_netevent = FALSE;
715 ret = select_result (pend_netevent_wParam, pend_netevent_lParam);
718 if (ret == 0 && !session_closed) {
719 /* Abnormal exits will already have set session_closed and taken
720 * appropriate action. */
721 if (cfg.close_on_exit == COE_ALWAYS ||
722 cfg.close_on_exit == COE_NORMAL)
725 session_closed = TRUE;
726 SetWindowText (hwnd, "PuTTY (inactive)");
727 MessageBox(hwnd, "Connection closed by remote host",
728 "PuTTY", MB_OK | MB_ICONINFORMATION);
734 * Copy the colour palette from the configuration data into defpal.
735 * This is non-trivial because the colour indices are different.
737 static void cfgtopalette(void) {
739 static const int ww[] = {
740 6, 7, 8, 9, 10, 11, 12, 13,
741 14, 15, 16, 17, 18, 19, 20, 21,
742 0, 1, 2, 3, 4, 4, 5, 5
745 for (i=0; i<24; i++) {
747 defpal[i].rgbtRed = cfg.colours[w][0];
748 defpal[i].rgbtGreen = cfg.colours[w][1];
749 defpal[i].rgbtBlue = cfg.colours[w][2];
754 * Set up the colour palette.
756 static void init_palette(void) {
758 HDC hdc = GetDC (hwnd);
760 if (cfg.try_palette &&
761 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
762 logpal = smalloc(sizeof(*logpal)
763 - sizeof(logpal->palPalEntry)
764 + NCOLOURS * sizeof(PALETTEENTRY));
765 logpal->palVersion = 0x300;
766 logpal->palNumEntries = NCOLOURS;
767 for (i = 0; i < NCOLOURS; i++) {
768 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
769 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
770 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
771 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
773 pal = CreatePalette (logpal);
775 SelectPalette (hdc, pal, FALSE);
776 RealizePalette (hdc);
777 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
781 ReleaseDC (hwnd, hdc);
784 for (i=0; i<NCOLOURS; i++)
785 colours[i] = PALETTERGB(defpal[i].rgbtRed,
789 for(i=0; i<NCOLOURS; i++)
790 colours[i] = RGB(defpal[i].rgbtRed,
796 * Initialise all the fonts we will need. There may be as many as
797 * eight or as few as one. We also:
799 * - check the font width and height, correcting our guesses if
802 * - verify that the bold font is the same width as the ordinary
803 * one, and engage shadow bolding if not.
805 * - verify that the underlined font is the same width as the
806 * ordinary one (manual underlining by means of line drawing can
807 * be done in a pinch).
809 static void init_fonts(int pick_width) {
814 int fw_dontcare, fw_bold;
823 if (cfg.fontisbold) {
824 fw_dontcare = FW_BOLD;
827 fw_dontcare = FW_DONTCARE;
833 font_height = cfg.fontheight;
834 if (font_height > 0) {
835 font_height = -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
837 font_width = pick_width;
840 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
841 c, OUT_DEFAULT_PRECIS, \
842 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
843 FIXED_PITCH | FF_DONTCARE, cfg.font)
845 if (cfg.vtmode != VT_OEMONLY) {
846 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
848 SelectObject (hdc, fonts[FONT_NORMAL]);
849 GetTextMetrics(hdc, &tm);
850 font_height = tm.tmHeight;
851 font_width = tm.tmAveCharWidth;
853 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
856 * Some fonts, e.g. 9-pt Courier, draw their underlines
857 * outside their character cell. We successfully prevent
858 * screen corruption by clipping the text output, but then
859 * we lose the underline completely. Here we try to work
860 * out whether this is such a font, and if it is, we set a
861 * flag that causes underlines to be drawn by hand.
863 * Having tried other more sophisticated approaches (such
864 * as examining the TEXTMETRIC structure or requesting the
865 * height of a string), I think we'll do this the brute
866 * force way: we create a small bitmap, draw an underlined
867 * space on it, and test to see whether any pixels are
868 * foreground-coloured. (Since we expect the underline to
869 * go all the way across the character cell, we only search
870 * down a single column of the bitmap, half way across.)
874 HBITMAP und_bm, und_oldbm;
878 und_dc = CreateCompatibleDC(hdc);
879 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
880 und_oldbm = SelectObject(und_dc, und_bm);
881 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
882 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
883 SetTextColor (und_dc, RGB(255,255,255));
884 SetBkColor (und_dc, RGB(0,0,0));
885 SetBkMode (und_dc, OPAQUE);
886 ExtTextOut (und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
888 for (i = 0; i < font_height; i++) {
889 c = GetPixel(und_dc, font_width/2, i);
893 SelectObject(und_dc, und_oldbm);
894 DeleteObject(und_bm);
896 font_needs_hand_underlining = !gotit;
899 if (bold_mode == BOLD_FONT) {
900 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
901 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
904 if (cfg.vtmode == VT_OEMANSI) {
905 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
906 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
908 if (bold_mode == BOLD_FONT) {
909 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
910 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
916 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
918 SelectObject (hdc, fonts[FONT_OEM]);
919 GetTextMetrics(hdc, &tm);
920 font_height = tm.tmHeight;
921 font_width = tm.tmAveCharWidth;
923 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
925 if (bold_mode == BOLD_FONT) {
926 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
927 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
932 descent = tm.tmAscent + 1;
933 if (descent >= font_height)
934 descent = font_height - 1;
935 firstchar = tm.tmFirstChar;
937 for (i=0; i<8; i++) {
939 if (SelectObject (hdc, fonts[i]) &&
940 GetTextMetrics(hdc, &tm) )
941 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
947 ReleaseDC (hwnd, hdc);
949 /* ... This is wrong in OEM only mode */
950 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
951 (bold_mode == BOLD_FONT &&
952 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
954 DeleteObject (fonts[FONT_UNDERLINE]);
955 if (bold_mode == BOLD_FONT)
956 DeleteObject (fonts[FONT_BOLDUND]);
959 if (bold_mode == BOLD_FONT &&
960 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
961 bold_mode = BOLD_SHADOW;
962 DeleteObject (fonts[FONT_BOLD]);
963 if (und_mode == UND_FONT)
964 DeleteObject (fonts[FONT_BOLDUND]);
968 /* With the fascist font painting it doesn't matter if the linedraw font
969 * isn't exactly the right size anymore so we don't have to check this.
971 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
972 if( cfg.fontcharset == OEM_CHARSET )
974 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
975 "different sizes. Using OEM-only mode instead",
976 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
977 cfg.vtmode = VT_OEMONLY;
979 else if( firstchar < ' ' )
981 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
982 "different sizes. Using XTerm mode instead",
983 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
984 cfg.vtmode = VT_XWINDOWS;
988 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
989 "different sizes. Using ISO8859-1 mode instead",
990 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
991 cfg.vtmode = VT_POORMAN;
996 DeleteObject (fonts[i]);
1002 void request_resize (int w, int h, int refont) {
1005 /* If the window is maximized supress resizing attempts */
1006 if(IsZoomed(hwnd)) return;
1009 /* Don't do this in OEMANSI, you may get disable messages */
1010 if (refont && w != cols && (cols==80 || cols==132)
1011 && cfg.vtmode != VT_OEMANSI)
1013 if (refont && w != cols && (cols==80 || cols==132))
1016 /* If font width too big for screen should we shrink the font more ? */
1018 font_width = ((font_width*cols+w/2)/w);
1025 DeleteObject(fonts[i]);
1027 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1028 und_mode = UND_FONT;
1029 init_fonts(font_width);
1033 static int first_time = 1;
1039 /* Get the size of the screen */
1040 if (GetClientRect(GetDesktopWindow(),&ss))
1041 /* first_time = 0 */;
1042 else { first_time = 2; break; }
1044 /* Make sure the values are sane */
1045 width = (ss.right-ss.left-extra_width ) / font_width;
1046 height = (ss.bottom-ss.top-extra_height ) / font_height;
1048 if (w>width) w=width;
1049 if (h>height) h=height;
1055 width = extra_width + font_width * w;
1056 height = extra_height + font_height * h;
1058 SetWindowPos (hwnd, NULL, 0, 0, width, height,
1059 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1060 SWP_NOMOVE | SWP_NOZORDER);
1063 static void click (Mouse_Button b, int x, int y, int shift, int ctrl) {
1064 int thistime = GetMessageTime();
1066 if (send_raw_mouse) {
1067 term_mouse(b, MA_CLICK, x, y, shift, ctrl);
1071 if (lastbtn == b && thistime - lasttime < dbltime) {
1072 lastact = (lastact == MA_CLICK ? MA_2CLK :
1073 lastact == MA_2CLK ? MA_3CLK :
1074 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1079 if (lastact != MA_NOTHING)
1080 term_mouse (b, lastact, x, y, shift, ctrl);
1081 lasttime = thistime;
1085 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1086 * into a cooked one (SELECT, EXTEND, PASTE).
1088 Mouse_Button translate_button(Mouse_Button button) {
1089 if (button == MBT_LEFT)
1091 if (button == MBT_MIDDLE)
1092 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1093 if (button == MBT_RIGHT)
1094 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1097 static void show_mouseptr(int show) {
1098 static int cursor_visible = 1;
1099 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1101 if (cursor_visible && !show)
1103 else if (!cursor_visible && show)
1105 cursor_visible = show;
1108 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
1109 WPARAM wParam, LPARAM lParam) {
1111 static int ignore_size = FALSE;
1112 static int ignore_clip = FALSE;
1113 static int just_reconfigged = FALSE;
1114 static int resizing = FALSE;
1115 static int need_backend_resize = FALSE;
1119 if (pending_netevent)
1120 enact_pending_netevent();
1127 if (cfg.ping_interval > 0)
1131 if (now-last_movement > cfg.ping_interval)
1133 back->special(TS_PING);
1134 last_movement = now;
1142 if (!cfg.warn_on_close || session_closed ||
1143 MessageBox(hwnd, "Are you sure you want to close this session?",
1144 "PuTTY Exit Confirmation",
1145 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1146 DestroyWindow(hwnd);
1150 PostQuitMessage (0);
1153 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1165 PROCESS_INFORMATION pi;
1166 HANDLE filemap = NULL;
1168 if (wParam == IDM_DUPSESS) {
1170 * Allocate a file-mapping memory chunk for the
1173 SECURITY_ATTRIBUTES sa;
1176 sa.nLength = sizeof(sa);
1177 sa.lpSecurityDescriptor = NULL;
1178 sa.bInheritHandle = TRUE;
1179 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
1186 p = (Config *)MapViewOfFile(filemap,
1188 0, 0, sizeof(Config));
1190 *p = cfg; /* structure copy */
1194 sprintf(c, "putty &%p", filemap);
1196 } else if (wParam == IDM_SAVEDSESS) {
1197 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
1198 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1200 cl = NULL; /* not a very important failure mode */
1202 sprintf(cl, "putty @%s", session);
1208 GetModuleFileName (NULL, b, sizeof(b)-1);
1210 si.lpReserved = NULL;
1211 si.lpDesktop = NULL;
1215 si.lpReserved2 = NULL;
1216 CreateProcess (b, cl, NULL, NULL, TRUE,
1217 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1220 CloseHandle(filemap);
1227 int prev_alwaysontop = cfg.alwaysontop;
1228 int prev_sunken_edge = cfg.sunken_edge;
1229 char oldlogfile[FILENAME_MAX];
1231 int need_setwpos = FALSE;
1232 int old_fwidth, old_fheight;
1234 strcpy(oldlogfile, cfg.logfilename);
1235 oldlogtype = cfg.logtype;
1238 old_fwidth = font_width;
1239 old_fheight = font_height;
1240 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1242 if (!do_reconfig(hwnd))
1245 if (strcmp(oldlogfile, cfg.logfilename) ||
1246 oldlogtype != cfg.logtype) {
1247 logfclose(); /* reset logging */
1251 just_reconfigged = TRUE;
1256 DeleteObject(fonts[i]);
1258 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1259 und_mode = UND_FONT;
1263 * Flush the line discipline's edit buffer in the
1264 * case where local editing has just been disabled.
1266 ldisc_send(NULL, 0);
1274 /* Enable or disable the scroll bar, etc */
1276 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1277 LONG nexflag, exflag = GetWindowLong(hwnd, GWL_EXSTYLE);
1280 if (cfg.alwaysontop != prev_alwaysontop) {
1281 if (cfg.alwaysontop) {
1282 nexflag |= WS_EX_TOPMOST;
1283 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1284 SWP_NOMOVE | SWP_NOSIZE);
1286 nexflag &= ~(WS_EX_TOPMOST);
1287 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1288 SWP_NOMOVE | SWP_NOSIZE);
1291 if (cfg.sunken_edge)
1292 nexflag |= WS_EX_CLIENTEDGE;
1294 nexflag &= ~(WS_EX_CLIENTEDGE);
1297 if (cfg.scrollbar) nflg |= WS_VSCROLL;
1298 else nflg &= ~WS_VSCROLL;
1300 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1302 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1304 if (nflg != flag || nexflag != exflag)
1309 SetWindowLong(hwnd, GWL_STYLE, nflg);
1310 if (nexflag != exflag)
1311 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1313 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1315 SetWindowPos(hwnd, NULL, 0,0,0,0,
1316 SWP_NOACTIVATE|SWP_NOCOPYBITS|
1317 SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|
1320 GetWindowRect (hwnd, &wr);
1321 GetClientRect (hwnd, &cr);
1322 extra_width = wr.right - wr.left - cr.right + cr.left;
1323 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1327 if (cfg.height != rows ||
1328 cfg.width != cols ||
1329 old_fwidth != font_width ||
1330 old_fheight != font_height ||
1331 cfg.savelines != savelines ||
1332 cfg.sunken_edge != prev_sunken_edge)
1333 need_setwpos = TRUE;
1334 term_size(cfg.height, cfg.width, cfg.savelines);
1335 InvalidateRect(hwnd, NULL, TRUE);
1338 SetWindowPos (hwnd, NULL, 0, 0,
1339 extra_width + font_width * cfg.width,
1340 extra_height + font_height * cfg.height,
1341 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1342 SWP_NOMOVE | SWP_NOZORDER);
1344 set_title(cfg.wintitle);
1345 if (IsIconic(hwnd)) {
1346 SetWindowText (hwnd,
1347 cfg.win_name_always ? window_name : icon_name);
1360 case IDM_TEL_AYT: back->special (TS_AYT); break;
1361 case IDM_TEL_BRK: back->special (TS_BRK); break;
1362 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1363 case IDM_TEL_EC: back->special (TS_EC); break;
1364 case IDM_TEL_EL: back->special (TS_EL); break;
1365 case IDM_TEL_GA: back->special (TS_GA); break;
1366 case IDM_TEL_NOP: back->special (TS_NOP); break;
1367 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1368 case IDM_TEL_AO: back->special (TS_AO); break;
1369 case IDM_TEL_IP: back->special (TS_IP); break;
1370 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1371 case IDM_TEL_EOR: back->special (TS_EOR); break;
1372 case IDM_TEL_EOF: back->special (TS_EOF); break;
1377 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1378 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1383 #define X_POS(l) ((int)(short)LOWORD(l))
1384 #define Y_POS(l) ((int)(short)HIWORD(l))
1386 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1387 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1388 #define WHEEL_DELTA 120
1391 wheel_accumulator += (short)HIWORD(wParam);
1392 wParam = LOWORD(wParam);
1394 /* process events when the threshold is reached */
1395 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1398 /* reduce amount for next time */
1399 if (wheel_accumulator > 0) {
1401 wheel_accumulator -= WHEEL_DELTA;
1403 else if (wheel_accumulator < 0) {
1405 wheel_accumulator += WHEEL_DELTA;
1410 if (send_raw_mouse) {
1411 /* send a mouse-down followed by a mouse up */
1414 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1415 wParam & MK_SHIFT, wParam & MK_CONTROL);
1418 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1419 wParam & MK_SHIFT, wParam & MK_CONTROL);
1421 /* trigger a scroll */
1422 term_scroll(0, b == MBT_WHEEL_UP ? -rows/2 : rows/2);
1427 case WM_LBUTTONDOWN:
1428 case WM_MBUTTONDOWN:
1429 case WM_RBUTTONDOWN:
1436 case WM_LBUTTONDOWN: button = MBT_LEFT; press = 1; break;
1437 case WM_MBUTTONDOWN: button = MBT_MIDDLE; press = 1; break;
1438 case WM_RBUTTONDOWN: button = MBT_RIGHT; press = 1; break;
1439 case WM_LBUTTONUP: button = MBT_LEFT; press = 0; break;
1440 case WM_MBUTTONUP: button = MBT_MIDDLE; press = 0; break;
1441 case WM_RBUTTONUP: button = MBT_RIGHT; press = 0; break;
1446 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1447 wParam & MK_SHIFT, wParam & MK_CONTROL);
1450 term_mouse (button, MA_RELEASE,
1451 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1452 wParam & MK_SHIFT, wParam & MK_CONTROL);
1460 * Add the mouse position and message time to the random
1463 noise_ultralight(lParam);
1465 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1467 if (wParam & MK_LBUTTON)
1469 else if (wParam & MK_MBUTTON)
1470 b = cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1472 b = cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1473 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1474 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT, wParam & MK_CONTROL);
1477 case WM_NCMOUSEMOVE:
1479 noise_ultralight(lParam);
1481 case WM_IGNORE_CLIP:
1482 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1484 case WM_DESTROYCLIPBOARD:
1487 ignore_clip = FALSE;
1493 hdc = BeginPaint (hwnd, &p);
1495 SelectPalette (hdc, pal, TRUE);
1496 RealizePalette (hdc);
1498 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1499 p.rcPaint.right, p.rcPaint.bottom);
1500 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1501 SelectObject (hdc, GetStockObject(WHITE_PEN));
1502 EndPaint (hwnd, &p);
1507 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1508 * but the only one that's likely to try to overload us is FD_READ.
1509 * This means buffering just one is fine.
1511 if (pending_netevent)
1512 enact_pending_netevent();
1514 pending_netevent = TRUE;
1515 pend_netevent_wParam=wParam;
1516 pend_netevent_lParam=lParam;
1517 time(&last_movement);
1521 CreateCaret(hwnd, caretbm, font_width, font_height);
1534 case WM_IGNORE_SIZE:
1535 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1537 case WM_ENTERSIZEMOVE:
1540 need_backend_resize = FALSE;
1542 case WM_EXITSIZEMOVE:
1545 if (need_backend_resize)
1550 int width, height, w, h, ew, eh;
1551 LPRECT r = (LPRECT)lParam;
1553 width = r->right - r->left - extra_width;
1554 height = r->bottom - r->top - extra_height;
1555 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1556 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1557 UpdateSizeTip(hwnd, w, h);
1558 ew = width - w * font_width;
1559 eh = height - h * font_height;
1561 if (wParam == WMSZ_LEFT ||
1562 wParam == WMSZ_BOTTOMLEFT ||
1563 wParam == WMSZ_TOPLEFT)
1569 if (wParam == WMSZ_TOP ||
1570 wParam == WMSZ_TOPRIGHT ||
1571 wParam == WMSZ_TOPLEFT)
1581 /* break; (never reached) */
1583 if (wParam == SIZE_MINIMIZED) {
1584 SetWindowText (hwnd,
1585 cfg.win_name_always ? window_name : icon_name);
1588 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1589 SetWindowText (hwnd, window_name);
1591 int width, height, w, h;
1592 #if 0 /* we have fixed this using WM_SIZING now */
1596 width = LOWORD(lParam);
1597 height = HIWORD(lParam);
1598 w = width / font_width; if (w < 1) w = 1;
1599 h = height / font_height; if (h < 1) h = 1;
1600 #if 0 /* we have fixed this using WM_SIZING now */
1601 ew = width - w * font_width;
1602 eh = height - h * font_height;
1603 if (ew != 0 || eh != 0) {
1605 GetWindowRect (hwnd, &r);
1606 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1607 SetWindowPos (hwnd, NULL, 0, 0,
1608 r.right - r.left - ew, r.bottom - r.top - eh,
1609 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1612 if (w != cols || h != rows || just_reconfigged) {
1614 term_size (h, w, cfg.savelines);
1616 * Don't call back->size in mid-resize. (To prevent
1617 * massive numbers of resize events getting sent
1618 * down the connection during an NT opaque drag.)
1623 need_backend_resize = TRUE;
1624 just_reconfigged = FALSE;
1627 ignore_size = FALSE;
1630 switch (LOWORD(wParam)) {
1631 case SB_BOTTOM: term_scroll(-1, 0); break;
1632 case SB_TOP: term_scroll(+1, 0); break;
1633 case SB_LINEDOWN: term_scroll (0, +1); break;
1634 case SB_LINEUP: term_scroll (0, -1); break;
1635 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1636 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1637 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1638 term_scroll (1, HIWORD(wParam)); break;
1641 case WM_PALETTECHANGED:
1642 if ((HWND) wParam != hwnd && pal != NULL) {
1643 HDC hdc = get_ctx();
1645 if (RealizePalette (hdc) > 0)
1651 case WM_QUERYNEWPALETTE:
1653 HDC hdc = get_ctx();
1655 if (RealizePalette (hdc) > 0)
1667 * Add the scan code and keypress timing to the random
1670 noise_ultralight(lParam);
1673 * We don't do TranslateMessage since it disassociates the
1674 * resulting CHAR message from the KEYDOWN that sparked it,
1675 * which we occasionally don't want. Instead, we process
1676 * KEYDOWN, and call the Win32 translator functions so that
1677 * we get the translations under _our_ control.
1680 unsigned char buf[20];
1683 if (wParam==VK_PROCESSKEY) {
1686 m.message = WM_KEYDOWN;
1688 m.lParam = lParam & 0xdfff;
1689 TranslateMessage(&m);
1691 len = TranslateKey (message, wParam, lParam, buf);
1693 return DefWindowProc (hwnd, message, wParam, lParam);
1694 ldisc_send (buf, len);
1703 unsigned char buf[2];
1706 buf[0] = wParam >> 8;
1707 ldisc_send (buf, 2);
1712 * Nevertheless, we are prepared to deal with WM_CHAR
1713 * messages, should they crop up. So if someone wants to
1714 * post the things to us as part of a macro manoeuvre,
1715 * we're ready to cope.
1718 char c = xlat_kbd2tty((unsigned char)wParam);
1723 if (send_raw_mouse) {
1724 SetCursor(LoadCursor(NULL, IDC_ARROW));
1729 return DefWindowProc (hwnd, message, wParam, lParam);
1733 * Move the system caret. (We maintain one, even though it's
1734 * invisible, for the benefit of blind people: apparently some
1735 * helper software tracks the system caret, so we should arrange to
1738 void sys_cursor(int x, int y) {
1740 SetCaretPos(x * font_width, y * font_height);
1744 * Draw a line of text in the window, at given character
1745 * coordinates, in given attributes.
1747 * We are allowed to fiddle with the contents of `text'.
1749 void do_text (Context ctx, int x, int y, char *text, int len,
1750 unsigned long attr, int lattr) {
1752 int nfg, nbg, nfont;
1755 int force_manual_underline = 0;
1756 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1757 static int *IpDx = 0, IpDxLEN = 0;;
1759 if (len>IpDxLEN || IpDx[0] != fnt_width) {
1763 IpDx = smalloc((len+16)*sizeof(int));
1766 for(i=0; i<IpDxLEN; i++)
1767 IpDx[i] = fnt_width;
1773 if ((attr & ATTR_ACTCURS) && cfg.cursor_type == 0) {
1774 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1775 attr ^= ATTR_CUR_XOR;
1779 if (cfg.vtmode == VT_OEMONLY)
1783 * Map high-half characters in order to approximate ISO using
1784 * OEM character set. No characters are missing if the OEM codepage
1787 if (nfont & FONT_OEM) {
1789 for (i=0; i<len; i++)
1790 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1792 /* This is CP850 ... perfect translation */
1793 static const char oemhighhalf[] =
1794 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1795 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1796 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1797 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1798 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1799 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1800 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1801 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1802 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1803 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1804 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1805 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1808 /* This is CP437 ... junk translation */
1809 static const unsigned char oemhighhalf[] = {
1810 0x20, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1811 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1812 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1813 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1814 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1815 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1816 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1817 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1818 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1819 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1820 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1821 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1824 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1828 if (attr & ATTR_LINEDRW) {
1831 static const char poorman[] =
1832 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1835 static const char oemmap_437[] =
1836 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1837 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1840 static const char oemmap_850[] =
1841 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1842 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1844 /* Poor windows font ... eg: windows courier */
1845 static const char oemmap[] =
1846 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1847 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1850 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1851 * VT100 line drawing chars; everything else stays normal.
1853 * Actually '_' maps to space too, but that's done before.
1855 switch (cfg.vtmode) {
1857 for (i=0; i<len; i++)
1858 if (text[i] >= '\x60' && text[i] <= '\x7E')
1859 text[i] += '\x01' - '\x60';
1862 /* Make sure we actually have an OEM font */
1863 if (fonts[nfont|FONT_OEM]) {
1866 for (i=0; i<len; i++)
1867 if (text[i] >= '\x60' && text[i] <= '\x7E')
1868 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1872 for (i=0; i<len; i++)
1873 if (text[i] >= '\x60' && text[i] <= '\x7E')
1874 text[i] = poorman[(unsigned char)text[i] - 0x60];
1879 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1880 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1881 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1883 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1884 nfont |= FONT_UNDERLINE;
1887 if (nfont&FONT_UNDERLINE)
1888 force_manual_underline = 1;
1889 /* Don't do the same for manual bold, it could be bad news. */
1891 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1893 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1894 force_manual_underline = 1;
1895 if (attr & ATTR_REVERSE) {
1896 t = nfg; nfg = nbg; nbg = t;
1898 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1900 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1904 SelectObject (hdc, fonts[nfont]);
1905 SetTextColor (hdc, fg);
1906 SetBkColor (hdc, bg);
1907 SetBkMode (hdc, OPAQUE);
1910 line_box.right = x+fnt_width*len;
1911 line_box.bottom = y+font_height;
1912 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1913 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1914 SetBkMode (hdc, TRANSPARENT);
1916 /* GRR: This draws the character outside it's box and can leave
1917 * 'droppings' even with the clip box! I suppose I could loop it
1918 * one character at a time ... yuk.
1920 * Or ... I could do a test print with "W", and use +1 or -1 for this
1921 * shift depending on if the leftmost column is blank...
1923 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1925 if (force_manual_underline ||
1926 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1928 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1929 MoveToEx (hdc, x, y+descent, NULL);
1930 LineTo (hdc, x+len*fnt_width, y+descent);
1931 oldpen = SelectObject (hdc, oldpen);
1932 DeleteObject (oldpen);
1934 if ((attr & ATTR_PASCURS) && cfg.cursor_type == 0) {
1937 pts[0].x = pts[1].x = pts[4].x = x;
1938 pts[2].x = pts[3].x = x+fnt_width-1;
1939 pts[0].y = pts[3].y = pts[4].y = y;
1940 pts[1].y = pts[2].y = y+font_height-1;
1941 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1942 Polyline (hdc, pts, 5);
1943 oldpen = SelectObject (hdc, oldpen);
1944 DeleteObject (oldpen);
1946 if ((attr & (ATTR_ACTCURS | ATTR_PASCURS)) && cfg.cursor_type != 0) {
1947 int startx, starty, dx, dy, length, i;
1948 if (cfg.cursor_type == 1) {
1949 startx = x; starty = y+descent;
1950 dx = 1; dy = 0; length = fnt_width;
1953 if (attr & ATTR_RIGHTCURS)
1954 xadjust = fnt_width-1;
1955 startx = x+xadjust; starty = y;
1956 dx = 0; dy = 1; length = font_height;
1958 if (attr & ATTR_ACTCURS) {
1960 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1961 MoveToEx (hdc, startx, starty, NULL);
1962 LineTo (hdc, startx+dx*length, starty+dy*length);
1963 oldpen = SelectObject (hdc, oldpen);
1964 DeleteObject (oldpen);
1966 for (i = 0; i < length; i++) {
1968 SetPixel(hdc, startx, starty, colours[23]);
1970 startx += dx; starty += dy;
1976 static int check_compose(int first, int second) {
1978 static char * composetbl[] = {
1979 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1980 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1981 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1982 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1983 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1984 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1985 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1986 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1987 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1988 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1989 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1990 "\"uü", "'yý", "htþ", "\"yÿ",
1994 static int recurse = 0;
1997 for(c=composetbl; *c; c++) {
1998 if( (*c)[0] == first && (*c)[1] == second)
2000 return (*c)[2] & 0xFF;
2007 nc = check_compose(second, first);
2009 nc = check_compose(toupper(first), toupper(second));
2011 nc = check_compose(toupper(second), toupper(first));
2019 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2020 * codes. Returns number of bytes used or zero to drop the message
2021 * or -1 to forward the message to windows.
2023 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2024 unsigned char *output) {
2026 int scan, left_alt = 0, key_down, shift_state;
2028 unsigned char * p = output;
2029 static int alt_state = 0;
2031 HKL kbd_layout = GetKeyboardLayout(0);
2033 static WORD keys[3];
2034 static int compose_char = 0;
2035 static WPARAM compose_key = 0;
2037 r = GetKeyboardState(keystate);
2038 if (!r) memset(keystate, 0, sizeof(keystate));
2042 { /* Tell us all about key events */
2043 static BYTE oldstate[256];
2044 static int first = 1;
2047 if(first) memcpy(oldstate, keystate, sizeof(oldstate));
2050 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT) {
2052 } else if ((HIWORD(lParam)&KF_UP) && scan==(HIWORD(lParam) & 0xFF) ) {
2056 if (wParam >= VK_F1 && wParam <= VK_F20 )
2057 debug(("K_F%d", wParam+1-VK_F1));
2060 case VK_SHIFT: debug(("SHIFT")); break;
2061 case VK_CONTROL: debug(("CTRL")); break;
2062 case VK_MENU: debug(("ALT")); break;
2063 default: debug(("VK_%02x", wParam));
2065 if(message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2067 debug((", S%02x", scan=(HIWORD(lParam) & 0xFF) ));
2069 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2070 if (ch>=' ' && ch<='~') debug((", '%c'", ch));
2071 else if (ch) debug((", $%02x", ch));
2073 if (keys[0]) debug((", KB0=%02x", keys[0]));
2074 if (keys[1]) debug((", KB1=%02x", keys[1]));
2075 if (keys[2]) debug((", KB2=%02x", keys[2]));
2077 if ( (keystate[VK_SHIFT]&0x80)!=0) debug((", S"));
2078 if ( (keystate[VK_CONTROL]&0x80)!=0) debug((", C"));
2079 if ( (HIWORD(lParam)&KF_EXTENDED) ) debug((", E"));
2080 if ( (HIWORD(lParam)&KF_UP) ) debug((", U"));
2083 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
2085 else if ( (HIWORD(lParam)&KF_UP) )
2086 oldstate[wParam&0xFF] ^= 0x80;
2088 oldstate[wParam&0xFF] ^= 0x81;
2090 for(ch=0; ch<256; ch++)
2091 if (oldstate[ch] != keystate[ch])
2092 debug((", M%02x=%02x", ch, keystate[ch]));
2094 memcpy(oldstate, keystate, sizeof(oldstate));
2098 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED)) {
2099 keystate[VK_RMENU] = keystate[VK_MENU];
2103 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2104 if ( (cfg.funky_type == 3 ||
2105 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2106 && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
2108 wParam = VK_EXECUTE;
2110 /* UnToggle NUMLock */
2111 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
2112 keystate[VK_NUMLOCK] ^= 1;
2115 /* And write back the 'adjusted' state */
2116 SetKeyboardState (keystate);
2119 /* Disable Auto repeat if required */
2120 if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
2123 if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
2126 key_down = ((HIWORD(lParam)&KF_UP)==0);
2128 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2129 if (left_alt && (keystate[VK_CONTROL]&0x80)) {
2130 if (cfg.ctrlaltkeys)
2131 keystate[VK_MENU] = 0;
2133 keystate[VK_RMENU] = 0x80;
2138 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2139 shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
2140 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
2142 /* Note if AltGr was pressed and if it was used as a compose key */
2143 if (!compose_state) {
2144 compose_key = 0x100;
2145 if (cfg.compose_key) {
2146 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
2147 compose_key = wParam;
2149 if (wParam == VK_APPS)
2150 compose_key = wParam;
2153 if (wParam == compose_key)
2155 if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
2157 else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
2162 else if (compose_state==1 && wParam != VK_CONTROL)
2166 * Record that we pressed key so the scroll window can be reset, but
2167 * be careful to avoid Shift-UP/Down
2169 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
2173 /* Make sure we're not pasting */
2174 if (key_down) term_nopaste();
2176 if (compose_state>1 && left_alt) compose_state = 0;
2178 /* Sanitize the number pad if not using a PC NumPad */
2179 if( left_alt || (app_keypad_keys && !cfg.no_applic_k
2180 && cfg.funky_type != 2)
2181 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state )
2183 if ((HIWORD(lParam)&KF_EXTENDED) == 0)
2188 case VK_INSERT: nParam = VK_NUMPAD0; break;
2189 case VK_END: nParam = VK_NUMPAD1; break;
2190 case VK_DOWN: nParam = VK_NUMPAD2; break;
2191 case VK_NEXT: nParam = VK_NUMPAD3; break;
2192 case VK_LEFT: nParam = VK_NUMPAD4; break;
2193 case VK_CLEAR: nParam = VK_NUMPAD5; break;
2194 case VK_RIGHT: nParam = VK_NUMPAD6; break;
2195 case VK_HOME: nParam = VK_NUMPAD7; break;
2196 case VK_UP: nParam = VK_NUMPAD8; break;
2197 case VK_PRIOR: nParam = VK_NUMPAD9; break;
2198 case VK_DELETE: nParam = VK_DECIMAL; break;
2202 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
2208 /* If a key is pressed and AltGr is not active */
2209 if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
2211 /* Okay, prepare for most alts then ...*/
2212 if (left_alt) *p++ = '\033';
2214 /* Lets see if it's a pattern we know all about ... */
2215 if (wParam == VK_PRIOR && shift_state == 1) {
2216 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2219 if (wParam == VK_NEXT && shift_state == 1) {
2220 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2223 if (wParam == VK_INSERT && shift_state == 1) {
2224 term_mouse (MBT_PASTE, MA_CLICK, 0, 0, 0, 0);
2225 term_mouse (MBT_PASTE, MA_RELEASE, 0, 0, 0, 0);
2228 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2231 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2233 PostMessage(hwnd, WM_CHAR, ' ', 0);
2234 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2237 /* Control-Numlock for app-keypad mode switch */
2238 if (wParam == VK_PAUSE && shift_state == 2) {
2239 app_keypad_keys ^= 1;
2243 /* Nethack keypad */
2244 if (cfg.nethack_keypad && !left_alt) {
2246 case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
2247 case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
2248 case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
2249 case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
2250 case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
2251 case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
2252 case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
2253 case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
2254 case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
2258 /* Application Keypad */
2262 if ( cfg.funky_type == 3 ||
2263 ( cfg.funky_type <= 1 &&
2264 app_keypad_keys && !cfg.no_applic_k)) switch(wParam) {
2265 case VK_EXECUTE: xkey = 'P'; break;
2266 case VK_DIVIDE: xkey = 'Q'; break;
2267 case VK_MULTIPLY:xkey = 'R'; break;
2268 case VK_SUBTRACT:xkey = 'S'; break;
2270 if(app_keypad_keys && !cfg.no_applic_k) switch(wParam) {
2271 case VK_NUMPAD0: xkey = 'p'; break;
2272 case VK_NUMPAD1: xkey = 'q'; break;
2273 case VK_NUMPAD2: xkey = 'r'; break;
2274 case VK_NUMPAD3: xkey = 's'; break;
2275 case VK_NUMPAD4: xkey = 't'; break;
2276 case VK_NUMPAD5: xkey = 'u'; break;
2277 case VK_NUMPAD6: xkey = 'v'; break;
2278 case VK_NUMPAD7: xkey = 'w'; break;
2279 case VK_NUMPAD8: xkey = 'x'; break;
2280 case VK_NUMPAD9: xkey = 'y'; break;
2282 case VK_DECIMAL: xkey = 'n'; break;
2283 case VK_ADD: if(cfg.funky_type==2) {
2284 if(shift_state) xkey = 'l';
2286 } else if(shift_state) xkey = 'm';
2290 case VK_DIVIDE: if(cfg.funky_type==2) xkey = 'o'; break;
2291 case VK_MULTIPLY:if(cfg.funky_type==2) xkey = 'j'; break;
2292 case VK_SUBTRACT:if(cfg.funky_type==2) xkey = 'm'; break;
2295 if (HIWORD(lParam)&KF_EXTENDED)
2303 if (xkey>='P' && xkey<='S')
2304 p += sprintf((char *)p, "\x1B%c", xkey);
2306 p += sprintf((char *)p, "\x1B?%c", xkey);
2309 p += sprintf((char *)p, "\x1BO%c", xkey);
2314 if (wParam == VK_BACK && shift_state == 0 ) /* Backspace */
2316 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2319 if (wParam == VK_TAB && shift_state == 1 ) /* Shift tab */
2321 *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
2323 if (wParam == VK_SPACE && shift_state == 2 ) /* Ctrl-Space */
2325 *p++ = 0; return p - output;
2327 if (wParam == VK_SPACE && shift_state == 3 ) /* Ctrl-Shift-Space */
2329 *p++ = 160; return p - output;
2331 if (wParam == VK_CANCEL && shift_state == 2 ) /* Ctrl-Break */
2333 *p++ = 3; return p - output;
2335 if (wParam == VK_PAUSE) /* Break/Pause */
2337 *p++ = 26; *p++ = 0; return -2;
2339 /* Control-2 to Control-8 are special */
2340 if (shift_state == 2 && wParam >= '2' && wParam <= '8')
2342 *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
2345 if (shift_state == 2 && wParam == 0xBD) {
2349 if (shift_state == 2 && wParam == 0xDF) {
2353 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2354 *p++ = '\r'; *p++ = '\n';
2359 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2360 * for integer decimal nn.)
2362 * We also deal with the weird ones here. Linux VCs replace F1
2363 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2364 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2369 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
2370 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
2371 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
2372 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
2373 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
2374 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
2375 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
2376 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
2377 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
2378 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
2379 case VK_F11: code = 23; break;
2380 case VK_F12: code = 24; break;
2381 case VK_F13: code = 25; break;
2382 case VK_F14: code = 26; break;
2383 case VK_F15: code = 28; break;
2384 case VK_F16: code = 29; break;
2385 case VK_F17: code = 31; break;
2386 case VK_F18: code = 32; break;
2387 case VK_F19: code = 33; break;
2388 case VK_F20: code = 34; break;
2389 case VK_HOME: code = 1; break;
2390 case VK_INSERT: code = 2; break;
2391 case VK_DELETE: code = 3; break;
2392 case VK_END: code = 4; break;
2393 case VK_PRIOR: code = 5; break;
2394 case VK_NEXT: code = 6; break;
2396 /* Reorder edit keys to physical order */
2397 if (cfg.funky_type == 3 && code <= 6 ) code = "\0\2\1\4\5\3\6"[code];
2399 if (vt52_mode && code > 0 && code <= 6) {
2400 p += sprintf((char *)p, "\x1B%c", " HLMEIG"[code]);
2404 if (cfg.funky_type == 5 && code >= 11 && code <= 24) {
2405 p += sprintf((char *)p, "\x1B[%c", code + 'M' - 11);
2408 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
2410 if (code>15) offt++; if (code>21) offt++;
2412 p += sprintf((char *)p, "\x1B%c", code + 'P' - 11 - offt);
2414 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11 - offt);
2417 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2418 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
2421 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2423 p += sprintf((char *)p, "\x1B%c", code + 'P' - 11);
2425 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
2428 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2429 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
2433 p += sprintf((char *)p, "\x1B[%d~", code);
2438 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2439 * some reason seems to send VK_CLEAR to Windows...).
2444 case VK_UP: xkey = 'A'; break;
2445 case VK_DOWN: xkey = 'B'; break;
2446 case VK_RIGHT: xkey = 'C'; break;
2447 case VK_LEFT: xkey = 'D'; break;
2448 case VK_CLEAR: xkey = 'G'; break;
2453 p += sprintf((char *)p, "\x1B%c", xkey);
2454 else if (app_cursor_keys && !cfg.no_applic_c)
2455 p += sprintf((char *)p, "\x1BO%c", xkey);
2457 p += sprintf((char *)p, "\x1B[%c", xkey);
2463 * Finally, deal with Return ourselves. (Win95 seems to
2464 * foul it up when Alt is pressed, for some reason.)
2466 if (wParam == VK_RETURN) /* Return */
2473 /* Okay we've done everything interesting; let windows deal with
2474 * the boring stuff */
2476 BOOL capsOn=keystate[VK_CAPITAL] !=0;
2478 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2479 if(cfg.xlat_capslockcyr)
2480 keystate[VK_CAPITAL] = 0;
2482 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
2488 unsigned char ch = (unsigned char)keys[i];
2490 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
2495 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
2499 if ((nc=check_compose(compose_char,ch)) == -1)
2501 MessageBeep(MB_ICONHAND);
2504 *p++ = xlat_kbd2tty((unsigned char)nc);
2510 if( left_alt && key_down ) *p++ = '\033';
2516 ch = xlat_latkbd2win(ch);
2517 *p++ = xlat_kbd2tty(ch);
2521 /* This is so the ALT-Numpad and dead keys work correctly. */
2526 /* If we're definitly not building up an ALT-54321 then clear it */
2527 if (!left_alt) keys[0] = 0;
2530 /* ALT alone may or may not want to bring up the System menu */
2531 if (wParam == VK_MENU) {
2533 if (message == WM_SYSKEYDOWN)
2535 else if (message == WM_SYSKEYUP && alt_state)
2536 PostMessage(hwnd, WM_CHAR, ' ', 0);
2537 if (message == WM_SYSKEYUP)
2547 void set_title (char *title) {
2548 sfree (window_name);
2549 window_name = smalloc(1+strlen(title));
2550 strcpy (window_name, title);
2551 if (cfg.win_name_always || !IsIconic(hwnd))
2552 SetWindowText (hwnd, title);
2555 void set_icon (char *title) {
2557 icon_name = smalloc(1+strlen(title));
2558 strcpy (icon_name, title);
2559 if (!cfg.win_name_always && IsIconic(hwnd))
2560 SetWindowText (hwnd, title);
2563 void set_sbar (int total, int start, int page) {
2566 if (!cfg.scrollbar) return;
2568 si.cbSize = sizeof(si);
2569 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2571 si.nMax = total - 1;
2575 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2578 Context get_ctx(void) {
2583 SelectPalette (hdc, pal, FALSE);
2589 void free_ctx (Context ctx) {
2590 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2591 ReleaseDC (hwnd, ctx);
2594 static void real_palette_set (int n, int r, int g, int b) {
2596 logpal->palPalEntry[n].peRed = r;
2597 logpal->palPalEntry[n].peGreen = g;
2598 logpal->palPalEntry[n].peBlue = b;
2599 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2600 colours[n] = PALETTERGB(r, g, b);
2601 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2603 colours[n] = RGB(r, g, b);
2606 void palette_set (int n, int r, int g, int b) {
2607 static const int first[21] = {
2608 0, 2, 4, 6, 8, 10, 12, 14,
2609 1, 3, 5, 7, 9, 11, 13, 15,
2612 real_palette_set (first[n], r, g, b);
2614 real_palette_set (first[n]+1, r, g, b);
2616 HDC hdc = get_ctx();
2617 UnrealizeObject (pal);
2618 RealizePalette (hdc);
2623 void palette_reset (void) {
2626 for (i = 0; i < NCOLOURS; i++) {
2628 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2629 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2630 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2631 logpal->palPalEntry[i].peFlags = 0;
2632 colours[i] = PALETTERGB(defpal[i].rgbtRed,
2633 defpal[i].rgbtGreen,
2634 defpal[i].rgbtBlue);
2636 colours[i] = RGB(defpal[i].rgbtRed,
2637 defpal[i].rgbtGreen,
2638 defpal[i].rgbtBlue);
2643 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2645 RealizePalette (hdc);
2650 void write_clip (void *data, int len, int must_deselect) {
2654 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2657 lock = GlobalLock (clipdata);
2660 memcpy (lock, data, len);
2661 ((unsigned char *) lock) [len] = 0;
2662 GlobalUnlock (clipdata);
2665 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2667 if (OpenClipboard (hwnd)) {
2669 SetClipboardData (CF_TEXT, clipdata);
2672 GlobalFree (clipdata);
2675 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2678 void get_clip (void **p, int *len) {
2679 static HGLOBAL clipdata = NULL;
2683 GlobalUnlock (clipdata);
2687 if (OpenClipboard (NULL)) {
2688 clipdata = GetClipboardData (CF_TEXT);
2691 *p = GlobalLock (clipdata);
2705 * Move `lines' lines from position `from' to position `to' in the
2708 void optimised_move (int to, int from, int lines) {
2712 min = (to < from ? to : from);
2713 max = to + from - min;
2715 r.left = 0; r.right = cols * font_width;
2716 r.top = min * font_height; r.bottom = (max+lines) * font_height;
2717 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2721 * Print a message box and perform a fatal exit.
2723 void fatalbox(char *fmt, ...) {
2728 vsprintf(stuff, fmt, ap);
2730 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2737 void beep(int mode) {
2738 if (mode == BELL_DEFAULT) {
2740 * For MessageBeep style bells, we want to be careful of
2741 * timing, because they don't have the nice property of
2742 * PlaySound bells that each one cancels the previous
2743 * active one. So we limit the rate to one per 50ms or so.
2745 static long lastbeep = 0;
2748 now = GetTickCount();
2749 beepdiff = now - lastbeep;
2750 if (beepdiff >= 0 && beepdiff < 50)
2754 } else if (mode == BELL_WAVEFILE) {
2755 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
2756 char buf[sizeof(cfg.bell_wavefile)+80];
2757 sprintf(buf, "Unable to play sound file\n%s\n"
2758 "Using default sound instead", cfg.bell_wavefile);
2759 MessageBox(hwnd, buf, "PuTTY Sound Error", MB_OK | MB_ICONEXCLAMATION);
2760 cfg.beep = BELL_DEFAULT;