15 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
20 #define IDM_SHOWLOG 0x0010
21 #define IDM_NEWSESS 0x0020
22 #define IDM_DUPSESS 0x0030
23 #define IDM_RECONF 0x0040
24 #define IDM_CLRSB 0x0050
25 #define IDM_RESET 0x0060
26 #define IDM_TEL_AYT 0x0070
27 #define IDM_TEL_BRK 0x0080
28 #define IDM_TEL_SYNCH 0x0090
29 #define IDM_TEL_EC 0x00a0
30 #define IDM_TEL_EL 0x00b0
31 #define IDM_TEL_GA 0x00c0
32 #define IDM_TEL_NOP 0x00d0
33 #define IDM_TEL_ABORT 0x00e0
34 #define IDM_TEL_AO 0x00f0
35 #define IDM_TEL_IP 0x0100
36 #define IDM_TEL_SUSP 0x0110
37 #define IDM_TEL_EOR 0x0120
38 #define IDM_TEL_EOF 0x0130
39 #define IDM_ABOUT 0x0140
40 #define IDM_SAVEDSESS 0x0150
42 #define IDM_SAVED_MIN 0x1000
43 #define IDM_SAVED_MAX 0x2000
45 #define WM_IGNORE_SIZE (WM_XUSER + 1)
46 #define WM_IGNORE_CLIP (WM_XUSER + 2)
48 static LRESULT CALLBACK
WndProc (HWND
, UINT
, WPARAM
, LPARAM
);
49 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
, unsigned char *output
);
50 static void cfgtopalette(void);
51 static void init_palette(void);
52 static void init_fonts(int);
54 static int extra_width
, extra_height
;
56 static int pending_netevent
= 0;
57 static WPARAM pend_netevent_wParam
= 0;
58 static LPARAM pend_netevent_lParam
= 0;
59 static void enact_pending_netevent(void);
61 static time_t last_movement
= 0;
65 #define FONT_UNDERLINE 2
66 #define FONT_BOLDUND 3
68 #define FONT_OEMBOLD 5
69 #define FONT_OEMBOLDUND 6
71 static HFONT fonts
[8];
72 static int font_needs_hand_underlining
;
74 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
82 static COLORREF colours
[NCOLOURS
];
84 static LPLOGPALETTE logpal
;
85 static RGBTRIPLE defpal
[NCOLOURS
];
89 static HBITMAP caretbm
;
91 static int dbltime
, lasttime
, lastact
;
92 static Mouse_Button lastbtn
;
94 static char *window_name
, *icon_name
;
96 static Ldisc
*real_ldisc
;
98 void begin_session(void) {
102 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
) {
103 static char appname
[] = "PuTTY";
108 int guess_width
, guess_height
;
111 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
113 winsock_ver
= MAKEWORD(1, 1);
114 if (WSAStartup(winsock_ver
, &wsadata
)) {
115 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
116 MB_OK
| MB_ICONEXCLAMATION
);
119 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
120 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
121 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
125 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
127 InitCommonControls();
130 * Process the command line.
135 default_protocol
= DEFAULT_PROTOCOL
;
136 default_port
= DEFAULT_PORT
;
138 do_defaults(NULL
, &cfg
);
141 while (*p
&& isspace(*p
)) p
++;
144 * Process command line options first. Yes, this can be
145 * done better, and it will be as soon as I have the
149 char *q
= p
+ strcspn(p
, " \t");
152 tolower(p
[0]) == 's' &&
153 tolower(p
[1]) == 's' &&
154 tolower(p
[2]) == 'h') {
155 default_protocol
= cfg
.protocol
= PROT_SSH
;
156 default_port
= cfg
.port
= 22;
157 } else if (q
== p
+ 3 &&
158 tolower(p
[0]) == 'l' &&
159 tolower(p
[1]) == 'o' &&
160 tolower(p
[2]) == 'g') {
161 logfile
= "putty.log";
162 } else if (q
== p
+ 7 &&
163 tolower(p
[0]) == 'c' &&
164 tolower(p
[1]) == 'l' &&
165 tolower(p
[2]) == 'e' &&
166 tolower(p
[3]) == 'a' &&
167 tolower(p
[4]) == 'n' &&
168 tolower(p
[5]) == 'u' &&
169 tolower(p
[6]) == 'p') {
171 * `putty -cleanup'. Remove all registry entries
172 * associated with PuTTY, and also find and delete
173 * the random seed file.
176 "This procedure will remove ALL Registry\n"
177 "entries associated with PuTTY, and will\n"
178 "also remove the PuTTY random seed file.\n"
180 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
181 "SESSIONS. Are you really sure you want\n"
184 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
189 p
= q
+ strspn(q
, " \t");
193 * An initial @ means to activate a saved session.
196 do_defaults (p
+1, &cfg
);
197 if (!*cfg
.host
&& !do_config()) {
201 } else if (*p
== '&') {
203 * An initial & means we've been given a command line
204 * containing the hex value of a HANDLE for a file
205 * mapping object, which we must then extract as a
210 if (sscanf(p
+1, "%p", &filemap
) == 1 &&
211 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
212 0, 0, sizeof(Config
))) != NULL
) {
215 CloseHandle(filemap
);
216 } else if (!do_config()) {
223 * If the hostname starts with "telnet:", set the
224 * protocol to Telnet and process the string as a
227 if (!strncmp(q
, "telnet:", 7)) {
231 if (q
[0] == '/' && q
[1] == '/')
233 cfg
.protocol
= PROT_TELNET
;
235 while (*p
&& *p
!= ':' && *p
!= '/') p
++;
243 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
244 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
246 while (*p
&& !isspace(*p
)) p
++;
249 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
250 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
251 while (*p
&& isspace(*p
)) p
++;
264 /* See if host is of the form user@host */
265 if (cfg
.host
[0] != '\0') {
266 char *atsign
= strchr(cfg
.host
, '@');
267 /* Make sure we're not overflowing the user field */
269 if (atsign
-cfg
.host
< sizeof cfg
.username
) {
270 strncpy (cfg
.username
, cfg
.host
, atsign
-cfg
.host
);
271 cfg
.username
[atsign
-cfg
.host
] = '\0';
273 memmove(cfg
.host
, atsign
+1, 1+strlen(atsign
+1));
279 * Select protocol. This is farmed out into a table in a
280 * separate file to enable an ssh-free variant.
285 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
286 if (backends
[i
].protocol
== cfg
.protocol
) {
287 back
= backends
[i
].backend
;
291 MessageBox(NULL
, "Unsupported protocol number found",
292 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
298 real_ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
299 /* To start with, we use the simple line discipline, so we can
300 * type passwords etc without fear of them being echoed... */
301 ldisc
= &ldisc_simple
;
305 wndclass
.lpfnWndProc
= WndProc
;
306 wndclass
.cbClsExtra
= 0;
307 wndclass
.cbWndExtra
= 0;
308 wndclass
.hInstance
= inst
;
309 wndclass
.hIcon
= LoadIcon (inst
,
310 MAKEINTRESOURCE(IDI_MAINICON
));
311 wndclass
.hCursor
= LoadCursor (NULL
, IDC_IBEAM
);
312 wndclass
.hbrBackground
= GetStockObject (BLACK_BRUSH
);
313 wndclass
.lpszMenuName
= NULL
;
314 wndclass
.lpszClassName
= appname
;
316 RegisterClass (&wndclass
);
321 savelines
= cfg
.savelines
;
327 * Guess some defaults for the window size. This all gets
328 * updated later, so we don't really care too much. However, we
329 * do want the font width/height guesses to correspond to a
330 * large font rather than a small one...
337 term_size (cfg
.height
, cfg
.width
, cfg
.savelines
);
338 guess_width
= extra_width
+ font_width
* cols
;
339 guess_height
= extra_height
+ font_height
* rows
;
342 HWND w
= GetDesktopWindow();
343 GetWindowRect (w
, &r
);
344 if (guess_width
> r
.right
- r
.left
)
345 guess_width
= r
.right
- r
.left
;
346 if (guess_height
> r
.bottom
- r
.top
)
347 guess_height
= r
.bottom
- r
.top
;
351 int winmode
= WS_OVERLAPPEDWINDOW
|WS_VSCROLL
;
352 if (!cfg
.scrollbar
) winmode
&= ~(WS_VSCROLL
);
353 if (cfg
.locksize
) winmode
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
354 hwnd
= CreateWindow (appname
, appname
,
356 CW_USEDEFAULT
, CW_USEDEFAULT
,
357 guess_width
, guess_height
,
358 NULL
, NULL
, inst
, NULL
);
362 * Initialise the fonts, simultaneously correcting the guesses
363 * for font_{width,height}.
365 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
370 * Correct the guesses for extra_{width,height}.
374 GetWindowRect (hwnd
, &wr
);
375 GetClientRect (hwnd
, &cr
);
376 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
377 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
381 * Resize the window, now we know what size we _really_ want it
384 guess_width
= extra_width
+ font_width
* cols
;
385 guess_height
= extra_height
+ font_height
* rows
;
386 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
387 SetWindowPos (hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
388 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
391 * Set up a caret bitmap, with no content.
395 int size
= (font_width
+15)/16 * 2 * font_height
;
396 bits
= calloc(size
, 1);
397 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
402 * Initialise the scroll bar.
407 si
.cbSize
= sizeof(si
);
408 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
413 SetScrollInfo (hwnd
, SB_VERT
, &si
, FALSE
);
417 * Start up the telnet connection.
421 char msg
[1024], *title
;
424 error
= back
->init (hwnd
, cfg
.host
, cfg
.port
, &realhost
);
426 sprintf(msg
, "Unable to open connection:\n%s", error
);
427 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
430 window_name
= icon_name
= NULL
;
432 title
= cfg
.wintitle
;
434 sprintf(msg
, "%s - PuTTY", realhost
);
441 session_closed
= FALSE
;
444 * Set up the input and output buffers.
447 outbuf_reap
= outbuf_head
= 0;
450 * Prepare the mouse handler.
452 lastact
= MA_NOTHING
;
453 lastbtn
= MB_NOTHING
;
454 dbltime
= GetDoubleClickTime();
457 * Set up the session-control options on the system menu.
460 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
464 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
465 if (cfg
.protocol
== PROT_TELNET
) {
467 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
468 AppendMenu (p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
469 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
470 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
471 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
472 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
473 AppendMenu (p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
474 AppendMenu (p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
475 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
476 AppendMenu (p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
477 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
478 AppendMenu (p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
479 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
480 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
481 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
482 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
483 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
, "Telnet Command");
484 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
486 AppendMenu (m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
487 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
488 AppendMenu (m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session");
489 AppendMenu (m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
492 for (i
= 1 ; i
< ((nsessions
< 256) ? nsessions
: 256) ; i
++)
493 AppendMenu (s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
) , sessions
[i
]);
494 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
495 AppendMenu (m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings");
496 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
497 AppendMenu (m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
498 AppendMenu (m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
499 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
500 AppendMenu (m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
504 * Finally show the window!
506 ShowWindow (hwnd
, show
);
509 * Set the palette up.
515 has_focus
= (GetForegroundWindow() == hwnd
);
518 if (GetMessage (&msg
, NULL
, 0, 0) == 1)
520 int timer_id
= 0, long_timer
= 0;
522 while (msg
.message
!= WM_QUIT
) {
523 /* Sometimes DispatchMessage calls routines that use their own
524 * GetMessage loop, setup this timer so we get some control back.
526 * Also call term_update() from the timer so that if the host
527 * is sending data flat out we still do redraws.
529 if(timer_id
&& long_timer
) {
530 KillTimer(hwnd
, timer_id
);
531 long_timer
= timer_id
= 0;
534 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
535 DispatchMessage (&msg
);
537 /* Make sure we blink everything that needs it. */
540 /* Send the paste buffer if there's anything to send */
543 /* If there's nothing new in the queue then we can do everything
544 * we've delayed, reading the socket, writing, and repainting
547 if (PeekMessage (&msg
, NULL
, 0, 0, PM_REMOVE
))
550 if (pending_netevent
) {
551 enact_pending_netevent();
553 /* Force the cursor blink on */
556 if (PeekMessage (&msg
, NULL
, 0, 0, PM_REMOVE
))
560 /* Okay there is now nothing to do so we make sure the screen is
561 * completely up to date then tell windows to call us in a little
565 KillTimer(hwnd
, timer_id
);
574 timer_id
= SetTimer(hwnd
, 1, 59500, NULL
);
576 timer_id
= SetTimer(hwnd
, 1, 250, NULL
);
579 /* There's no point rescanning everything in the message queue
580 * so we do an apperently unneccesary wait here
583 if (GetMessage (&msg
, NULL
, 0, 0) != 1)
595 DeleteObject(fonts
[i
]);
602 if (cfg
.protocol
== PROT_SSH
) {
613 * Print a message box and close the connection.
615 void connection_fatal(char *fmt
, ...) {
620 vsprintf(stuff
, fmt
, ap
);
622 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
623 if (cfg
.close_on_exit
)
626 session_closed
= TRUE
;
627 SetWindowText (hwnd
, "PuTTY (inactive)");
632 * Actually do the job requested by a WM_NETEVENT
634 static void enact_pending_netevent(void) {
636 static int reentering
= 0;
639 return; /* don't unpend the pending */
641 pending_netevent
= FALSE
;
644 i
= back
->msg (pend_netevent_wParam
, pend_netevent_lParam
);
649 switch (WSABASEERR
+ (-i
) % 10000) {
651 sprintf(buf
, "Connection reset by peer");
653 case WSAECONNABORTED
:
654 sprintf(buf
, "Connection aborted");
657 sprintf(buf
, "Unexpected network error %d", -i
);
660 connection_fatal(buf
);
663 if (cfg
.close_on_exit
)
666 session_closed
= TRUE
;
667 MessageBox(hwnd
, "Connection closed by remote host",
668 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
669 SetWindowText (hwnd
, "PuTTY (inactive)");
675 * Copy the colour palette from the configuration data into defpal.
676 * This is non-trivial because the colour indices are different.
678 static void cfgtopalette(void) {
680 static const int ww
[] = {
681 6, 7, 8, 9, 10, 11, 12, 13,
682 14, 15, 16, 17, 18, 19, 20, 21,
683 0, 1, 2, 3, 4, 4, 5, 5
686 for (i
=0; i
<24; i
++) {
688 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
689 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
690 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
695 * Set up the colour palette.
697 static void init_palette(void) {
699 HDC hdc
= GetDC (hwnd
);
701 if (cfg
.try_palette
&&
702 GetDeviceCaps (hdc
, RASTERCAPS
) & RC_PALETTE
) {
703 logpal
= smalloc(sizeof(*logpal
)
704 - sizeof(logpal
->palPalEntry
)
705 + NCOLOURS
* sizeof(PALETTEENTRY
));
706 logpal
->palVersion
= 0x300;
707 logpal
->palNumEntries
= NCOLOURS
;
708 for (i
= 0; i
< NCOLOURS
; i
++) {
709 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
710 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
711 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
712 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
714 pal
= CreatePalette (logpal
);
716 SelectPalette (hdc
, pal
, FALSE
);
717 RealizePalette (hdc
);
718 SelectPalette (hdc
, GetStockObject (DEFAULT_PALETTE
),
722 ReleaseDC (hwnd
, hdc
);
725 for (i
=0; i
<NCOLOURS
; i
++)
726 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
730 for(i
=0; i
<NCOLOURS
; i
++)
731 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
737 * Initialise all the fonts we will need. There may be as many as
738 * eight or as few as one. We also:
740 * - check the font width and height, correcting our guesses if
743 * - verify that the bold font is the same width as the ordinary
744 * one, and engage shadow bolding if not.
746 * - verify that the underlined font is the same width as the
747 * ordinary one (manual underlining by means of line drawing can
748 * be done in a pinch).
750 static void init_fonts(int pick_width
) {
755 int fw_dontcare
, fw_bold
;
764 if (cfg
.fontisbold
) {
765 fw_dontcare
= FW_BOLD
;
768 fw_dontcare
= FW_DONTCARE
;
774 font_height
= cfg
.fontheight
;
775 font_width
= pick_width
;
778 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
779 c, OUT_DEFAULT_PRECIS, \
780 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
781 FIXED_PITCH | FF_DONTCARE, cfg.font)
783 if (cfg
.vtmode
!= VT_OEMONLY
) {
784 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
786 SelectObject (hdc
, fonts
[FONT_NORMAL
]);
787 GetTextMetrics(hdc
, &tm
);
788 font_height
= tm
.tmHeight
;
789 font_width
= tm
.tmAveCharWidth
;
791 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
794 * Some fonts, e.g. 9-pt Courier, draw their underlines
795 * outside their character cell. We successfully prevent
796 * screen corruption by clipping the text output, but then
797 * we lose the underline completely. Here we try to work
798 * out whether this is such a font, and if it is, we set a
799 * flag that causes underlines to be drawn by hand.
801 * Having tried other more sophisticated approaches (such
802 * as examining the TEXTMETRIC structure or requesting the
803 * height of a string), I think we'll do this the brute
804 * force way: we create a small bitmap, draw an underlined
805 * space on it, and test to see whether any pixels are
806 * foreground-coloured. (Since we expect the underline to
807 * go all the way across the character cell, we only search
808 * down a single column of the bitmap, half way across.)
812 HBITMAP und_bm
, und_oldbm
;
816 und_dc
= CreateCompatibleDC(hdc
);
817 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
818 und_oldbm
= SelectObject(und_dc
, und_bm
);
819 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
820 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
821 SetTextColor (und_dc
, RGB(255,255,255));
822 SetBkColor (und_dc
, RGB(0,0,0));
823 SetBkMode (und_dc
, OPAQUE
);
824 ExtTextOut (und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
826 for (i
= 0; i
< font_height
; i
++) {
827 c
= GetPixel(und_dc
, font_width
/2, i
);
831 SelectObject(und_dc
, und_oldbm
);
832 DeleteObject(und_bm
);
834 font_needs_hand_underlining
= !gotit
;
837 if (bold_mode
== BOLD_FONT
) {
838 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
839 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
842 if (cfg
.vtmode
== VT_OEMANSI
) {
843 f(FONT_OEM
, OEM_CHARSET
, fw_dontcare
, FALSE
);
844 f(FONT_OEMUND
, OEM_CHARSET
, fw_dontcare
, TRUE
);
846 if (bold_mode
== BOLD_FONT
) {
847 f(FONT_OEMBOLD
, OEM_CHARSET
, fw_bold
, FALSE
);
848 f(FONT_OEMBOLDUND
, OEM_CHARSET
, fw_bold
, TRUE
);
854 f(FONT_OEM
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
856 SelectObject (hdc
, fonts
[FONT_OEM
]);
857 GetTextMetrics(hdc
, &tm
);
858 font_height
= tm
.tmHeight
;
859 font_width
= tm
.tmAveCharWidth
;
861 f(FONT_OEMUND
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
863 if (bold_mode
== BOLD_FONT
) {
864 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
865 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
870 descent
= tm
.tmAscent
+ 1;
871 if (descent
>= font_height
)
872 descent
= font_height
- 1;
873 firstchar
= tm
.tmFirstChar
;
875 for (i
=0; i
<8; i
++) {
877 if (SelectObject (hdc
, fonts
[i
]) &&
878 GetTextMetrics(hdc
, &tm
) )
879 fsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
885 ReleaseDC (hwnd
, hdc
);
887 /* ... This is wrong in OEM only mode */
888 if (fsize
[FONT_UNDERLINE
] != fsize
[FONT_NORMAL
] ||
889 (bold_mode
== BOLD_FONT
&&
890 fsize
[FONT_BOLDUND
] != fsize
[FONT_BOLD
])) {
892 DeleteObject (fonts
[FONT_UNDERLINE
]);
893 if (bold_mode
== BOLD_FONT
)
894 DeleteObject (fonts
[FONT_BOLDUND
]);
897 if (bold_mode
== BOLD_FONT
&&
898 fsize
[FONT_BOLD
] != fsize
[FONT_NORMAL
]) {
899 bold_mode
= BOLD_SHADOW
;
900 DeleteObject (fonts
[FONT_BOLD
]);
901 if (und_mode
== UND_FONT
)
902 DeleteObject (fonts
[FONT_BOLDUND
]);
906 /* With the fascist font painting it doesn't matter if the linedraw font
907 * isn't exactly the right size anymore so we don't have to check this.
909 if (cfg
.vtmode
== VT_OEMANSI
&& fsize
[FONT_OEM
] != fsize
[FONT_NORMAL
] ) {
910 if( cfg
.fontcharset
== OEM_CHARSET
)
912 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
913 "different sizes. Using OEM-only mode instead",
914 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
915 cfg
.vtmode
= VT_OEMONLY
;
917 else if( firstchar
< ' ' )
919 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
920 "different sizes. Using XTerm mode instead",
921 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
922 cfg
.vtmode
= VT_XWINDOWS
;
926 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
927 "different sizes. Using ISO8859-1 mode instead",
928 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
929 cfg
.vtmode
= VT_POORMAN
;
934 DeleteObject (fonts
[i
]);
940 void request_resize (int w
, int h
, int refont
) {
943 /* If the window is maximized supress resizing attempts */
944 if(IsZoomed(hwnd
)) return;
947 /* Don't do this in OEMANSI, you may get disable messages */
948 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132)
949 && cfg
.vtmode
!= VT_OEMANSI
)
951 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132))
954 /* If font width too big for screen should we shrink the font more ? */
956 font_width
= ((font_width
*cols
+w
/2)/w
);
963 DeleteObject(fonts
[i
]);
965 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
967 init_fonts(font_width
);
971 static int first_time
= 1;
977 /* Get the size of the screen */
978 if (GetClientRect(GetDesktopWindow(),&ss
))
979 /* first_time = 0 */;
980 else { first_time
= 2; break; }
982 /* Make sure the values are sane */
983 width
= (ss
.right
-ss
.left
-extra_width
) / font_width
;
984 height
= (ss
.bottom
-ss
.top
-extra_height
) / font_height
;
986 if (w
>width
) w
=width
;
987 if (h
>height
) h
=height
;
993 width
= extra_width
+ font_width
* w
;
994 height
= extra_height
+ font_height
* h
;
996 SetWindowPos (hwnd
, NULL
, 0, 0, width
, height
,
997 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
998 SWP_NOMOVE
| SWP_NOZORDER
);
1001 static void click (Mouse_Button b
, int x
, int y
) {
1002 int thistime
= GetMessageTime();
1004 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1005 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1006 lastact
== MA_2CLK ? MA_3CLK
:
1007 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1012 if (lastact
!= MA_NOTHING
)
1013 term_mouse (b
, lastact
, x
, y
);
1014 lasttime
= thistime
;
1017 static LRESULT CALLBACK
WndProc (HWND hwnd
, UINT message
,
1018 WPARAM wParam
, LPARAM lParam
) {
1020 static int ignore_size
= FALSE
;
1021 static int ignore_clip
= FALSE
;
1022 static int just_reconfigged
= FALSE
;
1023 static int resizing
= FALSE
;
1027 if (pending_netevent
)
1028 enact_pending_netevent();
1034 if (cfg
.ping_interval
> 0)
1038 if (now
-last_movement
> cfg
.ping_interval
* 60 - 10)
1040 back
->special(TS_PING
);
1041 last_movement
= now
;
1048 if (!cfg
.warn_on_close
|| session_closed
||
1049 MessageBox(hwnd
, "Are you sure you want to close this session?",
1050 "PuTTY Exit Confirmation",
1051 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1052 DestroyWindow(hwnd
);
1055 PostQuitMessage (0);
1058 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1070 PROCESS_INFORMATION pi
;
1071 HANDLE filemap
= NULL
;
1073 if (wParam
== IDM_DUPSESS
) {
1075 * Allocate a file-mapping memory chunk for the
1078 SECURITY_ATTRIBUTES sa
;
1081 sa
.nLength
= sizeof(sa
);
1082 sa
.lpSecurityDescriptor
= NULL
;
1083 sa
.bInheritHandle
= TRUE
;
1084 filemap
= CreateFileMapping((HANDLE
)0xFFFFFFFF,
1091 p
= (Config
*)MapViewOfFile(filemap
,
1093 0, 0, sizeof(Config
));
1095 *p
= cfg
; /* structure copy */
1099 sprintf(c
, "putty &%p", filemap
);
1101 } else if (wParam
== IDM_SAVEDSESS
) {
1102 char *session
= sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1103 cl
= malloc(16 + strlen(session
)); /* 8, but play safe */
1105 cl
= NULL
; /* not a very important failure mode */
1107 sprintf(cl
, "putty @%s", session
);
1113 GetModuleFileName (NULL
, b
, sizeof(b
)-1);
1115 si
.lpReserved
= NULL
;
1116 si
.lpDesktop
= NULL
;
1120 si
.lpReserved2
= NULL
;
1121 CreateProcess (b
, cl
, NULL
, NULL
, TRUE
,
1122 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1125 CloseHandle(filemap
);
1131 if (!do_reconfig(hwnd
))
1133 just_reconfigged
= TRUE
;
1138 DeleteObject(fonts
[i
]);
1140 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1141 und_mode
= UND_FONT
;
1144 /* Telnet will change local echo -> remote if the remote asks */
1145 if (cfg
.protocol
!= PROT_TELNET
)
1146 ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
1154 /* Enable or disable the scroll bar, etc */
1156 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1159 if (cfg
.scrollbar
) nflg
|= WS_VSCROLL
;
1160 else nflg
&= ~WS_VSCROLL
;
1162 nflg
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1164 nflg
|= (WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1170 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1171 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1172 SetWindowPos(hwnd
, NULL
, 0,0,0,0,
1173 SWP_NOACTIVATE
|SWP_NOCOPYBITS
|
1174 SWP_NOMOVE
|SWP_NOSIZE
| SWP_NOZORDER
|
1177 GetWindowRect (hwnd
, &wr
);
1178 GetClientRect (hwnd
, &cr
);
1179 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1180 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1184 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1185 InvalidateRect(hwnd
, NULL
, TRUE
);
1186 SetWindowPos (hwnd
, NULL
, 0, 0,
1187 extra_width
+ font_width
* cfg
.width
,
1188 extra_height
+ font_height
* cfg
.height
,
1189 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1190 SWP_NOMOVE
| SWP_NOZORDER
);
1191 if (IsIconic(hwnd
)) {
1192 SetWindowText (hwnd
,
1193 cfg
.win_name_always ? window_name
: icon_name
);
1202 case IDM_TEL_AYT
: back
->special (TS_AYT
); break;
1203 case IDM_TEL_BRK
: back
->special (TS_BRK
); break;
1204 case IDM_TEL_SYNCH
: back
->special (TS_SYNCH
); break;
1205 case IDM_TEL_EC
: back
->special (TS_EC
); break;
1206 case IDM_TEL_EL
: back
->special (TS_EL
); break;
1207 case IDM_TEL_GA
: back
->special (TS_GA
); break;
1208 case IDM_TEL_NOP
: back
->special (TS_NOP
); break;
1209 case IDM_TEL_ABORT
: back
->special (TS_ABORT
); break;
1210 case IDM_TEL_AO
: back
->special (TS_AO
); break;
1211 case IDM_TEL_IP
: back
->special (TS_IP
); break;
1212 case IDM_TEL_SUSP
: back
->special (TS_SUSP
); break;
1213 case IDM_TEL_EOR
: back
->special (TS_EOR
); break;
1214 case IDM_TEL_EOF
: back
->special (TS_EOF
); break;
1219 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1220 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1225 #define X_POS(l) ((int)(short)LOWORD(l))
1226 #define Y_POS(l) ((int)(short)HIWORD(l))
1228 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1229 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1231 case WM_LBUTTONDOWN
:
1232 click (MB_SELECT
, TO_CHR_X(X_POS(lParam
)),
1233 TO_CHR_Y(Y_POS(lParam
)));
1237 term_mouse (MB_SELECT
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1238 TO_CHR_Y(Y_POS(lParam
)));
1241 case WM_MBUTTONDOWN
:
1243 click (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1244 TO_CHR_X(X_POS(lParam
)),
1245 TO_CHR_Y(Y_POS(lParam
)));
1248 term_mouse (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1249 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1250 TO_CHR_Y(Y_POS(lParam
)));
1253 case WM_RBUTTONDOWN
:
1255 click (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1256 TO_CHR_X(X_POS(lParam
)),
1257 TO_CHR_Y(Y_POS(lParam
)));
1260 term_mouse (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1261 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1262 TO_CHR_Y(Y_POS(lParam
)));
1267 * Add the mouse position and message time to the random
1268 * number noise, if we're using ssh.
1270 if (cfg
.protocol
== PROT_SSH
)
1271 noise_ultralight(lParam
);
1273 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1275 if (wParam
& MK_LBUTTON
)
1277 else if (wParam
& MK_MBUTTON
)
1278 b
= cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
;
1280 b
= cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
;
1281 term_mouse (b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1282 TO_CHR_Y(Y_POS(lParam
)));
1285 case WM_IGNORE_CLIP
:
1286 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1288 case WM_DESTROYCLIPBOARD
:
1291 ignore_clip
= FALSE
;
1297 hdc
= BeginPaint (hwnd
, &p
);
1299 SelectPalette (hdc
, pal
, TRUE
);
1300 RealizePalette (hdc
);
1302 term_paint (hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1303 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1304 SelectObject (hdc
, GetStockObject(SYSTEM_FONT
));
1305 SelectObject (hdc
, GetStockObject(WHITE_PEN
));
1306 EndPaint (hwnd
, &p
);
1311 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1312 * but the only one that's likely to try to overload us is FD_READ.
1313 * This means buffering just one is fine.
1315 if (pending_netevent
)
1316 enact_pending_netevent();
1318 pending_netevent
= TRUE
;
1319 pend_netevent_wParam
=wParam
;
1320 pend_netevent_lParam
=lParam
;
1321 time(&last_movement
);
1325 CreateCaret(hwnd
, caretbm
, 0, 0);
1336 case WM_IGNORE_SIZE
:
1337 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1339 case WM_ENTERSIZEMOVE
:
1343 case WM_EXITSIZEMOVE
:
1350 int width
, height
, w
, h
, ew
, eh
;
1351 LPRECT r
= (LPRECT
)lParam
;
1353 width
= r
->right
- r
->left
- extra_width
;
1354 height
= r
->bottom
- r
->top
- extra_height
;
1355 w
= (width
+ font_width
/2) / font_width
; if (w
< 1) w
= 1;
1356 h
= (height
+ font_height
/2) / font_height
; if (h
< 1) h
= 1;
1357 UpdateSizeTip(hwnd
, w
, h
);
1358 ew
= width
- w
* font_width
;
1359 eh
= height
- h
* font_height
;
1361 if (wParam
== WMSZ_LEFT
||
1362 wParam
== WMSZ_BOTTOMLEFT
||
1363 wParam
== WMSZ_TOPLEFT
)
1369 if (wParam
== WMSZ_TOP
||
1370 wParam
== WMSZ_TOPRIGHT
||
1371 wParam
== WMSZ_TOPLEFT
)
1381 /* break; (never reached) */
1383 if (wParam
== SIZE_MINIMIZED
) {
1384 SetWindowText (hwnd
,
1385 cfg
.win_name_always ? window_name
: icon_name
);
1388 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1389 SetWindowText (hwnd
, window_name
);
1391 int width
, height
, w
, h
;
1392 #if 0 /* we have fixed this using WM_SIZING now */
1396 width
= LOWORD(lParam
);
1397 height
= HIWORD(lParam
);
1398 w
= width
/ font_width
; if (w
< 1) w
= 1;
1399 h
= height
/ font_height
; if (h
< 1) h
= 1;
1400 #if 0 /* we have fixed this using WM_SIZING now */
1401 ew
= width
- w
* font_width
;
1402 eh
= height
- h
* font_height
;
1403 if (ew
!= 0 || eh
!= 0) {
1405 GetWindowRect (hwnd
, &r
);
1406 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1407 SetWindowPos (hwnd
, NULL
, 0, 0,
1408 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1409 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1412 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1414 term_size (h
, w
, cfg
.savelines
);
1416 * Don't call back->size in mid-resize. (To prevent
1417 * massive numbers of resize events getting sent
1418 * down the connection during an NT opaque drag.)
1422 just_reconfigged
= FALSE
;
1425 ignore_size
= FALSE
;
1428 switch (LOWORD(wParam
)) {
1429 case SB_BOTTOM
: term_scroll(-1, 0); break;
1430 case SB_TOP
: term_scroll(+1, 0); break;
1431 case SB_LINEDOWN
: term_scroll (0, +1); break;
1432 case SB_LINEUP
: term_scroll (0, -1); break;
1433 case SB_PAGEDOWN
: term_scroll (0, +rows
/2); break;
1434 case SB_PAGEUP
: term_scroll (0, -rows
/2); break;
1435 case SB_THUMBPOSITION
: case SB_THUMBTRACK
:
1436 term_scroll (1, HIWORD(wParam
)); break;
1439 case WM_PALETTECHANGED
:
1440 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1441 HDC hdc
= get_ctx();
1443 if (RealizePalette (hdc
) > 0)
1449 case WM_QUERYNEWPALETTE
:
1451 HDC hdc
= get_ctx();
1453 if (RealizePalette (hdc
) > 0)
1465 * Add the scan code and keypress timing to the random
1466 * number noise, if we're using ssh.
1468 if (cfg
.protocol
== PROT_SSH
)
1469 noise_ultralight(lParam
);
1472 * We don't do TranslateMessage since it disassociates the
1473 * resulting CHAR message from the KEYDOWN that sparked it,
1474 * which we occasionally don't want. Instead, we process
1475 * KEYDOWN, and call the Win32 translator functions so that
1476 * we get the translations under _our_ control.
1479 unsigned char buf
[20];
1482 len
= TranslateKey (message
, wParam
, lParam
, buf
);
1484 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1485 ldisc
->send (buf
, len
);
1491 * Nevertheless, we are prepared to deal with WM_CHAR
1492 * messages, should they crop up. So if someone wants to
1493 * post the things to us as part of a macro manoeuvre,
1494 * we're ready to cope.
1497 char c
= xlat_kbd2tty((unsigned char)wParam
);
1498 ldisc
->send (&c
, 1);
1503 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1507 * Move the system caret. (We maintain one, even though it's
1508 * invisible, for the benefit of blind people: apparently some
1509 * helper software tracks the system caret, so we should arrange to
1512 void sys_cursor(int x
, int y
) {
1513 SetCaretPos(x
* font_width
, y
* font_height
);
1517 * Draw a line of text in the window, at given character
1518 * coordinates, in given attributes.
1520 * We are allowed to fiddle with the contents of `text'.
1522 void do_text (Context ctx
, int x
, int y
, char *text
, int len
,
1523 unsigned long attr
, int lattr
) {
1525 int nfg
, nbg
, nfont
;
1528 int force_manual_underline
= 0;
1529 int fnt_width
= font_width
*(1+(lattr
!=LATTR_NORM
));
1530 static int *IpDx
= 0, IpDxLEN
= 0;;
1532 if (len
>IpDxLEN
|| IpDx
[0] != fnt_width
) {
1536 IpDx
= smalloc((len
+16)*sizeof(int));
1539 for(i
=0; i
<IpDxLEN
; i
++)
1540 IpDx
[i
] = fnt_width
;
1546 if (attr
& ATTR_ACTCURS
) {
1547 attr
&= (bold_mode
== BOLD_COLOURS ?
0x300200 : 0x300300);
1548 attr
^= ATTR_CUR_XOR
;
1552 if (cfg
.vtmode
== VT_OEMONLY
)
1556 * Map high-half characters in order to approximate ISO using
1557 * OEM character set. No characters are missing if the OEM codepage
1560 if (nfont
& FONT_OEM
) {
1562 for (i
=0; i
<len
; i
++)
1563 if (text
[i
] >= '\xA0' && text
[i
] <= '\xFF') {
1565 /* This is CP850 ... perfect translation */
1566 static const char oemhighhalf
[] =
1567 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1568 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1569 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1570 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1571 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1572 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1573 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1574 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1575 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1576 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1577 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1578 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1581 /* This is CP437 ... junk translation */
1582 static const unsigned char oemhighhalf
[] = {
1583 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1584 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1585 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1586 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1587 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1588 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1589 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1590 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1591 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1592 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1593 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1594 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1597 text
[i
] = oemhighhalf
[(unsigned char)text
[i
] - 0xA0];
1601 if (attr
& ATTR_LINEDRW
) {
1604 static const char poorman
[] =
1605 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1608 static const char oemmap_437
[] =
1609 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1610 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1613 static const char oemmap_850
[] =
1614 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1615 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1617 /* Poor windows font ... eg: windows courier */
1618 static const char oemmap
[] =
1619 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1620 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1623 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1624 * VT100 line drawing chars; everything else stays normal.
1626 * Actually '_' maps to space too, but that's done before.
1628 switch (cfg
.vtmode
) {
1630 for (i
=0; i
<len
; i
++)
1631 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1632 text
[i
] += '\x01' - '\x60';
1635 /* Make sure we actually have an OEM font */
1636 if (fonts
[nfont
|FONT_OEM
]) {
1639 for (i
=0; i
<len
; i
++)
1640 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1641 text
[i
] = oemmap
[(unsigned char)text
[i
] - 0x60];
1645 for (i
=0; i
<len
; i
++)
1646 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1647 text
[i
] = poorman
[(unsigned char)text
[i
] - 0x60];
1652 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
1653 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
1654 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
1656 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
1657 nfont
|= FONT_UNDERLINE
;
1660 if (nfont
&FONT_UNDERLINE
)
1661 force_manual_underline
= 1;
1662 /* Don't do the same for manual bold, it could be bad news. */
1664 nfont
&= ~(FONT_BOLD
|FONT_UNDERLINE
);
1666 if (font_needs_hand_underlining
&& (attr
& ATTR_UNDER
))
1667 force_manual_underline
= 1;
1668 if (attr
& ATTR_REVERSE
) {
1669 t
= nfg
; nfg
= nbg
; nbg
= t
;
1671 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
1673 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
1677 SelectObject (hdc
, fonts
[nfont
]);
1678 SetTextColor (hdc
, fg
);
1679 SetBkColor (hdc
, bg
);
1680 SetBkMode (hdc
, OPAQUE
);
1683 line_box
.right
= x
+fnt_width
*len
;
1684 line_box
.bottom
= y
+font_height
;
1685 ExtTextOut (hdc
, x
, y
, ETO_CLIPPED
|ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
1686 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
1687 SetBkMode (hdc
, TRANSPARENT
);
1689 /* GRR: This draws the character outside it's box and can leave
1690 * 'droppings' even with the clip box! I suppose I could loop it
1691 * one character at a time ... yuk.
1693 * Or ... I could do a test print with "W", and use +1 or -1 for this
1694 * shift depending on if the leftmost column is blank...
1696 ExtTextOut (hdc
, x
-1, y
, ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
1698 if (force_manual_underline
||
1699 (und_mode
== UND_LINE
&& (attr
& ATTR_UNDER
))) {
1701 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, fg
));
1702 MoveToEx (hdc
, x
, y
+descent
, NULL
);
1703 LineTo (hdc
, x
+len
*fnt_width
, y
+descent
);
1704 oldpen
= SelectObject (hdc
, oldpen
);
1705 DeleteObject (oldpen
);
1707 if (attr
& ATTR_PASCURS
) {
1710 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
1711 pts
[2].x
= pts
[3].x
= x
+fnt_width
-1;
1712 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
1713 pts
[1].y
= pts
[2].y
= y
+font_height
-1;
1714 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
1715 Polyline (hdc
, pts
, 5);
1716 oldpen
= SelectObject (hdc
, oldpen
);
1717 DeleteObject (oldpen
);
1721 static int check_compose(int first
, int second
) {
1723 static char * composetbl
[] = {
1724 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1725 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1726 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1727 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1728 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1729 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1730 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1731 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1732 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1733 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1734 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1735 "\"uü", "'yý", "htþ", "\"yÿ",
1739 static int recurse
= 0;
1746 sprintf(buf
, "cc(%d,%d)", first
, second
);
1751 for(c
=composetbl
; *c
; c
++) {
1752 if( (*c
)[0] == first
&& (*c
)[1] == second
)
1754 return (*c
)[2] & 0xFF;
1761 nc
= check_compose(second
, first
);
1763 nc
= check_compose(toupper(first
), toupper(second
));
1765 nc
= check_compose(toupper(second
), toupper(first
));
1773 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1774 * codes. Returns number of bytes used or zero to drop the message
1775 * or -1 to forward the message to windows.
1777 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
, unsigned char *output
) {
1779 int scan
, left_alt
= 0, key_down
, shift_state
;
1781 unsigned char * p
= output
;
1783 static WORD keys
[3];
1784 static int compose_state
= 0;
1785 static int compose_char
= 0;
1786 static WPARAM compose_key
= 0;
1788 r
= GetKeyboardState(keystate
);
1789 if (!r
) memset(keystate
, 0, sizeof(keystate
));
1793 { /* Tell us all about key events */
1794 static BYTE oldstate
[256];
1795 static int first
= 1;
1798 if(first
) memcpy(oldstate
, keystate
, sizeof(oldstate
));
1801 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
) {
1803 } else if ((HIWORD(lParam
)&KF_UP
) && scan
==(HIWORD(lParam
) & 0xFF) ) {
1807 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
1808 debug(("K_F%d", wParam
+1-VK_F1
));
1811 case VK_SHIFT
: debug(("SHIFT")); break;
1812 case VK_CONTROL
: debug(("CTRL")); break;
1813 case VK_MENU
: debug(("ALT")); break;
1814 default: debug(("VK_%02x", wParam
));
1816 if(message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
1818 debug((", S%02x", scan
=(HIWORD(lParam
) & 0xFF) ));
1820 ch
= MapVirtualKey(wParam
, 2);
1821 if (ch
>=' ' && ch
<='~') debug((", '%c'", ch
));
1822 else if (ch
) debug((", $%02x", ch
));
1824 if (keys
[0]) debug((", KB0=%02x", keys
[0]));
1825 if (keys
[1]) debug((", KB1=%02x", keys
[1]));
1826 if (keys
[2]) debug((", KB2=%02x", keys
[2]));
1828 if ( (keystate
[VK_SHIFT
]&0x80)!=0) debug((", S"));
1829 if ( (keystate
[VK_CONTROL
]&0x80)!=0) debug((", C"));
1830 if ( (HIWORD(lParam
)&KF_EXTENDED
) ) debug((", E"));
1831 if ( (HIWORD(lParam
)&KF_UP
) ) debug((", U"));
1834 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
)
1836 else if ( (HIWORD(lParam
)&KF_UP
) )
1837 oldstate
[wParam
&0xFF] ^= 0x80;
1839 oldstate
[wParam
&0xFF] ^= 0x81;
1841 for(ch
=0; ch
<256; ch
++)
1842 if (oldstate
[ch
] != keystate
[ch
])
1843 debug((", M%02x=%02x", ch
, keystate
[ch
]));
1845 memcpy(oldstate
, keystate
, sizeof(oldstate
));
1849 /* Note if AltGr was pressed and if it was used as a compose key */
1850 if (wParam
== VK_MENU
&& (HIWORD(lParam
)&KF_EXTENDED
))
1852 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
1853 if (!compose_state
) compose_key
= wParam
;
1855 if (wParam
== VK_APPS
&& !compose_state
)
1856 compose_key
= wParam
;
1858 if (wParam
== compose_key
)
1860 if (compose_state
== 0 && (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
1862 else if (compose_state
== 1 && (HIWORD(lParam
)&KF_UP
))
1867 else if (compose_state
==1 && wParam
!= VK_CONTROL
)
1870 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1871 if ( (cfg
.funky_type
== 3 || (cfg
.funky_type
<= 1 && app_keypad_keys
))
1872 && wParam
==VK_NUMLOCK
&& !(keystate
[VK_SHIFT
]&0x80)) {
1874 wParam
= VK_EXECUTE
;
1876 /* UnToggle NUMLock */
1877 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
1878 keystate
[VK_NUMLOCK
] ^= 1;
1881 /* And write back the 'adjusted' state */
1882 SetKeyboardState (keystate
);
1885 /* Disable Auto repeat if required */
1886 if (repeat_off
&& (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
)
1889 if ((HIWORD(lParam
)&KF_ALTDOWN
) && (keystate
[VK_RMENU
]&0x80) == 0)
1892 key_down
= ((HIWORD(lParam
)&KF_UP
)==0);
1894 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1895 if (left_alt
&& (keystate
[VK_CONTROL
]&0x80))
1896 keystate
[VK_MENU
] = 0;
1898 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
1899 shift_state
= ((keystate
[VK_SHIFT
]&0x80)!=0)
1900 + ((keystate
[VK_CONTROL
]&0x80)!=0)*2;
1903 * Record that we pressed key so the scroll window can be reset, but
1904 * be careful to avoid Shift-UP/Down
1906 if( wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
1910 /* Make sure we're not pasting */
1911 if (key_down
) term_nopaste();
1913 if (compose_state
>1 && left_alt
) compose_state
= 0;
1915 /* Sanitize the number pad if not using a PC NumPad */
1916 if( left_alt
|| (app_keypad_keys
&& cfg
.funky_type
!= 2)
1917 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
)
1919 if ((HIWORD(lParam
)&KF_EXTENDED
) == 0)
1924 case VK_INSERT
: nParam
= VK_NUMPAD0
; break;
1925 case VK_END
: nParam
= VK_NUMPAD1
; break;
1926 case VK_DOWN
: nParam
= VK_NUMPAD2
; break;
1927 case VK_NEXT
: nParam
= VK_NUMPAD3
; break;
1928 case VK_LEFT
: nParam
= VK_NUMPAD4
; break;
1929 case VK_CLEAR
: nParam
= VK_NUMPAD5
; break;
1930 case VK_RIGHT
: nParam
= VK_NUMPAD6
; break;
1931 case VK_HOME
: nParam
= VK_NUMPAD7
; break;
1932 case VK_UP
: nParam
= VK_NUMPAD8
; break;
1933 case VK_PRIOR
: nParam
= VK_NUMPAD9
; break;
1934 case VK_DELETE
: nParam
= VK_DECIMAL
; break;
1938 if (keystate
[VK_NUMLOCK
]&1) shift_state
|= 1;
1944 /* If a key is pressed and AltGr is not active */
1945 if (key_down
&& (keystate
[VK_RMENU
]&0x80) == 0 && !compose_state
)
1947 /* Okay, prepare for most alts then ...*/
1948 if (left_alt
) *p
++ = '\033';
1950 /* Lets see if it's a pattern we know all about ... */
1951 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
1952 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
1955 if (wParam
== VK_NEXT
&& shift_state
== 1) {
1956 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
1959 if (wParam
== VK_INSERT
&& shift_state
== 1) {
1960 term_mouse (MB_PASTE
, MA_CLICK
, 0, 0);
1961 term_mouse (MB_PASTE
, MA_RELEASE
, 0, 0);
1964 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
1967 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
1969 SendMessage (hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
1972 /* Control-Numlock for app-keypad mode switch */
1973 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
1974 app_keypad_keys
^= 1;
1978 /* Nethack keypad */
1979 if (cfg
.nethack_keypad
&& !left_alt
) {
1981 case VK_NUMPAD1
: *p
++ = shift_state ?
'B': 'b'; return p
-output
;
1982 case VK_NUMPAD2
: *p
++ = shift_state ?
'J': 'j'; return p
-output
;
1983 case VK_NUMPAD3
: *p
++ = shift_state ?
'N': 'n'; return p
-output
;
1984 case VK_NUMPAD4
: *p
++ = shift_state ?
'H': 'h'; return p
-output
;
1985 case VK_NUMPAD5
: *p
++ = shift_state ?
'.': '.'; return p
-output
;
1986 case VK_NUMPAD6
: *p
++ = shift_state ?
'L': 'l'; return p
-output
;
1987 case VK_NUMPAD7
: *p
++ = shift_state ?
'Y': 'y'; return p
-output
;
1988 case VK_NUMPAD8
: *p
++ = shift_state ?
'K': 'k'; return p
-output
;
1989 case VK_NUMPAD9
: *p
++ = shift_state ?
'U': 'u'; return p
-output
;
1993 /* Application Keypad */
1997 if ( cfg
.funky_type
== 3 ||
1998 ( cfg
.funky_type
<= 1 && app_keypad_keys
)) switch(wParam
) {
1999 case VK_EXECUTE
: xkey
= 'P'; break;
2000 case VK_DIVIDE
: xkey
= 'Q'; break;
2001 case VK_MULTIPLY
:xkey
= 'R'; break;
2002 case VK_SUBTRACT
:xkey
= 'S'; break;
2004 if(app_keypad_keys
) switch(wParam
) {
2005 case VK_NUMPAD0
: xkey
= 'p'; break;
2006 case VK_NUMPAD1
: xkey
= 'q'; break;
2007 case VK_NUMPAD2
: xkey
= 'r'; break;
2008 case VK_NUMPAD3
: xkey
= 's'; break;
2009 case VK_NUMPAD4
: xkey
= 't'; break;
2010 case VK_NUMPAD5
: xkey
= 'u'; break;
2011 case VK_NUMPAD6
: xkey
= 'v'; break;
2012 case VK_NUMPAD7
: xkey
= 'w'; break;
2013 case VK_NUMPAD8
: xkey
= 'x'; break;
2014 case VK_NUMPAD9
: xkey
= 'y'; break;
2016 case VK_DECIMAL
: xkey
= 'n'; break;
2017 case VK_ADD
: if(cfg
.funky_type
==2) {
2018 if(shift_state
) xkey
= 'l';
2020 } else if(shift_state
) xkey
= 'm';
2024 case VK_DIVIDE
: if(cfg
.funky_type
==2) xkey
= 'o'; break;
2025 case VK_MULTIPLY
:if(cfg
.funky_type
==2) xkey
= 'j'; break;
2026 case VK_SUBTRACT
:if(cfg
.funky_type
==2) xkey
= 'm'; break;
2029 if (HIWORD(lParam
)&KF_EXTENDED
)
2037 if (xkey
>='P' && xkey
<='S')
2038 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
2040 p
+= sprintf((char *)p
, "\x1B?%c", xkey
);
2043 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
2048 if (wParam
== VK_BACK
&& shift_state
== 0 ) /* Backspace */
2050 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
2053 if (wParam
== VK_TAB
&& shift_state
== 1 ) /* Shift tab */
2055 *p
++ = 0x1B; *p
++ = '['; *p
++ = 'Z'; return p
- output
;
2057 if (wParam
== VK_SPACE
&& shift_state
== 2 ) /* Ctrl-Space */
2059 *p
++ = 0; return p
- output
;
2061 if (wParam
== VK_SPACE
&& shift_state
== 3 ) /* Ctrl-Shift-Space */
2063 *p
++ = 160; return p
- output
;
2065 if (wParam
== VK_CANCEL
&& shift_state
== 2 ) /* Ctrl-Break */
2067 *p
++ = 3; return p
- output
;
2069 /* Control-2 to Control-8 are special */
2070 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8')
2072 *p
++ = "\000\033\034\035\036\037\177"[wParam
-'2'];
2075 if (shift_state
== 2 && wParam
== 0xBD) {
2079 if (shift_state
== 2 && wParam
== 0xDF) {
2083 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
2084 *p
++ = '\r'; *p
++ = '\n';
2089 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2090 * for integer decimal nn.)
2092 * We also deal with the weird ones here. Linux VCs replace F1
2093 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2094 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2099 case VK_F1
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11); break;
2100 case VK_F2
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12); break;
2101 case VK_F3
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13); break;
2102 case VK_F4
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14); break;
2103 case VK_F5
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15); break;
2104 case VK_F6
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17); break;
2105 case VK_F7
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18); break;
2106 case VK_F8
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19); break;
2107 case VK_F9
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20); break;
2108 case VK_F10
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21); break;
2109 case VK_F11
: code
= 23; break;
2110 case VK_F12
: code
= 24; break;
2111 case VK_F13
: code
= 25; break;
2112 case VK_F14
: code
= 26; break;
2113 case VK_F15
: code
= 28; break;
2114 case VK_F16
: code
= 29; break;
2115 case VK_F17
: code
= 31; break;
2116 case VK_F18
: code
= 32; break;
2117 case VK_F19
: code
= 33; break;
2118 case VK_F20
: code
= 34; break;
2119 case VK_HOME
: code
= 1; break;
2120 case VK_INSERT
: code
= 2; break;
2121 case VK_DELETE
: code
= 3; break;
2122 case VK_END
: code
= 4; break;
2123 case VK_PRIOR
: code
= 5; break;
2124 case VK_NEXT
: code
= 6; break;
2126 /* Reorder edit keys to physical order */
2127 if (cfg
.funky_type
== 3 && code
<= 6 ) code
= "\0\2\1\4\5\3\6"[code
];
2129 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
2130 p
+= sprintf((char *)p
, "\x1B[[%c", code
+ 'A' - 11);
2133 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
2135 p
+= sprintf((char *)p
, "\x1B%c", code
+ 'P' - 11);
2137 p
+= sprintf((char *)p
, "\x1BO%c", code
+ 'P' - 11);
2140 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
2141 p
+= sprintf((char *)p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
2145 p
+= sprintf((char *)p
, "\x1B[%d~", code
);
2150 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2151 * some reason seems to send VK_CLEAR to Windows...).
2156 case VK_UP
: xkey
= 'A'; break;
2157 case VK_DOWN
: xkey
= 'B'; break;
2158 case VK_RIGHT
: xkey
= 'C'; break;
2159 case VK_LEFT
: xkey
= 'D'; break;
2160 case VK_CLEAR
: xkey
= 'G'; break;
2165 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
2166 else if (app_cursor_keys
)
2167 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
2169 p
+= sprintf((char *)p
, "\x1B[%c", xkey
);
2175 * Finally, deal with Return ourselves. (Win95 seems to
2176 * foul it up when Alt is pressed, for some reason.)
2178 if (wParam
== VK_RETURN
) /* Return */
2185 /* Okay we've done everything interesting; let windows deal with
2186 * the boring stuff */
2188 BOOL capsOn
=keystate
[VK_CAPITAL
] !=0;
2190 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2191 if(cfg
.xlat_capslockcyr
)
2192 keystate
[VK_CAPITAL
] = 0;
2194 r
= ToAscii (wParam
, scan
, keystate
, keys
, 0);
2200 unsigned char ch
= (unsigned char)keys
[i
];
2202 if (compose_state
==2 && (ch
&0x80) == 0 && ch
>' ') {
2207 if (compose_state
==3 && (ch
&0x80) == 0 && ch
>' ') {
2211 if ((nc
=check_compose(compose_char
,ch
)) == -1)
2216 *p
++ = xlat_kbd2tty((unsigned char)nc
);
2222 if( left_alt
&& key_down
) *p
++ = '\033';
2228 ch
= xlat_latkbd2win(ch
);
2229 *p
++ = xlat_kbd2tty(ch
);
2233 /* This is so the ALT-Numpad and dead keys work correctly. */
2240 /* This stops ALT press-release doing a 'COMMAND MENU' function */
2241 if (message
== WM_SYSKEYUP
&& wParam
== VK_MENU
)
2247 void set_title (char *title
) {
2248 sfree (window_name
);
2249 window_name
= smalloc(1+strlen(title
));
2250 strcpy (window_name
, title
);
2251 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
2252 SetWindowText (hwnd
, title
);
2255 void set_icon (char *title
) {
2257 icon_name
= smalloc(1+strlen(title
));
2258 strcpy (icon_name
, title
);
2259 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
2260 SetWindowText (hwnd
, title
);
2263 void set_sbar (int total
, int start
, int page
) {
2266 if (!cfg
.scrollbar
) return;
2268 si
.cbSize
= sizeof(si
);
2269 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
2271 si
.nMax
= total
- 1;
2275 SetScrollInfo (hwnd
, SB_VERT
, &si
, TRUE
);
2278 Context
get_ctx(void) {
2283 SelectPalette (hdc
, pal
, FALSE
);
2289 void free_ctx (Context ctx
) {
2290 SelectPalette (ctx
, GetStockObject (DEFAULT_PALETTE
), FALSE
);
2291 ReleaseDC (hwnd
, ctx
);
2294 static void real_palette_set (int n
, int r
, int g
, int b
) {
2296 logpal
->palPalEntry
[n
].peRed
= r
;
2297 logpal
->palPalEntry
[n
].peGreen
= g
;
2298 logpal
->palPalEntry
[n
].peBlue
= b
;
2299 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
2300 colours
[n
] = PALETTERGB(r
, g
, b
);
2301 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2303 colours
[n
] = RGB(r
, g
, b
);
2306 void palette_set (int n
, int r
, int g
, int b
) {
2307 static const int first
[21] = {
2308 0, 2, 4, 6, 8, 10, 12, 14,
2309 1, 3, 5, 7, 9, 11, 13, 15,
2312 real_palette_set (first
[n
], r
, g
, b
);
2314 real_palette_set (first
[n
]+1, r
, g
, b
);
2316 HDC hdc
= get_ctx();
2317 UnrealizeObject (pal
);
2318 RealizePalette (hdc
);
2323 void palette_reset (void) {
2326 for (i
= 0; i
< NCOLOURS
; i
++) {
2328 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
2329 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
2330 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
2331 logpal
->palPalEntry
[i
].peFlags
= 0;
2332 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
2333 defpal
[i
].rgbtGreen
,
2334 defpal
[i
].rgbtBlue
);
2336 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
2337 defpal
[i
].rgbtGreen
,
2338 defpal
[i
].rgbtBlue
);
2343 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2345 RealizePalette (hdc
);
2350 void write_clip (void *data
, int len
, int must_deselect
) {
2354 clipdata
= GlobalAlloc (GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
2357 lock
= GlobalLock (clipdata
);
2360 memcpy (lock
, data
, len
);
2361 ((unsigned char *) lock
) [len
] = 0;
2362 GlobalUnlock (clipdata
);
2365 SendMessage (hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
2367 if (OpenClipboard (hwnd
)) {
2369 SetClipboardData (CF_TEXT
, clipdata
);
2372 GlobalFree (clipdata
);
2375 SendMessage (hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
2378 void get_clip (void **p
, int *len
) {
2379 static HGLOBAL clipdata
= NULL
;
2383 GlobalUnlock (clipdata
);
2387 if (OpenClipboard (NULL
)) {
2388 clipdata
= GetClipboardData (CF_TEXT
);
2391 *p
= GlobalLock (clipdata
);
2405 * Move `lines' lines from position `from' to position `to' in the
2408 void optimised_move (int to
, int from
, int lines
) {
2412 min
= (to
< from ? to
: from
);
2413 max
= to
+ from
- min
;
2415 r
.left
= 0; r
.right
= cols
* font_width
;
2416 r
.top
= min
* font_height
; r
.bottom
= (max
+lines
) * font_height
;
2417 ScrollWindow (hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
2421 * Print a message box and perform a fatal exit.
2423 void fatalbox(char *fmt
, ...) {
2428 vsprintf(stuff
, fmt
, ap
);
2430 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
2437 void beep(int errorbeep
) {
2438 static long last_beep
= 0;
2439 long now
, beep_diff
;
2441 now
= GetTickCount();
2442 beep_diff
= now
-last_beep
;
2444 /* Make sure we only respond to one beep per packet or so */
2445 if (beep_diff
>=0 && beep_diff
<50)
2449 MessageBeep(MB_ICONHAND
);
2453 last_beep
= GetTickCount();