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 SetWindowText(hwnd
, "PuTTY (inactive)");
914 * Report an error at the command-line parsing stage.
916 void cmdline_error(char *fmt
, ...)
922 vsprintf(stuff
, fmt
, ap
);
924 MessageBox(hwnd
, stuff
, "PuTTY Command Line Error", MB_ICONERROR
| MB_OK
);
929 * Actually do the job requested by a WM_NETEVENT
931 static void enact_pending_netevent(void)
933 static int reentering
= 0;
934 extern int select_result(WPARAM
, LPARAM
);
938 return; /* don't unpend the pending */
940 pending_netevent
= FALSE
;
943 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
946 if (ret
== 0 && !session_closed
) {
947 /* Abnormal exits will already have set session_closed and taken
948 * appropriate action. */
949 if (cfg
.close_on_exit
== COE_ALWAYS
||
950 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
952 session_closed
= TRUE
;
953 SetWindowText(hwnd
, "PuTTY (inactive)");
954 MessageBox(hwnd
, "Connection closed by remote host",
955 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
961 * Copy the colour palette from the configuration data into defpal.
962 * This is non-trivial because the colour indices are different.
964 static void cfgtopalette(void)
967 static const int ww
[] = {
968 6, 7, 8, 9, 10, 11, 12, 13,
969 14, 15, 16, 17, 18, 19, 20, 21,
970 0, 1, 2, 3, 4, 4, 5, 5
973 for (i
= 0; i
< 24; i
++) {
975 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
976 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
977 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
982 * Set up the colour palette.
984 static void init_palette(void)
987 HDC hdc
= GetDC(hwnd
);
989 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
990 logpal
= smalloc(sizeof(*logpal
)
991 - sizeof(logpal
->palPalEntry
)
992 + NCOLOURS
* sizeof(PALETTEENTRY
));
993 logpal
->palVersion
= 0x300;
994 logpal
->palNumEntries
= NCOLOURS
;
995 for (i
= 0; i
< NCOLOURS
; i
++) {
996 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
997 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
998 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
999 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
1001 pal
= CreatePalette(logpal
);
1003 SelectPalette(hdc
, pal
, FALSE
);
1004 RealizePalette(hdc
);
1005 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
1008 ReleaseDC(hwnd
, hdc
);
1011 for (i
= 0; i
< NCOLOURS
; i
++)
1012 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
1013 defpal
[i
].rgbtGreen
,
1014 defpal
[i
].rgbtBlue
);
1016 for (i
= 0; i
< NCOLOURS
; i
++)
1017 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
1018 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
1022 * Initialise all the fonts we will need initially. There may be as many as
1023 * three or as few as one. The other (poentially) twentyone fonts are done
1024 * if/when they are needed.
1028 * - check the font width and height, correcting our guesses if
1031 * - verify that the bold font is the same width as the ordinary
1032 * one, and engage shadow bolding if not.
1034 * - verify that the underlined font is the same width as the
1035 * ordinary one (manual underlining by means of line drawing can
1036 * be done in a pinch).
1038 static void init_fonts(int pick_width
, int pick_height
)
1045 int fw_dontcare
, fw_bold
;
1047 for (i
= 0; i
< FONT_MAXNO
; i
++)
1050 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1051 und_mode
= UND_FONT
;
1053 if (cfg
.fontisbold
) {
1054 fw_dontcare
= FW_BOLD
;
1057 fw_dontcare
= FW_DONTCARE
;
1064 font_height
= pick_height
;
1066 font_height
= cfg
.fontheight
;
1067 if (font_height
> 0) {
1069 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
1072 font_width
= pick_width
;
1074 #define f(i,c,w,u) \
1075 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
1076 c, OUT_DEFAULT_PRECIS, \
1077 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
1078 FIXED_PITCH | FF_DONTCARE, cfg.font)
1080 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
1082 lfont
.lfHeight
= font_height
;
1083 lfont
.lfWidth
= font_width
;
1084 lfont
.lfEscapement
= 0;
1085 lfont
.lfOrientation
= 0;
1086 lfont
.lfWeight
= fw_dontcare
;
1087 lfont
.lfItalic
= FALSE
;
1088 lfont
.lfUnderline
= FALSE
;
1089 lfont
.lfStrikeOut
= FALSE
;
1090 lfont
.lfCharSet
= cfg
.fontcharset
;
1091 lfont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
1092 lfont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
1093 lfont
.lfQuality
= DEFAULT_QUALITY
;
1094 lfont
.lfPitchAndFamily
= FIXED_PITCH
| FF_DONTCARE
;
1095 strncpy(lfont
.lfFaceName
, cfg
.font
, LF_FACESIZE
);
1097 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
1098 GetTextMetrics(hdc
, &tm
);
1100 if (pick_width
== 0 || pick_height
== 0) {
1101 font_height
= tm
.tmHeight
;
1102 font_width
= tm
.tmAveCharWidth
;
1104 font_dualwidth
= (tm
.tmAveCharWidth
!= tm
.tmMaxCharWidth
);
1106 #ifdef RDB_DEBUG_PATCH
1107 debug(23, "Primary font H=%d, AW=%d, MW=%d",
1108 tm
.tmHeight
, tm
.tmAveCharWidth
, tm
.tmMaxCharWidth
);
1113 DWORD cset
= tm
.tmCharSet
;
1114 memset(&info
, 0xFF, sizeof(info
));
1116 /* !!! Yes the next line is right */
1117 if (cset
== OEM_CHARSET
)
1118 ucsdata
.font_codepage
= GetOEMCP();
1120 if (TranslateCharsetInfo ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
))
1121 ucsdata
.font_codepage
= info
.ciACP
;
1123 ucsdata
.font_codepage
= -1;
1125 GetCPInfo(ucsdata
.font_codepage
, &cpinfo
);
1126 ucsdata
.dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
1129 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
1132 * Some fonts, e.g. 9-pt Courier, draw their underlines
1133 * outside their character cell. We successfully prevent
1134 * screen corruption by clipping the text output, but then
1135 * we lose the underline completely. Here we try to work
1136 * out whether this is such a font, and if it is, we set a
1137 * flag that causes underlines to be drawn by hand.
1139 * Having tried other more sophisticated approaches (such
1140 * as examining the TEXTMETRIC structure or requesting the
1141 * height of a string), I think we'll do this the brute
1142 * force way: we create a small bitmap, draw an underlined
1143 * space on it, and test to see whether any pixels are
1144 * foreground-coloured. (Since we expect the underline to
1145 * go all the way across the character cell, we only search
1146 * down a single column of the bitmap, half way across.)
1150 HBITMAP und_bm
, und_oldbm
;
1154 und_dc
= CreateCompatibleDC(hdc
);
1155 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
1156 und_oldbm
= SelectObject(und_dc
, und_bm
);
1157 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
1158 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
1159 SetTextColor(und_dc
, RGB(255, 255, 255));
1160 SetBkColor(und_dc
, RGB(0, 0, 0));
1161 SetBkMode(und_dc
, OPAQUE
);
1162 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
1164 for (i
= 0; i
< font_height
; i
++) {
1165 c
= GetPixel(und_dc
, font_width
/ 2, i
);
1166 if (c
!= RGB(0, 0, 0))
1169 SelectObject(und_dc
, und_oldbm
);
1170 DeleteObject(und_bm
);
1173 und_mode
= UND_LINE
;
1174 DeleteObject(fonts
[FONT_UNDERLINE
]);
1175 fonts
[FONT_UNDERLINE
] = 0;
1179 if (bold_mode
== BOLD_FONT
) {
1180 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
1184 descent
= tm
.tmAscent
+ 1;
1185 if (descent
>= font_height
)
1186 descent
= font_height
- 1;
1188 for (i
= 0; i
< 3; i
++) {
1190 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
1191 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
1198 ReleaseDC(hwnd
, hdc
);
1200 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1201 und_mode
= UND_LINE
;
1202 DeleteObject(fonts
[FONT_UNDERLINE
]);
1203 fonts
[FONT_UNDERLINE
] = 0;
1206 if (bold_mode
== BOLD_FONT
&&
1207 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1208 bold_mode
= BOLD_SHADOW
;
1209 DeleteObject(fonts
[FONT_BOLD
]);
1210 fonts
[FONT_BOLD
] = 0;
1212 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1214 init_ucs(&cfg
, &ucsdata
);
1217 static void another_font(int fontno
)
1220 int fw_dontcare
, fw_bold
;
1224 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1227 basefont
= (fontno
& ~(FONT_BOLDUND
));
1228 if (basefont
!= fontno
&& !fontflag
[basefont
])
1229 another_font(basefont
);
1231 if (cfg
.fontisbold
) {
1232 fw_dontcare
= FW_BOLD
;
1235 fw_dontcare
= FW_DONTCARE
;
1239 c
= cfg
.fontcharset
;
1245 if (fontno
& FONT_WIDE
)
1247 if (fontno
& FONT_NARROW
)
1249 if (fontno
& FONT_OEM
)
1251 if (fontno
& FONT_BOLD
)
1253 if (fontno
& FONT_UNDERLINE
)
1257 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1258 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1259 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1260 FIXED_PITCH
| FF_DONTCARE
, s
);
1262 fontflag
[fontno
] = 1;
1265 static void deinit_fonts(void)
1268 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1270 DeleteObject(fonts
[i
]);
1276 void request_resize(void *frontend
, int w
, int h
)
1280 /* If the window is maximized supress resizing attempts */
1281 if (IsZoomed(hwnd
)) {
1282 if (cfg
.resize_action
== RESIZE_TERM
)
1286 if (cfg
.resize_action
== RESIZE_DISABLED
) return;
1287 if (h
== term
->rows
&& w
== term
->cols
) return;
1289 /* Sanity checks ... */
1291 static int first_time
= 1;
1294 switch (first_time
) {
1296 /* Get the size of the screen */
1297 if (get_fullscreen_rect(&ss
))
1298 /* first_time = 0 */ ;
1304 /* Make sure the values are sane */
1305 width
= (ss
.right
- ss
.left
- extra_width
) / 4;
1306 height
= (ss
.bottom
- ss
.top
- extra_height
) / 6;
1308 if (w
> width
|| h
> height
)
1317 term_size(term
, h
, w
, cfg
.savelines
);
1319 if (cfg
.resize_action
!= RESIZE_FONT
&& !IsZoomed(hwnd
)) {
1320 width
= extra_width
+ font_width
* w
;
1321 height
= extra_height
+ font_height
* h
;
1323 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1324 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1325 SWP_NOMOVE
| SWP_NOZORDER
);
1329 InvalidateRect(hwnd
, NULL
, TRUE
);
1332 static void reset_window(int reinit
) {
1334 * This function decides how to resize or redraw when the
1335 * user changes something.
1337 * This function doesn't like to change the terminal size but if the
1338 * font size is locked that may be it's only soluion.
1340 int win_width
, win_height
;
1343 #ifdef RDB_DEBUG_PATCH
1344 debug((27, "reset_window()"));
1347 /* Current window sizes ... */
1348 GetWindowRect(hwnd
, &wr
);
1349 GetClientRect(hwnd
, &cr
);
1351 win_width
= cr
.right
- cr
.left
;
1352 win_height
= cr
.bottom
- cr
.top
;
1354 if (cfg
.resize_action
== RESIZE_DISABLED
) reinit
= 2;
1356 /* Are we being forced to reload the fonts ? */
1358 #ifdef RDB_DEBUG_PATCH
1359 debug((27, "reset_window() -- Forced deinit"));
1365 /* Oh, looks like we're minimised */
1366 if (win_width
== 0 || win_height
== 0)
1369 /* Is the window out of position ? */
1371 (offset_width
!= (win_width
-font_width
*term
->cols
)/2 ||
1372 offset_height
!= (win_height
-font_height
*term
->rows
)/2) ){
1373 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1374 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1375 InvalidateRect(hwnd
, NULL
, TRUE
);
1376 #ifdef RDB_DEBUG_PATCH
1377 debug((27, "reset_window() -> Reposition terminal"));
1381 if (IsZoomed(hwnd
)) {
1382 /* We're fullscreen, this means we must not change the size of
1383 * the window so it's the font size or the terminal itself.
1386 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1387 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1389 if (cfg
.resize_action
!= RESIZE_TERM
) {
1390 if ( font_width
!= win_width
/term
->cols
||
1391 font_height
!= win_height
/term
->rows
) {
1393 init_fonts(win_width
/term
->cols
, win_height
/term
->rows
);
1394 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1395 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1396 InvalidateRect(hwnd
, NULL
, TRUE
);
1397 #ifdef RDB_DEBUG_PATCH
1398 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1399 font_width
, font_height
));
1403 if ( font_width
!= win_width
/term
->cols
||
1404 font_height
!= win_height
/term
->rows
) {
1405 /* Our only choice at this point is to change the
1406 * size of the terminal; Oh well.
1408 term_size(term
, win_height
/font_height
, win_width
/font_width
,
1410 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1411 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1412 InvalidateRect(hwnd
, NULL
, TRUE
);
1413 #ifdef RDB_DEBUG_PATCH
1414 debug((27, "reset_window() -> Zoomed term_size"));
1421 /* Hmm, a force re-init means we should ignore the current window
1422 * so we resize to the default font size.
1425 #ifdef RDB_DEBUG_PATCH
1426 debug((27, "reset_window() -> Forced re-init"));
1429 offset_width
= offset_height
= cfg
.window_border
;
1430 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1431 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1433 if (win_width
!= font_width
*term
->cols
+ offset_width
*2 ||
1434 win_height
!= font_height
*term
->rows
+ offset_height
*2) {
1436 /* If this is too large windows will resize it to the maximum
1437 * allowed window size, we will then be back in here and resize
1438 * the font or terminal to fit.
1440 SetWindowPos(hwnd
, NULL
, 0, 0,
1441 font_width
*term
->cols
+ extra_width
,
1442 font_height
*term
->rows
+ extra_height
,
1443 SWP_NOMOVE
| SWP_NOZORDER
);
1446 InvalidateRect(hwnd
, NULL
, TRUE
);
1450 /* Okay the user doesn't want us to change the font so we try the
1451 * window. But that may be too big for the screen which forces us
1452 * to change the terminal.
1454 if ((cfg
.resize_action
== RESIZE_TERM
&& reinit
<=0) ||
1455 (cfg
.resize_action
== RESIZE_EITHER
&& reinit
<0) ||
1457 offset_width
= offset_height
= cfg
.window_border
;
1458 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1459 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1461 if (win_width
!= font_width
*term
->cols
+ offset_width
*2 ||
1462 win_height
!= font_height
*term
->rows
+ offset_height
*2) {
1467 get_fullscreen_rect(&ss
);
1469 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1470 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1473 if ( term
->rows
> height
|| term
->cols
> width
) {
1474 if (cfg
.resize_action
== RESIZE_EITHER
) {
1475 /* Make the font the biggest we can */
1476 if (term
->cols
> width
)
1477 font_width
= (ss
.right
- ss
.left
- extra_width
)
1479 if (term
->rows
> height
)
1480 font_height
= (ss
.bottom
- ss
.top
- extra_height
)
1484 init_fonts(font_width
, font_height
);
1486 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1487 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1489 if ( height
> term
->rows
) height
= term
->rows
;
1490 if ( width
> term
->cols
) width
= term
->cols
;
1491 term_size(term
, height
, width
, cfg
.savelines
);
1492 #ifdef RDB_DEBUG_PATCH
1493 debug((27, "reset_window() -> term resize to (%d,%d)",
1499 SetWindowPos(hwnd
, NULL
, 0, 0,
1500 font_width
*term
->cols
+ extra_width
,
1501 font_height
*term
->rows
+ extra_height
,
1502 SWP_NOMOVE
| SWP_NOZORDER
);
1504 InvalidateRect(hwnd
, NULL
, TRUE
);
1505 #ifdef RDB_DEBUG_PATCH
1506 debug((27, "reset_window() -> window resize to (%d,%d)",
1507 font_width
*term
->cols
+ extra_width
,
1508 font_height
*term
->rows
+ extra_height
));
1514 /* We're allowed to or must change the font but do we want to ? */
1516 if (font_width
!= (win_width
-cfg
.window_border
*2)/term
->cols
||
1517 font_height
!= (win_height
-cfg
.window_border
*2)/term
->rows
) {
1520 init_fonts((win_width
-cfg
.window_border
*2)/term
->cols
,
1521 (win_height
-cfg
.window_border
*2)/term
->rows
);
1522 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1523 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1525 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+offset_width
*2;
1526 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1528 InvalidateRect(hwnd
, NULL
, TRUE
);
1529 #ifdef RDB_DEBUG_PATCH
1530 debug((25, "reset_window() -> font resize to (%d,%d)",
1531 font_width
, font_height
));
1536 static void set_input_locale(HKL kl
)
1540 GetLocaleInfo(LOWORD(kl
), LOCALE_IDEFAULTANSICODEPAGE
,
1541 lbuf
, sizeof(lbuf
));
1543 kbd_codepage
= atoi(lbuf
);
1546 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
, int alt
)
1548 int thistime
= GetMessageTime();
1550 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1551 lastbtn
= MBT_NOTHING
;
1552 term_mouse(term
, b
, MA_CLICK
, x
, y
, shift
, ctrl
, alt
);
1556 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1557 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1558 lastact
== MA_2CLK ? MA_3CLK
:
1559 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1564 if (lastact
!= MA_NOTHING
)
1565 term_mouse(term
, b
, lastact
, x
, y
, shift
, ctrl
, alt
);
1566 lasttime
= thistime
;
1570 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1571 * into a cooked one (SELECT, EXTEND, PASTE).
1573 Mouse_Button
translate_button(void *frontend
, Mouse_Button button
)
1575 if (button
== MBT_LEFT
)
1577 if (button
== MBT_MIDDLE
)
1578 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1579 if (button
== MBT_RIGHT
)
1580 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1581 return 0; /* shouldn't happen */
1584 static void show_mouseptr(int show
)
1586 static int cursor_visible
= 1;
1587 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1589 if (cursor_visible
&& !show
)
1591 else if (!cursor_visible
&& show
)
1593 cursor_visible
= show
;
1596 static int is_alt_pressed(void)
1599 int r
= GetKeyboardState(keystate
);
1602 if (keystate
[VK_MENU
] & 0x80)
1604 if (keystate
[VK_RMENU
] & 0x80)
1609 static int is_shift_pressed(void)
1612 int r
= GetKeyboardState(keystate
);
1615 if (keystate
[VK_SHIFT
] & 0x80)
1620 static int resizing
;
1622 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1623 WPARAM wParam
, LPARAM lParam
)
1626 static int ignore_clip
= FALSE
;
1627 static int need_backend_resize
= FALSE
;
1628 static int fullscr_on_max
= FALSE
;
1632 if (pending_netevent
)
1633 enact_pending_netevent();
1634 if (GetCapture() != hwnd
||
1635 (send_raw_mouse
&& !(cfg
.mouse_override
&& is_shift_pressed())))
1641 if (cfg
.ping_interval
> 0) {
1644 if (now
- last_movement
> cfg
.ping_interval
) {
1645 back
->special(backhandle
, TS_PING
);
1646 last_movement
= now
;
1649 net_pending_errors();
1655 if (!cfg
.warn_on_close
|| session_closed
||
1657 "Are you sure you want to close this session?",
1658 "PuTTY Exit Confirmation",
1659 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1660 DestroyWindow(hwnd
);
1667 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1679 PROCESS_INFORMATION pi
;
1680 HANDLE filemap
= NULL
;
1682 if (wParam
== IDM_DUPSESS
) {
1684 * Allocate a file-mapping memory chunk for the
1687 SECURITY_ATTRIBUTES sa
;
1690 sa
.nLength
= sizeof(sa
);
1691 sa
.lpSecurityDescriptor
= NULL
;
1692 sa
.bInheritHandle
= TRUE
;
1693 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1696 0, sizeof(Config
), NULL
);
1698 p
= (Config
*) MapViewOfFile(filemap
,
1700 0, 0, sizeof(Config
));
1702 *p
= cfg
; /* structure copy */
1706 sprintf(c
, "putty &%p", filemap
);
1708 } else if (wParam
== IDM_SAVEDSESS
) {
1709 if ((lParam
- IDM_SAVED_MIN
) / 16 < sesslist
.nsessions
) {
1711 sesslist
.sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1712 cl
= smalloc(16 + strlen(session
));
1713 /* 8, but play safe */
1716 /* not a very important failure mode */
1718 sprintf(cl
, "putty @%s", session
);
1726 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1728 si
.lpReserved
= NULL
;
1729 si
.lpDesktop
= NULL
;
1733 si
.lpReserved2
= NULL
;
1734 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1735 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1738 CloseHandle(filemap
);
1748 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1751 if (!do_reconfig(hwnd
))
1755 /* Disable full-screen if resizing forbidden */
1756 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
1757 EnableMenuItem(m
, IDM_FULLSCREEN
, MF_BYCOMMAND
|
1758 (cfg
.resize_action
== RESIZE_DISABLED
)
1759 ? MF_GRAYED
: MF_ENABLED
);
1760 /* Gracefully unzoom if necessary */
1761 if (IsZoomed(hwnd
) &&
1762 (cfg
.resize_action
== RESIZE_DISABLED
)) {
1763 ShowWindow(hwnd
, SW_RESTORE
);
1767 /* Pass new config data to the logging module */
1768 log_reconfig(logctx
, &cfg
);
1772 * Flush the line discipline's edit buffer in the
1773 * case where local editing has just been disabled.
1775 ldisc_send(ldisc
, NULL
, 0, 0);
1783 /* Pass new config data to the terminal */
1784 term_reconfig(term
, &cfg
);
1786 /* Pass new config data to the back end */
1787 back
->reconfig(backhandle
, &cfg
);
1789 /* Screen size changed ? */
1790 if (cfg
.height
!= prev_cfg
.height
||
1791 cfg
.width
!= prev_cfg
.width
||
1792 cfg
.savelines
!= prev_cfg
.savelines
||
1793 cfg
.resize_action
== RESIZE_FONT
||
1794 (cfg
.resize_action
== RESIZE_EITHER
&& IsZoomed(hwnd
)) ||
1795 cfg
.resize_action
== RESIZE_DISABLED
)
1796 term_size(term
, cfg
.height
, cfg
.width
, cfg
.savelines
);
1798 /* Enable or disable the scroll bar, etc */
1800 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1801 LONG nexflag
, exflag
=
1802 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1805 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1806 if (cfg
.alwaysontop
) {
1807 nexflag
|= WS_EX_TOPMOST
;
1808 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1809 SWP_NOMOVE
| SWP_NOSIZE
);
1811 nexflag
&= ~(WS_EX_TOPMOST
);
1812 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1813 SWP_NOMOVE
| SWP_NOSIZE
);
1816 if (cfg
.sunken_edge
)
1817 nexflag
|= WS_EX_CLIENTEDGE
;
1819 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1822 if (is_full_screen() ?
1823 cfg
.scrollbar_in_fullscreen
: cfg
.scrollbar
)
1826 nflg
&= ~WS_VSCROLL
;
1828 if (cfg
.resize_action
== RESIZE_DISABLED
||
1830 nflg
&= ~WS_THICKFRAME
;
1832 nflg
|= WS_THICKFRAME
;
1834 if (cfg
.resize_action
== RESIZE_DISABLED
)
1835 nflg
&= ~WS_MAXIMIZEBOX
;
1837 nflg
|= WS_MAXIMIZEBOX
;
1839 if (nflg
!= flag
|| nexflag
!= exflag
) {
1841 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1842 if (nexflag
!= exflag
)
1843 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1845 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1846 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1847 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1855 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1860 set_title(NULL
, cfg
.wintitle
);
1861 if (IsIconic(hwnd
)) {
1863 cfg
.win_name_always ? window_name
:
1867 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1868 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1869 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1870 cfg
.fontheight
!= prev_cfg
.fontheight
||
1871 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1872 cfg
.vtmode
!= prev_cfg
.vtmode
||
1873 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1874 cfg
.resize_action
== RESIZE_DISABLED
||
1875 cfg
.resize_action
== RESIZE_EITHER
||
1876 (cfg
.resize_action
!= prev_cfg
.resize_action
))
1879 InvalidateRect(hwnd
, NULL
, TRUE
);
1880 reset_window(init_lvl
);
1881 net_pending_errors();
1892 ldisc_send(ldisc
, NULL
, 0, 0);
1895 back
->special(backhandle
, TS_AYT
);
1896 net_pending_errors();
1899 back
->special(backhandle
, TS_BRK
);
1900 net_pending_errors();
1903 back
->special(backhandle
, TS_SYNCH
);
1904 net_pending_errors();
1907 back
->special(backhandle
, TS_EC
);
1908 net_pending_errors();
1911 back
->special(backhandle
, TS_EL
);
1912 net_pending_errors();
1915 back
->special(backhandle
, TS_GA
);
1916 net_pending_errors();
1919 back
->special(backhandle
, TS_NOP
);
1920 net_pending_errors();
1923 back
->special(backhandle
, TS_ABORT
);
1924 net_pending_errors();
1927 back
->special(backhandle
, TS_AO
);
1928 net_pending_errors();
1931 back
->special(backhandle
, TS_IP
);
1932 net_pending_errors();
1935 back
->special(backhandle
, TS_SUSP
);
1936 net_pending_errors();
1939 back
->special(backhandle
, TS_EOR
);
1940 net_pending_errors();
1943 back
->special(backhandle
, TS_EOF
);
1944 net_pending_errors();
1950 WinHelp(hwnd
, help_path
,
1951 help_has_contents ? HELP_FINDER
: HELP_CONTENTS
, 0);
1955 * We get this if the System menu has been activated
1962 * We get this if the System menu has been activated
1963 * using the keyboard. This might happen from within
1964 * TranslateKey, in which case it really wants to be
1965 * followed by a `space' character to actually _bring
1966 * the menu up_ rather than just sitting there in
1967 * `ready to appear' state.
1969 show_mouseptr(1); /* make sure pointer is visible */
1971 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1973 case IDM_FULLSCREEN
:
1977 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1978 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1983 #define X_POS(l) ((int)(short)LOWORD(l))
1984 #define Y_POS(l) ((int)(short)HIWORD(l))
1986 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1987 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1988 case WM_LBUTTONDOWN
:
1989 case WM_MBUTTONDOWN
:
1990 case WM_RBUTTONDOWN
:
1998 case WM_LBUTTONDOWN
:
2002 case WM_MBUTTONDOWN
:
2003 button
= MBT_MIDDLE
;
2006 case WM_RBUTTONDOWN
:
2015 button
= MBT_MIDDLE
;
2023 button
= press
= 0; /* shouldn't happen */
2027 * Special case: in full-screen mode, if the left
2028 * button is clicked in the very top left corner of the
2029 * window, we put up the System menu instead of doing
2032 if (is_full_screen() && press
&& button
== MBT_LEFT
&&
2033 X_POS(lParam
) == 0 && Y_POS(lParam
) == 0) {
2034 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_MOUSEMENU
, 0);
2039 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
2040 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
,
2044 term_mouse(term
, button
, MA_RELEASE
,
2045 TO_CHR_X(X_POS(lParam
)),
2046 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
2047 wParam
& MK_CONTROL
, is_alt_pressed());
2055 * Add the mouse position and message time to the random
2058 noise_ultralight(lParam
);
2060 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
) &&
2061 GetCapture() == hwnd
) {
2063 if (wParam
& MK_LBUTTON
)
2065 else if (wParam
& MK_MBUTTON
)
2069 term_mouse(term
, b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
2070 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
2071 wParam
& MK_CONTROL
, is_alt_pressed());
2074 case WM_NCMOUSEMOVE
:
2076 noise_ultralight(lParam
);
2078 case WM_IGNORE_CLIP
:
2079 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
2081 case WM_DESTROYCLIPBOARD
:
2083 term_deselect(term
);
2084 ignore_clip
= FALSE
;
2090 hdc
= BeginPaint(hwnd
, &p
);
2092 SelectPalette(hdc
, pal
, TRUE
);
2093 RealizePalette(hdc
);
2095 term_paint(term
, hdc
,
2096 (p
.rcPaint
.left
-offset_width
)/font_width
,
2097 (p
.rcPaint
.top
-offset_height
)/font_height
,
2098 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
2099 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
,
2103 p
.rcPaint
.left
< offset_width
||
2104 p
.rcPaint
.top
< offset_height
||
2105 p
.rcPaint
.right
>= offset_width
+ font_width
*term
->cols
||
2106 p
.rcPaint
.bottom
>= offset_height
+ font_height
*term
->rows
)
2108 HBRUSH fillcolour
, oldbrush
;
2110 fillcolour
= CreateSolidBrush (
2111 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
2112 oldbrush
= SelectObject(hdc
, fillcolour
);
2113 edge
= CreatePen(PS_SOLID
, 0,
2114 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
2115 oldpen
= SelectObject(hdc
, edge
);
2118 * Jordan Russell reports that this apparently
2119 * ineffectual IntersectClipRect() call masks a
2120 * Windows NT/2K bug causing strange display
2121 * problems when the PuTTY window is taller than
2122 * the primary monitor. It seems harmless enough...
2124 IntersectClipRect(hdc
,
2125 p
.rcPaint
.left
, p
.rcPaint
.top
,
2126 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
2128 ExcludeClipRect(hdc
,
2129 offset_width
, offset_height
,
2130 offset_width
+font_width
*term
->cols
,
2131 offset_height
+font_height
*term
->rows
);
2133 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
2134 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
2136 // SelectClipRgn(hdc, NULL);
2138 SelectObject(hdc
, oldbrush
);
2139 DeleteObject(fillcolour
);
2140 SelectObject(hdc
, oldpen
);
2143 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
2144 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
2150 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2151 * but the only one that's likely to try to overload us is FD_READ.
2152 * This means buffering just one is fine.
2154 if (pending_netevent
)
2155 enact_pending_netevent();
2157 pending_netevent
= TRUE
;
2158 pend_netevent_wParam
= wParam
;
2159 pend_netevent_lParam
= lParam
;
2160 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
2161 enact_pending_netevent();
2163 time(&last_movement
);
2166 term
->has_focus
= TRUE
;
2167 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
2169 flash_window(0); /* stop */
2176 term
->has_focus
= FALSE
;
2178 caret_x
= caret_y
= -1; /* ensure caret is replaced next time */
2182 case WM_ENTERSIZEMOVE
:
2183 #ifdef RDB_DEBUG_PATCH
2184 debug((27, "WM_ENTERSIZEMOVE"));
2188 need_backend_resize
= FALSE
;
2190 case WM_EXITSIZEMOVE
:
2193 #ifdef RDB_DEBUG_PATCH
2194 debug((27, "WM_EXITSIZEMOVE"));
2196 if (need_backend_resize
) {
2197 term_size(term
, cfg
.height
, cfg
.width
, cfg
.savelines
);
2198 InvalidateRect(hwnd
, NULL
, TRUE
);
2203 * This does two jobs:
2204 * 1) Keep the sizetip uptodate
2205 * 2) Make sure the window size is _stepped_ in units of the font size.
2207 if (cfg
.resize_action
!= RESIZE_FONT
&& !is_alt_pressed()) {
2208 int width
, height
, w
, h
, ew
, eh
;
2209 LPRECT r
= (LPRECT
) lParam
;
2211 if ( !need_backend_resize
&& cfg
.resize_action
== RESIZE_EITHER
&&
2212 (cfg
.height
!= term
->rows
|| cfg
.width
!= term
->cols
)) {
2214 * Great! It seems that both the terminal size and the
2215 * font size have been changed and the user is now dragging.
2217 * It will now be difficult to get back to the configured
2220 * This would be easier but it seems to be too confusing.
2222 term_size(term, cfg.height, cfg.width, cfg.savelines);
2225 cfg
.height
=term
->rows
; cfg
.width
=term
->cols
;
2227 InvalidateRect(hwnd
, NULL
, TRUE
);
2228 need_backend_resize
= TRUE
;
2231 width
= r
->right
- r
->left
- extra_width
;
2232 height
= r
->bottom
- r
->top
- extra_height
;
2233 w
= (width
+ font_width
/ 2) / font_width
;
2236 h
= (height
+ font_height
/ 2) / font_height
;
2239 UpdateSizeTip(hwnd
, w
, h
);
2240 ew
= width
- w
* font_width
;
2241 eh
= height
- h
* font_height
;
2243 if (wParam
== WMSZ_LEFT
||
2244 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2250 if (wParam
== WMSZ_TOP
||
2251 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2261 int width
, height
, w
, h
, rv
= 0;
2262 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
2263 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2264 LPRECT r
= (LPRECT
) lParam
;
2266 width
= r
->right
- r
->left
- ex_width
;
2267 height
= r
->bottom
- r
->top
- ex_height
;
2269 w
= (width
+ term
->cols
/2)/term
->cols
;
2270 h
= (height
+ term
->rows
/2)/term
->rows
;
2271 if ( r
->right
!= r
->left
+ w
*term
->cols
+ ex_width
)
2274 if (wParam
== WMSZ_LEFT
||
2275 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2276 r
->left
= r
->right
- w
*term
->cols
- ex_width
;
2278 r
->right
= r
->left
+ w
*term
->cols
+ ex_width
;
2280 if (r
->bottom
!= r
->top
+ h
*term
->rows
+ ex_height
)
2283 if (wParam
== WMSZ_TOP
||
2284 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2285 r
->top
= r
->bottom
- h
*term
->rows
- ex_height
;
2287 r
->bottom
= r
->top
+ h
*term
->rows
+ ex_height
;
2291 /* break; (never reached) */
2292 case WM_FULLSCR_ON_MAX
:
2293 fullscr_on_max
= TRUE
;
2296 sys_cursor_update();
2299 #ifdef RDB_DEBUG_PATCH
2300 debug((27, "WM_SIZE %s (%d,%d)",
2301 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2302 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2303 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2304 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2306 LOWORD(lParam
), HIWORD(lParam
)));
2308 if (wParam
== SIZE_MINIMIZED
)
2310 cfg
.win_name_always ? window_name
: icon_name
);
2311 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2312 SetWindowText(hwnd
, window_name
);
2313 if (wParam
== SIZE_RESTORED
)
2314 clear_full_screen();
2315 if (wParam
== SIZE_MAXIMIZED
&& fullscr_on_max
) {
2316 fullscr_on_max
= FALSE
;
2320 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2321 /* A resize, well it better be a minimize. */
2325 int width
, height
, w
, h
;
2327 width
= LOWORD(lParam
);
2328 height
= HIWORD(lParam
);
2331 if (wParam
== SIZE_MAXIMIZED
&& !was_zoomed
) {
2333 prev_rows
= term
->rows
;
2334 prev_cols
= term
->cols
;
2335 if (cfg
.resize_action
== RESIZE_TERM
) {
2336 w
= width
/ font_width
;
2338 h
= height
/ font_height
;
2341 term_size(term
, h
, w
, cfg
.savelines
);
2344 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2346 if (cfg
.resize_action
== RESIZE_TERM
)
2347 term_size(term
, prev_rows
, prev_cols
, cfg
.savelines
);
2348 if (cfg
.resize_action
!= RESIZE_FONT
)
2353 /* This is an unexpected resize, these will normally happen
2354 * if the window is too large. Probably either the user
2355 * selected a huge font or the screen size has changed.
2357 * This is also called with minimize.
2359 else reset_window(-1);
2363 * Don't call back->size in mid-resize. (To prevent
2364 * massive numbers of resize events getting sent
2365 * down the connection during an NT opaque drag.)
2368 if (cfg
.resize_action
!= RESIZE_FONT
&& !is_alt_pressed()) {
2369 need_backend_resize
= TRUE
;
2370 w
= (width
-cfg
.window_border
*2) / font_width
;
2372 h
= (height
-cfg
.window_border
*2) / font_height
;
2381 sys_cursor_update();
2384 switch (LOWORD(wParam
)) {
2386 term_scroll(term
, -1, 0);
2389 term_scroll(term
, +1, 0);
2392 term_scroll(term
, 0, +1);
2395 term_scroll(term
, 0, -1);
2398 term_scroll(term
, 0, +term
->rows
/ 2);
2401 term_scroll(term
, 0, -term
->rows
/ 2);
2403 case SB_THUMBPOSITION
:
2405 term_scroll(term
, 1, HIWORD(wParam
));
2409 case WM_PALETTECHANGED
:
2410 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2411 HDC hdc
= get_ctx(NULL
);
2413 if (RealizePalette(hdc
) > 0)
2419 case WM_QUERYNEWPALETTE
:
2421 HDC hdc
= get_ctx(NULL
);
2423 if (RealizePalette(hdc
) > 0)
2435 * Add the scan code and keypress timing to the random
2438 noise_ultralight(lParam
);
2441 * We don't do TranslateMessage since it disassociates the
2442 * resulting CHAR message from the KEYDOWN that sparked it,
2443 * which we occasionally don't want. Instead, we process
2444 * KEYDOWN, and call the Win32 translator functions so that
2445 * we get the translations under _our_ control.
2448 unsigned char buf
[20];
2451 if (wParam
== VK_PROCESSKEY
) {
2454 m
.message
= WM_KEYDOWN
;
2456 m
.lParam
= lParam
& 0xdfff;
2457 TranslateMessage(&m
);
2459 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2461 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2465 * Interrupt an ongoing paste. I'm not sure
2466 * this is sensible, but for the moment it's
2467 * preferable to having to faff about buffering
2473 * We need not bother about stdin backlogs
2474 * here, because in GUI PuTTY we can't do
2475 * anything about it anyway; there's no means
2476 * of asking Windows to hold off on KEYDOWN
2477 * messages. We _have_ to buffer everything
2480 term_seen_key_event(term
);
2481 ldisc_send(ldisc
, buf
, len
, 1);
2486 net_pending_errors();
2488 case WM_INPUTLANGCHANGE
:
2489 /* wParam == Font number */
2490 /* lParam == Locale */
2491 set_input_locale((HKL
)lParam
);
2492 sys_cursor_update();
2495 if(wParam
== IMN_SETOPENSTATUS
) {
2496 HIMC hImc
= ImmGetContext(hwnd
);
2497 ImmSetCompositionFont(hImc
, &lfont
);
2498 ImmReleaseContext(hwnd
, hImc
);
2502 case WM_IME_COMPOSITION
:
2508 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2509 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2511 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2512 break; /* fall back to DefWindowProc */
2514 hIMC
= ImmGetContext(hwnd
);
2515 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2519 buff
= (char*) smalloc(n
);
2520 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2522 * Jaeyoun Chung reports that Korean character
2523 * input doesn't work correctly if we do a single
2524 * luni_send() covering the whole of buff. So
2525 * instead we luni_send the characters one by one.
2527 term_seen_key_event(term
);
2528 for (i
= 0; i
< n
; i
+= 2) {
2529 luni_send(ldisc
, (unsigned short *)(buff
+i
), 1, 1);
2533 ImmReleaseContext(hwnd
, hIMC
);
2538 if (wParam
& 0xFF00) {
2539 unsigned char buf
[2];
2542 buf
[0] = wParam
>> 8;
2543 term_seen_key_event(term
);
2544 lpage_send(ldisc
, kbd_codepage
, buf
, 2, 1);
2546 char c
= (unsigned char) wParam
;
2547 term_seen_key_event(term
);
2548 lpage_send(ldisc
, kbd_codepage
, &c
, 1, 1);
2554 * Nevertheless, we are prepared to deal with WM_CHAR
2555 * messages, should they crop up. So if someone wants to
2556 * post the things to us as part of a macro manoeuvre,
2557 * we're ready to cope.
2560 char c
= (unsigned char)wParam
;
2561 term_seen_key_event(term
);
2562 lpage_send(ldisc
, CP_ACP
, &c
, 1, 1);
2566 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2567 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2571 if (message
== wm_mousewheel
|| message
== WM_MOUSEWHEEL
) {
2572 int shift_pressed
=0, control_pressed
=0;
2574 if (message
== WM_MOUSEWHEEL
) {
2575 wheel_accumulator
+= (short)HIWORD(wParam
);
2576 shift_pressed
=LOWORD(wParam
) & MK_SHIFT
;
2577 control_pressed
=LOWORD(wParam
) & MK_CONTROL
;
2580 wheel_accumulator
+= (int)wParam
;
2581 if (GetKeyboardState(keys
)!=0) {
2582 shift_pressed
=keys
[VK_SHIFT
]&0x80;
2583 control_pressed
=keys
[VK_CONTROL
]&0x80;
2587 /* process events when the threshold is reached */
2588 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
2591 /* reduce amount for next time */
2592 if (wheel_accumulator
> 0) {
2594 wheel_accumulator
-= WHEEL_DELTA
;
2595 } else if (wheel_accumulator
< 0) {
2597 wheel_accumulator
+= WHEEL_DELTA
;
2601 if (send_raw_mouse
&&
2602 !(cfg
.mouse_override
&& shift_pressed
)) {
2603 /* send a mouse-down followed by a mouse up */
2606 TO_CHR_X(X_POS(lParam
)),
2607 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2608 control_pressed
, is_alt_pressed());
2609 term_mouse(term
, b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
2610 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2611 control_pressed
, is_alt_pressed());
2613 /* trigger a scroll */
2614 term_scroll(term
, 0,
2616 -term
->rows
/ 2 : term
->rows
/ 2);
2623 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2627 * Move the system caret. (We maintain one, even though it's
2628 * invisible, for the benefit of blind people: apparently some
2629 * helper software tracks the system caret, so we should arrange to
2632 void sys_cursor(void *frontend
, int x
, int y
)
2636 if (!term
->has_focus
) return;
2639 * Avoid gratuitously re-updating the cursor position and IMM
2640 * window if there's no actual change required.
2642 cx
= x
* font_width
+ offset_width
;
2643 cy
= y
* font_height
+ offset_height
;
2644 if (cx
== caret_x
&& cy
== caret_y
)
2649 sys_cursor_update();
2652 static void sys_cursor_update(void)
2657 if (!term
->has_focus
) return;
2659 if (caret_x
< 0 || caret_y
< 0)
2662 SetCaretPos(caret_x
, caret_y
);
2664 /* IMM calls on Win98 and beyond only */
2665 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2667 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2668 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2670 /* we should have the IMM functions */
2671 hIMC
= ImmGetContext(hwnd
);
2672 cf
.dwStyle
= CFS_POINT
;
2673 cf
.ptCurrentPos
.x
= caret_x
;
2674 cf
.ptCurrentPos
.y
= caret_y
;
2675 ImmSetCompositionWindow(hIMC
, &cf
);
2677 ImmReleaseContext(hwnd
, hIMC
);
2681 * Draw a line of text in the window, at given character
2682 * coordinates, in given attributes.
2684 * We are allowed to fiddle with the contents of `text'.
2686 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2687 unsigned long attr
, int lattr
)
2690 int nfg
, nbg
, nfont
;
2693 int force_manual_underline
= 0;
2694 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2695 int char_width
= fnt_width
;
2696 int text_adjust
= 0;
2697 static int *IpDx
= 0, IpDxLEN
= 0;
2699 if (attr
& ATTR_WIDE
)
2702 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2704 if (len
> IpDxLEN
) {
2706 IpDx
= smalloc((len
+ 16) * sizeof(int));
2707 IpDxLEN
= (len
+ 16);
2709 for (i
= 0; i
< IpDxLEN
; i
++)
2710 IpDx
[i
] = char_width
;
2713 /* Only want the left half of double width lines */
2714 if (lattr
!= LATTR_NORM
&& x
*2 >= term
->cols
)
2722 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || term
->big_cursor
)) {
2723 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2724 attr
^= ATTR_CUR_XOR
;
2728 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2729 /* Assume a poorman font is borken in other ways too. */
2739 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2742 if (attr
& ATTR_NARROW
)
2743 nfont
|= FONT_NARROW
;
2745 /* Special hack for the VT100 linedraw glyphs. */
2746 if ((attr
& CSET_MASK
) == 0x2300) {
2747 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2748 switch ((unsigned char) (text
[0])) {
2750 text_adjust
= -2 * font_height
/ 5;
2753 text_adjust
= -1 * font_height
/ 5;
2756 text_adjust
= font_height
/ 5;
2759 text_adjust
= 2 * font_height
/ 5;
2762 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2765 text
[0] = (char) (ucsdata
.unitab_xterm
['q'] & CHAR_MASK
);
2766 attr
|= (ucsdata
.unitab_xterm
['q'] & CSET_MASK
);
2767 if (attr
& ATTR_UNDER
) {
2768 attr
&= ~ATTR_UNDER
;
2769 force_manual_underline
= 1;
2774 /* Anything left as an original character set is unprintable. */
2775 if (DIRECT_CHAR(attr
)) {
2778 memset(text
, 0xFD, len
);
2782 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2785 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2786 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2787 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2789 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2790 nfont
|= FONT_UNDERLINE
;
2791 another_font(nfont
);
2792 if (!fonts
[nfont
]) {
2793 if (nfont
& FONT_UNDERLINE
)
2794 force_manual_underline
= 1;
2795 /* Don't do the same for manual bold, it could be bad news. */
2797 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2799 another_font(nfont
);
2801 nfont
= FONT_NORMAL
;
2802 if (attr
& ATTR_REVERSE
) {
2807 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2809 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2813 SelectObject(hdc
, fonts
[nfont
]);
2814 SetTextColor(hdc
, fg
);
2815 SetBkColor(hdc
, bg
);
2816 SetBkMode(hdc
, OPAQUE
);
2819 line_box
.right
= x
+ char_width
* len
;
2820 line_box
.bottom
= y
+ font_height
;
2822 /* Only want the left half of double width lines */
2823 if (line_box
.right
> font_width
*term
->cols
+offset_width
)
2824 line_box
.right
= font_width
*term
->cols
+offset_width
;
2826 /* We're using a private area for direct to font. (512 chars.) */
2827 if (ucsdata
.dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2828 /* Ho Hum, dbcs fonts are a PITA! */
2829 /* To display on W9x I have to convert to UCS */
2830 static wchar_t *uni_buf
= 0;
2831 static int uni_len
= 0;
2833 if (len
> uni_len
) {
2835 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2838 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2839 uni_buf
[nlen
] = 0xFFFD;
2840 if (IsDBCSLeadByteEx(ucsdata
.font_codepage
, (BYTE
) text
[mptr
])) {
2841 IpDx
[nlen
] += char_width
;
2842 MultiByteToWideChar(ucsdata
.font_codepage
, MB_USEGLYPHCHARS
,
2843 text
+mptr
, 2, uni_buf
+nlen
, 1);
2848 MultiByteToWideChar(ucsdata
.font_codepage
, MB_USEGLYPHCHARS
,
2849 text
+mptr
, 1, uni_buf
+nlen
, 1);
2857 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2858 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2859 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2860 SetBkMode(hdc
, TRANSPARENT
);
2861 ExtTextOutW(hdc
, x
- 1,
2862 y
- font_height
* (lattr
==
2863 LATTR_BOT
) + text_adjust
,
2864 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2868 } else if (DIRECT_FONT(attr
)) {
2870 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2871 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2872 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2873 SetBkMode(hdc
, TRANSPARENT
);
2875 /* GRR: This draws the character outside it's box and can leave
2876 * 'droppings' even with the clip box! I suppose I could loop it
2877 * one character at a time ... yuk.
2879 * Or ... I could do a test print with "W", and use +1 or -1 for this
2880 * shift depending on if the leftmost column is blank...
2882 ExtTextOut(hdc
, x
- 1,
2883 y
- font_height
* (lattr
==
2884 LATTR_BOT
) + text_adjust
,
2885 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2888 /* And 'normal' unicode characters */
2889 static WCHAR
*wbuf
= NULL
;
2890 static int wlen
= 0;
2895 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2897 for (i
= 0; i
< len
; i
++)
2898 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2901 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2902 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2904 /* And the shadow bold hack. */
2905 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2906 SetBkMode(hdc
, TRANSPARENT
);
2907 ExtTextOutW(hdc
, x
- 1,
2908 y
- font_height
* (lattr
==
2909 LATTR_BOT
) + text_adjust
,
2910 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2913 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2914 (und_mode
== UND_LINE
2915 && (attr
& ATTR_UNDER
)))) {
2918 if (lattr
== LATTR_BOT
)
2919 dec
= dec
* 2 - font_height
;
2921 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2922 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2923 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2924 oldpen
= SelectObject(hdc
, oldpen
);
2925 DeleteObject(oldpen
);
2929 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2930 unsigned long attr
, int lattr
)
2936 int ctype
= cfg
.cursor_type
;
2938 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || term
->big_cursor
)) {
2939 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2940 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2944 attr
|= TATTR_RIGHTCURS
;
2947 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2948 if (attr
& ATTR_WIDE
)
2955 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || term
->big_cursor
)) {
2958 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2959 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2960 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2961 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2962 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2963 Polyline(hdc
, pts
, 5);
2964 oldpen
= SelectObject(hdc
, oldpen
);
2965 DeleteObject(oldpen
);
2966 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2967 int startx
, starty
, dx
, dy
, length
, i
;
2970 starty
= y
+ descent
;
2973 length
= char_width
;
2976 if (attr
& TATTR_RIGHTCURS
)
2977 xadjust
= char_width
- 1;
2978 startx
= x
+ xadjust
;
2982 length
= font_height
;
2984 if (attr
& TATTR_ACTCURS
) {
2987 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2988 MoveToEx(hdc
, startx
, starty
, NULL
);
2989 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2990 oldpen
= SelectObject(hdc
, oldpen
);
2991 DeleteObject(oldpen
);
2993 for (i
= 0; i
< length
; i
++) {
2995 SetPixel(hdc
, startx
, starty
, colours
[23]);
3004 /* This function gets the actual width of a character in the normal font.
3006 int char_width(Context ctx
, int uc
) {
3010 /* If the font max is the same as the font ave width then this
3011 * function is a no-op.
3013 if (!font_dualwidth
) return 1;
3015 switch (uc
& CSET_MASK
) {
3017 uc
= ucsdata
.unitab_line
[uc
& 0xFF];
3020 uc
= ucsdata
.unitab_xterm
[uc
& 0xFF];
3023 uc
= ucsdata
.unitab_scoacs
[uc
& 0xFF];
3026 if (DIRECT_FONT(uc
)) {
3027 if (ucsdata
.dbcs_screenfont
) return 1;
3029 /* Speedup, I know of no font where ascii is the wrong width */
3030 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
3033 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
3034 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
3035 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
3036 another_font(FONT_OEM
);
3037 if (!fonts
[FONT_OEM
]) return 0;
3039 SelectObject(hdc
, fonts
[FONT_OEM
]);
3043 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
3044 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
3047 /* Speedup, I know of no font where ascii is the wrong width */
3048 if (uc
>= ' ' && uc
<= '~') return 1;
3050 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
3051 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
3052 /* Okay that one worked */ ;
3053 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
3054 /* This should work on 9x too, but it's "less accurate" */ ;
3059 ibuf
+= font_width
/ 2 -1;
3066 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
3067 * codes. Returns number of bytes used or zero to drop the message
3068 * or -1 to forward the message to windows.
3070 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
3071 unsigned char *output
)
3074 int scan
, left_alt
= 0, key_down
, shift_state
;
3076 unsigned char *p
= output
;
3077 static int alt_sum
= 0;
3079 HKL kbd_layout
= GetKeyboardLayout(0);
3081 static WORD keys
[3];
3082 static int compose_char
= 0;
3083 static WPARAM compose_key
= 0;
3085 r
= GetKeyboardState(keystate
);
3087 memset(keystate
, 0, sizeof(keystate
));
3090 #define SHOW_TOASCII_RESULT
3091 { /* Tell us all about key events */
3092 static BYTE oldstate
[256];
3093 static int first
= 1;
3097 memcpy(oldstate
, keystate
, sizeof(oldstate
));
3100 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
3102 } else if ((HIWORD(lParam
) & KF_UP
)
3103 && scan
== (HIWORD(lParam
) & 0xFF)) {
3107 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
3108 debug(("K_F%d", wParam
+ 1 - VK_F1
));
3121 debug(("VK_%02x", wParam
));
3123 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
3125 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
3127 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
3128 if (ch
>= ' ' && ch
<= '~')
3129 debug((", '%c'", ch
));
3131 debug((", $%02x", ch
));
3134 debug((", KB0=%02x", keys
[0]));
3136 debug((", KB1=%02x", keys
[1]));
3138 debug((", KB2=%02x", keys
[2]));
3140 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
3142 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
3144 if ((HIWORD(lParam
) & KF_EXTENDED
))
3146 if ((HIWORD(lParam
) & KF_UP
))
3150 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
3151 else if ((HIWORD(lParam
) & KF_UP
))
3152 oldstate
[wParam
& 0xFF] ^= 0x80;
3154 oldstate
[wParam
& 0xFF] ^= 0x81;
3156 for (ch
= 0; ch
< 256; ch
++)
3157 if (oldstate
[ch
] != keystate
[ch
])
3158 debug((", M%02x=%02x", ch
, keystate
[ch
]));
3160 memcpy(oldstate
, keystate
, sizeof(oldstate
));
3164 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
3165 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
3169 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
3170 if ((cfg
.funky_type
== 3 ||
3171 (cfg
.funky_type
<= 1 && term
->app_keypad_keys
&&
3173 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
3175 wParam
= VK_EXECUTE
;
3177 /* UnToggle NUMLock */
3178 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
3179 keystate
[VK_NUMLOCK
] ^= 1;
3182 /* And write back the 'adjusted' state */
3183 SetKeyboardState(keystate
);
3186 /* Disable Auto repeat if required */
3187 if (term
->repeat_off
&&
3188 (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
3191 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
3194 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
3196 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
3197 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
3198 if (cfg
.ctrlaltkeys
)
3199 keystate
[VK_MENU
] = 0;
3201 keystate
[VK_RMENU
] = 0x80;
3206 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
3207 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
3208 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
3210 /* Note if AltGr was pressed and if it was used as a compose key */
3211 if (!compose_state
) {
3212 compose_key
= 0x100;
3213 if (cfg
.compose_key
) {
3214 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
3215 compose_key
= wParam
;
3217 if (wParam
== VK_APPS
)
3218 compose_key
= wParam
;
3221 if (wParam
== compose_key
) {
3222 if (compose_state
== 0
3223 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
3225 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
3229 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
3232 if (compose_state
> 1 && left_alt
)
3235 /* Sanitize the number pad if not using a PC NumPad */
3236 if (left_alt
|| (term
->app_keypad_keys
&& !cfg
.no_applic_k
3237 && cfg
.funky_type
!= 2)
3238 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
3239 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
3243 nParam
= VK_NUMPAD0
;
3246 nParam
= VK_NUMPAD1
;
3249 nParam
= VK_NUMPAD2
;
3252 nParam
= VK_NUMPAD3
;
3255 nParam
= VK_NUMPAD4
;
3258 nParam
= VK_NUMPAD5
;
3261 nParam
= VK_NUMPAD6
;
3264 nParam
= VK_NUMPAD7
;
3267 nParam
= VK_NUMPAD8
;
3270 nParam
= VK_NUMPAD9
;
3273 nParam
= VK_DECIMAL
;
3277 if (keystate
[VK_NUMLOCK
] & 1)
3284 /* If a key is pressed and AltGr is not active */
3285 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
3286 /* Okay, prepare for most alts then ... */
3290 /* Lets see if it's a pattern we know all about ... */
3291 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
3292 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
3295 if (wParam
== VK_PRIOR
&& shift_state
== 2) {
3296 SendMessage(hwnd
, WM_VSCROLL
, SB_LINEUP
, 0);
3299 if (wParam
== VK_NEXT
&& shift_state
== 1) {
3300 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
3303 if (wParam
== VK_NEXT
&& shift_state
== 2) {
3304 SendMessage(hwnd
, WM_VSCROLL
, SB_LINEDOWN
, 0);
3307 if (wParam
== VK_INSERT
&& shift_state
== 1) {
3308 term_do_paste(term
);
3311 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
3314 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
3315 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
3318 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
&&
3319 (cfg
.resize_action
!= RESIZE_DISABLED
)) {
3320 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) != KF_REPEAT
)
3324 /* Control-Numlock for app-keypad mode switch */
3325 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
3326 term
->app_keypad_keys
^= 1;
3330 /* Nethack keypad */
3331 if (cfg
.nethack_keypad
&& !left_alt
) {
3334 *p
++ = shift_state ?
'B' : 'b';
3337 *p
++ = shift_state ?
'J' : 'j';
3340 *p
++ = shift_state ?
'N' : 'n';
3343 *p
++ = shift_state ?
'H' : 'h';
3346 *p
++ = shift_state ?
'.' : '.';
3349 *p
++ = shift_state ?
'L' : 'l';
3352 *p
++ = shift_state ?
'Y' : 'y';
3355 *p
++ = shift_state ?
'K' : 'k';
3358 *p
++ = shift_state ?
'U' : 'u';
3363 /* Application Keypad */
3367 if (cfg
.funky_type
== 3 ||
3368 (cfg
.funky_type
<= 1 &&
3369 term
->app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3383 if (term
->app_keypad_keys
&& !cfg
.no_applic_k
)
3420 if (cfg
.funky_type
== 2) {
3425 } else if (shift_state
)
3432 if (cfg
.funky_type
== 2)
3436 if (cfg
.funky_type
== 2)
3440 if (cfg
.funky_type
== 2)
3445 if (HIWORD(lParam
) & KF_EXTENDED
)
3450 if (term
->vt52_mode
) {
3451 if (xkey
>= 'P' && xkey
<= 'S')
3452 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3454 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3456 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3461 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3462 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3466 if (wParam
== VK_BACK
&& shift_state
== 1) { /* Shift Backspace */
3467 /* We do the opposite of what is configured */
3468 *p
++ = (cfg
.bksp_is_delete ?
0x08 : 0x7F);
3472 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3478 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3482 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3486 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3491 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3496 /* Control-2 to Control-8 are special */
3497 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3498 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3501 if (shift_state
== 2 && (wParam
== 0xBD || wParam
== 0xBF)) {
3505 if (shift_state
== 2 && wParam
== 0xDF) {
3509 if (shift_state
== 0 && wParam
== VK_RETURN
&& term
->cr_lf_return
) {
3516 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3517 * for integer decimal nn.)
3519 * We also deal with the weird ones here. Linux VCs replace F1
3520 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3521 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3527 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3530 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3533 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3536 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3539 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3542 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3545 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3548 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3551 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3554 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3587 if ((shift_state
&2) == 0) switch (wParam
) {
3607 /* Reorder edit keys to physical order */
3608 if (cfg
.funky_type
== 3 && code
<= 6)
3609 code
= "\0\2\1\4\5\3\6"[code
];
3611 if (term
->vt52_mode
&& code
> 0 && code
<= 6) {
3612 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3616 if (cfg
.funky_type
== 5 && /* SCO function keys */
3617 code
>= 11 && code
<= 34) {
3618 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3621 case VK_F1
: index
= 0; break;
3622 case VK_F2
: index
= 1; break;
3623 case VK_F3
: index
= 2; break;
3624 case VK_F4
: index
= 3; break;
3625 case VK_F5
: index
= 4; break;
3626 case VK_F6
: index
= 5; break;
3627 case VK_F7
: index
= 6; break;
3628 case VK_F8
: index
= 7; break;
3629 case VK_F9
: index
= 8; break;
3630 case VK_F10
: index
= 9; break;
3631 case VK_F11
: index
= 10; break;
3632 case VK_F12
: index
= 11; break;
3634 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3635 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3636 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3639 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3640 code
>= 1 && code
<= 6) {
3641 char codes
[] = "HL.FIG";
3645 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3649 if ((term
->vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3655 if (term
->vt52_mode
)
3656 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3659 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3662 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3663 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3666 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3667 if (term
->vt52_mode
)
3668 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3670 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3673 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3674 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3678 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3683 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3684 * some reason seems to send VK_CLEAR to Windows...).
3706 if (term
->vt52_mode
)
3707 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3709 int app_flg
= (term
->app_cursor_keys
&& !cfg
.no_applic_c
);
3712 * RDB: VT100 & VT102 manuals both state the
3713 * app cursor keys only work if the app keypad
3716 * SGT: That may well be true, but xterm
3717 * disagrees and so does at least one
3718 * application, so I've #if'ed this out and the
3719 * behaviour is back to PuTTY's original: app
3720 * cursor and app keypad are independently
3721 * switchable modes. If anyone complains about
3722 * _this_ I'll have to put in a configurable
3725 if (!term
->app_keypad_keys
)
3728 /* Useful mapping of Ctrl-arrows */
3729 if (shift_state
== 2)
3733 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3735 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3742 * Finally, deal with Return ourselves. (Win95 seems to
3743 * foul it up when Alt is pressed, for some reason.)
3745 if (wParam
== VK_RETURN
) { /* Return */
3751 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3752 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3757 /* Okay we've done everything interesting; let windows deal with
3758 * the boring stuff */
3762 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3763 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3765 keystate
[VK_CAPITAL
] = 0;
3768 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3769 #ifdef SHOW_TOASCII_RESULT
3770 if (r
== 1 && !key_down
) {
3772 if (in_utf(term
) || ucsdata
.dbcs_screenfont
)
3773 debug((", (U+%04x)", alt_sum
));
3775 debug((", LCH(%d)", alt_sum
));
3777 debug((", ACH(%d)", keys
[0]));
3782 for (r1
= 0; r1
< r
; r1
++) {
3783 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3792 * Interrupt an ongoing paste. I'm not sure this is
3793 * sensible, but for the moment it's preferable to
3794 * having to faff about buffering things.
3799 for (i
= 0; i
< r
; i
++) {
3800 unsigned char ch
= (unsigned char) keys
[i
];
3802 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3807 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3811 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3812 MessageBeep(MB_ICONHAND
);
3816 term_seen_key_event(term
);
3817 luni_send(ldisc
, &keybuf
, 1, 1);
3825 if (in_utf(term
) || ucsdata
.dbcs_screenfont
) {
3827 term_seen_key_event(term
);
3828 luni_send(ldisc
, &keybuf
, 1, 1);
3830 ch
= (char) alt_sum
;
3832 * We need not bother about stdin
3833 * backlogs here, because in GUI PuTTY
3834 * we can't do anything about it
3835 * anyway; there's no means of asking
3836 * Windows to hold off on KEYDOWN
3837 * messages. We _have_ to buffer
3838 * everything we're sent.
3840 term_seen_key_event(term
);
3841 ldisc_send(ldisc
, &ch
, 1, 1);
3845 term_seen_key_event(term
);
3846 lpage_send(ldisc
, kbd_codepage
, &ch
, 1, 1);
3848 if(capsOn
&& ch
< 0x80) {
3851 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3852 term_seen_key_event(term
);
3853 luni_send(ldisc
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3858 term_seen_key_event(term
);
3859 lpage_send(ldisc
, kbd_codepage
,
3860 cbuf
+!left_alt
, 1+!!left_alt
, 1);
3866 /* This is so the ALT-Numpad and dead keys work correctly. */
3871 /* If we're definitly not building up an ALT-54321 then clear it */
3874 /* If we will be using alt_sum fix the 256s */
3875 else if (keys
[0] && (in_utf(term
) || ucsdata
.dbcs_screenfont
))
3880 * ALT alone may or may not want to bring up the System menu.
3881 * If it's not meant to, we return 0 on presses or releases of
3882 * ALT, to show that we've swallowed the keystroke. Otherwise
3883 * we return -1, which means Windows will give the keystroke
3884 * its default handling (i.e. bring up the System menu).
3886 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3892 void request_paste(void *frontend
)
3895 * In Windows, pasting is synchronous: we can read the
3896 * clipboard with no difficulty, so request_paste() can just go
3899 term_do_paste(term
);
3902 void set_title(void *frontend
, char *title
)
3905 window_name
= smalloc(1 + strlen(title
));
3906 strcpy(window_name
, title
);
3907 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3908 SetWindowText(hwnd
, title
);
3911 void set_icon(void *frontend
, char *title
)
3914 icon_name
= smalloc(1 + strlen(title
));
3915 strcpy(icon_name
, title
);
3916 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3917 SetWindowText(hwnd
, title
);
3920 void set_sbar(void *frontend
, int total
, int start
, int page
)
3924 if (is_full_screen() ?
!cfg
.scrollbar_in_fullscreen
: !cfg
.scrollbar
)
3927 si
.cbSize
= sizeof(si
);
3928 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3930 si
.nMax
= total
- 1;
3934 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3937 Context
get_ctx(void *frontend
)
3943 SelectPalette(hdc
, pal
, FALSE
);
3949 void free_ctx(Context ctx
)
3951 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3952 ReleaseDC(hwnd
, ctx
);
3955 static void real_palette_set(int n
, int r
, int g
, int b
)
3958 logpal
->palPalEntry
[n
].peRed
= r
;
3959 logpal
->palPalEntry
[n
].peGreen
= g
;
3960 logpal
->palPalEntry
[n
].peBlue
= b
;
3961 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3962 colours
[n
] = PALETTERGB(r
, g
, b
);
3963 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3965 colours
[n
] = RGB(r
, g
, b
);
3968 void palette_set(void *frontend
, int n
, int r
, int g
, int b
)
3970 static const int first
[21] = {
3971 0, 2, 4, 6, 8, 10, 12, 14,
3972 1, 3, 5, 7, 9, 11, 13, 15,
3975 real_palette_set(first
[n
], r
, g
, b
);
3977 real_palette_set(first
[n
] + 1, r
, g
, b
);
3979 HDC hdc
= get_ctx(frontend
);
3980 UnrealizeObject(pal
);
3981 RealizePalette(hdc
);
3986 void palette_reset(void *frontend
)
3990 for (i
= 0; i
< NCOLOURS
; i
++) {
3992 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3993 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3994 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3995 logpal
->palPalEntry
[i
].peFlags
= 0;
3996 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3997 defpal
[i
].rgbtGreen
,
3998 defpal
[i
].rgbtBlue
);
4000 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
4001 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
4006 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
4007 hdc
= get_ctx(frontend
);
4008 RealizePalette(hdc
);
4013 void write_aclip(void *frontend
, char *data
, int len
, int must_deselect
)
4018 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
4021 lock
= GlobalLock(clipdata
);
4024 memcpy(lock
, data
, len
);
4025 ((unsigned char *) lock
)[len
] = 0;
4026 GlobalUnlock(clipdata
);
4029 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
4031 if (OpenClipboard(hwnd
)) {
4033 SetClipboardData(CF_TEXT
, clipdata
);
4036 GlobalFree(clipdata
);
4039 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
4043 * Note: unlike write_aclip() this will not append a nul.
4045 void write_clip(void *frontend
, wchar_t * data
, int len
, int must_deselect
)
4047 HGLOBAL clipdata
, clipdata2
, clipdata3
;
4049 void *lock
, *lock2
, *lock3
;
4051 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
4053 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
4054 len
* sizeof(wchar_t));
4055 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
4057 if (!clipdata
|| !clipdata2
) {
4059 GlobalFree(clipdata
);
4061 GlobalFree(clipdata2
);
4064 if (!(lock
= GlobalLock(clipdata
)))
4066 if (!(lock2
= GlobalLock(clipdata2
)))
4069 memcpy(lock
, data
, len
* sizeof(wchar_t));
4070 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
4072 if (cfg
.rtf_paste
) {
4073 wchar_t unitab
[256];
4075 unsigned char *tdata
= (unsigned char *)lock2
;
4076 wchar_t *udata
= (wchar_t *)lock
;
4077 int rtflen
= 0, uindex
= 0, tindex
= 0;
4079 int multilen
, blen
, alen
, totallen
, i
;
4080 char before
[16], after
[4];
4082 get_unitab(CP_ACP
, unitab
, 0);
4084 rtfsize
= 100 + strlen(cfg
.font
);
4085 rtf
= smalloc(rtfsize
);
4086 sprintf(rtf
, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
4087 GetACP(), cfg
.font
);
4088 rtflen
= strlen(rtf
);
4091 * We want to construct a piece of RTF that specifies the
4092 * same Unicode text. To do this we will read back in
4093 * parallel from the Unicode data in `udata' and the
4094 * non-Unicode data in `tdata'. For each character in
4095 * `tdata' which becomes the right thing in `udata' when
4096 * looked up in `unitab', we just copy straight over from
4097 * tdata. For each one that doesn't, we must WCToMB it
4098 * individually and produce a \u escape sequence.
4100 * It would probably be more robust to just bite the bullet
4101 * and WCToMB each individual Unicode character one by one,
4102 * then MBToWC each one back to see if it was an accurate
4103 * translation; but that strikes me as a horrifying number
4104 * of Windows API calls so I want to see if this faster way
4105 * will work. If it screws up badly we can always revert to
4106 * the simple and slow way.
4108 while (tindex
< len2
&& uindex
< len
&&
4109 tdata
[tindex
] && udata
[uindex
]) {
4110 if (tindex
+ 1 < len2
&&
4111 tdata
[tindex
] == '\r' &&
4112 tdata
[tindex
+1] == '\n') {
4116 if (unitab
[tdata
[tindex
]] == udata
[uindex
]) {
4122 multilen
= WideCharToMultiByte(CP_ACP
, 0, unitab
+uindex
, 1,
4123 NULL
, 0, NULL
, NULL
);
4124 if (multilen
!= 1) {
4125 blen
= sprintf(before
, "{\\uc%d\\u%d", multilen
,
4127 alen
= 1; strcpy(after
, "}");
4129 blen
= sprintf(before
, "\\u%d", udata
[uindex
]);
4130 alen
= 0; after
[0] = '\0';
4133 assert(tindex
+ multilen
<= len2
);
4134 totallen
= blen
+ alen
;
4135 for (i
= 0; i
< multilen
; i
++) {
4136 if (tdata
[tindex
+i
] == '\\' ||
4137 tdata
[tindex
+i
] == '{' ||
4138 tdata
[tindex
+i
] == '}')
4140 else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A)
4141 totallen
+= 6; /* \par\r\n */
4142 else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20)
4148 if (rtfsize
< rtflen
+ totallen
+ 3) {
4149 rtfsize
= rtflen
+ totallen
+ 512;
4150 rtf
= srealloc(rtf
, rtfsize
);
4153 strcpy(rtf
+ rtflen
, before
); rtflen
+= blen
;
4154 for (i
= 0; i
< multilen
; i
++) {
4155 if (tdata
[tindex
+i
] == '\\' ||
4156 tdata
[tindex
+i
] == '{' ||
4157 tdata
[tindex
+i
] == '}') {
4158 rtf
[rtflen
++] = '\\';
4159 rtf
[rtflen
++] = tdata
[tindex
+i
];
4160 } else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A) {
4161 rtflen
+= sprintf(rtf
+rtflen
, "\\par\r\n");
4162 } else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20) {
4163 rtflen
+= sprintf(rtf
+rtflen
, "\\'%02x", tdata
[tindex
+i
]);
4165 rtf
[rtflen
++] = tdata
[tindex
+i
];
4168 strcpy(rtf
+ rtflen
, after
); rtflen
+= alen
;
4174 strcpy(rtf
+ rtflen
, "}");
4177 clipdata3
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, rtflen
);
4178 if (clipdata3
&& (lock3
= GlobalLock(clipdata3
)) != NULL
) {
4180 GlobalUnlock(clipdata3
);
4186 GlobalUnlock(clipdata
);
4187 GlobalUnlock(clipdata2
);
4190 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
4192 if (OpenClipboard(hwnd
)) {
4194 SetClipboardData(CF_UNICODETEXT
, clipdata
);
4195 SetClipboardData(CF_TEXT
, clipdata2
);
4197 SetClipboardData(RegisterClipboardFormat(CF_RTF
), clipdata3
);
4200 GlobalFree(clipdata
);
4201 GlobalFree(clipdata2
);
4205 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
4208 void get_clip(void *frontend
, wchar_t ** p
, int *len
)
4210 static HGLOBAL clipdata
= NULL
;
4211 static wchar_t *converted
= 0;
4220 GlobalUnlock(clipdata
);
4223 } else if (OpenClipboard(NULL
)) {
4224 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
4226 *p
= GlobalLock(clipdata
);
4228 for (p2
= *p
; *p2
; p2
++);
4232 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
4236 s
= GlobalLock(clipdata
);
4237 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
4238 *p
= converted
= smalloc(i
* sizeof(wchar_t));
4239 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
4252 * Move `lines' lines from position `from' to position `to' in the
4255 void optimised_move(void *frontend
, int to
, int from
, int lines
)
4260 min
= (to
< from ? to
: from
);
4261 max
= to
+ from
- min
;
4263 r
.left
= offset_width
;
4264 r
.right
= offset_width
+ term
->cols
* font_width
;
4265 r
.top
= offset_height
+ min
* font_height
;
4266 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
4267 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
4272 * Print a message box and perform a fatal exit.
4274 void fatalbox(char *fmt
, ...)
4280 vsprintf(stuff
, fmt
, ap
);
4282 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
4287 * Print a modal (Really Bad) message box and perform a fatal exit.
4289 void modalfatalbox(char *fmt
, ...)
4295 vsprintf(stuff
, fmt
, ap
);
4297 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error",
4298 MB_SYSTEMMODAL
| MB_ICONERROR
| MB_OK
);
4303 * Manage window caption / taskbar flashing, if enabled.
4304 * 0 = stop, 1 = maintain, 2 = start
4306 static void flash_window(int mode
)
4308 static long last_flash
= 0;
4309 static int flashing
= 0;
4310 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
4313 FlashWindow(hwnd
, FALSE
);
4317 } else if (mode
== 2) {
4320 last_flash
= GetTickCount();
4322 FlashWindow(hwnd
, TRUE
);
4325 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
4328 long now
= GetTickCount();
4329 long fdiff
= now
- last_flash
;
4330 if (fdiff
< 0 || fdiff
> 450) {
4332 FlashWindow(hwnd
, TRUE
); /* toggle */
4341 void beep(void *frontend
, int mode
)
4343 if (mode
== BELL_DEFAULT
) {
4345 * For MessageBeep style bells, we want to be careful of
4346 * timing, because they don't have the nice property of
4347 * PlaySound bells that each one cancels the previous
4348 * active one. So we limit the rate to one per 50ms or so.
4350 static long lastbeep
= 0;
4353 beepdiff
= GetTickCount() - lastbeep
;
4354 if (beepdiff
>= 0 && beepdiff
< 50)
4358 * The above MessageBeep call takes time, so we record the
4359 * time _after_ it finishes rather than before it starts.
4361 lastbeep
= GetTickCount();
4362 } else if (mode
== BELL_WAVEFILE
) {
4363 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
4364 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
4365 sprintf(buf
, "Unable to play sound file\n%s\n"
4366 "Using default sound instead", cfg
.bell_wavefile
);
4367 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
4368 MB_OK
| MB_ICONEXCLAMATION
);
4369 cfg
.beep
= BELL_DEFAULT
;
4372 /* Otherwise, either visual bell or disabled; do nothing here */
4373 if (!term
->has_focus
) {
4374 flash_window(2); /* start */
4379 * Minimise or restore the window in response to a server-side
4382 void set_iconic(void *frontend
, int iconic
)
4384 if (IsIconic(hwnd
)) {
4386 ShowWindow(hwnd
, SW_RESTORE
);
4389 ShowWindow(hwnd
, SW_MINIMIZE
);
4394 * Move the window in response to a server-side request.
4396 void move_window(void *frontend
, int x
, int y
)
4398 if (cfg
.resize_action
== RESIZE_DISABLED
||
4399 cfg
.resize_action
== RESIZE_FONT
||
4403 SetWindowPos(hwnd
, NULL
, x
, y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
);
4407 * Move the window to the top or bottom of the z-order in response
4408 * to a server-side request.
4410 void set_zorder(void *frontend
, int top
)
4412 if (cfg
.alwaysontop
)
4413 return; /* ignore */
4414 SetWindowPos(hwnd
, top ? HWND_TOP
: HWND_BOTTOM
, 0, 0, 0, 0,
4415 SWP_NOMOVE
| SWP_NOSIZE
);
4419 * Refresh the window in response to a server-side request.
4421 void refresh_window(void *frontend
)
4423 InvalidateRect(hwnd
, NULL
, TRUE
);
4427 * Maximise or restore the window in response to a server-side
4430 void set_zoomed(void *frontend
, int zoomed
)
4432 if (IsZoomed(hwnd
)) {
4434 ShowWindow(hwnd
, SW_RESTORE
);
4437 ShowWindow(hwnd
, SW_MAXIMIZE
);
4442 * Report whether the window is iconic, for terminal reports.
4444 int is_iconic(void *frontend
)
4446 return IsIconic(hwnd
);
4450 * Report the window's position, for terminal reports.
4452 void get_window_pos(void *frontend
, int *x
, int *y
)
4455 GetWindowRect(hwnd
, &r
);
4461 * Report the window's pixel size, for terminal reports.
4463 void get_window_pixels(void *frontend
, int *x
, int *y
)
4466 GetWindowRect(hwnd
, &r
);
4467 *x
= r
.right
- r
.left
;
4468 *y
= r
.bottom
- r
.top
;
4472 * Return the window or icon title.
4474 char *get_window_title(void *frontend
, int icon
)
4476 return icon ? icon_name
: window_name
;
4480 * See if we're in full-screen mode.
4482 int is_full_screen()
4484 if (!IsZoomed(hwnd
))
4486 if (GetWindowLong(hwnd
, GWL_STYLE
) & WS_CAPTION
)
4491 /* Get the rect/size of a full screen window using the nearest available
4492 * monitor in multimon systems; default to something sensible if only
4493 * one monitor is present. */
4494 static int get_fullscreen_rect(RECT
* ss
)
4496 #ifdef MONITOR_DEFAULTTONEAREST
4499 mon
= MonitorFromWindow(hwnd
, MONITOR_DEFAULTTONEAREST
);
4500 mi
.cbSize
= sizeof(mi
);
4501 GetMonitorInfo(mon
, &mi
);
4503 /* structure copy */
4507 /* could also use code like this:
4508 ss->left = ss->top = 0;
4509 ss->right = GetSystemMetrics(SM_CXSCREEN);
4510 ss->bottom = GetSystemMetrics(SM_CYSCREEN);
4512 return GetClientRect(GetDesktopWindow(), ss
);
4518 * Go full-screen. This should only be called when we are already
4521 void make_full_screen()
4526 assert(IsZoomed(hwnd
));
4528 if (is_full_screen())
4531 /* Remove the window furniture. */
4532 style
= GetWindowLong(hwnd
, GWL_STYLE
);
4533 style
&= ~(WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
);
4534 if (cfg
.scrollbar_in_fullscreen
)
4535 style
|= WS_VSCROLL
;
4537 style
&= ~WS_VSCROLL
;
4538 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4540 /* Resize ourselves to exactly cover the nearest monitor. */
4541 get_fullscreen_rect(&ss
);
4542 SetWindowPos(hwnd
, HWND_TOP
, ss
.left
, ss
.top
,
4547 /* Tick the menu item in the System menu. */
4548 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4553 * Clear the full-screen attributes.
4555 void clear_full_screen()
4557 DWORD oldstyle
, style
;
4559 /* Reinstate the window furniture. */
4560 style
= oldstyle
= GetWindowLong(hwnd
, GWL_STYLE
);
4561 style
|= WS_CAPTION
| WS_BORDER
;
4562 if (cfg
.resize_action
== RESIZE_DISABLED
)
4563 style
&= ~WS_THICKFRAME
;
4565 style
|= WS_THICKFRAME
;
4567 style
|= WS_VSCROLL
;
4569 style
&= ~WS_VSCROLL
;
4570 if (style
!= oldstyle
) {
4571 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4572 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
4573 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
4577 /* Untick the menu item in the System menu. */
4578 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4583 * Toggle full-screen mode.
4585 void flip_full_screen()
4587 if (is_full_screen()) {
4588 ShowWindow(hwnd
, SW_RESTORE
);
4589 } else if (IsZoomed(hwnd
)) {
4592 SendMessage(hwnd
, WM_FULLSCR_ON_MAX
, 0, 0);
4593 ShowWindow(hwnd
, SW_MAXIMIZE
);
4597 void frontend_keypress(void *handle
)
4600 * Keypress termination in non-Close-On-Exit mode is not
4601 * currently supported in PuTTY proper, because the window
4602 * always has a perfectly good Close button anyway. So we do