20 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
26 #define IDM_SHOWLOG 0x0010
27 #define IDM_NEWSESS 0x0020
28 #define IDM_DUPSESS 0x0030
29 #define IDM_RECONF 0x0040
30 #define IDM_CLRSB 0x0050
31 #define IDM_RESET 0x0060
32 #define IDM_TEL_AYT 0x0070
33 #define IDM_TEL_BRK 0x0080
34 #define IDM_TEL_SYNCH 0x0090
35 #define IDM_TEL_EC 0x00a0
36 #define IDM_TEL_EL 0x00b0
37 #define IDM_TEL_GA 0x00c0
38 #define IDM_TEL_NOP 0x00d0
39 #define IDM_TEL_ABORT 0x00e0
40 #define IDM_TEL_AO 0x00f0
41 #define IDM_TEL_IP 0x0100
42 #define IDM_TEL_SUSP 0x0110
43 #define IDM_TEL_EOR 0x0120
44 #define IDM_TEL_EOF 0x0130
45 #define IDM_ABOUT 0x0140
46 #define IDM_SAVEDSESS 0x0150
47 #define IDM_COPYALL 0x0160
48 #define IDM_FULLSCREEN 0x0170
50 #define IDM_SESSLGP 0x0250 /* log type printable */
51 #define IDM_SESSLGA 0x0260 /* log type all chars */
52 #define IDM_SESSLGE 0x0270 /* log end */
53 #define IDM_SAVED_MIN 0x1000
54 #define IDM_SAVED_MAX 0x2000
56 #define WM_IGNORE_CLIP (WM_XUSER + 2)
58 /* Needed for Chinese support and apparently not always defined. */
60 #define VK_PROCESSKEY 0xE5
63 /* Needed for mouse wheel support and not defined in earlier SDKs. */
65 #define WM_MOUSEWHEEL 0x020A
68 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
69 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
70 unsigned char *output);
71 static void cfgtopalette(void);
72 static void init_palette(void);
73 static void init_fonts(int, int);
74 static void another_font(int);
75 static void deinit_fonts(void);
77 /* Window layout information */
78 static void reset_window(int);
79 static int full_screen = 0;
80 static int extra_width, extra_height;
81 static int font_width, font_height, font_dualwidth;
82 static int offset_width, offset_height;
83 static int was_zoomed = 0;
84 static int prev_rows, prev_cols;
86 static int pending_netevent = 0;
87 static WPARAM pend_netevent_wParam = 0;
88 static LPARAM pend_netevent_lParam = 0;
89 static void enact_pending_netevent(void);
90 static void flash_window(int mode);
91 static void flip_full_screen(void);
93 static time_t last_movement = 0;
97 #define FONT_UNDERLINE 2
98 #define FONT_BOLDUND 3
99 #define FONT_WIDE 0x04
100 #define FONT_HIGH 0x08
101 #define FONT_NARROW 0x10
103 #define FONT_OEM 0x20
104 #define FONT_OEMBOLD 0x21
105 #define FONT_OEMUND 0x22
106 #define FONT_OEMBOLDUND 0x23
108 #define FONT_MAXNO 0x2F
110 static HFONT fonts[FONT_MAXNO];
111 static int fontflag[FONT_MAXNO];
113 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
121 static COLORREF colours[NCOLOURS];
123 static LPLOGPALETTE logpal;
124 static RGBTRIPLE defpal[NCOLOURS];
128 static HBITMAP caretbm;
130 static int dbltime, lasttime, lastact;
131 static Mouse_Button lastbtn;
133 /* this allows xterm-style mouse handling. */
134 static int send_raw_mouse = 0;
135 static int wheel_accumulator = 0;
137 static char *window_name, *icon_name;
139 static int compose_state = 0;
141 static OSVERSIONINFO osVersion;
143 /* Dummy routine, only required in plink. */
144 void ldisc_update(int echo, int edit)
148 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
150 static char appname[] = "PuTTY";
155 int guess_width, guess_height;
158 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
160 winsock_ver = MAKEWORD(1, 1);
161 if (WSAStartup(winsock_ver, &wsadata)) {
162 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
163 MB_OK | MB_ICONEXCLAMATION);
166 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
167 MessageBox(NULL, "WinSock version is incompatible with 1.1",
168 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
172 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
175 InitCommonControls();
177 /* Ensure a Maximize setting in Explorer doesn't maximise the
182 ZeroMemory(&osVersion, sizeof(osVersion));
183 osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
184 if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
185 MessageBox(NULL, "Windows refuses to report a version",
186 "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
192 * Process the command line.
197 default_protocol = DEFAULT_PROTOCOL;
198 default_port = DEFAULT_PORT;
199 cfg.logtype = LGTYP_NONE;
201 do_defaults(NULL, &cfg);
204 while (*p && isspace(*p))
208 * Process command line options first. Yes, this can be
209 * done better, and it will be as soon as I have the
213 char *q = p + strcspn(p, " \t");
216 tolower(p[0]) == 's' &&
217 tolower(p[1]) == 's' && tolower(p[2]) == 'h') {
218 default_protocol = cfg.protocol = PROT_SSH;
219 default_port = cfg.port = 22;
220 } else if (q == p + 7 &&
221 tolower(p[0]) == 'c' &&
222 tolower(p[1]) == 'l' &&
223 tolower(p[2]) == 'e' &&
224 tolower(p[3]) == 'a' &&
225 tolower(p[4]) == 'n' &&
226 tolower(p[5]) == 'u' && tolower(p[6]) == 'p') {
228 * `putty -cleanup'. Remove all registry entries
229 * associated with PuTTY, and also find and delete
230 * the random seed file.
233 "This procedure will remove ALL Registry\n"
234 "entries associated with PuTTY, and will\n"
235 "also remove the PuTTY random seed file.\n"
237 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
238 "SESSIONS. Are you really sure you want\n"
241 MB_YESNO | MB_ICONWARNING) == IDYES) {
246 p = q + strspn(q, " \t");
250 * An initial @ means to activate a saved session.
254 while (i > 1 && isspace(p[i - 1]))
257 do_defaults(p + 1, &cfg);
258 if (!*cfg.host && !do_config()) {
262 } else if (*p == '&') {
264 * An initial & means we've been given a command line
265 * containing the hex value of a HANDLE for a file
266 * mapping object, which we must then extract as a
271 if (sscanf(p + 1, "%p", &filemap) == 1 &&
272 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
273 0, 0, sizeof(Config))) != NULL) {
276 CloseHandle(filemap);
277 } else if (!do_config()) {
284 * If the hostname starts with "telnet:", set the
285 * protocol to Telnet and process the string as a
288 if (!strncmp(q, "telnet:", 7)) {
292 if (q[0] == '/' && q[1] == '/')
294 cfg.protocol = PROT_TELNET;
296 while (*p && *p != ':' && *p != '/')
305 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
306 cfg.host[sizeof(cfg.host) - 1] = '\0';
308 while (*p && !isspace(*p))
312 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
313 cfg.host[sizeof(cfg.host) - 1] = '\0';
314 while (*p && isspace(*p))
329 * Trim leading whitespace off the hostname if it's there.
332 int space = strspn(cfg.host, " \t");
333 memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
336 /* See if host is of the form user@host */
337 if (cfg.host[0] != '\0') {
338 char *atsign = strchr(cfg.host, '@');
339 /* Make sure we're not overflowing the user field */
341 if (atsign - cfg.host < sizeof cfg.username) {
342 strncpy(cfg.username, cfg.host, atsign - cfg.host);
343 cfg.username[atsign - cfg.host] = '\0';
345 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
350 * Trim a colon suffix off the hostname if it's there.
352 cfg.host[strcspn(cfg.host, ":")] = '\0';
356 * Select protocol. This is farmed out into a table in a
357 * separate file to enable an ssh-free variant.
362 for (i = 0; backends[i].backend != NULL; i++)
363 if (backends[i].protocol == cfg.protocol) {
364 back = backends[i].backend;
368 MessageBox(NULL, "Unsupported protocol number found",
369 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
375 /* Check for invalid Port number (i.e. zero) */
377 MessageBox(NULL, "Invalid Port Number",
378 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
385 wndclass.lpfnWndProc = WndProc;
386 wndclass.cbClsExtra = 0;
387 wndclass.cbWndExtra = 0;
388 wndclass.hInstance = inst;
389 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
390 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
391 wndclass.hbrBackground = NULL;
392 wndclass.lpszMenuName = NULL;
393 wndclass.lpszClassName = appname;
395 RegisterClass(&wndclass);
400 savelines = cfg.savelines;
406 * Guess some defaults for the window size. This all gets
407 * updated later, so we don't really care too much. However, we
408 * do want the font width/height guesses to correspond to a
409 * large font rather than a small one...
416 term_size(cfg.height, cfg.width, cfg.savelines);
417 guess_width = extra_width + font_width * cols;
418 guess_height = extra_height + font_height * rows;
421 HWND w = GetDesktopWindow();
422 GetWindowRect(w, &r);
423 if (guess_width > r.right - r.left)
424 guess_width = r.right - r.left;
425 if (guess_height > r.bottom - r.top)
426 guess_height = r.bottom - r.top;
430 int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
433 winmode &= ~(WS_VSCROLL);
434 if (cfg.resize_action == RESIZE_DISABLED)
435 winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
437 exwinmode |= WS_EX_TOPMOST;
439 exwinmode |= WS_EX_CLIENTEDGE;
440 hwnd = CreateWindowEx(exwinmode, appname, appname,
441 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
442 guess_width, guess_height,
443 NULL, NULL, inst, NULL);
447 * Initialise the fonts, simultaneously correcting the guesses
448 * for font_{width,height}.
453 * Correct the guesses for extra_{width,height}.
457 GetWindowRect(hwnd, &wr);
458 GetClientRect(hwnd, &cr);
459 offset_width = offset_height = cfg.window_border;
460 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
461 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
465 * Resize the window, now we know what size we _really_ want it
468 guess_width = extra_width + font_width * cols;
469 guess_height = extra_height + font_height * rows;
470 SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
471 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
474 * Set up a caret bitmap, with no content.
478 int size = (font_width + 15) / 16 * 2 * font_height;
479 bits = smalloc(size);
480 memset(bits, 0, size);
481 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
484 CreateCaret(hwnd, caretbm, font_width, font_height);
487 * Initialise the scroll bar.
492 si.cbSize = sizeof(si);
493 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
498 SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
502 * Start up the telnet connection.
506 char msg[1024], *title;
509 error = back->init(cfg.host, cfg.port, &realhost);
511 sprintf(msg, "Unable to open connection to\n"
512 "%.800s\n" "%s", cfg.host, error);
513 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
516 window_name = icon_name = NULL;
518 title = cfg.wintitle;
520 sprintf(msg, "%s - PuTTY", realhost);
528 session_closed = FALSE;
531 * Prepare the mouse handler.
533 lastact = MA_NOTHING;
534 lastbtn = MBT_NOTHING;
535 dbltime = GetDoubleClickTime();
538 * Set up the session-control options on the system menu.
541 HMENU m = GetSystemMenu(hwnd, FALSE);
545 AppendMenu(m, MF_SEPARATOR, 0, 0);
546 if (cfg.protocol == PROT_TELNET) {
548 AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
549 AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
550 AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
551 AppendMenu(p, MF_SEPARATOR, 0, 0);
552 AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
553 AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
554 AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
555 AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
556 AppendMenu(p, MF_SEPARATOR, 0, 0);
557 AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
558 AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
559 AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
560 AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
561 AppendMenu(p, MF_SEPARATOR, 0, 0);
562 AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
563 AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
564 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
566 AppendMenu(m, MF_SEPARATOR, 0, 0);
568 AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
569 AppendMenu(m, MF_SEPARATOR, 0, 0);
570 AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
571 AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
574 for (i = 1; i < ((nsessions < 256) ? nsessions : 256); i++)
575 AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
577 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
578 AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
579 AppendMenu(m, MF_SEPARATOR, 0, 0);
580 AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
581 AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
582 AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
583 AppendMenu(m, MF_SEPARATOR, 0, 0);
584 AppendMenu(m, MF_ENABLED, IDM_FULLSCREEN, "&Full Screen");
585 AppendMenu(m, MF_SEPARATOR, 0, 0);
586 AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
590 * Finally show the window!
592 ShowWindow(hwnd, show);
593 SetForegroundWindow(hwnd);
596 * Open the initial log file if there is one.
601 * Set the palette up.
607 has_focus = (GetForegroundWindow() == hwnd);
610 if (GetMessage(&msg, NULL, 0, 0) == 1) {
611 int timer_id = 0, long_timer = 0;
613 while (msg.message != WM_QUIT) {
614 /* Sometimes DispatchMessage calls routines that use their own
615 * GetMessage loop, setup this timer so we get some control back.
617 * Also call term_update() from the timer so that if the host
618 * is sending data flat out we still do redraws.
620 if (timer_id && long_timer) {
621 KillTimer(hwnd, timer_id);
622 long_timer = timer_id = 0;
625 timer_id = SetTimer(hwnd, 1, 20, NULL);
626 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
627 DispatchMessage(&msg);
629 /* Make sure we blink everything that needs it. */
632 /* Send the paste buffer if there's anything to send */
635 /* If there's nothing new in the queue then we can do everything
636 * we've delayed, reading the socket, writing, and repainting
639 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
642 if (pending_netevent) {
643 enact_pending_netevent();
645 /* Force the cursor blink on */
648 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
652 /* Okay there is now nothing to do so we make sure the screen is
653 * completely up to date then tell windows to call us in a little
657 KillTimer(hwnd, timer_id);
665 flash_window(1); /* maintain */
668 /* Hmm, term_update didn't want to do an update too soon ... */
669 timer_id = SetTimer(hwnd, 1, 50, NULL);
671 timer_id = SetTimer(hwnd, 1, 500, NULL);
673 timer_id = SetTimer(hwnd, 1, 100, NULL);
676 /* There's no point rescanning everything in the message queue
677 * so we do an apparently unnecessary wait here
680 if (GetMessage(&msg, NULL, 0, 0) != 1)
694 if (cfg.protocol == PROT_SSH) {
705 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
707 char *do_select(SOCKET skt, int startup)
712 events = (FD_CONNECT | FD_READ | FD_WRITE |
713 FD_OOB | FD_CLOSE | FD_ACCEPT);
718 return "do_select(): internal error (hwnd==NULL)";
719 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
720 switch (WSAGetLastError()) {
722 return "Network is down";
724 return "WSAAsyncSelect(): unknown error";
731 * set or clear the "raw mouse message" mode
733 void set_raw_mouse_mode(int activate)
735 send_raw_mouse = activate;
736 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
740 * Print a message box and close the connection.
742 void connection_fatal(char *fmt, ...)
748 vsprintf(stuff, fmt, ap);
750 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
751 if (cfg.close_on_exit == COE_ALWAYS)
754 session_closed = TRUE;
755 SetWindowText(hwnd, "PuTTY (inactive)");
760 * Actually do the job requested by a WM_NETEVENT
762 static void enact_pending_netevent(void)
764 static int reentering = 0;
765 extern int select_result(WPARAM, LPARAM);
769 return; /* don't unpend the pending */
771 pending_netevent = FALSE;
774 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
777 if (ret == 0 && !session_closed) {
778 /* Abnormal exits will already have set session_closed and taken
779 * appropriate action. */
780 if (cfg.close_on_exit == COE_ALWAYS ||
781 cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
783 session_closed = TRUE;
784 SetWindowText(hwnd, "PuTTY (inactive)");
785 MessageBox(hwnd, "Connection closed by remote host",
786 "PuTTY", MB_OK | MB_ICONINFORMATION);
792 * Copy the colour palette from the configuration data into defpal.
793 * This is non-trivial because the colour indices are different.
795 static void cfgtopalette(void)
798 static const int ww[] = {
799 6, 7, 8, 9, 10, 11, 12, 13,
800 14, 15, 16, 17, 18, 19, 20, 21,
801 0, 1, 2, 3, 4, 4, 5, 5
804 for (i = 0; i < 24; i++) {
806 defpal[i].rgbtRed = cfg.colours[w][0];
807 defpal[i].rgbtGreen = cfg.colours[w][1];
808 defpal[i].rgbtBlue = cfg.colours[w][2];
813 * Set up the colour palette.
815 static void init_palette(void)
818 HDC hdc = GetDC(hwnd);
820 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
821 logpal = smalloc(sizeof(*logpal)
822 - sizeof(logpal->palPalEntry)
823 + NCOLOURS * sizeof(PALETTEENTRY));
824 logpal->palVersion = 0x300;
825 logpal->palNumEntries = NCOLOURS;
826 for (i = 0; i < NCOLOURS; i++) {
827 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
828 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
829 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
830 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
832 pal = CreatePalette(logpal);
834 SelectPalette(hdc, pal, FALSE);
836 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
839 ReleaseDC(hwnd, hdc);
842 for (i = 0; i < NCOLOURS; i++)
843 colours[i] = PALETTERGB(defpal[i].rgbtRed,
847 for (i = 0; i < NCOLOURS; i++)
848 colours[i] = RGB(defpal[i].rgbtRed,
849 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
853 * Initialise all the fonts we will need initially. There may be as many as
854 * three or as few as one. The other (poentially) twentyone fonts are done
855 * if/when they are needed.
859 * - check the font width and height, correcting our guesses if
862 * - verify that the bold font is the same width as the ordinary
863 * one, and engage shadow bolding if not.
865 * - verify that the underlined font is the same width as the
866 * ordinary one (manual underlining by means of line drawing can
867 * be done in a pinch).
869 static void init_fonts(int pick_width, int pick_height)
876 int fw_dontcare, fw_bold;
878 for (i = 0; i < FONT_MAXNO; i++)
881 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
884 if (cfg.fontisbold) {
885 fw_dontcare = FW_BOLD;
888 fw_dontcare = FW_DONTCARE;
895 font_height = pick_height;
897 font_height = cfg.fontheight;
898 if (font_height > 0) {
900 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
903 font_width = pick_width;
906 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
907 c, OUT_DEFAULT_PRECIS, \
908 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
909 FIXED_PITCH | FF_DONTCARE, cfg.font)
911 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
913 SelectObject(hdc, fonts[FONT_NORMAL]);
914 GetTextMetrics(hdc, &tm);
916 if (pick_width == 0 || pick_height == 0) {
917 font_height = tm.tmHeight;
918 font_width = tm.tmAveCharWidth;
920 font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth);
922 #ifdef RDB_DEBUG_PATCH
923 debug(23, "Primary font H=%d, AW=%d, MW=%d",
924 tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth);
929 DWORD cset = tm.tmCharSet;
930 memset(&info, 0xFF, sizeof(info));
932 /* !!! Yes the next line is right */
933 if (cset == OEM_CHARSET)
934 font_codepage = GetOEMCP();
936 if (TranslateCharsetInfo
937 ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
942 GetCPInfo(font_codepage, &cpinfo);
943 dbcs_screenfont = (cpinfo.MaxCharSize > 1);
946 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
949 * Some fonts, e.g. 9-pt Courier, draw their underlines
950 * outside their character cell. We successfully prevent
951 * screen corruption by clipping the text output, but then
952 * we lose the underline completely. Here we try to work
953 * out whether this is such a font, and if it is, we set a
954 * flag that causes underlines to be drawn by hand.
956 * Having tried other more sophisticated approaches (such
957 * as examining the TEXTMETRIC structure or requesting the
958 * height of a string), I think we'll do this the brute
959 * force way: we create a small bitmap, draw an underlined
960 * space on it, and test to see whether any pixels are
961 * foreground-coloured. (Since we expect the underline to
962 * go all the way across the character cell, we only search
963 * down a single column of the bitmap, half way across.)
967 HBITMAP und_bm, und_oldbm;
971 und_dc = CreateCompatibleDC(hdc);
972 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
973 und_oldbm = SelectObject(und_dc, und_bm);
974 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
975 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
976 SetTextColor(und_dc, RGB(255, 255, 255));
977 SetBkColor(und_dc, RGB(0, 0, 0));
978 SetBkMode(und_dc, OPAQUE);
979 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
981 for (i = 0; i < font_height; i++) {
982 c = GetPixel(und_dc, font_width / 2, i);
983 if (c != RGB(0, 0, 0))
986 SelectObject(und_dc, und_oldbm);
987 DeleteObject(und_bm);
991 DeleteObject(fonts[FONT_UNDERLINE]);
992 fonts[FONT_UNDERLINE] = 0;
996 if (bold_mode == BOLD_FONT) {
997 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
1001 descent = tm.tmAscent + 1;
1002 if (descent >= font_height)
1003 descent = font_height - 1;
1005 for (i = 0; i < 3; i++) {
1007 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
1008 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
1015 ReleaseDC(hwnd, hdc);
1017 if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
1018 und_mode = UND_LINE;
1019 DeleteObject(fonts[FONT_UNDERLINE]);
1020 fonts[FONT_UNDERLINE] = 0;
1023 if (bold_mode == BOLD_FONT &&
1024 fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
1025 bold_mode = BOLD_SHADOW;
1026 DeleteObject(fonts[FONT_BOLD]);
1027 fonts[FONT_BOLD] = 0;
1029 fontflag[0] = fontflag[1] = fontflag[2] = 1;
1034 static void another_font(int fontno)
1037 int fw_dontcare, fw_bold;
1041 if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1044 basefont = (fontno & ~(FONT_BOLDUND));
1045 if (basefont != fontno && !fontflag[basefont])
1046 another_font(basefont);
1048 if (cfg.fontisbold) {
1049 fw_dontcare = FW_BOLD;
1052 fw_dontcare = FW_DONTCARE;
1056 c = cfg.fontcharset;
1062 if (fontno & FONT_WIDE)
1064 if (fontno & FONT_NARROW)
1066 if (fontno & FONT_OEM)
1068 if (fontno & FONT_BOLD)
1070 if (fontno & FONT_UNDERLINE)
1074 CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1075 FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1076 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1077 FIXED_PITCH | FF_DONTCARE, s);
1079 fontflag[fontno] = 1;
1082 static void deinit_fonts(void)
1085 for (i = 0; i < FONT_MAXNO; i++) {
1087 DeleteObject(fonts[i]);
1093 void request_resize(int w, int h)
1097 /* If the window is maximized supress resizing attempts */
1098 if (IsZoomed(hwnd)) {
1099 if (cfg.resize_action != RESIZE_FONT)
1103 if (cfg.resize_action == RESIZE_DISABLED) return;
1104 if (h == rows && w == cols) return;
1106 /* Sanity checks ... */
1108 static int first_time = 1;
1111 switch (first_time) {
1113 /* Get the size of the screen */
1114 if (GetClientRect(GetDesktopWindow(), &ss))
1115 /* first_time = 0 */ ;
1121 /* Make sure the values are sane */
1122 width = (ss.right - ss.left - extra_width) / 4;
1123 height = (ss.bottom - ss.top - extra_height) / 6;
1125 if (w > width || h > height)
1134 term_size(h, w, cfg.savelines);
1136 if (cfg.resize_action != RESIZE_FONT) {
1137 width = extra_width + font_width * w;
1138 height = extra_height + font_height * h;
1140 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1141 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1142 SWP_NOMOVE | SWP_NOZORDER);
1146 InvalidateRect(hwnd, NULL, TRUE);
1149 static void reset_window(int reinit) {
1151 * This function decides how to resize or redraw when the
1152 * user changes something.
1154 * This function doesn't like to change the terminal size but if the
1155 * font size is locked that may be it's only soluion.
1157 int win_width, win_height;
1160 #ifdef RDB_DEBUG_PATCH
1161 debug((27, "reset_window()"));
1164 /* Current window sizes ... */
1165 GetWindowRect(hwnd, &wr);
1166 GetClientRect(hwnd, &cr);
1168 win_width = cr.right - cr.left;
1169 win_height = cr.bottom - cr.top;
1171 /* Are we being forced to reload the fonts ? */
1173 #ifdef RDB_DEBUG_PATCH
1174 debug((27, "reset_window() -- Forced deinit"));
1180 /* Oh, looks like we're minimised */
1181 if (win_width == 0 || win_height == 0)
1184 /* Is the window out of position ? */
1186 (offset_width != (win_width-font_width*cols)/2 ||
1187 offset_height != (win_height-font_height*rows)/2) ){
1188 offset_width = (win_width-font_width*cols)/2;
1189 offset_height = (win_height-font_height*rows)/2;
1190 InvalidateRect(hwnd, NULL, TRUE);
1191 #ifdef RDB_DEBUG_PATCH
1192 debug((27, "reset_window() -> Reposition terminal"));
1196 if (IsZoomed(hwnd)) {
1197 /* We're fullscreen, this means we must not change the size of
1198 * the window so it's the font size or the terminal itself.
1201 extra_width = wr.right - wr.left - cr.right + cr.left;
1202 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1204 if (cfg.resize_action == RESIZE_FONT) {
1205 if ( font_width != win_width/cols ||
1206 font_height != win_height/rows) {
1208 init_fonts(win_width/cols, win_height/rows);
1209 offset_width = (win_width-font_width*cols)/2;
1210 offset_height = (win_height-font_height*rows)/2;
1211 InvalidateRect(hwnd, NULL, TRUE);
1212 #ifdef RDB_DEBUG_PATCH
1213 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1214 font_width, font_height));
1218 if ( font_width != win_width/cols ||
1219 font_height != win_height/rows) {
1220 /* Our only choice at this point is to change the
1221 * size of the terminal; Oh well.
1223 term_size( win_height/font_height, win_width/font_width,
1225 offset_width = (win_width-font_width*cols)/2;
1226 offset_height = (win_height-font_height*rows)/2;
1227 InvalidateRect(hwnd, NULL, TRUE);
1228 #ifdef RDB_DEBUG_PATCH
1229 debug((27, "reset_window() -> Zoomed term_size"));
1236 /* Hmm, a force re-init means we should ignore the current window
1237 * so we resize to the default font size.
1240 #ifdef RDB_DEBUG_PATCH
1241 debug((27, "reset_window() -> Forced re-init"));
1244 offset_width = offset_height = cfg.window_border;
1245 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1246 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1248 if (win_width != font_width*cols + offset_width*2 ||
1249 win_height != font_height*rows + offset_height*2) {
1251 /* If this is too large windows will resize it to the maximum
1252 * allowed window size, we will then be back in here and resize
1253 * the font or terminal to fit.
1255 SetWindowPos(hwnd, NULL, 0, 0,
1256 font_width*cols + extra_width,
1257 font_height*rows + extra_height,
1258 SWP_NOMOVE | SWP_NOZORDER);
1263 /* Okay the user doesn't want us to change the font so we try the
1264 * window. But that may be too big for the screen which forces us
1265 * to change the terminal.
1267 if ((cfg.resize_action != RESIZE_FONT && reinit==0) || reinit>0) {
1268 offset_width = offset_height = cfg.window_border;
1269 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1270 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1272 if (win_width != font_width*cols + offset_width*2 ||
1273 win_height != font_height*rows + offset_height*2) {
1278 GetClientRect(GetDesktopWindow(), &ss);
1279 width = (ss.right - ss.left - extra_width) / font_width;
1280 height = (ss.bottom - ss.top - extra_height) / font_height;
1283 if ( rows > height || cols > width ) {
1284 if ( height > rows ) height = rows;
1285 if ( width > cols ) width = cols;
1286 term_size(height, width, cfg.savelines);
1287 #ifdef RDB_DEBUG_PATCH
1288 debug((27, "reset_window() -> term resize to (%d,%d)",
1293 SetWindowPos(hwnd, NULL, 0, 0,
1294 font_width*cols + extra_width,
1295 font_height*rows + extra_height,
1296 SWP_NOMOVE | SWP_NOZORDER);
1298 InvalidateRect(hwnd, NULL, TRUE);
1299 #ifdef RDB_DEBUG_PATCH
1300 debug((27, "reset_window() -> window resize to (%d,%d)",
1301 font_width*cols + extra_width,
1302 font_height*rows + extra_height));
1308 /* We're allowed to or must change the font but do we want to ? */
1310 if (font_width != (win_width-cfg.window_border*2)/cols ||
1311 font_height != (win_height-cfg.window_border*2)/rows) {
1314 init_fonts((win_width-cfg.window_border*2)/cols,
1315 (win_height-cfg.window_border*2)/rows);
1316 offset_width = (win_width-font_width*cols)/2;
1317 offset_height = (win_height-font_height*rows)/2;
1319 extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2;
1320 extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2;
1322 InvalidateRect(hwnd, NULL, TRUE);
1323 #ifdef RDB_DEBUG_PATCH
1324 debug((25, "reset_window() -> font resize to (%d,%d)",
1325 font_width, font_height));
1330 static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
1332 int thistime = GetMessageTime();
1334 if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1335 lastbtn = MBT_NOTHING;
1336 term_mouse(b, MA_CLICK, x, y, shift, ctrl, alt);
1340 if (lastbtn == b && thistime - lasttime < dbltime) {
1341 lastact = (lastact == MA_CLICK ? MA_2CLK :
1342 lastact == MA_2CLK ? MA_3CLK :
1343 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1348 if (lastact != MA_NOTHING)
1349 term_mouse(b, lastact, x, y, shift, ctrl, alt);
1350 lasttime = thistime;
1354 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1355 * into a cooked one (SELECT, EXTEND, PASTE).
1357 Mouse_Button translate_button(Mouse_Button button)
1359 if (button == MBT_LEFT)
1361 if (button == MBT_MIDDLE)
1362 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1363 if (button == MBT_RIGHT)
1364 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1365 return 0; /* shouldn't happen */
1368 static void show_mouseptr(int show)
1370 static int cursor_visible = 1;
1371 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1373 if (cursor_visible && !show)
1375 else if (!cursor_visible && show)
1377 cursor_visible = show;
1380 static int is_alt_pressed(void)
1383 int r = GetKeyboardState(keystate);
1386 if (keystate[VK_MENU] & 0x80)
1388 if (keystate[VK_RMENU] & 0x80)
1393 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1394 WPARAM wParam, LPARAM lParam)
1397 static int ignore_clip = FALSE;
1398 static int resizing = FALSE;
1399 static int need_backend_resize = FALSE;
1403 if (pending_netevent)
1404 enact_pending_netevent();
1410 if (cfg.ping_interval > 0) {
1413 if (now - last_movement > cfg.ping_interval) {
1414 back->special(TS_PING);
1415 last_movement = now;
1418 net_pending_errors();
1424 if (!cfg.warn_on_close || session_closed ||
1426 "Are you sure you want to close this session?",
1427 "PuTTY Exit Confirmation",
1428 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1429 DestroyWindow(hwnd);
1436 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1448 PROCESS_INFORMATION pi;
1449 HANDLE filemap = NULL;
1451 if (wParam == IDM_DUPSESS) {
1453 * Allocate a file-mapping memory chunk for the
1456 SECURITY_ATTRIBUTES sa;
1459 sa.nLength = sizeof(sa);
1460 sa.lpSecurityDescriptor = NULL;
1461 sa.bInheritHandle = TRUE;
1462 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1465 0, sizeof(Config), NULL);
1467 p = (Config *) MapViewOfFile(filemap,
1469 0, 0, sizeof(Config));
1471 *p = cfg; /* structure copy */
1475 sprintf(c, "putty &%p", filemap);
1477 } else if (wParam == IDM_SAVEDSESS) {
1479 sessions[(lParam - IDM_SAVED_MIN) / 16];
1480 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1482 cl = NULL; /* not a very important failure mode */
1484 sprintf(cl, "putty @%s", session);
1490 GetModuleFileName(NULL, b, sizeof(b) - 1);
1492 si.lpReserved = NULL;
1493 si.lpDesktop = NULL;
1497 si.lpReserved2 = NULL;
1498 CreateProcess(b, cl, NULL, NULL, TRUE,
1499 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1502 CloseHandle(filemap);
1512 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1515 if (!do_reconfig(hwnd))
1518 /* If user forcibly disables full-screen, gracefully unzoom */
1519 if (full_screen && !cfg.fullscreenonaltenter) {
1523 if (strcmp(prev_cfg.logfilename, cfg.logfilename) ||
1524 prev_cfg.logtype != cfg.logtype) {
1525 logfclose(); /* reset logging */
1531 * Flush the line discipline's edit buffer in the
1532 * case where local editing has just been disabled.
1534 ldisc_send(NULL, 0, 0);
1542 /* Screen size changed ? */
1543 if (cfg.height != prev_cfg.height ||
1544 cfg.width != prev_cfg.width ||
1545 cfg.savelines != prev_cfg.savelines ||
1546 cfg.resize_action != RESIZE_TERM)
1547 term_size(cfg.height, cfg.width, cfg.savelines);
1549 /* Enable or disable the scroll bar, etc */
1551 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1552 LONG nexflag, exflag =
1553 GetWindowLong(hwnd, GWL_EXSTYLE);
1556 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
1557 if (cfg.alwaysontop) {
1558 nexflag |= WS_EX_TOPMOST;
1559 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1560 SWP_NOMOVE | SWP_NOSIZE);
1562 nexflag &= ~(WS_EX_TOPMOST);
1563 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1564 SWP_NOMOVE | SWP_NOSIZE);
1567 if (cfg.sunken_edge)
1568 nexflag |= WS_EX_CLIENTEDGE;
1570 nexflag &= ~(WS_EX_CLIENTEDGE);
1576 nflg &= ~WS_VSCROLL;
1577 if (cfg.resize_action == RESIZE_DISABLED)
1578 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1580 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1582 if (nflg != flag || nexflag != exflag) {
1584 SetWindowLong(hwnd, GWL_STYLE, nflg);
1585 if (nexflag != exflag)
1586 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1588 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1589 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1590 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1598 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
1603 set_title(cfg.wintitle);
1604 if (IsIconic(hwnd)) {
1606 cfg.win_name_always ? window_name :
1610 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1611 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1612 cfg.fontisbold != prev_cfg.fontisbold ||
1613 cfg.fontheight != prev_cfg.fontheight ||
1614 cfg.fontcharset != prev_cfg.fontcharset ||
1615 cfg.vtmode != prev_cfg.vtmode ||
1616 cfg.bold_colour != prev_cfg.bold_colour ||
1617 (cfg.resize_action != RESIZE_FONT &&
1618 prev_cfg.resize_action == RESIZE_FONT))
1621 InvalidateRect(hwnd, NULL, TRUE);
1622 reset_window(init_lvl);
1623 net_pending_errors();
1636 back->special(TS_AYT);
1637 net_pending_errors();
1640 back->special(TS_BRK);
1641 net_pending_errors();
1644 back->special(TS_SYNCH);
1645 net_pending_errors();
1648 back->special(TS_EC);
1649 net_pending_errors();
1652 back->special(TS_EL);
1653 net_pending_errors();
1656 back->special(TS_GA);
1657 net_pending_errors();
1660 back->special(TS_NOP);
1661 net_pending_errors();
1664 back->special(TS_ABORT);
1665 net_pending_errors();
1668 back->special(TS_AO);
1669 net_pending_errors();
1672 back->special(TS_IP);
1673 net_pending_errors();
1676 back->special(TS_SUSP);
1677 net_pending_errors();
1680 back->special(TS_EOR);
1681 net_pending_errors();
1684 back->special(TS_EOF);
1685 net_pending_errors();
1692 * We get this if the System menu has been activated.
1693 * This might happen from within TranslateKey, in which
1694 * case it really wants to be followed by a `space'
1695 * character to actually _bring the menu up_ rather
1696 * than just sitting there in `ready to appear' state.
1699 PostMessage(hwnd, WM_CHAR, ' ', 0);
1701 case IDM_FULLSCREEN:
1705 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1706 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1711 #define X_POS(l) ((int)(short)LOWORD(l))
1712 #define Y_POS(l) ((int)(short)HIWORD(l))
1714 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1715 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1716 #define WHEEL_DELTA 120
1719 wheel_accumulator += (short) HIWORD(wParam);
1720 wParam = LOWORD(wParam);
1722 /* process events when the threshold is reached */
1723 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1726 /* reduce amount for next time */
1727 if (wheel_accumulator > 0) {
1729 wheel_accumulator -= WHEEL_DELTA;
1730 } else if (wheel_accumulator < 0) {
1732 wheel_accumulator += WHEEL_DELTA;
1736 if (send_raw_mouse) {
1737 /* send a mouse-down followed by a mouse up */
1741 TO_CHR_X(X_POS(lParam)),
1742 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1743 wParam & MK_CONTROL, is_alt_pressed());
1744 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1745 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1746 wParam & MK_CONTROL, is_alt_pressed());
1748 /* trigger a scroll */
1750 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1755 case WM_LBUTTONDOWN:
1756 case WM_MBUTTONDOWN:
1757 case WM_RBUTTONDOWN:
1765 case WM_LBUTTONDOWN:
1769 case WM_MBUTTONDOWN:
1770 button = MBT_MIDDLE;
1773 case WM_RBUTTONDOWN:
1782 button = MBT_MIDDLE;
1790 button = press = 0; /* shouldn't happen */
1795 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1796 wParam & MK_SHIFT, wParam & MK_CONTROL,
1800 term_mouse(button, MA_RELEASE,
1801 TO_CHR_X(X_POS(lParam)),
1802 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1803 wParam & MK_CONTROL, is_alt_pressed());
1811 * Add the mouse position and message time to the random
1814 noise_ultralight(lParam);
1816 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1818 if (wParam & MK_LBUTTON)
1820 else if (wParam & MK_MBUTTON)
1824 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1825 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1826 wParam & MK_CONTROL, is_alt_pressed());
1829 case WM_NCMOUSEMOVE:
1831 noise_ultralight(lParam);
1833 case WM_IGNORE_CLIP:
1834 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1836 case WM_DESTROYCLIPBOARD:
1839 ignore_clip = FALSE;
1845 hdc = BeginPaint(hwnd, &p);
1847 SelectPalette(hdc, pal, TRUE);
1848 RealizePalette(hdc);
1851 (p.rcPaint.left-offset_width)/font_width,
1852 (p.rcPaint.top-offset_height)/font_height,
1853 (p.rcPaint.right-offset_width-1)/font_width,
1854 (p.rcPaint.bottom-offset_height-1)/font_height);
1857 p.rcPaint.left < offset_width ||
1858 p.rcPaint.top < offset_height ||
1859 p.rcPaint.right >= offset_width + font_width*cols ||
1860 p.rcPaint.bottom>= offset_height + font_height*rows)
1862 HBRUSH fillcolour, oldbrush;
1864 fillcolour = CreateSolidBrush (
1865 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1866 oldbrush = SelectObject(hdc, fillcolour);
1867 edge = CreatePen(PS_SOLID, 0,
1868 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1869 oldpen = SelectObject(hdc, edge);
1871 ExcludeClipRect(hdc,
1872 offset_width, offset_height,
1873 offset_width+font_width*cols,
1874 offset_height+font_height*rows);
1876 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
1877 p.rcPaint.right, p.rcPaint.bottom);
1879 // SelectClipRgn(hdc, NULL);
1881 SelectObject(hdc, oldbrush);
1882 DeleteObject(fillcolour);
1883 SelectObject(hdc, oldpen);
1886 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1887 SelectObject(hdc, GetStockObject(WHITE_PEN));
1893 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1894 * but the only one that's likely to try to overload us is FD_READ.
1895 * This means buffering just one is fine.
1897 if (pending_netevent)
1898 enact_pending_netevent();
1900 pending_netevent = TRUE;
1901 pend_netevent_wParam = wParam;
1902 pend_netevent_lParam = lParam;
1903 if (WSAGETSELECTEVENT(lParam) != FD_READ)
1904 enact_pending_netevent();
1906 time(&last_movement);
1910 CreateCaret(hwnd, caretbm, font_width, font_height);
1912 flash_window(0); /* stop */
1924 case WM_ENTERSIZEMOVE:
1925 #ifdef RDB_DEBUG_PATCH
1926 debug((27, "WM_ENTERSIZEMOVE"));
1930 need_backend_resize = FALSE;
1932 case WM_EXITSIZEMOVE:
1935 #ifdef RDB_DEBUG_PATCH
1936 debug((27, "WM_EXITSIZEMOVE"));
1938 if (need_backend_resize) {
1939 term_size(cfg.height, cfg.width, cfg.savelines);
1940 InvalidateRect(hwnd, NULL, TRUE);
1945 * This does two jobs:
1946 * 1) Keep the sizetip uptodate
1947 * 2) Make sure the window size is _stepped_ in units of the font size.
1949 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
1950 int width, height, w, h, ew, eh;
1951 LPRECT r = (LPRECT) lParam;
1953 if ( !need_backend_resize &&
1954 (cfg.height != rows || cfg.width != cols )) {
1956 * Great! It seems the host has been changing the terminal
1957 * size, well the user is now grabbing so this is probably
1958 * the least confusing solution in the long run even though
1959 * it a is suprise. Unfortunatly the only way to prevent
1960 * this seems to be to let the host change the window size
1961 * and as that's a user option we're still right back here.
1963 term_size(cfg.height, cfg.width, cfg.savelines);
1965 InvalidateRect(hwnd, NULL, TRUE);
1966 need_backend_resize = TRUE;
1969 width = r->right - r->left - extra_width;
1970 height = r->bottom - r->top - extra_height;
1971 w = (width + font_width / 2) / font_width;
1974 h = (height + font_height / 2) / font_height;
1977 UpdateSizeTip(hwnd, w, h);
1978 ew = width - w * font_width;
1979 eh = height - h * font_height;
1981 if (wParam == WMSZ_LEFT ||
1982 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1988 if (wParam == WMSZ_TOP ||
1989 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1999 int width, height, w, h, rv = 0;
2000 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2001 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2002 LPRECT r = (LPRECT) lParam;
2004 width = r->right - r->left - ex_width;
2005 height = r->bottom - r->top - ex_height;
2007 w = (width + cols/2)/cols;
2008 h = (height + rows/2)/rows;
2009 if ( r->right != r->left + w*cols + ex_width)
2012 if (wParam == WMSZ_LEFT ||
2013 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2014 r->left = r->right - w*cols - ex_width;
2016 r->right = r->left + w*cols + ex_width;
2018 if (r->bottom != r->top + h*rows + ex_height)
2021 if (wParam == WMSZ_TOP ||
2022 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2023 r->top = r->bottom - h*rows - ex_height;
2025 r->bottom = r->top + h*rows + ex_height;
2029 /* break; (never reached) */
2031 #ifdef RDB_DEBUG_PATCH
2032 debug((27, "WM_SIZE %s (%d,%d)",
2033 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2034 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2035 (wParam == SIZE_RESTORED && resizing) ? "to":
2036 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2038 LOWORD(lParam), HIWORD(lParam)));
2040 if (wParam == SIZE_MINIMIZED) {
2042 cfg.win_name_always ? window_name : icon_name);
2045 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2046 SetWindowText(hwnd, window_name);
2048 if (cfg.resize_action == RESIZE_DISABLED) {
2049 /* A resize, well it better be a minimize. */
2053 int width, height, w, h;
2055 width = LOWORD(lParam);
2056 height = HIWORD(lParam);
2059 if (wParam == SIZE_MAXIMIZED) {
2063 if (cfg.resize_action != RESIZE_FONT) {
2064 w = width / font_width;
2066 h = height / font_height;
2069 term_size(h, w, cfg.savelines);
2072 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2074 if (cfg.resize_action != RESIZE_FONT)
2075 term_size(prev_rows, prev_cols, cfg.savelines);
2078 /* This is an unexpected resize, these will normally happen
2079 * if the window is too large. Probably either the user
2080 * selected a huge font or the screen size has changed.
2082 * This is also called with minimize.
2084 else reset_window(-1);
2088 * Don't call back->size in mid-resize. (To prevent
2089 * massive numbers of resize events getting sent
2090 * down the connection during an NT opaque drag.)
2093 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
2094 need_backend_resize = TRUE;
2095 w = (width-cfg.window_border*2) / font_width;
2097 h = (height-cfg.window_border*2) / font_height;
2108 switch (LOWORD(wParam)) {
2122 term_scroll(0, +rows / 2);
2125 term_scroll(0, -rows / 2);
2127 case SB_THUMBPOSITION:
2129 term_scroll(1, HIWORD(wParam));
2133 case WM_PALETTECHANGED:
2134 if ((HWND) wParam != hwnd && pal != NULL) {
2135 HDC hdc = get_ctx();
2137 if (RealizePalette(hdc) > 0)
2143 case WM_QUERYNEWPALETTE:
2145 HDC hdc = get_ctx();
2147 if (RealizePalette(hdc) > 0)
2159 * Add the scan code and keypress timing to the random
2162 noise_ultralight(lParam);
2165 * We don't do TranslateMessage since it disassociates the
2166 * resulting CHAR message from the KEYDOWN that sparked it,
2167 * which we occasionally don't want. Instead, we process
2168 * KEYDOWN, and call the Win32 translator functions so that
2169 * we get the translations under _our_ control.
2172 unsigned char buf[20];
2175 if (wParam == VK_PROCESSKEY) {
2178 m.message = WM_KEYDOWN;
2180 m.lParam = lParam & 0xdfff;
2181 TranslateMessage(&m);
2183 len = TranslateKey(message, wParam, lParam, buf);
2185 return DefWindowProc(hwnd, message, wParam, lParam);
2189 * Interrupt an ongoing paste. I'm not sure
2190 * this is sensible, but for the moment it's
2191 * preferable to having to faff about buffering
2197 * We need not bother about stdin backlogs
2198 * here, because in GUI PuTTY we can't do
2199 * anything about it anyway; there's no means
2200 * of asking Windows to hold off on KEYDOWN
2201 * messages. We _have_ to buffer everything
2204 ldisc_send(buf, len, 1);
2209 net_pending_errors();
2211 case WM_INPUTLANGCHANGE:
2213 /* wParam == Font number */
2214 /* lParam == Locale */
2216 HKL NewInputLocale = (HKL) lParam;
2218 // lParam == GetKeyboardLayout(0);
2220 GetLocaleInfo(LOWORD(NewInputLocale),
2221 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
2223 kbd_codepage = atoi(lbuf);
2226 case WM_IME_COMPOSITION:
2232 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2233 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2235 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2236 break; /* fall back to DefWindowProc */
2238 hIMC = ImmGetContext(hwnd);
2239 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2242 buff = (char*) smalloc(n);
2243 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2244 luni_send((unsigned short *)buff, n / 2, 1);
2247 ImmReleaseContext(hwnd, hIMC);
2252 if (wParam & 0xFF00) {
2253 unsigned char buf[2];
2256 buf[0] = wParam >> 8;
2257 lpage_send(kbd_codepage, buf, 2, 1);
2259 char c = (unsigned char) wParam;
2260 lpage_send(kbd_codepage, &c, 1, 1);
2266 * Nevertheless, we are prepared to deal with WM_CHAR
2267 * messages, should they crop up. So if someone wants to
2268 * post the things to us as part of a macro manoeuvre,
2269 * we're ready to cope.
2272 char c = (unsigned char)wParam;
2273 lpage_send(CP_ACP, &c, 1, 1);
2277 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2278 SetCursor(LoadCursor(NULL, IDC_ARROW));
2283 return DefWindowProc(hwnd, message, wParam, lParam);
2287 * Move the system caret. (We maintain one, even though it's
2288 * invisible, for the benefit of blind people: apparently some
2289 * helper software tracks the system caret, so we should arrange to
2292 void sys_cursor(int x, int y)
2297 if (!has_focus) return;
2299 SetCaretPos(x * font_width + offset_width,
2300 y * font_height + offset_height);
2302 /* IMM calls on Win98 and beyond only */
2303 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2305 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2306 osVersion.dwMinorVersion == 0) return; /* 95 */
2308 /* we should have the IMM functions */
2309 hIMC = ImmGetContext(hwnd);
2310 cf.dwStyle = CFS_POINT;
2311 cf.ptCurrentPos.x = x * font_width;
2312 cf.ptCurrentPos.y = y * font_height;
2313 ImmSetCompositionWindow(hIMC, &cf);
2315 ImmReleaseContext(hwnd, hIMC);
2319 * Draw a line of text in the window, at given character
2320 * coordinates, in given attributes.
2322 * We are allowed to fiddle with the contents of `text'.
2324 void do_text(Context ctx, int x, int y, char *text, int len,
2325 unsigned long attr, int lattr)
2328 int nfg, nbg, nfont;
2331 int force_manual_underline = 0;
2332 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2333 int char_width = fnt_width;
2334 int text_adjust = 0;
2335 static int *IpDx = 0, IpDxLEN = 0;
2337 if (attr & ATTR_WIDE)
2340 if (len > IpDxLEN || IpDx[0] != char_width) {
2342 if (len > IpDxLEN) {
2344 IpDx = smalloc((len + 16) * sizeof(int));
2345 IpDxLEN = (len + 16);
2347 for (i = 0; i < IpDxLEN; i++)
2348 IpDx[i] = char_width;
2351 /* Only want the left half of double width lines */
2352 if (lattr != LATTR_NORM && x*2 >= cols)
2360 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2361 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2362 attr ^= ATTR_CUR_XOR;
2366 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2367 /* Assume a poorman font is borken in other ways too. */
2377 nfont |= FONT_WIDE + FONT_HIGH;
2380 if (attr & ATTR_NARROW)
2381 nfont |= FONT_NARROW;
2383 /* Special hack for the VT100 linedraw glyphs. */
2384 if ((attr & CSET_MASK) == 0x2300) {
2385 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2386 switch ((unsigned char) (text[0])) {
2388 text_adjust = -2 * font_height / 5;
2391 text_adjust = -1 * font_height / 5;
2394 text_adjust = font_height / 5;
2397 text_adjust = 2 * font_height / 5;
2400 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2403 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2404 attr |= (unitab_xterm['q'] & CSET_MASK);
2405 if (attr & ATTR_UNDER) {
2406 attr &= ~ATTR_UNDER;
2407 force_manual_underline = 1;
2412 /* Anything left as an original character set is unprintable. */
2413 if (DIRECT_CHAR(attr)) {
2416 memset(text, 0xFD, len);
2420 if ((attr & CSET_MASK) == ATTR_OEMCP)
2423 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2424 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2425 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2427 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2428 nfont |= FONT_UNDERLINE;
2429 another_font(nfont);
2430 if (!fonts[nfont]) {
2431 if (nfont & FONT_UNDERLINE)
2432 force_manual_underline = 1;
2433 /* Don't do the same for manual bold, it could be bad news. */
2435 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2437 another_font(nfont);
2439 nfont = FONT_NORMAL;
2440 if (attr & ATTR_REVERSE) {
2445 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2447 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2451 SelectObject(hdc, fonts[nfont]);
2452 SetTextColor(hdc, fg);
2453 SetBkColor(hdc, bg);
2454 SetBkMode(hdc, OPAQUE);
2457 line_box.right = x + char_width * len;
2458 line_box.bottom = y + font_height;
2460 /* Only want the left half of double width lines */
2461 if (line_box.right > font_width*cols+offset_width)
2462 line_box.right = font_width*cols+offset_width;
2464 /* We're using a private area for direct to font. (512 chars.) */
2465 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2466 /* Ho Hum, dbcs fonts are a PITA! */
2467 /* To display on W9x I have to convert to UCS */
2468 static wchar_t *uni_buf = 0;
2469 static int uni_len = 0;
2471 if (len > uni_len) {
2473 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2476 for(nlen = mptr = 0; mptr<len; mptr++) {
2477 uni_buf[nlen] = 0xFFFD;
2478 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2479 IpDx[nlen] += char_width;
2480 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2481 text+mptr, 2, uni_buf+nlen, 1);
2486 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2487 text+mptr, 1, uni_buf+nlen, 1);
2495 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2496 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2497 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2498 SetBkMode(hdc, TRANSPARENT);
2499 ExtTextOutW(hdc, x - 1,
2500 y - font_height * (lattr ==
2501 LATTR_BOT) + text_adjust,
2502 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2506 } else if (DIRECT_FONT(attr)) {
2508 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2509 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2510 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2511 SetBkMode(hdc, TRANSPARENT);
2513 /* GRR: This draws the character outside it's box and can leave
2514 * 'droppings' even with the clip box! I suppose I could loop it
2515 * one character at a time ... yuk.
2517 * Or ... I could do a test print with "W", and use +1 or -1 for this
2518 * shift depending on if the leftmost column is blank...
2520 ExtTextOut(hdc, x - 1,
2521 y - font_height * (lattr ==
2522 LATTR_BOT) + text_adjust,
2523 ETO_CLIPPED, &line_box, text, len, IpDx);
2526 /* And 'normal' unicode characters */
2527 static WCHAR *wbuf = NULL;
2528 static int wlen = 0;
2533 wbuf = smalloc(wlen * sizeof(WCHAR));
2535 for (i = 0; i < len; i++)
2536 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2539 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2540 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2542 /* And the shadow bold hack. */
2543 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2544 SetBkMode(hdc, TRANSPARENT);
2545 ExtTextOutW(hdc, x - 1,
2546 y - font_height * (lattr ==
2547 LATTR_BOT) + text_adjust,
2548 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2551 if (lattr != LATTR_TOP && (force_manual_underline ||
2552 (und_mode == UND_LINE
2553 && (attr & ATTR_UNDER)))) {
2556 if (lattr == LATTR_BOT)
2557 dec = dec * 2 - font_height;
2559 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2560 MoveToEx(hdc, x, y + dec, NULL);
2561 LineTo(hdc, x + len * char_width, y + dec);
2562 oldpen = SelectObject(hdc, oldpen);
2563 DeleteObject(oldpen);
2567 void do_cursor(Context ctx, int x, int y, char *text, int len,
2568 unsigned long attr, int lattr)
2574 int ctype = cfg.cursor_type;
2576 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2577 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2578 do_text(ctx, x, y, text, len, attr, lattr);
2582 attr |= TATTR_RIGHTCURS;
2585 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2586 if (attr & ATTR_WIDE)
2593 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2596 pts[0].x = pts[1].x = pts[4].x = x;
2597 pts[2].x = pts[3].x = x + char_width - 1;
2598 pts[0].y = pts[3].y = pts[4].y = y;
2599 pts[1].y = pts[2].y = y + font_height - 1;
2600 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2601 Polyline(hdc, pts, 5);
2602 oldpen = SelectObject(hdc, oldpen);
2603 DeleteObject(oldpen);
2604 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2605 int startx, starty, dx, dy, length, i;
2608 starty = y + descent;
2611 length = char_width;
2614 if (attr & TATTR_RIGHTCURS)
2615 xadjust = char_width - 1;
2616 startx = x + xadjust;
2620 length = font_height;
2622 if (attr & TATTR_ACTCURS) {
2625 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2626 MoveToEx(hdc, startx, starty, NULL);
2627 LineTo(hdc, startx + dx * length, starty + dy * length);
2628 oldpen = SelectObject(hdc, oldpen);
2629 DeleteObject(oldpen);
2631 for (i = 0; i < length; i++) {
2633 SetPixel(hdc, startx, starty, colours[23]);
2642 /* This function gets the actual width of a character in the normal font.
2644 int CharWidth(Context ctx, int uc) {
2648 /* If the font max is the same as the font ave width then this
2649 * function is a no-op.
2651 if (!font_dualwidth) return 1;
2653 switch (uc & CSET_MASK) {
2655 uc = unitab_line[uc & 0xFF];
2658 uc = unitab_xterm[uc & 0xFF];
2661 uc = unitab_scoacs[uc & 0xFF];
2664 if (DIRECT_FONT(uc)) {
2665 if (dbcs_screenfont) return 1;
2667 /* Speedup, I know of no font where ascii is the wrong width */
2668 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2671 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2672 SelectObject(hdc, fonts[FONT_NORMAL]);
2673 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2674 another_font(FONT_OEM);
2675 if (!fonts[FONT_OEM]) return 0;
2677 SelectObject(hdc, fonts[FONT_OEM]);
2681 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2682 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2685 /* Speedup, I know of no font where ascii is the wrong width */
2686 if (uc >= ' ' && uc <= '~') return 1;
2688 SelectObject(hdc, fonts[FONT_NORMAL]);
2689 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2690 /* Okay that one worked */ ;
2691 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2692 /* This should work on 9x too, but it's "less accurate" */ ;
2697 ibuf += font_width / 2 -1;
2704 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2705 * codes. Returns number of bytes used or zero to drop the message
2706 * or -1 to forward the message to windows.
2708 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2709 unsigned char *output)
2712 int scan, left_alt = 0, key_down, shift_state;
2714 unsigned char *p = output;
2715 static int alt_sum = 0;
2717 HKL kbd_layout = GetKeyboardLayout(0);
2719 static WORD keys[3];
2720 static int compose_char = 0;
2721 static WPARAM compose_key = 0;
2723 r = GetKeyboardState(keystate);
2725 memset(keystate, 0, sizeof(keystate));
2728 #define SHOW_TOASCII_RESULT
2729 { /* Tell us all about key events */
2730 static BYTE oldstate[256];
2731 static int first = 1;
2735 memcpy(oldstate, keystate, sizeof(oldstate));
2738 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2740 } else if ((HIWORD(lParam) & KF_UP)
2741 && scan == (HIWORD(lParam) & 0xFF)) {
2745 if (wParam >= VK_F1 && wParam <= VK_F20)
2746 debug(("K_F%d", wParam + 1 - VK_F1));
2759 debug(("VK_%02x", wParam));
2761 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2763 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2765 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2766 if (ch >= ' ' && ch <= '~')
2767 debug((", '%c'", ch));
2769 debug((", $%02x", ch));
2772 debug((", KB0=%02x", keys[0]));
2774 debug((", KB1=%02x", keys[1]));
2776 debug((", KB2=%02x", keys[2]));
2778 if ((keystate[VK_SHIFT] & 0x80) != 0)
2780 if ((keystate[VK_CONTROL] & 0x80) != 0)
2782 if ((HIWORD(lParam) & KF_EXTENDED))
2784 if ((HIWORD(lParam) & KF_UP))
2788 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2789 else if ((HIWORD(lParam) & KF_UP))
2790 oldstate[wParam & 0xFF] ^= 0x80;
2792 oldstate[wParam & 0xFF] ^= 0x81;
2794 for (ch = 0; ch < 256; ch++)
2795 if (oldstate[ch] != keystate[ch])
2796 debug((", M%02x=%02x", ch, keystate[ch]));
2798 memcpy(oldstate, keystate, sizeof(oldstate));
2802 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2803 keystate[VK_RMENU] = keystate[VK_MENU];
2807 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2808 if ((cfg.funky_type == 3 ||
2809 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2810 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2812 wParam = VK_EXECUTE;
2814 /* UnToggle NUMLock */
2815 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2816 keystate[VK_NUMLOCK] ^= 1;
2819 /* And write back the 'adjusted' state */
2820 SetKeyboardState(keystate);
2823 /* Disable Auto repeat if required */
2824 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2827 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2830 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2832 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2833 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2834 if (cfg.ctrlaltkeys)
2835 keystate[VK_MENU] = 0;
2837 keystate[VK_RMENU] = 0x80;
2842 alt_pressed = (left_alt && key_down);
2844 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2845 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2846 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2848 /* Note if AltGr was pressed and if it was used as a compose key */
2849 if (!compose_state) {
2850 compose_key = 0x100;
2851 if (cfg.compose_key) {
2852 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2853 compose_key = wParam;
2855 if (wParam == VK_APPS)
2856 compose_key = wParam;
2859 if (wParam == compose_key) {
2860 if (compose_state == 0
2861 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2863 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2867 } else if (compose_state == 1 && wParam != VK_CONTROL)
2871 * Record that we pressed key so the scroll window can be reset, but
2872 * be careful to avoid Shift-UP/Down
2874 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
2875 wParam != VK_MENU && wParam != VK_CONTROL) {
2879 if (compose_state > 1 && left_alt)
2882 /* Sanitize the number pad if not using a PC NumPad */
2883 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2884 && cfg.funky_type != 2)
2885 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2886 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2890 nParam = VK_NUMPAD0;
2893 nParam = VK_NUMPAD1;
2896 nParam = VK_NUMPAD2;
2899 nParam = VK_NUMPAD3;
2902 nParam = VK_NUMPAD4;
2905 nParam = VK_NUMPAD5;
2908 nParam = VK_NUMPAD6;
2911 nParam = VK_NUMPAD7;
2914 nParam = VK_NUMPAD8;
2917 nParam = VK_NUMPAD9;
2920 nParam = VK_DECIMAL;
2924 if (keystate[VK_NUMLOCK] & 1)
2931 /* If a key is pressed and AltGr is not active */
2932 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2933 /* Okay, prepare for most alts then ... */
2937 /* Lets see if it's a pattern we know all about ... */
2938 if (wParam == VK_PRIOR && shift_state == 1) {
2939 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2942 if (wParam == VK_NEXT && shift_state == 1) {
2943 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2946 if (wParam == VK_INSERT && shift_state == 1) {
2950 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2953 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2954 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2957 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter) {
2961 /* Control-Numlock for app-keypad mode switch */
2962 if (wParam == VK_PAUSE && shift_state == 2) {
2963 app_keypad_keys ^= 1;
2967 /* Nethack keypad */
2968 if (cfg.nethack_keypad && !left_alt) {
2971 *p++ = shift_state ? 'B' : 'b';
2974 *p++ = shift_state ? 'J' : 'j';
2977 *p++ = shift_state ? 'N' : 'n';
2980 *p++ = shift_state ? 'H' : 'h';
2983 *p++ = shift_state ? '.' : '.';
2986 *p++ = shift_state ? 'L' : 'l';
2989 *p++ = shift_state ? 'Y' : 'y';
2992 *p++ = shift_state ? 'K' : 'k';
2995 *p++ = shift_state ? 'U' : 'u';
3000 /* Application Keypad */
3004 if (cfg.funky_type == 3 ||
3005 (cfg.funky_type <= 1 &&
3006 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3020 if (app_keypad_keys && !cfg.no_applic_k)
3057 if (cfg.funky_type == 2) {
3062 } else if (shift_state)
3069 if (cfg.funky_type == 2)
3073 if (cfg.funky_type == 2)
3077 if (cfg.funky_type == 2)
3082 if (HIWORD(lParam) & KF_EXTENDED)
3088 if (xkey >= 'P' && xkey <= 'S')
3089 p += sprintf((char *) p, "\x1B%c", xkey);
3091 p += sprintf((char *) p, "\x1B?%c", xkey);
3093 p += sprintf((char *) p, "\x1BO%c", xkey);
3098 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
3099 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3103 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3109 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3113 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3117 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3122 if (wParam == VK_PAUSE) { /* Break/Pause */
3127 /* Control-2 to Control-8 are special */
3128 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3129 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3132 if (shift_state == 2 && wParam == 0xBD) {
3136 if (shift_state == 2 && wParam == 0xDF) {
3140 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3147 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3148 * for integer decimal nn.)
3150 * We also deal with the weird ones here. Linux VCs replace F1
3151 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3152 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3158 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3161 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3164 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3167 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3170 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3173 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3176 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3179 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3182 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3185 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3218 if ((shift_state&2) == 0) switch (wParam) {
3238 /* Reorder edit keys to physical order */
3239 if (cfg.funky_type == 3 && code <= 6)
3240 code = "\0\2\1\4\5\3\6"[code];
3242 if (vt52_mode && code > 0 && code <= 6) {
3243 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3247 if (cfg.funky_type == 5 && /* SCO function keys */
3248 code >= 11 && code <= 34) {
3249 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3252 case VK_F1: index = 0; break;
3253 case VK_F2: index = 1; break;
3254 case VK_F3: index = 2; break;
3255 case VK_F4: index = 3; break;
3256 case VK_F5: index = 4; break;
3257 case VK_F6: index = 5; break;
3258 case VK_F7: index = 6; break;
3259 case VK_F8: index = 7; break;
3260 case VK_F9: index = 8; break;
3261 case VK_F10: index = 9; break;
3262 case VK_F11: index = 10; break;
3263 case VK_F12: index = 11; break;
3265 if (keystate[VK_SHIFT] & 0x80) index += 12;
3266 if (keystate[VK_CONTROL] & 0x80) index += 24;
3267 p += sprintf((char *) p, "\x1B[%c", codes[index]);
3270 if (cfg.funky_type == 5 && /* SCO small keypad */
3271 code >= 1 && code <= 6) {
3272 char codes[] = "HL.FIG";
3276 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3280 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3287 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3290 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3293 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3294 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3297 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3299 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3301 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3304 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3305 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3309 p += sprintf((char *) p, "\x1B[%d~", code);
3314 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3315 * some reason seems to send VK_CLEAR to Windows...).
3338 p += sprintf((char *) p, "\x1B%c", xkey);
3340 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3341 /* VT100 & VT102 manuals both state the app cursor keys
3342 * only work if the app keypad is on.
3344 if (!app_keypad_keys)
3346 /* Useful mapping of Ctrl-arrows */
3347 if (shift_state == 2)
3351 p += sprintf((char *) p, "\x1BO%c", xkey);
3353 p += sprintf((char *) p, "\x1B[%c", xkey);
3360 * Finally, deal with Return ourselves. (Win95 seems to
3361 * foul it up when Alt is pressed, for some reason.)
3363 if (wParam == VK_RETURN) { /* Return */
3369 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3370 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3375 /* Okay we've done everything interesting; let windows deal with
3376 * the boring stuff */
3380 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3381 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3383 keystate[VK_CAPITAL] = 0;
3386 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3387 #ifdef SHOW_TOASCII_RESULT
3388 if (r == 1 && !key_down) {
3390 if (in_utf || dbcs_screenfont)
3391 debug((", (U+%04x)", alt_sum));
3393 debug((", LCH(%d)", alt_sum));
3395 debug((", ACH(%d)", keys[0]));
3400 for (r1 = 0; r1 < r; r1++) {
3401 debug(("%s%d", r1 ? "," : "", keys[r1]));
3410 * Interrupt an ongoing paste. I'm not sure this is
3411 * sensible, but for the moment it's preferable to
3412 * having to faff about buffering things.
3417 for (i = 0; i < r; i++) {
3418 unsigned char ch = (unsigned char) keys[i];
3420 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3425 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3429 if ((nc = check_compose(compose_char, ch)) == -1) {
3430 MessageBeep(MB_ICONHAND);
3434 luni_send(&keybuf, 1, 1);
3442 if (in_utf || dbcs_screenfont) {
3444 luni_send(&keybuf, 1, 1);
3446 ch = (char) alt_sum;
3448 * We need not bother about stdin
3449 * backlogs here, because in GUI PuTTY
3450 * we can't do anything about it
3451 * anyway; there's no means of asking
3452 * Windows to hold off on KEYDOWN
3453 * messages. We _have_ to buffer
3454 * everything we're sent.
3456 ldisc_send(&ch, 1, 1);
3460 lpage_send(kbd_codepage, &ch, 1, 1);
3462 if(capsOn && ch < 0x80) {
3465 cbuf[1] = xlat_uskbd2cyrllic(ch);
3466 luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3471 lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3477 /* This is so the ALT-Numpad and dead keys work correctly. */
3482 /* If we're definitly not building up an ALT-54321 then clear it */
3485 /* If we will be using alt_sum fix the 256s */
3486 else if (keys[0] && (in_utf || dbcs_screenfont))
3491 * ALT alone may or may not want to bring up the System menu.
3492 * If it's not meant to, we return 0 on presses or releases of
3493 * ALT, to show that we've swallowed the keystroke. Otherwise
3494 * we return -1, which means Windows will give the keystroke
3495 * its default handling (i.e. bring up the System menu).
3497 if (wParam == VK_MENU && !cfg.alt_only)
3503 void set_title(char *title)
3506 window_name = smalloc(1 + strlen(title));
3507 strcpy(window_name, title);
3508 if (cfg.win_name_always || !IsIconic(hwnd))
3509 SetWindowText(hwnd, title);
3512 void set_icon(char *title)
3515 icon_name = smalloc(1 + strlen(title));
3516 strcpy(icon_name, title);
3517 if (!cfg.win_name_always && IsIconic(hwnd))
3518 SetWindowText(hwnd, title);
3521 void set_sbar(int total, int start, int page)
3528 si.cbSize = sizeof(si);
3529 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3531 si.nMax = total - 1;
3535 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3538 Context get_ctx(void)
3544 SelectPalette(hdc, pal, FALSE);
3550 void free_ctx(Context ctx)
3552 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3553 ReleaseDC(hwnd, ctx);
3556 static void real_palette_set(int n, int r, int g, int b)
3559 logpal->palPalEntry[n].peRed = r;
3560 logpal->palPalEntry[n].peGreen = g;
3561 logpal->palPalEntry[n].peBlue = b;
3562 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3563 colours[n] = PALETTERGB(r, g, b);
3564 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3566 colours[n] = RGB(r, g, b);
3569 void palette_set(int n, int r, int g, int b)
3571 static const int first[21] = {
3572 0, 2, 4, 6, 8, 10, 12, 14,
3573 1, 3, 5, 7, 9, 11, 13, 15,
3576 real_palette_set(first[n], r, g, b);
3578 real_palette_set(first[n] + 1, r, g, b);
3580 HDC hdc = get_ctx();
3581 UnrealizeObject(pal);
3582 RealizePalette(hdc);
3587 void palette_reset(void)
3591 for (i = 0; i < NCOLOURS; i++) {
3593 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3594 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3595 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3596 logpal->palPalEntry[i].peFlags = 0;
3597 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3598 defpal[i].rgbtGreen,
3599 defpal[i].rgbtBlue);
3601 colours[i] = RGB(defpal[i].rgbtRed,
3602 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3607 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3609 RealizePalette(hdc);
3614 void write_aclip(char *data, int len, int must_deselect)
3619 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3622 lock = GlobalLock(clipdata);
3625 memcpy(lock, data, len);
3626 ((unsigned char *) lock)[len] = 0;
3627 GlobalUnlock(clipdata);
3630 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3632 if (OpenClipboard(hwnd)) {
3634 SetClipboardData(CF_TEXT, clipdata);
3637 GlobalFree(clipdata);
3640 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3644 * Note: unlike write_aclip() this will not append a nul.
3646 void write_clip(wchar_t * data, int len, int must_deselect)
3648 HGLOBAL clipdata, clipdata2, clipdata3;
3650 void *lock, *lock2, *lock3;
3652 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3654 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3655 len * sizeof(wchar_t));
3656 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3658 if (!clipdata || !clipdata2 || !clipdata3) {
3660 GlobalFree(clipdata);
3662 GlobalFree(clipdata2);
3664 GlobalFree(clipdata3);
3667 if (!(lock = GlobalLock(clipdata)))
3669 if (!(lock2 = GlobalLock(clipdata2)))
3672 memcpy(lock, data, len * sizeof(wchar_t));
3673 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3675 if (cfg.rtf_paste) {
3676 wchar_t unitab[256];
3678 unsigned char *tdata = (unsigned char *)lock2;
3679 wchar_t *udata = (wchar_t *)lock;
3680 int rtflen = 0, uindex = 0, tindex = 0;
3682 int multilen, blen, alen, totallen, i;
3683 char before[16], after[4];
3685 get_unitab(CP_ACP, unitab, 0);
3687 rtfsize = 100 + strlen(cfg.font);
3688 rtf = smalloc(rtfsize);
3689 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3690 GetACP(), cfg.font);
3691 rtflen = strlen(rtf);
3694 * We want to construct a piece of RTF that specifies the
3695 * same Unicode text. To do this we will read back in
3696 * parallel from the Unicode data in `udata' and the
3697 * non-Unicode data in `tdata'. For each character in
3698 * `tdata' which becomes the right thing in `udata' when
3699 * looked up in `unitab', we just copy straight over from
3700 * tdata. For each one that doesn't, we must WCToMB it
3701 * individually and produce a \u escape sequence.
3703 * It would probably be more robust to just bite the bullet
3704 * and WCToMB each individual Unicode character one by one,
3705 * then MBToWC each one back to see if it was an accurate
3706 * translation; but that strikes me as a horrifying number
3707 * of Windows API calls so I want to see if this faster way
3708 * will work. If it screws up badly we can always revert to
3709 * the simple and slow way.
3711 while (tindex < len2 && uindex < len &&
3712 tdata[tindex] && udata[uindex]) {
3713 if (tindex + 1 < len2 &&
3714 tdata[tindex] == '\r' &&
3715 tdata[tindex+1] == '\n') {
3719 if (unitab[tdata[tindex]] == udata[uindex]) {
3725 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
3726 NULL, 0, NULL, NULL);
3727 if (multilen != 1) {
3728 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
3730 alen = 1; strcpy(after, "}");
3732 blen = sprintf(before, "\\u%d", udata[uindex]);
3733 alen = 0; after[0] = '\0';
3736 assert(tindex + multilen <= len2);
3737 totallen = blen + alen;
3738 for (i = 0; i < multilen; i++) {
3739 if (tdata[tindex+i] == '\\' ||
3740 tdata[tindex+i] == '{' ||
3741 tdata[tindex+i] == '}')
3743 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
3744 totallen += 6; /* \par\r\n */
3745 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
3751 if (rtfsize < rtflen + totallen + 3) {
3752 rtfsize = rtflen + totallen + 512;
3753 rtf = srealloc(rtf, rtfsize);
3756 strcpy(rtf + rtflen, before); rtflen += blen;
3757 for (i = 0; i < multilen; i++) {
3758 if (tdata[tindex+i] == '\\' ||
3759 tdata[tindex+i] == '{' ||
3760 tdata[tindex+i] == '}') {
3761 rtf[rtflen++] = '\\';
3762 rtf[rtflen++] = tdata[tindex+i];
3763 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
3764 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
3765 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
3766 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
3768 rtf[rtflen++] = tdata[tindex+i];
3771 strcpy(rtf + rtflen, after); rtflen += alen;
3777 strcpy(rtf + rtflen, "}");
3780 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
3781 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
3783 GlobalUnlock(clipdata3);
3789 GlobalUnlock(clipdata);
3790 GlobalUnlock(clipdata2);
3793 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3795 if (OpenClipboard(hwnd)) {
3797 SetClipboardData(CF_UNICODETEXT, clipdata);
3798 SetClipboardData(CF_TEXT, clipdata2);
3800 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
3803 GlobalFree(clipdata);
3804 GlobalFree(clipdata2);
3808 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3811 void get_clip(wchar_t ** p, int *len)
3813 static HGLOBAL clipdata = NULL;
3814 static wchar_t *converted = 0;
3823 GlobalUnlock(clipdata);
3826 } else if (OpenClipboard(NULL)) {
3827 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3829 *p = GlobalLock(clipdata);
3831 for (p2 = *p; *p2; p2++);
3835 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3839 s = GlobalLock(clipdata);
3840 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3841 *p = converted = smalloc(i * sizeof(wchar_t));
3842 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3855 * Move `lines' lines from position `from' to position `to' in the
3858 void optimised_move(int to, int from, int lines)
3863 min = (to < from ? to : from);
3864 max = to + from - min;
3866 r.left = offset_width;
3867 r.right = offset_width + cols * font_width;
3868 r.top = offset_height + min * font_height;
3869 r.bottom = offset_height + (max + lines) * font_height;
3870 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3875 * Print a message box and perform a fatal exit.
3877 void fatalbox(char *fmt, ...)
3883 vsprintf(stuff, fmt, ap);
3885 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3890 * Manage window caption / taskbar flashing, if enabled.
3891 * 0 = stop, 1 = maintain, 2 = start
3893 static void flash_window(int mode)
3895 static long last_flash = 0;
3896 static int flashing = 0;
3897 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3900 FlashWindow(hwnd, FALSE);
3904 } else if (mode == 2) {
3907 last_flash = GetTickCount();
3909 FlashWindow(hwnd, TRUE);
3912 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3915 long now = GetTickCount();
3916 long fdiff = now - last_flash;
3917 if (fdiff < 0 || fdiff > 450) {
3919 FlashWindow(hwnd, TRUE); /* toggle */
3930 if (mode == BELL_DEFAULT) {
3932 * For MessageBeep style bells, we want to be careful of
3933 * timing, because they don't have the nice property of
3934 * PlaySound bells that each one cancels the previous
3935 * active one. So we limit the rate to one per 50ms or so.
3937 static long lastbeep = 0;
3940 beepdiff = GetTickCount() - lastbeep;
3941 if (beepdiff >= 0 && beepdiff < 50)
3945 * The above MessageBeep call takes time, so we record the
3946 * time _after_ it finishes rather than before it starts.
3948 lastbeep = GetTickCount();
3949 } else if (mode == BELL_WAVEFILE) {
3950 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3951 char buf[sizeof(cfg.bell_wavefile) + 80];
3952 sprintf(buf, "Unable to play sound file\n%s\n"
3953 "Using default sound instead", cfg.bell_wavefile);
3954 MessageBox(hwnd, buf, "PuTTY Sound Error",
3955 MB_OK | MB_ICONEXCLAMATION);
3956 cfg.beep = BELL_DEFAULT;
3959 /* Otherwise, either visual bell or disabled; do nothing here */
3961 flash_window(2); /* start */
3966 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3968 * Revised by <wez@thebrainroom.com>
3970 static void flip_full_screen(void)
3975 wp.length = sizeof(wp);
3976 GetWindowPlacement(hwnd, &wp);
3978 full_screen = !full_screen;
3981 if (wp.showCmd == SW_SHOWMAXIMIZED) {
3982 /* Ooops it was already 'zoomed' we have to unzoom it before
3983 * everything will work right.
3985 wp.showCmd = SW_SHOWNORMAL;
3986 SetWindowPlacement(hwnd, &wp);
3989 style = GetWindowLong(hwnd, GWL_STYLE) & ~WS_CAPTION;
3990 style &= ~WS_VSCROLL;
3991 if (cfg.scrollbar_in_fullscreen)
3992 style |= WS_VSCROLL;
3993 SetWindowLong(hwnd, GWL_STYLE, style);
3995 /* This seems to be needed otherwize explorer doesn't notice
3996 * we want to go fullscreen and it's bar is still visible
3998 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
3999 SWP_NOACTIVATE | SWP_NOCOPYBITS |
4000 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4003 wp.showCmd = SW_SHOWMAXIMIZED;
4004 SetWindowPlacement(hwnd, &wp);
4006 style = GetWindowLong(hwnd, GWL_STYLE) | WS_CAPTION;
4007 style &= ~WS_VSCROLL;
4009 style |= WS_VSCROLL;
4010 SetWindowLong(hwnd, GWL_STYLE, style);
4012 /* Don't need to do a SetWindowPos as the resize will force a
4015 wp.showCmd = SW_SHOWNORMAL;
4016 SetWindowPlacement(hwnd, &wp);
4019 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4020 MF_BYCOMMAND| full_screen ? MF_CHECKED : MF_UNCHECKED);