15 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
21 #define IDM_SHOWLOG 0x0010
22 #define IDM_NEWSESS 0x0020
23 #define IDM_DUPSESS 0x0030
24 #define IDM_RECONF 0x0040
25 #define IDM_CLRSB 0x0050
26 #define IDM_RESET 0x0060
27 #define IDM_TEL_AYT 0x0070
28 #define IDM_TEL_BRK 0x0080
29 #define IDM_TEL_SYNCH 0x0090
30 #define IDM_TEL_EC 0x00a0
31 #define IDM_TEL_EL 0x00b0
32 #define IDM_TEL_GA 0x00c0
33 #define IDM_TEL_NOP 0x00d0
34 #define IDM_TEL_ABORT 0x00e0
35 #define IDM_TEL_AO 0x00f0
36 #define IDM_TEL_IP 0x0100
37 #define IDM_TEL_SUSP 0x0110
38 #define IDM_TEL_EOR 0x0120
39 #define IDM_TEL_EOF 0x0130
40 #define IDM_ABOUT 0x0140
41 #define IDM_SAVEDSESS 0x0150
43 #define IDM_SAVED_MIN 0x1000
44 #define IDM_SAVED_MAX 0x2000
46 #define WM_IGNORE_SIZE (WM_XUSER + 1)
47 #define WM_IGNORE_CLIP (WM_XUSER + 2)
49 /* Needed for Chinese support and apparently not always defined. */
51 #define VK_PROCESSKEY 0xE5
54 static LRESULT CALLBACK
WndProc (HWND
, UINT
, WPARAM
, LPARAM
);
55 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
, unsigned char *output
);
56 static void cfgtopalette(void);
57 static void init_palette(void);
58 static void init_fonts(int);
60 static int extra_width
, extra_height
;
62 static int pending_netevent
= 0;
63 static WPARAM pend_netevent_wParam
= 0;
64 static LPARAM pend_netevent_lParam
= 0;
65 static void enact_pending_netevent(void);
67 static time_t last_movement
= 0;
71 #define FONT_UNDERLINE 2
72 #define FONT_BOLDUND 3
74 #define FONT_OEMBOLD 5
75 #define FONT_OEMBOLDUND 6
77 static HFONT fonts
[8];
78 static int font_needs_hand_underlining
;
80 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
88 static COLORREF colours
[NCOLOURS
];
90 static LPLOGPALETTE logpal
;
91 static RGBTRIPLE defpal
[NCOLOURS
];
95 static HBITMAP caretbm
;
97 static int dbltime
, lasttime
, lastact
;
98 static Mouse_Button lastbtn
;
100 static char *window_name
, *icon_name
;
102 static Ldisc
*real_ldisc
;
104 void begin_session(void) {
108 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
) {
109 static char appname
[] = "PuTTY";
114 int guess_width
, guess_height
;
117 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
119 winsock_ver
= MAKEWORD(1, 1);
120 if (WSAStartup(winsock_ver
, &wsadata
)) {
121 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
122 MB_OK
| MB_ICONEXCLAMATION
);
125 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
126 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
127 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
131 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
133 InitCommonControls();
135 /* Ensure a Maximize setting in Explorer doesn't maximise the
140 * Process the command line.
145 default_protocol
= DEFAULT_PROTOCOL
;
146 default_port
= DEFAULT_PORT
;
148 do_defaults(NULL
, &cfg
);
151 while (*p
&& isspace(*p
)) p
++;
154 * Process command line options first. Yes, this can be
155 * done better, and it will be as soon as I have the
159 char *q
= p
+ strcspn(p
, " \t");
162 tolower(p
[0]) == 's' &&
163 tolower(p
[1]) == 's' &&
164 tolower(p
[2]) == 'h') {
165 default_protocol
= cfg
.protocol
= PROT_SSH
;
166 default_port
= cfg
.port
= 22;
167 } else if (q
== p
+ 3 &&
168 tolower(p
[0]) == 'l' &&
169 tolower(p
[1]) == 'o' &&
170 tolower(p
[2]) == 'g') {
171 logfile
= "putty.log";
172 } else if (q
== p
+ 7 &&
173 tolower(p
[0]) == 'c' &&
174 tolower(p
[1]) == 'l' &&
175 tolower(p
[2]) == 'e' &&
176 tolower(p
[3]) == 'a' &&
177 tolower(p
[4]) == 'n' &&
178 tolower(p
[5]) == 'u' &&
179 tolower(p
[6]) == 'p') {
181 * `putty -cleanup'. Remove all registry entries
182 * associated with PuTTY, and also find and delete
183 * the random seed file.
186 "This procedure will remove ALL Registry\n"
187 "entries associated with PuTTY, and will\n"
188 "also remove the PuTTY random seed file.\n"
190 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
191 "SESSIONS. Are you really sure you want\n"
194 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
199 p
= q
+ strspn(q
, " \t");
203 * An initial @ means to activate a saved session.
207 while (i
> 1 && isspace(p
[i
-1]))
210 do_defaults (p
+1, &cfg
);
211 if (!*cfg
.host
&& !do_config()) {
215 } else if (*p
== '&') {
217 * An initial & means we've been given a command line
218 * containing the hex value of a HANDLE for a file
219 * mapping object, which we must then extract as a
224 if (sscanf(p
+1, "%p", &filemap
) == 1 &&
225 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
226 0, 0, sizeof(Config
))) != NULL
) {
229 CloseHandle(filemap
);
230 } else if (!do_config()) {
237 * If the hostname starts with "telnet:", set the
238 * protocol to Telnet and process the string as a
241 if (!strncmp(q
, "telnet:", 7)) {
245 if (q
[0] == '/' && q
[1] == '/')
247 cfg
.protocol
= PROT_TELNET
;
249 while (*p
&& *p
!= ':' && *p
!= '/') p
++;
257 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
258 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
260 while (*p
&& !isspace(*p
)) p
++;
263 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
264 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
265 while (*p
&& isspace(*p
)) p
++;
278 /* See if host is of the form user@host */
279 if (cfg
.host
[0] != '\0') {
280 char *atsign
= strchr(cfg
.host
, '@');
281 /* Make sure we're not overflowing the user field */
283 if (atsign
-cfg
.host
< sizeof cfg
.username
) {
284 strncpy (cfg
.username
, cfg
.host
, atsign
-cfg
.host
);
285 cfg
.username
[atsign
-cfg
.host
] = '\0';
287 memmove(cfg
.host
, atsign
+1, 1+strlen(atsign
+1));
293 * Select protocol. This is farmed out into a table in a
294 * separate file to enable an ssh-free variant.
299 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
300 if (backends
[i
].protocol
== cfg
.protocol
) {
301 back
= backends
[i
].backend
;
305 MessageBox(NULL
, "Unsupported protocol number found",
306 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
312 real_ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
313 /* To start with, we use the simple line discipline, so we can
314 * type passwords etc without fear of them being echoed... */
315 ldisc
= &ldisc_simple
;
319 wndclass
.lpfnWndProc
= WndProc
;
320 wndclass
.cbClsExtra
= 0;
321 wndclass
.cbWndExtra
= 0;
322 wndclass
.hInstance
= inst
;
323 wndclass
.hIcon
= LoadIcon (inst
,
324 MAKEINTRESOURCE(IDI_MAINICON
));
325 wndclass
.hCursor
= LoadCursor (NULL
, IDC_IBEAM
);
326 wndclass
.hbrBackground
= GetStockObject (BLACK_BRUSH
);
327 wndclass
.lpszMenuName
= NULL
;
328 wndclass
.lpszClassName
= appname
;
330 RegisterClass (&wndclass
);
335 savelines
= cfg
.savelines
;
341 * Guess some defaults for the window size. This all gets
342 * updated later, so we don't really care too much. However, we
343 * do want the font width/height guesses to correspond to a
344 * large font rather than a small one...
351 term_size (cfg
.height
, cfg
.width
, cfg
.savelines
);
352 guess_width
= extra_width
+ font_width
* cols
;
353 guess_height
= extra_height
+ font_height
* rows
;
356 HWND w
= GetDesktopWindow();
357 GetWindowRect (w
, &r
);
358 if (guess_width
> r
.right
- r
.left
)
359 guess_width
= r
.right
- r
.left
;
360 if (guess_height
> r
.bottom
- r
.top
)
361 guess_height
= r
.bottom
- r
.top
;
365 int winmode
= WS_OVERLAPPEDWINDOW
|WS_VSCROLL
;
366 if (!cfg
.scrollbar
) winmode
&= ~(WS_VSCROLL
);
367 if (cfg
.locksize
) winmode
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
368 hwnd
= CreateWindow (appname
, appname
,
370 CW_USEDEFAULT
, CW_USEDEFAULT
,
371 guess_width
, guess_height
,
372 NULL
, NULL
, inst
, NULL
);
376 * Initialise the fonts, simultaneously correcting the guesses
377 * for font_{width,height}.
379 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
384 * Correct the guesses for extra_{width,height}.
388 GetWindowRect (hwnd
, &wr
);
389 GetClientRect (hwnd
, &cr
);
390 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
391 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
395 * Resize the window, now we know what size we _really_ want it
398 guess_width
= extra_width
+ font_width
* cols
;
399 guess_height
= extra_height
+ font_height
* rows
;
400 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
401 SetWindowPos (hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
402 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
405 * Set up a caret bitmap, with no content.
409 int size
= (font_width
+15)/16 * 2 * font_height
;
410 bits
= calloc(size
, 1);
411 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
416 * Initialise the scroll bar.
421 si
.cbSize
= sizeof(si
);
422 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
427 SetScrollInfo (hwnd
, SB_VERT
, &si
, FALSE
);
431 * Start up the telnet connection.
435 char msg
[1024], *title
;
438 error
= back
->init (hwnd
, cfg
.host
, cfg
.port
, &realhost
);
440 sprintf(msg
, "Unable to open connection:\n%s", error
);
441 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
444 window_name
= icon_name
= NULL
;
446 title
= cfg
.wintitle
;
448 sprintf(msg
, "%s - PuTTY", realhost
);
455 session_closed
= FALSE
;
458 * Set up the input and output buffers.
461 outbuf_reap
= outbuf_head
= 0;
464 * Prepare the mouse handler.
466 lastact
= MA_NOTHING
;
467 lastbtn
= MB_NOTHING
;
468 dbltime
= GetDoubleClickTime();
471 * Set up the session-control options on the system menu.
474 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
478 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
479 if (cfg
.protocol
== PROT_TELNET
) {
481 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
482 AppendMenu (p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
483 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
484 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
485 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
486 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
487 AppendMenu (p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
488 AppendMenu (p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
489 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
490 AppendMenu (p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
491 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
492 AppendMenu (p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
493 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
494 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
495 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
496 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
497 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
, "Telnet Command");
498 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
500 AppendMenu (m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
501 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
502 AppendMenu (m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session");
503 AppendMenu (m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
506 for (i
= 1 ; i
< ((nsessions
< 256) ? nsessions
: 256) ; i
++)
507 AppendMenu (s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
) , sessions
[i
]);
508 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
509 AppendMenu (m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings");
510 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
511 AppendMenu (m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
512 AppendMenu (m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
513 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
514 AppendMenu (m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
518 * Finally show the window!
520 ShowWindow (hwnd
, show
);
523 * Set the palette up.
529 has_focus
= (GetForegroundWindow() == hwnd
);
532 if (GetMessage (&msg
, NULL
, 0, 0) == 1)
534 int timer_id
= 0, long_timer
= 0;
536 while (msg
.message
!= WM_QUIT
) {
537 /* Sometimes DispatchMessage calls routines that use their own
538 * GetMessage loop, setup this timer so we get some control back.
540 * Also call term_update() from the timer so that if the host
541 * is sending data flat out we still do redraws.
543 if(timer_id
&& long_timer
) {
544 KillTimer(hwnd
, timer_id
);
545 long_timer
= timer_id
= 0;
548 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
549 DispatchMessage (&msg
);
551 /* Make sure we blink everything that needs it. */
554 /* Send the paste buffer if there's anything to send */
557 /* If there's nothing new in the queue then we can do everything
558 * we've delayed, reading the socket, writing, and repainting
561 if (PeekMessage (&msg
, NULL
, 0, 0, PM_REMOVE
))
564 if (pending_netevent
) {
565 enact_pending_netevent();
567 /* Force the cursor blink on */
570 if (PeekMessage (&msg
, NULL
, 0, 0, PM_REMOVE
))
574 /* Okay there is now nothing to do so we make sure the screen is
575 * completely up to date then tell windows to call us in a little
579 KillTimer(hwnd
, timer_id
);
588 timer_id
= SetTimer(hwnd
, 1, 59500, NULL
);
590 timer_id
= SetTimer(hwnd
, 1, 250, NULL
);
593 /* There's no point rescanning everything in the message queue
594 * so we do an apperently unneccesary wait here
597 if (GetMessage (&msg
, NULL
, 0, 0) != 1)
609 DeleteObject(fonts
[i
]);
616 if (cfg
.protocol
== PROT_SSH
) {
627 * Print a message box and close the connection.
629 void connection_fatal(char *fmt
, ...) {
634 vsprintf(stuff
, fmt
, ap
);
636 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
637 if (cfg
.close_on_exit
)
640 session_closed
= TRUE
;
641 SetWindowText (hwnd
, "PuTTY (inactive)");
646 * Actually do the job requested by a WM_NETEVENT
648 static void enact_pending_netevent(void) {
650 static int reentering
= 0;
653 return; /* don't unpend the pending */
655 pending_netevent
= FALSE
;
658 i
= back
->msg (pend_netevent_wParam
, pend_netevent_lParam
);
663 switch (WSABASEERR
+ (-i
) % 10000) {
665 sprintf(buf
, "Connection reset by peer");
667 case WSAECONNABORTED
:
668 sprintf(buf
, "Connection aborted");
671 sprintf(buf
, "Unexpected network error %d", -i
);
674 connection_fatal(buf
);
677 if (cfg
.close_on_exit
)
680 session_closed
= TRUE
;
681 MessageBox(hwnd
, "Connection closed by remote host",
682 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
683 SetWindowText (hwnd
, "PuTTY (inactive)");
689 * Copy the colour palette from the configuration data into defpal.
690 * This is non-trivial because the colour indices are different.
692 static void cfgtopalette(void) {
694 static const int ww
[] = {
695 6, 7, 8, 9, 10, 11, 12, 13,
696 14, 15, 16, 17, 18, 19, 20, 21,
697 0, 1, 2, 3, 4, 4, 5, 5
700 for (i
=0; i
<24; i
++) {
702 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
703 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
704 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
709 * Set up the colour palette.
711 static void init_palette(void) {
713 HDC hdc
= GetDC (hwnd
);
715 if (cfg
.try_palette
&&
716 GetDeviceCaps (hdc
, RASTERCAPS
) & RC_PALETTE
) {
717 logpal
= smalloc(sizeof(*logpal
)
718 - sizeof(logpal
->palPalEntry
)
719 + NCOLOURS
* sizeof(PALETTEENTRY
));
720 logpal
->palVersion
= 0x300;
721 logpal
->palNumEntries
= NCOLOURS
;
722 for (i
= 0; i
< NCOLOURS
; i
++) {
723 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
724 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
725 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
726 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
728 pal
= CreatePalette (logpal
);
730 SelectPalette (hdc
, pal
, FALSE
);
731 RealizePalette (hdc
);
732 SelectPalette (hdc
, GetStockObject (DEFAULT_PALETTE
),
736 ReleaseDC (hwnd
, hdc
);
739 for (i
=0; i
<NCOLOURS
; i
++)
740 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
744 for(i
=0; i
<NCOLOURS
; i
++)
745 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
751 * Initialise all the fonts we will need. There may be as many as
752 * eight or as few as one. We also:
754 * - check the font width and height, correcting our guesses if
757 * - verify that the bold font is the same width as the ordinary
758 * one, and engage shadow bolding if not.
760 * - verify that the underlined font is the same width as the
761 * ordinary one (manual underlining by means of line drawing can
762 * be done in a pinch).
764 static void init_fonts(int pick_width
) {
769 int fw_dontcare
, fw_bold
;
778 if (cfg
.fontisbold
) {
779 fw_dontcare
= FW_BOLD
;
782 fw_dontcare
= FW_DONTCARE
;
788 font_height
= cfg
.fontheight
;
789 font_width
= pick_width
;
792 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
793 c, OUT_DEFAULT_PRECIS, \
794 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
795 FIXED_PITCH | FF_DONTCARE, cfg.font)
797 if (cfg
.vtmode
!= VT_OEMONLY
) {
798 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
800 SelectObject (hdc
, fonts
[FONT_NORMAL
]);
801 GetTextMetrics(hdc
, &tm
);
802 font_height
= tm
.tmHeight
;
803 font_width
= tm
.tmAveCharWidth
;
805 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
808 * Some fonts, e.g. 9-pt Courier, draw their underlines
809 * outside their character cell. We successfully prevent
810 * screen corruption by clipping the text output, but then
811 * we lose the underline completely. Here we try to work
812 * out whether this is such a font, and if it is, we set a
813 * flag that causes underlines to be drawn by hand.
815 * Having tried other more sophisticated approaches (such
816 * as examining the TEXTMETRIC structure or requesting the
817 * height of a string), I think we'll do this the brute
818 * force way: we create a small bitmap, draw an underlined
819 * space on it, and test to see whether any pixels are
820 * foreground-coloured. (Since we expect the underline to
821 * go all the way across the character cell, we only search
822 * down a single column of the bitmap, half way across.)
826 HBITMAP und_bm
, und_oldbm
;
830 und_dc
= CreateCompatibleDC(hdc
);
831 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
832 und_oldbm
= SelectObject(und_dc
, und_bm
);
833 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
834 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
835 SetTextColor (und_dc
, RGB(255,255,255));
836 SetBkColor (und_dc
, RGB(0,0,0));
837 SetBkMode (und_dc
, OPAQUE
);
838 ExtTextOut (und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
840 for (i
= 0; i
< font_height
; i
++) {
841 c
= GetPixel(und_dc
, font_width
/2, i
);
845 SelectObject(und_dc
, und_oldbm
);
846 DeleteObject(und_bm
);
848 font_needs_hand_underlining
= !gotit
;
851 if (bold_mode
== BOLD_FONT
) {
852 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
853 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
856 if (cfg
.vtmode
== VT_OEMANSI
) {
857 f(FONT_OEM
, OEM_CHARSET
, fw_dontcare
, FALSE
);
858 f(FONT_OEMUND
, OEM_CHARSET
, fw_dontcare
, TRUE
);
860 if (bold_mode
== BOLD_FONT
) {
861 f(FONT_OEMBOLD
, OEM_CHARSET
, fw_bold
, FALSE
);
862 f(FONT_OEMBOLDUND
, OEM_CHARSET
, fw_bold
, TRUE
);
868 f(FONT_OEM
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
870 SelectObject (hdc
, fonts
[FONT_OEM
]);
871 GetTextMetrics(hdc
, &tm
);
872 font_height
= tm
.tmHeight
;
873 font_width
= tm
.tmAveCharWidth
;
875 f(FONT_OEMUND
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
877 if (bold_mode
== BOLD_FONT
) {
878 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
879 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
884 descent
= tm
.tmAscent
+ 1;
885 if (descent
>= font_height
)
886 descent
= font_height
- 1;
887 firstchar
= tm
.tmFirstChar
;
889 for (i
=0; i
<8; i
++) {
891 if (SelectObject (hdc
, fonts
[i
]) &&
892 GetTextMetrics(hdc
, &tm
) )
893 fsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
899 ReleaseDC (hwnd
, hdc
);
901 /* ... This is wrong in OEM only mode */
902 if (fsize
[FONT_UNDERLINE
] != fsize
[FONT_NORMAL
] ||
903 (bold_mode
== BOLD_FONT
&&
904 fsize
[FONT_BOLDUND
] != fsize
[FONT_BOLD
])) {
906 DeleteObject (fonts
[FONT_UNDERLINE
]);
907 if (bold_mode
== BOLD_FONT
)
908 DeleteObject (fonts
[FONT_BOLDUND
]);
911 if (bold_mode
== BOLD_FONT
&&
912 fsize
[FONT_BOLD
] != fsize
[FONT_NORMAL
]) {
913 bold_mode
= BOLD_SHADOW
;
914 DeleteObject (fonts
[FONT_BOLD
]);
915 if (und_mode
== UND_FONT
)
916 DeleteObject (fonts
[FONT_BOLDUND
]);
920 /* With the fascist font painting it doesn't matter if the linedraw font
921 * isn't exactly the right size anymore so we don't have to check this.
923 if (cfg
.vtmode
== VT_OEMANSI
&& fsize
[FONT_OEM
] != fsize
[FONT_NORMAL
] ) {
924 if( cfg
.fontcharset
== OEM_CHARSET
)
926 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
927 "different sizes. Using OEM-only mode instead",
928 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
929 cfg
.vtmode
= VT_OEMONLY
;
931 else if( firstchar
< ' ' )
933 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
934 "different sizes. Using XTerm mode instead",
935 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
936 cfg
.vtmode
= VT_XWINDOWS
;
940 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
941 "different sizes. Using ISO8859-1 mode instead",
942 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
943 cfg
.vtmode
= VT_POORMAN
;
948 DeleteObject (fonts
[i
]);
954 void request_resize (int w
, int h
, int refont
) {
957 /* If the window is maximized supress resizing attempts */
958 if(IsZoomed(hwnd
)) return;
961 /* Don't do this in OEMANSI, you may get disable messages */
962 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132)
963 && cfg
.vtmode
!= VT_OEMANSI
)
965 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132))
968 /* If font width too big for screen should we shrink the font more ? */
970 font_width
= ((font_width
*cols
+w
/2)/w
);
977 DeleteObject(fonts
[i
]);
979 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
981 init_fonts(font_width
);
985 static int first_time
= 1;
991 /* Get the size of the screen */
992 if (GetClientRect(GetDesktopWindow(),&ss
))
993 /* first_time = 0 */;
994 else { first_time
= 2; break; }
996 /* Make sure the values are sane */
997 width
= (ss
.right
-ss
.left
-extra_width
) / font_width
;
998 height
= (ss
.bottom
-ss
.top
-extra_height
) / font_height
;
1000 if (w
>width
) w
=width
;
1001 if (h
>height
) h
=height
;
1007 width
= extra_width
+ font_width
* w
;
1008 height
= extra_height
+ font_height
* h
;
1010 SetWindowPos (hwnd
, NULL
, 0, 0, width
, height
,
1011 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1012 SWP_NOMOVE
| SWP_NOZORDER
);
1015 static void click (Mouse_Button b
, int x
, int y
) {
1016 int thistime
= GetMessageTime();
1018 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1019 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1020 lastact
== MA_2CLK ? MA_3CLK
:
1021 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1026 if (lastact
!= MA_NOTHING
)
1027 term_mouse (b
, lastact
, x
, y
);
1028 lasttime
= thistime
;
1031 static LRESULT CALLBACK
WndProc (HWND hwnd
, UINT message
,
1032 WPARAM wParam
, LPARAM lParam
) {
1034 static int ignore_size
= FALSE
;
1035 static int ignore_clip
= FALSE
;
1036 static int just_reconfigged
= FALSE
;
1037 static int resizing
= FALSE
;
1041 if (pending_netevent
)
1042 enact_pending_netevent();
1048 if (cfg
.ping_interval
> 0)
1052 if (now
-last_movement
> cfg
.ping_interval
* 60 - 10)
1054 back
->special(TS_PING
);
1055 last_movement
= now
;
1062 if (!cfg
.warn_on_close
|| session_closed
||
1063 MessageBox(hwnd
, "Are you sure you want to close this session?",
1064 "PuTTY Exit Confirmation",
1065 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1066 DestroyWindow(hwnd
);
1069 PostQuitMessage (0);
1072 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1084 PROCESS_INFORMATION pi
;
1085 HANDLE filemap
= NULL
;
1087 if (wParam
== IDM_DUPSESS
) {
1089 * Allocate a file-mapping memory chunk for the
1092 SECURITY_ATTRIBUTES sa
;
1095 sa
.nLength
= sizeof(sa
);
1096 sa
.lpSecurityDescriptor
= NULL
;
1097 sa
.bInheritHandle
= TRUE
;
1098 filemap
= CreateFileMapping((HANDLE
)0xFFFFFFFF,
1105 p
= (Config
*)MapViewOfFile(filemap
,
1107 0, 0, sizeof(Config
));
1109 *p
= cfg
; /* structure copy */
1113 sprintf(c
, "putty &%p", filemap
);
1115 } else if (wParam
== IDM_SAVEDSESS
) {
1116 char *session
= sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1117 cl
= malloc(16 + strlen(session
)); /* 8, but play safe */
1119 cl
= NULL
; /* not a very important failure mode */
1121 sprintf(cl
, "putty @%s", session
);
1127 GetModuleFileName (NULL
, b
, sizeof(b
)-1);
1129 si
.lpReserved
= NULL
;
1130 si
.lpDesktop
= NULL
;
1134 si
.lpReserved2
= NULL
;
1135 CreateProcess (b
, cl
, NULL
, NULL
, TRUE
,
1136 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1139 CloseHandle(filemap
);
1145 if (!do_reconfig(hwnd
))
1147 just_reconfigged
= TRUE
;
1152 DeleteObject(fonts
[i
]);
1154 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1155 und_mode
= UND_FONT
;
1158 /* Telnet will change local echo -> remote if the remote asks */
1159 if (cfg
.protocol
!= PROT_TELNET
)
1160 ldisc
= (cfg
.ldisc_term ?
&ldisc_term
: &ldisc_simple
);
1168 /* Enable or disable the scroll bar, etc */
1170 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1173 if (cfg
.scrollbar
) nflg
|= WS_VSCROLL
;
1174 else nflg
&= ~WS_VSCROLL
;
1176 nflg
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1178 nflg
|= (WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1184 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1185 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1186 SetWindowPos(hwnd
, NULL
, 0,0,0,0,
1187 SWP_NOACTIVATE
|SWP_NOCOPYBITS
|
1188 SWP_NOMOVE
|SWP_NOSIZE
| SWP_NOZORDER
|
1191 GetWindowRect (hwnd
, &wr
);
1192 GetClientRect (hwnd
, &cr
);
1193 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1194 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1198 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1199 InvalidateRect(hwnd
, NULL
, TRUE
);
1200 SetWindowPos (hwnd
, NULL
, 0, 0,
1201 extra_width
+ font_width
* cfg
.width
,
1202 extra_height
+ font_height
* cfg
.height
,
1203 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1204 SWP_NOMOVE
| SWP_NOZORDER
);
1205 if (IsIconic(hwnd
)) {
1206 SetWindowText (hwnd
,
1207 cfg
.win_name_always ? window_name
: icon_name
);
1216 case IDM_TEL_AYT
: back
->special (TS_AYT
); break;
1217 case IDM_TEL_BRK
: back
->special (TS_BRK
); break;
1218 case IDM_TEL_SYNCH
: back
->special (TS_SYNCH
); break;
1219 case IDM_TEL_EC
: back
->special (TS_EC
); break;
1220 case IDM_TEL_EL
: back
->special (TS_EL
); break;
1221 case IDM_TEL_GA
: back
->special (TS_GA
); break;
1222 case IDM_TEL_NOP
: back
->special (TS_NOP
); break;
1223 case IDM_TEL_ABORT
: back
->special (TS_ABORT
); break;
1224 case IDM_TEL_AO
: back
->special (TS_AO
); break;
1225 case IDM_TEL_IP
: back
->special (TS_IP
); break;
1226 case IDM_TEL_SUSP
: back
->special (TS_SUSP
); break;
1227 case IDM_TEL_EOR
: back
->special (TS_EOR
); break;
1228 case IDM_TEL_EOF
: back
->special (TS_EOF
); break;
1233 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1234 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1239 #define X_POS(l) ((int)(short)LOWORD(l))
1240 #define Y_POS(l) ((int)(short)HIWORD(l))
1242 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1243 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1245 case WM_LBUTTONDOWN
:
1246 click (MB_SELECT
, TO_CHR_X(X_POS(lParam
)),
1247 TO_CHR_Y(Y_POS(lParam
)));
1251 term_mouse (MB_SELECT
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1252 TO_CHR_Y(Y_POS(lParam
)));
1255 case WM_MBUTTONDOWN
:
1257 click (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1258 TO_CHR_X(X_POS(lParam
)),
1259 TO_CHR_Y(Y_POS(lParam
)));
1262 term_mouse (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1263 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1264 TO_CHR_Y(Y_POS(lParam
)));
1267 case WM_RBUTTONDOWN
:
1269 click (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1270 TO_CHR_X(X_POS(lParam
)),
1271 TO_CHR_Y(Y_POS(lParam
)));
1274 term_mouse (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1275 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1276 TO_CHR_Y(Y_POS(lParam
)));
1281 * Add the mouse position and message time to the random
1282 * number noise, if we're using ssh.
1284 if (cfg
.protocol
== PROT_SSH
)
1285 noise_ultralight(lParam
);
1287 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1289 if (wParam
& MK_LBUTTON
)
1291 else if (wParam
& MK_MBUTTON
)
1292 b
= cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
;
1294 b
= cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
;
1295 term_mouse (b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1296 TO_CHR_Y(Y_POS(lParam
)));
1299 case WM_IGNORE_CLIP
:
1300 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1302 case WM_DESTROYCLIPBOARD
:
1305 ignore_clip
= FALSE
;
1311 hdc
= BeginPaint (hwnd
, &p
);
1313 SelectPalette (hdc
, pal
, TRUE
);
1314 RealizePalette (hdc
);
1316 term_paint (hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1317 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1318 SelectObject (hdc
, GetStockObject(SYSTEM_FONT
));
1319 SelectObject (hdc
, GetStockObject(WHITE_PEN
));
1320 EndPaint (hwnd
, &p
);
1325 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1326 * but the only one that's likely to try to overload us is FD_READ.
1327 * This means buffering just one is fine.
1329 if (pending_netevent
)
1330 enact_pending_netevent();
1332 pending_netevent
= TRUE
;
1333 pend_netevent_wParam
=wParam
;
1334 pend_netevent_lParam
=lParam
;
1335 time(&last_movement
);
1339 CreateCaret(hwnd
, caretbm
, 0, 0);
1350 case WM_IGNORE_SIZE
:
1351 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1353 case WM_ENTERSIZEMOVE
:
1357 case WM_EXITSIZEMOVE
:
1364 int width
, height
, w
, h
, ew
, eh
;
1365 LPRECT r
= (LPRECT
)lParam
;
1367 width
= r
->right
- r
->left
- extra_width
;
1368 height
= r
->bottom
- r
->top
- extra_height
;
1369 w
= (width
+ font_width
/2) / font_width
; if (w
< 1) w
= 1;
1370 h
= (height
+ font_height
/2) / font_height
; if (h
< 1) h
= 1;
1371 UpdateSizeTip(hwnd
, w
, h
);
1372 ew
= width
- w
* font_width
;
1373 eh
= height
- h
* font_height
;
1375 if (wParam
== WMSZ_LEFT
||
1376 wParam
== WMSZ_BOTTOMLEFT
||
1377 wParam
== WMSZ_TOPLEFT
)
1383 if (wParam
== WMSZ_TOP
||
1384 wParam
== WMSZ_TOPRIGHT
||
1385 wParam
== WMSZ_TOPLEFT
)
1395 /* break; (never reached) */
1397 if (wParam
== SIZE_MINIMIZED
) {
1398 SetWindowText (hwnd
,
1399 cfg
.win_name_always ? window_name
: icon_name
);
1402 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1403 SetWindowText (hwnd
, window_name
);
1405 int width
, height
, w
, h
;
1406 #if 0 /* we have fixed this using WM_SIZING now */
1410 width
= LOWORD(lParam
);
1411 height
= HIWORD(lParam
);
1412 w
= width
/ font_width
; if (w
< 1) w
= 1;
1413 h
= height
/ font_height
; if (h
< 1) h
= 1;
1414 #if 0 /* we have fixed this using WM_SIZING now */
1415 ew
= width
- w
* font_width
;
1416 eh
= height
- h
* font_height
;
1417 if (ew
!= 0 || eh
!= 0) {
1419 GetWindowRect (hwnd
, &r
);
1420 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1421 SetWindowPos (hwnd
, NULL
, 0, 0,
1422 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1423 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1426 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1428 term_size (h
, w
, cfg
.savelines
);
1430 * Don't call back->size in mid-resize. (To prevent
1431 * massive numbers of resize events getting sent
1432 * down the connection during an NT opaque drag.)
1436 just_reconfigged
= FALSE
;
1439 ignore_size
= FALSE
;
1442 switch (LOWORD(wParam
)) {
1443 case SB_BOTTOM
: term_scroll(-1, 0); break;
1444 case SB_TOP
: term_scroll(+1, 0); break;
1445 case SB_LINEDOWN
: term_scroll (0, +1); break;
1446 case SB_LINEUP
: term_scroll (0, -1); break;
1447 case SB_PAGEDOWN
: term_scroll (0, +rows
/2); break;
1448 case SB_PAGEUP
: term_scroll (0, -rows
/2); break;
1449 case SB_THUMBPOSITION
: case SB_THUMBTRACK
:
1450 term_scroll (1, HIWORD(wParam
)); break;
1453 case WM_PALETTECHANGED
:
1454 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1455 HDC hdc
= get_ctx();
1457 if (RealizePalette (hdc
) > 0)
1463 case WM_QUERYNEWPALETTE
:
1465 HDC hdc
= get_ctx();
1467 if (RealizePalette (hdc
) > 0)
1479 * Add the scan code and keypress timing to the random
1480 * number noise, if we're using ssh.
1482 if (cfg
.protocol
== PROT_SSH
)
1483 noise_ultralight(lParam
);
1486 * We don't do TranslateMessage since it disassociates the
1487 * resulting CHAR message from the KEYDOWN that sparked it,
1488 * which we occasionally don't want. Instead, we process
1489 * KEYDOWN, and call the Win32 translator functions so that
1490 * we get the translations under _our_ control.
1493 unsigned char buf
[20];
1496 if (wParam
==VK_PROCESSKEY
) {
1499 m
.message
= WM_KEYDOWN
;
1501 m
.lParam
= lParam
& 0xdfff;
1502 TranslateMessage(&m
);
1504 len
= TranslateKey (message
, wParam
, lParam
, buf
);
1506 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1507 ldisc
->send (buf
, len
);
1513 unsigned char buf
[2];
1516 buf
[0] = wParam
>> 8;
1517 ldisc
->send (buf
, 2);
1522 * Nevertheless, we are prepared to deal with WM_CHAR
1523 * messages, should they crop up. So if someone wants to
1524 * post the things to us as part of a macro manoeuvre,
1525 * we're ready to cope.
1528 char c
= xlat_kbd2tty((unsigned char)wParam
);
1529 ldisc
->send (&c
, 1);
1534 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1538 * Move the system caret. (We maintain one, even though it's
1539 * invisible, for the benefit of blind people: apparently some
1540 * helper software tracks the system caret, so we should arrange to
1543 void sys_cursor(int x
, int y
) {
1544 SetCaretPos(x
* font_width
, y
* font_height
);
1548 * Draw a line of text in the window, at given character
1549 * coordinates, in given attributes.
1551 * We are allowed to fiddle with the contents of `text'.
1553 void do_text (Context ctx
, int x
, int y
, char *text
, int len
,
1554 unsigned long attr
, int lattr
) {
1556 int nfg
, nbg
, nfont
;
1559 int force_manual_underline
= 0;
1560 int fnt_width
= font_width
*(1+(lattr
!=LATTR_NORM
));
1561 static int *IpDx
= 0, IpDxLEN
= 0;;
1563 if (len
>IpDxLEN
|| IpDx
[0] != fnt_width
) {
1567 IpDx
= smalloc((len
+16)*sizeof(int));
1570 for(i
=0; i
<IpDxLEN
; i
++)
1571 IpDx
[i
] = fnt_width
;
1577 if (attr
& ATTR_ACTCURS
) {
1578 attr
&= (bold_mode
== BOLD_COLOURS ?
0x300200 : 0x300300);
1579 attr
^= ATTR_CUR_XOR
;
1583 if (cfg
.vtmode
== VT_OEMONLY
)
1587 * Map high-half characters in order to approximate ISO using
1588 * OEM character set. No characters are missing if the OEM codepage
1591 if (nfont
& FONT_OEM
) {
1593 for (i
=0; i
<len
; i
++)
1594 if (text
[i
] >= '\xA0' && text
[i
] <= '\xFF') {
1596 /* This is CP850 ... perfect translation */
1597 static const char oemhighhalf
[] =
1598 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1599 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1600 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1601 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1602 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1603 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1604 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1605 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1606 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1607 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1608 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1609 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1612 /* This is CP437 ... junk translation */
1613 static const unsigned char oemhighhalf
[] = {
1614 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1615 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1616 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1617 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1618 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1619 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1620 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1621 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1622 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1623 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1624 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1625 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1628 text
[i
] = oemhighhalf
[(unsigned char)text
[i
] - 0xA0];
1632 if (attr
& ATTR_LINEDRW
) {
1635 static const char poorman
[] =
1636 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1639 static const char oemmap_437
[] =
1640 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1641 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1644 static const char oemmap_850
[] =
1645 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1646 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1648 /* Poor windows font ... eg: windows courier */
1649 static const char oemmap
[] =
1650 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1651 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1654 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1655 * VT100 line drawing chars; everything else stays normal.
1657 * Actually '_' maps to space too, but that's done before.
1659 switch (cfg
.vtmode
) {
1661 for (i
=0; i
<len
; i
++)
1662 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1663 text
[i
] += '\x01' - '\x60';
1666 /* Make sure we actually have an OEM font */
1667 if (fonts
[nfont
|FONT_OEM
]) {
1670 for (i
=0; i
<len
; i
++)
1671 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1672 text
[i
] = oemmap
[(unsigned char)text
[i
] - 0x60];
1676 for (i
=0; i
<len
; i
++)
1677 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1678 text
[i
] = poorman
[(unsigned char)text
[i
] - 0x60];
1683 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
1684 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
1685 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
1687 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
1688 nfont
|= FONT_UNDERLINE
;
1691 if (nfont
&FONT_UNDERLINE
)
1692 force_manual_underline
= 1;
1693 /* Don't do the same for manual bold, it could be bad news. */
1695 nfont
&= ~(FONT_BOLD
|FONT_UNDERLINE
);
1697 if (font_needs_hand_underlining
&& (attr
& ATTR_UNDER
))
1698 force_manual_underline
= 1;
1699 if (attr
& ATTR_REVERSE
) {
1700 t
= nfg
; nfg
= nbg
; nbg
= t
;
1702 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
1704 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
1708 SelectObject (hdc
, fonts
[nfont
]);
1709 SetTextColor (hdc
, fg
);
1710 SetBkColor (hdc
, bg
);
1711 SetBkMode (hdc
, OPAQUE
);
1714 line_box
.right
= x
+fnt_width
*len
;
1715 line_box
.bottom
= y
+font_height
;
1716 ExtTextOut (hdc
, x
, y
, ETO_CLIPPED
|ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
1717 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
1718 SetBkMode (hdc
, TRANSPARENT
);
1720 /* GRR: This draws the character outside it's box and can leave
1721 * 'droppings' even with the clip box! I suppose I could loop it
1722 * one character at a time ... yuk.
1724 * Or ... I could do a test print with "W", and use +1 or -1 for this
1725 * shift depending on if the leftmost column is blank...
1727 ExtTextOut (hdc
, x
-1, y
, ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
1729 if (force_manual_underline
||
1730 (und_mode
== UND_LINE
&& (attr
& ATTR_UNDER
))) {
1732 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, fg
));
1733 MoveToEx (hdc
, x
, y
+descent
, NULL
);
1734 LineTo (hdc
, x
+len
*fnt_width
, y
+descent
);
1735 oldpen
= SelectObject (hdc
, oldpen
);
1736 DeleteObject (oldpen
);
1738 if (attr
& ATTR_PASCURS
) {
1741 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
1742 pts
[2].x
= pts
[3].x
= x
+fnt_width
-1;
1743 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
1744 pts
[1].y
= pts
[2].y
= y
+font_height
-1;
1745 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
1746 Polyline (hdc
, pts
, 5);
1747 oldpen
= SelectObject (hdc
, oldpen
);
1748 DeleteObject (oldpen
);
1752 static int check_compose(int first
, int second
) {
1754 static char * composetbl
[] = {
1755 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1756 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1757 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1758 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1759 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1760 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1761 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1762 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1763 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1764 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1765 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1766 "\"uü", "'yý", "htþ", "\"yÿ",
1770 static int recurse
= 0;
1773 for(c
=composetbl
; *c
; c
++) {
1774 if( (*c
)[0] == first
&& (*c
)[1] == second
)
1776 return (*c
)[2] & 0xFF;
1783 nc
= check_compose(second
, first
);
1785 nc
= check_compose(toupper(first
), toupper(second
));
1787 nc
= check_compose(toupper(second
), toupper(first
));
1795 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1796 * codes. Returns number of bytes used or zero to drop the message
1797 * or -1 to forward the message to windows.
1799 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
1800 unsigned char *output
) {
1802 int scan
, left_alt
= 0, key_down
, shift_state
;
1804 unsigned char * p
= output
;
1806 static WORD keys
[3];
1807 static int compose_state
= 0;
1808 static int compose_char
= 0;
1809 static WPARAM compose_key
= 0;
1811 r
= GetKeyboardState(keystate
);
1812 if (!r
) memset(keystate
, 0, sizeof(keystate
));
1816 { /* Tell us all about key events */
1817 static BYTE oldstate
[256];
1818 static int first
= 1;
1821 if(first
) memcpy(oldstate
, keystate
, sizeof(oldstate
));
1824 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
) {
1826 } else if ((HIWORD(lParam
)&KF_UP
) && scan
==(HIWORD(lParam
) & 0xFF) ) {
1830 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
1831 debug(("K_F%d", wParam
+1-VK_F1
));
1834 case VK_SHIFT
: debug(("SHIFT")); break;
1835 case VK_CONTROL
: debug(("CTRL")); break;
1836 case VK_MENU
: debug(("ALT")); break;
1837 default: debug(("VK_%02x", wParam
));
1839 if(message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
1841 debug((", S%02x", scan
=(HIWORD(lParam
) & 0xFF) ));
1843 ch
= MapVirtualKey(wParam
, 2);
1844 if (ch
>=' ' && ch
<='~') debug((", '%c'", ch
));
1845 else if (ch
) debug((", $%02x", ch
));
1847 if (keys
[0]) debug((", KB0=%02x", keys
[0]));
1848 if (keys
[1]) debug((", KB1=%02x", keys
[1]));
1849 if (keys
[2]) debug((", KB2=%02x", keys
[2]));
1851 if ( (keystate
[VK_SHIFT
]&0x80)!=0) debug((", S"));
1852 if ( (keystate
[VK_CONTROL
]&0x80)!=0) debug((", C"));
1853 if ( (HIWORD(lParam
)&KF_EXTENDED
) ) debug((", E"));
1854 if ( (HIWORD(lParam
)&KF_UP
) ) debug((", U"));
1857 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
)
1859 else if ( (HIWORD(lParam
)&KF_UP
) )
1860 oldstate
[wParam
&0xFF] ^= 0x80;
1862 oldstate
[wParam
&0xFF] ^= 0x81;
1864 for(ch
=0; ch
<256; ch
++)
1865 if (oldstate
[ch
] != keystate
[ch
])
1866 debug((", M%02x=%02x", ch
, keystate
[ch
]));
1868 memcpy(oldstate
, keystate
, sizeof(oldstate
));
1872 /* Note if AltGr was pressed and if it was used as a compose key */
1873 if (cfg
.compose_key
) {
1874 if (wParam
== VK_MENU
&& (HIWORD(lParam
)&KF_EXTENDED
))
1876 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
1877 if (!compose_state
) compose_key
= wParam
;
1879 if (wParam
== VK_APPS
&& !compose_state
)
1880 compose_key
= wParam
;
1882 if (wParam
== compose_key
)
1884 if (compose_state
== 0 && (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
1886 else if (compose_state
== 1 && (HIWORD(lParam
)&KF_UP
))
1891 else if (compose_state
==1 && wParam
!= VK_CONTROL
)
1896 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1897 if ( (cfg
.funky_type
== 3 || (cfg
.funky_type
<= 1 && app_keypad_keys
))
1898 && wParam
==VK_NUMLOCK
&& !(keystate
[VK_SHIFT
]&0x80)) {
1900 wParam
= VK_EXECUTE
;
1902 /* UnToggle NUMLock */
1903 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
1904 keystate
[VK_NUMLOCK
] ^= 1;
1907 /* And write back the 'adjusted' state */
1908 SetKeyboardState (keystate
);
1911 /* Disable Auto repeat if required */
1912 if (repeat_off
&& (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
)
1915 if ((HIWORD(lParam
)&KF_ALTDOWN
) && (keystate
[VK_RMENU
]&0x80) == 0)
1918 key_down
= ((HIWORD(lParam
)&KF_UP
)==0);
1920 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1921 if (left_alt
&& (keystate
[VK_CONTROL
]&0x80))
1922 keystate
[VK_MENU
] = 0;
1924 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
1925 shift_state
= ((keystate
[VK_SHIFT
]&0x80)!=0)
1926 + ((keystate
[VK_CONTROL
]&0x80)!=0)*2;
1929 * Record that we pressed key so the scroll window can be reset, but
1930 * be careful to avoid Shift-UP/Down
1932 if( wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
1936 /* Make sure we're not pasting */
1937 if (key_down
) term_nopaste();
1939 if (compose_state
>1 && left_alt
) compose_state
= 0;
1941 /* Sanitize the number pad if not using a PC NumPad */
1942 if( left_alt
|| (app_keypad_keys
&& cfg
.funky_type
!= 2)
1943 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
)
1945 if ((HIWORD(lParam
)&KF_EXTENDED
) == 0)
1950 case VK_INSERT
: nParam
= VK_NUMPAD0
; break;
1951 case VK_END
: nParam
= VK_NUMPAD1
; break;
1952 case VK_DOWN
: nParam
= VK_NUMPAD2
; break;
1953 case VK_NEXT
: nParam
= VK_NUMPAD3
; break;
1954 case VK_LEFT
: nParam
= VK_NUMPAD4
; break;
1955 case VK_CLEAR
: nParam
= VK_NUMPAD5
; break;
1956 case VK_RIGHT
: nParam
= VK_NUMPAD6
; break;
1957 case VK_HOME
: nParam
= VK_NUMPAD7
; break;
1958 case VK_UP
: nParam
= VK_NUMPAD8
; break;
1959 case VK_PRIOR
: nParam
= VK_NUMPAD9
; break;
1960 case VK_DELETE
: nParam
= VK_DECIMAL
; break;
1964 if (keystate
[VK_NUMLOCK
]&1) shift_state
|= 1;
1970 /* If a key is pressed and AltGr is not active */
1971 if (key_down
&& (keystate
[VK_RMENU
]&0x80) == 0 && !compose_state
)
1973 /* Okay, prepare for most alts then ...*/
1974 if (left_alt
) *p
++ = '\033';
1976 /* Lets see if it's a pattern we know all about ... */
1977 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
1978 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
1981 if (wParam
== VK_NEXT
&& shift_state
== 1) {
1982 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
1985 if (wParam
== VK_INSERT
&& shift_state
== 1) {
1986 term_mouse (MB_PASTE
, MA_CLICK
, 0, 0);
1987 term_mouse (MB_PASTE
, MA_RELEASE
, 0, 0);
1990 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
1993 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
1995 SendMessage (hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
1998 /* Control-Numlock for app-keypad mode switch */
1999 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
2000 app_keypad_keys
^= 1;
2004 /* Nethack keypad */
2005 if (cfg
.nethack_keypad
&& !left_alt
) {
2007 case VK_NUMPAD1
: *p
++ = shift_state ?
'B': 'b'; return p
-output
;
2008 case VK_NUMPAD2
: *p
++ = shift_state ?
'J': 'j'; return p
-output
;
2009 case VK_NUMPAD3
: *p
++ = shift_state ?
'N': 'n'; return p
-output
;
2010 case VK_NUMPAD4
: *p
++ = shift_state ?
'H': 'h'; return p
-output
;
2011 case VK_NUMPAD5
: *p
++ = shift_state ?
'.': '.'; return p
-output
;
2012 case VK_NUMPAD6
: *p
++ = shift_state ?
'L': 'l'; return p
-output
;
2013 case VK_NUMPAD7
: *p
++ = shift_state ?
'Y': 'y'; return p
-output
;
2014 case VK_NUMPAD8
: *p
++ = shift_state ?
'K': 'k'; return p
-output
;
2015 case VK_NUMPAD9
: *p
++ = shift_state ?
'U': 'u'; return p
-output
;
2019 /* Application Keypad */
2023 if ( cfg
.funky_type
== 3 ||
2024 ( cfg
.funky_type
<= 1 && app_keypad_keys
)) switch(wParam
) {
2025 case VK_EXECUTE
: xkey
= 'P'; break;
2026 case VK_DIVIDE
: xkey
= 'Q'; break;
2027 case VK_MULTIPLY
:xkey
= 'R'; break;
2028 case VK_SUBTRACT
:xkey
= 'S'; break;
2030 if(app_keypad_keys
) switch(wParam
) {
2031 case VK_NUMPAD0
: xkey
= 'p'; break;
2032 case VK_NUMPAD1
: xkey
= 'q'; break;
2033 case VK_NUMPAD2
: xkey
= 'r'; break;
2034 case VK_NUMPAD3
: xkey
= 's'; break;
2035 case VK_NUMPAD4
: xkey
= 't'; break;
2036 case VK_NUMPAD5
: xkey
= 'u'; break;
2037 case VK_NUMPAD6
: xkey
= 'v'; break;
2038 case VK_NUMPAD7
: xkey
= 'w'; break;
2039 case VK_NUMPAD8
: xkey
= 'x'; break;
2040 case VK_NUMPAD9
: xkey
= 'y'; break;
2042 case VK_DECIMAL
: xkey
= 'n'; break;
2043 case VK_ADD
: if(cfg
.funky_type
==2) {
2044 if(shift_state
) xkey
= 'l';
2046 } else if(shift_state
) xkey
= 'm';
2050 case VK_DIVIDE
: if(cfg
.funky_type
==2) xkey
= 'o'; break;
2051 case VK_MULTIPLY
:if(cfg
.funky_type
==2) xkey
= 'j'; break;
2052 case VK_SUBTRACT
:if(cfg
.funky_type
==2) xkey
= 'm'; break;
2055 if (HIWORD(lParam
)&KF_EXTENDED
)
2063 if (xkey
>='P' && xkey
<='S')
2064 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
2066 p
+= sprintf((char *)p
, "\x1B?%c", xkey
);
2069 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
2074 if (wParam
== VK_BACK
&& shift_state
== 0 ) /* Backspace */
2076 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
2079 if (wParam
== VK_TAB
&& shift_state
== 1 ) /* Shift tab */
2081 *p
++ = 0x1B; *p
++ = '['; *p
++ = 'Z'; return p
- output
;
2083 if (wParam
== VK_SPACE
&& shift_state
== 2 ) /* Ctrl-Space */
2085 *p
++ = 0; return p
- output
;
2087 if (wParam
== VK_SPACE
&& shift_state
== 3 ) /* Ctrl-Shift-Space */
2089 *p
++ = 160; return p
- output
;
2091 if (wParam
== VK_CANCEL
&& shift_state
== 2 ) /* Ctrl-Break */
2093 *p
++ = 3; return p
- output
;
2095 /* Control-2 to Control-8 are special */
2096 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8')
2098 *p
++ = "\000\033\034\035\036\037\177"[wParam
-'2'];
2101 if (shift_state
== 2 && wParam
== 0xBD) {
2105 if (shift_state
== 2 && wParam
== 0xDF) {
2109 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
2110 *p
++ = '\r'; *p
++ = '\n';
2115 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2116 * for integer decimal nn.)
2118 * We also deal with the weird ones here. Linux VCs replace F1
2119 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2120 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2125 case VK_F1
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11); break;
2126 case VK_F2
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12); break;
2127 case VK_F3
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13); break;
2128 case VK_F4
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14); break;
2129 case VK_F5
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15); break;
2130 case VK_F6
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17); break;
2131 case VK_F7
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18); break;
2132 case VK_F8
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19); break;
2133 case VK_F9
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20); break;
2134 case VK_F10
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21); break;
2135 case VK_F11
: code
= 23; break;
2136 case VK_F12
: code
= 24; break;
2137 case VK_F13
: code
= 25; break;
2138 case VK_F14
: code
= 26; break;
2139 case VK_F15
: code
= 28; break;
2140 case VK_F16
: code
= 29; break;
2141 case VK_F17
: code
= 31; break;
2142 case VK_F18
: code
= 32; break;
2143 case VK_F19
: code
= 33; break;
2144 case VK_F20
: code
= 34; break;
2145 case VK_HOME
: code
= 1; break;
2146 case VK_INSERT
: code
= 2; break;
2147 case VK_DELETE
: code
= 3; break;
2148 case VK_END
: code
= 4; break;
2149 case VK_PRIOR
: code
= 5; break;
2150 case VK_NEXT
: code
= 6; break;
2152 /* Reorder edit keys to physical order */
2153 if (cfg
.funky_type
== 3 && code
<= 6 ) code
= "\0\2\1\4\5\3\6"[code
];
2155 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
2156 p
+= sprintf((char *)p
, "\x1B[[%c", code
+ 'A' - 11);
2159 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
2161 p
+= sprintf((char *)p
, "\x1B%c", code
+ 'P' - 11);
2163 p
+= sprintf((char *)p
, "\x1BO%c", code
+ 'P' - 11);
2166 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
2167 p
+= sprintf((char *)p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
2171 p
+= sprintf((char *)p
, "\x1B[%d~", code
);
2176 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2177 * some reason seems to send VK_CLEAR to Windows...).
2182 case VK_UP
: xkey
= 'A'; break;
2183 case VK_DOWN
: xkey
= 'B'; break;
2184 case VK_RIGHT
: xkey
= 'C'; break;
2185 case VK_LEFT
: xkey
= 'D'; break;
2186 case VK_CLEAR
: xkey
= 'G'; break;
2191 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
2192 else if (app_cursor_keys
)
2193 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
2195 p
+= sprintf((char *)p
, "\x1B[%c", xkey
);
2201 * Finally, deal with Return ourselves. (Win95 seems to
2202 * foul it up when Alt is pressed, for some reason.)
2204 if (wParam
== VK_RETURN
) /* Return */
2211 /* Okay we've done everything interesting; let windows deal with
2212 * the boring stuff */
2214 BOOL capsOn
=keystate
[VK_CAPITAL
] !=0;
2216 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2217 if(cfg
.xlat_capslockcyr
)
2218 keystate
[VK_CAPITAL
] = 0;
2220 r
= ToAscii (wParam
, scan
, keystate
, keys
, 0);
2226 unsigned char ch
= (unsigned char)keys
[i
];
2228 if (compose_state
==2 && (ch
&0x80) == 0 && ch
>' ') {
2233 if (compose_state
==3 && (ch
&0x80) == 0 && ch
>' ') {
2237 if ((nc
=check_compose(compose_char
,ch
)) == -1)
2239 MessageBeep(MB_ICONHAND
);
2242 *p
++ = xlat_kbd2tty((unsigned char)nc
);
2248 if( left_alt
&& key_down
) *p
++ = '\033';
2254 ch
= xlat_latkbd2win(ch
);
2255 *p
++ = xlat_kbd2tty(ch
);
2259 /* This is so the ALT-Numpad and dead keys work correctly. */
2266 /* This stops ALT press-release doing a 'COMMAND MENU' function */
2267 if (!cfg
.alt_only
) {
2268 if (message
== WM_SYSKEYUP
&& wParam
== VK_MENU
)
2275 void set_title (char *title
) {
2276 sfree (window_name
);
2277 window_name
= smalloc(1+strlen(title
));
2278 strcpy (window_name
, title
);
2279 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
2280 SetWindowText (hwnd
, title
);
2283 void set_icon (char *title
) {
2285 icon_name
= smalloc(1+strlen(title
));
2286 strcpy (icon_name
, title
);
2287 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
2288 SetWindowText (hwnd
, title
);
2291 void set_sbar (int total
, int start
, int page
) {
2294 if (!cfg
.scrollbar
) return;
2296 si
.cbSize
= sizeof(si
);
2297 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
2299 si
.nMax
= total
- 1;
2303 SetScrollInfo (hwnd
, SB_VERT
, &si
, TRUE
);
2306 Context
get_ctx(void) {
2311 SelectPalette (hdc
, pal
, FALSE
);
2317 void free_ctx (Context ctx
) {
2318 SelectPalette (ctx
, GetStockObject (DEFAULT_PALETTE
), FALSE
);
2319 ReleaseDC (hwnd
, ctx
);
2322 static void real_palette_set (int n
, int r
, int g
, int b
) {
2324 logpal
->palPalEntry
[n
].peRed
= r
;
2325 logpal
->palPalEntry
[n
].peGreen
= g
;
2326 logpal
->palPalEntry
[n
].peBlue
= b
;
2327 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
2328 colours
[n
] = PALETTERGB(r
, g
, b
);
2329 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2331 colours
[n
] = RGB(r
, g
, b
);
2334 void palette_set (int n
, int r
, int g
, int b
) {
2335 static const int first
[21] = {
2336 0, 2, 4, 6, 8, 10, 12, 14,
2337 1, 3, 5, 7, 9, 11, 13, 15,
2340 real_palette_set (first
[n
], r
, g
, b
);
2342 real_palette_set (first
[n
]+1, r
, g
, b
);
2344 HDC hdc
= get_ctx();
2345 UnrealizeObject (pal
);
2346 RealizePalette (hdc
);
2351 void palette_reset (void) {
2354 for (i
= 0; i
< NCOLOURS
; i
++) {
2356 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
2357 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
2358 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
2359 logpal
->palPalEntry
[i
].peFlags
= 0;
2360 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
2361 defpal
[i
].rgbtGreen
,
2362 defpal
[i
].rgbtBlue
);
2364 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
2365 defpal
[i
].rgbtGreen
,
2366 defpal
[i
].rgbtBlue
);
2371 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2373 RealizePalette (hdc
);
2378 void write_clip (void *data
, int len
, int must_deselect
) {
2382 clipdata
= GlobalAlloc (GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
2385 lock
= GlobalLock (clipdata
);
2388 memcpy (lock
, data
, len
);
2389 ((unsigned char *) lock
) [len
] = 0;
2390 GlobalUnlock (clipdata
);
2393 SendMessage (hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
2395 if (OpenClipboard (hwnd
)) {
2397 SetClipboardData (CF_TEXT
, clipdata
);
2400 GlobalFree (clipdata
);
2403 SendMessage (hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
2406 void get_clip (void **p
, int *len
) {
2407 static HGLOBAL clipdata
= NULL
;
2411 GlobalUnlock (clipdata
);
2415 if (OpenClipboard (NULL
)) {
2416 clipdata
= GetClipboardData (CF_TEXT
);
2419 *p
= GlobalLock (clipdata
);
2433 * Move `lines' lines from position `from' to position `to' in the
2436 void optimised_move (int to
, int from
, int lines
) {
2440 min
= (to
< from ? to
: from
);
2441 max
= to
+ from
- min
;
2443 r
.left
= 0; r
.right
= cols
* font_width
;
2444 r
.top
= min
* font_height
; r
.bottom
= (max
+lines
) * font_height
;
2445 ScrollWindow (hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
2449 * Print a message box and perform a fatal exit.
2451 void fatalbox(char *fmt
, ...) {
2456 vsprintf(stuff
, fmt
, ap
);
2458 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
2465 void beep(int errorbeep
) {
2466 static long last_beep
= 0;
2467 long now
, beep_diff
;
2469 now
= GetTickCount();
2470 beep_diff
= now
-last_beep
;
2472 /* Make sure we only respond to one beep per packet or so */
2473 if (beep_diff
>=0 && beep_diff
<50)
2477 MessageBeep(MB_ICONHAND
);
2481 last_beep
= GetTickCount();