15 #define COMPILE_MULTIMON_STUBS
25 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
31 #define IDM_SHOWLOG 0x0010
32 #define IDM_NEWSESS 0x0020
33 #define IDM_DUPSESS 0x0030
34 #define IDM_RECONF 0x0040
35 #define IDM_CLRSB 0x0050
36 #define IDM_RESET 0x0060
37 #define IDM_TEL_AYT 0x0070
38 #define IDM_TEL_BRK 0x0080
39 #define IDM_TEL_SYNCH 0x0090
40 #define IDM_TEL_EC 0x00a0
41 #define IDM_TEL_EL 0x00b0
42 #define IDM_TEL_GA 0x00c0
43 #define IDM_TEL_NOP 0x00d0
44 #define IDM_TEL_ABORT 0x00e0
45 #define IDM_TEL_AO 0x00f0
46 #define IDM_TEL_IP 0x0100
47 #define IDM_TEL_SUSP 0x0110
48 #define IDM_TEL_EOR 0x0120
49 #define IDM_TEL_EOF 0x0130
50 #define IDM_HELP 0x0140
51 #define IDM_ABOUT 0x0150
52 #define IDM_SAVEDSESS 0x0160
53 #define IDM_COPYALL 0x0170
54 #define IDM_FULLSCREEN 0x0180
56 #define IDM_SESSLGP 0x0250 /* log type printable */
57 #define IDM_SESSLGA 0x0260 /* log type all chars */
58 #define IDM_SESSLGE 0x0270 /* log end */
59 #define IDM_SAVED_MIN 0x1000
60 #define IDM_SAVED_MAX 0x2000
62 #define WM_IGNORE_CLIP (WM_XUSER + 2)
63 #define WM_FULLSCR_ON_MAX (WM_XUSER + 3)
65 /* Needed for Chinese support and apparently not always defined. */
67 #define VK_PROCESSKEY 0xE5
70 /* Needed for mouse wheel support and not defined in earlier SDKs. */
72 #define WM_MOUSEWHEEL 0x020A
75 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
76 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
77 unsigned char *output);
78 static void cfgtopalette(void);
79 static void init_palette(void);
80 static void init_fonts(int, int);
81 static void another_font(int);
82 static void deinit_fonts(void);
83 static void set_input_locale(HKL);
85 static int is_full_screen(void);
86 static void make_full_screen(void);
87 static void clear_full_screen(void);
88 static void flip_full_screen(void);
90 /* Window layout information */
91 static void reset_window(int);
92 static int extra_width, extra_height;
93 static int font_width, font_height, font_dualwidth;
94 static int offset_width, offset_height;
95 static int was_zoomed = 0;
96 static int prev_rows, prev_cols;
98 static int pending_netevent = 0;
99 static WPARAM pend_netevent_wParam = 0;
100 static LPARAM pend_netevent_lParam = 0;
101 static void enact_pending_netevent(void);
102 static void flash_window(int mode);
104 static time_t last_movement = 0;
106 #define FONT_NORMAL 0
108 #define FONT_UNDERLINE 2
109 #define FONT_BOLDUND 3
110 #define FONT_WIDE 0x04
111 #define FONT_HIGH 0x08
112 #define FONT_NARROW 0x10
114 #define FONT_OEM 0x20
115 #define FONT_OEMBOLD 0x21
116 #define FONT_OEMUND 0x22
117 #define FONT_OEMBOLDUND 0x23
119 #define FONT_MAXNO 0x2F
121 static HFONT fonts[FONT_MAXNO];
122 static LOGFONT lfont;
123 static int fontflag[FONT_MAXNO];
125 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
133 static COLORREF colours[NCOLOURS];
135 static LPLOGPALETTE logpal;
136 static RGBTRIPLE defpal[NCOLOURS];
140 static HBITMAP caretbm;
142 static int dbltime, lasttime, lastact;
143 static Mouse_Button lastbtn;
145 /* this allows xterm-style mouse handling. */
146 static int send_raw_mouse = 0;
147 static int wheel_accumulator = 0;
149 static char *window_name, *icon_name;
151 static int compose_state = 0;
153 static OSVERSIONINFO osVersion;
155 /* Dummy routine, only required in plink. */
156 void ldisc_update(int echo, int edit)
160 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
162 static char appname[] = "PuTTY";
167 int guess_width, guess_height;
170 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
172 winsock_ver = MAKEWORD(1, 1);
173 if (WSAStartup(winsock_ver, &wsadata)) {
174 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
175 MB_OK | MB_ICONEXCLAMATION);
178 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
179 MessageBox(NULL, "WinSock version is incompatible with 1.1",
180 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
184 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
187 InitCommonControls();
189 /* Ensure a Maximize setting in Explorer doesn't maximise the
194 ZeroMemory(&osVersion, sizeof(osVersion));
195 osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
196 if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
197 MessageBox(NULL, "Windows refuses to report a version",
198 "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
204 * See if we can find our Help file.
207 char b[2048], *p, *q, *r;
209 GetModuleFileName(NULL, b, sizeof(b) - 1);
211 p = strrchr(b, '\\');
212 if (p && p >= r) r = p+1;
214 if (q && q >= r) r = q+1;
215 strcpy(r, "putty.hlp");
216 if ( (fp = fopen(b, "r")) != NULL) {
217 help_path = dupstr(b);
221 strcpy(r, "putty.cnt");
222 if ( (fp = fopen(b, "r")) != NULL) {
223 help_has_contents = TRUE;
226 help_has_contents = FALSE;
230 * Process the command line.
235 default_protocol = DEFAULT_PROTOCOL;
236 default_port = DEFAULT_PORT;
237 cfg.logtype = LGTYP_NONE;
239 do_defaults(NULL, &cfg);
242 while (*p && isspace(*p))
246 * Process command line options first. Yes, this can be
247 * done better, and it will be as soon as I have the
251 char *q = p + strcspn(p, " \t");
254 tolower(p[0]) == 's' &&
255 tolower(p[1]) == 's' && tolower(p[2]) == 'h') {
256 default_protocol = cfg.protocol = PROT_SSH;
257 default_port = cfg.port = 22;
258 } else if (q == p + 7 &&
259 tolower(p[0]) == 'c' &&
260 tolower(p[1]) == 'l' &&
261 tolower(p[2]) == 'e' &&
262 tolower(p[3]) == 'a' &&
263 tolower(p[4]) == 'n' &&
264 tolower(p[5]) == 'u' && tolower(p[6]) == 'p') {
266 * `putty -cleanup'. Remove all registry entries
267 * associated with PuTTY, and also find and delete
268 * the random seed file.
271 "This procedure will remove ALL Registry\n"
272 "entries associated with PuTTY, and will\n"
273 "also remove the PuTTY random seed file.\n"
275 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
276 "SESSIONS. Are you really sure you want\n"
279 MB_YESNO | MB_ICONWARNING) == IDYES) {
284 p = q + strspn(q, " \t");
288 * An initial @ means to activate a saved session.
292 while (i > 1 && isspace(p[i - 1]))
295 do_defaults(p + 1, &cfg);
296 if (!*cfg.host && !do_config()) {
300 } else if (*p == '&') {
302 * An initial & means we've been given a command line
303 * containing the hex value of a HANDLE for a file
304 * mapping object, which we must then extract as a
309 if (sscanf(p + 1, "%p", &filemap) == 1 &&
310 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
311 0, 0, sizeof(Config))) != NULL) {
314 CloseHandle(filemap);
315 } else if (!do_config()) {
322 * If the hostname starts with "telnet:", set the
323 * protocol to Telnet and process the string as a
326 if (!strncmp(q, "telnet:", 7)) {
330 if (q[0] == '/' && q[1] == '/')
332 cfg.protocol = PROT_TELNET;
334 while (*p && *p != ':' && *p != '/')
343 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
344 cfg.host[sizeof(cfg.host) - 1] = '\0';
346 while (*p && !isspace(*p))
350 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
351 cfg.host[sizeof(cfg.host) - 1] = '\0';
352 while (*p && isspace(*p))
367 * Trim leading whitespace off the hostname if it's there.
370 int space = strspn(cfg.host, " \t");
371 memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
374 /* See if host is of the form user@host */
375 if (cfg.host[0] != '\0') {
376 char *atsign = strchr(cfg.host, '@');
377 /* Make sure we're not overflowing the user field */
379 if (atsign - cfg.host < sizeof cfg.username) {
380 strncpy(cfg.username, cfg.host, atsign - cfg.host);
381 cfg.username[atsign - cfg.host] = '\0';
383 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
388 * Trim a colon suffix off the hostname if it's there.
390 cfg.host[strcspn(cfg.host, ":")] = '\0';
394 * Select protocol. This is farmed out into a table in a
395 * separate file to enable an ssh-free variant.
400 for (i = 0; backends[i].backend != NULL; i++)
401 if (backends[i].protocol == cfg.protocol) {
402 back = backends[i].backend;
406 MessageBox(NULL, "Unsupported protocol number found",
407 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
413 /* Check for invalid Port number (i.e. zero) */
415 MessageBox(NULL, "Invalid Port Number",
416 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
423 wndclass.lpfnWndProc = WndProc;
424 wndclass.cbClsExtra = 0;
425 wndclass.cbWndExtra = 0;
426 wndclass.hInstance = inst;
427 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
428 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
429 wndclass.hbrBackground = NULL;
430 wndclass.lpszMenuName = NULL;
431 wndclass.lpszClassName = appname;
433 RegisterClass(&wndclass);
438 savelines = cfg.savelines;
444 * Guess some defaults for the window size. This all gets
445 * updated later, so we don't really care too much. However, we
446 * do want the font width/height guesses to correspond to a
447 * large font rather than a small one...
454 term_size(cfg.height, cfg.width, cfg.savelines);
455 guess_width = extra_width + font_width * cols;
456 guess_height = extra_height + font_height * rows;
459 HWND w = GetDesktopWindow();
460 GetWindowRect(w, &r);
461 if (guess_width > r.right - r.left)
462 guess_width = r.right - r.left;
463 if (guess_height > r.bottom - r.top)
464 guess_height = r.bottom - r.top;
468 int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
471 winmode &= ~(WS_VSCROLL);
472 if (cfg.resize_action == RESIZE_DISABLED)
473 winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
475 exwinmode |= WS_EX_TOPMOST;
477 exwinmode |= WS_EX_CLIENTEDGE;
478 hwnd = CreateWindowEx(exwinmode, appname, appname,
479 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
480 guess_width, guess_height,
481 NULL, NULL, inst, NULL);
485 * Initialise the fonts, simultaneously correcting the guesses
486 * for font_{width,height}.
491 * Correct the guesses for extra_{width,height}.
495 GetWindowRect(hwnd, &wr);
496 GetClientRect(hwnd, &cr);
497 offset_width = offset_height = cfg.window_border;
498 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
499 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
503 * Resize the window, now we know what size we _really_ want it
506 guess_width = extra_width + font_width * cols;
507 guess_height = extra_height + font_height * rows;
508 SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
509 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
512 * Set up a caret bitmap, with no content.
516 int size = (font_width + 15) / 16 * 2 * font_height;
517 bits = smalloc(size);
518 memset(bits, 0, size);
519 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
522 CreateCaret(hwnd, caretbm, font_width, font_height);
525 * Initialise the scroll bar.
530 si.cbSize = sizeof(si);
531 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
536 SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
540 * Start up the telnet connection.
544 char msg[1024], *title;
547 error = back->init(cfg.host, cfg.port, &realhost, cfg.tcp_nodelay);
549 sprintf(msg, "Unable to open connection to\n"
550 "%.800s\n" "%s", cfg.host, error);
551 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
554 window_name = icon_name = NULL;
556 title = cfg.wintitle;
558 sprintf(msg, "%s - PuTTY", realhost);
566 session_closed = FALSE;
569 * Prepare the mouse handler.
571 lastact = MA_NOTHING;
572 lastbtn = MBT_NOTHING;
573 dbltime = GetDoubleClickTime();
576 * Set up the session-control options on the system menu.
579 HMENU m = GetSystemMenu(hwnd, FALSE);
583 AppendMenu(m, MF_SEPARATOR, 0, 0);
584 if (cfg.protocol == PROT_TELNET) {
586 AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
587 AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
588 AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
589 AppendMenu(p, MF_SEPARATOR, 0, 0);
590 AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
591 AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
592 AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
593 AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
594 AppendMenu(p, MF_SEPARATOR, 0, 0);
595 AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
596 AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
597 AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
598 AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
599 AppendMenu(p, MF_SEPARATOR, 0, 0);
600 AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
601 AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
602 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
604 AppendMenu(m, MF_SEPARATOR, 0, 0);
606 AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
607 AppendMenu(m, MF_SEPARATOR, 0, 0);
608 AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
609 AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
612 for (i = 1; i < ((nsessions < 256) ? nsessions : 256); i++)
613 AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
615 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
616 AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
617 AppendMenu(m, MF_SEPARATOR, 0, 0);
618 AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
619 AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
620 AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
621 AppendMenu(m, MF_SEPARATOR, 0, 0);
622 AppendMenu(m, (cfg.resize_action == RESIZE_DISABLED) ?
623 MF_GRAYED : MF_ENABLED, IDM_FULLSCREEN, "&Full Screen");
624 AppendMenu(m, MF_SEPARATOR, 0, 0);
626 AppendMenu(m, MF_ENABLED, IDM_HELP, "&Help");
627 AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
631 * Set up the initial input locale.
633 set_input_locale(GetKeyboardLayout(0));
636 * Finally show the window!
638 ShowWindow(hwnd, show);
639 SetForegroundWindow(hwnd);
642 * Open the initial log file if there is one.
647 * Set the palette up.
653 has_focus = (GetForegroundWindow() == hwnd);
656 if (GetMessage(&msg, NULL, 0, 0) == 1) {
657 int timer_id = 0, long_timer = 0;
659 while (msg.message != WM_QUIT) {
660 /* Sometimes DispatchMessage calls routines that use their own
661 * GetMessage loop, setup this timer so we get some control back.
663 * Also call term_update() from the timer so that if the host
664 * is sending data flat out we still do redraws.
666 if (timer_id && long_timer) {
667 KillTimer(hwnd, timer_id);
668 long_timer = timer_id = 0;
671 timer_id = SetTimer(hwnd, 1, 20, NULL);
672 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
673 DispatchMessage(&msg);
675 /* Make sure we blink everything that needs it. */
678 /* Send the paste buffer if there's anything to send */
681 /* If there's nothing new in the queue then we can do everything
682 * we've delayed, reading the socket, writing, and repainting
685 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
688 if (pending_netevent) {
689 enact_pending_netevent();
691 /* Force the cursor blink on */
694 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
698 /* Okay there is now nothing to do so we make sure the screen is
699 * completely up to date then tell windows to call us in a little
703 KillTimer(hwnd, timer_id);
711 flash_window(1); /* maintain */
713 /* The messages seem unreliable; especially if we're being tricky */
714 has_focus = (GetForegroundWindow() == hwnd);
717 /* Hmm, term_update didn't want to do an update too soon ... */
718 timer_id = SetTimer(hwnd, 1, 50, NULL);
720 timer_id = SetTimer(hwnd, 1, 500, NULL);
722 timer_id = SetTimer(hwnd, 1, 100, NULL);
725 /* There's no point rescanning everything in the message queue
726 * so we do an apparently unnecessary wait here
729 if (GetMessage(&msg, NULL, 0, 0) != 1)
743 if (cfg.protocol == PROT_SSH) {
754 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
756 char *do_select(SOCKET skt, int startup)
761 events = (FD_CONNECT | FD_READ | FD_WRITE |
762 FD_OOB | FD_CLOSE | FD_ACCEPT);
767 return "do_select(): internal error (hwnd==NULL)";
768 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
769 switch (WSAGetLastError()) {
771 return "Network is down";
773 return "WSAAsyncSelect(): unknown error";
780 * set or clear the "raw mouse message" mode
782 void set_raw_mouse_mode(int activate)
784 send_raw_mouse = activate;
785 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
789 * Print a message box and close the connection.
791 void connection_fatal(char *fmt, ...)
797 vsprintf(stuff, fmt, ap);
799 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
800 if (cfg.close_on_exit == COE_ALWAYS)
803 session_closed = TRUE;
804 SetWindowText(hwnd, "PuTTY (inactive)");
809 * Actually do the job requested by a WM_NETEVENT
811 static void enact_pending_netevent(void)
813 static int reentering = 0;
814 extern int select_result(WPARAM, LPARAM);
818 return; /* don't unpend the pending */
820 pending_netevent = FALSE;
823 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
826 if (ret == 0 && !session_closed) {
827 /* Abnormal exits will already have set session_closed and taken
828 * appropriate action. */
829 if (cfg.close_on_exit == COE_ALWAYS ||
830 cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
832 session_closed = TRUE;
833 SetWindowText(hwnd, "PuTTY (inactive)");
834 MessageBox(hwnd, "Connection closed by remote host",
835 "PuTTY", MB_OK | MB_ICONINFORMATION);
841 * Copy the colour palette from the configuration data into defpal.
842 * This is non-trivial because the colour indices are different.
844 static void cfgtopalette(void)
847 static const int ww[] = {
848 6, 7, 8, 9, 10, 11, 12, 13,
849 14, 15, 16, 17, 18, 19, 20, 21,
850 0, 1, 2, 3, 4, 4, 5, 5
853 for (i = 0; i < 24; i++) {
855 defpal[i].rgbtRed = cfg.colours[w][0];
856 defpal[i].rgbtGreen = cfg.colours[w][1];
857 defpal[i].rgbtBlue = cfg.colours[w][2];
862 * Set up the colour palette.
864 static void init_palette(void)
867 HDC hdc = GetDC(hwnd);
869 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
870 logpal = smalloc(sizeof(*logpal)
871 - sizeof(logpal->palPalEntry)
872 + NCOLOURS * sizeof(PALETTEENTRY));
873 logpal->palVersion = 0x300;
874 logpal->palNumEntries = NCOLOURS;
875 for (i = 0; i < NCOLOURS; i++) {
876 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
877 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
878 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
879 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
881 pal = CreatePalette(logpal);
883 SelectPalette(hdc, pal, FALSE);
885 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
888 ReleaseDC(hwnd, hdc);
891 for (i = 0; i < NCOLOURS; i++)
892 colours[i] = PALETTERGB(defpal[i].rgbtRed,
896 for (i = 0; i < NCOLOURS; i++)
897 colours[i] = RGB(defpal[i].rgbtRed,
898 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
902 * Initialise all the fonts we will need initially. There may be as many as
903 * three or as few as one. The other (poentially) twentyone fonts are done
904 * if/when they are needed.
908 * - check the font width and height, correcting our guesses if
911 * - verify that the bold font is the same width as the ordinary
912 * one, and engage shadow bolding if not.
914 * - verify that the underlined font is the same width as the
915 * ordinary one (manual underlining by means of line drawing can
916 * be done in a pinch).
918 static void init_fonts(int pick_width, int pick_height)
925 int fw_dontcare, fw_bold;
927 for (i = 0; i < FONT_MAXNO; i++)
930 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
933 if (cfg.fontisbold) {
934 fw_dontcare = FW_BOLD;
937 fw_dontcare = FW_DONTCARE;
944 font_height = pick_height;
946 font_height = cfg.fontheight;
947 if (font_height > 0) {
949 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
952 font_width = pick_width;
955 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
956 c, OUT_DEFAULT_PRECIS, \
957 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
958 FIXED_PITCH | FF_DONTCARE, cfg.font)
960 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
962 lfont.lfHeight = font_height;
963 lfont.lfWidth = font_width;
964 lfont.lfEscapement = 0;
965 lfont.lfOrientation = 0;
966 lfont.lfWeight = fw_dontcare;
967 lfont.lfItalic = FALSE;
968 lfont.lfUnderline = FALSE;
969 lfont.lfStrikeOut = FALSE;
970 lfont.lfCharSet = cfg.fontcharset;
971 lfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
972 lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
973 lfont.lfQuality = DEFAULT_QUALITY;
974 lfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
975 strncpy(lfont.lfFaceName, cfg.font, LF_FACESIZE);
977 SelectObject(hdc, fonts[FONT_NORMAL]);
978 GetTextMetrics(hdc, &tm);
980 if (pick_width == 0 || pick_height == 0) {
981 font_height = tm.tmHeight;
982 font_width = tm.tmAveCharWidth;
984 font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth);
986 #ifdef RDB_DEBUG_PATCH
987 debug(23, "Primary font H=%d, AW=%d, MW=%d",
988 tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth);
993 DWORD cset = tm.tmCharSet;
994 memset(&info, 0xFF, sizeof(info));
996 /* !!! Yes the next line is right */
997 if (cset == OEM_CHARSET)
998 font_codepage = GetOEMCP();
1000 if (TranslateCharsetInfo
1001 ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
1006 GetCPInfo(font_codepage, &cpinfo);
1007 dbcs_screenfont = (cpinfo.MaxCharSize > 1);
1010 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
1013 * Some fonts, e.g. 9-pt Courier, draw their underlines
1014 * outside their character cell. We successfully prevent
1015 * screen corruption by clipping the text output, but then
1016 * we lose the underline completely. Here we try to work
1017 * out whether this is such a font, and if it is, we set a
1018 * flag that causes underlines to be drawn by hand.
1020 * Having tried other more sophisticated approaches (such
1021 * as examining the TEXTMETRIC structure or requesting the
1022 * height of a string), I think we'll do this the brute
1023 * force way: we create a small bitmap, draw an underlined
1024 * space on it, and test to see whether any pixels are
1025 * foreground-coloured. (Since we expect the underline to
1026 * go all the way across the character cell, we only search
1027 * down a single column of the bitmap, half way across.)
1031 HBITMAP und_bm, und_oldbm;
1035 und_dc = CreateCompatibleDC(hdc);
1036 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
1037 und_oldbm = SelectObject(und_dc, und_bm);
1038 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
1039 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
1040 SetTextColor(und_dc, RGB(255, 255, 255));
1041 SetBkColor(und_dc, RGB(0, 0, 0));
1042 SetBkMode(und_dc, OPAQUE);
1043 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
1045 for (i = 0; i < font_height; i++) {
1046 c = GetPixel(und_dc, font_width / 2, i);
1047 if (c != RGB(0, 0, 0))
1050 SelectObject(und_dc, und_oldbm);
1051 DeleteObject(und_bm);
1054 und_mode = UND_LINE;
1055 DeleteObject(fonts[FONT_UNDERLINE]);
1056 fonts[FONT_UNDERLINE] = 0;
1060 if (bold_mode == BOLD_FONT) {
1061 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
1065 descent = tm.tmAscent + 1;
1066 if (descent >= font_height)
1067 descent = font_height - 1;
1069 for (i = 0; i < 3; i++) {
1071 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
1072 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
1079 ReleaseDC(hwnd, hdc);
1081 if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
1082 und_mode = UND_LINE;
1083 DeleteObject(fonts[FONT_UNDERLINE]);
1084 fonts[FONT_UNDERLINE] = 0;
1087 if (bold_mode == BOLD_FONT &&
1088 fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
1089 bold_mode = BOLD_SHADOW;
1090 DeleteObject(fonts[FONT_BOLD]);
1091 fonts[FONT_BOLD] = 0;
1093 fontflag[0] = fontflag[1] = fontflag[2] = 1;
1098 static void another_font(int fontno)
1101 int fw_dontcare, fw_bold;
1105 if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1108 basefont = (fontno & ~(FONT_BOLDUND));
1109 if (basefont != fontno && !fontflag[basefont])
1110 another_font(basefont);
1112 if (cfg.fontisbold) {
1113 fw_dontcare = FW_BOLD;
1116 fw_dontcare = FW_DONTCARE;
1120 c = cfg.fontcharset;
1126 if (fontno & FONT_WIDE)
1128 if (fontno & FONT_NARROW)
1130 if (fontno & FONT_OEM)
1132 if (fontno & FONT_BOLD)
1134 if (fontno & FONT_UNDERLINE)
1138 CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1139 FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1140 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1141 FIXED_PITCH | FF_DONTCARE, s);
1143 fontflag[fontno] = 1;
1146 static void deinit_fonts(void)
1149 for (i = 0; i < FONT_MAXNO; i++) {
1151 DeleteObject(fonts[i]);
1157 void request_resize(int w, int h)
1161 /* If the window is maximized supress resizing attempts */
1162 if (IsZoomed(hwnd)) {
1163 if (cfg.resize_action == RESIZE_TERM)
1167 if (cfg.resize_action == RESIZE_DISABLED) return;
1168 if (h == rows && w == cols) return;
1170 /* Sanity checks ... */
1172 static int first_time = 1;
1175 switch (first_time) {
1177 /* Get the size of the screen */
1178 if (GetClientRect(GetDesktopWindow(), &ss))
1179 /* first_time = 0 */ ;
1185 /* Make sure the values are sane */
1186 width = (ss.right - ss.left - extra_width) / 4;
1187 height = (ss.bottom - ss.top - extra_height) / 6;
1189 if (w > width || h > height)
1198 term_size(h, w, cfg.savelines);
1200 if (cfg.resize_action != RESIZE_FONT && !IsZoomed(hwnd)) {
1201 width = extra_width + font_width * w;
1202 height = extra_height + font_height * h;
1204 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1205 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1206 SWP_NOMOVE | SWP_NOZORDER);
1210 InvalidateRect(hwnd, NULL, TRUE);
1213 static void reset_window(int reinit) {
1215 * This function decides how to resize or redraw when the
1216 * user changes something.
1218 * This function doesn't like to change the terminal size but if the
1219 * font size is locked that may be it's only soluion.
1221 int win_width, win_height;
1224 #ifdef RDB_DEBUG_PATCH
1225 debug((27, "reset_window()"));
1228 /* Current window sizes ... */
1229 GetWindowRect(hwnd, &wr);
1230 GetClientRect(hwnd, &cr);
1232 win_width = cr.right - cr.left;
1233 win_height = cr.bottom - cr.top;
1235 if (cfg.resize_action == RESIZE_DISABLED) reinit = 2;
1237 /* Are we being forced to reload the fonts ? */
1239 #ifdef RDB_DEBUG_PATCH
1240 debug((27, "reset_window() -- Forced deinit"));
1246 /* Oh, looks like we're minimised */
1247 if (win_width == 0 || win_height == 0)
1250 /* Is the window out of position ? */
1252 (offset_width != (win_width-font_width*cols)/2 ||
1253 offset_height != (win_height-font_height*rows)/2) ){
1254 offset_width = (win_width-font_width*cols)/2;
1255 offset_height = (win_height-font_height*rows)/2;
1256 InvalidateRect(hwnd, NULL, TRUE);
1257 #ifdef RDB_DEBUG_PATCH
1258 debug((27, "reset_window() -> Reposition terminal"));
1262 if (IsZoomed(hwnd)) {
1263 /* We're fullscreen, this means we must not change the size of
1264 * the window so it's the font size or the terminal itself.
1267 extra_width = wr.right - wr.left - cr.right + cr.left;
1268 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1270 if (cfg.resize_action != RESIZE_TERM) {
1271 if ( font_width != win_width/cols ||
1272 font_height != win_height/rows) {
1274 init_fonts(win_width/cols, win_height/rows);
1275 offset_width = (win_width-font_width*cols)/2;
1276 offset_height = (win_height-font_height*rows)/2;
1277 InvalidateRect(hwnd, NULL, TRUE);
1278 #ifdef RDB_DEBUG_PATCH
1279 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1280 font_width, font_height));
1284 if ( font_width != win_width/cols ||
1285 font_height != win_height/rows) {
1286 /* Our only choice at this point is to change the
1287 * size of the terminal; Oh well.
1289 term_size( win_height/font_height, win_width/font_width,
1291 offset_width = (win_width-font_width*cols)/2;
1292 offset_height = (win_height-font_height*rows)/2;
1293 InvalidateRect(hwnd, NULL, TRUE);
1294 #ifdef RDB_DEBUG_PATCH
1295 debug((27, "reset_window() -> Zoomed term_size"));
1302 /* Hmm, a force re-init means we should ignore the current window
1303 * so we resize to the default font size.
1306 #ifdef RDB_DEBUG_PATCH
1307 debug((27, "reset_window() -> Forced re-init"));
1310 offset_width = offset_height = cfg.window_border;
1311 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1312 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1314 if (win_width != font_width*cols + offset_width*2 ||
1315 win_height != font_height*rows + offset_height*2) {
1317 /* If this is too large windows will resize it to the maximum
1318 * allowed window size, we will then be back in here and resize
1319 * the font or terminal to fit.
1321 SetWindowPos(hwnd, NULL, 0, 0,
1322 font_width*cols + extra_width,
1323 font_height*rows + extra_height,
1324 SWP_NOMOVE | SWP_NOZORDER);
1327 InvalidateRect(hwnd, NULL, TRUE);
1331 /* Okay the user doesn't want us to change the font so we try the
1332 * window. But that may be too big for the screen which forces us
1333 * to change the terminal.
1335 if ((cfg.resize_action == RESIZE_TERM && reinit<=0) ||
1336 (cfg.resize_action == RESIZE_EITHER && reinit<0) ||
1338 offset_width = offset_height = cfg.window_border;
1339 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1340 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1342 if (win_width != font_width*cols + offset_width*2 ||
1343 win_height != font_height*rows + offset_height*2) {
1348 GetClientRect(GetDesktopWindow(), &ss);
1349 width = (ss.right - ss.left - extra_width) / font_width;
1350 height = (ss.bottom - ss.top - extra_height) / font_height;
1353 if ( rows > height || cols > width ) {
1354 if (cfg.resize_action == RESIZE_EITHER) {
1355 /* Make the font the biggest we can */
1357 font_width = (ss.right - ss.left - extra_width)/cols;
1359 font_height = (ss.bottom - ss.top - extra_height)/rows;
1362 init_fonts(font_width, font_height);
1364 width = (ss.right - ss.left - extra_width) / font_width;
1365 height = (ss.bottom - ss.top - extra_height) / font_height;
1367 if ( height > rows ) height = rows;
1368 if ( width > cols ) width = cols;
1369 term_size(height, width, cfg.savelines);
1370 #ifdef RDB_DEBUG_PATCH
1371 debug((27, "reset_window() -> term resize to (%d,%d)",
1377 SetWindowPos(hwnd, NULL, 0, 0,
1378 font_width*cols + extra_width,
1379 font_height*rows + extra_height,
1380 SWP_NOMOVE | SWP_NOZORDER);
1382 InvalidateRect(hwnd, NULL, TRUE);
1383 #ifdef RDB_DEBUG_PATCH
1384 debug((27, "reset_window() -> window resize to (%d,%d)",
1385 font_width*cols + extra_width,
1386 font_height*rows + extra_height));
1392 /* We're allowed to or must change the font but do we want to ? */
1394 if (font_width != (win_width-cfg.window_border*2)/cols ||
1395 font_height != (win_height-cfg.window_border*2)/rows) {
1398 init_fonts((win_width-cfg.window_border*2)/cols,
1399 (win_height-cfg.window_border*2)/rows);
1400 offset_width = (win_width-font_width*cols)/2;
1401 offset_height = (win_height-font_height*rows)/2;
1403 extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2;
1404 extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2;
1406 InvalidateRect(hwnd, NULL, TRUE);
1407 #ifdef RDB_DEBUG_PATCH
1408 debug((25, "reset_window() -> font resize to (%d,%d)",
1409 font_width, font_height));
1414 static void set_input_locale(HKL kl)
1418 GetLocaleInfo(LOWORD(kl), LOCALE_IDEFAULTANSICODEPAGE,
1419 lbuf, sizeof(lbuf));
1421 kbd_codepage = atoi(lbuf);
1424 static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
1426 int thistime = GetMessageTime();
1428 if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1429 lastbtn = MBT_NOTHING;
1430 term_mouse(b, MA_CLICK, x, y, shift, ctrl, alt);
1434 if (lastbtn == b && thistime - lasttime < dbltime) {
1435 lastact = (lastact == MA_CLICK ? MA_2CLK :
1436 lastact == MA_2CLK ? MA_3CLK :
1437 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1442 if (lastact != MA_NOTHING)
1443 term_mouse(b, lastact, x, y, shift, ctrl, alt);
1444 lasttime = thistime;
1448 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1449 * into a cooked one (SELECT, EXTEND, PASTE).
1451 Mouse_Button translate_button(Mouse_Button button)
1453 if (button == MBT_LEFT)
1455 if (button == MBT_MIDDLE)
1456 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1457 if (button == MBT_RIGHT)
1458 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1459 return 0; /* shouldn't happen */
1462 static void show_mouseptr(int show)
1464 static int cursor_visible = 1;
1465 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1467 if (cursor_visible && !show)
1469 else if (!cursor_visible && show)
1471 cursor_visible = show;
1474 static int is_alt_pressed(void)
1477 int r = GetKeyboardState(keystate);
1480 if (keystate[VK_MENU] & 0x80)
1482 if (keystate[VK_RMENU] & 0x80)
1487 static int resizing;
1489 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1490 WPARAM wParam, LPARAM lParam)
1493 static int ignore_clip = FALSE;
1494 static int need_backend_resize = FALSE;
1495 static int fullscr_on_max = FALSE;
1499 if (pending_netevent)
1500 enact_pending_netevent();
1506 if (cfg.ping_interval > 0) {
1509 if (now - last_movement > cfg.ping_interval) {
1510 back->special(TS_PING);
1511 last_movement = now;
1514 net_pending_errors();
1520 if (!cfg.warn_on_close || session_closed ||
1522 "Are you sure you want to close this session?",
1523 "PuTTY Exit Confirmation",
1524 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1525 DestroyWindow(hwnd);
1532 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1544 PROCESS_INFORMATION pi;
1545 HANDLE filemap = NULL;
1547 if (wParam == IDM_DUPSESS) {
1549 * Allocate a file-mapping memory chunk for the
1552 SECURITY_ATTRIBUTES sa;
1555 sa.nLength = sizeof(sa);
1556 sa.lpSecurityDescriptor = NULL;
1557 sa.bInheritHandle = TRUE;
1558 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1561 0, sizeof(Config), NULL);
1563 p = (Config *) MapViewOfFile(filemap,
1565 0, 0, sizeof(Config));
1567 *p = cfg; /* structure copy */
1571 sprintf(c, "putty &%p", filemap);
1573 } else if (wParam == IDM_SAVEDSESS) {
1574 if ((lParam - IDM_SAVED_MIN) / 16 < nsessions) {
1576 sessions[(lParam - IDM_SAVED_MIN) / 16];
1577 cl = smalloc(16 + strlen(session));
1578 /* 8, but play safe */
1581 /* not a very important failure mode */
1583 sprintf(cl, "putty @%s", session);
1591 GetModuleFileName(NULL, b, sizeof(b) - 1);
1593 si.lpReserved = NULL;
1594 si.lpDesktop = NULL;
1598 si.lpReserved2 = NULL;
1599 CreateProcess(b, cl, NULL, NULL, TRUE,
1600 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1603 CloseHandle(filemap);
1613 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1616 if (!do_reconfig(hwnd))
1620 /* Disable full-screen if resizing forbidden */
1621 HMENU m = GetSystemMenu (hwnd, FALSE);
1622 EnableMenuItem(m, IDM_FULLSCREEN, MF_BYCOMMAND |
1623 (cfg.resize_action == RESIZE_DISABLED)
1624 ? MF_GRAYED : MF_ENABLED);
1625 /* Gracefully unzoom if necessary */
1626 if (IsZoomed(hwnd) &&
1627 (cfg.resize_action == RESIZE_DISABLED)) {
1628 ShowWindow(hwnd, SW_RESTORE);
1632 if (strcmp(prev_cfg.logfilename, cfg.logfilename) ||
1633 prev_cfg.logtype != cfg.logtype) {
1634 logfclose(); /* reset logging */
1640 * Flush the line discipline's edit buffer in the
1641 * case where local editing has just been disabled.
1643 ldisc_send(NULL, 0, 0);
1651 /* Screen size changed ? */
1652 if (cfg.height != prev_cfg.height ||
1653 cfg.width != prev_cfg.width ||
1654 cfg.savelines != prev_cfg.savelines ||
1655 cfg.resize_action == RESIZE_FONT ||
1656 cfg.resize_action == RESIZE_DISABLED)
1657 term_size(cfg.height, cfg.width, cfg.savelines);
1659 /* Enable or disable the scroll bar, etc */
1661 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1662 LONG nexflag, exflag =
1663 GetWindowLong(hwnd, GWL_EXSTYLE);
1666 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
1667 if (cfg.alwaysontop) {
1668 nexflag |= WS_EX_TOPMOST;
1669 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1670 SWP_NOMOVE | SWP_NOSIZE);
1672 nexflag &= ~(WS_EX_TOPMOST);
1673 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1674 SWP_NOMOVE | SWP_NOSIZE);
1677 if (cfg.sunken_edge)
1678 nexflag |= WS_EX_CLIENTEDGE;
1680 nexflag &= ~(WS_EX_CLIENTEDGE);
1683 if (is_full_screen() ?
1684 cfg.scrollbar_in_fullscreen : cfg.scrollbar)
1687 nflg &= ~WS_VSCROLL;
1688 if (cfg.resize_action == RESIZE_DISABLED)
1689 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1691 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1693 if (nflg != flag || nexflag != exflag) {
1695 SetWindowLong(hwnd, GWL_STYLE, nflg);
1696 if (nexflag != exflag)
1697 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1699 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1700 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1701 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1709 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
1714 set_title(cfg.wintitle);
1715 if (IsIconic(hwnd)) {
1717 cfg.win_name_always ? window_name :
1721 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1722 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1723 cfg.fontisbold != prev_cfg.fontisbold ||
1724 cfg.fontheight != prev_cfg.fontheight ||
1725 cfg.fontcharset != prev_cfg.fontcharset ||
1726 cfg.vtmode != prev_cfg.vtmode ||
1727 cfg.bold_colour != prev_cfg.bold_colour ||
1728 cfg.resize_action == RESIZE_DISABLED ||
1729 cfg.resize_action == RESIZE_EITHER ||
1730 (cfg.resize_action != prev_cfg.resize_action))
1733 InvalidateRect(hwnd, NULL, TRUE);
1734 reset_window(init_lvl);
1735 net_pending_errors();
1748 back->special(TS_AYT);
1749 net_pending_errors();
1752 back->special(TS_BRK);
1753 net_pending_errors();
1756 back->special(TS_SYNCH);
1757 net_pending_errors();
1760 back->special(TS_EC);
1761 net_pending_errors();
1764 back->special(TS_EL);
1765 net_pending_errors();
1768 back->special(TS_GA);
1769 net_pending_errors();
1772 back->special(TS_NOP);
1773 net_pending_errors();
1776 back->special(TS_ABORT);
1777 net_pending_errors();
1780 back->special(TS_AO);
1781 net_pending_errors();
1784 back->special(TS_IP);
1785 net_pending_errors();
1788 back->special(TS_SUSP);
1789 net_pending_errors();
1792 back->special(TS_EOR);
1793 net_pending_errors();
1796 back->special(TS_EOF);
1797 net_pending_errors();
1803 WinHelp(hwnd, help_path,
1804 help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0);
1808 * We get this if the System menu has been activated
1815 * We get this if the System menu has been activated
1816 * using the keyboard. This might happen from within
1817 * TranslateKey, in which case it really wants to be
1818 * followed by a `space' character to actually _bring
1819 * the menu up_ rather than just sitting there in
1820 * `ready to appear' state.
1822 show_mouseptr(1); /* make sure pointer is visible */
1824 PostMessage(hwnd, WM_CHAR, ' ', 0);
1826 case IDM_FULLSCREEN:
1830 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1831 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1836 #define X_POS(l) ((int)(short)LOWORD(l))
1837 #define Y_POS(l) ((int)(short)HIWORD(l))
1839 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1840 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1841 #define WHEEL_DELTA 120
1844 wheel_accumulator += (short) HIWORD(wParam);
1845 wParam = LOWORD(wParam);
1847 /* process events when the threshold is reached */
1848 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1851 /* reduce amount for next time */
1852 if (wheel_accumulator > 0) {
1854 wheel_accumulator -= WHEEL_DELTA;
1855 } else if (wheel_accumulator < 0) {
1857 wheel_accumulator += WHEEL_DELTA;
1861 if (send_raw_mouse) {
1862 /* send a mouse-down followed by a mouse up */
1866 TO_CHR_X(X_POS(lParam)),
1867 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1868 wParam & MK_CONTROL, is_alt_pressed());
1869 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1870 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1871 wParam & MK_CONTROL, is_alt_pressed());
1873 /* trigger a scroll */
1875 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1880 case WM_LBUTTONDOWN:
1881 case WM_MBUTTONDOWN:
1882 case WM_RBUTTONDOWN:
1890 case WM_LBUTTONDOWN:
1894 case WM_MBUTTONDOWN:
1895 button = MBT_MIDDLE;
1898 case WM_RBUTTONDOWN:
1907 button = MBT_MIDDLE;
1915 button = press = 0; /* shouldn't happen */
1919 * Special case: in full-screen mode, if the left
1920 * button is clicked in the very top left corner of the
1921 * window, we put up the System menu instead of doing
1924 if (is_full_screen() && press && button == MBT_LEFT &&
1925 X_POS(lParam) == 0 && Y_POS(lParam) == 0) {
1926 SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0);
1931 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1932 wParam & MK_SHIFT, wParam & MK_CONTROL,
1936 term_mouse(button, MA_RELEASE,
1937 TO_CHR_X(X_POS(lParam)),
1938 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1939 wParam & MK_CONTROL, is_alt_pressed());
1947 * Add the mouse position and message time to the random
1950 noise_ultralight(lParam);
1952 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1954 if (wParam & MK_LBUTTON)
1956 else if (wParam & MK_MBUTTON)
1960 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1961 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1962 wParam & MK_CONTROL, is_alt_pressed());
1965 case WM_NCMOUSEMOVE:
1967 noise_ultralight(lParam);
1969 case WM_IGNORE_CLIP:
1970 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1972 case WM_DESTROYCLIPBOARD:
1975 ignore_clip = FALSE;
1981 hdc = BeginPaint(hwnd, &p);
1983 SelectPalette(hdc, pal, TRUE);
1984 RealizePalette(hdc);
1987 (p.rcPaint.left-offset_width)/font_width,
1988 (p.rcPaint.top-offset_height)/font_height,
1989 (p.rcPaint.right-offset_width-1)/font_width,
1990 (p.rcPaint.bottom-offset_height-1)/font_height);
1993 p.rcPaint.left < offset_width ||
1994 p.rcPaint.top < offset_height ||
1995 p.rcPaint.right >= offset_width + font_width*cols ||
1996 p.rcPaint.bottom>= offset_height + font_height*rows)
1998 HBRUSH fillcolour, oldbrush;
2000 fillcolour = CreateSolidBrush (
2001 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2002 oldbrush = SelectObject(hdc, fillcolour);
2003 edge = CreatePen(PS_SOLID, 0,
2004 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2005 oldpen = SelectObject(hdc, edge);
2007 ExcludeClipRect(hdc,
2008 offset_width, offset_height,
2009 offset_width+font_width*cols,
2010 offset_height+font_height*rows);
2012 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
2013 p.rcPaint.right, p.rcPaint.bottom);
2015 // SelectClipRgn(hdc, NULL);
2017 SelectObject(hdc, oldbrush);
2018 DeleteObject(fillcolour);
2019 SelectObject(hdc, oldpen);
2022 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
2023 SelectObject(hdc, GetStockObject(WHITE_PEN));
2029 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2030 * but the only one that's likely to try to overload us is FD_READ.
2031 * This means buffering just one is fine.
2033 if (pending_netevent)
2034 enact_pending_netevent();
2036 pending_netevent = TRUE;
2037 pend_netevent_wParam = wParam;
2038 pend_netevent_lParam = lParam;
2039 if (WSAGETSELECTEVENT(lParam) != FD_READ)
2040 enact_pending_netevent();
2042 time(&last_movement);
2046 CreateCaret(hwnd, caretbm, font_width, font_height);
2048 flash_window(0); /* stop */
2060 case WM_ENTERSIZEMOVE:
2061 #ifdef RDB_DEBUG_PATCH
2062 debug((27, "WM_ENTERSIZEMOVE"));
2066 need_backend_resize = FALSE;
2068 case WM_EXITSIZEMOVE:
2071 #ifdef RDB_DEBUG_PATCH
2072 debug((27, "WM_EXITSIZEMOVE"));
2074 if (need_backend_resize) {
2075 term_size(cfg.height, cfg.width, cfg.savelines);
2076 InvalidateRect(hwnd, NULL, TRUE);
2081 * This does two jobs:
2082 * 1) Keep the sizetip uptodate
2083 * 2) Make sure the window size is _stepped_ in units of the font size.
2085 if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
2086 int width, height, w, h, ew, eh;
2087 LPRECT r = (LPRECT) lParam;
2089 if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
2090 (cfg.height != rows || cfg.width != cols )) {
2092 * Great! It seems that both the terminal size and the
2093 * font size have been changed and the user is now dragging.
2095 * It will now be difficult to get back to the configured
2098 * This would be easier but it seems to be too confusing.
2100 term_size(cfg.height, cfg.width, cfg.savelines);
2103 cfg.height=rows; cfg.width=cols;
2105 InvalidateRect(hwnd, NULL, TRUE);
2106 need_backend_resize = TRUE;
2109 width = r->right - r->left - extra_width;
2110 height = r->bottom - r->top - extra_height;
2111 w = (width + font_width / 2) / font_width;
2114 h = (height + font_height / 2) / font_height;
2117 UpdateSizeTip(hwnd, w, h);
2118 ew = width - w * font_width;
2119 eh = height - h * font_height;
2121 if (wParam == WMSZ_LEFT ||
2122 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2128 if (wParam == WMSZ_TOP ||
2129 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2139 int width, height, w, h, rv = 0;
2140 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2141 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2142 LPRECT r = (LPRECT) lParam;
2144 width = r->right - r->left - ex_width;
2145 height = r->bottom - r->top - ex_height;
2147 w = (width + cols/2)/cols;
2148 h = (height + rows/2)/rows;
2149 if ( r->right != r->left + w*cols + ex_width)
2152 if (wParam == WMSZ_LEFT ||
2153 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2154 r->left = r->right - w*cols - ex_width;
2156 r->right = r->left + w*cols + ex_width;
2158 if (r->bottom != r->top + h*rows + ex_height)
2161 if (wParam == WMSZ_TOP ||
2162 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2163 r->top = r->bottom - h*rows - ex_height;
2165 r->bottom = r->top + h*rows + ex_height;
2169 /* break; (never reached) */
2170 case WM_FULLSCR_ON_MAX:
2171 fullscr_on_max = TRUE;
2174 #ifdef RDB_DEBUG_PATCH
2175 debug((27, "WM_SIZE %s (%d,%d)",
2176 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2177 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2178 (wParam == SIZE_RESTORED && resizing) ? "to":
2179 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2181 LOWORD(lParam), HIWORD(lParam)));
2183 if (wParam == SIZE_MINIMIZED)
2185 cfg.win_name_always ? window_name : icon_name);
2186 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2187 SetWindowText(hwnd, window_name);
2189 if (cfg.resize_action == RESIZE_DISABLED) {
2190 /* A resize, well it better be a minimize. */
2194 int width, height, w, h;
2196 width = LOWORD(lParam);
2197 height = HIWORD(lParam);
2200 if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
2204 if (cfg.resize_action == RESIZE_TERM) {
2205 w = width / font_width;
2207 h = height / font_height;
2210 term_size(h, w, cfg.savelines);
2214 fullscr_on_max = FALSE;
2216 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2218 clear_full_screen();
2219 if (cfg.resize_action == RESIZE_TERM)
2220 term_size(prev_rows, prev_cols, cfg.savelines);
2221 if (cfg.resize_action != RESIZE_FONT)
2226 /* This is an unexpected resize, these will normally happen
2227 * if the window is too large. Probably either the user
2228 * selected a huge font or the screen size has changed.
2230 * This is also called with minimize.
2232 else reset_window(-1);
2236 * Don't call back->size in mid-resize. (To prevent
2237 * massive numbers of resize events getting sent
2238 * down the connection during an NT opaque drag.)
2241 if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
2242 need_backend_resize = TRUE;
2243 w = (width-cfg.window_border*2) / font_width;
2245 h = (height-cfg.window_border*2) / font_height;
2256 switch (LOWORD(wParam)) {
2270 term_scroll(0, +rows / 2);
2273 term_scroll(0, -rows / 2);
2275 case SB_THUMBPOSITION:
2277 term_scroll(1, HIWORD(wParam));
2281 case WM_PALETTECHANGED:
2282 if ((HWND) wParam != hwnd && pal != NULL) {
2283 HDC hdc = get_ctx();
2285 if (RealizePalette(hdc) > 0)
2291 case WM_QUERYNEWPALETTE:
2293 HDC hdc = get_ctx();
2295 if (RealizePalette(hdc) > 0)
2307 * Add the scan code and keypress timing to the random
2310 noise_ultralight(lParam);
2313 * We don't do TranslateMessage since it disassociates the
2314 * resulting CHAR message from the KEYDOWN that sparked it,
2315 * which we occasionally don't want. Instead, we process
2316 * KEYDOWN, and call the Win32 translator functions so that
2317 * we get the translations under _our_ control.
2320 unsigned char buf[20];
2323 if (wParam == VK_PROCESSKEY) {
2326 m.message = WM_KEYDOWN;
2328 m.lParam = lParam & 0xdfff;
2329 TranslateMessage(&m);
2331 len = TranslateKey(message, wParam, lParam, buf);
2333 return DefWindowProc(hwnd, message, wParam, lParam);
2337 * Interrupt an ongoing paste. I'm not sure
2338 * this is sensible, but for the moment it's
2339 * preferable to having to faff about buffering
2345 * We need not bother about stdin backlogs
2346 * here, because in GUI PuTTY we can't do
2347 * anything about it anyway; there's no means
2348 * of asking Windows to hold off on KEYDOWN
2349 * messages. We _have_ to buffer everything
2352 ldisc_send(buf, len, 1);
2357 net_pending_errors();
2359 case WM_INPUTLANGCHANGE:
2360 /* wParam == Font number */
2361 /* lParam == Locale */
2362 set_input_locale((HKL)lParam);
2365 if(wParam == IMN_SETOPENSTATUS) {
2366 HIMC hImc = ImmGetContext(hwnd);
2367 ImmSetCompositionFont(hImc, &lfont);
2368 ImmReleaseContext(hwnd, hImc);
2372 case WM_IME_COMPOSITION:
2378 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2379 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2381 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2382 break; /* fall back to DefWindowProc */
2384 hIMC = ImmGetContext(hwnd);
2385 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2388 buff = (char*) smalloc(n);
2389 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2390 luni_send((unsigned short *)buff, n / 2, 1);
2393 ImmReleaseContext(hwnd, hIMC);
2398 if (wParam & 0xFF00) {
2399 unsigned char buf[2];
2402 buf[0] = wParam >> 8;
2403 lpage_send(kbd_codepage, buf, 2, 1);
2405 char c = (unsigned char) wParam;
2406 lpage_send(kbd_codepage, &c, 1, 1);
2412 * Nevertheless, we are prepared to deal with WM_CHAR
2413 * messages, should they crop up. So if someone wants to
2414 * post the things to us as part of a macro manoeuvre,
2415 * we're ready to cope.
2418 char c = (unsigned char)wParam;
2419 lpage_send(CP_ACP, &c, 1, 1);
2423 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2424 SetCursor(LoadCursor(NULL, IDC_ARROW));
2429 return DefWindowProc(hwnd, message, wParam, lParam);
2433 * Move the system caret. (We maintain one, even though it's
2434 * invisible, for the benefit of blind people: apparently some
2435 * helper software tracks the system caret, so we should arrange to
2438 void sys_cursor(int x, int y)
2443 if (!has_focus) return;
2445 SetCaretPos(x * font_width + offset_width,
2446 y * font_height + offset_height);
2448 /* IMM calls on Win98 and beyond only */
2449 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2451 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2452 osVersion.dwMinorVersion == 0) return; /* 95 */
2454 /* we should have the IMM functions */
2455 hIMC = ImmGetContext(hwnd);
2456 cf.dwStyle = CFS_POINT;
2457 cf.ptCurrentPos.x = x * font_width + offset_width;
2458 cf.ptCurrentPos.y = y * font_height + offset_height;
2459 ImmSetCompositionWindow(hIMC, &cf);
2461 ImmReleaseContext(hwnd, hIMC);
2465 * Draw a line of text in the window, at given character
2466 * coordinates, in given attributes.
2468 * We are allowed to fiddle with the contents of `text'.
2470 void do_text(Context ctx, int x, int y, char *text, int len,
2471 unsigned long attr, int lattr)
2474 int nfg, nbg, nfont;
2477 int force_manual_underline = 0;
2478 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2479 int char_width = fnt_width;
2480 int text_adjust = 0;
2481 static int *IpDx = 0, IpDxLEN = 0;
2483 if (attr & ATTR_WIDE)
2486 if (len > IpDxLEN || IpDx[0] != char_width) {
2488 if (len > IpDxLEN) {
2490 IpDx = smalloc((len + 16) * sizeof(int));
2491 IpDxLEN = (len + 16);
2493 for (i = 0; i < IpDxLEN; i++)
2494 IpDx[i] = char_width;
2497 /* Only want the left half of double width lines */
2498 if (lattr != LATTR_NORM && x*2 >= cols)
2506 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2507 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2508 attr ^= ATTR_CUR_XOR;
2512 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2513 /* Assume a poorman font is borken in other ways too. */
2523 nfont |= FONT_WIDE + FONT_HIGH;
2526 if (attr & ATTR_NARROW)
2527 nfont |= FONT_NARROW;
2529 /* Special hack for the VT100 linedraw glyphs. */
2530 if ((attr & CSET_MASK) == 0x2300) {
2531 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2532 switch ((unsigned char) (text[0])) {
2534 text_adjust = -2 * font_height / 5;
2537 text_adjust = -1 * font_height / 5;
2540 text_adjust = font_height / 5;
2543 text_adjust = 2 * font_height / 5;
2546 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2549 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2550 attr |= (unitab_xterm['q'] & CSET_MASK);
2551 if (attr & ATTR_UNDER) {
2552 attr &= ~ATTR_UNDER;
2553 force_manual_underline = 1;
2558 /* Anything left as an original character set is unprintable. */
2559 if (DIRECT_CHAR(attr)) {
2562 memset(text, 0xFD, len);
2566 if ((attr & CSET_MASK) == ATTR_OEMCP)
2569 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2570 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2571 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2573 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2574 nfont |= FONT_UNDERLINE;
2575 another_font(nfont);
2576 if (!fonts[nfont]) {
2577 if (nfont & FONT_UNDERLINE)
2578 force_manual_underline = 1;
2579 /* Don't do the same for manual bold, it could be bad news. */
2581 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2583 another_font(nfont);
2585 nfont = FONT_NORMAL;
2586 if (attr & ATTR_REVERSE) {
2591 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2593 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2597 SelectObject(hdc, fonts[nfont]);
2598 SetTextColor(hdc, fg);
2599 SetBkColor(hdc, bg);
2600 SetBkMode(hdc, OPAQUE);
2603 line_box.right = x + char_width * len;
2604 line_box.bottom = y + font_height;
2606 /* Only want the left half of double width lines */
2607 if (line_box.right > font_width*cols+offset_width)
2608 line_box.right = font_width*cols+offset_width;
2610 /* We're using a private area for direct to font. (512 chars.) */
2611 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2612 /* Ho Hum, dbcs fonts are a PITA! */
2613 /* To display on W9x I have to convert to UCS */
2614 static wchar_t *uni_buf = 0;
2615 static int uni_len = 0;
2617 if (len > uni_len) {
2619 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2622 for(nlen = mptr = 0; mptr<len; mptr++) {
2623 uni_buf[nlen] = 0xFFFD;
2624 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2625 IpDx[nlen] += char_width;
2626 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2627 text+mptr, 2, uni_buf+nlen, 1);
2632 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2633 text+mptr, 1, uni_buf+nlen, 1);
2641 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2642 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2643 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2644 SetBkMode(hdc, TRANSPARENT);
2645 ExtTextOutW(hdc, x - 1,
2646 y - font_height * (lattr ==
2647 LATTR_BOT) + text_adjust,
2648 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2652 } else if (DIRECT_FONT(attr)) {
2654 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2655 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2656 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2657 SetBkMode(hdc, TRANSPARENT);
2659 /* GRR: This draws the character outside it's box and can leave
2660 * 'droppings' even with the clip box! I suppose I could loop it
2661 * one character at a time ... yuk.
2663 * Or ... I could do a test print with "W", and use +1 or -1 for this
2664 * shift depending on if the leftmost column is blank...
2666 ExtTextOut(hdc, x - 1,
2667 y - font_height * (lattr ==
2668 LATTR_BOT) + text_adjust,
2669 ETO_CLIPPED, &line_box, text, len, IpDx);
2672 /* And 'normal' unicode characters */
2673 static WCHAR *wbuf = NULL;
2674 static int wlen = 0;
2679 wbuf = smalloc(wlen * sizeof(WCHAR));
2681 for (i = 0; i < len; i++)
2682 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2685 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2686 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2688 /* And the shadow bold hack. */
2689 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2690 SetBkMode(hdc, TRANSPARENT);
2691 ExtTextOutW(hdc, x - 1,
2692 y - font_height * (lattr ==
2693 LATTR_BOT) + text_adjust,
2694 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2697 if (lattr != LATTR_TOP && (force_manual_underline ||
2698 (und_mode == UND_LINE
2699 && (attr & ATTR_UNDER)))) {
2702 if (lattr == LATTR_BOT)
2703 dec = dec * 2 - font_height;
2705 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2706 MoveToEx(hdc, x, y + dec, NULL);
2707 LineTo(hdc, x + len * char_width, y + dec);
2708 oldpen = SelectObject(hdc, oldpen);
2709 DeleteObject(oldpen);
2713 void do_cursor(Context ctx, int x, int y, char *text, int len,
2714 unsigned long attr, int lattr)
2720 int ctype = cfg.cursor_type;
2722 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2723 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2724 do_text(ctx, x, y, text, len, attr, lattr);
2728 attr |= TATTR_RIGHTCURS;
2731 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2732 if (attr & ATTR_WIDE)
2739 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2742 pts[0].x = pts[1].x = pts[4].x = x;
2743 pts[2].x = pts[3].x = x + char_width - 1;
2744 pts[0].y = pts[3].y = pts[4].y = y;
2745 pts[1].y = pts[2].y = y + font_height - 1;
2746 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2747 Polyline(hdc, pts, 5);
2748 oldpen = SelectObject(hdc, oldpen);
2749 DeleteObject(oldpen);
2750 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2751 int startx, starty, dx, dy, length, i;
2754 starty = y + descent;
2757 length = char_width;
2760 if (attr & TATTR_RIGHTCURS)
2761 xadjust = char_width - 1;
2762 startx = x + xadjust;
2766 length = font_height;
2768 if (attr & TATTR_ACTCURS) {
2771 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2772 MoveToEx(hdc, startx, starty, NULL);
2773 LineTo(hdc, startx + dx * length, starty + dy * length);
2774 oldpen = SelectObject(hdc, oldpen);
2775 DeleteObject(oldpen);
2777 for (i = 0; i < length; i++) {
2779 SetPixel(hdc, startx, starty, colours[23]);
2788 /* This function gets the actual width of a character in the normal font.
2790 int CharWidth(Context ctx, int uc) {
2794 /* If the font max is the same as the font ave width then this
2795 * function is a no-op.
2797 if (!font_dualwidth) return 1;
2799 switch (uc & CSET_MASK) {
2801 uc = unitab_line[uc & 0xFF];
2804 uc = unitab_xterm[uc & 0xFF];
2807 uc = unitab_scoacs[uc & 0xFF];
2810 if (DIRECT_FONT(uc)) {
2811 if (dbcs_screenfont) return 1;
2813 /* Speedup, I know of no font where ascii is the wrong width */
2814 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2817 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2818 SelectObject(hdc, fonts[FONT_NORMAL]);
2819 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2820 another_font(FONT_OEM);
2821 if (!fonts[FONT_OEM]) return 0;
2823 SelectObject(hdc, fonts[FONT_OEM]);
2827 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2828 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2831 /* Speedup, I know of no font where ascii is the wrong width */
2832 if (uc >= ' ' && uc <= '~') return 1;
2834 SelectObject(hdc, fonts[FONT_NORMAL]);
2835 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2836 /* Okay that one worked */ ;
2837 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2838 /* This should work on 9x too, but it's "less accurate" */ ;
2843 ibuf += font_width / 2 -1;
2850 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2851 * codes. Returns number of bytes used or zero to drop the message
2852 * or -1 to forward the message to windows.
2854 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2855 unsigned char *output)
2858 int scan, left_alt = 0, key_down, shift_state;
2860 unsigned char *p = output;
2861 static int alt_sum = 0;
2863 HKL kbd_layout = GetKeyboardLayout(0);
2865 static WORD keys[3];
2866 static int compose_char = 0;
2867 static WPARAM compose_key = 0;
2869 r = GetKeyboardState(keystate);
2871 memset(keystate, 0, sizeof(keystate));
2874 #define SHOW_TOASCII_RESULT
2875 { /* Tell us all about key events */
2876 static BYTE oldstate[256];
2877 static int first = 1;
2881 memcpy(oldstate, keystate, sizeof(oldstate));
2884 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2886 } else if ((HIWORD(lParam) & KF_UP)
2887 && scan == (HIWORD(lParam) & 0xFF)) {
2891 if (wParam >= VK_F1 && wParam <= VK_F20)
2892 debug(("K_F%d", wParam + 1 - VK_F1));
2905 debug(("VK_%02x", wParam));
2907 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2909 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2911 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2912 if (ch >= ' ' && ch <= '~')
2913 debug((", '%c'", ch));
2915 debug((", $%02x", ch));
2918 debug((", KB0=%02x", keys[0]));
2920 debug((", KB1=%02x", keys[1]));
2922 debug((", KB2=%02x", keys[2]));
2924 if ((keystate[VK_SHIFT] & 0x80) != 0)
2926 if ((keystate[VK_CONTROL] & 0x80) != 0)
2928 if ((HIWORD(lParam) & KF_EXTENDED))
2930 if ((HIWORD(lParam) & KF_UP))
2934 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2935 else if ((HIWORD(lParam) & KF_UP))
2936 oldstate[wParam & 0xFF] ^= 0x80;
2938 oldstate[wParam & 0xFF] ^= 0x81;
2940 for (ch = 0; ch < 256; ch++)
2941 if (oldstate[ch] != keystate[ch])
2942 debug((", M%02x=%02x", ch, keystate[ch]));
2944 memcpy(oldstate, keystate, sizeof(oldstate));
2948 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2949 keystate[VK_RMENU] = keystate[VK_MENU];
2953 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2954 if ((cfg.funky_type == 3 ||
2955 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2956 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2958 wParam = VK_EXECUTE;
2960 /* UnToggle NUMLock */
2961 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2962 keystate[VK_NUMLOCK] ^= 1;
2965 /* And write back the 'adjusted' state */
2966 SetKeyboardState(keystate);
2969 /* Disable Auto repeat if required */
2970 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2973 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2976 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2978 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2979 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2980 if (cfg.ctrlaltkeys)
2981 keystate[VK_MENU] = 0;
2983 keystate[VK_RMENU] = 0x80;
2988 alt_pressed = (left_alt && key_down);
2990 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2991 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2992 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2994 /* Note if AltGr was pressed and if it was used as a compose key */
2995 if (!compose_state) {
2996 compose_key = 0x100;
2997 if (cfg.compose_key) {
2998 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2999 compose_key = wParam;
3001 if (wParam == VK_APPS)
3002 compose_key = wParam;
3005 if (wParam == compose_key) {
3006 if (compose_state == 0
3007 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
3009 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
3013 } else if (compose_state == 1 && wParam != VK_CONTROL)
3017 * Record that we pressed key so the scroll window can be reset, but
3018 * be careful to avoid Shift-UP/Down
3020 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
3021 wParam != VK_MENU && wParam != VK_CONTROL) {
3025 if (compose_state > 1 && left_alt)
3028 /* Sanitize the number pad if not using a PC NumPad */
3029 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
3030 && cfg.funky_type != 2)
3031 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
3032 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
3036 nParam = VK_NUMPAD0;
3039 nParam = VK_NUMPAD1;
3042 nParam = VK_NUMPAD2;
3045 nParam = VK_NUMPAD3;
3048 nParam = VK_NUMPAD4;
3051 nParam = VK_NUMPAD5;
3054 nParam = VK_NUMPAD6;
3057 nParam = VK_NUMPAD7;
3060 nParam = VK_NUMPAD8;
3063 nParam = VK_NUMPAD9;
3066 nParam = VK_DECIMAL;
3070 if (keystate[VK_NUMLOCK] & 1)
3077 /* If a key is pressed and AltGr is not active */
3078 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3079 /* Okay, prepare for most alts then ... */
3083 /* Lets see if it's a pattern we know all about ... */
3084 if (wParam == VK_PRIOR && shift_state == 1) {
3085 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3088 if (wParam == VK_NEXT && shift_state == 1) {
3089 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3092 if (wParam == VK_INSERT && shift_state == 1) {
3096 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
3099 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
3100 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3103 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3104 (cfg.resize_action != RESIZE_DISABLED)) {
3105 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3109 /* Control-Numlock for app-keypad mode switch */
3110 if (wParam == VK_PAUSE && shift_state == 2) {
3111 app_keypad_keys ^= 1;
3115 /* Nethack keypad */
3116 if (cfg.nethack_keypad && !left_alt) {
3119 *p++ = shift_state ? 'B' : 'b';
3122 *p++ = shift_state ? 'J' : 'j';
3125 *p++ = shift_state ? 'N' : 'n';
3128 *p++ = shift_state ? 'H' : 'h';
3131 *p++ = shift_state ? '.' : '.';
3134 *p++ = shift_state ? 'L' : 'l';
3137 *p++ = shift_state ? 'Y' : 'y';
3140 *p++ = shift_state ? 'K' : 'k';
3143 *p++ = shift_state ? 'U' : 'u';
3148 /* Application Keypad */
3152 if (cfg.funky_type == 3 ||
3153 (cfg.funky_type <= 1 &&
3154 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3168 if (app_keypad_keys && !cfg.no_applic_k)
3205 if (cfg.funky_type == 2) {
3210 } else if (shift_state)
3217 if (cfg.funky_type == 2)
3221 if (cfg.funky_type == 2)
3225 if (cfg.funky_type == 2)
3230 if (HIWORD(lParam) & KF_EXTENDED)
3236 if (xkey >= 'P' && xkey <= 'S')
3237 p += sprintf((char *) p, "\x1B%c", xkey);
3239 p += sprintf((char *) p, "\x1B?%c", xkey);
3241 p += sprintf((char *) p, "\x1BO%c", xkey);
3246 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
3247 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3251 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3257 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3261 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3265 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3270 if (wParam == VK_PAUSE) { /* Break/Pause */
3275 /* Control-2 to Control-8 are special */
3276 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3277 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3280 if (shift_state == 2 && wParam == 0xBD) {
3284 if (shift_state == 2 && wParam == 0xDF) {
3288 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3295 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3296 * for integer decimal nn.)
3298 * We also deal with the weird ones here. Linux VCs replace F1
3299 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3300 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3306 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3309 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3312 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3315 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3318 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3321 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3324 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3327 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3330 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3333 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3366 if ((shift_state&2) == 0) switch (wParam) {
3386 /* Reorder edit keys to physical order */
3387 if (cfg.funky_type == 3 && code <= 6)
3388 code = "\0\2\1\4\5\3\6"[code];
3390 if (vt52_mode && code > 0 && code <= 6) {
3391 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3395 if (cfg.funky_type == 5 && /* SCO function keys */
3396 code >= 11 && code <= 34) {
3397 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3400 case VK_F1: index = 0; break;
3401 case VK_F2: index = 1; break;
3402 case VK_F3: index = 2; break;
3403 case VK_F4: index = 3; break;
3404 case VK_F5: index = 4; break;
3405 case VK_F6: index = 5; break;
3406 case VK_F7: index = 6; break;
3407 case VK_F8: index = 7; break;
3408 case VK_F9: index = 8; break;
3409 case VK_F10: index = 9; break;
3410 case VK_F11: index = 10; break;
3411 case VK_F12: index = 11; break;
3413 if (keystate[VK_SHIFT] & 0x80) index += 12;
3414 if (keystate[VK_CONTROL] & 0x80) index += 24;
3415 p += sprintf((char *) p, "\x1B[%c", codes[index]);
3418 if (cfg.funky_type == 5 && /* SCO small keypad */
3419 code >= 1 && code <= 6) {
3420 char codes[] = "HL.FIG";
3424 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3428 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3435 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3438 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3441 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3442 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3445 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3447 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3449 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3452 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3453 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3457 p += sprintf((char *) p, "\x1B[%d~", code);
3462 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3463 * some reason seems to send VK_CLEAR to Windows...).
3486 p += sprintf((char *) p, "\x1B%c", xkey);
3488 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3489 /* VT100 & VT102 manuals both state the app cursor keys
3490 * only work if the app keypad is on.
3492 if (!app_keypad_keys)
3494 /* Useful mapping of Ctrl-arrows */
3495 if (shift_state == 2)
3499 p += sprintf((char *) p, "\x1BO%c", xkey);
3501 p += sprintf((char *) p, "\x1B[%c", xkey);
3508 * Finally, deal with Return ourselves. (Win95 seems to
3509 * foul it up when Alt is pressed, for some reason.)
3511 if (wParam == VK_RETURN) { /* Return */
3517 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3518 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3523 /* Okay we've done everything interesting; let windows deal with
3524 * the boring stuff */
3528 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3529 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3531 keystate[VK_CAPITAL] = 0;
3534 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3535 #ifdef SHOW_TOASCII_RESULT
3536 if (r == 1 && !key_down) {
3538 if (in_utf || dbcs_screenfont)
3539 debug((", (U+%04x)", alt_sum));
3541 debug((", LCH(%d)", alt_sum));
3543 debug((", ACH(%d)", keys[0]));
3548 for (r1 = 0; r1 < r; r1++) {
3549 debug(("%s%d", r1 ? "," : "", keys[r1]));
3558 * Interrupt an ongoing paste. I'm not sure this is
3559 * sensible, but for the moment it's preferable to
3560 * having to faff about buffering things.
3565 for (i = 0; i < r; i++) {
3566 unsigned char ch = (unsigned char) keys[i];
3568 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3573 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3577 if ((nc = check_compose(compose_char, ch)) == -1) {
3578 MessageBeep(MB_ICONHAND);
3582 luni_send(&keybuf, 1, 1);
3590 if (in_utf || dbcs_screenfont) {
3592 luni_send(&keybuf, 1, 1);
3594 ch = (char) alt_sum;
3596 * We need not bother about stdin
3597 * backlogs here, because in GUI PuTTY
3598 * we can't do anything about it
3599 * anyway; there's no means of asking
3600 * Windows to hold off on KEYDOWN
3601 * messages. We _have_ to buffer
3602 * everything we're sent.
3604 ldisc_send(&ch, 1, 1);
3608 lpage_send(kbd_codepage, &ch, 1, 1);
3610 if(capsOn && ch < 0x80) {
3613 cbuf[1] = xlat_uskbd2cyrllic(ch);
3614 luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3619 lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3625 /* This is so the ALT-Numpad and dead keys work correctly. */
3630 /* If we're definitly not building up an ALT-54321 then clear it */
3633 /* If we will be using alt_sum fix the 256s */
3634 else if (keys[0] && (in_utf || dbcs_screenfont))
3639 * ALT alone may or may not want to bring up the System menu.
3640 * If it's not meant to, we return 0 on presses or releases of
3641 * ALT, to show that we've swallowed the keystroke. Otherwise
3642 * we return -1, which means Windows will give the keystroke
3643 * its default handling (i.e. bring up the System menu).
3645 if (wParam == VK_MENU && !cfg.alt_only)
3651 void set_title(char *title)
3654 window_name = smalloc(1 + strlen(title));
3655 strcpy(window_name, title);
3656 if (cfg.win_name_always || !IsIconic(hwnd))
3657 SetWindowText(hwnd, title);
3660 void set_icon(char *title)
3663 icon_name = smalloc(1 + strlen(title));
3664 strcpy(icon_name, title);
3665 if (!cfg.win_name_always && IsIconic(hwnd))
3666 SetWindowText(hwnd, title);
3669 void set_sbar(int total, int start, int page)
3673 if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
3676 si.cbSize = sizeof(si);
3677 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3679 si.nMax = total - 1;
3683 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3686 Context get_ctx(void)
3692 SelectPalette(hdc, pal, FALSE);
3698 void free_ctx(Context ctx)
3700 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3701 ReleaseDC(hwnd, ctx);
3704 static void real_palette_set(int n, int r, int g, int b)
3707 logpal->palPalEntry[n].peRed = r;
3708 logpal->palPalEntry[n].peGreen = g;
3709 logpal->palPalEntry[n].peBlue = b;
3710 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3711 colours[n] = PALETTERGB(r, g, b);
3712 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3714 colours[n] = RGB(r, g, b);
3717 void palette_set(int n, int r, int g, int b)
3719 static const int first[21] = {
3720 0, 2, 4, 6, 8, 10, 12, 14,
3721 1, 3, 5, 7, 9, 11, 13, 15,
3724 real_palette_set(first[n], r, g, b);
3726 real_palette_set(first[n] + 1, r, g, b);
3728 HDC hdc = get_ctx();
3729 UnrealizeObject(pal);
3730 RealizePalette(hdc);
3735 void palette_reset(void)
3739 for (i = 0; i < NCOLOURS; i++) {
3741 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3742 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3743 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3744 logpal->palPalEntry[i].peFlags = 0;
3745 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3746 defpal[i].rgbtGreen,
3747 defpal[i].rgbtBlue);
3749 colours[i] = RGB(defpal[i].rgbtRed,
3750 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3755 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3757 RealizePalette(hdc);
3762 void write_aclip(char *data, int len, int must_deselect)
3767 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3770 lock = GlobalLock(clipdata);
3773 memcpy(lock, data, len);
3774 ((unsigned char *) lock)[len] = 0;
3775 GlobalUnlock(clipdata);
3778 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3780 if (OpenClipboard(hwnd)) {
3782 SetClipboardData(CF_TEXT, clipdata);
3785 GlobalFree(clipdata);
3788 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3792 * Note: unlike write_aclip() this will not append a nul.
3794 void write_clip(wchar_t * data, int len, int must_deselect)
3796 HGLOBAL clipdata, clipdata2, clipdata3;
3798 void *lock, *lock2, *lock3;
3800 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3802 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3803 len * sizeof(wchar_t));
3804 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3806 if (!clipdata || !clipdata2) {
3808 GlobalFree(clipdata);
3810 GlobalFree(clipdata2);
3813 if (!(lock = GlobalLock(clipdata)))
3815 if (!(lock2 = GlobalLock(clipdata2)))
3818 memcpy(lock, data, len * sizeof(wchar_t));
3819 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3821 if (cfg.rtf_paste) {
3822 wchar_t unitab[256];
3824 unsigned char *tdata = (unsigned char *)lock2;
3825 wchar_t *udata = (wchar_t *)lock;
3826 int rtflen = 0, uindex = 0, tindex = 0;
3828 int multilen, blen, alen, totallen, i;
3829 char before[16], after[4];
3831 get_unitab(CP_ACP, unitab, 0);
3833 rtfsize = 100 + strlen(cfg.font);
3834 rtf = smalloc(rtfsize);
3835 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3836 GetACP(), cfg.font);
3837 rtflen = strlen(rtf);
3840 * We want to construct a piece of RTF that specifies the
3841 * same Unicode text. To do this we will read back in
3842 * parallel from the Unicode data in `udata' and the
3843 * non-Unicode data in `tdata'. For each character in
3844 * `tdata' which becomes the right thing in `udata' when
3845 * looked up in `unitab', we just copy straight over from
3846 * tdata. For each one that doesn't, we must WCToMB it
3847 * individually and produce a \u escape sequence.
3849 * It would probably be more robust to just bite the bullet
3850 * and WCToMB each individual Unicode character one by one,
3851 * then MBToWC each one back to see if it was an accurate
3852 * translation; but that strikes me as a horrifying number
3853 * of Windows API calls so I want to see if this faster way
3854 * will work. If it screws up badly we can always revert to
3855 * the simple and slow way.
3857 while (tindex < len2 && uindex < len &&
3858 tdata[tindex] && udata[uindex]) {
3859 if (tindex + 1 < len2 &&
3860 tdata[tindex] == '\r' &&
3861 tdata[tindex+1] == '\n') {
3865 if (unitab[tdata[tindex]] == udata[uindex]) {
3871 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
3872 NULL, 0, NULL, NULL);
3873 if (multilen != 1) {
3874 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
3876 alen = 1; strcpy(after, "}");
3878 blen = sprintf(before, "\\u%d", udata[uindex]);
3879 alen = 0; after[0] = '\0';
3882 assert(tindex + multilen <= len2);
3883 totallen = blen + alen;
3884 for (i = 0; i < multilen; i++) {
3885 if (tdata[tindex+i] == '\\' ||
3886 tdata[tindex+i] == '{' ||
3887 tdata[tindex+i] == '}')
3889 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
3890 totallen += 6; /* \par\r\n */
3891 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
3897 if (rtfsize < rtflen + totallen + 3) {
3898 rtfsize = rtflen + totallen + 512;
3899 rtf = srealloc(rtf, rtfsize);
3902 strcpy(rtf + rtflen, before); rtflen += blen;
3903 for (i = 0; i < multilen; i++) {
3904 if (tdata[tindex+i] == '\\' ||
3905 tdata[tindex+i] == '{' ||
3906 tdata[tindex+i] == '}') {
3907 rtf[rtflen++] = '\\';
3908 rtf[rtflen++] = tdata[tindex+i];
3909 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
3910 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
3911 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
3912 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
3914 rtf[rtflen++] = tdata[tindex+i];
3917 strcpy(rtf + rtflen, after); rtflen += alen;
3923 strcpy(rtf + rtflen, "}");
3926 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
3927 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
3929 GlobalUnlock(clipdata3);
3935 GlobalUnlock(clipdata);
3936 GlobalUnlock(clipdata2);
3939 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3941 if (OpenClipboard(hwnd)) {
3943 SetClipboardData(CF_UNICODETEXT, clipdata);
3944 SetClipboardData(CF_TEXT, clipdata2);
3946 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
3949 GlobalFree(clipdata);
3950 GlobalFree(clipdata2);
3954 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3957 void get_clip(wchar_t ** p, int *len)
3959 static HGLOBAL clipdata = NULL;
3960 static wchar_t *converted = 0;
3969 GlobalUnlock(clipdata);
3972 } else if (OpenClipboard(NULL)) {
3973 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3975 *p = GlobalLock(clipdata);
3977 for (p2 = *p; *p2; p2++);
3981 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3985 s = GlobalLock(clipdata);
3986 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3987 *p = converted = smalloc(i * sizeof(wchar_t));
3988 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
4001 * Move `lines' lines from position `from' to position `to' in the
4004 void optimised_move(int to, int from, int lines)
4009 min = (to < from ? to : from);
4010 max = to + from - min;
4012 r.left = offset_width;
4013 r.right = offset_width + cols * font_width;
4014 r.top = offset_height + min * font_height;
4015 r.bottom = offset_height + (max + lines) * font_height;
4016 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
4021 * Print a message box and perform a fatal exit.
4023 void fatalbox(char *fmt, ...)
4029 vsprintf(stuff, fmt, ap);
4031 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
4036 * Manage window caption / taskbar flashing, if enabled.
4037 * 0 = stop, 1 = maintain, 2 = start
4039 static void flash_window(int mode)
4041 static long last_flash = 0;
4042 static int flashing = 0;
4043 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
4046 FlashWindow(hwnd, FALSE);
4050 } else if (mode == 2) {
4053 last_flash = GetTickCount();
4055 FlashWindow(hwnd, TRUE);
4058 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4061 long now = GetTickCount();
4062 long fdiff = now - last_flash;
4063 if (fdiff < 0 || fdiff > 450) {
4065 FlashWindow(hwnd, TRUE); /* toggle */
4076 if (mode == BELL_DEFAULT) {
4078 * For MessageBeep style bells, we want to be careful of
4079 * timing, because they don't have the nice property of
4080 * PlaySound bells that each one cancels the previous
4081 * active one. So we limit the rate to one per 50ms or so.
4083 static long lastbeep = 0;
4086 beepdiff = GetTickCount() - lastbeep;
4087 if (beepdiff >= 0 && beepdiff < 50)
4091 * The above MessageBeep call takes time, so we record the
4092 * time _after_ it finishes rather than before it starts.
4094 lastbeep = GetTickCount();
4095 } else if (mode == BELL_WAVEFILE) {
4096 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
4097 char buf[sizeof(cfg.bell_wavefile) + 80];
4098 sprintf(buf, "Unable to play sound file\n%s\n"
4099 "Using default sound instead", cfg.bell_wavefile);
4100 MessageBox(hwnd, buf, "PuTTY Sound Error",
4101 MB_OK | MB_ICONEXCLAMATION);
4102 cfg.beep = BELL_DEFAULT;
4105 /* Otherwise, either visual bell or disabled; do nothing here */
4107 flash_window(2); /* start */
4112 * Minimise or restore the window in response to a server-side
4115 void set_iconic(int iconic)
4117 if (IsIconic(hwnd)) {
4119 ShowWindow(hwnd, SW_RESTORE);
4122 ShowWindow(hwnd, SW_MINIMIZE);
4127 * Move the window in response to a server-side request.
4129 void move_window(int x, int y)
4131 SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4135 * Move the window to the top or bottom of the z-order in response
4136 * to a server-side request.
4138 void set_zorder(int top)
4140 if (cfg.alwaysontop)
4141 return; /* ignore */
4142 SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4143 SWP_NOMOVE | SWP_NOSIZE);
4147 * Refresh the window in response to a server-side request.
4149 void refresh_window(void)
4151 InvalidateRect(hwnd, NULL, TRUE);
4155 * Maximise or restore the window in response to a server-side
4158 void set_zoomed(int zoomed)
4160 if (IsZoomed(hwnd)) {
4162 ShowWindow(hwnd, SW_RESTORE);
4165 ShowWindow(hwnd, SW_MAXIMIZE);
4170 * Report whether the window is iconic, for terminal reports.
4174 return IsIconic(hwnd);
4178 * Report the window's position, for terminal reports.
4180 void get_window_pos(int *x, int *y)
4183 GetWindowRect(hwnd, &r);
4189 * Report the window's pixel size, for terminal reports.
4191 void get_window_pixels(int *x, int *y)
4194 GetWindowRect(hwnd, &r);
4195 *x = r.right - r.left;
4196 *y = r.bottom - r.top;
4200 * Return the window or icon title.
4202 char *get_window_title(int icon)
4204 return icon ? icon_name : window_name;
4208 * See if we're in full-screen mode.
4210 int is_full_screen()
4212 if (!IsZoomed(hwnd))
4214 if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
4220 * Go full-screen. This should only be called when we are already
4223 void make_full_screen()
4228 assert(IsZoomed(hwnd));
4230 /* Remove the window furniture. */
4231 style = GetWindowLong(hwnd, GWL_STYLE);
4232 style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
4233 if (cfg.scrollbar_in_fullscreen)
4234 style |= WS_VSCROLL;
4236 style &= ~WS_VSCROLL;
4237 SetWindowLong(hwnd, GWL_STYLE, style);
4239 /* Resize ourselves to exactly cover the nearest monitor. */
4240 #ifdef MONITOR_DEFAULTTONEAREST
4244 mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
4245 mi.cbSize = sizeof(mi);
4246 GetMonitorInfo(mon, &mi);
4247 x = mi.rcMonitor.left;
4248 y = mi.rcMonitor.top;
4249 w = mi.rcMonitor.right;
4250 h = mi.rcMonitor.bottom;
4254 w = GetSystemMetrics(SM_CXSCREEN);
4255 h = GetSystemMetrics(SM_CYSCREEN);
4257 SetWindowPos(hwnd, HWND_TOP, x, y, w, h, SWP_FRAMECHANGED);
4259 /* Tick the menu item in the System menu. */
4260 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4265 * Clear the full-screen attributes.
4267 void clear_full_screen()
4269 DWORD oldstyle, style;
4271 /* Reinstate the window furniture. */
4272 style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
4273 style |= WS_CAPTION | WS_BORDER | WS_THICKFRAME;
4275 style |= WS_VSCROLL;
4277 style &= ~WS_VSCROLL;
4278 if (style != oldstyle) {
4279 SetWindowLong(hwnd, GWL_STYLE, style);
4280 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4281 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4285 /* Untick the menu item in the System menu. */
4286 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4291 * Toggle full-screen mode.
4293 void flip_full_screen()
4295 if (is_full_screen()) {
4296 ShowWindow(hwnd, SW_RESTORE);
4297 } else if (IsZoomed(hwnd)) {
4300 SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
4301 ShowWindow(hwnd, SW_MAXIMIZE);