15 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
20 #define IDM_SHOWLOG 0x0010
21 #define IDM_NEWSESS 0x0020
22 #define IDM_DUPSESS 0x0030
23 #define IDM_RECONF 0x0040
24 #define IDM_CLRSB 0x0050
25 #define IDM_RESET 0x0060
26 #define IDM_TEL_AYT 0x0070
27 #define IDM_TEL_BRK 0x0080
28 #define IDM_TEL_SYNCH 0x0090
29 #define IDM_TEL_EC 0x00a0
30 #define IDM_TEL_EL 0x00b0
31 #define IDM_TEL_GA 0x00c0
32 #define IDM_TEL_NOP 0x00d0
33 #define IDM_TEL_ABORT 0x00e0
34 #define IDM_TEL_AO 0x00f0
35 #define IDM_TEL_IP 0x0100
36 #define IDM_TEL_SUSP 0x0110
37 #define IDM_TEL_EOR 0x0120
38 #define IDM_TEL_EOF 0x0130
39 #define IDM_ABOUT 0x0140
40 #define IDM_SAVEDSESS 0x0150
42 #define IDM_SAVED_MIN 0x1000
43 #define IDM_SAVED_MAX 0x2000
45 #define WM_IGNORE_SIZE (WM_XUSER + 1)
46 #define WM_IGNORE_CLIP (WM_XUSER + 2)
48 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
49 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
50 static void cfgtopalette(void);
51 static void init_palette(void);
52 static void init_fonts(int);
54 static int extra_width, extra_height;
56 static int pending_netevent = 0;
57 static WPARAM pend_netevent_wParam = 0;
58 static LPARAM pend_netevent_lParam = 0;
59 static void enact_pending_netevent(void);
61 static time_t last_movement = 0;
65 #define FONT_UNDERLINE 2
66 #define FONT_BOLDUND 3
68 #define FONT_OEMBOLD 5
69 #define FONT_OEMBOLDUND 6
71 static HFONT fonts[8];
72 static int font_needs_hand_underlining;
74 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
82 static COLORREF colours[NCOLOURS];
84 static LPLOGPALETTE logpal;
85 static RGBTRIPLE defpal[NCOLOURS];
89 static HBITMAP caretbm;
91 static int dbltime, lasttime, lastact;
92 static Mouse_Button lastbtn;
94 static char *window_name, *icon_name;
96 static Ldisc *real_ldisc;
98 void begin_session(void) {
102 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
103 static char appname[] = "PuTTY";
108 int guess_width, guess_height;
111 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
113 winsock_ver = MAKEWORD(1, 1);
114 if (WSAStartup(winsock_ver, &wsadata)) {
115 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
116 MB_OK | MB_ICONEXCLAMATION);
119 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
120 MessageBox(NULL, "WinSock version is incompatible with 1.1",
121 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
125 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
127 InitCommonControls();
130 * Process the command line.
135 default_protocol = DEFAULT_PROTOCOL;
136 default_port = DEFAULT_PORT;
138 do_defaults(NULL, &cfg);
141 while (*p && isspace(*p)) p++;
144 * Process command line options first. Yes, this can be
145 * done better, and it will be as soon as I have the
149 char *q = p + strcspn(p, " \t");
152 tolower(p[0]) == 's' &&
153 tolower(p[1]) == 's' &&
154 tolower(p[2]) == 'h') {
155 default_protocol = cfg.protocol = PROT_SSH;
156 default_port = cfg.port = 22;
157 } else if (q == p + 3 &&
158 tolower(p[0]) == 'l' &&
159 tolower(p[1]) == 'o' &&
160 tolower(p[2]) == 'g') {
161 logfile = "putty.log";
162 } else if (q == p + 7 &&
163 tolower(p[0]) == 'c' &&
164 tolower(p[1]) == 'l' &&
165 tolower(p[2]) == 'e' &&
166 tolower(p[3]) == 'a' &&
167 tolower(p[4]) == 'n' &&
168 tolower(p[5]) == 'u' &&
169 tolower(p[6]) == 'p') {
171 * `putty -cleanup'. Remove all registry entries
172 * associated with PuTTY, and also find and delete
173 * the random seed file.
176 "This procedure will remove ALL Registry\n"
177 "entries associated with PuTTY, and will\n"
178 "also remove the PuTTY random seed file.\n"
180 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
181 "SESSIONS. Are you really sure you want\n"
184 MB_YESNO | MB_ICONWARNING) == IDYES) {
189 p = q + strspn(q, " \t");
193 * An initial @ means to activate a saved session.
196 do_defaults (p+1, &cfg);
197 if (!*cfg.host && !do_config()) {
201 } else if (*p == '&') {
203 * An initial & means we've been given a command line
204 * containing the hex value of a HANDLE for a file
205 * mapping object, which we must then extract as a
210 if (sscanf(p+1, "%p", &filemap) == 1 &&
211 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
212 0, 0, sizeof(Config))) != NULL) {
215 CloseHandle(filemap);
216 } else if (!do_config()) {
223 * If the hostname starts with "telnet:", set the
224 * protocol to Telnet and process the string as a
227 if (!strncmp(q, "telnet:", 7)) {
231 if (q[0] == '/' && q[1] == '/')
233 cfg.protocol = PROT_TELNET;
235 while (*p && *p != ':' && *p != '/') p++;
243 strncpy (cfg.host, q, sizeof(cfg.host)-1);
244 cfg.host[sizeof(cfg.host)-1] = '\0';
246 while (*p && !isspace(*p)) p++;
249 strncpy (cfg.host, q, sizeof(cfg.host)-1);
250 cfg.host[sizeof(cfg.host)-1] = '\0';
251 while (*p && isspace(*p)) p++;
264 /* See if host is of the form user@host */
265 if (cfg.host[0] != '\0') {
266 char *atsign = strchr(cfg.host, '@');
267 /* Make sure we're not overflowing the user field */
269 if (atsign-cfg.host < sizeof cfg.username) {
270 strncpy (cfg.username, cfg.host, atsign-cfg.host);
271 cfg.username[atsign-cfg.host] = '\0';
273 memmove(cfg.host, atsign+1, 1+strlen(atsign+1));
279 * Select protocol. This is farmed out into a table in a
280 * separate file to enable an ssh-free variant.
285 for (i = 0; backends[i].backend != NULL; i++)
286 if (backends[i].protocol == cfg.protocol) {
287 back = backends[i].backend;
291 MessageBox(NULL, "Unsupported protocol number found",
292 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
298 real_ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
299 /* To start with, we use the simple line discipline, so we can
300 * type passwords etc without fear of them being echoed... */
301 ldisc = &ldisc_simple;
305 wndclass.lpfnWndProc = WndProc;
306 wndclass.cbClsExtra = 0;
307 wndclass.cbWndExtra = 0;
308 wndclass.hInstance = inst;
309 wndclass.hIcon = LoadIcon (inst,
310 MAKEINTRESOURCE(IDI_MAINICON));
311 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
312 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
313 wndclass.lpszMenuName = NULL;
314 wndclass.lpszClassName = appname;
316 RegisterClass (&wndclass);
321 savelines = cfg.savelines;
327 * Guess some defaults for the window size. This all gets
328 * updated later, so we don't really care too much. However, we
329 * do want the font width/height guesses to correspond to a
330 * large font rather than a small one...
337 term_size (cfg.height, cfg.width, cfg.savelines);
338 guess_width = extra_width + font_width * cols;
339 guess_height = extra_height + font_height * rows;
342 HWND w = GetDesktopWindow();
343 GetWindowRect (w, &r);
344 if (guess_width > r.right - r.left)
345 guess_width = r.right - r.left;
346 if (guess_height > r.bottom - r.top)
347 guess_height = r.bottom - r.top;
351 int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
352 if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
353 if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
354 hwnd = CreateWindow (appname, appname,
356 CW_USEDEFAULT, CW_USEDEFAULT,
357 guess_width, guess_height,
358 NULL, NULL, inst, NULL);
362 * Initialise the fonts, simultaneously correcting the guesses
363 * for font_{width,height}.
365 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
370 * Correct the guesses for extra_{width,height}.
374 GetWindowRect (hwnd, &wr);
375 GetClientRect (hwnd, &cr);
376 extra_width = wr.right - wr.left - cr.right + cr.left;
377 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
381 * Resize the window, now we know what size we _really_ want it
384 guess_width = extra_width + font_width * cols;
385 guess_height = extra_height + font_height * rows;
386 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
387 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
388 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
391 * Set up a caret bitmap, with no content.
395 int size = (font_width+15)/16 * 2 * font_height;
396 bits = calloc(size, 1);
397 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
402 * Initialise the scroll bar.
407 si.cbSize = sizeof(si);
408 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
413 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
417 * Start up the telnet connection.
421 char msg[1024], *title;
424 error = back->init (hwnd, cfg.host, cfg.port, &realhost);
426 sprintf(msg, "Unable to open connection:\n%s", error);
427 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
430 window_name = icon_name = NULL;
432 title = cfg.wintitle;
434 sprintf(msg, "%s - PuTTY", realhost);
441 session_closed = FALSE;
444 * Set up the input and output buffers.
447 outbuf_reap = outbuf_head = 0;
450 * Prepare the mouse handler.
452 lastact = MA_NOTHING;
453 lastbtn = MB_NOTHING;
454 dbltime = GetDoubleClickTime();
457 * Set up the session-control options on the system menu.
460 HMENU m = GetSystemMenu (hwnd, FALSE);
464 AppendMenu (m, MF_SEPARATOR, 0, 0);
465 if (cfg.protocol == PROT_TELNET) {
467 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
468 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
469 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
470 AppendMenu (p, MF_SEPARATOR, 0, 0);
471 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
472 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
473 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
474 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
475 AppendMenu (p, MF_SEPARATOR, 0, 0);
476 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
477 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
478 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
479 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
480 AppendMenu (p, MF_SEPARATOR, 0, 0);
481 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
482 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
483 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
484 AppendMenu (m, MF_SEPARATOR, 0, 0);
486 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
487 AppendMenu (m, MF_SEPARATOR, 0, 0);
488 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
489 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
492 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
493 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
494 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
495 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
496 AppendMenu (m, MF_SEPARATOR, 0, 0);
497 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
498 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
499 AppendMenu (m, MF_SEPARATOR, 0, 0);
500 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
504 * Finally show the window!
506 ShowWindow (hwnd, show);
509 * Set the palette up.
515 has_focus = (GetForegroundWindow() == hwnd);
518 if (GetMessage (&msg, NULL, 0, 0) == 1)
520 int timer_id = 0, long_timer = 0;
522 while (msg.message != WM_QUIT) {
523 /* Sometimes DispatchMessage calls routines that use their own
524 * GetMessage loop, setup this timer so we get some control back.
526 * Also call term_update() from the timer so that if the host
527 * is sending data flat out we still do redraws.
529 if(timer_id && long_timer) {
530 KillTimer(hwnd, timer_id);
531 long_timer = timer_id = 0;
534 timer_id = SetTimer(hwnd, 1, 20, NULL);
535 DispatchMessage (&msg);
537 /* Make sure we blink everything that needs it. */
540 /* Send the paste buffer if there's anything to send */
543 /* If there's nothing new in the queue then we can do everything
544 * we've delayed, reading the socket, writing, and repainting
547 if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
550 if (pending_netevent) {
551 enact_pending_netevent();
553 /* Force the cursor blink on */
556 if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
560 /* Okay there is now nothing to do so we make sure the screen is
561 * completely up to date then tell windows to call us in a little
565 KillTimer(hwnd, timer_id);
574 timer_id = SetTimer(hwnd, 1, 59500, NULL);
576 timer_id = SetTimer(hwnd, 1, 250, NULL);
579 /* There's no point rescanning everything in the message queue
580 * so we do an apperently unneccesary wait here
583 if (GetMessage (&msg, NULL, 0, 0) != 1)
595 DeleteObject(fonts[i]);
602 if (cfg.protocol == PROT_SSH) {
613 * Print a message box and close the connection.
615 void connection_fatal(char *fmt, ...) {
620 vsprintf(stuff, fmt, ap);
622 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
623 if (cfg.close_on_exit)
626 session_closed = TRUE;
627 SetWindowText (hwnd, "PuTTY (inactive)");
632 * Actually do the job requested by a WM_NETEVENT
634 static void enact_pending_netevent(void) {
636 static int reentering = 0;
639 return; /* don't unpend the pending */
641 pending_netevent = FALSE;
644 i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
649 switch (WSABASEERR + (-i) % 10000) {
651 sprintf(buf, "Connection reset by peer");
654 sprintf(buf, "Unexpected network error %d", -i);
657 connection_fatal(buf);
660 if (cfg.close_on_exit)
663 session_closed = TRUE;
664 MessageBox(hwnd, "Connection closed by remote host",
665 "PuTTY", MB_OK | MB_ICONINFORMATION);
666 SetWindowText (hwnd, "PuTTY (inactive)");
672 * Copy the colour palette from the configuration data into defpal.
673 * This is non-trivial because the colour indices are different.
675 static void cfgtopalette(void) {
677 static const int ww[] = {
678 6, 7, 8, 9, 10, 11, 12, 13,
679 14, 15, 16, 17, 18, 19, 20, 21,
680 0, 1, 2, 3, 4, 4, 5, 5
683 for (i=0; i<24; i++) {
685 defpal[i].rgbtRed = cfg.colours[w][0];
686 defpal[i].rgbtGreen = cfg.colours[w][1];
687 defpal[i].rgbtBlue = cfg.colours[w][2];
692 * Set up the colour palette.
694 static void init_palette(void) {
696 HDC hdc = GetDC (hwnd);
698 if (cfg.try_palette &&
699 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
700 logpal = smalloc(sizeof(*logpal)
701 - sizeof(logpal->palPalEntry)
702 + NCOLOURS * sizeof(PALETTEENTRY));
703 logpal->palVersion = 0x300;
704 logpal->palNumEntries = NCOLOURS;
705 for (i = 0; i < NCOLOURS; i++) {
706 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
707 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
708 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
709 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
711 pal = CreatePalette (logpal);
713 SelectPalette (hdc, pal, FALSE);
714 RealizePalette (hdc);
715 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
719 ReleaseDC (hwnd, hdc);
722 for (i=0; i<NCOLOURS; i++)
723 colours[i] = PALETTERGB(defpal[i].rgbtRed,
727 for(i=0; i<NCOLOURS; i++)
728 colours[i] = RGB(defpal[i].rgbtRed,
734 * Initialise all the fonts we will need. There may be as many as
735 * eight or as few as one. We also:
737 * - check the font width and height, correcting our guesses if
740 * - verify that the bold font is the same width as the ordinary
741 * one, and engage shadow bolding if not.
743 * - verify that the underlined font is the same width as the
744 * ordinary one (manual underlining by means of line drawing can
745 * be done in a pinch).
747 static void init_fonts(int pick_width) {
752 int fw_dontcare, fw_bold;
761 if (cfg.fontisbold) {
762 fw_dontcare = FW_BOLD;
765 fw_dontcare = FW_DONTCARE;
771 font_height = cfg.fontheight;
772 font_width = pick_width;
775 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
776 c, OUT_DEFAULT_PRECIS, \
777 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
778 FIXED_PITCH | FF_DONTCARE, cfg.font)
780 if (cfg.vtmode != VT_OEMONLY) {
781 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
783 SelectObject (hdc, fonts[FONT_NORMAL]);
784 GetTextMetrics(hdc, &tm);
785 font_height = tm.tmHeight;
786 font_width = tm.tmAveCharWidth;
788 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
791 * Some fonts, e.g. 9-pt Courier, draw their underlines
792 * outside their character cell. We successfully prevent
793 * screen corruption by clipping the text output, but then
794 * we lose the underline completely. Here we try to work
795 * out whether this is such a font, and if it is, we set a
796 * flag that causes underlines to be drawn by hand.
798 * Having tried other more sophisticated approaches (such
799 * as examining the TEXTMETRIC structure or requesting the
800 * height of a string), I think we'll do this the brute
801 * force way: we create a small bitmap, draw an underlined
802 * space on it, and test to see whether any pixels are
803 * foreground-coloured. (Since we expect the underline to
804 * go all the way across the character cell, we only search
805 * down a single column of the bitmap, half way across.)
809 HBITMAP und_bm, und_oldbm;
813 und_dc = CreateCompatibleDC(hdc);
814 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
815 und_oldbm = SelectObject(und_dc, und_bm);
816 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
817 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
818 SetTextColor (und_dc, RGB(255,255,255));
819 SetBkColor (und_dc, RGB(0,0,0));
820 SetBkMode (und_dc, OPAQUE);
821 ExtTextOut (und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
823 for (i = 0; i < font_height; i++) {
824 c = GetPixel(und_dc, font_width/2, i);
828 SelectObject(und_dc, und_oldbm);
829 DeleteObject(und_bm);
831 font_needs_hand_underlining = !gotit;
834 if (bold_mode == BOLD_FONT) {
835 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
836 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
839 if (cfg.vtmode == VT_OEMANSI) {
840 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
841 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
843 if (bold_mode == BOLD_FONT) {
844 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
845 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
851 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
853 SelectObject (hdc, fonts[FONT_OEM]);
854 GetTextMetrics(hdc, &tm);
855 font_height = tm.tmHeight;
856 font_width = tm.tmAveCharWidth;
858 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
860 if (bold_mode == BOLD_FONT) {
861 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
862 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
867 descent = tm.tmAscent + 1;
868 if (descent >= font_height)
869 descent = font_height - 1;
870 firstchar = tm.tmFirstChar;
872 for (i=0; i<8; i++) {
874 if (SelectObject (hdc, fonts[i]) &&
875 GetTextMetrics(hdc, &tm) )
876 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
882 ReleaseDC (hwnd, hdc);
884 /* ... This is wrong in OEM only mode */
885 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
886 (bold_mode == BOLD_FONT &&
887 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
889 DeleteObject (fonts[FONT_UNDERLINE]);
890 if (bold_mode == BOLD_FONT)
891 DeleteObject (fonts[FONT_BOLDUND]);
894 if (bold_mode == BOLD_FONT &&
895 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
896 bold_mode = BOLD_SHADOW;
897 DeleteObject (fonts[FONT_BOLD]);
898 if (und_mode == UND_FONT)
899 DeleteObject (fonts[FONT_BOLDUND]);
903 /* With the fascist font painting it doesn't matter if the linedraw font
904 * isn't exactly the right size anymore so we don't have to check this.
906 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
907 if( cfg.fontcharset == OEM_CHARSET )
909 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
910 "different sizes. Using OEM-only mode instead",
911 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
912 cfg.vtmode = VT_OEMONLY;
914 else if( firstchar < ' ' )
916 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
917 "different sizes. Using XTerm mode instead",
918 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
919 cfg.vtmode = VT_XWINDOWS;
923 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
924 "different sizes. Using ISO8859-1 mode instead",
925 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
926 cfg.vtmode = VT_POORMAN;
931 DeleteObject (fonts[i]);
937 void request_resize (int w, int h, int refont) {
940 /* If the window is maximized supress resizing attempts */
941 if(IsZoomed(hwnd)) return;
944 /* Don't do this in OEMANSI, you may get disable messages */
945 if (refont && w != cols && (cols==80 || cols==132)
946 && cfg.vtmode != VT_OEMANSI)
948 if (refont && w != cols && (cols==80 || cols==132))
951 /* If font width too big for screen should we shrink the font more ? */
953 font_width = ((font_width*cols+w/2)/w);
960 DeleteObject(fonts[i]);
962 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
964 init_fonts(font_width);
968 static int first_time = 1;
974 /* Get the size of the screen */
975 if (GetClientRect(GetDesktopWindow(),&ss))
976 /* first_time = 0 */;
977 else { first_time = 2; break; }
979 /* Make sure the values are sane */
980 width = (ss.right-ss.left-extra_width ) / font_width;
981 height = (ss.bottom-ss.top-extra_height ) / font_height;
983 if (w>width) w=width;
984 if (h>height) h=height;
990 width = extra_width + font_width * w;
991 height = extra_height + font_height * h;
993 SetWindowPos (hwnd, NULL, 0, 0, width, height,
994 SWP_NOACTIVATE | SWP_NOCOPYBITS |
995 SWP_NOMOVE | SWP_NOZORDER);
998 static void click (Mouse_Button b, int x, int y) {
999 int thistime = GetMessageTime();
1001 if (lastbtn == b && thistime - lasttime < dbltime) {
1002 lastact = (lastact == MA_CLICK ? MA_2CLK :
1003 lastact == MA_2CLK ? MA_3CLK :
1004 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1009 if (lastact != MA_NOTHING)
1010 term_mouse (b, lastact, x, y);
1011 lasttime = thistime;
1014 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
1015 WPARAM wParam, LPARAM lParam) {
1017 static int ignore_size = FALSE;
1018 static int ignore_clip = FALSE;
1019 static int just_reconfigged = FALSE;
1020 static int resizing = FALSE;
1024 if (pending_netevent)
1025 enact_pending_netevent();
1031 if (cfg.ping_interval > 0)
1035 if (now-last_movement > cfg.ping_interval * 60 - 10)
1037 back->special(TS_PING);
1038 last_movement = now;
1045 if (!cfg.warn_on_close || session_closed ||
1046 MessageBox(hwnd, "Are you sure you want to close this session?",
1047 "PuTTY Exit Confirmation",
1048 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1049 DestroyWindow(hwnd);
1052 PostQuitMessage (0);
1055 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1067 PROCESS_INFORMATION pi;
1068 HANDLE filemap = NULL;
1070 if (wParam == IDM_DUPSESS) {
1072 * Allocate a file-mapping memory chunk for the
1075 SECURITY_ATTRIBUTES sa;
1078 sa.nLength = sizeof(sa);
1079 sa.lpSecurityDescriptor = NULL;
1080 sa.bInheritHandle = TRUE;
1081 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
1088 p = (Config *)MapViewOfFile(filemap,
1090 0, 0, sizeof(Config));
1092 *p = cfg; /* structure copy */
1096 sprintf(c, "putty &%p", filemap);
1098 } else if (wParam == IDM_SAVEDSESS) {
1099 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
1100 cl = malloc(16 + strlen(session)); /* 8, but play safe */
1102 cl = NULL; /* not a very important failure mode */
1104 sprintf(cl, "putty @%s", session);
1110 GetModuleFileName (NULL, b, sizeof(b)-1);
1112 si.lpReserved = NULL;
1113 si.lpDesktop = NULL;
1117 si.lpReserved2 = NULL;
1118 CreateProcess (b, cl, NULL, NULL, TRUE,
1119 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1122 CloseHandle(filemap);
1128 if (!do_reconfig(hwnd))
1130 just_reconfigged = TRUE;
1135 DeleteObject(fonts[i]);
1137 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1138 und_mode = UND_FONT;
1141 /* Telnet will change local echo -> remote if the remote asks */
1142 if (cfg.protocol != PROT_TELNET)
1143 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
1151 /* Enable or disable the scroll bar, etc */
1153 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1156 if (cfg.scrollbar) nflg |= WS_VSCROLL;
1157 else nflg &= ~WS_VSCROLL;
1159 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1161 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1167 SetWindowLong(hwnd, GWL_STYLE, nflg);
1168 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1169 SetWindowPos(hwnd, NULL, 0,0,0,0,
1170 SWP_NOACTIVATE|SWP_NOCOPYBITS|
1171 SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER|
1174 GetWindowRect (hwnd, &wr);
1175 GetClientRect (hwnd, &cr);
1176 extra_width = wr.right - wr.left - cr.right + cr.left;
1177 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1181 term_size(cfg.height, cfg.width, cfg.savelines);
1182 InvalidateRect(hwnd, NULL, TRUE);
1183 SetWindowPos (hwnd, NULL, 0, 0,
1184 extra_width + font_width * cfg.width,
1185 extra_height + font_height * cfg.height,
1186 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1187 SWP_NOMOVE | SWP_NOZORDER);
1188 if (IsIconic(hwnd)) {
1189 SetWindowText (hwnd,
1190 cfg.win_name_always ? window_name : icon_name);
1199 case IDM_TEL_AYT: back->special (TS_AYT); break;
1200 case IDM_TEL_BRK: back->special (TS_BRK); break;
1201 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1202 case IDM_TEL_EC: back->special (TS_EC); break;
1203 case IDM_TEL_EL: back->special (TS_EL); break;
1204 case IDM_TEL_GA: back->special (TS_GA); break;
1205 case IDM_TEL_NOP: back->special (TS_NOP); break;
1206 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1207 case IDM_TEL_AO: back->special (TS_AO); break;
1208 case IDM_TEL_IP: back->special (TS_IP); break;
1209 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1210 case IDM_TEL_EOR: back->special (TS_EOR); break;
1211 case IDM_TEL_EOF: back->special (TS_EOF); break;
1216 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1217 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1222 #define X_POS(l) ((int)(short)LOWORD(l))
1223 #define Y_POS(l) ((int)(short)HIWORD(l))
1225 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1226 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1228 case WM_LBUTTONDOWN:
1229 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1230 TO_CHR_Y(Y_POS(lParam)));
1234 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1235 TO_CHR_Y(Y_POS(lParam)));
1238 case WM_MBUTTONDOWN:
1240 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1241 TO_CHR_X(X_POS(lParam)),
1242 TO_CHR_Y(Y_POS(lParam)));
1245 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1246 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1247 TO_CHR_Y(Y_POS(lParam)));
1250 case WM_RBUTTONDOWN:
1252 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1253 TO_CHR_X(X_POS(lParam)),
1254 TO_CHR_Y(Y_POS(lParam)));
1257 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1258 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1259 TO_CHR_Y(Y_POS(lParam)));
1264 * Add the mouse position and message time to the random
1265 * number noise, if we're using ssh.
1267 if (cfg.protocol == PROT_SSH)
1268 noise_ultralight(lParam);
1270 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1272 if (wParam & MK_LBUTTON)
1274 else if (wParam & MK_MBUTTON)
1275 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1277 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1278 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1279 TO_CHR_Y(Y_POS(lParam)));
1282 case WM_IGNORE_CLIP:
1283 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1285 case WM_DESTROYCLIPBOARD:
1288 ignore_clip = FALSE;
1294 hdc = BeginPaint (hwnd, &p);
1296 SelectPalette (hdc, pal, TRUE);
1297 RealizePalette (hdc);
1299 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1300 p.rcPaint.right, p.rcPaint.bottom);
1301 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1302 SelectObject (hdc, GetStockObject(WHITE_PEN));
1303 EndPaint (hwnd, &p);
1308 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1309 * but the only one that's likely to try to overload us is FD_READ.
1310 * This means buffering just one is fine.
1312 if (pending_netevent)
1313 enact_pending_netevent();
1315 pending_netevent = TRUE;
1316 pend_netevent_wParam=wParam;
1317 pend_netevent_lParam=lParam;
1318 time(&last_movement);
1322 CreateCaret(hwnd, caretbm, 0, 0);
1333 case WM_IGNORE_SIZE:
1334 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1336 case WM_ENTERSIZEMOVE:
1340 case WM_EXITSIZEMOVE:
1347 int width, height, w, h, ew, eh;
1348 LPRECT r = (LPRECT)lParam;
1350 width = r->right - r->left - extra_width;
1351 height = r->bottom - r->top - extra_height;
1352 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1353 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1354 UpdateSizeTip(hwnd, w, h);
1355 ew = width - w * font_width;
1356 eh = height - h * font_height;
1358 if (wParam == WMSZ_LEFT ||
1359 wParam == WMSZ_BOTTOMLEFT ||
1360 wParam == WMSZ_TOPLEFT)
1366 if (wParam == WMSZ_TOP ||
1367 wParam == WMSZ_TOPRIGHT ||
1368 wParam == WMSZ_TOPLEFT)
1378 /* break; (never reached) */
1380 if (wParam == SIZE_MINIMIZED) {
1381 SetWindowText (hwnd,
1382 cfg.win_name_always ? window_name : icon_name);
1385 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1386 SetWindowText (hwnd, window_name);
1388 int width, height, w, h;
1389 #if 0 /* we have fixed this using WM_SIZING now */
1393 width = LOWORD(lParam);
1394 height = HIWORD(lParam);
1395 w = width / font_width; if (w < 1) w = 1;
1396 h = height / font_height; if (h < 1) h = 1;
1397 #if 0 /* we have fixed this using WM_SIZING now */
1398 ew = width - w * font_width;
1399 eh = height - h * font_height;
1400 if (ew != 0 || eh != 0) {
1402 GetWindowRect (hwnd, &r);
1403 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1404 SetWindowPos (hwnd, NULL, 0, 0,
1405 r.right - r.left - ew, r.bottom - r.top - eh,
1406 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1409 if (w != cols || h != rows || just_reconfigged) {
1411 term_size (h, w, cfg.savelines);
1413 * Don't call back->size in mid-resize. (To prevent
1414 * massive numbers of resize events getting sent
1415 * down the connection during an NT opaque drag.)
1419 just_reconfigged = FALSE;
1422 ignore_size = FALSE;
1425 switch (LOWORD(wParam)) {
1426 case SB_BOTTOM: term_scroll(-1, 0); break;
1427 case SB_TOP: term_scroll(+1, 0); break;
1428 case SB_LINEDOWN: term_scroll (0, +1); break;
1429 case SB_LINEUP: term_scroll (0, -1); break;
1430 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1431 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1432 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1433 term_scroll (1, HIWORD(wParam)); break;
1436 case WM_PALETTECHANGED:
1437 if ((HWND) wParam != hwnd && pal != NULL) {
1438 HDC hdc = get_ctx();
1440 if (RealizePalette (hdc) > 0)
1446 case WM_QUERYNEWPALETTE:
1448 HDC hdc = get_ctx();
1450 if (RealizePalette (hdc) > 0)
1462 * Add the scan code and keypress timing to the random
1463 * number noise, if we're using ssh.
1465 if (cfg.protocol == PROT_SSH)
1466 noise_ultralight(lParam);
1469 * We don't do TranslateMessage since it disassociates the
1470 * resulting CHAR message from the KEYDOWN that sparked it,
1471 * which we occasionally don't want. Instead, we process
1472 * KEYDOWN, and call the Win32 translator functions so that
1473 * we get the translations under _our_ control.
1476 unsigned char buf[20];
1479 len = TranslateKey (message, wParam, lParam, buf);
1481 return DefWindowProc (hwnd, message, wParam, lParam);
1482 ldisc->send (buf, len);
1488 * Nevertheless, we are prepared to deal with WM_CHAR
1489 * messages, should they crop up. So if someone wants to
1490 * post the things to us as part of a macro manoeuvre,
1491 * we're ready to cope.
1494 char c = xlat_kbd2tty((unsigned char)wParam);
1495 ldisc->send (&c, 1);
1500 return DefWindowProc (hwnd, message, wParam, lParam);
1504 * Move the system caret. (We maintain one, even though it's
1505 * invisible, for the benefit of blind people: apparently some
1506 * helper software tracks the system caret, so we should arrange to
1509 void sys_cursor(int x, int y) {
1510 SetCaretPos(x * font_width, y * font_height);
1514 * Draw a line of text in the window, at given character
1515 * coordinates, in given attributes.
1517 * We are allowed to fiddle with the contents of `text'.
1519 void do_text (Context ctx, int x, int y, char *text, int len,
1520 unsigned long attr, int lattr) {
1522 int nfg, nbg, nfont;
1525 int force_manual_underline = 0;
1526 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1527 static int *IpDx = 0, IpDxLEN = 0;;
1529 if (len>IpDxLEN || IpDx[0] != fnt_width) {
1533 IpDx = smalloc((len+16)*sizeof(int));
1536 for(i=0; i<IpDxLEN; i++)
1537 IpDx[i] = fnt_width;
1543 if (attr & ATTR_ACTCURS) {
1544 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1545 attr ^= ATTR_CUR_XOR;
1549 if (cfg.vtmode == VT_OEMONLY)
1553 * Map high-half characters in order to approximate ISO using
1554 * OEM character set. No characters are missing if the OEM codepage
1557 if (nfont & FONT_OEM) {
1559 for (i=0; i<len; i++)
1560 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1562 /* This is CP850 ... perfect translation */
1563 static const char oemhighhalf[] =
1564 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1565 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1566 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1567 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1568 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1569 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1570 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1571 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1572 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1573 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1574 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1575 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1578 /* This is CP437 ... junk translation */
1579 static const unsigned char oemhighhalf[] = {
1580 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1581 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1582 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1583 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1584 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1585 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1586 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1587 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1588 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1589 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1590 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1591 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1594 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1598 if (attr & ATTR_LINEDRW) {
1601 static const char poorman[] =
1602 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1605 static const char oemmap_437[] =
1606 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1607 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1610 static const char oemmap_850[] =
1611 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1612 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1614 /* Poor windows font ... eg: windows courier */
1615 static const char oemmap[] =
1616 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1617 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1620 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1621 * VT100 line drawing chars; everything else stays normal.
1623 * Actually '_' maps to space too, but that's done before.
1625 switch (cfg.vtmode) {
1627 for (i=0; i<len; i++)
1628 if (text[i] >= '\x60' && text[i] <= '\x7E')
1629 text[i] += '\x01' - '\x60';
1632 /* Make sure we actually have an OEM font */
1633 if (fonts[nfont|FONT_OEM]) {
1636 for (i=0; i<len; i++)
1637 if (text[i] >= '\x60' && text[i] <= '\x7E')
1638 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1642 for (i=0; i<len; i++)
1643 if (text[i] >= '\x60' && text[i] <= '\x7E')
1644 text[i] = poorman[(unsigned char)text[i] - 0x60];
1649 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1650 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1651 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1653 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1654 nfont |= FONT_UNDERLINE;
1657 if (nfont&FONT_UNDERLINE)
1658 force_manual_underline = 1;
1659 /* Don't do the same for manual bold, it could be bad news. */
1661 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1663 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1664 force_manual_underline = 1;
1665 if (attr & ATTR_REVERSE) {
1666 t = nfg; nfg = nbg; nbg = t;
1668 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1670 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1674 SelectObject (hdc, fonts[nfont]);
1675 SetTextColor (hdc, fg);
1676 SetBkColor (hdc, bg);
1677 SetBkMode (hdc, OPAQUE);
1680 line_box.right = x+fnt_width*len;
1681 line_box.bottom = y+font_height;
1682 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1683 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1684 SetBkMode (hdc, TRANSPARENT);
1686 /* GRR: This draws the character outside it's box and can leave
1687 * 'droppings' even with the clip box! I suppose I could loop it
1688 * one character at a time ... yuk.
1690 * Or ... I could do a test print with "W", and use +1 or -1 for this
1691 * shift depending on if the leftmost column is blank...
1693 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1695 if (force_manual_underline ||
1696 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1698 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1699 MoveToEx (hdc, x, y+descent, NULL);
1700 LineTo (hdc, x+len*fnt_width, y+descent);
1701 oldpen = SelectObject (hdc, oldpen);
1702 DeleteObject (oldpen);
1704 if (attr & ATTR_PASCURS) {
1707 pts[0].x = pts[1].x = pts[4].x = x;
1708 pts[2].x = pts[3].x = x+fnt_width-1;
1709 pts[0].y = pts[3].y = pts[4].y = y;
1710 pts[1].y = pts[2].y = y+font_height-1;
1711 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1712 Polyline (hdc, pts, 5);
1713 oldpen = SelectObject (hdc, oldpen);
1714 DeleteObject (oldpen);
1718 static int check_compose(int first, int second) {
1720 static char * composetbl[] = {
1721 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1722 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1723 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1724 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1725 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1726 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1727 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1728 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1729 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1730 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1731 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1732 "\"uü", "'yý", "htþ", "\"yÿ",
1736 static int recurse = 0;
1743 sprintf(buf, "cc(%d,%d)", first, second);
1748 for(c=composetbl; *c; c++) {
1749 if( (*c)[0] == first && (*c)[1] == second)
1751 return (*c)[2] & 0xFF;
1758 nc = check_compose(second, first);
1760 nc = check_compose(toupper(first), toupper(second));
1762 nc = check_compose(toupper(second), toupper(first));
1770 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1771 * codes. Returns number of bytes used or zero to drop the message
1772 * or -1 to forward the message to windows.
1774 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output) {
1776 int scan, left_alt = 0, key_down, shift_state;
1778 unsigned char * p = output;
1780 static WORD keys[3];
1781 static int compose_state = 0;
1782 static int compose_char = 0;
1783 static WPARAM compose_key = 0;
1785 r = GetKeyboardState(keystate);
1786 if (!r) memset(keystate, 0, sizeof(keystate));
1790 { /* Tell us all about key events */
1791 static BYTE oldstate[256];
1792 static int first = 1;
1795 if(first) memcpy(oldstate, keystate, sizeof(oldstate));
1798 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT) {
1800 } else if ((HIWORD(lParam)&KF_UP) && scan==(HIWORD(lParam) & 0xFF) ) {
1804 if (wParam >= VK_F1 && wParam <= VK_F20 )
1805 debug(("K_F%d", wParam+1-VK_F1));
1808 case VK_SHIFT: debug(("SHIFT")); break;
1809 case VK_CONTROL: debug(("CTRL")); break;
1810 case VK_MENU: debug(("ALT")); break;
1811 default: debug(("VK_%02x", wParam));
1813 if(message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
1815 debug((", S%02x", scan=(HIWORD(lParam) & 0xFF) ));
1817 ch = MapVirtualKey(wParam, 2);
1818 if (ch>=' ' && ch<='~') debug((", '%c'", ch));
1819 else if (ch) debug((", $%02x", ch));
1821 if (keys[0]) debug((", KB0=%02x", keys[0]));
1822 if (keys[1]) debug((", KB1=%02x", keys[1]));
1823 if (keys[2]) debug((", KB2=%02x", keys[2]));
1825 if ( (keystate[VK_SHIFT]&0x80)!=0) debug((", S"));
1826 if ( (keystate[VK_CONTROL]&0x80)!=0) debug((", C"));
1827 if ( (HIWORD(lParam)&KF_EXTENDED) ) debug((", E"));
1828 if ( (HIWORD(lParam)&KF_UP) ) debug((", U"));
1831 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
1833 else if ( (HIWORD(lParam)&KF_UP) )
1834 oldstate[wParam&0xFF] ^= 0x80;
1836 oldstate[wParam&0xFF] ^= 0x81;
1838 for(ch=0; ch<256; ch++)
1839 if (oldstate[ch] != keystate[ch])
1840 debug((", M%02x=%02x", ch, keystate[ch]));
1842 memcpy(oldstate, keystate, sizeof(oldstate));
1846 /* Note if AltGr was pressed and if it was used as a compose key */
1847 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
1849 keystate[VK_RMENU] = keystate[VK_MENU];
1850 if (!compose_state) compose_key = wParam;
1852 if (wParam == VK_APPS && !compose_state)
1853 compose_key = wParam;
1855 if (wParam == compose_key)
1857 if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1859 else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
1864 else if (compose_state==1 && wParam != VK_CONTROL)
1867 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1868 if ( (cfg.funky_type == 3 || (cfg.funky_type <= 1 && app_keypad_keys))
1869 && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
1871 wParam = VK_EXECUTE;
1873 /* UnToggle NUMLock */
1874 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1875 keystate[VK_NUMLOCK] ^= 1;
1878 /* And write back the 'adjusted' state */
1879 SetKeyboardState (keystate);
1882 /* Disable Auto repeat if required */
1883 if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
1886 if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
1889 key_down = ((HIWORD(lParam)&KF_UP)==0);
1891 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1892 if (left_alt && (keystate[VK_CONTROL]&0x80))
1893 keystate[VK_MENU] = 0;
1895 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
1896 shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
1897 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
1900 * Record that we pressed key so the scroll window can be reset, but
1901 * be careful to avoid Shift-UP/Down
1903 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
1907 /* Make sure we're not pasting */
1908 if (key_down) term_nopaste();
1910 if (compose_state>1 && left_alt) compose_state = 0;
1912 /* Sanitize the number pad if not using a PC NumPad */
1913 if( left_alt || (app_keypad_keys && cfg.funky_type != 2)
1914 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state )
1916 if ((HIWORD(lParam)&KF_EXTENDED) == 0)
1921 case VK_INSERT: nParam = VK_NUMPAD0; break;
1922 case VK_END: nParam = VK_NUMPAD1; break;
1923 case VK_DOWN: nParam = VK_NUMPAD2; break;
1924 case VK_NEXT: nParam = VK_NUMPAD3; break;
1925 case VK_LEFT: nParam = VK_NUMPAD4; break;
1926 case VK_CLEAR: nParam = VK_NUMPAD5; break;
1927 case VK_RIGHT: nParam = VK_NUMPAD6; break;
1928 case VK_HOME: nParam = VK_NUMPAD7; break;
1929 case VK_UP: nParam = VK_NUMPAD8; break;
1930 case VK_PRIOR: nParam = VK_NUMPAD9; break;
1931 case VK_DELETE: nParam = VK_DECIMAL; break;
1935 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
1941 /* If a key is pressed and AltGr is not active */
1942 if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
1944 /* Okay, prepare for most alts then ...*/
1945 if (left_alt) *p++ = '\033';
1947 /* Lets see if it's a pattern we know all about ... */
1948 if (wParam == VK_PRIOR && shift_state == 1) {
1949 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
1952 if (wParam == VK_NEXT && shift_state == 1) {
1953 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
1956 if (wParam == VK_INSERT && shift_state == 1) {
1957 term_mouse (MB_PASTE, MA_CLICK, 0, 0);
1958 term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
1961 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
1964 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
1966 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
1969 /* Control-Numlock for app-keypad mode switch */
1970 if (wParam == VK_PAUSE && shift_state == 2) {
1971 app_keypad_keys ^= 1;
1975 /* Nethack keypad */
1976 if (cfg.nethack_keypad && !left_alt) {
1978 case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
1979 case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
1980 case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
1981 case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
1982 case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
1983 case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
1984 case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
1985 case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
1986 case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
1990 /* Application Keypad */
1994 if ( cfg.funky_type == 3 ||
1995 ( cfg.funky_type <= 1 && app_keypad_keys)) switch(wParam) {
1996 case VK_EXECUTE: xkey = 'P'; break;
1997 case VK_DIVIDE: xkey = 'Q'; break;
1998 case VK_MULTIPLY:xkey = 'R'; break;
1999 case VK_SUBTRACT:xkey = 'S'; break;
2001 if(app_keypad_keys) switch(wParam) {
2002 case VK_NUMPAD0: xkey = 'p'; break;
2003 case VK_NUMPAD1: xkey = 'q'; break;
2004 case VK_NUMPAD2: xkey = 'r'; break;
2005 case VK_NUMPAD3: xkey = 's'; break;
2006 case VK_NUMPAD4: xkey = 't'; break;
2007 case VK_NUMPAD5: xkey = 'u'; break;
2008 case VK_NUMPAD6: xkey = 'v'; break;
2009 case VK_NUMPAD7: xkey = 'w'; break;
2010 case VK_NUMPAD8: xkey = 'x'; break;
2011 case VK_NUMPAD9: xkey = 'y'; break;
2013 case VK_DECIMAL: xkey = 'n'; break;
2014 case VK_ADD: if(cfg.funky_type==2) {
2015 if(shift_state) xkey = 'l';
2017 } else if(shift_state) xkey = 'm';
2021 case VK_DIVIDE: if(cfg.funky_type==2) xkey = 'o'; break;
2022 case VK_MULTIPLY:if(cfg.funky_type==2) xkey = 'j'; break;
2023 case VK_SUBTRACT:if(cfg.funky_type==2) xkey = 'm'; break;
2026 if (HIWORD(lParam)&KF_EXTENDED)
2034 if (xkey>='P' && xkey<='S')
2035 p += sprintf((char *)p, "\x1B%c", xkey);
2037 p += sprintf((char *)p, "\x1B?%c", xkey);
2040 p += sprintf((char *)p, "\x1BO%c", xkey);
2045 if (wParam == VK_BACK && shift_state == 0 ) /* Backspace */
2047 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2050 if (wParam == VK_TAB && shift_state == 1 ) /* Shift tab */
2052 *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
2054 if (wParam == VK_SPACE && shift_state == 2 ) /* Ctrl-Space */
2056 *p++ = 0; return p - output;
2058 if (wParam == VK_SPACE && shift_state == 3 ) /* Ctrl-Shift-Space */
2060 *p++ = 160; return p - output;
2062 if (wParam == VK_CANCEL && shift_state == 2 ) /* Ctrl-Break */
2064 *p++ = 3; return p - output;
2066 /* Control-2 to Control-8 are special */
2067 if (shift_state == 2 && wParam >= '2' && wParam <= '8')
2069 *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
2072 if (shift_state == 2 && wParam == 0xBD) {
2076 if (shift_state == 2 && wParam == 0xDF) {
2080 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2081 *p++ = '\r'; *p++ = '\n';
2086 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2087 * for integer decimal nn.)
2089 * We also deal with the weird ones here. Linux VCs replace F1
2090 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2091 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2096 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
2097 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
2098 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
2099 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
2100 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
2101 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
2102 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
2103 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
2104 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
2105 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
2106 case VK_F11: code = 23; break;
2107 case VK_F12: code = 24; break;
2108 case VK_F13: code = 25; break;
2109 case VK_F14: code = 26; break;
2110 case VK_F15: code = 28; break;
2111 case VK_F16: code = 29; break;
2112 case VK_F17: code = 31; break;
2113 case VK_F18: code = 32; break;
2114 case VK_F19: code = 33; break;
2115 case VK_F20: code = 34; break;
2116 case VK_HOME: code = 1; break;
2117 case VK_INSERT: code = 2; break;
2118 case VK_DELETE: code = 3; break;
2119 case VK_END: code = 4; break;
2120 case VK_PRIOR: code = 5; break;
2121 case VK_NEXT: code = 6; break;
2123 /* Reorder edit keys to physical order */
2124 if (cfg.funky_type == 3 && code <= 6 ) code = "\0\2\1\4\5\3\6"[code];
2126 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2127 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
2130 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2132 p += sprintf((char *)p, "\x1B%c", code + 'P' - 11);
2134 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
2137 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2138 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
2142 p += sprintf((char *)p, "\x1B[%d~", code);
2147 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2148 * some reason seems to send VK_CLEAR to Windows...).
2153 case VK_UP: xkey = 'A'; break;
2154 case VK_DOWN: xkey = 'B'; break;
2155 case VK_RIGHT: xkey = 'C'; break;
2156 case VK_LEFT: xkey = 'D'; break;
2157 case VK_CLEAR: xkey = 'G'; break;
2162 p += sprintf((char *)p, "\x1B%c", xkey);
2163 else if (app_cursor_keys)
2164 p += sprintf((char *)p, "\x1BO%c", xkey);
2166 p += sprintf((char *)p, "\x1B[%c", xkey);
2172 * Finally, deal with Return ourselves. (Win95 seems to
2173 * foul it up when Alt is pressed, for some reason.)
2175 if (wParam == VK_RETURN) /* Return */
2182 /* Okay we've done everything interesting; let windows deal with
2183 * the boring stuff */
2185 BOOL capsOn=keystate[VK_CAPITAL] !=0;
2187 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2188 if(cfg.xlat_capslockcyr)
2189 keystate[VK_CAPITAL] = 0;
2191 r = ToAscii (wParam, scan, keystate, keys, 0);
2197 unsigned char ch = (unsigned char)keys[i];
2199 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
2204 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
2208 if ((nc=check_compose(compose_char,ch)) == -1)
2213 *p++ = xlat_kbd2tty((unsigned char)nc);
2219 if( left_alt && key_down ) *p++ = '\033';
2225 ch = xlat_latkbd2win(ch);
2226 *p++ = xlat_kbd2tty(ch);
2230 /* This is so the ALT-Numpad and dead keys work correctly. */
2237 /* This stops ALT press-release doing a 'COMMAND MENU' function */
2238 if (message == WM_SYSKEYUP && wParam == VK_MENU)
2244 void set_title (char *title) {
2245 sfree (window_name);
2246 window_name = smalloc(1+strlen(title));
2247 strcpy (window_name, title);
2248 if (cfg.win_name_always || !IsIconic(hwnd))
2249 SetWindowText (hwnd, title);
2252 void set_icon (char *title) {
2254 icon_name = smalloc(1+strlen(title));
2255 strcpy (icon_name, title);
2256 if (!cfg.win_name_always && IsIconic(hwnd))
2257 SetWindowText (hwnd, title);
2260 void set_sbar (int total, int start, int page) {
2263 if (!cfg.scrollbar) return;
2265 si.cbSize = sizeof(si);
2266 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2268 si.nMax = total - 1;
2272 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2275 Context get_ctx(void) {
2280 SelectPalette (hdc, pal, FALSE);
2286 void free_ctx (Context ctx) {
2287 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2288 ReleaseDC (hwnd, ctx);
2291 static void real_palette_set (int n, int r, int g, int b) {
2293 logpal->palPalEntry[n].peRed = r;
2294 logpal->palPalEntry[n].peGreen = g;
2295 logpal->palPalEntry[n].peBlue = b;
2296 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2297 colours[n] = PALETTERGB(r, g, b);
2298 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2300 colours[n] = RGB(r, g, b);
2303 void palette_set (int n, int r, int g, int b) {
2304 static const int first[21] = {
2305 0, 2, 4, 6, 8, 10, 12, 14,
2306 1, 3, 5, 7, 9, 11, 13, 15,
2309 real_palette_set (first[n], r, g, b);
2311 real_palette_set (first[n]+1, r, g, b);
2313 HDC hdc = get_ctx();
2314 UnrealizeObject (pal);
2315 RealizePalette (hdc);
2320 void palette_reset (void) {
2323 for (i = 0; i < NCOLOURS; i++) {
2325 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2326 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2327 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2328 logpal->palPalEntry[i].peFlags = 0;
2329 colours[i] = PALETTERGB(defpal[i].rgbtRed,
2330 defpal[i].rgbtGreen,
2331 defpal[i].rgbtBlue);
2333 colours[i] = RGB(defpal[i].rgbtRed,
2334 defpal[i].rgbtGreen,
2335 defpal[i].rgbtBlue);
2340 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2342 RealizePalette (hdc);
2347 void write_clip (void *data, int len, int must_deselect) {
2351 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2354 lock = GlobalLock (clipdata);
2357 memcpy (lock, data, len);
2358 ((unsigned char *) lock) [len] = 0;
2359 GlobalUnlock (clipdata);
2362 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2364 if (OpenClipboard (hwnd)) {
2366 SetClipboardData (CF_TEXT, clipdata);
2369 GlobalFree (clipdata);
2372 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2375 void get_clip (void **p, int *len) {
2376 static HGLOBAL clipdata = NULL;
2380 GlobalUnlock (clipdata);
2384 if (OpenClipboard (NULL)) {
2385 clipdata = GetClipboardData (CF_TEXT);
2388 *p = GlobalLock (clipdata);
2402 * Move `lines' lines from position `from' to position `to' in the
2405 void optimised_move (int to, int from, int lines) {
2409 min = (to < from ? to : from);
2410 max = to + from - min;
2412 r.left = 0; r.right = cols * font_width;
2413 r.top = min * font_height; r.bottom = (max+lines) * font_height;
2414 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2418 * Print a message box and perform a fatal exit.
2420 void fatalbox(char *fmt, ...) {
2425 vsprintf(stuff, fmt, ap);
2427 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2434 void beep(int errorbeep) {
2435 static long last_beep = 0;
2436 long now, beep_diff;
2438 now = GetTickCount();
2439 beep_diff = now-last_beep;
2441 /* Make sure we only respond to one beep per packet or so */
2442 if (beep_diff>=0 && beep_diff<50)
2446 MessageBeep(MB_ICONHAND);
2450 last_beep = GetTickCount();