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 extra_width, extra_height;
76 static int pending_netevent = 0;
77 static WPARAM pend_netevent_wParam = 0;
78 static LPARAM pend_netevent_lParam = 0;
79 static void enact_pending_netevent(void);
80 static void flash_window(int mode);
82 static time_t last_movement = 0;
86 #define FONT_UNDERLINE 2
87 #define FONT_BOLDUND 3
88 #define FONT_WIDE 0x04
89 #define FONT_HIGH 0x08
90 #define FONT_NARROW 0x10
92 #define FONT_OEMBOLD 0x21
93 #define FONT_OEMUND 0x22
94 #define FONT_OEMBOLDUND 0x23
95 #define FONT_MSGOTHIC 0x40
96 #define FONT_MINGLIU 0x60
97 #define FONT_GULIMCHE 0x80
98 #define FONT_MAXNO 0x8F
100 static HFONT fonts[FONT_MAXNO];
101 static int fontflag[FONT_MAXNO];
103 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
111 static COLORREF colours[NCOLOURS];
113 static LPLOGPALETTE logpal;
114 static RGBTRIPLE defpal[NCOLOURS];
118 static HBITMAP caretbm;
120 static int dbltime, lasttime, lastact;
121 static Mouse_Button lastbtn;
123 /* this allows xterm-style mouse handling. */
124 static int send_raw_mouse = 0;
125 static int wheel_accumulator = 0;
127 static char *window_name, *icon_name;
129 static int compose_state = 0;
131 static OSVERSIONINFOEX osVersion;
133 /* Dummy routine, only required in plink. */
134 void ldisc_update(int echo, int edit)
138 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
140 static char appname[] = "PuTTY";
145 int guess_width, guess_height;
148 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
150 winsock_ver = MAKEWORD(1, 1);
151 if (WSAStartup(winsock_ver, &wsadata)) {
152 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
153 MB_OK | MB_ICONEXCLAMATION);
156 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
157 MessageBox(NULL, "WinSock version is incompatible with 1.1",
158 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
162 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
165 InitCommonControls();
167 /* Ensure a Maximize setting in Explorer doesn't maximise the
172 ZeroMemory(&osVersion, sizeof(osVersion));
173 osVersion.dwOSVersionInfoSize = sizeof(osVersion);
175 if(!GetVersionEx ((OSVERSIONINFO *) &osVersion)) {
176 // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO.
178 osVersion.dwOSVersionInfoSize = sizeof (osVersion);
179 if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion))
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_READ | FD_WRITE | FD_OOB | FD_CLOSE;
717 return "do_select(): internal error (hwnd==NULL)";
718 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
719 switch (WSAGetLastError()) {
721 return "Network is down";
723 return "WSAAsyncSelect(): unknown error";
730 * set or clear the "raw mouse message" mode
732 void set_raw_mouse_mode(int activate)
734 send_raw_mouse = activate;
735 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
739 * Print a message box and close the connection.
741 void connection_fatal(char *fmt, ...)
747 vsprintf(stuff, fmt, ap);
749 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
750 if (cfg.close_on_exit == COE_ALWAYS)
753 session_closed = TRUE;
754 SetWindowText(hwnd, "PuTTY (inactive)");
759 * Actually do the job requested by a WM_NETEVENT
761 static void enact_pending_netevent(void)
763 static int reentering = 0;
764 extern int select_result(WPARAM, LPARAM);
768 return; /* don't unpend the pending */
770 pending_netevent = FALSE;
773 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
776 if (ret == 0 && !session_closed) {
777 /* Abnormal exits will already have set session_closed and taken
778 * appropriate action. */
779 if (cfg.close_on_exit == COE_ALWAYS ||
780 cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
782 session_closed = TRUE;
783 SetWindowText(hwnd, "PuTTY (inactive)");
784 MessageBox(hwnd, "Connection closed by remote host",
785 "PuTTY", MB_OK | MB_ICONINFORMATION);
791 * Copy the colour palette from the configuration data into defpal.
792 * This is non-trivial because the colour indices are different.
794 static void cfgtopalette(void)
797 static const int ww[] = {
798 6, 7, 8, 9, 10, 11, 12, 13,
799 14, 15, 16, 17, 18, 19, 20, 21,
800 0, 1, 2, 3, 4, 4, 5, 5
803 for (i = 0; i < 24; i++) {
805 defpal[i].rgbtRed = cfg.colours[w][0];
806 defpal[i].rgbtGreen = cfg.colours[w][1];
807 defpal[i].rgbtBlue = cfg.colours[w][2];
812 * Set up the colour palette.
814 static void init_palette(void)
817 HDC hdc = GetDC(hwnd);
819 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
820 logpal = smalloc(sizeof(*logpal)
821 - sizeof(logpal->palPalEntry)
822 + NCOLOURS * sizeof(PALETTEENTRY));
823 logpal->palVersion = 0x300;
824 logpal->palNumEntries = NCOLOURS;
825 for (i = 0; i < NCOLOURS; i++) {
826 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
827 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
828 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
829 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
831 pal = CreatePalette(logpal);
833 SelectPalette(hdc, pal, FALSE);
835 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
838 ReleaseDC(hwnd, hdc);
841 for (i = 0; i < NCOLOURS; i++)
842 colours[i] = PALETTERGB(defpal[i].rgbtRed,
846 for (i = 0; i < NCOLOURS; i++)
847 colours[i] = RGB(defpal[i].rgbtRed,
848 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
852 * Initialise all the fonts we will need initially. There may be as many as
853 * three or as few as one. The other (poentially) twentyone fonts are done
854 * if/when they are needed.
858 * - check the font width and height, correcting our guesses if
861 * - verify that the bold font is the same width as the ordinary
862 * one, and engage shadow bolding if not.
864 * - verify that the underlined font is the same width as the
865 * ordinary one (manual underlining by means of line drawing can
866 * be done in a pinch).
868 static void init_fonts(int pick_width)
875 int fw_dontcare, fw_bold;
877 for (i = 0; i < FONT_MAXNO; i++)
880 if (cfg.fontisbold) {
881 fw_dontcare = FW_BOLD;
884 fw_dontcare = FW_DONTCARE;
890 font_height = cfg.fontheight;
891 if (font_height > 0) {
893 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
895 font_width = pick_width;
898 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
899 c, OUT_DEFAULT_PRECIS, \
900 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
901 FIXED_PITCH | FF_DONTCARE, cfg.font)
903 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
905 SelectObject(hdc, fonts[FONT_NORMAL]);
906 GetTextMetrics(hdc, &tm);
907 font_height = tm.tmHeight;
908 font_width = tm.tmAveCharWidth;
912 DWORD cset = tm.tmCharSet;
913 memset(&info, 0xFF, sizeof(info));
915 /* !!! Yes the next line is right */
916 if (cset == OEM_CHARSET)
917 font_codepage = GetOEMCP();
919 if (TranslateCharsetInfo
920 ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
925 GetCPInfo(font_codepage, &cpinfo);
926 dbcs_screenfont = (cpinfo.MaxCharSize > 1);
929 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
932 * Some fonts, e.g. 9-pt Courier, draw their underlines
933 * outside their character cell. We successfully prevent
934 * screen corruption by clipping the text output, but then
935 * we lose the underline completely. Here we try to work
936 * out whether this is such a font, and if it is, we set a
937 * flag that causes underlines to be drawn by hand.
939 * Having tried other more sophisticated approaches (such
940 * as examining the TEXTMETRIC structure or requesting the
941 * height of a string), I think we'll do this the brute
942 * force way: we create a small bitmap, draw an underlined
943 * space on it, and test to see whether any pixels are
944 * foreground-coloured. (Since we expect the underline to
945 * go all the way across the character cell, we only search
946 * down a single column of the bitmap, half way across.)
950 HBITMAP und_bm, und_oldbm;
954 und_dc = CreateCompatibleDC(hdc);
955 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
956 und_oldbm = SelectObject(und_dc, und_bm);
957 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
958 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
959 SetTextColor(und_dc, RGB(255, 255, 255));
960 SetBkColor(und_dc, RGB(0, 0, 0));
961 SetBkMode(und_dc, OPAQUE);
962 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
964 for (i = 0; i < font_height; i++) {
965 c = GetPixel(und_dc, font_width / 2, i);
966 if (c != RGB(0, 0, 0))
969 SelectObject(und_dc, und_oldbm);
970 DeleteObject(und_bm);
974 DeleteObject(fonts[FONT_UNDERLINE]);
975 fonts[FONT_UNDERLINE] = 0;
979 if (bold_mode == BOLD_FONT) {
980 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
984 descent = tm.tmAscent + 1;
985 if (descent >= font_height)
986 descent = font_height - 1;
988 for (i = 0; i < 3; i++) {
990 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
991 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
998 ReleaseDC(hwnd, hdc);
1000 if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
1001 und_mode = UND_LINE;
1002 DeleteObject(fonts[FONT_UNDERLINE]);
1003 fonts[FONT_UNDERLINE] = 0;
1006 if (bold_mode == BOLD_FONT &&
1007 fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
1008 bold_mode = BOLD_SHADOW;
1009 DeleteObject(fonts[FONT_BOLD]);
1010 fonts[FONT_BOLD] = 0;
1012 fontflag[0] = fontflag[1] = fontflag[2] = 1;
1017 static void another_font(int fontno)
1020 int fw_dontcare, fw_bold;
1024 if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1027 basefont = (fontno & ~(FONT_BOLDUND));
1028 if (basefont != fontno && !fontflag[basefont])
1029 another_font(basefont);
1031 if (cfg.fontisbold) {
1032 fw_dontcare = FW_BOLD;
1035 fw_dontcare = FW_DONTCARE;
1039 c = cfg.fontcharset;
1045 if (fontno & FONT_WIDE)
1047 if (fontno & FONT_NARROW)
1049 if (fontno & FONT_OEM)
1051 if (fontno & FONT_BOLD)
1053 if (fontno & FONT_UNDERLINE)
1057 CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1058 FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1059 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1060 FIXED_PITCH | FF_DONTCARE, s);
1062 fontflag[fontno] = 1;
1065 static void deinit_fonts(void)
1068 for (i = 0; i < FONT_MAXNO; i++) {
1070 DeleteObject(fonts[i]);
1076 void request_resize(int w, int h, int refont)
1080 /* If the window is maximized supress resizing attempts */
1084 if (refont && w != cols && (cols == 80 || cols == 132)) {
1085 /* If font width too big for screen should we shrink the font more ? */
1087 font_width = ((font_width * cols + w / 2) / w);
1091 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1092 und_mode = UND_FONT;
1093 init_fonts(font_width);
1095 static int first_time = 1;
1098 switch (first_time) {
1100 /* Get the size of the screen */
1101 if (GetClientRect(GetDesktopWindow(), &ss))
1102 /* first_time = 0 */ ;
1108 /* Make sure the values are sane */
1109 width = (ss.right - ss.left - extra_width) / font_width;
1110 height = (ss.bottom - ss.top - extra_height) / font_height;
1123 width = extra_width + font_width * w;
1124 height = extra_height + font_height * h;
1126 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1127 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1128 SWP_NOMOVE | SWP_NOZORDER);
1131 static void click(Mouse_Button b, int x, int y, int shift, int ctrl)
1133 int thistime = GetMessageTime();
1135 if (send_raw_mouse) {
1136 term_mouse(b, MA_CLICK, x, y, shift, ctrl);
1140 if (lastbtn == b && thistime - lasttime < dbltime) {
1141 lastact = (lastact == MA_CLICK ? MA_2CLK :
1142 lastact == MA_2CLK ? MA_3CLK :
1143 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1148 if (lastact != MA_NOTHING)
1149 term_mouse(b, lastact, x, y, shift, ctrl);
1150 lasttime = thistime;
1154 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1155 * into a cooked one (SELECT, EXTEND, PASTE).
1157 Mouse_Button translate_button(Mouse_Button button)
1159 if (button == MBT_LEFT)
1161 if (button == MBT_MIDDLE)
1162 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1163 if (button == MBT_RIGHT)
1164 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1165 return 0; /* shouldn't happen */
1168 static void show_mouseptr(int show)
1170 static int cursor_visible = 1;
1171 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1173 if (cursor_visible && !show)
1175 else if (!cursor_visible && show)
1177 cursor_visible = show;
1180 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1181 WPARAM wParam, LPARAM lParam)
1184 static int ignore_size = FALSE;
1185 static int ignore_clip = FALSE;
1186 static int just_reconfigged = FALSE;
1187 static int resizing = FALSE;
1188 static int need_backend_resize = FALSE;
1189 static int defered_resize = FALSE;
1193 if (pending_netevent)
1194 enact_pending_netevent();
1201 if (cfg.ping_interval > 0) {
1204 if (now - last_movement > cfg.ping_interval) {
1205 back->special(TS_PING);
1206 last_movement = now;
1214 if (!cfg.warn_on_close || session_closed ||
1216 "Are you sure you want to close this session?",
1217 "PuTTY Exit Confirmation",
1218 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1219 DestroyWindow(hwnd);
1226 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1238 PROCESS_INFORMATION pi;
1239 HANDLE filemap = NULL;
1241 if (wParam == IDM_DUPSESS) {
1243 * Allocate a file-mapping memory chunk for the
1246 SECURITY_ATTRIBUTES sa;
1249 sa.nLength = sizeof(sa);
1250 sa.lpSecurityDescriptor = NULL;
1251 sa.bInheritHandle = TRUE;
1252 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1255 0, sizeof(Config), NULL);
1257 p = (Config *) MapViewOfFile(filemap,
1259 0, 0, sizeof(Config));
1261 *p = cfg; /* structure copy */
1265 sprintf(c, "putty &%p", filemap);
1267 } else if (wParam == IDM_SAVEDSESS) {
1269 sessions[(lParam - IDM_SAVED_MIN) / 16];
1270 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1272 cl = NULL; /* not a very important failure mode */
1274 sprintf(cl, "putty @%s", session);
1280 GetModuleFileName(NULL, b, sizeof(b) - 1);
1282 si.lpReserved = NULL;
1283 si.lpDesktop = NULL;
1287 si.lpReserved2 = NULL;
1288 CreateProcess(b, cl, NULL, NULL, TRUE,
1289 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1292 CloseHandle(filemap);
1299 int prev_alwaysontop = cfg.alwaysontop;
1300 int prev_sunken_edge = cfg.sunken_edge;
1301 char oldlogfile[FILENAME_MAX];
1303 int need_setwpos = FALSE;
1304 int old_fwidth, old_fheight;
1306 strcpy(oldlogfile, cfg.logfilename);
1307 oldlogtype = cfg.logtype;
1308 old_fwidth = font_width;
1309 old_fheight = font_height;
1310 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1312 if (!do_reconfig(hwnd))
1315 if (strcmp(oldlogfile, cfg.logfilename) ||
1316 oldlogtype != cfg.logtype) {
1317 logfclose(); /* reset logging */
1321 just_reconfigged = TRUE;
1323 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1324 und_mode = UND_FONT;
1328 * Flush the line discipline's edit buffer in the
1329 * case where local editing has just been disabled.
1331 ldisc_send(NULL, 0);
1339 /* Enable or disable the scroll bar, etc */
1341 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1342 LONG nexflag, exflag =
1343 GetWindowLong(hwnd, GWL_EXSTYLE);
1346 if (cfg.alwaysontop != prev_alwaysontop) {
1347 if (cfg.alwaysontop) {
1348 nexflag |= WS_EX_TOPMOST;
1349 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1350 SWP_NOMOVE | SWP_NOSIZE);
1352 nexflag &= ~(WS_EX_TOPMOST);
1353 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1354 SWP_NOMOVE | SWP_NOSIZE);
1357 if (cfg.sunken_edge)
1358 nexflag |= WS_EX_CLIENTEDGE;
1360 nexflag &= ~(WS_EX_CLIENTEDGE);
1366 nflg &= ~WS_VSCROLL;
1368 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1370 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1372 if (nflg != flag || nexflag != exflag) {
1376 SetWindowLong(hwnd, GWL_STYLE, nflg);
1377 if (nexflag != exflag)
1378 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1380 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1382 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1383 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1384 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER
1385 | SWP_FRAMECHANGED);
1387 GetWindowRect(hwnd, &wr);
1388 GetClientRect(hwnd, &cr);
1390 wr.right - wr.left - cr.right + cr.left;
1392 wr.bottom - wr.top - cr.bottom + cr.top;
1393 need_setwpos = TRUE;
1397 if (cfg.height != rows ||
1398 cfg.width != cols ||
1399 old_fwidth != font_width ||
1400 old_fheight != font_height ||
1401 cfg.savelines != savelines ||
1402 cfg.sunken_edge != prev_sunken_edge)
1403 need_setwpos = TRUE;
1405 if (IsZoomed(hwnd)) {
1409 defered_resize = TRUE;
1411 GetClientRect(hwnd, &cr);
1412 w = cr.right - cr.left;
1413 h = cr.bottom - cr.top;
1417 h = h / font_height;
1421 term_size(h, w, cfg.savelines);
1422 InvalidateRect(hwnd, NULL, TRUE);
1425 term_size(cfg.height, cfg.width, cfg.savelines);
1426 InvalidateRect(hwnd, NULL, TRUE);
1428 SetWindowPos(hwnd, NULL, 0, 0,
1429 extra_width + font_width * cfg.width,
1431 font_height * cfg.height,
1432 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1433 SWP_NOMOVE | SWP_NOZORDER);
1437 if (cfg.locksize && IsZoomed(hwnd))
1439 set_title(cfg.wintitle);
1440 if (IsIconic(hwnd)) {
1442 cfg.win_name_always ? window_name :
1457 back->special(TS_AYT);
1460 back->special(TS_BRK);
1463 back->special(TS_SYNCH);
1466 back->special(TS_EC);
1469 back->special(TS_EL);
1472 back->special(TS_GA);
1475 back->special(TS_NOP);
1478 back->special(TS_ABORT);
1481 back->special(TS_AO);
1484 back->special(TS_IP);
1487 back->special(TS_SUSP);
1490 back->special(TS_EOR);
1493 back->special(TS_EOF);
1499 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1500 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1505 #define X_POS(l) ((int)(short)LOWORD(l))
1506 #define Y_POS(l) ((int)(short)HIWORD(l))
1508 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1509 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1510 #define WHEEL_DELTA 120
1513 wheel_accumulator += (short) HIWORD(wParam);
1514 wParam = LOWORD(wParam);
1516 /* process events when the threshold is reached */
1517 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1520 /* reduce amount for next time */
1521 if (wheel_accumulator > 0) {
1523 wheel_accumulator -= WHEEL_DELTA;
1524 } else if (wheel_accumulator < 0) {
1526 wheel_accumulator += WHEEL_DELTA;
1530 if (send_raw_mouse) {
1531 /* send a mouse-down followed by a mouse up */
1534 TO_CHR_X(X_POS(lParam)),
1535 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1536 wParam & MK_CONTROL);
1537 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1538 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1539 wParam & MK_CONTROL);
1541 /* trigger a scroll */
1543 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1548 case WM_LBUTTONDOWN:
1549 case WM_MBUTTONDOWN:
1550 case WM_RBUTTONDOWN:
1557 case WM_LBUTTONDOWN:
1561 case WM_MBUTTONDOWN:
1562 button = MBT_MIDDLE;
1565 case WM_RBUTTONDOWN:
1574 button = MBT_MIDDLE;
1582 button = press = 0; /* shouldn't happen */
1587 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1588 wParam & MK_SHIFT, wParam & MK_CONTROL);
1591 term_mouse(button, MA_RELEASE,
1592 TO_CHR_X(X_POS(lParam)),
1593 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1594 wParam & MK_CONTROL);
1602 * Add the mouse position and message time to the random
1605 noise_ultralight(lParam);
1607 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1609 if (wParam & MK_LBUTTON)
1611 else if (wParam & MK_MBUTTON)
1615 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1616 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1617 wParam & MK_CONTROL);
1620 case WM_NCMOUSEMOVE:
1622 noise_ultralight(lParam);
1624 case WM_IGNORE_CLIP:
1625 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1627 case WM_DESTROYCLIPBOARD:
1630 ignore_clip = FALSE;
1636 hdc = BeginPaint(hwnd, &p);
1638 SelectPalette(hdc, pal, TRUE);
1639 RealizePalette(hdc);
1641 term_paint(hdc, p.rcPaint.left, p.rcPaint.top,
1642 p.rcPaint.right, p.rcPaint.bottom);
1643 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1644 SelectObject(hdc, GetStockObject(WHITE_PEN));
1650 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1651 * but the only one that's likely to try to overload us is FD_READ.
1652 * This means buffering just one is fine.
1654 if (pending_netevent)
1655 enact_pending_netevent();
1657 pending_netevent = TRUE;
1658 pend_netevent_wParam = wParam;
1659 pend_netevent_lParam = lParam;
1660 time(&last_movement);
1664 CreateCaret(hwnd, caretbm, font_width, font_height);
1666 flash_window(0); /* stop */
1678 case WM_IGNORE_SIZE:
1679 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1681 case WM_ENTERSIZEMOVE:
1684 need_backend_resize = FALSE;
1686 case WM_EXITSIZEMOVE:
1689 if (need_backend_resize)
1694 int width, height, w, h, ew, eh;
1695 LPRECT r = (LPRECT) lParam;
1697 width = r->right - r->left - extra_width;
1698 height = r->bottom - r->top - extra_height;
1699 w = (width + font_width / 2) / font_width;
1702 h = (height + font_height / 2) / font_height;
1705 UpdateSizeTip(hwnd, w, h);
1706 ew = width - w * font_width;
1707 eh = height - h * font_height;
1709 if (wParam == WMSZ_LEFT ||
1710 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1716 if (wParam == WMSZ_TOP ||
1717 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1727 /* break; (never reached) */
1729 if (wParam == SIZE_MINIMIZED) {
1731 cfg.win_name_always ? window_name : icon_name);
1734 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1735 SetWindowText(hwnd, window_name);
1737 int width, height, w, h;
1738 #if 0 /* we have fixed this using WM_SIZING now */
1742 width = LOWORD(lParam);
1743 height = HIWORD(lParam);
1744 w = width / font_width;
1747 h = height / font_height;
1750 #if 0 /* we have fixed this using WM_SIZING now */
1751 ew = width - w * font_width;
1752 eh = height - h * font_height;
1753 if (ew != 0 || eh != 0) {
1755 GetWindowRect(hwnd, &r);
1756 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1757 SetWindowPos(hwnd, NULL, 0, 0,
1758 r.right - r.left - ew, r.bottom - r.top - eh,
1759 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1762 if (w != cols || h != rows || just_reconfigged) {
1764 term_size(h, w, cfg.savelines);
1766 * Don't call back->size in mid-resize. (To prevent
1767 * massive numbers of resize events getting sent
1768 * down the connection during an NT opaque drag.)
1773 need_backend_resize = TRUE;
1777 just_reconfigged = FALSE;
1780 if (wParam == SIZE_RESTORED && defered_resize) {
1781 defered_resize = FALSE;
1782 SetWindowPos(hwnd, NULL, 0, 0,
1783 extra_width + font_width * cfg.width,
1784 extra_height + font_height * cfg.height,
1785 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1786 SWP_NOMOVE | SWP_NOZORDER);
1788 ignore_size = FALSE;
1791 switch (LOWORD(wParam)) {
1805 term_scroll(0, +rows / 2);
1808 term_scroll(0, -rows / 2);
1810 case SB_THUMBPOSITION:
1812 term_scroll(1, HIWORD(wParam));
1816 case WM_PALETTECHANGED:
1817 if ((HWND) wParam != hwnd && pal != NULL) {
1818 HDC hdc = get_ctx();
1820 if (RealizePalette(hdc) > 0)
1826 case WM_QUERYNEWPALETTE:
1828 HDC hdc = get_ctx();
1830 if (RealizePalette(hdc) > 0)
1842 * Add the scan code and keypress timing to the random
1845 noise_ultralight(lParam);
1848 * We don't do TranslateMessage since it disassociates the
1849 * resulting CHAR message from the KEYDOWN that sparked it,
1850 * which we occasionally don't want. Instead, we process
1851 * KEYDOWN, and call the Win32 translator functions so that
1852 * we get the translations under _our_ control.
1855 unsigned char buf[20];
1858 if (wParam == VK_PROCESSKEY) {
1861 m.message = WM_KEYDOWN;
1863 m.lParam = lParam & 0xdfff;
1864 TranslateMessage(&m);
1866 len = TranslateKey(message, wParam, lParam, buf);
1868 return DefWindowProc(hwnd, message, wParam, lParam);
1869 ldisc_send(buf, len);
1876 case WM_INPUTLANGCHANGE:
1878 /* wParam == Font number */
1879 /* lParam == Locale */
1881 HKL NewInputLocale = (HKL) lParam;
1883 // lParam == GetKeyboardLayout(0);
1885 GetLocaleInfo(LOWORD(NewInputLocale),
1886 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
1888 kbd_codepage = atoi(lbuf);
1891 case WM_IME_COMPOSITION:
1897 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
1898 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
1900 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
1901 break; /* fall back to DefWindowProc */
1903 hIMC = ImmGetContext(hwnd);
1904 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
1907 buff = (char*) smalloc(n);
1908 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
1909 luni_send((unsigned short *)buff, n / 2);
1912 ImmReleaseContext(hwnd, hIMC);
1917 if (wParam & 0xFF00) {
1918 unsigned char buf[2];
1921 buf[0] = wParam >> 8;
1922 lpage_send(kbd_codepage, buf, 2);
1924 char c = (unsigned char) wParam;
1925 lpage_send(kbd_codepage, &c, 1);
1931 * Nevertheless, we are prepared to deal with WM_CHAR
1932 * messages, should they crop up. So if someone wants to
1933 * post the things to us as part of a macro manoeuvre,
1934 * we're ready to cope.
1937 char c = (unsigned char)wParam;
1938 lpage_send(CP_ACP, &c, 1);
1942 if (send_raw_mouse) {
1943 SetCursor(LoadCursor(NULL, IDC_ARROW));
1948 return DefWindowProc(hwnd, message, wParam, lParam);
1952 * Move the system caret. (We maintain one, even though it's
1953 * invisible, for the benefit of blind people: apparently some
1954 * helper software tracks the system caret, so we should arrange to
1957 void sys_cursor(int x, int y)
1962 if (!has_focus) return;
1964 SetCaretPos(x * font_width, y * font_height);
1966 /* IMM calls on Win98 and beyond only */
1967 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
1969 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
1970 osVersion.dwMinorVersion == 0) return; /* 95 */
1972 /* we should have the IMM functions */
1973 hIMC = ImmGetContext(hwnd);
1974 cf.dwStyle = CFS_POINT;
1975 cf.ptCurrentPos.x = x * font_width;
1976 cf.ptCurrentPos.y = y * font_height;
1977 ImmSetCompositionWindow(hIMC, &cf);
1979 ImmReleaseContext(hwnd, hIMC);
1983 * Draw a line of text in the window, at given character
1984 * coordinates, in given attributes.
1986 * We are allowed to fiddle with the contents of `text'.
1988 void do_text(Context ctx, int x, int y, char *text, int len,
1989 unsigned long attr, int lattr)
1992 int nfg, nbg, nfont;
1995 int force_manual_underline = 0;
1996 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
1997 int char_width = fnt_width;
1998 int text_adjust = 0;
1999 static int *IpDx = 0, IpDxLEN = 0;
2001 if (attr & ATTR_WIDE)
2004 if (len > IpDxLEN || IpDx[0] != char_width) {
2006 if (len > IpDxLEN) {
2008 IpDx = smalloc((len + 16) * sizeof(int));
2009 IpDxLEN = (len + 16);
2011 for (i = 0; i < IpDxLEN; i++)
2012 IpDx[i] = char_width;
2018 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2019 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2020 attr ^= ATTR_CUR_XOR;
2024 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2025 /* Assume a poorman font is borken in other ways too. */
2035 nfont |= FONT_WIDE + FONT_HIGH;
2039 /* Special hack for the VT100 linedraw glyphs. */
2040 if ((attr & CSET_MASK) == 0x2300) {
2041 if (!dbcs_screenfont &&
2042 text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2043 switch ((unsigned char) (text[0])) {
2045 text_adjust = -2 * font_height / 5;
2048 text_adjust = -1 * font_height / 5;
2051 text_adjust = font_height / 5;
2054 text_adjust = 2 * font_height / 5;
2057 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2060 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2061 attr |= (unitab_xterm['q'] & CSET_MASK);
2062 if (attr & ATTR_UNDER) {
2063 attr &= ~ATTR_UNDER;
2064 force_manual_underline = 1;
2069 /* Anything left as an original character set is unprintable. */
2070 if (DIRECT_CHAR(attr)) {
2073 memset(text, 0xFF, len);
2077 if ((attr & CSET_MASK) == ATTR_OEMCP)
2080 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2081 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2082 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2084 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2085 nfont |= FONT_UNDERLINE;
2086 another_font(nfont);
2087 if (!fonts[nfont]) {
2088 if (nfont & FONT_UNDERLINE)
2089 force_manual_underline = 1;
2090 /* Don't do the same for manual bold, it could be bad news. */
2092 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2094 another_font(nfont);
2096 nfont = FONT_NORMAL;
2097 if (attr & ATTR_REVERSE) {
2102 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2104 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2108 SelectObject(hdc, fonts[nfont]);
2109 SetTextColor(hdc, fg);
2110 SetBkColor(hdc, bg);
2111 SetBkMode(hdc, OPAQUE);
2114 line_box.right = x + char_width * len;
2115 line_box.bottom = y + font_height;
2117 /* We're using a private area for direct to font. (512 chars.) */
2118 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2119 /* Ho Hum, dbcs fonts are a PITA! */
2120 /* To display on W9x I have to convert to UCS */
2121 static wchar_t *uni_buf = 0;
2122 static int uni_len = 0;
2124 if (len > uni_len) {
2126 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2128 nlen = MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2129 text, len, uni_buf, uni_len);
2135 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2136 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, 0);
2137 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2138 SetBkMode(hdc, TRANSPARENT);
2139 ExtTextOutW(hdc, x - 1,
2140 y - font_height * (lattr ==
2141 LATTR_BOT) + text_adjust,
2142 ETO_CLIPPED, &line_box, uni_buf, nlen, 0);
2144 } else if (DIRECT_FONT(attr)) {
2146 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2147 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2148 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2149 SetBkMode(hdc, TRANSPARENT);
2151 /* GRR: This draws the character outside it's box and can leave
2152 * 'droppings' even with the clip box! I suppose I could loop it
2153 * one character at a time ... yuk.
2155 * Or ... I could do a test print with "W", and use +1 or -1 for this
2156 * shift depending on if the leftmost column is blank...
2158 ExtTextOut(hdc, x - 1,
2159 y - font_height * (lattr ==
2160 LATTR_BOT) + text_adjust,
2161 ETO_CLIPPED, &line_box, text, len, IpDx);
2164 /* And 'normal' unicode characters */
2165 static WCHAR *wbuf = NULL;
2166 static int wlen = 0;
2171 wbuf = smalloc(wlen * sizeof(WCHAR));
2173 for (i = 0; i < len; i++)
2174 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2177 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2178 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2180 /* And the shadow bold hack. */
2181 if (bold_mode == BOLD_SHADOW) {
2182 SetBkMode(hdc, TRANSPARENT);
2183 ExtTextOutW(hdc, x - 1,
2184 y - font_height * (lattr ==
2185 LATTR_BOT) + text_adjust,
2186 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2189 if (lattr != LATTR_TOP && (force_manual_underline ||
2190 (und_mode == UND_LINE
2191 && (attr & ATTR_UNDER)))) {
2194 if (lattr == LATTR_BOT)
2195 dec = dec * 2 - font_height;
2197 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2198 MoveToEx(hdc, x, y + dec, NULL);
2199 LineTo(hdc, x + len * char_width, y + dec);
2200 oldpen = SelectObject(hdc, oldpen);
2201 DeleteObject(oldpen);
2205 void do_cursor(Context ctx, int x, int y, char *text, int len,
2206 unsigned long attr, int lattr)
2212 int ctype = cfg.cursor_type;
2214 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2215 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2216 do_text(ctx, x, y, text, len, attr, lattr);
2220 attr |= TATTR_RIGHTCURS;
2223 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2224 if (attr & ATTR_WIDE)
2229 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2232 pts[0].x = pts[1].x = pts[4].x = x;
2233 pts[2].x = pts[3].x = x + char_width - 1;
2234 pts[0].y = pts[3].y = pts[4].y = y;
2235 pts[1].y = pts[2].y = y + font_height - 1;
2236 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2237 Polyline(hdc, pts, 5);
2238 oldpen = SelectObject(hdc, oldpen);
2239 DeleteObject(oldpen);
2240 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2241 int startx, starty, dx, dy, length, i;
2244 starty = y + descent;
2247 length = char_width;
2250 if (attr & TATTR_RIGHTCURS)
2251 xadjust = char_width - 1;
2252 startx = x + xadjust;
2256 length = font_height;
2258 if (attr & TATTR_ACTCURS) {
2261 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2262 MoveToEx(hdc, startx, starty, NULL);
2263 LineTo(hdc, startx + dx * length, starty + dy * length);
2264 oldpen = SelectObject(hdc, oldpen);
2265 DeleteObject(oldpen);
2267 for (i = 0; i < length; i++) {
2269 SetPixel(hdc, startx, starty, colours[23]);
2279 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2280 * codes. Returns number of bytes used or zero to drop the message
2281 * or -1 to forward the message to windows.
2283 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2284 unsigned char *output)
2287 int scan, left_alt = 0, key_down, shift_state;
2289 unsigned char *p = output;
2290 static int alt_state = 0;
2291 static int alt_sum = 0;
2293 HKL kbd_layout = GetKeyboardLayout(0);
2295 static WORD keys[3];
2296 static int compose_char = 0;
2297 static WPARAM compose_key = 0;
2299 r = GetKeyboardState(keystate);
2301 memset(keystate, 0, sizeof(keystate));
2304 #define SHOW_TOASCII_RESULT
2305 { /* Tell us all about key events */
2306 static BYTE oldstate[256];
2307 static int first = 1;
2311 memcpy(oldstate, keystate, sizeof(oldstate));
2314 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2316 } else if ((HIWORD(lParam) & KF_UP)
2317 && scan == (HIWORD(lParam) & 0xFF)) {
2321 if (wParam >= VK_F1 && wParam <= VK_F20)
2322 debug(("K_F%d", wParam + 1 - VK_F1));
2335 debug(("VK_%02x", wParam));
2337 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2339 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2341 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2342 if (ch >= ' ' && ch <= '~')
2343 debug((", '%c'", ch));
2345 debug((", $%02x", ch));
2348 debug((", KB0=%02x", keys[0]));
2350 debug((", KB1=%02x", keys[1]));
2352 debug((", KB2=%02x", keys[2]));
2354 if ((keystate[VK_SHIFT] & 0x80) != 0)
2356 if ((keystate[VK_CONTROL] & 0x80) != 0)
2358 if ((HIWORD(lParam) & KF_EXTENDED))
2360 if ((HIWORD(lParam) & KF_UP))
2364 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2365 else if ((HIWORD(lParam) & KF_UP))
2366 oldstate[wParam & 0xFF] ^= 0x80;
2368 oldstate[wParam & 0xFF] ^= 0x81;
2370 for (ch = 0; ch < 256; ch++)
2371 if (oldstate[ch] != keystate[ch])
2372 debug((", M%02x=%02x", ch, keystate[ch]));
2374 memcpy(oldstate, keystate, sizeof(oldstate));
2378 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2379 keystate[VK_RMENU] = keystate[VK_MENU];
2383 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2384 if ((cfg.funky_type == 3 ||
2385 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2386 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2388 wParam = VK_EXECUTE;
2390 /* UnToggle NUMLock */
2391 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2392 keystate[VK_NUMLOCK] ^= 1;
2395 /* And write back the 'adjusted' state */
2396 SetKeyboardState(keystate);
2399 /* Disable Auto repeat if required */
2400 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2403 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2406 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2408 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2409 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2410 if (cfg.ctrlaltkeys)
2411 keystate[VK_MENU] = 0;
2413 keystate[VK_RMENU] = 0x80;
2418 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2419 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2420 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2422 /* Note if AltGr was pressed and if it was used as a compose key */
2423 if (!compose_state) {
2424 compose_key = 0x100;
2425 if (cfg.compose_key) {
2426 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2427 compose_key = wParam;
2429 if (wParam == VK_APPS)
2430 compose_key = wParam;
2433 if (wParam == compose_key) {
2434 if (compose_state == 0
2435 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2437 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2441 } else if (compose_state == 1 && wParam != VK_CONTROL)
2445 * Record that we pressed key so the scroll window can be reset, but
2446 * be careful to avoid Shift-UP/Down
2448 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2452 /* Make sure we're not pasting */
2456 if (compose_state > 1 && left_alt)
2459 /* Sanitize the number pad if not using a PC NumPad */
2460 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2461 && cfg.funky_type != 2)
2462 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2463 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2467 nParam = VK_NUMPAD0;
2470 nParam = VK_NUMPAD1;
2473 nParam = VK_NUMPAD2;
2476 nParam = VK_NUMPAD3;
2479 nParam = VK_NUMPAD4;
2482 nParam = VK_NUMPAD5;
2485 nParam = VK_NUMPAD6;
2488 nParam = VK_NUMPAD7;
2491 nParam = VK_NUMPAD8;
2494 nParam = VK_NUMPAD9;
2497 nParam = VK_DECIMAL;
2501 if (keystate[VK_NUMLOCK] & 1)
2508 /* If a key is pressed and AltGr is not active */
2509 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2510 /* Okay, prepare for most alts then ... */
2514 /* Lets see if it's a pattern we know all about ... */
2515 if (wParam == VK_PRIOR && shift_state == 1) {
2516 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2519 if (wParam == VK_NEXT && shift_state == 1) {
2520 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2523 if (wParam == VK_INSERT && shift_state == 1) {
2527 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2530 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2532 PostMessage(hwnd, WM_CHAR, ' ', 0);
2533 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2536 /* Control-Numlock for app-keypad mode switch */
2537 if (wParam == VK_PAUSE && shift_state == 2) {
2538 app_keypad_keys ^= 1;
2542 /* Nethack keypad */
2543 if (cfg.nethack_keypad && !left_alt) {
2546 *p++ = shift_state ? 'B' : 'b';
2549 *p++ = shift_state ? 'J' : 'j';
2552 *p++ = shift_state ? 'N' : 'n';
2555 *p++ = shift_state ? 'H' : 'h';
2558 *p++ = shift_state ? '.' : '.';
2561 *p++ = shift_state ? 'L' : 'l';
2564 *p++ = shift_state ? 'Y' : 'y';
2567 *p++ = shift_state ? 'K' : 'k';
2570 *p++ = shift_state ? 'U' : 'u';
2575 /* Application Keypad */
2579 if (cfg.funky_type == 3 ||
2580 (cfg.funky_type <= 1 &&
2581 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
2595 if (app_keypad_keys && !cfg.no_applic_k)
2632 if (cfg.funky_type == 2) {
2637 } else if (shift_state)
2644 if (cfg.funky_type == 2)
2648 if (cfg.funky_type == 2)
2652 if (cfg.funky_type == 2)
2657 if (HIWORD(lParam) & KF_EXTENDED)
2663 if (xkey >= 'P' && xkey <= 'S')
2664 p += sprintf((char *) p, "\x1B%c", xkey);
2666 p += sprintf((char *) p, "\x1B?%c", xkey);
2668 p += sprintf((char *) p, "\x1BO%c", xkey);
2673 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
2674 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2678 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
2684 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
2688 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
2692 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
2697 if (wParam == VK_PAUSE) { /* Break/Pause */
2702 /* Control-2 to Control-8 are special */
2703 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
2704 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
2707 if (shift_state == 2 && wParam == 0xBD) {
2711 if (shift_state == 2 && wParam == 0xDF) {
2715 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2722 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2723 * for integer decimal nn.)
2725 * We also deal with the weird ones here. Linux VCs replace F1
2726 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2727 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2733 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
2736 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
2739 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
2742 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
2745 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
2748 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
2751 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
2754 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
2757 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
2760 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
2811 /* Reorder edit keys to physical order */
2812 if (cfg.funky_type == 3 && code <= 6)
2813 code = "\0\2\1\4\5\3\6"[code];
2815 if (vt52_mode && code > 0 && code <= 6) {
2816 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
2820 if (cfg.funky_type == 5 && /* SCO function keys */
2821 code >= 11 && code <= 34) {
2822 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
2825 case VK_F1: index = 0; break;
2826 case VK_F2: index = 1; break;
2827 case VK_F3: index = 2; break;
2828 case VK_F4: index = 3; break;
2829 case VK_F5: index = 4; break;
2830 case VK_F6: index = 5; break;
2831 case VK_F7: index = 6; break;
2832 case VK_F8: index = 7; break;
2833 case VK_F9: index = 8; break;
2834 case VK_F10: index = 9; break;
2835 case VK_F11: index = 10; break;
2836 case VK_F12: index = 11; break;
2838 if (keystate[VK_SHIFT] & 0x80) index += 12;
2839 if (keystate[VK_CONTROL] & 0x80) index += 24;
2840 p += sprintf((char *) p, "\x1B[%c", codes[index]);
2843 if (cfg.funky_type == 5 && /* SCO small keypad */
2844 code >= 1 && code <= 6) {
2845 char codes[] = "HL.FIG";
2849 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
2853 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
2860 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
2863 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
2866 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2867 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
2870 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2872 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
2874 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
2877 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2878 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
2882 p += sprintf((char *) p, "\x1B[%d~", code);
2887 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2888 * some reason seems to send VK_CLEAR to Windows...).
2911 p += sprintf((char *) p, "\x1B%c", xkey);
2913 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
2914 /* VT100 & VT102 manuals both state the app cursor keys
2915 * only work if the app keypad is on.
2917 if (!app_keypad_keys)
2919 /* Useful mapping of Ctrl-arrows */
2920 if (shift_state == 2)
2924 p += sprintf((char *) p, "\x1BO%c", xkey);
2926 p += sprintf((char *) p, "\x1B[%c", xkey);
2933 * Finally, deal with Return ourselves. (Win95 seems to
2934 * foul it up when Alt is pressed, for some reason.)
2936 if (wParam == VK_RETURN) { /* Return */
2942 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
2943 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
2948 /* Okay we've done everything interesting; let windows deal with
2949 * the boring stuff */
2951 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
2952 #ifdef SHOW_TOASCII_RESULT
2953 if (r == 1 && !key_down) {
2955 if (in_utf || dbcs_screenfont)
2956 debug((", (U+%04x)", alt_sum));
2958 debug((", LCH(%d)", alt_sum));
2960 debug((", ACH(%d)", keys[0]));
2965 for (r1 = 0; r1 < r; r1++) {
2966 debug(("%s%d", r1 ? "," : "", keys[r1]));
2974 for (i = 0; i < r; i++) {
2975 unsigned char ch = (unsigned char) keys[i];
2977 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
2982 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
2986 if ((nc = check_compose(compose_char, ch)) == -1) {
2987 MessageBeep(MB_ICONHAND);
2991 luni_send(&keybuf, 1);
2999 if (in_utf || dbcs_screenfont) {
3001 luni_send(&keybuf, 1);
3003 ch = (char) alt_sum;
3008 lpage_send(kbd_codepage, &ch, 1);
3010 static char cbuf[] = "\033 ";
3012 lpage_send(kbd_codepage, cbuf + !left_alt,
3017 /* This is so the ALT-Numpad and dead keys work correctly. */
3022 /* If we're definitly not building up an ALT-54321 then clear it */
3025 /* If we will be using alt_sum fix the 256s */
3026 else if (keys[0] && (in_utf || dbcs_screenfont))
3030 /* ALT alone may or may not want to bring up the System menu */
3031 if (wParam == VK_MENU) {
3033 if (message == WM_SYSKEYDOWN)
3035 else if (message == WM_SYSKEYUP && alt_state)
3036 PostMessage(hwnd, WM_CHAR, ' ', 0);
3037 if (message == WM_SYSKEYUP)
3047 void set_title(char *title)
3050 window_name = smalloc(1 + strlen(title));
3051 strcpy(window_name, title);
3052 if (cfg.win_name_always || !IsIconic(hwnd))
3053 SetWindowText(hwnd, title);
3056 void set_icon(char *title)
3059 icon_name = smalloc(1 + strlen(title));
3060 strcpy(icon_name, title);
3061 if (!cfg.win_name_always && IsIconic(hwnd))
3062 SetWindowText(hwnd, title);
3065 void set_sbar(int total, int start, int page)
3072 si.cbSize = sizeof(si);
3073 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3075 si.nMax = total - 1;
3079 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3082 Context get_ctx(void)
3088 SelectPalette(hdc, pal, FALSE);
3094 void free_ctx(Context ctx)
3096 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3097 ReleaseDC(hwnd, ctx);
3100 static void real_palette_set(int n, int r, int g, int b)
3103 logpal->palPalEntry[n].peRed = r;
3104 logpal->palPalEntry[n].peGreen = g;
3105 logpal->palPalEntry[n].peBlue = b;
3106 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3107 colours[n] = PALETTERGB(r, g, b);
3108 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3110 colours[n] = RGB(r, g, b);
3113 void palette_set(int n, int r, int g, int b)
3115 static const int first[21] = {
3116 0, 2, 4, 6, 8, 10, 12, 14,
3117 1, 3, 5, 7, 9, 11, 13, 15,
3120 real_palette_set(first[n], r, g, b);
3122 real_palette_set(first[n] + 1, r, g, b);
3124 HDC hdc = get_ctx();
3125 UnrealizeObject(pal);
3126 RealizePalette(hdc);
3131 void palette_reset(void)
3135 for (i = 0; i < NCOLOURS; i++) {
3137 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3138 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3139 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3140 logpal->palPalEntry[i].peFlags = 0;
3141 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3142 defpal[i].rgbtGreen,
3143 defpal[i].rgbtBlue);
3145 colours[i] = RGB(defpal[i].rgbtRed,
3146 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3151 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3153 RealizePalette(hdc);
3158 void write_aclip(char *data, int len, int must_deselect)
3163 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3166 lock = GlobalLock(clipdata);
3169 memcpy(lock, data, len);
3170 ((unsigned char *) lock)[len] = 0;
3171 GlobalUnlock(clipdata);
3174 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3176 if (OpenClipboard(hwnd)) {
3178 SetClipboardData(CF_TEXT, clipdata);
3181 GlobalFree(clipdata);
3184 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3188 * Note: unlike write_aclip() this will not append a nul.
3190 void write_clip(wchar_t * data, int len, int must_deselect)
3197 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3199 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3200 len * sizeof(wchar_t));
3201 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3203 if (!clipdata || !clipdata2) {
3205 GlobalFree(clipdata);
3207 GlobalFree(clipdata2);
3210 if (!(lock = GlobalLock(clipdata)))
3212 if (!(lock2 = GlobalLock(clipdata2)))
3215 memcpy(lock, data, len * sizeof(wchar_t));
3216 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3218 GlobalUnlock(clipdata);
3219 GlobalUnlock(clipdata2);
3222 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3224 if (OpenClipboard(hwnd)) {
3226 SetClipboardData(CF_UNICODETEXT, clipdata);
3227 SetClipboardData(CF_TEXT, clipdata2);
3230 GlobalFree(clipdata);
3231 GlobalFree(clipdata2);
3235 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3238 void get_clip(wchar_t ** p, int *len)
3240 static HGLOBAL clipdata = NULL;
3241 static wchar_t *converted = 0;
3250 GlobalUnlock(clipdata);
3253 } else if (OpenClipboard(NULL)) {
3254 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3256 *p = GlobalLock(clipdata);
3258 for (p2 = *p; *p2; p2++);
3262 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3266 s = GlobalLock(clipdata);
3267 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3268 *p = converted = smalloc(i * sizeof(wchar_t));
3269 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3282 * Move `lines' lines from position `from' to position `to' in the
3285 void optimised_move(int to, int from, int lines)
3290 min = (to < from ? to : from);
3291 max = to + from - min;
3294 r.right = cols * font_width;
3295 r.top = min * font_height;
3296 r.bottom = (max + lines) * font_height;
3297 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3302 * Print a message box and perform a fatal exit.
3304 void fatalbox(char *fmt, ...)
3310 vsprintf(stuff, fmt, ap);
3312 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3317 * Manage window caption / taskbar flashing, if enabled.
3318 * 0 = stop, 1 = maintain, 2 = start
3320 static void flash_window(int mode)
3322 static long last_flash = 0;
3323 static int flashing = 0;
3324 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3327 FlashWindow(hwnd, FALSE);
3331 } else if (mode == 2) {
3334 last_flash = GetTickCount();
3336 FlashWindow(hwnd, TRUE);
3339 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3342 long now = GetTickCount();
3343 long fdiff = now - last_flash;
3344 if (fdiff < 0 || fdiff > 450) {
3346 FlashWindow(hwnd, TRUE); /* toggle */
3357 if (mode == BELL_DEFAULT) {
3359 * For MessageBeep style bells, we want to be careful of
3360 * timing, because they don't have the nice property of
3361 * PlaySound bells that each one cancels the previous
3362 * active one. So we limit the rate to one per 50ms or so.
3364 static long lastbeep = 0;
3367 beepdiff = GetTickCount() - lastbeep;
3368 if (beepdiff >= 0 && beepdiff < 50)
3372 * The above MessageBeep call takes time, so we record the
3373 * time _after_ it finishes rather than before it starts.
3375 lastbeep = GetTickCount();
3376 } else if (mode == BELL_WAVEFILE) {
3377 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3378 char buf[sizeof(cfg.bell_wavefile) + 80];
3379 sprintf(buf, "Unable to play sound file\n%s\n"
3380 "Using default sound instead", cfg.bell_wavefile);
3381 MessageBox(hwnd, buf, "PuTTY Sound Error",
3382 MB_OK | MB_ICONEXCLAMATION);
3383 cfg.beep = BELL_DEFAULT;
3386 /* Otherwise, either visual bell or disabled; do nothing here */
3388 flash_window(2); /* start */