16 #define COMPILE_MULTIMON_STUBS
27 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
34 #define IDM_SHOWLOG 0x0010
35 #define IDM_NEWSESS 0x0020
36 #define IDM_DUPSESS 0x0030
37 #define IDM_RECONF 0x0040
38 #define IDM_CLRSB 0x0050
39 #define IDM_RESET 0x0060
40 #define IDM_TEL_AYT 0x0070
41 #define IDM_TEL_BRK 0x0080
42 #define IDM_TEL_SYNCH 0x0090
43 #define IDM_TEL_EC 0x00a0
44 #define IDM_TEL_EL 0x00b0
45 #define IDM_TEL_GA 0x00c0
46 #define IDM_TEL_NOP 0x00d0
47 #define IDM_TEL_ABORT 0x00e0
48 #define IDM_TEL_AO 0x00f0
49 #define IDM_TEL_IP 0x0100
50 #define IDM_TEL_SUSP 0x0110
51 #define IDM_TEL_EOR 0x0120
52 #define IDM_TEL_EOF 0x0130
53 #define IDM_HELP 0x0140
54 #define IDM_ABOUT 0x0150
55 #define IDM_SAVEDSESS 0x0160
56 #define IDM_COPYALL 0x0170
57 #define IDM_FULLSCREEN 0x0180
59 #define IDM_SESSLGP 0x0250 /* log type printable */
60 #define IDM_SESSLGA 0x0260 /* log type all chars */
61 #define IDM_SESSLGE 0x0270 /* log end */
62 #define IDM_SAVED_MIN 0x1000
63 #define IDM_SAVED_MAX 0x2000
65 #define WM_IGNORE_CLIP (WM_XUSER + 2)
66 #define WM_FULLSCR_ON_MAX (WM_XUSER + 3)
68 /* Needed for Chinese support and apparently not always defined. */
70 #define VK_PROCESSKEY 0xE5
73 /* Mouse wheel support. */
75 #define WM_MOUSEWHEEL 0x020A /* not defined in earlier SDKs */
78 #define WHEEL_DELTA 120
81 static LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
82 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
83 unsigned char *output
);
84 static void cfgtopalette(void);
85 static void init_palette(void);
86 static void init_fonts(int, int);
87 static void another_font(int);
88 static void deinit_fonts(void);
89 static void set_input_locale(HKL
);
91 static int is_full_screen(void);
92 static void make_full_screen(void);
93 static void clear_full_screen(void);
94 static void flip_full_screen(void);
96 /* Window layout information */
97 static void reset_window(int);
98 static int extra_width
, extra_height
;
99 static int font_width
, font_height
, font_dualwidth
;
100 static int offset_width
, offset_height
;
101 static int was_zoomed
= 0;
102 static int prev_rows
, prev_cols
;
104 static int pending_netevent
= 0;
105 static WPARAM pend_netevent_wParam
= 0;
106 static LPARAM pend_netevent_lParam
= 0;
107 static void enact_pending_netevent(void);
108 static void flash_window(int mode
);
109 static void sys_cursor_update(void);
110 static int is_shift_pressed(void);
111 static int get_fullscreen_rect(RECT
* ss
);
113 static time_t last_movement
= 0;
115 static int caret_x
= -1, caret_y
= -1;
117 static int kbd_codepage
;
120 static Backend
*back
;
121 static void *backhandle
;
123 static struct unicode_data ucsdata
;
124 static int session_closed
;
126 Config cfg
; /* exported to windlg.c */
128 extern struct sesslist sesslist
; /* imported from windlg.c */
130 #define FONT_NORMAL 0
132 #define FONT_UNDERLINE 2
133 #define FONT_BOLDUND 3
134 #define FONT_WIDE 0x04
135 #define FONT_HIGH 0x08
136 #define FONT_NARROW 0x10
138 #define FONT_OEM 0x20
139 #define FONT_OEMBOLD 0x21
140 #define FONT_OEMUND 0x22
141 #define FONT_OEMBOLDUND 0x23
143 #define FONT_MAXNO 0x2F
145 static HFONT fonts
[FONT_MAXNO
];
146 static LOGFONT lfont
;
147 static int fontflag
[FONT_MAXNO
];
149 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
157 static COLORREF colours
[NCOLOURS
];
159 static LPLOGPALETTE logpal
;
160 static RGBTRIPLE defpal
[NCOLOURS
];
164 static HBITMAP caretbm
;
166 static int dbltime
, lasttime
, lastact
;
167 static Mouse_Button lastbtn
;
169 /* this allows xterm-style mouse handling. */
170 static int send_raw_mouse
= 0;
171 static int wheel_accumulator
= 0;
173 static char *window_name
, *icon_name
;
175 static int compose_state
= 0;
177 static int wsa_started
= 0;
179 static OSVERSIONINFO osVersion
;
181 static UINT wm_mousewheel
= WM_MOUSEWHEEL
;
183 /* Dummy routine, only required in plink. */
184 void ldisc_update(void *frontend
, int echo
, int edit
)
188 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
190 static char appname
[] = "PuTTY";
195 int guess_width
, guess_height
;
198 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
200 winsock_ver
= MAKEWORD(1, 1);
201 if (WSAStartup(winsock_ver
, &wsadata
)) {
202 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
203 MB_OK
| MB_ICONEXCLAMATION
);
206 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
207 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
208 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
213 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
216 InitCommonControls();
218 /* Ensure a Maximize setting in Explorer doesn't maximise the
223 ZeroMemory(&osVersion
, sizeof(osVersion
));
224 osVersion
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
225 if (!GetVersionEx ( (OSVERSIONINFO
*) &osVersion
)) {
226 MessageBox(NULL
, "Windows refuses to report a version",
227 "PuTTY Fatal Error", MB_OK
| MB_ICONEXCLAMATION
);
233 * If we're running a version of Windows that doesn't support
234 * WM_MOUSEWHEEL, find out what message number we should be
237 if (osVersion
.dwMajorVersion
< 4 ||
238 (osVersion
.dwMajorVersion
== 4 &&
239 osVersion
.dwPlatformId
!= VER_PLATFORM_WIN32_NT
))
240 wm_mousewheel
= RegisterWindowMessage("MSWHEEL_ROLLMSG");
243 * See if we can find our Help file.
246 char b
[2048], *p
, *q
, *r
;
248 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
250 p
= strrchr(b
, '\\');
251 if (p
&& p
>= r
) r
= p
+1;
253 if (q
&& q
>= r
) r
= q
+1;
254 strcpy(r
, "putty.hlp");
255 if ( (fp
= fopen(b
, "r")) != NULL
) {
256 help_path
= dupstr(b
);
260 strcpy(r
, "putty.cnt");
261 if ( (fp
= fopen(b
, "r")) != NULL
) {
262 help_has_contents
= TRUE
;
265 help_has_contents
= FALSE
;
269 * Process the command line.
275 default_protocol
= DEFAULT_PROTOCOL
;
276 default_port
= DEFAULT_PORT
;
277 cfg
.logtype
= LGTYP_NONE
;
279 do_defaults(NULL
, &cfg
);
284 * Process a couple of command-line options which are more
285 * easily dealt with before the line is broken up into
286 * words. These are the soon-to-be-defunct @sessionname and
287 * the internal-use-only &sharedmemoryhandle, neither of
288 * which are combined with anything else.
290 while (*p
&& isspace(*p
))
294 while (i
> 1 && isspace(p
[i
- 1]))
297 do_defaults(p
+ 1, &cfg
);
298 if (!*cfg
.host
&& !do_config()) {
302 } else if (*p
== '&') {
304 * An initial & means we've been given a command line
305 * containing the hex value of a HANDLE for a file
306 * mapping object, which we must then extract as a
311 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
312 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
313 0, 0, sizeof(Config
))) != NULL
) {
316 CloseHandle(filemap
);
317 } else if (!do_config()) {
323 * Otherwise, break up the command line and deal with
329 split_into_argv(cmdline
, &argc
, &argv
, NULL
);
331 for (i
= 0; i
< argc
; i
++) {
335 ret
= cmdline_process_param(p
, i
+1<argc?argv
[i
+1]:NULL
,
338 cmdline_error("option \"%s\" requires an argument", p
);
339 } else if (ret
== 2) {
340 i
++; /* skip next argument */
341 } else if (ret
== 1) {
342 continue; /* nothing further needs doing */
343 } else if (!strcmp(p
, "-cleanup")) {
345 * `putty -cleanup'. Remove all registry
346 * entries associated with PuTTY, and also find
347 * and delete the random seed file.
350 "This procedure will remove ALL Registry\n"
351 "entries associated with PuTTY, and will\n"
352 "also remove the PuTTY random seed file.\n"
354 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
355 "SESSIONS. Are you really sure you want\n"
358 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
362 } else if (*p
!= '-') {
366 * If we already have a host name, treat
367 * this argument as a port number. NB we
368 * have to treat this as a saved -P
369 * argument, so that it will be deferred
370 * until it's a good moment to run it.
372 int ret
= cmdline_process_param("-P", p
, 1, &cfg
);
374 } else if (!strncmp(q
, "telnet:", 7)) {
376 * If the hostname starts with "telnet:",
377 * set the protocol to Telnet and process
378 * the string as a Telnet URL.
383 if (q
[0] == '/' && q
[1] == '/')
385 cfg
.protocol
= PROT_TELNET
;
387 while (*p
&& *p
!= ':' && *p
!= '/')
396 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
397 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
401 * Otherwise, treat this argument as a host
404 while (*p
&& !isspace(*p
))
408 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
409 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
413 cmdline_error("unknown option \"%s\"", p
);
418 cmdline_run_saved(&cfg
);
420 if (!*cfg
.host
&& !do_config()) {
426 * Trim leading whitespace off the hostname if it's there.
429 int space
= strspn(cfg
.host
, " \t");
430 memmove(cfg
.host
, cfg
.host
+space
, 1+strlen(cfg
.host
)-space
);
433 /* See if host is of the form user@host */
434 if (cfg
.host
[0] != '\0') {
435 char *atsign
= strchr(cfg
.host
, '@');
436 /* Make sure we're not overflowing the user field */
438 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
439 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
440 cfg
.username
[atsign
- cfg
.host
] = '\0';
442 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
447 * Trim a colon suffix off the hostname if it's there.
449 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
452 * Remove any remaining whitespace from the hostname.
456 while (cfg
.host
[p2
] != '\0') {
457 if (cfg
.host
[p2
] != ' ' && cfg
.host
[p2
] != '\t') {
458 cfg
.host
[p1
] = cfg
.host
[p2
];
468 * Select protocol. This is farmed out into a table in a
469 * separate file to enable an ssh-free variant.
474 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
475 if (backends
[i
].protocol
== cfg
.protocol
) {
476 back
= backends
[i
].backend
;
480 MessageBox(NULL
, "Unsupported protocol number found",
481 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
487 /* Check for invalid Port number (i.e. zero) */
489 MessageBox(NULL
, "Invalid Port Number",
490 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
497 wndclass
.lpfnWndProc
= WndProc
;
498 wndclass
.cbClsExtra
= 0;
499 wndclass
.cbWndExtra
= 0;
500 wndclass
.hInstance
= inst
;
501 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
502 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
503 wndclass
.hbrBackground
= NULL
;
504 wndclass
.lpszMenuName
= NULL
;
505 wndclass
.lpszClassName
= appname
;
507 RegisterClass(&wndclass
);
512 memset(&ucsdata
, 0, sizeof(ucsdata
));
514 term
= term_init(&cfg
, &ucsdata
, NULL
);
515 logctx
= log_init(NULL
, &cfg
);
516 term_provide_logctx(term
, logctx
);
521 * Guess some defaults for the window size. This all gets
522 * updated later, so we don't really care too much. However, we
523 * do want the font width/height guesses to correspond to a
524 * large font rather than a small one...
531 term_size(term
, cfg
.height
, cfg
.width
, cfg
.savelines
);
532 guess_width
= extra_width
+ font_width
* term
->cols
;
533 guess_height
= extra_height
+ font_height
* term
->rows
;
536 get_fullscreen_rect(&r
);
537 if (guess_width
> r
.right
- r
.left
)
538 guess_width
= r
.right
- r
.left
;
539 if (guess_height
> r
.bottom
- r
.top
)
540 guess_height
= r
.bottom
- r
.top
;
544 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
547 winmode
&= ~(WS_VSCROLL
);
548 if (cfg
.resize_action
== RESIZE_DISABLED
)
549 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
551 exwinmode
|= WS_EX_TOPMOST
;
553 exwinmode
|= WS_EX_CLIENTEDGE
;
554 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
555 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
556 guess_width
, guess_height
,
557 NULL
, NULL
, inst
, NULL
);
561 * Initialise the fonts, simultaneously correcting the guesses
562 * for font_{width,height}.
567 * Correct the guesses for extra_{width,height}.
571 GetWindowRect(hwnd
, &wr
);
572 GetClientRect(hwnd
, &cr
);
573 offset_width
= offset_height
= cfg
.window_border
;
574 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
575 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
579 * Resize the window, now we know what size we _really_ want it
582 guess_width
= extra_width
+ font_width
* term
->cols
;
583 guess_height
= extra_height
+ font_height
* term
->rows
;
584 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
585 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
588 * Set up a caret bitmap, with no content.
592 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
593 bits
= smalloc(size
);
594 memset(bits
, 0, size
);
595 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
598 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
601 * Initialise the scroll bar.
606 si
.cbSize
= sizeof(si
);
607 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
609 si
.nMax
= term
->rows
- 1;
610 si
.nPage
= term
->rows
;
612 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
616 * Start up the telnet connection.
620 char msg
[1024], *title
;
623 error
= back
->init((void *)term
, &backhandle
, &cfg
,
624 cfg
.host
, cfg
.port
, &realhost
, cfg
.tcp_nodelay
);
625 back
->provide_logctx(backhandle
, logctx
);
627 sprintf(msg
, "Unable to open connection to\n"
628 "%.800s\n" "%s", cfg
.host
, error
);
629 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
632 window_name
= icon_name
= NULL
;
634 title
= cfg
.wintitle
;
636 sprintf(msg
, "%s - PuTTY", realhost
);
640 set_title(NULL
, title
);
641 set_icon(NULL
, title
);
645 * Connect the terminal to the backend for resize purposes.
647 term_provide_resize_fn(term
, back
->size
, backhandle
);
650 * Set up a line discipline.
652 ldisc
= ldisc_create(&cfg
, term
, back
, backhandle
, NULL
);
654 session_closed
= FALSE
;
657 * Prepare the mouse handler.
659 lastact
= MA_NOTHING
;
660 lastbtn
= MBT_NOTHING
;
661 dbltime
= GetDoubleClickTime();
664 * Set up the session-control options on the system menu.
667 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
671 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
672 if (cfg
.protocol
== PROT_TELNET
) {
674 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
675 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
676 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
677 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
678 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
679 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
680 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
681 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
682 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
683 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
684 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
685 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
686 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
687 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
688 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
689 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
690 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
692 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
694 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
695 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
696 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
697 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
699 get_sesslist(&sesslist
, TRUE
);
701 i
< ((sesslist
.nsessions
< 256) ? sesslist
.nsessions
: 256);
703 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
704 sesslist
.sessions
[i
]);
705 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
706 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
707 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
708 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
709 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
710 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
711 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
712 AppendMenu(m
, (cfg
.resize_action
== RESIZE_DISABLED
) ?
713 MF_GRAYED
: MF_ENABLED
, IDM_FULLSCREEN
, "&Full Screen");
714 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
716 AppendMenu(m
, MF_ENABLED
, IDM_HELP
, "&Help");
717 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
721 * Set up the initial input locale.
723 set_input_locale(GetKeyboardLayout(0));
726 * Open the initial log file if there is one.
731 * Finally show the window!
733 ShowWindow(hwnd
, show
);
734 SetForegroundWindow(hwnd
);
737 * Set the palette up.
743 term
->has_focus
= (GetForegroundWindow() == hwnd
);
746 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
747 int timer_id
= 0, long_timer
= 0;
749 while (msg
.message
!= WM_QUIT
) {
750 /* Sometimes DispatchMessage calls routines that use their own
751 * GetMessage loop, setup this timer so we get some control back.
753 * Also call term_update() from the timer so that if the host
754 * is sending data flat out we still do redraws.
756 if (timer_id
&& long_timer
) {
757 KillTimer(hwnd
, timer_id
);
758 long_timer
= timer_id
= 0;
761 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
762 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
763 DispatchMessage(&msg
);
765 /* Make sure we blink everything that needs it. */
768 /* Send the paste buffer if there's anything to send */
771 /* If there's nothing new in the queue then we can do everything
772 * we've delayed, reading the socket, writing, and repainting
775 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
778 if (pending_netevent
) {
779 enact_pending_netevent();
781 /* Force the cursor blink on */
784 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
788 /* Okay there is now nothing to do so we make sure the screen is
789 * completely up to date then tell windows to call us in a little
793 KillTimer(hwnd
, timer_id
);
797 if (GetCapture() != hwnd
||
799 !(cfg
.mouse_override
&& is_shift_pressed())))
804 flash_window(1); /* maintain */
806 /* The messages seem unreliable; especially if we're being tricky */
807 term
->has_focus
= (GetForegroundWindow() == hwnd
);
810 /* Hmm, term_update didn't want to do an update too soon ... */
811 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
812 else if (!term
->has_focus
)
813 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
815 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
818 /* There's no point rescanning everything in the message queue
819 * so we do an apparently unnecessary wait here
822 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
827 cleanup_exit(msg
.wParam
); /* this doesn't return... */
828 return msg
.wParam
; /* ... but optimiser doesn't know */
834 void cleanup_exit(int code
)
847 if (cfg
.protocol
== PROT_SSH
) {
858 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
860 char *do_select(SOCKET skt
, int startup
)
865 events
= (FD_CONNECT
| FD_READ
| FD_WRITE
|
866 FD_OOB
| FD_CLOSE
| FD_ACCEPT
);
871 return "do_select(): internal error (hwnd==NULL)";
872 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
873 switch (WSAGetLastError()) {
875 return "Network is down";
877 return "WSAAsyncSelect(): unknown error";
884 * set or clear the "raw mouse message" mode
886 void set_raw_mouse_mode(void *frontend
, int activate
)
888 activate
= activate
&& !cfg
.no_mouse_rep
;
889 send_raw_mouse
= activate
;
890 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
894 * Print a message box and close the connection.
896 void connection_fatal(void *frontend
, char *fmt
, ...)
902 vsprintf(stuff
, fmt
, ap
);
904 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
905 if (cfg
.close_on_exit
== COE_ALWAYS
)
908 session_closed
= TRUE
;
909 set_icon(NULL
, "PuTTY (inactive)");
910 set_title(NULL
, "PuTTY (inactive)");
915 * Report an error at the command-line parsing stage.
917 void cmdline_error(char *fmt
, ...)
923 vsprintf(stuff
, fmt
, ap
);
925 MessageBox(hwnd
, stuff
, "PuTTY Command Line Error", MB_ICONERROR
| MB_OK
);
930 * Actually do the job requested by a WM_NETEVENT
932 static void enact_pending_netevent(void)
934 static int reentering
= 0;
935 extern int select_result(WPARAM
, LPARAM
);
939 return; /* don't unpend the pending */
941 pending_netevent
= FALSE
;
944 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
947 if (ret
== 0 && !session_closed
) {
948 /* Abnormal exits will already have set session_closed and taken
949 * appropriate action. */
950 if (cfg
.close_on_exit
== COE_ALWAYS
||
951 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
953 session_closed
= TRUE
;
954 set_icon(NULL
, "PuTTY (inactive)");
955 set_title(NULL
, "PuTTY (inactive)");
956 MessageBox(hwnd
, "Connection closed by remote host",
957 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
963 * Copy the colour palette from the configuration data into defpal.
964 * This is non-trivial because the colour indices are different.
966 static void cfgtopalette(void)
969 static const int ww
[] = {
970 6, 7, 8, 9, 10, 11, 12, 13,
971 14, 15, 16, 17, 18, 19, 20, 21,
972 0, 1, 2, 3, 4, 4, 5, 5
975 for (i
= 0; i
< 24; i
++) {
977 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
978 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
979 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
984 * Set up the colour palette.
986 static void init_palette(void)
989 HDC hdc
= GetDC(hwnd
);
991 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
992 logpal
= smalloc(sizeof(*logpal
)
993 - sizeof(logpal
->palPalEntry
)
994 + NCOLOURS
* sizeof(PALETTEENTRY
));
995 logpal
->palVersion
= 0x300;
996 logpal
->palNumEntries
= NCOLOURS
;
997 for (i
= 0; i
< NCOLOURS
; i
++) {
998 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
999 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
1000 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
1001 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
1003 pal
= CreatePalette(logpal
);
1005 SelectPalette(hdc
, pal
, FALSE
);
1006 RealizePalette(hdc
);
1007 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
1010 ReleaseDC(hwnd
, hdc
);
1013 for (i
= 0; i
< NCOLOURS
; i
++)
1014 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
1015 defpal
[i
].rgbtGreen
,
1016 defpal
[i
].rgbtBlue
);
1018 for (i
= 0; i
< NCOLOURS
; i
++)
1019 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
1020 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
1024 * Initialise all the fonts we will need initially. There may be as many as
1025 * three or as few as one. The other (poentially) twentyone fonts are done
1026 * if/when they are needed.
1030 * - check the font width and height, correcting our guesses if
1033 * - verify that the bold font is the same width as the ordinary
1034 * one, and engage shadow bolding if not.
1036 * - verify that the underlined font is the same width as the
1037 * ordinary one (manual underlining by means of line drawing can
1038 * be done in a pinch).
1040 static void init_fonts(int pick_width
, int pick_height
)
1047 int fw_dontcare
, fw_bold
;
1049 for (i
= 0; i
< FONT_MAXNO
; i
++)
1052 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1053 und_mode
= UND_FONT
;
1055 if (cfg
.fontisbold
) {
1056 fw_dontcare
= FW_BOLD
;
1059 fw_dontcare
= FW_DONTCARE
;
1066 font_height
= pick_height
;
1068 font_height
= cfg
.fontheight
;
1069 if (font_height
> 0) {
1071 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
1074 font_width
= pick_width
;
1076 #define f(i,c,w,u) \
1077 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
1078 c, OUT_DEFAULT_PRECIS, \
1079 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
1080 FIXED_PITCH | FF_DONTCARE, cfg.font)
1082 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
1084 lfont
.lfHeight
= font_height
;
1085 lfont
.lfWidth
= font_width
;
1086 lfont
.lfEscapement
= 0;
1087 lfont
.lfOrientation
= 0;
1088 lfont
.lfWeight
= fw_dontcare
;
1089 lfont
.lfItalic
= FALSE
;
1090 lfont
.lfUnderline
= FALSE
;
1091 lfont
.lfStrikeOut
= FALSE
;
1092 lfont
.lfCharSet
= cfg
.fontcharset
;
1093 lfont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
1094 lfont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
1095 lfont
.lfQuality
= DEFAULT_QUALITY
;
1096 lfont
.lfPitchAndFamily
= FIXED_PITCH
| FF_DONTCARE
;
1097 strncpy(lfont
.lfFaceName
, cfg
.font
, LF_FACESIZE
);
1099 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
1100 GetTextMetrics(hdc
, &tm
);
1102 if (pick_width
== 0 || pick_height
== 0) {
1103 font_height
= tm
.tmHeight
;
1104 font_width
= tm
.tmAveCharWidth
;
1106 font_dualwidth
= (tm
.tmAveCharWidth
!= tm
.tmMaxCharWidth
);
1108 #ifdef RDB_DEBUG_PATCH
1109 debug(23, "Primary font H=%d, AW=%d, MW=%d",
1110 tm
.tmHeight
, tm
.tmAveCharWidth
, tm
.tmMaxCharWidth
);
1115 DWORD cset
= tm
.tmCharSet
;
1116 memset(&info
, 0xFF, sizeof(info
));
1118 /* !!! Yes the next line is right */
1119 if (cset
== OEM_CHARSET
)
1120 ucsdata
.font_codepage
= GetOEMCP();
1122 if (TranslateCharsetInfo ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
))
1123 ucsdata
.font_codepage
= info
.ciACP
;
1125 ucsdata
.font_codepage
= -1;
1127 GetCPInfo(ucsdata
.font_codepage
, &cpinfo
);
1128 ucsdata
.dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
1131 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
1134 * Some fonts, e.g. 9-pt Courier, draw their underlines
1135 * outside their character cell. We successfully prevent
1136 * screen corruption by clipping the text output, but then
1137 * we lose the underline completely. Here we try to work
1138 * out whether this is such a font, and if it is, we set a
1139 * flag that causes underlines to be drawn by hand.
1141 * Having tried other more sophisticated approaches (such
1142 * as examining the TEXTMETRIC structure or requesting the
1143 * height of a string), I think we'll do this the brute
1144 * force way: we create a small bitmap, draw an underlined
1145 * space on it, and test to see whether any pixels are
1146 * foreground-coloured. (Since we expect the underline to
1147 * go all the way across the character cell, we only search
1148 * down a single column of the bitmap, half way across.)
1152 HBITMAP und_bm
, und_oldbm
;
1156 und_dc
= CreateCompatibleDC(hdc
);
1157 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
1158 und_oldbm
= SelectObject(und_dc
, und_bm
);
1159 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
1160 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
1161 SetTextColor(und_dc
, RGB(255, 255, 255));
1162 SetBkColor(und_dc
, RGB(0, 0, 0));
1163 SetBkMode(und_dc
, OPAQUE
);
1164 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
1166 for (i
= 0; i
< font_height
; i
++) {
1167 c
= GetPixel(und_dc
, font_width
/ 2, i
);
1168 if (c
!= RGB(0, 0, 0))
1171 SelectObject(und_dc
, und_oldbm
);
1172 DeleteObject(und_bm
);
1175 und_mode
= UND_LINE
;
1176 DeleteObject(fonts
[FONT_UNDERLINE
]);
1177 fonts
[FONT_UNDERLINE
] = 0;
1181 if (bold_mode
== BOLD_FONT
) {
1182 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
1186 descent
= tm
.tmAscent
+ 1;
1187 if (descent
>= font_height
)
1188 descent
= font_height
- 1;
1190 for (i
= 0; i
< 3; i
++) {
1192 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
1193 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
1200 ReleaseDC(hwnd
, hdc
);
1202 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1203 und_mode
= UND_LINE
;
1204 DeleteObject(fonts
[FONT_UNDERLINE
]);
1205 fonts
[FONT_UNDERLINE
] = 0;
1208 if (bold_mode
== BOLD_FONT
&&
1209 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1210 bold_mode
= BOLD_SHADOW
;
1211 DeleteObject(fonts
[FONT_BOLD
]);
1212 fonts
[FONT_BOLD
] = 0;
1214 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1216 init_ucs(&cfg
, &ucsdata
);
1219 static void another_font(int fontno
)
1222 int fw_dontcare
, fw_bold
;
1226 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1229 basefont
= (fontno
& ~(FONT_BOLDUND
));
1230 if (basefont
!= fontno
&& !fontflag
[basefont
])
1231 another_font(basefont
);
1233 if (cfg
.fontisbold
) {
1234 fw_dontcare
= FW_BOLD
;
1237 fw_dontcare
= FW_DONTCARE
;
1241 c
= cfg
.fontcharset
;
1247 if (fontno
& FONT_WIDE
)
1249 if (fontno
& FONT_NARROW
)
1251 if (fontno
& FONT_OEM
)
1253 if (fontno
& FONT_BOLD
)
1255 if (fontno
& FONT_UNDERLINE
)
1259 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1260 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1261 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1262 FIXED_PITCH
| FF_DONTCARE
, s
);
1264 fontflag
[fontno
] = 1;
1267 static void deinit_fonts(void)
1270 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1272 DeleteObject(fonts
[i
]);
1278 void request_resize(void *frontend
, int w
, int h
)
1282 /* If the window is maximized supress resizing attempts */
1283 if (IsZoomed(hwnd
)) {
1284 if (cfg
.resize_action
== RESIZE_TERM
)
1288 if (cfg
.resize_action
== RESIZE_DISABLED
) return;
1289 if (h
== term
->rows
&& w
== term
->cols
) return;
1291 /* Sanity checks ... */
1293 static int first_time
= 1;
1296 switch (first_time
) {
1298 /* Get the size of the screen */
1299 if (get_fullscreen_rect(&ss
))
1300 /* first_time = 0 */ ;
1306 /* Make sure the values are sane */
1307 width
= (ss
.right
- ss
.left
- extra_width
) / 4;
1308 height
= (ss
.bottom
- ss
.top
- extra_height
) / 6;
1310 if (w
> width
|| h
> height
)
1319 term_size(term
, h
, w
, cfg
.savelines
);
1321 if (cfg
.resize_action
!= RESIZE_FONT
&& !IsZoomed(hwnd
)) {
1322 width
= extra_width
+ font_width
* w
;
1323 height
= extra_height
+ font_height
* h
;
1325 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1326 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1327 SWP_NOMOVE
| SWP_NOZORDER
);
1331 InvalidateRect(hwnd
, NULL
, TRUE
);
1334 static void reset_window(int reinit
) {
1336 * This function decides how to resize or redraw when the
1337 * user changes something.
1339 * This function doesn't like to change the terminal size but if the
1340 * font size is locked that may be it's only soluion.
1342 int win_width
, win_height
;
1345 #ifdef RDB_DEBUG_PATCH
1346 debug((27, "reset_window()"));
1349 /* Current window sizes ... */
1350 GetWindowRect(hwnd
, &wr
);
1351 GetClientRect(hwnd
, &cr
);
1353 win_width
= cr
.right
- cr
.left
;
1354 win_height
= cr
.bottom
- cr
.top
;
1356 if (cfg
.resize_action
== RESIZE_DISABLED
) reinit
= 2;
1358 /* Are we being forced to reload the fonts ? */
1360 #ifdef RDB_DEBUG_PATCH
1361 debug((27, "reset_window() -- Forced deinit"));
1367 /* Oh, looks like we're minimised */
1368 if (win_width
== 0 || win_height
== 0)
1371 /* Is the window out of position ? */
1373 (offset_width
!= (win_width
-font_width
*term
->cols
)/2 ||
1374 offset_height
!= (win_height
-font_height
*term
->rows
)/2) ){
1375 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1376 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1377 InvalidateRect(hwnd
, NULL
, TRUE
);
1378 #ifdef RDB_DEBUG_PATCH
1379 debug((27, "reset_window() -> Reposition terminal"));
1383 if (IsZoomed(hwnd
)) {
1384 /* We're fullscreen, this means we must not change the size of
1385 * the window so it's the font size or the terminal itself.
1388 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1389 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1391 if (cfg
.resize_action
!= RESIZE_TERM
) {
1392 if ( font_width
!= win_width
/term
->cols
||
1393 font_height
!= win_height
/term
->rows
) {
1395 init_fonts(win_width
/term
->cols
, win_height
/term
->rows
);
1396 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1397 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1398 InvalidateRect(hwnd
, NULL
, TRUE
);
1399 #ifdef RDB_DEBUG_PATCH
1400 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1401 font_width
, font_height
));
1405 if ( font_width
!= win_width
/term
->cols
||
1406 font_height
!= win_height
/term
->rows
) {
1407 /* Our only choice at this point is to change the
1408 * size of the terminal; Oh well.
1410 term_size(term
, win_height
/font_height
, win_width
/font_width
,
1412 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1413 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1414 InvalidateRect(hwnd
, NULL
, TRUE
);
1415 #ifdef RDB_DEBUG_PATCH
1416 debug((27, "reset_window() -> Zoomed term_size"));
1423 /* Hmm, a force re-init means we should ignore the current window
1424 * so we resize to the default font size.
1427 #ifdef RDB_DEBUG_PATCH
1428 debug((27, "reset_window() -> Forced re-init"));
1431 offset_width
= offset_height
= cfg
.window_border
;
1432 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1433 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1435 if (win_width
!= font_width
*term
->cols
+ offset_width
*2 ||
1436 win_height
!= font_height
*term
->rows
+ offset_height
*2) {
1438 /* If this is too large windows will resize it to the maximum
1439 * allowed window size, we will then be back in here and resize
1440 * the font or terminal to fit.
1442 SetWindowPos(hwnd
, NULL
, 0, 0,
1443 font_width
*term
->cols
+ extra_width
,
1444 font_height
*term
->rows
+ extra_height
,
1445 SWP_NOMOVE
| SWP_NOZORDER
);
1448 InvalidateRect(hwnd
, NULL
, TRUE
);
1452 /* Okay the user doesn't want us to change the font so we try the
1453 * window. But that may be too big for the screen which forces us
1454 * to change the terminal.
1456 if ((cfg
.resize_action
== RESIZE_TERM
&& reinit
<=0) ||
1457 (cfg
.resize_action
== RESIZE_EITHER
&& reinit
<0) ||
1459 offset_width
= offset_height
= cfg
.window_border
;
1460 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1461 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1463 if (win_width
!= font_width
*term
->cols
+ offset_width
*2 ||
1464 win_height
!= font_height
*term
->rows
+ offset_height
*2) {
1469 get_fullscreen_rect(&ss
);
1471 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1472 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1475 if ( term
->rows
> height
|| term
->cols
> width
) {
1476 if (cfg
.resize_action
== RESIZE_EITHER
) {
1477 /* Make the font the biggest we can */
1478 if (term
->cols
> width
)
1479 font_width
= (ss
.right
- ss
.left
- extra_width
)
1481 if (term
->rows
> height
)
1482 font_height
= (ss
.bottom
- ss
.top
- extra_height
)
1486 init_fonts(font_width
, font_height
);
1488 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1489 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1491 if ( height
> term
->rows
) height
= term
->rows
;
1492 if ( width
> term
->cols
) width
= term
->cols
;
1493 term_size(term
, height
, width
, cfg
.savelines
);
1494 #ifdef RDB_DEBUG_PATCH
1495 debug((27, "reset_window() -> term resize to (%d,%d)",
1501 SetWindowPos(hwnd
, NULL
, 0, 0,
1502 font_width
*term
->cols
+ extra_width
,
1503 font_height
*term
->rows
+ extra_height
,
1504 SWP_NOMOVE
| SWP_NOZORDER
);
1506 InvalidateRect(hwnd
, NULL
, TRUE
);
1507 #ifdef RDB_DEBUG_PATCH
1508 debug((27, "reset_window() -> window resize to (%d,%d)",
1509 font_width
*term
->cols
+ extra_width
,
1510 font_height
*term
->rows
+ extra_height
));
1516 /* We're allowed to or must change the font but do we want to ? */
1518 if (font_width
!= (win_width
-cfg
.window_border
*2)/term
->cols
||
1519 font_height
!= (win_height
-cfg
.window_border
*2)/term
->rows
) {
1522 init_fonts((win_width
-cfg
.window_border
*2)/term
->cols
,
1523 (win_height
-cfg
.window_border
*2)/term
->rows
);
1524 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1525 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1527 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+offset_width
*2;
1528 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1530 InvalidateRect(hwnd
, NULL
, TRUE
);
1531 #ifdef RDB_DEBUG_PATCH
1532 debug((25, "reset_window() -> font resize to (%d,%d)",
1533 font_width
, font_height
));
1538 static void set_input_locale(HKL kl
)
1542 GetLocaleInfo(LOWORD(kl
), LOCALE_IDEFAULTANSICODEPAGE
,
1543 lbuf
, sizeof(lbuf
));
1545 kbd_codepage
= atoi(lbuf
);
1548 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
, int alt
)
1550 int thistime
= GetMessageTime();
1552 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1553 lastbtn
= MBT_NOTHING
;
1554 term_mouse(term
, b
, MA_CLICK
, x
, y
, shift
, ctrl
, alt
);
1558 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1559 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1560 lastact
== MA_2CLK ? MA_3CLK
:
1561 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1566 if (lastact
!= MA_NOTHING
)
1567 term_mouse(term
, b
, lastact
, x
, y
, shift
, ctrl
, alt
);
1568 lasttime
= thistime
;
1572 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1573 * into a cooked one (SELECT, EXTEND, PASTE).
1575 Mouse_Button
translate_button(void *frontend
, Mouse_Button button
)
1577 if (button
== MBT_LEFT
)
1579 if (button
== MBT_MIDDLE
)
1580 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1581 if (button
== MBT_RIGHT
)
1582 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1583 return 0; /* shouldn't happen */
1586 static void show_mouseptr(int show
)
1588 static int cursor_visible
= 1;
1589 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1591 if (cursor_visible
&& !show
)
1593 else if (!cursor_visible
&& show
)
1595 cursor_visible
= show
;
1598 static int is_alt_pressed(void)
1601 int r
= GetKeyboardState(keystate
);
1604 if (keystate
[VK_MENU
] & 0x80)
1606 if (keystate
[VK_RMENU
] & 0x80)
1611 static int is_shift_pressed(void)
1614 int r
= GetKeyboardState(keystate
);
1617 if (keystate
[VK_SHIFT
] & 0x80)
1622 static int resizing
;
1624 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1625 WPARAM wParam
, LPARAM lParam
)
1628 static int ignore_clip
= FALSE
;
1629 static int need_backend_resize
= FALSE
;
1630 static int fullscr_on_max
= FALSE
;
1634 if (pending_netevent
)
1635 enact_pending_netevent();
1636 if (GetCapture() != hwnd
||
1637 (send_raw_mouse
&& !(cfg
.mouse_override
&& is_shift_pressed())))
1643 if (cfg
.ping_interval
> 0) {
1646 if (now
- last_movement
> cfg
.ping_interval
) {
1647 back
->special(backhandle
, TS_PING
);
1648 last_movement
= now
;
1651 net_pending_errors();
1657 if (!cfg
.warn_on_close
|| session_closed
||
1659 "Are you sure you want to close this session?",
1660 "PuTTY Exit Confirmation",
1661 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1662 DestroyWindow(hwnd
);
1669 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1681 PROCESS_INFORMATION pi
;
1682 HANDLE filemap
= NULL
;
1684 if (wParam
== IDM_DUPSESS
) {
1686 * Allocate a file-mapping memory chunk for the
1689 SECURITY_ATTRIBUTES sa
;
1692 sa
.nLength
= sizeof(sa
);
1693 sa
.lpSecurityDescriptor
= NULL
;
1694 sa
.bInheritHandle
= TRUE
;
1695 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1698 0, sizeof(Config
), NULL
);
1700 p
= (Config
*) MapViewOfFile(filemap
,
1702 0, 0, sizeof(Config
));
1704 *p
= cfg
; /* structure copy */
1708 sprintf(c
, "putty &%p", filemap
);
1710 } else if (wParam
== IDM_SAVEDSESS
) {
1711 if ((lParam
- IDM_SAVED_MIN
) / 16 < sesslist
.nsessions
) {
1713 sesslist
.sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1714 cl
= smalloc(16 + strlen(session
));
1715 /* 8, but play safe */
1718 /* not a very important failure mode */
1720 sprintf(cl
, "putty @%s", session
);
1728 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1730 si
.lpReserved
= NULL
;
1731 si
.lpDesktop
= NULL
;
1735 si
.lpReserved2
= NULL
;
1736 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1737 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1740 CloseHandle(filemap
);
1750 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1753 if (!do_reconfig(hwnd
))
1757 /* Disable full-screen if resizing forbidden */
1758 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
1759 EnableMenuItem(m
, IDM_FULLSCREEN
, MF_BYCOMMAND
|
1760 (cfg
.resize_action
== RESIZE_DISABLED
)
1761 ? MF_GRAYED
: MF_ENABLED
);
1762 /* Gracefully unzoom if necessary */
1763 if (IsZoomed(hwnd
) &&
1764 (cfg
.resize_action
== RESIZE_DISABLED
)) {
1765 ShowWindow(hwnd
, SW_RESTORE
);
1769 /* Pass new config data to the logging module */
1770 log_reconfig(logctx
, &cfg
);
1774 * Flush the line discipline's edit buffer in the
1775 * case where local editing has just been disabled.
1777 ldisc_send(ldisc
, NULL
, 0, 0);
1785 /* Pass new config data to the terminal */
1786 term_reconfig(term
, &cfg
);
1788 /* Pass new config data to the back end */
1789 back
->reconfig(backhandle
, &cfg
);
1791 /* Screen size changed ? */
1792 if (cfg
.height
!= prev_cfg
.height
||
1793 cfg
.width
!= prev_cfg
.width
||
1794 cfg
.savelines
!= prev_cfg
.savelines
||
1795 cfg
.resize_action
== RESIZE_FONT
||
1796 (cfg
.resize_action
== RESIZE_EITHER
&& IsZoomed(hwnd
)) ||
1797 cfg
.resize_action
== RESIZE_DISABLED
)
1798 term_size(term
, cfg
.height
, cfg
.width
, cfg
.savelines
);
1800 /* Enable or disable the scroll bar, etc */
1802 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1803 LONG nexflag
, exflag
=
1804 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1807 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1808 if (cfg
.alwaysontop
) {
1809 nexflag
|= WS_EX_TOPMOST
;
1810 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1811 SWP_NOMOVE
| SWP_NOSIZE
);
1813 nexflag
&= ~(WS_EX_TOPMOST
);
1814 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1815 SWP_NOMOVE
| SWP_NOSIZE
);
1818 if (cfg
.sunken_edge
)
1819 nexflag
|= WS_EX_CLIENTEDGE
;
1821 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1824 if (is_full_screen() ?
1825 cfg
.scrollbar_in_fullscreen
: cfg
.scrollbar
)
1828 nflg
&= ~WS_VSCROLL
;
1830 if (cfg
.resize_action
== RESIZE_DISABLED
||
1832 nflg
&= ~WS_THICKFRAME
;
1834 nflg
|= WS_THICKFRAME
;
1836 if (cfg
.resize_action
== RESIZE_DISABLED
)
1837 nflg
&= ~WS_MAXIMIZEBOX
;
1839 nflg
|= WS_MAXIMIZEBOX
;
1841 if (nflg
!= flag
|| nexflag
!= exflag
) {
1843 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1844 if (nexflag
!= exflag
)
1845 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1847 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1848 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1849 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1857 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1862 set_title(NULL
, cfg
.wintitle
);
1863 if (IsIconic(hwnd
)) {
1865 cfg
.win_name_always ? window_name
:
1869 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1870 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1871 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1872 cfg
.fontheight
!= prev_cfg
.fontheight
||
1873 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1874 cfg
.vtmode
!= prev_cfg
.vtmode
||
1875 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1876 cfg
.resize_action
== RESIZE_DISABLED
||
1877 cfg
.resize_action
== RESIZE_EITHER
||
1878 (cfg
.resize_action
!= prev_cfg
.resize_action
))
1881 InvalidateRect(hwnd
, NULL
, TRUE
);
1882 reset_window(init_lvl
);
1883 net_pending_errors();
1894 ldisc_send(ldisc
, NULL
, 0, 0);
1897 back
->special(backhandle
, TS_AYT
);
1898 net_pending_errors();
1901 back
->special(backhandle
, TS_BRK
);
1902 net_pending_errors();
1905 back
->special(backhandle
, TS_SYNCH
);
1906 net_pending_errors();
1909 back
->special(backhandle
, TS_EC
);
1910 net_pending_errors();
1913 back
->special(backhandle
, TS_EL
);
1914 net_pending_errors();
1917 back
->special(backhandle
, TS_GA
);
1918 net_pending_errors();
1921 back
->special(backhandle
, TS_NOP
);
1922 net_pending_errors();
1925 back
->special(backhandle
, TS_ABORT
);
1926 net_pending_errors();
1929 back
->special(backhandle
, TS_AO
);
1930 net_pending_errors();
1933 back
->special(backhandle
, TS_IP
);
1934 net_pending_errors();
1937 back
->special(backhandle
, TS_SUSP
);
1938 net_pending_errors();
1941 back
->special(backhandle
, TS_EOR
);
1942 net_pending_errors();
1945 back
->special(backhandle
, TS_EOF
);
1946 net_pending_errors();
1952 WinHelp(hwnd
, help_path
,
1953 help_has_contents ? HELP_FINDER
: HELP_CONTENTS
, 0);
1957 * We get this if the System menu has been activated
1964 * We get this if the System menu has been activated
1965 * using the keyboard. This might happen from within
1966 * TranslateKey, in which case it really wants to be
1967 * followed by a `space' character to actually _bring
1968 * the menu up_ rather than just sitting there in
1969 * `ready to appear' state.
1971 show_mouseptr(1); /* make sure pointer is visible */
1973 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1975 case IDM_FULLSCREEN
:
1979 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1980 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1985 #define X_POS(l) ((int)(short)LOWORD(l))
1986 #define Y_POS(l) ((int)(short)HIWORD(l))
1988 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1989 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1990 case WM_LBUTTONDOWN
:
1991 case WM_MBUTTONDOWN
:
1992 case WM_RBUTTONDOWN
:
2000 case WM_LBUTTONDOWN
:
2004 case WM_MBUTTONDOWN
:
2005 button
= MBT_MIDDLE
;
2008 case WM_RBUTTONDOWN
:
2017 button
= MBT_MIDDLE
;
2025 button
= press
= 0; /* shouldn't happen */
2029 * Special case: in full-screen mode, if the left
2030 * button is clicked in the very top left corner of the
2031 * window, we put up the System menu instead of doing
2034 if (is_full_screen() && press
&& button
== MBT_LEFT
&&
2035 X_POS(lParam
) == 0 && Y_POS(lParam
) == 0) {
2036 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_MOUSEMENU
, 0);
2041 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
2042 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
,
2046 term_mouse(term
, button
, MA_RELEASE
,
2047 TO_CHR_X(X_POS(lParam
)),
2048 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
2049 wParam
& MK_CONTROL
, is_alt_pressed());
2057 * Add the mouse position and message time to the random
2060 noise_ultralight(lParam
);
2062 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
) &&
2063 GetCapture() == hwnd
) {
2065 if (wParam
& MK_LBUTTON
)
2067 else if (wParam
& MK_MBUTTON
)
2071 term_mouse(term
, b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
2072 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
2073 wParam
& MK_CONTROL
, is_alt_pressed());
2076 case WM_NCMOUSEMOVE
:
2078 noise_ultralight(lParam
);
2080 case WM_IGNORE_CLIP
:
2081 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
2083 case WM_DESTROYCLIPBOARD
:
2085 term_deselect(term
);
2086 ignore_clip
= FALSE
;
2092 hdc
= BeginPaint(hwnd
, &p
);
2094 SelectPalette(hdc
, pal
, TRUE
);
2095 RealizePalette(hdc
);
2097 term_paint(term
, hdc
,
2098 (p
.rcPaint
.left
-offset_width
)/font_width
,
2099 (p
.rcPaint
.top
-offset_height
)/font_height
,
2100 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
2101 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
,
2105 p
.rcPaint
.left
< offset_width
||
2106 p
.rcPaint
.top
< offset_height
||
2107 p
.rcPaint
.right
>= offset_width
+ font_width
*term
->cols
||
2108 p
.rcPaint
.bottom
>= offset_height
+ font_height
*term
->rows
)
2110 HBRUSH fillcolour
, oldbrush
;
2112 fillcolour
= CreateSolidBrush (
2113 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
2114 oldbrush
= SelectObject(hdc
, fillcolour
);
2115 edge
= CreatePen(PS_SOLID
, 0,
2116 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
2117 oldpen
= SelectObject(hdc
, edge
);
2120 * Jordan Russell reports that this apparently
2121 * ineffectual IntersectClipRect() call masks a
2122 * Windows NT/2K bug causing strange display
2123 * problems when the PuTTY window is taller than
2124 * the primary monitor. It seems harmless enough...
2126 IntersectClipRect(hdc
,
2127 p
.rcPaint
.left
, p
.rcPaint
.top
,
2128 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
2130 ExcludeClipRect(hdc
,
2131 offset_width
, offset_height
,
2132 offset_width
+font_width
*term
->cols
,
2133 offset_height
+font_height
*term
->rows
);
2135 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
2136 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
2138 // SelectClipRgn(hdc, NULL);
2140 SelectObject(hdc
, oldbrush
);
2141 DeleteObject(fillcolour
);
2142 SelectObject(hdc
, oldpen
);
2145 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
2146 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
2152 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2153 * but the only one that's likely to try to overload us is FD_READ.
2154 * This means buffering just one is fine.
2156 if (pending_netevent
)
2157 enact_pending_netevent();
2159 pending_netevent
= TRUE
;
2160 pend_netevent_wParam
= wParam
;
2161 pend_netevent_lParam
= lParam
;
2162 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
2163 enact_pending_netevent();
2165 time(&last_movement
);
2168 term
->has_focus
= TRUE
;
2169 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
2171 flash_window(0); /* stop */
2178 term
->has_focus
= FALSE
;
2180 caret_x
= caret_y
= -1; /* ensure caret is replaced next time */
2184 case WM_ENTERSIZEMOVE
:
2185 #ifdef RDB_DEBUG_PATCH
2186 debug((27, "WM_ENTERSIZEMOVE"));
2190 need_backend_resize
= FALSE
;
2192 case WM_EXITSIZEMOVE
:
2195 #ifdef RDB_DEBUG_PATCH
2196 debug((27, "WM_EXITSIZEMOVE"));
2198 if (need_backend_resize
) {
2199 term_size(term
, cfg
.height
, cfg
.width
, cfg
.savelines
);
2200 InvalidateRect(hwnd
, NULL
, TRUE
);
2205 * This does two jobs:
2206 * 1) Keep the sizetip uptodate
2207 * 2) Make sure the window size is _stepped_ in units of the font size.
2209 if (cfg
.resize_action
!= RESIZE_FONT
&& !is_alt_pressed()) {
2210 int width
, height
, w
, h
, ew
, eh
;
2211 LPRECT r
= (LPRECT
) lParam
;
2213 if ( !need_backend_resize
&& cfg
.resize_action
== RESIZE_EITHER
&&
2214 (cfg
.height
!= term
->rows
|| cfg
.width
!= term
->cols
)) {
2216 * Great! It seems that both the terminal size and the
2217 * font size have been changed and the user is now dragging.
2219 * It will now be difficult to get back to the configured
2222 * This would be easier but it seems to be too confusing.
2224 term_size(term, cfg.height, cfg.width, cfg.savelines);
2227 cfg
.height
=term
->rows
; cfg
.width
=term
->cols
;
2229 InvalidateRect(hwnd
, NULL
, TRUE
);
2230 need_backend_resize
= TRUE
;
2233 width
= r
->right
- r
->left
- extra_width
;
2234 height
= r
->bottom
- r
->top
- extra_height
;
2235 w
= (width
+ font_width
/ 2) / font_width
;
2238 h
= (height
+ font_height
/ 2) / font_height
;
2241 UpdateSizeTip(hwnd
, w
, h
);
2242 ew
= width
- w
* font_width
;
2243 eh
= height
- h
* font_height
;
2245 if (wParam
== WMSZ_LEFT
||
2246 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2252 if (wParam
== WMSZ_TOP
||
2253 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2263 int width
, height
, w
, h
, rv
= 0;
2264 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
2265 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2266 LPRECT r
= (LPRECT
) lParam
;
2268 width
= r
->right
- r
->left
- ex_width
;
2269 height
= r
->bottom
- r
->top
- ex_height
;
2271 w
= (width
+ term
->cols
/2)/term
->cols
;
2272 h
= (height
+ term
->rows
/2)/term
->rows
;
2273 if ( r
->right
!= r
->left
+ w
*term
->cols
+ ex_width
)
2276 if (wParam
== WMSZ_LEFT
||
2277 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2278 r
->left
= r
->right
- w
*term
->cols
- ex_width
;
2280 r
->right
= r
->left
+ w
*term
->cols
+ ex_width
;
2282 if (r
->bottom
!= r
->top
+ h
*term
->rows
+ ex_height
)
2285 if (wParam
== WMSZ_TOP
||
2286 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2287 r
->top
= r
->bottom
- h
*term
->rows
- ex_height
;
2289 r
->bottom
= r
->top
+ h
*term
->rows
+ ex_height
;
2293 /* break; (never reached) */
2294 case WM_FULLSCR_ON_MAX
:
2295 fullscr_on_max
= TRUE
;
2298 sys_cursor_update();
2301 #ifdef RDB_DEBUG_PATCH
2302 debug((27, "WM_SIZE %s (%d,%d)",
2303 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2304 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2305 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2306 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2308 LOWORD(lParam
), HIWORD(lParam
)));
2310 if (wParam
== SIZE_MINIMIZED
)
2312 cfg
.win_name_always ? window_name
: icon_name
);
2313 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2314 SetWindowText(hwnd
, window_name
);
2315 if (wParam
== SIZE_RESTORED
)
2316 clear_full_screen();
2317 if (wParam
== SIZE_MAXIMIZED
&& fullscr_on_max
) {
2318 fullscr_on_max
= FALSE
;
2322 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2323 /* A resize, well it better be a minimize. */
2327 int width
, height
, w
, h
;
2329 width
= LOWORD(lParam
);
2330 height
= HIWORD(lParam
);
2333 if (wParam
== SIZE_MAXIMIZED
&& !was_zoomed
) {
2335 prev_rows
= term
->rows
;
2336 prev_cols
= term
->cols
;
2337 if (cfg
.resize_action
== RESIZE_TERM
) {
2338 w
= width
/ font_width
;
2340 h
= height
/ font_height
;
2343 term_size(term
, h
, w
, cfg
.savelines
);
2346 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2348 if (cfg
.resize_action
== RESIZE_TERM
)
2349 term_size(term
, prev_rows
, prev_cols
, cfg
.savelines
);
2350 if (cfg
.resize_action
!= RESIZE_FONT
)
2355 /* This is an unexpected resize, these will normally happen
2356 * if the window is too large. Probably either the user
2357 * selected a huge font or the screen size has changed.
2359 * This is also called with minimize.
2361 else reset_window(-1);
2365 * Don't call back->size in mid-resize. (To prevent
2366 * massive numbers of resize events getting sent
2367 * down the connection during an NT opaque drag.)
2370 if (cfg
.resize_action
!= RESIZE_FONT
&& !is_alt_pressed()) {
2371 need_backend_resize
= TRUE
;
2372 w
= (width
-cfg
.window_border
*2) / font_width
;
2374 h
= (height
-cfg
.window_border
*2) / font_height
;
2383 sys_cursor_update();
2386 switch (LOWORD(wParam
)) {
2388 term_scroll(term
, -1, 0);
2391 term_scroll(term
, +1, 0);
2394 term_scroll(term
, 0, +1);
2397 term_scroll(term
, 0, -1);
2400 term_scroll(term
, 0, +term
->rows
/ 2);
2403 term_scroll(term
, 0, -term
->rows
/ 2);
2405 case SB_THUMBPOSITION
:
2407 term_scroll(term
, 1, HIWORD(wParam
));
2411 case WM_PALETTECHANGED
:
2412 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2413 HDC hdc
= get_ctx(NULL
);
2415 if (RealizePalette(hdc
) > 0)
2421 case WM_QUERYNEWPALETTE
:
2423 HDC hdc
= get_ctx(NULL
);
2425 if (RealizePalette(hdc
) > 0)
2437 * Add the scan code and keypress timing to the random
2440 noise_ultralight(lParam
);
2443 * We don't do TranslateMessage since it disassociates the
2444 * resulting CHAR message from the KEYDOWN that sparked it,
2445 * which we occasionally don't want. Instead, we process
2446 * KEYDOWN, and call the Win32 translator functions so that
2447 * we get the translations under _our_ control.
2450 unsigned char buf
[20];
2453 if (wParam
== VK_PROCESSKEY
) {
2456 m
.message
= WM_KEYDOWN
;
2458 m
.lParam
= lParam
& 0xdfff;
2459 TranslateMessage(&m
);
2461 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2463 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2467 * Interrupt an ongoing paste. I'm not sure
2468 * this is sensible, but for the moment it's
2469 * preferable to having to faff about buffering
2475 * We need not bother about stdin backlogs
2476 * here, because in GUI PuTTY we can't do
2477 * anything about it anyway; there's no means
2478 * of asking Windows to hold off on KEYDOWN
2479 * messages. We _have_ to buffer everything
2482 term_seen_key_event(term
);
2483 ldisc_send(ldisc
, buf
, len
, 1);
2488 net_pending_errors();
2490 case WM_INPUTLANGCHANGE
:
2491 /* wParam == Font number */
2492 /* lParam == Locale */
2493 set_input_locale((HKL
)lParam
);
2494 sys_cursor_update();
2497 if(wParam
== IMN_SETOPENSTATUS
) {
2498 HIMC hImc
= ImmGetContext(hwnd
);
2499 ImmSetCompositionFont(hImc
, &lfont
);
2500 ImmReleaseContext(hwnd
, hImc
);
2504 case WM_IME_COMPOSITION
:
2510 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2511 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2513 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2514 break; /* fall back to DefWindowProc */
2516 hIMC
= ImmGetContext(hwnd
);
2517 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2521 buff
= (char*) smalloc(n
);
2522 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2524 * Jaeyoun Chung reports that Korean character
2525 * input doesn't work correctly if we do a single
2526 * luni_send() covering the whole of buff. So
2527 * instead we luni_send the characters one by one.
2529 term_seen_key_event(term
);
2530 for (i
= 0; i
< n
; i
+= 2) {
2531 luni_send(ldisc
, (unsigned short *)(buff
+i
), 1, 1);
2535 ImmReleaseContext(hwnd
, hIMC
);
2540 if (wParam
& 0xFF00) {
2541 unsigned char buf
[2];
2544 buf
[0] = wParam
>> 8;
2545 term_seen_key_event(term
);
2546 lpage_send(ldisc
, kbd_codepage
, buf
, 2, 1);
2548 char c
= (unsigned char) wParam
;
2549 term_seen_key_event(term
);
2550 lpage_send(ldisc
, kbd_codepage
, &c
, 1, 1);
2556 * Nevertheless, we are prepared to deal with WM_CHAR
2557 * messages, should they crop up. So if someone wants to
2558 * post the things to us as part of a macro manoeuvre,
2559 * we're ready to cope.
2562 char c
= (unsigned char)wParam
;
2563 term_seen_key_event(term
);
2564 lpage_send(ldisc
, CP_ACP
, &c
, 1, 1);
2568 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2569 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2573 if (message
== wm_mousewheel
|| message
== WM_MOUSEWHEEL
) {
2574 int shift_pressed
=0, control_pressed
=0;
2576 if (message
== WM_MOUSEWHEEL
) {
2577 wheel_accumulator
+= (short)HIWORD(wParam
);
2578 shift_pressed
=LOWORD(wParam
) & MK_SHIFT
;
2579 control_pressed
=LOWORD(wParam
) & MK_CONTROL
;
2582 wheel_accumulator
+= (int)wParam
;
2583 if (GetKeyboardState(keys
)!=0) {
2584 shift_pressed
=keys
[VK_SHIFT
]&0x80;
2585 control_pressed
=keys
[VK_CONTROL
]&0x80;
2589 /* process events when the threshold is reached */
2590 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
2593 /* reduce amount for next time */
2594 if (wheel_accumulator
> 0) {
2596 wheel_accumulator
-= WHEEL_DELTA
;
2597 } else if (wheel_accumulator
< 0) {
2599 wheel_accumulator
+= WHEEL_DELTA
;
2603 if (send_raw_mouse
&&
2604 !(cfg
.mouse_override
&& shift_pressed
)) {
2605 /* send a mouse-down followed by a mouse up */
2608 TO_CHR_X(X_POS(lParam
)),
2609 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2610 control_pressed
, is_alt_pressed());
2611 term_mouse(term
, b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
2612 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2613 control_pressed
, is_alt_pressed());
2615 /* trigger a scroll */
2616 term_scroll(term
, 0,
2618 -term
->rows
/ 2 : term
->rows
/ 2);
2625 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2629 * Move the system caret. (We maintain one, even though it's
2630 * invisible, for the benefit of blind people: apparently some
2631 * helper software tracks the system caret, so we should arrange to
2634 void sys_cursor(void *frontend
, int x
, int y
)
2638 if (!term
->has_focus
) return;
2641 * Avoid gratuitously re-updating the cursor position and IMM
2642 * window if there's no actual change required.
2644 cx
= x
* font_width
+ offset_width
;
2645 cy
= y
* font_height
+ offset_height
;
2646 if (cx
== caret_x
&& cy
== caret_y
)
2651 sys_cursor_update();
2654 static void sys_cursor_update(void)
2659 if (!term
->has_focus
) return;
2661 if (caret_x
< 0 || caret_y
< 0)
2664 SetCaretPos(caret_x
, caret_y
);
2666 /* IMM calls on Win98 and beyond only */
2667 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2669 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2670 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2672 /* we should have the IMM functions */
2673 hIMC
= ImmGetContext(hwnd
);
2674 cf
.dwStyle
= CFS_POINT
;
2675 cf
.ptCurrentPos
.x
= caret_x
;
2676 cf
.ptCurrentPos
.y
= caret_y
;
2677 ImmSetCompositionWindow(hIMC
, &cf
);
2679 ImmReleaseContext(hwnd
, hIMC
);
2683 * Draw a line of text in the window, at given character
2684 * coordinates, in given attributes.
2686 * We are allowed to fiddle with the contents of `text'.
2688 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2689 unsigned long attr
, int lattr
)
2692 int nfg
, nbg
, nfont
;
2695 int force_manual_underline
= 0;
2696 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2697 int char_width
= fnt_width
;
2698 int text_adjust
= 0;
2699 static int *IpDx
= 0, IpDxLEN
= 0;
2701 if (attr
& ATTR_WIDE
)
2704 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2706 if (len
> IpDxLEN
) {
2708 IpDx
= smalloc((len
+ 16) * sizeof(int));
2709 IpDxLEN
= (len
+ 16);
2711 for (i
= 0; i
< IpDxLEN
; i
++)
2712 IpDx
[i
] = char_width
;
2715 /* Only want the left half of double width lines */
2716 if (lattr
!= LATTR_NORM
&& x
*2 >= term
->cols
)
2724 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || term
->big_cursor
)) {
2725 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2726 attr
^= ATTR_CUR_XOR
;
2730 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2731 /* Assume a poorman font is borken in other ways too. */
2741 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2744 if (attr
& ATTR_NARROW
)
2745 nfont
|= FONT_NARROW
;
2747 /* Special hack for the VT100 linedraw glyphs. */
2748 if ((attr
& CSET_MASK
) == 0x2300) {
2749 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2750 switch ((unsigned char) (text
[0])) {
2752 text_adjust
= -2 * font_height
/ 5;
2755 text_adjust
= -1 * font_height
/ 5;
2758 text_adjust
= font_height
/ 5;
2761 text_adjust
= 2 * font_height
/ 5;
2764 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2767 text
[0] = (char) (ucsdata
.unitab_xterm
['q'] & CHAR_MASK
);
2768 attr
|= (ucsdata
.unitab_xterm
['q'] & CSET_MASK
);
2769 if (attr
& ATTR_UNDER
) {
2770 attr
&= ~ATTR_UNDER
;
2771 force_manual_underline
= 1;
2776 /* Anything left as an original character set is unprintable. */
2777 if (DIRECT_CHAR(attr
)) {
2780 memset(text
, 0xFD, len
);
2784 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2787 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2788 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2789 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2791 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2792 nfont
|= FONT_UNDERLINE
;
2793 another_font(nfont
);
2794 if (!fonts
[nfont
]) {
2795 if (nfont
& FONT_UNDERLINE
)
2796 force_manual_underline
= 1;
2797 /* Don't do the same for manual bold, it could be bad news. */
2799 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2801 another_font(nfont
);
2803 nfont
= FONT_NORMAL
;
2804 if (attr
& ATTR_REVERSE
) {
2809 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2811 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2815 SelectObject(hdc
, fonts
[nfont
]);
2816 SetTextColor(hdc
, fg
);
2817 SetBkColor(hdc
, bg
);
2818 SetBkMode(hdc
, OPAQUE
);
2821 line_box
.right
= x
+ char_width
* len
;
2822 line_box
.bottom
= y
+ font_height
;
2824 /* Only want the left half of double width lines */
2825 if (line_box
.right
> font_width
*term
->cols
+offset_width
)
2826 line_box
.right
= font_width
*term
->cols
+offset_width
;
2828 /* We're using a private area for direct to font. (512 chars.) */
2829 if (ucsdata
.dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2830 /* Ho Hum, dbcs fonts are a PITA! */
2831 /* To display on W9x I have to convert to UCS */
2832 static wchar_t *uni_buf
= 0;
2833 static int uni_len
= 0;
2835 if (len
> uni_len
) {
2837 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2840 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2841 uni_buf
[nlen
] = 0xFFFD;
2842 if (IsDBCSLeadByteEx(ucsdata
.font_codepage
, (BYTE
) text
[mptr
])) {
2843 IpDx
[nlen
] += char_width
;
2844 MultiByteToWideChar(ucsdata
.font_codepage
, MB_USEGLYPHCHARS
,
2845 text
+mptr
, 2, uni_buf
+nlen
, 1);
2850 MultiByteToWideChar(ucsdata
.font_codepage
, MB_USEGLYPHCHARS
,
2851 text
+mptr
, 1, uni_buf
+nlen
, 1);
2859 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2860 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2861 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2862 SetBkMode(hdc
, TRANSPARENT
);
2863 ExtTextOutW(hdc
, x
- 1,
2864 y
- font_height
* (lattr
==
2865 LATTR_BOT
) + text_adjust
,
2866 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2870 } else if (DIRECT_FONT(attr
)) {
2872 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2873 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2874 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2875 SetBkMode(hdc
, TRANSPARENT
);
2877 /* GRR: This draws the character outside it's box and can leave
2878 * 'droppings' even with the clip box! I suppose I could loop it
2879 * one character at a time ... yuk.
2881 * Or ... I could do a test print with "W", and use +1 or -1 for this
2882 * shift depending on if the leftmost column is blank...
2884 ExtTextOut(hdc
, x
- 1,
2885 y
- font_height
* (lattr
==
2886 LATTR_BOT
) + text_adjust
,
2887 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2890 /* And 'normal' unicode characters */
2891 static WCHAR
*wbuf
= NULL
;
2892 static int wlen
= 0;
2897 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2899 for (i
= 0; i
< len
; i
++)
2900 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2903 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2904 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2906 /* And the shadow bold hack. */
2907 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2908 SetBkMode(hdc
, TRANSPARENT
);
2909 ExtTextOutW(hdc
, x
- 1,
2910 y
- font_height
* (lattr
==
2911 LATTR_BOT
) + text_adjust
,
2912 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2915 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2916 (und_mode
== UND_LINE
2917 && (attr
& ATTR_UNDER
)))) {
2920 if (lattr
== LATTR_BOT
)
2921 dec
= dec
* 2 - font_height
;
2923 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2924 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2925 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2926 oldpen
= SelectObject(hdc
, oldpen
);
2927 DeleteObject(oldpen
);
2931 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2932 unsigned long attr
, int lattr
)
2938 int ctype
= cfg
.cursor_type
;
2940 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || term
->big_cursor
)) {
2941 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2942 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2946 attr
|= TATTR_RIGHTCURS
;
2949 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2950 if (attr
& ATTR_WIDE
)
2957 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || term
->big_cursor
)) {
2960 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2961 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2962 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2963 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2964 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2965 Polyline(hdc
, pts
, 5);
2966 oldpen
= SelectObject(hdc
, oldpen
);
2967 DeleteObject(oldpen
);
2968 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2969 int startx
, starty
, dx
, dy
, length
, i
;
2972 starty
= y
+ descent
;
2975 length
= char_width
;
2978 if (attr
& TATTR_RIGHTCURS
)
2979 xadjust
= char_width
- 1;
2980 startx
= x
+ xadjust
;
2984 length
= font_height
;
2986 if (attr
& TATTR_ACTCURS
) {
2989 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2990 MoveToEx(hdc
, startx
, starty
, NULL
);
2991 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2992 oldpen
= SelectObject(hdc
, oldpen
);
2993 DeleteObject(oldpen
);
2995 for (i
= 0; i
< length
; i
++) {
2997 SetPixel(hdc
, startx
, starty
, colours
[23]);
3006 /* This function gets the actual width of a character in the normal font.
3008 int char_width(Context ctx
, int uc
) {
3012 /* If the font max is the same as the font ave width then this
3013 * function is a no-op.
3015 if (!font_dualwidth
) return 1;
3017 switch (uc
& CSET_MASK
) {
3019 uc
= ucsdata
.unitab_line
[uc
& 0xFF];
3022 uc
= ucsdata
.unitab_xterm
[uc
& 0xFF];
3025 uc
= ucsdata
.unitab_scoacs
[uc
& 0xFF];
3028 if (DIRECT_FONT(uc
)) {
3029 if (ucsdata
.dbcs_screenfont
) return 1;
3031 /* Speedup, I know of no font where ascii is the wrong width */
3032 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
3035 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
3036 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
3037 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
3038 another_font(FONT_OEM
);
3039 if (!fonts
[FONT_OEM
]) return 0;
3041 SelectObject(hdc
, fonts
[FONT_OEM
]);
3045 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
3046 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
3049 /* Speedup, I know of no font where ascii is the wrong width */
3050 if (uc
>= ' ' && uc
<= '~') return 1;
3052 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
3053 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
3054 /* Okay that one worked */ ;
3055 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
3056 /* This should work on 9x too, but it's "less accurate" */ ;
3061 ibuf
+= font_width
/ 2 -1;
3068 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
3069 * codes. Returns number of bytes used or zero to drop the message
3070 * or -1 to forward the message to windows.
3072 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
3073 unsigned char *output
)
3076 int scan
, left_alt
= 0, key_down
, shift_state
;
3078 unsigned char *p
= output
;
3079 static int alt_sum
= 0;
3081 HKL kbd_layout
= GetKeyboardLayout(0);
3083 static WORD keys
[3];
3084 static int compose_char
= 0;
3085 static WPARAM compose_key
= 0;
3087 r
= GetKeyboardState(keystate
);
3089 memset(keystate
, 0, sizeof(keystate
));
3092 #define SHOW_TOASCII_RESULT
3093 { /* Tell us all about key events */
3094 static BYTE oldstate
[256];
3095 static int first
= 1;
3099 memcpy(oldstate
, keystate
, sizeof(oldstate
));
3102 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
3104 } else if ((HIWORD(lParam
) & KF_UP
)
3105 && scan
== (HIWORD(lParam
) & 0xFF)) {
3109 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
3110 debug(("K_F%d", wParam
+ 1 - VK_F1
));
3123 debug(("VK_%02x", wParam
));
3125 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
3127 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
3129 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
3130 if (ch
>= ' ' && ch
<= '~')
3131 debug((", '%c'", ch
));
3133 debug((", $%02x", ch
));
3136 debug((", KB0=%02x", keys
[0]));
3138 debug((", KB1=%02x", keys
[1]));
3140 debug((", KB2=%02x", keys
[2]));
3142 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
3144 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
3146 if ((HIWORD(lParam
) & KF_EXTENDED
))
3148 if ((HIWORD(lParam
) & KF_UP
))
3152 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
3153 else if ((HIWORD(lParam
) & KF_UP
))
3154 oldstate
[wParam
& 0xFF] ^= 0x80;
3156 oldstate
[wParam
& 0xFF] ^= 0x81;
3158 for (ch
= 0; ch
< 256; ch
++)
3159 if (oldstate
[ch
] != keystate
[ch
])
3160 debug((", M%02x=%02x", ch
, keystate
[ch
]));
3162 memcpy(oldstate
, keystate
, sizeof(oldstate
));
3166 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
3167 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
3171 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
3172 if ((cfg
.funky_type
== 3 ||
3173 (cfg
.funky_type
<= 1 && term
->app_keypad_keys
&&
3175 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
3177 wParam
= VK_EXECUTE
;
3179 /* UnToggle NUMLock */
3180 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
3181 keystate
[VK_NUMLOCK
] ^= 1;
3184 /* And write back the 'adjusted' state */
3185 SetKeyboardState(keystate
);
3188 /* Disable Auto repeat if required */
3189 if (term
->repeat_off
&&
3190 (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
3193 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
3196 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
3198 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
3199 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
3200 if (cfg
.ctrlaltkeys
)
3201 keystate
[VK_MENU
] = 0;
3203 keystate
[VK_RMENU
] = 0x80;
3208 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
3209 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
3210 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
3212 /* Note if AltGr was pressed and if it was used as a compose key */
3213 if (!compose_state
) {
3214 compose_key
= 0x100;
3215 if (cfg
.compose_key
) {
3216 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
3217 compose_key
= wParam
;
3219 if (wParam
== VK_APPS
)
3220 compose_key
= wParam
;
3223 if (wParam
== compose_key
) {
3224 if (compose_state
== 0
3225 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
3227 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
3231 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
3234 if (compose_state
> 1 && left_alt
)
3237 /* Sanitize the number pad if not using a PC NumPad */
3238 if (left_alt
|| (term
->app_keypad_keys
&& !cfg
.no_applic_k
3239 && cfg
.funky_type
!= 2)
3240 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
3241 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
3245 nParam
= VK_NUMPAD0
;
3248 nParam
= VK_NUMPAD1
;
3251 nParam
= VK_NUMPAD2
;
3254 nParam
= VK_NUMPAD3
;
3257 nParam
= VK_NUMPAD4
;
3260 nParam
= VK_NUMPAD5
;
3263 nParam
= VK_NUMPAD6
;
3266 nParam
= VK_NUMPAD7
;
3269 nParam
= VK_NUMPAD8
;
3272 nParam
= VK_NUMPAD9
;
3275 nParam
= VK_DECIMAL
;
3279 if (keystate
[VK_NUMLOCK
] & 1)
3286 /* If a key is pressed and AltGr is not active */
3287 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
3288 /* Okay, prepare for most alts then ... */
3292 /* Lets see if it's a pattern we know all about ... */
3293 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
3294 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
3297 if (wParam
== VK_PRIOR
&& shift_state
== 2) {
3298 SendMessage(hwnd
, WM_VSCROLL
, SB_LINEUP
, 0);
3301 if (wParam
== VK_NEXT
&& shift_state
== 1) {
3302 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
3305 if (wParam
== VK_NEXT
&& shift_state
== 2) {
3306 SendMessage(hwnd
, WM_VSCROLL
, SB_LINEDOWN
, 0);
3309 if (wParam
== VK_INSERT
&& shift_state
== 1) {
3310 term_do_paste(term
);
3313 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
3316 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
3317 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
3320 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
&&
3321 (cfg
.resize_action
!= RESIZE_DISABLED
)) {
3322 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) != KF_REPEAT
)
3326 /* Control-Numlock for app-keypad mode switch */
3327 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
3328 term
->app_keypad_keys
^= 1;
3332 /* Nethack keypad */
3333 if (cfg
.nethack_keypad
&& !left_alt
) {
3336 *p
++ = shift_state ?
'B' : 'b';
3339 *p
++ = shift_state ?
'J' : 'j';
3342 *p
++ = shift_state ?
'N' : 'n';
3345 *p
++ = shift_state ?
'H' : 'h';
3348 *p
++ = shift_state ?
'.' : '.';
3351 *p
++ = shift_state ?
'L' : 'l';
3354 *p
++ = shift_state ?
'Y' : 'y';
3357 *p
++ = shift_state ?
'K' : 'k';
3360 *p
++ = shift_state ?
'U' : 'u';
3365 /* Application Keypad */
3369 if (cfg
.funky_type
== 3 ||
3370 (cfg
.funky_type
<= 1 &&
3371 term
->app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3385 if (term
->app_keypad_keys
&& !cfg
.no_applic_k
)
3422 if (cfg
.funky_type
== 2) {
3427 } else if (shift_state
)
3434 if (cfg
.funky_type
== 2)
3438 if (cfg
.funky_type
== 2)
3442 if (cfg
.funky_type
== 2)
3447 if (HIWORD(lParam
) & KF_EXTENDED
)
3452 if (term
->vt52_mode
) {
3453 if (xkey
>= 'P' && xkey
<= 'S')
3454 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3456 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3458 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3463 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3464 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3468 if (wParam
== VK_BACK
&& shift_state
== 1) { /* Shift Backspace */
3469 /* We do the opposite of what is configured */
3470 *p
++ = (cfg
.bksp_is_delete ?
0x08 : 0x7F);
3474 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3480 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3484 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3488 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3493 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3498 /* Control-2 to Control-8 are special */
3499 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3500 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3503 if (shift_state
== 2 && (wParam
== 0xBD || wParam
== 0xBF)) {
3507 if (shift_state
== 2 && wParam
== 0xDF) {
3511 if (shift_state
== 0 && wParam
== VK_RETURN
&& term
->cr_lf_return
) {
3518 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3519 * for integer decimal nn.)
3521 * We also deal with the weird ones here. Linux VCs replace F1
3522 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3523 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3529 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3532 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3535 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3538 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3541 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3544 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3547 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3550 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3553 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3556 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3589 if ((shift_state
&2) == 0) switch (wParam
) {
3609 /* Reorder edit keys to physical order */
3610 if (cfg
.funky_type
== 3 && code
<= 6)
3611 code
= "\0\2\1\4\5\3\6"[code
];
3613 if (term
->vt52_mode
&& code
> 0 && code
<= 6) {
3614 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3618 if (cfg
.funky_type
== 5 && /* SCO function keys */
3619 code
>= 11 && code
<= 34) {
3620 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3623 case VK_F1
: index
= 0; break;
3624 case VK_F2
: index
= 1; break;
3625 case VK_F3
: index
= 2; break;
3626 case VK_F4
: index
= 3; break;
3627 case VK_F5
: index
= 4; break;
3628 case VK_F6
: index
= 5; break;
3629 case VK_F7
: index
= 6; break;
3630 case VK_F8
: index
= 7; break;
3631 case VK_F9
: index
= 8; break;
3632 case VK_F10
: index
= 9; break;
3633 case VK_F11
: index
= 10; break;
3634 case VK_F12
: index
= 11; break;
3636 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3637 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3638 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3641 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3642 code
>= 1 && code
<= 6) {
3643 char codes
[] = "HL.FIG";
3647 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3651 if ((term
->vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3657 if (term
->vt52_mode
)
3658 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3661 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3664 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3665 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3668 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3669 if (term
->vt52_mode
)
3670 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3672 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3675 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3676 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3680 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3685 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3686 * some reason seems to send VK_CLEAR to Windows...).
3708 if (term
->vt52_mode
)
3709 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3711 int app_flg
= (term
->app_cursor_keys
&& !cfg
.no_applic_c
);
3714 * RDB: VT100 & VT102 manuals both state the
3715 * app cursor keys only work if the app keypad
3718 * SGT: That may well be true, but xterm
3719 * disagrees and so does at least one
3720 * application, so I've #if'ed this out and the
3721 * behaviour is back to PuTTY's original: app
3722 * cursor and app keypad are independently
3723 * switchable modes. If anyone complains about
3724 * _this_ I'll have to put in a configurable
3727 if (!term
->app_keypad_keys
)
3730 /* Useful mapping of Ctrl-arrows */
3731 if (shift_state
== 2)
3735 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3737 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3744 * Finally, deal with Return ourselves. (Win95 seems to
3745 * foul it up when Alt is pressed, for some reason.)
3747 if (wParam
== VK_RETURN
) { /* Return */
3753 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3754 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3759 /* Okay we've done everything interesting; let windows deal with
3760 * the boring stuff */
3764 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3765 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3767 keystate
[VK_CAPITAL
] = 0;
3770 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3771 #ifdef SHOW_TOASCII_RESULT
3772 if (r
== 1 && !key_down
) {
3774 if (in_utf(term
) || ucsdata
.dbcs_screenfont
)
3775 debug((", (U+%04x)", alt_sum
));
3777 debug((", LCH(%d)", alt_sum
));
3779 debug((", ACH(%d)", keys
[0]));
3784 for (r1
= 0; r1
< r
; r1
++) {
3785 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3794 * Interrupt an ongoing paste. I'm not sure this is
3795 * sensible, but for the moment it's preferable to
3796 * having to faff about buffering things.
3801 for (i
= 0; i
< r
; i
++) {
3802 unsigned char ch
= (unsigned char) keys
[i
];
3804 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3809 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3813 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3814 MessageBeep(MB_ICONHAND
);
3818 term_seen_key_event(term
);
3819 luni_send(ldisc
, &keybuf
, 1, 1);
3827 if (in_utf(term
) || ucsdata
.dbcs_screenfont
) {
3829 term_seen_key_event(term
);
3830 luni_send(ldisc
, &keybuf
, 1, 1);
3832 ch
= (char) alt_sum
;
3834 * We need not bother about stdin
3835 * backlogs here, because in GUI PuTTY
3836 * we can't do anything about it
3837 * anyway; there's no means of asking
3838 * Windows to hold off on KEYDOWN
3839 * messages. We _have_ to buffer
3840 * everything we're sent.
3842 term_seen_key_event(term
);
3843 ldisc_send(ldisc
, &ch
, 1, 1);
3847 term_seen_key_event(term
);
3848 lpage_send(ldisc
, kbd_codepage
, &ch
, 1, 1);
3850 if(capsOn
&& ch
< 0x80) {
3853 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3854 term_seen_key_event(term
);
3855 luni_send(ldisc
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3860 term_seen_key_event(term
);
3861 lpage_send(ldisc
, kbd_codepage
,
3862 cbuf
+!left_alt
, 1+!!left_alt
, 1);
3868 /* This is so the ALT-Numpad and dead keys work correctly. */
3873 /* If we're definitly not building up an ALT-54321 then clear it */
3876 /* If we will be using alt_sum fix the 256s */
3877 else if (keys
[0] && (in_utf(term
) || ucsdata
.dbcs_screenfont
))
3882 * ALT alone may or may not want to bring up the System menu.
3883 * If it's not meant to, we return 0 on presses or releases of
3884 * ALT, to show that we've swallowed the keystroke. Otherwise
3885 * we return -1, which means Windows will give the keystroke
3886 * its default handling (i.e. bring up the System menu).
3888 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3894 void request_paste(void *frontend
)
3897 * In Windows, pasting is synchronous: we can read the
3898 * clipboard with no difficulty, so request_paste() can just go
3901 term_do_paste(term
);
3904 void set_title(void *frontend
, char *title
)
3907 window_name
= smalloc(1 + strlen(title
));
3908 strcpy(window_name
, title
);
3909 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3910 SetWindowText(hwnd
, title
);
3913 void set_icon(void *frontend
, char *title
)
3916 icon_name
= smalloc(1 + strlen(title
));
3917 strcpy(icon_name
, title
);
3918 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3919 SetWindowText(hwnd
, title
);
3922 void set_sbar(void *frontend
, int total
, int start
, int page
)
3926 if (is_full_screen() ?
!cfg
.scrollbar_in_fullscreen
: !cfg
.scrollbar
)
3929 si
.cbSize
= sizeof(si
);
3930 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3932 si
.nMax
= total
- 1;
3936 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3939 Context
get_ctx(void *frontend
)
3945 SelectPalette(hdc
, pal
, FALSE
);
3951 void free_ctx(Context ctx
)
3953 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3954 ReleaseDC(hwnd
, ctx
);
3957 static void real_palette_set(int n
, int r
, int g
, int b
)
3960 logpal
->palPalEntry
[n
].peRed
= r
;
3961 logpal
->palPalEntry
[n
].peGreen
= g
;
3962 logpal
->palPalEntry
[n
].peBlue
= b
;
3963 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3964 colours
[n
] = PALETTERGB(r
, g
, b
);
3965 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3967 colours
[n
] = RGB(r
, g
, b
);
3970 void palette_set(void *frontend
, int n
, int r
, int g
, int b
)
3972 static const int first
[21] = {
3973 0, 2, 4, 6, 8, 10, 12, 14,
3974 1, 3, 5, 7, 9, 11, 13, 15,
3977 real_palette_set(first
[n
], r
, g
, b
);
3979 real_palette_set(first
[n
] + 1, r
, g
, b
);
3981 HDC hdc
= get_ctx(frontend
);
3982 UnrealizeObject(pal
);
3983 RealizePalette(hdc
);
3988 void palette_reset(void *frontend
)
3992 for (i
= 0; i
< NCOLOURS
; i
++) {
3994 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3995 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3996 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3997 logpal
->palPalEntry
[i
].peFlags
= 0;
3998 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3999 defpal
[i
].rgbtGreen
,
4000 defpal
[i
].rgbtBlue
);
4002 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
4003 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
4008 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
4009 hdc
= get_ctx(frontend
);
4010 RealizePalette(hdc
);
4015 void write_aclip(void *frontend
, char *data
, int len
, int must_deselect
)
4020 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
4023 lock
= GlobalLock(clipdata
);
4026 memcpy(lock
, data
, len
);
4027 ((unsigned char *) lock
)[len
] = 0;
4028 GlobalUnlock(clipdata
);
4031 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
4033 if (OpenClipboard(hwnd
)) {
4035 SetClipboardData(CF_TEXT
, clipdata
);
4038 GlobalFree(clipdata
);
4041 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
4045 * Note: unlike write_aclip() this will not append a nul.
4047 void write_clip(void *frontend
, wchar_t * data
, int len
, int must_deselect
)
4049 HGLOBAL clipdata
, clipdata2
, clipdata3
;
4051 void *lock
, *lock2
, *lock3
;
4053 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
4055 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
4056 len
* sizeof(wchar_t));
4057 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
4059 if (!clipdata
|| !clipdata2
) {
4061 GlobalFree(clipdata
);
4063 GlobalFree(clipdata2
);
4066 if (!(lock
= GlobalLock(clipdata
)))
4068 if (!(lock2
= GlobalLock(clipdata2
)))
4071 memcpy(lock
, data
, len
* sizeof(wchar_t));
4072 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
4074 if (cfg
.rtf_paste
) {
4075 wchar_t unitab
[256];
4077 unsigned char *tdata
= (unsigned char *)lock2
;
4078 wchar_t *udata
= (wchar_t *)lock
;
4079 int rtflen
= 0, uindex
= 0, tindex
= 0;
4081 int multilen
, blen
, alen
, totallen
, i
;
4082 char before
[16], after
[4];
4084 get_unitab(CP_ACP
, unitab
, 0);
4086 rtfsize
= 100 + strlen(cfg
.font
);
4087 rtf
= smalloc(rtfsize
);
4088 sprintf(rtf
, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
4089 GetACP(), cfg
.font
);
4090 rtflen
= strlen(rtf
);
4093 * We want to construct a piece of RTF that specifies the
4094 * same Unicode text. To do this we will read back in
4095 * parallel from the Unicode data in `udata' and the
4096 * non-Unicode data in `tdata'. For each character in
4097 * `tdata' which becomes the right thing in `udata' when
4098 * looked up in `unitab', we just copy straight over from
4099 * tdata. For each one that doesn't, we must WCToMB it
4100 * individually and produce a \u escape sequence.
4102 * It would probably be more robust to just bite the bullet
4103 * and WCToMB each individual Unicode character one by one,
4104 * then MBToWC each one back to see if it was an accurate
4105 * translation; but that strikes me as a horrifying number
4106 * of Windows API calls so I want to see if this faster way
4107 * will work. If it screws up badly we can always revert to
4108 * the simple and slow way.
4110 while (tindex
< len2
&& uindex
< len
&&
4111 tdata
[tindex
] && udata
[uindex
]) {
4112 if (tindex
+ 1 < len2
&&
4113 tdata
[tindex
] == '\r' &&
4114 tdata
[tindex
+1] == '\n') {
4118 if (unitab
[tdata
[tindex
]] == udata
[uindex
]) {
4124 multilen
= WideCharToMultiByte(CP_ACP
, 0, unitab
+uindex
, 1,
4125 NULL
, 0, NULL
, NULL
);
4126 if (multilen
!= 1) {
4127 blen
= sprintf(before
, "{\\uc%d\\u%d", multilen
,
4129 alen
= 1; strcpy(after
, "}");
4131 blen
= sprintf(before
, "\\u%d", udata
[uindex
]);
4132 alen
= 0; after
[0] = '\0';
4135 assert(tindex
+ multilen
<= len2
);
4136 totallen
= blen
+ alen
;
4137 for (i
= 0; i
< multilen
; i
++) {
4138 if (tdata
[tindex
+i
] == '\\' ||
4139 tdata
[tindex
+i
] == '{' ||
4140 tdata
[tindex
+i
] == '}')
4142 else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A)
4143 totallen
+= 6; /* \par\r\n */
4144 else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20)
4150 if (rtfsize
< rtflen
+ totallen
+ 3) {
4151 rtfsize
= rtflen
+ totallen
+ 512;
4152 rtf
= srealloc(rtf
, rtfsize
);
4155 strcpy(rtf
+ rtflen
, before
); rtflen
+= blen
;
4156 for (i
= 0; i
< multilen
; i
++) {
4157 if (tdata
[tindex
+i
] == '\\' ||
4158 tdata
[tindex
+i
] == '{' ||
4159 tdata
[tindex
+i
] == '}') {
4160 rtf
[rtflen
++] = '\\';
4161 rtf
[rtflen
++] = tdata
[tindex
+i
];
4162 } else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A) {
4163 rtflen
+= sprintf(rtf
+rtflen
, "\\par\r\n");
4164 } else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20) {
4165 rtflen
+= sprintf(rtf
+rtflen
, "\\'%02x", tdata
[tindex
+i
]);
4167 rtf
[rtflen
++] = tdata
[tindex
+i
];
4170 strcpy(rtf
+ rtflen
, after
); rtflen
+= alen
;
4176 strcpy(rtf
+ rtflen
, "}");
4179 clipdata3
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, rtflen
);
4180 if (clipdata3
&& (lock3
= GlobalLock(clipdata3
)) != NULL
) {
4182 GlobalUnlock(clipdata3
);
4188 GlobalUnlock(clipdata
);
4189 GlobalUnlock(clipdata2
);
4192 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
4194 if (OpenClipboard(hwnd
)) {
4196 SetClipboardData(CF_UNICODETEXT
, clipdata
);
4197 SetClipboardData(CF_TEXT
, clipdata2
);
4199 SetClipboardData(RegisterClipboardFormat(CF_RTF
), clipdata3
);
4202 GlobalFree(clipdata
);
4203 GlobalFree(clipdata2
);
4207 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
4210 void get_clip(void *frontend
, wchar_t ** p
, int *len
)
4212 static HGLOBAL clipdata
= NULL
;
4213 static wchar_t *converted
= 0;
4222 GlobalUnlock(clipdata
);
4225 } else if (OpenClipboard(NULL
)) {
4226 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
4228 *p
= GlobalLock(clipdata
);
4230 for (p2
= *p
; *p2
; p2
++);
4234 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
4238 s
= GlobalLock(clipdata
);
4239 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
4240 *p
= converted
= smalloc(i
* sizeof(wchar_t));
4241 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
4254 * Move `lines' lines from position `from' to position `to' in the
4257 void optimised_move(void *frontend
, int to
, int from
, int lines
)
4262 min
= (to
< from ? to
: from
);
4263 max
= to
+ from
- min
;
4265 r
.left
= offset_width
;
4266 r
.right
= offset_width
+ term
->cols
* font_width
;
4267 r
.top
= offset_height
+ min
* font_height
;
4268 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
4269 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
4274 * Print a message box and perform a fatal exit.
4276 void fatalbox(char *fmt
, ...)
4282 vsprintf(stuff
, fmt
, ap
);
4284 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
4289 * Print a modal (Really Bad) message box and perform a fatal exit.
4291 void modalfatalbox(char *fmt
, ...)
4297 vsprintf(stuff
, fmt
, ap
);
4299 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error",
4300 MB_SYSTEMMODAL
| MB_ICONERROR
| MB_OK
);
4305 * Manage window caption / taskbar flashing, if enabled.
4306 * 0 = stop, 1 = maintain, 2 = start
4308 static void flash_window(int mode
)
4310 static long last_flash
= 0;
4311 static int flashing
= 0;
4312 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
4315 FlashWindow(hwnd
, FALSE
);
4319 } else if (mode
== 2) {
4322 last_flash
= GetTickCount();
4324 FlashWindow(hwnd
, TRUE
);
4327 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
4330 long now
= GetTickCount();
4331 long fdiff
= now
- last_flash
;
4332 if (fdiff
< 0 || fdiff
> 450) {
4334 FlashWindow(hwnd
, TRUE
); /* toggle */
4343 void beep(void *frontend
, int mode
)
4345 if (mode
== BELL_DEFAULT
) {
4347 * For MessageBeep style bells, we want to be careful of
4348 * timing, because they don't have the nice property of
4349 * PlaySound bells that each one cancels the previous
4350 * active one. So we limit the rate to one per 50ms or so.
4352 static long lastbeep
= 0;
4355 beepdiff
= GetTickCount() - lastbeep
;
4356 if (beepdiff
>= 0 && beepdiff
< 50)
4360 * The above MessageBeep call takes time, so we record the
4361 * time _after_ it finishes rather than before it starts.
4363 lastbeep
= GetTickCount();
4364 } else if (mode
== BELL_WAVEFILE
) {
4365 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
4366 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
4367 sprintf(buf
, "Unable to play sound file\n%s\n"
4368 "Using default sound instead", cfg
.bell_wavefile
);
4369 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
4370 MB_OK
| MB_ICONEXCLAMATION
);
4371 cfg
.beep
= BELL_DEFAULT
;
4374 /* Otherwise, either visual bell or disabled; do nothing here */
4375 if (!term
->has_focus
) {
4376 flash_window(2); /* start */
4381 * Minimise or restore the window in response to a server-side
4384 void set_iconic(void *frontend
, int iconic
)
4386 if (IsIconic(hwnd
)) {
4388 ShowWindow(hwnd
, SW_RESTORE
);
4391 ShowWindow(hwnd
, SW_MINIMIZE
);
4396 * Move the window in response to a server-side request.
4398 void move_window(void *frontend
, int x
, int y
)
4400 if (cfg
.resize_action
== RESIZE_DISABLED
||
4401 cfg
.resize_action
== RESIZE_FONT
||
4405 SetWindowPos(hwnd
, NULL
, x
, y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
);
4409 * Move the window to the top or bottom of the z-order in response
4410 * to a server-side request.
4412 void set_zorder(void *frontend
, int top
)
4414 if (cfg
.alwaysontop
)
4415 return; /* ignore */
4416 SetWindowPos(hwnd
, top ? HWND_TOP
: HWND_BOTTOM
, 0, 0, 0, 0,
4417 SWP_NOMOVE
| SWP_NOSIZE
);
4421 * Refresh the window in response to a server-side request.
4423 void refresh_window(void *frontend
)
4425 InvalidateRect(hwnd
, NULL
, TRUE
);
4429 * Maximise or restore the window in response to a server-side
4432 void set_zoomed(void *frontend
, int zoomed
)
4434 if (IsZoomed(hwnd
)) {
4436 ShowWindow(hwnd
, SW_RESTORE
);
4439 ShowWindow(hwnd
, SW_MAXIMIZE
);
4444 * Report whether the window is iconic, for terminal reports.
4446 int is_iconic(void *frontend
)
4448 return IsIconic(hwnd
);
4452 * Report the window's position, for terminal reports.
4454 void get_window_pos(void *frontend
, int *x
, int *y
)
4457 GetWindowRect(hwnd
, &r
);
4463 * Report the window's pixel size, for terminal reports.
4465 void get_window_pixels(void *frontend
, int *x
, int *y
)
4468 GetWindowRect(hwnd
, &r
);
4469 *x
= r
.right
- r
.left
;
4470 *y
= r
.bottom
- r
.top
;
4474 * Return the window or icon title.
4476 char *get_window_title(void *frontend
, int icon
)
4478 return icon ? icon_name
: window_name
;
4482 * See if we're in full-screen mode.
4484 int is_full_screen()
4486 if (!IsZoomed(hwnd
))
4488 if (GetWindowLong(hwnd
, GWL_STYLE
) & WS_CAPTION
)
4493 /* Get the rect/size of a full screen window using the nearest available
4494 * monitor in multimon systems; default to something sensible if only
4495 * one monitor is present. */
4496 static int get_fullscreen_rect(RECT
* ss
)
4498 #ifdef MONITOR_DEFAULTTONEAREST
4501 mon
= MonitorFromWindow(hwnd
, MONITOR_DEFAULTTONEAREST
);
4502 mi
.cbSize
= sizeof(mi
);
4503 GetMonitorInfo(mon
, &mi
);
4505 /* structure copy */
4509 /* could also use code like this:
4510 ss->left = ss->top = 0;
4511 ss->right = GetSystemMetrics(SM_CXSCREEN);
4512 ss->bottom = GetSystemMetrics(SM_CYSCREEN);
4514 return GetClientRect(GetDesktopWindow(), ss
);
4520 * Go full-screen. This should only be called when we are already
4523 void make_full_screen()
4528 assert(IsZoomed(hwnd
));
4530 if (is_full_screen())
4533 /* Remove the window furniture. */
4534 style
= GetWindowLong(hwnd
, GWL_STYLE
);
4535 style
&= ~(WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
);
4536 if (cfg
.scrollbar_in_fullscreen
)
4537 style
|= WS_VSCROLL
;
4539 style
&= ~WS_VSCROLL
;
4540 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4542 /* Resize ourselves to exactly cover the nearest monitor. */
4543 get_fullscreen_rect(&ss
);
4544 SetWindowPos(hwnd
, HWND_TOP
, ss
.left
, ss
.top
,
4549 /* Tick the menu item in the System menu. */
4550 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4555 * Clear the full-screen attributes.
4557 void clear_full_screen()
4559 DWORD oldstyle
, style
;
4561 /* Reinstate the window furniture. */
4562 style
= oldstyle
= GetWindowLong(hwnd
, GWL_STYLE
);
4563 style
|= WS_CAPTION
| WS_BORDER
;
4564 if (cfg
.resize_action
== RESIZE_DISABLED
)
4565 style
&= ~WS_THICKFRAME
;
4567 style
|= WS_THICKFRAME
;
4569 style
|= WS_VSCROLL
;
4571 style
&= ~WS_VSCROLL
;
4572 if (style
!= oldstyle
) {
4573 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4574 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
4575 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
4579 /* Untick the menu item in the System menu. */
4580 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4585 * Toggle full-screen mode.
4587 void flip_full_screen()
4589 if (is_full_screen()) {
4590 ShowWindow(hwnd
, SW_RESTORE
);
4591 } else if (IsZoomed(hwnd
)) {
4594 SendMessage(hwnd
, WM_FULLSCR_ON_MAX
, 0, 0);
4595 ShowWindow(hwnd
, SW_MAXIMIZE
);
4599 void frontend_keypress(void *handle
)
4602 * Keypress termination in non-Close-On-Exit mode is not
4603 * currently supported in PuTTY proper, because the window
4604 * always has a perfectly good Close button anyway. So we do