16 #define COMPILE_MULTIMON_STUBS
27 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
34 #define IDM_SHOWLOG 0x0010
35 #define IDM_NEWSESS 0x0020
36 #define IDM_DUPSESS 0x0030
37 #define IDM_RECONF 0x0040
38 #define IDM_CLRSB 0x0050
39 #define IDM_RESET 0x0060
40 #define IDM_TEL_AYT 0x0070
41 #define IDM_TEL_BRK 0x0080
42 #define IDM_TEL_SYNCH 0x0090
43 #define IDM_TEL_EC 0x00a0
44 #define IDM_TEL_EL 0x00b0
45 #define IDM_TEL_GA 0x00c0
46 #define IDM_TEL_NOP 0x00d0
47 #define IDM_TEL_ABORT 0x00e0
48 #define IDM_TEL_AO 0x00f0
49 #define IDM_TEL_IP 0x0100
50 #define IDM_TEL_SUSP 0x0110
51 #define IDM_TEL_EOR 0x0120
52 #define IDM_TEL_EOF 0x0130
53 #define IDM_HELP 0x0140
54 #define IDM_ABOUT 0x0150
55 #define IDM_SAVEDSESS 0x0160
56 #define IDM_COPYALL 0x0170
57 #define IDM_FULLSCREEN 0x0180
59 #define IDM_SESSLGP 0x0250 /* log type printable */
60 #define IDM_SESSLGA 0x0260 /* log type all chars */
61 #define IDM_SESSLGE 0x0270 /* log end */
62 #define IDM_SAVED_MIN 0x1000
63 #define IDM_SAVED_MAX 0x2000
65 #define WM_IGNORE_CLIP (WM_XUSER + 2)
66 #define WM_FULLSCR_ON_MAX (WM_XUSER + 3)
68 /* Needed for Chinese support and apparently not always defined. */
70 #define VK_PROCESSKEY 0xE5
73 /* Mouse wheel support. */
75 #define WM_MOUSEWHEEL 0x020A /* not defined in earlier SDKs */
78 #define WHEEL_DELTA 120
81 static LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
82 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
83 unsigned char *output
);
84 static void cfgtopalette(void);
85 static void init_palette(void);
86 static void init_fonts(int, int);
87 static void another_font(int);
88 static void deinit_fonts(void);
89 static void set_input_locale(HKL
);
90 static int do_mouse_wheel_msg(UINT message
, WPARAM wParam
, LPARAM lParam
);
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;
119 static Backend
*back
;
120 static void *backhandle
;
122 #define FONT_NORMAL 0
124 #define FONT_UNDERLINE 2
125 #define FONT_BOLDUND 3
126 #define FONT_WIDE 0x04
127 #define FONT_HIGH 0x08
128 #define FONT_NARROW 0x10
130 #define FONT_OEM 0x20
131 #define FONT_OEMBOLD 0x21
132 #define FONT_OEMUND 0x22
133 #define FONT_OEMBOLDUND 0x23
135 #define FONT_MAXNO 0x2F
137 static HFONT fonts
[FONT_MAXNO
];
138 static LOGFONT lfont
;
139 static int fontflag
[FONT_MAXNO
];
141 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
149 static COLORREF colours
[NCOLOURS
];
151 static LPLOGPALETTE logpal
;
152 static RGBTRIPLE defpal
[NCOLOURS
];
156 static HBITMAP caretbm
;
158 static int dbltime
, lasttime
, lastact
;
159 static Mouse_Button lastbtn
;
161 /* this allows xterm-style mouse handling. */
162 static int send_raw_mouse
= 0;
163 static int wheel_accumulator
= 0;
165 static char *window_name
, *icon_name
;
167 static int compose_state
= 0;
169 static int wsa_started
= 0;
171 static OSVERSIONINFO osVersion
;
173 static UINT wm_mousewheel
= WM_MOUSEWHEEL
;
175 /* Dummy routine, only required in plink. */
176 void ldisc_update(void *frontend
, int echo
, int edit
)
180 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
182 static char appname
[] = "PuTTY";
187 int guess_width
, guess_height
;
190 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
192 winsock_ver
= MAKEWORD(1, 1);
193 if (WSAStartup(winsock_ver
, &wsadata
)) {
194 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
195 MB_OK
| MB_ICONEXCLAMATION
);
198 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
199 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
200 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
205 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
208 InitCommonControls();
210 /* Ensure a Maximize setting in Explorer doesn't maximise the
215 ZeroMemory(&osVersion
, sizeof(osVersion
));
216 osVersion
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
217 if (!GetVersionEx ( (OSVERSIONINFO
*) &osVersion
)) {
218 MessageBox(NULL
, "Windows refuses to report a version",
219 "PuTTY Fatal Error", MB_OK
| MB_ICONEXCLAMATION
);
225 * If we're running a version of Windows that doesn't support
226 * WM_MOUSEWHEEL, find out what message number we should be
229 if (osVersion
.dwMajorVersion
< 4 ||
230 (osVersion
.dwMajorVersion
== 4 &&
231 osVersion
.dwPlatformId
!= VER_PLATFORM_WIN32_NT
))
232 wm_mousewheel
= RegisterWindowMessage("MSWHEEL_ROLLMSG");
235 * See if we can find our Help file.
238 char b
[2048], *p
, *q
, *r
;
240 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
242 p
= strrchr(b
, '\\');
243 if (p
&& p
>= r
) r
= p
+1;
245 if (q
&& q
>= r
) r
= q
+1;
246 strcpy(r
, "putty.hlp");
247 if ( (fp
= fopen(b
, "r")) != NULL
) {
248 help_path
= dupstr(b
);
252 strcpy(r
, "putty.cnt");
253 if ( (fp
= fopen(b
, "r")) != NULL
) {
254 help_has_contents
= TRUE
;
257 help_has_contents
= FALSE
;
261 * Process the command line.
267 default_protocol
= DEFAULT_PROTOCOL
;
268 default_port
= DEFAULT_PORT
;
269 cfg
.logtype
= LGTYP_NONE
;
271 do_defaults(NULL
, &cfg
);
276 * Process a couple of command-line options which are more
277 * easily dealt with before the line is broken up into
278 * words. These are the soon-to-be-defunct @sessionname and
279 * the internal-use-only &sharedmemoryhandle, neither of
280 * which are combined with anything else.
282 while (*p
&& isspace(*p
))
286 while (i
> 1 && isspace(p
[i
- 1]))
289 do_defaults(p
+ 1, &cfg
);
290 if (!*cfg
.host
&& !do_config()) {
294 } else if (*p
== '&') {
296 * An initial & means we've been given a command line
297 * containing the hex value of a HANDLE for a file
298 * mapping object, which we must then extract as a
303 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
304 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
305 0, 0, sizeof(Config
))) != NULL
) {
308 CloseHandle(filemap
);
309 } else if (!do_config()) {
315 * Otherwise, break up the command line and deal with
321 split_into_argv(cmdline
, &argc
, &argv
, NULL
);
323 for (i
= 0; i
< argc
; i
++) {
327 ret
= cmdline_process_param(p
, i
+1<argc?argv
[i
+1]:NULL
, 1);
329 cmdline_error("option \"%s\" requires an argument", p
);
330 } else if (ret
== 2) {
331 i
++; /* skip next argument */
332 } else if (ret
== 1) {
333 continue; /* nothing further needs doing */
334 } else if (!strcmp(p
, "-cleanup")) {
336 * `putty -cleanup'. Remove all registry
337 * entries associated with PuTTY, and also find
338 * and delete the random seed file.
341 "This procedure will remove ALL Registry\n"
342 "entries associated with PuTTY, and will\n"
343 "also remove the PuTTY random seed file.\n"
345 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
346 "SESSIONS. Are you really sure you want\n"
349 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
353 } else if (*p
!= '-') {
357 * If we already have a host name, treat
358 * this argument as a port number. NB we
359 * have to treat this as a saved -P
360 * argument, so that it will be deferred
361 * until it's a good moment to run it.
363 int ret
= cmdline_process_param("-P", p
, 1);
365 } else if (!strncmp(q
, "telnet:", 7)) {
367 * If the hostname starts with "telnet:",
368 * set the protocol to Telnet and process
369 * the string as a Telnet URL.
374 if (q
[0] == '/' && q
[1] == '/')
376 cfg
.protocol
= PROT_TELNET
;
378 while (*p
&& *p
!= ':' && *p
!= '/')
387 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
388 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
392 * Otherwise, treat this argument as a host
395 while (*p
&& !isspace(*p
))
399 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
400 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
409 if (!*cfg
.host
&& !do_config()) {
415 * Trim leading whitespace off the hostname if it's there.
418 int space
= strspn(cfg
.host
, " \t");
419 memmove(cfg
.host
, cfg
.host
+space
, 1+strlen(cfg
.host
)-space
);
422 /* See if host is of the form user@host */
423 if (cfg
.host
[0] != '\0') {
424 char *atsign
= strchr(cfg
.host
, '@');
425 /* Make sure we're not overflowing the user field */
427 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
428 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
429 cfg
.username
[atsign
- cfg
.host
] = '\0';
431 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
436 * Trim a colon suffix off the hostname if it's there.
438 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
441 * Remove any remaining whitespace from the hostname.
445 while (cfg
.host
[p2
] != '\0') {
446 if (cfg
.host
[p2
] != ' ' && cfg
.host
[p2
] != '\t') {
447 cfg
.host
[p1
] = cfg
.host
[p2
];
457 * Select protocol. This is farmed out into a table in a
458 * separate file to enable an ssh-free variant.
463 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
464 if (backends
[i
].protocol
== cfg
.protocol
) {
465 back
= backends
[i
].backend
;
469 MessageBox(NULL
, "Unsupported protocol number found",
470 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
476 /* Check for invalid Port number (i.e. zero) */
478 MessageBox(NULL
, "Invalid Port Number",
479 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
486 wndclass
.lpfnWndProc
= WndProc
;
487 wndclass
.cbClsExtra
= 0;
488 wndclass
.cbWndExtra
= 0;
489 wndclass
.hInstance
= inst
;
490 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
491 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
492 wndclass
.hbrBackground
= NULL
;
493 wndclass
.lpszMenuName
= NULL
;
494 wndclass
.lpszClassName
= appname
;
496 RegisterClass(&wndclass
);
506 * Guess some defaults for the window size. This all gets
507 * updated later, so we don't really care too much. However, we
508 * do want the font width/height guesses to correspond to a
509 * large font rather than a small one...
516 term_size(term
, cfg
.height
, cfg
.width
, cfg
.savelines
);
517 guess_width
= extra_width
+ font_width
* term
->cols
;
518 guess_height
= extra_height
+ font_height
* term
->rows
;
521 get_fullscreen_rect(&r
);
522 if (guess_width
> r
.right
- r
.left
)
523 guess_width
= r
.right
- r
.left
;
524 if (guess_height
> r
.bottom
- r
.top
)
525 guess_height
= r
.bottom
- r
.top
;
529 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
532 winmode
&= ~(WS_VSCROLL
);
533 if (cfg
.resize_action
== RESIZE_DISABLED
)
534 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
536 exwinmode
|= WS_EX_TOPMOST
;
538 exwinmode
|= WS_EX_CLIENTEDGE
;
539 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
540 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
541 guess_width
, guess_height
,
542 NULL
, NULL
, inst
, NULL
);
546 * Initialise the fonts, simultaneously correcting the guesses
547 * for font_{width,height}.
552 * Correct the guesses for extra_{width,height}.
556 GetWindowRect(hwnd
, &wr
);
557 GetClientRect(hwnd
, &cr
);
558 offset_width
= offset_height
= cfg
.window_border
;
559 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
560 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
564 * Resize the window, now we know what size we _really_ want it
567 guess_width
= extra_width
+ font_width
* term
->cols
;
568 guess_height
= extra_height
+ font_height
* term
->rows
;
569 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
570 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
573 * Set up a caret bitmap, with no content.
577 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
578 bits
= smalloc(size
);
579 memset(bits
, 0, size
);
580 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
583 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
586 * Initialise the scroll bar.
591 si
.cbSize
= sizeof(si
);
592 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
594 si
.nMax
= term
->rows
- 1;
595 si
.nPage
= term
->rows
;
597 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
601 * Start up the telnet connection.
605 char msg
[1024], *title
;
608 error
= back
->init((void *)term
, &backhandle
,
609 cfg
.host
, cfg
.port
, &realhost
, cfg
.tcp_nodelay
);
611 sprintf(msg
, "Unable to open connection to\n"
612 "%.800s\n" "%s", cfg
.host
, error
);
613 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
616 window_name
= icon_name
= NULL
;
618 title
= cfg
.wintitle
;
620 sprintf(msg
, "%s - PuTTY", realhost
);
629 * Connect the terminal to the backend for resize purposes.
631 term_provide_resize_fn(term
, back
->size
, backhandle
);
634 * Set up a line discipline.
636 ldisc
= ldisc_create(term
, back
, backhandle
, NULL
);
638 session_closed
= FALSE
;
641 * Prepare the mouse handler.
643 lastact
= MA_NOTHING
;
644 lastbtn
= MBT_NOTHING
;
645 dbltime
= GetDoubleClickTime();
648 * Set up the session-control options on the system menu.
651 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
655 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
656 if (cfg
.protocol
== PROT_TELNET
) {
658 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
659 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
660 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
661 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
662 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
663 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
664 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
665 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
666 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
667 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
668 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
669 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
670 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
671 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
672 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
673 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
674 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
676 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
678 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
679 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
680 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
681 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
684 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
685 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
687 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
688 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
689 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
690 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
691 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
692 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
693 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
694 AppendMenu(m
, (cfg
.resize_action
== RESIZE_DISABLED
) ?
695 MF_GRAYED
: MF_ENABLED
, IDM_FULLSCREEN
, "&Full Screen");
696 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
698 AppendMenu(m
, MF_ENABLED
, IDM_HELP
, "&Help");
699 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
703 * Set up the initial input locale.
705 set_input_locale(GetKeyboardLayout(0));
708 * Open the initial log file if there is one.
713 * Finally show the window!
715 ShowWindow(hwnd
, show
);
716 SetForegroundWindow(hwnd
);
719 * Set the palette up.
725 term
->has_focus
= (GetForegroundWindow() == hwnd
);
728 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
729 int timer_id
= 0, long_timer
= 0;
731 while (msg
.message
!= WM_QUIT
) {
732 /* Sometimes DispatchMessage calls routines that use their own
733 * GetMessage loop, setup this timer so we get some control back.
735 * Also call term_update() from the timer so that if the host
736 * is sending data flat out we still do redraws.
738 if (timer_id
&& long_timer
) {
739 KillTimer(hwnd
, timer_id
);
740 long_timer
= timer_id
= 0;
743 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
744 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
745 DispatchMessage(&msg
);
747 /* Make sure we blink everything that needs it. */
750 /* Send the paste buffer if there's anything to send */
753 /* If there's nothing new in the queue then we can do everything
754 * we've delayed, reading the socket, writing, and repainting
757 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
760 if (pending_netevent
) {
761 enact_pending_netevent();
763 /* Force the cursor blink on */
766 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
770 /* Okay there is now nothing to do so we make sure the screen is
771 * completely up to date then tell windows to call us in a little
775 KillTimer(hwnd
, timer_id
);
779 if (GetCapture() != hwnd
||
781 !(cfg
.mouse_override
&& is_shift_pressed())))
786 flash_window(1); /* maintain */
788 /* The messages seem unreliable; especially if we're being tricky */
789 term
->has_focus
= (GetForegroundWindow() == hwnd
);
792 /* Hmm, term_update didn't want to do an update too soon ... */
793 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
794 else if (!term
->has_focus
)
795 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
797 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
800 /* There's no point rescanning everything in the message queue
801 * so we do an apparently unnecessary wait here
804 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
809 cleanup_exit(msg
.wParam
); /* this doesn't return... */
810 return msg
.wParam
; /* ... but optimiser doesn't know */
816 void cleanup_exit(int code
)
829 if (cfg
.protocol
== PROT_SSH
) {
840 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
842 char *do_select(SOCKET skt
, int startup
)
847 events
= (FD_CONNECT
| FD_READ
| FD_WRITE
|
848 FD_OOB
| FD_CLOSE
| FD_ACCEPT
);
853 return "do_select(): internal error (hwnd==NULL)";
854 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
855 switch (WSAGetLastError()) {
857 return "Network is down";
859 return "WSAAsyncSelect(): unknown error";
866 * set or clear the "raw mouse message" mode
868 void set_raw_mouse_mode(int activate
)
870 activate
= activate
&& !cfg
.no_mouse_rep
;
871 send_raw_mouse
= activate
;
872 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
876 * Print a message box and close the connection.
878 void connection_fatal(char *fmt
, ...)
884 vsprintf(stuff
, fmt
, ap
);
886 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
887 if (cfg
.close_on_exit
== COE_ALWAYS
)
890 session_closed
= TRUE
;
891 SetWindowText(hwnd
, "PuTTY (inactive)");
896 * Report an error at the command-line parsing stage.
898 void cmdline_error(char *fmt
, ...)
904 vsprintf(stuff
, fmt
, ap
);
906 MessageBox(hwnd
, stuff
, "PuTTY Command Line Error", MB_ICONERROR
| MB_OK
);
911 * Actually do the job requested by a WM_NETEVENT
913 static void enact_pending_netevent(void)
915 static int reentering
= 0;
916 extern int select_result(WPARAM
, LPARAM
);
920 return; /* don't unpend the pending */
922 pending_netevent
= FALSE
;
925 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
928 if (ret
== 0 && !session_closed
) {
929 /* Abnormal exits will already have set session_closed and taken
930 * appropriate action. */
931 if (cfg
.close_on_exit
== COE_ALWAYS
||
932 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
934 session_closed
= TRUE
;
935 SetWindowText(hwnd
, "PuTTY (inactive)");
936 MessageBox(hwnd
, "Connection closed by remote host",
937 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
943 * Copy the colour palette from the configuration data into defpal.
944 * This is non-trivial because the colour indices are different.
946 static void cfgtopalette(void)
949 static const int ww
[] = {
950 6, 7, 8, 9, 10, 11, 12, 13,
951 14, 15, 16, 17, 18, 19, 20, 21,
952 0, 1, 2, 3, 4, 4, 5, 5
955 for (i
= 0; i
< 24; i
++) {
957 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
958 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
959 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
964 * Set up the colour palette.
966 static void init_palette(void)
969 HDC hdc
= GetDC(hwnd
);
971 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
972 logpal
= smalloc(sizeof(*logpal
)
973 - sizeof(logpal
->palPalEntry
)
974 + NCOLOURS
* sizeof(PALETTEENTRY
));
975 logpal
->palVersion
= 0x300;
976 logpal
->palNumEntries
= NCOLOURS
;
977 for (i
= 0; i
< NCOLOURS
; i
++) {
978 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
979 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
980 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
981 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
983 pal
= CreatePalette(logpal
);
985 SelectPalette(hdc
, pal
, FALSE
);
987 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
990 ReleaseDC(hwnd
, hdc
);
993 for (i
= 0; i
< NCOLOURS
; i
++)
994 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
998 for (i
= 0; i
< NCOLOURS
; i
++)
999 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
1000 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
1004 * Initialise all the fonts we will need initially. There may be as many as
1005 * three or as few as one. The other (poentially) twentyone fonts are done
1006 * if/when they are needed.
1010 * - check the font width and height, correcting our guesses if
1013 * - verify that the bold font is the same width as the ordinary
1014 * one, and engage shadow bolding if not.
1016 * - verify that the underlined font is the same width as the
1017 * ordinary one (manual underlining by means of line drawing can
1018 * be done in a pinch).
1020 static void init_fonts(int pick_width
, int pick_height
)
1027 int fw_dontcare
, fw_bold
;
1029 for (i
= 0; i
< FONT_MAXNO
; i
++)
1032 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1033 und_mode
= UND_FONT
;
1035 if (cfg
.fontisbold
) {
1036 fw_dontcare
= FW_BOLD
;
1039 fw_dontcare
= FW_DONTCARE
;
1046 font_height
= pick_height
;
1048 font_height
= cfg
.fontheight
;
1049 if (font_height
> 0) {
1051 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
1054 font_width
= pick_width
;
1056 #define f(i,c,w,u) \
1057 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
1058 c, OUT_DEFAULT_PRECIS, \
1059 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
1060 FIXED_PITCH | FF_DONTCARE, cfg.font)
1062 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
1064 lfont
.lfHeight
= font_height
;
1065 lfont
.lfWidth
= font_width
;
1066 lfont
.lfEscapement
= 0;
1067 lfont
.lfOrientation
= 0;
1068 lfont
.lfWeight
= fw_dontcare
;
1069 lfont
.lfItalic
= FALSE
;
1070 lfont
.lfUnderline
= FALSE
;
1071 lfont
.lfStrikeOut
= FALSE
;
1072 lfont
.lfCharSet
= cfg
.fontcharset
;
1073 lfont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
1074 lfont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
1075 lfont
.lfQuality
= DEFAULT_QUALITY
;
1076 lfont
.lfPitchAndFamily
= FIXED_PITCH
| FF_DONTCARE
;
1077 strncpy(lfont
.lfFaceName
, cfg
.font
, LF_FACESIZE
);
1079 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
1080 GetTextMetrics(hdc
, &tm
);
1082 if (pick_width
== 0 || pick_height
== 0) {
1083 font_height
= tm
.tmHeight
;
1084 font_width
= tm
.tmAveCharWidth
;
1086 font_dualwidth
= (tm
.tmAveCharWidth
!= tm
.tmMaxCharWidth
);
1088 #ifdef RDB_DEBUG_PATCH
1089 debug(23, "Primary font H=%d, AW=%d, MW=%d",
1090 tm
.tmHeight
, tm
.tmAveCharWidth
, tm
.tmMaxCharWidth
);
1095 DWORD cset
= tm
.tmCharSet
;
1096 memset(&info
, 0xFF, sizeof(info
));
1098 /* !!! Yes the next line is right */
1099 if (cset
== OEM_CHARSET
)
1100 font_codepage
= GetOEMCP();
1102 if (TranslateCharsetInfo
1103 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
1108 GetCPInfo(font_codepage
, &cpinfo
);
1109 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
1112 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
1115 * Some fonts, e.g. 9-pt Courier, draw their underlines
1116 * outside their character cell. We successfully prevent
1117 * screen corruption by clipping the text output, but then
1118 * we lose the underline completely. Here we try to work
1119 * out whether this is such a font, and if it is, we set a
1120 * flag that causes underlines to be drawn by hand.
1122 * Having tried other more sophisticated approaches (such
1123 * as examining the TEXTMETRIC structure or requesting the
1124 * height of a string), I think we'll do this the brute
1125 * force way: we create a small bitmap, draw an underlined
1126 * space on it, and test to see whether any pixels are
1127 * foreground-coloured. (Since we expect the underline to
1128 * go all the way across the character cell, we only search
1129 * down a single column of the bitmap, half way across.)
1133 HBITMAP und_bm
, und_oldbm
;
1137 und_dc
= CreateCompatibleDC(hdc
);
1138 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
1139 und_oldbm
= SelectObject(und_dc
, und_bm
);
1140 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
1141 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
1142 SetTextColor(und_dc
, RGB(255, 255, 255));
1143 SetBkColor(und_dc
, RGB(0, 0, 0));
1144 SetBkMode(und_dc
, OPAQUE
);
1145 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
1147 for (i
= 0; i
< font_height
; i
++) {
1148 c
= GetPixel(und_dc
, font_width
/ 2, i
);
1149 if (c
!= RGB(0, 0, 0))
1152 SelectObject(und_dc
, und_oldbm
);
1153 DeleteObject(und_bm
);
1156 und_mode
= UND_LINE
;
1157 DeleteObject(fonts
[FONT_UNDERLINE
]);
1158 fonts
[FONT_UNDERLINE
] = 0;
1162 if (bold_mode
== BOLD_FONT
) {
1163 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
1167 descent
= tm
.tmAscent
+ 1;
1168 if (descent
>= font_height
)
1169 descent
= font_height
- 1;
1171 for (i
= 0; i
< 3; i
++) {
1173 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
1174 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
1181 ReleaseDC(hwnd
, hdc
);
1183 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1184 und_mode
= UND_LINE
;
1185 DeleteObject(fonts
[FONT_UNDERLINE
]);
1186 fonts
[FONT_UNDERLINE
] = 0;
1189 if (bold_mode
== BOLD_FONT
&&
1190 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1191 bold_mode
= BOLD_SHADOW
;
1192 DeleteObject(fonts
[FONT_BOLD
]);
1193 fonts
[FONT_BOLD
] = 0;
1195 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1200 static void another_font(int fontno
)
1203 int fw_dontcare
, fw_bold
;
1207 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1210 basefont
= (fontno
& ~(FONT_BOLDUND
));
1211 if (basefont
!= fontno
&& !fontflag
[basefont
])
1212 another_font(basefont
);
1214 if (cfg
.fontisbold
) {
1215 fw_dontcare
= FW_BOLD
;
1218 fw_dontcare
= FW_DONTCARE
;
1222 c
= cfg
.fontcharset
;
1228 if (fontno
& FONT_WIDE
)
1230 if (fontno
& FONT_NARROW
)
1232 if (fontno
& FONT_OEM
)
1234 if (fontno
& FONT_BOLD
)
1236 if (fontno
& FONT_UNDERLINE
)
1240 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1241 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1242 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1243 FIXED_PITCH
| FF_DONTCARE
, s
);
1245 fontflag
[fontno
] = 1;
1248 static void deinit_fonts(void)
1251 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1253 DeleteObject(fonts
[i
]);
1259 void request_resize(int w
, int h
)
1263 /* If the window is maximized supress resizing attempts */
1264 if (IsZoomed(hwnd
)) {
1265 if (cfg
.resize_action
== RESIZE_TERM
)
1269 if (cfg
.resize_action
== RESIZE_DISABLED
) return;
1270 if (h
== term
->rows
&& w
== term
->cols
) return;
1272 /* Sanity checks ... */
1274 static int first_time
= 1;
1277 switch (first_time
) {
1279 /* Get the size of the screen */
1280 if (get_fullscreen_rect(&ss
))
1281 /* first_time = 0 */ ;
1287 /* Make sure the values are sane */
1288 width
= (ss
.right
- ss
.left
- extra_width
) / 4;
1289 height
= (ss
.bottom
- ss
.top
- extra_height
) / 6;
1291 if (w
> width
|| h
> height
)
1300 term_size(term
, h
, w
, cfg
.savelines
);
1302 if (cfg
.resize_action
!= RESIZE_FONT
&& !IsZoomed(hwnd
)) {
1303 width
= extra_width
+ font_width
* w
;
1304 height
= extra_height
+ font_height
* h
;
1306 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1307 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1308 SWP_NOMOVE
| SWP_NOZORDER
);
1312 InvalidateRect(hwnd
, NULL
, TRUE
);
1315 static void reset_window(int reinit
) {
1317 * This function decides how to resize or redraw when the
1318 * user changes something.
1320 * This function doesn't like to change the terminal size but if the
1321 * font size is locked that may be it's only soluion.
1323 int win_width
, win_height
;
1326 #ifdef RDB_DEBUG_PATCH
1327 debug((27, "reset_window()"));
1330 /* Current window sizes ... */
1331 GetWindowRect(hwnd
, &wr
);
1332 GetClientRect(hwnd
, &cr
);
1334 win_width
= cr
.right
- cr
.left
;
1335 win_height
= cr
.bottom
- cr
.top
;
1337 if (cfg
.resize_action
== RESIZE_DISABLED
) reinit
= 2;
1339 /* Are we being forced to reload the fonts ? */
1341 #ifdef RDB_DEBUG_PATCH
1342 debug((27, "reset_window() -- Forced deinit"));
1348 /* Oh, looks like we're minimised */
1349 if (win_width
== 0 || win_height
== 0)
1352 /* Is the window out of position ? */
1354 (offset_width
!= (win_width
-font_width
*term
->cols
)/2 ||
1355 offset_height
!= (win_height
-font_height
*term
->rows
)/2) ){
1356 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1357 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1358 InvalidateRect(hwnd
, NULL
, TRUE
);
1359 #ifdef RDB_DEBUG_PATCH
1360 debug((27, "reset_window() -> Reposition terminal"));
1364 if (IsZoomed(hwnd
)) {
1365 /* We're fullscreen, this means we must not change the size of
1366 * the window so it's the font size or the terminal itself.
1369 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1370 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1372 if (cfg
.resize_action
!= RESIZE_TERM
) {
1373 if ( font_width
!= win_width
/term
->cols
||
1374 font_height
!= win_height
/term
->rows
) {
1376 init_fonts(win_width
/term
->cols
, win_height
/term
->rows
);
1377 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1378 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1379 InvalidateRect(hwnd
, NULL
, TRUE
);
1380 #ifdef RDB_DEBUG_PATCH
1381 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1382 font_width
, font_height
));
1386 if ( font_width
!= win_width
/term
->cols
||
1387 font_height
!= win_height
/term
->rows
) {
1388 /* Our only choice at this point is to change the
1389 * size of the terminal; Oh well.
1391 term_size(term
, win_height
/font_height
, win_width
/font_width
,
1393 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1394 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1395 InvalidateRect(hwnd
, NULL
, TRUE
);
1396 #ifdef RDB_DEBUG_PATCH
1397 debug((27, "reset_window() -> Zoomed term_size"));
1404 /* Hmm, a force re-init means we should ignore the current window
1405 * so we resize to the default font size.
1408 #ifdef RDB_DEBUG_PATCH
1409 debug((27, "reset_window() -> Forced re-init"));
1412 offset_width
= offset_height
= cfg
.window_border
;
1413 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1414 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1416 if (win_width
!= font_width
*term
->cols
+ offset_width
*2 ||
1417 win_height
!= font_height
*term
->rows
+ offset_height
*2) {
1419 /* If this is too large windows will resize it to the maximum
1420 * allowed window size, we will then be back in here and resize
1421 * the font or terminal to fit.
1423 SetWindowPos(hwnd
, NULL
, 0, 0,
1424 font_width
*term
->cols
+ extra_width
,
1425 font_height
*term
->rows
+ extra_height
,
1426 SWP_NOMOVE
| SWP_NOZORDER
);
1429 InvalidateRect(hwnd
, NULL
, TRUE
);
1433 /* Okay the user doesn't want us to change the font so we try the
1434 * window. But that may be too big for the screen which forces us
1435 * to change the terminal.
1437 if ((cfg
.resize_action
== RESIZE_TERM
&& reinit
<=0) ||
1438 (cfg
.resize_action
== RESIZE_EITHER
&& reinit
<0) ||
1440 offset_width
= offset_height
= cfg
.window_border
;
1441 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1442 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1444 if (win_width
!= font_width
*term
->cols
+ offset_width
*2 ||
1445 win_height
!= font_height
*term
->rows
+ offset_height
*2) {
1450 get_fullscreen_rect(&ss
);
1452 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1453 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1456 if ( term
->rows
> height
|| term
->cols
> width
) {
1457 if (cfg
.resize_action
== RESIZE_EITHER
) {
1458 /* Make the font the biggest we can */
1459 if (term
->cols
> width
)
1460 font_width
= (ss
.right
- ss
.left
- extra_width
)
1462 if (term
->rows
> height
)
1463 font_height
= (ss
.bottom
- ss
.top
- extra_height
)
1467 init_fonts(font_width
, font_height
);
1469 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1470 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1472 if ( height
> term
->rows
) height
= term
->rows
;
1473 if ( width
> term
->cols
) width
= term
->cols
;
1474 term_size(term
, height
, width
, cfg
.savelines
);
1475 #ifdef RDB_DEBUG_PATCH
1476 debug((27, "reset_window() -> term resize to (%d,%d)",
1482 SetWindowPos(hwnd
, NULL
, 0, 0,
1483 font_width
*term
->cols
+ extra_width
,
1484 font_height
*term
->rows
+ extra_height
,
1485 SWP_NOMOVE
| SWP_NOZORDER
);
1487 InvalidateRect(hwnd
, NULL
, TRUE
);
1488 #ifdef RDB_DEBUG_PATCH
1489 debug((27, "reset_window() -> window resize to (%d,%d)",
1490 font_width
*term
->cols
+ extra_width
,
1491 font_height
*term
->rows
+ extra_height
));
1497 /* We're allowed to or must change the font but do we want to ? */
1499 if (font_width
!= (win_width
-cfg
.window_border
*2)/term
->cols
||
1500 font_height
!= (win_height
-cfg
.window_border
*2)/term
->rows
) {
1503 init_fonts((win_width
-cfg
.window_border
*2)/term
->cols
,
1504 (win_height
-cfg
.window_border
*2)/term
->rows
);
1505 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1506 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1508 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+offset_width
*2;
1509 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1511 InvalidateRect(hwnd
, NULL
, TRUE
);
1512 #ifdef RDB_DEBUG_PATCH
1513 debug((25, "reset_window() -> font resize to (%d,%d)",
1514 font_width
, font_height
));
1519 static void set_input_locale(HKL kl
)
1523 GetLocaleInfo(LOWORD(kl
), LOCALE_IDEFAULTANSICODEPAGE
,
1524 lbuf
, sizeof(lbuf
));
1526 kbd_codepage
= atoi(lbuf
);
1529 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
, int alt
)
1531 int thistime
= GetMessageTime();
1533 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1534 lastbtn
= MBT_NOTHING
;
1535 term_mouse(term
, b
, MA_CLICK
, x
, y
, shift
, ctrl
, alt
);
1539 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1540 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1541 lastact
== MA_2CLK ? MA_3CLK
:
1542 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1547 if (lastact
!= MA_NOTHING
)
1548 term_mouse(term
, b
, lastact
, x
, y
, shift
, ctrl
, alt
);
1549 lasttime
= thistime
;
1553 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1554 * into a cooked one (SELECT, EXTEND, PASTE).
1556 Mouse_Button
translate_button(Mouse_Button button
)
1558 if (button
== MBT_LEFT
)
1560 if (button
== MBT_MIDDLE
)
1561 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1562 if (button
== MBT_RIGHT
)
1563 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1564 return 0; /* shouldn't happen */
1567 static void show_mouseptr(int show
)
1569 static int cursor_visible
= 1;
1570 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1572 if (cursor_visible
&& !show
)
1574 else if (!cursor_visible
&& show
)
1576 cursor_visible
= show
;
1579 static int is_alt_pressed(void)
1582 int r
= GetKeyboardState(keystate
);
1585 if (keystate
[VK_MENU
] & 0x80)
1587 if (keystate
[VK_RMENU
] & 0x80)
1592 static int is_shift_pressed(void)
1595 int r
= GetKeyboardState(keystate
);
1598 if (keystate
[VK_SHIFT
] & 0x80)
1603 static int resizing
;
1605 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1606 WPARAM wParam
, LPARAM lParam
)
1609 static int ignore_clip
= FALSE
;
1610 static int need_backend_resize
= FALSE
;
1611 static int fullscr_on_max
= FALSE
;
1615 if (pending_netevent
)
1616 enact_pending_netevent();
1617 if (GetCapture() != hwnd
||
1618 (send_raw_mouse
&& !(cfg
.mouse_override
&& is_shift_pressed())))
1624 if (cfg
.ping_interval
> 0) {
1627 if (now
- last_movement
> cfg
.ping_interval
) {
1628 back
->special(backhandle
, TS_PING
);
1629 last_movement
= now
;
1632 net_pending_errors();
1638 if (!cfg
.warn_on_close
|| session_closed
||
1640 "Are you sure you want to close this session?",
1641 "PuTTY Exit Confirmation",
1642 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1643 DestroyWindow(hwnd
);
1650 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1662 PROCESS_INFORMATION pi
;
1663 HANDLE filemap
= NULL
;
1665 if (wParam
== IDM_DUPSESS
) {
1667 * Allocate a file-mapping memory chunk for the
1670 SECURITY_ATTRIBUTES sa
;
1673 sa
.nLength
= sizeof(sa
);
1674 sa
.lpSecurityDescriptor
= NULL
;
1675 sa
.bInheritHandle
= TRUE
;
1676 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1679 0, sizeof(Config
), NULL
);
1681 p
= (Config
*) MapViewOfFile(filemap
,
1683 0, 0, sizeof(Config
));
1685 *p
= cfg
; /* structure copy */
1689 sprintf(c
, "putty &%p", filemap
);
1691 } else if (wParam
== IDM_SAVEDSESS
) {
1692 if ((lParam
- IDM_SAVED_MIN
) / 16 < nsessions
) {
1694 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1695 cl
= smalloc(16 + strlen(session
));
1696 /* 8, but play safe */
1699 /* not a very important failure mode */
1701 sprintf(cl
, "putty @%s", session
);
1709 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1711 si
.lpReserved
= NULL
;
1712 si
.lpDesktop
= NULL
;
1716 si
.lpReserved2
= NULL
;
1717 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1718 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1721 CloseHandle(filemap
);
1731 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1734 if (!do_reconfig(hwnd
))
1738 /* Disable full-screen if resizing forbidden */
1739 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
1740 EnableMenuItem(m
, IDM_FULLSCREEN
, MF_BYCOMMAND
|
1741 (cfg
.resize_action
== RESIZE_DISABLED
)
1742 ? MF_GRAYED
: MF_ENABLED
);
1743 /* Gracefully unzoom if necessary */
1744 if (IsZoomed(hwnd
) &&
1745 (cfg
.resize_action
== RESIZE_DISABLED
)) {
1746 ShowWindow(hwnd
, SW_RESTORE
);
1750 if (strcmp(prev_cfg
.logfilename
, cfg
.logfilename
) ||
1751 prev_cfg
.logtype
!= cfg
.logtype
) {
1752 logfclose(); /* reset logging */
1758 * Flush the line discipline's edit buffer in the
1759 * case where local editing has just been disabled.
1761 ldisc_send(ldisc
, NULL
, 0, 0);
1769 /* Give terminal a heads-up on miscellaneous stuff */
1770 term_reconfig(term
);
1772 /* Screen size changed ? */
1773 if (cfg
.height
!= prev_cfg
.height
||
1774 cfg
.width
!= prev_cfg
.width
||
1775 cfg
.savelines
!= prev_cfg
.savelines
||
1776 cfg
.resize_action
== RESIZE_FONT
||
1777 (cfg
.resize_action
== RESIZE_EITHER
&& IsZoomed(hwnd
)) ||
1778 cfg
.resize_action
== RESIZE_DISABLED
)
1779 term_size(term
, cfg
.height
, cfg
.width
, cfg
.savelines
);
1781 /* Enable or disable the scroll bar, etc */
1783 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1784 LONG nexflag
, exflag
=
1785 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1788 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1789 if (cfg
.alwaysontop
) {
1790 nexflag
|= WS_EX_TOPMOST
;
1791 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1792 SWP_NOMOVE
| SWP_NOSIZE
);
1794 nexflag
&= ~(WS_EX_TOPMOST
);
1795 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1796 SWP_NOMOVE
| SWP_NOSIZE
);
1799 if (cfg
.sunken_edge
)
1800 nexflag
|= WS_EX_CLIENTEDGE
;
1802 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1805 if (is_full_screen() ?
1806 cfg
.scrollbar_in_fullscreen
: cfg
.scrollbar
)
1809 nflg
&= ~WS_VSCROLL
;
1811 if (cfg
.resize_action
== RESIZE_DISABLED
||
1813 nflg
&= ~WS_THICKFRAME
;
1815 nflg
|= WS_THICKFRAME
;
1817 if (cfg
.resize_action
== RESIZE_DISABLED
)
1818 nflg
&= ~WS_MAXIMIZEBOX
;
1820 nflg
|= WS_MAXIMIZEBOX
;
1822 if (nflg
!= flag
|| nexflag
!= exflag
) {
1824 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1825 if (nexflag
!= exflag
)
1826 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1828 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1829 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1830 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1838 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1843 set_title(cfg
.wintitle
);
1844 if (IsIconic(hwnd
)) {
1846 cfg
.win_name_always ? window_name
:
1850 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1851 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1852 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1853 cfg
.fontheight
!= prev_cfg
.fontheight
||
1854 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1855 cfg
.vtmode
!= prev_cfg
.vtmode
||
1856 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1857 cfg
.resize_action
== RESIZE_DISABLED
||
1858 cfg
.resize_action
== RESIZE_EITHER
||
1859 (cfg
.resize_action
!= prev_cfg
.resize_action
))
1862 InvalidateRect(hwnd
, NULL
, TRUE
);
1863 reset_window(init_lvl
);
1864 net_pending_errors();
1875 ldisc_send(ldisc
, NULL
, 0, 0);
1878 back
->special(backhandle
, TS_AYT
);
1879 net_pending_errors();
1882 back
->special(backhandle
, TS_BRK
);
1883 net_pending_errors();
1886 back
->special(backhandle
, TS_SYNCH
);
1887 net_pending_errors();
1890 back
->special(backhandle
, TS_EC
);
1891 net_pending_errors();
1894 back
->special(backhandle
, TS_EL
);
1895 net_pending_errors();
1898 back
->special(backhandle
, TS_GA
);
1899 net_pending_errors();
1902 back
->special(backhandle
, TS_NOP
);
1903 net_pending_errors();
1906 back
->special(backhandle
, TS_ABORT
);
1907 net_pending_errors();
1910 back
->special(backhandle
, TS_AO
);
1911 net_pending_errors();
1914 back
->special(backhandle
, TS_IP
);
1915 net_pending_errors();
1918 back
->special(backhandle
, TS_SUSP
);
1919 net_pending_errors();
1922 back
->special(backhandle
, TS_EOR
);
1923 net_pending_errors();
1926 back
->special(backhandle
, TS_EOF
);
1927 net_pending_errors();
1933 WinHelp(hwnd
, help_path
,
1934 help_has_contents ? HELP_FINDER
: HELP_CONTENTS
, 0);
1938 * We get this if the System menu has been activated
1945 * We get this if the System menu has been activated
1946 * using the keyboard. This might happen from within
1947 * TranslateKey, in which case it really wants to be
1948 * followed by a `space' character to actually _bring
1949 * the menu up_ rather than just sitting there in
1950 * `ready to appear' state.
1952 show_mouseptr(1); /* make sure pointer is visible */
1954 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1956 case IDM_FULLSCREEN
:
1960 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1961 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1966 #define X_POS(l) ((int)(short)LOWORD(l))
1967 #define Y_POS(l) ((int)(short)HIWORD(l))
1969 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1970 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1971 case WM_LBUTTONDOWN
:
1972 case WM_MBUTTONDOWN
:
1973 case WM_RBUTTONDOWN
:
1981 case WM_LBUTTONDOWN
:
1985 case WM_MBUTTONDOWN
:
1986 button
= MBT_MIDDLE
;
1989 case WM_RBUTTONDOWN
:
1998 button
= MBT_MIDDLE
;
2006 button
= press
= 0; /* shouldn't happen */
2010 * Special case: in full-screen mode, if the left
2011 * button is clicked in the very top left corner of the
2012 * window, we put up the System menu instead of doing
2015 if (is_full_screen() && press
&& button
== MBT_LEFT
&&
2016 X_POS(lParam
) == 0 && Y_POS(lParam
) == 0) {
2017 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_MOUSEMENU
, 0);
2022 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
2023 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
,
2027 term_mouse(term
, button
, MA_RELEASE
,
2028 TO_CHR_X(X_POS(lParam
)),
2029 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
2030 wParam
& MK_CONTROL
, is_alt_pressed());
2038 * Add the mouse position and message time to the random
2041 noise_ultralight(lParam
);
2043 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
) &&
2044 GetCapture() == hwnd
) {
2046 if (wParam
& MK_LBUTTON
)
2048 else if (wParam
& MK_MBUTTON
)
2052 term_mouse(term
, b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
2053 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
2054 wParam
& MK_CONTROL
, is_alt_pressed());
2057 case WM_NCMOUSEMOVE
:
2059 noise_ultralight(lParam
);
2061 case WM_IGNORE_CLIP
:
2062 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
2064 case WM_DESTROYCLIPBOARD
:
2066 term_deselect(term
);
2067 ignore_clip
= FALSE
;
2073 hdc
= BeginPaint(hwnd
, &p
);
2075 SelectPalette(hdc
, pal
, TRUE
);
2076 RealizePalette(hdc
);
2078 term_paint(term
, hdc
,
2079 (p
.rcPaint
.left
-offset_width
)/font_width
,
2080 (p
.rcPaint
.top
-offset_height
)/font_height
,
2081 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
2082 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
2085 p
.rcPaint
.left
< offset_width
||
2086 p
.rcPaint
.top
< offset_height
||
2087 p
.rcPaint
.right
>= offset_width
+ font_width
*term
->cols
||
2088 p
.rcPaint
.bottom
>= offset_height
+ font_height
*term
->rows
)
2090 HBRUSH fillcolour
, oldbrush
;
2092 fillcolour
= CreateSolidBrush (
2093 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
2094 oldbrush
= SelectObject(hdc
, fillcolour
);
2095 edge
= CreatePen(PS_SOLID
, 0,
2096 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
2097 oldpen
= SelectObject(hdc
, edge
);
2100 * Jordan Russell reports that this apparently
2101 * ineffectual IntersectClipRect() call masks a
2102 * Windows NT/2K bug causing strange display
2103 * problems when the PuTTY window is taller than
2104 * the primary monitor. It seems harmless enough...
2106 IntersectClipRect(hdc
,
2107 p
.rcPaint
.left
, p
.rcPaint
.top
,
2108 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
2110 ExcludeClipRect(hdc
,
2111 offset_width
, offset_height
,
2112 offset_width
+font_width
*term
->cols
,
2113 offset_height
+font_height
*term
->rows
);
2115 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
2116 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
2118 // SelectClipRgn(hdc, NULL);
2120 SelectObject(hdc
, oldbrush
);
2121 DeleteObject(fillcolour
);
2122 SelectObject(hdc
, oldpen
);
2125 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
2126 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
2132 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2133 * but the only one that's likely to try to overload us is FD_READ.
2134 * This means buffering just one is fine.
2136 if (pending_netevent
)
2137 enact_pending_netevent();
2139 pending_netevent
= TRUE
;
2140 pend_netevent_wParam
= wParam
;
2141 pend_netevent_lParam
= lParam
;
2142 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
2143 enact_pending_netevent();
2145 time(&last_movement
);
2148 term
->has_focus
= TRUE
;
2149 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
2151 flash_window(0); /* stop */
2158 term
->has_focus
= FALSE
;
2160 caret_x
= caret_y
= -1; /* ensure caret is replaced next time */
2164 case WM_ENTERSIZEMOVE
:
2165 #ifdef RDB_DEBUG_PATCH
2166 debug((27, "WM_ENTERSIZEMOVE"));
2170 need_backend_resize
= FALSE
;
2172 case WM_EXITSIZEMOVE
:
2175 #ifdef RDB_DEBUG_PATCH
2176 debug((27, "WM_EXITSIZEMOVE"));
2178 if (need_backend_resize
) {
2179 term_size(term
, cfg
.height
, cfg
.width
, cfg
.savelines
);
2180 InvalidateRect(hwnd
, NULL
, TRUE
);
2185 * This does two jobs:
2186 * 1) Keep the sizetip uptodate
2187 * 2) Make sure the window size is _stepped_ in units of the font size.
2189 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2190 int width
, height
, w
, h
, ew
, eh
;
2191 LPRECT r
= (LPRECT
) lParam
;
2193 if ( !need_backend_resize
&& cfg
.resize_action
== RESIZE_EITHER
&&
2194 (cfg
.height
!= term
->rows
|| cfg
.width
!= term
->cols
)) {
2196 * Great! It seems that both the terminal size and the
2197 * font size have been changed and the user is now dragging.
2199 * It will now be difficult to get back to the configured
2202 * This would be easier but it seems to be too confusing.
2204 term_size(term, cfg.height, cfg.width, cfg.savelines);
2207 cfg
.height
=term
->rows
; cfg
.width
=term
->cols
;
2209 InvalidateRect(hwnd
, NULL
, TRUE
);
2210 need_backend_resize
= TRUE
;
2213 width
= r
->right
- r
->left
- extra_width
;
2214 height
= r
->bottom
- r
->top
- extra_height
;
2215 w
= (width
+ font_width
/ 2) / font_width
;
2218 h
= (height
+ font_height
/ 2) / font_height
;
2221 UpdateSizeTip(hwnd
, w
, h
);
2222 ew
= width
- w
* font_width
;
2223 eh
= height
- h
* font_height
;
2225 if (wParam
== WMSZ_LEFT
||
2226 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2232 if (wParam
== WMSZ_TOP
||
2233 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2243 int width
, height
, w
, h
, rv
= 0;
2244 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
2245 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2246 LPRECT r
= (LPRECT
) lParam
;
2248 width
= r
->right
- r
->left
- ex_width
;
2249 height
= r
->bottom
- r
->top
- ex_height
;
2251 w
= (width
+ term
->cols
/2)/term
->cols
;
2252 h
= (height
+ term
->rows
/2)/term
->rows
;
2253 if ( r
->right
!= r
->left
+ w
*term
->cols
+ ex_width
)
2256 if (wParam
== WMSZ_LEFT
||
2257 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2258 r
->left
= r
->right
- w
*term
->cols
- ex_width
;
2260 r
->right
= r
->left
+ w
*term
->cols
+ ex_width
;
2262 if (r
->bottom
!= r
->top
+ h
*term
->rows
+ ex_height
)
2265 if (wParam
== WMSZ_TOP
||
2266 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2267 r
->top
= r
->bottom
- h
*term
->rows
- ex_height
;
2269 r
->bottom
= r
->top
+ h
*term
->rows
+ ex_height
;
2273 /* break; (never reached) */
2274 case WM_FULLSCR_ON_MAX
:
2275 fullscr_on_max
= TRUE
;
2278 sys_cursor_update();
2281 #ifdef RDB_DEBUG_PATCH
2282 debug((27, "WM_SIZE %s (%d,%d)",
2283 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2284 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2285 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2286 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2288 LOWORD(lParam
), HIWORD(lParam
)));
2290 if (wParam
== SIZE_MINIMIZED
)
2292 cfg
.win_name_always ? window_name
: icon_name
);
2293 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2294 SetWindowText(hwnd
, window_name
);
2295 if (wParam
== SIZE_RESTORED
)
2296 clear_full_screen();
2297 if (wParam
== SIZE_MAXIMIZED
&& fullscr_on_max
) {
2298 fullscr_on_max
= FALSE
;
2302 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2303 /* A resize, well it better be a minimize. */
2307 int width
, height
, w
, h
;
2309 width
= LOWORD(lParam
);
2310 height
= HIWORD(lParam
);
2313 if (wParam
== SIZE_MAXIMIZED
&& !was_zoomed
) {
2315 prev_rows
= term
->rows
;
2316 prev_cols
= term
->cols
;
2317 if (cfg
.resize_action
== RESIZE_TERM
) {
2318 w
= width
/ font_width
;
2320 h
= height
/ font_height
;
2323 term_size(term
, h
, w
, cfg
.savelines
);
2326 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2328 if (cfg
.resize_action
== RESIZE_TERM
)
2329 term_size(term
, prev_rows
, prev_cols
, cfg
.savelines
);
2330 if (cfg
.resize_action
!= RESIZE_FONT
)
2335 /* This is an unexpected resize, these will normally happen
2336 * if the window is too large. Probably either the user
2337 * selected a huge font or the screen size has changed.
2339 * This is also called with minimize.
2341 else reset_window(-1);
2345 * Don't call back->size in mid-resize. (To prevent
2346 * massive numbers of resize events getting sent
2347 * down the connection during an NT opaque drag.)
2350 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2351 need_backend_resize
= TRUE
;
2352 w
= (width
-cfg
.window_border
*2) / font_width
;
2354 h
= (height
-cfg
.window_border
*2) / font_height
;
2363 sys_cursor_update();
2366 switch (LOWORD(wParam
)) {
2368 term_scroll(term
, -1, 0);
2371 term_scroll(term
, +1, 0);
2374 term_scroll(term
, 0, +1);
2377 term_scroll(term
, 0, -1);
2380 term_scroll(term
, 0, +term
->rows
/ 2);
2383 term_scroll(term
, 0, -term
->rows
/ 2);
2385 case SB_THUMBPOSITION
:
2387 term_scroll(term
, 1, HIWORD(wParam
));
2391 case WM_PALETTECHANGED
:
2392 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2393 HDC hdc
= get_ctx();
2395 if (RealizePalette(hdc
) > 0)
2401 case WM_QUERYNEWPALETTE
:
2403 HDC hdc
= get_ctx();
2405 if (RealizePalette(hdc
) > 0)
2417 * Add the scan code and keypress timing to the random
2420 noise_ultralight(lParam
);
2423 * We don't do TranslateMessage since it disassociates the
2424 * resulting CHAR message from the KEYDOWN that sparked it,
2425 * which we occasionally don't want. Instead, we process
2426 * KEYDOWN, and call the Win32 translator functions so that
2427 * we get the translations under _our_ control.
2430 unsigned char buf
[20];
2433 if (wParam
== VK_PROCESSKEY
) {
2436 m
.message
= WM_KEYDOWN
;
2438 m
.lParam
= lParam
& 0xdfff;
2439 TranslateMessage(&m
);
2441 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2443 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2447 * Interrupt an ongoing paste. I'm not sure
2448 * this is sensible, but for the moment it's
2449 * preferable to having to faff about buffering
2455 * We need not bother about stdin backlogs
2456 * here, because in GUI PuTTY we can't do
2457 * anything about it anyway; there's no means
2458 * of asking Windows to hold off on KEYDOWN
2459 * messages. We _have_ to buffer everything
2462 term_seen_key_event(term
);
2463 ldisc_send(ldisc
, buf
, len
, 1);
2468 net_pending_errors();
2470 case WM_INPUTLANGCHANGE
:
2471 /* wParam == Font number */
2472 /* lParam == Locale */
2473 set_input_locale((HKL
)lParam
);
2474 sys_cursor_update();
2477 if(wParam
== IMN_SETOPENSTATUS
) {
2478 HIMC hImc
= ImmGetContext(hwnd
);
2479 ImmSetCompositionFont(hImc
, &lfont
);
2480 ImmReleaseContext(hwnd
, hImc
);
2484 case WM_IME_COMPOSITION
:
2490 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2491 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2493 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2494 break; /* fall back to DefWindowProc */
2496 hIMC
= ImmGetContext(hwnd
);
2497 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2501 buff
= (char*) smalloc(n
);
2502 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2504 * Jaeyoun Chung reports that Korean character
2505 * input doesn't work correctly if we do a single
2506 * luni_send() covering the whole of buff. So
2507 * instead we luni_send the characters one by one.
2509 term_seen_key_event(term
);
2510 for (i
= 0; i
< n
; i
+= 2) {
2511 luni_send(ldisc
, (unsigned short *)(buff
+i
), 1, 1);
2515 ImmReleaseContext(hwnd
, hIMC
);
2520 if (wParam
& 0xFF00) {
2521 unsigned char buf
[2];
2524 buf
[0] = wParam
>> 8;
2525 term_seen_key_event(term
);
2526 lpage_send(ldisc
, kbd_codepage
, buf
, 2, 1);
2528 char c
= (unsigned char) wParam
;
2529 term_seen_key_event(term
);
2530 lpage_send(ldisc
, kbd_codepage
, &c
, 1, 1);
2536 * Nevertheless, we are prepared to deal with WM_CHAR
2537 * messages, should they crop up. So if someone wants to
2538 * post the things to us as part of a macro manoeuvre,
2539 * we're ready to cope.
2542 char c
= (unsigned char)wParam
;
2543 term_seen_key_event(term
);
2544 lpage_send(ldisc
, CP_ACP
, &c
, 1, 1);
2548 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2549 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2553 if (message
== wm_mousewheel
|| message
== WM_MOUSEWHEEL
) {
2554 int shift_pressed
=0, control_pressed
=0, alt_pressed
=0;
2556 if (message
== WM_MOUSEWHEEL
) {
2557 wheel_accumulator
+= (short)HIWORD(wParam
);
2558 shift_pressed
=LOWORD(wParam
) & MK_SHIFT
;
2559 control_pressed
=LOWORD(wParam
) & MK_CONTROL
;
2562 wheel_accumulator
+= (int)wParam
;
2563 if (GetKeyboardState(keys
)!=0) {
2564 shift_pressed
=keys
[VK_SHIFT
]&0x80;
2565 control_pressed
=keys
[VK_CONTROL
]&0x80;
2569 /* process events when the threshold is reached */
2570 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
2573 /* reduce amount for next time */
2574 if (wheel_accumulator
> 0) {
2576 wheel_accumulator
-= WHEEL_DELTA
;
2577 } else if (wheel_accumulator
< 0) {
2579 wheel_accumulator
+= WHEEL_DELTA
;
2583 if (send_raw_mouse
&&
2584 !(cfg
.mouse_override
&& shift_pressed
)) {
2585 /* send a mouse-down followed by a mouse up */
2588 TO_CHR_X(X_POS(lParam
)),
2589 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2590 control_pressed
, is_alt_pressed());
2591 term_mouse(term
, b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
2592 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2593 control_pressed
, is_alt_pressed());
2595 /* trigger a scroll */
2596 term_scroll(term
, 0,
2598 -term
->rows
/ 2 : term
->rows
/ 2);
2605 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2609 * Move the system caret. (We maintain one, even though it's
2610 * invisible, for the benefit of blind people: apparently some
2611 * helper software tracks the system caret, so we should arrange to
2614 void sys_cursor(int x
, int y
)
2618 if (!term
->has_focus
) return;
2621 * Avoid gratuitously re-updating the cursor position and IMM
2622 * window if there's no actual change required.
2624 cx
= x
* font_width
+ offset_width
;
2625 cy
= y
* font_height
+ offset_height
;
2626 if (cx
== caret_x
&& cy
== caret_y
)
2631 sys_cursor_update();
2634 static void sys_cursor_update(void)
2639 if (!term
->has_focus
) return;
2641 if (caret_x
< 0 || caret_y
< 0)
2644 SetCaretPos(caret_x
, caret_y
);
2646 /* IMM calls on Win98 and beyond only */
2647 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2649 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2650 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2652 /* we should have the IMM functions */
2653 hIMC
= ImmGetContext(hwnd
);
2654 cf
.dwStyle
= CFS_POINT
;
2655 cf
.ptCurrentPos
.x
= caret_x
;
2656 cf
.ptCurrentPos
.y
= caret_y
;
2657 ImmSetCompositionWindow(hIMC
, &cf
);
2659 ImmReleaseContext(hwnd
, hIMC
);
2663 * Draw a line of text in the window, at given character
2664 * coordinates, in given attributes.
2666 * We are allowed to fiddle with the contents of `text'.
2668 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2669 unsigned long attr
, int lattr
)
2672 int nfg
, nbg
, nfont
;
2675 int force_manual_underline
= 0;
2676 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2677 int char_width
= fnt_width
;
2678 int text_adjust
= 0;
2679 static int *IpDx
= 0, IpDxLEN
= 0;
2681 if (attr
& ATTR_WIDE
)
2684 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2686 if (len
> IpDxLEN
) {
2688 IpDx
= smalloc((len
+ 16) * sizeof(int));
2689 IpDxLEN
= (len
+ 16);
2691 for (i
= 0; i
< IpDxLEN
; i
++)
2692 IpDx
[i
] = char_width
;
2695 /* Only want the left half of double width lines */
2696 if (lattr
!= LATTR_NORM
&& x
*2 >= term
->cols
)
2704 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || term
->big_cursor
)) {
2705 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2706 attr
^= ATTR_CUR_XOR
;
2710 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2711 /* Assume a poorman font is borken in other ways too. */
2721 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2724 if (attr
& ATTR_NARROW
)
2725 nfont
|= FONT_NARROW
;
2727 /* Special hack for the VT100 linedraw glyphs. */
2728 if ((attr
& CSET_MASK
) == 0x2300) {
2729 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2730 switch ((unsigned char) (text
[0])) {
2732 text_adjust
= -2 * font_height
/ 5;
2735 text_adjust
= -1 * font_height
/ 5;
2738 text_adjust
= font_height
/ 5;
2741 text_adjust
= 2 * font_height
/ 5;
2744 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2747 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2748 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2749 if (attr
& ATTR_UNDER
) {
2750 attr
&= ~ATTR_UNDER
;
2751 force_manual_underline
= 1;
2756 /* Anything left as an original character set is unprintable. */
2757 if (DIRECT_CHAR(attr
)) {
2760 memset(text
, 0xFD, len
);
2764 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2767 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2768 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2769 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2771 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2772 nfont
|= FONT_UNDERLINE
;
2773 another_font(nfont
);
2774 if (!fonts
[nfont
]) {
2775 if (nfont
& FONT_UNDERLINE
)
2776 force_manual_underline
= 1;
2777 /* Don't do the same for manual bold, it could be bad news. */
2779 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2781 another_font(nfont
);
2783 nfont
= FONT_NORMAL
;
2784 if (attr
& ATTR_REVERSE
) {
2789 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2791 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2795 SelectObject(hdc
, fonts
[nfont
]);
2796 SetTextColor(hdc
, fg
);
2797 SetBkColor(hdc
, bg
);
2798 SetBkMode(hdc
, OPAQUE
);
2801 line_box
.right
= x
+ char_width
* len
;
2802 line_box
.bottom
= y
+ font_height
;
2804 /* Only want the left half of double width lines */
2805 if (line_box
.right
> font_width
*term
->cols
+offset_width
)
2806 line_box
.right
= font_width
*term
->cols
+offset_width
;
2808 /* We're using a private area for direct to font. (512 chars.) */
2809 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2810 /* Ho Hum, dbcs fonts are a PITA! */
2811 /* To display on W9x I have to convert to UCS */
2812 static wchar_t *uni_buf
= 0;
2813 static int uni_len
= 0;
2815 if (len
> uni_len
) {
2817 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2820 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2821 uni_buf
[nlen
] = 0xFFFD;
2822 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2823 IpDx
[nlen
] += char_width
;
2824 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2825 text
+mptr
, 2, uni_buf
+nlen
, 1);
2830 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2831 text
+mptr
, 1, uni_buf
+nlen
, 1);
2839 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2840 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2841 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2842 SetBkMode(hdc
, TRANSPARENT
);
2843 ExtTextOutW(hdc
, x
- 1,
2844 y
- font_height
* (lattr
==
2845 LATTR_BOT
) + text_adjust
,
2846 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2850 } else if (DIRECT_FONT(attr
)) {
2852 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2853 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2854 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2855 SetBkMode(hdc
, TRANSPARENT
);
2857 /* GRR: This draws the character outside it's box and can leave
2858 * 'droppings' even with the clip box! I suppose I could loop it
2859 * one character at a time ... yuk.
2861 * Or ... I could do a test print with "W", and use +1 or -1 for this
2862 * shift depending on if the leftmost column is blank...
2864 ExtTextOut(hdc
, x
- 1,
2865 y
- font_height
* (lattr
==
2866 LATTR_BOT
) + text_adjust
,
2867 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2870 /* And 'normal' unicode characters */
2871 static WCHAR
*wbuf
= NULL
;
2872 static int wlen
= 0;
2877 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2879 for (i
= 0; i
< len
; i
++)
2880 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2883 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2884 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2886 /* And the shadow bold hack. */
2887 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2888 SetBkMode(hdc
, TRANSPARENT
);
2889 ExtTextOutW(hdc
, x
- 1,
2890 y
- font_height
* (lattr
==
2891 LATTR_BOT
) + text_adjust
,
2892 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2895 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2896 (und_mode
== UND_LINE
2897 && (attr
& ATTR_UNDER
)))) {
2900 if (lattr
== LATTR_BOT
)
2901 dec
= dec
* 2 - font_height
;
2903 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2904 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2905 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2906 oldpen
= SelectObject(hdc
, oldpen
);
2907 DeleteObject(oldpen
);
2911 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2912 unsigned long attr
, int lattr
)
2918 int ctype
= cfg
.cursor_type
;
2920 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || term
->big_cursor
)) {
2921 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2922 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2926 attr
|= TATTR_RIGHTCURS
;
2929 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2930 if (attr
& ATTR_WIDE
)
2937 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || term
->big_cursor
)) {
2940 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2941 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2942 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2943 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2944 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2945 Polyline(hdc
, pts
, 5);
2946 oldpen
= SelectObject(hdc
, oldpen
);
2947 DeleteObject(oldpen
);
2948 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2949 int startx
, starty
, dx
, dy
, length
, i
;
2952 starty
= y
+ descent
;
2955 length
= char_width
;
2958 if (attr
& TATTR_RIGHTCURS
)
2959 xadjust
= char_width
- 1;
2960 startx
= x
+ xadjust
;
2964 length
= font_height
;
2966 if (attr
& TATTR_ACTCURS
) {
2969 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2970 MoveToEx(hdc
, startx
, starty
, NULL
);
2971 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2972 oldpen
= SelectObject(hdc
, oldpen
);
2973 DeleteObject(oldpen
);
2975 for (i
= 0; i
< length
; i
++) {
2977 SetPixel(hdc
, startx
, starty
, colours
[23]);
2986 /* This function gets the actual width of a character in the normal font.
2988 int CharWidth(Context ctx
, int uc
) {
2992 /* If the font max is the same as the font ave width then this
2993 * function is a no-op.
2995 if (!font_dualwidth
) return 1;
2997 switch (uc
& CSET_MASK
) {
2999 uc
= unitab_line
[uc
& 0xFF];
3002 uc
= unitab_xterm
[uc
& 0xFF];
3005 uc
= unitab_scoacs
[uc
& 0xFF];
3008 if (DIRECT_FONT(uc
)) {
3009 if (dbcs_screenfont
) return 1;
3011 /* Speedup, I know of no font where ascii is the wrong width */
3012 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
3015 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
3016 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
3017 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
3018 another_font(FONT_OEM
);
3019 if (!fonts
[FONT_OEM
]) return 0;
3021 SelectObject(hdc
, fonts
[FONT_OEM
]);
3025 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
3026 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
3029 /* Speedup, I know of no font where ascii is the wrong width */
3030 if (uc
>= ' ' && uc
<= '~') return 1;
3032 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
3033 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
3034 /* Okay that one worked */ ;
3035 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
3036 /* This should work on 9x too, but it's "less accurate" */ ;
3041 ibuf
+= font_width
/ 2 -1;
3048 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
3049 * codes. Returns number of bytes used or zero to drop the message
3050 * or -1 to forward the message to windows.
3052 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
3053 unsigned char *output
)
3056 int scan
, left_alt
= 0, key_down
, shift_state
;
3058 unsigned char *p
= output
;
3059 static int alt_sum
= 0;
3061 HKL kbd_layout
= GetKeyboardLayout(0);
3063 static WORD keys
[3];
3064 static int compose_char
= 0;
3065 static WPARAM compose_key
= 0;
3067 r
= GetKeyboardState(keystate
);
3069 memset(keystate
, 0, sizeof(keystate
));
3072 #define SHOW_TOASCII_RESULT
3073 { /* Tell us all about key events */
3074 static BYTE oldstate
[256];
3075 static int first
= 1;
3079 memcpy(oldstate
, keystate
, sizeof(oldstate
));
3082 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
3084 } else if ((HIWORD(lParam
) & KF_UP
)
3085 && scan
== (HIWORD(lParam
) & 0xFF)) {
3089 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
3090 debug(("K_F%d", wParam
+ 1 - VK_F1
));
3103 debug(("VK_%02x", wParam
));
3105 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
3107 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
3109 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
3110 if (ch
>= ' ' && ch
<= '~')
3111 debug((", '%c'", ch
));
3113 debug((", $%02x", ch
));
3116 debug((", KB0=%02x", keys
[0]));
3118 debug((", KB1=%02x", keys
[1]));
3120 debug((", KB2=%02x", keys
[2]));
3122 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
3124 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
3126 if ((HIWORD(lParam
) & KF_EXTENDED
))
3128 if ((HIWORD(lParam
) & KF_UP
))
3132 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
3133 else if ((HIWORD(lParam
) & KF_UP
))
3134 oldstate
[wParam
& 0xFF] ^= 0x80;
3136 oldstate
[wParam
& 0xFF] ^= 0x81;
3138 for (ch
= 0; ch
< 256; ch
++)
3139 if (oldstate
[ch
] != keystate
[ch
])
3140 debug((", M%02x=%02x", ch
, keystate
[ch
]));
3142 memcpy(oldstate
, keystate
, sizeof(oldstate
));
3146 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
3147 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
3151 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
3152 if ((cfg
.funky_type
== 3 ||
3153 (cfg
.funky_type
<= 1 && term
->app_keypad_keys
&&
3155 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
3157 wParam
= VK_EXECUTE
;
3159 /* UnToggle NUMLock */
3160 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
3161 keystate
[VK_NUMLOCK
] ^= 1;
3164 /* And write back the 'adjusted' state */
3165 SetKeyboardState(keystate
);
3168 /* Disable Auto repeat if required */
3169 if (term
->repeat_off
&&
3170 (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
3173 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
3176 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
3178 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
3179 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
3180 if (cfg
.ctrlaltkeys
)
3181 keystate
[VK_MENU
] = 0;
3183 keystate
[VK_RMENU
] = 0x80;
3188 alt_pressed
= (left_alt
&& key_down
);
3190 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
3191 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
3192 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
3194 /* Note if AltGr was pressed and if it was used as a compose key */
3195 if (!compose_state
) {
3196 compose_key
= 0x100;
3197 if (cfg
.compose_key
) {
3198 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
3199 compose_key
= wParam
;
3201 if (wParam
== VK_APPS
)
3202 compose_key
= wParam
;
3205 if (wParam
== compose_key
) {
3206 if (compose_state
== 0
3207 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
3209 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
3213 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
3216 if (compose_state
> 1 && left_alt
)
3219 /* Sanitize the number pad if not using a PC NumPad */
3220 if (left_alt
|| (term
->app_keypad_keys
&& !cfg
.no_applic_k
3221 && cfg
.funky_type
!= 2)
3222 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
3223 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
3227 nParam
= VK_NUMPAD0
;
3230 nParam
= VK_NUMPAD1
;
3233 nParam
= VK_NUMPAD2
;
3236 nParam
= VK_NUMPAD3
;
3239 nParam
= VK_NUMPAD4
;
3242 nParam
= VK_NUMPAD5
;
3245 nParam
= VK_NUMPAD6
;
3248 nParam
= VK_NUMPAD7
;
3251 nParam
= VK_NUMPAD8
;
3254 nParam
= VK_NUMPAD9
;
3257 nParam
= VK_DECIMAL
;
3261 if (keystate
[VK_NUMLOCK
] & 1)
3268 /* If a key is pressed and AltGr is not active */
3269 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
3270 /* Okay, prepare for most alts then ... */
3274 /* Lets see if it's a pattern we know all about ... */
3275 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
3276 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
3279 if (wParam
== VK_NEXT
&& shift_state
== 1) {
3280 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
3283 if (wParam
== VK_INSERT
&& shift_state
== 1) {
3284 term_do_paste(term
);
3287 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
3290 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
3291 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
3294 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
&&
3295 (cfg
.resize_action
!= RESIZE_DISABLED
)) {
3296 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) != KF_REPEAT
)
3300 /* Control-Numlock for app-keypad mode switch */
3301 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
3302 term
->app_keypad_keys
^= 1;
3306 /* Nethack keypad */
3307 if (cfg
.nethack_keypad
&& !left_alt
) {
3310 *p
++ = shift_state ?
'B' : 'b';
3313 *p
++ = shift_state ?
'J' : 'j';
3316 *p
++ = shift_state ?
'N' : 'n';
3319 *p
++ = shift_state ?
'H' : 'h';
3322 *p
++ = shift_state ?
'.' : '.';
3325 *p
++ = shift_state ?
'L' : 'l';
3328 *p
++ = shift_state ?
'Y' : 'y';
3331 *p
++ = shift_state ?
'K' : 'k';
3334 *p
++ = shift_state ?
'U' : 'u';
3339 /* Application Keypad */
3343 if (cfg
.funky_type
== 3 ||
3344 (cfg
.funky_type
<= 1 &&
3345 term
->app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3359 if (term
->app_keypad_keys
&& !cfg
.no_applic_k
)
3396 if (cfg
.funky_type
== 2) {
3401 } else if (shift_state
)
3408 if (cfg
.funky_type
== 2)
3412 if (cfg
.funky_type
== 2)
3416 if (cfg
.funky_type
== 2)
3421 if (HIWORD(lParam
) & KF_EXTENDED
)
3426 if (term
->vt52_mode
) {
3427 if (xkey
>= 'P' && xkey
<= 'S')
3428 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3430 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3432 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3437 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3438 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3442 if (wParam
== VK_BACK
&& shift_state
== 1) { /* Shift Backspace */
3443 /* We do the opposite of what is configured */
3444 *p
++ = (cfg
.bksp_is_delete ?
0x08 : 0x7F);
3448 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3454 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3458 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3462 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3467 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3472 /* Control-2 to Control-8 are special */
3473 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3474 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3477 if (shift_state
== 2 && wParam
== 0xBD) {
3481 if (shift_state
== 2 && wParam
== 0xDF) {
3485 if (shift_state
== 0 && wParam
== VK_RETURN
&& term
->cr_lf_return
) {
3492 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3493 * for integer decimal nn.)
3495 * We also deal with the weird ones here. Linux VCs replace F1
3496 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3497 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3503 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3506 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3509 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3512 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3515 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3518 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3521 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3524 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3527 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3530 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3563 if ((shift_state
&2) == 0) switch (wParam
) {
3583 /* Reorder edit keys to physical order */
3584 if (cfg
.funky_type
== 3 && code
<= 6)
3585 code
= "\0\2\1\4\5\3\6"[code
];
3587 if (term
->vt52_mode
&& code
> 0 && code
<= 6) {
3588 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3592 if (cfg
.funky_type
== 5 && /* SCO function keys */
3593 code
>= 11 && code
<= 34) {
3594 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3597 case VK_F1
: index
= 0; break;
3598 case VK_F2
: index
= 1; break;
3599 case VK_F3
: index
= 2; break;
3600 case VK_F4
: index
= 3; break;
3601 case VK_F5
: index
= 4; break;
3602 case VK_F6
: index
= 5; break;
3603 case VK_F7
: index
= 6; break;
3604 case VK_F8
: index
= 7; break;
3605 case VK_F9
: index
= 8; break;
3606 case VK_F10
: index
= 9; break;
3607 case VK_F11
: index
= 10; break;
3608 case VK_F12
: index
= 11; break;
3610 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3611 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3612 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3615 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3616 code
>= 1 && code
<= 6) {
3617 char codes
[] = "HL.FIG";
3621 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3625 if ((term
->vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3631 if (term
->vt52_mode
)
3632 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3635 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3638 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3639 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3642 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3643 if (term
->vt52_mode
)
3644 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3646 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3649 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3650 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3654 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3659 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3660 * some reason seems to send VK_CLEAR to Windows...).
3682 if (term
->vt52_mode
)
3683 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3685 int app_flg
= (term
->app_cursor_keys
&& !cfg
.no_applic_c
);
3688 * RDB: VT100 & VT102 manuals both state the
3689 * app cursor keys only work if the app keypad
3692 * SGT: That may well be true, but xterm
3693 * disagrees and so does at least one
3694 * application, so I've #if'ed this out and the
3695 * behaviour is back to PuTTY's original: app
3696 * cursor and app keypad are independently
3697 * switchable modes. If anyone complains about
3698 * _this_ I'll have to put in a configurable
3701 if (!term
->app_keypad_keys
)
3704 /* Useful mapping of Ctrl-arrows */
3705 if (shift_state
== 2)
3709 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3711 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3718 * Finally, deal with Return ourselves. (Win95 seems to
3719 * foul it up when Alt is pressed, for some reason.)
3721 if (wParam
== VK_RETURN
) { /* Return */
3727 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3728 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3733 /* Okay we've done everything interesting; let windows deal with
3734 * the boring stuff */
3738 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3739 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3741 keystate
[VK_CAPITAL
] = 0;
3744 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3745 #ifdef SHOW_TOASCII_RESULT
3746 if (r
== 1 && !key_down
) {
3748 if (in_utf(term
) || dbcs_screenfont
)
3749 debug((", (U+%04x)", alt_sum
));
3751 debug((", LCH(%d)", alt_sum
));
3753 debug((", ACH(%d)", keys
[0]));
3758 for (r1
= 0; r1
< r
; r1
++) {
3759 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3768 * Interrupt an ongoing paste. I'm not sure this is
3769 * sensible, but for the moment it's preferable to
3770 * having to faff about buffering things.
3775 for (i
= 0; i
< r
; i
++) {
3776 unsigned char ch
= (unsigned char) keys
[i
];
3778 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3783 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3787 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3788 MessageBeep(MB_ICONHAND
);
3792 term_seen_key_event(term
);
3793 luni_send(ldisc
, &keybuf
, 1, 1);
3801 if (in_utf(term
) || dbcs_screenfont
) {
3803 term_seen_key_event(term
);
3804 luni_send(ldisc
, &keybuf
, 1, 1);
3806 ch
= (char) alt_sum
;
3808 * We need not bother about stdin
3809 * backlogs here, because in GUI PuTTY
3810 * we can't do anything about it
3811 * anyway; there's no means of asking
3812 * Windows to hold off on KEYDOWN
3813 * messages. We _have_ to buffer
3814 * everything we're sent.
3816 term_seen_key_event(term
);
3817 ldisc_send(ldisc
, &ch
, 1, 1);
3821 term_seen_key_event(term
);
3822 lpage_send(ldisc
, kbd_codepage
, &ch
, 1, 1);
3824 if(capsOn
&& ch
< 0x80) {
3827 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3828 term_seen_key_event(term
);
3829 luni_send(ldisc
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3834 term_seen_key_event(term
);
3835 lpage_send(ldisc
, kbd_codepage
,
3836 cbuf
+!left_alt
, 1+!!left_alt
, 1);
3842 /* This is so the ALT-Numpad and dead keys work correctly. */
3847 /* If we're definitly not building up an ALT-54321 then clear it */
3850 /* If we will be using alt_sum fix the 256s */
3851 else if (keys
[0] && (in_utf(term
) || dbcs_screenfont
))
3856 * ALT alone may or may not want to bring up the System menu.
3857 * If it's not meant to, we return 0 on presses or releases of
3858 * ALT, to show that we've swallowed the keystroke. Otherwise
3859 * we return -1, which means Windows will give the keystroke
3860 * its default handling (i.e. bring up the System menu).
3862 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3868 void request_paste(void)
3871 * In Windows, pasting is synchronous: we can read the
3872 * clipboard with no difficulty, so request_paste() can just go
3875 term_do_paste(term
);
3878 void set_title(char *title
)
3881 window_name
= smalloc(1 + strlen(title
));
3882 strcpy(window_name
, title
);
3883 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3884 SetWindowText(hwnd
, title
);
3887 void set_icon(char *title
)
3890 icon_name
= smalloc(1 + strlen(title
));
3891 strcpy(icon_name
, title
);
3892 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3893 SetWindowText(hwnd
, title
);
3896 void set_sbar(int total
, int start
, int page
)
3900 if (is_full_screen() ?
!cfg
.scrollbar_in_fullscreen
: !cfg
.scrollbar
)
3903 si
.cbSize
= sizeof(si
);
3904 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3906 si
.nMax
= total
- 1;
3910 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3913 Context
get_ctx(void)
3919 SelectPalette(hdc
, pal
, FALSE
);
3925 void free_ctx(Context ctx
)
3927 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3928 ReleaseDC(hwnd
, ctx
);
3931 static void real_palette_set(int n
, int r
, int g
, int b
)
3934 logpal
->palPalEntry
[n
].peRed
= r
;
3935 logpal
->palPalEntry
[n
].peGreen
= g
;
3936 logpal
->palPalEntry
[n
].peBlue
= b
;
3937 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3938 colours
[n
] = PALETTERGB(r
, g
, b
);
3939 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3941 colours
[n
] = RGB(r
, g
, b
);
3944 void palette_set(int n
, int r
, int g
, int b
)
3946 static const int first
[21] = {
3947 0, 2, 4, 6, 8, 10, 12, 14,
3948 1, 3, 5, 7, 9, 11, 13, 15,
3951 real_palette_set(first
[n
], r
, g
, b
);
3953 real_palette_set(first
[n
] + 1, r
, g
, b
);
3955 HDC hdc
= get_ctx();
3956 UnrealizeObject(pal
);
3957 RealizePalette(hdc
);
3962 void palette_reset(void)
3966 for (i
= 0; i
< NCOLOURS
; i
++) {
3968 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3969 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3970 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3971 logpal
->palPalEntry
[i
].peFlags
= 0;
3972 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3973 defpal
[i
].rgbtGreen
,
3974 defpal
[i
].rgbtBlue
);
3976 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3977 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3982 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3984 RealizePalette(hdc
);
3989 void write_aclip(char *data
, int len
, int must_deselect
)
3994 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3997 lock
= GlobalLock(clipdata
);
4000 memcpy(lock
, data
, len
);
4001 ((unsigned char *) lock
)[len
] = 0;
4002 GlobalUnlock(clipdata
);
4005 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
4007 if (OpenClipboard(hwnd
)) {
4009 SetClipboardData(CF_TEXT
, clipdata
);
4012 GlobalFree(clipdata
);
4015 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
4019 * Note: unlike write_aclip() this will not append a nul.
4021 void write_clip(wchar_t * data
, int len
, int must_deselect
)
4023 HGLOBAL clipdata
, clipdata2
, clipdata3
;
4025 void *lock
, *lock2
, *lock3
;
4027 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
4029 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
4030 len
* sizeof(wchar_t));
4031 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
4033 if (!clipdata
|| !clipdata2
) {
4035 GlobalFree(clipdata
);
4037 GlobalFree(clipdata2
);
4040 if (!(lock
= GlobalLock(clipdata
)))
4042 if (!(lock2
= GlobalLock(clipdata2
)))
4045 memcpy(lock
, data
, len
* sizeof(wchar_t));
4046 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
4048 if (cfg
.rtf_paste
) {
4049 wchar_t unitab
[256];
4051 unsigned char *tdata
= (unsigned char *)lock2
;
4052 wchar_t *udata
= (wchar_t *)lock
;
4053 int rtflen
= 0, uindex
= 0, tindex
= 0;
4055 int multilen
, blen
, alen
, totallen
, i
;
4056 char before
[16], after
[4];
4058 get_unitab(CP_ACP
, unitab
, 0);
4060 rtfsize
= 100 + strlen(cfg
.font
);
4061 rtf
= smalloc(rtfsize
);
4062 sprintf(rtf
, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
4063 GetACP(), cfg
.font
);
4064 rtflen
= strlen(rtf
);
4067 * We want to construct a piece of RTF that specifies the
4068 * same Unicode text. To do this we will read back in
4069 * parallel from the Unicode data in `udata' and the
4070 * non-Unicode data in `tdata'. For each character in
4071 * `tdata' which becomes the right thing in `udata' when
4072 * looked up in `unitab', we just copy straight over from
4073 * tdata. For each one that doesn't, we must WCToMB it
4074 * individually and produce a \u escape sequence.
4076 * It would probably be more robust to just bite the bullet
4077 * and WCToMB each individual Unicode character one by one,
4078 * then MBToWC each one back to see if it was an accurate
4079 * translation; but that strikes me as a horrifying number
4080 * of Windows API calls so I want to see if this faster way
4081 * will work. If it screws up badly we can always revert to
4082 * the simple and slow way.
4084 while (tindex
< len2
&& uindex
< len
&&
4085 tdata
[tindex
] && udata
[uindex
]) {
4086 if (tindex
+ 1 < len2
&&
4087 tdata
[tindex
] == '\r' &&
4088 tdata
[tindex
+1] == '\n') {
4092 if (unitab
[tdata
[tindex
]] == udata
[uindex
]) {
4098 multilen
= WideCharToMultiByte(CP_ACP
, 0, unitab
+uindex
, 1,
4099 NULL
, 0, NULL
, NULL
);
4100 if (multilen
!= 1) {
4101 blen
= sprintf(before
, "{\\uc%d\\u%d", multilen
,
4103 alen
= 1; strcpy(after
, "}");
4105 blen
= sprintf(before
, "\\u%d", udata
[uindex
]);
4106 alen
= 0; after
[0] = '\0';
4109 assert(tindex
+ multilen
<= len2
);
4110 totallen
= blen
+ alen
;
4111 for (i
= 0; i
< multilen
; i
++) {
4112 if (tdata
[tindex
+i
] == '\\' ||
4113 tdata
[tindex
+i
] == '{' ||
4114 tdata
[tindex
+i
] == '}')
4116 else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A)
4117 totallen
+= 6; /* \par\r\n */
4118 else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20)
4124 if (rtfsize
< rtflen
+ totallen
+ 3) {
4125 rtfsize
= rtflen
+ totallen
+ 512;
4126 rtf
= srealloc(rtf
, rtfsize
);
4129 strcpy(rtf
+ rtflen
, before
); rtflen
+= blen
;
4130 for (i
= 0; i
< multilen
; i
++) {
4131 if (tdata
[tindex
+i
] == '\\' ||
4132 tdata
[tindex
+i
] == '{' ||
4133 tdata
[tindex
+i
] == '}') {
4134 rtf
[rtflen
++] = '\\';
4135 rtf
[rtflen
++] = tdata
[tindex
+i
];
4136 } else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A) {
4137 rtflen
+= sprintf(rtf
+rtflen
, "\\par\r\n");
4138 } else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20) {
4139 rtflen
+= sprintf(rtf
+rtflen
, "\\'%02x", tdata
[tindex
+i
]);
4141 rtf
[rtflen
++] = tdata
[tindex
+i
];
4144 strcpy(rtf
+ rtflen
, after
); rtflen
+= alen
;
4150 strcpy(rtf
+ rtflen
, "}");
4153 clipdata3
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, rtflen
);
4154 if (clipdata3
&& (lock3
= GlobalLock(clipdata3
)) != NULL
) {
4156 GlobalUnlock(clipdata3
);
4162 GlobalUnlock(clipdata
);
4163 GlobalUnlock(clipdata2
);
4166 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
4168 if (OpenClipboard(hwnd
)) {
4170 SetClipboardData(CF_UNICODETEXT
, clipdata
);
4171 SetClipboardData(CF_TEXT
, clipdata2
);
4173 SetClipboardData(RegisterClipboardFormat(CF_RTF
), clipdata3
);
4176 GlobalFree(clipdata
);
4177 GlobalFree(clipdata2
);
4181 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
4184 void get_clip(wchar_t ** p
, int *len
)
4186 static HGLOBAL clipdata
= NULL
;
4187 static wchar_t *converted
= 0;
4196 GlobalUnlock(clipdata
);
4199 } else if (OpenClipboard(NULL
)) {
4200 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
4202 *p
= GlobalLock(clipdata
);
4204 for (p2
= *p
; *p2
; p2
++);
4208 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
4212 s
= GlobalLock(clipdata
);
4213 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
4214 *p
= converted
= smalloc(i
* sizeof(wchar_t));
4215 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
4228 * Move `lines' lines from position `from' to position `to' in the
4231 void optimised_move(int to
, int from
, int lines
)
4236 min
= (to
< from ? to
: from
);
4237 max
= to
+ from
- min
;
4239 r
.left
= offset_width
;
4240 r
.right
= offset_width
+ term
->cols
* font_width
;
4241 r
.top
= offset_height
+ min
* font_height
;
4242 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
4243 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
4248 * Print a message box and perform a fatal exit.
4250 void fatalbox(char *fmt
, ...)
4256 vsprintf(stuff
, fmt
, ap
);
4258 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
4263 * Print a modal (Really Bad) message box and perform a fatal exit.
4265 void modalfatalbox(char *fmt
, ...)
4271 vsprintf(stuff
, fmt
, ap
);
4273 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error",
4274 MB_SYSTEMMODAL
| MB_ICONERROR
| MB_OK
);
4279 * Manage window caption / taskbar flashing, if enabled.
4280 * 0 = stop, 1 = maintain, 2 = start
4282 static void flash_window(int mode
)
4284 static long last_flash
= 0;
4285 static int flashing
= 0;
4286 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
4289 FlashWindow(hwnd
, FALSE
);
4293 } else if (mode
== 2) {
4296 last_flash
= GetTickCount();
4298 FlashWindow(hwnd
, TRUE
);
4301 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
4304 long now
= GetTickCount();
4305 long fdiff
= now
- last_flash
;
4306 if (fdiff
< 0 || fdiff
> 450) {
4308 FlashWindow(hwnd
, TRUE
); /* toggle */
4319 if (mode
== BELL_DEFAULT
) {
4321 * For MessageBeep style bells, we want to be careful of
4322 * timing, because they don't have the nice property of
4323 * PlaySound bells that each one cancels the previous
4324 * active one. So we limit the rate to one per 50ms or so.
4326 static long lastbeep
= 0;
4329 beepdiff
= GetTickCount() - lastbeep
;
4330 if (beepdiff
>= 0 && beepdiff
< 50)
4334 * The above MessageBeep call takes time, so we record the
4335 * time _after_ it finishes rather than before it starts.
4337 lastbeep
= GetTickCount();
4338 } else if (mode
== BELL_WAVEFILE
) {
4339 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
4340 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
4341 sprintf(buf
, "Unable to play sound file\n%s\n"
4342 "Using default sound instead", cfg
.bell_wavefile
);
4343 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
4344 MB_OK
| MB_ICONEXCLAMATION
);
4345 cfg
.beep
= BELL_DEFAULT
;
4348 /* Otherwise, either visual bell or disabled; do nothing here */
4349 if (!term
->has_focus
) {
4350 flash_window(2); /* start */
4355 * Minimise or restore the window in response to a server-side
4358 void set_iconic(int iconic
)
4360 if (IsIconic(hwnd
)) {
4362 ShowWindow(hwnd
, SW_RESTORE
);
4365 ShowWindow(hwnd
, SW_MINIMIZE
);
4370 * Move the window in response to a server-side request.
4372 void move_window(int x
, int y
)
4374 if (cfg
.resize_action
== RESIZE_DISABLED
||
4375 cfg
.resize_action
== RESIZE_FONT
||
4379 SetWindowPos(hwnd
, NULL
, x
, y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
);
4383 * Move the window to the top or bottom of the z-order in response
4384 * to a server-side request.
4386 void set_zorder(int top
)
4388 if (cfg
.alwaysontop
)
4389 return; /* ignore */
4390 SetWindowPos(hwnd
, top ? HWND_TOP
: HWND_BOTTOM
, 0, 0, 0, 0,
4391 SWP_NOMOVE
| SWP_NOSIZE
);
4395 * Refresh the window in response to a server-side request.
4397 void refresh_window(void)
4399 InvalidateRect(hwnd
, NULL
, TRUE
);
4403 * Maximise or restore the window in response to a server-side
4406 void set_zoomed(int zoomed
)
4408 if (IsZoomed(hwnd
)) {
4410 ShowWindow(hwnd
, SW_RESTORE
);
4413 ShowWindow(hwnd
, SW_MAXIMIZE
);
4418 * Report whether the window is iconic, for terminal reports.
4422 return IsIconic(hwnd
);
4426 * Report the window's position, for terminal reports.
4428 void get_window_pos(int *x
, int *y
)
4431 GetWindowRect(hwnd
, &r
);
4437 * Report the window's pixel size, for terminal reports.
4439 void get_window_pixels(int *x
, int *y
)
4442 GetWindowRect(hwnd
, &r
);
4443 *x
= r
.right
- r
.left
;
4444 *y
= r
.bottom
- r
.top
;
4448 * Return the window or icon title.
4450 char *get_window_title(int icon
)
4452 return icon ? icon_name
: window_name
;
4456 * See if we're in full-screen mode.
4458 int is_full_screen()
4460 if (!IsZoomed(hwnd
))
4462 if (GetWindowLong(hwnd
, GWL_STYLE
) & WS_CAPTION
)
4467 /* Get the rect/size of a full screen window using the nearest available
4468 * monitor in multimon systems; default to something sensible if only
4469 * one monitor is present. */
4470 static int get_fullscreen_rect(RECT
* ss
)
4472 #ifdef MONITOR_DEFAULTTONEAREST
4475 mon
= MonitorFromWindow(hwnd
, MONITOR_DEFAULTTONEAREST
);
4476 mi
.cbSize
= sizeof(mi
);
4477 GetMonitorInfo(mon
, &mi
);
4479 /* structure copy */
4483 /* could also use code like this:
4484 ss->left = ss->top = 0;
4485 ss->right = GetSystemMetrics(SM_CXSCREEN);
4486 ss->bottom = GetSystemMetrics(SM_CYSCREEN);
4488 return GetClientRect(GetDesktopWindow(), ss
);
4494 * Go full-screen. This should only be called when we are already
4497 void make_full_screen()
4502 assert(IsZoomed(hwnd
));
4504 if (is_full_screen())
4507 /* Remove the window furniture. */
4508 style
= GetWindowLong(hwnd
, GWL_STYLE
);
4509 style
&= ~(WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
);
4510 if (cfg
.scrollbar_in_fullscreen
)
4511 style
|= WS_VSCROLL
;
4513 style
&= ~WS_VSCROLL
;
4514 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4516 /* Resize ourselves to exactly cover the nearest monitor. */
4517 get_fullscreen_rect(&ss
);
4518 SetWindowPos(hwnd
, HWND_TOP
, ss
.left
, ss
.top
,
4523 /* Tick the menu item in the System menu. */
4524 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4529 * Clear the full-screen attributes.
4531 void clear_full_screen()
4533 DWORD oldstyle
, style
;
4535 /* Reinstate the window furniture. */
4536 style
= oldstyle
= GetWindowLong(hwnd
, GWL_STYLE
);
4537 style
|= WS_CAPTION
| WS_BORDER
;
4538 if (cfg
.resize_action
== RESIZE_DISABLED
)
4539 style
&= ~WS_THICKFRAME
;
4541 style
|= WS_THICKFRAME
;
4543 style
|= WS_VSCROLL
;
4545 style
&= ~WS_VSCROLL
;
4546 if (style
!= oldstyle
) {
4547 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4548 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
4549 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
4553 /* Untick the menu item in the System menu. */
4554 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4559 * Toggle full-screen mode.
4561 void flip_full_screen()
4563 if (is_full_screen()) {
4564 ShowWindow(hwnd
, SW_RESTORE
);
4565 } else if (IsZoomed(hwnd
)) {
4568 SendMessage(hwnd
, WM_FULLSCR_ON_MAX
, 0, 0);
4569 ShowWindow(hwnd
, SW_MAXIMIZE
);
4573 void frontend_keypress(void *handle
)
4576 * Keypress termination in non-Close-On-Exit mode is not
4577 * currently supported in PuTTY proper, because the window
4578 * always has a perfectly good Close button anyway. So we do