14 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
18 #define IDM_SHOWLOG 0x0010
19 #define IDM_NEWSESS 0x0020
20 #define IDM_DUPSESS 0x0030
21 #define IDM_RECONF 0x0040
22 #define IDM_CLRSB 0x0050
23 #define IDM_RESET 0x0060
24 #define IDM_TEL_AYT 0x0070
25 #define IDM_TEL_BRK 0x0080
26 #define IDM_TEL_SYNCH 0x0090
27 #define IDM_TEL_EC 0x00a0
28 #define IDM_TEL_EL 0x00b0
29 #define IDM_TEL_GA 0x00c0
30 #define IDM_TEL_NOP 0x00d0
31 #define IDM_TEL_ABORT 0x00e0
32 #define IDM_TEL_AO 0x00f0
33 #define IDM_TEL_IP 0x0100
34 #define IDM_TEL_SUSP 0x0110
35 #define IDM_TEL_EOR 0x0120
36 #define IDM_TEL_EOF 0x0130
37 #define IDM_ABOUT 0x0140
38 #define IDM_SAVEDSESS 0x0150
40 #define IDM_SAVED_MIN 0x1000
41 #define IDM_SAVED_MAX 0x2000
43 #define WM_IGNORE_SIZE (WM_XUSER + 1)
44 #define WM_IGNORE_CLIP (WM_XUSER + 2)
45 #define WM_IGNORE_KEYMENU (WM_XUSER + 3)
47 static LRESULT CALLBACK
WndProc (HWND
, UINT
, WPARAM
, LPARAM
);
48 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
, unsigned char *output
);
49 static void cfgtopalette(void);
50 static void init_palette(void);
51 static void init_fonts(int);
53 static int extra_width
, extra_height
;
55 static int pending_netevent
= 0;
56 static WPARAM pend_netevent_wParam
= 0;
57 static LPARAM pend_netevent_lParam
= 0;
58 static void enact_pending_netevent(void);
62 #define FONT_UNDERLINE 2
63 #define FONT_BOLDUND 3
65 #define FONT_OEMBOLD 5
66 #define FONT_OEMBOLDUND 6
68 static HFONT fonts
[8];
69 static int font_needs_hand_underlining
;
71 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
79 static COLORREF colours
[NCOLOURS
];
81 static LPLOGPALETTE logpal
;
82 static RGBTRIPLE defpal
[NCOLOURS
];
86 static int dbltime
, lasttime
, lastact
;
87 static Mouse_Button lastbtn
;
89 static char *window_name
, *icon_name
;
91 static Ldisc
*real_ldisc
;
93 void begin_session(void) {
97 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
) {
98 static char appname
[] = "PuTTY";
103 int guess_width
, guess_height
;
106 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
108 winsock_ver
= MAKEWORD(1, 1);
109 if (WSAStartup(winsock_ver
, &wsadata
)) {
110 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
111 MB_OK
| MB_ICONEXCLAMATION
);
114 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
115 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
116 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
120 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
122 InitCommonControls();
125 * Process the command line.
130 default_protocol
= DEFAULT_PROTOCOL
;
131 default_port
= DEFAULT_PORT
;
136 while (*p
&& isspace(*p
)) p
++;
139 * Process command line options first. Yes, this can be
140 * done better, and it will be as soon as I have the
144 char *q
= p
+ strcspn(p
, " \t");
147 tolower(p
[0]) == 's' &&
148 tolower(p
[1]) == 's' &&
149 tolower(p
[2]) == 'h') {
150 default_protocol
= cfg
.protocol
= PROT_SSH
;
151 default_port
= cfg
.port
= 22;
152 } else if (q
== p
+ 3 &&
153 tolower(p
[0]) == 'l' &&
154 tolower(p
[1]) == 'o' &&
155 tolower(p
[2]) == 'g') {
156 logfile
= "putty.log";
158 p
= q
+ strspn(q
, " \t");
162 * An initial @ means to activate a saved session.
166 if (!*cfg
.host
&& !do_config()) {
170 } else if (*p
== '&') {
172 * An initial & means we've been given a command line
173 * containing the hex value of a HANDLE for a file
174 * mapping object, which we must then extract as a
179 if (sscanf(p
+1, "%p", &filemap
) == 1 &&
180 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
181 0, 0, sizeof(Config
))) != NULL
) {
184 CloseHandle(filemap
);
185 } else if (!do_config()) {
192 * If the hostname starts with "telnet:", set the
193 * protocol to Telnet and process the string as a
196 if (!strncmp(q
, "telnet:", 7)) {
200 if (q
[0] == '/' && q
[1] == '/')
202 cfg
.protocol
= PROT_TELNET
;
204 while (*p
&& *p
!= ':' && *p
!= '/') p
++;
212 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
213 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
215 while (*p
&& !isspace(*p
)) p
++;
218 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
219 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
220 while (*p
&& isspace(*p
)) p
++;
233 /* See if host is of the form user@host */
234 if (cfg
.host
[0] != '\0') {
235 char *atsign
= strchr(cfg
.host
, '@');
236 /* Make sure we're not overflowing the user field */
238 if (atsign
-cfg
.host
< sizeof cfg
.username
) {
239 strncpy (cfg
.username
, cfg
.host
, atsign
-cfg
.host
);
240 cfg
.username
[atsign
-cfg
.host
] = '\0';
242 memmove(cfg
.host
, atsign
+1, 1+strlen(atsign
+1));
248 * Select protocol. This is farmed out into a table in a
249 * separate file to enable an ssh-free variant.
254 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
255 if (backends
[i
].protocol
== cfg
.protocol
) {
256 back
= backends
[i
].backend
;
260 MessageBox(NULL
, "Unsupported protocol number found",
261 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
267 real_ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
268 /* To start with, we use the simple line discipline, so we can
269 * type passwords etc without fear of them being echoed... */
270 ldisc
= &ldisc_simple
;
274 wndclass
.lpfnWndProc
= WndProc
;
275 wndclass
.cbClsExtra
= 0;
276 wndclass
.cbWndExtra
= 0;
277 wndclass
.hInstance
= inst
;
278 wndclass
.hIcon
= LoadIcon (inst
,
279 MAKEINTRESOURCE(IDI_MAINICON
));
280 wndclass
.hCursor
= LoadCursor (NULL
, IDC_IBEAM
);
281 wndclass
.hbrBackground
= GetStockObject (BLACK_BRUSH
);
282 wndclass
.lpszMenuName
= NULL
;
283 wndclass
.lpszClassName
= appname
;
285 RegisterClass (&wndclass
);
290 savelines
= cfg
.savelines
;
296 * Guess some defaults for the window size. This all gets
297 * updated later, so we don't really care too much. However, we
298 * do want the font width/height guesses to correspond to a
299 * large font rather than a small one...
306 term_size (cfg
.height
, cfg
.width
, cfg
.savelines
);
307 guess_width
= extra_width
+ font_width
* cols
;
308 guess_height
= extra_height
+ font_height
* rows
;
311 HWND w
= GetDesktopWindow();
312 GetWindowRect (w
, &r
);
313 if (guess_width
> r
.right
- r
.left
)
314 guess_width
= r
.right
- r
.left
;
315 if (guess_height
> r
.bottom
- r
.top
)
316 guess_height
= r
.bottom
- r
.top
;
320 int winmode
= WS_OVERLAPPEDWINDOW
|WS_VSCROLL
;
321 if (!cfg
.scrollbar
) winmode
&= ~(WS_VSCROLL
);
322 if (cfg
.locksize
) winmode
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
323 hwnd
= CreateWindow (appname
, appname
,
325 CW_USEDEFAULT
, CW_USEDEFAULT
,
326 guess_width
, guess_height
,
327 NULL
, NULL
, inst
, NULL
);
331 * Initialise the fonts, simultaneously correcting the guesses
332 * for font_{width,height}.
334 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
339 * Correct the guesses for extra_{width,height}.
343 GetWindowRect (hwnd
, &wr
);
344 GetClientRect (hwnd
, &cr
);
345 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
346 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
350 * Resize the window, now we know what size we _really_ want it
353 guess_width
= extra_width
+ font_width
* cols
;
354 guess_height
= extra_height
+ font_height
* rows
;
355 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
356 SetWindowPos (hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
357 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
360 * Initialise the scroll bar.
365 si
.cbSize
= sizeof(si
);
366 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
371 SetScrollInfo (hwnd
, SB_VERT
, &si
, FALSE
);
375 * Start up the telnet connection.
382 error
= back
->init (hwnd
, cfg
.host
, cfg
.port
, &realhost
);
384 sprintf(msg
, "Unable to open connection:\n%s", error
);
385 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
388 window_name
= icon_name
= NULL
;
389 sprintf(msg
, "%s - PuTTY", realhost
);
394 session_closed
= FALSE
;
397 * Set up the input and output buffers.
400 outbuf_reap
= outbuf_head
= 0;
403 * Prepare the mouse handler.
405 lastact
= MA_NOTHING
;
406 lastbtn
= MB_NOTHING
;
407 dbltime
= GetDoubleClickTime();
410 * Set up the session-control options on the system menu.
413 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
417 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
418 if (cfg
.protocol
== PROT_TELNET
) {
420 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
421 AppendMenu (p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
422 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
423 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
424 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
425 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
426 AppendMenu (p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
427 AppendMenu (p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
428 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
429 AppendMenu (p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
430 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
431 AppendMenu (p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
432 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
433 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
434 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
435 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
436 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
, "Telnet Command");
437 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
439 AppendMenu (m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
440 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
441 AppendMenu (m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session");
442 AppendMenu (m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
445 for (i
= 1 ; i
< ((nsessions
< 256) ? nsessions
: 256) ; i
++)
446 AppendMenu (s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
) , sessions
[i
]);
447 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
448 AppendMenu (m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings");
449 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
450 AppendMenu (m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
451 AppendMenu (m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
452 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
453 AppendMenu (m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
457 * Finally show the window!
459 ShowWindow (hwnd
, show
);
462 * Set the palette up.
468 has_focus
= (GetForegroundWindow() == hwnd
);
472 int timer_id
= 0, long_timer
= 0;
474 while (GetMessage (&msg
, NULL
, 0, 0) == 1) {
475 /* Sometimes DispatchMessage calls routines that use their own
476 * GetMessage loop, setup this timer so we get some control back.
478 * Also call term_update() from the timer so that if the host
479 * is sending data flat out we still do redraws.
481 if(timer_id
&& long_timer
) {
482 KillTimer(hwnd
, timer_id
);
483 long_timer
= timer_id
= 0;
486 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
487 DispatchMessage (&msg
);
489 /* This is too fast, but I'll leave it for now 'cause it shows
490 * how often term_update is called (far too often at times!)
494 /* Send the paste buffer if there's anything to send */
497 /* If there's nothing new in the queue then we can do everything
498 * we've delayed, reading the socket, writing, and repainting
501 if (!PeekMessage (&msg
, NULL
, 0, 0, PM_NOREMOVE
)) {
502 if (pending_netevent
) {
503 enact_pending_netevent();
508 if (!PeekMessage (&msg
, NULL
, 0, 0, PM_NOREMOVE
)) {
510 KillTimer(hwnd
, timer_id
);
517 timer_id
= SetTimer(hwnd
, 1, 2000, NULL
);
518 else if (cfg
.blinktext
)
519 timer_id
= SetTimer(hwnd
, 1, 250, NULL
);
521 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
534 DeleteObject(fonts
[i
]);
541 if (cfg
.protocol
== PROT_SSH
) {
552 * Print a message box and close the connection.
554 void connection_fatal(char *fmt
, ...) {
559 vsprintf(stuff
, fmt
, ap
);
561 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
562 if (cfg
.close_on_exit
)
565 session_closed
= TRUE
;
566 SetWindowText (hwnd
, "PuTTY (inactive)");
571 * Actually do the job requested by a WM_NETEVENT
573 static void enact_pending_netevent(void) {
575 static int reentering
= 0;
578 return; /* don't unpend the pending */
580 pending_netevent
= FALSE
;
583 i
= back
->msg (pend_netevent_wParam
, pend_netevent_lParam
);
588 switch (WSABASEERR
+ (-i
) % 10000) {
590 sprintf(buf
, "Connection reset by peer");
593 sprintf(buf
, "Unexpected network error %d", -i
);
596 connection_fatal(buf
);
599 if (cfg
.close_on_exit
)
602 session_closed
= TRUE
;
603 MessageBox(hwnd
, "Connection closed by remote host",
604 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
605 SetWindowText (hwnd
, "PuTTY (inactive)");
611 * Copy the colour palette from the configuration data into defpal.
612 * This is non-trivial because the colour indices are different.
614 static void cfgtopalette(void) {
616 static const int ww
[] = {
617 6, 7, 8, 9, 10, 11, 12, 13,
618 14, 15, 16, 17, 18, 19, 20, 21,
619 0, 1, 2, 3, 4, 4, 5, 5
622 for (i
=0; i
<24; i
++) {
624 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
625 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
626 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
631 * Set up the colour palette.
633 static void init_palette(void) {
635 HDC hdc
= GetDC (hwnd
);
637 if (cfg
.try_palette
&&
638 GetDeviceCaps (hdc
, RASTERCAPS
) & RC_PALETTE
) {
639 logpal
= smalloc(sizeof(*logpal
)
640 - sizeof(logpal
->palPalEntry
)
641 + NCOLOURS
* sizeof(PALETTEENTRY
));
642 logpal
->palVersion
= 0x300;
643 logpal
->palNumEntries
= NCOLOURS
;
644 for (i
= 0; i
< NCOLOURS
; i
++) {
645 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
646 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
647 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
648 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
650 pal
= CreatePalette (logpal
);
652 SelectPalette (hdc
, pal
, FALSE
);
653 RealizePalette (hdc
);
654 SelectPalette (hdc
, GetStockObject (DEFAULT_PALETTE
),
658 ReleaseDC (hwnd
, hdc
);
661 for (i
=0; i
<NCOLOURS
; i
++)
662 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
666 for(i
=0; i
<NCOLOURS
; i
++)
667 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
673 * Initialise all the fonts we will need. There may be as many as
674 * eight or as few as one. We also:
676 * - check the font width and height, correcting our guesses if
679 * - verify that the bold font is the same width as the ordinary
680 * one, and engage shadow bolding if not.
682 * - verify that the underlined font is the same width as the
683 * ordinary one (manual underlining by means of line drawing can
684 * be done in a pinch).
686 static void init_fonts(int pick_width
) {
691 int fw_dontcare
, fw_bold
;
700 if (cfg
.fontisbold
) {
701 fw_dontcare
= FW_BOLD
;
704 fw_dontcare
= FW_DONTCARE
;
710 font_height
= cfg
.fontheight
;
711 font_width
= pick_width
;
714 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
715 c, OUT_DEFAULT_PRECIS, \
716 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
717 FIXED_PITCH | FF_DONTCARE, cfg.font)
719 if (cfg
.vtmode
!= VT_OEMONLY
) {
720 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
722 SelectObject (hdc
, fonts
[FONT_NORMAL
]);
723 GetTextMetrics(hdc
, &tm
);
724 font_height
= tm
.tmHeight
;
725 font_width
= tm
.tmAveCharWidth
;
727 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
730 * Some fonts, e.g. 9-pt Courier, draw their underlines
731 * outside their character cell. We successfully prevent
732 * screen corruption by clipping the text output, but then
733 * we lose the underline completely. Here we try to work
734 * out whether this is such a font, and if it is, we set a
735 * flag that causes underlines to be drawn by hand.
737 * Having tried other more sophisticated approaches (such
738 * as examining the TEXTMETRIC structure or requesting the
739 * height of a string), I think we'll do this the brute
740 * force way: we create a small bitmap, draw an underlined
741 * space on it, and test to see whether any pixels are
742 * foreground-coloured. (Since we expect the underline to
743 * go all the way across the character cell, we only search
744 * down a single column of the bitmap, half way across.)
748 HBITMAP und_bm
, und_oldbm
;
752 und_dc
= CreateCompatibleDC(hdc
);
753 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
754 und_oldbm
= SelectObject(und_dc
, und_bm
);
755 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
756 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
757 SetTextColor (und_dc
, RGB(255,255,255));
758 SetBkColor (und_dc
, RGB(0,0,0));
759 SetBkMode (und_dc
, OPAQUE
);
760 ExtTextOut (und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
762 for (i
= 0; i
< font_height
; i
++) {
763 c
= GetPixel(und_dc
, font_width
/2, i
);
767 SelectObject(und_dc
, und_oldbm
);
768 DeleteObject(und_bm
);
770 font_needs_hand_underlining
= !gotit
;
773 if (bold_mode
== BOLD_FONT
) {
774 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
775 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
778 if (cfg
.vtmode
== VT_OEMANSI
) {
779 f(FONT_OEM
, OEM_CHARSET
, fw_dontcare
, FALSE
);
780 f(FONT_OEMUND
, OEM_CHARSET
, fw_dontcare
, TRUE
);
782 if (bold_mode
== BOLD_FONT
) {
783 f(FONT_OEMBOLD
, OEM_CHARSET
, fw_bold
, FALSE
);
784 f(FONT_OEMBOLDUND
, OEM_CHARSET
, fw_bold
, TRUE
);
790 f(FONT_OEM
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
792 SelectObject (hdc
, fonts
[FONT_OEM
]);
793 GetTextMetrics(hdc
, &tm
);
794 font_height
= tm
.tmHeight
;
795 font_width
= tm
.tmAveCharWidth
;
797 f(FONT_OEMUND
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
799 if (bold_mode
== BOLD_FONT
) {
800 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
801 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
806 descent
= tm
.tmAscent
+ 1;
807 if (descent
>= font_height
)
808 descent
= font_height
- 1;
809 firstchar
= tm
.tmFirstChar
;
811 for (i
=0; i
<8; i
++) {
813 if (SelectObject (hdc
, fonts
[i
]) &&
814 GetTextMetrics(hdc
, &tm
) )
815 fsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
821 ReleaseDC (hwnd
, hdc
);
823 /* ... This is wrong in OEM only mode */
824 if (fsize
[FONT_UNDERLINE
] != fsize
[FONT_NORMAL
] ||
825 (bold_mode
== BOLD_FONT
&&
826 fsize
[FONT_BOLDUND
] != fsize
[FONT_BOLD
])) {
828 DeleteObject (fonts
[FONT_UNDERLINE
]);
829 if (bold_mode
== BOLD_FONT
)
830 DeleteObject (fonts
[FONT_BOLDUND
]);
833 if (bold_mode
== BOLD_FONT
&&
834 fsize
[FONT_BOLD
] != fsize
[FONT_NORMAL
]) {
835 bold_mode
= BOLD_SHADOW
;
836 DeleteObject (fonts
[FONT_BOLD
]);
837 if (und_mode
== UND_FONT
)
838 DeleteObject (fonts
[FONT_BOLDUND
]);
842 /* With the fascist font painting it doesn't matter if the linedraw font
843 * isn't exactly the right size anymore so we don't have to check this.
845 if (cfg
.vtmode
== VT_OEMANSI
&& fsize
[FONT_OEM
] != fsize
[FONT_NORMAL
] ) {
846 if( cfg
.fontcharset
== OEM_CHARSET
)
848 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
849 "different sizes. Using OEM-only mode instead",
850 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
851 cfg
.vtmode
= VT_OEMONLY
;
853 else if( firstchar
< ' ' )
855 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
856 "different sizes. Using XTerm mode instead",
857 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
858 cfg
.vtmode
= VT_XWINDOWS
;
862 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
863 "different sizes. Using ISO8859-1 mode instead",
864 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
865 cfg
.vtmode
= VT_POORMAN
;
870 DeleteObject (fonts
[i
]);
876 void request_resize (int w
, int h
, int refont
) {
879 /* If the window is maximized supress resizing attempts */
880 if(IsZoomed(hwnd
)) return;
883 /* Don't do this in OEMANSI, you may get disable messages */
884 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132)
885 && cfg
.vtmode
!= VT_OEMANSI
)
887 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132))
890 /* If font width too big for screen should we shrink the font more ? */
892 font_width
= ((font_width
*cols
+w
/2)/w
);
899 DeleteObject(fonts
[i
]);
901 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
903 init_fonts(font_width
);
907 static int first_time
= 1;
913 /* Get the size of the screen */
914 if (GetClientRect(GetDesktopWindow(),&ss
))
915 /* first_time = 0 */;
916 else { first_time
= 2; break; }
918 /* Make sure the values are sane */
919 width
= (ss
.right
-ss
.left
-extra_width
) / font_width
;
920 height
= (ss
.bottom
-ss
.top
-extra_height
) / font_height
;
922 if (w
>width
) w
=width
;
923 if (h
>height
) h
=height
;
929 width
= extra_width
+ font_width
* w
;
930 height
= extra_height
+ font_height
* h
;
932 SetWindowPos (hwnd
, NULL
, 0, 0, width
, height
,
933 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
934 SWP_NOMOVE
| SWP_NOZORDER
);
937 static void click (Mouse_Button b
, int x
, int y
) {
938 int thistime
= GetMessageTime();
940 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
941 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
942 lastact
== MA_2CLK ? MA_3CLK
:
943 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
948 if (lastact
!= MA_NOTHING
)
949 term_mouse (b
, lastact
, x
, y
);
953 static LRESULT CALLBACK
WndProc (HWND hwnd
, UINT message
,
954 WPARAM wParam
, LPARAM lParam
) {
956 static int ignore_size
= FALSE
;
957 static int ignore_clip
= FALSE
;
958 static int ignore_keymenu
= TRUE
;
959 static int just_reconfigged
= FALSE
;
960 static int resizing
= FALSE
;
964 if (pending_netevent
)
965 enact_pending_netevent();
973 if (!cfg
.warn_on_close
|| session_closed
||
974 MessageBox(hwnd
, "Are you sure you want to close this session?",
975 "PuTTY Exit Confirmation",
976 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
983 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
986 return 0; /* don't put up system menu on Alt */
999 PROCESS_INFORMATION pi
;
1000 HANDLE filemap
= NULL
;
1002 if (wParam
== IDM_DUPSESS
) {
1004 * Allocate a file-mapping memory chunk for the
1007 SECURITY_ATTRIBUTES sa
;
1010 sa
.nLength
= sizeof(sa
);
1011 sa
.lpSecurityDescriptor
= NULL
;
1012 sa
.bInheritHandle
= TRUE
;
1013 filemap
= CreateFileMapping((HANDLE
)0xFFFFFFFF,
1020 p
= (Config
*)MapViewOfFile(filemap
,
1022 0, 0, sizeof(Config
));
1024 *p
= cfg
; /* structure copy */
1028 sprintf(c
, "putty &%p", filemap
);
1030 } else if (wParam
== IDM_SAVEDSESS
) {
1031 char *session
= sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1032 cl
= malloc(16 + strlen(session
)); /* 8, but play safe */
1034 cl
= NULL
; /* not a very important failure mode */
1036 sprintf(cl
, "putty @%s", session
);
1042 GetModuleFileName (NULL
, b
, sizeof(b
)-1);
1044 si
.lpReserved
= NULL
;
1045 si
.lpDesktop
= NULL
;
1049 si
.lpReserved2
= NULL
;
1050 CreateProcess (b
, cl
, NULL
, NULL
, TRUE
,
1051 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1054 CloseHandle(filemap
);
1060 if (!do_reconfig(hwnd
))
1062 just_reconfigged
= TRUE
;
1067 DeleteObject(fonts
[i
]);
1069 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1070 und_mode
= UND_FONT
;
1073 /* Telnet will change local echo -> remote if the remote asks */
1074 if (cfg
.protocol
!= PROT_TELNET
)
1075 ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
1083 /* Enable or disable the scroll bar, etc */
1085 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1088 if (cfg
.scrollbar
) nflg
|= WS_VSCROLL
;
1089 else nflg
&= ~WS_VSCROLL
;
1091 nflg
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1093 nflg
|= (WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1099 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1100 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1101 SetWindowPos(hwnd
, NULL
, 0,0,0,0,
1102 SWP_NOACTIVATE
|SWP_NOCOPYBITS
|
1103 SWP_NOMOVE
|SWP_NOSIZE
| SWP_NOZORDER
|
1106 GetWindowRect (hwnd
, &wr
);
1107 GetClientRect (hwnd
, &cr
);
1108 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1109 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1113 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1114 InvalidateRect(hwnd
, NULL
, TRUE
);
1115 SetWindowPos (hwnd
, NULL
, 0, 0,
1116 extra_width
+ font_width
* cfg
.width
,
1117 extra_height
+ font_height
* cfg
.height
,
1118 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1119 SWP_NOMOVE
| SWP_NOZORDER
);
1120 if (IsIconic(hwnd
)) {
1121 SetWindowText (hwnd
,
1122 cfg
.win_name_always ? window_name
: icon_name
);
1131 case IDM_TEL_AYT
: back
->special (TS_AYT
); break;
1132 case IDM_TEL_BRK
: back
->special (TS_BRK
); break;
1133 case IDM_TEL_SYNCH
: back
->special (TS_SYNCH
); break;
1134 case IDM_TEL_EC
: back
->special (TS_EC
); break;
1135 case IDM_TEL_EL
: back
->special (TS_EL
); break;
1136 case IDM_TEL_GA
: back
->special (TS_GA
); break;
1137 case IDM_TEL_NOP
: back
->special (TS_NOP
); break;
1138 case IDM_TEL_ABORT
: back
->special (TS_ABORT
); break;
1139 case IDM_TEL_AO
: back
->special (TS_AO
); break;
1140 case IDM_TEL_IP
: back
->special (TS_IP
); break;
1141 case IDM_TEL_SUSP
: back
->special (TS_SUSP
); break;
1142 case IDM_TEL_EOR
: back
->special (TS_EOR
); break;
1143 case IDM_TEL_EOF
: back
->special (TS_EOF
); break;
1148 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1149 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1154 #define X_POS(l) ((int)(short)LOWORD(l))
1155 #define Y_POS(l) ((int)(short)HIWORD(l))
1157 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1158 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1160 case WM_LBUTTONDOWN
:
1161 click (MB_SELECT
, TO_CHR_X(X_POS(lParam
)),
1162 TO_CHR_Y(Y_POS(lParam
)));
1166 term_mouse (MB_SELECT
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1167 TO_CHR_Y(Y_POS(lParam
)));
1170 case WM_MBUTTONDOWN
:
1172 click (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1173 TO_CHR_X(X_POS(lParam
)),
1174 TO_CHR_Y(Y_POS(lParam
)));
1177 term_mouse (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1178 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1179 TO_CHR_Y(Y_POS(lParam
)));
1182 case WM_RBUTTONDOWN
:
1184 click (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1185 TO_CHR_X(X_POS(lParam
)),
1186 TO_CHR_Y(Y_POS(lParam
)));
1189 term_mouse (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1190 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1191 TO_CHR_Y(Y_POS(lParam
)));
1196 * Add the mouse position and message time to the random
1197 * number noise, if we're using ssh.
1199 if (cfg
.protocol
== PROT_SSH
)
1200 noise_ultralight(lParam
);
1202 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1204 if (wParam
& MK_LBUTTON
)
1206 else if (wParam
& MK_MBUTTON
)
1207 b
= cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
;
1209 b
= cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
;
1210 term_mouse (b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1211 TO_CHR_Y(Y_POS(lParam
)));
1214 case WM_IGNORE_CLIP
:
1215 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1217 case WM_IGNORE_KEYMENU
:
1218 ignore_keymenu
= wParam
; /* do or don't ignore SC_KEYMENU */
1220 case WM_DESTROYCLIPBOARD
:
1223 ignore_clip
= FALSE
;
1228 hdc
= BeginPaint (hwnd
, &p
);
1230 SelectPalette (hdc
, pal
, TRUE
);
1231 RealizePalette (hdc
);
1233 term_paint (hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1234 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1235 SelectObject (hdc
, GetStockObject(SYSTEM_FONT
));
1236 SelectObject (hdc
, GetStockObject(WHITE_PEN
));
1237 EndPaint (hwnd
, &p
);
1241 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1242 * but the only one that's likely to try to overload us is FD_READ.
1243 * This means buffering just one is fine.
1245 if (pending_netevent
)
1246 enact_pending_netevent();
1248 pending_netevent
= TRUE
;
1249 pend_netevent_wParam
=wParam
;
1250 pend_netevent_lParam
=lParam
;
1262 case WM_IGNORE_SIZE
:
1263 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1265 case WM_ENTERSIZEMOVE
:
1269 case WM_EXITSIZEMOVE
:
1276 int width
, height
, w
, h
, ew
, eh
;
1277 LPRECT r
= (LPRECT
)lParam
;
1279 width
= r
->right
- r
->left
- extra_width
;
1280 height
= r
->bottom
- r
->top
- extra_height
;
1281 w
= (width
+ font_width
/2) / font_width
; if (w
< 1) w
= 1;
1282 h
= (height
+ font_height
/2) / font_height
; if (h
< 1) h
= 1;
1283 UpdateSizeTip(hwnd
, w
, h
);
1284 ew
= width
- w
* font_width
;
1285 eh
= height
- h
* font_height
;
1287 if (wParam
== WMSZ_LEFT
||
1288 wParam
== WMSZ_BOTTOMLEFT
||
1289 wParam
== WMSZ_TOPLEFT
)
1295 if (wParam
== WMSZ_TOP
||
1296 wParam
== WMSZ_TOPRIGHT
||
1297 wParam
== WMSZ_TOPLEFT
)
1307 /* break; (never reached) */
1309 if (wParam
== SIZE_MINIMIZED
) {
1310 SetWindowText (hwnd
,
1311 cfg
.win_name_always ? window_name
: icon_name
);
1314 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1315 SetWindowText (hwnd
, window_name
);
1317 int width
, height
, w
, h
;
1318 #if 0 /* we have fixed this using WM_SIZING now */
1322 width
= LOWORD(lParam
);
1323 height
= HIWORD(lParam
);
1324 w
= width
/ font_width
; if (w
< 1) w
= 1;
1325 h
= height
/ font_height
; if (h
< 1) h
= 1;
1326 #if 0 /* we have fixed this using WM_SIZING now */
1327 ew
= width
- w
* font_width
;
1328 eh
= height
- h
* font_height
;
1329 if (ew
!= 0 || eh
!= 0) {
1331 GetWindowRect (hwnd
, &r
);
1332 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1333 SetWindowPos (hwnd
, NULL
, 0, 0,
1334 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1335 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1338 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1340 term_size (h
, w
, cfg
.savelines
);
1342 * Don't call back->size in mid-resize. (To prevent
1343 * massive numbers of resize events getting sent
1344 * down the connection during an NT opaque drag.)
1348 just_reconfigged
= FALSE
;
1351 ignore_size
= FALSE
;
1354 switch (LOWORD(wParam
)) {
1355 case SB_BOTTOM
: term_scroll(-1, 0); break;
1356 case SB_TOP
: term_scroll(+1, 0); break;
1357 case SB_LINEDOWN
: term_scroll (0, +1); break;
1358 case SB_LINEUP
: term_scroll (0, -1); break;
1359 case SB_PAGEDOWN
: term_scroll (0, +rows
/2); break;
1360 case SB_PAGEUP
: term_scroll (0, -rows
/2); break;
1361 case SB_THUMBPOSITION
: case SB_THUMBTRACK
:
1362 term_scroll (1, HIWORD(wParam
)); break;
1365 case WM_PALETTECHANGED
:
1366 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1367 HDC hdc
= get_ctx();
1369 if (RealizePalette (hdc
) > 0)
1375 case WM_QUERYNEWPALETTE
:
1377 HDC hdc
= get_ctx();
1379 if (RealizePalette (hdc
) > 0)
1391 * Add the scan code and keypress timing to the random
1392 * number noise, if we're using ssh.
1394 if (cfg
.protocol
== PROT_SSH
)
1395 noise_ultralight(lParam
);
1398 * We don't do TranslateMessage since it disassociates the
1399 * resulting CHAR message from the KEYDOWN that sparked it,
1400 * which we occasionally don't want. Instead, we process
1401 * KEYDOWN, and call the Win32 translator functions so that
1402 * we get the translations under _our_ control.
1405 unsigned char buf
[20];
1408 len
= TranslateKey (message
, wParam
, lParam
, buf
);
1410 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1411 ldisc
->send (buf
, len
);
1417 * Nevertheless, we are prepared to deal with WM_CHAR
1418 * messages, should they crop up. So if someone wants to
1419 * post the things to us as part of a macro manoeuvre,
1420 * we're ready to cope.
1423 char c
= xlat_kbd2tty((unsigned char)wParam
);
1424 ldisc
->send (&c
, 1);
1429 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1433 * Draw a line of text in the window, at given character
1434 * coordinates, in given attributes.
1436 * We are allowed to fiddle with the contents of `text'.
1438 void do_text (Context ctx
, int x
, int y
, char *text
, int len
,
1439 unsigned long attr
, int lattr
) {
1441 int nfg
, nbg
, nfont
;
1444 int force_manual_underline
= 0;
1445 int fnt_width
= font_width
*(1+(lattr
!=LATTR_NORM
));
1446 static int *IpDx
= 0, IpDxLEN
= 0;;
1448 if (len
>IpDxLEN
|| IpDx
[0] != fnt_width
) {
1452 IpDx
= smalloc((len
+16)*sizeof(int));
1455 for(i
=0; i
<IpDxLEN
; i
++)
1456 IpDx
[i
] = fnt_width
;
1462 if (attr
& ATTR_ACTCURS
) {
1463 attr
&= (bold_mode
== BOLD_COLOURS ?
0x300200 : 0x300300);
1464 attr
^= ATTR_CUR_XOR
;
1468 if (cfg
.vtmode
== VT_OEMONLY
)
1472 * Map high-half characters in order to approximate ISO using
1473 * OEM character set. No characters are missing if the OEM codepage
1476 if (nfont
& FONT_OEM
) {
1478 for (i
=0; i
<len
; i
++)
1479 if (text
[i
] >= '\xA0' && text
[i
] <= '\xFF') {
1481 /* This is CP850 ... perfect translation */
1482 static const char oemhighhalf
[] =
1483 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1484 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1485 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1486 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1487 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1488 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1489 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1490 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1491 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1492 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1493 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1494 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1497 /* This is CP437 ... junk translation */
1498 static const unsigned char oemhighhalf
[] = {
1499 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1500 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1501 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1502 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1503 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1504 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1505 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1506 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1507 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1508 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1509 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1510 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1513 text
[i
] = oemhighhalf
[(unsigned char)text
[i
] - 0xA0];
1517 if (attr
& ATTR_GBCHR
) {
1520 * GB mapping: map # to pound, and everything else stays
1523 for (i
=0; i
<len
; i
++)
1525 text
[i
] = cfg
.vtmode
== VT_OEMONLY ?
'\x9C' : '\xA3';
1526 } else if (attr
& ATTR_LINEDRW
) {
1529 static const char poorman
[] =
1530 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1533 static const char oemmap_437
[] =
1534 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1535 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1538 static const char oemmap_850
[] =
1539 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1540 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1542 /* Poor windows font ... eg: windows courier */
1543 static const char oemmap
[] =
1544 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1545 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1548 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1549 * VT100 line drawing chars; everything else stays normal.
1551 switch (cfg
.vtmode
) {
1553 for (i
=0; i
<len
; i
++)
1554 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1555 text
[i
] += '\x01' - '\x60';
1558 /* Make sure we actually have an OEM font */
1559 if (fonts
[nfont
|FONT_OEM
]) {
1562 for (i
=0; i
<len
; i
++)
1563 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1564 text
[i
] = oemmap
[(unsigned char)text
[i
] - 0x60];
1568 for (i
=0; i
<len
; i
++)
1569 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1570 text
[i
] = poorman
[(unsigned char)text
[i
] - 0x60];
1575 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
1576 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
1577 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
1579 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
1580 nfont
|= FONT_UNDERLINE
;
1583 if (nfont
&FONT_UNDERLINE
)
1584 force_manual_underline
= 1;
1585 /* Don't do the same for manual bold, it could be bad news. */
1587 nfont
&= ~(FONT_BOLD
|FONT_UNDERLINE
);
1589 if (font_needs_hand_underlining
&& (attr
& ATTR_UNDER
))
1590 force_manual_underline
= 1;
1591 if (attr
& ATTR_REVERSE
) {
1592 t
= nfg
; nfg
= nbg
; nbg
= t
;
1594 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
1596 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
1600 SelectObject (hdc
, fonts
[nfont
]);
1601 SetTextColor (hdc
, fg
);
1602 SetBkColor (hdc
, bg
);
1603 SetBkMode (hdc
, OPAQUE
);
1606 line_box
.right
= x
+fnt_width
*len
;
1607 line_box
.bottom
= y
+font_height
;
1608 ExtTextOut (hdc
, x
, y
, ETO_CLIPPED
|ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
1609 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
1610 SetBkMode (hdc
, TRANSPARENT
);
1612 /* GRR: This draws the character outside it's box and can leave
1613 * 'droppings' even with the clip box! I suppose I could loop it
1614 * one character at a time ... yuk.
1616 * Or ... I could do a test print with "W", and use +1 or -1 for this
1617 * shift depending on if the leftmost column is blank...
1619 ExtTextOut (hdc
, x
-1, y
, ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
1621 if (force_manual_underline
||
1622 (und_mode
== UND_LINE
&& (attr
& ATTR_UNDER
))) {
1624 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, fg
));
1625 MoveToEx (hdc
, x
, y
+descent
, NULL
);
1626 LineTo (hdc
, x
+len
*fnt_width
, y
+descent
);
1627 oldpen
= SelectObject (hdc
, oldpen
);
1628 DeleteObject (oldpen
);
1630 if (attr
& ATTR_PASCURS
) {
1633 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
1634 pts
[2].x
= pts
[3].x
= x
+fnt_width
-1;
1635 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
1636 pts
[1].y
= pts
[2].y
= y
+font_height
-1;
1637 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
1638 Polyline (hdc
, pts
, 5);
1639 oldpen
= SelectObject (hdc
, oldpen
);
1640 DeleteObject (oldpen
);
1644 static int check_compose(int first
, int second
) {
1646 static char * composetbl
[] = {
1647 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1648 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1649 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1650 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1651 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1652 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1653 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1654 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1655 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1656 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1657 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1658 "\"uü", "'yý", "htþ", "\"yÿ",
1662 static int recurse
= 0;
1669 sprintf(buf
, "cc(%d,%d)", first
, second
);
1674 for(c
=composetbl
; *c
; c
++) {
1675 if( (*c
)[0] == first
&& (*c
)[1] == second
)
1677 return (*c
)[2] & 0xFF;
1684 nc
= check_compose(second
, first
);
1686 nc
= check_compose(toupper(first
), toupper(second
));
1688 nc
= check_compose(toupper(second
), toupper(first
));
1696 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1697 * codes. Returns number of bytes used or zero to drop the message
1698 * or -1 to forward the message to windows.
1700 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
, unsigned char *output
) {
1702 int scan
, left_alt
= 0, key_down
, shift_state
;
1704 unsigned char * p
= output
;
1706 static WORD keys
[3];
1707 static int compose_state
= 0;
1708 static int compose_char
= 0;
1709 static WPARAM compose_key
= 0;
1711 r
= GetKeyboardState(keystate
);
1712 if (!r
) memset(keystate
, 0, sizeof(keystate
));
1715 /* Note if AltGr was pressed and if it was used as a compose key */
1716 if (wParam
== VK_MENU
&& (HIWORD(lParam
)&KF_EXTENDED
))
1718 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
1719 if (!compose_state
) compose_key
= wParam
;
1721 if (wParam
== VK_APPS
&& !compose_state
)
1722 compose_key
= wParam
;
1724 if (wParam
== compose_key
)
1726 if (compose_state
== 0 && (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
1728 else if (compose_state
== 1 && (HIWORD(lParam
)&KF_UP
))
1733 else if (compose_state
==1 && wParam
!= VK_CONTROL
)
1736 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1737 if ( (cfg
.funky_type
== 0 || (cfg
.funky_type
== 1 && app_keypad_keys
))
1738 && wParam
==VK_NUMLOCK
&& !(keystate
[VK_SHIFT
]&0x80)) {
1740 wParam
= VK_EXECUTE
;
1742 /* UnToggle NUMLock */
1743 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
1744 keystate
[VK_NUMLOCK
] ^= 1;
1747 /* And write back the 'adjusted' state */
1748 SetKeyboardState (keystate
);
1751 /* Disable Auto repeat if required */
1752 if (repeat_off
&& (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
)
1755 if ((HIWORD(lParam
)&KF_ALTDOWN
) && (keystate
[VK_RMENU
]&0x80) == 0)
1758 key_down
= ((HIWORD(lParam
)&KF_UP
)==0);
1760 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1761 if (left_alt
&& (keystate
[VK_CONTROL
]&0x80))
1762 keystate
[VK_MENU
] = 0;
1764 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
1765 shift_state
= ((keystate
[VK_SHIFT
]&0x80)!=0)
1766 + ((keystate
[VK_CONTROL
]&0x80)!=0)*2;
1769 * Record that we pressed key so the scroll window can be reset, but
1770 * be careful to avoid Shift-UP/Down
1772 if( wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
1776 /* Make sure we're not pasting */
1777 if (key_down
) term_nopaste();
1779 if (compose_state
>1 && left_alt
) compose_state
= 0;
1781 /* Sanitize the number pad if not using a PC NumPad */
1782 if( left_alt
|| (app_keypad_keys
&& cfg
.funky_type
!= 2)
1783 || cfg
.nethack_keypad
|| compose_state
)
1785 if ((HIWORD(lParam
)&KF_EXTENDED
) == 0)
1790 case VK_INSERT
: nParam
= VK_NUMPAD0
; break;
1791 case VK_END
: nParam
= VK_NUMPAD1
; break;
1792 case VK_DOWN
: nParam
= VK_NUMPAD2
; break;
1793 case VK_NEXT
: nParam
= VK_NUMPAD3
; break;
1794 case VK_LEFT
: nParam
= VK_NUMPAD4
; break;
1795 case VK_CLEAR
: nParam
= VK_NUMPAD5
; break;
1796 case VK_RIGHT
: nParam
= VK_NUMPAD6
; break;
1797 case VK_HOME
: nParam
= VK_NUMPAD7
; break;
1798 case VK_UP
: nParam
= VK_NUMPAD8
; break;
1799 case VK_PRIOR
: nParam
= VK_NUMPAD9
; break;
1800 case VK_DELETE
: nParam
= VK_DECIMAL
; break;
1804 if (keystate
[VK_NUMLOCK
]&1) shift_state
|= 1;
1810 /* If a key is pressed and AltGr is not active */
1811 if (key_down
&& (keystate
[VK_RMENU
]&0x80) == 0 && !compose_state
)
1813 /* Okay, prepare for most alts then ...*/
1814 if (left_alt
) *p
++ = '\033';
1816 /* Lets see if it's a pattern we know all about ... */
1817 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
1818 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
1821 if (wParam
== VK_NEXT
&& shift_state
== 1) {
1822 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
1825 if (wParam
== VK_INSERT
&& shift_state
== 1) {
1826 term_mouse (MB_PASTE
, MA_CLICK
, 0, 0);
1827 term_mouse (MB_PASTE
, MA_RELEASE
, 0, 0);
1830 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
1833 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
1835 SendMessage (hwnd
, WM_IGNORE_KEYMENU
, FALSE
, 0);
1836 SendMessage (hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
1837 SendMessage (hwnd
, WM_IGNORE_KEYMENU
, TRUE
, 0);
1841 /* Nethack keypad */
1842 if (cfg
.nethack_keypad
&& !left_alt
) {
1844 case VK_NUMPAD1
: *p
++ = shift_state ?
'B': 'b'; return p
-output
;
1845 case VK_NUMPAD2
: *p
++ = shift_state ?
'J': 'j'; return p
-output
;
1846 case VK_NUMPAD3
: *p
++ = shift_state ?
'N': 'n'; return p
-output
;
1847 case VK_NUMPAD4
: *p
++ = shift_state ?
'H': 'h'; return p
-output
;
1848 case VK_NUMPAD5
: *p
++ = shift_state ?
'.': '.'; return p
-output
;
1849 case VK_NUMPAD6
: *p
++ = shift_state ?
'L': 'l'; return p
-output
;
1850 case VK_NUMPAD7
: *p
++ = shift_state ?
'Y': 'y'; return p
-output
;
1851 case VK_NUMPAD8
: *p
++ = shift_state ?
'K': 'k'; return p
-output
;
1852 case VK_NUMPAD9
: *p
++ = shift_state ?
'U': 'u'; return p
-output
;
1856 /* Application Keypad */
1860 if ( cfg
.funky_type
== 0 ||
1861 ( cfg
.funky_type
== 1 && app_keypad_keys
)) switch(wParam
) {
1862 case VK_EXECUTE
: if (app_keypad_keys
) xkey
= 'P'; break;
1863 case VK_DIVIDE
: xkey
= 'Q'; break;
1864 case VK_MULTIPLY
:xkey
= 'R'; break;
1865 case VK_SUBTRACT
:xkey
= 'S'; break;
1867 if(app_keypad_keys
) switch(wParam
) {
1868 case VK_NUMPAD0
: xkey
= 'p'; break;
1869 case VK_NUMPAD1
: xkey
= 'q'; break;
1870 case VK_NUMPAD2
: xkey
= 'r'; break;
1871 case VK_NUMPAD3
: xkey
= 's'; break;
1872 case VK_NUMPAD4
: xkey
= 't'; break;
1873 case VK_NUMPAD5
: xkey
= 'u'; break;
1874 case VK_NUMPAD6
: xkey
= 'v'; break;
1875 case VK_NUMPAD7
: xkey
= 'w'; break;
1876 case VK_NUMPAD8
: xkey
= 'x'; break;
1877 case VK_NUMPAD9
: xkey
= 'y'; break;
1879 case VK_DECIMAL
: xkey
= 'n'; break;
1880 case VK_ADD
: if(shift_state
) xkey
= 'm';
1884 if (HIWORD(lParam
)&KF_EXTENDED
)
1892 if (xkey
>='P' && xkey
<='S')
1893 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
1895 p
+= sprintf((char *)p
, "\x1B?%c", xkey
);
1898 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
1903 if (wParam
== VK_BACK
&& shift_state
== 0 ) /* Backspace */
1905 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
1908 if (wParam
== VK_TAB
&& shift_state
== 1 ) /* Shift tab */
1910 *p
++ = 0x1B; *p
++ = '['; *p
++ = 'Z'; return p
- output
;
1912 if (wParam
== VK_SPACE
&& shift_state
== 2 ) /* Ctrl-Space */
1914 *p
++ = 0; return p
- output
;
1916 if (wParam
== VK_SPACE
&& shift_state
== 3 ) /* Ctrl-Shift-Space */
1918 *p
++ = 160; return p
- output
;
1920 if (wParam
== VK_CANCEL
&& shift_state
== 2 ) /* Ctrl-Break */
1922 *p
++ = 3; return p
- output
;
1924 /* Control-2 to Control-8 are special */
1925 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8')
1927 *p
++ = "\000\033\034\035\036\037\177"[wParam
-'2'];
1930 if (shift_state
== 2 && wParam
== 0xBD) {
1934 if (shift_state
== 2 && wParam
== 0xDF) {
1938 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
1939 *p
++ = '\r'; *p
++ = '\n';
1944 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1945 * for integer decimal nn.)
1947 * We also deal with the weird ones here. Linux VCs replace F1
1948 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1949 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1954 case VK_F1
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11); break;
1955 case VK_F2
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12); break;
1956 case VK_F3
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13); break;
1957 case VK_F4
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14); break;
1958 case VK_F5
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15); break;
1959 case VK_F6
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17); break;
1960 case VK_F7
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18); break;
1961 case VK_F8
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19); break;
1962 case VK_F9
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20); break;
1963 case VK_F10
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21); break;
1964 case VK_F11
: code
= 23; break;
1965 case VK_F12
: code
= 24; break;
1966 case VK_F13
: code
= 25; break;
1967 case VK_F14
: code
= 26; break;
1968 case VK_F15
: code
= 28; break;
1969 case VK_F16
: code
= 29; break;
1970 case VK_F17
: code
= 31; break;
1971 case VK_F18
: code
= 32; break;
1972 case VK_F19
: code
= 33; break;
1973 case VK_F20
: code
= 34; break;
1974 case VK_HOME
: code
= 1; break;
1975 case VK_INSERT
: code
= 2; break;
1976 case VK_DELETE
: code
= 3; break;
1977 case VK_END
: code
= 4; break;
1978 case VK_PRIOR
: code
= 5; break;
1979 case VK_NEXT
: code
= 6; break;
1981 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
1982 p
+= sprintf((char *)p
, "\x1B[[%c", code
+ 'A' - 11);
1985 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
1986 p
+= sprintf((char *)p
, "\x1BO%c", code
+ 'P' - 11);
1989 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
1990 p
+= sprintf((char *)p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
1994 p
+= sprintf((char *)p
, "\x1B[%d~", code
);
1999 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2000 * some reason seems to send VK_CLEAR to Windows...).
2005 case VK_UP
: xkey
= 'A'; break;
2006 case VK_DOWN
: xkey
= 'B'; break;
2007 case VK_RIGHT
: xkey
= 'C'; break;
2008 case VK_LEFT
: xkey
= 'D'; break;
2009 case VK_CLEAR
: xkey
= 'G'; break;
2014 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
2015 else if (app_cursor_keys
)
2016 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
2018 p
+= sprintf((char *)p
, "\x1B[%c", xkey
);
2024 /* Okay we've done everything interesting; let windows deal with
2025 * the boring stuff */
2027 BOOL capsOn
=keystate
[VK_CAPITAL
] !=0;
2029 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2030 if(cfg
.xlat_capslockcyr
)
2031 keystate
[VK_CAPITAL
] = 0;
2033 r
= ToAscii (wParam
, scan
, keystate
, keys
, 0);
2039 unsigned char ch
= (unsigned char)keys
[i
];
2041 if (compose_state
==2 && (ch
&0x80) == 0 && ch
>' ') {
2046 if (compose_state
==3 && (ch
&0x80) == 0 && ch
>' ') {
2050 if ((nc
=check_compose(compose_char
,ch
)) == -1)
2055 *p
++ = xlat_kbd2tty((unsigned char)nc
);
2061 if( left_alt
&& key_down
) *p
++ = '\033';
2067 ch
= xlat_latkbd2win(ch
);
2068 *p
++ = xlat_kbd2tty(ch
);
2072 /* This is so the ALT-Numpad and dead keys work correctly. */
2079 /* This stops ALT press-release doing a 'COMMAND MENU' function */
2081 if (message
== WM_SYSKEYUP
&& wParam
== VK_MENU
)
2083 keystate
[VK_MENU
] = 0;
2091 void set_title (char *title
) {
2092 sfree (window_name
);
2093 window_name
= smalloc(1+strlen(title
));
2094 strcpy (window_name
, title
);
2095 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
2096 SetWindowText (hwnd
, title
);
2099 void set_icon (char *title
) {
2101 icon_name
= smalloc(1+strlen(title
));
2102 strcpy (icon_name
, title
);
2103 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
2104 SetWindowText (hwnd
, title
);
2107 void set_sbar (int total
, int start
, int page
) {
2110 if (!cfg
.scrollbar
) return;
2112 si
.cbSize
= sizeof(si
);
2113 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
2115 si
.nMax
= total
- 1;
2119 SetScrollInfo (hwnd
, SB_VERT
, &si
, TRUE
);
2122 Context
get_ctx(void) {
2127 SelectPalette (hdc
, pal
, FALSE
);
2133 void free_ctx (Context ctx
) {
2134 SelectPalette (ctx
, GetStockObject (DEFAULT_PALETTE
), FALSE
);
2135 ReleaseDC (hwnd
, ctx
);
2138 static void real_palette_set (int n
, int r
, int g
, int b
) {
2140 logpal
->palPalEntry
[n
].peRed
= r
;
2141 logpal
->palPalEntry
[n
].peGreen
= g
;
2142 logpal
->palPalEntry
[n
].peBlue
= b
;
2143 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
2144 colours
[n
] = PALETTERGB(r
, g
, b
);
2145 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2147 colours
[n
] = RGB(r
, g
, b
);
2150 void palette_set (int n
, int r
, int g
, int b
) {
2151 static const int first
[21] = {
2152 0, 2, 4, 6, 8, 10, 12, 14,
2153 1, 3, 5, 7, 9, 11, 13, 15,
2156 real_palette_set (first
[n
], r
, g
, b
);
2158 real_palette_set (first
[n
]+1, r
, g
, b
);
2160 HDC hdc
= get_ctx();
2161 UnrealizeObject (pal
);
2162 RealizePalette (hdc
);
2167 void palette_reset (void) {
2170 for (i
= 0; i
< NCOLOURS
; i
++) {
2172 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
2173 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
2174 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
2175 logpal
->palPalEntry
[i
].peFlags
= 0;
2176 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
2177 defpal
[i
].rgbtGreen
,
2178 defpal
[i
].rgbtBlue
);
2180 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
2181 defpal
[i
].rgbtGreen
,
2182 defpal
[i
].rgbtBlue
);
2187 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2189 RealizePalette (hdc
);
2194 void write_clip (void *data
, int len
) {
2198 clipdata
= GlobalAlloc (GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
2201 lock
= GlobalLock (clipdata
);
2204 memcpy (lock
, data
, len
);
2205 ((unsigned char *) lock
) [len
] = 0;
2206 GlobalUnlock (clipdata
);
2208 SendMessage (hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
2209 if (OpenClipboard (hwnd
)) {
2211 SetClipboardData (CF_TEXT
, clipdata
);
2214 GlobalFree (clipdata
);
2215 SendMessage (hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
2218 void get_clip (void **p
, int *len
) {
2219 static HGLOBAL clipdata
= NULL
;
2223 GlobalUnlock (clipdata
);
2227 if (OpenClipboard (NULL
)) {
2228 clipdata
= GetClipboardData (CF_TEXT
);
2231 *p
= GlobalLock (clipdata
);
2245 * Move `lines' lines from position `from' to position `to' in the
2248 void optimised_move (int to
, int from
, int lines
) {
2252 min
= (to
< from ? to
: from
);
2253 max
= to
+ from
- min
;
2255 r
.left
= 0; r
.right
= cols
* font_width
;
2256 r
.top
= min
* font_height
; r
.bottom
= (max
+lines
) * font_height
;
2257 ScrollWindow (hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
2261 * Print a message box and perform a fatal exit.
2263 void fatalbox(char *fmt
, ...) {
2268 vsprintf(stuff
, fmt
, ap
);
2270 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
2277 void beep(int errorbeep
) {
2278 static long last_beep
= 0;
2279 long now
, beep_diff
;
2281 now
= GetTickCount();
2282 beep_diff
= now
-last_beep
;
2284 /* Make sure we only respond to one beep per packet or so */
2285 if (beep_diff
>=0 && beep_diff
<50)
2289 MessageBeep(MB_ICONHAND
);
2293 last_beep
= GetTickCount();