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 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
399 * Initialise the scroll bar.
404 si
.cbSize
= sizeof(si
);
405 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
410 SetScrollInfo (hwnd
, SB_VERT
, &si
, FALSE
);
414 * Start up the telnet connection.
421 error
= back
->init (hwnd
, cfg
.host
, cfg
.port
, &realhost
);
423 sprintf(msg
, "Unable to open connection:\n%s", error
);
424 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
427 window_name
= icon_name
= NULL
;
428 sprintf(msg
, "%s - PuTTY", realhost
);
433 session_closed
= FALSE
;
436 * Set up the input and output buffers.
439 outbuf_reap
= outbuf_head
= 0;
442 * Prepare the mouse handler.
444 lastact
= MA_NOTHING
;
445 lastbtn
= MB_NOTHING
;
446 dbltime
= GetDoubleClickTime();
449 * Set up the session-control options on the system menu.
452 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
456 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
457 if (cfg
.protocol
== PROT_TELNET
) {
459 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
460 AppendMenu (p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
461 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
462 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
463 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
464 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
465 AppendMenu (p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
466 AppendMenu (p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
467 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
468 AppendMenu (p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
469 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
470 AppendMenu (p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
471 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
472 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
473 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
474 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
475 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
, "Telnet Command");
476 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
478 AppendMenu (m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
479 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
480 AppendMenu (m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session");
481 AppendMenu (m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
484 for (i
= 1 ; i
< ((nsessions
< 256) ? nsessions
: 256) ; i
++)
485 AppendMenu (s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
) , sessions
[i
]);
486 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
487 AppendMenu (m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings");
488 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
489 AppendMenu (m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
490 AppendMenu (m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
491 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
492 AppendMenu (m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
496 * Finally show the window!
498 ShowWindow (hwnd
, show
);
501 * Set the palette up.
507 has_focus
= (GetForegroundWindow() == hwnd
);
511 int timer_id
= 0, long_timer
= 0;
513 while (GetMessage (&msg
, NULL
, 0, 0) == 1) {
514 /* Sometimes DispatchMessage calls routines that use their own
515 * GetMessage loop, setup this timer so we get some control back.
517 * Also call term_update() from the timer so that if the host
518 * is sending data flat out we still do redraws.
520 if(timer_id
&& long_timer
) {
521 KillTimer(hwnd
, timer_id
);
522 long_timer
= timer_id
= 0;
525 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
526 DispatchMessage (&msg
);
528 /* This is too fast, but I'll leave it for now 'cause it shows
529 * how often term_update is called (far too often at times!)
533 /* Send the paste buffer if there's anything to send */
536 /* If there's nothing new in the queue then we can do everything
537 * we've delayed, reading the socket, writing, and repainting
540 if (!PeekMessage (&msg
, NULL
, 0, 0, PM_NOREMOVE
)) {
541 if (pending_netevent
) {
542 enact_pending_netevent();
547 if (!PeekMessage (&msg
, NULL
, 0, 0, PM_NOREMOVE
)) {
549 KillTimer(hwnd
, timer_id
);
558 timer_id
= SetTimer(hwnd
, 1, 2000, NULL
);
559 else if (cfg
.blinktext
)
560 timer_id
= SetTimer(hwnd
, 1, 250, NULL
);
562 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
575 DeleteObject(fonts
[i
]);
582 if (cfg
.protocol
== PROT_SSH
) {
593 * Print a message box and close the connection.
595 void connection_fatal(char *fmt
, ...) {
600 vsprintf(stuff
, fmt
, ap
);
602 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
603 if (cfg
.close_on_exit
)
606 session_closed
= TRUE
;
607 SetWindowText (hwnd
, "PuTTY (inactive)");
612 * Actually do the job requested by a WM_NETEVENT
614 static void enact_pending_netevent(void) {
616 static int reentering
= 0;
619 return; /* don't unpend the pending */
621 pending_netevent
= FALSE
;
624 i
= back
->msg (pend_netevent_wParam
, pend_netevent_lParam
);
629 switch (WSABASEERR
+ (-i
) % 10000) {
631 sprintf(buf
, "Connection reset by peer");
634 sprintf(buf
, "Unexpected network error %d", -i
);
637 connection_fatal(buf
);
640 if (cfg
.close_on_exit
)
643 session_closed
= TRUE
;
644 MessageBox(hwnd
, "Connection closed by remote host",
645 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
646 SetWindowText (hwnd
, "PuTTY (inactive)");
652 * Copy the colour palette from the configuration data into defpal.
653 * This is non-trivial because the colour indices are different.
655 static void cfgtopalette(void) {
657 static const int ww
[] = {
658 6, 7, 8, 9, 10, 11, 12, 13,
659 14, 15, 16, 17, 18, 19, 20, 21,
660 0, 1, 2, 3, 4, 4, 5, 5
663 for (i
=0; i
<24; i
++) {
665 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
666 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
667 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
672 * Set up the colour palette.
674 static void init_palette(void) {
676 HDC hdc
= GetDC (hwnd
);
678 if (cfg
.try_palette
&&
679 GetDeviceCaps (hdc
, RASTERCAPS
) & RC_PALETTE
) {
680 logpal
= smalloc(sizeof(*logpal
)
681 - sizeof(logpal
->palPalEntry
)
682 + NCOLOURS
* sizeof(PALETTEENTRY
));
683 logpal
->palVersion
= 0x300;
684 logpal
->palNumEntries
= NCOLOURS
;
685 for (i
= 0; i
< NCOLOURS
; i
++) {
686 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
687 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
688 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
689 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
691 pal
= CreatePalette (logpal
);
693 SelectPalette (hdc
, pal
, FALSE
);
694 RealizePalette (hdc
);
695 SelectPalette (hdc
, GetStockObject (DEFAULT_PALETTE
),
699 ReleaseDC (hwnd
, hdc
);
702 for (i
=0; i
<NCOLOURS
; i
++)
703 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
707 for(i
=0; i
<NCOLOURS
; i
++)
708 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
714 * Initialise all the fonts we will need. There may be as many as
715 * eight or as few as one. We also:
717 * - check the font width and height, correcting our guesses if
720 * - verify that the bold font is the same width as the ordinary
721 * one, and engage shadow bolding if not.
723 * - verify that the underlined font is the same width as the
724 * ordinary one (manual underlining by means of line drawing can
725 * be done in a pinch).
727 static void init_fonts(int pick_width
) {
732 int fw_dontcare
, fw_bold
;
741 if (cfg
.fontisbold
) {
742 fw_dontcare
= FW_BOLD
;
745 fw_dontcare
= FW_DONTCARE
;
751 font_height
= cfg
.fontheight
;
752 font_width
= pick_width
;
755 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
756 c, OUT_DEFAULT_PRECIS, \
757 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
758 FIXED_PITCH | FF_DONTCARE, cfg.font)
760 if (cfg
.vtmode
!= VT_OEMONLY
) {
761 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
763 SelectObject (hdc
, fonts
[FONT_NORMAL
]);
764 GetTextMetrics(hdc
, &tm
);
765 font_height
= tm
.tmHeight
;
766 font_width
= tm
.tmAveCharWidth
;
768 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
771 * Some fonts, e.g. 9-pt Courier, draw their underlines
772 * outside their character cell. We successfully prevent
773 * screen corruption by clipping the text output, but then
774 * we lose the underline completely. Here we try to work
775 * out whether this is such a font, and if it is, we set a
776 * flag that causes underlines to be drawn by hand.
778 * Having tried other more sophisticated approaches (such
779 * as examining the TEXTMETRIC structure or requesting the
780 * height of a string), I think we'll do this the brute
781 * force way: we create a small bitmap, draw an underlined
782 * space on it, and test to see whether any pixels are
783 * foreground-coloured. (Since we expect the underline to
784 * go all the way across the character cell, we only search
785 * down a single column of the bitmap, half way across.)
789 HBITMAP und_bm
, und_oldbm
;
793 und_dc
= CreateCompatibleDC(hdc
);
794 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
795 und_oldbm
= SelectObject(und_dc
, und_bm
);
796 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
797 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
798 SetTextColor (und_dc
, RGB(255,255,255));
799 SetBkColor (und_dc
, RGB(0,0,0));
800 SetBkMode (und_dc
, OPAQUE
);
801 ExtTextOut (und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
803 for (i
= 0; i
< font_height
; i
++) {
804 c
= GetPixel(und_dc
, font_width
/2, i
);
808 SelectObject(und_dc
, und_oldbm
);
809 DeleteObject(und_bm
);
811 font_needs_hand_underlining
= !gotit
;
814 if (bold_mode
== BOLD_FONT
) {
815 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
816 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
819 if (cfg
.vtmode
== VT_OEMANSI
) {
820 f(FONT_OEM
, OEM_CHARSET
, fw_dontcare
, FALSE
);
821 f(FONT_OEMUND
, OEM_CHARSET
, fw_dontcare
, TRUE
);
823 if (bold_mode
== BOLD_FONT
) {
824 f(FONT_OEMBOLD
, OEM_CHARSET
, fw_bold
, FALSE
);
825 f(FONT_OEMBOLDUND
, OEM_CHARSET
, fw_bold
, TRUE
);
831 f(FONT_OEM
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
833 SelectObject (hdc
, fonts
[FONT_OEM
]);
834 GetTextMetrics(hdc
, &tm
);
835 font_height
= tm
.tmHeight
;
836 font_width
= tm
.tmAveCharWidth
;
838 f(FONT_OEMUND
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
840 if (bold_mode
== BOLD_FONT
) {
841 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
842 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
847 descent
= tm
.tmAscent
+ 1;
848 if (descent
>= font_height
)
849 descent
= font_height
- 1;
850 firstchar
= tm
.tmFirstChar
;
852 for (i
=0; i
<8; i
++) {
854 if (SelectObject (hdc
, fonts
[i
]) &&
855 GetTextMetrics(hdc
, &tm
) )
856 fsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
862 ReleaseDC (hwnd
, hdc
);
864 /* ... This is wrong in OEM only mode */
865 if (fsize
[FONT_UNDERLINE
] != fsize
[FONT_NORMAL
] ||
866 (bold_mode
== BOLD_FONT
&&
867 fsize
[FONT_BOLDUND
] != fsize
[FONT_BOLD
])) {
869 DeleteObject (fonts
[FONT_UNDERLINE
]);
870 if (bold_mode
== BOLD_FONT
)
871 DeleteObject (fonts
[FONT_BOLDUND
]);
874 if (bold_mode
== BOLD_FONT
&&
875 fsize
[FONT_BOLD
] != fsize
[FONT_NORMAL
]) {
876 bold_mode
= BOLD_SHADOW
;
877 DeleteObject (fonts
[FONT_BOLD
]);
878 if (und_mode
== UND_FONT
)
879 DeleteObject (fonts
[FONT_BOLDUND
]);
883 /* With the fascist font painting it doesn't matter if the linedraw font
884 * isn't exactly the right size anymore so we don't have to check this.
886 if (cfg
.vtmode
== VT_OEMANSI
&& fsize
[FONT_OEM
] != fsize
[FONT_NORMAL
] ) {
887 if( cfg
.fontcharset
== OEM_CHARSET
)
889 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
890 "different sizes. Using OEM-only mode instead",
891 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
892 cfg
.vtmode
= VT_OEMONLY
;
894 else if( firstchar
< ' ' )
896 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
897 "different sizes. Using XTerm mode instead",
898 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
899 cfg
.vtmode
= VT_XWINDOWS
;
903 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
904 "different sizes. Using ISO8859-1 mode instead",
905 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
906 cfg
.vtmode
= VT_POORMAN
;
911 DeleteObject (fonts
[i
]);
917 void request_resize (int w
, int h
, int refont
) {
920 /* If the window is maximized supress resizing attempts */
921 if(IsZoomed(hwnd
)) return;
924 /* Don't do this in OEMANSI, you may get disable messages */
925 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132)
926 && cfg
.vtmode
!= VT_OEMANSI
)
928 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132))
931 /* If font width too big for screen should we shrink the font more ? */
933 font_width
= ((font_width
*cols
+w
/2)/w
);
940 DeleteObject(fonts
[i
]);
942 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
944 init_fonts(font_width
);
948 static int first_time
= 1;
954 /* Get the size of the screen */
955 if (GetClientRect(GetDesktopWindow(),&ss
))
956 /* first_time = 0 */;
957 else { first_time
= 2; break; }
959 /* Make sure the values are sane */
960 width
= (ss
.right
-ss
.left
-extra_width
) / font_width
;
961 height
= (ss
.bottom
-ss
.top
-extra_height
) / font_height
;
963 if (w
>width
) w
=width
;
964 if (h
>height
) h
=height
;
970 width
= extra_width
+ font_width
* w
;
971 height
= extra_height
+ font_height
* h
;
973 SetWindowPos (hwnd
, NULL
, 0, 0, width
, height
,
974 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
975 SWP_NOMOVE
| SWP_NOZORDER
);
978 static void click (Mouse_Button b
, int x
, int y
) {
979 int thistime
= GetMessageTime();
981 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
982 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
983 lastact
== MA_2CLK ? MA_3CLK
:
984 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
989 if (lastact
!= MA_NOTHING
)
990 term_mouse (b
, lastact
, x
, y
);
994 static LRESULT CALLBACK
WndProc (HWND hwnd
, UINT message
,
995 WPARAM wParam
, LPARAM lParam
) {
997 static int ignore_size
= FALSE
;
998 static int ignore_clip
= FALSE
;
999 static int just_reconfigged
= FALSE
;
1000 static int resizing
= FALSE
;
1004 if (pending_netevent
)
1005 enact_pending_netevent();
1015 if (!cfg
.warn_on_close
|| session_closed
||
1016 MessageBox(hwnd
, "Are you sure you want to close this session?",
1017 "PuTTY Exit Confirmation",
1018 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1019 DestroyWindow(hwnd
);
1022 PostQuitMessage (0);
1025 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1037 PROCESS_INFORMATION pi
;
1038 HANDLE filemap
= NULL
;
1040 if (wParam
== IDM_DUPSESS
) {
1042 * Allocate a file-mapping memory chunk for the
1045 SECURITY_ATTRIBUTES sa
;
1048 sa
.nLength
= sizeof(sa
);
1049 sa
.lpSecurityDescriptor
= NULL
;
1050 sa
.bInheritHandle
= TRUE
;
1051 filemap
= CreateFileMapping((HANDLE
)0xFFFFFFFF,
1058 p
= (Config
*)MapViewOfFile(filemap
,
1060 0, 0, sizeof(Config
));
1062 *p
= cfg
; /* structure copy */
1066 sprintf(c
, "putty &%p", filemap
);
1068 } else if (wParam
== IDM_SAVEDSESS
) {
1069 char *session
= sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1070 cl
= malloc(16 + strlen(session
)); /* 8, but play safe */
1072 cl
= NULL
; /* not a very important failure mode */
1074 sprintf(cl
, "putty @%s", session
);
1080 GetModuleFileName (NULL
, b
, sizeof(b
)-1);
1082 si
.lpReserved
= NULL
;
1083 si
.lpDesktop
= NULL
;
1087 si
.lpReserved2
= NULL
;
1088 CreateProcess (b
, cl
, NULL
, NULL
, TRUE
,
1089 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1092 CloseHandle(filemap
);
1098 if (!do_reconfig(hwnd
))
1100 just_reconfigged
= TRUE
;
1105 DeleteObject(fonts
[i
]);
1107 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1108 und_mode
= UND_FONT
;
1111 /* Telnet will change local echo -> remote if the remote asks */
1112 if (cfg
.protocol
!= PROT_TELNET
)
1113 ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
1121 /* Enable or disable the scroll bar, etc */
1123 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1126 if (cfg
.scrollbar
) nflg
|= WS_VSCROLL
;
1127 else nflg
&= ~WS_VSCROLL
;
1129 nflg
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1131 nflg
|= (WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1137 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1138 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1139 SetWindowPos(hwnd
, NULL
, 0,0,0,0,
1140 SWP_NOACTIVATE
|SWP_NOCOPYBITS
|
1141 SWP_NOMOVE
|SWP_NOSIZE
| SWP_NOZORDER
|
1144 GetWindowRect (hwnd
, &wr
);
1145 GetClientRect (hwnd
, &cr
);
1146 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1147 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1151 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1152 InvalidateRect(hwnd
, NULL
, TRUE
);
1153 SetWindowPos (hwnd
, NULL
, 0, 0,
1154 extra_width
+ font_width
* cfg
.width
,
1155 extra_height
+ font_height
* cfg
.height
,
1156 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1157 SWP_NOMOVE
| SWP_NOZORDER
);
1158 if (IsIconic(hwnd
)) {
1159 SetWindowText (hwnd
,
1160 cfg
.win_name_always ? window_name
: icon_name
);
1169 case IDM_TEL_AYT
: back
->special (TS_AYT
); break;
1170 case IDM_TEL_BRK
: back
->special (TS_BRK
); break;
1171 case IDM_TEL_SYNCH
: back
->special (TS_SYNCH
); break;
1172 case IDM_TEL_EC
: back
->special (TS_EC
); break;
1173 case IDM_TEL_EL
: back
->special (TS_EL
); break;
1174 case IDM_TEL_GA
: back
->special (TS_GA
); break;
1175 case IDM_TEL_NOP
: back
->special (TS_NOP
); break;
1176 case IDM_TEL_ABORT
: back
->special (TS_ABORT
); break;
1177 case IDM_TEL_AO
: back
->special (TS_AO
); break;
1178 case IDM_TEL_IP
: back
->special (TS_IP
); break;
1179 case IDM_TEL_SUSP
: back
->special (TS_SUSP
); break;
1180 case IDM_TEL_EOR
: back
->special (TS_EOR
); break;
1181 case IDM_TEL_EOF
: back
->special (TS_EOF
); break;
1186 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1187 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1192 #define X_POS(l) ((int)(short)LOWORD(l))
1193 #define Y_POS(l) ((int)(short)HIWORD(l))
1195 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1196 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1198 case WM_LBUTTONDOWN
:
1199 click (MB_SELECT
, TO_CHR_X(X_POS(lParam
)),
1200 TO_CHR_Y(Y_POS(lParam
)));
1204 term_mouse (MB_SELECT
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1205 TO_CHR_Y(Y_POS(lParam
)));
1208 case WM_MBUTTONDOWN
:
1210 click (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1211 TO_CHR_X(X_POS(lParam
)),
1212 TO_CHR_Y(Y_POS(lParam
)));
1215 term_mouse (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1216 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1217 TO_CHR_Y(Y_POS(lParam
)));
1220 case WM_RBUTTONDOWN
:
1222 click (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1223 TO_CHR_X(X_POS(lParam
)),
1224 TO_CHR_Y(Y_POS(lParam
)));
1227 term_mouse (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1228 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1229 TO_CHR_Y(Y_POS(lParam
)));
1234 * Add the mouse position and message time to the random
1235 * number noise, if we're using ssh.
1237 if (cfg
.protocol
== PROT_SSH
)
1238 noise_ultralight(lParam
);
1240 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1242 if (wParam
& MK_LBUTTON
)
1244 else if (wParam
& MK_MBUTTON
)
1245 b
= cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
;
1247 b
= cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
;
1248 term_mouse (b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1249 TO_CHR_Y(Y_POS(lParam
)));
1252 case WM_IGNORE_CLIP
:
1253 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1255 case WM_DESTROYCLIPBOARD
:
1258 ignore_clip
= FALSE
;
1264 hdc
= BeginPaint (hwnd
, &p
);
1266 SelectPalette (hdc
, pal
, TRUE
);
1267 RealizePalette (hdc
);
1269 term_paint (hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1270 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1271 SelectObject (hdc
, GetStockObject(SYSTEM_FONT
));
1272 SelectObject (hdc
, GetStockObject(WHITE_PEN
));
1273 EndPaint (hwnd
, &p
);
1278 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1279 * but the only one that's likely to try to overload us is FD_READ.
1280 * This means buffering just one is fine.
1282 if (pending_netevent
)
1283 enact_pending_netevent();
1285 pending_netevent
= TRUE
;
1286 pend_netevent_wParam
=wParam
;
1287 pend_netevent_lParam
=lParam
;
1291 CreateCaret(hwnd
, caretbm
, 0, 0);
1302 case WM_IGNORE_SIZE
:
1303 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1305 case WM_ENTERSIZEMOVE
:
1309 case WM_EXITSIZEMOVE
:
1316 int width
, height
, w
, h
, ew
, eh
;
1317 LPRECT r
= (LPRECT
)lParam
;
1319 width
= r
->right
- r
->left
- extra_width
;
1320 height
= r
->bottom
- r
->top
- extra_height
;
1321 w
= (width
+ font_width
/2) / font_width
; if (w
< 1) w
= 1;
1322 h
= (height
+ font_height
/2) / font_height
; if (h
< 1) h
= 1;
1323 UpdateSizeTip(hwnd
, w
, h
);
1324 ew
= width
- w
* font_width
;
1325 eh
= height
- h
* font_height
;
1327 if (wParam
== WMSZ_LEFT
||
1328 wParam
== WMSZ_BOTTOMLEFT
||
1329 wParam
== WMSZ_TOPLEFT
)
1335 if (wParam
== WMSZ_TOP
||
1336 wParam
== WMSZ_TOPRIGHT
||
1337 wParam
== WMSZ_TOPLEFT
)
1347 /* break; (never reached) */
1349 if (wParam
== SIZE_MINIMIZED
) {
1350 SetWindowText (hwnd
,
1351 cfg
.win_name_always ? window_name
: icon_name
);
1354 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1355 SetWindowText (hwnd
, window_name
);
1357 int width
, height
, w
, h
;
1358 #if 0 /* we have fixed this using WM_SIZING now */
1362 width
= LOWORD(lParam
);
1363 height
= HIWORD(lParam
);
1364 w
= width
/ font_width
; if (w
< 1) w
= 1;
1365 h
= height
/ font_height
; if (h
< 1) h
= 1;
1366 #if 0 /* we have fixed this using WM_SIZING now */
1367 ew
= width
- w
* font_width
;
1368 eh
= height
- h
* font_height
;
1369 if (ew
!= 0 || eh
!= 0) {
1371 GetWindowRect (hwnd
, &r
);
1372 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1373 SetWindowPos (hwnd
, NULL
, 0, 0,
1374 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1375 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1378 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1380 term_size (h
, w
, cfg
.savelines
);
1382 * Don't call back->size in mid-resize. (To prevent
1383 * massive numbers of resize events getting sent
1384 * down the connection during an NT opaque drag.)
1388 just_reconfigged
= FALSE
;
1391 ignore_size
= FALSE
;
1394 switch (LOWORD(wParam
)) {
1395 case SB_BOTTOM
: term_scroll(-1, 0); break;
1396 case SB_TOP
: term_scroll(+1, 0); break;
1397 case SB_LINEDOWN
: term_scroll (0, +1); break;
1398 case SB_LINEUP
: term_scroll (0, -1); break;
1399 case SB_PAGEDOWN
: term_scroll (0, +rows
/2); break;
1400 case SB_PAGEUP
: term_scroll (0, -rows
/2); break;
1401 case SB_THUMBPOSITION
: case SB_THUMBTRACK
:
1402 term_scroll (1, HIWORD(wParam
)); break;
1405 case WM_PALETTECHANGED
:
1406 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1407 HDC hdc
= get_ctx();
1409 if (RealizePalette (hdc
) > 0)
1415 case WM_QUERYNEWPALETTE
:
1417 HDC hdc
= get_ctx();
1419 if (RealizePalette (hdc
) > 0)
1431 * Add the scan code and keypress timing to the random
1432 * number noise, if we're using ssh.
1434 if (cfg
.protocol
== PROT_SSH
)
1435 noise_ultralight(lParam
);
1438 * We don't do TranslateMessage since it disassociates the
1439 * resulting CHAR message from the KEYDOWN that sparked it,
1440 * which we occasionally don't want. Instead, we process
1441 * KEYDOWN, and call the Win32 translator functions so that
1442 * we get the translations under _our_ control.
1445 unsigned char buf
[20];
1448 len
= TranslateKey (message
, wParam
, lParam
, buf
);
1450 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1451 ldisc
->send (buf
, len
);
1457 * Nevertheless, we are prepared to deal with WM_CHAR
1458 * messages, should they crop up. So if someone wants to
1459 * post the things to us as part of a macro manoeuvre,
1460 * we're ready to cope.
1463 char c
= xlat_kbd2tty((unsigned char)wParam
);
1464 ldisc
->send (&c
, 1);
1469 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1473 * Move the system caret. (We maintain one, even though it's
1474 * invisible, for the benefit of blind people: apparently some
1475 * helper software tracks the system caret, so we should arrange to
1478 void sys_cursor(int x
, int y
) {
1479 SetCaretPos(x
* font_width
, y
* font_height
);
1483 * Draw a line of text in the window, at given character
1484 * coordinates, in given attributes.
1486 * We are allowed to fiddle with the contents of `text'.
1488 void do_text (Context ctx
, int x
, int y
, char *text
, int len
,
1489 unsigned long attr
, int lattr
) {
1491 int nfg
, nbg
, nfont
;
1494 int force_manual_underline
= 0;
1495 int fnt_width
= font_width
*(1+(lattr
!=LATTR_NORM
));
1496 static int *IpDx
= 0, IpDxLEN
= 0;;
1498 if (len
>IpDxLEN
|| IpDx
[0] != fnt_width
) {
1502 IpDx
= smalloc((len
+16)*sizeof(int));
1505 for(i
=0; i
<IpDxLEN
; i
++)
1506 IpDx
[i
] = fnt_width
;
1512 if (attr
& ATTR_ACTCURS
) {
1513 attr
&= (bold_mode
== BOLD_COLOURS ?
0x300200 : 0x300300);
1514 attr
^= ATTR_CUR_XOR
;
1518 if (cfg
.vtmode
== VT_OEMONLY
)
1522 * Map high-half characters in order to approximate ISO using
1523 * OEM character set. No characters are missing if the OEM codepage
1526 if (nfont
& FONT_OEM
) {
1528 for (i
=0; i
<len
; i
++)
1529 if (text
[i
] >= '\xA0' && text
[i
] <= '\xFF') {
1531 /* This is CP850 ... perfect translation */
1532 static const char oemhighhalf
[] =
1533 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1534 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1535 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1536 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1537 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1538 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1539 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1540 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1541 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1542 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1543 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1544 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1547 /* This is CP437 ... junk translation */
1548 static const unsigned char oemhighhalf
[] = {
1549 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1550 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1551 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1552 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1553 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1554 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1555 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1556 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1557 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1558 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1559 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1560 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1563 text
[i
] = oemhighhalf
[(unsigned char)text
[i
] - 0xA0];
1567 if (attr
& ATTR_GBCHR
) {
1570 * GB mapping: map # to pound, and everything else stays
1573 for (i
=0; i
<len
; i
++)
1575 text
[i
] = cfg
.vtmode
== VT_OEMONLY ?
'\x9C' : '\xA3';
1576 } else if (attr
& ATTR_LINEDRW
) {
1579 static const char poorman
[] =
1580 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1583 static const char oemmap_437
[] =
1584 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1585 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1588 static const char oemmap_850
[] =
1589 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1590 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1592 /* Poor windows font ... eg: windows courier */
1593 static const char oemmap
[] =
1594 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1595 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1598 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1599 * VT100 line drawing chars; everything else stays normal.
1601 switch (cfg
.vtmode
) {
1603 for (i
=0; i
<len
; i
++)
1604 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1605 text
[i
] += '\x01' - '\x60';
1608 /* Make sure we actually have an OEM font */
1609 if (fonts
[nfont
|FONT_OEM
]) {
1612 for (i
=0; i
<len
; i
++)
1613 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1614 text
[i
] = oemmap
[(unsigned char)text
[i
] - 0x60];
1618 for (i
=0; i
<len
; i
++)
1619 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1620 text
[i
] = poorman
[(unsigned char)text
[i
] - 0x60];
1625 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
1626 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
1627 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
1629 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
1630 nfont
|= FONT_UNDERLINE
;
1633 if (nfont
&FONT_UNDERLINE
)
1634 force_manual_underline
= 1;
1635 /* Don't do the same for manual bold, it could be bad news. */
1637 nfont
&= ~(FONT_BOLD
|FONT_UNDERLINE
);
1639 if (font_needs_hand_underlining
&& (attr
& ATTR_UNDER
))
1640 force_manual_underline
= 1;
1641 if (attr
& ATTR_REVERSE
) {
1642 t
= nfg
; nfg
= nbg
; nbg
= t
;
1644 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
1646 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
1650 SelectObject (hdc
, fonts
[nfont
]);
1651 SetTextColor (hdc
, fg
);
1652 SetBkColor (hdc
, bg
);
1653 SetBkMode (hdc
, OPAQUE
);
1656 line_box
.right
= x
+fnt_width
*len
;
1657 line_box
.bottom
= y
+font_height
;
1658 ExtTextOut (hdc
, x
, y
, ETO_CLIPPED
|ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
1659 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
1660 SetBkMode (hdc
, TRANSPARENT
);
1662 /* GRR: This draws the character outside it's box and can leave
1663 * 'droppings' even with the clip box! I suppose I could loop it
1664 * one character at a time ... yuk.
1666 * Or ... I could do a test print with "W", and use +1 or -1 for this
1667 * shift depending on if the leftmost column is blank...
1669 ExtTextOut (hdc
, x
-1, y
, ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
1671 if (force_manual_underline
||
1672 (und_mode
== UND_LINE
&& (attr
& ATTR_UNDER
))) {
1674 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, fg
));
1675 MoveToEx (hdc
, x
, y
+descent
, NULL
);
1676 LineTo (hdc
, x
+len
*fnt_width
, y
+descent
);
1677 oldpen
= SelectObject (hdc
, oldpen
);
1678 DeleteObject (oldpen
);
1680 if (attr
& ATTR_PASCURS
) {
1683 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
1684 pts
[2].x
= pts
[3].x
= x
+fnt_width
-1;
1685 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
1686 pts
[1].y
= pts
[2].y
= y
+font_height
-1;
1687 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
1688 Polyline (hdc
, pts
, 5);
1689 oldpen
= SelectObject (hdc
, oldpen
);
1690 DeleteObject (oldpen
);
1694 static int check_compose(int first
, int second
) {
1696 static char * composetbl
[] = {
1697 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1698 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1699 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1700 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1701 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1702 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1703 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1704 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1705 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1706 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1707 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1708 "\"uü", "'yý", "htþ", "\"yÿ",
1712 static int recurse
= 0;
1719 sprintf(buf
, "cc(%d,%d)", first
, second
);
1724 for(c
=composetbl
; *c
; c
++) {
1725 if( (*c
)[0] == first
&& (*c
)[1] == second
)
1727 return (*c
)[2] & 0xFF;
1734 nc
= check_compose(second
, first
);
1736 nc
= check_compose(toupper(first
), toupper(second
));
1738 nc
= check_compose(toupper(second
), toupper(first
));
1746 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1747 * codes. Returns number of bytes used or zero to drop the message
1748 * or -1 to forward the message to windows.
1750 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
, unsigned char *output
) {
1752 int scan
, left_alt
= 0, key_down
, shift_state
;
1754 unsigned char * p
= output
;
1756 static WORD keys
[3];
1757 static int compose_state
= 0;
1758 static int compose_char
= 0;
1759 static WPARAM compose_key
= 0;
1761 r
= GetKeyboardState(keystate
);
1762 if (!r
) memset(keystate
, 0, sizeof(keystate
));
1765 /* Note if AltGr was pressed and if it was used as a compose key */
1766 if (wParam
== VK_MENU
&& (HIWORD(lParam
)&KF_EXTENDED
))
1768 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
1769 if (!compose_state
) compose_key
= wParam
;
1771 if (wParam
== VK_APPS
&& !compose_state
)
1772 compose_key
= wParam
;
1774 if (wParam
== compose_key
)
1776 if (compose_state
== 0 && (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
1778 else if (compose_state
== 1 && (HIWORD(lParam
)&KF_UP
))
1783 else if (compose_state
==1 && wParam
!= VK_CONTROL
)
1786 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1787 if ( (cfg
.funky_type
== 0 || (cfg
.funky_type
== 1 && app_keypad_keys
))
1788 && wParam
==VK_NUMLOCK
&& !(keystate
[VK_SHIFT
]&0x80)) {
1790 wParam
= VK_EXECUTE
;
1792 /* UnToggle NUMLock */
1793 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
1794 keystate
[VK_NUMLOCK
] ^= 1;
1797 /* And write back the 'adjusted' state */
1798 SetKeyboardState (keystate
);
1801 /* Disable Auto repeat if required */
1802 if (repeat_off
&& (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
)
1805 if ((HIWORD(lParam
)&KF_ALTDOWN
) && (keystate
[VK_RMENU
]&0x80) == 0)
1808 key_down
= ((HIWORD(lParam
)&KF_UP
)==0);
1810 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1811 if (left_alt
&& (keystate
[VK_CONTROL
]&0x80))
1812 keystate
[VK_MENU
] = 0;
1814 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
1815 shift_state
= ((keystate
[VK_SHIFT
]&0x80)!=0)
1816 + ((keystate
[VK_CONTROL
]&0x80)!=0)*2;
1819 * Record that we pressed key so the scroll window can be reset, but
1820 * be careful to avoid Shift-UP/Down
1822 if( wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
1826 /* Make sure we're not pasting */
1827 if (key_down
) term_nopaste();
1829 if (compose_state
>1 && left_alt
) compose_state
= 0;
1831 /* Sanitize the number pad if not using a PC NumPad */
1832 if( left_alt
|| (app_keypad_keys
&& cfg
.funky_type
!= 2)
1833 || cfg
.nethack_keypad
|| compose_state
)
1835 if ((HIWORD(lParam
)&KF_EXTENDED
) == 0)
1840 case VK_INSERT
: nParam
= VK_NUMPAD0
; break;
1841 case VK_END
: nParam
= VK_NUMPAD1
; break;
1842 case VK_DOWN
: nParam
= VK_NUMPAD2
; break;
1843 case VK_NEXT
: nParam
= VK_NUMPAD3
; break;
1844 case VK_LEFT
: nParam
= VK_NUMPAD4
; break;
1845 case VK_CLEAR
: nParam
= VK_NUMPAD5
; break;
1846 case VK_RIGHT
: nParam
= VK_NUMPAD6
; break;
1847 case VK_HOME
: nParam
= VK_NUMPAD7
; break;
1848 case VK_UP
: nParam
= VK_NUMPAD8
; break;
1849 case VK_PRIOR
: nParam
= VK_NUMPAD9
; break;
1850 case VK_DELETE
: nParam
= VK_DECIMAL
; break;
1854 if (keystate
[VK_NUMLOCK
]&1) shift_state
|= 1;
1860 /* If a key is pressed and AltGr is not active */
1861 if (key_down
&& (keystate
[VK_RMENU
]&0x80) == 0 && !compose_state
)
1863 /* Okay, prepare for most alts then ...*/
1864 if (left_alt
) *p
++ = '\033';
1866 /* Lets see if it's a pattern we know all about ... */
1867 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
1868 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
1871 if (wParam
== VK_NEXT
&& shift_state
== 1) {
1872 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
1875 if (wParam
== VK_INSERT
&& shift_state
== 1) {
1876 term_mouse (MB_PASTE
, MA_CLICK
, 0, 0);
1877 term_mouse (MB_PASTE
, MA_RELEASE
, 0, 0);
1880 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
1883 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
1885 SendMessage (hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
1889 /* Nethack keypad */
1890 if (cfg
.nethack_keypad
&& !left_alt
) {
1892 case VK_NUMPAD1
: *p
++ = shift_state ?
'B': 'b'; return p
-output
;
1893 case VK_NUMPAD2
: *p
++ = shift_state ?
'J': 'j'; return p
-output
;
1894 case VK_NUMPAD3
: *p
++ = shift_state ?
'N': 'n'; return p
-output
;
1895 case VK_NUMPAD4
: *p
++ = shift_state ?
'H': 'h'; return p
-output
;
1896 case VK_NUMPAD5
: *p
++ = shift_state ?
'.': '.'; return p
-output
;
1897 case VK_NUMPAD6
: *p
++ = shift_state ?
'L': 'l'; return p
-output
;
1898 case VK_NUMPAD7
: *p
++ = shift_state ?
'Y': 'y'; return p
-output
;
1899 case VK_NUMPAD8
: *p
++ = shift_state ?
'K': 'k'; return p
-output
;
1900 case VK_NUMPAD9
: *p
++ = shift_state ?
'U': 'u'; return p
-output
;
1904 /* Application Keypad */
1908 if ( cfg
.funky_type
== 0 ||
1909 ( cfg
.funky_type
== 1 && app_keypad_keys
)) switch(wParam
) {
1910 case VK_EXECUTE
: if (app_keypad_keys
) xkey
= 'P'; break;
1911 case VK_DIVIDE
: xkey
= 'Q'; break;
1912 case VK_MULTIPLY
:xkey
= 'R'; break;
1913 case VK_SUBTRACT
:xkey
= 'S'; break;
1915 if(app_keypad_keys
) switch(wParam
) {
1916 case VK_NUMPAD0
: xkey
= 'p'; break;
1917 case VK_NUMPAD1
: xkey
= 'q'; break;
1918 case VK_NUMPAD2
: xkey
= 'r'; break;
1919 case VK_NUMPAD3
: xkey
= 's'; break;
1920 case VK_NUMPAD4
: xkey
= 't'; break;
1921 case VK_NUMPAD5
: xkey
= 'u'; break;
1922 case VK_NUMPAD6
: xkey
= 'v'; break;
1923 case VK_NUMPAD7
: xkey
= 'w'; break;
1924 case VK_NUMPAD8
: xkey
= 'x'; break;
1925 case VK_NUMPAD9
: xkey
= 'y'; break;
1927 case VK_DECIMAL
: xkey
= 'n'; break;
1928 case VK_ADD
: if(shift_state
) xkey
= 'm';
1932 if (HIWORD(lParam
)&KF_EXTENDED
)
1940 if (xkey
>='P' && xkey
<='S')
1941 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
1943 p
+= sprintf((char *)p
, "\x1B?%c", xkey
);
1946 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
1951 if (wParam
== VK_BACK
&& shift_state
== 0 ) /* Backspace */
1953 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
1956 if (wParam
== VK_TAB
&& shift_state
== 1 ) /* Shift tab */
1958 *p
++ = 0x1B; *p
++ = '['; *p
++ = 'Z'; return p
- output
;
1960 if (wParam
== VK_SPACE
&& shift_state
== 2 ) /* Ctrl-Space */
1962 *p
++ = 0; return p
- output
;
1964 if (wParam
== VK_SPACE
&& shift_state
== 3 ) /* Ctrl-Shift-Space */
1966 *p
++ = 160; return p
- output
;
1968 if (wParam
== VK_CANCEL
&& shift_state
== 2 ) /* Ctrl-Break */
1970 *p
++ = 3; return p
- output
;
1972 /* Control-2 to Control-8 are special */
1973 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8')
1975 *p
++ = "\000\033\034\035\036\037\177"[wParam
-'2'];
1978 if (shift_state
== 2 && wParam
== 0xBD) {
1982 if (shift_state
== 2 && wParam
== 0xDF) {
1986 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
1987 *p
++ = '\r'; *p
++ = '\n';
1992 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1993 * for integer decimal nn.)
1995 * We also deal with the weird ones here. Linux VCs replace F1
1996 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1997 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2002 case VK_F1
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11); break;
2003 case VK_F2
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12); break;
2004 case VK_F3
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13); break;
2005 case VK_F4
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14); break;
2006 case VK_F5
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15); break;
2007 case VK_F6
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17); break;
2008 case VK_F7
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18); break;
2009 case VK_F8
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19); break;
2010 case VK_F9
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20); break;
2011 case VK_F10
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21); break;
2012 case VK_F11
: code
= 23; break;
2013 case VK_F12
: code
= 24; break;
2014 case VK_F13
: code
= 25; break;
2015 case VK_F14
: code
= 26; break;
2016 case VK_F15
: code
= 28; break;
2017 case VK_F16
: code
= 29; break;
2018 case VK_F17
: code
= 31; break;
2019 case VK_F18
: code
= 32; break;
2020 case VK_F19
: code
= 33; break;
2021 case VK_F20
: code
= 34; break;
2022 case VK_HOME
: code
= 1; break;
2023 case VK_INSERT
: code
= 2; break;
2024 case VK_DELETE
: code
= 3; break;
2025 case VK_END
: code
= 4; break;
2026 case VK_PRIOR
: code
= 5; break;
2027 case VK_NEXT
: code
= 6; break;
2029 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
2030 p
+= sprintf((char *)p
, "\x1B[[%c", code
+ 'A' - 11);
2033 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
2034 p
+= sprintf((char *)p
, "\x1BO%c", code
+ 'P' - 11);
2037 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
2038 p
+= sprintf((char *)p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
2042 p
+= sprintf((char *)p
, "\x1B[%d~", code
);
2047 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2048 * some reason seems to send VK_CLEAR to Windows...).
2053 case VK_UP
: xkey
= 'A'; break;
2054 case VK_DOWN
: xkey
= 'B'; break;
2055 case VK_RIGHT
: xkey
= 'C'; break;
2056 case VK_LEFT
: xkey
= 'D'; break;
2057 case VK_CLEAR
: xkey
= 'G'; break;
2062 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
2063 else if (app_cursor_keys
)
2064 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
2066 p
+= sprintf((char *)p
, "\x1B[%c", xkey
);
2072 /* Okay we've done everything interesting; let windows deal with
2073 * the boring stuff */
2075 BOOL capsOn
=keystate
[VK_CAPITAL
] !=0;
2077 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2078 if(cfg
.xlat_capslockcyr
)
2079 keystate
[VK_CAPITAL
] = 0;
2081 r
= ToAscii (wParam
, scan
, keystate
, keys
, 0);
2087 unsigned char ch
= (unsigned char)keys
[i
];
2089 if (compose_state
==2 && (ch
&0x80) == 0 && ch
>' ') {
2094 if (compose_state
==3 && (ch
&0x80) == 0 && ch
>' ') {
2098 if ((nc
=check_compose(compose_char
,ch
)) == -1)
2103 *p
++ = xlat_kbd2tty((unsigned char)nc
);
2109 if( left_alt
&& key_down
) *p
++ = '\033';
2115 ch
= xlat_latkbd2win(ch
);
2116 *p
++ = xlat_kbd2tty(ch
);
2120 /* This is so the ALT-Numpad and dead keys work correctly. */
2127 /* This stops ALT press-release doing a 'COMMAND MENU' function */
2128 if (message
== WM_SYSKEYUP
&& wParam
== VK_MENU
)
2130 keystate
[VK_MENU
] = 0;
2137 void set_title (char *title
) {
2138 sfree (window_name
);
2139 window_name
= smalloc(1+strlen(title
));
2140 strcpy (window_name
, title
);
2141 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
2142 SetWindowText (hwnd
, title
);
2145 void set_icon (char *title
) {
2147 icon_name
= smalloc(1+strlen(title
));
2148 strcpy (icon_name
, title
);
2149 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
2150 SetWindowText (hwnd
, title
);
2153 void set_sbar (int total
, int start
, int page
) {
2156 if (!cfg
.scrollbar
) return;
2158 si
.cbSize
= sizeof(si
);
2159 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
2161 si
.nMax
= total
- 1;
2165 SetScrollInfo (hwnd
, SB_VERT
, &si
, TRUE
);
2168 Context
get_ctx(void) {
2173 SelectPalette (hdc
, pal
, FALSE
);
2179 void free_ctx (Context ctx
) {
2180 SelectPalette (ctx
, GetStockObject (DEFAULT_PALETTE
), FALSE
);
2181 ReleaseDC (hwnd
, ctx
);
2184 static void real_palette_set (int n
, int r
, int g
, int b
) {
2186 logpal
->palPalEntry
[n
].peRed
= r
;
2187 logpal
->palPalEntry
[n
].peGreen
= g
;
2188 logpal
->palPalEntry
[n
].peBlue
= b
;
2189 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
2190 colours
[n
] = PALETTERGB(r
, g
, b
);
2191 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2193 colours
[n
] = RGB(r
, g
, b
);
2196 void palette_set (int n
, int r
, int g
, int b
) {
2197 static const int first
[21] = {
2198 0, 2, 4, 6, 8, 10, 12, 14,
2199 1, 3, 5, 7, 9, 11, 13, 15,
2202 real_palette_set (first
[n
], r
, g
, b
);
2204 real_palette_set (first
[n
]+1, r
, g
, b
);
2206 HDC hdc
= get_ctx();
2207 UnrealizeObject (pal
);
2208 RealizePalette (hdc
);
2213 void palette_reset (void) {
2216 for (i
= 0; i
< NCOLOURS
; i
++) {
2218 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
2219 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
2220 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
2221 logpal
->palPalEntry
[i
].peFlags
= 0;
2222 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
2223 defpal
[i
].rgbtGreen
,
2224 defpal
[i
].rgbtBlue
);
2226 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
2227 defpal
[i
].rgbtGreen
,
2228 defpal
[i
].rgbtBlue
);
2233 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2235 RealizePalette (hdc
);
2240 void write_clip (void *data
, int len
) {
2244 clipdata
= GlobalAlloc (GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
2247 lock
= GlobalLock (clipdata
);
2250 memcpy (lock
, data
, len
);
2251 ((unsigned char *) lock
) [len
] = 0;
2252 GlobalUnlock (clipdata
);
2254 SendMessage (hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
2255 if (OpenClipboard (hwnd
)) {
2257 SetClipboardData (CF_TEXT
, clipdata
);
2260 GlobalFree (clipdata
);
2261 SendMessage (hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
2264 void get_clip (void **p
, int *len
) {
2265 static HGLOBAL clipdata
= NULL
;
2269 GlobalUnlock (clipdata
);
2273 if (OpenClipboard (NULL
)) {
2274 clipdata
= GetClipboardData (CF_TEXT
);
2277 *p
= GlobalLock (clipdata
);
2291 * Move `lines' lines from position `from' to position `to' in the
2294 void optimised_move (int to
, int from
, int lines
) {
2298 min
= (to
< from ? to
: from
);
2299 max
= to
+ from
- min
;
2301 r
.left
= 0; r
.right
= cols
* font_width
;
2302 r
.top
= min
* font_height
; r
.bottom
= (max
+lines
) * font_height
;
2303 ScrollWindow (hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
2307 * Print a message box and perform a fatal exit.
2309 void fatalbox(char *fmt
, ...) {
2314 vsprintf(stuff
, fmt
, ap
);
2316 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
2323 void beep(int errorbeep
) {
2324 static long last_beep
= 0;
2325 long now
, beep_diff
;
2327 now
= GetTickCount();
2328 beep_diff
= now
-last_beep
;
2330 /* Make sure we only respond to one beep per packet or so */
2331 if (beep_diff
>=0 && beep_diff
<50)
2335 MessageBeep(MB_ICONHAND
);
2339 last_beep
= GetTickCount();