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
, int alt
)
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
, alt
);
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
, alt
);
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 int is_alt_pressed(void)
1391 int r
= GetKeyboardState(keystate
);
1394 if (keystate
[VK_MENU
] & 0x80)
1396 if (keystate
[VK_RMENU
] & 0x80)
1401 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1402 WPARAM wParam
, LPARAM lParam
)
1405 static int ignore_clip
= FALSE
;
1406 static int resizing
= FALSE
;
1407 static int need_backend_resize
= FALSE
;
1411 if (pending_netevent
)
1412 enact_pending_netevent();
1418 if (cfg
.ping_interval
> 0) {
1421 if (now
- last_movement
> cfg
.ping_interval
) {
1422 back
->special(TS_PING
);
1423 last_movement
= now
;
1426 net_pending_errors();
1432 if (!cfg
.warn_on_close
|| session_closed
||
1434 "Are you sure you want to close this session?",
1435 "PuTTY Exit Confirmation",
1436 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1437 DestroyWindow(hwnd
);
1444 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1456 PROCESS_INFORMATION pi
;
1457 HANDLE filemap
= NULL
;
1459 if (wParam
== IDM_DUPSESS
) {
1461 * Allocate a file-mapping memory chunk for the
1464 SECURITY_ATTRIBUTES sa
;
1467 sa
.nLength
= sizeof(sa
);
1468 sa
.lpSecurityDescriptor
= NULL
;
1469 sa
.bInheritHandle
= TRUE
;
1470 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1473 0, sizeof(Config
), NULL
);
1475 p
= (Config
*) MapViewOfFile(filemap
,
1477 0, 0, sizeof(Config
));
1479 *p
= cfg
; /* structure copy */
1483 sprintf(c
, "putty &%p", filemap
);
1485 } else if (wParam
== IDM_SAVEDSESS
) {
1487 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1488 cl
= smalloc(16 + strlen(session
)); /* 8, but play safe */
1490 cl
= NULL
; /* not a very important failure mode */
1492 sprintf(cl
, "putty @%s", session
);
1498 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1500 si
.lpReserved
= NULL
;
1501 si
.lpDesktop
= NULL
;
1505 si
.lpReserved2
= NULL
;
1506 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1507 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1510 CloseHandle(filemap
);
1520 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1523 if (!do_reconfig(hwnd
))
1526 /* If user forcibly disables full-screen, gracefully unzoom */
1527 if (full_screen
&& !cfg
.fullscreenonaltenter
) {
1531 if (strcmp(prev_cfg
.logfilename
, cfg
.logfilename
) ||
1532 prev_cfg
.logtype
!= cfg
.logtype
) {
1533 logfclose(); /* reset logging */
1539 * Flush the line discipline's edit buffer in the
1540 * case where local editing has just been disabled.
1542 ldisc_send(NULL
, 0, 0);
1550 /* Screen size changed ? */
1551 if (cfg
.height
!= prev_cfg
.height
||
1552 cfg
.width
!= prev_cfg
.width
||
1553 cfg
.savelines
!= prev_cfg
.savelines
||
1554 cfg
.resize_action
!= RESIZE_TERM
)
1555 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1557 /* Enable or disable the scroll bar, etc */
1559 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1560 LONG nexflag
, exflag
=
1561 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1564 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1565 if (cfg
.alwaysontop
) {
1566 nexflag
|= WS_EX_TOPMOST
;
1567 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1568 SWP_NOMOVE
| SWP_NOSIZE
);
1570 nexflag
&= ~(WS_EX_TOPMOST
);
1571 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1572 SWP_NOMOVE
| SWP_NOSIZE
);
1575 if (cfg
.sunken_edge
)
1576 nexflag
|= WS_EX_CLIENTEDGE
;
1578 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1584 nflg
&= ~WS_VSCROLL
;
1585 if (cfg
.resize_action
== RESIZE_DISABLED
)
1586 nflg
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1588 nflg
|= (WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1590 if (nflg
!= flag
|| nexflag
!= exflag
) {
1592 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1593 if (nexflag
!= exflag
)
1594 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1596 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1597 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1598 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1606 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1611 set_title(cfg
.wintitle
);
1612 if (IsIconic(hwnd
)) {
1614 cfg
.win_name_always ? window_name
:
1618 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1619 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1620 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1621 cfg
.fontheight
!= prev_cfg
.fontheight
||
1622 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1623 cfg
.vtmode
!= prev_cfg
.vtmode
||
1624 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1625 (cfg
.resize_action
!= RESIZE_FONT
&&
1626 prev_cfg
.resize_action
== RESIZE_FONT
))
1629 InvalidateRect(hwnd
, NULL
, TRUE
);
1630 reset_window(init_lvl
);
1631 net_pending_errors();
1644 back
->special(TS_AYT
);
1645 net_pending_errors();
1648 back
->special(TS_BRK
);
1649 net_pending_errors();
1652 back
->special(TS_SYNCH
);
1653 net_pending_errors();
1656 back
->special(TS_EC
);
1657 net_pending_errors();
1660 back
->special(TS_EL
);
1661 net_pending_errors();
1664 back
->special(TS_GA
);
1665 net_pending_errors();
1668 back
->special(TS_NOP
);
1669 net_pending_errors();
1672 back
->special(TS_ABORT
);
1673 net_pending_errors();
1676 back
->special(TS_AO
);
1677 net_pending_errors();
1680 back
->special(TS_IP
);
1681 net_pending_errors();
1684 back
->special(TS_SUSP
);
1685 net_pending_errors();
1688 back
->special(TS_EOR
);
1689 net_pending_errors();
1692 back
->special(TS_EOF
);
1693 net_pending_errors();
1700 * We get this if the System menu has been activated.
1701 * This might happen from within TranslateKey, in which
1702 * case it really wants to be followed by a `space'
1703 * character to actually _bring the menu up_ rather
1704 * than just sitting there in `ready to appear' state.
1707 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1709 case IDM_FULLSCREEN
:
1713 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1714 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1719 #define X_POS(l) ((int)(short)LOWORD(l))
1720 #define Y_POS(l) ((int)(short)HIWORD(l))
1722 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1723 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1724 #define WHEEL_DELTA 120
1727 wheel_accumulator
+= (short) HIWORD(wParam
);
1728 wParam
= LOWORD(wParam
);
1730 /* process events when the threshold is reached */
1731 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
1734 /* reduce amount for next time */
1735 if (wheel_accumulator
> 0) {
1737 wheel_accumulator
-= WHEEL_DELTA
;
1738 } else if (wheel_accumulator
< 0) {
1740 wheel_accumulator
+= WHEEL_DELTA
;
1744 if (send_raw_mouse
) {
1745 /* send a mouse-down followed by a mouse up */
1749 TO_CHR_X(X_POS(lParam
)),
1750 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1751 wParam
& MK_CONTROL
, is_alt_pressed());
1752 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1753 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1754 wParam
& MK_CONTROL
, is_alt_pressed());
1756 /* trigger a scroll */
1758 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
1763 case WM_LBUTTONDOWN
:
1764 case WM_MBUTTONDOWN
:
1765 case WM_RBUTTONDOWN
:
1773 case WM_LBUTTONDOWN
:
1777 case WM_MBUTTONDOWN
:
1778 button
= MBT_MIDDLE
;
1781 case WM_RBUTTONDOWN
:
1790 button
= MBT_MIDDLE
;
1798 button
= press
= 0; /* shouldn't happen */
1803 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1804 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
,
1808 term_mouse(button
, MA_RELEASE
,
1809 TO_CHR_X(X_POS(lParam
)),
1810 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1811 wParam
& MK_CONTROL
, is_alt_pressed());
1819 * Add the mouse position and message time to the random
1822 noise_ultralight(lParam
);
1824 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1826 if (wParam
& MK_LBUTTON
)
1828 else if (wParam
& MK_MBUTTON
)
1832 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1833 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1834 wParam
& MK_CONTROL
, is_alt_pressed());
1837 case WM_NCMOUSEMOVE
:
1839 noise_ultralight(lParam
);
1841 case WM_IGNORE_CLIP
:
1842 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1844 case WM_DESTROYCLIPBOARD
:
1847 ignore_clip
= FALSE
;
1853 hdc
= BeginPaint(hwnd
, &p
);
1855 SelectPalette(hdc
, pal
, TRUE
);
1856 RealizePalette(hdc
);
1859 (p
.rcPaint
.left
-offset_width
)/font_width
,
1860 (p
.rcPaint
.top
-offset_height
)/font_height
,
1861 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
1862 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
1865 p
.rcPaint
.left
< offset_width
||
1866 p
.rcPaint
.top
< offset_height
||
1867 p
.rcPaint
.right
>= offset_width
+ font_width
*cols
||
1868 p
.rcPaint
.bottom
>= offset_height
+ font_height
*rows
)
1870 HBRUSH fillcolour
, oldbrush
;
1872 fillcolour
= CreateSolidBrush (
1873 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1874 oldbrush
= SelectObject(hdc
, fillcolour
);
1875 edge
= CreatePen(PS_SOLID
, 0,
1876 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1877 oldpen
= SelectObject(hdc
, edge
);
1879 ExcludeClipRect(hdc
,
1880 offset_width
, offset_height
,
1881 offset_width
+font_width
*cols
,
1882 offset_height
+font_height
*rows
);
1884 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1885 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1887 // SelectClipRgn(hdc, NULL);
1889 SelectObject(hdc
, oldbrush
);
1890 DeleteObject(fillcolour
);
1891 SelectObject(hdc
, oldpen
);
1894 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
1895 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
1901 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1902 * but the only one that's likely to try to overload us is FD_READ.
1903 * This means buffering just one is fine.
1905 if (pending_netevent
)
1906 enact_pending_netevent();
1908 pending_netevent
= TRUE
;
1909 pend_netevent_wParam
= wParam
;
1910 pend_netevent_lParam
= lParam
;
1911 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
1912 enact_pending_netevent();
1914 time(&last_movement
);
1918 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
1920 flash_window(0); /* stop */
1932 case WM_ENTERSIZEMOVE
:
1933 #ifdef RDB_DEBUG_PATCH
1934 debug((27, "WM_ENTERSIZEMOVE"));
1938 need_backend_resize
= FALSE
;
1940 case WM_EXITSIZEMOVE
:
1943 #ifdef RDB_DEBUG_PATCH
1944 debug((27, "WM_EXITSIZEMOVE"));
1946 if (need_backend_resize
) {
1947 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1948 InvalidateRect(hwnd
, NULL
, TRUE
);
1953 * This does two jobs:
1954 * 1) Keep the sizetip uptodate
1955 * 2) Make sure the window size is _stepped_ in units of the font size.
1957 if (cfg
.resize_action
== RESIZE_TERM
&& !alt_pressed
) {
1958 int width
, height
, w
, h
, ew
, eh
;
1959 LPRECT r
= (LPRECT
) lParam
;
1961 if ( !need_backend_resize
&&
1962 (cfg
.height
!= rows
|| cfg
.width
!= cols
)) {
1964 * Great! It seems the host has been changing the terminal
1965 * size, well the user is now grabbing so this is probably
1966 * the least confusing solution in the long run even though
1967 * it a is suprise. Unfortunatly the only way to prevent
1968 * this seems to be to let the host change the window size
1969 * and as that's a user option we're still right back here.
1971 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1973 InvalidateRect(hwnd
, NULL
, TRUE
);
1974 need_backend_resize
= TRUE
;
1977 width
= r
->right
- r
->left
- extra_width
;
1978 height
= r
->bottom
- r
->top
- extra_height
;
1979 w
= (width
+ font_width
/ 2) / font_width
;
1982 h
= (height
+ font_height
/ 2) / font_height
;
1985 UpdateSizeTip(hwnd
, w
, h
);
1986 ew
= width
- w
* font_width
;
1987 eh
= height
- h
* font_height
;
1989 if (wParam
== WMSZ_LEFT
||
1990 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
1996 if (wParam
== WMSZ_TOP
||
1997 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2007 int width
, height
, w
, h
, rv
= 0;
2008 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
2009 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2010 LPRECT r
= (LPRECT
) lParam
;
2012 width
= r
->right
- r
->left
- ex_width
;
2013 height
= r
->bottom
- r
->top
- ex_height
;
2015 w
= (width
+ cols
/2)/cols
;
2016 h
= (height
+ rows
/2)/rows
;
2017 if ( r
->right
!= r
->left
+ w
*cols
+ ex_width
)
2020 if (wParam
== WMSZ_LEFT
||
2021 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2022 r
->left
= r
->right
- w
*cols
- ex_width
;
2024 r
->right
= r
->left
+ w
*cols
+ ex_width
;
2026 if (r
->bottom
!= r
->top
+ h
*rows
+ ex_height
)
2029 if (wParam
== WMSZ_TOP
||
2030 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2031 r
->top
= r
->bottom
- h
*rows
- ex_height
;
2033 r
->bottom
= r
->top
+ h
*rows
+ ex_height
;
2037 /* break; (never reached) */
2039 #ifdef RDB_DEBUG_PATCH
2040 debug((27, "WM_SIZE %s (%d,%d)",
2041 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2042 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2043 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2044 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2046 LOWORD(lParam
), HIWORD(lParam
)));
2048 if (wParam
== SIZE_MINIMIZED
) {
2050 cfg
.win_name_always ? window_name
: icon_name
);
2053 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2054 SetWindowText(hwnd
, window_name
);
2056 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2057 /* A resize, well it better be a minimize. */
2061 int width
, height
, w
, h
;
2063 width
= LOWORD(lParam
);
2064 height
= HIWORD(lParam
);
2067 if (wParam
== SIZE_MAXIMIZED
) {
2071 if (cfg
.resize_action
!= RESIZE_FONT
) {
2072 w
= width
/ font_width
;
2074 h
= height
/ font_height
;
2077 term_size(h
, w
, cfg
.savelines
);
2080 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2082 if (cfg
.resize_action
!= RESIZE_FONT
)
2083 term_size(prev_rows
, prev_cols
, cfg
.savelines
);
2085 } else if (was_full_screen
) {
2086 was_full_screen
= 0;
2087 if (cfg
.resize_action
!= RESIZE_FONT
)
2088 term_size(pre_fs_rows
, pre_fs_cols
, cfg
.savelines
);
2091 /* This is an unexpected resize, these will normally happen
2092 * if the window is too large. Probably either the user
2093 * selected a huge font or the screen size has changed.
2095 * This is also called with minimize.
2097 else reset_window(-1);
2101 * Don't call back->size in mid-resize. (To prevent
2102 * massive numbers of resize events getting sent
2103 * down the connection during an NT opaque drag.)
2106 if (cfg
.resize_action
== RESIZE_TERM
&& !alt_pressed
) {
2107 need_backend_resize
= TRUE
;
2108 w
= (width
-cfg
.window_border
*2) / font_width
;
2110 h
= (height
-cfg
.window_border
*2) / font_height
;
2121 switch (LOWORD(wParam
)) {
2135 term_scroll(0, +rows
/ 2);
2138 term_scroll(0, -rows
/ 2);
2140 case SB_THUMBPOSITION
:
2142 term_scroll(1, HIWORD(wParam
));
2146 case WM_PALETTECHANGED
:
2147 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2148 HDC hdc
= get_ctx();
2150 if (RealizePalette(hdc
) > 0)
2156 case WM_QUERYNEWPALETTE
:
2158 HDC hdc
= get_ctx();
2160 if (RealizePalette(hdc
) > 0)
2172 * Add the scan code and keypress timing to the random
2175 noise_ultralight(lParam
);
2178 * We don't do TranslateMessage since it disassociates the
2179 * resulting CHAR message from the KEYDOWN that sparked it,
2180 * which we occasionally don't want. Instead, we process
2181 * KEYDOWN, and call the Win32 translator functions so that
2182 * we get the translations under _our_ control.
2185 unsigned char buf
[20];
2188 if (wParam
== VK_PROCESSKEY
) {
2191 m
.message
= WM_KEYDOWN
;
2193 m
.lParam
= lParam
& 0xdfff;
2194 TranslateMessage(&m
);
2196 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2198 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2202 * Interrupt an ongoing paste. I'm not sure
2203 * this is sensible, but for the moment it's
2204 * preferable to having to faff about buffering
2210 * We need not bother about stdin backlogs
2211 * here, because in GUI PuTTY we can't do
2212 * anything about it anyway; there's no means
2213 * of asking Windows to hold off on KEYDOWN
2214 * messages. We _have_ to buffer everything
2217 ldisc_send(buf
, len
, 1);
2222 net_pending_errors();
2224 case WM_INPUTLANGCHANGE
:
2226 /* wParam == Font number */
2227 /* lParam == Locale */
2229 HKL NewInputLocale
= (HKL
) lParam
;
2231 // lParam == GetKeyboardLayout(0);
2233 GetLocaleInfo(LOWORD(NewInputLocale
),
2234 LOCALE_IDEFAULTANSICODEPAGE
, lbuf
, sizeof(lbuf
));
2236 kbd_codepage
= atoi(lbuf
);
2239 case WM_IME_COMPOSITION
:
2245 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2246 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2248 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2249 break; /* fall back to DefWindowProc */
2251 hIMC
= ImmGetContext(hwnd
);
2252 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2255 buff
= (char*) smalloc(n
);
2256 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2257 luni_send((unsigned short *)buff
, n
/ 2, 1);
2260 ImmReleaseContext(hwnd
, hIMC
);
2265 if (wParam
& 0xFF00) {
2266 unsigned char buf
[2];
2269 buf
[0] = wParam
>> 8;
2270 lpage_send(kbd_codepage
, buf
, 2, 1);
2272 char c
= (unsigned char) wParam
;
2273 lpage_send(kbd_codepage
, &c
, 1, 1);
2279 * Nevertheless, we are prepared to deal with WM_CHAR
2280 * messages, should they crop up. So if someone wants to
2281 * post the things to us as part of a macro manoeuvre,
2282 * we're ready to cope.
2285 char c
= (unsigned char)wParam
;
2286 lpage_send(CP_ACP
, &c
, 1, 1);
2290 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2291 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2296 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2300 * Move the system caret. (We maintain one, even though it's
2301 * invisible, for the benefit of blind people: apparently some
2302 * helper software tracks the system caret, so we should arrange to
2305 void sys_cursor(int x
, int y
)
2310 if (!has_focus
) return;
2312 SetCaretPos(x
* font_width
+ offset_width
,
2313 y
* font_height
+ offset_height
);
2315 /* IMM calls on Win98 and beyond only */
2316 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2318 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2319 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2321 /* we should have the IMM functions */
2322 hIMC
= ImmGetContext(hwnd
);
2323 cf
.dwStyle
= CFS_POINT
;
2324 cf
.ptCurrentPos
.x
= x
* font_width
;
2325 cf
.ptCurrentPos
.y
= y
* font_height
;
2326 ImmSetCompositionWindow(hIMC
, &cf
);
2328 ImmReleaseContext(hwnd
, hIMC
);
2332 * Draw a line of text in the window, at given character
2333 * coordinates, in given attributes.
2335 * We are allowed to fiddle with the contents of `text'.
2337 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2338 unsigned long attr
, int lattr
)
2341 int nfg
, nbg
, nfont
;
2344 int force_manual_underline
= 0;
2345 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2346 int char_width
= fnt_width
;
2347 int text_adjust
= 0;
2348 static int *IpDx
= 0, IpDxLEN
= 0;
2350 if (attr
& ATTR_WIDE
)
2353 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2355 if (len
> IpDxLEN
) {
2357 IpDx
= smalloc((len
+ 16) * sizeof(int));
2358 IpDxLEN
= (len
+ 16);
2360 for (i
= 0; i
< IpDxLEN
; i
++)
2361 IpDx
[i
] = char_width
;
2364 /* Only want the left half of double width lines */
2365 if (lattr
!= LATTR_NORM
&& x
*2 >= cols
)
2373 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
2374 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2375 attr
^= ATTR_CUR_XOR
;
2379 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2380 /* Assume a poorman font is borken in other ways too. */
2390 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2393 if (attr
& ATTR_NARROW
)
2394 nfont
|= FONT_NARROW
;
2396 /* Special hack for the VT100 linedraw glyphs. */
2397 if ((attr
& CSET_MASK
) == 0x2300) {
2398 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2399 switch ((unsigned char) (text
[0])) {
2401 text_adjust
= -2 * font_height
/ 5;
2404 text_adjust
= -1 * font_height
/ 5;
2407 text_adjust
= font_height
/ 5;
2410 text_adjust
= 2 * font_height
/ 5;
2413 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2416 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2417 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2418 if (attr
& ATTR_UNDER
) {
2419 attr
&= ~ATTR_UNDER
;
2420 force_manual_underline
= 1;
2425 /* Anything left as an original character set is unprintable. */
2426 if (DIRECT_CHAR(attr
)) {
2429 memset(text
, 0xFD, len
);
2433 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2436 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2437 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2438 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2440 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2441 nfont
|= FONT_UNDERLINE
;
2442 another_font(nfont
);
2443 if (!fonts
[nfont
]) {
2444 if (nfont
& FONT_UNDERLINE
)
2445 force_manual_underline
= 1;
2446 /* Don't do the same for manual bold, it could be bad news. */
2448 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2450 another_font(nfont
);
2452 nfont
= FONT_NORMAL
;
2453 if (attr
& ATTR_REVERSE
) {
2458 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2460 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2464 SelectObject(hdc
, fonts
[nfont
]);
2465 SetTextColor(hdc
, fg
);
2466 SetBkColor(hdc
, bg
);
2467 SetBkMode(hdc
, OPAQUE
);
2470 line_box
.right
= x
+ char_width
* len
;
2471 line_box
.bottom
= y
+ font_height
;
2473 /* Only want the left half of double width lines */
2474 if (line_box
.right
> font_width
*cols
+offset_width
)
2475 line_box
.right
= font_width
*cols
+offset_width
;
2477 /* We're using a private area for direct to font. (512 chars.) */
2478 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2479 /* Ho Hum, dbcs fonts are a PITA! */
2480 /* To display on W9x I have to convert to UCS */
2481 static wchar_t *uni_buf
= 0;
2482 static int uni_len
= 0;
2484 if (len
> uni_len
) {
2486 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2489 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2490 uni_buf
[nlen
] = 0xFFFD;
2491 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2492 IpDx
[nlen
] += char_width
;
2493 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2494 text
+mptr
, 2, uni_buf
+nlen
, 1);
2499 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2500 text
+mptr
, 1, uni_buf
+nlen
, 1);
2508 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2509 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2510 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2511 SetBkMode(hdc
, TRANSPARENT
);
2512 ExtTextOutW(hdc
, x
- 1,
2513 y
- font_height
* (lattr
==
2514 LATTR_BOT
) + text_adjust
,
2515 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2519 } else if (DIRECT_FONT(attr
)) {
2521 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2522 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2523 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2524 SetBkMode(hdc
, TRANSPARENT
);
2526 /* GRR: This draws the character outside it's box and can leave
2527 * 'droppings' even with the clip box! I suppose I could loop it
2528 * one character at a time ... yuk.
2530 * Or ... I could do a test print with "W", and use +1 or -1 for this
2531 * shift depending on if the leftmost column is blank...
2533 ExtTextOut(hdc
, x
- 1,
2534 y
- font_height
* (lattr
==
2535 LATTR_BOT
) + text_adjust
,
2536 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2539 /* And 'normal' unicode characters */
2540 static WCHAR
*wbuf
= NULL
;
2541 static int wlen
= 0;
2546 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2548 for (i
= 0; i
< len
; i
++)
2549 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2552 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2553 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2555 /* And the shadow bold hack. */
2556 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2557 SetBkMode(hdc
, TRANSPARENT
);
2558 ExtTextOutW(hdc
, x
- 1,
2559 y
- font_height
* (lattr
==
2560 LATTR_BOT
) + text_adjust
,
2561 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2564 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2565 (und_mode
== UND_LINE
2566 && (attr
& ATTR_UNDER
)))) {
2569 if (lattr
== LATTR_BOT
)
2570 dec
= dec
* 2 - font_height
;
2572 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2573 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2574 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2575 oldpen
= SelectObject(hdc
, oldpen
);
2576 DeleteObject(oldpen
);
2580 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2581 unsigned long attr
, int lattr
)
2587 int ctype
= cfg
.cursor_type
;
2589 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2590 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2591 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2595 attr
|= TATTR_RIGHTCURS
;
2598 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2599 if (attr
& ATTR_WIDE
)
2606 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2609 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2610 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2611 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2612 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2613 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2614 Polyline(hdc
, pts
, 5);
2615 oldpen
= SelectObject(hdc
, oldpen
);
2616 DeleteObject(oldpen
);
2617 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2618 int startx
, starty
, dx
, dy
, length
, i
;
2621 starty
= y
+ descent
;
2624 length
= char_width
;
2627 if (attr
& TATTR_RIGHTCURS
)
2628 xadjust
= char_width
- 1;
2629 startx
= x
+ xadjust
;
2633 length
= font_height
;
2635 if (attr
& TATTR_ACTCURS
) {
2638 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2639 MoveToEx(hdc
, startx
, starty
, NULL
);
2640 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2641 oldpen
= SelectObject(hdc
, oldpen
);
2642 DeleteObject(oldpen
);
2644 for (i
= 0; i
< length
; i
++) {
2646 SetPixel(hdc
, startx
, starty
, colours
[23]);
2655 /* This function gets the actual width of a character in the normal font.
2657 int CharWidth(Context ctx
, int uc
) {
2661 /* If the font max is the same as the font ave width then this
2662 * function is a no-op.
2664 if (!font_dualwidth
) return 1;
2666 switch (uc
& CSET_MASK
) {
2668 uc
= unitab_line
[uc
& 0xFF];
2671 uc
= unitab_xterm
[uc
& 0xFF];
2674 uc
= unitab_scoacs
[uc
& 0xFF];
2677 if (DIRECT_FONT(uc
)) {
2678 if (dbcs_screenfont
) return 1;
2680 /* Speedup, I know of no font where ascii is the wrong width */
2681 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
2684 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
2685 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2686 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
2687 another_font(FONT_OEM
);
2688 if (!fonts
[FONT_OEM
]) return 0;
2690 SelectObject(hdc
, fonts
[FONT_OEM
]);
2694 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
2695 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
2698 /* Speedup, I know of no font where ascii is the wrong width */
2699 if (uc
>= ' ' && uc
<= '~') return 1;
2701 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2702 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
2703 /* Okay that one worked */ ;
2704 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
2705 /* This should work on 9x too, but it's "less accurate" */ ;
2710 ibuf
+= font_width
/ 2 -1;
2717 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2718 * codes. Returns number of bytes used or zero to drop the message
2719 * or -1 to forward the message to windows.
2721 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2722 unsigned char *output
)
2725 int scan
, left_alt
= 0, key_down
, shift_state
;
2727 unsigned char *p
= output
;
2728 static int alt_sum
= 0;
2730 HKL kbd_layout
= GetKeyboardLayout(0);
2732 static WORD keys
[3];
2733 static int compose_char
= 0;
2734 static WPARAM compose_key
= 0;
2736 r
= GetKeyboardState(keystate
);
2738 memset(keystate
, 0, sizeof(keystate
));
2741 #define SHOW_TOASCII_RESULT
2742 { /* Tell us all about key events */
2743 static BYTE oldstate
[256];
2744 static int first
= 1;
2748 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2751 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2753 } else if ((HIWORD(lParam
) & KF_UP
)
2754 && scan
== (HIWORD(lParam
) & 0xFF)) {
2758 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2759 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2772 debug(("VK_%02x", wParam
));
2774 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2776 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2778 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2779 if (ch
>= ' ' && ch
<= '~')
2780 debug((", '%c'", ch
));
2782 debug((", $%02x", ch
));
2785 debug((", KB0=%02x", keys
[0]));
2787 debug((", KB1=%02x", keys
[1]));
2789 debug((", KB2=%02x", keys
[2]));
2791 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2793 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2795 if ((HIWORD(lParam
) & KF_EXTENDED
))
2797 if ((HIWORD(lParam
) & KF_UP
))
2801 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2802 else if ((HIWORD(lParam
) & KF_UP
))
2803 oldstate
[wParam
& 0xFF] ^= 0x80;
2805 oldstate
[wParam
& 0xFF] ^= 0x81;
2807 for (ch
= 0; ch
< 256; ch
++)
2808 if (oldstate
[ch
] != keystate
[ch
])
2809 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2811 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2815 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2816 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2820 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2821 if ((cfg
.funky_type
== 3 ||
2822 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2823 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2825 wParam
= VK_EXECUTE
;
2827 /* UnToggle NUMLock */
2828 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2829 keystate
[VK_NUMLOCK
] ^= 1;
2832 /* And write back the 'adjusted' state */
2833 SetKeyboardState(keystate
);
2836 /* Disable Auto repeat if required */
2837 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2840 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2843 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2845 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2846 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2847 if (cfg
.ctrlaltkeys
)
2848 keystate
[VK_MENU
] = 0;
2850 keystate
[VK_RMENU
] = 0x80;
2855 alt_pressed
= (left_alt
&& key_down
);
2857 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2858 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2859 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2861 /* Note if AltGr was pressed and if it was used as a compose key */
2862 if (!compose_state
) {
2863 compose_key
= 0x100;
2864 if (cfg
.compose_key
) {
2865 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2866 compose_key
= wParam
;
2868 if (wParam
== VK_APPS
)
2869 compose_key
= wParam
;
2872 if (wParam
== compose_key
) {
2873 if (compose_state
== 0
2874 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2876 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2880 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
2884 * Record that we pressed key so the scroll window can be reset, but
2885 * be careful to avoid Shift-UP/Down
2887 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
) {
2891 if (compose_state
> 1 && left_alt
)
2894 /* Sanitize the number pad if not using a PC NumPad */
2895 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2896 && cfg
.funky_type
!= 2)
2897 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
2898 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
2902 nParam
= VK_NUMPAD0
;
2905 nParam
= VK_NUMPAD1
;
2908 nParam
= VK_NUMPAD2
;
2911 nParam
= VK_NUMPAD3
;
2914 nParam
= VK_NUMPAD4
;
2917 nParam
= VK_NUMPAD5
;
2920 nParam
= VK_NUMPAD6
;
2923 nParam
= VK_NUMPAD7
;
2926 nParam
= VK_NUMPAD8
;
2929 nParam
= VK_NUMPAD9
;
2932 nParam
= VK_DECIMAL
;
2936 if (keystate
[VK_NUMLOCK
] & 1)
2943 /* If a key is pressed and AltGr is not active */
2944 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
2945 /* Okay, prepare for most alts then ... */
2949 /* Lets see if it's a pattern we know all about ... */
2950 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
2951 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
2954 if (wParam
== VK_NEXT
&& shift_state
== 1) {
2955 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
2958 if (wParam
== VK_INSERT
&& shift_state
== 1) {
2962 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
2965 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
2966 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
2969 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
) {
2973 /* Control-Numlock for app-keypad mode switch */
2974 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
2975 app_keypad_keys
^= 1;
2979 /* Nethack keypad */
2980 if (cfg
.nethack_keypad
&& !left_alt
) {
2983 *p
++ = shift_state ?
'B' : 'b';
2986 *p
++ = shift_state ?
'J' : 'j';
2989 *p
++ = shift_state ?
'N' : 'n';
2992 *p
++ = shift_state ?
'H' : 'h';
2995 *p
++ = shift_state ?
'.' : '.';
2998 *p
++ = shift_state ?
'L' : 'l';
3001 *p
++ = shift_state ?
'Y' : 'y';
3004 *p
++ = shift_state ?
'K' : 'k';
3007 *p
++ = shift_state ?
'U' : 'u';
3012 /* Application Keypad */
3016 if (cfg
.funky_type
== 3 ||
3017 (cfg
.funky_type
<= 1 &&
3018 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3032 if (app_keypad_keys
&& !cfg
.no_applic_k
)
3069 if (cfg
.funky_type
== 2) {
3074 } else if (shift_state
)
3081 if (cfg
.funky_type
== 2)
3085 if (cfg
.funky_type
== 2)
3089 if (cfg
.funky_type
== 2)
3094 if (HIWORD(lParam
) & KF_EXTENDED
)
3100 if (xkey
>= 'P' && xkey
<= 'S')
3101 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3103 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3105 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3110 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3111 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3115 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3121 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3125 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3129 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3134 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3139 /* Control-2 to Control-8 are special */
3140 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3141 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3144 if (shift_state
== 2 && wParam
== 0xBD) {
3148 if (shift_state
== 2 && wParam
== 0xDF) {
3152 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
3159 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3160 * for integer decimal nn.)
3162 * We also deal with the weird ones here. Linux VCs replace F1
3163 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3164 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3170 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3173 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3176 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3179 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3182 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3185 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3188 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3191 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3194 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3197 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3230 if ((shift_state
&2) == 0) switch (wParam
) {
3250 /* Reorder edit keys to physical order */
3251 if (cfg
.funky_type
== 3 && code
<= 6)
3252 code
= "\0\2\1\4\5\3\6"[code
];
3254 if (vt52_mode
&& code
> 0 && code
<= 6) {
3255 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3259 if (cfg
.funky_type
== 5 && /* SCO function keys */
3260 code
>= 11 && code
<= 34) {
3261 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3264 case VK_F1
: index
= 0; break;
3265 case VK_F2
: index
= 1; break;
3266 case VK_F3
: index
= 2; break;
3267 case VK_F4
: index
= 3; break;
3268 case VK_F5
: index
= 4; break;
3269 case VK_F6
: index
= 5; break;
3270 case VK_F7
: index
= 6; break;
3271 case VK_F8
: index
= 7; break;
3272 case VK_F9
: index
= 8; break;
3273 case VK_F10
: index
= 9; break;
3274 case VK_F11
: index
= 10; break;
3275 case VK_F12
: index
= 11; break;
3277 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3278 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3279 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3282 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3283 code
>= 1 && code
<= 6) {
3284 char codes
[] = "HL.FIG";
3288 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3292 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3299 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3302 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3305 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3306 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3309 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3311 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3313 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3316 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3317 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3321 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3326 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3327 * some reason seems to send VK_CLEAR to Windows...).
3350 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3352 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
3353 /* VT100 & VT102 manuals both state the app cursor keys
3354 * only work if the app keypad is on.
3356 if (!app_keypad_keys
)
3358 /* Useful mapping of Ctrl-arrows */
3359 if (shift_state
== 2)
3363 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3365 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3372 * Finally, deal with Return ourselves. (Win95 seems to
3373 * foul it up when Alt is pressed, for some reason.)
3375 if (wParam
== VK_RETURN
) { /* Return */
3381 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3382 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3387 /* Okay we've done everything interesting; let windows deal with
3388 * the boring stuff */
3392 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3393 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3395 keystate
[VK_CAPITAL
] = 0;
3398 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3399 #ifdef SHOW_TOASCII_RESULT
3400 if (r
== 1 && !key_down
) {
3402 if (in_utf
|| dbcs_screenfont
)
3403 debug((", (U+%04x)", alt_sum
));
3405 debug((", LCH(%d)", alt_sum
));
3407 debug((", ACH(%d)", keys
[0]));
3412 for (r1
= 0; r1
< r
; r1
++) {
3413 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3422 * Interrupt an ongoing paste. I'm not sure this is
3423 * sensible, but for the moment it's preferable to
3424 * having to faff about buffering things.
3429 for (i
= 0; i
< r
; i
++) {
3430 unsigned char ch
= (unsigned char) keys
[i
];
3432 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3437 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3441 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3442 MessageBeep(MB_ICONHAND
);
3446 luni_send(&keybuf
, 1, 1);
3454 if (in_utf
|| dbcs_screenfont
) {
3456 luni_send(&keybuf
, 1, 1);
3458 ch
= (char) alt_sum
;
3460 * We need not bother about stdin
3461 * backlogs here, because in GUI PuTTY
3462 * we can't do anything about it
3463 * anyway; there's no means of asking
3464 * Windows to hold off on KEYDOWN
3465 * messages. We _have_ to buffer
3466 * everything we're sent.
3468 ldisc_send(&ch
, 1, 1);
3472 lpage_send(kbd_codepage
, &ch
, 1, 1);
3474 if(capsOn
&& ch
< 0x80) {
3477 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3478 luni_send(cbuf
+!left_alt
, 1+!!left_alt
, 1);
3483 lpage_send(kbd_codepage
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3489 /* This is so the ALT-Numpad and dead keys work correctly. */
3494 /* If we're definitly not building up an ALT-54321 then clear it */
3497 /* If we will be using alt_sum fix the 256s */
3498 else if (keys
[0] && (in_utf
|| dbcs_screenfont
))
3503 * ALT alone may or may not want to bring up the System menu.
3504 * If it's not meant to, we return 0 on presses or releases of
3505 * ALT, to show that we've swallowed the keystroke. Otherwise
3506 * we return -1, which means Windows will give the keystroke
3507 * its default handling (i.e. bring up the System menu).
3509 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3515 void set_title(char *title
)
3518 window_name
= smalloc(1 + strlen(title
));
3519 strcpy(window_name
, title
);
3520 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3521 SetWindowText(hwnd
, title
);
3524 void set_icon(char *title
)
3527 icon_name
= smalloc(1 + strlen(title
));
3528 strcpy(icon_name
, title
);
3529 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3530 SetWindowText(hwnd
, title
);
3533 void set_sbar(int total
, int start
, int page
)
3540 si
.cbSize
= sizeof(si
);
3541 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3543 si
.nMax
= total
- 1;
3547 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3550 Context
get_ctx(void)
3556 SelectPalette(hdc
, pal
, FALSE
);
3562 void free_ctx(Context ctx
)
3564 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3565 ReleaseDC(hwnd
, ctx
);
3568 static void real_palette_set(int n
, int r
, int g
, int b
)
3571 logpal
->palPalEntry
[n
].peRed
= r
;
3572 logpal
->palPalEntry
[n
].peGreen
= g
;
3573 logpal
->palPalEntry
[n
].peBlue
= b
;
3574 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3575 colours
[n
] = PALETTERGB(r
, g
, b
);
3576 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3578 colours
[n
] = RGB(r
, g
, b
);
3581 void palette_set(int n
, int r
, int g
, int b
)
3583 static const int first
[21] = {
3584 0, 2, 4, 6, 8, 10, 12, 14,
3585 1, 3, 5, 7, 9, 11, 13, 15,
3588 real_palette_set(first
[n
], r
, g
, b
);
3590 real_palette_set(first
[n
] + 1, r
, g
, b
);
3592 HDC hdc
= get_ctx();
3593 UnrealizeObject(pal
);
3594 RealizePalette(hdc
);
3599 void palette_reset(void)
3603 for (i
= 0; i
< NCOLOURS
; i
++) {
3605 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3606 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3607 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3608 logpal
->palPalEntry
[i
].peFlags
= 0;
3609 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3610 defpal
[i
].rgbtGreen
,
3611 defpal
[i
].rgbtBlue
);
3613 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3614 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3619 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3621 RealizePalette(hdc
);
3626 void write_aclip(char *data
, int len
, int must_deselect
)
3631 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3634 lock
= GlobalLock(clipdata
);
3637 memcpy(lock
, data
, len
);
3638 ((unsigned char *) lock
)[len
] = 0;
3639 GlobalUnlock(clipdata
);
3642 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3644 if (OpenClipboard(hwnd
)) {
3646 SetClipboardData(CF_TEXT
, clipdata
);
3649 GlobalFree(clipdata
);
3652 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3656 * Note: unlike write_aclip() this will not append a nul.
3658 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3665 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3667 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3668 len
* sizeof(wchar_t));
3669 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3671 if (!clipdata
|| !clipdata2
) {
3673 GlobalFree(clipdata
);
3675 GlobalFree(clipdata2
);
3678 if (!(lock
= GlobalLock(clipdata
)))
3680 if (!(lock2
= GlobalLock(clipdata2
)))
3683 memcpy(lock
, data
, len
* sizeof(wchar_t));
3684 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3686 GlobalUnlock(clipdata
);
3687 GlobalUnlock(clipdata2
);
3690 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3692 if (OpenClipboard(hwnd
)) {
3694 SetClipboardData(CF_UNICODETEXT
, clipdata
);
3695 SetClipboardData(CF_TEXT
, clipdata2
);
3698 GlobalFree(clipdata
);
3699 GlobalFree(clipdata2
);
3703 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3706 void get_clip(wchar_t ** p
, int *len
)
3708 static HGLOBAL clipdata
= NULL
;
3709 static wchar_t *converted
= 0;
3718 GlobalUnlock(clipdata
);
3721 } else if (OpenClipboard(NULL
)) {
3722 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
3724 *p
= GlobalLock(clipdata
);
3726 for (p2
= *p
; *p2
; p2
++);
3730 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
3734 s
= GlobalLock(clipdata
);
3735 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
3736 *p
= converted
= smalloc(i
* sizeof(wchar_t));
3737 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
3750 * Move `lines' lines from position `from' to position `to' in the
3753 void optimised_move(int to
, int from
, int lines
)
3758 min
= (to
< from ? to
: from
);
3759 max
= to
+ from
- min
;
3761 r
.left
= offset_width
;
3762 r
.right
= offset_width
+ cols
* font_width
;
3763 r
.top
= offset_height
+ min
* font_height
;
3764 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
3765 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
3770 * Print a message box and perform a fatal exit.
3772 void fatalbox(char *fmt
, ...)
3778 vsprintf(stuff
, fmt
, ap
);
3780 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
3785 * Manage window caption / taskbar flashing, if enabled.
3786 * 0 = stop, 1 = maintain, 2 = start
3788 static void flash_window(int mode
)
3790 static long last_flash
= 0;
3791 static int flashing
= 0;
3792 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
3795 FlashWindow(hwnd
, FALSE
);
3799 } else if (mode
== 2) {
3802 last_flash
= GetTickCount();
3804 FlashWindow(hwnd
, TRUE
);
3807 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
3810 long now
= GetTickCount();
3811 long fdiff
= now
- last_flash
;
3812 if (fdiff
< 0 || fdiff
> 450) {
3814 FlashWindow(hwnd
, TRUE
); /* toggle */
3825 if (mode
== BELL_DEFAULT
) {
3827 * For MessageBeep style bells, we want to be careful of
3828 * timing, because they don't have the nice property of
3829 * PlaySound bells that each one cancels the previous
3830 * active one. So we limit the rate to one per 50ms or so.
3832 static long lastbeep
= 0;
3835 beepdiff
= GetTickCount() - lastbeep
;
3836 if (beepdiff
>= 0 && beepdiff
< 50)
3840 * The above MessageBeep call takes time, so we record the
3841 * time _after_ it finishes rather than before it starts.
3843 lastbeep
= GetTickCount();
3844 } else if (mode
== BELL_WAVEFILE
) {
3845 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
3846 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
3847 sprintf(buf
, "Unable to play sound file\n%s\n"
3848 "Using default sound instead", cfg
.bell_wavefile
);
3849 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
3850 MB_OK
| MB_ICONEXCLAMATION
);
3851 cfg
.beep
= BELL_DEFAULT
;
3854 /* Otherwise, either visual bell or disabled; do nothing here */
3856 flash_window(2); /* start */
3861 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3863 * Revised by <wez@thebrainroom.com>
3865 static void flip_full_screen(void)
3867 want_full_screen
= !want_full_screen
;
3869 if (full_screen
== want_full_screen
)
3872 full_screen
= want_full_screen
;
3874 old_wind_placement
.length
= sizeof(old_wind_placement
);
3878 #if !defined(NO_MULTIMON) && defined(MONITOR_DEFAULTTONEAREST)
3879 /* The multi-monitor safe way of doing things */
3883 mon
= MonitorFromWindow(hwnd
, MONITOR_DEFAULTTONEAREST
);
3884 mi
.cbSize
= sizeof(mi
);
3885 GetMonitorInfo(mon
, &mi
);
3886 x
= mi
.rcMonitor
.left
;
3887 y
= mi
.rcMonitor
.top
;
3888 cx
= mi
.rcMonitor
.right
;
3889 cy
= mi
.rcMonitor
.bottom
;
3891 /* good old fashioned way of doing it */
3894 cx
= GetSystemMetrics(SM_CXSCREEN
);
3895 cy
= GetSystemMetrics(SM_CYSCREEN
);
3898 /* save rows for when we "restore" back down again */
3902 GetWindowPlacement(hwnd
, &old_wind_placement
);
3903 SetWindowLong(hwnd
, GWL_STYLE
,
3904 GetWindowLong(hwnd
, GWL_STYLE
)
3905 & ~((cfg
.scrollbar_in_fullscreen ?
0 : WS_VSCROLL
)
3906 | WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
));
3907 /* become topmost */
3908 SetWindowPos(hwnd
, HWND_TOP
, x
, y
, cx
, cy
, SWP_FRAMECHANGED
);
3910 was_full_screen
= 1;
3911 SetWindowLong(hwnd
, GWL_STYLE
,
3912 GetWindowLong(hwnd
, GWL_STYLE
)
3913 | (cfg
.scrollbar ? WS_VSCROLL
: 0)
3914 | WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
);
3915 SetWindowPos(hwnd
, HWND_TOP
, 0, 0, 0, 0,
3916 SWP_NOMOVE
|SWP_NOSIZE
|SWP_FRAMECHANGED
);
3917 SetWindowPlacement(hwnd
,&old_wind_placement
);
3919 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
3920 MF_BYCOMMAND
| full_screen ? MF_CHECKED
: MF_UNCHECKED
);