17 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
23 #define IDM_SHOWLOG 0x0010
24 #define IDM_NEWSESS 0x0020
25 #define IDM_DUPSESS 0x0030
26 #define IDM_RECONF 0x0040
27 #define IDM_CLRSB 0x0050
28 #define IDM_RESET 0x0060
29 #define IDM_TEL_AYT 0x0070
30 #define IDM_TEL_BRK 0x0080
31 #define IDM_TEL_SYNCH 0x0090
32 #define IDM_TEL_EC 0x00a0
33 #define IDM_TEL_EL 0x00b0
34 #define IDM_TEL_GA 0x00c0
35 #define IDM_TEL_NOP 0x00d0
36 #define IDM_TEL_ABORT 0x00e0
37 #define IDM_TEL_AO 0x00f0
38 #define IDM_TEL_IP 0x0100
39 #define IDM_TEL_SUSP 0x0110
40 #define IDM_TEL_EOR 0x0120
41 #define IDM_TEL_EOF 0x0130
42 #define IDM_ABOUT 0x0140
43 #define IDM_SAVEDSESS 0x0150
44 #define IDM_COPYALL 0x0160
46 #define IDM_SESSLGP 0x0250 /* log type printable */
47 #define IDM_SESSLGA 0x0260 /* log type all chars */
48 #define IDM_SESSLGE 0x0270 /* log end */
49 #define IDM_SAVED_MIN 0x1000
50 #define IDM_SAVED_MAX 0x2000
52 #define WM_IGNORE_SIZE (WM_XUSER + 1)
53 #define WM_IGNORE_CLIP (WM_XUSER + 2)
55 /* Needed for Chinese support and apparently not always defined. */
57 #define VK_PROCESSKEY 0xE5
60 static LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
61 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
62 unsigned char *output
);
63 static void cfgtopalette(void);
64 static void init_palette(void);
65 static void init_fonts(int);
67 static int extra_width
, extra_height
;
69 static int pending_netevent
= 0;
70 static WPARAM pend_netevent_wParam
= 0;
71 static LPARAM pend_netevent_lParam
= 0;
72 static void enact_pending_netevent(void);
74 static time_t last_movement
= 0;
78 #define FONT_UNDERLINE 2
79 #define FONT_BOLDUND 3
81 #define FONT_OEMBOLD 5
82 #define FONT_OEMBOLDUND 6
84 static HFONT fonts
[8];
85 static int font_needs_hand_underlining
;
87 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
95 static COLORREF colours
[NCOLOURS
];
97 static LPLOGPALETTE logpal
;
98 static RGBTRIPLE defpal
[NCOLOURS
];
102 static HBITMAP caretbm
;
104 static int dbltime
, lasttime
, lastact
;
105 static Mouse_Button lastbtn
;
107 /* this allows xterm-style mouse handling. */
108 static int send_raw_mouse
= 0;
109 static int wheel_accumulator
= 0;
111 static char *window_name
, *icon_name
;
113 static int compose_state
= 0;
115 /* Dummy routine, only required in plink. */
116 void ldisc_update(int echo
, int edit
)
120 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
122 static char appname
[] = "PuTTY";
127 int guess_width
, guess_height
;
130 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
132 winsock_ver
= MAKEWORD(1, 1);
133 if (WSAStartup(winsock_ver
, &wsadata
)) {
134 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
135 MB_OK
| MB_ICONEXCLAMATION
);
138 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
139 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
140 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
144 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
147 InitCommonControls();
149 /* Ensure a Maximize setting in Explorer doesn't maximise the
154 * Process the command line.
159 default_protocol
= DEFAULT_PROTOCOL
;
160 default_port
= DEFAULT_PORT
;
161 cfg
.logtype
= LGTYP_NONE
;
163 do_defaults(NULL
, &cfg
);
166 while (*p
&& isspace(*p
))
170 * Process command line options first. Yes, this can be
171 * done better, and it will be as soon as I have the
175 char *q
= p
+ strcspn(p
, " \t");
178 tolower(p
[0]) == 's' &&
179 tolower(p
[1]) == 's' && tolower(p
[2]) == 'h') {
180 default_protocol
= cfg
.protocol
= PROT_SSH
;
181 default_port
= cfg
.port
= 22;
182 } else if (q
== p
+ 7 &&
183 tolower(p
[0]) == 'c' &&
184 tolower(p
[1]) == 'l' &&
185 tolower(p
[2]) == 'e' &&
186 tolower(p
[3]) == 'a' &&
187 tolower(p
[4]) == 'n' &&
188 tolower(p
[5]) == 'u' && tolower(p
[6]) == 'p') {
190 * `putty -cleanup'. Remove all registry entries
191 * associated with PuTTY, and also find and delete
192 * the random seed file.
195 "This procedure will remove ALL Registry\n"
196 "entries associated with PuTTY, and will\n"
197 "also remove the PuTTY random seed file.\n"
199 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
200 "SESSIONS. Are you really sure you want\n"
203 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
208 p
= q
+ strspn(q
, " \t");
212 * An initial @ means to activate a saved session.
216 while (i
> 1 && isspace(p
[i
- 1]))
219 do_defaults(p
+ 1, &cfg
);
220 if (!*cfg
.host
&& !do_config()) {
224 } else if (*p
== '&') {
226 * An initial & means we've been given a command line
227 * containing the hex value of a HANDLE for a file
228 * mapping object, which we must then extract as a
233 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
234 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
235 0, 0, sizeof(Config
))) != NULL
) {
238 CloseHandle(filemap
);
239 } else if (!do_config()) {
246 * If the hostname starts with "telnet:", set the
247 * protocol to Telnet and process the string as a
250 if (!strncmp(q
, "telnet:", 7)) {
254 if (q
[0] == '/' && q
[1] == '/')
256 cfg
.protocol
= PROT_TELNET
;
258 while (*p
&& *p
!= ':' && *p
!= '/')
267 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
268 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
270 while (*p
&& !isspace(*p
))
274 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
275 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
276 while (*p
&& isspace(*p
))
290 /* See if host is of the form user@host */
291 if (cfg
.host
[0] != '\0') {
292 char *atsign
= strchr(cfg
.host
, '@');
293 /* Make sure we're not overflowing the user field */
295 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
296 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
297 cfg
.username
[atsign
- cfg
.host
] = '\0';
299 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
304 * Trim a colon suffix off the hostname if it's there.
306 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
310 * Select protocol. This is farmed out into a table in a
311 * separate file to enable an ssh-free variant.
316 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
317 if (backends
[i
].protocol
== cfg
.protocol
) {
318 back
= backends
[i
].backend
;
322 MessageBox(NULL
, "Unsupported protocol number found",
323 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
329 /* Check for invalid Port number (i.e. zero) */
331 MessageBox(NULL
, "Invalid Port Number",
332 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
339 wndclass
.lpfnWndProc
= WndProc
;
340 wndclass
.cbClsExtra
= 0;
341 wndclass
.cbWndExtra
= 0;
342 wndclass
.hInstance
= inst
;
343 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
344 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
345 wndclass
.hbrBackground
= GetStockObject(BLACK_BRUSH
);
346 wndclass
.lpszMenuName
= NULL
;
347 wndclass
.lpszClassName
= appname
;
349 RegisterClass(&wndclass
);
354 savelines
= cfg
.savelines
;
360 * Guess some defaults for the window size. This all gets
361 * updated later, so we don't really care too much. However, we
362 * do want the font width/height guesses to correspond to a
363 * large font rather than a small one...
370 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
371 guess_width
= extra_width
+ font_width
* cols
;
372 guess_height
= extra_height
+ font_height
* rows
;
375 HWND w
= GetDesktopWindow();
376 GetWindowRect(w
, &r
);
377 if (guess_width
> r
.right
- r
.left
)
378 guess_width
= r
.right
- r
.left
;
379 if (guess_height
> r
.bottom
- r
.top
)
380 guess_height
= r
.bottom
- r
.top
;
384 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
387 winmode
&= ~(WS_VSCROLL
);
389 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
391 exwinmode
|= WS_EX_TOPMOST
;
393 exwinmode
|= WS_EX_CLIENTEDGE
;
394 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
395 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
396 guess_width
, guess_height
,
397 NULL
, NULL
, inst
, NULL
);
401 * Initialise the fonts, simultaneously correcting the guesses
402 * for font_{width,height}.
404 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
409 * Correct the guesses for extra_{width,height}.
413 GetWindowRect(hwnd
, &wr
);
414 GetClientRect(hwnd
, &cr
);
415 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
416 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
420 * Resize the window, now we know what size we _really_ want it
423 guess_width
= extra_width
+ font_width
* cols
;
424 guess_height
= extra_height
+ font_height
* rows
;
425 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
426 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
427 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
430 * Set up a caret bitmap, with no content.
434 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
435 bits
= smalloc(size
);
436 memset(bits
, 0, size
);
437 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
440 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
443 * Initialise the scroll bar.
448 si
.cbSize
= sizeof(si
);
449 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
454 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
458 * Start up the telnet connection.
462 char msg
[1024], *title
;
465 error
= back
->init(cfg
.host
, cfg
.port
, &realhost
);
467 sprintf(msg
, "Unable to open connection to\n"
468 "%.800s\n" "%s", cfg
.host
, error
);
469 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
472 window_name
= icon_name
= NULL
;
474 title
= cfg
.wintitle
;
476 sprintf(msg
, "%s - PuTTY", realhost
);
484 session_closed
= FALSE
;
487 * Set up the input and output buffers.
490 outbuf_reap
= outbuf_head
= 0;
493 * Prepare the mouse handler.
495 lastact
= MA_NOTHING
;
496 lastbtn
= MBT_NOTHING
;
497 dbltime
= GetDoubleClickTime();
500 * Set up the session-control options on the system menu.
503 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
507 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
508 if (cfg
.protocol
== PROT_TELNET
) {
510 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
511 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
512 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
513 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
514 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
515 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
516 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
517 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
518 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
519 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
520 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
521 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
522 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
523 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
524 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
525 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
526 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
528 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
530 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
531 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
532 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
533 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
536 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
537 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
539 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
540 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
541 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
542 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
543 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
544 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
545 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
546 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
550 * Finally show the window!
552 ShowWindow(hwnd
, show
);
555 * Open the initial log file if there is one.
560 * Set the palette up.
566 has_focus
= (GetForegroundWindow() == hwnd
);
569 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
570 int timer_id
= 0, long_timer
= 0;
572 while (msg
.message
!= WM_QUIT
) {
573 /* Sometimes DispatchMessage calls routines that use their own
574 * GetMessage loop, setup this timer so we get some control back.
576 * Also call term_update() from the timer so that if the host
577 * is sending data flat out we still do redraws.
579 if (timer_id
&& long_timer
) {
580 KillTimer(hwnd
, timer_id
);
581 long_timer
= timer_id
= 0;
584 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
585 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
586 DispatchMessage(&msg
);
588 /* Make sure we blink everything that needs it. */
591 /* Send the paste buffer if there's anything to send */
594 /* If there's nothing new in the queue then we can do everything
595 * we've delayed, reading the socket, writing, and repainting
598 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
601 if (pending_netevent
) {
602 enact_pending_netevent();
604 /* Force the cursor blink on */
607 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
611 /* Okay there is now nothing to do so we make sure the screen is
612 * completely up to date then tell windows to call us in a little
616 KillTimer(hwnd
, timer_id
);
625 /* Hmm, term_update didn't want to do an update too soon ... */
626 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
628 timer_id
= SetTimer(hwnd
, 1, 2000, NULL
);
630 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
633 /* There's no point rescanning everything in the message queue
634 * so we do an apparently unnecessary wait here
637 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
647 for (i
= 0; i
< 8; i
++)
649 DeleteObject(fonts
[i
]);
656 if (cfg
.protocol
== PROT_SSH
) {
667 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
669 char *do_select(SOCKET skt
, int startup
)
674 events
= FD_READ
| FD_WRITE
| FD_OOB
| FD_CLOSE
;
679 return "do_select(): internal error (hwnd==NULL)";
680 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
681 switch (WSAGetLastError()) {
683 return "Network is down";
685 return "WSAAsyncSelect(): unknown error";
692 * set or clear the "raw mouse message" mode
694 void set_raw_mouse_mode(int activate
)
696 send_raw_mouse
= activate
;
697 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
701 * Print a message box and close the connection.
703 void connection_fatal(char *fmt
, ...)
709 vsprintf(stuff
, fmt
, ap
);
711 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
712 if (cfg
.close_on_exit
== COE_ALWAYS
)
715 session_closed
= TRUE
;
716 SetWindowText(hwnd
, "PuTTY (inactive)");
721 * Actually do the job requested by a WM_NETEVENT
723 static void enact_pending_netevent(void)
725 static int reentering
= 0;
726 extern int select_result(WPARAM
, LPARAM
);
730 return; /* don't unpend the pending */
732 pending_netevent
= FALSE
;
735 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
738 if (ret
== 0 && !session_closed
) {
739 /* Abnormal exits will already have set session_closed and taken
740 * appropriate action. */
741 if (cfg
.close_on_exit
== COE_ALWAYS
||
742 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
744 session_closed
= TRUE
;
745 SetWindowText(hwnd
, "PuTTY (inactive)");
746 MessageBox(hwnd
, "Connection closed by remote host",
747 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
753 * Copy the colour palette from the configuration data into defpal.
754 * This is non-trivial because the colour indices are different.
756 static void cfgtopalette(void)
759 static const int ww
[] = {
760 6, 7, 8, 9, 10, 11, 12, 13,
761 14, 15, 16, 17, 18, 19, 20, 21,
762 0, 1, 2, 3, 4, 4, 5, 5
765 for (i
= 0; i
< 24; i
++) {
767 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
768 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
769 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
774 * Set up the colour palette.
776 static void init_palette(void)
779 HDC hdc
= GetDC(hwnd
);
781 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
782 logpal
= smalloc(sizeof(*logpal
)
783 - sizeof(logpal
->palPalEntry
)
784 + NCOLOURS
* sizeof(PALETTEENTRY
));
785 logpal
->palVersion
= 0x300;
786 logpal
->palNumEntries
= NCOLOURS
;
787 for (i
= 0; i
< NCOLOURS
; i
++) {
788 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
789 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
790 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
791 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
793 pal
= CreatePalette(logpal
);
795 SelectPalette(hdc
, pal
, FALSE
);
797 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
800 ReleaseDC(hwnd
, hdc
);
803 for (i
= 0; i
< NCOLOURS
; i
++)
804 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
808 for (i
= 0; i
< NCOLOURS
; i
++)
809 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
810 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
814 * Initialise all the fonts we will need. There may be as many as
815 * eight or as few as one. We also:
817 * - check the font width and height, correcting our guesses if
820 * - verify that the bold font is the same width as the ordinary
821 * one, and engage shadow bolding if not.
823 * - verify that the underlined font is the same width as the
824 * ordinary one (manual underlining by means of line drawing can
825 * be done in a pinch).
827 static void init_fonts(int pick_width
)
833 int fw_dontcare
, fw_bold
;
839 for (i
= 0; i
< 8; i
++)
842 if (cfg
.fontisbold
) {
843 fw_dontcare
= FW_BOLD
;
846 fw_dontcare
= FW_DONTCARE
;
852 font_height
= cfg
.fontheight
;
853 if (font_height
> 0) {
855 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
857 font_width
= pick_width
;
860 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
861 c, OUT_DEFAULT_PRECIS, \
862 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
863 FIXED_PITCH | FF_DONTCARE, cfg.font)
865 if (cfg
.vtmode
!= VT_OEMONLY
) {
866 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
868 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
869 GetTextMetrics(hdc
, &tm
);
870 font_height
= tm
.tmHeight
;
871 font_width
= tm
.tmAveCharWidth
;
873 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
876 * Some fonts, e.g. 9-pt Courier, draw their underlines
877 * outside their character cell. We successfully prevent
878 * screen corruption by clipping the text output, but then
879 * we lose the underline completely. Here we try to work
880 * out whether this is such a font, and if it is, we set a
881 * flag that causes underlines to be drawn by hand.
883 * Having tried other more sophisticated approaches (such
884 * as examining the TEXTMETRIC structure or requesting the
885 * height of a string), I think we'll do this the brute
886 * force way: we create a small bitmap, draw an underlined
887 * space on it, and test to see whether any pixels are
888 * foreground-coloured. (Since we expect the underline to
889 * go all the way across the character cell, we only search
890 * down a single column of the bitmap, half way across.)
894 HBITMAP und_bm
, und_oldbm
;
898 und_dc
= CreateCompatibleDC(hdc
);
899 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
900 und_oldbm
= SelectObject(und_dc
, und_bm
);
901 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
902 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
903 SetTextColor(und_dc
, RGB(255, 255, 255));
904 SetBkColor(und_dc
, RGB(0, 0, 0));
905 SetBkMode(und_dc
, OPAQUE
);
906 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
908 for (i
= 0; i
< font_height
; i
++) {
909 c
= GetPixel(und_dc
, font_width
/ 2, i
);
910 if (c
!= RGB(0, 0, 0))
913 SelectObject(und_dc
, und_oldbm
);
914 DeleteObject(und_bm
);
916 font_needs_hand_underlining
= !gotit
;
919 if (bold_mode
== BOLD_FONT
) {
920 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
921 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
924 if (cfg
.vtmode
== VT_OEMANSI
) {
925 f(FONT_OEM
, OEM_CHARSET
, fw_dontcare
, FALSE
);
926 f(FONT_OEMUND
, OEM_CHARSET
, fw_dontcare
, TRUE
);
928 if (bold_mode
== BOLD_FONT
) {
929 f(FONT_OEMBOLD
, OEM_CHARSET
, fw_bold
, FALSE
);
930 f(FONT_OEMBOLDUND
, OEM_CHARSET
, fw_bold
, TRUE
);
934 f(FONT_OEM
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
936 SelectObject(hdc
, fonts
[FONT_OEM
]);
937 GetTextMetrics(hdc
, &tm
);
938 font_height
= tm
.tmHeight
;
939 font_width
= tm
.tmAveCharWidth
;
941 f(FONT_OEMUND
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
943 if (bold_mode
== BOLD_FONT
) {
944 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
945 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
950 descent
= tm
.tmAscent
+ 1;
951 if (descent
>= font_height
)
952 descent
= font_height
- 1;
953 firstchar
= tm
.tmFirstChar
;
955 for (i
= 0; i
< 8; i
++) {
957 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
958 fsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
965 ReleaseDC(hwnd
, hdc
);
967 /* ... This is wrong in OEM only mode */
968 if (fsize
[FONT_UNDERLINE
] != fsize
[FONT_NORMAL
] ||
969 (bold_mode
== BOLD_FONT
&&
970 fsize
[FONT_BOLDUND
] != fsize
[FONT_BOLD
])) {
972 DeleteObject(fonts
[FONT_UNDERLINE
]);
973 if (bold_mode
== BOLD_FONT
)
974 DeleteObject(fonts
[FONT_BOLDUND
]);
977 if (bold_mode
== BOLD_FONT
&& fsize
[FONT_BOLD
] != fsize
[FONT_NORMAL
]) {
978 bold_mode
= BOLD_SHADOW
;
979 DeleteObject(fonts
[FONT_BOLD
]);
980 if (und_mode
== UND_FONT
)
981 DeleteObject(fonts
[FONT_BOLDUND
]);
984 /* With the fascist font painting it doesn't matter if the linedraw font
985 * isn't exactly the right size anymore so we don't have to check this.
987 if (cfg
.vtmode
== VT_OEMANSI
&& fsize
[FONT_OEM
] != fsize
[FONT_NORMAL
]) {
988 if (cfg
.fontcharset
== OEM_CHARSET
) {
989 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
990 "different sizes. Using OEM-only mode instead",
991 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
992 cfg
.vtmode
= VT_OEMONLY
;
993 } else if (firstchar
< ' ') {
994 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
995 "different sizes. Using XTerm mode instead",
996 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
997 cfg
.vtmode
= VT_XWINDOWS
;
999 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
1000 "different sizes. Using ISO8859-1 mode instead",
1001 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
1002 cfg
.vtmode
= VT_POORMAN
;
1005 for (i
= 0; i
< 8; i
++)
1007 DeleteObject(fonts
[i
]);
1013 void request_resize(int w
, int h
, int refont
)
1017 /* If the window is maximized supress resizing attempts */
1022 /* Don't do this in OEMANSI, you may get disable messages */
1023 if (refont
&& w
!= cols
&& (cols
== 80 || cols
== 132)
1024 && cfg
.vtmode
!= VT_OEMANSI
)
1026 if (refont
&& w
!= cols
&& (cols
== 80 || cols
== 132))
1029 /* If font width too big for screen should we shrink the font more ? */
1031 font_width
= ((font_width
* cols
+ w
/ 2) / w
);
1036 for (i
= 0; i
< 8; i
++)
1038 DeleteObject(fonts
[i
]);
1040 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1041 und_mode
= UND_FONT
;
1042 init_fonts(font_width
);
1044 static int first_time
= 1;
1047 switch (first_time
) {
1049 /* Get the size of the screen */
1050 if (GetClientRect(GetDesktopWindow(), &ss
))
1051 /* first_time = 0 */ ;
1057 /* Make sure the values are sane */
1058 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1059 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1072 width
= extra_width
+ font_width
* w
;
1073 height
= extra_height
+ font_height
* h
;
1075 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1076 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1077 SWP_NOMOVE
| SWP_NOZORDER
);
1080 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
)
1082 int thistime
= GetMessageTime();
1084 if (send_raw_mouse
) {
1085 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
);
1089 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1090 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1091 lastact
== MA_2CLK ? MA_3CLK
:
1092 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1097 if (lastact
!= MA_NOTHING
)
1098 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
);
1099 lasttime
= thistime
;
1103 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1104 * into a cooked one (SELECT, EXTEND, PASTE).
1106 Mouse_Button
translate_button(Mouse_Button button
)
1108 if (button
== MBT_LEFT
)
1110 if (button
== MBT_MIDDLE
)
1111 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1112 if (button
== MBT_RIGHT
)
1113 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1116 static void show_mouseptr(int show
)
1118 static int cursor_visible
= 1;
1119 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1121 if (cursor_visible
&& !show
)
1123 else if (!cursor_visible
&& show
)
1125 cursor_visible
= show
;
1128 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1129 WPARAM wParam
, LPARAM lParam
)
1132 static int ignore_size
= FALSE
;
1133 static int ignore_clip
= FALSE
;
1134 static int just_reconfigged
= FALSE
;
1135 static int resizing
= FALSE
;
1136 static int need_backend_resize
= FALSE
;
1137 static int defered_resize
= FALSE
;
1141 if (pending_netevent
)
1142 enact_pending_netevent();
1149 if (cfg
.ping_interval
> 0) {
1152 if (now
- last_movement
> cfg
.ping_interval
) {
1153 back
->special(TS_PING
);
1154 last_movement
= now
;
1162 if (!cfg
.warn_on_close
|| session_closed
||
1164 "Are you sure you want to close this session?",
1165 "PuTTY Exit Confirmation",
1166 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1167 DestroyWindow(hwnd
);
1174 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1186 PROCESS_INFORMATION pi
;
1187 HANDLE filemap
= NULL
;
1189 if (wParam
== IDM_DUPSESS
) {
1191 * Allocate a file-mapping memory chunk for the
1194 SECURITY_ATTRIBUTES sa
;
1197 sa
.nLength
= sizeof(sa
);
1198 sa
.lpSecurityDescriptor
= NULL
;
1199 sa
.bInheritHandle
= TRUE
;
1200 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1203 0, sizeof(Config
), NULL
);
1205 p
= (Config
*) MapViewOfFile(filemap
,
1207 0, 0, sizeof(Config
));
1209 *p
= cfg
; /* structure copy */
1213 sprintf(c
, "putty &%p", filemap
);
1215 } else if (wParam
== IDM_SAVEDSESS
) {
1217 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1218 cl
= smalloc(16 + strlen(session
)); /* 8, but play safe */
1220 cl
= NULL
; /* not a very important failure mode */
1222 sprintf(cl
, "putty @%s", session
);
1228 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1230 si
.lpReserved
= NULL
;
1231 si
.lpDesktop
= NULL
;
1235 si
.lpReserved2
= NULL
;
1236 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1237 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1240 CloseHandle(filemap
);
1247 int prev_alwaysontop
= cfg
.alwaysontop
;
1248 int prev_sunken_edge
= cfg
.sunken_edge
;
1249 char oldlogfile
[FILENAME_MAX
];
1251 int need_setwpos
= FALSE
;
1252 int old_fwidth
, old_fheight
;
1254 strcpy(oldlogfile
, cfg
.logfilename
);
1255 oldlogtype
= cfg
.logtype
;
1256 old_fwidth
= font_width
;
1257 old_fheight
= font_height
;
1258 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1260 if (!do_reconfig(hwnd
))
1263 if (strcmp(oldlogfile
, cfg
.logfilename
) ||
1264 oldlogtype
!= cfg
.logtype
) {
1265 logfclose(); /* reset logging */
1269 just_reconfigged
= TRUE
;
1272 for (i
= 0; i
< 8; i
++)
1274 DeleteObject(fonts
[i
]);
1276 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1277 und_mode
= UND_FONT
;
1281 * Flush the line discipline's edit buffer in the
1282 * case where local editing has just been disabled.
1284 ldisc_send(NULL
, 0);
1292 /* Enable or disable the scroll bar, etc */
1294 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1295 LONG nexflag
, exflag
=
1296 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1299 if (cfg
.alwaysontop
!= prev_alwaysontop
) {
1300 if (cfg
.alwaysontop
) {
1301 nexflag
|= WS_EX_TOPMOST
;
1302 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1303 SWP_NOMOVE
| SWP_NOSIZE
);
1305 nexflag
&= ~(WS_EX_TOPMOST
);
1306 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1307 SWP_NOMOVE
| SWP_NOSIZE
);
1310 if (cfg
.sunken_edge
)
1311 nexflag
|= WS_EX_CLIENTEDGE
;
1313 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1319 nflg
&= ~WS_VSCROLL
;
1321 nflg
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1323 nflg
|= (WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1325 if (nflg
!= flag
|| nexflag
!= exflag
) {
1329 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1330 if (nexflag
!= exflag
)
1331 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1333 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
1335 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1336 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1337 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
1338 | SWP_FRAMECHANGED
);
1340 GetWindowRect(hwnd
, &wr
);
1341 GetClientRect(hwnd
, &cr
);
1343 wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1345 wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1346 need_setwpos
= TRUE
;
1350 if (cfg
.height
!= rows
||
1351 cfg
.width
!= cols
||
1352 old_fwidth
!= font_width
||
1353 old_fheight
!= font_height
||
1354 cfg
.savelines
!= savelines
||
1355 cfg
.sunken_edge
!= prev_sunken_edge
)
1356 need_setwpos
= TRUE
;
1358 if (IsZoomed(hwnd
)) {
1362 defered_resize
= TRUE
;
1364 GetClientRect(hwnd
, &cr
);
1365 w
= cr
.right
- cr
.left
;
1366 h
= cr
.bottom
- cr
.top
;
1370 h
= h
/ font_height
;
1374 term_size(h
, w
, cfg
.savelines
);
1375 InvalidateRect(hwnd
, NULL
, TRUE
);
1378 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1379 InvalidateRect(hwnd
, NULL
, TRUE
);
1381 SetWindowPos(hwnd
, NULL
, 0, 0,
1382 extra_width
+ font_width
* cfg
.width
,
1384 font_height
* cfg
.height
,
1385 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1386 SWP_NOMOVE
| SWP_NOZORDER
);
1390 if (cfg
.locksize
&& IsZoomed(hwnd
))
1392 set_title(cfg
.wintitle
);
1393 if (IsIconic(hwnd
)) {
1395 cfg
.win_name_always ? window_name
:
1410 back
->special(TS_AYT
);
1413 back
->special(TS_BRK
);
1416 back
->special(TS_SYNCH
);
1419 back
->special(TS_EC
);
1422 back
->special(TS_EL
);
1425 back
->special(TS_GA
);
1428 back
->special(TS_NOP
);
1431 back
->special(TS_ABORT
);
1434 back
->special(TS_AO
);
1437 back
->special(TS_IP
);
1440 back
->special(TS_SUSP
);
1443 back
->special(TS_EOR
);
1446 back
->special(TS_EOF
);
1452 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1453 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1458 #define X_POS(l) ((int)(short)LOWORD(l))
1459 #define Y_POS(l) ((int)(short)HIWORD(l))
1461 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1462 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1463 #define WHEEL_DELTA 120
1466 wheel_accumulator
+= (short) HIWORD(wParam
);
1467 wParam
= LOWORD(wParam
);
1469 /* process events when the threshold is reached */
1470 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
1473 /* reduce amount for next time */
1474 if (wheel_accumulator
> 0) {
1476 wheel_accumulator
-= WHEEL_DELTA
;
1477 } else if (wheel_accumulator
< 0) {
1479 wheel_accumulator
+= WHEEL_DELTA
;
1483 if (send_raw_mouse
) {
1484 /* send a mouse-down followed by a mouse up */
1487 TO_CHR_X(X_POS(lParam
)),
1488 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1489 wParam
& MK_CONTROL
);
1490 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1491 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1492 wParam
& MK_CONTROL
);
1494 /* trigger a scroll */
1496 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
1501 case WM_LBUTTONDOWN
:
1502 case WM_MBUTTONDOWN
:
1503 case WM_RBUTTONDOWN
:
1510 case WM_LBUTTONDOWN
:
1514 case WM_MBUTTONDOWN
:
1515 button
= MBT_MIDDLE
;
1518 case WM_RBUTTONDOWN
:
1527 button
= MBT_MIDDLE
;
1538 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1539 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
);
1542 term_mouse(button
, MA_RELEASE
,
1543 TO_CHR_X(X_POS(lParam
)),
1544 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1545 wParam
& MK_CONTROL
);
1553 * Add the mouse position and message time to the random
1556 noise_ultralight(lParam
);
1558 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1560 if (wParam
& MK_LBUTTON
)
1562 else if (wParam
& MK_MBUTTON
)
1563 b
= cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1565 b
= cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1566 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1567 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1568 wParam
& MK_CONTROL
);
1571 case WM_NCMOUSEMOVE
:
1573 noise_ultralight(lParam
);
1575 case WM_IGNORE_CLIP
:
1576 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1578 case WM_DESTROYCLIPBOARD
:
1581 ignore_clip
= FALSE
;
1587 hdc
= BeginPaint(hwnd
, &p
);
1589 SelectPalette(hdc
, pal
, TRUE
);
1590 RealizePalette(hdc
);
1592 term_paint(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1593 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1594 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
1595 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
1601 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1602 * but the only one that's likely to try to overload us is FD_READ.
1603 * This means buffering just one is fine.
1605 if (pending_netevent
)
1606 enact_pending_netevent();
1608 pending_netevent
= TRUE
;
1609 pend_netevent_wParam
= wParam
;
1610 pend_netevent_lParam
= lParam
;
1611 time(&last_movement
);
1615 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
1628 case WM_IGNORE_SIZE
:
1629 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1631 case WM_ENTERSIZEMOVE
:
1634 need_backend_resize
= FALSE
;
1636 case WM_EXITSIZEMOVE
:
1639 if (need_backend_resize
)
1644 int width
, height
, w
, h
, ew
, eh
;
1645 LPRECT r
= (LPRECT
) lParam
;
1647 width
= r
->right
- r
->left
- extra_width
;
1648 height
= r
->bottom
- r
->top
- extra_height
;
1649 w
= (width
+ font_width
/ 2) / font_width
;
1652 h
= (height
+ font_height
/ 2) / font_height
;
1655 UpdateSizeTip(hwnd
, w
, h
);
1656 ew
= width
- w
* font_width
;
1657 eh
= height
- h
* font_height
;
1659 if (wParam
== WMSZ_LEFT
||
1660 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
1666 if (wParam
== WMSZ_TOP
||
1667 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
1677 /* break; (never reached) */
1679 if (wParam
== SIZE_MINIMIZED
) {
1681 cfg
.win_name_always ? window_name
: icon_name
);
1684 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1685 SetWindowText(hwnd
, window_name
);
1687 int width
, height
, w
, h
;
1688 #if 0 /* we have fixed this using WM_SIZING now */
1692 width
= LOWORD(lParam
);
1693 height
= HIWORD(lParam
);
1694 w
= width
/ font_width
;
1697 h
= height
/ font_height
;
1700 #if 0 /* we have fixed this using WM_SIZING now */
1701 ew
= width
- w
* font_width
;
1702 eh
= height
- h
* font_height
;
1703 if (ew
!= 0 || eh
!= 0) {
1705 GetWindowRect(hwnd
, &r
);
1706 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
1707 SetWindowPos(hwnd
, NULL
, 0, 0,
1708 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1709 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1712 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1714 term_size(h
, w
, cfg
.savelines
);
1716 * Don't call back->size in mid-resize. (To prevent
1717 * massive numbers of resize events getting sent
1718 * down the connection during an NT opaque drag.)
1723 need_backend_resize
= TRUE
;
1727 just_reconfigged
= FALSE
;
1730 if (wParam
== SIZE_RESTORED
&& defered_resize
) {
1731 defered_resize
= FALSE
;
1732 SetWindowPos(hwnd
, NULL
, 0, 0,
1733 extra_width
+ font_width
* cfg
.width
,
1734 extra_height
+ font_height
* cfg
.height
,
1735 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1736 SWP_NOMOVE
| SWP_NOZORDER
);
1738 ignore_size
= FALSE
;
1741 switch (LOWORD(wParam
)) {
1755 term_scroll(0, +rows
/ 2);
1758 term_scroll(0, -rows
/ 2);
1760 case SB_THUMBPOSITION
:
1762 term_scroll(1, HIWORD(wParam
));
1766 case WM_PALETTECHANGED
:
1767 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1768 HDC hdc
= get_ctx();
1770 if (RealizePalette(hdc
) > 0)
1776 case WM_QUERYNEWPALETTE
:
1778 HDC hdc
= get_ctx();
1780 if (RealizePalette(hdc
) > 0)
1792 * Add the scan code and keypress timing to the random
1795 noise_ultralight(lParam
);
1798 * We don't do TranslateMessage since it disassociates the
1799 * resulting CHAR message from the KEYDOWN that sparked it,
1800 * which we occasionally don't want. Instead, we process
1801 * KEYDOWN, and call the Win32 translator functions so that
1802 * we get the translations under _our_ control.
1805 unsigned char buf
[20];
1808 if (wParam
== VK_PROCESSKEY
) {
1811 m
.message
= WM_KEYDOWN
;
1813 m
.lParam
= lParam
& 0xdfff;
1814 TranslateMessage(&m
);
1816 len
= TranslateKey(message
, wParam
, lParam
, buf
);
1818 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1819 ldisc_send(buf
, len
);
1828 unsigned char buf
[2];
1831 buf
[0] = wParam
>> 8;
1837 * Nevertheless, we are prepared to deal with WM_CHAR
1838 * messages, should they crop up. So if someone wants to
1839 * post the things to us as part of a macro manoeuvre,
1840 * we're ready to cope.
1843 char c
= xlat_kbd2tty((unsigned char) wParam
);
1848 if (send_raw_mouse
) {
1849 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
1854 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1858 * Move the system caret. (We maintain one, even though it's
1859 * invisible, for the benefit of blind people: apparently some
1860 * helper software tracks the system caret, so we should arrange to
1863 void sys_cursor(int x
, int y
)
1866 SetCaretPos(x
* font_width
, y
* font_height
);
1870 * Draw a line of text in the window, at given character
1871 * coordinates, in given attributes.
1873 * We are allowed to fiddle with the contents of `text'.
1875 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
1876 unsigned long attr
, int lattr
)
1879 int nfg
, nbg
, nfont
;
1882 int force_manual_underline
= 0;
1883 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
1884 static int *IpDx
= 0, IpDxLEN
= 0;;
1886 if (len
> IpDxLEN
|| IpDx
[0] != fnt_width
) {
1888 if (len
> IpDxLEN
) {
1890 IpDx
= smalloc((len
+ 16) * sizeof(int));
1891 IpDxLEN
= (len
+ 16);
1893 for (i
= 0; i
< IpDxLEN
; i
++)
1894 IpDx
[i
] = fnt_width
;
1900 if ((attr
& ATTR_ACTCURS
) && cfg
.cursor_type
== 0) {
1901 attr
&= (bold_mode
== BOLD_COLOURS ?
0x300200 : 0x300300);
1902 attr
^= ATTR_CUR_XOR
;
1906 if (cfg
.vtmode
== VT_OEMONLY
)
1910 * Map high-half characters in order to approximate ISO using
1911 * OEM character set. No characters are missing if the OEM codepage
1914 if (nfont
& FONT_OEM
) {
1916 for (i
= 0; i
< len
; i
++)
1917 if (text
[i
] >= '\xA0' && text
[i
] <= '\xFF') {
1919 /* This is CP850 ... perfect translation */
1920 static const char oemhighhalf
[] = "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1921 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1922 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1923 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1924 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1925 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1926 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1927 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1928 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1929 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1930 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1931 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1934 /* This is CP437 ... junk translation */
1935 static const unsigned char oemhighhalf
[] = {
1936 0x20, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1937 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1938 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1939 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1940 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1941 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1942 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1943 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1944 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1945 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1946 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1947 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1950 text
[i
] = oemhighhalf
[(unsigned char) text
[i
] - 0xA0];
1954 if (attr
& ATTR_LINEDRW
) {
1957 static const char poorman
[] =
1958 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1961 static const char oemmap_437
[] =
1962 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1963 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1966 static const char oemmap_850
[] =
1967 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1968 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1970 /* Poor windows font ... eg: windows courier */
1971 static const char oemmap
[] =
1972 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1973 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1976 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1977 * VT100 line drawing chars; everything else stays normal.
1979 * Actually '_' maps to space too, but that's done before.
1981 switch (cfg
.vtmode
) {
1983 for (i
= 0; i
< len
; i
++)
1984 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1985 text
[i
] += '\x01' - '\x60';
1988 /* Make sure we actually have an OEM font */
1989 if (fonts
[nfont
| FONT_OEM
]) {
1992 for (i
= 0; i
< len
; i
++)
1993 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1994 text
[i
] = oemmap
[(unsigned char) text
[i
] - 0x60];
1998 for (i
= 0; i
< len
; i
++)
1999 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
2000 text
[i
] = poorman
[(unsigned char) text
[i
] - 0x60];
2005 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2006 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2007 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2009 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2010 nfont
|= FONT_UNDERLINE
;
2011 if (!fonts
[nfont
]) {
2012 if (nfont
& FONT_UNDERLINE
)
2013 force_manual_underline
= 1;
2014 /* Don't do the same for manual bold, it could be bad news. */
2016 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2018 if (font_needs_hand_underlining
&& (attr
& ATTR_UNDER
))
2019 force_manual_underline
= 1;
2020 if (attr
& ATTR_REVERSE
) {
2025 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2027 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2031 SelectObject(hdc
, fonts
[nfont
]);
2032 SetTextColor(hdc
, fg
);
2033 SetBkColor(hdc
, bg
);
2034 SetBkMode(hdc
, OPAQUE
);
2037 line_box
.right
= x
+ fnt_width
* len
;
2038 line_box
.bottom
= y
+ font_height
;
2039 ExtTextOut(hdc
, x
, y
, ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
,
2041 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2042 SetBkMode(hdc
, TRANSPARENT
);
2044 /* GRR: This draws the character outside it's box and can leave
2045 * 'droppings' even with the clip box! I suppose I could loop it
2046 * one character at a time ... yuk.
2048 * Or ... I could do a test print with "W", and use +1 or -1 for this
2049 * shift depending on if the leftmost column is blank...
2051 ExtTextOut(hdc
, x
- 1, y
, ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2053 if (force_manual_underline
||
2054 (und_mode
== UND_LINE
&& (attr
& ATTR_UNDER
))) {
2056 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2057 MoveToEx(hdc
, x
, y
+ descent
, NULL
);
2058 LineTo(hdc
, x
+ len
* fnt_width
, y
+ descent
);
2059 oldpen
= SelectObject(hdc
, oldpen
);
2060 DeleteObject(oldpen
);
2062 if ((attr
& ATTR_PASCURS
) && cfg
.cursor_type
== 0) {
2065 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2066 pts
[2].x
= pts
[3].x
= x
+ fnt_width
- 1;
2067 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2068 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2069 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2070 Polyline(hdc
, pts
, 5);
2071 oldpen
= SelectObject(hdc
, oldpen
);
2072 DeleteObject(oldpen
);
2074 if ((attr
& (ATTR_ACTCURS
| ATTR_PASCURS
)) && cfg
.cursor_type
!= 0) {
2075 int startx
, starty
, dx
, dy
, length
, i
;
2076 if (cfg
.cursor_type
== 1) {
2078 starty
= y
+ descent
;
2084 if (attr
& ATTR_RIGHTCURS
)
2085 xadjust
= fnt_width
- 1;
2086 startx
= x
+ xadjust
;
2090 length
= font_height
;
2092 if (attr
& ATTR_ACTCURS
) {
2095 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2096 MoveToEx(hdc
, startx
, starty
, NULL
);
2097 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2098 oldpen
= SelectObject(hdc
, oldpen
);
2099 DeleteObject(oldpen
);
2101 for (i
= 0; i
< length
; i
++) {
2103 SetPixel(hdc
, startx
, starty
, colours
[23]);
2112 static int check_compose(int first
, int second
)
2115 static char *composetbl
[] = {
2116 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡",
2118 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§",
2120 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
2121 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·",
2123 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ",
2125 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
2126 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
2127 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
2128 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ",
2130 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð",
2132 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú",
2134 "\"uü", "'yý", "htþ", "\"yÿ",
2139 static int recurse
= 0;
2142 for (c
= composetbl
; *c
; c
++) {
2143 if ((*c
)[0] == first
&& (*c
)[1] == second
) {
2144 return (*c
)[2] & 0xFF;
2150 nc
= check_compose(second
, first
);
2152 nc
= check_compose(toupper(first
), toupper(second
));
2154 nc
= check_compose(toupper(second
), toupper(first
));
2162 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2163 * codes. Returns number of bytes used or zero to drop the message
2164 * or -1 to forward the message to windows.
2166 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2167 unsigned char *output
)
2170 int scan
, left_alt
= 0, key_down
, shift_state
;
2172 unsigned char *p
= output
;
2173 static int alt_state
= 0;
2175 HKL kbd_layout
= GetKeyboardLayout(0);
2177 static WORD keys
[3];
2178 static int compose_char
= 0;
2179 static WPARAM compose_key
= 0;
2181 r
= GetKeyboardState(keystate
);
2183 memset(keystate
, 0, sizeof(keystate
));
2186 { /* Tell us all about key events */
2187 static BYTE oldstate
[256];
2188 static int first
= 1;
2192 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2195 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2197 } else if ((HIWORD(lParam
) & KF_UP
)
2198 && scan
== (HIWORD(lParam
) & 0xFF)) {
2202 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2203 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2216 debug(("VK_%02x", wParam
));
2218 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2220 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2222 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2223 if (ch
>= ' ' && ch
<= '~')
2224 debug((", '%c'", ch
));
2226 debug((", $%02x", ch
));
2229 debug((", KB0=%02x", keys
[0]));
2231 debug((", KB1=%02x", keys
[1]));
2233 debug((", KB2=%02x", keys
[2]));
2235 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2237 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2239 if ((HIWORD(lParam
) & KF_EXTENDED
))
2241 if ((HIWORD(lParam
) & KF_UP
))
2245 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2246 else if ((HIWORD(lParam
) & KF_UP
))
2247 oldstate
[wParam
& 0xFF] ^= 0x80;
2249 oldstate
[wParam
& 0xFF] ^= 0x81;
2251 for (ch
= 0; ch
< 256; ch
++)
2252 if (oldstate
[ch
] != keystate
[ch
])
2253 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2255 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2259 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2260 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2264 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2265 if ((cfg
.funky_type
== 3 ||
2266 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2267 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2269 wParam
= VK_EXECUTE
;
2271 /* UnToggle NUMLock */
2272 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2273 keystate
[VK_NUMLOCK
] ^= 1;
2276 /* And write back the 'adjusted' state */
2277 SetKeyboardState(keystate
);
2280 /* Disable Auto repeat if required */
2281 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2284 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2287 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2289 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2290 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2291 if (cfg
.ctrlaltkeys
)
2292 keystate
[VK_MENU
] = 0;
2294 keystate
[VK_RMENU
] = 0x80;
2299 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2300 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2301 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2303 /* Note if AltGr was pressed and if it was used as a compose key */
2304 if (!compose_state
) {
2305 compose_key
= 0x100;
2306 if (cfg
.compose_key
) {
2307 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2308 compose_key
= wParam
;
2310 if (wParam
== VK_APPS
)
2311 compose_key
= wParam
;
2314 if (wParam
== compose_key
) {
2315 if (compose_state
== 0
2316 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2318 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2322 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
2326 * Record that we pressed key so the scroll window can be reset, but
2327 * be careful to avoid Shift-UP/Down
2329 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
2333 /* Make sure we're not pasting */
2337 if (compose_state
> 1 && left_alt
)
2340 /* Sanitize the number pad if not using a PC NumPad */
2341 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2342 && cfg
.funky_type
!= 2)
2343 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
2344 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
2348 nParam
= VK_NUMPAD0
;
2351 nParam
= VK_NUMPAD1
;
2354 nParam
= VK_NUMPAD2
;
2357 nParam
= VK_NUMPAD3
;
2360 nParam
= VK_NUMPAD4
;
2363 nParam
= VK_NUMPAD5
;
2366 nParam
= VK_NUMPAD6
;
2369 nParam
= VK_NUMPAD7
;
2372 nParam
= VK_NUMPAD8
;
2375 nParam
= VK_NUMPAD9
;
2378 nParam
= VK_DECIMAL
;
2382 if (keystate
[VK_NUMLOCK
] & 1)
2389 /* If a key is pressed and AltGr is not active */
2390 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
2391 /* Okay, prepare for most alts then ... */
2395 /* Lets see if it's a pattern we know all about ... */
2396 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2397 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2400 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2401 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
2404 if (wParam
== VK_INSERT
&& shift_state
== 1) {
2405 term_mouse(MBT_PASTE
, MA_CLICK
, 0, 0, 0, 0);
2406 term_mouse(MBT_PASTE
, MA_RELEASE
, 0, 0, 0, 0);
2409 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
2412 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
2414 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
2415 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
2418 /* Control-Numlock for app-keypad mode switch */
2419 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
2420 app_keypad_keys
^= 1;
2424 /* Nethack keypad */
2425 if (cfg
.nethack_keypad
&& !left_alt
) {
2428 *p
++ = shift_state ?
'B' : 'b';
2431 *p
++ = shift_state ?
'J' : 'j';
2434 *p
++ = shift_state ?
'N' : 'n';
2437 *p
++ = shift_state ?
'H' : 'h';
2440 *p
++ = shift_state ?
'.' : '.';
2443 *p
++ = shift_state ?
'L' : 'l';
2446 *p
++ = shift_state ?
'Y' : 'y';
2449 *p
++ = shift_state ?
'K' : 'k';
2452 *p
++ = shift_state ?
'U' : 'u';
2457 /* Application Keypad */
2461 if (cfg
.funky_type
== 3 ||
2462 (cfg
.funky_type
<= 1 &&
2463 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
2477 if (app_keypad_keys
&& !cfg
.no_applic_k
)
2514 if (cfg
.funky_type
== 2) {
2519 } else if (shift_state
)
2526 if (cfg
.funky_type
== 2)
2530 if (cfg
.funky_type
== 2)
2534 if (cfg
.funky_type
== 2)
2539 if (HIWORD(lParam
) & KF_EXTENDED
)
2545 if (xkey
>= 'P' && xkey
<= 'S')
2546 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
2548 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
2550 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
2555 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
2556 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
2560 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
2566 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
2570 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
2574 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
2579 if (wParam
== VK_PAUSE
) { /* Break/Pause */
2584 /* Control-2 to Control-8 are special */
2585 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
2586 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
2589 if (shift_state
== 2 && wParam
== 0xBD) {
2593 if (shift_state
== 2 && wParam
== 0xDF) {
2597 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
2604 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2605 * for integer decimal nn.)
2607 * We also deal with the weird ones here. Linux VCs replace F1
2608 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2609 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2615 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
2618 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
2621 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
2624 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
2627 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
2630 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
2633 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
2636 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
2639 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
2642 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
2693 /* Reorder edit keys to physical order */
2694 if (cfg
.funky_type
== 3 && code
<= 6)
2695 code
= "\0\2\1\4\5\3\6"[code
];
2697 if (vt52_mode
&& code
> 0 && code
<= 6) {
2698 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
2702 if (cfg
.funky_type
== 5 && code
>= 11 && code
<= 24) {
2703 p
+= sprintf((char *) p
, "\x1B[%c", code
+ 'M' - 11);
2706 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
2713 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
2716 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
2719 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
2720 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
2723 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
2725 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
2727 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
2730 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
2731 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
2735 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
2740 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2741 * some reason seems to send VK_CLEAR to Windows...).
2764 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
2766 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
2767 /* VT100 & VT102 manuals both state the app cursor keys
2768 * only work if the app keypad is on.
2770 if (!app_keypad_keys
)
2772 /* Useful mapping of Ctrl-arrows */
2773 if (shift_state
== 2)
2777 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
2779 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
2786 * Finally, deal with Return ourselves. (Win95 seems to
2787 * foul it up when Alt is pressed, for some reason.)
2789 if (wParam
== VK_RETURN
) { /* Return */
2796 /* Okay we've done everything interesting; let windows deal with
2797 * the boring stuff */
2799 BOOL capsOn
= keystate
[VK_CAPITAL
] != 0;
2801 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2802 if (cfg
.xlat_capslockcyr
)
2803 keystate
[VK_CAPITAL
] = 0;
2805 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
2808 for (i
= 0; i
< r
; i
++) {
2809 unsigned char ch
= (unsigned char) keys
[i
];
2811 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
2816 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
2820 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
2821 MessageBeep(MB_ICONHAND
);
2824 *p
++ = xlat_kbd2tty((unsigned char) nc
);
2830 if (left_alt
&& key_down
)
2836 ch
= xlat_latkbd2win(ch
);
2837 *p
++ = xlat_kbd2tty(ch
);
2841 /* This is so the ALT-Numpad and dead keys work correctly. */
2846 /* If we're definitly not building up an ALT-54321 then clear it */
2851 /* ALT alone may or may not want to bring up the System menu */
2852 if (wParam
== VK_MENU
) {
2854 if (message
== WM_SYSKEYDOWN
)
2856 else if (message
== WM_SYSKEYUP
&& alt_state
)
2857 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
2858 if (message
== WM_SYSKEYUP
)
2868 void set_title(char *title
)
2871 window_name
= smalloc(1 + strlen(title
));
2872 strcpy(window_name
, title
);
2873 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
2874 SetWindowText(hwnd
, title
);
2877 void set_icon(char *title
)
2880 icon_name
= smalloc(1 + strlen(title
));
2881 strcpy(icon_name
, title
);
2882 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
2883 SetWindowText(hwnd
, title
);
2886 void set_sbar(int total
, int start
, int page
)
2893 si
.cbSize
= sizeof(si
);
2894 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
2896 si
.nMax
= total
- 1;
2900 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
2903 Context
get_ctx(void)
2909 SelectPalette(hdc
, pal
, FALSE
);
2915 void free_ctx(Context ctx
)
2917 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
2918 ReleaseDC(hwnd
, ctx
);
2921 static void real_palette_set(int n
, int r
, int g
, int b
)
2924 logpal
->palPalEntry
[n
].peRed
= r
;
2925 logpal
->palPalEntry
[n
].peGreen
= g
;
2926 logpal
->palPalEntry
[n
].peBlue
= b
;
2927 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
2928 colours
[n
] = PALETTERGB(r
, g
, b
);
2929 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2931 colours
[n
] = RGB(r
, g
, b
);
2934 void palette_set(int n
, int r
, int g
, int b
)
2936 static const int first
[21] = {
2937 0, 2, 4, 6, 8, 10, 12, 14,
2938 1, 3, 5, 7, 9, 11, 13, 15,
2941 real_palette_set(first
[n
], r
, g
, b
);
2943 real_palette_set(first
[n
] + 1, r
, g
, b
);
2945 HDC hdc
= get_ctx();
2946 UnrealizeObject(pal
);
2947 RealizePalette(hdc
);
2952 void palette_reset(void)
2956 for (i
= 0; i
< NCOLOURS
; i
++) {
2958 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
2959 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
2960 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
2961 logpal
->palPalEntry
[i
].peFlags
= 0;
2962 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
2963 defpal
[i
].rgbtGreen
,
2964 defpal
[i
].rgbtBlue
);
2966 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
2967 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
2972 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2974 RealizePalette(hdc
);
2979 void write_clip(void *data
, int len
, int must_deselect
)
2984 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
2987 lock
= GlobalLock(clipdata
);
2990 memcpy(lock
, data
, len
);
2991 ((unsigned char *) lock
)[len
] = 0;
2992 GlobalUnlock(clipdata
);
2995 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
2997 if (OpenClipboard(hwnd
)) {
2999 SetClipboardData(CF_TEXT
, clipdata
);
3002 GlobalFree(clipdata
);
3005 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3008 void get_clip(void **p
, int *len
)
3010 static HGLOBAL clipdata
= NULL
;
3014 GlobalUnlock(clipdata
);
3018 if (OpenClipboard(NULL
)) {
3019 clipdata
= GetClipboardData(CF_TEXT
);
3022 *p
= GlobalLock(clipdata
);
3036 * Move `lines' lines from position `from' to position `to' in the
3039 void optimised_move(int to
, int from
, int lines
)
3044 min
= (to
< from ? to
: from
);
3045 max
= to
+ from
- min
;
3048 r
.right
= cols
* font_width
;
3049 r
.top
= min
* font_height
;
3050 r
.bottom
= (max
+ lines
) * font_height
;
3051 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
3055 * Print a message box and perform a fatal exit.
3057 void fatalbox(char *fmt
, ...)
3063 vsprintf(stuff
, fmt
, ap
);
3065 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
3074 if (mode
== BELL_DEFAULT
) {
3076 * For MessageBeep style bells, we want to be careful of
3077 * timing, because they don't have the nice property of
3078 * PlaySound bells that each one cancels the previous
3079 * active one. So we limit the rate to one per 50ms or so.
3081 static long lastbeep
= 0;
3084 beepdiff
= GetTickCount() - lastbeep
;
3085 if (beepdiff
>= 0 && beepdiff
< 50)
3089 * The above MessageBeep call takes time, so we record the
3090 * time _after_ it finishes rather than before it starts.
3092 lastbeep
= GetTickCount();
3093 } else if (mode
== BELL_WAVEFILE
) {
3094 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
3095 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
3096 sprintf(buf
, "Unable to play sound file\n%s\n"
3097 "Using default sound instead", cfg
.bell_wavefile
);
3098 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
3099 MB_OK
| MB_ICONEXCLAMATION
);
3100 cfg
.beep
= BELL_DEFAULT
;