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);
81 static time_t last_movement = 0;
85 #define FONT_UNDERLINE 2
86 #define FONT_BOLDUND 3
87 #define FONT_WIDE 0x04
88 #define FONT_HIGH 0x08
89 #define FONT_NARROW 0x10
91 #define FONT_OEMBOLD 0x21
92 #define FONT_OEMUND 0x22
93 #define FONT_OEMBOLDUND 0x23
94 #define FONT_MSGOTHIC 0x40
95 #define FONT_MINGLIU 0x60
96 #define FONT_GULIMCHE 0x80
97 #define FONT_MAXNO 0x8F
99 static HFONT fonts[FONT_MAXNO];
100 static int fontflag[FONT_MAXNO];
102 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
110 static COLORREF colours[NCOLOURS];
112 static LPLOGPALETTE logpal;
113 static RGBTRIPLE defpal[NCOLOURS];
117 static HBITMAP caretbm;
119 static int dbltime, lasttime, lastact;
120 static Mouse_Button lastbtn;
122 /* this allows xterm-style mouse handling. */
123 static int send_raw_mouse = 0;
124 static int wheel_accumulator = 0;
126 static char *window_name, *icon_name;
128 static int compose_state = 0;
130 /* Dummy routine, only required in plink. */
131 void ldisc_update(int echo, int edit)
135 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
137 static char appname[] = "PuTTY";
142 int guess_width, guess_height;
145 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
147 winsock_ver = MAKEWORD(1, 1);
148 if (WSAStartup(winsock_ver, &wsadata)) {
149 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
150 MB_OK | MB_ICONEXCLAMATION);
153 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
154 MessageBox(NULL, "WinSock version is incompatible with 1.1",
155 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
159 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
162 InitCommonControls();
164 /* Ensure a Maximize setting in Explorer doesn't maximise the
169 * Process the command line.
174 default_protocol = DEFAULT_PROTOCOL;
175 default_port = DEFAULT_PORT;
176 cfg.logtype = LGTYP_NONE;
178 do_defaults(NULL, &cfg);
181 while (*p && isspace(*p))
185 * Process command line options first. Yes, this can be
186 * done better, and it will be as soon as I have the
190 char *q = p + strcspn(p, " \t");
193 tolower(p[0]) == 's' &&
194 tolower(p[1]) == 's' && tolower(p[2]) == 'h') {
195 default_protocol = cfg.protocol = PROT_SSH;
196 default_port = cfg.port = 22;
197 } else if (q == p + 7 &&
198 tolower(p[0]) == 'c' &&
199 tolower(p[1]) == 'l' &&
200 tolower(p[2]) == 'e' &&
201 tolower(p[3]) == 'a' &&
202 tolower(p[4]) == 'n' &&
203 tolower(p[5]) == 'u' && tolower(p[6]) == 'p') {
205 * `putty -cleanup'. Remove all registry entries
206 * associated with PuTTY, and also find and delete
207 * the random seed file.
210 "This procedure will remove ALL Registry\n"
211 "entries associated with PuTTY, and will\n"
212 "also remove the PuTTY random seed file.\n"
214 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
215 "SESSIONS. Are you really sure you want\n"
218 MB_YESNO | MB_ICONWARNING) == IDYES) {
223 p = q + strspn(q, " \t");
227 * An initial @ means to activate a saved session.
231 while (i > 1 && isspace(p[i - 1]))
234 do_defaults(p + 1, &cfg);
235 if (!*cfg.host && !do_config()) {
239 } else if (*p == '&') {
241 * An initial & means we've been given a command line
242 * containing the hex value of a HANDLE for a file
243 * mapping object, which we must then extract as a
248 if (sscanf(p + 1, "%p", &filemap) == 1 &&
249 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
250 0, 0, sizeof(Config))) != NULL) {
253 CloseHandle(filemap);
254 } else if (!do_config()) {
261 * If the hostname starts with "telnet:", set the
262 * protocol to Telnet and process the string as a
265 if (!strncmp(q, "telnet:", 7)) {
269 if (q[0] == '/' && q[1] == '/')
271 cfg.protocol = PROT_TELNET;
273 while (*p && *p != ':' && *p != '/')
282 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
283 cfg.host[sizeof(cfg.host) - 1] = '\0';
285 while (*p && !isspace(*p))
289 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
290 cfg.host[sizeof(cfg.host) - 1] = '\0';
291 while (*p && isspace(*p))
305 /* See if host is of the form user@host */
306 if (cfg.host[0] != '\0') {
307 char *atsign = strchr(cfg.host, '@');
308 /* Make sure we're not overflowing the user field */
310 if (atsign - cfg.host < sizeof cfg.username) {
311 strncpy(cfg.username, cfg.host, atsign - cfg.host);
312 cfg.username[atsign - cfg.host] = '\0';
314 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
319 * Trim a colon suffix off the hostname if it's there.
321 cfg.host[strcspn(cfg.host, ":")] = '\0';
325 * Select protocol. This is farmed out into a table in a
326 * separate file to enable an ssh-free variant.
331 for (i = 0; backends[i].backend != NULL; i++)
332 if (backends[i].protocol == cfg.protocol) {
333 back = backends[i].backend;
337 MessageBox(NULL, "Unsupported protocol number found",
338 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
344 /* Check for invalid Port number (i.e. zero) */
346 MessageBox(NULL, "Invalid Port Number",
347 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
354 wndclass.lpfnWndProc = WndProc;
355 wndclass.cbClsExtra = 0;
356 wndclass.cbWndExtra = 0;
357 wndclass.hInstance = inst;
358 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
359 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
360 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
361 wndclass.lpszMenuName = NULL;
362 wndclass.lpszClassName = appname;
364 RegisterClass(&wndclass);
369 savelines = cfg.savelines;
375 * Guess some defaults for the window size. This all gets
376 * updated later, so we don't really care too much. However, we
377 * do want the font width/height guesses to correspond to a
378 * large font rather than a small one...
385 term_size(cfg.height, cfg.width, cfg.savelines);
386 guess_width = extra_width + font_width * cols;
387 guess_height = extra_height + font_height * rows;
390 HWND w = GetDesktopWindow();
391 GetWindowRect(w, &r);
392 if (guess_width > r.right - r.left)
393 guess_width = r.right - r.left;
394 if (guess_height > r.bottom - r.top)
395 guess_height = r.bottom - r.top;
399 int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
402 winmode &= ~(WS_VSCROLL);
404 winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
406 exwinmode |= WS_EX_TOPMOST;
408 exwinmode |= WS_EX_CLIENTEDGE;
409 hwnd = CreateWindowEx(exwinmode, appname, appname,
410 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
411 guess_width, guess_height,
412 NULL, NULL, inst, NULL);
416 * Initialise the fonts, simultaneously correcting the guesses
417 * for font_{width,height}.
419 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
424 * Correct the guesses for extra_{width,height}.
428 GetWindowRect(hwnd, &wr);
429 GetClientRect(hwnd, &cr);
430 extra_width = wr.right - wr.left - cr.right + cr.left;
431 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
435 * Resize the window, now we know what size we _really_ want it
438 guess_width = extra_width + font_width * cols;
439 guess_height = extra_height + font_height * rows;
440 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
441 SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
442 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
445 * Set up a caret bitmap, with no content.
449 int size = (font_width + 15) / 16 * 2 * font_height;
450 bits = smalloc(size);
451 memset(bits, 0, size);
452 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
455 CreateCaret(hwnd, caretbm, font_width, font_height);
458 * Initialise the scroll bar.
463 si.cbSize = sizeof(si);
464 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
469 SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
473 * Start up the telnet connection.
477 char msg[1024], *title;
480 error = back->init(cfg.host, cfg.port, &realhost);
482 sprintf(msg, "Unable to open connection to\n"
483 "%.800s\n" "%s", cfg.host, error);
484 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
487 window_name = icon_name = NULL;
489 title = cfg.wintitle;
491 sprintf(msg, "%s - PuTTY", realhost);
499 session_closed = FALSE;
502 * Set up the input and output buffers.
505 outbuf_reap = outbuf_head = 0;
508 * Prepare the mouse handler.
510 lastact = MA_NOTHING;
511 lastbtn = MBT_NOTHING;
512 dbltime = GetDoubleClickTime();
515 * Set up the session-control options on the system menu.
518 HMENU m = GetSystemMenu(hwnd, FALSE);
522 AppendMenu(m, MF_SEPARATOR, 0, 0);
523 if (cfg.protocol == PROT_TELNET) {
525 AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
526 AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
527 AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
528 AppendMenu(p, MF_SEPARATOR, 0, 0);
529 AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
530 AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
531 AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
532 AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
533 AppendMenu(p, MF_SEPARATOR, 0, 0);
534 AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
535 AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
536 AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
537 AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
538 AppendMenu(p, MF_SEPARATOR, 0, 0);
539 AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
540 AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
541 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
543 AppendMenu(m, MF_SEPARATOR, 0, 0);
545 AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
546 AppendMenu(m, MF_SEPARATOR, 0, 0);
547 AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
548 AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
551 for (i = 1; i < ((nsessions < 256) ? nsessions : 256); i++)
552 AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
554 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
555 AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
556 AppendMenu(m, MF_SEPARATOR, 0, 0);
557 AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
558 AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
559 AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
560 AppendMenu(m, MF_SEPARATOR, 0, 0);
561 AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
565 * Finally show the window!
567 ShowWindow(hwnd, show);
570 * Open the initial log file if there is one.
575 * Set the palette up.
581 has_focus = (GetForegroundWindow() == hwnd);
584 if (GetMessage(&msg, NULL, 0, 0) == 1) {
585 int timer_id = 0, long_timer = 0;
587 while (msg.message != WM_QUIT) {
588 /* Sometimes DispatchMessage calls routines that use their own
589 * GetMessage loop, setup this timer so we get some control back.
591 * Also call term_update() from the timer so that if the host
592 * is sending data flat out we still do redraws.
594 if (timer_id && long_timer) {
595 KillTimer(hwnd, timer_id);
596 long_timer = timer_id = 0;
599 timer_id = SetTimer(hwnd, 1, 20, NULL);
600 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
601 DispatchMessage(&msg);
603 /* Make sure we blink everything that needs it. */
606 /* Send the paste buffer if there's anything to send */
609 /* If there's nothing new in the queue then we can do everything
610 * we've delayed, reading the socket, writing, and repainting
613 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
616 if (pending_netevent) {
617 enact_pending_netevent();
619 /* Force the cursor blink on */
622 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
626 /* Okay there is now nothing to do so we make sure the screen is
627 * completely up to date then tell windows to call us in a little
631 KillTimer(hwnd, timer_id);
640 /* Hmm, term_update didn't want to do an update too soon ... */
641 timer_id = SetTimer(hwnd, 1, 50, NULL);
643 timer_id = SetTimer(hwnd, 1, 2000, NULL);
645 timer_id = SetTimer(hwnd, 1, 100, NULL);
648 /* There's no point rescanning everything in the message queue
649 * so we do an apparently unnecessary wait here
652 if (GetMessage(&msg, NULL, 0, 0) != 1)
666 if (cfg.protocol == PROT_SSH) {
677 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
679 char *do_select(SOCKET skt, int startup)
684 events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE;
689 return "do_select(): internal error (hwnd==NULL)";
690 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
691 switch (WSAGetLastError()) {
693 return "Network is down";
695 return "WSAAsyncSelect(): unknown error";
702 * set or clear the "raw mouse message" mode
704 void set_raw_mouse_mode(int activate)
706 send_raw_mouse = activate;
707 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
711 * Print a message box and close the connection.
713 void connection_fatal(char *fmt, ...)
719 vsprintf(stuff, fmt, ap);
721 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
722 if (cfg.close_on_exit == COE_ALWAYS)
725 session_closed = TRUE;
726 SetWindowText(hwnd, "PuTTY (inactive)");
731 * Actually do the job requested by a WM_NETEVENT
733 static void enact_pending_netevent(void)
735 static int reentering = 0;
736 extern int select_result(WPARAM, LPARAM);
740 return; /* don't unpend the pending */
742 pending_netevent = FALSE;
745 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
748 if (ret == 0 && !session_closed) {
749 /* Abnormal exits will already have set session_closed and taken
750 * appropriate action. */
751 if (cfg.close_on_exit == COE_ALWAYS ||
752 cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
754 session_closed = TRUE;
755 SetWindowText(hwnd, "PuTTY (inactive)");
756 MessageBox(hwnd, "Connection closed by remote host",
757 "PuTTY", MB_OK | MB_ICONINFORMATION);
763 * Copy the colour palette from the configuration data into defpal.
764 * This is non-trivial because the colour indices are different.
766 static void cfgtopalette(void)
769 static const int ww[] = {
770 6, 7, 8, 9, 10, 11, 12, 13,
771 14, 15, 16, 17, 18, 19, 20, 21,
772 0, 1, 2, 3, 4, 4, 5, 5
775 for (i = 0; i < 24; i++) {
777 defpal[i].rgbtRed = cfg.colours[w][0];
778 defpal[i].rgbtGreen = cfg.colours[w][1];
779 defpal[i].rgbtBlue = cfg.colours[w][2];
784 * Set up the colour palette.
786 static void init_palette(void)
789 HDC hdc = GetDC(hwnd);
791 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
792 logpal = smalloc(sizeof(*logpal)
793 - sizeof(logpal->palPalEntry)
794 + NCOLOURS * sizeof(PALETTEENTRY));
795 logpal->palVersion = 0x300;
796 logpal->palNumEntries = NCOLOURS;
797 for (i = 0; i < NCOLOURS; i++) {
798 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
799 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
800 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
801 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
803 pal = CreatePalette(logpal);
805 SelectPalette(hdc, pal, FALSE);
807 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
810 ReleaseDC(hwnd, hdc);
813 for (i = 0; i < NCOLOURS; i++)
814 colours[i] = PALETTERGB(defpal[i].rgbtRed,
818 for (i = 0; i < NCOLOURS; i++)
819 colours[i] = RGB(defpal[i].rgbtRed,
820 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
824 * Initialise all the fonts we will need initially. There may be as many as
825 * three or as few as one. The other (poentially) twentyone fonts are done
826 * if/when they are needed.
830 * - check the font width and height, correcting our guesses if
833 * - verify that the bold font is the same width as the ordinary
834 * one, and engage shadow bolding if not.
836 * - verify that the underlined font is the same width as the
837 * ordinary one (manual underlining by means of line drawing can
838 * be done in a pinch).
840 static void init_fonts(int pick_width)
847 int fw_dontcare, fw_bold;
849 for (i = 0; i < FONT_MAXNO; i++)
852 if (cfg.fontisbold) {
853 fw_dontcare = FW_BOLD;
856 fw_dontcare = FW_DONTCARE;
862 font_height = cfg.fontheight;
863 if (font_height > 0) {
865 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
867 font_width = pick_width;
870 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
871 c, OUT_DEFAULT_PRECIS, \
872 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
873 FIXED_PITCH | FF_DONTCARE, cfg.font)
875 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
877 SelectObject(hdc, fonts[FONT_NORMAL]);
878 GetTextMetrics(hdc, &tm);
879 font_height = tm.tmHeight;
880 font_width = tm.tmAveCharWidth;
884 DWORD cset = tm.tmCharSet;
885 memset(&info, 0xFF, sizeof(info));
887 /* !!! Yes the next line is right */
888 if (cset == OEM_CHARSET)
889 font_codepage = GetOEMCP();
891 if (TranslateCharsetInfo
892 ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
897 GetCPInfo(font_codepage, &cpinfo);
898 dbcs_screenfont = (cpinfo.MaxCharSize > 1);
901 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
904 * Some fonts, e.g. 9-pt Courier, draw their underlines
905 * outside their character cell. We successfully prevent
906 * screen corruption by clipping the text output, but then
907 * we lose the underline completely. Here we try to work
908 * out whether this is such a font, and if it is, we set a
909 * flag that causes underlines to be drawn by hand.
911 * Having tried other more sophisticated approaches (such
912 * as examining the TEXTMETRIC structure or requesting the
913 * height of a string), I think we'll do this the brute
914 * force way: we create a small bitmap, draw an underlined
915 * space on it, and test to see whether any pixels are
916 * foreground-coloured. (Since we expect the underline to
917 * go all the way across the character cell, we only search
918 * down a single column of the bitmap, half way across.)
922 HBITMAP und_bm, und_oldbm;
926 und_dc = CreateCompatibleDC(hdc);
927 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
928 und_oldbm = SelectObject(und_dc, und_bm);
929 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
930 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
931 SetTextColor(und_dc, RGB(255, 255, 255));
932 SetBkColor(und_dc, RGB(0, 0, 0));
933 SetBkMode(und_dc, OPAQUE);
934 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
936 for (i = 0; i < font_height; i++) {
937 c = GetPixel(und_dc, font_width / 2, i);
938 if (c != RGB(0, 0, 0))
941 SelectObject(und_dc, und_oldbm);
942 DeleteObject(und_bm);
946 DeleteObject(fonts[FONT_UNDERLINE]);
947 fonts[FONT_UNDERLINE] = 0;
951 if (bold_mode == BOLD_FONT) {
952 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
956 descent = tm.tmAscent + 1;
957 if (descent >= font_height)
958 descent = font_height - 1;
960 for (i = 0; i < 3; i++) {
962 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
963 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
970 ReleaseDC(hwnd, hdc);
972 if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
974 DeleteObject(fonts[FONT_UNDERLINE]);
975 fonts[FONT_UNDERLINE] = 0;
978 if (bold_mode == BOLD_FONT &&
979 fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
980 bold_mode = BOLD_SHADOW;
981 DeleteObject(fonts[FONT_BOLD]);
982 fonts[FONT_BOLD] = 0;
984 fontflag[0] = fontflag[1] = fontflag[2] = 1;
989 static void another_font(int fontno)
992 int fw_dontcare, fw_bold;
996 if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
999 basefont = (fontno & ~(FONT_BOLDUND));
1000 if (basefont != fontno && !fontflag[basefont])
1001 another_font(basefont);
1003 if (cfg.fontisbold) {
1004 fw_dontcare = FW_BOLD;
1007 fw_dontcare = FW_DONTCARE;
1011 c = cfg.fontcharset;
1017 if (fontno & FONT_WIDE)
1019 if (fontno & FONT_NARROW)
1021 if (fontno & FONT_OEM)
1023 if (fontno & FONT_BOLD)
1025 if (fontno & FONT_UNDERLINE)
1029 CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1030 FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1031 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1032 FIXED_PITCH | FF_DONTCARE, s);
1034 fontflag[fontno] = 1;
1037 static void deinit_fonts(void)
1040 for (i = 0; i < FONT_MAXNO; i++) {
1042 DeleteObject(fonts[i]);
1048 void request_resize(int w, int h, int refont)
1052 /* If the window is maximized supress resizing attempts */
1056 if (refont && w != cols && (cols == 80 || cols == 132)) {
1057 /* If font width too big for screen should we shrink the font more ? */
1059 font_width = ((font_width * cols + w / 2) / w);
1063 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1064 und_mode = UND_FONT;
1065 init_fonts(font_width);
1067 static int first_time = 1;
1070 switch (first_time) {
1072 /* Get the size of the screen */
1073 if (GetClientRect(GetDesktopWindow(), &ss))
1074 /* first_time = 0 */ ;
1080 /* Make sure the values are sane */
1081 width = (ss.right - ss.left - extra_width) / font_width;
1082 height = (ss.bottom - ss.top - extra_height) / font_height;
1095 width = extra_width + font_width * w;
1096 height = extra_height + font_height * h;
1098 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1099 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1100 SWP_NOMOVE | SWP_NOZORDER);
1103 static void click(Mouse_Button b, int x, int y, int shift, int ctrl)
1105 int thistime = GetMessageTime();
1107 if (send_raw_mouse) {
1108 term_mouse(b, MA_CLICK, x, y, shift, ctrl);
1112 if (lastbtn == b && thistime - lasttime < dbltime) {
1113 lastact = (lastact == MA_CLICK ? MA_2CLK :
1114 lastact == MA_2CLK ? MA_3CLK :
1115 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1120 if (lastact != MA_NOTHING)
1121 term_mouse(b, lastact, x, y, shift, ctrl);
1122 lasttime = thistime;
1126 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1127 * into a cooked one (SELECT, EXTEND, PASTE).
1129 Mouse_Button translate_button(Mouse_Button button)
1131 if (button == MBT_LEFT)
1133 if (button == MBT_MIDDLE)
1134 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1135 if (button == MBT_RIGHT)
1136 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1139 static void show_mouseptr(int show)
1141 static int cursor_visible = 1;
1142 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1144 if (cursor_visible && !show)
1146 else if (!cursor_visible && show)
1148 cursor_visible = show;
1151 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1152 WPARAM wParam, LPARAM lParam)
1155 static int ignore_size = FALSE;
1156 static int ignore_clip = FALSE;
1157 static int just_reconfigged = FALSE;
1158 static int resizing = FALSE;
1159 static int need_backend_resize = FALSE;
1160 static int defered_resize = FALSE;
1164 if (pending_netevent)
1165 enact_pending_netevent();
1172 if (cfg.ping_interval > 0) {
1175 if (now - last_movement > cfg.ping_interval) {
1176 back->special(TS_PING);
1177 last_movement = now;
1185 if (!cfg.warn_on_close || session_closed ||
1187 "Are you sure you want to close this session?",
1188 "PuTTY Exit Confirmation",
1189 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1190 DestroyWindow(hwnd);
1197 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1209 PROCESS_INFORMATION pi;
1210 HANDLE filemap = NULL;
1212 if (wParam == IDM_DUPSESS) {
1214 * Allocate a file-mapping memory chunk for the
1217 SECURITY_ATTRIBUTES sa;
1220 sa.nLength = sizeof(sa);
1221 sa.lpSecurityDescriptor = NULL;
1222 sa.bInheritHandle = TRUE;
1223 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1226 0, sizeof(Config), NULL);
1228 p = (Config *) MapViewOfFile(filemap,
1230 0, 0, sizeof(Config));
1232 *p = cfg; /* structure copy */
1236 sprintf(c, "putty &%p", filemap);
1238 } else if (wParam == IDM_SAVEDSESS) {
1240 sessions[(lParam - IDM_SAVED_MIN) / 16];
1241 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1243 cl = NULL; /* not a very important failure mode */
1245 sprintf(cl, "putty @%s", session);
1251 GetModuleFileName(NULL, b, sizeof(b) - 1);
1253 si.lpReserved = NULL;
1254 si.lpDesktop = NULL;
1258 si.lpReserved2 = NULL;
1259 CreateProcess(b, cl, NULL, NULL, TRUE,
1260 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1263 CloseHandle(filemap);
1270 int prev_alwaysontop = cfg.alwaysontop;
1271 int prev_sunken_edge = cfg.sunken_edge;
1272 char oldlogfile[FILENAME_MAX];
1274 int need_setwpos = FALSE;
1275 int old_fwidth, old_fheight;
1277 strcpy(oldlogfile, cfg.logfilename);
1278 oldlogtype = cfg.logtype;
1279 old_fwidth = font_width;
1280 old_fheight = font_height;
1281 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1283 if (!do_reconfig(hwnd))
1286 if (strcmp(oldlogfile, cfg.logfilename) ||
1287 oldlogtype != cfg.logtype) {
1288 logfclose(); /* reset logging */
1292 just_reconfigged = TRUE;
1294 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1295 und_mode = UND_FONT;
1299 * Flush the line discipline's edit buffer in the
1300 * case where local editing has just been disabled.
1302 ldisc_send(NULL, 0);
1310 /* Enable or disable the scroll bar, etc */
1312 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1313 LONG nexflag, exflag =
1314 GetWindowLong(hwnd, GWL_EXSTYLE);
1317 if (cfg.alwaysontop != prev_alwaysontop) {
1318 if (cfg.alwaysontop) {
1319 nexflag |= WS_EX_TOPMOST;
1320 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1321 SWP_NOMOVE | SWP_NOSIZE);
1323 nexflag &= ~(WS_EX_TOPMOST);
1324 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1325 SWP_NOMOVE | SWP_NOSIZE);
1328 if (cfg.sunken_edge)
1329 nexflag |= WS_EX_CLIENTEDGE;
1331 nexflag &= ~(WS_EX_CLIENTEDGE);
1337 nflg &= ~WS_VSCROLL;
1339 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1341 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1343 if (nflg != flag || nexflag != exflag) {
1347 SetWindowLong(hwnd, GWL_STYLE, nflg);
1348 if (nexflag != exflag)
1349 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1351 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1353 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1354 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1355 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER
1356 | SWP_FRAMECHANGED);
1358 GetWindowRect(hwnd, &wr);
1359 GetClientRect(hwnd, &cr);
1361 wr.right - wr.left - cr.right + cr.left;
1363 wr.bottom - wr.top - cr.bottom + cr.top;
1364 need_setwpos = TRUE;
1368 if (cfg.height != rows ||
1369 cfg.width != cols ||
1370 old_fwidth != font_width ||
1371 old_fheight != font_height ||
1372 cfg.savelines != savelines ||
1373 cfg.sunken_edge != prev_sunken_edge)
1374 need_setwpos = TRUE;
1376 if (IsZoomed(hwnd)) {
1380 defered_resize = TRUE;
1382 GetClientRect(hwnd, &cr);
1383 w = cr.right - cr.left;
1384 h = cr.bottom - cr.top;
1388 h = h / font_height;
1392 term_size(h, w, cfg.savelines);
1393 InvalidateRect(hwnd, NULL, TRUE);
1396 term_size(cfg.height, cfg.width, cfg.savelines);
1397 InvalidateRect(hwnd, NULL, TRUE);
1399 SetWindowPos(hwnd, NULL, 0, 0,
1400 extra_width + font_width * cfg.width,
1402 font_height * cfg.height,
1403 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1404 SWP_NOMOVE | SWP_NOZORDER);
1408 if (cfg.locksize && IsZoomed(hwnd))
1410 set_title(cfg.wintitle);
1411 if (IsIconic(hwnd)) {
1413 cfg.win_name_always ? window_name :
1428 back->special(TS_AYT);
1431 back->special(TS_BRK);
1434 back->special(TS_SYNCH);
1437 back->special(TS_EC);
1440 back->special(TS_EL);
1443 back->special(TS_GA);
1446 back->special(TS_NOP);
1449 back->special(TS_ABORT);
1452 back->special(TS_AO);
1455 back->special(TS_IP);
1458 back->special(TS_SUSP);
1461 back->special(TS_EOR);
1464 back->special(TS_EOF);
1470 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1471 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1476 #define X_POS(l) ((int)(short)LOWORD(l))
1477 #define Y_POS(l) ((int)(short)HIWORD(l))
1479 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1480 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1481 #define WHEEL_DELTA 120
1484 wheel_accumulator += (short) HIWORD(wParam);
1485 wParam = LOWORD(wParam);
1487 /* process events when the threshold is reached */
1488 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1491 /* reduce amount for next time */
1492 if (wheel_accumulator > 0) {
1494 wheel_accumulator -= WHEEL_DELTA;
1495 } else if (wheel_accumulator < 0) {
1497 wheel_accumulator += WHEEL_DELTA;
1501 if (send_raw_mouse) {
1502 /* send a mouse-down followed by a mouse up */
1505 TO_CHR_X(X_POS(lParam)),
1506 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1507 wParam & MK_CONTROL);
1508 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1509 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1510 wParam & MK_CONTROL);
1512 /* trigger a scroll */
1514 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1519 case WM_LBUTTONDOWN:
1520 case WM_MBUTTONDOWN:
1521 case WM_RBUTTONDOWN:
1528 case WM_LBUTTONDOWN:
1532 case WM_MBUTTONDOWN:
1533 button = MBT_MIDDLE;
1536 case WM_RBUTTONDOWN:
1545 button = MBT_MIDDLE;
1556 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1557 wParam & MK_SHIFT, wParam & MK_CONTROL);
1560 term_mouse(button, MA_RELEASE,
1561 TO_CHR_X(X_POS(lParam)),
1562 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1563 wParam & MK_CONTROL);
1571 * Add the mouse position and message time to the random
1574 noise_ultralight(lParam);
1576 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1578 if (wParam & MK_LBUTTON)
1580 else if (wParam & MK_MBUTTON)
1581 b = cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1583 b = cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1584 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1585 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1586 wParam & MK_CONTROL);
1589 case WM_NCMOUSEMOVE:
1591 noise_ultralight(lParam);
1593 case WM_IGNORE_CLIP:
1594 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1596 case WM_DESTROYCLIPBOARD:
1599 ignore_clip = FALSE;
1605 hdc = BeginPaint(hwnd, &p);
1607 SelectPalette(hdc, pal, TRUE);
1608 RealizePalette(hdc);
1610 term_paint(hdc, p.rcPaint.left, p.rcPaint.top,
1611 p.rcPaint.right, p.rcPaint.bottom);
1612 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1613 SelectObject(hdc, GetStockObject(WHITE_PEN));
1619 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1620 * but the only one that's likely to try to overload us is FD_READ.
1621 * This means buffering just one is fine.
1623 if (pending_netevent)
1624 enact_pending_netevent();
1626 pending_netevent = TRUE;
1627 pend_netevent_wParam = wParam;
1628 pend_netevent_lParam = lParam;
1629 time(&last_movement);
1633 CreateCaret(hwnd, caretbm, font_width, font_height);
1646 case WM_IGNORE_SIZE:
1647 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1649 case WM_ENTERSIZEMOVE:
1652 need_backend_resize = FALSE;
1654 case WM_EXITSIZEMOVE:
1657 if (need_backend_resize)
1662 int width, height, w, h, ew, eh;
1663 LPRECT r = (LPRECT) lParam;
1665 width = r->right - r->left - extra_width;
1666 height = r->bottom - r->top - extra_height;
1667 w = (width + font_width / 2) / font_width;
1670 h = (height + font_height / 2) / font_height;
1673 UpdateSizeTip(hwnd, w, h);
1674 ew = width - w * font_width;
1675 eh = height - h * font_height;
1677 if (wParam == WMSZ_LEFT ||
1678 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1684 if (wParam == WMSZ_TOP ||
1685 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1695 /* break; (never reached) */
1697 if (wParam == SIZE_MINIMIZED) {
1699 cfg.win_name_always ? window_name : icon_name);
1702 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1703 SetWindowText(hwnd, window_name);
1705 int width, height, w, h;
1706 #if 0 /* we have fixed this using WM_SIZING now */
1710 width = LOWORD(lParam);
1711 height = HIWORD(lParam);
1712 w = width / font_width;
1715 h = height / font_height;
1718 #if 0 /* we have fixed this using WM_SIZING now */
1719 ew = width - w * font_width;
1720 eh = height - h * font_height;
1721 if (ew != 0 || eh != 0) {
1723 GetWindowRect(hwnd, &r);
1724 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1725 SetWindowPos(hwnd, NULL, 0, 0,
1726 r.right - r.left - ew, r.bottom - r.top - eh,
1727 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1730 if (w != cols || h != rows || just_reconfigged) {
1732 term_size(h, w, cfg.savelines);
1734 * Don't call back->size in mid-resize. (To prevent
1735 * massive numbers of resize events getting sent
1736 * down the connection during an NT opaque drag.)
1741 need_backend_resize = TRUE;
1745 just_reconfigged = FALSE;
1748 if (wParam == SIZE_RESTORED && defered_resize) {
1749 defered_resize = FALSE;
1750 SetWindowPos(hwnd, NULL, 0, 0,
1751 extra_width + font_width * cfg.width,
1752 extra_height + font_height * cfg.height,
1753 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1754 SWP_NOMOVE | SWP_NOZORDER);
1756 ignore_size = FALSE;
1759 switch (LOWORD(wParam)) {
1773 term_scroll(0, +rows / 2);
1776 term_scroll(0, -rows / 2);
1778 case SB_THUMBPOSITION:
1780 term_scroll(1, HIWORD(wParam));
1784 case WM_PALETTECHANGED:
1785 if ((HWND) wParam != hwnd && pal != NULL) {
1786 HDC hdc = get_ctx();
1788 if (RealizePalette(hdc) > 0)
1794 case WM_QUERYNEWPALETTE:
1796 HDC hdc = get_ctx();
1798 if (RealizePalette(hdc) > 0)
1810 * Add the scan code and keypress timing to the random
1813 noise_ultralight(lParam);
1816 * We don't do TranslateMessage since it disassociates the
1817 * resulting CHAR message from the KEYDOWN that sparked it,
1818 * which we occasionally don't want. Instead, we process
1819 * KEYDOWN, and call the Win32 translator functions so that
1820 * we get the translations under _our_ control.
1823 unsigned char buf[20];
1826 if (wParam == VK_PROCESSKEY) {
1829 m.message = WM_KEYDOWN;
1831 m.lParam = lParam & 0xdfff;
1832 TranslateMessage(&m);
1834 len = TranslateKey(message, wParam, lParam, buf);
1836 return DefWindowProc(hwnd, message, wParam, lParam);
1837 ldisc_send(buf, len);
1844 case WM_INPUTLANGCHANGE:
1846 /* wParam == Font number */
1847 /* lParam == Locale */
1849 HKL NewInputLocale = (HKL) lParam;
1851 // lParam == GetKeyboardLayout(0);
1853 GetLocaleInfo(LOWORD(NewInputLocale),
1854 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
1856 kbd_codepage = atoi(lbuf);
1860 if (wParam & 0xFF00) {
1861 unsigned char buf[2];
1864 buf[0] = wParam >> 8;
1865 lpage_send(kbd_codepage, buf, 2);
1867 char c = (unsigned char) wParam;
1868 lpage_send(kbd_codepage, &c, 1);
1874 * Nevertheless, we are prepared to deal with WM_CHAR
1875 * messages, should they crop up. So if someone wants to
1876 * post the things to us as part of a macro manoeuvre,
1877 * we're ready to cope.
1880 char c = (unsigned char)wParam;
1881 lpage_send(CP_ACP, &c, 1);
1885 if (send_raw_mouse) {
1886 SetCursor(LoadCursor(NULL, IDC_ARROW));
1891 return DefWindowProc(hwnd, message, wParam, lParam);
1895 * Move the system caret. (We maintain one, even though it's
1896 * invisible, for the benefit of blind people: apparently some
1897 * helper software tracks the system caret, so we should arrange to
1900 void sys_cursor(int x, int y)
1903 SetCaretPos(x * font_width, y * font_height);
1907 * Draw a line of text in the window, at given character
1908 * coordinates, in given attributes.
1910 * We are allowed to fiddle with the contents of `text'.
1912 void do_text(Context ctx, int x, int y, char *text, int len,
1913 unsigned long attr, int lattr)
1916 int nfg, nbg, nfont;
1919 int force_manual_underline = 0;
1920 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
1921 int char_width = fnt_width;
1922 int text_adjust = 0;
1923 static int *IpDx = 0, IpDxLEN = 0;
1925 if (attr & ATTR_WIDE)
1928 if (len > IpDxLEN || IpDx[0] != char_width) {
1930 if (len > IpDxLEN) {
1932 IpDx = smalloc((len + 16) * sizeof(int));
1933 IpDxLEN = (len + 16);
1935 for (i = 0; i < IpDxLEN; i++)
1936 IpDx[i] = char_width;
1942 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
1943 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
1944 attr ^= ATTR_CUR_XOR;
1948 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
1949 /* Assume a poorman font is borken in other ways too. */
1959 nfont |= FONT_WIDE + FONT_HIGH;
1963 /* Special hack for the VT100 linedraw glyphs. */
1964 if ((attr & CSET_MASK) == 0x2300) {
1965 if (!dbcs_screenfont &&
1966 text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
1967 switch ((unsigned char) (text[0])) {
1969 text_adjust = -2 * font_height / 5;
1972 text_adjust = -1 * font_height / 5;
1975 text_adjust = font_height / 5;
1978 text_adjust = 2 * font_height / 5;
1981 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
1984 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
1985 attr |= (unitab_xterm['q'] & CSET_MASK);
1986 if (attr & ATTR_UNDER) {
1987 attr &= ~ATTR_UNDER;
1988 force_manual_underline = 1;
1993 /* Anything left as an original character set is unprintable. */
1994 if (DIRECT_CHAR(attr)) {
1997 memset(text, 0xFF, len);
2001 if ((attr & CSET_MASK) == ATTR_OEMCP)
2004 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2005 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2006 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2008 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2009 nfont |= FONT_UNDERLINE;
2010 another_font(nfont);
2011 if (!fonts[nfont]) {
2012 if (nfont & FONT_UNDERLINE)
2013 force_manual_underline = 1;
2014 /* Don't do the same for manual bold, it could be bad news. */
2016 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2018 another_font(nfont);
2020 nfont = FONT_NORMAL;
2021 if (attr & ATTR_REVERSE) {
2026 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2028 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2032 SelectObject(hdc, fonts[nfont]);
2033 SetTextColor(hdc, fg);
2034 SetBkColor(hdc, bg);
2035 SetBkMode(hdc, OPAQUE);
2038 line_box.right = x + char_width * len;
2039 line_box.bottom = y + font_height;
2041 /* We're using a private area for direct to font. (512 chars.) */
2042 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2043 /* Ho Hum, dbcs fonts are a PITA! */
2044 /* To display on W9x I have to convert to UCS */
2045 static wchar_t *uni_buf = 0;
2046 static int uni_len = 0;
2048 if (len > uni_len) {
2050 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2052 nlen = MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2053 text, len, uni_buf, uni_len);
2059 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2060 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, 0);
2061 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2062 SetBkMode(hdc, TRANSPARENT);
2063 ExtTextOutW(hdc, x - 1,
2064 y - font_height * (lattr ==
2065 LATTR_BOT) + text_adjust,
2066 ETO_CLIPPED, &line_box, uni_buf, nlen, 0);
2068 } else if (DIRECT_FONT(attr)) {
2070 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2071 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2072 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2073 SetBkMode(hdc, TRANSPARENT);
2075 /* GRR: This draws the character outside it's box and can leave
2076 * 'droppings' even with the clip box! I suppose I could loop it
2077 * one character at a time ... yuk.
2079 * Or ... I could do a test print with "W", and use +1 or -1 for this
2080 * shift depending on if the leftmost column is blank...
2082 ExtTextOut(hdc, x - 1,
2083 y - font_height * (lattr ==
2084 LATTR_BOT) + text_adjust,
2085 ETO_CLIPPED, &line_box, text, len, IpDx);
2088 /* And 'normal' unicode characters */
2089 static WCHAR *wbuf = NULL;
2090 static int wlen = 0;
2095 wbuf = smalloc(wlen * sizeof(WCHAR));
2097 for (i = 0; i < len; i++)
2098 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2101 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2102 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2104 /* And the shadow bold hack. */
2105 if (bold_mode == BOLD_SHADOW) {
2106 SetBkMode(hdc, TRANSPARENT);
2107 ExtTextOutW(hdc, x - 1,
2108 y - font_height * (lattr ==
2109 LATTR_BOT) + text_adjust,
2110 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2113 if (lattr != LATTR_TOP && (force_manual_underline ||
2114 (und_mode == UND_LINE
2115 && (attr & ATTR_UNDER)))) {
2118 if (lattr == LATTR_BOT)
2119 dec = dec * 2 - font_height;
2121 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2122 MoveToEx(hdc, x, y + dec, NULL);
2123 LineTo(hdc, x + len * char_width, y + dec);
2124 oldpen = SelectObject(hdc, oldpen);
2125 DeleteObject(oldpen);
2129 void do_cursor(Context ctx, int x, int y, char *text, int len,
2130 unsigned long attr, int lattr)
2136 int ctype = cfg.cursor_type;
2138 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2139 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2140 do_text(ctx, x, y, text, len, attr, lattr);
2144 attr |= TATTR_RIGHTCURS;
2147 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2148 if (attr & ATTR_WIDE)
2153 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2156 pts[0].x = pts[1].x = pts[4].x = x;
2157 pts[2].x = pts[3].x = x + char_width - 1;
2158 pts[0].y = pts[3].y = pts[4].y = y;
2159 pts[1].y = pts[2].y = y + font_height - 1;
2160 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2161 Polyline(hdc, pts, 5);
2162 oldpen = SelectObject(hdc, oldpen);
2163 DeleteObject(oldpen);
2164 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2165 int startx, starty, dx, dy, length, i;
2168 starty = y + descent;
2171 length = char_width;
2174 if (attr & TATTR_RIGHTCURS)
2175 xadjust = char_width - 1;
2176 startx = x + xadjust;
2180 length = font_height;
2182 if (attr & TATTR_ACTCURS) {
2185 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2186 MoveToEx(hdc, startx, starty, NULL);
2187 LineTo(hdc, startx + dx * length, starty + dy * length);
2188 oldpen = SelectObject(hdc, oldpen);
2189 DeleteObject(oldpen);
2191 for (i = 0; i < length; i++) {
2193 SetPixel(hdc, startx, starty, colours[23]);
2203 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2204 * codes. Returns number of bytes used or zero to drop the message
2205 * or -1 to forward the message to windows.
2207 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2208 unsigned char *output)
2211 int scan, left_alt = 0, key_down, shift_state;
2213 unsigned char *p = output;
2214 static int alt_state = 0;
2215 static int alt_sum = 0;
2217 HKL kbd_layout = GetKeyboardLayout(0);
2219 static WORD keys[3];
2220 static int compose_char = 0;
2221 static WPARAM compose_key = 0;
2223 r = GetKeyboardState(keystate);
2225 memset(keystate, 0, sizeof(keystate));
2228 #define SHOW_TOASCII_RESULT
2229 { /* Tell us all about key events */
2230 static BYTE oldstate[256];
2231 static int first = 1;
2235 memcpy(oldstate, keystate, sizeof(oldstate));
2238 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2240 } else if ((HIWORD(lParam) & KF_UP)
2241 && scan == (HIWORD(lParam) & 0xFF)) {
2245 if (wParam >= VK_F1 && wParam <= VK_F20)
2246 debug(("K_F%d", wParam + 1 - VK_F1));
2259 debug(("VK_%02x", wParam));
2261 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2263 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2265 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2266 if (ch >= ' ' && ch <= '~')
2267 debug((", '%c'", ch));
2269 debug((", $%02x", ch));
2272 debug((", KB0=%02x", keys[0]));
2274 debug((", KB1=%02x", keys[1]));
2276 debug((", KB2=%02x", keys[2]));
2278 if ((keystate[VK_SHIFT] & 0x80) != 0)
2280 if ((keystate[VK_CONTROL] & 0x80) != 0)
2282 if ((HIWORD(lParam) & KF_EXTENDED))
2284 if ((HIWORD(lParam) & KF_UP))
2288 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2289 else if ((HIWORD(lParam) & KF_UP))
2290 oldstate[wParam & 0xFF] ^= 0x80;
2292 oldstate[wParam & 0xFF] ^= 0x81;
2294 for (ch = 0; ch < 256; ch++)
2295 if (oldstate[ch] != keystate[ch])
2296 debug((", M%02x=%02x", ch, keystate[ch]));
2298 memcpy(oldstate, keystate, sizeof(oldstate));
2302 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2303 keystate[VK_RMENU] = keystate[VK_MENU];
2307 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2308 if ((cfg.funky_type == 3 ||
2309 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2310 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2312 wParam = VK_EXECUTE;
2314 /* UnToggle NUMLock */
2315 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2316 keystate[VK_NUMLOCK] ^= 1;
2319 /* And write back the 'adjusted' state */
2320 SetKeyboardState(keystate);
2323 /* Disable Auto repeat if required */
2324 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2327 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2330 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2332 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2333 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2334 if (cfg.ctrlaltkeys)
2335 keystate[VK_MENU] = 0;
2337 keystate[VK_RMENU] = 0x80;
2342 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2343 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2344 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2346 /* Note if AltGr was pressed and if it was used as a compose key */
2347 if (!compose_state) {
2348 compose_key = 0x100;
2349 if (cfg.compose_key) {
2350 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2351 compose_key = wParam;
2353 if (wParam == VK_APPS)
2354 compose_key = wParam;
2357 if (wParam == compose_key) {
2358 if (compose_state == 0
2359 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2361 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2365 } else if (compose_state == 1 && wParam != VK_CONTROL)
2369 * Record that we pressed key so the scroll window can be reset, but
2370 * be careful to avoid Shift-UP/Down
2372 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2376 /* Make sure we're not pasting */
2380 if (compose_state > 1 && left_alt)
2383 /* Sanitize the number pad if not using a PC NumPad */
2384 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2385 && cfg.funky_type != 2)
2386 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2387 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2391 nParam = VK_NUMPAD0;
2394 nParam = VK_NUMPAD1;
2397 nParam = VK_NUMPAD2;
2400 nParam = VK_NUMPAD3;
2403 nParam = VK_NUMPAD4;
2406 nParam = VK_NUMPAD5;
2409 nParam = VK_NUMPAD6;
2412 nParam = VK_NUMPAD7;
2415 nParam = VK_NUMPAD8;
2418 nParam = VK_NUMPAD9;
2421 nParam = VK_DECIMAL;
2425 if (keystate[VK_NUMLOCK] & 1)
2432 /* If a key is pressed and AltGr is not active */
2433 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2434 /* Okay, prepare for most alts then ... */
2438 /* Lets see if it's a pattern we know all about ... */
2439 if (wParam == VK_PRIOR && shift_state == 1) {
2440 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2443 if (wParam == VK_NEXT && shift_state == 1) {
2444 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2447 if (wParam == VK_INSERT && shift_state == 1) {
2448 term_mouse(MBT_PASTE, MA_CLICK, 0, 0, 0, 0);
2449 term_mouse(MBT_PASTE, MA_RELEASE, 0, 0, 0, 0);
2452 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2455 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2457 PostMessage(hwnd, WM_CHAR, ' ', 0);
2458 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2461 /* Control-Numlock for app-keypad mode switch */
2462 if (wParam == VK_PAUSE && shift_state == 2) {
2463 app_keypad_keys ^= 1;
2467 /* Nethack keypad */
2468 if (cfg.nethack_keypad && !left_alt) {
2471 *p++ = shift_state ? 'B' : 'b';
2474 *p++ = shift_state ? 'J' : 'j';
2477 *p++ = shift_state ? 'N' : 'n';
2480 *p++ = shift_state ? 'H' : 'h';
2483 *p++ = shift_state ? '.' : '.';
2486 *p++ = shift_state ? 'L' : 'l';
2489 *p++ = shift_state ? 'Y' : 'y';
2492 *p++ = shift_state ? 'K' : 'k';
2495 *p++ = shift_state ? 'U' : 'u';
2500 /* Application Keypad */
2504 if (cfg.funky_type == 3 ||
2505 (cfg.funky_type <= 1 &&
2506 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
2520 if (app_keypad_keys && !cfg.no_applic_k)
2557 if (cfg.funky_type == 2) {
2562 } else if (shift_state)
2569 if (cfg.funky_type == 2)
2573 if (cfg.funky_type == 2)
2577 if (cfg.funky_type == 2)
2582 if (HIWORD(lParam) & KF_EXTENDED)
2588 if (xkey >= 'P' && xkey <= 'S')
2589 p += sprintf((char *) p, "\x1B%c", xkey);
2591 p += sprintf((char *) p, "\x1B?%c", xkey);
2593 p += sprintf((char *) p, "\x1BO%c", xkey);
2598 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
2599 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2603 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
2609 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
2613 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
2617 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
2622 if (wParam == VK_PAUSE) { /* Break/Pause */
2627 /* Control-2 to Control-8 are special */
2628 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
2629 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
2632 if (shift_state == 2 && wParam == 0xBD) {
2636 if (shift_state == 2 && wParam == 0xDF) {
2640 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2647 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2648 * for integer decimal nn.)
2650 * We also deal with the weird ones here. Linux VCs replace F1
2651 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2652 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2658 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
2661 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
2664 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
2667 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
2670 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
2673 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
2676 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
2679 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
2682 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
2685 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
2736 /* Reorder edit keys to physical order */
2737 if (cfg.funky_type == 3 && code <= 6)
2738 code = "\0\2\1\4\5\3\6"[code];
2740 if (vt52_mode && code > 0 && code <= 6) {
2741 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
2745 if (cfg.funky_type == 5 && code >= 11 && code <= 34) {
2746 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
2749 case VK_F1: index = 0; break;
2750 case VK_F2: index = 1; break;
2751 case VK_F3: index = 2; break;
2752 case VK_F4: index = 3; break;
2753 case VK_F5: index = 4; break;
2754 case VK_F6: index = 5; break;
2755 case VK_F7: index = 6; break;
2756 case VK_F8: index = 7; break;
2757 case VK_F9: index = 8; break;
2758 case VK_F10: index = 9; break;
2759 case VK_F11: index = 10; break;
2760 case VK_F12: index = 11; break;
2762 if (keystate[VK_SHIFT] & 0x80) index += 12;
2763 if (keystate[VK_CONTROL] & 0x80) index += 24;
2764 p += sprintf((char *) p, "\x1B[%c", codes[index]);
2767 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
2774 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
2777 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
2780 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2781 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
2784 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2786 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
2788 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
2791 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2792 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
2796 p += sprintf((char *) p, "\x1B[%d~", code);
2801 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2802 * some reason seems to send VK_CLEAR to Windows...).
2825 p += sprintf((char *) p, "\x1B%c", xkey);
2827 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
2828 /* VT100 & VT102 manuals both state the app cursor keys
2829 * only work if the app keypad is on.
2831 if (!app_keypad_keys)
2833 /* Useful mapping of Ctrl-arrows */
2834 if (shift_state == 2)
2838 p += sprintf((char *) p, "\x1BO%c", xkey);
2840 p += sprintf((char *) p, "\x1B[%c", xkey);
2847 * Finally, deal with Return ourselves. (Win95 seems to
2848 * foul it up when Alt is pressed, for some reason.)
2850 if (wParam == VK_RETURN) { /* Return */
2856 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
2857 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
2862 /* Okay we've done everything interesting; let windows deal with
2863 * the boring stuff */
2865 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
2866 #ifdef SHOW_TOASCII_RESULT
2867 if (r == 1 && !key_down) {
2869 if (utf || dbcs_screenfont)
2870 debug((", (U+%04x)", alt_sum));
2872 debug((", LCH(%d)", alt_sum));
2874 debug((", ACH(%d)", keys[0]));
2879 for (r1 = 0; r1 < r; r1++) {
2880 debug(("%s%d", r1 ? "," : "", keys[r1]));
2888 for (i = 0; i < r; i++) {
2889 unsigned char ch = (unsigned char) keys[i];
2891 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
2896 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
2900 if ((nc = check_compose(compose_char, ch)) == -1) {
2901 MessageBeep(MB_ICONHAND);
2905 luni_send(&keybuf, 1);
2913 if (utf || dbcs_screenfont) {
2915 luni_send(&keybuf, 1);
2917 ch = (char) alt_sum;
2922 lpage_send(kbd_codepage, &ch, 1);
2924 static char cbuf[] = "\033 ";
2926 lpage_send(kbd_codepage, cbuf + !left_alt,
2931 /* This is so the ALT-Numpad and dead keys work correctly. */
2936 /* If we're definitly not building up an ALT-54321 then clear it */
2939 /* If we will be using alt_sum fix the 256s */
2940 else if (keys[0] && (utf || dbcs_screenfont))
2944 /* ALT alone may or may not want to bring up the System menu */
2945 if (wParam == VK_MENU) {
2947 if (message == WM_SYSKEYDOWN)
2949 else if (message == WM_SYSKEYUP && alt_state)
2950 PostMessage(hwnd, WM_CHAR, ' ', 0);
2951 if (message == WM_SYSKEYUP)
2961 void set_title(char *title)
2964 window_name = smalloc(1 + strlen(title));
2965 strcpy(window_name, title);
2966 if (cfg.win_name_always || !IsIconic(hwnd))
2967 SetWindowText(hwnd, title);
2970 void set_icon(char *title)
2973 icon_name = smalloc(1 + strlen(title));
2974 strcpy(icon_name, title);
2975 if (!cfg.win_name_always && IsIconic(hwnd))
2976 SetWindowText(hwnd, title);
2979 void set_sbar(int total, int start, int page)
2986 si.cbSize = sizeof(si);
2987 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2989 si.nMax = total - 1;
2993 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
2996 Context get_ctx(void)
3002 SelectPalette(hdc, pal, FALSE);
3008 void free_ctx(Context ctx)
3010 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3011 ReleaseDC(hwnd, ctx);
3014 static void real_palette_set(int n, int r, int g, int b)
3017 logpal->palPalEntry[n].peRed = r;
3018 logpal->palPalEntry[n].peGreen = g;
3019 logpal->palPalEntry[n].peBlue = b;
3020 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3021 colours[n] = PALETTERGB(r, g, b);
3022 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3024 colours[n] = RGB(r, g, b);
3027 void palette_set(int n, int r, int g, int b)
3029 static const int first[21] = {
3030 0, 2, 4, 6, 8, 10, 12, 14,
3031 1, 3, 5, 7, 9, 11, 13, 15,
3034 real_palette_set(first[n], r, g, b);
3036 real_palette_set(first[n] + 1, r, g, b);
3038 HDC hdc = get_ctx();
3039 UnrealizeObject(pal);
3040 RealizePalette(hdc);
3045 void palette_reset(void)
3049 for (i = 0; i < NCOLOURS; i++) {
3051 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3052 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3053 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3054 logpal->palPalEntry[i].peFlags = 0;
3055 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3056 defpal[i].rgbtGreen,
3057 defpal[i].rgbtBlue);
3059 colours[i] = RGB(defpal[i].rgbtRed,
3060 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3065 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3067 RealizePalette(hdc);
3072 void write_aclip(char *data, int len, int must_deselect)
3077 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3080 lock = GlobalLock(clipdata);
3083 memcpy(lock, data, len);
3084 ((unsigned char *) lock)[len] = 0;
3085 GlobalUnlock(clipdata);
3088 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3090 if (OpenClipboard(hwnd)) {
3092 SetClipboardData(CF_TEXT, clipdata);
3095 GlobalFree(clipdata);
3098 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3102 * Note: unlike write_aclip() this will not append a nul.
3104 void write_clip(wchar_t * data, int len, int must_deselect)
3111 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3113 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3114 len * sizeof(wchar_t));
3115 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3117 if (!clipdata || !clipdata2) {
3119 GlobalFree(clipdata);
3121 GlobalFree(clipdata2);
3124 if (!(lock = GlobalLock(clipdata)))
3126 if (!(lock2 = GlobalLock(clipdata2)))
3129 memcpy(lock, data, len * sizeof(wchar_t));
3130 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3132 GlobalUnlock(clipdata);
3133 GlobalUnlock(clipdata2);
3136 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3138 if (OpenClipboard(hwnd)) {
3140 SetClipboardData(CF_UNICODETEXT, clipdata);
3141 SetClipboardData(CF_TEXT, clipdata2);
3144 GlobalFree(clipdata);
3145 GlobalFree(clipdata2);
3149 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3152 void get_clip(wchar_t ** p, int *len)
3154 static HGLOBAL clipdata = NULL;
3155 static wchar_t *converted = 0;
3164 GlobalUnlock(clipdata);
3167 } else if (OpenClipboard(NULL)) {
3168 if (clipdata = GetClipboardData(CF_UNICODETEXT)) {
3170 *p = GlobalLock(clipdata);
3172 for (p2 = *p; *p2; p2++);
3176 } else if (clipdata = GetClipboardData(CF_TEXT)) {
3180 s = GlobalLock(clipdata);
3181 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3182 *p = converted = smalloc(i * sizeof(wchar_t));
3183 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3196 * Move `lines' lines from position `from' to position `to' in the
3199 void optimised_move(int to, int from, int lines)
3204 min = (to < from ? to : from);
3205 max = to + from - min;
3208 r.right = cols * font_width;
3209 r.top = min * font_height;
3210 r.bottom = (max + lines) * font_height;
3211 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3216 * Print a message box and perform a fatal exit.
3218 void fatalbox(char *fmt, ...)
3224 vsprintf(stuff, fmt, ap);
3226 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3235 if (mode == BELL_DEFAULT) {
3237 * For MessageBeep style bells, we want to be careful of
3238 * timing, because they don't have the nice property of
3239 * PlaySound bells that each one cancels the previous
3240 * active one. So we limit the rate to one per 50ms or so.
3242 static long lastbeep = 0;
3245 beepdiff = GetTickCount() - lastbeep;
3246 if (beepdiff >= 0 && beepdiff < 50)
3250 * The above MessageBeep call takes time, so we record the
3251 * time _after_ it finishes rather than before it starts.
3253 lastbeep = GetTickCount();
3254 } else if (mode == BELL_WAVEFILE) {
3255 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3256 char buf[sizeof(cfg.bell_wavefile) + 80];
3257 sprintf(buf, "Unable to play sound file\n%s\n"
3258 "Using default sound instead", cfg.bell_wavefile);
3259 MessageBox(hwnd, buf, "PuTTY Sound Error",
3260 MB_OK | MB_ICONEXCLAMATION);
3261 cfg.beep = BELL_DEFAULT;