17 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
23 #define IDM_SHOWLOG 0x0010
24 #define IDM_NEWSESS 0x0020
25 #define IDM_DUPSESS 0x0030
26 #define IDM_RECONF 0x0040
27 #define IDM_CLRSB 0x0050
28 #define IDM_RESET 0x0060
29 #define IDM_TEL_AYT 0x0070
30 #define IDM_TEL_BRK 0x0080
31 #define IDM_TEL_SYNCH 0x0090
32 #define IDM_TEL_EC 0x00a0
33 #define IDM_TEL_EL 0x00b0
34 #define IDM_TEL_GA 0x00c0
35 #define IDM_TEL_NOP 0x00d0
36 #define IDM_TEL_ABORT 0x00e0
37 #define IDM_TEL_AO 0x00f0
38 #define IDM_TEL_IP 0x0100
39 #define IDM_TEL_SUSP 0x0110
40 #define IDM_TEL_EOR 0x0120
41 #define IDM_TEL_EOF 0x0130
42 #define IDM_ABOUT 0x0140
43 #define IDM_SAVEDSESS 0x0150
44 #define IDM_COPYALL 0x0160
46 #define IDM_SESSLGP 0x0250 /* log type printable */
47 #define IDM_SESSLGA 0x0260 /* log type all chars */
48 #define IDM_SESSLGE 0x0270 /* log end */
49 #define IDM_SAVED_MIN 0x1000
50 #define IDM_SAVED_MAX 0x2000
52 #define WM_IGNORE_SIZE (WM_XUSER + 1)
53 #define WM_IGNORE_CLIP (WM_XUSER + 2)
55 /* Needed for Chinese support and apparently not always defined. */
57 #define VK_PROCESSKEY 0xE5
60 /* Needed for mouse wheel support and not defined in earlier SDKs. */
62 #define WM_MOUSEWHEEL 0x020A
65 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
66 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
67 unsigned char *output);
68 static void cfgtopalette(void);
69 static void init_palette(void);
70 static void init_fonts(int);
71 static void another_font(int);
72 static void deinit_fonts(void);
74 static int full_screen = 0, extra_width, extra_height;
75 static LONG old_wind_style;
76 static WINDOWPLACEMENT old_wind_placement;
78 static int pending_netevent = 0;
79 static WPARAM pend_netevent_wParam = 0;
80 static LPARAM pend_netevent_lParam = 0;
81 static void enact_pending_netevent(void);
82 static void flash_window(int mode);
83 static void flip_full_screen(void);
85 static time_t last_movement = 0;
89 #define FONT_UNDERLINE 2
90 #define FONT_BOLDUND 3
91 #define FONT_WIDE 0x04
92 #define FONT_HIGH 0x08
93 #define FONT_NARROW 0x10
95 #define FONT_OEMBOLD 0x21
96 #define FONT_OEMUND 0x22
97 #define FONT_OEMBOLDUND 0x23
98 #define FONT_MSGOTHIC 0x40
99 #define FONT_MINGLIU 0x60
100 #define FONT_GULIMCHE 0x80
101 #define FONT_MAXNO 0x8F
103 static HFONT fonts[FONT_MAXNO];
104 static int fontflag[FONT_MAXNO];
106 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
114 static COLORREF colours[NCOLOURS];
116 static LPLOGPALETTE logpal;
117 static RGBTRIPLE defpal[NCOLOURS];
121 static HBITMAP caretbm;
123 static int dbltime, lasttime, lastact;
124 static Mouse_Button lastbtn;
126 /* this allows xterm-style mouse handling. */
127 static int send_raw_mouse = 0;
128 static int wheel_accumulator = 0;
130 static char *window_name, *icon_name;
132 static int compose_state = 0;
134 static OSVERSIONINFO osVersion;
136 /* Dummy routine, only required in plink. */
137 void ldisc_update(int echo, int edit)
141 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
143 static char appname[] = "PuTTY";
148 int guess_width, guess_height;
151 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
153 winsock_ver = MAKEWORD(1, 1);
154 if (WSAStartup(winsock_ver, &wsadata)) {
155 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
156 MB_OK | MB_ICONEXCLAMATION);
159 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
160 MessageBox(NULL, "WinSock version is incompatible with 1.1",
161 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
165 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
168 InitCommonControls();
170 /* Ensure a Maximize setting in Explorer doesn't maximise the
175 ZeroMemory(&osVersion, sizeof(osVersion));
176 osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
177 if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
178 MessageBox(NULL, "Windows refuses to report a version",
179 "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
185 * Process the command line.
190 default_protocol = DEFAULT_PROTOCOL;
191 default_port = DEFAULT_PORT;
192 cfg.logtype = LGTYP_NONE;
194 do_defaults(NULL, &cfg);
197 while (*p && isspace(*p))
201 * Process command line options first. Yes, this can be
202 * done better, and it will be as soon as I have the
206 char *q = p + strcspn(p, " \t");
209 tolower(p[0]) == 's' &&
210 tolower(p[1]) == 's' && tolower(p[2]) == 'h') {
211 default_protocol = cfg.protocol = PROT_SSH;
212 default_port = cfg.port = 22;
213 } else if (q == p + 7 &&
214 tolower(p[0]) == 'c' &&
215 tolower(p[1]) == 'l' &&
216 tolower(p[2]) == 'e' &&
217 tolower(p[3]) == 'a' &&
218 tolower(p[4]) == 'n' &&
219 tolower(p[5]) == 'u' && tolower(p[6]) == 'p') {
221 * `putty -cleanup'. Remove all registry entries
222 * associated with PuTTY, and also find and delete
223 * the random seed file.
226 "This procedure will remove ALL Registry\n"
227 "entries associated with PuTTY, and will\n"
228 "also remove the PuTTY random seed file.\n"
230 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
231 "SESSIONS. Are you really sure you want\n"
234 MB_YESNO | MB_ICONWARNING) == IDYES) {
239 p = q + strspn(q, " \t");
243 * An initial @ means to activate a saved session.
247 while (i > 1 && isspace(p[i - 1]))
250 do_defaults(p + 1, &cfg);
251 if (!*cfg.host && !do_config()) {
255 } else if (*p == '&') {
257 * An initial & means we've been given a command line
258 * containing the hex value of a HANDLE for a file
259 * mapping object, which we must then extract as a
264 if (sscanf(p + 1, "%p", &filemap) == 1 &&
265 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
266 0, 0, sizeof(Config))) != NULL) {
269 CloseHandle(filemap);
270 } else if (!do_config()) {
277 * If the hostname starts with "telnet:", set the
278 * protocol to Telnet and process the string as a
281 if (!strncmp(q, "telnet:", 7)) {
285 if (q[0] == '/' && q[1] == '/')
287 cfg.protocol = PROT_TELNET;
289 while (*p && *p != ':' && *p != '/')
298 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
299 cfg.host[sizeof(cfg.host) - 1] = '\0';
301 while (*p && !isspace(*p))
305 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
306 cfg.host[sizeof(cfg.host) - 1] = '\0';
307 while (*p && isspace(*p))
322 * Trim leading whitespace off the hostname if it's there.
325 int space = strspn(cfg.host, " \t");
326 memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
329 /* See if host is of the form user@host */
330 if (cfg.host[0] != '\0') {
331 char *atsign = strchr(cfg.host, '@');
332 /* Make sure we're not overflowing the user field */
334 if (atsign - cfg.host < sizeof cfg.username) {
335 strncpy(cfg.username, cfg.host, atsign - cfg.host);
336 cfg.username[atsign - cfg.host] = '\0';
338 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
343 * Trim a colon suffix off the hostname if it's there.
345 cfg.host[strcspn(cfg.host, ":")] = '\0';
349 * Select protocol. This is farmed out into a table in a
350 * separate file to enable an ssh-free variant.
355 for (i = 0; backends[i].backend != NULL; i++)
356 if (backends[i].protocol == cfg.protocol) {
357 back = backends[i].backend;
361 MessageBox(NULL, "Unsupported protocol number found",
362 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
368 /* Check for invalid Port number (i.e. zero) */
370 MessageBox(NULL, "Invalid Port Number",
371 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
378 wndclass.lpfnWndProc = WndProc;
379 wndclass.cbClsExtra = 0;
380 wndclass.cbWndExtra = 0;
381 wndclass.hInstance = inst;
382 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
383 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
384 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
385 wndclass.lpszMenuName = NULL;
386 wndclass.lpszClassName = appname;
388 RegisterClass(&wndclass);
393 savelines = cfg.savelines;
399 * Guess some defaults for the window size. This all gets
400 * updated later, so we don't really care too much. However, we
401 * do want the font width/height guesses to correspond to a
402 * large font rather than a small one...
409 term_size(cfg.height, cfg.width, cfg.savelines);
410 guess_width = extra_width + font_width * cols;
411 guess_height = extra_height + font_height * rows;
414 HWND w = GetDesktopWindow();
415 GetWindowRect(w, &r);
416 if (guess_width > r.right - r.left)
417 guess_width = r.right - r.left;
418 if (guess_height > r.bottom - r.top)
419 guess_height = r.bottom - r.top;
423 int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
426 winmode &= ~(WS_VSCROLL);
428 winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
430 exwinmode |= WS_EX_TOPMOST;
432 exwinmode |= WS_EX_CLIENTEDGE;
433 hwnd = CreateWindowEx(exwinmode, appname, appname,
434 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
435 guess_width, guess_height,
436 NULL, NULL, inst, NULL);
440 * Initialise the fonts, simultaneously correcting the guesses
441 * for font_{width,height}.
443 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
448 * Correct the guesses for extra_{width,height}.
452 GetWindowRect(hwnd, &wr);
453 GetClientRect(hwnd, &cr);
454 extra_width = wr.right - wr.left - cr.right + cr.left;
455 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
459 * Resize the window, now we know what size we _really_ want it
462 guess_width = extra_width + font_width * cols;
463 guess_height = extra_height + font_height * rows;
464 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
465 SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
466 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
469 * Set up a caret bitmap, with no content.
473 int size = (font_width + 15) / 16 * 2 * font_height;
474 bits = smalloc(size);
475 memset(bits, 0, size);
476 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
479 CreateCaret(hwnd, caretbm, font_width, font_height);
482 * Initialise the scroll bar.
487 si.cbSize = sizeof(si);
488 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
493 SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
497 * Start up the telnet connection.
501 char msg[1024], *title;
504 error = back->init(cfg.host, cfg.port, &realhost);
506 sprintf(msg, "Unable to open connection to\n"
507 "%.800s\n" "%s", cfg.host, error);
508 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
511 window_name = icon_name = NULL;
513 title = cfg.wintitle;
515 sprintf(msg, "%s - PuTTY", realhost);
523 session_closed = FALSE;
526 * Set up the input and output buffers.
529 outbuf_reap = outbuf_head = 0;
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, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
589 * Finally show the window!
591 ShowWindow(hwnd, show);
592 SetForegroundWindow(hwnd);
595 * Open the initial log file if there is one.
600 * Set the palette up.
606 has_focus = (GetForegroundWindow() == hwnd);
609 if (GetMessage(&msg, NULL, 0, 0) == 1) {
610 int timer_id = 0, long_timer = 0;
612 while (msg.message != WM_QUIT) {
613 /* Sometimes DispatchMessage calls routines that use their own
614 * GetMessage loop, setup this timer so we get some control back.
616 * Also call term_update() from the timer so that if the host
617 * is sending data flat out we still do redraws.
619 if (timer_id && long_timer) {
620 KillTimer(hwnd, timer_id);
621 long_timer = timer_id = 0;
624 timer_id = SetTimer(hwnd, 1, 20, NULL);
625 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
626 DispatchMessage(&msg);
628 /* Make sure we blink everything that needs it. */
631 /* Send the paste buffer if there's anything to send */
634 /* If there's nothing new in the queue then we can do everything
635 * we've delayed, reading the socket, writing, and repainting
638 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
641 if (pending_netevent) {
642 enact_pending_netevent();
644 /* Force the cursor blink on */
647 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
651 /* Okay there is now nothing to do so we make sure the screen is
652 * completely up to date then tell windows to call us in a little
656 KillTimer(hwnd, timer_id);
665 flash_window(1); /* maintain */
668 /* Hmm, term_update didn't want to do an update too soon ... */
669 timer_id = SetTimer(hwnd, 1, 50, NULL);
671 timer_id = SetTimer(hwnd, 1, 500, NULL);
673 timer_id = SetTimer(hwnd, 1, 100, NULL);
676 /* There's no point rescanning everything in the message queue
677 * so we do an apparently unnecessary wait here
680 if (GetMessage(&msg, NULL, 0, 0) != 1)
694 if (cfg.protocol == PROT_SSH) {
705 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
707 char *do_select(SOCKET skt, int startup)
712 events = (FD_CONNECT | FD_READ | FD_WRITE |
713 FD_OOB | FD_CLOSE | FD_ACCEPT);
718 return "do_select(): internal error (hwnd==NULL)";
719 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
720 switch (WSAGetLastError()) {
722 return "Network is down";
724 return "WSAAsyncSelect(): unknown error";
731 * set or clear the "raw mouse message" mode
733 void set_raw_mouse_mode(int activate)
735 send_raw_mouse = activate;
736 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
740 * Print a message box and close the connection.
742 void connection_fatal(char *fmt, ...)
748 vsprintf(stuff, fmt, ap);
750 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
751 if (cfg.close_on_exit == COE_ALWAYS)
754 session_closed = TRUE;
755 SetWindowText(hwnd, "PuTTY (inactive)");
760 * Actually do the job requested by a WM_NETEVENT
762 static void enact_pending_netevent(void)
764 static int reentering = 0;
765 extern int select_result(WPARAM, LPARAM);
769 return; /* don't unpend the pending */
771 pending_netevent = FALSE;
774 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
777 if (ret == 0 && !session_closed) {
778 /* Abnormal exits will already have set session_closed and taken
779 * appropriate action. */
780 if (cfg.close_on_exit == COE_ALWAYS ||
781 cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
783 session_closed = TRUE;
784 SetWindowText(hwnd, "PuTTY (inactive)");
785 MessageBox(hwnd, "Connection closed by remote host",
786 "PuTTY", MB_OK | MB_ICONINFORMATION);
792 * Copy the colour palette from the configuration data into defpal.
793 * This is non-trivial because the colour indices are different.
795 static void cfgtopalette(void)
798 static const int ww[] = {
799 6, 7, 8, 9, 10, 11, 12, 13,
800 14, 15, 16, 17, 18, 19, 20, 21,
801 0, 1, 2, 3, 4, 4, 5, 5
804 for (i = 0; i < 24; i++) {
806 defpal[i].rgbtRed = cfg.colours[w][0];
807 defpal[i].rgbtGreen = cfg.colours[w][1];
808 defpal[i].rgbtBlue = cfg.colours[w][2];
813 * Set up the colour palette.
815 static void init_palette(void)
818 HDC hdc = GetDC(hwnd);
820 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
821 logpal = smalloc(sizeof(*logpal)
822 - sizeof(logpal->palPalEntry)
823 + NCOLOURS * sizeof(PALETTEENTRY));
824 logpal->palVersion = 0x300;
825 logpal->palNumEntries = NCOLOURS;
826 for (i = 0; i < NCOLOURS; i++) {
827 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
828 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
829 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
830 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
832 pal = CreatePalette(logpal);
834 SelectPalette(hdc, pal, FALSE);
836 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
839 ReleaseDC(hwnd, hdc);
842 for (i = 0; i < NCOLOURS; i++)
843 colours[i] = PALETTERGB(defpal[i].rgbtRed,
847 for (i = 0; i < NCOLOURS; i++)
848 colours[i] = RGB(defpal[i].rgbtRed,
849 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
853 * Initialise all the fonts we will need initially. There may be as many as
854 * three or as few as one. The other (poentially) twentyone fonts are done
855 * if/when they are needed.
859 * - check the font width and height, correcting our guesses if
862 * - verify that the bold font is the same width as the ordinary
863 * one, and engage shadow bolding if not.
865 * - verify that the underlined font is the same width as the
866 * ordinary one (manual underlining by means of line drawing can
867 * be done in a pinch).
869 static void init_fonts(int pick_width)
876 int fw_dontcare, fw_bold;
878 for (i = 0; i < FONT_MAXNO; i++)
881 if (cfg.fontisbold) {
882 fw_dontcare = FW_BOLD;
885 fw_dontcare = FW_DONTCARE;
891 font_height = cfg.fontheight;
892 if (font_height > 0) {
894 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
896 font_width = pick_width;
899 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
900 c, OUT_DEFAULT_PRECIS, \
901 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
902 FIXED_PITCH | FF_DONTCARE, cfg.font)
904 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
906 SelectObject(hdc, fonts[FONT_NORMAL]);
907 GetTextMetrics(hdc, &tm);
908 font_height = tm.tmHeight;
909 font_width = tm.tmAveCharWidth;
913 DWORD cset = tm.tmCharSet;
914 memset(&info, 0xFF, sizeof(info));
916 /* !!! Yes the next line is right */
917 if (cset == OEM_CHARSET)
918 font_codepage = GetOEMCP();
920 if (TranslateCharsetInfo
921 ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
926 GetCPInfo(font_codepage, &cpinfo);
927 dbcs_screenfont = (cpinfo.MaxCharSize > 1);
930 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
933 * Some fonts, e.g. 9-pt Courier, draw their underlines
934 * outside their character cell. We successfully prevent
935 * screen corruption by clipping the text output, but then
936 * we lose the underline completely. Here we try to work
937 * out whether this is such a font, and if it is, we set a
938 * flag that causes underlines to be drawn by hand.
940 * Having tried other more sophisticated approaches (such
941 * as examining the TEXTMETRIC structure or requesting the
942 * height of a string), I think we'll do this the brute
943 * force way: we create a small bitmap, draw an underlined
944 * space on it, and test to see whether any pixels are
945 * foreground-coloured. (Since we expect the underline to
946 * go all the way across the character cell, we only search
947 * down a single column of the bitmap, half way across.)
951 HBITMAP und_bm, und_oldbm;
955 und_dc = CreateCompatibleDC(hdc);
956 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
957 und_oldbm = SelectObject(und_dc, und_bm);
958 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
959 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
960 SetTextColor(und_dc, RGB(255, 255, 255));
961 SetBkColor(und_dc, RGB(0, 0, 0));
962 SetBkMode(und_dc, OPAQUE);
963 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
965 for (i = 0; i < font_height; i++) {
966 c = GetPixel(und_dc, font_width / 2, i);
967 if (c != RGB(0, 0, 0))
970 SelectObject(und_dc, und_oldbm);
971 DeleteObject(und_bm);
975 DeleteObject(fonts[FONT_UNDERLINE]);
976 fonts[FONT_UNDERLINE] = 0;
980 if (bold_mode == BOLD_FONT) {
981 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
985 descent = tm.tmAscent + 1;
986 if (descent >= font_height)
987 descent = font_height - 1;
989 for (i = 0; i < 3; i++) {
991 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
992 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
999 ReleaseDC(hwnd, hdc);
1001 if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
1002 und_mode = UND_LINE;
1003 DeleteObject(fonts[FONT_UNDERLINE]);
1004 fonts[FONT_UNDERLINE] = 0;
1007 if (bold_mode == BOLD_FONT &&
1008 fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
1009 bold_mode = BOLD_SHADOW;
1010 DeleteObject(fonts[FONT_BOLD]);
1011 fonts[FONT_BOLD] = 0;
1013 fontflag[0] = fontflag[1] = fontflag[2] = 1;
1018 static void another_font(int fontno)
1021 int fw_dontcare, fw_bold;
1025 if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1028 basefont = (fontno & ~(FONT_BOLDUND));
1029 if (basefont != fontno && !fontflag[basefont])
1030 another_font(basefont);
1032 if (cfg.fontisbold) {
1033 fw_dontcare = FW_BOLD;
1036 fw_dontcare = FW_DONTCARE;
1040 c = cfg.fontcharset;
1046 if (fontno & FONT_WIDE)
1048 if (fontno & FONT_NARROW)
1050 if (fontno & FONT_OEM)
1052 if (fontno & FONT_BOLD)
1054 if (fontno & FONT_UNDERLINE)
1058 CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1059 FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1060 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1061 FIXED_PITCH | FF_DONTCARE, s);
1063 fontflag[fontno] = 1;
1066 static void deinit_fonts(void)
1069 for (i = 0; i < FONT_MAXNO; i++) {
1071 DeleteObject(fonts[i]);
1077 void request_resize(int w, int h, int refont)
1081 /* If the window is maximized supress resizing attempts */
1085 if (refont && w != cols && (cols == 80 || cols == 132)) {
1086 /* If font width too big for screen should we shrink the font more ? */
1088 font_width = ((font_width * cols + w / 2) / w);
1092 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1093 und_mode = UND_FONT;
1094 init_fonts(font_width);
1096 static int first_time = 1;
1099 switch (first_time) {
1101 /* Get the size of the screen */
1102 if (GetClientRect(GetDesktopWindow(), &ss))
1103 /* first_time = 0 */ ;
1109 /* Make sure the values are sane */
1110 width = (ss.right - ss.left - extra_width) / font_width;
1111 height = (ss.bottom - ss.top - extra_height) / font_height;
1124 width = extra_width + font_width * w;
1125 height = extra_height + font_height * h;
1127 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1128 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1129 SWP_NOMOVE | SWP_NOZORDER);
1132 static void click(Mouse_Button b, int x, int y, int shift, int ctrl)
1134 int thistime = GetMessageTime();
1136 if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1137 lastbtn = MBT_NOTHING;
1138 term_mouse(b, MA_CLICK, x, y, shift, ctrl);
1142 if (lastbtn == b && thistime - lasttime < dbltime) {
1143 lastact = (lastact == MA_CLICK ? MA_2CLK :
1144 lastact == MA_2CLK ? MA_3CLK :
1145 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1150 if (lastact != MA_NOTHING)
1151 term_mouse(b, lastact, x, y, shift, ctrl);
1152 lasttime = thistime;
1156 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1157 * into a cooked one (SELECT, EXTEND, PASTE).
1159 Mouse_Button translate_button(Mouse_Button button)
1161 if (button == MBT_LEFT)
1163 if (button == MBT_MIDDLE)
1164 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1165 if (button == MBT_RIGHT)
1166 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1167 return 0; /* shouldn't happen */
1170 static void show_mouseptr(int show)
1172 static int cursor_visible = 1;
1173 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1175 if (cursor_visible && !show)
1177 else if (!cursor_visible && show)
1179 cursor_visible = show;
1182 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1183 WPARAM wParam, LPARAM lParam)
1186 static int ignore_size = FALSE;
1187 static int ignore_clip = FALSE;
1188 static int just_reconfigged = FALSE;
1189 static int resizing = FALSE;
1190 static int need_backend_resize = FALSE;
1191 static int defered_resize = FALSE;
1195 if (pending_netevent)
1196 enact_pending_netevent();
1203 if (cfg.ping_interval > 0) {
1206 if (now - last_movement > cfg.ping_interval) {
1207 back->special(TS_PING);
1208 last_movement = now;
1216 if (!cfg.warn_on_close || session_closed ||
1218 "Are you sure you want to close this session?",
1219 "PuTTY Exit Confirmation",
1220 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1221 DestroyWindow(hwnd);
1228 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1240 PROCESS_INFORMATION pi;
1241 HANDLE filemap = NULL;
1243 if (wParam == IDM_DUPSESS) {
1245 * Allocate a file-mapping memory chunk for the
1248 SECURITY_ATTRIBUTES sa;
1251 sa.nLength = sizeof(sa);
1252 sa.lpSecurityDescriptor = NULL;
1253 sa.bInheritHandle = TRUE;
1254 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1257 0, sizeof(Config), NULL);
1259 p = (Config *) MapViewOfFile(filemap,
1261 0, 0, sizeof(Config));
1263 *p = cfg; /* structure copy */
1267 sprintf(c, "putty &%p", filemap);
1269 } else if (wParam == IDM_SAVEDSESS) {
1271 sessions[(lParam - IDM_SAVED_MIN) / 16];
1272 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1274 cl = NULL; /* not a very important failure mode */
1276 sprintf(cl, "putty @%s", session);
1282 GetModuleFileName(NULL, b, sizeof(b) - 1);
1284 si.lpReserved = NULL;
1285 si.lpDesktop = NULL;
1289 si.lpReserved2 = NULL;
1290 CreateProcess(b, cl, NULL, NULL, TRUE,
1291 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1294 CloseHandle(filemap);
1301 int prev_alwaysontop = cfg.alwaysontop;
1302 int prev_sunken_edge = cfg.sunken_edge;
1303 char oldlogfile[FILENAME_MAX];
1305 int need_setwpos = FALSE;
1306 int old_fwidth, old_fheight;
1308 strcpy(oldlogfile, cfg.logfilename);
1309 oldlogtype = cfg.logtype;
1310 old_fwidth = font_width;
1311 old_fheight = font_height;
1312 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1314 if (!do_reconfig(hwnd))
1317 if (strcmp(oldlogfile, cfg.logfilename) ||
1318 oldlogtype != cfg.logtype) {
1319 logfclose(); /* reset logging */
1323 just_reconfigged = TRUE;
1325 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1326 und_mode = UND_FONT;
1330 * Flush the line discipline's edit buffer in the
1331 * case where local editing has just been disabled.
1333 ldisc_send(NULL, 0);
1341 /* Enable or disable the scroll bar, etc */
1343 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1344 LONG nexflag, exflag =
1345 GetWindowLong(hwnd, GWL_EXSTYLE);
1348 if (cfg.alwaysontop != prev_alwaysontop) {
1349 if (cfg.alwaysontop) {
1350 nexflag |= WS_EX_TOPMOST;
1351 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1352 SWP_NOMOVE | SWP_NOSIZE);
1354 nexflag &= ~(WS_EX_TOPMOST);
1355 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1356 SWP_NOMOVE | SWP_NOSIZE);
1359 if (cfg.sunken_edge)
1360 nexflag |= WS_EX_CLIENTEDGE;
1362 nexflag &= ~(WS_EX_CLIENTEDGE);
1368 nflg &= ~WS_VSCROLL;
1370 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1372 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1374 if (nflg != flag || nexflag != exflag) {
1378 SetWindowLong(hwnd, GWL_STYLE, nflg);
1379 if (nexflag != exflag)
1380 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1382 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1384 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1385 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1386 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER
1387 | SWP_FRAMECHANGED);
1389 GetWindowRect(hwnd, &wr);
1390 GetClientRect(hwnd, &cr);
1392 wr.right - wr.left - cr.right + cr.left;
1394 wr.bottom - wr.top - cr.bottom + cr.top;
1395 need_setwpos = TRUE;
1399 if (cfg.height != rows ||
1400 cfg.width != cols ||
1401 old_fwidth != font_width ||
1402 old_fheight != font_height ||
1403 cfg.savelines != savelines ||
1404 cfg.sunken_edge != prev_sunken_edge)
1405 need_setwpos = TRUE;
1407 if (IsZoomed(hwnd)) {
1411 defered_resize = TRUE;
1413 GetClientRect(hwnd, &cr);
1414 w = cr.right - cr.left;
1415 h = cr.bottom - cr.top;
1419 h = h / font_height;
1423 term_size(h, w, cfg.savelines);
1424 InvalidateRect(hwnd, NULL, TRUE);
1427 term_size(cfg.height, cfg.width, cfg.savelines);
1428 InvalidateRect(hwnd, NULL, TRUE);
1430 SetWindowPos(hwnd, NULL, 0, 0,
1431 extra_width + font_width * cfg.width,
1433 font_height * cfg.height,
1434 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1435 SWP_NOMOVE | SWP_NOZORDER);
1439 if (cfg.locksize && IsZoomed(hwnd))
1441 set_title(cfg.wintitle);
1442 if (IsIconic(hwnd)) {
1444 cfg.win_name_always ? window_name :
1459 back->special(TS_AYT);
1462 back->special(TS_BRK);
1465 back->special(TS_SYNCH);
1468 back->special(TS_EC);
1471 back->special(TS_EL);
1474 back->special(TS_GA);
1477 back->special(TS_NOP);
1480 back->special(TS_ABORT);
1483 back->special(TS_AO);
1486 back->special(TS_IP);
1489 back->special(TS_SUSP);
1492 back->special(TS_EOR);
1495 back->special(TS_EOF);
1502 * We get this if the System menu has been activated.
1503 * This might happen from within TranslateKey, in which
1504 * case it really wants to be followed by a `space'
1505 * character to actually _bring the menu up_ rather
1506 * than just sitting there in `ready to appear' state.
1509 PostMessage(hwnd, WM_CHAR, ' ', 0);
1512 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1513 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1518 #define X_POS(l) ((int)(short)LOWORD(l))
1519 #define Y_POS(l) ((int)(short)HIWORD(l))
1521 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1522 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1523 #define WHEEL_DELTA 120
1526 wheel_accumulator += (short) HIWORD(wParam);
1527 wParam = LOWORD(wParam);
1529 /* process events when the threshold is reached */
1530 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1533 /* reduce amount for next time */
1534 if (wheel_accumulator > 0) {
1536 wheel_accumulator -= WHEEL_DELTA;
1537 } else if (wheel_accumulator < 0) {
1539 wheel_accumulator += WHEEL_DELTA;
1543 if (send_raw_mouse) {
1544 /* send a mouse-down followed by a mouse up */
1547 TO_CHR_X(X_POS(lParam)),
1548 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1549 wParam & MK_CONTROL);
1550 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1551 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1552 wParam & MK_CONTROL);
1554 /* trigger a scroll */
1556 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1561 case WM_LBUTTONDOWN:
1562 case WM_MBUTTONDOWN:
1563 case WM_RBUTTONDOWN:
1570 case WM_LBUTTONDOWN:
1574 case WM_MBUTTONDOWN:
1575 button = MBT_MIDDLE;
1578 case WM_RBUTTONDOWN:
1587 button = MBT_MIDDLE;
1595 button = press = 0; /* shouldn't happen */
1600 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1601 wParam & MK_SHIFT, wParam & MK_CONTROL);
1604 term_mouse(button, MA_RELEASE,
1605 TO_CHR_X(X_POS(lParam)),
1606 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1607 wParam & MK_CONTROL);
1615 * Add the mouse position and message time to the random
1618 noise_ultralight(lParam);
1620 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1622 if (wParam & MK_LBUTTON)
1624 else if (wParam & MK_MBUTTON)
1628 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1629 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1630 wParam & MK_CONTROL);
1633 case WM_NCMOUSEMOVE:
1635 noise_ultralight(lParam);
1637 case WM_IGNORE_CLIP:
1638 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1640 case WM_DESTROYCLIPBOARD:
1643 ignore_clip = FALSE;
1649 hdc = BeginPaint(hwnd, &p);
1651 SelectPalette(hdc, pal, TRUE);
1652 RealizePalette(hdc);
1654 term_paint(hdc, p.rcPaint.left, p.rcPaint.top,
1655 p.rcPaint.right, p.rcPaint.bottom);
1656 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1657 SelectObject(hdc, GetStockObject(WHITE_PEN));
1663 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1664 * but the only one that's likely to try to overload us is FD_READ.
1665 * This means buffering just one is fine.
1667 if (pending_netevent)
1668 enact_pending_netevent();
1670 pending_netevent = TRUE;
1671 pend_netevent_wParam = wParam;
1672 pend_netevent_lParam = lParam;
1673 if (WSAGETSELECTEVENT(lParam) != FD_READ)
1674 enact_pending_netevent();
1676 time(&last_movement);
1680 CreateCaret(hwnd, caretbm, font_width, font_height);
1682 flash_window(0); /* stop */
1694 case WM_IGNORE_SIZE:
1695 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1697 case WM_ENTERSIZEMOVE:
1700 need_backend_resize = FALSE;
1702 case WM_EXITSIZEMOVE:
1705 if (need_backend_resize)
1710 int width, height, w, h, ew, eh;
1711 LPRECT r = (LPRECT) lParam;
1713 width = r->right - r->left - extra_width;
1714 height = r->bottom - r->top - extra_height;
1715 w = (width + font_width / 2) / font_width;
1718 h = (height + font_height / 2) / font_height;
1721 UpdateSizeTip(hwnd, w, h);
1722 ew = width - w * font_width;
1723 eh = height - h * font_height;
1725 if (wParam == WMSZ_LEFT ||
1726 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1732 if (wParam == WMSZ_TOP ||
1733 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1743 /* break; (never reached) */
1745 if (wParam == SIZE_MINIMIZED) {
1747 cfg.win_name_always ? window_name : icon_name);
1750 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1751 SetWindowText(hwnd, window_name);
1753 int width, height, w, h;
1754 #if 0 /* we have fixed this using WM_SIZING now */
1758 width = LOWORD(lParam);
1759 height = HIWORD(lParam);
1760 w = width / font_width;
1763 h = height / font_height;
1766 #if 0 /* we have fixed this using WM_SIZING now */
1767 ew = width - w * font_width;
1768 eh = height - h * font_height;
1769 if (ew != 0 || eh != 0) {
1771 GetWindowRect(hwnd, &r);
1772 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1773 SetWindowPos(hwnd, NULL, 0, 0,
1774 r.right - r.left - ew, r.bottom - r.top - eh,
1775 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1778 if (w != cols || h != rows || just_reconfigged) {
1780 term_size(h, w, cfg.savelines);
1782 * Don't call back->size in mid-resize. (To prevent
1783 * massive numbers of resize events getting sent
1784 * down the connection during an NT opaque drag.)
1789 need_backend_resize = TRUE;
1793 just_reconfigged = FALSE;
1796 if (wParam == SIZE_RESTORED && defered_resize) {
1797 defered_resize = FALSE;
1798 SetWindowPos(hwnd, NULL, 0, 0,
1799 extra_width + font_width * cfg.width,
1800 extra_height + font_height * cfg.height,
1801 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1802 SWP_NOMOVE | SWP_NOZORDER);
1804 ignore_size = FALSE;
1807 switch (LOWORD(wParam)) {
1821 term_scroll(0, +rows / 2);
1824 term_scroll(0, -rows / 2);
1826 case SB_THUMBPOSITION:
1828 term_scroll(1, HIWORD(wParam));
1832 case WM_PALETTECHANGED:
1833 if ((HWND) wParam != hwnd && pal != NULL) {
1834 HDC hdc = get_ctx();
1836 if (RealizePalette(hdc) > 0)
1842 case WM_QUERYNEWPALETTE:
1844 HDC hdc = get_ctx();
1846 if (RealizePalette(hdc) > 0)
1858 * Add the scan code and keypress timing to the random
1861 noise_ultralight(lParam);
1864 * We don't do TranslateMessage since it disassociates the
1865 * resulting CHAR message from the KEYDOWN that sparked it,
1866 * which we occasionally don't want. Instead, we process
1867 * KEYDOWN, and call the Win32 translator functions so that
1868 * we get the translations under _our_ control.
1871 unsigned char buf[20];
1874 if (wParam == VK_PROCESSKEY) {
1877 m.message = WM_KEYDOWN;
1879 m.lParam = lParam & 0xdfff;
1880 TranslateMessage(&m);
1882 len = TranslateKey(message, wParam, lParam, buf);
1884 return DefWindowProc(hwnd, message, wParam, lParam);
1888 * We need not bother about stdin backlogs
1889 * here, because in GUI PuTTY we can't do
1890 * anything about it anyway; there's no means
1891 * of asking Windows to hold off on KEYDOWN
1892 * messages. We _have_ to buffer everything
1895 ldisc_send(buf, len);
1901 case WM_INPUTLANGCHANGE:
1903 /* wParam == Font number */
1904 /* lParam == Locale */
1906 HKL NewInputLocale = (HKL) lParam;
1908 // lParam == GetKeyboardLayout(0);
1910 GetLocaleInfo(LOWORD(NewInputLocale),
1911 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
1913 kbd_codepage = atoi(lbuf);
1916 case WM_IME_COMPOSITION:
1922 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
1923 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
1925 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
1926 break; /* fall back to DefWindowProc */
1928 hIMC = ImmGetContext(hwnd);
1929 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
1932 buff = (char*) smalloc(n);
1933 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
1934 luni_send((unsigned short *)buff, n / 2);
1937 ImmReleaseContext(hwnd, hIMC);
1942 if (wParam & 0xFF00) {
1943 unsigned char buf[2];
1946 buf[0] = wParam >> 8;
1947 lpage_send(kbd_codepage, buf, 2);
1949 char c = (unsigned char) wParam;
1950 lpage_send(kbd_codepage, &c, 1);
1956 * Nevertheless, we are prepared to deal with WM_CHAR
1957 * messages, should they crop up. So if someone wants to
1958 * post the things to us as part of a macro manoeuvre,
1959 * we're ready to cope.
1962 char c = (unsigned char)wParam;
1963 lpage_send(CP_ACP, &c, 1);
1967 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
1968 SetCursor(LoadCursor(NULL, IDC_ARROW));
1973 return DefWindowProc(hwnd, message, wParam, lParam);
1977 * Move the system caret. (We maintain one, even though it's
1978 * invisible, for the benefit of blind people: apparently some
1979 * helper software tracks the system caret, so we should arrange to
1982 void sys_cursor(int x, int y)
1987 if (!has_focus) return;
1989 SetCaretPos(x * font_width, y * font_height);
1991 /* IMM calls on Win98 and beyond only */
1992 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
1994 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
1995 osVersion.dwMinorVersion == 0) return; /* 95 */
1997 /* we should have the IMM functions */
1998 hIMC = ImmGetContext(hwnd);
1999 cf.dwStyle = CFS_POINT;
2000 cf.ptCurrentPos.x = x * font_width;
2001 cf.ptCurrentPos.y = y * font_height;
2002 ImmSetCompositionWindow(hIMC, &cf);
2004 ImmReleaseContext(hwnd, hIMC);
2008 * Draw a line of text in the window, at given character
2009 * coordinates, in given attributes.
2011 * We are allowed to fiddle with the contents of `text'.
2013 void do_text(Context ctx, int x, int y, char *text, int len,
2014 unsigned long attr, int lattr)
2017 int nfg, nbg, nfont;
2020 int force_manual_underline = 0;
2021 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2022 int char_width = fnt_width;
2023 int text_adjust = 0;
2024 static int *IpDx = 0, IpDxLEN = 0;
2026 if (attr & ATTR_WIDE)
2029 if (len > IpDxLEN || IpDx[0] != char_width) {
2031 if (len > IpDxLEN) {
2033 IpDx = smalloc((len + 16) * sizeof(int));
2034 IpDxLEN = (len + 16);
2036 for (i = 0; i < IpDxLEN; i++)
2037 IpDx[i] = char_width;
2043 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2044 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2045 attr ^= ATTR_CUR_XOR;
2049 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2050 /* Assume a poorman font is borken in other ways too. */
2060 nfont |= FONT_WIDE + FONT_HIGH;
2064 /* Special hack for the VT100 linedraw glyphs. */
2065 if ((attr & CSET_MASK) == 0x2300) {
2066 if (!dbcs_screenfont &&
2067 text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2068 switch ((unsigned char) (text[0])) {
2070 text_adjust = -2 * font_height / 5;
2073 text_adjust = -1 * font_height / 5;
2076 text_adjust = font_height / 5;
2079 text_adjust = 2 * font_height / 5;
2082 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2085 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2086 attr |= (unitab_xterm['q'] & CSET_MASK);
2087 if (attr & ATTR_UNDER) {
2088 attr &= ~ATTR_UNDER;
2089 force_manual_underline = 1;
2094 /* Anything left as an original character set is unprintable. */
2095 if (DIRECT_CHAR(attr)) {
2098 memset(text, 0xFF, len);
2102 if ((attr & CSET_MASK) == ATTR_OEMCP)
2105 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2106 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2107 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2109 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2110 nfont |= FONT_UNDERLINE;
2111 another_font(nfont);
2112 if (!fonts[nfont]) {
2113 if (nfont & FONT_UNDERLINE)
2114 force_manual_underline = 1;
2115 /* Don't do the same for manual bold, it could be bad news. */
2117 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2119 another_font(nfont);
2121 nfont = FONT_NORMAL;
2122 if (attr & ATTR_REVERSE) {
2127 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2129 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2133 SelectObject(hdc, fonts[nfont]);
2134 SetTextColor(hdc, fg);
2135 SetBkColor(hdc, bg);
2136 SetBkMode(hdc, OPAQUE);
2139 line_box.right = x + char_width * len;
2140 line_box.bottom = y + font_height;
2142 /* We're using a private area for direct to font. (512 chars.) */
2143 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2144 /* Ho Hum, dbcs fonts are a PITA! */
2145 /* To display on W9x I have to convert to UCS */
2146 static wchar_t *uni_buf = 0;
2147 static int uni_len = 0;
2149 if (len > uni_len) {
2151 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2153 nlen = MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2154 text, len, uni_buf, uni_len);
2160 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2161 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, 0);
2162 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2163 SetBkMode(hdc, TRANSPARENT);
2164 ExtTextOutW(hdc, x - 1,
2165 y - font_height * (lattr ==
2166 LATTR_BOT) + text_adjust,
2167 ETO_CLIPPED, &line_box, uni_buf, nlen, 0);
2169 } else if (DIRECT_FONT(attr)) {
2171 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2172 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2173 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2174 SetBkMode(hdc, TRANSPARENT);
2176 /* GRR: This draws the character outside it's box and can leave
2177 * 'droppings' even with the clip box! I suppose I could loop it
2178 * one character at a time ... yuk.
2180 * Or ... I could do a test print with "W", and use +1 or -1 for this
2181 * shift depending on if the leftmost column is blank...
2183 ExtTextOut(hdc, x - 1,
2184 y - font_height * (lattr ==
2185 LATTR_BOT) + text_adjust,
2186 ETO_CLIPPED, &line_box, text, len, IpDx);
2189 /* And 'normal' unicode characters */
2190 static WCHAR *wbuf = NULL;
2191 static int wlen = 0;
2196 wbuf = smalloc(wlen * sizeof(WCHAR));
2198 for (i = 0; i < len; i++)
2199 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2202 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2203 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2205 /* And the shadow bold hack. */
2206 if (bold_mode == BOLD_SHADOW) {
2207 SetBkMode(hdc, TRANSPARENT);
2208 ExtTextOutW(hdc, x - 1,
2209 y - font_height * (lattr ==
2210 LATTR_BOT) + text_adjust,
2211 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2214 if (lattr != LATTR_TOP && (force_manual_underline ||
2215 (und_mode == UND_LINE
2216 && (attr & ATTR_UNDER)))) {
2219 if (lattr == LATTR_BOT)
2220 dec = dec * 2 - font_height;
2222 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2223 MoveToEx(hdc, x, y + dec, NULL);
2224 LineTo(hdc, x + len * char_width, y + dec);
2225 oldpen = SelectObject(hdc, oldpen);
2226 DeleteObject(oldpen);
2230 void do_cursor(Context ctx, int x, int y, char *text, int len,
2231 unsigned long attr, int lattr)
2237 int ctype = cfg.cursor_type;
2239 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2240 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2241 do_text(ctx, x, y, text, len, attr, lattr);
2245 attr |= TATTR_RIGHTCURS;
2248 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2249 if (attr & ATTR_WIDE)
2254 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2257 pts[0].x = pts[1].x = pts[4].x = x;
2258 pts[2].x = pts[3].x = x + char_width - 1;
2259 pts[0].y = pts[3].y = pts[4].y = y;
2260 pts[1].y = pts[2].y = y + font_height - 1;
2261 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2262 Polyline(hdc, pts, 5);
2263 oldpen = SelectObject(hdc, oldpen);
2264 DeleteObject(oldpen);
2265 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2266 int startx, starty, dx, dy, length, i;
2269 starty = y + descent;
2272 length = char_width;
2275 if (attr & TATTR_RIGHTCURS)
2276 xadjust = char_width - 1;
2277 startx = x + xadjust;
2281 length = font_height;
2283 if (attr & TATTR_ACTCURS) {
2286 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2287 MoveToEx(hdc, startx, starty, NULL);
2288 LineTo(hdc, startx + dx * length, starty + dy * length);
2289 oldpen = SelectObject(hdc, oldpen);
2290 DeleteObject(oldpen);
2292 for (i = 0; i < length; i++) {
2294 SetPixel(hdc, startx, starty, colours[23]);
2304 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2305 * codes. Returns number of bytes used or zero to drop the message
2306 * or -1 to forward the message to windows.
2308 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2309 unsigned char *output)
2312 int scan, left_alt = 0, key_down, shift_state;
2314 unsigned char *p = output;
2315 static int alt_sum = 0;
2317 HKL kbd_layout = GetKeyboardLayout(0);
2319 static WORD keys[3];
2320 static int compose_char = 0;
2321 static WPARAM compose_key = 0;
2323 r = GetKeyboardState(keystate);
2325 memset(keystate, 0, sizeof(keystate));
2328 #define SHOW_TOASCII_RESULT
2329 { /* Tell us all about key events */
2330 static BYTE oldstate[256];
2331 static int first = 1;
2335 memcpy(oldstate, keystate, sizeof(oldstate));
2338 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2340 } else if ((HIWORD(lParam) & KF_UP)
2341 && scan == (HIWORD(lParam) & 0xFF)) {
2345 if (wParam >= VK_F1 && wParam <= VK_F20)
2346 debug(("K_F%d", wParam + 1 - VK_F1));
2359 debug(("VK_%02x", wParam));
2361 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2363 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2365 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2366 if (ch >= ' ' && ch <= '~')
2367 debug((", '%c'", ch));
2369 debug((", $%02x", ch));
2372 debug((", KB0=%02x", keys[0]));
2374 debug((", KB1=%02x", keys[1]));
2376 debug((", KB2=%02x", keys[2]));
2378 if ((keystate[VK_SHIFT] & 0x80) != 0)
2380 if ((keystate[VK_CONTROL] & 0x80) != 0)
2382 if ((HIWORD(lParam) & KF_EXTENDED))
2384 if ((HIWORD(lParam) & KF_UP))
2388 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2389 else if ((HIWORD(lParam) & KF_UP))
2390 oldstate[wParam & 0xFF] ^= 0x80;
2392 oldstate[wParam & 0xFF] ^= 0x81;
2394 for (ch = 0; ch < 256; ch++)
2395 if (oldstate[ch] != keystate[ch])
2396 debug((", M%02x=%02x", ch, keystate[ch]));
2398 memcpy(oldstate, keystate, sizeof(oldstate));
2402 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2403 keystate[VK_RMENU] = keystate[VK_MENU];
2407 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2408 if ((cfg.funky_type == 3 ||
2409 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2410 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2412 wParam = VK_EXECUTE;
2414 /* UnToggle NUMLock */
2415 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2416 keystate[VK_NUMLOCK] ^= 1;
2419 /* And write back the 'adjusted' state */
2420 SetKeyboardState(keystate);
2423 /* Disable Auto repeat if required */
2424 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2427 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2430 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2432 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2433 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2434 if (cfg.ctrlaltkeys)
2435 keystate[VK_MENU] = 0;
2437 keystate[VK_RMENU] = 0x80;
2442 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2443 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2444 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2446 /* Note if AltGr was pressed and if it was used as a compose key */
2447 if (!compose_state) {
2448 compose_key = 0x100;
2449 if (cfg.compose_key) {
2450 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2451 compose_key = wParam;
2453 if (wParam == VK_APPS)
2454 compose_key = wParam;
2457 if (wParam == compose_key) {
2458 if (compose_state == 0
2459 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2461 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2465 } else if (compose_state == 1 && wParam != VK_CONTROL)
2469 * Record that we pressed key so the scroll window can be reset, but
2470 * be careful to avoid Shift-UP/Down
2472 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2476 /* Make sure we're not pasting */
2480 if (compose_state > 1 && left_alt)
2483 /* Sanitize the number pad if not using a PC NumPad */
2484 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2485 && cfg.funky_type != 2)
2486 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2487 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2491 nParam = VK_NUMPAD0;
2494 nParam = VK_NUMPAD1;
2497 nParam = VK_NUMPAD2;
2500 nParam = VK_NUMPAD3;
2503 nParam = VK_NUMPAD4;
2506 nParam = VK_NUMPAD5;
2509 nParam = VK_NUMPAD6;
2512 nParam = VK_NUMPAD7;
2515 nParam = VK_NUMPAD8;
2518 nParam = VK_NUMPAD9;
2521 nParam = VK_DECIMAL;
2525 if (keystate[VK_NUMLOCK] & 1)
2532 /* If a key is pressed and AltGr is not active */
2533 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2534 /* Okay, prepare for most alts then ... */
2538 /* Lets see if it's a pattern we know all about ... */
2539 if (wParam == VK_PRIOR && shift_state == 1) {
2540 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2543 if (wParam == VK_NEXT && shift_state == 1) {
2544 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2547 if (wParam == VK_INSERT && shift_state == 1) {
2551 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2554 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2555 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2558 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter) {
2562 /* Control-Numlock for app-keypad mode switch */
2563 if (wParam == VK_PAUSE && shift_state == 2) {
2564 app_keypad_keys ^= 1;
2568 /* Nethack keypad */
2569 if (cfg.nethack_keypad && !left_alt) {
2572 *p++ = shift_state ? 'B' : 'b';
2575 *p++ = shift_state ? 'J' : 'j';
2578 *p++ = shift_state ? 'N' : 'n';
2581 *p++ = shift_state ? 'H' : 'h';
2584 *p++ = shift_state ? '.' : '.';
2587 *p++ = shift_state ? 'L' : 'l';
2590 *p++ = shift_state ? 'Y' : 'y';
2593 *p++ = shift_state ? 'K' : 'k';
2596 *p++ = shift_state ? 'U' : 'u';
2601 /* Application Keypad */
2605 if (cfg.funky_type == 3 ||
2606 (cfg.funky_type <= 1 &&
2607 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
2621 if (app_keypad_keys && !cfg.no_applic_k)
2658 if (cfg.funky_type == 2) {
2663 } else if (shift_state)
2670 if (cfg.funky_type == 2)
2674 if (cfg.funky_type == 2)
2678 if (cfg.funky_type == 2)
2683 if (HIWORD(lParam) & KF_EXTENDED)
2689 if (xkey >= 'P' && xkey <= 'S')
2690 p += sprintf((char *) p, "\x1B%c", xkey);
2692 p += sprintf((char *) p, "\x1B?%c", xkey);
2694 p += sprintf((char *) p, "\x1BO%c", xkey);
2699 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
2700 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2704 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
2710 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
2714 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
2718 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
2723 if (wParam == VK_PAUSE) { /* Break/Pause */
2728 /* Control-2 to Control-8 are special */
2729 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
2730 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
2733 if (shift_state == 2 && wParam == 0xBD) {
2737 if (shift_state == 2 && wParam == 0xDF) {
2741 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2748 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2749 * for integer decimal nn.)
2751 * We also deal with the weird ones here. Linux VCs replace F1
2752 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2753 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2759 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
2762 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
2765 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
2768 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
2771 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
2774 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
2777 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
2780 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
2783 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
2786 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
2819 if ((shift_state&2) == 0) switch (wParam) {
2839 /* Reorder edit keys to physical order */
2840 if (cfg.funky_type == 3 && code <= 6)
2841 code = "\0\2\1\4\5\3\6"[code];
2843 if (vt52_mode && code > 0 && code <= 6) {
2844 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
2848 if (cfg.funky_type == 5 && /* SCO function keys */
2849 code >= 11 && code <= 34) {
2850 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
2853 case VK_F1: index = 0; break;
2854 case VK_F2: index = 1; break;
2855 case VK_F3: index = 2; break;
2856 case VK_F4: index = 3; break;
2857 case VK_F5: index = 4; break;
2858 case VK_F6: index = 5; break;
2859 case VK_F7: index = 6; break;
2860 case VK_F8: index = 7; break;
2861 case VK_F9: index = 8; break;
2862 case VK_F10: index = 9; break;
2863 case VK_F11: index = 10; break;
2864 case VK_F12: index = 11; break;
2866 if (keystate[VK_SHIFT] & 0x80) index += 12;
2867 if (keystate[VK_CONTROL] & 0x80) index += 24;
2868 p += sprintf((char *) p, "\x1B[%c", codes[index]);
2871 if (cfg.funky_type == 5 && /* SCO small keypad */
2872 code >= 1 && code <= 6) {
2873 char codes[] = "HL.FIG";
2877 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
2881 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
2888 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
2891 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
2894 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2895 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
2898 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2900 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
2902 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
2905 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2906 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
2910 p += sprintf((char *) p, "\x1B[%d~", code);
2915 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2916 * some reason seems to send VK_CLEAR to Windows...).
2939 p += sprintf((char *) p, "\x1B%c", xkey);
2941 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
2942 /* VT100 & VT102 manuals both state the app cursor keys
2943 * only work if the app keypad is on.
2945 if (!app_keypad_keys)
2947 /* Useful mapping of Ctrl-arrows */
2948 if (shift_state == 2)
2952 p += sprintf((char *) p, "\x1BO%c", xkey);
2954 p += sprintf((char *) p, "\x1B[%c", xkey);
2961 * Finally, deal with Return ourselves. (Win95 seems to
2962 * foul it up when Alt is pressed, for some reason.)
2964 if (wParam == VK_RETURN) { /* Return */
2970 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
2971 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
2976 /* Okay we've done everything interesting; let windows deal with
2977 * the boring stuff */
2979 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
2980 #ifdef SHOW_TOASCII_RESULT
2981 if (r == 1 && !key_down) {
2983 if (in_utf || dbcs_screenfont)
2984 debug((", (U+%04x)", alt_sum));
2986 debug((", LCH(%d)", alt_sum));
2988 debug((", ACH(%d)", keys[0]));
2993 for (r1 = 0; r1 < r; r1++) {
2994 debug(("%s%d", r1 ? "," : "", keys[r1]));
3002 for (i = 0; i < r; i++) {
3003 unsigned char ch = (unsigned char) keys[i];
3005 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3010 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3014 if ((nc = check_compose(compose_char, ch)) == -1) {
3015 MessageBeep(MB_ICONHAND);
3019 luni_send(&keybuf, 1);
3027 if (in_utf || dbcs_screenfont) {
3029 luni_send(&keybuf, 1);
3031 ch = (char) alt_sum;
3033 * We need not bother about stdin
3034 * backlogs here, because in GUI PuTTY
3035 * we can't do anything about it
3036 * anyway; there's no means of asking
3037 * Windows to hold off on KEYDOWN
3038 * messages. We _have_ to buffer
3039 * everything we're sent.
3045 lpage_send(kbd_codepage, &ch, 1);
3047 static char cbuf[] = "\033 ";
3049 lpage_send(kbd_codepage, cbuf + !left_alt,
3055 /* This is so the ALT-Numpad and dead keys work correctly. */
3060 /* If we're definitly not building up an ALT-54321 then clear it */
3063 /* If we will be using alt_sum fix the 256s */
3064 else if (keys[0] && (in_utf || dbcs_screenfont))
3069 * ALT alone may or may not want to bring up the System menu.
3070 * If it's not meant to, we return 0 on presses or releases of
3071 * ALT, to show that we've swallowed the keystroke. Otherwise
3072 * we return -1, which means Windows will give the keystroke
3073 * its default handling (i.e. bring up the System menu).
3075 if (wParam == VK_MENU && !cfg.alt_only)
3081 void set_title(char *title)
3084 window_name = smalloc(1 + strlen(title));
3085 strcpy(window_name, title);
3086 if (cfg.win_name_always || !IsIconic(hwnd))
3087 SetWindowText(hwnd, title);
3090 void set_icon(char *title)
3093 icon_name = smalloc(1 + strlen(title));
3094 strcpy(icon_name, title);
3095 if (!cfg.win_name_always && IsIconic(hwnd))
3096 SetWindowText(hwnd, title);
3099 void set_sbar(int total, int start, int page)
3106 si.cbSize = sizeof(si);
3107 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3109 si.nMax = total - 1;
3113 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3116 Context get_ctx(void)
3122 SelectPalette(hdc, pal, FALSE);
3128 void free_ctx(Context ctx)
3130 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3131 ReleaseDC(hwnd, ctx);
3134 static void real_palette_set(int n, int r, int g, int b)
3137 logpal->palPalEntry[n].peRed = r;
3138 logpal->palPalEntry[n].peGreen = g;
3139 logpal->palPalEntry[n].peBlue = b;
3140 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3141 colours[n] = PALETTERGB(r, g, b);
3142 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3144 colours[n] = RGB(r, g, b);
3147 void palette_set(int n, int r, int g, int b)
3149 static const int first[21] = {
3150 0, 2, 4, 6, 8, 10, 12, 14,
3151 1, 3, 5, 7, 9, 11, 13, 15,
3154 real_palette_set(first[n], r, g, b);
3156 real_palette_set(first[n] + 1, r, g, b);
3158 HDC hdc = get_ctx();
3159 UnrealizeObject(pal);
3160 RealizePalette(hdc);
3165 void palette_reset(void)
3169 for (i = 0; i < NCOLOURS; i++) {
3171 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3172 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3173 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3174 logpal->palPalEntry[i].peFlags = 0;
3175 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3176 defpal[i].rgbtGreen,
3177 defpal[i].rgbtBlue);
3179 colours[i] = RGB(defpal[i].rgbtRed,
3180 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3185 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3187 RealizePalette(hdc);
3192 void write_aclip(char *data, int len, int must_deselect)
3197 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3200 lock = GlobalLock(clipdata);
3203 memcpy(lock, data, len);
3204 ((unsigned char *) lock)[len] = 0;
3205 GlobalUnlock(clipdata);
3208 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3210 if (OpenClipboard(hwnd)) {
3212 SetClipboardData(CF_TEXT, clipdata);
3215 GlobalFree(clipdata);
3218 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3222 * Note: unlike write_aclip() this will not append a nul.
3224 void write_clip(wchar_t * data, int len, int must_deselect)
3231 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3233 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3234 len * sizeof(wchar_t));
3235 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3237 if (!clipdata || !clipdata2) {
3239 GlobalFree(clipdata);
3241 GlobalFree(clipdata2);
3244 if (!(lock = GlobalLock(clipdata)))
3246 if (!(lock2 = GlobalLock(clipdata2)))
3249 memcpy(lock, data, len * sizeof(wchar_t));
3250 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3252 GlobalUnlock(clipdata);
3253 GlobalUnlock(clipdata2);
3256 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3258 if (OpenClipboard(hwnd)) {
3260 SetClipboardData(CF_UNICODETEXT, clipdata);
3261 SetClipboardData(CF_TEXT, clipdata2);
3264 GlobalFree(clipdata);
3265 GlobalFree(clipdata2);
3269 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3272 void get_clip(wchar_t ** p, int *len)
3274 static HGLOBAL clipdata = NULL;
3275 static wchar_t *converted = 0;
3284 GlobalUnlock(clipdata);
3287 } else if (OpenClipboard(NULL)) {
3288 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3290 *p = GlobalLock(clipdata);
3292 for (p2 = *p; *p2; p2++);
3296 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3300 s = GlobalLock(clipdata);
3301 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3302 *p = converted = smalloc(i * sizeof(wchar_t));
3303 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3316 * Move `lines' lines from position `from' to position `to' in the
3319 void optimised_move(int to, int from, int lines)
3324 min = (to < from ? to : from);
3325 max = to + from - min;
3328 r.right = cols * font_width;
3329 r.top = min * font_height;
3330 r.bottom = (max + lines) * font_height;
3331 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3336 * Print a message box and perform a fatal exit.
3338 void fatalbox(char *fmt, ...)
3344 vsprintf(stuff, fmt, ap);
3346 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3351 * Manage window caption / taskbar flashing, if enabled.
3352 * 0 = stop, 1 = maintain, 2 = start
3354 static void flash_window(int mode)
3356 static long last_flash = 0;
3357 static int flashing = 0;
3358 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3361 FlashWindow(hwnd, FALSE);
3365 } else if (mode == 2) {
3368 last_flash = GetTickCount();
3370 FlashWindow(hwnd, TRUE);
3373 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3376 long now = GetTickCount();
3377 long fdiff = now - last_flash;
3378 if (fdiff < 0 || fdiff > 450) {
3380 FlashWindow(hwnd, TRUE); /* toggle */
3391 if (mode == BELL_DEFAULT) {
3393 * For MessageBeep style bells, we want to be careful of
3394 * timing, because they don't have the nice property of
3395 * PlaySound bells that each one cancels the previous
3396 * active one. So we limit the rate to one per 50ms or so.
3398 static long lastbeep = 0;
3401 beepdiff = GetTickCount() - lastbeep;
3402 if (beepdiff >= 0 && beepdiff < 50)
3406 * The above MessageBeep call takes time, so we record the
3407 * time _after_ it finishes rather than before it starts.
3409 lastbeep = GetTickCount();
3410 } else if (mode == BELL_WAVEFILE) {
3411 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3412 char buf[sizeof(cfg.bell_wavefile) + 80];
3413 sprintf(buf, "Unable to play sound file\n%s\n"
3414 "Using default sound instead", cfg.bell_wavefile);
3415 MessageBox(hwnd, buf, "PuTTY Sound Error",
3416 MB_OK | MB_ICONEXCLAMATION);
3417 cfg.beep = BELL_DEFAULT;
3420 /* Otherwise, either visual bell or disabled; do nothing here */
3422 flash_window(2); /* start */
3427 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3430 static void flip_full_screen(void)
3435 cx = GetSystemMetrics(SM_CXSCREEN);
3436 cy = GetSystemMetrics(SM_CYSCREEN);
3437 GetWindowPlacement(hwnd, &old_wind_placement);
3438 old_wind_style = GetWindowLong(hwnd, GWL_STYLE);
3439 SetWindowLong(hwnd, GWL_STYLE,
3440 old_wind_style & ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME));
3441 SetWindowPos(hwnd, HWND_TOP, 0, 0, cx, cy, SWP_SHOWWINDOW);
3444 SetWindowLong(hwnd, GWL_STYLE, old_wind_style);
3445 SetWindowPlacement(hwnd,&old_wind_placement);