16 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
22 #define IDM_SHOWLOG 0x0010
23 #define IDM_NEWSESS 0x0020
24 #define IDM_DUPSESS 0x0030
25 #define IDM_RECONF 0x0040
26 #define IDM_CLRSB 0x0050
27 #define IDM_RESET 0x0060
28 #define IDM_TEL_AYT 0x0070
29 #define IDM_TEL_BRK 0x0080
30 #define IDM_TEL_SYNCH 0x0090
31 #define IDM_TEL_EC 0x00a0
32 #define IDM_TEL_EL 0x00b0
33 #define IDM_TEL_GA 0x00c0
34 #define IDM_TEL_NOP 0x00d0
35 #define IDM_TEL_ABORT 0x00e0
36 #define IDM_TEL_AO 0x00f0
37 #define IDM_TEL_IP 0x0100
38 #define IDM_TEL_SUSP 0x0110
39 #define IDM_TEL_EOR 0x0120
40 #define IDM_TEL_EOF 0x0130
41 #define IDM_ABOUT 0x0140
42 #define IDM_SAVEDSESS 0x0150
43 #define IDM_COPYALL 0x0160
45 #define IDM_SAVED_MIN 0x1000
46 #define IDM_SAVED_MAX 0x2000
48 #define WM_IGNORE_SIZE (WM_XUSER + 1)
49 #define WM_IGNORE_CLIP (WM_XUSER + 2)
51 /* Needed for Chinese support and apparently not always defined. */
53 #define VK_PROCESSKEY 0xE5
56 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
57 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
58 static void cfgtopalette(void);
59 static void init_palette(void);
60 static void init_fonts(int);
62 static int extra_width, extra_height;
64 static int pending_netevent = 0;
65 static WPARAM pend_netevent_wParam = 0;
66 static LPARAM pend_netevent_lParam = 0;
67 static void enact_pending_netevent(void);
69 static time_t last_movement = 0;
73 #define FONT_UNDERLINE 2
74 #define FONT_BOLDUND 3
76 #define FONT_OEMBOLD 5
77 #define FONT_OEMBOLDUND 6
79 static HFONT fonts[8];
80 static int font_needs_hand_underlining;
82 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
90 static COLORREF colours[NCOLOURS];
92 static LPLOGPALETTE logpal;
93 static RGBTRIPLE defpal[NCOLOURS];
97 static HBITMAP caretbm;
99 static int dbltime, lasttime, lastact;
100 static Mouse_Button lastbtn;
102 static char *window_name, *icon_name;
104 static int compose_state = 0;
106 /* Dummy routine, only required in plink. */
107 void ldisc_update(int echo, int edit) {}
109 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
110 static char appname[] = "PuTTY";
115 int guess_width, guess_height;
118 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
120 winsock_ver = MAKEWORD(1, 1);
121 if (WSAStartup(winsock_ver, &wsadata)) {
122 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
123 MB_OK | MB_ICONEXCLAMATION);
126 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
127 MessageBox(NULL, "WinSock version is incompatible with 1.1",
128 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
132 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
135 InitCommonControls();
137 /* Ensure a Maximize setting in Explorer doesn't maximise the
142 * Process the command line.
147 default_protocol = DEFAULT_PROTOCOL;
148 default_port = DEFAULT_PORT;
149 cfg.logtype = LGTYP_NONE;
151 do_defaults(NULL, &cfg);
154 while (*p && isspace(*p)) p++;
157 * Process command line options first. Yes, this can be
158 * done better, and it will be as soon as I have the
162 char *q = p + strcspn(p, " \t");
165 tolower(p[0]) == 's' &&
166 tolower(p[1]) == 's' &&
167 tolower(p[2]) == 'h') {
168 default_protocol = cfg.protocol = PROT_SSH;
169 default_port = cfg.port = 22;
170 } else if (q == p + 7 &&
171 tolower(p[0]) == 'c' &&
172 tolower(p[1]) == 'l' &&
173 tolower(p[2]) == 'e' &&
174 tolower(p[3]) == 'a' &&
175 tolower(p[4]) == 'n' &&
176 tolower(p[5]) == 'u' &&
177 tolower(p[6]) == 'p') {
179 * `putty -cleanup'. Remove all registry entries
180 * associated with PuTTY, and also find and delete
181 * the random seed file.
184 "This procedure will remove ALL Registry\n"
185 "entries associated with PuTTY, and will\n"
186 "also remove the PuTTY random seed file.\n"
188 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
189 "SESSIONS. Are you really sure you want\n"
192 MB_YESNO | MB_ICONWARNING) == IDYES) {
197 p = q + strspn(q, " \t");
201 * An initial @ means to activate a saved session.
205 while (i > 1 && isspace(p[i-1]))
208 do_defaults (p+1, &cfg);
209 if (!*cfg.host && !do_config()) {
213 } else if (*p == '&') {
215 * An initial & means we've been given a command line
216 * containing the hex value of a HANDLE for a file
217 * mapping object, which we must then extract as a
222 if (sscanf(p+1, "%p", &filemap) == 1 &&
223 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
224 0, 0, sizeof(Config))) != NULL) {
227 CloseHandle(filemap);
228 } else if (!do_config()) {
235 * If the hostname starts with "telnet:", set the
236 * protocol to Telnet and process the string as a
239 if (!strncmp(q, "telnet:", 7)) {
243 if (q[0] == '/' && q[1] == '/')
245 cfg.protocol = PROT_TELNET;
247 while (*p && *p != ':' && *p != '/') p++;
255 strncpy (cfg.host, q, sizeof(cfg.host)-1);
256 cfg.host[sizeof(cfg.host)-1] = '\0';
258 while (*p && !isspace(*p)) p++;
261 strncpy (cfg.host, q, sizeof(cfg.host)-1);
262 cfg.host[sizeof(cfg.host)-1] = '\0';
263 while (*p && isspace(*p)) p++;
276 /* See if host is of the form user@host */
277 if (cfg.host[0] != '\0') {
278 char *atsign = strchr(cfg.host, '@');
279 /* Make sure we're not overflowing the user field */
281 if (atsign-cfg.host < sizeof cfg.username) {
282 strncpy (cfg.username, cfg.host, atsign-cfg.host);
283 cfg.username[atsign-cfg.host] = '\0';
285 memmove(cfg.host, atsign+1, 1+strlen(atsign+1));
291 * Select protocol. This is farmed out into a table in a
292 * separate file to enable an ssh-free variant.
297 for (i = 0; backends[i].backend != NULL; i++)
298 if (backends[i].protocol == cfg.protocol) {
299 back = backends[i].backend;
303 MessageBox(NULL, "Unsupported protocol number found",
304 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
310 /* Check for invalid Port number (i.e. zero) */
312 MessageBox(NULL, "Invalid Port Number",
313 "PuTTY Internal Error", MB_OK |MB_ICONEXCLAMATION);
320 wndclass.lpfnWndProc = WndProc;
321 wndclass.cbClsExtra = 0;
322 wndclass.cbWndExtra = 0;
323 wndclass.hInstance = inst;
324 wndclass.hIcon = LoadIcon (inst,
325 MAKEINTRESOURCE(IDI_MAINICON));
326 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
327 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
328 wndclass.lpszMenuName = NULL;
329 wndclass.lpszClassName = appname;
331 RegisterClass (&wndclass);
336 savelines = cfg.savelines;
342 * Guess some defaults for the window size. This all gets
343 * updated later, so we don't really care too much. However, we
344 * do want the font width/height guesses to correspond to a
345 * large font rather than a small one...
352 term_size (cfg.height, cfg.width, cfg.savelines);
353 guess_width = extra_width + font_width * cols;
354 guess_height = extra_height + font_height * rows;
357 HWND w = GetDesktopWindow();
358 GetWindowRect (w, &r);
359 if (guess_width > r.right - r.left)
360 guess_width = r.right - r.left;
361 if (guess_height > r.bottom - r.top)
362 guess_height = r.bottom - r.top;
366 int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
368 if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
369 if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
370 if (cfg.alwaysontop) exwinmode = WS_EX_TOPMOST;
371 hwnd = CreateWindowEx (exwinmode, appname, appname,
372 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
373 guess_width, guess_height,
374 NULL, NULL, inst, NULL);
378 * Initialise the fonts, simultaneously correcting the guesses
379 * for font_{width,height}.
381 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
386 * Correct the guesses for extra_{width,height}.
390 GetWindowRect (hwnd, &wr);
391 GetClientRect (hwnd, &cr);
392 extra_width = wr.right - wr.left - cr.right + cr.left;
393 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
397 * Resize the window, now we know what size we _really_ want it
400 guess_width = extra_width + font_width * cols;
401 guess_height = extra_height + font_height * rows;
402 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
403 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
404 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
407 * Set up a caret bitmap, with no content.
411 int size = (font_width+15)/16 * 2 * font_height;
412 bits = smalloc(size);
413 memset(bits, 0, size);
414 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
417 CreateCaret(hwnd, caretbm, font_width, font_height);
420 * Initialise the scroll bar.
425 si.cbSize = sizeof(si);
426 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
431 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
435 * Start up the telnet connection.
439 char msg[1024], *title;
442 error = back->init (cfg.host, cfg.port, &realhost);
444 sprintf(msg, "Unable to open connection:\n%s", error);
445 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
448 window_name = icon_name = NULL;
450 title = cfg.wintitle;
452 sprintf(msg, "%s - PuTTY", realhost);
459 session_closed = FALSE;
462 * Set up the input and output buffers.
465 outbuf_reap = outbuf_head = 0;
468 * Prepare the mouse handler.
470 lastact = MA_NOTHING;
471 lastbtn = MB_NOTHING;
472 dbltime = GetDoubleClickTime();
475 * Set up the session-control options on the system menu.
478 HMENU m = GetSystemMenu (hwnd, FALSE);
482 AppendMenu (m, MF_SEPARATOR, 0, 0);
483 if (cfg.protocol == PROT_TELNET) {
485 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
486 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
487 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
488 AppendMenu (p, MF_SEPARATOR, 0, 0);
489 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
490 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
491 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
492 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
493 AppendMenu (p, MF_SEPARATOR, 0, 0);
494 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
495 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
496 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
497 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
498 AppendMenu (p, MF_SEPARATOR, 0, 0);
499 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
500 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
501 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
502 AppendMenu (m, MF_SEPARATOR, 0, 0);
504 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
505 AppendMenu (m, MF_SEPARATOR, 0, 0);
506 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
507 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
510 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
511 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
512 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
513 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
514 AppendMenu (m, MF_SEPARATOR, 0, 0);
515 AppendMenu (m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
516 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
517 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
518 AppendMenu (m, MF_SEPARATOR, 0, 0);
519 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
523 * Finally show the window!
525 ShowWindow (hwnd, show);
528 * Open the initial log file if there is one.
533 * Set the palette up.
539 has_focus = (GetForegroundWindow() == hwnd);
542 if (GetMessage (&msg, NULL, 0, 0) == 1)
544 int timer_id = 0, long_timer = 0;
546 while (msg.message != WM_QUIT) {
547 /* Sometimes DispatchMessage calls routines that use their own
548 * GetMessage loop, setup this timer so we get some control back.
550 * Also call term_update() from the timer so that if the host
551 * is sending data flat out we still do redraws.
553 if(timer_id && long_timer) {
554 KillTimer(hwnd, timer_id);
555 long_timer = timer_id = 0;
558 timer_id = SetTimer(hwnd, 1, 20, NULL);
559 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
560 DispatchMessage (&msg);
562 /* Make sure we blink everything that needs it. */
565 /* Send the paste buffer if there's anything to send */
568 /* If there's nothing new in the queue then we can do everything
569 * we've delayed, reading the socket, writing, and repainting
572 if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
575 if (pending_netevent) {
576 enact_pending_netevent();
578 /* Force the cursor blink on */
581 if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
585 /* Okay there is now nothing to do so we make sure the screen is
586 * completely up to date then tell windows to call us in a little
590 KillTimer(hwnd, timer_id);
599 /* Hmm, term_update didn't want to do an update too soon ... */
600 timer_id = SetTimer(hwnd, 1, 50, NULL);
602 timer_id = SetTimer(hwnd, 1, 59500, NULL);
604 timer_id = SetTimer(hwnd, 1, 100, NULL);
607 /* There's no point rescanning everything in the message queue
608 * so we do an apparently unnecessary wait here
611 if (GetMessage (&msg, NULL, 0, 0) != 1)
623 DeleteObject(fonts[i]);
630 if (cfg.protocol == PROT_SSH) {
641 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
643 char *do_select(SOCKET skt, int startup) {
647 events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE;
652 return "do_select(): internal error (hwnd==NULL)";
653 if (WSAAsyncSelect (skt, hwnd, msg, events) == SOCKET_ERROR) {
654 switch (WSAGetLastError()) {
655 case WSAENETDOWN: return "Network is down";
656 default: return "WSAAsyncSelect(): unknown error";
663 * Print a message box and close the connection.
665 void connection_fatal(char *fmt, ...) {
670 vsprintf(stuff, fmt, ap);
672 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
673 if (cfg.close_on_exit == COE_ALWAYS)
676 session_closed = TRUE;
677 SetWindowText (hwnd, "PuTTY (inactive)");
682 * Actually do the job requested by a WM_NETEVENT
684 static void enact_pending_netevent(void) {
685 static int reentering = 0;
686 extern int select_result(WPARAM, LPARAM);
690 return; /* don't unpend the pending */
692 pending_netevent = FALSE;
695 ret = select_result (pend_netevent_wParam, pend_netevent_lParam);
698 if (ret == 0 && !session_closed) {
699 /* Abnormal exits will already have set session_closed and taken
700 * appropriate action. */
701 if (cfg.close_on_exit == COE_ALWAYS ||
702 cfg.close_on_exit == COE_NORMAL)
705 session_closed = TRUE;
706 SetWindowText (hwnd, "PuTTY (inactive)");
707 MessageBox(hwnd, "Connection closed by remote host",
708 "PuTTY", MB_OK | MB_ICONINFORMATION);
714 * Copy the colour palette from the configuration data into defpal.
715 * This is non-trivial because the colour indices are different.
717 static void cfgtopalette(void) {
719 static const int ww[] = {
720 6, 7, 8, 9, 10, 11, 12, 13,
721 14, 15, 16, 17, 18, 19, 20, 21,
722 0, 1, 2, 3, 4, 4, 5, 5
725 for (i=0; i<24; i++) {
727 defpal[i].rgbtRed = cfg.colours[w][0];
728 defpal[i].rgbtGreen = cfg.colours[w][1];
729 defpal[i].rgbtBlue = cfg.colours[w][2];
734 * Set up the colour palette.
736 static void init_palette(void) {
738 HDC hdc = GetDC (hwnd);
740 if (cfg.try_palette &&
741 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
742 logpal = smalloc(sizeof(*logpal)
743 - sizeof(logpal->palPalEntry)
744 + NCOLOURS * sizeof(PALETTEENTRY));
745 logpal->palVersion = 0x300;
746 logpal->palNumEntries = NCOLOURS;
747 for (i = 0; i < NCOLOURS; i++) {
748 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
749 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
750 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
751 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
753 pal = CreatePalette (logpal);
755 SelectPalette (hdc, pal, FALSE);
756 RealizePalette (hdc);
757 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
761 ReleaseDC (hwnd, hdc);
764 for (i=0; i<NCOLOURS; i++)
765 colours[i] = PALETTERGB(defpal[i].rgbtRed,
769 for(i=0; i<NCOLOURS; i++)
770 colours[i] = RGB(defpal[i].rgbtRed,
776 * Initialise all the fonts we will need. There may be as many as
777 * eight or as few as one. We also:
779 * - check the font width and height, correcting our guesses if
782 * - verify that the bold font is the same width as the ordinary
783 * one, and engage shadow bolding if not.
785 * - verify that the underlined font is the same width as the
786 * ordinary one (manual underlining by means of line drawing can
787 * be done in a pinch).
789 static void init_fonts(int pick_width) {
794 int fw_dontcare, fw_bold;
803 if (cfg.fontisbold) {
804 fw_dontcare = FW_BOLD;
807 fw_dontcare = FW_DONTCARE;
813 font_height = cfg.fontheight;
814 if (font_height > 0) {
815 font_height = -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
817 font_width = pick_width;
820 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
821 c, OUT_DEFAULT_PRECIS, \
822 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
823 FIXED_PITCH | FF_DONTCARE, cfg.font)
825 if (cfg.vtmode != VT_OEMONLY) {
826 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
828 SelectObject (hdc, fonts[FONT_NORMAL]);
829 GetTextMetrics(hdc, &tm);
830 font_height = tm.tmHeight;
831 font_width = tm.tmAveCharWidth;
833 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
836 * Some fonts, e.g. 9-pt Courier, draw their underlines
837 * outside their character cell. We successfully prevent
838 * screen corruption by clipping the text output, but then
839 * we lose the underline completely. Here we try to work
840 * out whether this is such a font, and if it is, we set a
841 * flag that causes underlines to be drawn by hand.
843 * Having tried other more sophisticated approaches (such
844 * as examining the TEXTMETRIC structure or requesting the
845 * height of a string), I think we'll do this the brute
846 * force way: we create a small bitmap, draw an underlined
847 * space on it, and test to see whether any pixels are
848 * foreground-coloured. (Since we expect the underline to
849 * go all the way across the character cell, we only search
850 * down a single column of the bitmap, half way across.)
854 HBITMAP und_bm, und_oldbm;
858 und_dc = CreateCompatibleDC(hdc);
859 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
860 und_oldbm = SelectObject(und_dc, und_bm);
861 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
862 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
863 SetTextColor (und_dc, RGB(255,255,255));
864 SetBkColor (und_dc, RGB(0,0,0));
865 SetBkMode (und_dc, OPAQUE);
866 ExtTextOut (und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
868 for (i = 0; i < font_height; i++) {
869 c = GetPixel(und_dc, font_width/2, i);
873 SelectObject(und_dc, und_oldbm);
874 DeleteObject(und_bm);
876 font_needs_hand_underlining = !gotit;
879 if (bold_mode == BOLD_FONT) {
880 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
881 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
884 if (cfg.vtmode == VT_OEMANSI) {
885 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
886 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
888 if (bold_mode == BOLD_FONT) {
889 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
890 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
896 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
898 SelectObject (hdc, fonts[FONT_OEM]);
899 GetTextMetrics(hdc, &tm);
900 font_height = tm.tmHeight;
901 font_width = tm.tmAveCharWidth;
903 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
905 if (bold_mode == BOLD_FONT) {
906 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
907 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
912 descent = tm.tmAscent + 1;
913 if (descent >= font_height)
914 descent = font_height - 1;
915 firstchar = tm.tmFirstChar;
917 for (i=0; i<8; i++) {
919 if (SelectObject (hdc, fonts[i]) &&
920 GetTextMetrics(hdc, &tm) )
921 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
927 ReleaseDC (hwnd, hdc);
929 /* ... This is wrong in OEM only mode */
930 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
931 (bold_mode == BOLD_FONT &&
932 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
934 DeleteObject (fonts[FONT_UNDERLINE]);
935 if (bold_mode == BOLD_FONT)
936 DeleteObject (fonts[FONT_BOLDUND]);
939 if (bold_mode == BOLD_FONT &&
940 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
941 bold_mode = BOLD_SHADOW;
942 DeleteObject (fonts[FONT_BOLD]);
943 if (und_mode == UND_FONT)
944 DeleteObject (fonts[FONT_BOLDUND]);
948 /* With the fascist font painting it doesn't matter if the linedraw font
949 * isn't exactly the right size anymore so we don't have to check this.
951 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
952 if( cfg.fontcharset == OEM_CHARSET )
954 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
955 "different sizes. Using OEM-only mode instead",
956 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
957 cfg.vtmode = VT_OEMONLY;
959 else if( firstchar < ' ' )
961 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
962 "different sizes. Using XTerm mode instead",
963 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
964 cfg.vtmode = VT_XWINDOWS;
968 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
969 "different sizes. Using ISO8859-1 mode instead",
970 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
971 cfg.vtmode = VT_POORMAN;
976 DeleteObject (fonts[i]);
982 void request_resize (int w, int h, int refont) {
985 /* If the window is maximized supress resizing attempts */
986 if(IsZoomed(hwnd)) return;
989 /* Don't do this in OEMANSI, you may get disable messages */
990 if (refont && w != cols && (cols==80 || cols==132)
991 && cfg.vtmode != VT_OEMANSI)
993 if (refont && w != cols && (cols==80 || cols==132))
996 /* If font width too big for screen should we shrink the font more ? */
998 font_width = ((font_width*cols+w/2)/w);
1005 DeleteObject(fonts[i]);
1007 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1008 und_mode = UND_FONT;
1009 init_fonts(font_width);
1013 static int first_time = 1;
1019 /* Get the size of the screen */
1020 if (GetClientRect(GetDesktopWindow(),&ss))
1021 /* first_time = 0 */;
1022 else { first_time = 2; break; }
1024 /* Make sure the values are sane */
1025 width = (ss.right-ss.left-extra_width ) / font_width;
1026 height = (ss.bottom-ss.top-extra_height ) / font_height;
1028 if (w>width) w=width;
1029 if (h>height) h=height;
1035 width = extra_width + font_width * w;
1036 height = extra_height + font_height * h;
1038 SetWindowPos (hwnd, NULL, 0, 0, width, height,
1039 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1040 SWP_NOMOVE | SWP_NOZORDER);
1043 static void click (Mouse_Button b, int x, int y) {
1044 int thistime = GetMessageTime();
1046 if (lastbtn == b && thistime - lasttime < dbltime) {
1047 lastact = (lastact == MA_CLICK ? MA_2CLK :
1048 lastact == MA_2CLK ? MA_3CLK :
1049 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1054 if (lastact != MA_NOTHING)
1055 term_mouse (b, lastact, x, y);
1056 lasttime = thistime;
1059 static void show_mouseptr(int show) {
1060 static int cursor_visible = 1;
1061 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1063 if (cursor_visible && !show)
1065 else if (!cursor_visible && show)
1067 cursor_visible = show;
1070 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
1071 WPARAM wParam, LPARAM lParam) {
1073 static int ignore_size = FALSE;
1074 static int ignore_clip = FALSE;
1075 static int just_reconfigged = FALSE;
1076 static int resizing = FALSE;
1077 static int need_backend_resize = FALSE;
1081 if (pending_netevent)
1082 enact_pending_netevent();
1089 if (cfg.ping_interval > 0)
1093 if (now-last_movement > cfg.ping_interval)
1095 back->special(TS_PING);
1096 last_movement = now;
1104 if (!cfg.warn_on_close || session_closed ||
1105 MessageBox(hwnd, "Are you sure you want to close this session?",
1106 "PuTTY Exit Confirmation",
1107 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1108 DestroyWindow(hwnd);
1112 PostQuitMessage (0);
1115 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1127 PROCESS_INFORMATION pi;
1128 HANDLE filemap = NULL;
1130 if (wParam == IDM_DUPSESS) {
1132 * Allocate a file-mapping memory chunk for the
1135 SECURITY_ATTRIBUTES sa;
1138 sa.nLength = sizeof(sa);
1139 sa.lpSecurityDescriptor = NULL;
1140 sa.bInheritHandle = TRUE;
1141 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
1148 p = (Config *)MapViewOfFile(filemap,
1150 0, 0, sizeof(Config));
1152 *p = cfg; /* structure copy */
1156 sprintf(c, "putty &%p", filemap);
1158 } else if (wParam == IDM_SAVEDSESS) {
1159 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
1160 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1162 cl = NULL; /* not a very important failure mode */
1164 sprintf(cl, "putty @%s", session);
1170 GetModuleFileName (NULL, b, sizeof(b)-1);
1172 si.lpReserved = NULL;
1173 si.lpDesktop = NULL;
1177 si.lpReserved2 = NULL;
1178 CreateProcess (b, cl, NULL, NULL, TRUE,
1179 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1182 CloseHandle(filemap);
1189 int prev_alwaysontop = cfg.alwaysontop;
1190 char oldlogfile[FILENAME_MAX];
1192 int need_setwpos = FALSE;
1193 int old_fwidth, old_fheight;
1195 strcpy(oldlogfile, cfg.logfilename);
1196 oldlogtype = cfg.logtype;
1199 old_fwidth = font_width;
1200 old_fheight = font_height;
1201 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1203 if (!do_reconfig(hwnd))
1206 if (strcmp(oldlogfile, cfg.logfilename) ||
1207 oldlogtype != cfg.logtype) {
1208 logfclose(); /* reset logging */
1212 just_reconfigged = TRUE;
1217 DeleteObject(fonts[i]);
1219 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1220 und_mode = UND_FONT;
1224 * Flush the line discipline's edit buffer in the
1225 * case where local editing has just been disabled.
1227 ldisc_send(NULL, 0);
1235 /* Enable or disable the scroll bar, etc */
1237 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1238 LONG nexflag, exflag = GetWindowLong(hwnd, GWL_EXSTYLE);
1241 if (cfg.alwaysontop != prev_alwaysontop) {
1242 if (cfg.alwaysontop) {
1243 nexflag = WS_EX_TOPMOST;
1244 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1245 SWP_NOMOVE | SWP_NOSIZE);
1248 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1249 SWP_NOMOVE | SWP_NOSIZE);
1254 if (cfg.scrollbar) nflg |= WS_VSCROLL;
1255 else nflg &= ~WS_VSCROLL;
1257 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1259 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1261 if (nflg != flag || nexflag != exflag)
1266 SetWindowLong(hwnd, GWL_STYLE, nflg);
1267 if (nexflag != exflag)
1268 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1270 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1272 SetWindowPos(hwnd, NULL, 0,0,0,0,
1273 SWP_NOACTIVATE|SWP_NOCOPYBITS|
1274 SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|
1277 GetWindowRect (hwnd, &wr);
1278 GetClientRect (hwnd, &cr);
1279 extra_width = wr.right - wr.left - cr.right + cr.left;
1280 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1284 if (cfg.height != rows ||
1285 cfg.width != cols ||
1286 old_fwidth != font_width ||
1287 old_fheight != font_height ||
1288 cfg.savelines != savelines)
1289 need_setwpos = TRUE;
1290 term_size(cfg.height, cfg.width, cfg.savelines);
1291 InvalidateRect(hwnd, NULL, TRUE);
1294 SetWindowPos (hwnd, NULL, 0, 0,
1295 extra_width + font_width * cfg.width,
1296 extra_height + font_height * cfg.height,
1297 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1298 SWP_NOMOVE | SWP_NOZORDER);
1300 set_title(cfg.wintitle);
1301 if (IsIconic(hwnd)) {
1302 SetWindowText (hwnd,
1303 cfg.win_name_always ? window_name : icon_name);
1316 case IDM_TEL_AYT: back->special (TS_AYT); break;
1317 case IDM_TEL_BRK: back->special (TS_BRK); break;
1318 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1319 case IDM_TEL_EC: back->special (TS_EC); break;
1320 case IDM_TEL_EL: back->special (TS_EL); break;
1321 case IDM_TEL_GA: back->special (TS_GA); break;
1322 case IDM_TEL_NOP: back->special (TS_NOP); break;
1323 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1324 case IDM_TEL_AO: back->special (TS_AO); break;
1325 case IDM_TEL_IP: back->special (TS_IP); break;
1326 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1327 case IDM_TEL_EOR: back->special (TS_EOR); break;
1328 case IDM_TEL_EOF: back->special (TS_EOF); break;
1333 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1334 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1339 #define X_POS(l) ((int)(short)LOWORD(l))
1340 #define Y_POS(l) ((int)(short)HIWORD(l))
1342 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1343 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1345 case WM_LBUTTONDOWN:
1347 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1348 TO_CHR_Y(Y_POS(lParam)));
1353 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1354 TO_CHR_Y(Y_POS(lParam)));
1357 case WM_MBUTTONDOWN:
1360 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1361 TO_CHR_X(X_POS(lParam)),
1362 TO_CHR_Y(Y_POS(lParam)));
1366 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1367 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1368 TO_CHR_Y(Y_POS(lParam)));
1371 case WM_RBUTTONDOWN:
1374 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1375 TO_CHR_X(X_POS(lParam)),
1376 TO_CHR_Y(Y_POS(lParam)));
1380 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1381 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1382 TO_CHR_Y(Y_POS(lParam)));
1388 * Add the mouse position and message time to the random
1391 noise_ultralight(lParam);
1393 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1395 if (wParam & MK_LBUTTON)
1397 else if (wParam & MK_MBUTTON)
1398 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1400 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1401 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1402 TO_CHR_Y(Y_POS(lParam)));
1405 case WM_NCMOUSEMOVE:
1407 noise_ultralight(lParam);
1409 case WM_IGNORE_CLIP:
1410 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1412 case WM_DESTROYCLIPBOARD:
1415 ignore_clip = FALSE;
1421 hdc = BeginPaint (hwnd, &p);
1423 SelectPalette (hdc, pal, TRUE);
1424 RealizePalette (hdc);
1426 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1427 p.rcPaint.right, p.rcPaint.bottom);
1428 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1429 SelectObject (hdc, GetStockObject(WHITE_PEN));
1430 EndPaint (hwnd, &p);
1435 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1436 * but the only one that's likely to try to overload us is FD_READ.
1437 * This means buffering just one is fine.
1439 if (pending_netevent)
1440 enact_pending_netevent();
1442 pending_netevent = TRUE;
1443 pend_netevent_wParam=wParam;
1444 pend_netevent_lParam=lParam;
1445 time(&last_movement);
1449 CreateCaret(hwnd, caretbm, font_width, font_height);
1462 case WM_IGNORE_SIZE:
1463 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1465 case WM_ENTERSIZEMOVE:
1468 need_backend_resize = FALSE;
1470 case WM_EXITSIZEMOVE:
1473 if (need_backend_resize)
1478 int width, height, w, h, ew, eh;
1479 LPRECT r = (LPRECT)lParam;
1481 width = r->right - r->left - extra_width;
1482 height = r->bottom - r->top - extra_height;
1483 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1484 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1485 UpdateSizeTip(hwnd, w, h);
1486 ew = width - w * font_width;
1487 eh = height - h * font_height;
1489 if (wParam == WMSZ_LEFT ||
1490 wParam == WMSZ_BOTTOMLEFT ||
1491 wParam == WMSZ_TOPLEFT)
1497 if (wParam == WMSZ_TOP ||
1498 wParam == WMSZ_TOPRIGHT ||
1499 wParam == WMSZ_TOPLEFT)
1509 /* break; (never reached) */
1511 if (wParam == SIZE_MINIMIZED) {
1512 SetWindowText (hwnd,
1513 cfg.win_name_always ? window_name : icon_name);
1516 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1517 SetWindowText (hwnd, window_name);
1519 int width, height, w, h;
1520 #if 0 /* we have fixed this using WM_SIZING now */
1524 width = LOWORD(lParam);
1525 height = HIWORD(lParam);
1526 w = width / font_width; if (w < 1) w = 1;
1527 h = height / font_height; if (h < 1) h = 1;
1528 #if 0 /* we have fixed this using WM_SIZING now */
1529 ew = width - w * font_width;
1530 eh = height - h * font_height;
1531 if (ew != 0 || eh != 0) {
1533 GetWindowRect (hwnd, &r);
1534 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1535 SetWindowPos (hwnd, NULL, 0, 0,
1536 r.right - r.left - ew, r.bottom - r.top - eh,
1537 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1540 if (w != cols || h != rows || just_reconfigged) {
1542 term_size (h, w, cfg.savelines);
1544 * Don't call back->size in mid-resize. (To prevent
1545 * massive numbers of resize events getting sent
1546 * down the connection during an NT opaque drag.)
1551 need_backend_resize = TRUE;
1552 just_reconfigged = FALSE;
1555 ignore_size = FALSE;
1558 switch (LOWORD(wParam)) {
1559 case SB_BOTTOM: term_scroll(-1, 0); break;
1560 case SB_TOP: term_scroll(+1, 0); break;
1561 case SB_LINEDOWN: term_scroll (0, +1); break;
1562 case SB_LINEUP: term_scroll (0, -1); break;
1563 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1564 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1565 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1566 term_scroll (1, HIWORD(wParam)); break;
1569 case WM_PALETTECHANGED:
1570 if ((HWND) wParam != hwnd && pal != NULL) {
1571 HDC hdc = get_ctx();
1573 if (RealizePalette (hdc) > 0)
1579 case WM_QUERYNEWPALETTE:
1581 HDC hdc = get_ctx();
1583 if (RealizePalette (hdc) > 0)
1595 * Add the scan code and keypress timing to the random
1598 noise_ultralight(lParam);
1601 * We don't do TranslateMessage since it disassociates the
1602 * resulting CHAR message from the KEYDOWN that sparked it,
1603 * which we occasionally don't want. Instead, we process
1604 * KEYDOWN, and call the Win32 translator functions so that
1605 * we get the translations under _our_ control.
1608 unsigned char buf[20];
1611 if (wParam==VK_PROCESSKEY) {
1614 m.message = WM_KEYDOWN;
1616 m.lParam = lParam & 0xdfff;
1617 TranslateMessage(&m);
1619 len = TranslateKey (message, wParam, lParam, buf);
1621 return DefWindowProc (hwnd, message, wParam, lParam);
1622 ldisc_send (buf, len);
1631 unsigned char buf[2];
1634 buf[0] = wParam >> 8;
1635 ldisc_send (buf, 2);
1640 * Nevertheless, we are prepared to deal with WM_CHAR
1641 * messages, should they crop up. So if someone wants to
1642 * post the things to us as part of a macro manoeuvre,
1643 * we're ready to cope.
1646 char c = xlat_kbd2tty((unsigned char)wParam);
1652 return DefWindowProc (hwnd, message, wParam, lParam);
1656 * Move the system caret. (We maintain one, even though it's
1657 * invisible, for the benefit of blind people: apparently some
1658 * helper software tracks the system caret, so we should arrange to
1661 void sys_cursor(int x, int y) {
1662 SetCaretPos(x * font_width, y * font_height);
1666 * Draw a line of text in the window, at given character
1667 * coordinates, in given attributes.
1669 * We are allowed to fiddle with the contents of `text'.
1671 void do_text (Context ctx, int x, int y, char *text, int len,
1672 unsigned long attr, int lattr) {
1674 int nfg, nbg, nfont;
1677 int force_manual_underline = 0;
1678 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1679 static int *IpDx = 0, IpDxLEN = 0;;
1681 if (len>IpDxLEN || IpDx[0] != fnt_width) {
1685 IpDx = smalloc((len+16)*sizeof(int));
1688 for(i=0; i<IpDxLEN; i++)
1689 IpDx[i] = fnt_width;
1695 if ((attr & ATTR_ACTCURS) && cfg.cursor_type == 0) {
1696 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1697 attr ^= ATTR_CUR_XOR;
1701 if (cfg.vtmode == VT_OEMONLY)
1705 * Map high-half characters in order to approximate ISO using
1706 * OEM character set. No characters are missing if the OEM codepage
1709 if (nfont & FONT_OEM) {
1711 for (i=0; i<len; i++)
1712 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1714 /* This is CP850 ... perfect translation */
1715 static const char oemhighhalf[] =
1716 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1717 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1718 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1719 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1720 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1721 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1722 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1723 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1724 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1725 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1726 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1727 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1730 /* This is CP437 ... junk translation */
1731 static const unsigned char oemhighhalf[] = {
1732 0x20, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1733 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1734 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1735 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1736 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1737 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1738 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1739 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1740 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1741 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1742 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1743 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1746 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1750 if (attr & ATTR_LINEDRW) {
1753 static const char poorman[] =
1754 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1757 static const char oemmap_437[] =
1758 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1759 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1762 static const char oemmap_850[] =
1763 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1764 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1766 /* Poor windows font ... eg: windows courier */
1767 static const char oemmap[] =
1768 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1769 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1772 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1773 * VT100 line drawing chars; everything else stays normal.
1775 * Actually '_' maps to space too, but that's done before.
1777 switch (cfg.vtmode) {
1779 for (i=0; i<len; i++)
1780 if (text[i] >= '\x60' && text[i] <= '\x7E')
1781 text[i] += '\x01' - '\x60';
1784 /* Make sure we actually have an OEM font */
1785 if (fonts[nfont|FONT_OEM]) {
1788 for (i=0; i<len; i++)
1789 if (text[i] >= '\x60' && text[i] <= '\x7E')
1790 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1794 for (i=0; i<len; i++)
1795 if (text[i] >= '\x60' && text[i] <= '\x7E')
1796 text[i] = poorman[(unsigned char)text[i] - 0x60];
1801 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1802 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1803 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1805 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1806 nfont |= FONT_UNDERLINE;
1809 if (nfont&FONT_UNDERLINE)
1810 force_manual_underline = 1;
1811 /* Don't do the same for manual bold, it could be bad news. */
1813 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1815 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1816 force_manual_underline = 1;
1817 if (attr & ATTR_REVERSE) {
1818 t = nfg; nfg = nbg; nbg = t;
1820 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1822 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1826 SelectObject (hdc, fonts[nfont]);
1827 SetTextColor (hdc, fg);
1828 SetBkColor (hdc, bg);
1829 SetBkMode (hdc, OPAQUE);
1832 line_box.right = x+fnt_width*len;
1833 line_box.bottom = y+font_height;
1834 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1835 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1836 SetBkMode (hdc, TRANSPARENT);
1838 /* GRR: This draws the character outside it's box and can leave
1839 * 'droppings' even with the clip box! I suppose I could loop it
1840 * one character at a time ... yuk.
1842 * Or ... I could do a test print with "W", and use +1 or -1 for this
1843 * shift depending on if the leftmost column is blank...
1845 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1847 if (force_manual_underline ||
1848 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1850 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1851 MoveToEx (hdc, x, y+descent, NULL);
1852 LineTo (hdc, x+len*fnt_width, y+descent);
1853 oldpen = SelectObject (hdc, oldpen);
1854 DeleteObject (oldpen);
1856 if ((attr & ATTR_PASCURS) && cfg.cursor_type == 0) {
1859 pts[0].x = pts[1].x = pts[4].x = x;
1860 pts[2].x = pts[3].x = x+fnt_width-1;
1861 pts[0].y = pts[3].y = pts[4].y = y;
1862 pts[1].y = pts[2].y = y+font_height-1;
1863 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1864 Polyline (hdc, pts, 5);
1865 oldpen = SelectObject (hdc, oldpen);
1866 DeleteObject (oldpen);
1868 if ((attr & (ATTR_ACTCURS | ATTR_PASCURS)) && cfg.cursor_type != 0) {
1869 int startx, starty, dx, dy, length, i;
1870 if (cfg.cursor_type == 1) {
1871 startx = x; starty = y+descent;
1872 dx = 1; dy = 0; length = fnt_width;
1875 if (attr & ATTR_RIGHTCURS)
1876 xadjust = fnt_width-1;
1877 startx = x+xadjust; starty = y;
1878 dx = 0; dy = 1; length = font_height;
1880 if (attr & ATTR_ACTCURS) {
1882 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1883 MoveToEx (hdc, startx, starty, NULL);
1884 LineTo (hdc, startx+dx*length, starty+dy*length);
1885 oldpen = SelectObject (hdc, oldpen);
1886 DeleteObject (oldpen);
1888 for (i = 0; i < length; i++) {
1890 SetPixel(hdc, startx, starty, colours[23]);
1892 startx += dx; starty += dy;
1898 static int check_compose(int first, int second) {
1900 static char * composetbl[] = {
1901 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1902 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1903 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1904 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1905 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1906 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1907 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1908 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1909 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1910 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1911 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1912 "\"uü", "'yý", "htþ", "\"yÿ",
1916 static int recurse = 0;
1919 for(c=composetbl; *c; c++) {
1920 if( (*c)[0] == first && (*c)[1] == second)
1922 return (*c)[2] & 0xFF;
1929 nc = check_compose(second, first);
1931 nc = check_compose(toupper(first), toupper(second));
1933 nc = check_compose(toupper(second), toupper(first));
1941 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1942 * codes. Returns number of bytes used or zero to drop the message
1943 * or -1 to forward the message to windows.
1945 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
1946 unsigned char *output) {
1948 int scan, left_alt = 0, key_down, shift_state;
1950 unsigned char * p = output;
1951 static int alt_state = 0;
1953 HKL kbd_layout = GetKeyboardLayout(0);
1955 static WORD keys[3];
1956 static int compose_char = 0;
1957 static WPARAM compose_key = 0;
1959 r = GetKeyboardState(keystate);
1960 if (!r) memset(keystate, 0, sizeof(keystate));
1964 { /* Tell us all about key events */
1965 static BYTE oldstate[256];
1966 static int first = 1;
1969 if(first) memcpy(oldstate, keystate, sizeof(oldstate));
1972 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT) {
1974 } else if ((HIWORD(lParam)&KF_UP) && scan==(HIWORD(lParam) & 0xFF) ) {
1978 if (wParam >= VK_F1 && wParam <= VK_F20 )
1979 debug(("K_F%d", wParam+1-VK_F1));
1982 case VK_SHIFT: debug(("SHIFT")); break;
1983 case VK_CONTROL: debug(("CTRL")); break;
1984 case VK_MENU: debug(("ALT")); break;
1985 default: debug(("VK_%02x", wParam));
1987 if(message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
1989 debug((", S%02x", scan=(HIWORD(lParam) & 0xFF) ));
1991 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
1992 if (ch>=' ' && ch<='~') debug((", '%c'", ch));
1993 else if (ch) debug((", $%02x", ch));
1995 if (keys[0]) debug((", KB0=%02x", keys[0]));
1996 if (keys[1]) debug((", KB1=%02x", keys[1]));
1997 if (keys[2]) debug((", KB2=%02x", keys[2]));
1999 if ( (keystate[VK_SHIFT]&0x80)!=0) debug((", S"));
2000 if ( (keystate[VK_CONTROL]&0x80)!=0) debug((", C"));
2001 if ( (HIWORD(lParam)&KF_EXTENDED) ) debug((", E"));
2002 if ( (HIWORD(lParam)&KF_UP) ) debug((", U"));
2005 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
2007 else if ( (HIWORD(lParam)&KF_UP) )
2008 oldstate[wParam&0xFF] ^= 0x80;
2010 oldstate[wParam&0xFF] ^= 0x81;
2012 for(ch=0; ch<256; ch++)
2013 if (oldstate[ch] != keystate[ch])
2014 debug((", M%02x=%02x", ch, keystate[ch]));
2016 memcpy(oldstate, keystate, sizeof(oldstate));
2020 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED)) {
2021 keystate[VK_RMENU] = keystate[VK_MENU];
2025 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2026 if ( (cfg.funky_type == 3 ||
2027 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2028 && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
2030 wParam = VK_EXECUTE;
2032 /* UnToggle NUMLock */
2033 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
2034 keystate[VK_NUMLOCK] ^= 1;
2037 /* And write back the 'adjusted' state */
2038 SetKeyboardState (keystate);
2041 /* Disable Auto repeat if required */
2042 if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
2045 if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
2048 key_down = ((HIWORD(lParam)&KF_UP)==0);
2050 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2051 if (left_alt && (keystate[VK_CONTROL]&0x80)) {
2052 if (cfg.ctrlaltkeys)
2053 keystate[VK_MENU] = 0;
2055 keystate[VK_RMENU] = 0x80;
2060 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2061 shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
2062 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
2064 /* Note if AltGr was pressed and if it was used as a compose key */
2065 if (!compose_state) {
2066 compose_key = 0x100;
2067 if (cfg.compose_key) {
2068 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
2069 compose_key = wParam;
2071 if (wParam == VK_APPS)
2072 compose_key = wParam;
2075 if (wParam == compose_key)
2077 if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
2079 else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
2084 else if (compose_state==1 && wParam != VK_CONTROL)
2088 * Record that we pressed key so the scroll window can be reset, but
2089 * be careful to avoid Shift-UP/Down
2091 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
2095 /* Make sure we're not pasting */
2096 if (key_down) term_nopaste();
2098 if (compose_state>1 && left_alt) compose_state = 0;
2100 /* Sanitize the number pad if not using a PC NumPad */
2101 if( left_alt || (app_keypad_keys && !cfg.no_applic_k
2102 && cfg.funky_type != 2)
2103 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state )
2105 if ((HIWORD(lParam)&KF_EXTENDED) == 0)
2110 case VK_INSERT: nParam = VK_NUMPAD0; break;
2111 case VK_END: nParam = VK_NUMPAD1; break;
2112 case VK_DOWN: nParam = VK_NUMPAD2; break;
2113 case VK_NEXT: nParam = VK_NUMPAD3; break;
2114 case VK_LEFT: nParam = VK_NUMPAD4; break;
2115 case VK_CLEAR: nParam = VK_NUMPAD5; break;
2116 case VK_RIGHT: nParam = VK_NUMPAD6; break;
2117 case VK_HOME: nParam = VK_NUMPAD7; break;
2118 case VK_UP: nParam = VK_NUMPAD8; break;
2119 case VK_PRIOR: nParam = VK_NUMPAD9; break;
2120 case VK_DELETE: nParam = VK_DECIMAL; break;
2124 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
2130 /* If a key is pressed and AltGr is not active */
2131 if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
2133 /* Okay, prepare for most alts then ...*/
2134 if (left_alt) *p++ = '\033';
2136 /* Lets see if it's a pattern we know all about ... */
2137 if (wParam == VK_PRIOR && shift_state == 1) {
2138 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2141 if (wParam == VK_NEXT && shift_state == 1) {
2142 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2145 if (wParam == VK_INSERT && shift_state == 1) {
2146 term_mouse (MB_PASTE, MA_CLICK, 0, 0);
2147 term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
2150 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2153 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2155 PostMessage(hwnd, WM_CHAR, ' ', 0);
2156 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2159 /* Control-Numlock for app-keypad mode switch */
2160 if (wParam == VK_PAUSE && shift_state == 2) {
2161 app_keypad_keys ^= 1;
2165 /* Nethack keypad */
2166 if (cfg.nethack_keypad && !left_alt) {
2168 case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
2169 case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
2170 case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
2171 case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
2172 case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
2173 case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
2174 case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
2175 case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
2176 case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
2180 /* Application Keypad */
2184 if ( cfg.funky_type == 3 ||
2185 ( cfg.funky_type <= 1 &&
2186 app_keypad_keys && !cfg.no_applic_k)) switch(wParam) {
2187 case VK_EXECUTE: xkey = 'P'; break;
2188 case VK_DIVIDE: xkey = 'Q'; break;
2189 case VK_MULTIPLY:xkey = 'R'; break;
2190 case VK_SUBTRACT:xkey = 'S'; break;
2192 if(app_keypad_keys && !cfg.no_applic_k) switch(wParam) {
2193 case VK_NUMPAD0: xkey = 'p'; break;
2194 case VK_NUMPAD1: xkey = 'q'; break;
2195 case VK_NUMPAD2: xkey = 'r'; break;
2196 case VK_NUMPAD3: xkey = 's'; break;
2197 case VK_NUMPAD4: xkey = 't'; break;
2198 case VK_NUMPAD5: xkey = 'u'; break;
2199 case VK_NUMPAD6: xkey = 'v'; break;
2200 case VK_NUMPAD7: xkey = 'w'; break;
2201 case VK_NUMPAD8: xkey = 'x'; break;
2202 case VK_NUMPAD9: xkey = 'y'; break;
2204 case VK_DECIMAL: xkey = 'n'; break;
2205 case VK_ADD: if(cfg.funky_type==2) {
2206 if(shift_state) xkey = 'l';
2208 } else if(shift_state) xkey = 'm';
2212 case VK_DIVIDE: if(cfg.funky_type==2) xkey = 'o'; break;
2213 case VK_MULTIPLY:if(cfg.funky_type==2) xkey = 'j'; break;
2214 case VK_SUBTRACT:if(cfg.funky_type==2) xkey = 'm'; break;
2217 if (HIWORD(lParam)&KF_EXTENDED)
2225 if (xkey>='P' && xkey<='S')
2226 p += sprintf((char *)p, "\x1B%c", xkey);
2228 p += sprintf((char *)p, "\x1B?%c", xkey);
2231 p += sprintf((char *)p, "\x1BO%c", xkey);
2236 if (wParam == VK_BACK && shift_state == 0 ) /* Backspace */
2238 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2241 if (wParam == VK_TAB && shift_state == 1 ) /* Shift tab */
2243 *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
2245 if (wParam == VK_SPACE && shift_state == 2 ) /* Ctrl-Space */
2247 *p++ = 0; return p - output;
2249 if (wParam == VK_SPACE && shift_state == 3 ) /* Ctrl-Shift-Space */
2251 *p++ = 160; return p - output;
2253 if (wParam == VK_CANCEL && shift_state == 2 ) /* Ctrl-Break */
2255 *p++ = 3; return p - output;
2257 if (wParam == VK_PAUSE) /* Break/Pause */
2259 *p++ = 26; *p++ = 0; return -2;
2261 /* Control-2 to Control-8 are special */
2262 if (shift_state == 2 && wParam >= '2' && wParam <= '8')
2264 *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
2267 if (shift_state == 2 && wParam == 0xBD) {
2271 if (shift_state == 2 && wParam == 0xDF) {
2275 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2276 *p++ = '\r'; *p++ = '\n';
2281 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2282 * for integer decimal nn.)
2284 * We also deal with the weird ones here. Linux VCs replace F1
2285 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2286 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2291 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
2292 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
2293 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
2294 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
2295 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
2296 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
2297 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
2298 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
2299 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
2300 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
2301 case VK_F11: code = 23; break;
2302 case VK_F12: code = 24; break;
2303 case VK_F13: code = 25; break;
2304 case VK_F14: code = 26; break;
2305 case VK_F15: code = 28; break;
2306 case VK_F16: code = 29; break;
2307 case VK_F17: code = 31; break;
2308 case VK_F18: code = 32; break;
2309 case VK_F19: code = 33; break;
2310 case VK_F20: code = 34; break;
2311 case VK_HOME: code = 1; break;
2312 case VK_INSERT: code = 2; break;
2313 case VK_DELETE: code = 3; break;
2314 case VK_END: code = 4; break;
2315 case VK_PRIOR: code = 5; break;
2316 case VK_NEXT: code = 6; break;
2318 /* Reorder edit keys to physical order */
2319 if (cfg.funky_type == 3 && code <= 6 ) code = "\0\2\1\4\5\3\6"[code];
2321 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2322 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
2325 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2327 p += sprintf((char *)p, "\x1B%c", code + 'P' - 11);
2329 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
2332 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2333 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
2337 p += sprintf((char *)p, "\x1B[%d~", code);
2342 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2343 * some reason seems to send VK_CLEAR to Windows...).
2348 case VK_UP: xkey = 'A'; break;
2349 case VK_DOWN: xkey = 'B'; break;
2350 case VK_RIGHT: xkey = 'C'; break;
2351 case VK_LEFT: xkey = 'D'; break;
2352 case VK_CLEAR: xkey = 'G'; break;
2357 p += sprintf((char *)p, "\x1B%c", xkey);
2358 else if (app_cursor_keys && !cfg.no_applic_c)
2359 p += sprintf((char *)p, "\x1BO%c", xkey);
2361 p += sprintf((char *)p, "\x1B[%c", xkey);
2367 * Finally, deal with Return ourselves. (Win95 seems to
2368 * foul it up when Alt is pressed, for some reason.)
2370 if (wParam == VK_RETURN) /* Return */
2377 /* Okay we've done everything interesting; let windows deal with
2378 * the boring stuff */
2380 BOOL capsOn=keystate[VK_CAPITAL] !=0;
2382 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2383 if(cfg.xlat_capslockcyr)
2384 keystate[VK_CAPITAL] = 0;
2386 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
2392 unsigned char ch = (unsigned char)keys[i];
2394 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
2399 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
2403 if ((nc=check_compose(compose_char,ch)) == -1)
2405 MessageBeep(MB_ICONHAND);
2408 *p++ = xlat_kbd2tty((unsigned char)nc);
2414 if( left_alt && key_down ) *p++ = '\033';
2420 ch = xlat_latkbd2win(ch);
2421 *p++ = xlat_kbd2tty(ch);
2425 /* This is so the ALT-Numpad and dead keys work correctly. */
2430 /* If we're definitly not building up an ALT-54321 then clear it */
2431 if (!left_alt) keys[0] = 0;
2434 /* ALT alone may or may not want to bring up the System menu */
2435 if (wParam == VK_MENU) {
2437 if (message == WM_SYSKEYDOWN)
2439 else if (message == WM_SYSKEYUP && alt_state)
2440 PostMessage(hwnd, WM_CHAR, ' ', 0);
2441 if (message == WM_SYSKEYUP)
2451 void set_title (char *title) {
2452 sfree (window_name);
2453 window_name = smalloc(1+strlen(title));
2454 strcpy (window_name, title);
2455 if (cfg.win_name_always || !IsIconic(hwnd))
2456 SetWindowText (hwnd, title);
2459 void set_icon (char *title) {
2461 icon_name = smalloc(1+strlen(title));
2462 strcpy (icon_name, title);
2463 if (!cfg.win_name_always && IsIconic(hwnd))
2464 SetWindowText (hwnd, title);
2467 void set_sbar (int total, int start, int page) {
2470 if (!cfg.scrollbar) return;
2472 si.cbSize = sizeof(si);
2473 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2475 si.nMax = total - 1;
2479 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2482 Context get_ctx(void) {
2487 SelectPalette (hdc, pal, FALSE);
2493 void free_ctx (Context ctx) {
2494 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2495 ReleaseDC (hwnd, ctx);
2498 static void real_palette_set (int n, int r, int g, int b) {
2500 logpal->palPalEntry[n].peRed = r;
2501 logpal->palPalEntry[n].peGreen = g;
2502 logpal->palPalEntry[n].peBlue = b;
2503 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2504 colours[n] = PALETTERGB(r, g, b);
2505 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2507 colours[n] = RGB(r, g, b);
2510 void palette_set (int n, int r, int g, int b) {
2511 static const int first[21] = {
2512 0, 2, 4, 6, 8, 10, 12, 14,
2513 1, 3, 5, 7, 9, 11, 13, 15,
2516 real_palette_set (first[n], r, g, b);
2518 real_palette_set (first[n]+1, r, g, b);
2520 HDC hdc = get_ctx();
2521 UnrealizeObject (pal);
2522 RealizePalette (hdc);
2527 void palette_reset (void) {
2530 for (i = 0; i < NCOLOURS; i++) {
2532 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2533 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2534 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2535 logpal->palPalEntry[i].peFlags = 0;
2536 colours[i] = PALETTERGB(defpal[i].rgbtRed,
2537 defpal[i].rgbtGreen,
2538 defpal[i].rgbtBlue);
2540 colours[i] = RGB(defpal[i].rgbtRed,
2541 defpal[i].rgbtGreen,
2542 defpal[i].rgbtBlue);
2547 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2549 RealizePalette (hdc);
2554 void write_clip (void *data, int len, int must_deselect) {
2558 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2561 lock = GlobalLock (clipdata);
2564 memcpy (lock, data, len);
2565 ((unsigned char *) lock) [len] = 0;
2566 GlobalUnlock (clipdata);
2569 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2571 if (OpenClipboard (hwnd)) {
2573 SetClipboardData (CF_TEXT, clipdata);
2576 GlobalFree (clipdata);
2579 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2582 void get_clip (void **p, int *len) {
2583 static HGLOBAL clipdata = NULL;
2587 GlobalUnlock (clipdata);
2591 if (OpenClipboard (NULL)) {
2592 clipdata = GetClipboardData (CF_TEXT);
2595 *p = GlobalLock (clipdata);
2609 * Move `lines' lines from position `from' to position `to' in the
2612 void optimised_move (int to, int from, int lines) {
2616 min = (to < from ? to : from);
2617 max = to + from - min;
2619 r.left = 0; r.right = cols * font_width;
2620 r.top = min * font_height; r.bottom = (max+lines) * font_height;
2621 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2625 * Print a message box and perform a fatal exit.
2627 void fatalbox(char *fmt, ...) {
2632 vsprintf(stuff, fmt, ap);
2634 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2641 void beep(int mode) {
2642 if (mode == BELL_DEFAULT) {
2644 } else if (mode == BELL_WAVEFILE) {
2645 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
2646 char buf[sizeof(cfg.bell_wavefile)+80];
2647 sprintf(buf, "Unable to play sound file\n%s\n"
2648 "Using default sound instead", cfg.bell_wavefile);
2649 MessageBox(hwnd, buf, "PuTTY Sound Error", MB_OK | MB_ICONEXCLAMATION);
2650 cfg.beep = BELL_DEFAULT;