8 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
12 #define IDM_SHOWLOG 0x0010
13 #define IDM_NEWSESS 0x0020
14 #define IDM_DUPSESS 0x0030
15 #define IDM_RECONF 0x0040
16 #define IDM_CLRSB 0x0050
17 #define IDM_RESET 0x0060
18 #define IDM_TEL_AYT 0x0070
19 #define IDM_TEL_BRK 0x0080
20 #define IDM_TEL_SYNCH 0x0090
21 #define IDM_TEL_EC 0x00a0
22 #define IDM_TEL_EL 0x00b0
23 #define IDM_TEL_GA 0x00c0
24 #define IDM_TEL_NOP 0x00d0
25 #define IDM_TEL_ABORT 0x00e0
26 #define IDM_TEL_AO 0x00f0
27 #define IDM_TEL_IP 0x0100
28 #define IDM_TEL_SUSP 0x0110
29 #define IDM_TEL_EOR 0x0120
30 #define IDM_TEL_EOF 0x0130
31 #define IDM_ABOUT 0x0140
32 #define IDM_SAVEDSESS 0x0150
34 #define IDM_SAVED_MIN 0x1000
35 #define IDM_SAVED_MAX 0x2000
37 #define WM_IGNORE_SIZE (WM_XUSER + 1)
38 #define WM_IGNORE_CLIP (WM_XUSER + 2)
40 static LRESULT CALLBACK
WndProc (HWND
, UINT
, WPARAM
, LPARAM
);
41 static int TranslateKey(WPARAM wParam
, LPARAM lParam
, unsigned char *output
);
42 static void cfgtopalette(void);
43 static void init_palette(void);
44 static void init_fonts(int);
46 static int extra_width
, extra_height
;
48 static int pending_netevent
= 0;
49 static WPARAM pend_netevent_wParam
= 0;
50 static LPARAM pend_netevent_lParam
= 0;
51 static void enact_pending_netevent(void);
55 #define FONT_UNDERLINE 2
56 #define FONT_BOLDUND 3
58 #define FONT_OEMBOLD 5
59 #define FONT_OEMBOLDUND 6
61 static HFONT fonts
[8];
63 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
71 static COLORREF colours
[NCOLOURS
];
73 static LPLOGPALETTE logpal
;
74 static RGBTRIPLE defpal
[NCOLOURS
];
78 static int dbltime
, lasttime
, lastact
;
79 static Mouse_Button lastbtn
;
81 static char *window_name
, *icon_name
;
83 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
) {
84 static char appname
[] = "PuTTY";
89 int guess_width
, guess_height
;
93 winsock_ver
= MAKEWORD(1, 1);
94 if (WSAStartup(winsock_ver
, &wsadata
)) {
95 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
96 MB_OK
| MB_ICONEXCLAMATION
);
99 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
100 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
101 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
105 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
107 InitCommonControls();
110 * Process the command line.
115 default_protocol
= DEFAULT_PROTOCOL
;
116 default_port
= DEFAULT_PORT
;
121 while (*p
&& isspace(*p
)) p
++;
124 * Process command line options first. Yes, this can be
125 * done better, and it will be as soon as I have the
129 char *q
= p
+ strcspn(p
, " \t");
132 tolower(p
[0]) == 's' &&
133 tolower(p
[1]) == 's' &&
134 tolower(p
[2]) == 'h') {
135 default_protocol
= cfg
.protocol
= PROT_SSH
;
136 default_port
= cfg
.port
= 22;
137 } else if (q
== p
+ 3 &&
138 tolower(p
[0]) == 'l' &&
139 tolower(p
[1]) == 'o' &&
140 tolower(p
[2]) == 'g') {
141 logfile
= "putty.log";
143 p
= q
+ strspn(q
, " \t");
147 * An initial @ means to activate a saved session.
151 if (!*cfg
.host
&& !do_config()) {
155 } else if (*p
== '&') {
157 * An initial & means we've been given a command line
158 * containing the hex value of a HANDLE for a file
159 * mapping object, which we must then extract as a
164 if (sscanf(p
+1, "%p", &filemap
) == 1 &&
165 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
166 0, 0, sizeof(Config
))) != NULL
) {
169 CloseHandle(filemap
);
170 } else if (!do_config()) {
177 * If the hostname starts with "telnet://", set the
178 * protocol to Telnet and process the string as a
181 if (!strncmp(q
, "telnet://", 9)) {
185 cfg
.protocol
= PROT_TELNET
;
187 while (*p
&& *p
!= ':' && *p
!= '/') p
++;
195 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
196 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
198 while (*p
&& !isspace(*p
)) p
++;
201 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
202 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
203 while (*p
&& isspace(*p
)) p
++;
218 * Select protocol. This is farmed out into a table in a
219 * separate file to enable an ssh-free variant.
224 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
225 if (backends
[i
].protocol
== cfg
.protocol
) {
226 back
= backends
[i
].backend
;
230 MessageBox(NULL
, "Unsupported protocol number found",
231 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
237 ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
241 wndclass
.lpfnWndProc
= WndProc
;
242 wndclass
.cbClsExtra
= 0;
243 wndclass
.cbWndExtra
= 0;
244 wndclass
.hInstance
= inst
;
245 wndclass
.hIcon
= LoadIcon (inst
,
246 MAKEINTRESOURCE(IDI_MAINICON
));
247 wndclass
.hCursor
= LoadCursor (NULL
, IDC_IBEAM
);
248 wndclass
.hbrBackground
= GetStockObject (BLACK_BRUSH
);
249 wndclass
.lpszMenuName
= NULL
;
250 wndclass
.lpszClassName
= appname
;
252 RegisterClass (&wndclass
);
257 savelines
= cfg
.savelines
;
263 * Guess some defaults for the window size. This all gets
264 * updated later, so we don't really care too much. However, we
265 * do want the font width/height guesses to correspond to a
266 * large font rather than a small one...
273 term_size (cfg
.height
, cfg
.width
, cfg
.savelines
);
274 guess_width
= extra_width
+ font_width
* cols
;
275 guess_height
= extra_height
+ font_height
* rows
;
278 HWND w
= GetDesktopWindow();
279 GetWindowRect (w
, &r
);
280 if (guess_width
> r
.right
- r
.left
)
281 guess_width
= r
.right
- r
.left
;
282 if (guess_height
> r
.bottom
- r
.top
)
283 guess_height
= r
.bottom
- r
.top
;
286 hwnd
= CreateWindow (appname
, appname
,
287 WS_OVERLAPPEDWINDOW
| WS_VSCROLL
,
288 CW_USEDEFAULT
, CW_USEDEFAULT
,
289 guess_width
, guess_height
,
290 NULL
, NULL
, inst
, NULL
);
293 * Initialise the fonts, simultaneously correcting the guesses
294 * for font_{width,height}.
296 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
301 * Correct the guesses for extra_{width,height}.
305 GetWindowRect (hwnd
, &wr
);
306 GetClientRect (hwnd
, &cr
);
307 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
308 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
312 * Resize the window, now we know what size we _really_ want it
315 guess_width
= extra_width
+ font_width
* cols
;
316 guess_height
= extra_height
+ font_height
* rows
;
317 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
318 SetWindowPos (hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
319 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
322 * Initialise the scroll bar.
327 si
.cbSize
= sizeof(si
);
328 si
.fMask
= SIF_RANGE
| SIF_PAGE
| SIF_POS
| SIF_DISABLENOSCROLL
;
333 SetScrollInfo (hwnd
, SB_VERT
, &si
, FALSE
);
337 * Start up the telnet connection.
344 error
= back
->init (hwnd
, cfg
.host
, cfg
.port
, &realhost
);
346 sprintf(msg
, "Unable to open connection:\n%s", error
);
347 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
350 window_name
= icon_name
= NULL
;
351 sprintf(msg
, "%s - PuTTY", realhost
);
356 session_closed
= FALSE
;
359 * Set up the input and output buffers.
361 inbuf_reap
= inbuf_head
= 0;
362 outbuf_reap
= outbuf_head
= 0;
365 * Choose unscroll method
367 unscroll_event
= US_DISP
;
370 * Prepare the mouse handler.
372 lastact
= MA_NOTHING
;
373 lastbtn
= MB_NOTHING
;
374 dbltime
= GetDoubleClickTime();
377 * Set up the session-control options on the system menu.
380 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
384 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
385 if (cfg
.protocol
== PROT_TELNET
) {
387 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
388 AppendMenu (p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
389 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
390 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
391 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
392 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
393 AppendMenu (p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
394 AppendMenu (p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
395 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
396 AppendMenu (p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
397 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
398 AppendMenu (p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
399 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
400 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
401 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
402 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
403 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
, "Telnet Command");
404 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
406 AppendMenu (m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
407 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
408 AppendMenu (m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session");
409 AppendMenu (m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
412 for (i
= 1 ; i
< ((nsessions
< 256) ? nsessions
: 256) ; i
++)
413 AppendMenu (s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
) , sessions
[i
]);
414 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
415 AppendMenu (m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings");
416 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
417 AppendMenu (m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
418 AppendMenu (m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
419 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
420 AppendMenu (m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
424 * Finally show the window!
426 ShowWindow (hwnd
, show
);
429 * Set the palette up.
435 has_focus
= (GetForegroundWindow() == hwnd
);
439 int timer_id
= 0, long_timer
= 0;
441 while (GetMessage (&msg
, NULL
, 0, 0) == 1) {
442 /* Sometimes DispatchMessage calls routines that use their own
443 * GetMessage loop, setup this timer so we get some control back.
445 * Also call term_update() from the timer so that if the host
446 * is sending data flat out we still do redraws.
448 if(timer_id
&& long_timer
) {
449 KillTimer(hwnd
, timer_id
);
450 long_timer
= timer_id
= 0;
453 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
454 DispatchMessage (&msg
);
456 /* This is too fast, but I'll leave it for now 'cause it shows
457 * how often term_update is called (far too often at times!)
461 /* If there's nothing new in the queue then we can do everything
462 * we've delayed, reading the socket, writing, and repainting
465 if (!PeekMessage (&msg
, NULL
, 0, 0, PM_NOREMOVE
)) {
466 if (pending_netevent
) {
467 enact_pending_netevent();
472 if (!PeekMessage (&msg
, NULL
, 0, 0, PM_NOREMOVE
)) {
474 KillTimer(hwnd
, timer_id
);
477 if (inbuf_reap
!= inbuf_head
)
480 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
493 DeleteObject(fonts
[i
]);
500 if (cfg
.protocol
== PROT_SSH
) {
511 * Actually do the job requested by a WM_NETEVENT
513 static void enact_pending_netevent(void) {
515 pending_netevent
= FALSE
;
516 i
= back
->msg (pend_netevent_wParam
, pend_netevent_lParam
);
520 switch (WSABASEERR
+ (-i
) % 10000) {
522 sprintf(buf
, "Connection reset by peer");
525 sprintf(buf
, "Unexpected network error %d", -i
);
528 MessageBox(hwnd
, buf
, "PuTTY Fatal Error",
529 MB_ICONERROR
| MB_OK
);
532 if (cfg
.close_on_exit
)
535 session_closed
= TRUE
;
536 MessageBox(hwnd
, "Connection closed by remote host",
537 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
538 SetWindowText (hwnd
, "PuTTY (inactive)");
544 * Copy the colour palette from the configuration data into defpal.
545 * This is non-trivial because the colour indices are different.
547 static void cfgtopalette(void) {
549 static const int ww
[] = {
550 6, 7, 8, 9, 10, 11, 12, 13,
551 14, 15, 16, 17, 18, 19, 20, 21,
552 0, 1, 2, 3, 4, 4, 5, 5
555 for (i
=0; i
<24; i
++) {
557 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
558 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
559 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
564 * Set up the colour palette.
566 static void init_palette(void) {
568 HDC hdc
= GetDC (hwnd
);
570 if (cfg
.try_palette
&&
571 GetDeviceCaps (hdc
, RASTERCAPS
) & RC_PALETTE
) {
572 logpal
= smalloc(sizeof(*logpal
)
573 - sizeof(logpal
->palPalEntry
)
574 + NCOLOURS
* sizeof(PALETTEENTRY
));
575 logpal
->palVersion
= 0x300;
576 logpal
->palNumEntries
= NCOLOURS
;
577 for (i
= 0; i
< NCOLOURS
; i
++) {
578 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
579 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
580 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
581 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
583 pal
= CreatePalette (logpal
);
585 SelectPalette (hdc
, pal
, FALSE
);
586 RealizePalette (hdc
);
587 SelectPalette (hdc
, GetStockObject (DEFAULT_PALETTE
),
591 ReleaseDC (hwnd
, hdc
);
594 for (i
=0; i
<NCOLOURS
; i
++)
595 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
599 for(i
=0; i
<NCOLOURS
; i
++)
600 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
606 * Initialise all the fonts we will need. There may be as many as
607 * eight or as few as one. We also:
609 * - check the font width and height, correcting our guesses if
612 * - verify that the bold font is the same width as the ordinary
613 * one, and engage shadow bolding if not.
615 * - verify that the underlined font is the same width as the
616 * ordinary one (manual underlining by means of line drawing can
617 * be done in a pinch).
619 static void init_fonts(int pick_width
) {
624 int fw_dontcare
, fw_bold
;
633 if (cfg
.fontisbold
) {
634 fw_dontcare
= FW_BOLD
;
637 fw_dontcare
= FW_DONTCARE
;
643 font_height
= cfg
.fontheight
;
644 font_width
= pick_width
;
647 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
648 c, OUT_DEFAULT_PRECIS, \
649 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
650 FIXED_PITCH | FF_DONTCARE, cfg.font)
652 if (cfg
.vtmode
!= VT_OEMONLY
) {
653 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
655 SelectObject (hdc
, fonts
[FONT_NORMAL
]);
656 GetTextMetrics(hdc
, &tm
);
657 font_height
= tm
.tmHeight
;
658 font_width
= tm
.tmAveCharWidth
;
660 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
662 if (bold_mode
== BOLD_FONT
) {
663 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
664 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
667 if (cfg
.vtmode
== VT_OEMANSI
) {
668 f(FONT_OEM
, OEM_CHARSET
, fw_dontcare
, FALSE
);
669 f(FONT_OEMUND
, OEM_CHARSET
, fw_dontcare
, TRUE
);
671 if (bold_mode
== BOLD_FONT
) {
672 f(FONT_OEMBOLD
, OEM_CHARSET
, fw_bold
, FALSE
);
673 f(FONT_OEMBOLDUND
, OEM_CHARSET
, fw_bold
, TRUE
);
679 f(FONT_OEM
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
681 SelectObject (hdc
, fonts
[FONT_OEM
]);
682 GetTextMetrics(hdc
, &tm
);
683 font_height
= tm
.tmHeight
;
684 font_width
= tm
.tmAveCharWidth
;
686 f(FONT_OEMUND
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
688 if (bold_mode
== BOLD_FONT
) {
689 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
690 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
695 descent
= tm
.tmAscent
+ 1;
696 if (descent
>= font_height
)
697 descent
= font_height
- 1;
698 firstchar
= tm
.tmFirstChar
;
700 for (i
=0; i
<8; i
++) {
702 if (SelectObject (hdc
, fonts
[i
]) &&
703 GetTextMetrics(hdc
, &tm
) )
704 fsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
710 ReleaseDC (hwnd
, hdc
);
712 /* ... This is wrong in OEM only mode */
713 if (fsize
[FONT_UNDERLINE
] != fsize
[FONT_NORMAL
] ||
714 (bold_mode
== BOLD_FONT
&&
715 fsize
[FONT_BOLDUND
] != fsize
[FONT_BOLD
])) {
717 DeleteObject (fonts
[FONT_UNDERLINE
]);
718 if (bold_mode
== BOLD_FONT
)
719 DeleteObject (fonts
[FONT_BOLDUND
]);
722 if (bold_mode
== BOLD_FONT
&&
723 fsize
[FONT_BOLD
] != fsize
[FONT_NORMAL
]) {
724 bold_mode
= BOLD_SHADOW
;
725 DeleteObject (fonts
[FONT_BOLD
]);
726 if (und_mode
== UND_FONT
)
727 DeleteObject (fonts
[FONT_BOLDUND
]);
731 /* With the fascist font painting it doesn't matter if the linedraw font
732 * isn't exactly the right size anymore so we don't have to check this.
734 if (cfg
.vtmode
== VT_OEMANSI
&& fsize
[FONT_OEM
] != fsize
[FONT_NORMAL
] ) {
735 if( cfg
.fontcharset
== OEM_CHARSET
)
737 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
738 "different sizes. Using OEM-only mode instead",
739 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
740 cfg
.vtmode
= VT_OEMONLY
;
742 else if( firstchar
< ' ' )
744 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
745 "different sizes. Using XTerm mode instead",
746 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
747 cfg
.vtmode
= VT_XWINDOWS
;
751 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
752 "different sizes. Using ISO8859-1 mode instead",
753 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
754 cfg
.vtmode
= VT_POORMAN
;
759 DeleteObject (fonts
[i
]);
765 void request_resize (int w
, int h
, int refont
) {
769 /* Don't do this in OEMANSI, you may get disable messages */
770 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132)
771 && cfg
.vtmode
!= VT_OEMANSI
)
773 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132))
776 /* If font width too big for screen should we shrink the font more ? */
778 font_width
= ((font_width
*cols
+w
/2)/w
);
785 DeleteObject(fonts
[i
]);
787 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
789 init_fonts(font_width
);
792 width
= extra_width
+ font_width
* w
;
793 height
= extra_height
+ font_height
* h
;
795 SetWindowPos (hwnd
, NULL
, 0, 0, width
, height
,
796 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
797 SWP_NOMOVE
| SWP_NOZORDER
);
800 static void click (Mouse_Button b
, int x
, int y
) {
801 int thistime
= GetMessageTime();
803 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
804 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
805 lastact
== MA_2CLK ? MA_3CLK
:
806 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
811 if (lastact
!= MA_NOTHING
)
812 term_mouse (b
, lastact
, x
, y
);
816 static LRESULT CALLBACK
WndProc (HWND hwnd
, UINT message
,
817 WPARAM wParam
, LPARAM lParam
) {
819 static int ignore_size
= FALSE
;
820 static int ignore_clip
= FALSE
;
821 static int just_reconfigged
= FALSE
;
825 if (pending_netevent
)
826 enact_pending_netevent();
827 if (inbuf_reap
!= inbuf_head
)
834 if (!cfg
.warn_on_close
|| session_closed
||
835 MessageBox(hwnd
, "Are you sure you want to close this session?",
836 "PuTTY Exit Confirmation",
837 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
844 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
856 PROCESS_INFORMATION pi
;
857 HANDLE filemap
= NULL
;
859 if (wParam
== IDM_DUPSESS
) {
861 * Allocate a file-mapping memory chunk for the
864 SECURITY_ATTRIBUTES sa
;
867 sa
.nLength
= sizeof(sa
);
868 sa
.lpSecurityDescriptor
= NULL
;
869 sa
.bInheritHandle
= TRUE
;
870 filemap
= CreateFileMapping((HANDLE
)0xFFFFFFFF,
877 p
= (Config
*)MapViewOfFile(filemap
,
879 0, 0, sizeof(Config
));
881 *p
= cfg
; /* structure copy */
885 sprintf(c
, "putty &%p", filemap
);
887 } else if (wParam
== IDM_SAVEDSESS
) {
888 char *session
= sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
889 cl
= malloc(16 + strlen(session
)); /* 8, but play safe */
891 cl
= NULL
; /* not a very important failure mode */
893 sprintf(cl
, "putty @%s", session
);
899 GetModuleFileName (NULL
, b
, sizeof(b
)-1);
901 si
.lpReserved
= NULL
;
906 si
.lpReserved2
= NULL
;
907 CreateProcess (b
, cl
, NULL
, NULL
, TRUE
,
908 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
911 CloseHandle(filemap
);
917 if (!do_reconfig(hwnd
))
919 just_reconfigged
= TRUE
;
924 DeleteObject(fonts
[i
]);
926 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
930 /* Telnet will change local echo -> remote if the remote asks */
931 if (cfg
.protocol
!= PROT_TELNET
)
932 ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
939 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
940 InvalidateRect(hwnd
, NULL
, TRUE
);
941 SetWindowPos (hwnd
, NULL
, 0, 0,
942 extra_width
+ font_width
* cfg
.width
,
943 extra_height
+ font_height
* cfg
.height
,
944 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
945 SWP_NOMOVE
| SWP_NOZORDER
);
946 if (IsIconic(hwnd
)) {
948 cfg
.win_name_always ? window_name
: icon_name
);
957 case IDM_TEL_AYT
: back
->special (TS_AYT
); break;
958 case IDM_TEL_BRK
: back
->special (TS_BRK
); break;
959 case IDM_TEL_SYNCH
: back
->special (TS_SYNCH
); break;
960 case IDM_TEL_EC
: back
->special (TS_EC
); break;
961 case IDM_TEL_EL
: back
->special (TS_EL
); break;
962 case IDM_TEL_GA
: back
->special (TS_GA
); break;
963 case IDM_TEL_NOP
: back
->special (TS_NOP
); break;
964 case IDM_TEL_ABORT
: back
->special (TS_ABORT
); break;
965 case IDM_TEL_AO
: back
->special (TS_AO
); break;
966 case IDM_TEL_IP
: back
->special (TS_IP
); break;
967 case IDM_TEL_SUSP
: back
->special (TS_SUSP
); break;
968 case IDM_TEL_EOR
: back
->special (TS_EOR
); break;
969 case IDM_TEL_EOF
: back
->special (TS_EOF
); break;
974 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
975 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
980 #define X_POS(l) ((int)(short)LOWORD(l))
981 #define Y_POS(l) ((int)(short)HIWORD(l))
983 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
984 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
987 click (MB_SELECT
, TO_CHR_X(X_POS(lParam
)),
988 TO_CHR_Y(Y_POS(lParam
)));
992 term_mouse (MB_SELECT
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
993 TO_CHR_Y(Y_POS(lParam
)));
998 click (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
999 TO_CHR_X(X_POS(lParam
)),
1000 TO_CHR_Y(Y_POS(lParam
)));
1003 term_mouse (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1004 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1005 TO_CHR_Y(Y_POS(lParam
)));
1008 case WM_RBUTTONDOWN
:
1010 click (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1011 TO_CHR_X(X_POS(lParam
)),
1012 TO_CHR_Y(Y_POS(lParam
)));
1015 term_mouse (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1016 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1017 TO_CHR_Y(Y_POS(lParam
)));
1022 * Add the mouse position and message time to the random
1023 * number noise, if we're using ssh.
1025 if (cfg
.protocol
== PROT_SSH
)
1026 noise_ultralight(lParam
);
1028 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1030 if (wParam
& MK_LBUTTON
)
1032 else if (wParam
& MK_MBUTTON
)
1033 b
= cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
;
1035 b
= cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
;
1036 term_mouse (b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1037 TO_CHR_Y(Y_POS(lParam
)));
1040 case WM_IGNORE_CLIP
:
1041 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1043 case WM_DESTROYCLIPBOARD
:
1046 ignore_clip
= FALSE
;
1051 hdc
= BeginPaint (hwnd
, &p
);
1053 SelectPalette (hdc
, pal
, TRUE
);
1054 RealizePalette (hdc
);
1056 term_paint (hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1057 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1058 SelectObject (hdc
, GetStockObject(SYSTEM_FONT
));
1059 SelectObject (hdc
, GetStockObject(WHITE_PEN
));
1060 EndPaint (hwnd
, &p
);
1064 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1065 * but the only one that's likely to try to overload us is FD_READ.
1066 * This means buffering just one is fine.
1068 if (pending_netevent
)
1069 enact_pending_netevent();
1071 pending_netevent
= TRUE
;
1072 pend_netevent_wParam
=wParam
;
1073 pend_netevent_lParam
=lParam
;
1085 case WM_IGNORE_SIZE
:
1086 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1088 case WM_ENTERSIZEMOVE
:
1091 case WM_EXITSIZEMOVE
:
1096 int width
, height
, w
, h
, ew
, eh
;
1097 LPRECT r
= (LPRECT
)lParam
;
1099 width
= r
->right
- r
->left
- extra_width
;
1100 height
= r
->bottom
- r
->top
- extra_height
;
1101 w
= (width
+ font_width
/2) / font_width
; if (w
< 1) w
= 1;
1102 h
= (height
+ font_height
/2) / font_height
; if (h
< 1) h
= 1;
1103 UpdateSizeTip(hwnd
, w
, h
);
1104 ew
= width
- w
* font_width
;
1105 eh
= height
- h
* font_height
;
1107 if (wParam
== WMSZ_LEFT
||
1108 wParam
== WMSZ_BOTTOMLEFT
||
1109 wParam
== WMSZ_TOPLEFT
)
1115 if (wParam
== WMSZ_TOP
||
1116 wParam
== WMSZ_TOPRIGHT
||
1117 wParam
== WMSZ_TOPLEFT
)
1127 /* break; (never reached) */
1129 if (wParam
== SIZE_MINIMIZED
) {
1130 SetWindowText (hwnd
,
1131 cfg
.win_name_always ? window_name
: icon_name
);
1134 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1135 SetWindowText (hwnd
, window_name
);
1137 int width
, height
, w
, h
;
1138 #if 0 /* we have fixed this using WM_SIZING now */
1142 width
= LOWORD(lParam
);
1143 height
= HIWORD(lParam
);
1144 w
= width
/ font_width
; if (w
< 1) w
= 1;
1145 h
= height
/ font_height
; if (h
< 1) h
= 1;
1146 #if 0 /* we have fixed this using WM_SIZING now */
1147 ew
= width
- w
* font_width
;
1148 eh
= height
- h
* font_height
;
1149 if (ew
!= 0 || eh
!= 0) {
1151 GetWindowRect (hwnd
, &r
);
1152 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1153 SetWindowPos (hwnd
, NULL
, 0, 0,
1154 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1155 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1158 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1160 term_size (h
, w
, cfg
.savelines
);
1162 just_reconfigged
= FALSE
;
1165 ignore_size
= FALSE
;
1168 switch (LOWORD(wParam
)) {
1169 case SB_BOTTOM
: term_scroll(-1, 0); break;
1170 case SB_TOP
: term_scroll(+1, 0); break;
1171 case SB_LINEDOWN
: term_scroll (0, +1); break;
1172 case SB_LINEUP
: term_scroll (0, -1); break;
1173 case SB_PAGEDOWN
: term_scroll (0, +rows
/2); break;
1174 case SB_PAGEUP
: term_scroll (0, -rows
/2); break;
1175 case SB_THUMBPOSITION
: case SB_THUMBTRACK
:
1176 term_scroll (1, HIWORD(wParam
)); break;
1179 case WM_PALETTECHANGED
:
1180 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1181 HDC hdc
= get_ctx();
1183 if (RealizePalette (hdc
) > 0)
1189 case WM_QUERYNEWPALETTE
:
1191 HDC hdc
= get_ctx();
1193 if (RealizePalette (hdc
) > 0)
1203 * Add the scan code and keypress timing to the random
1204 * number noise, if we're using ssh.
1206 if (cfg
.protocol
== PROT_SSH
)
1207 noise_ultralight(lParam
);
1210 * We don't do TranslateMessage since it disassociates the
1211 * resulting CHAR message from the KEYDOWN that sparked it,
1212 * which we occasionally don't want. Instead, we process
1213 * KEYDOWN, and call the Win32 translator functions so that
1214 * we get the translations under _our_ control.
1217 unsigned char buf
[20];
1220 len
= TranslateKey (wParam
, lParam
, buf
);
1222 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1223 ldisc
->send (buf
, len
);
1229 * We handle KEYUP ourselves in order to distinghish left
1230 * and right Alt or Control keys, which Windows won't do
1231 * right if left to itself. See also the special processing
1232 * at the top of TranslateKey.
1236 int ret
= GetKeyboardState(keystate
);
1237 if (ret
&& wParam
== VK_MENU
) {
1238 if (lParam
& 0x1000000) keystate
[VK_RMENU
] = 0;
1239 else keystate
[VK_LMENU
] = 0;
1240 SetKeyboardState (keystate
);
1242 if (ret
&& wParam
== VK_CONTROL
) {
1243 if (lParam
& 0x1000000) keystate
[VK_RCONTROL
] = 0;
1244 else keystate
[VK_LCONTROL
] = 0;
1245 SetKeyboardState (keystate
);
1249 * We don't return here, in order to allow Windows to do
1250 * its own KEYUP processing as well.
1256 * Nevertheless, we are prepared to deal with WM_CHAR
1257 * messages, should they crop up. So if someone wants to
1258 * post the things to us as part of a macro manoeuvre,
1259 * we're ready to cope.
1262 char c
= xlat_kbd2tty((unsigned char)wParam
);
1263 ldisc
->send (&c
, 1);
1268 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1272 * Draw a line of text in the window, at given character
1273 * coordinates, in given attributes.
1275 * We are allowed to fiddle with the contents of `text'.
1277 void do_text (Context ctx
, int x
, int y
, char *text
, int len
,
1278 unsigned long attr
) {
1279 int lattr
= 0; /* Will be arg later for line attribute type */
1281 int nfg
, nbg
, nfont
;
1284 int force_manual_underline
= 0;
1285 static int *IpDx
= 0, IpDxLEN
= 0;;
1287 if (len
>IpDxLEN
|| IpDx
[0] != font_width
*(1+!lattr
)) {
1291 IpDx
= smalloc((len
+16)*sizeof(int));
1294 for(i
=0; i
<len
; i
++)
1295 IpDx
[i
] = font_width
;
1303 if (attr
& ATTR_ACTCURS
) {
1304 attr
&= (bold_mode
== BOLD_COLOURS ?
0x200 : 0x300);
1305 attr
^= ATTR_CUR_XOR
;
1309 if (cfg
.vtmode
== VT_OEMONLY
)
1313 * Map high-half characters in order to approximate ISO using
1314 * OEM character set. No characters are missing if the OEM codepage
1317 if (nfont
& FONT_OEM
) {
1319 for (i
=0; i
<len
; i
++)
1320 if (text
[i
] >= '\xA0' && text
[i
] <= '\xFF') {
1322 /* This is CP850 ... perfect translation */
1323 static const char oemhighhalf
[] =
1324 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1325 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1326 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1327 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1328 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1329 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1330 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1331 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1332 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1333 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1334 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1335 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1338 /* This is CP437 ... junk translation */
1339 static const unsigned char oemhighhalf
[] = {
1340 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1341 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1342 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1343 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1344 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1345 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1346 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1347 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1348 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1349 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1350 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1351 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1354 text
[i
] = oemhighhalf
[(unsigned char)text
[i
] - 0xA0];
1358 if (attr
& ATTR_GBCHR
) {
1361 * GB mapping: map # to pound, and everything else stays
1364 for (i
=0; i
<len
; i
++)
1366 text
[i
] = cfg
.vtmode
== VT_OEMONLY ?
'\x9C' : '\xA3';
1367 } else if (attr
& ATTR_LINEDRW
) {
1370 static const char poorman
[] =
1371 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1374 static const char oemmap_437
[] =
1375 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1376 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1379 static const char oemmap_850
[] =
1380 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1381 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1383 /* Poor windows font ... eg: windows courier */
1384 static const char oemmap
[] =
1385 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1386 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1389 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1390 * VT100 line drawing chars; everything else stays normal.
1392 switch (cfg
.vtmode
) {
1394 for (i
=0; i
<len
; i
++)
1395 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1396 text
[i
] += '\x01' - '\x60';
1399 /* Make sure we actually have an OEM font */
1400 if (fonts
[nfont
|FONT_OEM
]) {
1403 for (i
=0; i
<len
; i
++)
1404 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1405 text
[i
] = oemmap
[(unsigned char)text
[i
] - 0x60];
1409 for (i
=0; i
<len
; i
++)
1410 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1411 text
[i
] = poorman
[(unsigned char)text
[i
] - 0x60];
1416 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
1417 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
1418 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
1420 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
1421 nfont
|= FONT_UNDERLINE
;
1424 if (nfont
&FONT_UNDERLINE
)
1425 force_manual_underline
= 1;
1426 /* Don't do the same for manual bold, it could be bad news. */
1428 nfont
&= ~(FONT_BOLD
|FONT_UNDERLINE
);
1430 if (attr
& ATTR_REVERSE
) {
1431 t
= nfg
; nfg
= nbg
; nbg
= t
;
1433 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
1435 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
1439 SelectObject (hdc
, fonts
[nfont
]);
1440 SetTextColor (hdc
, fg
);
1441 SetBkColor (hdc
, bg
);
1442 SetBkMode (hdc
, OPAQUE
);
1445 line_box
.right
= x
+font_width
*len
;
1446 line_box
.bottom
= y
+font_height
;
1447 ExtTextOut (hdc
, x
, y
, ETO_CLIPPED
|ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
1448 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
1449 SetBkMode (hdc
, TRANSPARENT
);
1451 /* GRR: This draws the character outside it's box and can leave
1452 * 'droppings' even with the clip box! I suppose I could loop it
1453 * one character at a time ... yuk. */
1454 ExtTextOut (hdc
, x
-1, y
, ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
1456 if (force_manual_underline
||
1457 (und_mode
== UND_LINE
&& (attr
& ATTR_UNDER
))) {
1459 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, fg
));
1460 MoveToEx (hdc
, x
, y
+descent
, NULL
);
1461 LineTo (hdc
, x
+len
*font_width
, y
+descent
);
1462 oldpen
= SelectObject (hdc
, oldpen
);
1463 DeleteObject (oldpen
);
1465 if (attr
& ATTR_PASCURS
) {
1468 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
1469 pts
[2].x
= pts
[3].x
= x
+font_width
-1;
1470 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
1471 pts
[1].y
= pts
[2].y
= y
+font_height
-1;
1472 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
1473 Polyline (hdc
, pts
, 5);
1474 oldpen
= SelectObject (hdc
, oldpen
);
1475 DeleteObject (oldpen
);
1480 * Translate a WM_(SYS)?KEYDOWN message into a string of ASCII
1481 * codes. Returns number of bytes used.
1483 static int TranslateKey(WPARAM wParam
, LPARAM lParam
, unsigned char *output
) {
1484 unsigned char *p
= output
;
1487 int cancel_alt
= FALSE
;
1490 * Get hold of the keyboard state, because we'll need it a few
1493 ret
= GetKeyboardState(keystate
);
1496 * Record that we pressed key so the scroll window can be reset, but
1497 * be careful to avoid Shift-UP/Down
1499 if( wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
1504 * Windows does not always want to distinguish left and right
1505 * Alt or Control keys. Thus we keep track of them ourselves.
1506 * See also the WM_KEYUP handler.
1508 if (wParam
== VK_MENU
) {
1509 if (lParam
& 0x1000000) keystate
[VK_RMENU
] = 0x80;
1510 else keystate
[VK_LMENU
] = 0x80;
1511 SetKeyboardState (keystate
);
1514 if (wParam
== VK_CONTROL
) {
1515 if (lParam
& 0x1000000) keystate
[VK_RCONTROL
] = 0x80;
1516 else keystate
[VK_LCONTROL
] = 0x80;
1517 SetKeyboardState (keystate
);
1522 * Prepend ESC, and cancel ALT, if ALT was pressed at the time
1523 * and it wasn't AltGr.
1525 if (lParam
& 0x20000000 && (keystate
[VK_LMENU
] & 0x80)) {
1531 * NetHack keypad mode. This may conflict with Shift-PgUp/PgDn,
1532 * so we do it first.
1534 if (cfg
.nethack_keypad
) {
1535 int shift
= keystate
[VK_SHIFT
] & 0x80;
1537 * NB the shifted versions only work with numlock off.
1539 switch ( (lParam
>> 16) & 0x1FF ) {
1540 case 0x047: *p
++ = shift ?
'Y' : 'y'; return p
- output
;
1541 case 0x048: *p
++ = shift ?
'K' : 'k'; return p
- output
;
1542 case 0x049: *p
++ = shift ?
'U' : 'u'; return p
- output
;
1543 case 0x04B: *p
++ = shift ?
'H' : 'h'; return p
- output
;
1544 case 0x04C: *p
++ = '.'; return p
- output
;
1545 case 0x04D: *p
++ = shift ?
'L' : 'l'; return p
- output
;
1546 case 0x04F: *p
++ = shift ?
'B' : 'b'; return p
- output
;
1547 case 0x050: *p
++ = shift ?
'J' : 'j'; return p
- output
;
1548 case 0x051: *p
++ = shift ?
'N' : 'n'; return p
- output
;
1549 case 0x053: *p
++ = '.'; return p
- output
;
1554 * Shift-PgUp, Shift-PgDn, and Alt-F4 all produce window
1555 * events: we'll deal with those now.
1557 if (ret
&& (keystate
[VK_SHIFT
] & 0x80) && wParam
== VK_PRIOR
) {
1558 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
1561 if (ret
&& (keystate
[VK_SHIFT
] & 0x80) && wParam
== VK_NEXT
) {
1562 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
1565 if ((lParam
& 0x20000000) && wParam
== VK_F4
&& cfg
.alt_f4
) {
1568 if ((lParam
& 0x20000000) && wParam
== VK_SPACE
&& cfg
.alt_space
) {
1569 SendMessage (hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
1574 * In general, the strategy is to see what the Windows keymap
1575 * translation has to say for itself, and then process function
1576 * keys and suchlike ourselves if that fails. But first we must
1577 * deal with the small number of special cases which the
1578 * Windows keymap translator thinks it can do but gets wrong.
1580 * First special case: we might want the Backspace key to send
1583 if (wParam
== VK_BACK
) {
1584 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
1589 * Control-Space should send ^@ (0x00), not Space.
1591 if (ret
&& (keystate
[VK_CONTROL
] & 0x80) && wParam
== VK_SPACE
) {
1596 if (app_keypad_keys
) {
1598 * If we're in applications keypad mode, we have to process it
1599 * before char-map translation, because it will pre-empt lots
1600 * of stuff, even if NumLock is off.
1604 * Hack to ensure NumLock doesn't interfere with
1605 * perception of Shift for Keypad Plus. I don't pretend
1606 * to understand this, but it seems to work as is.
1607 * Leave it alone, or die.
1609 keystate
[VK_NUMLOCK
] = 0;
1610 SetKeyboardState (keystate
);
1611 GetKeyboardState (keystate
);
1613 switch ( (lParam
>> 16) & 0x1FF ) {
1614 case 0x145: p
+= sprintf((char *)p
, "\x1BOP"); return p
- output
;
1615 case 0x135: p
+= sprintf((char *)p
, "\x1BOQ"); return p
- output
;
1616 case 0x037: p
+= sprintf((char *)p
, "\x1BOR"); return p
- output
;
1617 case 0x047: p
+= sprintf((char *)p
, "\x1BOw"); return p
- output
;
1618 case 0x048: p
+= sprintf((char *)p
, "\x1BOx"); return p
- output
;
1619 case 0x049: p
+= sprintf((char *)p
, "\x1BOy"); return p
- output
;
1620 case 0x04A: p
+= sprintf((char *)p
, "\x1BOS"); return p
- output
;
1621 case 0x04B: p
+= sprintf((char *)p
, "\x1BOt"); return p
- output
;
1622 case 0x04C: p
+= sprintf((char *)p
, "\x1BOu"); return p
- output
;
1623 case 0x04D: p
+= sprintf((char *)p
, "\x1BOv"); return p
- output
;
1624 case 0x04E: /* keypad + is ^[Ol, but ^[Om with Shift */
1625 p
+= sprintf((char *)p
,
1626 (ret
&& (keystate
[VK_SHIFT
] & 0x80)) ?
1627 "\x1BOm" : "\x1BOl");
1629 case 0x04F: p
+= sprintf((char *)p
, "\x1BOq"); return p
- output
;
1630 case 0x050: p
+= sprintf((char *)p
, "\x1BOr"); return p
- output
;
1631 case 0x051: p
+= sprintf((char *)p
, "\x1BOs"); return p
- output
;
1632 case 0x052: p
+= sprintf((char *)p
, "\x1BOp"); return p
- output
;
1633 case 0x053: p
+= sprintf((char *)p
, "\x1BOn"); return p
- output
;
1634 case 0x11C: p
+= sprintf((char *)p
, "\x1BOM"); return p
- output
;
1639 * Shift-Tab should send ESC [ Z.
1641 if (ret
&& (keystate
[VK_SHIFT
] & 0x80) && wParam
== VK_TAB
) {
1642 *p
++ = 0x1B; /* ESC */
1649 * Before doing Windows charmap translation, remove LeftALT
1650 * from the keymap, since its sole effect should be to prepend
1651 * ESC, which we've already done. Note that removal of LeftALT
1652 * has to happen _after_ the above call to SetKeyboardState, or
1653 * dire things will befall.
1656 keystate
[VK_MENU
] = keystate
[VK_RMENU
];
1657 keystate
[VK_LMENU
] = 0;
1661 * Attempt the Windows char-map translation.
1666 BOOL capsOn
=keystate
[VK_CAPITAL
] !=0;
1668 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
1669 if(cfg
.xlat_capslockcyr
)
1670 keystate
[VK_CAPITAL
] = 0;
1672 r
= ToAscii (wParam
, (lParam
>> 16) & 0xFF,
1676 chr
= xlat_latkbd2win((unsigned char)(chr
& 0xFF));
1678 *p
++ = xlat_kbd2tty((unsigned char)(chr
& 0xFF));
1684 * OK, we haven't had a key code from the keymap translation.
1685 * We'll try our various special cases and function keys, and
1686 * then give up. (There's nothing wrong with giving up:
1687 * Scrollock, Pause/Break, and of course the various buckybit
1688 * keys all produce KEYDOWN events that we really _do_ want to
1693 * Control-2 should return ^@ (0x00), Control-6 should return
1694 * ^^ (0x1E), and Control-Minus should return ^_ (0x1F). Since
1695 * the DOS keyboard handling did it, and we have nothing better
1696 * to do with the key combo in question, we'll also map
1697 * Control-Backquote to ^\ (0x1C).
1699 * In addition a real VT100 maps Ctrl-3/4/5/7 and 8.
1701 if (ret
&& (keystate
[VK_CONTROL
] & 0x80) && wParam
== '2') {
1705 if (ret
&& (keystate
[VK_CONTROL
] & 0x80) && wParam
== '3') {
1709 if (ret
&& (keystate
[VK_CONTROL
] & 0x80) && wParam
== '4') {
1713 if (ret
&& (keystate
[VK_CONTROL
] & 0x80) && wParam
== '5') {
1717 if (ret
&& (keystate
[VK_CONTROL
] & 0x80) && wParam
== '6') {
1721 if (ret
&& (keystate
[VK_CONTROL
] & 0x80) && wParam
== '7') {
1725 if (ret
&& (keystate
[VK_CONTROL
] & 0x80) && wParam
== '8') {
1729 if (ret
&& (keystate
[VK_CONTROL
] & 0x80) && wParam
== 0xBD) {
1733 if (ret
&& (keystate
[VK_CONTROL
] & 0x80) && wParam
== 0xDF) {
1739 * First, all the keys that do tilde codes. (ESC '[' nn '~',
1740 * for integer decimal nn.)
1742 * We also deal with the weird ones here. Linux VCs replace F1
1743 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1744 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1749 case VK_F1
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11); break;
1750 case VK_F2
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12); break;
1751 case VK_F3
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13); break;
1752 case VK_F4
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14); break;
1753 case VK_F5
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15); break;
1754 case VK_F6
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17); break;
1755 case VK_F7
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18); break;
1756 case VK_F8
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19); break;
1757 case VK_F9
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20); break;
1758 case VK_F10
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21); break;
1759 case VK_F11
: code
= 23; break;
1760 case VK_F12
: code
= 24; break;
1761 case VK_HOME
: code
= 1; break;
1762 case VK_INSERT
: code
= 2; break;
1763 case VK_DELETE
: code
= 3; break;
1764 case VK_END
: code
= 4; break;
1765 case VK_PRIOR
: code
= 5; break;
1766 case VK_NEXT
: code
= 6; break;
1768 if (cfg
.linux_funkeys
&& code
>= 11 && code
<= 15) {
1769 p
+= sprintf((char *)p
, "\x1B[[%c", code
+ 'A' - 11);
1772 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
1773 p
+= sprintf((char *)p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
1777 p
+= sprintf((char *)p
, "\x1B[%d~", code
);
1782 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
1783 * some reason seems to send VK_CLEAR to Windows...).
1787 p
+= sprintf((char *)p
, app_cursor_keys ?
"\x1BOA" : "\x1B[A");
1790 p
+= sprintf((char *)p
, app_cursor_keys ?
"\x1BOB" : "\x1B[B");
1793 p
+= sprintf((char *)p
, app_cursor_keys ?
"\x1BOC" : "\x1B[C");
1796 p
+= sprintf((char *)p
, app_cursor_keys ?
"\x1BOD" : "\x1B[D");
1798 case VK_CLEAR
: p
+= sprintf((char *)p
, "\x1B[G"); return p
- output
;
1804 void set_title (char *title
) {
1805 sfree (window_name
);
1806 window_name
= smalloc(1+strlen(title
));
1807 strcpy (window_name
, title
);
1808 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
1809 SetWindowText (hwnd
, title
);
1812 void set_icon (char *title
) {
1814 icon_name
= smalloc(1+strlen(title
));
1815 strcpy (icon_name
, title
);
1816 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
1817 SetWindowText (hwnd
, title
);
1820 void set_sbar (int total
, int start
, int page
) {
1822 si
.cbSize
= sizeof(si
);
1823 si
.fMask
= SIF_RANGE
| SIF_PAGE
| SIF_POS
| SIF_DISABLENOSCROLL
;
1825 si
.nMax
= total
- 1;
1829 SetScrollInfo (hwnd
, SB_VERT
, &si
, TRUE
);
1832 Context
get_ctx(void) {
1837 SelectPalette (hdc
, pal
, FALSE
);
1843 void free_ctx (Context ctx
) {
1844 SelectPalette (ctx
, GetStockObject (DEFAULT_PALETTE
), FALSE
);
1845 ReleaseDC (hwnd
, ctx
);
1848 static void real_palette_set (int n
, int r
, int g
, int b
) {
1850 logpal
->palPalEntry
[n
].peRed
= r
;
1851 logpal
->palPalEntry
[n
].peGreen
= g
;
1852 logpal
->palPalEntry
[n
].peBlue
= b
;
1853 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
1854 colours
[n
] = PALETTERGB(r
, g
, b
);
1855 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
1857 colours
[n
] = RGB(r
, g
, b
);
1860 void palette_set (int n
, int r
, int g
, int b
) {
1861 static const int first
[21] = {
1862 0, 2, 4, 6, 8, 10, 12, 14,
1863 1, 3, 5, 7, 9, 11, 13, 15,
1866 real_palette_set (first
[n
], r
, g
, b
);
1868 real_palette_set (first
[n
]+1, r
, g
, b
);
1870 HDC hdc
= get_ctx();
1871 UnrealizeObject (pal
);
1872 RealizePalette (hdc
);
1877 void palette_reset (void) {
1880 for (i
= 0; i
< NCOLOURS
; i
++) {
1882 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
1883 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
1884 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
1885 logpal
->palPalEntry
[i
].peFlags
= 0;
1886 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
1887 defpal
[i
].rgbtGreen
,
1888 defpal
[i
].rgbtBlue
);
1890 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
1891 defpal
[i
].rgbtGreen
,
1892 defpal
[i
].rgbtBlue
);
1897 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
1899 RealizePalette (hdc
);
1904 void write_clip (void *data
, int len
) {
1908 clipdata
= GlobalAlloc (GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
1911 lock
= GlobalLock (clipdata
);
1914 memcpy (lock
, data
, len
);
1915 ((unsigned char *) lock
) [len
] = 0;
1916 GlobalUnlock (clipdata
);
1918 SendMessage (hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
1919 if (OpenClipboard (hwnd
)) {
1921 SetClipboardData (CF_TEXT
, clipdata
);
1924 GlobalFree (clipdata
);
1925 SendMessage (hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
1928 void get_clip (void **p
, int *len
) {
1929 static HGLOBAL clipdata
= NULL
;
1933 GlobalUnlock (clipdata
);
1937 if (OpenClipboard (NULL
)) {
1938 clipdata
= GetClipboardData (CF_TEXT
);
1941 *p
= GlobalLock (clipdata
);
1955 * Move `lines' lines from position `from' to position `to' in the
1958 void optimised_move (int to
, int from
, int lines
) {
1962 min
= (to
< from ? to
: from
);
1963 max
= to
+ from
- min
;
1965 r
.left
= 0; r
.right
= cols
* font_width
;
1966 r
.top
= min
* font_height
; r
.bottom
= (max
+lines
) * font_height
;
1967 ScrollWindow (hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
1971 * Print a message box and perform a fatal exit.
1973 void fatalbox(char *fmt
, ...) {
1978 vsprintf(stuff
, fmt
, ap
);
1980 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);