20 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
26 #define IDM_SHOWLOG 0x0010
27 #define IDM_NEWSESS 0x0020
28 #define IDM_DUPSESS 0x0030
29 #define IDM_RECONF 0x0040
30 #define IDM_CLRSB 0x0050
31 #define IDM_RESET 0x0060
32 #define IDM_TEL_AYT 0x0070
33 #define IDM_TEL_BRK 0x0080
34 #define IDM_TEL_SYNCH 0x0090
35 #define IDM_TEL_EC 0x00a0
36 #define IDM_TEL_EL 0x00b0
37 #define IDM_TEL_GA 0x00c0
38 #define IDM_TEL_NOP 0x00d0
39 #define IDM_TEL_ABORT 0x00e0
40 #define IDM_TEL_AO 0x00f0
41 #define IDM_TEL_IP 0x0100
42 #define IDM_TEL_SUSP 0x0110
43 #define IDM_TEL_EOR 0x0120
44 #define IDM_TEL_EOF 0x0130
45 #define IDM_ABOUT 0x0140
46 #define IDM_SAVEDSESS 0x0150
47 #define IDM_COPYALL 0x0160
48 #define IDM_FULLSCREEN 0x0170
50 #define IDM_SESSLGP 0x0250 /* log type printable */
51 #define IDM_SESSLGA 0x0260 /* log type all chars */
52 #define IDM_SESSLGE 0x0270 /* log end */
53 #define IDM_SAVED_MIN 0x1000
54 #define IDM_SAVED_MAX 0x2000
56 #define WM_IGNORE_CLIP (WM_XUSER + 2)
58 /* Needed for Chinese support and apparently not always defined. */
60 #define VK_PROCESSKEY 0xE5
63 /* Needed for mouse wheel support and not defined in earlier SDKs. */
65 #define WM_MOUSEWHEEL 0x020A
68 static LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
69 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
70 unsigned char *output
);
71 static void cfgtopalette(void);
72 static void init_palette(void);
73 static void init_fonts(int, int);
74 static void another_font(int);
75 static void deinit_fonts(void);
77 /* Window layout information */
78 static void reset_window(int);
79 static int full_screen
= 0;
80 static int extra_width
, extra_height
;
81 static int font_width
, font_height
, font_dualwidth
;
82 static int offset_width
, offset_height
;
83 static int was_zoomed
= 0;
84 static int prev_rows
, prev_cols
;
86 static int pending_netevent
= 0;
87 static WPARAM pend_netevent_wParam
= 0;
88 static LPARAM pend_netevent_lParam
= 0;
89 static void enact_pending_netevent(void);
90 static void flash_window(int mode
);
91 static void flip_full_screen(void);
93 static time_t last_movement
= 0;
97 #define FONT_UNDERLINE 2
98 #define FONT_BOLDUND 3
99 #define FONT_WIDE 0x04
100 #define FONT_HIGH 0x08
101 #define FONT_NARROW 0x10
103 #define FONT_OEM 0x20
104 #define FONT_OEMBOLD 0x21
105 #define FONT_OEMUND 0x22
106 #define FONT_OEMBOLDUND 0x23
108 #define FONT_MAXNO 0x2F
110 static HFONT fonts
[FONT_MAXNO
];
111 static LOGFONT lfont
;
112 static int fontflag
[FONT_MAXNO
];
114 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
122 static COLORREF colours
[NCOLOURS
];
124 static LPLOGPALETTE logpal
;
125 static RGBTRIPLE defpal
[NCOLOURS
];
129 static HBITMAP caretbm
;
131 static int dbltime
, lasttime
, lastact
;
132 static Mouse_Button lastbtn
;
134 /* this allows xterm-style mouse handling. */
135 static int send_raw_mouse
= 0;
136 static int wheel_accumulator
= 0;
138 static char *window_name
, *icon_name
;
140 static int compose_state
= 0;
142 static OSVERSIONINFO osVersion
;
144 /* Dummy routine, only required in plink. */
145 void ldisc_update(int echo
, int edit
)
149 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
151 static char appname
[] = "PuTTY";
156 int guess_width
, guess_height
;
159 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
161 winsock_ver
= MAKEWORD(1, 1);
162 if (WSAStartup(winsock_ver
, &wsadata
)) {
163 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
164 MB_OK
| MB_ICONEXCLAMATION
);
167 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
168 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
169 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
173 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
176 InitCommonControls();
178 /* Ensure a Maximize setting in Explorer doesn't maximise the
183 ZeroMemory(&osVersion
, sizeof(osVersion
));
184 osVersion
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
185 if (!GetVersionEx ( (OSVERSIONINFO
*) &osVersion
)) {
186 MessageBox(NULL
, "Windows refuses to report a version",
187 "PuTTY Fatal Error", MB_OK
| MB_ICONEXCLAMATION
);
193 * Process the command line.
198 default_protocol
= DEFAULT_PROTOCOL
;
199 default_port
= DEFAULT_PORT
;
200 cfg
.logtype
= LGTYP_NONE
;
202 do_defaults(NULL
, &cfg
);
205 while (*p
&& isspace(*p
))
209 * Process command line options first. Yes, this can be
210 * done better, and it will be as soon as I have the
214 char *q
= p
+ strcspn(p
, " \t");
217 tolower(p
[0]) == 's' &&
218 tolower(p
[1]) == 's' && tolower(p
[2]) == 'h') {
219 default_protocol
= cfg
.protocol
= PROT_SSH
;
220 default_port
= cfg
.port
= 22;
221 } else if (q
== p
+ 7 &&
222 tolower(p
[0]) == 'c' &&
223 tolower(p
[1]) == 'l' &&
224 tolower(p
[2]) == 'e' &&
225 tolower(p
[3]) == 'a' &&
226 tolower(p
[4]) == 'n' &&
227 tolower(p
[5]) == 'u' && tolower(p
[6]) == 'p') {
229 * `putty -cleanup'. Remove all registry entries
230 * associated with PuTTY, and also find and delete
231 * the random seed file.
234 "This procedure will remove ALL Registry\n"
235 "entries associated with PuTTY, and will\n"
236 "also remove the PuTTY random seed file.\n"
238 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
239 "SESSIONS. Are you really sure you want\n"
242 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
247 p
= q
+ strspn(q
, " \t");
251 * An initial @ means to activate a saved session.
255 while (i
> 1 && isspace(p
[i
- 1]))
258 do_defaults(p
+ 1, &cfg
);
259 if (!*cfg
.host
&& !do_config()) {
263 } else if (*p
== '&') {
265 * An initial & means we've been given a command line
266 * containing the hex value of a HANDLE for a file
267 * mapping object, which we must then extract as a
272 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
273 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
274 0, 0, sizeof(Config
))) != NULL
) {
277 CloseHandle(filemap
);
278 } else if (!do_config()) {
285 * If the hostname starts with "telnet:", set the
286 * protocol to Telnet and process the string as a
289 if (!strncmp(q
, "telnet:", 7)) {
293 if (q
[0] == '/' && q
[1] == '/')
295 cfg
.protocol
= PROT_TELNET
;
297 while (*p
&& *p
!= ':' && *p
!= '/')
306 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
307 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
309 while (*p
&& !isspace(*p
))
313 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
314 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
315 while (*p
&& isspace(*p
))
330 * Trim leading whitespace off the hostname if it's there.
333 int space
= strspn(cfg
.host
, " \t");
334 memmove(cfg
.host
, cfg
.host
+space
, 1+strlen(cfg
.host
)-space
);
337 /* See if host is of the form user@host */
338 if (cfg
.host
[0] != '\0') {
339 char *atsign
= strchr(cfg
.host
, '@');
340 /* Make sure we're not overflowing the user field */
342 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
343 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
344 cfg
.username
[atsign
- cfg
.host
] = '\0';
346 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
351 * Trim a colon suffix off the hostname if it's there.
353 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
357 * Select protocol. This is farmed out into a table in a
358 * separate file to enable an ssh-free variant.
363 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
364 if (backends
[i
].protocol
== cfg
.protocol
) {
365 back
= backends
[i
].backend
;
369 MessageBox(NULL
, "Unsupported protocol number found",
370 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
376 /* Check for invalid Port number (i.e. zero) */
378 MessageBox(NULL
, "Invalid Port Number",
379 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
386 wndclass
.lpfnWndProc
= WndProc
;
387 wndclass
.cbClsExtra
= 0;
388 wndclass
.cbWndExtra
= 0;
389 wndclass
.hInstance
= inst
;
390 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
391 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
392 wndclass
.hbrBackground
= NULL
;
393 wndclass
.lpszMenuName
= NULL
;
394 wndclass
.lpszClassName
= appname
;
396 RegisterClass(&wndclass
);
401 savelines
= cfg
.savelines
;
407 * Guess some defaults for the window size. This all gets
408 * updated later, so we don't really care too much. However, we
409 * do want the font width/height guesses to correspond to a
410 * large font rather than a small one...
417 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
418 guess_width
= extra_width
+ font_width
* cols
;
419 guess_height
= extra_height
+ font_height
* rows
;
422 HWND w
= GetDesktopWindow();
423 GetWindowRect(w
, &r
);
424 if (guess_width
> r
.right
- r
.left
)
425 guess_width
= r
.right
- r
.left
;
426 if (guess_height
> r
.bottom
- r
.top
)
427 guess_height
= r
.bottom
- r
.top
;
431 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
434 winmode
&= ~(WS_VSCROLL
);
435 if (cfg
.resize_action
== RESIZE_DISABLED
)
436 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
438 exwinmode
|= WS_EX_TOPMOST
;
440 exwinmode
|= WS_EX_CLIENTEDGE
;
441 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
442 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
443 guess_width
, guess_height
,
444 NULL
, NULL
, inst
, NULL
);
448 * Initialise the fonts, simultaneously correcting the guesses
449 * for font_{width,height}.
454 * Correct the guesses for extra_{width,height}.
458 GetWindowRect(hwnd
, &wr
);
459 GetClientRect(hwnd
, &cr
);
460 offset_width
= offset_height
= cfg
.window_border
;
461 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
462 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
466 * Resize the window, now we know what size we _really_ want it
469 guess_width
= extra_width
+ font_width
* cols
;
470 guess_height
= extra_height
+ font_height
* rows
;
471 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
472 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
475 * Set up a caret bitmap, with no content.
479 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
480 bits
= smalloc(size
);
481 memset(bits
, 0, size
);
482 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
485 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
488 * Initialise the scroll bar.
493 si
.cbSize
= sizeof(si
);
494 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
499 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
503 * Start up the telnet connection.
507 char msg
[1024], *title
;
510 error
= back
->init(cfg
.host
, cfg
.port
, &realhost
);
512 sprintf(msg
, "Unable to open connection to\n"
513 "%.800s\n" "%s", cfg
.host
, error
);
514 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
517 window_name
= icon_name
= NULL
;
519 title
= cfg
.wintitle
;
521 sprintf(msg
, "%s - PuTTY", realhost
);
529 session_closed
= FALSE
;
532 * Prepare the mouse handler.
534 lastact
= MA_NOTHING
;
535 lastbtn
= MBT_NOTHING
;
536 dbltime
= GetDoubleClickTime();
539 * Set up the session-control options on the system menu.
542 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
546 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
547 if (cfg
.protocol
== PROT_TELNET
) {
549 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
550 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
551 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
552 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
553 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
554 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
555 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
556 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
557 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
558 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
559 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
560 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
561 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
562 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
563 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
564 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
565 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
567 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
569 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
570 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
571 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
572 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
575 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
576 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
578 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
579 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
580 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
581 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
582 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
583 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
584 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
585 AppendMenu(m
, MF_ENABLED
, IDM_FULLSCREEN
, "&Full Screen");
586 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
587 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
591 * Finally show the window!
593 ShowWindow(hwnd
, show
);
594 SetForegroundWindow(hwnd
);
597 * Open the initial log file if there is one.
602 * Set the palette up.
608 has_focus
= (GetForegroundWindow() == hwnd
);
611 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
612 int timer_id
= 0, long_timer
= 0;
614 while (msg
.message
!= WM_QUIT
) {
615 /* Sometimes DispatchMessage calls routines that use their own
616 * GetMessage loop, setup this timer so we get some control back.
618 * Also call term_update() from the timer so that if the host
619 * is sending data flat out we still do redraws.
621 if (timer_id
&& long_timer
) {
622 KillTimer(hwnd
, timer_id
);
623 long_timer
= timer_id
= 0;
626 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
627 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
628 DispatchMessage(&msg
);
630 /* Make sure we blink everything that needs it. */
633 /* Send the paste buffer if there's anything to send */
636 /* If there's nothing new in the queue then we can do everything
637 * we've delayed, reading the socket, writing, and repainting
640 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
643 if (pending_netevent
) {
644 enact_pending_netevent();
646 /* Force the cursor blink on */
649 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
653 /* Okay there is now nothing to do so we make sure the screen is
654 * completely up to date then tell windows to call us in a little
658 KillTimer(hwnd
, timer_id
);
666 flash_window(1); /* maintain */
669 /* Hmm, term_update didn't want to do an update too soon ... */
670 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
672 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
674 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
677 /* There's no point rescanning everything in the message queue
678 * so we do an apparently unnecessary wait here
681 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
695 if (cfg
.protocol
== PROT_SSH
) {
706 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
708 char *do_select(SOCKET skt
, int startup
)
713 events
= (FD_CONNECT
| FD_READ
| FD_WRITE
|
714 FD_OOB
| FD_CLOSE
| FD_ACCEPT
);
719 return "do_select(): internal error (hwnd==NULL)";
720 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
721 switch (WSAGetLastError()) {
723 return "Network is down";
725 return "WSAAsyncSelect(): unknown error";
732 * set or clear the "raw mouse message" mode
734 void set_raw_mouse_mode(int activate
)
736 send_raw_mouse
= activate
;
737 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
741 * Print a message box and close the connection.
743 void connection_fatal(char *fmt
, ...)
749 vsprintf(stuff
, fmt
, ap
);
751 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
752 if (cfg
.close_on_exit
== COE_ALWAYS
)
755 session_closed
= TRUE
;
756 SetWindowText(hwnd
, "PuTTY (inactive)");
761 * Actually do the job requested by a WM_NETEVENT
763 static void enact_pending_netevent(void)
765 static int reentering
= 0;
766 extern int select_result(WPARAM
, LPARAM
);
770 return; /* don't unpend the pending */
772 pending_netevent
= FALSE
;
775 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
778 if (ret
== 0 && !session_closed
) {
779 /* Abnormal exits will already have set session_closed and taken
780 * appropriate action. */
781 if (cfg
.close_on_exit
== COE_ALWAYS
||
782 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
784 session_closed
= TRUE
;
785 SetWindowText(hwnd
, "PuTTY (inactive)");
786 MessageBox(hwnd
, "Connection closed by remote host",
787 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
793 * Copy the colour palette from the configuration data into defpal.
794 * This is non-trivial because the colour indices are different.
796 static void cfgtopalette(void)
799 static const int ww
[] = {
800 6, 7, 8, 9, 10, 11, 12, 13,
801 14, 15, 16, 17, 18, 19, 20, 21,
802 0, 1, 2, 3, 4, 4, 5, 5
805 for (i
= 0; i
< 24; i
++) {
807 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
808 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
809 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
814 * Set up the colour palette.
816 static void init_palette(void)
819 HDC hdc
= GetDC(hwnd
);
821 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
822 logpal
= smalloc(sizeof(*logpal
)
823 - sizeof(logpal
->palPalEntry
)
824 + NCOLOURS
* sizeof(PALETTEENTRY
));
825 logpal
->palVersion
= 0x300;
826 logpal
->palNumEntries
= NCOLOURS
;
827 for (i
= 0; i
< NCOLOURS
; i
++) {
828 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
829 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
830 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
831 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
833 pal
= CreatePalette(logpal
);
835 SelectPalette(hdc
, pal
, FALSE
);
837 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
840 ReleaseDC(hwnd
, hdc
);
843 for (i
= 0; i
< NCOLOURS
; i
++)
844 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
848 for (i
= 0; i
< NCOLOURS
; i
++)
849 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
850 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
854 * Initialise all the fonts we will need initially. There may be as many as
855 * three or as few as one. The other (poentially) twentyone fonts are done
856 * if/when they are needed.
860 * - check the font width and height, correcting our guesses if
863 * - verify that the bold font is the same width as the ordinary
864 * one, and engage shadow bolding if not.
866 * - verify that the underlined font is the same width as the
867 * ordinary one (manual underlining by means of line drawing can
868 * be done in a pinch).
870 static void init_fonts(int pick_width
, int pick_height
)
877 int fw_dontcare
, fw_bold
;
879 for (i
= 0; i
< FONT_MAXNO
; i
++)
882 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
885 if (cfg
.fontisbold
) {
886 fw_dontcare
= FW_BOLD
;
889 fw_dontcare
= FW_DONTCARE
;
896 font_height
= pick_height
;
898 font_height
= cfg
.fontheight
;
899 if (font_height
> 0) {
901 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
904 font_width
= pick_width
;
907 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
908 c, OUT_DEFAULT_PRECIS, \
909 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
910 FIXED_PITCH | FF_DONTCARE, cfg.font)
912 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
914 lfont
.lfHeight
= font_height
;
915 lfont
.lfWidth
= font_width
;
916 lfont
.lfEscapement
= 0;
917 lfont
.lfOrientation
= 0;
918 lfont
.lfWeight
= fw_dontcare
;
919 lfont
.lfItalic
= FALSE
;
920 lfont
.lfUnderline
= FALSE
;
921 lfont
.lfStrikeOut
= FALSE
;
922 lfont
.lfCharSet
= cfg
.fontcharset
;
923 lfont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
924 lfont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
925 lfont
.lfQuality
= DEFAULT_QUALITY
;
926 lfont
.lfPitchAndFamily
= FIXED_PITCH
| FF_DONTCARE
;
927 strncpy(lfont
.lfFaceName
, cfg
.font
, LF_FACESIZE
);
929 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
930 GetTextMetrics(hdc
, &tm
);
932 if (pick_width
== 0 || pick_height
== 0) {
933 font_height
= tm
.tmHeight
;
934 font_width
= tm
.tmAveCharWidth
;
936 font_dualwidth
= (tm
.tmAveCharWidth
!= tm
.tmMaxCharWidth
);
938 #ifdef RDB_DEBUG_PATCH
939 debug(23, "Primary font H=%d, AW=%d, MW=%d",
940 tm
.tmHeight
, tm
.tmAveCharWidth
, tm
.tmMaxCharWidth
);
945 DWORD cset
= tm
.tmCharSet
;
946 memset(&info
, 0xFF, sizeof(info
));
948 /* !!! Yes the next line is right */
949 if (cset
== OEM_CHARSET
)
950 font_codepage
= GetOEMCP();
952 if (TranslateCharsetInfo
953 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
958 GetCPInfo(font_codepage
, &cpinfo
);
959 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
962 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
965 * Some fonts, e.g. 9-pt Courier, draw their underlines
966 * outside their character cell. We successfully prevent
967 * screen corruption by clipping the text output, but then
968 * we lose the underline completely. Here we try to work
969 * out whether this is such a font, and if it is, we set a
970 * flag that causes underlines to be drawn by hand.
972 * Having tried other more sophisticated approaches (such
973 * as examining the TEXTMETRIC structure or requesting the
974 * height of a string), I think we'll do this the brute
975 * force way: we create a small bitmap, draw an underlined
976 * space on it, and test to see whether any pixels are
977 * foreground-coloured. (Since we expect the underline to
978 * go all the way across the character cell, we only search
979 * down a single column of the bitmap, half way across.)
983 HBITMAP und_bm
, und_oldbm
;
987 und_dc
= CreateCompatibleDC(hdc
);
988 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
989 und_oldbm
= SelectObject(und_dc
, und_bm
);
990 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
991 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
992 SetTextColor(und_dc
, RGB(255, 255, 255));
993 SetBkColor(und_dc
, RGB(0, 0, 0));
994 SetBkMode(und_dc
, OPAQUE
);
995 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
997 for (i
= 0; i
< font_height
; i
++) {
998 c
= GetPixel(und_dc
, font_width
/ 2, i
);
999 if (c
!= RGB(0, 0, 0))
1002 SelectObject(und_dc
, und_oldbm
);
1003 DeleteObject(und_bm
);
1006 und_mode
= UND_LINE
;
1007 DeleteObject(fonts
[FONT_UNDERLINE
]);
1008 fonts
[FONT_UNDERLINE
] = 0;
1012 if (bold_mode
== BOLD_FONT
) {
1013 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
1017 descent
= tm
.tmAscent
+ 1;
1018 if (descent
>= font_height
)
1019 descent
= font_height
- 1;
1021 for (i
= 0; i
< 3; i
++) {
1023 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
1024 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
1031 ReleaseDC(hwnd
, hdc
);
1033 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1034 und_mode
= UND_LINE
;
1035 DeleteObject(fonts
[FONT_UNDERLINE
]);
1036 fonts
[FONT_UNDERLINE
] = 0;
1039 if (bold_mode
== BOLD_FONT
&&
1040 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1041 bold_mode
= BOLD_SHADOW
;
1042 DeleteObject(fonts
[FONT_BOLD
]);
1043 fonts
[FONT_BOLD
] = 0;
1045 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1050 static void another_font(int fontno
)
1053 int fw_dontcare
, fw_bold
;
1057 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1060 basefont
= (fontno
& ~(FONT_BOLDUND
));
1061 if (basefont
!= fontno
&& !fontflag
[basefont
])
1062 another_font(basefont
);
1064 if (cfg
.fontisbold
) {
1065 fw_dontcare
= FW_BOLD
;
1068 fw_dontcare
= FW_DONTCARE
;
1072 c
= cfg
.fontcharset
;
1078 if (fontno
& FONT_WIDE
)
1080 if (fontno
& FONT_NARROW
)
1082 if (fontno
& FONT_OEM
)
1084 if (fontno
& FONT_BOLD
)
1086 if (fontno
& FONT_UNDERLINE
)
1090 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1091 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1092 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1093 FIXED_PITCH
| FF_DONTCARE
, s
);
1095 fontflag
[fontno
] = 1;
1098 static void deinit_fonts(void)
1101 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1103 DeleteObject(fonts
[i
]);
1109 void request_resize(int w
, int h
)
1113 /* If the window is maximized supress resizing attempts */
1114 if (IsZoomed(hwnd
)) {
1115 if (cfg
.resize_action
!= RESIZE_FONT
)
1119 if (cfg
.resize_action
== RESIZE_DISABLED
) return;
1120 if (h
== rows
&& w
== cols
) return;
1122 /* Sanity checks ... */
1124 static int first_time
= 1;
1127 switch (first_time
) {
1129 /* Get the size of the screen */
1130 if (GetClientRect(GetDesktopWindow(), &ss
))
1131 /* first_time = 0 */ ;
1137 /* Make sure the values are sane */
1138 width
= (ss
.right
- ss
.left
- extra_width
) / 4;
1139 height
= (ss
.bottom
- ss
.top
- extra_height
) / 6;
1141 if (w
> width
|| h
> height
)
1150 term_size(h
, w
, cfg
.savelines
);
1152 if (cfg
.resize_action
!= RESIZE_FONT
) {
1153 width
= extra_width
+ font_width
* w
;
1154 height
= extra_height
+ font_height
* h
;
1156 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1157 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1158 SWP_NOMOVE
| SWP_NOZORDER
);
1162 InvalidateRect(hwnd
, NULL
, TRUE
);
1165 static void reset_window(int reinit
) {
1167 * This function decides how to resize or redraw when the
1168 * user changes something.
1170 * This function doesn't like to change the terminal size but if the
1171 * font size is locked that may be it's only soluion.
1173 int win_width
, win_height
;
1176 #ifdef RDB_DEBUG_PATCH
1177 debug((27, "reset_window()"));
1180 /* Current window sizes ... */
1181 GetWindowRect(hwnd
, &wr
);
1182 GetClientRect(hwnd
, &cr
);
1184 win_width
= cr
.right
- cr
.left
;
1185 win_height
= cr
.bottom
- cr
.top
;
1187 /* Are we being forced to reload the fonts ? */
1189 #ifdef RDB_DEBUG_PATCH
1190 debug((27, "reset_window() -- Forced deinit"));
1196 /* Oh, looks like we're minimised */
1197 if (win_width
== 0 || win_height
== 0)
1200 /* Is the window out of position ? */
1202 (offset_width
!= (win_width
-font_width
*cols
)/2 ||
1203 offset_height
!= (win_height
-font_height
*rows
)/2) ){
1204 offset_width
= (win_width
-font_width
*cols
)/2;
1205 offset_height
= (win_height
-font_height
*rows
)/2;
1206 InvalidateRect(hwnd
, NULL
, TRUE
);
1207 #ifdef RDB_DEBUG_PATCH
1208 debug((27, "reset_window() -> Reposition terminal"));
1212 if (IsZoomed(hwnd
)) {
1213 /* We're fullscreen, this means we must not change the size of
1214 * the window so it's the font size or the terminal itself.
1217 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1218 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1220 if (cfg
.resize_action
== RESIZE_FONT
) {
1221 if ( font_width
!= win_width
/cols
||
1222 font_height
!= win_height
/rows
) {
1224 init_fonts(win_width
/cols
, win_height
/rows
);
1225 offset_width
= (win_width
-font_width
*cols
)/2;
1226 offset_height
= (win_height
-font_height
*rows
)/2;
1227 InvalidateRect(hwnd
, NULL
, TRUE
);
1228 #ifdef RDB_DEBUG_PATCH
1229 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1230 font_width
, font_height
));
1234 if ( font_width
!= win_width
/cols
||
1235 font_height
!= win_height
/rows
) {
1236 /* Our only choice at this point is to change the
1237 * size of the terminal; Oh well.
1239 term_size( win_height
/font_height
, win_width
/font_width
,
1241 offset_width
= (win_width
-font_width
*cols
)/2;
1242 offset_height
= (win_height
-font_height
*rows
)/2;
1243 InvalidateRect(hwnd
, NULL
, TRUE
);
1244 #ifdef RDB_DEBUG_PATCH
1245 debug((27, "reset_window() -> Zoomed term_size"));
1252 /* Hmm, a force re-init means we should ignore the current window
1253 * so we resize to the default font size.
1256 #ifdef RDB_DEBUG_PATCH
1257 debug((27, "reset_window() -> Forced re-init"));
1260 offset_width
= offset_height
= cfg
.window_border
;
1261 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1262 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1264 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1265 win_height
!= font_height
*rows
+ offset_height
*2) {
1267 /* If this is too large windows will resize it to the maximum
1268 * allowed window size, we will then be back in here and resize
1269 * the font or terminal to fit.
1271 SetWindowPos(hwnd
, NULL
, 0, 0,
1272 font_width
*cols
+ extra_width
,
1273 font_height
*rows
+ extra_height
,
1274 SWP_NOMOVE
| SWP_NOZORDER
);
1279 /* Okay the user doesn't want us to change the font so we try the
1280 * window. But that may be too big for the screen which forces us
1281 * to change the terminal.
1283 if ((cfg
.resize_action
!= RESIZE_FONT
&& reinit
==0) || reinit
>0) {
1284 offset_width
= offset_height
= cfg
.window_border
;
1285 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1286 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1288 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1289 win_height
!= font_height
*rows
+ offset_height
*2) {
1294 GetClientRect(GetDesktopWindow(), &ss
);
1295 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1296 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1299 if ( rows
> height
|| cols
> width
) {
1300 if ( height
> rows
) height
= rows
;
1301 if ( width
> cols
) width
= cols
;
1302 term_size(height
, width
, cfg
.savelines
);
1303 #ifdef RDB_DEBUG_PATCH
1304 debug((27, "reset_window() -> term resize to (%d,%d)",
1309 SetWindowPos(hwnd
, NULL
, 0, 0,
1310 font_width
*cols
+ extra_width
,
1311 font_height
*rows
+ extra_height
,
1312 SWP_NOMOVE
| SWP_NOZORDER
);
1314 InvalidateRect(hwnd
, NULL
, TRUE
);
1315 #ifdef RDB_DEBUG_PATCH
1316 debug((27, "reset_window() -> window resize to (%d,%d)",
1317 font_width
*cols
+ extra_width
,
1318 font_height
*rows
+ extra_height
));
1324 /* We're allowed to or must change the font but do we want to ? */
1326 if (font_width
!= (win_width
-cfg
.window_border
*2)/cols
||
1327 font_height
!= (win_height
-cfg
.window_border
*2)/rows
) {
1330 init_fonts((win_width
-cfg
.window_border
*2)/cols
,
1331 (win_height
-cfg
.window_border
*2)/rows
);
1332 offset_width
= (win_width
-font_width
*cols
)/2;
1333 offset_height
= (win_height
-font_height
*rows
)/2;
1335 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+offset_width
*2;
1336 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1338 InvalidateRect(hwnd
, NULL
, TRUE
);
1339 #ifdef RDB_DEBUG_PATCH
1340 debug((25, "reset_window() -> font resize to (%d,%d)",
1341 font_width
, font_height
));
1346 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
, int alt
)
1348 int thistime
= GetMessageTime();
1350 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1351 lastbtn
= MBT_NOTHING
;
1352 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
, alt
);
1356 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1357 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1358 lastact
== MA_2CLK ? MA_3CLK
:
1359 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1364 if (lastact
!= MA_NOTHING
)
1365 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
, alt
);
1366 lasttime
= thistime
;
1370 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1371 * into a cooked one (SELECT, EXTEND, PASTE).
1373 Mouse_Button
translate_button(Mouse_Button button
)
1375 if (button
== MBT_LEFT
)
1377 if (button
== MBT_MIDDLE
)
1378 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1379 if (button
== MBT_RIGHT
)
1380 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1381 return 0; /* shouldn't happen */
1384 static void show_mouseptr(int show
)
1386 static int cursor_visible
= 1;
1387 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1389 if (cursor_visible
&& !show
)
1391 else if (!cursor_visible
&& show
)
1393 cursor_visible
= show
;
1396 static int is_alt_pressed(void)
1399 int r
= GetKeyboardState(keystate
);
1402 if (keystate
[VK_MENU
] & 0x80)
1404 if (keystate
[VK_RMENU
] & 0x80)
1409 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1410 WPARAM wParam
, LPARAM lParam
)
1413 static int ignore_clip
= FALSE
;
1414 static int resizing
= FALSE
;
1415 static int need_backend_resize
= FALSE
;
1419 if (pending_netevent
)
1420 enact_pending_netevent();
1426 if (cfg
.ping_interval
> 0) {
1429 if (now
- last_movement
> cfg
.ping_interval
) {
1430 back
->special(TS_PING
);
1431 last_movement
= now
;
1434 net_pending_errors();
1440 if (!cfg
.warn_on_close
|| session_closed
||
1442 "Are you sure you want to close this session?",
1443 "PuTTY Exit Confirmation",
1444 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1445 DestroyWindow(hwnd
);
1452 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1464 PROCESS_INFORMATION pi
;
1465 HANDLE filemap
= NULL
;
1467 if (wParam
== IDM_DUPSESS
) {
1469 * Allocate a file-mapping memory chunk for the
1472 SECURITY_ATTRIBUTES sa
;
1475 sa
.nLength
= sizeof(sa
);
1476 sa
.lpSecurityDescriptor
= NULL
;
1477 sa
.bInheritHandle
= TRUE
;
1478 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1481 0, sizeof(Config
), NULL
);
1483 p
= (Config
*) MapViewOfFile(filemap
,
1485 0, 0, sizeof(Config
));
1487 *p
= cfg
; /* structure copy */
1491 sprintf(c
, "putty &%p", filemap
);
1493 } else if (wParam
== IDM_SAVEDSESS
) {
1495 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1496 cl
= smalloc(16 + strlen(session
)); /* 8, but play safe */
1498 cl
= NULL
; /* not a very important failure mode */
1500 sprintf(cl
, "putty @%s", session
);
1506 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1508 si
.lpReserved
= NULL
;
1509 si
.lpDesktop
= NULL
;
1513 si
.lpReserved2
= NULL
;
1514 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1515 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1518 CloseHandle(filemap
);
1528 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1531 if (!do_reconfig(hwnd
))
1534 /* If user forcibly disables full-screen, gracefully unzoom */
1535 if (full_screen
&& !cfg
.fullscreenonaltenter
) {
1539 if (strcmp(prev_cfg
.logfilename
, cfg
.logfilename
) ||
1540 prev_cfg
.logtype
!= cfg
.logtype
) {
1541 logfclose(); /* reset logging */
1547 * Flush the line discipline's edit buffer in the
1548 * case where local editing has just been disabled.
1550 ldisc_send(NULL
, 0, 0);
1558 /* Screen size changed ? */
1559 if (cfg
.height
!= prev_cfg
.height
||
1560 cfg
.width
!= prev_cfg
.width
||
1561 cfg
.savelines
!= prev_cfg
.savelines
||
1562 cfg
.resize_action
!= RESIZE_TERM
)
1563 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1565 /* Enable or disable the scroll bar, etc */
1567 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1568 LONG nexflag
, exflag
=
1569 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1572 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1573 if (cfg
.alwaysontop
) {
1574 nexflag
|= WS_EX_TOPMOST
;
1575 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1576 SWP_NOMOVE
| SWP_NOSIZE
);
1578 nexflag
&= ~(WS_EX_TOPMOST
);
1579 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1580 SWP_NOMOVE
| SWP_NOSIZE
);
1583 if (cfg
.sunken_edge
)
1584 nexflag
|= WS_EX_CLIENTEDGE
;
1586 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1592 nflg
&= ~WS_VSCROLL
;
1593 if (cfg
.resize_action
== RESIZE_DISABLED
)
1594 nflg
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1596 nflg
|= (WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1598 if (nflg
!= flag
|| nexflag
!= exflag
) {
1600 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1601 if (nexflag
!= exflag
)
1602 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1604 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1605 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1606 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1614 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1619 set_title(cfg
.wintitle
);
1620 if (IsIconic(hwnd
)) {
1622 cfg
.win_name_always ? window_name
:
1626 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1627 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1628 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1629 cfg
.fontheight
!= prev_cfg
.fontheight
||
1630 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1631 cfg
.vtmode
!= prev_cfg
.vtmode
||
1632 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1633 (cfg
.resize_action
!= RESIZE_FONT
&&
1634 prev_cfg
.resize_action
== RESIZE_FONT
))
1637 InvalidateRect(hwnd
, NULL
, TRUE
);
1638 reset_window(init_lvl
);
1639 net_pending_errors();
1652 back
->special(TS_AYT
);
1653 net_pending_errors();
1656 back
->special(TS_BRK
);
1657 net_pending_errors();
1660 back
->special(TS_SYNCH
);
1661 net_pending_errors();
1664 back
->special(TS_EC
);
1665 net_pending_errors();
1668 back
->special(TS_EL
);
1669 net_pending_errors();
1672 back
->special(TS_GA
);
1673 net_pending_errors();
1676 back
->special(TS_NOP
);
1677 net_pending_errors();
1680 back
->special(TS_ABORT
);
1681 net_pending_errors();
1684 back
->special(TS_AO
);
1685 net_pending_errors();
1688 back
->special(TS_IP
);
1689 net_pending_errors();
1692 back
->special(TS_SUSP
);
1693 net_pending_errors();
1696 back
->special(TS_EOR
);
1697 net_pending_errors();
1700 back
->special(TS_EOF
);
1701 net_pending_errors();
1708 * We get this if the System menu has been activated
1715 * We get this if the System menu has been activated
1716 * using the keyboard. This might happen from within
1717 * TranslateKey, in which case it really wants to be
1718 * followed by a `space' character to actually _bring
1719 * the menu up_ rather than just sitting there in
1720 * `ready to appear' state.
1722 show_mouseptr(1); /* make sure pointer is visible */
1724 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1726 case IDM_FULLSCREEN
:
1730 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1731 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1736 #define X_POS(l) ((int)(short)LOWORD(l))
1737 #define Y_POS(l) ((int)(short)HIWORD(l))
1739 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1740 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1741 #define WHEEL_DELTA 120
1744 wheel_accumulator
+= (short) HIWORD(wParam
);
1745 wParam
= LOWORD(wParam
);
1747 /* process events when the threshold is reached */
1748 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
1751 /* reduce amount for next time */
1752 if (wheel_accumulator
> 0) {
1754 wheel_accumulator
-= WHEEL_DELTA
;
1755 } else if (wheel_accumulator
< 0) {
1757 wheel_accumulator
+= WHEEL_DELTA
;
1761 if (send_raw_mouse
) {
1762 /* send a mouse-down followed by a mouse up */
1766 TO_CHR_X(X_POS(lParam
)),
1767 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1768 wParam
& MK_CONTROL
, is_alt_pressed());
1769 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1770 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1771 wParam
& MK_CONTROL
, is_alt_pressed());
1773 /* trigger a scroll */
1775 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
1780 case WM_LBUTTONDOWN
:
1781 case WM_MBUTTONDOWN
:
1782 case WM_RBUTTONDOWN
:
1790 case WM_LBUTTONDOWN
:
1794 case WM_MBUTTONDOWN
:
1795 button
= MBT_MIDDLE
;
1798 case WM_RBUTTONDOWN
:
1807 button
= MBT_MIDDLE
;
1815 button
= press
= 0; /* shouldn't happen */
1820 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1821 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
,
1825 term_mouse(button
, MA_RELEASE
,
1826 TO_CHR_X(X_POS(lParam
)),
1827 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1828 wParam
& MK_CONTROL
, is_alt_pressed());
1836 * Add the mouse position and message time to the random
1839 noise_ultralight(lParam
);
1841 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1843 if (wParam
& MK_LBUTTON
)
1845 else if (wParam
& MK_MBUTTON
)
1849 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1850 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1851 wParam
& MK_CONTROL
, is_alt_pressed());
1854 case WM_NCMOUSEMOVE
:
1856 noise_ultralight(lParam
);
1858 case WM_IGNORE_CLIP
:
1859 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1861 case WM_DESTROYCLIPBOARD
:
1864 ignore_clip
= FALSE
;
1870 hdc
= BeginPaint(hwnd
, &p
);
1872 SelectPalette(hdc
, pal
, TRUE
);
1873 RealizePalette(hdc
);
1876 (p
.rcPaint
.left
-offset_width
)/font_width
,
1877 (p
.rcPaint
.top
-offset_height
)/font_height
,
1878 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
1879 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
1882 p
.rcPaint
.left
< offset_width
||
1883 p
.rcPaint
.top
< offset_height
||
1884 p
.rcPaint
.right
>= offset_width
+ font_width
*cols
||
1885 p
.rcPaint
.bottom
>= offset_height
+ font_height
*rows
)
1887 HBRUSH fillcolour
, oldbrush
;
1889 fillcolour
= CreateSolidBrush (
1890 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1891 oldbrush
= SelectObject(hdc
, fillcolour
);
1892 edge
= CreatePen(PS_SOLID
, 0,
1893 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1894 oldpen
= SelectObject(hdc
, edge
);
1896 ExcludeClipRect(hdc
,
1897 offset_width
, offset_height
,
1898 offset_width
+font_width
*cols
,
1899 offset_height
+font_height
*rows
);
1901 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1902 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1904 // SelectClipRgn(hdc, NULL);
1906 SelectObject(hdc
, oldbrush
);
1907 DeleteObject(fillcolour
);
1908 SelectObject(hdc
, oldpen
);
1911 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
1912 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
1918 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1919 * but the only one that's likely to try to overload us is FD_READ.
1920 * This means buffering just one is fine.
1922 if (pending_netevent
)
1923 enact_pending_netevent();
1925 pending_netevent
= TRUE
;
1926 pend_netevent_wParam
= wParam
;
1927 pend_netevent_lParam
= lParam
;
1928 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
1929 enact_pending_netevent();
1931 time(&last_movement
);
1935 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
1937 flash_window(0); /* stop */
1949 case WM_ENTERSIZEMOVE
:
1950 #ifdef RDB_DEBUG_PATCH
1951 debug((27, "WM_ENTERSIZEMOVE"));
1955 need_backend_resize
= FALSE
;
1957 case WM_EXITSIZEMOVE
:
1960 #ifdef RDB_DEBUG_PATCH
1961 debug((27, "WM_EXITSIZEMOVE"));
1963 if (need_backend_resize
) {
1964 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1965 InvalidateRect(hwnd
, NULL
, TRUE
);
1970 * This does two jobs:
1971 * 1) Keep the sizetip uptodate
1972 * 2) Make sure the window size is _stepped_ in units of the font size.
1974 if (cfg
.resize_action
== RESIZE_TERM
&& !alt_pressed
) {
1975 int width
, height
, w
, h
, ew
, eh
;
1976 LPRECT r
= (LPRECT
) lParam
;
1978 if ( !need_backend_resize
&&
1979 (cfg
.height
!= rows
|| cfg
.width
!= cols
)) {
1981 * Great! It seems the host has been changing the terminal
1982 * size, well the user is now grabbing so this is probably
1983 * the least confusing solution in the long run even though
1984 * it a is suprise. Unfortunatly the only way to prevent
1985 * this seems to be to let the host change the window size
1986 * and as that's a user option we're still right back here.
1988 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1990 InvalidateRect(hwnd
, NULL
, TRUE
);
1991 need_backend_resize
= TRUE
;
1994 width
= r
->right
- r
->left
- extra_width
;
1995 height
= r
->bottom
- r
->top
- extra_height
;
1996 w
= (width
+ font_width
/ 2) / font_width
;
1999 h
= (height
+ font_height
/ 2) / font_height
;
2002 UpdateSizeTip(hwnd
, w
, h
);
2003 ew
= width
- w
* font_width
;
2004 eh
= height
- h
* font_height
;
2006 if (wParam
== WMSZ_LEFT
||
2007 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2013 if (wParam
== WMSZ_TOP
||
2014 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2024 int width
, height
, w
, h
, rv
= 0;
2025 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
2026 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2027 LPRECT r
= (LPRECT
) lParam
;
2029 width
= r
->right
- r
->left
- ex_width
;
2030 height
= r
->bottom
- r
->top
- ex_height
;
2032 w
= (width
+ cols
/2)/cols
;
2033 h
= (height
+ rows
/2)/rows
;
2034 if ( r
->right
!= r
->left
+ w
*cols
+ ex_width
)
2037 if (wParam
== WMSZ_LEFT
||
2038 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2039 r
->left
= r
->right
- w
*cols
- ex_width
;
2041 r
->right
= r
->left
+ w
*cols
+ ex_width
;
2043 if (r
->bottom
!= r
->top
+ h
*rows
+ ex_height
)
2046 if (wParam
== WMSZ_TOP
||
2047 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2048 r
->top
= r
->bottom
- h
*rows
- ex_height
;
2050 r
->bottom
= r
->top
+ h
*rows
+ ex_height
;
2054 /* break; (never reached) */
2056 #ifdef RDB_DEBUG_PATCH
2057 debug((27, "WM_SIZE %s (%d,%d)",
2058 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2059 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2060 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2061 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2063 LOWORD(lParam
), HIWORD(lParam
)));
2065 if (wParam
== SIZE_MINIMIZED
) {
2067 cfg
.win_name_always ? window_name
: icon_name
);
2070 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2071 SetWindowText(hwnd
, window_name
);
2073 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2074 /* A resize, well it better be a minimize. */
2078 int width
, height
, w
, h
;
2080 width
= LOWORD(lParam
);
2081 height
= HIWORD(lParam
);
2084 if (wParam
== SIZE_MAXIMIZED
) {
2088 if (cfg
.resize_action
!= RESIZE_FONT
) {
2089 w
= width
/ font_width
;
2091 h
= height
/ font_height
;
2094 term_size(h
, w
, cfg
.savelines
);
2097 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2099 if (cfg
.resize_action
!= RESIZE_FONT
)
2100 term_size(prev_rows
, prev_cols
, cfg
.savelines
);
2103 /* This is an unexpected resize, these will normally happen
2104 * if the window is too large. Probably either the user
2105 * selected a huge font or the screen size has changed.
2107 * This is also called with minimize.
2109 else reset_window(-1);
2113 * Don't call back->size in mid-resize. (To prevent
2114 * massive numbers of resize events getting sent
2115 * down the connection during an NT opaque drag.)
2118 if (cfg
.resize_action
== RESIZE_TERM
&& !alt_pressed
) {
2119 need_backend_resize
= TRUE
;
2120 w
= (width
-cfg
.window_border
*2) / font_width
;
2122 h
= (height
-cfg
.window_border
*2) / font_height
;
2133 switch (LOWORD(wParam
)) {
2147 term_scroll(0, +rows
/ 2);
2150 term_scroll(0, -rows
/ 2);
2152 case SB_THUMBPOSITION
:
2154 term_scroll(1, HIWORD(wParam
));
2158 case WM_PALETTECHANGED
:
2159 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2160 HDC hdc
= get_ctx();
2162 if (RealizePalette(hdc
) > 0)
2168 case WM_QUERYNEWPALETTE
:
2170 HDC hdc
= get_ctx();
2172 if (RealizePalette(hdc
) > 0)
2184 * Add the scan code and keypress timing to the random
2187 noise_ultralight(lParam
);
2190 * We don't do TranslateMessage since it disassociates the
2191 * resulting CHAR message from the KEYDOWN that sparked it,
2192 * which we occasionally don't want. Instead, we process
2193 * KEYDOWN, and call the Win32 translator functions so that
2194 * we get the translations under _our_ control.
2197 unsigned char buf
[20];
2200 if (wParam
== VK_PROCESSKEY
) {
2203 m
.message
= WM_KEYDOWN
;
2205 m
.lParam
= lParam
& 0xdfff;
2206 TranslateMessage(&m
);
2208 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2210 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2214 * Interrupt an ongoing paste. I'm not sure
2215 * this is sensible, but for the moment it's
2216 * preferable to having to faff about buffering
2222 * We need not bother about stdin backlogs
2223 * here, because in GUI PuTTY we can't do
2224 * anything about it anyway; there's no means
2225 * of asking Windows to hold off on KEYDOWN
2226 * messages. We _have_ to buffer everything
2229 ldisc_send(buf
, len
, 1);
2234 net_pending_errors();
2236 case WM_INPUTLANGCHANGE
:
2238 /* wParam == Font number */
2239 /* lParam == Locale */
2241 HKL NewInputLocale
= (HKL
) lParam
;
2243 // lParam == GetKeyboardLayout(0);
2245 GetLocaleInfo(LOWORD(NewInputLocale
),
2246 LOCALE_IDEFAULTANSICODEPAGE
, lbuf
, sizeof(lbuf
));
2248 kbd_codepage
= atoi(lbuf
);
2252 if(wParam
== IMN_SETOPENSTATUS
) {
2253 HIMC hImc
= ImmGetContext(hwnd
);
2254 ImmSetCompositionFont(hImc
, &lfont
);
2255 ImmReleaseContext(hwnd
, hImc
);
2259 case WM_IME_COMPOSITION
:
2265 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2266 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2268 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2269 break; /* fall back to DefWindowProc */
2271 hIMC
= ImmGetContext(hwnd
);
2272 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2275 buff
= (char*) smalloc(n
);
2276 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2277 luni_send((unsigned short *)buff
, n
/ 2, 1);
2280 ImmReleaseContext(hwnd
, hIMC
);
2285 if (wParam
& 0xFF00) {
2286 unsigned char buf
[2];
2289 buf
[0] = wParam
>> 8;
2290 lpage_send(kbd_codepage
, buf
, 2, 1);
2292 char c
= (unsigned char) wParam
;
2293 lpage_send(kbd_codepage
, &c
, 1, 1);
2299 * Nevertheless, we are prepared to deal with WM_CHAR
2300 * messages, should they crop up. So if someone wants to
2301 * post the things to us as part of a macro manoeuvre,
2302 * we're ready to cope.
2305 char c
= (unsigned char)wParam
;
2306 lpage_send(CP_ACP
, &c
, 1, 1);
2310 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2311 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2316 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2320 * Move the system caret. (We maintain one, even though it's
2321 * invisible, for the benefit of blind people: apparently some
2322 * helper software tracks the system caret, so we should arrange to
2325 void sys_cursor(int x
, int y
)
2330 if (!has_focus
) return;
2332 SetCaretPos(x
* font_width
+ offset_width
,
2333 y
* font_height
+ offset_height
);
2335 /* IMM calls on Win98 and beyond only */
2336 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2338 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2339 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2341 /* we should have the IMM functions */
2342 hIMC
= ImmGetContext(hwnd
);
2343 cf
.dwStyle
= CFS_POINT
;
2344 cf
.ptCurrentPos
.x
= x
* font_width
+ offset_width
;
2345 cf
.ptCurrentPos
.y
= y
* font_height
+ offset_height
;
2346 ImmSetCompositionWindow(hIMC
, &cf
);
2348 ImmReleaseContext(hwnd
, hIMC
);
2352 * Draw a line of text in the window, at given character
2353 * coordinates, in given attributes.
2355 * We are allowed to fiddle with the contents of `text'.
2357 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2358 unsigned long attr
, int lattr
)
2361 int nfg
, nbg
, nfont
;
2364 int force_manual_underline
= 0;
2365 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2366 int char_width
= fnt_width
;
2367 int text_adjust
= 0;
2368 static int *IpDx
= 0, IpDxLEN
= 0;
2370 if (attr
& ATTR_WIDE
)
2373 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2375 if (len
> IpDxLEN
) {
2377 IpDx
= smalloc((len
+ 16) * sizeof(int));
2378 IpDxLEN
= (len
+ 16);
2380 for (i
= 0; i
< IpDxLEN
; i
++)
2381 IpDx
[i
] = char_width
;
2384 /* Only want the left half of double width lines */
2385 if (lattr
!= LATTR_NORM
&& x
*2 >= cols
)
2393 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
2394 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2395 attr
^= ATTR_CUR_XOR
;
2399 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2400 /* Assume a poorman font is borken in other ways too. */
2410 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2413 if (attr
& ATTR_NARROW
)
2414 nfont
|= FONT_NARROW
;
2416 /* Special hack for the VT100 linedraw glyphs. */
2417 if ((attr
& CSET_MASK
) == 0x2300) {
2418 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2419 switch ((unsigned char) (text
[0])) {
2421 text_adjust
= -2 * font_height
/ 5;
2424 text_adjust
= -1 * font_height
/ 5;
2427 text_adjust
= font_height
/ 5;
2430 text_adjust
= 2 * font_height
/ 5;
2433 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2436 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2437 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2438 if (attr
& ATTR_UNDER
) {
2439 attr
&= ~ATTR_UNDER
;
2440 force_manual_underline
= 1;
2445 /* Anything left as an original character set is unprintable. */
2446 if (DIRECT_CHAR(attr
)) {
2449 memset(text
, 0xFD, len
);
2453 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2456 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2457 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2458 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2460 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2461 nfont
|= FONT_UNDERLINE
;
2462 another_font(nfont
);
2463 if (!fonts
[nfont
]) {
2464 if (nfont
& FONT_UNDERLINE
)
2465 force_manual_underline
= 1;
2466 /* Don't do the same for manual bold, it could be bad news. */
2468 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2470 another_font(nfont
);
2472 nfont
= FONT_NORMAL
;
2473 if (attr
& ATTR_REVERSE
) {
2478 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2480 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2484 SelectObject(hdc
, fonts
[nfont
]);
2485 SetTextColor(hdc
, fg
);
2486 SetBkColor(hdc
, bg
);
2487 SetBkMode(hdc
, OPAQUE
);
2490 line_box
.right
= x
+ char_width
* len
;
2491 line_box
.bottom
= y
+ font_height
;
2493 /* Only want the left half of double width lines */
2494 if (line_box
.right
> font_width
*cols
+offset_width
)
2495 line_box
.right
= font_width
*cols
+offset_width
;
2497 /* We're using a private area for direct to font. (512 chars.) */
2498 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2499 /* Ho Hum, dbcs fonts are a PITA! */
2500 /* To display on W9x I have to convert to UCS */
2501 static wchar_t *uni_buf
= 0;
2502 static int uni_len
= 0;
2504 if (len
> uni_len
) {
2506 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2509 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2510 uni_buf
[nlen
] = 0xFFFD;
2511 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2512 IpDx
[nlen
] += char_width
;
2513 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2514 text
+mptr
, 2, uni_buf
+nlen
, 1);
2519 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2520 text
+mptr
, 1, uni_buf
+nlen
, 1);
2528 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2529 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2530 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2531 SetBkMode(hdc
, TRANSPARENT
);
2532 ExtTextOutW(hdc
, x
- 1,
2533 y
- font_height
* (lattr
==
2534 LATTR_BOT
) + text_adjust
,
2535 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2539 } else if (DIRECT_FONT(attr
)) {
2541 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2542 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2543 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2544 SetBkMode(hdc
, TRANSPARENT
);
2546 /* GRR: This draws the character outside it's box and can leave
2547 * 'droppings' even with the clip box! I suppose I could loop it
2548 * one character at a time ... yuk.
2550 * Or ... I could do a test print with "W", and use +1 or -1 for this
2551 * shift depending on if the leftmost column is blank...
2553 ExtTextOut(hdc
, x
- 1,
2554 y
- font_height
* (lattr
==
2555 LATTR_BOT
) + text_adjust
,
2556 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2559 /* And 'normal' unicode characters */
2560 static WCHAR
*wbuf
= NULL
;
2561 static int wlen
= 0;
2566 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2568 for (i
= 0; i
< len
; i
++)
2569 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2572 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2573 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2575 /* And the shadow bold hack. */
2576 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2577 SetBkMode(hdc
, TRANSPARENT
);
2578 ExtTextOutW(hdc
, x
- 1,
2579 y
- font_height
* (lattr
==
2580 LATTR_BOT
) + text_adjust
,
2581 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2584 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2585 (und_mode
== UND_LINE
2586 && (attr
& ATTR_UNDER
)))) {
2589 if (lattr
== LATTR_BOT
)
2590 dec
= dec
* 2 - font_height
;
2592 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2593 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2594 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2595 oldpen
= SelectObject(hdc
, oldpen
);
2596 DeleteObject(oldpen
);
2600 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2601 unsigned long attr
, int lattr
)
2607 int ctype
= cfg
.cursor_type
;
2609 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2610 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2611 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2615 attr
|= TATTR_RIGHTCURS
;
2618 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2619 if (attr
& ATTR_WIDE
)
2626 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2629 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2630 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2631 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2632 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2633 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2634 Polyline(hdc
, pts
, 5);
2635 oldpen
= SelectObject(hdc
, oldpen
);
2636 DeleteObject(oldpen
);
2637 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2638 int startx
, starty
, dx
, dy
, length
, i
;
2641 starty
= y
+ descent
;
2644 length
= char_width
;
2647 if (attr
& TATTR_RIGHTCURS
)
2648 xadjust
= char_width
- 1;
2649 startx
= x
+ xadjust
;
2653 length
= font_height
;
2655 if (attr
& TATTR_ACTCURS
) {
2658 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2659 MoveToEx(hdc
, startx
, starty
, NULL
);
2660 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2661 oldpen
= SelectObject(hdc
, oldpen
);
2662 DeleteObject(oldpen
);
2664 for (i
= 0; i
< length
; i
++) {
2666 SetPixel(hdc
, startx
, starty
, colours
[23]);
2675 /* This function gets the actual width of a character in the normal font.
2677 int CharWidth(Context ctx
, int uc
) {
2681 /* If the font max is the same as the font ave width then this
2682 * function is a no-op.
2684 if (!font_dualwidth
) return 1;
2686 switch (uc
& CSET_MASK
) {
2688 uc
= unitab_line
[uc
& 0xFF];
2691 uc
= unitab_xterm
[uc
& 0xFF];
2694 uc
= unitab_scoacs
[uc
& 0xFF];
2697 if (DIRECT_FONT(uc
)) {
2698 if (dbcs_screenfont
) return 1;
2700 /* Speedup, I know of no font where ascii is the wrong width */
2701 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
2704 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
2705 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2706 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
2707 another_font(FONT_OEM
);
2708 if (!fonts
[FONT_OEM
]) return 0;
2710 SelectObject(hdc
, fonts
[FONT_OEM
]);
2714 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
2715 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
2718 /* Speedup, I know of no font where ascii is the wrong width */
2719 if (uc
>= ' ' && uc
<= '~') return 1;
2721 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2722 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
2723 /* Okay that one worked */ ;
2724 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
2725 /* This should work on 9x too, but it's "less accurate" */ ;
2730 ibuf
+= font_width
/ 2 -1;
2737 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2738 * codes. Returns number of bytes used or zero to drop the message
2739 * or -1 to forward the message to windows.
2741 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2742 unsigned char *output
)
2745 int scan
, left_alt
= 0, key_down
, shift_state
;
2747 unsigned char *p
= output
;
2748 static int alt_sum
= 0;
2750 HKL kbd_layout
= GetKeyboardLayout(0);
2752 static WORD keys
[3];
2753 static int compose_char
= 0;
2754 static WPARAM compose_key
= 0;
2756 r
= GetKeyboardState(keystate
);
2758 memset(keystate
, 0, sizeof(keystate
));
2761 #define SHOW_TOASCII_RESULT
2762 { /* Tell us all about key events */
2763 static BYTE oldstate
[256];
2764 static int first
= 1;
2768 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2771 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2773 } else if ((HIWORD(lParam
) & KF_UP
)
2774 && scan
== (HIWORD(lParam
) & 0xFF)) {
2778 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2779 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2792 debug(("VK_%02x", wParam
));
2794 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2796 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2798 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2799 if (ch
>= ' ' && ch
<= '~')
2800 debug((", '%c'", ch
));
2802 debug((", $%02x", ch
));
2805 debug((", KB0=%02x", keys
[0]));
2807 debug((", KB1=%02x", keys
[1]));
2809 debug((", KB2=%02x", keys
[2]));
2811 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2813 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2815 if ((HIWORD(lParam
) & KF_EXTENDED
))
2817 if ((HIWORD(lParam
) & KF_UP
))
2821 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2822 else if ((HIWORD(lParam
) & KF_UP
))
2823 oldstate
[wParam
& 0xFF] ^= 0x80;
2825 oldstate
[wParam
& 0xFF] ^= 0x81;
2827 for (ch
= 0; ch
< 256; ch
++)
2828 if (oldstate
[ch
] != keystate
[ch
])
2829 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2831 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2835 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2836 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2840 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2841 if ((cfg
.funky_type
== 3 ||
2842 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2843 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2845 wParam
= VK_EXECUTE
;
2847 /* UnToggle NUMLock */
2848 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2849 keystate
[VK_NUMLOCK
] ^= 1;
2852 /* And write back the 'adjusted' state */
2853 SetKeyboardState(keystate
);
2856 /* Disable Auto repeat if required */
2857 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2860 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2863 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2865 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2866 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2867 if (cfg
.ctrlaltkeys
)
2868 keystate
[VK_MENU
] = 0;
2870 keystate
[VK_RMENU
] = 0x80;
2875 alt_pressed
= (left_alt
&& key_down
);
2877 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2878 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2879 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2881 /* Note if AltGr was pressed and if it was used as a compose key */
2882 if (!compose_state
) {
2883 compose_key
= 0x100;
2884 if (cfg
.compose_key
) {
2885 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2886 compose_key
= wParam
;
2888 if (wParam
== VK_APPS
)
2889 compose_key
= wParam
;
2892 if (wParam
== compose_key
) {
2893 if (compose_state
== 0
2894 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2896 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2900 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
2904 * Record that we pressed key so the scroll window can be reset, but
2905 * be careful to avoid Shift-UP/Down
2907 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
&&
2908 wParam
!= VK_MENU
&& wParam
!= VK_CONTROL
) {
2912 if (compose_state
> 1 && left_alt
)
2915 /* Sanitize the number pad if not using a PC NumPad */
2916 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2917 && cfg
.funky_type
!= 2)
2918 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
2919 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
2923 nParam
= VK_NUMPAD0
;
2926 nParam
= VK_NUMPAD1
;
2929 nParam
= VK_NUMPAD2
;
2932 nParam
= VK_NUMPAD3
;
2935 nParam
= VK_NUMPAD4
;
2938 nParam
= VK_NUMPAD5
;
2941 nParam
= VK_NUMPAD6
;
2944 nParam
= VK_NUMPAD7
;
2947 nParam
= VK_NUMPAD8
;
2950 nParam
= VK_NUMPAD9
;
2953 nParam
= VK_DECIMAL
;
2957 if (keystate
[VK_NUMLOCK
] & 1)
2964 /* If a key is pressed and AltGr is not active */
2965 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
2966 /* Okay, prepare for most alts then ... */
2970 /* Lets see if it's a pattern we know all about ... */
2971 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2972 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2975 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2976 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
2979 if (wParam
== VK_INSERT
&& shift_state
== 1) {
2983 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
2986 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
2987 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
2990 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
) {
2994 /* Control-Numlock for app-keypad mode switch */
2995 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
2996 app_keypad_keys
^= 1;
3000 /* Nethack keypad */
3001 if (cfg
.nethack_keypad
&& !left_alt
) {
3004 *p
++ = shift_state ?
'B' : 'b';
3007 *p
++ = shift_state ?
'J' : 'j';
3010 *p
++ = shift_state ?
'N' : 'n';
3013 *p
++ = shift_state ?
'H' : 'h';
3016 *p
++ = shift_state ?
'.' : '.';
3019 *p
++ = shift_state ?
'L' : 'l';
3022 *p
++ = shift_state ?
'Y' : 'y';
3025 *p
++ = shift_state ?
'K' : 'k';
3028 *p
++ = shift_state ?
'U' : 'u';
3033 /* Application Keypad */
3037 if (cfg
.funky_type
== 3 ||
3038 (cfg
.funky_type
<= 1 &&
3039 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3053 if (app_keypad_keys
&& !cfg
.no_applic_k
)
3090 if (cfg
.funky_type
== 2) {
3095 } else if (shift_state
)
3102 if (cfg
.funky_type
== 2)
3106 if (cfg
.funky_type
== 2)
3110 if (cfg
.funky_type
== 2)
3115 if (HIWORD(lParam
) & KF_EXTENDED
)
3121 if (xkey
>= 'P' && xkey
<= 'S')
3122 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3124 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3126 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3131 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3132 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3136 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3142 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3146 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3150 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3155 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3160 /* Control-2 to Control-8 are special */
3161 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3162 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3165 if (shift_state
== 2 && wParam
== 0xBD) {
3169 if (shift_state
== 2 && wParam
== 0xDF) {
3173 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
3180 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3181 * for integer decimal nn.)
3183 * We also deal with the weird ones here. Linux VCs replace F1
3184 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3185 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3191 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3194 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3197 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3200 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3203 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3206 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3209 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3212 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3215 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3218 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3251 if ((shift_state
&2) == 0) switch (wParam
) {
3271 /* Reorder edit keys to physical order */
3272 if (cfg
.funky_type
== 3 && code
<= 6)
3273 code
= "\0\2\1\4\5\3\6"[code
];
3275 if (vt52_mode
&& code
> 0 && code
<= 6) {
3276 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3280 if (cfg
.funky_type
== 5 && /* SCO function keys */
3281 code
>= 11 && code
<= 34) {
3282 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3285 case VK_F1
: index
= 0; break;
3286 case VK_F2
: index
= 1; break;
3287 case VK_F3
: index
= 2; break;
3288 case VK_F4
: index
= 3; break;
3289 case VK_F5
: index
= 4; break;
3290 case VK_F6
: index
= 5; break;
3291 case VK_F7
: index
= 6; break;
3292 case VK_F8
: index
= 7; break;
3293 case VK_F9
: index
= 8; break;
3294 case VK_F10
: index
= 9; break;
3295 case VK_F11
: index
= 10; break;
3296 case VK_F12
: index
= 11; break;
3298 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3299 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3300 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3303 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3304 code
>= 1 && code
<= 6) {
3305 char codes
[] = "HL.FIG";
3309 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3313 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3320 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3323 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3326 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3327 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3330 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3332 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3334 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3337 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3338 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3342 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3347 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3348 * some reason seems to send VK_CLEAR to Windows...).
3371 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3373 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
3374 /* VT100 & VT102 manuals both state the app cursor keys
3375 * only work if the app keypad is on.
3377 if (!app_keypad_keys
)
3379 /* Useful mapping of Ctrl-arrows */
3380 if (shift_state
== 2)
3384 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3386 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3393 * Finally, deal with Return ourselves. (Win95 seems to
3394 * foul it up when Alt is pressed, for some reason.)
3396 if (wParam
== VK_RETURN
) { /* Return */
3402 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3403 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3408 /* Okay we've done everything interesting; let windows deal with
3409 * the boring stuff */
3413 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3414 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3416 keystate
[VK_CAPITAL
] = 0;
3419 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3420 #ifdef SHOW_TOASCII_RESULT
3421 if (r
== 1 && !key_down
) {
3423 if (in_utf
|| dbcs_screenfont
)
3424 debug((", (U+%04x)", alt_sum
));
3426 debug((", LCH(%d)", alt_sum
));
3428 debug((", ACH(%d)", keys
[0]));
3433 for (r1
= 0; r1
< r
; r1
++) {
3434 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3443 * Interrupt an ongoing paste. I'm not sure this is
3444 * sensible, but for the moment it's preferable to
3445 * having to faff about buffering things.
3450 for (i
= 0; i
< r
; i
++) {
3451 unsigned char ch
= (unsigned char) keys
[i
];
3453 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3458 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3462 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3463 MessageBeep(MB_ICONHAND
);
3467 luni_send(&keybuf
, 1, 1);
3475 if (in_utf
|| dbcs_screenfont
) {
3477 luni_send(&keybuf
, 1, 1);
3479 ch
= (char) alt_sum
;
3481 * We need not bother about stdin
3482 * backlogs here, because in GUI PuTTY
3483 * we can't do anything about it
3484 * anyway; there's no means of asking
3485 * Windows to hold off on KEYDOWN
3486 * messages. We _have_ to buffer
3487 * everything we're sent.
3489 ldisc_send(&ch
, 1, 1);
3493 lpage_send(kbd_codepage
, &ch
, 1, 1);
3495 if(capsOn
&& ch
< 0x80) {
3498 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3499 luni_send(cbuf
+!left_alt
, 1+!!left_alt
, 1);
3504 lpage_send(kbd_codepage
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3510 /* This is so the ALT-Numpad and dead keys work correctly. */
3515 /* If we're definitly not building up an ALT-54321 then clear it */
3518 /* If we will be using alt_sum fix the 256s */
3519 else if (keys
[0] && (in_utf
|| dbcs_screenfont
))
3524 * ALT alone may or may not want to bring up the System menu.
3525 * If it's not meant to, we return 0 on presses or releases of
3526 * ALT, to show that we've swallowed the keystroke. Otherwise
3527 * we return -1, which means Windows will give the keystroke
3528 * its default handling (i.e. bring up the System menu).
3530 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3536 void set_title(char *title
)
3539 window_name
= smalloc(1 + strlen(title
));
3540 strcpy(window_name
, title
);
3541 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3542 SetWindowText(hwnd
, title
);
3545 void set_icon(char *title
)
3548 icon_name
= smalloc(1 + strlen(title
));
3549 strcpy(icon_name
, title
);
3550 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3551 SetWindowText(hwnd
, title
);
3554 void set_sbar(int total
, int start
, int page
)
3561 si
.cbSize
= sizeof(si
);
3562 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3564 si
.nMax
= total
- 1;
3568 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3571 Context
get_ctx(void)
3577 SelectPalette(hdc
, pal
, FALSE
);
3583 void free_ctx(Context ctx
)
3585 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3586 ReleaseDC(hwnd
, ctx
);
3589 static void real_palette_set(int n
, int r
, int g
, int b
)
3592 logpal
->palPalEntry
[n
].peRed
= r
;
3593 logpal
->palPalEntry
[n
].peGreen
= g
;
3594 logpal
->palPalEntry
[n
].peBlue
= b
;
3595 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3596 colours
[n
] = PALETTERGB(r
, g
, b
);
3597 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3599 colours
[n
] = RGB(r
, g
, b
);
3602 void palette_set(int n
, int r
, int g
, int b
)
3604 static const int first
[21] = {
3605 0, 2, 4, 6, 8, 10, 12, 14,
3606 1, 3, 5, 7, 9, 11, 13, 15,
3609 real_palette_set(first
[n
], r
, g
, b
);
3611 real_palette_set(first
[n
] + 1, r
, g
, b
);
3613 HDC hdc
= get_ctx();
3614 UnrealizeObject(pal
);
3615 RealizePalette(hdc
);
3620 void palette_reset(void)
3624 for (i
= 0; i
< NCOLOURS
; i
++) {
3626 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3627 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3628 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3629 logpal
->palPalEntry
[i
].peFlags
= 0;
3630 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3631 defpal
[i
].rgbtGreen
,
3632 defpal
[i
].rgbtBlue
);
3634 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3635 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3640 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3642 RealizePalette(hdc
);
3647 void write_aclip(char *data
, int len
, int must_deselect
)
3652 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3655 lock
= GlobalLock(clipdata
);
3658 memcpy(lock
, data
, len
);
3659 ((unsigned char *) lock
)[len
] = 0;
3660 GlobalUnlock(clipdata
);
3663 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3665 if (OpenClipboard(hwnd
)) {
3667 SetClipboardData(CF_TEXT
, clipdata
);
3670 GlobalFree(clipdata
);
3673 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3677 * Note: unlike write_aclip() this will not append a nul.
3679 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3681 HGLOBAL clipdata
, clipdata2
, clipdata3
;
3683 void *lock
, *lock2
, *lock3
;
3685 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3687 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3688 len
* sizeof(wchar_t));
3689 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3691 if (!clipdata
|| !clipdata2
|| !clipdata3
) {
3693 GlobalFree(clipdata
);
3695 GlobalFree(clipdata2
);
3697 GlobalFree(clipdata3
);
3700 if (!(lock
= GlobalLock(clipdata
)))
3702 if (!(lock2
= GlobalLock(clipdata2
)))
3705 memcpy(lock
, data
, len
* sizeof(wchar_t));
3706 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3708 if (cfg
.rtf_paste
) {
3709 wchar_t unitab
[256];
3711 unsigned char *tdata
= (unsigned char *)lock2
;
3712 wchar_t *udata
= (wchar_t *)lock
;
3713 int rtflen
= 0, uindex
= 0, tindex
= 0;
3715 int multilen
, blen
, alen
, totallen
, i
;
3716 char before
[16], after
[4];
3718 get_unitab(CP_ACP
, unitab
, 0);
3720 rtfsize
= 100 + strlen(cfg
.font
);
3721 rtf
= smalloc(rtfsize
);
3722 sprintf(rtf
, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3723 GetACP(), cfg
.font
);
3724 rtflen
= strlen(rtf
);
3727 * We want to construct a piece of RTF that specifies the
3728 * same Unicode text. To do this we will read back in
3729 * parallel from the Unicode data in `udata' and the
3730 * non-Unicode data in `tdata'. For each character in
3731 * `tdata' which becomes the right thing in `udata' when
3732 * looked up in `unitab', we just copy straight over from
3733 * tdata. For each one that doesn't, we must WCToMB it
3734 * individually and produce a \u escape sequence.
3736 * It would probably be more robust to just bite the bullet
3737 * and WCToMB each individual Unicode character one by one,
3738 * then MBToWC each one back to see if it was an accurate
3739 * translation; but that strikes me as a horrifying number
3740 * of Windows API calls so I want to see if this faster way
3741 * will work. If it screws up badly we can always revert to
3742 * the simple and slow way.
3744 while (tindex
< len2
&& uindex
< len
&&
3745 tdata
[tindex
] && udata
[uindex
]) {
3746 if (tindex
+ 1 < len2
&&
3747 tdata
[tindex
] == '\r' &&
3748 tdata
[tindex
+1] == '\n') {
3752 if (unitab
[tdata
[tindex
]] == udata
[uindex
]) {
3758 multilen
= WideCharToMultiByte(CP_ACP
, 0, unitab
+uindex
, 1,
3759 NULL
, 0, NULL
, NULL
);
3760 if (multilen
!= 1) {
3761 blen
= sprintf(before
, "{\\uc%d\\u%d", multilen
,
3763 alen
= 1; strcpy(after
, "}");
3765 blen
= sprintf(before
, "\\u%d", udata
[uindex
]);
3766 alen
= 0; after
[0] = '\0';
3769 assert(tindex
+ multilen
<= len2
);
3770 totallen
= blen
+ alen
;
3771 for (i
= 0; i
< multilen
; i
++) {
3772 if (tdata
[tindex
+i
] == '\\' ||
3773 tdata
[tindex
+i
] == '{' ||
3774 tdata
[tindex
+i
] == '}')
3776 else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A)
3777 totallen
+= 6; /* \par\r\n */
3778 else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20)
3784 if (rtfsize
< rtflen
+ totallen
+ 3) {
3785 rtfsize
= rtflen
+ totallen
+ 512;
3786 rtf
= srealloc(rtf
, rtfsize
);
3789 strcpy(rtf
+ rtflen
, before
); rtflen
+= blen
;
3790 for (i
= 0; i
< multilen
; i
++) {
3791 if (tdata
[tindex
+i
] == '\\' ||
3792 tdata
[tindex
+i
] == '{' ||
3793 tdata
[tindex
+i
] == '}') {
3794 rtf
[rtflen
++] = '\\';
3795 rtf
[rtflen
++] = tdata
[tindex
+i
];
3796 } else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A) {
3797 rtflen
+= sprintf(rtf
+rtflen
, "\\par\r\n");
3798 } else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20) {
3799 rtflen
+= sprintf(rtf
+rtflen
, "\\'%02x", tdata
[tindex
+i
]);
3801 rtf
[rtflen
++] = tdata
[tindex
+i
];
3804 strcpy(rtf
+ rtflen
, after
); rtflen
+= alen
;
3810 strcpy(rtf
+ rtflen
, "}");
3813 clipdata3
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, rtflen
);
3814 if (clipdata3
&& (lock3
= GlobalLock(clipdata3
)) != NULL
) {
3816 GlobalUnlock(clipdata3
);
3822 GlobalUnlock(clipdata
);
3823 GlobalUnlock(clipdata2
);
3826 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3828 if (OpenClipboard(hwnd
)) {
3830 SetClipboardData(CF_UNICODETEXT
, clipdata
);
3831 SetClipboardData(CF_TEXT
, clipdata2
);
3833 SetClipboardData(RegisterClipboardFormat(CF_RTF
), clipdata3
);
3836 GlobalFree(clipdata
);
3837 GlobalFree(clipdata2
);
3841 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3844 void get_clip(wchar_t ** p
, int *len
)
3846 static HGLOBAL clipdata
= NULL
;
3847 static wchar_t *converted
= 0;
3856 GlobalUnlock(clipdata
);
3859 } else if (OpenClipboard(NULL
)) {
3860 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
3862 *p
= GlobalLock(clipdata
);
3864 for (p2
= *p
; *p2
; p2
++);
3868 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
3872 s
= GlobalLock(clipdata
);
3873 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
3874 *p
= converted
= smalloc(i
* sizeof(wchar_t));
3875 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
3888 * Move `lines' lines from position `from' to position `to' in the
3891 void optimised_move(int to
, int from
, int lines
)
3896 min
= (to
< from ? to
: from
);
3897 max
= to
+ from
- min
;
3899 r
.left
= offset_width
;
3900 r
.right
= offset_width
+ cols
* font_width
;
3901 r
.top
= offset_height
+ min
* font_height
;
3902 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
3903 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
3908 * Print a message box and perform a fatal exit.
3910 void fatalbox(char *fmt
, ...)
3916 vsprintf(stuff
, fmt
, ap
);
3918 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
3923 * Manage window caption / taskbar flashing, if enabled.
3924 * 0 = stop, 1 = maintain, 2 = start
3926 static void flash_window(int mode
)
3928 static long last_flash
= 0;
3929 static int flashing
= 0;
3930 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
3933 FlashWindow(hwnd
, FALSE
);
3937 } else if (mode
== 2) {
3940 last_flash
= GetTickCount();
3942 FlashWindow(hwnd
, TRUE
);
3945 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
3948 long now
= GetTickCount();
3949 long fdiff
= now
- last_flash
;
3950 if (fdiff
< 0 || fdiff
> 450) {
3952 FlashWindow(hwnd
, TRUE
); /* toggle */
3963 if (mode
== BELL_DEFAULT
) {
3965 * For MessageBeep style bells, we want to be careful of
3966 * timing, because they don't have the nice property of
3967 * PlaySound bells that each one cancels the previous
3968 * active one. So we limit the rate to one per 50ms or so.
3970 static long lastbeep
= 0;
3973 beepdiff
= GetTickCount() - lastbeep
;
3974 if (beepdiff
>= 0 && beepdiff
< 50)
3978 * The above MessageBeep call takes time, so we record the
3979 * time _after_ it finishes rather than before it starts.
3981 lastbeep
= GetTickCount();
3982 } else if (mode
== BELL_WAVEFILE
) {
3983 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
3984 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
3985 sprintf(buf
, "Unable to play sound file\n%s\n"
3986 "Using default sound instead", cfg
.bell_wavefile
);
3987 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
3988 MB_OK
| MB_ICONEXCLAMATION
);
3989 cfg
.beep
= BELL_DEFAULT
;
3992 /* Otherwise, either visual bell or disabled; do nothing here */
3994 flash_window(2); /* start */
3999 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
4001 * Revised by <wez@thebrainroom.com>
4003 static void flip_full_screen(void)
4008 wp
.length
= sizeof(wp
);
4009 GetWindowPlacement(hwnd
, &wp
);
4011 full_screen
= !full_screen
;
4014 if (wp
.showCmd
== SW_SHOWMAXIMIZED
) {
4015 /* Ooops it was already 'zoomed' we have to unzoom it before
4016 * everything will work right.
4018 wp
.showCmd
= SW_SHOWNORMAL
;
4019 SetWindowPlacement(hwnd
, &wp
);
4022 style
= GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_CAPTION
;
4023 style
&= ~WS_VSCROLL
;
4024 if (cfg
.scrollbar_in_fullscreen
)
4025 style
|= WS_VSCROLL
;
4026 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4028 /* This seems to be needed otherwize explorer doesn't notice
4029 * we want to go fullscreen and it's bar is still visible
4031 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
4032 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
4033 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
4036 wp
.showCmd
= SW_SHOWMAXIMIZED
;
4037 SetWindowPlacement(hwnd
, &wp
);
4039 style
= GetWindowLong(hwnd
, GWL_STYLE
) | WS_CAPTION
;
4040 style
&= ~WS_VSCROLL
;
4042 style
|= WS_VSCROLL
;
4043 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4045 /* Don't need to do a SetWindowPos as the resize will force a
4048 wp
.showCmd
= SW_SHOWNORMAL
;
4049 SetWindowPlacement(hwnd
, &wp
);
4052 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4053 MF_BYCOMMAND
| full_screen ? MF_CHECKED
: MF_UNCHECKED
);