17 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
23 #define IDM_SHOWLOG 0x0010
24 #define IDM_NEWSESS 0x0020
25 #define IDM_DUPSESS 0x0030
26 #define IDM_RECONF 0x0040
27 #define IDM_CLRSB 0x0050
28 #define IDM_RESET 0x0060
29 #define IDM_TEL_AYT 0x0070
30 #define IDM_TEL_BRK 0x0080
31 #define IDM_TEL_SYNCH 0x0090
32 #define IDM_TEL_EC 0x00a0
33 #define IDM_TEL_EL 0x00b0
34 #define IDM_TEL_GA 0x00c0
35 #define IDM_TEL_NOP 0x00d0
36 #define IDM_TEL_ABORT 0x00e0
37 #define IDM_TEL_AO 0x00f0
38 #define IDM_TEL_IP 0x0100
39 #define IDM_TEL_SUSP 0x0110
40 #define IDM_TEL_EOR 0x0120
41 #define IDM_TEL_EOF 0x0130
42 #define IDM_ABOUT 0x0140
43 #define IDM_SAVEDSESS 0x0150
44 #define IDM_COPYALL 0x0160
46 #define IDM_SESSLGP 0x0250 /* log type printable */
47 #define IDM_SESSLGA 0x0260 /* log type all chars */
48 #define IDM_SESSLGE 0x0270 /* log end */
49 #define IDM_SAVED_MIN 0x1000
50 #define IDM_SAVED_MAX 0x2000
52 #define WM_IGNORE_SIZE (WM_XUSER + 1)
53 #define WM_IGNORE_CLIP (WM_XUSER + 2)
55 /* Needed for Chinese support and apparently not always defined. */
57 #define VK_PROCESSKEY 0xE5
60 static LRESULT CALLBACK
WndProc (HWND
, UINT
, WPARAM
, LPARAM
);
61 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
, unsigned char *output
);
62 static void cfgtopalette(void);
63 static void init_palette(void);
64 static void init_fonts(int);
66 static int extra_width
, extra_height
;
68 static int pending_netevent
= 0;
69 static WPARAM pend_netevent_wParam
= 0;
70 static LPARAM pend_netevent_lParam
= 0;
71 static void enact_pending_netevent(void);
73 static time_t last_movement
= 0;
77 #define FONT_UNDERLINE 2
78 #define FONT_BOLDUND 3
80 #define FONT_OEMBOLD 5
81 #define FONT_OEMBOLDUND 6
83 static HFONT fonts
[8];
84 static int font_needs_hand_underlining
;
86 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
94 static COLORREF colours
[NCOLOURS
];
96 static LPLOGPALETTE logpal
;
97 static RGBTRIPLE defpal
[NCOLOURS
];
101 static HBITMAP caretbm
;
103 static int dbltime
, lasttime
, lastact
;
104 static Mouse_Button lastbtn
;
106 static char *window_name
, *icon_name
;
108 static int compose_state
= 0;
110 /* Dummy routine, only required in plink. */
111 void ldisc_update(int echo
, int edit
) {}
113 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
) {
114 static char appname
[] = "PuTTY";
119 int guess_width
, guess_height
;
122 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
124 winsock_ver
= MAKEWORD(1, 1);
125 if (WSAStartup(winsock_ver
, &wsadata
)) {
126 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
127 MB_OK
| MB_ICONEXCLAMATION
);
130 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
131 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
132 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
136 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
139 InitCommonControls();
141 /* Ensure a Maximize setting in Explorer doesn't maximise the
146 * Process the command line.
151 default_protocol
= DEFAULT_PROTOCOL
;
152 default_port
= DEFAULT_PORT
;
153 cfg
.logtype
= LGTYP_NONE
;
155 do_defaults(NULL
, &cfg
);
158 while (*p
&& isspace(*p
)) p
++;
161 * Process command line options first. Yes, this can be
162 * done better, and it will be as soon as I have the
166 char *q
= p
+ strcspn(p
, " \t");
169 tolower(p
[0]) == 's' &&
170 tolower(p
[1]) == 's' &&
171 tolower(p
[2]) == 'h') {
172 default_protocol
= cfg
.protocol
= PROT_SSH
;
173 default_port
= cfg
.port
= 22;
174 } else if (q
== p
+ 7 &&
175 tolower(p
[0]) == 'c' &&
176 tolower(p
[1]) == 'l' &&
177 tolower(p
[2]) == 'e' &&
178 tolower(p
[3]) == 'a' &&
179 tolower(p
[4]) == 'n' &&
180 tolower(p
[5]) == 'u' &&
181 tolower(p
[6]) == 'p') {
183 * `putty -cleanup'. Remove all registry entries
184 * associated with PuTTY, and also find and delete
185 * the random seed file.
188 "This procedure will remove ALL Registry\n"
189 "entries associated with PuTTY, and will\n"
190 "also remove the PuTTY random seed file.\n"
192 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
193 "SESSIONS. Are you really sure you want\n"
196 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
201 p
= q
+ strspn(q
, " \t");
205 * An initial @ means to activate a saved session.
209 while (i
> 1 && isspace(p
[i
-1]))
212 do_defaults (p
+1, &cfg
);
213 if (!*cfg
.host
&& !do_config()) {
217 } else if (*p
== '&') {
219 * An initial & means we've been given a command line
220 * containing the hex value of a HANDLE for a file
221 * mapping object, which we must then extract as a
226 if (sscanf(p
+1, "%p", &filemap
) == 1 &&
227 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
228 0, 0, sizeof(Config
))) != NULL
) {
231 CloseHandle(filemap
);
232 } else if (!do_config()) {
239 * If the hostname starts with "telnet:", set the
240 * protocol to Telnet and process the string as a
243 if (!strncmp(q
, "telnet:", 7)) {
247 if (q
[0] == '/' && q
[1] == '/')
249 cfg
.protocol
= PROT_TELNET
;
251 while (*p
&& *p
!= ':' && *p
!= '/') p
++;
259 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
260 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
262 while (*p
&& !isspace(*p
)) p
++;
265 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
266 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
267 while (*p
&& isspace(*p
)) p
++;
280 /* See if host is of the form user@host */
281 if (cfg
.host
[0] != '\0') {
282 char *atsign
= strchr(cfg
.host
, '@');
283 /* Make sure we're not overflowing the user field */
285 if (atsign
-cfg
.host
< sizeof cfg
.username
) {
286 strncpy (cfg
.username
, cfg
.host
, atsign
-cfg
.host
);
287 cfg
.username
[atsign
-cfg
.host
] = '\0';
289 memmove(cfg
.host
, atsign
+1, 1+strlen(atsign
+1));
295 * Select protocol. This is farmed out into a table in a
296 * separate file to enable an ssh-free variant.
301 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
302 if (backends
[i
].protocol
== cfg
.protocol
) {
303 back
= backends
[i
].backend
;
307 MessageBox(NULL
, "Unsupported protocol number found",
308 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
314 /* Check for invalid Port number (i.e. zero) */
316 MessageBox(NULL
, "Invalid Port Number",
317 "PuTTY Internal Error", MB_OK
|MB_ICONEXCLAMATION
);
324 wndclass
.lpfnWndProc
= WndProc
;
325 wndclass
.cbClsExtra
= 0;
326 wndclass
.cbWndExtra
= 0;
327 wndclass
.hInstance
= inst
;
328 wndclass
.hIcon
= LoadIcon (inst
,
329 MAKEINTRESOURCE(IDI_MAINICON
));
330 wndclass
.hCursor
= LoadCursor (NULL
, IDC_IBEAM
);
331 wndclass
.hbrBackground
= GetStockObject (BLACK_BRUSH
);
332 wndclass
.lpszMenuName
= NULL
;
333 wndclass
.lpszClassName
= appname
;
335 RegisterClass (&wndclass
);
340 savelines
= cfg
.savelines
;
346 * Guess some defaults for the window size. This all gets
347 * updated later, so we don't really care too much. However, we
348 * do want the font width/height guesses to correspond to a
349 * large font rather than a small one...
356 term_size (cfg
.height
, cfg
.width
, cfg
.savelines
);
357 guess_width
= extra_width
+ font_width
* cols
;
358 guess_height
= extra_height
+ font_height
* rows
;
361 HWND w
= GetDesktopWindow();
362 GetWindowRect (w
, &r
);
363 if (guess_width
> r
.right
- r
.left
)
364 guess_width
= r
.right
- r
.left
;
365 if (guess_height
> r
.bottom
- r
.top
)
366 guess_height
= r
.bottom
- r
.top
;
370 int winmode
= WS_OVERLAPPEDWINDOW
|WS_VSCROLL
;
372 if (!cfg
.scrollbar
) winmode
&= ~(WS_VSCROLL
);
373 if (cfg
.locksize
) winmode
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
374 if (cfg
.alwaysontop
) exwinmode
|= WS_EX_TOPMOST
;
375 if (cfg
.sunken_edge
) exwinmode
|= WS_EX_CLIENTEDGE
;
376 hwnd
= CreateWindowEx (exwinmode
, appname
, appname
,
377 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
378 guess_width
, guess_height
,
379 NULL
, NULL
, inst
, NULL
);
383 * Initialise the fonts, simultaneously correcting the guesses
384 * for font_{width,height}.
386 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
391 * Correct the guesses for extra_{width,height}.
395 GetWindowRect (hwnd
, &wr
);
396 GetClientRect (hwnd
, &cr
);
397 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
398 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
402 * Resize the window, now we know what size we _really_ want it
405 guess_width
= extra_width
+ font_width
* cols
;
406 guess_height
= extra_height
+ font_height
* rows
;
407 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
408 SetWindowPos (hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
409 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
412 * Set up a caret bitmap, with no content.
416 int size
= (font_width
+15)/16 * 2 * font_height
;
417 bits
= smalloc(size
);
418 memset(bits
, 0, size
);
419 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
422 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
425 * Initialise the scroll bar.
430 si
.cbSize
= sizeof(si
);
431 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
436 SetScrollInfo (hwnd
, SB_VERT
, &si
, FALSE
);
440 * Start up the telnet connection.
444 char msg
[1024], *title
;
447 error
= back
->init (cfg
.host
, cfg
.port
, &realhost
);
449 sprintf(msg
, "Unable to open connection to\n"
451 "%s", cfg
.host
, error
);
452 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
455 window_name
= icon_name
= NULL
;
457 title
= cfg
.wintitle
;
459 sprintf(msg
, "%s - PuTTY", realhost
);
466 session_closed
= FALSE
;
469 * Set up the input and output buffers.
472 outbuf_reap
= outbuf_head
= 0;
475 * Prepare the mouse handler.
477 lastact
= MA_NOTHING
;
478 lastbtn
= MB_NOTHING
;
479 dbltime
= GetDoubleClickTime();
482 * Set up the session-control options on the system menu.
485 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
489 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
490 if (cfg
.protocol
== PROT_TELNET
) {
492 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
493 AppendMenu (p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
494 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
495 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
496 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
497 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
498 AppendMenu (p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
499 AppendMenu (p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
500 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
501 AppendMenu (p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
502 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
503 AppendMenu (p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
504 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
505 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
506 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
507 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
508 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
, "Telnet Command");
509 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
511 AppendMenu (m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
512 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
513 AppendMenu (m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
514 AppendMenu (m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
517 for (i
= 1 ; i
< ((nsessions
< 256) ? nsessions
: 256) ; i
++)
518 AppendMenu (s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
) , sessions
[i
]);
519 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
520 AppendMenu (m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
521 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
522 AppendMenu (m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
523 AppendMenu (m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
524 AppendMenu (m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
525 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
526 AppendMenu (m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
530 * Finally show the window!
532 ShowWindow (hwnd
, show
);
535 * Open the initial log file if there is one.
540 * Set the palette up.
546 has_focus
= (GetForegroundWindow() == hwnd
);
549 if (GetMessage (&msg
, NULL
, 0, 0) == 1)
551 int timer_id
= 0, long_timer
= 0;
553 while (msg
.message
!= WM_QUIT
) {
554 /* Sometimes DispatchMessage calls routines that use their own
555 * GetMessage loop, setup this timer so we get some control back.
557 * Also call term_update() from the timer so that if the host
558 * is sending data flat out we still do redraws.
560 if(timer_id
&& long_timer
) {
561 KillTimer(hwnd
, timer_id
);
562 long_timer
= timer_id
= 0;
565 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
566 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
567 DispatchMessage (&msg
);
569 /* Make sure we blink everything that needs it. */
572 /* Send the paste buffer if there's anything to send */
575 /* If there's nothing new in the queue then we can do everything
576 * we've delayed, reading the socket, writing, and repainting
579 if (PeekMessage (&msg
, NULL
, 0, 0, PM_REMOVE
))
582 if (pending_netevent
) {
583 enact_pending_netevent();
585 /* Force the cursor blink on */
588 if (PeekMessage (&msg
, NULL
, 0, 0, PM_REMOVE
))
592 /* Okay there is now nothing to do so we make sure the screen is
593 * completely up to date then tell windows to call us in a little
597 KillTimer(hwnd
, timer_id
);
606 /* Hmm, term_update didn't want to do an update too soon ... */
607 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
609 timer_id
= SetTimer(hwnd
, 1, 2000, NULL
);
611 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
614 /* There's no point rescanning everything in the message queue
615 * so we do an apparently unnecessary wait here
618 if (GetMessage (&msg
, NULL
, 0, 0) != 1)
630 DeleteObject(fonts
[i
]);
637 if (cfg
.protocol
== PROT_SSH
) {
648 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
650 char *do_select(SOCKET skt
, int startup
) {
654 events
= FD_READ
| FD_WRITE
| FD_OOB
| FD_CLOSE
;
659 return "do_select(): internal error (hwnd==NULL)";
660 if (WSAAsyncSelect (skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
661 switch (WSAGetLastError()) {
662 case WSAENETDOWN
: return "Network is down";
663 default: return "WSAAsyncSelect(): unknown error";
670 * Print a message box and close the connection.
672 void connection_fatal(char *fmt
, ...) {
677 vsprintf(stuff
, fmt
, ap
);
679 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
680 if (cfg
.close_on_exit
== COE_ALWAYS
)
683 session_closed
= TRUE
;
684 SetWindowText (hwnd
, "PuTTY (inactive)");
689 * Actually do the job requested by a WM_NETEVENT
691 static void enact_pending_netevent(void) {
692 static int reentering
= 0;
693 extern int select_result(WPARAM
, LPARAM
);
697 return; /* don't unpend the pending */
699 pending_netevent
= FALSE
;
702 ret
= select_result (pend_netevent_wParam
, pend_netevent_lParam
);
705 if (ret
== 0 && !session_closed
) {
706 /* Abnormal exits will already have set session_closed and taken
707 * appropriate action. */
708 if (cfg
.close_on_exit
== COE_ALWAYS
||
709 cfg
.close_on_exit
== COE_NORMAL
)
712 session_closed
= TRUE
;
713 SetWindowText (hwnd
, "PuTTY (inactive)");
714 MessageBox(hwnd
, "Connection closed by remote host",
715 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
721 * Copy the colour palette from the configuration data into defpal.
722 * This is non-trivial because the colour indices are different.
724 static void cfgtopalette(void) {
726 static const int ww
[] = {
727 6, 7, 8, 9, 10, 11, 12, 13,
728 14, 15, 16, 17, 18, 19, 20, 21,
729 0, 1, 2, 3, 4, 4, 5, 5
732 for (i
=0; i
<24; i
++) {
734 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
735 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
736 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
741 * Set up the colour palette.
743 static void init_palette(void) {
745 HDC hdc
= GetDC (hwnd
);
747 if (cfg
.try_palette
&&
748 GetDeviceCaps (hdc
, RASTERCAPS
) & RC_PALETTE
) {
749 logpal
= smalloc(sizeof(*logpal
)
750 - sizeof(logpal
->palPalEntry
)
751 + NCOLOURS
* sizeof(PALETTEENTRY
));
752 logpal
->palVersion
= 0x300;
753 logpal
->palNumEntries
= NCOLOURS
;
754 for (i
= 0; i
< NCOLOURS
; i
++) {
755 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
756 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
757 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
758 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
760 pal
= CreatePalette (logpal
);
762 SelectPalette (hdc
, pal
, FALSE
);
763 RealizePalette (hdc
);
764 SelectPalette (hdc
, GetStockObject (DEFAULT_PALETTE
),
768 ReleaseDC (hwnd
, hdc
);
771 for (i
=0; i
<NCOLOURS
; i
++)
772 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
776 for(i
=0; i
<NCOLOURS
; i
++)
777 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
783 * Initialise all the fonts we will need. There may be as many as
784 * eight or as few as one. We also:
786 * - check the font width and height, correcting our guesses if
789 * - verify that the bold font is the same width as the ordinary
790 * one, and engage shadow bolding if not.
792 * - verify that the underlined font is the same width as the
793 * ordinary one (manual underlining by means of line drawing can
794 * be done in a pinch).
796 static void init_fonts(int pick_width
) {
801 int fw_dontcare
, fw_bold
;
810 if (cfg
.fontisbold
) {
811 fw_dontcare
= FW_BOLD
;
814 fw_dontcare
= FW_DONTCARE
;
820 font_height
= cfg
.fontheight
;
821 if (font_height
> 0) {
822 font_height
= -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
824 font_width
= pick_width
;
827 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
828 c, OUT_DEFAULT_PRECIS, \
829 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
830 FIXED_PITCH | FF_DONTCARE, cfg.font)
832 if (cfg
.vtmode
!= VT_OEMONLY
) {
833 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
835 SelectObject (hdc
, fonts
[FONT_NORMAL
]);
836 GetTextMetrics(hdc
, &tm
);
837 font_height
= tm
.tmHeight
;
838 font_width
= tm
.tmAveCharWidth
;
840 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
843 * Some fonts, e.g. 9-pt Courier, draw their underlines
844 * outside their character cell. We successfully prevent
845 * screen corruption by clipping the text output, but then
846 * we lose the underline completely. Here we try to work
847 * out whether this is such a font, and if it is, we set a
848 * flag that causes underlines to be drawn by hand.
850 * Having tried other more sophisticated approaches (such
851 * as examining the TEXTMETRIC structure or requesting the
852 * height of a string), I think we'll do this the brute
853 * force way: we create a small bitmap, draw an underlined
854 * space on it, and test to see whether any pixels are
855 * foreground-coloured. (Since we expect the underline to
856 * go all the way across the character cell, we only search
857 * down a single column of the bitmap, half way across.)
861 HBITMAP und_bm
, und_oldbm
;
865 und_dc
= CreateCompatibleDC(hdc
);
866 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
867 und_oldbm
= SelectObject(und_dc
, und_bm
);
868 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
869 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
870 SetTextColor (und_dc
, RGB(255,255,255));
871 SetBkColor (und_dc
, RGB(0,0,0));
872 SetBkMode (und_dc
, OPAQUE
);
873 ExtTextOut (und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
875 for (i
= 0; i
< font_height
; i
++) {
876 c
= GetPixel(und_dc
, font_width
/2, i
);
880 SelectObject(und_dc
, und_oldbm
);
881 DeleteObject(und_bm
);
883 font_needs_hand_underlining
= !gotit
;
886 if (bold_mode
== BOLD_FONT
) {
887 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
888 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
891 if (cfg
.vtmode
== VT_OEMANSI
) {
892 f(FONT_OEM
, OEM_CHARSET
, fw_dontcare
, FALSE
);
893 f(FONT_OEMUND
, OEM_CHARSET
, fw_dontcare
, TRUE
);
895 if (bold_mode
== BOLD_FONT
) {
896 f(FONT_OEMBOLD
, OEM_CHARSET
, fw_bold
, FALSE
);
897 f(FONT_OEMBOLDUND
, OEM_CHARSET
, fw_bold
, TRUE
);
903 f(FONT_OEM
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
905 SelectObject (hdc
, fonts
[FONT_OEM
]);
906 GetTextMetrics(hdc
, &tm
);
907 font_height
= tm
.tmHeight
;
908 font_width
= tm
.tmAveCharWidth
;
910 f(FONT_OEMUND
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
912 if (bold_mode
== BOLD_FONT
) {
913 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
914 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
919 descent
= tm
.tmAscent
+ 1;
920 if (descent
>= font_height
)
921 descent
= font_height
- 1;
922 firstchar
= tm
.tmFirstChar
;
924 for (i
=0; i
<8; i
++) {
926 if (SelectObject (hdc
, fonts
[i
]) &&
927 GetTextMetrics(hdc
, &tm
) )
928 fsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
934 ReleaseDC (hwnd
, hdc
);
936 /* ... This is wrong in OEM only mode */
937 if (fsize
[FONT_UNDERLINE
] != fsize
[FONT_NORMAL
] ||
938 (bold_mode
== BOLD_FONT
&&
939 fsize
[FONT_BOLDUND
] != fsize
[FONT_BOLD
])) {
941 DeleteObject (fonts
[FONT_UNDERLINE
]);
942 if (bold_mode
== BOLD_FONT
)
943 DeleteObject (fonts
[FONT_BOLDUND
]);
946 if (bold_mode
== BOLD_FONT
&&
947 fsize
[FONT_BOLD
] != fsize
[FONT_NORMAL
]) {
948 bold_mode
= BOLD_SHADOW
;
949 DeleteObject (fonts
[FONT_BOLD
]);
950 if (und_mode
== UND_FONT
)
951 DeleteObject (fonts
[FONT_BOLDUND
]);
955 /* With the fascist font painting it doesn't matter if the linedraw font
956 * isn't exactly the right size anymore so we don't have to check this.
958 if (cfg
.vtmode
== VT_OEMANSI
&& fsize
[FONT_OEM
] != fsize
[FONT_NORMAL
] ) {
959 if( cfg
.fontcharset
== OEM_CHARSET
)
961 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
962 "different sizes. Using OEM-only mode instead",
963 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
964 cfg
.vtmode
= VT_OEMONLY
;
966 else if( firstchar
< ' ' )
968 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
969 "different sizes. Using XTerm mode instead",
970 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
971 cfg
.vtmode
= VT_XWINDOWS
;
975 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
976 "different sizes. Using ISO8859-1 mode instead",
977 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
978 cfg
.vtmode
= VT_POORMAN
;
983 DeleteObject (fonts
[i
]);
989 void request_resize (int w
, int h
, int refont
) {
992 /* If the window is maximized supress resizing attempts */
993 if(IsZoomed(hwnd
)) return;
996 /* Don't do this in OEMANSI, you may get disable messages */
997 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132)
998 && cfg
.vtmode
!= VT_OEMANSI
)
1000 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132))
1003 /* If font width too big for screen should we shrink the font more ? */
1005 font_width
= ((font_width
*cols
+w
/2)/w
);
1012 DeleteObject(fonts
[i
]);
1014 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1015 und_mode
= UND_FONT
;
1016 init_fonts(font_width
);
1020 static int first_time
= 1;
1026 /* Get the size of the screen */
1027 if (GetClientRect(GetDesktopWindow(),&ss
))
1028 /* first_time = 0 */;
1029 else { first_time
= 2; break; }
1031 /* Make sure the values are sane */
1032 width
= (ss
.right
-ss
.left
-extra_width
) / font_width
;
1033 height
= (ss
.bottom
-ss
.top
-extra_height
) / font_height
;
1035 if (w
>width
) w
=width
;
1036 if (h
>height
) h
=height
;
1042 width
= extra_width
+ font_width
* w
;
1043 height
= extra_height
+ font_height
* h
;
1045 SetWindowPos (hwnd
, NULL
, 0, 0, width
, height
,
1046 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1047 SWP_NOMOVE
| SWP_NOZORDER
);
1050 static void click (Mouse_Button b
, int x
, int y
) {
1051 int thistime
= GetMessageTime();
1053 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1054 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1055 lastact
== MA_2CLK ? MA_3CLK
:
1056 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1061 if (lastact
!= MA_NOTHING
)
1062 term_mouse (b
, lastact
, x
, y
);
1063 lasttime
= thistime
;
1066 static void show_mouseptr(int show
) {
1067 static int cursor_visible
= 1;
1068 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1070 if (cursor_visible
&& !show
)
1072 else if (!cursor_visible
&& show
)
1074 cursor_visible
= show
;
1077 static LRESULT CALLBACK
WndProc (HWND hwnd
, UINT message
,
1078 WPARAM wParam
, LPARAM lParam
) {
1080 static int ignore_size
= FALSE
;
1081 static int ignore_clip
= FALSE
;
1082 static int just_reconfigged
= FALSE
;
1083 static int resizing
= FALSE
;
1084 static int need_backend_resize
= FALSE
;
1088 if (pending_netevent
)
1089 enact_pending_netevent();
1096 if (cfg
.ping_interval
> 0)
1100 if (now
-last_movement
> cfg
.ping_interval
)
1102 back
->special(TS_PING
);
1103 last_movement
= now
;
1111 if (!cfg
.warn_on_close
|| session_closed
||
1112 MessageBox(hwnd
, "Are you sure you want to close this session?",
1113 "PuTTY Exit Confirmation",
1114 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1115 DestroyWindow(hwnd
);
1119 PostQuitMessage (0);
1122 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1134 PROCESS_INFORMATION pi
;
1135 HANDLE filemap
= NULL
;
1137 if (wParam
== IDM_DUPSESS
) {
1139 * Allocate a file-mapping memory chunk for the
1142 SECURITY_ATTRIBUTES sa
;
1145 sa
.nLength
= sizeof(sa
);
1146 sa
.lpSecurityDescriptor
= NULL
;
1147 sa
.bInheritHandle
= TRUE
;
1148 filemap
= CreateFileMapping((HANDLE
)0xFFFFFFFF,
1155 p
= (Config
*)MapViewOfFile(filemap
,
1157 0, 0, sizeof(Config
));
1159 *p
= cfg
; /* structure copy */
1163 sprintf(c
, "putty &%p", filemap
);
1165 } else if (wParam
== IDM_SAVEDSESS
) {
1166 char *session
= sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1167 cl
= smalloc(16 + strlen(session
)); /* 8, but play safe */
1169 cl
= NULL
; /* not a very important failure mode */
1171 sprintf(cl
, "putty @%s", session
);
1177 GetModuleFileName (NULL
, b
, sizeof(b
)-1);
1179 si
.lpReserved
= NULL
;
1180 si
.lpDesktop
= NULL
;
1184 si
.lpReserved2
= NULL
;
1185 CreateProcess (b
, cl
, NULL
, NULL
, TRUE
,
1186 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1189 CloseHandle(filemap
);
1196 int prev_alwaysontop
= cfg
.alwaysontop
;
1197 int prev_sunken_edge
= cfg
.sunken_edge
;
1198 char oldlogfile
[FILENAME_MAX
];
1200 int need_setwpos
= FALSE
;
1201 int old_fwidth
, old_fheight
;
1203 strcpy(oldlogfile
, cfg
.logfilename
);
1204 oldlogtype
= cfg
.logtype
;
1207 old_fwidth
= font_width
;
1208 old_fheight
= font_height
;
1209 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1211 if (!do_reconfig(hwnd
))
1214 if (strcmp(oldlogfile
, cfg
.logfilename
) ||
1215 oldlogtype
!= cfg
.logtype
) {
1216 logfclose(); /* reset logging */
1220 just_reconfigged
= TRUE
;
1225 DeleteObject(fonts
[i
]);
1227 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1228 und_mode
= UND_FONT
;
1232 * Flush the line discipline's edit buffer in the
1233 * case where local editing has just been disabled.
1235 ldisc_send(NULL
, 0);
1243 /* Enable or disable the scroll bar, etc */
1245 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1246 LONG nexflag
, exflag
= GetWindowLong(hwnd
, GWL_EXSTYLE
);
1249 if (cfg
.alwaysontop
!= prev_alwaysontop
) {
1250 if (cfg
.alwaysontop
) {
1251 nexflag
|= WS_EX_TOPMOST
;
1252 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1253 SWP_NOMOVE
| SWP_NOSIZE
);
1255 nexflag
&= ~(WS_EX_TOPMOST
);
1256 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1257 SWP_NOMOVE
| SWP_NOSIZE
);
1260 if (cfg
.sunken_edge
)
1261 nexflag
|= WS_EX_CLIENTEDGE
;
1263 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1266 if (cfg
.scrollbar
) nflg
|= WS_VSCROLL
;
1267 else nflg
&= ~WS_VSCROLL
;
1269 nflg
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1271 nflg
|= (WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1273 if (nflg
!= flag
|| nexflag
!= exflag
)
1278 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1279 if (nexflag
!= exflag
)
1280 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1282 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1284 SetWindowPos(hwnd
, NULL
, 0,0,0,0,
1285 SWP_NOACTIVATE
|SWP_NOCOPYBITS
|
1286 SWP_NOMOVE
|SWP_NOSIZE
|SWP_NOZORDER
|
1289 GetWindowRect (hwnd
, &wr
);
1290 GetClientRect (hwnd
, &cr
);
1291 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1292 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1296 if (cfg
.height
!= rows
||
1297 cfg
.width
!= cols
||
1298 old_fwidth
!= font_width
||
1299 old_fheight
!= font_height
||
1300 cfg
.savelines
!= savelines
||
1301 cfg
.sunken_edge
!= prev_sunken_edge
)
1302 need_setwpos
= TRUE
;
1303 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1304 InvalidateRect(hwnd
, NULL
, TRUE
);
1307 SetWindowPos (hwnd
, NULL
, 0, 0,
1308 extra_width
+ font_width
* cfg
.width
,
1309 extra_height
+ font_height
* cfg
.height
,
1310 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1311 SWP_NOMOVE
| SWP_NOZORDER
);
1313 set_title(cfg
.wintitle
);
1314 if (IsIconic(hwnd
)) {
1315 SetWindowText (hwnd
,
1316 cfg
.win_name_always ? window_name
: icon_name
);
1329 case IDM_TEL_AYT
: back
->special (TS_AYT
); break;
1330 case IDM_TEL_BRK
: back
->special (TS_BRK
); break;
1331 case IDM_TEL_SYNCH
: back
->special (TS_SYNCH
); break;
1332 case IDM_TEL_EC
: back
->special (TS_EC
); break;
1333 case IDM_TEL_EL
: back
->special (TS_EL
); break;
1334 case IDM_TEL_GA
: back
->special (TS_GA
); break;
1335 case IDM_TEL_NOP
: back
->special (TS_NOP
); break;
1336 case IDM_TEL_ABORT
: back
->special (TS_ABORT
); break;
1337 case IDM_TEL_AO
: back
->special (TS_AO
); break;
1338 case IDM_TEL_IP
: back
->special (TS_IP
); break;
1339 case IDM_TEL_SUSP
: back
->special (TS_SUSP
); break;
1340 case IDM_TEL_EOR
: back
->special (TS_EOR
); break;
1341 case IDM_TEL_EOF
: back
->special (TS_EOF
); break;
1346 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1347 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1352 #define X_POS(l) ((int)(short)LOWORD(l))
1353 #define Y_POS(l) ((int)(short)HIWORD(l))
1355 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1356 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1358 case WM_LBUTTONDOWN
:
1360 click (MB_SELECT
, TO_CHR_X(X_POS(lParam
)),
1361 TO_CHR_Y(Y_POS(lParam
)));
1366 term_mouse (MB_SELECT
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1367 TO_CHR_Y(Y_POS(lParam
)));
1370 case WM_MBUTTONDOWN
:
1373 click (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1374 TO_CHR_X(X_POS(lParam
)),
1375 TO_CHR_Y(Y_POS(lParam
)));
1379 term_mouse (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1380 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1381 TO_CHR_Y(Y_POS(lParam
)));
1384 case WM_RBUTTONDOWN
:
1387 click (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1388 TO_CHR_X(X_POS(lParam
)),
1389 TO_CHR_Y(Y_POS(lParam
)));
1393 term_mouse (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1394 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1395 TO_CHR_Y(Y_POS(lParam
)));
1401 * Add the mouse position and message time to the random
1404 noise_ultralight(lParam
);
1406 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1408 if (wParam
& MK_LBUTTON
)
1410 else if (wParam
& MK_MBUTTON
)
1411 b
= cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
;
1413 b
= cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
;
1414 term_mouse (b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1415 TO_CHR_Y(Y_POS(lParam
)));
1418 case WM_NCMOUSEMOVE
:
1420 noise_ultralight(lParam
);
1422 case WM_IGNORE_CLIP
:
1423 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1425 case WM_DESTROYCLIPBOARD
:
1428 ignore_clip
= FALSE
;
1434 hdc
= BeginPaint (hwnd
, &p
);
1436 SelectPalette (hdc
, pal
, TRUE
);
1437 RealizePalette (hdc
);
1439 term_paint (hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1440 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1441 SelectObject (hdc
, GetStockObject(SYSTEM_FONT
));
1442 SelectObject (hdc
, GetStockObject(WHITE_PEN
));
1443 EndPaint (hwnd
, &p
);
1448 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1449 * but the only one that's likely to try to overload us is FD_READ.
1450 * This means buffering just one is fine.
1452 if (pending_netevent
)
1453 enact_pending_netevent();
1455 pending_netevent
= TRUE
;
1456 pend_netevent_wParam
=wParam
;
1457 pend_netevent_lParam
=lParam
;
1458 time(&last_movement
);
1462 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
1475 case WM_IGNORE_SIZE
:
1476 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1478 case WM_ENTERSIZEMOVE
:
1481 need_backend_resize
= FALSE
;
1483 case WM_EXITSIZEMOVE
:
1486 if (need_backend_resize
)
1491 int width
, height
, w
, h
, ew
, eh
;
1492 LPRECT r
= (LPRECT
)lParam
;
1494 width
= r
->right
- r
->left
- extra_width
;
1495 height
= r
->bottom
- r
->top
- extra_height
;
1496 w
= (width
+ font_width
/2) / font_width
; if (w
< 1) w
= 1;
1497 h
= (height
+ font_height
/2) / font_height
; if (h
< 1) h
= 1;
1498 UpdateSizeTip(hwnd
, w
, h
);
1499 ew
= width
- w
* font_width
;
1500 eh
= height
- h
* font_height
;
1502 if (wParam
== WMSZ_LEFT
||
1503 wParam
== WMSZ_BOTTOMLEFT
||
1504 wParam
== WMSZ_TOPLEFT
)
1510 if (wParam
== WMSZ_TOP
||
1511 wParam
== WMSZ_TOPRIGHT
||
1512 wParam
== WMSZ_TOPLEFT
)
1522 /* break; (never reached) */
1524 if (wParam
== SIZE_MINIMIZED
) {
1525 SetWindowText (hwnd
,
1526 cfg
.win_name_always ? window_name
: icon_name
);
1529 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1530 SetWindowText (hwnd
, window_name
);
1532 int width
, height
, w
, h
;
1533 #if 0 /* we have fixed this using WM_SIZING now */
1537 width
= LOWORD(lParam
);
1538 height
= HIWORD(lParam
);
1539 w
= width
/ font_width
; if (w
< 1) w
= 1;
1540 h
= height
/ font_height
; if (h
< 1) h
= 1;
1541 #if 0 /* we have fixed this using WM_SIZING now */
1542 ew
= width
- w
* font_width
;
1543 eh
= height
- h
* font_height
;
1544 if (ew
!= 0 || eh
!= 0) {
1546 GetWindowRect (hwnd
, &r
);
1547 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1548 SetWindowPos (hwnd
, NULL
, 0, 0,
1549 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1550 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1553 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1555 term_size (h
, w
, cfg
.savelines
);
1557 * Don't call back->size in mid-resize. (To prevent
1558 * massive numbers of resize events getting sent
1559 * down the connection during an NT opaque drag.)
1564 need_backend_resize
= TRUE
;
1565 just_reconfigged
= FALSE
;
1568 ignore_size
= FALSE
;
1571 switch (LOWORD(wParam
)) {
1572 case SB_BOTTOM
: term_scroll(-1, 0); break;
1573 case SB_TOP
: term_scroll(+1, 0); break;
1574 case SB_LINEDOWN
: term_scroll (0, +1); break;
1575 case SB_LINEUP
: term_scroll (0, -1); break;
1576 case SB_PAGEDOWN
: term_scroll (0, +rows
/2); break;
1577 case SB_PAGEUP
: term_scroll (0, -rows
/2); break;
1578 case SB_THUMBPOSITION
: case SB_THUMBTRACK
:
1579 term_scroll (1, HIWORD(wParam
)); break;
1582 case WM_PALETTECHANGED
:
1583 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1584 HDC hdc
= get_ctx();
1586 if (RealizePalette (hdc
) > 0)
1592 case WM_QUERYNEWPALETTE
:
1594 HDC hdc
= get_ctx();
1596 if (RealizePalette (hdc
) > 0)
1608 * Add the scan code and keypress timing to the random
1611 noise_ultralight(lParam
);
1614 * We don't do TranslateMessage since it disassociates the
1615 * resulting CHAR message from the KEYDOWN that sparked it,
1616 * which we occasionally don't want. Instead, we process
1617 * KEYDOWN, and call the Win32 translator functions so that
1618 * we get the translations under _our_ control.
1621 unsigned char buf
[20];
1624 if (wParam
==VK_PROCESSKEY
) {
1627 m
.message
= WM_KEYDOWN
;
1629 m
.lParam
= lParam
& 0xdfff;
1630 TranslateMessage(&m
);
1632 len
= TranslateKey (message
, wParam
, lParam
, buf
);
1634 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1635 ldisc_send (buf
, len
);
1644 unsigned char buf
[2];
1647 buf
[0] = wParam
>> 8;
1648 ldisc_send (buf
, 2);
1653 * Nevertheless, we are prepared to deal with WM_CHAR
1654 * messages, should they crop up. So if someone wants to
1655 * post the things to us as part of a macro manoeuvre,
1656 * we're ready to cope.
1659 char c
= xlat_kbd2tty((unsigned char)wParam
);
1665 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1669 * Move the system caret. (We maintain one, even though it's
1670 * invisible, for the benefit of blind people: apparently some
1671 * helper software tracks the system caret, so we should arrange to
1674 void sys_cursor(int x
, int y
) {
1676 SetCaretPos(x
* font_width
, y
* font_height
);
1680 * Draw a line of text in the window, at given character
1681 * coordinates, in given attributes.
1683 * We are allowed to fiddle with the contents of `text'.
1685 void do_text (Context ctx
, int x
, int y
, char *text
, int len
,
1686 unsigned long attr
, int lattr
) {
1688 int nfg
, nbg
, nfont
;
1691 int force_manual_underline
= 0;
1692 int fnt_width
= font_width
*(1+(lattr
!=LATTR_NORM
));
1693 static int *IpDx
= 0, IpDxLEN
= 0;;
1695 if (len
>IpDxLEN
|| IpDx
[0] != fnt_width
) {
1699 IpDx
= smalloc((len
+16)*sizeof(int));
1702 for(i
=0; i
<IpDxLEN
; i
++)
1703 IpDx
[i
] = fnt_width
;
1709 if ((attr
& ATTR_ACTCURS
) && cfg
.cursor_type
== 0) {
1710 attr
&= (bold_mode
== BOLD_COLOURS ?
0x300200 : 0x300300);
1711 attr
^= ATTR_CUR_XOR
;
1715 if (cfg
.vtmode
== VT_OEMONLY
)
1719 * Map high-half characters in order to approximate ISO using
1720 * OEM character set. No characters are missing if the OEM codepage
1723 if (nfont
& FONT_OEM
) {
1725 for (i
=0; i
<len
; i
++)
1726 if (text
[i
] >= '\xA0' && text
[i
] <= '\xFF') {
1728 /* This is CP850 ... perfect translation */
1729 static const char oemhighhalf
[] =
1730 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1731 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1732 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1733 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1734 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1735 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1736 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1737 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1738 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1739 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1740 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1741 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1744 /* This is CP437 ... junk translation */
1745 static const unsigned char oemhighhalf
[] = {
1746 0x20, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1747 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1748 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1749 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1750 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1751 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1752 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1753 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1754 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1755 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1756 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1757 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1760 text
[i
] = oemhighhalf
[(unsigned char)text
[i
] - 0xA0];
1764 if (attr
& ATTR_LINEDRW
) {
1767 static const char poorman
[] =
1768 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1771 static const char oemmap_437
[] =
1772 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1773 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1776 static const char oemmap_850
[] =
1777 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1778 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1780 /* Poor windows font ... eg: windows courier */
1781 static const char oemmap
[] =
1782 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1783 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1786 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1787 * VT100 line drawing chars; everything else stays normal.
1789 * Actually '_' maps to space too, but that's done before.
1791 switch (cfg
.vtmode
) {
1793 for (i
=0; i
<len
; i
++)
1794 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1795 text
[i
] += '\x01' - '\x60';
1798 /* Make sure we actually have an OEM font */
1799 if (fonts
[nfont
|FONT_OEM
]) {
1802 for (i
=0; i
<len
; i
++)
1803 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1804 text
[i
] = oemmap
[(unsigned char)text
[i
] - 0x60];
1808 for (i
=0; i
<len
; i
++)
1809 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1810 text
[i
] = poorman
[(unsigned char)text
[i
] - 0x60];
1815 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
1816 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
1817 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
1819 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
1820 nfont
|= FONT_UNDERLINE
;
1823 if (nfont
&FONT_UNDERLINE
)
1824 force_manual_underline
= 1;
1825 /* Don't do the same for manual bold, it could be bad news. */
1827 nfont
&= ~(FONT_BOLD
|FONT_UNDERLINE
);
1829 if (font_needs_hand_underlining
&& (attr
& ATTR_UNDER
))
1830 force_manual_underline
= 1;
1831 if (attr
& ATTR_REVERSE
) {
1832 t
= nfg
; nfg
= nbg
; nbg
= t
;
1834 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
1836 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
1840 SelectObject (hdc
, fonts
[nfont
]);
1841 SetTextColor (hdc
, fg
);
1842 SetBkColor (hdc
, bg
);
1843 SetBkMode (hdc
, OPAQUE
);
1846 line_box
.right
= x
+fnt_width
*len
;
1847 line_box
.bottom
= y
+font_height
;
1848 ExtTextOut (hdc
, x
, y
, ETO_CLIPPED
|ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
1849 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
1850 SetBkMode (hdc
, TRANSPARENT
);
1852 /* GRR: This draws the character outside it's box and can leave
1853 * 'droppings' even with the clip box! I suppose I could loop it
1854 * one character at a time ... yuk.
1856 * Or ... I could do a test print with "W", and use +1 or -1 for this
1857 * shift depending on if the leftmost column is blank...
1859 ExtTextOut (hdc
, x
-1, y
, ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
1861 if (force_manual_underline
||
1862 (und_mode
== UND_LINE
&& (attr
& ATTR_UNDER
))) {
1864 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, fg
));
1865 MoveToEx (hdc
, x
, y
+descent
, NULL
);
1866 LineTo (hdc
, x
+len
*fnt_width
, y
+descent
);
1867 oldpen
= SelectObject (hdc
, oldpen
);
1868 DeleteObject (oldpen
);
1870 if ((attr
& ATTR_PASCURS
) && cfg
.cursor_type
== 0) {
1873 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
1874 pts
[2].x
= pts
[3].x
= x
+fnt_width
-1;
1875 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
1876 pts
[1].y
= pts
[2].y
= y
+font_height
-1;
1877 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
1878 Polyline (hdc
, pts
, 5);
1879 oldpen
= SelectObject (hdc
, oldpen
);
1880 DeleteObject (oldpen
);
1882 if ((attr
& (ATTR_ACTCURS
| ATTR_PASCURS
)) && cfg
.cursor_type
!= 0) {
1883 int startx
, starty
, dx
, dy
, length
, i
;
1884 if (cfg
.cursor_type
== 1) {
1885 startx
= x
; starty
= y
+descent
;
1886 dx
= 1; dy
= 0; length
= fnt_width
;
1889 if (attr
& ATTR_RIGHTCURS
)
1890 xadjust
= fnt_width
-1;
1891 startx
= x
+xadjust
; starty
= y
;
1892 dx
= 0; dy
= 1; length
= font_height
;
1894 if (attr
& ATTR_ACTCURS
) {
1896 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
1897 MoveToEx (hdc
, startx
, starty
, NULL
);
1898 LineTo (hdc
, startx
+dx
*length
, starty
+dy
*length
);
1899 oldpen
= SelectObject (hdc
, oldpen
);
1900 DeleteObject (oldpen
);
1902 for (i
= 0; i
< length
; i
++) {
1904 SetPixel(hdc
, startx
, starty
, colours
[23]);
1906 startx
+= dx
; starty
+= dy
;
1912 static int check_compose(int first
, int second
) {
1914 static char * composetbl
[] = {
1915 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1916 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1917 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1918 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1919 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1920 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1921 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1922 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1923 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1924 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1925 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1926 "\"uü", "'yý", "htþ", "\"yÿ",
1930 static int recurse
= 0;
1933 for(c
=composetbl
; *c
; c
++) {
1934 if( (*c
)[0] == first
&& (*c
)[1] == second
)
1936 return (*c
)[2] & 0xFF;
1943 nc
= check_compose(second
, first
);
1945 nc
= check_compose(toupper(first
), toupper(second
));
1947 nc
= check_compose(toupper(second
), toupper(first
));
1955 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1956 * codes. Returns number of bytes used or zero to drop the message
1957 * or -1 to forward the message to windows.
1959 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
1960 unsigned char *output
) {
1962 int scan
, left_alt
= 0, key_down
, shift_state
;
1964 unsigned char * p
= output
;
1965 static int alt_state
= 0;
1967 HKL kbd_layout
= GetKeyboardLayout(0);
1969 static WORD keys
[3];
1970 static int compose_char
= 0;
1971 static WPARAM compose_key
= 0;
1973 r
= GetKeyboardState(keystate
);
1974 if (!r
) memset(keystate
, 0, sizeof(keystate
));
1978 { /* Tell us all about key events */
1979 static BYTE oldstate
[256];
1980 static int first
= 1;
1983 if(first
) memcpy(oldstate
, keystate
, sizeof(oldstate
));
1986 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
) {
1988 } else if ((HIWORD(lParam
)&KF_UP
) && scan
==(HIWORD(lParam
) & 0xFF) ) {
1992 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
1993 debug(("K_F%d", wParam
+1-VK_F1
));
1996 case VK_SHIFT
: debug(("SHIFT")); break;
1997 case VK_CONTROL
: debug(("CTRL")); break;
1998 case VK_MENU
: debug(("ALT")); break;
1999 default: debug(("VK_%02x", wParam
));
2001 if(message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2003 debug((", S%02x", scan
=(HIWORD(lParam
) & 0xFF) ));
2005 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2006 if (ch
>=' ' && ch
<='~') debug((", '%c'", ch
));
2007 else if (ch
) debug((", $%02x", ch
));
2009 if (keys
[0]) debug((", KB0=%02x", keys
[0]));
2010 if (keys
[1]) debug((", KB1=%02x", keys
[1]));
2011 if (keys
[2]) debug((", KB2=%02x", keys
[2]));
2013 if ( (keystate
[VK_SHIFT
]&0x80)!=0) debug((", S"));
2014 if ( (keystate
[VK_CONTROL
]&0x80)!=0) debug((", C"));
2015 if ( (HIWORD(lParam
)&KF_EXTENDED
) ) debug((", E"));
2016 if ( (HIWORD(lParam
)&KF_UP
) ) debug((", U"));
2019 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
)
2021 else if ( (HIWORD(lParam
)&KF_UP
) )
2022 oldstate
[wParam
&0xFF] ^= 0x80;
2024 oldstate
[wParam
&0xFF] ^= 0x81;
2026 for(ch
=0; ch
<256; ch
++)
2027 if (oldstate
[ch
] != keystate
[ch
])
2028 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2030 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2034 if (wParam
== VK_MENU
&& (HIWORD(lParam
)&KF_EXTENDED
)) {
2035 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2039 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2040 if ( (cfg
.funky_type
== 3 ||
2041 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2042 && wParam
==VK_NUMLOCK
&& !(keystate
[VK_SHIFT
]&0x80)) {
2044 wParam
= VK_EXECUTE
;
2046 /* UnToggle NUMLock */
2047 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
2048 keystate
[VK_NUMLOCK
] ^= 1;
2051 /* And write back the 'adjusted' state */
2052 SetKeyboardState (keystate
);
2055 /* Disable Auto repeat if required */
2056 if (repeat_off
&& (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
)
2059 if ((HIWORD(lParam
)&KF_ALTDOWN
) && (keystate
[VK_RMENU
]&0x80) == 0)
2062 key_down
= ((HIWORD(lParam
)&KF_UP
)==0);
2064 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2065 if (left_alt
&& (keystate
[VK_CONTROL
]&0x80)) {
2066 if (cfg
.ctrlaltkeys
)
2067 keystate
[VK_MENU
] = 0;
2069 keystate
[VK_RMENU
] = 0x80;
2074 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2075 shift_state
= ((keystate
[VK_SHIFT
]&0x80)!=0)
2076 + ((keystate
[VK_CONTROL
]&0x80)!=0)*2;
2078 /* Note if AltGr was pressed and if it was used as a compose key */
2079 if (!compose_state
) {
2080 compose_key
= 0x100;
2081 if (cfg
.compose_key
) {
2082 if (wParam
== VK_MENU
&& (HIWORD(lParam
)&KF_EXTENDED
))
2083 compose_key
= wParam
;
2085 if (wParam
== VK_APPS
)
2086 compose_key
= wParam
;
2089 if (wParam
== compose_key
)
2091 if (compose_state
== 0 && (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
2093 else if (compose_state
== 1 && (HIWORD(lParam
)&KF_UP
))
2098 else if (compose_state
==1 && wParam
!= VK_CONTROL
)
2102 * Record that we pressed key so the scroll window can be reset, but
2103 * be careful to avoid Shift-UP/Down
2105 if( wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
2109 /* Make sure we're not pasting */
2110 if (key_down
) term_nopaste();
2112 if (compose_state
>1 && left_alt
) compose_state
= 0;
2114 /* Sanitize the number pad if not using a PC NumPad */
2115 if( left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2116 && cfg
.funky_type
!= 2)
2117 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
)
2119 if ((HIWORD(lParam
)&KF_EXTENDED
) == 0)
2124 case VK_INSERT
: nParam
= VK_NUMPAD0
; break;
2125 case VK_END
: nParam
= VK_NUMPAD1
; break;
2126 case VK_DOWN
: nParam
= VK_NUMPAD2
; break;
2127 case VK_NEXT
: nParam
= VK_NUMPAD3
; break;
2128 case VK_LEFT
: nParam
= VK_NUMPAD4
; break;
2129 case VK_CLEAR
: nParam
= VK_NUMPAD5
; break;
2130 case VK_RIGHT
: nParam
= VK_NUMPAD6
; break;
2131 case VK_HOME
: nParam
= VK_NUMPAD7
; break;
2132 case VK_UP
: nParam
= VK_NUMPAD8
; break;
2133 case VK_PRIOR
: nParam
= VK_NUMPAD9
; break;
2134 case VK_DELETE
: nParam
= VK_DECIMAL
; break;
2138 if (keystate
[VK_NUMLOCK
]&1) shift_state
|= 1;
2144 /* If a key is pressed and AltGr is not active */
2145 if (key_down
&& (keystate
[VK_RMENU
]&0x80) == 0 && !compose_state
)
2147 /* Okay, prepare for most alts then ...*/
2148 if (left_alt
) *p
++ = '\033';
2150 /* Lets see if it's a pattern we know all about ... */
2151 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2152 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2155 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2156 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
2159 if (wParam
== VK_INSERT
&& shift_state
== 1) {
2160 term_mouse (MB_PASTE
, MA_CLICK
, 0, 0);
2161 term_mouse (MB_PASTE
, MA_RELEASE
, 0, 0);
2164 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
2167 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
2169 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
2170 SendMessage (hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
2173 /* Control-Numlock for app-keypad mode switch */
2174 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
2175 app_keypad_keys
^= 1;
2179 /* Nethack keypad */
2180 if (cfg
.nethack_keypad
&& !left_alt
) {
2182 case VK_NUMPAD1
: *p
++ = shift_state ?
'B': 'b'; return p
-output
;
2183 case VK_NUMPAD2
: *p
++ = shift_state ?
'J': 'j'; return p
-output
;
2184 case VK_NUMPAD3
: *p
++ = shift_state ?
'N': 'n'; return p
-output
;
2185 case VK_NUMPAD4
: *p
++ = shift_state ?
'H': 'h'; return p
-output
;
2186 case VK_NUMPAD5
: *p
++ = shift_state ?
'.': '.'; return p
-output
;
2187 case VK_NUMPAD6
: *p
++ = shift_state ?
'L': 'l'; return p
-output
;
2188 case VK_NUMPAD7
: *p
++ = shift_state ?
'Y': 'y'; return p
-output
;
2189 case VK_NUMPAD8
: *p
++ = shift_state ?
'K': 'k'; return p
-output
;
2190 case VK_NUMPAD9
: *p
++ = shift_state ?
'U': 'u'; return p
-output
;
2194 /* Application Keypad */
2198 if ( cfg
.funky_type
== 3 ||
2199 ( cfg
.funky_type
<= 1 &&
2200 app_keypad_keys
&& !cfg
.no_applic_k
)) switch(wParam
) {
2201 case VK_EXECUTE
: xkey
= 'P'; break;
2202 case VK_DIVIDE
: xkey
= 'Q'; break;
2203 case VK_MULTIPLY
:xkey
= 'R'; break;
2204 case VK_SUBTRACT
:xkey
= 'S'; break;
2206 if(app_keypad_keys
&& !cfg
.no_applic_k
) switch(wParam
) {
2207 case VK_NUMPAD0
: xkey
= 'p'; break;
2208 case VK_NUMPAD1
: xkey
= 'q'; break;
2209 case VK_NUMPAD2
: xkey
= 'r'; break;
2210 case VK_NUMPAD3
: xkey
= 's'; break;
2211 case VK_NUMPAD4
: xkey
= 't'; break;
2212 case VK_NUMPAD5
: xkey
= 'u'; break;
2213 case VK_NUMPAD6
: xkey
= 'v'; break;
2214 case VK_NUMPAD7
: xkey
= 'w'; break;
2215 case VK_NUMPAD8
: xkey
= 'x'; break;
2216 case VK_NUMPAD9
: xkey
= 'y'; break;
2218 case VK_DECIMAL
: xkey
= 'n'; break;
2219 case VK_ADD
: if(cfg
.funky_type
==2) {
2220 if(shift_state
) xkey
= 'l';
2222 } else if(shift_state
) xkey
= 'm';
2226 case VK_DIVIDE
: if(cfg
.funky_type
==2) xkey
= 'o'; break;
2227 case VK_MULTIPLY
:if(cfg
.funky_type
==2) xkey
= 'j'; break;
2228 case VK_SUBTRACT
:if(cfg
.funky_type
==2) xkey
= 'm'; break;
2231 if (HIWORD(lParam
)&KF_EXTENDED
)
2239 if (xkey
>='P' && xkey
<='S')
2240 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
2242 p
+= sprintf((char *)p
, "\x1B?%c", xkey
);
2245 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
2250 if (wParam
== VK_BACK
&& shift_state
== 0 ) /* Backspace */
2252 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
2255 if (wParam
== VK_TAB
&& shift_state
== 1 ) /* Shift tab */
2257 *p
++ = 0x1B; *p
++ = '['; *p
++ = 'Z'; return p
- output
;
2259 if (wParam
== VK_SPACE
&& shift_state
== 2 ) /* Ctrl-Space */
2261 *p
++ = 0; return p
- output
;
2263 if (wParam
== VK_SPACE
&& shift_state
== 3 ) /* Ctrl-Shift-Space */
2265 *p
++ = 160; return p
- output
;
2267 if (wParam
== VK_CANCEL
&& shift_state
== 2 ) /* Ctrl-Break */
2269 *p
++ = 3; return p
- output
;
2271 if (wParam
== VK_PAUSE
) /* Break/Pause */
2273 *p
++ = 26; *p
++ = 0; return -2;
2275 /* Control-2 to Control-8 are special */
2276 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8')
2278 *p
++ = "\000\033\034\035\036\037\177"[wParam
-'2'];
2281 if (shift_state
== 2 && wParam
== 0xBD) {
2285 if (shift_state
== 2 && wParam
== 0xDF) {
2289 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
2290 *p
++ = '\r'; *p
++ = '\n';
2295 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2296 * for integer decimal nn.)
2298 * We also deal with the weird ones here. Linux VCs replace F1
2299 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2300 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2305 case VK_F1
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11); break;
2306 case VK_F2
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12); break;
2307 case VK_F3
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13); break;
2308 case VK_F4
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14); break;
2309 case VK_F5
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15); break;
2310 case VK_F6
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17); break;
2311 case VK_F7
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18); break;
2312 case VK_F8
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19); break;
2313 case VK_F9
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20); break;
2314 case VK_F10
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21); break;
2315 case VK_F11
: code
= 23; break;
2316 case VK_F12
: code
= 24; break;
2317 case VK_F13
: code
= 25; break;
2318 case VK_F14
: code
= 26; break;
2319 case VK_F15
: code
= 28; break;
2320 case VK_F16
: code
= 29; break;
2321 case VK_F17
: code
= 31; break;
2322 case VK_F18
: code
= 32; break;
2323 case VK_F19
: code
= 33; break;
2324 case VK_F20
: code
= 34; break;
2325 case VK_HOME
: code
= 1; break;
2326 case VK_INSERT
: code
= 2; break;
2327 case VK_DELETE
: code
= 3; break;
2328 case VK_END
: code
= 4; break;
2329 case VK_PRIOR
: code
= 5; break;
2330 case VK_NEXT
: code
= 6; break;
2332 /* Reorder edit keys to physical order */
2333 if (cfg
.funky_type
== 3 && code
<= 6 ) code
= "\0\2\1\4\5\3\6"[code
];
2335 if (vt52_mode
&& code
> 0 && code
<= 6) {
2336 p
+= sprintf((char *)p
, "\x1B%c", " HLMEIG"[code
]);
2340 if (cfg
.funky_type
== 5 && code
>= 11 && code
<= 24) {
2341 p
+= sprintf((char *)p
, "\x1B[%c", code
+ 'M' - 11);
2344 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
2346 if (code
>15) offt
++; if (code
>21) offt
++;
2348 p
+= sprintf((char *)p
, "\x1B%c", code
+ 'P' - 11 - offt
);
2350 p
+= sprintf((char *)p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
2353 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
2354 p
+= sprintf((char *)p
, "\x1B[[%c", code
+ 'A' - 11);
2357 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
2359 p
+= sprintf((char *)p
, "\x1B%c", code
+ 'P' - 11);
2361 p
+= sprintf((char *)p
, "\x1BO%c", code
+ 'P' - 11);
2364 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
2365 p
+= sprintf((char *)p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
2369 p
+= sprintf((char *)p
, "\x1B[%d~", code
);
2374 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2375 * some reason seems to send VK_CLEAR to Windows...).
2380 case VK_UP
: xkey
= 'A'; break;
2381 case VK_DOWN
: xkey
= 'B'; break;
2382 case VK_RIGHT
: xkey
= 'C'; break;
2383 case VK_LEFT
: xkey
= 'D'; break;
2384 case VK_CLEAR
: xkey
= 'G'; break;
2389 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
2390 else if (app_cursor_keys
&& !cfg
.no_applic_c
)
2391 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
2393 p
+= sprintf((char *)p
, "\x1B[%c", xkey
);
2399 * Finally, deal with Return ourselves. (Win95 seems to
2400 * foul it up when Alt is pressed, for some reason.)
2402 if (wParam
== VK_RETURN
) /* Return */
2409 /* Okay we've done everything interesting; let windows deal with
2410 * the boring stuff */
2412 BOOL capsOn
=keystate
[VK_CAPITAL
] !=0;
2414 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2415 if(cfg
.xlat_capslockcyr
)
2416 keystate
[VK_CAPITAL
] = 0;
2418 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
2424 unsigned char ch
= (unsigned char)keys
[i
];
2426 if (compose_state
==2 && (ch
&0x80) == 0 && ch
>' ') {
2431 if (compose_state
==3 && (ch
&0x80) == 0 && ch
>' ') {
2435 if ((nc
=check_compose(compose_char
,ch
)) == -1)
2437 MessageBeep(MB_ICONHAND
);
2440 *p
++ = xlat_kbd2tty((unsigned char)nc
);
2446 if( left_alt
&& key_down
) *p
++ = '\033';
2452 ch
= xlat_latkbd2win(ch
);
2453 *p
++ = xlat_kbd2tty(ch
);
2457 /* This is so the ALT-Numpad and dead keys work correctly. */
2462 /* If we're definitly not building up an ALT-54321 then clear it */
2463 if (!left_alt
) keys
[0] = 0;
2466 /* ALT alone may or may not want to bring up the System menu */
2467 if (wParam
== VK_MENU
) {
2469 if (message
== WM_SYSKEYDOWN
)
2471 else if (message
== WM_SYSKEYUP
&& alt_state
)
2472 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
2473 if (message
== WM_SYSKEYUP
)
2483 void set_title (char *title
) {
2484 sfree (window_name
);
2485 window_name
= smalloc(1+strlen(title
));
2486 strcpy (window_name
, title
);
2487 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
2488 SetWindowText (hwnd
, title
);
2491 void set_icon (char *title
) {
2493 icon_name
= smalloc(1+strlen(title
));
2494 strcpy (icon_name
, title
);
2495 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
2496 SetWindowText (hwnd
, title
);
2499 void set_sbar (int total
, int start
, int page
) {
2502 if (!cfg
.scrollbar
) return;
2504 si
.cbSize
= sizeof(si
);
2505 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
2507 si
.nMax
= total
- 1;
2511 SetScrollInfo (hwnd
, SB_VERT
, &si
, TRUE
);
2514 Context
get_ctx(void) {
2519 SelectPalette (hdc
, pal
, FALSE
);
2525 void free_ctx (Context ctx
) {
2526 SelectPalette (ctx
, GetStockObject (DEFAULT_PALETTE
), FALSE
);
2527 ReleaseDC (hwnd
, ctx
);
2530 static void real_palette_set (int n
, int r
, int g
, int b
) {
2532 logpal
->palPalEntry
[n
].peRed
= r
;
2533 logpal
->palPalEntry
[n
].peGreen
= g
;
2534 logpal
->palPalEntry
[n
].peBlue
= b
;
2535 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
2536 colours
[n
] = PALETTERGB(r
, g
, b
);
2537 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2539 colours
[n
] = RGB(r
, g
, b
);
2542 void palette_set (int n
, int r
, int g
, int b
) {
2543 static const int first
[21] = {
2544 0, 2, 4, 6, 8, 10, 12, 14,
2545 1, 3, 5, 7, 9, 11, 13, 15,
2548 real_palette_set (first
[n
], r
, g
, b
);
2550 real_palette_set (first
[n
]+1, r
, g
, b
);
2552 HDC hdc
= get_ctx();
2553 UnrealizeObject (pal
);
2554 RealizePalette (hdc
);
2559 void palette_reset (void) {
2562 for (i
= 0; i
< NCOLOURS
; i
++) {
2564 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
2565 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
2566 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
2567 logpal
->palPalEntry
[i
].peFlags
= 0;
2568 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
2569 defpal
[i
].rgbtGreen
,
2570 defpal
[i
].rgbtBlue
);
2572 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
2573 defpal
[i
].rgbtGreen
,
2574 defpal
[i
].rgbtBlue
);
2579 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2581 RealizePalette (hdc
);
2586 void write_clip (void *data
, int len
, int must_deselect
) {
2590 clipdata
= GlobalAlloc (GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
2593 lock
= GlobalLock (clipdata
);
2596 memcpy (lock
, data
, len
);
2597 ((unsigned char *) lock
) [len
] = 0;
2598 GlobalUnlock (clipdata
);
2601 SendMessage (hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
2603 if (OpenClipboard (hwnd
)) {
2605 SetClipboardData (CF_TEXT
, clipdata
);
2608 GlobalFree (clipdata
);
2611 SendMessage (hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
2614 void get_clip (void **p
, int *len
) {
2615 static HGLOBAL clipdata
= NULL
;
2619 GlobalUnlock (clipdata
);
2623 if (OpenClipboard (NULL
)) {
2624 clipdata
= GetClipboardData (CF_TEXT
);
2627 *p
= GlobalLock (clipdata
);
2641 * Move `lines' lines from position `from' to position `to' in the
2644 void optimised_move (int to
, int from
, int lines
) {
2648 min
= (to
< from ? to
: from
);
2649 max
= to
+ from
- min
;
2651 r
.left
= 0; r
.right
= cols
* font_width
;
2652 r
.top
= min
* font_height
; r
.bottom
= (max
+lines
) * font_height
;
2653 ScrollWindow (hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
2657 * Print a message box and perform a fatal exit.
2659 void fatalbox(char *fmt
, ...) {
2664 vsprintf(stuff
, fmt
, ap
);
2666 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
2673 void beep(int mode
) {
2674 if (mode
== BELL_DEFAULT
) {
2676 * For MessageBeep style bells, we want to be careful of
2677 * timing, because they don't have the nice property of
2678 * PlaySound bells that each one cancels the previous
2679 * active one. So we limit the rate to one per 50ms or so.
2681 static long lastbeep
= 0;
2684 now
= GetTickCount();
2685 beepdiff
= now
- lastbeep
;
2686 if (beepdiff
>= 0 && beepdiff
< 50)
2690 } else if (mode
== BELL_WAVEFILE
) {
2691 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
2692 char buf
[sizeof(cfg
.bell_wavefile
)+80];
2693 sprintf(buf
, "Unable to play sound file\n%s\n"
2694 "Using default sound instead", cfg
.bell_wavefile
);
2695 MessageBox(hwnd
, buf
, "PuTTY Sound Error", MB_OK
| MB_ICONEXCLAMATION
);
2696 cfg
.beep
= BELL_DEFAULT
;