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 get_fullscreen_rect(RECT
* ss
);
113 static time_t last_movement
= 0;
115 static int caret_x
= -1, caret_y
= -1;
117 #define FONT_NORMAL 0
119 #define FONT_UNDERLINE 2
120 #define FONT_BOLDUND 3
121 #define FONT_WIDE 0x04
122 #define FONT_HIGH 0x08
123 #define FONT_NARROW 0x10
125 #define FONT_OEM 0x20
126 #define FONT_OEMBOLD 0x21
127 #define FONT_OEMUND 0x22
128 #define FONT_OEMBOLDUND 0x23
130 #define FONT_MAXNO 0x2F
132 static HFONT fonts
[FONT_MAXNO
];
133 static LOGFONT lfont
;
134 static int fontflag
[FONT_MAXNO
];
136 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
144 static COLORREF colours
[NCOLOURS
];
146 static LPLOGPALETTE logpal
;
147 static RGBTRIPLE defpal
[NCOLOURS
];
151 static HBITMAP caretbm
;
153 static int dbltime
, lasttime
, lastact
;
154 static Mouse_Button lastbtn
;
156 /* this allows xterm-style mouse handling. */
157 static int send_raw_mouse
= 0;
158 static int wheel_accumulator
= 0;
160 static char *window_name
, *icon_name
;
162 static int compose_state
= 0;
164 static int wsa_started
= 0;
166 static OSVERSIONINFO osVersion
;
168 static UINT wm_mousewheel
= WM_MOUSEWHEEL
;
170 /* Dummy routine, only required in plink. */
171 void ldisc_update(int echo
, int edit
)
175 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
177 static char appname
[] = "PuTTY";
182 int guess_width
, guess_height
;
185 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
187 winsock_ver
= MAKEWORD(1, 1);
188 if (WSAStartup(winsock_ver
, &wsadata
)) {
189 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
190 MB_OK
| MB_ICONEXCLAMATION
);
193 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
194 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
195 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
200 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
203 InitCommonControls();
205 /* Ensure a Maximize setting in Explorer doesn't maximise the
210 ZeroMemory(&osVersion
, sizeof(osVersion
));
211 osVersion
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
212 if (!GetVersionEx ( (OSVERSIONINFO
*) &osVersion
)) {
213 MessageBox(NULL
, "Windows refuses to report a version",
214 "PuTTY Fatal Error", MB_OK
| MB_ICONEXCLAMATION
);
220 * If we're running a version of Windows that doesn't support
221 * WM_MOUSEWHEEL, find out what message number we should be
224 if (osVersion
.dwMajorVersion
< 4 ||
225 (osVersion
.dwMajorVersion
== 4 &&
226 osVersion
.dwPlatformId
!= VER_PLATFORM_WIN32_NT
))
227 wm_mousewheel
= RegisterWindowMessage("MSWHEEL_ROLLMSG");
230 * See if we can find our Help file.
233 char b
[2048], *p
, *q
, *r
;
235 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
237 p
= strrchr(b
, '\\');
238 if (p
&& p
>= r
) r
= p
+1;
240 if (q
&& q
>= r
) r
= q
+1;
241 strcpy(r
, "putty.hlp");
242 if ( (fp
= fopen(b
, "r")) != NULL
) {
243 help_path
= dupstr(b
);
247 strcpy(r
, "putty.cnt");
248 if ( (fp
= fopen(b
, "r")) != NULL
) {
249 help_has_contents
= TRUE
;
252 help_has_contents
= FALSE
;
256 * Process the command line.
262 default_protocol
= DEFAULT_PROTOCOL
;
263 default_port
= DEFAULT_PORT
;
264 cfg
.logtype
= LGTYP_NONE
;
266 do_defaults(NULL
, &cfg
);
271 * Process a couple of command-line options which are more
272 * easily dealt with before the line is broken up into
273 * words. These are the soon-to-be-defunct @sessionname and
274 * the internal-use-only &sharedmemoryhandle, neither of
275 * which are combined with anything else.
277 while (*p
&& isspace(*p
))
281 while (i
> 1 && isspace(p
[i
- 1]))
284 do_defaults(p
+ 1, &cfg
);
285 if (!*cfg
.host
&& !do_config()) {
289 } else if (*p
== '&') {
291 * An initial & means we've been given a command line
292 * containing the hex value of a HANDLE for a file
293 * mapping object, which we must then extract as a
298 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
299 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
300 0, 0, sizeof(Config
))) != NULL
) {
303 CloseHandle(filemap
);
304 } else if (!do_config()) {
310 * Otherwise, break up the command line and deal with
316 split_into_argv(cmdline
, &argc
, &argv
, NULL
);
318 for (i
= 0; i
< argc
; i
++) {
322 ret
= cmdline_process_param(p
, i
+1<argc?argv
[i
+1]:NULL
, 1);
324 cmdline_error("option \"%s\" requires an argument", p
);
325 } else if (ret
== 2) {
326 i
++; /* skip next argument */
327 } else if (ret
== 1) {
328 continue; /* nothing further needs doing */
329 } else if (!strcmp(p
, "-cleanup")) {
331 * `putty -cleanup'. Remove all registry
332 * entries associated with PuTTY, and also find
333 * and delete the random seed file.
336 "This procedure will remove ALL Registry\n"
337 "entries associated with PuTTY, and will\n"
338 "also remove the PuTTY random seed file.\n"
340 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
341 "SESSIONS. Are you really sure you want\n"
344 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
348 } else if (*p
!= '-') {
352 * If we already have a host name, treat
353 * this argument as a port number. NB we
354 * have to treat this as a saved -P
355 * argument, so that it will be deferred
356 * until it's a good moment to run it.
358 int ret
= cmdline_process_param("-P", p
, 1);
360 } else if (!strncmp(q
, "telnet:", 7)) {
362 * If the hostname starts with "telnet:",
363 * set the protocol to Telnet and process
364 * the string as a Telnet URL.
369 if (q
[0] == '/' && q
[1] == '/')
371 cfg
.protocol
= PROT_TELNET
;
373 while (*p
&& *p
!= ':' && *p
!= '/')
382 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
383 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
387 * Otherwise, treat this argument as a host
390 while (*p
&& !isspace(*p
))
394 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
395 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
404 if (!*cfg
.host
&& !do_config()) {
410 * Trim leading whitespace off the hostname if it's there.
413 int space
= strspn(cfg
.host
, " \t");
414 memmove(cfg
.host
, cfg
.host
+space
, 1+strlen(cfg
.host
)-space
);
417 /* See if host is of the form user@host */
418 if (cfg
.host
[0] != '\0') {
419 char *atsign
= strchr(cfg
.host
, '@');
420 /* Make sure we're not overflowing the user field */
422 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
423 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
424 cfg
.username
[atsign
- cfg
.host
] = '\0';
426 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
431 * Trim a colon suffix off the hostname if it's there.
433 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
436 * Remove any remaining whitespace from the hostname.
440 while (cfg
.host
[p2
] != '\0') {
441 if (cfg
.host
[p2
] != ' ' && cfg
.host
[p2
] != '\t') {
442 cfg
.host
[p1
] = cfg
.host
[p2
];
452 * Select protocol. This is farmed out into a table in a
453 * separate file to enable an ssh-free variant.
458 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
459 if (backends
[i
].protocol
== cfg
.protocol
) {
460 back
= backends
[i
].backend
;
464 MessageBox(NULL
, "Unsupported protocol number found",
465 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
471 /* Check for invalid Port number (i.e. zero) */
473 MessageBox(NULL
, "Invalid Port Number",
474 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
481 wndclass
.lpfnWndProc
= WndProc
;
482 wndclass
.cbClsExtra
= 0;
483 wndclass
.cbWndExtra
= 0;
484 wndclass
.hInstance
= inst
;
485 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
486 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
487 wndclass
.hbrBackground
= NULL
;
488 wndclass
.lpszMenuName
= NULL
;
489 wndclass
.lpszClassName
= appname
;
491 RegisterClass(&wndclass
);
501 * Guess some defaults for the window size. This all gets
502 * updated later, so we don't really care too much. However, we
503 * do want the font width/height guesses to correspond to a
504 * large font rather than a small one...
511 term_size(term
, cfg
.height
, cfg
.width
, cfg
.savelines
);
512 guess_width
= extra_width
+ font_width
* term
->cols
;
513 guess_height
= extra_height
+ font_height
* term
->rows
;
516 get_fullscreen_rect(&r
);
517 if (guess_width
> r
.right
- r
.left
)
518 guess_width
= r
.right
- r
.left
;
519 if (guess_height
> r
.bottom
- r
.top
)
520 guess_height
= r
.bottom
- r
.top
;
524 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
527 winmode
&= ~(WS_VSCROLL
);
528 if (cfg
.resize_action
== RESIZE_DISABLED
)
529 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
531 exwinmode
|= WS_EX_TOPMOST
;
533 exwinmode
|= WS_EX_CLIENTEDGE
;
534 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
535 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
536 guess_width
, guess_height
,
537 NULL
, NULL
, inst
, NULL
);
541 * Initialise the fonts, simultaneously correcting the guesses
542 * for font_{width,height}.
547 * Correct the guesses for extra_{width,height}.
551 GetWindowRect(hwnd
, &wr
);
552 GetClientRect(hwnd
, &cr
);
553 offset_width
= offset_height
= cfg
.window_border
;
554 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
555 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
559 * Resize the window, now we know what size we _really_ want it
562 guess_width
= extra_width
+ font_width
* term
->cols
;
563 guess_height
= extra_height
+ font_height
* term
->rows
;
564 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
565 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
568 * Set up a caret bitmap, with no content.
572 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
573 bits
= smalloc(size
);
574 memset(bits
, 0, size
);
575 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
578 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
581 * Initialise the scroll bar.
586 si
.cbSize
= sizeof(si
);
587 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
589 si
.nMax
= term
->rows
- 1;
590 si
.nPage
= term
->rows
;
592 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
596 * Start up the telnet connection.
600 char msg
[1024], *title
;
603 error
= back
->init((void *)term
, &backhandle
,
604 cfg
.host
, cfg
.port
, &realhost
, cfg
.tcp_nodelay
);
606 sprintf(msg
, "Unable to open connection to\n"
607 "%.800s\n" "%s", cfg
.host
, error
);
608 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
611 window_name
= icon_name
= NULL
;
613 title
= cfg
.wintitle
;
615 sprintf(msg
, "%s - PuTTY", realhost
);
624 * Connect the terminal to the backend for resize purposes.
626 term_provide_resize_fn(term
, back
->size
, backhandle
);
628 session_closed
= FALSE
;
631 * Prepare the mouse handler.
633 lastact
= MA_NOTHING
;
634 lastbtn
= MBT_NOTHING
;
635 dbltime
= GetDoubleClickTime();
638 * Set up the session-control options on the system menu.
641 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
645 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
646 if (cfg
.protocol
== PROT_TELNET
) {
648 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
649 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
650 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
651 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
652 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
653 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
654 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
655 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
656 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
657 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
658 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
659 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
660 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
661 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
662 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
663 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
664 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
666 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
668 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
669 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
670 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
671 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
674 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
675 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
677 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
678 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
679 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
680 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
681 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
682 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
683 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
684 AppendMenu(m
, (cfg
.resize_action
== RESIZE_DISABLED
) ?
685 MF_GRAYED
: MF_ENABLED
, IDM_FULLSCREEN
, "&Full Screen");
686 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
688 AppendMenu(m
, MF_ENABLED
, IDM_HELP
, "&Help");
689 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
693 * Set up the initial input locale.
695 set_input_locale(GetKeyboardLayout(0));
698 * Open the initial log file if there is one.
703 * Finally show the window!
705 ShowWindow(hwnd
, show
);
706 SetForegroundWindow(hwnd
);
709 * Set the palette up.
715 term
->has_focus
= (GetForegroundWindow() == hwnd
);
718 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
719 int timer_id
= 0, long_timer
= 0;
721 while (msg
.message
!= WM_QUIT
) {
722 /* Sometimes DispatchMessage calls routines that use their own
723 * GetMessage loop, setup this timer so we get some control back.
725 * Also call term_update() from the timer so that if the host
726 * is sending data flat out we still do redraws.
728 if (timer_id
&& long_timer
) {
729 KillTimer(hwnd
, timer_id
);
730 long_timer
= timer_id
= 0;
733 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
734 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
735 DispatchMessage(&msg
);
737 /* Make sure we blink everything that needs it. */
740 /* Send the paste buffer if there's anything to send */
743 /* If there's nothing new in the queue then we can do everything
744 * we've delayed, reading the socket, writing, and repainting
747 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
750 if (pending_netevent
) {
751 enact_pending_netevent();
753 /* Force the cursor blink on */
756 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
760 /* Okay there is now nothing to do so we make sure the screen is
761 * completely up to date then tell windows to call us in a little
765 KillTimer(hwnd
, timer_id
);
769 if (GetCapture() != hwnd
||
771 !(cfg
.mouse_override
&& is_shift_pressed())))
776 flash_window(1); /* maintain */
778 /* The messages seem unreliable; especially if we're being tricky */
779 term
->has_focus
= (GetForegroundWindow() == hwnd
);
782 /* Hmm, term_update didn't want to do an update too soon ... */
783 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
784 else if (!term
->has_focus
)
785 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
787 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
790 /* There's no point rescanning everything in the message queue
791 * so we do an apparently unnecessary wait here
794 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
799 cleanup_exit(msg
.wParam
); /* this doesn't return... */
800 return msg
.wParam
; /* ... but optimiser doesn't know */
806 void cleanup_exit(int code
)
819 if (cfg
.protocol
== PROT_SSH
) {
830 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
832 char *do_select(SOCKET skt
, int startup
)
837 events
= (FD_CONNECT
| FD_READ
| FD_WRITE
|
838 FD_OOB
| FD_CLOSE
| FD_ACCEPT
);
843 return "do_select(): internal error (hwnd==NULL)";
844 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
845 switch (WSAGetLastError()) {
847 return "Network is down";
849 return "WSAAsyncSelect(): unknown error";
856 * set or clear the "raw mouse message" mode
858 void set_raw_mouse_mode(int activate
)
860 activate
= activate
&& !cfg
.no_mouse_rep
;
861 send_raw_mouse
= activate
;
862 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
866 * Print a message box and close the connection.
868 void connection_fatal(char *fmt
, ...)
874 vsprintf(stuff
, fmt
, ap
);
876 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
877 if (cfg
.close_on_exit
== COE_ALWAYS
)
880 session_closed
= TRUE
;
881 SetWindowText(hwnd
, "PuTTY (inactive)");
886 * Report an error at the command-line parsing stage.
888 void cmdline_error(char *fmt
, ...)
894 vsprintf(stuff
, fmt
, ap
);
896 MessageBox(hwnd
, stuff
, "PuTTY Command Line Error", MB_ICONERROR
| MB_OK
);
901 * Actually do the job requested by a WM_NETEVENT
903 static void enact_pending_netevent(void)
905 static int reentering
= 0;
906 extern int select_result(WPARAM
, LPARAM
);
910 return; /* don't unpend the pending */
912 pending_netevent
= FALSE
;
915 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
918 if (ret
== 0 && !session_closed
) {
919 /* Abnormal exits will already have set session_closed and taken
920 * appropriate action. */
921 if (cfg
.close_on_exit
== COE_ALWAYS
||
922 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
924 session_closed
= TRUE
;
925 SetWindowText(hwnd
, "PuTTY (inactive)");
926 MessageBox(hwnd
, "Connection closed by remote host",
927 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
933 * Copy the colour palette from the configuration data into defpal.
934 * This is non-trivial because the colour indices are different.
936 static void cfgtopalette(void)
939 static const int ww
[] = {
940 6, 7, 8, 9, 10, 11, 12, 13,
941 14, 15, 16, 17, 18, 19, 20, 21,
942 0, 1, 2, 3, 4, 4, 5, 5
945 for (i
= 0; i
< 24; i
++) {
947 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
948 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
949 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
954 * Set up the colour palette.
956 static void init_palette(void)
959 HDC hdc
= GetDC(hwnd
);
961 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
962 logpal
= smalloc(sizeof(*logpal
)
963 - sizeof(logpal
->palPalEntry
)
964 + NCOLOURS
* sizeof(PALETTEENTRY
));
965 logpal
->palVersion
= 0x300;
966 logpal
->palNumEntries
= NCOLOURS
;
967 for (i
= 0; i
< NCOLOURS
; i
++) {
968 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
969 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
970 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
971 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
973 pal
= CreatePalette(logpal
);
975 SelectPalette(hdc
, pal
, FALSE
);
977 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
980 ReleaseDC(hwnd
, hdc
);
983 for (i
= 0; i
< NCOLOURS
; i
++)
984 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
988 for (i
= 0; i
< NCOLOURS
; i
++)
989 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
990 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
994 * Initialise all the fonts we will need initially. There may be as many as
995 * three or as few as one. The other (poentially) twentyone fonts are done
996 * if/when they are needed.
1000 * - check the font width and height, correcting our guesses if
1003 * - verify that the bold font is the same width as the ordinary
1004 * one, and engage shadow bolding if not.
1006 * - verify that the underlined font is the same width as the
1007 * ordinary one (manual underlining by means of line drawing can
1008 * be done in a pinch).
1010 static void init_fonts(int pick_width
, int pick_height
)
1017 int fw_dontcare
, fw_bold
;
1019 for (i
= 0; i
< FONT_MAXNO
; i
++)
1022 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1023 und_mode
= UND_FONT
;
1025 if (cfg
.fontisbold
) {
1026 fw_dontcare
= FW_BOLD
;
1029 fw_dontcare
= FW_DONTCARE
;
1036 font_height
= pick_height
;
1038 font_height
= cfg
.fontheight
;
1039 if (font_height
> 0) {
1041 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
1044 font_width
= pick_width
;
1046 #define f(i,c,w,u) \
1047 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
1048 c, OUT_DEFAULT_PRECIS, \
1049 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
1050 FIXED_PITCH | FF_DONTCARE, cfg.font)
1052 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
1054 lfont
.lfHeight
= font_height
;
1055 lfont
.lfWidth
= font_width
;
1056 lfont
.lfEscapement
= 0;
1057 lfont
.lfOrientation
= 0;
1058 lfont
.lfWeight
= fw_dontcare
;
1059 lfont
.lfItalic
= FALSE
;
1060 lfont
.lfUnderline
= FALSE
;
1061 lfont
.lfStrikeOut
= FALSE
;
1062 lfont
.lfCharSet
= cfg
.fontcharset
;
1063 lfont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
1064 lfont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
1065 lfont
.lfQuality
= DEFAULT_QUALITY
;
1066 lfont
.lfPitchAndFamily
= FIXED_PITCH
| FF_DONTCARE
;
1067 strncpy(lfont
.lfFaceName
, cfg
.font
, LF_FACESIZE
);
1069 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
1070 GetTextMetrics(hdc
, &tm
);
1072 if (pick_width
== 0 || pick_height
== 0) {
1073 font_height
= tm
.tmHeight
;
1074 font_width
= tm
.tmAveCharWidth
;
1076 font_dualwidth
= (tm
.tmAveCharWidth
!= tm
.tmMaxCharWidth
);
1078 #ifdef RDB_DEBUG_PATCH
1079 debug(23, "Primary font H=%d, AW=%d, MW=%d",
1080 tm
.tmHeight
, tm
.tmAveCharWidth
, tm
.tmMaxCharWidth
);
1085 DWORD cset
= tm
.tmCharSet
;
1086 memset(&info
, 0xFF, sizeof(info
));
1088 /* !!! Yes the next line is right */
1089 if (cset
== OEM_CHARSET
)
1090 font_codepage
= GetOEMCP();
1092 if (TranslateCharsetInfo
1093 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
1098 GetCPInfo(font_codepage
, &cpinfo
);
1099 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
1102 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
1105 * Some fonts, e.g. 9-pt Courier, draw their underlines
1106 * outside their character cell. We successfully prevent
1107 * screen corruption by clipping the text output, but then
1108 * we lose the underline completely. Here we try to work
1109 * out whether this is such a font, and if it is, we set a
1110 * flag that causes underlines to be drawn by hand.
1112 * Having tried other more sophisticated approaches (such
1113 * as examining the TEXTMETRIC structure or requesting the
1114 * height of a string), I think we'll do this the brute
1115 * force way: we create a small bitmap, draw an underlined
1116 * space on it, and test to see whether any pixels are
1117 * foreground-coloured. (Since we expect the underline to
1118 * go all the way across the character cell, we only search
1119 * down a single column of the bitmap, half way across.)
1123 HBITMAP und_bm
, und_oldbm
;
1127 und_dc
= CreateCompatibleDC(hdc
);
1128 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
1129 und_oldbm
= SelectObject(und_dc
, und_bm
);
1130 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
1131 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
1132 SetTextColor(und_dc
, RGB(255, 255, 255));
1133 SetBkColor(und_dc
, RGB(0, 0, 0));
1134 SetBkMode(und_dc
, OPAQUE
);
1135 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
1137 for (i
= 0; i
< font_height
; i
++) {
1138 c
= GetPixel(und_dc
, font_width
/ 2, i
);
1139 if (c
!= RGB(0, 0, 0))
1142 SelectObject(und_dc
, und_oldbm
);
1143 DeleteObject(und_bm
);
1146 und_mode
= UND_LINE
;
1147 DeleteObject(fonts
[FONT_UNDERLINE
]);
1148 fonts
[FONT_UNDERLINE
] = 0;
1152 if (bold_mode
== BOLD_FONT
) {
1153 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
1157 descent
= tm
.tmAscent
+ 1;
1158 if (descent
>= font_height
)
1159 descent
= font_height
- 1;
1161 for (i
= 0; i
< 3; i
++) {
1163 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
1164 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
1171 ReleaseDC(hwnd
, hdc
);
1173 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1174 und_mode
= UND_LINE
;
1175 DeleteObject(fonts
[FONT_UNDERLINE
]);
1176 fonts
[FONT_UNDERLINE
] = 0;
1179 if (bold_mode
== BOLD_FONT
&&
1180 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1181 bold_mode
= BOLD_SHADOW
;
1182 DeleteObject(fonts
[FONT_BOLD
]);
1183 fonts
[FONT_BOLD
] = 0;
1185 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1190 static void another_font(int fontno
)
1193 int fw_dontcare
, fw_bold
;
1197 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1200 basefont
= (fontno
& ~(FONT_BOLDUND
));
1201 if (basefont
!= fontno
&& !fontflag
[basefont
])
1202 another_font(basefont
);
1204 if (cfg
.fontisbold
) {
1205 fw_dontcare
= FW_BOLD
;
1208 fw_dontcare
= FW_DONTCARE
;
1212 c
= cfg
.fontcharset
;
1218 if (fontno
& FONT_WIDE
)
1220 if (fontno
& FONT_NARROW
)
1222 if (fontno
& FONT_OEM
)
1224 if (fontno
& FONT_BOLD
)
1226 if (fontno
& FONT_UNDERLINE
)
1230 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1231 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1232 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1233 FIXED_PITCH
| FF_DONTCARE
, s
);
1235 fontflag
[fontno
] = 1;
1238 static void deinit_fonts(void)
1241 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1243 DeleteObject(fonts
[i
]);
1249 void request_resize(int w
, int h
)
1253 /* If the window is maximized supress resizing attempts */
1254 if (IsZoomed(hwnd
)) {
1255 if (cfg
.resize_action
== RESIZE_TERM
)
1259 if (cfg
.resize_action
== RESIZE_DISABLED
) return;
1260 if (h
== term
->rows
&& w
== term
->cols
) return;
1262 /* Sanity checks ... */
1264 static int first_time
= 1;
1267 switch (first_time
) {
1269 /* Get the size of the screen */
1270 if (get_fullscreen_rect(&ss
))
1271 /* first_time = 0 */ ;
1277 /* Make sure the values are sane */
1278 width
= (ss
.right
- ss
.left
- extra_width
) / 4;
1279 height
= (ss
.bottom
- ss
.top
- extra_height
) / 6;
1281 if (w
> width
|| h
> height
)
1290 term_size(term
, h
, w
, cfg
.savelines
);
1292 if (cfg
.resize_action
!= RESIZE_FONT
&& !IsZoomed(hwnd
)) {
1293 width
= extra_width
+ font_width
* w
;
1294 height
= extra_height
+ font_height
* h
;
1296 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1297 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1298 SWP_NOMOVE
| SWP_NOZORDER
);
1302 InvalidateRect(hwnd
, NULL
, TRUE
);
1305 static void reset_window(int reinit
) {
1307 * This function decides how to resize or redraw when the
1308 * user changes something.
1310 * This function doesn't like to change the terminal size but if the
1311 * font size is locked that may be it's only soluion.
1313 int win_width
, win_height
;
1316 #ifdef RDB_DEBUG_PATCH
1317 debug((27, "reset_window()"));
1320 /* Current window sizes ... */
1321 GetWindowRect(hwnd
, &wr
);
1322 GetClientRect(hwnd
, &cr
);
1324 win_width
= cr
.right
- cr
.left
;
1325 win_height
= cr
.bottom
- cr
.top
;
1327 if (cfg
.resize_action
== RESIZE_DISABLED
) reinit
= 2;
1329 /* Are we being forced to reload the fonts ? */
1331 #ifdef RDB_DEBUG_PATCH
1332 debug((27, "reset_window() -- Forced deinit"));
1338 /* Oh, looks like we're minimised */
1339 if (win_width
== 0 || win_height
== 0)
1342 /* Is the window out of position ? */
1344 (offset_width
!= (win_width
-font_width
*term
->cols
)/2 ||
1345 offset_height
!= (win_height
-font_height
*term
->rows
)/2) ){
1346 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1347 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1348 InvalidateRect(hwnd
, NULL
, TRUE
);
1349 #ifdef RDB_DEBUG_PATCH
1350 debug((27, "reset_window() -> Reposition terminal"));
1354 if (IsZoomed(hwnd
)) {
1355 /* We're fullscreen, this means we must not change the size of
1356 * the window so it's the font size or the terminal itself.
1359 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1360 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1362 if (cfg
.resize_action
!= RESIZE_TERM
) {
1363 if ( font_width
!= win_width
/term
->cols
||
1364 font_height
!= win_height
/term
->rows
) {
1366 init_fonts(win_width
/term
->cols
, win_height
/term
->rows
);
1367 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1368 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1369 InvalidateRect(hwnd
, NULL
, TRUE
);
1370 #ifdef RDB_DEBUG_PATCH
1371 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1372 font_width
, font_height
));
1376 if ( font_width
!= win_width
/term
->cols
||
1377 font_height
!= win_height
/term
->rows
) {
1378 /* Our only choice at this point is to change the
1379 * size of the terminal; Oh well.
1381 term_size(term
, win_height
/font_height
, win_width
/font_width
,
1383 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1384 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1385 InvalidateRect(hwnd
, NULL
, TRUE
);
1386 #ifdef RDB_DEBUG_PATCH
1387 debug((27, "reset_window() -> Zoomed term_size"));
1394 /* Hmm, a force re-init means we should ignore the current window
1395 * so we resize to the default font size.
1398 #ifdef RDB_DEBUG_PATCH
1399 debug((27, "reset_window() -> Forced re-init"));
1402 offset_width
= offset_height
= cfg
.window_border
;
1403 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1404 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1406 if (win_width
!= font_width
*term
->cols
+ offset_width
*2 ||
1407 win_height
!= font_height
*term
->rows
+ offset_height
*2) {
1409 /* If this is too large windows will resize it to the maximum
1410 * allowed window size, we will then be back in here and resize
1411 * the font or terminal to fit.
1413 SetWindowPos(hwnd
, NULL
, 0, 0,
1414 font_width
*term
->cols
+ extra_width
,
1415 font_height
*term
->rows
+ extra_height
,
1416 SWP_NOMOVE
| SWP_NOZORDER
);
1419 InvalidateRect(hwnd
, NULL
, TRUE
);
1423 /* Okay the user doesn't want us to change the font so we try the
1424 * window. But that may be too big for the screen which forces us
1425 * to change the terminal.
1427 if ((cfg
.resize_action
== RESIZE_TERM
&& reinit
<=0) ||
1428 (cfg
.resize_action
== RESIZE_EITHER
&& reinit
<0) ||
1430 offset_width
= offset_height
= cfg
.window_border
;
1431 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1432 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1434 if (win_width
!= font_width
*term
->cols
+ offset_width
*2 ||
1435 win_height
!= font_height
*term
->rows
+ offset_height
*2) {
1440 get_fullscreen_rect(&ss
);
1442 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1443 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1446 if ( term
->rows
> height
|| term
->cols
> width
) {
1447 if (cfg
.resize_action
== RESIZE_EITHER
) {
1448 /* Make the font the biggest we can */
1449 if (term
->cols
> width
)
1450 font_width
= (ss
.right
- ss
.left
- extra_width
)
1452 if (term
->rows
> height
)
1453 font_height
= (ss
.bottom
- ss
.top
- extra_height
)
1457 init_fonts(font_width
, font_height
);
1459 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1460 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1462 if ( height
> term
->rows
) height
= term
->rows
;
1463 if ( width
> term
->cols
) width
= term
->cols
;
1464 term_size(term
, height
, width
, cfg
.savelines
);
1465 #ifdef RDB_DEBUG_PATCH
1466 debug((27, "reset_window() -> term resize to (%d,%d)",
1472 SetWindowPos(hwnd
, NULL
, 0, 0,
1473 font_width
*term
->cols
+ extra_width
,
1474 font_height
*term
->rows
+ extra_height
,
1475 SWP_NOMOVE
| SWP_NOZORDER
);
1477 InvalidateRect(hwnd
, NULL
, TRUE
);
1478 #ifdef RDB_DEBUG_PATCH
1479 debug((27, "reset_window() -> window resize to (%d,%d)",
1480 font_width
*term
->cols
+ extra_width
,
1481 font_height
*term
->rows
+ extra_height
));
1487 /* We're allowed to or must change the font but do we want to ? */
1489 if (font_width
!= (win_width
-cfg
.window_border
*2)/term
->cols
||
1490 font_height
!= (win_height
-cfg
.window_border
*2)/term
->rows
) {
1493 init_fonts((win_width
-cfg
.window_border
*2)/term
->cols
,
1494 (win_height
-cfg
.window_border
*2)/term
->rows
);
1495 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1496 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1498 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+offset_width
*2;
1499 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1501 InvalidateRect(hwnd
, NULL
, TRUE
);
1502 #ifdef RDB_DEBUG_PATCH
1503 debug((25, "reset_window() -> font resize to (%d,%d)",
1504 font_width
, font_height
));
1509 static void set_input_locale(HKL kl
)
1513 GetLocaleInfo(LOWORD(kl
), LOCALE_IDEFAULTANSICODEPAGE
,
1514 lbuf
, sizeof(lbuf
));
1516 kbd_codepage
= atoi(lbuf
);
1519 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
, int alt
)
1521 int thistime
= GetMessageTime();
1523 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1524 lastbtn
= MBT_NOTHING
;
1525 term_mouse(term
, b
, MA_CLICK
, x
, y
, shift
, ctrl
, alt
);
1529 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1530 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1531 lastact
== MA_2CLK ? MA_3CLK
:
1532 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1537 if (lastact
!= MA_NOTHING
)
1538 term_mouse(term
, b
, lastact
, x
, y
, shift
, ctrl
, alt
);
1539 lasttime
= thistime
;
1543 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1544 * into a cooked one (SELECT, EXTEND, PASTE).
1546 Mouse_Button
translate_button(Mouse_Button button
)
1548 if (button
== MBT_LEFT
)
1550 if (button
== MBT_MIDDLE
)
1551 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1552 if (button
== MBT_RIGHT
)
1553 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1554 return 0; /* shouldn't happen */
1557 static void show_mouseptr(int show
)
1559 static int cursor_visible
= 1;
1560 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1562 if (cursor_visible
&& !show
)
1564 else if (!cursor_visible
&& show
)
1566 cursor_visible
= show
;
1569 static int is_alt_pressed(void)
1572 int r
= GetKeyboardState(keystate
);
1575 if (keystate
[VK_MENU
] & 0x80)
1577 if (keystate
[VK_RMENU
] & 0x80)
1582 static int is_shift_pressed(void)
1585 int r
= GetKeyboardState(keystate
);
1588 if (keystate
[VK_SHIFT
] & 0x80)
1593 static int resizing
;
1595 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1596 WPARAM wParam
, LPARAM lParam
)
1599 static int ignore_clip
= FALSE
;
1600 static int need_backend_resize
= FALSE
;
1601 static int fullscr_on_max
= FALSE
;
1605 if (pending_netevent
)
1606 enact_pending_netevent();
1607 if (GetCapture() != hwnd
||
1608 (send_raw_mouse
&& !(cfg
.mouse_override
&& is_shift_pressed())))
1614 if (cfg
.ping_interval
> 0) {
1617 if (now
- last_movement
> cfg
.ping_interval
) {
1618 back
->special(backhandle
, TS_PING
);
1619 last_movement
= now
;
1622 net_pending_errors();
1628 if (!cfg
.warn_on_close
|| session_closed
||
1630 "Are you sure you want to close this session?",
1631 "PuTTY Exit Confirmation",
1632 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1633 DestroyWindow(hwnd
);
1640 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1652 PROCESS_INFORMATION pi
;
1653 HANDLE filemap
= NULL
;
1655 if (wParam
== IDM_DUPSESS
) {
1657 * Allocate a file-mapping memory chunk for the
1660 SECURITY_ATTRIBUTES sa
;
1663 sa
.nLength
= sizeof(sa
);
1664 sa
.lpSecurityDescriptor
= NULL
;
1665 sa
.bInheritHandle
= TRUE
;
1666 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1669 0, sizeof(Config
), NULL
);
1671 p
= (Config
*) MapViewOfFile(filemap
,
1673 0, 0, sizeof(Config
));
1675 *p
= cfg
; /* structure copy */
1679 sprintf(c
, "putty &%p", filemap
);
1681 } else if (wParam
== IDM_SAVEDSESS
) {
1682 if ((lParam
- IDM_SAVED_MIN
) / 16 < nsessions
) {
1684 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1685 cl
= smalloc(16 + strlen(session
));
1686 /* 8, but play safe */
1689 /* not a very important failure mode */
1691 sprintf(cl
, "putty @%s", session
);
1699 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1701 si
.lpReserved
= NULL
;
1702 si
.lpDesktop
= NULL
;
1706 si
.lpReserved2
= NULL
;
1707 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1708 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1711 CloseHandle(filemap
);
1721 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1724 if (!do_reconfig(hwnd
))
1728 /* Disable full-screen if resizing forbidden */
1729 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
1730 EnableMenuItem(m
, IDM_FULLSCREEN
, MF_BYCOMMAND
|
1731 (cfg
.resize_action
== RESIZE_DISABLED
)
1732 ? MF_GRAYED
: MF_ENABLED
);
1733 /* Gracefully unzoom if necessary */
1734 if (IsZoomed(hwnd
) &&
1735 (cfg
.resize_action
== RESIZE_DISABLED
)) {
1736 ShowWindow(hwnd
, SW_RESTORE
);
1740 if (strcmp(prev_cfg
.logfilename
, cfg
.logfilename
) ||
1741 prev_cfg
.logtype
!= cfg
.logtype
) {
1742 logfclose(); /* reset logging */
1748 * Flush the line discipline's edit buffer in the
1749 * case where local editing has just been disabled.
1751 ldisc_send(NULL
, 0, 0);
1759 /* Give terminal a heads-up on miscellaneous stuff */
1760 term_reconfig(term
);
1762 /* Screen size changed ? */
1763 if (cfg
.height
!= prev_cfg
.height
||
1764 cfg
.width
!= prev_cfg
.width
||
1765 cfg
.savelines
!= prev_cfg
.savelines
||
1766 cfg
.resize_action
== RESIZE_FONT
||
1767 (cfg
.resize_action
== RESIZE_EITHER
&& IsZoomed(hwnd
)) ||
1768 cfg
.resize_action
== RESIZE_DISABLED
)
1769 term_size(term
, cfg
.height
, cfg
.width
, cfg
.savelines
);
1771 /* Enable or disable the scroll bar, etc */
1773 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1774 LONG nexflag
, exflag
=
1775 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1778 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1779 if (cfg
.alwaysontop
) {
1780 nexflag
|= WS_EX_TOPMOST
;
1781 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1782 SWP_NOMOVE
| SWP_NOSIZE
);
1784 nexflag
&= ~(WS_EX_TOPMOST
);
1785 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1786 SWP_NOMOVE
| SWP_NOSIZE
);
1789 if (cfg
.sunken_edge
)
1790 nexflag
|= WS_EX_CLIENTEDGE
;
1792 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1795 if (is_full_screen() ?
1796 cfg
.scrollbar_in_fullscreen
: cfg
.scrollbar
)
1799 nflg
&= ~WS_VSCROLL
;
1801 if (cfg
.resize_action
== RESIZE_DISABLED
||
1803 nflg
&= ~WS_THICKFRAME
;
1805 nflg
|= WS_THICKFRAME
;
1807 if (cfg
.resize_action
== RESIZE_DISABLED
)
1808 nflg
&= ~WS_MAXIMIZEBOX
;
1810 nflg
|= WS_MAXIMIZEBOX
;
1812 if (nflg
!= flag
|| nexflag
!= exflag
) {
1814 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1815 if (nexflag
!= exflag
)
1816 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1818 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1819 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1820 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1828 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1833 set_title(cfg
.wintitle
);
1834 if (IsIconic(hwnd
)) {
1836 cfg
.win_name_always ? window_name
:
1840 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1841 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1842 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1843 cfg
.fontheight
!= prev_cfg
.fontheight
||
1844 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1845 cfg
.vtmode
!= prev_cfg
.vtmode
||
1846 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1847 cfg
.resize_action
== RESIZE_DISABLED
||
1848 cfg
.resize_action
== RESIZE_EITHER
||
1849 (cfg
.resize_action
!= prev_cfg
.resize_action
))
1852 InvalidateRect(hwnd
, NULL
, TRUE
);
1853 reset_window(init_lvl
);
1854 net_pending_errors();
1865 ldisc_send(NULL
, 0, 0);
1868 back
->special(backhandle
, TS_AYT
);
1869 net_pending_errors();
1872 back
->special(backhandle
, TS_BRK
);
1873 net_pending_errors();
1876 back
->special(backhandle
, TS_SYNCH
);
1877 net_pending_errors();
1880 back
->special(backhandle
, TS_EC
);
1881 net_pending_errors();
1884 back
->special(backhandle
, TS_EL
);
1885 net_pending_errors();
1888 back
->special(backhandle
, TS_GA
);
1889 net_pending_errors();
1892 back
->special(backhandle
, TS_NOP
);
1893 net_pending_errors();
1896 back
->special(backhandle
, TS_ABORT
);
1897 net_pending_errors();
1900 back
->special(backhandle
, TS_AO
);
1901 net_pending_errors();
1904 back
->special(backhandle
, TS_IP
);
1905 net_pending_errors();
1908 back
->special(backhandle
, TS_SUSP
);
1909 net_pending_errors();
1912 back
->special(backhandle
, TS_EOR
);
1913 net_pending_errors();
1916 back
->special(backhandle
, TS_EOF
);
1917 net_pending_errors();
1923 WinHelp(hwnd
, help_path
,
1924 help_has_contents ? HELP_FINDER
: HELP_CONTENTS
, 0);
1928 * We get this if the System menu has been activated
1935 * We get this if the System menu has been activated
1936 * using the keyboard. This might happen from within
1937 * TranslateKey, in which case it really wants to be
1938 * followed by a `space' character to actually _bring
1939 * the menu up_ rather than just sitting there in
1940 * `ready to appear' state.
1942 show_mouseptr(1); /* make sure pointer is visible */
1944 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1946 case IDM_FULLSCREEN
:
1950 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1951 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1956 #define X_POS(l) ((int)(short)LOWORD(l))
1957 #define Y_POS(l) ((int)(short)HIWORD(l))
1959 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1960 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1961 case WM_LBUTTONDOWN
:
1962 case WM_MBUTTONDOWN
:
1963 case WM_RBUTTONDOWN
:
1971 case WM_LBUTTONDOWN
:
1975 case WM_MBUTTONDOWN
:
1976 button
= MBT_MIDDLE
;
1979 case WM_RBUTTONDOWN
:
1988 button
= MBT_MIDDLE
;
1996 button
= press
= 0; /* shouldn't happen */
2000 * Special case: in full-screen mode, if the left
2001 * button is clicked in the very top left corner of the
2002 * window, we put up the System menu instead of doing
2005 if (is_full_screen() && press
&& button
== MBT_LEFT
&&
2006 X_POS(lParam
) == 0 && Y_POS(lParam
) == 0) {
2007 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_MOUSEMENU
, 0);
2012 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
2013 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
,
2017 term_mouse(term
, button
, MA_RELEASE
,
2018 TO_CHR_X(X_POS(lParam
)),
2019 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
2020 wParam
& MK_CONTROL
, is_alt_pressed());
2028 * Add the mouse position and message time to the random
2031 noise_ultralight(lParam
);
2033 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
) &&
2034 GetCapture() == hwnd
) {
2036 if (wParam
& MK_LBUTTON
)
2038 else if (wParam
& MK_MBUTTON
)
2042 term_mouse(term
, b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
2043 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
2044 wParam
& MK_CONTROL
, is_alt_pressed());
2047 case WM_NCMOUSEMOVE
:
2049 noise_ultralight(lParam
);
2051 case WM_IGNORE_CLIP
:
2052 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
2054 case WM_DESTROYCLIPBOARD
:
2056 term_deselect(term
);
2057 ignore_clip
= FALSE
;
2063 hdc
= BeginPaint(hwnd
, &p
);
2065 SelectPalette(hdc
, pal
, TRUE
);
2066 RealizePalette(hdc
);
2068 term_paint(term
, hdc
,
2069 (p
.rcPaint
.left
-offset_width
)/font_width
,
2070 (p
.rcPaint
.top
-offset_height
)/font_height
,
2071 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
2072 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
2075 p
.rcPaint
.left
< offset_width
||
2076 p
.rcPaint
.top
< offset_height
||
2077 p
.rcPaint
.right
>= offset_width
+ font_width
*term
->cols
||
2078 p
.rcPaint
.bottom
>= offset_height
+ font_height
*term
->rows
)
2080 HBRUSH fillcolour
, oldbrush
;
2082 fillcolour
= CreateSolidBrush (
2083 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
2084 oldbrush
= SelectObject(hdc
, fillcolour
);
2085 edge
= CreatePen(PS_SOLID
, 0,
2086 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
2087 oldpen
= SelectObject(hdc
, edge
);
2090 * Jordan Russell reports that this apparently
2091 * ineffectual IntersectClipRect() call masks a
2092 * Windows NT/2K bug causing strange display
2093 * problems when the PuTTY window is taller than
2094 * the primary monitor. It seems harmless enough...
2096 IntersectClipRect(hdc
,
2097 p
.rcPaint
.left
, p
.rcPaint
.top
,
2098 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
2100 ExcludeClipRect(hdc
,
2101 offset_width
, offset_height
,
2102 offset_width
+font_width
*term
->cols
,
2103 offset_height
+font_height
*term
->rows
);
2105 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
2106 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
2108 // SelectClipRgn(hdc, NULL);
2110 SelectObject(hdc
, oldbrush
);
2111 DeleteObject(fillcolour
);
2112 SelectObject(hdc
, oldpen
);
2115 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
2116 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
2122 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2123 * but the only one that's likely to try to overload us is FD_READ.
2124 * This means buffering just one is fine.
2126 if (pending_netevent
)
2127 enact_pending_netevent();
2129 pending_netevent
= TRUE
;
2130 pend_netevent_wParam
= wParam
;
2131 pend_netevent_lParam
= lParam
;
2132 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
2133 enact_pending_netevent();
2135 time(&last_movement
);
2138 term
->has_focus
= TRUE
;
2139 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
2141 flash_window(0); /* stop */
2148 term
->has_focus
= FALSE
;
2150 caret_x
= caret_y
= -1; /* ensure caret is replaced next time */
2154 case WM_ENTERSIZEMOVE
:
2155 #ifdef RDB_DEBUG_PATCH
2156 debug((27, "WM_ENTERSIZEMOVE"));
2160 need_backend_resize
= FALSE
;
2162 case WM_EXITSIZEMOVE
:
2165 #ifdef RDB_DEBUG_PATCH
2166 debug((27, "WM_EXITSIZEMOVE"));
2168 if (need_backend_resize
) {
2169 term_size(term
, cfg
.height
, cfg
.width
, cfg
.savelines
);
2170 InvalidateRect(hwnd
, NULL
, TRUE
);
2175 * This does two jobs:
2176 * 1) Keep the sizetip uptodate
2177 * 2) Make sure the window size is _stepped_ in units of the font size.
2179 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2180 int width
, height
, w
, h
, ew
, eh
;
2181 LPRECT r
= (LPRECT
) lParam
;
2183 if ( !need_backend_resize
&& cfg
.resize_action
== RESIZE_EITHER
&&
2184 (cfg
.height
!= term
->rows
|| cfg
.width
!= term
->cols
)) {
2186 * Great! It seems that both the terminal size and the
2187 * font size have been changed and the user is now dragging.
2189 * It will now be difficult to get back to the configured
2192 * This would be easier but it seems to be too confusing.
2194 term_size(term, cfg.height, cfg.width, cfg.savelines);
2197 cfg
.height
=term
->rows
; cfg
.width
=term
->cols
;
2199 InvalidateRect(hwnd
, NULL
, TRUE
);
2200 need_backend_resize
= TRUE
;
2203 width
= r
->right
- r
->left
- extra_width
;
2204 height
= r
->bottom
- r
->top
- extra_height
;
2205 w
= (width
+ font_width
/ 2) / font_width
;
2208 h
= (height
+ font_height
/ 2) / font_height
;
2211 UpdateSizeTip(hwnd
, w
, h
);
2212 ew
= width
- w
* font_width
;
2213 eh
= height
- h
* font_height
;
2215 if (wParam
== WMSZ_LEFT
||
2216 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2222 if (wParam
== WMSZ_TOP
||
2223 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2233 int width
, height
, w
, h
, rv
= 0;
2234 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
2235 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2236 LPRECT r
= (LPRECT
) lParam
;
2238 width
= r
->right
- r
->left
- ex_width
;
2239 height
= r
->bottom
- r
->top
- ex_height
;
2241 w
= (width
+ term
->cols
/2)/term
->cols
;
2242 h
= (height
+ term
->rows
/2)/term
->rows
;
2243 if ( r
->right
!= r
->left
+ w
*term
->cols
+ ex_width
)
2246 if (wParam
== WMSZ_LEFT
||
2247 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2248 r
->left
= r
->right
- w
*term
->cols
- ex_width
;
2250 r
->right
= r
->left
+ w
*term
->cols
+ ex_width
;
2252 if (r
->bottom
!= r
->top
+ h
*term
->rows
+ ex_height
)
2255 if (wParam
== WMSZ_TOP
||
2256 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2257 r
->top
= r
->bottom
- h
*term
->rows
- ex_height
;
2259 r
->bottom
= r
->top
+ h
*term
->rows
+ ex_height
;
2263 /* break; (never reached) */
2264 case WM_FULLSCR_ON_MAX
:
2265 fullscr_on_max
= TRUE
;
2268 sys_cursor_update();
2271 #ifdef RDB_DEBUG_PATCH
2272 debug((27, "WM_SIZE %s (%d,%d)",
2273 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2274 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2275 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2276 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2278 LOWORD(lParam
), HIWORD(lParam
)));
2280 if (wParam
== SIZE_MINIMIZED
)
2282 cfg
.win_name_always ? window_name
: icon_name
);
2283 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2284 SetWindowText(hwnd
, window_name
);
2285 if (wParam
== SIZE_RESTORED
)
2286 clear_full_screen();
2287 if (wParam
== SIZE_MAXIMIZED
&& fullscr_on_max
) {
2288 fullscr_on_max
= FALSE
;
2292 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2293 /* A resize, well it better be a minimize. */
2297 int width
, height
, w
, h
;
2299 width
= LOWORD(lParam
);
2300 height
= HIWORD(lParam
);
2303 if (wParam
== SIZE_MAXIMIZED
&& !was_zoomed
) {
2305 prev_rows
= term
->rows
;
2306 prev_cols
= term
->cols
;
2307 if (cfg
.resize_action
== RESIZE_TERM
) {
2308 w
= width
/ font_width
;
2310 h
= height
/ font_height
;
2313 term_size(term
, h
, w
, cfg
.savelines
);
2316 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2318 if (cfg
.resize_action
== RESIZE_TERM
)
2319 term_size(term
, prev_rows
, prev_cols
, cfg
.savelines
);
2320 if (cfg
.resize_action
!= RESIZE_FONT
)
2325 /* This is an unexpected resize, these will normally happen
2326 * if the window is too large. Probably either the user
2327 * selected a huge font or the screen size has changed.
2329 * This is also called with minimize.
2331 else reset_window(-1);
2335 * Don't call back->size in mid-resize. (To prevent
2336 * massive numbers of resize events getting sent
2337 * down the connection during an NT opaque drag.)
2340 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2341 need_backend_resize
= TRUE
;
2342 w
= (width
-cfg
.window_border
*2) / font_width
;
2344 h
= (height
-cfg
.window_border
*2) / font_height
;
2353 sys_cursor_update();
2356 switch (LOWORD(wParam
)) {
2358 term_scroll(term
, -1, 0);
2361 term_scroll(term
, +1, 0);
2364 term_scroll(term
, 0, +1);
2367 term_scroll(term
, 0, -1);
2370 term_scroll(term
, 0, +term
->rows
/ 2);
2373 term_scroll(term
, 0, -term
->rows
/ 2);
2375 case SB_THUMBPOSITION
:
2377 term_scroll(term
, 1, HIWORD(wParam
));
2381 case WM_PALETTECHANGED
:
2382 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2383 HDC hdc
= get_ctx();
2385 if (RealizePalette(hdc
) > 0)
2391 case WM_QUERYNEWPALETTE
:
2393 HDC hdc
= get_ctx();
2395 if (RealizePalette(hdc
) > 0)
2407 * Add the scan code and keypress timing to the random
2410 noise_ultralight(lParam
);
2413 * We don't do TranslateMessage since it disassociates the
2414 * resulting CHAR message from the KEYDOWN that sparked it,
2415 * which we occasionally don't want. Instead, we process
2416 * KEYDOWN, and call the Win32 translator functions so that
2417 * we get the translations under _our_ control.
2420 unsigned char buf
[20];
2423 if (wParam
== VK_PROCESSKEY
) {
2426 m
.message
= WM_KEYDOWN
;
2428 m
.lParam
= lParam
& 0xdfff;
2429 TranslateMessage(&m
);
2431 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2433 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2437 * Interrupt an ongoing paste. I'm not sure
2438 * this is sensible, but for the moment it's
2439 * preferable to having to faff about buffering
2445 * We need not bother about stdin backlogs
2446 * here, because in GUI PuTTY we can't do
2447 * anything about it anyway; there's no means
2448 * of asking Windows to hold off on KEYDOWN
2449 * messages. We _have_ to buffer everything
2452 term_seen_key_event(term
);
2453 ldisc_send(buf
, len
, 1);
2458 net_pending_errors();
2460 case WM_INPUTLANGCHANGE
:
2461 /* wParam == Font number */
2462 /* lParam == Locale */
2463 set_input_locale((HKL
)lParam
);
2464 sys_cursor_update();
2467 if(wParam
== IMN_SETOPENSTATUS
) {
2468 HIMC hImc
= ImmGetContext(hwnd
);
2469 ImmSetCompositionFont(hImc
, &lfont
);
2470 ImmReleaseContext(hwnd
, hImc
);
2474 case WM_IME_COMPOSITION
:
2480 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2481 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2483 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2484 break; /* fall back to DefWindowProc */
2486 hIMC
= ImmGetContext(hwnd
);
2487 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2491 buff
= (char*) smalloc(n
);
2492 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2494 * Jaeyoun Chung reports that Korean character
2495 * input doesn't work correctly if we do a single
2496 * luni_send() covering the whole of buff. So
2497 * instead we luni_send the characters one by one.
2499 term_seen_key_event(term
);
2500 for (i
= 0; i
< n
; i
+= 2) {
2501 luni_send((unsigned short *)(buff
+i
), 1, 1);
2505 ImmReleaseContext(hwnd
, hIMC
);
2510 if (wParam
& 0xFF00) {
2511 unsigned char buf
[2];
2514 buf
[0] = wParam
>> 8;
2515 term_seen_key_event(term
);
2516 lpage_send(kbd_codepage
, buf
, 2, 1);
2518 char c
= (unsigned char) wParam
;
2519 term_seen_key_event(term
);
2520 lpage_send(kbd_codepage
, &c
, 1, 1);
2526 * Nevertheless, we are prepared to deal with WM_CHAR
2527 * messages, should they crop up. So if someone wants to
2528 * post the things to us as part of a macro manoeuvre,
2529 * we're ready to cope.
2532 char c
= (unsigned char)wParam
;
2533 term_seen_key_event(term
);
2534 lpage_send(CP_ACP
, &c
, 1, 1);
2538 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2539 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2543 if (message
== wm_mousewheel
|| message
== WM_MOUSEWHEEL
) {
2544 int shift_pressed
=0, control_pressed
=0, alt_pressed
=0;
2546 if (message
== WM_MOUSEWHEEL
) {
2547 wheel_accumulator
+= (short)HIWORD(wParam
);
2548 shift_pressed
=LOWORD(wParam
) & MK_SHIFT
;
2549 control_pressed
=LOWORD(wParam
) & MK_CONTROL
;
2552 wheel_accumulator
+= (int)wParam
;
2553 if (GetKeyboardState(keys
)!=0) {
2554 shift_pressed
=keys
[VK_SHIFT
]&0x80;
2555 control_pressed
=keys
[VK_CONTROL
]&0x80;
2559 /* process events when the threshold is reached */
2560 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
2563 /* reduce amount for next time */
2564 if (wheel_accumulator
> 0) {
2566 wheel_accumulator
-= WHEEL_DELTA
;
2567 } else if (wheel_accumulator
< 0) {
2569 wheel_accumulator
+= WHEEL_DELTA
;
2573 if (send_raw_mouse
&&
2574 !(cfg
.mouse_override
&& shift_pressed
)) {
2575 /* send a mouse-down followed by a mouse up */
2578 TO_CHR_X(X_POS(lParam
)),
2579 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2580 control_pressed
, is_alt_pressed());
2581 term_mouse(term
, b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
2582 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2583 control_pressed
, is_alt_pressed());
2585 /* trigger a scroll */
2586 term_scroll(term
, 0,
2588 -term
->rows
/ 2 : term
->rows
/ 2);
2595 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2599 * Move the system caret. (We maintain one, even though it's
2600 * invisible, for the benefit of blind people: apparently some
2601 * helper software tracks the system caret, so we should arrange to
2604 void sys_cursor(int x
, int y
)
2608 if (!term
->has_focus
) return;
2611 * Avoid gratuitously re-updating the cursor position and IMM
2612 * window if there's no actual change required.
2614 cx
= x
* font_width
+ offset_width
;
2615 cy
= y
* font_height
+ offset_height
;
2616 if (cx
== caret_x
&& cy
== caret_y
)
2621 sys_cursor_update();
2624 static void sys_cursor_update(void)
2629 if (!term
->has_focus
) return;
2631 if (caret_x
< 0 || caret_y
< 0)
2634 SetCaretPos(caret_x
, caret_y
);
2636 /* IMM calls on Win98 and beyond only */
2637 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2639 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2640 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2642 /* we should have the IMM functions */
2643 hIMC
= ImmGetContext(hwnd
);
2644 cf
.dwStyle
= CFS_POINT
;
2645 cf
.ptCurrentPos
.x
= caret_x
;
2646 cf
.ptCurrentPos
.y
= caret_y
;
2647 ImmSetCompositionWindow(hIMC
, &cf
);
2649 ImmReleaseContext(hwnd
, hIMC
);
2653 * Draw a line of text in the window, at given character
2654 * coordinates, in given attributes.
2656 * We are allowed to fiddle with the contents of `text'.
2658 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2659 unsigned long attr
, int lattr
)
2662 int nfg
, nbg
, nfont
;
2665 int force_manual_underline
= 0;
2666 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2667 int char_width
= fnt_width
;
2668 int text_adjust
= 0;
2669 static int *IpDx
= 0, IpDxLEN
= 0;
2671 if (attr
& ATTR_WIDE
)
2674 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2676 if (len
> IpDxLEN
) {
2678 IpDx
= smalloc((len
+ 16) * sizeof(int));
2679 IpDxLEN
= (len
+ 16);
2681 for (i
= 0; i
< IpDxLEN
; i
++)
2682 IpDx
[i
] = char_width
;
2685 /* Only want the left half of double width lines */
2686 if (lattr
!= LATTR_NORM
&& x
*2 >= term
->cols
)
2694 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || term
->big_cursor
)) {
2695 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2696 attr
^= ATTR_CUR_XOR
;
2700 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2701 /* Assume a poorman font is borken in other ways too. */
2711 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2714 if (attr
& ATTR_NARROW
)
2715 nfont
|= FONT_NARROW
;
2717 /* Special hack for the VT100 linedraw glyphs. */
2718 if ((attr
& CSET_MASK
) == 0x2300) {
2719 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2720 switch ((unsigned char) (text
[0])) {
2722 text_adjust
= -2 * font_height
/ 5;
2725 text_adjust
= -1 * font_height
/ 5;
2728 text_adjust
= font_height
/ 5;
2731 text_adjust
= 2 * font_height
/ 5;
2734 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2737 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2738 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2739 if (attr
& ATTR_UNDER
) {
2740 attr
&= ~ATTR_UNDER
;
2741 force_manual_underline
= 1;
2746 /* Anything left as an original character set is unprintable. */
2747 if (DIRECT_CHAR(attr
)) {
2750 memset(text
, 0xFD, len
);
2754 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2757 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2758 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2759 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2761 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2762 nfont
|= FONT_UNDERLINE
;
2763 another_font(nfont
);
2764 if (!fonts
[nfont
]) {
2765 if (nfont
& FONT_UNDERLINE
)
2766 force_manual_underline
= 1;
2767 /* Don't do the same for manual bold, it could be bad news. */
2769 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2771 another_font(nfont
);
2773 nfont
= FONT_NORMAL
;
2774 if (attr
& ATTR_REVERSE
) {
2779 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2781 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2785 SelectObject(hdc
, fonts
[nfont
]);
2786 SetTextColor(hdc
, fg
);
2787 SetBkColor(hdc
, bg
);
2788 SetBkMode(hdc
, OPAQUE
);
2791 line_box
.right
= x
+ char_width
* len
;
2792 line_box
.bottom
= y
+ font_height
;
2794 /* Only want the left half of double width lines */
2795 if (line_box
.right
> font_width
*term
->cols
+offset_width
)
2796 line_box
.right
= font_width
*term
->cols
+offset_width
;
2798 /* We're using a private area for direct to font. (512 chars.) */
2799 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2800 /* Ho Hum, dbcs fonts are a PITA! */
2801 /* To display on W9x I have to convert to UCS */
2802 static wchar_t *uni_buf
= 0;
2803 static int uni_len
= 0;
2805 if (len
> uni_len
) {
2807 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2810 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2811 uni_buf
[nlen
] = 0xFFFD;
2812 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2813 IpDx
[nlen
] += char_width
;
2814 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2815 text
+mptr
, 2, uni_buf
+nlen
, 1);
2820 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2821 text
+mptr
, 1, uni_buf
+nlen
, 1);
2829 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2830 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2831 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2832 SetBkMode(hdc
, TRANSPARENT
);
2833 ExtTextOutW(hdc
, x
- 1,
2834 y
- font_height
* (lattr
==
2835 LATTR_BOT
) + text_adjust
,
2836 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2840 } else if (DIRECT_FONT(attr
)) {
2842 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2843 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2844 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2845 SetBkMode(hdc
, TRANSPARENT
);
2847 /* GRR: This draws the character outside it's box and can leave
2848 * 'droppings' even with the clip box! I suppose I could loop it
2849 * one character at a time ... yuk.
2851 * Or ... I could do a test print with "W", and use +1 or -1 for this
2852 * shift depending on if the leftmost column is blank...
2854 ExtTextOut(hdc
, x
- 1,
2855 y
- font_height
* (lattr
==
2856 LATTR_BOT
) + text_adjust
,
2857 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2860 /* And 'normal' unicode characters */
2861 static WCHAR
*wbuf
= NULL
;
2862 static int wlen
= 0;
2867 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2869 for (i
= 0; i
< len
; i
++)
2870 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2873 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2874 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2876 /* And the shadow bold hack. */
2877 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2878 SetBkMode(hdc
, TRANSPARENT
);
2879 ExtTextOutW(hdc
, x
- 1,
2880 y
- font_height
* (lattr
==
2881 LATTR_BOT
) + text_adjust
,
2882 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2885 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2886 (und_mode
== UND_LINE
2887 && (attr
& ATTR_UNDER
)))) {
2890 if (lattr
== LATTR_BOT
)
2891 dec
= dec
* 2 - font_height
;
2893 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2894 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2895 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2896 oldpen
= SelectObject(hdc
, oldpen
);
2897 DeleteObject(oldpen
);
2901 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2902 unsigned long attr
, int lattr
)
2908 int ctype
= cfg
.cursor_type
;
2910 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || term
->big_cursor
)) {
2911 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2912 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2916 attr
|= TATTR_RIGHTCURS
;
2919 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2920 if (attr
& ATTR_WIDE
)
2927 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || term
->big_cursor
)) {
2930 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2931 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2932 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2933 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2934 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2935 Polyline(hdc
, pts
, 5);
2936 oldpen
= SelectObject(hdc
, oldpen
);
2937 DeleteObject(oldpen
);
2938 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2939 int startx
, starty
, dx
, dy
, length
, i
;
2942 starty
= y
+ descent
;
2945 length
= char_width
;
2948 if (attr
& TATTR_RIGHTCURS
)
2949 xadjust
= char_width
- 1;
2950 startx
= x
+ xadjust
;
2954 length
= font_height
;
2956 if (attr
& TATTR_ACTCURS
) {
2959 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2960 MoveToEx(hdc
, startx
, starty
, NULL
);
2961 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2962 oldpen
= SelectObject(hdc
, oldpen
);
2963 DeleteObject(oldpen
);
2965 for (i
= 0; i
< length
; i
++) {
2967 SetPixel(hdc
, startx
, starty
, colours
[23]);
2976 /* This function gets the actual width of a character in the normal font.
2978 int CharWidth(Context ctx
, int uc
) {
2982 /* If the font max is the same as the font ave width then this
2983 * function is a no-op.
2985 if (!font_dualwidth
) return 1;
2987 switch (uc
& CSET_MASK
) {
2989 uc
= unitab_line
[uc
& 0xFF];
2992 uc
= unitab_xterm
[uc
& 0xFF];
2995 uc
= unitab_scoacs
[uc
& 0xFF];
2998 if (DIRECT_FONT(uc
)) {
2999 if (dbcs_screenfont
) return 1;
3001 /* Speedup, I know of no font where ascii is the wrong width */
3002 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
3005 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
3006 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
3007 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
3008 another_font(FONT_OEM
);
3009 if (!fonts
[FONT_OEM
]) return 0;
3011 SelectObject(hdc
, fonts
[FONT_OEM
]);
3015 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
3016 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
3019 /* Speedup, I know of no font where ascii is the wrong width */
3020 if (uc
>= ' ' && uc
<= '~') return 1;
3022 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
3023 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
3024 /* Okay that one worked */ ;
3025 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
3026 /* This should work on 9x too, but it's "less accurate" */ ;
3031 ibuf
+= font_width
/ 2 -1;
3038 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
3039 * codes. Returns number of bytes used or zero to drop the message
3040 * or -1 to forward the message to windows.
3042 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
3043 unsigned char *output
)
3046 int scan
, left_alt
= 0, key_down
, shift_state
;
3048 unsigned char *p
= output
;
3049 static int alt_sum
= 0;
3051 HKL kbd_layout
= GetKeyboardLayout(0);
3053 static WORD keys
[3];
3054 static int compose_char
= 0;
3055 static WPARAM compose_key
= 0;
3057 r
= GetKeyboardState(keystate
);
3059 memset(keystate
, 0, sizeof(keystate
));
3062 #define SHOW_TOASCII_RESULT
3063 { /* Tell us all about key events */
3064 static BYTE oldstate
[256];
3065 static int first
= 1;
3069 memcpy(oldstate
, keystate
, sizeof(oldstate
));
3072 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
3074 } else if ((HIWORD(lParam
) & KF_UP
)
3075 && scan
== (HIWORD(lParam
) & 0xFF)) {
3079 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
3080 debug(("K_F%d", wParam
+ 1 - VK_F1
));
3093 debug(("VK_%02x", wParam
));
3095 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
3097 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
3099 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
3100 if (ch
>= ' ' && ch
<= '~')
3101 debug((", '%c'", ch
));
3103 debug((", $%02x", ch
));
3106 debug((", KB0=%02x", keys
[0]));
3108 debug((", KB1=%02x", keys
[1]));
3110 debug((", KB2=%02x", keys
[2]));
3112 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
3114 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
3116 if ((HIWORD(lParam
) & KF_EXTENDED
))
3118 if ((HIWORD(lParam
) & KF_UP
))
3122 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
3123 else if ((HIWORD(lParam
) & KF_UP
))
3124 oldstate
[wParam
& 0xFF] ^= 0x80;
3126 oldstate
[wParam
& 0xFF] ^= 0x81;
3128 for (ch
= 0; ch
< 256; ch
++)
3129 if (oldstate
[ch
] != keystate
[ch
])
3130 debug((", M%02x=%02x", ch
, keystate
[ch
]));
3132 memcpy(oldstate
, keystate
, sizeof(oldstate
));
3136 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
3137 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
3141 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
3142 if ((cfg
.funky_type
== 3 ||
3143 (cfg
.funky_type
<= 1 && term
->app_keypad_keys
&&
3145 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
3147 wParam
= VK_EXECUTE
;
3149 /* UnToggle NUMLock */
3150 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
3151 keystate
[VK_NUMLOCK
] ^= 1;
3154 /* And write back the 'adjusted' state */
3155 SetKeyboardState(keystate
);
3158 /* Disable Auto repeat if required */
3159 if (term
->repeat_off
&&
3160 (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
3163 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
3166 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
3168 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
3169 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
3170 if (cfg
.ctrlaltkeys
)
3171 keystate
[VK_MENU
] = 0;
3173 keystate
[VK_RMENU
] = 0x80;
3178 alt_pressed
= (left_alt
&& key_down
);
3180 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
3181 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
3182 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
3184 /* Note if AltGr was pressed and if it was used as a compose key */
3185 if (!compose_state
) {
3186 compose_key
= 0x100;
3187 if (cfg
.compose_key
) {
3188 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
3189 compose_key
= wParam
;
3191 if (wParam
== VK_APPS
)
3192 compose_key
= wParam
;
3195 if (wParam
== compose_key
) {
3196 if (compose_state
== 0
3197 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
3199 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
3203 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
3206 if (compose_state
> 1 && left_alt
)
3209 /* Sanitize the number pad if not using a PC NumPad */
3210 if (left_alt
|| (term
->app_keypad_keys
&& !cfg
.no_applic_k
3211 && cfg
.funky_type
!= 2)
3212 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
3213 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
3217 nParam
= VK_NUMPAD0
;
3220 nParam
= VK_NUMPAD1
;
3223 nParam
= VK_NUMPAD2
;
3226 nParam
= VK_NUMPAD3
;
3229 nParam
= VK_NUMPAD4
;
3232 nParam
= VK_NUMPAD5
;
3235 nParam
= VK_NUMPAD6
;
3238 nParam
= VK_NUMPAD7
;
3241 nParam
= VK_NUMPAD8
;
3244 nParam
= VK_NUMPAD9
;
3247 nParam
= VK_DECIMAL
;
3251 if (keystate
[VK_NUMLOCK
] & 1)
3258 /* If a key is pressed and AltGr is not active */
3259 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
3260 /* Okay, prepare for most alts then ... */
3264 /* Lets see if it's a pattern we know all about ... */
3265 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
3266 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
3269 if (wParam
== VK_NEXT
&& shift_state
== 1) {
3270 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
3273 if (wParam
== VK_INSERT
&& shift_state
== 1) {
3274 term_do_paste(term
);
3277 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
3280 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
3281 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
3284 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
&&
3285 (cfg
.resize_action
!= RESIZE_DISABLED
)) {
3286 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) != KF_REPEAT
)
3290 /* Control-Numlock for app-keypad mode switch */
3291 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
3292 term
->app_keypad_keys
^= 1;
3296 /* Nethack keypad */
3297 if (cfg
.nethack_keypad
&& !left_alt
) {
3300 *p
++ = shift_state ?
'B' : 'b';
3303 *p
++ = shift_state ?
'J' : 'j';
3306 *p
++ = shift_state ?
'N' : 'n';
3309 *p
++ = shift_state ?
'H' : 'h';
3312 *p
++ = shift_state ?
'.' : '.';
3315 *p
++ = shift_state ?
'L' : 'l';
3318 *p
++ = shift_state ?
'Y' : 'y';
3321 *p
++ = shift_state ?
'K' : 'k';
3324 *p
++ = shift_state ?
'U' : 'u';
3329 /* Application Keypad */
3333 if (cfg
.funky_type
== 3 ||
3334 (cfg
.funky_type
<= 1 &&
3335 term
->app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3349 if (term
->app_keypad_keys
&& !cfg
.no_applic_k
)
3386 if (cfg
.funky_type
== 2) {
3391 } else if (shift_state
)
3398 if (cfg
.funky_type
== 2)
3402 if (cfg
.funky_type
== 2)
3406 if (cfg
.funky_type
== 2)
3411 if (HIWORD(lParam
) & KF_EXTENDED
)
3416 if (term
->vt52_mode
) {
3417 if (xkey
>= 'P' && xkey
<= 'S')
3418 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3420 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3422 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3427 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3428 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3432 if (wParam
== VK_BACK
&& shift_state
== 1) { /* Shift Backspace */
3433 /* We do the opposite of what is configured */
3434 *p
++ = (cfg
.bksp_is_delete ?
0x08 : 0x7F);
3438 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3444 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3448 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3452 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3457 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3462 /* Control-2 to Control-8 are special */
3463 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3464 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3467 if (shift_state
== 2 && wParam
== 0xBD) {
3471 if (shift_state
== 2 && wParam
== 0xDF) {
3475 if (shift_state
== 0 && wParam
== VK_RETURN
&& term
->cr_lf_return
) {
3482 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3483 * for integer decimal nn.)
3485 * We also deal with the weird ones here. Linux VCs replace F1
3486 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3487 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3493 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3496 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3499 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3502 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3505 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3508 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3511 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3514 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3517 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3520 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3553 if ((shift_state
&2) == 0) switch (wParam
) {
3573 /* Reorder edit keys to physical order */
3574 if (cfg
.funky_type
== 3 && code
<= 6)
3575 code
= "\0\2\1\4\5\3\6"[code
];
3577 if (term
->vt52_mode
&& code
> 0 && code
<= 6) {
3578 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3582 if (cfg
.funky_type
== 5 && /* SCO function keys */
3583 code
>= 11 && code
<= 34) {
3584 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3587 case VK_F1
: index
= 0; break;
3588 case VK_F2
: index
= 1; break;
3589 case VK_F3
: index
= 2; break;
3590 case VK_F4
: index
= 3; break;
3591 case VK_F5
: index
= 4; break;
3592 case VK_F6
: index
= 5; break;
3593 case VK_F7
: index
= 6; break;
3594 case VK_F8
: index
= 7; break;
3595 case VK_F9
: index
= 8; break;
3596 case VK_F10
: index
= 9; break;
3597 case VK_F11
: index
= 10; break;
3598 case VK_F12
: index
= 11; break;
3600 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3601 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3602 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3605 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3606 code
>= 1 && code
<= 6) {
3607 char codes
[] = "HL.FIG";
3611 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3615 if ((term
->vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3621 if (term
->vt52_mode
)
3622 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3625 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3628 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3629 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3632 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3633 if (term
->vt52_mode
)
3634 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3636 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3639 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3640 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3644 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3649 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3650 * some reason seems to send VK_CLEAR to Windows...).
3672 if (term
->vt52_mode
)
3673 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3675 int app_flg
= (term
->app_cursor_keys
&& !cfg
.no_applic_c
);
3678 * RDB: VT100 & VT102 manuals both state the
3679 * app cursor keys only work if the app keypad
3682 * SGT: That may well be true, but xterm
3683 * disagrees and so does at least one
3684 * application, so I've #if'ed this out and the
3685 * behaviour is back to PuTTY's original: app
3686 * cursor and app keypad are independently
3687 * switchable modes. If anyone complains about
3688 * _this_ I'll have to put in a configurable
3691 if (!term
->app_keypad_keys
)
3694 /* Useful mapping of Ctrl-arrows */
3695 if (shift_state
== 2)
3699 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3701 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3708 * Finally, deal with Return ourselves. (Win95 seems to
3709 * foul it up when Alt is pressed, for some reason.)
3711 if (wParam
== VK_RETURN
) { /* Return */
3717 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3718 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3723 /* Okay we've done everything interesting; let windows deal with
3724 * the boring stuff */
3728 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3729 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3731 keystate
[VK_CAPITAL
] = 0;
3734 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3735 #ifdef SHOW_TOASCII_RESULT
3736 if (r
== 1 && !key_down
) {
3738 if (in_utf(term
) || dbcs_screenfont
)
3739 debug((", (U+%04x)", alt_sum
));
3741 debug((", LCH(%d)", alt_sum
));
3743 debug((", ACH(%d)", keys
[0]));
3748 for (r1
= 0; r1
< r
; r1
++) {
3749 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3758 * Interrupt an ongoing paste. I'm not sure this is
3759 * sensible, but for the moment it's preferable to
3760 * having to faff about buffering things.
3765 for (i
= 0; i
< r
; i
++) {
3766 unsigned char ch
= (unsigned char) keys
[i
];
3768 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3773 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3777 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3778 MessageBeep(MB_ICONHAND
);
3782 term_seen_key_event(term
);
3783 luni_send(&keybuf
, 1, 1);
3791 if (in_utf(term
) || dbcs_screenfont
) {
3793 term_seen_key_event(term
);
3794 luni_send(&keybuf
, 1, 1);
3796 ch
= (char) alt_sum
;
3798 * We need not bother about stdin
3799 * backlogs here, because in GUI PuTTY
3800 * we can't do anything about it
3801 * anyway; there's no means of asking
3802 * Windows to hold off on KEYDOWN
3803 * messages. We _have_ to buffer
3804 * everything we're sent.
3806 term_seen_key_event(term
);
3807 ldisc_send(&ch
, 1, 1);
3811 term_seen_key_event(term
);
3812 lpage_send(kbd_codepage
, &ch
, 1, 1);
3814 if(capsOn
&& ch
< 0x80) {
3817 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3818 term_seen_key_event(term
);
3819 luni_send(cbuf
+!left_alt
, 1+!!left_alt
, 1);
3824 term_seen_key_event(term
);
3825 lpage_send(kbd_codepage
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3831 /* This is so the ALT-Numpad and dead keys work correctly. */
3836 /* If we're definitly not building up an ALT-54321 then clear it */
3839 /* If we will be using alt_sum fix the 256s */
3840 else if (keys
[0] && (in_utf(term
) || dbcs_screenfont
))
3845 * ALT alone may or may not want to bring up the System menu.
3846 * If it's not meant to, we return 0 on presses or releases of
3847 * ALT, to show that we've swallowed the keystroke. Otherwise
3848 * we return -1, which means Windows will give the keystroke
3849 * its default handling (i.e. bring up the System menu).
3851 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3857 void request_paste(void)
3860 * In Windows, pasting is synchronous: we can read the
3861 * clipboard with no difficulty, so request_paste() can just go
3864 term_do_paste(term
);
3867 void set_title(char *title
)
3870 window_name
= smalloc(1 + strlen(title
));
3871 strcpy(window_name
, title
);
3872 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3873 SetWindowText(hwnd
, title
);
3876 void set_icon(char *title
)
3879 icon_name
= smalloc(1 + strlen(title
));
3880 strcpy(icon_name
, title
);
3881 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3882 SetWindowText(hwnd
, title
);
3885 void set_sbar(int total
, int start
, int page
)
3889 if (is_full_screen() ?
!cfg
.scrollbar_in_fullscreen
: !cfg
.scrollbar
)
3892 si
.cbSize
= sizeof(si
);
3893 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3895 si
.nMax
= total
- 1;
3899 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3902 Context
get_ctx(void)
3908 SelectPalette(hdc
, pal
, FALSE
);
3914 void free_ctx(Context ctx
)
3916 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3917 ReleaseDC(hwnd
, ctx
);
3920 static void real_palette_set(int n
, int r
, int g
, int b
)
3923 logpal
->palPalEntry
[n
].peRed
= r
;
3924 logpal
->palPalEntry
[n
].peGreen
= g
;
3925 logpal
->palPalEntry
[n
].peBlue
= b
;
3926 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3927 colours
[n
] = PALETTERGB(r
, g
, b
);
3928 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3930 colours
[n
] = RGB(r
, g
, b
);
3933 void palette_set(int n
, int r
, int g
, int b
)
3935 static const int first
[21] = {
3936 0, 2, 4, 6, 8, 10, 12, 14,
3937 1, 3, 5, 7, 9, 11, 13, 15,
3940 real_palette_set(first
[n
], r
, g
, b
);
3942 real_palette_set(first
[n
] + 1, r
, g
, b
);
3944 HDC hdc
= get_ctx();
3945 UnrealizeObject(pal
);
3946 RealizePalette(hdc
);
3951 void palette_reset(void)
3955 for (i
= 0; i
< NCOLOURS
; i
++) {
3957 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3958 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3959 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3960 logpal
->palPalEntry
[i
].peFlags
= 0;
3961 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3962 defpal
[i
].rgbtGreen
,
3963 defpal
[i
].rgbtBlue
);
3965 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3966 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3971 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3973 RealizePalette(hdc
);
3978 void write_aclip(char *data
, int len
, int must_deselect
)
3983 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3986 lock
= GlobalLock(clipdata
);
3989 memcpy(lock
, data
, len
);
3990 ((unsigned char *) lock
)[len
] = 0;
3991 GlobalUnlock(clipdata
);
3994 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3996 if (OpenClipboard(hwnd
)) {
3998 SetClipboardData(CF_TEXT
, clipdata
);
4001 GlobalFree(clipdata
);
4004 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
4008 * Note: unlike write_aclip() this will not append a nul.
4010 void write_clip(wchar_t * data
, int len
, int must_deselect
)
4012 HGLOBAL clipdata
, clipdata2
, clipdata3
;
4014 void *lock
, *lock2
, *lock3
;
4016 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
4018 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
4019 len
* sizeof(wchar_t));
4020 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
4022 if (!clipdata
|| !clipdata2
) {
4024 GlobalFree(clipdata
);
4026 GlobalFree(clipdata2
);
4029 if (!(lock
= GlobalLock(clipdata
)))
4031 if (!(lock2
= GlobalLock(clipdata2
)))
4034 memcpy(lock
, data
, len
* sizeof(wchar_t));
4035 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
4037 if (cfg
.rtf_paste
) {
4038 wchar_t unitab
[256];
4040 unsigned char *tdata
= (unsigned char *)lock2
;
4041 wchar_t *udata
= (wchar_t *)lock
;
4042 int rtflen
= 0, uindex
= 0, tindex
= 0;
4044 int multilen
, blen
, alen
, totallen
, i
;
4045 char before
[16], after
[4];
4047 get_unitab(CP_ACP
, unitab
, 0);
4049 rtfsize
= 100 + strlen(cfg
.font
);
4050 rtf
= smalloc(rtfsize
);
4051 sprintf(rtf
, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
4052 GetACP(), cfg
.font
);
4053 rtflen
= strlen(rtf
);
4056 * We want to construct a piece of RTF that specifies the
4057 * same Unicode text. To do this we will read back in
4058 * parallel from the Unicode data in `udata' and the
4059 * non-Unicode data in `tdata'. For each character in
4060 * `tdata' which becomes the right thing in `udata' when
4061 * looked up in `unitab', we just copy straight over from
4062 * tdata. For each one that doesn't, we must WCToMB it
4063 * individually and produce a \u escape sequence.
4065 * It would probably be more robust to just bite the bullet
4066 * and WCToMB each individual Unicode character one by one,
4067 * then MBToWC each one back to see if it was an accurate
4068 * translation; but that strikes me as a horrifying number
4069 * of Windows API calls so I want to see if this faster way
4070 * will work. If it screws up badly we can always revert to
4071 * the simple and slow way.
4073 while (tindex
< len2
&& uindex
< len
&&
4074 tdata
[tindex
] && udata
[uindex
]) {
4075 if (tindex
+ 1 < len2
&&
4076 tdata
[tindex
] == '\r' &&
4077 tdata
[tindex
+1] == '\n') {
4081 if (unitab
[tdata
[tindex
]] == udata
[uindex
]) {
4087 multilen
= WideCharToMultiByte(CP_ACP
, 0, unitab
+uindex
, 1,
4088 NULL
, 0, NULL
, NULL
);
4089 if (multilen
!= 1) {
4090 blen
= sprintf(before
, "{\\uc%d\\u%d", multilen
,
4092 alen
= 1; strcpy(after
, "}");
4094 blen
= sprintf(before
, "\\u%d", udata
[uindex
]);
4095 alen
= 0; after
[0] = '\0';
4098 assert(tindex
+ multilen
<= len2
);
4099 totallen
= blen
+ alen
;
4100 for (i
= 0; i
< multilen
; i
++) {
4101 if (tdata
[tindex
+i
] == '\\' ||
4102 tdata
[tindex
+i
] == '{' ||
4103 tdata
[tindex
+i
] == '}')
4105 else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A)
4106 totallen
+= 6; /* \par\r\n */
4107 else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20)
4113 if (rtfsize
< rtflen
+ totallen
+ 3) {
4114 rtfsize
= rtflen
+ totallen
+ 512;
4115 rtf
= srealloc(rtf
, rtfsize
);
4118 strcpy(rtf
+ rtflen
, before
); rtflen
+= blen
;
4119 for (i
= 0; i
< multilen
; i
++) {
4120 if (tdata
[tindex
+i
] == '\\' ||
4121 tdata
[tindex
+i
] == '{' ||
4122 tdata
[tindex
+i
] == '}') {
4123 rtf
[rtflen
++] = '\\';
4124 rtf
[rtflen
++] = tdata
[tindex
+i
];
4125 } else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A) {
4126 rtflen
+= sprintf(rtf
+rtflen
, "\\par\r\n");
4127 } else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20) {
4128 rtflen
+= sprintf(rtf
+rtflen
, "\\'%02x", tdata
[tindex
+i
]);
4130 rtf
[rtflen
++] = tdata
[tindex
+i
];
4133 strcpy(rtf
+ rtflen
, after
); rtflen
+= alen
;
4139 strcpy(rtf
+ rtflen
, "}");
4142 clipdata3
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, rtflen
);
4143 if (clipdata3
&& (lock3
= GlobalLock(clipdata3
)) != NULL
) {
4145 GlobalUnlock(clipdata3
);
4151 GlobalUnlock(clipdata
);
4152 GlobalUnlock(clipdata2
);
4155 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
4157 if (OpenClipboard(hwnd
)) {
4159 SetClipboardData(CF_UNICODETEXT
, clipdata
);
4160 SetClipboardData(CF_TEXT
, clipdata2
);
4162 SetClipboardData(RegisterClipboardFormat(CF_RTF
), clipdata3
);
4165 GlobalFree(clipdata
);
4166 GlobalFree(clipdata2
);
4170 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
4173 void get_clip(wchar_t ** p
, int *len
)
4175 static HGLOBAL clipdata
= NULL
;
4176 static wchar_t *converted
= 0;
4185 GlobalUnlock(clipdata
);
4188 } else if (OpenClipboard(NULL
)) {
4189 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
4191 *p
= GlobalLock(clipdata
);
4193 for (p2
= *p
; *p2
; p2
++);
4197 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
4201 s
= GlobalLock(clipdata
);
4202 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
4203 *p
= converted
= smalloc(i
* sizeof(wchar_t));
4204 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
4217 * Move `lines' lines from position `from' to position `to' in the
4220 void optimised_move(int to
, int from
, int lines
)
4225 min
= (to
< from ? to
: from
);
4226 max
= to
+ from
- min
;
4228 r
.left
= offset_width
;
4229 r
.right
= offset_width
+ term
->cols
* font_width
;
4230 r
.top
= offset_height
+ min
* font_height
;
4231 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
4232 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
4237 * Print a message box and perform a fatal exit.
4239 void fatalbox(char *fmt
, ...)
4245 vsprintf(stuff
, fmt
, ap
);
4247 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
4252 * Print a modal (Really Bad) message box and perform a fatal exit.
4254 void modalfatalbox(char *fmt
, ...)
4260 vsprintf(stuff
, fmt
, ap
);
4262 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error",
4263 MB_SYSTEMMODAL
| MB_ICONERROR
| MB_OK
);
4268 * Manage window caption / taskbar flashing, if enabled.
4269 * 0 = stop, 1 = maintain, 2 = start
4271 static void flash_window(int mode
)
4273 static long last_flash
= 0;
4274 static int flashing
= 0;
4275 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
4278 FlashWindow(hwnd
, FALSE
);
4282 } else if (mode
== 2) {
4285 last_flash
= GetTickCount();
4287 FlashWindow(hwnd
, TRUE
);
4290 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
4293 long now
= GetTickCount();
4294 long fdiff
= now
- last_flash
;
4295 if (fdiff
< 0 || fdiff
> 450) {
4297 FlashWindow(hwnd
, TRUE
); /* toggle */
4308 if (mode
== BELL_DEFAULT
) {
4310 * For MessageBeep style bells, we want to be careful of
4311 * timing, because they don't have the nice property of
4312 * PlaySound bells that each one cancels the previous
4313 * active one. So we limit the rate to one per 50ms or so.
4315 static long lastbeep
= 0;
4318 beepdiff
= GetTickCount() - lastbeep
;
4319 if (beepdiff
>= 0 && beepdiff
< 50)
4323 * The above MessageBeep call takes time, so we record the
4324 * time _after_ it finishes rather than before it starts.
4326 lastbeep
= GetTickCount();
4327 } else if (mode
== BELL_WAVEFILE
) {
4328 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
4329 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
4330 sprintf(buf
, "Unable to play sound file\n%s\n"
4331 "Using default sound instead", cfg
.bell_wavefile
);
4332 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
4333 MB_OK
| MB_ICONEXCLAMATION
);
4334 cfg
.beep
= BELL_DEFAULT
;
4337 /* Otherwise, either visual bell or disabled; do nothing here */
4338 if (!term
->has_focus
) {
4339 flash_window(2); /* start */
4344 * Minimise or restore the window in response to a server-side
4347 void set_iconic(int iconic
)
4349 if (IsIconic(hwnd
)) {
4351 ShowWindow(hwnd
, SW_RESTORE
);
4354 ShowWindow(hwnd
, SW_MINIMIZE
);
4359 * Move the window in response to a server-side request.
4361 void move_window(int x
, int y
)
4363 if (cfg
.resize_action
== RESIZE_DISABLED
||
4364 cfg
.resize_action
== RESIZE_FONT
||
4368 SetWindowPos(hwnd
, NULL
, x
, y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
);
4372 * Move the window to the top or bottom of the z-order in response
4373 * to a server-side request.
4375 void set_zorder(int top
)
4377 if (cfg
.alwaysontop
)
4378 return; /* ignore */
4379 SetWindowPos(hwnd
, top ? HWND_TOP
: HWND_BOTTOM
, 0, 0, 0, 0,
4380 SWP_NOMOVE
| SWP_NOSIZE
);
4384 * Refresh the window in response to a server-side request.
4386 void refresh_window(void)
4388 InvalidateRect(hwnd
, NULL
, TRUE
);
4392 * Maximise or restore the window in response to a server-side
4395 void set_zoomed(int zoomed
)
4397 if (IsZoomed(hwnd
)) {
4399 ShowWindow(hwnd
, SW_RESTORE
);
4402 ShowWindow(hwnd
, SW_MAXIMIZE
);
4407 * Report whether the window is iconic, for terminal reports.
4411 return IsIconic(hwnd
);
4415 * Report the window's position, for terminal reports.
4417 void get_window_pos(int *x
, int *y
)
4420 GetWindowRect(hwnd
, &r
);
4426 * Report the window's pixel size, for terminal reports.
4428 void get_window_pixels(int *x
, int *y
)
4431 GetWindowRect(hwnd
, &r
);
4432 *x
= r
.right
- r
.left
;
4433 *y
= r
.bottom
- r
.top
;
4437 * Return the window or icon title.
4439 char *get_window_title(int icon
)
4441 return icon ? icon_name
: window_name
;
4445 * See if we're in full-screen mode.
4447 int is_full_screen()
4449 if (!IsZoomed(hwnd
))
4451 if (GetWindowLong(hwnd
, GWL_STYLE
) & WS_CAPTION
)
4456 /* Get the rect/size of a full screen window using the nearest available
4457 * monitor in multimon systems; default to something sensible if only
4458 * one monitor is present. */
4459 static int get_fullscreen_rect(RECT
* ss
)
4461 #ifdef MONITOR_DEFAULTTONEAREST
4464 mon
= MonitorFromWindow(hwnd
, MONITOR_DEFAULTTONEAREST
);
4465 mi
.cbSize
= sizeof(mi
);
4466 GetMonitorInfo(mon
, &mi
);
4468 /* structure copy */
4472 /* could also use code like this:
4473 ss->left = ss->top = 0;
4474 ss->right = GetSystemMetrics(SM_CXSCREEN);
4475 ss->bottom = GetSystemMetrics(SM_CYSCREEN);
4477 return GetClientRect(GetDesktopWindow(), ss
);
4483 * Go full-screen. This should only be called when we are already
4486 void make_full_screen()
4491 assert(IsZoomed(hwnd
));
4493 if (is_full_screen())
4496 /* Remove the window furniture. */
4497 style
= GetWindowLong(hwnd
, GWL_STYLE
);
4498 style
&= ~(WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
);
4499 if (cfg
.scrollbar_in_fullscreen
)
4500 style
|= WS_VSCROLL
;
4502 style
&= ~WS_VSCROLL
;
4503 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4505 /* Resize ourselves to exactly cover the nearest monitor. */
4506 get_fullscreen_rect(&ss
);
4507 SetWindowPos(hwnd
, HWND_TOP
, ss
.left
, ss
.top
,
4512 /* Tick the menu item in the System menu. */
4513 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4518 * Clear the full-screen attributes.
4520 void clear_full_screen()
4522 DWORD oldstyle
, style
;
4524 /* Reinstate the window furniture. */
4525 style
= oldstyle
= GetWindowLong(hwnd
, GWL_STYLE
);
4526 style
|= WS_CAPTION
| WS_BORDER
;
4527 if (cfg
.resize_action
== RESIZE_DISABLED
)
4528 style
&= ~WS_THICKFRAME
;
4530 style
|= WS_THICKFRAME
;
4532 style
|= WS_VSCROLL
;
4534 style
&= ~WS_VSCROLL
;
4535 if (style
!= oldstyle
) {
4536 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4537 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
4538 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
4542 /* Untick the menu item in the System menu. */
4543 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4548 * Toggle full-screen mode.
4550 void flip_full_screen()
4552 if (is_full_screen()) {
4553 ShowWindow(hwnd
, SW_RESTORE
);
4554 } else if (IsZoomed(hwnd
)) {
4557 SendMessage(hwnd
, WM_FULLSCR_ON_MAX
, 0, 0);
4558 ShowWindow(hwnd
, SW_MAXIMIZE
);
4562 void frontend_keypress(void)
4565 * Keypress termination in non-Close-On-Exit mode is not
4566 * currently supported in PuTTY proper, because the window
4567 * always has a perfectly good Close button anyway. So we do