20 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
26 #define IDM_SHOWLOG 0x0010
27 #define IDM_NEWSESS 0x0020
28 #define IDM_DUPSESS 0x0030
29 #define IDM_RECONF 0x0040
30 #define IDM_CLRSB 0x0050
31 #define IDM_RESET 0x0060
32 #define IDM_TEL_AYT 0x0070
33 #define IDM_TEL_BRK 0x0080
34 #define IDM_TEL_SYNCH 0x0090
35 #define IDM_TEL_EC 0x00a0
36 #define IDM_TEL_EL 0x00b0
37 #define IDM_TEL_GA 0x00c0
38 #define IDM_TEL_NOP 0x00d0
39 #define IDM_TEL_ABORT 0x00e0
40 #define IDM_TEL_AO 0x00f0
41 #define IDM_TEL_IP 0x0100
42 #define IDM_TEL_SUSP 0x0110
43 #define IDM_TEL_EOR 0x0120
44 #define IDM_TEL_EOF 0x0130
45 #define IDM_ABOUT 0x0140
46 #define IDM_SAVEDSESS 0x0150
47 #define IDM_COPYALL 0x0160
48 #define IDM_FULLSCREEN 0x0170
50 #define IDM_SESSLGP 0x0250 /* log type printable */
51 #define IDM_SESSLGA 0x0260 /* log type all chars */
52 #define IDM_SESSLGE 0x0270 /* log end */
53 #define IDM_SAVED_MIN 0x1000
54 #define IDM_SAVED_MAX 0x2000
56 #define WM_IGNORE_CLIP (WM_XUSER + 2)
58 /* Needed for Chinese support and apparently not always defined. */
60 #define VK_PROCESSKEY 0xE5
63 /* Needed for mouse wheel support and not defined in earlier SDKs. */
65 #define WM_MOUSEWHEEL 0x020A
68 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
69 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
70 unsigned char *output);
71 static void cfgtopalette(void);
72 static void init_palette(void);
73 static void init_fonts(int, int);
74 static void another_font(int);
75 static void deinit_fonts(void);
77 /* Window layout information */
78 static void reset_window(int);
79 static int full_screen = 0;
80 static int extra_width, extra_height;
81 static int font_width, font_height, font_dualwidth;
82 static int offset_width, offset_height;
83 static int was_zoomed = 0;
84 static int prev_rows, prev_cols;
86 static int pending_netevent = 0;
87 static WPARAM pend_netevent_wParam = 0;
88 static LPARAM pend_netevent_lParam = 0;
89 static void enact_pending_netevent(void);
90 static void flash_window(int mode);
91 static void flip_full_screen(void);
93 static time_t last_movement = 0;
97 #define FONT_UNDERLINE 2
98 #define FONT_BOLDUND 3
99 #define FONT_WIDE 0x04
100 #define FONT_HIGH 0x08
101 #define FONT_NARROW 0x10
103 #define FONT_OEM 0x20
104 #define FONT_OEMBOLD 0x21
105 #define FONT_OEMUND 0x22
106 #define FONT_OEMBOLDUND 0x23
108 #define FONT_MAXNO 0x2F
110 static HFONT fonts[FONT_MAXNO];
111 static LOGFONT lfont;
112 static int fontflag[FONT_MAXNO];
114 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
122 static COLORREF colours[NCOLOURS];
124 static LPLOGPALETTE logpal;
125 static RGBTRIPLE defpal[NCOLOURS];
129 static HBITMAP caretbm;
131 static int dbltime, lasttime, lastact;
132 static Mouse_Button lastbtn;
134 /* this allows xterm-style mouse handling. */
135 static int send_raw_mouse = 0;
136 static int wheel_accumulator = 0;
138 static char *window_name, *icon_name;
140 static int compose_state = 0;
142 static OSVERSIONINFO osVersion;
144 /* Dummy routine, only required in plink. */
145 void ldisc_update(int echo, int edit)
149 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
151 static char appname[] = "PuTTY";
156 int guess_width, guess_height;
159 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
161 winsock_ver = MAKEWORD(1, 1);
162 if (WSAStartup(winsock_ver, &wsadata)) {
163 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
164 MB_OK | MB_ICONEXCLAMATION);
167 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
168 MessageBox(NULL, "WinSock version is incompatible with 1.1",
169 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
173 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
176 InitCommonControls();
178 /* Ensure a Maximize setting in Explorer doesn't maximise the
183 ZeroMemory(&osVersion, sizeof(osVersion));
184 osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
185 if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
186 MessageBox(NULL, "Windows refuses to report a version",
187 "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
193 * Process the command line.
198 default_protocol = DEFAULT_PROTOCOL;
199 default_port = DEFAULT_PORT;
200 cfg.logtype = LGTYP_NONE;
202 do_defaults(NULL, &cfg);
205 while (*p && isspace(*p))
209 * Process command line options first. Yes, this can be
210 * done better, and it will be as soon as I have the
214 char *q = p + strcspn(p, " \t");
217 tolower(p[0]) == 's' &&
218 tolower(p[1]) == 's' && tolower(p[2]) == 'h') {
219 default_protocol = cfg.protocol = PROT_SSH;
220 default_port = cfg.port = 22;
221 } else if (q == p + 7 &&
222 tolower(p[0]) == 'c' &&
223 tolower(p[1]) == 'l' &&
224 tolower(p[2]) == 'e' &&
225 tolower(p[3]) == 'a' &&
226 tolower(p[4]) == 'n' &&
227 tolower(p[5]) == 'u' && tolower(p[6]) == 'p') {
229 * `putty -cleanup'. Remove all registry entries
230 * associated with PuTTY, and also find and delete
231 * the random seed file.
234 "This procedure will remove ALL Registry\n"
235 "entries associated with PuTTY, and will\n"
236 "also remove the PuTTY random seed file.\n"
238 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
239 "SESSIONS. Are you really sure you want\n"
242 MB_YESNO | MB_ICONWARNING) == IDYES) {
247 p = q + strspn(q, " \t");
251 * An initial @ means to activate a saved session.
255 while (i > 1 && isspace(p[i - 1]))
258 do_defaults(p + 1, &cfg);
259 if (!*cfg.host && !do_config()) {
263 } else if (*p == '&') {
265 * An initial & means we've been given a command line
266 * containing the hex value of a HANDLE for a file
267 * mapping object, which we must then extract as a
272 if (sscanf(p + 1, "%p", &filemap) == 1 &&
273 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
274 0, 0, sizeof(Config))) != NULL) {
277 CloseHandle(filemap);
278 } else if (!do_config()) {
285 * If the hostname starts with "telnet:", set the
286 * protocol to Telnet and process the string as a
289 if (!strncmp(q, "telnet:", 7)) {
293 if (q[0] == '/' && q[1] == '/')
295 cfg.protocol = PROT_TELNET;
297 while (*p && *p != ':' && *p != '/')
306 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
307 cfg.host[sizeof(cfg.host) - 1] = '\0';
309 while (*p && !isspace(*p))
313 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
314 cfg.host[sizeof(cfg.host) - 1] = '\0';
315 while (*p && isspace(*p))
330 * Trim leading whitespace off the hostname if it's there.
333 int space = strspn(cfg.host, " \t");
334 memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
337 /* See if host is of the form user@host */
338 if (cfg.host[0] != '\0') {
339 char *atsign = strchr(cfg.host, '@');
340 /* Make sure we're not overflowing the user field */
342 if (atsign - cfg.host < sizeof cfg.username) {
343 strncpy(cfg.username, cfg.host, atsign - cfg.host);
344 cfg.username[atsign - cfg.host] = '\0';
346 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
351 * Trim a colon suffix off the hostname if it's there.
353 cfg.host[strcspn(cfg.host, ":")] = '\0';
357 * Select protocol. This is farmed out into a table in a
358 * separate file to enable an ssh-free variant.
363 for (i = 0; backends[i].backend != NULL; i++)
364 if (backends[i].protocol == cfg.protocol) {
365 back = backends[i].backend;
369 MessageBox(NULL, "Unsupported protocol number found",
370 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
376 /* Check for invalid Port number (i.e. zero) */
378 MessageBox(NULL, "Invalid Port Number",
379 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
386 wndclass.lpfnWndProc = WndProc;
387 wndclass.cbClsExtra = 0;
388 wndclass.cbWndExtra = 0;
389 wndclass.hInstance = inst;
390 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
391 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
392 wndclass.hbrBackground = NULL;
393 wndclass.lpszMenuName = NULL;
394 wndclass.lpszClassName = appname;
396 RegisterClass(&wndclass);
401 savelines = cfg.savelines;
407 * Guess some defaults for the window size. This all gets
408 * updated later, so we don't really care too much. However, we
409 * do want the font width/height guesses to correspond to a
410 * large font rather than a small one...
417 term_size(cfg.height, cfg.width, cfg.savelines);
418 guess_width = extra_width + font_width * cols;
419 guess_height = extra_height + font_height * rows;
422 HWND w = GetDesktopWindow();
423 GetWindowRect(w, &r);
424 if (guess_width > r.right - r.left)
425 guess_width = r.right - r.left;
426 if (guess_height > r.bottom - r.top)
427 guess_height = r.bottom - r.top;
431 int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
434 winmode &= ~(WS_VSCROLL);
435 if (cfg.resize_action == RESIZE_DISABLED)
436 winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
438 exwinmode |= WS_EX_TOPMOST;
440 exwinmode |= WS_EX_CLIENTEDGE;
441 hwnd = CreateWindowEx(exwinmode, appname, appname,
442 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
443 guess_width, guess_height,
444 NULL, NULL, inst, NULL);
448 * Initialise the fonts, simultaneously correcting the guesses
449 * for font_{width,height}.
454 * Correct the guesses for extra_{width,height}.
458 GetWindowRect(hwnd, &wr);
459 GetClientRect(hwnd, &cr);
460 offset_width = offset_height = cfg.window_border;
461 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
462 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
466 * Resize the window, now we know what size we _really_ want it
469 guess_width = extra_width + font_width * cols;
470 guess_height = extra_height + font_height * rows;
471 SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
472 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
475 * Set up a caret bitmap, with no content.
479 int size = (font_width + 15) / 16 * 2 * font_height;
480 bits = smalloc(size);
481 memset(bits, 0, size);
482 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
485 CreateCaret(hwnd, caretbm, font_width, font_height);
488 * Initialise the scroll bar.
493 si.cbSize = sizeof(si);
494 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
499 SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
503 * Start up the telnet connection.
507 char msg[1024], *title;
510 error = back->init(cfg.host, cfg.port, &realhost);
512 sprintf(msg, "Unable to open connection to\n"
513 "%.800s\n" "%s", cfg.host, error);
514 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
517 window_name = icon_name = NULL;
519 title = cfg.wintitle;
521 sprintf(msg, "%s - PuTTY", realhost);
529 session_closed = FALSE;
532 * Prepare the mouse handler.
534 lastact = MA_NOTHING;
535 lastbtn = MBT_NOTHING;
536 dbltime = GetDoubleClickTime();
539 * Set up the session-control options on the system menu.
542 HMENU m = GetSystemMenu(hwnd, FALSE);
546 AppendMenu(m, MF_SEPARATOR, 0, 0);
547 if (cfg.protocol == PROT_TELNET) {
549 AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
550 AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
551 AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
552 AppendMenu(p, MF_SEPARATOR, 0, 0);
553 AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
554 AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
555 AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
556 AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
557 AppendMenu(p, MF_SEPARATOR, 0, 0);
558 AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
559 AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
560 AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
561 AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
562 AppendMenu(p, MF_SEPARATOR, 0, 0);
563 AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
564 AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
565 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
567 AppendMenu(m, MF_SEPARATOR, 0, 0);
569 AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
570 AppendMenu(m, MF_SEPARATOR, 0, 0);
571 AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
572 AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
575 for (i = 1; i < ((nsessions < 256) ? nsessions : 256); i++)
576 AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
578 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
579 AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
580 AppendMenu(m, MF_SEPARATOR, 0, 0);
581 AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
582 AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
583 AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
584 AppendMenu(m, MF_SEPARATOR, 0, 0);
585 AppendMenu(m, (cfg.resize_action == RESIZE_DISABLED) ?
586 MF_GRAYED : MF_ENABLED, IDM_FULLSCREEN, "&Full Screen");
587 AppendMenu(m, MF_SEPARATOR, 0, 0);
588 AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
592 * Finally show the window!
594 ShowWindow(hwnd, show);
595 SetForegroundWindow(hwnd);
598 * Open the initial log file if there is one.
603 * Set the palette up.
609 has_focus = (GetForegroundWindow() == hwnd);
612 if (GetMessage(&msg, NULL, 0, 0) == 1) {
613 int timer_id = 0, long_timer = 0;
615 while (msg.message != WM_QUIT) {
616 /* Sometimes DispatchMessage calls routines that use their own
617 * GetMessage loop, setup this timer so we get some control back.
619 * Also call term_update() from the timer so that if the host
620 * is sending data flat out we still do redraws.
622 if (timer_id && long_timer) {
623 KillTimer(hwnd, timer_id);
624 long_timer = timer_id = 0;
627 timer_id = SetTimer(hwnd, 1, 20, NULL);
628 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
629 DispatchMessage(&msg);
631 /* Make sure we blink everything that needs it. */
634 /* Send the paste buffer if there's anything to send */
637 /* If there's nothing new in the queue then we can do everything
638 * we've delayed, reading the socket, writing, and repainting
641 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
644 if (pending_netevent) {
645 enact_pending_netevent();
647 /* Force the cursor blink on */
650 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
654 /* Okay there is now nothing to do so we make sure the screen is
655 * completely up to date then tell windows to call us in a little
659 KillTimer(hwnd, timer_id);
667 flash_window(1); /* maintain */
669 /* The messages seem unreliable; especially if we're being tricky */
670 has_focus = (GetForegroundWindow() == hwnd);
673 /* Hmm, term_update didn't want to do an update too soon ... */
674 timer_id = SetTimer(hwnd, 1, 50, NULL);
676 timer_id = SetTimer(hwnd, 1, 500, NULL);
678 timer_id = SetTimer(hwnd, 1, 100, NULL);
681 /* There's no point rescanning everything in the message queue
682 * so we do an apparently unnecessary wait here
685 if (GetMessage(&msg, NULL, 0, 0) != 1)
699 if (cfg.protocol == PROT_SSH) {
710 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
712 char *do_select(SOCKET skt, int startup)
717 events = (FD_CONNECT | FD_READ | FD_WRITE |
718 FD_OOB | FD_CLOSE | FD_ACCEPT);
723 return "do_select(): internal error (hwnd==NULL)";
724 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
725 switch (WSAGetLastError()) {
727 return "Network is down";
729 return "WSAAsyncSelect(): unknown error";
736 * set or clear the "raw mouse message" mode
738 void set_raw_mouse_mode(int activate)
740 send_raw_mouse = activate;
741 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
745 * Print a message box and close the connection.
747 void connection_fatal(char *fmt, ...)
753 vsprintf(stuff, fmt, ap);
755 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
756 if (cfg.close_on_exit == COE_ALWAYS)
759 session_closed = TRUE;
760 SetWindowText(hwnd, "PuTTY (inactive)");
765 * Actually do the job requested by a WM_NETEVENT
767 static void enact_pending_netevent(void)
769 static int reentering = 0;
770 extern int select_result(WPARAM, LPARAM);
774 return; /* don't unpend the pending */
776 pending_netevent = FALSE;
779 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
782 if (ret == 0 && !session_closed) {
783 /* Abnormal exits will already have set session_closed and taken
784 * appropriate action. */
785 if (cfg.close_on_exit == COE_ALWAYS ||
786 cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
788 session_closed = TRUE;
789 SetWindowText(hwnd, "PuTTY (inactive)");
790 MessageBox(hwnd, "Connection closed by remote host",
791 "PuTTY", MB_OK | MB_ICONINFORMATION);
797 * Copy the colour palette from the configuration data into defpal.
798 * This is non-trivial because the colour indices are different.
800 static void cfgtopalette(void)
803 static const int ww[] = {
804 6, 7, 8, 9, 10, 11, 12, 13,
805 14, 15, 16, 17, 18, 19, 20, 21,
806 0, 1, 2, 3, 4, 4, 5, 5
809 for (i = 0; i < 24; i++) {
811 defpal[i].rgbtRed = cfg.colours[w][0];
812 defpal[i].rgbtGreen = cfg.colours[w][1];
813 defpal[i].rgbtBlue = cfg.colours[w][2];
818 * Set up the colour palette.
820 static void init_palette(void)
823 HDC hdc = GetDC(hwnd);
825 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
826 logpal = smalloc(sizeof(*logpal)
827 - sizeof(logpal->palPalEntry)
828 + NCOLOURS * sizeof(PALETTEENTRY));
829 logpal->palVersion = 0x300;
830 logpal->palNumEntries = NCOLOURS;
831 for (i = 0; i < NCOLOURS; i++) {
832 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
833 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
834 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
835 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
837 pal = CreatePalette(logpal);
839 SelectPalette(hdc, pal, FALSE);
841 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
844 ReleaseDC(hwnd, hdc);
847 for (i = 0; i < NCOLOURS; i++)
848 colours[i] = PALETTERGB(defpal[i].rgbtRed,
852 for (i = 0; i < NCOLOURS; i++)
853 colours[i] = RGB(defpal[i].rgbtRed,
854 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
858 * Initialise all the fonts we will need initially. There may be as many as
859 * three or as few as one. The other (poentially) twentyone fonts are done
860 * if/when they are needed.
864 * - check the font width and height, correcting our guesses if
867 * - verify that the bold font is the same width as the ordinary
868 * one, and engage shadow bolding if not.
870 * - verify that the underlined font is the same width as the
871 * ordinary one (manual underlining by means of line drawing can
872 * be done in a pinch).
874 static void init_fonts(int pick_width, int pick_height)
881 int fw_dontcare, fw_bold;
883 for (i = 0; i < FONT_MAXNO; i++)
886 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
889 if (cfg.fontisbold) {
890 fw_dontcare = FW_BOLD;
893 fw_dontcare = FW_DONTCARE;
900 font_height = pick_height;
902 font_height = cfg.fontheight;
903 if (font_height > 0) {
905 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
908 font_width = pick_width;
911 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
912 c, OUT_DEFAULT_PRECIS, \
913 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
914 FIXED_PITCH | FF_DONTCARE, cfg.font)
916 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
918 lfont.lfHeight = font_height;
919 lfont.lfWidth = font_width;
920 lfont.lfEscapement = 0;
921 lfont.lfOrientation = 0;
922 lfont.lfWeight = fw_dontcare;
923 lfont.lfItalic = FALSE;
924 lfont.lfUnderline = FALSE;
925 lfont.lfStrikeOut = FALSE;
926 lfont.lfCharSet = cfg.fontcharset;
927 lfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
928 lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
929 lfont.lfQuality = DEFAULT_QUALITY;
930 lfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
931 strncpy(lfont.lfFaceName, cfg.font, LF_FACESIZE);
933 SelectObject(hdc, fonts[FONT_NORMAL]);
934 GetTextMetrics(hdc, &tm);
936 if (pick_width == 0 || pick_height == 0) {
937 font_height = tm.tmHeight;
938 font_width = tm.tmAveCharWidth;
940 font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth);
942 #ifdef RDB_DEBUG_PATCH
943 debug(23, "Primary font H=%d, AW=%d, MW=%d",
944 tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth);
949 DWORD cset = tm.tmCharSet;
950 memset(&info, 0xFF, sizeof(info));
952 /* !!! Yes the next line is right */
953 if (cset == OEM_CHARSET)
954 font_codepage = GetOEMCP();
956 if (TranslateCharsetInfo
957 ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
962 GetCPInfo(font_codepage, &cpinfo);
963 dbcs_screenfont = (cpinfo.MaxCharSize > 1);
966 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
969 * Some fonts, e.g. 9-pt Courier, draw their underlines
970 * outside their character cell. We successfully prevent
971 * screen corruption by clipping the text output, but then
972 * we lose the underline completely. Here we try to work
973 * out whether this is such a font, and if it is, we set a
974 * flag that causes underlines to be drawn by hand.
976 * Having tried other more sophisticated approaches (such
977 * as examining the TEXTMETRIC structure or requesting the
978 * height of a string), I think we'll do this the brute
979 * force way: we create a small bitmap, draw an underlined
980 * space on it, and test to see whether any pixels are
981 * foreground-coloured. (Since we expect the underline to
982 * go all the way across the character cell, we only search
983 * down a single column of the bitmap, half way across.)
987 HBITMAP und_bm, und_oldbm;
991 und_dc = CreateCompatibleDC(hdc);
992 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
993 und_oldbm = SelectObject(und_dc, und_bm);
994 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
995 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
996 SetTextColor(und_dc, RGB(255, 255, 255));
997 SetBkColor(und_dc, RGB(0, 0, 0));
998 SetBkMode(und_dc, OPAQUE);
999 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
1001 for (i = 0; i < font_height; i++) {
1002 c = GetPixel(und_dc, font_width / 2, i);
1003 if (c != RGB(0, 0, 0))
1006 SelectObject(und_dc, und_oldbm);
1007 DeleteObject(und_bm);
1010 und_mode = UND_LINE;
1011 DeleteObject(fonts[FONT_UNDERLINE]);
1012 fonts[FONT_UNDERLINE] = 0;
1016 if (bold_mode == BOLD_FONT) {
1017 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
1021 descent = tm.tmAscent + 1;
1022 if (descent >= font_height)
1023 descent = font_height - 1;
1025 for (i = 0; i < 3; i++) {
1027 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
1028 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
1035 ReleaseDC(hwnd, hdc);
1037 if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
1038 und_mode = UND_LINE;
1039 DeleteObject(fonts[FONT_UNDERLINE]);
1040 fonts[FONT_UNDERLINE] = 0;
1043 if (bold_mode == BOLD_FONT &&
1044 fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
1045 bold_mode = BOLD_SHADOW;
1046 DeleteObject(fonts[FONT_BOLD]);
1047 fonts[FONT_BOLD] = 0;
1049 fontflag[0] = fontflag[1] = fontflag[2] = 1;
1054 static void another_font(int fontno)
1057 int fw_dontcare, fw_bold;
1061 if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1064 basefont = (fontno & ~(FONT_BOLDUND));
1065 if (basefont != fontno && !fontflag[basefont])
1066 another_font(basefont);
1068 if (cfg.fontisbold) {
1069 fw_dontcare = FW_BOLD;
1072 fw_dontcare = FW_DONTCARE;
1076 c = cfg.fontcharset;
1082 if (fontno & FONT_WIDE)
1084 if (fontno & FONT_NARROW)
1086 if (fontno & FONT_OEM)
1088 if (fontno & FONT_BOLD)
1090 if (fontno & FONT_UNDERLINE)
1094 CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1095 FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1096 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1097 FIXED_PITCH | FF_DONTCARE, s);
1099 fontflag[fontno] = 1;
1102 static void deinit_fonts(void)
1105 for (i = 0; i < FONT_MAXNO; i++) {
1107 DeleteObject(fonts[i]);
1113 void request_resize(int w, int h)
1117 /* If the window is maximized supress resizing attempts */
1118 if (IsZoomed(hwnd)) {
1119 if (cfg.resize_action == RESIZE_TERM)
1123 if (cfg.resize_action == RESIZE_DISABLED) return;
1124 if (h == rows && w == cols) return;
1126 /* Sanity checks ... */
1128 static int first_time = 1;
1131 switch (first_time) {
1133 /* Get the size of the screen */
1134 if (GetClientRect(GetDesktopWindow(), &ss))
1135 /* first_time = 0 */ ;
1141 /* Make sure the values are sane */
1142 width = (ss.right - ss.left - extra_width) / 4;
1143 height = (ss.bottom - ss.top - extra_height) / 6;
1145 if (w > width || h > height)
1154 term_size(h, w, cfg.savelines);
1156 if (cfg.resize_action != RESIZE_FONT && !IsZoomed(hwnd)) {
1157 width = extra_width + font_width * w;
1158 height = extra_height + font_height * h;
1160 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1161 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1162 SWP_NOMOVE | SWP_NOZORDER);
1166 InvalidateRect(hwnd, NULL, TRUE);
1169 static void reset_window(int reinit) {
1171 * This function decides how to resize or redraw when the
1172 * user changes something.
1174 * This function doesn't like to change the terminal size but if the
1175 * font size is locked that may be it's only soluion.
1177 int win_width, win_height;
1180 #ifdef RDB_DEBUG_PATCH
1181 debug((27, "reset_window()"));
1184 /* Current window sizes ... */
1185 GetWindowRect(hwnd, &wr);
1186 GetClientRect(hwnd, &cr);
1188 win_width = cr.right - cr.left;
1189 win_height = cr.bottom - cr.top;
1191 if (cfg.resize_action == RESIZE_DISABLED) reinit = 2;
1193 /* Are we being forced to reload the fonts ? */
1195 #ifdef RDB_DEBUG_PATCH
1196 debug((27, "reset_window() -- Forced deinit"));
1202 /* Oh, looks like we're minimised */
1203 if (win_width == 0 || win_height == 0)
1206 /* Is the window out of position ? */
1208 (offset_width != (win_width-font_width*cols)/2 ||
1209 offset_height != (win_height-font_height*rows)/2) ){
1210 offset_width = (win_width-font_width*cols)/2;
1211 offset_height = (win_height-font_height*rows)/2;
1212 InvalidateRect(hwnd, NULL, TRUE);
1213 #ifdef RDB_DEBUG_PATCH
1214 debug((27, "reset_window() -> Reposition terminal"));
1218 if (IsZoomed(hwnd)) {
1219 /* We're fullscreen, this means we must not change the size of
1220 * the window so it's the font size or the terminal itself.
1223 extra_width = wr.right - wr.left - cr.right + cr.left;
1224 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1226 if (cfg.resize_action != RESIZE_TERM) {
1227 if ( font_width != win_width/cols ||
1228 font_height != win_height/rows) {
1230 init_fonts(win_width/cols, win_height/rows);
1231 offset_width = (win_width-font_width*cols)/2;
1232 offset_height = (win_height-font_height*rows)/2;
1233 InvalidateRect(hwnd, NULL, TRUE);
1234 #ifdef RDB_DEBUG_PATCH
1235 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1236 font_width, font_height));
1240 if ( font_width != win_width/cols ||
1241 font_height != win_height/rows) {
1242 /* Our only choice at this point is to change the
1243 * size of the terminal; Oh well.
1245 term_size( win_height/font_height, win_width/font_width,
1247 offset_width = (win_width-font_width*cols)/2;
1248 offset_height = (win_height-font_height*rows)/2;
1249 InvalidateRect(hwnd, NULL, TRUE);
1250 #ifdef RDB_DEBUG_PATCH
1251 debug((27, "reset_window() -> Zoomed term_size"));
1258 /* Hmm, a force re-init means we should ignore the current window
1259 * so we resize to the default font size.
1262 #ifdef RDB_DEBUG_PATCH
1263 debug((27, "reset_window() -> Forced re-init"));
1266 offset_width = offset_height = cfg.window_border;
1267 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1268 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1270 if (win_width != font_width*cols + offset_width*2 ||
1271 win_height != font_height*rows + offset_height*2) {
1273 /* If this is too large windows will resize it to the maximum
1274 * allowed window size, we will then be back in here and resize
1275 * the font or terminal to fit.
1277 SetWindowPos(hwnd, NULL, 0, 0,
1278 font_width*cols + extra_width,
1279 font_height*rows + extra_height,
1280 SWP_NOMOVE | SWP_NOZORDER);
1283 InvalidateRect(hwnd, NULL, TRUE);
1287 /* Okay the user doesn't want us to change the font so we try the
1288 * window. But that may be too big for the screen which forces us
1289 * to change the terminal.
1291 if ((cfg.resize_action == RESIZE_TERM && reinit<=0) ||
1292 (cfg.resize_action == RESIZE_EITHER && reinit<0) ||
1294 offset_width = offset_height = cfg.window_border;
1295 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1296 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1298 if (win_width != font_width*cols + offset_width*2 ||
1299 win_height != font_height*rows + offset_height*2) {
1304 GetClientRect(GetDesktopWindow(), &ss);
1305 width = (ss.right - ss.left - extra_width) / font_width;
1306 height = (ss.bottom - ss.top - extra_height) / font_height;
1309 if ( rows > height || cols > width ) {
1310 if (cfg.resize_action == RESIZE_EITHER) {
1311 /* Make the font the biggest we can */
1313 font_width = (ss.right - ss.left - extra_width)/cols;
1315 font_height = (ss.bottom - ss.top - extra_height)/rows;
1318 init_fonts(font_width, font_height);
1320 width = (ss.right - ss.left - extra_width) / font_width;
1321 height = (ss.bottom - ss.top - extra_height) / font_height;
1323 if ( height > rows ) height = rows;
1324 if ( width > cols ) width = cols;
1325 term_size(height, width, cfg.savelines);
1326 #ifdef RDB_DEBUG_PATCH
1327 debug((27, "reset_window() -> term resize to (%d,%d)",
1333 SetWindowPos(hwnd, NULL, 0, 0,
1334 font_width*cols + extra_width,
1335 font_height*rows + extra_height,
1336 SWP_NOMOVE | SWP_NOZORDER);
1338 InvalidateRect(hwnd, NULL, TRUE);
1339 #ifdef RDB_DEBUG_PATCH
1340 debug((27, "reset_window() -> window resize to (%d,%d)",
1341 font_width*cols + extra_width,
1342 font_height*rows + extra_height));
1348 /* We're allowed to or must change the font but do we want to ? */
1350 if (font_width != (win_width-cfg.window_border*2)/cols ||
1351 font_height != (win_height-cfg.window_border*2)/rows) {
1354 init_fonts((win_width-cfg.window_border*2)/cols,
1355 (win_height-cfg.window_border*2)/rows);
1356 offset_width = (win_width-font_width*cols)/2;
1357 offset_height = (win_height-font_height*rows)/2;
1359 extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2;
1360 extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2;
1362 InvalidateRect(hwnd, NULL, TRUE);
1363 #ifdef RDB_DEBUG_PATCH
1364 debug((25, "reset_window() -> font resize to (%d,%d)",
1365 font_width, font_height));
1370 static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
1372 int thistime = GetMessageTime();
1374 if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1375 lastbtn = MBT_NOTHING;
1376 term_mouse(b, MA_CLICK, x, y, shift, ctrl, alt);
1380 if (lastbtn == b && thistime - lasttime < dbltime) {
1381 lastact = (lastact == MA_CLICK ? MA_2CLK :
1382 lastact == MA_2CLK ? MA_3CLK :
1383 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1388 if (lastact != MA_NOTHING)
1389 term_mouse(b, lastact, x, y, shift, ctrl, alt);
1390 lasttime = thistime;
1394 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1395 * into a cooked one (SELECT, EXTEND, PASTE).
1397 Mouse_Button translate_button(Mouse_Button button)
1399 if (button == MBT_LEFT)
1401 if (button == MBT_MIDDLE)
1402 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1403 if (button == MBT_RIGHT)
1404 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1405 return 0; /* shouldn't happen */
1408 static void show_mouseptr(int show)
1410 static int cursor_visible = 1;
1411 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1413 if (cursor_visible && !show)
1415 else if (!cursor_visible && show)
1417 cursor_visible = show;
1420 static int is_alt_pressed(void)
1423 int r = GetKeyboardState(keystate);
1426 if (keystate[VK_MENU] & 0x80)
1428 if (keystate[VK_RMENU] & 0x80)
1433 static int resizing;
1435 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1436 WPARAM wParam, LPARAM lParam)
1439 static int ignore_clip = FALSE;
1440 static int need_backend_resize = FALSE;
1444 if (pending_netevent)
1445 enact_pending_netevent();
1451 if (cfg.ping_interval > 0) {
1454 if (now - last_movement > cfg.ping_interval) {
1455 back->special(TS_PING);
1456 last_movement = now;
1459 net_pending_errors();
1465 if (!cfg.warn_on_close || session_closed ||
1467 "Are you sure you want to close this session?",
1468 "PuTTY Exit Confirmation",
1469 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1470 DestroyWindow(hwnd);
1477 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1489 PROCESS_INFORMATION pi;
1490 HANDLE filemap = NULL;
1492 if (wParam == IDM_DUPSESS) {
1494 * Allocate a file-mapping memory chunk for the
1497 SECURITY_ATTRIBUTES sa;
1500 sa.nLength = sizeof(sa);
1501 sa.lpSecurityDescriptor = NULL;
1502 sa.bInheritHandle = TRUE;
1503 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1506 0, sizeof(Config), NULL);
1508 p = (Config *) MapViewOfFile(filemap,
1510 0, 0, sizeof(Config));
1512 *p = cfg; /* structure copy */
1516 sprintf(c, "putty &%p", filemap);
1518 } else if (wParam == IDM_SAVEDSESS) {
1520 sessions[(lParam - IDM_SAVED_MIN) / 16];
1521 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1523 cl = NULL; /* not a very important failure mode */
1525 sprintf(cl, "putty @%s", session);
1531 GetModuleFileName(NULL, b, sizeof(b) - 1);
1533 si.lpReserved = NULL;
1534 si.lpDesktop = NULL;
1538 si.lpReserved2 = NULL;
1539 CreateProcess(b, cl, NULL, NULL, TRUE,
1540 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1543 CloseHandle(filemap);
1553 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1556 if (!do_reconfig(hwnd))
1560 /* Disable full-screen if resizing forbidden */
1561 HMENU m = GetSystemMenu (hwnd, FALSE);
1562 EnableMenuItem(m, IDM_FULLSCREEN, MF_BYCOMMAND |
1563 (cfg.resize_action == RESIZE_DISABLED)
1564 ? MF_GRAYED : MF_ENABLED);
1565 /* Gracefully unzoom if necessary */
1567 (cfg.resize_action == RESIZE_DISABLED)) {
1572 if (strcmp(prev_cfg.logfilename, cfg.logfilename) ||
1573 prev_cfg.logtype != cfg.logtype) {
1574 logfclose(); /* reset logging */
1580 * Flush the line discipline's edit buffer in the
1581 * case where local editing has just been disabled.
1583 ldisc_send(NULL, 0, 0);
1591 /* Screen size changed ? */
1592 if (cfg.height != prev_cfg.height ||
1593 cfg.width != prev_cfg.width ||
1594 cfg.savelines != prev_cfg.savelines ||
1595 cfg.resize_action == RESIZE_FONT ||
1596 cfg.resize_action == RESIZE_DISABLED)
1597 term_size(cfg.height, cfg.width, cfg.savelines);
1599 /* Enable or disable the scroll bar, etc */
1601 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1602 LONG nexflag, exflag =
1603 GetWindowLong(hwnd, GWL_EXSTYLE);
1606 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
1607 if (cfg.alwaysontop) {
1608 nexflag |= WS_EX_TOPMOST;
1609 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1610 SWP_NOMOVE | SWP_NOSIZE);
1612 nexflag &= ~(WS_EX_TOPMOST);
1613 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1614 SWP_NOMOVE | SWP_NOSIZE);
1617 if (cfg.sunken_edge)
1618 nexflag |= WS_EX_CLIENTEDGE;
1620 nexflag &= ~(WS_EX_CLIENTEDGE);
1624 cfg.scrollbar_in_fullscreen : cfg.scrollbar)
1627 nflg &= ~WS_VSCROLL;
1628 if (cfg.resize_action == RESIZE_DISABLED)
1629 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1631 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1633 if (nflg != flag || nexflag != exflag) {
1635 SetWindowLong(hwnd, GWL_STYLE, nflg);
1636 if (nexflag != exflag)
1637 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1639 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1640 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1641 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1649 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
1654 set_title(cfg.wintitle);
1655 if (IsIconic(hwnd)) {
1657 cfg.win_name_always ? window_name :
1661 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1662 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1663 cfg.fontisbold != prev_cfg.fontisbold ||
1664 cfg.fontheight != prev_cfg.fontheight ||
1665 cfg.fontcharset != prev_cfg.fontcharset ||
1666 cfg.vtmode != prev_cfg.vtmode ||
1667 cfg.bold_colour != prev_cfg.bold_colour ||
1668 cfg.resize_action == RESIZE_DISABLED ||
1669 cfg.resize_action == RESIZE_EITHER ||
1670 (cfg.resize_action != prev_cfg.resize_action))
1673 InvalidateRect(hwnd, NULL, TRUE);
1674 reset_window(init_lvl);
1675 net_pending_errors();
1688 back->special(TS_AYT);
1689 net_pending_errors();
1692 back->special(TS_BRK);
1693 net_pending_errors();
1696 back->special(TS_SYNCH);
1697 net_pending_errors();
1700 back->special(TS_EC);
1701 net_pending_errors();
1704 back->special(TS_EL);
1705 net_pending_errors();
1708 back->special(TS_GA);
1709 net_pending_errors();
1712 back->special(TS_NOP);
1713 net_pending_errors();
1716 back->special(TS_ABORT);
1717 net_pending_errors();
1720 back->special(TS_AO);
1721 net_pending_errors();
1724 back->special(TS_IP);
1725 net_pending_errors();
1728 back->special(TS_SUSP);
1729 net_pending_errors();
1732 back->special(TS_EOR);
1733 net_pending_errors();
1736 back->special(TS_EOF);
1737 net_pending_errors();
1744 * We get this if the System menu has been activated
1751 * We get this if the System menu has been activated
1752 * using the keyboard. This might happen from within
1753 * TranslateKey, in which case it really wants to be
1754 * followed by a `space' character to actually _bring
1755 * the menu up_ rather than just sitting there in
1756 * `ready to appear' state.
1758 show_mouseptr(1); /* make sure pointer is visible */
1760 PostMessage(hwnd, WM_CHAR, ' ', 0);
1762 case IDM_FULLSCREEN:
1766 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1767 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1772 #define X_POS(l) ((int)(short)LOWORD(l))
1773 #define Y_POS(l) ((int)(short)HIWORD(l))
1775 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1776 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1777 #define WHEEL_DELTA 120
1780 wheel_accumulator += (short) HIWORD(wParam);
1781 wParam = LOWORD(wParam);
1783 /* process events when the threshold is reached */
1784 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1787 /* reduce amount for next time */
1788 if (wheel_accumulator > 0) {
1790 wheel_accumulator -= WHEEL_DELTA;
1791 } else if (wheel_accumulator < 0) {
1793 wheel_accumulator += WHEEL_DELTA;
1797 if (send_raw_mouse) {
1798 /* send a mouse-down followed by a mouse up */
1802 TO_CHR_X(X_POS(lParam)),
1803 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1804 wParam & MK_CONTROL, is_alt_pressed());
1805 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1806 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1807 wParam & MK_CONTROL, is_alt_pressed());
1809 /* trigger a scroll */
1811 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1816 case WM_LBUTTONDOWN:
1817 case WM_MBUTTONDOWN:
1818 case WM_RBUTTONDOWN:
1826 case WM_LBUTTONDOWN:
1830 case WM_MBUTTONDOWN:
1831 button = MBT_MIDDLE;
1834 case WM_RBUTTONDOWN:
1843 button = MBT_MIDDLE;
1851 button = press = 0; /* shouldn't happen */
1855 * Special case: in full-screen mode, if the left
1856 * button is clicked in the very top left corner of the
1857 * window, we put up the System menu instead of doing
1860 if (full_screen && press && button == MBT_LEFT &&
1861 X_POS(lParam) == 0 && Y_POS(lParam) == 0) {
1862 SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0);
1867 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1868 wParam & MK_SHIFT, wParam & MK_CONTROL,
1872 term_mouse(button, MA_RELEASE,
1873 TO_CHR_X(X_POS(lParam)),
1874 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1875 wParam & MK_CONTROL, is_alt_pressed());
1883 * Add the mouse position and message time to the random
1886 noise_ultralight(lParam);
1888 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1890 if (wParam & MK_LBUTTON)
1892 else if (wParam & MK_MBUTTON)
1896 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1897 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1898 wParam & MK_CONTROL, is_alt_pressed());
1901 case WM_NCMOUSEMOVE:
1903 noise_ultralight(lParam);
1905 case WM_IGNORE_CLIP:
1906 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1908 case WM_DESTROYCLIPBOARD:
1911 ignore_clip = FALSE;
1917 hdc = BeginPaint(hwnd, &p);
1919 SelectPalette(hdc, pal, TRUE);
1920 RealizePalette(hdc);
1923 (p.rcPaint.left-offset_width)/font_width,
1924 (p.rcPaint.top-offset_height)/font_height,
1925 (p.rcPaint.right-offset_width-1)/font_width,
1926 (p.rcPaint.bottom-offset_height-1)/font_height);
1929 p.rcPaint.left < offset_width ||
1930 p.rcPaint.top < offset_height ||
1931 p.rcPaint.right >= offset_width + font_width*cols ||
1932 p.rcPaint.bottom>= offset_height + font_height*rows)
1934 HBRUSH fillcolour, oldbrush;
1936 fillcolour = CreateSolidBrush (
1937 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1938 oldbrush = SelectObject(hdc, fillcolour);
1939 edge = CreatePen(PS_SOLID, 0,
1940 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1941 oldpen = SelectObject(hdc, edge);
1943 ExcludeClipRect(hdc,
1944 offset_width, offset_height,
1945 offset_width+font_width*cols,
1946 offset_height+font_height*rows);
1948 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
1949 p.rcPaint.right, p.rcPaint.bottom);
1951 // SelectClipRgn(hdc, NULL);
1953 SelectObject(hdc, oldbrush);
1954 DeleteObject(fillcolour);
1955 SelectObject(hdc, oldpen);
1958 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1959 SelectObject(hdc, GetStockObject(WHITE_PEN));
1965 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1966 * but the only one that's likely to try to overload us is FD_READ.
1967 * This means buffering just one is fine.
1969 if (pending_netevent)
1970 enact_pending_netevent();
1972 pending_netevent = TRUE;
1973 pend_netevent_wParam = wParam;
1974 pend_netevent_lParam = lParam;
1975 if (WSAGETSELECTEVENT(lParam) != FD_READ)
1976 enact_pending_netevent();
1978 time(&last_movement);
1982 CreateCaret(hwnd, caretbm, font_width, font_height);
1984 flash_window(0); /* stop */
1990 if (full_screen) flip_full_screen();
1997 case WM_ENTERSIZEMOVE:
1998 #ifdef RDB_DEBUG_PATCH
1999 debug((27, "WM_ENTERSIZEMOVE"));
2003 need_backend_resize = FALSE;
2005 case WM_EXITSIZEMOVE:
2008 #ifdef RDB_DEBUG_PATCH
2009 debug((27, "WM_EXITSIZEMOVE"));
2011 if (need_backend_resize) {
2012 term_size(cfg.height, cfg.width, cfg.savelines);
2013 InvalidateRect(hwnd, NULL, TRUE);
2018 * This does two jobs:
2019 * 1) Keep the sizetip uptodate
2020 * 2) Make sure the window size is _stepped_ in units of the font size.
2022 if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
2023 int width, height, w, h, ew, eh;
2024 LPRECT r = (LPRECT) lParam;
2026 if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
2027 (cfg.height != rows || cfg.width != cols )) {
2029 * Great! It seems that both the terminal size and the
2030 * font size have been changed and the user is now dragging.
2032 * It will now be difficult to get back to the configured
2035 * This would be easier but it seems to be too confusing.
2037 term_size(cfg.height, cfg.width, cfg.savelines);
2040 cfg.height=rows; cfg.width=cols;
2042 InvalidateRect(hwnd, NULL, TRUE);
2043 need_backend_resize = TRUE;
2046 width = r->right - r->left - extra_width;
2047 height = r->bottom - r->top - extra_height;
2048 w = (width + font_width / 2) / font_width;
2051 h = (height + font_height / 2) / font_height;
2054 UpdateSizeTip(hwnd, w, h);
2055 ew = width - w * font_width;
2056 eh = height - h * font_height;
2058 if (wParam == WMSZ_LEFT ||
2059 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2065 if (wParam == WMSZ_TOP ||
2066 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2076 int width, height, w, h, rv = 0;
2077 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2078 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2079 LPRECT r = (LPRECT) lParam;
2081 width = r->right - r->left - ex_width;
2082 height = r->bottom - r->top - ex_height;
2084 w = (width + cols/2)/cols;
2085 h = (height + rows/2)/rows;
2086 if ( r->right != r->left + w*cols + ex_width)
2089 if (wParam == WMSZ_LEFT ||
2090 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2091 r->left = r->right - w*cols - ex_width;
2093 r->right = r->left + w*cols + ex_width;
2095 if (r->bottom != r->top + h*rows + ex_height)
2098 if (wParam == WMSZ_TOP ||
2099 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2100 r->top = r->bottom - h*rows - ex_height;
2102 r->bottom = r->top + h*rows + ex_height;
2106 /* break; (never reached) */
2108 #ifdef RDB_DEBUG_PATCH
2109 debug((27, "WM_SIZE %s (%d,%d)",
2110 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2111 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2112 (wParam == SIZE_RESTORED && resizing) ? "to":
2113 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2115 LOWORD(lParam), HIWORD(lParam)));
2117 if (wParam == SIZE_MINIMIZED)
2119 cfg.win_name_always ? window_name : icon_name);
2120 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2121 SetWindowText(hwnd, window_name);
2123 if (cfg.resize_action == RESIZE_DISABLED) {
2124 /* A resize, well it better be a minimize. */
2128 int width, height, w, h;
2130 width = LOWORD(lParam);
2131 height = HIWORD(lParam);
2134 if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
2138 if (cfg.resize_action == RESIZE_TERM) {
2139 w = width / font_width;
2141 h = height / font_height;
2144 term_size(h, w, cfg.savelines);
2147 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2149 if (cfg.resize_action == RESIZE_TERM)
2150 term_size(prev_rows, prev_cols, cfg.savelines);
2151 if (cfg.resize_action != RESIZE_FONT)
2156 /* This is an unexpected resize, these will normally happen
2157 * if the window is too large. Probably either the user
2158 * selected a huge font or the screen size has changed.
2160 * This is also called with minimize.
2162 else reset_window(-1);
2166 * Don't call back->size in mid-resize. (To prevent
2167 * massive numbers of resize events getting sent
2168 * down the connection during an NT opaque drag.)
2171 if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
2172 need_backend_resize = TRUE;
2173 w = (width-cfg.window_border*2) / font_width;
2175 h = (height-cfg.window_border*2) / font_height;
2186 switch (LOWORD(wParam)) {
2200 term_scroll(0, +rows / 2);
2203 term_scroll(0, -rows / 2);
2205 case SB_THUMBPOSITION:
2207 term_scroll(1, HIWORD(wParam));
2211 case WM_PALETTECHANGED:
2212 if ((HWND) wParam != hwnd && pal != NULL) {
2213 HDC hdc = get_ctx();
2215 if (RealizePalette(hdc) > 0)
2221 case WM_QUERYNEWPALETTE:
2223 HDC hdc = get_ctx();
2225 if (RealizePalette(hdc) > 0)
2237 * Add the scan code and keypress timing to the random
2240 noise_ultralight(lParam);
2243 * We don't do TranslateMessage since it disassociates the
2244 * resulting CHAR message from the KEYDOWN that sparked it,
2245 * which we occasionally don't want. Instead, we process
2246 * KEYDOWN, and call the Win32 translator functions so that
2247 * we get the translations under _our_ control.
2250 unsigned char buf[20];
2253 if (wParam == VK_PROCESSKEY) {
2256 m.message = WM_KEYDOWN;
2258 m.lParam = lParam & 0xdfff;
2259 TranslateMessage(&m);
2261 len = TranslateKey(message, wParam, lParam, buf);
2263 return DefWindowProc(hwnd, message, wParam, lParam);
2267 * Interrupt an ongoing paste. I'm not sure
2268 * this is sensible, but for the moment it's
2269 * preferable to having to faff about buffering
2275 * We need not bother about stdin backlogs
2276 * here, because in GUI PuTTY we can't do
2277 * anything about it anyway; there's no means
2278 * of asking Windows to hold off on KEYDOWN
2279 * messages. We _have_ to buffer everything
2282 ldisc_send(buf, len, 1);
2287 net_pending_errors();
2289 case WM_INPUTLANGCHANGE:
2291 /* wParam == Font number */
2292 /* lParam == Locale */
2294 HKL NewInputLocale = (HKL) lParam;
2296 // lParam == GetKeyboardLayout(0);
2298 GetLocaleInfo(LOWORD(NewInputLocale),
2299 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
2301 kbd_codepage = atoi(lbuf);
2305 if(wParam == IMN_SETOPENSTATUS) {
2306 HIMC hImc = ImmGetContext(hwnd);
2307 ImmSetCompositionFont(hImc, &lfont);
2308 ImmReleaseContext(hwnd, hImc);
2312 case WM_IME_COMPOSITION:
2318 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2319 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2321 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2322 break; /* fall back to DefWindowProc */
2324 hIMC = ImmGetContext(hwnd);
2325 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2328 buff = (char*) smalloc(n);
2329 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2330 luni_send((unsigned short *)buff, n / 2, 1);
2333 ImmReleaseContext(hwnd, hIMC);
2338 if (wParam & 0xFF00) {
2339 unsigned char buf[2];
2342 buf[0] = wParam >> 8;
2343 lpage_send(kbd_codepage, buf, 2, 1);
2345 char c = (unsigned char) wParam;
2346 lpage_send(kbd_codepage, &c, 1, 1);
2352 * Nevertheless, we are prepared to deal with WM_CHAR
2353 * messages, should they crop up. So if someone wants to
2354 * post the things to us as part of a macro manoeuvre,
2355 * we're ready to cope.
2358 char c = (unsigned char)wParam;
2359 lpage_send(CP_ACP, &c, 1, 1);
2363 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2364 SetCursor(LoadCursor(NULL, IDC_ARROW));
2369 return DefWindowProc(hwnd, message, wParam, lParam);
2373 * Move the system caret. (We maintain one, even though it's
2374 * invisible, for the benefit of blind people: apparently some
2375 * helper software tracks the system caret, so we should arrange to
2378 void sys_cursor(int x, int y)
2383 if (!has_focus) return;
2385 SetCaretPos(x * font_width + offset_width,
2386 y * font_height + offset_height);
2388 /* IMM calls on Win98 and beyond only */
2389 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2391 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2392 osVersion.dwMinorVersion == 0) return; /* 95 */
2394 /* we should have the IMM functions */
2395 hIMC = ImmGetContext(hwnd);
2396 cf.dwStyle = CFS_POINT;
2397 cf.ptCurrentPos.x = x * font_width + offset_width;
2398 cf.ptCurrentPos.y = y * font_height + offset_height;
2399 ImmSetCompositionWindow(hIMC, &cf);
2401 ImmReleaseContext(hwnd, hIMC);
2405 * Draw a line of text in the window, at given character
2406 * coordinates, in given attributes.
2408 * We are allowed to fiddle with the contents of `text'.
2410 void do_text(Context ctx, int x, int y, char *text, int len,
2411 unsigned long attr, int lattr)
2414 int nfg, nbg, nfont;
2417 int force_manual_underline = 0;
2418 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2419 int char_width = fnt_width;
2420 int text_adjust = 0;
2421 static int *IpDx = 0, IpDxLEN = 0;
2423 if (attr & ATTR_WIDE)
2426 if (len > IpDxLEN || IpDx[0] != char_width) {
2428 if (len > IpDxLEN) {
2430 IpDx = smalloc((len + 16) * sizeof(int));
2431 IpDxLEN = (len + 16);
2433 for (i = 0; i < IpDxLEN; i++)
2434 IpDx[i] = char_width;
2437 /* Only want the left half of double width lines */
2438 if (lattr != LATTR_NORM && x*2 >= cols)
2446 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2447 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2448 attr ^= ATTR_CUR_XOR;
2452 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2453 /* Assume a poorman font is borken in other ways too. */
2463 nfont |= FONT_WIDE + FONT_HIGH;
2466 if (attr & ATTR_NARROW)
2467 nfont |= FONT_NARROW;
2469 /* Special hack for the VT100 linedraw glyphs. */
2470 if ((attr & CSET_MASK) == 0x2300) {
2471 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2472 switch ((unsigned char) (text[0])) {
2474 text_adjust = -2 * font_height / 5;
2477 text_adjust = -1 * font_height / 5;
2480 text_adjust = font_height / 5;
2483 text_adjust = 2 * font_height / 5;
2486 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2489 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2490 attr |= (unitab_xterm['q'] & CSET_MASK);
2491 if (attr & ATTR_UNDER) {
2492 attr &= ~ATTR_UNDER;
2493 force_manual_underline = 1;
2498 /* Anything left as an original character set is unprintable. */
2499 if (DIRECT_CHAR(attr)) {
2502 memset(text, 0xFD, len);
2506 if ((attr & CSET_MASK) == ATTR_OEMCP)
2509 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2510 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2511 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2513 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2514 nfont |= FONT_UNDERLINE;
2515 another_font(nfont);
2516 if (!fonts[nfont]) {
2517 if (nfont & FONT_UNDERLINE)
2518 force_manual_underline = 1;
2519 /* Don't do the same for manual bold, it could be bad news. */
2521 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2523 another_font(nfont);
2525 nfont = FONT_NORMAL;
2526 if (attr & ATTR_REVERSE) {
2531 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2533 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2537 SelectObject(hdc, fonts[nfont]);
2538 SetTextColor(hdc, fg);
2539 SetBkColor(hdc, bg);
2540 SetBkMode(hdc, OPAQUE);
2543 line_box.right = x + char_width * len;
2544 line_box.bottom = y + font_height;
2546 /* Only want the left half of double width lines */
2547 if (line_box.right > font_width*cols+offset_width)
2548 line_box.right = font_width*cols+offset_width;
2550 /* We're using a private area for direct to font. (512 chars.) */
2551 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2552 /* Ho Hum, dbcs fonts are a PITA! */
2553 /* To display on W9x I have to convert to UCS */
2554 static wchar_t *uni_buf = 0;
2555 static int uni_len = 0;
2557 if (len > uni_len) {
2559 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2562 for(nlen = mptr = 0; mptr<len; mptr++) {
2563 uni_buf[nlen] = 0xFFFD;
2564 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2565 IpDx[nlen] += char_width;
2566 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2567 text+mptr, 2, uni_buf+nlen, 1);
2572 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2573 text+mptr, 1, uni_buf+nlen, 1);
2581 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2582 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2583 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2584 SetBkMode(hdc, TRANSPARENT);
2585 ExtTextOutW(hdc, x - 1,
2586 y - font_height * (lattr ==
2587 LATTR_BOT) + text_adjust,
2588 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2592 } else if (DIRECT_FONT(attr)) {
2594 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2595 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2596 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2597 SetBkMode(hdc, TRANSPARENT);
2599 /* GRR: This draws the character outside it's box and can leave
2600 * 'droppings' even with the clip box! I suppose I could loop it
2601 * one character at a time ... yuk.
2603 * Or ... I could do a test print with "W", and use +1 or -1 for this
2604 * shift depending on if the leftmost column is blank...
2606 ExtTextOut(hdc, x - 1,
2607 y - font_height * (lattr ==
2608 LATTR_BOT) + text_adjust,
2609 ETO_CLIPPED, &line_box, text, len, IpDx);
2612 /* And 'normal' unicode characters */
2613 static WCHAR *wbuf = NULL;
2614 static int wlen = 0;
2619 wbuf = smalloc(wlen * sizeof(WCHAR));
2621 for (i = 0; i < len; i++)
2622 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2625 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2626 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2628 /* And the shadow bold hack. */
2629 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2630 SetBkMode(hdc, TRANSPARENT);
2631 ExtTextOutW(hdc, x - 1,
2632 y - font_height * (lattr ==
2633 LATTR_BOT) + text_adjust,
2634 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2637 if (lattr != LATTR_TOP && (force_manual_underline ||
2638 (und_mode == UND_LINE
2639 && (attr & ATTR_UNDER)))) {
2642 if (lattr == LATTR_BOT)
2643 dec = dec * 2 - font_height;
2645 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2646 MoveToEx(hdc, x, y + dec, NULL);
2647 LineTo(hdc, x + len * char_width, y + dec);
2648 oldpen = SelectObject(hdc, oldpen);
2649 DeleteObject(oldpen);
2653 void do_cursor(Context ctx, int x, int y, char *text, int len,
2654 unsigned long attr, int lattr)
2660 int ctype = cfg.cursor_type;
2662 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2663 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2664 do_text(ctx, x, y, text, len, attr, lattr);
2668 attr |= TATTR_RIGHTCURS;
2671 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2672 if (attr & ATTR_WIDE)
2679 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2682 pts[0].x = pts[1].x = pts[4].x = x;
2683 pts[2].x = pts[3].x = x + char_width - 1;
2684 pts[0].y = pts[3].y = pts[4].y = y;
2685 pts[1].y = pts[2].y = y + font_height - 1;
2686 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2687 Polyline(hdc, pts, 5);
2688 oldpen = SelectObject(hdc, oldpen);
2689 DeleteObject(oldpen);
2690 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2691 int startx, starty, dx, dy, length, i;
2694 starty = y + descent;
2697 length = char_width;
2700 if (attr & TATTR_RIGHTCURS)
2701 xadjust = char_width - 1;
2702 startx = x + xadjust;
2706 length = font_height;
2708 if (attr & TATTR_ACTCURS) {
2711 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2712 MoveToEx(hdc, startx, starty, NULL);
2713 LineTo(hdc, startx + dx * length, starty + dy * length);
2714 oldpen = SelectObject(hdc, oldpen);
2715 DeleteObject(oldpen);
2717 for (i = 0; i < length; i++) {
2719 SetPixel(hdc, startx, starty, colours[23]);
2728 /* This function gets the actual width of a character in the normal font.
2730 int CharWidth(Context ctx, int uc) {
2734 /* If the font max is the same as the font ave width then this
2735 * function is a no-op.
2737 if (!font_dualwidth) return 1;
2739 switch (uc & CSET_MASK) {
2741 uc = unitab_line[uc & 0xFF];
2744 uc = unitab_xterm[uc & 0xFF];
2747 uc = unitab_scoacs[uc & 0xFF];
2750 if (DIRECT_FONT(uc)) {
2751 if (dbcs_screenfont) return 1;
2753 /* Speedup, I know of no font where ascii is the wrong width */
2754 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2757 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2758 SelectObject(hdc, fonts[FONT_NORMAL]);
2759 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2760 another_font(FONT_OEM);
2761 if (!fonts[FONT_OEM]) return 0;
2763 SelectObject(hdc, fonts[FONT_OEM]);
2767 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2768 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2771 /* Speedup, I know of no font where ascii is the wrong width */
2772 if (uc >= ' ' && uc <= '~') return 1;
2774 SelectObject(hdc, fonts[FONT_NORMAL]);
2775 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2776 /* Okay that one worked */ ;
2777 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2778 /* This should work on 9x too, but it's "less accurate" */ ;
2783 ibuf += font_width / 2 -1;
2790 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2791 * codes. Returns number of bytes used or zero to drop the message
2792 * or -1 to forward the message to windows.
2794 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2795 unsigned char *output)
2798 int scan, left_alt = 0, key_down, shift_state;
2800 unsigned char *p = output;
2801 static int alt_sum = 0;
2803 HKL kbd_layout = GetKeyboardLayout(0);
2805 static WORD keys[3];
2806 static int compose_char = 0;
2807 static WPARAM compose_key = 0;
2809 r = GetKeyboardState(keystate);
2811 memset(keystate, 0, sizeof(keystate));
2814 #define SHOW_TOASCII_RESULT
2815 { /* Tell us all about key events */
2816 static BYTE oldstate[256];
2817 static int first = 1;
2821 memcpy(oldstate, keystate, sizeof(oldstate));
2824 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2826 } else if ((HIWORD(lParam) & KF_UP)
2827 && scan == (HIWORD(lParam) & 0xFF)) {
2831 if (wParam >= VK_F1 && wParam <= VK_F20)
2832 debug(("K_F%d", wParam + 1 - VK_F1));
2845 debug(("VK_%02x", wParam));
2847 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2849 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2851 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2852 if (ch >= ' ' && ch <= '~')
2853 debug((", '%c'", ch));
2855 debug((", $%02x", ch));
2858 debug((", KB0=%02x", keys[0]));
2860 debug((", KB1=%02x", keys[1]));
2862 debug((", KB2=%02x", keys[2]));
2864 if ((keystate[VK_SHIFT] & 0x80) != 0)
2866 if ((keystate[VK_CONTROL] & 0x80) != 0)
2868 if ((HIWORD(lParam) & KF_EXTENDED))
2870 if ((HIWORD(lParam) & KF_UP))
2874 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2875 else if ((HIWORD(lParam) & KF_UP))
2876 oldstate[wParam & 0xFF] ^= 0x80;
2878 oldstate[wParam & 0xFF] ^= 0x81;
2880 for (ch = 0; ch < 256; ch++)
2881 if (oldstate[ch] != keystate[ch])
2882 debug((", M%02x=%02x", ch, keystate[ch]));
2884 memcpy(oldstate, keystate, sizeof(oldstate));
2888 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2889 keystate[VK_RMENU] = keystate[VK_MENU];
2893 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2894 if ((cfg.funky_type == 3 ||
2895 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2896 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2898 wParam = VK_EXECUTE;
2900 /* UnToggle NUMLock */
2901 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2902 keystate[VK_NUMLOCK] ^= 1;
2905 /* And write back the 'adjusted' state */
2906 SetKeyboardState(keystate);
2909 /* Disable Auto repeat if required */
2910 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2913 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2916 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2918 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2919 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2920 if (cfg.ctrlaltkeys)
2921 keystate[VK_MENU] = 0;
2923 keystate[VK_RMENU] = 0x80;
2928 alt_pressed = (left_alt && key_down);
2930 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2931 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2932 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2934 /* Note if AltGr was pressed and if it was used as a compose key */
2935 if (!compose_state) {
2936 compose_key = 0x100;
2937 if (cfg.compose_key) {
2938 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2939 compose_key = wParam;
2941 if (wParam == VK_APPS)
2942 compose_key = wParam;
2945 if (wParam == compose_key) {
2946 if (compose_state == 0
2947 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2949 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2953 } else if (compose_state == 1 && wParam != VK_CONTROL)
2957 * Record that we pressed key so the scroll window can be reset, but
2958 * be careful to avoid Shift-UP/Down
2960 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
2961 wParam != VK_MENU && wParam != VK_CONTROL) {
2965 if (compose_state > 1 && left_alt)
2968 /* Sanitize the number pad if not using a PC NumPad */
2969 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2970 && cfg.funky_type != 2)
2971 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2972 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2976 nParam = VK_NUMPAD0;
2979 nParam = VK_NUMPAD1;
2982 nParam = VK_NUMPAD2;
2985 nParam = VK_NUMPAD3;
2988 nParam = VK_NUMPAD4;
2991 nParam = VK_NUMPAD5;
2994 nParam = VK_NUMPAD6;
2997 nParam = VK_NUMPAD7;
3000 nParam = VK_NUMPAD8;
3003 nParam = VK_NUMPAD9;
3006 nParam = VK_DECIMAL;
3010 if (keystate[VK_NUMLOCK] & 1)
3017 /* If a key is pressed and AltGr is not active */
3018 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3019 /* Okay, prepare for most alts then ... */
3023 /* Lets see if it's a pattern we know all about ... */
3024 if (wParam == VK_PRIOR && shift_state == 1) {
3025 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3028 if (wParam == VK_NEXT && shift_state == 1) {
3029 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3032 if (wParam == VK_INSERT && shift_state == 1) {
3036 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
3039 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
3040 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3043 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3044 (cfg.resize_action != RESIZE_DISABLED)) {
3045 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3049 /* Control-Numlock for app-keypad mode switch */
3050 if (wParam == VK_PAUSE && shift_state == 2) {
3051 app_keypad_keys ^= 1;
3055 /* Nethack keypad */
3056 if (cfg.nethack_keypad && !left_alt) {
3059 *p++ = shift_state ? 'B' : 'b';
3062 *p++ = shift_state ? 'J' : 'j';
3065 *p++ = shift_state ? 'N' : 'n';
3068 *p++ = shift_state ? 'H' : 'h';
3071 *p++ = shift_state ? '.' : '.';
3074 *p++ = shift_state ? 'L' : 'l';
3077 *p++ = shift_state ? 'Y' : 'y';
3080 *p++ = shift_state ? 'K' : 'k';
3083 *p++ = shift_state ? 'U' : 'u';
3088 /* Application Keypad */
3092 if (cfg.funky_type == 3 ||
3093 (cfg.funky_type <= 1 &&
3094 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3108 if (app_keypad_keys && !cfg.no_applic_k)
3145 if (cfg.funky_type == 2) {
3150 } else if (shift_state)
3157 if (cfg.funky_type == 2)
3161 if (cfg.funky_type == 2)
3165 if (cfg.funky_type == 2)
3170 if (HIWORD(lParam) & KF_EXTENDED)
3176 if (xkey >= 'P' && xkey <= 'S')
3177 p += sprintf((char *) p, "\x1B%c", xkey);
3179 p += sprintf((char *) p, "\x1B?%c", xkey);
3181 p += sprintf((char *) p, "\x1BO%c", xkey);
3186 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
3187 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3191 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3197 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3201 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3205 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3210 if (wParam == VK_PAUSE) { /* Break/Pause */
3215 /* Control-2 to Control-8 are special */
3216 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3217 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3220 if (shift_state == 2 && wParam == 0xBD) {
3224 if (shift_state == 2 && wParam == 0xDF) {
3228 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3235 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3236 * for integer decimal nn.)
3238 * We also deal with the weird ones here. Linux VCs replace F1
3239 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3240 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3246 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3249 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3252 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3255 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3258 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3261 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3264 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3267 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3270 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3273 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3306 if ((shift_state&2) == 0) switch (wParam) {
3326 /* Reorder edit keys to physical order */
3327 if (cfg.funky_type == 3 && code <= 6)
3328 code = "\0\2\1\4\5\3\6"[code];
3330 if (vt52_mode && code > 0 && code <= 6) {
3331 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3335 if (cfg.funky_type == 5 && /* SCO function keys */
3336 code >= 11 && code <= 34) {
3337 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3340 case VK_F1: index = 0; break;
3341 case VK_F2: index = 1; break;
3342 case VK_F3: index = 2; break;
3343 case VK_F4: index = 3; break;
3344 case VK_F5: index = 4; break;
3345 case VK_F6: index = 5; break;
3346 case VK_F7: index = 6; break;
3347 case VK_F8: index = 7; break;
3348 case VK_F9: index = 8; break;
3349 case VK_F10: index = 9; break;
3350 case VK_F11: index = 10; break;
3351 case VK_F12: index = 11; break;
3353 if (keystate[VK_SHIFT] & 0x80) index += 12;
3354 if (keystate[VK_CONTROL] & 0x80) index += 24;
3355 p += sprintf((char *) p, "\x1B[%c", codes[index]);
3358 if (cfg.funky_type == 5 && /* SCO small keypad */
3359 code >= 1 && code <= 6) {
3360 char codes[] = "HL.FIG";
3364 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3368 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3375 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3378 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3381 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3382 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3385 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3387 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3389 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3392 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3393 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3397 p += sprintf((char *) p, "\x1B[%d~", code);
3402 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3403 * some reason seems to send VK_CLEAR to Windows...).
3426 p += sprintf((char *) p, "\x1B%c", xkey);
3428 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3429 /* VT100 & VT102 manuals both state the app cursor keys
3430 * only work if the app keypad is on.
3432 if (!app_keypad_keys)
3434 /* Useful mapping of Ctrl-arrows */
3435 if (shift_state == 2)
3439 p += sprintf((char *) p, "\x1BO%c", xkey);
3441 p += sprintf((char *) p, "\x1B[%c", xkey);
3448 * Finally, deal with Return ourselves. (Win95 seems to
3449 * foul it up when Alt is pressed, for some reason.)
3451 if (wParam == VK_RETURN) { /* Return */
3457 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3458 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3463 /* Okay we've done everything interesting; let windows deal with
3464 * the boring stuff */
3468 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3469 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3471 keystate[VK_CAPITAL] = 0;
3474 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3475 #ifdef SHOW_TOASCII_RESULT
3476 if (r == 1 && !key_down) {
3478 if (in_utf || dbcs_screenfont)
3479 debug((", (U+%04x)", alt_sum));
3481 debug((", LCH(%d)", alt_sum));
3483 debug((", ACH(%d)", keys[0]));
3488 for (r1 = 0; r1 < r; r1++) {
3489 debug(("%s%d", r1 ? "," : "", keys[r1]));
3498 * Interrupt an ongoing paste. I'm not sure this is
3499 * sensible, but for the moment it's preferable to
3500 * having to faff about buffering things.
3505 for (i = 0; i < r; i++) {
3506 unsigned char ch = (unsigned char) keys[i];
3508 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3513 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3517 if ((nc = check_compose(compose_char, ch)) == -1) {
3518 MessageBeep(MB_ICONHAND);
3522 luni_send(&keybuf, 1, 1);
3530 if (in_utf || dbcs_screenfont) {
3532 luni_send(&keybuf, 1, 1);
3534 ch = (char) alt_sum;
3536 * We need not bother about stdin
3537 * backlogs here, because in GUI PuTTY
3538 * we can't do anything about it
3539 * anyway; there's no means of asking
3540 * Windows to hold off on KEYDOWN
3541 * messages. We _have_ to buffer
3542 * everything we're sent.
3544 ldisc_send(&ch, 1, 1);
3548 lpage_send(kbd_codepage, &ch, 1, 1);
3550 if(capsOn && ch < 0x80) {
3553 cbuf[1] = xlat_uskbd2cyrllic(ch);
3554 luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3559 lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3565 /* This is so the ALT-Numpad and dead keys work correctly. */
3570 /* If we're definitly not building up an ALT-54321 then clear it */
3573 /* If we will be using alt_sum fix the 256s */
3574 else if (keys[0] && (in_utf || dbcs_screenfont))
3579 * ALT alone may or may not want to bring up the System menu.
3580 * If it's not meant to, we return 0 on presses or releases of
3581 * ALT, to show that we've swallowed the keystroke. Otherwise
3582 * we return -1, which means Windows will give the keystroke
3583 * its default handling (i.e. bring up the System menu).
3585 if (wParam == VK_MENU && !cfg.alt_only)
3591 void set_title(char *title)
3594 window_name = smalloc(1 + strlen(title));
3595 strcpy(window_name, title);
3596 if (cfg.win_name_always || !IsIconic(hwnd))
3597 SetWindowText(hwnd, title);
3600 void set_icon(char *title)
3603 icon_name = smalloc(1 + strlen(title));
3604 strcpy(icon_name, title);
3605 if (!cfg.win_name_always && IsIconic(hwnd))
3606 SetWindowText(hwnd, title);
3609 void set_sbar(int total, int start, int page)
3613 if ((full_screen && !cfg.scrollbar_in_fullscreen) ||
3614 (!full_screen && !cfg.scrollbar))
3617 si.cbSize = sizeof(si);
3618 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3620 si.nMax = total - 1;
3624 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3627 Context get_ctx(void)
3633 SelectPalette(hdc, pal, FALSE);
3639 void free_ctx(Context ctx)
3641 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3642 ReleaseDC(hwnd, ctx);
3645 static void real_palette_set(int n, int r, int g, int b)
3648 logpal->palPalEntry[n].peRed = r;
3649 logpal->palPalEntry[n].peGreen = g;
3650 logpal->palPalEntry[n].peBlue = b;
3651 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3652 colours[n] = PALETTERGB(r, g, b);
3653 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3655 colours[n] = RGB(r, g, b);
3658 void palette_set(int n, int r, int g, int b)
3660 static const int first[21] = {
3661 0, 2, 4, 6, 8, 10, 12, 14,
3662 1, 3, 5, 7, 9, 11, 13, 15,
3665 real_palette_set(first[n], r, g, b);
3667 real_palette_set(first[n] + 1, r, g, b);
3669 HDC hdc = get_ctx();
3670 UnrealizeObject(pal);
3671 RealizePalette(hdc);
3676 void palette_reset(void)
3680 for (i = 0; i < NCOLOURS; i++) {
3682 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3683 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3684 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3685 logpal->palPalEntry[i].peFlags = 0;
3686 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3687 defpal[i].rgbtGreen,
3688 defpal[i].rgbtBlue);
3690 colours[i] = RGB(defpal[i].rgbtRed,
3691 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3696 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3698 RealizePalette(hdc);
3703 void write_aclip(char *data, int len, int must_deselect)
3708 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3711 lock = GlobalLock(clipdata);
3714 memcpy(lock, data, len);
3715 ((unsigned char *) lock)[len] = 0;
3716 GlobalUnlock(clipdata);
3719 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3721 if (OpenClipboard(hwnd)) {
3723 SetClipboardData(CF_TEXT, clipdata);
3726 GlobalFree(clipdata);
3729 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3733 * Note: unlike write_aclip() this will not append a nul.
3735 void write_clip(wchar_t * data, int len, int must_deselect)
3737 HGLOBAL clipdata, clipdata2, clipdata3;
3739 void *lock, *lock2, *lock3;
3741 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3743 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3744 len * sizeof(wchar_t));
3745 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3747 if (!clipdata || !clipdata2 || !clipdata3) {
3749 GlobalFree(clipdata);
3751 GlobalFree(clipdata2);
3753 GlobalFree(clipdata3);
3756 if (!(lock = GlobalLock(clipdata)))
3758 if (!(lock2 = GlobalLock(clipdata2)))
3761 memcpy(lock, data, len * sizeof(wchar_t));
3762 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3764 if (cfg.rtf_paste) {
3765 wchar_t unitab[256];
3767 unsigned char *tdata = (unsigned char *)lock2;
3768 wchar_t *udata = (wchar_t *)lock;
3769 int rtflen = 0, uindex = 0, tindex = 0;
3771 int multilen, blen, alen, totallen, i;
3772 char before[16], after[4];
3774 get_unitab(CP_ACP, unitab, 0);
3776 rtfsize = 100 + strlen(cfg.font);
3777 rtf = smalloc(rtfsize);
3778 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3779 GetACP(), cfg.font);
3780 rtflen = strlen(rtf);
3783 * We want to construct a piece of RTF that specifies the
3784 * same Unicode text. To do this we will read back in
3785 * parallel from the Unicode data in `udata' and the
3786 * non-Unicode data in `tdata'. For each character in
3787 * `tdata' which becomes the right thing in `udata' when
3788 * looked up in `unitab', we just copy straight over from
3789 * tdata. For each one that doesn't, we must WCToMB it
3790 * individually and produce a \u escape sequence.
3792 * It would probably be more robust to just bite the bullet
3793 * and WCToMB each individual Unicode character one by one,
3794 * then MBToWC each one back to see if it was an accurate
3795 * translation; but that strikes me as a horrifying number
3796 * of Windows API calls so I want to see if this faster way
3797 * will work. If it screws up badly we can always revert to
3798 * the simple and slow way.
3800 while (tindex < len2 && uindex < len &&
3801 tdata[tindex] && udata[uindex]) {
3802 if (tindex + 1 < len2 &&
3803 tdata[tindex] == '\r' &&
3804 tdata[tindex+1] == '\n') {
3808 if (unitab[tdata[tindex]] == udata[uindex]) {
3814 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
3815 NULL, 0, NULL, NULL);
3816 if (multilen != 1) {
3817 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
3819 alen = 1; strcpy(after, "}");
3821 blen = sprintf(before, "\\u%d", udata[uindex]);
3822 alen = 0; after[0] = '\0';
3825 assert(tindex + multilen <= len2);
3826 totallen = blen + alen;
3827 for (i = 0; i < multilen; i++) {
3828 if (tdata[tindex+i] == '\\' ||
3829 tdata[tindex+i] == '{' ||
3830 tdata[tindex+i] == '}')
3832 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
3833 totallen += 6; /* \par\r\n */
3834 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
3840 if (rtfsize < rtflen + totallen + 3) {
3841 rtfsize = rtflen + totallen + 512;
3842 rtf = srealloc(rtf, rtfsize);
3845 strcpy(rtf + rtflen, before); rtflen += blen;
3846 for (i = 0; i < multilen; i++) {
3847 if (tdata[tindex+i] == '\\' ||
3848 tdata[tindex+i] == '{' ||
3849 tdata[tindex+i] == '}') {
3850 rtf[rtflen++] = '\\';
3851 rtf[rtflen++] = tdata[tindex+i];
3852 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
3853 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
3854 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
3855 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
3857 rtf[rtflen++] = tdata[tindex+i];
3860 strcpy(rtf + rtflen, after); rtflen += alen;
3866 strcpy(rtf + rtflen, "}");
3869 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
3870 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
3872 GlobalUnlock(clipdata3);
3878 GlobalUnlock(clipdata);
3879 GlobalUnlock(clipdata2);
3882 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3884 if (OpenClipboard(hwnd)) {
3886 SetClipboardData(CF_UNICODETEXT, clipdata);
3887 SetClipboardData(CF_TEXT, clipdata2);
3889 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
3892 GlobalFree(clipdata);
3893 GlobalFree(clipdata2);
3897 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3900 void get_clip(wchar_t ** p, int *len)
3902 static HGLOBAL clipdata = NULL;
3903 static wchar_t *converted = 0;
3912 GlobalUnlock(clipdata);
3915 } else if (OpenClipboard(NULL)) {
3916 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3918 *p = GlobalLock(clipdata);
3920 for (p2 = *p; *p2; p2++);
3924 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3928 s = GlobalLock(clipdata);
3929 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3930 *p = converted = smalloc(i * sizeof(wchar_t));
3931 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3944 * Move `lines' lines from position `from' to position `to' in the
3947 void optimised_move(int to, int from, int lines)
3952 min = (to < from ? to : from);
3953 max = to + from - min;
3955 r.left = offset_width;
3956 r.right = offset_width + cols * font_width;
3957 r.top = offset_height + min * font_height;
3958 r.bottom = offset_height + (max + lines) * font_height;
3959 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3964 * Print a message box and perform a fatal exit.
3966 void fatalbox(char *fmt, ...)
3972 vsprintf(stuff, fmt, ap);
3974 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3979 * Manage window caption / taskbar flashing, if enabled.
3980 * 0 = stop, 1 = maintain, 2 = start
3982 static void flash_window(int mode)
3984 static long last_flash = 0;
3985 static int flashing = 0;
3986 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3989 FlashWindow(hwnd, FALSE);
3993 } else if (mode == 2) {
3996 last_flash = GetTickCount();
3998 FlashWindow(hwnd, TRUE);
4001 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4004 long now = GetTickCount();
4005 long fdiff = now - last_flash;
4006 if (fdiff < 0 || fdiff > 450) {
4008 FlashWindow(hwnd, TRUE); /* toggle */
4019 if (mode == BELL_DEFAULT) {
4021 * For MessageBeep style bells, we want to be careful of
4022 * timing, because they don't have the nice property of
4023 * PlaySound bells that each one cancels the previous
4024 * active one. So we limit the rate to one per 50ms or so.
4026 static long lastbeep = 0;
4029 beepdiff = GetTickCount() - lastbeep;
4030 if (beepdiff >= 0 && beepdiff < 50)
4034 * The above MessageBeep call takes time, so we record the
4035 * time _after_ it finishes rather than before it starts.
4037 lastbeep = GetTickCount();
4038 } else if (mode == BELL_WAVEFILE) {
4039 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
4040 char buf[sizeof(cfg.bell_wavefile) + 80];
4041 sprintf(buf, "Unable to play sound file\n%s\n"
4042 "Using default sound instead", cfg.bell_wavefile);
4043 MessageBox(hwnd, buf, "PuTTY Sound Error",
4044 MB_OK | MB_ICONEXCLAMATION);
4045 cfg.beep = BELL_DEFAULT;
4048 /* Otherwise, either visual bell or disabled; do nothing here */
4050 flash_window(2); /* start */
4055 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
4057 * Revised by <wez@thebrainroom.com>
4059 static void flip_full_screen(void)
4064 wp.length = sizeof(wp);
4065 GetWindowPlacement(hwnd, &wp);
4067 full_screen = !full_screen;
4070 if (wp.showCmd == SW_SHOWMAXIMIZED) {
4071 /* Ooops it was already 'zoomed' we have to unzoom it before
4072 * everything will work right.
4074 wp.showCmd = SW_SHOWNORMAL;
4075 SetWindowPlacement(hwnd, &wp);
4078 style = GetWindowLong(hwnd, GWL_STYLE) & ~(WS_CAPTION|WS_THICKFRAME);
4079 style &= ~WS_VSCROLL;
4080 if (cfg.scrollbar_in_fullscreen)
4081 style |= WS_VSCROLL;
4082 SetWindowLong(hwnd, GWL_STYLE, style);
4084 /* Some versions of explorer get confused and don't take
4085 * notice of us going fullscreen, so go topmost too.
4087 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
4088 SWP_NOACTIVATE | SWP_NOCOPYBITS |
4089 SWP_NOMOVE | SWP_NOSIZE |
4092 wp.showCmd = SW_SHOWMAXIMIZED;
4093 SetWindowPlacement(hwnd, &wp);
4095 style = GetWindowLong(hwnd, GWL_STYLE) | WS_CAPTION;
4096 if (cfg.resize_action != RESIZE_DISABLED)
4097 style |= WS_THICKFRAME;
4098 style &= ~WS_VSCROLL;
4100 style |= WS_VSCROLL;
4101 SetWindowLong(hwnd, GWL_STYLE, style);
4103 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
4104 SWP_NOACTIVATE | SWP_NOCOPYBITS |
4105 SWP_NOMOVE | SWP_NOSIZE |
4108 wp.showCmd = SW_SHOWNORMAL;
4109 SetWindowPlacement(hwnd, &wp);
4112 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4113 MF_BYCOMMAND| full_screen ? MF_CHECKED : MF_UNCHECKED);
4117 * Minimise or restore the window in response to a server-side
4120 void set_iconic(int iconic)
4122 if (IsIconic(hwnd)) {
4124 ShowWindow(hwnd, SW_RESTORE);
4127 ShowWindow(hwnd, SW_MINIMIZE);
4132 * Move the window in response to a server-side request.
4134 void move_window(int x, int y)
4136 SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4140 * Move the window to the top or bottom of the z-order in response
4141 * to a server-side request.
4143 void set_zorder(int top)
4145 if (cfg.alwaysontop || full_screen)
4146 return; /* ignore */
4147 SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4148 SWP_NOMOVE | SWP_NOSIZE);
4152 * Refresh the window in response to a server-side request.
4154 void refresh_window(void)
4156 InvalidateRect(hwnd, NULL, TRUE);
4160 * Maximise or restore the window in response to a server-side
4163 void set_zoomed(int zoomed)
4165 if (IsZoomed(hwnd) || full_screen) {
4170 ShowWindow(hwnd, SW_RESTORE);
4174 ShowWindow(hwnd, SW_MAXIMIZE);
4179 * Report whether the window is iconic, for terminal reports.
4183 return IsIconic(hwnd);
4187 * Report the window's position, for terminal reports.
4189 void get_window_pos(int *x, int *y)
4192 GetWindowRect(hwnd, &r);
4198 * Report the window's pixel size, for terminal reports.
4200 void get_window_pixels(int *x, int *y)
4203 GetWindowRect(hwnd, &r);
4204 *x = r.right - r.left;
4205 *y = r.bottom - r.top;
4209 * Return the window or icon title.
4211 char *get_window_title(int icon)
4213 return icon ? icon_name : window_name;