14 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
19 #define IDM_SHOWLOG 0x0010
20 #define IDM_NEWSESS 0x0020
21 #define IDM_DUPSESS 0x0030
22 #define IDM_RECONF 0x0040
23 #define IDM_CLRSB 0x0050
24 #define IDM_RESET 0x0060
25 #define IDM_TEL_AYT 0x0070
26 #define IDM_TEL_BRK 0x0080
27 #define IDM_TEL_SYNCH 0x0090
28 #define IDM_TEL_EC 0x00a0
29 #define IDM_TEL_EL 0x00b0
30 #define IDM_TEL_GA 0x00c0
31 #define IDM_TEL_NOP 0x00d0
32 #define IDM_TEL_ABORT 0x00e0
33 #define IDM_TEL_AO 0x00f0
34 #define IDM_TEL_IP 0x0100
35 #define IDM_TEL_SUSP 0x0110
36 #define IDM_TEL_EOR 0x0120
37 #define IDM_TEL_EOF 0x0130
38 #define IDM_ABOUT 0x0140
39 #define IDM_SAVEDSESS 0x0150
41 #define IDM_SAVED_MIN 0x1000
42 #define IDM_SAVED_MAX 0x2000
44 #define WM_IGNORE_SIZE (WM_XUSER + 1)
45 #define WM_IGNORE_CLIP (WM_XUSER + 2)
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 HBITMAP caretbm
;
88 static int dbltime
, lasttime
, lastact
;
89 static Mouse_Button lastbtn
;
91 static char *window_name
, *icon_name
;
93 static Ldisc
*real_ldisc
;
95 void begin_session(void) {
99 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
) {
100 static char appname
[] = "PuTTY";
105 int guess_width
, guess_height
;
108 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
110 winsock_ver
= MAKEWORD(1, 1);
111 if (WSAStartup(winsock_ver
, &wsadata
)) {
112 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
113 MB_OK
| MB_ICONEXCLAMATION
);
116 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
117 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
118 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
122 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
124 InitCommonControls();
127 * Process the command line.
132 default_protocol
= DEFAULT_PROTOCOL
;
133 default_port
= DEFAULT_PORT
;
138 while (*p
&& isspace(*p
)) p
++;
141 * Process command line options first. Yes, this can be
142 * done better, and it will be as soon as I have the
146 char *q
= p
+ strcspn(p
, " \t");
149 tolower(p
[0]) == 's' &&
150 tolower(p
[1]) == 's' &&
151 tolower(p
[2]) == 'h') {
152 default_protocol
= cfg
.protocol
= PROT_SSH
;
153 default_port
= cfg
.port
= 22;
154 } else if (q
== p
+ 3 &&
155 tolower(p
[0]) == 'l' &&
156 tolower(p
[1]) == 'o' &&
157 tolower(p
[2]) == 'g') {
158 logfile
= "putty.log";
159 } else if (q
== p
+ 7 &&
160 tolower(p
[0]) == 'c' &&
161 tolower(p
[1]) == 'l' &&
162 tolower(p
[2]) == 'e' &&
163 tolower(p
[3]) == 'a' &&
164 tolower(p
[4]) == 'n' &&
165 tolower(p
[5]) == 'u' &&
166 tolower(p
[6]) == 'p') {
168 * `putty -cleanup'. Remove all registry entries
169 * associated with PuTTY, and also find and delete
170 * the random seed file.
173 "This procedure will remove ALL Registry\n"
174 "entries associated with PuTTY, and will\n"
175 "also remove the PuTTY random seed file.\n"
177 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
178 "SESSIONS. Are you really sure you want\n"
181 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
186 p
= q
+ strspn(q
, " \t");
190 * An initial @ means to activate a saved session.
194 if (!*cfg
.host
&& !do_config()) {
198 } else if (*p
== '&') {
200 * An initial & means we've been given a command line
201 * containing the hex value of a HANDLE for a file
202 * mapping object, which we must then extract as a
207 if (sscanf(p
+1, "%p", &filemap
) == 1 &&
208 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
209 0, 0, sizeof(Config
))) != NULL
) {
212 CloseHandle(filemap
);
213 } else if (!do_config()) {
220 * If the hostname starts with "telnet:", set the
221 * protocol to Telnet and process the string as a
224 if (!strncmp(q
, "telnet:", 7)) {
228 if (q
[0] == '/' && q
[1] == '/')
230 cfg
.protocol
= PROT_TELNET
;
232 while (*p
&& *p
!= ':' && *p
!= '/') p
++;
240 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
241 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
243 while (*p
&& !isspace(*p
)) p
++;
246 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
247 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
248 while (*p
&& isspace(*p
)) p
++;
261 /* See if host is of the form user@host */
262 if (cfg
.host
[0] != '\0') {
263 char *atsign
= strchr(cfg
.host
, '@');
264 /* Make sure we're not overflowing the user field */
266 if (atsign
-cfg
.host
< sizeof cfg
.username
) {
267 strncpy (cfg
.username
, cfg
.host
, atsign
-cfg
.host
);
268 cfg
.username
[atsign
-cfg
.host
] = '\0';
270 memmove(cfg
.host
, atsign
+1, 1+strlen(atsign
+1));
276 * Select protocol. This is farmed out into a table in a
277 * separate file to enable an ssh-free variant.
282 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
283 if (backends
[i
].protocol
== cfg
.protocol
) {
284 back
= backends
[i
].backend
;
288 MessageBox(NULL
, "Unsupported protocol number found",
289 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
295 real_ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
296 /* To start with, we use the simple line discipline, so we can
297 * type passwords etc without fear of them being echoed... */
298 ldisc
= &ldisc_simple
;
302 wndclass
.lpfnWndProc
= WndProc
;
303 wndclass
.cbClsExtra
= 0;
304 wndclass
.cbWndExtra
= 0;
305 wndclass
.hInstance
= inst
;
306 wndclass
.hIcon
= LoadIcon (inst
,
307 MAKEINTRESOURCE(IDI_MAINICON
));
308 wndclass
.hCursor
= LoadCursor (NULL
, IDC_IBEAM
);
309 wndclass
.hbrBackground
= GetStockObject (BLACK_BRUSH
);
310 wndclass
.lpszMenuName
= NULL
;
311 wndclass
.lpszClassName
= appname
;
313 RegisterClass (&wndclass
);
318 savelines
= cfg
.savelines
;
324 * Guess some defaults for the window size. This all gets
325 * updated later, so we don't really care too much. However, we
326 * do want the font width/height guesses to correspond to a
327 * large font rather than a small one...
334 term_size (cfg
.height
, cfg
.width
, cfg
.savelines
);
335 guess_width
= extra_width
+ font_width
* cols
;
336 guess_height
= extra_height
+ font_height
* rows
;
339 HWND w
= GetDesktopWindow();
340 GetWindowRect (w
, &r
);
341 if (guess_width
> r
.right
- r
.left
)
342 guess_width
= r
.right
- r
.left
;
343 if (guess_height
> r
.bottom
- r
.top
)
344 guess_height
= r
.bottom
- r
.top
;
348 int winmode
= WS_OVERLAPPEDWINDOW
|WS_VSCROLL
;
349 if (!cfg
.scrollbar
) winmode
&= ~(WS_VSCROLL
);
350 if (cfg
.locksize
) winmode
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
351 hwnd
= CreateWindow (appname
, appname
,
353 CW_USEDEFAULT
, CW_USEDEFAULT
,
354 guess_width
, guess_height
,
355 NULL
, NULL
, inst
, NULL
);
359 * Initialise the fonts, simultaneously correcting the guesses
360 * for font_{width,height}.
362 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
367 * Correct the guesses for extra_{width,height}.
371 GetWindowRect (hwnd
, &wr
);
372 GetClientRect (hwnd
, &cr
);
373 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
374 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
378 * Resize the window, now we know what size we _really_ want it
381 guess_width
= extra_width
+ font_width
* cols
;
382 guess_height
= extra_height
+ font_height
* rows
;
383 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
384 SetWindowPos (hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
385 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
388 * Set up a caret bitmap, with no content.
392 int size
= (font_width
+15)/16 * 2 * font_height
;
393 bits
= calloc(size
, 1);
394 memset(bits
, 0x55, size
);
395 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
400 * Initialise the scroll bar.
405 si
.cbSize
= sizeof(si
);
406 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
411 SetScrollInfo (hwnd
, SB_VERT
, &si
, FALSE
);
415 * Start up the telnet connection.
422 error
= back
->init (hwnd
, cfg
.host
, cfg
.port
, &realhost
);
424 sprintf(msg
, "Unable to open connection:\n%s", error
);
425 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
428 window_name
= icon_name
= NULL
;
429 sprintf(msg
, "%s - PuTTY", realhost
);
434 session_closed
= FALSE
;
437 * Set up the input and output buffers.
440 outbuf_reap
= outbuf_head
= 0;
443 * Prepare the mouse handler.
445 lastact
= MA_NOTHING
;
446 lastbtn
= MB_NOTHING
;
447 dbltime
= GetDoubleClickTime();
450 * Set up the session-control options on the system menu.
453 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
457 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
458 if (cfg
.protocol
== PROT_TELNET
) {
460 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
461 AppendMenu (p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
462 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
463 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
464 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
465 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
466 AppendMenu (p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
467 AppendMenu (p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
468 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
469 AppendMenu (p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
470 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
471 AppendMenu (p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
472 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
473 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
474 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
475 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
476 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
, "Telnet Command");
477 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
479 AppendMenu (m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
480 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
481 AppendMenu (m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session");
482 AppendMenu (m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
485 for (i
= 1 ; i
< ((nsessions
< 256) ? nsessions
: 256) ; i
++)
486 AppendMenu (s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
) , sessions
[i
]);
487 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
488 AppendMenu (m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings");
489 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
490 AppendMenu (m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
491 AppendMenu (m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
492 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
493 AppendMenu (m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
497 * Finally show the window!
499 ShowWindow (hwnd
, show
);
502 * Set the palette up.
508 has_focus
= (GetForegroundWindow() == hwnd
);
512 int timer_id
= 0, long_timer
= 0;
514 while (GetMessage (&msg
, NULL
, 0, 0) == 1) {
515 /* Sometimes DispatchMessage calls routines that use their own
516 * GetMessage loop, setup this timer so we get some control back.
518 * Also call term_update() from the timer so that if the host
519 * is sending data flat out we still do redraws.
521 if(timer_id
&& long_timer
) {
522 KillTimer(hwnd
, timer_id
);
523 long_timer
= timer_id
= 0;
526 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
527 DispatchMessage (&msg
);
529 /* This is too fast, but I'll leave it for now 'cause it shows
530 * how often term_update is called (far too often at times!)
534 /* Send the paste buffer if there's anything to send */
537 /* If there's nothing new in the queue then we can do everything
538 * we've delayed, reading the socket, writing, and repainting
541 if (!PeekMessage (&msg
, NULL
, 0, 0, PM_NOREMOVE
)) {
542 if (pending_netevent
) {
543 enact_pending_netevent();
548 if (!PeekMessage (&msg
, NULL
, 0, 0, PM_NOREMOVE
)) {
550 KillTimer(hwnd
, timer_id
);
559 timer_id
= SetTimer(hwnd
, 1, 2000, NULL
);
560 else if (cfg
.blinktext
)
561 timer_id
= SetTimer(hwnd
, 1, 250, NULL
);
563 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
576 DeleteObject(fonts
[i
]);
583 if (cfg
.protocol
== PROT_SSH
) {
594 * Print a message box and close the connection.
596 void connection_fatal(char *fmt
, ...) {
601 vsprintf(stuff
, fmt
, ap
);
603 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
604 if (cfg
.close_on_exit
)
607 session_closed
= TRUE
;
608 SetWindowText (hwnd
, "PuTTY (inactive)");
613 * Actually do the job requested by a WM_NETEVENT
615 static void enact_pending_netevent(void) {
617 static int reentering
= 0;
620 return; /* don't unpend the pending */
622 pending_netevent
= FALSE
;
625 i
= back
->msg (pend_netevent_wParam
, pend_netevent_lParam
);
630 switch (WSABASEERR
+ (-i
) % 10000) {
632 sprintf(buf
, "Connection reset by peer");
635 sprintf(buf
, "Unexpected network error %d", -i
);
638 connection_fatal(buf
);
641 if (cfg
.close_on_exit
)
644 session_closed
= TRUE
;
645 MessageBox(hwnd
, "Connection closed by remote host",
646 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
647 SetWindowText (hwnd
, "PuTTY (inactive)");
653 * Copy the colour palette from the configuration data into defpal.
654 * This is non-trivial because the colour indices are different.
656 static void cfgtopalette(void) {
658 static const int ww
[] = {
659 6, 7, 8, 9, 10, 11, 12, 13,
660 14, 15, 16, 17, 18, 19, 20, 21,
661 0, 1, 2, 3, 4, 4, 5, 5
664 for (i
=0; i
<24; i
++) {
666 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
667 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
668 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
673 * Set up the colour palette.
675 static void init_palette(void) {
677 HDC hdc
= GetDC (hwnd
);
679 if (cfg
.try_palette
&&
680 GetDeviceCaps (hdc
, RASTERCAPS
) & RC_PALETTE
) {
681 logpal
= smalloc(sizeof(*logpal
)
682 - sizeof(logpal
->palPalEntry
)
683 + NCOLOURS
* sizeof(PALETTEENTRY
));
684 logpal
->palVersion
= 0x300;
685 logpal
->palNumEntries
= NCOLOURS
;
686 for (i
= 0; i
< NCOLOURS
; i
++) {
687 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
688 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
689 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
690 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
692 pal
= CreatePalette (logpal
);
694 SelectPalette (hdc
, pal
, FALSE
);
695 RealizePalette (hdc
);
696 SelectPalette (hdc
, GetStockObject (DEFAULT_PALETTE
),
700 ReleaseDC (hwnd
, hdc
);
703 for (i
=0; i
<NCOLOURS
; i
++)
704 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
708 for(i
=0; i
<NCOLOURS
; i
++)
709 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
715 * Initialise all the fonts we will need. There may be as many as
716 * eight or as few as one. We also:
718 * - check the font width and height, correcting our guesses if
721 * - verify that the bold font is the same width as the ordinary
722 * one, and engage shadow bolding if not.
724 * - verify that the underlined font is the same width as the
725 * ordinary one (manual underlining by means of line drawing can
726 * be done in a pinch).
728 static void init_fonts(int pick_width
) {
733 int fw_dontcare
, fw_bold
;
742 if (cfg
.fontisbold
) {
743 fw_dontcare
= FW_BOLD
;
746 fw_dontcare
= FW_DONTCARE
;
752 font_height
= cfg
.fontheight
;
753 font_width
= pick_width
;
756 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
757 c, OUT_DEFAULT_PRECIS, \
758 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
759 FIXED_PITCH | FF_DONTCARE, cfg.font)
761 if (cfg
.vtmode
!= VT_OEMONLY
) {
762 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
764 SelectObject (hdc
, fonts
[FONT_NORMAL
]);
765 GetTextMetrics(hdc
, &tm
);
766 font_height
= tm
.tmHeight
;
767 font_width
= tm
.tmAveCharWidth
;
769 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
772 * Some fonts, e.g. 9-pt Courier, draw their underlines
773 * outside their character cell. We successfully prevent
774 * screen corruption by clipping the text output, but then
775 * we lose the underline completely. Here we try to work
776 * out whether this is such a font, and if it is, we set a
777 * flag that causes underlines to be drawn by hand.
779 * Having tried other more sophisticated approaches (such
780 * as examining the TEXTMETRIC structure or requesting the
781 * height of a string), I think we'll do this the brute
782 * force way: we create a small bitmap, draw an underlined
783 * space on it, and test to see whether any pixels are
784 * foreground-coloured. (Since we expect the underline to
785 * go all the way across the character cell, we only search
786 * down a single column of the bitmap, half way across.)
790 HBITMAP und_bm
, und_oldbm
;
794 und_dc
= CreateCompatibleDC(hdc
);
795 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
796 und_oldbm
= SelectObject(und_dc
, und_bm
);
797 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
798 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
799 SetTextColor (und_dc
, RGB(255,255,255));
800 SetBkColor (und_dc
, RGB(0,0,0));
801 SetBkMode (und_dc
, OPAQUE
);
802 ExtTextOut (und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
804 for (i
= 0; i
< font_height
; i
++) {
805 c
= GetPixel(und_dc
, font_width
/2, i
);
809 SelectObject(und_dc
, und_oldbm
);
810 DeleteObject(und_bm
);
812 font_needs_hand_underlining
= !gotit
;
815 if (bold_mode
== BOLD_FONT
) {
816 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
817 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
820 if (cfg
.vtmode
== VT_OEMANSI
) {
821 f(FONT_OEM
, OEM_CHARSET
, fw_dontcare
, FALSE
);
822 f(FONT_OEMUND
, OEM_CHARSET
, fw_dontcare
, TRUE
);
824 if (bold_mode
== BOLD_FONT
) {
825 f(FONT_OEMBOLD
, OEM_CHARSET
, fw_bold
, FALSE
);
826 f(FONT_OEMBOLDUND
, OEM_CHARSET
, fw_bold
, TRUE
);
832 f(FONT_OEM
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
834 SelectObject (hdc
, fonts
[FONT_OEM
]);
835 GetTextMetrics(hdc
, &tm
);
836 font_height
= tm
.tmHeight
;
837 font_width
= tm
.tmAveCharWidth
;
839 f(FONT_OEMUND
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
841 if (bold_mode
== BOLD_FONT
) {
842 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
843 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
848 descent
= tm
.tmAscent
+ 1;
849 if (descent
>= font_height
)
850 descent
= font_height
- 1;
851 firstchar
= tm
.tmFirstChar
;
853 for (i
=0; i
<8; i
++) {
855 if (SelectObject (hdc
, fonts
[i
]) &&
856 GetTextMetrics(hdc
, &tm
) )
857 fsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
863 ReleaseDC (hwnd
, hdc
);
865 /* ... This is wrong in OEM only mode */
866 if (fsize
[FONT_UNDERLINE
] != fsize
[FONT_NORMAL
] ||
867 (bold_mode
== BOLD_FONT
&&
868 fsize
[FONT_BOLDUND
] != fsize
[FONT_BOLD
])) {
870 DeleteObject (fonts
[FONT_UNDERLINE
]);
871 if (bold_mode
== BOLD_FONT
)
872 DeleteObject (fonts
[FONT_BOLDUND
]);
875 if (bold_mode
== BOLD_FONT
&&
876 fsize
[FONT_BOLD
] != fsize
[FONT_NORMAL
]) {
877 bold_mode
= BOLD_SHADOW
;
878 DeleteObject (fonts
[FONT_BOLD
]);
879 if (und_mode
== UND_FONT
)
880 DeleteObject (fonts
[FONT_BOLDUND
]);
884 /* With the fascist font painting it doesn't matter if the linedraw font
885 * isn't exactly the right size anymore so we don't have to check this.
887 if (cfg
.vtmode
== VT_OEMANSI
&& fsize
[FONT_OEM
] != fsize
[FONT_NORMAL
] ) {
888 if( cfg
.fontcharset
== OEM_CHARSET
)
890 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
891 "different sizes. Using OEM-only mode instead",
892 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
893 cfg
.vtmode
= VT_OEMONLY
;
895 else if( firstchar
< ' ' )
897 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
898 "different sizes. Using XTerm mode instead",
899 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
900 cfg
.vtmode
= VT_XWINDOWS
;
904 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
905 "different sizes. Using ISO8859-1 mode instead",
906 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
907 cfg
.vtmode
= VT_POORMAN
;
912 DeleteObject (fonts
[i
]);
918 void request_resize (int w
, int h
, int refont
) {
921 /* If the window is maximized supress resizing attempts */
922 if(IsZoomed(hwnd
)) return;
925 /* Don't do this in OEMANSI, you may get disable messages */
926 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132)
927 && cfg
.vtmode
!= VT_OEMANSI
)
929 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132))
932 /* If font width too big for screen should we shrink the font more ? */
934 font_width
= ((font_width
*cols
+w
/2)/w
);
941 DeleteObject(fonts
[i
]);
943 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
945 init_fonts(font_width
);
949 static int first_time
= 1;
955 /* Get the size of the screen */
956 if (GetClientRect(GetDesktopWindow(),&ss
))
957 /* first_time = 0 */;
958 else { first_time
= 2; break; }
960 /* Make sure the values are sane */
961 width
= (ss
.right
-ss
.left
-extra_width
) / font_width
;
962 height
= (ss
.bottom
-ss
.top
-extra_height
) / font_height
;
964 if (w
>width
) w
=width
;
965 if (h
>height
) h
=height
;
971 width
= extra_width
+ font_width
* w
;
972 height
= extra_height
+ font_height
* h
;
974 SetWindowPos (hwnd
, NULL
, 0, 0, width
, height
,
975 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
976 SWP_NOMOVE
| SWP_NOZORDER
);
979 static void click (Mouse_Button b
, int x
, int y
) {
980 int thistime
= GetMessageTime();
982 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
983 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
984 lastact
== MA_2CLK ? MA_3CLK
:
985 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
990 if (lastact
!= MA_NOTHING
)
991 term_mouse (b
, lastact
, x
, y
);
995 static LRESULT CALLBACK
WndProc (HWND hwnd
, UINT message
,
996 WPARAM wParam
, LPARAM lParam
) {
998 static int ignore_size
= FALSE
;
999 static int ignore_clip
= FALSE
;
1000 static int just_reconfigged
= FALSE
;
1001 static int resizing
= FALSE
;
1005 if (pending_netevent
)
1006 enact_pending_netevent();
1016 if (!cfg
.warn_on_close
|| session_closed
||
1017 MessageBox(hwnd
, "Are you sure you want to close this session?",
1018 "PuTTY Exit Confirmation",
1019 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1020 DestroyWindow(hwnd
);
1023 PostQuitMessage (0);
1026 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1038 PROCESS_INFORMATION pi
;
1039 HANDLE filemap
= NULL
;
1041 if (wParam
== IDM_DUPSESS
) {
1043 * Allocate a file-mapping memory chunk for the
1046 SECURITY_ATTRIBUTES sa
;
1049 sa
.nLength
= sizeof(sa
);
1050 sa
.lpSecurityDescriptor
= NULL
;
1051 sa
.bInheritHandle
= TRUE
;
1052 filemap
= CreateFileMapping((HANDLE
)0xFFFFFFFF,
1059 p
= (Config
*)MapViewOfFile(filemap
,
1061 0, 0, sizeof(Config
));
1063 *p
= cfg
; /* structure copy */
1067 sprintf(c
, "putty &%p", filemap
);
1069 } else if (wParam
== IDM_SAVEDSESS
) {
1070 char *session
= sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1071 cl
= malloc(16 + strlen(session
)); /* 8, but play safe */
1073 cl
= NULL
; /* not a very important failure mode */
1075 sprintf(cl
, "putty @%s", session
);
1081 GetModuleFileName (NULL
, b
, sizeof(b
)-1);
1083 si
.lpReserved
= NULL
;
1084 si
.lpDesktop
= NULL
;
1088 si
.lpReserved2
= NULL
;
1089 CreateProcess (b
, cl
, NULL
, NULL
, TRUE
,
1090 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1093 CloseHandle(filemap
);
1099 if (!do_reconfig(hwnd
))
1101 just_reconfigged
= TRUE
;
1106 DeleteObject(fonts
[i
]);
1108 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1109 und_mode
= UND_FONT
;
1112 /* Telnet will change local echo -> remote if the remote asks */
1113 if (cfg
.protocol
!= PROT_TELNET
)
1114 ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
1122 /* Enable or disable the scroll bar, etc */
1124 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1127 if (cfg
.scrollbar
) nflg
|= WS_VSCROLL
;
1128 else nflg
&= ~WS_VSCROLL
;
1130 nflg
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1132 nflg
|= (WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1138 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1139 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1140 SetWindowPos(hwnd
, NULL
, 0,0,0,0,
1141 SWP_NOACTIVATE
|SWP_NOCOPYBITS
|
1142 SWP_NOMOVE
|SWP_NOSIZE
| SWP_NOZORDER
|
1145 GetWindowRect (hwnd
, &wr
);
1146 GetClientRect (hwnd
, &cr
);
1147 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1148 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1152 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1153 InvalidateRect(hwnd
, NULL
, TRUE
);
1154 SetWindowPos (hwnd
, NULL
, 0, 0,
1155 extra_width
+ font_width
* cfg
.width
,
1156 extra_height
+ font_height
* cfg
.height
,
1157 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1158 SWP_NOMOVE
| SWP_NOZORDER
);
1159 if (IsIconic(hwnd
)) {
1160 SetWindowText (hwnd
,
1161 cfg
.win_name_always ? window_name
: icon_name
);
1170 case IDM_TEL_AYT
: back
->special (TS_AYT
); break;
1171 case IDM_TEL_BRK
: back
->special (TS_BRK
); break;
1172 case IDM_TEL_SYNCH
: back
->special (TS_SYNCH
); break;
1173 case IDM_TEL_EC
: back
->special (TS_EC
); break;
1174 case IDM_TEL_EL
: back
->special (TS_EL
); break;
1175 case IDM_TEL_GA
: back
->special (TS_GA
); break;
1176 case IDM_TEL_NOP
: back
->special (TS_NOP
); break;
1177 case IDM_TEL_ABORT
: back
->special (TS_ABORT
); break;
1178 case IDM_TEL_AO
: back
->special (TS_AO
); break;
1179 case IDM_TEL_IP
: back
->special (TS_IP
); break;
1180 case IDM_TEL_SUSP
: back
->special (TS_SUSP
); break;
1181 case IDM_TEL_EOR
: back
->special (TS_EOR
); break;
1182 case IDM_TEL_EOF
: back
->special (TS_EOF
); break;
1187 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1188 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1193 #define X_POS(l) ((int)(short)LOWORD(l))
1194 #define Y_POS(l) ((int)(short)HIWORD(l))
1196 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1197 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1199 case WM_LBUTTONDOWN
:
1200 click (MB_SELECT
, TO_CHR_X(X_POS(lParam
)),
1201 TO_CHR_Y(Y_POS(lParam
)));
1205 term_mouse (MB_SELECT
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1206 TO_CHR_Y(Y_POS(lParam
)));
1209 case WM_MBUTTONDOWN
:
1211 click (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1212 TO_CHR_X(X_POS(lParam
)),
1213 TO_CHR_Y(Y_POS(lParam
)));
1216 term_mouse (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1217 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1218 TO_CHR_Y(Y_POS(lParam
)));
1221 case WM_RBUTTONDOWN
:
1223 click (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1224 TO_CHR_X(X_POS(lParam
)),
1225 TO_CHR_Y(Y_POS(lParam
)));
1228 term_mouse (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1229 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1230 TO_CHR_Y(Y_POS(lParam
)));
1235 * Add the mouse position and message time to the random
1236 * number noise, if we're using ssh.
1238 if (cfg
.protocol
== PROT_SSH
)
1239 noise_ultralight(lParam
);
1241 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1243 if (wParam
& MK_LBUTTON
)
1245 else if (wParam
& MK_MBUTTON
)
1246 b
= cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
;
1248 b
= cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
;
1249 term_mouse (b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1250 TO_CHR_Y(Y_POS(lParam
)));
1253 case WM_IGNORE_CLIP
:
1254 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1256 case WM_DESTROYCLIPBOARD
:
1259 ignore_clip
= FALSE
;
1265 hdc
= BeginPaint (hwnd
, &p
);
1267 SelectPalette (hdc
, pal
, TRUE
);
1268 RealizePalette (hdc
);
1270 term_paint (hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1271 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1272 SelectObject (hdc
, GetStockObject(SYSTEM_FONT
));
1273 SelectObject (hdc
, GetStockObject(WHITE_PEN
));
1274 EndPaint (hwnd
, &p
);
1279 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1280 * but the only one that's likely to try to overload us is FD_READ.
1281 * This means buffering just one is fine.
1283 if (pending_netevent
)
1284 enact_pending_netevent();
1286 pending_netevent
= TRUE
;
1287 pend_netevent_wParam
=wParam
;
1288 pend_netevent_lParam
=lParam
;
1292 CreateCaret(hwnd
, caretbm
, 0, 0);
1303 case WM_IGNORE_SIZE
:
1304 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1306 case WM_ENTERSIZEMOVE
:
1310 case WM_EXITSIZEMOVE
:
1317 int width
, height
, w
, h
, ew
, eh
;
1318 LPRECT r
= (LPRECT
)lParam
;
1320 width
= r
->right
- r
->left
- extra_width
;
1321 height
= r
->bottom
- r
->top
- extra_height
;
1322 w
= (width
+ font_width
/2) / font_width
; if (w
< 1) w
= 1;
1323 h
= (height
+ font_height
/2) / font_height
; if (h
< 1) h
= 1;
1324 UpdateSizeTip(hwnd
, w
, h
);
1325 ew
= width
- w
* font_width
;
1326 eh
= height
- h
* font_height
;
1328 if (wParam
== WMSZ_LEFT
||
1329 wParam
== WMSZ_BOTTOMLEFT
||
1330 wParam
== WMSZ_TOPLEFT
)
1336 if (wParam
== WMSZ_TOP
||
1337 wParam
== WMSZ_TOPRIGHT
||
1338 wParam
== WMSZ_TOPLEFT
)
1348 /* break; (never reached) */
1350 if (wParam
== SIZE_MINIMIZED
) {
1351 SetWindowText (hwnd
,
1352 cfg
.win_name_always ? window_name
: icon_name
);
1355 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1356 SetWindowText (hwnd
, window_name
);
1358 int width
, height
, w
, h
;
1359 #if 0 /* we have fixed this using WM_SIZING now */
1363 width
= LOWORD(lParam
);
1364 height
= HIWORD(lParam
);
1365 w
= width
/ font_width
; if (w
< 1) w
= 1;
1366 h
= height
/ font_height
; if (h
< 1) h
= 1;
1367 #if 0 /* we have fixed this using WM_SIZING now */
1368 ew
= width
- w
* font_width
;
1369 eh
= height
- h
* font_height
;
1370 if (ew
!= 0 || eh
!= 0) {
1372 GetWindowRect (hwnd
, &r
);
1373 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1374 SetWindowPos (hwnd
, NULL
, 0, 0,
1375 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1376 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1379 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1381 term_size (h
, w
, cfg
.savelines
);
1383 * Don't call back->size in mid-resize. (To prevent
1384 * massive numbers of resize events getting sent
1385 * down the connection during an NT opaque drag.)
1389 just_reconfigged
= FALSE
;
1392 ignore_size
= FALSE
;
1395 switch (LOWORD(wParam
)) {
1396 case SB_BOTTOM
: term_scroll(-1, 0); break;
1397 case SB_TOP
: term_scroll(+1, 0); break;
1398 case SB_LINEDOWN
: term_scroll (0, +1); break;
1399 case SB_LINEUP
: term_scroll (0, -1); break;
1400 case SB_PAGEDOWN
: term_scroll (0, +rows
/2); break;
1401 case SB_PAGEUP
: term_scroll (0, -rows
/2); break;
1402 case SB_THUMBPOSITION
: case SB_THUMBTRACK
:
1403 term_scroll (1, HIWORD(wParam
)); break;
1406 case WM_PALETTECHANGED
:
1407 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1408 HDC hdc
= get_ctx();
1410 if (RealizePalette (hdc
) > 0)
1416 case WM_QUERYNEWPALETTE
:
1418 HDC hdc
= get_ctx();
1420 if (RealizePalette (hdc
) > 0)
1432 * Add the scan code and keypress timing to the random
1433 * number noise, if we're using ssh.
1435 if (cfg
.protocol
== PROT_SSH
)
1436 noise_ultralight(lParam
);
1439 * We don't do TranslateMessage since it disassociates the
1440 * resulting CHAR message from the KEYDOWN that sparked it,
1441 * which we occasionally don't want. Instead, we process
1442 * KEYDOWN, and call the Win32 translator functions so that
1443 * we get the translations under _our_ control.
1446 unsigned char buf
[20];
1449 len
= TranslateKey (message
, wParam
, lParam
, buf
);
1451 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1452 ldisc
->send (buf
, len
);
1458 * Nevertheless, we are prepared to deal with WM_CHAR
1459 * messages, should they crop up. So if someone wants to
1460 * post the things to us as part of a macro manoeuvre,
1461 * we're ready to cope.
1464 char c
= xlat_kbd2tty((unsigned char)wParam
);
1465 ldisc
->send (&c
, 1);
1470 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1474 * Move the system caret. (We maintain one, even though it's
1475 * invisible, for the benefit of blind people: apparently some
1476 * helper software tracks the system caret, so we should arrange to
1479 void sys_cursor(int x
, int y
) {
1480 SetCaretPos(x
* font_width
, y
* font_height
);
1484 * Draw a line of text in the window, at given character
1485 * coordinates, in given attributes.
1487 * We are allowed to fiddle with the contents of `text'.
1489 void do_text (Context ctx
, int x
, int y
, char *text
, int len
,
1490 unsigned long attr
, int lattr
) {
1492 int nfg
, nbg
, nfont
;
1495 int force_manual_underline
= 0;
1496 int fnt_width
= font_width
*(1+(lattr
!=LATTR_NORM
));
1497 static int *IpDx
= 0, IpDxLEN
= 0;;
1499 if (len
>IpDxLEN
|| IpDx
[0] != fnt_width
) {
1503 IpDx
= smalloc((len
+16)*sizeof(int));
1506 for(i
=0; i
<IpDxLEN
; i
++)
1507 IpDx
[i
] = fnt_width
;
1513 if (attr
& ATTR_ACTCURS
) {
1514 attr
&= (bold_mode
== BOLD_COLOURS ?
0x300200 : 0x300300);
1515 attr
^= ATTR_CUR_XOR
;
1519 if (cfg
.vtmode
== VT_OEMONLY
)
1523 * Map high-half characters in order to approximate ISO using
1524 * OEM character set. No characters are missing if the OEM codepage
1527 if (nfont
& FONT_OEM
) {
1529 for (i
=0; i
<len
; i
++)
1530 if (text
[i
] >= '\xA0' && text
[i
] <= '\xFF') {
1532 /* This is CP850 ... perfect translation */
1533 static const char oemhighhalf
[] =
1534 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1535 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1536 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1537 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1538 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1539 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1540 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1541 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1542 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1543 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1544 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1545 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1548 /* This is CP437 ... junk translation */
1549 static const unsigned char oemhighhalf
[] = {
1550 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1551 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1552 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1553 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1554 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1555 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1556 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1557 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1558 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1559 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1560 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1561 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1564 text
[i
] = oemhighhalf
[(unsigned char)text
[i
] - 0xA0];
1568 if (attr
& ATTR_GBCHR
) {
1571 * GB mapping: map # to pound, and everything else stays
1574 for (i
=0; i
<len
; i
++)
1576 text
[i
] = cfg
.vtmode
== VT_OEMONLY ?
'\x9C' : '\xA3';
1577 } else if (attr
& ATTR_LINEDRW
) {
1580 static const char poorman
[] =
1581 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1584 static const char oemmap_437
[] =
1585 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1586 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1589 static const char oemmap_850
[] =
1590 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1591 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1593 /* Poor windows font ... eg: windows courier */
1594 static const char oemmap
[] =
1595 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1596 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1599 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1600 * VT100 line drawing chars; everything else stays normal.
1602 switch (cfg
.vtmode
) {
1604 for (i
=0; i
<len
; i
++)
1605 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1606 text
[i
] += '\x01' - '\x60';
1609 /* Make sure we actually have an OEM font */
1610 if (fonts
[nfont
|FONT_OEM
]) {
1613 for (i
=0; i
<len
; i
++)
1614 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1615 text
[i
] = oemmap
[(unsigned char)text
[i
] - 0x60];
1619 for (i
=0; i
<len
; i
++)
1620 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1621 text
[i
] = poorman
[(unsigned char)text
[i
] - 0x60];
1626 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
1627 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
1628 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
1630 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
1631 nfont
|= FONT_UNDERLINE
;
1634 if (nfont
&FONT_UNDERLINE
)
1635 force_manual_underline
= 1;
1636 /* Don't do the same for manual bold, it could be bad news. */
1638 nfont
&= ~(FONT_BOLD
|FONT_UNDERLINE
);
1640 if (font_needs_hand_underlining
&& (attr
& ATTR_UNDER
))
1641 force_manual_underline
= 1;
1642 if (attr
& ATTR_REVERSE
) {
1643 t
= nfg
; nfg
= nbg
; nbg
= t
;
1645 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
1647 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
1651 SelectObject (hdc
, fonts
[nfont
]);
1652 SetTextColor (hdc
, fg
);
1653 SetBkColor (hdc
, bg
);
1654 SetBkMode (hdc
, OPAQUE
);
1657 line_box
.right
= x
+fnt_width
*len
;
1658 line_box
.bottom
= y
+font_height
;
1659 ExtTextOut (hdc
, x
, y
, ETO_CLIPPED
|ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
1660 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
1661 SetBkMode (hdc
, TRANSPARENT
);
1663 /* GRR: This draws the character outside it's box and can leave
1664 * 'droppings' even with the clip box! I suppose I could loop it
1665 * one character at a time ... yuk.
1667 * Or ... I could do a test print with "W", and use +1 or -1 for this
1668 * shift depending on if the leftmost column is blank...
1670 ExtTextOut (hdc
, x
-1, y
, ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
1672 if (force_manual_underline
||
1673 (und_mode
== UND_LINE
&& (attr
& ATTR_UNDER
))) {
1675 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, fg
));
1676 MoveToEx (hdc
, x
, y
+descent
, NULL
);
1677 LineTo (hdc
, x
+len
*fnt_width
, y
+descent
);
1678 oldpen
= SelectObject (hdc
, oldpen
);
1679 DeleteObject (oldpen
);
1681 if (attr
& ATTR_PASCURS
) {
1684 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
1685 pts
[2].x
= pts
[3].x
= x
+fnt_width
-1;
1686 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
1687 pts
[1].y
= pts
[2].y
= y
+font_height
-1;
1688 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
1689 Polyline (hdc
, pts
, 5);
1690 oldpen
= SelectObject (hdc
, oldpen
);
1691 DeleteObject (oldpen
);
1695 static int check_compose(int first
, int second
) {
1697 static char * composetbl
[] = {
1698 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1699 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1700 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1701 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1702 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1703 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1704 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1705 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1706 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1707 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1708 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1709 "\"uü", "'yý", "htþ", "\"yÿ",
1713 static int recurse
= 0;
1720 sprintf(buf
, "cc(%d,%d)", first
, second
);
1725 for(c
=composetbl
; *c
; c
++) {
1726 if( (*c
)[0] == first
&& (*c
)[1] == second
)
1728 return (*c
)[2] & 0xFF;
1735 nc
= check_compose(second
, first
);
1737 nc
= check_compose(toupper(first
), toupper(second
));
1739 nc
= check_compose(toupper(second
), toupper(first
));
1747 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1748 * codes. Returns number of bytes used or zero to drop the message
1749 * or -1 to forward the message to windows.
1751 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
, unsigned char *output
) {
1753 int scan
, left_alt
= 0, key_down
, shift_state
;
1755 unsigned char * p
= output
;
1757 static WORD keys
[3];
1758 static int compose_state
= 0;
1759 static int compose_char
= 0;
1760 static WPARAM compose_key
= 0;
1762 r
= GetKeyboardState(keystate
);
1763 if (!r
) memset(keystate
, 0, sizeof(keystate
));
1766 /* Note if AltGr was pressed and if it was used as a compose key */
1767 if (wParam
== VK_MENU
&& (HIWORD(lParam
)&KF_EXTENDED
))
1769 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
1770 if (!compose_state
) compose_key
= wParam
;
1772 if (wParam
== VK_APPS
&& !compose_state
)
1773 compose_key
= wParam
;
1775 if (wParam
== compose_key
)
1777 if (compose_state
== 0 && (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
1779 else if (compose_state
== 1 && (HIWORD(lParam
)&KF_UP
))
1784 else if (compose_state
==1 && wParam
!= VK_CONTROL
)
1787 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1788 if ( (cfg
.funky_type
== 0 || (cfg
.funky_type
== 1 && app_keypad_keys
))
1789 && wParam
==VK_NUMLOCK
&& !(keystate
[VK_SHIFT
]&0x80)) {
1791 wParam
= VK_EXECUTE
;
1793 /* UnToggle NUMLock */
1794 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
1795 keystate
[VK_NUMLOCK
] ^= 1;
1798 /* And write back the 'adjusted' state */
1799 SetKeyboardState (keystate
);
1802 /* Disable Auto repeat if required */
1803 if (repeat_off
&& (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
)
1806 if ((HIWORD(lParam
)&KF_ALTDOWN
) && (keystate
[VK_RMENU
]&0x80) == 0)
1809 key_down
= ((HIWORD(lParam
)&KF_UP
)==0);
1811 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1812 if (left_alt
&& (keystate
[VK_CONTROL
]&0x80))
1813 keystate
[VK_MENU
] = 0;
1815 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
1816 shift_state
= ((keystate
[VK_SHIFT
]&0x80)!=0)
1817 + ((keystate
[VK_CONTROL
]&0x80)!=0)*2;
1820 * Record that we pressed key so the scroll window can be reset, but
1821 * be careful to avoid Shift-UP/Down
1823 if( wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
1827 /* Make sure we're not pasting */
1828 if (key_down
) term_nopaste();
1830 if (compose_state
>1 && left_alt
) compose_state
= 0;
1832 /* Sanitize the number pad if not using a PC NumPad */
1833 if( left_alt
|| (app_keypad_keys
&& cfg
.funky_type
!= 2)
1834 || cfg
.nethack_keypad
|| compose_state
)
1836 if ((HIWORD(lParam
)&KF_EXTENDED
) == 0)
1841 case VK_INSERT
: nParam
= VK_NUMPAD0
; break;
1842 case VK_END
: nParam
= VK_NUMPAD1
; break;
1843 case VK_DOWN
: nParam
= VK_NUMPAD2
; break;
1844 case VK_NEXT
: nParam
= VK_NUMPAD3
; break;
1845 case VK_LEFT
: nParam
= VK_NUMPAD4
; break;
1846 case VK_CLEAR
: nParam
= VK_NUMPAD5
; break;
1847 case VK_RIGHT
: nParam
= VK_NUMPAD6
; break;
1848 case VK_HOME
: nParam
= VK_NUMPAD7
; break;
1849 case VK_UP
: nParam
= VK_NUMPAD8
; break;
1850 case VK_PRIOR
: nParam
= VK_NUMPAD9
; break;
1851 case VK_DELETE
: nParam
= VK_DECIMAL
; break;
1855 if (keystate
[VK_NUMLOCK
]&1) shift_state
|= 1;
1861 /* If a key is pressed and AltGr is not active */
1862 if (key_down
&& (keystate
[VK_RMENU
]&0x80) == 0 && !compose_state
)
1864 /* Okay, prepare for most alts then ...*/
1865 if (left_alt
) *p
++ = '\033';
1867 /* Lets see if it's a pattern we know all about ... */
1868 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
1869 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
1872 if (wParam
== VK_NEXT
&& shift_state
== 1) {
1873 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
1876 if (wParam
== VK_INSERT
&& shift_state
== 1) {
1877 term_mouse (MB_PASTE
, MA_CLICK
, 0, 0);
1878 term_mouse (MB_PASTE
, MA_RELEASE
, 0, 0);
1881 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
1884 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
1886 SendMessage (hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
1890 /* Nethack keypad */
1891 if (cfg
.nethack_keypad
&& !left_alt
) {
1893 case VK_NUMPAD1
: *p
++ = shift_state ?
'B': 'b'; return p
-output
;
1894 case VK_NUMPAD2
: *p
++ = shift_state ?
'J': 'j'; return p
-output
;
1895 case VK_NUMPAD3
: *p
++ = shift_state ?
'N': 'n'; return p
-output
;
1896 case VK_NUMPAD4
: *p
++ = shift_state ?
'H': 'h'; return p
-output
;
1897 case VK_NUMPAD5
: *p
++ = shift_state ?
'.': '.'; return p
-output
;
1898 case VK_NUMPAD6
: *p
++ = shift_state ?
'L': 'l'; return p
-output
;
1899 case VK_NUMPAD7
: *p
++ = shift_state ?
'Y': 'y'; return p
-output
;
1900 case VK_NUMPAD8
: *p
++ = shift_state ?
'K': 'k'; return p
-output
;
1901 case VK_NUMPAD9
: *p
++ = shift_state ?
'U': 'u'; return p
-output
;
1905 /* Application Keypad */
1909 if ( cfg
.funky_type
== 0 ||
1910 ( cfg
.funky_type
== 1 && app_keypad_keys
)) switch(wParam
) {
1911 case VK_EXECUTE
: if (app_keypad_keys
) xkey
= 'P'; break;
1912 case VK_DIVIDE
: xkey
= 'Q'; break;
1913 case VK_MULTIPLY
:xkey
= 'R'; break;
1914 case VK_SUBTRACT
:xkey
= 'S'; break;
1916 if(app_keypad_keys
) switch(wParam
) {
1917 case VK_NUMPAD0
: xkey
= 'p'; break;
1918 case VK_NUMPAD1
: xkey
= 'q'; break;
1919 case VK_NUMPAD2
: xkey
= 'r'; break;
1920 case VK_NUMPAD3
: xkey
= 's'; break;
1921 case VK_NUMPAD4
: xkey
= 't'; break;
1922 case VK_NUMPAD5
: xkey
= 'u'; break;
1923 case VK_NUMPAD6
: xkey
= 'v'; break;
1924 case VK_NUMPAD7
: xkey
= 'w'; break;
1925 case VK_NUMPAD8
: xkey
= 'x'; break;
1926 case VK_NUMPAD9
: xkey
= 'y'; break;
1928 case VK_DECIMAL
: xkey
= 'n'; break;
1929 case VK_ADD
: if(shift_state
) xkey
= 'm';
1933 if (HIWORD(lParam
)&KF_EXTENDED
)
1941 if (xkey
>='P' && xkey
<='S')
1942 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
1944 p
+= sprintf((char *)p
, "\x1B?%c", xkey
);
1947 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
1952 if (wParam
== VK_BACK
&& shift_state
== 0 ) /* Backspace */
1954 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
1957 if (wParam
== VK_TAB
&& shift_state
== 1 ) /* Shift tab */
1959 *p
++ = 0x1B; *p
++ = '['; *p
++ = 'Z'; return p
- output
;
1961 if (wParam
== VK_SPACE
&& shift_state
== 2 ) /* Ctrl-Space */
1963 *p
++ = 0; return p
- output
;
1965 if (wParam
== VK_SPACE
&& shift_state
== 3 ) /* Ctrl-Shift-Space */
1967 *p
++ = 160; return p
- output
;
1969 if (wParam
== VK_CANCEL
&& shift_state
== 2 ) /* Ctrl-Break */
1971 *p
++ = 3; return p
- output
;
1973 /* Control-2 to Control-8 are special */
1974 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8')
1976 *p
++ = "\000\033\034\035\036\037\177"[wParam
-'2'];
1979 if (shift_state
== 2 && wParam
== 0xBD) {
1983 if (shift_state
== 2 && wParam
== 0xDF) {
1987 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
1988 *p
++ = '\r'; *p
++ = '\n';
1993 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1994 * for integer decimal nn.)
1996 * We also deal with the weird ones here. Linux VCs replace F1
1997 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1998 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2003 case VK_F1
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11); break;
2004 case VK_F2
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12); break;
2005 case VK_F3
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13); break;
2006 case VK_F4
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14); break;
2007 case VK_F5
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15); break;
2008 case VK_F6
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17); break;
2009 case VK_F7
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18); break;
2010 case VK_F8
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19); break;
2011 case VK_F9
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20); break;
2012 case VK_F10
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21); break;
2013 case VK_F11
: code
= 23; break;
2014 case VK_F12
: code
= 24; break;
2015 case VK_F13
: code
= 25; break;
2016 case VK_F14
: code
= 26; break;
2017 case VK_F15
: code
= 28; break;
2018 case VK_F16
: code
= 29; break;
2019 case VK_F17
: code
= 31; break;
2020 case VK_F18
: code
= 32; break;
2021 case VK_F19
: code
= 33; break;
2022 case VK_F20
: code
= 34; break;
2023 case VK_HOME
: code
= 1; break;
2024 case VK_INSERT
: code
= 2; break;
2025 case VK_DELETE
: code
= 3; break;
2026 case VK_END
: code
= 4; break;
2027 case VK_PRIOR
: code
= 5; break;
2028 case VK_NEXT
: code
= 6; break;
2030 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
2031 p
+= sprintf((char *)p
, "\x1B[[%c", code
+ 'A' - 11);
2034 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
2035 p
+= sprintf((char *)p
, "\x1BO%c", code
+ 'P' - 11);
2038 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
2039 p
+= sprintf((char *)p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
2043 p
+= sprintf((char *)p
, "\x1B[%d~", code
);
2048 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2049 * some reason seems to send VK_CLEAR to Windows...).
2054 case VK_UP
: xkey
= 'A'; break;
2055 case VK_DOWN
: xkey
= 'B'; break;
2056 case VK_RIGHT
: xkey
= 'C'; break;
2057 case VK_LEFT
: xkey
= 'D'; break;
2058 case VK_CLEAR
: xkey
= 'G'; break;
2063 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
2064 else if (app_cursor_keys
)
2065 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
2067 p
+= sprintf((char *)p
, "\x1B[%c", xkey
);
2073 /* Okay we've done everything interesting; let windows deal with
2074 * the boring stuff */
2076 BOOL capsOn
=keystate
[VK_CAPITAL
] !=0;
2078 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2079 if(cfg
.xlat_capslockcyr
)
2080 keystate
[VK_CAPITAL
] = 0;
2082 r
= ToAscii (wParam
, scan
, keystate
, keys
, 0);
2088 unsigned char ch
= (unsigned char)keys
[i
];
2090 if (compose_state
==2 && (ch
&0x80) == 0 && ch
>' ') {
2095 if (compose_state
==3 && (ch
&0x80) == 0 && ch
>' ') {
2099 if ((nc
=check_compose(compose_char
,ch
)) == -1)
2104 *p
++ = xlat_kbd2tty((unsigned char)nc
);
2110 if( left_alt
&& key_down
) *p
++ = '\033';
2116 ch
= xlat_latkbd2win(ch
);
2117 *p
++ = xlat_kbd2tty(ch
);
2121 /* This is so the ALT-Numpad and dead keys work correctly. */
2128 /* This stops ALT press-release doing a 'COMMAND MENU' function */
2129 if (message
== WM_SYSKEYUP
&& wParam
== VK_MENU
)
2131 keystate
[VK_MENU
] = 0;
2138 void set_title (char *title
) {
2139 sfree (window_name
);
2140 window_name
= smalloc(1+strlen(title
));
2141 strcpy (window_name
, title
);
2142 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
2143 SetWindowText (hwnd
, title
);
2146 void set_icon (char *title
) {
2148 icon_name
= smalloc(1+strlen(title
));
2149 strcpy (icon_name
, title
);
2150 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
2151 SetWindowText (hwnd
, title
);
2154 void set_sbar (int total
, int start
, int page
) {
2157 if (!cfg
.scrollbar
) return;
2159 si
.cbSize
= sizeof(si
);
2160 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
2162 si
.nMax
= total
- 1;
2166 SetScrollInfo (hwnd
, SB_VERT
, &si
, TRUE
);
2169 Context
get_ctx(void) {
2174 SelectPalette (hdc
, pal
, FALSE
);
2180 void free_ctx (Context ctx
) {
2181 SelectPalette (ctx
, GetStockObject (DEFAULT_PALETTE
), FALSE
);
2182 ReleaseDC (hwnd
, ctx
);
2185 static void real_palette_set (int n
, int r
, int g
, int b
) {
2187 logpal
->palPalEntry
[n
].peRed
= r
;
2188 logpal
->palPalEntry
[n
].peGreen
= g
;
2189 logpal
->palPalEntry
[n
].peBlue
= b
;
2190 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
2191 colours
[n
] = PALETTERGB(r
, g
, b
);
2192 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2194 colours
[n
] = RGB(r
, g
, b
);
2197 void palette_set (int n
, int r
, int g
, int b
) {
2198 static const int first
[21] = {
2199 0, 2, 4, 6, 8, 10, 12, 14,
2200 1, 3, 5, 7, 9, 11, 13, 15,
2203 real_palette_set (first
[n
], r
, g
, b
);
2205 real_palette_set (first
[n
]+1, r
, g
, b
);
2207 HDC hdc
= get_ctx();
2208 UnrealizeObject (pal
);
2209 RealizePalette (hdc
);
2214 void palette_reset (void) {
2217 for (i
= 0; i
< NCOLOURS
; i
++) {
2219 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
2220 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
2221 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
2222 logpal
->palPalEntry
[i
].peFlags
= 0;
2223 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
2224 defpal
[i
].rgbtGreen
,
2225 defpal
[i
].rgbtBlue
);
2227 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
2228 defpal
[i
].rgbtGreen
,
2229 defpal
[i
].rgbtBlue
);
2234 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2236 RealizePalette (hdc
);
2241 void write_clip (void *data
, int len
) {
2245 clipdata
= GlobalAlloc (GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
2248 lock
= GlobalLock (clipdata
);
2251 memcpy (lock
, data
, len
);
2252 ((unsigned char *) lock
) [len
] = 0;
2253 GlobalUnlock (clipdata
);
2255 SendMessage (hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
2256 if (OpenClipboard (hwnd
)) {
2258 SetClipboardData (CF_TEXT
, clipdata
);
2261 GlobalFree (clipdata
);
2262 SendMessage (hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
2265 void get_clip (void **p
, int *len
) {
2266 static HGLOBAL clipdata
= NULL
;
2270 GlobalUnlock (clipdata
);
2274 if (OpenClipboard (NULL
)) {
2275 clipdata
= GetClipboardData (CF_TEXT
);
2278 *p
= GlobalLock (clipdata
);
2292 * Move `lines' lines from position `from' to position `to' in the
2295 void optimised_move (int to
, int from
, int lines
) {
2299 min
= (to
< from ? to
: from
);
2300 max
= to
+ from
- min
;
2302 r
.left
= 0; r
.right
= cols
* font_width
;
2303 r
.top
= min
* font_height
; r
.bottom
= (max
+lines
) * font_height
;
2304 ScrollWindow (hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
2308 * Print a message box and perform a fatal exit.
2310 void fatalbox(char *fmt
, ...) {
2315 vsprintf(stuff
, fmt
, ap
);
2317 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
2324 void beep(int errorbeep
) {
2325 static long last_beep
= 0;
2326 long now
, beep_diff
;
2328 now
= GetTickCount();
2329 beep_diff
= now
-last_beep
;
2331 /* Make sure we only respond to one beep per packet or so */
2332 if (beep_diff
>=0 && beep_diff
<50)
2336 MessageBeep(MB_ICONHAND
);
2340 last_beep
= GetTickCount();