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 /* Dummy routine, only required in plink. */
132 void ldisc_update(int echo
, int edit
)
136 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
138 static char appname
[] = "PuTTY";
143 int guess_width
, guess_height
;
146 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
148 winsock_ver
= MAKEWORD(1, 1);
149 if (WSAStartup(winsock_ver
, &wsadata
)) {
150 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
151 MB_OK
| MB_ICONEXCLAMATION
);
154 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
155 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
156 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
160 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
163 InitCommonControls();
165 /* Ensure a Maximize setting in Explorer doesn't maximise the
170 * Process the command line.
175 default_protocol
= DEFAULT_PROTOCOL
;
176 default_port
= DEFAULT_PORT
;
177 cfg
.logtype
= LGTYP_NONE
;
179 do_defaults(NULL
, &cfg
);
182 while (*p
&& isspace(*p
))
186 * Process command line options first. Yes, this can be
187 * done better, and it will be as soon as I have the
191 char *q
= p
+ strcspn(p
, " \t");
194 tolower(p
[0]) == 's' &&
195 tolower(p
[1]) == 's' && tolower(p
[2]) == 'h') {
196 default_protocol
= cfg
.protocol
= PROT_SSH
;
197 default_port
= cfg
.port
= 22;
198 } else if (q
== p
+ 7 &&
199 tolower(p
[0]) == 'c' &&
200 tolower(p
[1]) == 'l' &&
201 tolower(p
[2]) == 'e' &&
202 tolower(p
[3]) == 'a' &&
203 tolower(p
[4]) == 'n' &&
204 tolower(p
[5]) == 'u' && tolower(p
[6]) == 'p') {
206 * `putty -cleanup'. Remove all registry entries
207 * associated with PuTTY, and also find and delete
208 * the random seed file.
211 "This procedure will remove ALL Registry\n"
212 "entries associated with PuTTY, and will\n"
213 "also remove the PuTTY random seed file.\n"
215 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
216 "SESSIONS. Are you really sure you want\n"
219 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
224 p
= q
+ strspn(q
, " \t");
228 * An initial @ means to activate a saved session.
232 while (i
> 1 && isspace(p
[i
- 1]))
235 do_defaults(p
+ 1, &cfg
);
236 if (!*cfg
.host
&& !do_config()) {
240 } else if (*p
== '&') {
242 * An initial & means we've been given a command line
243 * containing the hex value of a HANDLE for a file
244 * mapping object, which we must then extract as a
249 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
250 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
251 0, 0, sizeof(Config
))) != NULL
) {
254 CloseHandle(filemap
);
255 } else if (!do_config()) {
262 * If the hostname starts with "telnet:", set the
263 * protocol to Telnet and process the string as a
266 if (!strncmp(q
, "telnet:", 7)) {
270 if (q
[0] == '/' && q
[1] == '/')
272 cfg
.protocol
= PROT_TELNET
;
274 while (*p
&& *p
!= ':' && *p
!= '/')
283 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
284 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
286 while (*p
&& !isspace(*p
))
290 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
291 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
292 while (*p
&& isspace(*p
))
307 * Trim leading whitespace off the hostname if it's there.
310 int space
= strspn(cfg
.host
, " \t");
311 memmove(cfg
.host
, cfg
.host
+space
, 1+strlen(cfg
.host
)-space
);
314 /* See if host is of the form user@host */
315 if (cfg
.host
[0] != '\0') {
316 char *atsign
= strchr(cfg
.host
, '@');
317 /* Make sure we're not overflowing the user field */
319 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
320 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
321 cfg
.username
[atsign
- cfg
.host
] = '\0';
323 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
328 * Trim a colon suffix off the hostname if it's there.
330 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
334 * Select protocol. This is farmed out into a table in a
335 * separate file to enable an ssh-free variant.
340 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
341 if (backends
[i
].protocol
== cfg
.protocol
) {
342 back
= backends
[i
].backend
;
346 MessageBox(NULL
, "Unsupported protocol number found",
347 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
353 /* Check for invalid Port number (i.e. zero) */
355 MessageBox(NULL
, "Invalid Port Number",
356 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
363 wndclass
.lpfnWndProc
= WndProc
;
364 wndclass
.cbClsExtra
= 0;
365 wndclass
.cbWndExtra
= 0;
366 wndclass
.hInstance
= inst
;
367 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
368 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
369 wndclass
.hbrBackground
= GetStockObject(BLACK_BRUSH
);
370 wndclass
.lpszMenuName
= NULL
;
371 wndclass
.lpszClassName
= appname
;
373 RegisterClass(&wndclass
);
378 savelines
= cfg
.savelines
;
384 * Guess some defaults for the window size. This all gets
385 * updated later, so we don't really care too much. However, we
386 * do want the font width/height guesses to correspond to a
387 * large font rather than a small one...
394 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
395 guess_width
= extra_width
+ font_width
* cols
;
396 guess_height
= extra_height
+ font_height
* rows
;
399 HWND w
= GetDesktopWindow();
400 GetWindowRect(w
, &r
);
401 if (guess_width
> r
.right
- r
.left
)
402 guess_width
= r
.right
- r
.left
;
403 if (guess_height
> r
.bottom
- r
.top
)
404 guess_height
= r
.bottom
- r
.top
;
408 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
411 winmode
&= ~(WS_VSCROLL
);
413 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
415 exwinmode
|= WS_EX_TOPMOST
;
417 exwinmode
|= WS_EX_CLIENTEDGE
;
418 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
419 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
420 guess_width
, guess_height
,
421 NULL
, NULL
, inst
, NULL
);
425 * Initialise the fonts, simultaneously correcting the guesses
426 * for font_{width,height}.
428 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
433 * Correct the guesses for extra_{width,height}.
437 GetWindowRect(hwnd
, &wr
);
438 GetClientRect(hwnd
, &cr
);
439 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
440 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
444 * Resize the window, now we know what size we _really_ want it
447 guess_width
= extra_width
+ font_width
* cols
;
448 guess_height
= extra_height
+ font_height
* rows
;
449 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
450 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
451 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
454 * Set up a caret bitmap, with no content.
458 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
459 bits
= smalloc(size
);
460 memset(bits
, 0, size
);
461 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
464 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
467 * Initialise the scroll bar.
472 si
.cbSize
= sizeof(si
);
473 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
478 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
482 * Start up the telnet connection.
486 char msg
[1024], *title
;
489 error
= back
->init(cfg
.host
, cfg
.port
, &realhost
);
491 sprintf(msg
, "Unable to open connection to\n"
492 "%.800s\n" "%s", cfg
.host
, error
);
493 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
496 window_name
= icon_name
= NULL
;
498 title
= cfg
.wintitle
;
500 sprintf(msg
, "%s - PuTTY", realhost
);
508 session_closed
= FALSE
;
511 * Set up the input and output buffers.
514 outbuf_reap
= outbuf_head
= 0;
517 * Prepare the mouse handler.
519 lastact
= MA_NOTHING
;
520 lastbtn
= MBT_NOTHING
;
521 dbltime
= GetDoubleClickTime();
524 * Set up the session-control options on the system menu.
527 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
531 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
532 if (cfg
.protocol
== PROT_TELNET
) {
534 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
535 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
536 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
537 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
538 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
539 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
540 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
541 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
542 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
543 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
544 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
545 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
546 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
547 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
548 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
549 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
550 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
552 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
554 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
555 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
556 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
557 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
560 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
561 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
563 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
564 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
565 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
566 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
567 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
568 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
569 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
570 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
574 * Finally show the window!
576 ShowWindow(hwnd
, show
);
579 * Open the initial log file if there is one.
584 * Set the palette up.
590 has_focus
= (GetForegroundWindow() == hwnd
);
593 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
594 int timer_id
= 0, long_timer
= 0;
596 while (msg
.message
!= WM_QUIT
) {
597 /* Sometimes DispatchMessage calls routines that use their own
598 * GetMessage loop, setup this timer so we get some control back.
600 * Also call term_update() from the timer so that if the host
601 * is sending data flat out we still do redraws.
603 if (timer_id
&& long_timer
) {
604 KillTimer(hwnd
, timer_id
);
605 long_timer
= timer_id
= 0;
608 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
609 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
610 DispatchMessage(&msg
);
612 /* Make sure we blink everything that needs it. */
615 /* Send the paste buffer if there's anything to send */
618 /* If there's nothing new in the queue then we can do everything
619 * we've delayed, reading the socket, writing, and repainting
622 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
625 if (pending_netevent
) {
626 enact_pending_netevent();
628 /* Force the cursor blink on */
631 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
635 /* Okay there is now nothing to do so we make sure the screen is
636 * completely up to date then tell windows to call us in a little
640 KillTimer(hwnd
, timer_id
);
649 flash_window(1); /* maintain */
652 /* Hmm, term_update didn't want to do an update too soon ... */
653 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
655 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
657 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
660 /* There's no point rescanning everything in the message queue
661 * so we do an apparently unnecessary wait here
664 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
678 if (cfg
.protocol
== PROT_SSH
) {
689 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
691 char *do_select(SOCKET skt
, int startup
)
696 events
= FD_READ
| FD_WRITE
| FD_OOB
| FD_CLOSE
;
701 return "do_select(): internal error (hwnd==NULL)";
702 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
703 switch (WSAGetLastError()) {
705 return "Network is down";
707 return "WSAAsyncSelect(): unknown error";
714 * set or clear the "raw mouse message" mode
716 void set_raw_mouse_mode(int activate
)
718 send_raw_mouse
= activate
;
719 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
723 * Print a message box and close the connection.
725 void connection_fatal(char *fmt
, ...)
731 vsprintf(stuff
, fmt
, ap
);
733 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
734 if (cfg
.close_on_exit
== COE_ALWAYS
)
737 session_closed
= TRUE
;
738 SetWindowText(hwnd
, "PuTTY (inactive)");
743 * Actually do the job requested by a WM_NETEVENT
745 static void enact_pending_netevent(void)
747 static int reentering
= 0;
748 extern int select_result(WPARAM
, LPARAM
);
752 return; /* don't unpend the pending */
754 pending_netevent
= FALSE
;
757 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
760 if (ret
== 0 && !session_closed
) {
761 /* Abnormal exits will already have set session_closed and taken
762 * appropriate action. */
763 if (cfg
.close_on_exit
== COE_ALWAYS
||
764 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
766 session_closed
= TRUE
;
767 SetWindowText(hwnd
, "PuTTY (inactive)");
768 MessageBox(hwnd
, "Connection closed by remote host",
769 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
775 * Copy the colour palette from the configuration data into defpal.
776 * This is non-trivial because the colour indices are different.
778 static void cfgtopalette(void)
781 static const int ww
[] = {
782 6, 7, 8, 9, 10, 11, 12, 13,
783 14, 15, 16, 17, 18, 19, 20, 21,
784 0, 1, 2, 3, 4, 4, 5, 5
787 for (i
= 0; i
< 24; i
++) {
789 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
790 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
791 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
796 * Set up the colour palette.
798 static void init_palette(void)
801 HDC hdc
= GetDC(hwnd
);
803 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
804 logpal
= smalloc(sizeof(*logpal
)
805 - sizeof(logpal
->palPalEntry
)
806 + NCOLOURS
* sizeof(PALETTEENTRY
));
807 logpal
->palVersion
= 0x300;
808 logpal
->palNumEntries
= NCOLOURS
;
809 for (i
= 0; i
< NCOLOURS
; i
++) {
810 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
811 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
812 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
813 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
815 pal
= CreatePalette(logpal
);
817 SelectPalette(hdc
, pal
, FALSE
);
819 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
822 ReleaseDC(hwnd
, hdc
);
825 for (i
= 0; i
< NCOLOURS
; i
++)
826 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
830 for (i
= 0; i
< NCOLOURS
; i
++)
831 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
832 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
836 * Initialise all the fonts we will need initially. There may be as many as
837 * three or as few as one. The other (poentially) twentyone fonts are done
838 * if/when they are needed.
842 * - check the font width and height, correcting our guesses if
845 * - verify that the bold font is the same width as the ordinary
846 * one, and engage shadow bolding if not.
848 * - verify that the underlined font is the same width as the
849 * ordinary one (manual underlining by means of line drawing can
850 * be done in a pinch).
852 static void init_fonts(int pick_width
)
859 int fw_dontcare
, fw_bold
;
861 for (i
= 0; i
< FONT_MAXNO
; i
++)
864 if (cfg
.fontisbold
) {
865 fw_dontcare
= FW_BOLD
;
868 fw_dontcare
= FW_DONTCARE
;
874 font_height
= cfg
.fontheight
;
875 if (font_height
> 0) {
877 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
879 font_width
= pick_width
;
882 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
883 c, OUT_DEFAULT_PRECIS, \
884 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
885 FIXED_PITCH | FF_DONTCARE, cfg.font)
887 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
889 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
890 GetTextMetrics(hdc
, &tm
);
891 font_height
= tm
.tmHeight
;
892 font_width
= tm
.tmAveCharWidth
;
896 DWORD cset
= tm
.tmCharSet
;
897 memset(&info
, 0xFF, sizeof(info
));
899 /* !!! Yes the next line is right */
900 if (cset
== OEM_CHARSET
)
901 font_codepage
= GetOEMCP();
903 if (TranslateCharsetInfo
904 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
909 GetCPInfo(font_codepage
, &cpinfo
);
910 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
913 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
916 * Some fonts, e.g. 9-pt Courier, draw their underlines
917 * outside their character cell. We successfully prevent
918 * screen corruption by clipping the text output, but then
919 * we lose the underline completely. Here we try to work
920 * out whether this is such a font, and if it is, we set a
921 * flag that causes underlines to be drawn by hand.
923 * Having tried other more sophisticated approaches (such
924 * as examining the TEXTMETRIC structure or requesting the
925 * height of a string), I think we'll do this the brute
926 * force way: we create a small bitmap, draw an underlined
927 * space on it, and test to see whether any pixels are
928 * foreground-coloured. (Since we expect the underline to
929 * go all the way across the character cell, we only search
930 * down a single column of the bitmap, half way across.)
934 HBITMAP und_bm
, und_oldbm
;
938 und_dc
= CreateCompatibleDC(hdc
);
939 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
940 und_oldbm
= SelectObject(und_dc
, und_bm
);
941 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
942 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
943 SetTextColor(und_dc
, RGB(255, 255, 255));
944 SetBkColor(und_dc
, RGB(0, 0, 0));
945 SetBkMode(und_dc
, OPAQUE
);
946 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
948 for (i
= 0; i
< font_height
; i
++) {
949 c
= GetPixel(und_dc
, font_width
/ 2, i
);
950 if (c
!= RGB(0, 0, 0))
953 SelectObject(und_dc
, und_oldbm
);
954 DeleteObject(und_bm
);
958 DeleteObject(fonts
[FONT_UNDERLINE
]);
959 fonts
[FONT_UNDERLINE
] = 0;
963 if (bold_mode
== BOLD_FONT
) {
964 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
968 descent
= tm
.tmAscent
+ 1;
969 if (descent
>= font_height
)
970 descent
= font_height
- 1;
972 for (i
= 0; i
< 3; i
++) {
974 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
975 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
982 ReleaseDC(hwnd
, hdc
);
984 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
986 DeleteObject(fonts
[FONT_UNDERLINE
]);
987 fonts
[FONT_UNDERLINE
] = 0;
990 if (bold_mode
== BOLD_FONT
&&
991 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
992 bold_mode
= BOLD_SHADOW
;
993 DeleteObject(fonts
[FONT_BOLD
]);
994 fonts
[FONT_BOLD
] = 0;
996 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1001 static void another_font(int fontno
)
1004 int fw_dontcare
, fw_bold
;
1008 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1011 basefont
= (fontno
& ~(FONT_BOLDUND
));
1012 if (basefont
!= fontno
&& !fontflag
[basefont
])
1013 another_font(basefont
);
1015 if (cfg
.fontisbold
) {
1016 fw_dontcare
= FW_BOLD
;
1019 fw_dontcare
= FW_DONTCARE
;
1023 c
= cfg
.fontcharset
;
1029 if (fontno
& FONT_WIDE
)
1031 if (fontno
& FONT_NARROW
)
1033 if (fontno
& FONT_OEM
)
1035 if (fontno
& FONT_BOLD
)
1037 if (fontno
& FONT_UNDERLINE
)
1041 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1042 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1043 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1044 FIXED_PITCH
| FF_DONTCARE
, s
);
1046 fontflag
[fontno
] = 1;
1049 static void deinit_fonts(void)
1052 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1054 DeleteObject(fonts
[i
]);
1060 void request_resize(int w
, int h
, int refont
)
1064 /* If the window is maximized supress resizing attempts */
1068 if (refont
&& w
!= cols
&& (cols
== 80 || cols
== 132)) {
1069 /* If font width too big for screen should we shrink the font more ? */
1071 font_width
= ((font_width
* cols
+ w
/ 2) / w
);
1075 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1076 und_mode
= UND_FONT
;
1077 init_fonts(font_width
);
1079 static int first_time
= 1;
1082 switch (first_time
) {
1084 /* Get the size of the screen */
1085 if (GetClientRect(GetDesktopWindow(), &ss
))
1086 /* first_time = 0 */ ;
1092 /* Make sure the values are sane */
1093 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1094 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1107 width
= extra_width
+ font_width
* w
;
1108 height
= extra_height
+ font_height
* h
;
1110 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1111 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1112 SWP_NOMOVE
| SWP_NOZORDER
);
1115 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
)
1117 int thistime
= GetMessageTime();
1119 if (send_raw_mouse
) {
1120 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
);
1124 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1125 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1126 lastact
== MA_2CLK ? MA_3CLK
:
1127 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1132 if (lastact
!= MA_NOTHING
)
1133 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
);
1134 lasttime
= thistime
;
1138 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1139 * into a cooked one (SELECT, EXTEND, PASTE).
1141 Mouse_Button
translate_button(Mouse_Button button
)
1143 if (button
== MBT_LEFT
)
1145 if (button
== MBT_MIDDLE
)
1146 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1147 if (button
== MBT_RIGHT
)
1148 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1149 return 0; /* shouldn't happen */
1152 static void show_mouseptr(int show
)
1154 static int cursor_visible
= 1;
1155 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1157 if (cursor_visible
&& !show
)
1159 else if (!cursor_visible
&& show
)
1161 cursor_visible
= show
;
1164 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1165 WPARAM wParam
, LPARAM lParam
)
1168 static int ignore_size
= FALSE
;
1169 static int ignore_clip
= FALSE
;
1170 static int just_reconfigged
= FALSE
;
1171 static int resizing
= FALSE
;
1172 static int need_backend_resize
= FALSE
;
1173 static int defered_resize
= FALSE
;
1177 if (pending_netevent
)
1178 enact_pending_netevent();
1185 if (cfg
.ping_interval
> 0) {
1188 if (now
- last_movement
> cfg
.ping_interval
) {
1189 back
->special(TS_PING
);
1190 last_movement
= now
;
1198 if (!cfg
.warn_on_close
|| session_closed
||
1200 "Are you sure you want to close this session?",
1201 "PuTTY Exit Confirmation",
1202 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1203 DestroyWindow(hwnd
);
1210 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1222 PROCESS_INFORMATION pi
;
1223 HANDLE filemap
= NULL
;
1225 if (wParam
== IDM_DUPSESS
) {
1227 * Allocate a file-mapping memory chunk for the
1230 SECURITY_ATTRIBUTES sa
;
1233 sa
.nLength
= sizeof(sa
);
1234 sa
.lpSecurityDescriptor
= NULL
;
1235 sa
.bInheritHandle
= TRUE
;
1236 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1239 0, sizeof(Config
), NULL
);
1241 p
= (Config
*) MapViewOfFile(filemap
,
1243 0, 0, sizeof(Config
));
1245 *p
= cfg
; /* structure copy */
1249 sprintf(c
, "putty &%p", filemap
);
1251 } else if (wParam
== IDM_SAVEDSESS
) {
1253 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1254 cl
= smalloc(16 + strlen(session
)); /* 8, but play safe */
1256 cl
= NULL
; /* not a very important failure mode */
1258 sprintf(cl
, "putty @%s", session
);
1264 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1266 si
.lpReserved
= NULL
;
1267 si
.lpDesktop
= NULL
;
1271 si
.lpReserved2
= NULL
;
1272 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1273 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1276 CloseHandle(filemap
);
1283 int prev_alwaysontop
= cfg
.alwaysontop
;
1284 int prev_sunken_edge
= cfg
.sunken_edge
;
1285 char oldlogfile
[FILENAME_MAX
];
1287 int need_setwpos
= FALSE
;
1288 int old_fwidth
, old_fheight
;
1290 strcpy(oldlogfile
, cfg
.logfilename
);
1291 oldlogtype
= cfg
.logtype
;
1292 old_fwidth
= font_width
;
1293 old_fheight
= font_height
;
1294 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1296 if (!do_reconfig(hwnd
))
1299 if (strcmp(oldlogfile
, cfg
.logfilename
) ||
1300 oldlogtype
!= cfg
.logtype
) {
1301 logfclose(); /* reset logging */
1305 just_reconfigged
= TRUE
;
1307 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1308 und_mode
= UND_FONT
;
1312 * Flush the line discipline's edit buffer in the
1313 * case where local editing has just been disabled.
1315 ldisc_send(NULL
, 0);
1323 /* Enable or disable the scroll bar, etc */
1325 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1326 LONG nexflag
, exflag
=
1327 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1330 if (cfg
.alwaysontop
!= prev_alwaysontop
) {
1331 if (cfg
.alwaysontop
) {
1332 nexflag
|= WS_EX_TOPMOST
;
1333 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1334 SWP_NOMOVE
| SWP_NOSIZE
);
1336 nexflag
&= ~(WS_EX_TOPMOST
);
1337 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1338 SWP_NOMOVE
| SWP_NOSIZE
);
1341 if (cfg
.sunken_edge
)
1342 nexflag
|= WS_EX_CLIENTEDGE
;
1344 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1350 nflg
&= ~WS_VSCROLL
;
1352 nflg
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1354 nflg
|= (WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1356 if (nflg
!= flag
|| nexflag
!= exflag
) {
1360 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1361 if (nexflag
!= exflag
)
1362 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1364 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
1366 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1367 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1368 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
1369 | SWP_FRAMECHANGED
);
1371 GetWindowRect(hwnd
, &wr
);
1372 GetClientRect(hwnd
, &cr
);
1374 wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1376 wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1377 need_setwpos
= TRUE
;
1381 if (cfg
.height
!= rows
||
1382 cfg
.width
!= cols
||
1383 old_fwidth
!= font_width
||
1384 old_fheight
!= font_height
||
1385 cfg
.savelines
!= savelines
||
1386 cfg
.sunken_edge
!= prev_sunken_edge
)
1387 need_setwpos
= TRUE
;
1389 if (IsZoomed(hwnd
)) {
1393 defered_resize
= TRUE
;
1395 GetClientRect(hwnd
, &cr
);
1396 w
= cr
.right
- cr
.left
;
1397 h
= cr
.bottom
- cr
.top
;
1401 h
= h
/ font_height
;
1405 term_size(h
, w
, cfg
.savelines
);
1406 InvalidateRect(hwnd
, NULL
, TRUE
);
1409 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1410 InvalidateRect(hwnd
, NULL
, TRUE
);
1412 SetWindowPos(hwnd
, NULL
, 0, 0,
1413 extra_width
+ font_width
* cfg
.width
,
1415 font_height
* cfg
.height
,
1416 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1417 SWP_NOMOVE
| SWP_NOZORDER
);
1421 if (cfg
.locksize
&& IsZoomed(hwnd
))
1423 set_title(cfg
.wintitle
);
1424 if (IsIconic(hwnd
)) {
1426 cfg
.win_name_always ? window_name
:
1441 back
->special(TS_AYT
);
1444 back
->special(TS_BRK
);
1447 back
->special(TS_SYNCH
);
1450 back
->special(TS_EC
);
1453 back
->special(TS_EL
);
1456 back
->special(TS_GA
);
1459 back
->special(TS_NOP
);
1462 back
->special(TS_ABORT
);
1465 back
->special(TS_AO
);
1468 back
->special(TS_IP
);
1471 back
->special(TS_SUSP
);
1474 back
->special(TS_EOR
);
1477 back
->special(TS_EOF
);
1483 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1484 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1489 #define X_POS(l) ((int)(short)LOWORD(l))
1490 #define Y_POS(l) ((int)(short)HIWORD(l))
1492 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1493 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1494 #define WHEEL_DELTA 120
1497 wheel_accumulator
+= (short) HIWORD(wParam
);
1498 wParam
= LOWORD(wParam
);
1500 /* process events when the threshold is reached */
1501 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
1504 /* reduce amount for next time */
1505 if (wheel_accumulator
> 0) {
1507 wheel_accumulator
-= WHEEL_DELTA
;
1508 } else if (wheel_accumulator
< 0) {
1510 wheel_accumulator
+= WHEEL_DELTA
;
1514 if (send_raw_mouse
) {
1515 /* send a mouse-down followed by a mouse up */
1518 TO_CHR_X(X_POS(lParam
)),
1519 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1520 wParam
& MK_CONTROL
);
1521 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1522 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1523 wParam
& MK_CONTROL
);
1525 /* trigger a scroll */
1527 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
1532 case WM_LBUTTONDOWN
:
1533 case WM_MBUTTONDOWN
:
1534 case WM_RBUTTONDOWN
:
1541 case WM_LBUTTONDOWN
:
1545 case WM_MBUTTONDOWN
:
1546 button
= MBT_MIDDLE
;
1549 case WM_RBUTTONDOWN
:
1558 button
= MBT_MIDDLE
;
1566 button
= press
= 0; /* shouldn't happen */
1571 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1572 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
);
1575 term_mouse(button
, MA_RELEASE
,
1576 TO_CHR_X(X_POS(lParam
)),
1577 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1578 wParam
& MK_CONTROL
);
1586 * Add the mouse position and message time to the random
1589 noise_ultralight(lParam
);
1591 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1593 if (wParam
& MK_LBUTTON
)
1595 else if (wParam
& MK_MBUTTON
)
1596 b
= cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1598 b
= cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1599 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1600 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1601 wParam
& MK_CONTROL
);
1604 case WM_NCMOUSEMOVE
:
1606 noise_ultralight(lParam
);
1608 case WM_IGNORE_CLIP
:
1609 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1611 case WM_DESTROYCLIPBOARD
:
1614 ignore_clip
= FALSE
;
1620 hdc
= BeginPaint(hwnd
, &p
);
1622 SelectPalette(hdc
, pal
, TRUE
);
1623 RealizePalette(hdc
);
1625 term_paint(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1626 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1627 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
1628 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
1634 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1635 * but the only one that's likely to try to overload us is FD_READ.
1636 * This means buffering just one is fine.
1638 if (pending_netevent
)
1639 enact_pending_netevent();
1641 pending_netevent
= TRUE
;
1642 pend_netevent_wParam
= wParam
;
1643 pend_netevent_lParam
= lParam
;
1644 time(&last_movement
);
1648 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
1650 flash_window(0); /* stop */
1662 case WM_IGNORE_SIZE
:
1663 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1665 case WM_ENTERSIZEMOVE
:
1668 need_backend_resize
= FALSE
;
1670 case WM_EXITSIZEMOVE
:
1673 if (need_backend_resize
)
1678 int width
, height
, w
, h
, ew
, eh
;
1679 LPRECT r
= (LPRECT
) lParam
;
1681 width
= r
->right
- r
->left
- extra_width
;
1682 height
= r
->bottom
- r
->top
- extra_height
;
1683 w
= (width
+ font_width
/ 2) / font_width
;
1686 h
= (height
+ font_height
/ 2) / font_height
;
1689 UpdateSizeTip(hwnd
, w
, h
);
1690 ew
= width
- w
* font_width
;
1691 eh
= height
- h
* font_height
;
1693 if (wParam
== WMSZ_LEFT
||
1694 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
1700 if (wParam
== WMSZ_TOP
||
1701 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
1711 /* break; (never reached) */
1713 if (wParam
== SIZE_MINIMIZED
) {
1715 cfg
.win_name_always ? window_name
: icon_name
);
1718 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1719 SetWindowText(hwnd
, window_name
);
1721 int width
, height
, w
, h
;
1722 #if 0 /* we have fixed this using WM_SIZING now */
1726 width
= LOWORD(lParam
);
1727 height
= HIWORD(lParam
);
1728 w
= width
/ font_width
;
1731 h
= height
/ font_height
;
1734 #if 0 /* we have fixed this using WM_SIZING now */
1735 ew
= width
- w
* font_width
;
1736 eh
= height
- h
* font_height
;
1737 if (ew
!= 0 || eh
!= 0) {
1739 GetWindowRect(hwnd
, &r
);
1740 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
1741 SetWindowPos(hwnd
, NULL
, 0, 0,
1742 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1743 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1746 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1748 term_size(h
, w
, cfg
.savelines
);
1750 * Don't call back->size in mid-resize. (To prevent
1751 * massive numbers of resize events getting sent
1752 * down the connection during an NT opaque drag.)
1757 need_backend_resize
= TRUE
;
1761 just_reconfigged
= FALSE
;
1764 if (wParam
== SIZE_RESTORED
&& defered_resize
) {
1765 defered_resize
= FALSE
;
1766 SetWindowPos(hwnd
, NULL
, 0, 0,
1767 extra_width
+ font_width
* cfg
.width
,
1768 extra_height
+ font_height
* cfg
.height
,
1769 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1770 SWP_NOMOVE
| SWP_NOZORDER
);
1772 ignore_size
= FALSE
;
1775 switch (LOWORD(wParam
)) {
1789 term_scroll(0, +rows
/ 2);
1792 term_scroll(0, -rows
/ 2);
1794 case SB_THUMBPOSITION
:
1796 term_scroll(1, HIWORD(wParam
));
1800 case WM_PALETTECHANGED
:
1801 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1802 HDC hdc
= get_ctx();
1804 if (RealizePalette(hdc
) > 0)
1810 case WM_QUERYNEWPALETTE
:
1812 HDC hdc
= get_ctx();
1814 if (RealizePalette(hdc
) > 0)
1826 * Add the scan code and keypress timing to the random
1829 noise_ultralight(lParam
);
1832 * We don't do TranslateMessage since it disassociates the
1833 * resulting CHAR message from the KEYDOWN that sparked it,
1834 * which we occasionally don't want. Instead, we process
1835 * KEYDOWN, and call the Win32 translator functions so that
1836 * we get the translations under _our_ control.
1839 unsigned char buf
[20];
1842 if (wParam
== VK_PROCESSKEY
) {
1845 m
.message
= WM_KEYDOWN
;
1847 m
.lParam
= lParam
& 0xdfff;
1848 TranslateMessage(&m
);
1850 len
= TranslateKey(message
, wParam
, lParam
, buf
);
1852 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1853 ldisc_send(buf
, len
);
1860 case WM_INPUTLANGCHANGE
:
1862 /* wParam == Font number */
1863 /* lParam == Locale */
1865 HKL NewInputLocale
= (HKL
) lParam
;
1867 // lParam == GetKeyboardLayout(0);
1869 GetLocaleInfo(LOWORD(NewInputLocale
),
1870 LOCALE_IDEFAULTANSICODEPAGE
, lbuf
, sizeof(lbuf
));
1872 kbd_codepage
= atoi(lbuf
);
1876 if (wParam
& 0xFF00) {
1877 unsigned char buf
[2];
1880 buf
[0] = wParam
>> 8;
1881 lpage_send(kbd_codepage
, buf
, 2);
1883 char c
= (unsigned char) wParam
;
1884 lpage_send(kbd_codepage
, &c
, 1);
1890 * Nevertheless, we are prepared to deal with WM_CHAR
1891 * messages, should they crop up. So if someone wants to
1892 * post the things to us as part of a macro manoeuvre,
1893 * we're ready to cope.
1896 char c
= (unsigned char)wParam
;
1897 lpage_send(CP_ACP
, &c
, 1);
1901 if (send_raw_mouse
) {
1902 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
1907 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1911 * Move the system caret. (We maintain one, even though it's
1912 * invisible, for the benefit of blind people: apparently some
1913 * helper software tracks the system caret, so we should arrange to
1916 void sys_cursor(int x
, int y
)
1919 SetCaretPos(x
* font_width
, y
* font_height
);
1923 * Draw a line of text in the window, at given character
1924 * coordinates, in given attributes.
1926 * We are allowed to fiddle with the contents of `text'.
1928 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
1929 unsigned long attr
, int lattr
)
1932 int nfg
, nbg
, nfont
;
1935 int force_manual_underline
= 0;
1936 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
1937 int char_width
= fnt_width
;
1938 int text_adjust
= 0;
1939 static int *IpDx
= 0, IpDxLEN
= 0;
1941 if (attr
& ATTR_WIDE
)
1944 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
1946 if (len
> IpDxLEN
) {
1948 IpDx
= smalloc((len
+ 16) * sizeof(int));
1949 IpDxLEN
= (len
+ 16);
1951 for (i
= 0; i
< IpDxLEN
; i
++)
1952 IpDx
[i
] = char_width
;
1958 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
1959 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
1960 attr
^= ATTR_CUR_XOR
;
1964 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
1965 /* Assume a poorman font is borken in other ways too. */
1975 nfont
|= FONT_WIDE
+ FONT_HIGH
;
1979 /* Special hack for the VT100 linedraw glyphs. */
1980 if ((attr
& CSET_MASK
) == 0x2300) {
1981 if (!dbcs_screenfont
&&
1982 text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
1983 switch ((unsigned char) (text
[0])) {
1985 text_adjust
= -2 * font_height
/ 5;
1988 text_adjust
= -1 * font_height
/ 5;
1991 text_adjust
= font_height
/ 5;
1994 text_adjust
= 2 * font_height
/ 5;
1997 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2000 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2001 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2002 if (attr
& ATTR_UNDER
) {
2003 attr
&= ~ATTR_UNDER
;
2004 force_manual_underline
= 1;
2009 /* Anything left as an original character set is unprintable. */
2010 if (DIRECT_CHAR(attr
)) {
2013 memset(text
, 0xFF, len
);
2017 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2020 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2021 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2022 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2024 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2025 nfont
|= FONT_UNDERLINE
;
2026 another_font(nfont
);
2027 if (!fonts
[nfont
]) {
2028 if (nfont
& FONT_UNDERLINE
)
2029 force_manual_underline
= 1;
2030 /* Don't do the same for manual bold, it could be bad news. */
2032 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2034 another_font(nfont
);
2036 nfont
= FONT_NORMAL
;
2037 if (attr
& ATTR_REVERSE
) {
2042 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2044 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2048 SelectObject(hdc
, fonts
[nfont
]);
2049 SetTextColor(hdc
, fg
);
2050 SetBkColor(hdc
, bg
);
2051 SetBkMode(hdc
, OPAQUE
);
2054 line_box
.right
= x
+ char_width
* len
;
2055 line_box
.bottom
= y
+ font_height
;
2057 /* We're using a private area for direct to font. (512 chars.) */
2058 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2059 /* Ho Hum, dbcs fonts are a PITA! */
2060 /* To display on W9x I have to convert to UCS */
2061 static wchar_t *uni_buf
= 0;
2062 static int uni_len
= 0;
2064 if (len
> uni_len
) {
2066 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2068 nlen
= MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2069 text
, len
, uni_buf
, uni_len
);
2075 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2076 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, 0);
2077 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2078 SetBkMode(hdc
, TRANSPARENT
);
2079 ExtTextOutW(hdc
, x
- 1,
2080 y
- font_height
* (lattr
==
2081 LATTR_BOT
) + text_adjust
,
2082 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, 0);
2084 } else if (DIRECT_FONT(attr
)) {
2086 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2087 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2088 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2089 SetBkMode(hdc
, TRANSPARENT
);
2091 /* GRR: This draws the character outside it's box and can leave
2092 * 'droppings' even with the clip box! I suppose I could loop it
2093 * one character at a time ... yuk.
2095 * Or ... I could do a test print with "W", and use +1 or -1 for this
2096 * shift depending on if the leftmost column is blank...
2098 ExtTextOut(hdc
, x
- 1,
2099 y
- font_height
* (lattr
==
2100 LATTR_BOT
) + text_adjust
,
2101 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2104 /* And 'normal' unicode characters */
2105 static WCHAR
*wbuf
= NULL
;
2106 static int wlen
= 0;
2111 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2113 for (i
= 0; i
< len
; i
++)
2114 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2117 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2118 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2120 /* And the shadow bold hack. */
2121 if (bold_mode
== BOLD_SHADOW
) {
2122 SetBkMode(hdc
, TRANSPARENT
);
2123 ExtTextOutW(hdc
, x
- 1,
2124 y
- font_height
* (lattr
==
2125 LATTR_BOT
) + text_adjust
,
2126 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2129 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2130 (und_mode
== UND_LINE
2131 && (attr
& ATTR_UNDER
)))) {
2134 if (lattr
== LATTR_BOT
)
2135 dec
= dec
* 2 - font_height
;
2137 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2138 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2139 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2140 oldpen
= SelectObject(hdc
, oldpen
);
2141 DeleteObject(oldpen
);
2145 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2146 unsigned long attr
, int lattr
)
2152 int ctype
= cfg
.cursor_type
;
2154 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2155 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2156 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2160 attr
|= TATTR_RIGHTCURS
;
2163 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2164 if (attr
& ATTR_WIDE
)
2169 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2172 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2173 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2174 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2175 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2176 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2177 Polyline(hdc
, pts
, 5);
2178 oldpen
= SelectObject(hdc
, oldpen
);
2179 DeleteObject(oldpen
);
2180 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2181 int startx
, starty
, dx
, dy
, length
, i
;
2184 starty
= y
+ descent
;
2187 length
= char_width
;
2190 if (attr
& TATTR_RIGHTCURS
)
2191 xadjust
= char_width
- 1;
2192 startx
= x
+ xadjust
;
2196 length
= font_height
;
2198 if (attr
& TATTR_ACTCURS
) {
2201 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2202 MoveToEx(hdc
, startx
, starty
, NULL
);
2203 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2204 oldpen
= SelectObject(hdc
, oldpen
);
2205 DeleteObject(oldpen
);
2207 for (i
= 0; i
< length
; i
++) {
2209 SetPixel(hdc
, startx
, starty
, colours
[23]);
2219 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2220 * codes. Returns number of bytes used or zero to drop the message
2221 * or -1 to forward the message to windows.
2223 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2224 unsigned char *output
)
2227 int scan
, left_alt
= 0, key_down
, shift_state
;
2229 unsigned char *p
= output
;
2230 static int alt_state
= 0;
2231 static int alt_sum
= 0;
2233 HKL kbd_layout
= GetKeyboardLayout(0);
2235 static WORD keys
[3];
2236 static int compose_char
= 0;
2237 static WPARAM compose_key
= 0;
2239 r
= GetKeyboardState(keystate
);
2241 memset(keystate
, 0, sizeof(keystate
));
2244 #define SHOW_TOASCII_RESULT
2245 { /* Tell us all about key events */
2246 static BYTE oldstate
[256];
2247 static int first
= 1;
2251 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2254 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2256 } else if ((HIWORD(lParam
) & KF_UP
)
2257 && scan
== (HIWORD(lParam
) & 0xFF)) {
2261 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2262 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2275 debug(("VK_%02x", wParam
));
2277 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2279 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2281 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2282 if (ch
>= ' ' && ch
<= '~')
2283 debug((", '%c'", ch
));
2285 debug((", $%02x", ch
));
2288 debug((", KB0=%02x", keys
[0]));
2290 debug((", KB1=%02x", keys
[1]));
2292 debug((", KB2=%02x", keys
[2]));
2294 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2296 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2298 if ((HIWORD(lParam
) & KF_EXTENDED
))
2300 if ((HIWORD(lParam
) & KF_UP
))
2304 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2305 else if ((HIWORD(lParam
) & KF_UP
))
2306 oldstate
[wParam
& 0xFF] ^= 0x80;
2308 oldstate
[wParam
& 0xFF] ^= 0x81;
2310 for (ch
= 0; ch
< 256; ch
++)
2311 if (oldstate
[ch
] != keystate
[ch
])
2312 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2314 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2318 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2319 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2323 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2324 if ((cfg
.funky_type
== 3 ||
2325 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2326 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2328 wParam
= VK_EXECUTE
;
2330 /* UnToggle NUMLock */
2331 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2332 keystate
[VK_NUMLOCK
] ^= 1;
2335 /* And write back the 'adjusted' state */
2336 SetKeyboardState(keystate
);
2339 /* Disable Auto repeat if required */
2340 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2343 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2346 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2348 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2349 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2350 if (cfg
.ctrlaltkeys
)
2351 keystate
[VK_MENU
] = 0;
2353 keystate
[VK_RMENU
] = 0x80;
2358 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2359 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2360 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2362 /* Note if AltGr was pressed and if it was used as a compose key */
2363 if (!compose_state
) {
2364 compose_key
= 0x100;
2365 if (cfg
.compose_key
) {
2366 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2367 compose_key
= wParam
;
2369 if (wParam
== VK_APPS
)
2370 compose_key
= wParam
;
2373 if (wParam
== compose_key
) {
2374 if (compose_state
== 0
2375 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2377 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2381 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
2385 * Record that we pressed key so the scroll window can be reset, but
2386 * be careful to avoid Shift-UP/Down
2388 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
2392 /* Make sure we're not pasting */
2396 if (compose_state
> 1 && left_alt
)
2399 /* Sanitize the number pad if not using a PC NumPad */
2400 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2401 && cfg
.funky_type
!= 2)
2402 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
2403 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
2407 nParam
= VK_NUMPAD0
;
2410 nParam
= VK_NUMPAD1
;
2413 nParam
= VK_NUMPAD2
;
2416 nParam
= VK_NUMPAD3
;
2419 nParam
= VK_NUMPAD4
;
2422 nParam
= VK_NUMPAD5
;
2425 nParam
= VK_NUMPAD6
;
2428 nParam
= VK_NUMPAD7
;
2431 nParam
= VK_NUMPAD8
;
2434 nParam
= VK_NUMPAD9
;
2437 nParam
= VK_DECIMAL
;
2441 if (keystate
[VK_NUMLOCK
] & 1)
2448 /* If a key is pressed and AltGr is not active */
2449 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
2450 /* Okay, prepare for most alts then ... */
2454 /* Lets see if it's a pattern we know all about ... */
2455 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2456 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2459 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2460 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
2463 if (wParam
== VK_INSERT
&& shift_state
== 1) {
2464 term_mouse(MBT_PASTE
, MA_CLICK
, 0, 0, 0, 0);
2465 term_mouse(MBT_PASTE
, MA_RELEASE
, 0, 0, 0, 0);
2468 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
2471 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
2473 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
2474 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
2477 /* Control-Numlock for app-keypad mode switch */
2478 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
2479 app_keypad_keys
^= 1;
2483 /* Nethack keypad */
2484 if (cfg
.nethack_keypad
&& !left_alt
) {
2487 *p
++ = shift_state ?
'B' : 'b';
2490 *p
++ = shift_state ?
'J' : 'j';
2493 *p
++ = shift_state ?
'N' : 'n';
2496 *p
++ = shift_state ?
'H' : 'h';
2499 *p
++ = shift_state ?
'.' : '.';
2502 *p
++ = shift_state ?
'L' : 'l';
2505 *p
++ = shift_state ?
'Y' : 'y';
2508 *p
++ = shift_state ?
'K' : 'k';
2511 *p
++ = shift_state ?
'U' : 'u';
2516 /* Application Keypad */
2520 if (cfg
.funky_type
== 3 ||
2521 (cfg
.funky_type
<= 1 &&
2522 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
2536 if (app_keypad_keys
&& !cfg
.no_applic_k
)
2573 if (cfg
.funky_type
== 2) {
2578 } else if (shift_state
)
2585 if (cfg
.funky_type
== 2)
2589 if (cfg
.funky_type
== 2)
2593 if (cfg
.funky_type
== 2)
2598 if (HIWORD(lParam
) & KF_EXTENDED
)
2604 if (xkey
>= 'P' && xkey
<= 'S')
2605 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
2607 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
2609 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
2614 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
2615 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
2619 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
2625 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
2629 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
2633 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
2638 if (wParam
== VK_PAUSE
) { /* Break/Pause */
2643 /* Control-2 to Control-8 are special */
2644 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
2645 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
2648 if (shift_state
== 2 && wParam
== 0xBD) {
2652 if (shift_state
== 2 && wParam
== 0xDF) {
2656 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
2663 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2664 * for integer decimal nn.)
2666 * We also deal with the weird ones here. Linux VCs replace F1
2667 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2668 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2674 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
2677 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
2680 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
2683 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
2686 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
2689 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
2692 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
2695 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
2698 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
2701 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
2752 /* Reorder edit keys to physical order */
2753 if (cfg
.funky_type
== 3 && code
<= 6)
2754 code
= "\0\2\1\4\5\3\6"[code
];
2756 if (vt52_mode
&& code
> 0 && code
<= 6) {
2757 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
2761 if (cfg
.funky_type
== 5 && code
>= 11 && code
<= 34) {
2762 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
2765 case VK_F1
: index
= 0; break;
2766 case VK_F2
: index
= 1; break;
2767 case VK_F3
: index
= 2; break;
2768 case VK_F4
: index
= 3; break;
2769 case VK_F5
: index
= 4; break;
2770 case VK_F6
: index
= 5; break;
2771 case VK_F7
: index
= 6; break;
2772 case VK_F8
: index
= 7; break;
2773 case VK_F9
: index
= 8; break;
2774 case VK_F10
: index
= 9; break;
2775 case VK_F11
: index
= 10; break;
2776 case VK_F12
: index
= 11; break;
2778 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
2779 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
2780 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
2783 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
2790 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
2793 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
2796 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
2797 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
2800 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
2802 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
2804 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
2807 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
2808 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
2812 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
2817 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2818 * some reason seems to send VK_CLEAR to Windows...).
2841 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
2843 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
2844 /* VT100 & VT102 manuals both state the app cursor keys
2845 * only work if the app keypad is on.
2847 if (!app_keypad_keys
)
2849 /* Useful mapping of Ctrl-arrows */
2850 if (shift_state
== 2)
2854 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
2856 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
2863 * Finally, deal with Return ourselves. (Win95 seems to
2864 * foul it up when Alt is pressed, for some reason.)
2866 if (wParam
== VK_RETURN
) { /* Return */
2872 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
2873 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
2878 /* Okay we've done everything interesting; let windows deal with
2879 * the boring stuff */
2881 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
2882 #ifdef SHOW_TOASCII_RESULT
2883 if (r
== 1 && !key_down
) {
2885 if (utf
|| dbcs_screenfont
)
2886 debug((", (U+%04x)", alt_sum
));
2888 debug((", LCH(%d)", alt_sum
));
2890 debug((", ACH(%d)", keys
[0]));
2895 for (r1
= 0; r1
< r
; r1
++) {
2896 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
2904 for (i
= 0; i
< r
; i
++) {
2905 unsigned char ch
= (unsigned char) keys
[i
];
2907 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
2912 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
2916 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
2917 MessageBeep(MB_ICONHAND
);
2921 luni_send(&keybuf
, 1);
2929 if (utf
|| dbcs_screenfont
) {
2931 luni_send(&keybuf
, 1);
2933 ch
= (char) alt_sum
;
2938 lpage_send(kbd_codepage
, &ch
, 1);
2940 static char cbuf
[] = "\033 ";
2942 lpage_send(kbd_codepage
, cbuf
+ !left_alt
,
2947 /* This is so the ALT-Numpad and dead keys work correctly. */
2952 /* If we're definitly not building up an ALT-54321 then clear it */
2955 /* If we will be using alt_sum fix the 256s */
2956 else if (keys
[0] && (utf
|| dbcs_screenfont
))
2960 /* ALT alone may or may not want to bring up the System menu */
2961 if (wParam
== VK_MENU
) {
2963 if (message
== WM_SYSKEYDOWN
)
2965 else if (message
== WM_SYSKEYUP
&& alt_state
)
2966 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
2967 if (message
== WM_SYSKEYUP
)
2977 void set_title(char *title
)
2980 window_name
= smalloc(1 + strlen(title
));
2981 strcpy(window_name
, title
);
2982 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
2983 SetWindowText(hwnd
, title
);
2986 void set_icon(char *title
)
2989 icon_name
= smalloc(1 + strlen(title
));
2990 strcpy(icon_name
, title
);
2991 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
2992 SetWindowText(hwnd
, title
);
2995 void set_sbar(int total
, int start
, int page
)
3002 si
.cbSize
= sizeof(si
);
3003 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3005 si
.nMax
= total
- 1;
3009 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3012 Context
get_ctx(void)
3018 SelectPalette(hdc
, pal
, FALSE
);
3024 void free_ctx(Context ctx
)
3026 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3027 ReleaseDC(hwnd
, ctx
);
3030 static void real_palette_set(int n
, int r
, int g
, int b
)
3033 logpal
->palPalEntry
[n
].peRed
= r
;
3034 logpal
->palPalEntry
[n
].peGreen
= g
;
3035 logpal
->palPalEntry
[n
].peBlue
= b
;
3036 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3037 colours
[n
] = PALETTERGB(r
, g
, b
);
3038 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3040 colours
[n
] = RGB(r
, g
, b
);
3043 void palette_set(int n
, int r
, int g
, int b
)
3045 static const int first
[21] = {
3046 0, 2, 4, 6, 8, 10, 12, 14,
3047 1, 3, 5, 7, 9, 11, 13, 15,
3050 real_palette_set(first
[n
], r
, g
, b
);
3052 real_palette_set(first
[n
] + 1, r
, g
, b
);
3054 HDC hdc
= get_ctx();
3055 UnrealizeObject(pal
);
3056 RealizePalette(hdc
);
3061 void palette_reset(void)
3065 for (i
= 0; i
< NCOLOURS
; i
++) {
3067 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3068 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3069 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3070 logpal
->palPalEntry
[i
].peFlags
= 0;
3071 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3072 defpal
[i
].rgbtGreen
,
3073 defpal
[i
].rgbtBlue
);
3075 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3076 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3081 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3083 RealizePalette(hdc
);
3088 void write_aclip(char *data
, int len
, int must_deselect
)
3093 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3096 lock
= GlobalLock(clipdata
);
3099 memcpy(lock
, data
, len
);
3100 ((unsigned char *) lock
)[len
] = 0;
3101 GlobalUnlock(clipdata
);
3104 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3106 if (OpenClipboard(hwnd
)) {
3108 SetClipboardData(CF_TEXT
, clipdata
);
3111 GlobalFree(clipdata
);
3114 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3118 * Note: unlike write_aclip() this will not append a nul.
3120 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3127 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3129 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3130 len
* sizeof(wchar_t));
3131 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3133 if (!clipdata
|| !clipdata2
) {
3135 GlobalFree(clipdata
);
3137 GlobalFree(clipdata2
);
3140 if (!(lock
= GlobalLock(clipdata
)))
3142 if (!(lock2
= GlobalLock(clipdata2
)))
3145 memcpy(lock
, data
, len
* sizeof(wchar_t));
3146 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3148 GlobalUnlock(clipdata
);
3149 GlobalUnlock(clipdata2
);
3152 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3154 if (OpenClipboard(hwnd
)) {
3156 SetClipboardData(CF_UNICODETEXT
, clipdata
);
3157 SetClipboardData(CF_TEXT
, clipdata2
);
3160 GlobalFree(clipdata
);
3161 GlobalFree(clipdata2
);
3165 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3168 void get_clip(wchar_t ** p
, int *len
)
3170 static HGLOBAL clipdata
= NULL
;
3171 static wchar_t *converted
= 0;
3180 GlobalUnlock(clipdata
);
3183 } else if (OpenClipboard(NULL
)) {
3184 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
3186 *p
= GlobalLock(clipdata
);
3188 for (p2
= *p
; *p2
; p2
++);
3192 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
3196 s
= GlobalLock(clipdata
);
3197 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
3198 *p
= converted
= smalloc(i
* sizeof(wchar_t));
3199 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
3212 * Move `lines' lines from position `from' to position `to' in the
3215 void optimised_move(int to
, int from
, int lines
)
3220 min
= (to
< from ? to
: from
);
3221 max
= to
+ from
- min
;
3224 r
.right
= cols
* font_width
;
3225 r
.top
= min
* font_height
;
3226 r
.bottom
= (max
+ lines
) * font_height
;
3227 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
3232 * Print a message box and perform a fatal exit.
3234 void fatalbox(char *fmt
, ...)
3240 vsprintf(stuff
, fmt
, ap
);
3242 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
3247 * Manage window caption / taskbar flashing, if enabled.
3248 * 0 = stop, 1 = maintain, 2 = start
3250 static void flash_window(int mode
)
3252 static long last_flash
= 0;
3253 static int flashing
= 0;
3254 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
3257 FlashWindow(hwnd
, FALSE
);
3261 } else if (mode
== 2) {
3264 last_flash
= GetTickCount();
3266 FlashWindow(hwnd
, TRUE
);
3269 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
3272 long now
= GetTickCount();
3273 long fdiff
= now
- last_flash
;
3274 if (fdiff
< 0 || fdiff
> 450) {
3276 FlashWindow(hwnd
, TRUE
); /* toggle */
3287 if (mode
== BELL_DEFAULT
) {
3289 * For MessageBeep style bells, we want to be careful of
3290 * timing, because they don't have the nice property of
3291 * PlaySound bells that each one cancels the previous
3292 * active one. So we limit the rate to one per 50ms or so.
3294 static long lastbeep
= 0;
3297 beepdiff
= GetTickCount() - lastbeep
;
3298 if (beepdiff
>= 0 && beepdiff
< 50)
3302 * The above MessageBeep call takes time, so we record the
3303 * time _after_ it finishes rather than before it starts.
3305 lastbeep
= GetTickCount();
3306 } else if (mode
== BELL_WAVEFILE
) {
3307 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
3308 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
3309 sprintf(buf
, "Unable to play sound file\n%s\n"
3310 "Using default sound instead", cfg
.bell_wavefile
);
3311 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
3312 MB_OK
| MB_ICONEXCLAMATION
);
3313 cfg
.beep
= BELL_DEFAULT
;
3316 /* Otherwise, either visual bell or disabled; do nothing here */
3318 flash_window(2); /* start */