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
, (cfg
.resize_action
== RESIZE_DISABLED
) ?
586 MF_GRAYED
: MF_ENABLED
, IDM_FULLSCREEN
, "&Full Screen");
587 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
588 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
592 * Finally show the window!
594 ShowWindow(hwnd
, show
);
595 SetForegroundWindow(hwnd
);
598 * Open the initial log file if there is one.
603 * Set the palette up.
609 has_focus
= (GetForegroundWindow() == hwnd
);
612 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
613 int timer_id
= 0, long_timer
= 0;
615 while (msg
.message
!= WM_QUIT
) {
616 /* Sometimes DispatchMessage calls routines that use their own
617 * GetMessage loop, setup this timer so we get some control back.
619 * Also call term_update() from the timer so that if the host
620 * is sending data flat out we still do redraws.
622 if (timer_id
&& long_timer
) {
623 KillTimer(hwnd
, timer_id
);
624 long_timer
= timer_id
= 0;
627 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
628 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
629 DispatchMessage(&msg
);
631 /* Make sure we blink everything that needs it. */
634 /* Send the paste buffer if there's anything to send */
637 /* If there's nothing new in the queue then we can do everything
638 * we've delayed, reading the socket, writing, and repainting
641 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
644 if (pending_netevent
) {
645 enact_pending_netevent();
647 /* Force the cursor blink on */
650 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
654 /* Okay there is now nothing to do so we make sure the screen is
655 * completely up to date then tell windows to call us in a little
659 KillTimer(hwnd
, timer_id
);
667 flash_window(1); /* maintain */
670 /* Hmm, term_update didn't want to do an update too soon ... */
671 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
673 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
675 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
678 /* There's no point rescanning everything in the message queue
679 * so we do an apparently unnecessary wait here
682 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
696 if (cfg
.protocol
== PROT_SSH
) {
707 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
709 char *do_select(SOCKET skt
, int startup
)
714 events
= (FD_CONNECT
| FD_READ
| FD_WRITE
|
715 FD_OOB
| FD_CLOSE
| FD_ACCEPT
);
720 return "do_select(): internal error (hwnd==NULL)";
721 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
722 switch (WSAGetLastError()) {
724 return "Network is down";
726 return "WSAAsyncSelect(): unknown error";
733 * set or clear the "raw mouse message" mode
735 void set_raw_mouse_mode(int activate
)
737 send_raw_mouse
= activate
;
738 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
742 * Print a message box and close the connection.
744 void connection_fatal(char *fmt
, ...)
750 vsprintf(stuff
, fmt
, ap
);
752 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
753 if (cfg
.close_on_exit
== COE_ALWAYS
)
756 session_closed
= TRUE
;
757 SetWindowText(hwnd
, "PuTTY (inactive)");
762 * Actually do the job requested by a WM_NETEVENT
764 static void enact_pending_netevent(void)
766 static int reentering
= 0;
767 extern int select_result(WPARAM
, LPARAM
);
771 return; /* don't unpend the pending */
773 pending_netevent
= FALSE
;
776 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
779 if (ret
== 0 && !session_closed
) {
780 /* Abnormal exits will already have set session_closed and taken
781 * appropriate action. */
782 if (cfg
.close_on_exit
== COE_ALWAYS
||
783 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
785 session_closed
= TRUE
;
786 SetWindowText(hwnd
, "PuTTY (inactive)");
787 MessageBox(hwnd
, "Connection closed by remote host",
788 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
794 * Copy the colour palette from the configuration data into defpal.
795 * This is non-trivial because the colour indices are different.
797 static void cfgtopalette(void)
800 static const int ww
[] = {
801 6, 7, 8, 9, 10, 11, 12, 13,
802 14, 15, 16, 17, 18, 19, 20, 21,
803 0, 1, 2, 3, 4, 4, 5, 5
806 for (i
= 0; i
< 24; i
++) {
808 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
809 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
810 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
815 * Set up the colour palette.
817 static void init_palette(void)
820 HDC hdc
= GetDC(hwnd
);
822 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
823 logpal
= smalloc(sizeof(*logpal
)
824 - sizeof(logpal
->palPalEntry
)
825 + NCOLOURS
* sizeof(PALETTEENTRY
));
826 logpal
->palVersion
= 0x300;
827 logpal
->palNumEntries
= NCOLOURS
;
828 for (i
= 0; i
< NCOLOURS
; i
++) {
829 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
830 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
831 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
832 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
834 pal
= CreatePalette(logpal
);
836 SelectPalette(hdc
, pal
, FALSE
);
838 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
841 ReleaseDC(hwnd
, hdc
);
844 for (i
= 0; i
< NCOLOURS
; i
++)
845 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
849 for (i
= 0; i
< NCOLOURS
; i
++)
850 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
851 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
855 * Initialise all the fonts we will need initially. There may be as many as
856 * three or as few as one. The other (poentially) twentyone fonts are done
857 * if/when they are needed.
861 * - check the font width and height, correcting our guesses if
864 * - verify that the bold font is the same width as the ordinary
865 * one, and engage shadow bolding if not.
867 * - verify that the underlined font is the same width as the
868 * ordinary one (manual underlining by means of line drawing can
869 * be done in a pinch).
871 static void init_fonts(int pick_width
, int pick_height
)
878 int fw_dontcare
, fw_bold
;
880 for (i
= 0; i
< FONT_MAXNO
; i
++)
883 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
886 if (cfg
.fontisbold
) {
887 fw_dontcare
= FW_BOLD
;
890 fw_dontcare
= FW_DONTCARE
;
897 font_height
= pick_height
;
899 font_height
= cfg
.fontheight
;
900 if (font_height
> 0) {
902 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
905 font_width
= pick_width
;
908 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
909 c, OUT_DEFAULT_PRECIS, \
910 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
911 FIXED_PITCH | FF_DONTCARE, cfg.font)
913 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
915 lfont
.lfHeight
= font_height
;
916 lfont
.lfWidth
= font_width
;
917 lfont
.lfEscapement
= 0;
918 lfont
.lfOrientation
= 0;
919 lfont
.lfWeight
= fw_dontcare
;
920 lfont
.lfItalic
= FALSE
;
921 lfont
.lfUnderline
= FALSE
;
922 lfont
.lfStrikeOut
= FALSE
;
923 lfont
.lfCharSet
= cfg
.fontcharset
;
924 lfont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
925 lfont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
926 lfont
.lfQuality
= DEFAULT_QUALITY
;
927 lfont
.lfPitchAndFamily
= FIXED_PITCH
| FF_DONTCARE
;
928 strncpy(lfont
.lfFaceName
, cfg
.font
, LF_FACESIZE
);
930 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
931 GetTextMetrics(hdc
, &tm
);
933 if (pick_width
== 0 || pick_height
== 0) {
934 font_height
= tm
.tmHeight
;
935 font_width
= tm
.tmAveCharWidth
;
937 font_dualwidth
= (tm
.tmAveCharWidth
!= tm
.tmMaxCharWidth
);
939 #ifdef RDB_DEBUG_PATCH
940 debug(23, "Primary font H=%d, AW=%d, MW=%d",
941 tm
.tmHeight
, tm
.tmAveCharWidth
, tm
.tmMaxCharWidth
);
946 DWORD cset
= tm
.tmCharSet
;
947 memset(&info
, 0xFF, sizeof(info
));
949 /* !!! Yes the next line is right */
950 if (cset
== OEM_CHARSET
)
951 font_codepage
= GetOEMCP();
953 if (TranslateCharsetInfo
954 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
959 GetCPInfo(font_codepage
, &cpinfo
);
960 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
963 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
966 * Some fonts, e.g. 9-pt Courier, draw their underlines
967 * outside their character cell. We successfully prevent
968 * screen corruption by clipping the text output, but then
969 * we lose the underline completely. Here we try to work
970 * out whether this is such a font, and if it is, we set a
971 * flag that causes underlines to be drawn by hand.
973 * Having tried other more sophisticated approaches (such
974 * as examining the TEXTMETRIC structure or requesting the
975 * height of a string), I think we'll do this the brute
976 * force way: we create a small bitmap, draw an underlined
977 * space on it, and test to see whether any pixels are
978 * foreground-coloured. (Since we expect the underline to
979 * go all the way across the character cell, we only search
980 * down a single column of the bitmap, half way across.)
984 HBITMAP und_bm
, und_oldbm
;
988 und_dc
= CreateCompatibleDC(hdc
);
989 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
990 und_oldbm
= SelectObject(und_dc
, und_bm
);
991 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
992 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
993 SetTextColor(und_dc
, RGB(255, 255, 255));
994 SetBkColor(und_dc
, RGB(0, 0, 0));
995 SetBkMode(und_dc
, OPAQUE
);
996 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
998 for (i
= 0; i
< font_height
; i
++) {
999 c
= GetPixel(und_dc
, font_width
/ 2, i
);
1000 if (c
!= RGB(0, 0, 0))
1003 SelectObject(und_dc
, und_oldbm
);
1004 DeleteObject(und_bm
);
1007 und_mode
= UND_LINE
;
1008 DeleteObject(fonts
[FONT_UNDERLINE
]);
1009 fonts
[FONT_UNDERLINE
] = 0;
1013 if (bold_mode
== BOLD_FONT
) {
1014 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
1018 descent
= tm
.tmAscent
+ 1;
1019 if (descent
>= font_height
)
1020 descent
= font_height
- 1;
1022 for (i
= 0; i
< 3; i
++) {
1024 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
1025 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
1032 ReleaseDC(hwnd
, hdc
);
1034 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1035 und_mode
= UND_LINE
;
1036 DeleteObject(fonts
[FONT_UNDERLINE
]);
1037 fonts
[FONT_UNDERLINE
] = 0;
1040 if (bold_mode
== BOLD_FONT
&&
1041 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1042 bold_mode
= BOLD_SHADOW
;
1043 DeleteObject(fonts
[FONT_BOLD
]);
1044 fonts
[FONT_BOLD
] = 0;
1046 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1051 static void another_font(int fontno
)
1054 int fw_dontcare
, fw_bold
;
1058 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1061 basefont
= (fontno
& ~(FONT_BOLDUND
));
1062 if (basefont
!= fontno
&& !fontflag
[basefont
])
1063 another_font(basefont
);
1065 if (cfg
.fontisbold
) {
1066 fw_dontcare
= FW_BOLD
;
1069 fw_dontcare
= FW_DONTCARE
;
1073 c
= cfg
.fontcharset
;
1079 if (fontno
& FONT_WIDE
)
1081 if (fontno
& FONT_NARROW
)
1083 if (fontno
& FONT_OEM
)
1085 if (fontno
& FONT_BOLD
)
1087 if (fontno
& FONT_UNDERLINE
)
1091 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1092 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1093 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1094 FIXED_PITCH
| FF_DONTCARE
, s
);
1096 fontflag
[fontno
] = 1;
1099 static void deinit_fonts(void)
1102 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1104 DeleteObject(fonts
[i
]);
1110 void request_resize(int w
, int h
)
1114 /* If the window is maximized supress resizing attempts */
1115 if (IsZoomed(hwnd
)) {
1116 if (cfg
.resize_action
!= RESIZE_FONT
)
1120 if (cfg
.resize_action
== RESIZE_DISABLED
) return;
1121 if (h
== rows
&& w
== cols
) return;
1123 /* Sanity checks ... */
1125 static int first_time
= 1;
1128 switch (first_time
) {
1130 /* Get the size of the screen */
1131 if (GetClientRect(GetDesktopWindow(), &ss
))
1132 /* first_time = 0 */ ;
1138 /* Make sure the values are sane */
1139 width
= (ss
.right
- ss
.left
- extra_width
) / 4;
1140 height
= (ss
.bottom
- ss
.top
- extra_height
) / 6;
1142 if (w
> width
|| h
> height
)
1151 term_size(h
, w
, cfg
.savelines
);
1153 if (cfg
.resize_action
!= RESIZE_FONT
) {
1154 width
= extra_width
+ font_width
* w
;
1155 height
= extra_height
+ font_height
* h
;
1157 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1158 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1159 SWP_NOMOVE
| SWP_NOZORDER
);
1163 InvalidateRect(hwnd
, NULL
, TRUE
);
1166 static void reset_window(int reinit
) {
1168 * This function decides how to resize or redraw when the
1169 * user changes something.
1171 * This function doesn't like to change the terminal size but if the
1172 * font size is locked that may be it's only soluion.
1174 int win_width
, win_height
;
1177 #ifdef RDB_DEBUG_PATCH
1178 debug((27, "reset_window()"));
1181 /* Current window sizes ... */
1182 GetWindowRect(hwnd
, &wr
);
1183 GetClientRect(hwnd
, &cr
);
1185 win_width
= cr
.right
- cr
.left
;
1186 win_height
= cr
.bottom
- cr
.top
;
1188 /* Are we being forced to reload the fonts ? */
1190 #ifdef RDB_DEBUG_PATCH
1191 debug((27, "reset_window() -- Forced deinit"));
1197 /* Oh, looks like we're minimised */
1198 if (win_width
== 0 || win_height
== 0)
1201 /* Is the window out of position ? */
1203 (offset_width
!= (win_width
-font_width
*cols
)/2 ||
1204 offset_height
!= (win_height
-font_height
*rows
)/2) ){
1205 offset_width
= (win_width
-font_width
*cols
)/2;
1206 offset_height
= (win_height
-font_height
*rows
)/2;
1207 InvalidateRect(hwnd
, NULL
, TRUE
);
1208 #ifdef RDB_DEBUG_PATCH
1209 debug((27, "reset_window() -> Reposition terminal"));
1213 if (IsZoomed(hwnd
)) {
1214 /* We're fullscreen, this means we must not change the size of
1215 * the window so it's the font size or the terminal itself.
1218 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1219 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1221 if (cfg
.resize_action
== RESIZE_FONT
) {
1222 if ( font_width
!= win_width
/cols
||
1223 font_height
!= win_height
/rows
) {
1225 init_fonts(win_width
/cols
, win_height
/rows
);
1226 offset_width
= (win_width
-font_width
*cols
)/2;
1227 offset_height
= (win_height
-font_height
*rows
)/2;
1228 InvalidateRect(hwnd
, NULL
, TRUE
);
1229 #ifdef RDB_DEBUG_PATCH
1230 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1231 font_width
, font_height
));
1235 if ( font_width
!= win_width
/cols
||
1236 font_height
!= win_height
/rows
) {
1237 /* Our only choice at this point is to change the
1238 * size of the terminal; Oh well.
1240 term_size( win_height
/font_height
, win_width
/font_width
,
1242 offset_width
= (win_width
-font_width
*cols
)/2;
1243 offset_height
= (win_height
-font_height
*rows
)/2;
1244 InvalidateRect(hwnd
, NULL
, TRUE
);
1245 #ifdef RDB_DEBUG_PATCH
1246 debug((27, "reset_window() -> Zoomed term_size"));
1253 /* Hmm, a force re-init means we should ignore the current window
1254 * so we resize to the default font size.
1257 #ifdef RDB_DEBUG_PATCH
1258 debug((27, "reset_window() -> Forced re-init"));
1261 offset_width
= offset_height
= cfg
.window_border
;
1262 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1263 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1265 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1266 win_height
!= font_height
*rows
+ offset_height
*2) {
1268 /* If this is too large windows will resize it to the maximum
1269 * allowed window size, we will then be back in here and resize
1270 * the font or terminal to fit.
1272 SetWindowPos(hwnd
, NULL
, 0, 0,
1273 font_width
*cols
+ extra_width
,
1274 font_height
*rows
+ extra_height
,
1275 SWP_NOMOVE
| SWP_NOZORDER
);
1280 /* Okay the user doesn't want us to change the font so we try the
1281 * window. But that may be too big for the screen which forces us
1282 * to change the terminal.
1284 if ((cfg
.resize_action
!= RESIZE_FONT
&& reinit
==0) || reinit
>0) {
1285 offset_width
= offset_height
= cfg
.window_border
;
1286 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1287 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1289 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1290 win_height
!= font_height
*rows
+ offset_height
*2) {
1295 GetClientRect(GetDesktopWindow(), &ss
);
1296 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1297 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1300 if ( rows
> height
|| cols
> width
) {
1301 if ( height
> rows
) height
= rows
;
1302 if ( width
> cols
) width
= cols
;
1303 term_size(height
, width
, cfg
.savelines
);
1304 #ifdef RDB_DEBUG_PATCH
1305 debug((27, "reset_window() -> term resize to (%d,%d)",
1310 SetWindowPos(hwnd
, NULL
, 0, 0,
1311 font_width
*cols
+ extra_width
,
1312 font_height
*rows
+ extra_height
,
1313 SWP_NOMOVE
| SWP_NOZORDER
);
1315 InvalidateRect(hwnd
, NULL
, TRUE
);
1316 #ifdef RDB_DEBUG_PATCH
1317 debug((27, "reset_window() -> window resize to (%d,%d)",
1318 font_width
*cols
+ extra_width
,
1319 font_height
*rows
+ extra_height
));
1325 /* We're allowed to or must change the font but do we want to ? */
1327 if (font_width
!= (win_width
-cfg
.window_border
*2)/cols
||
1328 font_height
!= (win_height
-cfg
.window_border
*2)/rows
) {
1331 init_fonts((win_width
-cfg
.window_border
*2)/cols
,
1332 (win_height
-cfg
.window_border
*2)/rows
);
1333 offset_width
= (win_width
-font_width
*cols
)/2;
1334 offset_height
= (win_height
-font_height
*rows
)/2;
1336 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+offset_width
*2;
1337 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1339 InvalidateRect(hwnd
, NULL
, TRUE
);
1340 #ifdef RDB_DEBUG_PATCH
1341 debug((25, "reset_window() -> font resize to (%d,%d)",
1342 font_width
, font_height
));
1347 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
, int alt
)
1349 int thistime
= GetMessageTime();
1351 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1352 lastbtn
= MBT_NOTHING
;
1353 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
, alt
);
1357 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1358 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1359 lastact
== MA_2CLK ? MA_3CLK
:
1360 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1365 if (lastact
!= MA_NOTHING
)
1366 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
, alt
);
1367 lasttime
= thistime
;
1371 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1372 * into a cooked one (SELECT, EXTEND, PASTE).
1374 Mouse_Button
translate_button(Mouse_Button button
)
1376 if (button
== MBT_LEFT
)
1378 if (button
== MBT_MIDDLE
)
1379 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1380 if (button
== MBT_RIGHT
)
1381 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1382 return 0; /* shouldn't happen */
1385 static void show_mouseptr(int show
)
1387 static int cursor_visible
= 1;
1388 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1390 if (cursor_visible
&& !show
)
1392 else if (!cursor_visible
&& show
)
1394 cursor_visible
= show
;
1397 static int is_alt_pressed(void)
1400 int r
= GetKeyboardState(keystate
);
1403 if (keystate
[VK_MENU
] & 0x80)
1405 if (keystate
[VK_RMENU
] & 0x80)
1410 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1411 WPARAM wParam
, LPARAM lParam
)
1414 static int ignore_clip
= FALSE
;
1415 static int resizing
= FALSE
;
1416 static int need_backend_resize
= FALSE
;
1420 if (pending_netevent
)
1421 enact_pending_netevent();
1427 if (cfg
.ping_interval
> 0) {
1430 if (now
- last_movement
> cfg
.ping_interval
) {
1431 back
->special(TS_PING
);
1432 last_movement
= now
;
1435 net_pending_errors();
1441 if (!cfg
.warn_on_close
|| session_closed
||
1443 "Are you sure you want to close this session?",
1444 "PuTTY Exit Confirmation",
1445 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1446 DestroyWindow(hwnd
);
1453 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1465 PROCESS_INFORMATION pi
;
1466 HANDLE filemap
= NULL
;
1468 if (wParam
== IDM_DUPSESS
) {
1470 * Allocate a file-mapping memory chunk for the
1473 SECURITY_ATTRIBUTES sa
;
1476 sa
.nLength
= sizeof(sa
);
1477 sa
.lpSecurityDescriptor
= NULL
;
1478 sa
.bInheritHandle
= TRUE
;
1479 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1482 0, sizeof(Config
), NULL
);
1484 p
= (Config
*) MapViewOfFile(filemap
,
1486 0, 0, sizeof(Config
));
1488 *p
= cfg
; /* structure copy */
1492 sprintf(c
, "putty &%p", filemap
);
1494 } else if (wParam
== IDM_SAVEDSESS
) {
1496 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1497 cl
= smalloc(16 + strlen(session
)); /* 8, but play safe */
1499 cl
= NULL
; /* not a very important failure mode */
1501 sprintf(cl
, "putty @%s", session
);
1507 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1509 si
.lpReserved
= NULL
;
1510 si
.lpDesktop
= NULL
;
1514 si
.lpReserved2
= NULL
;
1515 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1516 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1519 CloseHandle(filemap
);
1529 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1532 if (!do_reconfig(hwnd
))
1536 /* Disable full-screen if resizing forbidden */
1537 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
1538 EnableMenuItem(m
, IDM_FULLSCREEN
, MF_BYCOMMAND
|
1539 (cfg
.resize_action
== RESIZE_DISABLED
)
1540 ? MF_GRAYED
: MF_ENABLED
);
1541 /* Gracefully unzoom if necessary */
1543 (cfg
.resize_action
== RESIZE_DISABLED
)) {
1548 if (strcmp(prev_cfg
.logfilename
, cfg
.logfilename
) ||
1549 prev_cfg
.logtype
!= cfg
.logtype
) {
1550 logfclose(); /* reset logging */
1556 * Flush the line discipline's edit buffer in the
1557 * case where local editing has just been disabled.
1559 ldisc_send(NULL
, 0, 0);
1567 /* Screen size changed ? */
1568 if (cfg
.height
!= prev_cfg
.height
||
1569 cfg
.width
!= prev_cfg
.width
||
1570 cfg
.savelines
!= prev_cfg
.savelines
||
1571 cfg
.resize_action
!= RESIZE_TERM
)
1572 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1574 /* Enable or disable the scroll bar, etc */
1576 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1577 LONG nexflag
, exflag
=
1578 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1581 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1582 if (cfg
.alwaysontop
) {
1583 nexflag
|= WS_EX_TOPMOST
;
1584 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1585 SWP_NOMOVE
| SWP_NOSIZE
);
1587 nexflag
&= ~(WS_EX_TOPMOST
);
1588 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1589 SWP_NOMOVE
| SWP_NOSIZE
);
1592 if (cfg
.sunken_edge
)
1593 nexflag
|= WS_EX_CLIENTEDGE
;
1595 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1599 cfg
.scrollbar_in_fullscreen
: cfg
.scrollbar
)
1602 nflg
&= ~WS_VSCROLL
;
1603 if (cfg
.resize_action
== RESIZE_DISABLED
)
1604 nflg
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1606 nflg
|= (WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1608 if (nflg
!= flag
|| nexflag
!= exflag
) {
1610 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1611 if (nexflag
!= exflag
)
1612 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1614 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1615 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1616 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1624 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1629 set_title(cfg
.wintitle
);
1630 if (IsIconic(hwnd
)) {
1632 cfg
.win_name_always ? window_name
:
1636 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1637 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1638 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1639 cfg
.fontheight
!= prev_cfg
.fontheight
||
1640 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1641 cfg
.vtmode
!= prev_cfg
.vtmode
||
1642 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1643 (cfg
.resize_action
!= RESIZE_FONT
&&
1644 prev_cfg
.resize_action
== RESIZE_FONT
))
1647 InvalidateRect(hwnd
, NULL
, TRUE
);
1648 reset_window(init_lvl
);
1649 net_pending_errors();
1662 back
->special(TS_AYT
);
1663 net_pending_errors();
1666 back
->special(TS_BRK
);
1667 net_pending_errors();
1670 back
->special(TS_SYNCH
);
1671 net_pending_errors();
1674 back
->special(TS_EC
);
1675 net_pending_errors();
1678 back
->special(TS_EL
);
1679 net_pending_errors();
1682 back
->special(TS_GA
);
1683 net_pending_errors();
1686 back
->special(TS_NOP
);
1687 net_pending_errors();
1690 back
->special(TS_ABORT
);
1691 net_pending_errors();
1694 back
->special(TS_AO
);
1695 net_pending_errors();
1698 back
->special(TS_IP
);
1699 net_pending_errors();
1702 back
->special(TS_SUSP
);
1703 net_pending_errors();
1706 back
->special(TS_EOR
);
1707 net_pending_errors();
1710 back
->special(TS_EOF
);
1711 net_pending_errors();
1718 * We get this if the System menu has been activated
1725 * We get this if the System menu has been activated
1726 * using the keyboard. This might happen from within
1727 * TranslateKey, in which case it really wants to be
1728 * followed by a `space' character to actually _bring
1729 * the menu up_ rather than just sitting there in
1730 * `ready to appear' state.
1732 show_mouseptr(1); /* make sure pointer is visible */
1734 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1736 case IDM_FULLSCREEN
:
1740 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1741 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1746 #define X_POS(l) ((int)(short)LOWORD(l))
1747 #define Y_POS(l) ((int)(short)HIWORD(l))
1749 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1750 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1751 #define WHEEL_DELTA 120
1754 wheel_accumulator
+= (short) HIWORD(wParam
);
1755 wParam
= LOWORD(wParam
);
1757 /* process events when the threshold is reached */
1758 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
1761 /* reduce amount for next time */
1762 if (wheel_accumulator
> 0) {
1764 wheel_accumulator
-= WHEEL_DELTA
;
1765 } else if (wheel_accumulator
< 0) {
1767 wheel_accumulator
+= WHEEL_DELTA
;
1771 if (send_raw_mouse
) {
1772 /* send a mouse-down followed by a mouse up */
1776 TO_CHR_X(X_POS(lParam
)),
1777 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1778 wParam
& MK_CONTROL
, is_alt_pressed());
1779 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1780 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1781 wParam
& MK_CONTROL
, is_alt_pressed());
1783 /* trigger a scroll */
1785 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
1790 case WM_LBUTTONDOWN
:
1791 case WM_MBUTTONDOWN
:
1792 case WM_RBUTTONDOWN
:
1800 case WM_LBUTTONDOWN
:
1804 case WM_MBUTTONDOWN
:
1805 button
= MBT_MIDDLE
;
1808 case WM_RBUTTONDOWN
:
1817 button
= MBT_MIDDLE
;
1825 button
= press
= 0; /* shouldn't happen */
1829 * Special case: in full-screen mode, if the left
1830 * button is clicked in the very top left corner of the
1831 * window, we put up the System menu instead of doing
1834 if (full_screen
&& press
&& button
== MBT_LEFT
&&
1835 X_POS(lParam
) == 0 && Y_POS(lParam
) == 0) {
1836 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_MOUSEMENU
, 0);
1841 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1842 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
,
1846 term_mouse(button
, MA_RELEASE
,
1847 TO_CHR_X(X_POS(lParam
)),
1848 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1849 wParam
& MK_CONTROL
, is_alt_pressed());
1857 * Add the mouse position and message time to the random
1860 noise_ultralight(lParam
);
1862 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1864 if (wParam
& MK_LBUTTON
)
1866 else if (wParam
& MK_MBUTTON
)
1870 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1871 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1872 wParam
& MK_CONTROL
, is_alt_pressed());
1875 case WM_NCMOUSEMOVE
:
1877 noise_ultralight(lParam
);
1879 case WM_IGNORE_CLIP
:
1880 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1882 case WM_DESTROYCLIPBOARD
:
1885 ignore_clip
= FALSE
;
1891 hdc
= BeginPaint(hwnd
, &p
);
1893 SelectPalette(hdc
, pal
, TRUE
);
1894 RealizePalette(hdc
);
1897 (p
.rcPaint
.left
-offset_width
)/font_width
,
1898 (p
.rcPaint
.top
-offset_height
)/font_height
,
1899 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
1900 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
1903 p
.rcPaint
.left
< offset_width
||
1904 p
.rcPaint
.top
< offset_height
||
1905 p
.rcPaint
.right
>= offset_width
+ font_width
*cols
||
1906 p
.rcPaint
.bottom
>= offset_height
+ font_height
*rows
)
1908 HBRUSH fillcolour
, oldbrush
;
1910 fillcolour
= CreateSolidBrush (
1911 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1912 oldbrush
= SelectObject(hdc
, fillcolour
);
1913 edge
= CreatePen(PS_SOLID
, 0,
1914 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1915 oldpen
= SelectObject(hdc
, edge
);
1917 ExcludeClipRect(hdc
,
1918 offset_width
, offset_height
,
1919 offset_width
+font_width
*cols
,
1920 offset_height
+font_height
*rows
);
1922 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1923 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1925 // SelectClipRgn(hdc, NULL);
1927 SelectObject(hdc
, oldbrush
);
1928 DeleteObject(fillcolour
);
1929 SelectObject(hdc
, oldpen
);
1932 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
1933 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
1939 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1940 * but the only one that's likely to try to overload us is FD_READ.
1941 * This means buffering just one is fine.
1943 if (pending_netevent
)
1944 enact_pending_netevent();
1946 pending_netevent
= TRUE
;
1947 pend_netevent_wParam
= wParam
;
1948 pend_netevent_lParam
= lParam
;
1949 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
1950 enact_pending_netevent();
1952 time(&last_movement
);
1956 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
1958 flash_window(0); /* stop */
1970 case WM_ENTERSIZEMOVE
:
1971 #ifdef RDB_DEBUG_PATCH
1972 debug((27, "WM_ENTERSIZEMOVE"));
1976 need_backend_resize
= FALSE
;
1978 case WM_EXITSIZEMOVE
:
1981 #ifdef RDB_DEBUG_PATCH
1982 debug((27, "WM_EXITSIZEMOVE"));
1984 if (need_backend_resize
) {
1985 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1986 InvalidateRect(hwnd
, NULL
, TRUE
);
1991 * This does two jobs:
1992 * 1) Keep the sizetip uptodate
1993 * 2) Make sure the window size is _stepped_ in units of the font size.
1995 if (cfg
.resize_action
== RESIZE_TERM
&& !alt_pressed
) {
1996 int width
, height
, w
, h
, ew
, eh
;
1997 LPRECT r
= (LPRECT
) lParam
;
1999 if ( !need_backend_resize
&&
2000 (cfg
.height
!= rows
|| cfg
.width
!= cols
)) {
2002 * Great! It seems the host has been changing the terminal
2003 * size, well the user is now grabbing so this is probably
2004 * the least confusing solution in the long run even though
2005 * it a is suprise. Unfortunatly the only way to prevent
2006 * this seems to be to let the host change the window size
2007 * and as that's a user option we're still right back here.
2009 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
2011 InvalidateRect(hwnd
, NULL
, TRUE
);
2012 need_backend_resize
= TRUE
;
2015 width
= r
->right
- r
->left
- extra_width
;
2016 height
= r
->bottom
- r
->top
- extra_height
;
2017 w
= (width
+ font_width
/ 2) / font_width
;
2020 h
= (height
+ font_height
/ 2) / font_height
;
2023 UpdateSizeTip(hwnd
, w
, h
);
2024 ew
= width
- w
* font_width
;
2025 eh
= height
- h
* font_height
;
2027 if (wParam
== WMSZ_LEFT
||
2028 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2034 if (wParam
== WMSZ_TOP
||
2035 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2045 int width
, height
, w
, h
, rv
= 0;
2046 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
2047 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2048 LPRECT r
= (LPRECT
) lParam
;
2050 width
= r
->right
- r
->left
- ex_width
;
2051 height
= r
->bottom
- r
->top
- ex_height
;
2053 w
= (width
+ cols
/2)/cols
;
2054 h
= (height
+ rows
/2)/rows
;
2055 if ( r
->right
!= r
->left
+ w
*cols
+ ex_width
)
2058 if (wParam
== WMSZ_LEFT
||
2059 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2060 r
->left
= r
->right
- w
*cols
- ex_width
;
2062 r
->right
= r
->left
+ w
*cols
+ ex_width
;
2064 if (r
->bottom
!= r
->top
+ h
*rows
+ ex_height
)
2067 if (wParam
== WMSZ_TOP
||
2068 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2069 r
->top
= r
->bottom
- h
*rows
- ex_height
;
2071 r
->bottom
= r
->top
+ h
*rows
+ ex_height
;
2075 /* break; (never reached) */
2077 #ifdef RDB_DEBUG_PATCH
2078 debug((27, "WM_SIZE %s (%d,%d)",
2079 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2080 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2081 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2082 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2084 LOWORD(lParam
), HIWORD(lParam
)));
2086 if (wParam
== SIZE_MINIMIZED
) {
2088 cfg
.win_name_always ? window_name
: icon_name
);
2091 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2092 SetWindowText(hwnd
, window_name
);
2094 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2095 /* A resize, well it better be a minimize. */
2099 int width
, height
, w
, h
;
2101 width
= LOWORD(lParam
);
2102 height
= HIWORD(lParam
);
2105 if (wParam
== SIZE_MAXIMIZED
) {
2109 if (cfg
.resize_action
!= RESIZE_FONT
) {
2110 w
= width
/ font_width
;
2112 h
= height
/ font_height
;
2115 term_size(h
, w
, cfg
.savelines
);
2118 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2120 if (cfg
.resize_action
!= RESIZE_FONT
)
2121 term_size(prev_rows
, prev_cols
, cfg
.savelines
);
2124 /* This is an unexpected resize, these will normally happen
2125 * if the window is too large. Probably either the user
2126 * selected a huge font or the screen size has changed.
2128 * This is also called with minimize.
2130 else reset_window(-1);
2134 * Don't call back->size in mid-resize. (To prevent
2135 * massive numbers of resize events getting sent
2136 * down the connection during an NT opaque drag.)
2139 if (cfg
.resize_action
== RESIZE_TERM
&& !alt_pressed
) {
2140 need_backend_resize
= TRUE
;
2141 w
= (width
-cfg
.window_border
*2) / font_width
;
2143 h
= (height
-cfg
.window_border
*2) / font_height
;
2154 switch (LOWORD(wParam
)) {
2168 term_scroll(0, +rows
/ 2);
2171 term_scroll(0, -rows
/ 2);
2173 case SB_THUMBPOSITION
:
2175 term_scroll(1, HIWORD(wParam
));
2179 case WM_PALETTECHANGED
:
2180 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2181 HDC hdc
= get_ctx();
2183 if (RealizePalette(hdc
) > 0)
2189 case WM_QUERYNEWPALETTE
:
2191 HDC hdc
= get_ctx();
2193 if (RealizePalette(hdc
) > 0)
2205 * Add the scan code and keypress timing to the random
2208 noise_ultralight(lParam
);
2211 * We don't do TranslateMessage since it disassociates the
2212 * resulting CHAR message from the KEYDOWN that sparked it,
2213 * which we occasionally don't want. Instead, we process
2214 * KEYDOWN, and call the Win32 translator functions so that
2215 * we get the translations under _our_ control.
2218 unsigned char buf
[20];
2221 if (wParam
== VK_PROCESSKEY
) {
2224 m
.message
= WM_KEYDOWN
;
2226 m
.lParam
= lParam
& 0xdfff;
2227 TranslateMessage(&m
);
2229 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2231 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2235 * Interrupt an ongoing paste. I'm not sure
2236 * this is sensible, but for the moment it's
2237 * preferable to having to faff about buffering
2243 * We need not bother about stdin backlogs
2244 * here, because in GUI PuTTY we can't do
2245 * anything about it anyway; there's no means
2246 * of asking Windows to hold off on KEYDOWN
2247 * messages. We _have_ to buffer everything
2250 ldisc_send(buf
, len
, 1);
2255 net_pending_errors();
2257 case WM_INPUTLANGCHANGE
:
2259 /* wParam == Font number */
2260 /* lParam == Locale */
2262 HKL NewInputLocale
= (HKL
) lParam
;
2264 // lParam == GetKeyboardLayout(0);
2266 GetLocaleInfo(LOWORD(NewInputLocale
),
2267 LOCALE_IDEFAULTANSICODEPAGE
, lbuf
, sizeof(lbuf
));
2269 kbd_codepage
= atoi(lbuf
);
2273 if(wParam
== IMN_SETOPENSTATUS
) {
2274 HIMC hImc
= ImmGetContext(hwnd
);
2275 ImmSetCompositionFont(hImc
, &lfont
);
2276 ImmReleaseContext(hwnd
, hImc
);
2280 case WM_IME_COMPOSITION
:
2286 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2287 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2289 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2290 break; /* fall back to DefWindowProc */
2292 hIMC
= ImmGetContext(hwnd
);
2293 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2296 buff
= (char*) smalloc(n
);
2297 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2298 luni_send((unsigned short *)buff
, n
/ 2, 1);
2301 ImmReleaseContext(hwnd
, hIMC
);
2306 if (wParam
& 0xFF00) {
2307 unsigned char buf
[2];
2310 buf
[0] = wParam
>> 8;
2311 lpage_send(kbd_codepage
, buf
, 2, 1);
2313 char c
= (unsigned char) wParam
;
2314 lpage_send(kbd_codepage
, &c
, 1, 1);
2320 * Nevertheless, we are prepared to deal with WM_CHAR
2321 * messages, should they crop up. So if someone wants to
2322 * post the things to us as part of a macro manoeuvre,
2323 * we're ready to cope.
2326 char c
= (unsigned char)wParam
;
2327 lpage_send(CP_ACP
, &c
, 1, 1);
2331 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2332 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2337 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2341 * Move the system caret. (We maintain one, even though it's
2342 * invisible, for the benefit of blind people: apparently some
2343 * helper software tracks the system caret, so we should arrange to
2346 void sys_cursor(int x
, int y
)
2351 if (!has_focus
) return;
2353 SetCaretPos(x
* font_width
+ offset_width
,
2354 y
* font_height
+ offset_height
);
2356 /* IMM calls on Win98 and beyond only */
2357 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2359 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2360 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2362 /* we should have the IMM functions */
2363 hIMC
= ImmGetContext(hwnd
);
2364 cf
.dwStyle
= CFS_POINT
;
2365 cf
.ptCurrentPos
.x
= x
* font_width
+ offset_width
;
2366 cf
.ptCurrentPos
.y
= y
* font_height
+ offset_height
;
2367 ImmSetCompositionWindow(hIMC
, &cf
);
2369 ImmReleaseContext(hwnd
, hIMC
);
2373 * Draw a line of text in the window, at given character
2374 * coordinates, in given attributes.
2376 * We are allowed to fiddle with the contents of `text'.
2378 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2379 unsigned long attr
, int lattr
)
2382 int nfg
, nbg
, nfont
;
2385 int force_manual_underline
= 0;
2386 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2387 int char_width
= fnt_width
;
2388 int text_adjust
= 0;
2389 static int *IpDx
= 0, IpDxLEN
= 0;
2391 if (attr
& ATTR_WIDE
)
2394 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2396 if (len
> IpDxLEN
) {
2398 IpDx
= smalloc((len
+ 16) * sizeof(int));
2399 IpDxLEN
= (len
+ 16);
2401 for (i
= 0; i
< IpDxLEN
; i
++)
2402 IpDx
[i
] = char_width
;
2405 /* Only want the left half of double width lines */
2406 if (lattr
!= LATTR_NORM
&& x
*2 >= cols
)
2414 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
2415 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2416 attr
^= ATTR_CUR_XOR
;
2420 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2421 /* Assume a poorman font is borken in other ways too. */
2431 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2434 if (attr
& ATTR_NARROW
)
2435 nfont
|= FONT_NARROW
;
2437 /* Special hack for the VT100 linedraw glyphs. */
2438 if ((attr
& CSET_MASK
) == 0x2300) {
2439 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2440 switch ((unsigned char) (text
[0])) {
2442 text_adjust
= -2 * font_height
/ 5;
2445 text_adjust
= -1 * font_height
/ 5;
2448 text_adjust
= font_height
/ 5;
2451 text_adjust
= 2 * font_height
/ 5;
2454 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2457 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2458 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2459 if (attr
& ATTR_UNDER
) {
2460 attr
&= ~ATTR_UNDER
;
2461 force_manual_underline
= 1;
2466 /* Anything left as an original character set is unprintable. */
2467 if (DIRECT_CHAR(attr
)) {
2470 memset(text
, 0xFD, len
);
2474 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2477 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2478 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2479 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2481 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2482 nfont
|= FONT_UNDERLINE
;
2483 another_font(nfont
);
2484 if (!fonts
[nfont
]) {
2485 if (nfont
& FONT_UNDERLINE
)
2486 force_manual_underline
= 1;
2487 /* Don't do the same for manual bold, it could be bad news. */
2489 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2491 another_font(nfont
);
2493 nfont
= FONT_NORMAL
;
2494 if (attr
& ATTR_REVERSE
) {
2499 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2501 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2505 SelectObject(hdc
, fonts
[nfont
]);
2506 SetTextColor(hdc
, fg
);
2507 SetBkColor(hdc
, bg
);
2508 SetBkMode(hdc
, OPAQUE
);
2511 line_box
.right
= x
+ char_width
* len
;
2512 line_box
.bottom
= y
+ font_height
;
2514 /* Only want the left half of double width lines */
2515 if (line_box
.right
> font_width
*cols
+offset_width
)
2516 line_box
.right
= font_width
*cols
+offset_width
;
2518 /* We're using a private area for direct to font. (512 chars.) */
2519 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2520 /* Ho Hum, dbcs fonts are a PITA! */
2521 /* To display on W9x I have to convert to UCS */
2522 static wchar_t *uni_buf
= 0;
2523 static int uni_len
= 0;
2525 if (len
> uni_len
) {
2527 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2530 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2531 uni_buf
[nlen
] = 0xFFFD;
2532 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2533 IpDx
[nlen
] += char_width
;
2534 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2535 text
+mptr
, 2, uni_buf
+nlen
, 1);
2540 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2541 text
+mptr
, 1, uni_buf
+nlen
, 1);
2549 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2550 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2551 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2552 SetBkMode(hdc
, TRANSPARENT
);
2553 ExtTextOutW(hdc
, x
- 1,
2554 y
- font_height
* (lattr
==
2555 LATTR_BOT
) + text_adjust
,
2556 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2560 } else if (DIRECT_FONT(attr
)) {
2562 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2563 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2564 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2565 SetBkMode(hdc
, TRANSPARENT
);
2567 /* GRR: This draws the character outside it's box and can leave
2568 * 'droppings' even with the clip box! I suppose I could loop it
2569 * one character at a time ... yuk.
2571 * Or ... I could do a test print with "W", and use +1 or -1 for this
2572 * shift depending on if the leftmost column is blank...
2574 ExtTextOut(hdc
, x
- 1,
2575 y
- font_height
* (lattr
==
2576 LATTR_BOT
) + text_adjust
,
2577 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2580 /* And 'normal' unicode characters */
2581 static WCHAR
*wbuf
= NULL
;
2582 static int wlen
= 0;
2587 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2589 for (i
= 0; i
< len
; i
++)
2590 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2593 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2594 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2596 /* And the shadow bold hack. */
2597 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2598 SetBkMode(hdc
, TRANSPARENT
);
2599 ExtTextOutW(hdc
, x
- 1,
2600 y
- font_height
* (lattr
==
2601 LATTR_BOT
) + text_adjust
,
2602 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2605 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2606 (und_mode
== UND_LINE
2607 && (attr
& ATTR_UNDER
)))) {
2610 if (lattr
== LATTR_BOT
)
2611 dec
= dec
* 2 - font_height
;
2613 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2614 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2615 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2616 oldpen
= SelectObject(hdc
, oldpen
);
2617 DeleteObject(oldpen
);
2621 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2622 unsigned long attr
, int lattr
)
2628 int ctype
= cfg
.cursor_type
;
2630 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2631 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2632 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2636 attr
|= TATTR_RIGHTCURS
;
2639 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2640 if (attr
& ATTR_WIDE
)
2647 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2650 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2651 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2652 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2653 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2654 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2655 Polyline(hdc
, pts
, 5);
2656 oldpen
= SelectObject(hdc
, oldpen
);
2657 DeleteObject(oldpen
);
2658 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2659 int startx
, starty
, dx
, dy
, length
, i
;
2662 starty
= y
+ descent
;
2665 length
= char_width
;
2668 if (attr
& TATTR_RIGHTCURS
)
2669 xadjust
= char_width
- 1;
2670 startx
= x
+ xadjust
;
2674 length
= font_height
;
2676 if (attr
& TATTR_ACTCURS
) {
2679 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2680 MoveToEx(hdc
, startx
, starty
, NULL
);
2681 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2682 oldpen
= SelectObject(hdc
, oldpen
);
2683 DeleteObject(oldpen
);
2685 for (i
= 0; i
< length
; i
++) {
2687 SetPixel(hdc
, startx
, starty
, colours
[23]);
2696 /* This function gets the actual width of a character in the normal font.
2698 int CharWidth(Context ctx
, int uc
) {
2702 /* If the font max is the same as the font ave width then this
2703 * function is a no-op.
2705 if (!font_dualwidth
) return 1;
2707 switch (uc
& CSET_MASK
) {
2709 uc
= unitab_line
[uc
& 0xFF];
2712 uc
= unitab_xterm
[uc
& 0xFF];
2715 uc
= unitab_scoacs
[uc
& 0xFF];
2718 if (DIRECT_FONT(uc
)) {
2719 if (dbcs_screenfont
) return 1;
2721 /* Speedup, I know of no font where ascii is the wrong width */
2722 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
2725 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
2726 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2727 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
2728 another_font(FONT_OEM
);
2729 if (!fonts
[FONT_OEM
]) return 0;
2731 SelectObject(hdc
, fonts
[FONT_OEM
]);
2735 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
2736 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
2739 /* Speedup, I know of no font where ascii is the wrong width */
2740 if (uc
>= ' ' && uc
<= '~') return 1;
2742 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2743 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
2744 /* Okay that one worked */ ;
2745 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
2746 /* This should work on 9x too, but it's "less accurate" */ ;
2751 ibuf
+= font_width
/ 2 -1;
2758 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2759 * codes. Returns number of bytes used or zero to drop the message
2760 * or -1 to forward the message to windows.
2762 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2763 unsigned char *output
)
2766 int scan
, left_alt
= 0, key_down
, shift_state
;
2768 unsigned char *p
= output
;
2769 static int alt_sum
= 0;
2771 HKL kbd_layout
= GetKeyboardLayout(0);
2773 static WORD keys
[3];
2774 static int compose_char
= 0;
2775 static WPARAM compose_key
= 0;
2777 r
= GetKeyboardState(keystate
);
2779 memset(keystate
, 0, sizeof(keystate
));
2782 #define SHOW_TOASCII_RESULT
2783 { /* Tell us all about key events */
2784 static BYTE oldstate
[256];
2785 static int first
= 1;
2789 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2792 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2794 } else if ((HIWORD(lParam
) & KF_UP
)
2795 && scan
== (HIWORD(lParam
) & 0xFF)) {
2799 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2800 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2813 debug(("VK_%02x", wParam
));
2815 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2817 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2819 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2820 if (ch
>= ' ' && ch
<= '~')
2821 debug((", '%c'", ch
));
2823 debug((", $%02x", ch
));
2826 debug((", KB0=%02x", keys
[0]));
2828 debug((", KB1=%02x", keys
[1]));
2830 debug((", KB2=%02x", keys
[2]));
2832 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2834 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2836 if ((HIWORD(lParam
) & KF_EXTENDED
))
2838 if ((HIWORD(lParam
) & KF_UP
))
2842 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2843 else if ((HIWORD(lParam
) & KF_UP
))
2844 oldstate
[wParam
& 0xFF] ^= 0x80;
2846 oldstate
[wParam
& 0xFF] ^= 0x81;
2848 for (ch
= 0; ch
< 256; ch
++)
2849 if (oldstate
[ch
] != keystate
[ch
])
2850 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2852 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2856 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2857 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2861 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2862 if ((cfg
.funky_type
== 3 ||
2863 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2864 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2866 wParam
= VK_EXECUTE
;
2868 /* UnToggle NUMLock */
2869 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2870 keystate
[VK_NUMLOCK
] ^= 1;
2873 /* And write back the 'adjusted' state */
2874 SetKeyboardState(keystate
);
2877 /* Disable Auto repeat if required */
2878 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2881 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2884 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2886 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2887 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2888 if (cfg
.ctrlaltkeys
)
2889 keystate
[VK_MENU
] = 0;
2891 keystate
[VK_RMENU
] = 0x80;
2896 alt_pressed
= (left_alt
&& key_down
);
2898 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2899 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2900 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2902 /* Note if AltGr was pressed and if it was used as a compose key */
2903 if (!compose_state
) {
2904 compose_key
= 0x100;
2905 if (cfg
.compose_key
) {
2906 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2907 compose_key
= wParam
;
2909 if (wParam
== VK_APPS
)
2910 compose_key
= wParam
;
2913 if (wParam
== compose_key
) {
2914 if (compose_state
== 0
2915 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2917 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2921 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
2925 * Record that we pressed key so the scroll window can be reset, but
2926 * be careful to avoid Shift-UP/Down
2928 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
&&
2929 wParam
!= VK_MENU
&& wParam
!= VK_CONTROL
) {
2933 if (compose_state
> 1 && left_alt
)
2936 /* Sanitize the number pad if not using a PC NumPad */
2937 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2938 && cfg
.funky_type
!= 2)
2939 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
2940 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
2944 nParam
= VK_NUMPAD0
;
2947 nParam
= VK_NUMPAD1
;
2950 nParam
= VK_NUMPAD2
;
2953 nParam
= VK_NUMPAD3
;
2956 nParam
= VK_NUMPAD4
;
2959 nParam
= VK_NUMPAD5
;
2962 nParam
= VK_NUMPAD6
;
2965 nParam
= VK_NUMPAD7
;
2968 nParam
= VK_NUMPAD8
;
2971 nParam
= VK_NUMPAD9
;
2974 nParam
= VK_DECIMAL
;
2978 if (keystate
[VK_NUMLOCK
] & 1)
2985 /* If a key is pressed and AltGr is not active */
2986 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
2987 /* Okay, prepare for most alts then ... */
2991 /* Lets see if it's a pattern we know all about ... */
2992 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2993 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2996 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2997 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
3000 if (wParam
== VK_INSERT
&& shift_state
== 1) {
3004 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
3007 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
3008 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
3011 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
&&
3012 (cfg
.resize_action
!= RESIZE_DISABLED
)) {
3016 /* Control-Numlock for app-keypad mode switch */
3017 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
3018 app_keypad_keys
^= 1;
3022 /* Nethack keypad */
3023 if (cfg
.nethack_keypad
&& !left_alt
) {
3026 *p
++ = shift_state ?
'B' : 'b';
3029 *p
++ = shift_state ?
'J' : 'j';
3032 *p
++ = shift_state ?
'N' : 'n';
3035 *p
++ = shift_state ?
'H' : 'h';
3038 *p
++ = shift_state ?
'.' : '.';
3041 *p
++ = shift_state ?
'L' : 'l';
3044 *p
++ = shift_state ?
'Y' : 'y';
3047 *p
++ = shift_state ?
'K' : 'k';
3050 *p
++ = shift_state ?
'U' : 'u';
3055 /* Application Keypad */
3059 if (cfg
.funky_type
== 3 ||
3060 (cfg
.funky_type
<= 1 &&
3061 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3075 if (app_keypad_keys
&& !cfg
.no_applic_k
)
3112 if (cfg
.funky_type
== 2) {
3117 } else if (shift_state
)
3124 if (cfg
.funky_type
== 2)
3128 if (cfg
.funky_type
== 2)
3132 if (cfg
.funky_type
== 2)
3137 if (HIWORD(lParam
) & KF_EXTENDED
)
3143 if (xkey
>= 'P' && xkey
<= 'S')
3144 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3146 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3148 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3153 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3154 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3158 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3164 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3168 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3172 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3177 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3182 /* Control-2 to Control-8 are special */
3183 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3184 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3187 if (shift_state
== 2 && wParam
== 0xBD) {
3191 if (shift_state
== 2 && wParam
== 0xDF) {
3195 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
3202 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3203 * for integer decimal nn.)
3205 * We also deal with the weird ones here. Linux VCs replace F1
3206 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3207 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3213 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3216 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3219 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3222 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3225 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3228 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3231 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3234 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3237 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3240 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3273 if ((shift_state
&2) == 0) switch (wParam
) {
3293 /* Reorder edit keys to physical order */
3294 if (cfg
.funky_type
== 3 && code
<= 6)
3295 code
= "\0\2\1\4\5\3\6"[code
];
3297 if (vt52_mode
&& code
> 0 && code
<= 6) {
3298 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3302 if (cfg
.funky_type
== 5 && /* SCO function keys */
3303 code
>= 11 && code
<= 34) {
3304 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3307 case VK_F1
: index
= 0; break;
3308 case VK_F2
: index
= 1; break;
3309 case VK_F3
: index
= 2; break;
3310 case VK_F4
: index
= 3; break;
3311 case VK_F5
: index
= 4; break;
3312 case VK_F6
: index
= 5; break;
3313 case VK_F7
: index
= 6; break;
3314 case VK_F8
: index
= 7; break;
3315 case VK_F9
: index
= 8; break;
3316 case VK_F10
: index
= 9; break;
3317 case VK_F11
: index
= 10; break;
3318 case VK_F12
: index
= 11; break;
3320 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3321 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3322 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3325 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3326 code
>= 1 && code
<= 6) {
3327 char codes
[] = "HL.FIG";
3331 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3335 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3342 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3345 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3348 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3349 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3352 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3354 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3356 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3359 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3360 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3364 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3369 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3370 * some reason seems to send VK_CLEAR to Windows...).
3393 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3395 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
3396 /* VT100 & VT102 manuals both state the app cursor keys
3397 * only work if the app keypad is on.
3399 if (!app_keypad_keys
)
3401 /* Useful mapping of Ctrl-arrows */
3402 if (shift_state
== 2)
3406 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3408 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3415 * Finally, deal with Return ourselves. (Win95 seems to
3416 * foul it up when Alt is pressed, for some reason.)
3418 if (wParam
== VK_RETURN
) { /* Return */
3424 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3425 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3430 /* Okay we've done everything interesting; let windows deal with
3431 * the boring stuff */
3435 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3436 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3438 keystate
[VK_CAPITAL
] = 0;
3441 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3442 #ifdef SHOW_TOASCII_RESULT
3443 if (r
== 1 && !key_down
) {
3445 if (in_utf
|| dbcs_screenfont
)
3446 debug((", (U+%04x)", alt_sum
));
3448 debug((", LCH(%d)", alt_sum
));
3450 debug((", ACH(%d)", keys
[0]));
3455 for (r1
= 0; r1
< r
; r1
++) {
3456 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3465 * Interrupt an ongoing paste. I'm not sure this is
3466 * sensible, but for the moment it's preferable to
3467 * having to faff about buffering things.
3472 for (i
= 0; i
< r
; i
++) {
3473 unsigned char ch
= (unsigned char) keys
[i
];
3475 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3480 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3484 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3485 MessageBeep(MB_ICONHAND
);
3489 luni_send(&keybuf
, 1, 1);
3497 if (in_utf
|| dbcs_screenfont
) {
3499 luni_send(&keybuf
, 1, 1);
3501 ch
= (char) alt_sum
;
3503 * We need not bother about stdin
3504 * backlogs here, because in GUI PuTTY
3505 * we can't do anything about it
3506 * anyway; there's no means of asking
3507 * Windows to hold off on KEYDOWN
3508 * messages. We _have_ to buffer
3509 * everything we're sent.
3511 ldisc_send(&ch
, 1, 1);
3515 lpage_send(kbd_codepage
, &ch
, 1, 1);
3517 if(capsOn
&& ch
< 0x80) {
3520 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3521 luni_send(cbuf
+!left_alt
, 1+!!left_alt
, 1);
3526 lpage_send(kbd_codepage
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3532 /* This is so the ALT-Numpad and dead keys work correctly. */
3537 /* If we're definitly not building up an ALT-54321 then clear it */
3540 /* If we will be using alt_sum fix the 256s */
3541 else if (keys
[0] && (in_utf
|| dbcs_screenfont
))
3546 * ALT alone may or may not want to bring up the System menu.
3547 * If it's not meant to, we return 0 on presses or releases of
3548 * ALT, to show that we've swallowed the keystroke. Otherwise
3549 * we return -1, which means Windows will give the keystroke
3550 * its default handling (i.e. bring up the System menu).
3552 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3558 void set_title(char *title
)
3561 window_name
= smalloc(1 + strlen(title
));
3562 strcpy(window_name
, title
);
3563 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3564 SetWindowText(hwnd
, title
);
3567 void set_icon(char *title
)
3570 icon_name
= smalloc(1 + strlen(title
));
3571 strcpy(icon_name
, title
);
3572 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3573 SetWindowText(hwnd
, title
);
3576 void set_sbar(int total
, int start
, int page
)
3583 si
.cbSize
= sizeof(si
);
3584 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3586 si
.nMax
= total
- 1;
3590 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3593 Context
get_ctx(void)
3599 SelectPalette(hdc
, pal
, FALSE
);
3605 void free_ctx(Context ctx
)
3607 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3608 ReleaseDC(hwnd
, ctx
);
3611 static void real_palette_set(int n
, int r
, int g
, int b
)
3614 logpal
->palPalEntry
[n
].peRed
= r
;
3615 logpal
->palPalEntry
[n
].peGreen
= g
;
3616 logpal
->palPalEntry
[n
].peBlue
= b
;
3617 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3618 colours
[n
] = PALETTERGB(r
, g
, b
);
3619 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3621 colours
[n
] = RGB(r
, g
, b
);
3624 void palette_set(int n
, int r
, int g
, int b
)
3626 static const int first
[21] = {
3627 0, 2, 4, 6, 8, 10, 12, 14,
3628 1, 3, 5, 7, 9, 11, 13, 15,
3631 real_palette_set(first
[n
], r
, g
, b
);
3633 real_palette_set(first
[n
] + 1, r
, g
, b
);
3635 HDC hdc
= get_ctx();
3636 UnrealizeObject(pal
);
3637 RealizePalette(hdc
);
3642 void palette_reset(void)
3646 for (i
= 0; i
< NCOLOURS
; i
++) {
3648 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3649 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3650 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3651 logpal
->palPalEntry
[i
].peFlags
= 0;
3652 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3653 defpal
[i
].rgbtGreen
,
3654 defpal
[i
].rgbtBlue
);
3656 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3657 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3662 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3664 RealizePalette(hdc
);
3669 void write_aclip(char *data
, int len
, int must_deselect
)
3674 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3677 lock
= GlobalLock(clipdata
);
3680 memcpy(lock
, data
, len
);
3681 ((unsigned char *) lock
)[len
] = 0;
3682 GlobalUnlock(clipdata
);
3685 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3687 if (OpenClipboard(hwnd
)) {
3689 SetClipboardData(CF_TEXT
, clipdata
);
3692 GlobalFree(clipdata
);
3695 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3699 * Note: unlike write_aclip() this will not append a nul.
3701 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3703 HGLOBAL clipdata
, clipdata2
, clipdata3
;
3705 void *lock
, *lock2
, *lock3
;
3707 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3709 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3710 len
* sizeof(wchar_t));
3711 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3713 if (!clipdata
|| !clipdata2
|| !clipdata3
) {
3715 GlobalFree(clipdata
);
3717 GlobalFree(clipdata2
);
3719 GlobalFree(clipdata3
);
3722 if (!(lock
= GlobalLock(clipdata
)))
3724 if (!(lock2
= GlobalLock(clipdata2
)))
3727 memcpy(lock
, data
, len
* sizeof(wchar_t));
3728 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3730 if (cfg
.rtf_paste
) {
3731 wchar_t unitab
[256];
3733 unsigned char *tdata
= (unsigned char *)lock2
;
3734 wchar_t *udata
= (wchar_t *)lock
;
3735 int rtflen
= 0, uindex
= 0, tindex
= 0;
3737 int multilen
, blen
, alen
, totallen
, i
;
3738 char before
[16], after
[4];
3740 get_unitab(CP_ACP
, unitab
, 0);
3742 rtfsize
= 100 + strlen(cfg
.font
);
3743 rtf
= smalloc(rtfsize
);
3744 sprintf(rtf
, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3745 GetACP(), cfg
.font
);
3746 rtflen
= strlen(rtf
);
3749 * We want to construct a piece of RTF that specifies the
3750 * same Unicode text. To do this we will read back in
3751 * parallel from the Unicode data in `udata' and the
3752 * non-Unicode data in `tdata'. For each character in
3753 * `tdata' which becomes the right thing in `udata' when
3754 * looked up in `unitab', we just copy straight over from
3755 * tdata. For each one that doesn't, we must WCToMB it
3756 * individually and produce a \u escape sequence.
3758 * It would probably be more robust to just bite the bullet
3759 * and WCToMB each individual Unicode character one by one,
3760 * then MBToWC each one back to see if it was an accurate
3761 * translation; but that strikes me as a horrifying number
3762 * of Windows API calls so I want to see if this faster way
3763 * will work. If it screws up badly we can always revert to
3764 * the simple and slow way.
3766 while (tindex
< len2
&& uindex
< len
&&
3767 tdata
[tindex
] && udata
[uindex
]) {
3768 if (tindex
+ 1 < len2
&&
3769 tdata
[tindex
] == '\r' &&
3770 tdata
[tindex
+1] == '\n') {
3774 if (unitab
[tdata
[tindex
]] == udata
[uindex
]) {
3780 multilen
= WideCharToMultiByte(CP_ACP
, 0, unitab
+uindex
, 1,
3781 NULL
, 0, NULL
, NULL
);
3782 if (multilen
!= 1) {
3783 blen
= sprintf(before
, "{\\uc%d\\u%d", multilen
,
3785 alen
= 1; strcpy(after
, "}");
3787 blen
= sprintf(before
, "\\u%d", udata
[uindex
]);
3788 alen
= 0; after
[0] = '\0';
3791 assert(tindex
+ multilen
<= len2
);
3792 totallen
= blen
+ alen
;
3793 for (i
= 0; i
< multilen
; i
++) {
3794 if (tdata
[tindex
+i
] == '\\' ||
3795 tdata
[tindex
+i
] == '{' ||
3796 tdata
[tindex
+i
] == '}')
3798 else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A)
3799 totallen
+= 6; /* \par\r\n */
3800 else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20)
3806 if (rtfsize
< rtflen
+ totallen
+ 3) {
3807 rtfsize
= rtflen
+ totallen
+ 512;
3808 rtf
= srealloc(rtf
, rtfsize
);
3811 strcpy(rtf
+ rtflen
, before
); rtflen
+= blen
;
3812 for (i
= 0; i
< multilen
; i
++) {
3813 if (tdata
[tindex
+i
] == '\\' ||
3814 tdata
[tindex
+i
] == '{' ||
3815 tdata
[tindex
+i
] == '}') {
3816 rtf
[rtflen
++] = '\\';
3817 rtf
[rtflen
++] = tdata
[tindex
+i
];
3818 } else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A) {
3819 rtflen
+= sprintf(rtf
+rtflen
, "\\par\r\n");
3820 } else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20) {
3821 rtflen
+= sprintf(rtf
+rtflen
, "\\'%02x", tdata
[tindex
+i
]);
3823 rtf
[rtflen
++] = tdata
[tindex
+i
];
3826 strcpy(rtf
+ rtflen
, after
); rtflen
+= alen
;
3832 strcpy(rtf
+ rtflen
, "}");
3835 clipdata3
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, rtflen
);
3836 if (clipdata3
&& (lock3
= GlobalLock(clipdata3
)) != NULL
) {
3838 GlobalUnlock(clipdata3
);
3844 GlobalUnlock(clipdata
);
3845 GlobalUnlock(clipdata2
);
3848 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3850 if (OpenClipboard(hwnd
)) {
3852 SetClipboardData(CF_UNICODETEXT
, clipdata
);
3853 SetClipboardData(CF_TEXT
, clipdata2
);
3855 SetClipboardData(RegisterClipboardFormat(CF_RTF
), clipdata3
);
3858 GlobalFree(clipdata
);
3859 GlobalFree(clipdata2
);
3863 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3866 void get_clip(wchar_t ** p
, int *len
)
3868 static HGLOBAL clipdata
= NULL
;
3869 static wchar_t *converted
= 0;
3878 GlobalUnlock(clipdata
);
3881 } else if (OpenClipboard(NULL
)) {
3882 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
3884 *p
= GlobalLock(clipdata
);
3886 for (p2
= *p
; *p2
; p2
++);
3890 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
3894 s
= GlobalLock(clipdata
);
3895 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
3896 *p
= converted
= smalloc(i
* sizeof(wchar_t));
3897 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
3910 * Move `lines' lines from position `from' to position `to' in the
3913 void optimised_move(int to
, int from
, int lines
)
3918 min
= (to
< from ? to
: from
);
3919 max
= to
+ from
- min
;
3921 r
.left
= offset_width
;
3922 r
.right
= offset_width
+ cols
* font_width
;
3923 r
.top
= offset_height
+ min
* font_height
;
3924 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
3925 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
3930 * Print a message box and perform a fatal exit.
3932 void fatalbox(char *fmt
, ...)
3938 vsprintf(stuff
, fmt
, ap
);
3940 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
3945 * Manage window caption / taskbar flashing, if enabled.
3946 * 0 = stop, 1 = maintain, 2 = start
3948 static void flash_window(int mode
)
3950 static long last_flash
= 0;
3951 static int flashing
= 0;
3952 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
3955 FlashWindow(hwnd
, FALSE
);
3959 } else if (mode
== 2) {
3962 last_flash
= GetTickCount();
3964 FlashWindow(hwnd
, TRUE
);
3967 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
3970 long now
= GetTickCount();
3971 long fdiff
= now
- last_flash
;
3972 if (fdiff
< 0 || fdiff
> 450) {
3974 FlashWindow(hwnd
, TRUE
); /* toggle */
3985 if (mode
== BELL_DEFAULT
) {
3987 * For MessageBeep style bells, we want to be careful of
3988 * timing, because they don't have the nice property of
3989 * PlaySound bells that each one cancels the previous
3990 * active one. So we limit the rate to one per 50ms or so.
3992 static long lastbeep
= 0;
3995 beepdiff
= GetTickCount() - lastbeep
;
3996 if (beepdiff
>= 0 && beepdiff
< 50)
4000 * The above MessageBeep call takes time, so we record the
4001 * time _after_ it finishes rather than before it starts.
4003 lastbeep
= GetTickCount();
4004 } else if (mode
== BELL_WAVEFILE
) {
4005 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
4006 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
4007 sprintf(buf
, "Unable to play sound file\n%s\n"
4008 "Using default sound instead", cfg
.bell_wavefile
);
4009 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
4010 MB_OK
| MB_ICONEXCLAMATION
);
4011 cfg
.beep
= BELL_DEFAULT
;
4014 /* Otherwise, either visual bell or disabled; do nothing here */
4016 flash_window(2); /* start */
4021 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
4023 * Revised by <wez@thebrainroom.com>
4025 static void flip_full_screen(void)
4030 wp
.length
= sizeof(wp
);
4031 GetWindowPlacement(hwnd
, &wp
);
4033 full_screen
= !full_screen
;
4036 if (wp
.showCmd
== SW_SHOWMAXIMIZED
) {
4037 /* Ooops it was already 'zoomed' we have to unzoom it before
4038 * everything will work right.
4040 wp
.showCmd
= SW_SHOWNORMAL
;
4041 SetWindowPlacement(hwnd
, &wp
);
4044 style
= GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_CAPTION
;
4045 style
&= ~WS_VSCROLL
;
4046 if (cfg
.scrollbar_in_fullscreen
)
4047 style
|= WS_VSCROLL
;
4048 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4050 /* This seems to be needed otherwize explorer doesn't notice
4051 * we want to go fullscreen and it's bar is still visible
4053 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
4054 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
4055 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
4058 wp
.showCmd
= SW_SHOWMAXIMIZED
;
4059 SetWindowPlacement(hwnd
, &wp
);
4061 style
= GetWindowLong(hwnd
, GWL_STYLE
) | WS_CAPTION
;
4062 style
&= ~WS_VSCROLL
;
4064 style
|= WS_VSCROLL
;
4065 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4067 /* Don't need to do a SetWindowPos as the resize will force a
4070 wp
.showCmd
= SW_SHOWNORMAL
;
4071 SetWindowPlacement(hwnd
, &wp
);
4074 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4075 MF_BYCOMMAND
| full_screen ? MF_CHECKED
: MF_UNCHECKED
);