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 * Print a message box and close the connection.
526 void connection_fatal(char *fmt
, ...) {
531 vsprintf(stuff
, fmt
, ap
);
533 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
534 if (cfg
.close_on_exit
)
537 session_closed
= TRUE
;
538 SetWindowText (hwnd
, "PuTTY (inactive)");
543 * Actually do the job requested by a WM_NETEVENT
545 static void enact_pending_netevent(void) {
547 static int reentering
= 0;
550 return; /* don't unpend the pending */
552 pending_netevent
= FALSE
;
555 i
= back
->msg (pend_netevent_wParam
, pend_netevent_lParam
);
560 switch (WSABASEERR
+ (-i
) % 10000) {
562 sprintf(buf
, "Connection reset by peer");
565 sprintf(buf
, "Unexpected network error %d", -i
);
568 connection_fatal(buf
);
571 if (cfg
.close_on_exit
)
574 session_closed
= TRUE
;
575 MessageBox(hwnd
, "Connection closed by remote host",
576 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
577 SetWindowText (hwnd
, "PuTTY (inactive)");
583 * Copy the colour palette from the configuration data into defpal.
584 * This is non-trivial because the colour indices are different.
586 static void cfgtopalette(void) {
588 static const int ww
[] = {
589 6, 7, 8, 9, 10, 11, 12, 13,
590 14, 15, 16, 17, 18, 19, 20, 21,
591 0, 1, 2, 3, 4, 4, 5, 5
594 for (i
=0; i
<24; i
++) {
596 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
597 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
598 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
603 * Set up the colour palette.
605 static void init_palette(void) {
607 HDC hdc
= GetDC (hwnd
);
609 if (cfg
.try_palette
&&
610 GetDeviceCaps (hdc
, RASTERCAPS
) & RC_PALETTE
) {
611 logpal
= smalloc(sizeof(*logpal
)
612 - sizeof(logpal
->palPalEntry
)
613 + NCOLOURS
* sizeof(PALETTEENTRY
));
614 logpal
->palVersion
= 0x300;
615 logpal
->palNumEntries
= NCOLOURS
;
616 for (i
= 0; i
< NCOLOURS
; i
++) {
617 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
618 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
619 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
620 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
622 pal
= CreatePalette (logpal
);
624 SelectPalette (hdc
, pal
, FALSE
);
625 RealizePalette (hdc
);
626 SelectPalette (hdc
, GetStockObject (DEFAULT_PALETTE
),
630 ReleaseDC (hwnd
, hdc
);
633 for (i
=0; i
<NCOLOURS
; i
++)
634 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
638 for(i
=0; i
<NCOLOURS
; i
++)
639 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
645 * Initialise all the fonts we will need. There may be as many as
646 * eight or as few as one. We also:
648 * - check the font width and height, correcting our guesses if
651 * - verify that the bold font is the same width as the ordinary
652 * one, and engage shadow bolding if not.
654 * - verify that the underlined font is the same width as the
655 * ordinary one (manual underlining by means of line drawing can
656 * be done in a pinch).
658 static void init_fonts(int pick_width
) {
663 int fw_dontcare
, fw_bold
;
672 if (cfg
.fontisbold
) {
673 fw_dontcare
= FW_BOLD
;
676 fw_dontcare
= FW_DONTCARE
;
682 font_height
= cfg
.fontheight
;
683 font_width
= pick_width
;
686 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
687 c, OUT_DEFAULT_PRECIS, \
688 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
689 FIXED_PITCH | FF_DONTCARE, cfg.font)
691 if (cfg
.vtmode
!= VT_OEMONLY
) {
692 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
694 SelectObject (hdc
, fonts
[FONT_NORMAL
]);
695 GetTextMetrics(hdc
, &tm
);
696 font_height
= tm
.tmHeight
;
697 font_width
= tm
.tmAveCharWidth
;
699 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
702 * Some fonts, e.g. 9-pt Courier, draw their underlines
703 * outside their character cell. We successfully prevent
704 * screen corruption by clipping the text output, but then
705 * we lose the underline completely. Here we try to work
706 * out whether this is such a font, and if it is, we set a
707 * flag that causes underlines to be drawn by hand.
709 * Having tried other more sophisticated approaches (such
710 * as examining the TEXTMETRIC structure or requesting the
711 * height of a string), I think we'll do this the brute
712 * force way: we create a small bitmap, draw an underlined
713 * space on it, and test to see whether any pixels are
714 * foreground-coloured. (Since we expect the underline to
715 * go all the way across the character cell, we only search
716 * down a single column of the bitmap, half way across.)
720 HBITMAP und_bm
, und_oldbm
;
724 und_dc
= CreateCompatibleDC(hdc
);
725 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
726 und_oldbm
= SelectObject(und_dc
, und_bm
);
727 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
728 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
729 SetTextColor (und_dc
, RGB(255,255,255));
730 SetBkColor (und_dc
, RGB(0,0,0));
731 SetBkMode (und_dc
, OPAQUE
);
732 ExtTextOut (und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
734 for (i
= 0; i
< font_height
; i
++) {
735 c
= GetPixel(und_dc
, font_width
/2, i
);
739 SelectObject(und_dc
, und_oldbm
);
740 DeleteObject(und_bm
);
742 font_needs_hand_underlining
= !gotit
;
745 if (bold_mode
== BOLD_FONT
) {
746 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
747 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
750 if (cfg
.vtmode
== VT_OEMANSI
) {
751 f(FONT_OEM
, OEM_CHARSET
, fw_dontcare
, FALSE
);
752 f(FONT_OEMUND
, OEM_CHARSET
, fw_dontcare
, TRUE
);
754 if (bold_mode
== BOLD_FONT
) {
755 f(FONT_OEMBOLD
, OEM_CHARSET
, fw_bold
, FALSE
);
756 f(FONT_OEMBOLDUND
, OEM_CHARSET
, fw_bold
, TRUE
);
762 f(FONT_OEM
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
764 SelectObject (hdc
, fonts
[FONT_OEM
]);
765 GetTextMetrics(hdc
, &tm
);
766 font_height
= tm
.tmHeight
;
767 font_width
= tm
.tmAveCharWidth
;
769 f(FONT_OEMUND
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
771 if (bold_mode
== BOLD_FONT
) {
772 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
773 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
778 descent
= tm
.tmAscent
+ 1;
779 if (descent
>= font_height
)
780 descent
= font_height
- 1;
781 firstchar
= tm
.tmFirstChar
;
783 for (i
=0; i
<8; i
++) {
785 if (SelectObject (hdc
, fonts
[i
]) &&
786 GetTextMetrics(hdc
, &tm
) )
787 fsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
793 ReleaseDC (hwnd
, hdc
);
795 /* ... This is wrong in OEM only mode */
796 if (fsize
[FONT_UNDERLINE
] != fsize
[FONT_NORMAL
] ||
797 (bold_mode
== BOLD_FONT
&&
798 fsize
[FONT_BOLDUND
] != fsize
[FONT_BOLD
])) {
800 DeleteObject (fonts
[FONT_UNDERLINE
]);
801 if (bold_mode
== BOLD_FONT
)
802 DeleteObject (fonts
[FONT_BOLDUND
]);
805 if (bold_mode
== BOLD_FONT
&&
806 fsize
[FONT_BOLD
] != fsize
[FONT_NORMAL
]) {
807 bold_mode
= BOLD_SHADOW
;
808 DeleteObject (fonts
[FONT_BOLD
]);
809 if (und_mode
== UND_FONT
)
810 DeleteObject (fonts
[FONT_BOLDUND
]);
814 /* With the fascist font painting it doesn't matter if the linedraw font
815 * isn't exactly the right size anymore so we don't have to check this.
817 if (cfg
.vtmode
== VT_OEMANSI
&& fsize
[FONT_OEM
] != fsize
[FONT_NORMAL
] ) {
818 if( cfg
.fontcharset
== OEM_CHARSET
)
820 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
821 "different sizes. Using OEM-only mode instead",
822 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
823 cfg
.vtmode
= VT_OEMONLY
;
825 else if( firstchar
< ' ' )
827 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
828 "different sizes. Using XTerm mode instead",
829 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
830 cfg
.vtmode
= VT_XWINDOWS
;
834 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
835 "different sizes. Using ISO8859-1 mode instead",
836 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
837 cfg
.vtmode
= VT_POORMAN
;
842 DeleteObject (fonts
[i
]);
848 void request_resize (int w
, int h
, int refont
) {
851 /* If the window is maximized supress resizing attempts */
852 if(IsZoomed(hwnd
)) return;
855 /* Don't do this in OEMANSI, you may get disable messages */
856 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132)
857 && cfg
.vtmode
!= VT_OEMANSI
)
859 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132))
862 /* If font width too big for screen should we shrink the font more ? */
864 font_width
= ((font_width
*cols
+w
/2)/w
);
871 DeleteObject(fonts
[i
]);
873 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
875 init_fonts(font_width
);
879 static int first_time
= 1;
885 /* Get the size of the screen */
886 if (GetClientRect(GetDesktopWindow(),&ss
))
887 /* first_time = 0 */;
888 else { first_time
= 2; break; }
890 /* Make sure the values are sane */
891 width
= (ss
.right
-ss
.left
-extra_width
) / font_width
;
892 height
= (ss
.bottom
-ss
.top
-extra_height
) / font_height
;
894 if (w
>width
) w
=width
;
895 if (h
>height
) h
=height
;
901 width
= extra_width
+ font_width
* w
;
902 height
= extra_height
+ font_height
* h
;
904 SetWindowPos (hwnd
, NULL
, 0, 0, width
, height
,
905 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
906 SWP_NOMOVE
| SWP_NOZORDER
);
909 static void click (Mouse_Button b
, int x
, int y
) {
910 int thistime
= GetMessageTime();
912 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
913 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
914 lastact
== MA_2CLK ? MA_3CLK
:
915 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
920 if (lastact
!= MA_NOTHING
)
921 term_mouse (b
, lastact
, x
, y
);
925 static LRESULT CALLBACK
WndProc (HWND hwnd
, UINT message
,
926 WPARAM wParam
, LPARAM lParam
) {
928 static int ignore_size
= FALSE
;
929 static int ignore_clip
= FALSE
;
930 static int ignore_keymenu
= TRUE
;
931 static int just_reconfigged
= FALSE
;
935 if (pending_netevent
)
936 enact_pending_netevent();
944 if (!cfg
.warn_on_close
|| session_closed
||
945 MessageBox(hwnd
, "Are you sure you want to close this session?",
946 "PuTTY Exit Confirmation",
947 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
954 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
957 return 0; /* don't put up system menu on Alt */
970 PROCESS_INFORMATION pi
;
971 HANDLE filemap
= NULL
;
973 if (wParam
== IDM_DUPSESS
) {
975 * Allocate a file-mapping memory chunk for the
978 SECURITY_ATTRIBUTES sa
;
981 sa
.nLength
= sizeof(sa
);
982 sa
.lpSecurityDescriptor
= NULL
;
983 sa
.bInheritHandle
= TRUE
;
984 filemap
= CreateFileMapping((HANDLE
)0xFFFFFFFF,
991 p
= (Config
*)MapViewOfFile(filemap
,
993 0, 0, sizeof(Config
));
995 *p
= cfg
; /* structure copy */
999 sprintf(c
, "putty &%p", filemap
);
1001 } else if (wParam
== IDM_SAVEDSESS
) {
1002 char *session
= sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1003 cl
= malloc(16 + strlen(session
)); /* 8, but play safe */
1005 cl
= NULL
; /* not a very important failure mode */
1007 sprintf(cl
, "putty @%s", session
);
1013 GetModuleFileName (NULL
, b
, sizeof(b
)-1);
1015 si
.lpReserved
= NULL
;
1016 si
.lpDesktop
= NULL
;
1020 si
.lpReserved2
= NULL
;
1021 CreateProcess (b
, cl
, NULL
, NULL
, TRUE
,
1022 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1025 CloseHandle(filemap
);
1031 if (!do_reconfig(hwnd
))
1033 just_reconfigged
= TRUE
;
1038 DeleteObject(fonts
[i
]);
1040 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1041 und_mode
= UND_FONT
;
1044 /* Telnet will change local echo -> remote if the remote asks */
1045 if (cfg
.protocol
!= PROT_TELNET
)
1046 ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
1054 /* Enable or disable the scroll bar, etc */
1056 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1059 if (cfg
.scrollbar
) nflg
|= WS_VSCROLL
;
1060 else nflg
&= ~WS_VSCROLL
;
1062 nflg
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1064 nflg
|= (WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1070 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1071 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1072 SetWindowPos(hwnd
, NULL
, 0,0,0,0,
1073 SWP_NOACTIVATE
|SWP_NOCOPYBITS
|
1074 SWP_NOMOVE
|SWP_NOSIZE
| SWP_NOZORDER
|
1077 GetWindowRect (hwnd
, &wr
);
1078 GetClientRect (hwnd
, &cr
);
1079 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1080 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1084 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1085 InvalidateRect(hwnd
, NULL
, TRUE
);
1086 SetWindowPos (hwnd
, NULL
, 0, 0,
1087 extra_width
+ font_width
* cfg
.width
,
1088 extra_height
+ font_height
* cfg
.height
,
1089 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1090 SWP_NOMOVE
| SWP_NOZORDER
);
1091 if (IsIconic(hwnd
)) {
1092 SetWindowText (hwnd
,
1093 cfg
.win_name_always ? window_name
: icon_name
);
1102 case IDM_TEL_AYT
: back
->special (TS_AYT
); break;
1103 case IDM_TEL_BRK
: back
->special (TS_BRK
); break;
1104 case IDM_TEL_SYNCH
: back
->special (TS_SYNCH
); break;
1105 case IDM_TEL_EC
: back
->special (TS_EC
); break;
1106 case IDM_TEL_EL
: back
->special (TS_EL
); break;
1107 case IDM_TEL_GA
: back
->special (TS_GA
); break;
1108 case IDM_TEL_NOP
: back
->special (TS_NOP
); break;
1109 case IDM_TEL_ABORT
: back
->special (TS_ABORT
); break;
1110 case IDM_TEL_AO
: back
->special (TS_AO
); break;
1111 case IDM_TEL_IP
: back
->special (TS_IP
); break;
1112 case IDM_TEL_SUSP
: back
->special (TS_SUSP
); break;
1113 case IDM_TEL_EOR
: back
->special (TS_EOR
); break;
1114 case IDM_TEL_EOF
: back
->special (TS_EOF
); break;
1119 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1120 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1125 #define X_POS(l) ((int)(short)LOWORD(l))
1126 #define Y_POS(l) ((int)(short)HIWORD(l))
1128 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1129 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1131 case WM_LBUTTONDOWN
:
1132 click (MB_SELECT
, TO_CHR_X(X_POS(lParam
)),
1133 TO_CHR_Y(Y_POS(lParam
)));
1137 term_mouse (MB_SELECT
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1138 TO_CHR_Y(Y_POS(lParam
)));
1141 case WM_MBUTTONDOWN
:
1143 click (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1144 TO_CHR_X(X_POS(lParam
)),
1145 TO_CHR_Y(Y_POS(lParam
)));
1148 term_mouse (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1149 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1150 TO_CHR_Y(Y_POS(lParam
)));
1153 case WM_RBUTTONDOWN
:
1155 click (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1156 TO_CHR_X(X_POS(lParam
)),
1157 TO_CHR_Y(Y_POS(lParam
)));
1160 term_mouse (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1161 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1162 TO_CHR_Y(Y_POS(lParam
)));
1167 * Add the mouse position and message time to the random
1168 * number noise, if we're using ssh.
1170 if (cfg
.protocol
== PROT_SSH
)
1171 noise_ultralight(lParam
);
1173 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1175 if (wParam
& MK_LBUTTON
)
1177 else if (wParam
& MK_MBUTTON
)
1178 b
= cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
;
1180 b
= cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
;
1181 term_mouse (b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1182 TO_CHR_Y(Y_POS(lParam
)));
1185 case WM_IGNORE_CLIP
:
1186 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1188 case WM_IGNORE_KEYMENU
:
1189 ignore_keymenu
= wParam
; /* do or don't ignore SC_KEYMENU */
1191 case WM_DESTROYCLIPBOARD
:
1194 ignore_clip
= FALSE
;
1199 hdc
= BeginPaint (hwnd
, &p
);
1201 SelectPalette (hdc
, pal
, TRUE
);
1202 RealizePalette (hdc
);
1204 term_paint (hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1205 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1206 SelectObject (hdc
, GetStockObject(SYSTEM_FONT
));
1207 SelectObject (hdc
, GetStockObject(WHITE_PEN
));
1208 EndPaint (hwnd
, &p
);
1212 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1213 * but the only one that's likely to try to overload us is FD_READ.
1214 * This means buffering just one is fine.
1216 if (pending_netevent
)
1217 enact_pending_netevent();
1219 pending_netevent
= TRUE
;
1220 pend_netevent_wParam
=wParam
;
1221 pend_netevent_lParam
=lParam
;
1233 case WM_IGNORE_SIZE
:
1234 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1236 case WM_ENTERSIZEMOVE
:
1239 case WM_EXITSIZEMOVE
:
1244 int width
, height
, w
, h
, ew
, eh
;
1245 LPRECT r
= (LPRECT
)lParam
;
1247 width
= r
->right
- r
->left
- extra_width
;
1248 height
= r
->bottom
- r
->top
- extra_height
;
1249 w
= (width
+ font_width
/2) / font_width
; if (w
< 1) w
= 1;
1250 h
= (height
+ font_height
/2) / font_height
; if (h
< 1) h
= 1;
1251 UpdateSizeTip(hwnd
, w
, h
);
1252 ew
= width
- w
* font_width
;
1253 eh
= height
- h
* font_height
;
1255 if (wParam
== WMSZ_LEFT
||
1256 wParam
== WMSZ_BOTTOMLEFT
||
1257 wParam
== WMSZ_TOPLEFT
)
1263 if (wParam
== WMSZ_TOP
||
1264 wParam
== WMSZ_TOPRIGHT
||
1265 wParam
== WMSZ_TOPLEFT
)
1275 /* break; (never reached) */
1277 if (wParam
== SIZE_MINIMIZED
) {
1278 SetWindowText (hwnd
,
1279 cfg
.win_name_always ? window_name
: icon_name
);
1282 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1283 SetWindowText (hwnd
, window_name
);
1285 int width
, height
, w
, h
;
1286 #if 0 /* we have fixed this using WM_SIZING now */
1290 width
= LOWORD(lParam
);
1291 height
= HIWORD(lParam
);
1292 w
= width
/ font_width
; if (w
< 1) w
= 1;
1293 h
= height
/ font_height
; if (h
< 1) h
= 1;
1294 #if 0 /* we have fixed this using WM_SIZING now */
1295 ew
= width
- w
* font_width
;
1296 eh
= height
- h
* font_height
;
1297 if (ew
!= 0 || eh
!= 0) {
1299 GetWindowRect (hwnd
, &r
);
1300 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1301 SetWindowPos (hwnd
, NULL
, 0, 0,
1302 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1303 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1306 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1308 term_size (h
, w
, cfg
.savelines
);
1310 just_reconfigged
= FALSE
;
1313 ignore_size
= FALSE
;
1316 switch (LOWORD(wParam
)) {
1317 case SB_BOTTOM
: term_scroll(-1, 0); break;
1318 case SB_TOP
: term_scroll(+1, 0); break;
1319 case SB_LINEDOWN
: term_scroll (0, +1); break;
1320 case SB_LINEUP
: term_scroll (0, -1); break;
1321 case SB_PAGEDOWN
: term_scroll (0, +rows
/2); break;
1322 case SB_PAGEUP
: term_scroll (0, -rows
/2); break;
1323 case SB_THUMBPOSITION
: case SB_THUMBTRACK
:
1324 term_scroll (1, HIWORD(wParam
)); break;
1327 case WM_PALETTECHANGED
:
1328 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1329 HDC hdc
= get_ctx();
1331 if (RealizePalette (hdc
) > 0)
1337 case WM_QUERYNEWPALETTE
:
1339 HDC hdc
= get_ctx();
1341 if (RealizePalette (hdc
) > 0)
1353 * Add the scan code and keypress timing to the random
1354 * number noise, if we're using ssh.
1356 if (cfg
.protocol
== PROT_SSH
)
1357 noise_ultralight(lParam
);
1360 * We don't do TranslateMessage since it disassociates the
1361 * resulting CHAR message from the KEYDOWN that sparked it,
1362 * which we occasionally don't want. Instead, we process
1363 * KEYDOWN, and call the Win32 translator functions so that
1364 * we get the translations under _our_ control.
1367 unsigned char buf
[20];
1370 len
= TranslateKey (message
, wParam
, lParam
, buf
);
1372 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1373 ldisc
->send (buf
, len
);
1379 * Nevertheless, we are prepared to deal with WM_CHAR
1380 * messages, should they crop up. So if someone wants to
1381 * post the things to us as part of a macro manoeuvre,
1382 * we're ready to cope.
1385 char c
= xlat_kbd2tty((unsigned char)wParam
);
1386 ldisc
->send (&c
, 1);
1391 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1395 * Draw a line of text in the window, at given character
1396 * coordinates, in given attributes.
1398 * We are allowed to fiddle with the contents of `text'.
1400 void do_text (Context ctx
, int x
, int y
, char *text
, int len
,
1401 unsigned long attr
, int lattr
) {
1403 int nfg
, nbg
, nfont
;
1406 int force_manual_underline
= 0;
1407 int fnt_width
= font_width
*(1+(lattr
!=LATTR_NORM
));
1408 static int *IpDx
= 0, IpDxLEN
= 0;;
1410 if (len
>IpDxLEN
|| IpDx
[0] != fnt_width
) {
1414 IpDx
= smalloc((len
+16)*sizeof(int));
1417 for(i
=0; i
<IpDxLEN
; i
++)
1418 IpDx
[i
] = fnt_width
;
1424 if (attr
& ATTR_ACTCURS
) {
1425 attr
&= (bold_mode
== BOLD_COLOURS ?
0x300200 : 0x300300);
1426 attr
^= ATTR_CUR_XOR
;
1430 if (cfg
.vtmode
== VT_OEMONLY
)
1434 * Map high-half characters in order to approximate ISO using
1435 * OEM character set. No characters are missing if the OEM codepage
1438 if (nfont
& FONT_OEM
) {
1440 for (i
=0; i
<len
; i
++)
1441 if (text
[i
] >= '\xA0' && text
[i
] <= '\xFF') {
1443 /* This is CP850 ... perfect translation */
1444 static const char oemhighhalf
[] =
1445 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1446 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1447 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1448 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1449 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1450 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1451 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1452 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1453 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1454 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1455 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1456 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1459 /* This is CP437 ... junk translation */
1460 static const unsigned char oemhighhalf
[] = {
1461 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1462 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1463 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1464 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1465 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1466 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1467 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1468 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1469 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1470 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1471 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1472 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1475 text
[i
] = oemhighhalf
[(unsigned char)text
[i
] - 0xA0];
1479 if (attr
& ATTR_GBCHR
) {
1482 * GB mapping: map # to pound, and everything else stays
1485 for (i
=0; i
<len
; i
++)
1487 text
[i
] = cfg
.vtmode
== VT_OEMONLY ?
'\x9C' : '\xA3';
1488 } else if (attr
& ATTR_LINEDRW
) {
1491 static const char poorman
[] =
1492 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1495 static const char oemmap_437
[] =
1496 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1497 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1500 static const char oemmap_850
[] =
1501 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1502 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1504 /* Poor windows font ... eg: windows courier */
1505 static const char oemmap
[] =
1506 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1507 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1510 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1511 * VT100 line drawing chars; everything else stays normal.
1513 switch (cfg
.vtmode
) {
1515 for (i
=0; i
<len
; i
++)
1516 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1517 text
[i
] += '\x01' - '\x60';
1520 /* Make sure we actually have an OEM font */
1521 if (fonts
[nfont
|FONT_OEM
]) {
1524 for (i
=0; i
<len
; i
++)
1525 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1526 text
[i
] = oemmap
[(unsigned char)text
[i
] - 0x60];
1530 for (i
=0; i
<len
; i
++)
1531 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1532 text
[i
] = poorman
[(unsigned char)text
[i
] - 0x60];
1537 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
1538 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
1539 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
1541 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
1542 nfont
|= FONT_UNDERLINE
;
1545 if (nfont
&FONT_UNDERLINE
)
1546 force_manual_underline
= 1;
1547 /* Don't do the same for manual bold, it could be bad news. */
1549 nfont
&= ~(FONT_BOLD
|FONT_UNDERLINE
);
1551 if (font_needs_hand_underlining
&& (attr
& ATTR_UNDER
))
1552 force_manual_underline
= 1;
1553 if (attr
& ATTR_REVERSE
) {
1554 t
= nfg
; nfg
= nbg
; nbg
= t
;
1556 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
1558 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
1562 SelectObject (hdc
, fonts
[nfont
]);
1563 SetTextColor (hdc
, fg
);
1564 SetBkColor (hdc
, bg
);
1565 SetBkMode (hdc
, OPAQUE
);
1568 line_box
.right
= x
+fnt_width
*len
;
1569 line_box
.bottom
= y
+font_height
;
1570 ExtTextOut (hdc
, x
, y
, ETO_CLIPPED
|ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
1571 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
1572 SetBkMode (hdc
, TRANSPARENT
);
1574 /* GRR: This draws the character outside it's box and can leave
1575 * 'droppings' even with the clip box! I suppose I could loop it
1576 * one character at a time ... yuk.
1578 * Or ... I could do a test print with "W", and use +1 or -1 for this
1579 * shift depending on if the leftmost column is blank...
1581 ExtTextOut (hdc
, x
-1, y
, ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
1583 if (force_manual_underline
||
1584 (und_mode
== UND_LINE
&& (attr
& ATTR_UNDER
))) {
1586 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, fg
));
1587 MoveToEx (hdc
, x
, y
+descent
, NULL
);
1588 LineTo (hdc
, x
+len
*fnt_width
, y
+descent
);
1589 oldpen
= SelectObject (hdc
, oldpen
);
1590 DeleteObject (oldpen
);
1592 if (attr
& ATTR_PASCURS
) {
1595 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
1596 pts
[2].x
= pts
[3].x
= x
+fnt_width
-1;
1597 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
1598 pts
[1].y
= pts
[2].y
= y
+font_height
-1;
1599 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
1600 Polyline (hdc
, pts
, 5);
1601 oldpen
= SelectObject (hdc
, oldpen
);
1602 DeleteObject (oldpen
);
1606 static int check_compose(int first
, int second
) {
1608 static char * composetbl
[] = {
1609 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1610 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1611 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1612 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1613 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1614 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1615 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1616 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1617 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1618 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1619 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1620 "\"uü", "'yý", "htþ", "\"yÿ",
1624 static int recurse
= 0;
1631 sprintf(buf
, "cc(%d,%d)", first
, second
);
1636 for(c
=composetbl
; *c
; c
++) {
1637 if( (*c
)[0] == first
&& (*c
)[1] == second
)
1639 return (*c
)[2] & 0xFF;
1646 nc
= check_compose(second
, first
);
1648 nc
= check_compose(toupper(first
), toupper(second
));
1650 nc
= check_compose(toupper(second
), toupper(first
));
1658 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1659 * codes. Returns number of bytes used or zero to drop the message
1660 * or -1 to forward the message to windows.
1662 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
, unsigned char *output
) {
1664 int scan
, left_alt
= 0, key_down
, shift_state
;
1666 unsigned char * p
= output
;
1668 static WORD keys
[3];
1669 static int compose_state
= 0;
1670 static int compose_char
= 0;
1671 static WPARAM compose_key
= 0;
1673 r
= GetKeyboardState(keystate
);
1674 if (!r
) memset(keystate
, 0, sizeof(keystate
));
1677 /* Note if AltGr was pressed and if it was used as a compose key */
1678 if (wParam
== VK_MENU
&& (HIWORD(lParam
)&KF_EXTENDED
))
1680 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
1681 if (!compose_state
) compose_key
= wParam
;
1683 if (wParam
== VK_APPS
&& !compose_state
)
1684 compose_key
= wParam
;
1686 if (wParam
== compose_key
)
1688 if (compose_state
== 0 && (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
1690 else if (compose_state
== 1 && (HIWORD(lParam
)&KF_UP
))
1695 else if (compose_state
==1 && wParam
!= VK_CONTROL
)
1698 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1699 if ( (cfg
.funky_type
== 0 || (cfg
.funky_type
== 1 && app_keypad_keys
))
1700 && wParam
==VK_NUMLOCK
&& !(keystate
[VK_SHIFT
]&0x80)) {
1702 wParam
= VK_EXECUTE
;
1704 /* UnToggle NUMLock */
1705 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
1706 keystate
[VK_NUMLOCK
] ^= 1;
1709 /* And write back the 'adjusted' state */
1710 SetKeyboardState (keystate
);
1713 /* Disable Auto repeat if required */
1714 if (repeat_off
&& (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
)
1717 if ((HIWORD(lParam
)&KF_ALTDOWN
) && (keystate
[VK_RMENU
]&0x80) == 0)
1720 key_down
= ((HIWORD(lParam
)&KF_UP
)==0);
1722 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1723 if (left_alt
&& (keystate
[VK_CONTROL
]&0x80))
1724 keystate
[VK_MENU
] = 0;
1726 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
1727 shift_state
= ((keystate
[VK_SHIFT
]&0x80)!=0)
1728 + ((keystate
[VK_CONTROL
]&0x80)!=0)*2;
1731 * Record that we pressed key so the scroll window can be reset, but
1732 * be careful to avoid Shift-UP/Down
1734 if( wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
1738 /* Make sure we're not pasting */
1739 if (key_down
) term_nopaste();
1741 if (compose_state
>1 && left_alt
) compose_state
= 0;
1743 /* Sanitize the number pad if not using a PC NumPad */
1744 if( left_alt
|| (app_keypad_keys
&& cfg
.funky_type
!= 2)
1745 || cfg
.nethack_keypad
|| compose_state
)
1747 if ((HIWORD(lParam
)&KF_EXTENDED
) == 0)
1752 case VK_INSERT
: nParam
= VK_NUMPAD0
; break;
1753 case VK_END
: nParam
= VK_NUMPAD1
; break;
1754 case VK_DOWN
: nParam
= VK_NUMPAD2
; break;
1755 case VK_NEXT
: nParam
= VK_NUMPAD3
; break;
1756 case VK_LEFT
: nParam
= VK_NUMPAD4
; break;
1757 case VK_CLEAR
: nParam
= VK_NUMPAD5
; break;
1758 case VK_RIGHT
: nParam
= VK_NUMPAD6
; break;
1759 case VK_HOME
: nParam
= VK_NUMPAD7
; break;
1760 case VK_UP
: nParam
= VK_NUMPAD8
; break;
1761 case VK_PRIOR
: nParam
= VK_NUMPAD9
; break;
1762 case VK_DELETE
: nParam
= VK_DECIMAL
; break;
1766 if (keystate
[VK_NUMLOCK
]&1) shift_state
|= 1;
1772 /* If a key is pressed and AltGr is not active */
1773 if (key_down
&& (keystate
[VK_RMENU
]&0x80) == 0 && !compose_state
)
1775 /* Okay, prepare for most alts then ...*/
1776 if (left_alt
) *p
++ = '\033';
1778 /* Lets see if it's a pattern we know all about ... */
1779 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
1780 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
1783 if (wParam
== VK_NEXT
&& shift_state
== 1) {
1784 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
1787 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
1790 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
1792 SendMessage (hwnd
, WM_IGNORE_KEYMENU
, FALSE
, 0);
1793 SendMessage (hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
1794 SendMessage (hwnd
, WM_IGNORE_KEYMENU
, TRUE
, 0);
1798 /* Nethack keypad */
1799 if (cfg
.nethack_keypad
&& !left_alt
) {
1801 case VK_NUMPAD1
: *p
++ = shift_state ?
'B': 'b'; return p
-output
;
1802 case VK_NUMPAD2
: *p
++ = shift_state ?
'J': 'j'; return p
-output
;
1803 case VK_NUMPAD3
: *p
++ = shift_state ?
'N': 'n'; return p
-output
;
1804 case VK_NUMPAD4
: *p
++ = shift_state ?
'H': 'h'; return p
-output
;
1805 case VK_NUMPAD5
: *p
++ = shift_state ?
'.': '.'; return p
-output
;
1806 case VK_NUMPAD6
: *p
++ = shift_state ?
'L': 'l'; return p
-output
;
1807 case VK_NUMPAD7
: *p
++ = shift_state ?
'Y': 'y'; return p
-output
;
1808 case VK_NUMPAD8
: *p
++ = shift_state ?
'K': 'k'; return p
-output
;
1809 case VK_NUMPAD9
: *p
++ = shift_state ?
'U': 'u'; return p
-output
;
1813 /* Application Keypad */
1817 if ( cfg
.funky_type
== 0 ||
1818 ( cfg
.funky_type
== 1 && app_keypad_keys
)) switch(wParam
) {
1819 case VK_EXECUTE
: if (app_keypad_keys
) xkey
= 'P'; break;
1820 case VK_DIVIDE
: xkey
= 'Q'; break;
1821 case VK_MULTIPLY
:xkey
= 'R'; break;
1822 case VK_SUBTRACT
:xkey
= 'S'; break;
1824 if(app_keypad_keys
) switch(wParam
) {
1825 case VK_NUMPAD0
: xkey
= 'p'; break;
1826 case VK_NUMPAD1
: xkey
= 'q'; break;
1827 case VK_NUMPAD2
: xkey
= 'r'; break;
1828 case VK_NUMPAD3
: xkey
= 's'; break;
1829 case VK_NUMPAD4
: xkey
= 't'; break;
1830 case VK_NUMPAD5
: xkey
= 'u'; break;
1831 case VK_NUMPAD6
: xkey
= 'v'; break;
1832 case VK_NUMPAD7
: xkey
= 'w'; break;
1833 case VK_NUMPAD8
: xkey
= 'x'; break;
1834 case VK_NUMPAD9
: xkey
= 'y'; break;
1836 case VK_DECIMAL
: xkey
= 'n'; break;
1837 case VK_ADD
: if(shift_state
) xkey
= 'm';
1841 if (HIWORD(lParam
)&KF_EXTENDED
)
1849 if (xkey
>='P' && xkey
<='S')
1850 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
1852 p
+= sprintf((char *)p
, "\x1B?%c", xkey
);
1855 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
1860 if (wParam
== VK_BACK
&& shift_state
== 0 ) /* Backspace */
1862 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
1865 if (wParam
== VK_TAB
&& shift_state
== 1 ) /* Shift tab */
1867 *p
++ = 0x1B; *p
++ = '['; *p
++ = 'Z'; return p
- output
;
1869 if (wParam
== VK_SPACE
&& shift_state
== 2 ) /* Ctrl-Space */
1871 *p
++ = 0; return p
- output
;
1873 if (wParam
== VK_SPACE
&& shift_state
== 3 ) /* Ctrl-Shift-Space */
1875 *p
++ = 160; return p
- output
;
1877 if (wParam
== VK_CANCEL
&& shift_state
== 2 ) /* Ctrl-Break */
1879 *p
++ = 3; return p
- output
;
1881 /* Control-2 to Control-8 are special */
1882 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8')
1884 *p
++ = "\000\033\034\035\036\037\177"[wParam
-'2'];
1887 if (shift_state
== 2 && wParam
== 0xBD) {
1891 if (shift_state
== 2 && wParam
== 0xDF) {
1895 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
1896 *p
++ = '\r'; *p
++ = '\n';
1901 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1902 * for integer decimal nn.)
1904 * We also deal with the weird ones here. Linux VCs replace F1
1905 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1906 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1911 case VK_F1
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11); break;
1912 case VK_F2
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12); break;
1913 case VK_F3
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13); break;
1914 case VK_F4
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14); break;
1915 case VK_F5
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15); break;
1916 case VK_F6
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17); break;
1917 case VK_F7
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18); break;
1918 case VK_F8
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19); break;
1919 case VK_F9
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20); break;
1920 case VK_F10
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21); break;
1921 case VK_F11
: code
= 23; break;
1922 case VK_F12
: code
= 24; break;
1923 case VK_F13
: code
= 25; break;
1924 case VK_F14
: code
= 26; break;
1925 case VK_F15
: code
= 28; break;
1926 case VK_F16
: code
= 29; break;
1927 case VK_F17
: code
= 31; break;
1928 case VK_F18
: code
= 32; break;
1929 case VK_F19
: code
= 33; break;
1930 case VK_F20
: code
= 34; break;
1931 case VK_HOME
: code
= 1; break;
1932 case VK_INSERT
: code
= 2; break;
1933 case VK_DELETE
: code
= 3; break;
1934 case VK_END
: code
= 4; break;
1935 case VK_PRIOR
: code
= 5; break;
1936 case VK_NEXT
: code
= 6; break;
1938 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
1939 p
+= sprintf((char *)p
, "\x1B[[%c", code
+ 'A' - 11);
1942 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
1943 p
+= sprintf((char *)p
, "\x1BO%c", code
+ 'P' - 11);
1946 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
1947 p
+= sprintf((char *)p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
1951 p
+= sprintf((char *)p
, "\x1B[%d~", code
);
1956 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
1957 * some reason seems to send VK_CLEAR to Windows...).
1962 case VK_UP
: xkey
= 'A'; break;
1963 case VK_DOWN
: xkey
= 'B'; break;
1964 case VK_RIGHT
: xkey
= 'C'; break;
1965 case VK_LEFT
: xkey
= 'D'; break;
1966 case VK_CLEAR
: xkey
= 'G'; break;
1971 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
1972 else if (app_cursor_keys
)
1973 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
1975 p
+= sprintf((char *)p
, "\x1B[%c", xkey
);
1981 /* Okay we've done everything interesting; let windows deal with
1982 * the boring stuff */
1984 BOOL capsOn
=keystate
[VK_CAPITAL
] !=0;
1986 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
1987 if(cfg
.xlat_capslockcyr
)
1988 keystate
[VK_CAPITAL
] = 0;
1990 r
= ToAscii (wParam
, scan
, keystate
, keys
, 0);
1996 unsigned char ch
= (unsigned char)keys
[i
];
1998 if (compose_state
==2 && (ch
&0x80) == 0 && ch
>' ') {
2003 if (compose_state
==3 && (ch
&0x80) == 0 && ch
>' ') {
2007 if ((nc
=check_compose(compose_char
,ch
)) == -1)
2012 *p
++ = xlat_kbd2tty((unsigned char)nc
);
2018 if( left_alt
&& key_down
) *p
++ = '\033';
2024 ch
= xlat_latkbd2win(ch
);
2025 *p
++ = xlat_kbd2tty(ch
);
2029 /* This is so the ALT-Numpad and dead keys work correctly. */
2036 /* This stops ALT press-release doing a 'COMMAND MENU' function */
2038 if (message
== WM_SYSKEYUP
&& wParam
== VK_MENU
)
2040 keystate
[VK_MENU
] = 0;
2048 void set_title (char *title
) {
2049 sfree (window_name
);
2050 window_name
= smalloc(1+strlen(title
));
2051 strcpy (window_name
, title
);
2052 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
2053 SetWindowText (hwnd
, title
);
2056 void set_icon (char *title
) {
2058 icon_name
= smalloc(1+strlen(title
));
2059 strcpy (icon_name
, title
);
2060 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
2061 SetWindowText (hwnd
, title
);
2064 void set_sbar (int total
, int start
, int page
) {
2067 if (!cfg
.scrollbar
) return;
2069 si
.cbSize
= sizeof(si
);
2070 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
2072 si
.nMax
= total
- 1;
2076 SetScrollInfo (hwnd
, SB_VERT
, &si
, TRUE
);
2079 Context
get_ctx(void) {
2084 SelectPalette (hdc
, pal
, FALSE
);
2090 void free_ctx (Context ctx
) {
2091 SelectPalette (ctx
, GetStockObject (DEFAULT_PALETTE
), FALSE
);
2092 ReleaseDC (hwnd
, ctx
);
2095 static void real_palette_set (int n
, int r
, int g
, int b
) {
2097 logpal
->palPalEntry
[n
].peRed
= r
;
2098 logpal
->palPalEntry
[n
].peGreen
= g
;
2099 logpal
->palPalEntry
[n
].peBlue
= b
;
2100 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
2101 colours
[n
] = PALETTERGB(r
, g
, b
);
2102 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2104 colours
[n
] = RGB(r
, g
, b
);
2107 void palette_set (int n
, int r
, int g
, int b
) {
2108 static const int first
[21] = {
2109 0, 2, 4, 6, 8, 10, 12, 14,
2110 1, 3, 5, 7, 9, 11, 13, 15,
2113 real_palette_set (first
[n
], r
, g
, b
);
2115 real_palette_set (first
[n
]+1, r
, g
, b
);
2117 HDC hdc
= get_ctx();
2118 UnrealizeObject (pal
);
2119 RealizePalette (hdc
);
2124 void palette_reset (void) {
2127 for (i
= 0; i
< NCOLOURS
; i
++) {
2129 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
2130 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
2131 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
2132 logpal
->palPalEntry
[i
].peFlags
= 0;
2133 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
2134 defpal
[i
].rgbtGreen
,
2135 defpal
[i
].rgbtBlue
);
2137 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
2138 defpal
[i
].rgbtGreen
,
2139 defpal
[i
].rgbtBlue
);
2144 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2146 RealizePalette (hdc
);
2151 void write_clip (void *data
, int len
) {
2155 clipdata
= GlobalAlloc (GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
2158 lock
= GlobalLock (clipdata
);
2161 memcpy (lock
, data
, len
);
2162 ((unsigned char *) lock
) [len
] = 0;
2163 GlobalUnlock (clipdata
);
2165 SendMessage (hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
2166 if (OpenClipboard (hwnd
)) {
2168 SetClipboardData (CF_TEXT
, clipdata
);
2171 GlobalFree (clipdata
);
2172 SendMessage (hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
2175 void get_clip (void **p
, int *len
) {
2176 static HGLOBAL clipdata
= NULL
;
2180 GlobalUnlock (clipdata
);
2184 if (OpenClipboard (NULL
)) {
2185 clipdata
= GetClipboardData (CF_TEXT
);
2188 *p
= GlobalLock (clipdata
);
2202 * Move `lines' lines from position `from' to position `to' in the
2205 void optimised_move (int to
, int from
, int lines
) {
2209 min
= (to
< from ? to
: from
);
2210 max
= to
+ from
- min
;
2212 r
.left
= 0; r
.right
= cols
* font_width
;
2213 r
.top
= min
* font_height
; r
.bottom
= (max
+lines
) * font_height
;
2214 ScrollWindow (hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
2218 * Print a message box and perform a fatal exit.
2220 void fatalbox(char *fmt
, ...) {
2225 vsprintf(stuff
, fmt
, ap
);
2227 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
2234 void beep(int errorbeep
) {
2235 static long last_beep
= 0;
2236 long now
, beep_diff
;
2238 now
= GetTickCount();
2239 beep_diff
= now
-last_beep
;
2241 /* Make sure we only respond to one beep per packet or so */
2242 if (beep_diff
>=0 && beep_diff
<50)
2246 MessageBeep(MB_ICONHAND
);
2250 last_beep
= GetTickCount();