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)
39 #define WM_IGNORE_KEYMENU (WM_XUSER + 3)
41 static LRESULT CALLBACK
WndProc (HWND
, UINT
, WPARAM
, LPARAM
);
42 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
, unsigned char *output
);
43 static void cfgtopalette(void);
44 static void init_palette(void);
45 static void init_fonts(int);
47 static int extra_width
, extra_height
;
49 static int pending_netevent
= 0;
50 static WPARAM pend_netevent_wParam
= 0;
51 static LPARAM pend_netevent_lParam
= 0;
52 static void enact_pending_netevent(void);
56 #define FONT_UNDERLINE 2
57 #define FONT_BOLDUND 3
59 #define FONT_OEMBOLD 5
60 #define FONT_OEMBOLDUND 6
62 static HFONT fonts
[8];
63 static int font_needs_hand_underlining
;
65 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
73 static COLORREF colours
[NCOLOURS
];
75 static LPLOGPALETTE logpal
;
76 static RGBTRIPLE defpal
[NCOLOURS
];
80 static int dbltime
, lasttime
, lastact
;
81 static Mouse_Button lastbtn
;
83 static char *window_name
, *icon_name
;
85 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
) {
86 static char appname
[] = "PuTTY";
91 int guess_width
, guess_height
;
94 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
96 winsock_ver
= MAKEWORD(1, 1);
97 if (WSAStartup(winsock_ver
, &wsadata
)) {
98 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
99 MB_OK
| MB_ICONEXCLAMATION
);
102 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
103 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
104 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
108 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
110 InitCommonControls();
113 * Process the command line.
118 default_protocol
= DEFAULT_PROTOCOL
;
119 default_port
= DEFAULT_PORT
;
124 while (*p
&& isspace(*p
)) p
++;
127 * Process command line options first. Yes, this can be
128 * done better, and it will be as soon as I have the
132 char *q
= p
+ strcspn(p
, " \t");
135 tolower(p
[0]) == 's' &&
136 tolower(p
[1]) == 's' &&
137 tolower(p
[2]) == 'h') {
138 default_protocol
= cfg
.protocol
= PROT_SSH
;
139 default_port
= cfg
.port
= 22;
140 } else if (q
== p
+ 3 &&
141 tolower(p
[0]) == 'l' &&
142 tolower(p
[1]) == 'o' &&
143 tolower(p
[2]) == 'g') {
144 logfile
= "putty.log";
146 p
= q
+ strspn(q
, " \t");
150 * An initial @ means to activate a saved session.
154 if (!*cfg
.host
&& !do_config()) {
158 } else if (*p
== '&') {
160 * An initial & means we've been given a command line
161 * containing the hex value of a HANDLE for a file
162 * mapping object, which we must then extract as a
167 if (sscanf(p
+1, "%p", &filemap
) == 1 &&
168 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
169 0, 0, sizeof(Config
))) != NULL
) {
172 CloseHandle(filemap
);
173 } else if (!do_config()) {
180 * If the hostname starts with "telnet:", set the
181 * protocol to Telnet and process the string as a
184 if (!strncmp(q
, "telnet:", 7)) {
188 if (q
[0] == '/' && q
[1] == '/')
190 cfg
.protocol
= PROT_TELNET
;
192 while (*p
&& *p
!= ':' && *p
!= '/') p
++;
200 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
201 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
203 while (*p
&& !isspace(*p
)) p
++;
206 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
207 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
208 while (*p
&& isspace(*p
)) p
++;
223 * Select protocol. This is farmed out into a table in a
224 * separate file to enable an ssh-free variant.
229 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
230 if (backends
[i
].protocol
== cfg
.protocol
) {
231 back
= backends
[i
].backend
;
235 MessageBox(NULL
, "Unsupported protocol number found",
236 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
242 ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
246 wndclass
.lpfnWndProc
= WndProc
;
247 wndclass
.cbClsExtra
= 0;
248 wndclass
.cbWndExtra
= 0;
249 wndclass
.hInstance
= inst
;
250 wndclass
.hIcon
= LoadIcon (inst
,
251 MAKEINTRESOURCE(IDI_MAINICON
));
252 wndclass
.hCursor
= LoadCursor (NULL
, IDC_IBEAM
);
253 wndclass
.hbrBackground
= GetStockObject (BLACK_BRUSH
);
254 wndclass
.lpszMenuName
= NULL
;
255 wndclass
.lpszClassName
= appname
;
257 RegisterClass (&wndclass
);
262 savelines
= cfg
.savelines
;
268 * Guess some defaults for the window size. This all gets
269 * updated later, so we don't really care too much. However, we
270 * do want the font width/height guesses to correspond to a
271 * large font rather than a small one...
278 term_size (cfg
.height
, cfg
.width
, cfg
.savelines
);
279 guess_width
= extra_width
+ font_width
* cols
;
280 guess_height
= extra_height
+ font_height
* rows
;
283 HWND w
= GetDesktopWindow();
284 GetWindowRect (w
, &r
);
285 if (guess_width
> r
.right
- r
.left
)
286 guess_width
= r
.right
- r
.left
;
287 if (guess_height
> r
.bottom
- r
.top
)
288 guess_height
= r
.bottom
- r
.top
;
292 int winmode
= WS_OVERLAPPEDWINDOW
|WS_VSCROLL
;
293 if (!cfg
.scrollbar
) winmode
&= ~(WS_VSCROLL
);
294 if (cfg
.locksize
) winmode
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
295 hwnd
= CreateWindow (appname
, appname
,
297 CW_USEDEFAULT
, CW_USEDEFAULT
,
298 guess_width
, guess_height
,
299 NULL
, NULL
, inst
, NULL
);
303 * Initialise the fonts, simultaneously correcting the guesses
304 * for font_{width,height}.
306 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
311 * Correct the guesses for extra_{width,height}.
315 GetWindowRect (hwnd
, &wr
);
316 GetClientRect (hwnd
, &cr
);
317 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
318 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
322 * Resize the window, now we know what size we _really_ want it
325 guess_width
= extra_width
+ font_width
* cols
;
326 guess_height
= extra_height
+ font_height
* rows
;
327 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
328 SetWindowPos (hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
329 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
332 * Initialise the scroll bar.
337 si
.cbSize
= sizeof(si
);
338 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
343 SetScrollInfo (hwnd
, SB_VERT
, &si
, FALSE
);
347 * Start up the telnet connection.
354 error
= back
->init (hwnd
, cfg
.host
, cfg
.port
, &realhost
);
356 sprintf(msg
, "Unable to open connection:\n%s", error
);
357 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
360 window_name
= icon_name
= NULL
;
361 sprintf(msg
, "%s - PuTTY", realhost
);
366 session_closed
= FALSE
;
369 * Set up the input and output buffers.
372 outbuf_reap
= outbuf_head
= 0;
375 * Prepare the mouse handler.
377 lastact
= MA_NOTHING
;
378 lastbtn
= MB_NOTHING
;
379 dbltime
= GetDoubleClickTime();
382 * Set up the session-control options on the system menu.
385 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
389 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
390 if (cfg
.protocol
== PROT_TELNET
) {
392 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
393 AppendMenu (p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
394 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
395 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
396 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
397 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
398 AppendMenu (p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
399 AppendMenu (p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
400 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
401 AppendMenu (p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
402 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
403 AppendMenu (p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
404 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
405 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
406 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
407 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
408 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
, "Telnet Command");
409 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
411 AppendMenu (m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
412 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
413 AppendMenu (m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session");
414 AppendMenu (m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
417 for (i
= 1 ; i
< ((nsessions
< 256) ? nsessions
: 256) ; i
++)
418 AppendMenu (s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
) , sessions
[i
]);
419 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
420 AppendMenu (m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings");
421 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
422 AppendMenu (m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
423 AppendMenu (m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
424 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
425 AppendMenu (m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
429 * Finally show the window!
431 ShowWindow (hwnd
, show
);
434 * Set the palette up.
440 has_focus
= (GetForegroundWindow() == hwnd
);
444 int timer_id
= 0, long_timer
= 0;
446 while (GetMessage (&msg
, NULL
, 0, 0) == 1) {
447 /* Sometimes DispatchMessage calls routines that use their own
448 * GetMessage loop, setup this timer so we get some control back.
450 * Also call term_update() from the timer so that if the host
451 * is sending data flat out we still do redraws.
453 if(timer_id
&& long_timer
) {
454 KillTimer(hwnd
, timer_id
);
455 long_timer
= timer_id
= 0;
458 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
459 DispatchMessage (&msg
);
461 /* This is too fast, but I'll leave it for now 'cause it shows
462 * how often term_update is called (far too often at times!)
466 /* Send the paste buffer if there's anything to send */
469 /* If there's nothing new in the queue then we can do everything
470 * we've delayed, reading the socket, writing, and repainting
473 if (!PeekMessage (&msg
, NULL
, 0, 0, PM_NOREMOVE
)) {
474 if (pending_netevent
) {
475 enact_pending_netevent();
480 if (!PeekMessage (&msg
, NULL
, 0, 0, PM_NOREMOVE
)) {
482 KillTimer(hwnd
, timer_id
);
489 timer_id
= SetTimer(hwnd
, 1, 2000, NULL
);
490 else if (cfg
.blinktext
)
491 timer_id
= SetTimer(hwnd
, 1, 250, NULL
);
493 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
506 DeleteObject(fonts
[i
]);
513 if (cfg
.protocol
== PROT_SSH
) {
524 * Actually do the job requested by a WM_NETEVENT
526 static void enact_pending_netevent(void) {
528 static int reentering
= 0;
531 return; /* don't unpend the pending */
533 pending_netevent
= FALSE
;
536 i
= back
->msg (pend_netevent_wParam
, pend_netevent_lParam
);
541 switch (WSABASEERR
+ (-i
) % 10000) {
543 sprintf(buf
, "Connection reset by peer");
546 sprintf(buf
, "Unexpected network error %d", -i
);
549 MessageBox(hwnd
, buf
, "PuTTY Fatal Error",
550 MB_ICONERROR
| MB_OK
);
553 if (cfg
.close_on_exit
)
556 session_closed
= TRUE
;
557 MessageBox(hwnd
, "Connection closed by remote host",
558 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
559 SetWindowText (hwnd
, "PuTTY (inactive)");
565 * Copy the colour palette from the configuration data into defpal.
566 * This is non-trivial because the colour indices are different.
568 static void cfgtopalette(void) {
570 static const int ww
[] = {
571 6, 7, 8, 9, 10, 11, 12, 13,
572 14, 15, 16, 17, 18, 19, 20, 21,
573 0, 1, 2, 3, 4, 4, 5, 5
576 for (i
=0; i
<24; i
++) {
578 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
579 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
580 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
585 * Set up the colour palette.
587 static void init_palette(void) {
589 HDC hdc
= GetDC (hwnd
);
591 if (cfg
.try_palette
&&
592 GetDeviceCaps (hdc
, RASTERCAPS
) & RC_PALETTE
) {
593 logpal
= smalloc(sizeof(*logpal
)
594 - sizeof(logpal
->palPalEntry
)
595 + NCOLOURS
* sizeof(PALETTEENTRY
));
596 logpal
->palVersion
= 0x300;
597 logpal
->palNumEntries
= NCOLOURS
;
598 for (i
= 0; i
< NCOLOURS
; i
++) {
599 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
600 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
601 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
602 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
604 pal
= CreatePalette (logpal
);
606 SelectPalette (hdc
, pal
, FALSE
);
607 RealizePalette (hdc
);
608 SelectPalette (hdc
, GetStockObject (DEFAULT_PALETTE
),
612 ReleaseDC (hwnd
, hdc
);
615 for (i
=0; i
<NCOLOURS
; i
++)
616 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
620 for(i
=0; i
<NCOLOURS
; i
++)
621 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
627 * Initialise all the fonts we will need. There may be as many as
628 * eight or as few as one. We also:
630 * - check the font width and height, correcting our guesses if
633 * - verify that the bold font is the same width as the ordinary
634 * one, and engage shadow bolding if not.
636 * - verify that the underlined font is the same width as the
637 * ordinary one (manual underlining by means of line drawing can
638 * be done in a pinch).
640 static void init_fonts(int pick_width
) {
645 int fw_dontcare
, fw_bold
;
654 if (cfg
.fontisbold
) {
655 fw_dontcare
= FW_BOLD
;
658 fw_dontcare
= FW_DONTCARE
;
664 font_height
= cfg
.fontheight
;
665 font_width
= pick_width
;
668 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
669 c, OUT_DEFAULT_PRECIS, \
670 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
671 FIXED_PITCH | FF_DONTCARE, cfg.font)
673 if (cfg
.vtmode
!= VT_OEMONLY
) {
674 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
676 SelectObject (hdc
, fonts
[FONT_NORMAL
]);
677 GetTextMetrics(hdc
, &tm
);
678 font_height
= tm
.tmHeight
;
679 font_width
= tm
.tmAveCharWidth
;
681 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
684 * Some fonts, e.g. 9-pt Courier, draw their underlines
685 * outside their character cell. We successfully prevent
686 * screen corruption by clipping the text output, but then
687 * we lose the underline completely. Here we try to work
688 * out whether this is such a font, and if it is, we set a
689 * flag that causes underlines to be drawn by hand.
691 * Having tried other more sophisticated approaches (such
692 * as examining the TEXTMETRIC structure or requesting the
693 * height of a string), I think we'll do this the brute
694 * force way: we create a small bitmap, draw an underlined
695 * space on it, and test to see whether any pixels are
696 * foreground-coloured. (Since we expect the underline to
697 * go all the way across the character cell, we only search
698 * down a single column of the bitmap, half way across.)
702 HBITMAP und_bm
, und_oldbm
;
706 und_dc
= CreateCompatibleDC(hdc
);
707 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
708 und_oldbm
= SelectObject(und_dc
, und_bm
);
709 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
710 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
711 SetTextColor (und_dc
, RGB(255,255,255));
712 SetBkColor (und_dc
, RGB(0,0,0));
713 SetBkMode (und_dc
, OPAQUE
);
714 ExtTextOut (und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
716 for (i
= 0; i
< font_height
; i
++) {
717 c
= GetPixel(und_dc
, font_width
/2, i
);
721 SelectObject(und_dc
, und_oldbm
);
722 DeleteObject(und_bm
);
724 font_needs_hand_underlining
= !gotit
;
727 if (bold_mode
== BOLD_FONT
) {
728 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
729 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
732 if (cfg
.vtmode
== VT_OEMANSI
) {
733 f(FONT_OEM
, OEM_CHARSET
, fw_dontcare
, FALSE
);
734 f(FONT_OEMUND
, OEM_CHARSET
, fw_dontcare
, TRUE
);
736 if (bold_mode
== BOLD_FONT
) {
737 f(FONT_OEMBOLD
, OEM_CHARSET
, fw_bold
, FALSE
);
738 f(FONT_OEMBOLDUND
, OEM_CHARSET
, fw_bold
, TRUE
);
744 f(FONT_OEM
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
746 SelectObject (hdc
, fonts
[FONT_OEM
]);
747 GetTextMetrics(hdc
, &tm
);
748 font_height
= tm
.tmHeight
;
749 font_width
= tm
.tmAveCharWidth
;
751 f(FONT_OEMUND
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
753 if (bold_mode
== BOLD_FONT
) {
754 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
755 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
760 descent
= tm
.tmAscent
+ 1;
761 if (descent
>= font_height
)
762 descent
= font_height
- 1;
763 firstchar
= tm
.tmFirstChar
;
765 for (i
=0; i
<8; i
++) {
767 if (SelectObject (hdc
, fonts
[i
]) &&
768 GetTextMetrics(hdc
, &tm
) )
769 fsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
775 ReleaseDC (hwnd
, hdc
);
777 /* ... This is wrong in OEM only mode */
778 if (fsize
[FONT_UNDERLINE
] != fsize
[FONT_NORMAL
] ||
779 (bold_mode
== BOLD_FONT
&&
780 fsize
[FONT_BOLDUND
] != fsize
[FONT_BOLD
])) {
782 DeleteObject (fonts
[FONT_UNDERLINE
]);
783 if (bold_mode
== BOLD_FONT
)
784 DeleteObject (fonts
[FONT_BOLDUND
]);
787 if (bold_mode
== BOLD_FONT
&&
788 fsize
[FONT_BOLD
] != fsize
[FONT_NORMAL
]) {
789 bold_mode
= BOLD_SHADOW
;
790 DeleteObject (fonts
[FONT_BOLD
]);
791 if (und_mode
== UND_FONT
)
792 DeleteObject (fonts
[FONT_BOLDUND
]);
796 /* With the fascist font painting it doesn't matter if the linedraw font
797 * isn't exactly the right size anymore so we don't have to check this.
799 if (cfg
.vtmode
== VT_OEMANSI
&& fsize
[FONT_OEM
] != fsize
[FONT_NORMAL
] ) {
800 if( cfg
.fontcharset
== OEM_CHARSET
)
802 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
803 "different sizes. Using OEM-only mode instead",
804 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
805 cfg
.vtmode
= VT_OEMONLY
;
807 else if( firstchar
< ' ' )
809 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
810 "different sizes. Using XTerm mode instead",
811 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
812 cfg
.vtmode
= VT_XWINDOWS
;
816 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
817 "different sizes. Using ISO8859-1 mode instead",
818 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
819 cfg
.vtmode
= VT_POORMAN
;
824 DeleteObject (fonts
[i
]);
830 void request_resize (int w
, int h
, int refont
) {
833 /* If the window is maximized supress resizing attempts */
834 if(IsZoomed(hwnd
)) return;
837 /* Don't do this in OEMANSI, you may get disable messages */
838 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132)
839 && cfg
.vtmode
!= VT_OEMANSI
)
841 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132))
844 /* If font width too big for screen should we shrink the font more ? */
846 font_width
= ((font_width
*cols
+w
/2)/w
);
853 DeleteObject(fonts
[i
]);
855 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
857 init_fonts(font_width
);
861 static int first_time
= 1;
867 /* Get the size of the screen */
868 if (GetClientRect(GetDesktopWindow(),&ss
))
869 /* first_time = 0 */;
870 else { first_time
= 2; break; }
872 /* Make sure the values are sane */
873 width
= (ss
.right
-ss
.left
-extra_width
) / font_width
;
874 height
= (ss
.bottom
-ss
.top
-extra_height
) / font_height
;
876 if (w
>width
) w
=width
;
877 if (h
>height
) h
=height
;
883 width
= extra_width
+ font_width
* w
;
884 height
= extra_height
+ font_height
* h
;
886 SetWindowPos (hwnd
, NULL
, 0, 0, width
, height
,
887 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
888 SWP_NOMOVE
| SWP_NOZORDER
);
891 static void click (Mouse_Button b
, int x
, int y
) {
892 int thistime
= GetMessageTime();
894 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
895 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
896 lastact
== MA_2CLK ? MA_3CLK
:
897 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
902 if (lastact
!= MA_NOTHING
)
903 term_mouse (b
, lastact
, x
, y
);
907 static LRESULT CALLBACK
WndProc (HWND hwnd
, UINT message
,
908 WPARAM wParam
, LPARAM lParam
) {
910 static int ignore_size
= FALSE
;
911 static int ignore_clip
= FALSE
;
912 static int ignore_keymenu
= TRUE
;
913 static int just_reconfigged
= FALSE
;
917 if (pending_netevent
)
918 enact_pending_netevent();
926 if (!cfg
.warn_on_close
|| session_closed
||
927 MessageBox(hwnd
, "Are you sure you want to close this session?",
928 "PuTTY Exit Confirmation",
929 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
936 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
939 return 0; /* don't put up system menu on Alt */
952 PROCESS_INFORMATION pi
;
953 HANDLE filemap
= NULL
;
955 if (wParam
== IDM_DUPSESS
) {
957 * Allocate a file-mapping memory chunk for the
960 SECURITY_ATTRIBUTES sa
;
963 sa
.nLength
= sizeof(sa
);
964 sa
.lpSecurityDescriptor
= NULL
;
965 sa
.bInheritHandle
= TRUE
;
966 filemap
= CreateFileMapping((HANDLE
)0xFFFFFFFF,
973 p
= (Config
*)MapViewOfFile(filemap
,
975 0, 0, sizeof(Config
));
977 *p
= cfg
; /* structure copy */
981 sprintf(c
, "putty &%p", filemap
);
983 } else if (wParam
== IDM_SAVEDSESS
) {
984 char *session
= sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
985 cl
= malloc(16 + strlen(session
)); /* 8, but play safe */
987 cl
= NULL
; /* not a very important failure mode */
989 sprintf(cl
, "putty @%s", session
);
995 GetModuleFileName (NULL
, b
, sizeof(b
)-1);
997 si
.lpReserved
= NULL
;
1002 si
.lpReserved2
= NULL
;
1003 CreateProcess (b
, cl
, NULL
, NULL
, TRUE
,
1004 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1007 CloseHandle(filemap
);
1013 if (!do_reconfig(hwnd
))
1015 just_reconfigged
= TRUE
;
1020 DeleteObject(fonts
[i
]);
1022 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1023 und_mode
= UND_FONT
;
1026 /* Telnet will change local echo -> remote if the remote asks */
1027 if (cfg
.protocol
!= PROT_TELNET
)
1028 ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
1036 /* Enable or disable the scroll bar, etc */
1038 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1041 if (cfg
.scrollbar
) nflg
|= WS_VSCROLL
;
1042 else nflg
&= ~WS_VSCROLL
;
1044 nflg
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1046 nflg
|= (WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1052 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1053 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1054 SetWindowPos(hwnd
, NULL
, 0,0,0,0,
1055 SWP_NOACTIVATE
|SWP_NOCOPYBITS
|
1056 SWP_NOMOVE
|SWP_NOSIZE
| SWP_NOZORDER
|
1059 GetWindowRect (hwnd
, &wr
);
1060 GetClientRect (hwnd
, &cr
);
1061 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1062 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1066 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1067 InvalidateRect(hwnd
, NULL
, TRUE
);
1068 SetWindowPos (hwnd
, NULL
, 0, 0,
1069 extra_width
+ font_width
* cfg
.width
,
1070 extra_height
+ font_height
* cfg
.height
,
1071 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1072 SWP_NOMOVE
| SWP_NOZORDER
);
1073 if (IsIconic(hwnd
)) {
1074 SetWindowText (hwnd
,
1075 cfg
.win_name_always ? window_name
: icon_name
);
1084 case IDM_TEL_AYT
: back
->special (TS_AYT
); break;
1085 case IDM_TEL_BRK
: back
->special (TS_BRK
); break;
1086 case IDM_TEL_SYNCH
: back
->special (TS_SYNCH
); break;
1087 case IDM_TEL_EC
: back
->special (TS_EC
); break;
1088 case IDM_TEL_EL
: back
->special (TS_EL
); break;
1089 case IDM_TEL_GA
: back
->special (TS_GA
); break;
1090 case IDM_TEL_NOP
: back
->special (TS_NOP
); break;
1091 case IDM_TEL_ABORT
: back
->special (TS_ABORT
); break;
1092 case IDM_TEL_AO
: back
->special (TS_AO
); break;
1093 case IDM_TEL_IP
: back
->special (TS_IP
); break;
1094 case IDM_TEL_SUSP
: back
->special (TS_SUSP
); break;
1095 case IDM_TEL_EOR
: back
->special (TS_EOR
); break;
1096 case IDM_TEL_EOF
: back
->special (TS_EOF
); break;
1101 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1102 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1107 #define X_POS(l) ((int)(short)LOWORD(l))
1108 #define Y_POS(l) ((int)(short)HIWORD(l))
1110 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1111 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1113 case WM_LBUTTONDOWN
:
1114 click (MB_SELECT
, TO_CHR_X(X_POS(lParam
)),
1115 TO_CHR_Y(Y_POS(lParam
)));
1119 term_mouse (MB_SELECT
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1120 TO_CHR_Y(Y_POS(lParam
)));
1123 case WM_MBUTTONDOWN
:
1125 click (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1126 TO_CHR_X(X_POS(lParam
)),
1127 TO_CHR_Y(Y_POS(lParam
)));
1130 term_mouse (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1131 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1132 TO_CHR_Y(Y_POS(lParam
)));
1135 case WM_RBUTTONDOWN
:
1137 click (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1138 TO_CHR_X(X_POS(lParam
)),
1139 TO_CHR_Y(Y_POS(lParam
)));
1142 term_mouse (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1143 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1144 TO_CHR_Y(Y_POS(lParam
)));
1149 * Add the mouse position and message time to the random
1150 * number noise, if we're using ssh.
1152 if (cfg
.protocol
== PROT_SSH
)
1153 noise_ultralight(lParam
);
1155 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1157 if (wParam
& MK_LBUTTON
)
1159 else if (wParam
& MK_MBUTTON
)
1160 b
= cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
;
1162 b
= cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
;
1163 term_mouse (b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1164 TO_CHR_Y(Y_POS(lParam
)));
1167 case WM_IGNORE_CLIP
:
1168 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1170 case WM_IGNORE_KEYMENU
:
1171 ignore_keymenu
= wParam
; /* do or don't ignore SC_KEYMENU */
1173 case WM_DESTROYCLIPBOARD
:
1176 ignore_clip
= FALSE
;
1181 hdc
= BeginPaint (hwnd
, &p
);
1183 SelectPalette (hdc
, pal
, TRUE
);
1184 RealizePalette (hdc
);
1186 term_paint (hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1187 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1188 SelectObject (hdc
, GetStockObject(SYSTEM_FONT
));
1189 SelectObject (hdc
, GetStockObject(WHITE_PEN
));
1190 EndPaint (hwnd
, &p
);
1194 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1195 * but the only one that's likely to try to overload us is FD_READ.
1196 * This means buffering just one is fine.
1198 if (pending_netevent
)
1199 enact_pending_netevent();
1201 pending_netevent
= TRUE
;
1202 pend_netevent_wParam
=wParam
;
1203 pend_netevent_lParam
=lParam
;
1215 case WM_IGNORE_SIZE
:
1216 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1218 case WM_ENTERSIZEMOVE
:
1221 case WM_EXITSIZEMOVE
:
1226 int width
, height
, w
, h
, ew
, eh
;
1227 LPRECT r
= (LPRECT
)lParam
;
1229 width
= r
->right
- r
->left
- extra_width
;
1230 height
= r
->bottom
- r
->top
- extra_height
;
1231 w
= (width
+ font_width
/2) / font_width
; if (w
< 1) w
= 1;
1232 h
= (height
+ font_height
/2) / font_height
; if (h
< 1) h
= 1;
1233 UpdateSizeTip(hwnd
, w
, h
);
1234 ew
= width
- w
* font_width
;
1235 eh
= height
- h
* font_height
;
1237 if (wParam
== WMSZ_LEFT
||
1238 wParam
== WMSZ_BOTTOMLEFT
||
1239 wParam
== WMSZ_TOPLEFT
)
1245 if (wParam
== WMSZ_TOP
||
1246 wParam
== WMSZ_TOPRIGHT
||
1247 wParam
== WMSZ_TOPLEFT
)
1257 /* break; (never reached) */
1259 if (wParam
== SIZE_MINIMIZED
) {
1260 SetWindowText (hwnd
,
1261 cfg
.win_name_always ? window_name
: icon_name
);
1264 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1265 SetWindowText (hwnd
, window_name
);
1267 int width
, height
, w
, h
;
1268 #if 0 /* we have fixed this using WM_SIZING now */
1272 width
= LOWORD(lParam
);
1273 height
= HIWORD(lParam
);
1274 w
= width
/ font_width
; if (w
< 1) w
= 1;
1275 h
= height
/ font_height
; if (h
< 1) h
= 1;
1276 #if 0 /* we have fixed this using WM_SIZING now */
1277 ew
= width
- w
* font_width
;
1278 eh
= height
- h
* font_height
;
1279 if (ew
!= 0 || eh
!= 0) {
1281 GetWindowRect (hwnd
, &r
);
1282 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1283 SetWindowPos (hwnd
, NULL
, 0, 0,
1284 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1285 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1288 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1290 term_size (h
, w
, cfg
.savelines
);
1292 just_reconfigged
= FALSE
;
1295 ignore_size
= FALSE
;
1298 switch (LOWORD(wParam
)) {
1299 case SB_BOTTOM
: term_scroll(-1, 0); break;
1300 case SB_TOP
: term_scroll(+1, 0); break;
1301 case SB_LINEDOWN
: term_scroll (0, +1); break;
1302 case SB_LINEUP
: term_scroll (0, -1); break;
1303 case SB_PAGEDOWN
: term_scroll (0, +rows
/2); break;
1304 case SB_PAGEUP
: term_scroll (0, -rows
/2); break;
1305 case SB_THUMBPOSITION
: case SB_THUMBTRACK
:
1306 term_scroll (1, HIWORD(wParam
)); break;
1309 case WM_PALETTECHANGED
:
1310 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1311 HDC hdc
= get_ctx();
1313 if (RealizePalette (hdc
) > 0)
1319 case WM_QUERYNEWPALETTE
:
1321 HDC hdc
= get_ctx();
1323 if (RealizePalette (hdc
) > 0)
1335 * Add the scan code and keypress timing to the random
1336 * number noise, if we're using ssh.
1338 if (cfg
.protocol
== PROT_SSH
)
1339 noise_ultralight(lParam
);
1342 * We don't do TranslateMessage since it disassociates the
1343 * resulting CHAR message from the KEYDOWN that sparked it,
1344 * which we occasionally don't want. Instead, we process
1345 * KEYDOWN, and call the Win32 translator functions so that
1346 * we get the translations under _our_ control.
1349 unsigned char buf
[20];
1352 len
= TranslateKey (message
, wParam
, lParam
, buf
);
1354 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1355 ldisc
->send (buf
, len
);
1361 * Nevertheless, we are prepared to deal with WM_CHAR
1362 * messages, should they crop up. So if someone wants to
1363 * post the things to us as part of a macro manoeuvre,
1364 * we're ready to cope.
1367 char c
= xlat_kbd2tty((unsigned char)wParam
);
1368 ldisc
->send (&c
, 1);
1373 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1377 * Draw a line of text in the window, at given character
1378 * coordinates, in given attributes.
1380 * We are allowed to fiddle with the contents of `text'.
1382 void do_text (Context ctx
, int x
, int y
, char *text
, int len
,
1383 unsigned long attr
, int lattr
) {
1385 int nfg
, nbg
, nfont
;
1388 int force_manual_underline
= 0;
1389 int fnt_width
= font_width
*(1+(lattr
!=LATTR_NORM
));
1390 static int *IpDx
= 0, IpDxLEN
= 0;;
1392 if (len
>IpDxLEN
|| IpDx
[0] != fnt_width
) {
1396 IpDx
= smalloc((len
+16)*sizeof(int));
1399 for(i
=0; i
<IpDxLEN
; i
++)
1400 IpDx
[i
] = fnt_width
;
1406 if (attr
& ATTR_ACTCURS
) {
1407 attr
&= (bold_mode
== BOLD_COLOURS ?
0x300200 : 0x300300);
1408 attr
^= ATTR_CUR_XOR
;
1412 if (cfg
.vtmode
== VT_OEMONLY
)
1416 * Map high-half characters in order to approximate ISO using
1417 * OEM character set. No characters are missing if the OEM codepage
1420 if (nfont
& FONT_OEM
) {
1422 for (i
=0; i
<len
; i
++)
1423 if (text
[i
] >= '\xA0' && text
[i
] <= '\xFF') {
1425 /* This is CP850 ... perfect translation */
1426 static const char oemhighhalf
[] =
1427 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1428 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1429 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1430 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1431 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1432 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1433 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1434 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1435 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1436 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1437 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1438 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1441 /* This is CP437 ... junk translation */
1442 static const unsigned char oemhighhalf
[] = {
1443 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1444 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1445 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1446 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1447 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1448 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1449 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1450 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1451 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1452 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1453 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1454 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1457 text
[i
] = oemhighhalf
[(unsigned char)text
[i
] - 0xA0];
1461 if (attr
& ATTR_GBCHR
) {
1464 * GB mapping: map # to pound, and everything else stays
1467 for (i
=0; i
<len
; i
++)
1469 text
[i
] = cfg
.vtmode
== VT_OEMONLY ?
'\x9C' : '\xA3';
1470 } else if (attr
& ATTR_LINEDRW
) {
1473 static const char poorman
[] =
1474 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1477 static const char oemmap_437
[] =
1478 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1479 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1482 static const char oemmap_850
[] =
1483 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1484 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1486 /* Poor windows font ... eg: windows courier */
1487 static const char oemmap
[] =
1488 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1489 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1492 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1493 * VT100 line drawing chars; everything else stays normal.
1495 switch (cfg
.vtmode
) {
1497 for (i
=0; i
<len
; i
++)
1498 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1499 text
[i
] += '\x01' - '\x60';
1502 /* Make sure we actually have an OEM font */
1503 if (fonts
[nfont
|FONT_OEM
]) {
1506 for (i
=0; i
<len
; i
++)
1507 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1508 text
[i
] = oemmap
[(unsigned char)text
[i
] - 0x60];
1512 for (i
=0; i
<len
; i
++)
1513 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1514 text
[i
] = poorman
[(unsigned char)text
[i
] - 0x60];
1519 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
1520 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
1521 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
1523 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
1524 nfont
|= FONT_UNDERLINE
;
1527 if (nfont
&FONT_UNDERLINE
)
1528 force_manual_underline
= 1;
1529 /* Don't do the same for manual bold, it could be bad news. */
1531 nfont
&= ~(FONT_BOLD
|FONT_UNDERLINE
);
1533 if (font_needs_hand_underlining
&& (attr
& ATTR_UNDER
))
1534 force_manual_underline
= 1;
1535 if (attr
& ATTR_REVERSE
) {
1536 t
= nfg
; nfg
= nbg
; nbg
= t
;
1538 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
1540 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
1544 SelectObject (hdc
, fonts
[nfont
]);
1545 SetTextColor (hdc
, fg
);
1546 SetBkColor (hdc
, bg
);
1547 SetBkMode (hdc
, OPAQUE
);
1550 line_box
.right
= x
+fnt_width
*len
;
1551 line_box
.bottom
= y
+font_height
;
1552 ExtTextOut (hdc
, x
, y
, ETO_CLIPPED
|ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
1553 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
1554 SetBkMode (hdc
, TRANSPARENT
);
1556 /* GRR: This draws the character outside it's box and can leave
1557 * 'droppings' even with the clip box! I suppose I could loop it
1558 * one character at a time ... yuk.
1560 * Or ... I could do a test print with "W", and use +1 or -1 for this
1561 * shift depending on if the leftmost column is blank...
1563 ExtTextOut (hdc
, x
-1, y
, ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
1565 if (force_manual_underline
||
1566 (und_mode
== UND_LINE
&& (attr
& ATTR_UNDER
))) {
1568 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, fg
));
1569 MoveToEx (hdc
, x
, y
+descent
, NULL
);
1570 LineTo (hdc
, x
+len
*fnt_width
, y
+descent
);
1571 oldpen
= SelectObject (hdc
, oldpen
);
1572 DeleteObject (oldpen
);
1574 if (attr
& ATTR_PASCURS
) {
1577 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
1578 pts
[2].x
= pts
[3].x
= x
+fnt_width
-1;
1579 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
1580 pts
[1].y
= pts
[2].y
= y
+font_height
-1;
1581 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
1582 Polyline (hdc
, pts
, 5);
1583 oldpen
= SelectObject (hdc
, oldpen
);
1584 DeleteObject (oldpen
);
1588 static int check_compose(int first
, int second
) {
1590 static char * composetbl
[] = {
1591 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1592 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1593 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1594 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1595 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1596 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1597 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1598 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1599 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1600 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1601 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1602 "\"uü", "'yý", "htþ", "\"yÿ",
1606 static int recurse
= 0;
1613 sprintf(buf
, "cc(%d,%d)", first
, second
);
1618 for(c
=composetbl
; *c
; c
++) {
1619 if( (*c
)[0] == first
&& (*c
)[1] == second
)
1621 return (*c
)[2] & 0xFF;
1628 nc
= check_compose(second
, first
);
1630 nc
= check_compose(toupper(first
), toupper(second
));
1632 nc
= check_compose(toupper(second
), toupper(first
));
1640 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1641 * codes. Returns number of bytes used or zero to drop the message
1642 * or -1 to forward the message to windows.
1644 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
, unsigned char *output
) {
1646 int scan
, left_alt
= 0, key_down
, shift_state
;
1648 unsigned char * p
= output
;
1650 static WORD keys
[3];
1651 static int compose_state
= 0;
1652 static int compose_char
= 0;
1653 static WPARAM compose_key
= 0;
1655 r
= GetKeyboardState(keystate
);
1656 if (!r
) memset(keystate
, 0, sizeof(keystate
));
1659 /* Note if AltGr was pressed and if it was used as a compose key */
1660 if (wParam
== VK_MENU
&& (HIWORD(lParam
)&KF_EXTENDED
))
1662 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
1663 if (!compose_state
) compose_key
= wParam
;
1665 if (wParam
== VK_APPS
&& !compose_state
)
1666 compose_key
= wParam
;
1668 if (wParam
== compose_key
)
1670 if (compose_state
== 0 && (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
1672 else if (compose_state
== 1 && (HIWORD(lParam
)&KF_UP
))
1677 else if (compose_state
==1 && wParam
!= VK_CONTROL
)
1680 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1681 if ( (cfg
.funky_type
== 0 || (cfg
.funky_type
== 1 && app_keypad_keys
))
1682 && wParam
==VK_NUMLOCK
&& !(keystate
[VK_SHIFT
]&0x80)) {
1684 wParam
= VK_EXECUTE
;
1686 /* UnToggle NUMLock */
1687 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
1688 keystate
[VK_NUMLOCK
] ^= 1;
1691 /* And write back the 'adjusted' state */
1692 SetKeyboardState (keystate
);
1695 /* Disable Auto repeat if required */
1696 if (repeat_off
&& (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
)
1699 if ((HIWORD(lParam
)&KF_ALTDOWN
) && (keystate
[VK_RMENU
]&0x80) == 0)
1702 key_down
= ((HIWORD(lParam
)&KF_UP
)==0);
1704 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1705 if (left_alt
&& (keystate
[VK_CONTROL
]&0x80))
1706 keystate
[VK_MENU
] = 0;
1708 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
1709 shift_state
= ((keystate
[VK_SHIFT
]&0x80)!=0)
1710 + ((keystate
[VK_CONTROL
]&0x80)!=0)*2;
1713 * Record that we pressed key so the scroll window can be reset, but
1714 * be careful to avoid Shift-UP/Down
1716 if( wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
1720 /* Make sure we're not pasting */
1721 if (key_down
) term_nopaste();
1723 if (compose_state
>1 && left_alt
) compose_state
= 0;
1725 /* Sanitize the number pad if not using a PC NumPad */
1726 if( left_alt
|| (app_keypad_keys
&& cfg
.funky_type
!= 2)
1727 || cfg
.nethack_keypad
|| compose_state
)
1729 if ((HIWORD(lParam
)&KF_EXTENDED
) == 0)
1734 case VK_INSERT
: nParam
= VK_NUMPAD0
; break;
1735 case VK_END
: nParam
= VK_NUMPAD1
; break;
1736 case VK_DOWN
: nParam
= VK_NUMPAD2
; break;
1737 case VK_NEXT
: nParam
= VK_NUMPAD3
; break;
1738 case VK_LEFT
: nParam
= VK_NUMPAD4
; break;
1739 case VK_CLEAR
: nParam
= VK_NUMPAD5
; break;
1740 case VK_RIGHT
: nParam
= VK_NUMPAD6
; break;
1741 case VK_HOME
: nParam
= VK_NUMPAD7
; break;
1742 case VK_UP
: nParam
= VK_NUMPAD8
; break;
1743 case VK_PRIOR
: nParam
= VK_NUMPAD9
; break;
1744 case VK_DELETE
: nParam
= VK_DECIMAL
; break;
1748 if (keystate
[VK_NUMLOCK
]&1) shift_state
|= 1;
1754 /* If a key is pressed and AltGr is not active */
1755 if (key_down
&& (keystate
[VK_RMENU
]&0x80) == 0 && !compose_state
)
1757 /* Okay, prepare for most alts then ...*/
1758 if (left_alt
) *p
++ = '\033';
1760 /* Lets see if it's a pattern we know all about ... */
1761 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
1762 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
1765 if (wParam
== VK_NEXT
&& shift_state
== 1) {
1766 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
1769 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
1772 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
1774 SendMessage (hwnd
, WM_IGNORE_KEYMENU
, FALSE
, 0);
1775 SendMessage (hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
1776 SendMessage (hwnd
, WM_IGNORE_KEYMENU
, TRUE
, 0);
1780 /* Nethack keypad */
1781 if (cfg
.nethack_keypad
&& !left_alt
) {
1783 case VK_NUMPAD1
: *p
++ = shift_state ?
'B': 'b'; return p
-output
;
1784 case VK_NUMPAD2
: *p
++ = shift_state ?
'J': 'j'; return p
-output
;
1785 case VK_NUMPAD3
: *p
++ = shift_state ?
'N': 'n'; return p
-output
;
1786 case VK_NUMPAD4
: *p
++ = shift_state ?
'H': 'h'; return p
-output
;
1787 case VK_NUMPAD5
: *p
++ = shift_state ?
'.': '.'; return p
-output
;
1788 case VK_NUMPAD6
: *p
++ = shift_state ?
'L': 'l'; return p
-output
;
1789 case VK_NUMPAD7
: *p
++ = shift_state ?
'Y': 'y'; return p
-output
;
1790 case VK_NUMPAD8
: *p
++ = shift_state ?
'K': 'k'; return p
-output
;
1791 case VK_NUMPAD9
: *p
++ = shift_state ?
'U': 'u'; return p
-output
;
1795 /* Application Keypad */
1799 if ( cfg
.funky_type
== 0 ||
1800 ( cfg
.funky_type
== 1 && app_keypad_keys
)) switch(wParam
) {
1801 case VK_EXECUTE
: if (app_keypad_keys
) xkey
= 'P'; break;
1802 case VK_DIVIDE
: xkey
= 'Q'; break;
1803 case VK_MULTIPLY
:xkey
= 'R'; break;
1804 case VK_SUBTRACT
:xkey
= 'S'; break;
1806 if(app_keypad_keys
) switch(wParam
) {
1807 case VK_NUMPAD0
: xkey
= 'p'; break;
1808 case VK_NUMPAD1
: xkey
= 'q'; break;
1809 case VK_NUMPAD2
: xkey
= 'r'; break;
1810 case VK_NUMPAD3
: xkey
= 's'; break;
1811 case VK_NUMPAD4
: xkey
= 't'; break;
1812 case VK_NUMPAD5
: xkey
= 'u'; break;
1813 case VK_NUMPAD6
: xkey
= 'v'; break;
1814 case VK_NUMPAD7
: xkey
= 'w'; break;
1815 case VK_NUMPAD8
: xkey
= 'x'; break;
1816 case VK_NUMPAD9
: xkey
= 'y'; break;
1818 case VK_DECIMAL
: xkey
= 'n'; break;
1819 case VK_ADD
: if(shift_state
) xkey
= 'm';
1823 if (HIWORD(lParam
)&KF_EXTENDED
)
1831 if (xkey
>='P' && xkey
<='S')
1832 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
1834 p
+= sprintf((char *)p
, "\x1B?%c", xkey
);
1837 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
1842 if (wParam
== VK_BACK
&& shift_state
== 0 ) /* Backspace */
1844 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
1847 if (wParam
== VK_TAB
&& shift_state
== 1 ) /* Shift tab */
1849 *p
++ = 0x1B; *p
++ = '['; *p
++ = 'Z'; return p
- output
;
1851 if (wParam
== VK_SPACE
&& shift_state
== 2 ) /* Ctrl-Space */
1853 *p
++ = 0; return p
- output
;
1855 if (wParam
== VK_SPACE
&& shift_state
== 3 ) /* Ctrl-Shift-Space */
1857 *p
++ = 160; return p
- output
;
1859 if (wParam
== VK_CANCEL
&& shift_state
== 2 ) /* Ctrl-Break */
1861 *p
++ = 3; return p
- output
;
1863 /* Control-2 to Control-8 are special */
1864 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8')
1866 *p
++ = "\000\033\034\035\036\037\177"[wParam
-'2'];
1869 if (shift_state
== 2 && wParam
== 0xBD) {
1873 if (shift_state
== 2 && wParam
== 0xDF) {
1877 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
1878 *p
++ = '\r'; *p
++ = '\n';
1883 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1884 * for integer decimal nn.)
1886 * We also deal with the weird ones here. Linux VCs replace F1
1887 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1888 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1893 case VK_F1
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11); break;
1894 case VK_F2
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12); break;
1895 case VK_F3
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13); break;
1896 case VK_F4
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14); break;
1897 case VK_F5
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15); break;
1898 case VK_F6
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17); break;
1899 case VK_F7
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18); break;
1900 case VK_F8
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19); break;
1901 case VK_F9
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20); break;
1902 case VK_F10
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21); break;
1903 case VK_F11
: code
= 23; break;
1904 case VK_F12
: code
= 24; break;
1905 case VK_F13
: code
= 25; break;
1906 case VK_F14
: code
= 26; break;
1907 case VK_F15
: code
= 28; break;
1908 case VK_F16
: code
= 29; break;
1909 case VK_F17
: code
= 31; break;
1910 case VK_F18
: code
= 32; break;
1911 case VK_F19
: code
= 33; break;
1912 case VK_F20
: code
= 34; break;
1913 case VK_HOME
: code
= 1; break;
1914 case VK_INSERT
: code
= 2; break;
1915 case VK_DELETE
: code
= 3; break;
1916 case VK_END
: code
= 4; break;
1917 case VK_PRIOR
: code
= 5; break;
1918 case VK_NEXT
: code
= 6; break;
1920 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
1921 p
+= sprintf((char *)p
, "\x1B[[%c", code
+ 'A' - 11);
1924 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
1925 p
+= sprintf((char *)p
, "\x1BO%c", code
+ 'P' - 11);
1928 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
1929 p
+= sprintf((char *)p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
1933 p
+= sprintf((char *)p
, "\x1B[%d~", code
);
1938 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
1939 * some reason seems to send VK_CLEAR to Windows...).
1944 case VK_UP
: xkey
= 'A'; break;
1945 case VK_DOWN
: xkey
= 'B'; break;
1946 case VK_RIGHT
: xkey
= 'C'; break;
1947 case VK_LEFT
: xkey
= 'D'; break;
1948 case VK_CLEAR
: xkey
= 'G'; break;
1953 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
1954 else if (app_cursor_keys
)
1955 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
1957 p
+= sprintf((char *)p
, "\x1B[%c", xkey
);
1963 /* Okay we've done everything interesting; let windows deal with
1964 * the boring stuff */
1966 BOOL capsOn
=keystate
[VK_CAPITAL
] !=0;
1968 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
1969 if(cfg
.xlat_capslockcyr
)
1970 keystate
[VK_CAPITAL
] = 0;
1972 r
= ToAscii (wParam
, scan
, keystate
, keys
, 0);
1978 unsigned char ch
= (unsigned char)keys
[i
];
1980 if (compose_state
==2 && (ch
&0x80) == 0 && ch
>' ') {
1985 if (compose_state
==3 && (ch
&0x80) == 0 && ch
>' ') {
1989 if ((nc
=check_compose(compose_char
,ch
)) == -1)
1994 *p
++ = xlat_kbd2tty((unsigned char)nc
);
2000 if( left_alt
&& key_down
) *p
++ = '\033';
2006 ch
= xlat_latkbd2win(ch
);
2007 *p
++ = xlat_kbd2tty(ch
);
2011 /* This is so the ALT-Numpad and dead keys work correctly. */
2018 /* This stops ALT press-release doing a 'COMMAND MENU' function */
2020 if (message
== WM_SYSKEYUP
&& wParam
== VK_MENU
)
2022 keystate
[VK_MENU
] = 0;
2030 void set_title (char *title
) {
2031 sfree (window_name
);
2032 window_name
= smalloc(1+strlen(title
));
2033 strcpy (window_name
, title
);
2034 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
2035 SetWindowText (hwnd
, title
);
2038 void set_icon (char *title
) {
2040 icon_name
= smalloc(1+strlen(title
));
2041 strcpy (icon_name
, title
);
2042 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
2043 SetWindowText (hwnd
, title
);
2046 void set_sbar (int total
, int start
, int page
) {
2049 if (!cfg
.scrollbar
) return;
2051 si
.cbSize
= sizeof(si
);
2052 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
2054 si
.nMax
= total
- 1;
2058 SetScrollInfo (hwnd
, SB_VERT
, &si
, TRUE
);
2061 Context
get_ctx(void) {
2066 SelectPalette (hdc
, pal
, FALSE
);
2072 void free_ctx (Context ctx
) {
2073 SelectPalette (ctx
, GetStockObject (DEFAULT_PALETTE
), FALSE
);
2074 ReleaseDC (hwnd
, ctx
);
2077 static void real_palette_set (int n
, int r
, int g
, int b
) {
2079 logpal
->palPalEntry
[n
].peRed
= r
;
2080 logpal
->palPalEntry
[n
].peGreen
= g
;
2081 logpal
->palPalEntry
[n
].peBlue
= b
;
2082 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
2083 colours
[n
] = PALETTERGB(r
, g
, b
);
2084 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2086 colours
[n
] = RGB(r
, g
, b
);
2089 void palette_set (int n
, int r
, int g
, int b
) {
2090 static const int first
[21] = {
2091 0, 2, 4, 6, 8, 10, 12, 14,
2092 1, 3, 5, 7, 9, 11, 13, 15,
2095 real_palette_set (first
[n
], r
, g
, b
);
2097 real_palette_set (first
[n
]+1, r
, g
, b
);
2099 HDC hdc
= get_ctx();
2100 UnrealizeObject (pal
);
2101 RealizePalette (hdc
);
2106 void palette_reset (void) {
2109 for (i
= 0; i
< NCOLOURS
; i
++) {
2111 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
2112 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
2113 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
2114 logpal
->palPalEntry
[i
].peFlags
= 0;
2115 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
2116 defpal
[i
].rgbtGreen
,
2117 defpal
[i
].rgbtBlue
);
2119 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
2120 defpal
[i
].rgbtGreen
,
2121 defpal
[i
].rgbtBlue
);
2126 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2128 RealizePalette (hdc
);
2133 void write_clip (void *data
, int len
) {
2137 clipdata
= GlobalAlloc (GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
2140 lock
= GlobalLock (clipdata
);
2143 memcpy (lock
, data
, len
);
2144 ((unsigned char *) lock
) [len
] = 0;
2145 GlobalUnlock (clipdata
);
2147 SendMessage (hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
2148 if (OpenClipboard (hwnd
)) {
2150 SetClipboardData (CF_TEXT
, clipdata
);
2153 GlobalFree (clipdata
);
2154 SendMessage (hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
2157 void get_clip (void **p
, int *len
) {
2158 static HGLOBAL clipdata
= NULL
;
2162 GlobalUnlock (clipdata
);
2166 if (OpenClipboard (NULL
)) {
2167 clipdata
= GetClipboardData (CF_TEXT
);
2170 *p
= GlobalLock (clipdata
);
2184 * Move `lines' lines from position `from' to position `to' in the
2187 void optimised_move (int to
, int from
, int lines
) {
2191 min
= (to
< from ? to
: from
);
2192 max
= to
+ from
- min
;
2194 r
.left
= 0; r
.right
= cols
* font_width
;
2195 r
.top
= min
* font_height
; r
.bottom
= (max
+lines
) * font_height
;
2196 ScrollWindow (hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
2200 * Print a message box and perform a fatal exit.
2202 void fatalbox(char *fmt
, ...) {
2207 vsprintf(stuff
, fmt
, ap
);
2209 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
2216 void beep(int errorbeep
) {
2217 static long last_beep
= 0;
2218 long now
, beep_diff
;
2220 now
= GetTickCount();
2221 beep_diff
= now
-last_beep
;
2223 /* Make sure we only respond to one beep per packet or so */
2224 if (beep_diff
>=0 && beep_diff
<50)
2228 MessageBeep(MB_ICONHAND
);
2232 last_beep
= GetTickCount();