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_EITHER && IsZoomed(hwnd)) ||
1657 cfg.resize_action == RESIZE_DISABLED)
1658 term_size(cfg.height, cfg.width, cfg.savelines);
1660 /* Enable or disable the scroll bar, etc */
1662 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1663 LONG nexflag, exflag =
1664 GetWindowLong(hwnd, GWL_EXSTYLE);
1667 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
1668 if (cfg.alwaysontop) {
1669 nexflag |= WS_EX_TOPMOST;
1670 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1671 SWP_NOMOVE | SWP_NOSIZE);
1673 nexflag &= ~(WS_EX_TOPMOST);
1674 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1675 SWP_NOMOVE | SWP_NOSIZE);
1678 if (cfg.sunken_edge)
1679 nexflag |= WS_EX_CLIENTEDGE;
1681 nexflag &= ~(WS_EX_CLIENTEDGE);
1684 if (is_full_screen() ?
1685 cfg.scrollbar_in_fullscreen : cfg.scrollbar)
1688 nflg &= ~WS_VSCROLL;
1690 if (cfg.resize_action == RESIZE_DISABLED ||
1692 nflg &= ~WS_THICKFRAME;
1694 nflg |= WS_THICKFRAME;
1696 if (cfg.resize_action == RESIZE_DISABLED)
1697 nflg &= ~WS_MAXIMIZEBOX;
1699 nflg |= WS_MAXIMIZEBOX;
1701 if (nflg != flag || nexflag != exflag) {
1703 SetWindowLong(hwnd, GWL_STYLE, nflg);
1704 if (nexflag != exflag)
1705 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1707 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1708 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1709 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1717 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
1722 set_title(cfg.wintitle);
1723 if (IsIconic(hwnd)) {
1725 cfg.win_name_always ? window_name :
1729 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1730 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1731 cfg.fontisbold != prev_cfg.fontisbold ||
1732 cfg.fontheight != prev_cfg.fontheight ||
1733 cfg.fontcharset != prev_cfg.fontcharset ||
1734 cfg.vtmode != prev_cfg.vtmode ||
1735 cfg.bold_colour != prev_cfg.bold_colour ||
1736 cfg.resize_action == RESIZE_DISABLED ||
1737 cfg.resize_action == RESIZE_EITHER ||
1738 (cfg.resize_action != prev_cfg.resize_action))
1741 InvalidateRect(hwnd, NULL, TRUE);
1742 reset_window(init_lvl);
1743 net_pending_errors();
1756 back->special(TS_AYT);
1757 net_pending_errors();
1760 back->special(TS_BRK);
1761 net_pending_errors();
1764 back->special(TS_SYNCH);
1765 net_pending_errors();
1768 back->special(TS_EC);
1769 net_pending_errors();
1772 back->special(TS_EL);
1773 net_pending_errors();
1776 back->special(TS_GA);
1777 net_pending_errors();
1780 back->special(TS_NOP);
1781 net_pending_errors();
1784 back->special(TS_ABORT);
1785 net_pending_errors();
1788 back->special(TS_AO);
1789 net_pending_errors();
1792 back->special(TS_IP);
1793 net_pending_errors();
1796 back->special(TS_SUSP);
1797 net_pending_errors();
1800 back->special(TS_EOR);
1801 net_pending_errors();
1804 back->special(TS_EOF);
1805 net_pending_errors();
1811 WinHelp(hwnd, help_path,
1812 help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0);
1816 * We get this if the System menu has been activated
1823 * We get this if the System menu has been activated
1824 * using the keyboard. This might happen from within
1825 * TranslateKey, in which case it really wants to be
1826 * followed by a `space' character to actually _bring
1827 * the menu up_ rather than just sitting there in
1828 * `ready to appear' state.
1830 show_mouseptr(1); /* make sure pointer is visible */
1832 PostMessage(hwnd, WM_CHAR, ' ', 0);
1834 case IDM_FULLSCREEN:
1838 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1839 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1844 #define X_POS(l) ((int)(short)LOWORD(l))
1845 #define Y_POS(l) ((int)(short)HIWORD(l))
1847 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1848 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1849 #define WHEEL_DELTA 120
1852 wheel_accumulator += (short) HIWORD(wParam);
1853 wParam = LOWORD(wParam);
1855 /* process events when the threshold is reached */
1856 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1859 /* reduce amount for next time */
1860 if (wheel_accumulator > 0) {
1862 wheel_accumulator -= WHEEL_DELTA;
1863 } else if (wheel_accumulator < 0) {
1865 wheel_accumulator += WHEEL_DELTA;
1869 if (send_raw_mouse) {
1870 /* send a mouse-down followed by a mouse up */
1874 TO_CHR_X(X_POS(lParam)),
1875 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1876 wParam & MK_CONTROL, is_alt_pressed());
1877 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1878 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1879 wParam & MK_CONTROL, is_alt_pressed());
1881 /* trigger a scroll */
1883 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1888 case WM_LBUTTONDOWN:
1889 case WM_MBUTTONDOWN:
1890 case WM_RBUTTONDOWN:
1898 case WM_LBUTTONDOWN:
1902 case WM_MBUTTONDOWN:
1903 button = MBT_MIDDLE;
1906 case WM_RBUTTONDOWN:
1915 button = MBT_MIDDLE;
1923 button = press = 0; /* shouldn't happen */
1927 * Special case: in full-screen mode, if the left
1928 * button is clicked in the very top left corner of the
1929 * window, we put up the System menu instead of doing
1932 if (is_full_screen() && press && button == MBT_LEFT &&
1933 X_POS(lParam) == 0 && Y_POS(lParam) == 0) {
1934 SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0);
1939 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1940 wParam & MK_SHIFT, wParam & MK_CONTROL,
1944 term_mouse(button, MA_RELEASE,
1945 TO_CHR_X(X_POS(lParam)),
1946 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1947 wParam & MK_CONTROL, is_alt_pressed());
1955 * Add the mouse position and message time to the random
1958 noise_ultralight(lParam);
1960 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1962 if (wParam & MK_LBUTTON)
1964 else if (wParam & MK_MBUTTON)
1968 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1969 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1970 wParam & MK_CONTROL, is_alt_pressed());
1973 case WM_NCMOUSEMOVE:
1975 noise_ultralight(lParam);
1977 case WM_IGNORE_CLIP:
1978 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1980 case WM_DESTROYCLIPBOARD:
1983 ignore_clip = FALSE;
1989 hdc = BeginPaint(hwnd, &p);
1991 SelectPalette(hdc, pal, TRUE);
1992 RealizePalette(hdc);
1995 (p.rcPaint.left-offset_width)/font_width,
1996 (p.rcPaint.top-offset_height)/font_height,
1997 (p.rcPaint.right-offset_width-1)/font_width,
1998 (p.rcPaint.bottom-offset_height-1)/font_height);
2001 p.rcPaint.left < offset_width ||
2002 p.rcPaint.top < offset_height ||
2003 p.rcPaint.right >= offset_width + font_width*cols ||
2004 p.rcPaint.bottom>= offset_height + font_height*rows)
2006 HBRUSH fillcolour, oldbrush;
2008 fillcolour = CreateSolidBrush (
2009 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2010 oldbrush = SelectObject(hdc, fillcolour);
2011 edge = CreatePen(PS_SOLID, 0,
2012 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2013 oldpen = SelectObject(hdc, edge);
2015 ExcludeClipRect(hdc,
2016 offset_width, offset_height,
2017 offset_width+font_width*cols,
2018 offset_height+font_height*rows);
2020 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
2021 p.rcPaint.right, p.rcPaint.bottom);
2023 // SelectClipRgn(hdc, NULL);
2025 SelectObject(hdc, oldbrush);
2026 DeleteObject(fillcolour);
2027 SelectObject(hdc, oldpen);
2030 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
2031 SelectObject(hdc, GetStockObject(WHITE_PEN));
2037 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2038 * but the only one that's likely to try to overload us is FD_READ.
2039 * This means buffering just one is fine.
2041 if (pending_netevent)
2042 enact_pending_netevent();
2044 pending_netevent = TRUE;
2045 pend_netevent_wParam = wParam;
2046 pend_netevent_lParam = lParam;
2047 if (WSAGETSELECTEVENT(lParam) != FD_READ)
2048 enact_pending_netevent();
2050 time(&last_movement);
2054 CreateCaret(hwnd, caretbm, font_width, font_height);
2056 flash_window(0); /* stop */
2068 case WM_ENTERSIZEMOVE:
2069 #ifdef RDB_DEBUG_PATCH
2070 debug((27, "WM_ENTERSIZEMOVE"));
2074 need_backend_resize = FALSE;
2076 case WM_EXITSIZEMOVE:
2079 #ifdef RDB_DEBUG_PATCH
2080 debug((27, "WM_EXITSIZEMOVE"));
2082 if (need_backend_resize) {
2083 term_size(cfg.height, cfg.width, cfg.savelines);
2084 InvalidateRect(hwnd, NULL, TRUE);
2089 * This does two jobs:
2090 * 1) Keep the sizetip uptodate
2091 * 2) Make sure the window size is _stepped_ in units of the font size.
2093 if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
2094 int width, height, w, h, ew, eh;
2095 LPRECT r = (LPRECT) lParam;
2097 if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
2098 (cfg.height != rows || cfg.width != cols )) {
2100 * Great! It seems that both the terminal size and the
2101 * font size have been changed and the user is now dragging.
2103 * It will now be difficult to get back to the configured
2106 * This would be easier but it seems to be too confusing.
2108 term_size(cfg.height, cfg.width, cfg.savelines);
2111 cfg.height=rows; cfg.width=cols;
2113 InvalidateRect(hwnd, NULL, TRUE);
2114 need_backend_resize = TRUE;
2117 width = r->right - r->left - extra_width;
2118 height = r->bottom - r->top - extra_height;
2119 w = (width + font_width / 2) / font_width;
2122 h = (height + font_height / 2) / font_height;
2125 UpdateSizeTip(hwnd, w, h);
2126 ew = width - w * font_width;
2127 eh = height - h * font_height;
2129 if (wParam == WMSZ_LEFT ||
2130 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2136 if (wParam == WMSZ_TOP ||
2137 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2147 int width, height, w, h, rv = 0;
2148 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2149 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2150 LPRECT r = (LPRECT) lParam;
2152 width = r->right - r->left - ex_width;
2153 height = r->bottom - r->top - ex_height;
2155 w = (width + cols/2)/cols;
2156 h = (height + rows/2)/rows;
2157 if ( r->right != r->left + w*cols + ex_width)
2160 if (wParam == WMSZ_LEFT ||
2161 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2162 r->left = r->right - w*cols - ex_width;
2164 r->right = r->left + w*cols + ex_width;
2166 if (r->bottom != r->top + h*rows + ex_height)
2169 if (wParam == WMSZ_TOP ||
2170 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2171 r->top = r->bottom - h*rows - ex_height;
2173 r->bottom = r->top + h*rows + ex_height;
2177 /* break; (never reached) */
2178 case WM_FULLSCR_ON_MAX:
2179 fullscr_on_max = TRUE;
2182 #ifdef RDB_DEBUG_PATCH
2183 debug((27, "WM_SIZE %s (%d,%d)",
2184 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2185 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2186 (wParam == SIZE_RESTORED && resizing) ? "to":
2187 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2189 LOWORD(lParam), HIWORD(lParam)));
2191 if (wParam == SIZE_MINIMIZED)
2193 cfg.win_name_always ? window_name : icon_name);
2194 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2195 SetWindowText(hwnd, window_name);
2196 if (wParam == SIZE_RESTORED)
2197 clear_full_screen();
2198 if (wParam == SIZE_MAXIMIZED && fullscr_on_max) {
2200 fullscr_on_max = FALSE;
2203 if (cfg.resize_action == RESIZE_DISABLED) {
2204 /* A resize, well it better be a minimize. */
2208 int width, height, w, h;
2210 width = LOWORD(lParam);
2211 height = HIWORD(lParam);
2214 if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
2218 if (cfg.resize_action == RESIZE_TERM) {
2219 w = width / font_width;
2221 h = height / font_height;
2224 term_size(h, w, cfg.savelines);
2227 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2229 if (cfg.resize_action == RESIZE_TERM)
2230 term_size(prev_rows, prev_cols, cfg.savelines);
2231 if (cfg.resize_action != RESIZE_FONT)
2236 /* This is an unexpected resize, these will normally happen
2237 * if the window is too large. Probably either the user
2238 * selected a huge font or the screen size has changed.
2240 * This is also called with minimize.
2242 else reset_window(-1);
2246 * Don't call back->size in mid-resize. (To prevent
2247 * massive numbers of resize events getting sent
2248 * down the connection during an NT opaque drag.)
2251 if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
2252 need_backend_resize = TRUE;
2253 w = (width-cfg.window_border*2) / font_width;
2255 h = (height-cfg.window_border*2) / font_height;
2266 switch (LOWORD(wParam)) {
2280 term_scroll(0, +rows / 2);
2283 term_scroll(0, -rows / 2);
2285 case SB_THUMBPOSITION:
2287 term_scroll(1, HIWORD(wParam));
2291 case WM_PALETTECHANGED:
2292 if ((HWND) wParam != hwnd && pal != NULL) {
2293 HDC hdc = get_ctx();
2295 if (RealizePalette(hdc) > 0)
2301 case WM_QUERYNEWPALETTE:
2303 HDC hdc = get_ctx();
2305 if (RealizePalette(hdc) > 0)
2317 * Add the scan code and keypress timing to the random
2320 noise_ultralight(lParam);
2323 * We don't do TranslateMessage since it disassociates the
2324 * resulting CHAR message from the KEYDOWN that sparked it,
2325 * which we occasionally don't want. Instead, we process
2326 * KEYDOWN, and call the Win32 translator functions so that
2327 * we get the translations under _our_ control.
2330 unsigned char buf[20];
2333 if (wParam == VK_PROCESSKEY) {
2336 m.message = WM_KEYDOWN;
2338 m.lParam = lParam & 0xdfff;
2339 TranslateMessage(&m);
2341 len = TranslateKey(message, wParam, lParam, buf);
2343 return DefWindowProc(hwnd, message, wParam, lParam);
2347 * Interrupt an ongoing paste. I'm not sure
2348 * this is sensible, but for the moment it's
2349 * preferable to having to faff about buffering
2355 * We need not bother about stdin backlogs
2356 * here, because in GUI PuTTY we can't do
2357 * anything about it anyway; there's no means
2358 * of asking Windows to hold off on KEYDOWN
2359 * messages. We _have_ to buffer everything
2362 ldisc_send(buf, len, 1);
2367 net_pending_errors();
2369 case WM_INPUTLANGCHANGE:
2370 /* wParam == Font number */
2371 /* lParam == Locale */
2372 set_input_locale((HKL)lParam);
2375 if(wParam == IMN_SETOPENSTATUS) {
2376 HIMC hImc = ImmGetContext(hwnd);
2377 ImmSetCompositionFont(hImc, &lfont);
2378 ImmReleaseContext(hwnd, hImc);
2382 case WM_IME_COMPOSITION:
2388 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2389 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2391 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2392 break; /* fall back to DefWindowProc */
2394 hIMC = ImmGetContext(hwnd);
2395 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2398 buff = (char*) smalloc(n);
2399 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2400 luni_send((unsigned short *)buff, n / 2, 1);
2403 ImmReleaseContext(hwnd, hIMC);
2408 if (wParam & 0xFF00) {
2409 unsigned char buf[2];
2412 buf[0] = wParam >> 8;
2413 lpage_send(kbd_codepage, buf, 2, 1);
2415 char c = (unsigned char) wParam;
2416 lpage_send(kbd_codepage, &c, 1, 1);
2422 * Nevertheless, we are prepared to deal with WM_CHAR
2423 * messages, should they crop up. So if someone wants to
2424 * post the things to us as part of a macro manoeuvre,
2425 * we're ready to cope.
2428 char c = (unsigned char)wParam;
2429 lpage_send(CP_ACP, &c, 1, 1);
2433 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2434 SetCursor(LoadCursor(NULL, IDC_ARROW));
2439 return DefWindowProc(hwnd, message, wParam, lParam);
2443 * Move the system caret. (We maintain one, even though it's
2444 * invisible, for the benefit of blind people: apparently some
2445 * helper software tracks the system caret, so we should arrange to
2448 void sys_cursor(int x, int y)
2453 if (!has_focus) return;
2455 SetCaretPos(x * font_width + offset_width,
2456 y * font_height + offset_height);
2458 /* IMM calls on Win98 and beyond only */
2459 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2461 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2462 osVersion.dwMinorVersion == 0) return; /* 95 */
2464 /* we should have the IMM functions */
2465 hIMC = ImmGetContext(hwnd);
2466 cf.dwStyle = CFS_POINT;
2467 cf.ptCurrentPos.x = x * font_width + offset_width;
2468 cf.ptCurrentPos.y = y * font_height + offset_height;
2469 ImmSetCompositionWindow(hIMC, &cf);
2471 ImmReleaseContext(hwnd, hIMC);
2475 * Draw a line of text in the window, at given character
2476 * coordinates, in given attributes.
2478 * We are allowed to fiddle with the contents of `text'.
2480 void do_text(Context ctx, int x, int y, char *text, int len,
2481 unsigned long attr, int lattr)
2484 int nfg, nbg, nfont;
2487 int force_manual_underline = 0;
2488 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2489 int char_width = fnt_width;
2490 int text_adjust = 0;
2491 static int *IpDx = 0, IpDxLEN = 0;
2493 if (attr & ATTR_WIDE)
2496 if (len > IpDxLEN || IpDx[0] != char_width) {
2498 if (len > IpDxLEN) {
2500 IpDx = smalloc((len + 16) * sizeof(int));
2501 IpDxLEN = (len + 16);
2503 for (i = 0; i < IpDxLEN; i++)
2504 IpDx[i] = char_width;
2507 /* Only want the left half of double width lines */
2508 if (lattr != LATTR_NORM && x*2 >= cols)
2516 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2517 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2518 attr ^= ATTR_CUR_XOR;
2522 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2523 /* Assume a poorman font is borken in other ways too. */
2533 nfont |= FONT_WIDE + FONT_HIGH;
2536 if (attr & ATTR_NARROW)
2537 nfont |= FONT_NARROW;
2539 /* Special hack for the VT100 linedraw glyphs. */
2540 if ((attr & CSET_MASK) == 0x2300) {
2541 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2542 switch ((unsigned char) (text[0])) {
2544 text_adjust = -2 * font_height / 5;
2547 text_adjust = -1 * font_height / 5;
2550 text_adjust = font_height / 5;
2553 text_adjust = 2 * font_height / 5;
2556 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2559 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2560 attr |= (unitab_xterm['q'] & CSET_MASK);
2561 if (attr & ATTR_UNDER) {
2562 attr &= ~ATTR_UNDER;
2563 force_manual_underline = 1;
2568 /* Anything left as an original character set is unprintable. */
2569 if (DIRECT_CHAR(attr)) {
2572 memset(text, 0xFD, len);
2576 if ((attr & CSET_MASK) == ATTR_OEMCP)
2579 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2580 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2581 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2583 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2584 nfont |= FONT_UNDERLINE;
2585 another_font(nfont);
2586 if (!fonts[nfont]) {
2587 if (nfont & FONT_UNDERLINE)
2588 force_manual_underline = 1;
2589 /* Don't do the same for manual bold, it could be bad news. */
2591 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2593 another_font(nfont);
2595 nfont = FONT_NORMAL;
2596 if (attr & ATTR_REVERSE) {
2601 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2603 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2607 SelectObject(hdc, fonts[nfont]);
2608 SetTextColor(hdc, fg);
2609 SetBkColor(hdc, bg);
2610 SetBkMode(hdc, OPAQUE);
2613 line_box.right = x + char_width * len;
2614 line_box.bottom = y + font_height;
2616 /* Only want the left half of double width lines */
2617 if (line_box.right > font_width*cols+offset_width)
2618 line_box.right = font_width*cols+offset_width;
2620 /* We're using a private area for direct to font. (512 chars.) */
2621 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2622 /* Ho Hum, dbcs fonts are a PITA! */
2623 /* To display on W9x I have to convert to UCS */
2624 static wchar_t *uni_buf = 0;
2625 static int uni_len = 0;
2627 if (len > uni_len) {
2629 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2632 for(nlen = mptr = 0; mptr<len; mptr++) {
2633 uni_buf[nlen] = 0xFFFD;
2634 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2635 IpDx[nlen] += char_width;
2636 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2637 text+mptr, 2, uni_buf+nlen, 1);
2642 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2643 text+mptr, 1, uni_buf+nlen, 1);
2651 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2652 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2653 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2654 SetBkMode(hdc, TRANSPARENT);
2655 ExtTextOutW(hdc, x - 1,
2656 y - font_height * (lattr ==
2657 LATTR_BOT) + text_adjust,
2658 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2662 } else if (DIRECT_FONT(attr)) {
2664 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2665 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2666 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2667 SetBkMode(hdc, TRANSPARENT);
2669 /* GRR: This draws the character outside it's box and can leave
2670 * 'droppings' even with the clip box! I suppose I could loop it
2671 * one character at a time ... yuk.
2673 * Or ... I could do a test print with "W", and use +1 or -1 for this
2674 * shift depending on if the leftmost column is blank...
2676 ExtTextOut(hdc, x - 1,
2677 y - font_height * (lattr ==
2678 LATTR_BOT) + text_adjust,
2679 ETO_CLIPPED, &line_box, text, len, IpDx);
2682 /* And 'normal' unicode characters */
2683 static WCHAR *wbuf = NULL;
2684 static int wlen = 0;
2689 wbuf = smalloc(wlen * sizeof(WCHAR));
2691 for (i = 0; i < len; i++)
2692 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2695 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2696 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2698 /* And the shadow bold hack. */
2699 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2700 SetBkMode(hdc, TRANSPARENT);
2701 ExtTextOutW(hdc, x - 1,
2702 y - font_height * (lattr ==
2703 LATTR_BOT) + text_adjust,
2704 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2707 if (lattr != LATTR_TOP && (force_manual_underline ||
2708 (und_mode == UND_LINE
2709 && (attr & ATTR_UNDER)))) {
2712 if (lattr == LATTR_BOT)
2713 dec = dec * 2 - font_height;
2715 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2716 MoveToEx(hdc, x, y + dec, NULL);
2717 LineTo(hdc, x + len * char_width, y + dec);
2718 oldpen = SelectObject(hdc, oldpen);
2719 DeleteObject(oldpen);
2723 void do_cursor(Context ctx, int x, int y, char *text, int len,
2724 unsigned long attr, int lattr)
2730 int ctype = cfg.cursor_type;
2732 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2733 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2734 do_text(ctx, x, y, text, len, attr, lattr);
2738 attr |= TATTR_RIGHTCURS;
2741 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2742 if (attr & ATTR_WIDE)
2749 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2752 pts[0].x = pts[1].x = pts[4].x = x;
2753 pts[2].x = pts[3].x = x + char_width - 1;
2754 pts[0].y = pts[3].y = pts[4].y = y;
2755 pts[1].y = pts[2].y = y + font_height - 1;
2756 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2757 Polyline(hdc, pts, 5);
2758 oldpen = SelectObject(hdc, oldpen);
2759 DeleteObject(oldpen);
2760 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2761 int startx, starty, dx, dy, length, i;
2764 starty = y + descent;
2767 length = char_width;
2770 if (attr & TATTR_RIGHTCURS)
2771 xadjust = char_width - 1;
2772 startx = x + xadjust;
2776 length = font_height;
2778 if (attr & TATTR_ACTCURS) {
2781 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2782 MoveToEx(hdc, startx, starty, NULL);
2783 LineTo(hdc, startx + dx * length, starty + dy * length);
2784 oldpen = SelectObject(hdc, oldpen);
2785 DeleteObject(oldpen);
2787 for (i = 0; i < length; i++) {
2789 SetPixel(hdc, startx, starty, colours[23]);
2798 /* This function gets the actual width of a character in the normal font.
2800 int CharWidth(Context ctx, int uc) {
2804 /* If the font max is the same as the font ave width then this
2805 * function is a no-op.
2807 if (!font_dualwidth) return 1;
2809 switch (uc & CSET_MASK) {
2811 uc = unitab_line[uc & 0xFF];
2814 uc = unitab_xterm[uc & 0xFF];
2817 uc = unitab_scoacs[uc & 0xFF];
2820 if (DIRECT_FONT(uc)) {
2821 if (dbcs_screenfont) return 1;
2823 /* Speedup, I know of no font where ascii is the wrong width */
2824 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2827 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2828 SelectObject(hdc, fonts[FONT_NORMAL]);
2829 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2830 another_font(FONT_OEM);
2831 if (!fonts[FONT_OEM]) return 0;
2833 SelectObject(hdc, fonts[FONT_OEM]);
2837 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2838 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2841 /* Speedup, I know of no font where ascii is the wrong width */
2842 if (uc >= ' ' && uc <= '~') return 1;
2844 SelectObject(hdc, fonts[FONT_NORMAL]);
2845 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2846 /* Okay that one worked */ ;
2847 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2848 /* This should work on 9x too, but it's "less accurate" */ ;
2853 ibuf += font_width / 2 -1;
2860 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2861 * codes. Returns number of bytes used or zero to drop the message
2862 * or -1 to forward the message to windows.
2864 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2865 unsigned char *output)
2868 int scan, left_alt = 0, key_down, shift_state;
2870 unsigned char *p = output;
2871 static int alt_sum = 0;
2873 HKL kbd_layout = GetKeyboardLayout(0);
2875 static WORD keys[3];
2876 static int compose_char = 0;
2877 static WPARAM compose_key = 0;
2879 r = GetKeyboardState(keystate);
2881 memset(keystate, 0, sizeof(keystate));
2884 #define SHOW_TOASCII_RESULT
2885 { /* Tell us all about key events */
2886 static BYTE oldstate[256];
2887 static int first = 1;
2891 memcpy(oldstate, keystate, sizeof(oldstate));
2894 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2896 } else if ((HIWORD(lParam) & KF_UP)
2897 && scan == (HIWORD(lParam) & 0xFF)) {
2901 if (wParam >= VK_F1 && wParam <= VK_F20)
2902 debug(("K_F%d", wParam + 1 - VK_F1));
2915 debug(("VK_%02x", wParam));
2917 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2919 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2921 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2922 if (ch >= ' ' && ch <= '~')
2923 debug((", '%c'", ch));
2925 debug((", $%02x", ch));
2928 debug((", KB0=%02x", keys[0]));
2930 debug((", KB1=%02x", keys[1]));
2932 debug((", KB2=%02x", keys[2]));
2934 if ((keystate[VK_SHIFT] & 0x80) != 0)
2936 if ((keystate[VK_CONTROL] & 0x80) != 0)
2938 if ((HIWORD(lParam) & KF_EXTENDED))
2940 if ((HIWORD(lParam) & KF_UP))
2944 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2945 else if ((HIWORD(lParam) & KF_UP))
2946 oldstate[wParam & 0xFF] ^= 0x80;
2948 oldstate[wParam & 0xFF] ^= 0x81;
2950 for (ch = 0; ch < 256; ch++)
2951 if (oldstate[ch] != keystate[ch])
2952 debug((", M%02x=%02x", ch, keystate[ch]));
2954 memcpy(oldstate, keystate, sizeof(oldstate));
2958 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2959 keystate[VK_RMENU] = keystate[VK_MENU];
2963 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2964 if ((cfg.funky_type == 3 ||
2965 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2966 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2968 wParam = VK_EXECUTE;
2970 /* UnToggle NUMLock */
2971 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2972 keystate[VK_NUMLOCK] ^= 1;
2975 /* And write back the 'adjusted' state */
2976 SetKeyboardState(keystate);
2979 /* Disable Auto repeat if required */
2980 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2983 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2986 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2988 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2989 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2990 if (cfg.ctrlaltkeys)
2991 keystate[VK_MENU] = 0;
2993 keystate[VK_RMENU] = 0x80;
2998 alt_pressed = (left_alt && key_down);
3000 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
3001 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
3002 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
3004 /* Note if AltGr was pressed and if it was used as a compose key */
3005 if (!compose_state) {
3006 compose_key = 0x100;
3007 if (cfg.compose_key) {
3008 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
3009 compose_key = wParam;
3011 if (wParam == VK_APPS)
3012 compose_key = wParam;
3015 if (wParam == compose_key) {
3016 if (compose_state == 0
3017 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
3019 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
3023 } else if (compose_state == 1 && wParam != VK_CONTROL)
3027 * Record that we pressed key so the scroll window can be reset, but
3028 * be careful to avoid Shift-UP/Down
3030 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
3031 wParam != VK_MENU && wParam != VK_CONTROL) {
3035 if (compose_state > 1 && left_alt)
3038 /* Sanitize the number pad if not using a PC NumPad */
3039 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
3040 && cfg.funky_type != 2)
3041 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
3042 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
3046 nParam = VK_NUMPAD0;
3049 nParam = VK_NUMPAD1;
3052 nParam = VK_NUMPAD2;
3055 nParam = VK_NUMPAD3;
3058 nParam = VK_NUMPAD4;
3061 nParam = VK_NUMPAD5;
3064 nParam = VK_NUMPAD6;
3067 nParam = VK_NUMPAD7;
3070 nParam = VK_NUMPAD8;
3073 nParam = VK_NUMPAD9;
3076 nParam = VK_DECIMAL;
3080 if (keystate[VK_NUMLOCK] & 1)
3087 /* If a key is pressed and AltGr is not active */
3088 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3089 /* Okay, prepare for most alts then ... */
3093 /* Lets see if it's a pattern we know all about ... */
3094 if (wParam == VK_PRIOR && shift_state == 1) {
3095 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3098 if (wParam == VK_NEXT && shift_state == 1) {
3099 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3102 if (wParam == VK_INSERT && shift_state == 1) {
3106 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
3109 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
3110 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3113 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3114 (cfg.resize_action != RESIZE_DISABLED)) {
3115 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3119 /* Control-Numlock for app-keypad mode switch */
3120 if (wParam == VK_PAUSE && shift_state == 2) {
3121 app_keypad_keys ^= 1;
3125 /* Nethack keypad */
3126 if (cfg.nethack_keypad && !left_alt) {
3129 *p++ = shift_state ? 'B' : 'b';
3132 *p++ = shift_state ? 'J' : 'j';
3135 *p++ = shift_state ? 'N' : 'n';
3138 *p++ = shift_state ? 'H' : 'h';
3141 *p++ = shift_state ? '.' : '.';
3144 *p++ = shift_state ? 'L' : 'l';
3147 *p++ = shift_state ? 'Y' : 'y';
3150 *p++ = shift_state ? 'K' : 'k';
3153 *p++ = shift_state ? 'U' : 'u';
3158 /* Application Keypad */
3162 if (cfg.funky_type == 3 ||
3163 (cfg.funky_type <= 1 &&
3164 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3178 if (app_keypad_keys && !cfg.no_applic_k)
3215 if (cfg.funky_type == 2) {
3220 } else if (shift_state)
3227 if (cfg.funky_type == 2)
3231 if (cfg.funky_type == 2)
3235 if (cfg.funky_type == 2)
3240 if (HIWORD(lParam) & KF_EXTENDED)
3246 if (xkey >= 'P' && xkey <= 'S')
3247 p += sprintf((char *) p, "\x1B%c", xkey);
3249 p += sprintf((char *) p, "\x1B?%c", xkey);
3251 p += sprintf((char *) p, "\x1BO%c", xkey);
3256 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
3257 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3261 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3267 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3271 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3275 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3280 if (wParam == VK_PAUSE) { /* Break/Pause */
3285 /* Control-2 to Control-8 are special */
3286 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3287 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3290 if (shift_state == 2 && wParam == 0xBD) {
3294 if (shift_state == 2 && wParam == 0xDF) {
3298 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3305 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3306 * for integer decimal nn.)
3308 * We also deal with the weird ones here. Linux VCs replace F1
3309 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3310 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3316 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3319 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3322 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3325 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3328 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3331 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3334 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3337 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3340 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3343 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3376 if ((shift_state&2) == 0) switch (wParam) {
3396 /* Reorder edit keys to physical order */
3397 if (cfg.funky_type == 3 && code <= 6)
3398 code = "\0\2\1\4\5\3\6"[code];
3400 if (vt52_mode && code > 0 && code <= 6) {
3401 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3405 if (cfg.funky_type == 5 && /* SCO function keys */
3406 code >= 11 && code <= 34) {
3407 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3410 case VK_F1: index = 0; break;
3411 case VK_F2: index = 1; break;
3412 case VK_F3: index = 2; break;
3413 case VK_F4: index = 3; break;
3414 case VK_F5: index = 4; break;
3415 case VK_F6: index = 5; break;
3416 case VK_F7: index = 6; break;
3417 case VK_F8: index = 7; break;
3418 case VK_F9: index = 8; break;
3419 case VK_F10: index = 9; break;
3420 case VK_F11: index = 10; break;
3421 case VK_F12: index = 11; break;
3423 if (keystate[VK_SHIFT] & 0x80) index += 12;
3424 if (keystate[VK_CONTROL] & 0x80) index += 24;
3425 p += sprintf((char *) p, "\x1B[%c", codes[index]);
3428 if (cfg.funky_type == 5 && /* SCO small keypad */
3429 code >= 1 && code <= 6) {
3430 char codes[] = "HL.FIG";
3434 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3438 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3445 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3448 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3451 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3452 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3455 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3457 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3459 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3462 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3463 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3467 p += sprintf((char *) p, "\x1B[%d~", code);
3472 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3473 * some reason seems to send VK_CLEAR to Windows...).
3496 p += sprintf((char *) p, "\x1B%c", xkey);
3498 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3499 /* VT100 & VT102 manuals both state the app cursor keys
3500 * only work if the app keypad is on.
3502 if (!app_keypad_keys)
3504 /* Useful mapping of Ctrl-arrows */
3505 if (shift_state == 2)
3509 p += sprintf((char *) p, "\x1BO%c", xkey);
3511 p += sprintf((char *) p, "\x1B[%c", xkey);
3518 * Finally, deal with Return ourselves. (Win95 seems to
3519 * foul it up when Alt is pressed, for some reason.)
3521 if (wParam == VK_RETURN) { /* Return */
3527 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3528 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3533 /* Okay we've done everything interesting; let windows deal with
3534 * the boring stuff */
3538 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3539 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3541 keystate[VK_CAPITAL] = 0;
3544 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3545 #ifdef SHOW_TOASCII_RESULT
3546 if (r == 1 && !key_down) {
3548 if (in_utf || dbcs_screenfont)
3549 debug((", (U+%04x)", alt_sum));
3551 debug((", LCH(%d)", alt_sum));
3553 debug((", ACH(%d)", keys[0]));
3558 for (r1 = 0; r1 < r; r1++) {
3559 debug(("%s%d", r1 ? "," : "", keys[r1]));
3568 * Interrupt an ongoing paste. I'm not sure this is
3569 * sensible, but for the moment it's preferable to
3570 * having to faff about buffering things.
3575 for (i = 0; i < r; i++) {
3576 unsigned char ch = (unsigned char) keys[i];
3578 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3583 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3587 if ((nc = check_compose(compose_char, ch)) == -1) {
3588 MessageBeep(MB_ICONHAND);
3592 luni_send(&keybuf, 1, 1);
3600 if (in_utf || dbcs_screenfont) {
3602 luni_send(&keybuf, 1, 1);
3604 ch = (char) alt_sum;
3606 * We need not bother about stdin
3607 * backlogs here, because in GUI PuTTY
3608 * we can't do anything about it
3609 * anyway; there's no means of asking
3610 * Windows to hold off on KEYDOWN
3611 * messages. We _have_ to buffer
3612 * everything we're sent.
3614 ldisc_send(&ch, 1, 1);
3618 lpage_send(kbd_codepage, &ch, 1, 1);
3620 if(capsOn && ch < 0x80) {
3623 cbuf[1] = xlat_uskbd2cyrllic(ch);
3624 luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3629 lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3635 /* This is so the ALT-Numpad and dead keys work correctly. */
3640 /* If we're definitly not building up an ALT-54321 then clear it */
3643 /* If we will be using alt_sum fix the 256s */
3644 else if (keys[0] && (in_utf || dbcs_screenfont))
3649 * ALT alone may or may not want to bring up the System menu.
3650 * If it's not meant to, we return 0 on presses or releases of
3651 * ALT, to show that we've swallowed the keystroke. Otherwise
3652 * we return -1, which means Windows will give the keystroke
3653 * its default handling (i.e. bring up the System menu).
3655 if (wParam == VK_MENU && !cfg.alt_only)
3661 void set_title(char *title)
3664 window_name = smalloc(1 + strlen(title));
3665 strcpy(window_name, title);
3666 if (cfg.win_name_always || !IsIconic(hwnd))
3667 SetWindowText(hwnd, title);
3670 void set_icon(char *title)
3673 icon_name = smalloc(1 + strlen(title));
3674 strcpy(icon_name, title);
3675 if (!cfg.win_name_always && IsIconic(hwnd))
3676 SetWindowText(hwnd, title);
3679 void set_sbar(int total, int start, int page)
3683 if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
3686 si.cbSize = sizeof(si);
3687 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3689 si.nMax = total - 1;
3693 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3696 Context get_ctx(void)
3702 SelectPalette(hdc, pal, FALSE);
3708 void free_ctx(Context ctx)
3710 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3711 ReleaseDC(hwnd, ctx);
3714 static void real_palette_set(int n, int r, int g, int b)
3717 logpal->palPalEntry[n].peRed = r;
3718 logpal->palPalEntry[n].peGreen = g;
3719 logpal->palPalEntry[n].peBlue = b;
3720 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3721 colours[n] = PALETTERGB(r, g, b);
3722 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3724 colours[n] = RGB(r, g, b);
3727 void palette_set(int n, int r, int g, int b)
3729 static const int first[21] = {
3730 0, 2, 4, 6, 8, 10, 12, 14,
3731 1, 3, 5, 7, 9, 11, 13, 15,
3734 real_palette_set(first[n], r, g, b);
3736 real_palette_set(first[n] + 1, r, g, b);
3738 HDC hdc = get_ctx();
3739 UnrealizeObject(pal);
3740 RealizePalette(hdc);
3745 void palette_reset(void)
3749 for (i = 0; i < NCOLOURS; i++) {
3751 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3752 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3753 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3754 logpal->palPalEntry[i].peFlags = 0;
3755 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3756 defpal[i].rgbtGreen,
3757 defpal[i].rgbtBlue);
3759 colours[i] = RGB(defpal[i].rgbtRed,
3760 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3765 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3767 RealizePalette(hdc);
3772 void write_aclip(char *data, int len, int must_deselect)
3777 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3780 lock = GlobalLock(clipdata);
3783 memcpy(lock, data, len);
3784 ((unsigned char *) lock)[len] = 0;
3785 GlobalUnlock(clipdata);
3788 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3790 if (OpenClipboard(hwnd)) {
3792 SetClipboardData(CF_TEXT, clipdata);
3795 GlobalFree(clipdata);
3798 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3802 * Note: unlike write_aclip() this will not append a nul.
3804 void write_clip(wchar_t * data, int len, int must_deselect)
3806 HGLOBAL clipdata, clipdata2, clipdata3;
3808 void *lock, *lock2, *lock3;
3810 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3812 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3813 len * sizeof(wchar_t));
3814 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3816 if (!clipdata || !clipdata2) {
3818 GlobalFree(clipdata);
3820 GlobalFree(clipdata2);
3823 if (!(lock = GlobalLock(clipdata)))
3825 if (!(lock2 = GlobalLock(clipdata2)))
3828 memcpy(lock, data, len * sizeof(wchar_t));
3829 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3831 if (cfg.rtf_paste) {
3832 wchar_t unitab[256];
3834 unsigned char *tdata = (unsigned char *)lock2;
3835 wchar_t *udata = (wchar_t *)lock;
3836 int rtflen = 0, uindex = 0, tindex = 0;
3838 int multilen, blen, alen, totallen, i;
3839 char before[16], after[4];
3841 get_unitab(CP_ACP, unitab, 0);
3843 rtfsize = 100 + strlen(cfg.font);
3844 rtf = smalloc(rtfsize);
3845 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3846 GetACP(), cfg.font);
3847 rtflen = strlen(rtf);
3850 * We want to construct a piece of RTF that specifies the
3851 * same Unicode text. To do this we will read back in
3852 * parallel from the Unicode data in `udata' and the
3853 * non-Unicode data in `tdata'. For each character in
3854 * `tdata' which becomes the right thing in `udata' when
3855 * looked up in `unitab', we just copy straight over from
3856 * tdata. For each one that doesn't, we must WCToMB it
3857 * individually and produce a \u escape sequence.
3859 * It would probably be more robust to just bite the bullet
3860 * and WCToMB each individual Unicode character one by one,
3861 * then MBToWC each one back to see if it was an accurate
3862 * translation; but that strikes me as a horrifying number
3863 * of Windows API calls so I want to see if this faster way
3864 * will work. If it screws up badly we can always revert to
3865 * the simple and slow way.
3867 while (tindex < len2 && uindex < len &&
3868 tdata[tindex] && udata[uindex]) {
3869 if (tindex + 1 < len2 &&
3870 tdata[tindex] == '\r' &&
3871 tdata[tindex+1] == '\n') {
3875 if (unitab[tdata[tindex]] == udata[uindex]) {
3881 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
3882 NULL, 0, NULL, NULL);
3883 if (multilen != 1) {
3884 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
3886 alen = 1; strcpy(after, "}");
3888 blen = sprintf(before, "\\u%d", udata[uindex]);
3889 alen = 0; after[0] = '\0';
3892 assert(tindex + multilen <= len2);
3893 totallen = blen + alen;
3894 for (i = 0; i < multilen; i++) {
3895 if (tdata[tindex+i] == '\\' ||
3896 tdata[tindex+i] == '{' ||
3897 tdata[tindex+i] == '}')
3899 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
3900 totallen += 6; /* \par\r\n */
3901 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
3907 if (rtfsize < rtflen + totallen + 3) {
3908 rtfsize = rtflen + totallen + 512;
3909 rtf = srealloc(rtf, rtfsize);
3912 strcpy(rtf + rtflen, before); rtflen += blen;
3913 for (i = 0; i < multilen; i++) {
3914 if (tdata[tindex+i] == '\\' ||
3915 tdata[tindex+i] == '{' ||
3916 tdata[tindex+i] == '}') {
3917 rtf[rtflen++] = '\\';
3918 rtf[rtflen++] = tdata[tindex+i];
3919 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
3920 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
3921 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
3922 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
3924 rtf[rtflen++] = tdata[tindex+i];
3927 strcpy(rtf + rtflen, after); rtflen += alen;
3933 strcpy(rtf + rtflen, "}");
3936 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
3937 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
3939 GlobalUnlock(clipdata3);
3945 GlobalUnlock(clipdata);
3946 GlobalUnlock(clipdata2);
3949 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3951 if (OpenClipboard(hwnd)) {
3953 SetClipboardData(CF_UNICODETEXT, clipdata);
3954 SetClipboardData(CF_TEXT, clipdata2);
3956 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
3959 GlobalFree(clipdata);
3960 GlobalFree(clipdata2);
3964 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3967 void get_clip(wchar_t ** p, int *len)
3969 static HGLOBAL clipdata = NULL;
3970 static wchar_t *converted = 0;
3979 GlobalUnlock(clipdata);
3982 } else if (OpenClipboard(NULL)) {
3983 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3985 *p = GlobalLock(clipdata);
3987 for (p2 = *p; *p2; p2++);
3991 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3995 s = GlobalLock(clipdata);
3996 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3997 *p = converted = smalloc(i * sizeof(wchar_t));
3998 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
4011 * Move `lines' lines from position `from' to position `to' in the
4014 void optimised_move(int to, int from, int lines)
4019 min = (to < from ? to : from);
4020 max = to + from - min;
4022 r.left = offset_width;
4023 r.right = offset_width + cols * font_width;
4024 r.top = offset_height + min * font_height;
4025 r.bottom = offset_height + (max + lines) * font_height;
4026 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
4031 * Print a message box and perform a fatal exit.
4033 void fatalbox(char *fmt, ...)
4039 vsprintf(stuff, fmt, ap);
4041 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
4046 * Manage window caption / taskbar flashing, if enabled.
4047 * 0 = stop, 1 = maintain, 2 = start
4049 static void flash_window(int mode)
4051 static long last_flash = 0;
4052 static int flashing = 0;
4053 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
4056 FlashWindow(hwnd, FALSE);
4060 } else if (mode == 2) {
4063 last_flash = GetTickCount();
4065 FlashWindow(hwnd, TRUE);
4068 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4071 long now = GetTickCount();
4072 long fdiff = now - last_flash;
4073 if (fdiff < 0 || fdiff > 450) {
4075 FlashWindow(hwnd, TRUE); /* toggle */
4086 if (mode == BELL_DEFAULT) {
4088 * For MessageBeep style bells, we want to be careful of
4089 * timing, because they don't have the nice property of
4090 * PlaySound bells that each one cancels the previous
4091 * active one. So we limit the rate to one per 50ms or so.
4093 static long lastbeep = 0;
4096 beepdiff = GetTickCount() - lastbeep;
4097 if (beepdiff >= 0 && beepdiff < 50)
4101 * The above MessageBeep call takes time, so we record the
4102 * time _after_ it finishes rather than before it starts.
4104 lastbeep = GetTickCount();
4105 } else if (mode == BELL_WAVEFILE) {
4106 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
4107 char buf[sizeof(cfg.bell_wavefile) + 80];
4108 sprintf(buf, "Unable to play sound file\n%s\n"
4109 "Using default sound instead", cfg.bell_wavefile);
4110 MessageBox(hwnd, buf, "PuTTY Sound Error",
4111 MB_OK | MB_ICONEXCLAMATION);
4112 cfg.beep = BELL_DEFAULT;
4115 /* Otherwise, either visual bell or disabled; do nothing here */
4117 flash_window(2); /* start */
4122 * Minimise or restore the window in response to a server-side
4125 void set_iconic(int iconic)
4127 if (IsIconic(hwnd)) {
4129 ShowWindow(hwnd, SW_RESTORE);
4132 ShowWindow(hwnd, SW_MINIMIZE);
4137 * Move the window in response to a server-side request.
4139 void move_window(int x, int y)
4141 SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4145 * Move the window to the top or bottom of the z-order in response
4146 * to a server-side request.
4148 void set_zorder(int top)
4150 if (cfg.alwaysontop)
4151 return; /* ignore */
4152 SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4153 SWP_NOMOVE | SWP_NOSIZE);
4157 * Refresh the window in response to a server-side request.
4159 void refresh_window(void)
4161 InvalidateRect(hwnd, NULL, TRUE);
4165 * Maximise or restore the window in response to a server-side
4168 void set_zoomed(int zoomed)
4170 if (IsZoomed(hwnd)) {
4172 ShowWindow(hwnd, SW_RESTORE);
4175 ShowWindow(hwnd, SW_MAXIMIZE);
4180 * Report whether the window is iconic, for terminal reports.
4184 return IsIconic(hwnd);
4188 * Report the window's position, for terminal reports.
4190 void get_window_pos(int *x, int *y)
4193 GetWindowRect(hwnd, &r);
4199 * Report the window's pixel size, for terminal reports.
4201 void get_window_pixels(int *x, int *y)
4204 GetWindowRect(hwnd, &r);
4205 *x = r.right - r.left;
4206 *y = r.bottom - r.top;
4210 * Return the window or icon title.
4212 char *get_window_title(int icon)
4214 return icon ? icon_name : window_name;
4218 * See if we're in full-screen mode.
4220 int is_full_screen()
4222 if (!IsZoomed(hwnd))
4224 if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
4230 * Go full-screen. This should only be called when we are already
4233 void make_full_screen()
4238 assert(IsZoomed(hwnd));
4240 /* Remove the window furniture. */
4241 style = GetWindowLong(hwnd, GWL_STYLE);
4242 style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
4243 if (cfg.scrollbar_in_fullscreen)
4244 style |= WS_VSCROLL;
4246 style &= ~WS_VSCROLL;
4247 SetWindowLong(hwnd, GWL_STYLE, style);
4249 /* Resize ourselves to exactly cover the nearest monitor. */
4250 #ifdef MONITOR_DEFAULTTONEAREST
4254 mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
4255 mi.cbSize = sizeof(mi);
4256 GetMonitorInfo(mon, &mi);
4257 x = mi.rcMonitor.left;
4258 y = mi.rcMonitor.top;
4259 w = mi.rcMonitor.right;
4260 h = mi.rcMonitor.bottom;
4264 w = GetSystemMetrics(SM_CXSCREEN);
4265 h = GetSystemMetrics(SM_CYSCREEN);
4267 SetWindowPos(hwnd, HWND_TOP, x, y, w, h, SWP_FRAMECHANGED);
4269 /* Tick the menu item in the System menu. */
4270 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4275 * Clear the full-screen attributes.
4277 void clear_full_screen()
4279 DWORD oldstyle, style;
4281 /* Reinstate the window furniture. */
4282 style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
4283 style |= WS_CAPTION | WS_BORDER;
4284 if (cfg.resize_action == RESIZE_DISABLED)
4285 style &= ~WS_THICKFRAME;
4287 style |= WS_THICKFRAME;
4289 style |= WS_VSCROLL;
4291 style &= ~WS_VSCROLL;
4292 if (style != oldstyle) {
4293 SetWindowLong(hwnd, GWL_STYLE, style);
4294 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4295 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4299 /* Untick the menu item in the System menu. */
4300 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4305 * Toggle full-screen mode.
4307 void flip_full_screen()
4309 if (is_full_screen()) {
4310 ShowWindow(hwnd, SW_RESTORE);
4311 } else if (IsZoomed(hwnd)) {
4314 SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
4315 ShowWindow(hwnd, SW_MAXIMIZE);