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
,
62 unsigned char *output
);
63 static void cfgtopalette(void);
64 static void init_palette(void);
65 static void init_fonts(int);
67 static int extra_width
, extra_height
;
69 static int pending_netevent
= 0;
70 static WPARAM pend_netevent_wParam
= 0;
71 static LPARAM pend_netevent_lParam
= 0;
72 static void enact_pending_netevent(void);
74 static time_t last_movement
= 0;
78 #define FONT_UNDERLINE 2
79 #define FONT_BOLDUND 3
81 #define FONT_OEMBOLD 5
82 #define FONT_OEMBOLDUND 6
84 static HFONT fonts
[8];
85 static int font_needs_hand_underlining
;
87 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
95 static COLORREF colours
[NCOLOURS
];
97 static LPLOGPALETTE logpal
;
98 static RGBTRIPLE defpal
[NCOLOURS
];
102 static HBITMAP caretbm
;
104 static int dbltime
, lasttime
, lastact
;
105 static Mouse_Button lastbtn
;
107 /* this allows xterm-style mouse handling. */
108 static int send_raw_mouse
= 0;
109 static int wheel_accumulator
= 0;
111 static char *window_name
, *icon_name
;
113 static int compose_state
= 0;
115 /* Dummy routine, only required in plink. */
116 void ldisc_update(int echo
, int edit
)
120 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
122 static char appname
[] = "PuTTY";
127 int guess_width
, guess_height
;
130 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
132 winsock_ver
= MAKEWORD(1, 1);
133 if (WSAStartup(winsock_ver
, &wsadata
)) {
134 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
135 MB_OK
| MB_ICONEXCLAMATION
);
138 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
139 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
140 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
144 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
147 InitCommonControls();
149 /* Ensure a Maximize setting in Explorer doesn't maximise the
154 * Process the command line.
159 default_protocol
= DEFAULT_PROTOCOL
;
160 default_port
= DEFAULT_PORT
;
161 cfg
.logtype
= LGTYP_NONE
;
163 do_defaults(NULL
, &cfg
);
166 while (*p
&& isspace(*p
))
170 * Process command line options first. Yes, this can be
171 * done better, and it will be as soon as I have the
175 char *q
= p
+ strcspn(p
, " \t");
178 tolower(p
[0]) == 's' &&
179 tolower(p
[1]) == 's' && tolower(p
[2]) == 'h') {
180 default_protocol
= cfg
.protocol
= PROT_SSH
;
181 default_port
= cfg
.port
= 22;
182 } else if (q
== p
+ 7 &&
183 tolower(p
[0]) == 'c' &&
184 tolower(p
[1]) == 'l' &&
185 tolower(p
[2]) == 'e' &&
186 tolower(p
[3]) == 'a' &&
187 tolower(p
[4]) == 'n' &&
188 tolower(p
[5]) == 'u' && tolower(p
[6]) == 'p') {
190 * `putty -cleanup'. Remove all registry entries
191 * associated with PuTTY, and also find and delete
192 * the random seed file.
195 "This procedure will remove ALL Registry\n"
196 "entries associated with PuTTY, and will\n"
197 "also remove the PuTTY random seed file.\n"
199 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
200 "SESSIONS. Are you really sure you want\n"
203 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
208 p
= q
+ strspn(q
, " \t");
212 * An initial @ means to activate a saved session.
216 while (i
> 1 && isspace(p
[i
- 1]))
219 do_defaults(p
+ 1, &cfg
);
220 if (!*cfg
.host
&& !do_config()) {
224 } else if (*p
== '&') {
226 * An initial & means we've been given a command line
227 * containing the hex value of a HANDLE for a file
228 * mapping object, which we must then extract as a
233 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
234 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
235 0, 0, sizeof(Config
))) != NULL
) {
238 CloseHandle(filemap
);
239 } else if (!do_config()) {
246 * If the hostname starts with "telnet:", set the
247 * protocol to Telnet and process the string as a
250 if (!strncmp(q
, "telnet:", 7)) {
254 if (q
[0] == '/' && q
[1] == '/')
256 cfg
.protocol
= PROT_TELNET
;
258 while (*p
&& *p
!= ':' && *p
!= '/')
267 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
268 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
270 while (*p
&& !isspace(*p
))
274 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
275 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
276 while (*p
&& isspace(*p
))
290 /* See if host is of the form user@host */
291 if (cfg
.host
[0] != '\0') {
292 char *atsign
= strchr(cfg
.host
, '@');
293 /* Make sure we're not overflowing the user field */
295 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
296 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
297 cfg
.username
[atsign
- cfg
.host
] = '\0';
299 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
304 * Trim a colon suffix off the hostname if it's there.
306 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
310 * Select protocol. This is farmed out into a table in a
311 * separate file to enable an ssh-free variant.
316 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
317 if (backends
[i
].protocol
== cfg
.protocol
) {
318 back
= backends
[i
].backend
;
322 MessageBox(NULL
, "Unsupported protocol number found",
323 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
329 /* Check for invalid Port number (i.e. zero) */
331 MessageBox(NULL
, "Invalid Port Number",
332 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
339 wndclass
.lpfnWndProc
= WndProc
;
340 wndclass
.cbClsExtra
= 0;
341 wndclass
.cbWndExtra
= 0;
342 wndclass
.hInstance
= inst
;
343 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
344 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
345 wndclass
.hbrBackground
= GetStockObject(BLACK_BRUSH
);
346 wndclass
.lpszMenuName
= NULL
;
347 wndclass
.lpszClassName
= appname
;
349 RegisterClass(&wndclass
);
354 savelines
= cfg
.savelines
;
360 * Guess some defaults for the window size. This all gets
361 * updated later, so we don't really care too much. However, we
362 * do want the font width/height guesses to correspond to a
363 * large font rather than a small one...
370 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
371 guess_width
= extra_width
+ font_width
* cols
;
372 guess_height
= extra_height
+ font_height
* rows
;
375 HWND w
= GetDesktopWindow();
376 GetWindowRect(w
, &r
);
377 if (guess_width
> r
.right
- r
.left
)
378 guess_width
= r
.right
- r
.left
;
379 if (guess_height
> r
.bottom
- r
.top
)
380 guess_height
= r
.bottom
- r
.top
;
384 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
387 winmode
&= ~(WS_VSCROLL
);
389 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
391 exwinmode
|= WS_EX_TOPMOST
;
393 exwinmode
|= WS_EX_CLIENTEDGE
;
394 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
395 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
396 guess_width
, guess_height
,
397 NULL
, NULL
, inst
, NULL
);
401 * Initialise the fonts, simultaneously correcting the guesses
402 * for font_{width,height}.
404 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
409 * Correct the guesses for extra_{width,height}.
413 GetWindowRect(hwnd
, &wr
);
414 GetClientRect(hwnd
, &cr
);
415 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
416 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
420 * Resize the window, now we know what size we _really_ want it
423 guess_width
= extra_width
+ font_width
* cols
;
424 guess_height
= extra_height
+ font_height
* rows
;
425 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
426 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
427 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
430 * Set up a caret bitmap, with no content.
434 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
435 bits
= smalloc(size
);
436 memset(bits
, 0, size
);
437 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
440 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
443 * Initialise the scroll bar.
448 si
.cbSize
= sizeof(si
);
449 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
454 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
458 * Start up the telnet connection.
462 char msg
[1024], *title
;
465 error
= back
->init(cfg
.host
, cfg
.port
, &realhost
);
467 sprintf(msg
, "Unable to open connection to\n"
468 "%.800s\n" "%s", cfg
.host
, error
);
469 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
472 window_name
= icon_name
= NULL
;
474 title
= cfg
.wintitle
;
476 sprintf(msg
, "%s - PuTTY", realhost
);
483 session_closed
= FALSE
;
486 * Set up the input and output buffers.
489 outbuf_reap
= outbuf_head
= 0;
492 * Prepare the mouse handler.
494 lastact
= MA_NOTHING
;
495 lastbtn
= MBT_NOTHING
;
496 dbltime
= GetDoubleClickTime();
499 * Set up the session-control options on the system menu.
502 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
506 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
507 if (cfg
.protocol
== PROT_TELNET
) {
509 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
510 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
511 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
512 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
513 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
514 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
515 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
516 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
517 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
518 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
519 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
520 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
521 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
522 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
523 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
524 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
525 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
527 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
529 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
530 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
531 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
532 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
535 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
536 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
538 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
539 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
540 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
541 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
542 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
543 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
544 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
545 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
549 * Finally show the window!
551 ShowWindow(hwnd
, show
);
554 * Open the initial log file if there is one.
559 * Set the palette up.
565 has_focus
= (GetForegroundWindow() == hwnd
);
568 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
569 int timer_id
= 0, long_timer
= 0;
571 while (msg
.message
!= WM_QUIT
) {
572 /* Sometimes DispatchMessage calls routines that use their own
573 * GetMessage loop, setup this timer so we get some control back.
575 * Also call term_update() from the timer so that if the host
576 * is sending data flat out we still do redraws.
578 if (timer_id
&& long_timer
) {
579 KillTimer(hwnd
, timer_id
);
580 long_timer
= timer_id
= 0;
583 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
584 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
585 DispatchMessage(&msg
);
587 /* Make sure we blink everything that needs it. */
590 /* Send the paste buffer if there's anything to send */
593 /* If there's nothing new in the queue then we can do everything
594 * we've delayed, reading the socket, writing, and repainting
597 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
600 if (pending_netevent
) {
601 enact_pending_netevent();
603 /* Force the cursor blink on */
606 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
610 /* Okay there is now nothing to do so we make sure the screen is
611 * completely up to date then tell windows to call us in a little
615 KillTimer(hwnd
, timer_id
);
624 /* Hmm, term_update didn't want to do an update too soon ... */
625 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
627 timer_id
= SetTimer(hwnd
, 1, 2000, NULL
);
629 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
632 /* There's no point rescanning everything in the message queue
633 * so we do an apparently unnecessary wait here
636 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
646 for (i
= 0; i
< 8; i
++)
648 DeleteObject(fonts
[i
]);
655 if (cfg
.protocol
== PROT_SSH
) {
666 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
668 char *do_select(SOCKET skt
, int startup
)
673 events
= FD_READ
| FD_WRITE
| FD_OOB
| FD_CLOSE
;
678 return "do_select(): internal error (hwnd==NULL)";
679 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
680 switch (WSAGetLastError()) {
682 return "Network is down";
684 return "WSAAsyncSelect(): unknown error";
691 * set or clear the "raw mouse message" mode
693 void set_raw_mouse_mode(int activate
)
695 send_raw_mouse
= activate
;
696 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
700 * Print a message box and close the connection.
702 void connection_fatal(char *fmt
, ...)
708 vsprintf(stuff
, fmt
, ap
);
710 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
711 if (cfg
.close_on_exit
== COE_ALWAYS
)
714 session_closed
= TRUE
;
715 SetWindowText(hwnd
, "PuTTY (inactive)");
720 * Actually do the job requested by a WM_NETEVENT
722 static void enact_pending_netevent(void)
724 static int reentering
= 0;
725 extern int select_result(WPARAM
, LPARAM
);
729 return; /* don't unpend the pending */
731 pending_netevent
= FALSE
;
734 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
737 if (ret
== 0 && !session_closed
) {
738 /* Abnormal exits will already have set session_closed and taken
739 * appropriate action. */
740 if (cfg
.close_on_exit
== COE_ALWAYS
||
741 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
743 session_closed
= TRUE
;
744 SetWindowText(hwnd
, "PuTTY (inactive)");
745 MessageBox(hwnd
, "Connection closed by remote host",
746 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
752 * Copy the colour palette from the configuration data into defpal.
753 * This is non-trivial because the colour indices are different.
755 static void cfgtopalette(void)
758 static const int ww
[] = {
759 6, 7, 8, 9, 10, 11, 12, 13,
760 14, 15, 16, 17, 18, 19, 20, 21,
761 0, 1, 2, 3, 4, 4, 5, 5
764 for (i
= 0; i
< 24; i
++) {
766 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
767 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
768 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
773 * Set up the colour palette.
775 static void init_palette(void)
778 HDC hdc
= GetDC(hwnd
);
780 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
781 logpal
= smalloc(sizeof(*logpal
)
782 - sizeof(logpal
->palPalEntry
)
783 + NCOLOURS
* sizeof(PALETTEENTRY
));
784 logpal
->palVersion
= 0x300;
785 logpal
->palNumEntries
= NCOLOURS
;
786 for (i
= 0; i
< NCOLOURS
; i
++) {
787 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
788 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
789 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
790 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
792 pal
= CreatePalette(logpal
);
794 SelectPalette(hdc
, pal
, FALSE
);
796 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
799 ReleaseDC(hwnd
, hdc
);
802 for (i
= 0; i
< NCOLOURS
; i
++)
803 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
807 for (i
= 0; i
< NCOLOURS
; i
++)
808 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
809 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
813 * Initialise all the fonts we will need. There may be as many as
814 * eight or as few as one. We also:
816 * - check the font width and height, correcting our guesses if
819 * - verify that the bold font is the same width as the ordinary
820 * one, and engage shadow bolding if not.
822 * - verify that the underlined font is the same width as the
823 * ordinary one (manual underlining by means of line drawing can
824 * be done in a pinch).
826 static void init_fonts(int pick_width
)
832 int fw_dontcare
, fw_bold
;
838 for (i
= 0; i
< 8; i
++)
841 if (cfg
.fontisbold
) {
842 fw_dontcare
= FW_BOLD
;
845 fw_dontcare
= FW_DONTCARE
;
851 font_height
= cfg
.fontheight
;
852 if (font_height
> 0) {
854 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
856 font_width
= pick_width
;
859 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
860 c, OUT_DEFAULT_PRECIS, \
861 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
862 FIXED_PITCH | FF_DONTCARE, cfg.font)
864 if (cfg
.vtmode
!= VT_OEMONLY
) {
865 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
867 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
868 GetTextMetrics(hdc
, &tm
);
869 font_height
= tm
.tmHeight
;
870 font_width
= tm
.tmAveCharWidth
;
872 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
875 * Some fonts, e.g. 9-pt Courier, draw their underlines
876 * outside their character cell. We successfully prevent
877 * screen corruption by clipping the text output, but then
878 * we lose the underline completely. Here we try to work
879 * out whether this is such a font, and if it is, we set a
880 * flag that causes underlines to be drawn by hand.
882 * Having tried other more sophisticated approaches (such
883 * as examining the TEXTMETRIC structure or requesting the
884 * height of a string), I think we'll do this the brute
885 * force way: we create a small bitmap, draw an underlined
886 * space on it, and test to see whether any pixels are
887 * foreground-coloured. (Since we expect the underline to
888 * go all the way across the character cell, we only search
889 * down a single column of the bitmap, half way across.)
893 HBITMAP und_bm
, und_oldbm
;
897 und_dc
= CreateCompatibleDC(hdc
);
898 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
899 und_oldbm
= SelectObject(und_dc
, und_bm
);
900 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
901 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
902 SetTextColor(und_dc
, RGB(255, 255, 255));
903 SetBkColor(und_dc
, RGB(0, 0, 0));
904 SetBkMode(und_dc
, OPAQUE
);
905 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
907 for (i
= 0; i
< font_height
; i
++) {
908 c
= GetPixel(und_dc
, font_width
/ 2, i
);
909 if (c
!= RGB(0, 0, 0))
912 SelectObject(und_dc
, und_oldbm
);
913 DeleteObject(und_bm
);
915 font_needs_hand_underlining
= !gotit
;
918 if (bold_mode
== BOLD_FONT
) {
919 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
920 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
923 if (cfg
.vtmode
== VT_OEMANSI
) {
924 f(FONT_OEM
, OEM_CHARSET
, fw_dontcare
, FALSE
);
925 f(FONT_OEMUND
, OEM_CHARSET
, fw_dontcare
, TRUE
);
927 if (bold_mode
== BOLD_FONT
) {
928 f(FONT_OEMBOLD
, OEM_CHARSET
, fw_bold
, FALSE
);
929 f(FONT_OEMBOLDUND
, OEM_CHARSET
, fw_bold
, TRUE
);
933 f(FONT_OEM
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
935 SelectObject(hdc
, fonts
[FONT_OEM
]);
936 GetTextMetrics(hdc
, &tm
);
937 font_height
= tm
.tmHeight
;
938 font_width
= tm
.tmAveCharWidth
;
940 f(FONT_OEMUND
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
942 if (bold_mode
== BOLD_FONT
) {
943 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
944 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
949 descent
= tm
.tmAscent
+ 1;
950 if (descent
>= font_height
)
951 descent
= font_height
- 1;
952 firstchar
= tm
.tmFirstChar
;
954 for (i
= 0; i
< 8; i
++) {
956 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
957 fsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
964 ReleaseDC(hwnd
, hdc
);
966 /* ... This is wrong in OEM only mode */
967 if (fsize
[FONT_UNDERLINE
] != fsize
[FONT_NORMAL
] ||
968 (bold_mode
== BOLD_FONT
&&
969 fsize
[FONT_BOLDUND
] != fsize
[FONT_BOLD
])) {
971 DeleteObject(fonts
[FONT_UNDERLINE
]);
972 if (bold_mode
== BOLD_FONT
)
973 DeleteObject(fonts
[FONT_BOLDUND
]);
976 if (bold_mode
== BOLD_FONT
&& fsize
[FONT_BOLD
] != fsize
[FONT_NORMAL
]) {
977 bold_mode
= BOLD_SHADOW
;
978 DeleteObject(fonts
[FONT_BOLD
]);
979 if (und_mode
== UND_FONT
)
980 DeleteObject(fonts
[FONT_BOLDUND
]);
983 /* With the fascist font painting it doesn't matter if the linedraw font
984 * isn't exactly the right size anymore so we don't have to check this.
986 if (cfg
.vtmode
== VT_OEMANSI
&& fsize
[FONT_OEM
] != fsize
[FONT_NORMAL
]) {
987 if (cfg
.fontcharset
== OEM_CHARSET
) {
988 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
989 "different sizes. Using OEM-only mode instead",
990 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
991 cfg
.vtmode
= VT_OEMONLY
;
992 } else if (firstchar
< ' ') {
993 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
994 "different sizes. Using XTerm mode instead",
995 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
996 cfg
.vtmode
= VT_XWINDOWS
;
998 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
999 "different sizes. Using ISO8859-1 mode instead",
1000 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
1001 cfg
.vtmode
= VT_POORMAN
;
1004 for (i
= 0; i
< 8; i
++)
1006 DeleteObject(fonts
[i
]);
1012 void request_resize(int w
, int h
, int refont
)
1016 /* If the window is maximized supress resizing attempts */
1021 /* Don't do this in OEMANSI, you may get disable messages */
1022 if (refont
&& w
!= cols
&& (cols
== 80 || cols
== 132)
1023 && cfg
.vtmode
!= VT_OEMANSI
)
1025 if (refont
&& w
!= cols
&& (cols
== 80 || cols
== 132))
1028 /* If font width too big for screen should we shrink the font more ? */
1030 font_width
= ((font_width
* cols
+ w
/ 2) / w
);
1035 for (i
= 0; i
< 8; i
++)
1037 DeleteObject(fonts
[i
]);
1039 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1040 und_mode
= UND_FONT
;
1041 init_fonts(font_width
);
1043 static int first_time
= 1;
1046 switch (first_time
) {
1048 /* Get the size of the screen */
1049 if (GetClientRect(GetDesktopWindow(), &ss
))
1050 /* first_time = 0 */ ;
1056 /* Make sure the values are sane */
1057 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1058 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1071 width
= extra_width
+ font_width
* w
;
1072 height
= extra_height
+ font_height
* h
;
1074 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1075 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1076 SWP_NOMOVE
| SWP_NOZORDER
);
1079 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
)
1081 int thistime
= GetMessageTime();
1083 if (send_raw_mouse
) {
1084 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
);
1088 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1089 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1090 lastact
== MA_2CLK ? MA_3CLK
:
1091 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1096 if (lastact
!= MA_NOTHING
)
1097 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
);
1098 lasttime
= thistime
;
1102 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1103 * into a cooked one (SELECT, EXTEND, PASTE).
1105 Mouse_Button
translate_button(Mouse_Button button
)
1107 if (button
== MBT_LEFT
)
1109 if (button
== MBT_MIDDLE
)
1110 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1111 if (button
== MBT_RIGHT
)
1112 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1115 static void show_mouseptr(int show
)
1117 static int cursor_visible
= 1;
1118 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1120 if (cursor_visible
&& !show
)
1122 else if (!cursor_visible
&& show
)
1124 cursor_visible
= show
;
1127 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1128 WPARAM wParam
, LPARAM lParam
)
1131 static int ignore_size
= FALSE
;
1132 static int ignore_clip
= FALSE
;
1133 static int just_reconfigged
= FALSE
;
1134 static int resizing
= FALSE
;
1135 static int need_backend_resize
= FALSE
;
1139 if (pending_netevent
)
1140 enact_pending_netevent();
1147 if (cfg
.ping_interval
> 0) {
1150 if (now
- last_movement
> cfg
.ping_interval
) {
1151 back
->special(TS_PING
);
1152 last_movement
= now
;
1160 if (!cfg
.warn_on_close
|| session_closed
||
1162 "Are you sure you want to close this session?",
1163 "PuTTY Exit Confirmation",
1164 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1165 DestroyWindow(hwnd
);
1172 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1184 PROCESS_INFORMATION pi
;
1185 HANDLE filemap
= NULL
;
1187 if (wParam
== IDM_DUPSESS
) {
1189 * Allocate a file-mapping memory chunk for the
1192 SECURITY_ATTRIBUTES sa
;
1195 sa
.nLength
= sizeof(sa
);
1196 sa
.lpSecurityDescriptor
= NULL
;
1197 sa
.bInheritHandle
= TRUE
;
1198 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1201 0, sizeof(Config
), NULL
);
1203 p
= (Config
*) MapViewOfFile(filemap
,
1205 0, 0, sizeof(Config
));
1207 *p
= cfg
; /* structure copy */
1211 sprintf(c
, "putty &%p", filemap
);
1213 } else if (wParam
== IDM_SAVEDSESS
) {
1215 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1216 cl
= smalloc(16 + strlen(session
)); /* 8, but play safe */
1218 cl
= NULL
; /* not a very important failure mode */
1220 sprintf(cl
, "putty @%s", session
);
1226 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1228 si
.lpReserved
= NULL
;
1229 si
.lpDesktop
= NULL
;
1233 si
.lpReserved2
= NULL
;
1234 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1235 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1238 CloseHandle(filemap
);
1245 int prev_alwaysontop
= cfg
.alwaysontop
;
1246 int prev_sunken_edge
= cfg
.sunken_edge
;
1247 char oldlogfile
[FILENAME_MAX
];
1249 int need_setwpos
= FALSE
;
1250 int old_fwidth
, old_fheight
;
1252 strcpy(oldlogfile
, cfg
.logfilename
);
1253 oldlogtype
= cfg
.logtype
;
1256 old_fwidth
= font_width
;
1257 old_fheight
= font_height
;
1258 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1260 if (!do_reconfig(hwnd
))
1263 if (strcmp(oldlogfile
, cfg
.logfilename
) ||
1264 oldlogtype
!= cfg
.logtype
) {
1265 logfclose(); /* reset logging */
1269 just_reconfigged
= TRUE
;
1272 for (i
= 0; i
< 8; i
++)
1274 DeleteObject(fonts
[i
]);
1276 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1277 und_mode
= UND_FONT
;
1281 * Flush the line discipline's edit buffer in the
1282 * case where local editing has just been disabled.
1284 ldisc_send(NULL
, 0);
1292 /* Enable or disable the scroll bar, etc */
1294 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1295 LONG nexflag
, exflag
=
1296 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1299 if (cfg
.alwaysontop
!= prev_alwaysontop
) {
1300 if (cfg
.alwaysontop
) {
1301 nexflag
|= WS_EX_TOPMOST
;
1302 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1303 SWP_NOMOVE
| SWP_NOSIZE
);
1305 nexflag
&= ~(WS_EX_TOPMOST
);
1306 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1307 SWP_NOMOVE
| SWP_NOSIZE
);
1310 if (cfg
.sunken_edge
)
1311 nexflag
|= WS_EX_CLIENTEDGE
;
1313 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1319 nflg
&= ~WS_VSCROLL
;
1321 nflg
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1323 nflg
|= (WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1325 if (nflg
!= flag
|| nexflag
!= exflag
) {
1329 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1330 if (nexflag
!= exflag
)
1331 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1333 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
1335 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1336 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1337 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
1338 | SWP_FRAMECHANGED
);
1340 GetWindowRect(hwnd
, &wr
);
1341 GetClientRect(hwnd
, &cr
);
1343 wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1345 wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1349 if (cfg
.height
!= rows
||
1350 cfg
.width
!= cols
||
1351 old_fwidth
!= font_width
||
1352 old_fheight
!= font_height
||
1353 cfg
.savelines
!= savelines
||
1354 cfg
.sunken_edge
!= prev_sunken_edge
)
1355 need_setwpos
= TRUE
;
1356 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1357 InvalidateRect(hwnd
, NULL
, TRUE
);
1360 SetWindowPos(hwnd
, NULL
, 0, 0,
1361 extra_width
+ font_width
* cfg
.width
,
1362 extra_height
+ font_height
* cfg
.height
,
1363 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1364 SWP_NOMOVE
| SWP_NOZORDER
);
1366 set_title(cfg
.wintitle
);
1367 if (IsIconic(hwnd
)) {
1369 cfg
.win_name_always ? window_name
:
1384 back
->special(TS_AYT
);
1387 back
->special(TS_BRK
);
1390 back
->special(TS_SYNCH
);
1393 back
->special(TS_EC
);
1396 back
->special(TS_EL
);
1399 back
->special(TS_GA
);
1402 back
->special(TS_NOP
);
1405 back
->special(TS_ABORT
);
1408 back
->special(TS_AO
);
1411 back
->special(TS_IP
);
1414 back
->special(TS_SUSP
);
1417 back
->special(TS_EOR
);
1420 back
->special(TS_EOF
);
1426 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1427 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1432 #define X_POS(l) ((int)(short)LOWORD(l))
1433 #define Y_POS(l) ((int)(short)HIWORD(l))
1435 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1436 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1437 #define WHEEL_DELTA 120
1440 wheel_accumulator
+= (short) HIWORD(wParam
);
1441 wParam
= LOWORD(wParam
);
1443 /* process events when the threshold is reached */
1444 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
1447 /* reduce amount for next time */
1448 if (wheel_accumulator
> 0) {
1450 wheel_accumulator
-= WHEEL_DELTA
;
1451 } else if (wheel_accumulator
< 0) {
1453 wheel_accumulator
+= WHEEL_DELTA
;
1457 if (send_raw_mouse
) {
1458 /* send a mouse-down followed by a mouse up */
1461 TO_CHR_X(X_POS(lParam
)),
1462 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1463 wParam
& MK_CONTROL
);
1464 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1465 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1466 wParam
& MK_CONTROL
);
1468 /* trigger a scroll */
1470 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
1475 case WM_LBUTTONDOWN
:
1476 case WM_MBUTTONDOWN
:
1477 case WM_RBUTTONDOWN
:
1484 case WM_LBUTTONDOWN
:
1488 case WM_MBUTTONDOWN
:
1489 button
= MBT_MIDDLE
;
1492 case WM_RBUTTONDOWN
:
1501 button
= MBT_MIDDLE
;
1512 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1513 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
);
1516 term_mouse(button
, MA_RELEASE
,
1517 TO_CHR_X(X_POS(lParam
)),
1518 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1519 wParam
& MK_CONTROL
);
1527 * Add the mouse position and message time to the random
1530 noise_ultralight(lParam
);
1532 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1534 if (wParam
& MK_LBUTTON
)
1536 else if (wParam
& MK_MBUTTON
)
1537 b
= cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1539 b
= cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1540 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1541 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1542 wParam
& MK_CONTROL
);
1545 case WM_NCMOUSEMOVE
:
1547 noise_ultralight(lParam
);
1549 case WM_IGNORE_CLIP
:
1550 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1552 case WM_DESTROYCLIPBOARD
:
1555 ignore_clip
= FALSE
;
1561 hdc
= BeginPaint(hwnd
, &p
);
1563 SelectPalette(hdc
, pal
, TRUE
);
1564 RealizePalette(hdc
);
1566 term_paint(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1567 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1568 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
1569 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
1575 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1576 * but the only one that's likely to try to overload us is FD_READ.
1577 * This means buffering just one is fine.
1579 if (pending_netevent
)
1580 enact_pending_netevent();
1582 pending_netevent
= TRUE
;
1583 pend_netevent_wParam
= wParam
;
1584 pend_netevent_lParam
= lParam
;
1585 time(&last_movement
);
1589 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
1602 case WM_IGNORE_SIZE
:
1603 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1605 case WM_ENTERSIZEMOVE
:
1608 need_backend_resize
= FALSE
;
1610 case WM_EXITSIZEMOVE
:
1613 if (need_backend_resize
)
1618 int width
, height
, w
, h
, ew
, eh
;
1619 LPRECT r
= (LPRECT
) lParam
;
1621 width
= r
->right
- r
->left
- extra_width
;
1622 height
= r
->bottom
- r
->top
- extra_height
;
1623 w
= (width
+ font_width
/ 2) / font_width
;
1626 h
= (height
+ font_height
/ 2) / font_height
;
1629 UpdateSizeTip(hwnd
, w
, h
);
1630 ew
= width
- w
* font_width
;
1631 eh
= height
- h
* font_height
;
1633 if (wParam
== WMSZ_LEFT
||
1634 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
1640 if (wParam
== WMSZ_TOP
||
1641 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
1651 /* break; (never reached) */
1653 if (wParam
== SIZE_MINIMIZED
) {
1655 cfg
.win_name_always ? window_name
: icon_name
);
1658 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1659 SetWindowText(hwnd
, window_name
);
1661 int width
, height
, w
, h
;
1662 #if 0 /* we have fixed this using WM_SIZING now */
1666 width
= LOWORD(lParam
);
1667 height
= HIWORD(lParam
);
1668 w
= width
/ font_width
;
1671 h
= height
/ font_height
;
1674 #if 0 /* we have fixed this using WM_SIZING now */
1675 ew
= width
- w
* font_width
;
1676 eh
= height
- h
* font_height
;
1677 if (ew
!= 0 || eh
!= 0) {
1679 GetWindowRect(hwnd
, &r
);
1680 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
1681 SetWindowPos(hwnd
, NULL
, 0, 0,
1682 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1683 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1686 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1688 term_size(h
, w
, cfg
.savelines
);
1690 * Don't call back->size in mid-resize. (To prevent
1691 * massive numbers of resize events getting sent
1692 * down the connection during an NT opaque drag.)
1697 need_backend_resize
= TRUE
;
1698 just_reconfigged
= FALSE
;
1701 ignore_size
= FALSE
;
1704 switch (LOWORD(wParam
)) {
1718 term_scroll(0, +rows
/ 2);
1721 term_scroll(0, -rows
/ 2);
1723 case SB_THUMBPOSITION
:
1725 term_scroll(1, HIWORD(wParam
));
1729 case WM_PALETTECHANGED
:
1730 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1731 HDC hdc
= get_ctx();
1733 if (RealizePalette(hdc
) > 0)
1739 case WM_QUERYNEWPALETTE
:
1741 HDC hdc
= get_ctx();
1743 if (RealizePalette(hdc
) > 0)
1755 * Add the scan code and keypress timing to the random
1758 noise_ultralight(lParam
);
1761 * We don't do TranslateMessage since it disassociates the
1762 * resulting CHAR message from the KEYDOWN that sparked it,
1763 * which we occasionally don't want. Instead, we process
1764 * KEYDOWN, and call the Win32 translator functions so that
1765 * we get the translations under _our_ control.
1768 unsigned char buf
[20];
1771 if (wParam
== VK_PROCESSKEY
) {
1774 m
.message
= WM_KEYDOWN
;
1776 m
.lParam
= lParam
& 0xdfff;
1777 TranslateMessage(&m
);
1779 len
= TranslateKey(message
, wParam
, lParam
, buf
);
1781 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1782 ldisc_send(buf
, len
);
1791 unsigned char buf
[2];
1794 buf
[0] = wParam
>> 8;
1800 * Nevertheless, we are prepared to deal with WM_CHAR
1801 * messages, should they crop up. So if someone wants to
1802 * post the things to us as part of a macro manoeuvre,
1803 * we're ready to cope.
1806 char c
= xlat_kbd2tty((unsigned char) wParam
);
1811 if (send_raw_mouse
) {
1812 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
1817 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1821 * Move the system caret. (We maintain one, even though it's
1822 * invisible, for the benefit of blind people: apparently some
1823 * helper software tracks the system caret, so we should arrange to
1826 void sys_cursor(int x
, int y
)
1829 SetCaretPos(x
* font_width
, y
* font_height
);
1833 * Draw a line of text in the window, at given character
1834 * coordinates, in given attributes.
1836 * We are allowed to fiddle with the contents of `text'.
1838 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
1839 unsigned long attr
, int lattr
)
1842 int nfg
, nbg
, nfont
;
1845 int force_manual_underline
= 0;
1846 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
1847 static int *IpDx
= 0, IpDxLEN
= 0;;
1849 if (len
> IpDxLEN
|| IpDx
[0] != fnt_width
) {
1851 if (len
> IpDxLEN
) {
1853 IpDx
= smalloc((len
+ 16) * sizeof(int));
1854 IpDxLEN
= (len
+ 16);
1856 for (i
= 0; i
< IpDxLEN
; i
++)
1857 IpDx
[i
] = fnt_width
;
1863 if ((attr
& ATTR_ACTCURS
) && cfg
.cursor_type
== 0) {
1864 attr
&= (bold_mode
== BOLD_COLOURS ?
0x300200 : 0x300300);
1865 attr
^= ATTR_CUR_XOR
;
1869 if (cfg
.vtmode
== VT_OEMONLY
)
1873 * Map high-half characters in order to approximate ISO using
1874 * OEM character set. No characters are missing if the OEM codepage
1877 if (nfont
& FONT_OEM
) {
1879 for (i
= 0; i
< len
; i
++)
1880 if (text
[i
] >= '\xA0' && text
[i
] <= '\xFF') {
1882 /* This is CP850 ... perfect translation */
1883 static const char oemhighhalf
[] = "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1884 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1885 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1886 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1887 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1888 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1889 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1890 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1891 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1892 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1893 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1894 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1897 /* This is CP437 ... junk translation */
1898 static const unsigned char oemhighhalf
[] = {
1899 0x20, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1900 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1901 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1902 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1903 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1904 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1905 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1906 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1907 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1908 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1909 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1910 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1913 text
[i
] = oemhighhalf
[(unsigned char) text
[i
] - 0xA0];
1917 if (attr
& ATTR_LINEDRW
) {
1920 static const char poorman
[] =
1921 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1924 static const char oemmap_437
[] =
1925 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1926 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1929 static const char oemmap_850
[] =
1930 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1931 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1933 /* Poor windows font ... eg: windows courier */
1934 static const char oemmap
[] =
1935 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1936 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1939 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1940 * VT100 line drawing chars; everything else stays normal.
1942 * Actually '_' maps to space too, but that's done before.
1944 switch (cfg
.vtmode
) {
1946 for (i
= 0; i
< len
; i
++)
1947 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1948 text
[i
] += '\x01' - '\x60';
1951 /* Make sure we actually have an OEM font */
1952 if (fonts
[nfont
| FONT_OEM
]) {
1955 for (i
= 0; i
< len
; i
++)
1956 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1957 text
[i
] = oemmap
[(unsigned char) text
[i
] - 0x60];
1961 for (i
= 0; i
< len
; i
++)
1962 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1963 text
[i
] = poorman
[(unsigned char) text
[i
] - 0x60];
1968 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
1969 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
1970 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
1972 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
1973 nfont
|= FONT_UNDERLINE
;
1974 if (!fonts
[nfont
]) {
1975 if (nfont
& FONT_UNDERLINE
)
1976 force_manual_underline
= 1;
1977 /* Don't do the same for manual bold, it could be bad news. */
1979 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
1981 if (font_needs_hand_underlining
&& (attr
& ATTR_UNDER
))
1982 force_manual_underline
= 1;
1983 if (attr
& ATTR_REVERSE
) {
1988 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
1990 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
1994 SelectObject(hdc
, fonts
[nfont
]);
1995 SetTextColor(hdc
, fg
);
1996 SetBkColor(hdc
, bg
);
1997 SetBkMode(hdc
, OPAQUE
);
2000 line_box
.right
= x
+ fnt_width
* len
;
2001 line_box
.bottom
= y
+ font_height
;
2002 ExtTextOut(hdc
, x
, y
, ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
,
2004 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2005 SetBkMode(hdc
, TRANSPARENT
);
2007 /* GRR: This draws the character outside it's box and can leave
2008 * 'droppings' even with the clip box! I suppose I could loop it
2009 * one character at a time ... yuk.
2011 * Or ... I could do a test print with "W", and use +1 or -1 for this
2012 * shift depending on if the leftmost column is blank...
2014 ExtTextOut(hdc
, x
- 1, y
, ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2016 if (force_manual_underline
||
2017 (und_mode
== UND_LINE
&& (attr
& ATTR_UNDER
))) {
2019 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2020 MoveToEx(hdc
, x
, y
+ descent
, NULL
);
2021 LineTo(hdc
, x
+ len
* fnt_width
, y
+ descent
);
2022 oldpen
= SelectObject(hdc
, oldpen
);
2023 DeleteObject(oldpen
);
2025 if ((attr
& ATTR_PASCURS
) && cfg
.cursor_type
== 0) {
2028 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2029 pts
[2].x
= pts
[3].x
= x
+ fnt_width
- 1;
2030 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2031 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2032 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2033 Polyline(hdc
, pts
, 5);
2034 oldpen
= SelectObject(hdc
, oldpen
);
2035 DeleteObject(oldpen
);
2037 if ((attr
& (ATTR_ACTCURS
| ATTR_PASCURS
)) && cfg
.cursor_type
!= 0) {
2038 int startx
, starty
, dx
, dy
, length
, i
;
2039 if (cfg
.cursor_type
== 1) {
2041 starty
= y
+ descent
;
2047 if (attr
& ATTR_RIGHTCURS
)
2048 xadjust
= fnt_width
- 1;
2049 startx
= x
+ xadjust
;
2053 length
= font_height
;
2055 if (attr
& ATTR_ACTCURS
) {
2058 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2059 MoveToEx(hdc
, startx
, starty
, NULL
);
2060 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2061 oldpen
= SelectObject(hdc
, oldpen
);
2062 DeleteObject(oldpen
);
2064 for (i
= 0; i
< length
; i
++) {
2066 SetPixel(hdc
, startx
, starty
, colours
[23]);
2075 static int check_compose(int first
, int second
)
2078 static char *composetbl
[] = {
2079 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡",
2081 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§",
2083 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
2084 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·",
2086 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ",
2088 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
2089 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
2090 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
2091 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ",
2093 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð",
2095 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú",
2097 "\"uü", "'yý", "htþ", "\"yÿ",
2102 static int recurse
= 0;
2105 for (c
= composetbl
; *c
; c
++) {
2106 if ((*c
)[0] == first
&& (*c
)[1] == second
) {
2107 return (*c
)[2] & 0xFF;
2113 nc
= check_compose(second
, first
);
2115 nc
= check_compose(toupper(first
), toupper(second
));
2117 nc
= check_compose(toupper(second
), toupper(first
));
2125 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2126 * codes. Returns number of bytes used or zero to drop the message
2127 * or -1 to forward the message to windows.
2129 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2130 unsigned char *output
)
2133 int scan
, left_alt
= 0, key_down
, shift_state
;
2135 unsigned char *p
= output
;
2136 static int alt_state
= 0;
2138 HKL kbd_layout
= GetKeyboardLayout(0);
2140 static WORD keys
[3];
2141 static int compose_char
= 0;
2142 static WPARAM compose_key
= 0;
2144 r
= GetKeyboardState(keystate
);
2146 memset(keystate
, 0, sizeof(keystate
));
2149 { /* Tell us all about key events */
2150 static BYTE oldstate
[256];
2151 static int first
= 1;
2155 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2158 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2160 } else if ((HIWORD(lParam
) & KF_UP
)
2161 && scan
== (HIWORD(lParam
) & 0xFF)) {
2165 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2166 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2179 debug(("VK_%02x", wParam
));
2181 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2183 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2185 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2186 if (ch
>= ' ' && ch
<= '~')
2187 debug((", '%c'", ch
));
2189 debug((", $%02x", ch
));
2192 debug((", KB0=%02x", keys
[0]));
2194 debug((", KB1=%02x", keys
[1]));
2196 debug((", KB2=%02x", keys
[2]));
2198 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2200 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2202 if ((HIWORD(lParam
) & KF_EXTENDED
))
2204 if ((HIWORD(lParam
) & KF_UP
))
2208 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2209 else if ((HIWORD(lParam
) & KF_UP
))
2210 oldstate
[wParam
& 0xFF] ^= 0x80;
2212 oldstate
[wParam
& 0xFF] ^= 0x81;
2214 for (ch
= 0; ch
< 256; ch
++)
2215 if (oldstate
[ch
] != keystate
[ch
])
2216 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2218 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2222 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2223 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2227 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2228 if ((cfg
.funky_type
== 3 ||
2229 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2230 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2232 wParam
= VK_EXECUTE
;
2234 /* UnToggle NUMLock */
2235 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2236 keystate
[VK_NUMLOCK
] ^= 1;
2239 /* And write back the 'adjusted' state */
2240 SetKeyboardState(keystate
);
2243 /* Disable Auto repeat if required */
2244 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2247 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2250 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2252 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2253 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2254 if (cfg
.ctrlaltkeys
)
2255 keystate
[VK_MENU
] = 0;
2257 keystate
[VK_RMENU
] = 0x80;
2262 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2263 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2264 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2266 /* Note if AltGr was pressed and if it was used as a compose key */
2267 if (!compose_state
) {
2268 compose_key
= 0x100;
2269 if (cfg
.compose_key
) {
2270 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2271 compose_key
= wParam
;
2273 if (wParam
== VK_APPS
)
2274 compose_key
= wParam
;
2277 if (wParam
== compose_key
) {
2278 if (compose_state
== 0
2279 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2281 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2285 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
2289 * Record that we pressed key so the scroll window can be reset, but
2290 * be careful to avoid Shift-UP/Down
2292 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
2296 /* Make sure we're not pasting */
2300 if (compose_state
> 1 && left_alt
)
2303 /* Sanitize the number pad if not using a PC NumPad */
2304 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2305 && cfg
.funky_type
!= 2)
2306 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
2307 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
2311 nParam
= VK_NUMPAD0
;
2314 nParam
= VK_NUMPAD1
;
2317 nParam
= VK_NUMPAD2
;
2320 nParam
= VK_NUMPAD3
;
2323 nParam
= VK_NUMPAD4
;
2326 nParam
= VK_NUMPAD5
;
2329 nParam
= VK_NUMPAD6
;
2332 nParam
= VK_NUMPAD7
;
2335 nParam
= VK_NUMPAD8
;
2338 nParam
= VK_NUMPAD9
;
2341 nParam
= VK_DECIMAL
;
2345 if (keystate
[VK_NUMLOCK
] & 1)
2352 /* If a key is pressed and AltGr is not active */
2353 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
2354 /* Okay, prepare for most alts then ... */
2358 /* Lets see if it's a pattern we know all about ... */
2359 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2360 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2363 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2364 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
2367 if (wParam
== VK_INSERT
&& shift_state
== 1) {
2368 term_mouse(MBT_PASTE
, MA_CLICK
, 0, 0, 0, 0);
2369 term_mouse(MBT_PASTE
, MA_RELEASE
, 0, 0, 0, 0);
2372 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
2375 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
2377 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
2378 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
2381 /* Control-Numlock for app-keypad mode switch */
2382 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
2383 app_keypad_keys
^= 1;
2387 /* Nethack keypad */
2388 if (cfg
.nethack_keypad
&& !left_alt
) {
2391 *p
++ = shift_state ?
'B' : 'b';
2394 *p
++ = shift_state ?
'J' : 'j';
2397 *p
++ = shift_state ?
'N' : 'n';
2400 *p
++ = shift_state ?
'H' : 'h';
2403 *p
++ = shift_state ?
'.' : '.';
2406 *p
++ = shift_state ?
'L' : 'l';
2409 *p
++ = shift_state ?
'Y' : 'y';
2412 *p
++ = shift_state ?
'K' : 'k';
2415 *p
++ = shift_state ?
'U' : 'u';
2420 /* Application Keypad */
2424 if (cfg
.funky_type
== 3 ||
2425 (cfg
.funky_type
<= 1 &&
2426 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
2440 if (app_keypad_keys
&& !cfg
.no_applic_k
)
2477 if (cfg
.funky_type
== 2) {
2482 } else if (shift_state
)
2489 if (cfg
.funky_type
== 2)
2493 if (cfg
.funky_type
== 2)
2497 if (cfg
.funky_type
== 2)
2502 if (HIWORD(lParam
) & KF_EXTENDED
)
2508 if (xkey
>= 'P' && xkey
<= 'S')
2509 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
2511 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
2513 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
2518 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
2519 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
2522 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
2528 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
2532 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
2536 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
2540 if (wParam
== VK_PAUSE
) { /* Break/Pause */
2545 /* Control-2 to Control-8 are special */
2546 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
2547 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
2550 if (shift_state
== 2 && wParam
== 0xBD) {
2554 if (shift_state
== 2 && wParam
== 0xDF) {
2558 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
2565 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2566 * for integer decimal nn.)
2568 * We also deal with the weird ones here. Linux VCs replace F1
2569 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2570 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2576 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
2579 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
2582 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
2585 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
2588 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
2591 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
2594 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
2597 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
2600 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
2603 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
2654 /* Reorder edit keys to physical order */
2655 if (cfg
.funky_type
== 3 && code
<= 6)
2656 code
= "\0\2\1\4\5\3\6"[code
];
2658 if (vt52_mode
&& code
> 0 && code
<= 6) {
2659 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
2663 if (cfg
.funky_type
== 5 && code
>= 11 && code
<= 24) {
2664 p
+= sprintf((char *) p
, "\x1B[%c", code
+ 'M' - 11);
2667 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
2674 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
2677 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
2680 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
2681 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
2684 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
2686 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
2688 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
2691 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
2692 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
2696 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
2701 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2702 * some reason seems to send VK_CLEAR to Windows...).
2725 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
2726 else if (app_cursor_keys
&& !cfg
.no_applic_c
)
2727 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
2729 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
2735 * Finally, deal with Return ourselves. (Win95 seems to
2736 * foul it up when Alt is pressed, for some reason.)
2738 if (wParam
== VK_RETURN
) { /* Return */
2744 /* Okay we've done everything interesting; let windows deal with
2745 * the boring stuff */
2747 BOOL capsOn
= keystate
[VK_CAPITAL
] != 0;
2749 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2750 if (cfg
.xlat_capslockcyr
)
2751 keystate
[VK_CAPITAL
] = 0;
2753 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
2756 for (i
= 0; i
< r
; i
++) {
2757 unsigned char ch
= (unsigned char) keys
[i
];
2759 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
2764 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
2768 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
2769 MessageBeep(MB_ICONHAND
);
2772 *p
++ = xlat_kbd2tty((unsigned char) nc
);
2778 if (left_alt
&& key_down
)
2784 ch
= xlat_latkbd2win(ch
);
2785 *p
++ = xlat_kbd2tty(ch
);
2789 /* This is so the ALT-Numpad and dead keys work correctly. */
2794 /* If we're definitly not building up an ALT-54321 then clear it */
2799 /* ALT alone may or may not want to bring up the System menu */
2800 if (wParam
== VK_MENU
) {
2802 if (message
== WM_SYSKEYDOWN
)
2804 else if (message
== WM_SYSKEYUP
&& alt_state
)
2805 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
2806 if (message
== WM_SYSKEYUP
)
2816 void set_title(char *title
)
2819 window_name
= smalloc(1 + strlen(title
));
2820 strcpy(window_name
, title
);
2821 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
2822 SetWindowText(hwnd
, title
);
2825 void set_icon(char *title
)
2828 icon_name
= smalloc(1 + strlen(title
));
2829 strcpy(icon_name
, title
);
2830 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
2831 SetWindowText(hwnd
, title
);
2834 void set_sbar(int total
, int start
, int page
)
2841 si
.cbSize
= sizeof(si
);
2842 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
2844 si
.nMax
= total
- 1;
2848 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
2851 Context
get_ctx(void)
2857 SelectPalette(hdc
, pal
, FALSE
);
2863 void free_ctx(Context ctx
)
2865 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
2866 ReleaseDC(hwnd
, ctx
);
2869 static void real_palette_set(int n
, int r
, int g
, int b
)
2872 logpal
->palPalEntry
[n
].peRed
= r
;
2873 logpal
->palPalEntry
[n
].peGreen
= g
;
2874 logpal
->palPalEntry
[n
].peBlue
= b
;
2875 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
2876 colours
[n
] = PALETTERGB(r
, g
, b
);
2877 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2879 colours
[n
] = RGB(r
, g
, b
);
2882 void palette_set(int n
, int r
, int g
, int b
)
2884 static const int first
[21] = {
2885 0, 2, 4, 6, 8, 10, 12, 14,
2886 1, 3, 5, 7, 9, 11, 13, 15,
2889 real_palette_set(first
[n
], r
, g
, b
);
2891 real_palette_set(first
[n
] + 1, r
, g
, b
);
2893 HDC hdc
= get_ctx();
2894 UnrealizeObject(pal
);
2895 RealizePalette(hdc
);
2900 void palette_reset(void)
2904 for (i
= 0; i
< NCOLOURS
; i
++) {
2906 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
2907 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
2908 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
2909 logpal
->palPalEntry
[i
].peFlags
= 0;
2910 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
2911 defpal
[i
].rgbtGreen
,
2912 defpal
[i
].rgbtBlue
);
2914 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
2915 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
2920 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2922 RealizePalette(hdc
);
2927 void write_clip(void *data
, int len
, int must_deselect
)
2932 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
2935 lock
= GlobalLock(clipdata
);
2938 memcpy(lock
, data
, len
);
2939 ((unsigned char *) lock
)[len
] = 0;
2940 GlobalUnlock(clipdata
);
2943 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
2945 if (OpenClipboard(hwnd
)) {
2947 SetClipboardData(CF_TEXT
, clipdata
);
2950 GlobalFree(clipdata
);
2953 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
2956 void get_clip(void **p
, int *len
)
2958 static HGLOBAL clipdata
= NULL
;
2962 GlobalUnlock(clipdata
);
2966 if (OpenClipboard(NULL
)) {
2967 clipdata
= GetClipboardData(CF_TEXT
);
2970 *p
= GlobalLock(clipdata
);
2984 * Move `lines' lines from position `from' to position `to' in the
2987 void optimised_move(int to
, int from
, int lines
)
2992 min
= (to
< from ? to
: from
);
2993 max
= to
+ from
- min
;
2996 r
.right
= cols
* font_width
;
2997 r
.top
= min
* font_height
;
2998 r
.bottom
= (max
+ lines
) * font_height
;
2999 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
3003 * Print a message box and perform a fatal exit.
3005 void fatalbox(char *fmt
, ...)
3011 vsprintf(stuff
, fmt
, ap
);
3013 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
3022 if (mode
== BELL_DEFAULT
) {
3024 * For MessageBeep style bells, we want to be careful of
3025 * timing, because they don't have the nice property of
3026 * PlaySound bells that each one cancels the previous
3027 * active one. So we limit the rate to one per 50ms or so.
3029 static long lastbeep
= 0;
3032 now
= GetTickCount();
3033 beepdiff
= now
- lastbeep
;
3034 if (beepdiff
>= 0 && beepdiff
< 50)
3038 } else if (mode
== BELL_WAVEFILE
) {
3039 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
3040 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
3041 sprintf(buf
, "Unable to play sound file\n%s\n"
3042 "Using default sound instead", cfg
.bell_wavefile
);
3043 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
3044 MB_OK
| MB_ICONEXCLAMATION
);
3045 cfg
.beep
= BELL_DEFAULT
;