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 Mouse_Button
translate_button(Mouse_Button button
);
82 static LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
83 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
84 unsigned char *output
);
85 static void cfgtopalette(void);
86 static void init_palette(void);
87 static void init_fonts(int, int);
88 static void another_font(int);
89 static void deinit_fonts(void);
90 static void set_input_locale(HKL
);
92 static int is_full_screen(void);
93 static void make_full_screen(void);
94 static void clear_full_screen(void);
95 static void flip_full_screen(void);
97 /* Window layout information */
98 static void reset_window(int);
99 static int extra_width
, extra_height
;
100 static int font_width
, font_height
, font_dualwidth
;
101 static int offset_width
, offset_height
;
102 static int was_zoomed
= 0;
103 static int prev_rows
, prev_cols
;
105 static int pending_netevent
= 0;
106 static WPARAM pend_netevent_wParam
= 0;
107 static LPARAM pend_netevent_lParam
= 0;
108 static void enact_pending_netevent(void);
109 static void flash_window(int mode
);
110 static void sys_cursor_update(void);
111 static int is_shift_pressed(void);
112 static int get_fullscreen_rect(RECT
* ss
);
114 static time_t last_movement
= 0;
116 static int caret_x
= -1, caret_y
= -1;
118 static int kbd_codepage
;
121 static Backend
*back
;
122 static void *backhandle
;
124 static struct unicode_data ucsdata
;
125 static int session_closed
;
127 Config cfg
; /* exported to windlg.c */
129 extern struct sesslist sesslist
; /* imported from windlg.c */
131 #define FONT_NORMAL 0
133 #define FONT_UNDERLINE 2
134 #define FONT_BOLDUND 3
135 #define FONT_WIDE 0x04
136 #define FONT_HIGH 0x08
137 #define FONT_NARROW 0x10
139 #define FONT_OEM 0x20
140 #define FONT_OEMBOLD 0x21
141 #define FONT_OEMUND 0x22
142 #define FONT_OEMBOLDUND 0x23
144 #define FONT_MAXNO 0x2F
146 static HFONT fonts
[FONT_MAXNO
];
147 static LOGFONT lfont
;
148 static int fontflag
[FONT_MAXNO
];
150 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
158 static COLORREF colours
[NCOLOURS
];
160 static LPLOGPALETTE logpal
;
161 static RGBTRIPLE defpal
[NCOLOURS
];
165 static HBITMAP caretbm
;
167 static int dbltime
, lasttime
, lastact
;
168 static Mouse_Button lastbtn
;
170 /* this allows xterm-style mouse handling. */
171 static int send_raw_mouse
= 0;
172 static int wheel_accumulator
= 0;
174 static char *window_name
, *icon_name
;
176 static int compose_state
= 0;
178 static int wsa_started
= 0;
180 static OSVERSIONINFO osVersion
;
182 static UINT wm_mousewheel
= WM_MOUSEWHEEL
;
184 /* Dummy routine, only required in plink. */
185 void ldisc_update(void *frontend
, int echo
, int edit
)
189 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
191 static char appname
[] = "PuTTY";
196 int guess_width
, guess_height
;
199 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
201 winsock_ver
= MAKEWORD(1, 1);
202 if (WSAStartup(winsock_ver
, &wsadata
)) {
203 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
204 MB_OK
| MB_ICONEXCLAMATION
);
207 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
208 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
209 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
214 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
217 InitCommonControls();
219 /* Ensure a Maximize setting in Explorer doesn't maximise the
224 ZeroMemory(&osVersion
, sizeof(osVersion
));
225 osVersion
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
226 if (!GetVersionEx ( (OSVERSIONINFO
*) &osVersion
)) {
227 MessageBox(NULL
, "Windows refuses to report a version",
228 "PuTTY Fatal Error", MB_OK
| MB_ICONEXCLAMATION
);
234 * If we're running a version of Windows that doesn't support
235 * WM_MOUSEWHEEL, find out what message number we should be
238 if (osVersion
.dwMajorVersion
< 4 ||
239 (osVersion
.dwMajorVersion
== 4 &&
240 osVersion
.dwPlatformId
!= VER_PLATFORM_WIN32_NT
))
241 wm_mousewheel
= RegisterWindowMessage("MSWHEEL_ROLLMSG");
244 * See if we can find our Help file.
247 char b
[2048], *p
, *q
, *r
;
249 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
251 p
= strrchr(b
, '\\');
252 if (p
&& p
>= r
) r
= p
+1;
254 if (q
&& q
>= r
) r
= q
+1;
255 strcpy(r
, "putty.hlp");
256 if ( (fp
= fopen(b
, "r")) != NULL
) {
257 help_path
= dupstr(b
);
261 strcpy(r
, "putty.cnt");
262 if ( (fp
= fopen(b
, "r")) != NULL
) {
263 help_has_contents
= TRUE
;
266 help_has_contents
= FALSE
;
270 * Process the command line.
276 default_protocol
= be_default_protocol
;
277 /* Find the appropriate default port. */
280 default_port
= 0; /* illegal */
281 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
282 if (backends
[i
].protocol
== default_protocol
) {
283 default_port
= backends
[i
].backend
->default_port
;
287 cfg
.logtype
= LGTYP_NONE
;
289 do_defaults(NULL
, &cfg
);
294 * Process a couple of command-line options which are more
295 * easily dealt with before the line is broken up into
296 * words. These are the soon-to-be-defunct @sessionname and
297 * the internal-use-only &sharedmemoryhandle, neither of
298 * which are combined with anything else.
300 while (*p
&& isspace(*p
))
304 while (i
> 1 && isspace(p
[i
- 1]))
307 do_defaults(p
+ 1, &cfg
);
308 if (!*cfg
.host
&& !do_config()) {
312 } else if (*p
== '&') {
314 * An initial & means we've been given a command line
315 * containing the hex value of a HANDLE for a file
316 * mapping object, which we must then extract as a
321 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
322 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
323 0, 0, sizeof(Config
))) != NULL
) {
326 CloseHandle(filemap
);
327 } else if (!do_config()) {
333 * Otherwise, break up the command line and deal with
339 split_into_argv(cmdline
, &argc
, &argv
, NULL
);
341 for (i
= 0; i
< argc
; i
++) {
345 ret
= cmdline_process_param(p
, i
+1<argc?argv
[i
+1]:NULL
,
348 cmdline_error("option \"%s\" requires an argument", p
);
349 } else if (ret
== 2) {
350 i
++; /* skip next argument */
351 } else if (ret
== 1) {
352 continue; /* nothing further needs doing */
353 } else if (!strcmp(p
, "-cleanup")) {
355 * `putty -cleanup'. Remove all registry
356 * entries associated with PuTTY, and also find
357 * and delete the random seed file.
360 "This procedure will remove ALL Registry\n"
361 "entries associated with PuTTY, and will\n"
362 "also remove the PuTTY random seed file.\n"
364 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
365 "SESSIONS. Are you really sure you want\n"
368 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
372 } else if (*p
!= '-') {
376 * If we already have a host name, treat
377 * this argument as a port number. NB we
378 * have to treat this as a saved -P
379 * argument, so that it will be deferred
380 * until it's a good moment to run it.
382 int ret
= cmdline_process_param("-P", p
, 1, &cfg
);
384 } else if (!strncmp(q
, "telnet:", 7)) {
386 * If the hostname starts with "telnet:",
387 * set the protocol to Telnet and process
388 * the string as a Telnet URL.
393 if (q
[0] == '/' && q
[1] == '/')
395 cfg
.protocol
= PROT_TELNET
;
397 while (*p
&& *p
!= ':' && *p
!= '/')
406 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
407 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
411 * Otherwise, treat this argument as a host
414 while (*p
&& !isspace(*p
))
418 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
419 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
423 cmdline_error("unknown option \"%s\"", p
);
428 cmdline_run_saved(&cfg
);
430 if (!*cfg
.host
&& !do_config()) {
436 * Trim leading whitespace off the hostname if it's there.
439 int space
= strspn(cfg
.host
, " \t");
440 memmove(cfg
.host
, cfg
.host
+space
, 1+strlen(cfg
.host
)-space
);
443 /* See if host is of the form user@host */
444 if (cfg
.host
[0] != '\0') {
445 char *atsign
= strchr(cfg
.host
, '@');
446 /* Make sure we're not overflowing the user field */
448 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
449 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
450 cfg
.username
[atsign
- cfg
.host
] = '\0';
452 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
457 * Trim a colon suffix off the hostname if it's there.
459 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
462 * Remove any remaining whitespace from the hostname.
466 while (cfg
.host
[p2
] != '\0') {
467 if (cfg
.host
[p2
] != ' ' && cfg
.host
[p2
] != '\t') {
468 cfg
.host
[p1
] = cfg
.host
[p2
];
478 * Select protocol. This is farmed out into a table in a
479 * separate file to enable an ssh-free variant.
484 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
485 if (backends
[i
].protocol
== cfg
.protocol
) {
486 back
= backends
[i
].backend
;
490 MessageBox(NULL
, "Unsupported protocol number found",
491 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
497 /* Check for invalid Port number (i.e. zero) */
499 MessageBox(NULL
, "Invalid Port Number",
500 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
507 wndclass
.lpfnWndProc
= WndProc
;
508 wndclass
.cbClsExtra
= 0;
509 wndclass
.cbWndExtra
= 0;
510 wndclass
.hInstance
= inst
;
511 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
512 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
513 wndclass
.hbrBackground
= NULL
;
514 wndclass
.lpszMenuName
= NULL
;
515 wndclass
.lpszClassName
= appname
;
517 RegisterClass(&wndclass
);
522 memset(&ucsdata
, 0, sizeof(ucsdata
));
524 term
= term_init(&cfg
, &ucsdata
, NULL
);
525 logctx
= log_init(NULL
, &cfg
);
526 term_provide_logctx(term
, logctx
);
531 * Guess some defaults for the window size. This all gets
532 * updated later, so we don't really care too much. However, we
533 * do want the font width/height guesses to correspond to a
534 * large font rather than a small one...
541 term_size(term
, cfg
.height
, cfg
.width
, cfg
.savelines
);
542 guess_width
= extra_width
+ font_width
* term
->cols
;
543 guess_height
= extra_height
+ font_height
* term
->rows
;
546 get_fullscreen_rect(&r
);
547 if (guess_width
> r
.right
- r
.left
)
548 guess_width
= r
.right
- r
.left
;
549 if (guess_height
> r
.bottom
- r
.top
)
550 guess_height
= r
.bottom
- r
.top
;
554 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
557 winmode
&= ~(WS_VSCROLL
);
558 if (cfg
.resize_action
== RESIZE_DISABLED
)
559 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
561 exwinmode
|= WS_EX_TOPMOST
;
563 exwinmode
|= WS_EX_CLIENTEDGE
;
564 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
565 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
566 guess_width
, guess_height
,
567 NULL
, NULL
, inst
, NULL
);
571 * Initialise the fonts, simultaneously correcting the guesses
572 * for font_{width,height}.
577 * Correct the guesses for extra_{width,height}.
581 GetWindowRect(hwnd
, &wr
);
582 GetClientRect(hwnd
, &cr
);
583 offset_width
= offset_height
= cfg
.window_border
;
584 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
585 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
589 * Resize the window, now we know what size we _really_ want it
592 guess_width
= extra_width
+ font_width
* term
->cols
;
593 guess_height
= extra_height
+ font_height
* term
->rows
;
594 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
595 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
598 * Set up a caret bitmap, with no content.
602 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
603 bits
= snewn(size
, char);
604 memset(bits
, 0, size
);
605 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
608 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
611 * Initialise the scroll bar.
616 si
.cbSize
= sizeof(si
);
617 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
619 si
.nMax
= term
->rows
- 1;
620 si
.nPage
= term
->rows
;
622 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
626 * Start up the telnet connection.
630 char msg
[1024], *title
;
633 error
= back
->init((void *)term
, &backhandle
, &cfg
,
634 cfg
.host
, cfg
.port
, &realhost
, cfg
.tcp_nodelay
);
635 back
->provide_logctx(backhandle
, logctx
);
637 sprintf(msg
, "Unable to open connection to\n"
638 "%.800s\n" "%s", cfg
.host
, error
);
639 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
642 window_name
= icon_name
= NULL
;
644 title
= cfg
.wintitle
;
646 sprintf(msg
, "%s - PuTTY", realhost
);
650 set_title(NULL
, title
);
651 set_icon(NULL
, title
);
655 * Connect the terminal to the backend for resize purposes.
657 term_provide_resize_fn(term
, back
->size
, backhandle
);
660 * Set up a line discipline.
662 ldisc
= ldisc_create(&cfg
, term
, back
, backhandle
, NULL
);
664 session_closed
= FALSE
;
667 * Prepare the mouse handler.
669 lastact
= MA_NOTHING
;
670 lastbtn
= MBT_NOTHING
;
671 dbltime
= GetDoubleClickTime();
674 * Set up the session-control options on the system menu.
677 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
681 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
682 if (cfg
.protocol
== PROT_TELNET
) {
684 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
685 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
686 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
687 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
688 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
689 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
690 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
691 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
692 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
693 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
694 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
695 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
696 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
697 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
698 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
699 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
700 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
702 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
704 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
705 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
706 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
707 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
709 get_sesslist(&sesslist
, TRUE
);
711 i
< ((sesslist
.nsessions
< 256) ? sesslist
.nsessions
: 256);
713 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
714 sesslist
.sessions
[i
]);
715 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
716 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
717 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
718 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
719 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
720 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
721 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
722 AppendMenu(m
, (cfg
.resize_action
== RESIZE_DISABLED
) ?
723 MF_GRAYED
: MF_ENABLED
, IDM_FULLSCREEN
, "&Full Screen");
724 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
726 AppendMenu(m
, MF_ENABLED
, IDM_HELP
, "&Help");
727 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
731 * Set up the initial input locale.
733 set_input_locale(GetKeyboardLayout(0));
736 * Open the initial log file if there is one.
741 * Finally show the window!
743 ShowWindow(hwnd
, show
);
744 SetForegroundWindow(hwnd
);
747 * Set the palette up.
753 term
->has_focus
= (GetForegroundWindow() == hwnd
);
756 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
757 int timer_id
= 0, long_timer
= 0;
759 while (msg
.message
!= WM_QUIT
) {
760 /* Sometimes DispatchMessage calls routines that use their own
761 * GetMessage loop, setup this timer so we get some control back.
763 * Also call term_update() from the timer so that if the host
764 * is sending data flat out we still do redraws.
766 if (timer_id
&& long_timer
) {
767 KillTimer(hwnd
, timer_id
);
768 long_timer
= timer_id
= 0;
771 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
772 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
773 DispatchMessage(&msg
);
775 /* Make sure we blink everything that needs it. */
778 /* Send the paste buffer if there's anything to send */
781 /* If there's nothing new in the queue then we can do everything
782 * we've delayed, reading the socket, writing, and repainting
785 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
788 if (pending_netevent
) {
789 enact_pending_netevent();
791 /* Force the cursor blink on */
794 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
798 /* Okay there is now nothing to do so we make sure the screen is
799 * completely up to date then tell windows to call us in a little
803 KillTimer(hwnd
, timer_id
);
807 if (GetCapture() != hwnd
||
809 !(cfg
.mouse_override
&& is_shift_pressed())))
814 flash_window(1); /* maintain */
816 /* The messages seem unreliable; especially if we're being tricky */
817 term
->has_focus
= (GetForegroundWindow() == hwnd
);
820 /* Hmm, term_update didn't want to do an update too soon ... */
821 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
822 else if (!term
->has_focus
)
823 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
825 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
828 /* There's no point rescanning everything in the message queue
829 * so we do an apparently unnecessary wait here
832 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
837 cleanup_exit(msg
.wParam
); /* this doesn't return... */
838 return msg
.wParam
; /* ... but optimiser doesn't know */
844 void cleanup_exit(int code
)
857 if (cfg
.protocol
== PROT_SSH
) {
868 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
870 char *do_select(SOCKET skt
, int startup
)
875 events
= (FD_CONNECT
| FD_READ
| FD_WRITE
|
876 FD_OOB
| FD_CLOSE
| FD_ACCEPT
);
881 return "do_select(): internal error (hwnd==NULL)";
882 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
883 switch (WSAGetLastError()) {
885 return "Network is down";
887 return "WSAAsyncSelect(): unknown error";
894 * set or clear the "raw mouse message" mode
896 void set_raw_mouse_mode(void *frontend
, int activate
)
898 activate
= activate
&& !cfg
.no_mouse_rep
;
899 send_raw_mouse
= activate
;
900 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
904 * Print a message box and close the connection.
906 void connection_fatal(void *frontend
, char *fmt
, ...)
912 vsprintf(stuff
, fmt
, ap
);
914 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
915 if (cfg
.close_on_exit
== FORCE_ON
)
918 session_closed
= TRUE
;
919 set_icon(NULL
, "PuTTY (inactive)");
920 set_title(NULL
, "PuTTY (inactive)");
925 * Report an error at the command-line parsing stage.
927 void cmdline_error(char *fmt
, ...)
933 vsprintf(stuff
, fmt
, ap
);
935 MessageBox(hwnd
, stuff
, "PuTTY Command Line Error", MB_ICONERROR
| MB_OK
);
940 * Actually do the job requested by a WM_NETEVENT
942 static void enact_pending_netevent(void)
944 static int reentering
= 0;
945 extern int select_result(WPARAM
, LPARAM
);
949 return; /* don't unpend the pending */
951 pending_netevent
= FALSE
;
954 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
957 if (ret
== 0 && !session_closed
) {
958 /* Abnormal exits will already have set session_closed and taken
959 * appropriate action. */
960 if (cfg
.close_on_exit
== FORCE_ON
||
961 cfg
.close_on_exit
== AUTO
) PostQuitMessage(0);
963 session_closed
= TRUE
;
964 set_icon(NULL
, "PuTTY (inactive)");
965 set_title(NULL
, "PuTTY (inactive)");
966 MessageBox(hwnd
, "Connection closed by remote host",
967 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
973 * Copy the colour palette from the configuration data into defpal.
974 * This is non-trivial because the colour indices are different.
976 static void cfgtopalette(void)
979 static const int ww
[] = {
980 6, 7, 8, 9, 10, 11, 12, 13,
981 14, 15, 16, 17, 18, 19, 20, 21,
982 0, 1, 2, 3, 4, 4, 5, 5
985 for (i
= 0; i
< 24; i
++) {
987 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
988 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
989 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
994 * Set up the colour palette.
996 static void init_palette(void)
999 HDC hdc
= GetDC(hwnd
);
1001 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
1003 * This is a genuine case where we must use smalloc
1004 * because the snew macros can't cope.
1006 logpal
= smalloc(sizeof(*logpal
)
1007 - sizeof(logpal
->palPalEntry
)
1008 + NCOLOURS
* sizeof(PALETTEENTRY
));
1009 logpal
->palVersion
= 0x300;
1010 logpal
->palNumEntries
= NCOLOURS
;
1011 for (i
= 0; i
< NCOLOURS
; i
++) {
1012 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
1013 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
1014 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
1015 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
1017 pal
= CreatePalette(logpal
);
1019 SelectPalette(hdc
, pal
, FALSE
);
1020 RealizePalette(hdc
);
1021 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
1024 ReleaseDC(hwnd
, hdc
);
1027 for (i
= 0; i
< NCOLOURS
; i
++)
1028 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
1029 defpal
[i
].rgbtGreen
,
1030 defpal
[i
].rgbtBlue
);
1032 for (i
= 0; i
< NCOLOURS
; i
++)
1033 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
1034 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
1038 * Initialise all the fonts we will need initially. There may be as many as
1039 * three or as few as one. The other (poentially) twentyone fonts are done
1040 * if/when they are needed.
1044 * - check the font width and height, correcting our guesses if
1047 * - verify that the bold font is the same width as the ordinary
1048 * one, and engage shadow bolding if not.
1050 * - verify that the underlined font is the same width as the
1051 * ordinary one (manual underlining by means of line drawing can
1052 * be done in a pinch).
1054 static void init_fonts(int pick_width
, int pick_height
)
1061 int fw_dontcare
, fw_bold
;
1063 for (i
= 0; i
< FONT_MAXNO
; i
++)
1066 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1067 und_mode
= UND_FONT
;
1069 if (cfg
.font
.isbold
) {
1070 fw_dontcare
= FW_BOLD
;
1073 fw_dontcare
= FW_DONTCARE
;
1080 font_height
= pick_height
;
1082 font_height
= cfg
.font
.height
;
1083 if (font_height
> 0) {
1085 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
1088 font_width
= pick_width
;
1090 #define f(i,c,w,u) \
1091 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
1092 c, OUT_DEFAULT_PRECIS, \
1093 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
1094 FIXED_PITCH | FF_DONTCARE, cfg.font.name)
1096 f(FONT_NORMAL
, cfg
.font
.charset
, fw_dontcare
, FALSE
);
1098 lfont
.lfHeight
= font_height
;
1099 lfont
.lfWidth
= font_width
;
1100 lfont
.lfEscapement
= 0;
1101 lfont
.lfOrientation
= 0;
1102 lfont
.lfWeight
= fw_dontcare
;
1103 lfont
.lfItalic
= FALSE
;
1104 lfont
.lfUnderline
= FALSE
;
1105 lfont
.lfStrikeOut
= FALSE
;
1106 lfont
.lfCharSet
= cfg
.font
.charset
;
1107 lfont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
1108 lfont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
1109 lfont
.lfQuality
= DEFAULT_QUALITY
;
1110 lfont
.lfPitchAndFamily
= FIXED_PITCH
| FF_DONTCARE
;
1111 strncpy(lfont
.lfFaceName
, cfg
.font
.name
, LF_FACESIZE
);
1113 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
1114 GetTextMetrics(hdc
, &tm
);
1116 if (pick_width
== 0 || pick_height
== 0) {
1117 font_height
= tm
.tmHeight
;
1118 font_width
= tm
.tmAveCharWidth
;
1120 font_dualwidth
= (tm
.tmAveCharWidth
!= tm
.tmMaxCharWidth
);
1122 #ifdef RDB_DEBUG_PATCH
1123 debug(23, "Primary font H=%d, AW=%d, MW=%d",
1124 tm
.tmHeight
, tm
.tmAveCharWidth
, tm
.tmMaxCharWidth
);
1129 DWORD cset
= tm
.tmCharSet
;
1130 memset(&info
, 0xFF, sizeof(info
));
1132 /* !!! Yes the next line is right */
1133 if (cset
== OEM_CHARSET
)
1134 ucsdata
.font_codepage
= GetOEMCP();
1136 if (TranslateCharsetInfo ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
))
1137 ucsdata
.font_codepage
= info
.ciACP
;
1139 ucsdata
.font_codepage
= -1;
1141 GetCPInfo(ucsdata
.font_codepage
, &cpinfo
);
1142 ucsdata
.dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
1145 f(FONT_UNDERLINE
, cfg
.font
.charset
, fw_dontcare
, TRUE
);
1148 * Some fonts, e.g. 9-pt Courier, draw their underlines
1149 * outside their character cell. We successfully prevent
1150 * screen corruption by clipping the text output, but then
1151 * we lose the underline completely. Here we try to work
1152 * out whether this is such a font, and if it is, we set a
1153 * flag that causes underlines to be drawn by hand.
1155 * Having tried other more sophisticated approaches (such
1156 * as examining the TEXTMETRIC structure or requesting the
1157 * height of a string), I think we'll do this the brute
1158 * force way: we create a small bitmap, draw an underlined
1159 * space on it, and test to see whether any pixels are
1160 * foreground-coloured. (Since we expect the underline to
1161 * go all the way across the character cell, we only search
1162 * down a single column of the bitmap, half way across.)
1166 HBITMAP und_bm
, und_oldbm
;
1170 und_dc
= CreateCompatibleDC(hdc
);
1171 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
1172 und_oldbm
= SelectObject(und_dc
, und_bm
);
1173 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
1174 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
1175 SetTextColor(und_dc
, RGB(255, 255, 255));
1176 SetBkColor(und_dc
, RGB(0, 0, 0));
1177 SetBkMode(und_dc
, OPAQUE
);
1178 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
1180 for (i
= 0; i
< font_height
; i
++) {
1181 c
= GetPixel(und_dc
, font_width
/ 2, i
);
1182 if (c
!= RGB(0, 0, 0))
1185 SelectObject(und_dc
, und_oldbm
);
1186 DeleteObject(und_bm
);
1189 und_mode
= UND_LINE
;
1190 DeleteObject(fonts
[FONT_UNDERLINE
]);
1191 fonts
[FONT_UNDERLINE
] = 0;
1195 if (bold_mode
== BOLD_FONT
) {
1196 f(FONT_BOLD
, cfg
.font
.charset
, fw_bold
, FALSE
);
1200 descent
= tm
.tmAscent
+ 1;
1201 if (descent
>= font_height
)
1202 descent
= font_height
- 1;
1204 for (i
= 0; i
< 3; i
++) {
1206 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
1207 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
1214 ReleaseDC(hwnd
, hdc
);
1216 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1217 und_mode
= UND_LINE
;
1218 DeleteObject(fonts
[FONT_UNDERLINE
]);
1219 fonts
[FONT_UNDERLINE
] = 0;
1222 if (bold_mode
== BOLD_FONT
&&
1223 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1224 bold_mode
= BOLD_SHADOW
;
1225 DeleteObject(fonts
[FONT_BOLD
]);
1226 fonts
[FONT_BOLD
] = 0;
1228 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1230 init_ucs(&cfg
, &ucsdata
);
1233 static void another_font(int fontno
)
1236 int fw_dontcare
, fw_bold
;
1240 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1243 basefont
= (fontno
& ~(FONT_BOLDUND
));
1244 if (basefont
!= fontno
&& !fontflag
[basefont
])
1245 another_font(basefont
);
1247 if (cfg
.font
.isbold
) {
1248 fw_dontcare
= FW_BOLD
;
1251 fw_dontcare
= FW_DONTCARE
;
1255 c
= cfg
.font
.charset
;
1261 if (fontno
& FONT_WIDE
)
1263 if (fontno
& FONT_NARROW
)
1265 if (fontno
& FONT_OEM
)
1267 if (fontno
& FONT_BOLD
)
1269 if (fontno
& FONT_UNDERLINE
)
1273 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1274 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1275 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1276 FIXED_PITCH
| FF_DONTCARE
, s
);
1278 fontflag
[fontno
] = 1;
1281 static void deinit_fonts(void)
1284 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1286 DeleteObject(fonts
[i
]);
1292 void request_resize(void *frontend
, int w
, int h
)
1296 /* If the window is maximized supress resizing attempts */
1297 if (IsZoomed(hwnd
)) {
1298 if (cfg
.resize_action
== RESIZE_TERM
)
1302 if (cfg
.resize_action
== RESIZE_DISABLED
) return;
1303 if (h
== term
->rows
&& w
== term
->cols
) return;
1305 /* Sanity checks ... */
1307 static int first_time
= 1;
1310 switch (first_time
) {
1312 /* Get the size of the screen */
1313 if (get_fullscreen_rect(&ss
))
1314 /* first_time = 0 */ ;
1320 /* Make sure the values are sane */
1321 width
= (ss
.right
- ss
.left
- extra_width
) / 4;
1322 height
= (ss
.bottom
- ss
.top
- extra_height
) / 6;
1324 if (w
> width
|| h
> height
)
1333 term_size(term
, h
, w
, cfg
.savelines
);
1335 if (cfg
.resize_action
!= RESIZE_FONT
&& !IsZoomed(hwnd
)) {
1336 width
= extra_width
+ font_width
* w
;
1337 height
= extra_height
+ font_height
* h
;
1339 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1340 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1341 SWP_NOMOVE
| SWP_NOZORDER
);
1345 InvalidateRect(hwnd
, NULL
, TRUE
);
1348 static void reset_window(int reinit
) {
1350 * This function decides how to resize or redraw when the
1351 * user changes something.
1353 * This function doesn't like to change the terminal size but if the
1354 * font size is locked that may be it's only soluion.
1356 int win_width
, win_height
;
1359 #ifdef RDB_DEBUG_PATCH
1360 debug((27, "reset_window()"));
1363 /* Current window sizes ... */
1364 GetWindowRect(hwnd
, &wr
);
1365 GetClientRect(hwnd
, &cr
);
1367 win_width
= cr
.right
- cr
.left
;
1368 win_height
= cr
.bottom
- cr
.top
;
1370 if (cfg
.resize_action
== RESIZE_DISABLED
) reinit
= 2;
1372 /* Are we being forced to reload the fonts ? */
1374 #ifdef RDB_DEBUG_PATCH
1375 debug((27, "reset_window() -- Forced deinit"));
1381 /* Oh, looks like we're minimised */
1382 if (win_width
== 0 || win_height
== 0)
1385 /* Is the window out of position ? */
1387 (offset_width
!= (win_width
-font_width
*term
->cols
)/2 ||
1388 offset_height
!= (win_height
-font_height
*term
->rows
)/2) ){
1389 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1390 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1391 InvalidateRect(hwnd
, NULL
, TRUE
);
1392 #ifdef RDB_DEBUG_PATCH
1393 debug((27, "reset_window() -> Reposition terminal"));
1397 if (IsZoomed(hwnd
)) {
1398 /* We're fullscreen, this means we must not change the size of
1399 * the window so it's the font size or the terminal itself.
1402 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1403 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1405 if (cfg
.resize_action
!= RESIZE_TERM
) {
1406 if ( font_width
!= win_width
/term
->cols
||
1407 font_height
!= win_height
/term
->rows
) {
1409 init_fonts(win_width
/term
->cols
, win_height
/term
->rows
);
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((25, "reset_window() -> Z font resize to (%d, %d)",
1415 font_width
, font_height
));
1419 if ( font_width
!= win_width
/term
->cols
||
1420 font_height
!= win_height
/term
->rows
) {
1421 /* Our only choice at this point is to change the
1422 * size of the terminal; Oh well.
1424 term_size(term
, win_height
/font_height
, win_width
/font_width
,
1426 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1427 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1428 InvalidateRect(hwnd
, NULL
, TRUE
);
1429 #ifdef RDB_DEBUG_PATCH
1430 debug((27, "reset_window() -> Zoomed term_size"));
1437 /* Hmm, a force re-init means we should ignore the current window
1438 * so we resize to the default font size.
1441 #ifdef RDB_DEBUG_PATCH
1442 debug((27, "reset_window() -> Forced re-init"));
1445 offset_width
= offset_height
= cfg
.window_border
;
1446 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1447 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1449 if (win_width
!= font_width
*term
->cols
+ offset_width
*2 ||
1450 win_height
!= font_height
*term
->rows
+ offset_height
*2) {
1452 /* If this is too large windows will resize it to the maximum
1453 * allowed window size, we will then be back in here and resize
1454 * the font or terminal to fit.
1456 SetWindowPos(hwnd
, NULL
, 0, 0,
1457 font_width
*term
->cols
+ extra_width
,
1458 font_height
*term
->rows
+ extra_height
,
1459 SWP_NOMOVE
| SWP_NOZORDER
);
1462 InvalidateRect(hwnd
, NULL
, TRUE
);
1466 /* Okay the user doesn't want us to change the font so we try the
1467 * window. But that may be too big for the screen which forces us
1468 * to change the terminal.
1470 if ((cfg
.resize_action
== RESIZE_TERM
&& reinit
<=0) ||
1471 (cfg
.resize_action
== RESIZE_EITHER
&& reinit
<0) ||
1473 offset_width
= offset_height
= cfg
.window_border
;
1474 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1475 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1477 if (win_width
!= font_width
*term
->cols
+ offset_width
*2 ||
1478 win_height
!= font_height
*term
->rows
+ offset_height
*2) {
1483 get_fullscreen_rect(&ss
);
1485 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1486 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1489 if ( term
->rows
> height
|| term
->cols
> width
) {
1490 if (cfg
.resize_action
== RESIZE_EITHER
) {
1491 /* Make the font the biggest we can */
1492 if (term
->cols
> width
)
1493 font_width
= (ss
.right
- ss
.left
- extra_width
)
1495 if (term
->rows
> height
)
1496 font_height
= (ss
.bottom
- ss
.top
- extra_height
)
1500 init_fonts(font_width
, font_height
);
1502 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1503 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1505 if ( height
> term
->rows
) height
= term
->rows
;
1506 if ( width
> term
->cols
) width
= term
->cols
;
1507 term_size(term
, height
, width
, cfg
.savelines
);
1508 #ifdef RDB_DEBUG_PATCH
1509 debug((27, "reset_window() -> term resize to (%d,%d)",
1515 SetWindowPos(hwnd
, NULL
, 0, 0,
1516 font_width
*term
->cols
+ extra_width
,
1517 font_height
*term
->rows
+ extra_height
,
1518 SWP_NOMOVE
| SWP_NOZORDER
);
1520 InvalidateRect(hwnd
, NULL
, TRUE
);
1521 #ifdef RDB_DEBUG_PATCH
1522 debug((27, "reset_window() -> window resize to (%d,%d)",
1523 font_width
*term
->cols
+ extra_width
,
1524 font_height
*term
->rows
+ extra_height
));
1530 /* We're allowed to or must change the font but do we want to ? */
1532 if (font_width
!= (win_width
-cfg
.window_border
*2)/term
->cols
||
1533 font_height
!= (win_height
-cfg
.window_border
*2)/term
->rows
) {
1536 init_fonts((win_width
-cfg
.window_border
*2)/term
->cols
,
1537 (win_height
-cfg
.window_border
*2)/term
->rows
);
1538 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1539 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1541 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+offset_width
*2;
1542 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1544 InvalidateRect(hwnd
, NULL
, TRUE
);
1545 #ifdef RDB_DEBUG_PATCH
1546 debug((25, "reset_window() -> font resize to (%d,%d)",
1547 font_width
, font_height
));
1552 static void set_input_locale(HKL kl
)
1556 GetLocaleInfo(LOWORD(kl
), LOCALE_IDEFAULTANSICODEPAGE
,
1557 lbuf
, sizeof(lbuf
));
1559 kbd_codepage
= atoi(lbuf
);
1562 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
, int alt
)
1564 int thistime
= GetMessageTime();
1566 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1567 lastbtn
= MBT_NOTHING
;
1568 term_mouse(term
, b
, translate_button(b
), MA_CLICK
,
1569 x
, y
, shift
, ctrl
, alt
);
1573 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1574 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1575 lastact
== MA_2CLK ? MA_3CLK
:
1576 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1581 if (lastact
!= MA_NOTHING
)
1582 term_mouse(term
, b
, translate_button(b
), lastact
,
1583 x
, y
, shift
, ctrl
, alt
);
1584 lasttime
= thistime
;
1588 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1589 * into a cooked one (SELECT, EXTEND, PASTE).
1591 static Mouse_Button
translate_button(Mouse_Button button
)
1593 if (button
== MBT_LEFT
)
1595 if (button
== MBT_MIDDLE
)
1596 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1597 if (button
== MBT_RIGHT
)
1598 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1599 return 0; /* shouldn't happen */
1602 static void show_mouseptr(int show
)
1604 static int cursor_visible
= 1;
1605 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1607 if (cursor_visible
&& !show
)
1609 else if (!cursor_visible
&& show
)
1611 cursor_visible
= show
;
1614 static int is_alt_pressed(void)
1617 int r
= GetKeyboardState(keystate
);
1620 if (keystate
[VK_MENU
] & 0x80)
1622 if (keystate
[VK_RMENU
] & 0x80)
1627 static int is_shift_pressed(void)
1630 int r
= GetKeyboardState(keystate
);
1633 if (keystate
[VK_SHIFT
] & 0x80)
1638 static int resizing
;
1640 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1641 WPARAM wParam
, LPARAM lParam
)
1644 static int ignore_clip
= FALSE
;
1645 static int need_backend_resize
= FALSE
;
1646 static int fullscr_on_max
= FALSE
;
1650 if (pending_netevent
)
1651 enact_pending_netevent();
1652 if (GetCapture() != hwnd
||
1653 (send_raw_mouse
&& !(cfg
.mouse_override
&& is_shift_pressed())))
1659 if (cfg
.ping_interval
> 0) {
1662 if (now
- last_movement
> cfg
.ping_interval
) {
1663 back
->special(backhandle
, TS_PING
);
1664 last_movement
= now
;
1667 net_pending_errors();
1673 if (!cfg
.warn_on_close
|| session_closed
||
1675 "Are you sure you want to close this session?",
1676 "PuTTY Exit Confirmation",
1677 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1678 DestroyWindow(hwnd
);
1685 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1697 PROCESS_INFORMATION pi
;
1698 HANDLE filemap
= NULL
;
1700 if (wParam
== IDM_DUPSESS
) {
1702 * Allocate a file-mapping memory chunk for the
1705 SECURITY_ATTRIBUTES sa
;
1708 sa
.nLength
= sizeof(sa
);
1709 sa
.lpSecurityDescriptor
= NULL
;
1710 sa
.bInheritHandle
= TRUE
;
1711 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1714 0, sizeof(Config
), NULL
);
1716 p
= (Config
*) MapViewOfFile(filemap
,
1718 0, 0, sizeof(Config
));
1720 *p
= cfg
; /* structure copy */
1724 sprintf(c
, "putty &%p", filemap
);
1726 } else if (wParam
== IDM_SAVEDSESS
) {
1727 if ((lParam
- IDM_SAVED_MIN
) / 16 < sesslist
.nsessions
) {
1729 sesslist
.sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1730 cl
= snewn(16 + strlen(session
), char);
1731 /* 8, but play safe */
1734 /* not a very important failure mode */
1736 sprintf(cl
, "putty @%s", session
);
1744 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1746 si
.lpReserved
= NULL
;
1747 si
.lpDesktop
= NULL
;
1751 si
.lpReserved2
= NULL
;
1752 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1753 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1756 CloseHandle(filemap
);
1766 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1769 if (!do_reconfig(hwnd
))
1773 /* Disable full-screen if resizing forbidden */
1774 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
1775 EnableMenuItem(m
, IDM_FULLSCREEN
, MF_BYCOMMAND
|
1776 (cfg
.resize_action
== RESIZE_DISABLED
)
1777 ? MF_GRAYED
: MF_ENABLED
);
1778 /* Gracefully unzoom if necessary */
1779 if (IsZoomed(hwnd
) &&
1780 (cfg
.resize_action
== RESIZE_DISABLED
)) {
1781 ShowWindow(hwnd
, SW_RESTORE
);
1785 /* Pass new config data to the logging module */
1786 log_reconfig(logctx
, &cfg
);
1790 * Flush the line discipline's edit buffer in the
1791 * case where local editing has just been disabled.
1793 ldisc_send(ldisc
, NULL
, 0, 0);
1801 /* Pass new config data to the terminal */
1802 term_reconfig(term
, &cfg
);
1804 /* Pass new config data to the back end */
1805 back
->reconfig(backhandle
, &cfg
);
1807 /* Screen size changed ? */
1808 if (cfg
.height
!= prev_cfg
.height
||
1809 cfg
.width
!= prev_cfg
.width
||
1810 cfg
.savelines
!= prev_cfg
.savelines
||
1811 cfg
.resize_action
== RESIZE_FONT
||
1812 (cfg
.resize_action
== RESIZE_EITHER
&& IsZoomed(hwnd
)) ||
1813 cfg
.resize_action
== RESIZE_DISABLED
)
1814 term_size(term
, cfg
.height
, cfg
.width
, cfg
.savelines
);
1816 /* Enable or disable the scroll bar, etc */
1818 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1819 LONG nexflag
, exflag
=
1820 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1823 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1824 if (cfg
.alwaysontop
) {
1825 nexflag
|= WS_EX_TOPMOST
;
1826 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1827 SWP_NOMOVE
| SWP_NOSIZE
);
1829 nexflag
&= ~(WS_EX_TOPMOST
);
1830 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1831 SWP_NOMOVE
| SWP_NOSIZE
);
1834 if (cfg
.sunken_edge
)
1835 nexflag
|= WS_EX_CLIENTEDGE
;
1837 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1840 if (is_full_screen() ?
1841 cfg
.scrollbar_in_fullscreen
: cfg
.scrollbar
)
1844 nflg
&= ~WS_VSCROLL
;
1846 if (cfg
.resize_action
== RESIZE_DISABLED
||
1848 nflg
&= ~WS_THICKFRAME
;
1850 nflg
|= WS_THICKFRAME
;
1852 if (cfg
.resize_action
== RESIZE_DISABLED
)
1853 nflg
&= ~WS_MAXIMIZEBOX
;
1855 nflg
|= WS_MAXIMIZEBOX
;
1857 if (nflg
!= flag
|| nexflag
!= exflag
) {
1859 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1860 if (nexflag
!= exflag
)
1861 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1863 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1864 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1865 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1873 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1878 set_title(NULL
, cfg
.wintitle
);
1879 if (IsIconic(hwnd
)) {
1881 cfg
.win_name_always ? window_name
:
1885 if (strcmp(cfg
.font
.name
, prev_cfg
.font
.name
) != 0 ||
1886 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1887 cfg
.font
.isbold
!= prev_cfg
.font
.isbold
||
1888 cfg
.font
.height
!= prev_cfg
.font
.height
||
1889 cfg
.font
.charset
!= prev_cfg
.font
.charset
||
1890 cfg
.vtmode
!= prev_cfg
.vtmode
||
1891 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1892 cfg
.resize_action
== RESIZE_DISABLED
||
1893 cfg
.resize_action
== RESIZE_EITHER
||
1894 (cfg
.resize_action
!= prev_cfg
.resize_action
))
1897 InvalidateRect(hwnd
, NULL
, TRUE
);
1898 reset_window(init_lvl
);
1899 net_pending_errors();
1910 ldisc_send(ldisc
, NULL
, 0, 0);
1913 back
->special(backhandle
, TS_AYT
);
1914 net_pending_errors();
1917 back
->special(backhandle
, TS_BRK
);
1918 net_pending_errors();
1921 back
->special(backhandle
, TS_SYNCH
);
1922 net_pending_errors();
1925 back
->special(backhandle
, TS_EC
);
1926 net_pending_errors();
1929 back
->special(backhandle
, TS_EL
);
1930 net_pending_errors();
1933 back
->special(backhandle
, TS_GA
);
1934 net_pending_errors();
1937 back
->special(backhandle
, TS_NOP
);
1938 net_pending_errors();
1941 back
->special(backhandle
, TS_ABORT
);
1942 net_pending_errors();
1945 back
->special(backhandle
, TS_AO
);
1946 net_pending_errors();
1949 back
->special(backhandle
, TS_IP
);
1950 net_pending_errors();
1953 back
->special(backhandle
, TS_SUSP
);
1954 net_pending_errors();
1957 back
->special(backhandle
, TS_EOR
);
1958 net_pending_errors();
1961 back
->special(backhandle
, TS_EOF
);
1962 net_pending_errors();
1968 WinHelp(hwnd
, help_path
,
1969 help_has_contents ? HELP_FINDER
: HELP_CONTENTS
, 0);
1973 * We get this if the System menu has been activated
1980 * We get this if the System menu has been activated
1981 * using the keyboard. This might happen from within
1982 * TranslateKey, in which case it really wants to be
1983 * followed by a `space' character to actually _bring
1984 * the menu up_ rather than just sitting there in
1985 * `ready to appear' state.
1987 show_mouseptr(1); /* make sure pointer is visible */
1989 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1991 case IDM_FULLSCREEN
:
1995 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1996 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
2001 #define X_POS(l) ((int)(short)LOWORD(l))
2002 #define Y_POS(l) ((int)(short)HIWORD(l))
2004 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
2005 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
2006 case WM_LBUTTONDOWN
:
2007 case WM_MBUTTONDOWN
:
2008 case WM_RBUTTONDOWN
:
2016 case WM_LBUTTONDOWN
:
2020 case WM_MBUTTONDOWN
:
2021 button
= MBT_MIDDLE
;
2024 case WM_RBUTTONDOWN
:
2033 button
= MBT_MIDDLE
;
2041 button
= press
= 0; /* shouldn't happen */
2045 * Special case: in full-screen mode, if the left
2046 * button is clicked in the very top left corner of the
2047 * window, we put up the System menu instead of doing
2050 if (is_full_screen() && press
&& button
== MBT_LEFT
&&
2051 X_POS(lParam
) == 0 && Y_POS(lParam
) == 0) {
2052 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_MOUSEMENU
, 0);
2057 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
2058 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
,
2062 term_mouse(term
, button
, translate_button(button
), MA_RELEASE
,
2063 TO_CHR_X(X_POS(lParam
)),
2064 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
2065 wParam
& MK_CONTROL
, is_alt_pressed());
2073 * Add the mouse position and message time to the random
2076 noise_ultralight(lParam
);
2078 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
) &&
2079 GetCapture() == hwnd
) {
2081 if (wParam
& MK_LBUTTON
)
2083 else if (wParam
& MK_MBUTTON
)
2087 term_mouse(term
, b
, translate_button(b
), MA_DRAG
,
2088 TO_CHR_X(X_POS(lParam
)),
2089 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
2090 wParam
& MK_CONTROL
, is_alt_pressed());
2093 case WM_NCMOUSEMOVE
:
2095 noise_ultralight(lParam
);
2097 case WM_IGNORE_CLIP
:
2098 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
2100 case WM_DESTROYCLIPBOARD
:
2102 term_deselect(term
);
2103 ignore_clip
= FALSE
;
2109 hdc
= BeginPaint(hwnd
, &p
);
2111 SelectPalette(hdc
, pal
, TRUE
);
2112 RealizePalette(hdc
);
2114 term_paint(term
, hdc
,
2115 (p
.rcPaint
.left
-offset_width
)/font_width
,
2116 (p
.rcPaint
.top
-offset_height
)/font_height
,
2117 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
2118 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
,
2122 p
.rcPaint
.left
< offset_width
||
2123 p
.rcPaint
.top
< offset_height
||
2124 p
.rcPaint
.right
>= offset_width
+ font_width
*term
->cols
||
2125 p
.rcPaint
.bottom
>= offset_height
+ font_height
*term
->rows
)
2127 HBRUSH fillcolour
, oldbrush
;
2129 fillcolour
= CreateSolidBrush (
2130 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
2131 oldbrush
= SelectObject(hdc
, fillcolour
);
2132 edge
= CreatePen(PS_SOLID
, 0,
2133 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
2134 oldpen
= SelectObject(hdc
, edge
);
2137 * Jordan Russell reports that this apparently
2138 * ineffectual IntersectClipRect() call masks a
2139 * Windows NT/2K bug causing strange display
2140 * problems when the PuTTY window is taller than
2141 * the primary monitor. It seems harmless enough...
2143 IntersectClipRect(hdc
,
2144 p
.rcPaint
.left
, p
.rcPaint
.top
,
2145 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
2147 ExcludeClipRect(hdc
,
2148 offset_width
, offset_height
,
2149 offset_width
+font_width
*term
->cols
,
2150 offset_height
+font_height
*term
->rows
);
2152 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
2153 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
2155 // SelectClipRgn(hdc, NULL);
2157 SelectObject(hdc
, oldbrush
);
2158 DeleteObject(fillcolour
);
2159 SelectObject(hdc
, oldpen
);
2162 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
2163 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
2169 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2170 * but the only one that's likely to try to overload us is FD_READ.
2171 * This means buffering just one is fine.
2173 if (pending_netevent
)
2174 enact_pending_netevent();
2176 pending_netevent
= TRUE
;
2177 pend_netevent_wParam
= wParam
;
2178 pend_netevent_lParam
= lParam
;
2179 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
2180 enact_pending_netevent();
2182 time(&last_movement
);
2185 term
->has_focus
= TRUE
;
2186 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
2188 flash_window(0); /* stop */
2195 term
->has_focus
= FALSE
;
2197 caret_x
= caret_y
= -1; /* ensure caret is replaced next time */
2201 case WM_ENTERSIZEMOVE
:
2202 #ifdef RDB_DEBUG_PATCH
2203 debug((27, "WM_ENTERSIZEMOVE"));
2207 need_backend_resize
= FALSE
;
2209 case WM_EXITSIZEMOVE
:
2212 #ifdef RDB_DEBUG_PATCH
2213 debug((27, "WM_EXITSIZEMOVE"));
2215 if (need_backend_resize
) {
2216 term_size(term
, cfg
.height
, cfg
.width
, cfg
.savelines
);
2217 InvalidateRect(hwnd
, NULL
, TRUE
);
2222 * This does two jobs:
2223 * 1) Keep the sizetip uptodate
2224 * 2) Make sure the window size is _stepped_ in units of the font size.
2226 if (cfg
.resize_action
!= RESIZE_FONT
&& !is_alt_pressed()) {
2227 int width
, height
, w
, h
, ew
, eh
;
2228 LPRECT r
= (LPRECT
) lParam
;
2230 if ( !need_backend_resize
&& cfg
.resize_action
== RESIZE_EITHER
&&
2231 (cfg
.height
!= term
->rows
|| cfg
.width
!= term
->cols
)) {
2233 * Great! It seems that both the terminal size and the
2234 * font size have been changed and the user is now dragging.
2236 * It will now be difficult to get back to the configured
2239 * This would be easier but it seems to be too confusing.
2241 term_size(term, cfg.height, cfg.width, cfg.savelines);
2244 cfg
.height
=term
->rows
; cfg
.width
=term
->cols
;
2246 InvalidateRect(hwnd
, NULL
, TRUE
);
2247 need_backend_resize
= TRUE
;
2250 width
= r
->right
- r
->left
- extra_width
;
2251 height
= r
->bottom
- r
->top
- extra_height
;
2252 w
= (width
+ font_width
/ 2) / font_width
;
2255 h
= (height
+ font_height
/ 2) / font_height
;
2258 UpdateSizeTip(hwnd
, w
, h
);
2259 ew
= width
- w
* font_width
;
2260 eh
= height
- h
* font_height
;
2262 if (wParam
== WMSZ_LEFT
||
2263 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2269 if (wParam
== WMSZ_TOP
||
2270 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2280 int width
, height
, w
, h
, rv
= 0;
2281 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
2282 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2283 LPRECT r
= (LPRECT
) lParam
;
2285 width
= r
->right
- r
->left
- ex_width
;
2286 height
= r
->bottom
- r
->top
- ex_height
;
2288 w
= (width
+ term
->cols
/2)/term
->cols
;
2289 h
= (height
+ term
->rows
/2)/term
->rows
;
2290 if ( r
->right
!= r
->left
+ w
*term
->cols
+ ex_width
)
2293 if (wParam
== WMSZ_LEFT
||
2294 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2295 r
->left
= r
->right
- w
*term
->cols
- ex_width
;
2297 r
->right
= r
->left
+ w
*term
->cols
+ ex_width
;
2299 if (r
->bottom
!= r
->top
+ h
*term
->rows
+ ex_height
)
2302 if (wParam
== WMSZ_TOP
||
2303 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2304 r
->top
= r
->bottom
- h
*term
->rows
- ex_height
;
2306 r
->bottom
= r
->top
+ h
*term
->rows
+ ex_height
;
2310 /* break; (never reached) */
2311 case WM_FULLSCR_ON_MAX
:
2312 fullscr_on_max
= TRUE
;
2315 sys_cursor_update();
2318 #ifdef RDB_DEBUG_PATCH
2319 debug((27, "WM_SIZE %s (%d,%d)",
2320 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2321 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2322 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2323 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2325 LOWORD(lParam
), HIWORD(lParam
)));
2327 if (wParam
== SIZE_MINIMIZED
)
2329 cfg
.win_name_always ? window_name
: icon_name
);
2330 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2331 SetWindowText(hwnd
, window_name
);
2332 if (wParam
== SIZE_RESTORED
)
2333 clear_full_screen();
2334 if (wParam
== SIZE_MAXIMIZED
&& fullscr_on_max
) {
2335 fullscr_on_max
= FALSE
;
2339 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2340 /* A resize, well it better be a minimize. */
2344 int width
, height
, w
, h
;
2346 width
= LOWORD(lParam
);
2347 height
= HIWORD(lParam
);
2350 if (wParam
== SIZE_MAXIMIZED
&& !was_zoomed
) {
2352 prev_rows
= term
->rows
;
2353 prev_cols
= term
->cols
;
2354 if (cfg
.resize_action
== RESIZE_TERM
) {
2355 w
= width
/ font_width
;
2357 h
= height
/ font_height
;
2360 term_size(term
, h
, w
, cfg
.savelines
);
2363 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2365 if (cfg
.resize_action
== RESIZE_TERM
)
2366 term_size(term
, prev_rows
, prev_cols
, cfg
.savelines
);
2367 if (cfg
.resize_action
!= RESIZE_FONT
)
2372 /* This is an unexpected resize, these will normally happen
2373 * if the window is too large. Probably either the user
2374 * selected a huge font or the screen size has changed.
2376 * This is also called with minimize.
2378 else reset_window(-1);
2382 * Don't call back->size in mid-resize. (To prevent
2383 * massive numbers of resize events getting sent
2384 * down the connection during an NT opaque drag.)
2387 if (cfg
.resize_action
!= RESIZE_FONT
&& !is_alt_pressed()) {
2388 need_backend_resize
= TRUE
;
2389 w
= (width
-cfg
.window_border
*2) / font_width
;
2391 h
= (height
-cfg
.window_border
*2) / font_height
;
2400 sys_cursor_update();
2403 switch (LOWORD(wParam
)) {
2405 term_scroll(term
, -1, 0);
2408 term_scroll(term
, +1, 0);
2411 term_scroll(term
, 0, +1);
2414 term_scroll(term
, 0, -1);
2417 term_scroll(term
, 0, +term
->rows
/ 2);
2420 term_scroll(term
, 0, -term
->rows
/ 2);
2422 case SB_THUMBPOSITION
:
2424 term_scroll(term
, 1, HIWORD(wParam
));
2428 case WM_PALETTECHANGED
:
2429 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2430 HDC hdc
= get_ctx(NULL
);
2432 if (RealizePalette(hdc
) > 0)
2438 case WM_QUERYNEWPALETTE
:
2440 HDC hdc
= get_ctx(NULL
);
2442 if (RealizePalette(hdc
) > 0)
2454 * Add the scan code and keypress timing to the random
2457 noise_ultralight(lParam
);
2460 * We don't do TranslateMessage since it disassociates the
2461 * resulting CHAR message from the KEYDOWN that sparked it,
2462 * which we occasionally don't want. Instead, we process
2463 * KEYDOWN, and call the Win32 translator functions so that
2464 * we get the translations under _our_ control.
2467 unsigned char buf
[20];
2470 if (wParam
== VK_PROCESSKEY
) {
2473 m
.message
= WM_KEYDOWN
;
2475 m
.lParam
= lParam
& 0xdfff;
2476 TranslateMessage(&m
);
2478 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2480 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2484 * Interrupt an ongoing paste. I'm not sure
2485 * this is sensible, but for the moment it's
2486 * preferable to having to faff about buffering
2492 * We need not bother about stdin backlogs
2493 * here, because in GUI PuTTY we can't do
2494 * anything about it anyway; there's no means
2495 * of asking Windows to hold off on KEYDOWN
2496 * messages. We _have_ to buffer everything
2499 term_seen_key_event(term
);
2500 ldisc_send(ldisc
, buf
, len
, 1);
2505 net_pending_errors();
2507 case WM_INPUTLANGCHANGE
:
2508 /* wParam == Font number */
2509 /* lParam == Locale */
2510 set_input_locale((HKL
)lParam
);
2511 sys_cursor_update();
2514 if(wParam
== IMN_SETOPENSTATUS
) {
2515 HIMC hImc
= ImmGetContext(hwnd
);
2516 ImmSetCompositionFont(hImc
, &lfont
);
2517 ImmReleaseContext(hwnd
, hImc
);
2521 case WM_IME_COMPOSITION
:
2527 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2528 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2530 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2531 break; /* fall back to DefWindowProc */
2533 hIMC
= ImmGetContext(hwnd
);
2534 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2538 buff
= snewn(n
, char);
2539 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2541 * Jaeyoun Chung reports that Korean character
2542 * input doesn't work correctly if we do a single
2543 * luni_send() covering the whole of buff. So
2544 * instead we luni_send the characters one by one.
2546 term_seen_key_event(term
);
2547 for (i
= 0; i
< n
; i
+= 2) {
2548 luni_send(ldisc
, (unsigned short *)(buff
+i
), 1, 1);
2552 ImmReleaseContext(hwnd
, hIMC
);
2557 if (wParam
& 0xFF00) {
2558 unsigned char buf
[2];
2561 buf
[0] = wParam
>> 8;
2562 term_seen_key_event(term
);
2563 lpage_send(ldisc
, kbd_codepage
, buf
, 2, 1);
2565 char c
= (unsigned char) wParam
;
2566 term_seen_key_event(term
);
2567 lpage_send(ldisc
, kbd_codepage
, &c
, 1, 1);
2573 * Nevertheless, we are prepared to deal with WM_CHAR
2574 * messages, should they crop up. So if someone wants to
2575 * post the things to us as part of a macro manoeuvre,
2576 * we're ready to cope.
2579 char c
= (unsigned char)wParam
;
2580 term_seen_key_event(term
);
2581 lpage_send(ldisc
, CP_ACP
, &c
, 1, 1);
2585 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2586 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2590 if (message
== wm_mousewheel
|| message
== WM_MOUSEWHEEL
) {
2591 int shift_pressed
=0, control_pressed
=0;
2593 if (message
== WM_MOUSEWHEEL
) {
2594 wheel_accumulator
+= (short)HIWORD(wParam
);
2595 shift_pressed
=LOWORD(wParam
) & MK_SHIFT
;
2596 control_pressed
=LOWORD(wParam
) & MK_CONTROL
;
2599 wheel_accumulator
+= (int)wParam
;
2600 if (GetKeyboardState(keys
)!=0) {
2601 shift_pressed
=keys
[VK_SHIFT
]&0x80;
2602 control_pressed
=keys
[VK_CONTROL
]&0x80;
2606 /* process events when the threshold is reached */
2607 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
2610 /* reduce amount for next time */
2611 if (wheel_accumulator
> 0) {
2613 wheel_accumulator
-= WHEEL_DELTA
;
2614 } else if (wheel_accumulator
< 0) {
2616 wheel_accumulator
+= WHEEL_DELTA
;
2620 if (send_raw_mouse
&&
2621 !(cfg
.mouse_override
&& shift_pressed
)) {
2622 /* send a mouse-down followed by a mouse up */
2623 term_mouse(term
, b
, translate_button(b
),
2625 TO_CHR_X(X_POS(lParam
)),
2626 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2627 control_pressed
, is_alt_pressed());
2628 term_mouse(term
, b
, translate_button(b
),
2629 MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
2630 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2631 control_pressed
, is_alt_pressed());
2633 /* trigger a scroll */
2634 term_scroll(term
, 0,
2636 -term
->rows
/ 2 : term
->rows
/ 2);
2643 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2647 * Move the system caret. (We maintain one, even though it's
2648 * invisible, for the benefit of blind people: apparently some
2649 * helper software tracks the system caret, so we should arrange to
2652 void sys_cursor(void *frontend
, int x
, int y
)
2656 if (!term
->has_focus
) return;
2659 * Avoid gratuitously re-updating the cursor position and IMM
2660 * window if there's no actual change required.
2662 cx
= x
* font_width
+ offset_width
;
2663 cy
= y
* font_height
+ offset_height
;
2664 if (cx
== caret_x
&& cy
== caret_y
)
2669 sys_cursor_update();
2672 static void sys_cursor_update(void)
2677 if (!term
->has_focus
) return;
2679 if (caret_x
< 0 || caret_y
< 0)
2682 SetCaretPos(caret_x
, caret_y
);
2684 /* IMM calls on Win98 and beyond only */
2685 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2687 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2688 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2690 /* we should have the IMM functions */
2691 hIMC
= ImmGetContext(hwnd
);
2692 cf
.dwStyle
= CFS_POINT
;
2693 cf
.ptCurrentPos
.x
= caret_x
;
2694 cf
.ptCurrentPos
.y
= caret_y
;
2695 ImmSetCompositionWindow(hIMC
, &cf
);
2697 ImmReleaseContext(hwnd
, hIMC
);
2701 * Draw a line of text in the window, at given character
2702 * coordinates, in given attributes.
2704 * We are allowed to fiddle with the contents of `text'.
2706 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2707 unsigned long attr
, int lattr
)
2710 int nfg
, nbg
, nfont
;
2713 int force_manual_underline
= 0;
2714 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2715 int char_width
= fnt_width
;
2716 int text_adjust
= 0;
2717 static int *IpDx
= 0, IpDxLEN
= 0;
2719 if (attr
& ATTR_WIDE
)
2722 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2724 if (len
> IpDxLEN
) {
2726 IpDx
= snewn(len
+ 16, int);
2727 IpDxLEN
= (len
+ 16);
2729 for (i
= 0; i
< IpDxLEN
; i
++)
2730 IpDx
[i
] = char_width
;
2733 /* Only want the left half of double width lines */
2734 if (lattr
!= LATTR_NORM
&& x
*2 >= term
->cols
)
2742 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || term
->big_cursor
)) {
2743 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2744 attr
^= ATTR_CUR_XOR
;
2748 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2749 /* Assume a poorman font is borken in other ways too. */
2759 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2762 if (attr
& ATTR_NARROW
)
2763 nfont
|= FONT_NARROW
;
2765 /* Special hack for the VT100 linedraw glyphs. */
2766 if ((attr
& CSET_MASK
) == 0x2300) {
2767 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2768 switch ((unsigned char) (text
[0])) {
2770 text_adjust
= -2 * font_height
/ 5;
2773 text_adjust
= -1 * font_height
/ 5;
2776 text_adjust
= font_height
/ 5;
2779 text_adjust
= 2 * font_height
/ 5;
2782 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2785 text
[0] = (char) (ucsdata
.unitab_xterm
['q'] & CHAR_MASK
);
2786 attr
|= (ucsdata
.unitab_xterm
['q'] & CSET_MASK
);
2787 if (attr
& ATTR_UNDER
) {
2788 attr
&= ~ATTR_UNDER
;
2789 force_manual_underline
= 1;
2794 /* Anything left as an original character set is unprintable. */
2795 if (DIRECT_CHAR(attr
)) {
2798 memset(text
, 0xFD, len
);
2802 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2805 nfg
= ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2806 nfg
= 2 * (nfg
& 0xF) + (nfg
& 0x10 ?
1 : 0);
2807 nbg
= ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2808 nbg
= 2 * (nbg
& 0xF) + (nbg
& 0x10 ?
1 : 0);
2809 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2811 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2812 nfont
|= FONT_UNDERLINE
;
2813 another_font(nfont
);
2814 if (!fonts
[nfont
]) {
2815 if (nfont
& FONT_UNDERLINE
)
2816 force_manual_underline
= 1;
2817 /* Don't do the same for manual bold, it could be bad news. */
2819 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2821 another_font(nfont
);
2823 nfont
= FONT_NORMAL
;
2824 if (attr
& ATTR_REVERSE
) {
2829 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2831 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2835 SelectObject(hdc
, fonts
[nfont
]);
2836 SetTextColor(hdc
, fg
);
2837 SetBkColor(hdc
, bg
);
2838 SetBkMode(hdc
, OPAQUE
);
2841 line_box
.right
= x
+ char_width
* len
;
2842 line_box
.bottom
= y
+ font_height
;
2844 /* Only want the left half of double width lines */
2845 if (line_box
.right
> font_width
*term
->cols
+offset_width
)
2846 line_box
.right
= font_width
*term
->cols
+offset_width
;
2848 /* We're using a private area for direct to font. (512 chars.) */
2849 if (ucsdata
.dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2850 /* Ho Hum, dbcs fonts are a PITA! */
2851 /* To display on W9x I have to convert to UCS */
2852 static wchar_t *uni_buf
= 0;
2853 static int uni_len
= 0;
2855 if (len
> uni_len
) {
2858 uni_buf
= snewn(uni_len
, wchar_t);
2861 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2862 uni_buf
[nlen
] = 0xFFFD;
2863 if (IsDBCSLeadByteEx(ucsdata
.font_codepage
, (BYTE
) text
[mptr
])) {
2864 IpDx
[nlen
] += char_width
;
2865 MultiByteToWideChar(ucsdata
.font_codepage
, MB_USEGLYPHCHARS
,
2866 text
+mptr
, 2, uni_buf
+nlen
, 1);
2871 MultiByteToWideChar(ucsdata
.font_codepage
, MB_USEGLYPHCHARS
,
2872 text
+mptr
, 1, uni_buf
+nlen
, 1);
2880 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2881 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2882 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2883 SetBkMode(hdc
, TRANSPARENT
);
2884 ExtTextOutW(hdc
, x
- 1,
2885 y
- font_height
* (lattr
==
2886 LATTR_BOT
) + text_adjust
,
2887 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2891 } else if (DIRECT_FONT(attr
)) {
2893 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2894 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2895 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2896 SetBkMode(hdc
, TRANSPARENT
);
2898 /* GRR: This draws the character outside it's box and can leave
2899 * 'droppings' even with the clip box! I suppose I could loop it
2900 * one character at a time ... yuk.
2902 * Or ... I could do a test print with "W", and use +1 or -1 for this
2903 * shift depending on if the leftmost column is blank...
2905 ExtTextOut(hdc
, x
- 1,
2906 y
- font_height
* (lattr
==
2907 LATTR_BOT
) + text_adjust
,
2908 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2911 /* And 'normal' unicode characters */
2912 static WCHAR
*wbuf
= NULL
;
2913 static int wlen
= 0;
2918 wbuf
= snewn(wlen
, WCHAR
);
2920 for (i
= 0; i
< len
; i
++)
2921 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2924 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2925 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2927 /* And the shadow bold hack. */
2928 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2929 SetBkMode(hdc
, TRANSPARENT
);
2930 ExtTextOutW(hdc
, x
- 1,
2931 y
- font_height
* (lattr
==
2932 LATTR_BOT
) + text_adjust
,
2933 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2936 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2937 (und_mode
== UND_LINE
2938 && (attr
& ATTR_UNDER
)))) {
2941 if (lattr
== LATTR_BOT
)
2942 dec
= dec
* 2 - font_height
;
2944 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2945 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2946 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2947 oldpen
= SelectObject(hdc
, oldpen
);
2948 DeleteObject(oldpen
);
2952 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2953 unsigned long attr
, int lattr
)
2959 int ctype
= cfg
.cursor_type
;
2961 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || term
->big_cursor
)) {
2962 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2963 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2967 attr
|= TATTR_RIGHTCURS
;
2970 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2971 if (attr
& ATTR_WIDE
)
2978 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || term
->big_cursor
)) {
2981 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2982 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2983 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2984 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2985 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2986 Polyline(hdc
, pts
, 5);
2987 oldpen
= SelectObject(hdc
, oldpen
);
2988 DeleteObject(oldpen
);
2989 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2990 int startx
, starty
, dx
, dy
, length
, i
;
2993 starty
= y
+ descent
;
2996 length
= char_width
;
2999 if (attr
& TATTR_RIGHTCURS
)
3000 xadjust
= char_width
- 1;
3001 startx
= x
+ xadjust
;
3005 length
= font_height
;
3007 if (attr
& TATTR_ACTCURS
) {
3010 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
3011 MoveToEx(hdc
, startx
, starty
, NULL
);
3012 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
3013 oldpen
= SelectObject(hdc
, oldpen
);
3014 DeleteObject(oldpen
);
3016 for (i
= 0; i
< length
; i
++) {
3018 SetPixel(hdc
, startx
, starty
, colours
[23]);
3027 /* This function gets the actual width of a character in the normal font.
3029 int char_width(Context ctx
, int uc
) {
3033 /* If the font max is the same as the font ave width then this
3034 * function is a no-op.
3036 if (!font_dualwidth
) return 1;
3038 switch (uc
& CSET_MASK
) {
3040 uc
= ucsdata
.unitab_line
[uc
& 0xFF];
3043 uc
= ucsdata
.unitab_xterm
[uc
& 0xFF];
3046 uc
= ucsdata
.unitab_scoacs
[uc
& 0xFF];
3049 if (DIRECT_FONT(uc
)) {
3050 if (ucsdata
.dbcs_screenfont
) return 1;
3052 /* Speedup, I know of no font where ascii is the wrong width */
3053 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
3056 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
3057 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
3058 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
3059 another_font(FONT_OEM
);
3060 if (!fonts
[FONT_OEM
]) return 0;
3062 SelectObject(hdc
, fonts
[FONT_OEM
]);
3066 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
3067 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
3070 /* Speedup, I know of no font where ascii is the wrong width */
3071 if (uc
>= ' ' && uc
<= '~') return 1;
3073 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
3074 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
3075 /* Okay that one worked */ ;
3076 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
3077 /* This should work on 9x too, but it's "less accurate" */ ;
3082 ibuf
+= font_width
/ 2 -1;
3089 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
3090 * codes. Returns number of bytes used or zero to drop the message
3091 * or -1 to forward the message to windows.
3093 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
3094 unsigned char *output
)
3097 int scan
, left_alt
= 0, key_down
, shift_state
;
3099 unsigned char *p
= output
;
3100 static int alt_sum
= 0;
3102 HKL kbd_layout
= GetKeyboardLayout(0);
3104 static WORD keys
[3];
3105 static int compose_char
= 0;
3106 static WPARAM compose_key
= 0;
3108 r
= GetKeyboardState(keystate
);
3110 memset(keystate
, 0, sizeof(keystate
));
3113 #define SHOW_TOASCII_RESULT
3114 { /* Tell us all about key events */
3115 static BYTE oldstate
[256];
3116 static int first
= 1;
3120 memcpy(oldstate
, keystate
, sizeof(oldstate
));
3123 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
3125 } else if ((HIWORD(lParam
) & KF_UP
)
3126 && scan
== (HIWORD(lParam
) & 0xFF)) {
3130 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
3131 debug(("K_F%d", wParam
+ 1 - VK_F1
));
3144 debug(("VK_%02x", wParam
));
3146 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
3148 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
3150 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
3151 if (ch
>= ' ' && ch
<= '~')
3152 debug((", '%c'", ch
));
3154 debug((", $%02x", ch
));
3157 debug((", KB0=%02x", keys
[0]));
3159 debug((", KB1=%02x", keys
[1]));
3161 debug((", KB2=%02x", keys
[2]));
3163 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
3165 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
3167 if ((HIWORD(lParam
) & KF_EXTENDED
))
3169 if ((HIWORD(lParam
) & KF_UP
))
3173 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
3174 else if ((HIWORD(lParam
) & KF_UP
))
3175 oldstate
[wParam
& 0xFF] ^= 0x80;
3177 oldstate
[wParam
& 0xFF] ^= 0x81;
3179 for (ch
= 0; ch
< 256; ch
++)
3180 if (oldstate
[ch
] != keystate
[ch
])
3181 debug((", M%02x=%02x", ch
, keystate
[ch
]));
3183 memcpy(oldstate
, keystate
, sizeof(oldstate
));
3187 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
3188 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
3192 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
3193 if ((cfg
.funky_type
== 3 ||
3194 (cfg
.funky_type
<= 1 && term
->app_keypad_keys
&&
3196 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
3198 wParam
= VK_EXECUTE
;
3200 /* UnToggle NUMLock */
3201 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
3202 keystate
[VK_NUMLOCK
] ^= 1;
3205 /* And write back the 'adjusted' state */
3206 SetKeyboardState(keystate
);
3209 /* Disable Auto repeat if required */
3210 if (term
->repeat_off
&&
3211 (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
3214 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
3217 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
3219 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
3220 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
3221 if (cfg
.ctrlaltkeys
)
3222 keystate
[VK_MENU
] = 0;
3224 keystate
[VK_RMENU
] = 0x80;
3229 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
3230 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
3231 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
3233 /* Note if AltGr was pressed and if it was used as a compose key */
3234 if (!compose_state
) {
3235 compose_key
= 0x100;
3236 if (cfg
.compose_key
) {
3237 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
3238 compose_key
= wParam
;
3240 if (wParam
== VK_APPS
)
3241 compose_key
= wParam
;
3244 if (wParam
== compose_key
) {
3245 if (compose_state
== 0
3246 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
3248 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
3252 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
3255 if (compose_state
> 1 && left_alt
)
3258 /* Sanitize the number pad if not using a PC NumPad */
3259 if (left_alt
|| (term
->app_keypad_keys
&& !cfg
.no_applic_k
3260 && cfg
.funky_type
!= 2)
3261 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
3262 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
3266 nParam
= VK_NUMPAD0
;
3269 nParam
= VK_NUMPAD1
;
3272 nParam
= VK_NUMPAD2
;
3275 nParam
= VK_NUMPAD3
;
3278 nParam
= VK_NUMPAD4
;
3281 nParam
= VK_NUMPAD5
;
3284 nParam
= VK_NUMPAD6
;
3287 nParam
= VK_NUMPAD7
;
3290 nParam
= VK_NUMPAD8
;
3293 nParam
= VK_NUMPAD9
;
3296 nParam
= VK_DECIMAL
;
3300 if (keystate
[VK_NUMLOCK
] & 1)
3307 /* If a key is pressed and AltGr is not active */
3308 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
3309 /* Okay, prepare for most alts then ... */
3313 /* Lets see if it's a pattern we know all about ... */
3314 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
3315 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
3318 if (wParam
== VK_PRIOR
&& shift_state
== 2) {
3319 SendMessage(hwnd
, WM_VSCROLL
, SB_LINEUP
, 0);
3322 if (wParam
== VK_NEXT
&& shift_state
== 1) {
3323 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
3326 if (wParam
== VK_NEXT
&& shift_state
== 2) {
3327 SendMessage(hwnd
, WM_VSCROLL
, SB_LINEDOWN
, 0);
3330 if (wParam
== VK_INSERT
&& shift_state
== 1) {
3331 term_do_paste(term
);
3334 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
3337 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
3338 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
3341 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
&&
3342 (cfg
.resize_action
!= RESIZE_DISABLED
)) {
3343 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) != KF_REPEAT
)
3347 /* Control-Numlock for app-keypad mode switch */
3348 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
3349 term
->app_keypad_keys
^= 1;
3353 /* Nethack keypad */
3354 if (cfg
.nethack_keypad
&& !left_alt
) {
3357 *p
++ = shift_state ?
'B' : 'b';
3360 *p
++ = shift_state ?
'J' : 'j';
3363 *p
++ = shift_state ?
'N' : 'n';
3366 *p
++ = shift_state ?
'H' : 'h';
3369 *p
++ = shift_state ?
'.' : '.';
3372 *p
++ = shift_state ?
'L' : 'l';
3375 *p
++ = shift_state ?
'Y' : 'y';
3378 *p
++ = shift_state ?
'K' : 'k';
3381 *p
++ = shift_state ?
'U' : 'u';
3386 /* Application Keypad */
3390 if (cfg
.funky_type
== 3 ||
3391 (cfg
.funky_type
<= 1 &&
3392 term
->app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3406 if (term
->app_keypad_keys
&& !cfg
.no_applic_k
)
3443 if (cfg
.funky_type
== 2) {
3448 } else if (shift_state
)
3455 if (cfg
.funky_type
== 2)
3459 if (cfg
.funky_type
== 2)
3463 if (cfg
.funky_type
== 2)
3468 if (HIWORD(lParam
) & KF_EXTENDED
)
3473 if (term
->vt52_mode
) {
3474 if (xkey
>= 'P' && xkey
<= 'S')
3475 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3477 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3479 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3484 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3485 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3489 if (wParam
== VK_BACK
&& shift_state
== 1) { /* Shift Backspace */
3490 /* We do the opposite of what is configured */
3491 *p
++ = (cfg
.bksp_is_delete ?
0x08 : 0x7F);
3495 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3501 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3505 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3509 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3514 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3519 /* Control-2 to Control-8 are special */
3520 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3521 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3524 if (shift_state
== 2 && (wParam
== 0xBD || wParam
== 0xBF)) {
3528 if (shift_state
== 2 && wParam
== 0xDF) {
3532 if (shift_state
== 0 && wParam
== VK_RETURN
&& term
->cr_lf_return
) {
3539 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3540 * for integer decimal nn.)
3542 * We also deal with the weird ones here. Linux VCs replace F1
3543 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3544 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3550 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3553 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3556 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3559 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3562 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3565 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3568 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3571 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3574 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3577 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3610 if ((shift_state
&2) == 0) switch (wParam
) {
3630 /* Reorder edit keys to physical order */
3631 if (cfg
.funky_type
== 3 && code
<= 6)
3632 code
= "\0\2\1\4\5\3\6"[code
];
3634 if (term
->vt52_mode
&& code
> 0 && code
<= 6) {
3635 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3639 if (cfg
.funky_type
== 5 && /* SCO function keys */
3640 code
>= 11 && code
<= 34) {
3641 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3644 case VK_F1
: index
= 0; break;
3645 case VK_F2
: index
= 1; break;
3646 case VK_F3
: index
= 2; break;
3647 case VK_F4
: index
= 3; break;
3648 case VK_F5
: index
= 4; break;
3649 case VK_F6
: index
= 5; break;
3650 case VK_F7
: index
= 6; break;
3651 case VK_F8
: index
= 7; break;
3652 case VK_F9
: index
= 8; break;
3653 case VK_F10
: index
= 9; break;
3654 case VK_F11
: index
= 10; break;
3655 case VK_F12
: index
= 11; break;
3657 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3658 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3659 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3662 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3663 code
>= 1 && code
<= 6) {
3664 char codes
[] = "HL.FIG";
3668 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3672 if ((term
->vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3678 if (term
->vt52_mode
)
3679 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3682 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3685 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3686 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3689 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3690 if (term
->vt52_mode
)
3691 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3693 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3696 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3697 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3701 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3706 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3707 * some reason seems to send VK_CLEAR to Windows...).
3729 if (term
->vt52_mode
)
3730 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3732 int app_flg
= (term
->app_cursor_keys
&& !cfg
.no_applic_c
);
3735 * RDB: VT100 & VT102 manuals both state the
3736 * app cursor keys only work if the app keypad
3739 * SGT: That may well be true, but xterm
3740 * disagrees and so does at least one
3741 * application, so I've #if'ed this out and the
3742 * behaviour is back to PuTTY's original: app
3743 * cursor and app keypad are independently
3744 * switchable modes. If anyone complains about
3745 * _this_ I'll have to put in a configurable
3748 if (!term
->app_keypad_keys
)
3751 /* Useful mapping of Ctrl-arrows */
3752 if (shift_state
== 2)
3756 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3758 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3765 * Finally, deal with Return ourselves. (Win95 seems to
3766 * foul it up when Alt is pressed, for some reason.)
3768 if (wParam
== VK_RETURN
) { /* Return */
3774 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3775 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3780 /* Okay we've done everything interesting; let windows deal with
3781 * the boring stuff */
3785 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3786 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3788 keystate
[VK_CAPITAL
] = 0;
3791 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3792 #ifdef SHOW_TOASCII_RESULT
3793 if (r
== 1 && !key_down
) {
3795 if (in_utf(term
) || ucsdata
.dbcs_screenfont
)
3796 debug((", (U+%04x)", alt_sum
));
3798 debug((", LCH(%d)", alt_sum
));
3800 debug((", ACH(%d)", keys
[0]));
3805 for (r1
= 0; r1
< r
; r1
++) {
3806 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3815 * Interrupt an ongoing paste. I'm not sure this is
3816 * sensible, but for the moment it's preferable to
3817 * having to faff about buffering things.
3822 for (i
= 0; i
< r
; i
++) {
3823 unsigned char ch
= (unsigned char) keys
[i
];
3825 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3830 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3834 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3835 MessageBeep(MB_ICONHAND
);
3839 term_seen_key_event(term
);
3840 luni_send(ldisc
, &keybuf
, 1, 1);
3848 if (in_utf(term
) || ucsdata
.dbcs_screenfont
) {
3850 term_seen_key_event(term
);
3851 luni_send(ldisc
, &keybuf
, 1, 1);
3853 ch
= (char) alt_sum
;
3855 * We need not bother about stdin
3856 * backlogs here, because in GUI PuTTY
3857 * we can't do anything about it
3858 * anyway; there's no means of asking
3859 * Windows to hold off on KEYDOWN
3860 * messages. We _have_ to buffer
3861 * everything we're sent.
3863 term_seen_key_event(term
);
3864 ldisc_send(ldisc
, &ch
, 1, 1);
3868 term_seen_key_event(term
);
3869 lpage_send(ldisc
, kbd_codepage
, &ch
, 1, 1);
3871 if(capsOn
&& ch
< 0x80) {
3874 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3875 term_seen_key_event(term
);
3876 luni_send(ldisc
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3881 term_seen_key_event(term
);
3882 lpage_send(ldisc
, kbd_codepage
,
3883 cbuf
+!left_alt
, 1+!!left_alt
, 1);
3889 /* This is so the ALT-Numpad and dead keys work correctly. */
3894 /* If we're definitly not building up an ALT-54321 then clear it */
3897 /* If we will be using alt_sum fix the 256s */
3898 else if (keys
[0] && (in_utf(term
) || ucsdata
.dbcs_screenfont
))
3903 * ALT alone may or may not want to bring up the System menu.
3904 * If it's not meant to, we return 0 on presses or releases of
3905 * ALT, to show that we've swallowed the keystroke. Otherwise
3906 * we return -1, which means Windows will give the keystroke
3907 * its default handling (i.e. bring up the System menu).
3909 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3915 void request_paste(void *frontend
)
3918 * In Windows, pasting is synchronous: we can read the
3919 * clipboard with no difficulty, so request_paste() can just go
3922 term_do_paste(term
);
3925 void set_title(void *frontend
, char *title
)
3928 window_name
= snewn(1 + strlen(title
), char);
3929 strcpy(window_name
, title
);
3930 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3931 SetWindowText(hwnd
, title
);
3934 void set_icon(void *frontend
, char *title
)
3937 icon_name
= snewn(1 + strlen(title
), char);
3938 strcpy(icon_name
, title
);
3939 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3940 SetWindowText(hwnd
, title
);
3943 void set_sbar(void *frontend
, int total
, int start
, int page
)
3947 if (is_full_screen() ?
!cfg
.scrollbar_in_fullscreen
: !cfg
.scrollbar
)
3950 si
.cbSize
= sizeof(si
);
3951 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3953 si
.nMax
= total
- 1;
3957 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3960 Context
get_ctx(void *frontend
)
3966 SelectPalette(hdc
, pal
, FALSE
);
3972 void free_ctx(Context ctx
)
3974 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3975 ReleaseDC(hwnd
, ctx
);
3978 static void real_palette_set(int n
, int r
, int g
, int b
)
3981 logpal
->palPalEntry
[n
].peRed
= r
;
3982 logpal
->palPalEntry
[n
].peGreen
= g
;
3983 logpal
->palPalEntry
[n
].peBlue
= b
;
3984 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3985 colours
[n
] = PALETTERGB(r
, g
, b
);
3986 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3988 colours
[n
] = RGB(r
, g
, b
);
3991 void palette_set(void *frontend
, int n
, int r
, int g
, int b
)
3993 static const int first
[21] = {
3994 0, 2, 4, 6, 8, 10, 12, 14,
3995 1, 3, 5, 7, 9, 11, 13, 15,
3998 real_palette_set(first
[n
], r
, g
, b
);
4000 real_palette_set(first
[n
] + 1, r
, g
, b
);
4002 HDC hdc
= get_ctx(frontend
);
4003 UnrealizeObject(pal
);
4004 RealizePalette(hdc
);
4009 void palette_reset(void *frontend
)
4013 for (i
= 0; i
< NCOLOURS
; i
++) {
4015 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
4016 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
4017 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
4018 logpal
->palPalEntry
[i
].peFlags
= 0;
4019 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
4020 defpal
[i
].rgbtGreen
,
4021 defpal
[i
].rgbtBlue
);
4023 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
4024 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
4029 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
4030 hdc
= get_ctx(frontend
);
4031 RealizePalette(hdc
);
4036 void write_aclip(void *frontend
, char *data
, int len
, int must_deselect
)
4041 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
4044 lock
= GlobalLock(clipdata
);
4047 memcpy(lock
, data
, len
);
4048 ((unsigned char *) lock
)[len
] = 0;
4049 GlobalUnlock(clipdata
);
4052 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
4054 if (OpenClipboard(hwnd
)) {
4056 SetClipboardData(CF_TEXT
, clipdata
);
4059 GlobalFree(clipdata
);
4062 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
4066 * Note: unlike write_aclip() this will not append a nul.
4068 void write_clip(void *frontend
, wchar_t * data
, int len
, int must_deselect
)
4070 HGLOBAL clipdata
, clipdata2
, clipdata3
;
4072 void *lock
, *lock2
, *lock3
;
4074 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
4076 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
4077 len
* sizeof(wchar_t));
4078 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
4080 if (!clipdata
|| !clipdata2
) {
4082 GlobalFree(clipdata
);
4084 GlobalFree(clipdata2
);
4087 if (!(lock
= GlobalLock(clipdata
)))
4089 if (!(lock2
= GlobalLock(clipdata2
)))
4092 memcpy(lock
, data
, len
* sizeof(wchar_t));
4093 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
4095 if (cfg
.rtf_paste
) {
4096 wchar_t unitab
[256];
4098 unsigned char *tdata
= (unsigned char *)lock2
;
4099 wchar_t *udata
= (wchar_t *)lock
;
4100 int rtflen
= 0, uindex
= 0, tindex
= 0;
4102 int multilen
, blen
, alen
, totallen
, i
;
4103 char before
[16], after
[4];
4105 get_unitab(CP_ACP
, unitab
, 0);
4107 rtfsize
= 100 + strlen(cfg
.font
.name
);
4108 rtf
= snewn(rtfsize
, char);
4109 sprintf(rtf
, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
4110 GetACP(), cfg
.font
.name
);
4111 rtflen
= strlen(rtf
);
4114 * We want to construct a piece of RTF that specifies the
4115 * same Unicode text. To do this we will read back in
4116 * parallel from the Unicode data in `udata' and the
4117 * non-Unicode data in `tdata'. For each character in
4118 * `tdata' which becomes the right thing in `udata' when
4119 * looked up in `unitab', we just copy straight over from
4120 * tdata. For each one that doesn't, we must WCToMB it
4121 * individually and produce a \u escape sequence.
4123 * It would probably be more robust to just bite the bullet
4124 * and WCToMB each individual Unicode character one by one,
4125 * then MBToWC each one back to see if it was an accurate
4126 * translation; but that strikes me as a horrifying number
4127 * of Windows API calls so I want to see if this faster way
4128 * will work. If it screws up badly we can always revert to
4129 * the simple and slow way.
4131 while (tindex
< len2
&& uindex
< len
&&
4132 tdata
[tindex
] && udata
[uindex
]) {
4133 if (tindex
+ 1 < len2
&&
4134 tdata
[tindex
] == '\r' &&
4135 tdata
[tindex
+1] == '\n') {
4139 if (unitab
[tdata
[tindex
]] == udata
[uindex
]) {
4145 multilen
= WideCharToMultiByte(CP_ACP
, 0, unitab
+uindex
, 1,
4146 NULL
, 0, NULL
, NULL
);
4147 if (multilen
!= 1) {
4148 blen
= sprintf(before
, "{\\uc%d\\u%d", multilen
,
4150 alen
= 1; strcpy(after
, "}");
4152 blen
= sprintf(before
, "\\u%d", udata
[uindex
]);
4153 alen
= 0; after
[0] = '\0';
4156 assert(tindex
+ multilen
<= len2
);
4157 totallen
= blen
+ alen
;
4158 for (i
= 0; i
< multilen
; i
++) {
4159 if (tdata
[tindex
+i
] == '\\' ||
4160 tdata
[tindex
+i
] == '{' ||
4161 tdata
[tindex
+i
] == '}')
4163 else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A)
4164 totallen
+= 6; /* \par\r\n */
4165 else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20)
4171 if (rtfsize
< rtflen
+ totallen
+ 3) {
4172 rtfsize
= rtflen
+ totallen
+ 512;
4173 rtf
= sresize(rtf
, rtfsize
, char);
4176 strcpy(rtf
+ rtflen
, before
); rtflen
+= blen
;
4177 for (i
= 0; i
< multilen
; i
++) {
4178 if (tdata
[tindex
+i
] == '\\' ||
4179 tdata
[tindex
+i
] == '{' ||
4180 tdata
[tindex
+i
] == '}') {
4181 rtf
[rtflen
++] = '\\';
4182 rtf
[rtflen
++] = tdata
[tindex
+i
];
4183 } else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A) {
4184 rtflen
+= sprintf(rtf
+rtflen
, "\\par\r\n");
4185 } else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20) {
4186 rtflen
+= sprintf(rtf
+rtflen
, "\\'%02x", tdata
[tindex
+i
]);
4188 rtf
[rtflen
++] = tdata
[tindex
+i
];
4191 strcpy(rtf
+ rtflen
, after
); rtflen
+= alen
;
4197 strcpy(rtf
+ rtflen
, "}");
4200 clipdata3
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, rtflen
);
4201 if (clipdata3
&& (lock3
= GlobalLock(clipdata3
)) != NULL
) {
4203 GlobalUnlock(clipdata3
);
4209 GlobalUnlock(clipdata
);
4210 GlobalUnlock(clipdata2
);
4213 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
4215 if (OpenClipboard(hwnd
)) {
4217 SetClipboardData(CF_UNICODETEXT
, clipdata
);
4218 SetClipboardData(CF_TEXT
, clipdata2
);
4220 SetClipboardData(RegisterClipboardFormat(CF_RTF
), clipdata3
);
4223 GlobalFree(clipdata
);
4224 GlobalFree(clipdata2
);
4228 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
4231 void get_clip(void *frontend
, wchar_t ** p
, int *len
)
4233 static HGLOBAL clipdata
= NULL
;
4234 static wchar_t *converted
= 0;
4243 GlobalUnlock(clipdata
);
4246 } else if (OpenClipboard(NULL
)) {
4247 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
4249 *p
= GlobalLock(clipdata
);
4251 for (p2
= *p
; *p2
; p2
++);
4255 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
4259 s
= GlobalLock(clipdata
);
4260 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
4261 *p
= converted
= snewn(i
, wchar_t);
4262 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
4275 * Move `lines' lines from position `from' to position `to' in the
4278 void optimised_move(void *frontend
, int to
, int from
, int lines
)
4283 min
= (to
< from ? to
: from
);
4284 max
= to
+ from
- min
;
4286 r
.left
= offset_width
;
4287 r
.right
= offset_width
+ term
->cols
* font_width
;
4288 r
.top
= offset_height
+ min
* font_height
;
4289 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
4290 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
4295 * Print a message box and perform a fatal exit.
4297 void fatalbox(char *fmt
, ...)
4303 vsprintf(stuff
, fmt
, ap
);
4305 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
4310 * Print a modal (Really Bad) message box and perform a fatal exit.
4312 void modalfatalbox(char *fmt
, ...)
4318 vsprintf(stuff
, fmt
, ap
);
4320 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error",
4321 MB_SYSTEMMODAL
| MB_ICONERROR
| MB_OK
);
4326 * Manage window caption / taskbar flashing, if enabled.
4327 * 0 = stop, 1 = maintain, 2 = start
4329 static void flash_window(int mode
)
4331 static long last_flash
= 0;
4332 static int flashing
= 0;
4333 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
4336 FlashWindow(hwnd
, FALSE
);
4340 } else if (mode
== 2) {
4343 last_flash
= GetTickCount();
4345 FlashWindow(hwnd
, TRUE
);
4348 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
4351 long now
= GetTickCount();
4352 long fdiff
= now
- last_flash
;
4353 if (fdiff
< 0 || fdiff
> 450) {
4355 FlashWindow(hwnd
, TRUE
); /* toggle */
4364 void beep(void *frontend
, int mode
)
4366 if (mode
== BELL_DEFAULT
) {
4368 * For MessageBeep style bells, we want to be careful of
4369 * timing, because they don't have the nice property of
4370 * PlaySound bells that each one cancels the previous
4371 * active one. So we limit the rate to one per 50ms or so.
4373 static long lastbeep
= 0;
4376 beepdiff
= GetTickCount() - lastbeep
;
4377 if (beepdiff
>= 0 && beepdiff
< 50)
4381 * The above MessageBeep call takes time, so we record the
4382 * time _after_ it finishes rather than before it starts.
4384 lastbeep
= GetTickCount();
4385 } else if (mode
== BELL_WAVEFILE
) {
4386 if (!PlaySound(cfg
.bell_wavefile
.path
, NULL
,
4387 SND_ASYNC
| SND_FILENAME
)) {
4388 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
4389 sprintf(buf
, "Unable to play sound file\n%s\n"
4390 "Using default sound instead", cfg
.bell_wavefile
);
4391 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
4392 MB_OK
| MB_ICONEXCLAMATION
);
4393 cfg
.beep
= BELL_DEFAULT
;
4396 /* Otherwise, either visual bell or disabled; do nothing here */
4397 if (!term
->has_focus
) {
4398 flash_window(2); /* start */
4403 * Minimise or restore the window in response to a server-side
4406 void set_iconic(void *frontend
, int iconic
)
4408 if (IsIconic(hwnd
)) {
4410 ShowWindow(hwnd
, SW_RESTORE
);
4413 ShowWindow(hwnd
, SW_MINIMIZE
);
4418 * Move the window in response to a server-side request.
4420 void move_window(void *frontend
, int x
, int y
)
4422 if (cfg
.resize_action
== RESIZE_DISABLED
||
4423 cfg
.resize_action
== RESIZE_FONT
||
4427 SetWindowPos(hwnd
, NULL
, x
, y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
);
4431 * Move the window to the top or bottom of the z-order in response
4432 * to a server-side request.
4434 void set_zorder(void *frontend
, int top
)
4436 if (cfg
.alwaysontop
)
4437 return; /* ignore */
4438 SetWindowPos(hwnd
, top ? HWND_TOP
: HWND_BOTTOM
, 0, 0, 0, 0,
4439 SWP_NOMOVE
| SWP_NOSIZE
);
4443 * Refresh the window in response to a server-side request.
4445 void refresh_window(void *frontend
)
4447 InvalidateRect(hwnd
, NULL
, TRUE
);
4451 * Maximise or restore the window in response to a server-side
4454 void set_zoomed(void *frontend
, int zoomed
)
4456 if (IsZoomed(hwnd
)) {
4458 ShowWindow(hwnd
, SW_RESTORE
);
4461 ShowWindow(hwnd
, SW_MAXIMIZE
);
4466 * Report whether the window is iconic, for terminal reports.
4468 int is_iconic(void *frontend
)
4470 return IsIconic(hwnd
);
4474 * Report the window's position, for terminal reports.
4476 void get_window_pos(void *frontend
, int *x
, int *y
)
4479 GetWindowRect(hwnd
, &r
);
4485 * Report the window's pixel size, for terminal reports.
4487 void get_window_pixels(void *frontend
, int *x
, int *y
)
4490 GetWindowRect(hwnd
, &r
);
4491 *x
= r
.right
- r
.left
;
4492 *y
= r
.bottom
- r
.top
;
4496 * Return the window or icon title.
4498 char *get_window_title(void *frontend
, int icon
)
4500 return icon ? icon_name
: window_name
;
4504 * See if we're in full-screen mode.
4506 int is_full_screen()
4508 if (!IsZoomed(hwnd
))
4510 if (GetWindowLong(hwnd
, GWL_STYLE
) & WS_CAPTION
)
4515 /* Get the rect/size of a full screen window using the nearest available
4516 * monitor in multimon systems; default to something sensible if only
4517 * one monitor is present. */
4518 static int get_fullscreen_rect(RECT
* ss
)
4520 #ifdef MONITOR_DEFAULTTONEAREST
4523 mon
= MonitorFromWindow(hwnd
, MONITOR_DEFAULTTONEAREST
);
4524 mi
.cbSize
= sizeof(mi
);
4525 GetMonitorInfo(mon
, &mi
);
4527 /* structure copy */
4531 /* could also use code like this:
4532 ss->left = ss->top = 0;
4533 ss->right = GetSystemMetrics(SM_CXSCREEN);
4534 ss->bottom = GetSystemMetrics(SM_CYSCREEN);
4536 return GetClientRect(GetDesktopWindow(), ss
);
4542 * Go full-screen. This should only be called when we are already
4545 void make_full_screen()
4550 assert(IsZoomed(hwnd
));
4552 if (is_full_screen())
4555 /* Remove the window furniture. */
4556 style
= GetWindowLong(hwnd
, GWL_STYLE
);
4557 style
&= ~(WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
);
4558 if (cfg
.scrollbar_in_fullscreen
)
4559 style
|= WS_VSCROLL
;
4561 style
&= ~WS_VSCROLL
;
4562 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4564 /* Resize ourselves to exactly cover the nearest monitor. */
4565 get_fullscreen_rect(&ss
);
4566 SetWindowPos(hwnd
, HWND_TOP
, ss
.left
, ss
.top
,
4571 /* Tick the menu item in the System menu. */
4572 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4577 * Clear the full-screen attributes.
4579 void clear_full_screen()
4581 DWORD oldstyle
, style
;
4583 /* Reinstate the window furniture. */
4584 style
= oldstyle
= GetWindowLong(hwnd
, GWL_STYLE
);
4585 style
|= WS_CAPTION
| WS_BORDER
;
4586 if (cfg
.resize_action
== RESIZE_DISABLED
)
4587 style
&= ~WS_THICKFRAME
;
4589 style
|= WS_THICKFRAME
;
4591 style
|= WS_VSCROLL
;
4593 style
&= ~WS_VSCROLL
;
4594 if (style
!= oldstyle
) {
4595 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4596 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
4597 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
4601 /* Untick the menu item in the System menu. */
4602 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4607 * Toggle full-screen mode.
4609 void flip_full_screen()
4611 if (is_full_screen()) {
4612 ShowWindow(hwnd
, SW_RESTORE
);
4613 } else if (IsZoomed(hwnd
)) {
4616 SendMessage(hwnd
, WM_FULLSCR_ON_MAX
, 0, 0);
4617 ShowWindow(hwnd
, SW_MAXIMIZE
);
4621 void frontend_keypress(void *handle
)
4624 * Keypress termination in non-Close-On-Exit mode is not
4625 * currently supported in PuTTY proper, because the window
4626 * always has a perfectly good Close button anyway. So we do