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));
305 * Select protocol. This is farmed out into a table in a
306 * separate file to enable an ssh-free variant.
311 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
312 if (backends
[i
].protocol
== cfg
.protocol
) {
313 back
= backends
[i
].backend
;
317 MessageBox(NULL
, "Unsupported protocol number found",
318 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
324 /* Check for invalid Port number (i.e. zero) */
326 MessageBox(NULL
, "Invalid Port Number",
327 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
334 wndclass
.lpfnWndProc
= WndProc
;
335 wndclass
.cbClsExtra
= 0;
336 wndclass
.cbWndExtra
= 0;
337 wndclass
.hInstance
= inst
;
338 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
339 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
340 wndclass
.hbrBackground
= GetStockObject(BLACK_BRUSH
);
341 wndclass
.lpszMenuName
= NULL
;
342 wndclass
.lpszClassName
= appname
;
344 RegisterClass(&wndclass
);
349 savelines
= cfg
.savelines
;
355 * Guess some defaults for the window size. This all gets
356 * updated later, so we don't really care too much. However, we
357 * do want the font width/height guesses to correspond to a
358 * large font rather than a small one...
365 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
366 guess_width
= extra_width
+ font_width
* cols
;
367 guess_height
= extra_height
+ font_height
* rows
;
370 HWND w
= GetDesktopWindow();
371 GetWindowRect(w
, &r
);
372 if (guess_width
> r
.right
- r
.left
)
373 guess_width
= r
.right
- r
.left
;
374 if (guess_height
> r
.bottom
- r
.top
)
375 guess_height
= r
.bottom
- r
.top
;
379 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
382 winmode
&= ~(WS_VSCROLL
);
384 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
386 exwinmode
|= WS_EX_TOPMOST
;
388 exwinmode
|= WS_EX_CLIENTEDGE
;
389 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
390 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
391 guess_width
, guess_height
,
392 NULL
, NULL
, inst
, NULL
);
396 * Initialise the fonts, simultaneously correcting the guesses
397 * for font_{width,height}.
399 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
404 * Correct the guesses for extra_{width,height}.
408 GetWindowRect(hwnd
, &wr
);
409 GetClientRect(hwnd
, &cr
);
410 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
411 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
415 * Resize the window, now we know what size we _really_ want it
418 guess_width
= extra_width
+ font_width
* cols
;
419 guess_height
= extra_height
+ font_height
* rows
;
420 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
421 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
422 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
425 * Set up a caret bitmap, with no content.
429 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
430 bits
= smalloc(size
);
431 memset(bits
, 0, size
);
432 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
435 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
438 * Initialise the scroll bar.
443 si
.cbSize
= sizeof(si
);
444 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
449 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
453 * Start up the telnet connection.
457 char msg
[1024], *title
;
460 error
= back
->init(cfg
.host
, cfg
.port
, &realhost
);
462 sprintf(msg
, "Unable to open connection to\n"
463 "%.800s\n" "%s", cfg
.host
, error
);
464 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
467 window_name
= icon_name
= NULL
;
469 title
= cfg
.wintitle
;
471 sprintf(msg
, "%s - PuTTY", realhost
);
478 session_closed
= FALSE
;
481 * Set up the input and output buffers.
484 outbuf_reap
= outbuf_head
= 0;
487 * Prepare the mouse handler.
489 lastact
= MA_NOTHING
;
490 lastbtn
= MBT_NOTHING
;
491 dbltime
= GetDoubleClickTime();
494 * Set up the session-control options on the system menu.
497 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
501 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
502 if (cfg
.protocol
== PROT_TELNET
) {
504 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
505 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
506 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
507 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
508 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
509 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
510 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
511 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
512 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
513 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
514 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
515 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
516 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
517 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
518 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
519 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
520 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
522 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
524 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
525 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
526 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
527 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
530 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
531 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
533 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
534 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
535 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
536 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
537 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
538 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
539 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
540 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
544 * Finally show the window!
546 ShowWindow(hwnd
, show
);
549 * Open the initial log file if there is one.
554 * Set the palette up.
560 has_focus
= (GetForegroundWindow() == hwnd
);
563 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
564 int timer_id
= 0, long_timer
= 0;
566 while (msg
.message
!= WM_QUIT
) {
567 /* Sometimes DispatchMessage calls routines that use their own
568 * GetMessage loop, setup this timer so we get some control back.
570 * Also call term_update() from the timer so that if the host
571 * is sending data flat out we still do redraws.
573 if (timer_id
&& long_timer
) {
574 KillTimer(hwnd
, timer_id
);
575 long_timer
= timer_id
= 0;
578 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
579 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
580 DispatchMessage(&msg
);
582 /* Make sure we blink everything that needs it. */
585 /* Send the paste buffer if there's anything to send */
588 /* If there's nothing new in the queue then we can do everything
589 * we've delayed, reading the socket, writing, and repainting
592 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
595 if (pending_netevent
) {
596 enact_pending_netevent();
598 /* Force the cursor blink on */
601 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
605 /* Okay there is now nothing to do so we make sure the screen is
606 * completely up to date then tell windows to call us in a little
610 KillTimer(hwnd
, timer_id
);
619 /* Hmm, term_update didn't want to do an update too soon ... */
620 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
622 timer_id
= SetTimer(hwnd
, 1, 2000, NULL
);
624 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
627 /* There's no point rescanning everything in the message queue
628 * so we do an apparently unnecessary wait here
631 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
641 for (i
= 0; i
< 8; i
++)
643 DeleteObject(fonts
[i
]);
650 if (cfg
.protocol
== PROT_SSH
) {
661 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
663 char *do_select(SOCKET skt
, int startup
)
668 events
= FD_READ
| FD_WRITE
| FD_OOB
| FD_CLOSE
;
673 return "do_select(): internal error (hwnd==NULL)";
674 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
675 switch (WSAGetLastError()) {
677 return "Network is down";
679 return "WSAAsyncSelect(): unknown error";
686 * set or clear the "raw mouse message" mode
688 void set_raw_mouse_mode(int activate
)
690 send_raw_mouse
= activate
;
691 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
695 * Print a message box and close the connection.
697 void connection_fatal(char *fmt
, ...)
703 vsprintf(stuff
, fmt
, ap
);
705 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
706 if (cfg
.close_on_exit
== COE_ALWAYS
)
709 session_closed
= TRUE
;
710 SetWindowText(hwnd
, "PuTTY (inactive)");
715 * Actually do the job requested by a WM_NETEVENT
717 static void enact_pending_netevent(void)
719 static int reentering
= 0;
720 extern int select_result(WPARAM
, LPARAM
);
724 return; /* don't unpend the pending */
726 pending_netevent
= FALSE
;
729 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
732 if (ret
== 0 && !session_closed
) {
733 /* Abnormal exits will already have set session_closed and taken
734 * appropriate action. */
735 if (cfg
.close_on_exit
== COE_ALWAYS
||
736 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
738 session_closed
= TRUE
;
739 SetWindowText(hwnd
, "PuTTY (inactive)");
740 MessageBox(hwnd
, "Connection closed by remote host",
741 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
747 * Copy the colour palette from the configuration data into defpal.
748 * This is non-trivial because the colour indices are different.
750 static void cfgtopalette(void)
753 static const int ww
[] = {
754 6, 7, 8, 9, 10, 11, 12, 13,
755 14, 15, 16, 17, 18, 19, 20, 21,
756 0, 1, 2, 3, 4, 4, 5, 5
759 for (i
= 0; i
< 24; i
++) {
761 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
762 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
763 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
768 * Set up the colour palette.
770 static void init_palette(void)
773 HDC hdc
= GetDC(hwnd
);
775 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
776 logpal
= smalloc(sizeof(*logpal
)
777 - sizeof(logpal
->palPalEntry
)
778 + NCOLOURS
* sizeof(PALETTEENTRY
));
779 logpal
->palVersion
= 0x300;
780 logpal
->palNumEntries
= NCOLOURS
;
781 for (i
= 0; i
< NCOLOURS
; i
++) {
782 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
783 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
784 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
785 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
787 pal
= CreatePalette(logpal
);
789 SelectPalette(hdc
, pal
, FALSE
);
791 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
794 ReleaseDC(hwnd
, hdc
);
797 for (i
= 0; i
< NCOLOURS
; i
++)
798 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
802 for (i
= 0; i
< NCOLOURS
; i
++)
803 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
804 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
808 * Initialise all the fonts we will need. There may be as many as
809 * eight or as few as one. We also:
811 * - check the font width and height, correcting our guesses if
814 * - verify that the bold font is the same width as the ordinary
815 * one, and engage shadow bolding if not.
817 * - verify that the underlined font is the same width as the
818 * ordinary one (manual underlining by means of line drawing can
819 * be done in a pinch).
821 static void init_fonts(int pick_width
)
827 int fw_dontcare
, fw_bold
;
833 for (i
= 0; i
< 8; i
++)
836 if (cfg
.fontisbold
) {
837 fw_dontcare
= FW_BOLD
;
840 fw_dontcare
= FW_DONTCARE
;
846 font_height
= cfg
.fontheight
;
847 if (font_height
> 0) {
849 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
851 font_width
= pick_width
;
854 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
855 c, OUT_DEFAULT_PRECIS, \
856 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
857 FIXED_PITCH | FF_DONTCARE, cfg.font)
859 if (cfg
.vtmode
!= VT_OEMONLY
) {
860 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
862 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
863 GetTextMetrics(hdc
, &tm
);
864 font_height
= tm
.tmHeight
;
865 font_width
= tm
.tmAveCharWidth
;
867 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
870 * Some fonts, e.g. 9-pt Courier, draw their underlines
871 * outside their character cell. We successfully prevent
872 * screen corruption by clipping the text output, but then
873 * we lose the underline completely. Here we try to work
874 * out whether this is such a font, and if it is, we set a
875 * flag that causes underlines to be drawn by hand.
877 * Having tried other more sophisticated approaches (such
878 * as examining the TEXTMETRIC structure or requesting the
879 * height of a string), I think we'll do this the brute
880 * force way: we create a small bitmap, draw an underlined
881 * space on it, and test to see whether any pixels are
882 * foreground-coloured. (Since we expect the underline to
883 * go all the way across the character cell, we only search
884 * down a single column of the bitmap, half way across.)
888 HBITMAP und_bm
, und_oldbm
;
892 und_dc
= CreateCompatibleDC(hdc
);
893 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
894 und_oldbm
= SelectObject(und_dc
, und_bm
);
895 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
896 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
897 SetTextColor(und_dc
, RGB(255, 255, 255));
898 SetBkColor(und_dc
, RGB(0, 0, 0));
899 SetBkMode(und_dc
, OPAQUE
);
900 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
902 for (i
= 0; i
< font_height
; i
++) {
903 c
= GetPixel(und_dc
, font_width
/ 2, i
);
904 if (c
!= RGB(0, 0, 0))
907 SelectObject(und_dc
, und_oldbm
);
908 DeleteObject(und_bm
);
910 font_needs_hand_underlining
= !gotit
;
913 if (bold_mode
== BOLD_FONT
) {
914 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
915 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
918 if (cfg
.vtmode
== VT_OEMANSI
) {
919 f(FONT_OEM
, OEM_CHARSET
, fw_dontcare
, FALSE
);
920 f(FONT_OEMUND
, OEM_CHARSET
, fw_dontcare
, TRUE
);
922 if (bold_mode
== BOLD_FONT
) {
923 f(FONT_OEMBOLD
, OEM_CHARSET
, fw_bold
, FALSE
);
924 f(FONT_OEMBOLDUND
, OEM_CHARSET
, fw_bold
, TRUE
);
928 f(FONT_OEM
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
930 SelectObject(hdc
, fonts
[FONT_OEM
]);
931 GetTextMetrics(hdc
, &tm
);
932 font_height
= tm
.tmHeight
;
933 font_width
= tm
.tmAveCharWidth
;
935 f(FONT_OEMUND
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
937 if (bold_mode
== BOLD_FONT
) {
938 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
939 f(FONT_BOLDUND
, cfg
.fontcharset
, fw_bold
, TRUE
);
944 descent
= tm
.tmAscent
+ 1;
945 if (descent
>= font_height
)
946 descent
= font_height
- 1;
947 firstchar
= tm
.tmFirstChar
;
949 for (i
= 0; i
< 8; i
++) {
951 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
952 fsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
959 ReleaseDC(hwnd
, hdc
);
961 /* ... This is wrong in OEM only mode */
962 if (fsize
[FONT_UNDERLINE
] != fsize
[FONT_NORMAL
] ||
963 (bold_mode
== BOLD_FONT
&&
964 fsize
[FONT_BOLDUND
] != fsize
[FONT_BOLD
])) {
966 DeleteObject(fonts
[FONT_UNDERLINE
]);
967 if (bold_mode
== BOLD_FONT
)
968 DeleteObject(fonts
[FONT_BOLDUND
]);
971 if (bold_mode
== BOLD_FONT
&& fsize
[FONT_BOLD
] != fsize
[FONT_NORMAL
]) {
972 bold_mode
= BOLD_SHADOW
;
973 DeleteObject(fonts
[FONT_BOLD
]);
974 if (und_mode
== UND_FONT
)
975 DeleteObject(fonts
[FONT_BOLDUND
]);
978 /* With the fascist font painting it doesn't matter if the linedraw font
979 * isn't exactly the right size anymore so we don't have to check this.
981 if (cfg
.vtmode
== VT_OEMANSI
&& fsize
[FONT_OEM
] != fsize
[FONT_NORMAL
]) {
982 if (cfg
.fontcharset
== OEM_CHARSET
) {
983 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
984 "different sizes. Using OEM-only mode instead",
985 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
986 cfg
.vtmode
= VT_OEMONLY
;
987 } else if (firstchar
< ' ') {
988 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
989 "different sizes. Using XTerm mode instead",
990 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
991 cfg
.vtmode
= VT_XWINDOWS
;
993 MessageBox(NULL
, "The OEM and ANSI versions of this font are\n"
994 "different sizes. Using ISO8859-1 mode instead",
995 "Font Size Mismatch", MB_ICONINFORMATION
| MB_OK
);
996 cfg
.vtmode
= VT_POORMAN
;
999 for (i
= 0; i
< 8; i
++)
1001 DeleteObject(fonts
[i
]);
1007 void request_resize(int w
, int h
, int refont
)
1011 /* If the window is maximized supress resizing attempts */
1016 /* Don't do this in OEMANSI, you may get disable messages */
1017 if (refont
&& w
!= cols
&& (cols
== 80 || cols
== 132)
1018 && cfg
.vtmode
!= VT_OEMANSI
)
1020 if (refont
&& w
!= cols
&& (cols
== 80 || cols
== 132))
1023 /* If font width too big for screen should we shrink the font more ? */
1025 font_width
= ((font_width
* cols
+ w
/ 2) / w
);
1030 for (i
= 0; i
< 8; i
++)
1032 DeleteObject(fonts
[i
]);
1034 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1035 und_mode
= UND_FONT
;
1036 init_fonts(font_width
);
1038 static int first_time
= 1;
1041 switch (first_time
) {
1043 /* Get the size of the screen */
1044 if (GetClientRect(GetDesktopWindow(), &ss
))
1045 /* first_time = 0 */ ;
1051 /* Make sure the values are sane */
1052 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1053 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1066 width
= extra_width
+ font_width
* w
;
1067 height
= extra_height
+ font_height
* h
;
1069 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1070 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1071 SWP_NOMOVE
| SWP_NOZORDER
);
1074 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
)
1076 int thistime
= GetMessageTime();
1078 if (send_raw_mouse
) {
1079 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
);
1083 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1084 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1085 lastact
== MA_2CLK ? MA_3CLK
:
1086 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1091 if (lastact
!= MA_NOTHING
)
1092 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
);
1093 lasttime
= thistime
;
1097 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1098 * into a cooked one (SELECT, EXTEND, PASTE).
1100 Mouse_Button
translate_button(Mouse_Button button
)
1102 if (button
== MBT_LEFT
)
1104 if (button
== MBT_MIDDLE
)
1105 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1106 if (button
== MBT_RIGHT
)
1107 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1110 static void show_mouseptr(int show
)
1112 static int cursor_visible
= 1;
1113 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1115 if (cursor_visible
&& !show
)
1117 else if (!cursor_visible
&& show
)
1119 cursor_visible
= show
;
1122 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1123 WPARAM wParam
, LPARAM lParam
)
1126 static int ignore_size
= FALSE
;
1127 static int ignore_clip
= FALSE
;
1128 static int just_reconfigged
= FALSE
;
1129 static int resizing
= FALSE
;
1130 static int need_backend_resize
= FALSE
;
1134 if (pending_netevent
)
1135 enact_pending_netevent();
1142 if (cfg
.ping_interval
> 0) {
1145 if (now
- last_movement
> cfg
.ping_interval
) {
1146 back
->special(TS_PING
);
1147 last_movement
= now
;
1155 if (!cfg
.warn_on_close
|| session_closed
||
1157 "Are you sure you want to close this session?",
1158 "PuTTY Exit Confirmation",
1159 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1160 DestroyWindow(hwnd
);
1167 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1179 PROCESS_INFORMATION pi
;
1180 HANDLE filemap
= NULL
;
1182 if (wParam
== IDM_DUPSESS
) {
1184 * Allocate a file-mapping memory chunk for the
1187 SECURITY_ATTRIBUTES sa
;
1190 sa
.nLength
= sizeof(sa
);
1191 sa
.lpSecurityDescriptor
= NULL
;
1192 sa
.bInheritHandle
= TRUE
;
1193 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1196 0, sizeof(Config
), NULL
);
1198 p
= (Config
*) MapViewOfFile(filemap
,
1200 0, 0, sizeof(Config
));
1202 *p
= cfg
; /* structure copy */
1206 sprintf(c
, "putty &%p", filemap
);
1208 } else if (wParam
== IDM_SAVEDSESS
) {
1210 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1211 cl
= smalloc(16 + strlen(session
)); /* 8, but play safe */
1213 cl
= NULL
; /* not a very important failure mode */
1215 sprintf(cl
, "putty @%s", session
);
1221 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1223 si
.lpReserved
= NULL
;
1224 si
.lpDesktop
= NULL
;
1228 si
.lpReserved2
= NULL
;
1229 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1230 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1233 CloseHandle(filemap
);
1240 int prev_alwaysontop
= cfg
.alwaysontop
;
1241 int prev_sunken_edge
= cfg
.sunken_edge
;
1242 char oldlogfile
[FILENAME_MAX
];
1244 int need_setwpos
= FALSE
;
1245 int old_fwidth
, old_fheight
;
1247 strcpy(oldlogfile
, cfg
.logfilename
);
1248 oldlogtype
= cfg
.logtype
;
1251 old_fwidth
= font_width
;
1252 old_fheight
= font_height
;
1253 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1255 if (!do_reconfig(hwnd
))
1258 if (strcmp(oldlogfile
, cfg
.logfilename
) ||
1259 oldlogtype
!= cfg
.logtype
) {
1260 logfclose(); /* reset logging */
1264 just_reconfigged
= TRUE
;
1267 for (i
= 0; i
< 8; i
++)
1269 DeleteObject(fonts
[i
]);
1271 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1272 und_mode
= UND_FONT
;
1276 * Flush the line discipline's edit buffer in the
1277 * case where local editing has just been disabled.
1279 ldisc_send(NULL
, 0);
1287 /* Enable or disable the scroll bar, etc */
1289 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1290 LONG nexflag
, exflag
=
1291 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1294 if (cfg
.alwaysontop
!= prev_alwaysontop
) {
1295 if (cfg
.alwaysontop
) {
1296 nexflag
|= WS_EX_TOPMOST
;
1297 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1298 SWP_NOMOVE
| SWP_NOSIZE
);
1300 nexflag
&= ~(WS_EX_TOPMOST
);
1301 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1302 SWP_NOMOVE
| SWP_NOSIZE
);
1305 if (cfg
.sunken_edge
)
1306 nexflag
|= WS_EX_CLIENTEDGE
;
1308 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1314 nflg
&= ~WS_VSCROLL
;
1316 nflg
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1318 nflg
|= (WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1320 if (nflg
!= flag
|| nexflag
!= exflag
) {
1324 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1325 if (nexflag
!= exflag
)
1326 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1328 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
1330 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1331 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1332 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
1333 | SWP_FRAMECHANGED
);
1335 GetWindowRect(hwnd
, &wr
);
1336 GetClientRect(hwnd
, &cr
);
1338 wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1340 wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1344 if (cfg
.height
!= rows
||
1345 cfg
.width
!= cols
||
1346 old_fwidth
!= font_width
||
1347 old_fheight
!= font_height
||
1348 cfg
.savelines
!= savelines
||
1349 cfg
.sunken_edge
!= prev_sunken_edge
)
1350 need_setwpos
= TRUE
;
1351 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1352 InvalidateRect(hwnd
, NULL
, TRUE
);
1355 SetWindowPos(hwnd
, NULL
, 0, 0,
1356 extra_width
+ font_width
* cfg
.width
,
1357 extra_height
+ font_height
* cfg
.height
,
1358 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1359 SWP_NOMOVE
| SWP_NOZORDER
);
1361 set_title(cfg
.wintitle
);
1362 if (IsIconic(hwnd
)) {
1364 cfg
.win_name_always ? window_name
:
1379 back
->special(TS_AYT
);
1382 back
->special(TS_BRK
);
1385 back
->special(TS_SYNCH
);
1388 back
->special(TS_EC
);
1391 back
->special(TS_EL
);
1394 back
->special(TS_GA
);
1397 back
->special(TS_NOP
);
1400 back
->special(TS_ABORT
);
1403 back
->special(TS_AO
);
1406 back
->special(TS_IP
);
1409 back
->special(TS_SUSP
);
1412 back
->special(TS_EOR
);
1415 back
->special(TS_EOF
);
1421 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1422 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1427 #define X_POS(l) ((int)(short)LOWORD(l))
1428 #define Y_POS(l) ((int)(short)HIWORD(l))
1430 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1431 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1432 #define WHEEL_DELTA 120
1435 wheel_accumulator
+= (short) HIWORD(wParam
);
1436 wParam
= LOWORD(wParam
);
1438 /* process events when the threshold is reached */
1439 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
1442 /* reduce amount for next time */
1443 if (wheel_accumulator
> 0) {
1445 wheel_accumulator
-= WHEEL_DELTA
;
1446 } else if (wheel_accumulator
< 0) {
1448 wheel_accumulator
+= WHEEL_DELTA
;
1452 if (send_raw_mouse
) {
1453 /* send a mouse-down followed by a mouse up */
1456 TO_CHR_X(X_POS(lParam
)),
1457 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1458 wParam
& MK_CONTROL
);
1459 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1460 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1461 wParam
& MK_CONTROL
);
1463 /* trigger a scroll */
1465 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
1470 case WM_LBUTTONDOWN
:
1471 case WM_MBUTTONDOWN
:
1472 case WM_RBUTTONDOWN
:
1479 case WM_LBUTTONDOWN
:
1483 case WM_MBUTTONDOWN
:
1484 button
= MBT_MIDDLE
;
1487 case WM_RBUTTONDOWN
:
1496 button
= MBT_MIDDLE
;
1507 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1508 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
);
1511 term_mouse(button
, MA_RELEASE
,
1512 TO_CHR_X(X_POS(lParam
)),
1513 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1514 wParam
& MK_CONTROL
);
1522 * Add the mouse position and message time to the random
1525 noise_ultralight(lParam
);
1527 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1529 if (wParam
& MK_LBUTTON
)
1531 else if (wParam
& MK_MBUTTON
)
1532 b
= cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1534 b
= cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1535 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1536 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1537 wParam
& MK_CONTROL
);
1540 case WM_NCMOUSEMOVE
:
1542 noise_ultralight(lParam
);
1544 case WM_IGNORE_CLIP
:
1545 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1547 case WM_DESTROYCLIPBOARD
:
1550 ignore_clip
= FALSE
;
1556 hdc
= BeginPaint(hwnd
, &p
);
1558 SelectPalette(hdc
, pal
, TRUE
);
1559 RealizePalette(hdc
);
1561 term_paint(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1562 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1563 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
1564 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
1570 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1571 * but the only one that's likely to try to overload us is FD_READ.
1572 * This means buffering just one is fine.
1574 if (pending_netevent
)
1575 enact_pending_netevent();
1577 pending_netevent
= TRUE
;
1578 pend_netevent_wParam
= wParam
;
1579 pend_netevent_lParam
= lParam
;
1580 time(&last_movement
);
1584 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
1597 case WM_IGNORE_SIZE
:
1598 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1600 case WM_ENTERSIZEMOVE
:
1603 need_backend_resize
= FALSE
;
1605 case WM_EXITSIZEMOVE
:
1608 if (need_backend_resize
)
1613 int width
, height
, w
, h
, ew
, eh
;
1614 LPRECT r
= (LPRECT
) lParam
;
1616 width
= r
->right
- r
->left
- extra_width
;
1617 height
= r
->bottom
- r
->top
- extra_height
;
1618 w
= (width
+ font_width
/ 2) / font_width
;
1621 h
= (height
+ font_height
/ 2) / font_height
;
1624 UpdateSizeTip(hwnd
, w
, h
);
1625 ew
= width
- w
* font_width
;
1626 eh
= height
- h
* font_height
;
1628 if (wParam
== WMSZ_LEFT
||
1629 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
1635 if (wParam
== WMSZ_TOP
||
1636 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
1646 /* break; (never reached) */
1648 if (wParam
== SIZE_MINIMIZED
) {
1650 cfg
.win_name_always ? window_name
: icon_name
);
1653 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1654 SetWindowText(hwnd
, window_name
);
1656 int width
, height
, w
, h
;
1657 #if 0 /* we have fixed this using WM_SIZING now */
1661 width
= LOWORD(lParam
);
1662 height
= HIWORD(lParam
);
1663 w
= width
/ font_width
;
1666 h
= height
/ font_height
;
1669 #if 0 /* we have fixed this using WM_SIZING now */
1670 ew
= width
- w
* font_width
;
1671 eh
= height
- h
* font_height
;
1672 if (ew
!= 0 || eh
!= 0) {
1674 GetWindowRect(hwnd
, &r
);
1675 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
1676 SetWindowPos(hwnd
, NULL
, 0, 0,
1677 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1678 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1681 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1683 term_size(h
, w
, cfg
.savelines
);
1685 * Don't call back->size in mid-resize. (To prevent
1686 * massive numbers of resize events getting sent
1687 * down the connection during an NT opaque drag.)
1692 need_backend_resize
= TRUE
;
1693 just_reconfigged
= FALSE
;
1696 ignore_size
= FALSE
;
1699 switch (LOWORD(wParam
)) {
1713 term_scroll(0, +rows
/ 2);
1716 term_scroll(0, -rows
/ 2);
1718 case SB_THUMBPOSITION
:
1720 term_scroll(1, HIWORD(wParam
));
1724 case WM_PALETTECHANGED
:
1725 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1726 HDC hdc
= get_ctx();
1728 if (RealizePalette(hdc
) > 0)
1734 case WM_QUERYNEWPALETTE
:
1736 HDC hdc
= get_ctx();
1738 if (RealizePalette(hdc
) > 0)
1750 * Add the scan code and keypress timing to the random
1753 noise_ultralight(lParam
);
1756 * We don't do TranslateMessage since it disassociates the
1757 * resulting CHAR message from the KEYDOWN that sparked it,
1758 * which we occasionally don't want. Instead, we process
1759 * KEYDOWN, and call the Win32 translator functions so that
1760 * we get the translations under _our_ control.
1763 unsigned char buf
[20];
1766 if (wParam
== VK_PROCESSKEY
) {
1769 m
.message
= WM_KEYDOWN
;
1771 m
.lParam
= lParam
& 0xdfff;
1772 TranslateMessage(&m
);
1774 len
= TranslateKey(message
, wParam
, lParam
, buf
);
1776 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1777 ldisc_send(buf
, len
);
1786 unsigned char buf
[2];
1789 buf
[0] = wParam
>> 8;
1795 * Nevertheless, we are prepared to deal with WM_CHAR
1796 * messages, should they crop up. So if someone wants to
1797 * post the things to us as part of a macro manoeuvre,
1798 * we're ready to cope.
1801 char c
= xlat_kbd2tty((unsigned char) wParam
);
1806 if (send_raw_mouse
) {
1807 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
1812 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1816 * Move the system caret. (We maintain one, even though it's
1817 * invisible, for the benefit of blind people: apparently some
1818 * helper software tracks the system caret, so we should arrange to
1821 void sys_cursor(int x
, int y
)
1824 SetCaretPos(x
* font_width
, y
* font_height
);
1828 * Draw a line of text in the window, at given character
1829 * coordinates, in given attributes.
1831 * We are allowed to fiddle with the contents of `text'.
1833 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
1834 unsigned long attr
, int lattr
)
1837 int nfg
, nbg
, nfont
;
1840 int force_manual_underline
= 0;
1841 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
1842 static int *IpDx
= 0, IpDxLEN
= 0;;
1844 if (len
> IpDxLEN
|| IpDx
[0] != fnt_width
) {
1846 if (len
> IpDxLEN
) {
1848 IpDx
= smalloc((len
+ 16) * sizeof(int));
1849 IpDxLEN
= (len
+ 16);
1851 for (i
= 0; i
< IpDxLEN
; i
++)
1852 IpDx
[i
] = fnt_width
;
1858 if ((attr
& ATTR_ACTCURS
) && cfg
.cursor_type
== 0) {
1859 attr
&= (bold_mode
== BOLD_COLOURS ?
0x300200 : 0x300300);
1860 attr
^= ATTR_CUR_XOR
;
1864 if (cfg
.vtmode
== VT_OEMONLY
)
1868 * Map high-half characters in order to approximate ISO using
1869 * OEM character set. No characters are missing if the OEM codepage
1872 if (nfont
& FONT_OEM
) {
1874 for (i
= 0; i
< len
; i
++)
1875 if (text
[i
] >= '\xA0' && text
[i
] <= '\xFF') {
1877 /* This is CP850 ... perfect translation */
1878 static const char oemhighhalf
[] = "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1879 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1880 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1881 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1882 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1883 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1884 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1885 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1886 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1887 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1888 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1889 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1892 /* This is CP437 ... junk translation */
1893 static const unsigned char oemhighhalf
[] = {
1894 0x20, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1895 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1896 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1897 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1898 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1899 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1900 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1901 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1902 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1903 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1904 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1905 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1908 text
[i
] = oemhighhalf
[(unsigned char) text
[i
] - 0xA0];
1912 if (attr
& ATTR_LINEDRW
) {
1915 static const char poorman
[] =
1916 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1919 static const char oemmap_437
[] =
1920 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1921 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1924 static const char oemmap_850
[] =
1925 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1926 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1928 /* Poor windows font ... eg: windows courier */
1929 static const char oemmap
[] =
1930 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1931 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1934 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1935 * VT100 line drawing chars; everything else stays normal.
1937 * Actually '_' maps to space too, but that's done before.
1939 switch (cfg
.vtmode
) {
1941 for (i
= 0; i
< len
; i
++)
1942 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1943 text
[i
] += '\x01' - '\x60';
1946 /* Make sure we actually have an OEM font */
1947 if (fonts
[nfont
| FONT_OEM
]) {
1950 for (i
= 0; i
< len
; i
++)
1951 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1952 text
[i
] = oemmap
[(unsigned char) text
[i
] - 0x60];
1956 for (i
= 0; i
< len
; i
++)
1957 if (text
[i
] >= '\x60' && text
[i
] <= '\x7E')
1958 text
[i
] = poorman
[(unsigned char) text
[i
] - 0x60];
1963 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
1964 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
1965 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
1967 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
1968 nfont
|= FONT_UNDERLINE
;
1969 if (!fonts
[nfont
]) {
1970 if (nfont
& FONT_UNDERLINE
)
1971 force_manual_underline
= 1;
1972 /* Don't do the same for manual bold, it could be bad news. */
1974 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
1976 if (font_needs_hand_underlining
&& (attr
& ATTR_UNDER
))
1977 force_manual_underline
= 1;
1978 if (attr
& ATTR_REVERSE
) {
1983 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
1985 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
1989 SelectObject(hdc
, fonts
[nfont
]);
1990 SetTextColor(hdc
, fg
);
1991 SetBkColor(hdc
, bg
);
1992 SetBkMode(hdc
, OPAQUE
);
1995 line_box
.right
= x
+ fnt_width
* len
;
1996 line_box
.bottom
= y
+ font_height
;
1997 ExtTextOut(hdc
, x
, y
, ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
,
1999 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2000 SetBkMode(hdc
, TRANSPARENT
);
2002 /* GRR: This draws the character outside it's box and can leave
2003 * 'droppings' even with the clip box! I suppose I could loop it
2004 * one character at a time ... yuk.
2006 * Or ... I could do a test print with "W", and use +1 or -1 for this
2007 * shift depending on if the leftmost column is blank...
2009 ExtTextOut(hdc
, x
- 1, y
, ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2011 if (force_manual_underline
||
2012 (und_mode
== UND_LINE
&& (attr
& ATTR_UNDER
))) {
2014 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2015 MoveToEx(hdc
, x
, y
+ descent
, NULL
);
2016 LineTo(hdc
, x
+ len
* fnt_width
, y
+ descent
);
2017 oldpen
= SelectObject(hdc
, oldpen
);
2018 DeleteObject(oldpen
);
2020 if ((attr
& ATTR_PASCURS
) && cfg
.cursor_type
== 0) {
2023 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2024 pts
[2].x
= pts
[3].x
= x
+ fnt_width
- 1;
2025 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2026 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2027 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2028 Polyline(hdc
, pts
, 5);
2029 oldpen
= SelectObject(hdc
, oldpen
);
2030 DeleteObject(oldpen
);
2032 if ((attr
& (ATTR_ACTCURS
| ATTR_PASCURS
)) && cfg
.cursor_type
!= 0) {
2033 int startx
, starty
, dx
, dy
, length
, i
;
2034 if (cfg
.cursor_type
== 1) {
2036 starty
= y
+ descent
;
2042 if (attr
& ATTR_RIGHTCURS
)
2043 xadjust
= fnt_width
- 1;
2044 startx
= x
+ xadjust
;
2048 length
= font_height
;
2050 if (attr
& ATTR_ACTCURS
) {
2053 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2054 MoveToEx(hdc
, startx
, starty
, NULL
);
2055 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2056 oldpen
= SelectObject(hdc
, oldpen
);
2057 DeleteObject(oldpen
);
2059 for (i
= 0; i
< length
; i
++) {
2061 SetPixel(hdc
, startx
, starty
, colours
[23]);
2070 static int check_compose(int first
, int second
)
2073 static char *composetbl
[] = {
2074 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡",
2076 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§",
2078 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
2079 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·",
2081 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ",
2083 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
2084 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
2085 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
2086 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ",
2088 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð",
2090 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú",
2092 "\"uü", "'yý", "htþ", "\"yÿ",
2097 static int recurse
= 0;
2100 for (c
= composetbl
; *c
; c
++) {
2101 if ((*c
)[0] == first
&& (*c
)[1] == second
) {
2102 return (*c
)[2] & 0xFF;
2108 nc
= check_compose(second
, first
);
2110 nc
= check_compose(toupper(first
), toupper(second
));
2112 nc
= check_compose(toupper(second
), toupper(first
));
2120 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2121 * codes. Returns number of bytes used or zero to drop the message
2122 * or -1 to forward the message to windows.
2124 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2125 unsigned char *output
)
2128 int scan
, left_alt
= 0, key_down
, shift_state
;
2130 unsigned char *p
= output
;
2131 static int alt_state
= 0;
2133 HKL kbd_layout
= GetKeyboardLayout(0);
2135 static WORD keys
[3];
2136 static int compose_char
= 0;
2137 static WPARAM compose_key
= 0;
2139 r
= GetKeyboardState(keystate
);
2141 memset(keystate
, 0, sizeof(keystate
));
2144 { /* Tell us all about key events */
2145 static BYTE oldstate
[256];
2146 static int first
= 1;
2150 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2153 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2155 } else if ((HIWORD(lParam
) & KF_UP
)
2156 && scan
== (HIWORD(lParam
) & 0xFF)) {
2160 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2161 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2174 debug(("VK_%02x", wParam
));
2176 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2178 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2180 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2181 if (ch
>= ' ' && ch
<= '~')
2182 debug((", '%c'", ch
));
2184 debug((", $%02x", ch
));
2187 debug((", KB0=%02x", keys
[0]));
2189 debug((", KB1=%02x", keys
[1]));
2191 debug((", KB2=%02x", keys
[2]));
2193 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2195 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2197 if ((HIWORD(lParam
) & KF_EXTENDED
))
2199 if ((HIWORD(lParam
) & KF_UP
))
2203 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2204 else if ((HIWORD(lParam
) & KF_UP
))
2205 oldstate
[wParam
& 0xFF] ^= 0x80;
2207 oldstate
[wParam
& 0xFF] ^= 0x81;
2209 for (ch
= 0; ch
< 256; ch
++)
2210 if (oldstate
[ch
] != keystate
[ch
])
2211 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2213 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2217 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2218 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2222 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2223 if ((cfg
.funky_type
== 3 ||
2224 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2225 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2227 wParam
= VK_EXECUTE
;
2229 /* UnToggle NUMLock */
2230 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2231 keystate
[VK_NUMLOCK
] ^= 1;
2234 /* And write back the 'adjusted' state */
2235 SetKeyboardState(keystate
);
2238 /* Disable Auto repeat if required */
2239 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2242 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2245 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2247 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2248 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2249 if (cfg
.ctrlaltkeys
)
2250 keystate
[VK_MENU
] = 0;
2252 keystate
[VK_RMENU
] = 0x80;
2257 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2258 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2259 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2261 /* Note if AltGr was pressed and if it was used as a compose key */
2262 if (!compose_state
) {
2263 compose_key
= 0x100;
2264 if (cfg
.compose_key
) {
2265 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2266 compose_key
= wParam
;
2268 if (wParam
== VK_APPS
)
2269 compose_key
= wParam
;
2272 if (wParam
== compose_key
) {
2273 if (compose_state
== 0
2274 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2276 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2280 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
2284 * Record that we pressed key so the scroll window can be reset, but
2285 * be careful to avoid Shift-UP/Down
2287 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
2291 /* Make sure we're not pasting */
2295 if (compose_state
> 1 && left_alt
)
2298 /* Sanitize the number pad if not using a PC NumPad */
2299 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2300 && cfg
.funky_type
!= 2)
2301 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
2302 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
2306 nParam
= VK_NUMPAD0
;
2309 nParam
= VK_NUMPAD1
;
2312 nParam
= VK_NUMPAD2
;
2315 nParam
= VK_NUMPAD3
;
2318 nParam
= VK_NUMPAD4
;
2321 nParam
= VK_NUMPAD5
;
2324 nParam
= VK_NUMPAD6
;
2327 nParam
= VK_NUMPAD7
;
2330 nParam
= VK_NUMPAD8
;
2333 nParam
= VK_NUMPAD9
;
2336 nParam
= VK_DECIMAL
;
2340 if (keystate
[VK_NUMLOCK
] & 1)
2347 /* If a key is pressed and AltGr is not active */
2348 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
2349 /* Okay, prepare for most alts then ... */
2353 /* Lets see if it's a pattern we know all about ... */
2354 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2355 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2358 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2359 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
2362 if (wParam
== VK_INSERT
&& shift_state
== 1) {
2363 term_mouse(MBT_PASTE
, MA_CLICK
, 0, 0, 0, 0);
2364 term_mouse(MBT_PASTE
, MA_RELEASE
, 0, 0, 0, 0);
2367 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
2370 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
2372 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
2373 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
2376 /* Control-Numlock for app-keypad mode switch */
2377 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
2378 app_keypad_keys
^= 1;
2382 /* Nethack keypad */
2383 if (cfg
.nethack_keypad
&& !left_alt
) {
2386 *p
++ = shift_state ?
'B' : 'b';
2389 *p
++ = shift_state ?
'J' : 'j';
2392 *p
++ = shift_state ?
'N' : 'n';
2395 *p
++ = shift_state ?
'H' : 'h';
2398 *p
++ = shift_state ?
'.' : '.';
2401 *p
++ = shift_state ?
'L' : 'l';
2404 *p
++ = shift_state ?
'Y' : 'y';
2407 *p
++ = shift_state ?
'K' : 'k';
2410 *p
++ = shift_state ?
'U' : 'u';
2415 /* Application Keypad */
2419 if (cfg
.funky_type
== 3 ||
2420 (cfg
.funky_type
<= 1 &&
2421 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
2435 if (app_keypad_keys
&& !cfg
.no_applic_k
)
2472 if (cfg
.funky_type
== 2) {
2477 } else if (shift_state
)
2484 if (cfg
.funky_type
== 2)
2488 if (cfg
.funky_type
== 2)
2492 if (cfg
.funky_type
== 2)
2497 if (HIWORD(lParam
) & KF_EXTENDED
)
2503 if (xkey
>= 'P' && xkey
<= 'S')
2504 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
2506 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
2508 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
2513 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
2514 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
2517 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
2523 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
2527 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
2531 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
2535 if (wParam
== VK_PAUSE
) { /* Break/Pause */
2540 /* Control-2 to Control-8 are special */
2541 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
2542 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
2545 if (shift_state
== 2 && wParam
== 0xBD) {
2549 if (shift_state
== 2 && wParam
== 0xDF) {
2553 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
2560 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2561 * for integer decimal nn.)
2563 * We also deal with the weird ones here. Linux VCs replace F1
2564 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2565 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2571 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
2574 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
2577 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
2580 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
2583 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
2586 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
2589 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
2592 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
2595 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
2598 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
2649 /* Reorder edit keys to physical order */
2650 if (cfg
.funky_type
== 3 && code
<= 6)
2651 code
= "\0\2\1\4\5\3\6"[code
];
2653 if (vt52_mode
&& code
> 0 && code
<= 6) {
2654 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
2658 if (cfg
.funky_type
== 5 && code
>= 11 && code
<= 24) {
2659 p
+= sprintf((char *) p
, "\x1B[%c", code
+ 'M' - 11);
2662 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
2669 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
2672 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
2675 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
2676 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
2679 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
2681 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
2683 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
2686 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
2687 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
2691 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
2696 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2697 * some reason seems to send VK_CLEAR to Windows...).
2720 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
2721 else if (app_cursor_keys
&& !cfg
.no_applic_c
)
2722 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
2724 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
2730 * Finally, deal with Return ourselves. (Win95 seems to
2731 * foul it up when Alt is pressed, for some reason.)
2733 if (wParam
== VK_RETURN
) { /* Return */
2739 /* Okay we've done everything interesting; let windows deal with
2740 * the boring stuff */
2742 BOOL capsOn
= keystate
[VK_CAPITAL
] != 0;
2744 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2745 if (cfg
.xlat_capslockcyr
)
2746 keystate
[VK_CAPITAL
] = 0;
2748 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
2751 for (i
= 0; i
< r
; i
++) {
2752 unsigned char ch
= (unsigned char) keys
[i
];
2754 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
2759 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
2763 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
2764 MessageBeep(MB_ICONHAND
);
2767 *p
++ = xlat_kbd2tty((unsigned char) nc
);
2773 if (left_alt
&& key_down
)
2779 ch
= xlat_latkbd2win(ch
);
2780 *p
++ = xlat_kbd2tty(ch
);
2784 /* This is so the ALT-Numpad and dead keys work correctly. */
2789 /* If we're definitly not building up an ALT-54321 then clear it */
2794 /* ALT alone may or may not want to bring up the System menu */
2795 if (wParam
== VK_MENU
) {
2797 if (message
== WM_SYSKEYDOWN
)
2799 else if (message
== WM_SYSKEYUP
&& alt_state
)
2800 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
2801 if (message
== WM_SYSKEYUP
)
2811 void set_title(char *title
)
2814 window_name
= smalloc(1 + strlen(title
));
2815 strcpy(window_name
, title
);
2816 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
2817 SetWindowText(hwnd
, title
);
2820 void set_icon(char *title
)
2823 icon_name
= smalloc(1 + strlen(title
));
2824 strcpy(icon_name
, title
);
2825 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
2826 SetWindowText(hwnd
, title
);
2829 void set_sbar(int total
, int start
, int page
)
2836 si
.cbSize
= sizeof(si
);
2837 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
2839 si
.nMax
= total
- 1;
2843 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
2846 Context
get_ctx(void)
2852 SelectPalette(hdc
, pal
, FALSE
);
2858 void free_ctx(Context ctx
)
2860 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
2861 ReleaseDC(hwnd
, ctx
);
2864 static void real_palette_set(int n
, int r
, int g
, int b
)
2867 logpal
->palPalEntry
[n
].peRed
= r
;
2868 logpal
->palPalEntry
[n
].peGreen
= g
;
2869 logpal
->palPalEntry
[n
].peBlue
= b
;
2870 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
2871 colours
[n
] = PALETTERGB(r
, g
, b
);
2872 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2874 colours
[n
] = RGB(r
, g
, b
);
2877 void palette_set(int n
, int r
, int g
, int b
)
2879 static const int first
[21] = {
2880 0, 2, 4, 6, 8, 10, 12, 14,
2881 1, 3, 5, 7, 9, 11, 13, 15,
2884 real_palette_set(first
[n
], r
, g
, b
);
2886 real_palette_set(first
[n
] + 1, r
, g
, b
);
2888 HDC hdc
= get_ctx();
2889 UnrealizeObject(pal
);
2890 RealizePalette(hdc
);
2895 void palette_reset(void)
2899 for (i
= 0; i
< NCOLOURS
; i
++) {
2901 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
2902 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
2903 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
2904 logpal
->palPalEntry
[i
].peFlags
= 0;
2905 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
2906 defpal
[i
].rgbtGreen
,
2907 defpal
[i
].rgbtBlue
);
2909 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
2910 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
2915 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
2917 RealizePalette(hdc
);
2922 void write_clip(void *data
, int len
, int must_deselect
)
2927 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
2930 lock
= GlobalLock(clipdata
);
2933 memcpy(lock
, data
, len
);
2934 ((unsigned char *) lock
)[len
] = 0;
2935 GlobalUnlock(clipdata
);
2938 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
2940 if (OpenClipboard(hwnd
)) {
2942 SetClipboardData(CF_TEXT
, clipdata
);
2945 GlobalFree(clipdata
);
2948 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
2951 void get_clip(void **p
, int *len
)
2953 static HGLOBAL clipdata
= NULL
;
2957 GlobalUnlock(clipdata
);
2961 if (OpenClipboard(NULL
)) {
2962 clipdata
= GetClipboardData(CF_TEXT
);
2965 *p
= GlobalLock(clipdata
);
2979 * Move `lines' lines from position `from' to position `to' in the
2982 void optimised_move(int to
, int from
, int lines
)
2987 min
= (to
< from ? to
: from
);
2988 max
= to
+ from
- min
;
2991 r
.right
= cols
* font_width
;
2992 r
.top
= min
* font_height
;
2993 r
.bottom
= (max
+ lines
) * font_height
;
2994 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
2998 * Print a message box and perform a fatal exit.
3000 void fatalbox(char *fmt
, ...)
3006 vsprintf(stuff
, fmt
, ap
);
3008 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
3017 if (mode
== BELL_DEFAULT
) {
3019 * For MessageBeep style bells, we want to be careful of
3020 * timing, because they don't have the nice property of
3021 * PlaySound bells that each one cancels the previous
3022 * active one. So we limit the rate to one per 50ms or so.
3024 static long lastbeep
= 0;
3027 now
= GetTickCount();
3028 beepdiff
= now
- lastbeep
;
3029 if (beepdiff
>= 0 && beepdiff
< 50)
3033 } else if (mode
== BELL_WAVEFILE
) {
3034 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
3035 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
3036 sprintf(buf
, "Unable to play sound file\n%s\n"
3037 "Using default sound instead", cfg
.bell_wavefile
);
3038 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
3039 MB_OK
| MB_ICONEXCLAMATION
);
3040 cfg
.beep
= BELL_DEFAULT
;