13 #if (WINVER < 0x0500) && !defined(NO_MULTIMON)
14 #define COMPILE_MULTIMON_STUBS
23 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
29 #define IDM_SHOWLOG 0x0010
30 #define IDM_NEWSESS 0x0020
31 #define IDM_DUPSESS 0x0030
32 #define IDM_RECONF 0x0040
33 #define IDM_CLRSB 0x0050
34 #define IDM_RESET 0x0060
35 #define IDM_TEL_AYT 0x0070
36 #define IDM_TEL_BRK 0x0080
37 #define IDM_TEL_SYNCH 0x0090
38 #define IDM_TEL_EC 0x00a0
39 #define IDM_TEL_EL 0x00b0
40 #define IDM_TEL_GA 0x00c0
41 #define IDM_TEL_NOP 0x00d0
42 #define IDM_TEL_ABORT 0x00e0
43 #define IDM_TEL_AO 0x00f0
44 #define IDM_TEL_IP 0x0100
45 #define IDM_TEL_SUSP 0x0110
46 #define IDM_TEL_EOR 0x0120
47 #define IDM_TEL_EOF 0x0130
48 #define IDM_ABOUT 0x0140
49 #define IDM_SAVEDSESS 0x0150
50 #define IDM_COPYALL 0x0160
51 #define IDM_FULLSCREEN 0x0170
53 #define IDM_SESSLGP 0x0250 /* log type printable */
54 #define IDM_SESSLGA 0x0260 /* log type all chars */
55 #define IDM_SESSLGE 0x0270 /* log end */
56 #define IDM_SAVED_MIN 0x1000
57 #define IDM_SAVED_MAX 0x2000
59 #define WM_IGNORE_CLIP (WM_XUSER + 2)
61 /* Needed for Chinese support and apparently not always defined. */
63 #define VK_PROCESSKEY 0xE5
66 /* Needed for mouse wheel support and not defined in earlier SDKs. */
68 #define WM_MOUSEWHEEL 0x020A
71 static LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
72 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
73 unsigned char *output
);
74 static void cfgtopalette(void);
75 static void init_palette(void);
76 static void init_fonts(int, int);
77 static void another_font(int);
78 static void deinit_fonts(void);
80 /* Window layout information */
81 static void reset_window(int);
82 static int full_screen
= 0, want_full_screen
= 0;
83 static int extra_width
, extra_height
;
84 static int font_width
, font_height
, font_dualwidth
;
85 static int offset_width
, offset_height
;
86 static int was_zoomed
= 0;
87 static int was_full_screen
= 0;
88 static int prev_rows
, prev_cols
;
89 static int pre_fs_rows
, pre_fs_cols
;
91 static LONG old_wind_style
;
92 static WINDOWPLACEMENT old_wind_placement
;
94 static int pending_netevent
= 0;
95 static WPARAM pend_netevent_wParam
= 0;
96 static LPARAM pend_netevent_lParam
= 0;
97 static void enact_pending_netevent(void);
98 static void flash_window(int mode
);
99 static void flip_full_screen(void);
101 static time_t last_movement
= 0;
103 #define FONT_NORMAL 0
105 #define FONT_UNDERLINE 2
106 #define FONT_BOLDUND 3
107 #define FONT_WIDE 0x04
108 #define FONT_HIGH 0x08
109 #define FONT_NARROW 0x10
111 #define FONT_OEM 0x20
112 #define FONT_OEMBOLD 0x21
113 #define FONT_OEMUND 0x22
114 #define FONT_OEMBOLDUND 0x23
116 #define FONT_MAXNO 0x2F
118 static HFONT fonts
[FONT_MAXNO
];
119 static int fontflag
[FONT_MAXNO
];
121 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
129 static COLORREF colours
[NCOLOURS
];
131 static LPLOGPALETTE logpal
;
132 static RGBTRIPLE defpal
[NCOLOURS
];
136 static HBITMAP caretbm
;
138 static int dbltime
, lasttime
, lastact
;
139 static Mouse_Button lastbtn
;
141 /* this allows xterm-style mouse handling. */
142 static int send_raw_mouse
= 0;
143 static int wheel_accumulator
= 0;
145 static char *window_name
, *icon_name
;
147 static int compose_state
= 0;
149 static OSVERSIONINFO osVersion
;
151 /* Dummy routine, only required in plink. */
152 void ldisc_update(int echo
, int edit
)
156 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
158 static char appname
[] = "PuTTY";
163 int guess_width
, guess_height
;
166 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
168 winsock_ver
= MAKEWORD(1, 1);
169 if (WSAStartup(winsock_ver
, &wsadata
)) {
170 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
171 MB_OK
| MB_ICONEXCLAMATION
);
174 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
175 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
176 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
180 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
183 InitCommonControls();
185 /* Ensure a Maximize setting in Explorer doesn't maximise the
190 ZeroMemory(&osVersion
, sizeof(osVersion
));
191 osVersion
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
192 if (!GetVersionEx ( (OSVERSIONINFO
*) &osVersion
)) {
193 MessageBox(NULL
, "Windows refuses to report a version",
194 "PuTTY Fatal Error", MB_OK
| MB_ICONEXCLAMATION
);
200 * Process the command line.
205 default_protocol
= DEFAULT_PROTOCOL
;
206 default_port
= DEFAULT_PORT
;
207 cfg
.logtype
= LGTYP_NONE
;
209 do_defaults(NULL
, &cfg
);
212 while (*p
&& isspace(*p
))
216 * Process command line options first. Yes, this can be
217 * done better, and it will be as soon as I have the
221 char *q
= p
+ strcspn(p
, " \t");
224 tolower(p
[0]) == 's' &&
225 tolower(p
[1]) == 's' && tolower(p
[2]) == 'h') {
226 default_protocol
= cfg
.protocol
= PROT_SSH
;
227 default_port
= cfg
.port
= 22;
228 } else if (q
== p
+ 7 &&
229 tolower(p
[0]) == 'c' &&
230 tolower(p
[1]) == 'l' &&
231 tolower(p
[2]) == 'e' &&
232 tolower(p
[3]) == 'a' &&
233 tolower(p
[4]) == 'n' &&
234 tolower(p
[5]) == 'u' && tolower(p
[6]) == 'p') {
236 * `putty -cleanup'. Remove all registry entries
237 * associated with PuTTY, and also find and delete
238 * the random seed file.
241 "This procedure will remove ALL Registry\n"
242 "entries associated with PuTTY, and will\n"
243 "also remove the PuTTY random seed file.\n"
245 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
246 "SESSIONS. Are you really sure you want\n"
249 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
254 p
= q
+ strspn(q
, " \t");
258 * An initial @ means to activate a saved session.
262 while (i
> 1 && isspace(p
[i
- 1]))
265 do_defaults(p
+ 1, &cfg
);
266 if (!*cfg
.host
&& !do_config()) {
270 } else if (*p
== '&') {
272 * An initial & means we've been given a command line
273 * containing the hex value of a HANDLE for a file
274 * mapping object, which we must then extract as a
279 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
280 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
281 0, 0, sizeof(Config
))) != NULL
) {
284 CloseHandle(filemap
);
285 } else if (!do_config()) {
292 * If the hostname starts with "telnet:", set the
293 * protocol to Telnet and process the string as a
296 if (!strncmp(q
, "telnet:", 7)) {
300 if (q
[0] == '/' && q
[1] == '/')
302 cfg
.protocol
= PROT_TELNET
;
304 while (*p
&& *p
!= ':' && *p
!= '/')
313 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
314 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
316 while (*p
&& !isspace(*p
))
320 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
321 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
322 while (*p
&& isspace(*p
))
337 * Trim leading whitespace off the hostname if it's there.
340 int space
= strspn(cfg
.host
, " \t");
341 memmove(cfg
.host
, cfg
.host
+space
, 1+strlen(cfg
.host
)-space
);
344 /* See if host is of the form user@host */
345 if (cfg
.host
[0] != '\0') {
346 char *atsign
= strchr(cfg
.host
, '@');
347 /* Make sure we're not overflowing the user field */
349 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
350 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
351 cfg
.username
[atsign
- cfg
.host
] = '\0';
353 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
358 * Trim a colon suffix off the hostname if it's there.
360 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
364 * Select protocol. This is farmed out into a table in a
365 * separate file to enable an ssh-free variant.
370 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
371 if (backends
[i
].protocol
== cfg
.protocol
) {
372 back
= backends
[i
].backend
;
376 MessageBox(NULL
, "Unsupported protocol number found",
377 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
383 /* Check for invalid Port number (i.e. zero) */
385 MessageBox(NULL
, "Invalid Port Number",
386 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
393 wndclass
.lpfnWndProc
= WndProc
;
394 wndclass
.cbClsExtra
= 0;
395 wndclass
.cbWndExtra
= 0;
396 wndclass
.hInstance
= inst
;
397 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
398 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
399 wndclass
.hbrBackground
= NULL
;
400 wndclass
.lpszMenuName
= NULL
;
401 wndclass
.lpszClassName
= appname
;
403 RegisterClass(&wndclass
);
408 savelines
= cfg
.savelines
;
414 * Guess some defaults for the window size. This all gets
415 * updated later, so we don't really care too much. However, we
416 * do want the font width/height guesses to correspond to a
417 * large font rather than a small one...
424 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
425 guess_width
= extra_width
+ font_width
* cols
;
426 guess_height
= extra_height
+ font_height
* rows
;
429 HWND w
= GetDesktopWindow();
430 GetWindowRect(w
, &r
);
431 if (guess_width
> r
.right
- r
.left
)
432 guess_width
= r
.right
- r
.left
;
433 if (guess_height
> r
.bottom
- r
.top
)
434 guess_height
= r
.bottom
- r
.top
;
438 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
441 winmode
&= ~(WS_VSCROLL
);
442 if (cfg
.resize_action
== RESIZE_DISABLED
)
443 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
445 exwinmode
|= WS_EX_TOPMOST
;
447 exwinmode
|= WS_EX_CLIENTEDGE
;
448 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
449 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
450 guess_width
, guess_height
,
451 NULL
, NULL
, inst
, NULL
);
455 * Initialise the fonts, simultaneously correcting the guesses
456 * for font_{width,height}.
461 * Correct the guesses for extra_{width,height}.
465 GetWindowRect(hwnd
, &wr
);
466 GetClientRect(hwnd
, &cr
);
467 offset_width
= offset_height
= cfg
.window_border
;
468 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
469 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
473 * Resize the window, now we know what size we _really_ want it
476 guess_width
= extra_width
+ font_width
* cols
;
477 guess_height
= extra_height
+ font_height
* rows
;
478 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
479 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
482 * Set up a caret bitmap, with no content.
486 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
487 bits
= smalloc(size
);
488 memset(bits
, 0, size
);
489 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
492 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
495 * Initialise the scroll bar.
500 si
.cbSize
= sizeof(si
);
501 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
506 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
510 * Start up the telnet connection.
514 char msg
[1024], *title
;
517 error
= back
->init(cfg
.host
, cfg
.port
, &realhost
);
519 sprintf(msg
, "Unable to open connection to\n"
520 "%.800s\n" "%s", cfg
.host
, error
);
521 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
524 window_name
= icon_name
= NULL
;
526 title
= cfg
.wintitle
;
528 sprintf(msg
, "%s - PuTTY", realhost
);
536 session_closed
= FALSE
;
539 * Prepare the mouse handler.
541 lastact
= MA_NOTHING
;
542 lastbtn
= MBT_NOTHING
;
543 dbltime
= GetDoubleClickTime();
546 * Set up the session-control options on the system menu.
549 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
553 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
554 if (cfg
.protocol
== PROT_TELNET
) {
556 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
557 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
558 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
559 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
560 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
561 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
562 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
563 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
564 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
565 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
566 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
567 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
568 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
569 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
570 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
571 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
572 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
574 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
576 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
577 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
578 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
579 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
582 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
583 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
585 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
586 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
587 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
588 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
589 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
590 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
591 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
592 AppendMenu(m
, MF_ENABLED
, IDM_FULLSCREEN
, "&Full Screen");
593 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
594 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
598 * Finally show the window!
600 ShowWindow(hwnd
, show
);
601 SetForegroundWindow(hwnd
);
604 * Open the initial log file if there is one.
609 * Set the palette up.
615 has_focus
= (GetForegroundWindow() == hwnd
);
618 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
619 int timer_id
= 0, long_timer
= 0;
621 while (msg
.message
!= WM_QUIT
) {
622 /* Sometimes DispatchMessage calls routines that use their own
623 * GetMessage loop, setup this timer so we get some control back.
625 * Also call term_update() from the timer so that if the host
626 * is sending data flat out we still do redraws.
628 if (timer_id
&& long_timer
) {
629 KillTimer(hwnd
, timer_id
);
630 long_timer
= timer_id
= 0;
633 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
634 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
635 DispatchMessage(&msg
);
637 /* Make sure we blink everything that needs it. */
640 /* Send the paste buffer if there's anything to send */
643 /* If there's nothing new in the queue then we can do everything
644 * we've delayed, reading the socket, writing, and repainting
647 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
650 if (pending_netevent
) {
651 enact_pending_netevent();
653 /* Force the cursor blink on */
656 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
660 /* Okay there is now nothing to do so we make sure the screen is
661 * completely up to date then tell windows to call us in a little
665 KillTimer(hwnd
, timer_id
);
673 flash_window(1); /* maintain */
676 /* Hmm, term_update didn't want to do an update too soon ... */
677 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
679 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
681 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
684 /* There's no point rescanning everything in the message queue
685 * so we do an apparently unnecessary wait here
688 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
702 if (cfg
.protocol
== PROT_SSH
) {
713 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
715 char *do_select(SOCKET skt
, int startup
)
720 events
= (FD_CONNECT
| FD_READ
| FD_WRITE
|
721 FD_OOB
| FD_CLOSE
| FD_ACCEPT
);
726 return "do_select(): internal error (hwnd==NULL)";
727 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
728 switch (WSAGetLastError()) {
730 return "Network is down";
732 return "WSAAsyncSelect(): unknown error";
739 * set or clear the "raw mouse message" mode
741 void set_raw_mouse_mode(int activate
)
743 send_raw_mouse
= activate
;
744 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
748 * Print a message box and close the connection.
750 void connection_fatal(char *fmt
, ...)
756 vsprintf(stuff
, fmt
, ap
);
758 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
759 if (cfg
.close_on_exit
== COE_ALWAYS
)
762 session_closed
= TRUE
;
763 SetWindowText(hwnd
, "PuTTY (inactive)");
768 * Actually do the job requested by a WM_NETEVENT
770 static void enact_pending_netevent(void)
772 static int reentering
= 0;
773 extern int select_result(WPARAM
, LPARAM
);
777 return; /* don't unpend the pending */
779 pending_netevent
= FALSE
;
782 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
785 if (ret
== 0 && !session_closed
) {
786 /* Abnormal exits will already have set session_closed and taken
787 * appropriate action. */
788 if (cfg
.close_on_exit
== COE_ALWAYS
||
789 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
791 session_closed
= TRUE
;
792 SetWindowText(hwnd
, "PuTTY (inactive)");
793 MessageBox(hwnd
, "Connection closed by remote host",
794 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
800 * Copy the colour palette from the configuration data into defpal.
801 * This is non-trivial because the colour indices are different.
803 static void cfgtopalette(void)
806 static const int ww
[] = {
807 6, 7, 8, 9, 10, 11, 12, 13,
808 14, 15, 16, 17, 18, 19, 20, 21,
809 0, 1, 2, 3, 4, 4, 5, 5
812 for (i
= 0; i
< 24; i
++) {
814 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
815 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
816 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
821 * Set up the colour palette.
823 static void init_palette(void)
826 HDC hdc
= GetDC(hwnd
);
828 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
829 logpal
= smalloc(sizeof(*logpal
)
830 - sizeof(logpal
->palPalEntry
)
831 + NCOLOURS
* sizeof(PALETTEENTRY
));
832 logpal
->palVersion
= 0x300;
833 logpal
->palNumEntries
= NCOLOURS
;
834 for (i
= 0; i
< NCOLOURS
; i
++) {
835 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
836 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
837 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
838 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
840 pal
= CreatePalette(logpal
);
842 SelectPalette(hdc
, pal
, FALSE
);
844 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
847 ReleaseDC(hwnd
, hdc
);
850 for (i
= 0; i
< NCOLOURS
; i
++)
851 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
855 for (i
= 0; i
< NCOLOURS
; i
++)
856 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
857 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
861 * Initialise all the fonts we will need initially. There may be as many as
862 * three or as few as one. The other (poentially) twentyone fonts are done
863 * if/when they are needed.
867 * - check the font width and height, correcting our guesses if
870 * - verify that the bold font is the same width as the ordinary
871 * one, and engage shadow bolding if not.
873 * - verify that the underlined font is the same width as the
874 * ordinary one (manual underlining by means of line drawing can
875 * be done in a pinch).
877 static void init_fonts(int pick_width
, int pick_height
)
884 int fw_dontcare
, fw_bold
;
886 for (i
= 0; i
< FONT_MAXNO
; i
++)
889 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
892 if (cfg
.fontisbold
) {
893 fw_dontcare
= FW_BOLD
;
896 fw_dontcare
= FW_DONTCARE
;
903 font_height
= pick_height
;
905 font_height
= cfg
.fontheight
;
906 if (font_height
> 0) {
908 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
911 font_width
= pick_width
;
914 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
915 c, OUT_DEFAULT_PRECIS, \
916 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
917 FIXED_PITCH | FF_DONTCARE, cfg.font)
919 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
921 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
922 GetTextMetrics(hdc
, &tm
);
924 if (pick_width
== 0 || pick_height
== 0) {
925 font_height
= tm
.tmHeight
;
926 font_width
= tm
.tmAveCharWidth
;
928 font_dualwidth
= (tm
.tmAveCharWidth
!= tm
.tmMaxCharWidth
);
930 #ifdef RDB_DEBUG_PATCH
931 debug(23, "Primary font H=%d, AW=%d, MW=%d",
932 tm
.tmHeight
, tm
.tmAveCharWidth
, tm
.tmMaxCharWidth
);
937 DWORD cset
= tm
.tmCharSet
;
938 memset(&info
, 0xFF, sizeof(info
));
940 /* !!! Yes the next line is right */
941 if (cset
== OEM_CHARSET
)
942 font_codepage
= GetOEMCP();
944 if (TranslateCharsetInfo
945 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
950 GetCPInfo(font_codepage
, &cpinfo
);
951 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
954 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
957 * Some fonts, e.g. 9-pt Courier, draw their underlines
958 * outside their character cell. We successfully prevent
959 * screen corruption by clipping the text output, but then
960 * we lose the underline completely. Here we try to work
961 * out whether this is such a font, and if it is, we set a
962 * flag that causes underlines to be drawn by hand.
964 * Having tried other more sophisticated approaches (such
965 * as examining the TEXTMETRIC structure or requesting the
966 * height of a string), I think we'll do this the brute
967 * force way: we create a small bitmap, draw an underlined
968 * space on it, and test to see whether any pixels are
969 * foreground-coloured. (Since we expect the underline to
970 * go all the way across the character cell, we only search
971 * down a single column of the bitmap, half way across.)
975 HBITMAP und_bm
, und_oldbm
;
979 und_dc
= CreateCompatibleDC(hdc
);
980 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
981 und_oldbm
= SelectObject(und_dc
, und_bm
);
982 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
983 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
984 SetTextColor(und_dc
, RGB(255, 255, 255));
985 SetBkColor(und_dc
, RGB(0, 0, 0));
986 SetBkMode(und_dc
, OPAQUE
);
987 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
989 for (i
= 0; i
< font_height
; i
++) {
990 c
= GetPixel(und_dc
, font_width
/ 2, i
);
991 if (c
!= RGB(0, 0, 0))
994 SelectObject(und_dc
, und_oldbm
);
995 DeleteObject(und_bm
);
999 DeleteObject(fonts
[FONT_UNDERLINE
]);
1000 fonts
[FONT_UNDERLINE
] = 0;
1004 if (bold_mode
== BOLD_FONT
) {
1005 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
1009 descent
= tm
.tmAscent
+ 1;
1010 if (descent
>= font_height
)
1011 descent
= font_height
- 1;
1013 for (i
= 0; i
< 3; i
++) {
1015 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
1016 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
1023 ReleaseDC(hwnd
, hdc
);
1025 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1026 und_mode
= UND_LINE
;
1027 DeleteObject(fonts
[FONT_UNDERLINE
]);
1028 fonts
[FONT_UNDERLINE
] = 0;
1031 if (bold_mode
== BOLD_FONT
&&
1032 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1033 bold_mode
= BOLD_SHADOW
;
1034 DeleteObject(fonts
[FONT_BOLD
]);
1035 fonts
[FONT_BOLD
] = 0;
1037 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1042 static void another_font(int fontno
)
1045 int fw_dontcare
, fw_bold
;
1049 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1052 basefont
= (fontno
& ~(FONT_BOLDUND
));
1053 if (basefont
!= fontno
&& !fontflag
[basefont
])
1054 another_font(basefont
);
1056 if (cfg
.fontisbold
) {
1057 fw_dontcare
= FW_BOLD
;
1060 fw_dontcare
= FW_DONTCARE
;
1064 c
= cfg
.fontcharset
;
1070 if (fontno
& FONT_WIDE
)
1072 if (fontno
& FONT_NARROW
)
1074 if (fontno
& FONT_OEM
)
1076 if (fontno
& FONT_BOLD
)
1078 if (fontno
& FONT_UNDERLINE
)
1082 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1083 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1084 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1085 FIXED_PITCH
| FF_DONTCARE
, s
);
1087 fontflag
[fontno
] = 1;
1090 static void deinit_fonts(void)
1093 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1095 DeleteObject(fonts
[i
]);
1101 void request_resize(int w
, int h
)
1105 /* If the window is maximized supress resizing attempts */
1106 if (IsZoomed(hwnd
)) {
1107 if (cfg
.resize_action
!= RESIZE_FONT
)
1111 if (cfg
.resize_action
== RESIZE_DISABLED
) return;
1112 if (h
== rows
&& w
== cols
) return;
1114 /* Sanity checks ... */
1116 static int first_time
= 1;
1119 switch (first_time
) {
1121 /* Get the size of the screen */
1122 if (GetClientRect(GetDesktopWindow(), &ss
))
1123 /* first_time = 0 */ ;
1129 /* Make sure the values are sane */
1130 width
= (ss
.right
- ss
.left
- extra_width
) / 4;
1131 height
= (ss
.bottom
- ss
.top
- extra_height
) / 6;
1133 if (w
> width
|| h
> height
)
1142 term_size(h
, w
, cfg
.savelines
);
1144 if (cfg
.resize_action
!= RESIZE_FONT
) {
1145 width
= extra_width
+ font_width
* w
;
1146 height
= extra_height
+ font_height
* h
;
1148 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1149 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1150 SWP_NOMOVE
| SWP_NOZORDER
);
1154 InvalidateRect(hwnd
, NULL
, TRUE
);
1157 static void reset_window(int reinit
) {
1159 * This function decides how to resize or redraw when the
1160 * user changes something.
1162 * This function doesn't like to change the terminal size but if the
1163 * font size is locked that may be it's only soluion.
1165 int win_width
, win_height
;
1168 #ifdef RDB_DEBUG_PATCH
1169 debug((27, "reset_window()"));
1172 /* Current window sizes ... */
1173 GetWindowRect(hwnd
, &wr
);
1174 GetClientRect(hwnd
, &cr
);
1176 win_width
= cr
.right
- cr
.left
;
1177 win_height
= cr
.bottom
- cr
.top
;
1179 /* Are we being forced to reload the fonts ? */
1181 #ifdef RDB_DEBUG_PATCH
1182 debug((27, "reset_window() -- Forced deinit"));
1188 /* Oh, looks like we're minimised */
1189 if (win_width
== 0 || win_height
== 0)
1192 /* Is the window out of position ? */
1194 (offset_width
!= (win_width
-font_width
*cols
)/2 ||
1195 offset_height
!= (win_height
-font_height
*rows
)/2) ){
1196 offset_width
= (win_width
-font_width
*cols
)/2;
1197 offset_height
= (win_height
-font_height
*rows
)/2;
1198 InvalidateRect(hwnd
, NULL
, TRUE
);
1199 #ifdef RDB_DEBUG_PATCH
1200 debug((27, "reset_window() -> Reposition terminal"));
1204 if (IsZoomed(hwnd
) || full_screen
) {
1205 /* We're fullscreen, this means we must not change the size of
1206 * the window so it's the font size or the terminal itself.
1209 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1210 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1212 if (cfg
.resize_action
== RESIZE_FONT
) {
1213 if ( font_width
!= win_width
/cols
||
1214 font_height
!= win_height
/rows
) {
1216 init_fonts(win_width
/cols
, win_height
/rows
);
1217 offset_width
= (win_width
-font_width
*cols
)/2;
1218 offset_height
= (win_height
-font_height
*rows
)/2;
1219 InvalidateRect(hwnd
, NULL
, TRUE
);
1220 #ifdef RDB_DEBUG_PATCH
1221 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1222 font_width
, font_height
));
1226 if ( font_width
!= win_width
/cols
||
1227 font_height
!= win_height
/rows
) {
1228 /* Our only choice at this point is to change the
1229 * size of the terminal; Oh well.
1231 term_size( win_height
/font_height
, win_width
/font_width
,
1233 offset_width
= (win_width
-font_width
*cols
)/2;
1234 offset_height
= (win_height
-font_height
*rows
)/2;
1235 InvalidateRect(hwnd
, NULL
, TRUE
);
1236 #ifdef RDB_DEBUG_PATCH
1237 debug((27, "reset_window() -> Zoomed term_size"));
1244 /* Hmm, a force re-init means we should ignore the current window
1245 * so we resize to the default font size.
1248 #ifdef RDB_DEBUG_PATCH
1249 debug((27, "reset_window() -> Forced re-init"));
1252 offset_width
= offset_height
= cfg
.window_border
;
1253 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1254 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1256 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1257 win_height
!= font_height
*rows
+ offset_height
*2) {
1259 /* If this is too large windows will resize it to the maximum
1260 * allowed window size, we will then be back in here and resize
1261 * the font or terminal to fit.
1263 SetWindowPos(hwnd
, NULL
, 0, 0,
1264 font_width
*cols
+ extra_width
,
1265 font_height
*rows
+ extra_height
,
1266 SWP_NOMOVE
| SWP_NOZORDER
);
1271 /* Okay the user doesn't want us to change the font so we try the
1272 * window. But that may be too big for the screen which forces us
1273 * to change the terminal.
1275 if ((cfg
.resize_action
!= RESIZE_FONT
&& reinit
==0) || reinit
>0) {
1276 offset_width
= offset_height
= cfg
.window_border
;
1277 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1278 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1280 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1281 win_height
!= font_height
*rows
+ offset_height
*2) {
1286 GetClientRect(GetDesktopWindow(), &ss
);
1287 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1288 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1291 if ( rows
> height
|| cols
> width
) {
1292 if ( height
> rows
) height
= rows
;
1293 if ( width
> cols
) width
= cols
;
1294 term_size(height
, width
, cfg
.savelines
);
1295 #ifdef RDB_DEBUG_PATCH
1296 debug((27, "reset_window() -> term resize to (%d,%d)",
1301 SetWindowPos(hwnd
, NULL
, 0, 0,
1302 font_width
*cols
+ extra_width
,
1303 font_height
*rows
+ extra_height
,
1304 SWP_NOMOVE
| SWP_NOZORDER
);
1306 InvalidateRect(hwnd
, NULL
, TRUE
);
1307 #ifdef RDB_DEBUG_PATCH
1308 debug((27, "reset_window() -> window resize to (%d,%d)",
1309 font_width
*cols
+ extra_width
,
1310 font_height
*rows
+ extra_height
));
1316 /* We're allowed to or must change the font but do we want to ? */
1318 if (font_width
!= (win_width
-cfg
.window_border
*2)/cols
||
1319 font_height
!= (win_height
-cfg
.window_border
*2)/rows
) {
1322 init_fonts((win_width
-cfg
.window_border
*2)/cols
,
1323 (win_height
-cfg
.window_border
*2)/rows
);
1324 offset_width
= (win_width
-font_width
*cols
)/2;
1325 offset_height
= (win_height
-font_height
*rows
)/2;
1327 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+offset_width
*2;
1328 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1330 InvalidateRect(hwnd
, NULL
, TRUE
);
1331 #ifdef RDB_DEBUG_PATCH
1332 debug((25, "reset_window() -> font resize to (%d,%d)",
1333 font_width
, font_height
));
1338 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
)
1340 int thistime
= GetMessageTime();
1342 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1343 lastbtn
= MBT_NOTHING
;
1344 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
);
1348 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1349 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1350 lastact
== MA_2CLK ? MA_3CLK
:
1351 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1356 if (lastact
!= MA_NOTHING
)
1357 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
);
1358 lasttime
= thistime
;
1362 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1363 * into a cooked one (SELECT, EXTEND, PASTE).
1365 Mouse_Button
translate_button(Mouse_Button button
)
1367 if (button
== MBT_LEFT
)
1369 if (button
== MBT_MIDDLE
)
1370 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1371 if (button
== MBT_RIGHT
)
1372 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1373 return 0; /* shouldn't happen */
1376 static void show_mouseptr(int show
)
1378 static int cursor_visible
= 1;
1379 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1381 if (cursor_visible
&& !show
)
1383 else if (!cursor_visible
&& show
)
1385 cursor_visible
= show
;
1388 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1389 WPARAM wParam
, LPARAM lParam
)
1392 static int ignore_clip
= FALSE
;
1393 static int resizing
= FALSE
;
1394 static int need_backend_resize
= FALSE
;
1398 if (pending_netevent
)
1399 enact_pending_netevent();
1405 if (cfg
.ping_interval
> 0) {
1408 if (now
- last_movement
> cfg
.ping_interval
) {
1409 back
->special(TS_PING
);
1410 last_movement
= now
;
1413 net_pending_errors();
1419 if (!cfg
.warn_on_close
|| session_closed
||
1421 "Are you sure you want to close this session?",
1422 "PuTTY Exit Confirmation",
1423 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1424 DestroyWindow(hwnd
);
1431 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1443 PROCESS_INFORMATION pi
;
1444 HANDLE filemap
= NULL
;
1446 if (wParam
== IDM_DUPSESS
) {
1448 * Allocate a file-mapping memory chunk for the
1451 SECURITY_ATTRIBUTES sa
;
1454 sa
.nLength
= sizeof(sa
);
1455 sa
.lpSecurityDescriptor
= NULL
;
1456 sa
.bInheritHandle
= TRUE
;
1457 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1460 0, sizeof(Config
), NULL
);
1462 p
= (Config
*) MapViewOfFile(filemap
,
1464 0, 0, sizeof(Config
));
1466 *p
= cfg
; /* structure copy */
1470 sprintf(c
, "putty &%p", filemap
);
1472 } else if (wParam
== IDM_SAVEDSESS
) {
1474 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1475 cl
= smalloc(16 + strlen(session
)); /* 8, but play safe */
1477 cl
= NULL
; /* not a very important failure mode */
1479 sprintf(cl
, "putty @%s", session
);
1485 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1487 si
.lpReserved
= NULL
;
1488 si
.lpDesktop
= NULL
;
1492 si
.lpReserved2
= NULL
;
1493 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1494 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1497 CloseHandle(filemap
);
1507 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1510 if (!do_reconfig(hwnd
))
1513 /* If user forcibly disables full-screen, gracefully unzoom */
1514 if (full_screen
&& !cfg
.fullscreenonaltenter
) {
1518 if (strcmp(prev_cfg
.logfilename
, cfg
.logfilename
) ||
1519 prev_cfg
.logtype
!= cfg
.logtype
) {
1520 logfclose(); /* reset logging */
1526 * Flush the line discipline's edit buffer in the
1527 * case where local editing has just been disabled.
1529 ldisc_send(NULL
, 0, 0);
1537 /* Screen size changed ? */
1538 if (cfg
.height
!= prev_cfg
.height
||
1539 cfg
.width
!= prev_cfg
.width
||
1540 cfg
.savelines
!= prev_cfg
.savelines
||
1541 cfg
.resize_action
!= RESIZE_TERM
)
1542 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1544 /* Enable or disable the scroll bar, etc */
1546 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1547 LONG nexflag
, exflag
=
1548 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1551 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1552 if (cfg
.alwaysontop
) {
1553 nexflag
|= WS_EX_TOPMOST
;
1554 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1555 SWP_NOMOVE
| SWP_NOSIZE
);
1557 nexflag
&= ~(WS_EX_TOPMOST
);
1558 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1559 SWP_NOMOVE
| SWP_NOSIZE
);
1562 if (cfg
.sunken_edge
)
1563 nexflag
|= WS_EX_CLIENTEDGE
;
1565 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1571 nflg
&= ~WS_VSCROLL
;
1572 if (cfg
.resize_action
== RESIZE_DISABLED
)
1573 nflg
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1575 nflg
|= (WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1577 if (nflg
!= flag
|| nexflag
!= exflag
) {
1579 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1580 if (nexflag
!= exflag
)
1581 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1583 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1584 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1585 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1593 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1598 set_title(cfg
.wintitle
);
1599 if (IsIconic(hwnd
)) {
1601 cfg
.win_name_always ? window_name
:
1605 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1606 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1607 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1608 cfg
.fontheight
!= prev_cfg
.fontheight
||
1609 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1610 cfg
.vtmode
!= prev_cfg
.vtmode
||
1611 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1612 (cfg
.resize_action
!= RESIZE_FONT
&&
1613 prev_cfg
.resize_action
== RESIZE_FONT
))
1616 InvalidateRect(hwnd
, NULL
, TRUE
);
1617 reset_window(init_lvl
);
1618 net_pending_errors();
1631 back
->special(TS_AYT
);
1632 net_pending_errors();
1635 back
->special(TS_BRK
);
1636 net_pending_errors();
1639 back
->special(TS_SYNCH
);
1640 net_pending_errors();
1643 back
->special(TS_EC
);
1644 net_pending_errors();
1647 back
->special(TS_EL
);
1648 net_pending_errors();
1651 back
->special(TS_GA
);
1652 net_pending_errors();
1655 back
->special(TS_NOP
);
1656 net_pending_errors();
1659 back
->special(TS_ABORT
);
1660 net_pending_errors();
1663 back
->special(TS_AO
);
1664 net_pending_errors();
1667 back
->special(TS_IP
);
1668 net_pending_errors();
1671 back
->special(TS_SUSP
);
1672 net_pending_errors();
1675 back
->special(TS_EOR
);
1676 net_pending_errors();
1679 back
->special(TS_EOF
);
1680 net_pending_errors();
1687 * We get this if the System menu has been activated.
1688 * This might happen from within TranslateKey, in which
1689 * case it really wants to be followed by a `space'
1690 * character to actually _bring the menu up_ rather
1691 * than just sitting there in `ready to appear' state.
1694 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1696 case IDM_FULLSCREEN
:
1700 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1701 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1706 #define X_POS(l) ((int)(short)LOWORD(l))
1707 #define Y_POS(l) ((int)(short)HIWORD(l))
1709 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1710 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1711 #define WHEEL_DELTA 120
1714 wheel_accumulator
+= (short) HIWORD(wParam
);
1715 wParam
= LOWORD(wParam
);
1717 /* process events when the threshold is reached */
1718 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
1721 /* reduce amount for next time */
1722 if (wheel_accumulator
> 0) {
1724 wheel_accumulator
-= WHEEL_DELTA
;
1725 } else if (wheel_accumulator
< 0) {
1727 wheel_accumulator
+= WHEEL_DELTA
;
1731 if (send_raw_mouse
) {
1732 /* send a mouse-down followed by a mouse up */
1735 TO_CHR_X(X_POS(lParam
)),
1736 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1737 wParam
& MK_CONTROL
);
1738 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1739 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1740 wParam
& MK_CONTROL
);
1742 /* trigger a scroll */
1744 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
1749 case WM_LBUTTONDOWN
:
1750 case WM_MBUTTONDOWN
:
1751 case WM_RBUTTONDOWN
:
1758 case WM_LBUTTONDOWN
:
1762 case WM_MBUTTONDOWN
:
1763 button
= MBT_MIDDLE
;
1766 case WM_RBUTTONDOWN
:
1775 button
= MBT_MIDDLE
;
1783 button
= press
= 0; /* shouldn't happen */
1788 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1789 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
);
1792 term_mouse(button
, MA_RELEASE
,
1793 TO_CHR_X(X_POS(lParam
)),
1794 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1795 wParam
& MK_CONTROL
);
1803 * Add the mouse position and message time to the random
1806 noise_ultralight(lParam
);
1808 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1810 if (wParam
& MK_LBUTTON
)
1812 else if (wParam
& MK_MBUTTON
)
1816 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1817 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1818 wParam
& MK_CONTROL
);
1821 case WM_NCMOUSEMOVE
:
1823 noise_ultralight(lParam
);
1825 case WM_IGNORE_CLIP
:
1826 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1828 case WM_DESTROYCLIPBOARD
:
1831 ignore_clip
= FALSE
;
1837 hdc
= BeginPaint(hwnd
, &p
);
1839 SelectPalette(hdc
, pal
, TRUE
);
1840 RealizePalette(hdc
);
1843 (p
.rcPaint
.left
-offset_width
)/font_width
,
1844 (p
.rcPaint
.top
-offset_height
)/font_height
,
1845 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
1846 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
1849 p
.rcPaint
.left
< offset_width
||
1850 p
.rcPaint
.top
< offset_height
||
1851 p
.rcPaint
.right
>= offset_width
+ font_width
*cols
||
1852 p
.rcPaint
.bottom
>= offset_height
+ font_height
*rows
)
1854 HBRUSH fillcolour
, oldbrush
;
1856 fillcolour
= CreateSolidBrush (
1857 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1858 oldbrush
= SelectObject(hdc
, fillcolour
);
1859 edge
= CreatePen(PS_SOLID
, 0,
1860 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1861 oldpen
= SelectObject(hdc
, edge
);
1863 ExcludeClipRect(hdc
,
1864 offset_width
, offset_height
,
1865 offset_width
+font_width
*cols
,
1866 offset_height
+font_height
*rows
);
1868 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1869 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1871 // SelectClipRgn(hdc, NULL);
1873 SelectObject(hdc
, oldbrush
);
1874 DeleteObject(fillcolour
);
1875 SelectObject(hdc
, oldpen
);
1878 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
1879 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
1885 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1886 * but the only one that's likely to try to overload us is FD_READ.
1887 * This means buffering just one is fine.
1889 if (pending_netevent
)
1890 enact_pending_netevent();
1892 pending_netevent
= TRUE
;
1893 pend_netevent_wParam
= wParam
;
1894 pend_netevent_lParam
= lParam
;
1895 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
1896 enact_pending_netevent();
1898 time(&last_movement
);
1902 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
1904 flash_window(0); /* stop */
1916 case WM_ENTERSIZEMOVE
:
1917 #ifdef RDB_DEBUG_PATCH
1918 debug((27, "WM_ENTERSIZEMOVE"));
1922 need_backend_resize
= FALSE
;
1924 case WM_EXITSIZEMOVE
:
1927 #ifdef RDB_DEBUG_PATCH
1928 debug((27, "WM_EXITSIZEMOVE"));
1930 if (need_backend_resize
) {
1931 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1932 InvalidateRect(hwnd
, NULL
, TRUE
);
1937 * This does two jobs:
1938 * 1) Keep the sizetip uptodate
1939 * 2) Make sure the window size is _stepped_ in units of the font size.
1941 if (cfg
.resize_action
== RESIZE_TERM
&& !alt_pressed
) {
1942 int width
, height
, w
, h
, ew
, eh
;
1943 LPRECT r
= (LPRECT
) lParam
;
1945 if ( !need_backend_resize
&&
1946 (cfg
.height
!= rows
|| cfg
.width
!= cols
)) {
1948 * Great! It seems the host has been changing the terminal
1949 * size, well the user is now grabbing so this is probably
1950 * the least confusing solution in the long run even though
1951 * it a is suprise. Unfortunatly the only way to prevent
1952 * this seems to be to let the host change the window size
1953 * and as that's a user option we're still right back here.
1955 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1957 InvalidateRect(hwnd
, NULL
, TRUE
);
1958 need_backend_resize
= TRUE
;
1961 width
= r
->right
- r
->left
- extra_width
;
1962 height
= r
->bottom
- r
->top
- extra_height
;
1963 w
= (width
+ font_width
/ 2) / font_width
;
1966 h
= (height
+ font_height
/ 2) / font_height
;
1969 UpdateSizeTip(hwnd
, w
, h
);
1970 ew
= width
- w
* font_width
;
1971 eh
= height
- h
* font_height
;
1973 if (wParam
== WMSZ_LEFT
||
1974 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
1980 if (wParam
== WMSZ_TOP
||
1981 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
1991 int width
, height
, w
, h
, rv
= 0;
1992 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
1993 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
1994 LPRECT r
= (LPRECT
) lParam
;
1996 width
= r
->right
- r
->left
- ex_width
;
1997 height
= r
->bottom
- r
->top
- ex_height
;
1999 w
= (width
+ cols
/2)/cols
;
2000 h
= (height
+ rows
/2)/rows
;
2001 if ( r
->right
!= r
->left
+ w
*cols
+ ex_width
)
2004 if (wParam
== WMSZ_LEFT
||
2005 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2006 r
->left
= r
->right
- w
*cols
- ex_width
;
2008 r
->right
= r
->left
+ w
*cols
+ ex_width
;
2010 if (r
->bottom
!= r
->top
+ h
*rows
+ ex_height
)
2013 if (wParam
== WMSZ_TOP
||
2014 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2015 r
->top
= r
->bottom
- h
*rows
- ex_height
;
2017 r
->bottom
= r
->top
+ h
*rows
+ ex_height
;
2021 /* break; (never reached) */
2023 #ifdef RDB_DEBUG_PATCH
2024 debug((27, "WM_SIZE %s (%d,%d)",
2025 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2026 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2027 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2028 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2030 LOWORD(lParam
), HIWORD(lParam
)));
2032 if (wParam
== SIZE_MINIMIZED
) {
2034 cfg
.win_name_always ? window_name
: icon_name
);
2037 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2038 SetWindowText(hwnd
, window_name
);
2040 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2041 /* A resize, well it better be a minimize. */
2045 int width
, height
, w
, h
;
2047 width
= LOWORD(lParam
);
2048 height
= HIWORD(lParam
);
2051 if (wParam
== SIZE_MAXIMIZED
) {
2055 if (cfg
.resize_action
!= RESIZE_FONT
) {
2056 w
= width
/ font_width
;
2058 h
= height
/ font_height
;
2061 term_size(h
, w
, cfg
.savelines
);
2064 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2066 if (cfg
.resize_action
!= RESIZE_FONT
)
2067 term_size(prev_rows
, prev_cols
, cfg
.savelines
);
2069 } else if (was_full_screen
) {
2070 was_full_screen
= 0;
2071 if (cfg
.resize_action
!= RESIZE_FONT
)
2072 term_size(pre_fs_rows
, pre_fs_cols
, cfg
.savelines
);
2075 /* This is an unexpected resize, these will normally happen
2076 * if the window is too large. Probably either the user
2077 * selected a huge font or the screen size has changed.
2079 * This is also called with minimize.
2081 else reset_window(-1);
2085 * Don't call back->size in mid-resize. (To prevent
2086 * massive numbers of resize events getting sent
2087 * down the connection during an NT opaque drag.)
2090 if (cfg
.resize_action
== RESIZE_TERM
&& !alt_pressed
) {
2091 need_backend_resize
= TRUE
;
2092 w
= (width
-cfg
.window_border
*2) / font_width
;
2094 h
= (height
-cfg
.window_border
*2) / font_height
;
2105 switch (LOWORD(wParam
)) {
2119 term_scroll(0, +rows
/ 2);
2122 term_scroll(0, -rows
/ 2);
2124 case SB_THUMBPOSITION
:
2126 term_scroll(1, HIWORD(wParam
));
2130 case WM_PALETTECHANGED
:
2131 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2132 HDC hdc
= get_ctx();
2134 if (RealizePalette(hdc
) > 0)
2140 case WM_QUERYNEWPALETTE
:
2142 HDC hdc
= get_ctx();
2144 if (RealizePalette(hdc
) > 0)
2156 * Add the scan code and keypress timing to the random
2159 noise_ultralight(lParam
);
2162 * We don't do TranslateMessage since it disassociates the
2163 * resulting CHAR message from the KEYDOWN that sparked it,
2164 * which we occasionally don't want. Instead, we process
2165 * KEYDOWN, and call the Win32 translator functions so that
2166 * we get the translations under _our_ control.
2169 unsigned char buf
[20];
2172 if (wParam
== VK_PROCESSKEY
) {
2175 m
.message
= WM_KEYDOWN
;
2177 m
.lParam
= lParam
& 0xdfff;
2178 TranslateMessage(&m
);
2180 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2182 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2186 * Interrupt an ongoing paste. I'm not sure
2187 * this is sensible, but for the moment it's
2188 * preferable to having to faff about buffering
2194 * We need not bother about stdin backlogs
2195 * here, because in GUI PuTTY we can't do
2196 * anything about it anyway; there's no means
2197 * of asking Windows to hold off on KEYDOWN
2198 * messages. We _have_ to buffer everything
2201 ldisc_send(buf
, len
, 1);
2206 net_pending_errors();
2208 case WM_INPUTLANGCHANGE
:
2210 /* wParam == Font number */
2211 /* lParam == Locale */
2213 HKL NewInputLocale
= (HKL
) lParam
;
2215 // lParam == GetKeyboardLayout(0);
2217 GetLocaleInfo(LOWORD(NewInputLocale
),
2218 LOCALE_IDEFAULTANSICODEPAGE
, lbuf
, sizeof(lbuf
));
2220 kbd_codepage
= atoi(lbuf
);
2223 case WM_IME_COMPOSITION
:
2229 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2230 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2232 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2233 break; /* fall back to DefWindowProc */
2235 hIMC
= ImmGetContext(hwnd
);
2236 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2239 buff
= (char*) smalloc(n
);
2240 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2241 luni_send((unsigned short *)buff
, n
/ 2, 1);
2244 ImmReleaseContext(hwnd
, hIMC
);
2249 if (wParam
& 0xFF00) {
2250 unsigned char buf
[2];
2253 buf
[0] = wParam
>> 8;
2254 lpage_send(kbd_codepage
, buf
, 2, 1);
2256 char c
= (unsigned char) wParam
;
2257 lpage_send(kbd_codepage
, &c
, 1, 1);
2263 * Nevertheless, we are prepared to deal with WM_CHAR
2264 * messages, should they crop up. So if someone wants to
2265 * post the things to us as part of a macro manoeuvre,
2266 * we're ready to cope.
2269 char c
= (unsigned char)wParam
;
2270 lpage_send(CP_ACP
, &c
, 1, 1);
2274 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2275 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2280 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2284 * Move the system caret. (We maintain one, even though it's
2285 * invisible, for the benefit of blind people: apparently some
2286 * helper software tracks the system caret, so we should arrange to
2289 void sys_cursor(int x
, int y
)
2294 if (!has_focus
) return;
2296 SetCaretPos(x
* font_width
+ offset_width
,
2297 y
* font_height
+ offset_height
);
2299 /* IMM calls on Win98 and beyond only */
2300 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2302 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2303 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2305 /* we should have the IMM functions */
2306 hIMC
= ImmGetContext(hwnd
);
2307 cf
.dwStyle
= CFS_POINT
;
2308 cf
.ptCurrentPos
.x
= x
* font_width
;
2309 cf
.ptCurrentPos
.y
= y
* font_height
;
2310 ImmSetCompositionWindow(hIMC
, &cf
);
2312 ImmReleaseContext(hwnd
, hIMC
);
2316 * Draw a line of text in the window, at given character
2317 * coordinates, in given attributes.
2319 * We are allowed to fiddle with the contents of `text'.
2321 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2322 unsigned long attr
, int lattr
)
2325 int nfg
, nbg
, nfont
;
2328 int force_manual_underline
= 0;
2329 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2330 int char_width
= fnt_width
;
2331 int text_adjust
= 0;
2332 static int *IpDx
= 0, IpDxLEN
= 0;
2334 if (attr
& ATTR_WIDE
)
2337 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2339 if (len
> IpDxLEN
) {
2341 IpDx
= smalloc((len
+ 16) * sizeof(int));
2342 IpDxLEN
= (len
+ 16);
2344 for (i
= 0; i
< IpDxLEN
; i
++)
2345 IpDx
[i
] = char_width
;
2348 /* Only want the left half of double width lines */
2349 if (lattr
!= LATTR_NORM
&& x
*2 >= cols
)
2357 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
2358 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2359 attr
^= ATTR_CUR_XOR
;
2363 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2364 /* Assume a poorman font is borken in other ways too. */
2374 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2377 if (attr
& ATTR_NARROW
)
2378 nfont
|= FONT_NARROW
;
2380 /* Special hack for the VT100 linedraw glyphs. */
2381 if ((attr
& CSET_MASK
) == 0x2300) {
2382 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2383 switch ((unsigned char) (text
[0])) {
2385 text_adjust
= -2 * font_height
/ 5;
2388 text_adjust
= -1 * font_height
/ 5;
2391 text_adjust
= font_height
/ 5;
2394 text_adjust
= 2 * font_height
/ 5;
2397 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2400 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2401 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2402 if (attr
& ATTR_UNDER
) {
2403 attr
&= ~ATTR_UNDER
;
2404 force_manual_underline
= 1;
2409 /* Anything left as an original character set is unprintable. */
2410 if (DIRECT_CHAR(attr
)) {
2413 memset(text
, 0xFD, len
);
2417 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2420 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2421 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2422 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2424 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2425 nfont
|= FONT_UNDERLINE
;
2426 another_font(nfont
);
2427 if (!fonts
[nfont
]) {
2428 if (nfont
& FONT_UNDERLINE
)
2429 force_manual_underline
= 1;
2430 /* Don't do the same for manual bold, it could be bad news. */
2432 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2434 another_font(nfont
);
2436 nfont
= FONT_NORMAL
;
2437 if (attr
& ATTR_REVERSE
) {
2442 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2444 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2448 SelectObject(hdc
, fonts
[nfont
]);
2449 SetTextColor(hdc
, fg
);
2450 SetBkColor(hdc
, bg
);
2451 SetBkMode(hdc
, OPAQUE
);
2454 line_box
.right
= x
+ char_width
* len
;
2455 line_box
.bottom
= y
+ font_height
;
2457 /* Only want the left half of double width lines */
2458 if (line_box
.right
> font_width
*cols
+offset_width
)
2459 line_box
.right
= font_width
*cols
+offset_width
;
2461 /* We're using a private area for direct to font. (512 chars.) */
2462 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2463 /* Ho Hum, dbcs fonts are a PITA! */
2464 /* To display on W9x I have to convert to UCS */
2465 static wchar_t *uni_buf
= 0;
2466 static int uni_len
= 0;
2468 if (len
> uni_len
) {
2470 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2473 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2474 uni_buf
[nlen
] = 0xFFFD;
2475 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2476 IpDx
[nlen
] += char_width
;
2477 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2478 text
+mptr
, 2, uni_buf
+nlen
, 1);
2483 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2484 text
+mptr
, 1, uni_buf
+nlen
, 1);
2492 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2493 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2494 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2495 SetBkMode(hdc
, TRANSPARENT
);
2496 ExtTextOutW(hdc
, x
- 1,
2497 y
- font_height
* (lattr
==
2498 LATTR_BOT
) + text_adjust
,
2499 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2503 } else if (DIRECT_FONT(attr
)) {
2505 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2506 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2507 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2508 SetBkMode(hdc
, TRANSPARENT
);
2510 /* GRR: This draws the character outside it's box and can leave
2511 * 'droppings' even with the clip box! I suppose I could loop it
2512 * one character at a time ... yuk.
2514 * Or ... I could do a test print with "W", and use +1 or -1 for this
2515 * shift depending on if the leftmost column is blank...
2517 ExtTextOut(hdc
, x
- 1,
2518 y
- font_height
* (lattr
==
2519 LATTR_BOT
) + text_adjust
,
2520 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2523 /* And 'normal' unicode characters */
2524 static WCHAR
*wbuf
= NULL
;
2525 static int wlen
= 0;
2530 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2532 for (i
= 0; i
< len
; i
++)
2533 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2536 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2537 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2539 /* And the shadow bold hack. */
2540 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2541 SetBkMode(hdc
, TRANSPARENT
);
2542 ExtTextOutW(hdc
, x
- 1,
2543 y
- font_height
* (lattr
==
2544 LATTR_BOT
) + text_adjust
,
2545 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2548 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2549 (und_mode
== UND_LINE
2550 && (attr
& ATTR_UNDER
)))) {
2553 if (lattr
== LATTR_BOT
)
2554 dec
= dec
* 2 - font_height
;
2556 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2557 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2558 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2559 oldpen
= SelectObject(hdc
, oldpen
);
2560 DeleteObject(oldpen
);
2564 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2565 unsigned long attr
, int lattr
)
2571 int ctype
= cfg
.cursor_type
;
2573 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2574 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2575 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2579 attr
|= TATTR_RIGHTCURS
;
2582 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2583 if (attr
& ATTR_WIDE
)
2590 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2593 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2594 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2595 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2596 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2597 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2598 Polyline(hdc
, pts
, 5);
2599 oldpen
= SelectObject(hdc
, oldpen
);
2600 DeleteObject(oldpen
);
2601 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2602 int startx
, starty
, dx
, dy
, length
, i
;
2605 starty
= y
+ descent
;
2608 length
= char_width
;
2611 if (attr
& TATTR_RIGHTCURS
)
2612 xadjust
= char_width
- 1;
2613 startx
= x
+ xadjust
;
2617 length
= font_height
;
2619 if (attr
& TATTR_ACTCURS
) {
2622 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2623 MoveToEx(hdc
, startx
, starty
, NULL
);
2624 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2625 oldpen
= SelectObject(hdc
, oldpen
);
2626 DeleteObject(oldpen
);
2628 for (i
= 0; i
< length
; i
++) {
2630 SetPixel(hdc
, startx
, starty
, colours
[23]);
2639 /* This function gets the actual width of a character in the normal font.
2641 int CharWidth(Context ctx
, int uc
) {
2645 /* If the font max is the same as the font ave width then this
2646 * function is a no-op.
2648 if (!font_dualwidth
) return 1;
2650 switch (uc
& CSET_MASK
) {
2652 uc
= unitab_line
[uc
& 0xFF];
2655 uc
= unitab_xterm
[uc
& 0xFF];
2658 uc
= unitab_scoacs
[uc
& 0xFF];
2661 if (DIRECT_FONT(uc
)) {
2662 if (dbcs_screenfont
) return 1;
2664 /* Speedup, I know of no font where ascii is the wrong width */
2665 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
2668 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
2669 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2670 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
2671 another_font(FONT_OEM
);
2672 if (!fonts
[FONT_OEM
]) return 0;
2674 SelectObject(hdc
, fonts
[FONT_OEM
]);
2678 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
2679 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
2682 /* Speedup, I know of no font where ascii is the wrong width */
2683 if (uc
>= ' ' && uc
<= '~') return 1;
2685 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2686 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
2687 /* Okay that one worked */ ;
2688 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
2689 /* This should work on 9x too, but it's "less accurate" */ ;
2694 ibuf
+= font_width
/ 2 -1;
2701 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2702 * codes. Returns number of bytes used or zero to drop the message
2703 * or -1 to forward the message to windows.
2705 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2706 unsigned char *output
)
2709 int scan
, left_alt
= 0, key_down
, shift_state
;
2711 unsigned char *p
= output
;
2712 static int alt_sum
= 0;
2714 HKL kbd_layout
= GetKeyboardLayout(0);
2716 static WORD keys
[3];
2717 static int compose_char
= 0;
2718 static WPARAM compose_key
= 0;
2720 r
= GetKeyboardState(keystate
);
2722 memset(keystate
, 0, sizeof(keystate
));
2725 #define SHOW_TOASCII_RESULT
2726 { /* Tell us all about key events */
2727 static BYTE oldstate
[256];
2728 static int first
= 1;
2732 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2735 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2737 } else if ((HIWORD(lParam
) & KF_UP
)
2738 && scan
== (HIWORD(lParam
) & 0xFF)) {
2742 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2743 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2756 debug(("VK_%02x", wParam
));
2758 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2760 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2762 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2763 if (ch
>= ' ' && ch
<= '~')
2764 debug((", '%c'", ch
));
2766 debug((", $%02x", ch
));
2769 debug((", KB0=%02x", keys
[0]));
2771 debug((", KB1=%02x", keys
[1]));
2773 debug((", KB2=%02x", keys
[2]));
2775 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2777 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2779 if ((HIWORD(lParam
) & KF_EXTENDED
))
2781 if ((HIWORD(lParam
) & KF_UP
))
2785 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2786 else if ((HIWORD(lParam
) & KF_UP
))
2787 oldstate
[wParam
& 0xFF] ^= 0x80;
2789 oldstate
[wParam
& 0xFF] ^= 0x81;
2791 for (ch
= 0; ch
< 256; ch
++)
2792 if (oldstate
[ch
] != keystate
[ch
])
2793 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2795 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2799 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2800 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2804 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2805 if ((cfg
.funky_type
== 3 ||
2806 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2807 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2809 wParam
= VK_EXECUTE
;
2811 /* UnToggle NUMLock */
2812 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2813 keystate
[VK_NUMLOCK
] ^= 1;
2816 /* And write back the 'adjusted' state */
2817 SetKeyboardState(keystate
);
2820 /* Disable Auto repeat if required */
2821 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2824 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2827 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2829 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2830 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2831 if (cfg
.ctrlaltkeys
)
2832 keystate
[VK_MENU
] = 0;
2834 keystate
[VK_RMENU
] = 0x80;
2839 alt_pressed
= (left_alt
&& key_down
);
2841 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2842 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2843 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2845 /* Note if AltGr was pressed and if it was used as a compose key */
2846 if (!compose_state
) {
2847 compose_key
= 0x100;
2848 if (cfg
.compose_key
) {
2849 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2850 compose_key
= wParam
;
2852 if (wParam
== VK_APPS
)
2853 compose_key
= wParam
;
2856 if (wParam
== compose_key
) {
2857 if (compose_state
== 0
2858 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2860 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2864 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
2868 * Record that we pressed key so the scroll window can be reset, but
2869 * be careful to avoid Shift-UP/Down
2871 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
2875 if (compose_state
> 1 && left_alt
)
2878 /* Sanitize the number pad if not using a PC NumPad */
2879 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2880 && cfg
.funky_type
!= 2)
2881 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
2882 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
2886 nParam
= VK_NUMPAD0
;
2889 nParam
= VK_NUMPAD1
;
2892 nParam
= VK_NUMPAD2
;
2895 nParam
= VK_NUMPAD3
;
2898 nParam
= VK_NUMPAD4
;
2901 nParam
= VK_NUMPAD5
;
2904 nParam
= VK_NUMPAD6
;
2907 nParam
= VK_NUMPAD7
;
2910 nParam
= VK_NUMPAD8
;
2913 nParam
= VK_NUMPAD9
;
2916 nParam
= VK_DECIMAL
;
2920 if (keystate
[VK_NUMLOCK
] & 1)
2927 /* If a key is pressed and AltGr is not active */
2928 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
2929 /* Okay, prepare for most alts then ... */
2933 /* Lets see if it's a pattern we know all about ... */
2934 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2935 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2938 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2939 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
2942 if (wParam
== VK_INSERT
&& shift_state
== 1) {
2946 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
2949 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
2950 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
2953 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
) {
2957 /* Control-Numlock for app-keypad mode switch */
2958 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
2959 app_keypad_keys
^= 1;
2963 /* Nethack keypad */
2964 if (cfg
.nethack_keypad
&& !left_alt
) {
2967 *p
++ = shift_state ?
'B' : 'b';
2970 *p
++ = shift_state ?
'J' : 'j';
2973 *p
++ = shift_state ?
'N' : 'n';
2976 *p
++ = shift_state ?
'H' : 'h';
2979 *p
++ = shift_state ?
'.' : '.';
2982 *p
++ = shift_state ?
'L' : 'l';
2985 *p
++ = shift_state ?
'Y' : 'y';
2988 *p
++ = shift_state ?
'K' : 'k';
2991 *p
++ = shift_state ?
'U' : 'u';
2996 /* Application Keypad */
3000 if (cfg
.funky_type
== 3 ||
3001 (cfg
.funky_type
<= 1 &&
3002 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3016 if (app_keypad_keys
&& !cfg
.no_applic_k
)
3053 if (cfg
.funky_type
== 2) {
3058 } else if (shift_state
)
3065 if (cfg
.funky_type
== 2)
3069 if (cfg
.funky_type
== 2)
3073 if (cfg
.funky_type
== 2)
3078 if (HIWORD(lParam
) & KF_EXTENDED
)
3084 if (xkey
>= 'P' && xkey
<= 'S')
3085 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3087 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3089 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3094 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3095 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3099 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3105 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3109 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3113 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3118 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3123 /* Control-2 to Control-8 are special */
3124 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3125 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3128 if (shift_state
== 2 && wParam
== 0xBD) {
3132 if (shift_state
== 2 && wParam
== 0xDF) {
3136 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
3143 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3144 * for integer decimal nn.)
3146 * We also deal with the weird ones here. Linux VCs replace F1
3147 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3148 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3154 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3157 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3160 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3163 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3166 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3169 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3172 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3175 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3178 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3181 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3214 if ((shift_state
&2) == 0) switch (wParam
) {
3234 /* Reorder edit keys to physical order */
3235 if (cfg
.funky_type
== 3 && code
<= 6)
3236 code
= "\0\2\1\4\5\3\6"[code
];
3238 if (vt52_mode
&& code
> 0 && code
<= 6) {
3239 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3243 if (cfg
.funky_type
== 5 && /* SCO function keys */
3244 code
>= 11 && code
<= 34) {
3245 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3248 case VK_F1
: index
= 0; break;
3249 case VK_F2
: index
= 1; break;
3250 case VK_F3
: index
= 2; break;
3251 case VK_F4
: index
= 3; break;
3252 case VK_F5
: index
= 4; break;
3253 case VK_F6
: index
= 5; break;
3254 case VK_F7
: index
= 6; break;
3255 case VK_F8
: index
= 7; break;
3256 case VK_F9
: index
= 8; break;
3257 case VK_F10
: index
= 9; break;
3258 case VK_F11
: index
= 10; break;
3259 case VK_F12
: index
= 11; break;
3261 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3262 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3263 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3266 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3267 code
>= 1 && code
<= 6) {
3268 char codes
[] = "HL.FIG";
3272 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3276 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3283 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3286 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3289 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3290 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3293 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3295 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3297 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3300 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3301 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3305 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3310 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3311 * some reason seems to send VK_CLEAR to Windows...).
3334 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3336 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
3337 /* VT100 & VT102 manuals both state the app cursor keys
3338 * only work if the app keypad is on.
3340 if (!app_keypad_keys
)
3342 /* Useful mapping of Ctrl-arrows */
3343 if (shift_state
== 2)
3347 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3349 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3356 * Finally, deal with Return ourselves. (Win95 seems to
3357 * foul it up when Alt is pressed, for some reason.)
3359 if (wParam
== VK_RETURN
) { /* Return */
3365 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3366 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3371 /* Okay we've done everything interesting; let windows deal with
3372 * the boring stuff */
3376 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3377 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3379 keystate
[VK_CAPITAL
] = 0;
3382 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3383 #ifdef SHOW_TOASCII_RESULT
3384 if (r
== 1 && !key_down
) {
3386 if (in_utf
|| dbcs_screenfont
)
3387 debug((", (U+%04x)", alt_sum
));
3389 debug((", LCH(%d)", alt_sum
));
3391 debug((", ACH(%d)", keys
[0]));
3396 for (r1
= 0; r1
< r
; r1
++) {
3397 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3406 * Interrupt an ongoing paste. I'm not sure this is
3407 * sensible, but for the moment it's preferable to
3408 * having to faff about buffering things.
3413 for (i
= 0; i
< r
; i
++) {
3414 unsigned char ch
= (unsigned char) keys
[i
];
3416 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3421 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3425 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3426 MessageBeep(MB_ICONHAND
);
3430 luni_send(&keybuf
, 1, 1);
3438 if (in_utf
|| dbcs_screenfont
) {
3440 luni_send(&keybuf
, 1, 1);
3442 ch
= (char) alt_sum
;
3444 * We need not bother about stdin
3445 * backlogs here, because in GUI PuTTY
3446 * we can't do anything about it
3447 * anyway; there's no means of asking
3448 * Windows to hold off on KEYDOWN
3449 * messages. We _have_ to buffer
3450 * everything we're sent.
3452 ldisc_send(&ch
, 1, 1);
3456 lpage_send(kbd_codepage
, &ch
, 1, 1);
3458 if(capsOn
&& ch
< 0x80) {
3461 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3462 luni_send(cbuf
+!left_alt
, 1+!!left_alt
, 1);
3467 lpage_send(kbd_codepage
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3473 /* This is so the ALT-Numpad and dead keys work correctly. */
3478 /* If we're definitly not building up an ALT-54321 then clear it */
3481 /* If we will be using alt_sum fix the 256s */
3482 else if (keys
[0] && (in_utf
|| dbcs_screenfont
))
3487 * ALT alone may or may not want to bring up the System menu.
3488 * If it's not meant to, we return 0 on presses or releases of
3489 * ALT, to show that we've swallowed the keystroke. Otherwise
3490 * we return -1, which means Windows will give the keystroke
3491 * its default handling (i.e. bring up the System menu).
3493 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3499 void set_title(char *title
)
3502 window_name
= smalloc(1 + strlen(title
));
3503 strcpy(window_name
, title
);
3504 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3505 SetWindowText(hwnd
, title
);
3508 void set_icon(char *title
)
3511 icon_name
= smalloc(1 + strlen(title
));
3512 strcpy(icon_name
, title
);
3513 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3514 SetWindowText(hwnd
, title
);
3517 void set_sbar(int total
, int start
, int page
)
3524 si
.cbSize
= sizeof(si
);
3525 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3527 si
.nMax
= total
- 1;
3531 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3534 Context
get_ctx(void)
3540 SelectPalette(hdc
, pal
, FALSE
);
3546 void free_ctx(Context ctx
)
3548 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3549 ReleaseDC(hwnd
, ctx
);
3552 static void real_palette_set(int n
, int r
, int g
, int b
)
3555 logpal
->palPalEntry
[n
].peRed
= r
;
3556 logpal
->palPalEntry
[n
].peGreen
= g
;
3557 logpal
->palPalEntry
[n
].peBlue
= b
;
3558 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3559 colours
[n
] = PALETTERGB(r
, g
, b
);
3560 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3562 colours
[n
] = RGB(r
, g
, b
);
3565 void palette_set(int n
, int r
, int g
, int b
)
3567 static const int first
[21] = {
3568 0, 2, 4, 6, 8, 10, 12, 14,
3569 1, 3, 5, 7, 9, 11, 13, 15,
3572 real_palette_set(first
[n
], r
, g
, b
);
3574 real_palette_set(first
[n
] + 1, r
, g
, b
);
3576 HDC hdc
= get_ctx();
3577 UnrealizeObject(pal
);
3578 RealizePalette(hdc
);
3583 void palette_reset(void)
3587 for (i
= 0; i
< NCOLOURS
; i
++) {
3589 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3590 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3591 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3592 logpal
->palPalEntry
[i
].peFlags
= 0;
3593 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3594 defpal
[i
].rgbtGreen
,
3595 defpal
[i
].rgbtBlue
);
3597 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3598 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3603 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3605 RealizePalette(hdc
);
3610 void write_aclip(char *data
, int len
, int must_deselect
)
3615 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3618 lock
= GlobalLock(clipdata
);
3621 memcpy(lock
, data
, len
);
3622 ((unsigned char *) lock
)[len
] = 0;
3623 GlobalUnlock(clipdata
);
3626 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3628 if (OpenClipboard(hwnd
)) {
3630 SetClipboardData(CF_TEXT
, clipdata
);
3633 GlobalFree(clipdata
);
3636 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3640 * Note: unlike write_aclip() this will not append a nul.
3642 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3649 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3651 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3652 len
* sizeof(wchar_t));
3653 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3655 if (!clipdata
|| !clipdata2
) {
3657 GlobalFree(clipdata
);
3659 GlobalFree(clipdata2
);
3662 if (!(lock
= GlobalLock(clipdata
)))
3664 if (!(lock2
= GlobalLock(clipdata2
)))
3667 memcpy(lock
, data
, len
* sizeof(wchar_t));
3668 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3670 GlobalUnlock(clipdata
);
3671 GlobalUnlock(clipdata2
);
3674 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3676 if (OpenClipboard(hwnd
)) {
3678 SetClipboardData(CF_UNICODETEXT
, clipdata
);
3679 SetClipboardData(CF_TEXT
, clipdata2
);
3682 GlobalFree(clipdata
);
3683 GlobalFree(clipdata2
);
3687 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3690 void get_clip(wchar_t ** p
, int *len
)
3692 static HGLOBAL clipdata
= NULL
;
3693 static wchar_t *converted
= 0;
3702 GlobalUnlock(clipdata
);
3705 } else if (OpenClipboard(NULL
)) {
3706 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
3708 *p
= GlobalLock(clipdata
);
3710 for (p2
= *p
; *p2
; p2
++);
3714 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
3718 s
= GlobalLock(clipdata
);
3719 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
3720 *p
= converted
= smalloc(i
* sizeof(wchar_t));
3721 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
3734 * Move `lines' lines from position `from' to position `to' in the
3737 void optimised_move(int to
, int from
, int lines
)
3742 min
= (to
< from ? to
: from
);
3743 max
= to
+ from
- min
;
3745 r
.left
= offset_width
;
3746 r
.right
= offset_width
+ cols
* font_width
;
3747 r
.top
= offset_height
+ min
* font_height
;
3748 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
3749 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
3754 * Print a message box and perform a fatal exit.
3756 void fatalbox(char *fmt
, ...)
3762 vsprintf(stuff
, fmt
, ap
);
3764 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
3769 * Manage window caption / taskbar flashing, if enabled.
3770 * 0 = stop, 1 = maintain, 2 = start
3772 static void flash_window(int mode
)
3774 static long last_flash
= 0;
3775 static int flashing
= 0;
3776 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
3779 FlashWindow(hwnd
, FALSE
);
3783 } else if (mode
== 2) {
3786 last_flash
= GetTickCount();
3788 FlashWindow(hwnd
, TRUE
);
3791 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
3794 long now
= GetTickCount();
3795 long fdiff
= now
- last_flash
;
3796 if (fdiff
< 0 || fdiff
> 450) {
3798 FlashWindow(hwnd
, TRUE
); /* toggle */
3809 if (mode
== BELL_DEFAULT
) {
3811 * For MessageBeep style bells, we want to be careful of
3812 * timing, because they don't have the nice property of
3813 * PlaySound bells that each one cancels the previous
3814 * active one. So we limit the rate to one per 50ms or so.
3816 static long lastbeep
= 0;
3819 beepdiff
= GetTickCount() - lastbeep
;
3820 if (beepdiff
>= 0 && beepdiff
< 50)
3824 * The above MessageBeep call takes time, so we record the
3825 * time _after_ it finishes rather than before it starts.
3827 lastbeep
= GetTickCount();
3828 } else if (mode
== BELL_WAVEFILE
) {
3829 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
3830 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
3831 sprintf(buf
, "Unable to play sound file\n%s\n"
3832 "Using default sound instead", cfg
.bell_wavefile
);
3833 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
3834 MB_OK
| MB_ICONEXCLAMATION
);
3835 cfg
.beep
= BELL_DEFAULT
;
3838 /* Otherwise, either visual bell or disabled; do nothing here */
3840 flash_window(2); /* start */
3845 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3847 * Revised by <wez@thebrainroom.com>
3849 static void flip_full_screen(void)
3851 want_full_screen
= !want_full_screen
;
3853 if (full_screen
== want_full_screen
)
3856 full_screen
= want_full_screen
;
3858 old_wind_placement
.length
= sizeof(old_wind_placement
);
3862 #if !defined(NO_MULTIMON) && defined(MONITOR_DEFAULTTONEAREST)
3863 /* The multi-monitor safe way of doing things */
3867 mon
= MonitorFromWindow(hwnd
, MONITOR_DEFAULTTONEAREST
);
3868 mi
.cbSize
= sizeof(mi
);
3869 GetMonitorInfo(mon
, &mi
);
3870 x
= mi
.rcMonitor
.left
;
3871 y
= mi
.rcMonitor
.top
;
3872 cx
= mi
.rcMonitor
.right
;
3873 cy
= mi
.rcMonitor
.bottom
;
3875 /* good old fashioned way of doing it */
3878 cx
= GetSystemMetrics(SM_CXSCREEN
);
3879 cy
= GetSystemMetrics(SM_CYSCREEN
);
3882 /* save rows for when we "restore" back down again */
3886 GetWindowPlacement(hwnd
, &old_wind_placement
);
3887 SetWindowLong(hwnd
, GWL_STYLE
,
3888 GetWindowLong(hwnd
, GWL_STYLE
)
3889 & ~((cfg
.scrollbar_in_fullscreen ?
0 : WS_VSCROLL
)
3890 | WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
));
3891 /* become topmost */
3892 SetWindowPos(hwnd
, HWND_TOP
, x
, y
, cx
, cy
, SWP_FRAMECHANGED
);
3894 was_full_screen
= 1;
3895 SetWindowLong(hwnd
, GWL_STYLE
,
3896 GetWindowLong(hwnd
, GWL_STYLE
)
3897 | (cfg
.scrollbar ? WS_VSCROLL
: 0)
3898 | WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
);
3899 SetWindowPos(hwnd
, HWND_TOP
, 0, 0, 0, 0,
3900 SWP_NOMOVE
|SWP_NOSIZE
|SWP_FRAMECHANGED
);
3901 SetWindowPlacement(hwnd
,&old_wind_placement
);
3903 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
3904 MF_BYCOMMAND
| full_screen ? MF_CHECKED
: MF_UNCHECKED
);