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 static Ldisc
*real_ldisc
;
87 void begin_session(void) {
91 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
) {
92 static char appname
[] = "PuTTY";
97 int guess_width
, guess_height
;
100 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
102 winsock_ver
= MAKEWORD(1, 1);
103 if (WSAStartup(winsock_ver
, &wsadata
)) {
104 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
105 MB_OK
| MB_ICONEXCLAMATION
);
108 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
109 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
110 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
114 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
116 InitCommonControls();
119 * Process the command line.
124 default_protocol
= DEFAULT_PROTOCOL
;
125 default_port
= DEFAULT_PORT
;
130 while (*p
&& isspace(*p
)) p
++;
133 * Process command line options first. Yes, this can be
134 * done better, and it will be as soon as I have the
138 char *q
= p
+ strcspn(p
, " \t");
141 tolower(p
[0]) == 's' &&
142 tolower(p
[1]) == 's' &&
143 tolower(p
[2]) == 'h') {
144 default_protocol
= cfg
.protocol
= PROT_SSH
;
145 default_port
= cfg
.port
= 22;
146 } else if (q
== p
+ 3 &&
147 tolower(p
[0]) == 'l' &&
148 tolower(p
[1]) == 'o' &&
149 tolower(p
[2]) == 'g') {
150 logfile
= "putty.log";
152 p
= q
+ strspn(q
, " \t");
156 * An initial @ means to activate a saved session.
160 if (!*cfg
.host
&& !do_config()) {
164 } else if (*p
== '&') {
166 * An initial & means we've been given a command line
167 * containing the hex value of a HANDLE for a file
168 * mapping object, which we must then extract as a
173 if (sscanf(p
+1, "%p", &filemap
) == 1 &&
174 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
175 0, 0, sizeof(Config
))) != NULL
) {
178 CloseHandle(filemap
);
179 } else if (!do_config()) {
186 * If the hostname starts with "telnet:", set the
187 * protocol to Telnet and process the string as a
190 if (!strncmp(q
, "telnet:", 7)) {
194 if (q
[0] == '/' && q
[1] == '/')
196 cfg
.protocol
= PROT_TELNET
;
198 while (*p
&& *p
!= ':' && *p
!= '/') p
++;
206 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
207 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
209 while (*p
&& !isspace(*p
)) p
++;
212 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
213 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
214 while (*p
&& isspace(*p
)) p
++;
229 * Select protocol. This is farmed out into a table in a
230 * separate file to enable an ssh-free variant.
235 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
236 if (backends
[i
].protocol
== cfg
.protocol
) {
237 back
= backends
[i
].backend
;
241 MessageBox(NULL
, "Unsupported protocol number found",
242 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
248 real_ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
249 /* To start with, we use the simple line discipline, so we can
250 * type passwords etc without fear of them being echoed... */
251 ldisc
= &ldisc_simple
;
255 wndclass
.lpfnWndProc
= WndProc
;
256 wndclass
.cbClsExtra
= 0;
257 wndclass
.cbWndExtra
= 0;
258 wndclass
.hInstance
= inst
;
259 wndclass
.hIcon
= LoadIcon (inst
,
260 MAKEINTRESOURCE(IDI_MAINICON
));
261 wndclass
.hCursor
= LoadCursor (NULL
, IDC_IBEAM
);
262 wndclass
.hbrBackground
= GetStockObject (BLACK_BRUSH
);
263 wndclass
.lpszMenuName
= NULL
;
264 wndclass
.lpszClassName
= appname
;
266 RegisterClass (&wndclass
);
271 savelines
= cfg
.savelines
;
277 * Guess some defaults for the window size. This all gets
278 * updated later, so we don't really care too much. However, we
279 * do want the font width/height guesses to correspond to a
280 * large font rather than a small one...
287 term_size (cfg
.height
, cfg
.width
, cfg
.savelines
);
288 guess_width
= extra_width
+ font_width
* cols
;
289 guess_height
= extra_height
+ font_height
* rows
;
292 HWND w
= GetDesktopWindow();
293 GetWindowRect (w
, &r
);
294 if (guess_width
> r
.right
- r
.left
)
295 guess_width
= r
.right
- r
.left
;
296 if (guess_height
> r
.bottom
- r
.top
)
297 guess_height
= r
.bottom
- r
.top
;
301 int winmode
= WS_OVERLAPPEDWINDOW
|WS_VSCROLL
;
302 if (!cfg
.scrollbar
) winmode
&= ~(WS_VSCROLL
);
303 if (cfg
.locksize
) winmode
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
304 hwnd
= CreateWindow (appname
, appname
,
306 CW_USEDEFAULT
, CW_USEDEFAULT
,
307 guess_width
, guess_height
,
308 NULL
, NULL
, inst
, NULL
);
312 * Initialise the fonts, simultaneously correcting the guesses
313 * for font_{width,height}.
315 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
320 * Correct the guesses for extra_{width,height}.
324 GetWindowRect (hwnd
, &wr
);
325 GetClientRect (hwnd
, &cr
);
326 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
327 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
331 * Resize the window, now we know what size we _really_ want it
334 guess_width
= extra_width
+ font_width
* cols
;
335 guess_height
= extra_height
+ font_height
* rows
;
336 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
337 SetWindowPos (hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
338 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
341 * Initialise the scroll bar.
346 si
.cbSize
= sizeof(si
);
347 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
352 SetScrollInfo (hwnd
, SB_VERT
, &si
, FALSE
);
356 * Start up the telnet connection.
363 error
= back
->init (hwnd
, cfg
.host
, cfg
.port
, &realhost
);
365 sprintf(msg
, "Unable to open connection:\n%s", error
);
366 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
369 window_name
= icon_name
= NULL
;
370 sprintf(msg
, "%s - PuTTY", realhost
);
375 session_closed
= FALSE
;
378 * Set up the input and output buffers.
381 outbuf_reap
= outbuf_head
= 0;
384 * Prepare the mouse handler.
386 lastact
= MA_NOTHING
;
387 lastbtn
= MB_NOTHING
;
388 dbltime
= GetDoubleClickTime();
391 * Set up the session-control options on the system menu.
394 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
398 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
399 if (cfg
.protocol
== PROT_TELNET
) {
401 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
402 AppendMenu (p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
403 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
404 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
405 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
406 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
407 AppendMenu (p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
408 AppendMenu (p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
409 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
410 AppendMenu (p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
411 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
412 AppendMenu (p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
413 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
414 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
415 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
416 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
417 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
, "Telnet Command");
418 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
420 AppendMenu (m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
421 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
422 AppendMenu (m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session");
423 AppendMenu (m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
426 for (i
= 1 ; i
< ((nsessions
< 256) ? nsessions
: 256) ; i
++)
427 AppendMenu (s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
) , sessions
[i
]);
428 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
429 AppendMenu (m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings");
430 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
431 AppendMenu (m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
432 AppendMenu (m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
433 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
434 AppendMenu (m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
438 * Finally show the window!
440 ShowWindow (hwnd
, show
);
443 * Set the palette up.
449 has_focus
= (GetForegroundWindow() == hwnd
);
453 int timer_id
= 0, long_timer
= 0;
455 while (GetMessage (&msg
, NULL
, 0, 0) == 1) {
456 /* Sometimes DispatchMessage calls routines that use their own
457 * GetMessage loop, setup this timer so we get some control back.
459 * Also call term_update() from the timer so that if the host
460 * is sending data flat out we still do redraws.
462 if(timer_id
&& long_timer
) {
463 KillTimer(hwnd
, timer_id
);
464 long_timer
= timer_id
= 0;
467 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
468 DispatchMessage (&msg
);
470 /* This is too fast, but I'll leave it for now 'cause it shows
471 * how often term_update is called (far too often at times!)
475 /* Send the paste buffer if there's anything to send */
478 /* If there's nothing new in the queue then we can do everything
479 * we've delayed, reading the socket, writing, and repainting
482 if (!PeekMessage (&msg
, NULL
, 0, 0, PM_NOREMOVE
)) {
483 if (pending_netevent
) {
484 enact_pending_netevent();
489 if (!PeekMessage (&msg
, NULL
, 0, 0, PM_NOREMOVE
)) {
491 KillTimer(hwnd
, timer_id
);
498 timer_id
= SetTimer(hwnd
, 1, 2000, NULL
);
499 else if (cfg
.blinktext
)
500 timer_id
= SetTimer(hwnd
, 1, 250, NULL
);
502 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
515 DeleteObject(fonts
[i
]);
522 if (cfg
.protocol
== PROT_SSH
) {
533 * Print a message box and close the connection.
535 void connection_fatal(char *fmt
, ...) {
540 vsprintf(stuff
, fmt
, ap
);
542 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
543 if (cfg
.close_on_exit
)
546 session_closed
= TRUE
;
547 SetWindowText (hwnd
, "PuTTY (inactive)");
552 * Actually do the job requested by a WM_NETEVENT
554 static void enact_pending_netevent(void) {
556 static int reentering
= 0;
559 return; /* don't unpend the pending */
561 pending_netevent
= FALSE
;
564 i
= back
->msg (pend_netevent_wParam
, pend_netevent_lParam
);
569 switch (WSABASEERR
+ (-i
) % 10000) {
571 sprintf(buf
, "Connection reset by peer");
574 sprintf(buf
, "Unexpected network error %d", -i
);
577 connection_fatal(buf
);
580 if (cfg
.close_on_exit
)
583 session_closed
= TRUE
;
584 MessageBox(hwnd
, "Connection closed by remote host",
585 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
586 SetWindowText (hwnd
, "PuTTY (inactive)");
592 * Copy the colour palette from the configuration data into defpal.
593 * This is non-trivial because the colour indices are different.
595 static void cfgtopalette(void) {
597 static const int ww
[] = {
598 6, 7, 8, 9, 10, 11, 12, 13,
599 14, 15, 16, 17, 18, 19, 20, 21,
600 0, 1, 2, 3, 4, 4, 5, 5
603 for (i
=0; i
<24; i
++) {
605 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
606 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
607 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
612 * Set up the colour palette.
614 static void init_palette(void) {
616 HDC hdc
= GetDC (hwnd
);
618 if (cfg
.try_palette
&&
619 GetDeviceCaps (hdc
, RASTERCAPS
) & RC_PALETTE
) {
620 logpal
= smalloc(sizeof(*logpal
)
621 - sizeof(logpal
->palPalEntry
)
622 + NCOLOURS
* sizeof(PALETTEENTRY
));
623 logpal
->palVersion
= 0x300;
624 logpal
->palNumEntries
= NCOLOURS
;
625 for (i
= 0; i
< NCOLOURS
; i
++) {
626 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
627 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
628 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
629 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
631 pal
= CreatePalette (logpal
);
633 SelectPalette (hdc
, pal
, FALSE
);
634 RealizePalette (hdc
);
635 SelectPalette (hdc
, GetStockObject (DEFAULT_PALETTE
),
639 ReleaseDC (hwnd
, hdc
);
642 for (i
=0; i
<NCOLOURS
; i
++)
643 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
647 for(i
=0; i
<NCOLOURS
; i
++)
648 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
654 * Initialise all the fonts we will need. There may be as many as
655 * eight or as few as one. We also:
657 * - check the font width and height, correcting our guesses if
660 * - verify that the bold font is the same width as the ordinary
661 * one, and engage shadow bolding if not.
663 * - verify that the underlined font is the same width as the
664 * ordinary one (manual underlining by means of line drawing can
665 * be done in a pinch).
667 static void init_fonts(int pick_width
) {
672 int fw_dontcare
, fw_bold
;
681 if (cfg
.fontisbold
) {
682 fw_dontcare
= FW_BOLD
;
685 fw_dontcare
= FW_DONTCARE
;
691 font_height
= cfg
.fontheight
;
692 font_width
= pick_width
;
695 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
696 c, OUT_DEFAULT_PRECIS, \
697 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
698 FIXED_PITCH | FF_DONTCARE, cfg.font)
700 if (cfg
.vtmode
!= VT_OEMONLY
) {
701 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
703 SelectObject (hdc
, fonts
[FONT_NORMAL
]);
704 GetTextMetrics(hdc
, &tm
);
705 font_height
= tm
.tmHeight
;
706 font_width
= tm
.tmAveCharWidth
;
708 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
711 * Some fonts, e.g. 9-pt Courier, draw their underlines
712 * outside their character cell. We successfully prevent
713 * screen corruption by clipping the text output, but then
714 * we lose the underline completely. Here we try to work
715 * out whether this is such a font, and if it is, we set a
716 * flag that causes underlines to be drawn by hand.
718 * Having tried other more sophisticated approaches (such
719 * as examining the TEXTMETRIC structure or requesting the
720 * height of a string), I think we'll do this the brute
721 * force way: we create a small bitmap, draw an underlined
722 * space on it, and test to see whether any pixels are
723 * foreground-coloured. (Since we expect the underline to
724 * go all the way across the character cell, we only search
725 * down a single column of the bitmap, half way across.)
729 HBITMAP und_bm
, und_oldbm
;
733 und_dc
= CreateCompatibleDC(hdc
);
734 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
735 und_oldbm
= SelectObject(und_dc
, und_bm
);
736 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
737 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
738 SetTextColor (und_dc
, RGB(255,255,255));
739 SetBkColor (und_dc
, RGB(0,0,0));
740 SetBkMode (und_dc
, OPAQUE
);
741 ExtTextOut (und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
743 for (i
= 0; i
< font_height
; i
++) {
744 c
= GetPixel(und_dc
, font_width
/2, i
);
748 SelectObject(und_dc
, und_oldbm
);
749 DeleteObject(und_bm
);
751 font_needs_hand_underlining
= !gotit
;
754 if (bold_mode
== BOLD_FONT
) {
755 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
756 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
759 if (cfg
.vtmode
== VT_OEMANSI
) {
760 f(FONT_OEM
, OEM_CHARSET
, fw_dontcare
, FALSE
);
761 f(FONT_OEMUND
, OEM_CHARSET
, fw_dontcare
, TRUE
);
763 if (bold_mode
== BOLD_FONT
) {
764 f(FONT_OEMBOLD
, OEM_CHARSET
, fw_bold
, FALSE
);
765 f(FONT_OEMBOLDUND
, OEM_CHARSET
, fw_bold
, TRUE
);
771 f(FONT_OEM
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
773 SelectObject (hdc
, fonts
[FONT_OEM
]);
774 GetTextMetrics(hdc
, &tm
);
775 font_height
= tm
.tmHeight
;
776 font_width
= tm
.tmAveCharWidth
;
778 f(FONT_OEMUND
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
780 if (bold_mode
== BOLD_FONT
) {
781 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
782 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
787 descent
= tm
.tmAscent
+ 1;
788 if (descent
>= font_height
)
789 descent
= font_height
- 1;
790 firstchar
= tm
.tmFirstChar
;
792 for (i
=0; i
<8; i
++) {
794 if (SelectObject (hdc
, fonts
[i
]) &&
795 GetTextMetrics(hdc
, &tm
) )
796 fsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
802 ReleaseDC (hwnd
, hdc
);
804 /* ... This is wrong in OEM only mode */
805 if (fsize
[FONT_UNDERLINE
] != fsize
[FONT_NORMAL
] ||
806 (bold_mode
== BOLD_FONT
&&
807 fsize
[FONT_BOLDUND
] != fsize
[FONT_BOLD
])) {
809 DeleteObject (fonts
[FONT_UNDERLINE
]);
810 if (bold_mode
== BOLD_FONT
)
811 DeleteObject (fonts
[FONT_BOLDUND
]);
814 if (bold_mode
== BOLD_FONT
&&
815 fsize
[FONT_BOLD
] != fsize
[FONT_NORMAL
]) {
816 bold_mode
= BOLD_SHADOW
;
817 DeleteObject (fonts
[FONT_BOLD
]);
818 if (und_mode
== UND_FONT
)
819 DeleteObject (fonts
[FONT_BOLDUND
]);
823 /* With the fascist font painting it doesn't matter if the linedraw font
824 * isn't exactly the right size anymore so we don't have to check this.
826 if (cfg
.vtmode
== VT_OEMANSI
&& fsize
[FONT_OEM
] != fsize
[FONT_NORMAL
] ) {
827 if( cfg
.fontcharset
== OEM_CHARSET
)
829 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
830 "different sizes. Using OEM-only mode instead",
831 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
832 cfg
.vtmode
= VT_OEMONLY
;
834 else if( firstchar
< ' ' )
836 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
837 "different sizes. Using XTerm mode instead",
838 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
839 cfg
.vtmode
= VT_XWINDOWS
;
843 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
844 "different sizes. Using ISO8859-1 mode instead",
845 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
846 cfg
.vtmode
= VT_POORMAN
;
851 DeleteObject (fonts
[i
]);
857 void request_resize (int w
, int h
, int refont
) {
860 /* If the window is maximized supress resizing attempts */
861 if(IsZoomed(hwnd
)) return;
864 /* Don't do this in OEMANSI, you may get disable messages */
865 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132)
866 && cfg
.vtmode
!= VT_OEMANSI
)
868 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132))
871 /* If font width too big for screen should we shrink the font more ? */
873 font_width
= ((font_width
*cols
+w
/2)/w
);
880 DeleteObject(fonts
[i
]);
882 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
884 init_fonts(font_width
);
888 static int first_time
= 1;
894 /* Get the size of the screen */
895 if (GetClientRect(GetDesktopWindow(),&ss
))
896 /* first_time = 0 */;
897 else { first_time
= 2; break; }
899 /* Make sure the values are sane */
900 width
= (ss
.right
-ss
.left
-extra_width
) / font_width
;
901 height
= (ss
.bottom
-ss
.top
-extra_height
) / font_height
;
903 if (w
>width
) w
=width
;
904 if (h
>height
) h
=height
;
910 width
= extra_width
+ font_width
* w
;
911 height
= extra_height
+ font_height
* h
;
913 SetWindowPos (hwnd
, NULL
, 0, 0, width
, height
,
914 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
915 SWP_NOMOVE
| SWP_NOZORDER
);
918 static void click (Mouse_Button b
, int x
, int y
) {
919 int thistime
= GetMessageTime();
921 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
922 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
923 lastact
== MA_2CLK ? MA_3CLK
:
924 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
929 if (lastact
!= MA_NOTHING
)
930 term_mouse (b
, lastact
, x
, y
);
934 static LRESULT CALLBACK
WndProc (HWND hwnd
, UINT message
,
935 WPARAM wParam
, LPARAM lParam
) {
937 static int ignore_size
= FALSE
;
938 static int ignore_clip
= FALSE
;
939 static int ignore_keymenu
= TRUE
;
940 static int just_reconfigged
= FALSE
;
944 if (pending_netevent
)
945 enact_pending_netevent();
953 if (!cfg
.warn_on_close
|| session_closed
||
954 MessageBox(hwnd
, "Are you sure you want to close this session?",
955 "PuTTY Exit Confirmation",
956 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
963 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
966 return 0; /* don't put up system menu on Alt */
979 PROCESS_INFORMATION pi
;
980 HANDLE filemap
= NULL
;
982 if (wParam
== IDM_DUPSESS
) {
984 * Allocate a file-mapping memory chunk for the
987 SECURITY_ATTRIBUTES sa
;
990 sa
.nLength
= sizeof(sa
);
991 sa
.lpSecurityDescriptor
= NULL
;
992 sa
.bInheritHandle
= TRUE
;
993 filemap
= CreateFileMapping((HANDLE
)0xFFFFFFFF,
1000 p
= (Config
*)MapViewOfFile(filemap
,
1002 0, 0, sizeof(Config
));
1004 *p
= cfg
; /* structure copy */
1008 sprintf(c
, "putty &%p", filemap
);
1010 } else if (wParam
== IDM_SAVEDSESS
) {
1011 char *session
= sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1012 cl
= malloc(16 + strlen(session
)); /* 8, but play safe */
1014 cl
= NULL
; /* not a very important failure mode */
1016 sprintf(cl
, "putty @%s", session
);
1022 GetModuleFileName (NULL
, b
, sizeof(b
)-1);
1024 si
.lpReserved
= NULL
;
1025 si
.lpDesktop
= NULL
;
1029 si
.lpReserved2
= NULL
;
1030 CreateProcess (b
, cl
, NULL
, NULL
, TRUE
,
1031 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1034 CloseHandle(filemap
);
1040 if (!do_reconfig(hwnd
))
1042 just_reconfigged
= TRUE
;
1047 DeleteObject(fonts
[i
]);
1049 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1050 und_mode
= UND_FONT
;
1053 /* Telnet will change local echo -> remote if the remote asks */
1054 if (cfg
.protocol
!= PROT_TELNET
)
1055 ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
1063 /* Enable or disable the scroll bar, etc */
1065 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1068 if (cfg
.scrollbar
) nflg
|= WS_VSCROLL
;
1069 else nflg
&= ~WS_VSCROLL
;
1071 nflg
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1073 nflg
|= (WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1079 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1080 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1081 SetWindowPos(hwnd
, NULL
, 0,0,0,0,
1082 SWP_NOACTIVATE
|SWP_NOCOPYBITS
|
1083 SWP_NOMOVE
|SWP_NOSIZE
| SWP_NOZORDER
|
1086 GetWindowRect (hwnd
, &wr
);
1087 GetClientRect (hwnd
, &cr
);
1088 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1089 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1093 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1094 InvalidateRect(hwnd
, NULL
, TRUE
);
1095 SetWindowPos (hwnd
, NULL
, 0, 0,
1096 extra_width
+ font_width
* cfg
.width
,
1097 extra_height
+ font_height
* cfg
.height
,
1098 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1099 SWP_NOMOVE
| SWP_NOZORDER
);
1100 if (IsIconic(hwnd
)) {
1101 SetWindowText (hwnd
,
1102 cfg
.win_name_always ? window_name
: icon_name
);
1111 case IDM_TEL_AYT
: back
->special (TS_AYT
); break;
1112 case IDM_TEL_BRK
: back
->special (TS_BRK
); break;
1113 case IDM_TEL_SYNCH
: back
->special (TS_SYNCH
); break;
1114 case IDM_TEL_EC
: back
->special (TS_EC
); break;
1115 case IDM_TEL_EL
: back
->special (TS_EL
); break;
1116 case IDM_TEL_GA
: back
->special (TS_GA
); break;
1117 case IDM_TEL_NOP
: back
->special (TS_NOP
); break;
1118 case IDM_TEL_ABORT
: back
->special (TS_ABORT
); break;
1119 case IDM_TEL_AO
: back
->special (TS_AO
); break;
1120 case IDM_TEL_IP
: back
->special (TS_IP
); break;
1121 case IDM_TEL_SUSP
: back
->special (TS_SUSP
); break;
1122 case IDM_TEL_EOR
: back
->special (TS_EOR
); break;
1123 case IDM_TEL_EOF
: back
->special (TS_EOF
); break;
1128 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1129 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1134 #define X_POS(l) ((int)(short)LOWORD(l))
1135 #define Y_POS(l) ((int)(short)HIWORD(l))
1137 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1138 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1140 case WM_LBUTTONDOWN
:
1141 click (MB_SELECT
, TO_CHR_X(X_POS(lParam
)),
1142 TO_CHR_Y(Y_POS(lParam
)));
1146 term_mouse (MB_SELECT
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1147 TO_CHR_Y(Y_POS(lParam
)));
1150 case WM_MBUTTONDOWN
:
1152 click (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1153 TO_CHR_X(X_POS(lParam
)),
1154 TO_CHR_Y(Y_POS(lParam
)));
1157 term_mouse (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1158 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1159 TO_CHR_Y(Y_POS(lParam
)));
1162 case WM_RBUTTONDOWN
:
1164 click (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1165 TO_CHR_X(X_POS(lParam
)),
1166 TO_CHR_Y(Y_POS(lParam
)));
1169 term_mouse (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1170 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1171 TO_CHR_Y(Y_POS(lParam
)));
1176 * Add the mouse position and message time to the random
1177 * number noise, if we're using ssh.
1179 if (cfg
.protocol
== PROT_SSH
)
1180 noise_ultralight(lParam
);
1182 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1184 if (wParam
& MK_LBUTTON
)
1186 else if (wParam
& MK_MBUTTON
)
1187 b
= cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
;
1189 b
= cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
;
1190 term_mouse (b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1191 TO_CHR_Y(Y_POS(lParam
)));
1194 case WM_IGNORE_CLIP
:
1195 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1197 case WM_IGNORE_KEYMENU
:
1198 ignore_keymenu
= wParam
; /* do or don't ignore SC_KEYMENU */
1200 case WM_DESTROYCLIPBOARD
:
1203 ignore_clip
= FALSE
;
1208 hdc
= BeginPaint (hwnd
, &p
);
1210 SelectPalette (hdc
, pal
, TRUE
);
1211 RealizePalette (hdc
);
1213 term_paint (hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1214 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1215 SelectObject (hdc
, GetStockObject(SYSTEM_FONT
));
1216 SelectObject (hdc
, GetStockObject(WHITE_PEN
));
1217 EndPaint (hwnd
, &p
);
1221 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1222 * but the only one that's likely to try to overload us is FD_READ.
1223 * This means buffering just one is fine.
1225 if (pending_netevent
)
1226 enact_pending_netevent();
1228 pending_netevent
= TRUE
;
1229 pend_netevent_wParam
=wParam
;
1230 pend_netevent_lParam
=lParam
;
1242 case WM_IGNORE_SIZE
:
1243 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1245 case WM_ENTERSIZEMOVE
:
1248 case WM_EXITSIZEMOVE
:
1253 int width
, height
, w
, h
, ew
, eh
;
1254 LPRECT r
= (LPRECT
)lParam
;
1256 width
= r
->right
- r
->left
- extra_width
;
1257 height
= r
->bottom
- r
->top
- extra_height
;
1258 w
= (width
+ font_width
/2) / font_width
; if (w
< 1) w
= 1;
1259 h
= (height
+ font_height
/2) / font_height
; if (h
< 1) h
= 1;
1260 UpdateSizeTip(hwnd
, w
, h
);
1261 ew
= width
- w
* font_width
;
1262 eh
= height
- h
* font_height
;
1264 if (wParam
== WMSZ_LEFT
||
1265 wParam
== WMSZ_BOTTOMLEFT
||
1266 wParam
== WMSZ_TOPLEFT
)
1272 if (wParam
== WMSZ_TOP
||
1273 wParam
== WMSZ_TOPRIGHT
||
1274 wParam
== WMSZ_TOPLEFT
)
1284 /* break; (never reached) */
1286 if (wParam
== SIZE_MINIMIZED
) {
1287 SetWindowText (hwnd
,
1288 cfg
.win_name_always ? window_name
: icon_name
);
1291 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1292 SetWindowText (hwnd
, window_name
);
1294 int width
, height
, w
, h
;
1295 #if 0 /* we have fixed this using WM_SIZING now */
1299 width
= LOWORD(lParam
);
1300 height
= HIWORD(lParam
);
1301 w
= width
/ font_width
; if (w
< 1) w
= 1;
1302 h
= height
/ font_height
; if (h
< 1) h
= 1;
1303 #if 0 /* we have fixed this using WM_SIZING now */
1304 ew
= width
- w
* font_width
;
1305 eh
= height
- h
* font_height
;
1306 if (ew
!= 0 || eh
!= 0) {
1308 GetWindowRect (hwnd
, &r
);
1309 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1310 SetWindowPos (hwnd
, NULL
, 0, 0,
1311 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1312 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1315 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1317 term_size (h
, w
, cfg
.savelines
);
1319 just_reconfigged
= FALSE
;
1322 ignore_size
= FALSE
;
1325 switch (LOWORD(wParam
)) {
1326 case SB_BOTTOM
: term_scroll(-1, 0); break;
1327 case SB_TOP
: term_scroll(+1, 0); break;
1328 case SB_LINEDOWN
: term_scroll (0, +1); break;
1329 case SB_LINEUP
: term_scroll (0, -1); break;
1330 case SB_PAGEDOWN
: term_scroll (0, +rows
/2); break;
1331 case SB_PAGEUP
: term_scroll (0, -rows
/2); break;
1332 case SB_THUMBPOSITION
: case SB_THUMBTRACK
:
1333 term_scroll (1, HIWORD(wParam
)); break;
1336 case WM_PALETTECHANGED
:
1337 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1338 HDC hdc
= get_ctx();
1340 if (RealizePalette (hdc
) > 0)
1346 case WM_QUERYNEWPALETTE
:
1348 HDC hdc
= get_ctx();
1350 if (RealizePalette (hdc
) > 0)
1362 * Add the scan code and keypress timing to the random
1363 * number noise, if we're using ssh.
1365 if (cfg
.protocol
== PROT_SSH
)
1366 noise_ultralight(lParam
);
1369 * We don't do TranslateMessage since it disassociates the
1370 * resulting CHAR message from the KEYDOWN that sparked it,
1371 * which we occasionally don't want. Instead, we process
1372 * KEYDOWN, and call the Win32 translator functions so that
1373 * we get the translations under _our_ control.
1376 unsigned char buf
[20];
1379 len
= TranslateKey (message
, wParam
, lParam
, buf
);
1381 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1382 ldisc
->send (buf
, len
);
1388 * Nevertheless, we are prepared to deal with WM_CHAR
1389 * messages, should they crop up. So if someone wants to
1390 * post the things to us as part of a macro manoeuvre,
1391 * we're ready to cope.
1394 char c
= xlat_kbd2tty((unsigned char)wParam
);
1395 ldisc
->send (&c
, 1);
1400 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1404 * Draw a line of text in the window, at given character
1405 * coordinates, in given attributes.
1407 * We are allowed to fiddle with the contents of `text'.
1409 void do_text (Context ctx
, int x
, int y
, char *text
, int len
,
1410 unsigned long attr
, int lattr
) {
1412 int nfg
, nbg
, nfont
;
1415 int force_manual_underline
= 0;
1416 int fnt_width
= font_width
*(1+(lattr
!=LATTR_NORM
));
1417 static int *IpDx
= 0, IpDxLEN
= 0;;
1419 if (len
>IpDxLEN
|| IpDx
[0] != fnt_width
) {
1423 IpDx
= smalloc((len
+16)*sizeof(int));
1426 for(i
=0; i
<IpDxLEN
; i
++)
1427 IpDx
[i
] = fnt_width
;
1433 if (attr
& ATTR_ACTCURS
) {
1434 attr
&= (bold_mode
== BOLD_COLOURS ?
0x300200 : 0x300300);
1435 attr
^= ATTR_CUR_XOR
;
1439 if (cfg
.vtmode
== VT_OEMONLY
)
1443 * Map high-half characters in order to approximate ISO using
1444 * OEM character set. No characters are missing if the OEM codepage
1447 if (nfont
& FONT_OEM
) {
1449 for (i
=0; i
<len
; i
++)
1450 if (text
[i
] >= '\xA0' && text
[i
] <= '\xFF') {
1452 /* This is CP850 ... perfect translation */
1453 static const char oemhighhalf
[] =
1454 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1455 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1456 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1457 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1458 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1459 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1460 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1461 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1462 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1463 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1464 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1465 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1468 /* This is CP437 ... junk translation */
1469 static const unsigned char oemhighhalf
[] = {
1470 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1471 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1472 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1473 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1474 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1475 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1476 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1477 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1478 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1479 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1480 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1481 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1484 text
[i
] = oemhighhalf
[(unsigned char)text
[i
] - 0xA0];
1488 if (attr
& ATTR_GBCHR
) {
1491 * GB mapping: map # to pound, and everything else stays
1494 for (i
=0; i
<len
; i
++)
1496 text
[i
] = cfg
.vtmode
== VT_OEMONLY ?
'\x9C' : '\xA3';
1497 } else if (attr
& ATTR_LINEDRW
) {
1500 static const char poorman
[] =
1501 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1504 static const char oemmap_437
[] =
1505 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1506 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1509 static const char oemmap_850
[] =
1510 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1511 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1513 /* Poor windows font ... eg: windows courier */
1514 static const char oemmap
[] =
1515 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1516 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1519 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1520 * VT100 line drawing chars; everything else stays normal.
1522 switch (cfg
.vtmode
) {
1524 for (i
=0; i
<len
; i
++)
1525 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1526 text
[i
] += '\x01' - '\x60';
1529 /* Make sure we actually have an OEM font */
1530 if (fonts
[nfont
|FONT_OEM
]) {
1533 for (i
=0; i
<len
; i
++)
1534 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1535 text
[i
] = oemmap
[(unsigned char)text
[i
] - 0x60];
1539 for (i
=0; i
<len
; i
++)
1540 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1541 text
[i
] = poorman
[(unsigned char)text
[i
] - 0x60];
1546 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
1547 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
1548 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
1550 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
1551 nfont
|= FONT_UNDERLINE
;
1554 if (nfont
&FONT_UNDERLINE
)
1555 force_manual_underline
= 1;
1556 /* Don't do the same for manual bold, it could be bad news. */
1558 nfont
&= ~(FONT_BOLD
|FONT_UNDERLINE
);
1560 if (font_needs_hand_underlining
&& (attr
& ATTR_UNDER
))
1561 force_manual_underline
= 1;
1562 if (attr
& ATTR_REVERSE
) {
1563 t
= nfg
; nfg
= nbg
; nbg
= t
;
1565 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
1567 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
1571 SelectObject (hdc
, fonts
[nfont
]);
1572 SetTextColor (hdc
, fg
);
1573 SetBkColor (hdc
, bg
);
1574 SetBkMode (hdc
, OPAQUE
);
1577 line_box
.right
= x
+fnt_width
*len
;
1578 line_box
.bottom
= y
+font_height
;
1579 ExtTextOut (hdc
, x
, y
, ETO_CLIPPED
|ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
1580 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
1581 SetBkMode (hdc
, TRANSPARENT
);
1583 /* GRR: This draws the character outside it's box and can leave
1584 * 'droppings' even with the clip box! I suppose I could loop it
1585 * one character at a time ... yuk.
1587 * Or ... I could do a test print with "W", and use +1 or -1 for this
1588 * shift depending on if the leftmost column is blank...
1590 ExtTextOut (hdc
, x
-1, y
, ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
1592 if (force_manual_underline
||
1593 (und_mode
== UND_LINE
&& (attr
& ATTR_UNDER
))) {
1595 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, fg
));
1596 MoveToEx (hdc
, x
, y
+descent
, NULL
);
1597 LineTo (hdc
, x
+len
*fnt_width
, y
+descent
);
1598 oldpen
= SelectObject (hdc
, oldpen
);
1599 DeleteObject (oldpen
);
1601 if (attr
& ATTR_PASCURS
) {
1604 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
1605 pts
[2].x
= pts
[3].x
= x
+fnt_width
-1;
1606 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
1607 pts
[1].y
= pts
[2].y
= y
+font_height
-1;
1608 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
1609 Polyline (hdc
, pts
, 5);
1610 oldpen
= SelectObject (hdc
, oldpen
);
1611 DeleteObject (oldpen
);
1615 static int check_compose(int first
, int second
) {
1617 static char * composetbl
[] = {
1618 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1619 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1620 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1621 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1622 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1623 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1624 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1625 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1626 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1627 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1628 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1629 "\"uü", "'yý", "htþ", "\"yÿ",
1633 static int recurse
= 0;
1640 sprintf(buf
, "cc(%d,%d)", first
, second
);
1645 for(c
=composetbl
; *c
; c
++) {
1646 if( (*c
)[0] == first
&& (*c
)[1] == second
)
1648 return (*c
)[2] & 0xFF;
1655 nc
= check_compose(second
, first
);
1657 nc
= check_compose(toupper(first
), toupper(second
));
1659 nc
= check_compose(toupper(second
), toupper(first
));
1667 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1668 * codes. Returns number of bytes used or zero to drop the message
1669 * or -1 to forward the message to windows.
1671 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
, unsigned char *output
) {
1673 int scan
, left_alt
= 0, key_down
, shift_state
;
1675 unsigned char * p
= output
;
1677 static WORD keys
[3];
1678 static int compose_state
= 0;
1679 static int compose_char
= 0;
1680 static WPARAM compose_key
= 0;
1682 r
= GetKeyboardState(keystate
);
1683 if (!r
) memset(keystate
, 0, sizeof(keystate
));
1686 /* Note if AltGr was pressed and if it was used as a compose key */
1687 if (wParam
== VK_MENU
&& (HIWORD(lParam
)&KF_EXTENDED
))
1689 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
1690 if (!compose_state
) compose_key
= wParam
;
1692 if (wParam
== VK_APPS
&& !compose_state
)
1693 compose_key
= wParam
;
1695 if (wParam
== compose_key
)
1697 if (compose_state
== 0 && (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
1699 else if (compose_state
== 1 && (HIWORD(lParam
)&KF_UP
))
1704 else if (compose_state
==1 && wParam
!= VK_CONTROL
)
1707 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1708 if ( (cfg
.funky_type
== 0 || (cfg
.funky_type
== 1 && app_keypad_keys
))
1709 && wParam
==VK_NUMLOCK
&& !(keystate
[VK_SHIFT
]&0x80)) {
1711 wParam
= VK_EXECUTE
;
1713 /* UnToggle NUMLock */
1714 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
1715 keystate
[VK_NUMLOCK
] ^= 1;
1718 /* And write back the 'adjusted' state */
1719 SetKeyboardState (keystate
);
1722 /* Disable Auto repeat if required */
1723 if (repeat_off
&& (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
)
1726 if ((HIWORD(lParam
)&KF_ALTDOWN
) && (keystate
[VK_RMENU
]&0x80) == 0)
1729 key_down
= ((HIWORD(lParam
)&KF_UP
)==0);
1731 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1732 if (left_alt
&& (keystate
[VK_CONTROL
]&0x80))
1733 keystate
[VK_MENU
] = 0;
1735 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
1736 shift_state
= ((keystate
[VK_SHIFT
]&0x80)!=0)
1737 + ((keystate
[VK_CONTROL
]&0x80)!=0)*2;
1740 * Record that we pressed key so the scroll window can be reset, but
1741 * be careful to avoid Shift-UP/Down
1743 if( wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
1747 /* Make sure we're not pasting */
1748 if (key_down
) term_nopaste();
1750 if (compose_state
>1 && left_alt
) compose_state
= 0;
1752 /* Sanitize the number pad if not using a PC NumPad */
1753 if( left_alt
|| (app_keypad_keys
&& cfg
.funky_type
!= 2)
1754 || cfg
.nethack_keypad
|| compose_state
)
1756 if ((HIWORD(lParam
)&KF_EXTENDED
) == 0)
1761 case VK_INSERT
: nParam
= VK_NUMPAD0
; break;
1762 case VK_END
: nParam
= VK_NUMPAD1
; break;
1763 case VK_DOWN
: nParam
= VK_NUMPAD2
; break;
1764 case VK_NEXT
: nParam
= VK_NUMPAD3
; break;
1765 case VK_LEFT
: nParam
= VK_NUMPAD4
; break;
1766 case VK_CLEAR
: nParam
= VK_NUMPAD5
; break;
1767 case VK_RIGHT
: nParam
= VK_NUMPAD6
; break;
1768 case VK_HOME
: nParam
= VK_NUMPAD7
; break;
1769 case VK_UP
: nParam
= VK_NUMPAD8
; break;
1770 case VK_PRIOR
: nParam
= VK_NUMPAD9
; break;
1771 case VK_DELETE
: nParam
= VK_DECIMAL
; break;
1775 if (keystate
[VK_NUMLOCK
]&1) shift_state
|= 1;
1781 /* If a key is pressed and AltGr is not active */
1782 if (key_down
&& (keystate
[VK_RMENU
]&0x80) == 0 && !compose_state
)
1784 /* Okay, prepare for most alts then ...*/
1785 if (left_alt
) *p
++ = '\033';
1787 /* Lets see if it's a pattern we know all about ... */
1788 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
1789 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
1792 if (wParam
== VK_NEXT
&& shift_state
== 1) {
1793 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
1796 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
1799 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
1801 SendMessage (hwnd
, WM_IGNORE_KEYMENU
, FALSE
, 0);
1802 SendMessage (hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
1803 SendMessage (hwnd
, WM_IGNORE_KEYMENU
, TRUE
, 0);
1807 /* Nethack keypad */
1808 if (cfg
.nethack_keypad
&& !left_alt
) {
1810 case VK_NUMPAD1
: *p
++ = shift_state ?
'B': 'b'; return p
-output
;
1811 case VK_NUMPAD2
: *p
++ = shift_state ?
'J': 'j'; return p
-output
;
1812 case VK_NUMPAD3
: *p
++ = shift_state ?
'N': 'n'; return p
-output
;
1813 case VK_NUMPAD4
: *p
++ = shift_state ?
'H': 'h'; return p
-output
;
1814 case VK_NUMPAD5
: *p
++ = shift_state ?
'.': '.'; return p
-output
;
1815 case VK_NUMPAD6
: *p
++ = shift_state ?
'L': 'l'; return p
-output
;
1816 case VK_NUMPAD7
: *p
++ = shift_state ?
'Y': 'y'; return p
-output
;
1817 case VK_NUMPAD8
: *p
++ = shift_state ?
'K': 'k'; return p
-output
;
1818 case VK_NUMPAD9
: *p
++ = shift_state ?
'U': 'u'; return p
-output
;
1822 /* Application Keypad */
1826 if ( cfg
.funky_type
== 0 ||
1827 ( cfg
.funky_type
== 1 && app_keypad_keys
)) switch(wParam
) {
1828 case VK_EXECUTE
: if (app_keypad_keys
) xkey
= 'P'; break;
1829 case VK_DIVIDE
: xkey
= 'Q'; break;
1830 case VK_MULTIPLY
:xkey
= 'R'; break;
1831 case VK_SUBTRACT
:xkey
= 'S'; break;
1833 if(app_keypad_keys
) switch(wParam
) {
1834 case VK_NUMPAD0
: xkey
= 'p'; break;
1835 case VK_NUMPAD1
: xkey
= 'q'; break;
1836 case VK_NUMPAD2
: xkey
= 'r'; break;
1837 case VK_NUMPAD3
: xkey
= 's'; break;
1838 case VK_NUMPAD4
: xkey
= 't'; break;
1839 case VK_NUMPAD5
: xkey
= 'u'; break;
1840 case VK_NUMPAD6
: xkey
= 'v'; break;
1841 case VK_NUMPAD7
: xkey
= 'w'; break;
1842 case VK_NUMPAD8
: xkey
= 'x'; break;
1843 case VK_NUMPAD9
: xkey
= 'y'; break;
1845 case VK_DECIMAL
: xkey
= 'n'; break;
1846 case VK_ADD
: if(shift_state
) xkey
= 'm';
1850 if (HIWORD(lParam
)&KF_EXTENDED
)
1858 if (xkey
>='P' && xkey
<='S')
1859 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
1861 p
+= sprintf((char *)p
, "\x1B?%c", xkey
);
1864 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
1869 if (wParam
== VK_BACK
&& shift_state
== 0 ) /* Backspace */
1871 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
1874 if (wParam
== VK_TAB
&& shift_state
== 1 ) /* Shift tab */
1876 *p
++ = 0x1B; *p
++ = '['; *p
++ = 'Z'; return p
- output
;
1878 if (wParam
== VK_SPACE
&& shift_state
== 2 ) /* Ctrl-Space */
1880 *p
++ = 0; return p
- output
;
1882 if (wParam
== VK_SPACE
&& shift_state
== 3 ) /* Ctrl-Shift-Space */
1884 *p
++ = 160; return p
- output
;
1886 if (wParam
== VK_CANCEL
&& shift_state
== 2 ) /* Ctrl-Break */
1888 *p
++ = 3; return p
- output
;
1890 /* Control-2 to Control-8 are special */
1891 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8')
1893 *p
++ = "\000\033\034\035\036\037\177"[wParam
-'2'];
1896 if (shift_state
== 2 && wParam
== 0xBD) {
1900 if (shift_state
== 2 && wParam
== 0xDF) {
1904 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
1905 *p
++ = '\r'; *p
++ = '\n';
1910 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1911 * for integer decimal nn.)
1913 * We also deal with the weird ones here. Linux VCs replace F1
1914 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1915 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1920 case VK_F1
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11); break;
1921 case VK_F2
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12); break;
1922 case VK_F3
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13); break;
1923 case VK_F4
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14); break;
1924 case VK_F5
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15); break;
1925 case VK_F6
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17); break;
1926 case VK_F7
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18); break;
1927 case VK_F8
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19); break;
1928 case VK_F9
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20); break;
1929 case VK_F10
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21); break;
1930 case VK_F11
: code
= 23; break;
1931 case VK_F12
: code
= 24; break;
1932 case VK_F13
: code
= 25; break;
1933 case VK_F14
: code
= 26; break;
1934 case VK_F15
: code
= 28; break;
1935 case VK_F16
: code
= 29; break;
1936 case VK_F17
: code
= 31; break;
1937 case VK_F18
: code
= 32; break;
1938 case VK_F19
: code
= 33; break;
1939 case VK_F20
: code
= 34; break;
1940 case VK_HOME
: code
= 1; break;
1941 case VK_INSERT
: code
= 2; break;
1942 case VK_DELETE
: code
= 3; break;
1943 case VK_END
: code
= 4; break;
1944 case VK_PRIOR
: code
= 5; break;
1945 case VK_NEXT
: code
= 6; break;
1947 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
1948 p
+= sprintf((char *)p
, "\x1B[[%c", code
+ 'A' - 11);
1951 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
1952 p
+= sprintf((char *)p
, "\x1BO%c", code
+ 'P' - 11);
1955 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
1956 p
+= sprintf((char *)p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
1960 p
+= sprintf((char *)p
, "\x1B[%d~", code
);
1965 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
1966 * some reason seems to send VK_CLEAR to Windows...).
1971 case VK_UP
: xkey
= 'A'; break;
1972 case VK_DOWN
: xkey
= 'B'; break;
1973 case VK_RIGHT
: xkey
= 'C'; break;
1974 case VK_LEFT
: xkey
= 'D'; break;
1975 case VK_CLEAR
: xkey
= 'G'; break;
1980 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
1981 else if (app_cursor_keys
)
1982 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
1984 p
+= sprintf((char *)p
, "\x1B[%c", xkey
);
1990 /* Okay we've done everything interesting; let windows deal with
1991 * the boring stuff */
1993 BOOL capsOn
=keystate
[VK_CAPITAL
] !=0;
1995 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
1996 if(cfg
.xlat_capslockcyr
)
1997 keystate
[VK_CAPITAL
] = 0;
1999 r
= ToAscii (wParam
, scan
, keystate
, keys
, 0);
2005 unsigned char ch
= (unsigned char)keys
[i
];
2007 if (compose_state
==2 && (ch
&0x80) == 0 && ch
>' ') {
2012 if (compose_state
==3 && (ch
&0x80) == 0 && ch
>' ') {
2016 if ((nc
=check_compose(compose_char
,ch
)) == -1)
2021 *p
++ = xlat_kbd2tty((unsigned char)nc
);
2027 if( left_alt
&& key_down
) *p
++ = '\033';
2033 ch
= xlat_latkbd2win(ch
);
2034 *p
++ = xlat_kbd2tty(ch
);
2038 /* This is so the ALT-Numpad and dead keys work correctly. */
2045 /* This stops ALT press-release doing a 'COMMAND MENU' function */
2047 if (message
== WM_SYSKEYUP
&& wParam
== VK_MENU
)
2049 keystate
[VK_MENU
] = 0;
2057 void set_title (char *title
) {
2058 sfree (window_name
);
2059 window_name
= smalloc(1+strlen(title
));
2060 strcpy (window_name
, title
);
2061 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
2062 SetWindowText (hwnd
, title
);
2065 void set_icon (char *title
) {
2067 icon_name
= smalloc(1+strlen(title
));
2068 strcpy (icon_name
, title
);
2069 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
2070 SetWindowText (hwnd
, title
);
2073 void set_sbar (int total
, int start
, int page
) {
2076 if (!cfg
.scrollbar
) return;
2078 si
.cbSize
= sizeof(si
);
2079 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
2081 si
.nMax
= total
- 1;
2085 SetScrollInfo (hwnd
, SB_VERT
, &si
, TRUE
);
2088 Context
get_ctx(void) {
2093 SelectPalette (hdc
, pal
, FALSE
);
2099 void free_ctx (Context ctx
) {
2100 SelectPalette (ctx
, GetStockObject (DEFAULT_PALETTE
), FALSE
);
2101 ReleaseDC (hwnd
, ctx
);
2104 static void real_palette_set (int n
, int r
, int g
, int b
) {
2106 logpal
->palPalEntry
[n
].peRed
= r
;
2107 logpal
->palPalEntry
[n
].peGreen
= g
;
2108 logpal
->palPalEntry
[n
].peBlue
= b
;
2109 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
2110 colours
[n
] = PALETTERGB(r
, g
, b
);
2111 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2113 colours
[n
] = RGB(r
, g
, b
);
2116 void palette_set (int n
, int r
, int g
, int b
) {
2117 static const int first
[21] = {
2118 0, 2, 4, 6, 8, 10, 12, 14,
2119 1, 3, 5, 7, 9, 11, 13, 15,
2122 real_palette_set (first
[n
], r
, g
, b
);
2124 real_palette_set (first
[n
]+1, r
, g
, b
);
2126 HDC hdc
= get_ctx();
2127 UnrealizeObject (pal
);
2128 RealizePalette (hdc
);
2133 void palette_reset (void) {
2136 for (i
= 0; i
< NCOLOURS
; i
++) {
2138 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
2139 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
2140 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
2141 logpal
->palPalEntry
[i
].peFlags
= 0;
2142 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
2143 defpal
[i
].rgbtGreen
,
2144 defpal
[i
].rgbtBlue
);
2146 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
2147 defpal
[i
].rgbtGreen
,
2148 defpal
[i
].rgbtBlue
);
2153 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2155 RealizePalette (hdc
);
2160 void write_clip (void *data
, int len
) {
2164 clipdata
= GlobalAlloc (GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
2167 lock
= GlobalLock (clipdata
);
2170 memcpy (lock
, data
, len
);
2171 ((unsigned char *) lock
) [len
] = 0;
2172 GlobalUnlock (clipdata
);
2174 SendMessage (hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
2175 if (OpenClipboard (hwnd
)) {
2177 SetClipboardData (CF_TEXT
, clipdata
);
2180 GlobalFree (clipdata
);
2181 SendMessage (hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
2184 void get_clip (void **p
, int *len
) {
2185 static HGLOBAL clipdata
= NULL
;
2189 GlobalUnlock (clipdata
);
2193 if (OpenClipboard (NULL
)) {
2194 clipdata
= GetClipboardData (CF_TEXT
);
2197 *p
= GlobalLock (clipdata
);
2211 * Move `lines' lines from position `from' to position `to' in the
2214 void optimised_move (int to
, int from
, int lines
) {
2218 min
= (to
< from ? to
: from
);
2219 max
= to
+ from
- min
;
2221 r
.left
= 0; r
.right
= cols
* font_width
;
2222 r
.top
= min
* font_height
; r
.bottom
= (max
+lines
) * font_height
;
2223 ScrollWindow (hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
2227 * Print a message box and perform a fatal exit.
2229 void fatalbox(char *fmt
, ...) {
2234 vsprintf(stuff
, fmt
, ap
);
2236 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
2243 void beep(int errorbeep
) {
2244 static long last_beep
= 0;
2245 long now
, beep_diff
;
2247 now
= GetTickCount();
2248 beep_diff
= now
-last_beep
;
2250 /* Make sure we only respond to one beep per packet or so */
2251 if (beep_diff
>=0 && beep_diff
<50)
2255 MessageBeep(MB_ICONHAND
);
2259 last_beep
= GetTickCount();