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
.locksize
&& cfg
.lockfont
)
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
)) {
1099 if (cfg
.lockfont
&& cfg
.locksize
) 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
);
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
.lockfont
) {
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
.lockfont
&& 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
||
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
.locksize
&& cfg
.lockfont
)
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
.locksize
&& cfg
.lockfont
&& 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
.lockfont
&& !prev_cfg
.lockfont
))
1597 InvalidateRect(hwnd
, NULL
, TRUE
);
1598 reset_window(init_lvl
);
1611 back
->special(TS_AYT
);
1614 back
->special(TS_BRK
);
1617 back
->special(TS_SYNCH
);
1620 back
->special(TS_EC
);
1623 back
->special(TS_EL
);
1626 back
->special(TS_GA
);
1629 back
->special(TS_NOP
);
1632 back
->special(TS_ABORT
);
1635 back
->special(TS_AO
);
1638 back
->special(TS_IP
);
1641 back
->special(TS_SUSP
);
1644 back
->special(TS_EOR
);
1647 back
->special(TS_EOF
);
1654 * We get this if the System menu has been activated.
1655 * This might happen from within TranslateKey, in which
1656 * case it really wants to be followed by a `space'
1657 * character to actually _bring the menu up_ rather
1658 * than just sitting there in `ready to appear' state.
1661 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1664 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1665 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1670 #define X_POS(l) ((int)(short)LOWORD(l))
1671 #define Y_POS(l) ((int)(short)HIWORD(l))
1673 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1674 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1675 #define WHEEL_DELTA 120
1678 wheel_accumulator
+= (short) HIWORD(wParam
);
1679 wParam
= LOWORD(wParam
);
1681 /* process events when the threshold is reached */
1682 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
1685 /* reduce amount for next time */
1686 if (wheel_accumulator
> 0) {
1688 wheel_accumulator
-= WHEEL_DELTA
;
1689 } else if (wheel_accumulator
< 0) {
1691 wheel_accumulator
+= WHEEL_DELTA
;
1695 if (send_raw_mouse
) {
1696 /* send a mouse-down followed by a mouse up */
1699 TO_CHR_X(X_POS(lParam
)),
1700 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1701 wParam
& MK_CONTROL
);
1702 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1703 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1704 wParam
& MK_CONTROL
);
1706 /* trigger a scroll */
1708 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
1713 case WM_LBUTTONDOWN
:
1714 case WM_MBUTTONDOWN
:
1715 case WM_RBUTTONDOWN
:
1722 case WM_LBUTTONDOWN
:
1726 case WM_MBUTTONDOWN
:
1727 button
= MBT_MIDDLE
;
1730 case WM_RBUTTONDOWN
:
1739 button
= MBT_MIDDLE
;
1747 button
= press
= 0; /* shouldn't happen */
1752 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1753 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
);
1756 term_mouse(button
, MA_RELEASE
,
1757 TO_CHR_X(X_POS(lParam
)),
1758 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1759 wParam
& MK_CONTROL
);
1767 * Add the mouse position and message time to the random
1770 noise_ultralight(lParam
);
1772 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1774 if (wParam
& MK_LBUTTON
)
1776 else if (wParam
& MK_MBUTTON
)
1780 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1781 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1782 wParam
& MK_CONTROL
);
1785 case WM_NCMOUSEMOVE
:
1787 noise_ultralight(lParam
);
1789 case WM_IGNORE_CLIP
:
1790 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1792 case WM_DESTROYCLIPBOARD
:
1795 ignore_clip
= FALSE
;
1801 hdc
= BeginPaint(hwnd
, &p
);
1803 SelectPalette(hdc
, pal
, TRUE
);
1804 RealizePalette(hdc
);
1807 (p
.rcPaint
.left
-offset_width
)/font_width
,
1808 (p
.rcPaint
.top
-offset_height
)/font_height
,
1809 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
1810 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
1813 p
.rcPaint
.left
< offset_width
||
1814 p
.rcPaint
.top
< offset_height
||
1815 p
.rcPaint
.right
>= offset_width
+ font_width
*cols
||
1816 p
.rcPaint
.bottom
>= offset_height
+ font_height
*rows
)
1818 HBRUSH fillcolour
, oldbrush
;
1820 fillcolour
= CreateSolidBrush (
1821 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1822 oldbrush
= SelectObject(hdc
, fillcolour
);
1823 edge
= CreatePen(PS_SOLID
, 0,
1824 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1825 oldpen
= SelectObject(hdc
, edge
);
1827 ExcludeClipRect(hdc
,
1828 offset_width
, offset_height
,
1829 offset_width
+font_width
*cols
,
1830 offset_height
+font_height
*rows
);
1832 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1833 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1835 // SelectClipRgn(hdc, NULL);
1837 SelectObject(hdc
, oldbrush
);
1838 DeleteObject(fillcolour
);
1839 SelectObject(hdc
, oldpen
);
1842 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
1843 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
1849 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1850 * but the only one that's likely to try to overload us is FD_READ.
1851 * This means buffering just one is fine.
1853 if (pending_netevent
)
1854 enact_pending_netevent();
1856 pending_netevent
= TRUE
;
1857 pend_netevent_wParam
= wParam
;
1858 pend_netevent_lParam
= lParam
;
1859 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
1860 enact_pending_netevent();
1862 time(&last_movement
);
1866 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
1868 flash_window(0); /* stop */
1880 case WM_ENTERSIZEMOVE
:
1881 #ifdef RDB_DEBUG_PATCH
1882 debug((27, "WM_ENTERSIZEMOVE"));
1886 need_backend_resize
= FALSE
;
1888 case WM_EXITSIZEMOVE
:
1891 #ifdef RDB_DEBUG_PATCH
1892 debug((27, "WM_EXITSIZEMOVE"));
1894 if (need_backend_resize
) {
1895 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1896 InvalidateRect(hwnd
, NULL
, TRUE
);
1901 * This does two jobs:
1902 * 1) Keep the sizetip uptodate
1903 * 2) Make sure the window size is _stepped_ in units of the font size.
1905 if (!cfg
.locksize
&& !alt_pressed
) {
1906 int width
, height
, w
, h
, ew
, eh
;
1907 LPRECT r
= (LPRECT
) lParam
;
1909 if ( !need_backend_resize
&&
1910 (cfg
.height
!= rows
|| cfg
.width
!= cols
)) {
1912 * Great! It seems the host has been changing the terminal
1913 * size, well the user is now grabbing so this is probably
1914 * the least confusing solution in the long run even though
1915 * it a is suprise. Unfortunatly the only way to prevent
1916 * this seems to be to let the host change the window size
1917 * and as that's a user option we're still right back here.
1919 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1921 InvalidateRect(hwnd
, NULL
, TRUE
);
1922 need_backend_resize
= TRUE
;
1925 width
= r
->right
- r
->left
- extra_width
;
1926 height
= r
->bottom
- r
->top
- extra_height
;
1927 w
= (width
+ font_width
/ 2) / font_width
;
1930 h
= (height
+ font_height
/ 2) / font_height
;
1933 UpdateSizeTip(hwnd
, w
, h
);
1934 ew
= width
- w
* font_width
;
1935 eh
= height
- h
* font_height
;
1937 if (wParam
== WMSZ_LEFT
||
1938 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
1944 if (wParam
== WMSZ_TOP
||
1945 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
1955 int width
, height
, w
, h
, rv
= 0;
1956 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
1957 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
1958 LPRECT r
= (LPRECT
) lParam
;
1960 width
= r
->right
- r
->left
- ex_width
;
1961 height
= r
->bottom
- r
->top
- ex_height
;
1963 w
= (width
+ cols
/2)/cols
;
1964 h
= (height
+ rows
/2)/rows
;
1965 if ( r
->right
!= r
->left
+ w
*cols
+ ex_width
)
1968 if (wParam
== WMSZ_LEFT
||
1969 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
1970 r
->left
= r
->right
- w
*cols
- ex_width
;
1972 r
->right
= r
->left
+ w
*cols
+ ex_width
;
1974 if (r
->bottom
!= r
->top
+ h
*rows
+ ex_height
)
1977 if (wParam
== WMSZ_TOP
||
1978 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
1979 r
->top
= r
->bottom
- h
*rows
- ex_height
;
1981 r
->bottom
= r
->top
+ h
*rows
+ ex_height
;
1986 /* break; (never reached) */
1988 #ifdef RDB_DEBUG_PATCH
1989 debug((27, "WM_SIZE %s (%d,%d)",
1990 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
1991 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
1992 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
1993 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
1995 LOWORD(lParam
), HIWORD(lParam
)));
1997 if (wParam
== SIZE_MINIMIZED
) {
1999 cfg
.win_name_always ? window_name
: icon_name
);
2002 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2003 SetWindowText(hwnd
, window_name
);
2005 if (cfg
.lockfont
&& cfg
.locksize
) {
2006 /* A resize, well it better be a minimize. */
2010 int width
, height
, w
, h
;
2012 width
= LOWORD(lParam
);
2013 height
= HIWORD(lParam
);
2016 if (wParam
== SIZE_MAXIMIZED
) {
2021 w
= width
/ font_width
;
2023 h
= height
/ font_height
;
2026 term_size(h
, w
, cfg
.savelines
);
2029 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2032 term_size(prev_rows
, prev_cols
, cfg
.savelines
);
2035 /* This is an unexpected resize, these will normally happen
2036 * if the window is too large. Probably either the user
2037 * selected a huge font or the screen size has changed.
2039 * This is also called with minimize.
2041 else reset_window(-1);
2045 * Don't call back->size in mid-resize. (To prevent
2046 * massive numbers of resize events getting sent
2047 * down the connection during an NT opaque drag.)
2050 if (!cfg
.locksize
&& !alt_pressed
) {
2051 need_backend_resize
= TRUE
;
2052 w
= (width
-cfg
.window_border
*2) / font_width
;
2054 h
= (height
-cfg
.window_border
*2) / font_height
;
2065 switch (LOWORD(wParam
)) {
2079 term_scroll(0, +rows
/ 2);
2082 term_scroll(0, -rows
/ 2);
2084 case SB_THUMBPOSITION
:
2086 term_scroll(1, HIWORD(wParam
));
2090 case WM_PALETTECHANGED
:
2091 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2092 HDC hdc
= get_ctx();
2094 if (RealizePalette(hdc
) > 0)
2100 case WM_QUERYNEWPALETTE
:
2102 HDC hdc
= get_ctx();
2104 if (RealizePalette(hdc
) > 0)
2116 * Add the scan code and keypress timing to the random
2119 noise_ultralight(lParam
);
2122 * We don't do TranslateMessage since it disassociates the
2123 * resulting CHAR message from the KEYDOWN that sparked it,
2124 * which we occasionally don't want. Instead, we process
2125 * KEYDOWN, and call the Win32 translator functions so that
2126 * we get the translations under _our_ control.
2129 unsigned char buf
[20];
2132 if (wParam
== VK_PROCESSKEY
) {
2135 m
.message
= WM_KEYDOWN
;
2137 m
.lParam
= lParam
& 0xdfff;
2138 TranslateMessage(&m
);
2140 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2142 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2146 * Interrupt an ongoing paste. I'm not sure
2147 * this is sensible, but for the moment it's
2148 * preferable to having to faff about buffering
2154 * We need not bother about stdin backlogs
2155 * here, because in GUI PuTTY we can't do
2156 * anything about it anyway; there's no means
2157 * of asking Windows to hold off on KEYDOWN
2158 * messages. We _have_ to buffer everything
2161 ldisc_send(buf
, len
, 1);
2167 case WM_INPUTLANGCHANGE
:
2169 /* wParam == Font number */
2170 /* lParam == Locale */
2172 HKL NewInputLocale
= (HKL
) lParam
;
2174 // lParam == GetKeyboardLayout(0);
2176 GetLocaleInfo(LOWORD(NewInputLocale
),
2177 LOCALE_IDEFAULTANSICODEPAGE
, lbuf
, sizeof(lbuf
));
2179 kbd_codepage
= atoi(lbuf
);
2182 case WM_IME_COMPOSITION
:
2188 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2189 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2191 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2192 break; /* fall back to DefWindowProc */
2194 hIMC
= ImmGetContext(hwnd
);
2195 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2198 buff
= (char*) smalloc(n
);
2199 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2200 luni_send((unsigned short *)buff
, n
/ 2, 1);
2203 ImmReleaseContext(hwnd
, hIMC
);
2208 if (wParam
& 0xFF00) {
2209 unsigned char buf
[2];
2212 buf
[0] = wParam
>> 8;
2213 lpage_send(kbd_codepage
, buf
, 2, 1);
2215 char c
= (unsigned char) wParam
;
2216 lpage_send(kbd_codepage
, &c
, 1, 1);
2222 * Nevertheless, we are prepared to deal with WM_CHAR
2223 * messages, should they crop up. So if someone wants to
2224 * post the things to us as part of a macro manoeuvre,
2225 * we're ready to cope.
2228 char c
= (unsigned char)wParam
;
2229 lpage_send(CP_ACP
, &c
, 1, 1);
2233 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2234 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2239 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2243 * Move the system caret. (We maintain one, even though it's
2244 * invisible, for the benefit of blind people: apparently some
2245 * helper software tracks the system caret, so we should arrange to
2248 void sys_cursor(int x
, int y
)
2253 if (!has_focus
) return;
2255 SetCaretPos(x
* font_width
+ offset_width
,
2256 y
* font_height
+ offset_height
);
2258 /* IMM calls on Win98 and beyond only */
2259 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2261 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2262 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2264 /* we should have the IMM functions */
2265 hIMC
= ImmGetContext(hwnd
);
2266 cf
.dwStyle
= CFS_POINT
;
2267 cf
.ptCurrentPos
.x
= x
* font_width
;
2268 cf
.ptCurrentPos
.y
= y
* font_height
;
2269 ImmSetCompositionWindow(hIMC
, &cf
);
2271 ImmReleaseContext(hwnd
, hIMC
);
2275 * Draw a line of text in the window, at given character
2276 * coordinates, in given attributes.
2278 * We are allowed to fiddle with the contents of `text'.
2280 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2281 unsigned long attr
, int lattr
)
2284 int nfg
, nbg
, nfont
;
2287 int force_manual_underline
= 0;
2288 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2289 int char_width
= fnt_width
;
2290 int text_adjust
= 0;
2291 static int *IpDx
= 0, IpDxLEN
= 0;
2293 if (attr
& ATTR_WIDE
)
2296 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2298 if (len
> IpDxLEN
) {
2300 IpDx
= smalloc((len
+ 16) * sizeof(int));
2301 IpDxLEN
= (len
+ 16);
2303 for (i
= 0; i
< IpDxLEN
; i
++)
2304 IpDx
[i
] = char_width
;
2307 /* Only want the left half of double width lines */
2308 if (lattr
!= LATTR_NORM
&& x
*2 >= cols
)
2316 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
2317 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2318 attr
^= ATTR_CUR_XOR
;
2322 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2323 /* Assume a poorman font is borken in other ways too. */
2333 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2336 if (attr
& ATTR_NARROW
)
2337 nfont
|= FONT_NARROW
;
2339 /* Special hack for the VT100 linedraw glyphs. */
2340 if ((attr
& CSET_MASK
) == 0x2300) {
2341 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2342 switch ((unsigned char) (text
[0])) {
2344 text_adjust
= -2 * font_height
/ 5;
2347 text_adjust
= -1 * font_height
/ 5;
2350 text_adjust
= font_height
/ 5;
2353 text_adjust
= 2 * font_height
/ 5;
2356 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2359 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2360 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2361 if (attr
& ATTR_UNDER
) {
2362 attr
&= ~ATTR_UNDER
;
2363 force_manual_underline
= 1;
2368 /* Anything left as an original character set is unprintable. */
2369 if (DIRECT_CHAR(attr
)) {
2372 memset(text
, 0xFD, len
);
2376 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2379 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2380 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2381 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2383 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2384 nfont
|= FONT_UNDERLINE
;
2385 another_font(nfont
);
2386 if (!fonts
[nfont
]) {
2387 if (nfont
& FONT_UNDERLINE
)
2388 force_manual_underline
= 1;
2389 /* Don't do the same for manual bold, it could be bad news. */
2391 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2393 another_font(nfont
);
2395 nfont
= FONT_NORMAL
;
2396 if (attr
& ATTR_REVERSE
) {
2401 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2403 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2407 SelectObject(hdc
, fonts
[nfont
]);
2408 SetTextColor(hdc
, fg
);
2409 SetBkColor(hdc
, bg
);
2410 SetBkMode(hdc
, OPAQUE
);
2413 line_box
.right
= x
+ char_width
* len
;
2414 line_box
.bottom
= y
+ font_height
;
2416 /* Only want the left half of double width lines */
2417 if (line_box
.right
> font_width
*cols
+offset_width
)
2418 line_box
.right
= font_width
*cols
+offset_width
;
2420 /* We're using a private area for direct to font. (512 chars.) */
2421 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2422 /* Ho Hum, dbcs fonts are a PITA! */
2423 /* To display on W9x I have to convert to UCS */
2424 static wchar_t *uni_buf
= 0;
2425 static int uni_len
= 0;
2427 if (len
> uni_len
) {
2429 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2432 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2433 uni_buf
[nlen
] = 0xFFFD;
2434 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2435 IpDx
[nlen
] += char_width
;
2436 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2437 text
+mptr
, 2, uni_buf
+nlen
, 1);
2442 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2443 text
+mptr
, 1, uni_buf
+nlen
, 1);
2451 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2452 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2453 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2454 SetBkMode(hdc
, TRANSPARENT
);
2455 ExtTextOutW(hdc
, x
- 1,
2456 y
- font_height
* (lattr
==
2457 LATTR_BOT
) + text_adjust
,
2458 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2462 } else if (DIRECT_FONT(attr
)) {
2464 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2465 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2466 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2467 SetBkMode(hdc
, TRANSPARENT
);
2469 /* GRR: This draws the character outside it's box and can leave
2470 * 'droppings' even with the clip box! I suppose I could loop it
2471 * one character at a time ... yuk.
2473 * Or ... I could do a test print with "W", and use +1 or -1 for this
2474 * shift depending on if the leftmost column is blank...
2476 ExtTextOut(hdc
, x
- 1,
2477 y
- font_height
* (lattr
==
2478 LATTR_BOT
) + text_adjust
,
2479 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2482 /* And 'normal' unicode characters */
2483 static WCHAR
*wbuf
= NULL
;
2484 static int wlen
= 0;
2489 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2491 for (i
= 0; i
< len
; i
++)
2492 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2495 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2496 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2498 /* And the shadow bold hack. */
2499 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2500 SetBkMode(hdc
, TRANSPARENT
);
2501 ExtTextOutW(hdc
, x
- 1,
2502 y
- font_height
* (lattr
==
2503 LATTR_BOT
) + text_adjust
,
2504 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2507 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2508 (und_mode
== UND_LINE
2509 && (attr
& ATTR_UNDER
)))) {
2512 if (lattr
== LATTR_BOT
)
2513 dec
= dec
* 2 - font_height
;
2515 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2516 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2517 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2518 oldpen
= SelectObject(hdc
, oldpen
);
2519 DeleteObject(oldpen
);
2523 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2524 unsigned long attr
, int lattr
)
2530 int ctype
= cfg
.cursor_type
;
2532 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2533 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2534 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2538 attr
|= TATTR_RIGHTCURS
;
2541 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2542 if (attr
& ATTR_WIDE
)
2549 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2552 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2553 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2554 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2555 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2556 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2557 Polyline(hdc
, pts
, 5);
2558 oldpen
= SelectObject(hdc
, oldpen
);
2559 DeleteObject(oldpen
);
2560 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2561 int startx
, starty
, dx
, dy
, length
, i
;
2564 starty
= y
+ descent
;
2567 length
= char_width
;
2570 if (attr
& TATTR_RIGHTCURS
)
2571 xadjust
= char_width
- 1;
2572 startx
= x
+ xadjust
;
2576 length
= font_height
;
2578 if (attr
& TATTR_ACTCURS
) {
2581 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2582 MoveToEx(hdc
, startx
, starty
, NULL
);
2583 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2584 oldpen
= SelectObject(hdc
, oldpen
);
2585 DeleteObject(oldpen
);
2587 for (i
= 0; i
< length
; i
++) {
2589 SetPixel(hdc
, startx
, starty
, colours
[23]);
2598 /* This function gets the actual width of a character in the normal font.
2600 int CharWidth(Context ctx
, int uc
) {
2604 /* If the font max is the same as the font ave width then this
2605 * function is a no-op.
2607 if (!font_dualwidth
) return 1;
2609 switch (uc
& CSET_MASK
) {
2611 uc
= unitab_line
[uc
& 0xFF];
2614 uc
= unitab_xterm
[uc
& 0xFF];
2617 uc
= unitab_scoacs
[uc
& 0xFF];
2620 if (DIRECT_FONT(uc
)) {
2621 if (dbcs_screenfont
) return 1;
2623 /* Speedup, I know of no font where ascii is the wrong width */
2624 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
2627 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
2628 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2629 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
2630 another_font(FONT_OEM
);
2631 if (!fonts
[FONT_OEM
]) return 0;
2633 SelectObject(hdc
, fonts
[FONT_OEM
]);
2637 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
2638 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
2641 /* Speedup, I know of no font where ascii is the wrong width */
2642 if (uc
>= ' ' && uc
<= '~') return 1;
2644 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2645 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
2646 /* Okay that one worked */ ;
2647 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
2648 /* This should work on 9x too, but it's "less accurate" */ ;
2653 ibuf
+= font_width
/ 2 -1;
2660 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2661 * codes. Returns number of bytes used or zero to drop the message
2662 * or -1 to forward the message to windows.
2664 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2665 unsigned char *output
)
2668 int scan
, left_alt
= 0, key_down
, shift_state
;
2670 unsigned char *p
= output
;
2671 static int alt_sum
= 0;
2673 HKL kbd_layout
= GetKeyboardLayout(0);
2675 static WORD keys
[3];
2676 static int compose_char
= 0;
2677 static WPARAM compose_key
= 0;
2679 r
= GetKeyboardState(keystate
);
2681 memset(keystate
, 0, sizeof(keystate
));
2684 #define SHOW_TOASCII_RESULT
2685 { /* Tell us all about key events */
2686 static BYTE oldstate
[256];
2687 static int first
= 1;
2691 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2694 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2696 } else if ((HIWORD(lParam
) & KF_UP
)
2697 && scan
== (HIWORD(lParam
) & 0xFF)) {
2701 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2702 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2715 debug(("VK_%02x", wParam
));
2717 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2719 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2721 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2722 if (ch
>= ' ' && ch
<= '~')
2723 debug((", '%c'", ch
));
2725 debug((", $%02x", ch
));
2728 debug((", KB0=%02x", keys
[0]));
2730 debug((", KB1=%02x", keys
[1]));
2732 debug((", KB2=%02x", keys
[2]));
2734 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2736 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2738 if ((HIWORD(lParam
) & KF_EXTENDED
))
2740 if ((HIWORD(lParam
) & KF_UP
))
2744 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2745 else if ((HIWORD(lParam
) & KF_UP
))
2746 oldstate
[wParam
& 0xFF] ^= 0x80;
2748 oldstate
[wParam
& 0xFF] ^= 0x81;
2750 for (ch
= 0; ch
< 256; ch
++)
2751 if (oldstate
[ch
] != keystate
[ch
])
2752 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2754 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2758 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2759 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2763 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2764 if ((cfg
.funky_type
== 3 ||
2765 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2766 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2768 wParam
= VK_EXECUTE
;
2770 /* UnToggle NUMLock */
2771 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2772 keystate
[VK_NUMLOCK
] ^= 1;
2775 /* And write back the 'adjusted' state */
2776 SetKeyboardState(keystate
);
2779 /* Disable Auto repeat if required */
2780 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2783 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2786 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2788 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2789 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2790 if (cfg
.ctrlaltkeys
)
2791 keystate
[VK_MENU
] = 0;
2793 keystate
[VK_RMENU
] = 0x80;
2798 alt_pressed
= (left_alt
&& key_down
);
2800 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2801 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2802 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2804 /* Note if AltGr was pressed and if it was used as a compose key */
2805 if (!compose_state
) {
2806 compose_key
= 0x100;
2807 if (cfg
.compose_key
) {
2808 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2809 compose_key
= wParam
;
2811 if (wParam
== VK_APPS
)
2812 compose_key
= wParam
;
2815 if (wParam
== compose_key
) {
2816 if (compose_state
== 0
2817 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2819 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2823 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
2827 * Record that we pressed key so the scroll window can be reset, but
2828 * be careful to avoid Shift-UP/Down
2830 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
2834 if (compose_state
> 1 && left_alt
)
2837 /* Sanitize the number pad if not using a PC NumPad */
2838 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2839 && cfg
.funky_type
!= 2)
2840 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
2841 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
2845 nParam
= VK_NUMPAD0
;
2848 nParam
= VK_NUMPAD1
;
2851 nParam
= VK_NUMPAD2
;
2854 nParam
= VK_NUMPAD3
;
2857 nParam
= VK_NUMPAD4
;
2860 nParam
= VK_NUMPAD5
;
2863 nParam
= VK_NUMPAD6
;
2866 nParam
= VK_NUMPAD7
;
2869 nParam
= VK_NUMPAD8
;
2872 nParam
= VK_NUMPAD9
;
2875 nParam
= VK_DECIMAL
;
2879 if (keystate
[VK_NUMLOCK
] & 1)
2886 /* If a key is pressed and AltGr is not active */
2887 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
2888 /* Okay, prepare for most alts then ... */
2892 /* Lets see if it's a pattern we know all about ... */
2893 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2894 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2897 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2898 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
2901 if (wParam
== VK_INSERT
&& shift_state
== 1) {
2905 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
2908 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
2909 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
2912 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
) {
2916 /* Control-Numlock for app-keypad mode switch */
2917 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
2918 app_keypad_keys
^= 1;
2922 /* Nethack keypad */
2923 if (cfg
.nethack_keypad
&& !left_alt
) {
2926 *p
++ = shift_state ?
'B' : 'b';
2929 *p
++ = shift_state ?
'J' : 'j';
2932 *p
++ = shift_state ?
'N' : 'n';
2935 *p
++ = shift_state ?
'H' : 'h';
2938 *p
++ = shift_state ?
'.' : '.';
2941 *p
++ = shift_state ?
'L' : 'l';
2944 *p
++ = shift_state ?
'Y' : 'y';
2947 *p
++ = shift_state ?
'K' : 'k';
2950 *p
++ = shift_state ?
'U' : 'u';
2955 /* Application Keypad */
2959 if (cfg
.funky_type
== 3 ||
2960 (cfg
.funky_type
<= 1 &&
2961 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
2975 if (app_keypad_keys
&& !cfg
.no_applic_k
)
3012 if (cfg
.funky_type
== 2) {
3017 } else if (shift_state
)
3024 if (cfg
.funky_type
== 2)
3028 if (cfg
.funky_type
== 2)
3032 if (cfg
.funky_type
== 2)
3037 if (HIWORD(lParam
) & KF_EXTENDED
)
3043 if (xkey
>= 'P' && xkey
<= 'S')
3044 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3046 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3048 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3053 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3054 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3058 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3064 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3068 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3072 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3077 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3082 /* Control-2 to Control-8 are special */
3083 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3084 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3087 if (shift_state
== 2 && wParam
== 0xBD) {
3091 if (shift_state
== 2 && wParam
== 0xDF) {
3095 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
3102 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3103 * for integer decimal nn.)
3105 * We also deal with the weird ones here. Linux VCs replace F1
3106 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3107 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3113 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3116 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3119 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3122 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3125 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3128 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3131 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3134 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3137 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3140 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3173 if ((shift_state
&2) == 0) switch (wParam
) {
3193 /* Reorder edit keys to physical order */
3194 if (cfg
.funky_type
== 3 && code
<= 6)
3195 code
= "\0\2\1\4\5\3\6"[code
];
3197 if (vt52_mode
&& code
> 0 && code
<= 6) {
3198 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3202 if (cfg
.funky_type
== 5 && /* SCO function keys */
3203 code
>= 11 && code
<= 34) {
3204 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3207 case VK_F1
: index
= 0; break;
3208 case VK_F2
: index
= 1; break;
3209 case VK_F3
: index
= 2; break;
3210 case VK_F4
: index
= 3; break;
3211 case VK_F5
: index
= 4; break;
3212 case VK_F6
: index
= 5; break;
3213 case VK_F7
: index
= 6; break;
3214 case VK_F8
: index
= 7; break;
3215 case VK_F9
: index
= 8; break;
3216 case VK_F10
: index
= 9; break;
3217 case VK_F11
: index
= 10; break;
3218 case VK_F12
: index
= 11; break;
3220 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3221 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3222 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3225 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3226 code
>= 1 && code
<= 6) {
3227 char codes
[] = "HL.FIG";
3231 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3235 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3242 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3245 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3248 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3249 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3252 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3254 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3256 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3259 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3260 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3264 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3269 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3270 * some reason seems to send VK_CLEAR to Windows...).
3293 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3295 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
3296 /* VT100 & VT102 manuals both state the app cursor keys
3297 * only work if the app keypad is on.
3299 if (!app_keypad_keys
)
3301 /* Useful mapping of Ctrl-arrows */
3302 if (shift_state
== 2)
3306 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3308 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3315 * Finally, deal with Return ourselves. (Win95 seems to
3316 * foul it up when Alt is pressed, for some reason.)
3318 if (wParam
== VK_RETURN
) { /* Return */
3324 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3325 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3330 /* Okay we've done everything interesting; let windows deal with
3331 * the boring stuff */
3335 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3336 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3338 keystate
[VK_CAPITAL
] = 0;
3341 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3342 #ifdef SHOW_TOASCII_RESULT
3343 if (r
== 1 && !key_down
) {
3345 if (in_utf
|| dbcs_screenfont
)
3346 debug((", (U+%04x)", alt_sum
));
3348 debug((", LCH(%d)", alt_sum
));
3350 debug((", ACH(%d)", keys
[0]));
3355 for (r1
= 0; r1
< r
; r1
++) {
3356 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3365 * Interrupt an ongoing paste. I'm not sure this is
3366 * sensible, but for the moment it's preferable to
3367 * having to faff about buffering things.
3372 for (i
= 0; i
< r
; i
++) {
3373 unsigned char ch
= (unsigned char) keys
[i
];
3375 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3380 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3384 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3385 MessageBeep(MB_ICONHAND
);
3389 luni_send(&keybuf
, 1, 1);
3397 if (in_utf
|| dbcs_screenfont
) {
3399 luni_send(&keybuf
, 1, 1);
3401 ch
= (char) alt_sum
;
3403 * We need not bother about stdin
3404 * backlogs here, because in GUI PuTTY
3405 * we can't do anything about it
3406 * anyway; there's no means of asking
3407 * Windows to hold off on KEYDOWN
3408 * messages. We _have_ to buffer
3409 * everything we're sent.
3411 ldisc_send(&ch
, 1, 1);
3415 lpage_send(kbd_codepage
, &ch
, 1, 1);
3417 if(capsOn
&& ch
< 0x80) {
3420 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3421 luni_send(cbuf
+!left_alt
, 1+!!left_alt
, 1);
3426 lpage_send(kbd_codepage
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3432 /* This is so the ALT-Numpad and dead keys work correctly. */
3437 /* If we're definitly not building up an ALT-54321 then clear it */
3440 /* If we will be using alt_sum fix the 256s */
3441 else if (keys
[0] && (in_utf
|| dbcs_screenfont
))
3446 * ALT alone may or may not want to bring up the System menu.
3447 * If it's not meant to, we return 0 on presses or releases of
3448 * ALT, to show that we've swallowed the keystroke. Otherwise
3449 * we return -1, which means Windows will give the keystroke
3450 * its default handling (i.e. bring up the System menu).
3452 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3458 void set_title(char *title
)
3461 window_name
= smalloc(1 + strlen(title
));
3462 strcpy(window_name
, title
);
3463 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3464 SetWindowText(hwnd
, title
);
3467 void set_icon(char *title
)
3470 icon_name
= smalloc(1 + strlen(title
));
3471 strcpy(icon_name
, title
);
3472 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3473 SetWindowText(hwnd
, title
);
3476 void set_sbar(int total
, int start
, int page
)
3483 si
.cbSize
= sizeof(si
);
3484 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3486 si
.nMax
= total
- 1;
3490 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3493 Context
get_ctx(void)
3499 SelectPalette(hdc
, pal
, FALSE
);
3505 void free_ctx(Context ctx
)
3507 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3508 ReleaseDC(hwnd
, ctx
);
3511 static void real_palette_set(int n
, int r
, int g
, int b
)
3514 logpal
->palPalEntry
[n
].peRed
= r
;
3515 logpal
->palPalEntry
[n
].peGreen
= g
;
3516 logpal
->palPalEntry
[n
].peBlue
= b
;
3517 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3518 colours
[n
] = PALETTERGB(r
, g
, b
);
3519 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3521 colours
[n
] = RGB(r
, g
, b
);
3524 void palette_set(int n
, int r
, int g
, int b
)
3526 static const int first
[21] = {
3527 0, 2, 4, 6, 8, 10, 12, 14,
3528 1, 3, 5, 7, 9, 11, 13, 15,
3531 real_palette_set(first
[n
], r
, g
, b
);
3533 real_palette_set(first
[n
] + 1, r
, g
, b
);
3535 HDC hdc
= get_ctx();
3536 UnrealizeObject(pal
);
3537 RealizePalette(hdc
);
3542 void palette_reset(void)
3546 for (i
= 0; i
< NCOLOURS
; i
++) {
3548 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3549 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3550 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3551 logpal
->palPalEntry
[i
].peFlags
= 0;
3552 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3553 defpal
[i
].rgbtGreen
,
3554 defpal
[i
].rgbtBlue
);
3556 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3557 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3562 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3564 RealizePalette(hdc
);
3569 void write_aclip(char *data
, int len
, int must_deselect
)
3574 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3577 lock
= GlobalLock(clipdata
);
3580 memcpy(lock
, data
, len
);
3581 ((unsigned char *) lock
)[len
] = 0;
3582 GlobalUnlock(clipdata
);
3585 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3587 if (OpenClipboard(hwnd
)) {
3589 SetClipboardData(CF_TEXT
, clipdata
);
3592 GlobalFree(clipdata
);
3595 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3599 * Note: unlike write_aclip() this will not append a nul.
3601 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3608 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3610 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3611 len
* sizeof(wchar_t));
3612 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3614 if (!clipdata
|| !clipdata2
) {
3616 GlobalFree(clipdata
);
3618 GlobalFree(clipdata2
);
3621 if (!(lock
= GlobalLock(clipdata
)))
3623 if (!(lock2
= GlobalLock(clipdata2
)))
3626 memcpy(lock
, data
, len
* sizeof(wchar_t));
3627 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3629 GlobalUnlock(clipdata
);
3630 GlobalUnlock(clipdata2
);
3633 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3635 if (OpenClipboard(hwnd
)) {
3637 SetClipboardData(CF_UNICODETEXT
, clipdata
);
3638 SetClipboardData(CF_TEXT
, clipdata2
);
3641 GlobalFree(clipdata
);
3642 GlobalFree(clipdata2
);
3646 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3649 void get_clip(wchar_t ** p
, int *len
)
3651 static HGLOBAL clipdata
= NULL
;
3652 static wchar_t *converted
= 0;
3661 GlobalUnlock(clipdata
);
3664 } else if (OpenClipboard(NULL
)) {
3665 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
3667 *p
= GlobalLock(clipdata
);
3669 for (p2
= *p
; *p2
; p2
++);
3673 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
3677 s
= GlobalLock(clipdata
);
3678 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
3679 *p
= converted
= smalloc(i
* sizeof(wchar_t));
3680 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
3693 * Move `lines' lines from position `from' to position `to' in the
3696 void optimised_move(int to
, int from
, int lines
)
3701 min
= (to
< from ? to
: from
);
3702 max
= to
+ from
- min
;
3704 r
.left
= offset_width
;
3705 r
.right
= offset_width
+ cols
* font_width
;
3706 r
.top
= offset_height
+ min
* font_height
;
3707 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
3708 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
3713 * Print a message box and perform a fatal exit.
3715 void fatalbox(char *fmt
, ...)
3721 vsprintf(stuff
, fmt
, ap
);
3723 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
3728 * Manage window caption / taskbar flashing, if enabled.
3729 * 0 = stop, 1 = maintain, 2 = start
3731 static void flash_window(int mode
)
3733 static long last_flash
= 0;
3734 static int flashing
= 0;
3735 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
3738 FlashWindow(hwnd
, FALSE
);
3742 } else if (mode
== 2) {
3745 last_flash
= GetTickCount();
3747 FlashWindow(hwnd
, TRUE
);
3750 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
3753 long now
= GetTickCount();
3754 long fdiff
= now
- last_flash
;
3755 if (fdiff
< 0 || fdiff
> 450) {
3757 FlashWindow(hwnd
, TRUE
); /* toggle */
3768 if (mode
== BELL_DEFAULT
) {
3770 * For MessageBeep style bells, we want to be careful of
3771 * timing, because they don't have the nice property of
3772 * PlaySound bells that each one cancels the previous
3773 * active one. So we limit the rate to one per 50ms or so.
3775 static long lastbeep
= 0;
3778 beepdiff
= GetTickCount() - lastbeep
;
3779 if (beepdiff
>= 0 && beepdiff
< 50)
3783 * The above MessageBeep call takes time, so we record the
3784 * time _after_ it finishes rather than before it starts.
3786 lastbeep
= GetTickCount();
3787 } else if (mode
== BELL_WAVEFILE
) {
3788 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
3789 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
3790 sprintf(buf
, "Unable to play sound file\n%s\n"
3791 "Using default sound instead", cfg
.bell_wavefile
);
3792 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
3793 MB_OK
| MB_ICONEXCLAMATION
);
3794 cfg
.beep
= BELL_DEFAULT
;
3797 /* Otherwise, either visual bell or disabled; do nothing here */
3799 flash_window(2); /* start */
3804 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3807 static void flip_full_screen(void)
3812 cx
= GetSystemMetrics(SM_CXSCREEN
);
3813 cy
= GetSystemMetrics(SM_CYSCREEN
);
3814 GetWindowPlacement(hwnd
, &old_wind_placement
);
3815 old_wind_style
= GetWindowLong(hwnd
, GWL_STYLE
);
3816 SetWindowLong(hwnd
, GWL_STYLE
,
3817 old_wind_style
& ~(WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
));
3818 SetWindowPos(hwnd
, HWND_TOP
, 0, 0, cx
, cy
, SWP_SHOWWINDOW
);
3821 SetWindowLong(hwnd
, GWL_STYLE
, old_wind_style
);
3822 SetWindowPlacement(hwnd
,&old_wind_placement
);