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);
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 * We need not bother about stdin backlogs
2147 * here, because in GUI PuTTY we can't do
2148 * anything about it anyway; there's no means
2149 * of asking Windows to hold off on KEYDOWN
2150 * messages. We _have_ to buffer everything
2153 ldisc_send(buf
, len
);
2159 case WM_INPUTLANGCHANGE
:
2161 /* wParam == Font number */
2162 /* lParam == Locale */
2164 HKL NewInputLocale
= (HKL
) lParam
;
2166 // lParam == GetKeyboardLayout(0);
2168 GetLocaleInfo(LOWORD(NewInputLocale
),
2169 LOCALE_IDEFAULTANSICODEPAGE
, lbuf
, sizeof(lbuf
));
2171 kbd_codepage
= atoi(lbuf
);
2174 case WM_IME_COMPOSITION
:
2180 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2181 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2183 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2184 break; /* fall back to DefWindowProc */
2186 hIMC
= ImmGetContext(hwnd
);
2187 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2190 buff
= (char*) smalloc(n
);
2191 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2192 luni_send((unsigned short *)buff
, n
/ 2);
2195 ImmReleaseContext(hwnd
, hIMC
);
2200 if (wParam
& 0xFF00) {
2201 unsigned char buf
[2];
2204 buf
[0] = wParam
>> 8;
2205 lpage_send(kbd_codepage
, buf
, 2);
2207 char c
= (unsigned char) wParam
;
2208 lpage_send(kbd_codepage
, &c
, 1);
2214 * Nevertheless, we are prepared to deal with WM_CHAR
2215 * messages, should they crop up. So if someone wants to
2216 * post the things to us as part of a macro manoeuvre,
2217 * we're ready to cope.
2220 char c
= (unsigned char)wParam
;
2221 lpage_send(CP_ACP
, &c
, 1);
2225 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2226 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2231 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2235 * Move the system caret. (We maintain one, even though it's
2236 * invisible, for the benefit of blind people: apparently some
2237 * helper software tracks the system caret, so we should arrange to
2240 void sys_cursor(int x
, int y
)
2245 if (!has_focus
) return;
2247 SetCaretPos(x
* font_width
+ offset_width
,
2248 y
* font_height
+ offset_height
);
2250 /* IMM calls on Win98 and beyond only */
2251 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2253 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2254 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2256 /* we should have the IMM functions */
2257 hIMC
= ImmGetContext(hwnd
);
2258 cf
.dwStyle
= CFS_POINT
;
2259 cf
.ptCurrentPos
.x
= x
* font_width
;
2260 cf
.ptCurrentPos
.y
= y
* font_height
;
2261 ImmSetCompositionWindow(hIMC
, &cf
);
2263 ImmReleaseContext(hwnd
, hIMC
);
2267 * Draw a line of text in the window, at given character
2268 * coordinates, in given attributes.
2270 * We are allowed to fiddle with the contents of `text'.
2272 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2273 unsigned long attr
, int lattr
)
2276 int nfg
, nbg
, nfont
;
2279 int force_manual_underline
= 0;
2280 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2281 int char_width
= fnt_width
;
2282 int text_adjust
= 0;
2283 static int *IpDx
= 0, IpDxLEN
= 0;
2285 if (attr
& ATTR_WIDE
)
2288 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2290 if (len
> IpDxLEN
) {
2292 IpDx
= smalloc((len
+ 16) * sizeof(int));
2293 IpDxLEN
= (len
+ 16);
2295 for (i
= 0; i
< IpDxLEN
; i
++)
2296 IpDx
[i
] = char_width
;
2299 /* Only want the left half of double width lines */
2300 if (lattr
!= LATTR_NORM
&& x
*2 >= cols
)
2308 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
2309 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2310 attr
^= ATTR_CUR_XOR
;
2314 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2315 /* Assume a poorman font is borken in other ways too. */
2325 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2328 if (attr
& ATTR_NARROW
)
2329 nfont
|= FONT_NARROW
;
2331 /* Special hack for the VT100 linedraw glyphs. */
2332 if ((attr
& CSET_MASK
) == 0x2300) {
2333 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2334 switch ((unsigned char) (text
[0])) {
2336 text_adjust
= -2 * font_height
/ 5;
2339 text_adjust
= -1 * font_height
/ 5;
2342 text_adjust
= font_height
/ 5;
2345 text_adjust
= 2 * font_height
/ 5;
2348 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2351 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2352 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2353 if (attr
& ATTR_UNDER
) {
2354 attr
&= ~ATTR_UNDER
;
2355 force_manual_underline
= 1;
2360 /* Anything left as an original character set is unprintable. */
2361 if (DIRECT_CHAR(attr
)) {
2364 memset(text
, 0xFD, len
);
2368 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2371 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2372 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2373 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2375 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2376 nfont
|= FONT_UNDERLINE
;
2377 another_font(nfont
);
2378 if (!fonts
[nfont
]) {
2379 if (nfont
& FONT_UNDERLINE
)
2380 force_manual_underline
= 1;
2381 /* Don't do the same for manual bold, it could be bad news. */
2383 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2385 another_font(nfont
);
2387 nfont
= FONT_NORMAL
;
2388 if (attr
& ATTR_REVERSE
) {
2393 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2395 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2399 SelectObject(hdc
, fonts
[nfont
]);
2400 SetTextColor(hdc
, fg
);
2401 SetBkColor(hdc
, bg
);
2402 SetBkMode(hdc
, OPAQUE
);
2405 line_box
.right
= x
+ char_width
* len
;
2406 line_box
.bottom
= y
+ font_height
;
2408 /* Only want the left half of double width lines */
2409 if (line_box
.right
> font_width
*cols
+offset_width
)
2410 line_box
.right
= font_width
*cols
+offset_width
;
2412 /* We're using a private area for direct to font. (512 chars.) */
2413 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2414 /* Ho Hum, dbcs fonts are a PITA! */
2415 /* To display on W9x I have to convert to UCS */
2416 static wchar_t *uni_buf
= 0;
2417 static int uni_len
= 0;
2419 if (len
> uni_len
) {
2421 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2424 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2425 uni_buf
[nlen
] = 0xFFFD;
2426 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2427 IpDx
[nlen
] += char_width
;
2428 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2429 text
+mptr
, 2, uni_buf
+nlen
, 1);
2434 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2435 text
+mptr
, 1, uni_buf
+nlen
, 1);
2443 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2444 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2445 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2446 SetBkMode(hdc
, TRANSPARENT
);
2447 ExtTextOutW(hdc
, x
- 1,
2448 y
- font_height
* (lattr
==
2449 LATTR_BOT
) + text_adjust
,
2450 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2454 } else if (DIRECT_FONT(attr
)) {
2456 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2457 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2458 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2459 SetBkMode(hdc
, TRANSPARENT
);
2461 /* GRR: This draws the character outside it's box and can leave
2462 * 'droppings' even with the clip box! I suppose I could loop it
2463 * one character at a time ... yuk.
2465 * Or ... I could do a test print with "W", and use +1 or -1 for this
2466 * shift depending on if the leftmost column is blank...
2468 ExtTextOut(hdc
, x
- 1,
2469 y
- font_height
* (lattr
==
2470 LATTR_BOT
) + text_adjust
,
2471 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2474 /* And 'normal' unicode characters */
2475 static WCHAR
*wbuf
= NULL
;
2476 static int wlen
= 0;
2481 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2483 for (i
= 0; i
< len
; i
++)
2484 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2487 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2488 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2490 /* And the shadow bold hack. */
2491 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2492 SetBkMode(hdc
, TRANSPARENT
);
2493 ExtTextOutW(hdc
, x
- 1,
2494 y
- font_height
* (lattr
==
2495 LATTR_BOT
) + text_adjust
,
2496 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2499 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2500 (und_mode
== UND_LINE
2501 && (attr
& ATTR_UNDER
)))) {
2504 if (lattr
== LATTR_BOT
)
2505 dec
= dec
* 2 - font_height
;
2507 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2508 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2509 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2510 oldpen
= SelectObject(hdc
, oldpen
);
2511 DeleteObject(oldpen
);
2515 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2516 unsigned long attr
, int lattr
)
2522 int ctype
= cfg
.cursor_type
;
2524 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2525 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2526 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2530 attr
|= TATTR_RIGHTCURS
;
2533 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2534 if (attr
& ATTR_WIDE
)
2541 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2544 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2545 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2546 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2547 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2548 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2549 Polyline(hdc
, pts
, 5);
2550 oldpen
= SelectObject(hdc
, oldpen
);
2551 DeleteObject(oldpen
);
2552 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2553 int startx
, starty
, dx
, dy
, length
, i
;
2556 starty
= y
+ descent
;
2559 length
= char_width
;
2562 if (attr
& TATTR_RIGHTCURS
)
2563 xadjust
= char_width
- 1;
2564 startx
= x
+ xadjust
;
2568 length
= font_height
;
2570 if (attr
& TATTR_ACTCURS
) {
2573 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2574 MoveToEx(hdc
, startx
, starty
, NULL
);
2575 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2576 oldpen
= SelectObject(hdc
, oldpen
);
2577 DeleteObject(oldpen
);
2579 for (i
= 0; i
< length
; i
++) {
2581 SetPixel(hdc
, startx
, starty
, colours
[23]);
2590 /* This function gets the actual width of a character in the normal font.
2592 int CharWidth(Context ctx
, int uc
) {
2596 /* If the font max is the same as the font ave width then this
2597 * function is a no-op.
2599 if (!font_dualwidth
) return 1;
2601 switch (uc
& CSET_MASK
) {
2603 uc
= unitab_line
[uc
& 0xFF];
2606 uc
= unitab_xterm
[uc
& 0xFF];
2609 uc
= unitab_scoacs
[uc
& 0xFF];
2612 if (DIRECT_FONT(uc
)) {
2613 if (dbcs_screenfont
) return 1;
2615 /* Speedup, I know of no font where ascii is the wrong width */
2616 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
2619 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
2620 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2621 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
2622 another_font(FONT_OEM
);
2623 if (!fonts
[FONT_OEM
]) return 0;
2625 SelectObject(hdc
, fonts
[FONT_OEM
]);
2629 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
2630 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
2633 /* Speedup, I know of no font where ascii is the wrong width */
2634 if (uc
>= ' ' && uc
<= '~') return 1;
2636 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2637 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
2638 /* Okay that one worked */ ;
2639 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
2640 /* This should work on 9x too, but it's "less accurate" */ ;
2645 ibuf
+= font_width
/ 2 -1;
2652 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2653 * codes. Returns number of bytes used or zero to drop the message
2654 * or -1 to forward the message to windows.
2656 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2657 unsigned char *output
)
2660 int scan
, left_alt
= 0, key_down
, shift_state
;
2662 unsigned char *p
= output
;
2663 static int alt_sum
= 0;
2665 HKL kbd_layout
= GetKeyboardLayout(0);
2667 static WORD keys
[3];
2668 static int compose_char
= 0;
2669 static WPARAM compose_key
= 0;
2671 r
= GetKeyboardState(keystate
);
2673 memset(keystate
, 0, sizeof(keystate
));
2676 #define SHOW_TOASCII_RESULT
2677 { /* Tell us all about key events */
2678 static BYTE oldstate
[256];
2679 static int first
= 1;
2683 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2686 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2688 } else if ((HIWORD(lParam
) & KF_UP
)
2689 && scan
== (HIWORD(lParam
) & 0xFF)) {
2693 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2694 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2707 debug(("VK_%02x", wParam
));
2709 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2711 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2713 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2714 if (ch
>= ' ' && ch
<= '~')
2715 debug((", '%c'", ch
));
2717 debug((", $%02x", ch
));
2720 debug((", KB0=%02x", keys
[0]));
2722 debug((", KB1=%02x", keys
[1]));
2724 debug((", KB2=%02x", keys
[2]));
2726 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2728 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2730 if ((HIWORD(lParam
) & KF_EXTENDED
))
2732 if ((HIWORD(lParam
) & KF_UP
))
2736 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2737 else if ((HIWORD(lParam
) & KF_UP
))
2738 oldstate
[wParam
& 0xFF] ^= 0x80;
2740 oldstate
[wParam
& 0xFF] ^= 0x81;
2742 for (ch
= 0; ch
< 256; ch
++)
2743 if (oldstate
[ch
] != keystate
[ch
])
2744 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2746 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2750 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2751 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2755 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2756 if ((cfg
.funky_type
== 3 ||
2757 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2758 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2760 wParam
= VK_EXECUTE
;
2762 /* UnToggle NUMLock */
2763 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2764 keystate
[VK_NUMLOCK
] ^= 1;
2767 /* And write back the 'adjusted' state */
2768 SetKeyboardState(keystate
);
2771 /* Disable Auto repeat if required */
2772 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2775 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2778 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2780 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2781 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2782 if (cfg
.ctrlaltkeys
)
2783 keystate
[VK_MENU
] = 0;
2785 keystate
[VK_RMENU
] = 0x80;
2790 alt_pressed
= (left_alt
&& key_down
);
2792 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2793 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2794 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2796 /* Note if AltGr was pressed and if it was used as a compose key */
2797 if (!compose_state
) {
2798 compose_key
= 0x100;
2799 if (cfg
.compose_key
) {
2800 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2801 compose_key
= wParam
;
2803 if (wParam
== VK_APPS
)
2804 compose_key
= wParam
;
2807 if (wParam
== compose_key
) {
2808 if (compose_state
== 0
2809 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2811 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2815 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
2819 * Record that we pressed key so the scroll window can be reset, but
2820 * be careful to avoid Shift-UP/Down
2822 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
2826 /* Make sure we're not pasting */
2830 if (compose_state
> 1 && left_alt
)
2833 /* Sanitize the number pad if not using a PC NumPad */
2834 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2835 && cfg
.funky_type
!= 2)
2836 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
2837 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
2841 nParam
= VK_NUMPAD0
;
2844 nParam
= VK_NUMPAD1
;
2847 nParam
= VK_NUMPAD2
;
2850 nParam
= VK_NUMPAD3
;
2853 nParam
= VK_NUMPAD4
;
2856 nParam
= VK_NUMPAD5
;
2859 nParam
= VK_NUMPAD6
;
2862 nParam
= VK_NUMPAD7
;
2865 nParam
= VK_NUMPAD8
;
2868 nParam
= VK_NUMPAD9
;
2871 nParam
= VK_DECIMAL
;
2875 if (keystate
[VK_NUMLOCK
] & 1)
2882 /* If a key is pressed and AltGr is not active */
2883 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
2884 /* Okay, prepare for most alts then ... */
2888 /* Lets see if it's a pattern we know all about ... */
2889 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2890 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2893 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2894 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
2897 if (wParam
== VK_INSERT
&& shift_state
== 1) {
2901 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
2904 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
2905 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
2908 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
) {
2912 /* Control-Numlock for app-keypad mode switch */
2913 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
2914 app_keypad_keys
^= 1;
2918 /* Nethack keypad */
2919 if (cfg
.nethack_keypad
&& !left_alt
) {
2922 *p
++ = shift_state ?
'B' : 'b';
2925 *p
++ = shift_state ?
'J' : 'j';
2928 *p
++ = shift_state ?
'N' : 'n';
2931 *p
++ = shift_state ?
'H' : 'h';
2934 *p
++ = shift_state ?
'.' : '.';
2937 *p
++ = shift_state ?
'L' : 'l';
2940 *p
++ = shift_state ?
'Y' : 'y';
2943 *p
++ = shift_state ?
'K' : 'k';
2946 *p
++ = shift_state ?
'U' : 'u';
2951 /* Application Keypad */
2955 if (cfg
.funky_type
== 3 ||
2956 (cfg
.funky_type
<= 1 &&
2957 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
2971 if (app_keypad_keys
&& !cfg
.no_applic_k
)
3008 if (cfg
.funky_type
== 2) {
3013 } else if (shift_state
)
3020 if (cfg
.funky_type
== 2)
3024 if (cfg
.funky_type
== 2)
3028 if (cfg
.funky_type
== 2)
3033 if (HIWORD(lParam
) & KF_EXTENDED
)
3039 if (xkey
>= 'P' && xkey
<= 'S')
3040 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3042 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3044 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3049 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3050 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3054 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3060 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3064 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3068 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3073 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3078 /* Control-2 to Control-8 are special */
3079 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3080 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3083 if (shift_state
== 2 && wParam
== 0xBD) {
3087 if (shift_state
== 2 && wParam
== 0xDF) {
3091 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
3098 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3099 * for integer decimal nn.)
3101 * We also deal with the weird ones here. Linux VCs replace F1
3102 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3103 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3109 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3112 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3115 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3118 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3121 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3124 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3127 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3130 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3133 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3136 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3169 if ((shift_state
&2) == 0) switch (wParam
) {
3189 /* Reorder edit keys to physical order */
3190 if (cfg
.funky_type
== 3 && code
<= 6)
3191 code
= "\0\2\1\4\5\3\6"[code
];
3193 if (vt52_mode
&& code
> 0 && code
<= 6) {
3194 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3198 if (cfg
.funky_type
== 5 && /* SCO function keys */
3199 code
>= 11 && code
<= 34) {
3200 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3203 case VK_F1
: index
= 0; break;
3204 case VK_F2
: index
= 1; break;
3205 case VK_F3
: index
= 2; break;
3206 case VK_F4
: index
= 3; break;
3207 case VK_F5
: index
= 4; break;
3208 case VK_F6
: index
= 5; break;
3209 case VK_F7
: index
= 6; break;
3210 case VK_F8
: index
= 7; break;
3211 case VK_F9
: index
= 8; break;
3212 case VK_F10
: index
= 9; break;
3213 case VK_F11
: index
= 10; break;
3214 case VK_F12
: index
= 11; break;
3216 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3217 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3218 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3221 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3222 code
>= 1 && code
<= 6) {
3223 char codes
[] = "HL.FIG";
3227 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3231 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3238 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3241 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3244 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3245 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3248 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3250 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3252 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3255 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3256 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3260 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3265 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3266 * some reason seems to send VK_CLEAR to Windows...).
3289 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3291 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
3292 /* VT100 & VT102 manuals both state the app cursor keys
3293 * only work if the app keypad is on.
3295 if (!app_keypad_keys
)
3297 /* Useful mapping of Ctrl-arrows */
3298 if (shift_state
== 2)
3302 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3304 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3311 * Finally, deal with Return ourselves. (Win95 seems to
3312 * foul it up when Alt is pressed, for some reason.)
3314 if (wParam
== VK_RETURN
) { /* Return */
3320 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3321 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3326 /* Okay we've done everything interesting; let windows deal with
3327 * the boring stuff */
3331 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3332 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3334 keystate
[VK_CAPITAL
] = 0;
3337 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3338 #ifdef SHOW_TOASCII_RESULT
3339 if (r
== 1 && !key_down
) {
3341 if (in_utf
|| dbcs_screenfont
)
3342 debug((", (U+%04x)", alt_sum
));
3344 debug((", LCH(%d)", alt_sum
));
3346 debug((", ACH(%d)", keys
[0]));
3351 for (r1
= 0; r1
< r
; r1
++) {
3352 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3360 for (i
= 0; i
< r
; i
++) {
3361 unsigned char ch
= (unsigned char) keys
[i
];
3363 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3368 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3372 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3373 MessageBeep(MB_ICONHAND
);
3377 luni_send(&keybuf
, 1);
3385 if (in_utf
|| dbcs_screenfont
) {
3387 luni_send(&keybuf
, 1);
3389 ch
= (char) alt_sum
;
3391 * We need not bother about stdin
3392 * backlogs here, because in GUI PuTTY
3393 * we can't do anything about it
3394 * anyway; there's no means of asking
3395 * Windows to hold off on KEYDOWN
3396 * messages. We _have_ to buffer
3397 * everything we're sent.
3403 lpage_send(kbd_codepage
, &ch
, 1);
3405 if(capsOn
&& ch
< 0x80) {
3408 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3409 luni_send(cbuf
+!left_alt
, 1+!!left_alt
);
3414 lpage_send(kbd_codepage
, cbuf
+!left_alt
, 1+!!left_alt
);
3420 /* This is so the ALT-Numpad and dead keys work correctly. */
3425 /* If we're definitly not building up an ALT-54321 then clear it */
3428 /* If we will be using alt_sum fix the 256s */
3429 else if (keys
[0] && (in_utf
|| dbcs_screenfont
))
3434 * ALT alone may or may not want to bring up the System menu.
3435 * If it's not meant to, we return 0 on presses or releases of
3436 * ALT, to show that we've swallowed the keystroke. Otherwise
3437 * we return -1, which means Windows will give the keystroke
3438 * its default handling (i.e. bring up the System menu).
3440 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3446 void set_title(char *title
)
3449 window_name
= smalloc(1 + strlen(title
));
3450 strcpy(window_name
, title
);
3451 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3452 SetWindowText(hwnd
, title
);
3455 void set_icon(char *title
)
3458 icon_name
= smalloc(1 + strlen(title
));
3459 strcpy(icon_name
, title
);
3460 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3461 SetWindowText(hwnd
, title
);
3464 void set_sbar(int total
, int start
, int page
)
3471 si
.cbSize
= sizeof(si
);
3472 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3474 si
.nMax
= total
- 1;
3478 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3481 Context
get_ctx(void)
3487 SelectPalette(hdc
, pal
, FALSE
);
3493 void free_ctx(Context ctx
)
3495 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3496 ReleaseDC(hwnd
, ctx
);
3499 static void real_palette_set(int n
, int r
, int g
, int b
)
3502 logpal
->palPalEntry
[n
].peRed
= r
;
3503 logpal
->palPalEntry
[n
].peGreen
= g
;
3504 logpal
->palPalEntry
[n
].peBlue
= b
;
3505 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3506 colours
[n
] = PALETTERGB(r
, g
, b
);
3507 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3509 colours
[n
] = RGB(r
, g
, b
);
3512 void palette_set(int n
, int r
, int g
, int b
)
3514 static const int first
[21] = {
3515 0, 2, 4, 6, 8, 10, 12, 14,
3516 1, 3, 5, 7, 9, 11, 13, 15,
3519 real_palette_set(first
[n
], r
, g
, b
);
3521 real_palette_set(first
[n
] + 1, r
, g
, b
);
3523 HDC hdc
= get_ctx();
3524 UnrealizeObject(pal
);
3525 RealizePalette(hdc
);
3530 void palette_reset(void)
3534 for (i
= 0; i
< NCOLOURS
; i
++) {
3536 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3537 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3538 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3539 logpal
->palPalEntry
[i
].peFlags
= 0;
3540 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3541 defpal
[i
].rgbtGreen
,
3542 defpal
[i
].rgbtBlue
);
3544 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3545 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3550 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3552 RealizePalette(hdc
);
3557 void write_aclip(char *data
, int len
, int must_deselect
)
3562 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3565 lock
= GlobalLock(clipdata
);
3568 memcpy(lock
, data
, len
);
3569 ((unsigned char *) lock
)[len
] = 0;
3570 GlobalUnlock(clipdata
);
3573 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3575 if (OpenClipboard(hwnd
)) {
3577 SetClipboardData(CF_TEXT
, clipdata
);
3580 GlobalFree(clipdata
);
3583 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3587 * Note: unlike write_aclip() this will not append a nul.
3589 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3596 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3598 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3599 len
* sizeof(wchar_t));
3600 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3602 if (!clipdata
|| !clipdata2
) {
3604 GlobalFree(clipdata
);
3606 GlobalFree(clipdata2
);
3609 if (!(lock
= GlobalLock(clipdata
)))
3611 if (!(lock2
= GlobalLock(clipdata2
)))
3614 memcpy(lock
, data
, len
* sizeof(wchar_t));
3615 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3617 GlobalUnlock(clipdata
);
3618 GlobalUnlock(clipdata2
);
3621 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3623 if (OpenClipboard(hwnd
)) {
3625 SetClipboardData(CF_UNICODETEXT
, clipdata
);
3626 SetClipboardData(CF_TEXT
, clipdata2
);
3629 GlobalFree(clipdata
);
3630 GlobalFree(clipdata2
);
3634 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3637 void get_clip(wchar_t ** p
, int *len
)
3639 static HGLOBAL clipdata
= NULL
;
3640 static wchar_t *converted
= 0;
3649 GlobalUnlock(clipdata
);
3652 } else if (OpenClipboard(NULL
)) {
3653 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
3655 *p
= GlobalLock(clipdata
);
3657 for (p2
= *p
; *p2
; p2
++);
3661 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
3665 s
= GlobalLock(clipdata
);
3666 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
3667 *p
= converted
= smalloc(i
* sizeof(wchar_t));
3668 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
3681 * Move `lines' lines from position `from' to position `to' in the
3684 void optimised_move(int to
, int from
, int lines
)
3689 min
= (to
< from ? to
: from
);
3690 max
= to
+ from
- min
;
3692 r
.left
= offset_width
;
3693 r
.right
= offset_width
+ cols
* font_width
;
3694 r
.top
= offset_height
+ min
* font_height
;
3695 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
3696 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
3701 * Print a message box and perform a fatal exit.
3703 void fatalbox(char *fmt
, ...)
3709 vsprintf(stuff
, fmt
, ap
);
3711 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
3716 * Manage window caption / taskbar flashing, if enabled.
3717 * 0 = stop, 1 = maintain, 2 = start
3719 static void flash_window(int mode
)
3721 static long last_flash
= 0;
3722 static int flashing
= 0;
3723 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
3726 FlashWindow(hwnd
, FALSE
);
3730 } else if (mode
== 2) {
3733 last_flash
= GetTickCount();
3735 FlashWindow(hwnd
, TRUE
);
3738 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
3741 long now
= GetTickCount();
3742 long fdiff
= now
- last_flash
;
3743 if (fdiff
< 0 || fdiff
> 450) {
3745 FlashWindow(hwnd
, TRUE
); /* toggle */
3756 if (mode
== BELL_DEFAULT
) {
3758 * For MessageBeep style bells, we want to be careful of
3759 * timing, because they don't have the nice property of
3760 * PlaySound bells that each one cancels the previous
3761 * active one. So we limit the rate to one per 50ms or so.
3763 static long lastbeep
= 0;
3766 beepdiff
= GetTickCount() - lastbeep
;
3767 if (beepdiff
>= 0 && beepdiff
< 50)
3771 * The above MessageBeep call takes time, so we record the
3772 * time _after_ it finishes rather than before it starts.
3774 lastbeep
= GetTickCount();
3775 } else if (mode
== BELL_WAVEFILE
) {
3776 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
3777 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
3778 sprintf(buf
, "Unable to play sound file\n%s\n"
3779 "Using default sound instead", cfg
.bell_wavefile
);
3780 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
3781 MB_OK
| MB_ICONEXCLAMATION
);
3782 cfg
.beep
= BELL_DEFAULT
;
3785 /* Otherwise, either visual bell or disabled; do nothing here */
3787 flash_window(2); /* start */
3792 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3795 static void flip_full_screen(void)
3800 cx
= GetSystemMetrics(SM_CXSCREEN
);
3801 cy
= GetSystemMetrics(SM_CYSCREEN
);
3802 GetWindowPlacement(hwnd
, &old_wind_placement
);
3803 old_wind_style
= GetWindowLong(hwnd
, GWL_STYLE
);
3804 SetWindowLong(hwnd
, GWL_STYLE
,
3805 old_wind_style
& ~(WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
));
3806 SetWindowPos(hwnd
, HWND_TOP
, 0, 0, cx
, cy
, SWP_SHOWWINDOW
);
3809 SetWindowLong(hwnd
, GWL_STYLE
, old_wind_style
);
3810 SetWindowPlacement(hwnd
,&old_wind_placement
);