16 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
22 #define IDM_SHOWLOG 0x0010
23 #define IDM_NEWSESS 0x0020
24 #define IDM_DUPSESS 0x0030
25 #define IDM_RECONF 0x0040
26 #define IDM_CLRSB 0x0050
27 #define IDM_RESET 0x0060
28 #define IDM_TEL_AYT 0x0070
29 #define IDM_TEL_BRK 0x0080
30 #define IDM_TEL_SYNCH 0x0090
31 #define IDM_TEL_EC 0x00a0
32 #define IDM_TEL_EL 0x00b0
33 #define IDM_TEL_GA 0x00c0
34 #define IDM_TEL_NOP 0x00d0
35 #define IDM_TEL_ABORT 0x00e0
36 #define IDM_TEL_AO 0x00f0
37 #define IDM_TEL_IP 0x0100
38 #define IDM_TEL_SUSP 0x0110
39 #define IDM_TEL_EOR 0x0120
40 #define IDM_TEL_EOF 0x0130
41 #define IDM_ABOUT 0x0140
42 #define IDM_SAVEDSESS 0x0150
43 #define IDM_COPYALL 0x0160
45 #define IDM_SESSLGP 0x0250 /* log type printable */
46 #define IDM_SESSLGA 0x0260 /* log type all chars */
47 #define IDM_SESSLGE 0x0270 /* log end */
48 #define IDM_SAVED_MIN 0x1000
49 #define IDM_SAVED_MAX 0x2000
51 #define WM_IGNORE_SIZE (WM_XUSER + 1)
52 #define WM_IGNORE_CLIP (WM_XUSER + 2)
54 /* Needed for Chinese support and apparently not always defined. */
56 #define VK_PROCESSKEY 0xE5
59 static LRESULT CALLBACK
WndProc (HWND
, UINT
, WPARAM
, LPARAM
);
60 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
, unsigned char *output
);
61 static void cfgtopalette(void);
62 static void init_palette(void);
63 static void init_fonts(int);
65 static int extra_width
, extra_height
;
67 static int pending_netevent
= 0;
68 static WPARAM pend_netevent_wParam
= 0;
69 static LPARAM pend_netevent_lParam
= 0;
70 static void enact_pending_netevent(void);
72 static time_t last_movement
= 0;
76 #define FONT_UNDERLINE 2
77 #define FONT_BOLDUND 3
79 #define FONT_OEMBOLD 5
80 #define FONT_OEMBOLDUND 6
82 static HFONT fonts
[8];
83 static int font_needs_hand_underlining
;
85 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
93 static COLORREF colours
[NCOLOURS
];
95 static LPLOGPALETTE logpal
;
96 static RGBTRIPLE defpal
[NCOLOURS
];
100 static HBITMAP caretbm
;
102 static int dbltime
, lasttime
, lastact
;
103 static Mouse_Button lastbtn
;
105 static char *window_name
, *icon_name
;
107 static int compose_state
= 0;
109 /* Dummy routine, only required in plink. */
110 void ldisc_update(int echo
, int edit
) {}
112 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
) {
113 static char appname
[] = "PuTTY";
118 int guess_width
, guess_height
;
121 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
123 winsock_ver
= MAKEWORD(1, 1);
124 if (WSAStartup(winsock_ver
, &wsadata
)) {
125 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
126 MB_OK
| MB_ICONEXCLAMATION
);
129 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
130 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
131 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
135 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
138 InitCommonControls();
140 /* Ensure a Maximize setting in Explorer doesn't maximise the
145 * Process the command line.
150 default_protocol
= DEFAULT_PROTOCOL
;
151 default_port
= DEFAULT_PORT
;
152 cfg
.logtype
= LGTYP_NONE
;
154 do_defaults(NULL
, &cfg
);
157 while (*p
&& isspace(*p
)) p
++;
160 * Process command line options first. Yes, this can be
161 * done better, and it will be as soon as I have the
165 char *q
= p
+ strcspn(p
, " \t");
168 tolower(p
[0]) == 's' &&
169 tolower(p
[1]) == 's' &&
170 tolower(p
[2]) == 'h') {
171 default_protocol
= cfg
.protocol
= PROT_SSH
;
172 default_port
= cfg
.port
= 22;
173 } else if (q
== p
+ 7 &&
174 tolower(p
[0]) == 'c' &&
175 tolower(p
[1]) == 'l' &&
176 tolower(p
[2]) == 'e' &&
177 tolower(p
[3]) == 'a' &&
178 tolower(p
[4]) == 'n' &&
179 tolower(p
[5]) == 'u' &&
180 tolower(p
[6]) == 'p') {
182 * `putty -cleanup'. Remove all registry entries
183 * associated with PuTTY, and also find and delete
184 * the random seed file.
187 "This procedure will remove ALL Registry\n"
188 "entries associated with PuTTY, and will\n"
189 "also remove the PuTTY random seed file.\n"
191 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
192 "SESSIONS. Are you really sure you want\n"
195 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
200 p
= q
+ strspn(q
, " \t");
204 * An initial @ means to activate a saved session.
208 while (i
> 1 && isspace(p
[i
-1]))
211 do_defaults (p
+1, &cfg
);
212 if (!*cfg
.host
&& !do_config()) {
216 } else if (*p
== '&') {
218 * An initial & means we've been given a command line
219 * containing the hex value of a HANDLE for a file
220 * mapping object, which we must then extract as a
225 if (sscanf(p
+1, "%p", &filemap
) == 1 &&
226 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
227 0, 0, sizeof(Config
))) != NULL
) {
230 CloseHandle(filemap
);
231 } else if (!do_config()) {
238 * If the hostname starts with "telnet:", set the
239 * protocol to Telnet and process the string as a
242 if (!strncmp(q
, "telnet:", 7)) {
246 if (q
[0] == '/' && q
[1] == '/')
248 cfg
.protocol
= PROT_TELNET
;
250 while (*p
&& *p
!= ':' && *p
!= '/') p
++;
258 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
259 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
261 while (*p
&& !isspace(*p
)) p
++;
264 strncpy (cfg
.host
, q
, sizeof(cfg
.host
)-1);
265 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
266 while (*p
&& isspace(*p
)) p
++;
279 /* See if host is of the form user@host */
280 if (cfg
.host
[0] != '\0') {
281 char *atsign
= strchr(cfg
.host
, '@');
282 /* Make sure we're not overflowing the user field */
284 if (atsign
-cfg
.host
< sizeof cfg
.username
) {
285 strncpy (cfg
.username
, cfg
.host
, atsign
-cfg
.host
);
286 cfg
.username
[atsign
-cfg
.host
] = '\0';
288 memmove(cfg
.host
, atsign
+1, 1+strlen(atsign
+1));
294 * Select protocol. This is farmed out into a table in a
295 * separate file to enable an ssh-free variant.
300 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
301 if (backends
[i
].protocol
== cfg
.protocol
) {
302 back
= backends
[i
].backend
;
306 MessageBox(NULL
, "Unsupported protocol number found",
307 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
313 /* Check for invalid Port number (i.e. zero) */
315 MessageBox(NULL
, "Invalid Port Number",
316 "PuTTY Internal Error", MB_OK
|MB_ICONEXCLAMATION
);
323 wndclass
.lpfnWndProc
= WndProc
;
324 wndclass
.cbClsExtra
= 0;
325 wndclass
.cbWndExtra
= 0;
326 wndclass
.hInstance
= inst
;
327 wndclass
.hIcon
= LoadIcon (inst
,
328 MAKEINTRESOURCE(IDI_MAINICON
));
329 wndclass
.hCursor
= LoadCursor (NULL
, IDC_IBEAM
);
330 wndclass
.hbrBackground
= GetStockObject (BLACK_BRUSH
);
331 wndclass
.lpszMenuName
= NULL
;
332 wndclass
.lpszClassName
= appname
;
334 RegisterClass (&wndclass
);
339 savelines
= cfg
.savelines
;
345 * Guess some defaults for the window size. This all gets
346 * updated later, so we don't really care too much. However, we
347 * do want the font width/height guesses to correspond to a
348 * large font rather than a small one...
355 term_size (cfg
.height
, cfg
.width
, cfg
.savelines
);
356 guess_width
= extra_width
+ font_width
* cols
;
357 guess_height
= extra_height
+ font_height
* rows
;
360 HWND w
= GetDesktopWindow();
361 GetWindowRect (w
, &r
);
362 if (guess_width
> r
.right
- r
.left
)
363 guess_width
= r
.right
- r
.left
;
364 if (guess_height
> r
.bottom
- r
.top
)
365 guess_height
= r
.bottom
- r
.top
;
369 int winmode
= WS_OVERLAPPEDWINDOW
|WS_VSCROLL
;
371 if (!cfg
.scrollbar
) winmode
&= ~(WS_VSCROLL
);
372 if (cfg
.locksize
) winmode
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
373 if (cfg
.alwaysontop
) exwinmode
= WS_EX_TOPMOST
;
374 hwnd
= CreateWindowEx (exwinmode
, appname
, appname
,
375 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
376 guess_width
, guess_height
,
377 NULL
, NULL
, inst
, NULL
);
381 * Initialise the fonts, simultaneously correcting the guesses
382 * for font_{width,height}.
384 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
389 * Correct the guesses for extra_{width,height}.
393 GetWindowRect (hwnd
, &wr
);
394 GetClientRect (hwnd
, &cr
);
395 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
396 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
400 * Resize the window, now we know what size we _really_ want it
403 guess_width
= extra_width
+ font_width
* cols
;
404 guess_height
= extra_height
+ font_height
* rows
;
405 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
406 SetWindowPos (hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
407 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
410 * Set up a caret bitmap, with no content.
414 int size
= (font_width
+15)/16 * 2 * font_height
;
415 bits
= smalloc(size
);
416 memset(bits
, 0, size
);
417 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
420 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
423 * Initialise the scroll bar.
428 si
.cbSize
= sizeof(si
);
429 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
434 SetScrollInfo (hwnd
, SB_VERT
, &si
, FALSE
);
438 * Start up the telnet connection.
442 char msg
[1024], *title
;
445 error
= back
->init (cfg
.host
, cfg
.port
, &realhost
);
447 sprintf(msg
, "Unable to open connection:\n%s", error
);
448 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
451 window_name
= icon_name
= NULL
;
453 title
= cfg
.wintitle
;
455 sprintf(msg
, "%s - PuTTY", realhost
);
462 session_closed
= FALSE
;
465 * Set up the input and output buffers.
468 outbuf_reap
= outbuf_head
= 0;
471 * Prepare the mouse handler.
473 lastact
= MA_NOTHING
;
474 lastbtn
= MB_NOTHING
;
475 dbltime
= GetDoubleClickTime();
478 * Set up the session-control options on the system menu.
481 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
485 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
486 if (cfg
.protocol
== PROT_TELNET
) {
488 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
489 AppendMenu (p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
490 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
491 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
492 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
493 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
494 AppendMenu (p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
495 AppendMenu (p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
496 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
497 AppendMenu (p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
498 AppendMenu (p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
499 AppendMenu (p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
500 AppendMenu (p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
501 AppendMenu (p
, MF_SEPARATOR
, 0, 0);
502 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
503 AppendMenu (p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
504 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
, "Telnet Command");
505 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
507 AppendMenu (m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
508 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
509 AppendMenu (m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
510 AppendMenu (m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
513 for (i
= 1 ; i
< ((nsessions
< 256) ? nsessions
: 256) ; i
++)
514 AppendMenu (s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
) , sessions
[i
]);
515 AppendMenu (m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
516 AppendMenu (m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
517 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
518 AppendMenu (m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
519 AppendMenu (m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
520 AppendMenu (m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
521 AppendMenu (m
, MF_SEPARATOR
, 0, 0);
522 AppendMenu (m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
526 * Finally show the window!
528 ShowWindow (hwnd
, show
);
531 * Open the initial log file if there is one.
536 * Set the palette up.
542 has_focus
= (GetForegroundWindow() == hwnd
);
545 if (GetMessage (&msg
, NULL
, 0, 0) == 1)
547 int timer_id
= 0, long_timer
= 0;
549 while (msg
.message
!= WM_QUIT
) {
550 /* Sometimes DispatchMessage calls routines that use their own
551 * GetMessage loop, setup this timer so we get some control back.
553 * Also call term_update() from the timer so that if the host
554 * is sending data flat out we still do redraws.
556 if(timer_id
&& long_timer
) {
557 KillTimer(hwnd
, timer_id
);
558 long_timer
= timer_id
= 0;
561 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
562 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
563 DispatchMessage (&msg
);
565 /* Make sure we blink everything that needs it. */
568 /* Send the paste buffer if there's anything to send */
571 /* If there's nothing new in the queue then we can do everything
572 * we've delayed, reading the socket, writing, and repainting
575 if (PeekMessage (&msg
, NULL
, 0, 0, PM_REMOVE
))
578 if (pending_netevent
) {
579 enact_pending_netevent();
581 /* Force the cursor blink on */
584 if (PeekMessage (&msg
, NULL
, 0, 0, PM_REMOVE
))
588 /* Okay there is now nothing to do so we make sure the screen is
589 * completely up to date then tell windows to call us in a little
593 KillTimer(hwnd
, timer_id
);
602 /* Hmm, term_update didn't want to do an update too soon ... */
603 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
605 timer_id
= SetTimer(hwnd
, 1, 59500, NULL
);
607 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
610 /* There's no point rescanning everything in the message queue
611 * so we do an apparently unnecessary wait here
614 if (GetMessage (&msg
, NULL
, 0, 0) != 1)
626 DeleteObject(fonts
[i
]);
633 if (cfg
.protocol
== PROT_SSH
) {
644 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
646 char *do_select(SOCKET skt
, int startup
) {
650 events
= FD_READ
| FD_WRITE
| FD_OOB
| FD_CLOSE
;
655 return "do_select(): internal error (hwnd==NULL)";
656 if (WSAAsyncSelect (skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
657 switch (WSAGetLastError()) {
658 case WSAENETDOWN
: return "Network is down";
659 default: return "WSAAsyncSelect(): unknown error";
666 * Print a message box and close the connection.
668 void connection_fatal(char *fmt
, ...) {
673 vsprintf(stuff
, fmt
, ap
);
675 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
676 if (cfg
.close_on_exit
== COE_ALWAYS
)
679 session_closed
= TRUE
;
680 SetWindowText (hwnd
, "PuTTY (inactive)");
685 * Actually do the job requested by a WM_NETEVENT
687 static void enact_pending_netevent(void) {
688 static int reentering
= 0;
689 extern int select_result(WPARAM
, LPARAM
);
693 return; /* don't unpend the pending */
695 pending_netevent
= FALSE
;
698 ret
= select_result (pend_netevent_wParam
, pend_netevent_lParam
);
701 if (ret
== 0 && !session_closed
) {
702 /* Abnormal exits will already have set session_closed and taken
703 * appropriate action. */
704 if (cfg
.close_on_exit
== COE_ALWAYS
||
705 cfg
.close_on_exit
== COE_NORMAL
)
708 session_closed
= TRUE
;
709 SetWindowText (hwnd
, "PuTTY (inactive)");
710 MessageBox(hwnd
, "Connection closed by remote host",
711 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
717 * Copy the colour palette from the configuration data into defpal.
718 * This is non-trivial because the colour indices are different.
720 static void cfgtopalette(void) {
722 static const int ww
[] = {
723 6, 7, 8, 9, 10, 11, 12, 13,
724 14, 15, 16, 17, 18, 19, 20, 21,
725 0, 1, 2, 3, 4, 4, 5, 5
728 for (i
=0; i
<24; i
++) {
730 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
731 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
732 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
737 * Set up the colour palette.
739 static void init_palette(void) {
741 HDC hdc
= GetDC (hwnd
);
743 if (cfg
.try_palette
&&
744 GetDeviceCaps (hdc
, RASTERCAPS
) & RC_PALETTE
) {
745 logpal
= smalloc(sizeof(*logpal
)
746 - sizeof(logpal
->palPalEntry
)
747 + NCOLOURS
* sizeof(PALETTEENTRY
));
748 logpal
->palVersion
= 0x300;
749 logpal
->palNumEntries
= NCOLOURS
;
750 for (i
= 0; i
< NCOLOURS
; i
++) {
751 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
752 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
753 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
754 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
756 pal
= CreatePalette (logpal
);
758 SelectPalette (hdc
, pal
, FALSE
);
759 RealizePalette (hdc
);
760 SelectPalette (hdc
, GetStockObject (DEFAULT_PALETTE
),
764 ReleaseDC (hwnd
, hdc
);
767 for (i
=0; i
<NCOLOURS
; i
++)
768 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
772 for(i
=0; i
<NCOLOURS
; i
++)
773 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
779 * Initialise all the fonts we will need. There may be as many as
780 * eight or as few as one. We also:
782 * - check the font width and height, correcting our guesses if
785 * - verify that the bold font is the same width as the ordinary
786 * one, and engage shadow bolding if not.
788 * - verify that the underlined font is the same width as the
789 * ordinary one (manual underlining by means of line drawing can
790 * be done in a pinch).
792 static void init_fonts(int pick_width
) {
797 int fw_dontcare
, fw_bold
;
806 if (cfg
.fontisbold
) {
807 fw_dontcare
= FW_BOLD
;
810 fw_dontcare
= FW_DONTCARE
;
816 font_height
= cfg
.fontheight
;
817 if (font_height
> 0) {
818 font_height
= -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
820 font_width
= pick_width
;
823 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
824 c, OUT_DEFAULT_PRECIS, \
825 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
826 FIXED_PITCH | FF_DONTCARE, cfg.font)
828 if (cfg
.vtmode
!= VT_OEMONLY
) {
829 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
831 SelectObject (hdc
, fonts
[FONT_NORMAL
]);
832 GetTextMetrics(hdc
, &tm
);
833 font_height
= tm
.tmHeight
;
834 font_width
= tm
.tmAveCharWidth
;
836 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
839 * Some fonts, e.g. 9-pt Courier, draw their underlines
840 * outside their character cell. We successfully prevent
841 * screen corruption by clipping the text output, but then
842 * we lose the underline completely. Here we try to work
843 * out whether this is such a font, and if it is, we set a
844 * flag that causes underlines to be drawn by hand.
846 * Having tried other more sophisticated approaches (such
847 * as examining the TEXTMETRIC structure or requesting the
848 * height of a string), I think we'll do this the brute
849 * force way: we create a small bitmap, draw an underlined
850 * space on it, and test to see whether any pixels are
851 * foreground-coloured. (Since we expect the underline to
852 * go all the way across the character cell, we only search
853 * down a single column of the bitmap, half way across.)
857 HBITMAP und_bm
, und_oldbm
;
861 und_dc
= CreateCompatibleDC(hdc
);
862 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
863 und_oldbm
= SelectObject(und_dc
, und_bm
);
864 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
865 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
866 SetTextColor (und_dc
, RGB(255,255,255));
867 SetBkColor (und_dc
, RGB(0,0,0));
868 SetBkMode (und_dc
, OPAQUE
);
869 ExtTextOut (und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
871 for (i
= 0; i
< font_height
; i
++) {
872 c
= GetPixel(und_dc
, font_width
/2, i
);
876 SelectObject(und_dc
, und_oldbm
);
877 DeleteObject(und_bm
);
879 font_needs_hand_underlining
= !gotit
;
882 if (bold_mode
== BOLD_FONT
) {
883 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
884 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
887 if (cfg
.vtmode
== VT_OEMANSI
) {
888 f(FONT_OEM
, OEM_CHARSET
, fw_dontcare
, FALSE
);
889 f(FONT_OEMUND
, OEM_CHARSET
, fw_dontcare
, TRUE
);
891 if (bold_mode
== BOLD_FONT
) {
892 f(FONT_OEMBOLD
, OEM_CHARSET
, fw_bold
, FALSE
);
893 f(FONT_OEMBOLDUND
, OEM_CHARSET
, fw_bold
, TRUE
);
899 f(FONT_OEM
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
901 SelectObject (hdc
, fonts
[FONT_OEM
]);
902 GetTextMetrics(hdc
, &tm
);
903 font_height
= tm
.tmHeight
;
904 font_width
= tm
.tmAveCharWidth
;
906 f(FONT_OEMUND
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
908 if (bold_mode
== BOLD_FONT
) {
909 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
910 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
915 descent
= tm
.tmAscent
+ 1;
916 if (descent
>= font_height
)
917 descent
= font_height
- 1;
918 firstchar
= tm
.tmFirstChar
;
920 for (i
=0; i
<8; i
++) {
922 if (SelectObject (hdc
, fonts
[i
]) &&
923 GetTextMetrics(hdc
, &tm
) )
924 fsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
930 ReleaseDC (hwnd
, hdc
);
932 /* ... This is wrong in OEM only mode */
933 if (fsize
[FONT_UNDERLINE
] != fsize
[FONT_NORMAL
] ||
934 (bold_mode
== BOLD_FONT
&&
935 fsize
[FONT_BOLDUND
] != fsize
[FONT_BOLD
])) {
937 DeleteObject (fonts
[FONT_UNDERLINE
]);
938 if (bold_mode
== BOLD_FONT
)
939 DeleteObject (fonts
[FONT_BOLDUND
]);
942 if (bold_mode
== BOLD_FONT
&&
943 fsize
[FONT_BOLD
] != fsize
[FONT_NORMAL
]) {
944 bold_mode
= BOLD_SHADOW
;
945 DeleteObject (fonts
[FONT_BOLD
]);
946 if (und_mode
== UND_FONT
)
947 DeleteObject (fonts
[FONT_BOLDUND
]);
951 /* With the fascist font painting it doesn't matter if the linedraw font
952 * isn't exactly the right size anymore so we don't have to check this.
954 if (cfg
.vtmode
== VT_OEMANSI
&& fsize
[FONT_OEM
] != fsize
[FONT_NORMAL
] ) {
955 if( cfg
.fontcharset
== OEM_CHARSET
)
957 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
958 "different sizes. Using OEM-only mode instead",
959 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
960 cfg
.vtmode
= VT_OEMONLY
;
962 else if( firstchar
< ' ' )
964 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
965 "different sizes. Using XTerm mode instead",
966 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
967 cfg
.vtmode
= VT_XWINDOWS
;
971 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
972 "different sizes. Using ISO8859-1 mode instead",
973 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
974 cfg
.vtmode
= VT_POORMAN
;
979 DeleteObject (fonts
[i
]);
985 void request_resize (int w
, int h
, int refont
) {
988 /* If the window is maximized supress resizing attempts */
989 if(IsZoomed(hwnd
)) return;
992 /* Don't do this in OEMANSI, you may get disable messages */
993 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132)
994 && cfg
.vtmode
!= VT_OEMANSI
)
996 if (refont
&& w
!= cols
&& (cols
==80 || cols
==132))
999 /* If font width too big for screen should we shrink the font more ? */
1001 font_width
= ((font_width
*cols
+w
/2)/w
);
1008 DeleteObject(fonts
[i
]);
1010 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1011 und_mode
= UND_FONT
;
1012 init_fonts(font_width
);
1016 static int first_time
= 1;
1022 /* Get the size of the screen */
1023 if (GetClientRect(GetDesktopWindow(),&ss
))
1024 /* first_time = 0 */;
1025 else { first_time
= 2; break; }
1027 /* Make sure the values are sane */
1028 width
= (ss
.right
-ss
.left
-extra_width
) / font_width
;
1029 height
= (ss
.bottom
-ss
.top
-extra_height
) / font_height
;
1031 if (w
>width
) w
=width
;
1032 if (h
>height
) h
=height
;
1038 width
= extra_width
+ font_width
* w
;
1039 height
= extra_height
+ font_height
* h
;
1041 SetWindowPos (hwnd
, NULL
, 0, 0, width
, height
,
1042 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1043 SWP_NOMOVE
| SWP_NOZORDER
);
1046 static void click (Mouse_Button b
, int x
, int y
) {
1047 int thistime
= GetMessageTime();
1049 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1050 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1051 lastact
== MA_2CLK ? MA_3CLK
:
1052 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1057 if (lastact
!= MA_NOTHING
)
1058 term_mouse (b
, lastact
, x
, y
);
1059 lasttime
= thistime
;
1062 static void show_mouseptr(int show
) {
1063 static int cursor_visible
= 1;
1064 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1066 if (cursor_visible
&& !show
)
1068 else if (!cursor_visible
&& show
)
1070 cursor_visible
= show
;
1073 static LRESULT CALLBACK
WndProc (HWND hwnd
, UINT message
,
1074 WPARAM wParam
, LPARAM lParam
) {
1076 static int ignore_size
= FALSE
;
1077 static int ignore_clip
= FALSE
;
1078 static int just_reconfigged
= FALSE
;
1079 static int resizing
= FALSE
;
1080 static int need_backend_resize
= FALSE
;
1084 if (pending_netevent
)
1085 enact_pending_netevent();
1092 if (cfg
.ping_interval
> 0)
1096 if (now
-last_movement
> cfg
.ping_interval
)
1098 back
->special(TS_PING
);
1099 last_movement
= now
;
1107 if (!cfg
.warn_on_close
|| session_closed
||
1108 MessageBox(hwnd
, "Are you sure you want to close this session?",
1109 "PuTTY Exit Confirmation",
1110 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1111 DestroyWindow(hwnd
);
1115 PostQuitMessage (0);
1118 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1130 PROCESS_INFORMATION pi
;
1131 HANDLE filemap
= NULL
;
1133 if (wParam
== IDM_DUPSESS
) {
1135 * Allocate a file-mapping memory chunk for the
1138 SECURITY_ATTRIBUTES sa
;
1141 sa
.nLength
= sizeof(sa
);
1142 sa
.lpSecurityDescriptor
= NULL
;
1143 sa
.bInheritHandle
= TRUE
;
1144 filemap
= CreateFileMapping((HANDLE
)0xFFFFFFFF,
1151 p
= (Config
*)MapViewOfFile(filemap
,
1153 0, 0, sizeof(Config
));
1155 *p
= cfg
; /* structure copy */
1159 sprintf(c
, "putty &%p", filemap
);
1161 } else if (wParam
== IDM_SAVEDSESS
) {
1162 char *session
= sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1163 cl
= smalloc(16 + strlen(session
)); /* 8, but play safe */
1165 cl
= NULL
; /* not a very important failure mode */
1167 sprintf(cl
, "putty @%s", session
);
1173 GetModuleFileName (NULL
, b
, sizeof(b
)-1);
1175 si
.lpReserved
= NULL
;
1176 si
.lpDesktop
= NULL
;
1180 si
.lpReserved2
= NULL
;
1181 CreateProcess (b
, cl
, NULL
, NULL
, TRUE
,
1182 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1185 CloseHandle(filemap
);
1192 int prev_alwaysontop
= cfg
.alwaysontop
;
1193 char oldlogfile
[FILENAME_MAX
];
1195 int need_setwpos
= FALSE
;
1196 int old_fwidth
, old_fheight
;
1198 strcpy(oldlogfile
, cfg
.logfilename
);
1199 oldlogtype
= cfg
.logtype
;
1202 old_fwidth
= font_width
;
1203 old_fheight
= font_height
;
1204 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1206 if (!do_reconfig(hwnd
))
1209 if (strcmp(oldlogfile
, cfg
.logfilename
) ||
1210 oldlogtype
!= cfg
.logtype
) {
1211 logfclose(); /* reset logging */
1215 just_reconfigged
= TRUE
;
1220 DeleteObject(fonts
[i
]);
1222 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1223 und_mode
= UND_FONT
;
1227 * Flush the line discipline's edit buffer in the
1228 * case where local editing has just been disabled.
1230 ldisc_send(NULL
, 0);
1238 /* Enable or disable the scroll bar, etc */
1240 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1241 LONG nexflag
, exflag
= GetWindowLong(hwnd
, GWL_EXSTYLE
);
1244 if (cfg
.alwaysontop
!= prev_alwaysontop
) {
1245 if (cfg
.alwaysontop
) {
1246 nexflag
= WS_EX_TOPMOST
;
1247 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1248 SWP_NOMOVE
| SWP_NOSIZE
);
1251 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1252 SWP_NOMOVE
| SWP_NOSIZE
);
1257 if (cfg
.scrollbar
) nflg
|= WS_VSCROLL
;
1258 else nflg
&= ~WS_VSCROLL
;
1260 nflg
&= ~(WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1262 nflg
|= (WS_THICKFRAME
|WS_MAXIMIZEBOX
);
1264 if (nflg
!= flag
|| nexflag
!= exflag
)
1269 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1270 if (nexflag
!= exflag
)
1271 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1273 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1275 SetWindowPos(hwnd
, NULL
, 0,0,0,0,
1276 SWP_NOACTIVATE
|SWP_NOCOPYBITS
|
1277 SWP_NOMOVE
|SWP_NOSIZE
|SWP_NOZORDER
|
1280 GetWindowRect (hwnd
, &wr
);
1281 GetClientRect (hwnd
, &cr
);
1282 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1283 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1287 if (cfg
.height
!= rows
||
1288 cfg
.width
!= cols
||
1289 old_fwidth
!= font_width
||
1290 old_fheight
!= font_height
||
1291 cfg
.savelines
!= savelines
)
1292 need_setwpos
= TRUE
;
1293 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1294 InvalidateRect(hwnd
, NULL
, TRUE
);
1297 SetWindowPos (hwnd
, NULL
, 0, 0,
1298 extra_width
+ font_width
* cfg
.width
,
1299 extra_height
+ font_height
* cfg
.height
,
1300 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1301 SWP_NOMOVE
| SWP_NOZORDER
);
1303 set_title(cfg
.wintitle
);
1304 if (IsIconic(hwnd
)) {
1305 SetWindowText (hwnd
,
1306 cfg
.win_name_always ? window_name
: icon_name
);
1319 case IDM_TEL_AYT
: back
->special (TS_AYT
); break;
1320 case IDM_TEL_BRK
: back
->special (TS_BRK
); break;
1321 case IDM_TEL_SYNCH
: back
->special (TS_SYNCH
); break;
1322 case IDM_TEL_EC
: back
->special (TS_EC
); break;
1323 case IDM_TEL_EL
: back
->special (TS_EL
); break;
1324 case IDM_TEL_GA
: back
->special (TS_GA
); break;
1325 case IDM_TEL_NOP
: back
->special (TS_NOP
); break;
1326 case IDM_TEL_ABORT
: back
->special (TS_ABORT
); break;
1327 case IDM_TEL_AO
: back
->special (TS_AO
); break;
1328 case IDM_TEL_IP
: back
->special (TS_IP
); break;
1329 case IDM_TEL_SUSP
: back
->special (TS_SUSP
); break;
1330 case IDM_TEL_EOR
: back
->special (TS_EOR
); break;
1331 case IDM_TEL_EOF
: back
->special (TS_EOF
); break;
1336 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1337 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1342 #define X_POS(l) ((int)(short)LOWORD(l))
1343 #define Y_POS(l) ((int)(short)HIWORD(l))
1345 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1346 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1348 case WM_LBUTTONDOWN
:
1350 click (MB_SELECT
, TO_CHR_X(X_POS(lParam
)),
1351 TO_CHR_Y(Y_POS(lParam
)));
1356 term_mouse (MB_SELECT
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1357 TO_CHR_Y(Y_POS(lParam
)));
1360 case WM_MBUTTONDOWN
:
1363 click (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1364 TO_CHR_X(X_POS(lParam
)),
1365 TO_CHR_Y(Y_POS(lParam
)));
1369 term_mouse (cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
,
1370 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1371 TO_CHR_Y(Y_POS(lParam
)));
1374 case WM_RBUTTONDOWN
:
1377 click (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1378 TO_CHR_X(X_POS(lParam
)),
1379 TO_CHR_Y(Y_POS(lParam
)));
1383 term_mouse (cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
,
1384 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1385 TO_CHR_Y(Y_POS(lParam
)));
1391 * Add the mouse position and message time to the random
1394 noise_ultralight(lParam
);
1396 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1398 if (wParam
& MK_LBUTTON
)
1400 else if (wParam
& MK_MBUTTON
)
1401 b
= cfg
.mouse_is_xterm ? MB_PASTE
: MB_EXTEND
;
1403 b
= cfg
.mouse_is_xterm ? MB_EXTEND
: MB_PASTE
;
1404 term_mouse (b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1405 TO_CHR_Y(Y_POS(lParam
)));
1408 case WM_NCMOUSEMOVE
:
1410 noise_ultralight(lParam
);
1412 case WM_IGNORE_CLIP
:
1413 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1415 case WM_DESTROYCLIPBOARD
:
1418 ignore_clip
= FALSE
;
1424 hdc
= BeginPaint (hwnd
, &p
);
1426 SelectPalette (hdc
, pal
, TRUE
);
1427 RealizePalette (hdc
);
1429 term_paint (hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1430 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1431 SelectObject (hdc
, GetStockObject(SYSTEM_FONT
));
1432 SelectObject (hdc
, GetStockObject(WHITE_PEN
));
1433 EndPaint (hwnd
, &p
);
1438 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1439 * but the only one that's likely to try to overload us is FD_READ.
1440 * This means buffering just one is fine.
1442 if (pending_netevent
)
1443 enact_pending_netevent();
1445 pending_netevent
= TRUE
;
1446 pend_netevent_wParam
=wParam
;
1447 pend_netevent_lParam
=lParam
;
1448 time(&last_movement
);
1452 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
1465 case WM_IGNORE_SIZE
:
1466 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1468 case WM_ENTERSIZEMOVE
:
1471 need_backend_resize
= FALSE
;
1473 case WM_EXITSIZEMOVE
:
1476 if (need_backend_resize
)
1481 int width
, height
, w
, h
, ew
, eh
;
1482 LPRECT r
= (LPRECT
)lParam
;
1484 width
= r
->right
- r
->left
- extra_width
;
1485 height
= r
->bottom
- r
->top
- extra_height
;
1486 w
= (width
+ font_width
/2) / font_width
; if (w
< 1) w
= 1;
1487 h
= (height
+ font_height
/2) / font_height
; if (h
< 1) h
= 1;
1488 UpdateSizeTip(hwnd
, w
, h
);
1489 ew
= width
- w
* font_width
;
1490 eh
= height
- h
* font_height
;
1492 if (wParam
== WMSZ_LEFT
||
1493 wParam
== WMSZ_BOTTOMLEFT
||
1494 wParam
== WMSZ_TOPLEFT
)
1500 if (wParam
== WMSZ_TOP
||
1501 wParam
== WMSZ_TOPRIGHT
||
1502 wParam
== WMSZ_TOPLEFT
)
1512 /* break; (never reached) */
1514 if (wParam
== SIZE_MINIMIZED
) {
1515 SetWindowText (hwnd
,
1516 cfg
.win_name_always ? window_name
: icon_name
);
1519 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1520 SetWindowText (hwnd
, window_name
);
1522 int width
, height
, w
, h
;
1523 #if 0 /* we have fixed this using WM_SIZING now */
1527 width
= LOWORD(lParam
);
1528 height
= HIWORD(lParam
);
1529 w
= width
/ font_width
; if (w
< 1) w
= 1;
1530 h
= height
/ font_height
; if (h
< 1) h
= 1;
1531 #if 0 /* we have fixed this using WM_SIZING now */
1532 ew
= width
- w
* font_width
;
1533 eh
= height
- h
* font_height
;
1534 if (ew
!= 0 || eh
!= 0) {
1536 GetWindowRect (hwnd
, &r
);
1537 SendMessage (hwnd
, WM_IGNORE_SIZE
, 0, 0);
1538 SetWindowPos (hwnd
, NULL
, 0, 0,
1539 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1540 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1543 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1545 term_size (h
, w
, cfg
.savelines
);
1547 * Don't call back->size in mid-resize. (To prevent
1548 * massive numbers of resize events getting sent
1549 * down the connection during an NT opaque drag.)
1554 need_backend_resize
= TRUE
;
1555 just_reconfigged
= FALSE
;
1558 ignore_size
= FALSE
;
1561 switch (LOWORD(wParam
)) {
1562 case SB_BOTTOM
: term_scroll(-1, 0); break;
1563 case SB_TOP
: term_scroll(+1, 0); break;
1564 case SB_LINEDOWN
: term_scroll (0, +1); break;
1565 case SB_LINEUP
: term_scroll (0, -1); break;
1566 case SB_PAGEDOWN
: term_scroll (0, +rows
/2); break;
1567 case SB_PAGEUP
: term_scroll (0, -rows
/2); break;
1568 case SB_THUMBPOSITION
: case SB_THUMBTRACK
:
1569 term_scroll (1, HIWORD(wParam
)); break;
1572 case WM_PALETTECHANGED
:
1573 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1574 HDC hdc
= get_ctx();
1576 if (RealizePalette (hdc
) > 0)
1582 case WM_QUERYNEWPALETTE
:
1584 HDC hdc
= get_ctx();
1586 if (RealizePalette (hdc
) > 0)
1598 * Add the scan code and keypress timing to the random
1601 noise_ultralight(lParam
);
1604 * We don't do TranslateMessage since it disassociates the
1605 * resulting CHAR message from the KEYDOWN that sparked it,
1606 * which we occasionally don't want. Instead, we process
1607 * KEYDOWN, and call the Win32 translator functions so that
1608 * we get the translations under _our_ control.
1611 unsigned char buf
[20];
1614 if (wParam
==VK_PROCESSKEY
) {
1617 m
.message
= WM_KEYDOWN
;
1619 m
.lParam
= lParam
& 0xdfff;
1620 TranslateMessage(&m
);
1622 len
= TranslateKey (message
, wParam
, lParam
, buf
);
1624 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1625 ldisc_send (buf
, len
);
1634 unsigned char buf
[2];
1637 buf
[0] = wParam
>> 8;
1638 ldisc_send (buf
, 2);
1643 * Nevertheless, we are prepared to deal with WM_CHAR
1644 * messages, should they crop up. So if someone wants to
1645 * post the things to us as part of a macro manoeuvre,
1646 * we're ready to cope.
1649 char c
= xlat_kbd2tty((unsigned char)wParam
);
1655 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
1659 * Move the system caret. (We maintain one, even though it's
1660 * invisible, for the benefit of blind people: apparently some
1661 * helper software tracks the system caret, so we should arrange to
1664 void sys_cursor(int x
, int y
) {
1665 SetCaretPos(x
* font_width
, y
* font_height
);
1669 * Draw a line of text in the window, at given character
1670 * coordinates, in given attributes.
1672 * We are allowed to fiddle with the contents of `text'.
1674 void do_text (Context ctx
, int x
, int y
, char *text
, int len
,
1675 unsigned long attr
, int lattr
) {
1677 int nfg
, nbg
, nfont
;
1680 int force_manual_underline
= 0;
1681 int fnt_width
= font_width
*(1+(lattr
!=LATTR_NORM
));
1682 static int *IpDx
= 0, IpDxLEN
= 0;;
1684 if (len
>IpDxLEN
|| IpDx
[0] != fnt_width
) {
1688 IpDx
= smalloc((len
+16)*sizeof(int));
1691 for(i
=0; i
<IpDxLEN
; i
++)
1692 IpDx
[i
] = fnt_width
;
1698 if ((attr
& ATTR_ACTCURS
) && cfg
.cursor_type
== 0) {
1699 attr
&= (bold_mode
== BOLD_COLOURS ?
0x300200 : 0x300300);
1700 attr
^= ATTR_CUR_XOR
;
1704 if (cfg
.vtmode
== VT_OEMONLY
)
1708 * Map high-half characters in order to approximate ISO using
1709 * OEM character set. No characters are missing if the OEM codepage
1712 if (nfont
& FONT_OEM
) {
1714 for (i
=0; i
<len
; i
++)
1715 if (text
[i
] >= '\xA0' && text
[i
] <= '\xFF') {
1717 /* This is CP850 ... perfect translation */
1718 static const char oemhighhalf
[] =
1719 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1720 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1721 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1722 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1723 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1724 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1725 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1726 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1727 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1728 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1729 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1730 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1733 /* This is CP437 ... junk translation */
1734 static const unsigned char oemhighhalf
[] = {
1735 0x20, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1736 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1737 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1738 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1739 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1740 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1741 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1742 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1743 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1744 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1745 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1746 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1749 text
[i
] = oemhighhalf
[(unsigned char)text
[i
] - 0xA0];
1753 if (attr
& ATTR_LINEDRW
) {
1756 static const char poorman
[] =
1757 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1760 static const char oemmap_437
[] =
1761 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1762 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1765 static const char oemmap_850
[] =
1766 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1767 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1769 /* Poor windows font ... eg: windows courier */
1770 static const char oemmap
[] =
1771 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1772 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1775 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1776 * VT100 line drawing chars; everything else stays normal.
1778 * Actually '_' maps to space too, but that's done before.
1780 switch (cfg
.vtmode
) {
1782 for (i
=0; i
<len
; i
++)
1783 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1784 text
[i
] += '\x01' - '\x60';
1787 /* Make sure we actually have an OEM font */
1788 if (fonts
[nfont
|FONT_OEM
]) {
1791 for (i
=0; i
<len
; i
++)
1792 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1793 text
[i
] = oemmap
[(unsigned char)text
[i
] - 0x60];
1797 for (i
=0; i
<len
; i
++)
1798 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1799 text
[i
] = poorman
[(unsigned char)text
[i
] - 0x60];
1804 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
1805 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
1806 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
1808 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
1809 nfont
|= FONT_UNDERLINE
;
1812 if (nfont
&FONT_UNDERLINE
)
1813 force_manual_underline
= 1;
1814 /* Don't do the same for manual bold, it could be bad news. */
1816 nfont
&= ~(FONT_BOLD
|FONT_UNDERLINE
);
1818 if (font_needs_hand_underlining
&& (attr
& ATTR_UNDER
))
1819 force_manual_underline
= 1;
1820 if (attr
& ATTR_REVERSE
) {
1821 t
= nfg
; nfg
= nbg
; nbg
= t
;
1823 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
1825 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
1829 SelectObject (hdc
, fonts
[nfont
]);
1830 SetTextColor (hdc
, fg
);
1831 SetBkColor (hdc
, bg
);
1832 SetBkMode (hdc
, OPAQUE
);
1835 line_box
.right
= x
+fnt_width
*len
;
1836 line_box
.bottom
= y
+font_height
;
1837 ExtTextOut (hdc
, x
, y
, ETO_CLIPPED
|ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
1838 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
1839 SetBkMode (hdc
, TRANSPARENT
);
1841 /* GRR: This draws the character outside it's box and can leave
1842 * 'droppings' even with the clip box! I suppose I could loop it
1843 * one character at a time ... yuk.
1845 * Or ... I could do a test print with "W", and use +1 or -1 for this
1846 * shift depending on if the leftmost column is blank...
1848 ExtTextOut (hdc
, x
-1, y
, ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
1850 if (force_manual_underline
||
1851 (und_mode
== UND_LINE
&& (attr
& ATTR_UNDER
))) {
1853 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, fg
));
1854 MoveToEx (hdc
, x
, y
+descent
, NULL
);
1855 LineTo (hdc
, x
+len
*fnt_width
, y
+descent
);
1856 oldpen
= SelectObject (hdc
, oldpen
);
1857 DeleteObject (oldpen
);
1859 if ((attr
& ATTR_PASCURS
) && cfg
.cursor_type
== 0) {
1862 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
1863 pts
[2].x
= pts
[3].x
= x
+fnt_width
-1;
1864 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
1865 pts
[1].y
= pts
[2].y
= y
+font_height
-1;
1866 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
1867 Polyline (hdc
, pts
, 5);
1868 oldpen
= SelectObject (hdc
, oldpen
);
1869 DeleteObject (oldpen
);
1871 if ((attr
& (ATTR_ACTCURS
| ATTR_PASCURS
)) && cfg
.cursor_type
!= 0) {
1872 int startx
, starty
, dx
, dy
, length
, i
;
1873 if (cfg
.cursor_type
== 1) {
1874 startx
= x
; starty
= y
+descent
;
1875 dx
= 1; dy
= 0; length
= fnt_width
;
1878 if (attr
& ATTR_RIGHTCURS
)
1879 xadjust
= fnt_width
-1;
1880 startx
= x
+xadjust
; starty
= y
;
1881 dx
= 0; dy
= 1; length
= font_height
;
1883 if (attr
& ATTR_ACTCURS
) {
1885 oldpen
= SelectObject (hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
1886 MoveToEx (hdc
, startx
, starty
, NULL
);
1887 LineTo (hdc
, startx
+dx
*length
, starty
+dy
*length
);
1888 oldpen
= SelectObject (hdc
, oldpen
);
1889 DeleteObject (oldpen
);
1891 for (i
= 0; i
< length
; i
++) {
1893 SetPixel(hdc
, startx
, starty
, colours
[23]);
1895 startx
+= dx
; starty
+= dy
;
1901 static int check_compose(int first
, int second
) {
1903 static char * composetbl
[] = {
1904 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1905 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1906 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1907 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1908 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1909 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1910 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1911 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1912 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1913 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1914 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1915 "\"uü", "'yý", "htþ", "\"yÿ",
1919 static int recurse
= 0;
1922 for(c
=composetbl
; *c
; c
++) {
1923 if( (*c
)[0] == first
&& (*c
)[1] == second
)
1925 return (*c
)[2] & 0xFF;
1932 nc
= check_compose(second
, first
);
1934 nc
= check_compose(toupper(first
), toupper(second
));
1936 nc
= check_compose(toupper(second
), toupper(first
));
1944 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1945 * codes. Returns number of bytes used or zero to drop the message
1946 * or -1 to forward the message to windows.
1948 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
1949 unsigned char *output
) {
1951 int scan
, left_alt
= 0, key_down
, shift_state
;
1953 unsigned char * p
= output
;
1954 static int alt_state
= 0;
1956 HKL kbd_layout
= GetKeyboardLayout(0);
1958 static WORD keys
[3];
1959 static int compose_char
= 0;
1960 static WPARAM compose_key
= 0;
1962 r
= GetKeyboardState(keystate
);
1963 if (!r
) memset(keystate
, 0, sizeof(keystate
));
1967 { /* Tell us all about key events */
1968 static BYTE oldstate
[256];
1969 static int first
= 1;
1972 if(first
) memcpy(oldstate
, keystate
, sizeof(oldstate
));
1975 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
) {
1977 } else if ((HIWORD(lParam
)&KF_UP
) && scan
==(HIWORD(lParam
) & 0xFF) ) {
1981 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
1982 debug(("K_F%d", wParam
+1-VK_F1
));
1985 case VK_SHIFT
: debug(("SHIFT")); break;
1986 case VK_CONTROL
: debug(("CTRL")); break;
1987 case VK_MENU
: debug(("ALT")); break;
1988 default: debug(("VK_%02x", wParam
));
1990 if(message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
1992 debug((", S%02x", scan
=(HIWORD(lParam
) & 0xFF) ));
1994 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
1995 if (ch
>=' ' && ch
<='~') debug((", '%c'", ch
));
1996 else if (ch
) debug((", $%02x", ch
));
1998 if (keys
[0]) debug((", KB0=%02x", keys
[0]));
1999 if (keys
[1]) debug((", KB1=%02x", keys
[1]));
2000 if (keys
[2]) debug((", KB2=%02x", keys
[2]));
2002 if ( (keystate
[VK_SHIFT
]&0x80)!=0) debug((", S"));
2003 if ( (keystate
[VK_CONTROL
]&0x80)!=0) debug((", C"));
2004 if ( (HIWORD(lParam
)&KF_EXTENDED
) ) debug((", E"));
2005 if ( (HIWORD(lParam
)&KF_UP
) ) debug((", U"));
2008 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
)
2010 else if ( (HIWORD(lParam
)&KF_UP
) )
2011 oldstate
[wParam
&0xFF] ^= 0x80;
2013 oldstate
[wParam
&0xFF] ^= 0x81;
2015 for(ch
=0; ch
<256; ch
++)
2016 if (oldstate
[ch
] != keystate
[ch
])
2017 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2019 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2023 if (wParam
== VK_MENU
&& (HIWORD(lParam
)&KF_EXTENDED
)) {
2024 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2028 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2029 if ( (cfg
.funky_type
== 3 ||
2030 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2031 && wParam
==VK_NUMLOCK
&& !(keystate
[VK_SHIFT
]&0x80)) {
2033 wParam
= VK_EXECUTE
;
2035 /* UnToggle NUMLock */
2036 if ((HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
2037 keystate
[VK_NUMLOCK
] ^= 1;
2040 /* And write back the 'adjusted' state */
2041 SetKeyboardState (keystate
);
2044 /* Disable Auto repeat if required */
2045 if (repeat_off
&& (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==KF_REPEAT
)
2048 if ((HIWORD(lParam
)&KF_ALTDOWN
) && (keystate
[VK_RMENU
]&0x80) == 0)
2051 key_down
= ((HIWORD(lParam
)&KF_UP
)==0);
2053 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2054 if (left_alt
&& (keystate
[VK_CONTROL
]&0x80)) {
2055 if (cfg
.ctrlaltkeys
)
2056 keystate
[VK_MENU
] = 0;
2058 keystate
[VK_RMENU
] = 0x80;
2063 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2064 shift_state
= ((keystate
[VK_SHIFT
]&0x80)!=0)
2065 + ((keystate
[VK_CONTROL
]&0x80)!=0)*2;
2067 /* Note if AltGr was pressed and if it was used as a compose key */
2068 if (!compose_state
) {
2069 compose_key
= 0x100;
2070 if (cfg
.compose_key
) {
2071 if (wParam
== VK_MENU
&& (HIWORD(lParam
)&KF_EXTENDED
))
2072 compose_key
= wParam
;
2074 if (wParam
== VK_APPS
)
2075 compose_key
= wParam
;
2078 if (wParam
== compose_key
)
2080 if (compose_state
== 0 && (HIWORD(lParam
)&(KF_UP
|KF_REPEAT
))==0)
2082 else if (compose_state
== 1 && (HIWORD(lParam
)&KF_UP
))
2087 else if (compose_state
==1 && wParam
!= VK_CONTROL
)
2091 * Record that we pressed key so the scroll window can be reset, but
2092 * be careful to avoid Shift-UP/Down
2094 if( wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
2098 /* Make sure we're not pasting */
2099 if (key_down
) term_nopaste();
2101 if (compose_state
>1 && left_alt
) compose_state
= 0;
2103 /* Sanitize the number pad if not using a PC NumPad */
2104 if( left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2105 && cfg
.funky_type
!= 2)
2106 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
)
2108 if ((HIWORD(lParam
)&KF_EXTENDED
) == 0)
2113 case VK_INSERT
: nParam
= VK_NUMPAD0
; break;
2114 case VK_END
: nParam
= VK_NUMPAD1
; break;
2115 case VK_DOWN
: nParam
= VK_NUMPAD2
; break;
2116 case VK_NEXT
: nParam
= VK_NUMPAD3
; break;
2117 case VK_LEFT
: nParam
= VK_NUMPAD4
; break;
2118 case VK_CLEAR
: nParam
= VK_NUMPAD5
; break;
2119 case VK_RIGHT
: nParam
= VK_NUMPAD6
; break;
2120 case VK_HOME
: nParam
= VK_NUMPAD7
; break;
2121 case VK_UP
: nParam
= VK_NUMPAD8
; break;
2122 case VK_PRIOR
: nParam
= VK_NUMPAD9
; break;
2123 case VK_DELETE
: nParam
= VK_DECIMAL
; break;
2127 if (keystate
[VK_NUMLOCK
]&1) shift_state
|= 1;
2133 /* If a key is pressed and AltGr is not active */
2134 if (key_down
&& (keystate
[VK_RMENU
]&0x80) == 0 && !compose_state
)
2136 /* Okay, prepare for most alts then ...*/
2137 if (left_alt
) *p
++ = '\033';
2139 /* Lets see if it's a pattern we know all about ... */
2140 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2141 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2144 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2145 SendMessage (hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
2148 if (wParam
== VK_INSERT
&& shift_state
== 1) {
2149 term_mouse (MB_PASTE
, MA_CLICK
, 0, 0);
2150 term_mouse (MB_PASTE
, MA_RELEASE
, 0, 0);
2153 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
2156 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
2158 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
2159 SendMessage (hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
2162 /* Control-Numlock for app-keypad mode switch */
2163 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
2164 app_keypad_keys
^= 1;
2168 /* Nethack keypad */
2169 if (cfg
.nethack_keypad
&& !left_alt
) {
2171 case VK_NUMPAD1
: *p
++ = shift_state ?
'B': 'b'; return p
-output
;
2172 case VK_NUMPAD2
: *p
++ = shift_state ?
'J': 'j'; return p
-output
;
2173 case VK_NUMPAD3
: *p
++ = shift_state ?
'N': 'n'; return p
-output
;
2174 case VK_NUMPAD4
: *p
++ = shift_state ?
'H': 'h'; return p
-output
;
2175 case VK_NUMPAD5
: *p
++ = shift_state ?
'.': '.'; return p
-output
;
2176 case VK_NUMPAD6
: *p
++ = shift_state ?
'L': 'l'; return p
-output
;
2177 case VK_NUMPAD7
: *p
++ = shift_state ?
'Y': 'y'; return p
-output
;
2178 case VK_NUMPAD8
: *p
++ = shift_state ?
'K': 'k'; return p
-output
;
2179 case VK_NUMPAD9
: *p
++ = shift_state ?
'U': 'u'; return p
-output
;
2183 /* Application Keypad */
2187 if ( cfg
.funky_type
== 3 ||
2188 ( cfg
.funky_type
<= 1 &&
2189 app_keypad_keys
&& !cfg
.no_applic_k
)) switch(wParam
) {
2190 case VK_EXECUTE
: xkey
= 'P'; break;
2191 case VK_DIVIDE
: xkey
= 'Q'; break;
2192 case VK_MULTIPLY
:xkey
= 'R'; break;
2193 case VK_SUBTRACT
:xkey
= 'S'; break;
2195 if(app_keypad_keys
&& !cfg
.no_applic_k
) switch(wParam
) {
2196 case VK_NUMPAD0
: xkey
= 'p'; break;
2197 case VK_NUMPAD1
: xkey
= 'q'; break;
2198 case VK_NUMPAD2
: xkey
= 'r'; break;
2199 case VK_NUMPAD3
: xkey
= 's'; break;
2200 case VK_NUMPAD4
: xkey
= 't'; break;
2201 case VK_NUMPAD5
: xkey
= 'u'; break;
2202 case VK_NUMPAD6
: xkey
= 'v'; break;
2203 case VK_NUMPAD7
: xkey
= 'w'; break;
2204 case VK_NUMPAD8
: xkey
= 'x'; break;
2205 case VK_NUMPAD9
: xkey
= 'y'; break;
2207 case VK_DECIMAL
: xkey
= 'n'; break;
2208 case VK_ADD
: if(cfg
.funky_type
==2) {
2209 if(shift_state
) xkey
= 'l';
2211 } else if(shift_state
) xkey
= 'm';
2215 case VK_DIVIDE
: if(cfg
.funky_type
==2) xkey
= 'o'; break;
2216 case VK_MULTIPLY
:if(cfg
.funky_type
==2) xkey
= 'j'; break;
2217 case VK_SUBTRACT
:if(cfg
.funky_type
==2) xkey
= 'm'; break;
2220 if (HIWORD(lParam
)&KF_EXTENDED
)
2228 if (xkey
>='P' && xkey
<='S')
2229 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
2231 p
+= sprintf((char *)p
, "\x1B?%c", xkey
);
2234 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
2239 if (wParam
== VK_BACK
&& shift_state
== 0 ) /* Backspace */
2241 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
2244 if (wParam
== VK_TAB
&& shift_state
== 1 ) /* Shift tab */
2246 *p
++ = 0x1B; *p
++ = '['; *p
++ = 'Z'; return p
- output
;
2248 if (wParam
== VK_SPACE
&& shift_state
== 2 ) /* Ctrl-Space */
2250 *p
++ = 0; return p
- output
;
2252 if (wParam
== VK_SPACE
&& shift_state
== 3 ) /* Ctrl-Shift-Space */
2254 *p
++ = 160; return p
- output
;
2256 if (wParam
== VK_CANCEL
&& shift_state
== 2 ) /* Ctrl-Break */
2258 *p
++ = 3; return p
- output
;
2260 if (wParam
== VK_PAUSE
) /* Break/Pause */
2262 *p
++ = 26; *p
++ = 0; return -2;
2264 /* Control-2 to Control-8 are special */
2265 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8')
2267 *p
++ = "\000\033\034\035\036\037\177"[wParam
-'2'];
2270 if (shift_state
== 2 && wParam
== 0xBD) {
2274 if (shift_state
== 2 && wParam
== 0xDF) {
2278 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
2279 *p
++ = '\r'; *p
++ = '\n';
2284 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2285 * for integer decimal nn.)
2287 * We also deal with the weird ones here. Linux VCs replace F1
2288 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2289 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2294 case VK_F1
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11); break;
2295 case VK_F2
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12); break;
2296 case VK_F3
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13); break;
2297 case VK_F4
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14); break;
2298 case VK_F5
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15); break;
2299 case VK_F6
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17); break;
2300 case VK_F7
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18); break;
2301 case VK_F8
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19); break;
2302 case VK_F9
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20); break;
2303 case VK_F10
: code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21); break;
2304 case VK_F11
: code
= 23; break;
2305 case VK_F12
: code
= 24; break;
2306 case VK_F13
: code
= 25; break;
2307 case VK_F14
: code
= 26; break;
2308 case VK_F15
: code
= 28; break;
2309 case VK_F16
: code
= 29; break;
2310 case VK_F17
: code
= 31; break;
2311 case VK_F18
: code
= 32; break;
2312 case VK_F19
: code
= 33; break;
2313 case VK_F20
: code
= 34; break;
2314 case VK_HOME
: code
= 1; break;
2315 case VK_INSERT
: code
= 2; break;
2316 case VK_DELETE
: code
= 3; break;
2317 case VK_END
: code
= 4; break;
2318 case VK_PRIOR
: code
= 5; break;
2319 case VK_NEXT
: code
= 6; break;
2321 /* Reorder edit keys to physical order */
2322 if (cfg
.funky_type
== 3 && code
<= 6 ) code
= "\0\2\1\4\5\3\6"[code
];
2324 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
2325 p
+= sprintf((char *)p
, "\x1B[[%c", code
+ 'A' - 11);
2328 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
2330 p
+= sprintf((char *)p
, "\x1B%c", code
+ 'P' - 11);
2332 p
+= sprintf((char *)p
, "\x1BO%c", code
+ 'P' - 11);
2335 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
2336 p
+= sprintf((char *)p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
2340 p
+= sprintf((char *)p
, "\x1B[%d~", code
);
2345 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2346 * some reason seems to send VK_CLEAR to Windows...).
2351 case VK_UP
: xkey
= 'A'; break;
2352 case VK_DOWN
: xkey
= 'B'; break;
2353 case VK_RIGHT
: xkey
= 'C'; break;
2354 case VK_LEFT
: xkey
= 'D'; break;
2355 case VK_CLEAR
: xkey
= 'G'; break;
2360 p
+= sprintf((char *)p
, "\x1B%c", xkey
);
2361 else if (app_cursor_keys
&& !cfg
.no_applic_c
)
2362 p
+= sprintf((char *)p
, "\x1BO%c", xkey
);
2364 p
+= sprintf((char *)p
, "\x1B[%c", xkey
);
2370 * Finally, deal with Return ourselves. (Win95 seems to
2371 * foul it up when Alt is pressed, for some reason.)
2373 if (wParam
== VK_RETURN
) /* Return */
2380 /* Okay we've done everything interesting; let windows deal with
2381 * the boring stuff */
2383 BOOL capsOn
=keystate
[VK_CAPITAL
] !=0;
2385 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2386 if(cfg
.xlat_capslockcyr
)
2387 keystate
[VK_CAPITAL
] = 0;
2389 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
2395 unsigned char ch
= (unsigned char)keys
[i
];
2397 if (compose_state
==2 && (ch
&0x80) == 0 && ch
>' ') {
2402 if (compose_state
==3 && (ch
&0x80) == 0 && ch
>' ') {
2406 if ((nc
=check_compose(compose_char
,ch
)) == -1)
2408 MessageBeep(MB_ICONHAND
);
2411 *p
++ = xlat_kbd2tty((unsigned char)nc
);
2417 if( left_alt
&& key_down
) *p
++ = '\033';
2423 ch
= xlat_latkbd2win(ch
);
2424 *p
++ = xlat_kbd2tty(ch
);
2428 /* This is so the ALT-Numpad and dead keys work correctly. */
2433 /* If we're definitly not building up an ALT-54321 then clear it */
2434 if (!left_alt
) keys
[0] = 0;
2437 /* ALT alone may or may not want to bring up the System menu */
2438 if (wParam
== VK_MENU
) {
2440 if (message
== WM_SYSKEYDOWN
)
2442 else if (message
== WM_SYSKEYUP
&& alt_state
)
2443 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
2444 if (message
== WM_SYSKEYUP
)
2454 void set_title (char *title
) {
2455 sfree (window_name
);
2456 window_name
= smalloc(1+strlen(title
));
2457 strcpy (window_name
, title
);
2458 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
2459 SetWindowText (hwnd
, title
);
2462 void set_icon (char *title
) {
2464 icon_name
= smalloc(1+strlen(title
));
2465 strcpy (icon_name
, title
);
2466 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
2467 SetWindowText (hwnd
, title
);
2470 void set_sbar (int total
, int start
, int page
) {
2473 if (!cfg
.scrollbar
) return;
2475 si
.cbSize
= sizeof(si
);
2476 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
2478 si
.nMax
= total
- 1;
2482 SetScrollInfo (hwnd
, SB_VERT
, &si
, TRUE
);
2485 Context
get_ctx(void) {
2490 SelectPalette (hdc
, pal
, FALSE
);
2496 void free_ctx (Context ctx
) {
2497 SelectPalette (ctx
, GetStockObject (DEFAULT_PALETTE
), FALSE
);
2498 ReleaseDC (hwnd
, ctx
);
2501 static void real_palette_set (int n
, int r
, int g
, int b
) {
2503 logpal
->palPalEntry
[n
].peRed
= r
;
2504 logpal
->palPalEntry
[n
].peGreen
= g
;
2505 logpal
->palPalEntry
[n
].peBlue
= b
;
2506 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
2507 colours
[n
] = PALETTERGB(r
, g
, b
);
2508 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2510 colours
[n
] = RGB(r
, g
, b
);
2513 void palette_set (int n
, int r
, int g
, int b
) {
2514 static const int first
[21] = {
2515 0, 2, 4, 6, 8, 10, 12, 14,
2516 1, 3, 5, 7, 9, 11, 13, 15,
2519 real_palette_set (first
[n
], r
, g
, b
);
2521 real_palette_set (first
[n
]+1, r
, g
, b
);
2523 HDC hdc
= get_ctx();
2524 UnrealizeObject (pal
);
2525 RealizePalette (hdc
);
2530 void palette_reset (void) {
2533 for (i
= 0; i
< NCOLOURS
; i
++) {
2535 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
2536 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
2537 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
2538 logpal
->palPalEntry
[i
].peFlags
= 0;
2539 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
2540 defpal
[i
].rgbtGreen
,
2541 defpal
[i
].rgbtBlue
);
2543 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
2544 defpal
[i
].rgbtGreen
,
2545 defpal
[i
].rgbtBlue
);
2550 SetPaletteEntries (pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2552 RealizePalette (hdc
);
2557 void write_clip (void *data
, int len
, int must_deselect
) {
2561 clipdata
= GlobalAlloc (GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
2564 lock
= GlobalLock (clipdata
);
2567 memcpy (lock
, data
, len
);
2568 ((unsigned char *) lock
) [len
] = 0;
2569 GlobalUnlock (clipdata
);
2572 SendMessage (hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
2574 if (OpenClipboard (hwnd
)) {
2576 SetClipboardData (CF_TEXT
, clipdata
);
2579 GlobalFree (clipdata
);
2582 SendMessage (hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
2585 void get_clip (void **p
, int *len
) {
2586 static HGLOBAL clipdata
= NULL
;
2590 GlobalUnlock (clipdata
);
2594 if (OpenClipboard (NULL
)) {
2595 clipdata
= GetClipboardData (CF_TEXT
);
2598 *p
= GlobalLock (clipdata
);
2612 * Move `lines' lines from position `from' to position `to' in the
2615 void optimised_move (int to
, int from
, int lines
) {
2619 min
= (to
< from ? to
: from
);
2620 max
= to
+ from
- min
;
2622 r
.left
= 0; r
.right
= cols
* font_width
;
2623 r
.top
= min
* font_height
; r
.bottom
= (max
+lines
) * font_height
;
2624 ScrollWindow (hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
2628 * Print a message box and perform a fatal exit.
2630 void fatalbox(char *fmt
, ...) {
2635 vsprintf(stuff
, fmt
, ap
);
2637 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
2644 void beep(int mode
) {
2645 if (mode
== BELL_DEFAULT
) {
2647 } else if (mode
== BELL_WAVEFILE
) {
2648 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
2649 char buf
[sizeof(cfg
.bell_wavefile
)+80];
2650 sprintf(buf
, "Unable to play sound file\n%s\n"
2651 "Using default sound instead", cfg
.bell_wavefile
);
2652 MessageBox(hwnd
, buf
, "PuTTY Sound Error", MB_OK
| MB_ICONEXCLAMATION
);
2653 cfg
.beep
= BELL_DEFAULT
;