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
;
1145 return 0; /* shouldn't happen */
1148 static void show_mouseptr(int show
)
1150 static int cursor_visible
= 1;
1151 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1153 if (cursor_visible
&& !show
)
1155 else if (!cursor_visible
&& show
)
1157 cursor_visible
= show
;
1160 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1161 WPARAM wParam
, LPARAM lParam
)
1164 static int ignore_size
= FALSE
;
1165 static int ignore_clip
= FALSE
;
1166 static int just_reconfigged
= FALSE
;
1167 static int resizing
= FALSE
;
1168 static int need_backend_resize
= FALSE
;
1169 static int defered_resize
= FALSE
;
1173 if (pending_netevent
)
1174 enact_pending_netevent();
1181 if (cfg
.ping_interval
> 0) {
1184 if (now
- last_movement
> cfg
.ping_interval
) {
1185 back
->special(TS_PING
);
1186 last_movement
= now
;
1194 if (!cfg
.warn_on_close
|| session_closed
||
1196 "Are you sure you want to close this session?",
1197 "PuTTY Exit Confirmation",
1198 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1199 DestroyWindow(hwnd
);
1206 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1218 PROCESS_INFORMATION pi
;
1219 HANDLE filemap
= NULL
;
1221 if (wParam
== IDM_DUPSESS
) {
1223 * Allocate a file-mapping memory chunk for the
1226 SECURITY_ATTRIBUTES sa
;
1229 sa
.nLength
= sizeof(sa
);
1230 sa
.lpSecurityDescriptor
= NULL
;
1231 sa
.bInheritHandle
= TRUE
;
1232 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1235 0, sizeof(Config
), NULL
);
1237 p
= (Config
*) MapViewOfFile(filemap
,
1239 0, 0, sizeof(Config
));
1241 *p
= cfg
; /* structure copy */
1245 sprintf(c
, "putty &%p", filemap
);
1247 } else if (wParam
== IDM_SAVEDSESS
) {
1249 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1250 cl
= smalloc(16 + strlen(session
)); /* 8, but play safe */
1252 cl
= NULL
; /* not a very important failure mode */
1254 sprintf(cl
, "putty @%s", session
);
1260 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1262 si
.lpReserved
= NULL
;
1263 si
.lpDesktop
= NULL
;
1267 si
.lpReserved2
= NULL
;
1268 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1269 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1272 CloseHandle(filemap
);
1279 int prev_alwaysontop
= cfg
.alwaysontop
;
1280 int prev_sunken_edge
= cfg
.sunken_edge
;
1281 char oldlogfile
[FILENAME_MAX
];
1283 int need_setwpos
= FALSE
;
1284 int old_fwidth
, old_fheight
;
1286 strcpy(oldlogfile
, cfg
.logfilename
);
1287 oldlogtype
= cfg
.logtype
;
1288 old_fwidth
= font_width
;
1289 old_fheight
= font_height
;
1290 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1292 if (!do_reconfig(hwnd
))
1295 if (strcmp(oldlogfile
, cfg
.logfilename
) ||
1296 oldlogtype
!= cfg
.logtype
) {
1297 logfclose(); /* reset logging */
1301 just_reconfigged
= TRUE
;
1303 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1304 und_mode
= UND_FONT
;
1308 * Flush the line discipline's edit buffer in the
1309 * case where local editing has just been disabled.
1311 ldisc_send(NULL
, 0);
1319 /* Enable or disable the scroll bar, etc */
1321 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1322 LONG nexflag
, exflag
=
1323 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1326 if (cfg
.alwaysontop
!= prev_alwaysontop
) {
1327 if (cfg
.alwaysontop
) {
1328 nexflag
|= WS_EX_TOPMOST
;
1329 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1330 SWP_NOMOVE
| SWP_NOSIZE
);
1332 nexflag
&= ~(WS_EX_TOPMOST
);
1333 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1334 SWP_NOMOVE
| SWP_NOSIZE
);
1337 if (cfg
.sunken_edge
)
1338 nexflag
|= WS_EX_CLIENTEDGE
;
1340 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1346 nflg
&= ~WS_VSCROLL
;
1348 nflg
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1350 nflg
|= (WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1352 if (nflg
!= flag
|| nexflag
!= exflag
) {
1356 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1357 if (nexflag
!= exflag
)
1358 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1360 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
1362 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1363 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1364 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
1365 | SWP_FRAMECHANGED
);
1367 GetWindowRect(hwnd
, &wr
);
1368 GetClientRect(hwnd
, &cr
);
1370 wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1372 wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1373 need_setwpos
= TRUE
;
1377 if (cfg
.height
!= rows
||
1378 cfg
.width
!= cols
||
1379 old_fwidth
!= font_width
||
1380 old_fheight
!= font_height
||
1381 cfg
.savelines
!= savelines
||
1382 cfg
.sunken_edge
!= prev_sunken_edge
)
1383 need_setwpos
= TRUE
;
1385 if (IsZoomed(hwnd
)) {
1389 defered_resize
= TRUE
;
1391 GetClientRect(hwnd
, &cr
);
1392 w
= cr
.right
- cr
.left
;
1393 h
= cr
.bottom
- cr
.top
;
1397 h
= h
/ font_height
;
1401 term_size(h
, w
, cfg
.savelines
);
1402 InvalidateRect(hwnd
, NULL
, TRUE
);
1405 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1406 InvalidateRect(hwnd
, NULL
, TRUE
);
1408 SetWindowPos(hwnd
, NULL
, 0, 0,
1409 extra_width
+ font_width
* cfg
.width
,
1411 font_height
* cfg
.height
,
1412 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1413 SWP_NOMOVE
| SWP_NOZORDER
);
1417 if (cfg
.locksize
&& IsZoomed(hwnd
))
1419 set_title(cfg
.wintitle
);
1420 if (IsIconic(hwnd
)) {
1422 cfg
.win_name_always ? window_name
:
1437 back
->special(TS_AYT
);
1440 back
->special(TS_BRK
);
1443 back
->special(TS_SYNCH
);
1446 back
->special(TS_EC
);
1449 back
->special(TS_EL
);
1452 back
->special(TS_GA
);
1455 back
->special(TS_NOP
);
1458 back
->special(TS_ABORT
);
1461 back
->special(TS_AO
);
1464 back
->special(TS_IP
);
1467 back
->special(TS_SUSP
);
1470 back
->special(TS_EOR
);
1473 back
->special(TS_EOF
);
1479 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1480 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1485 #define X_POS(l) ((int)(short)LOWORD(l))
1486 #define Y_POS(l) ((int)(short)HIWORD(l))
1488 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1489 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1490 #define WHEEL_DELTA 120
1493 wheel_accumulator
+= (short) HIWORD(wParam
);
1494 wParam
= LOWORD(wParam
);
1496 /* process events when the threshold is reached */
1497 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
1500 /* reduce amount for next time */
1501 if (wheel_accumulator
> 0) {
1503 wheel_accumulator
-= WHEEL_DELTA
;
1504 } else if (wheel_accumulator
< 0) {
1506 wheel_accumulator
+= WHEEL_DELTA
;
1510 if (send_raw_mouse
) {
1511 /* send a mouse-down followed by a mouse up */
1514 TO_CHR_X(X_POS(lParam
)),
1515 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1516 wParam
& MK_CONTROL
);
1517 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1518 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1519 wParam
& MK_CONTROL
);
1521 /* trigger a scroll */
1523 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
1528 case WM_LBUTTONDOWN
:
1529 case WM_MBUTTONDOWN
:
1530 case WM_RBUTTONDOWN
:
1537 case WM_LBUTTONDOWN
:
1541 case WM_MBUTTONDOWN
:
1542 button
= MBT_MIDDLE
;
1545 case WM_RBUTTONDOWN
:
1554 button
= MBT_MIDDLE
;
1562 button
= press
= 0; /* shouldn't happen */
1567 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1568 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
);
1571 term_mouse(button
, MA_RELEASE
,
1572 TO_CHR_X(X_POS(lParam
)),
1573 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1574 wParam
& MK_CONTROL
);
1582 * Add the mouse position and message time to the random
1585 noise_ultralight(lParam
);
1587 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1589 if (wParam
& MK_LBUTTON
)
1591 else if (wParam
& MK_MBUTTON
)
1592 b
= cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1594 b
= cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1595 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1596 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1597 wParam
& MK_CONTROL
);
1600 case WM_NCMOUSEMOVE
:
1602 noise_ultralight(lParam
);
1604 case WM_IGNORE_CLIP
:
1605 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1607 case WM_DESTROYCLIPBOARD
:
1610 ignore_clip
= FALSE
;
1616 hdc
= BeginPaint(hwnd
, &p
);
1618 SelectPalette(hdc
, pal
, TRUE
);
1619 RealizePalette(hdc
);
1621 term_paint(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1622 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1623 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
1624 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
1630 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1631 * but the only one that's likely to try to overload us is FD_READ.
1632 * This means buffering just one is fine.
1634 if (pending_netevent
)
1635 enact_pending_netevent();
1637 pending_netevent
= TRUE
;
1638 pend_netevent_wParam
= wParam
;
1639 pend_netevent_lParam
= lParam
;
1640 time(&last_movement
);
1644 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
1657 case WM_IGNORE_SIZE
:
1658 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1660 case WM_ENTERSIZEMOVE
:
1663 need_backend_resize
= FALSE
;
1665 case WM_EXITSIZEMOVE
:
1668 if (need_backend_resize
)
1673 int width
, height
, w
, h
, ew
, eh
;
1674 LPRECT r
= (LPRECT
) lParam
;
1676 width
= r
->right
- r
->left
- extra_width
;
1677 height
= r
->bottom
- r
->top
- extra_height
;
1678 w
= (width
+ font_width
/ 2) / font_width
;
1681 h
= (height
+ font_height
/ 2) / font_height
;
1684 UpdateSizeTip(hwnd
, w
, h
);
1685 ew
= width
- w
* font_width
;
1686 eh
= height
- h
* font_height
;
1688 if (wParam
== WMSZ_LEFT
||
1689 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
1695 if (wParam
== WMSZ_TOP
||
1696 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
1706 /* break; (never reached) */
1708 if (wParam
== SIZE_MINIMIZED
) {
1710 cfg
.win_name_always ? window_name
: icon_name
);
1713 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1714 SetWindowText(hwnd
, window_name
);
1716 int width
, height
, w
, h
;
1717 #if 0 /* we have fixed this using WM_SIZING now */
1721 width
= LOWORD(lParam
);
1722 height
= HIWORD(lParam
);
1723 w
= width
/ font_width
;
1726 h
= height
/ font_height
;
1729 #if 0 /* we have fixed this using WM_SIZING now */
1730 ew
= width
- w
* font_width
;
1731 eh
= height
- h
* font_height
;
1732 if (ew
!= 0 || eh
!= 0) {
1734 GetWindowRect(hwnd
, &r
);
1735 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
1736 SetWindowPos(hwnd
, NULL
, 0, 0,
1737 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1738 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1741 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1743 term_size(h
, w
, cfg
.savelines
);
1745 * Don't call back->size in mid-resize. (To prevent
1746 * massive numbers of resize events getting sent
1747 * down the connection during an NT opaque drag.)
1752 need_backend_resize
= TRUE
;
1756 just_reconfigged
= FALSE
;
1759 if (wParam
== SIZE_RESTORED
&& defered_resize
) {
1760 defered_resize
= FALSE
;
1761 SetWindowPos(hwnd
, NULL
, 0, 0,
1762 extra_width
+ font_width
* cfg
.width
,
1763 extra_height
+ font_height
* cfg
.height
,
1764 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1765 SWP_NOMOVE
| SWP_NOZORDER
);
1767 ignore_size
= FALSE
;
1770 switch (LOWORD(wParam
)) {
1784 term_scroll(0, +rows
/ 2);
1787 term_scroll(0, -rows
/ 2);
1789 case SB_THUMBPOSITION
:
1791 term_scroll(1, HIWORD(wParam
));
1795 case WM_PALETTECHANGED
:
1796 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1797 HDC hdc
= get_ctx();
1799 if (RealizePalette(hdc
) > 0)
1805 case WM_QUERYNEWPALETTE
:
1807 HDC hdc
= get_ctx();
1809 if (RealizePalette(hdc
) > 0)
1821 * Add the scan code and keypress timing to the random
1824 noise_ultralight(lParam
);
1827 * We don't do TranslateMessage since it disassociates the
1828 * resulting CHAR message from the KEYDOWN that sparked it,
1829 * which we occasionally don't want. Instead, we process
1830 * KEYDOWN, and call the Win32 translator functions so that
1831 * we get the translations under _our_ control.
1834 unsigned char buf
[20];
1837 if (wParam
== VK_PROCESSKEY
) {
1840 m
.message
= WM_KEYDOWN
;
1842 m
.lParam
= lParam
& 0xdfff;
1843 TranslateMessage(&m
);
1845 len
= TranslateKey(message
, wParam
, lParam
, buf
);
1847 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1848 ldisc_send(buf
, len
);
1855 case WM_INPUTLANGCHANGE
:
1857 /* wParam == Font number */
1858 /* lParam == Locale */
1860 HKL NewInputLocale
= (HKL
) lParam
;
1862 // lParam == GetKeyboardLayout(0);
1864 GetLocaleInfo(LOWORD(NewInputLocale
),
1865 LOCALE_IDEFAULTANSICODEPAGE
, lbuf
, sizeof(lbuf
));
1867 kbd_codepage
= atoi(lbuf
);
1871 if (wParam
& 0xFF00) {
1872 unsigned char buf
[2];
1875 buf
[0] = wParam
>> 8;
1876 lpage_send(kbd_codepage
, buf
, 2);
1878 char c
= (unsigned char) wParam
;
1879 lpage_send(kbd_codepage
, &c
, 1);
1885 * Nevertheless, we are prepared to deal with WM_CHAR
1886 * messages, should they crop up. So if someone wants to
1887 * post the things to us as part of a macro manoeuvre,
1888 * we're ready to cope.
1891 char c
= (unsigned char)wParam
;
1892 lpage_send(CP_ACP
, &c
, 1);
1896 if (send_raw_mouse
) {
1897 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
1902 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1906 * Move the system caret. (We maintain one, even though it's
1907 * invisible, for the benefit of blind people: apparently some
1908 * helper software tracks the system caret, so we should arrange to
1911 void sys_cursor(int x
, int y
)
1914 SetCaretPos(x
* font_width
, y
* font_height
);
1918 * Draw a line of text in the window, at given character
1919 * coordinates, in given attributes.
1921 * We are allowed to fiddle with the contents of `text'.
1923 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
1924 unsigned long attr
, int lattr
)
1927 int nfg
, nbg
, nfont
;
1930 int force_manual_underline
= 0;
1931 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
1932 int char_width
= fnt_width
;
1933 int text_adjust
= 0;
1934 static int *IpDx
= 0, IpDxLEN
= 0;
1936 if (attr
& ATTR_WIDE
)
1939 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
1941 if (len
> IpDxLEN
) {
1943 IpDx
= smalloc((len
+ 16) * sizeof(int));
1944 IpDxLEN
= (len
+ 16);
1946 for (i
= 0; i
< IpDxLEN
; i
++)
1947 IpDx
[i
] = char_width
;
1953 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
1954 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
1955 attr
^= ATTR_CUR_XOR
;
1959 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
1960 /* Assume a poorman font is borken in other ways too. */
1970 nfont
|= FONT_WIDE
+ FONT_HIGH
;
1974 /* Special hack for the VT100 linedraw glyphs. */
1975 if ((attr
& CSET_MASK
) == 0x2300) {
1976 if (!dbcs_screenfont
&&
1977 text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
1978 switch ((unsigned char) (text
[0])) {
1980 text_adjust
= -2 * font_height
/ 5;
1983 text_adjust
= -1 * font_height
/ 5;
1986 text_adjust
= font_height
/ 5;
1989 text_adjust
= 2 * font_height
/ 5;
1992 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
1995 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
1996 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
1997 if (attr
& ATTR_UNDER
) {
1998 attr
&= ~ATTR_UNDER
;
1999 force_manual_underline
= 1;
2004 /* Anything left as an original character set is unprintable. */
2005 if (DIRECT_CHAR(attr
)) {
2008 memset(text
, 0xFF, len
);
2012 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2015 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2016 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2017 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2019 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2020 nfont
|= FONT_UNDERLINE
;
2021 another_font(nfont
);
2022 if (!fonts
[nfont
]) {
2023 if (nfont
& FONT_UNDERLINE
)
2024 force_manual_underline
= 1;
2025 /* Don't do the same for manual bold, it could be bad news. */
2027 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2029 another_font(nfont
);
2031 nfont
= FONT_NORMAL
;
2032 if (attr
& ATTR_REVERSE
) {
2037 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2039 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2043 SelectObject(hdc
, fonts
[nfont
]);
2044 SetTextColor(hdc
, fg
);
2045 SetBkColor(hdc
, bg
);
2046 SetBkMode(hdc
, OPAQUE
);
2049 line_box
.right
= x
+ char_width
* len
;
2050 line_box
.bottom
= y
+ font_height
;
2052 /* We're using a private area for direct to font. (512 chars.) */
2053 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2054 /* Ho Hum, dbcs fonts are a PITA! */
2055 /* To display on W9x I have to convert to UCS */
2056 static wchar_t *uni_buf
= 0;
2057 static int uni_len
= 0;
2059 if (len
> uni_len
) {
2061 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2063 nlen
= MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2064 text
, len
, uni_buf
, uni_len
);
2070 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2071 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, 0);
2072 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2073 SetBkMode(hdc
, TRANSPARENT
);
2074 ExtTextOutW(hdc
, x
- 1,
2075 y
- font_height
* (lattr
==
2076 LATTR_BOT
) + text_adjust
,
2077 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, 0);
2079 } else if (DIRECT_FONT(attr
)) {
2081 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2082 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2083 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2084 SetBkMode(hdc
, TRANSPARENT
);
2086 /* GRR: This draws the character outside it's box and can leave
2087 * 'droppings' even with the clip box! I suppose I could loop it
2088 * one character at a time ... yuk.
2090 * Or ... I could do a test print with "W", and use +1 or -1 for this
2091 * shift depending on if the leftmost column is blank...
2093 ExtTextOut(hdc
, x
- 1,
2094 y
- font_height
* (lattr
==
2095 LATTR_BOT
) + text_adjust
,
2096 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2099 /* And 'normal' unicode characters */
2100 static WCHAR
*wbuf
= NULL
;
2101 static int wlen
= 0;
2106 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2108 for (i
= 0; i
< len
; i
++)
2109 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2112 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2113 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2115 /* And the shadow bold hack. */
2116 if (bold_mode
== BOLD_SHADOW
) {
2117 SetBkMode(hdc
, TRANSPARENT
);
2118 ExtTextOutW(hdc
, x
- 1,
2119 y
- font_height
* (lattr
==
2120 LATTR_BOT
) + text_adjust
,
2121 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2124 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2125 (und_mode
== UND_LINE
2126 && (attr
& ATTR_UNDER
)))) {
2129 if (lattr
== LATTR_BOT
)
2130 dec
= dec
* 2 - font_height
;
2132 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2133 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2134 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2135 oldpen
= SelectObject(hdc
, oldpen
);
2136 DeleteObject(oldpen
);
2140 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2141 unsigned long attr
, int lattr
)
2147 int ctype
= cfg
.cursor_type
;
2149 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2150 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2151 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2155 attr
|= TATTR_RIGHTCURS
;
2158 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2159 if (attr
& ATTR_WIDE
)
2164 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2167 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2168 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2169 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2170 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2171 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2172 Polyline(hdc
, pts
, 5);
2173 oldpen
= SelectObject(hdc
, oldpen
);
2174 DeleteObject(oldpen
);
2175 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2176 int startx
, starty
, dx
, dy
, length
, i
;
2179 starty
= y
+ descent
;
2182 length
= char_width
;
2185 if (attr
& TATTR_RIGHTCURS
)
2186 xadjust
= char_width
- 1;
2187 startx
= x
+ xadjust
;
2191 length
= font_height
;
2193 if (attr
& TATTR_ACTCURS
) {
2196 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2197 MoveToEx(hdc
, startx
, starty
, NULL
);
2198 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2199 oldpen
= SelectObject(hdc
, oldpen
);
2200 DeleteObject(oldpen
);
2202 for (i
= 0; i
< length
; i
++) {
2204 SetPixel(hdc
, startx
, starty
, colours
[23]);
2214 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2215 * codes. Returns number of bytes used or zero to drop the message
2216 * or -1 to forward the message to windows.
2218 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2219 unsigned char *output
)
2222 int scan
, left_alt
= 0, key_down
, shift_state
;
2224 unsigned char *p
= output
;
2225 static int alt_state
= 0;
2226 static int alt_sum
= 0;
2228 HKL kbd_layout
= GetKeyboardLayout(0);
2230 static WORD keys
[3];
2231 static int compose_char
= 0;
2232 static WPARAM compose_key
= 0;
2234 r
= GetKeyboardState(keystate
);
2236 memset(keystate
, 0, sizeof(keystate
));
2239 #define SHOW_TOASCII_RESULT
2240 { /* Tell us all about key events */
2241 static BYTE oldstate
[256];
2242 static int first
= 1;
2246 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2249 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2251 } else if ((HIWORD(lParam
) & KF_UP
)
2252 && scan
== (HIWORD(lParam
) & 0xFF)) {
2256 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2257 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2270 debug(("VK_%02x", wParam
));
2272 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2274 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2276 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2277 if (ch
>= ' ' && ch
<= '~')
2278 debug((", '%c'", ch
));
2280 debug((", $%02x", ch
));
2283 debug((", KB0=%02x", keys
[0]));
2285 debug((", KB1=%02x", keys
[1]));
2287 debug((", KB2=%02x", keys
[2]));
2289 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2291 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2293 if ((HIWORD(lParam
) & KF_EXTENDED
))
2295 if ((HIWORD(lParam
) & KF_UP
))
2299 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2300 else if ((HIWORD(lParam
) & KF_UP
))
2301 oldstate
[wParam
& 0xFF] ^= 0x80;
2303 oldstate
[wParam
& 0xFF] ^= 0x81;
2305 for (ch
= 0; ch
< 256; ch
++)
2306 if (oldstate
[ch
] != keystate
[ch
])
2307 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2309 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2313 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2314 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2318 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2319 if ((cfg
.funky_type
== 3 ||
2320 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2321 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2323 wParam
= VK_EXECUTE
;
2325 /* UnToggle NUMLock */
2326 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2327 keystate
[VK_NUMLOCK
] ^= 1;
2330 /* And write back the 'adjusted' state */
2331 SetKeyboardState(keystate
);
2334 /* Disable Auto repeat if required */
2335 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2338 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2341 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2343 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2344 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2345 if (cfg
.ctrlaltkeys
)
2346 keystate
[VK_MENU
] = 0;
2348 keystate
[VK_RMENU
] = 0x80;
2353 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2354 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2355 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2357 /* Note if AltGr was pressed and if it was used as a compose key */
2358 if (!compose_state
) {
2359 compose_key
= 0x100;
2360 if (cfg
.compose_key
) {
2361 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2362 compose_key
= wParam
;
2364 if (wParam
== VK_APPS
)
2365 compose_key
= wParam
;
2368 if (wParam
== compose_key
) {
2369 if (compose_state
== 0
2370 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2372 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2376 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
2380 * Record that we pressed key so the scroll window can be reset, but
2381 * be careful to avoid Shift-UP/Down
2383 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
2387 /* Make sure we're not pasting */
2391 if (compose_state
> 1 && left_alt
)
2394 /* Sanitize the number pad if not using a PC NumPad */
2395 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2396 && cfg
.funky_type
!= 2)
2397 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
2398 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
2402 nParam
= VK_NUMPAD0
;
2405 nParam
= VK_NUMPAD1
;
2408 nParam
= VK_NUMPAD2
;
2411 nParam
= VK_NUMPAD3
;
2414 nParam
= VK_NUMPAD4
;
2417 nParam
= VK_NUMPAD5
;
2420 nParam
= VK_NUMPAD6
;
2423 nParam
= VK_NUMPAD7
;
2426 nParam
= VK_NUMPAD8
;
2429 nParam
= VK_NUMPAD9
;
2432 nParam
= VK_DECIMAL
;
2436 if (keystate
[VK_NUMLOCK
] & 1)
2443 /* If a key is pressed and AltGr is not active */
2444 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
2445 /* Okay, prepare for most alts then ... */
2449 /* Lets see if it's a pattern we know all about ... */
2450 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2451 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2454 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2455 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
2458 if (wParam
== VK_INSERT
&& shift_state
== 1) {
2459 term_mouse(MBT_PASTE
, MA_CLICK
, 0, 0, 0, 0);
2460 term_mouse(MBT_PASTE
, MA_RELEASE
, 0, 0, 0, 0);
2463 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
2466 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
2468 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
2469 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
2472 /* Control-Numlock for app-keypad mode switch */
2473 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
2474 app_keypad_keys
^= 1;
2478 /* Nethack keypad */
2479 if (cfg
.nethack_keypad
&& !left_alt
) {
2482 *p
++ = shift_state ?
'B' : 'b';
2485 *p
++ = shift_state ?
'J' : 'j';
2488 *p
++ = shift_state ?
'N' : 'n';
2491 *p
++ = shift_state ?
'H' : 'h';
2494 *p
++ = shift_state ?
'.' : '.';
2497 *p
++ = shift_state ?
'L' : 'l';
2500 *p
++ = shift_state ?
'Y' : 'y';
2503 *p
++ = shift_state ?
'K' : 'k';
2506 *p
++ = shift_state ?
'U' : 'u';
2511 /* Application Keypad */
2515 if (cfg
.funky_type
== 3 ||
2516 (cfg
.funky_type
<= 1 &&
2517 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
2531 if (app_keypad_keys
&& !cfg
.no_applic_k
)
2568 if (cfg
.funky_type
== 2) {
2573 } else if (shift_state
)
2580 if (cfg
.funky_type
== 2)
2584 if (cfg
.funky_type
== 2)
2588 if (cfg
.funky_type
== 2)
2593 if (HIWORD(lParam
) & KF_EXTENDED
)
2599 if (xkey
>= 'P' && xkey
<= 'S')
2600 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
2602 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
2604 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
2609 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
2610 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
2614 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
2620 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
2624 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
2628 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
2633 if (wParam
== VK_PAUSE
) { /* Break/Pause */
2638 /* Control-2 to Control-8 are special */
2639 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
2640 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
2643 if (shift_state
== 2 && wParam
== 0xBD) {
2647 if (shift_state
== 2 && wParam
== 0xDF) {
2651 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
2658 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2659 * for integer decimal nn.)
2661 * We also deal with the weird ones here. Linux VCs replace F1
2662 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2663 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2669 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
2672 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
2675 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
2678 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
2681 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
2684 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
2687 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
2690 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
2693 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
2696 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
2747 /* Reorder edit keys to physical order */
2748 if (cfg
.funky_type
== 3 && code
<= 6)
2749 code
= "\0\2\1\4\5\3\6"[code
];
2751 if (vt52_mode
&& code
> 0 && code
<= 6) {
2752 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
2756 if (cfg
.funky_type
== 5 && code
>= 11 && code
<= 34) {
2757 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
2760 case VK_F1
: index
= 0; break;
2761 case VK_F2
: index
= 1; break;
2762 case VK_F3
: index
= 2; break;
2763 case VK_F4
: index
= 3; break;
2764 case VK_F5
: index
= 4; break;
2765 case VK_F6
: index
= 5; break;
2766 case VK_F7
: index
= 6; break;
2767 case VK_F8
: index
= 7; break;
2768 case VK_F9
: index
= 8; break;
2769 case VK_F10
: index
= 9; break;
2770 case VK_F11
: index
= 10; break;
2771 case VK_F12
: index
= 11; break;
2773 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
2774 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
2775 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
2778 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
2785 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
2788 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
2791 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
2792 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
2795 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
2797 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
2799 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
2802 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
2803 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
2807 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
2812 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2813 * some reason seems to send VK_CLEAR to Windows...).
2836 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
2838 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
2839 /* VT100 & VT102 manuals both state the app cursor keys
2840 * only work if the app keypad is on.
2842 if (!app_keypad_keys
)
2844 /* Useful mapping of Ctrl-arrows */
2845 if (shift_state
== 2)
2849 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
2851 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
2858 * Finally, deal with Return ourselves. (Win95 seems to
2859 * foul it up when Alt is pressed, for some reason.)
2861 if (wParam
== VK_RETURN
) { /* Return */
2867 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
2868 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
2873 /* Okay we've done everything interesting; let windows deal with
2874 * the boring stuff */
2876 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
2877 #ifdef SHOW_TOASCII_RESULT
2878 if (r
== 1 && !key_down
) {
2880 if (utf
|| dbcs_screenfont
)
2881 debug((", (U+%04x)", alt_sum
));
2883 debug((", LCH(%d)", alt_sum
));
2885 debug((", ACH(%d)", keys
[0]));
2890 for (r1
= 0; r1
< r
; r1
++) {
2891 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
2899 for (i
= 0; i
< r
; i
++) {
2900 unsigned char ch
= (unsigned char) keys
[i
];
2902 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
2907 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
2911 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
2912 MessageBeep(MB_ICONHAND
);
2916 luni_send(&keybuf
, 1);
2924 if (utf
|| dbcs_screenfont
) {
2926 luni_send(&keybuf
, 1);
2928 ch
= (char) alt_sum
;
2933 lpage_send(kbd_codepage
, &ch
, 1);
2935 static char cbuf
[] = "\033 ";
2937 lpage_send(kbd_codepage
, cbuf
+ !left_alt
,
2942 /* This is so the ALT-Numpad and dead keys work correctly. */
2947 /* If we're definitly not building up an ALT-54321 then clear it */
2950 /* If we will be using alt_sum fix the 256s */
2951 else if (keys
[0] && (utf
|| dbcs_screenfont
))
2955 /* ALT alone may or may not want to bring up the System menu */
2956 if (wParam
== VK_MENU
) {
2958 if (message
== WM_SYSKEYDOWN
)
2960 else if (message
== WM_SYSKEYUP
&& alt_state
)
2961 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
2962 if (message
== WM_SYSKEYUP
)
2972 void set_title(char *title
)
2975 window_name
= smalloc(1 + strlen(title
));
2976 strcpy(window_name
, title
);
2977 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
2978 SetWindowText(hwnd
, title
);
2981 void set_icon(char *title
)
2984 icon_name
= smalloc(1 + strlen(title
));
2985 strcpy(icon_name
, title
);
2986 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
2987 SetWindowText(hwnd
, title
);
2990 void set_sbar(int total
, int start
, int page
)
2997 si
.cbSize
= sizeof(si
);
2998 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3000 si
.nMax
= total
- 1;
3004 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3007 Context
get_ctx(void)
3013 SelectPalette(hdc
, pal
, FALSE
);
3019 void free_ctx(Context ctx
)
3021 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3022 ReleaseDC(hwnd
, ctx
);
3025 static void real_palette_set(int n
, int r
, int g
, int b
)
3028 logpal
->palPalEntry
[n
].peRed
= r
;
3029 logpal
->palPalEntry
[n
].peGreen
= g
;
3030 logpal
->palPalEntry
[n
].peBlue
= b
;
3031 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3032 colours
[n
] = PALETTERGB(r
, g
, b
);
3033 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3035 colours
[n
] = RGB(r
, g
, b
);
3038 void palette_set(int n
, int r
, int g
, int b
)
3040 static const int first
[21] = {
3041 0, 2, 4, 6, 8, 10, 12, 14,
3042 1, 3, 5, 7, 9, 11, 13, 15,
3045 real_palette_set(first
[n
], r
, g
, b
);
3047 real_palette_set(first
[n
] + 1, r
, g
, b
);
3049 HDC hdc
= get_ctx();
3050 UnrealizeObject(pal
);
3051 RealizePalette(hdc
);
3056 void palette_reset(void)
3060 for (i
= 0; i
< NCOLOURS
; i
++) {
3062 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3063 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3064 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3065 logpal
->palPalEntry
[i
].peFlags
= 0;
3066 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3067 defpal
[i
].rgbtGreen
,
3068 defpal
[i
].rgbtBlue
);
3070 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3071 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3076 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3078 RealizePalette(hdc
);
3083 void write_aclip(char *data
, int len
, int must_deselect
)
3088 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3091 lock
= GlobalLock(clipdata
);
3094 memcpy(lock
, data
, len
);
3095 ((unsigned char *) lock
)[len
] = 0;
3096 GlobalUnlock(clipdata
);
3099 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3101 if (OpenClipboard(hwnd
)) {
3103 SetClipboardData(CF_TEXT
, clipdata
);
3106 GlobalFree(clipdata
);
3109 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3113 * Note: unlike write_aclip() this will not append a nul.
3115 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3122 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3124 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3125 len
* sizeof(wchar_t));
3126 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3128 if (!clipdata
|| !clipdata2
) {
3130 GlobalFree(clipdata
);
3132 GlobalFree(clipdata2
);
3135 if (!(lock
= GlobalLock(clipdata
)))
3137 if (!(lock2
= GlobalLock(clipdata2
)))
3140 memcpy(lock
, data
, len
* sizeof(wchar_t));
3141 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3143 GlobalUnlock(clipdata
);
3144 GlobalUnlock(clipdata2
);
3147 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3149 if (OpenClipboard(hwnd
)) {
3151 SetClipboardData(CF_UNICODETEXT
, clipdata
);
3152 SetClipboardData(CF_TEXT
, clipdata2
);
3155 GlobalFree(clipdata
);
3156 GlobalFree(clipdata2
);
3160 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3163 void get_clip(wchar_t ** p
, int *len
)
3165 static HGLOBAL clipdata
= NULL
;
3166 static wchar_t *converted
= 0;
3175 GlobalUnlock(clipdata
);
3178 } else if (OpenClipboard(NULL
)) {
3179 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
3181 *p
= GlobalLock(clipdata
);
3183 for (p2
= *p
; *p2
; p2
++);
3187 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
3191 s
= GlobalLock(clipdata
);
3192 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
3193 *p
= converted
= smalloc(i
* sizeof(wchar_t));
3194 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
3207 * Move `lines' lines from position `from' to position `to' in the
3210 void optimised_move(int to
, int from
, int lines
)
3215 min
= (to
< from ? to
: from
);
3216 max
= to
+ from
- min
;
3219 r
.right
= cols
* font_width
;
3220 r
.top
= min
* font_height
;
3221 r
.bottom
= (max
+ lines
) * font_height
;
3222 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
3227 * Print a message box and perform a fatal exit.
3229 void fatalbox(char *fmt
, ...)
3235 vsprintf(stuff
, fmt
, ap
);
3237 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
3246 if (mode
== BELL_DEFAULT
) {
3248 * For MessageBeep style bells, we want to be careful of
3249 * timing, because they don't have the nice property of
3250 * PlaySound bells that each one cancels the previous
3251 * active one. So we limit the rate to one per 50ms or so.
3253 static long lastbeep
= 0;
3256 beepdiff
= GetTickCount() - lastbeep
;
3257 if (beepdiff
>= 0 && beepdiff
< 50)
3261 * The above MessageBeep call takes time, so we record the
3262 * time _after_ it finishes rather than before it starts.
3264 lastbeep
= GetTickCount();
3265 } else if (mode
== BELL_WAVEFILE
) {
3266 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
3267 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
3268 sprintf(buf
, "Unable to play sound file\n%s\n"
3269 "Using default sound instead", cfg
.bell_wavefile
);
3270 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
3271 MB_OK
| MB_ICONEXCLAMATION
);
3272 cfg
.beep
= BELL_DEFAULT
;