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);
81 static time_t last_movement
= 0;
85 #define FONT_UNDERLINE 2
86 #define FONT_BOLDUND 3
87 #define FONT_WIDE 0x04
88 #define FONT_HIGH 0x08
89 #define FONT_NARROW 0x10
91 #define FONT_OEMBOLD 0x21
92 #define FONT_OEMUND 0x22
93 #define FONT_OEMBOLDUND 0x23
94 #define FONT_MSGOTHIC 0x40
95 #define FONT_MINGLIU 0x60
96 #define FONT_GULIMCHE 0x80
97 #define FONT_MAXNO 0x8F
99 static HFONT fonts
[FONT_MAXNO
];
100 static int fontflag
[FONT_MAXNO
];
102 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
110 static COLORREF colours
[NCOLOURS
];
112 static LPLOGPALETTE logpal
;
113 static RGBTRIPLE defpal
[NCOLOURS
];
117 static HBITMAP caretbm
;
119 static int dbltime
, lasttime
, lastact
;
120 static Mouse_Button lastbtn
;
122 /* this allows xterm-style mouse handling. */
123 static int send_raw_mouse
= 0;
124 static int wheel_accumulator
= 0;
126 static char *window_name
, *icon_name
;
128 static int compose_state
= 0;
130 /* Dummy routine, only required in plink. */
131 void ldisc_update(int echo
, int edit
)
135 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
137 static char appname
[] = "PuTTY";
142 int guess_width
, guess_height
;
145 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
147 winsock_ver
= MAKEWORD(1, 1);
148 if (WSAStartup(winsock_ver
, &wsadata
)) {
149 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
150 MB_OK
| MB_ICONEXCLAMATION
);
153 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
154 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
155 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
159 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
162 InitCommonControls();
164 /* Ensure a Maximize setting in Explorer doesn't maximise the
169 * Process the command line.
174 default_protocol
= DEFAULT_PROTOCOL
;
175 default_port
= DEFAULT_PORT
;
176 cfg
.logtype
= LGTYP_NONE
;
178 do_defaults(NULL
, &cfg
);
181 while (*p
&& isspace(*p
))
185 * Process command line options first. Yes, this can be
186 * done better, and it will be as soon as I have the
190 char *q
= p
+ strcspn(p
, " \t");
193 tolower(p
[0]) == 's' &&
194 tolower(p
[1]) == 's' && tolower(p
[2]) == 'h') {
195 default_protocol
= cfg
.protocol
= PROT_SSH
;
196 default_port
= cfg
.port
= 22;
197 } else if (q
== p
+ 7 &&
198 tolower(p
[0]) == 'c' &&
199 tolower(p
[1]) == 'l' &&
200 tolower(p
[2]) == 'e' &&
201 tolower(p
[3]) == 'a' &&
202 tolower(p
[4]) == 'n' &&
203 tolower(p
[5]) == 'u' && tolower(p
[6]) == 'p') {
205 * `putty -cleanup'. Remove all registry entries
206 * associated with PuTTY, and also find and delete
207 * the random seed file.
210 "This procedure will remove ALL Registry\n"
211 "entries associated with PuTTY, and will\n"
212 "also remove the PuTTY random seed file.\n"
214 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
215 "SESSIONS. Are you really sure you want\n"
218 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
223 p
= q
+ strspn(q
, " \t");
227 * An initial @ means to activate a saved session.
231 while (i
> 1 && isspace(p
[i
- 1]))
234 do_defaults(p
+ 1, &cfg
);
235 if (!*cfg
.host
&& !do_config()) {
239 } else if (*p
== '&') {
241 * An initial & means we've been given a command line
242 * containing the hex value of a HANDLE for a file
243 * mapping object, which we must then extract as a
248 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
249 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
250 0, 0, sizeof(Config
))) != NULL
) {
253 CloseHandle(filemap
);
254 } else if (!do_config()) {
261 * If the hostname starts with "telnet:", set the
262 * protocol to Telnet and process the string as a
265 if (!strncmp(q
, "telnet:", 7)) {
269 if (q
[0] == '/' && q
[1] == '/')
271 cfg
.protocol
= PROT_TELNET
;
273 while (*p
&& *p
!= ':' && *p
!= '/')
282 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
283 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
285 while (*p
&& !isspace(*p
))
289 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
290 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
291 while (*p
&& isspace(*p
))
306 * Trim leading whitespace off the hostname if it's there.
309 int space
= strspn(cfg
.host
, " \t");
310 memmove(cfg
.host
, cfg
.host
+space
, 1+strlen(cfg
.host
)-space
);
313 /* See if host is of the form user@host */
314 if (cfg
.host
[0] != '\0') {
315 char *atsign
= strchr(cfg
.host
, '@');
316 /* Make sure we're not overflowing the user field */
318 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
319 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
320 cfg
.username
[atsign
- cfg
.host
] = '\0';
322 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
327 * Trim a colon suffix off the hostname if it's there.
329 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
333 * Select protocol. This is farmed out into a table in a
334 * separate file to enable an ssh-free variant.
339 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
340 if (backends
[i
].protocol
== cfg
.protocol
) {
341 back
= backends
[i
].backend
;
345 MessageBox(NULL
, "Unsupported protocol number found",
346 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
352 /* Check for invalid Port number (i.e. zero) */
354 MessageBox(NULL
, "Invalid Port Number",
355 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
362 wndclass
.lpfnWndProc
= WndProc
;
363 wndclass
.cbClsExtra
= 0;
364 wndclass
.cbWndExtra
= 0;
365 wndclass
.hInstance
= inst
;
366 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
367 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
368 wndclass
.hbrBackground
= GetStockObject(BLACK_BRUSH
);
369 wndclass
.lpszMenuName
= NULL
;
370 wndclass
.lpszClassName
= appname
;
372 RegisterClass(&wndclass
);
377 savelines
= cfg
.savelines
;
383 * Guess some defaults for the window size. This all gets
384 * updated later, so we don't really care too much. However, we
385 * do want the font width/height guesses to correspond to a
386 * large font rather than a small one...
393 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
394 guess_width
= extra_width
+ font_width
* cols
;
395 guess_height
= extra_height
+ font_height
* rows
;
398 HWND w
= GetDesktopWindow();
399 GetWindowRect(w
, &r
);
400 if (guess_width
> r
.right
- r
.left
)
401 guess_width
= r
.right
- r
.left
;
402 if (guess_height
> r
.bottom
- r
.top
)
403 guess_height
= r
.bottom
- r
.top
;
407 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
410 winmode
&= ~(WS_VSCROLL
);
412 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
414 exwinmode
|= WS_EX_TOPMOST
;
416 exwinmode
|= WS_EX_CLIENTEDGE
;
417 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
418 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
419 guess_width
, guess_height
,
420 NULL
, NULL
, inst
, NULL
);
424 * Initialise the fonts, simultaneously correcting the guesses
425 * for font_{width,height}.
427 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
432 * Correct the guesses for extra_{width,height}.
436 GetWindowRect(hwnd
, &wr
);
437 GetClientRect(hwnd
, &cr
);
438 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
439 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
443 * Resize the window, now we know what size we _really_ want it
446 guess_width
= extra_width
+ font_width
* cols
;
447 guess_height
= extra_height
+ font_height
* rows
;
448 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
449 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
450 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
453 * Set up a caret bitmap, with no content.
457 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
458 bits
= smalloc(size
);
459 memset(bits
, 0, size
);
460 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
463 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
466 * Initialise the scroll bar.
471 si
.cbSize
= sizeof(si
);
472 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
477 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
481 * Start up the telnet connection.
485 char msg
[1024], *title
;
488 error
= back
->init(cfg
.host
, cfg
.port
, &realhost
);
490 sprintf(msg
, "Unable to open connection to\n"
491 "%.800s\n" "%s", cfg
.host
, error
);
492 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
495 window_name
= icon_name
= NULL
;
497 title
= cfg
.wintitle
;
499 sprintf(msg
, "%s - PuTTY", realhost
);
507 session_closed
= FALSE
;
510 * Set up the input and output buffers.
513 outbuf_reap
= outbuf_head
= 0;
516 * Prepare the mouse handler.
518 lastact
= MA_NOTHING
;
519 lastbtn
= MBT_NOTHING
;
520 dbltime
= GetDoubleClickTime();
523 * Set up the session-control options on the system menu.
526 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
530 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
531 if (cfg
.protocol
== PROT_TELNET
) {
533 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
534 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
535 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
536 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
537 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
538 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
539 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
540 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
541 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
542 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
543 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
544 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
545 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
546 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
547 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
548 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
549 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
551 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
553 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
554 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
555 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
556 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
559 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
560 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
562 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
563 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
564 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
565 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
566 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
567 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
568 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
569 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
573 * Finally show the window!
575 ShowWindow(hwnd
, show
);
578 * Open the initial log file if there is one.
583 * Set the palette up.
589 has_focus
= (GetForegroundWindow() == hwnd
);
592 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
593 int timer_id
= 0, long_timer
= 0;
595 while (msg
.message
!= WM_QUIT
) {
596 /* Sometimes DispatchMessage calls routines that use their own
597 * GetMessage loop, setup this timer so we get some control back.
599 * Also call term_update() from the timer so that if the host
600 * is sending data flat out we still do redraws.
602 if (timer_id
&& long_timer
) {
603 KillTimer(hwnd
, timer_id
);
604 long_timer
= timer_id
= 0;
607 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
608 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
609 DispatchMessage(&msg
);
611 /* Make sure we blink everything that needs it. */
614 /* Send the paste buffer if there's anything to send */
617 /* If there's nothing new in the queue then we can do everything
618 * we've delayed, reading the socket, writing, and repainting
621 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
624 if (pending_netevent
) {
625 enact_pending_netevent();
627 /* Force the cursor blink on */
630 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
634 /* Okay there is now nothing to do so we make sure the screen is
635 * completely up to date then tell windows to call us in a little
639 KillTimer(hwnd
, timer_id
);
648 /* Hmm, term_update didn't want to do an update too soon ... */
649 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
651 timer_id
= SetTimer(hwnd
, 1, 2000, NULL
);
653 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
656 /* There's no point rescanning everything in the message queue
657 * so we do an apparently unnecessary wait here
660 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
674 if (cfg
.protocol
== PROT_SSH
) {
685 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
687 char *do_select(SOCKET skt
, int startup
)
692 events
= FD_READ
| FD_WRITE
| FD_OOB
| FD_CLOSE
;
697 return "do_select(): internal error (hwnd==NULL)";
698 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
699 switch (WSAGetLastError()) {
701 return "Network is down";
703 return "WSAAsyncSelect(): unknown error";
710 * set or clear the "raw mouse message" mode
712 void set_raw_mouse_mode(int activate
)
714 send_raw_mouse
= activate
;
715 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
719 * Print a message box and close the connection.
721 void connection_fatal(char *fmt
, ...)
727 vsprintf(stuff
, fmt
, ap
);
729 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
730 if (cfg
.close_on_exit
== COE_ALWAYS
)
733 session_closed
= TRUE
;
734 SetWindowText(hwnd
, "PuTTY (inactive)");
739 * Actually do the job requested by a WM_NETEVENT
741 static void enact_pending_netevent(void)
743 static int reentering
= 0;
744 extern int select_result(WPARAM
, LPARAM
);
748 return; /* don't unpend the pending */
750 pending_netevent
= FALSE
;
753 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
756 if (ret
== 0 && !session_closed
) {
757 /* Abnormal exits will already have set session_closed and taken
758 * appropriate action. */
759 if (cfg
.close_on_exit
== COE_ALWAYS
||
760 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
762 session_closed
= TRUE
;
763 SetWindowText(hwnd
, "PuTTY (inactive)");
764 MessageBox(hwnd
, "Connection closed by remote host",
765 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
771 * Copy the colour palette from the configuration data into defpal.
772 * This is non-trivial because the colour indices are different.
774 static void cfgtopalette(void)
777 static const int ww
[] = {
778 6, 7, 8, 9, 10, 11, 12, 13,
779 14, 15, 16, 17, 18, 19, 20, 21,
780 0, 1, 2, 3, 4, 4, 5, 5
783 for (i
= 0; i
< 24; i
++) {
785 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
786 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
787 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
792 * Set up the colour palette.
794 static void init_palette(void)
797 HDC hdc
= GetDC(hwnd
);
799 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
800 logpal
= smalloc(sizeof(*logpal
)
801 - sizeof(logpal
->palPalEntry
)
802 + NCOLOURS
* sizeof(PALETTEENTRY
));
803 logpal
->palVersion
= 0x300;
804 logpal
->palNumEntries
= NCOLOURS
;
805 for (i
= 0; i
< NCOLOURS
; i
++) {
806 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
807 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
808 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
809 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
811 pal
= CreatePalette(logpal
);
813 SelectPalette(hdc
, pal
, FALSE
);
815 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
818 ReleaseDC(hwnd
, hdc
);
821 for (i
= 0; i
< NCOLOURS
; i
++)
822 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
826 for (i
= 0; i
< NCOLOURS
; i
++)
827 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
828 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
832 * Initialise all the fonts we will need initially. There may be as many as
833 * three or as few as one. The other (poentially) twentyone fonts are done
834 * if/when they are needed.
838 * - check the font width and height, correcting our guesses if
841 * - verify that the bold font is the same width as the ordinary
842 * one, and engage shadow bolding if not.
844 * - verify that the underlined font is the same width as the
845 * ordinary one (manual underlining by means of line drawing can
846 * be done in a pinch).
848 static void init_fonts(int pick_width
)
855 int fw_dontcare
, fw_bold
;
857 for (i
= 0; i
< FONT_MAXNO
; i
++)
860 if (cfg
.fontisbold
) {
861 fw_dontcare
= FW_BOLD
;
864 fw_dontcare
= FW_DONTCARE
;
870 font_height
= cfg
.fontheight
;
871 if (font_height
> 0) {
873 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
875 font_width
= pick_width
;
878 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
879 c, OUT_DEFAULT_PRECIS, \
880 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
881 FIXED_PITCH | FF_DONTCARE, cfg.font)
883 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
885 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
886 GetTextMetrics(hdc
, &tm
);
887 font_height
= tm
.tmHeight
;
888 font_width
= tm
.tmAveCharWidth
;
892 DWORD cset
= tm
.tmCharSet
;
893 memset(&info
, 0xFF, sizeof(info
));
895 /* !!! Yes the next line is right */
896 if (cset
== OEM_CHARSET
)
897 font_codepage
= GetOEMCP();
899 if (TranslateCharsetInfo
900 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
905 GetCPInfo(font_codepage
, &cpinfo
);
906 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
909 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
912 * Some fonts, e.g. 9-pt Courier, draw their underlines
913 * outside their character cell. We successfully prevent
914 * screen corruption by clipping the text output, but then
915 * we lose the underline completely. Here we try to work
916 * out whether this is such a font, and if it is, we set a
917 * flag that causes underlines to be drawn by hand.
919 * Having tried other more sophisticated approaches (such
920 * as examining the TEXTMETRIC structure or requesting the
921 * height of a string), I think we'll do this the brute
922 * force way: we create a small bitmap, draw an underlined
923 * space on it, and test to see whether any pixels are
924 * foreground-coloured. (Since we expect the underline to
925 * go all the way across the character cell, we only search
926 * down a single column of the bitmap, half way across.)
930 HBITMAP und_bm
, und_oldbm
;
934 und_dc
= CreateCompatibleDC(hdc
);
935 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
936 und_oldbm
= SelectObject(und_dc
, und_bm
);
937 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
938 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
939 SetTextColor(und_dc
, RGB(255, 255, 255));
940 SetBkColor(und_dc
, RGB(0, 0, 0));
941 SetBkMode(und_dc
, OPAQUE
);
942 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
944 for (i
= 0; i
< font_height
; i
++) {
945 c
= GetPixel(und_dc
, font_width
/ 2, i
);
946 if (c
!= RGB(0, 0, 0))
949 SelectObject(und_dc
, und_oldbm
);
950 DeleteObject(und_bm
);
954 DeleteObject(fonts
[FONT_UNDERLINE
]);
955 fonts
[FONT_UNDERLINE
] = 0;
959 if (bold_mode
== BOLD_FONT
) {
960 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
964 descent
= tm
.tmAscent
+ 1;
965 if (descent
>= font_height
)
966 descent
= font_height
- 1;
968 for (i
= 0; i
< 3; i
++) {
970 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
971 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
978 ReleaseDC(hwnd
, hdc
);
980 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
982 DeleteObject(fonts
[FONT_UNDERLINE
]);
983 fonts
[FONT_UNDERLINE
] = 0;
986 if (bold_mode
== BOLD_FONT
&&
987 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
988 bold_mode
= BOLD_SHADOW
;
989 DeleteObject(fonts
[FONT_BOLD
]);
990 fonts
[FONT_BOLD
] = 0;
992 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
997 static void another_font(int fontno
)
1000 int fw_dontcare
, fw_bold
;
1004 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1007 basefont
= (fontno
& ~(FONT_BOLDUND
));
1008 if (basefont
!= fontno
&& !fontflag
[basefont
])
1009 another_font(basefont
);
1011 if (cfg
.fontisbold
) {
1012 fw_dontcare
= FW_BOLD
;
1015 fw_dontcare
= FW_DONTCARE
;
1019 c
= cfg
.fontcharset
;
1025 if (fontno
& FONT_WIDE
)
1027 if (fontno
& FONT_NARROW
)
1029 if (fontno
& FONT_OEM
)
1031 if (fontno
& FONT_BOLD
)
1033 if (fontno
& FONT_UNDERLINE
)
1037 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1038 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1039 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1040 FIXED_PITCH
| FF_DONTCARE
, s
);
1042 fontflag
[fontno
] = 1;
1045 static void deinit_fonts(void)
1048 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1050 DeleteObject(fonts
[i
]);
1056 void request_resize(int w
, int h
, int refont
)
1060 /* If the window is maximized supress resizing attempts */
1064 if (refont
&& w
!= cols
&& (cols
== 80 || cols
== 132)) {
1065 /* If font width too big for screen should we shrink the font more ? */
1067 font_width
= ((font_width
* cols
+ w
/ 2) / w
);
1071 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1072 und_mode
= UND_FONT
;
1073 init_fonts(font_width
);
1075 static int first_time
= 1;
1078 switch (first_time
) {
1080 /* Get the size of the screen */
1081 if (GetClientRect(GetDesktopWindow(), &ss
))
1082 /* first_time = 0 */ ;
1088 /* Make sure the values are sane */
1089 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1090 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1103 width
= extra_width
+ font_width
* w
;
1104 height
= extra_height
+ font_height
* h
;
1106 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1107 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1108 SWP_NOMOVE
| SWP_NOZORDER
);
1111 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
)
1113 int thistime
= GetMessageTime();
1115 if (send_raw_mouse
) {
1116 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
);
1120 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1121 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1122 lastact
== MA_2CLK ? MA_3CLK
:
1123 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1128 if (lastact
!= MA_NOTHING
)
1129 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
);
1130 lasttime
= thistime
;
1134 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1135 * into a cooked one (SELECT, EXTEND, PASTE).
1137 Mouse_Button
translate_button(Mouse_Button button
)
1139 if (button
== MBT_LEFT
)
1141 if (button
== MBT_MIDDLE
)
1142 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1143 if (button
== MBT_RIGHT
)
1144 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1147 static void show_mouseptr(int show
)
1149 static int cursor_visible
= 1;
1150 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1152 if (cursor_visible
&& !show
)
1154 else if (!cursor_visible
&& show
)
1156 cursor_visible
= show
;
1159 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1160 WPARAM wParam
, LPARAM lParam
)
1163 static int ignore_size
= FALSE
;
1164 static int ignore_clip
= FALSE
;
1165 static int just_reconfigged
= FALSE
;
1166 static int resizing
= FALSE
;
1167 static int need_backend_resize
= FALSE
;
1168 static int defered_resize
= FALSE
;
1172 if (pending_netevent
)
1173 enact_pending_netevent();
1180 if (cfg
.ping_interval
> 0) {
1183 if (now
- last_movement
> cfg
.ping_interval
) {
1184 back
->special(TS_PING
);
1185 last_movement
= now
;
1193 if (!cfg
.warn_on_close
|| session_closed
||
1195 "Are you sure you want to close this session?",
1196 "PuTTY Exit Confirmation",
1197 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1198 DestroyWindow(hwnd
);
1205 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1217 PROCESS_INFORMATION pi
;
1218 HANDLE filemap
= NULL
;
1220 if (wParam
== IDM_DUPSESS
) {
1222 * Allocate a file-mapping memory chunk for the
1225 SECURITY_ATTRIBUTES sa
;
1228 sa
.nLength
= sizeof(sa
);
1229 sa
.lpSecurityDescriptor
= NULL
;
1230 sa
.bInheritHandle
= TRUE
;
1231 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1234 0, sizeof(Config
), NULL
);
1236 p
= (Config
*) MapViewOfFile(filemap
,
1238 0, 0, sizeof(Config
));
1240 *p
= cfg
; /* structure copy */
1244 sprintf(c
, "putty &%p", filemap
);
1246 } else if (wParam
== IDM_SAVEDSESS
) {
1248 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1249 cl
= smalloc(16 + strlen(session
)); /* 8, but play safe */
1251 cl
= NULL
; /* not a very important failure mode */
1253 sprintf(cl
, "putty @%s", session
);
1259 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1261 si
.lpReserved
= NULL
;
1262 si
.lpDesktop
= NULL
;
1266 si
.lpReserved2
= NULL
;
1267 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1268 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1271 CloseHandle(filemap
);
1278 int prev_alwaysontop
= cfg
.alwaysontop
;
1279 int prev_sunken_edge
= cfg
.sunken_edge
;
1280 char oldlogfile
[FILENAME_MAX
];
1282 int need_setwpos
= FALSE
;
1283 int old_fwidth
, old_fheight
;
1285 strcpy(oldlogfile
, cfg
.logfilename
);
1286 oldlogtype
= cfg
.logtype
;
1287 old_fwidth
= font_width
;
1288 old_fheight
= font_height
;
1289 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1291 if (!do_reconfig(hwnd
))
1294 if (strcmp(oldlogfile
, cfg
.logfilename
) ||
1295 oldlogtype
!= cfg
.logtype
) {
1296 logfclose(); /* reset logging */
1300 just_reconfigged
= TRUE
;
1302 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1303 und_mode
= UND_FONT
;
1307 * Flush the line discipline's edit buffer in the
1308 * case where local editing has just been disabled.
1310 ldisc_send(NULL
, 0);
1318 /* Enable or disable the scroll bar, etc */
1320 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1321 LONG nexflag
, exflag
=
1322 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1325 if (cfg
.alwaysontop
!= prev_alwaysontop
) {
1326 if (cfg
.alwaysontop
) {
1327 nexflag
|= WS_EX_TOPMOST
;
1328 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1329 SWP_NOMOVE
| SWP_NOSIZE
);
1331 nexflag
&= ~(WS_EX_TOPMOST
);
1332 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1333 SWP_NOMOVE
| SWP_NOSIZE
);
1336 if (cfg
.sunken_edge
)
1337 nexflag
|= WS_EX_CLIENTEDGE
;
1339 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1345 nflg
&= ~WS_VSCROLL
;
1347 nflg
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1349 nflg
|= (WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1351 if (nflg
!= flag
|| nexflag
!= exflag
) {
1355 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1356 if (nexflag
!= exflag
)
1357 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1359 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
1361 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1362 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1363 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
1364 | SWP_FRAMECHANGED
);
1366 GetWindowRect(hwnd
, &wr
);
1367 GetClientRect(hwnd
, &cr
);
1369 wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1371 wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1372 need_setwpos
= TRUE
;
1376 if (cfg
.height
!= rows
||
1377 cfg
.width
!= cols
||
1378 old_fwidth
!= font_width
||
1379 old_fheight
!= font_height
||
1380 cfg
.savelines
!= savelines
||
1381 cfg
.sunken_edge
!= prev_sunken_edge
)
1382 need_setwpos
= TRUE
;
1384 if (IsZoomed(hwnd
)) {
1388 defered_resize
= TRUE
;
1390 GetClientRect(hwnd
, &cr
);
1391 w
= cr
.right
- cr
.left
;
1392 h
= cr
.bottom
- cr
.top
;
1396 h
= h
/ font_height
;
1400 term_size(h
, w
, cfg
.savelines
);
1401 InvalidateRect(hwnd
, NULL
, TRUE
);
1404 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1405 InvalidateRect(hwnd
, NULL
, TRUE
);
1407 SetWindowPos(hwnd
, NULL
, 0, 0,
1408 extra_width
+ font_width
* cfg
.width
,
1410 font_height
* cfg
.height
,
1411 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1412 SWP_NOMOVE
| SWP_NOZORDER
);
1416 if (cfg
.locksize
&& IsZoomed(hwnd
))
1418 set_title(cfg
.wintitle
);
1419 if (IsIconic(hwnd
)) {
1421 cfg
.win_name_always ? window_name
:
1436 back
->special(TS_AYT
);
1439 back
->special(TS_BRK
);
1442 back
->special(TS_SYNCH
);
1445 back
->special(TS_EC
);
1448 back
->special(TS_EL
);
1451 back
->special(TS_GA
);
1454 back
->special(TS_NOP
);
1457 back
->special(TS_ABORT
);
1460 back
->special(TS_AO
);
1463 back
->special(TS_IP
);
1466 back
->special(TS_SUSP
);
1469 back
->special(TS_EOR
);
1472 back
->special(TS_EOF
);
1478 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1479 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1484 #define X_POS(l) ((int)(short)LOWORD(l))
1485 #define Y_POS(l) ((int)(short)HIWORD(l))
1487 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1488 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1489 #define WHEEL_DELTA 120
1492 wheel_accumulator
+= (short) HIWORD(wParam
);
1493 wParam
= LOWORD(wParam
);
1495 /* process events when the threshold is reached */
1496 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
1499 /* reduce amount for next time */
1500 if (wheel_accumulator
> 0) {
1502 wheel_accumulator
-= WHEEL_DELTA
;
1503 } else if (wheel_accumulator
< 0) {
1505 wheel_accumulator
+= WHEEL_DELTA
;
1509 if (send_raw_mouse
) {
1510 /* send a mouse-down followed by a mouse up */
1513 TO_CHR_X(X_POS(lParam
)),
1514 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1515 wParam
& MK_CONTROL
);
1516 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1517 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1518 wParam
& MK_CONTROL
);
1520 /* trigger a scroll */
1522 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
1527 case WM_LBUTTONDOWN
:
1528 case WM_MBUTTONDOWN
:
1529 case WM_RBUTTONDOWN
:
1536 case WM_LBUTTONDOWN
:
1540 case WM_MBUTTONDOWN
:
1541 button
= MBT_MIDDLE
;
1544 case WM_RBUTTONDOWN
:
1553 button
= MBT_MIDDLE
;
1564 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1565 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
);
1568 term_mouse(button
, MA_RELEASE
,
1569 TO_CHR_X(X_POS(lParam
)),
1570 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1571 wParam
& MK_CONTROL
);
1579 * Add the mouse position and message time to the random
1582 noise_ultralight(lParam
);
1584 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1586 if (wParam
& MK_LBUTTON
)
1588 else if (wParam
& MK_MBUTTON
)
1589 b
= cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1591 b
= cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1592 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1593 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1594 wParam
& MK_CONTROL
);
1597 case WM_NCMOUSEMOVE
:
1599 noise_ultralight(lParam
);
1601 case WM_IGNORE_CLIP
:
1602 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1604 case WM_DESTROYCLIPBOARD
:
1607 ignore_clip
= FALSE
;
1613 hdc
= BeginPaint(hwnd
, &p
);
1615 SelectPalette(hdc
, pal
, TRUE
);
1616 RealizePalette(hdc
);
1618 term_paint(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1619 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1620 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
1621 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
1627 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1628 * but the only one that's likely to try to overload us is FD_READ.
1629 * This means buffering just one is fine.
1631 if (pending_netevent
)
1632 enact_pending_netevent();
1634 pending_netevent
= TRUE
;
1635 pend_netevent_wParam
= wParam
;
1636 pend_netevent_lParam
= lParam
;
1637 time(&last_movement
);
1641 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
1654 case WM_IGNORE_SIZE
:
1655 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1657 case WM_ENTERSIZEMOVE
:
1660 need_backend_resize
= FALSE
;
1662 case WM_EXITSIZEMOVE
:
1665 if (need_backend_resize
)
1670 int width
, height
, w
, h
, ew
, eh
;
1671 LPRECT r
= (LPRECT
) lParam
;
1673 width
= r
->right
- r
->left
- extra_width
;
1674 height
= r
->bottom
- r
->top
- extra_height
;
1675 w
= (width
+ font_width
/ 2) / font_width
;
1678 h
= (height
+ font_height
/ 2) / font_height
;
1681 UpdateSizeTip(hwnd
, w
, h
);
1682 ew
= width
- w
* font_width
;
1683 eh
= height
- h
* font_height
;
1685 if (wParam
== WMSZ_LEFT
||
1686 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
1692 if (wParam
== WMSZ_TOP
||
1693 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
1703 /* break; (never reached) */
1705 if (wParam
== SIZE_MINIMIZED
) {
1707 cfg
.win_name_always ? window_name
: icon_name
);
1710 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1711 SetWindowText(hwnd
, window_name
);
1713 int width
, height
, w
, h
;
1714 #if 0 /* we have fixed this using WM_SIZING now */
1718 width
= LOWORD(lParam
);
1719 height
= HIWORD(lParam
);
1720 w
= width
/ font_width
;
1723 h
= height
/ font_height
;
1726 #if 0 /* we have fixed this using WM_SIZING now */
1727 ew
= width
- w
* font_width
;
1728 eh
= height
- h
* font_height
;
1729 if (ew
!= 0 || eh
!= 0) {
1731 GetWindowRect(hwnd
, &r
);
1732 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
1733 SetWindowPos(hwnd
, NULL
, 0, 0,
1734 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1735 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1738 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1740 term_size(h
, w
, cfg
.savelines
);
1742 * Don't call back->size in mid-resize. (To prevent
1743 * massive numbers of resize events getting sent
1744 * down the connection during an NT opaque drag.)
1749 need_backend_resize
= TRUE
;
1753 just_reconfigged
= FALSE
;
1756 if (wParam
== SIZE_RESTORED
&& defered_resize
) {
1757 defered_resize
= FALSE
;
1758 SetWindowPos(hwnd
, NULL
, 0, 0,
1759 extra_width
+ font_width
* cfg
.width
,
1760 extra_height
+ font_height
* cfg
.height
,
1761 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1762 SWP_NOMOVE
| SWP_NOZORDER
);
1764 ignore_size
= FALSE
;
1767 switch (LOWORD(wParam
)) {
1781 term_scroll(0, +rows
/ 2);
1784 term_scroll(0, -rows
/ 2);
1786 case SB_THUMBPOSITION
:
1788 term_scroll(1, HIWORD(wParam
));
1792 case WM_PALETTECHANGED
:
1793 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1794 HDC hdc
= get_ctx();
1796 if (RealizePalette(hdc
) > 0)
1802 case WM_QUERYNEWPALETTE
:
1804 HDC hdc
= get_ctx();
1806 if (RealizePalette(hdc
) > 0)
1818 * Add the scan code and keypress timing to the random
1821 noise_ultralight(lParam
);
1824 * We don't do TranslateMessage since it disassociates the
1825 * resulting CHAR message from the KEYDOWN that sparked it,
1826 * which we occasionally don't want. Instead, we process
1827 * KEYDOWN, and call the Win32 translator functions so that
1828 * we get the translations under _our_ control.
1831 unsigned char buf
[20];
1834 if (wParam
== VK_PROCESSKEY
) {
1837 m
.message
= WM_KEYDOWN
;
1839 m
.lParam
= lParam
& 0xdfff;
1840 TranslateMessage(&m
);
1842 len
= TranslateKey(message
, wParam
, lParam
, buf
);
1844 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1845 ldisc_send(buf
, len
);
1852 case WM_INPUTLANGCHANGE
:
1854 /* wParam == Font number */
1855 /* lParam == Locale */
1857 HKL NewInputLocale
= (HKL
) lParam
;
1859 // lParam == GetKeyboardLayout(0);
1861 GetLocaleInfo(LOWORD(NewInputLocale
),
1862 LOCALE_IDEFAULTANSICODEPAGE
, lbuf
, sizeof(lbuf
));
1864 kbd_codepage
= atoi(lbuf
);
1868 if (wParam
& 0xFF00) {
1869 unsigned char buf
[2];
1872 buf
[0] = wParam
>> 8;
1873 lpage_send(kbd_codepage
, buf
, 2);
1875 char c
= (unsigned char) wParam
;
1876 lpage_send(kbd_codepage
, &c
, 1);
1882 * Nevertheless, we are prepared to deal with WM_CHAR
1883 * messages, should they crop up. So if someone wants to
1884 * post the things to us as part of a macro manoeuvre,
1885 * we're ready to cope.
1888 char c
= (unsigned char)wParam
;
1889 lpage_send(CP_ACP
, &c
, 1);
1893 if (send_raw_mouse
) {
1894 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
1899 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1903 * Move the system caret. (We maintain one, even though it's
1904 * invisible, for the benefit of blind people: apparently some
1905 * helper software tracks the system caret, so we should arrange to
1908 void sys_cursor(int x
, int y
)
1911 SetCaretPos(x
* font_width
, y
* font_height
);
1915 * Draw a line of text in the window, at given character
1916 * coordinates, in given attributes.
1918 * We are allowed to fiddle with the contents of `text'.
1920 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
1921 unsigned long attr
, int lattr
)
1924 int nfg
, nbg
, nfont
;
1927 int force_manual_underline
= 0;
1928 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
1929 int char_width
= fnt_width
;
1930 int text_adjust
= 0;
1931 static int *IpDx
= 0, IpDxLEN
= 0;
1933 if (attr
& ATTR_WIDE
)
1936 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
1938 if (len
> IpDxLEN
) {
1940 IpDx
= smalloc((len
+ 16) * sizeof(int));
1941 IpDxLEN
= (len
+ 16);
1943 for (i
= 0; i
< IpDxLEN
; i
++)
1944 IpDx
[i
] = char_width
;
1950 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
1951 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
1952 attr
^= ATTR_CUR_XOR
;
1956 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
1957 /* Assume a poorman font is borken in other ways too. */
1967 nfont
|= FONT_WIDE
+ FONT_HIGH
;
1971 /* Special hack for the VT100 linedraw glyphs. */
1972 if ((attr
& CSET_MASK
) == 0x2300) {
1973 if (!dbcs_screenfont
&&
1974 text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
1975 switch ((unsigned char) (text
[0])) {
1977 text_adjust
= -2 * font_height
/ 5;
1980 text_adjust
= -1 * font_height
/ 5;
1983 text_adjust
= font_height
/ 5;
1986 text_adjust
= 2 * font_height
/ 5;
1989 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
1992 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
1993 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
1994 if (attr
& ATTR_UNDER
) {
1995 attr
&= ~ATTR_UNDER
;
1996 force_manual_underline
= 1;
2001 /* Anything left as an original character set is unprintable. */
2002 if (DIRECT_CHAR(attr
)) {
2005 memset(text
, 0xFF, len
);
2009 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2012 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2013 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2014 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2016 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2017 nfont
|= FONT_UNDERLINE
;
2018 another_font(nfont
);
2019 if (!fonts
[nfont
]) {
2020 if (nfont
& FONT_UNDERLINE
)
2021 force_manual_underline
= 1;
2022 /* Don't do the same for manual bold, it could be bad news. */
2024 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2026 another_font(nfont
);
2028 nfont
= FONT_NORMAL
;
2029 if (attr
& ATTR_REVERSE
) {
2034 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2036 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2040 SelectObject(hdc
, fonts
[nfont
]);
2041 SetTextColor(hdc
, fg
);
2042 SetBkColor(hdc
, bg
);
2043 SetBkMode(hdc
, OPAQUE
);
2046 line_box
.right
= x
+ char_width
* len
;
2047 line_box
.bottom
= y
+ font_height
;
2049 /* We're using a private area for direct to font. (512 chars.) */
2050 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2051 /* Ho Hum, dbcs fonts are a PITA! */
2052 /* To display on W9x I have to convert to UCS */
2053 static wchar_t *uni_buf
= 0;
2054 static int uni_len
= 0;
2056 if (len
> uni_len
) {
2058 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2060 nlen
= MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2061 text
, len
, uni_buf
, uni_len
);
2067 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2068 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, 0);
2069 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2070 SetBkMode(hdc
, TRANSPARENT
);
2071 ExtTextOutW(hdc
, x
- 1,
2072 y
- font_height
* (lattr
==
2073 LATTR_BOT
) + text_adjust
,
2074 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, 0);
2076 } else if (DIRECT_FONT(attr
)) {
2078 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2079 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2080 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2081 SetBkMode(hdc
, TRANSPARENT
);
2083 /* GRR: This draws the character outside it's box and can leave
2084 * 'droppings' even with the clip box! I suppose I could loop it
2085 * one character at a time ... yuk.
2087 * Or ... I could do a test print with "W", and use +1 or -1 for this
2088 * shift depending on if the leftmost column is blank...
2090 ExtTextOut(hdc
, x
- 1,
2091 y
- font_height
* (lattr
==
2092 LATTR_BOT
) + text_adjust
,
2093 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2096 /* And 'normal' unicode characters */
2097 static WCHAR
*wbuf
= NULL
;
2098 static int wlen
= 0;
2103 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2105 for (i
= 0; i
< len
; i
++)
2106 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2109 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2110 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2112 /* And the shadow bold hack. */
2113 if (bold_mode
== BOLD_SHADOW
) {
2114 SetBkMode(hdc
, TRANSPARENT
);
2115 ExtTextOutW(hdc
, x
- 1,
2116 y
- font_height
* (lattr
==
2117 LATTR_BOT
) + text_adjust
,
2118 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2121 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2122 (und_mode
== UND_LINE
2123 && (attr
& ATTR_UNDER
)))) {
2126 if (lattr
== LATTR_BOT
)
2127 dec
= dec
* 2 - font_height
;
2129 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2130 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2131 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2132 oldpen
= SelectObject(hdc
, oldpen
);
2133 DeleteObject(oldpen
);
2137 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2138 unsigned long attr
, int lattr
)
2144 int ctype
= cfg
.cursor_type
;
2146 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2147 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2148 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2152 attr
|= TATTR_RIGHTCURS
;
2155 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2156 if (attr
& ATTR_WIDE
)
2161 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2164 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2165 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2166 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2167 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2168 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2169 Polyline(hdc
, pts
, 5);
2170 oldpen
= SelectObject(hdc
, oldpen
);
2171 DeleteObject(oldpen
);
2172 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2173 int startx
, starty
, dx
, dy
, length
, i
;
2176 starty
= y
+ descent
;
2179 length
= char_width
;
2182 if (attr
& TATTR_RIGHTCURS
)
2183 xadjust
= char_width
- 1;
2184 startx
= x
+ xadjust
;
2188 length
= font_height
;
2190 if (attr
& TATTR_ACTCURS
) {
2193 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2194 MoveToEx(hdc
, startx
, starty
, NULL
);
2195 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2196 oldpen
= SelectObject(hdc
, oldpen
);
2197 DeleteObject(oldpen
);
2199 for (i
= 0; i
< length
; i
++) {
2201 SetPixel(hdc
, startx
, starty
, colours
[23]);
2211 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2212 * codes. Returns number of bytes used or zero to drop the message
2213 * or -1 to forward the message to windows.
2215 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2216 unsigned char *output
)
2219 int scan
, left_alt
= 0, key_down
, shift_state
;
2221 unsigned char *p
= output
;
2222 static int alt_state
= 0;
2223 static int alt_sum
= 0;
2225 HKL kbd_layout
= GetKeyboardLayout(0);
2227 static WORD keys
[3];
2228 static int compose_char
= 0;
2229 static WPARAM compose_key
= 0;
2231 r
= GetKeyboardState(keystate
);
2233 memset(keystate
, 0, sizeof(keystate
));
2236 #define SHOW_TOASCII_RESULT
2237 { /* Tell us all about key events */
2238 static BYTE oldstate
[256];
2239 static int first
= 1;
2243 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2246 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2248 } else if ((HIWORD(lParam
) & KF_UP
)
2249 && scan
== (HIWORD(lParam
) & 0xFF)) {
2253 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2254 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2267 debug(("VK_%02x", wParam
));
2269 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2271 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2273 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2274 if (ch
>= ' ' && ch
<= '~')
2275 debug((", '%c'", ch
));
2277 debug((", $%02x", ch
));
2280 debug((", KB0=%02x", keys
[0]));
2282 debug((", KB1=%02x", keys
[1]));
2284 debug((", KB2=%02x", keys
[2]));
2286 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2288 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2290 if ((HIWORD(lParam
) & KF_EXTENDED
))
2292 if ((HIWORD(lParam
) & KF_UP
))
2296 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2297 else if ((HIWORD(lParam
) & KF_UP
))
2298 oldstate
[wParam
& 0xFF] ^= 0x80;
2300 oldstate
[wParam
& 0xFF] ^= 0x81;
2302 for (ch
= 0; ch
< 256; ch
++)
2303 if (oldstate
[ch
] != keystate
[ch
])
2304 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2306 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2310 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2311 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2315 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2316 if ((cfg
.funky_type
== 3 ||
2317 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2318 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2320 wParam
= VK_EXECUTE
;
2322 /* UnToggle NUMLock */
2323 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2324 keystate
[VK_NUMLOCK
] ^= 1;
2327 /* And write back the 'adjusted' state */
2328 SetKeyboardState(keystate
);
2331 /* Disable Auto repeat if required */
2332 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2335 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2338 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2340 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2341 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2342 if (cfg
.ctrlaltkeys
)
2343 keystate
[VK_MENU
] = 0;
2345 keystate
[VK_RMENU
] = 0x80;
2350 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2351 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2352 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2354 /* Note if AltGr was pressed and if it was used as a compose key */
2355 if (!compose_state
) {
2356 compose_key
= 0x100;
2357 if (cfg
.compose_key
) {
2358 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2359 compose_key
= wParam
;
2361 if (wParam
== VK_APPS
)
2362 compose_key
= wParam
;
2365 if (wParam
== compose_key
) {
2366 if (compose_state
== 0
2367 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2369 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2373 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
2377 * Record that we pressed key so the scroll window can be reset, but
2378 * be careful to avoid Shift-UP/Down
2380 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
2384 /* Make sure we're not pasting */
2388 if (compose_state
> 1 && left_alt
)
2391 /* Sanitize the number pad if not using a PC NumPad */
2392 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2393 && cfg
.funky_type
!= 2)
2394 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
2395 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
2399 nParam
= VK_NUMPAD0
;
2402 nParam
= VK_NUMPAD1
;
2405 nParam
= VK_NUMPAD2
;
2408 nParam
= VK_NUMPAD3
;
2411 nParam
= VK_NUMPAD4
;
2414 nParam
= VK_NUMPAD5
;
2417 nParam
= VK_NUMPAD6
;
2420 nParam
= VK_NUMPAD7
;
2423 nParam
= VK_NUMPAD8
;
2426 nParam
= VK_NUMPAD9
;
2429 nParam
= VK_DECIMAL
;
2433 if (keystate
[VK_NUMLOCK
] & 1)
2440 /* If a key is pressed and AltGr is not active */
2441 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
2442 /* Okay, prepare for most alts then ... */
2446 /* Lets see if it's a pattern we know all about ... */
2447 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2448 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2451 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2452 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
2455 if (wParam
== VK_INSERT
&& shift_state
== 1) {
2456 term_mouse(MBT_PASTE
, MA_CLICK
, 0, 0, 0, 0);
2457 term_mouse(MBT_PASTE
, MA_RELEASE
, 0, 0, 0, 0);
2460 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
2463 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
2465 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
2466 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
2469 /* Control-Numlock for app-keypad mode switch */
2470 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
2471 app_keypad_keys
^= 1;
2475 /* Nethack keypad */
2476 if (cfg
.nethack_keypad
&& !left_alt
) {
2479 *p
++ = shift_state ?
'B' : 'b';
2482 *p
++ = shift_state ?
'J' : 'j';
2485 *p
++ = shift_state ?
'N' : 'n';
2488 *p
++ = shift_state ?
'H' : 'h';
2491 *p
++ = shift_state ?
'.' : '.';
2494 *p
++ = shift_state ?
'L' : 'l';
2497 *p
++ = shift_state ?
'Y' : 'y';
2500 *p
++ = shift_state ?
'K' : 'k';
2503 *p
++ = shift_state ?
'U' : 'u';
2508 /* Application Keypad */
2512 if (cfg
.funky_type
== 3 ||
2513 (cfg
.funky_type
<= 1 &&
2514 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
2528 if (app_keypad_keys
&& !cfg
.no_applic_k
)
2565 if (cfg
.funky_type
== 2) {
2570 } else if (shift_state
)
2577 if (cfg
.funky_type
== 2)
2581 if (cfg
.funky_type
== 2)
2585 if (cfg
.funky_type
== 2)
2590 if (HIWORD(lParam
) & KF_EXTENDED
)
2596 if (xkey
>= 'P' && xkey
<= 'S')
2597 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
2599 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
2601 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
2606 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
2607 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
2611 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
2617 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
2621 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
2625 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
2630 if (wParam
== VK_PAUSE
) { /* Break/Pause */
2635 /* Control-2 to Control-8 are special */
2636 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
2637 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
2640 if (shift_state
== 2 && wParam
== 0xBD) {
2644 if (shift_state
== 2 && wParam
== 0xDF) {
2648 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
2655 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2656 * for integer decimal nn.)
2658 * We also deal with the weird ones here. Linux VCs replace F1
2659 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2660 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2666 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
2669 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
2672 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
2675 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
2678 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
2681 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
2684 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
2687 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
2690 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
2693 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
2744 /* Reorder edit keys to physical order */
2745 if (cfg
.funky_type
== 3 && code
<= 6)
2746 code
= "\0\2\1\4\5\3\6"[code
];
2748 if (vt52_mode
&& code
> 0 && code
<= 6) {
2749 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
2753 if (cfg
.funky_type
== 5 && code
>= 11 && code
<= 34) {
2754 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
2757 case VK_F1
: index
= 0; break;
2758 case VK_F2
: index
= 1; break;
2759 case VK_F3
: index
= 2; break;
2760 case VK_F4
: index
= 3; break;
2761 case VK_F5
: index
= 4; break;
2762 case VK_F6
: index
= 5; break;
2763 case VK_F7
: index
= 6; break;
2764 case VK_F8
: index
= 7; break;
2765 case VK_F9
: index
= 8; break;
2766 case VK_F10
: index
= 9; break;
2767 case VK_F11
: index
= 10; break;
2768 case VK_F12
: index
= 11; break;
2770 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
2771 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
2772 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
2775 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
2782 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
2785 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
2788 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
2789 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
2792 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
2794 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
2796 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
2799 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
2800 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
2804 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
2809 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2810 * some reason seems to send VK_CLEAR to Windows...).
2833 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
2835 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
2836 /* VT100 & VT102 manuals both state the app cursor keys
2837 * only work if the app keypad is on.
2839 if (!app_keypad_keys
)
2841 /* Useful mapping of Ctrl-arrows */
2842 if (shift_state
== 2)
2846 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
2848 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
2855 * Finally, deal with Return ourselves. (Win95 seems to
2856 * foul it up when Alt is pressed, for some reason.)
2858 if (wParam
== VK_RETURN
) { /* Return */
2864 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
2865 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
2870 /* Okay we've done everything interesting; let windows deal with
2871 * the boring stuff */
2873 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
2874 #ifdef SHOW_TOASCII_RESULT
2875 if (r
== 1 && !key_down
) {
2877 if (utf
|| dbcs_screenfont
)
2878 debug((", (U+%04x)", alt_sum
));
2880 debug((", LCH(%d)", alt_sum
));
2882 debug((", ACH(%d)", keys
[0]));
2887 for (r1
= 0; r1
< r
; r1
++) {
2888 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
2896 for (i
= 0; i
< r
; i
++) {
2897 unsigned char ch
= (unsigned char) keys
[i
];
2899 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
2904 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
2908 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
2909 MessageBeep(MB_ICONHAND
);
2913 luni_send(&keybuf
, 1);
2921 if (utf
|| dbcs_screenfont
) {
2923 luni_send(&keybuf
, 1);
2925 ch
= (char) alt_sum
;
2930 lpage_send(kbd_codepage
, &ch
, 1);
2932 static char cbuf
[] = "\033 ";
2934 lpage_send(kbd_codepage
, cbuf
+ !left_alt
,
2939 /* This is so the ALT-Numpad and dead keys work correctly. */
2944 /* If we're definitly not building up an ALT-54321 then clear it */
2947 /* If we will be using alt_sum fix the 256s */
2948 else if (keys
[0] && (utf
|| dbcs_screenfont
))
2952 /* ALT alone may or may not want to bring up the System menu */
2953 if (wParam
== VK_MENU
) {
2955 if (message
== WM_SYSKEYDOWN
)
2957 else if (message
== WM_SYSKEYUP
&& alt_state
)
2958 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
2959 if (message
== WM_SYSKEYUP
)
2969 void set_title(char *title
)
2972 window_name
= smalloc(1 + strlen(title
));
2973 strcpy(window_name
, title
);
2974 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
2975 SetWindowText(hwnd
, title
);
2978 void set_icon(char *title
)
2981 icon_name
= smalloc(1 + strlen(title
));
2982 strcpy(icon_name
, title
);
2983 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
2984 SetWindowText(hwnd
, title
);
2987 void set_sbar(int total
, int start
, int page
)
2994 si
.cbSize
= sizeof(si
);
2995 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
2997 si
.nMax
= total
- 1;
3001 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3004 Context
get_ctx(void)
3010 SelectPalette(hdc
, pal
, FALSE
);
3016 void free_ctx(Context ctx
)
3018 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3019 ReleaseDC(hwnd
, ctx
);
3022 static void real_palette_set(int n
, int r
, int g
, int b
)
3025 logpal
->palPalEntry
[n
].peRed
= r
;
3026 logpal
->palPalEntry
[n
].peGreen
= g
;
3027 logpal
->palPalEntry
[n
].peBlue
= b
;
3028 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3029 colours
[n
] = PALETTERGB(r
, g
, b
);
3030 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3032 colours
[n
] = RGB(r
, g
, b
);
3035 void palette_set(int n
, int r
, int g
, int b
)
3037 static const int first
[21] = {
3038 0, 2, 4, 6, 8, 10, 12, 14,
3039 1, 3, 5, 7, 9, 11, 13, 15,
3042 real_palette_set(first
[n
], r
, g
, b
);
3044 real_palette_set(first
[n
] + 1, r
, g
, b
);
3046 HDC hdc
= get_ctx();
3047 UnrealizeObject(pal
);
3048 RealizePalette(hdc
);
3053 void palette_reset(void)
3057 for (i
= 0; i
< NCOLOURS
; i
++) {
3059 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3060 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3061 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3062 logpal
->palPalEntry
[i
].peFlags
= 0;
3063 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3064 defpal
[i
].rgbtGreen
,
3065 defpal
[i
].rgbtBlue
);
3067 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3068 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3073 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3075 RealizePalette(hdc
);
3080 void write_aclip(char *data
, int len
, int must_deselect
)
3085 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3088 lock
= GlobalLock(clipdata
);
3091 memcpy(lock
, data
, len
);
3092 ((unsigned char *) lock
)[len
] = 0;
3093 GlobalUnlock(clipdata
);
3096 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3098 if (OpenClipboard(hwnd
)) {
3100 SetClipboardData(CF_TEXT
, clipdata
);
3103 GlobalFree(clipdata
);
3106 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3110 * Note: unlike write_aclip() this will not append a nul.
3112 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3119 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3121 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3122 len
* sizeof(wchar_t));
3123 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3125 if (!clipdata
|| !clipdata2
) {
3127 GlobalFree(clipdata
);
3129 GlobalFree(clipdata2
);
3132 if (!(lock
= GlobalLock(clipdata
)))
3134 if (!(lock2
= GlobalLock(clipdata2
)))
3137 memcpy(lock
, data
, len
* sizeof(wchar_t));
3138 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3140 GlobalUnlock(clipdata
);
3141 GlobalUnlock(clipdata2
);
3144 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3146 if (OpenClipboard(hwnd
)) {
3148 SetClipboardData(CF_UNICODETEXT
, clipdata
);
3149 SetClipboardData(CF_TEXT
, clipdata2
);
3152 GlobalFree(clipdata
);
3153 GlobalFree(clipdata2
);
3157 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3160 void get_clip(wchar_t ** p
, int *len
)
3162 static HGLOBAL clipdata
= NULL
;
3163 static wchar_t *converted
= 0;
3172 GlobalUnlock(clipdata
);
3175 } else if (OpenClipboard(NULL
)) {
3176 if (clipdata
= GetClipboardData(CF_UNICODETEXT
)) {
3178 *p
= GlobalLock(clipdata
);
3180 for (p2
= *p
; *p2
; p2
++);
3184 } else if (clipdata
= GetClipboardData(CF_TEXT
)) {
3188 s
= GlobalLock(clipdata
);
3189 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
3190 *p
= converted
= smalloc(i
* sizeof(wchar_t));
3191 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
3204 * Move `lines' lines from position `from' to position `to' in the
3207 void optimised_move(int to
, int from
, int lines
)
3212 min
= (to
< from ? to
: from
);
3213 max
= to
+ from
- min
;
3216 r
.right
= cols
* font_width
;
3217 r
.top
= min
* font_height
;
3218 r
.bottom
= (max
+ lines
) * font_height
;
3219 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
3224 * Print a message box and perform a fatal exit.
3226 void fatalbox(char *fmt
, ...)
3232 vsprintf(stuff
, fmt
, ap
);
3234 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
3243 if (mode
== BELL_DEFAULT
) {
3245 * For MessageBeep style bells, we want to be careful of
3246 * timing, because they don't have the nice property of
3247 * PlaySound bells that each one cancels the previous
3248 * active one. So we limit the rate to one per 50ms or so.
3250 static long lastbeep
= 0;
3253 beepdiff
= GetTickCount() - lastbeep
;
3254 if (beepdiff
>= 0 && beepdiff
< 50)
3258 * The above MessageBeep call takes time, so we record the
3259 * time _after_ it finishes rather than before it starts.
3261 lastbeep
= GetTickCount();
3262 } else if (mode
== BELL_WAVEFILE
) {
3263 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
3264 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
3265 sprintf(buf
, "Unable to play sound file\n%s\n"
3266 "Using default sound instead", cfg
.bell_wavefile
);
3267 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
3268 MB_OK
| MB_ICONEXCLAMATION
);
3269 cfg
.beep
= BELL_DEFAULT
;