17 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
23 #define IDM_SHOWLOG 0x0010
24 #define IDM_NEWSESS 0x0020
25 #define IDM_DUPSESS 0x0030
26 #define IDM_RECONF 0x0040
27 #define IDM_CLRSB 0x0050
28 #define IDM_RESET 0x0060
29 #define IDM_TEL_AYT 0x0070
30 #define IDM_TEL_BRK 0x0080
31 #define IDM_TEL_SYNCH 0x0090
32 #define IDM_TEL_EC 0x00a0
33 #define IDM_TEL_EL 0x00b0
34 #define IDM_TEL_GA 0x00c0
35 #define IDM_TEL_NOP 0x00d0
36 #define IDM_TEL_ABORT 0x00e0
37 #define IDM_TEL_AO 0x00f0
38 #define IDM_TEL_IP 0x0100
39 #define IDM_TEL_SUSP 0x0110
40 #define IDM_TEL_EOR 0x0120
41 #define IDM_TEL_EOF 0x0130
42 #define IDM_ABOUT 0x0140
43 #define IDM_SAVEDSESS 0x0150
44 #define IDM_COPYALL 0x0160
46 #define IDM_SESSLGP 0x0250 /* log type printable */
47 #define IDM_SESSLGA 0x0260 /* log type all chars */
48 #define IDM_SESSLGE 0x0270 /* log end */
49 #define IDM_SAVED_MIN 0x1000
50 #define IDM_SAVED_MAX 0x2000
52 #define WM_IGNORE_CLIP (WM_XUSER + 2)
54 /* Needed for Chinese support and apparently not always defined. */
56 #define VK_PROCESSKEY 0xE5
59 /* Needed for mouse wheel support and not defined in earlier SDKs. */
61 #define WM_MOUSEWHEEL 0x020A
64 static LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
65 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
66 unsigned char *output
);
67 static void cfgtopalette(void);
68 static void init_palette(void);
69 static void init_fonts(int, int);
70 static void another_font(int);
71 static void deinit_fonts(void);
73 /* Window layout information */
74 static void reset_window(int);
75 static int full_screen
= 0, extra_width
, extra_height
;
76 static int font_width
, font_height
, font_dualwidth
;
77 static int offset_width
, offset_height
;
78 static int was_zoomed
= 0;
79 static int prev_rows
, prev_cols
;
81 static LONG old_wind_style
;
82 static WINDOWPLACEMENT old_wind_placement
;
84 static int pending_netevent
= 0;
85 static WPARAM pend_netevent_wParam
= 0;
86 static LPARAM pend_netevent_lParam
= 0;
87 static void enact_pending_netevent(void);
88 static void flash_window(int mode
);
89 static void flip_full_screen(void);
91 static time_t last_movement
= 0;
95 #define FONT_UNDERLINE 2
96 #define FONT_BOLDUND 3
97 #define FONT_WIDE 0x04
98 #define FONT_HIGH 0x08
99 #define FONT_NARROW 0x10
101 #define FONT_OEM 0x20
102 #define FONT_OEMBOLD 0x21
103 #define FONT_OEMUND 0x22
104 #define FONT_OEMBOLDUND 0x23
106 #define FONT_MAXNO 0x2F
108 static HFONT fonts
[FONT_MAXNO
];
109 static int fontflag
[FONT_MAXNO
];
111 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
119 static COLORREF colours
[NCOLOURS
];
121 static LPLOGPALETTE logpal
;
122 static RGBTRIPLE defpal
[NCOLOURS
];
126 static HBITMAP caretbm
;
128 static int dbltime
, lasttime
, lastact
;
129 static Mouse_Button lastbtn
;
131 /* this allows xterm-style mouse handling. */
132 static int send_raw_mouse
= 0;
133 static int wheel_accumulator
= 0;
135 static char *window_name
, *icon_name
;
137 static int compose_state
= 0;
139 static OSVERSIONINFO osVersion
;
141 /* Dummy routine, only required in plink. */
142 void ldisc_update(int echo
, int edit
)
146 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
148 static char appname
[] = "PuTTY";
153 int guess_width
, guess_height
;
156 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
158 winsock_ver
= MAKEWORD(1, 1);
159 if (WSAStartup(winsock_ver
, &wsadata
)) {
160 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
161 MB_OK
| MB_ICONEXCLAMATION
);
164 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
165 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
166 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
170 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
173 InitCommonControls();
175 /* Ensure a Maximize setting in Explorer doesn't maximise the
180 ZeroMemory(&osVersion
, sizeof(osVersion
));
181 osVersion
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
182 if (!GetVersionEx ( (OSVERSIONINFO
*) &osVersion
)) {
183 MessageBox(NULL
, "Windows refuses to report a version",
184 "PuTTY Fatal Error", MB_OK
| MB_ICONEXCLAMATION
);
190 * Process the command line.
195 default_protocol
= DEFAULT_PROTOCOL
;
196 default_port
= DEFAULT_PORT
;
197 cfg
.logtype
= LGTYP_NONE
;
199 do_defaults(NULL
, &cfg
);
202 while (*p
&& isspace(*p
))
206 * Process command line options first. Yes, this can be
207 * done better, and it will be as soon as I have the
211 char *q
= p
+ strcspn(p
, " \t");
214 tolower(p
[0]) == 's' &&
215 tolower(p
[1]) == 's' && tolower(p
[2]) == 'h') {
216 default_protocol
= cfg
.protocol
= PROT_SSH
;
217 default_port
= cfg
.port
= 22;
218 } else if (q
== p
+ 7 &&
219 tolower(p
[0]) == 'c' &&
220 tolower(p
[1]) == 'l' &&
221 tolower(p
[2]) == 'e' &&
222 tolower(p
[3]) == 'a' &&
223 tolower(p
[4]) == 'n' &&
224 tolower(p
[5]) == 'u' && tolower(p
[6]) == 'p') {
226 * `putty -cleanup'. Remove all registry entries
227 * associated with PuTTY, and also find and delete
228 * the random seed file.
231 "This procedure will remove ALL Registry\n"
232 "entries associated with PuTTY, and will\n"
233 "also remove the PuTTY random seed file.\n"
235 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
236 "SESSIONS. Are you really sure you want\n"
239 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
244 p
= q
+ strspn(q
, " \t");
248 * An initial @ means to activate a saved session.
252 while (i
> 1 && isspace(p
[i
- 1]))
255 do_defaults(p
+ 1, &cfg
);
256 if (!*cfg
.host
&& !do_config()) {
260 } else if (*p
== '&') {
262 * An initial & means we've been given a command line
263 * containing the hex value of a HANDLE for a file
264 * mapping object, which we must then extract as a
269 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
270 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
271 0, 0, sizeof(Config
))) != NULL
) {
274 CloseHandle(filemap
);
275 } else if (!do_config()) {
282 * If the hostname starts with "telnet:", set the
283 * protocol to Telnet and process the string as a
286 if (!strncmp(q
, "telnet:", 7)) {
290 if (q
[0] == '/' && q
[1] == '/')
292 cfg
.protocol
= PROT_TELNET
;
294 while (*p
&& *p
!= ':' && *p
!= '/')
303 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
304 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
306 while (*p
&& !isspace(*p
))
310 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
311 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
312 while (*p
&& isspace(*p
))
327 * Trim leading whitespace off the hostname if it's there.
330 int space
= strspn(cfg
.host
, " \t");
331 memmove(cfg
.host
, cfg
.host
+space
, 1+strlen(cfg
.host
)-space
);
334 /* See if host is of the form user@host */
335 if (cfg
.host
[0] != '\0') {
336 char *atsign
= strchr(cfg
.host
, '@');
337 /* Make sure we're not overflowing the user field */
339 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
340 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
341 cfg
.username
[atsign
- cfg
.host
] = '\0';
343 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
348 * Trim a colon suffix off the hostname if it's there.
350 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
354 * Select protocol. This is farmed out into a table in a
355 * separate file to enable an ssh-free variant.
360 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
361 if (backends
[i
].protocol
== cfg
.protocol
) {
362 back
= backends
[i
].backend
;
366 MessageBox(NULL
, "Unsupported protocol number found",
367 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
373 /* Check for invalid Port number (i.e. zero) */
375 MessageBox(NULL
, "Invalid Port Number",
376 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
383 wndclass
.lpfnWndProc
= WndProc
;
384 wndclass
.cbClsExtra
= 0;
385 wndclass
.cbWndExtra
= 0;
386 wndclass
.hInstance
= inst
;
387 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
388 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
389 wndclass
.hbrBackground
= NULL
;
390 wndclass
.lpszMenuName
= NULL
;
391 wndclass
.lpszClassName
= appname
;
393 RegisterClass(&wndclass
);
398 savelines
= cfg
.savelines
;
404 * Guess some defaults for the window size. This all gets
405 * updated later, so we don't really care too much. However, we
406 * do want the font width/height guesses to correspond to a
407 * large font rather than a small one...
414 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
415 guess_width
= extra_width
+ font_width
* cols
;
416 guess_height
= extra_height
+ font_height
* rows
;
419 HWND w
= GetDesktopWindow();
420 GetWindowRect(w
, &r
);
421 if (guess_width
> r
.right
- r
.left
)
422 guess_width
= r
.right
- r
.left
;
423 if (guess_height
> r
.bottom
- r
.top
)
424 guess_height
= r
.bottom
- r
.top
;
428 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
431 winmode
&= ~(WS_VSCROLL
);
432 if (cfg
.resize_action
== RESIZE_DISABLED
)
433 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
435 exwinmode
|= WS_EX_TOPMOST
;
437 exwinmode
|= WS_EX_CLIENTEDGE
;
438 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
439 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
440 guess_width
, guess_height
,
441 NULL
, NULL
, inst
, NULL
);
445 * Initialise the fonts, simultaneously correcting the guesses
446 * for font_{width,height}.
451 * Correct the guesses for extra_{width,height}.
455 GetWindowRect(hwnd
, &wr
);
456 GetClientRect(hwnd
, &cr
);
457 offset_width
= offset_height
= cfg
.window_border
;
458 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
459 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
463 * Resize the window, now we know what size we _really_ want it
466 guess_width
= extra_width
+ font_width
* cols
;
467 guess_height
= extra_height
+ font_height
* rows
;
468 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
469 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
472 * Set up a caret bitmap, with no content.
476 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
477 bits
= smalloc(size
);
478 memset(bits
, 0, size
);
479 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
482 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
485 * Initialise the scroll bar.
490 si
.cbSize
= sizeof(si
);
491 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
496 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
500 * Start up the telnet connection.
504 char msg
[1024], *title
;
507 error
= back
->init(cfg
.host
, cfg
.port
, &realhost
);
509 sprintf(msg
, "Unable to open connection to\n"
510 "%.800s\n" "%s", cfg
.host
, error
);
511 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
514 window_name
= icon_name
= NULL
;
516 title
= cfg
.wintitle
;
518 sprintf(msg
, "%s - PuTTY", realhost
);
526 session_closed
= FALSE
;
529 * Prepare the mouse handler.
531 lastact
= MA_NOTHING
;
532 lastbtn
= MBT_NOTHING
;
533 dbltime
= GetDoubleClickTime();
536 * Set up the session-control options on the system menu.
539 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
543 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
544 if (cfg
.protocol
== PROT_TELNET
) {
546 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
547 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
548 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
549 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
550 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
551 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
552 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
553 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
554 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
555 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
556 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
557 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
558 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
559 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
560 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
561 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
562 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
564 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
566 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
567 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
568 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
569 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
572 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
573 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
575 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
576 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
577 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
578 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
579 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
580 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
581 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
582 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
586 * Finally show the window!
588 ShowWindow(hwnd
, show
);
589 SetForegroundWindow(hwnd
);
592 * Open the initial log file if there is one.
597 * Set the palette up.
603 has_focus
= (GetForegroundWindow() == hwnd
);
606 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
607 int timer_id
= 0, long_timer
= 0;
609 while (msg
.message
!= WM_QUIT
) {
610 /* Sometimes DispatchMessage calls routines that use their own
611 * GetMessage loop, setup this timer so we get some control back.
613 * Also call term_update() from the timer so that if the host
614 * is sending data flat out we still do redraws.
616 if (timer_id
&& long_timer
) {
617 KillTimer(hwnd
, timer_id
);
618 long_timer
= timer_id
= 0;
621 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
622 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
623 DispatchMessage(&msg
);
625 /* Make sure we blink everything that needs it. */
628 /* Send the paste buffer if there's anything to send */
631 /* If there's nothing new in the queue then we can do everything
632 * we've delayed, reading the socket, writing, and repainting
635 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
638 if (pending_netevent
) {
639 enact_pending_netevent();
641 /* Force the cursor blink on */
644 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
648 /* Okay there is now nothing to do so we make sure the screen is
649 * completely up to date then tell windows to call us in a little
653 KillTimer(hwnd
, timer_id
);
661 flash_window(1); /* maintain */
664 /* Hmm, term_update didn't want to do an update too soon ... */
665 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
667 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
669 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
672 /* There's no point rescanning everything in the message queue
673 * so we do an apparently unnecessary wait here
676 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
690 if (cfg
.protocol
== PROT_SSH
) {
701 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
703 char *do_select(SOCKET skt
, int startup
)
708 events
= (FD_CONNECT
| FD_READ
| FD_WRITE
|
709 FD_OOB
| FD_CLOSE
| FD_ACCEPT
);
714 return "do_select(): internal error (hwnd==NULL)";
715 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
716 switch (WSAGetLastError()) {
718 return "Network is down";
720 return "WSAAsyncSelect(): unknown error";
727 * set or clear the "raw mouse message" mode
729 void set_raw_mouse_mode(int activate
)
731 send_raw_mouse
= activate
;
732 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
736 * Print a message box and close the connection.
738 void connection_fatal(char *fmt
, ...)
744 vsprintf(stuff
, fmt
, ap
);
746 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
747 if (cfg
.close_on_exit
== COE_ALWAYS
)
750 session_closed
= TRUE
;
751 SetWindowText(hwnd
, "PuTTY (inactive)");
756 * Actually do the job requested by a WM_NETEVENT
758 static void enact_pending_netevent(void)
760 static int reentering
= 0;
761 extern int select_result(WPARAM
, LPARAM
);
765 return; /* don't unpend the pending */
767 pending_netevent
= FALSE
;
770 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
773 if (ret
== 0 && !session_closed
) {
774 /* Abnormal exits will already have set session_closed and taken
775 * appropriate action. */
776 if (cfg
.close_on_exit
== COE_ALWAYS
||
777 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
779 session_closed
= TRUE
;
780 SetWindowText(hwnd
, "PuTTY (inactive)");
781 MessageBox(hwnd
, "Connection closed by remote host",
782 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
788 * Copy the colour palette from the configuration data into defpal.
789 * This is non-trivial because the colour indices are different.
791 static void cfgtopalette(void)
794 static const int ww
[] = {
795 6, 7, 8, 9, 10, 11, 12, 13,
796 14, 15, 16, 17, 18, 19, 20, 21,
797 0, 1, 2, 3, 4, 4, 5, 5
800 for (i
= 0; i
< 24; i
++) {
802 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
803 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
804 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
809 * Set up the colour palette.
811 static void init_palette(void)
814 HDC hdc
= GetDC(hwnd
);
816 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
817 logpal
= smalloc(sizeof(*logpal
)
818 - sizeof(logpal
->palPalEntry
)
819 + NCOLOURS
* sizeof(PALETTEENTRY
));
820 logpal
->palVersion
= 0x300;
821 logpal
->palNumEntries
= NCOLOURS
;
822 for (i
= 0; i
< NCOLOURS
; i
++) {
823 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
824 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
825 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
826 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
828 pal
= CreatePalette(logpal
);
830 SelectPalette(hdc
, pal
, FALSE
);
832 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
835 ReleaseDC(hwnd
, hdc
);
838 for (i
= 0; i
< NCOLOURS
; i
++)
839 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
843 for (i
= 0; i
< NCOLOURS
; i
++)
844 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
845 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
849 * Initialise all the fonts we will need initially. There may be as many as
850 * three or as few as one. The other (poentially) twentyone fonts are done
851 * if/when they are needed.
855 * - check the font width and height, correcting our guesses if
858 * - verify that the bold font is the same width as the ordinary
859 * one, and engage shadow bolding if not.
861 * - verify that the underlined font is the same width as the
862 * ordinary one (manual underlining by means of line drawing can
863 * be done in a pinch).
865 static void init_fonts(int pick_width
, int pick_height
)
872 int fw_dontcare
, fw_bold
;
874 for (i
= 0; i
< FONT_MAXNO
; i
++)
877 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
880 if (cfg
.fontisbold
) {
881 fw_dontcare
= FW_BOLD
;
884 fw_dontcare
= FW_DONTCARE
;
891 font_height
= pick_height
;
893 font_height
= cfg
.fontheight
;
894 if (font_height
> 0) {
896 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
899 font_width
= pick_width
;
902 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
903 c, OUT_DEFAULT_PRECIS, \
904 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
905 FIXED_PITCH | FF_DONTCARE, cfg.font)
907 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
909 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
910 GetTextMetrics(hdc
, &tm
);
912 if (pick_width
== 0 || pick_height
== 0) {
913 font_height
= tm
.tmHeight
;
914 font_width
= tm
.tmAveCharWidth
;
916 font_dualwidth
= (tm
.tmAveCharWidth
!= tm
.tmMaxCharWidth
);
918 #ifdef RDB_DEBUG_PATCH
919 debug(23, "Primary font H=%d, AW=%d, MW=%d",
920 tm
.tmHeight
, tm
.tmAveCharWidth
, tm
.tmMaxCharWidth
);
925 DWORD cset
= tm
.tmCharSet
;
926 memset(&info
, 0xFF, sizeof(info
));
928 /* !!! Yes the next line is right */
929 if (cset
== OEM_CHARSET
)
930 font_codepage
= GetOEMCP();
932 if (TranslateCharsetInfo
933 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
938 GetCPInfo(font_codepage
, &cpinfo
);
939 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
942 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
945 * Some fonts, e.g. 9-pt Courier, draw their underlines
946 * outside their character cell. We successfully prevent
947 * screen corruption by clipping the text output, but then
948 * we lose the underline completely. Here we try to work
949 * out whether this is such a font, and if it is, we set a
950 * flag that causes underlines to be drawn by hand.
952 * Having tried other more sophisticated approaches (such
953 * as examining the TEXTMETRIC structure or requesting the
954 * height of a string), I think we'll do this the brute
955 * force way: we create a small bitmap, draw an underlined
956 * space on it, and test to see whether any pixels are
957 * foreground-coloured. (Since we expect the underline to
958 * go all the way across the character cell, we only search
959 * down a single column of the bitmap, half way across.)
963 HBITMAP und_bm
, und_oldbm
;
967 und_dc
= CreateCompatibleDC(hdc
);
968 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
969 und_oldbm
= SelectObject(und_dc
, und_bm
);
970 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
971 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
972 SetTextColor(und_dc
, RGB(255, 255, 255));
973 SetBkColor(und_dc
, RGB(0, 0, 0));
974 SetBkMode(und_dc
, OPAQUE
);
975 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
977 for (i
= 0; i
< font_height
; i
++) {
978 c
= GetPixel(und_dc
, font_width
/ 2, i
);
979 if (c
!= RGB(0, 0, 0))
982 SelectObject(und_dc
, und_oldbm
);
983 DeleteObject(und_bm
);
987 DeleteObject(fonts
[FONT_UNDERLINE
]);
988 fonts
[FONT_UNDERLINE
] = 0;
992 if (bold_mode
== BOLD_FONT
) {
993 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
997 descent
= tm
.tmAscent
+ 1;
998 if (descent
>= font_height
)
999 descent
= font_height
- 1;
1001 for (i
= 0; i
< 3; i
++) {
1003 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
1004 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
1011 ReleaseDC(hwnd
, hdc
);
1013 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1014 und_mode
= UND_LINE
;
1015 DeleteObject(fonts
[FONT_UNDERLINE
]);
1016 fonts
[FONT_UNDERLINE
] = 0;
1019 if (bold_mode
== BOLD_FONT
&&
1020 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1021 bold_mode
= BOLD_SHADOW
;
1022 DeleteObject(fonts
[FONT_BOLD
]);
1023 fonts
[FONT_BOLD
] = 0;
1025 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1030 static void another_font(int fontno
)
1033 int fw_dontcare
, fw_bold
;
1037 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1040 basefont
= (fontno
& ~(FONT_BOLDUND
));
1041 if (basefont
!= fontno
&& !fontflag
[basefont
])
1042 another_font(basefont
);
1044 if (cfg
.fontisbold
) {
1045 fw_dontcare
= FW_BOLD
;
1048 fw_dontcare
= FW_DONTCARE
;
1052 c
= cfg
.fontcharset
;
1058 if (fontno
& FONT_WIDE
)
1060 if (fontno
& FONT_NARROW
)
1062 if (fontno
& FONT_OEM
)
1064 if (fontno
& FONT_BOLD
)
1066 if (fontno
& FONT_UNDERLINE
)
1070 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1071 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1072 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1073 FIXED_PITCH
| FF_DONTCARE
, s
);
1075 fontflag
[fontno
] = 1;
1078 static void deinit_fonts(void)
1081 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1083 DeleteObject(fonts
[i
]);
1089 void request_resize(int w
, int h
)
1093 /* If the window is maximized supress resizing attempts */
1094 if (IsZoomed(hwnd
)) {
1095 if (cfg
.resize_action
!= RESIZE_FONT
)
1099 if (cfg
.resize_action
== RESIZE_DISABLED
) return;
1100 if (h
== rows
&& w
== cols
) return;
1102 /* Sanity checks ... */
1104 static int first_time
= 1;
1107 switch (first_time
) {
1109 /* Get the size of the screen */
1110 if (GetClientRect(GetDesktopWindow(), &ss
))
1111 /* first_time = 0 */ ;
1117 /* Make sure the values are sane */
1118 width
= (ss
.right
- ss
.left
- extra_width
) / 4;
1119 height
= (ss
.bottom
- ss
.top
- extra_height
) / 6;
1121 if (w
> width
|| h
> height
)
1130 term_size(h
, w
, cfg
.savelines
);
1132 if (cfg
.resize_action
!= RESIZE_FONT
) {
1133 width
= extra_width
+ font_width
* w
;
1134 height
= extra_height
+ font_height
* h
;
1136 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1137 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1138 SWP_NOMOVE
| SWP_NOZORDER
);
1142 InvalidateRect(hwnd
, NULL
, TRUE
);
1145 static void reset_window(int reinit
) {
1147 * This function decides how to resize or redraw when the
1148 * user changes something.
1150 * This function doesn't like to change the terminal size but if the
1151 * font size is locked that may be it's only soluion.
1153 int win_width
, win_height
;
1156 #ifdef RDB_DEBUG_PATCH
1157 debug((27, "reset_window()"));
1160 /* Current window sizes ... */
1161 GetWindowRect(hwnd
, &wr
);
1162 GetClientRect(hwnd
, &cr
);
1164 win_width
= cr
.right
- cr
.left
;
1165 win_height
= cr
.bottom
- cr
.top
;
1167 /* Are we being forced to reload the fonts ? */
1169 #ifdef RDB_DEBUG_PATCH
1170 debug((27, "reset_window() -- Forced deinit"));
1176 /* Oh, looks like we're minimised */
1177 if (win_width
== 0 || win_height
== 0)
1180 /* Is the window out of position ? */
1182 (offset_width
!= (win_width
-font_width
*cols
)/2 ||
1183 offset_height
!= (win_height
-font_height
*rows
)/2) ){
1184 offset_width
= (win_width
-font_width
*cols
)/2;
1185 offset_height
= (win_height
-font_height
*rows
)/2;
1186 InvalidateRect(hwnd
, NULL
, TRUE
);
1187 #ifdef RDB_DEBUG_PATCH
1188 debug((27, "reset_window() -> Reposition terminal"));
1192 if (IsZoomed(hwnd
)) {
1193 /* We're fullscreen, this means we must not change the size of
1194 * the window so it's the font size or the terminal itself.
1197 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1198 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1200 if (cfg
.resize_action
== RESIZE_FONT
) {
1201 if ( font_width
!= win_width
/cols
||
1202 font_height
!= win_height
/rows
) {
1204 init_fonts(win_width
/cols
, win_height
/rows
);
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((25, "reset_window() -> Z font resize to (%d, %d)",
1210 font_width
, font_height
));
1214 if ( font_width
!= win_width
/cols
||
1215 font_height
!= win_height
/rows
) {
1216 /* Our only choice at this point is to change the
1217 * size of the terminal; Oh well.
1219 term_size( win_height
/font_height
, win_width
/font_width
,
1221 offset_width
= (win_width
-font_width
*cols
)/2;
1222 offset_height
= (win_height
-font_height
*rows
)/2;
1223 InvalidateRect(hwnd
, NULL
, TRUE
);
1224 #ifdef RDB_DEBUG_PATCH
1225 debug((27, "reset_window() -> Zoomed term_size"));
1232 /* Hmm, a force re-init means we should ignore the current window
1233 * so we resize to the default font size.
1236 #ifdef RDB_DEBUG_PATCH
1237 debug((27, "reset_window() -> Forced re-init"));
1240 offset_width
= offset_height
= cfg
.window_border
;
1241 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1242 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1244 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1245 win_height
!= font_height
*rows
+ offset_height
*2) {
1247 /* If this is too large windows will resize it to the maximum
1248 * allowed window size, we will then be back in here and resize
1249 * the font or terminal to fit.
1251 SetWindowPos(hwnd
, NULL
, 0, 0,
1252 font_width
*cols
+ extra_width
,
1253 font_height
*rows
+ extra_height
,
1254 SWP_NOMOVE
| SWP_NOZORDER
);
1259 /* Okay the user doesn't want us to change the font so we try the
1260 * window. But that may be too big for the screen which forces us
1261 * to change the terminal.
1263 if ((cfg
.resize_action
!= RESIZE_FONT
&& reinit
==0) || reinit
>0) {
1264 offset_width
= offset_height
= cfg
.window_border
;
1265 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1266 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1268 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1269 win_height
!= font_height
*rows
+ offset_height
*2) {
1274 GetClientRect(GetDesktopWindow(), &ss
);
1275 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1276 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1279 if ( rows
> height
|| cols
> width
) {
1280 if ( height
> rows
) height
= rows
;
1281 if ( width
> cols
) width
= cols
;
1282 term_size(height
, width
, cfg
.savelines
);
1283 #ifdef RDB_DEBUG_PATCH
1284 debug((27, "reset_window() -> term resize to (%d,%d)",
1289 SetWindowPos(hwnd
, NULL
, 0, 0,
1290 font_width
*cols
+ extra_width
,
1291 font_height
*rows
+ extra_height
,
1292 SWP_NOMOVE
| SWP_NOZORDER
);
1294 InvalidateRect(hwnd
, NULL
, TRUE
);
1295 #ifdef RDB_DEBUG_PATCH
1296 debug((27, "reset_window() -> window resize to (%d,%d)",
1297 font_width
*cols
+ extra_width
,
1298 font_height
*rows
+ extra_height
));
1304 /* We're allowed to or must change the font but do we want to ? */
1306 if (font_width
!= (win_width
-cfg
.window_border
*2)/cols
||
1307 font_height
!= (win_height
-cfg
.window_border
*2)/rows
) {
1310 init_fonts((win_width
-cfg
.window_border
*2)/cols
,
1311 (win_height
-cfg
.window_border
*2)/rows
);
1312 offset_width
= (win_width
-font_width
*cols
)/2;
1313 offset_height
= (win_height
-font_height
*rows
)/2;
1315 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+offset_width
*2;
1316 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1318 InvalidateRect(hwnd
, NULL
, TRUE
);
1319 #ifdef RDB_DEBUG_PATCH
1320 debug((25, "reset_window() -> font resize to (%d,%d)",
1321 font_width
, font_height
));
1326 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
)
1328 int thistime
= GetMessageTime();
1330 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1331 lastbtn
= MBT_NOTHING
;
1332 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
);
1336 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1337 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1338 lastact
== MA_2CLK ? MA_3CLK
:
1339 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1344 if (lastact
!= MA_NOTHING
)
1345 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
);
1346 lasttime
= thistime
;
1350 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1351 * into a cooked one (SELECT, EXTEND, PASTE).
1353 Mouse_Button
translate_button(Mouse_Button button
)
1355 if (button
== MBT_LEFT
)
1357 if (button
== MBT_MIDDLE
)
1358 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1359 if (button
== MBT_RIGHT
)
1360 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1361 return 0; /* shouldn't happen */
1364 static void show_mouseptr(int show
)
1366 static int cursor_visible
= 1;
1367 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1369 if (cursor_visible
&& !show
)
1371 else if (!cursor_visible
&& show
)
1373 cursor_visible
= show
;
1376 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1377 WPARAM wParam
, LPARAM lParam
)
1380 static int ignore_clip
= FALSE
;
1381 static int resizing
= FALSE
;
1382 static int need_backend_resize
= FALSE
;
1386 if (pending_netevent
)
1387 enact_pending_netevent();
1393 if (cfg
.ping_interval
> 0) {
1396 if (now
- last_movement
> cfg
.ping_interval
) {
1397 back
->special(TS_PING
);
1398 last_movement
= now
;
1406 if (!cfg
.warn_on_close
|| session_closed
||
1408 "Are you sure you want to close this session?",
1409 "PuTTY Exit Confirmation",
1410 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1411 DestroyWindow(hwnd
);
1418 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1430 PROCESS_INFORMATION pi
;
1431 HANDLE filemap
= NULL
;
1433 if (wParam
== IDM_DUPSESS
) {
1435 * Allocate a file-mapping memory chunk for the
1438 SECURITY_ATTRIBUTES sa
;
1441 sa
.nLength
= sizeof(sa
);
1442 sa
.lpSecurityDescriptor
= NULL
;
1443 sa
.bInheritHandle
= TRUE
;
1444 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1447 0, sizeof(Config
), NULL
);
1449 p
= (Config
*) MapViewOfFile(filemap
,
1451 0, 0, sizeof(Config
));
1453 *p
= cfg
; /* structure copy */
1457 sprintf(c
, "putty &%p", filemap
);
1459 } else if (wParam
== IDM_SAVEDSESS
) {
1461 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1462 cl
= smalloc(16 + strlen(session
)); /* 8, but play safe */
1464 cl
= NULL
; /* not a very important failure mode */
1466 sprintf(cl
, "putty @%s", session
);
1472 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1474 si
.lpReserved
= NULL
;
1475 si
.lpDesktop
= NULL
;
1479 si
.lpReserved2
= NULL
;
1480 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1481 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1484 CloseHandle(filemap
);
1494 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1497 if (!do_reconfig(hwnd
))
1500 if (strcmp(prev_cfg
.logfilename
, cfg
.logfilename
) ||
1501 prev_cfg
.logtype
!= cfg
.logtype
) {
1502 logfclose(); /* reset logging */
1508 * Flush the line discipline's edit buffer in the
1509 * case where local editing has just been disabled.
1511 ldisc_send(NULL
, 0, 0);
1519 /* Screen size changed ? */
1520 if (cfg
.height
!= prev_cfg
.height
||
1521 cfg
.width
!= prev_cfg
.width
||
1522 cfg
.savelines
!= prev_cfg
.savelines
||
1523 cfg
.resize_action
!= RESIZE_TERM
)
1524 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1526 /* Enable or disable the scroll bar, etc */
1528 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1529 LONG nexflag
, exflag
=
1530 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1533 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1534 if (cfg
.alwaysontop
) {
1535 nexflag
|= WS_EX_TOPMOST
;
1536 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1537 SWP_NOMOVE
| SWP_NOSIZE
);
1539 nexflag
&= ~(WS_EX_TOPMOST
);
1540 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1541 SWP_NOMOVE
| SWP_NOSIZE
);
1544 if (cfg
.sunken_edge
)
1545 nexflag
|= WS_EX_CLIENTEDGE
;
1547 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1553 nflg
&= ~WS_VSCROLL
;
1554 if (cfg
.resize_action
== RESIZE_DISABLED
)
1555 nflg
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1557 nflg
|= (WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1559 if (nflg
!= flag
|| nexflag
!= exflag
) {
1561 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1562 if (nexflag
!= exflag
)
1563 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1565 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1566 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1567 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1575 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1580 set_title(cfg
.wintitle
);
1581 if (IsIconic(hwnd
)) {
1583 cfg
.win_name_always ? window_name
:
1587 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1588 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1589 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1590 cfg
.fontheight
!= prev_cfg
.fontheight
||
1591 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1592 cfg
.vtmode
!= prev_cfg
.vtmode
||
1593 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1594 (cfg
.resize_action
!= RESIZE_FONT
&&
1595 prev_cfg
.resize_action
== RESIZE_FONT
))
1598 InvalidateRect(hwnd
, NULL
, TRUE
);
1599 reset_window(init_lvl
);
1612 back
->special(TS_AYT
);
1615 back
->special(TS_BRK
);
1618 back
->special(TS_SYNCH
);
1621 back
->special(TS_EC
);
1624 back
->special(TS_EL
);
1627 back
->special(TS_GA
);
1630 back
->special(TS_NOP
);
1633 back
->special(TS_ABORT
);
1636 back
->special(TS_AO
);
1639 back
->special(TS_IP
);
1642 back
->special(TS_SUSP
);
1645 back
->special(TS_EOR
);
1648 back
->special(TS_EOF
);
1655 * We get this if the System menu has been activated.
1656 * This might happen from within TranslateKey, in which
1657 * case it really wants to be followed by a `space'
1658 * character to actually _bring the menu up_ rather
1659 * than just sitting there in `ready to appear' state.
1662 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1665 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1666 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1671 #define X_POS(l) ((int)(short)LOWORD(l))
1672 #define Y_POS(l) ((int)(short)HIWORD(l))
1674 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1675 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1676 #define WHEEL_DELTA 120
1679 wheel_accumulator
+= (short) HIWORD(wParam
);
1680 wParam
= LOWORD(wParam
);
1682 /* process events when the threshold is reached */
1683 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
1686 /* reduce amount for next time */
1687 if (wheel_accumulator
> 0) {
1689 wheel_accumulator
-= WHEEL_DELTA
;
1690 } else if (wheel_accumulator
< 0) {
1692 wheel_accumulator
+= WHEEL_DELTA
;
1696 if (send_raw_mouse
) {
1697 /* send a mouse-down followed by a mouse up */
1700 TO_CHR_X(X_POS(lParam
)),
1701 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1702 wParam
& MK_CONTROL
);
1703 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1704 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1705 wParam
& MK_CONTROL
);
1707 /* trigger a scroll */
1709 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
1714 case WM_LBUTTONDOWN
:
1715 case WM_MBUTTONDOWN
:
1716 case WM_RBUTTONDOWN
:
1723 case WM_LBUTTONDOWN
:
1727 case WM_MBUTTONDOWN
:
1728 button
= MBT_MIDDLE
;
1731 case WM_RBUTTONDOWN
:
1740 button
= MBT_MIDDLE
;
1748 button
= press
= 0; /* shouldn't happen */
1753 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1754 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
);
1757 term_mouse(button
, MA_RELEASE
,
1758 TO_CHR_X(X_POS(lParam
)),
1759 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1760 wParam
& MK_CONTROL
);
1768 * Add the mouse position and message time to the random
1771 noise_ultralight(lParam
);
1773 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1775 if (wParam
& MK_LBUTTON
)
1777 else if (wParam
& MK_MBUTTON
)
1781 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1782 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1783 wParam
& MK_CONTROL
);
1786 case WM_NCMOUSEMOVE
:
1788 noise_ultralight(lParam
);
1790 case WM_IGNORE_CLIP
:
1791 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1793 case WM_DESTROYCLIPBOARD
:
1796 ignore_clip
= FALSE
;
1802 hdc
= BeginPaint(hwnd
, &p
);
1804 SelectPalette(hdc
, pal
, TRUE
);
1805 RealizePalette(hdc
);
1808 (p
.rcPaint
.left
-offset_width
)/font_width
,
1809 (p
.rcPaint
.top
-offset_height
)/font_height
,
1810 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
1811 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
1814 p
.rcPaint
.left
< offset_width
||
1815 p
.rcPaint
.top
< offset_height
||
1816 p
.rcPaint
.right
>= offset_width
+ font_width
*cols
||
1817 p
.rcPaint
.bottom
>= offset_height
+ font_height
*rows
)
1819 HBRUSH fillcolour
, oldbrush
;
1821 fillcolour
= CreateSolidBrush (
1822 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1823 oldbrush
= SelectObject(hdc
, fillcolour
);
1824 edge
= CreatePen(PS_SOLID
, 0,
1825 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1826 oldpen
= SelectObject(hdc
, edge
);
1828 ExcludeClipRect(hdc
,
1829 offset_width
, offset_height
,
1830 offset_width
+font_width
*cols
,
1831 offset_height
+font_height
*rows
);
1833 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1834 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1836 // SelectClipRgn(hdc, NULL);
1838 SelectObject(hdc
, oldbrush
);
1839 DeleteObject(fillcolour
);
1840 SelectObject(hdc
, oldpen
);
1843 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
1844 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
1850 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1851 * but the only one that's likely to try to overload us is FD_READ.
1852 * This means buffering just one is fine.
1854 if (pending_netevent
)
1855 enact_pending_netevent();
1857 pending_netevent
= TRUE
;
1858 pend_netevent_wParam
= wParam
;
1859 pend_netevent_lParam
= lParam
;
1860 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
1861 enact_pending_netevent();
1863 time(&last_movement
);
1867 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
1869 flash_window(0); /* stop */
1881 case WM_ENTERSIZEMOVE
:
1882 #ifdef RDB_DEBUG_PATCH
1883 debug((27, "WM_ENTERSIZEMOVE"));
1887 need_backend_resize
= FALSE
;
1889 case WM_EXITSIZEMOVE
:
1892 #ifdef RDB_DEBUG_PATCH
1893 debug((27, "WM_EXITSIZEMOVE"));
1895 if (need_backend_resize
) {
1896 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1897 InvalidateRect(hwnd
, NULL
, TRUE
);
1902 * This does two jobs:
1903 * 1) Keep the sizetip uptodate
1904 * 2) Make sure the window size is _stepped_ in units of the font size.
1906 if (cfg
.resize_action
== RESIZE_TERM
&& !alt_pressed
) {
1907 int width
, height
, w
, h
, ew
, eh
;
1908 LPRECT r
= (LPRECT
) lParam
;
1910 if ( !need_backend_resize
&&
1911 (cfg
.height
!= rows
|| cfg
.width
!= cols
)) {
1913 * Great! It seems the host has been changing the terminal
1914 * size, well the user is now grabbing so this is probably
1915 * the least confusing solution in the long run even though
1916 * it a is suprise. Unfortunatly the only way to prevent
1917 * this seems to be to let the host change the window size
1918 * and as that's a user option we're still right back here.
1920 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1922 InvalidateRect(hwnd
, NULL
, TRUE
);
1923 need_backend_resize
= TRUE
;
1926 width
= r
->right
- r
->left
- extra_width
;
1927 height
= r
->bottom
- r
->top
- extra_height
;
1928 w
= (width
+ font_width
/ 2) / font_width
;
1931 h
= (height
+ font_height
/ 2) / font_height
;
1934 UpdateSizeTip(hwnd
, w
, h
);
1935 ew
= width
- w
* font_width
;
1936 eh
= height
- h
* font_height
;
1938 if (wParam
== WMSZ_LEFT
||
1939 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
1945 if (wParam
== WMSZ_TOP
||
1946 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
1956 int width
, height
, w
, h
, rv
= 0;
1957 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
1958 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
1959 LPRECT r
= (LPRECT
) lParam
;
1961 width
= r
->right
- r
->left
- ex_width
;
1962 height
= r
->bottom
- r
->top
- ex_height
;
1964 w
= (width
+ cols
/2)/cols
;
1965 h
= (height
+ rows
/2)/rows
;
1966 if ( r
->right
!= r
->left
+ w
*cols
+ ex_width
)
1969 if (wParam
== WMSZ_LEFT
||
1970 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
1971 r
->left
= r
->right
- w
*cols
- ex_width
;
1973 r
->right
= r
->left
+ w
*cols
+ ex_width
;
1975 if (r
->bottom
!= r
->top
+ h
*rows
+ ex_height
)
1978 if (wParam
== WMSZ_TOP
||
1979 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
1980 r
->top
= r
->bottom
- h
*rows
- ex_height
;
1982 r
->bottom
= r
->top
+ h
*rows
+ ex_height
;
1987 /* break; (never reached) */
1989 #ifdef RDB_DEBUG_PATCH
1990 debug((27, "WM_SIZE %s (%d,%d)",
1991 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
1992 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
1993 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
1994 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
1996 LOWORD(lParam
), HIWORD(lParam
)));
1998 if (wParam
== SIZE_MINIMIZED
) {
2000 cfg
.win_name_always ? window_name
: icon_name
);
2003 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2004 SetWindowText(hwnd
, window_name
);
2006 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2007 /* A resize, well it better be a minimize. */
2011 int width
, height
, w
, h
;
2013 width
= LOWORD(lParam
);
2014 height
= HIWORD(lParam
);
2017 if (wParam
== SIZE_MAXIMIZED
) {
2021 if (cfg
.resize_action
!= RESIZE_FONT
) {
2022 w
= width
/ font_width
;
2024 h
= height
/ font_height
;
2027 term_size(h
, w
, cfg
.savelines
);
2030 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2032 if (cfg
.resize_action
!= RESIZE_FONT
)
2033 term_size(prev_rows
, prev_cols
, cfg
.savelines
);
2036 /* This is an unexpected resize, these will normally happen
2037 * if the window is too large. Probably either the user
2038 * selected a huge font or the screen size has changed.
2040 * This is also called with minimize.
2042 else reset_window(-1);
2046 * Don't call back->size in mid-resize. (To prevent
2047 * massive numbers of resize events getting sent
2048 * down the connection during an NT opaque drag.)
2051 if (cfg
.resize_action
== RESIZE_TERM
&& !alt_pressed
) {
2052 need_backend_resize
= TRUE
;
2053 w
= (width
-cfg
.window_border
*2) / font_width
;
2055 h
= (height
-cfg
.window_border
*2) / font_height
;
2066 switch (LOWORD(wParam
)) {
2080 term_scroll(0, +rows
/ 2);
2083 term_scroll(0, -rows
/ 2);
2085 case SB_THUMBPOSITION
:
2087 term_scroll(1, HIWORD(wParam
));
2091 case WM_PALETTECHANGED
:
2092 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2093 HDC hdc
= get_ctx();
2095 if (RealizePalette(hdc
) > 0)
2101 case WM_QUERYNEWPALETTE
:
2103 HDC hdc
= get_ctx();
2105 if (RealizePalette(hdc
) > 0)
2117 * Add the scan code and keypress timing to the random
2120 noise_ultralight(lParam
);
2123 * We don't do TranslateMessage since it disassociates the
2124 * resulting CHAR message from the KEYDOWN that sparked it,
2125 * which we occasionally don't want. Instead, we process
2126 * KEYDOWN, and call the Win32 translator functions so that
2127 * we get the translations under _our_ control.
2130 unsigned char buf
[20];
2133 if (wParam
== VK_PROCESSKEY
) {
2136 m
.message
= WM_KEYDOWN
;
2138 m
.lParam
= lParam
& 0xdfff;
2139 TranslateMessage(&m
);
2141 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2143 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2147 * Interrupt an ongoing paste. I'm not sure
2148 * this is sensible, but for the moment it's
2149 * preferable to having to faff about buffering
2155 * We need not bother about stdin backlogs
2156 * here, because in GUI PuTTY we can't do
2157 * anything about it anyway; there's no means
2158 * of asking Windows to hold off on KEYDOWN
2159 * messages. We _have_ to buffer everything
2162 ldisc_send(buf
, len
, 1);
2168 case WM_INPUTLANGCHANGE
:
2170 /* wParam == Font number */
2171 /* lParam == Locale */
2173 HKL NewInputLocale
= (HKL
) lParam
;
2175 // lParam == GetKeyboardLayout(0);
2177 GetLocaleInfo(LOWORD(NewInputLocale
),
2178 LOCALE_IDEFAULTANSICODEPAGE
, lbuf
, sizeof(lbuf
));
2180 kbd_codepage
= atoi(lbuf
);
2183 case WM_IME_COMPOSITION
:
2189 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2190 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2192 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2193 break; /* fall back to DefWindowProc */
2195 hIMC
= ImmGetContext(hwnd
);
2196 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2199 buff
= (char*) smalloc(n
);
2200 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2201 luni_send((unsigned short *)buff
, n
/ 2, 1);
2204 ImmReleaseContext(hwnd
, hIMC
);
2209 if (wParam
& 0xFF00) {
2210 unsigned char buf
[2];
2213 buf
[0] = wParam
>> 8;
2214 lpage_send(kbd_codepage
, buf
, 2, 1);
2216 char c
= (unsigned char) wParam
;
2217 lpage_send(kbd_codepage
, &c
, 1, 1);
2223 * Nevertheless, we are prepared to deal with WM_CHAR
2224 * messages, should they crop up. So if someone wants to
2225 * post the things to us as part of a macro manoeuvre,
2226 * we're ready to cope.
2229 char c
= (unsigned char)wParam
;
2230 lpage_send(CP_ACP
, &c
, 1, 1);
2234 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2235 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2240 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2244 * Move the system caret. (We maintain one, even though it's
2245 * invisible, for the benefit of blind people: apparently some
2246 * helper software tracks the system caret, so we should arrange to
2249 void sys_cursor(int x
, int y
)
2254 if (!has_focus
) return;
2256 SetCaretPos(x
* font_width
+ offset_width
,
2257 y
* font_height
+ offset_height
);
2259 /* IMM calls on Win98 and beyond only */
2260 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2262 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2263 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2265 /* we should have the IMM functions */
2266 hIMC
= ImmGetContext(hwnd
);
2267 cf
.dwStyle
= CFS_POINT
;
2268 cf
.ptCurrentPos
.x
= x
* font_width
;
2269 cf
.ptCurrentPos
.y
= y
* font_height
;
2270 ImmSetCompositionWindow(hIMC
, &cf
);
2272 ImmReleaseContext(hwnd
, hIMC
);
2276 * Draw a line of text in the window, at given character
2277 * coordinates, in given attributes.
2279 * We are allowed to fiddle with the contents of `text'.
2281 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2282 unsigned long attr
, int lattr
)
2285 int nfg
, nbg
, nfont
;
2288 int force_manual_underline
= 0;
2289 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2290 int char_width
= fnt_width
;
2291 int text_adjust
= 0;
2292 static int *IpDx
= 0, IpDxLEN
= 0;
2294 if (attr
& ATTR_WIDE
)
2297 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2299 if (len
> IpDxLEN
) {
2301 IpDx
= smalloc((len
+ 16) * sizeof(int));
2302 IpDxLEN
= (len
+ 16);
2304 for (i
= 0; i
< IpDxLEN
; i
++)
2305 IpDx
[i
] = char_width
;
2308 /* Only want the left half of double width lines */
2309 if (lattr
!= LATTR_NORM
&& x
*2 >= cols
)
2317 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
2318 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2319 attr
^= ATTR_CUR_XOR
;
2323 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2324 /* Assume a poorman font is borken in other ways too. */
2334 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2337 if (attr
& ATTR_NARROW
)
2338 nfont
|= FONT_NARROW
;
2340 /* Special hack for the VT100 linedraw glyphs. */
2341 if ((attr
& CSET_MASK
) == 0x2300) {
2342 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2343 switch ((unsigned char) (text
[0])) {
2345 text_adjust
= -2 * font_height
/ 5;
2348 text_adjust
= -1 * font_height
/ 5;
2351 text_adjust
= font_height
/ 5;
2354 text_adjust
= 2 * font_height
/ 5;
2357 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2360 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2361 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2362 if (attr
& ATTR_UNDER
) {
2363 attr
&= ~ATTR_UNDER
;
2364 force_manual_underline
= 1;
2369 /* Anything left as an original character set is unprintable. */
2370 if (DIRECT_CHAR(attr
)) {
2373 memset(text
, 0xFD, len
);
2377 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2380 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2381 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2382 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2384 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2385 nfont
|= FONT_UNDERLINE
;
2386 another_font(nfont
);
2387 if (!fonts
[nfont
]) {
2388 if (nfont
& FONT_UNDERLINE
)
2389 force_manual_underline
= 1;
2390 /* Don't do the same for manual bold, it could be bad news. */
2392 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2394 another_font(nfont
);
2396 nfont
= FONT_NORMAL
;
2397 if (attr
& ATTR_REVERSE
) {
2402 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2404 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2408 SelectObject(hdc
, fonts
[nfont
]);
2409 SetTextColor(hdc
, fg
);
2410 SetBkColor(hdc
, bg
);
2411 SetBkMode(hdc
, OPAQUE
);
2414 line_box
.right
= x
+ char_width
* len
;
2415 line_box
.bottom
= y
+ font_height
;
2417 /* Only want the left half of double width lines */
2418 if (line_box
.right
> font_width
*cols
+offset_width
)
2419 line_box
.right
= font_width
*cols
+offset_width
;
2421 /* We're using a private area for direct to font. (512 chars.) */
2422 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2423 /* Ho Hum, dbcs fonts are a PITA! */
2424 /* To display on W9x I have to convert to UCS */
2425 static wchar_t *uni_buf
= 0;
2426 static int uni_len
= 0;
2428 if (len
> uni_len
) {
2430 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2433 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2434 uni_buf
[nlen
] = 0xFFFD;
2435 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2436 IpDx
[nlen
] += char_width
;
2437 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2438 text
+mptr
, 2, uni_buf
+nlen
, 1);
2443 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2444 text
+mptr
, 1, uni_buf
+nlen
, 1);
2452 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2453 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2454 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2455 SetBkMode(hdc
, TRANSPARENT
);
2456 ExtTextOutW(hdc
, x
- 1,
2457 y
- font_height
* (lattr
==
2458 LATTR_BOT
) + text_adjust
,
2459 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2463 } else if (DIRECT_FONT(attr
)) {
2465 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2466 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2467 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2468 SetBkMode(hdc
, TRANSPARENT
);
2470 /* GRR: This draws the character outside it's box and can leave
2471 * 'droppings' even with the clip box! I suppose I could loop it
2472 * one character at a time ... yuk.
2474 * Or ... I could do a test print with "W", and use +1 or -1 for this
2475 * shift depending on if the leftmost column is blank...
2477 ExtTextOut(hdc
, x
- 1,
2478 y
- font_height
* (lattr
==
2479 LATTR_BOT
) + text_adjust
,
2480 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2483 /* And 'normal' unicode characters */
2484 static WCHAR
*wbuf
= NULL
;
2485 static int wlen
= 0;
2490 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2492 for (i
= 0; i
< len
; i
++)
2493 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2496 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2497 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2499 /* And the shadow bold hack. */
2500 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2501 SetBkMode(hdc
, TRANSPARENT
);
2502 ExtTextOutW(hdc
, x
- 1,
2503 y
- font_height
* (lattr
==
2504 LATTR_BOT
) + text_adjust
,
2505 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2508 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2509 (und_mode
== UND_LINE
2510 && (attr
& ATTR_UNDER
)))) {
2513 if (lattr
== LATTR_BOT
)
2514 dec
= dec
* 2 - font_height
;
2516 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2517 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2518 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2519 oldpen
= SelectObject(hdc
, oldpen
);
2520 DeleteObject(oldpen
);
2524 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2525 unsigned long attr
, int lattr
)
2531 int ctype
= cfg
.cursor_type
;
2533 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2534 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2535 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2539 attr
|= TATTR_RIGHTCURS
;
2542 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2543 if (attr
& ATTR_WIDE
)
2550 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2553 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2554 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2555 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2556 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2557 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2558 Polyline(hdc
, pts
, 5);
2559 oldpen
= SelectObject(hdc
, oldpen
);
2560 DeleteObject(oldpen
);
2561 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2562 int startx
, starty
, dx
, dy
, length
, i
;
2565 starty
= y
+ descent
;
2568 length
= char_width
;
2571 if (attr
& TATTR_RIGHTCURS
)
2572 xadjust
= char_width
- 1;
2573 startx
= x
+ xadjust
;
2577 length
= font_height
;
2579 if (attr
& TATTR_ACTCURS
) {
2582 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2583 MoveToEx(hdc
, startx
, starty
, NULL
);
2584 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2585 oldpen
= SelectObject(hdc
, oldpen
);
2586 DeleteObject(oldpen
);
2588 for (i
= 0; i
< length
; i
++) {
2590 SetPixel(hdc
, startx
, starty
, colours
[23]);
2599 /* This function gets the actual width of a character in the normal font.
2601 int CharWidth(Context ctx
, int uc
) {
2605 /* If the font max is the same as the font ave width then this
2606 * function is a no-op.
2608 if (!font_dualwidth
) return 1;
2610 switch (uc
& CSET_MASK
) {
2612 uc
= unitab_line
[uc
& 0xFF];
2615 uc
= unitab_xterm
[uc
& 0xFF];
2618 uc
= unitab_scoacs
[uc
& 0xFF];
2621 if (DIRECT_FONT(uc
)) {
2622 if (dbcs_screenfont
) return 1;
2624 /* Speedup, I know of no font where ascii is the wrong width */
2625 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
2628 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
2629 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2630 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
2631 another_font(FONT_OEM
);
2632 if (!fonts
[FONT_OEM
]) return 0;
2634 SelectObject(hdc
, fonts
[FONT_OEM
]);
2638 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
2639 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
2642 /* Speedup, I know of no font where ascii is the wrong width */
2643 if (uc
>= ' ' && uc
<= '~') return 1;
2645 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2646 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
2647 /* Okay that one worked */ ;
2648 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
2649 /* This should work on 9x too, but it's "less accurate" */ ;
2654 ibuf
+= font_width
/ 2 -1;
2661 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2662 * codes. Returns number of bytes used or zero to drop the message
2663 * or -1 to forward the message to windows.
2665 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2666 unsigned char *output
)
2669 int scan
, left_alt
= 0, key_down
, shift_state
;
2671 unsigned char *p
= output
;
2672 static int alt_sum
= 0;
2674 HKL kbd_layout
= GetKeyboardLayout(0);
2676 static WORD keys
[3];
2677 static int compose_char
= 0;
2678 static WPARAM compose_key
= 0;
2680 r
= GetKeyboardState(keystate
);
2682 memset(keystate
, 0, sizeof(keystate
));
2685 #define SHOW_TOASCII_RESULT
2686 { /* Tell us all about key events */
2687 static BYTE oldstate
[256];
2688 static int first
= 1;
2692 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2695 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2697 } else if ((HIWORD(lParam
) & KF_UP
)
2698 && scan
== (HIWORD(lParam
) & 0xFF)) {
2702 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2703 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2716 debug(("VK_%02x", wParam
));
2718 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2720 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2722 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2723 if (ch
>= ' ' && ch
<= '~')
2724 debug((", '%c'", ch
));
2726 debug((", $%02x", ch
));
2729 debug((", KB0=%02x", keys
[0]));
2731 debug((", KB1=%02x", keys
[1]));
2733 debug((", KB2=%02x", keys
[2]));
2735 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2737 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2739 if ((HIWORD(lParam
) & KF_EXTENDED
))
2741 if ((HIWORD(lParam
) & KF_UP
))
2745 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2746 else if ((HIWORD(lParam
) & KF_UP
))
2747 oldstate
[wParam
& 0xFF] ^= 0x80;
2749 oldstate
[wParam
& 0xFF] ^= 0x81;
2751 for (ch
= 0; ch
< 256; ch
++)
2752 if (oldstate
[ch
] != keystate
[ch
])
2753 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2755 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2759 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2760 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2764 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2765 if ((cfg
.funky_type
== 3 ||
2766 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2767 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2769 wParam
= VK_EXECUTE
;
2771 /* UnToggle NUMLock */
2772 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2773 keystate
[VK_NUMLOCK
] ^= 1;
2776 /* And write back the 'adjusted' state */
2777 SetKeyboardState(keystate
);
2780 /* Disable Auto repeat if required */
2781 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2784 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2787 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2789 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2790 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2791 if (cfg
.ctrlaltkeys
)
2792 keystate
[VK_MENU
] = 0;
2794 keystate
[VK_RMENU
] = 0x80;
2799 alt_pressed
= (left_alt
&& key_down
);
2801 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2802 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2803 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2805 /* Note if AltGr was pressed and if it was used as a compose key */
2806 if (!compose_state
) {
2807 compose_key
= 0x100;
2808 if (cfg
.compose_key
) {
2809 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2810 compose_key
= wParam
;
2812 if (wParam
== VK_APPS
)
2813 compose_key
= wParam
;
2816 if (wParam
== compose_key
) {
2817 if (compose_state
== 0
2818 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2820 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2824 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
2828 * Record that we pressed key so the scroll window can be reset, but
2829 * be careful to avoid Shift-UP/Down
2831 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
2835 if (compose_state
> 1 && left_alt
)
2838 /* Sanitize the number pad if not using a PC NumPad */
2839 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2840 && cfg
.funky_type
!= 2)
2841 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
2842 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
2846 nParam
= VK_NUMPAD0
;
2849 nParam
= VK_NUMPAD1
;
2852 nParam
= VK_NUMPAD2
;
2855 nParam
= VK_NUMPAD3
;
2858 nParam
= VK_NUMPAD4
;
2861 nParam
= VK_NUMPAD5
;
2864 nParam
= VK_NUMPAD6
;
2867 nParam
= VK_NUMPAD7
;
2870 nParam
= VK_NUMPAD8
;
2873 nParam
= VK_NUMPAD9
;
2876 nParam
= VK_DECIMAL
;
2880 if (keystate
[VK_NUMLOCK
] & 1)
2887 /* If a key is pressed and AltGr is not active */
2888 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
2889 /* Okay, prepare for most alts then ... */
2893 /* Lets see if it's a pattern we know all about ... */
2894 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2895 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2898 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2899 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
2902 if (wParam
== VK_INSERT
&& shift_state
== 1) {
2906 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
2909 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
2910 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
2913 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
) {
2917 /* Control-Numlock for app-keypad mode switch */
2918 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
2919 app_keypad_keys
^= 1;
2923 /* Nethack keypad */
2924 if (cfg
.nethack_keypad
&& !left_alt
) {
2927 *p
++ = shift_state ?
'B' : 'b';
2930 *p
++ = shift_state ?
'J' : 'j';
2933 *p
++ = shift_state ?
'N' : 'n';
2936 *p
++ = shift_state ?
'H' : 'h';
2939 *p
++ = shift_state ?
'.' : '.';
2942 *p
++ = shift_state ?
'L' : 'l';
2945 *p
++ = shift_state ?
'Y' : 'y';
2948 *p
++ = shift_state ?
'K' : 'k';
2951 *p
++ = shift_state ?
'U' : 'u';
2956 /* Application Keypad */
2960 if (cfg
.funky_type
== 3 ||
2961 (cfg
.funky_type
<= 1 &&
2962 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
2976 if (app_keypad_keys
&& !cfg
.no_applic_k
)
3013 if (cfg
.funky_type
== 2) {
3018 } else if (shift_state
)
3025 if (cfg
.funky_type
== 2)
3029 if (cfg
.funky_type
== 2)
3033 if (cfg
.funky_type
== 2)
3038 if (HIWORD(lParam
) & KF_EXTENDED
)
3044 if (xkey
>= 'P' && xkey
<= 'S')
3045 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3047 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3049 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3054 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3055 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3059 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3065 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3069 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3073 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3078 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3083 /* Control-2 to Control-8 are special */
3084 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3085 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3088 if (shift_state
== 2 && wParam
== 0xBD) {
3092 if (shift_state
== 2 && wParam
== 0xDF) {
3096 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
3103 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3104 * for integer decimal nn.)
3106 * We also deal with the weird ones here. Linux VCs replace F1
3107 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3108 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3114 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3117 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3120 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3123 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3126 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3129 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3132 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3135 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3138 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3141 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3174 if ((shift_state
&2) == 0) switch (wParam
) {
3194 /* Reorder edit keys to physical order */
3195 if (cfg
.funky_type
== 3 && code
<= 6)
3196 code
= "\0\2\1\4\5\3\6"[code
];
3198 if (vt52_mode
&& code
> 0 && code
<= 6) {
3199 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3203 if (cfg
.funky_type
== 5 && /* SCO function keys */
3204 code
>= 11 && code
<= 34) {
3205 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3208 case VK_F1
: index
= 0; break;
3209 case VK_F2
: index
= 1; break;
3210 case VK_F3
: index
= 2; break;
3211 case VK_F4
: index
= 3; break;
3212 case VK_F5
: index
= 4; break;
3213 case VK_F6
: index
= 5; break;
3214 case VK_F7
: index
= 6; break;
3215 case VK_F8
: index
= 7; break;
3216 case VK_F9
: index
= 8; break;
3217 case VK_F10
: index
= 9; break;
3218 case VK_F11
: index
= 10; break;
3219 case VK_F12
: index
= 11; break;
3221 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3222 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3223 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3226 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3227 code
>= 1 && code
<= 6) {
3228 char codes
[] = "HL.FIG";
3232 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3236 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3243 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3246 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3249 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3250 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3253 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3255 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3257 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3260 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3261 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3265 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3270 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3271 * some reason seems to send VK_CLEAR to Windows...).
3294 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3296 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
3297 /* VT100 & VT102 manuals both state the app cursor keys
3298 * only work if the app keypad is on.
3300 if (!app_keypad_keys
)
3302 /* Useful mapping of Ctrl-arrows */
3303 if (shift_state
== 2)
3307 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3309 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3316 * Finally, deal with Return ourselves. (Win95 seems to
3317 * foul it up when Alt is pressed, for some reason.)
3319 if (wParam
== VK_RETURN
) { /* Return */
3325 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3326 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3331 /* Okay we've done everything interesting; let windows deal with
3332 * the boring stuff */
3336 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3337 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3339 keystate
[VK_CAPITAL
] = 0;
3342 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3343 #ifdef SHOW_TOASCII_RESULT
3344 if (r
== 1 && !key_down
) {
3346 if (in_utf
|| dbcs_screenfont
)
3347 debug((", (U+%04x)", alt_sum
));
3349 debug((", LCH(%d)", alt_sum
));
3351 debug((", ACH(%d)", keys
[0]));
3356 for (r1
= 0; r1
< r
; r1
++) {
3357 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3366 * Interrupt an ongoing paste. I'm not sure this is
3367 * sensible, but for the moment it's preferable to
3368 * having to faff about buffering things.
3373 for (i
= 0; i
< r
; i
++) {
3374 unsigned char ch
= (unsigned char) keys
[i
];
3376 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3381 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3385 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3386 MessageBeep(MB_ICONHAND
);
3390 luni_send(&keybuf
, 1, 1);
3398 if (in_utf
|| dbcs_screenfont
) {
3400 luni_send(&keybuf
, 1, 1);
3402 ch
= (char) alt_sum
;
3404 * We need not bother about stdin
3405 * backlogs here, because in GUI PuTTY
3406 * we can't do anything about it
3407 * anyway; there's no means of asking
3408 * Windows to hold off on KEYDOWN
3409 * messages. We _have_ to buffer
3410 * everything we're sent.
3412 ldisc_send(&ch
, 1, 1);
3416 lpage_send(kbd_codepage
, &ch
, 1, 1);
3418 if(capsOn
&& ch
< 0x80) {
3421 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3422 luni_send(cbuf
+!left_alt
, 1+!!left_alt
, 1);
3427 lpage_send(kbd_codepage
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3433 /* This is so the ALT-Numpad and dead keys work correctly. */
3438 /* If we're definitly not building up an ALT-54321 then clear it */
3441 /* If we will be using alt_sum fix the 256s */
3442 else if (keys
[0] && (in_utf
|| dbcs_screenfont
))
3447 * ALT alone may or may not want to bring up the System menu.
3448 * If it's not meant to, we return 0 on presses or releases of
3449 * ALT, to show that we've swallowed the keystroke. Otherwise
3450 * we return -1, which means Windows will give the keystroke
3451 * its default handling (i.e. bring up the System menu).
3453 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3459 void set_title(char *title
)
3462 window_name
= smalloc(1 + strlen(title
));
3463 strcpy(window_name
, title
);
3464 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3465 SetWindowText(hwnd
, title
);
3468 void set_icon(char *title
)
3471 icon_name
= smalloc(1 + strlen(title
));
3472 strcpy(icon_name
, title
);
3473 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3474 SetWindowText(hwnd
, title
);
3477 void set_sbar(int total
, int start
, int page
)
3484 si
.cbSize
= sizeof(si
);
3485 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3487 si
.nMax
= total
- 1;
3491 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3494 Context
get_ctx(void)
3500 SelectPalette(hdc
, pal
, FALSE
);
3506 void free_ctx(Context ctx
)
3508 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3509 ReleaseDC(hwnd
, ctx
);
3512 static void real_palette_set(int n
, int r
, int g
, int b
)
3515 logpal
->palPalEntry
[n
].peRed
= r
;
3516 logpal
->palPalEntry
[n
].peGreen
= g
;
3517 logpal
->palPalEntry
[n
].peBlue
= b
;
3518 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3519 colours
[n
] = PALETTERGB(r
, g
, b
);
3520 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3522 colours
[n
] = RGB(r
, g
, b
);
3525 void palette_set(int n
, int r
, int g
, int b
)
3527 static const int first
[21] = {
3528 0, 2, 4, 6, 8, 10, 12, 14,
3529 1, 3, 5, 7, 9, 11, 13, 15,
3532 real_palette_set(first
[n
], r
, g
, b
);
3534 real_palette_set(first
[n
] + 1, r
, g
, b
);
3536 HDC hdc
= get_ctx();
3537 UnrealizeObject(pal
);
3538 RealizePalette(hdc
);
3543 void palette_reset(void)
3547 for (i
= 0; i
< NCOLOURS
; i
++) {
3549 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3550 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3551 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3552 logpal
->palPalEntry
[i
].peFlags
= 0;
3553 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3554 defpal
[i
].rgbtGreen
,
3555 defpal
[i
].rgbtBlue
);
3557 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3558 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3563 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3565 RealizePalette(hdc
);
3570 void write_aclip(char *data
, int len
, int must_deselect
)
3575 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3578 lock
= GlobalLock(clipdata
);
3581 memcpy(lock
, data
, len
);
3582 ((unsigned char *) lock
)[len
] = 0;
3583 GlobalUnlock(clipdata
);
3586 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3588 if (OpenClipboard(hwnd
)) {
3590 SetClipboardData(CF_TEXT
, clipdata
);
3593 GlobalFree(clipdata
);
3596 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3600 * Note: unlike write_aclip() this will not append a nul.
3602 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3609 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3611 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3612 len
* sizeof(wchar_t));
3613 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3615 if (!clipdata
|| !clipdata2
) {
3617 GlobalFree(clipdata
);
3619 GlobalFree(clipdata2
);
3622 if (!(lock
= GlobalLock(clipdata
)))
3624 if (!(lock2
= GlobalLock(clipdata2
)))
3627 memcpy(lock
, data
, len
* sizeof(wchar_t));
3628 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3630 GlobalUnlock(clipdata
);
3631 GlobalUnlock(clipdata2
);
3634 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3636 if (OpenClipboard(hwnd
)) {
3638 SetClipboardData(CF_UNICODETEXT
, clipdata
);
3639 SetClipboardData(CF_TEXT
, clipdata2
);
3642 GlobalFree(clipdata
);
3643 GlobalFree(clipdata2
);
3647 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3650 void get_clip(wchar_t ** p
, int *len
)
3652 static HGLOBAL clipdata
= NULL
;
3653 static wchar_t *converted
= 0;
3662 GlobalUnlock(clipdata
);
3665 } else if (OpenClipboard(NULL
)) {
3666 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
3668 *p
= GlobalLock(clipdata
);
3670 for (p2
= *p
; *p2
; p2
++);
3674 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
3678 s
= GlobalLock(clipdata
);
3679 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
3680 *p
= converted
= smalloc(i
* sizeof(wchar_t));
3681 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
3694 * Move `lines' lines from position `from' to position `to' in the
3697 void optimised_move(int to
, int from
, int lines
)
3702 min
= (to
< from ? to
: from
);
3703 max
= to
+ from
- min
;
3705 r
.left
= offset_width
;
3706 r
.right
= offset_width
+ cols
* font_width
;
3707 r
.top
= offset_height
+ min
* font_height
;
3708 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
3709 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
3714 * Print a message box and perform a fatal exit.
3716 void fatalbox(char *fmt
, ...)
3722 vsprintf(stuff
, fmt
, ap
);
3724 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
3729 * Manage window caption / taskbar flashing, if enabled.
3730 * 0 = stop, 1 = maintain, 2 = start
3732 static void flash_window(int mode
)
3734 static long last_flash
= 0;
3735 static int flashing
= 0;
3736 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
3739 FlashWindow(hwnd
, FALSE
);
3743 } else if (mode
== 2) {
3746 last_flash
= GetTickCount();
3748 FlashWindow(hwnd
, TRUE
);
3751 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
3754 long now
= GetTickCount();
3755 long fdiff
= now
- last_flash
;
3756 if (fdiff
< 0 || fdiff
> 450) {
3758 FlashWindow(hwnd
, TRUE
); /* toggle */
3769 if (mode
== BELL_DEFAULT
) {
3771 * For MessageBeep style bells, we want to be careful of
3772 * timing, because they don't have the nice property of
3773 * PlaySound bells that each one cancels the previous
3774 * active one. So we limit the rate to one per 50ms or so.
3776 static long lastbeep
= 0;
3779 beepdiff
= GetTickCount() - lastbeep
;
3780 if (beepdiff
>= 0 && beepdiff
< 50)
3784 * The above MessageBeep call takes time, so we record the
3785 * time _after_ it finishes rather than before it starts.
3787 lastbeep
= GetTickCount();
3788 } else if (mode
== BELL_WAVEFILE
) {
3789 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
3790 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
3791 sprintf(buf
, "Unable to play sound file\n%s\n"
3792 "Using default sound instead", cfg
.bell_wavefile
);
3793 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
3794 MB_OK
| MB_ICONEXCLAMATION
);
3795 cfg
.beep
= BELL_DEFAULT
;
3798 /* Otherwise, either visual bell or disabled; do nothing here */
3800 flash_window(2); /* start */
3805 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3808 static void flip_full_screen(void)
3813 cx
= GetSystemMetrics(SM_CXSCREEN
);
3814 cy
= GetSystemMetrics(SM_CYSCREEN
);
3815 GetWindowPlacement(hwnd
, &old_wind_placement
);
3816 old_wind_style
= GetWindowLong(hwnd
, GWL_STYLE
);
3817 SetWindowLong(hwnd
, GWL_STYLE
,
3818 old_wind_style
& ~(WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
));
3819 SetWindowPos(hwnd
, HWND_TOP
, 0, 0, cx
, cy
, SWP_SHOWWINDOW
);
3822 SetWindowLong(hwnd
, GWL_STYLE
, old_wind_style
);
3823 SetWindowPlacement(hwnd
,&old_wind_placement
);