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);
66 static void another_font(int);
67 static void deinit_fonts(void);
69 static int extra_width
, extra_height
;
71 static int pending_netevent
= 0;
72 static WPARAM pend_netevent_wParam
= 0;
73 static LPARAM pend_netevent_lParam
= 0;
74 static void enact_pending_netevent(void);
76 static time_t last_movement
= 0;
80 #define FONT_UNDERLINE 2
81 #define FONT_BOLDUND 3
82 #define FONT_WIDE 0x04
83 #define FONT_HIGH 0x08
84 #define FONT_NARROW 0x10
86 #define FONT_OEMBOLD 0x21
87 #define FONT_OEMUND 0x22
88 #define FONT_OEMBOLDUND 0x23
89 #define FONT_MSGOTHIC 0x40
90 #define FONT_MINGLIU 0x60
91 #define FONT_GULIMCHE 0x80
92 #define FONT_MAXNO 0x8F
94 static HFONT fonts
[FONT_MAXNO
];
95 static int fontflag
[FONT_MAXNO
];
97 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
105 static COLORREF colours
[NCOLOURS
];
107 static LPLOGPALETTE logpal
;
108 static RGBTRIPLE defpal
[NCOLOURS
];
112 static HBITMAP caretbm
;
114 static int dbltime
, lasttime
, lastact
;
115 static Mouse_Button lastbtn
;
117 /* this allows xterm-style mouse handling. */
118 static int send_raw_mouse
= 0;
119 static int wheel_accumulator
= 0;
121 static char *window_name
, *icon_name
;
123 static int compose_state
= 0;
125 /* Dummy routine, only required in plink. */
126 void ldisc_update(int echo
, int edit
)
130 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
132 static char appname
[] = "PuTTY";
137 int guess_width
, guess_height
;
140 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
142 winsock_ver
= MAKEWORD(1, 1);
143 if (WSAStartup(winsock_ver
, &wsadata
)) {
144 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
145 MB_OK
| MB_ICONEXCLAMATION
);
148 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
149 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
150 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
154 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
157 InitCommonControls();
159 /* Ensure a Maximize setting in Explorer doesn't maximise the
164 * Process the command line.
169 default_protocol
= DEFAULT_PROTOCOL
;
170 default_port
= DEFAULT_PORT
;
171 cfg
.logtype
= LGTYP_NONE
;
173 do_defaults(NULL
, &cfg
);
176 while (*p
&& isspace(*p
))
180 * Process command line options first. Yes, this can be
181 * done better, and it will be as soon as I have the
185 char *q
= p
+ strcspn(p
, " \t");
188 tolower(p
[0]) == 's' &&
189 tolower(p
[1]) == 's' && tolower(p
[2]) == 'h') {
190 default_protocol
= cfg
.protocol
= PROT_SSH
;
191 default_port
= cfg
.port
= 22;
192 } else if (q
== p
+ 7 &&
193 tolower(p
[0]) == 'c' &&
194 tolower(p
[1]) == 'l' &&
195 tolower(p
[2]) == 'e' &&
196 tolower(p
[3]) == 'a' &&
197 tolower(p
[4]) == 'n' &&
198 tolower(p
[5]) == 'u' && tolower(p
[6]) == 'p') {
200 * `putty -cleanup'. Remove all registry entries
201 * associated with PuTTY, and also find and delete
202 * the random seed file.
205 "This procedure will remove ALL Registry\n"
206 "entries associated with PuTTY, and will\n"
207 "also remove the PuTTY random seed file.\n"
209 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
210 "SESSIONS. Are you really sure you want\n"
213 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
218 p
= q
+ strspn(q
, " \t");
222 * An initial @ means to activate a saved session.
226 while (i
> 1 && isspace(p
[i
- 1]))
229 do_defaults(p
+ 1, &cfg
);
230 if (!*cfg
.host
&& !do_config()) {
234 } else if (*p
== '&') {
236 * An initial & means we've been given a command line
237 * containing the hex value of a HANDLE for a file
238 * mapping object, which we must then extract as a
243 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
244 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
245 0, 0, sizeof(Config
))) != NULL
) {
248 CloseHandle(filemap
);
249 } else if (!do_config()) {
256 * If the hostname starts with "telnet:", set the
257 * protocol to Telnet and process the string as a
260 if (!strncmp(q
, "telnet:", 7)) {
264 if (q
[0] == '/' && q
[1] == '/')
266 cfg
.protocol
= PROT_TELNET
;
268 while (*p
&& *p
!= ':' && *p
!= '/')
277 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
278 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
280 while (*p
&& !isspace(*p
))
284 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
285 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
286 while (*p
&& isspace(*p
))
300 /* See if host is of the form user@host */
301 if (cfg
.host
[0] != '\0') {
302 char *atsign
= strchr(cfg
.host
, '@');
303 /* Make sure we're not overflowing the user field */
305 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
306 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
307 cfg
.username
[atsign
- cfg
.host
] = '\0';
309 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
314 * Trim a colon suffix off the hostname if it's there.
316 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
320 * Select protocol. This is farmed out into a table in a
321 * separate file to enable an ssh-free variant.
326 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
327 if (backends
[i
].protocol
== cfg
.protocol
) {
328 back
= backends
[i
].backend
;
332 MessageBox(NULL
, "Unsupported protocol number found",
333 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
339 /* Check for invalid Port number (i.e. zero) */
341 MessageBox(NULL
, "Invalid Port Number",
342 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
349 wndclass
.lpfnWndProc
= WndProc
;
350 wndclass
.cbClsExtra
= 0;
351 wndclass
.cbWndExtra
= 0;
352 wndclass
.hInstance
= inst
;
353 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
354 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
355 wndclass
.hbrBackground
= GetStockObject(BLACK_BRUSH
);
356 wndclass
.lpszMenuName
= NULL
;
357 wndclass
.lpszClassName
= appname
;
359 RegisterClass(&wndclass
);
364 savelines
= cfg
.savelines
;
370 * Guess some defaults for the window size. This all gets
371 * updated later, so we don't really care too much. However, we
372 * do want the font width/height guesses to correspond to a
373 * large font rather than a small one...
380 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
381 guess_width
= extra_width
+ font_width
* cols
;
382 guess_height
= extra_height
+ font_height
* rows
;
385 HWND w
= GetDesktopWindow();
386 GetWindowRect(w
, &r
);
387 if (guess_width
> r
.right
- r
.left
)
388 guess_width
= r
.right
- r
.left
;
389 if (guess_height
> r
.bottom
- r
.top
)
390 guess_height
= r
.bottom
- r
.top
;
394 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
397 winmode
&= ~(WS_VSCROLL
);
399 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
401 exwinmode
|= WS_EX_TOPMOST
;
403 exwinmode
|= WS_EX_CLIENTEDGE
;
404 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
405 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
406 guess_width
, guess_height
,
407 NULL
, NULL
, inst
, NULL
);
411 * Initialise the fonts, simultaneously correcting the guesses
412 * for font_{width,height}.
414 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
419 * Correct the guesses for extra_{width,height}.
423 GetWindowRect(hwnd
, &wr
);
424 GetClientRect(hwnd
, &cr
);
425 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
426 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
430 * Resize the window, now we know what size we _really_ want it
433 guess_width
= extra_width
+ font_width
* cols
;
434 guess_height
= extra_height
+ font_height
* rows
;
435 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
436 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
437 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
440 * Set up a caret bitmap, with no content.
444 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
445 bits
= smalloc(size
);
446 memset(bits
, 0, size
);
447 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
450 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
453 * Initialise the scroll bar.
458 si
.cbSize
= sizeof(si
);
459 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
464 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
468 * Start up the telnet connection.
472 char msg
[1024], *title
;
475 error
= back
->init(cfg
.host
, cfg
.port
, &realhost
);
477 sprintf(msg
, "Unable to open connection to\n"
478 "%.800s\n" "%s", cfg
.host
, error
);
479 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
482 window_name
= icon_name
= NULL
;
484 title
= cfg
.wintitle
;
486 sprintf(msg
, "%s - PuTTY", realhost
);
494 session_closed
= FALSE
;
497 * Set up the input and output buffers.
500 outbuf_reap
= outbuf_head
= 0;
503 * Prepare the mouse handler.
505 lastact
= MA_NOTHING
;
506 lastbtn
= MBT_NOTHING
;
507 dbltime
= GetDoubleClickTime();
510 * Set up the session-control options on the system menu.
513 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
517 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
518 if (cfg
.protocol
== PROT_TELNET
) {
520 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
521 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
522 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
523 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
524 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
525 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
526 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
527 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
528 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
529 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
530 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
531 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
532 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
533 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
534 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
535 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
536 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
538 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
540 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
541 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
542 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
543 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
546 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
547 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
549 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
550 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
551 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
552 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
553 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
554 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
555 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
556 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
560 * Finally show the window!
562 ShowWindow(hwnd
, show
);
565 * Open the initial log file if there is one.
570 * Set the palette up.
576 has_focus
= (GetForegroundWindow() == hwnd
);
579 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
580 int timer_id
= 0, long_timer
= 0;
582 while (msg
.message
!= WM_QUIT
) {
583 /* Sometimes DispatchMessage calls routines that use their own
584 * GetMessage loop, setup this timer so we get some control back.
586 * Also call term_update() from the timer so that if the host
587 * is sending data flat out we still do redraws.
589 if (timer_id
&& long_timer
) {
590 KillTimer(hwnd
, timer_id
);
591 long_timer
= timer_id
= 0;
594 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
595 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
596 DispatchMessage(&msg
);
598 /* Make sure we blink everything that needs it. */
601 /* Send the paste buffer if there's anything to send */
604 /* If there's nothing new in the queue then we can do everything
605 * we've delayed, reading the socket, writing, and repainting
608 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
611 if (pending_netevent
) {
612 enact_pending_netevent();
614 /* Force the cursor blink on */
617 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
621 /* Okay there is now nothing to do so we make sure the screen is
622 * completely up to date then tell windows to call us in a little
626 KillTimer(hwnd
, timer_id
);
635 /* Hmm, term_update didn't want to do an update too soon ... */
636 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
638 timer_id
= SetTimer(hwnd
, 1, 2000, NULL
);
640 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
643 /* There's no point rescanning everything in the message queue
644 * so we do an apparently unnecessary wait here
647 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
661 if (cfg
.protocol
== PROT_SSH
) {
672 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
674 char *do_select(SOCKET skt
, int startup
)
679 events
= FD_READ
| FD_WRITE
| FD_OOB
| FD_CLOSE
;
684 return "do_select(): internal error (hwnd==NULL)";
685 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
686 switch (WSAGetLastError()) {
688 return "Network is down";
690 return "WSAAsyncSelect(): unknown error";
697 * set or clear the "raw mouse message" mode
699 void set_raw_mouse_mode(int activate
)
701 send_raw_mouse
= activate
;
702 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
706 * Print a message box and close the connection.
708 void connection_fatal(char *fmt
, ...)
714 vsprintf(stuff
, fmt
, ap
);
716 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
717 if (cfg
.close_on_exit
== COE_ALWAYS
)
720 session_closed
= TRUE
;
721 SetWindowText(hwnd
, "PuTTY (inactive)");
726 * Actually do the job requested by a WM_NETEVENT
728 static void enact_pending_netevent(void)
730 static int reentering
= 0;
731 extern int select_result(WPARAM
, LPARAM
);
735 return; /* don't unpend the pending */
737 pending_netevent
= FALSE
;
740 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
743 if (ret
== 0 && !session_closed
) {
744 /* Abnormal exits will already have set session_closed and taken
745 * appropriate action. */
746 if (cfg
.close_on_exit
== COE_ALWAYS
||
747 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
749 session_closed
= TRUE
;
750 SetWindowText(hwnd
, "PuTTY (inactive)");
751 MessageBox(hwnd
, "Connection closed by remote host",
752 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
758 * Copy the colour palette from the configuration data into defpal.
759 * This is non-trivial because the colour indices are different.
761 static void cfgtopalette(void)
764 static const int ww
[] = {
765 6, 7, 8, 9, 10, 11, 12, 13,
766 14, 15, 16, 17, 18, 19, 20, 21,
767 0, 1, 2, 3, 4, 4, 5, 5
770 for (i
= 0; i
< 24; i
++) {
772 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
773 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
774 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
779 * Set up the colour palette.
781 static void init_palette(void)
784 HDC hdc
= GetDC(hwnd
);
786 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
787 logpal
= smalloc(sizeof(*logpal
)
788 - sizeof(logpal
->palPalEntry
)
789 + NCOLOURS
* sizeof(PALETTEENTRY
));
790 logpal
->palVersion
= 0x300;
791 logpal
->palNumEntries
= NCOLOURS
;
792 for (i
= 0; i
< NCOLOURS
; i
++) {
793 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
794 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
795 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
796 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
798 pal
= CreatePalette(logpal
);
800 SelectPalette(hdc
, pal
, FALSE
);
802 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
805 ReleaseDC(hwnd
, hdc
);
808 for (i
= 0; i
< NCOLOURS
; i
++)
809 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
813 for (i
= 0; i
< NCOLOURS
; i
++)
814 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
815 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
819 * Initialise all the fonts we will need initially. There may be as many as
820 * three or as few as one. The other (poentially) twentyone fonts are done
821 * if/when they are needed.
825 * - check the font width and height, correcting our guesses if
828 * - verify that the bold font is the same width as the ordinary
829 * one, and engage shadow bolding if not.
831 * - verify that the underlined font is the same width as the
832 * ordinary one (manual underlining by means of line drawing can
833 * be done in a pinch).
835 static void init_fonts(int pick_width
)
842 int fw_dontcare
, fw_bold
;
844 for (i
= 0; i
< FONT_MAXNO
; i
++)
847 if (cfg
.fontisbold
) {
848 fw_dontcare
= FW_BOLD
;
851 fw_dontcare
= FW_DONTCARE
;
857 font_height
= cfg
.fontheight
;
858 if (font_height
> 0) {
860 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
862 font_width
= pick_width
;
865 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
866 c, OUT_DEFAULT_PRECIS, \
867 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
868 FIXED_PITCH | FF_DONTCARE, cfg.font)
870 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
872 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
873 GetTextMetrics(hdc
, &tm
);
874 font_height
= tm
.tmHeight
;
875 font_width
= tm
.tmAveCharWidth
;
879 DWORD cset
= tm
.tmCharSet
;
880 memset(&info
, 0xFF, sizeof(info
));
882 /* !!! Yes the next line is right */
883 if (cset
== OEM_CHARSET
)
884 font_codepage
= GetOEMCP();
886 if (TranslateCharsetInfo
887 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
892 GetCPInfo(font_codepage
, &cpinfo
);
893 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
896 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
899 * Some fonts, e.g. 9-pt Courier, draw their underlines
900 * outside their character cell. We successfully prevent
901 * screen corruption by clipping the text output, but then
902 * we lose the underline completely. Here we try to work
903 * out whether this is such a font, and if it is, we set a
904 * flag that causes underlines to be drawn by hand.
906 * Having tried other more sophisticated approaches (such
907 * as examining the TEXTMETRIC structure or requesting the
908 * height of a string), I think we'll do this the brute
909 * force way: we create a small bitmap, draw an underlined
910 * space on it, and test to see whether any pixels are
911 * foreground-coloured. (Since we expect the underline to
912 * go all the way across the character cell, we only search
913 * down a single column of the bitmap, half way across.)
917 HBITMAP und_bm
, und_oldbm
;
921 und_dc
= CreateCompatibleDC(hdc
);
922 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
923 und_oldbm
= SelectObject(und_dc
, und_bm
);
924 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
925 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
926 SetTextColor(und_dc
, RGB(255, 255, 255));
927 SetBkColor(und_dc
, RGB(0, 0, 0));
928 SetBkMode(und_dc
, OPAQUE
);
929 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
931 for (i
= 0; i
< font_height
; i
++) {
932 c
= GetPixel(und_dc
, font_width
/ 2, i
);
933 if (c
!= RGB(0, 0, 0))
936 SelectObject(und_dc
, und_oldbm
);
937 DeleteObject(und_bm
);
941 DeleteObject(fonts
[FONT_UNDERLINE
]);
942 fonts
[FONT_UNDERLINE
] = 0;
946 if (bold_mode
== BOLD_FONT
) {
947 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
951 descent
= tm
.tmAscent
+ 1;
952 if (descent
>= font_height
)
953 descent
= font_height
- 1;
955 for (i
= 0; i
< 3; i
++) {
957 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
958 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
965 ReleaseDC(hwnd
, hdc
);
967 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
969 DeleteObject(fonts
[FONT_UNDERLINE
]);
970 fonts
[FONT_UNDERLINE
] = 0;
973 if (bold_mode
== BOLD_FONT
&&
974 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
975 bold_mode
= BOLD_SHADOW
;
976 DeleteObject(fonts
[FONT_BOLD
]);
977 fonts
[FONT_BOLD
] = 0;
979 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
984 static void another_font(int fontno
)
987 int fw_dontcare
, fw_bold
;
991 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
994 basefont
= (fontno
& ~(FONT_BOLDUND
));
995 if (basefont
!= fontno
&& !fontflag
[basefont
])
996 another_font(basefont
);
998 if (cfg
.fontisbold
) {
999 fw_dontcare
= FW_BOLD
;
1002 fw_dontcare
= FW_DONTCARE
;
1006 c
= cfg
.fontcharset
;
1012 if (fontno
& FONT_WIDE
)
1014 if (fontno
& FONT_NARROW
)
1016 if (fontno
& FONT_OEM
)
1018 if (fontno
& FONT_BOLD
)
1020 if (fontno
& FONT_UNDERLINE
)
1024 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1025 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1026 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1027 FIXED_PITCH
| FF_DONTCARE
, s
);
1029 fontflag
[fontno
] = 1;
1032 static void deinit_fonts(void)
1035 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1037 DeleteObject(fonts
[i
]);
1043 void request_resize(int w
, int h
, int refont
)
1047 /* If the window is maximized supress resizing attempts */
1051 if (refont
&& w
!= cols
&& (cols
== 80 || cols
== 132)) {
1052 /* If font width too big for screen should we shrink the font more ? */
1054 font_width
= ((font_width
* cols
+ w
/ 2) / w
);
1058 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1059 und_mode
= UND_FONT
;
1060 init_fonts(font_width
);
1062 static int first_time
= 1;
1065 switch (first_time
) {
1067 /* Get the size of the screen */
1068 if (GetClientRect(GetDesktopWindow(), &ss
))
1069 /* first_time = 0 */ ;
1075 /* Make sure the values are sane */
1076 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1077 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1090 width
= extra_width
+ font_width
* w
;
1091 height
= extra_height
+ font_height
* h
;
1093 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1094 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1095 SWP_NOMOVE
| SWP_NOZORDER
);
1098 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
)
1100 int thistime
= GetMessageTime();
1102 if (send_raw_mouse
) {
1103 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
);
1107 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1108 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1109 lastact
== MA_2CLK ? MA_3CLK
:
1110 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1115 if (lastact
!= MA_NOTHING
)
1116 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
);
1117 lasttime
= thistime
;
1121 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1122 * into a cooked one (SELECT, EXTEND, PASTE).
1124 Mouse_Button
translate_button(Mouse_Button button
)
1126 if (button
== MBT_LEFT
)
1128 if (button
== MBT_MIDDLE
)
1129 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1130 if (button
== MBT_RIGHT
)
1131 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1134 static void show_mouseptr(int show
)
1136 static int cursor_visible
= 1;
1137 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1139 if (cursor_visible
&& !show
)
1141 else if (!cursor_visible
&& show
)
1143 cursor_visible
= show
;
1146 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1147 WPARAM wParam
, LPARAM lParam
)
1150 static int ignore_size
= FALSE
;
1151 static int ignore_clip
= FALSE
;
1152 static int just_reconfigged
= FALSE
;
1153 static int resizing
= FALSE
;
1154 static int need_backend_resize
= FALSE
;
1155 static int defered_resize
= FALSE
;
1159 if (pending_netevent
)
1160 enact_pending_netevent();
1167 if (cfg
.ping_interval
> 0) {
1170 if (now
- last_movement
> cfg
.ping_interval
) {
1171 back
->special(TS_PING
);
1172 last_movement
= now
;
1180 if (!cfg
.warn_on_close
|| session_closed
||
1182 "Are you sure you want to close this session?",
1183 "PuTTY Exit Confirmation",
1184 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1185 DestroyWindow(hwnd
);
1192 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1204 PROCESS_INFORMATION pi
;
1205 HANDLE filemap
= NULL
;
1207 if (wParam
== IDM_DUPSESS
) {
1209 * Allocate a file-mapping memory chunk for the
1212 SECURITY_ATTRIBUTES sa
;
1215 sa
.nLength
= sizeof(sa
);
1216 sa
.lpSecurityDescriptor
= NULL
;
1217 sa
.bInheritHandle
= TRUE
;
1218 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1221 0, sizeof(Config
), NULL
);
1223 p
= (Config
*) MapViewOfFile(filemap
,
1225 0, 0, sizeof(Config
));
1227 *p
= cfg
; /* structure copy */
1231 sprintf(c
, "putty &%p", filemap
);
1233 } else if (wParam
== IDM_SAVEDSESS
) {
1235 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1236 cl
= smalloc(16 + strlen(session
)); /* 8, but play safe */
1238 cl
= NULL
; /* not a very important failure mode */
1240 sprintf(cl
, "putty @%s", session
);
1246 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1248 si
.lpReserved
= NULL
;
1249 si
.lpDesktop
= NULL
;
1253 si
.lpReserved2
= NULL
;
1254 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1255 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1258 CloseHandle(filemap
);
1265 int prev_alwaysontop
= cfg
.alwaysontop
;
1266 int prev_sunken_edge
= cfg
.sunken_edge
;
1267 char oldlogfile
[FILENAME_MAX
];
1269 int need_setwpos
= FALSE
;
1270 int old_fwidth
, old_fheight
;
1272 strcpy(oldlogfile
, cfg
.logfilename
);
1273 oldlogtype
= cfg
.logtype
;
1274 old_fwidth
= font_width
;
1275 old_fheight
= font_height
;
1276 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1278 if (!do_reconfig(hwnd
))
1281 if (strcmp(oldlogfile
, cfg
.logfilename
) ||
1282 oldlogtype
!= cfg
.logtype
) {
1283 logfclose(); /* reset logging */
1287 just_reconfigged
= TRUE
;
1289 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1290 und_mode
= UND_FONT
;
1294 * Flush the line discipline's edit buffer in the
1295 * case where local editing has just been disabled.
1297 ldisc_send(NULL
, 0);
1305 /* Enable or disable the scroll bar, etc */
1307 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1308 LONG nexflag
, exflag
=
1309 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1312 if (cfg
.alwaysontop
!= prev_alwaysontop
) {
1313 if (cfg
.alwaysontop
) {
1314 nexflag
|= WS_EX_TOPMOST
;
1315 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1316 SWP_NOMOVE
| SWP_NOSIZE
);
1318 nexflag
&= ~(WS_EX_TOPMOST
);
1319 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1320 SWP_NOMOVE
| SWP_NOSIZE
);
1323 if (cfg
.sunken_edge
)
1324 nexflag
|= WS_EX_CLIENTEDGE
;
1326 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1332 nflg
&= ~WS_VSCROLL
;
1334 nflg
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1336 nflg
|= (WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1338 if (nflg
!= flag
|| nexflag
!= exflag
) {
1342 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1343 if (nexflag
!= exflag
)
1344 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1346 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
1348 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1349 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1350 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
1351 | SWP_FRAMECHANGED
);
1353 GetWindowRect(hwnd
, &wr
);
1354 GetClientRect(hwnd
, &cr
);
1356 wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1358 wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1359 need_setwpos
= TRUE
;
1363 if (cfg
.height
!= rows
||
1364 cfg
.width
!= cols
||
1365 old_fwidth
!= font_width
||
1366 old_fheight
!= font_height
||
1367 cfg
.savelines
!= savelines
||
1368 cfg
.sunken_edge
!= prev_sunken_edge
)
1369 need_setwpos
= TRUE
;
1371 if (IsZoomed(hwnd
)) {
1375 defered_resize
= TRUE
;
1377 GetClientRect(hwnd
, &cr
);
1378 w
= cr
.right
- cr
.left
;
1379 h
= cr
.bottom
- cr
.top
;
1383 h
= h
/ font_height
;
1387 term_size(h
, w
, cfg
.savelines
);
1388 InvalidateRect(hwnd
, NULL
, TRUE
);
1391 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1392 InvalidateRect(hwnd
, NULL
, TRUE
);
1394 SetWindowPos(hwnd
, NULL
, 0, 0,
1395 extra_width
+ font_width
* cfg
.width
,
1397 font_height
* cfg
.height
,
1398 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1399 SWP_NOMOVE
| SWP_NOZORDER
);
1403 if (cfg
.locksize
&& IsZoomed(hwnd
))
1405 set_title(cfg
.wintitle
);
1406 if (IsIconic(hwnd
)) {
1408 cfg
.win_name_always ? window_name
:
1423 back
->special(TS_AYT
);
1426 back
->special(TS_BRK
);
1429 back
->special(TS_SYNCH
);
1432 back
->special(TS_EC
);
1435 back
->special(TS_EL
);
1438 back
->special(TS_GA
);
1441 back
->special(TS_NOP
);
1444 back
->special(TS_ABORT
);
1447 back
->special(TS_AO
);
1450 back
->special(TS_IP
);
1453 back
->special(TS_SUSP
);
1456 back
->special(TS_EOR
);
1459 back
->special(TS_EOF
);
1465 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1466 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1471 #define X_POS(l) ((int)(short)LOWORD(l))
1472 #define Y_POS(l) ((int)(short)HIWORD(l))
1474 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1475 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1476 #define WHEEL_DELTA 120
1479 wheel_accumulator
+= (short) HIWORD(wParam
);
1480 wParam
= LOWORD(wParam
);
1482 /* process events when the threshold is reached */
1483 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
1486 /* reduce amount for next time */
1487 if (wheel_accumulator
> 0) {
1489 wheel_accumulator
-= WHEEL_DELTA
;
1490 } else if (wheel_accumulator
< 0) {
1492 wheel_accumulator
+= WHEEL_DELTA
;
1496 if (send_raw_mouse
) {
1497 /* send a mouse-down followed by a mouse up */
1500 TO_CHR_X(X_POS(lParam
)),
1501 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1502 wParam
& MK_CONTROL
);
1503 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1504 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1505 wParam
& MK_CONTROL
);
1507 /* trigger a scroll */
1509 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
1514 case WM_LBUTTONDOWN
:
1515 case WM_MBUTTONDOWN
:
1516 case WM_RBUTTONDOWN
:
1523 case WM_LBUTTONDOWN
:
1527 case WM_MBUTTONDOWN
:
1528 button
= MBT_MIDDLE
;
1531 case WM_RBUTTONDOWN
:
1540 button
= MBT_MIDDLE
;
1551 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1552 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
);
1555 term_mouse(button
, MA_RELEASE
,
1556 TO_CHR_X(X_POS(lParam
)),
1557 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1558 wParam
& MK_CONTROL
);
1566 * Add the mouse position and message time to the random
1569 noise_ultralight(lParam
);
1571 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1573 if (wParam
& MK_LBUTTON
)
1575 else if (wParam
& MK_MBUTTON
)
1576 b
= cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1578 b
= cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1579 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1580 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1581 wParam
& MK_CONTROL
);
1584 case WM_NCMOUSEMOVE
:
1586 noise_ultralight(lParam
);
1588 case WM_IGNORE_CLIP
:
1589 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1591 case WM_DESTROYCLIPBOARD
:
1594 ignore_clip
= FALSE
;
1600 hdc
= BeginPaint(hwnd
, &p
);
1602 SelectPalette(hdc
, pal
, TRUE
);
1603 RealizePalette(hdc
);
1605 term_paint(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1606 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1607 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
1608 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
1614 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1615 * but the only one that's likely to try to overload us is FD_READ.
1616 * This means buffering just one is fine.
1618 if (pending_netevent
)
1619 enact_pending_netevent();
1621 pending_netevent
= TRUE
;
1622 pend_netevent_wParam
= wParam
;
1623 pend_netevent_lParam
= lParam
;
1624 time(&last_movement
);
1628 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
1641 case WM_IGNORE_SIZE
:
1642 ignore_size
= TRUE
; /* don't panic on next WM_SIZE msg */
1644 case WM_ENTERSIZEMOVE
:
1647 need_backend_resize
= FALSE
;
1649 case WM_EXITSIZEMOVE
:
1652 if (need_backend_resize
)
1657 int width
, height
, w
, h
, ew
, eh
;
1658 LPRECT r
= (LPRECT
) lParam
;
1660 width
= r
->right
- r
->left
- extra_width
;
1661 height
= r
->bottom
- r
->top
- extra_height
;
1662 w
= (width
+ font_width
/ 2) / font_width
;
1665 h
= (height
+ font_height
/ 2) / font_height
;
1668 UpdateSizeTip(hwnd
, w
, h
);
1669 ew
= width
- w
* font_width
;
1670 eh
= height
- h
* font_height
;
1672 if (wParam
== WMSZ_LEFT
||
1673 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
1679 if (wParam
== WMSZ_TOP
||
1680 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
1690 /* break; (never reached) */
1692 if (wParam
== SIZE_MINIMIZED
) {
1694 cfg
.win_name_always ? window_name
: icon_name
);
1697 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
1698 SetWindowText(hwnd
, window_name
);
1700 int width
, height
, w
, h
;
1701 #if 0 /* we have fixed this using WM_SIZING now */
1705 width
= LOWORD(lParam
);
1706 height
= HIWORD(lParam
);
1707 w
= width
/ font_width
;
1710 h
= height
/ font_height
;
1713 #if 0 /* we have fixed this using WM_SIZING now */
1714 ew
= width
- w
* font_width
;
1715 eh
= height
- h
* font_height
;
1716 if (ew
!= 0 || eh
!= 0) {
1718 GetWindowRect(hwnd
, &r
);
1719 SendMessage(hwnd
, WM_IGNORE_SIZE
, 0, 0);
1720 SetWindowPos(hwnd
, NULL
, 0, 0,
1721 r
.right
- r
.left
- ew
, r
.bottom
- r
.top
- eh
,
1722 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
1725 if (w
!= cols
|| h
!= rows
|| just_reconfigged
) {
1727 term_size(h
, w
, cfg
.savelines
);
1729 * Don't call back->size in mid-resize. (To prevent
1730 * massive numbers of resize events getting sent
1731 * down the connection during an NT opaque drag.)
1736 need_backend_resize
= TRUE
;
1740 just_reconfigged
= FALSE
;
1743 if (wParam
== SIZE_RESTORED
&& defered_resize
) {
1744 defered_resize
= FALSE
;
1745 SetWindowPos(hwnd
, NULL
, 0, 0,
1746 extra_width
+ font_width
* cfg
.width
,
1747 extra_height
+ font_height
* cfg
.height
,
1748 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1749 SWP_NOMOVE
| SWP_NOZORDER
);
1751 ignore_size
= FALSE
;
1754 switch (LOWORD(wParam
)) {
1768 term_scroll(0, +rows
/ 2);
1771 term_scroll(0, -rows
/ 2);
1773 case SB_THUMBPOSITION
:
1775 term_scroll(1, HIWORD(wParam
));
1779 case WM_PALETTECHANGED
:
1780 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
1781 HDC hdc
= get_ctx();
1783 if (RealizePalette(hdc
) > 0)
1789 case WM_QUERYNEWPALETTE
:
1791 HDC hdc
= get_ctx();
1793 if (RealizePalette(hdc
) > 0)
1805 * Add the scan code and keypress timing to the random
1808 noise_ultralight(lParam
);
1811 * We don't do TranslateMessage since it disassociates the
1812 * resulting CHAR message from the KEYDOWN that sparked it,
1813 * which we occasionally don't want. Instead, we process
1814 * KEYDOWN, and call the Win32 translator functions so that
1815 * we get the translations under _our_ control.
1818 unsigned char buf
[20];
1821 if (wParam
== VK_PROCESSKEY
) {
1824 m
.message
= WM_KEYDOWN
;
1826 m
.lParam
= lParam
& 0xdfff;
1827 TranslateMessage(&m
);
1829 len
= TranslateKey(message
, wParam
, lParam
, buf
);
1831 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1832 ldisc_send(buf
, len
);
1839 case WM_INPUTLANGCHANGE
:
1841 /* wParam == Font number */
1842 /* lParam == Locale */
1844 HKL NewInputLocale
= (HKL
) lParam
;
1846 // lParam == GetKeyboardLayout(0);
1848 GetLocaleInfo(LOWORD(NewInputLocale
),
1849 LOCALE_IDEFAULTANSICODEPAGE
, lbuf
, sizeof(lbuf
));
1851 kbd_codepage
= atoi(lbuf
);
1855 if (wParam
& 0xFF00) {
1856 unsigned char buf
[2];
1859 buf
[0] = wParam
>> 8;
1860 lpage_send(kbd_codepage
, buf
, 2);
1862 char c
= (unsigned char) wParam
;
1863 lpage_send(kbd_codepage
, &c
, 1);
1869 * Nevertheless, we are prepared to deal with WM_CHAR
1870 * messages, should they crop up. So if someone wants to
1871 * post the things to us as part of a macro manoeuvre,
1872 * we're ready to cope.
1875 char c
= (unsigned char)wParam
;
1876 lpage_send(CP_ACP
, &c
, 1);
1880 if (send_raw_mouse
) {
1881 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
1886 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1890 * Move the system caret. (We maintain one, even though it's
1891 * invisible, for the benefit of blind people: apparently some
1892 * helper software tracks the system caret, so we should arrange to
1895 void sys_cursor(int x
, int y
)
1898 SetCaretPos(x
* font_width
, y
* font_height
);
1902 * Draw a line of text in the window, at given character
1903 * coordinates, in given attributes.
1905 * We are allowed to fiddle with the contents of `text'.
1907 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
1908 unsigned long attr
, int lattr
)
1911 int nfg
, nbg
, nfont
;
1914 int force_manual_underline
= 0;
1915 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
1916 int char_width
= fnt_width
;
1917 int text_adjust
= 0;
1918 static int *IpDx
= 0, IpDxLEN
= 0;
1920 if (attr
& ATTR_WIDE
)
1923 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
1925 if (len
> IpDxLEN
) {
1927 IpDx
= smalloc((len
+ 16) * sizeof(int));
1928 IpDxLEN
= (len
+ 16);
1930 for (i
= 0; i
< IpDxLEN
; i
++)
1931 IpDx
[i
] = char_width
;
1937 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
1938 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
1939 attr
^= ATTR_CUR_XOR
;
1943 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
1944 /* Assume a poorman font is borken in other ways too. */
1954 nfont
|= FONT_WIDE
+ FONT_HIGH
;
1958 /* Special hack for the VT100 linedraw glyphs. */
1959 if ((attr
& CSET_MASK
) == 0x2300) {
1960 if (!dbcs_screenfont
&&
1961 text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
1962 switch ((unsigned char) (text
[0])) {
1964 text_adjust
= -2 * font_height
/ 5;
1967 text_adjust
= -1 * font_height
/ 5;
1970 text_adjust
= font_height
/ 5;
1973 text_adjust
= 2 * font_height
/ 5;
1976 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
1979 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
1980 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
1981 if (attr
& ATTR_UNDER
) {
1982 attr
&= ~ATTR_UNDER
;
1983 force_manual_underline
= 1;
1988 /* Anything left as an original character set is unprintable. */
1989 if (DIRECT_CHAR(attr
)) {
1992 memset(text
, 0xFF, len
);
1996 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
1999 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2000 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2001 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2003 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2004 nfont
|= FONT_UNDERLINE
;
2005 another_font(nfont
);
2006 if (!fonts
[nfont
]) {
2007 if (nfont
& FONT_UNDERLINE
)
2008 force_manual_underline
= 1;
2009 /* Don't do the same for manual bold, it could be bad news. */
2011 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2013 another_font(nfont
);
2015 nfont
= FONT_NORMAL
;
2016 if (attr
& ATTR_REVERSE
) {
2021 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2023 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2027 SelectObject(hdc
, fonts
[nfont
]);
2028 SetTextColor(hdc
, fg
);
2029 SetBkColor(hdc
, bg
);
2030 SetBkMode(hdc
, OPAQUE
);
2033 line_box
.right
= x
+ char_width
* len
;
2034 line_box
.bottom
= y
+ font_height
;
2036 /* We're using a private area for direct to font. (512 chars.) */
2037 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2038 /* Ho Hum, dbcs fonts are a PITA! */
2039 /* To display on W9x I have to convert to UCS */
2040 static wchar_t *uni_buf
= 0;
2041 static int uni_len
= 0;
2043 if (len
> uni_len
) {
2045 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2047 nlen
= MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2048 text
, len
, uni_buf
, uni_len
);
2054 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2055 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, 0);
2056 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2057 SetBkMode(hdc
, TRANSPARENT
);
2058 ExtTextOutW(hdc
, x
- 1,
2059 y
- font_height
* (lattr
==
2060 LATTR_BOT
) + text_adjust
,
2061 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, 0);
2063 } else if (DIRECT_FONT(attr
)) {
2065 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2066 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2067 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2068 SetBkMode(hdc
, TRANSPARENT
);
2070 /* GRR: This draws the character outside it's box and can leave
2071 * 'droppings' even with the clip box! I suppose I could loop it
2072 * one character at a time ... yuk.
2074 * Or ... I could do a test print with "W", and use +1 or -1 for this
2075 * shift depending on if the leftmost column is blank...
2077 ExtTextOut(hdc
, x
- 1,
2078 y
- font_height
* (lattr
==
2079 LATTR_BOT
) + text_adjust
,
2080 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2083 /* And 'normal' unicode characters */
2084 static WCHAR
*wbuf
= NULL
;
2085 static int wlen
= 0;
2090 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2092 for (i
= 0; i
< len
; i
++)
2093 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2096 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2097 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2099 /* And the shadow bold hack. */
2100 if (bold_mode
== BOLD_SHADOW
) {
2101 SetBkMode(hdc
, TRANSPARENT
);
2102 ExtTextOutW(hdc
, x
- 1,
2103 y
- font_height
* (lattr
==
2104 LATTR_BOT
) + text_adjust
,
2105 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2108 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2109 (und_mode
== UND_LINE
2110 && (attr
& ATTR_UNDER
)))) {
2113 if (lattr
== LATTR_BOT
)
2114 dec
= dec
* 2 - font_height
;
2116 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2117 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2118 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2119 oldpen
= SelectObject(hdc
, oldpen
);
2120 DeleteObject(oldpen
);
2124 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2125 unsigned long attr
, int lattr
)
2131 int ctype
= cfg
.cursor_type
;
2133 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2134 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2135 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2139 attr
|= TATTR_RIGHTCURS
;
2142 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2143 if (attr
& ATTR_WIDE
)
2148 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2151 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2152 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2153 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2154 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2155 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2156 Polyline(hdc
, pts
, 5);
2157 oldpen
= SelectObject(hdc
, oldpen
);
2158 DeleteObject(oldpen
);
2159 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2160 int startx
, starty
, dx
, dy
, length
, i
;
2163 starty
= y
+ descent
;
2166 length
= char_width
;
2169 if (attr
& TATTR_RIGHTCURS
)
2170 xadjust
= char_width
- 1;
2171 startx
= x
+ xadjust
;
2175 length
= font_height
;
2177 if (attr
& TATTR_ACTCURS
) {
2180 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2181 MoveToEx(hdc
, startx
, starty
, NULL
);
2182 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2183 oldpen
= SelectObject(hdc
, oldpen
);
2184 DeleteObject(oldpen
);
2186 for (i
= 0; i
< length
; i
++) {
2188 SetPixel(hdc
, startx
, starty
, colours
[23]);
2198 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2199 * codes. Returns number of bytes used or zero to drop the message
2200 * or -1 to forward the message to windows.
2202 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2203 unsigned char *output
)
2206 int scan
, left_alt
= 0, key_down
, shift_state
;
2208 unsigned char *p
= output
;
2209 static int alt_state
= 0;
2210 static int alt_sum
= 0;
2212 HKL kbd_layout
= GetKeyboardLayout(0);
2214 static WORD keys
[3];
2215 static int compose_char
= 0;
2216 static WPARAM compose_key
= 0;
2218 r
= GetKeyboardState(keystate
);
2220 memset(keystate
, 0, sizeof(keystate
));
2223 #define SHOW_TOASCII_RESULT
2224 { /* Tell us all about key events */
2225 static BYTE oldstate
[256];
2226 static int first
= 1;
2230 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2233 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2235 } else if ((HIWORD(lParam
) & KF_UP
)
2236 && scan
== (HIWORD(lParam
) & 0xFF)) {
2240 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2241 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2254 debug(("VK_%02x", wParam
));
2256 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2258 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2260 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2261 if (ch
>= ' ' && ch
<= '~')
2262 debug((", '%c'", ch
));
2264 debug((", $%02x", ch
));
2267 debug((", KB0=%02x", keys
[0]));
2269 debug((", KB1=%02x", keys
[1]));
2271 debug((", KB2=%02x", keys
[2]));
2273 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2275 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2277 if ((HIWORD(lParam
) & KF_EXTENDED
))
2279 if ((HIWORD(lParam
) & KF_UP
))
2283 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2284 else if ((HIWORD(lParam
) & KF_UP
))
2285 oldstate
[wParam
& 0xFF] ^= 0x80;
2287 oldstate
[wParam
& 0xFF] ^= 0x81;
2289 for (ch
= 0; ch
< 256; ch
++)
2290 if (oldstate
[ch
] != keystate
[ch
])
2291 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2293 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2297 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2298 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2302 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2303 if ((cfg
.funky_type
== 3 ||
2304 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2305 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2307 wParam
= VK_EXECUTE
;
2309 /* UnToggle NUMLock */
2310 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2311 keystate
[VK_NUMLOCK
] ^= 1;
2314 /* And write back the 'adjusted' state */
2315 SetKeyboardState(keystate
);
2318 /* Disable Auto repeat if required */
2319 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2322 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2325 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2327 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2328 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2329 if (cfg
.ctrlaltkeys
)
2330 keystate
[VK_MENU
] = 0;
2332 keystate
[VK_RMENU
] = 0x80;
2337 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2338 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2339 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2341 /* Note if AltGr was pressed and if it was used as a compose key */
2342 if (!compose_state
) {
2343 compose_key
= 0x100;
2344 if (cfg
.compose_key
) {
2345 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2346 compose_key
= wParam
;
2348 if (wParam
== VK_APPS
)
2349 compose_key
= wParam
;
2352 if (wParam
== compose_key
) {
2353 if (compose_state
== 0
2354 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2356 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2360 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
2364 * Record that we pressed key so the scroll window can be reset, but
2365 * be careful to avoid Shift-UP/Down
2367 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
2371 /* Make sure we're not pasting */
2375 if (compose_state
> 1 && left_alt
)
2378 /* Sanitize the number pad if not using a PC NumPad */
2379 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2380 && cfg
.funky_type
!= 2)
2381 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
2382 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
2386 nParam
= VK_NUMPAD0
;
2389 nParam
= VK_NUMPAD1
;
2392 nParam
= VK_NUMPAD2
;
2395 nParam
= VK_NUMPAD3
;
2398 nParam
= VK_NUMPAD4
;
2401 nParam
= VK_NUMPAD5
;
2404 nParam
= VK_NUMPAD6
;
2407 nParam
= VK_NUMPAD7
;
2410 nParam
= VK_NUMPAD8
;
2413 nParam
= VK_NUMPAD9
;
2416 nParam
= VK_DECIMAL
;
2420 if (keystate
[VK_NUMLOCK
] & 1)
2427 /* If a key is pressed and AltGr is not active */
2428 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
2429 /* Okay, prepare for most alts then ... */
2433 /* Lets see if it's a pattern we know all about ... */
2434 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2435 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2438 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2439 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
2442 if (wParam
== VK_INSERT
&& shift_state
== 1) {
2443 term_mouse(MBT_PASTE
, MA_CLICK
, 0, 0, 0, 0);
2444 term_mouse(MBT_PASTE
, MA_RELEASE
, 0, 0, 0, 0);
2447 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
2450 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
2452 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
2453 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
2456 /* Control-Numlock for app-keypad mode switch */
2457 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
2458 app_keypad_keys
^= 1;
2462 /* Nethack keypad */
2463 if (cfg
.nethack_keypad
&& !left_alt
) {
2466 *p
++ = shift_state ?
'B' : 'b';
2469 *p
++ = shift_state ?
'J' : 'j';
2472 *p
++ = shift_state ?
'N' : 'n';
2475 *p
++ = shift_state ?
'H' : 'h';
2478 *p
++ = shift_state ?
'.' : '.';
2481 *p
++ = shift_state ?
'L' : 'l';
2484 *p
++ = shift_state ?
'Y' : 'y';
2487 *p
++ = shift_state ?
'K' : 'k';
2490 *p
++ = shift_state ?
'U' : 'u';
2495 /* Application Keypad */
2499 if (cfg
.funky_type
== 3 ||
2500 (cfg
.funky_type
<= 1 &&
2501 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
2515 if (app_keypad_keys
&& !cfg
.no_applic_k
)
2552 if (cfg
.funky_type
== 2) {
2557 } else if (shift_state
)
2564 if (cfg
.funky_type
== 2)
2568 if (cfg
.funky_type
== 2)
2572 if (cfg
.funky_type
== 2)
2577 if (HIWORD(lParam
) & KF_EXTENDED
)
2583 if (xkey
>= 'P' && xkey
<= 'S')
2584 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
2586 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
2588 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
2593 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
2594 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
2598 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
2604 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
2608 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
2612 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
2617 if (wParam
== VK_PAUSE
) { /* Break/Pause */
2622 /* Control-2 to Control-8 are special */
2623 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
2624 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
2627 if (shift_state
== 2 && wParam
== 0xBD) {
2631 if (shift_state
== 2 && wParam
== 0xDF) {
2635 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
2642 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2643 * for integer decimal nn.)
2645 * We also deal with the weird ones here. Linux VCs replace F1
2646 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2647 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2653 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
2656 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
2659 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
2662 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
2665 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
2668 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
2671 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
2674 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
2677 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
2680 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
2731 /* Reorder edit keys to physical order */
2732 if (cfg
.funky_type
== 3 && code
<= 6)
2733 code
= "\0\2\1\4\5\3\6"[code
];
2735 if (vt52_mode
&& code
> 0 && code
<= 6) {
2736 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
2740 if (cfg
.funky_type
== 5 && code
>= 11 && code
<= 24) {
2741 p
+= sprintf((char *) p
, "\x1B[%c", code
+ 'M' - 11);
2744 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
2751 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
2754 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
2757 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
2758 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
2761 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
2763 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
2765 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
2768 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
2769 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
2773 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
2778 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2779 * some reason seems to send VK_CLEAR to Windows...).
2802 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
2804 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
2805 /* VT100 & VT102 manuals both state the app cursor keys
2806 * only work if the app keypad is on.
2808 if (!app_keypad_keys
)
2810 /* Useful mapping of Ctrl-arrows */
2811 if (shift_state
== 2)
2815 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
2817 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
2824 * Finally, deal with Return ourselves. (Win95 seems to
2825 * foul it up when Alt is pressed, for some reason.)
2827 if (wParam
== VK_RETURN
) { /* Return */
2833 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
2834 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
2839 /* Okay we've done everything interesting; let windows deal with
2840 * the boring stuff */
2842 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
2843 #ifdef SHOW_TOASCII_RESULT
2844 if (r
== 1 && !key_down
) {
2846 if (utf
|| dbcs_screenfont
)
2847 debug((", (U+%04x)", alt_sum
));
2849 debug((", LCH(%d)", alt_sum
));
2851 debug((", ACH(%d)", keys
[0]));
2856 for (r1
= 0; r1
< r
; r1
++) {
2857 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
2865 for (i
= 0; i
< r
; i
++) {
2866 unsigned char ch
= (unsigned char) keys
[i
];
2868 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
2873 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
2877 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
2878 MessageBeep(MB_ICONHAND
);
2882 luni_send(&keybuf
, 1);
2890 if (utf
|| dbcs_screenfont
) {
2892 luni_send(&keybuf
, 1);
2894 ch
= (char) alt_sum
;
2899 lpage_send(kbd_codepage
, &ch
, 1);
2901 static char cbuf
[] = "\033 ";
2903 lpage_send(kbd_codepage
, cbuf
+ !left_alt
,
2908 /* This is so the ALT-Numpad and dead keys work correctly. */
2913 /* If we're definitly not building up an ALT-54321 then clear it */
2916 /* If we will be using alt_sum fix the 256s */
2917 else if (keys
[0] && (utf
|| dbcs_screenfont
))
2921 /* ALT alone may or may not want to bring up the System menu */
2922 if (wParam
== VK_MENU
) {
2924 if (message
== WM_SYSKEYDOWN
)
2926 else if (message
== WM_SYSKEYUP
&& alt_state
)
2927 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
2928 if (message
== WM_SYSKEYUP
)
2938 void set_title(char *title
)
2941 window_name
= smalloc(1 + strlen(title
));
2942 strcpy(window_name
, title
);
2943 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
2944 SetWindowText(hwnd
, title
);
2947 void set_icon(char *title
)
2950 icon_name
= smalloc(1 + strlen(title
));
2951 strcpy(icon_name
, title
);
2952 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
2953 SetWindowText(hwnd
, title
);
2956 void set_sbar(int total
, int start
, int page
)
2963 si
.cbSize
= sizeof(si
);
2964 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
2966 si
.nMax
= total
- 1;
2970 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
2973 Context
get_ctx(void)
2979 SelectPalette(hdc
, pal
, FALSE
);
2985 void free_ctx(Context ctx
)
2987 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
2988 ReleaseDC(hwnd
, ctx
);
2991 static void real_palette_set(int n
, int r
, int g
, int b
)
2994 logpal
->palPalEntry
[n
].peRed
= r
;
2995 logpal
->palPalEntry
[n
].peGreen
= g
;
2996 logpal
->palPalEntry
[n
].peBlue
= b
;
2997 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
2998 colours
[n
] = PALETTERGB(r
, g
, b
);
2999 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3001 colours
[n
] = RGB(r
, g
, b
);
3004 void palette_set(int n
, int r
, int g
, int b
)
3006 static const int first
[21] = {
3007 0, 2, 4, 6, 8, 10, 12, 14,
3008 1, 3, 5, 7, 9, 11, 13, 15,
3011 real_palette_set(first
[n
], r
, g
, b
);
3013 real_palette_set(first
[n
] + 1, r
, g
, b
);
3015 HDC hdc
= get_ctx();
3016 UnrealizeObject(pal
);
3017 RealizePalette(hdc
);
3022 void palette_reset(void)
3026 for (i
= 0; i
< NCOLOURS
; i
++) {
3028 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3029 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3030 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3031 logpal
->palPalEntry
[i
].peFlags
= 0;
3032 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3033 defpal
[i
].rgbtGreen
,
3034 defpal
[i
].rgbtBlue
);
3036 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3037 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3042 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3044 RealizePalette(hdc
);
3049 void write_aclip(char *data
, int len
, int must_deselect
)
3054 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3057 lock
= GlobalLock(clipdata
);
3060 memcpy(lock
, data
, len
);
3061 ((unsigned char *) lock
)[len
] = 0;
3062 GlobalUnlock(clipdata
);
3065 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3067 if (OpenClipboard(hwnd
)) {
3069 SetClipboardData(CF_TEXT
, clipdata
);
3072 GlobalFree(clipdata
);
3075 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3079 * Note: unlike write_aclip() this will not append a nul.
3081 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3088 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3090 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3091 len
* sizeof(wchar_t));
3092 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3094 if (!clipdata
|| !clipdata2
) {
3096 GlobalFree(clipdata
);
3098 GlobalFree(clipdata2
);
3101 if (!(lock
= GlobalLock(clipdata
)))
3103 if (!(lock2
= GlobalLock(clipdata2
)))
3106 memcpy(lock
, data
, len
* sizeof(wchar_t));
3107 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3109 GlobalUnlock(clipdata
);
3110 GlobalUnlock(clipdata2
);
3113 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3115 if (OpenClipboard(hwnd
)) {
3117 SetClipboardData(CF_UNICODETEXT
, clipdata
);
3118 SetClipboardData(CF_TEXT
, clipdata2
);
3121 GlobalFree(clipdata
);
3122 GlobalFree(clipdata2
);
3126 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3129 void get_clip(wchar_t ** p
, int *len
)
3131 static HGLOBAL clipdata
= NULL
;
3132 static wchar_t *converted
= 0;
3141 GlobalUnlock(clipdata
);
3144 } else if (OpenClipboard(NULL
)) {
3145 if (clipdata
= GetClipboardData(CF_UNICODETEXT
)) {
3147 *p
= GlobalLock(clipdata
);
3149 for (p2
= *p
; *p2
; p2
++);
3153 } else if (clipdata
= GetClipboardData(CF_TEXT
)) {
3157 s
= GlobalLock(clipdata
);
3158 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
3159 *p
= converted
= smalloc(i
* sizeof(wchar_t));
3160 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
3173 * Move `lines' lines from position `from' to position `to' in the
3176 void optimised_move(int to
, int from
, int lines
)
3181 min
= (to
< from ? to
: from
);
3182 max
= to
+ from
- min
;
3185 r
.right
= cols
* font_width
;
3186 r
.top
= min
* font_height
;
3187 r
.bottom
= (max
+ lines
) * font_height
;
3188 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
3193 * Print a message box and perform a fatal exit.
3195 void fatalbox(char *fmt
, ...)
3201 vsprintf(stuff
, fmt
, ap
);
3203 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
3212 if (mode
== BELL_DEFAULT
) {
3214 * For MessageBeep style bells, we want to be careful of
3215 * timing, because they don't have the nice property of
3216 * PlaySound bells that each one cancels the previous
3217 * active one. So we limit the rate to one per 50ms or so.
3219 static long lastbeep
= 0;
3222 beepdiff
= GetTickCount() - lastbeep
;
3223 if (beepdiff
>= 0 && beepdiff
< 50)
3227 * The above MessageBeep call takes time, so we record the
3228 * time _after_ it finishes rather than before it starts.
3230 lastbeep
= GetTickCount();
3231 } else if (mode
== BELL_WAVEFILE
) {
3232 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
3233 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
3234 sprintf(buf
, "Unable to play sound file\n%s\n"
3235 "Using default sound instead", cfg
.bell_wavefile
);
3236 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
3237 MB_OK
| MB_ICONEXCLAMATION
);
3238 cfg
.beep
= BELL_DEFAULT
;