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 /* Needed for mouse wheel support and not defined in earlier SDKs. */
62 #define WM_MOUSEWHEEL 0x020A
65 static LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
66 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
67 unsigned char *output
);
68 static void cfgtopalette(void);
69 static void init_palette(void);
70 static void init_fonts(int);
71 static void another_font(int);
72 static void deinit_fonts(void);
74 static int extra_width
, extra_height
;
76 static int pending_netevent
= 0;
77 static WPARAM pend_netevent_wParam
= 0;
78 static LPARAM pend_netevent_lParam
= 0;
79 static void enact_pending_netevent(void);
80 static void flash_window(int mode
);
82 static time_t last_movement
= 0;
86 #define FONT_UNDERLINE 2
87 #define FONT_BOLDUND 3
88 #define FONT_WIDE 0x04
89 #define FONT_HIGH 0x08
90 #define FONT_NARROW 0x10
92 #define FONT_OEMBOLD 0x21
93 #define FONT_OEMUND 0x22
94 #define FONT_OEMBOLDUND 0x23
95 #define FONT_MSGOTHIC 0x40
96 #define FONT_MINGLIU 0x60
97 #define FONT_GULIMCHE 0x80
98 #define FONT_MAXNO 0x8F
100 static HFONT fonts
[FONT_MAXNO
];
101 static int fontflag
[FONT_MAXNO
];
103 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
111 static COLORREF colours
[NCOLOURS
];
113 static LPLOGPALETTE logpal
;
114 static RGBTRIPLE defpal
[NCOLOURS
];
118 static HBITMAP caretbm
;
120 static int dbltime
, lasttime
, lastact
;
121 static Mouse_Button lastbtn
;
123 /* this allows xterm-style mouse handling. */
124 static int send_raw_mouse
= 0;
125 static int wheel_accumulator
= 0;
127 static char *window_name
, *icon_name
;
129 static int compose_state
= 0;
131 static OSVERSIONINFO osVersion
;
133 /* Dummy routine, only required in plink. */
134 void ldisc_update(int echo
, int edit
)
138 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
140 static char appname
[] = "PuTTY";
145 int guess_width
, guess_height
;
148 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
150 winsock_ver
= MAKEWORD(1, 1);
151 if (WSAStartup(winsock_ver
, &wsadata
)) {
152 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
153 MB_OK
| MB_ICONEXCLAMATION
);
156 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
157 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
158 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
162 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
165 InitCommonControls();
167 /* Ensure a Maximize setting in Explorer doesn't maximise the
172 ZeroMemory(&osVersion
, sizeof(osVersion
));
173 osVersion
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
174 if (!GetVersionEx ( (OSVERSIONINFO
*) &osVersion
)) {
175 MessageBox(NULL
, "Windows refuses to report a version",
176 "PuTTY Fatal Error", MB_OK
| MB_ICONEXCLAMATION
);
182 * Process the command line.
187 default_protocol
= DEFAULT_PROTOCOL
;
188 default_port
= DEFAULT_PORT
;
189 cfg
.logtype
= LGTYP_NONE
;
191 do_defaults(NULL
, &cfg
);
194 while (*p
&& isspace(*p
))
198 * Process command line options first. Yes, this can be
199 * done better, and it will be as soon as I have the
203 char *q
= p
+ strcspn(p
, " \t");
206 tolower(p
[0]) == 's' &&
207 tolower(p
[1]) == 's' && tolower(p
[2]) == 'h') {
208 default_protocol
= cfg
.protocol
= PROT_SSH
;
209 default_port
= cfg
.port
= 22;
210 } else if (q
== p
+ 7 &&
211 tolower(p
[0]) == 'c' &&
212 tolower(p
[1]) == 'l' &&
213 tolower(p
[2]) == 'e' &&
214 tolower(p
[3]) == 'a' &&
215 tolower(p
[4]) == 'n' &&
216 tolower(p
[5]) == 'u' && tolower(p
[6]) == 'p') {
218 * `putty -cleanup'. Remove all registry entries
219 * associated with PuTTY, and also find and delete
220 * the random seed file.
223 "This procedure will remove ALL Registry\n"
224 "entries associated with PuTTY, and will\n"
225 "also remove the PuTTY random seed file.\n"
227 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
228 "SESSIONS. Are you really sure you want\n"
231 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
236 p
= q
+ strspn(q
, " \t");
240 * An initial @ means to activate a saved session.
244 while (i
> 1 && isspace(p
[i
- 1]))
247 do_defaults(p
+ 1, &cfg
);
248 if (!*cfg
.host
&& !do_config()) {
252 } else if (*p
== '&') {
254 * An initial & means we've been given a command line
255 * containing the hex value of a HANDLE for a file
256 * mapping object, which we must then extract as a
261 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
262 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
263 0, 0, sizeof(Config
))) != NULL
) {
266 CloseHandle(filemap
);
267 } else if (!do_config()) {
274 * If the hostname starts with "telnet:", set the
275 * protocol to Telnet and process the string as a
278 if (!strncmp(q
, "telnet:", 7)) {
282 if (q
[0] == '/' && q
[1] == '/')
284 cfg
.protocol
= PROT_TELNET
;
286 while (*p
&& *p
!= ':' && *p
!= '/')
295 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
296 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
298 while (*p
&& !isspace(*p
))
302 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
303 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
304 while (*p
&& isspace(*p
))
319 * Trim leading whitespace off the hostname if it's there.
322 int space
= strspn(cfg
.host
, " \t");
323 memmove(cfg
.host
, cfg
.host
+space
, 1+strlen(cfg
.host
)-space
);
326 /* See if host is of the form user@host */
327 if (cfg
.host
[0] != '\0') {
328 char *atsign
= strchr(cfg
.host
, '@');
329 /* Make sure we're not overflowing the user field */
331 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
332 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
333 cfg
.username
[atsign
- cfg
.host
] = '\0';
335 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
340 * Trim a colon suffix off the hostname if it's there.
342 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
346 * Select protocol. This is farmed out into a table in a
347 * separate file to enable an ssh-free variant.
352 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
353 if (backends
[i
].protocol
== cfg
.protocol
) {
354 back
= backends
[i
].backend
;
358 MessageBox(NULL
, "Unsupported protocol number found",
359 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
365 /* Check for invalid Port number (i.e. zero) */
367 MessageBox(NULL
, "Invalid Port Number",
368 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
375 wndclass
.lpfnWndProc
= WndProc
;
376 wndclass
.cbClsExtra
= 0;
377 wndclass
.cbWndExtra
= 0;
378 wndclass
.hInstance
= inst
;
379 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
380 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
381 wndclass
.hbrBackground
= GetStockObject(BLACK_BRUSH
);
382 wndclass
.lpszMenuName
= NULL
;
383 wndclass
.lpszClassName
= appname
;
385 RegisterClass(&wndclass
);
390 savelines
= cfg
.savelines
;
396 * Guess some defaults for the window size. This all gets
397 * updated later, so we don't really care too much. However, we
398 * do want the font width/height guesses to correspond to a
399 * large font rather than a small one...
406 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
407 guess_width
= extra_width
+ font_width
* cols
;
408 guess_height
= extra_height
+ font_height
* rows
;
411 HWND w
= GetDesktopWindow();
412 GetWindowRect(w
, &r
);
413 if (guess_width
> r
.right
- r
.left
)
414 guess_width
= r
.right
- r
.left
;
415 if (guess_height
> r
.bottom
- r
.top
)
416 guess_height
= r
.bottom
- r
.top
;
420 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
423 winmode
&= ~(WS_VSCROLL
);
425 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
427 exwinmode
|= WS_EX_TOPMOST
;
429 exwinmode
|= WS_EX_CLIENTEDGE
;
430 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
431 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
432 guess_width
, guess_height
,
433 NULL
, NULL
, inst
, NULL
);
437 * Initialise the fonts, simultaneously correcting the guesses
438 * for font_{width,height}.
440 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
445 * Correct the guesses for extra_{width,height}.
449 GetWindowRect(hwnd
, &wr
);
450 GetClientRect(hwnd
, &cr
);
451 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
452 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
456 * Resize the window, now we know what size we _really_ want it
459 guess_width
= extra_width
+ font_width
* cols
;
460 guess_height
= extra_height
+ font_height
* rows
;
461 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
462 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
463 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
466 * Set up a caret bitmap, with no content.
470 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
471 bits
= smalloc(size
);
472 memset(bits
, 0, size
);
473 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
476 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
479 * Initialise the scroll bar.
484 si
.cbSize
= sizeof(si
);
485 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
490 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
494 * Start up the telnet connection.
498 char msg
[1024], *title
;
501 error
= back
->init(cfg
.host
, cfg
.port
, &realhost
);
503 sprintf(msg
, "Unable to open connection to\n"
504 "%.800s\n" "%s", cfg
.host
, error
);
505 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
508 window_name
= icon_name
= NULL
;
510 title
= cfg
.wintitle
;
512 sprintf(msg
, "%s - PuTTY", realhost
);
520 session_closed
= FALSE
;
523 * Set up the input and output buffers.
526 outbuf_reap
= outbuf_head
= 0;
529 * Prepare the mouse handler.
531 lastact
= MA_NOTHING
;
532 lastbtn
= MBT_NOTHING
;
533 dbltime
= GetDoubleClickTime();
536 * Set up the session-control options on the system menu.
539 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
543 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
544 if (cfg
.protocol
== PROT_TELNET
) {
546 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
547 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
548 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
549 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
550 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
551 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
552 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
553 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
554 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
555 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
556 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
557 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
558 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
559 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
560 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
561 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
562 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
564 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
566 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
567 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
568 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
569 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
572 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
573 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
575 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
576 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
577 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
578 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
579 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
580 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
581 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
582 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
586 * Finally show the window!
588 ShowWindow(hwnd
, show
);
589 SetForegroundWindow(hwnd
);
592 * Open the initial log file if there is one.
597 * Set the palette up.
603 has_focus
= (GetForegroundWindow() == hwnd
);
606 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
607 int timer_id
= 0, long_timer
= 0;
609 while (msg
.message
!= WM_QUIT
) {
610 /* Sometimes DispatchMessage calls routines that use their own
611 * GetMessage loop, setup this timer so we get some control back.
613 * Also call term_update() from the timer so that if the host
614 * is sending data flat out we still do redraws.
616 if (timer_id
&& long_timer
) {
617 KillTimer(hwnd
, timer_id
);
618 long_timer
= timer_id
= 0;
621 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
622 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
623 DispatchMessage(&msg
);
625 /* Make sure we blink everything that needs it. */
628 /* Send the paste buffer if there's anything to send */
631 /* If there's nothing new in the queue then we can do everything
632 * we've delayed, reading the socket, writing, and repainting
635 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
638 if (pending_netevent
) {
639 enact_pending_netevent();
641 /* Force the cursor blink on */
644 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
648 /* Okay there is now nothing to do so we make sure the screen is
649 * completely up to date then tell windows to call us in a little
653 KillTimer(hwnd
, timer_id
);
662 flash_window(1); /* maintain */
665 /* Hmm, term_update didn't want to do an update too soon ... */
666 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
668 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
670 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
673 /* There's no point rescanning everything in the message queue
674 * so we do an apparently unnecessary wait here
677 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
691 if (cfg
.protocol
== PROT_SSH
) {
702 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
704 char *do_select(SOCKET skt
, int startup
)
709 events
= (FD_CONNECT
| FD_READ
| FD_WRITE
|
710 FD_OOB
| FD_CLOSE
| FD_ACCEPT
);
715 return "do_select(): internal error (hwnd==NULL)";
716 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
717 switch (WSAGetLastError()) {
719 return "Network is down";
721 return "WSAAsyncSelect(): unknown error";
728 * set or clear the "raw mouse message" mode
730 void set_raw_mouse_mode(int activate
)
732 send_raw_mouse
= activate
;
733 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
737 * Print a message box and close the connection.
739 void connection_fatal(char *fmt
, ...)
745 vsprintf(stuff
, fmt
, ap
);
747 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
748 if (cfg
.close_on_exit
== COE_ALWAYS
)
751 session_closed
= TRUE
;
752 SetWindowText(hwnd
, "PuTTY (inactive)");
757 * Actually do the job requested by a WM_NETEVENT
759 static void enact_pending_netevent(void)
761 static int reentering
= 0;
762 extern int select_result(WPARAM
, LPARAM
);
766 return; /* don't unpend the pending */
768 pending_netevent
= FALSE
;
771 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
774 if (ret
== 0 && !session_closed
) {
775 /* Abnormal exits will already have set session_closed and taken
776 * appropriate action. */
777 if (cfg
.close_on_exit
== COE_ALWAYS
||
778 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
780 session_closed
= TRUE
;
781 SetWindowText(hwnd
, "PuTTY (inactive)");
782 MessageBox(hwnd
, "Connection closed by remote host",
783 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
789 * Copy the colour palette from the configuration data into defpal.
790 * This is non-trivial because the colour indices are different.
792 static void cfgtopalette(void)
795 static const int ww
[] = {
796 6, 7, 8, 9, 10, 11, 12, 13,
797 14, 15, 16, 17, 18, 19, 20, 21,
798 0, 1, 2, 3, 4, 4, 5, 5
801 for (i
= 0; i
< 24; i
++) {
803 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
804 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
805 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
810 * Set up the colour palette.
812 static void init_palette(void)
815 HDC hdc
= GetDC(hwnd
);
817 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
818 logpal
= smalloc(sizeof(*logpal
)
819 - sizeof(logpal
->palPalEntry
)
820 + NCOLOURS
* sizeof(PALETTEENTRY
));
821 logpal
->palVersion
= 0x300;
822 logpal
->palNumEntries
= NCOLOURS
;
823 for (i
= 0; i
< NCOLOURS
; i
++) {
824 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
825 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
826 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
827 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
829 pal
= CreatePalette(logpal
);
831 SelectPalette(hdc
, pal
, FALSE
);
833 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
836 ReleaseDC(hwnd
, hdc
);
839 for (i
= 0; i
< NCOLOURS
; i
++)
840 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
844 for (i
= 0; i
< NCOLOURS
; i
++)
845 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
846 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
850 * Initialise all the fonts we will need initially. There may be as many as
851 * three or as few as one. The other (poentially) twentyone fonts are done
852 * if/when they are needed.
856 * - check the font width and height, correcting our guesses if
859 * - verify that the bold font is the same width as the ordinary
860 * one, and engage shadow bolding if not.
862 * - verify that the underlined font is the same width as the
863 * ordinary one (manual underlining by means of line drawing can
864 * be done in a pinch).
866 static void init_fonts(int pick_width
)
873 int fw_dontcare
, fw_bold
;
875 for (i
= 0; i
< FONT_MAXNO
; i
++)
878 if (cfg
.fontisbold
) {
879 fw_dontcare
= FW_BOLD
;
882 fw_dontcare
= FW_DONTCARE
;
888 font_height
= cfg
.fontheight
;
889 if (font_height
> 0) {
891 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
893 font_width
= pick_width
;
896 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
897 c, OUT_DEFAULT_PRECIS, \
898 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
899 FIXED_PITCH | FF_DONTCARE, cfg.font)
901 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
903 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
904 GetTextMetrics(hdc
, &tm
);
905 font_height
= tm
.tmHeight
;
906 font_width
= tm
.tmAveCharWidth
;
910 DWORD cset
= tm
.tmCharSet
;
911 memset(&info
, 0xFF, sizeof(info
));
913 /* !!! Yes the next line is right */
914 if (cset
== OEM_CHARSET
)
915 font_codepage
= GetOEMCP();
917 if (TranslateCharsetInfo
918 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
923 GetCPInfo(font_codepage
, &cpinfo
);
924 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
927 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
930 * Some fonts, e.g. 9-pt Courier, draw their underlines
931 * outside their character cell. We successfully prevent
932 * screen corruption by clipping the text output, but then
933 * we lose the underline completely. Here we try to work
934 * out whether this is such a font, and if it is, we set a
935 * flag that causes underlines to be drawn by hand.
937 * Having tried other more sophisticated approaches (such
938 * as examining the TEXTMETRIC structure or requesting the
939 * height of a string), I think we'll do this the brute
940 * force way: we create a small bitmap, draw an underlined
941 * space on it, and test to see whether any pixels are
942 * foreground-coloured. (Since we expect the underline to
943 * go all the way across the character cell, we only search
944 * down a single column of the bitmap, half way across.)
948 HBITMAP und_bm
, und_oldbm
;
952 und_dc
= CreateCompatibleDC(hdc
);
953 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
954 und_oldbm
= SelectObject(und_dc
, und_bm
);
955 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
956 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
957 SetTextColor(und_dc
, RGB(255, 255, 255));
958 SetBkColor(und_dc
, RGB(0, 0, 0));
959 SetBkMode(und_dc
, OPAQUE
);
960 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
962 for (i
= 0; i
< font_height
; i
++) {
963 c
= GetPixel(und_dc
, font_width
/ 2, i
);
964 if (c
!= RGB(0, 0, 0))
967 SelectObject(und_dc
, und_oldbm
);
968 DeleteObject(und_bm
);
972 DeleteObject(fonts
[FONT_UNDERLINE
]);
973 fonts
[FONT_UNDERLINE
] = 0;
977 if (bold_mode
== BOLD_FONT
) {
978 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
982 descent
= tm
.tmAscent
+ 1;
983 if (descent
>= font_height
)
984 descent
= font_height
- 1;
986 for (i
= 0; i
< 3; i
++) {
988 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
989 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
996 ReleaseDC(hwnd
, hdc
);
998 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1000 DeleteObject(fonts
[FONT_UNDERLINE
]);
1001 fonts
[FONT_UNDERLINE
] = 0;
1004 if (bold_mode
== BOLD_FONT
&&
1005 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1006 bold_mode
= BOLD_SHADOW
;
1007 DeleteObject(fonts
[FONT_BOLD
]);
1008 fonts
[FONT_BOLD
] = 0;
1010 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1015 static void another_font(int fontno
)
1018 int fw_dontcare
, fw_bold
;
1022 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1025 basefont
= (fontno
& ~(FONT_BOLDUND
));
1026 if (basefont
!= fontno
&& !fontflag
[basefont
])
1027 another_font(basefont
);
1029 if (cfg
.fontisbold
) {
1030 fw_dontcare
= FW_BOLD
;
1033 fw_dontcare
= FW_DONTCARE
;
1037 c
= cfg
.fontcharset
;
1043 if (fontno
& FONT_WIDE
)
1045 if (fontno
& FONT_NARROW
)
1047 if (fontno
& FONT_OEM
)
1049 if (fontno
& FONT_BOLD
)
1051 if (fontno
& FONT_UNDERLINE
)
1055 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1056 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1057 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1058 FIXED_PITCH
| FF_DONTCARE
, s
);
1060 fontflag
[fontno
] = 1;
1063 static void deinit_fonts(void)
1066 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1068 DeleteObject(fonts
[i
]);
1074 void request_resize(int w
, int h
, int refont
)
1078 /* If the window is maximized supress resizing attempts */
1082 if (refont
&& w
!= cols
&& (cols
== 80 || cols
== 132)) {
1083 /* If font width too big for screen should we shrink the font more ? */
1085 font_width
= ((font_width
* cols
+ w
/ 2) / w
);
1089 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1090 und_mode
= UND_FONT
;
1091 init_fonts(font_width
);
1093 static int first_time
= 1;
1096 switch (first_time
) {
1098 /* Get the size of the screen */
1099 if (GetClientRect(GetDesktopWindow(), &ss
))
1100 /* first_time = 0 */ ;
1106 /* Make sure the values are sane */
1107 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1108 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1121 width
= extra_width
+ font_width
* w
;
1122 height
= extra_height
+ font_height
* h
;
1124 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1125 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1126 SWP_NOMOVE
| SWP_NOZORDER
);
1129 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
)
1131 int thistime
= GetMessageTime();
1133 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1134 lastbtn
= MBT_NOTHING
;
1135 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
);
1139 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1140 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1141 lastact
== MA_2CLK ? MA_3CLK
:
1142 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1147 if (lastact
!= MA_NOTHING
)
1148 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
);
1149 lasttime
= thistime
;
1153 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1154 * into a cooked one (SELECT, EXTEND, PASTE).
1156 Mouse_Button
translate_button(Mouse_Button button
)
1158 if (button
== MBT_LEFT
)
1160 if (button
== MBT_MIDDLE
)
1161 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1162 if (button
== MBT_RIGHT
)
1163 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1164 return 0; /* shouldn't happen */
1167 static void show_mouseptr(int show
)
1169 static int cursor_visible
= 1;
1170 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1172 if (cursor_visible
&& !show
)
1174 else if (!cursor_visible
&& show
)
1176 cursor_visible
= show
;
1179 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1180 WPARAM wParam
, LPARAM lParam
)
1183 static int ignore_size
= FALSE
;
1184 static int ignore_clip
= FALSE
;
1185 static int just_reconfigged
= FALSE
;
1186 static int resizing
= FALSE
;
1187 static int need_backend_resize
= FALSE
;
1188 static int defered_resize
= FALSE
;
1192 if (pending_netevent
)
1193 enact_pending_netevent();
1200 if (cfg
.ping_interval
> 0) {
1203 if (now
- last_movement
> cfg
.ping_interval
) {
1204 back
->special(TS_PING
);
1205 last_movement
= now
;
1213 if (!cfg
.warn_on_close
|| session_closed
||
1215 "Are you sure you want to close this session?",
1216 "PuTTY Exit Confirmation",
1217 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1218 DestroyWindow(hwnd
);
1225 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1237 PROCESS_INFORMATION pi
;
1238 HANDLE filemap
= NULL
;
1240 if (wParam
== IDM_DUPSESS
) {
1242 * Allocate a file-mapping memory chunk for the
1245 SECURITY_ATTRIBUTES sa
;
1248 sa
.nLength
= sizeof(sa
);
1249 sa
.lpSecurityDescriptor
= NULL
;
1250 sa
.bInheritHandle
= TRUE
;
1251 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1254 0, sizeof(Config
), NULL
);
1256 p
= (Config
*) MapViewOfFile(filemap
,
1258 0, 0, sizeof(Config
));
1260 *p
= cfg
; /* structure copy */
1264 sprintf(c
, "putty &%p", filemap
);
1266 } else if (wParam
== IDM_SAVEDSESS
) {
1268 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1269 cl
= smalloc(16 + strlen(session
)); /* 8, but play safe */
1271 cl
= NULL
; /* not a very important failure mode */
1273 sprintf(cl
, "putty @%s", session
);
1279 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1281 si
.lpReserved
= NULL
;
1282 si
.lpDesktop
= NULL
;
1286 si
.lpReserved2
= NULL
;
1287 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1288 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1291 CloseHandle(filemap
);
1298 int prev_alwaysontop
= cfg
.alwaysontop
;
1299 int prev_sunken_edge
= cfg
.sunken_edge
;
1300 char oldlogfile
[FILENAME_MAX
];
1302 int need_setwpos
= FALSE
;
1303 int old_fwidth
, old_fheight
;
1305 strcpy(oldlogfile
, cfg
.logfilename
);
1306 oldlogtype
= cfg
.logtype
;
1307 old_fwidth
= font_width
;
1308 old_fheight
= font_height
;
1309 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1311 if (!do_reconfig(hwnd
))
1314 if (strcmp(oldlogfile
, cfg
.logfilename
) ||
1315 oldlogtype
!= cfg
.logtype
) {
1316 logfclose(); /* reset logging */
1320 just_reconfigged
= TRUE
;
1322 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1323 und_mode
= UND_FONT
;
1327 * Flush the line discipline's edit buffer in the
1328 * case where local editing has just been disabled.
1330 ldisc_send(NULL
, 0);
1338 /* Enable or disable the scroll bar, etc */
1340 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1341 LONG nexflag
, exflag
=
1342 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1345 if (cfg
.alwaysontop
!= prev_alwaysontop
) {
1346 if (cfg
.alwaysontop
) {
1347 nexflag
|= WS_EX_TOPMOST
;
1348 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1349 SWP_NOMOVE
| SWP_NOSIZE
);
1351 nexflag
&= ~(WS_EX_TOPMOST
);
1352 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1353 SWP_NOMOVE
| SWP_NOSIZE
);
1356 if (cfg
.sunken_edge
)
1357 nexflag
|= WS_EX_CLIENTEDGE
;
1359 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1365 nflg
&= ~WS_VSCROLL
;
1367 nflg
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1369 nflg
|= (WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1371 if (nflg
!= flag
|| nexflag
!= exflag
) {
1375 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1376 if (nexflag
!= exflag
)
1377 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1379 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
1381 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1382 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1383 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
1384 | SWP_FRAMECHANGED
);
1386 GetWindowRect(hwnd
, &wr
);
1387 GetClientRect(hwnd
, &cr
);
1389 wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1391 wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1392 need_setwpos
= TRUE
;
1396 if (cfg
.height
!= rows
||
1397 cfg
.width
!= cols
||
1398 old_fwidth
!= font_width
||
1399 old_fheight
!= font_height
||
1400 cfg
.savelines
!= savelines
||
1401 cfg
.sunken_edge
!= prev_sunken_edge
)
1402 need_setwpos
= TRUE
;
1404 if (IsZoomed(hwnd
)) {
1408 defered_resize
= TRUE
;
1410 GetClientRect(hwnd
, &cr
);
1411 w
= cr
.right
- cr
.left
;
1412 h
= cr
.bottom
- cr
.top
;
1416 h
= h
/ font_height
;
1420 term_size(h
, w
, cfg
.savelines
);
1421 InvalidateRect(hwnd
, NULL
, TRUE
);
1424 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1425 InvalidateRect(hwnd
, NULL
, TRUE
);
1427 SetWindowPos(hwnd
, NULL
, 0, 0,
1428 extra_width
+ font_width
* cfg
.width
,
1430 font_height
* cfg
.height
,
1431 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1432 SWP_NOMOVE
| SWP_NOZORDER
);
1436 if (cfg
.locksize
&& IsZoomed(hwnd
))
1438 set_title(cfg
.wintitle
);
1439 if (IsIconic(hwnd
)) {
1441 cfg
.win_name_always ? window_name
:
1456 back
->special(TS_AYT
);
1459 back
->special(TS_BRK
);
1462 back
->special(TS_SYNCH
);
1465 back
->special(TS_EC
);
1468 back
->special(TS_EL
);
1471 back
->special(TS_GA
);
1474 back
->special(TS_NOP
);
1477 back
->special(TS_ABORT
);
1480 back
->special(TS_AO
);
1483 back
->special(TS_IP
);
1486 back
->special(TS_SUSP
);
1489 back
->special(TS_EOR
);
1492 back
->special(TS_EOF
);
1499 * We get this if the System menu has been activated.
1500 * This might happen from within TranslateKey, in which
1501 * case it really wants to be followed by a `space'
1502 * character to actually _bring the menu up_ rather
1503 * than just sitting there in `ready to appear' state.
1506 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1509 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1510 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1515 #define X_POS(l) ((int)(short)LOWORD(l))
1516 #define Y_POS(l) ((int)(short)HIWORD(l))
1518 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1519 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1520 #define WHEEL_DELTA 120
1523 wheel_accumulator
+= (short) HIWORD(wParam
);
1524 wParam
= LOWORD(wParam
);
1526 /* process events when the threshold is reached */
1527 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
1530 /* reduce amount for next time */
1531 if (wheel_accumulator
> 0) {
1533 wheel_accumulator
-= WHEEL_DELTA
;
1534 } else if (wheel_accumulator
< 0) {
1536 wheel_accumulator
+= WHEEL_DELTA
;
1540 if (send_raw_mouse
) {
1541 /* send a mouse-down followed by a mouse up */
1544 TO_CHR_X(X_POS(lParam
)),
1545 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1546 wParam
& MK_CONTROL
);
1547 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1548 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1549 wParam
& MK_CONTROL
);
1551 /* trigger a scroll */
1553 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
1558 case WM_LBUTTONDOWN
:
1559 case WM_MBUTTONDOWN
:
1560 case WM_RBUTTONDOWN
:
1567 case WM_LBUTTONDOWN
:
1571 case WM_MBUTTONDOWN
:
1572 button
= MBT_MIDDLE
;
1575 case WM_RBUTTONDOWN
:
1584 button
= MBT_MIDDLE
;
1592 button
= press
= 0; /* shouldn't happen */
1597 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1598 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
);
1601 term_mouse(button
, MA_RELEASE
,
1602 TO_CHR_X(X_POS(lParam
)),
1603 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1604 wParam
& MK_CONTROL
);
1612 * Add the mouse position and message time to the random
1615 noise_ultralight(lParam
);
1617 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1619 if (wParam
& MK_LBUTTON
)
1621 else if (wParam
& MK_MBUTTON
)
1625 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1626 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1627 wParam
& MK_CONTROL
);
1630 case WM_NCMOUSEMOVE
:
1632 noise_ultralight(lParam
);
1634 case WM_IGNORE_CLIP
:
1635 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1637 case WM_DESTROYCLIPBOARD
:
1640 ignore_clip
= FALSE
;
1646 hdc
= BeginPaint(hwnd
, &p
);
1648 SelectPalette(hdc
, pal
, TRUE
);
1649 RealizePalette(hdc
);
1651 term_paint(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1652 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1653 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
1654 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
1660 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1661 * but the only one that's likely to try to overload us is FD_READ.
1662 * This means buffering just one is fine.
1664 if (pending_netevent
)
1665 enact_pending_netevent();
1667 pending_netevent
= TRUE
;
1668 pend_netevent_wParam
= wParam
;
1669 pend_netevent_lParam
= lParam
;
1670 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
1671 enact_pending_netevent();
1673 time(&last_movement
);
1677 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
1679 flash_window(0); /* stop */
1691 case WM_IGNORE_SIZE
:
1692 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1694 case WM_ENTERSIZEMOVE
:
1697 need_backend_resize
= FALSE
;
1699 case WM_EXITSIZEMOVE
:
1702 if (need_backend_resize
)
1707 int width
, height
, w
, h
, ew
, eh
;
1708 LPRECT r
= (LPRECT
) lParam
;
1710 width
= r
->right
- r
->left
- extra_width
;
1711 height
= r
->bottom
- r
->top
- extra_height
;
1712 w
= (width
+ font_width
/ 2) / font_width
;
1715 h
= (height
+ font_height
/ 2) / font_height
;
1718 UpdateSizeTip(hwnd
, w
, h
);
1719 ew
= width
- w
* font_width
;
1720 eh
= height
- h
* font_height
;
1722 if (wParam
== WMSZ_LEFT
||
1723 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
1729 if (wParam
== WMSZ_TOP
||
1730 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
1740 /* break; (never reached) */
1742 if (wParam
== SIZE_MINIMIZED
) {
1744 cfg
.win_name_always ? window_name
: icon_name
);
1747 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1748 SetWindowText(hwnd
, window_name
);
1750 int width
, height
, w
, h
;
1751 #if 0 /* we have fixed this using WM_SIZING now */
1755 width
= LOWORD(lParam
);
1756 height
= HIWORD(lParam
);
1757 w
= width
/ font_width
;
1760 h
= height
/ font_height
;
1763 #if 0 /* we have fixed this using WM_SIZING now */
1764 ew
= width
- w
* font_width
;
1765 eh
= height
- h
* font_height
;
1766 if (ew
!= 0 || eh
!= 0) {
1768 GetWindowRect(hwnd
, &r
);
1769 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
1770 SetWindowPos(hwnd
, NULL
, 0, 0,
1771 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1772 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1775 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1777 term_size(h
, w
, cfg
.savelines
);
1779 * Don't call back->size in mid-resize. (To prevent
1780 * massive numbers of resize events getting sent
1781 * down the connection during an NT opaque drag.)
1786 need_backend_resize
= TRUE
;
1790 just_reconfigged
= FALSE
;
1793 if (wParam
== SIZE_RESTORED
&& defered_resize
) {
1794 defered_resize
= FALSE
;
1795 SetWindowPos(hwnd
, NULL
, 0, 0,
1796 extra_width
+ font_width
* cfg
.width
,
1797 extra_height
+ font_height
* cfg
.height
,
1798 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1799 SWP_NOMOVE
| SWP_NOZORDER
);
1801 ignore_size
= FALSE
;
1804 switch (LOWORD(wParam
)) {
1818 term_scroll(0, +rows
/ 2);
1821 term_scroll(0, -rows
/ 2);
1823 case SB_THUMBPOSITION
:
1825 term_scroll(1, HIWORD(wParam
));
1829 case WM_PALETTECHANGED
:
1830 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1831 HDC hdc
= get_ctx();
1833 if (RealizePalette(hdc
) > 0)
1839 case WM_QUERYNEWPALETTE
:
1841 HDC hdc
= get_ctx();
1843 if (RealizePalette(hdc
) > 0)
1855 * Add the scan code and keypress timing to the random
1858 noise_ultralight(lParam
);
1861 * We don't do TranslateMessage since it disassociates the
1862 * resulting CHAR message from the KEYDOWN that sparked it,
1863 * which we occasionally don't want. Instead, we process
1864 * KEYDOWN, and call the Win32 translator functions so that
1865 * we get the translations under _our_ control.
1868 unsigned char buf
[20];
1871 if (wParam
== VK_PROCESSKEY
) {
1874 m
.message
= WM_KEYDOWN
;
1876 m
.lParam
= lParam
& 0xdfff;
1877 TranslateMessage(&m
);
1879 len
= TranslateKey(message
, wParam
, lParam
, buf
);
1881 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1885 * We need not bother about stdin backlogs
1886 * here, because in GUI PuTTY we can't do
1887 * anything about it anyway; there's no means
1888 * of asking Windows to hold off on KEYDOWN
1889 * messages. We _have_ to buffer everything
1892 ldisc_send(buf
, len
);
1898 case WM_INPUTLANGCHANGE
:
1900 /* wParam == Font number */
1901 /* lParam == Locale */
1903 HKL NewInputLocale
= (HKL
) lParam
;
1905 // lParam == GetKeyboardLayout(0);
1907 GetLocaleInfo(LOWORD(NewInputLocale
),
1908 LOCALE_IDEFAULTANSICODEPAGE
, lbuf
, sizeof(lbuf
));
1910 kbd_codepage
= atoi(lbuf
);
1913 case WM_IME_COMPOSITION
:
1919 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
1920 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
1922 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
1923 break; /* fall back to DefWindowProc */
1925 hIMC
= ImmGetContext(hwnd
);
1926 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
1929 buff
= (char*) smalloc(n
);
1930 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
1931 luni_send((unsigned short *)buff
, n
/ 2);
1934 ImmReleaseContext(hwnd
, hIMC
);
1939 if (wParam
& 0xFF00) {
1940 unsigned char buf
[2];
1943 buf
[0] = wParam
>> 8;
1944 lpage_send(kbd_codepage
, buf
, 2);
1946 char c
= (unsigned char) wParam
;
1947 lpage_send(kbd_codepage
, &c
, 1);
1953 * Nevertheless, we are prepared to deal with WM_CHAR
1954 * messages, should they crop up. So if someone wants to
1955 * post the things to us as part of a macro manoeuvre,
1956 * we're ready to cope.
1959 char c
= (unsigned char)wParam
;
1960 lpage_send(CP_ACP
, &c
, 1);
1964 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
1965 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
1970 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1974 * Move the system caret. (We maintain one, even though it's
1975 * invisible, for the benefit of blind people: apparently some
1976 * helper software tracks the system caret, so we should arrange to
1979 void sys_cursor(int x
, int y
)
1984 if (!has_focus
) return;
1986 SetCaretPos(x
* font_width
, y
* font_height
);
1988 /* IMM calls on Win98 and beyond only */
1989 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
1991 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
1992 osVersion
.dwMinorVersion
== 0) return; /* 95 */
1994 /* we should have the IMM functions */
1995 hIMC
= ImmGetContext(hwnd
);
1996 cf
.dwStyle
= CFS_POINT
;
1997 cf
.ptCurrentPos
.x
= x
* font_width
;
1998 cf
.ptCurrentPos
.y
= y
* font_height
;
1999 ImmSetCompositionWindow(hIMC
, &cf
);
2001 ImmReleaseContext(hwnd
, hIMC
);
2005 * Draw a line of text in the window, at given character
2006 * coordinates, in given attributes.
2008 * We are allowed to fiddle with the contents of `text'.
2010 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2011 unsigned long attr
, int lattr
)
2014 int nfg
, nbg
, nfont
;
2017 int force_manual_underline
= 0;
2018 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2019 int char_width
= fnt_width
;
2020 int text_adjust
= 0;
2021 static int *IpDx
= 0, IpDxLEN
= 0;
2023 if (attr
& ATTR_WIDE
)
2026 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2028 if (len
> IpDxLEN
) {
2030 IpDx
= smalloc((len
+ 16) * sizeof(int));
2031 IpDxLEN
= (len
+ 16);
2033 for (i
= 0; i
< IpDxLEN
; i
++)
2034 IpDx
[i
] = char_width
;
2040 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
2041 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2042 attr
^= ATTR_CUR_XOR
;
2046 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2047 /* Assume a poorman font is borken in other ways too. */
2057 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2061 /* Special hack for the VT100 linedraw glyphs. */
2062 if ((attr
& CSET_MASK
) == 0x2300) {
2063 if (!dbcs_screenfont
&&
2064 text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2065 switch ((unsigned char) (text
[0])) {
2067 text_adjust
= -2 * font_height
/ 5;
2070 text_adjust
= -1 * font_height
/ 5;
2073 text_adjust
= font_height
/ 5;
2076 text_adjust
= 2 * font_height
/ 5;
2079 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2082 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2083 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2084 if (attr
& ATTR_UNDER
) {
2085 attr
&= ~ATTR_UNDER
;
2086 force_manual_underline
= 1;
2091 /* Anything left as an original character set is unprintable. */
2092 if (DIRECT_CHAR(attr
)) {
2095 memset(text
, 0xFF, len
);
2099 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2102 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2103 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2104 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2106 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2107 nfont
|= FONT_UNDERLINE
;
2108 another_font(nfont
);
2109 if (!fonts
[nfont
]) {
2110 if (nfont
& FONT_UNDERLINE
)
2111 force_manual_underline
= 1;
2112 /* Don't do the same for manual bold, it could be bad news. */
2114 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2116 another_font(nfont
);
2118 nfont
= FONT_NORMAL
;
2119 if (attr
& ATTR_REVERSE
) {
2124 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2126 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2130 SelectObject(hdc
, fonts
[nfont
]);
2131 SetTextColor(hdc
, fg
);
2132 SetBkColor(hdc
, bg
);
2133 SetBkMode(hdc
, OPAQUE
);
2136 line_box
.right
= x
+ char_width
* len
;
2137 line_box
.bottom
= y
+ font_height
;
2139 /* We're using a private area for direct to font. (512 chars.) */
2140 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2141 /* Ho Hum, dbcs fonts are a PITA! */
2142 /* To display on W9x I have to convert to UCS */
2143 static wchar_t *uni_buf
= 0;
2144 static int uni_len
= 0;
2146 if (len
> uni_len
) {
2148 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2150 nlen
= MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2151 text
, len
, uni_buf
, uni_len
);
2157 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2158 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, 0);
2159 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2160 SetBkMode(hdc
, TRANSPARENT
);
2161 ExtTextOutW(hdc
, x
- 1,
2162 y
- font_height
* (lattr
==
2163 LATTR_BOT
) + text_adjust
,
2164 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, 0);
2166 } else if (DIRECT_FONT(attr
)) {
2168 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2169 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2170 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2171 SetBkMode(hdc
, TRANSPARENT
);
2173 /* GRR: This draws the character outside it's box and can leave
2174 * 'droppings' even with the clip box! I suppose I could loop it
2175 * one character at a time ... yuk.
2177 * Or ... I could do a test print with "W", and use +1 or -1 for this
2178 * shift depending on if the leftmost column is blank...
2180 ExtTextOut(hdc
, x
- 1,
2181 y
- font_height
* (lattr
==
2182 LATTR_BOT
) + text_adjust
,
2183 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2186 /* And 'normal' unicode characters */
2187 static WCHAR
*wbuf
= NULL
;
2188 static int wlen
= 0;
2193 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2195 for (i
= 0; i
< len
; i
++)
2196 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2199 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2200 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2202 /* And the shadow bold hack. */
2203 if (bold_mode
== BOLD_SHADOW
) {
2204 SetBkMode(hdc
, TRANSPARENT
);
2205 ExtTextOutW(hdc
, x
- 1,
2206 y
- font_height
* (lattr
==
2207 LATTR_BOT
) + text_adjust
,
2208 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2211 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2212 (und_mode
== UND_LINE
2213 && (attr
& ATTR_UNDER
)))) {
2216 if (lattr
== LATTR_BOT
)
2217 dec
= dec
* 2 - font_height
;
2219 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2220 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2221 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2222 oldpen
= SelectObject(hdc
, oldpen
);
2223 DeleteObject(oldpen
);
2227 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2228 unsigned long attr
, int lattr
)
2234 int ctype
= cfg
.cursor_type
;
2236 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2237 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2238 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2242 attr
|= TATTR_RIGHTCURS
;
2245 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2246 if (attr
& ATTR_WIDE
)
2251 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2254 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2255 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2256 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2257 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2258 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2259 Polyline(hdc
, pts
, 5);
2260 oldpen
= SelectObject(hdc
, oldpen
);
2261 DeleteObject(oldpen
);
2262 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2263 int startx
, starty
, dx
, dy
, length
, i
;
2266 starty
= y
+ descent
;
2269 length
= char_width
;
2272 if (attr
& TATTR_RIGHTCURS
)
2273 xadjust
= char_width
- 1;
2274 startx
= x
+ xadjust
;
2278 length
= font_height
;
2280 if (attr
& TATTR_ACTCURS
) {
2283 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2284 MoveToEx(hdc
, startx
, starty
, NULL
);
2285 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2286 oldpen
= SelectObject(hdc
, oldpen
);
2287 DeleteObject(oldpen
);
2289 for (i
= 0; i
< length
; i
++) {
2291 SetPixel(hdc
, startx
, starty
, colours
[23]);
2301 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2302 * codes. Returns number of bytes used or zero to drop the message
2303 * or -1 to forward the message to windows.
2305 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2306 unsigned char *output
)
2309 int scan
, left_alt
= 0, key_down
, shift_state
;
2311 unsigned char *p
= output
;
2312 static int alt_sum
= 0;
2314 HKL kbd_layout
= GetKeyboardLayout(0);
2316 static WORD keys
[3];
2317 static int compose_char
= 0;
2318 static WPARAM compose_key
= 0;
2320 r
= GetKeyboardState(keystate
);
2322 memset(keystate
, 0, sizeof(keystate
));
2325 #define SHOW_TOASCII_RESULT
2326 { /* Tell us all about key events */
2327 static BYTE oldstate
[256];
2328 static int first
= 1;
2332 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2335 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2337 } else if ((HIWORD(lParam
) & KF_UP
)
2338 && scan
== (HIWORD(lParam
) & 0xFF)) {
2342 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2343 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2356 debug(("VK_%02x", wParam
));
2358 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2360 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2362 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2363 if (ch
>= ' ' && ch
<= '~')
2364 debug((", '%c'", ch
));
2366 debug((", $%02x", ch
));
2369 debug((", KB0=%02x", keys
[0]));
2371 debug((", KB1=%02x", keys
[1]));
2373 debug((", KB2=%02x", keys
[2]));
2375 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2377 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2379 if ((HIWORD(lParam
) & KF_EXTENDED
))
2381 if ((HIWORD(lParam
) & KF_UP
))
2385 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2386 else if ((HIWORD(lParam
) & KF_UP
))
2387 oldstate
[wParam
& 0xFF] ^= 0x80;
2389 oldstate
[wParam
& 0xFF] ^= 0x81;
2391 for (ch
= 0; ch
< 256; ch
++)
2392 if (oldstate
[ch
] != keystate
[ch
])
2393 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2395 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2399 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2400 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2404 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2405 if ((cfg
.funky_type
== 3 ||
2406 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2407 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2409 wParam
= VK_EXECUTE
;
2411 /* UnToggle NUMLock */
2412 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2413 keystate
[VK_NUMLOCK
] ^= 1;
2416 /* And write back the 'adjusted' state */
2417 SetKeyboardState(keystate
);
2420 /* Disable Auto repeat if required */
2421 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2424 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2427 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2429 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2430 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2431 if (cfg
.ctrlaltkeys
)
2432 keystate
[VK_MENU
] = 0;
2434 keystate
[VK_RMENU
] = 0x80;
2439 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2440 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2441 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2443 /* Note if AltGr was pressed and if it was used as a compose key */
2444 if (!compose_state
) {
2445 compose_key
= 0x100;
2446 if (cfg
.compose_key
) {
2447 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2448 compose_key
= wParam
;
2450 if (wParam
== VK_APPS
)
2451 compose_key
= wParam
;
2454 if (wParam
== compose_key
) {
2455 if (compose_state
== 0
2456 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2458 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2462 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
2466 * Record that we pressed key so the scroll window can be reset, but
2467 * be careful to avoid Shift-UP/Down
2469 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
2473 /* Make sure we're not pasting */
2477 if (compose_state
> 1 && left_alt
)
2480 /* Sanitize the number pad if not using a PC NumPad */
2481 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2482 && cfg
.funky_type
!= 2)
2483 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
2484 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
2488 nParam
= VK_NUMPAD0
;
2491 nParam
= VK_NUMPAD1
;
2494 nParam
= VK_NUMPAD2
;
2497 nParam
= VK_NUMPAD3
;
2500 nParam
= VK_NUMPAD4
;
2503 nParam
= VK_NUMPAD5
;
2506 nParam
= VK_NUMPAD6
;
2509 nParam
= VK_NUMPAD7
;
2512 nParam
= VK_NUMPAD8
;
2515 nParam
= VK_NUMPAD9
;
2518 nParam
= VK_DECIMAL
;
2522 if (keystate
[VK_NUMLOCK
] & 1)
2529 /* If a key is pressed and AltGr is not active */
2530 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
2531 /* Okay, prepare for most alts then ... */
2535 /* Lets see if it's a pattern we know all about ... */
2536 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2537 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2540 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2541 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
2544 if (wParam
== VK_INSERT
&& shift_state
== 1) {
2548 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
2551 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
2552 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
2555 /* Control-Numlock for app-keypad mode switch */
2556 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
2557 app_keypad_keys
^= 1;
2561 /* Nethack keypad */
2562 if (cfg
.nethack_keypad
&& !left_alt
) {
2565 *p
++ = shift_state ?
'B' : 'b';
2568 *p
++ = shift_state ?
'J' : 'j';
2571 *p
++ = shift_state ?
'N' : 'n';
2574 *p
++ = shift_state ?
'H' : 'h';
2577 *p
++ = shift_state ?
'.' : '.';
2580 *p
++ = shift_state ?
'L' : 'l';
2583 *p
++ = shift_state ?
'Y' : 'y';
2586 *p
++ = shift_state ?
'K' : 'k';
2589 *p
++ = shift_state ?
'U' : 'u';
2594 /* Application Keypad */
2598 if (cfg
.funky_type
== 3 ||
2599 (cfg
.funky_type
<= 1 &&
2600 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
2614 if (app_keypad_keys
&& !cfg
.no_applic_k
)
2651 if (cfg
.funky_type
== 2) {
2656 } else if (shift_state
)
2663 if (cfg
.funky_type
== 2)
2667 if (cfg
.funky_type
== 2)
2671 if (cfg
.funky_type
== 2)
2676 if (HIWORD(lParam
) & KF_EXTENDED
)
2682 if (xkey
>= 'P' && xkey
<= 'S')
2683 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
2685 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
2687 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
2692 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
2693 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
2697 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
2703 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
2707 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
2711 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
2716 if (wParam
== VK_PAUSE
) { /* Break/Pause */
2721 /* Control-2 to Control-8 are special */
2722 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
2723 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
2726 if (shift_state
== 2 && wParam
== 0xBD) {
2730 if (shift_state
== 2 && wParam
== 0xDF) {
2734 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
2741 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2742 * for integer decimal nn.)
2744 * We also deal with the weird ones here. Linux VCs replace F1
2745 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2746 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2752 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
2755 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
2758 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
2761 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
2764 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
2767 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
2770 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
2773 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
2776 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
2779 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
2812 if ((shift_state
&2) == 0) switch (wParam
) {
2832 /* Reorder edit keys to physical order */
2833 if (cfg
.funky_type
== 3 && code
<= 6)
2834 code
= "\0\2\1\4\5\3\6"[code
];
2836 if (vt52_mode
&& code
> 0 && code
<= 6) {
2837 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
2841 if (cfg
.funky_type
== 5 && /* SCO function keys */
2842 code
>= 11 && code
<= 34) {
2843 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
2846 case VK_F1
: index
= 0; break;
2847 case VK_F2
: index
= 1; break;
2848 case VK_F3
: index
= 2; break;
2849 case VK_F4
: index
= 3; break;
2850 case VK_F5
: index
= 4; break;
2851 case VK_F6
: index
= 5; break;
2852 case VK_F7
: index
= 6; break;
2853 case VK_F8
: index
= 7; break;
2854 case VK_F9
: index
= 8; break;
2855 case VK_F10
: index
= 9; break;
2856 case VK_F11
: index
= 10; break;
2857 case VK_F12
: index
= 11; break;
2859 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
2860 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
2861 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
2864 if (cfg
.funky_type
== 5 && /* SCO small keypad */
2865 code
>= 1 && code
<= 6) {
2866 char codes
[] = "HL.FIG";
2870 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
2874 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
2881 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
2884 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
2887 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
2888 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
2891 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
2893 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
2895 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
2898 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
2899 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
2903 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
2908 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2909 * some reason seems to send VK_CLEAR to Windows...).
2932 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
2934 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
2935 /* VT100 & VT102 manuals both state the app cursor keys
2936 * only work if the app keypad is on.
2938 if (!app_keypad_keys
)
2940 /* Useful mapping of Ctrl-arrows */
2941 if (shift_state
== 2)
2945 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
2947 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
2954 * Finally, deal with Return ourselves. (Win95 seems to
2955 * foul it up when Alt is pressed, for some reason.)
2957 if (wParam
== VK_RETURN
) { /* Return */
2963 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
2964 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
2969 /* Okay we've done everything interesting; let windows deal with
2970 * the boring stuff */
2972 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
2973 #ifdef SHOW_TOASCII_RESULT
2974 if (r
== 1 && !key_down
) {
2976 if (in_utf
|| dbcs_screenfont
)
2977 debug((", (U+%04x)", alt_sum
));
2979 debug((", LCH(%d)", alt_sum
));
2981 debug((", ACH(%d)", keys
[0]));
2986 for (r1
= 0; r1
< r
; r1
++) {
2987 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
2995 for (i
= 0; i
< r
; i
++) {
2996 unsigned char ch
= (unsigned char) keys
[i
];
2998 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3003 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3007 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3008 MessageBeep(MB_ICONHAND
);
3012 luni_send(&keybuf
, 1);
3020 if (in_utf
|| dbcs_screenfont
) {
3022 luni_send(&keybuf
, 1);
3024 ch
= (char) alt_sum
;
3026 * We need not bother about stdin
3027 * backlogs here, because in GUI PuTTY
3028 * we can't do anything about it
3029 * anyway; there's no means of asking
3030 * Windows to hold off on KEYDOWN
3031 * messages. We _have_ to buffer
3032 * everything we're sent.
3038 lpage_send(kbd_codepage
, &ch
, 1);
3040 static char cbuf
[] = "\033 ";
3042 lpage_send(kbd_codepage
, cbuf
+ !left_alt
,
3048 /* This is so the ALT-Numpad and dead keys work correctly. */
3053 /* If we're definitly not building up an ALT-54321 then clear it */
3056 /* If we will be using alt_sum fix the 256s */
3057 else if (keys
[0] && (in_utf
|| dbcs_screenfont
))
3062 * ALT alone may or may not want to bring up the System menu.
3063 * If it's not meant to, we return 0 on presses or releases of
3064 * ALT, to show that we've swallowed the keystroke. Otherwise
3065 * we return -1, which means Windows will give the keystroke
3066 * its default handling (i.e. bring up the System menu).
3068 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3074 void set_title(char *title
)
3077 window_name
= smalloc(1 + strlen(title
));
3078 strcpy(window_name
, title
);
3079 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3080 SetWindowText(hwnd
, title
);
3083 void set_icon(char *title
)
3086 icon_name
= smalloc(1 + strlen(title
));
3087 strcpy(icon_name
, title
);
3088 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3089 SetWindowText(hwnd
, title
);
3092 void set_sbar(int total
, int start
, int page
)
3099 si
.cbSize
= sizeof(si
);
3100 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3102 si
.nMax
= total
- 1;
3106 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3109 Context
get_ctx(void)
3115 SelectPalette(hdc
, pal
, FALSE
);
3121 void free_ctx(Context ctx
)
3123 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3124 ReleaseDC(hwnd
, ctx
);
3127 static void real_palette_set(int n
, int r
, int g
, int b
)
3130 logpal
->palPalEntry
[n
].peRed
= r
;
3131 logpal
->palPalEntry
[n
].peGreen
= g
;
3132 logpal
->palPalEntry
[n
].peBlue
= b
;
3133 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3134 colours
[n
] = PALETTERGB(r
, g
, b
);
3135 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3137 colours
[n
] = RGB(r
, g
, b
);
3140 void palette_set(int n
, int r
, int g
, int b
)
3142 static const int first
[21] = {
3143 0, 2, 4, 6, 8, 10, 12, 14,
3144 1, 3, 5, 7, 9, 11, 13, 15,
3147 real_palette_set(first
[n
], r
, g
, b
);
3149 real_palette_set(first
[n
] + 1, r
, g
, b
);
3151 HDC hdc
= get_ctx();
3152 UnrealizeObject(pal
);
3153 RealizePalette(hdc
);
3158 void palette_reset(void)
3162 for (i
= 0; i
< NCOLOURS
; i
++) {
3164 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3165 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3166 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3167 logpal
->palPalEntry
[i
].peFlags
= 0;
3168 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3169 defpal
[i
].rgbtGreen
,
3170 defpal
[i
].rgbtBlue
);
3172 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3173 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3178 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3180 RealizePalette(hdc
);
3185 void write_aclip(char *data
, int len
, int must_deselect
)
3190 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3193 lock
= GlobalLock(clipdata
);
3196 memcpy(lock
, data
, len
);
3197 ((unsigned char *) lock
)[len
] = 0;
3198 GlobalUnlock(clipdata
);
3201 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3203 if (OpenClipboard(hwnd
)) {
3205 SetClipboardData(CF_TEXT
, clipdata
);
3208 GlobalFree(clipdata
);
3211 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3215 * Note: unlike write_aclip() this will not append a nul.
3217 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3224 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3226 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3227 len
* sizeof(wchar_t));
3228 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3230 if (!clipdata
|| !clipdata2
) {
3232 GlobalFree(clipdata
);
3234 GlobalFree(clipdata2
);
3237 if (!(lock
= GlobalLock(clipdata
)))
3239 if (!(lock2
= GlobalLock(clipdata2
)))
3242 memcpy(lock
, data
, len
* sizeof(wchar_t));
3243 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3245 GlobalUnlock(clipdata
);
3246 GlobalUnlock(clipdata2
);
3249 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3251 if (OpenClipboard(hwnd
)) {
3253 SetClipboardData(CF_UNICODETEXT
, clipdata
);
3254 SetClipboardData(CF_TEXT
, clipdata2
);
3257 GlobalFree(clipdata
);
3258 GlobalFree(clipdata2
);
3262 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3265 void get_clip(wchar_t ** p
, int *len
)
3267 static HGLOBAL clipdata
= NULL
;
3268 static wchar_t *converted
= 0;
3277 GlobalUnlock(clipdata
);
3280 } else if (OpenClipboard(NULL
)) {
3281 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
3283 *p
= GlobalLock(clipdata
);
3285 for (p2
= *p
; *p2
; p2
++);
3289 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
3293 s
= GlobalLock(clipdata
);
3294 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
3295 *p
= converted
= smalloc(i
* sizeof(wchar_t));
3296 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
3309 * Move `lines' lines from position `from' to position `to' in the
3312 void optimised_move(int to
, int from
, int lines
)
3317 min
= (to
< from ? to
: from
);
3318 max
= to
+ from
- min
;
3321 r
.right
= cols
* font_width
;
3322 r
.top
= min
* font_height
;
3323 r
.bottom
= (max
+ lines
) * font_height
;
3324 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
3329 * Print a message box and perform a fatal exit.
3331 void fatalbox(char *fmt
, ...)
3337 vsprintf(stuff
, fmt
, ap
);
3339 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
3344 * Manage window caption / taskbar flashing, if enabled.
3345 * 0 = stop, 1 = maintain, 2 = start
3347 static void flash_window(int mode
)
3349 static long last_flash
= 0;
3350 static int flashing
= 0;
3351 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
3354 FlashWindow(hwnd
, FALSE
);
3358 } else if (mode
== 2) {
3361 last_flash
= GetTickCount();
3363 FlashWindow(hwnd
, TRUE
);
3366 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
3369 long now
= GetTickCount();
3370 long fdiff
= now
- last_flash
;
3371 if (fdiff
< 0 || fdiff
> 450) {
3373 FlashWindow(hwnd
, TRUE
); /* toggle */
3384 if (mode
== BELL_DEFAULT
) {
3386 * For MessageBeep style bells, we want to be careful of
3387 * timing, because they don't have the nice property of
3388 * PlaySound bells that each one cancels the previous
3389 * active one. So we limit the rate to one per 50ms or so.
3391 static long lastbeep
= 0;
3394 beepdiff
= GetTickCount() - lastbeep
;
3395 if (beepdiff
>= 0 && beepdiff
< 50)
3399 * The above MessageBeep call takes time, so we record the
3400 * time _after_ it finishes rather than before it starts.
3402 lastbeep
= GetTickCount();
3403 } else if (mode
== BELL_WAVEFILE
) {
3404 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
3405 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
3406 sprintf(buf
, "Unable to play sound file\n%s\n"
3407 "Using default sound instead", cfg
.bell_wavefile
);
3408 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
3409 MB_OK
| MB_ICONEXCLAMATION
);
3410 cfg
.beep
= BELL_DEFAULT
;
3413 /* Otherwise, either visual bell or disabled; do nothing here */
3415 flash_window(2); /* start */