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(UINT message
, 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:", 7)) {
185 if (q
[0] == '/' && q
[1] == '/')
187 cfg
.protocol
= PROT_TELNET
;
189 while (*p
&& *p
!= ':' && *p
!= '/') p
++;
197 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
198 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
200 while (*p
&& !isspace(*p
)) p
++;
203 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
204 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
205 while (*p
&& isspace(*p
)) p
++;
220 * Select protocol. This is farmed out into a table in a
221 * separate file to enable an ssh-free variant.
226 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
227 if (backends
[i
].protocol
== cfg
.protocol
) {
228 back
= backends
[i
].backend
;
232 MessageBox(NULL
, "Unsupported protocol number found",
233 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
239 ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
243 wndclass
.lpfnWndProc
= WndProc
;
244 wndclass
.cbClsExtra
= 0;
245 wndclass
.cbWndExtra
= 0;
246 wndclass
.hInstance
= inst
;
247 wndclass
.hIcon
= LoadIcon (inst
,
248 MAKEINTRESOURCE(IDI_MAINICON
));
249 wndclass
.hCursor
= LoadCursor (NULL
, IDC_IBEAM
);
250 wndclass
.hbrBackground
= GetStockObject (BLACK_BRUSH
);
251 wndclass
.lpszMenuName
= NULL
;
252 wndclass
.lpszClassName
= appname
;
254 RegisterClass (&wndclass
);
259 savelines
= cfg
.savelines
;
265 * Guess some defaults for the window size. This all gets
266 * updated later, so we don't really care too much. However, we
267 * do want the font width/height guesses to correspond to a
268 * large font rather than a small one...
275 term_size (cfg
.height
, cfg
.width
, cfg
.savelines
);
276 guess_width
= extra_width
+ font_width
* cols
;
277 guess_height
= extra_height
+ font_height
* rows
;
280 HWND w
= GetDesktopWindow();
281 GetWindowRect (w
, &r
);
282 if (guess_width
> r
.right
- r
.left
)
283 guess_width
= r
.right
- r
.left
;
284 if (guess_height
> r
.bottom
- r
.top
)
285 guess_height
= r
.bottom
- r
.top
;
289 int winmode
= WS_OVERLAPPEDWINDOW
|WS_VSCROLL
;
290 if (!cfg
.scrollbar
) winmode
&= ~(WS_VSCROLL
);
291 if (cfg
.locksize
) winmode
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
292 hwnd
= CreateWindow (appname
, appname
,
294 CW_USEDEFAULT
, CW_USEDEFAULT
,
295 guess_width
, guess_height
,
296 NULL
, NULL
, inst
, NULL
);
300 * Initialise the fonts, simultaneously correcting the guesses
301 * for font_{width,height}.
303 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
308 * Correct the guesses for extra_{width,height}.
312 GetWindowRect (hwnd
, &wr
);
313 GetClientRect (hwnd
, &cr
);
314 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
315 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
319 * Resize the window, now we know what size we _really_ want it
322 guess_width
= extra_width
+ font_width
* cols
;
323 guess_height
= extra_height
+ font_height
* rows
;
324 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
325 SetWindowPos (hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
326 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
329 * Initialise the scroll bar.
334 si
.cbSize
= sizeof(si
);
335 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
340 SetScrollInfo (hwnd
, SB_VERT
, &si
, FALSE
);
344 * Start up the telnet connection.
351 error
= back
->init (hwnd
, cfg
.host
, cfg
.port
, &realhost
);
353 sprintf(msg
, "Unable to open connection:\n%s", error
);
354 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
357 window_name
= icon_name
= NULL
;
358 sprintf(msg
, "%s - PuTTY", realhost
);
363 session_closed
= FALSE
;
366 * Set up the input and output buffers.
369 outbuf_reap
= outbuf_head
= 0;
372 * Prepare the mouse handler.
374 lastact
= MA_NOTHING
;
375 lastbtn
= MB_NOTHING
;
376 dbltime
= GetDoubleClickTime();
379 * Set up the session-control options on the system menu.
382 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
386 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
387 if (cfg
.protocol
== PROT_TELNET
) {
389 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
390 AppendMenu (p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
391 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
392 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
393 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
394 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
395 AppendMenu (p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
396 AppendMenu (p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
397 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
398 AppendMenu (p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
399 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
400 AppendMenu (p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
401 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
402 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
403 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
404 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
405 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
, "Telnet Command");
406 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
408 AppendMenu (m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
409 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
410 AppendMenu (m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session");
411 AppendMenu (m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
414 for (i
= 1 ; i
< ((nsessions
< 256) ? nsessions
: 256) ; i
++)
415 AppendMenu (s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
) , sessions
[i
]);
416 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
417 AppendMenu (m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings");
418 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
419 AppendMenu (m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
420 AppendMenu (m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
421 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
422 AppendMenu (m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
426 * Finally show the window!
428 ShowWindow (hwnd
, show
);
431 * Set the palette up.
437 has_focus
= (GetForegroundWindow() == hwnd
);
441 int timer_id
= 0, long_timer
= 0;
443 while (GetMessage (&msg
, NULL
, 0, 0) == 1) {
444 /* Sometimes DispatchMessage calls routines that use their own
445 * GetMessage loop, setup this timer so we get some control back.
447 * Also call term_update() from the timer so that if the host
448 * is sending data flat out we still do redraws.
450 if(timer_id
&& long_timer
) {
451 KillTimer(hwnd
, timer_id
);
452 long_timer
= timer_id
= 0;
455 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
456 DispatchMessage (&msg
);
458 /* This is too fast, but I'll leave it for now 'cause it shows
459 * how often term_update is called (far too often at times!)
463 /* Send the paste buffer if there's anything to send */
466 /* If there's nothing new in the queue then we can do everything
467 * we've delayed, reading the socket, writing, and repainting
470 if (!PeekMessage (&msg
, NULL
, 0, 0, PM_NOREMOVE
)) {
471 if (pending_netevent
) {
472 enact_pending_netevent();
477 if (!PeekMessage (&msg
, NULL
, 0, 0, PM_NOREMOVE
)) {
479 KillTimer(hwnd
, timer_id
);
486 timer_id
= SetTimer(hwnd
, 1, 2000, NULL
);
487 else if (cfg
.blinktext
)
488 timer_id
= SetTimer(hwnd
, 1, 250, NULL
);
490 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
503 DeleteObject(fonts
[i
]);
510 if (cfg
.protocol
== PROT_SSH
) {
521 * Actually do the job requested by a WM_NETEVENT
523 static void enact_pending_netevent(void) {
525 pending_netevent
= FALSE
;
526 i
= back
->msg (pend_netevent_wParam
, pend_netevent_lParam
);
530 switch (WSABASEERR
+ (-i
) % 10000) {
532 sprintf(buf
, "Connection reset by peer");
535 sprintf(buf
, "Unexpected network error %d", -i
);
538 MessageBox(hwnd
, buf
, "PuTTY Fatal Error",
539 MB_ICONERROR
| MB_OK
);
542 if (cfg
.close_on_exit
)
545 session_closed
= TRUE
;
546 MessageBox(hwnd
, "Connection closed by remote host",
547 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
548 SetWindowText (hwnd
, "PuTTY (inactive)");
554 * Copy the colour palette from the configuration data into defpal.
555 * This is non-trivial because the colour indices are different.
557 static void cfgtopalette(void) {
559 static const int ww
[] = {
560 6, 7, 8, 9, 10, 11, 12, 13,
561 14, 15, 16, 17, 18, 19, 20, 21,
562 0, 1, 2, 3, 4, 4, 5, 5
565 for (i
=0; i
<24; i
++) {
567 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
568 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
569 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
574 * Set up the colour palette.
576 static void init_palette(void) {
578 HDC hdc
= GetDC (hwnd
);
580 if (cfg
.try_palette
&&
581 GetDeviceCaps (hdc
, RASTERCAPS
) & RC_PALETTE
) {
582 logpal
= smalloc(sizeof(*logpal
)
583 - sizeof(logpal
->palPalEntry
)
584 + NCOLOURS
* sizeof(PALETTEENTRY
));
585 logpal
->palVersion
= 0x300;
586 logpal
->palNumEntries
= NCOLOURS
;
587 for (i
= 0; i
< NCOLOURS
; i
++) {
588 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
589 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
590 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
591 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
593 pal
= CreatePalette (logpal
);
595 SelectPalette (hdc
, pal
, FALSE
);
596 RealizePalette (hdc
);
597 SelectPalette (hdc
, GetStockObject (DEFAULT_PALETTE
),
601 ReleaseDC (hwnd
, hdc
);
604 for (i
=0; i
<NCOLOURS
; i
++)
605 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
609 for(i
=0; i
<NCOLOURS
; i
++)
610 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
616 * Initialise all the fonts we will need. There may be as many as
617 * eight or as few as one. We also:
619 * - check the font width and height, correcting our guesses if
622 * - verify that the bold font is the same width as the ordinary
623 * one, and engage shadow bolding if not.
625 * - verify that the underlined font is the same width as the
626 * ordinary one (manual underlining by means of line drawing can
627 * be done in a pinch).
629 static void init_fonts(int pick_width
) {
634 int fw_dontcare
, fw_bold
;
643 if (cfg
.fontisbold
) {
644 fw_dontcare
= FW_BOLD
;
647 fw_dontcare
= FW_DONTCARE
;
653 font_height
= cfg
.fontheight
;
654 font_width
= pick_width
;
657 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
658 c, OUT_DEFAULT_PRECIS, \
659 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
660 FIXED_PITCH | FF_DONTCARE, cfg.font)
662 if (cfg
.vtmode
!= VT_OEMONLY
) {
663 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
665 SelectObject (hdc
, fonts
[FONT_NORMAL
]);
666 GetTextMetrics(hdc
, &tm
);
667 font_height
= tm
.tmHeight
;
668 font_width
= tm
.tmAveCharWidth
;
670 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
672 if (bold_mode
== BOLD_FONT
) {
673 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
674 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
677 if (cfg
.vtmode
== VT_OEMANSI
) {
678 f(FONT_OEM
, OEM_CHARSET
, fw_dontcare
, FALSE
);
679 f(FONT_OEMUND
, OEM_CHARSET
, fw_dontcare
, TRUE
);
681 if (bold_mode
== BOLD_FONT
) {
682 f(FONT_OEMBOLD
, OEM_CHARSET
, fw_bold
, FALSE
);
683 f(FONT_OEMBOLDUND
, OEM_CHARSET
, fw_bold
, TRUE
);
689 f(FONT_OEM
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
691 SelectObject (hdc
, fonts
[FONT_OEM
]);
692 GetTextMetrics(hdc
, &tm
);
693 font_height
= tm
.tmHeight
;
694 font_width
= tm
.tmAveCharWidth
;
696 f(FONT_OEMUND
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
698 if (bold_mode
== BOLD_FONT
) {
699 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
700 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
705 descent
= tm
.tmAscent
+ 1;
706 if (descent
>= font_height
)
707 descent
= font_height
- 1;
708 firstchar
= tm
.tmFirstChar
;
710 for (i
=0; i
<8; i
++) {
712 if (SelectObject (hdc
, fonts
[i
]) &&
713 GetTextMetrics(hdc
, &tm
) )
714 fsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
720 ReleaseDC (hwnd
, hdc
);
722 /* ... This is wrong in OEM only mode */
723 if (fsize
[FONT_UNDERLINE
] != fsize
[FONT_NORMAL
] ||
724 (bold_mode
== BOLD_FONT
&&
725 fsize
[FONT_BOLDUND
] != fsize
[FONT_BOLD
])) {
727 DeleteObject (fonts
[FONT_UNDERLINE
]);
728 if (bold_mode
== BOLD_FONT
)
729 DeleteObject (fonts
[FONT_BOLDUND
]);
732 if (bold_mode
== BOLD_FONT
&&
733 fsize
[FONT_BOLD
] != fsize
[FONT_NORMAL
]) {
734 bold_mode
= BOLD_SHADOW
;
735 DeleteObject (fonts
[FONT_BOLD
]);
736 if (und_mode
== UND_FONT
)
737 DeleteObject (fonts
[FONT_BOLDUND
]);
741 /* With the fascist font painting it doesn't matter if the linedraw font
742 * isn't exactly the right size anymore so we don't have to check this.
744 if (cfg
.vtmode
== VT_OEMANSI
&& fsize
[FONT_OEM
] != fsize
[FONT_NORMAL
] ) {
745 if( cfg
.fontcharset
== OEM_CHARSET
)
747 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
748 "different sizes. Using OEM-only mode instead",
749 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
750 cfg
.vtmode
= VT_OEMONLY
;
752 else if( firstchar
< ' ' )
754 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
755 "different sizes. Using XTerm mode instead",
756 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
757 cfg
.vtmode
= VT_XWINDOWS
;
761 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
762 "different sizes. Using ISO8859-1 mode instead",
763 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
764 cfg
.vtmode
= VT_POORMAN
;
769 DeleteObject (fonts
[i
]);
775 void request_resize (int w
, int h
, int refont
) {
778 /* If the window is maximized supress resizing attempts */
779 if(IsZoomed(hwnd
)) return;
782 /* Don't do this in OEMANSI, you may get disable messages */
783 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132)
784 && cfg
.vtmode
!= VT_OEMANSI
)
786 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132))
789 /* If font width too big for screen should we shrink the font more ? */
791 font_width
= ((font_width
*cols
+w
/2)/w
);
798 DeleteObject(fonts
[i
]);
800 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
802 init_fonts(font_width
);
806 static int first_time
= 1;
812 /* Get the size of the screen */
813 if (GetClientRect(GetDesktopWindow(),&ss
))
814 /* first_time = 0 */;
815 else { first_time
= 2; break; }
817 /* Make sure the values are sane */
818 width
= (ss
.right
-ss
.left
-extra_width
) / font_width
;
819 height
= (ss
.bottom
-ss
.top
-extra_height
) / font_height
;
821 if (w
>width
) w
=width
;
822 if (h
>height
) h
=height
;
828 width
= extra_width
+ font_width
* w
;
829 height
= extra_height
+ font_height
* h
;
831 SetWindowPos (hwnd
, NULL
, 0, 0, width
, height
,
832 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
833 SWP_NOMOVE
| SWP_NOZORDER
);
836 static void click (Mouse_Button b
, int x
, int y
) {
837 int thistime
= GetMessageTime();
839 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
840 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
841 lastact
== MA_2CLK ? MA_3CLK
:
842 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
847 if (lastact
!= MA_NOTHING
)
848 term_mouse (b
, lastact
, x
, y
);
852 static LRESULT CALLBACK
WndProc (HWND hwnd
, UINT message
,
853 WPARAM wParam
, LPARAM lParam
) {
855 static int ignore_size
= FALSE
;
856 static int ignore_clip
= FALSE
;
857 static int just_reconfigged
= FALSE
;
861 if (pending_netevent
)
862 enact_pending_netevent();
870 if (!cfg
.warn_on_close
|| session_closed
||
871 MessageBox(hwnd
, "Are you sure you want to close this session?",
872 "PuTTY Exit Confirmation",
873 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
880 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
892 PROCESS_INFORMATION pi
;
893 HANDLE filemap
= NULL
;
895 if (wParam
== IDM_DUPSESS
) {
897 * Allocate a file-mapping memory chunk for the
900 SECURITY_ATTRIBUTES sa
;
903 sa
.nLength
= sizeof(sa
);
904 sa
.lpSecurityDescriptor
= NULL
;
905 sa
.bInheritHandle
= TRUE
;
906 filemap
= CreateFileMapping((HANDLE
)0xFFFFFFFF,
913 p
= (Config
*)MapViewOfFile(filemap
,
915 0, 0, sizeof(Config
));
917 *p
= cfg
; /* structure copy */
921 sprintf(c
, "putty &%p", filemap
);
923 } else if (wParam
== IDM_SAVEDSESS
) {
924 char *session
= sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
925 cl
= malloc(16 + strlen(session
)); /* 8, but play safe */
927 cl
= NULL
; /* not a very important failure mode */
929 sprintf(cl
, "putty @%s", session
);
935 GetModuleFileName (NULL
, b
, sizeof(b
)-1);
937 si
.lpReserved
= NULL
;
942 si
.lpReserved2
= NULL
;
943 CreateProcess (b
, cl
, NULL
, NULL
, TRUE
,
944 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
947 CloseHandle(filemap
);
953 if (!do_reconfig(hwnd
))
955 just_reconfigged
= TRUE
;
960 DeleteObject(fonts
[i
]);
962 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
966 /* Telnet will change local echo -> remote if the remote asks */
967 if (cfg
.protocol
!= PROT_TELNET
)
968 ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
976 /* Enable or disable the scroll bar, etc */
978 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
981 if (cfg
.scrollbar
) nflg
|= WS_VSCROLL
;
982 else nflg
&= ~WS_VSCROLL
;
984 nflg
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
986 nflg
|= (WS_THICKFRAME
|WS_MAXIMIZEBOX
);
992 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
993 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
994 SetWindowPos(hwnd
, NULL
, 0,0,0,0,
995 SWP_NOACTIVATE
|SWP_NOCOPYBITS
|
996 SWP_NOMOVE
|SWP_NOSIZE
| SWP_NOZORDER
|
999 GetWindowRect (hwnd
, &wr
);
1000 GetClientRect (hwnd
, &cr
);
1001 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1002 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1006 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1007 InvalidateRect(hwnd
, NULL
, TRUE
);
1008 SetWindowPos (hwnd
, NULL
, 0, 0,
1009 extra_width
+ font_width
* cfg
.width
,
1010 extra_height
+ font_height
* cfg
.height
,
1011 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1012 SWP_NOMOVE
| SWP_NOZORDER
);
1013 if (IsIconic(hwnd
)) {
1014 SetWindowText (hwnd
,
1015 cfg
.win_name_always ? window_name
: icon_name
);
1024 case IDM_TEL_AYT
: back
->special (TS_AYT
); break;
1025 case IDM_TEL_BRK
: back
->special (TS_BRK
); break;
1026 case IDM_TEL_SYNCH
: back
->special (TS_SYNCH
); break;
1027 case IDM_TEL_EC
: back
->special (TS_EC
); break;
1028 case IDM_TEL_EL
: back
->special (TS_EL
); break;
1029 case IDM_TEL_GA
: back
->special (TS_GA
); break;
1030 case IDM_TEL_NOP
: back
->special (TS_NOP
); break;
1031 case IDM_TEL_ABORT
: back
->special (TS_ABORT
); break;
1032 case IDM_TEL_AO
: back
->special (TS_AO
); break;
1033 case IDM_TEL_IP
: back
->special (TS_IP
); break;
1034 case IDM_TEL_SUSP
: back
->special (TS_SUSP
); break;
1035 case IDM_TEL_EOR
: back
->special (TS_EOR
); break;
1036 case IDM_TEL_EOF
: back
->special (TS_EOF
); break;
1041 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1042 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1047 #define X_POS(l) ((int)(short)LOWORD(l))
1048 #define Y_POS(l) ((int)(short)HIWORD(l))
1050 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1051 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1053 case WM_LBUTTONDOWN
:
1054 click (MB_SELECT
, TO_CHR_X(X_POS(lParam
)),
1055 TO_CHR_Y(Y_POS(lParam
)));
1059 term_mouse (MB_SELECT
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1060 TO_CHR_Y(Y_POS(lParam
)));
1063 case WM_MBUTTONDOWN
:
1065 click (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1066 TO_CHR_X(X_POS(lParam
)),
1067 TO_CHR_Y(Y_POS(lParam
)));
1070 term_mouse (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1071 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1072 TO_CHR_Y(Y_POS(lParam
)));
1075 case WM_RBUTTONDOWN
:
1077 click (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1078 TO_CHR_X(X_POS(lParam
)),
1079 TO_CHR_Y(Y_POS(lParam
)));
1082 term_mouse (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1083 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1084 TO_CHR_Y(Y_POS(lParam
)));
1089 * Add the mouse position and message time to the random
1090 * number noise, if we're using ssh.
1092 if (cfg
.protocol
== PROT_SSH
)
1093 noise_ultralight(lParam
);
1095 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1097 if (wParam
& MK_LBUTTON
)
1099 else if (wParam
& MK_MBUTTON
)
1100 b
= cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
;
1102 b
= cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
;
1103 term_mouse (b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1104 TO_CHR_Y(Y_POS(lParam
)));
1107 case WM_IGNORE_CLIP
:
1108 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1110 case WM_DESTROYCLIPBOARD
:
1113 ignore_clip
= FALSE
;
1118 hdc
= BeginPaint (hwnd
, &p
);
1120 SelectPalette (hdc
, pal
, TRUE
);
1121 RealizePalette (hdc
);
1123 term_paint (hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1124 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1125 SelectObject (hdc
, GetStockObject(SYSTEM_FONT
));
1126 SelectObject (hdc
, GetStockObject(WHITE_PEN
));
1127 EndPaint (hwnd
, &p
);
1131 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1132 * but the only one that's likely to try to overload us is FD_READ.
1133 * This means buffering just one is fine.
1135 if (pending_netevent
)
1136 enact_pending_netevent();
1138 pending_netevent
= TRUE
;
1139 pend_netevent_wParam
=wParam
;
1140 pend_netevent_lParam
=lParam
;
1152 case WM_IGNORE_SIZE
:
1153 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1155 case WM_ENTERSIZEMOVE
:
1158 case WM_EXITSIZEMOVE
:
1163 int width
, height
, w
, h
, ew
, eh
;
1164 LPRECT r
= (LPRECT
)lParam
;
1166 width
= r
->right
- r
->left
- extra_width
;
1167 height
= r
->bottom
- r
->top
- extra_height
;
1168 w
= (width
+ font_width
/2) / font_width
; if (w
< 1) w
= 1;
1169 h
= (height
+ font_height
/2) / font_height
; if (h
< 1) h
= 1;
1170 UpdateSizeTip(hwnd
, w
, h
);
1171 ew
= width
- w
* font_width
;
1172 eh
= height
- h
* font_height
;
1174 if (wParam
== WMSZ_LEFT
||
1175 wParam
== WMSZ_BOTTOMLEFT
||
1176 wParam
== WMSZ_TOPLEFT
)
1182 if (wParam
== WMSZ_TOP
||
1183 wParam
== WMSZ_TOPRIGHT
||
1184 wParam
== WMSZ_TOPLEFT
)
1194 /* break; (never reached) */
1196 if (wParam
== SIZE_MINIMIZED
) {
1197 SetWindowText (hwnd
,
1198 cfg
.win_name_always ? window_name
: icon_name
);
1201 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1202 SetWindowText (hwnd
, window_name
);
1204 int width
, height
, w
, h
;
1205 #if 0 /* we have fixed this using WM_SIZING now */
1209 width
= LOWORD(lParam
);
1210 height
= HIWORD(lParam
);
1211 w
= width
/ font_width
; if (w
< 1) w
= 1;
1212 h
= height
/ font_height
; if (h
< 1) h
= 1;
1213 #if 0 /* we have fixed this using WM_SIZING now */
1214 ew
= width
- w
* font_width
;
1215 eh
= height
- h
* font_height
;
1216 if (ew
!= 0 || eh
!= 0) {
1218 GetWindowRect (hwnd
, &r
);
1219 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1220 SetWindowPos (hwnd
, NULL
, 0, 0,
1221 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1222 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1225 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1227 term_size (h
, w
, cfg
.savelines
);
1229 just_reconfigged
= FALSE
;
1232 ignore_size
= FALSE
;
1235 switch (LOWORD(wParam
)) {
1236 case SB_BOTTOM
: term_scroll(-1, 0); break;
1237 case SB_TOP
: term_scroll(+1, 0); break;
1238 case SB_LINEDOWN
: term_scroll (0, +1); break;
1239 case SB_LINEUP
: term_scroll (0, -1); break;
1240 case SB_PAGEDOWN
: term_scroll (0, +rows
/2); break;
1241 case SB_PAGEUP
: term_scroll (0, -rows
/2); break;
1242 case SB_THUMBPOSITION
: case SB_THUMBTRACK
:
1243 term_scroll (1, HIWORD(wParam
)); break;
1246 case WM_PALETTECHANGED
:
1247 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1248 HDC hdc
= get_ctx();
1250 if (RealizePalette (hdc
) > 0)
1256 case WM_QUERYNEWPALETTE
:
1258 HDC hdc
= get_ctx();
1260 if (RealizePalette (hdc
) > 0)
1272 * Add the scan code and keypress timing to the random
1273 * number noise, if we're using ssh.
1275 if (cfg
.protocol
== PROT_SSH
)
1276 noise_ultralight(lParam
);
1279 * We don't do TranslateMessage since it disassociates the
1280 * resulting CHAR message from the KEYDOWN that sparked it,
1281 * which we occasionally don't want. Instead, we process
1282 * KEYDOWN, and call the Win32 translator functions so that
1283 * we get the translations under _our_ control.
1286 unsigned char buf
[20];
1289 len
= TranslateKey (message
, wParam
, lParam
, buf
);
1291 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1292 ldisc
->send (buf
, len
);
1298 * Nevertheless, we are prepared to deal with WM_CHAR
1299 * messages, should they crop up. So if someone wants to
1300 * post the things to us as part of a macro manoeuvre,
1301 * we're ready to cope.
1304 char c
= xlat_kbd2tty((unsigned char)wParam
);
1305 ldisc
->send (&c
, 1);
1310 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1314 * Draw a line of text in the window, at given character
1315 * coordinates, in given attributes.
1317 * We are allowed to fiddle with the contents of `text'.
1319 void do_text (Context ctx
, int x
, int y
, char *text
, int len
,
1320 unsigned long attr
, int lattr
) {
1322 int nfg
, nbg
, nfont
;
1325 int force_manual_underline
= 0;
1326 int fnt_width
= font_width
*(1+(lattr
!=LATTR_NORM
));
1327 static int *IpDx
= 0, IpDxLEN
= 0;;
1329 if (len
>IpDxLEN
|| IpDx
[0] != fnt_width
) {
1333 IpDx
= smalloc((len
+16)*sizeof(int));
1336 for(i
=0; i
<IpDxLEN
; i
++)
1337 IpDx
[i
] = fnt_width
;
1343 if (attr
& ATTR_ACTCURS
) {
1344 attr
&= (bold_mode
== BOLD_COLOURS ?
0x300200 : 0x300300);
1345 attr
^= ATTR_CUR_XOR
;
1349 if (cfg
.vtmode
== VT_OEMONLY
)
1353 * Map high-half characters in order to approximate ISO using
1354 * OEM character set. No characters are missing if the OEM codepage
1357 if (nfont
& FONT_OEM
) {
1359 for (i
=0; i
<len
; i
++)
1360 if (text
[i
] >= '\xA0' && text
[i
] <= '\xFF') {
1362 /* This is CP850 ... perfect translation */
1363 static const char oemhighhalf
[] =
1364 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1365 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1366 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1367 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1368 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1369 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1370 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1371 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1372 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1373 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1374 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1375 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1378 /* This is CP437 ... junk translation */
1379 static const unsigned char oemhighhalf
[] = {
1380 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1381 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1382 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1383 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1384 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1385 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1386 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1387 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1388 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1389 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1390 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1391 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1394 text
[i
] = oemhighhalf
[(unsigned char)text
[i
] - 0xA0];
1398 if (attr
& ATTR_GBCHR
) {
1401 * GB mapping: map # to pound, and everything else stays
1404 for (i
=0; i
<len
; i
++)
1406 text
[i
] = cfg
.vtmode
== VT_OEMONLY ?
'\x9C' : '\xA3';
1407 } else if (attr
& ATTR_LINEDRW
) {
1410 static const char poorman
[] =
1411 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1414 static const char oemmap_437
[] =
1415 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1416 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1419 static const char oemmap_850
[] =
1420 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1421 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1423 /* Poor windows font ... eg: windows courier */
1424 static const char oemmap
[] =
1425 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1426 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1429 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1430 * VT100 line drawing chars; everything else stays normal.
1432 switch (cfg
.vtmode
) {
1434 for (i
=0; i
<len
; i
++)
1435 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1436 text
[i
] += '\x01' - '\x60';
1439 /* Make sure we actually have an OEM font */
1440 if (fonts
[nfont
|FONT_OEM
]) {
1443 for (i
=0; i
<len
; i
++)
1444 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1445 text
[i
] = oemmap
[(unsigned char)text
[i
] - 0x60];
1449 for (i
=0; i
<len
; i
++)
1450 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1451 text
[i
] = poorman
[(unsigned char)text
[i
] - 0x60];
1456 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
1457 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
1458 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
1460 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
1461 nfont
|= FONT_UNDERLINE
;
1464 if (nfont
&FONT_UNDERLINE
)
1465 force_manual_underline
= 1;
1466 /* Don't do the same for manual bold, it could be bad news. */
1468 nfont
&= ~(FONT_BOLD
|FONT_UNDERLINE
);
1470 if (attr
& ATTR_REVERSE
) {
1471 t
= nfg
; nfg
= nbg
; nbg
= t
;
1473 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
1475 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
1479 SelectObject (hdc
, fonts
[nfont
]);
1480 SetTextColor (hdc
, fg
);
1481 SetBkColor (hdc
, bg
);
1482 SetBkMode (hdc
, OPAQUE
);
1485 line_box
.right
= x
+fnt_width
*len
;
1486 line_box
.bottom
= y
+font_height
;
1487 ExtTextOut (hdc
, x
, y
, ETO_CLIPPED
|ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
1488 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
1489 SetBkMode (hdc
, TRANSPARENT
);
1491 /* GRR: This draws the character outside it's box and can leave
1492 * 'droppings' even with the clip box! I suppose I could loop it
1493 * one character at a time ... yuk.
1495 * Or ... I could do a test print with "W", and use +1 or -1 for this
1496 * shift depending on if the leftmost column is blank...
1498 ExtTextOut (hdc
, x
-1, y
, ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
1500 if (force_manual_underline
||
1501 (und_mode
== UND_LINE
&& (attr
& ATTR_UNDER
))) {
1503 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, fg
));
1504 MoveToEx (hdc
, x
, y
+descent
, NULL
);
1505 LineTo (hdc
, x
+len
*fnt_width
, y
+descent
);
1506 oldpen
= SelectObject (hdc
, oldpen
);
1507 DeleteObject (oldpen
);
1509 if (attr
& ATTR_PASCURS
) {
1512 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
1513 pts
[2].x
= pts
[3].x
= x
+fnt_width
-1;
1514 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
1515 pts
[1].y
= pts
[2].y
= y
+font_height
-1;
1516 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
1517 Polyline (hdc
, pts
, 5);
1518 oldpen
= SelectObject (hdc
, oldpen
);
1519 DeleteObject (oldpen
);
1523 static int check_compose(int first
, int second
) {
1525 static char * composetbl
[] = {
1526 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1527 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1528 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1529 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1530 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1531 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1532 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1533 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1534 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1535 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1536 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1537 "\"uü", "'yý", "htþ", "\"yÿ",
1541 static int recurse
= 0;
1548 sprintf(buf
, "cc(%d,%d)", first
, second
);
1553 for(c
=composetbl
; *c
; c
++) {
1554 if( (*c
)[0] == first
&& (*c
)[1] == second
)
1556 return (*c
)[2] & 0xFF;
1563 nc
= check_compose(second
, first
);
1565 nc
= check_compose(toupper(first
), toupper(second
));
1567 nc
= check_compose(toupper(second
), toupper(first
));
1575 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1576 * codes. Returns number of bytes used or zero to drop the message
1577 * or -1 to forward the message to windows.
1579 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
, unsigned char *output
) {
1581 int scan
, left_alt
= 0, key_down
, shift_state
;
1583 unsigned char * p
= output
;
1585 static WORD keys
[3];
1586 static int compose_state
= 0;
1587 static int compose_char
= 0;
1588 static WPARAM compose_key
= 0;
1590 r
= GetKeyboardState(keystate
);
1591 if (!r
) memset(keystate
, 0, sizeof(keystate
));
1594 /* Note if AltGr was pressed and if it was used as a compose key */
1595 if (wParam
== VK_MENU
&& (HIWORD(lParam
)&KF_EXTENDED
))
1597 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
1598 if (!compose_state
) compose_key
= wParam
;
1600 if (wParam
== VK_APPS
&& !compose_state
)
1601 compose_key
= wParam
;
1603 if (wParam
== compose_key
)
1605 if (compose_state
== 0 && (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
1607 else if (compose_state
== 1 && (HIWORD(lParam
)&KF_UP
))
1612 else if (compose_state
==1 && wParam
!= VK_CONTROL
)
1615 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1616 if ( (cfg
.funky_type
== 0 || (cfg
.funky_type
== 1 && app_keypad_keys
))
1617 && wParam
==VK_NUMLOCK
&& !(keystate
[VK_SHIFT
]&0x80)) {
1619 wParam
= VK_EXECUTE
;
1621 /* UnToggle NUMLock */
1622 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
1623 keystate
[VK_NUMLOCK
] ^= 1;
1626 /* And write back the 'adjusted' state */
1627 SetKeyboardState (keystate
);
1630 /* Disable Auto repeat if required */
1631 if (repeat_off
&& (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
)
1634 if ((HIWORD(lParam
)&KF_ALTDOWN
) && (keystate
[VK_RMENU
]&0x80) == 0)
1637 key_down
= ((HIWORD(lParam
)&KF_UP
)==0);
1639 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1640 if (left_alt
&& (keystate
[VK_CONTROL
]&0x80))
1641 keystate
[VK_MENU
] = 0;
1643 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
1644 shift_state
= ((keystate
[VK_SHIFT
]&0x80)!=0)
1645 + ((keystate
[VK_CONTROL
]&0x80)!=0)*2;
1648 * Record that we pressed key so the scroll window can be reset, but
1649 * be careful to avoid Shift-UP/Down
1651 if( wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
1655 /* Make sure we're not pasting */
1656 if (key_down
) term_nopaste();
1658 if (compose_state
>1 && left_alt
) compose_state
= 0;
1660 /* Sanitize the number pad if not using a PC NumPad */
1661 if( left_alt
|| (app_keypad_keys
&& cfg
.funky_type
!= 2)
1662 || cfg
.nethack_keypad
|| compose_state
)
1664 if ((HIWORD(lParam
)&KF_EXTENDED
) == 0)
1669 case VK_INSERT
: nParam
= VK_NUMPAD0
; break;
1670 case VK_END
: nParam
= VK_NUMPAD1
; break;
1671 case VK_DOWN
: nParam
= VK_NUMPAD2
; break;
1672 case VK_NEXT
: nParam
= VK_NUMPAD3
; break;
1673 case VK_LEFT
: nParam
= VK_NUMPAD4
; break;
1674 case VK_CLEAR
: nParam
= VK_NUMPAD5
; break;
1675 case VK_RIGHT
: nParam
= VK_NUMPAD6
; break;
1676 case VK_HOME
: nParam
= VK_NUMPAD7
; break;
1677 case VK_UP
: nParam
= VK_NUMPAD8
; break;
1678 case VK_PRIOR
: nParam
= VK_NUMPAD9
; break;
1679 case VK_DELETE
: nParam
= VK_DECIMAL
; break;
1683 if (keystate
[VK_NUMLOCK
]&1) shift_state
|= 1;
1689 /* If a key is pressed and AltGr is not active */
1690 if (key_down
&& (keystate
[VK_RMENU
]&0x80) == 0 && !compose_state
)
1692 /* Okay, prepare for most alts then ...*/
1693 if (left_alt
) *p
++ = '\033';
1695 /* Lets see if it's a pattern we know all about ... */
1696 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
1697 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
1700 if (wParam
== VK_NEXT
&& shift_state
== 1) {
1701 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
1704 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
1707 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
1708 SendMessage (hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
1712 /* Nethack keypad */
1713 if (cfg
.nethack_keypad
&& !left_alt
) {
1715 case VK_NUMPAD1
: *p
++ = shift_state ?
'B': 'b'; return p
-output
;
1716 case VK_NUMPAD2
: *p
++ = shift_state ?
'J': 'j'; return p
-output
;
1717 case VK_NUMPAD3
: *p
++ = shift_state ?
'N': 'n'; return p
-output
;
1718 case VK_NUMPAD4
: *p
++ = shift_state ?
'H': 'h'; return p
-output
;
1719 case VK_NUMPAD5
: *p
++ = shift_state ?
'.': '.'; return p
-output
;
1720 case VK_NUMPAD6
: *p
++ = shift_state ?
'L': 'l'; return p
-output
;
1721 case VK_NUMPAD7
: *p
++ = shift_state ?
'Y': 'y'; return p
-output
;
1722 case VK_NUMPAD8
: *p
++ = shift_state ?
'K': 'k'; return p
-output
;
1723 case VK_NUMPAD9
: *p
++ = shift_state ?
'U': 'u'; return p
-output
;
1727 /* Application Keypad */
1731 if ( cfg
.funky_type
== 0 ||
1732 ( cfg
.funky_type
== 1 && app_keypad_keys
)) switch(wParam
) {
1733 case VK_EXECUTE
: xkey
= 'P'; break;
1734 case VK_DIVIDE
: xkey
= 'Q'; break;
1735 case VK_MULTIPLY
:xkey
= 'R'; break;
1736 case VK_SUBTRACT
:xkey
= 'S'; break;
1738 if(app_keypad_keys
) switch(wParam
) {
1739 case VK_NUMPAD0
: xkey
= 'p'; break;
1740 case VK_NUMPAD1
: xkey
= 'q'; break;
1741 case VK_NUMPAD2
: xkey
= 'r'; break;
1742 case VK_NUMPAD3
: xkey
= 's'; break;
1743 case VK_NUMPAD4
: xkey
= 't'; break;
1744 case VK_NUMPAD5
: xkey
= 'u'; break;
1745 case VK_NUMPAD6
: xkey
= 'v'; break;
1746 case VK_NUMPAD7
: xkey
= 'w'; break;
1747 case VK_NUMPAD8
: xkey
= 'x'; break;
1748 case VK_NUMPAD9
: xkey
= 'y'; break;
1750 case VK_DECIMAL
: xkey
= 'n'; break;
1751 case VK_ADD
: if(shift_state
) xkey
= 'm';
1755 if (HIWORD(lParam
)&KF_EXTENDED
)
1763 if (xkey
>='P' && xkey
<='S')
1764 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
1766 p
+= sprintf((char *)p
, "\x1B?%c", xkey
);
1769 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
1774 if (wParam
== VK_BACK
&& shift_state
== 0 ) /* Backspace */
1776 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
1779 if (wParam
== VK_TAB
&& shift_state
== 1 ) /* Shift tab */
1781 *p
++ = 0x1B; *p
++ = '['; *p
++ = 'Z'; return p
- output
;
1783 if (wParam
== VK_SPACE
&& shift_state
== 2 ) /* Ctrl-Space */
1785 *p
++ = 0; return p
- output
;
1787 if (wParam
== VK_SPACE
&& shift_state
== 3 ) /* Ctrl-Shift-Space */
1789 *p
++ = 160; return p
- output
;
1791 if (wParam
== VK_CANCEL
&& shift_state
== 2 ) /* Ctrl-Break */
1793 *p
++ = 3; return p
- output
;
1795 /* Control-2 to Control-8 are special */
1796 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8')
1798 *p
++ = "\000\033\034\035\036\037\177"[wParam
-'2'];
1801 if (shift_state
== 2 && wParam
== 0xBD) {
1805 if (shift_state
== 2 && wParam
== 0xDF) {
1809 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
1810 *p
++ = '\r'; *p
++ = '\n';
1815 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1816 * for integer decimal nn.)
1818 * We also deal with the weird ones here. Linux VCs replace F1
1819 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1820 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1825 case VK_F1
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11); break;
1826 case VK_F2
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12); break;
1827 case VK_F3
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13); break;
1828 case VK_F4
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14); break;
1829 case VK_F5
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15); break;
1830 case VK_F6
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17); break;
1831 case VK_F7
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18); break;
1832 case VK_F8
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19); break;
1833 case VK_F9
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20); break;
1834 case VK_F10
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21); break;
1835 case VK_F11
: code
= 23; break;
1836 case VK_F12
: code
= 24; break;
1837 case VK_F13
: code
= 25; break;
1838 case VK_F14
: code
= 26; break;
1839 case VK_F15
: code
= 28; break;
1840 case VK_F16
: code
= 29; break;
1841 case VK_F17
: code
= 31; break;
1842 case VK_F18
: code
= 32; break;
1843 case VK_F19
: code
= 33; break;
1844 case VK_F20
: code
= 34; break;
1845 case VK_HOME
: code
= 1; break;
1846 case VK_INSERT
: code
= 2; break;
1847 case VK_DELETE
: code
= 3; break;
1848 case VK_END
: code
= 4; break;
1849 case VK_PRIOR
: code
= 5; break;
1850 case VK_NEXT
: code
= 6; break;
1852 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
1853 p
+= sprintf((char *)p
, "\x1B[[%c", code
+ 'A' - 11);
1856 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
1857 p
+= sprintf((char *)p
, "\x1BO%c", code
+ 'P' - 11);
1860 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
1861 p
+= sprintf((char *)p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
1865 p
+= sprintf((char *)p
, "\x1B[%d~", code
);
1870 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
1871 * some reason seems to send VK_CLEAR to Windows...).
1876 case VK_UP
: xkey
= 'A'; break;
1877 case VK_DOWN
: xkey
= 'B'; break;
1878 case VK_RIGHT
: xkey
= 'C'; break;
1879 case VK_LEFT
: xkey
= 'D'; break;
1880 case VK_CLEAR
: xkey
= 'G'; break;
1885 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
1886 else if (app_cursor_keys
)
1887 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
1889 p
+= sprintf((char *)p
, "\x1B[%c", xkey
);
1895 /* Okay we've done everything interesting; let windows deal with
1896 * the boring stuff */
1898 BOOL capsOn
=keystate
[VK_CAPITAL
] !=0;
1900 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
1901 if(cfg
.xlat_capslockcyr
)
1902 keystate
[VK_CAPITAL
] = 0;
1904 r
= ToAscii (wParam
, scan
, keystate
, keys
, 0);
1910 unsigned char ch
= (unsigned char)keys
[i
];
1912 if (compose_state
==2 && (ch
&0x80) == 0 && ch
>' ') {
1917 if (compose_state
==3 && (ch
&0x80) == 0 && ch
>' ') {
1921 if ((nc
=check_compose(compose_char
,ch
)) == -1)
1926 *p
++ = xlat_kbd2tty((unsigned char)nc
);
1932 if( left_alt
&& key_down
) *p
++ = '\033';
1938 ch
= xlat_latkbd2win(ch
);
1939 *p
++ = xlat_kbd2tty(ch
);
1943 /* This is so the ALT-Numpad and dead keys work correctly. */
1950 /* This stops ALT press-release doing a 'COMMAND MENU' function */
1952 if (message
== WM_SYSKEYUP
&& wParam
== VK_MENU
)
1954 keystate
[VK_MENU
] = 0;
1962 void set_title (char *title
) {
1963 sfree (window_name
);
1964 window_name
= smalloc(1+strlen(title
));
1965 strcpy (window_name
, title
);
1966 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
1967 SetWindowText (hwnd
, title
);
1970 void set_icon (char *title
) {
1972 icon_name
= smalloc(1+strlen(title
));
1973 strcpy (icon_name
, title
);
1974 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
1975 SetWindowText (hwnd
, title
);
1978 void set_sbar (int total
, int start
, int page
) {
1981 if (!cfg
.scrollbar
) return;
1983 si
.cbSize
= sizeof(si
);
1984 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
1986 si
.nMax
= total
- 1;
1990 SetScrollInfo (hwnd
, SB_VERT
, &si
, TRUE
);
1993 Context
get_ctx(void) {
1998 SelectPalette (hdc
, pal
, FALSE
);
2004 void free_ctx (Context ctx
) {
2005 SelectPalette (ctx
, GetStockObject (DEFAULT_PALETTE
), FALSE
);
2006 ReleaseDC (hwnd
, ctx
);
2009 static void real_palette_set (int n
, int r
, int g
, int b
) {
2011 logpal
->palPalEntry
[n
].peRed
= r
;
2012 logpal
->palPalEntry
[n
].peGreen
= g
;
2013 logpal
->palPalEntry
[n
].peBlue
= b
;
2014 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
2015 colours
[n
] = PALETTERGB(r
, g
, b
);
2016 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2018 colours
[n
] = RGB(r
, g
, b
);
2021 void palette_set (int n
, int r
, int g
, int b
) {
2022 static const int first
[21] = {
2023 0, 2, 4, 6, 8, 10, 12, 14,
2024 1, 3, 5, 7, 9, 11, 13, 15,
2027 real_palette_set (first
[n
], r
, g
, b
);
2029 real_palette_set (first
[n
]+1, r
, g
, b
);
2031 HDC hdc
= get_ctx();
2032 UnrealizeObject (pal
);
2033 RealizePalette (hdc
);
2038 void palette_reset (void) {
2041 for (i
= 0; i
< NCOLOURS
; i
++) {
2043 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
2044 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
2045 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
2046 logpal
->palPalEntry
[i
].peFlags
= 0;
2047 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
2048 defpal
[i
].rgbtGreen
,
2049 defpal
[i
].rgbtBlue
);
2051 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
2052 defpal
[i
].rgbtGreen
,
2053 defpal
[i
].rgbtBlue
);
2058 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2060 RealizePalette (hdc
);
2065 void write_clip (void *data
, int len
) {
2069 clipdata
= GlobalAlloc (GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
2072 lock
= GlobalLock (clipdata
);
2075 memcpy (lock
, data
, len
);
2076 ((unsigned char *) lock
) [len
] = 0;
2077 GlobalUnlock (clipdata
);
2079 SendMessage (hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
2080 if (OpenClipboard (hwnd
)) {
2082 SetClipboardData (CF_TEXT
, clipdata
);
2085 GlobalFree (clipdata
);
2086 SendMessage (hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
2089 void get_clip (void **p
, int *len
) {
2090 static HGLOBAL clipdata
= NULL
;
2094 GlobalUnlock (clipdata
);
2098 if (OpenClipboard (NULL
)) {
2099 clipdata
= GetClipboardData (CF_TEXT
);
2102 *p
= GlobalLock (clipdata
);
2116 * Move `lines' lines from position `from' to position `to' in the
2119 void optimised_move (int to
, int from
, int lines
) {
2123 min
= (to
< from ? to
: from
);
2124 max
= to
+ from
- min
;
2126 r
.left
= 0; r
.right
= cols
* font_width
;
2127 r
.top
= min
* font_height
; r
.bottom
= (max
+lines
) * font_height
;
2128 ScrollWindow (hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
2132 * Print a message box and perform a fatal exit.
2134 void fatalbox(char *fmt
, ...) {
2139 vsprintf(stuff
, fmt
, ap
);
2141 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
2148 void beep(int errorbeep
) {
2149 static long last_beep
= 0;
2150 long now
, beep_diff
;
2152 now
= GetTickCount();
2153 beep_diff
= now
-last_beep
;
2155 /* Make sure we only respond to one beep per packet or so */
2156 if (beep_diff
>=0 && beep_diff
<50)
2160 MessageBeep(MB_ICONHAND
);
2164 last_beep
= GetTickCount();