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 int resizing
;
1412 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1413 WPARAM wParam
, LPARAM lParam
)
1416 static int ignore_clip
= FALSE
;
1417 static int need_backend_resize
= FALSE
;
1421 if (pending_netevent
)
1422 enact_pending_netevent();
1428 if (cfg
.ping_interval
> 0) {
1431 if (now
- last_movement
> cfg
.ping_interval
) {
1432 back
->special(TS_PING
);
1433 last_movement
= now
;
1436 net_pending_errors();
1442 if (!cfg
.warn_on_close
|| session_closed
||
1444 "Are you sure you want to close this session?",
1445 "PuTTY Exit Confirmation",
1446 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1447 DestroyWindow(hwnd
);
1454 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1466 PROCESS_INFORMATION pi
;
1467 HANDLE filemap
= NULL
;
1469 if (wParam
== IDM_DUPSESS
) {
1471 * Allocate a file-mapping memory chunk for the
1474 SECURITY_ATTRIBUTES sa
;
1477 sa
.nLength
= sizeof(sa
);
1478 sa
.lpSecurityDescriptor
= NULL
;
1479 sa
.bInheritHandle
= TRUE
;
1480 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1483 0, sizeof(Config
), NULL
);
1485 p
= (Config
*) MapViewOfFile(filemap
,
1487 0, 0, sizeof(Config
));
1489 *p
= cfg
; /* structure copy */
1493 sprintf(c
, "putty &%p", filemap
);
1495 } else if (wParam
== IDM_SAVEDSESS
) {
1497 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1498 cl
= smalloc(16 + strlen(session
)); /* 8, but play safe */
1500 cl
= NULL
; /* not a very important failure mode */
1502 sprintf(cl
, "putty @%s", session
);
1508 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1510 si
.lpReserved
= NULL
;
1511 si
.lpDesktop
= NULL
;
1515 si
.lpReserved2
= NULL
;
1516 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1517 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1520 CloseHandle(filemap
);
1530 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1533 if (!do_reconfig(hwnd
))
1537 /* Disable full-screen if resizing forbidden */
1538 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
1539 EnableMenuItem(m
, IDM_FULLSCREEN
, MF_BYCOMMAND
|
1540 (cfg
.resize_action
== RESIZE_DISABLED
)
1541 ? MF_GRAYED
: MF_ENABLED
);
1542 /* Gracefully unzoom if necessary */
1544 (cfg
.resize_action
== RESIZE_DISABLED
)) {
1549 if (strcmp(prev_cfg
.logfilename
, cfg
.logfilename
) ||
1550 prev_cfg
.logtype
!= cfg
.logtype
) {
1551 logfclose(); /* reset logging */
1557 * Flush the line discipline's edit buffer in the
1558 * case where local editing has just been disabled.
1560 ldisc_send(NULL
, 0, 0);
1568 /* Screen size changed ? */
1569 if (cfg
.height
!= prev_cfg
.height
||
1570 cfg
.width
!= prev_cfg
.width
||
1571 cfg
.savelines
!= prev_cfg
.savelines
||
1572 cfg
.resize_action
!= RESIZE_TERM
)
1573 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1575 /* Enable or disable the scroll bar, etc */
1577 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1578 LONG nexflag
, exflag
=
1579 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1582 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1583 if (cfg
.alwaysontop
) {
1584 nexflag
|= WS_EX_TOPMOST
;
1585 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1586 SWP_NOMOVE
| SWP_NOSIZE
);
1588 nexflag
&= ~(WS_EX_TOPMOST
);
1589 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1590 SWP_NOMOVE
| SWP_NOSIZE
);
1593 if (cfg
.sunken_edge
)
1594 nexflag
|= WS_EX_CLIENTEDGE
;
1596 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1600 cfg
.scrollbar_in_fullscreen
: cfg
.scrollbar
)
1603 nflg
&= ~WS_VSCROLL
;
1604 if (cfg
.resize_action
== RESIZE_DISABLED
)
1605 nflg
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1607 nflg
|= (WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1609 if (nflg
!= flag
|| nexflag
!= exflag
) {
1611 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1612 if (nexflag
!= exflag
)
1613 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1615 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1616 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1617 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1625 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1630 set_title(cfg
.wintitle
);
1631 if (IsIconic(hwnd
)) {
1633 cfg
.win_name_always ? window_name
:
1637 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1638 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1639 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1640 cfg
.fontheight
!= prev_cfg
.fontheight
||
1641 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1642 cfg
.vtmode
!= prev_cfg
.vtmode
||
1643 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1644 (cfg
.resize_action
!= RESIZE_FONT
&&
1645 prev_cfg
.resize_action
== RESIZE_FONT
))
1648 InvalidateRect(hwnd
, NULL
, TRUE
);
1649 reset_window(init_lvl
);
1650 net_pending_errors();
1663 back
->special(TS_AYT
);
1664 net_pending_errors();
1667 back
->special(TS_BRK
);
1668 net_pending_errors();
1671 back
->special(TS_SYNCH
);
1672 net_pending_errors();
1675 back
->special(TS_EC
);
1676 net_pending_errors();
1679 back
->special(TS_EL
);
1680 net_pending_errors();
1683 back
->special(TS_GA
);
1684 net_pending_errors();
1687 back
->special(TS_NOP
);
1688 net_pending_errors();
1691 back
->special(TS_ABORT
);
1692 net_pending_errors();
1695 back
->special(TS_AO
);
1696 net_pending_errors();
1699 back
->special(TS_IP
);
1700 net_pending_errors();
1703 back
->special(TS_SUSP
);
1704 net_pending_errors();
1707 back
->special(TS_EOR
);
1708 net_pending_errors();
1711 back
->special(TS_EOF
);
1712 net_pending_errors();
1719 * We get this if the System menu has been activated
1726 * We get this if the System menu has been activated
1727 * using the keyboard. This might happen from within
1728 * TranslateKey, in which case it really wants to be
1729 * followed by a `space' character to actually _bring
1730 * the menu up_ rather than just sitting there in
1731 * `ready to appear' state.
1733 show_mouseptr(1); /* make sure pointer is visible */
1735 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1737 case IDM_FULLSCREEN
:
1741 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1742 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1747 #define X_POS(l) ((int)(short)LOWORD(l))
1748 #define Y_POS(l) ((int)(short)HIWORD(l))
1750 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1751 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1752 #define WHEEL_DELTA 120
1755 wheel_accumulator
+= (short) HIWORD(wParam
);
1756 wParam
= LOWORD(wParam
);
1758 /* process events when the threshold is reached */
1759 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
1762 /* reduce amount for next time */
1763 if (wheel_accumulator
> 0) {
1765 wheel_accumulator
-= WHEEL_DELTA
;
1766 } else if (wheel_accumulator
< 0) {
1768 wheel_accumulator
+= WHEEL_DELTA
;
1772 if (send_raw_mouse
) {
1773 /* send a mouse-down followed by a mouse up */
1777 TO_CHR_X(X_POS(lParam
)),
1778 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1779 wParam
& MK_CONTROL
, is_alt_pressed());
1780 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1781 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1782 wParam
& MK_CONTROL
, is_alt_pressed());
1784 /* trigger a scroll */
1786 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
1791 case WM_LBUTTONDOWN
:
1792 case WM_MBUTTONDOWN
:
1793 case WM_RBUTTONDOWN
:
1801 case WM_LBUTTONDOWN
:
1805 case WM_MBUTTONDOWN
:
1806 button
= MBT_MIDDLE
;
1809 case WM_RBUTTONDOWN
:
1818 button
= MBT_MIDDLE
;
1826 button
= press
= 0; /* shouldn't happen */
1830 * Special case: in full-screen mode, if the left
1831 * button is clicked in the very top left corner of the
1832 * window, we put up the System menu instead of doing
1835 if (full_screen
&& press
&& button
== MBT_LEFT
&&
1836 X_POS(lParam
) == 0 && Y_POS(lParam
) == 0) {
1837 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_MOUSEMENU
, 0);
1842 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1843 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
,
1847 term_mouse(button
, MA_RELEASE
,
1848 TO_CHR_X(X_POS(lParam
)),
1849 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1850 wParam
& MK_CONTROL
, is_alt_pressed());
1858 * Add the mouse position and message time to the random
1861 noise_ultralight(lParam
);
1863 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1865 if (wParam
& MK_LBUTTON
)
1867 else if (wParam
& MK_MBUTTON
)
1871 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1872 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1873 wParam
& MK_CONTROL
, is_alt_pressed());
1876 case WM_NCMOUSEMOVE
:
1878 noise_ultralight(lParam
);
1880 case WM_IGNORE_CLIP
:
1881 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1883 case WM_DESTROYCLIPBOARD
:
1886 ignore_clip
= FALSE
;
1892 hdc
= BeginPaint(hwnd
, &p
);
1894 SelectPalette(hdc
, pal
, TRUE
);
1895 RealizePalette(hdc
);
1898 (p
.rcPaint
.left
-offset_width
)/font_width
,
1899 (p
.rcPaint
.top
-offset_height
)/font_height
,
1900 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
1901 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
1904 p
.rcPaint
.left
< offset_width
||
1905 p
.rcPaint
.top
< offset_height
||
1906 p
.rcPaint
.right
>= offset_width
+ font_width
*cols
||
1907 p
.rcPaint
.bottom
>= offset_height
+ font_height
*rows
)
1909 HBRUSH fillcolour
, oldbrush
;
1911 fillcolour
= CreateSolidBrush (
1912 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1913 oldbrush
= SelectObject(hdc
, fillcolour
);
1914 edge
= CreatePen(PS_SOLID
, 0,
1915 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1916 oldpen
= SelectObject(hdc
, edge
);
1918 ExcludeClipRect(hdc
,
1919 offset_width
, offset_height
,
1920 offset_width
+font_width
*cols
,
1921 offset_height
+font_height
*rows
);
1923 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1924 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1926 // SelectClipRgn(hdc, NULL);
1928 SelectObject(hdc
, oldbrush
);
1929 DeleteObject(fillcolour
);
1930 SelectObject(hdc
, oldpen
);
1933 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
1934 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
1940 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1941 * but the only one that's likely to try to overload us is FD_READ.
1942 * This means buffering just one is fine.
1944 if (pending_netevent
)
1945 enact_pending_netevent();
1947 pending_netevent
= TRUE
;
1948 pend_netevent_wParam
= wParam
;
1949 pend_netevent_lParam
= lParam
;
1950 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
1951 enact_pending_netevent();
1953 time(&last_movement
);
1957 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
1959 flash_window(0); /* stop */
1971 case WM_ENTERSIZEMOVE
:
1972 #ifdef RDB_DEBUG_PATCH
1973 debug((27, "WM_ENTERSIZEMOVE"));
1977 need_backend_resize
= FALSE
;
1979 case WM_EXITSIZEMOVE
:
1982 #ifdef RDB_DEBUG_PATCH
1983 debug((27, "WM_EXITSIZEMOVE"));
1985 if (need_backend_resize
) {
1986 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1987 InvalidateRect(hwnd
, NULL
, TRUE
);
1992 * This does two jobs:
1993 * 1) Keep the sizetip uptodate
1994 * 2) Make sure the window size is _stepped_ in units of the font size.
1996 if (cfg
.resize_action
== RESIZE_TERM
&& !alt_pressed
) {
1997 int width
, height
, w
, h
, ew
, eh
;
1998 LPRECT r
= (LPRECT
) lParam
;
2000 if ( !need_backend_resize
&&
2001 (cfg
.height
!= rows
|| cfg
.width
!= cols
)) {
2003 * Great! It seems the host has been changing the terminal
2004 * size, well the user is now grabbing so this is probably
2005 * the least confusing solution in the long run even though
2006 * it a is suprise. Unfortunatly the only way to prevent
2007 * this seems to be to let the host change the window size
2008 * and as that's a user option we're still right back here.
2010 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
2012 InvalidateRect(hwnd
, NULL
, TRUE
);
2013 need_backend_resize
= TRUE
;
2016 width
= r
->right
- r
->left
- extra_width
;
2017 height
= r
->bottom
- r
->top
- extra_height
;
2018 w
= (width
+ font_width
/ 2) / font_width
;
2021 h
= (height
+ font_height
/ 2) / font_height
;
2024 UpdateSizeTip(hwnd
, w
, h
);
2025 ew
= width
- w
* font_width
;
2026 eh
= height
- h
* font_height
;
2028 if (wParam
== WMSZ_LEFT
||
2029 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2035 if (wParam
== WMSZ_TOP
||
2036 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2046 int width
, height
, w
, h
, rv
= 0;
2047 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
2048 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2049 LPRECT r
= (LPRECT
) lParam
;
2051 width
= r
->right
- r
->left
- ex_width
;
2052 height
= r
->bottom
- r
->top
- ex_height
;
2054 w
= (width
+ cols
/2)/cols
;
2055 h
= (height
+ rows
/2)/rows
;
2056 if ( r
->right
!= r
->left
+ w
*cols
+ ex_width
)
2059 if (wParam
== WMSZ_LEFT
||
2060 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2061 r
->left
= r
->right
- w
*cols
- ex_width
;
2063 r
->right
= r
->left
+ w
*cols
+ ex_width
;
2065 if (r
->bottom
!= r
->top
+ h
*rows
+ ex_height
)
2068 if (wParam
== WMSZ_TOP
||
2069 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2070 r
->top
= r
->bottom
- h
*rows
- ex_height
;
2072 r
->bottom
= r
->top
+ h
*rows
+ ex_height
;
2076 /* break; (never reached) */
2078 #ifdef RDB_DEBUG_PATCH
2079 debug((27, "WM_SIZE %s (%d,%d)",
2080 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2081 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2082 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2083 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2085 LOWORD(lParam
), HIWORD(lParam
)));
2087 if (wParam
== SIZE_MINIMIZED
) {
2089 cfg
.win_name_always ? window_name
: icon_name
);
2092 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2093 SetWindowText(hwnd
, window_name
);
2095 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2096 /* A resize, well it better be a minimize. */
2100 int width
, height
, w
, h
;
2102 width
= LOWORD(lParam
);
2103 height
= HIWORD(lParam
);
2106 if (wParam
== SIZE_MAXIMIZED
) {
2110 if (cfg
.resize_action
!= RESIZE_FONT
) {
2111 w
= width
/ font_width
;
2113 h
= height
/ font_height
;
2116 term_size(h
, w
, cfg
.savelines
);
2119 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2121 if (cfg
.resize_action
!= RESIZE_FONT
)
2122 term_size(prev_rows
, prev_cols
, cfg
.savelines
);
2125 /* This is an unexpected resize, these will normally happen
2126 * if the window is too large. Probably either the user
2127 * selected a huge font or the screen size has changed.
2129 * This is also called with minimize.
2131 else reset_window(-1);
2135 * Don't call back->size in mid-resize. (To prevent
2136 * massive numbers of resize events getting sent
2137 * down the connection during an NT opaque drag.)
2140 if (cfg
.resize_action
== RESIZE_TERM
&& !alt_pressed
) {
2141 need_backend_resize
= TRUE
;
2142 w
= (width
-cfg
.window_border
*2) / font_width
;
2144 h
= (height
-cfg
.window_border
*2) / font_height
;
2155 switch (LOWORD(wParam
)) {
2169 term_scroll(0, +rows
/ 2);
2172 term_scroll(0, -rows
/ 2);
2174 case SB_THUMBPOSITION
:
2176 term_scroll(1, HIWORD(wParam
));
2180 case WM_PALETTECHANGED
:
2181 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2182 HDC hdc
= get_ctx();
2184 if (RealizePalette(hdc
) > 0)
2190 case WM_QUERYNEWPALETTE
:
2192 HDC hdc
= get_ctx();
2194 if (RealizePalette(hdc
) > 0)
2206 * Add the scan code and keypress timing to the random
2209 noise_ultralight(lParam
);
2212 * We don't do TranslateMessage since it disassociates the
2213 * resulting CHAR message from the KEYDOWN that sparked it,
2214 * which we occasionally don't want. Instead, we process
2215 * KEYDOWN, and call the Win32 translator functions so that
2216 * we get the translations under _our_ control.
2219 unsigned char buf
[20];
2222 if (wParam
== VK_PROCESSKEY
) {
2225 m
.message
= WM_KEYDOWN
;
2227 m
.lParam
= lParam
& 0xdfff;
2228 TranslateMessage(&m
);
2230 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2232 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2236 * Interrupt an ongoing paste. I'm not sure
2237 * this is sensible, but for the moment it's
2238 * preferable to having to faff about buffering
2244 * We need not bother about stdin backlogs
2245 * here, because in GUI PuTTY we can't do
2246 * anything about it anyway; there's no means
2247 * of asking Windows to hold off on KEYDOWN
2248 * messages. We _have_ to buffer everything
2251 ldisc_send(buf
, len
, 1);
2256 net_pending_errors();
2258 case WM_INPUTLANGCHANGE
:
2260 /* wParam == Font number */
2261 /* lParam == Locale */
2263 HKL NewInputLocale
= (HKL
) lParam
;
2265 // lParam == GetKeyboardLayout(0);
2267 GetLocaleInfo(LOWORD(NewInputLocale
),
2268 LOCALE_IDEFAULTANSICODEPAGE
, lbuf
, sizeof(lbuf
));
2270 kbd_codepage
= atoi(lbuf
);
2274 if(wParam
== IMN_SETOPENSTATUS
) {
2275 HIMC hImc
= ImmGetContext(hwnd
);
2276 ImmSetCompositionFont(hImc
, &lfont
);
2277 ImmReleaseContext(hwnd
, hImc
);
2281 case WM_IME_COMPOSITION
:
2287 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2288 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2290 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2291 break; /* fall back to DefWindowProc */
2293 hIMC
= ImmGetContext(hwnd
);
2294 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2297 buff
= (char*) smalloc(n
);
2298 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2299 luni_send((unsigned short *)buff
, n
/ 2, 1);
2302 ImmReleaseContext(hwnd
, hIMC
);
2307 if (wParam
& 0xFF00) {
2308 unsigned char buf
[2];
2311 buf
[0] = wParam
>> 8;
2312 lpage_send(kbd_codepage
, buf
, 2, 1);
2314 char c
= (unsigned char) wParam
;
2315 lpage_send(kbd_codepage
, &c
, 1, 1);
2321 * Nevertheless, we are prepared to deal with WM_CHAR
2322 * messages, should they crop up. So if someone wants to
2323 * post the things to us as part of a macro manoeuvre,
2324 * we're ready to cope.
2327 char c
= (unsigned char)wParam
;
2328 lpage_send(CP_ACP
, &c
, 1, 1);
2332 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2333 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2338 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2342 * Move the system caret. (We maintain one, even though it's
2343 * invisible, for the benefit of blind people: apparently some
2344 * helper software tracks the system caret, so we should arrange to
2347 void sys_cursor(int x
, int y
)
2352 if (!has_focus
) return;
2354 SetCaretPos(x
* font_width
+ offset_width
,
2355 y
* font_height
+ offset_height
);
2357 /* IMM calls on Win98 and beyond only */
2358 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2360 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2361 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2363 /* we should have the IMM functions */
2364 hIMC
= ImmGetContext(hwnd
);
2365 cf
.dwStyle
= CFS_POINT
;
2366 cf
.ptCurrentPos
.x
= x
* font_width
+ offset_width
;
2367 cf
.ptCurrentPos
.y
= y
* font_height
+ offset_height
;
2368 ImmSetCompositionWindow(hIMC
, &cf
);
2370 ImmReleaseContext(hwnd
, hIMC
);
2374 * Draw a line of text in the window, at given character
2375 * coordinates, in given attributes.
2377 * We are allowed to fiddle with the contents of `text'.
2379 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2380 unsigned long attr
, int lattr
)
2383 int nfg
, nbg
, nfont
;
2386 int force_manual_underline
= 0;
2387 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2388 int char_width
= fnt_width
;
2389 int text_adjust
= 0;
2390 static int *IpDx
= 0, IpDxLEN
= 0;
2392 if (attr
& ATTR_WIDE
)
2395 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2397 if (len
> IpDxLEN
) {
2399 IpDx
= smalloc((len
+ 16) * sizeof(int));
2400 IpDxLEN
= (len
+ 16);
2402 for (i
= 0; i
< IpDxLEN
; i
++)
2403 IpDx
[i
] = char_width
;
2406 /* Only want the left half of double width lines */
2407 if (lattr
!= LATTR_NORM
&& x
*2 >= cols
)
2415 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
2416 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2417 attr
^= ATTR_CUR_XOR
;
2421 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2422 /* Assume a poorman font is borken in other ways too. */
2432 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2435 if (attr
& ATTR_NARROW
)
2436 nfont
|= FONT_NARROW
;
2438 /* Special hack for the VT100 linedraw glyphs. */
2439 if ((attr
& CSET_MASK
) == 0x2300) {
2440 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2441 switch ((unsigned char) (text
[0])) {
2443 text_adjust
= -2 * font_height
/ 5;
2446 text_adjust
= -1 * font_height
/ 5;
2449 text_adjust
= font_height
/ 5;
2452 text_adjust
= 2 * font_height
/ 5;
2455 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2458 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2459 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2460 if (attr
& ATTR_UNDER
) {
2461 attr
&= ~ATTR_UNDER
;
2462 force_manual_underline
= 1;
2467 /* Anything left as an original character set is unprintable. */
2468 if (DIRECT_CHAR(attr
)) {
2471 memset(text
, 0xFD, len
);
2475 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2478 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2479 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2480 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2482 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2483 nfont
|= FONT_UNDERLINE
;
2484 another_font(nfont
);
2485 if (!fonts
[nfont
]) {
2486 if (nfont
& FONT_UNDERLINE
)
2487 force_manual_underline
= 1;
2488 /* Don't do the same for manual bold, it could be bad news. */
2490 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2492 another_font(nfont
);
2494 nfont
= FONT_NORMAL
;
2495 if (attr
& ATTR_REVERSE
) {
2500 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2502 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2506 SelectObject(hdc
, fonts
[nfont
]);
2507 SetTextColor(hdc
, fg
);
2508 SetBkColor(hdc
, bg
);
2509 SetBkMode(hdc
, OPAQUE
);
2512 line_box
.right
= x
+ char_width
* len
;
2513 line_box
.bottom
= y
+ font_height
;
2515 /* Only want the left half of double width lines */
2516 if (line_box
.right
> font_width
*cols
+offset_width
)
2517 line_box
.right
= font_width
*cols
+offset_width
;
2519 /* We're using a private area for direct to font. (512 chars.) */
2520 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2521 /* Ho Hum, dbcs fonts are a PITA! */
2522 /* To display on W9x I have to convert to UCS */
2523 static wchar_t *uni_buf
= 0;
2524 static int uni_len
= 0;
2526 if (len
> uni_len
) {
2528 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2531 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2532 uni_buf
[nlen
] = 0xFFFD;
2533 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2534 IpDx
[nlen
] += char_width
;
2535 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2536 text
+mptr
, 2, uni_buf
+nlen
, 1);
2541 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2542 text
+mptr
, 1, uni_buf
+nlen
, 1);
2550 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2551 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2552 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2553 SetBkMode(hdc
, TRANSPARENT
);
2554 ExtTextOutW(hdc
, x
- 1,
2555 y
- font_height
* (lattr
==
2556 LATTR_BOT
) + text_adjust
,
2557 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2561 } else if (DIRECT_FONT(attr
)) {
2563 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2564 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2565 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2566 SetBkMode(hdc
, TRANSPARENT
);
2568 /* GRR: This draws the character outside it's box and can leave
2569 * 'droppings' even with the clip box! I suppose I could loop it
2570 * one character at a time ... yuk.
2572 * Or ... I could do a test print with "W", and use +1 or -1 for this
2573 * shift depending on if the leftmost column is blank...
2575 ExtTextOut(hdc
, x
- 1,
2576 y
- font_height
* (lattr
==
2577 LATTR_BOT
) + text_adjust
,
2578 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2581 /* And 'normal' unicode characters */
2582 static WCHAR
*wbuf
= NULL
;
2583 static int wlen
= 0;
2588 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2590 for (i
= 0; i
< len
; i
++)
2591 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2594 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2595 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2597 /* And the shadow bold hack. */
2598 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2599 SetBkMode(hdc
, TRANSPARENT
);
2600 ExtTextOutW(hdc
, x
- 1,
2601 y
- font_height
* (lattr
==
2602 LATTR_BOT
) + text_adjust
,
2603 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2606 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2607 (und_mode
== UND_LINE
2608 && (attr
& ATTR_UNDER
)))) {
2611 if (lattr
== LATTR_BOT
)
2612 dec
= dec
* 2 - font_height
;
2614 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2615 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2616 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2617 oldpen
= SelectObject(hdc
, oldpen
);
2618 DeleteObject(oldpen
);
2622 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2623 unsigned long attr
, int lattr
)
2629 int ctype
= cfg
.cursor_type
;
2631 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2632 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2633 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2637 attr
|= TATTR_RIGHTCURS
;
2640 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2641 if (attr
& ATTR_WIDE
)
2648 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2651 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2652 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2653 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2654 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2655 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2656 Polyline(hdc
, pts
, 5);
2657 oldpen
= SelectObject(hdc
, oldpen
);
2658 DeleteObject(oldpen
);
2659 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2660 int startx
, starty
, dx
, dy
, length
, i
;
2663 starty
= y
+ descent
;
2666 length
= char_width
;
2669 if (attr
& TATTR_RIGHTCURS
)
2670 xadjust
= char_width
- 1;
2671 startx
= x
+ xadjust
;
2675 length
= font_height
;
2677 if (attr
& TATTR_ACTCURS
) {
2680 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2681 MoveToEx(hdc
, startx
, starty
, NULL
);
2682 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2683 oldpen
= SelectObject(hdc
, oldpen
);
2684 DeleteObject(oldpen
);
2686 for (i
= 0; i
< length
; i
++) {
2688 SetPixel(hdc
, startx
, starty
, colours
[23]);
2697 /* This function gets the actual width of a character in the normal font.
2699 int CharWidth(Context ctx
, int uc
) {
2703 /* If the font max is the same as the font ave width then this
2704 * function is a no-op.
2706 if (!font_dualwidth
) return 1;
2708 switch (uc
& CSET_MASK
) {
2710 uc
= unitab_line
[uc
& 0xFF];
2713 uc
= unitab_xterm
[uc
& 0xFF];
2716 uc
= unitab_scoacs
[uc
& 0xFF];
2719 if (DIRECT_FONT(uc
)) {
2720 if (dbcs_screenfont
) return 1;
2722 /* Speedup, I know of no font where ascii is the wrong width */
2723 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
2726 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
2727 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2728 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
2729 another_font(FONT_OEM
);
2730 if (!fonts
[FONT_OEM
]) return 0;
2732 SelectObject(hdc
, fonts
[FONT_OEM
]);
2736 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
2737 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
2740 /* Speedup, I know of no font where ascii is the wrong width */
2741 if (uc
>= ' ' && uc
<= '~') return 1;
2743 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2744 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
2745 /* Okay that one worked */ ;
2746 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
2747 /* This should work on 9x too, but it's "less accurate" */ ;
2752 ibuf
+= font_width
/ 2 -1;
2759 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2760 * codes. Returns number of bytes used or zero to drop the message
2761 * or -1 to forward the message to windows.
2763 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2764 unsigned char *output
)
2767 int scan
, left_alt
= 0, key_down
, shift_state
;
2769 unsigned char *p
= output
;
2770 static int alt_sum
= 0;
2772 HKL kbd_layout
= GetKeyboardLayout(0);
2774 static WORD keys
[3];
2775 static int compose_char
= 0;
2776 static WPARAM compose_key
= 0;
2778 r
= GetKeyboardState(keystate
);
2780 memset(keystate
, 0, sizeof(keystate
));
2783 #define SHOW_TOASCII_RESULT
2784 { /* Tell us all about key events */
2785 static BYTE oldstate
[256];
2786 static int first
= 1;
2790 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2793 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2795 } else if ((HIWORD(lParam
) & KF_UP
)
2796 && scan
== (HIWORD(lParam
) & 0xFF)) {
2800 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2801 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2814 debug(("VK_%02x", wParam
));
2816 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2818 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2820 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2821 if (ch
>= ' ' && ch
<= '~')
2822 debug((", '%c'", ch
));
2824 debug((", $%02x", ch
));
2827 debug((", KB0=%02x", keys
[0]));
2829 debug((", KB1=%02x", keys
[1]));
2831 debug((", KB2=%02x", keys
[2]));
2833 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2835 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2837 if ((HIWORD(lParam
) & KF_EXTENDED
))
2839 if ((HIWORD(lParam
) & KF_UP
))
2843 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2844 else if ((HIWORD(lParam
) & KF_UP
))
2845 oldstate
[wParam
& 0xFF] ^= 0x80;
2847 oldstate
[wParam
& 0xFF] ^= 0x81;
2849 for (ch
= 0; ch
< 256; ch
++)
2850 if (oldstate
[ch
] != keystate
[ch
])
2851 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2853 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2857 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2858 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2862 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2863 if ((cfg
.funky_type
== 3 ||
2864 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2865 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2867 wParam
= VK_EXECUTE
;
2869 /* UnToggle NUMLock */
2870 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2871 keystate
[VK_NUMLOCK
] ^= 1;
2874 /* And write back the 'adjusted' state */
2875 SetKeyboardState(keystate
);
2878 /* Disable Auto repeat if required */
2879 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2882 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2885 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2887 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2888 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2889 if (cfg
.ctrlaltkeys
)
2890 keystate
[VK_MENU
] = 0;
2892 keystate
[VK_RMENU
] = 0x80;
2897 alt_pressed
= (left_alt
&& key_down
);
2899 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2900 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2901 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2903 /* Note if AltGr was pressed and if it was used as a compose key */
2904 if (!compose_state
) {
2905 compose_key
= 0x100;
2906 if (cfg
.compose_key
) {
2907 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2908 compose_key
= wParam
;
2910 if (wParam
== VK_APPS
)
2911 compose_key
= wParam
;
2914 if (wParam
== compose_key
) {
2915 if (compose_state
== 0
2916 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2918 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2922 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
2926 * Record that we pressed key so the scroll window can be reset, but
2927 * be careful to avoid Shift-UP/Down
2929 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
&&
2930 wParam
!= VK_MENU
&& wParam
!= VK_CONTROL
) {
2934 if (compose_state
> 1 && left_alt
)
2937 /* Sanitize the number pad if not using a PC NumPad */
2938 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2939 && cfg
.funky_type
!= 2)
2940 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
2941 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
2945 nParam
= VK_NUMPAD0
;
2948 nParam
= VK_NUMPAD1
;
2951 nParam
= VK_NUMPAD2
;
2954 nParam
= VK_NUMPAD3
;
2957 nParam
= VK_NUMPAD4
;
2960 nParam
= VK_NUMPAD5
;
2963 nParam
= VK_NUMPAD6
;
2966 nParam
= VK_NUMPAD7
;
2969 nParam
= VK_NUMPAD8
;
2972 nParam
= VK_NUMPAD9
;
2975 nParam
= VK_DECIMAL
;
2979 if (keystate
[VK_NUMLOCK
] & 1)
2986 /* If a key is pressed and AltGr is not active */
2987 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
2988 /* Okay, prepare for most alts then ... */
2992 /* Lets see if it's a pattern we know all about ... */
2993 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2994 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2997 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2998 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
3001 if (wParam
== VK_INSERT
&& shift_state
== 1) {
3005 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
3008 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
3009 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
3012 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
&&
3013 (cfg
.resize_action
!= RESIZE_DISABLED
)) {
3017 /* Control-Numlock for app-keypad mode switch */
3018 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
3019 app_keypad_keys
^= 1;
3023 /* Nethack keypad */
3024 if (cfg
.nethack_keypad
&& !left_alt
) {
3027 *p
++ = shift_state ?
'B' : 'b';
3030 *p
++ = shift_state ?
'J' : 'j';
3033 *p
++ = shift_state ?
'N' : 'n';
3036 *p
++ = shift_state ?
'H' : 'h';
3039 *p
++ = shift_state ?
'.' : '.';
3042 *p
++ = shift_state ?
'L' : 'l';
3045 *p
++ = shift_state ?
'Y' : 'y';
3048 *p
++ = shift_state ?
'K' : 'k';
3051 *p
++ = shift_state ?
'U' : 'u';
3056 /* Application Keypad */
3060 if (cfg
.funky_type
== 3 ||
3061 (cfg
.funky_type
<= 1 &&
3062 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3076 if (app_keypad_keys
&& !cfg
.no_applic_k
)
3113 if (cfg
.funky_type
== 2) {
3118 } else if (shift_state
)
3125 if (cfg
.funky_type
== 2)
3129 if (cfg
.funky_type
== 2)
3133 if (cfg
.funky_type
== 2)
3138 if (HIWORD(lParam
) & KF_EXTENDED
)
3144 if (xkey
>= 'P' && xkey
<= 'S')
3145 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3147 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3149 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3154 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3155 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3159 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3165 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3169 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3173 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3178 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3183 /* Control-2 to Control-8 are special */
3184 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3185 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3188 if (shift_state
== 2 && wParam
== 0xBD) {
3192 if (shift_state
== 2 && wParam
== 0xDF) {
3196 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
3203 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3204 * for integer decimal nn.)
3206 * We also deal with the weird ones here. Linux VCs replace F1
3207 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3208 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3214 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3217 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3220 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3223 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3226 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3229 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3232 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3235 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3238 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3241 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3274 if ((shift_state
&2) == 0) switch (wParam
) {
3294 /* Reorder edit keys to physical order */
3295 if (cfg
.funky_type
== 3 && code
<= 6)
3296 code
= "\0\2\1\4\5\3\6"[code
];
3298 if (vt52_mode
&& code
> 0 && code
<= 6) {
3299 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3303 if (cfg
.funky_type
== 5 && /* SCO function keys */
3304 code
>= 11 && code
<= 34) {
3305 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3308 case VK_F1
: index
= 0; break;
3309 case VK_F2
: index
= 1; break;
3310 case VK_F3
: index
= 2; break;
3311 case VK_F4
: index
= 3; break;
3312 case VK_F5
: index
= 4; break;
3313 case VK_F6
: index
= 5; break;
3314 case VK_F7
: index
= 6; break;
3315 case VK_F8
: index
= 7; break;
3316 case VK_F9
: index
= 8; break;
3317 case VK_F10
: index
= 9; break;
3318 case VK_F11
: index
= 10; break;
3319 case VK_F12
: index
= 11; break;
3321 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3322 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3323 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3326 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3327 code
>= 1 && code
<= 6) {
3328 char codes
[] = "HL.FIG";
3332 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3336 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3343 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3346 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3349 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3350 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3353 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3355 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3357 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3360 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3361 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3365 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3370 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3371 * some reason seems to send VK_CLEAR to Windows...).
3394 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3396 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
3397 /* VT100 & VT102 manuals both state the app cursor keys
3398 * only work if the app keypad is on.
3400 if (!app_keypad_keys
)
3402 /* Useful mapping of Ctrl-arrows */
3403 if (shift_state
== 2)
3407 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3409 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3416 * Finally, deal with Return ourselves. (Win95 seems to
3417 * foul it up when Alt is pressed, for some reason.)
3419 if (wParam
== VK_RETURN
) { /* Return */
3425 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3426 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3431 /* Okay we've done everything interesting; let windows deal with
3432 * the boring stuff */
3436 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3437 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3439 keystate
[VK_CAPITAL
] = 0;
3442 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3443 #ifdef SHOW_TOASCII_RESULT
3444 if (r
== 1 && !key_down
) {
3446 if (in_utf
|| dbcs_screenfont
)
3447 debug((", (U+%04x)", alt_sum
));
3449 debug((", LCH(%d)", alt_sum
));
3451 debug((", ACH(%d)", keys
[0]));
3456 for (r1
= 0; r1
< r
; r1
++) {
3457 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3466 * Interrupt an ongoing paste. I'm not sure this is
3467 * sensible, but for the moment it's preferable to
3468 * having to faff about buffering things.
3473 for (i
= 0; i
< r
; i
++) {
3474 unsigned char ch
= (unsigned char) keys
[i
];
3476 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3481 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3485 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3486 MessageBeep(MB_ICONHAND
);
3490 luni_send(&keybuf
, 1, 1);
3498 if (in_utf
|| dbcs_screenfont
) {
3500 luni_send(&keybuf
, 1, 1);
3502 ch
= (char) alt_sum
;
3504 * We need not bother about stdin
3505 * backlogs here, because in GUI PuTTY
3506 * we can't do anything about it
3507 * anyway; there's no means of asking
3508 * Windows to hold off on KEYDOWN
3509 * messages. We _have_ to buffer
3510 * everything we're sent.
3512 ldisc_send(&ch
, 1, 1);
3516 lpage_send(kbd_codepage
, &ch
, 1, 1);
3518 if(capsOn
&& ch
< 0x80) {
3521 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3522 luni_send(cbuf
+!left_alt
, 1+!!left_alt
, 1);
3527 lpage_send(kbd_codepage
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3533 /* This is so the ALT-Numpad and dead keys work correctly. */
3538 /* If we're definitly not building up an ALT-54321 then clear it */
3541 /* If we will be using alt_sum fix the 256s */
3542 else if (keys
[0] && (in_utf
|| dbcs_screenfont
))
3547 * ALT alone may or may not want to bring up the System menu.
3548 * If it's not meant to, we return 0 on presses or releases of
3549 * ALT, to show that we've swallowed the keystroke. Otherwise
3550 * we return -1, which means Windows will give the keystroke
3551 * its default handling (i.e. bring up the System menu).
3553 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3559 void set_title(char *title
)
3562 window_name
= smalloc(1 + strlen(title
));
3563 strcpy(window_name
, title
);
3564 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3565 SetWindowText(hwnd
, title
);
3568 void set_icon(char *title
)
3571 icon_name
= smalloc(1 + strlen(title
));
3572 strcpy(icon_name
, title
);
3573 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3574 SetWindowText(hwnd
, title
);
3577 void set_sbar(int total
, int start
, int page
)
3584 si
.cbSize
= sizeof(si
);
3585 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3587 si
.nMax
= total
- 1;
3591 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3594 Context
get_ctx(void)
3600 SelectPalette(hdc
, pal
, FALSE
);
3606 void free_ctx(Context ctx
)
3608 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3609 ReleaseDC(hwnd
, ctx
);
3612 static void real_palette_set(int n
, int r
, int g
, int b
)
3615 logpal
->palPalEntry
[n
].peRed
= r
;
3616 logpal
->palPalEntry
[n
].peGreen
= g
;
3617 logpal
->palPalEntry
[n
].peBlue
= b
;
3618 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3619 colours
[n
] = PALETTERGB(r
, g
, b
);
3620 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3622 colours
[n
] = RGB(r
, g
, b
);
3625 void palette_set(int n
, int r
, int g
, int b
)
3627 static const int first
[21] = {
3628 0, 2, 4, 6, 8, 10, 12, 14,
3629 1, 3, 5, 7, 9, 11, 13, 15,
3632 real_palette_set(first
[n
], r
, g
, b
);
3634 real_palette_set(first
[n
] + 1, r
, g
, b
);
3636 HDC hdc
= get_ctx();
3637 UnrealizeObject(pal
);
3638 RealizePalette(hdc
);
3643 void palette_reset(void)
3647 for (i
= 0; i
< NCOLOURS
; i
++) {
3649 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3650 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3651 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3652 logpal
->palPalEntry
[i
].peFlags
= 0;
3653 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3654 defpal
[i
].rgbtGreen
,
3655 defpal
[i
].rgbtBlue
);
3657 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3658 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3663 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3665 RealizePalette(hdc
);
3670 void write_aclip(char *data
, int len
, int must_deselect
)
3675 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3678 lock
= GlobalLock(clipdata
);
3681 memcpy(lock
, data
, len
);
3682 ((unsigned char *) lock
)[len
] = 0;
3683 GlobalUnlock(clipdata
);
3686 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3688 if (OpenClipboard(hwnd
)) {
3690 SetClipboardData(CF_TEXT
, clipdata
);
3693 GlobalFree(clipdata
);
3696 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3700 * Note: unlike write_aclip() this will not append a nul.
3702 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3704 HGLOBAL clipdata
, clipdata2
, clipdata3
;
3706 void *lock
, *lock2
, *lock3
;
3708 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3710 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3711 len
* sizeof(wchar_t));
3712 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3714 if (!clipdata
|| !clipdata2
|| !clipdata3
) {
3716 GlobalFree(clipdata
);
3718 GlobalFree(clipdata2
);
3720 GlobalFree(clipdata3
);
3723 if (!(lock
= GlobalLock(clipdata
)))
3725 if (!(lock2
= GlobalLock(clipdata2
)))
3728 memcpy(lock
, data
, len
* sizeof(wchar_t));
3729 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3731 if (cfg
.rtf_paste
) {
3732 wchar_t unitab
[256];
3734 unsigned char *tdata
= (unsigned char *)lock2
;
3735 wchar_t *udata
= (wchar_t *)lock
;
3736 int rtflen
= 0, uindex
= 0, tindex
= 0;
3738 int multilen
, blen
, alen
, totallen
, i
;
3739 char before
[16], after
[4];
3741 get_unitab(CP_ACP
, unitab
, 0);
3743 rtfsize
= 100 + strlen(cfg
.font
);
3744 rtf
= smalloc(rtfsize
);
3745 sprintf(rtf
, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3746 GetACP(), cfg
.font
);
3747 rtflen
= strlen(rtf
);
3750 * We want to construct a piece of RTF that specifies the
3751 * same Unicode text. To do this we will read back in
3752 * parallel from the Unicode data in `udata' and the
3753 * non-Unicode data in `tdata'. For each character in
3754 * `tdata' which becomes the right thing in `udata' when
3755 * looked up in `unitab', we just copy straight over from
3756 * tdata. For each one that doesn't, we must WCToMB it
3757 * individually and produce a \u escape sequence.
3759 * It would probably be more robust to just bite the bullet
3760 * and WCToMB each individual Unicode character one by one,
3761 * then MBToWC each one back to see if it was an accurate
3762 * translation; but that strikes me as a horrifying number
3763 * of Windows API calls so I want to see if this faster way
3764 * will work. If it screws up badly we can always revert to
3765 * the simple and slow way.
3767 while (tindex
< len2
&& uindex
< len
&&
3768 tdata
[tindex
] && udata
[uindex
]) {
3769 if (tindex
+ 1 < len2
&&
3770 tdata
[tindex
] == '\r' &&
3771 tdata
[tindex
+1] == '\n') {
3775 if (unitab
[tdata
[tindex
]] == udata
[uindex
]) {
3781 multilen
= WideCharToMultiByte(CP_ACP
, 0, unitab
+uindex
, 1,
3782 NULL
, 0, NULL
, NULL
);
3783 if (multilen
!= 1) {
3784 blen
= sprintf(before
, "{\\uc%d\\u%d", multilen
,
3786 alen
= 1; strcpy(after
, "}");
3788 blen
= sprintf(before
, "\\u%d", udata
[uindex
]);
3789 alen
= 0; after
[0] = '\0';
3792 assert(tindex
+ multilen
<= len2
);
3793 totallen
= blen
+ alen
;
3794 for (i
= 0; i
< multilen
; i
++) {
3795 if (tdata
[tindex
+i
] == '\\' ||
3796 tdata
[tindex
+i
] == '{' ||
3797 tdata
[tindex
+i
] == '}')
3799 else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A)
3800 totallen
+= 6; /* \par\r\n */
3801 else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20)
3807 if (rtfsize
< rtflen
+ totallen
+ 3) {
3808 rtfsize
= rtflen
+ totallen
+ 512;
3809 rtf
= srealloc(rtf
, rtfsize
);
3812 strcpy(rtf
+ rtflen
, before
); rtflen
+= blen
;
3813 for (i
= 0; i
< multilen
; i
++) {
3814 if (tdata
[tindex
+i
] == '\\' ||
3815 tdata
[tindex
+i
] == '{' ||
3816 tdata
[tindex
+i
] == '}') {
3817 rtf
[rtflen
++] = '\\';
3818 rtf
[rtflen
++] = tdata
[tindex
+i
];
3819 } else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A) {
3820 rtflen
+= sprintf(rtf
+rtflen
, "\\par\r\n");
3821 } else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20) {
3822 rtflen
+= sprintf(rtf
+rtflen
, "\\'%02x", tdata
[tindex
+i
]);
3824 rtf
[rtflen
++] = tdata
[tindex
+i
];
3827 strcpy(rtf
+ rtflen
, after
); rtflen
+= alen
;
3833 strcpy(rtf
+ rtflen
, "}");
3836 clipdata3
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, rtflen
);
3837 if (clipdata3
&& (lock3
= GlobalLock(clipdata3
)) != NULL
) {
3839 GlobalUnlock(clipdata3
);
3845 GlobalUnlock(clipdata
);
3846 GlobalUnlock(clipdata2
);
3849 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3851 if (OpenClipboard(hwnd
)) {
3853 SetClipboardData(CF_UNICODETEXT
, clipdata
);
3854 SetClipboardData(CF_TEXT
, clipdata2
);
3856 SetClipboardData(RegisterClipboardFormat(CF_RTF
), clipdata3
);
3859 GlobalFree(clipdata
);
3860 GlobalFree(clipdata2
);
3864 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3867 void get_clip(wchar_t ** p
, int *len
)
3869 static HGLOBAL clipdata
= NULL
;
3870 static wchar_t *converted
= 0;
3879 GlobalUnlock(clipdata
);
3882 } else if (OpenClipboard(NULL
)) {
3883 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
3885 *p
= GlobalLock(clipdata
);
3887 for (p2
= *p
; *p2
; p2
++);
3891 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
3895 s
= GlobalLock(clipdata
);
3896 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
3897 *p
= converted
= smalloc(i
* sizeof(wchar_t));
3898 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
3911 * Move `lines' lines from position `from' to position `to' in the
3914 void optimised_move(int to
, int from
, int lines
)
3919 min
= (to
< from ? to
: from
);
3920 max
= to
+ from
- min
;
3922 r
.left
= offset_width
;
3923 r
.right
= offset_width
+ cols
* font_width
;
3924 r
.top
= offset_height
+ min
* font_height
;
3925 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
3926 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
3931 * Print a message box and perform a fatal exit.
3933 void fatalbox(char *fmt
, ...)
3939 vsprintf(stuff
, fmt
, ap
);
3941 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
3946 * Manage window caption / taskbar flashing, if enabled.
3947 * 0 = stop, 1 = maintain, 2 = start
3949 static void flash_window(int mode
)
3951 static long last_flash
= 0;
3952 static int flashing
= 0;
3953 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
3956 FlashWindow(hwnd
, FALSE
);
3960 } else if (mode
== 2) {
3963 last_flash
= GetTickCount();
3965 FlashWindow(hwnd
, TRUE
);
3968 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
3971 long now
= GetTickCount();
3972 long fdiff
= now
- last_flash
;
3973 if (fdiff
< 0 || fdiff
> 450) {
3975 FlashWindow(hwnd
, TRUE
); /* toggle */
3986 if (mode
== BELL_DEFAULT
) {
3988 * For MessageBeep style bells, we want to be careful of
3989 * timing, because they don't have the nice property of
3990 * PlaySound bells that each one cancels the previous
3991 * active one. So we limit the rate to one per 50ms or so.
3993 static long lastbeep
= 0;
3996 beepdiff
= GetTickCount() - lastbeep
;
3997 if (beepdiff
>= 0 && beepdiff
< 50)
4001 * The above MessageBeep call takes time, so we record the
4002 * time _after_ it finishes rather than before it starts.
4004 lastbeep
= GetTickCount();
4005 } else if (mode
== BELL_WAVEFILE
) {
4006 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
4007 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
4008 sprintf(buf
, "Unable to play sound file\n%s\n"
4009 "Using default sound instead", cfg
.bell_wavefile
);
4010 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
4011 MB_OK
| MB_ICONEXCLAMATION
);
4012 cfg
.beep
= BELL_DEFAULT
;
4015 /* Otherwise, either visual bell or disabled; do nothing here */
4017 flash_window(2); /* start */
4022 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
4024 * Revised by <wez@thebrainroom.com>
4026 static void flip_full_screen(void)
4031 wp
.length
= sizeof(wp
);
4032 GetWindowPlacement(hwnd
, &wp
);
4034 full_screen
= !full_screen
;
4037 if (wp
.showCmd
== SW_SHOWMAXIMIZED
) {
4038 /* Ooops it was already 'zoomed' we have to unzoom it before
4039 * everything will work right.
4041 wp
.showCmd
= SW_SHOWNORMAL
;
4042 SetWindowPlacement(hwnd
, &wp
);
4045 style
= GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_CAPTION
;
4046 style
&= ~WS_VSCROLL
;
4047 if (cfg
.scrollbar_in_fullscreen
)
4048 style
|= WS_VSCROLL
;
4049 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4051 /* This seems to be needed otherwize explorer doesn't notice
4052 * we want to go fullscreen and it's bar is still visible
4054 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
4055 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
4056 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
4059 wp
.showCmd
= SW_SHOWMAXIMIZED
;
4060 SetWindowPlacement(hwnd
, &wp
);
4062 style
= GetWindowLong(hwnd
, GWL_STYLE
) | WS_CAPTION
;
4063 style
&= ~WS_VSCROLL
;
4065 style
|= WS_VSCROLL
;
4066 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4068 /* Don't need to do a SetWindowPos as the resize will force a
4071 wp
.showCmd
= SW_SHOWNORMAL
;
4072 SetWindowPlacement(hwnd
, &wp
);
4075 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4076 MF_BYCOMMAND
| full_screen ? MF_CHECKED
: MF_UNCHECKED
);
4080 * Minimise or restore the window in response to a server-side
4083 void set_iconic(int iconic
)
4085 if (IsIconic(hwnd
)) {
4087 ShowWindow(hwnd
, SW_RESTORE
);
4090 ShowWindow(hwnd
, SW_MINIMIZE
);
4095 * Move the window in response to a server-side request.
4097 void move_window(int x
, int y
)
4099 SetWindowPos(hwnd
, NULL
, x
, y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
);
4103 * Move the window to the top or bottom of the z-order in response
4104 * to a server-side request.
4106 void set_zorder(int top
)
4108 if (cfg
.alwaysontop
|| full_screen
)
4109 return; /* ignore */
4110 SetWindowPos(hwnd
, top ? HWND_TOP
: HWND_BOTTOM
, 0, 0, 0, 0,
4111 SWP_NOMOVE
| SWP_NOSIZE
);
4115 * Refresh the window in response to a server-side request.
4117 void refresh_window(void)
4119 InvalidateRect(hwnd
, NULL
, TRUE
);
4123 * Maximise or restore the window in response to a server-side
4126 void set_zoomed(int zoomed
)
4128 if (IsZoomed(hwnd
) || full_screen
) {
4133 ShowWindow(hwnd
, SW_RESTORE
);
4137 ShowWindow(hwnd
, SW_MAXIMIZE
);
4142 * Report whether the window is iconic, for terminal reports.
4146 return IsIconic(hwnd
);
4150 * Report the window's position, for terminal reports.
4152 void get_window_pos(int *x
, int *y
)
4155 GetWindowRect(hwnd
, &r
);
4161 * Report the window's pixel size, for terminal reports.
4163 void get_window_pixels(int *x
, int *y
)
4166 GetWindowRect(hwnd
, &r
);
4167 *x
= r
.right
- r
.left
;
4168 *y
= r
.bottom
- r
.top
;
4172 * Return the window or icon title.
4174 char *get_window_title(int icon
)
4176 return icon ? icon_name
: window_name
;