18 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
24 #define IDM_SHOWLOG 0x0010
25 #define IDM_NEWSESS 0x0020
26 #define IDM_DUPSESS 0x0030
27 #define IDM_RECONF 0x0040
28 #define IDM_CLRSB 0x0050
29 #define IDM_RESET 0x0060
30 #define IDM_TEL_AYT 0x0070
31 #define IDM_TEL_BRK 0x0080
32 #define IDM_TEL_SYNCH 0x0090
33 #define IDM_TEL_EC 0x00a0
34 #define IDM_TEL_EL 0x00b0
35 #define IDM_TEL_GA 0x00c0
36 #define IDM_TEL_NOP 0x00d0
37 #define IDM_TEL_ABORT 0x00e0
38 #define IDM_TEL_AO 0x00f0
39 #define IDM_TEL_IP 0x0100
40 #define IDM_TEL_SUSP 0x0110
41 #define IDM_TEL_EOR 0x0120
42 #define IDM_TEL_EOF 0x0130
43 #define IDM_ABOUT 0x0140
44 #define IDM_SAVEDSESS 0x0150
45 #define IDM_COPYALL 0x0160
46 #define IDM_FULLSCREEN 0x0170
48 #define IDM_SESSLGP 0x0250 /* log type printable */
49 #define IDM_SESSLGA 0x0260 /* log type all chars */
50 #define IDM_SESSLGE 0x0270 /* log end */
51 #define IDM_SAVED_MIN 0x1000
52 #define IDM_SAVED_MAX 0x2000
54 #define WM_IGNORE_CLIP (WM_XUSER + 2)
56 /* Needed for Chinese support and apparently not always defined. */
58 #define VK_PROCESSKEY 0xE5
61 /* Needed for mouse wheel support and not defined in earlier SDKs. */
63 #define WM_MOUSEWHEEL 0x020A
66 static LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
67 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
68 unsigned char *output
);
69 static void cfgtopalette(void);
70 static void init_palette(void);
71 static void init_fonts(int, int);
72 static void another_font(int);
73 static void deinit_fonts(void);
75 /* Window layout information */
76 static void reset_window(int);
77 static int full_screen
= 0;
78 static int extra_width
, extra_height
;
79 static int font_width
, font_height
, font_dualwidth
;
80 static int offset_width
, offset_height
;
81 static int was_zoomed
= 0;
82 static int prev_rows
, prev_cols
;
84 static int pending_netevent
= 0;
85 static WPARAM pend_netevent_wParam
= 0;
86 static LPARAM pend_netevent_lParam
= 0;
87 static void enact_pending_netevent(void);
88 static void flash_window(int mode
);
89 static void flip_full_screen(void);
91 static time_t last_movement
= 0;
95 #define FONT_UNDERLINE 2
96 #define FONT_BOLDUND 3
97 #define FONT_WIDE 0x04
98 #define FONT_HIGH 0x08
99 #define FONT_NARROW 0x10
101 #define FONT_OEM 0x20
102 #define FONT_OEMBOLD 0x21
103 #define FONT_OEMUND 0x22
104 #define FONT_OEMBOLDUND 0x23
106 #define FONT_MAXNO 0x2F
108 static HFONT fonts
[FONT_MAXNO
];
109 static int fontflag
[FONT_MAXNO
];
111 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
119 static COLORREF colours
[NCOLOURS
];
121 static LPLOGPALETTE logpal
;
122 static RGBTRIPLE defpal
[NCOLOURS
];
126 static HBITMAP caretbm
;
128 static int dbltime
, lasttime
, lastact
;
129 static Mouse_Button lastbtn
;
131 /* this allows xterm-style mouse handling. */
132 static int send_raw_mouse
= 0;
133 static int wheel_accumulator
= 0;
135 static char *window_name
, *icon_name
;
137 static int compose_state
= 0;
139 static OSVERSIONINFO osVersion
;
141 /* Dummy routine, only required in plink. */
142 void ldisc_update(int echo
, int edit
)
146 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
148 static char appname
[] = "PuTTY";
153 int guess_width
, guess_height
;
156 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
158 winsock_ver
= MAKEWORD(1, 1);
159 if (WSAStartup(winsock_ver
, &wsadata
)) {
160 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
161 MB_OK
| MB_ICONEXCLAMATION
);
164 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
165 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
166 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
170 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
173 InitCommonControls();
175 /* Ensure a Maximize setting in Explorer doesn't maximise the
180 ZeroMemory(&osVersion
, sizeof(osVersion
));
181 osVersion
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
182 if (!GetVersionEx ( (OSVERSIONINFO
*) &osVersion
)) {
183 MessageBox(NULL
, "Windows refuses to report a version",
184 "PuTTY Fatal Error", MB_OK
| MB_ICONEXCLAMATION
);
190 * Process the command line.
195 default_protocol
= DEFAULT_PROTOCOL
;
196 default_port
= DEFAULT_PORT
;
197 cfg
.logtype
= LGTYP_NONE
;
199 do_defaults(NULL
, &cfg
);
202 while (*p
&& isspace(*p
))
206 * Process command line options first. Yes, this can be
207 * done better, and it will be as soon as I have the
211 char *q
= p
+ strcspn(p
, " \t");
214 tolower(p
[0]) == 's' &&
215 tolower(p
[1]) == 's' && tolower(p
[2]) == 'h') {
216 default_protocol
= cfg
.protocol
= PROT_SSH
;
217 default_port
= cfg
.port
= 22;
218 } else if (q
== p
+ 7 &&
219 tolower(p
[0]) == 'c' &&
220 tolower(p
[1]) == 'l' &&
221 tolower(p
[2]) == 'e' &&
222 tolower(p
[3]) == 'a' &&
223 tolower(p
[4]) == 'n' &&
224 tolower(p
[5]) == 'u' && tolower(p
[6]) == 'p') {
226 * `putty -cleanup'. Remove all registry entries
227 * associated with PuTTY, and also find and delete
228 * the random seed file.
231 "This procedure will remove ALL Registry\n"
232 "entries associated with PuTTY, and will\n"
233 "also remove the PuTTY random seed file.\n"
235 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
236 "SESSIONS. Are you really sure you want\n"
239 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
244 p
= q
+ strspn(q
, " \t");
248 * An initial @ means to activate a saved session.
252 while (i
> 1 && isspace(p
[i
- 1]))
255 do_defaults(p
+ 1, &cfg
);
256 if (!*cfg
.host
&& !do_config()) {
260 } else if (*p
== '&') {
262 * An initial & means we've been given a command line
263 * containing the hex value of a HANDLE for a file
264 * mapping object, which we must then extract as a
269 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
270 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
271 0, 0, sizeof(Config
))) != NULL
) {
274 CloseHandle(filemap
);
275 } else if (!do_config()) {
282 * If the hostname starts with "telnet:", set the
283 * protocol to Telnet and process the string as a
286 if (!strncmp(q
, "telnet:", 7)) {
290 if (q
[0] == '/' && q
[1] == '/')
292 cfg
.protocol
= PROT_TELNET
;
294 while (*p
&& *p
!= ':' && *p
!= '/')
303 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
304 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
306 while (*p
&& !isspace(*p
))
310 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
311 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
312 while (*p
&& isspace(*p
))
327 * Trim leading whitespace off the hostname if it's there.
330 int space
= strspn(cfg
.host
, " \t");
331 memmove(cfg
.host
, cfg
.host
+space
, 1+strlen(cfg
.host
)-space
);
334 /* See if host is of the form user@host */
335 if (cfg
.host
[0] != '\0') {
336 char *atsign
= strchr(cfg
.host
, '@');
337 /* Make sure we're not overflowing the user field */
339 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
340 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
341 cfg
.username
[atsign
- cfg
.host
] = '\0';
343 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
348 * Trim a colon suffix off the hostname if it's there.
350 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
354 * Select protocol. This is farmed out into a table in a
355 * separate file to enable an ssh-free variant.
360 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
361 if (backends
[i
].protocol
== cfg
.protocol
) {
362 back
= backends
[i
].backend
;
366 MessageBox(NULL
, "Unsupported protocol number found",
367 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
373 /* Check for invalid Port number (i.e. zero) */
375 MessageBox(NULL
, "Invalid Port Number",
376 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
383 wndclass
.lpfnWndProc
= WndProc
;
384 wndclass
.cbClsExtra
= 0;
385 wndclass
.cbWndExtra
= 0;
386 wndclass
.hInstance
= inst
;
387 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
388 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
389 wndclass
.hbrBackground
= NULL
;
390 wndclass
.lpszMenuName
= NULL
;
391 wndclass
.lpszClassName
= appname
;
393 RegisterClass(&wndclass
);
398 savelines
= cfg
.savelines
;
404 * Guess some defaults for the window size. This all gets
405 * updated later, so we don't really care too much. However, we
406 * do want the font width/height guesses to correspond to a
407 * large font rather than a small one...
414 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
415 guess_width
= extra_width
+ font_width
* cols
;
416 guess_height
= extra_height
+ font_height
* rows
;
419 HWND w
= GetDesktopWindow();
420 GetWindowRect(w
, &r
);
421 if (guess_width
> r
.right
- r
.left
)
422 guess_width
= r
.right
- r
.left
;
423 if (guess_height
> r
.bottom
- r
.top
)
424 guess_height
= r
.bottom
- r
.top
;
428 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
431 winmode
&= ~(WS_VSCROLL
);
432 if (cfg
.resize_action
== RESIZE_DISABLED
)
433 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
435 exwinmode
|= WS_EX_TOPMOST
;
437 exwinmode
|= WS_EX_CLIENTEDGE
;
438 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
439 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
440 guess_width
, guess_height
,
441 NULL
, NULL
, inst
, NULL
);
445 * Initialise the fonts, simultaneously correcting the guesses
446 * for font_{width,height}.
451 * Correct the guesses for extra_{width,height}.
455 GetWindowRect(hwnd
, &wr
);
456 GetClientRect(hwnd
, &cr
);
457 offset_width
= offset_height
= cfg
.window_border
;
458 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
459 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
463 * Resize the window, now we know what size we _really_ want it
466 guess_width
= extra_width
+ font_width
* cols
;
467 guess_height
= extra_height
+ font_height
* rows
;
468 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
469 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
472 * Set up a caret bitmap, with no content.
476 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
477 bits
= smalloc(size
);
478 memset(bits
, 0, size
);
479 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
482 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
485 * Initialise the scroll bar.
490 si
.cbSize
= sizeof(si
);
491 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
496 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
500 * Start up the telnet connection.
504 char msg
[1024], *title
;
507 error
= back
->init(cfg
.host
, cfg
.port
, &realhost
);
509 sprintf(msg
, "Unable to open connection to\n"
510 "%.800s\n" "%s", cfg
.host
, error
);
511 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
514 window_name
= icon_name
= NULL
;
516 title
= cfg
.wintitle
;
518 sprintf(msg
, "%s - PuTTY", realhost
);
526 session_closed
= FALSE
;
529 * Prepare the mouse handler.
531 lastact
= MA_NOTHING
;
532 lastbtn
= MBT_NOTHING
;
533 dbltime
= GetDoubleClickTime();
536 * Set up the session-control options on the system menu.
539 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
543 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
544 if (cfg
.protocol
== PROT_TELNET
) {
546 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
547 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
548 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
549 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
550 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
551 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
552 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
553 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
554 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
555 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
556 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
557 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
558 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
559 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
560 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
561 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
562 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
564 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
566 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
567 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
568 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
569 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
572 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
573 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
575 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
576 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
577 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
578 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
579 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
580 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
581 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
582 AppendMenu(m
, MF_ENABLED
, IDM_FULLSCREEN
, "&Full Screen");
583 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
584 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
588 * Finally show the window!
590 ShowWindow(hwnd
, show
);
591 SetForegroundWindow(hwnd
);
594 * Open the initial log file if there is one.
599 * Set the palette up.
605 has_focus
= (GetForegroundWindow() == hwnd
);
608 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
609 int timer_id
= 0, long_timer
= 0;
611 while (msg
.message
!= WM_QUIT
) {
612 /* Sometimes DispatchMessage calls routines that use their own
613 * GetMessage loop, setup this timer so we get some control back.
615 * Also call term_update() from the timer so that if the host
616 * is sending data flat out we still do redraws.
618 if (timer_id
&& long_timer
) {
619 KillTimer(hwnd
, timer_id
);
620 long_timer
= timer_id
= 0;
623 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
624 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
625 DispatchMessage(&msg
);
627 /* Make sure we blink everything that needs it. */
630 /* Send the paste buffer if there's anything to send */
633 /* If there's nothing new in the queue then we can do everything
634 * we've delayed, reading the socket, writing, and repainting
637 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
640 if (pending_netevent
) {
641 enact_pending_netevent();
643 /* Force the cursor blink on */
646 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
650 /* Okay there is now nothing to do so we make sure the screen is
651 * completely up to date then tell windows to call us in a little
655 KillTimer(hwnd
, timer_id
);
663 flash_window(1); /* maintain */
666 /* Hmm, term_update didn't want to do an update too soon ... */
667 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
669 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
671 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
674 /* There's no point rescanning everything in the message queue
675 * so we do an apparently unnecessary wait here
678 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
692 if (cfg
.protocol
== PROT_SSH
) {
703 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
705 char *do_select(SOCKET skt
, int startup
)
710 events
= (FD_CONNECT
| FD_READ
| FD_WRITE
|
711 FD_OOB
| FD_CLOSE
| FD_ACCEPT
);
716 return "do_select(): internal error (hwnd==NULL)";
717 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
718 switch (WSAGetLastError()) {
720 return "Network is down";
722 return "WSAAsyncSelect(): unknown error";
729 * set or clear the "raw mouse message" mode
731 void set_raw_mouse_mode(int activate
)
733 send_raw_mouse
= activate
;
734 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
738 * Print a message box and close the connection.
740 void connection_fatal(char *fmt
, ...)
746 vsprintf(stuff
, fmt
, ap
);
748 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
749 if (cfg
.close_on_exit
== COE_ALWAYS
)
752 session_closed
= TRUE
;
753 SetWindowText(hwnd
, "PuTTY (inactive)");
758 * Actually do the job requested by a WM_NETEVENT
760 static void enact_pending_netevent(void)
762 static int reentering
= 0;
763 extern int select_result(WPARAM
, LPARAM
);
767 return; /* don't unpend the pending */
769 pending_netevent
= FALSE
;
772 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
775 if (ret
== 0 && !session_closed
) {
776 /* Abnormal exits will already have set session_closed and taken
777 * appropriate action. */
778 if (cfg
.close_on_exit
== COE_ALWAYS
||
779 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
781 session_closed
= TRUE
;
782 SetWindowText(hwnd
, "PuTTY (inactive)");
783 MessageBox(hwnd
, "Connection closed by remote host",
784 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
790 * Copy the colour palette from the configuration data into defpal.
791 * This is non-trivial because the colour indices are different.
793 static void cfgtopalette(void)
796 static const int ww
[] = {
797 6, 7, 8, 9, 10, 11, 12, 13,
798 14, 15, 16, 17, 18, 19, 20, 21,
799 0, 1, 2, 3, 4, 4, 5, 5
802 for (i
= 0; i
< 24; i
++) {
804 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
805 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
806 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
811 * Set up the colour palette.
813 static void init_palette(void)
816 HDC hdc
= GetDC(hwnd
);
818 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
819 logpal
= smalloc(sizeof(*logpal
)
820 - sizeof(logpal
->palPalEntry
)
821 + NCOLOURS
* sizeof(PALETTEENTRY
));
822 logpal
->palVersion
= 0x300;
823 logpal
->palNumEntries
= NCOLOURS
;
824 for (i
= 0; i
< NCOLOURS
; i
++) {
825 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
826 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
827 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
828 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
830 pal
= CreatePalette(logpal
);
832 SelectPalette(hdc
, pal
, FALSE
);
834 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
837 ReleaseDC(hwnd
, hdc
);
840 for (i
= 0; i
< NCOLOURS
; i
++)
841 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
845 for (i
= 0; i
< NCOLOURS
; i
++)
846 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
847 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
851 * Initialise all the fonts we will need initially. There may be as many as
852 * three or as few as one. The other (poentially) twentyone fonts are done
853 * if/when they are needed.
857 * - check the font width and height, correcting our guesses if
860 * - verify that the bold font is the same width as the ordinary
861 * one, and engage shadow bolding if not.
863 * - verify that the underlined font is the same width as the
864 * ordinary one (manual underlining by means of line drawing can
865 * be done in a pinch).
867 static void init_fonts(int pick_width
, int pick_height
)
874 int fw_dontcare
, fw_bold
;
876 for (i
= 0; i
< FONT_MAXNO
; i
++)
879 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
882 if (cfg
.fontisbold
) {
883 fw_dontcare
= FW_BOLD
;
886 fw_dontcare
= FW_DONTCARE
;
893 font_height
= pick_height
;
895 font_height
= cfg
.fontheight
;
896 if (font_height
> 0) {
898 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
901 font_width
= pick_width
;
904 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
905 c, OUT_DEFAULT_PRECIS, \
906 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
907 FIXED_PITCH | FF_DONTCARE, cfg.font)
909 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
911 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
912 GetTextMetrics(hdc
, &tm
);
914 if (pick_width
== 0 || pick_height
== 0) {
915 font_height
= tm
.tmHeight
;
916 font_width
= tm
.tmAveCharWidth
;
918 font_dualwidth
= (tm
.tmAveCharWidth
!= tm
.tmMaxCharWidth
);
920 #ifdef RDB_DEBUG_PATCH
921 debug(23, "Primary font H=%d, AW=%d, MW=%d",
922 tm
.tmHeight
, tm
.tmAveCharWidth
, tm
.tmMaxCharWidth
);
927 DWORD cset
= tm
.tmCharSet
;
928 memset(&info
, 0xFF, sizeof(info
));
930 /* !!! Yes the next line is right */
931 if (cset
== OEM_CHARSET
)
932 font_codepage
= GetOEMCP();
934 if (TranslateCharsetInfo
935 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
940 GetCPInfo(font_codepage
, &cpinfo
);
941 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
944 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
947 * Some fonts, e.g. 9-pt Courier, draw their underlines
948 * outside their character cell. We successfully prevent
949 * screen corruption by clipping the text output, but then
950 * we lose the underline completely. Here we try to work
951 * out whether this is such a font, and if it is, we set a
952 * flag that causes underlines to be drawn by hand.
954 * Having tried other more sophisticated approaches (such
955 * as examining the TEXTMETRIC structure or requesting the
956 * height of a string), I think we'll do this the brute
957 * force way: we create a small bitmap, draw an underlined
958 * space on it, and test to see whether any pixels are
959 * foreground-coloured. (Since we expect the underline to
960 * go all the way across the character cell, we only search
961 * down a single column of the bitmap, half way across.)
965 HBITMAP und_bm
, und_oldbm
;
969 und_dc
= CreateCompatibleDC(hdc
);
970 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
971 und_oldbm
= SelectObject(und_dc
, und_bm
);
972 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
973 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
974 SetTextColor(und_dc
, RGB(255, 255, 255));
975 SetBkColor(und_dc
, RGB(0, 0, 0));
976 SetBkMode(und_dc
, OPAQUE
);
977 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
979 for (i
= 0; i
< font_height
; i
++) {
980 c
= GetPixel(und_dc
, font_width
/ 2, i
);
981 if (c
!= RGB(0, 0, 0))
984 SelectObject(und_dc
, und_oldbm
);
985 DeleteObject(und_bm
);
989 DeleteObject(fonts
[FONT_UNDERLINE
]);
990 fonts
[FONT_UNDERLINE
] = 0;
994 if (bold_mode
== BOLD_FONT
) {
995 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
999 descent
= tm
.tmAscent
+ 1;
1000 if (descent
>= font_height
)
1001 descent
= font_height
- 1;
1003 for (i
= 0; i
< 3; i
++) {
1005 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
1006 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
1013 ReleaseDC(hwnd
, hdc
);
1015 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1016 und_mode
= UND_LINE
;
1017 DeleteObject(fonts
[FONT_UNDERLINE
]);
1018 fonts
[FONT_UNDERLINE
] = 0;
1021 if (bold_mode
== BOLD_FONT
&&
1022 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1023 bold_mode
= BOLD_SHADOW
;
1024 DeleteObject(fonts
[FONT_BOLD
]);
1025 fonts
[FONT_BOLD
] = 0;
1027 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1032 static void another_font(int fontno
)
1035 int fw_dontcare
, fw_bold
;
1039 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1042 basefont
= (fontno
& ~(FONT_BOLDUND
));
1043 if (basefont
!= fontno
&& !fontflag
[basefont
])
1044 another_font(basefont
);
1046 if (cfg
.fontisbold
) {
1047 fw_dontcare
= FW_BOLD
;
1050 fw_dontcare
= FW_DONTCARE
;
1054 c
= cfg
.fontcharset
;
1060 if (fontno
& FONT_WIDE
)
1062 if (fontno
& FONT_NARROW
)
1064 if (fontno
& FONT_OEM
)
1066 if (fontno
& FONT_BOLD
)
1068 if (fontno
& FONT_UNDERLINE
)
1072 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1073 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1074 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1075 FIXED_PITCH
| FF_DONTCARE
, s
);
1077 fontflag
[fontno
] = 1;
1080 static void deinit_fonts(void)
1083 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1085 DeleteObject(fonts
[i
]);
1091 void request_resize(int w
, int h
)
1095 /* If the window is maximized supress resizing attempts */
1096 if (IsZoomed(hwnd
)) {
1097 if (cfg
.resize_action
!= RESIZE_FONT
)
1101 if (cfg
.resize_action
== RESIZE_DISABLED
) return;
1102 if (h
== rows
&& w
== cols
) return;
1104 /* Sanity checks ... */
1106 static int first_time
= 1;
1109 switch (first_time
) {
1111 /* Get the size of the screen */
1112 if (GetClientRect(GetDesktopWindow(), &ss
))
1113 /* first_time = 0 */ ;
1119 /* Make sure the values are sane */
1120 width
= (ss
.right
- ss
.left
- extra_width
) / 4;
1121 height
= (ss
.bottom
- ss
.top
- extra_height
) / 6;
1123 if (w
> width
|| h
> height
)
1132 term_size(h
, w
, cfg
.savelines
);
1134 if (cfg
.resize_action
!= RESIZE_FONT
) {
1135 width
= extra_width
+ font_width
* w
;
1136 height
= extra_height
+ font_height
* h
;
1138 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1139 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1140 SWP_NOMOVE
| SWP_NOZORDER
);
1144 InvalidateRect(hwnd
, NULL
, TRUE
);
1147 static void reset_window(int reinit
) {
1149 * This function decides how to resize or redraw when the
1150 * user changes something.
1152 * This function doesn't like to change the terminal size but if the
1153 * font size is locked that may be it's only soluion.
1155 int win_width
, win_height
;
1158 #ifdef RDB_DEBUG_PATCH
1159 debug((27, "reset_window()"));
1162 /* Current window sizes ... */
1163 GetWindowRect(hwnd
, &wr
);
1164 GetClientRect(hwnd
, &cr
);
1166 win_width
= cr
.right
- cr
.left
;
1167 win_height
= cr
.bottom
- cr
.top
;
1169 /* Are we being forced to reload the fonts ? */
1171 #ifdef RDB_DEBUG_PATCH
1172 debug((27, "reset_window() -- Forced deinit"));
1178 /* Oh, looks like we're minimised */
1179 if (win_width
== 0 || win_height
== 0)
1182 /* Is the window out of position ? */
1184 (offset_width
!= (win_width
-font_width
*cols
)/2 ||
1185 offset_height
!= (win_height
-font_height
*rows
)/2) ){
1186 offset_width
= (win_width
-font_width
*cols
)/2;
1187 offset_height
= (win_height
-font_height
*rows
)/2;
1188 InvalidateRect(hwnd
, NULL
, TRUE
);
1189 #ifdef RDB_DEBUG_PATCH
1190 debug((27, "reset_window() -> Reposition terminal"));
1194 if (IsZoomed(hwnd
)) {
1195 /* We're fullscreen, this means we must not change the size of
1196 * the window so it's the font size or the terminal itself.
1199 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1200 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1202 if (cfg
.resize_action
== RESIZE_FONT
) {
1203 if ( font_width
!= win_width
/cols
||
1204 font_height
!= win_height
/rows
) {
1206 init_fonts(win_width
/cols
, win_height
/rows
);
1207 offset_width
= (win_width
-font_width
*cols
)/2;
1208 offset_height
= (win_height
-font_height
*rows
)/2;
1209 InvalidateRect(hwnd
, NULL
, TRUE
);
1210 #ifdef RDB_DEBUG_PATCH
1211 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1212 font_width
, font_height
));
1216 if ( font_width
!= win_width
/cols
||
1217 font_height
!= win_height
/rows
) {
1218 /* Our only choice at this point is to change the
1219 * size of the terminal; Oh well.
1221 term_size( win_height
/font_height
, win_width
/font_width
,
1223 offset_width
= (win_width
-font_width
*cols
)/2;
1224 offset_height
= (win_height
-font_height
*rows
)/2;
1225 InvalidateRect(hwnd
, NULL
, TRUE
);
1226 #ifdef RDB_DEBUG_PATCH
1227 debug((27, "reset_window() -> Zoomed term_size"));
1234 /* Hmm, a force re-init means we should ignore the current window
1235 * so we resize to the default font size.
1238 #ifdef RDB_DEBUG_PATCH
1239 debug((27, "reset_window() -> Forced re-init"));
1242 offset_width
= offset_height
= cfg
.window_border
;
1243 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1244 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1246 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1247 win_height
!= font_height
*rows
+ offset_height
*2) {
1249 /* If this is too large windows will resize it to the maximum
1250 * allowed window size, we will then be back in here and resize
1251 * the font or terminal to fit.
1253 SetWindowPos(hwnd
, NULL
, 0, 0,
1254 font_width
*cols
+ extra_width
,
1255 font_height
*rows
+ extra_height
,
1256 SWP_NOMOVE
| SWP_NOZORDER
);
1261 /* Okay the user doesn't want us to change the font so we try the
1262 * window. But that may be too big for the screen which forces us
1263 * to change the terminal.
1265 if ((cfg
.resize_action
!= RESIZE_FONT
&& reinit
==0) || reinit
>0) {
1266 offset_width
= offset_height
= cfg
.window_border
;
1267 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1268 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1270 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1271 win_height
!= font_height
*rows
+ offset_height
*2) {
1276 GetClientRect(GetDesktopWindow(), &ss
);
1277 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1278 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1281 if ( rows
> height
|| cols
> width
) {
1282 if ( height
> rows
) height
= rows
;
1283 if ( width
> cols
) width
= cols
;
1284 term_size(height
, width
, cfg
.savelines
);
1285 #ifdef RDB_DEBUG_PATCH
1286 debug((27, "reset_window() -> term resize to (%d,%d)",
1291 SetWindowPos(hwnd
, NULL
, 0, 0,
1292 font_width
*cols
+ extra_width
,
1293 font_height
*rows
+ extra_height
,
1294 SWP_NOMOVE
| SWP_NOZORDER
);
1296 InvalidateRect(hwnd
, NULL
, TRUE
);
1297 #ifdef RDB_DEBUG_PATCH
1298 debug((27, "reset_window() -> window resize to (%d,%d)",
1299 font_width
*cols
+ extra_width
,
1300 font_height
*rows
+ extra_height
));
1306 /* We're allowed to or must change the font but do we want to ? */
1308 if (font_width
!= (win_width
-cfg
.window_border
*2)/cols
||
1309 font_height
!= (win_height
-cfg
.window_border
*2)/rows
) {
1312 init_fonts((win_width
-cfg
.window_border
*2)/cols
,
1313 (win_height
-cfg
.window_border
*2)/rows
);
1314 offset_width
= (win_width
-font_width
*cols
)/2;
1315 offset_height
= (win_height
-font_height
*rows
)/2;
1317 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+offset_width
*2;
1318 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1320 InvalidateRect(hwnd
, NULL
, TRUE
);
1321 #ifdef RDB_DEBUG_PATCH
1322 debug((25, "reset_window() -> font resize to (%d,%d)",
1323 font_width
, font_height
));
1328 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
, int alt
)
1330 int thistime
= GetMessageTime();
1332 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1333 lastbtn
= MBT_NOTHING
;
1334 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
, alt
);
1338 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1339 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1340 lastact
== MA_2CLK ? MA_3CLK
:
1341 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1346 if (lastact
!= MA_NOTHING
)
1347 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
, alt
);
1348 lasttime
= thistime
;
1352 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1353 * into a cooked one (SELECT, EXTEND, PASTE).
1355 Mouse_Button
translate_button(Mouse_Button button
)
1357 if (button
== MBT_LEFT
)
1359 if (button
== MBT_MIDDLE
)
1360 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1361 if (button
== MBT_RIGHT
)
1362 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1363 return 0; /* shouldn't happen */
1366 static void show_mouseptr(int show
)
1368 static int cursor_visible
= 1;
1369 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1371 if (cursor_visible
&& !show
)
1373 else if (!cursor_visible
&& show
)
1375 cursor_visible
= show
;
1378 static int is_alt_pressed(void)
1381 int r
= GetKeyboardState(keystate
);
1384 if (keystate
[VK_MENU
] & 0x80)
1386 if (keystate
[VK_RMENU
] & 0x80)
1391 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1392 WPARAM wParam
, LPARAM lParam
)
1395 static int ignore_clip
= FALSE
;
1396 static int resizing
= FALSE
;
1397 static int need_backend_resize
= FALSE
;
1401 if (pending_netevent
)
1402 enact_pending_netevent();
1408 if (cfg
.ping_interval
> 0) {
1411 if (now
- last_movement
> cfg
.ping_interval
) {
1412 back
->special(TS_PING
);
1413 last_movement
= now
;
1416 net_pending_errors();
1422 if (!cfg
.warn_on_close
|| session_closed
||
1424 "Are you sure you want to close this session?",
1425 "PuTTY Exit Confirmation",
1426 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1427 DestroyWindow(hwnd
);
1434 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1446 PROCESS_INFORMATION pi
;
1447 HANDLE filemap
= NULL
;
1449 if (wParam
== IDM_DUPSESS
) {
1451 * Allocate a file-mapping memory chunk for the
1454 SECURITY_ATTRIBUTES sa
;
1457 sa
.nLength
= sizeof(sa
);
1458 sa
.lpSecurityDescriptor
= NULL
;
1459 sa
.bInheritHandle
= TRUE
;
1460 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1463 0, sizeof(Config
), NULL
);
1465 p
= (Config
*) MapViewOfFile(filemap
,
1467 0, 0, sizeof(Config
));
1469 *p
= cfg
; /* structure copy */
1473 sprintf(c
, "putty &%p", filemap
);
1475 } else if (wParam
== IDM_SAVEDSESS
) {
1477 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1478 cl
= smalloc(16 + strlen(session
)); /* 8, but play safe */
1480 cl
= NULL
; /* not a very important failure mode */
1482 sprintf(cl
, "putty @%s", session
);
1488 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1490 si
.lpReserved
= NULL
;
1491 si
.lpDesktop
= NULL
;
1495 si
.lpReserved2
= NULL
;
1496 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1497 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1500 CloseHandle(filemap
);
1510 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1513 if (!do_reconfig(hwnd
))
1516 /* If user forcibly disables full-screen, gracefully unzoom */
1517 if (full_screen
&& !cfg
.fullscreenonaltenter
) {
1521 if (strcmp(prev_cfg
.logfilename
, cfg
.logfilename
) ||
1522 prev_cfg
.logtype
!= cfg
.logtype
) {
1523 logfclose(); /* reset logging */
1529 * Flush the line discipline's edit buffer in the
1530 * case where local editing has just been disabled.
1532 ldisc_send(NULL
, 0, 0);
1540 /* Screen size changed ? */
1541 if (cfg
.height
!= prev_cfg
.height
||
1542 cfg
.width
!= prev_cfg
.width
||
1543 cfg
.savelines
!= prev_cfg
.savelines
||
1544 cfg
.resize_action
!= RESIZE_TERM
)
1545 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1547 /* Enable or disable the scroll bar, etc */
1549 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1550 LONG nexflag
, exflag
=
1551 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1554 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1555 if (cfg
.alwaysontop
) {
1556 nexflag
|= WS_EX_TOPMOST
;
1557 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1558 SWP_NOMOVE
| SWP_NOSIZE
);
1560 nexflag
&= ~(WS_EX_TOPMOST
);
1561 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1562 SWP_NOMOVE
| SWP_NOSIZE
);
1565 if (cfg
.sunken_edge
)
1566 nexflag
|= WS_EX_CLIENTEDGE
;
1568 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1574 nflg
&= ~WS_VSCROLL
;
1575 if (cfg
.resize_action
== RESIZE_DISABLED
)
1576 nflg
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1578 nflg
|= (WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1580 if (nflg
!= flag
|| nexflag
!= exflag
) {
1582 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1583 if (nexflag
!= exflag
)
1584 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1586 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1587 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1588 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1596 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1601 set_title(cfg
.wintitle
);
1602 if (IsIconic(hwnd
)) {
1604 cfg
.win_name_always ? window_name
:
1608 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1609 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1610 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1611 cfg
.fontheight
!= prev_cfg
.fontheight
||
1612 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1613 cfg
.vtmode
!= prev_cfg
.vtmode
||
1614 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1615 (cfg
.resize_action
!= RESIZE_FONT
&&
1616 prev_cfg
.resize_action
== RESIZE_FONT
))
1619 InvalidateRect(hwnd
, NULL
, TRUE
);
1620 reset_window(init_lvl
);
1621 net_pending_errors();
1634 back
->special(TS_AYT
);
1635 net_pending_errors();
1638 back
->special(TS_BRK
);
1639 net_pending_errors();
1642 back
->special(TS_SYNCH
);
1643 net_pending_errors();
1646 back
->special(TS_EC
);
1647 net_pending_errors();
1650 back
->special(TS_EL
);
1651 net_pending_errors();
1654 back
->special(TS_GA
);
1655 net_pending_errors();
1658 back
->special(TS_NOP
);
1659 net_pending_errors();
1662 back
->special(TS_ABORT
);
1663 net_pending_errors();
1666 back
->special(TS_AO
);
1667 net_pending_errors();
1670 back
->special(TS_IP
);
1671 net_pending_errors();
1674 back
->special(TS_SUSP
);
1675 net_pending_errors();
1678 back
->special(TS_EOR
);
1679 net_pending_errors();
1682 back
->special(TS_EOF
);
1683 net_pending_errors();
1690 * We get this if the System menu has been activated.
1691 * This might happen from within TranslateKey, in which
1692 * case it really wants to be followed by a `space'
1693 * character to actually _bring the menu up_ rather
1694 * than just sitting there in `ready to appear' state.
1697 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1699 case IDM_FULLSCREEN
:
1703 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1704 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1709 #define X_POS(l) ((int)(short)LOWORD(l))
1710 #define Y_POS(l) ((int)(short)HIWORD(l))
1712 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1713 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1714 #define WHEEL_DELTA 120
1717 wheel_accumulator
+= (short) HIWORD(wParam
);
1718 wParam
= LOWORD(wParam
);
1720 /* process events when the threshold is reached */
1721 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
1724 /* reduce amount for next time */
1725 if (wheel_accumulator
> 0) {
1727 wheel_accumulator
-= WHEEL_DELTA
;
1728 } else if (wheel_accumulator
< 0) {
1730 wheel_accumulator
+= WHEEL_DELTA
;
1734 if (send_raw_mouse
) {
1735 /* send a mouse-down followed by a mouse up */
1739 TO_CHR_X(X_POS(lParam
)),
1740 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1741 wParam
& MK_CONTROL
, is_alt_pressed());
1742 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1743 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1744 wParam
& MK_CONTROL
, is_alt_pressed());
1746 /* trigger a scroll */
1748 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
1753 case WM_LBUTTONDOWN
:
1754 case WM_MBUTTONDOWN
:
1755 case WM_RBUTTONDOWN
:
1763 case WM_LBUTTONDOWN
:
1767 case WM_MBUTTONDOWN
:
1768 button
= MBT_MIDDLE
;
1771 case WM_RBUTTONDOWN
:
1780 button
= MBT_MIDDLE
;
1788 button
= press
= 0; /* shouldn't happen */
1793 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1794 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
,
1798 term_mouse(button
, MA_RELEASE
,
1799 TO_CHR_X(X_POS(lParam
)),
1800 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1801 wParam
& MK_CONTROL
, is_alt_pressed());
1809 * Add the mouse position and message time to the random
1812 noise_ultralight(lParam
);
1814 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1816 if (wParam
& MK_LBUTTON
)
1818 else if (wParam
& MK_MBUTTON
)
1822 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1823 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1824 wParam
& MK_CONTROL
, is_alt_pressed());
1827 case WM_NCMOUSEMOVE
:
1829 noise_ultralight(lParam
);
1831 case WM_IGNORE_CLIP
:
1832 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1834 case WM_DESTROYCLIPBOARD
:
1837 ignore_clip
= FALSE
;
1843 hdc
= BeginPaint(hwnd
, &p
);
1845 SelectPalette(hdc
, pal
, TRUE
);
1846 RealizePalette(hdc
);
1849 (p
.rcPaint
.left
-offset_width
)/font_width
,
1850 (p
.rcPaint
.top
-offset_height
)/font_height
,
1851 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
1852 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
1855 p
.rcPaint
.left
< offset_width
||
1856 p
.rcPaint
.top
< offset_height
||
1857 p
.rcPaint
.right
>= offset_width
+ font_width
*cols
||
1858 p
.rcPaint
.bottom
>= offset_height
+ font_height
*rows
)
1860 HBRUSH fillcolour
, oldbrush
;
1862 fillcolour
= CreateSolidBrush (
1863 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1864 oldbrush
= SelectObject(hdc
, fillcolour
);
1865 edge
= CreatePen(PS_SOLID
, 0,
1866 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1867 oldpen
= SelectObject(hdc
, edge
);
1869 ExcludeClipRect(hdc
,
1870 offset_width
, offset_height
,
1871 offset_width
+font_width
*cols
,
1872 offset_height
+font_height
*rows
);
1874 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1875 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1877 // SelectClipRgn(hdc, NULL);
1879 SelectObject(hdc
, oldbrush
);
1880 DeleteObject(fillcolour
);
1881 SelectObject(hdc
, oldpen
);
1884 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
1885 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
1891 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1892 * but the only one that's likely to try to overload us is FD_READ.
1893 * This means buffering just one is fine.
1895 if (pending_netevent
)
1896 enact_pending_netevent();
1898 pending_netevent
= TRUE
;
1899 pend_netevent_wParam
= wParam
;
1900 pend_netevent_lParam
= lParam
;
1901 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
1902 enact_pending_netevent();
1904 time(&last_movement
);
1908 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
1910 flash_window(0); /* stop */
1922 case WM_ENTERSIZEMOVE
:
1923 #ifdef RDB_DEBUG_PATCH
1924 debug((27, "WM_ENTERSIZEMOVE"));
1928 need_backend_resize
= FALSE
;
1930 case WM_EXITSIZEMOVE
:
1933 #ifdef RDB_DEBUG_PATCH
1934 debug((27, "WM_EXITSIZEMOVE"));
1936 if (need_backend_resize
) {
1937 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1938 InvalidateRect(hwnd
, NULL
, TRUE
);
1943 * This does two jobs:
1944 * 1) Keep the sizetip uptodate
1945 * 2) Make sure the window size is _stepped_ in units of the font size.
1947 if (cfg
.resize_action
== RESIZE_TERM
&& !alt_pressed
) {
1948 int width
, height
, w
, h
, ew
, eh
;
1949 LPRECT r
= (LPRECT
) lParam
;
1951 if ( !need_backend_resize
&&
1952 (cfg
.height
!= rows
|| cfg
.width
!= cols
)) {
1954 * Great! It seems the host has been changing the terminal
1955 * size, well the user is now grabbing so this is probably
1956 * the least confusing solution in the long run even though
1957 * it a is suprise. Unfortunatly the only way to prevent
1958 * this seems to be to let the host change the window size
1959 * and as that's a user option we're still right back here.
1961 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1963 InvalidateRect(hwnd
, NULL
, TRUE
);
1964 need_backend_resize
= TRUE
;
1967 width
= r
->right
- r
->left
- extra_width
;
1968 height
= r
->bottom
- r
->top
- extra_height
;
1969 w
= (width
+ font_width
/ 2) / font_width
;
1972 h
= (height
+ font_height
/ 2) / font_height
;
1975 UpdateSizeTip(hwnd
, w
, h
);
1976 ew
= width
- w
* font_width
;
1977 eh
= height
- h
* font_height
;
1979 if (wParam
== WMSZ_LEFT
||
1980 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
1986 if (wParam
== WMSZ_TOP
||
1987 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
1997 int width
, height
, w
, h
, rv
= 0;
1998 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
1999 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2000 LPRECT r
= (LPRECT
) lParam
;
2002 width
= r
->right
- r
->left
- ex_width
;
2003 height
= r
->bottom
- r
->top
- ex_height
;
2005 w
= (width
+ cols
/2)/cols
;
2006 h
= (height
+ rows
/2)/rows
;
2007 if ( r
->right
!= r
->left
+ w
*cols
+ ex_width
)
2010 if (wParam
== WMSZ_LEFT
||
2011 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2012 r
->left
= r
->right
- w
*cols
- ex_width
;
2014 r
->right
= r
->left
+ w
*cols
+ ex_width
;
2016 if (r
->bottom
!= r
->top
+ h
*rows
+ ex_height
)
2019 if (wParam
== WMSZ_TOP
||
2020 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2021 r
->top
= r
->bottom
- h
*rows
- ex_height
;
2023 r
->bottom
= r
->top
+ h
*rows
+ ex_height
;
2027 /* break; (never reached) */
2029 #ifdef RDB_DEBUG_PATCH
2030 debug((27, "WM_SIZE %s (%d,%d)",
2031 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2032 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2033 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2034 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2036 LOWORD(lParam
), HIWORD(lParam
)));
2038 if (wParam
== SIZE_MINIMIZED
) {
2040 cfg
.win_name_always ? window_name
: icon_name
);
2043 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2044 SetWindowText(hwnd
, window_name
);
2046 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2047 /* A resize, well it better be a minimize. */
2051 int width
, height
, w
, h
;
2053 width
= LOWORD(lParam
);
2054 height
= HIWORD(lParam
);
2057 if (wParam
== SIZE_MAXIMIZED
) {
2061 if (cfg
.resize_action
!= RESIZE_FONT
) {
2062 w
= width
/ font_width
;
2064 h
= height
/ font_height
;
2067 term_size(h
, w
, cfg
.savelines
);
2070 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2072 if (cfg
.resize_action
!= RESIZE_FONT
)
2073 term_size(prev_rows
, prev_cols
, cfg
.savelines
);
2076 /* This is an unexpected resize, these will normally happen
2077 * if the window is too large. Probably either the user
2078 * selected a huge font or the screen size has changed.
2080 * This is also called with minimize.
2082 else reset_window(-1);
2086 * Don't call back->size in mid-resize. (To prevent
2087 * massive numbers of resize events getting sent
2088 * down the connection during an NT opaque drag.)
2091 if (cfg
.resize_action
== RESIZE_TERM
&& !alt_pressed
) {
2092 need_backend_resize
= TRUE
;
2093 w
= (width
-cfg
.window_border
*2) / font_width
;
2095 h
= (height
-cfg
.window_border
*2) / font_height
;
2106 switch (LOWORD(wParam
)) {
2120 term_scroll(0, +rows
/ 2);
2123 term_scroll(0, -rows
/ 2);
2125 case SB_THUMBPOSITION
:
2127 term_scroll(1, HIWORD(wParam
));
2131 case WM_PALETTECHANGED
:
2132 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2133 HDC hdc
= get_ctx();
2135 if (RealizePalette(hdc
) > 0)
2141 case WM_QUERYNEWPALETTE
:
2143 HDC hdc
= get_ctx();
2145 if (RealizePalette(hdc
) > 0)
2157 * Add the scan code and keypress timing to the random
2160 noise_ultralight(lParam
);
2163 * We don't do TranslateMessage since it disassociates the
2164 * resulting CHAR message from the KEYDOWN that sparked it,
2165 * which we occasionally don't want. Instead, we process
2166 * KEYDOWN, and call the Win32 translator functions so that
2167 * we get the translations under _our_ control.
2170 unsigned char buf
[20];
2173 if (wParam
== VK_PROCESSKEY
) {
2176 m
.message
= WM_KEYDOWN
;
2178 m
.lParam
= lParam
& 0xdfff;
2179 TranslateMessage(&m
);
2181 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2183 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2187 * Interrupt an ongoing paste. I'm not sure
2188 * this is sensible, but for the moment it's
2189 * preferable to having to faff about buffering
2195 * We need not bother about stdin backlogs
2196 * here, because in GUI PuTTY we can't do
2197 * anything about it anyway; there's no means
2198 * of asking Windows to hold off on KEYDOWN
2199 * messages. We _have_ to buffer everything
2202 ldisc_send(buf
, len
, 1);
2207 net_pending_errors();
2209 case WM_INPUTLANGCHANGE
:
2211 /* wParam == Font number */
2212 /* lParam == Locale */
2214 HKL NewInputLocale
= (HKL
) lParam
;
2216 // lParam == GetKeyboardLayout(0);
2218 GetLocaleInfo(LOWORD(NewInputLocale
),
2219 LOCALE_IDEFAULTANSICODEPAGE
, lbuf
, sizeof(lbuf
));
2221 kbd_codepage
= atoi(lbuf
);
2224 case WM_IME_COMPOSITION
:
2230 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2231 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2233 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2234 break; /* fall back to DefWindowProc */
2236 hIMC
= ImmGetContext(hwnd
);
2237 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2240 buff
= (char*) smalloc(n
);
2241 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2242 luni_send((unsigned short *)buff
, n
/ 2, 1);
2245 ImmReleaseContext(hwnd
, hIMC
);
2250 if (wParam
& 0xFF00) {
2251 unsigned char buf
[2];
2254 buf
[0] = wParam
>> 8;
2255 lpage_send(kbd_codepage
, buf
, 2, 1);
2257 char c
= (unsigned char) wParam
;
2258 lpage_send(kbd_codepage
, &c
, 1, 1);
2264 * Nevertheless, we are prepared to deal with WM_CHAR
2265 * messages, should they crop up. So if someone wants to
2266 * post the things to us as part of a macro manoeuvre,
2267 * we're ready to cope.
2270 char c
= (unsigned char)wParam
;
2271 lpage_send(CP_ACP
, &c
, 1, 1);
2275 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2276 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2281 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2285 * Move the system caret. (We maintain one, even though it's
2286 * invisible, for the benefit of blind people: apparently some
2287 * helper software tracks the system caret, so we should arrange to
2290 void sys_cursor(int x
, int y
)
2295 if (!has_focus
) return;
2297 SetCaretPos(x
* font_width
+ offset_width
,
2298 y
* font_height
+ offset_height
);
2300 /* IMM calls on Win98 and beyond only */
2301 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2303 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2304 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2306 /* we should have the IMM functions */
2307 hIMC
= ImmGetContext(hwnd
);
2308 cf
.dwStyle
= CFS_POINT
;
2309 cf
.ptCurrentPos
.x
= x
* font_width
;
2310 cf
.ptCurrentPos
.y
= y
* font_height
;
2311 ImmSetCompositionWindow(hIMC
, &cf
);
2313 ImmReleaseContext(hwnd
, hIMC
);
2317 * Draw a line of text in the window, at given character
2318 * coordinates, in given attributes.
2320 * We are allowed to fiddle with the contents of `text'.
2322 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2323 unsigned long attr
, int lattr
)
2326 int nfg
, nbg
, nfont
;
2329 int force_manual_underline
= 0;
2330 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2331 int char_width
= fnt_width
;
2332 int text_adjust
= 0;
2333 static int *IpDx
= 0, IpDxLEN
= 0;
2335 if (attr
& ATTR_WIDE
)
2338 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2340 if (len
> IpDxLEN
) {
2342 IpDx
= smalloc((len
+ 16) * sizeof(int));
2343 IpDxLEN
= (len
+ 16);
2345 for (i
= 0; i
< IpDxLEN
; i
++)
2346 IpDx
[i
] = char_width
;
2349 /* Only want the left half of double width lines */
2350 if (lattr
!= LATTR_NORM
&& x
*2 >= cols
)
2358 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
2359 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2360 attr
^= ATTR_CUR_XOR
;
2364 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2365 /* Assume a poorman font is borken in other ways too. */
2375 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2378 if (attr
& ATTR_NARROW
)
2379 nfont
|= FONT_NARROW
;
2381 /* Special hack for the VT100 linedraw glyphs. */
2382 if ((attr
& CSET_MASK
) == 0x2300) {
2383 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2384 switch ((unsigned char) (text
[0])) {
2386 text_adjust
= -2 * font_height
/ 5;
2389 text_adjust
= -1 * font_height
/ 5;
2392 text_adjust
= font_height
/ 5;
2395 text_adjust
= 2 * font_height
/ 5;
2398 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2401 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2402 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2403 if (attr
& ATTR_UNDER
) {
2404 attr
&= ~ATTR_UNDER
;
2405 force_manual_underline
= 1;
2410 /* Anything left as an original character set is unprintable. */
2411 if (DIRECT_CHAR(attr
)) {
2414 memset(text
, 0xFD, len
);
2418 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2421 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2422 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2423 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2425 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2426 nfont
|= FONT_UNDERLINE
;
2427 another_font(nfont
);
2428 if (!fonts
[nfont
]) {
2429 if (nfont
& FONT_UNDERLINE
)
2430 force_manual_underline
= 1;
2431 /* Don't do the same for manual bold, it could be bad news. */
2433 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2435 another_font(nfont
);
2437 nfont
= FONT_NORMAL
;
2438 if (attr
& ATTR_REVERSE
) {
2443 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2445 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2449 SelectObject(hdc
, fonts
[nfont
]);
2450 SetTextColor(hdc
, fg
);
2451 SetBkColor(hdc
, bg
);
2452 SetBkMode(hdc
, OPAQUE
);
2455 line_box
.right
= x
+ char_width
* len
;
2456 line_box
.bottom
= y
+ font_height
;
2458 /* Only want the left half of double width lines */
2459 if (line_box
.right
> font_width
*cols
+offset_width
)
2460 line_box
.right
= font_width
*cols
+offset_width
;
2462 /* We're using a private area for direct to font. (512 chars.) */
2463 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2464 /* Ho Hum, dbcs fonts are a PITA! */
2465 /* To display on W9x I have to convert to UCS */
2466 static wchar_t *uni_buf
= 0;
2467 static int uni_len
= 0;
2469 if (len
> uni_len
) {
2471 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2474 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2475 uni_buf
[nlen
] = 0xFFFD;
2476 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2477 IpDx
[nlen
] += char_width
;
2478 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2479 text
+mptr
, 2, uni_buf
+nlen
, 1);
2484 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2485 text
+mptr
, 1, uni_buf
+nlen
, 1);
2493 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2494 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2495 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2496 SetBkMode(hdc
, TRANSPARENT
);
2497 ExtTextOutW(hdc
, x
- 1,
2498 y
- font_height
* (lattr
==
2499 LATTR_BOT
) + text_adjust
,
2500 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2504 } else if (DIRECT_FONT(attr
)) {
2506 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2507 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2508 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2509 SetBkMode(hdc
, TRANSPARENT
);
2511 /* GRR: This draws the character outside it's box and can leave
2512 * 'droppings' even with the clip box! I suppose I could loop it
2513 * one character at a time ... yuk.
2515 * Or ... I could do a test print with "W", and use +1 or -1 for this
2516 * shift depending on if the leftmost column is blank...
2518 ExtTextOut(hdc
, x
- 1,
2519 y
- font_height
* (lattr
==
2520 LATTR_BOT
) + text_adjust
,
2521 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2524 /* And 'normal' unicode characters */
2525 static WCHAR
*wbuf
= NULL
;
2526 static int wlen
= 0;
2531 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2533 for (i
= 0; i
< len
; i
++)
2534 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2537 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2538 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2540 /* And the shadow bold hack. */
2541 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2542 SetBkMode(hdc
, TRANSPARENT
);
2543 ExtTextOutW(hdc
, x
- 1,
2544 y
- font_height
* (lattr
==
2545 LATTR_BOT
) + text_adjust
,
2546 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2549 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2550 (und_mode
== UND_LINE
2551 && (attr
& ATTR_UNDER
)))) {
2554 if (lattr
== LATTR_BOT
)
2555 dec
= dec
* 2 - font_height
;
2557 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2558 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2559 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2560 oldpen
= SelectObject(hdc
, oldpen
);
2561 DeleteObject(oldpen
);
2565 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2566 unsigned long attr
, int lattr
)
2572 int ctype
= cfg
.cursor_type
;
2574 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2575 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2576 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2580 attr
|= TATTR_RIGHTCURS
;
2583 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2584 if (attr
& ATTR_WIDE
)
2591 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2594 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2595 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2596 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2597 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2598 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2599 Polyline(hdc
, pts
, 5);
2600 oldpen
= SelectObject(hdc
, oldpen
);
2601 DeleteObject(oldpen
);
2602 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2603 int startx
, starty
, dx
, dy
, length
, i
;
2606 starty
= y
+ descent
;
2609 length
= char_width
;
2612 if (attr
& TATTR_RIGHTCURS
)
2613 xadjust
= char_width
- 1;
2614 startx
= x
+ xadjust
;
2618 length
= font_height
;
2620 if (attr
& TATTR_ACTCURS
) {
2623 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2624 MoveToEx(hdc
, startx
, starty
, NULL
);
2625 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2626 oldpen
= SelectObject(hdc
, oldpen
);
2627 DeleteObject(oldpen
);
2629 for (i
= 0; i
< length
; i
++) {
2631 SetPixel(hdc
, startx
, starty
, colours
[23]);
2640 /* This function gets the actual width of a character in the normal font.
2642 int CharWidth(Context ctx
, int uc
) {
2646 /* If the font max is the same as the font ave width then this
2647 * function is a no-op.
2649 if (!font_dualwidth
) return 1;
2651 switch (uc
& CSET_MASK
) {
2653 uc
= unitab_line
[uc
& 0xFF];
2656 uc
= unitab_xterm
[uc
& 0xFF];
2659 uc
= unitab_scoacs
[uc
& 0xFF];
2662 if (DIRECT_FONT(uc
)) {
2663 if (dbcs_screenfont
) return 1;
2665 /* Speedup, I know of no font where ascii is the wrong width */
2666 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
2669 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
2670 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2671 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
2672 another_font(FONT_OEM
);
2673 if (!fonts
[FONT_OEM
]) return 0;
2675 SelectObject(hdc
, fonts
[FONT_OEM
]);
2679 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
2680 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
2683 /* Speedup, I know of no font where ascii is the wrong width */
2684 if (uc
>= ' ' && uc
<= '~') return 1;
2686 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2687 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
2688 /* Okay that one worked */ ;
2689 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
2690 /* This should work on 9x too, but it's "less accurate" */ ;
2695 ibuf
+= font_width
/ 2 -1;
2702 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2703 * codes. Returns number of bytes used or zero to drop the message
2704 * or -1 to forward the message to windows.
2706 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2707 unsigned char *output
)
2710 int scan
, left_alt
= 0, key_down
, shift_state
;
2712 unsigned char *p
= output
;
2713 static int alt_sum
= 0;
2715 HKL kbd_layout
= GetKeyboardLayout(0);
2717 static WORD keys
[3];
2718 static int compose_char
= 0;
2719 static WPARAM compose_key
= 0;
2721 r
= GetKeyboardState(keystate
);
2723 memset(keystate
, 0, sizeof(keystate
));
2726 #define SHOW_TOASCII_RESULT
2727 { /* Tell us all about key events */
2728 static BYTE oldstate
[256];
2729 static int first
= 1;
2733 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2736 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2738 } else if ((HIWORD(lParam
) & KF_UP
)
2739 && scan
== (HIWORD(lParam
) & 0xFF)) {
2743 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2744 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2757 debug(("VK_%02x", wParam
));
2759 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2761 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2763 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2764 if (ch
>= ' ' && ch
<= '~')
2765 debug((", '%c'", ch
));
2767 debug((", $%02x", ch
));
2770 debug((", KB0=%02x", keys
[0]));
2772 debug((", KB1=%02x", keys
[1]));
2774 debug((", KB2=%02x", keys
[2]));
2776 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2778 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2780 if ((HIWORD(lParam
) & KF_EXTENDED
))
2782 if ((HIWORD(lParam
) & KF_UP
))
2786 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2787 else if ((HIWORD(lParam
) & KF_UP
))
2788 oldstate
[wParam
& 0xFF] ^= 0x80;
2790 oldstate
[wParam
& 0xFF] ^= 0x81;
2792 for (ch
= 0; ch
< 256; ch
++)
2793 if (oldstate
[ch
] != keystate
[ch
])
2794 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2796 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2800 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2801 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2805 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2806 if ((cfg
.funky_type
== 3 ||
2807 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2808 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2810 wParam
= VK_EXECUTE
;
2812 /* UnToggle NUMLock */
2813 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2814 keystate
[VK_NUMLOCK
] ^= 1;
2817 /* And write back the 'adjusted' state */
2818 SetKeyboardState(keystate
);
2821 /* Disable Auto repeat if required */
2822 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2825 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2828 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2830 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2831 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2832 if (cfg
.ctrlaltkeys
)
2833 keystate
[VK_MENU
] = 0;
2835 keystate
[VK_RMENU
] = 0x80;
2840 alt_pressed
= (left_alt
&& key_down
);
2842 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2843 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2844 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2846 /* Note if AltGr was pressed and if it was used as a compose key */
2847 if (!compose_state
) {
2848 compose_key
= 0x100;
2849 if (cfg
.compose_key
) {
2850 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2851 compose_key
= wParam
;
2853 if (wParam
== VK_APPS
)
2854 compose_key
= wParam
;
2857 if (wParam
== compose_key
) {
2858 if (compose_state
== 0
2859 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2861 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2865 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
2869 * Record that we pressed key so the scroll window can be reset, but
2870 * be careful to avoid Shift-UP/Down
2872 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
2876 if (compose_state
> 1 && left_alt
)
2879 /* Sanitize the number pad if not using a PC NumPad */
2880 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2881 && cfg
.funky_type
!= 2)
2882 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
2883 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
2887 nParam
= VK_NUMPAD0
;
2890 nParam
= VK_NUMPAD1
;
2893 nParam
= VK_NUMPAD2
;
2896 nParam
= VK_NUMPAD3
;
2899 nParam
= VK_NUMPAD4
;
2902 nParam
= VK_NUMPAD5
;
2905 nParam
= VK_NUMPAD6
;
2908 nParam
= VK_NUMPAD7
;
2911 nParam
= VK_NUMPAD8
;
2914 nParam
= VK_NUMPAD9
;
2917 nParam
= VK_DECIMAL
;
2921 if (keystate
[VK_NUMLOCK
] & 1)
2928 /* If a key is pressed and AltGr is not active */
2929 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
2930 /* Okay, prepare for most alts then ... */
2934 /* Lets see if it's a pattern we know all about ... */
2935 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2936 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2939 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2940 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
2943 if (wParam
== VK_INSERT
&& shift_state
== 1) {
2947 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
2950 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
2951 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
2954 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
) {
2958 /* Control-Numlock for app-keypad mode switch */
2959 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
2960 app_keypad_keys
^= 1;
2964 /* Nethack keypad */
2965 if (cfg
.nethack_keypad
&& !left_alt
) {
2968 *p
++ = shift_state ?
'B' : 'b';
2971 *p
++ = shift_state ?
'J' : 'j';
2974 *p
++ = shift_state ?
'N' : 'n';
2977 *p
++ = shift_state ?
'H' : 'h';
2980 *p
++ = shift_state ?
'.' : '.';
2983 *p
++ = shift_state ?
'L' : 'l';
2986 *p
++ = shift_state ?
'Y' : 'y';
2989 *p
++ = shift_state ?
'K' : 'k';
2992 *p
++ = shift_state ?
'U' : 'u';
2997 /* Application Keypad */
3001 if (cfg
.funky_type
== 3 ||
3002 (cfg
.funky_type
<= 1 &&
3003 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3017 if (app_keypad_keys
&& !cfg
.no_applic_k
)
3054 if (cfg
.funky_type
== 2) {
3059 } else if (shift_state
)
3066 if (cfg
.funky_type
== 2)
3070 if (cfg
.funky_type
== 2)
3074 if (cfg
.funky_type
== 2)
3079 if (HIWORD(lParam
) & KF_EXTENDED
)
3085 if (xkey
>= 'P' && xkey
<= 'S')
3086 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3088 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3090 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3095 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3096 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3100 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3106 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3110 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3114 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3119 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3124 /* Control-2 to Control-8 are special */
3125 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3126 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3129 if (shift_state
== 2 && wParam
== 0xBD) {
3133 if (shift_state
== 2 && wParam
== 0xDF) {
3137 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
3144 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3145 * for integer decimal nn.)
3147 * We also deal with the weird ones here. Linux VCs replace F1
3148 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3149 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3155 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3158 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3161 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3164 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3167 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3170 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3173 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3176 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3179 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3182 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3215 if ((shift_state
&2) == 0) switch (wParam
) {
3235 /* Reorder edit keys to physical order */
3236 if (cfg
.funky_type
== 3 && code
<= 6)
3237 code
= "\0\2\1\4\5\3\6"[code
];
3239 if (vt52_mode
&& code
> 0 && code
<= 6) {
3240 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3244 if (cfg
.funky_type
== 5 && /* SCO function keys */
3245 code
>= 11 && code
<= 34) {
3246 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3249 case VK_F1
: index
= 0; break;
3250 case VK_F2
: index
= 1; break;
3251 case VK_F3
: index
= 2; break;
3252 case VK_F4
: index
= 3; break;
3253 case VK_F5
: index
= 4; break;
3254 case VK_F6
: index
= 5; break;
3255 case VK_F7
: index
= 6; break;
3256 case VK_F8
: index
= 7; break;
3257 case VK_F9
: index
= 8; break;
3258 case VK_F10
: index
= 9; break;
3259 case VK_F11
: index
= 10; break;
3260 case VK_F12
: index
= 11; break;
3262 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3263 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3264 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3267 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3268 code
>= 1 && code
<= 6) {
3269 char codes
[] = "HL.FIG";
3273 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3277 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3284 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3287 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3290 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3291 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3294 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3296 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3298 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3301 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3302 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3306 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3311 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3312 * some reason seems to send VK_CLEAR to Windows...).
3335 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3337 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
3338 /* VT100 & VT102 manuals both state the app cursor keys
3339 * only work if the app keypad is on.
3341 if (!app_keypad_keys
)
3343 /* Useful mapping of Ctrl-arrows */
3344 if (shift_state
== 2)
3348 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3350 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3357 * Finally, deal with Return ourselves. (Win95 seems to
3358 * foul it up when Alt is pressed, for some reason.)
3360 if (wParam
== VK_RETURN
) { /* Return */
3366 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3367 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3372 /* Okay we've done everything interesting; let windows deal with
3373 * the boring stuff */
3377 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3378 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3380 keystate
[VK_CAPITAL
] = 0;
3383 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3384 #ifdef SHOW_TOASCII_RESULT
3385 if (r
== 1 && !key_down
) {
3387 if (in_utf
|| dbcs_screenfont
)
3388 debug((", (U+%04x)", alt_sum
));
3390 debug((", LCH(%d)", alt_sum
));
3392 debug((", ACH(%d)", keys
[0]));
3397 for (r1
= 0; r1
< r
; r1
++) {
3398 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3407 * Interrupt an ongoing paste. I'm not sure this is
3408 * sensible, but for the moment it's preferable to
3409 * having to faff about buffering things.
3414 for (i
= 0; i
< r
; i
++) {
3415 unsigned char ch
= (unsigned char) keys
[i
];
3417 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3422 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3426 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3427 MessageBeep(MB_ICONHAND
);
3431 luni_send(&keybuf
, 1, 1);
3439 if (in_utf
|| dbcs_screenfont
) {
3441 luni_send(&keybuf
, 1, 1);
3443 ch
= (char) alt_sum
;
3445 * We need not bother about stdin
3446 * backlogs here, because in GUI PuTTY
3447 * we can't do anything about it
3448 * anyway; there's no means of asking
3449 * Windows to hold off on KEYDOWN
3450 * messages. We _have_ to buffer
3451 * everything we're sent.
3453 ldisc_send(&ch
, 1, 1);
3457 lpage_send(kbd_codepage
, &ch
, 1, 1);
3459 if(capsOn
&& ch
< 0x80) {
3462 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3463 luni_send(cbuf
+!left_alt
, 1+!!left_alt
, 1);
3468 lpage_send(kbd_codepage
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3474 /* This is so the ALT-Numpad and dead keys work correctly. */
3479 /* If we're definitly not building up an ALT-54321 then clear it */
3482 /* If we will be using alt_sum fix the 256s */
3483 else if (keys
[0] && (in_utf
|| dbcs_screenfont
))
3488 * ALT alone may or may not want to bring up the System menu.
3489 * If it's not meant to, we return 0 on presses or releases of
3490 * ALT, to show that we've swallowed the keystroke. Otherwise
3491 * we return -1, which means Windows will give the keystroke
3492 * its default handling (i.e. bring up the System menu).
3494 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3500 void set_title(char *title
)
3503 window_name
= smalloc(1 + strlen(title
));
3504 strcpy(window_name
, title
);
3505 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3506 SetWindowText(hwnd
, title
);
3509 void set_icon(char *title
)
3512 icon_name
= smalloc(1 + strlen(title
));
3513 strcpy(icon_name
, title
);
3514 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3515 SetWindowText(hwnd
, title
);
3518 void set_sbar(int total
, int start
, int page
)
3525 si
.cbSize
= sizeof(si
);
3526 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3528 si
.nMax
= total
- 1;
3532 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3535 Context
get_ctx(void)
3541 SelectPalette(hdc
, pal
, FALSE
);
3547 void free_ctx(Context ctx
)
3549 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3550 ReleaseDC(hwnd
, ctx
);
3553 static void real_palette_set(int n
, int r
, int g
, int b
)
3556 logpal
->palPalEntry
[n
].peRed
= r
;
3557 logpal
->palPalEntry
[n
].peGreen
= g
;
3558 logpal
->palPalEntry
[n
].peBlue
= b
;
3559 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3560 colours
[n
] = PALETTERGB(r
, g
, b
);
3561 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3563 colours
[n
] = RGB(r
, g
, b
);
3566 void palette_set(int n
, int r
, int g
, int b
)
3568 static const int first
[21] = {
3569 0, 2, 4, 6, 8, 10, 12, 14,
3570 1, 3, 5, 7, 9, 11, 13, 15,
3573 real_palette_set(first
[n
], r
, g
, b
);
3575 real_palette_set(first
[n
] + 1, r
, g
, b
);
3577 HDC hdc
= get_ctx();
3578 UnrealizeObject(pal
);
3579 RealizePalette(hdc
);
3584 void palette_reset(void)
3588 for (i
= 0; i
< NCOLOURS
; i
++) {
3590 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3591 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3592 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3593 logpal
->palPalEntry
[i
].peFlags
= 0;
3594 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3595 defpal
[i
].rgbtGreen
,
3596 defpal
[i
].rgbtBlue
);
3598 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3599 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3604 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3606 RealizePalette(hdc
);
3611 void write_aclip(char *data
, int len
, int must_deselect
)
3616 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3619 lock
= GlobalLock(clipdata
);
3622 memcpy(lock
, data
, len
);
3623 ((unsigned char *) lock
)[len
] = 0;
3624 GlobalUnlock(clipdata
);
3627 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3629 if (OpenClipboard(hwnd
)) {
3631 SetClipboardData(CF_TEXT
, clipdata
);
3634 GlobalFree(clipdata
);
3637 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3641 * Note: unlike write_aclip() this will not append a nul.
3643 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3650 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3652 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3653 len
* sizeof(wchar_t));
3654 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3656 if (!clipdata
|| !clipdata2
) {
3658 GlobalFree(clipdata
);
3660 GlobalFree(clipdata2
);
3663 if (!(lock
= GlobalLock(clipdata
)))
3665 if (!(lock2
= GlobalLock(clipdata2
)))
3668 memcpy(lock
, data
, len
* sizeof(wchar_t));
3669 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3671 GlobalUnlock(clipdata
);
3672 GlobalUnlock(clipdata2
);
3675 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3677 if (OpenClipboard(hwnd
)) {
3679 SetClipboardData(CF_UNICODETEXT
, clipdata
);
3680 SetClipboardData(CF_TEXT
, clipdata2
);
3683 GlobalFree(clipdata
);
3684 GlobalFree(clipdata2
);
3688 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3691 void get_clip(wchar_t ** p
, int *len
)
3693 static HGLOBAL clipdata
= NULL
;
3694 static wchar_t *converted
= 0;
3703 GlobalUnlock(clipdata
);
3706 } else if (OpenClipboard(NULL
)) {
3707 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
3709 *p
= GlobalLock(clipdata
);
3711 for (p2
= *p
; *p2
; p2
++);
3715 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
3719 s
= GlobalLock(clipdata
);
3720 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
3721 *p
= converted
= smalloc(i
* sizeof(wchar_t));
3722 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
3735 * Move `lines' lines from position `from' to position `to' in the
3738 void optimised_move(int to
, int from
, int lines
)
3743 min
= (to
< from ? to
: from
);
3744 max
= to
+ from
- min
;
3746 r
.left
= offset_width
;
3747 r
.right
= offset_width
+ cols
* font_width
;
3748 r
.top
= offset_height
+ min
* font_height
;
3749 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
3750 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
3755 * Print a message box and perform a fatal exit.
3757 void fatalbox(char *fmt
, ...)
3763 vsprintf(stuff
, fmt
, ap
);
3765 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
3770 * Manage window caption / taskbar flashing, if enabled.
3771 * 0 = stop, 1 = maintain, 2 = start
3773 static void flash_window(int mode
)
3775 static long last_flash
= 0;
3776 static int flashing
= 0;
3777 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
3780 FlashWindow(hwnd
, FALSE
);
3784 } else if (mode
== 2) {
3787 last_flash
= GetTickCount();
3789 FlashWindow(hwnd
, TRUE
);
3792 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
3795 long now
= GetTickCount();
3796 long fdiff
= now
- last_flash
;
3797 if (fdiff
< 0 || fdiff
> 450) {
3799 FlashWindow(hwnd
, TRUE
); /* toggle */
3810 if (mode
== BELL_DEFAULT
) {
3812 * For MessageBeep style bells, we want to be careful of
3813 * timing, because they don't have the nice property of
3814 * PlaySound bells that each one cancels the previous
3815 * active one. So we limit the rate to one per 50ms or so.
3817 static long lastbeep
= 0;
3820 beepdiff
= GetTickCount() - lastbeep
;
3821 if (beepdiff
>= 0 && beepdiff
< 50)
3825 * The above MessageBeep call takes time, so we record the
3826 * time _after_ it finishes rather than before it starts.
3828 lastbeep
= GetTickCount();
3829 } else if (mode
== BELL_WAVEFILE
) {
3830 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
3831 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
3832 sprintf(buf
, "Unable to play sound file\n%s\n"
3833 "Using default sound instead", cfg
.bell_wavefile
);
3834 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
3835 MB_OK
| MB_ICONEXCLAMATION
);
3836 cfg
.beep
= BELL_DEFAULT
;
3839 /* Otherwise, either visual bell or disabled; do nothing here */
3841 flash_window(2); /* start */
3846 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3848 * Revised by <wez@thebrainroom.com>
3850 static void flip_full_screen(void)
3855 wp
.length
= sizeof(wp
);
3856 GetWindowPlacement(hwnd
, &wp
);
3858 full_screen
= !full_screen
;
3861 if (wp
.showCmd
== SW_SHOWMAXIMIZED
) {
3862 /* Ooops it was already 'zoomed' we have to unzoom it before
3863 * everything will work right.
3865 wp
.showCmd
= SW_SHOWNORMAL
;
3866 SetWindowPlacement(hwnd
, &wp
);
3869 style
= GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_CAPTION
;
3870 style
&= ~WS_VSCROLL
;
3871 if (cfg
.scrollbar_in_fullscreen
)
3872 style
|= WS_VSCROLL
;
3873 SetWindowLong(hwnd
, GWL_STYLE
, style
);
3875 /* This seems to be needed otherwize explorer doesn't notice
3876 * we want to go fullscreen and it's bar is still visible
3878 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
3879 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
3880 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
3883 wp
.showCmd
= SW_SHOWMAXIMIZED
;
3884 SetWindowPlacement(hwnd
, &wp
);
3886 style
= GetWindowLong(hwnd
, GWL_STYLE
) | WS_CAPTION
;
3887 style
&= ~WS_VSCROLL
;
3889 style
|= WS_VSCROLL
;
3890 SetWindowLong(hwnd
, GWL_STYLE
, style
);
3892 /* Don't need to do a SetWindowPos as the resize will force a
3895 wp
.showCmd
= SW_SHOWNORMAL
;
3896 SetWindowPlacement(hwnd
, &wp
);
3899 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
3900 MF_BYCOMMAND
| full_screen ? MF_CHECKED
: MF_UNCHECKED
);