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_CLIP (WM_XUSER + 2)
54 /* Needed for Chinese support and apparently not always defined. */
56 #define VK_PROCESSKEY 0xE5
59 /* Needed for mouse wheel support and not defined in earlier SDKs. */
61 #define WM_MOUSEWHEEL 0x020A
64 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
65 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
66 unsigned char *output);
67 static void cfgtopalette(void);
68 static void init_palette(void);
69 static void init_fonts(int, int);
70 static void another_font(int);
71 static void deinit_fonts(void);
73 /* Window layout information */
74 static void reset_window(int);
75 static int full_screen = 0, extra_width, extra_height;
76 static int font_width, font_height, font_dualwidth;
77 static int offset_width, offset_height;
78 static int was_zoomed = 0;
79 static int prev_rows, prev_cols;
81 static LONG old_wind_style;
82 static WINDOWPLACEMENT old_wind_placement;
84 static int pending_netevent = 0;
85 static WPARAM pend_netevent_wParam = 0;
86 static LPARAM pend_netevent_lParam = 0;
87 static void enact_pending_netevent(void);
88 static void flash_window(int mode);
89 static void flip_full_screen(void);
91 static time_t last_movement = 0;
95 #define FONT_UNDERLINE 2
96 #define FONT_BOLDUND 3
97 #define FONT_WIDE 0x04
98 #define FONT_HIGH 0x08
99 #define FONT_NARROW 0x10
101 #define FONT_OEM 0x20
102 #define FONT_OEMBOLD 0x21
103 #define FONT_OEMUND 0x22
104 #define FONT_OEMBOLDUND 0x23
106 #define FONT_MAXNO 0x2F
108 static HFONT fonts[FONT_MAXNO];
109 static int fontflag[FONT_MAXNO];
111 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
119 static COLORREF colours[NCOLOURS];
121 static LPLOGPALETTE logpal;
122 static RGBTRIPLE defpal[NCOLOURS];
126 static HBITMAP caretbm;
128 static int dbltime, lasttime, lastact;
129 static Mouse_Button lastbtn;
131 /* this allows xterm-style mouse handling. */
132 static int send_raw_mouse = 0;
133 static int wheel_accumulator = 0;
135 static char *window_name, *icon_name;
137 static int compose_state = 0;
139 static OSVERSIONINFO osVersion;
141 /* Dummy routine, only required in plink. */
142 void ldisc_update(int echo, int edit)
146 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
148 static char appname[] = "PuTTY";
153 int guess_width, guess_height;
156 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
158 winsock_ver = MAKEWORD(1, 1);
159 if (WSAStartup(winsock_ver, &wsadata)) {
160 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
161 MB_OK | MB_ICONEXCLAMATION);
164 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
165 MessageBox(NULL, "WinSock version is incompatible with 1.1",
166 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
170 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
173 InitCommonControls();
175 /* Ensure a Maximize setting in Explorer doesn't maximise the
180 ZeroMemory(&osVersion, sizeof(osVersion));
181 osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
182 if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
183 MessageBox(NULL, "Windows refuses to report a version",
184 "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
190 * Process the command line.
195 default_protocol = DEFAULT_PROTOCOL;
196 default_port = DEFAULT_PORT;
197 cfg.logtype = LGTYP_NONE;
199 do_defaults(NULL, &cfg);
202 while (*p && isspace(*p))
206 * Process command line options first. Yes, this can be
207 * done better, and it will be as soon as I have the
211 char *q = p + strcspn(p, " \t");
214 tolower(p[0]) == 's' &&
215 tolower(p[1]) == 's' && tolower(p[2]) == 'h') {
216 default_protocol = cfg.protocol = PROT_SSH;
217 default_port = cfg.port = 22;
218 } else if (q == p + 7 &&
219 tolower(p[0]) == 'c' &&
220 tolower(p[1]) == 'l' &&
221 tolower(p[2]) == 'e' &&
222 tolower(p[3]) == 'a' &&
223 tolower(p[4]) == 'n' &&
224 tolower(p[5]) == 'u' && tolower(p[6]) == 'p') {
226 * `putty -cleanup'. Remove all registry entries
227 * associated with PuTTY, and also find and delete
228 * the random seed file.
231 "This procedure will remove ALL Registry\n"
232 "entries associated with PuTTY, and will\n"
233 "also remove the PuTTY random seed file.\n"
235 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
236 "SESSIONS. Are you really sure you want\n"
239 MB_YESNO | MB_ICONWARNING) == IDYES) {
244 p = q + strspn(q, " \t");
248 * An initial @ means to activate a saved session.
252 while (i > 1 && isspace(p[i - 1]))
255 do_defaults(p + 1, &cfg);
256 if (!*cfg.host && !do_config()) {
260 } else if (*p == '&') {
262 * An initial & means we've been given a command line
263 * containing the hex value of a HANDLE for a file
264 * mapping object, which we must then extract as a
269 if (sscanf(p + 1, "%p", &filemap) == 1 &&
270 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
271 0, 0, sizeof(Config))) != NULL) {
274 CloseHandle(filemap);
275 } else if (!do_config()) {
282 * If the hostname starts with "telnet:", set the
283 * protocol to Telnet and process the string as a
286 if (!strncmp(q, "telnet:", 7)) {
290 if (q[0] == '/' && q[1] == '/')
292 cfg.protocol = PROT_TELNET;
294 while (*p && *p != ':' && *p != '/')
303 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
304 cfg.host[sizeof(cfg.host) - 1] = '\0';
306 while (*p && !isspace(*p))
310 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
311 cfg.host[sizeof(cfg.host) - 1] = '\0';
312 while (*p && isspace(*p))
327 * Trim leading whitespace off the hostname if it's there.
330 int space = strspn(cfg.host, " \t");
331 memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
334 /* See if host is of the form user@host */
335 if (cfg.host[0] != '\0') {
336 char *atsign = strchr(cfg.host, '@');
337 /* Make sure we're not overflowing the user field */
339 if (atsign - cfg.host < sizeof cfg.username) {
340 strncpy(cfg.username, cfg.host, atsign - cfg.host);
341 cfg.username[atsign - cfg.host] = '\0';
343 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
348 * Trim a colon suffix off the hostname if it's there.
350 cfg.host[strcspn(cfg.host, ":")] = '\0';
354 * Select protocol. This is farmed out into a table in a
355 * separate file to enable an ssh-free variant.
360 for (i = 0; backends[i].backend != NULL; i++)
361 if (backends[i].protocol == cfg.protocol) {
362 back = backends[i].backend;
366 MessageBox(NULL, "Unsupported protocol number found",
367 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
373 /* Check for invalid Port number (i.e. zero) */
375 MessageBox(NULL, "Invalid Port Number",
376 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
383 wndclass.lpfnWndProc = WndProc;
384 wndclass.cbClsExtra = 0;
385 wndclass.cbWndExtra = 0;
386 wndclass.hInstance = inst;
387 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
388 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
389 wndclass.hbrBackground = NULL;
390 wndclass.lpszMenuName = NULL;
391 wndclass.lpszClassName = appname;
393 RegisterClass(&wndclass);
398 savelines = cfg.savelines;
404 * Guess some defaults for the window size. This all gets
405 * updated later, so we don't really care too much. However, we
406 * do want the font width/height guesses to correspond to a
407 * large font rather than a small one...
414 term_size(cfg.height, cfg.width, cfg.savelines);
415 guess_width = extra_width + font_width * cols;
416 guess_height = extra_height + font_height * rows;
419 HWND w = GetDesktopWindow();
420 GetWindowRect(w, &r);
421 if (guess_width > r.right - r.left)
422 guess_width = r.right - r.left;
423 if (guess_height > r.bottom - r.top)
424 guess_height = r.bottom - r.top;
428 int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
431 winmode &= ~(WS_VSCROLL);
432 if (cfg.locksize && cfg.lockfont)
433 winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
435 exwinmode |= WS_EX_TOPMOST;
437 exwinmode |= WS_EX_CLIENTEDGE;
438 hwnd = CreateWindowEx(exwinmode, appname, appname,
439 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
440 guess_width, guess_height,
441 NULL, NULL, inst, NULL);
445 * Initialise the fonts, simultaneously correcting the guesses
446 * for font_{width,height}.
451 * Correct the guesses for extra_{width,height}.
455 GetWindowRect(hwnd, &wr);
456 GetClientRect(hwnd, &cr);
457 offset_width = offset_height = cfg.window_border;
458 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
459 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
463 * Resize the window, now we know what size we _really_ want it
466 guess_width = extra_width + font_width * cols;
467 guess_height = extra_height + font_height * rows;
468 SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
469 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
472 * Set up a caret bitmap, with no content.
476 int size = (font_width + 15) / 16 * 2 * font_height;
477 bits = smalloc(size);
478 memset(bits, 0, size);
479 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
482 CreateCaret(hwnd, caretbm, font_width, font_height);
485 * Initialise the scroll bar.
490 si.cbSize = sizeof(si);
491 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
496 SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
500 * Start up the telnet connection.
504 char msg[1024], *title;
507 error = back->init(cfg.host, cfg.port, &realhost);
509 sprintf(msg, "Unable to open connection to\n"
510 "%.800s\n" "%s", cfg.host, error);
511 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
514 window_name = icon_name = NULL;
516 title = cfg.wintitle;
518 sprintf(msg, "%s - PuTTY", realhost);
526 session_closed = FALSE;
529 * Set up the input and output buffers.
532 outbuf_reap = outbuf_head = 0;
535 * Prepare the mouse handler.
537 lastact = MA_NOTHING;
538 lastbtn = MBT_NOTHING;
539 dbltime = GetDoubleClickTime();
542 * Set up the session-control options on the system menu.
545 HMENU m = GetSystemMenu(hwnd, FALSE);
549 AppendMenu(m, MF_SEPARATOR, 0, 0);
550 if (cfg.protocol == PROT_TELNET) {
552 AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
553 AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
554 AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
555 AppendMenu(p, MF_SEPARATOR, 0, 0);
556 AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
557 AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
558 AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
559 AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
560 AppendMenu(p, MF_SEPARATOR, 0, 0);
561 AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
562 AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
563 AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
564 AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
565 AppendMenu(p, MF_SEPARATOR, 0, 0);
566 AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
567 AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
568 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
570 AppendMenu(m, MF_SEPARATOR, 0, 0);
572 AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
573 AppendMenu(m, MF_SEPARATOR, 0, 0);
574 AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
575 AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
578 for (i = 1; i < ((nsessions < 256) ? nsessions : 256); i++)
579 AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
581 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
582 AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
583 AppendMenu(m, MF_SEPARATOR, 0, 0);
584 AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
585 AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
586 AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
587 AppendMenu(m, MF_SEPARATOR, 0, 0);
588 AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
592 * Finally show the window!
594 ShowWindow(hwnd, show);
595 SetForegroundWindow(hwnd);
598 * Open the initial log file if there is one.
603 * Set the palette up.
609 has_focus = (GetForegroundWindow() == hwnd);
612 if (GetMessage(&msg, NULL, 0, 0) == 1) {
613 int timer_id = 0, long_timer = 0;
615 while (msg.message != WM_QUIT) {
616 /* Sometimes DispatchMessage calls routines that use their own
617 * GetMessage loop, setup this timer so we get some control back.
619 * Also call term_update() from the timer so that if the host
620 * is sending data flat out we still do redraws.
622 if (timer_id && long_timer) {
623 KillTimer(hwnd, timer_id);
624 long_timer = timer_id = 0;
627 timer_id = SetTimer(hwnd, 1, 20, NULL);
628 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
629 DispatchMessage(&msg);
631 /* Make sure we blink everything that needs it. */
634 /* Send the paste buffer if there's anything to send */
637 /* If there's nothing new in the queue then we can do everything
638 * we've delayed, reading the socket, writing, and repainting
641 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
644 if (pending_netevent) {
645 enact_pending_netevent();
647 /* Force the cursor blink on */
650 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
654 /* Okay there is now nothing to do so we make sure the screen is
655 * completely up to date then tell windows to call us in a little
659 KillTimer(hwnd, timer_id);
668 flash_window(1); /* maintain */
671 /* Hmm, term_update didn't want to do an update too soon ... */
672 timer_id = SetTimer(hwnd, 1, 50, NULL);
674 timer_id = SetTimer(hwnd, 1, 500, NULL);
676 timer_id = SetTimer(hwnd, 1, 100, NULL);
679 /* There's no point rescanning everything in the message queue
680 * so we do an apparently unnecessary wait here
683 if (GetMessage(&msg, NULL, 0, 0) != 1)
697 if (cfg.protocol == PROT_SSH) {
708 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
710 char *do_select(SOCKET skt, int startup)
715 events = (FD_CONNECT | FD_READ | FD_WRITE |
716 FD_OOB | FD_CLOSE | FD_ACCEPT);
721 return "do_select(): internal error (hwnd==NULL)";
722 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
723 switch (WSAGetLastError()) {
725 return "Network is down";
727 return "WSAAsyncSelect(): unknown error";
734 * set or clear the "raw mouse message" mode
736 void set_raw_mouse_mode(int activate)
738 send_raw_mouse = activate;
739 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
743 * Print a message box and close the connection.
745 void connection_fatal(char *fmt, ...)
751 vsprintf(stuff, fmt, ap);
753 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
754 if (cfg.close_on_exit == COE_ALWAYS)
757 session_closed = TRUE;
758 SetWindowText(hwnd, "PuTTY (inactive)");
763 * Actually do the job requested by a WM_NETEVENT
765 static void enact_pending_netevent(void)
767 static int reentering = 0;
768 extern int select_result(WPARAM, LPARAM);
772 return; /* don't unpend the pending */
774 pending_netevent = FALSE;
777 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
780 if (ret == 0 && !session_closed) {
781 /* Abnormal exits will already have set session_closed and taken
782 * appropriate action. */
783 if (cfg.close_on_exit == COE_ALWAYS ||
784 cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
786 session_closed = TRUE;
787 SetWindowText(hwnd, "PuTTY (inactive)");
788 MessageBox(hwnd, "Connection closed by remote host",
789 "PuTTY", MB_OK | MB_ICONINFORMATION);
795 * Copy the colour palette from the configuration data into defpal.
796 * This is non-trivial because the colour indices are different.
798 static void cfgtopalette(void)
801 static const int ww[] = {
802 6, 7, 8, 9, 10, 11, 12, 13,
803 14, 15, 16, 17, 18, 19, 20, 21,
804 0, 1, 2, 3, 4, 4, 5, 5
807 for (i = 0; i < 24; i++) {
809 defpal[i].rgbtRed = cfg.colours[w][0];
810 defpal[i].rgbtGreen = cfg.colours[w][1];
811 defpal[i].rgbtBlue = cfg.colours[w][2];
816 * Set up the colour palette.
818 static void init_palette(void)
821 HDC hdc = GetDC(hwnd);
823 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
824 logpal = smalloc(sizeof(*logpal)
825 - sizeof(logpal->palPalEntry)
826 + NCOLOURS * sizeof(PALETTEENTRY));
827 logpal->palVersion = 0x300;
828 logpal->palNumEntries = NCOLOURS;
829 for (i = 0; i < NCOLOURS; i++) {
830 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
831 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
832 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
833 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
835 pal = CreatePalette(logpal);
837 SelectPalette(hdc, pal, FALSE);
839 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
842 ReleaseDC(hwnd, hdc);
845 for (i = 0; i < NCOLOURS; i++)
846 colours[i] = PALETTERGB(defpal[i].rgbtRed,
850 for (i = 0; i < NCOLOURS; i++)
851 colours[i] = RGB(defpal[i].rgbtRed,
852 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
856 * Initialise all the fonts we will need initially. There may be as many as
857 * three or as few as one. The other (poentially) twentyone fonts are done
858 * if/when they are needed.
862 * - check the font width and height, correcting our guesses if
865 * - verify that the bold font is the same width as the ordinary
866 * one, and engage shadow bolding if not.
868 * - verify that the underlined font is the same width as the
869 * ordinary one (manual underlining by means of line drawing can
870 * be done in a pinch).
872 static void init_fonts(int pick_width, int pick_height)
879 int fw_dontcare, fw_bold;
881 for (i = 0; i < FONT_MAXNO; i++)
884 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
887 if (cfg.fontisbold) {
888 fw_dontcare = FW_BOLD;
891 fw_dontcare = FW_DONTCARE;
898 font_height = pick_height;
900 font_height = cfg.fontheight;
901 if (font_height > 0) {
903 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
906 font_width = pick_width;
909 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
910 c, OUT_DEFAULT_PRECIS, \
911 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
912 FIXED_PITCH | FF_DONTCARE, cfg.font)
914 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
916 SelectObject(hdc, fonts[FONT_NORMAL]);
917 GetTextMetrics(hdc, &tm);
919 if (pick_width == 0 || pick_height == 0) {
920 font_height = tm.tmHeight;
921 font_width = tm.tmAveCharWidth;
923 font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth);
925 #ifdef RDB_DEBUG_PATCH
926 debug(23, "Primary font H=%d, AW=%d, MW=%d",
927 tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth);
932 DWORD cset = tm.tmCharSet;
933 memset(&info, 0xFF, sizeof(info));
935 /* !!! Yes the next line is right */
936 if (cset == OEM_CHARSET)
937 font_codepage = GetOEMCP();
939 if (TranslateCharsetInfo
940 ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
945 GetCPInfo(font_codepage, &cpinfo);
946 dbcs_screenfont = (cpinfo.MaxCharSize > 1);
949 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
952 * Some fonts, e.g. 9-pt Courier, draw their underlines
953 * outside their character cell. We successfully prevent
954 * screen corruption by clipping the text output, but then
955 * we lose the underline completely. Here we try to work
956 * out whether this is such a font, and if it is, we set a
957 * flag that causes underlines to be drawn by hand.
959 * Having tried other more sophisticated approaches (such
960 * as examining the TEXTMETRIC structure or requesting the
961 * height of a string), I think we'll do this the brute
962 * force way: we create a small bitmap, draw an underlined
963 * space on it, and test to see whether any pixels are
964 * foreground-coloured. (Since we expect the underline to
965 * go all the way across the character cell, we only search
966 * down a single column of the bitmap, half way across.)
970 HBITMAP und_bm, und_oldbm;
974 und_dc = CreateCompatibleDC(hdc);
975 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
976 und_oldbm = SelectObject(und_dc, und_bm);
977 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
978 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
979 SetTextColor(und_dc, RGB(255, 255, 255));
980 SetBkColor(und_dc, RGB(0, 0, 0));
981 SetBkMode(und_dc, OPAQUE);
982 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
984 for (i = 0; i < font_height; i++) {
985 c = GetPixel(und_dc, font_width / 2, i);
986 if (c != RGB(0, 0, 0))
989 SelectObject(und_dc, und_oldbm);
990 DeleteObject(und_bm);
994 DeleteObject(fonts[FONT_UNDERLINE]);
995 fonts[FONT_UNDERLINE] = 0;
999 if (bold_mode == BOLD_FONT) {
1000 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
1004 descent = tm.tmAscent + 1;
1005 if (descent >= font_height)
1006 descent = font_height - 1;
1008 for (i = 0; i < 3; i++) {
1010 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
1011 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
1018 ReleaseDC(hwnd, hdc);
1020 if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
1021 und_mode = UND_LINE;
1022 DeleteObject(fonts[FONT_UNDERLINE]);
1023 fonts[FONT_UNDERLINE] = 0;
1026 if (bold_mode == BOLD_FONT &&
1027 fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
1028 bold_mode = BOLD_SHADOW;
1029 DeleteObject(fonts[FONT_BOLD]);
1030 fonts[FONT_BOLD] = 0;
1032 fontflag[0] = fontflag[1] = fontflag[2] = 1;
1037 static void another_font(int fontno)
1040 int fw_dontcare, fw_bold;
1044 if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1047 basefont = (fontno & ~(FONT_BOLDUND));
1048 if (basefont != fontno && !fontflag[basefont])
1049 another_font(basefont);
1051 if (cfg.fontisbold) {
1052 fw_dontcare = FW_BOLD;
1055 fw_dontcare = FW_DONTCARE;
1059 c = cfg.fontcharset;
1065 if (fontno & FONT_WIDE)
1067 if (fontno & FONT_NARROW)
1069 if (fontno & FONT_OEM)
1071 if (fontno & FONT_BOLD)
1073 if (fontno & FONT_UNDERLINE)
1077 CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1078 FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1079 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1080 FIXED_PITCH | FF_DONTCARE, s);
1082 fontflag[fontno] = 1;
1085 static void deinit_fonts(void)
1088 for (i = 0; i < FONT_MAXNO; i++) {
1090 DeleteObject(fonts[i]);
1096 void request_resize(int w, int h)
1100 /* If the window is maximized supress resizing attempts */
1101 if (IsZoomed(hwnd)) {
1106 if (cfg.lockfont && cfg.locksize) return;
1107 if (h == rows && w == cols) return;
1109 /* Sanity checks ... */
1111 static int first_time = 1;
1114 switch (first_time) {
1116 /* Get the size of the screen */
1117 if (GetClientRect(GetDesktopWindow(), &ss))
1118 /* first_time = 0 */ ;
1124 /* Make sure the values are sane */
1125 width = (ss.right - ss.left - extra_width) / 4;
1126 height = (ss.bottom - ss.top - extra_height) / 6;
1128 if (w > width || h > height)
1137 term_size(h, w, cfg.savelines);
1140 width = extra_width + font_width * w;
1141 height = extra_height + font_height * h;
1143 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1144 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1145 SWP_NOMOVE | SWP_NOZORDER);
1149 InvalidateRect(hwnd, NULL, TRUE);
1152 static void reset_window(int reinit) {
1154 * This function decides how to resize or redraw when the
1155 * user changes something.
1157 * This function doesn't like to change the terminal size but if the
1158 * font size is locked that may be it's only soluion.
1160 int win_width, win_height;
1163 #ifdef RDB_DEBUG_PATCH
1164 debug((27, "reset_window()"));
1167 /* Current window sizes ... */
1168 GetWindowRect(hwnd, &wr);
1169 GetClientRect(hwnd, &cr);
1171 win_width = cr.right - cr.left;
1172 win_height = cr.bottom - cr.top;
1174 /* Are we being forced to reload the fonts ? */
1176 #ifdef RDB_DEBUG_PATCH
1177 debug((27, "reset_window() -- Forced deinit"));
1183 /* Oh, looks like we're minimised */
1184 if (win_width == 0 || win_height == 0)
1187 /* Is the window out of position ? */
1189 (offset_width != (win_width-font_width*cols)/2 ||
1190 offset_height != (win_height-font_height*rows)/2) ){
1191 offset_width = (win_width-font_width*cols)/2;
1192 offset_height = (win_height-font_height*rows)/2;
1193 InvalidateRect(hwnd, NULL, TRUE);
1194 #ifdef RDB_DEBUG_PATCH
1195 debug((27, "reset_window() -> Reposition terminal"));
1199 if (IsZoomed(hwnd)) {
1200 /* We're fullscreen, this means we must not change the size of
1201 * the window so it's the font size or the terminal itself.
1204 extra_width = wr.right - wr.left - cr.right + cr.left;
1205 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1207 if (!cfg.lockfont) {
1208 if ( font_width != win_width/cols ||
1209 font_height != win_height/rows) {
1211 init_fonts(win_width/cols, win_height/rows);
1212 offset_width = (win_width-font_width*cols)/2;
1213 offset_height = (win_height-font_height*rows)/2;
1214 InvalidateRect(hwnd, NULL, TRUE);
1215 #ifdef RDB_DEBUG_PATCH
1216 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1217 font_width, font_height));
1221 if ( font_width != win_width/cols ||
1222 font_height != win_height/rows) {
1223 /* Our only choice at this point is to change the
1224 * size of the terminal; Oh well.
1226 term_size( win_height/font_height, win_width/font_width,
1228 offset_width = (win_width-font_width*cols)/2;
1229 offset_height = (win_height-font_height*rows)/2;
1230 InvalidateRect(hwnd, NULL, TRUE);
1231 #ifdef RDB_DEBUG_PATCH
1232 debug((27, "reset_window() -> Zoomed term_size"));
1239 /* Hmm, a force re-init means we should ignore the current window
1240 * so we resize to the default font size.
1243 #ifdef RDB_DEBUG_PATCH
1244 debug((27, "reset_window() -> Forced re-init"));
1247 offset_width = offset_height = cfg.window_border;
1248 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1249 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1251 if (win_width != font_width*cols + offset_width*2 ||
1252 win_height != font_height*rows + offset_height*2) {
1254 /* If this is too large windows will resize it to the maximum
1255 * allowed window size, we will then be back in here and resize
1256 * the font or terminal to fit.
1258 SetWindowPos(hwnd, NULL, 0, 0,
1259 font_width*cols + extra_width,
1260 font_height*rows + extra_height,
1261 SWP_NOMOVE | SWP_NOZORDER);
1266 /* Okay the user doesn't want us to change the font so we try the
1267 * window. But that may be too big for the screen which forces us
1268 * to change the terminal.
1270 if ((cfg.lockfont && reinit==0) || reinit>0) {
1271 offset_width = offset_height = cfg.window_border;
1272 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1273 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1275 if (win_width != font_width*cols + offset_width*2 ||
1276 win_height != font_height*rows + offset_height*2) {
1281 GetClientRect(GetDesktopWindow(), &ss);
1282 width = (ss.right - ss.left - extra_width) / font_width;
1283 height = (ss.bottom - ss.top - extra_height) / font_height;
1286 if ( rows > height || cols > width ) {
1287 if ( height > rows ) height = rows;
1288 if ( width > cols ) width = cols;
1289 term_size(height, width, cfg.savelines);
1290 #ifdef RDB_DEBUG_PATCH
1291 debug((27, "reset_window() -> term resize to (%d,%d)",
1296 SetWindowPos(hwnd, NULL, 0, 0,
1297 font_width*cols + extra_width,
1298 font_height*rows + extra_height,
1299 SWP_NOMOVE | SWP_NOZORDER);
1301 InvalidateRect(hwnd, NULL, TRUE);
1302 #ifdef RDB_DEBUG_PATCH
1303 debug((27, "reset_window() -> window resize to (%d,%d)",
1304 font_width*cols + extra_width,
1305 font_height*rows + extra_height));
1311 /* We're allowed to or must change the font but do we want to ? */
1313 if (font_width != (win_width-cfg.window_border*2)/cols ||
1314 font_height != (win_height-cfg.window_border*2)/rows) {
1317 init_fonts((win_width-cfg.window_border*2)/cols,
1318 (win_height-cfg.window_border*2)/rows);
1319 offset_width = (win_width-font_width*cols)/2;
1320 offset_height = (win_height-font_height*rows)/2;
1322 extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2;
1323 extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2;
1325 InvalidateRect(hwnd, NULL, TRUE);
1326 #ifdef RDB_DEBUG_PATCH
1327 debug((25, "reset_window() -> font resize to (%d,%d)",
1328 font_width, font_height));
1333 static void click(Mouse_Button b, int x, int y, int shift, int ctrl)
1335 int thistime = GetMessageTime();
1337 if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1338 lastbtn = MBT_NOTHING;
1339 term_mouse(b, MA_CLICK, x, y, shift, ctrl);
1343 if (lastbtn == b && thistime - lasttime < dbltime) {
1344 lastact = (lastact == MA_CLICK ? MA_2CLK :
1345 lastact == MA_2CLK ? MA_3CLK :
1346 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1351 if (lastact != MA_NOTHING)
1352 term_mouse(b, lastact, x, y, shift, ctrl);
1353 lasttime = thistime;
1357 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1358 * into a cooked one (SELECT, EXTEND, PASTE).
1360 Mouse_Button translate_button(Mouse_Button button)
1362 if (button == MBT_LEFT)
1364 if (button == MBT_MIDDLE)
1365 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1366 if (button == MBT_RIGHT)
1367 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1368 return 0; /* shouldn't happen */
1371 static void show_mouseptr(int show)
1373 static int cursor_visible = 1;
1374 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1376 if (cursor_visible && !show)
1378 else if (!cursor_visible && show)
1380 cursor_visible = show;
1383 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1384 WPARAM wParam, LPARAM lParam)
1387 static int ignore_clip = FALSE;
1388 static int resizing = FALSE;
1389 static int need_backend_resize = FALSE;
1393 if (pending_netevent)
1394 enact_pending_netevent();
1401 if (cfg.ping_interval > 0) {
1404 if (now - last_movement > cfg.ping_interval) {
1405 back->special(TS_PING);
1406 last_movement = now;
1414 if (!cfg.warn_on_close || session_closed ||
1416 "Are you sure you want to close this session?",
1417 "PuTTY Exit Confirmation",
1418 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1419 DestroyWindow(hwnd);
1426 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1438 PROCESS_INFORMATION pi;
1439 HANDLE filemap = NULL;
1441 if (wParam == IDM_DUPSESS) {
1443 * Allocate a file-mapping memory chunk for the
1446 SECURITY_ATTRIBUTES sa;
1449 sa.nLength = sizeof(sa);
1450 sa.lpSecurityDescriptor = NULL;
1451 sa.bInheritHandle = TRUE;
1452 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1455 0, sizeof(Config), NULL);
1457 p = (Config *) MapViewOfFile(filemap,
1459 0, 0, sizeof(Config));
1461 *p = cfg; /* structure copy */
1465 sprintf(c, "putty &%p", filemap);
1467 } else if (wParam == IDM_SAVEDSESS) {
1469 sessions[(lParam - IDM_SAVED_MIN) / 16];
1470 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1472 cl = NULL; /* not a very important failure mode */
1474 sprintf(cl, "putty @%s", session);
1480 GetModuleFileName(NULL, b, sizeof(b) - 1);
1482 si.lpReserved = NULL;
1483 si.lpDesktop = NULL;
1487 si.lpReserved2 = NULL;
1488 CreateProcess(b, cl, NULL, NULL, TRUE,
1489 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1492 CloseHandle(filemap);
1502 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1505 if (!do_reconfig(hwnd))
1508 if (strcmp(prev_cfg.logfilename, cfg.logfilename) ||
1509 prev_cfg.logtype != cfg.logtype) {
1510 logfclose(); /* reset logging */
1516 * Flush the line discipline's edit buffer in the
1517 * case where local editing has just been disabled.
1519 ldisc_send(NULL, 0);
1527 /* Screen size changed ? */
1528 if (cfg.height != prev_cfg.height ||
1529 cfg.width != prev_cfg.width ||
1530 cfg.savelines != prev_cfg.savelines ||
1532 term_size(cfg.height, cfg.width, cfg.savelines);
1534 /* Enable or disable the scroll bar, etc */
1536 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1537 LONG nexflag, exflag =
1538 GetWindowLong(hwnd, GWL_EXSTYLE);
1541 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
1542 if (cfg.alwaysontop) {
1543 nexflag |= WS_EX_TOPMOST;
1544 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1545 SWP_NOMOVE | SWP_NOSIZE);
1547 nexflag &= ~(WS_EX_TOPMOST);
1548 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1549 SWP_NOMOVE | SWP_NOSIZE);
1552 if (cfg.sunken_edge)
1553 nexflag |= WS_EX_CLIENTEDGE;
1555 nexflag &= ~(WS_EX_CLIENTEDGE);
1561 nflg &= ~WS_VSCROLL;
1562 if (cfg.locksize && cfg.lockfont)
1563 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1565 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1567 if (nflg != flag || nexflag != exflag) {
1569 SetWindowLong(hwnd, GWL_STYLE, nflg);
1570 if (nexflag != exflag)
1571 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1573 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1574 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1575 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1583 if (cfg.locksize && cfg.lockfont && IsZoomed(hwnd)) {
1588 set_title(cfg.wintitle);
1589 if (IsIconic(hwnd)) {
1591 cfg.win_name_always ? window_name :
1595 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1596 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1597 cfg.fontisbold != prev_cfg.fontisbold ||
1598 cfg.fontheight != prev_cfg.fontheight ||
1599 cfg.fontcharset != prev_cfg.fontcharset ||
1600 cfg.vtmode != prev_cfg.vtmode ||
1601 cfg.bold_colour != prev_cfg.bold_colour ||
1602 (cfg.lockfont && !prev_cfg.lockfont))
1605 InvalidateRect(hwnd, NULL, TRUE);
1606 reset_window(init_lvl);
1619 back->special(TS_AYT);
1622 back->special(TS_BRK);
1625 back->special(TS_SYNCH);
1628 back->special(TS_EC);
1631 back->special(TS_EL);
1634 back->special(TS_GA);
1637 back->special(TS_NOP);
1640 back->special(TS_ABORT);
1643 back->special(TS_AO);
1646 back->special(TS_IP);
1649 back->special(TS_SUSP);
1652 back->special(TS_EOR);
1655 back->special(TS_EOF);
1662 * We get this if the System menu has been activated.
1663 * This might happen from within TranslateKey, in which
1664 * case it really wants to be followed by a `space'
1665 * character to actually _bring the menu up_ rather
1666 * than just sitting there in `ready to appear' state.
1669 PostMessage(hwnd, WM_CHAR, ' ', 0);
1672 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1673 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1678 #define X_POS(l) ((int)(short)LOWORD(l))
1679 #define Y_POS(l) ((int)(short)HIWORD(l))
1681 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1682 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1683 #define WHEEL_DELTA 120
1686 wheel_accumulator += (short) HIWORD(wParam);
1687 wParam = LOWORD(wParam);
1689 /* process events when the threshold is reached */
1690 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1693 /* reduce amount for next time */
1694 if (wheel_accumulator > 0) {
1696 wheel_accumulator -= WHEEL_DELTA;
1697 } else if (wheel_accumulator < 0) {
1699 wheel_accumulator += WHEEL_DELTA;
1703 if (send_raw_mouse) {
1704 /* send a mouse-down followed by a mouse up */
1707 TO_CHR_X(X_POS(lParam)),
1708 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1709 wParam & MK_CONTROL);
1710 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1711 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1712 wParam & MK_CONTROL);
1714 /* trigger a scroll */
1716 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1721 case WM_LBUTTONDOWN:
1722 case WM_MBUTTONDOWN:
1723 case WM_RBUTTONDOWN:
1730 case WM_LBUTTONDOWN:
1734 case WM_MBUTTONDOWN:
1735 button = MBT_MIDDLE;
1738 case WM_RBUTTONDOWN:
1747 button = MBT_MIDDLE;
1755 button = press = 0; /* shouldn't happen */
1760 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1761 wParam & MK_SHIFT, wParam & MK_CONTROL);
1764 term_mouse(button, MA_RELEASE,
1765 TO_CHR_X(X_POS(lParam)),
1766 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1767 wParam & MK_CONTROL);
1775 * Add the mouse position and message time to the random
1778 noise_ultralight(lParam);
1780 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1782 if (wParam & MK_LBUTTON)
1784 else if (wParam & MK_MBUTTON)
1788 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1789 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1790 wParam & MK_CONTROL);
1793 case WM_NCMOUSEMOVE:
1795 noise_ultralight(lParam);
1797 case WM_IGNORE_CLIP:
1798 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1800 case WM_DESTROYCLIPBOARD:
1803 ignore_clip = FALSE;
1809 hdc = BeginPaint(hwnd, &p);
1811 SelectPalette(hdc, pal, TRUE);
1812 RealizePalette(hdc);
1815 (p.rcPaint.left-offset_width)/font_width,
1816 (p.rcPaint.top-offset_height)/font_height,
1817 (p.rcPaint.right-offset_width-1)/font_width,
1818 (p.rcPaint.bottom-offset_height-1)/font_height);
1821 p.rcPaint.left < offset_width ||
1822 p.rcPaint.top < offset_height ||
1823 p.rcPaint.right >= offset_width + font_width*cols ||
1824 p.rcPaint.bottom>= offset_height + font_height*rows)
1826 HBRUSH fillcolour, oldbrush;
1828 fillcolour = CreateSolidBrush (
1829 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1830 oldbrush = SelectObject(hdc, fillcolour);
1831 edge = CreatePen(PS_SOLID, 0,
1832 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1833 oldpen = SelectObject(hdc, edge);
1835 ExcludeClipRect(hdc,
1836 offset_width, offset_height,
1837 offset_width+font_width*cols,
1838 offset_height+font_height*rows);
1840 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
1841 p.rcPaint.right, p.rcPaint.bottom);
1843 // SelectClipRgn(hdc, NULL);
1845 SelectObject(hdc, oldbrush);
1846 DeleteObject(fillcolour);
1847 SelectObject(hdc, oldpen);
1850 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1851 SelectObject(hdc, GetStockObject(WHITE_PEN));
1857 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1858 * but the only one that's likely to try to overload us is FD_READ.
1859 * This means buffering just one is fine.
1861 if (pending_netevent)
1862 enact_pending_netevent();
1864 pending_netevent = TRUE;
1865 pend_netevent_wParam = wParam;
1866 pend_netevent_lParam = lParam;
1867 if (WSAGETSELECTEVENT(lParam) != FD_READ)
1868 enact_pending_netevent();
1870 time(&last_movement);
1874 CreateCaret(hwnd, caretbm, font_width, font_height);
1876 flash_window(0); /* stop */
1888 case WM_ENTERSIZEMOVE:
1889 #ifdef RDB_DEBUG_PATCH
1890 debug((27, "WM_ENTERSIZEMOVE"));
1894 need_backend_resize = FALSE;
1896 case WM_EXITSIZEMOVE:
1899 #ifdef RDB_DEBUG_PATCH
1900 debug((27, "WM_EXITSIZEMOVE"));
1902 if (need_backend_resize) {
1903 term_size(cfg.height, cfg.width, cfg.savelines);
1904 InvalidateRect(hwnd, NULL, TRUE);
1909 * This does two jobs:
1910 * 1) Keep the sizetip uptodate
1911 * 2) Make sure the window size is _stepped_ in units of the font size.
1913 if (!cfg.locksize && !alt_pressed) {
1914 int width, height, w, h, ew, eh;
1915 LPRECT r = (LPRECT) lParam;
1917 if ( !need_backend_resize &&
1918 (cfg.height != rows || cfg.width != cols )) {
1920 * Great! It seems the host has been changing the terminal
1921 * size, well the user is now grabbing so this is probably
1922 * the least confusing solution in the long run even though
1923 * it a is suprise. Unfortunatly the only way to prevent
1924 * this seems to be to let the host change the window size
1925 * and as that's a user option we're still right back here.
1927 term_size(cfg.height, cfg.width, cfg.savelines);
1929 InvalidateRect(hwnd, NULL, TRUE);
1930 need_backend_resize = TRUE;
1933 width = r->right - r->left - extra_width;
1934 height = r->bottom - r->top - extra_height;
1935 w = (width + font_width / 2) / font_width;
1938 h = (height + font_height / 2) / font_height;
1941 UpdateSizeTip(hwnd, w, h);
1942 ew = width - w * font_width;
1943 eh = height - h * font_height;
1945 if (wParam == WMSZ_LEFT ||
1946 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1952 if (wParam == WMSZ_TOP ||
1953 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1963 int width, height, w, h, rv = 0;
1964 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
1965 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
1966 LPRECT r = (LPRECT) lParam;
1968 width = r->right - r->left - ex_width;
1969 height = r->bottom - r->top - ex_height;
1971 w = (width + cols/2)/cols;
1972 h = (height + rows/2)/rows;
1973 if ( r->right != r->left + w*cols + ex_width)
1976 if (wParam == WMSZ_LEFT ||
1977 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1978 r->left = r->right - w*cols - ex_width;
1980 r->right = r->left + w*cols + ex_width;
1982 if (r->bottom != r->top + h*rows + ex_height)
1985 if (wParam == WMSZ_TOP ||
1986 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1987 r->top = r->bottom - h*rows - ex_height;
1989 r->bottom = r->top + h*rows + ex_height;
1994 /* break; (never reached) */
1996 #ifdef RDB_DEBUG_PATCH
1997 debug((27, "WM_SIZE %s (%d,%d)",
1998 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
1999 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2000 (wParam == SIZE_RESTORED && resizing) ? "to":
2001 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2003 LOWORD(lParam), HIWORD(lParam)));
2005 if (wParam == SIZE_MINIMIZED) {
2007 cfg.win_name_always ? window_name : icon_name);
2010 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2011 SetWindowText(hwnd, window_name);
2013 if (cfg.lockfont && cfg.locksize) {
2014 /* A resize, well it better be a minimize. */
2018 int width, height, w, h;
2020 width = LOWORD(lParam);
2021 height = HIWORD(lParam);
2024 if (wParam == SIZE_MAXIMIZED) {
2029 w = width / font_width;
2031 h = height / font_height;
2034 term_size(h, w, cfg.savelines);
2037 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2040 term_size(prev_rows, prev_cols, cfg.savelines);
2043 /* This is an unexpected resize, these will normally happen
2044 * if the window is too large. Probably either the user
2045 * selected a huge font or the screen size has changed.
2047 * This is also called with minimize.
2049 else reset_window(-1);
2053 * Don't call back->size in mid-resize. (To prevent
2054 * massive numbers of resize events getting sent
2055 * down the connection during an NT opaque drag.)
2058 if (!cfg.locksize && !alt_pressed) {
2059 need_backend_resize = TRUE;
2060 w = (width-cfg.window_border*2) / font_width;
2062 h = (height-cfg.window_border*2) / font_height;
2073 switch (LOWORD(wParam)) {
2087 term_scroll(0, +rows / 2);
2090 term_scroll(0, -rows / 2);
2092 case SB_THUMBPOSITION:
2094 term_scroll(1, HIWORD(wParam));
2098 case WM_PALETTECHANGED:
2099 if ((HWND) wParam != hwnd && pal != NULL) {
2100 HDC hdc = get_ctx();
2102 if (RealizePalette(hdc) > 0)
2108 case WM_QUERYNEWPALETTE:
2110 HDC hdc = get_ctx();
2112 if (RealizePalette(hdc) > 0)
2124 * Add the scan code and keypress timing to the random
2127 noise_ultralight(lParam);
2130 * We don't do TranslateMessage since it disassociates the
2131 * resulting CHAR message from the KEYDOWN that sparked it,
2132 * which we occasionally don't want. Instead, we process
2133 * KEYDOWN, and call the Win32 translator functions so that
2134 * we get the translations under _our_ control.
2137 unsigned char buf[20];
2140 if (wParam == VK_PROCESSKEY) {
2143 m.message = WM_KEYDOWN;
2145 m.lParam = lParam & 0xdfff;
2146 TranslateMessage(&m);
2148 len = TranslateKey(message, wParam, lParam, buf);
2150 return DefWindowProc(hwnd, message, wParam, lParam);
2154 * We need not bother about stdin backlogs
2155 * here, because in GUI PuTTY we can't do
2156 * anything about it anyway; there's no means
2157 * of asking Windows to hold off on KEYDOWN
2158 * messages. We _have_ to buffer everything
2161 ldisc_send(buf, len);
2167 case WM_INPUTLANGCHANGE:
2169 /* wParam == Font number */
2170 /* lParam == Locale */
2172 HKL NewInputLocale = (HKL) lParam;
2174 // lParam == GetKeyboardLayout(0);
2176 GetLocaleInfo(LOWORD(NewInputLocale),
2177 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
2179 kbd_codepage = atoi(lbuf);
2182 case WM_IME_COMPOSITION:
2188 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2189 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2191 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2192 break; /* fall back to DefWindowProc */
2194 hIMC = ImmGetContext(hwnd);
2195 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2198 buff = (char*) smalloc(n);
2199 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2200 luni_send((unsigned short *)buff, n / 2);
2203 ImmReleaseContext(hwnd, hIMC);
2208 if (wParam & 0xFF00) {
2209 unsigned char buf[2];
2212 buf[0] = wParam >> 8;
2213 lpage_send(kbd_codepage, buf, 2);
2215 char c = (unsigned char) wParam;
2216 lpage_send(kbd_codepage, &c, 1);
2222 * Nevertheless, we are prepared to deal with WM_CHAR
2223 * messages, should they crop up. So if someone wants to
2224 * post the things to us as part of a macro manoeuvre,
2225 * we're ready to cope.
2228 char c = (unsigned char)wParam;
2229 lpage_send(CP_ACP, &c, 1);
2233 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2234 SetCursor(LoadCursor(NULL, IDC_ARROW));
2239 return DefWindowProc(hwnd, message, wParam, lParam);
2243 * Move the system caret. (We maintain one, even though it's
2244 * invisible, for the benefit of blind people: apparently some
2245 * helper software tracks the system caret, so we should arrange to
2248 void sys_cursor(int x, int y)
2253 if (!has_focus) return;
2255 SetCaretPos(x * font_width + offset_width,
2256 y * font_height + offset_height);
2258 /* IMM calls on Win98 and beyond only */
2259 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2261 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2262 osVersion.dwMinorVersion == 0) return; /* 95 */
2264 /* we should have the IMM functions */
2265 hIMC = ImmGetContext(hwnd);
2266 cf.dwStyle = CFS_POINT;
2267 cf.ptCurrentPos.x = x * font_width;
2268 cf.ptCurrentPos.y = y * font_height;
2269 ImmSetCompositionWindow(hIMC, &cf);
2271 ImmReleaseContext(hwnd, hIMC);
2275 * Draw a line of text in the window, at given character
2276 * coordinates, in given attributes.
2278 * We are allowed to fiddle with the contents of `text'.
2280 void do_text(Context ctx, int x, int y, char *text, int len,
2281 unsigned long attr, int lattr)
2284 int nfg, nbg, nfont;
2287 int force_manual_underline = 0;
2288 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2289 int char_width = fnt_width;
2290 int text_adjust = 0;
2291 static int *IpDx = 0, IpDxLEN = 0;
2293 if (attr & ATTR_WIDE)
2296 if (len > IpDxLEN || IpDx[0] != char_width) {
2298 if (len > IpDxLEN) {
2300 IpDx = smalloc((len + 16) * sizeof(int));
2301 IpDxLEN = (len + 16);
2303 for (i = 0; i < IpDxLEN; i++)
2304 IpDx[i] = char_width;
2307 /* Only want the left half of double width lines */
2308 if (lattr != LATTR_NORM && x*2 >= cols)
2316 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2317 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2318 attr ^= ATTR_CUR_XOR;
2322 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2323 /* Assume a poorman font is borken in other ways too. */
2333 nfont |= FONT_WIDE + FONT_HIGH;
2336 if (attr & ATTR_NARROW)
2337 nfont |= FONT_NARROW;
2339 /* Special hack for the VT100 linedraw glyphs. */
2340 if ((attr & CSET_MASK) == 0x2300) {
2341 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2342 switch ((unsigned char) (text[0])) {
2344 text_adjust = -2 * font_height / 5;
2347 text_adjust = -1 * font_height / 5;
2350 text_adjust = font_height / 5;
2353 text_adjust = 2 * font_height / 5;
2356 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2359 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2360 attr |= (unitab_xterm['q'] & CSET_MASK);
2361 if (attr & ATTR_UNDER) {
2362 attr &= ~ATTR_UNDER;
2363 force_manual_underline = 1;
2368 /* Anything left as an original character set is unprintable. */
2369 if (DIRECT_CHAR(attr)) {
2372 memset(text, 0xFD, len);
2376 if ((attr & CSET_MASK) == ATTR_OEMCP)
2379 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2380 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2381 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2383 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2384 nfont |= FONT_UNDERLINE;
2385 another_font(nfont);
2386 if (!fonts[nfont]) {
2387 if (nfont & FONT_UNDERLINE)
2388 force_manual_underline = 1;
2389 /* Don't do the same for manual bold, it could be bad news. */
2391 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2393 another_font(nfont);
2395 nfont = FONT_NORMAL;
2396 if (attr & ATTR_REVERSE) {
2401 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2403 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2407 SelectObject(hdc, fonts[nfont]);
2408 SetTextColor(hdc, fg);
2409 SetBkColor(hdc, bg);
2410 SetBkMode(hdc, OPAQUE);
2413 line_box.right = x + char_width * len;
2414 line_box.bottom = y + font_height;
2416 /* Only want the left half of double width lines */
2417 if (line_box.right > font_width*cols+offset_width)
2418 line_box.right = font_width*cols+offset_width;
2420 /* We're using a private area for direct to font. (512 chars.) */
2421 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2422 /* Ho Hum, dbcs fonts are a PITA! */
2423 /* To display on W9x I have to convert to UCS */
2424 static wchar_t *uni_buf = 0;
2425 static int uni_len = 0;
2427 if (len > uni_len) {
2429 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2432 for(nlen = mptr = 0; mptr<len; mptr++) {
2433 uni_buf[nlen] = 0xFFFD;
2434 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2435 IpDx[nlen] += char_width;
2436 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2437 text+mptr, 2, uni_buf+nlen, 1);
2442 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2443 text+mptr, 1, uni_buf+nlen, 1);
2451 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2452 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2453 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2454 SetBkMode(hdc, TRANSPARENT);
2455 ExtTextOutW(hdc, x - 1,
2456 y - font_height * (lattr ==
2457 LATTR_BOT) + text_adjust,
2458 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2462 } else if (DIRECT_FONT(attr)) {
2464 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2465 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2466 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2467 SetBkMode(hdc, TRANSPARENT);
2469 /* GRR: This draws the character outside it's box and can leave
2470 * 'droppings' even with the clip box! I suppose I could loop it
2471 * one character at a time ... yuk.
2473 * Or ... I could do a test print with "W", and use +1 or -1 for this
2474 * shift depending on if the leftmost column is blank...
2476 ExtTextOut(hdc, x - 1,
2477 y - font_height * (lattr ==
2478 LATTR_BOT) + text_adjust,
2479 ETO_CLIPPED, &line_box, text, len, IpDx);
2482 /* And 'normal' unicode characters */
2483 static WCHAR *wbuf = NULL;
2484 static int wlen = 0;
2489 wbuf = smalloc(wlen * sizeof(WCHAR));
2491 for (i = 0; i < len; i++)
2492 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2495 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2496 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2498 /* And the shadow bold hack. */
2499 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2500 SetBkMode(hdc, TRANSPARENT);
2501 ExtTextOutW(hdc, x - 1,
2502 y - font_height * (lattr ==
2503 LATTR_BOT) + text_adjust,
2504 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2507 if (lattr != LATTR_TOP && (force_manual_underline ||
2508 (und_mode == UND_LINE
2509 && (attr & ATTR_UNDER)))) {
2512 if (lattr == LATTR_BOT)
2513 dec = dec * 2 - font_height;
2515 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2516 MoveToEx(hdc, x, y + dec, NULL);
2517 LineTo(hdc, x + len * char_width, y + dec);
2518 oldpen = SelectObject(hdc, oldpen);
2519 DeleteObject(oldpen);
2523 void do_cursor(Context ctx, int x, int y, char *text, int len,
2524 unsigned long attr, int lattr)
2530 int ctype = cfg.cursor_type;
2532 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2533 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2534 do_text(ctx, x, y, text, len, attr, lattr);
2538 attr |= TATTR_RIGHTCURS;
2541 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2542 if (attr & ATTR_WIDE)
2549 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2552 pts[0].x = pts[1].x = pts[4].x = x;
2553 pts[2].x = pts[3].x = x + char_width - 1;
2554 pts[0].y = pts[3].y = pts[4].y = y;
2555 pts[1].y = pts[2].y = y + font_height - 1;
2556 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2557 Polyline(hdc, pts, 5);
2558 oldpen = SelectObject(hdc, oldpen);
2559 DeleteObject(oldpen);
2560 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2561 int startx, starty, dx, dy, length, i;
2564 starty = y + descent;
2567 length = char_width;
2570 if (attr & TATTR_RIGHTCURS)
2571 xadjust = char_width - 1;
2572 startx = x + xadjust;
2576 length = font_height;
2578 if (attr & TATTR_ACTCURS) {
2581 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2582 MoveToEx(hdc, startx, starty, NULL);
2583 LineTo(hdc, startx + dx * length, starty + dy * length);
2584 oldpen = SelectObject(hdc, oldpen);
2585 DeleteObject(oldpen);
2587 for (i = 0; i < length; i++) {
2589 SetPixel(hdc, startx, starty, colours[23]);
2598 /* This function gets the actual width of a character in the normal font.
2600 int CharWidth(Context ctx, int uc) {
2604 /* If the font max is the same as the font ave width then this
2605 * function is a no-op.
2607 if (!font_dualwidth) return 1;
2609 switch (uc & CSET_MASK) {
2611 uc = unitab_line[uc & 0xFF];
2614 uc = unitab_xterm[uc & 0xFF];
2617 uc = unitab_scoacs[uc & 0xFF];
2620 if (DIRECT_FONT(uc)) {
2621 if (dbcs_screenfont) return 1;
2623 /* Speedup, I know of no font where ascii is the wrong width */
2624 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2627 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2628 SelectObject(hdc, fonts[FONT_NORMAL]);
2629 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2630 another_font(FONT_OEM);
2631 if (!fonts[FONT_OEM]) return 0;
2633 SelectObject(hdc, fonts[FONT_OEM]);
2637 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2638 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2641 /* Speedup, I know of no font where ascii is the wrong width */
2642 if (uc >= ' ' && uc <= '~') return 1;
2644 SelectObject(hdc, fonts[FONT_NORMAL]);
2645 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2646 /* Okay that one worked */ ;
2647 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2648 /* This should work on 9x too, but it's "less accurate" */ ;
2653 ibuf += font_width / 2 -1;
2660 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2661 * codes. Returns number of bytes used or zero to drop the message
2662 * or -1 to forward the message to windows.
2664 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2665 unsigned char *output)
2668 int scan, left_alt = 0, key_down, shift_state;
2670 unsigned char *p = output;
2671 static int alt_sum = 0;
2673 HKL kbd_layout = GetKeyboardLayout(0);
2675 static WORD keys[3];
2676 static int compose_char = 0;
2677 static WPARAM compose_key = 0;
2679 r = GetKeyboardState(keystate);
2681 memset(keystate, 0, sizeof(keystate));
2684 #define SHOW_TOASCII_RESULT
2685 { /* Tell us all about key events */
2686 static BYTE oldstate[256];
2687 static int first = 1;
2691 memcpy(oldstate, keystate, sizeof(oldstate));
2694 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2696 } else if ((HIWORD(lParam) & KF_UP)
2697 && scan == (HIWORD(lParam) & 0xFF)) {
2701 if (wParam >= VK_F1 && wParam <= VK_F20)
2702 debug(("K_F%d", wParam + 1 - VK_F1));
2715 debug(("VK_%02x", wParam));
2717 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2719 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2721 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2722 if (ch >= ' ' && ch <= '~')
2723 debug((", '%c'", ch));
2725 debug((", $%02x", ch));
2728 debug((", KB0=%02x", keys[0]));
2730 debug((", KB1=%02x", keys[1]));
2732 debug((", KB2=%02x", keys[2]));
2734 if ((keystate[VK_SHIFT] & 0x80) != 0)
2736 if ((keystate[VK_CONTROL] & 0x80) != 0)
2738 if ((HIWORD(lParam) & KF_EXTENDED))
2740 if ((HIWORD(lParam) & KF_UP))
2744 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2745 else if ((HIWORD(lParam) & KF_UP))
2746 oldstate[wParam & 0xFF] ^= 0x80;
2748 oldstate[wParam & 0xFF] ^= 0x81;
2750 for (ch = 0; ch < 256; ch++)
2751 if (oldstate[ch] != keystate[ch])
2752 debug((", M%02x=%02x", ch, keystate[ch]));
2754 memcpy(oldstate, keystate, sizeof(oldstate));
2758 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2759 keystate[VK_RMENU] = keystate[VK_MENU];
2763 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2764 if ((cfg.funky_type == 3 ||
2765 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2766 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2768 wParam = VK_EXECUTE;
2770 /* UnToggle NUMLock */
2771 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2772 keystate[VK_NUMLOCK] ^= 1;
2775 /* And write back the 'adjusted' state */
2776 SetKeyboardState(keystate);
2779 /* Disable Auto repeat if required */
2780 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2783 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2786 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2788 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2789 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2790 if (cfg.ctrlaltkeys)
2791 keystate[VK_MENU] = 0;
2793 keystate[VK_RMENU] = 0x80;
2798 alt_pressed = (left_alt && key_down);
2800 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2801 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2802 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2804 /* Note if AltGr was pressed and if it was used as a compose key */
2805 if (!compose_state) {
2806 compose_key = 0x100;
2807 if (cfg.compose_key) {
2808 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2809 compose_key = wParam;
2811 if (wParam == VK_APPS)
2812 compose_key = wParam;
2815 if (wParam == compose_key) {
2816 if (compose_state == 0
2817 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2819 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2823 } else if (compose_state == 1 && wParam != VK_CONTROL)
2827 * Record that we pressed key so the scroll window can be reset, but
2828 * be careful to avoid Shift-UP/Down
2830 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2834 /* Make sure we're not pasting */
2838 if (compose_state > 1 && left_alt)
2841 /* Sanitize the number pad if not using a PC NumPad */
2842 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2843 && cfg.funky_type != 2)
2844 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2845 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2849 nParam = VK_NUMPAD0;
2852 nParam = VK_NUMPAD1;
2855 nParam = VK_NUMPAD2;
2858 nParam = VK_NUMPAD3;
2861 nParam = VK_NUMPAD4;
2864 nParam = VK_NUMPAD5;
2867 nParam = VK_NUMPAD6;
2870 nParam = VK_NUMPAD7;
2873 nParam = VK_NUMPAD8;
2876 nParam = VK_NUMPAD9;
2879 nParam = VK_DECIMAL;
2883 if (keystate[VK_NUMLOCK] & 1)
2890 /* If a key is pressed and AltGr is not active */
2891 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2892 /* Okay, prepare for most alts then ... */
2896 /* Lets see if it's a pattern we know all about ... */
2897 if (wParam == VK_PRIOR && shift_state == 1) {
2898 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2901 if (wParam == VK_NEXT && shift_state == 1) {
2902 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2905 if (wParam == VK_INSERT && shift_state == 1) {
2909 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2912 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2913 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2916 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter) {
2920 /* Control-Numlock for app-keypad mode switch */
2921 if (wParam == VK_PAUSE && shift_state == 2) {
2922 app_keypad_keys ^= 1;
2926 /* Nethack keypad */
2927 if (cfg.nethack_keypad && !left_alt) {
2930 *p++ = shift_state ? 'B' : 'b';
2933 *p++ = shift_state ? 'J' : 'j';
2936 *p++ = shift_state ? 'N' : 'n';
2939 *p++ = shift_state ? 'H' : 'h';
2942 *p++ = shift_state ? '.' : '.';
2945 *p++ = shift_state ? 'L' : 'l';
2948 *p++ = shift_state ? 'Y' : 'y';
2951 *p++ = shift_state ? 'K' : 'k';
2954 *p++ = shift_state ? 'U' : 'u';
2959 /* Application Keypad */
2963 if (cfg.funky_type == 3 ||
2964 (cfg.funky_type <= 1 &&
2965 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
2979 if (app_keypad_keys && !cfg.no_applic_k)
3016 if (cfg.funky_type == 2) {
3021 } else if (shift_state)
3028 if (cfg.funky_type == 2)
3032 if (cfg.funky_type == 2)
3036 if (cfg.funky_type == 2)
3041 if (HIWORD(lParam) & KF_EXTENDED)
3047 if (xkey >= 'P' && xkey <= 'S')
3048 p += sprintf((char *) p, "\x1B%c", xkey);
3050 p += sprintf((char *) p, "\x1B?%c", xkey);
3052 p += sprintf((char *) p, "\x1BO%c", xkey);
3057 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
3058 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3062 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3068 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3072 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3076 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3081 if (wParam == VK_PAUSE) { /* Break/Pause */
3086 /* Control-2 to Control-8 are special */
3087 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3088 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3091 if (shift_state == 2 && wParam == 0xBD) {
3095 if (shift_state == 2 && wParam == 0xDF) {
3099 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3106 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3107 * for integer decimal nn.)
3109 * We also deal with the weird ones here. Linux VCs replace F1
3110 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3111 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3117 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3120 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3123 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3126 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3129 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3132 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3135 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3138 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3141 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3144 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3177 if ((shift_state&2) == 0) switch (wParam) {
3197 /* Reorder edit keys to physical order */
3198 if (cfg.funky_type == 3 && code <= 6)
3199 code = "\0\2\1\4\5\3\6"[code];
3201 if (vt52_mode && code > 0 && code <= 6) {
3202 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3206 if (cfg.funky_type == 5 && /* SCO function keys */
3207 code >= 11 && code <= 34) {
3208 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3211 case VK_F1: index = 0; break;
3212 case VK_F2: index = 1; break;
3213 case VK_F3: index = 2; break;
3214 case VK_F4: index = 3; break;
3215 case VK_F5: index = 4; break;
3216 case VK_F6: index = 5; break;
3217 case VK_F7: index = 6; break;
3218 case VK_F8: index = 7; break;
3219 case VK_F9: index = 8; break;
3220 case VK_F10: index = 9; break;
3221 case VK_F11: index = 10; break;
3222 case VK_F12: index = 11; break;
3224 if (keystate[VK_SHIFT] & 0x80) index += 12;
3225 if (keystate[VK_CONTROL] & 0x80) index += 24;
3226 p += sprintf((char *) p, "\x1B[%c", codes[index]);
3229 if (cfg.funky_type == 5 && /* SCO small keypad */
3230 code >= 1 && code <= 6) {
3231 char codes[] = "HL.FIG";
3235 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3239 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3246 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3249 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3252 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3253 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3256 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3258 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3260 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3263 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3264 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3268 p += sprintf((char *) p, "\x1B[%d~", code);
3273 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3274 * some reason seems to send VK_CLEAR to Windows...).
3297 p += sprintf((char *) p, "\x1B%c", xkey);
3299 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3300 /* VT100 & VT102 manuals both state the app cursor keys
3301 * only work if the app keypad is on.
3303 if (!app_keypad_keys)
3305 /* Useful mapping of Ctrl-arrows */
3306 if (shift_state == 2)
3310 p += sprintf((char *) p, "\x1BO%c", xkey);
3312 p += sprintf((char *) p, "\x1B[%c", xkey);
3319 * Finally, deal with Return ourselves. (Win95 seems to
3320 * foul it up when Alt is pressed, for some reason.)
3322 if (wParam == VK_RETURN) { /* Return */
3328 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3329 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3334 /* Okay we've done everything interesting; let windows deal with
3335 * the boring stuff */
3337 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3338 #ifdef SHOW_TOASCII_RESULT
3339 if (r == 1 && !key_down) {
3341 if (in_utf || dbcs_screenfont)
3342 debug((", (U+%04x)", alt_sum));
3344 debug((", LCH(%d)", alt_sum));
3346 debug((", ACH(%d)", keys[0]));
3351 for (r1 = 0; r1 < r; r1++) {
3352 debug(("%s%d", r1 ? "," : "", keys[r1]));
3360 for (i = 0; i < r; i++) {
3361 unsigned char ch = (unsigned char) keys[i];
3363 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3368 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3372 if ((nc = check_compose(compose_char, ch)) == -1) {
3373 MessageBeep(MB_ICONHAND);
3377 luni_send(&keybuf, 1);
3385 if (in_utf || dbcs_screenfont) {
3387 luni_send(&keybuf, 1);
3389 ch = (char) alt_sum;
3391 * We need not bother about stdin
3392 * backlogs here, because in GUI PuTTY
3393 * we can't do anything about it
3394 * anyway; there's no means of asking
3395 * Windows to hold off on KEYDOWN
3396 * messages. We _have_ to buffer
3397 * everything we're sent.
3403 lpage_send(kbd_codepage, &ch, 1);
3405 static char cbuf[] = "\033 ";
3407 lpage_send(kbd_codepage, cbuf + !left_alt,
3413 /* This is so the ALT-Numpad and dead keys work correctly. */
3418 /* If we're definitly not building up an ALT-54321 then clear it */
3421 /* If we will be using alt_sum fix the 256s */
3422 else if (keys[0] && (in_utf || dbcs_screenfont))
3427 * ALT alone may or may not want to bring up the System menu.
3428 * If it's not meant to, we return 0 on presses or releases of
3429 * ALT, to show that we've swallowed the keystroke. Otherwise
3430 * we return -1, which means Windows will give the keystroke
3431 * its default handling (i.e. bring up the System menu).
3433 if (wParam == VK_MENU && !cfg.alt_only)
3439 void set_title(char *title)
3442 window_name = smalloc(1 + strlen(title));
3443 strcpy(window_name, title);
3444 if (cfg.win_name_always || !IsIconic(hwnd))
3445 SetWindowText(hwnd, title);
3448 void set_icon(char *title)
3451 icon_name = smalloc(1 + strlen(title));
3452 strcpy(icon_name, title);
3453 if (!cfg.win_name_always && IsIconic(hwnd))
3454 SetWindowText(hwnd, title);
3457 void set_sbar(int total, int start, int page)
3464 si.cbSize = sizeof(si);
3465 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3467 si.nMax = total - 1;
3471 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3474 Context get_ctx(void)
3480 SelectPalette(hdc, pal, FALSE);
3486 void free_ctx(Context ctx)
3488 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3489 ReleaseDC(hwnd, ctx);
3492 static void real_palette_set(int n, int r, int g, int b)
3495 logpal->palPalEntry[n].peRed = r;
3496 logpal->palPalEntry[n].peGreen = g;
3497 logpal->palPalEntry[n].peBlue = b;
3498 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3499 colours[n] = PALETTERGB(r, g, b);
3500 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3502 colours[n] = RGB(r, g, b);
3505 void palette_set(int n, int r, int g, int b)
3507 static const int first[21] = {
3508 0, 2, 4, 6, 8, 10, 12, 14,
3509 1, 3, 5, 7, 9, 11, 13, 15,
3512 real_palette_set(first[n], r, g, b);
3514 real_palette_set(first[n] + 1, r, g, b);
3516 HDC hdc = get_ctx();
3517 UnrealizeObject(pal);
3518 RealizePalette(hdc);
3523 void palette_reset(void)
3527 for (i = 0; i < NCOLOURS; i++) {
3529 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3530 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3531 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3532 logpal->palPalEntry[i].peFlags = 0;
3533 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3534 defpal[i].rgbtGreen,
3535 defpal[i].rgbtBlue);
3537 colours[i] = RGB(defpal[i].rgbtRed,
3538 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3543 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3545 RealizePalette(hdc);
3550 void write_aclip(char *data, int len, int must_deselect)
3555 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3558 lock = GlobalLock(clipdata);
3561 memcpy(lock, data, len);
3562 ((unsigned char *) lock)[len] = 0;
3563 GlobalUnlock(clipdata);
3566 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3568 if (OpenClipboard(hwnd)) {
3570 SetClipboardData(CF_TEXT, clipdata);
3573 GlobalFree(clipdata);
3576 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3580 * Note: unlike write_aclip() this will not append a nul.
3582 void write_clip(wchar_t * data, int len, int must_deselect)
3589 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3591 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3592 len * sizeof(wchar_t));
3593 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3595 if (!clipdata || !clipdata2) {
3597 GlobalFree(clipdata);
3599 GlobalFree(clipdata2);
3602 if (!(lock = GlobalLock(clipdata)))
3604 if (!(lock2 = GlobalLock(clipdata2)))
3607 memcpy(lock, data, len * sizeof(wchar_t));
3608 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3610 GlobalUnlock(clipdata);
3611 GlobalUnlock(clipdata2);
3614 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3616 if (OpenClipboard(hwnd)) {
3618 SetClipboardData(CF_UNICODETEXT, clipdata);
3619 SetClipboardData(CF_TEXT, clipdata2);
3622 GlobalFree(clipdata);
3623 GlobalFree(clipdata2);
3627 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3630 void get_clip(wchar_t ** p, int *len)
3632 static HGLOBAL clipdata = NULL;
3633 static wchar_t *converted = 0;
3642 GlobalUnlock(clipdata);
3645 } else if (OpenClipboard(NULL)) {
3646 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3648 *p = GlobalLock(clipdata);
3650 for (p2 = *p; *p2; p2++);
3654 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3658 s = GlobalLock(clipdata);
3659 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3660 *p = converted = smalloc(i * sizeof(wchar_t));
3661 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3674 * Move `lines' lines from position `from' to position `to' in the
3677 void optimised_move(int to, int from, int lines)
3682 min = (to < from ? to : from);
3683 max = to + from - min;
3685 r.left = offset_width;
3686 r.right = offset_width + cols * font_width;
3687 r.top = offset_height + min * font_height;
3688 r.bottom = offset_height + (max + lines) * font_height;
3689 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3694 * Print a message box and perform a fatal exit.
3696 void fatalbox(char *fmt, ...)
3702 vsprintf(stuff, fmt, ap);
3704 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3709 * Manage window caption / taskbar flashing, if enabled.
3710 * 0 = stop, 1 = maintain, 2 = start
3712 static void flash_window(int mode)
3714 static long last_flash = 0;
3715 static int flashing = 0;
3716 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3719 FlashWindow(hwnd, FALSE);
3723 } else if (mode == 2) {
3726 last_flash = GetTickCount();
3728 FlashWindow(hwnd, TRUE);
3731 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3734 long now = GetTickCount();
3735 long fdiff = now - last_flash;
3736 if (fdiff < 0 || fdiff > 450) {
3738 FlashWindow(hwnd, TRUE); /* toggle */
3749 if (mode == BELL_DEFAULT) {
3751 * For MessageBeep style bells, we want to be careful of
3752 * timing, because they don't have the nice property of
3753 * PlaySound bells that each one cancels the previous
3754 * active one. So we limit the rate to one per 50ms or so.
3756 static long lastbeep = 0;
3759 beepdiff = GetTickCount() - lastbeep;
3760 if (beepdiff >= 0 && beepdiff < 50)
3764 * The above MessageBeep call takes time, so we record the
3765 * time _after_ it finishes rather than before it starts.
3767 lastbeep = GetTickCount();
3768 } else if (mode == BELL_WAVEFILE) {
3769 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3770 char buf[sizeof(cfg.bell_wavefile) + 80];
3771 sprintf(buf, "Unable to play sound file\n%s\n"
3772 "Using default sound instead", cfg.bell_wavefile);
3773 MessageBox(hwnd, buf, "PuTTY Sound Error",
3774 MB_OK | MB_ICONEXCLAMATION);
3775 cfg.beep = BELL_DEFAULT;
3778 /* Otherwise, either visual bell or disabled; do nothing here */
3780 flash_window(2); /* start */
3785 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3788 static void flip_full_screen(void)
3793 cx = GetSystemMetrics(SM_CXSCREEN);
3794 cy = GetSystemMetrics(SM_CYSCREEN);
3795 GetWindowPlacement(hwnd, &old_wind_placement);
3796 old_wind_style = GetWindowLong(hwnd, GWL_STYLE);
3797 SetWindowLong(hwnd, GWL_STYLE,
3798 old_wind_style & ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME));
3799 SetWindowPos(hwnd, HWND_TOP, 0, 0, cx, cy, SWP_SHOWWINDOW);
3802 SetWindowLong(hwnd, GWL_STYLE, old_wind_style);
3803 SetWindowPlacement(hwnd,&old_wind_placement);