13 #if (WINVER < 0x0500) && !defined(NO_MULTIMON)
14 #define COMPILE_MULTIMON_STUBS
23 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
29 #define IDM_SHOWLOG 0x0010
30 #define IDM_NEWSESS 0x0020
31 #define IDM_DUPSESS 0x0030
32 #define IDM_RECONF 0x0040
33 #define IDM_CLRSB 0x0050
34 #define IDM_RESET 0x0060
35 #define IDM_TEL_AYT 0x0070
36 #define IDM_TEL_BRK 0x0080
37 #define IDM_TEL_SYNCH 0x0090
38 #define IDM_TEL_EC 0x00a0
39 #define IDM_TEL_EL 0x00b0
40 #define IDM_TEL_GA 0x00c0
41 #define IDM_TEL_NOP 0x00d0
42 #define IDM_TEL_ABORT 0x00e0
43 #define IDM_TEL_AO 0x00f0
44 #define IDM_TEL_IP 0x0100
45 #define IDM_TEL_SUSP 0x0110
46 #define IDM_TEL_EOR 0x0120
47 #define IDM_TEL_EOF 0x0130
48 #define IDM_ABOUT 0x0140
49 #define IDM_SAVEDSESS 0x0150
50 #define IDM_COPYALL 0x0160
51 #define IDM_FULLSCREEN 0x0170
53 #define IDM_SESSLGP 0x0250 /* log type printable */
54 #define IDM_SESSLGA 0x0260 /* log type all chars */
55 #define IDM_SESSLGE 0x0270 /* log end */
56 #define IDM_SAVED_MIN 0x1000
57 #define IDM_SAVED_MAX 0x2000
59 #define WM_IGNORE_CLIP (WM_XUSER + 2)
61 /* Needed for Chinese support and apparently not always defined. */
63 #define VK_PROCESSKEY 0xE5
66 /* Needed for mouse wheel support and not defined in earlier SDKs. */
68 #define WM_MOUSEWHEEL 0x020A
71 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
72 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
73 unsigned char *output);
74 static void cfgtopalette(void);
75 static void init_palette(void);
76 static void init_fonts(int, int);
77 static void another_font(int);
78 static void deinit_fonts(void);
80 /* Window layout information */
81 static void reset_window(int);
82 static int full_screen = 0, want_full_screen = 0;
83 static int extra_width, extra_height;
84 static int font_width, font_height, font_dualwidth;
85 static int offset_width, offset_height;
86 static int was_zoomed = 0;
87 static int was_full_screen = 0;
88 static int prev_rows, prev_cols;
89 static int pre_fs_rows, pre_fs_cols;
91 static LONG old_wind_style;
92 static WINDOWPLACEMENT old_wind_placement;
94 static int pending_netevent = 0;
95 static WPARAM pend_netevent_wParam = 0;
96 static LPARAM pend_netevent_lParam = 0;
97 static void enact_pending_netevent(void);
98 static void flash_window(int mode);
99 static void flip_full_screen(void);
101 static time_t last_movement = 0;
103 #define FONT_NORMAL 0
105 #define FONT_UNDERLINE 2
106 #define FONT_BOLDUND 3
107 #define FONT_WIDE 0x04
108 #define FONT_HIGH 0x08
109 #define FONT_NARROW 0x10
111 #define FONT_OEM 0x20
112 #define FONT_OEMBOLD 0x21
113 #define FONT_OEMUND 0x22
114 #define FONT_OEMBOLDUND 0x23
116 #define FONT_MAXNO 0x2F
118 static HFONT fonts[FONT_MAXNO];
119 static int fontflag[FONT_MAXNO];
121 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
129 static COLORREF colours[NCOLOURS];
131 static LPLOGPALETTE logpal;
132 static RGBTRIPLE defpal[NCOLOURS];
136 static HBITMAP caretbm;
138 static int dbltime, lasttime, lastact;
139 static Mouse_Button lastbtn;
141 /* this allows xterm-style mouse handling. */
142 static int send_raw_mouse = 0;
143 static int wheel_accumulator = 0;
145 static char *window_name, *icon_name;
147 static int compose_state = 0;
149 static OSVERSIONINFO osVersion;
151 /* Dummy routine, only required in plink. */
152 void ldisc_update(int echo, int edit)
156 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
158 static char appname[] = "PuTTY";
163 int guess_width, guess_height;
166 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
168 winsock_ver = MAKEWORD(1, 1);
169 if (WSAStartup(winsock_ver, &wsadata)) {
170 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
171 MB_OK | MB_ICONEXCLAMATION);
174 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
175 MessageBox(NULL, "WinSock version is incompatible with 1.1",
176 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
180 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
183 InitCommonControls();
185 /* Ensure a Maximize setting in Explorer doesn't maximise the
190 ZeroMemory(&osVersion, sizeof(osVersion));
191 osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
192 if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
193 MessageBox(NULL, "Windows refuses to report a version",
194 "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
200 * Process the command line.
205 default_protocol = DEFAULT_PROTOCOL;
206 default_port = DEFAULT_PORT;
207 cfg.logtype = LGTYP_NONE;
209 do_defaults(NULL, &cfg);
212 while (*p && isspace(*p))
216 * Process command line options first. Yes, this can be
217 * done better, and it will be as soon as I have the
221 char *q = p + strcspn(p, " \t");
224 tolower(p[0]) == 's' &&
225 tolower(p[1]) == 's' && tolower(p[2]) == 'h') {
226 default_protocol = cfg.protocol = PROT_SSH;
227 default_port = cfg.port = 22;
228 } else if (q == p + 7 &&
229 tolower(p[0]) == 'c' &&
230 tolower(p[1]) == 'l' &&
231 tolower(p[2]) == 'e' &&
232 tolower(p[3]) == 'a' &&
233 tolower(p[4]) == 'n' &&
234 tolower(p[5]) == 'u' && tolower(p[6]) == 'p') {
236 * `putty -cleanup'. Remove all registry entries
237 * associated with PuTTY, and also find and delete
238 * the random seed file.
241 "This procedure will remove ALL Registry\n"
242 "entries associated with PuTTY, and will\n"
243 "also remove the PuTTY random seed file.\n"
245 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
246 "SESSIONS. Are you really sure you want\n"
249 MB_YESNO | MB_ICONWARNING) == IDYES) {
254 p = q + strspn(q, " \t");
258 * An initial @ means to activate a saved session.
262 while (i > 1 && isspace(p[i - 1]))
265 do_defaults(p + 1, &cfg);
266 if (!*cfg.host && !do_config()) {
270 } else if (*p == '&') {
272 * An initial & means we've been given a command line
273 * containing the hex value of a HANDLE for a file
274 * mapping object, which we must then extract as a
279 if (sscanf(p + 1, "%p", &filemap) == 1 &&
280 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
281 0, 0, sizeof(Config))) != NULL) {
284 CloseHandle(filemap);
285 } else if (!do_config()) {
292 * If the hostname starts with "telnet:", set the
293 * protocol to Telnet and process the string as a
296 if (!strncmp(q, "telnet:", 7)) {
300 if (q[0] == '/' && q[1] == '/')
302 cfg.protocol = PROT_TELNET;
304 while (*p && *p != ':' && *p != '/')
313 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
314 cfg.host[sizeof(cfg.host) - 1] = '\0';
316 while (*p && !isspace(*p))
320 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
321 cfg.host[sizeof(cfg.host) - 1] = '\0';
322 while (*p && isspace(*p))
337 * Trim leading whitespace off the hostname if it's there.
340 int space = strspn(cfg.host, " \t");
341 memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
344 /* See if host is of the form user@host */
345 if (cfg.host[0] != '\0') {
346 char *atsign = strchr(cfg.host, '@');
347 /* Make sure we're not overflowing the user field */
349 if (atsign - cfg.host < sizeof cfg.username) {
350 strncpy(cfg.username, cfg.host, atsign - cfg.host);
351 cfg.username[atsign - cfg.host] = '\0';
353 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
358 * Trim a colon suffix off the hostname if it's there.
360 cfg.host[strcspn(cfg.host, ":")] = '\0';
364 * Select protocol. This is farmed out into a table in a
365 * separate file to enable an ssh-free variant.
370 for (i = 0; backends[i].backend != NULL; i++)
371 if (backends[i].protocol == cfg.protocol) {
372 back = backends[i].backend;
376 MessageBox(NULL, "Unsupported protocol number found",
377 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
383 /* Check for invalid Port number (i.e. zero) */
385 MessageBox(NULL, "Invalid Port Number",
386 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
393 wndclass.lpfnWndProc = WndProc;
394 wndclass.cbClsExtra = 0;
395 wndclass.cbWndExtra = 0;
396 wndclass.hInstance = inst;
397 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
398 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
399 wndclass.hbrBackground = NULL;
400 wndclass.lpszMenuName = NULL;
401 wndclass.lpszClassName = appname;
403 RegisterClass(&wndclass);
408 savelines = cfg.savelines;
414 * Guess some defaults for the window size. This all gets
415 * updated later, so we don't really care too much. However, we
416 * do want the font width/height guesses to correspond to a
417 * large font rather than a small one...
424 term_size(cfg.height, cfg.width, cfg.savelines);
425 guess_width = extra_width + font_width * cols;
426 guess_height = extra_height + font_height * rows;
429 HWND w = GetDesktopWindow();
430 GetWindowRect(w, &r);
431 if (guess_width > r.right - r.left)
432 guess_width = r.right - r.left;
433 if (guess_height > r.bottom - r.top)
434 guess_height = r.bottom - r.top;
438 int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
441 winmode &= ~(WS_VSCROLL);
442 if (cfg.resize_action == RESIZE_DISABLED)
443 winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
445 exwinmode |= WS_EX_TOPMOST;
447 exwinmode |= WS_EX_CLIENTEDGE;
448 hwnd = CreateWindowEx(exwinmode, appname, appname,
449 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
450 guess_width, guess_height,
451 NULL, NULL, inst, NULL);
455 * Initialise the fonts, simultaneously correcting the guesses
456 * for font_{width,height}.
461 * Correct the guesses for extra_{width,height}.
465 GetWindowRect(hwnd, &wr);
466 GetClientRect(hwnd, &cr);
467 offset_width = offset_height = cfg.window_border;
468 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
469 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
473 * Resize the window, now we know what size we _really_ want it
476 guess_width = extra_width + font_width * cols;
477 guess_height = extra_height + font_height * rows;
478 SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
479 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
482 * Set up a caret bitmap, with no content.
486 int size = (font_width + 15) / 16 * 2 * font_height;
487 bits = smalloc(size);
488 memset(bits, 0, size);
489 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
492 CreateCaret(hwnd, caretbm, font_width, font_height);
495 * Initialise the scroll bar.
500 si.cbSize = sizeof(si);
501 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
506 SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
510 * Start up the telnet connection.
514 char msg[1024], *title;
517 error = back->init(cfg.host, cfg.port, &realhost);
519 sprintf(msg, "Unable to open connection to\n"
520 "%.800s\n" "%s", cfg.host, error);
521 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
524 window_name = icon_name = NULL;
526 title = cfg.wintitle;
528 sprintf(msg, "%s - PuTTY", realhost);
536 session_closed = FALSE;
539 * Prepare the mouse handler.
541 lastact = MA_NOTHING;
542 lastbtn = MBT_NOTHING;
543 dbltime = GetDoubleClickTime();
546 * Set up the session-control options on the system menu.
549 HMENU m = GetSystemMenu(hwnd, FALSE);
553 AppendMenu(m, MF_SEPARATOR, 0, 0);
554 if (cfg.protocol == PROT_TELNET) {
556 AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
557 AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
558 AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
559 AppendMenu(p, MF_SEPARATOR, 0, 0);
560 AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
561 AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
562 AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
563 AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
564 AppendMenu(p, MF_SEPARATOR, 0, 0);
565 AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
566 AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
567 AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
568 AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
569 AppendMenu(p, MF_SEPARATOR, 0, 0);
570 AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
571 AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
572 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
574 AppendMenu(m, MF_SEPARATOR, 0, 0);
576 AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
577 AppendMenu(m, MF_SEPARATOR, 0, 0);
578 AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
579 AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
582 for (i = 1; i < ((nsessions < 256) ? nsessions : 256); i++)
583 AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
585 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
586 AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
587 AppendMenu(m, MF_SEPARATOR, 0, 0);
588 AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
589 AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
590 AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
591 AppendMenu(m, MF_SEPARATOR, 0, 0);
592 AppendMenu(m, MF_ENABLED, IDM_FULLSCREEN, "&Full Screen");
593 AppendMenu(m, MF_SEPARATOR, 0, 0);
594 AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
598 * Finally show the window!
600 ShowWindow(hwnd, show);
601 SetForegroundWindow(hwnd);
604 * Open the initial log file if there is one.
609 * Set the palette up.
615 has_focus = (GetForegroundWindow() == hwnd);
618 if (GetMessage(&msg, NULL, 0, 0) == 1) {
619 int timer_id = 0, long_timer = 0;
621 while (msg.message != WM_QUIT) {
622 /* Sometimes DispatchMessage calls routines that use their own
623 * GetMessage loop, setup this timer so we get some control back.
625 * Also call term_update() from the timer so that if the host
626 * is sending data flat out we still do redraws.
628 if (timer_id && long_timer) {
629 KillTimer(hwnd, timer_id);
630 long_timer = timer_id = 0;
633 timer_id = SetTimer(hwnd, 1, 20, NULL);
634 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
635 DispatchMessage(&msg);
637 /* Make sure we blink everything that needs it. */
640 /* Send the paste buffer if there's anything to send */
643 /* If there's nothing new in the queue then we can do everything
644 * we've delayed, reading the socket, writing, and repainting
647 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
650 if (pending_netevent) {
651 enact_pending_netevent();
653 /* Force the cursor blink on */
656 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
660 /* Okay there is now nothing to do so we make sure the screen is
661 * completely up to date then tell windows to call us in a little
665 KillTimer(hwnd, timer_id);
673 flash_window(1); /* maintain */
676 /* Hmm, term_update didn't want to do an update too soon ... */
677 timer_id = SetTimer(hwnd, 1, 50, NULL);
679 timer_id = SetTimer(hwnd, 1, 500, NULL);
681 timer_id = SetTimer(hwnd, 1, 100, NULL);
684 /* There's no point rescanning everything in the message queue
685 * so we do an apparently unnecessary wait here
688 if (GetMessage(&msg, NULL, 0, 0) != 1)
702 if (cfg.protocol == PROT_SSH) {
713 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
715 char *do_select(SOCKET skt, int startup)
720 events = (FD_CONNECT | FD_READ | FD_WRITE |
721 FD_OOB | FD_CLOSE | FD_ACCEPT);
726 return "do_select(): internal error (hwnd==NULL)";
727 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
728 switch (WSAGetLastError()) {
730 return "Network is down";
732 return "WSAAsyncSelect(): unknown error";
739 * set or clear the "raw mouse message" mode
741 void set_raw_mouse_mode(int activate)
743 send_raw_mouse = activate;
744 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
748 * Print a message box and close the connection.
750 void connection_fatal(char *fmt, ...)
756 vsprintf(stuff, fmt, ap);
758 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
759 if (cfg.close_on_exit == COE_ALWAYS)
762 session_closed = TRUE;
763 SetWindowText(hwnd, "PuTTY (inactive)");
768 * Actually do the job requested by a WM_NETEVENT
770 static void enact_pending_netevent(void)
772 static int reentering = 0;
773 extern int select_result(WPARAM, LPARAM);
777 return; /* don't unpend the pending */
779 pending_netevent = FALSE;
782 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
785 if (ret == 0 && !session_closed) {
786 /* Abnormal exits will already have set session_closed and taken
787 * appropriate action. */
788 if (cfg.close_on_exit == COE_ALWAYS ||
789 cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
791 session_closed = TRUE;
792 SetWindowText(hwnd, "PuTTY (inactive)");
793 MessageBox(hwnd, "Connection closed by remote host",
794 "PuTTY", MB_OK | MB_ICONINFORMATION);
800 * Copy the colour palette from the configuration data into defpal.
801 * This is non-trivial because the colour indices are different.
803 static void cfgtopalette(void)
806 static const int ww[] = {
807 6, 7, 8, 9, 10, 11, 12, 13,
808 14, 15, 16, 17, 18, 19, 20, 21,
809 0, 1, 2, 3, 4, 4, 5, 5
812 for (i = 0; i < 24; i++) {
814 defpal[i].rgbtRed = cfg.colours[w][0];
815 defpal[i].rgbtGreen = cfg.colours[w][1];
816 defpal[i].rgbtBlue = cfg.colours[w][2];
821 * Set up the colour palette.
823 static void init_palette(void)
826 HDC hdc = GetDC(hwnd);
828 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
829 logpal = smalloc(sizeof(*logpal)
830 - sizeof(logpal->palPalEntry)
831 + NCOLOURS * sizeof(PALETTEENTRY));
832 logpal->palVersion = 0x300;
833 logpal->palNumEntries = NCOLOURS;
834 for (i = 0; i < NCOLOURS; i++) {
835 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
836 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
837 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
838 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
840 pal = CreatePalette(logpal);
842 SelectPalette(hdc, pal, FALSE);
844 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
847 ReleaseDC(hwnd, hdc);
850 for (i = 0; i < NCOLOURS; i++)
851 colours[i] = PALETTERGB(defpal[i].rgbtRed,
855 for (i = 0; i < NCOLOURS; i++)
856 colours[i] = RGB(defpal[i].rgbtRed,
857 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
861 * Initialise all the fonts we will need initially. There may be as many as
862 * three or as few as one. The other (poentially) twentyone fonts are done
863 * if/when they are needed.
867 * - check the font width and height, correcting our guesses if
870 * - verify that the bold font is the same width as the ordinary
871 * one, and engage shadow bolding if not.
873 * - verify that the underlined font is the same width as the
874 * ordinary one (manual underlining by means of line drawing can
875 * be done in a pinch).
877 static void init_fonts(int pick_width, int pick_height)
884 int fw_dontcare, fw_bold;
886 for (i = 0; i < FONT_MAXNO; i++)
889 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
892 if (cfg.fontisbold) {
893 fw_dontcare = FW_BOLD;
896 fw_dontcare = FW_DONTCARE;
903 font_height = pick_height;
905 font_height = cfg.fontheight;
906 if (font_height > 0) {
908 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
911 font_width = pick_width;
914 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
915 c, OUT_DEFAULT_PRECIS, \
916 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
917 FIXED_PITCH | FF_DONTCARE, cfg.font)
919 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
921 SelectObject(hdc, fonts[FONT_NORMAL]);
922 GetTextMetrics(hdc, &tm);
924 if (pick_width == 0 || pick_height == 0) {
925 font_height = tm.tmHeight;
926 font_width = tm.tmAveCharWidth;
928 font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth);
930 #ifdef RDB_DEBUG_PATCH
931 debug(23, "Primary font H=%d, AW=%d, MW=%d",
932 tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth);
937 DWORD cset = tm.tmCharSet;
938 memset(&info, 0xFF, sizeof(info));
940 /* !!! Yes the next line is right */
941 if (cset == OEM_CHARSET)
942 font_codepage = GetOEMCP();
944 if (TranslateCharsetInfo
945 ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
950 GetCPInfo(font_codepage, &cpinfo);
951 dbcs_screenfont = (cpinfo.MaxCharSize > 1);
954 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
957 * Some fonts, e.g. 9-pt Courier, draw their underlines
958 * outside their character cell. We successfully prevent
959 * screen corruption by clipping the text output, but then
960 * we lose the underline completely. Here we try to work
961 * out whether this is such a font, and if it is, we set a
962 * flag that causes underlines to be drawn by hand.
964 * Having tried other more sophisticated approaches (such
965 * as examining the TEXTMETRIC structure or requesting the
966 * height of a string), I think we'll do this the brute
967 * force way: we create a small bitmap, draw an underlined
968 * space on it, and test to see whether any pixels are
969 * foreground-coloured. (Since we expect the underline to
970 * go all the way across the character cell, we only search
971 * down a single column of the bitmap, half way across.)
975 HBITMAP und_bm, und_oldbm;
979 und_dc = CreateCompatibleDC(hdc);
980 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
981 und_oldbm = SelectObject(und_dc, und_bm);
982 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
983 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
984 SetTextColor(und_dc, RGB(255, 255, 255));
985 SetBkColor(und_dc, RGB(0, 0, 0));
986 SetBkMode(und_dc, OPAQUE);
987 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
989 for (i = 0; i < font_height; i++) {
990 c = GetPixel(und_dc, font_width / 2, i);
991 if (c != RGB(0, 0, 0))
994 SelectObject(und_dc, und_oldbm);
995 DeleteObject(und_bm);
999 DeleteObject(fonts[FONT_UNDERLINE]);
1000 fonts[FONT_UNDERLINE] = 0;
1004 if (bold_mode == BOLD_FONT) {
1005 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
1009 descent = tm.tmAscent + 1;
1010 if (descent >= font_height)
1011 descent = font_height - 1;
1013 for (i = 0; i < 3; i++) {
1015 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
1016 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
1023 ReleaseDC(hwnd, hdc);
1025 if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
1026 und_mode = UND_LINE;
1027 DeleteObject(fonts[FONT_UNDERLINE]);
1028 fonts[FONT_UNDERLINE] = 0;
1031 if (bold_mode == BOLD_FONT &&
1032 fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
1033 bold_mode = BOLD_SHADOW;
1034 DeleteObject(fonts[FONT_BOLD]);
1035 fonts[FONT_BOLD] = 0;
1037 fontflag[0] = fontflag[1] = fontflag[2] = 1;
1042 static void another_font(int fontno)
1045 int fw_dontcare, fw_bold;
1049 if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1052 basefont = (fontno & ~(FONT_BOLDUND));
1053 if (basefont != fontno && !fontflag[basefont])
1054 another_font(basefont);
1056 if (cfg.fontisbold) {
1057 fw_dontcare = FW_BOLD;
1060 fw_dontcare = FW_DONTCARE;
1064 c = cfg.fontcharset;
1070 if (fontno & FONT_WIDE)
1072 if (fontno & FONT_NARROW)
1074 if (fontno & FONT_OEM)
1076 if (fontno & FONT_BOLD)
1078 if (fontno & FONT_UNDERLINE)
1082 CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1083 FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1084 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1085 FIXED_PITCH | FF_DONTCARE, s);
1087 fontflag[fontno] = 1;
1090 static void deinit_fonts(void)
1093 for (i = 0; i < FONT_MAXNO; i++) {
1095 DeleteObject(fonts[i]);
1101 void request_resize(int w, int h)
1105 /* If the window is maximized supress resizing attempts */
1106 if (IsZoomed(hwnd)) {
1107 if (cfg.resize_action != RESIZE_FONT)
1111 if (cfg.resize_action == RESIZE_DISABLED) return;
1112 if (h == rows && w == cols) return;
1114 /* Sanity checks ... */
1116 static int first_time = 1;
1119 switch (first_time) {
1121 /* Get the size of the screen */
1122 if (GetClientRect(GetDesktopWindow(), &ss))
1123 /* first_time = 0 */ ;
1129 /* Make sure the values are sane */
1130 width = (ss.right - ss.left - extra_width) / 4;
1131 height = (ss.bottom - ss.top - extra_height) / 6;
1133 if (w > width || h > height)
1142 term_size(h, w, cfg.savelines);
1144 if (cfg.resize_action != RESIZE_FONT) {
1145 width = extra_width + font_width * w;
1146 height = extra_height + font_height * h;
1148 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1149 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1150 SWP_NOMOVE | SWP_NOZORDER);
1154 InvalidateRect(hwnd, NULL, TRUE);
1157 static void reset_window(int reinit) {
1159 * This function decides how to resize or redraw when the
1160 * user changes something.
1162 * This function doesn't like to change the terminal size but if the
1163 * font size is locked that may be it's only soluion.
1165 int win_width, win_height;
1168 #ifdef RDB_DEBUG_PATCH
1169 debug((27, "reset_window()"));
1172 /* Current window sizes ... */
1173 GetWindowRect(hwnd, &wr);
1174 GetClientRect(hwnd, &cr);
1176 win_width = cr.right - cr.left;
1177 win_height = cr.bottom - cr.top;
1179 /* Are we being forced to reload the fonts ? */
1181 #ifdef RDB_DEBUG_PATCH
1182 debug((27, "reset_window() -- Forced deinit"));
1188 /* Oh, looks like we're minimised */
1189 if (win_width == 0 || win_height == 0)
1192 /* Is the window out of position ? */
1194 (offset_width != (win_width-font_width*cols)/2 ||
1195 offset_height != (win_height-font_height*rows)/2) ){
1196 offset_width = (win_width-font_width*cols)/2;
1197 offset_height = (win_height-font_height*rows)/2;
1198 InvalidateRect(hwnd, NULL, TRUE);
1199 #ifdef RDB_DEBUG_PATCH
1200 debug((27, "reset_window() -> Reposition terminal"));
1204 if (IsZoomed(hwnd) || full_screen) {
1205 /* We're fullscreen, this means we must not change the size of
1206 * the window so it's the font size or the terminal itself.
1209 extra_width = wr.right - wr.left - cr.right + cr.left;
1210 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1212 if (cfg.resize_action == RESIZE_FONT) {
1213 if ( font_width != win_width/cols ||
1214 font_height != win_height/rows) {
1216 init_fonts(win_width/cols, win_height/rows);
1217 offset_width = (win_width-font_width*cols)/2;
1218 offset_height = (win_height-font_height*rows)/2;
1219 InvalidateRect(hwnd, NULL, TRUE);
1220 #ifdef RDB_DEBUG_PATCH
1221 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1222 font_width, font_height));
1226 if ( font_width != win_width/cols ||
1227 font_height != win_height/rows) {
1228 /* Our only choice at this point is to change the
1229 * size of the terminal; Oh well.
1231 term_size( win_height/font_height, win_width/font_width,
1233 offset_width = (win_width-font_width*cols)/2;
1234 offset_height = (win_height-font_height*rows)/2;
1235 InvalidateRect(hwnd, NULL, TRUE);
1236 #ifdef RDB_DEBUG_PATCH
1237 debug((27, "reset_window() -> Zoomed term_size"));
1244 /* Hmm, a force re-init means we should ignore the current window
1245 * so we resize to the default font size.
1248 #ifdef RDB_DEBUG_PATCH
1249 debug((27, "reset_window() -> Forced re-init"));
1252 offset_width = offset_height = cfg.window_border;
1253 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1254 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1256 if (win_width != font_width*cols + offset_width*2 ||
1257 win_height != font_height*rows + offset_height*2) {
1259 /* If this is too large windows will resize it to the maximum
1260 * allowed window size, we will then be back in here and resize
1261 * the font or terminal to fit.
1263 SetWindowPos(hwnd, NULL, 0, 0,
1264 font_width*cols + extra_width,
1265 font_height*rows + extra_height,
1266 SWP_NOMOVE | SWP_NOZORDER);
1271 /* Okay the user doesn't want us to change the font so we try the
1272 * window. But that may be too big for the screen which forces us
1273 * to change the terminal.
1275 if ((cfg.resize_action != RESIZE_FONT && reinit==0) || reinit>0) {
1276 offset_width = offset_height = cfg.window_border;
1277 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1278 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1280 if (win_width != font_width*cols + offset_width*2 ||
1281 win_height != font_height*rows + offset_height*2) {
1286 GetClientRect(GetDesktopWindow(), &ss);
1287 width = (ss.right - ss.left - extra_width) / font_width;
1288 height = (ss.bottom - ss.top - extra_height) / font_height;
1291 if ( rows > height || cols > width ) {
1292 if ( height > rows ) height = rows;
1293 if ( width > cols ) width = cols;
1294 term_size(height, width, cfg.savelines);
1295 #ifdef RDB_DEBUG_PATCH
1296 debug((27, "reset_window() -> term resize to (%d,%d)",
1301 SetWindowPos(hwnd, NULL, 0, 0,
1302 font_width*cols + extra_width,
1303 font_height*rows + extra_height,
1304 SWP_NOMOVE | SWP_NOZORDER);
1306 InvalidateRect(hwnd, NULL, TRUE);
1307 #ifdef RDB_DEBUG_PATCH
1308 debug((27, "reset_window() -> window resize to (%d,%d)",
1309 font_width*cols + extra_width,
1310 font_height*rows + extra_height));
1316 /* We're allowed to or must change the font but do we want to ? */
1318 if (font_width != (win_width-cfg.window_border*2)/cols ||
1319 font_height != (win_height-cfg.window_border*2)/rows) {
1322 init_fonts((win_width-cfg.window_border*2)/cols,
1323 (win_height-cfg.window_border*2)/rows);
1324 offset_width = (win_width-font_width*cols)/2;
1325 offset_height = (win_height-font_height*rows)/2;
1327 extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2;
1328 extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2;
1330 InvalidateRect(hwnd, NULL, TRUE);
1331 #ifdef RDB_DEBUG_PATCH
1332 debug((25, "reset_window() -> font resize to (%d,%d)",
1333 font_width, font_height));
1338 static void click(Mouse_Button b, int x, int y, int shift, int ctrl)
1340 int thistime = GetMessageTime();
1342 if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1343 lastbtn = MBT_NOTHING;
1344 term_mouse(b, MA_CLICK, x, y, shift, ctrl);
1348 if (lastbtn == b && thistime - lasttime < dbltime) {
1349 lastact = (lastact == MA_CLICK ? MA_2CLK :
1350 lastact == MA_2CLK ? MA_3CLK :
1351 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1356 if (lastact != MA_NOTHING)
1357 term_mouse(b, lastact, x, y, shift, ctrl);
1358 lasttime = thistime;
1362 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1363 * into a cooked one (SELECT, EXTEND, PASTE).
1365 Mouse_Button translate_button(Mouse_Button button)
1367 if (button == MBT_LEFT)
1369 if (button == MBT_MIDDLE)
1370 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1371 if (button == MBT_RIGHT)
1372 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1373 return 0; /* shouldn't happen */
1376 static void show_mouseptr(int show)
1378 static int cursor_visible = 1;
1379 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1381 if (cursor_visible && !show)
1383 else if (!cursor_visible && show)
1385 cursor_visible = show;
1388 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1389 WPARAM wParam, LPARAM lParam)
1392 static int ignore_clip = FALSE;
1393 static int resizing = FALSE;
1394 static int need_backend_resize = FALSE;
1398 if (pending_netevent)
1399 enact_pending_netevent();
1405 if (cfg.ping_interval > 0) {
1408 if (now - last_movement > cfg.ping_interval) {
1409 back->special(TS_PING);
1410 last_movement = now;
1418 if (!cfg.warn_on_close || session_closed ||
1420 "Are you sure you want to close this session?",
1421 "PuTTY Exit Confirmation",
1422 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1423 DestroyWindow(hwnd);
1430 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1442 PROCESS_INFORMATION pi;
1443 HANDLE filemap = NULL;
1445 if (wParam == IDM_DUPSESS) {
1447 * Allocate a file-mapping memory chunk for the
1450 SECURITY_ATTRIBUTES sa;
1453 sa.nLength = sizeof(sa);
1454 sa.lpSecurityDescriptor = NULL;
1455 sa.bInheritHandle = TRUE;
1456 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1459 0, sizeof(Config), NULL);
1461 p = (Config *) MapViewOfFile(filemap,
1463 0, 0, sizeof(Config));
1465 *p = cfg; /* structure copy */
1469 sprintf(c, "putty &%p", filemap);
1471 } else if (wParam == IDM_SAVEDSESS) {
1473 sessions[(lParam - IDM_SAVED_MIN) / 16];
1474 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1476 cl = NULL; /* not a very important failure mode */
1478 sprintf(cl, "putty @%s", session);
1484 GetModuleFileName(NULL, b, sizeof(b) - 1);
1486 si.lpReserved = NULL;
1487 si.lpDesktop = NULL;
1491 si.lpReserved2 = NULL;
1492 CreateProcess(b, cl, NULL, NULL, TRUE,
1493 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1496 CloseHandle(filemap);
1506 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1509 if (!do_reconfig(hwnd))
1512 /* If user forcibly disables full-screen, gracefully unzoom */
1513 if (full_screen && !cfg.fullscreenonaltenter) {
1517 if (strcmp(prev_cfg.logfilename, cfg.logfilename) ||
1518 prev_cfg.logtype != cfg.logtype) {
1519 logfclose(); /* reset logging */
1525 * Flush the line discipline's edit buffer in the
1526 * case where local editing has just been disabled.
1528 ldisc_send(NULL, 0, 0);
1536 /* Screen size changed ? */
1537 if (cfg.height != prev_cfg.height ||
1538 cfg.width != prev_cfg.width ||
1539 cfg.savelines != prev_cfg.savelines ||
1540 cfg.resize_action != RESIZE_TERM)
1541 term_size(cfg.height, cfg.width, cfg.savelines);
1543 /* Enable or disable the scroll bar, etc */
1545 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1546 LONG nexflag, exflag =
1547 GetWindowLong(hwnd, GWL_EXSTYLE);
1550 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
1551 if (cfg.alwaysontop) {
1552 nexflag |= WS_EX_TOPMOST;
1553 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1554 SWP_NOMOVE | SWP_NOSIZE);
1556 nexflag &= ~(WS_EX_TOPMOST);
1557 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1558 SWP_NOMOVE | SWP_NOSIZE);
1561 if (cfg.sunken_edge)
1562 nexflag |= WS_EX_CLIENTEDGE;
1564 nexflag &= ~(WS_EX_CLIENTEDGE);
1570 nflg &= ~WS_VSCROLL;
1571 if (cfg.resize_action == RESIZE_DISABLED)
1572 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1574 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1576 if (nflg != flag || nexflag != exflag) {
1578 SetWindowLong(hwnd, GWL_STYLE, nflg);
1579 if (nexflag != exflag)
1580 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1582 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1583 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1584 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1592 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
1597 set_title(cfg.wintitle);
1598 if (IsIconic(hwnd)) {
1600 cfg.win_name_always ? window_name :
1604 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1605 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1606 cfg.fontisbold != prev_cfg.fontisbold ||
1607 cfg.fontheight != prev_cfg.fontheight ||
1608 cfg.fontcharset != prev_cfg.fontcharset ||
1609 cfg.vtmode != prev_cfg.vtmode ||
1610 cfg.bold_colour != prev_cfg.bold_colour ||
1611 (cfg.resize_action != RESIZE_FONT &&
1612 prev_cfg.resize_action == RESIZE_FONT))
1615 InvalidateRect(hwnd, NULL, TRUE);
1616 reset_window(init_lvl);
1629 back->special(TS_AYT);
1632 back->special(TS_BRK);
1635 back->special(TS_SYNCH);
1638 back->special(TS_EC);
1641 back->special(TS_EL);
1644 back->special(TS_GA);
1647 back->special(TS_NOP);
1650 back->special(TS_ABORT);
1653 back->special(TS_AO);
1656 back->special(TS_IP);
1659 back->special(TS_SUSP);
1662 back->special(TS_EOR);
1665 back->special(TS_EOF);
1672 * We get this if the System menu has been activated.
1673 * This might happen from within TranslateKey, in which
1674 * case it really wants to be followed by a `space'
1675 * character to actually _bring the menu up_ rather
1676 * than just sitting there in `ready to appear' state.
1679 PostMessage(hwnd, WM_CHAR, ' ', 0);
1681 case IDM_FULLSCREEN:
1685 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1686 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1691 #define X_POS(l) ((int)(short)LOWORD(l))
1692 #define Y_POS(l) ((int)(short)HIWORD(l))
1694 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1695 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1696 #define WHEEL_DELTA 120
1699 wheel_accumulator += (short) HIWORD(wParam);
1700 wParam = LOWORD(wParam);
1702 /* process events when the threshold is reached */
1703 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1706 /* reduce amount for next time */
1707 if (wheel_accumulator > 0) {
1709 wheel_accumulator -= WHEEL_DELTA;
1710 } else if (wheel_accumulator < 0) {
1712 wheel_accumulator += WHEEL_DELTA;
1716 if (send_raw_mouse) {
1717 /* send a mouse-down followed by a mouse up */
1720 TO_CHR_X(X_POS(lParam)),
1721 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1722 wParam & MK_CONTROL);
1723 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1724 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1725 wParam & MK_CONTROL);
1727 /* trigger a scroll */
1729 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1734 case WM_LBUTTONDOWN:
1735 case WM_MBUTTONDOWN:
1736 case WM_RBUTTONDOWN:
1743 case WM_LBUTTONDOWN:
1747 case WM_MBUTTONDOWN:
1748 button = MBT_MIDDLE;
1751 case WM_RBUTTONDOWN:
1760 button = MBT_MIDDLE;
1768 button = press = 0; /* shouldn't happen */
1773 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1774 wParam & MK_SHIFT, wParam & MK_CONTROL);
1777 term_mouse(button, MA_RELEASE,
1778 TO_CHR_X(X_POS(lParam)),
1779 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1780 wParam & MK_CONTROL);
1788 * Add the mouse position and message time to the random
1791 noise_ultralight(lParam);
1793 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1795 if (wParam & MK_LBUTTON)
1797 else if (wParam & MK_MBUTTON)
1801 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1802 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1803 wParam & MK_CONTROL);
1806 case WM_NCMOUSEMOVE:
1808 noise_ultralight(lParam);
1810 case WM_IGNORE_CLIP:
1811 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1813 case WM_DESTROYCLIPBOARD:
1816 ignore_clip = FALSE;
1822 hdc = BeginPaint(hwnd, &p);
1824 SelectPalette(hdc, pal, TRUE);
1825 RealizePalette(hdc);
1828 (p.rcPaint.left-offset_width)/font_width,
1829 (p.rcPaint.top-offset_height)/font_height,
1830 (p.rcPaint.right-offset_width-1)/font_width,
1831 (p.rcPaint.bottom-offset_height-1)/font_height);
1834 p.rcPaint.left < offset_width ||
1835 p.rcPaint.top < offset_height ||
1836 p.rcPaint.right >= offset_width + font_width*cols ||
1837 p.rcPaint.bottom>= offset_height + font_height*rows)
1839 HBRUSH fillcolour, oldbrush;
1841 fillcolour = CreateSolidBrush (
1842 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1843 oldbrush = SelectObject(hdc, fillcolour);
1844 edge = CreatePen(PS_SOLID, 0,
1845 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1846 oldpen = SelectObject(hdc, edge);
1848 ExcludeClipRect(hdc,
1849 offset_width, offset_height,
1850 offset_width+font_width*cols,
1851 offset_height+font_height*rows);
1853 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
1854 p.rcPaint.right, p.rcPaint.bottom);
1856 // SelectClipRgn(hdc, NULL);
1858 SelectObject(hdc, oldbrush);
1859 DeleteObject(fillcolour);
1860 SelectObject(hdc, oldpen);
1863 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1864 SelectObject(hdc, GetStockObject(WHITE_PEN));
1870 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1871 * but the only one that's likely to try to overload us is FD_READ.
1872 * This means buffering just one is fine.
1874 if (pending_netevent)
1875 enact_pending_netevent();
1877 pending_netevent = TRUE;
1878 pend_netevent_wParam = wParam;
1879 pend_netevent_lParam = lParam;
1880 if (WSAGETSELECTEVENT(lParam) != FD_READ)
1881 enact_pending_netevent();
1883 time(&last_movement);
1887 CreateCaret(hwnd, caretbm, font_width, font_height);
1889 flash_window(0); /* stop */
1901 case WM_ENTERSIZEMOVE:
1902 #ifdef RDB_DEBUG_PATCH
1903 debug((27, "WM_ENTERSIZEMOVE"));
1907 need_backend_resize = FALSE;
1909 case WM_EXITSIZEMOVE:
1912 #ifdef RDB_DEBUG_PATCH
1913 debug((27, "WM_EXITSIZEMOVE"));
1915 if (need_backend_resize) {
1916 term_size(cfg.height, cfg.width, cfg.savelines);
1917 InvalidateRect(hwnd, NULL, TRUE);
1922 * This does two jobs:
1923 * 1) Keep the sizetip uptodate
1924 * 2) Make sure the window size is _stepped_ in units of the font size.
1926 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
1927 int width, height, w, h, ew, eh;
1928 LPRECT r = (LPRECT) lParam;
1930 if ( !need_backend_resize &&
1931 (cfg.height != rows || cfg.width != cols )) {
1933 * Great! It seems the host has been changing the terminal
1934 * size, well the user is now grabbing so this is probably
1935 * the least confusing solution in the long run even though
1936 * it a is suprise. Unfortunatly the only way to prevent
1937 * this seems to be to let the host change the window size
1938 * and as that's a user option we're still right back here.
1940 term_size(cfg.height, cfg.width, cfg.savelines);
1942 InvalidateRect(hwnd, NULL, TRUE);
1943 need_backend_resize = TRUE;
1946 width = r->right - r->left - extra_width;
1947 height = r->bottom - r->top - extra_height;
1948 w = (width + font_width / 2) / font_width;
1951 h = (height + font_height / 2) / font_height;
1954 UpdateSizeTip(hwnd, w, h);
1955 ew = width - w * font_width;
1956 eh = height - h * font_height;
1958 if (wParam == WMSZ_LEFT ||
1959 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1965 if (wParam == WMSZ_TOP ||
1966 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1976 int width, height, w, h, rv = 0;
1977 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
1978 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
1979 LPRECT r = (LPRECT) lParam;
1981 width = r->right - r->left - ex_width;
1982 height = r->bottom - r->top - ex_height;
1984 w = (width + cols/2)/cols;
1985 h = (height + rows/2)/rows;
1986 if ( r->right != r->left + w*cols + ex_width)
1989 if (wParam == WMSZ_LEFT ||
1990 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1991 r->left = r->right - w*cols - ex_width;
1993 r->right = r->left + w*cols + ex_width;
1995 if (r->bottom != r->top + h*rows + ex_height)
1998 if (wParam == WMSZ_TOP ||
1999 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2000 r->top = r->bottom - h*rows - ex_height;
2002 r->bottom = r->top + h*rows + ex_height;
2006 /* break; (never reached) */
2008 #ifdef RDB_DEBUG_PATCH
2009 debug((27, "WM_SIZE %s (%d,%d)",
2010 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2011 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2012 (wParam == SIZE_RESTORED && resizing) ? "to":
2013 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2015 LOWORD(lParam), HIWORD(lParam)));
2017 if (wParam == SIZE_MINIMIZED) {
2019 cfg.win_name_always ? window_name : icon_name);
2022 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2023 SetWindowText(hwnd, window_name);
2025 if (cfg.resize_action == RESIZE_DISABLED) {
2026 /* A resize, well it better be a minimize. */
2030 int width, height, w, h;
2032 width = LOWORD(lParam);
2033 height = HIWORD(lParam);
2036 if (wParam == SIZE_MAXIMIZED) {
2040 if (cfg.resize_action != RESIZE_FONT) {
2041 w = width / font_width;
2043 h = height / font_height;
2046 term_size(h, w, cfg.savelines);
2049 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2051 if (cfg.resize_action != RESIZE_FONT)
2052 term_size(prev_rows, prev_cols, cfg.savelines);
2054 } else if (was_full_screen) {
2055 was_full_screen = 0;
2056 if (cfg.resize_action != RESIZE_FONT)
2057 term_size(pre_fs_rows, pre_fs_cols, cfg.savelines);
2060 /* This is an unexpected resize, these will normally happen
2061 * if the window is too large. Probably either the user
2062 * selected a huge font or the screen size has changed.
2064 * This is also called with minimize.
2066 else reset_window(-1);
2070 * Don't call back->size in mid-resize. (To prevent
2071 * massive numbers of resize events getting sent
2072 * down the connection during an NT opaque drag.)
2075 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
2076 need_backend_resize = TRUE;
2077 w = (width-cfg.window_border*2) / font_width;
2079 h = (height-cfg.window_border*2) / font_height;
2090 switch (LOWORD(wParam)) {
2104 term_scroll(0, +rows / 2);
2107 term_scroll(0, -rows / 2);
2109 case SB_THUMBPOSITION:
2111 term_scroll(1, HIWORD(wParam));
2115 case WM_PALETTECHANGED:
2116 if ((HWND) wParam != hwnd && pal != NULL) {
2117 HDC hdc = get_ctx();
2119 if (RealizePalette(hdc) > 0)
2125 case WM_QUERYNEWPALETTE:
2127 HDC hdc = get_ctx();
2129 if (RealizePalette(hdc) > 0)
2141 * Add the scan code and keypress timing to the random
2144 noise_ultralight(lParam);
2147 * We don't do TranslateMessage since it disassociates the
2148 * resulting CHAR message from the KEYDOWN that sparked it,
2149 * which we occasionally don't want. Instead, we process
2150 * KEYDOWN, and call the Win32 translator functions so that
2151 * we get the translations under _our_ control.
2154 unsigned char buf[20];
2157 if (wParam == VK_PROCESSKEY) {
2160 m.message = WM_KEYDOWN;
2162 m.lParam = lParam & 0xdfff;
2163 TranslateMessage(&m);
2165 len = TranslateKey(message, wParam, lParam, buf);
2167 return DefWindowProc(hwnd, message, wParam, lParam);
2171 * Interrupt an ongoing paste. I'm not sure
2172 * this is sensible, but for the moment it's
2173 * preferable to having to faff about buffering
2179 * We need not bother about stdin backlogs
2180 * here, because in GUI PuTTY we can't do
2181 * anything about it anyway; there's no means
2182 * of asking Windows to hold off on KEYDOWN
2183 * messages. We _have_ to buffer everything
2186 ldisc_send(buf, len, 1);
2192 case WM_INPUTLANGCHANGE:
2194 /* wParam == Font number */
2195 /* lParam == Locale */
2197 HKL NewInputLocale = (HKL) lParam;
2199 // lParam == GetKeyboardLayout(0);
2201 GetLocaleInfo(LOWORD(NewInputLocale),
2202 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
2204 kbd_codepage = atoi(lbuf);
2207 case WM_IME_COMPOSITION:
2213 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2214 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2216 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2217 break; /* fall back to DefWindowProc */
2219 hIMC = ImmGetContext(hwnd);
2220 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2223 buff = (char*) smalloc(n);
2224 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2225 luni_send((unsigned short *)buff, n / 2, 1);
2228 ImmReleaseContext(hwnd, hIMC);
2233 if (wParam & 0xFF00) {
2234 unsigned char buf[2];
2237 buf[0] = wParam >> 8;
2238 lpage_send(kbd_codepage, buf, 2, 1);
2240 char c = (unsigned char) wParam;
2241 lpage_send(kbd_codepage, &c, 1, 1);
2247 * Nevertheless, we are prepared to deal with WM_CHAR
2248 * messages, should they crop up. So if someone wants to
2249 * post the things to us as part of a macro manoeuvre,
2250 * we're ready to cope.
2253 char c = (unsigned char)wParam;
2254 lpage_send(CP_ACP, &c, 1, 1);
2258 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2259 SetCursor(LoadCursor(NULL, IDC_ARROW));
2264 return DefWindowProc(hwnd, message, wParam, lParam);
2268 * Move the system caret. (We maintain one, even though it's
2269 * invisible, for the benefit of blind people: apparently some
2270 * helper software tracks the system caret, so we should arrange to
2273 void sys_cursor(int x, int y)
2278 if (!has_focus) return;
2280 SetCaretPos(x * font_width + offset_width,
2281 y * font_height + offset_height);
2283 /* IMM calls on Win98 and beyond only */
2284 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2286 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2287 osVersion.dwMinorVersion == 0) return; /* 95 */
2289 /* we should have the IMM functions */
2290 hIMC = ImmGetContext(hwnd);
2291 cf.dwStyle = CFS_POINT;
2292 cf.ptCurrentPos.x = x * font_width;
2293 cf.ptCurrentPos.y = y * font_height;
2294 ImmSetCompositionWindow(hIMC, &cf);
2296 ImmReleaseContext(hwnd, hIMC);
2300 * Draw a line of text in the window, at given character
2301 * coordinates, in given attributes.
2303 * We are allowed to fiddle with the contents of `text'.
2305 void do_text(Context ctx, int x, int y, char *text, int len,
2306 unsigned long attr, int lattr)
2309 int nfg, nbg, nfont;
2312 int force_manual_underline = 0;
2313 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2314 int char_width = fnt_width;
2315 int text_adjust = 0;
2316 static int *IpDx = 0, IpDxLEN = 0;
2318 if (attr & ATTR_WIDE)
2321 if (len > IpDxLEN || IpDx[0] != char_width) {
2323 if (len > IpDxLEN) {
2325 IpDx = smalloc((len + 16) * sizeof(int));
2326 IpDxLEN = (len + 16);
2328 for (i = 0; i < IpDxLEN; i++)
2329 IpDx[i] = char_width;
2332 /* Only want the left half of double width lines */
2333 if (lattr != LATTR_NORM && x*2 >= cols)
2341 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2342 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2343 attr ^= ATTR_CUR_XOR;
2347 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2348 /* Assume a poorman font is borken in other ways too. */
2358 nfont |= FONT_WIDE + FONT_HIGH;
2361 if (attr & ATTR_NARROW)
2362 nfont |= FONT_NARROW;
2364 /* Special hack for the VT100 linedraw glyphs. */
2365 if ((attr & CSET_MASK) == 0x2300) {
2366 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2367 switch ((unsigned char) (text[0])) {
2369 text_adjust = -2 * font_height / 5;
2372 text_adjust = -1 * font_height / 5;
2375 text_adjust = font_height / 5;
2378 text_adjust = 2 * font_height / 5;
2381 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2384 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2385 attr |= (unitab_xterm['q'] & CSET_MASK);
2386 if (attr & ATTR_UNDER) {
2387 attr &= ~ATTR_UNDER;
2388 force_manual_underline = 1;
2393 /* Anything left as an original character set is unprintable. */
2394 if (DIRECT_CHAR(attr)) {
2397 memset(text, 0xFD, len);
2401 if ((attr & CSET_MASK) == ATTR_OEMCP)
2404 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2405 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2406 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2408 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2409 nfont |= FONT_UNDERLINE;
2410 another_font(nfont);
2411 if (!fonts[nfont]) {
2412 if (nfont & FONT_UNDERLINE)
2413 force_manual_underline = 1;
2414 /* Don't do the same for manual bold, it could be bad news. */
2416 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2418 another_font(nfont);
2420 nfont = FONT_NORMAL;
2421 if (attr & ATTR_REVERSE) {
2426 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2428 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2432 SelectObject(hdc, fonts[nfont]);
2433 SetTextColor(hdc, fg);
2434 SetBkColor(hdc, bg);
2435 SetBkMode(hdc, OPAQUE);
2438 line_box.right = x + char_width * len;
2439 line_box.bottom = y + font_height;
2441 /* Only want the left half of double width lines */
2442 if (line_box.right > font_width*cols+offset_width)
2443 line_box.right = font_width*cols+offset_width;
2445 /* We're using a private area for direct to font. (512 chars.) */
2446 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2447 /* Ho Hum, dbcs fonts are a PITA! */
2448 /* To display on W9x I have to convert to UCS */
2449 static wchar_t *uni_buf = 0;
2450 static int uni_len = 0;
2452 if (len > uni_len) {
2454 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2457 for(nlen = mptr = 0; mptr<len; mptr++) {
2458 uni_buf[nlen] = 0xFFFD;
2459 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2460 IpDx[nlen] += char_width;
2461 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2462 text+mptr, 2, uni_buf+nlen, 1);
2467 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2468 text+mptr, 1, uni_buf+nlen, 1);
2476 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2477 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2478 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2479 SetBkMode(hdc, TRANSPARENT);
2480 ExtTextOutW(hdc, x - 1,
2481 y - font_height * (lattr ==
2482 LATTR_BOT) + text_adjust,
2483 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2487 } else if (DIRECT_FONT(attr)) {
2489 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2490 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2491 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2492 SetBkMode(hdc, TRANSPARENT);
2494 /* GRR: This draws the character outside it's box and can leave
2495 * 'droppings' even with the clip box! I suppose I could loop it
2496 * one character at a time ... yuk.
2498 * Or ... I could do a test print with "W", and use +1 or -1 for this
2499 * shift depending on if the leftmost column is blank...
2501 ExtTextOut(hdc, x - 1,
2502 y - font_height * (lattr ==
2503 LATTR_BOT) + text_adjust,
2504 ETO_CLIPPED, &line_box, text, len, IpDx);
2507 /* And 'normal' unicode characters */
2508 static WCHAR *wbuf = NULL;
2509 static int wlen = 0;
2514 wbuf = smalloc(wlen * sizeof(WCHAR));
2516 for (i = 0; i < len; i++)
2517 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2520 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2521 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2523 /* And the shadow bold hack. */
2524 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2525 SetBkMode(hdc, TRANSPARENT);
2526 ExtTextOutW(hdc, x - 1,
2527 y - font_height * (lattr ==
2528 LATTR_BOT) + text_adjust,
2529 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2532 if (lattr != LATTR_TOP && (force_manual_underline ||
2533 (und_mode == UND_LINE
2534 && (attr & ATTR_UNDER)))) {
2537 if (lattr == LATTR_BOT)
2538 dec = dec * 2 - font_height;
2540 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2541 MoveToEx(hdc, x, y + dec, NULL);
2542 LineTo(hdc, x + len * char_width, y + dec);
2543 oldpen = SelectObject(hdc, oldpen);
2544 DeleteObject(oldpen);
2548 void do_cursor(Context ctx, int x, int y, char *text, int len,
2549 unsigned long attr, int lattr)
2555 int ctype = cfg.cursor_type;
2557 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2558 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2559 do_text(ctx, x, y, text, len, attr, lattr);
2563 attr |= TATTR_RIGHTCURS;
2566 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2567 if (attr & ATTR_WIDE)
2574 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2577 pts[0].x = pts[1].x = pts[4].x = x;
2578 pts[2].x = pts[3].x = x + char_width - 1;
2579 pts[0].y = pts[3].y = pts[4].y = y;
2580 pts[1].y = pts[2].y = y + font_height - 1;
2581 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2582 Polyline(hdc, pts, 5);
2583 oldpen = SelectObject(hdc, oldpen);
2584 DeleteObject(oldpen);
2585 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2586 int startx, starty, dx, dy, length, i;
2589 starty = y + descent;
2592 length = char_width;
2595 if (attr & TATTR_RIGHTCURS)
2596 xadjust = char_width - 1;
2597 startx = x + xadjust;
2601 length = font_height;
2603 if (attr & TATTR_ACTCURS) {
2606 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2607 MoveToEx(hdc, startx, starty, NULL);
2608 LineTo(hdc, startx + dx * length, starty + dy * length);
2609 oldpen = SelectObject(hdc, oldpen);
2610 DeleteObject(oldpen);
2612 for (i = 0; i < length; i++) {
2614 SetPixel(hdc, startx, starty, colours[23]);
2623 /* This function gets the actual width of a character in the normal font.
2625 int CharWidth(Context ctx, int uc) {
2629 /* If the font max is the same as the font ave width then this
2630 * function is a no-op.
2632 if (!font_dualwidth) return 1;
2634 switch (uc & CSET_MASK) {
2636 uc = unitab_line[uc & 0xFF];
2639 uc = unitab_xterm[uc & 0xFF];
2642 uc = unitab_scoacs[uc & 0xFF];
2645 if (DIRECT_FONT(uc)) {
2646 if (dbcs_screenfont) return 1;
2648 /* Speedup, I know of no font where ascii is the wrong width */
2649 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2652 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2653 SelectObject(hdc, fonts[FONT_NORMAL]);
2654 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2655 another_font(FONT_OEM);
2656 if (!fonts[FONT_OEM]) return 0;
2658 SelectObject(hdc, fonts[FONT_OEM]);
2662 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2663 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2666 /* Speedup, I know of no font where ascii is the wrong width */
2667 if (uc >= ' ' && uc <= '~') return 1;
2669 SelectObject(hdc, fonts[FONT_NORMAL]);
2670 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2671 /* Okay that one worked */ ;
2672 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2673 /* This should work on 9x too, but it's "less accurate" */ ;
2678 ibuf += font_width / 2 -1;
2685 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2686 * codes. Returns number of bytes used or zero to drop the message
2687 * or -1 to forward the message to windows.
2689 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2690 unsigned char *output)
2693 int scan, left_alt = 0, key_down, shift_state;
2695 unsigned char *p = output;
2696 static int alt_sum = 0;
2698 HKL kbd_layout = GetKeyboardLayout(0);
2700 static WORD keys[3];
2701 static int compose_char = 0;
2702 static WPARAM compose_key = 0;
2704 r = GetKeyboardState(keystate);
2706 memset(keystate, 0, sizeof(keystate));
2709 #define SHOW_TOASCII_RESULT
2710 { /* Tell us all about key events */
2711 static BYTE oldstate[256];
2712 static int first = 1;
2716 memcpy(oldstate, keystate, sizeof(oldstate));
2719 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2721 } else if ((HIWORD(lParam) & KF_UP)
2722 && scan == (HIWORD(lParam) & 0xFF)) {
2726 if (wParam >= VK_F1 && wParam <= VK_F20)
2727 debug(("K_F%d", wParam + 1 - VK_F1));
2740 debug(("VK_%02x", wParam));
2742 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2744 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2746 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2747 if (ch >= ' ' && ch <= '~')
2748 debug((", '%c'", ch));
2750 debug((", $%02x", ch));
2753 debug((", KB0=%02x", keys[0]));
2755 debug((", KB1=%02x", keys[1]));
2757 debug((", KB2=%02x", keys[2]));
2759 if ((keystate[VK_SHIFT] & 0x80) != 0)
2761 if ((keystate[VK_CONTROL] & 0x80) != 0)
2763 if ((HIWORD(lParam) & KF_EXTENDED))
2765 if ((HIWORD(lParam) & KF_UP))
2769 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2770 else if ((HIWORD(lParam) & KF_UP))
2771 oldstate[wParam & 0xFF] ^= 0x80;
2773 oldstate[wParam & 0xFF] ^= 0x81;
2775 for (ch = 0; ch < 256; ch++)
2776 if (oldstate[ch] != keystate[ch])
2777 debug((", M%02x=%02x", ch, keystate[ch]));
2779 memcpy(oldstate, keystate, sizeof(oldstate));
2783 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2784 keystate[VK_RMENU] = keystate[VK_MENU];
2788 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2789 if ((cfg.funky_type == 3 ||
2790 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2791 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2793 wParam = VK_EXECUTE;
2795 /* UnToggle NUMLock */
2796 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2797 keystate[VK_NUMLOCK] ^= 1;
2800 /* And write back the 'adjusted' state */
2801 SetKeyboardState(keystate);
2804 /* Disable Auto repeat if required */
2805 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2808 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2811 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2813 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2814 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2815 if (cfg.ctrlaltkeys)
2816 keystate[VK_MENU] = 0;
2818 keystate[VK_RMENU] = 0x80;
2823 alt_pressed = (left_alt && key_down);
2825 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2826 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2827 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2829 /* Note if AltGr was pressed and if it was used as a compose key */
2830 if (!compose_state) {
2831 compose_key = 0x100;
2832 if (cfg.compose_key) {
2833 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2834 compose_key = wParam;
2836 if (wParam == VK_APPS)
2837 compose_key = wParam;
2840 if (wParam == compose_key) {
2841 if (compose_state == 0
2842 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2844 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2848 } else if (compose_state == 1 && wParam != VK_CONTROL)
2852 * Record that we pressed key so the scroll window can be reset, but
2853 * be careful to avoid Shift-UP/Down
2855 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2859 if (compose_state > 1 && left_alt)
2862 /* Sanitize the number pad if not using a PC NumPad */
2863 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2864 && cfg.funky_type != 2)
2865 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2866 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2870 nParam = VK_NUMPAD0;
2873 nParam = VK_NUMPAD1;
2876 nParam = VK_NUMPAD2;
2879 nParam = VK_NUMPAD3;
2882 nParam = VK_NUMPAD4;
2885 nParam = VK_NUMPAD5;
2888 nParam = VK_NUMPAD6;
2891 nParam = VK_NUMPAD7;
2894 nParam = VK_NUMPAD8;
2897 nParam = VK_NUMPAD9;
2900 nParam = VK_DECIMAL;
2904 if (keystate[VK_NUMLOCK] & 1)
2911 /* If a key is pressed and AltGr is not active */
2912 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2913 /* Okay, prepare for most alts then ... */
2917 /* Lets see if it's a pattern we know all about ... */
2918 if (wParam == VK_PRIOR && shift_state == 1) {
2919 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2922 if (wParam == VK_NEXT && shift_state == 1) {
2923 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2926 if (wParam == VK_INSERT && shift_state == 1) {
2930 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2933 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2934 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2937 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter) {
2941 /* Control-Numlock for app-keypad mode switch */
2942 if (wParam == VK_PAUSE && shift_state == 2) {
2943 app_keypad_keys ^= 1;
2947 /* Nethack keypad */
2948 if (cfg.nethack_keypad && !left_alt) {
2951 *p++ = shift_state ? 'B' : 'b';
2954 *p++ = shift_state ? 'J' : 'j';
2957 *p++ = shift_state ? 'N' : 'n';
2960 *p++ = shift_state ? 'H' : 'h';
2963 *p++ = shift_state ? '.' : '.';
2966 *p++ = shift_state ? 'L' : 'l';
2969 *p++ = shift_state ? 'Y' : 'y';
2972 *p++ = shift_state ? 'K' : 'k';
2975 *p++ = shift_state ? 'U' : 'u';
2980 /* Application Keypad */
2984 if (cfg.funky_type == 3 ||
2985 (cfg.funky_type <= 1 &&
2986 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3000 if (app_keypad_keys && !cfg.no_applic_k)
3037 if (cfg.funky_type == 2) {
3042 } else if (shift_state)
3049 if (cfg.funky_type == 2)
3053 if (cfg.funky_type == 2)
3057 if (cfg.funky_type == 2)
3062 if (HIWORD(lParam) & KF_EXTENDED)
3068 if (xkey >= 'P' && xkey <= 'S')
3069 p += sprintf((char *) p, "\x1B%c", xkey);
3071 p += sprintf((char *) p, "\x1B?%c", xkey);
3073 p += sprintf((char *) p, "\x1BO%c", xkey);
3078 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
3079 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3083 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3089 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3093 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3097 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3102 if (wParam == VK_PAUSE) { /* Break/Pause */
3107 /* Control-2 to Control-8 are special */
3108 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3109 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3112 if (shift_state == 2 && wParam == 0xBD) {
3116 if (shift_state == 2 && wParam == 0xDF) {
3120 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3127 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3128 * for integer decimal nn.)
3130 * We also deal with the weird ones here. Linux VCs replace F1
3131 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3132 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3138 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3141 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3144 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3147 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3150 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3153 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3156 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3159 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3162 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3165 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3198 if ((shift_state&2) == 0) switch (wParam) {
3218 /* Reorder edit keys to physical order */
3219 if (cfg.funky_type == 3 && code <= 6)
3220 code = "\0\2\1\4\5\3\6"[code];
3222 if (vt52_mode && code > 0 && code <= 6) {
3223 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3227 if (cfg.funky_type == 5 && /* SCO function keys */
3228 code >= 11 && code <= 34) {
3229 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3232 case VK_F1: index = 0; break;
3233 case VK_F2: index = 1; break;
3234 case VK_F3: index = 2; break;
3235 case VK_F4: index = 3; break;
3236 case VK_F5: index = 4; break;
3237 case VK_F6: index = 5; break;
3238 case VK_F7: index = 6; break;
3239 case VK_F8: index = 7; break;
3240 case VK_F9: index = 8; break;
3241 case VK_F10: index = 9; break;
3242 case VK_F11: index = 10; break;
3243 case VK_F12: index = 11; break;
3245 if (keystate[VK_SHIFT] & 0x80) index += 12;
3246 if (keystate[VK_CONTROL] & 0x80) index += 24;
3247 p += sprintf((char *) p, "\x1B[%c", codes[index]);
3250 if (cfg.funky_type == 5 && /* SCO small keypad */
3251 code >= 1 && code <= 6) {
3252 char codes[] = "HL.FIG";
3256 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3260 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3267 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3270 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3273 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3274 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3277 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3279 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3281 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3284 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3285 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3289 p += sprintf((char *) p, "\x1B[%d~", code);
3294 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3295 * some reason seems to send VK_CLEAR to Windows...).
3318 p += sprintf((char *) p, "\x1B%c", xkey);
3320 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3321 /* VT100 & VT102 manuals both state the app cursor keys
3322 * only work if the app keypad is on.
3324 if (!app_keypad_keys)
3326 /* Useful mapping of Ctrl-arrows */
3327 if (shift_state == 2)
3331 p += sprintf((char *) p, "\x1BO%c", xkey);
3333 p += sprintf((char *) p, "\x1B[%c", xkey);
3340 * Finally, deal with Return ourselves. (Win95 seems to
3341 * foul it up when Alt is pressed, for some reason.)
3343 if (wParam == VK_RETURN) { /* Return */
3349 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3350 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3355 /* Okay we've done everything interesting; let windows deal with
3356 * the boring stuff */
3360 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3361 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3363 keystate[VK_CAPITAL] = 0;
3366 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3367 #ifdef SHOW_TOASCII_RESULT
3368 if (r == 1 && !key_down) {
3370 if (in_utf || dbcs_screenfont)
3371 debug((", (U+%04x)", alt_sum));
3373 debug((", LCH(%d)", alt_sum));
3375 debug((", ACH(%d)", keys[0]));
3380 for (r1 = 0; r1 < r; r1++) {
3381 debug(("%s%d", r1 ? "," : "", keys[r1]));
3390 * Interrupt an ongoing paste. I'm not sure this is
3391 * sensible, but for the moment it's preferable to
3392 * having to faff about buffering things.
3397 for (i = 0; i < r; i++) {
3398 unsigned char ch = (unsigned char) keys[i];
3400 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3405 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3409 if ((nc = check_compose(compose_char, ch)) == -1) {
3410 MessageBeep(MB_ICONHAND);
3414 luni_send(&keybuf, 1, 1);
3422 if (in_utf || dbcs_screenfont) {
3424 luni_send(&keybuf, 1, 1);
3426 ch = (char) alt_sum;
3428 * We need not bother about stdin
3429 * backlogs here, because in GUI PuTTY
3430 * we can't do anything about it
3431 * anyway; there's no means of asking
3432 * Windows to hold off on KEYDOWN
3433 * messages. We _have_ to buffer
3434 * everything we're sent.
3436 ldisc_send(&ch, 1, 1);
3440 lpage_send(kbd_codepage, &ch, 1, 1);
3442 if(capsOn && ch < 0x80) {
3445 cbuf[1] = xlat_uskbd2cyrllic(ch);
3446 luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3451 lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3457 /* This is so the ALT-Numpad and dead keys work correctly. */
3462 /* If we're definitly not building up an ALT-54321 then clear it */
3465 /* If we will be using alt_sum fix the 256s */
3466 else if (keys[0] && (in_utf || dbcs_screenfont))
3471 * ALT alone may or may not want to bring up the System menu.
3472 * If it's not meant to, we return 0 on presses or releases of
3473 * ALT, to show that we've swallowed the keystroke. Otherwise
3474 * we return -1, which means Windows will give the keystroke
3475 * its default handling (i.e. bring up the System menu).
3477 if (wParam == VK_MENU && !cfg.alt_only)
3483 void set_title(char *title)
3486 window_name = smalloc(1 + strlen(title));
3487 strcpy(window_name, title);
3488 if (cfg.win_name_always || !IsIconic(hwnd))
3489 SetWindowText(hwnd, title);
3492 void set_icon(char *title)
3495 icon_name = smalloc(1 + strlen(title));
3496 strcpy(icon_name, title);
3497 if (!cfg.win_name_always && IsIconic(hwnd))
3498 SetWindowText(hwnd, title);
3501 void set_sbar(int total, int start, int page)
3508 si.cbSize = sizeof(si);
3509 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3511 si.nMax = total - 1;
3515 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3518 Context get_ctx(void)
3524 SelectPalette(hdc, pal, FALSE);
3530 void free_ctx(Context ctx)
3532 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3533 ReleaseDC(hwnd, ctx);
3536 static void real_palette_set(int n, int r, int g, int b)
3539 logpal->palPalEntry[n].peRed = r;
3540 logpal->palPalEntry[n].peGreen = g;
3541 logpal->palPalEntry[n].peBlue = b;
3542 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3543 colours[n] = PALETTERGB(r, g, b);
3544 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3546 colours[n] = RGB(r, g, b);
3549 void palette_set(int n, int r, int g, int b)
3551 static const int first[21] = {
3552 0, 2, 4, 6, 8, 10, 12, 14,
3553 1, 3, 5, 7, 9, 11, 13, 15,
3556 real_palette_set(first[n], r, g, b);
3558 real_palette_set(first[n] + 1, r, g, b);
3560 HDC hdc = get_ctx();
3561 UnrealizeObject(pal);
3562 RealizePalette(hdc);
3567 void palette_reset(void)
3571 for (i = 0; i < NCOLOURS; i++) {
3573 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3574 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3575 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3576 logpal->palPalEntry[i].peFlags = 0;
3577 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3578 defpal[i].rgbtGreen,
3579 defpal[i].rgbtBlue);
3581 colours[i] = RGB(defpal[i].rgbtRed,
3582 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3587 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3589 RealizePalette(hdc);
3594 void write_aclip(char *data, int len, int must_deselect)
3599 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3602 lock = GlobalLock(clipdata);
3605 memcpy(lock, data, len);
3606 ((unsigned char *) lock)[len] = 0;
3607 GlobalUnlock(clipdata);
3610 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3612 if (OpenClipboard(hwnd)) {
3614 SetClipboardData(CF_TEXT, clipdata);
3617 GlobalFree(clipdata);
3620 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3624 * Note: unlike write_aclip() this will not append a nul.
3626 void write_clip(wchar_t * data, int len, int must_deselect)
3633 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3635 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3636 len * sizeof(wchar_t));
3637 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3639 if (!clipdata || !clipdata2) {
3641 GlobalFree(clipdata);
3643 GlobalFree(clipdata2);
3646 if (!(lock = GlobalLock(clipdata)))
3648 if (!(lock2 = GlobalLock(clipdata2)))
3651 memcpy(lock, data, len * sizeof(wchar_t));
3652 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3654 GlobalUnlock(clipdata);
3655 GlobalUnlock(clipdata2);
3658 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3660 if (OpenClipboard(hwnd)) {
3662 SetClipboardData(CF_UNICODETEXT, clipdata);
3663 SetClipboardData(CF_TEXT, clipdata2);
3666 GlobalFree(clipdata);
3667 GlobalFree(clipdata2);
3671 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3674 void get_clip(wchar_t ** p, int *len)
3676 static HGLOBAL clipdata = NULL;
3677 static wchar_t *converted = 0;
3686 GlobalUnlock(clipdata);
3689 } else if (OpenClipboard(NULL)) {
3690 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3692 *p = GlobalLock(clipdata);
3694 for (p2 = *p; *p2; p2++);
3698 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3702 s = GlobalLock(clipdata);
3703 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3704 *p = converted = smalloc(i * sizeof(wchar_t));
3705 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3718 * Move `lines' lines from position `from' to position `to' in the
3721 void optimised_move(int to, int from, int lines)
3726 min = (to < from ? to : from);
3727 max = to + from - min;
3729 r.left = offset_width;
3730 r.right = offset_width + cols * font_width;
3731 r.top = offset_height + min * font_height;
3732 r.bottom = offset_height + (max + lines) * font_height;
3733 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3738 * Print a message box and perform a fatal exit.
3740 void fatalbox(char *fmt, ...)
3746 vsprintf(stuff, fmt, ap);
3748 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3753 * Manage window caption / taskbar flashing, if enabled.
3754 * 0 = stop, 1 = maintain, 2 = start
3756 static void flash_window(int mode)
3758 static long last_flash = 0;
3759 static int flashing = 0;
3760 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3763 FlashWindow(hwnd, FALSE);
3767 } else if (mode == 2) {
3770 last_flash = GetTickCount();
3772 FlashWindow(hwnd, TRUE);
3775 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3778 long now = GetTickCount();
3779 long fdiff = now - last_flash;
3780 if (fdiff < 0 || fdiff > 450) {
3782 FlashWindow(hwnd, TRUE); /* toggle */
3793 if (mode == BELL_DEFAULT) {
3795 * For MessageBeep style bells, we want to be careful of
3796 * timing, because they don't have the nice property of
3797 * PlaySound bells that each one cancels the previous
3798 * active one. So we limit the rate to one per 50ms or so.
3800 static long lastbeep = 0;
3803 beepdiff = GetTickCount() - lastbeep;
3804 if (beepdiff >= 0 && beepdiff < 50)
3808 * The above MessageBeep call takes time, so we record the
3809 * time _after_ it finishes rather than before it starts.
3811 lastbeep = GetTickCount();
3812 } else if (mode == BELL_WAVEFILE) {
3813 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3814 char buf[sizeof(cfg.bell_wavefile) + 80];
3815 sprintf(buf, "Unable to play sound file\n%s\n"
3816 "Using default sound instead", cfg.bell_wavefile);
3817 MessageBox(hwnd, buf, "PuTTY Sound Error",
3818 MB_OK | MB_ICONEXCLAMATION);
3819 cfg.beep = BELL_DEFAULT;
3822 /* Otherwise, either visual bell or disabled; do nothing here */
3824 flash_window(2); /* start */
3829 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3831 * Revised by <wez@thebrainroom.com>
3833 static void flip_full_screen(void)
3835 want_full_screen = !want_full_screen;
3837 if (full_screen == want_full_screen)
3840 full_screen = want_full_screen;
3842 old_wind_placement.length = sizeof(old_wind_placement);
3846 #if !defined(NO_MULTIMON) && defined(MONITOR_DEFAULTTONEAREST)
3847 /* The multi-monitor safe way of doing things */
3851 mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
3852 mi.cbSize = sizeof(mi);
3853 GetMonitorInfo(mon, &mi);
3854 x = mi.rcMonitor.left;
3855 y = mi.rcMonitor.top;
3856 cx = mi.rcMonitor.right;
3857 cy = mi.rcMonitor.bottom;
3859 /* good old fashioned way of doing it */
3862 cx = GetSystemMetrics(SM_CXSCREEN);
3863 cy = GetSystemMetrics(SM_CYSCREEN);
3866 /* save rows for when we "restore" back down again */
3870 GetWindowPlacement(hwnd, &old_wind_placement);
3871 SetWindowLong(hwnd, GWL_STYLE,
3872 GetWindowLong(hwnd, GWL_STYLE)
3873 & ~((cfg.scrollbar_in_fullscreen ? 0 : WS_VSCROLL)
3874 | WS_CAPTION | WS_BORDER | WS_THICKFRAME));
3875 /* become topmost */
3876 SetWindowPos(hwnd, HWND_TOP, x, y, cx, cy, SWP_FRAMECHANGED);
3878 was_full_screen = 1;
3879 SetWindowLong(hwnd, GWL_STYLE,
3880 GetWindowLong(hwnd, GWL_STYLE)
3881 | (cfg.scrollbar ? WS_VSCROLL : 0)
3882 | WS_CAPTION | WS_BORDER | WS_THICKFRAME);
3883 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
3884 SWP_NOMOVE|SWP_NOSIZE|SWP_FRAMECHANGED);
3885 SetWindowPlacement(hwnd,&old_wind_placement);
3887 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
3888 MF_BYCOMMAND| full_screen ? MF_CHECKED : MF_UNCHECKED);