15 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
21 #define IDM_SHOWLOG 0x0010
22 #define IDM_NEWSESS 0x0020
23 #define IDM_DUPSESS 0x0030
24 #define IDM_RECONF 0x0040
25 #define IDM_CLRSB 0x0050
26 #define IDM_RESET 0x0060
27 #define IDM_TEL_AYT 0x0070
28 #define IDM_TEL_BRK 0x0080
29 #define IDM_TEL_SYNCH 0x0090
30 #define IDM_TEL_EC 0x00a0
31 #define IDM_TEL_EL 0x00b0
32 #define IDM_TEL_GA 0x00c0
33 #define IDM_TEL_NOP 0x00d0
34 #define IDM_TEL_ABORT 0x00e0
35 #define IDM_TEL_AO 0x00f0
36 #define IDM_TEL_IP 0x0100
37 #define IDM_TEL_SUSP 0x0110
38 #define IDM_TEL_EOR 0x0120
39 #define IDM_TEL_EOF 0x0130
40 #define IDM_ABOUT 0x0140
41 #define IDM_SAVEDSESS 0x0150
43 #define IDM_SAVED_MIN 0x1000
44 #define IDM_SAVED_MAX 0x2000
46 #define WM_IGNORE_SIZE (WM_XUSER + 1)
47 #define WM_IGNORE_CLIP (WM_XUSER + 2)
49 static LRESULT CALLBACK
WndProc (HWND
, UINT
, WPARAM
, LPARAM
);
50 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
, unsigned char *output
);
51 static void cfgtopalette(void);
52 static void init_palette(void);
53 static void init_fonts(int);
55 static int extra_width
, extra_height
;
57 static int pending_netevent
= 0;
58 static WPARAM pend_netevent_wParam
= 0;
59 static LPARAM pend_netevent_lParam
= 0;
60 static void enact_pending_netevent(void);
62 static time_t last_movement
= 0;
66 #define FONT_UNDERLINE 2
67 #define FONT_BOLDUND 3
69 #define FONT_OEMBOLD 5
70 #define FONT_OEMBOLDUND 6
72 static HFONT fonts
[8];
73 static int font_needs_hand_underlining
;
75 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
83 static COLORREF colours
[NCOLOURS
];
85 static LPLOGPALETTE logpal
;
86 static RGBTRIPLE defpal
[NCOLOURS
];
90 static HBITMAP caretbm
;
92 static int dbltime
, lasttime
, lastact
;
93 static Mouse_Button lastbtn
;
95 static char *window_name
, *icon_name
;
97 static Ldisc
*real_ldisc
;
99 void begin_session(void) {
103 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
) {
104 static char appname
[] = "PuTTY";
109 int guess_width
, guess_height
;
112 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
114 winsock_ver
= MAKEWORD(1, 1);
115 if (WSAStartup(winsock_ver
, &wsadata
)) {
116 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
117 MB_OK
| MB_ICONEXCLAMATION
);
120 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
121 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
122 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
126 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
128 InitCommonControls();
130 /* Ensure a Maximize setting in Explorer doesn't maximise the
135 * Process the command line.
140 default_protocol
= DEFAULT_PROTOCOL
;
141 default_port
= DEFAULT_PORT
;
143 do_defaults(NULL
, &cfg
);
146 while (*p
&& isspace(*p
)) p
++;
149 * Process command line options first. Yes, this can be
150 * done better, and it will be as soon as I have the
154 char *q
= p
+ strcspn(p
, " \t");
157 tolower(p
[0]) == 's' &&
158 tolower(p
[1]) == 's' &&
159 tolower(p
[2]) == 'h') {
160 default_protocol
= cfg
.protocol
= PROT_SSH
;
161 default_port
= cfg
.port
= 22;
162 } else if (q
== p
+ 3 &&
163 tolower(p
[0]) == 'l' &&
164 tolower(p
[1]) == 'o' &&
165 tolower(p
[2]) == 'g') {
166 logfile
= "putty.log";
167 } else if (q
== p
+ 7 &&
168 tolower(p
[0]) == 'c' &&
169 tolower(p
[1]) == 'l' &&
170 tolower(p
[2]) == 'e' &&
171 tolower(p
[3]) == 'a' &&
172 tolower(p
[4]) == 'n' &&
173 tolower(p
[5]) == 'u' &&
174 tolower(p
[6]) == 'p') {
176 * `putty -cleanup'. Remove all registry entries
177 * associated with PuTTY, and also find and delete
178 * the random seed file.
181 "This procedure will remove ALL Registry\n"
182 "entries associated with PuTTY, and will\n"
183 "also remove the PuTTY random seed file.\n"
185 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
186 "SESSIONS. Are you really sure you want\n"
189 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
194 p
= q
+ strspn(q
, " \t");
198 * An initial @ means to activate a saved session.
202 while (i
> 1 && isspace(p
[i
-1]))
205 do_defaults (p
+1, &cfg
);
206 if (!*cfg
.host
&& !do_config()) {
210 } else if (*p
== '&') {
212 * An initial & means we've been given a command line
213 * containing the hex value of a HANDLE for a file
214 * mapping object, which we must then extract as a
219 if (sscanf(p
+1, "%p", &filemap
) == 1 &&
220 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
221 0, 0, sizeof(Config
))) != NULL
) {
224 CloseHandle(filemap
);
225 } else if (!do_config()) {
232 * If the hostname starts with "telnet:", set the
233 * protocol to Telnet and process the string as a
236 if (!strncmp(q
, "telnet:", 7)) {
240 if (q
[0] == '/' && q
[1] == '/')
242 cfg
.protocol
= PROT_TELNET
;
244 while (*p
&& *p
!= ':' && *p
!= '/') p
++;
252 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
253 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
255 while (*p
&& !isspace(*p
)) p
++;
258 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
259 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
260 while (*p
&& isspace(*p
)) p
++;
273 /* See if host is of the form user@host */
274 if (cfg
.host
[0] != '\0') {
275 char *atsign
= strchr(cfg
.host
, '@');
276 /* Make sure we're not overflowing the user field */
278 if (atsign
-cfg
.host
< sizeof cfg
.username
) {
279 strncpy (cfg
.username
, cfg
.host
, atsign
-cfg
.host
);
280 cfg
.username
[atsign
-cfg
.host
] = '\0';
282 memmove(cfg
.host
, atsign
+1, 1+strlen(atsign
+1));
288 * Select protocol. This is farmed out into a table in a
289 * separate file to enable an ssh-free variant.
294 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
295 if (backends
[i
].protocol
== cfg
.protocol
) {
296 back
= backends
[i
].backend
;
300 MessageBox(NULL
, "Unsupported protocol number found",
301 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
307 real_ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
308 /* To start with, we use the simple line discipline, so we can
309 * type passwords etc without fear of them being echoed... */
310 ldisc
= &ldisc_simple
;
314 wndclass
.lpfnWndProc
= WndProc
;
315 wndclass
.cbClsExtra
= 0;
316 wndclass
.cbWndExtra
= 0;
317 wndclass
.hInstance
= inst
;
318 wndclass
.hIcon
= LoadIcon (inst
,
319 MAKEINTRESOURCE(IDI_MAINICON
));
320 wndclass
.hCursor
= LoadCursor (NULL
, IDC_IBEAM
);
321 wndclass
.hbrBackground
= GetStockObject (BLACK_BRUSH
);
322 wndclass
.lpszMenuName
= NULL
;
323 wndclass
.lpszClassName
= appname
;
325 RegisterClass (&wndclass
);
330 savelines
= cfg
.savelines
;
336 * Guess some defaults for the window size. This all gets
337 * updated later, so we don't really care too much. However, we
338 * do want the font width/height guesses to correspond to a
339 * large font rather than a small one...
346 term_size (cfg
.height
, cfg
.width
, cfg
.savelines
);
347 guess_width
= extra_width
+ font_width
* cols
;
348 guess_height
= extra_height
+ font_height
* rows
;
351 HWND w
= GetDesktopWindow();
352 GetWindowRect (w
, &r
);
353 if (guess_width
> r
.right
- r
.left
)
354 guess_width
= r
.right
- r
.left
;
355 if (guess_height
> r
.bottom
- r
.top
)
356 guess_height
= r
.bottom
- r
.top
;
360 int winmode
= WS_OVERLAPPEDWINDOW
|WS_VSCROLL
;
361 if (!cfg
.scrollbar
) winmode
&= ~(WS_VSCROLL
);
362 if (cfg
.locksize
) winmode
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
363 hwnd
= CreateWindow (appname
, appname
,
365 CW_USEDEFAULT
, CW_USEDEFAULT
,
366 guess_width
, guess_height
,
367 NULL
, NULL
, inst
, NULL
);
371 * Initialise the fonts, simultaneously correcting the guesses
372 * for font_{width,height}.
374 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
379 * Correct the guesses for extra_{width,height}.
383 GetWindowRect (hwnd
, &wr
);
384 GetClientRect (hwnd
, &cr
);
385 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
386 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
390 * Resize the window, now we know what size we _really_ want it
393 guess_width
= extra_width
+ font_width
* cols
;
394 guess_height
= extra_height
+ font_height
* rows
;
395 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
396 SetWindowPos (hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
397 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
400 * Set up a caret bitmap, with no content.
404 int size
= (font_width
+15)/16 * 2 * font_height
;
405 bits
= calloc(size
, 1);
406 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
411 * Initialise the scroll bar.
416 si
.cbSize
= sizeof(si
);
417 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
422 SetScrollInfo (hwnd
, SB_VERT
, &si
, FALSE
);
426 * Start up the telnet connection.
430 char msg
[1024], *title
;
433 error
= back
->init (hwnd
, cfg
.host
, cfg
.port
, &realhost
);
435 sprintf(msg
, "Unable to open connection:\n%s", error
);
436 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
439 window_name
= icon_name
= NULL
;
441 title
= cfg
.wintitle
;
443 sprintf(msg
, "%s - PuTTY", realhost
);
450 session_closed
= FALSE
;
453 * Set up the input and output buffers.
456 outbuf_reap
= outbuf_head
= 0;
459 * Prepare the mouse handler.
461 lastact
= MA_NOTHING
;
462 lastbtn
= MB_NOTHING
;
463 dbltime
= GetDoubleClickTime();
466 * Set up the session-control options on the system menu.
469 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
473 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
474 if (cfg
.protocol
== PROT_TELNET
) {
476 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
477 AppendMenu (p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
478 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
479 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
480 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
481 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
482 AppendMenu (p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
483 AppendMenu (p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
484 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
485 AppendMenu (p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
486 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
487 AppendMenu (p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
488 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
489 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
490 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
491 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
492 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
, "Telnet Command");
493 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
495 AppendMenu (m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
496 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
497 AppendMenu (m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session");
498 AppendMenu (m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
501 for (i
= 1 ; i
< ((nsessions
< 256) ? nsessions
: 256) ; i
++)
502 AppendMenu (s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
) , sessions
[i
]);
503 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
504 AppendMenu (m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings");
505 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
506 AppendMenu (m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
507 AppendMenu (m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
508 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
509 AppendMenu (m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
513 * Finally show the window!
515 ShowWindow (hwnd
, show
);
518 * Set the palette up.
524 has_focus
= (GetForegroundWindow() == hwnd
);
527 if (GetMessage (&msg
, NULL
, 0, 0) == 1)
529 int timer_id
= 0, long_timer
= 0;
531 while (msg
.message
!= WM_QUIT
) {
532 /* Sometimes DispatchMessage calls routines that use their own
533 * GetMessage loop, setup this timer so we get some control back.
535 * Also call term_update() from the timer so that if the host
536 * is sending data flat out we still do redraws.
538 if(timer_id
&& long_timer
) {
539 KillTimer(hwnd
, timer_id
);
540 long_timer
= timer_id
= 0;
543 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
544 DispatchMessage (&msg
);
546 /* Make sure we blink everything that needs it. */
549 /* Send the paste buffer if there's anything to send */
552 /* If there's nothing new in the queue then we can do everything
553 * we've delayed, reading the socket, writing, and repainting
556 if (PeekMessage (&msg
, NULL
, 0, 0, PM_REMOVE
))
559 if (pending_netevent
) {
560 enact_pending_netevent();
562 /* Force the cursor blink on */
565 if (PeekMessage (&msg
, NULL
, 0, 0, PM_REMOVE
))
569 /* Okay there is now nothing to do so we make sure the screen is
570 * completely up to date then tell windows to call us in a little
574 KillTimer(hwnd
, timer_id
);
583 timer_id
= SetTimer(hwnd
, 1, 59500, NULL
);
585 timer_id
= SetTimer(hwnd
, 1, 250, NULL
);
588 /* There's no point rescanning everything in the message queue
589 * so we do an apperently unneccesary wait here
592 if (GetMessage (&msg
, NULL
, 0, 0) != 1)
604 DeleteObject(fonts
[i
]);
611 if (cfg
.protocol
== PROT_SSH
) {
622 * Print a message box and close the connection.
624 void connection_fatal(char *fmt
, ...) {
629 vsprintf(stuff
, fmt
, ap
);
631 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
632 if (cfg
.close_on_exit
)
635 session_closed
= TRUE
;
636 SetWindowText (hwnd
, "PuTTY (inactive)");
641 * Actually do the job requested by a WM_NETEVENT
643 static void enact_pending_netevent(void) {
645 static int reentering
= 0;
648 return; /* don't unpend the pending */
650 pending_netevent
= FALSE
;
653 i
= back
->msg (pend_netevent_wParam
, pend_netevent_lParam
);
658 switch (WSABASEERR
+ (-i
) % 10000) {
660 sprintf(buf
, "Connection reset by peer");
662 case WSAECONNABORTED
:
663 sprintf(buf
, "Connection aborted");
666 sprintf(buf
, "Unexpected network error %d", -i
);
669 connection_fatal(buf
);
672 if (cfg
.close_on_exit
)
675 session_closed
= TRUE
;
676 MessageBox(hwnd
, "Connection closed by remote host",
677 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
678 SetWindowText (hwnd
, "PuTTY (inactive)");
684 * Copy the colour palette from the configuration data into defpal.
685 * This is non-trivial because the colour indices are different.
687 static void cfgtopalette(void) {
689 static const int ww
[] = {
690 6, 7, 8, 9, 10, 11, 12, 13,
691 14, 15, 16, 17, 18, 19, 20, 21,
692 0, 1, 2, 3, 4, 4, 5, 5
695 for (i
=0; i
<24; i
++) {
697 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
698 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
699 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
704 * Set up the colour palette.
706 static void init_palette(void) {
708 HDC hdc
= GetDC (hwnd
);
710 if (cfg
.try_palette
&&
711 GetDeviceCaps (hdc
, RASTERCAPS
) & RC_PALETTE
) {
712 logpal
= smalloc(sizeof(*logpal
)
713 - sizeof(logpal
->palPalEntry
)
714 + NCOLOURS
* sizeof(PALETTEENTRY
));
715 logpal
->palVersion
= 0x300;
716 logpal
->palNumEntries
= NCOLOURS
;
717 for (i
= 0; i
< NCOLOURS
; i
++) {
718 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
719 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
720 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
721 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
723 pal
= CreatePalette (logpal
);
725 SelectPalette (hdc
, pal
, FALSE
);
726 RealizePalette (hdc
);
727 SelectPalette (hdc
, GetStockObject (DEFAULT_PALETTE
),
731 ReleaseDC (hwnd
, hdc
);
734 for (i
=0; i
<NCOLOURS
; i
++)
735 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
739 for(i
=0; i
<NCOLOURS
; i
++)
740 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
746 * Initialise all the fonts we will need. There may be as many as
747 * eight or as few as one. We also:
749 * - check the font width and height, correcting our guesses if
752 * - verify that the bold font is the same width as the ordinary
753 * one, and engage shadow bolding if not.
755 * - verify that the underlined font is the same width as the
756 * ordinary one (manual underlining by means of line drawing can
757 * be done in a pinch).
759 static void init_fonts(int pick_width
) {
764 int fw_dontcare
, fw_bold
;
773 if (cfg
.fontisbold
) {
774 fw_dontcare
= FW_BOLD
;
777 fw_dontcare
= FW_DONTCARE
;
783 font_height
= cfg
.fontheight
;
784 font_width
= pick_width
;
787 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
788 c, OUT_DEFAULT_PRECIS, \
789 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
790 FIXED_PITCH | FF_DONTCARE, cfg.font)
792 if (cfg
.vtmode
!= VT_OEMONLY
) {
793 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
795 SelectObject (hdc
, fonts
[FONT_NORMAL
]);
796 GetTextMetrics(hdc
, &tm
);
797 font_height
= tm
.tmHeight
;
798 font_width
= tm
.tmAveCharWidth
;
800 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
803 * Some fonts, e.g. 9-pt Courier, draw their underlines
804 * outside their character cell. We successfully prevent
805 * screen corruption by clipping the text output, but then
806 * we lose the underline completely. Here we try to work
807 * out whether this is such a font, and if it is, we set a
808 * flag that causes underlines to be drawn by hand.
810 * Having tried other more sophisticated approaches (such
811 * as examining the TEXTMETRIC structure or requesting the
812 * height of a string), I think we'll do this the brute
813 * force way: we create a small bitmap, draw an underlined
814 * space on it, and test to see whether any pixels are
815 * foreground-coloured. (Since we expect the underline to
816 * go all the way across the character cell, we only search
817 * down a single column of the bitmap, half way across.)
821 HBITMAP und_bm
, und_oldbm
;
825 und_dc
= CreateCompatibleDC(hdc
);
826 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
827 und_oldbm
= SelectObject(und_dc
, und_bm
);
828 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
829 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
830 SetTextColor (und_dc
, RGB(255,255,255));
831 SetBkColor (und_dc
, RGB(0,0,0));
832 SetBkMode (und_dc
, OPAQUE
);
833 ExtTextOut (und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
835 for (i
= 0; i
< font_height
; i
++) {
836 c
= GetPixel(und_dc
, font_width
/2, i
);
840 SelectObject(und_dc
, und_oldbm
);
841 DeleteObject(und_bm
);
843 font_needs_hand_underlining
= !gotit
;
846 if (bold_mode
== BOLD_FONT
) {
847 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
848 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
851 if (cfg
.vtmode
== VT_OEMANSI
) {
852 f(FONT_OEM
, OEM_CHARSET
, fw_dontcare
, FALSE
);
853 f(FONT_OEMUND
, OEM_CHARSET
, fw_dontcare
, TRUE
);
855 if (bold_mode
== BOLD_FONT
) {
856 f(FONT_OEMBOLD
, OEM_CHARSET
, fw_bold
, FALSE
);
857 f(FONT_OEMBOLDUND
, OEM_CHARSET
, fw_bold
, TRUE
);
863 f(FONT_OEM
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
865 SelectObject (hdc
, fonts
[FONT_OEM
]);
866 GetTextMetrics(hdc
, &tm
);
867 font_height
= tm
.tmHeight
;
868 font_width
= tm
.tmAveCharWidth
;
870 f(FONT_OEMUND
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
872 if (bold_mode
== BOLD_FONT
) {
873 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
874 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
879 descent
= tm
.tmAscent
+ 1;
880 if (descent
>= font_height
)
881 descent
= font_height
- 1;
882 firstchar
= tm
.tmFirstChar
;
884 for (i
=0; i
<8; i
++) {
886 if (SelectObject (hdc
, fonts
[i
]) &&
887 GetTextMetrics(hdc
, &tm
) )
888 fsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
894 ReleaseDC (hwnd
, hdc
);
896 /* ... This is wrong in OEM only mode */
897 if (fsize
[FONT_UNDERLINE
] != fsize
[FONT_NORMAL
] ||
898 (bold_mode
== BOLD_FONT
&&
899 fsize
[FONT_BOLDUND
] != fsize
[FONT_BOLD
])) {
901 DeleteObject (fonts
[FONT_UNDERLINE
]);
902 if (bold_mode
== BOLD_FONT
)
903 DeleteObject (fonts
[FONT_BOLDUND
]);
906 if (bold_mode
== BOLD_FONT
&&
907 fsize
[FONT_BOLD
] != fsize
[FONT_NORMAL
]) {
908 bold_mode
= BOLD_SHADOW
;
909 DeleteObject (fonts
[FONT_BOLD
]);
910 if (und_mode
== UND_FONT
)
911 DeleteObject (fonts
[FONT_BOLDUND
]);
915 /* With the fascist font painting it doesn't matter if the linedraw font
916 * isn't exactly the right size anymore so we don't have to check this.
918 if (cfg
.vtmode
== VT_OEMANSI
&& fsize
[FONT_OEM
] != fsize
[FONT_NORMAL
] ) {
919 if( cfg
.fontcharset
== OEM_CHARSET
)
921 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
922 "different sizes. Using OEM-only mode instead",
923 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
924 cfg
.vtmode
= VT_OEMONLY
;
926 else if( firstchar
< ' ' )
928 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
929 "different sizes. Using XTerm mode instead",
930 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
931 cfg
.vtmode
= VT_XWINDOWS
;
935 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
936 "different sizes. Using ISO8859-1 mode instead",
937 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
938 cfg
.vtmode
= VT_POORMAN
;
943 DeleteObject (fonts
[i
]);
949 void request_resize (int w
, int h
, int refont
) {
952 /* If the window is maximized supress resizing attempts */
953 if(IsZoomed(hwnd
)) return;
956 /* Don't do this in OEMANSI, you may get disable messages */
957 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132)
958 && cfg
.vtmode
!= VT_OEMANSI
)
960 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132))
963 /* If font width too big for screen should we shrink the font more ? */
965 font_width
= ((font_width
*cols
+w
/2)/w
);
972 DeleteObject(fonts
[i
]);
974 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
976 init_fonts(font_width
);
980 static int first_time
= 1;
986 /* Get the size of the screen */
987 if (GetClientRect(GetDesktopWindow(),&ss
))
988 /* first_time = 0 */;
989 else { first_time
= 2; break; }
991 /* Make sure the values are sane */
992 width
= (ss
.right
-ss
.left
-extra_width
) / font_width
;
993 height
= (ss
.bottom
-ss
.top
-extra_height
) / font_height
;
995 if (w
>width
) w
=width
;
996 if (h
>height
) h
=height
;
1002 width
= extra_width
+ font_width
* w
;
1003 height
= extra_height
+ font_height
* h
;
1005 SetWindowPos (hwnd
, NULL
, 0, 0, width
, height
,
1006 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1007 SWP_NOMOVE
| SWP_NOZORDER
);
1010 static void click (Mouse_Button b
, int x
, int y
) {
1011 int thistime
= GetMessageTime();
1013 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1014 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1015 lastact
== MA_2CLK ? MA_3CLK
:
1016 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1021 if (lastact
!= MA_NOTHING
)
1022 term_mouse (b
, lastact
, x
, y
);
1023 lasttime
= thistime
;
1026 static LRESULT CALLBACK
WndProc (HWND hwnd
, UINT message
,
1027 WPARAM wParam
, LPARAM lParam
) {
1029 static int ignore_size
= FALSE
;
1030 static int ignore_clip
= FALSE
;
1031 static int just_reconfigged
= FALSE
;
1032 static int resizing
= FALSE
;
1036 if (pending_netevent
)
1037 enact_pending_netevent();
1043 if (cfg
.ping_interval
> 0)
1047 if (now
-last_movement
> cfg
.ping_interval
* 60 - 10)
1049 back
->special(TS_PING
);
1050 last_movement
= now
;
1057 if (!cfg
.warn_on_close
|| session_closed
||
1058 MessageBox(hwnd
, "Are you sure you want to close this session?",
1059 "PuTTY Exit Confirmation",
1060 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1061 DestroyWindow(hwnd
);
1064 PostQuitMessage (0);
1067 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1079 PROCESS_INFORMATION pi
;
1080 HANDLE filemap
= NULL
;
1082 if (wParam
== IDM_DUPSESS
) {
1084 * Allocate a file-mapping memory chunk for the
1087 SECURITY_ATTRIBUTES sa
;
1090 sa
.nLength
= sizeof(sa
);
1091 sa
.lpSecurityDescriptor
= NULL
;
1092 sa
.bInheritHandle
= TRUE
;
1093 filemap
= CreateFileMapping((HANDLE
)0xFFFFFFFF,
1100 p
= (Config
*)MapViewOfFile(filemap
,
1102 0, 0, sizeof(Config
));
1104 *p
= cfg
; /* structure copy */
1108 sprintf(c
, "putty &%p", filemap
);
1110 } else if (wParam
== IDM_SAVEDSESS
) {
1111 char *session
= sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1112 cl
= malloc(16 + strlen(session
)); /* 8, but play safe */
1114 cl
= NULL
; /* not a very important failure mode */
1116 sprintf(cl
, "putty @%s", session
);
1122 GetModuleFileName (NULL
, b
, sizeof(b
)-1);
1124 si
.lpReserved
= NULL
;
1125 si
.lpDesktop
= NULL
;
1129 si
.lpReserved2
= NULL
;
1130 CreateProcess (b
, cl
, NULL
, NULL
, TRUE
,
1131 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1134 CloseHandle(filemap
);
1140 if (!do_reconfig(hwnd
))
1142 just_reconfigged
= TRUE
;
1147 DeleteObject(fonts
[i
]);
1149 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1150 und_mode
= UND_FONT
;
1153 /* Telnet will change local echo -> remote if the remote asks */
1154 if (cfg
.protocol
!= PROT_TELNET
)
1155 ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
1163 /* Enable or disable the scroll bar, etc */
1165 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1168 if (cfg
.scrollbar
) nflg
|= WS_VSCROLL
;
1169 else nflg
&= ~WS_VSCROLL
;
1171 nflg
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1173 nflg
|= (WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1179 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1180 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1181 SetWindowPos(hwnd
, NULL
, 0,0,0,0,
1182 SWP_NOACTIVATE
|SWP_NOCOPYBITS
|
1183 SWP_NOMOVE
|SWP_NOSIZE
| SWP_NOZORDER
|
1186 GetWindowRect (hwnd
, &wr
);
1187 GetClientRect (hwnd
, &cr
);
1188 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1189 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1193 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1194 InvalidateRect(hwnd
, NULL
, TRUE
);
1195 SetWindowPos (hwnd
, NULL
, 0, 0,
1196 extra_width
+ font_width
* cfg
.width
,
1197 extra_height
+ font_height
* cfg
.height
,
1198 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1199 SWP_NOMOVE
| SWP_NOZORDER
);
1200 if (IsIconic(hwnd
)) {
1201 SetWindowText (hwnd
,
1202 cfg
.win_name_always ? window_name
: icon_name
);
1211 case IDM_TEL_AYT
: back
->special (TS_AYT
); break;
1212 case IDM_TEL_BRK
: back
->special (TS_BRK
); break;
1213 case IDM_TEL_SYNCH
: back
->special (TS_SYNCH
); break;
1214 case IDM_TEL_EC
: back
->special (TS_EC
); break;
1215 case IDM_TEL_EL
: back
->special (TS_EL
); break;
1216 case IDM_TEL_GA
: back
->special (TS_GA
); break;
1217 case IDM_TEL_NOP
: back
->special (TS_NOP
); break;
1218 case IDM_TEL_ABORT
: back
->special (TS_ABORT
); break;
1219 case IDM_TEL_AO
: back
->special (TS_AO
); break;
1220 case IDM_TEL_IP
: back
->special (TS_IP
); break;
1221 case IDM_TEL_SUSP
: back
->special (TS_SUSP
); break;
1222 case IDM_TEL_EOR
: back
->special (TS_EOR
); break;
1223 case IDM_TEL_EOF
: back
->special (TS_EOF
); break;
1228 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1229 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1234 #define X_POS(l) ((int)(short)LOWORD(l))
1235 #define Y_POS(l) ((int)(short)HIWORD(l))
1237 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1238 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1240 case WM_LBUTTONDOWN
:
1241 click (MB_SELECT
, TO_CHR_X(X_POS(lParam
)),
1242 TO_CHR_Y(Y_POS(lParam
)));
1246 term_mouse (MB_SELECT
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1247 TO_CHR_Y(Y_POS(lParam
)));
1250 case WM_MBUTTONDOWN
:
1252 click (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1253 TO_CHR_X(X_POS(lParam
)),
1254 TO_CHR_Y(Y_POS(lParam
)));
1257 term_mouse (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1258 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1259 TO_CHR_Y(Y_POS(lParam
)));
1262 case WM_RBUTTONDOWN
:
1264 click (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1265 TO_CHR_X(X_POS(lParam
)),
1266 TO_CHR_Y(Y_POS(lParam
)));
1269 term_mouse (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1270 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1271 TO_CHR_Y(Y_POS(lParam
)));
1276 * Add the mouse position and message time to the random
1277 * number noise, if we're using ssh.
1279 if (cfg
.protocol
== PROT_SSH
)
1280 noise_ultralight(lParam
);
1282 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1284 if (wParam
& MK_LBUTTON
)
1286 else if (wParam
& MK_MBUTTON
)
1287 b
= cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
;
1289 b
= cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
;
1290 term_mouse (b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1291 TO_CHR_Y(Y_POS(lParam
)));
1294 case WM_IGNORE_CLIP
:
1295 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1297 case WM_DESTROYCLIPBOARD
:
1300 ignore_clip
= FALSE
;
1306 hdc
= BeginPaint (hwnd
, &p
);
1308 SelectPalette (hdc
, pal
, TRUE
);
1309 RealizePalette (hdc
);
1311 term_paint (hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1312 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1313 SelectObject (hdc
, GetStockObject(SYSTEM_FONT
));
1314 SelectObject (hdc
, GetStockObject(WHITE_PEN
));
1315 EndPaint (hwnd
, &p
);
1320 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1321 * but the only one that's likely to try to overload us is FD_READ.
1322 * This means buffering just one is fine.
1324 if (pending_netevent
)
1325 enact_pending_netevent();
1327 pending_netevent
= TRUE
;
1328 pend_netevent_wParam
=wParam
;
1329 pend_netevent_lParam
=lParam
;
1330 time(&last_movement
);
1334 CreateCaret(hwnd
, caretbm
, 0, 0);
1345 case WM_IGNORE_SIZE
:
1346 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1348 case WM_ENTERSIZEMOVE
:
1352 case WM_EXITSIZEMOVE
:
1359 int width
, height
, w
, h
, ew
, eh
;
1360 LPRECT r
= (LPRECT
)lParam
;
1362 width
= r
->right
- r
->left
- extra_width
;
1363 height
= r
->bottom
- r
->top
- extra_height
;
1364 w
= (width
+ font_width
/2) / font_width
; if (w
< 1) w
= 1;
1365 h
= (height
+ font_height
/2) / font_height
; if (h
< 1) h
= 1;
1366 UpdateSizeTip(hwnd
, w
, h
);
1367 ew
= width
- w
* font_width
;
1368 eh
= height
- h
* font_height
;
1370 if (wParam
== WMSZ_LEFT
||
1371 wParam
== WMSZ_BOTTOMLEFT
||
1372 wParam
== WMSZ_TOPLEFT
)
1378 if (wParam
== WMSZ_TOP
||
1379 wParam
== WMSZ_TOPRIGHT
||
1380 wParam
== WMSZ_TOPLEFT
)
1390 /* break; (never reached) */
1392 if (wParam
== SIZE_MINIMIZED
) {
1393 SetWindowText (hwnd
,
1394 cfg
.win_name_always ? window_name
: icon_name
);
1397 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1398 SetWindowText (hwnd
, window_name
);
1400 int width
, height
, w
, h
;
1401 #if 0 /* we have fixed this using WM_SIZING now */
1405 width
= LOWORD(lParam
);
1406 height
= HIWORD(lParam
);
1407 w
= width
/ font_width
; if (w
< 1) w
= 1;
1408 h
= height
/ font_height
; if (h
< 1) h
= 1;
1409 #if 0 /* we have fixed this using WM_SIZING now */
1410 ew
= width
- w
* font_width
;
1411 eh
= height
- h
* font_height
;
1412 if (ew
!= 0 || eh
!= 0) {
1414 GetWindowRect (hwnd
, &r
);
1415 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1416 SetWindowPos (hwnd
, NULL
, 0, 0,
1417 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1418 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1421 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1423 term_size (h
, w
, cfg
.savelines
);
1425 * Don't call back->size in mid-resize. (To prevent
1426 * massive numbers of resize events getting sent
1427 * down the connection during an NT opaque drag.)
1431 just_reconfigged
= FALSE
;
1434 ignore_size
= FALSE
;
1437 switch (LOWORD(wParam
)) {
1438 case SB_BOTTOM
: term_scroll(-1, 0); break;
1439 case SB_TOP
: term_scroll(+1, 0); break;
1440 case SB_LINEDOWN
: term_scroll (0, +1); break;
1441 case SB_LINEUP
: term_scroll (0, -1); break;
1442 case SB_PAGEDOWN
: term_scroll (0, +rows
/2); break;
1443 case SB_PAGEUP
: term_scroll (0, -rows
/2); break;
1444 case SB_THUMBPOSITION
: case SB_THUMBTRACK
:
1445 term_scroll (1, HIWORD(wParam
)); break;
1448 case WM_PALETTECHANGED
:
1449 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1450 HDC hdc
= get_ctx();
1452 if (RealizePalette (hdc
) > 0)
1458 case WM_QUERYNEWPALETTE
:
1460 HDC hdc
= get_ctx();
1462 if (RealizePalette (hdc
) > 0)
1474 * Add the scan code and keypress timing to the random
1475 * number noise, if we're using ssh.
1477 if (cfg
.protocol
== PROT_SSH
)
1478 noise_ultralight(lParam
);
1481 * We don't do TranslateMessage since it disassociates the
1482 * resulting CHAR message from the KEYDOWN that sparked it,
1483 * which we occasionally don't want. Instead, we process
1484 * KEYDOWN, and call the Win32 translator functions so that
1485 * we get the translations under _our_ control.
1488 unsigned char buf
[20];
1491 len
= TranslateKey (message
, wParam
, lParam
, buf
);
1493 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1494 ldisc
->send (buf
, len
);
1500 * Nevertheless, we are prepared to deal with WM_CHAR
1501 * messages, should they crop up. So if someone wants to
1502 * post the things to us as part of a macro manoeuvre,
1503 * we're ready to cope.
1506 char c
= xlat_kbd2tty((unsigned char)wParam
);
1507 ldisc
->send (&c
, 1);
1512 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1516 * Move the system caret. (We maintain one, even though it's
1517 * invisible, for the benefit of blind people: apparently some
1518 * helper software tracks the system caret, so we should arrange to
1521 void sys_cursor(int x
, int y
) {
1522 SetCaretPos(x
* font_width
, y
* font_height
);
1526 * Draw a line of text in the window, at given character
1527 * coordinates, in given attributes.
1529 * We are allowed to fiddle with the contents of `text'.
1531 void do_text (Context ctx
, int x
, int y
, char *text
, int len
,
1532 unsigned long attr
, int lattr
) {
1534 int nfg
, nbg
, nfont
;
1537 int force_manual_underline
= 0;
1538 int fnt_width
= font_width
*(1+(lattr
!=LATTR_NORM
));
1539 static int *IpDx
= 0, IpDxLEN
= 0;;
1541 if (len
>IpDxLEN
|| IpDx
[0] != fnt_width
) {
1545 IpDx
= smalloc((len
+16)*sizeof(int));
1548 for(i
=0; i
<IpDxLEN
; i
++)
1549 IpDx
[i
] = fnt_width
;
1555 if (attr
& ATTR_ACTCURS
) {
1556 attr
&= (bold_mode
== BOLD_COLOURS ?
0x300200 : 0x300300);
1557 attr
^= ATTR_CUR_XOR
;
1561 if (cfg
.vtmode
== VT_OEMONLY
)
1565 * Map high-half characters in order to approximate ISO using
1566 * OEM character set. No characters are missing if the OEM codepage
1569 if (nfont
& FONT_OEM
) {
1571 for (i
=0; i
<len
; i
++)
1572 if (text
[i
] >= '\xA0' && text
[i
] <= '\xFF') {
1574 /* This is CP850 ... perfect translation */
1575 static const char oemhighhalf
[] =
1576 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1577 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1578 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1579 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1580 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1581 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1582 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1583 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1584 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1585 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1586 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1587 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1590 /* This is CP437 ... junk translation */
1591 static const unsigned char oemhighhalf
[] = {
1592 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1593 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1594 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1595 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1596 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1597 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1598 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1599 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1600 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1601 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1602 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1603 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1606 text
[i
] = oemhighhalf
[(unsigned char)text
[i
] - 0xA0];
1610 if (attr
& ATTR_LINEDRW
) {
1613 static const char poorman
[] =
1614 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1617 static const char oemmap_437
[] =
1618 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1619 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1622 static const char oemmap_850
[] =
1623 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1624 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1626 /* Poor windows font ... eg: windows courier */
1627 static const char oemmap
[] =
1628 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1629 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1632 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1633 * VT100 line drawing chars; everything else stays normal.
1635 * Actually '_' maps to space too, but that's done before.
1637 switch (cfg
.vtmode
) {
1639 for (i
=0; i
<len
; i
++)
1640 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1641 text
[i
] += '\x01' - '\x60';
1644 /* Make sure we actually have an OEM font */
1645 if (fonts
[nfont
|FONT_OEM
]) {
1648 for (i
=0; i
<len
; i
++)
1649 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1650 text
[i
] = oemmap
[(unsigned char)text
[i
] - 0x60];
1654 for (i
=0; i
<len
; i
++)
1655 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1656 text
[i
] = poorman
[(unsigned char)text
[i
] - 0x60];
1661 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
1662 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
1663 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
1665 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
1666 nfont
|= FONT_UNDERLINE
;
1669 if (nfont
&FONT_UNDERLINE
)
1670 force_manual_underline
= 1;
1671 /* Don't do the same for manual bold, it could be bad news. */
1673 nfont
&= ~(FONT_BOLD
|FONT_UNDERLINE
);
1675 if (font_needs_hand_underlining
&& (attr
& ATTR_UNDER
))
1676 force_manual_underline
= 1;
1677 if (attr
& ATTR_REVERSE
) {
1678 t
= nfg
; nfg
= nbg
; nbg
= t
;
1680 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
1682 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
1686 SelectObject (hdc
, fonts
[nfont
]);
1687 SetTextColor (hdc
, fg
);
1688 SetBkColor (hdc
, bg
);
1689 SetBkMode (hdc
, OPAQUE
);
1692 line_box
.right
= x
+fnt_width
*len
;
1693 line_box
.bottom
= y
+font_height
;
1694 ExtTextOut (hdc
, x
, y
, ETO_CLIPPED
|ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
1695 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
1696 SetBkMode (hdc
, TRANSPARENT
);
1698 /* GRR: This draws the character outside it's box and can leave
1699 * 'droppings' even with the clip box! I suppose I could loop it
1700 * one character at a time ... yuk.
1702 * Or ... I could do a test print with "W", and use +1 or -1 for this
1703 * shift depending on if the leftmost column is blank...
1705 ExtTextOut (hdc
, x
-1, y
, ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
1707 if (force_manual_underline
||
1708 (und_mode
== UND_LINE
&& (attr
& ATTR_UNDER
))) {
1710 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, fg
));
1711 MoveToEx (hdc
, x
, y
+descent
, NULL
);
1712 LineTo (hdc
, x
+len
*fnt_width
, y
+descent
);
1713 oldpen
= SelectObject (hdc
, oldpen
);
1714 DeleteObject (oldpen
);
1716 if (attr
& ATTR_PASCURS
) {
1719 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
1720 pts
[2].x
= pts
[3].x
= x
+fnt_width
-1;
1721 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
1722 pts
[1].y
= pts
[2].y
= y
+font_height
-1;
1723 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
1724 Polyline (hdc
, pts
, 5);
1725 oldpen
= SelectObject (hdc
, oldpen
);
1726 DeleteObject (oldpen
);
1730 static int check_compose(int first
, int second
) {
1732 static char * composetbl
[] = {
1733 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1734 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1735 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1736 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1737 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1738 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1739 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1740 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1741 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1742 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1743 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1744 "\"uü", "'yý", "htþ", "\"yÿ",
1748 static int recurse
= 0;
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)
2213 MessageBeep(MB_ICONHAND
);
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();