16 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
22 #define IDM_SHOWLOG 0x0010
23 #define IDM_NEWSESS 0x0020
24 #define IDM_DUPSESS 0x0030
25 #define IDM_RECONF 0x0040
26 #define IDM_CLRSB 0x0050
27 #define IDM_RESET 0x0060
28 #define IDM_TEL_AYT 0x0070
29 #define IDM_TEL_BRK 0x0080
30 #define IDM_TEL_SYNCH 0x0090
31 #define IDM_TEL_EC 0x00a0
32 #define IDM_TEL_EL 0x00b0
33 #define IDM_TEL_GA 0x00c0
34 #define IDM_TEL_NOP 0x00d0
35 #define IDM_TEL_ABORT 0x00e0
36 #define IDM_TEL_AO 0x00f0
37 #define IDM_TEL_IP 0x0100
38 #define IDM_TEL_SUSP 0x0110
39 #define IDM_TEL_EOR 0x0120
40 #define IDM_TEL_EOF 0x0130
41 #define IDM_ABOUT 0x0140
42 #define IDM_SAVEDSESS 0x0150
43 #define IDM_COPYALL 0x0160
45 #define IDM_SAVED_MIN 0x1000
46 #define IDM_SAVED_MAX 0x2000
48 #define WM_IGNORE_SIZE (WM_XUSER + 1)
49 #define WM_IGNORE_CLIP (WM_XUSER + 2)
51 /* Needed for Chinese support and apparently not always defined. */
53 #define VK_PROCESSKEY 0xE5
56 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
57 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
58 static void cfgtopalette(void);
59 static void init_palette(void);
60 static void init_fonts(int);
62 static int extra_width, extra_height;
64 static int pending_netevent = 0;
65 static WPARAM pend_netevent_wParam = 0;
66 static LPARAM pend_netevent_lParam = 0;
67 static void enact_pending_netevent(void);
69 static time_t last_movement = 0;
73 #define FONT_UNDERLINE 2
74 #define FONT_BOLDUND 3
76 #define FONT_OEMBOLD 5
77 #define FONT_OEMBOLDUND 6
79 static HFONT fonts[8];
80 static int font_needs_hand_underlining;
82 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
90 static COLORREF colours[NCOLOURS];
92 static LPLOGPALETTE logpal;
93 static RGBTRIPLE defpal[NCOLOURS];
97 static HBITMAP caretbm;
99 static int dbltime, lasttime, lastact;
100 static Mouse_Button lastbtn;
102 static char *window_name, *icon_name;
104 static Ldisc *real_ldisc;
106 static int compose_state = 0;
108 void begin_session(void) {
112 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
113 static char appname[] = "PuTTY";
118 int guess_width, guess_height;
121 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
123 winsock_ver = MAKEWORD(1, 1);
124 if (WSAStartup(winsock_ver, &wsadata)) {
125 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
126 MB_OK | MB_ICONEXCLAMATION);
129 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
130 MessageBox(NULL, "WinSock version is incompatible with 1.1",
131 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
135 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
138 InitCommonControls();
140 /* Ensure a Maximize setting in Explorer doesn't maximise the
145 * Process the command line.
150 default_protocol = DEFAULT_PROTOCOL;
151 default_port = DEFAULT_PORT;
153 do_defaults(NULL, &cfg);
156 while (*p && isspace(*p)) p++;
159 * Process command line options first. Yes, this can be
160 * done better, and it will be as soon as I have the
164 char *q = p + strcspn(p, " \t");
167 tolower(p[0]) == 's' &&
168 tolower(p[1]) == 's' &&
169 tolower(p[2]) == 'h') {
170 default_protocol = cfg.protocol = PROT_SSH;
171 default_port = cfg.port = 22;
172 } else if (q == p + 3 &&
173 tolower(p[0]) == 'l' &&
174 tolower(p[1]) == 'o' &&
175 tolower(p[2]) == 'g') {
176 logfile = "putty.log";
177 } else if (q == p + 7 &&
178 tolower(p[0]) == 'c' &&
179 tolower(p[1]) == 'l' &&
180 tolower(p[2]) == 'e' &&
181 tolower(p[3]) == 'a' &&
182 tolower(p[4]) == 'n' &&
183 tolower(p[5]) == 'u' &&
184 tolower(p[6]) == 'p') {
186 * `putty -cleanup'. Remove all registry entries
187 * associated with PuTTY, and also find and delete
188 * the random seed file.
191 "This procedure will remove ALL Registry\n"
192 "entries associated with PuTTY, and will\n"
193 "also remove the PuTTY random seed file.\n"
195 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
196 "SESSIONS. Are you really sure you want\n"
199 MB_YESNO | MB_ICONWARNING) == IDYES) {
204 p = q + strspn(q, " \t");
208 * An initial @ means to activate a saved session.
212 while (i > 1 && isspace(p[i-1]))
215 do_defaults (p+1, &cfg);
216 if (!*cfg.host && !do_config()) {
220 } else if (*p == '&') {
222 * An initial & means we've been given a command line
223 * containing the hex value of a HANDLE for a file
224 * mapping object, which we must then extract as a
229 if (sscanf(p+1, "%p", &filemap) == 1 &&
230 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
231 0, 0, sizeof(Config))) != NULL) {
234 CloseHandle(filemap);
235 } else if (!do_config()) {
242 * If the hostname starts with "telnet:", set the
243 * protocol to Telnet and process the string as a
246 if (!strncmp(q, "telnet:", 7)) {
250 if (q[0] == '/' && q[1] == '/')
252 cfg.protocol = PROT_TELNET;
254 while (*p && *p != ':' && *p != '/') p++;
262 strncpy (cfg.host, q, sizeof(cfg.host)-1);
263 cfg.host[sizeof(cfg.host)-1] = '\0';
265 while (*p && !isspace(*p)) p++;
268 strncpy (cfg.host, q, sizeof(cfg.host)-1);
269 cfg.host[sizeof(cfg.host)-1] = '\0';
270 while (*p && isspace(*p)) p++;
283 /* See if host is of the form user@host */
284 if (cfg.host[0] != '\0') {
285 char *atsign = strchr(cfg.host, '@');
286 /* Make sure we're not overflowing the user field */
288 if (atsign-cfg.host < sizeof cfg.username) {
289 strncpy (cfg.username, cfg.host, atsign-cfg.host);
290 cfg.username[atsign-cfg.host] = '\0';
292 memmove(cfg.host, atsign+1, 1+strlen(atsign+1));
298 * Select protocol. This is farmed out into a table in a
299 * separate file to enable an ssh-free variant.
304 for (i = 0; backends[i].backend != NULL; i++)
305 if (backends[i].protocol == cfg.protocol) {
306 back = backends[i].backend;
310 MessageBox(NULL, "Unsupported protocol number found",
311 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
317 real_ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
318 /* To start with, we use the simple line discipline, so we can
319 * type passwords etc without fear of them being echoed... */
320 ldisc = &ldisc_simple;
324 wndclass.lpfnWndProc = WndProc;
325 wndclass.cbClsExtra = 0;
326 wndclass.cbWndExtra = 0;
327 wndclass.hInstance = inst;
328 wndclass.hIcon = LoadIcon (inst,
329 MAKEINTRESOURCE(IDI_MAINICON));
330 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
331 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
332 wndclass.lpszMenuName = NULL;
333 wndclass.lpszClassName = appname;
335 RegisterClass (&wndclass);
340 savelines = cfg.savelines;
346 * Guess some defaults for the window size. This all gets
347 * updated later, so we don't really care too much. However, we
348 * do want the font width/height guesses to correspond to a
349 * large font rather than a small one...
356 term_size (cfg.height, cfg.width, cfg.savelines);
357 guess_width = extra_width + font_width * cols;
358 guess_height = extra_height + font_height * rows;
361 HWND w = GetDesktopWindow();
362 GetWindowRect (w, &r);
363 if (guess_width > r.right - r.left)
364 guess_width = r.right - r.left;
365 if (guess_height > r.bottom - r.top)
366 guess_height = r.bottom - r.top;
370 int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
372 if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
373 if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
374 if (cfg.alwaysontop) exwinmode = WS_EX_TOPMOST;
375 hwnd = CreateWindowEx (exwinmode, appname, appname,
376 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
377 guess_width, guess_height,
378 NULL, NULL, inst, NULL);
382 * Initialise the fonts, simultaneously correcting the guesses
383 * for font_{width,height}.
385 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
390 * Correct the guesses for extra_{width,height}.
394 GetWindowRect (hwnd, &wr);
395 GetClientRect (hwnd, &cr);
396 extra_width = wr.right - wr.left - cr.right + cr.left;
397 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
401 * Resize the window, now we know what size we _really_ want it
404 guess_width = extra_width + font_width * cols;
405 guess_height = extra_height + font_height * rows;
406 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
407 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
408 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
411 * Set up a caret bitmap, with no content.
415 int size = (font_width+15)/16 * 2 * font_height;
416 bits = calloc(size, 1);
417 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
422 * Initialise the scroll bar.
427 si.cbSize = sizeof(si);
428 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
433 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
437 * Start up the telnet connection.
441 char msg[1024], *title;
444 error = back->init (cfg.host, cfg.port, &realhost);
446 sprintf(msg, "Unable to open connection:\n%s", error);
447 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
450 window_name = icon_name = NULL;
452 title = cfg.wintitle;
454 sprintf(msg, "%s - PuTTY", realhost);
461 session_closed = FALSE;
464 * Set up the input and output buffers.
467 outbuf_reap = outbuf_head = 0;
470 * Prepare the mouse handler.
472 lastact = MA_NOTHING;
473 lastbtn = MB_NOTHING;
474 dbltime = GetDoubleClickTime();
477 * Set up the session-control options on the system menu.
480 HMENU m = GetSystemMenu (hwnd, FALSE);
484 AppendMenu (m, MF_SEPARATOR, 0, 0);
485 if (cfg.protocol == PROT_TELNET) {
487 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
488 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
489 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
490 AppendMenu (p, MF_SEPARATOR, 0, 0);
491 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
492 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
493 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
494 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
495 AppendMenu (p, MF_SEPARATOR, 0, 0);
496 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
497 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
498 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
499 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
500 AppendMenu (p, MF_SEPARATOR, 0, 0);
501 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
502 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
503 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
504 AppendMenu (m, MF_SEPARATOR, 0, 0);
506 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
507 AppendMenu (m, MF_SEPARATOR, 0, 0);
508 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
509 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
512 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
513 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
514 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
515 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
516 AppendMenu (m, MF_SEPARATOR, 0, 0);
517 AppendMenu (m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
518 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
519 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
520 AppendMenu (m, MF_SEPARATOR, 0, 0);
521 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
525 * Finally show the window!
527 ShowWindow (hwnd, show);
530 * Set the palette up.
536 has_focus = (GetForegroundWindow() == hwnd);
539 if (GetMessage (&msg, NULL, 0, 0) == 1)
541 int timer_id = 0, long_timer = 0;
543 while (msg.message != WM_QUIT) {
544 /* Sometimes DispatchMessage calls routines that use their own
545 * GetMessage loop, setup this timer so we get some control back.
547 * Also call term_update() from the timer so that if the host
548 * is sending data flat out we still do redraws.
550 if(timer_id && long_timer) {
551 KillTimer(hwnd, timer_id);
552 long_timer = timer_id = 0;
555 timer_id = SetTimer(hwnd, 1, 20, NULL);
556 DispatchMessage (&msg);
558 /* Make sure we blink everything that needs it. */
561 /* Send the paste buffer if there's anything to send */
564 /* If there's nothing new in the queue then we can do everything
565 * we've delayed, reading the socket, writing, and repainting
568 if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
571 if (pending_netevent) {
572 enact_pending_netevent();
574 /* Force the cursor blink on */
577 if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
581 /* Okay there is now nothing to do so we make sure the screen is
582 * completely up to date then tell windows to call us in a little
586 KillTimer(hwnd, timer_id);
595 timer_id = SetTimer(hwnd, 1, 59500, NULL);
597 timer_id = SetTimer(hwnd, 1, 100, NULL);
600 /* There's no point rescanning everything in the message queue
601 * so we do an apperently unneccesary wait here
604 if (GetMessage (&msg, NULL, 0, 0) != 1)
616 DeleteObject(fonts[i]);
623 if (cfg.protocol == PROT_SSH) {
634 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
636 char *do_select(SOCKET skt, int startup) {
640 events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE;
645 return "do_select(): internal error (hwnd==NULL)";
646 if (WSAAsyncSelect (skt, hwnd, msg, events) == SOCKET_ERROR) {
647 switch (WSAGetLastError()) {
648 case WSAENETDOWN: return "Network is down";
649 default: return "WSAAsyncSelect(): unknown error";
656 * Print a message box and close the connection.
658 void connection_fatal(char *fmt, ...) {
663 vsprintf(stuff, fmt, ap);
665 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
666 if (cfg.close_on_exit)
669 session_closed = TRUE;
670 SetWindowText (hwnd, "PuTTY (inactive)");
675 * Actually do the job requested by a WM_NETEVENT
677 static void enact_pending_netevent(void) {
678 static int reentering = 0;
679 extern int select_result(WPARAM, LPARAM);
683 return; /* don't unpend the pending */
685 pending_netevent = FALSE;
688 ret = select_result (pend_netevent_wParam, pend_netevent_lParam);
692 if (cfg.close_on_exit)
695 session_closed = TRUE;
696 MessageBox(hwnd, "Connection closed by remote host",
697 "PuTTY", MB_OK | MB_ICONINFORMATION);
698 SetWindowText (hwnd, "PuTTY (inactive)");
704 * Copy the colour palette from the configuration data into defpal.
705 * This is non-trivial because the colour indices are different.
707 static void cfgtopalette(void) {
709 static const int ww[] = {
710 6, 7, 8, 9, 10, 11, 12, 13,
711 14, 15, 16, 17, 18, 19, 20, 21,
712 0, 1, 2, 3, 4, 4, 5, 5
715 for (i=0; i<24; i++) {
717 defpal[i].rgbtRed = cfg.colours[w][0];
718 defpal[i].rgbtGreen = cfg.colours[w][1];
719 defpal[i].rgbtBlue = cfg.colours[w][2];
724 * Set up the colour palette.
726 static void init_palette(void) {
728 HDC hdc = GetDC (hwnd);
730 if (cfg.try_palette &&
731 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
732 logpal = smalloc(sizeof(*logpal)
733 - sizeof(logpal->palPalEntry)
734 + NCOLOURS * sizeof(PALETTEENTRY));
735 logpal->palVersion = 0x300;
736 logpal->palNumEntries = NCOLOURS;
737 for (i = 0; i < NCOLOURS; i++) {
738 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
739 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
740 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
741 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
743 pal = CreatePalette (logpal);
745 SelectPalette (hdc, pal, FALSE);
746 RealizePalette (hdc);
747 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
751 ReleaseDC (hwnd, hdc);
754 for (i=0; i<NCOLOURS; i++)
755 colours[i] = PALETTERGB(defpal[i].rgbtRed,
759 for(i=0; i<NCOLOURS; i++)
760 colours[i] = RGB(defpal[i].rgbtRed,
766 * Initialise all the fonts we will need. There may be as many as
767 * eight or as few as one. We also:
769 * - check the font width and height, correcting our guesses if
772 * - verify that the bold font is the same width as the ordinary
773 * one, and engage shadow bolding if not.
775 * - verify that the underlined font is the same width as the
776 * ordinary one (manual underlining by means of line drawing can
777 * be done in a pinch).
779 static void init_fonts(int pick_width) {
784 int fw_dontcare, fw_bold;
793 if (cfg.fontisbold) {
794 fw_dontcare = FW_BOLD;
797 fw_dontcare = FW_DONTCARE;
803 font_height = cfg.fontheight;
804 font_width = pick_width;
807 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
808 c, OUT_DEFAULT_PRECIS, \
809 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
810 FIXED_PITCH | FF_DONTCARE, cfg.font)
812 if (cfg.vtmode != VT_OEMONLY) {
813 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
815 SelectObject (hdc, fonts[FONT_NORMAL]);
816 GetTextMetrics(hdc, &tm);
817 font_height = tm.tmHeight;
818 font_width = tm.tmAveCharWidth;
820 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
823 * Some fonts, e.g. 9-pt Courier, draw their underlines
824 * outside their character cell. We successfully prevent
825 * screen corruption by clipping the text output, but then
826 * we lose the underline completely. Here we try to work
827 * out whether this is such a font, and if it is, we set a
828 * flag that causes underlines to be drawn by hand.
830 * Having tried other more sophisticated approaches (such
831 * as examining the TEXTMETRIC structure or requesting the
832 * height of a string), I think we'll do this the brute
833 * force way: we create a small bitmap, draw an underlined
834 * space on it, and test to see whether any pixels are
835 * foreground-coloured. (Since we expect the underline to
836 * go all the way across the character cell, we only search
837 * down a single column of the bitmap, half way across.)
841 HBITMAP und_bm, und_oldbm;
845 und_dc = CreateCompatibleDC(hdc);
846 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
847 und_oldbm = SelectObject(und_dc, und_bm);
848 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
849 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
850 SetTextColor (und_dc, RGB(255,255,255));
851 SetBkColor (und_dc, RGB(0,0,0));
852 SetBkMode (und_dc, OPAQUE);
853 ExtTextOut (und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
855 for (i = 0; i < font_height; i++) {
856 c = GetPixel(und_dc, font_width/2, i);
860 SelectObject(und_dc, und_oldbm);
861 DeleteObject(und_bm);
863 font_needs_hand_underlining = !gotit;
866 if (bold_mode == BOLD_FONT) {
867 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
868 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
871 if (cfg.vtmode == VT_OEMANSI) {
872 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
873 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
875 if (bold_mode == BOLD_FONT) {
876 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
877 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
883 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
885 SelectObject (hdc, fonts[FONT_OEM]);
886 GetTextMetrics(hdc, &tm);
887 font_height = tm.tmHeight;
888 font_width = tm.tmAveCharWidth;
890 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
892 if (bold_mode == BOLD_FONT) {
893 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
894 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
899 descent = tm.tmAscent + 1;
900 if (descent >= font_height)
901 descent = font_height - 1;
902 firstchar = tm.tmFirstChar;
904 for (i=0; i<8; i++) {
906 if (SelectObject (hdc, fonts[i]) &&
907 GetTextMetrics(hdc, &tm) )
908 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
914 ReleaseDC (hwnd, hdc);
916 /* ... This is wrong in OEM only mode */
917 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
918 (bold_mode == BOLD_FONT &&
919 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
921 DeleteObject (fonts[FONT_UNDERLINE]);
922 if (bold_mode == BOLD_FONT)
923 DeleteObject (fonts[FONT_BOLDUND]);
926 if (bold_mode == BOLD_FONT &&
927 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
928 bold_mode = BOLD_SHADOW;
929 DeleteObject (fonts[FONT_BOLD]);
930 if (und_mode == UND_FONT)
931 DeleteObject (fonts[FONT_BOLDUND]);
935 /* With the fascist font painting it doesn't matter if the linedraw font
936 * isn't exactly the right size anymore so we don't have to check this.
938 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
939 if( cfg.fontcharset == OEM_CHARSET )
941 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
942 "different sizes. Using OEM-only mode instead",
943 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
944 cfg.vtmode = VT_OEMONLY;
946 else if( firstchar < ' ' )
948 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
949 "different sizes. Using XTerm mode instead",
950 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
951 cfg.vtmode = VT_XWINDOWS;
955 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
956 "different sizes. Using ISO8859-1 mode instead",
957 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
958 cfg.vtmode = VT_POORMAN;
963 DeleteObject (fonts[i]);
969 void request_resize (int w, int h, int refont) {
972 /* If the window is maximized supress resizing attempts */
973 if(IsZoomed(hwnd)) return;
976 /* Don't do this in OEMANSI, you may get disable messages */
977 if (refont && w != cols && (cols==80 || cols==132)
978 && cfg.vtmode != VT_OEMANSI)
980 if (refont && w != cols && (cols==80 || cols==132))
983 /* If font width too big for screen should we shrink the font more ? */
985 font_width = ((font_width*cols+w/2)/w);
992 DeleteObject(fonts[i]);
994 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
996 init_fonts(font_width);
1000 static int first_time = 1;
1006 /* Get the size of the screen */
1007 if (GetClientRect(GetDesktopWindow(),&ss))
1008 /* first_time = 0 */;
1009 else { first_time = 2; break; }
1011 /* Make sure the values are sane */
1012 width = (ss.right-ss.left-extra_width ) / font_width;
1013 height = (ss.bottom-ss.top-extra_height ) / font_height;
1015 if (w>width) w=width;
1016 if (h>height) h=height;
1022 width = extra_width + font_width * w;
1023 height = extra_height + font_height * h;
1025 SetWindowPos (hwnd, NULL, 0, 0, width, height,
1026 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1027 SWP_NOMOVE | SWP_NOZORDER);
1030 static void click (Mouse_Button b, int x, int y) {
1031 int thistime = GetMessageTime();
1033 if (lastbtn == b && thistime - lasttime < dbltime) {
1034 lastact = (lastact == MA_CLICK ? MA_2CLK :
1035 lastact == MA_2CLK ? MA_3CLK :
1036 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1041 if (lastact != MA_NOTHING)
1042 term_mouse (b, lastact, x, y);
1043 lasttime = thistime;
1046 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
1047 WPARAM wParam, LPARAM lParam) {
1049 static int ignore_size = FALSE;
1050 static int ignore_clip = FALSE;
1051 static int just_reconfigged = FALSE;
1052 static int resizing = FALSE;
1056 if (pending_netevent)
1057 enact_pending_netevent();
1064 if (cfg.ping_interval > 0)
1068 if (now-last_movement > cfg.ping_interval * 60 - 10)
1070 back->special(TS_PING);
1071 last_movement = now;
1078 if (!cfg.warn_on_close || session_closed ||
1079 MessageBox(hwnd, "Are you sure you want to close this session?",
1080 "PuTTY Exit Confirmation",
1081 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1082 DestroyWindow(hwnd);
1085 PostQuitMessage (0);
1088 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1100 PROCESS_INFORMATION pi;
1101 HANDLE filemap = NULL;
1103 if (wParam == IDM_DUPSESS) {
1105 * Allocate a file-mapping memory chunk for the
1108 SECURITY_ATTRIBUTES sa;
1111 sa.nLength = sizeof(sa);
1112 sa.lpSecurityDescriptor = NULL;
1113 sa.bInheritHandle = TRUE;
1114 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
1121 p = (Config *)MapViewOfFile(filemap,
1123 0, 0, sizeof(Config));
1125 *p = cfg; /* structure copy */
1129 sprintf(c, "putty &%p", filemap);
1131 } else if (wParam == IDM_SAVEDSESS) {
1132 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
1133 cl = malloc(16 + strlen(session)); /* 8, but play safe */
1135 cl = NULL; /* not a very important failure mode */
1137 sprintf(cl, "putty @%s", session);
1143 GetModuleFileName (NULL, b, sizeof(b)-1);
1145 si.lpReserved = NULL;
1146 si.lpDesktop = NULL;
1150 si.lpReserved2 = NULL;
1151 CreateProcess (b, cl, NULL, NULL, TRUE,
1152 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1155 CloseHandle(filemap);
1162 int prev_alwaysontop = cfg.alwaysontop;
1163 if (!do_reconfig(hwnd))
1165 just_reconfigged = TRUE;
1170 DeleteObject(fonts[i]);
1172 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1173 und_mode = UND_FONT;
1177 * Telnet will change local echo -> remote if the
1180 if (cfg.protocol != PROT_TELNET)
1181 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
1189 /* Enable or disable the scroll bar, etc */
1191 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1192 LONG nexflag, exflag = GetWindowLong(hwnd, GWL_EXSTYLE);
1195 if (cfg.alwaysontop != prev_alwaysontop) {
1196 if (cfg.alwaysontop) {
1197 nexflag = WS_EX_TOPMOST;
1198 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1199 SWP_NOMOVE | SWP_NOSIZE);
1202 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1203 SWP_NOMOVE | SWP_NOSIZE);
1208 if (cfg.scrollbar) nflg |= WS_VSCROLL;
1209 else nflg &= ~WS_VSCROLL;
1211 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1213 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1215 if (nflg != flag || nexflag != exflag)
1220 SetWindowLong(hwnd, GWL_STYLE, nflg);
1221 if (nexflag != exflag)
1222 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1224 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1225 SetWindowPos(hwnd, NULL, 0,0,0,0,
1226 SWP_NOACTIVATE|SWP_NOCOPYBITS|
1227 SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|
1230 GetWindowRect (hwnd, &wr);
1231 GetClientRect (hwnd, &cr);
1232 extra_width = wr.right - wr.left - cr.right + cr.left;
1233 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1237 term_size(cfg.height, cfg.width, cfg.savelines);
1238 InvalidateRect(hwnd, NULL, TRUE);
1239 SetWindowPos (hwnd, NULL, 0, 0,
1240 extra_width + font_width * cfg.width,
1241 extra_height + font_height * cfg.height,
1242 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1243 SWP_NOMOVE | SWP_NOZORDER);
1244 if (IsIconic(hwnd)) {
1245 SetWindowText (hwnd,
1246 cfg.win_name_always ? window_name : icon_name);
1259 case IDM_TEL_AYT: back->special (TS_AYT); break;
1260 case IDM_TEL_BRK: back->special (TS_BRK); break;
1261 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1262 case IDM_TEL_EC: back->special (TS_EC); break;
1263 case IDM_TEL_EL: back->special (TS_EL); break;
1264 case IDM_TEL_GA: back->special (TS_GA); break;
1265 case IDM_TEL_NOP: back->special (TS_NOP); break;
1266 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1267 case IDM_TEL_AO: back->special (TS_AO); break;
1268 case IDM_TEL_IP: back->special (TS_IP); break;
1269 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1270 case IDM_TEL_EOR: back->special (TS_EOR); break;
1271 case IDM_TEL_EOF: back->special (TS_EOF); break;
1276 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1277 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1282 #define X_POS(l) ((int)(short)LOWORD(l))
1283 #define Y_POS(l) ((int)(short)HIWORD(l))
1285 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1286 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1288 case WM_LBUTTONDOWN:
1289 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1290 TO_CHR_Y(Y_POS(lParam)));
1294 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1295 TO_CHR_Y(Y_POS(lParam)));
1298 case WM_MBUTTONDOWN:
1300 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1301 TO_CHR_X(X_POS(lParam)),
1302 TO_CHR_Y(Y_POS(lParam)));
1305 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1306 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1307 TO_CHR_Y(Y_POS(lParam)));
1310 case WM_RBUTTONDOWN:
1312 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1313 TO_CHR_X(X_POS(lParam)),
1314 TO_CHR_Y(Y_POS(lParam)));
1317 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1318 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1319 TO_CHR_Y(Y_POS(lParam)));
1324 * Add the mouse position and message time to the random
1327 noise_ultralight(lParam);
1329 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1331 if (wParam & MK_LBUTTON)
1333 else if (wParam & MK_MBUTTON)
1334 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1336 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1337 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1338 TO_CHR_Y(Y_POS(lParam)));
1341 case WM_IGNORE_CLIP:
1342 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1344 case WM_DESTROYCLIPBOARD:
1347 ignore_clip = FALSE;
1353 hdc = BeginPaint (hwnd, &p);
1355 SelectPalette (hdc, pal, TRUE);
1356 RealizePalette (hdc);
1358 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1359 p.rcPaint.right, p.rcPaint.bottom);
1360 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1361 SelectObject (hdc, GetStockObject(WHITE_PEN));
1362 EndPaint (hwnd, &p);
1367 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1368 * but the only one that's likely to try to overload us is FD_READ.
1369 * This means buffering just one is fine.
1371 if (pending_netevent)
1372 enact_pending_netevent();
1374 pending_netevent = TRUE;
1375 pend_netevent_wParam=wParam;
1376 pend_netevent_lParam=lParam;
1377 time(&last_movement);
1381 CreateCaret(hwnd, caretbm, 0, 0);
1393 case WM_IGNORE_SIZE:
1394 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1396 case WM_ENTERSIZEMOVE:
1400 case WM_EXITSIZEMOVE:
1407 int width, height, w, h, ew, eh;
1408 LPRECT r = (LPRECT)lParam;
1410 width = r->right - r->left - extra_width;
1411 height = r->bottom - r->top - extra_height;
1412 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1413 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1414 UpdateSizeTip(hwnd, w, h);
1415 ew = width - w * font_width;
1416 eh = height - h * font_height;
1418 if (wParam == WMSZ_LEFT ||
1419 wParam == WMSZ_BOTTOMLEFT ||
1420 wParam == WMSZ_TOPLEFT)
1426 if (wParam == WMSZ_TOP ||
1427 wParam == WMSZ_TOPRIGHT ||
1428 wParam == WMSZ_TOPLEFT)
1438 /* break; (never reached) */
1440 if (wParam == SIZE_MINIMIZED) {
1441 SetWindowText (hwnd,
1442 cfg.win_name_always ? window_name : icon_name);
1445 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1446 SetWindowText (hwnd, window_name);
1448 int width, height, w, h;
1449 #if 0 /* we have fixed this using WM_SIZING now */
1453 width = LOWORD(lParam);
1454 height = HIWORD(lParam);
1455 w = width / font_width; if (w < 1) w = 1;
1456 h = height / font_height; if (h < 1) h = 1;
1457 #if 0 /* we have fixed this using WM_SIZING now */
1458 ew = width - w * font_width;
1459 eh = height - h * font_height;
1460 if (ew != 0 || eh != 0) {
1462 GetWindowRect (hwnd, &r);
1463 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1464 SetWindowPos (hwnd, NULL, 0, 0,
1465 r.right - r.left - ew, r.bottom - r.top - eh,
1466 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1469 if (w != cols || h != rows || just_reconfigged) {
1471 term_size (h, w, cfg.savelines);
1473 * Don't call back->size in mid-resize. (To prevent
1474 * massive numbers of resize events getting sent
1475 * down the connection during an NT opaque drag.)
1479 just_reconfigged = FALSE;
1482 ignore_size = FALSE;
1485 switch (LOWORD(wParam)) {
1486 case SB_BOTTOM: term_scroll(-1, 0); break;
1487 case SB_TOP: term_scroll(+1, 0); break;
1488 case SB_LINEDOWN: term_scroll (0, +1); break;
1489 case SB_LINEUP: term_scroll (0, -1); break;
1490 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1491 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1492 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1493 term_scroll (1, HIWORD(wParam)); break;
1496 case WM_PALETTECHANGED:
1497 if ((HWND) wParam != hwnd && pal != NULL) {
1498 HDC hdc = get_ctx();
1500 if (RealizePalette (hdc) > 0)
1506 case WM_QUERYNEWPALETTE:
1508 HDC hdc = get_ctx();
1510 if (RealizePalette (hdc) > 0)
1522 * Add the scan code and keypress timing to the random
1525 noise_ultralight(lParam);
1528 * We don't do TranslateMessage since it disassociates the
1529 * resulting CHAR message from the KEYDOWN that sparked it,
1530 * which we occasionally don't want. Instead, we process
1531 * KEYDOWN, and call the Win32 translator functions so that
1532 * we get the translations under _our_ control.
1535 unsigned char buf[20];
1538 if (wParam==VK_PROCESSKEY) {
1541 m.message = WM_KEYDOWN;
1543 m.lParam = lParam & 0xdfff;
1544 TranslateMessage(&m);
1546 len = TranslateKey (message, wParam, lParam, buf);
1548 return DefWindowProc (hwnd, message, wParam, lParam);
1549 ldisc->send (buf, len);
1555 unsigned char buf[2];
1558 buf[0] = wParam >> 8;
1559 ldisc->send (buf, 2);
1564 * Nevertheless, we are prepared to deal with WM_CHAR
1565 * messages, should they crop up. So if someone wants to
1566 * post the things to us as part of a macro manoeuvre,
1567 * we're ready to cope.
1570 char c = xlat_kbd2tty((unsigned char)wParam);
1571 ldisc->send (&c, 1);
1576 return DefWindowProc (hwnd, message, wParam, lParam);
1580 * Move the system caret. (We maintain one, even though it's
1581 * invisible, for the benefit of blind people: apparently some
1582 * helper software tracks the system caret, so we should arrange to
1585 void sys_cursor(int x, int y) {
1586 SetCaretPos(x * font_width, y * font_height);
1590 * Draw a line of text in the window, at given character
1591 * coordinates, in given attributes.
1593 * We are allowed to fiddle with the contents of `text'.
1595 void do_text (Context ctx, int x, int y, char *text, int len,
1596 unsigned long attr, int lattr) {
1598 int nfg, nbg, nfont;
1601 int force_manual_underline = 0;
1602 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1603 static int *IpDx = 0, IpDxLEN = 0;;
1605 if (len>IpDxLEN || IpDx[0] != fnt_width) {
1609 IpDx = smalloc((len+16)*sizeof(int));
1612 for(i=0; i<IpDxLEN; i++)
1613 IpDx[i] = fnt_width;
1619 if (attr & ATTR_ACTCURS) {
1620 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1621 attr ^= ATTR_CUR_XOR;
1625 if (cfg.vtmode == VT_OEMONLY)
1629 * Map high-half characters in order to approximate ISO using
1630 * OEM character set. No characters are missing if the OEM codepage
1633 if (nfont & FONT_OEM) {
1635 for (i=0; i<len; i++)
1636 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1638 /* This is CP850 ... perfect translation */
1639 static const char oemhighhalf[] =
1640 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1641 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1642 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1643 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1644 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1645 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1646 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1647 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1648 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1649 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1650 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1651 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1654 /* This is CP437 ... junk translation */
1655 static const unsigned char oemhighhalf[] = {
1656 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1657 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1658 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1659 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1660 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1661 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1662 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1663 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1664 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1665 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1666 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1667 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1670 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1674 if (attr & ATTR_LINEDRW) {
1677 static const char poorman[] =
1678 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1681 static const char oemmap_437[] =
1682 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1683 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1686 static const char oemmap_850[] =
1687 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1688 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1690 /* Poor windows font ... eg: windows courier */
1691 static const char oemmap[] =
1692 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1693 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1696 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1697 * VT100 line drawing chars; everything else stays normal.
1699 * Actually '_' maps to space too, but that's done before.
1701 switch (cfg.vtmode) {
1703 for (i=0; i<len; i++)
1704 if (text[i] >= '\x60' && text[i] <= '\x7E')
1705 text[i] += '\x01' - '\x60';
1708 /* Make sure we actually have an OEM font */
1709 if (fonts[nfont|FONT_OEM]) {
1712 for (i=0; i<len; i++)
1713 if (text[i] >= '\x60' && text[i] <= '\x7E')
1714 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1718 for (i=0; i<len; i++)
1719 if (text[i] >= '\x60' && text[i] <= '\x7E')
1720 text[i] = poorman[(unsigned char)text[i] - 0x60];
1725 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1726 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1727 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1729 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1730 nfont |= FONT_UNDERLINE;
1733 if (nfont&FONT_UNDERLINE)
1734 force_manual_underline = 1;
1735 /* Don't do the same for manual bold, it could be bad news. */
1737 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1739 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1740 force_manual_underline = 1;
1741 if (attr & ATTR_REVERSE) {
1742 t = nfg; nfg = nbg; nbg = t;
1744 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1746 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1750 SelectObject (hdc, fonts[nfont]);
1751 SetTextColor (hdc, fg);
1752 SetBkColor (hdc, bg);
1753 SetBkMode (hdc, OPAQUE);
1756 line_box.right = x+fnt_width*len;
1757 line_box.bottom = y+font_height;
1758 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1759 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1760 SetBkMode (hdc, TRANSPARENT);
1762 /* GRR: This draws the character outside it's box and can leave
1763 * 'droppings' even with the clip box! I suppose I could loop it
1764 * one character at a time ... yuk.
1766 * Or ... I could do a test print with "W", and use +1 or -1 for this
1767 * shift depending on if the leftmost column is blank...
1769 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1771 if (force_manual_underline ||
1772 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1774 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1775 MoveToEx (hdc, x, y+descent, NULL);
1776 LineTo (hdc, x+len*fnt_width, y+descent);
1777 oldpen = SelectObject (hdc, oldpen);
1778 DeleteObject (oldpen);
1780 if (attr & ATTR_PASCURS) {
1783 pts[0].x = pts[1].x = pts[4].x = x;
1784 pts[2].x = pts[3].x = x+fnt_width-1;
1785 pts[0].y = pts[3].y = pts[4].y = y;
1786 pts[1].y = pts[2].y = y+font_height-1;
1787 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1788 Polyline (hdc, pts, 5);
1789 oldpen = SelectObject (hdc, oldpen);
1790 DeleteObject (oldpen);
1794 static int check_compose(int first, int second) {
1796 static char * composetbl[] = {
1797 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1798 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1799 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1800 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1801 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1802 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1803 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1804 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1805 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1806 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1807 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1808 "\"uü", "'yý", "htþ", "\"yÿ",
1812 static int recurse = 0;
1815 for(c=composetbl; *c; c++) {
1816 if( (*c)[0] == first && (*c)[1] == second)
1818 return (*c)[2] & 0xFF;
1825 nc = check_compose(second, first);
1827 nc = check_compose(toupper(first), toupper(second));
1829 nc = check_compose(toupper(second), toupper(first));
1837 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1838 * codes. Returns number of bytes used or zero to drop the message
1839 * or -1 to forward the message to windows.
1841 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
1842 unsigned char *output) {
1844 int scan, left_alt = 0, key_down, shift_state;
1846 unsigned char * p = output;
1848 static WORD keys[3];
1849 static int compose_char = 0;
1850 static WPARAM compose_key = 0;
1852 r = GetKeyboardState(keystate);
1853 if (!r) memset(keystate, 0, sizeof(keystate));
1857 { /* Tell us all about key events */
1858 static BYTE oldstate[256];
1859 static int first = 1;
1862 if(first) memcpy(oldstate, keystate, sizeof(oldstate));
1865 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT) {
1867 } else if ((HIWORD(lParam)&KF_UP) && scan==(HIWORD(lParam) & 0xFF) ) {
1871 if (wParam >= VK_F1 && wParam <= VK_F20 )
1872 debug(("K_F%d", wParam+1-VK_F1));
1875 case VK_SHIFT: debug(("SHIFT")); break;
1876 case VK_CONTROL: debug(("CTRL")); break;
1877 case VK_MENU: debug(("ALT")); break;
1878 default: debug(("VK_%02x", wParam));
1880 if(message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
1882 debug((", S%02x", scan=(HIWORD(lParam) & 0xFF) ));
1884 ch = MapVirtualKey(wParam, 2);
1885 if (ch>=' ' && ch<='~') debug((", '%c'", ch));
1886 else if (ch) debug((", $%02x", ch));
1888 if (keys[0]) debug((", KB0=%02x", keys[0]));
1889 if (keys[1]) debug((", KB1=%02x", keys[1]));
1890 if (keys[2]) debug((", KB2=%02x", keys[2]));
1892 if ( (keystate[VK_SHIFT]&0x80)!=0) debug((", S"));
1893 if ( (keystate[VK_CONTROL]&0x80)!=0) debug((", C"));
1894 if ( (HIWORD(lParam)&KF_EXTENDED) ) debug((", E"));
1895 if ( (HIWORD(lParam)&KF_UP) ) debug((", U"));
1898 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
1900 else if ( (HIWORD(lParam)&KF_UP) )
1901 oldstate[wParam&0xFF] ^= 0x80;
1903 oldstate[wParam&0xFF] ^= 0x81;
1905 for(ch=0; ch<256; ch++)
1906 if (oldstate[ch] != keystate[ch])
1907 debug((", M%02x=%02x", ch, keystate[ch]));
1909 memcpy(oldstate, keystate, sizeof(oldstate));
1913 /* Note if AltGr was pressed and if it was used as a compose key */
1914 if (cfg.compose_key) {
1915 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
1917 keystate[VK_RMENU] = keystate[VK_MENU];
1918 if (!compose_state) compose_key = wParam;
1920 if (wParam == VK_APPS && !compose_state)
1921 compose_key = wParam;
1923 if (wParam == compose_key)
1925 if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1927 else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
1932 else if (compose_state==1 && wParam != VK_CONTROL)
1937 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1938 if ( (cfg.funky_type == 3 ||
1939 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
1940 && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
1942 wParam = VK_EXECUTE;
1944 /* UnToggle NUMLock */
1945 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1946 keystate[VK_NUMLOCK] ^= 1;
1949 /* And write back the 'adjusted' state */
1950 SetKeyboardState (keystate);
1953 /* Disable Auto repeat if required */
1954 if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
1957 if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
1960 key_down = ((HIWORD(lParam)&KF_UP)==0);
1962 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1963 if (left_alt && (keystate[VK_CONTROL]&0x80))
1964 keystate[VK_MENU] = 0;
1966 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
1967 shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
1968 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
1971 * Record that we pressed key so the scroll window can be reset, but
1972 * be careful to avoid Shift-UP/Down
1974 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
1978 /* Make sure we're not pasting */
1979 if (key_down) term_nopaste();
1981 if (compose_state>1 && left_alt) compose_state = 0;
1983 /* Sanitize the number pad if not using a PC NumPad */
1984 if( left_alt || (app_keypad_keys && !cfg.no_applic_k
1985 && cfg.funky_type != 2)
1986 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state )
1988 if ((HIWORD(lParam)&KF_EXTENDED) == 0)
1993 case VK_INSERT: nParam = VK_NUMPAD0; break;
1994 case VK_END: nParam = VK_NUMPAD1; break;
1995 case VK_DOWN: nParam = VK_NUMPAD2; break;
1996 case VK_NEXT: nParam = VK_NUMPAD3; break;
1997 case VK_LEFT: nParam = VK_NUMPAD4; break;
1998 case VK_CLEAR: nParam = VK_NUMPAD5; break;
1999 case VK_RIGHT: nParam = VK_NUMPAD6; break;
2000 case VK_HOME: nParam = VK_NUMPAD7; break;
2001 case VK_UP: nParam = VK_NUMPAD8; break;
2002 case VK_PRIOR: nParam = VK_NUMPAD9; break;
2003 case VK_DELETE: nParam = VK_DECIMAL; break;
2007 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
2013 /* If a key is pressed and AltGr is not active */
2014 if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
2016 /* Okay, prepare for most alts then ...*/
2017 if (left_alt) *p++ = '\033';
2019 /* Lets see if it's a pattern we know all about ... */
2020 if (wParam == VK_PRIOR && shift_state == 1) {
2021 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2024 if (wParam == VK_NEXT && shift_state == 1) {
2025 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2028 if (wParam == VK_INSERT && shift_state == 1) {
2029 term_mouse (MB_PASTE, MA_CLICK, 0, 0);
2030 term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
2033 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2036 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2037 PostMessage(hwnd, WM_CHAR, ' ', 0);
2038 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2041 /* Control-Numlock for app-keypad mode switch */
2042 if (wParam == VK_PAUSE && shift_state == 2) {
2043 app_keypad_keys ^= 1;
2047 /* Nethack keypad */
2048 if (cfg.nethack_keypad && !left_alt) {
2050 case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
2051 case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
2052 case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
2053 case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
2054 case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
2055 case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
2056 case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
2057 case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
2058 case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
2062 /* Application Keypad */
2066 if ( cfg.funky_type == 3 ||
2067 ( cfg.funky_type <= 1 &&
2068 app_keypad_keys && !cfg.no_applic_k)) switch(wParam) {
2069 case VK_EXECUTE: xkey = 'P'; break;
2070 case VK_DIVIDE: xkey = 'Q'; break;
2071 case VK_MULTIPLY:xkey = 'R'; break;
2072 case VK_SUBTRACT:xkey = 'S'; break;
2074 if(app_keypad_keys && !cfg.no_applic_k) switch(wParam) {
2075 case VK_NUMPAD0: xkey = 'p'; break;
2076 case VK_NUMPAD1: xkey = 'q'; break;
2077 case VK_NUMPAD2: xkey = 'r'; break;
2078 case VK_NUMPAD3: xkey = 's'; break;
2079 case VK_NUMPAD4: xkey = 't'; break;
2080 case VK_NUMPAD5: xkey = 'u'; break;
2081 case VK_NUMPAD6: xkey = 'v'; break;
2082 case VK_NUMPAD7: xkey = 'w'; break;
2083 case VK_NUMPAD8: xkey = 'x'; break;
2084 case VK_NUMPAD9: xkey = 'y'; break;
2086 case VK_DECIMAL: xkey = 'n'; break;
2087 case VK_ADD: if(cfg.funky_type==2) {
2088 if(shift_state) xkey = 'l';
2090 } else if(shift_state) xkey = 'm';
2094 case VK_DIVIDE: if(cfg.funky_type==2) xkey = 'o'; break;
2095 case VK_MULTIPLY:if(cfg.funky_type==2) xkey = 'j'; break;
2096 case VK_SUBTRACT:if(cfg.funky_type==2) xkey = 'm'; break;
2099 if (HIWORD(lParam)&KF_EXTENDED)
2107 if (xkey>='P' && xkey<='S')
2108 p += sprintf((char *)p, "\x1B%c", xkey);
2110 p += sprintf((char *)p, "\x1B?%c", xkey);
2113 p += sprintf((char *)p, "\x1BO%c", xkey);
2118 if (wParam == VK_BACK && shift_state == 0 ) /* Backspace */
2120 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2123 if (wParam == VK_TAB && shift_state == 1 ) /* Shift tab */
2125 *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
2127 if (wParam == VK_SPACE && shift_state == 2 ) /* Ctrl-Space */
2129 *p++ = 0; return p - output;
2131 if (wParam == VK_SPACE && shift_state == 3 ) /* Ctrl-Shift-Space */
2133 *p++ = 160; return p - output;
2135 if (wParam == VK_CANCEL && shift_state == 2 ) /* Ctrl-Break */
2137 *p++ = 3; return p - output;
2139 /* Control-2 to Control-8 are special */
2140 if (shift_state == 2 && wParam >= '2' && wParam <= '8')
2142 *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
2145 if (shift_state == 2 && wParam == 0xBD) {
2149 if (shift_state == 2 && wParam == 0xDF) {
2153 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2154 *p++ = '\r'; *p++ = '\n';
2159 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2160 * for integer decimal nn.)
2162 * We also deal with the weird ones here. Linux VCs replace F1
2163 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2164 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2169 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
2170 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
2171 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
2172 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
2173 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
2174 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
2175 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
2176 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
2177 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
2178 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
2179 case VK_F11: code = 23; break;
2180 case VK_F12: code = 24; break;
2181 case VK_F13: code = 25; break;
2182 case VK_F14: code = 26; break;
2183 case VK_F15: code = 28; break;
2184 case VK_F16: code = 29; break;
2185 case VK_F17: code = 31; break;
2186 case VK_F18: code = 32; break;
2187 case VK_F19: code = 33; break;
2188 case VK_F20: code = 34; break;
2189 case VK_HOME: code = 1; break;
2190 case VK_INSERT: code = 2; break;
2191 case VK_DELETE: code = 3; break;
2192 case VK_END: code = 4; break;
2193 case VK_PRIOR: code = 5; break;
2194 case VK_NEXT: code = 6; break;
2196 /* Reorder edit keys to physical order */
2197 if (cfg.funky_type == 3 && code <= 6 ) code = "\0\2\1\4\5\3\6"[code];
2199 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2200 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
2203 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2205 p += sprintf((char *)p, "\x1B%c", code + 'P' - 11);
2207 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
2210 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2211 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
2215 p += sprintf((char *)p, "\x1B[%d~", code);
2220 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2221 * some reason seems to send VK_CLEAR to Windows...).
2226 case VK_UP: xkey = 'A'; break;
2227 case VK_DOWN: xkey = 'B'; break;
2228 case VK_RIGHT: xkey = 'C'; break;
2229 case VK_LEFT: xkey = 'D'; break;
2230 case VK_CLEAR: xkey = 'G'; break;
2235 p += sprintf((char *)p, "\x1B%c", xkey);
2236 else if (app_cursor_keys && !cfg.no_applic_c)
2237 p += sprintf((char *)p, "\x1BO%c", xkey);
2239 p += sprintf((char *)p, "\x1B[%c", xkey);
2245 * Finally, deal with Return ourselves. (Win95 seems to
2246 * foul it up when Alt is pressed, for some reason.)
2248 if (wParam == VK_RETURN) /* Return */
2255 /* Okay we've done everything interesting; let windows deal with
2256 * the boring stuff */
2258 BOOL capsOn=keystate[VK_CAPITAL] !=0;
2260 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2261 if(cfg.xlat_capslockcyr)
2262 keystate[VK_CAPITAL] = 0;
2264 r = ToAscii (wParam, scan, keystate, keys, 0);
2270 unsigned char ch = (unsigned char)keys[i];
2272 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
2277 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
2281 if ((nc=check_compose(compose_char,ch)) == -1)
2283 MessageBeep(MB_ICONHAND);
2286 *p++ = xlat_kbd2tty((unsigned char)nc);
2292 if( left_alt && key_down ) *p++ = '\033';
2298 ch = xlat_latkbd2win(ch);
2299 *p++ = xlat_kbd2tty(ch);
2303 /* This is so the ALT-Numpad and dead keys work correctly. */
2310 /* ALT alone may or may not want to bring up the System menu */
2311 if (wParam == VK_MENU) {
2313 static int alt_state = 0;
2314 if (message == WM_SYSKEYDOWN)
2316 else if (message == WM_SYSKEYUP && alt_state)
2317 PostMessage(hwnd, WM_CHAR, ' ', 0);
2318 if (message == WM_SYSKEYUP)
2327 void set_title (char *title) {
2328 sfree (window_name);
2329 window_name = smalloc(1+strlen(title));
2330 strcpy (window_name, title);
2331 if (cfg.win_name_always || !IsIconic(hwnd))
2332 SetWindowText (hwnd, title);
2335 void set_icon (char *title) {
2337 icon_name = smalloc(1+strlen(title));
2338 strcpy (icon_name, title);
2339 if (!cfg.win_name_always && IsIconic(hwnd))
2340 SetWindowText (hwnd, title);
2343 void set_sbar (int total, int start, int page) {
2346 if (!cfg.scrollbar) return;
2348 si.cbSize = sizeof(si);
2349 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2351 si.nMax = total - 1;
2355 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2358 Context get_ctx(void) {
2363 SelectPalette (hdc, pal, FALSE);
2369 void free_ctx (Context ctx) {
2370 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2371 ReleaseDC (hwnd, ctx);
2374 static void real_palette_set (int n, int r, int g, int b) {
2376 logpal->palPalEntry[n].peRed = r;
2377 logpal->palPalEntry[n].peGreen = g;
2378 logpal->palPalEntry[n].peBlue = b;
2379 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2380 colours[n] = PALETTERGB(r, g, b);
2381 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2383 colours[n] = RGB(r, g, b);
2386 void palette_set (int n, int r, int g, int b) {
2387 static const int first[21] = {
2388 0, 2, 4, 6, 8, 10, 12, 14,
2389 1, 3, 5, 7, 9, 11, 13, 15,
2392 real_palette_set (first[n], r, g, b);
2394 real_palette_set (first[n]+1, r, g, b);
2396 HDC hdc = get_ctx();
2397 UnrealizeObject (pal);
2398 RealizePalette (hdc);
2403 void palette_reset (void) {
2406 for (i = 0; i < NCOLOURS; i++) {
2408 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2409 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2410 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2411 logpal->palPalEntry[i].peFlags = 0;
2412 colours[i] = PALETTERGB(defpal[i].rgbtRed,
2413 defpal[i].rgbtGreen,
2414 defpal[i].rgbtBlue);
2416 colours[i] = RGB(defpal[i].rgbtRed,
2417 defpal[i].rgbtGreen,
2418 defpal[i].rgbtBlue);
2423 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2425 RealizePalette (hdc);
2430 void write_clip (void *data, int len, int must_deselect) {
2434 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2437 lock = GlobalLock (clipdata);
2440 memcpy (lock, data, len);
2441 ((unsigned char *) lock) [len] = 0;
2442 GlobalUnlock (clipdata);
2445 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2447 if (OpenClipboard (hwnd)) {
2449 SetClipboardData (CF_TEXT, clipdata);
2452 GlobalFree (clipdata);
2455 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2458 void get_clip (void **p, int *len) {
2459 static HGLOBAL clipdata = NULL;
2463 GlobalUnlock (clipdata);
2467 if (OpenClipboard (NULL)) {
2468 clipdata = GetClipboardData (CF_TEXT);
2471 *p = GlobalLock (clipdata);
2485 * Move `lines' lines from position `from' to position `to' in the
2488 void optimised_move (int to, int from, int lines) {
2492 min = (to < from ? to : from);
2493 max = to + from - min;
2495 r.left = 0; r.right = cols * font_width;
2496 r.top = min * font_height; r.bottom = (max+lines) * font_height;
2497 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2501 * Print a message box and perform a fatal exit.
2503 void fatalbox(char *fmt, ...) {
2508 vsprintf(stuff, fmt, ap);
2510 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2517 void beep(int errorbeep) {
2518 static long last_beep = 0;
2519 long now, beep_diff;
2521 now = GetTickCount();
2522 beep_diff = now-last_beep;
2524 /* Make sure we only respond to one beep per packet or so */
2525 if (beep_diff>=0 && beep_diff<50)
2529 MessageBeep(MB_ICONHAND);
2533 last_beep = GetTickCount();