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
1699 * We get this if the System menu has been activated
1700 * using the keyboard. This might happen from within
1701 * TranslateKey, in which case it really wants to be
1702 * followed by a `space' character to actually _bring
1703 * the menu up_ rather than just sitting there in
1704 * `ready to appear' state.
1706 show_mouseptr(1); /* make sure pointer is visible */
1708 PostMessage(hwnd, WM_CHAR, ' ', 0);
1710 case IDM_FULLSCREEN:
1714 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1715 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1720 #define X_POS(l) ((int)(short)LOWORD(l))
1721 #define Y_POS(l) ((int)(short)HIWORD(l))
1723 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1724 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1725 #define WHEEL_DELTA 120
1728 wheel_accumulator += (short) HIWORD(wParam);
1729 wParam = LOWORD(wParam);
1731 /* process events when the threshold is reached */
1732 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1735 /* reduce amount for next time */
1736 if (wheel_accumulator > 0) {
1738 wheel_accumulator -= WHEEL_DELTA;
1739 } else if (wheel_accumulator < 0) {
1741 wheel_accumulator += WHEEL_DELTA;
1745 if (send_raw_mouse) {
1746 /* send a mouse-down followed by a mouse up */
1750 TO_CHR_X(X_POS(lParam)),
1751 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1752 wParam & MK_CONTROL, is_alt_pressed());
1753 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1754 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1755 wParam & MK_CONTROL, is_alt_pressed());
1757 /* trigger a scroll */
1759 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1764 case WM_LBUTTONDOWN:
1765 case WM_MBUTTONDOWN:
1766 case WM_RBUTTONDOWN:
1774 case WM_LBUTTONDOWN:
1778 case WM_MBUTTONDOWN:
1779 button = MBT_MIDDLE;
1782 case WM_RBUTTONDOWN:
1791 button = MBT_MIDDLE;
1799 button = press = 0; /* shouldn't happen */
1804 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1805 wParam & MK_SHIFT, wParam & MK_CONTROL,
1809 term_mouse(button, MA_RELEASE,
1810 TO_CHR_X(X_POS(lParam)),
1811 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1812 wParam & MK_CONTROL, is_alt_pressed());
1820 * Add the mouse position and message time to the random
1823 noise_ultralight(lParam);
1825 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1827 if (wParam & MK_LBUTTON)
1829 else if (wParam & MK_MBUTTON)
1833 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1834 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1835 wParam & MK_CONTROL, is_alt_pressed());
1838 case WM_NCMOUSEMOVE:
1840 noise_ultralight(lParam);
1842 case WM_IGNORE_CLIP:
1843 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1845 case WM_DESTROYCLIPBOARD:
1848 ignore_clip = FALSE;
1854 hdc = BeginPaint(hwnd, &p);
1856 SelectPalette(hdc, pal, TRUE);
1857 RealizePalette(hdc);
1860 (p.rcPaint.left-offset_width)/font_width,
1861 (p.rcPaint.top-offset_height)/font_height,
1862 (p.rcPaint.right-offset_width-1)/font_width,
1863 (p.rcPaint.bottom-offset_height-1)/font_height);
1866 p.rcPaint.left < offset_width ||
1867 p.rcPaint.top < offset_height ||
1868 p.rcPaint.right >= offset_width + font_width*cols ||
1869 p.rcPaint.bottom>= offset_height + font_height*rows)
1871 HBRUSH fillcolour, oldbrush;
1873 fillcolour = CreateSolidBrush (
1874 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1875 oldbrush = SelectObject(hdc, fillcolour);
1876 edge = CreatePen(PS_SOLID, 0,
1877 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1878 oldpen = SelectObject(hdc, edge);
1880 ExcludeClipRect(hdc,
1881 offset_width, offset_height,
1882 offset_width+font_width*cols,
1883 offset_height+font_height*rows);
1885 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
1886 p.rcPaint.right, p.rcPaint.bottom);
1888 // SelectClipRgn(hdc, NULL);
1890 SelectObject(hdc, oldbrush);
1891 DeleteObject(fillcolour);
1892 SelectObject(hdc, oldpen);
1895 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1896 SelectObject(hdc, GetStockObject(WHITE_PEN));
1902 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1903 * but the only one that's likely to try to overload us is FD_READ.
1904 * This means buffering just one is fine.
1906 if (pending_netevent)
1907 enact_pending_netevent();
1909 pending_netevent = TRUE;
1910 pend_netevent_wParam = wParam;
1911 pend_netevent_lParam = lParam;
1912 if (WSAGETSELECTEVENT(lParam) != FD_READ)
1913 enact_pending_netevent();
1915 time(&last_movement);
1919 CreateCaret(hwnd, caretbm, font_width, font_height);
1921 flash_window(0); /* stop */
1933 case WM_ENTERSIZEMOVE:
1934 #ifdef RDB_DEBUG_PATCH
1935 debug((27, "WM_ENTERSIZEMOVE"));
1939 need_backend_resize = FALSE;
1941 case WM_EXITSIZEMOVE:
1944 #ifdef RDB_DEBUG_PATCH
1945 debug((27, "WM_EXITSIZEMOVE"));
1947 if (need_backend_resize) {
1948 term_size(cfg.height, cfg.width, cfg.savelines);
1949 InvalidateRect(hwnd, NULL, TRUE);
1954 * This does two jobs:
1955 * 1) Keep the sizetip uptodate
1956 * 2) Make sure the window size is _stepped_ in units of the font size.
1958 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
1959 int width, height, w, h, ew, eh;
1960 LPRECT r = (LPRECT) lParam;
1962 if ( !need_backend_resize &&
1963 (cfg.height != rows || cfg.width != cols )) {
1965 * Great! It seems the host has been changing the terminal
1966 * size, well the user is now grabbing so this is probably
1967 * the least confusing solution in the long run even though
1968 * it a is suprise. Unfortunatly the only way to prevent
1969 * this seems to be to let the host change the window size
1970 * and as that's a user option we're still right back here.
1972 term_size(cfg.height, cfg.width, cfg.savelines);
1974 InvalidateRect(hwnd, NULL, TRUE);
1975 need_backend_resize = TRUE;
1978 width = r->right - r->left - extra_width;
1979 height = r->bottom - r->top - extra_height;
1980 w = (width + font_width / 2) / font_width;
1983 h = (height + font_height / 2) / font_height;
1986 UpdateSizeTip(hwnd, w, h);
1987 ew = width - w * font_width;
1988 eh = height - h * font_height;
1990 if (wParam == WMSZ_LEFT ||
1991 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1997 if (wParam == WMSZ_TOP ||
1998 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2008 int width, height, w, h, rv = 0;
2009 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2010 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2011 LPRECT r = (LPRECT) lParam;
2013 width = r->right - r->left - ex_width;
2014 height = r->bottom - r->top - ex_height;
2016 w = (width + cols/2)/cols;
2017 h = (height + rows/2)/rows;
2018 if ( r->right != r->left + w*cols + ex_width)
2021 if (wParam == WMSZ_LEFT ||
2022 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2023 r->left = r->right - w*cols - ex_width;
2025 r->right = r->left + w*cols + ex_width;
2027 if (r->bottom != r->top + h*rows + ex_height)
2030 if (wParam == WMSZ_TOP ||
2031 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2032 r->top = r->bottom - h*rows - ex_height;
2034 r->bottom = r->top + h*rows + ex_height;
2038 /* break; (never reached) */
2040 #ifdef RDB_DEBUG_PATCH
2041 debug((27, "WM_SIZE %s (%d,%d)",
2042 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2043 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2044 (wParam == SIZE_RESTORED && resizing) ? "to":
2045 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2047 LOWORD(lParam), HIWORD(lParam)));
2049 if (wParam == SIZE_MINIMIZED) {
2051 cfg.win_name_always ? window_name : icon_name);
2054 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2055 SetWindowText(hwnd, window_name);
2057 if (cfg.resize_action == RESIZE_DISABLED) {
2058 /* A resize, well it better be a minimize. */
2062 int width, height, w, h;
2064 width = LOWORD(lParam);
2065 height = HIWORD(lParam);
2068 if (wParam == SIZE_MAXIMIZED) {
2072 if (cfg.resize_action != RESIZE_FONT) {
2073 w = width / font_width;
2075 h = height / font_height;
2078 term_size(h, w, cfg.savelines);
2081 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2083 if (cfg.resize_action != RESIZE_FONT)
2084 term_size(prev_rows, prev_cols, cfg.savelines);
2087 /* This is an unexpected resize, these will normally happen
2088 * if the window is too large. Probably either the user
2089 * selected a huge font or the screen size has changed.
2091 * This is also called with minimize.
2093 else reset_window(-1);
2097 * Don't call back->size in mid-resize. (To prevent
2098 * massive numbers of resize events getting sent
2099 * down the connection during an NT opaque drag.)
2102 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
2103 need_backend_resize = TRUE;
2104 w = (width-cfg.window_border*2) / font_width;
2106 h = (height-cfg.window_border*2) / font_height;
2117 switch (LOWORD(wParam)) {
2131 term_scroll(0, +rows / 2);
2134 term_scroll(0, -rows / 2);
2136 case SB_THUMBPOSITION:
2138 term_scroll(1, HIWORD(wParam));
2142 case WM_PALETTECHANGED:
2143 if ((HWND) wParam != hwnd && pal != NULL) {
2144 HDC hdc = get_ctx();
2146 if (RealizePalette(hdc) > 0)
2152 case WM_QUERYNEWPALETTE:
2154 HDC hdc = get_ctx();
2156 if (RealizePalette(hdc) > 0)
2168 * Add the scan code and keypress timing to the random
2171 noise_ultralight(lParam);
2174 * We don't do TranslateMessage since it disassociates the
2175 * resulting CHAR message from the KEYDOWN that sparked it,
2176 * which we occasionally don't want. Instead, we process
2177 * KEYDOWN, and call the Win32 translator functions so that
2178 * we get the translations under _our_ control.
2181 unsigned char buf[20];
2184 if (wParam == VK_PROCESSKEY) {
2187 m.message = WM_KEYDOWN;
2189 m.lParam = lParam & 0xdfff;
2190 TranslateMessage(&m);
2192 len = TranslateKey(message, wParam, lParam, buf);
2194 return DefWindowProc(hwnd, message, wParam, lParam);
2198 * Interrupt an ongoing paste. I'm not sure
2199 * this is sensible, but for the moment it's
2200 * preferable to having to faff about buffering
2206 * We need not bother about stdin backlogs
2207 * here, because in GUI PuTTY we can't do
2208 * anything about it anyway; there's no means
2209 * of asking Windows to hold off on KEYDOWN
2210 * messages. We _have_ to buffer everything
2213 ldisc_send(buf, len, 1);
2218 net_pending_errors();
2220 case WM_INPUTLANGCHANGE:
2222 /* wParam == Font number */
2223 /* lParam == Locale */
2225 HKL NewInputLocale = (HKL) lParam;
2227 // lParam == GetKeyboardLayout(0);
2229 GetLocaleInfo(LOWORD(NewInputLocale),
2230 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
2232 kbd_codepage = atoi(lbuf);
2235 case WM_IME_COMPOSITION:
2241 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2242 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2244 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2245 break; /* fall back to DefWindowProc */
2247 hIMC = ImmGetContext(hwnd);
2248 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2251 buff = (char*) smalloc(n);
2252 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2253 luni_send((unsigned short *)buff, n / 2, 1);
2256 ImmReleaseContext(hwnd, hIMC);
2261 if (wParam & 0xFF00) {
2262 unsigned char buf[2];
2265 buf[0] = wParam >> 8;
2266 lpage_send(kbd_codepage, buf, 2, 1);
2268 char c = (unsigned char) wParam;
2269 lpage_send(kbd_codepage, &c, 1, 1);
2275 * Nevertheless, we are prepared to deal with WM_CHAR
2276 * messages, should they crop up. So if someone wants to
2277 * post the things to us as part of a macro manoeuvre,
2278 * we're ready to cope.
2281 char c = (unsigned char)wParam;
2282 lpage_send(CP_ACP, &c, 1, 1);
2286 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2287 SetCursor(LoadCursor(NULL, IDC_ARROW));
2292 return DefWindowProc(hwnd, message, wParam, lParam);
2296 * Move the system caret. (We maintain one, even though it's
2297 * invisible, for the benefit of blind people: apparently some
2298 * helper software tracks the system caret, so we should arrange to
2301 void sys_cursor(int x, int y)
2306 if (!has_focus) return;
2308 SetCaretPos(x * font_width + offset_width,
2309 y * font_height + offset_height);
2311 /* IMM calls on Win98 and beyond only */
2312 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2314 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2315 osVersion.dwMinorVersion == 0) return; /* 95 */
2317 /* we should have the IMM functions */
2318 hIMC = ImmGetContext(hwnd);
2319 cf.dwStyle = CFS_POINT;
2320 cf.ptCurrentPos.x = x * font_width;
2321 cf.ptCurrentPos.y = y * font_height;
2322 ImmSetCompositionWindow(hIMC, &cf);
2324 ImmReleaseContext(hwnd, hIMC);
2328 * Draw a line of text in the window, at given character
2329 * coordinates, in given attributes.
2331 * We are allowed to fiddle with the contents of `text'.
2333 void do_text(Context ctx, int x, int y, char *text, int len,
2334 unsigned long attr, int lattr)
2337 int nfg, nbg, nfont;
2340 int force_manual_underline = 0;
2341 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2342 int char_width = fnt_width;
2343 int text_adjust = 0;
2344 static int *IpDx = 0, IpDxLEN = 0;
2346 if (attr & ATTR_WIDE)
2349 if (len > IpDxLEN || IpDx[0] != char_width) {
2351 if (len > IpDxLEN) {
2353 IpDx = smalloc((len + 16) * sizeof(int));
2354 IpDxLEN = (len + 16);
2356 for (i = 0; i < IpDxLEN; i++)
2357 IpDx[i] = char_width;
2360 /* Only want the left half of double width lines */
2361 if (lattr != LATTR_NORM && x*2 >= cols)
2369 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2370 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2371 attr ^= ATTR_CUR_XOR;
2375 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2376 /* Assume a poorman font is borken in other ways too. */
2386 nfont |= FONT_WIDE + FONT_HIGH;
2389 if (attr & ATTR_NARROW)
2390 nfont |= FONT_NARROW;
2392 /* Special hack for the VT100 linedraw glyphs. */
2393 if ((attr & CSET_MASK) == 0x2300) {
2394 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2395 switch ((unsigned char) (text[0])) {
2397 text_adjust = -2 * font_height / 5;
2400 text_adjust = -1 * font_height / 5;
2403 text_adjust = font_height / 5;
2406 text_adjust = 2 * font_height / 5;
2409 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2412 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2413 attr |= (unitab_xterm['q'] & CSET_MASK);
2414 if (attr & ATTR_UNDER) {
2415 attr &= ~ATTR_UNDER;
2416 force_manual_underline = 1;
2421 /* Anything left as an original character set is unprintable. */
2422 if (DIRECT_CHAR(attr)) {
2425 memset(text, 0xFD, len);
2429 if ((attr & CSET_MASK) == ATTR_OEMCP)
2432 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2433 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2434 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2436 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2437 nfont |= FONT_UNDERLINE;
2438 another_font(nfont);
2439 if (!fonts[nfont]) {
2440 if (nfont & FONT_UNDERLINE)
2441 force_manual_underline = 1;
2442 /* Don't do the same for manual bold, it could be bad news. */
2444 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2446 another_font(nfont);
2448 nfont = FONT_NORMAL;
2449 if (attr & ATTR_REVERSE) {
2454 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2456 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2460 SelectObject(hdc, fonts[nfont]);
2461 SetTextColor(hdc, fg);
2462 SetBkColor(hdc, bg);
2463 SetBkMode(hdc, OPAQUE);
2466 line_box.right = x + char_width * len;
2467 line_box.bottom = y + font_height;
2469 /* Only want the left half of double width lines */
2470 if (line_box.right > font_width*cols+offset_width)
2471 line_box.right = font_width*cols+offset_width;
2473 /* We're using a private area for direct to font. (512 chars.) */
2474 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2475 /* Ho Hum, dbcs fonts are a PITA! */
2476 /* To display on W9x I have to convert to UCS */
2477 static wchar_t *uni_buf = 0;
2478 static int uni_len = 0;
2480 if (len > uni_len) {
2482 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2485 for(nlen = mptr = 0; mptr<len; mptr++) {
2486 uni_buf[nlen] = 0xFFFD;
2487 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2488 IpDx[nlen] += char_width;
2489 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2490 text+mptr, 2, uni_buf+nlen, 1);
2495 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2496 text+mptr, 1, uni_buf+nlen, 1);
2504 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2505 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2506 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2507 SetBkMode(hdc, TRANSPARENT);
2508 ExtTextOutW(hdc, x - 1,
2509 y - font_height * (lattr ==
2510 LATTR_BOT) + text_adjust,
2511 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2515 } else if (DIRECT_FONT(attr)) {
2517 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2518 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2519 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2520 SetBkMode(hdc, TRANSPARENT);
2522 /* GRR: This draws the character outside it's box and can leave
2523 * 'droppings' even with the clip box! I suppose I could loop it
2524 * one character at a time ... yuk.
2526 * Or ... I could do a test print with "W", and use +1 or -1 for this
2527 * shift depending on if the leftmost column is blank...
2529 ExtTextOut(hdc, x - 1,
2530 y - font_height * (lattr ==
2531 LATTR_BOT) + text_adjust,
2532 ETO_CLIPPED, &line_box, text, len, IpDx);
2535 /* And 'normal' unicode characters */
2536 static WCHAR *wbuf = NULL;
2537 static int wlen = 0;
2542 wbuf = smalloc(wlen * sizeof(WCHAR));
2544 for (i = 0; i < len; i++)
2545 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2548 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2549 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2551 /* And the shadow bold hack. */
2552 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2553 SetBkMode(hdc, TRANSPARENT);
2554 ExtTextOutW(hdc, x - 1,
2555 y - font_height * (lattr ==
2556 LATTR_BOT) + text_adjust,
2557 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2560 if (lattr != LATTR_TOP && (force_manual_underline ||
2561 (und_mode == UND_LINE
2562 && (attr & ATTR_UNDER)))) {
2565 if (lattr == LATTR_BOT)
2566 dec = dec * 2 - font_height;
2568 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2569 MoveToEx(hdc, x, y + dec, NULL);
2570 LineTo(hdc, x + len * char_width, y + dec);
2571 oldpen = SelectObject(hdc, oldpen);
2572 DeleteObject(oldpen);
2576 void do_cursor(Context ctx, int x, int y, char *text, int len,
2577 unsigned long attr, int lattr)
2583 int ctype = cfg.cursor_type;
2585 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2586 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2587 do_text(ctx, x, y, text, len, attr, lattr);
2591 attr |= TATTR_RIGHTCURS;
2594 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2595 if (attr & ATTR_WIDE)
2602 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2605 pts[0].x = pts[1].x = pts[4].x = x;
2606 pts[2].x = pts[3].x = x + char_width - 1;
2607 pts[0].y = pts[3].y = pts[4].y = y;
2608 pts[1].y = pts[2].y = y + font_height - 1;
2609 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2610 Polyline(hdc, pts, 5);
2611 oldpen = SelectObject(hdc, oldpen);
2612 DeleteObject(oldpen);
2613 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2614 int startx, starty, dx, dy, length, i;
2617 starty = y + descent;
2620 length = char_width;
2623 if (attr & TATTR_RIGHTCURS)
2624 xadjust = char_width - 1;
2625 startx = x + xadjust;
2629 length = font_height;
2631 if (attr & TATTR_ACTCURS) {
2634 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2635 MoveToEx(hdc, startx, starty, NULL);
2636 LineTo(hdc, startx + dx * length, starty + dy * length);
2637 oldpen = SelectObject(hdc, oldpen);
2638 DeleteObject(oldpen);
2640 for (i = 0; i < length; i++) {
2642 SetPixel(hdc, startx, starty, colours[23]);
2651 /* This function gets the actual width of a character in the normal font.
2653 int CharWidth(Context ctx, int uc) {
2657 /* If the font max is the same as the font ave width then this
2658 * function is a no-op.
2660 if (!font_dualwidth) return 1;
2662 switch (uc & CSET_MASK) {
2664 uc = unitab_line[uc & 0xFF];
2667 uc = unitab_xterm[uc & 0xFF];
2670 uc = unitab_scoacs[uc & 0xFF];
2673 if (DIRECT_FONT(uc)) {
2674 if (dbcs_screenfont) return 1;
2676 /* Speedup, I know of no font where ascii is the wrong width */
2677 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2680 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2681 SelectObject(hdc, fonts[FONT_NORMAL]);
2682 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2683 another_font(FONT_OEM);
2684 if (!fonts[FONT_OEM]) return 0;
2686 SelectObject(hdc, fonts[FONT_OEM]);
2690 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2691 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2694 /* Speedup, I know of no font where ascii is the wrong width */
2695 if (uc >= ' ' && uc <= '~') return 1;
2697 SelectObject(hdc, fonts[FONT_NORMAL]);
2698 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2699 /* Okay that one worked */ ;
2700 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2701 /* This should work on 9x too, but it's "less accurate" */ ;
2706 ibuf += font_width / 2 -1;
2713 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2714 * codes. Returns number of bytes used or zero to drop the message
2715 * or -1 to forward the message to windows.
2717 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2718 unsigned char *output)
2721 int scan, left_alt = 0, key_down, shift_state;
2723 unsigned char *p = output;
2724 static int alt_sum = 0;
2726 HKL kbd_layout = GetKeyboardLayout(0);
2728 static WORD keys[3];
2729 static int compose_char = 0;
2730 static WPARAM compose_key = 0;
2732 r = GetKeyboardState(keystate);
2734 memset(keystate, 0, sizeof(keystate));
2737 #define SHOW_TOASCII_RESULT
2738 { /* Tell us all about key events */
2739 static BYTE oldstate[256];
2740 static int first = 1;
2744 memcpy(oldstate, keystate, sizeof(oldstate));
2747 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2749 } else if ((HIWORD(lParam) & KF_UP)
2750 && scan == (HIWORD(lParam) & 0xFF)) {
2754 if (wParam >= VK_F1 && wParam <= VK_F20)
2755 debug(("K_F%d", wParam + 1 - VK_F1));
2768 debug(("VK_%02x", wParam));
2770 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2772 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2774 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2775 if (ch >= ' ' && ch <= '~')
2776 debug((", '%c'", ch));
2778 debug((", $%02x", ch));
2781 debug((", KB0=%02x", keys[0]));
2783 debug((", KB1=%02x", keys[1]));
2785 debug((", KB2=%02x", keys[2]));
2787 if ((keystate[VK_SHIFT] & 0x80) != 0)
2789 if ((keystate[VK_CONTROL] & 0x80) != 0)
2791 if ((HIWORD(lParam) & KF_EXTENDED))
2793 if ((HIWORD(lParam) & KF_UP))
2797 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2798 else if ((HIWORD(lParam) & KF_UP))
2799 oldstate[wParam & 0xFF] ^= 0x80;
2801 oldstate[wParam & 0xFF] ^= 0x81;
2803 for (ch = 0; ch < 256; ch++)
2804 if (oldstate[ch] != keystate[ch])
2805 debug((", M%02x=%02x", ch, keystate[ch]));
2807 memcpy(oldstate, keystate, sizeof(oldstate));
2811 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2812 keystate[VK_RMENU] = keystate[VK_MENU];
2816 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2817 if ((cfg.funky_type == 3 ||
2818 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2819 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2821 wParam = VK_EXECUTE;
2823 /* UnToggle NUMLock */
2824 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2825 keystate[VK_NUMLOCK] ^= 1;
2828 /* And write back the 'adjusted' state */
2829 SetKeyboardState(keystate);
2832 /* Disable Auto repeat if required */
2833 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2836 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2839 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2841 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2842 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2843 if (cfg.ctrlaltkeys)
2844 keystate[VK_MENU] = 0;
2846 keystate[VK_RMENU] = 0x80;
2851 alt_pressed = (left_alt && key_down);
2853 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2854 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2855 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2857 /* Note if AltGr was pressed and if it was used as a compose key */
2858 if (!compose_state) {
2859 compose_key = 0x100;
2860 if (cfg.compose_key) {
2861 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2862 compose_key = wParam;
2864 if (wParam == VK_APPS)
2865 compose_key = wParam;
2868 if (wParam == compose_key) {
2869 if (compose_state == 0
2870 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2872 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2876 } else if (compose_state == 1 && wParam != VK_CONTROL)
2880 * Record that we pressed key so the scroll window can be reset, but
2881 * be careful to avoid Shift-UP/Down
2883 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
2884 wParam != VK_MENU && wParam != VK_CONTROL) {
2888 if (compose_state > 1 && left_alt)
2891 /* Sanitize the number pad if not using a PC NumPad */
2892 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2893 && cfg.funky_type != 2)
2894 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2895 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2899 nParam = VK_NUMPAD0;
2902 nParam = VK_NUMPAD1;
2905 nParam = VK_NUMPAD2;
2908 nParam = VK_NUMPAD3;
2911 nParam = VK_NUMPAD4;
2914 nParam = VK_NUMPAD5;
2917 nParam = VK_NUMPAD6;
2920 nParam = VK_NUMPAD7;
2923 nParam = VK_NUMPAD8;
2926 nParam = VK_NUMPAD9;
2929 nParam = VK_DECIMAL;
2933 if (keystate[VK_NUMLOCK] & 1)
2940 /* If a key is pressed and AltGr is not active */
2941 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2942 /* Okay, prepare for most alts then ... */
2946 /* Lets see if it's a pattern we know all about ... */
2947 if (wParam == VK_PRIOR && shift_state == 1) {
2948 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2951 if (wParam == VK_NEXT && shift_state == 1) {
2952 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2955 if (wParam == VK_INSERT && shift_state == 1) {
2959 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2962 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2963 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2966 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter) {
2970 /* Control-Numlock for app-keypad mode switch */
2971 if (wParam == VK_PAUSE && shift_state == 2) {
2972 app_keypad_keys ^= 1;
2976 /* Nethack keypad */
2977 if (cfg.nethack_keypad && !left_alt) {
2980 *p++ = shift_state ? 'B' : 'b';
2983 *p++ = shift_state ? 'J' : 'j';
2986 *p++ = shift_state ? 'N' : 'n';
2989 *p++ = shift_state ? 'H' : 'h';
2992 *p++ = shift_state ? '.' : '.';
2995 *p++ = shift_state ? 'L' : 'l';
2998 *p++ = shift_state ? 'Y' : 'y';
3001 *p++ = shift_state ? 'K' : 'k';
3004 *p++ = shift_state ? 'U' : 'u';
3009 /* Application Keypad */
3013 if (cfg.funky_type == 3 ||
3014 (cfg.funky_type <= 1 &&
3015 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3029 if (app_keypad_keys && !cfg.no_applic_k)
3066 if (cfg.funky_type == 2) {
3071 } else if (shift_state)
3078 if (cfg.funky_type == 2)
3082 if (cfg.funky_type == 2)
3086 if (cfg.funky_type == 2)
3091 if (HIWORD(lParam) & KF_EXTENDED)
3097 if (xkey >= 'P' && xkey <= 'S')
3098 p += sprintf((char *) p, "\x1B%c", xkey);
3100 p += sprintf((char *) p, "\x1B?%c", xkey);
3102 p += sprintf((char *) p, "\x1BO%c", xkey);
3107 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
3108 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3112 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3118 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3122 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3126 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3131 if (wParam == VK_PAUSE) { /* Break/Pause */
3136 /* Control-2 to Control-8 are special */
3137 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3138 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3141 if (shift_state == 2 && wParam == 0xBD) {
3145 if (shift_state == 2 && wParam == 0xDF) {
3149 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3156 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3157 * for integer decimal nn.)
3159 * We also deal with the weird ones here. Linux VCs replace F1
3160 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3161 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3167 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3170 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3173 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3176 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3179 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3182 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3185 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3188 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3191 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3194 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3227 if ((shift_state&2) == 0) switch (wParam) {
3247 /* Reorder edit keys to physical order */
3248 if (cfg.funky_type == 3 && code <= 6)
3249 code = "\0\2\1\4\5\3\6"[code];
3251 if (vt52_mode && code > 0 && code <= 6) {
3252 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3256 if (cfg.funky_type == 5 && /* SCO function keys */
3257 code >= 11 && code <= 34) {
3258 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3261 case VK_F1: index = 0; break;
3262 case VK_F2: index = 1; break;
3263 case VK_F3: index = 2; break;
3264 case VK_F4: index = 3; break;
3265 case VK_F5: index = 4; break;
3266 case VK_F6: index = 5; break;
3267 case VK_F7: index = 6; break;
3268 case VK_F8: index = 7; break;
3269 case VK_F9: index = 8; break;
3270 case VK_F10: index = 9; break;
3271 case VK_F11: index = 10; break;
3272 case VK_F12: index = 11; break;
3274 if (keystate[VK_SHIFT] & 0x80) index += 12;
3275 if (keystate[VK_CONTROL] & 0x80) index += 24;
3276 p += sprintf((char *) p, "\x1B[%c", codes[index]);
3279 if (cfg.funky_type == 5 && /* SCO small keypad */
3280 code >= 1 && code <= 6) {
3281 char codes[] = "HL.FIG";
3285 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3289 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3296 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3299 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3302 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3303 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3306 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3308 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3310 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3313 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3314 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3318 p += sprintf((char *) p, "\x1B[%d~", code);
3323 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3324 * some reason seems to send VK_CLEAR to Windows...).
3347 p += sprintf((char *) p, "\x1B%c", xkey);
3349 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3350 /* VT100 & VT102 manuals both state the app cursor keys
3351 * only work if the app keypad is on.
3353 if (!app_keypad_keys)
3355 /* Useful mapping of Ctrl-arrows */
3356 if (shift_state == 2)
3360 p += sprintf((char *) p, "\x1BO%c", xkey);
3362 p += sprintf((char *) p, "\x1B[%c", xkey);
3369 * Finally, deal with Return ourselves. (Win95 seems to
3370 * foul it up when Alt is pressed, for some reason.)
3372 if (wParam == VK_RETURN) { /* Return */
3378 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3379 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3384 /* Okay we've done everything interesting; let windows deal with
3385 * the boring stuff */
3389 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3390 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3392 keystate[VK_CAPITAL] = 0;
3395 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3396 #ifdef SHOW_TOASCII_RESULT
3397 if (r == 1 && !key_down) {
3399 if (in_utf || dbcs_screenfont)
3400 debug((", (U+%04x)", alt_sum));
3402 debug((", LCH(%d)", alt_sum));
3404 debug((", ACH(%d)", keys[0]));
3409 for (r1 = 0; r1 < r; r1++) {
3410 debug(("%s%d", r1 ? "," : "", keys[r1]));
3419 * Interrupt an ongoing paste. I'm not sure this is
3420 * sensible, but for the moment it's preferable to
3421 * having to faff about buffering things.
3426 for (i = 0; i < r; i++) {
3427 unsigned char ch = (unsigned char) keys[i];
3429 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3434 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3438 if ((nc = check_compose(compose_char, ch)) == -1) {
3439 MessageBeep(MB_ICONHAND);
3443 luni_send(&keybuf, 1, 1);
3451 if (in_utf || dbcs_screenfont) {
3453 luni_send(&keybuf, 1, 1);
3455 ch = (char) alt_sum;
3457 * We need not bother about stdin
3458 * backlogs here, because in GUI PuTTY
3459 * we can't do anything about it
3460 * anyway; there's no means of asking
3461 * Windows to hold off on KEYDOWN
3462 * messages. We _have_ to buffer
3463 * everything we're sent.
3465 ldisc_send(&ch, 1, 1);
3469 lpage_send(kbd_codepage, &ch, 1, 1);
3471 if(capsOn && ch < 0x80) {
3474 cbuf[1] = xlat_uskbd2cyrllic(ch);
3475 luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3480 lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3486 /* This is so the ALT-Numpad and dead keys work correctly. */
3491 /* If we're definitly not building up an ALT-54321 then clear it */
3494 /* If we will be using alt_sum fix the 256s */
3495 else if (keys[0] && (in_utf || dbcs_screenfont))
3500 * ALT alone may or may not want to bring up the System menu.
3501 * If it's not meant to, we return 0 on presses or releases of
3502 * ALT, to show that we've swallowed the keystroke. Otherwise
3503 * we return -1, which means Windows will give the keystroke
3504 * its default handling (i.e. bring up the System menu).
3506 if (wParam == VK_MENU && !cfg.alt_only)
3512 void set_title(char *title)
3515 window_name = smalloc(1 + strlen(title));
3516 strcpy(window_name, title);
3517 if (cfg.win_name_always || !IsIconic(hwnd))
3518 SetWindowText(hwnd, title);
3521 void set_icon(char *title)
3524 icon_name = smalloc(1 + strlen(title));
3525 strcpy(icon_name, title);
3526 if (!cfg.win_name_always && IsIconic(hwnd))
3527 SetWindowText(hwnd, title);
3530 void set_sbar(int total, int start, int page)
3537 si.cbSize = sizeof(si);
3538 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3540 si.nMax = total - 1;
3544 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3547 Context get_ctx(void)
3553 SelectPalette(hdc, pal, FALSE);
3559 void free_ctx(Context ctx)
3561 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3562 ReleaseDC(hwnd, ctx);
3565 static void real_palette_set(int n, int r, int g, int b)
3568 logpal->palPalEntry[n].peRed = r;
3569 logpal->palPalEntry[n].peGreen = g;
3570 logpal->palPalEntry[n].peBlue = b;
3571 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3572 colours[n] = PALETTERGB(r, g, b);
3573 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3575 colours[n] = RGB(r, g, b);
3578 void palette_set(int n, int r, int g, int b)
3580 static const int first[21] = {
3581 0, 2, 4, 6, 8, 10, 12, 14,
3582 1, 3, 5, 7, 9, 11, 13, 15,
3585 real_palette_set(first[n], r, g, b);
3587 real_palette_set(first[n] + 1, r, g, b);
3589 HDC hdc = get_ctx();
3590 UnrealizeObject(pal);
3591 RealizePalette(hdc);
3596 void palette_reset(void)
3600 for (i = 0; i < NCOLOURS; i++) {
3602 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3603 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3604 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3605 logpal->palPalEntry[i].peFlags = 0;
3606 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3607 defpal[i].rgbtGreen,
3608 defpal[i].rgbtBlue);
3610 colours[i] = RGB(defpal[i].rgbtRed,
3611 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3616 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3618 RealizePalette(hdc);
3623 void write_aclip(char *data, int len, int must_deselect)
3628 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3631 lock = GlobalLock(clipdata);
3634 memcpy(lock, data, len);
3635 ((unsigned char *) lock)[len] = 0;
3636 GlobalUnlock(clipdata);
3639 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3641 if (OpenClipboard(hwnd)) {
3643 SetClipboardData(CF_TEXT, clipdata);
3646 GlobalFree(clipdata);
3649 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3653 * Note: unlike write_aclip() this will not append a nul.
3655 void write_clip(wchar_t * data, int len, int must_deselect)
3657 HGLOBAL clipdata, clipdata2, clipdata3;
3659 void *lock, *lock2, *lock3;
3661 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3663 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3664 len * sizeof(wchar_t));
3665 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3667 if (!clipdata || !clipdata2 || !clipdata3) {
3669 GlobalFree(clipdata);
3671 GlobalFree(clipdata2);
3673 GlobalFree(clipdata3);
3676 if (!(lock = GlobalLock(clipdata)))
3678 if (!(lock2 = GlobalLock(clipdata2)))
3681 memcpy(lock, data, len * sizeof(wchar_t));
3682 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3684 if (cfg.rtf_paste) {
3685 wchar_t unitab[256];
3687 unsigned char *tdata = (unsigned char *)lock2;
3688 wchar_t *udata = (wchar_t *)lock;
3689 int rtflen = 0, uindex = 0, tindex = 0;
3691 int multilen, blen, alen, totallen, i;
3692 char before[16], after[4];
3694 get_unitab(CP_ACP, unitab, 0);
3696 rtfsize = 100 + strlen(cfg.font);
3697 rtf = smalloc(rtfsize);
3698 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3699 GetACP(), cfg.font);
3700 rtflen = strlen(rtf);
3703 * We want to construct a piece of RTF that specifies the
3704 * same Unicode text. To do this we will read back in
3705 * parallel from the Unicode data in `udata' and the
3706 * non-Unicode data in `tdata'. For each character in
3707 * `tdata' which becomes the right thing in `udata' when
3708 * looked up in `unitab', we just copy straight over from
3709 * tdata. For each one that doesn't, we must WCToMB it
3710 * individually and produce a \u escape sequence.
3712 * It would probably be more robust to just bite the bullet
3713 * and WCToMB each individual Unicode character one by one,
3714 * then MBToWC each one back to see if it was an accurate
3715 * translation; but that strikes me as a horrifying number
3716 * of Windows API calls so I want to see if this faster way
3717 * will work. If it screws up badly we can always revert to
3718 * the simple and slow way.
3720 while (tindex < len2 && uindex < len &&
3721 tdata[tindex] && udata[uindex]) {
3722 if (tindex + 1 < len2 &&
3723 tdata[tindex] == '\r' &&
3724 tdata[tindex+1] == '\n') {
3728 if (unitab[tdata[tindex]] == udata[uindex]) {
3734 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
3735 NULL, 0, NULL, NULL);
3736 if (multilen != 1) {
3737 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
3739 alen = 1; strcpy(after, "}");
3741 blen = sprintf(before, "\\u%d", udata[uindex]);
3742 alen = 0; after[0] = '\0';
3745 assert(tindex + multilen <= len2);
3746 totallen = blen + alen;
3747 for (i = 0; i < multilen; i++) {
3748 if (tdata[tindex+i] == '\\' ||
3749 tdata[tindex+i] == '{' ||
3750 tdata[tindex+i] == '}')
3752 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
3753 totallen += 6; /* \par\r\n */
3754 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
3760 if (rtfsize < rtflen + totallen + 3) {
3761 rtfsize = rtflen + totallen + 512;
3762 rtf = srealloc(rtf, rtfsize);
3765 strcpy(rtf + rtflen, before); rtflen += blen;
3766 for (i = 0; i < multilen; i++) {
3767 if (tdata[tindex+i] == '\\' ||
3768 tdata[tindex+i] == '{' ||
3769 tdata[tindex+i] == '}') {
3770 rtf[rtflen++] = '\\';
3771 rtf[rtflen++] = tdata[tindex+i];
3772 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
3773 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
3774 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
3775 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
3777 rtf[rtflen++] = tdata[tindex+i];
3780 strcpy(rtf + rtflen, after); rtflen += alen;
3786 strcpy(rtf + rtflen, "}");
3789 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
3790 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
3792 GlobalUnlock(clipdata3);
3798 GlobalUnlock(clipdata);
3799 GlobalUnlock(clipdata2);
3802 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3804 if (OpenClipboard(hwnd)) {
3806 SetClipboardData(CF_UNICODETEXT, clipdata);
3807 SetClipboardData(CF_TEXT, clipdata2);
3809 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
3812 GlobalFree(clipdata);
3813 GlobalFree(clipdata2);
3817 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3820 void get_clip(wchar_t ** p, int *len)
3822 static HGLOBAL clipdata = NULL;
3823 static wchar_t *converted = 0;
3832 GlobalUnlock(clipdata);
3835 } else if (OpenClipboard(NULL)) {
3836 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3838 *p = GlobalLock(clipdata);
3840 for (p2 = *p; *p2; p2++);
3844 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3848 s = GlobalLock(clipdata);
3849 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3850 *p = converted = smalloc(i * sizeof(wchar_t));
3851 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3864 * Move `lines' lines from position `from' to position `to' in the
3867 void optimised_move(int to, int from, int lines)
3872 min = (to < from ? to : from);
3873 max = to + from - min;
3875 r.left = offset_width;
3876 r.right = offset_width + cols * font_width;
3877 r.top = offset_height + min * font_height;
3878 r.bottom = offset_height + (max + lines) * font_height;
3879 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3884 * Print a message box and perform a fatal exit.
3886 void fatalbox(char *fmt, ...)
3892 vsprintf(stuff, fmt, ap);
3894 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3899 * Manage window caption / taskbar flashing, if enabled.
3900 * 0 = stop, 1 = maintain, 2 = start
3902 static void flash_window(int mode)
3904 static long last_flash = 0;
3905 static int flashing = 0;
3906 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3909 FlashWindow(hwnd, FALSE);
3913 } else if (mode == 2) {
3916 last_flash = GetTickCount();
3918 FlashWindow(hwnd, TRUE);
3921 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3924 long now = GetTickCount();
3925 long fdiff = now - last_flash;
3926 if (fdiff < 0 || fdiff > 450) {
3928 FlashWindow(hwnd, TRUE); /* toggle */
3939 if (mode == BELL_DEFAULT) {
3941 * For MessageBeep style bells, we want to be careful of
3942 * timing, because they don't have the nice property of
3943 * PlaySound bells that each one cancels the previous
3944 * active one. So we limit the rate to one per 50ms or so.
3946 static long lastbeep = 0;
3949 beepdiff = GetTickCount() - lastbeep;
3950 if (beepdiff >= 0 && beepdiff < 50)
3954 * The above MessageBeep call takes time, so we record the
3955 * time _after_ it finishes rather than before it starts.
3957 lastbeep = GetTickCount();
3958 } else if (mode == BELL_WAVEFILE) {
3959 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3960 char buf[sizeof(cfg.bell_wavefile) + 80];
3961 sprintf(buf, "Unable to play sound file\n%s\n"
3962 "Using default sound instead", cfg.bell_wavefile);
3963 MessageBox(hwnd, buf, "PuTTY Sound Error",
3964 MB_OK | MB_ICONEXCLAMATION);
3965 cfg.beep = BELL_DEFAULT;
3968 /* Otherwise, either visual bell or disabled; do nothing here */
3970 flash_window(2); /* start */
3975 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3977 * Revised by <wez@thebrainroom.com>
3979 static void flip_full_screen(void)
3984 wp.length = sizeof(wp);
3985 GetWindowPlacement(hwnd, &wp);
3987 full_screen = !full_screen;
3990 if (wp.showCmd == SW_SHOWMAXIMIZED) {
3991 /* Ooops it was already 'zoomed' we have to unzoom it before
3992 * everything will work right.
3994 wp.showCmd = SW_SHOWNORMAL;
3995 SetWindowPlacement(hwnd, &wp);
3998 style = GetWindowLong(hwnd, GWL_STYLE) & ~WS_CAPTION;
3999 style &= ~WS_VSCROLL;
4000 if (cfg.scrollbar_in_fullscreen)
4001 style |= WS_VSCROLL;
4002 SetWindowLong(hwnd, GWL_STYLE, style);
4004 /* This seems to be needed otherwize explorer doesn't notice
4005 * we want to go fullscreen and it's bar is still visible
4007 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4008 SWP_NOACTIVATE | SWP_NOCOPYBITS |
4009 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4012 wp.showCmd = SW_SHOWMAXIMIZED;
4013 SetWindowPlacement(hwnd, &wp);
4015 style = GetWindowLong(hwnd, GWL_STYLE) | WS_CAPTION;
4016 style &= ~WS_VSCROLL;
4018 style |= WS_VSCROLL;
4019 SetWindowLong(hwnd, GWL_STYLE, style);
4021 /* Don't need to do a SetWindowPos as the resize will force a
4024 wp.showCmd = SW_SHOWNORMAL;
4025 SetWindowPlacement(hwnd, &wp);
4028 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4029 MF_BYCOMMAND| full_screen ? MF_CHECKED : MF_UNCHECKED);