16 #define COMPILE_MULTIMON_STUBS
27 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
34 #define IDM_SHOWLOG 0x0010
35 #define IDM_NEWSESS 0x0020
36 #define IDM_DUPSESS 0x0030
37 #define IDM_RECONF 0x0040
38 #define IDM_CLRSB 0x0050
39 #define IDM_RESET 0x0060
40 #define IDM_TEL_AYT 0x0070
41 #define IDM_TEL_BRK 0x0080
42 #define IDM_TEL_SYNCH 0x0090
43 #define IDM_TEL_EC 0x00a0
44 #define IDM_TEL_EL 0x00b0
45 #define IDM_TEL_GA 0x00c0
46 #define IDM_TEL_NOP 0x00d0
47 #define IDM_TEL_ABORT 0x00e0
48 #define IDM_TEL_AO 0x00f0
49 #define IDM_TEL_IP 0x0100
50 #define IDM_TEL_SUSP 0x0110
51 #define IDM_TEL_EOR 0x0120
52 #define IDM_TEL_EOF 0x0130
53 #define IDM_HELP 0x0140
54 #define IDM_ABOUT 0x0150
55 #define IDM_SAVEDSESS 0x0160
56 #define IDM_COPYALL 0x0170
57 #define IDM_FULLSCREEN 0x0180
59 #define IDM_SESSLGP 0x0250 /* log type printable */
60 #define IDM_SESSLGA 0x0260 /* log type all chars */
61 #define IDM_SESSLGE 0x0270 /* log end */
62 #define IDM_SAVED_MIN 0x1000
63 #define IDM_SAVED_MAX 0x2000
65 #define WM_IGNORE_CLIP (WM_XUSER + 2)
66 #define WM_FULLSCR_ON_MAX (WM_XUSER + 3)
68 /* Needed for Chinese support and apparently not always defined. */
70 #define VK_PROCESSKEY 0xE5
73 /* Mouse wheel support. */
75 #define WM_MOUSEWHEEL 0x020A /* not defined in earlier SDKs */
78 #define WHEEL_DELTA 120
81 static LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
82 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
83 unsigned char *output
);
84 static void cfgtopalette(void);
85 static void init_palette(void);
86 static void init_fonts(int, int);
87 static void another_font(int);
88 static void deinit_fonts(void);
89 static void set_input_locale(HKL
);
90 static int do_mouse_wheel_msg(UINT message
, WPARAM wParam
, LPARAM lParam
);
92 static int is_full_screen(void);
93 static void make_full_screen(void);
94 static void clear_full_screen(void);
95 static void flip_full_screen(void);
97 /* Window layout information */
98 static void reset_window(int);
99 static int extra_width
, extra_height
;
100 static int font_width
, font_height
, font_dualwidth
;
101 static int offset_width
, offset_height
;
102 static int was_zoomed
= 0;
103 static int prev_rows
, prev_cols
;
105 static int pending_netevent
= 0;
106 static WPARAM pend_netevent_wParam
= 0;
107 static LPARAM pend_netevent_lParam
= 0;
108 static void enact_pending_netevent(void);
109 static void flash_window(int mode
);
110 static void sys_cursor_update(void);
111 static int is_shift_pressed(void);
112 static int get_fullscreen_rect(RECT
* ss
);
114 static time_t last_movement
= 0;
116 static int caret_x
= -1, caret_y
= -1;
119 static Backend
*back
;
120 static void *backhandle
;
121 static Terminal
*term
;
123 #define FONT_NORMAL 0
125 #define FONT_UNDERLINE 2
126 #define FONT_BOLDUND 3
127 #define FONT_WIDE 0x04
128 #define FONT_HIGH 0x08
129 #define FONT_NARROW 0x10
131 #define FONT_OEM 0x20
132 #define FONT_OEMBOLD 0x21
133 #define FONT_OEMUND 0x22
134 #define FONT_OEMBOLDUND 0x23
136 #define FONT_MAXNO 0x2F
138 static HFONT fonts
[FONT_MAXNO
];
139 static LOGFONT lfont
;
140 static int fontflag
[FONT_MAXNO
];
142 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
150 static COLORREF colours
[NCOLOURS
];
152 static LPLOGPALETTE logpal
;
153 static RGBTRIPLE defpal
[NCOLOURS
];
157 static HBITMAP caretbm
;
159 static int dbltime
, lasttime
, lastact
;
160 static Mouse_Button lastbtn
;
162 /* this allows xterm-style mouse handling. */
163 static int send_raw_mouse
= 0;
164 static int wheel_accumulator
= 0;
166 static char *window_name
, *icon_name
;
168 static int compose_state
= 0;
170 static int wsa_started
= 0;
172 static OSVERSIONINFO osVersion
;
174 static UINT wm_mousewheel
= WM_MOUSEWHEEL
;
176 /* Dummy routine, only required in plink. */
177 void ldisc_update(void *frontend
, int echo
, int edit
)
181 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
183 static char appname
[] = "PuTTY";
188 int guess_width
, guess_height
;
191 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
193 winsock_ver
= MAKEWORD(1, 1);
194 if (WSAStartup(winsock_ver
, &wsadata
)) {
195 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
196 MB_OK
| MB_ICONEXCLAMATION
);
199 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
200 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
201 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
206 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
209 InitCommonControls();
211 /* Ensure a Maximize setting in Explorer doesn't maximise the
216 ZeroMemory(&osVersion
, sizeof(osVersion
));
217 osVersion
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
218 if (!GetVersionEx ( (OSVERSIONINFO
*) &osVersion
)) {
219 MessageBox(NULL
, "Windows refuses to report a version",
220 "PuTTY Fatal Error", MB_OK
| MB_ICONEXCLAMATION
);
226 * If we're running a version of Windows that doesn't support
227 * WM_MOUSEWHEEL, find out what message number we should be
230 if (osVersion
.dwMajorVersion
< 4 ||
231 (osVersion
.dwMajorVersion
== 4 &&
232 osVersion
.dwPlatformId
!= VER_PLATFORM_WIN32_NT
))
233 wm_mousewheel
= RegisterWindowMessage("MSWHEEL_ROLLMSG");
236 * See if we can find our Help file.
239 char b
[2048], *p
, *q
, *r
;
241 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
243 p
= strrchr(b
, '\\');
244 if (p
&& p
>= r
) r
= p
+1;
246 if (q
&& q
>= r
) r
= q
+1;
247 strcpy(r
, "putty.hlp");
248 if ( (fp
= fopen(b
, "r")) != NULL
) {
249 help_path
= dupstr(b
);
253 strcpy(r
, "putty.cnt");
254 if ( (fp
= fopen(b
, "r")) != NULL
) {
255 help_has_contents
= TRUE
;
258 help_has_contents
= FALSE
;
262 * Process the command line.
268 default_protocol
= DEFAULT_PROTOCOL
;
269 default_port
= DEFAULT_PORT
;
270 cfg
.logtype
= LGTYP_NONE
;
272 do_defaults(NULL
, &cfg
);
277 * Process a couple of command-line options which are more
278 * easily dealt with before the line is broken up into
279 * words. These are the soon-to-be-defunct @sessionname and
280 * the internal-use-only &sharedmemoryhandle, neither of
281 * which are combined with anything else.
283 while (*p
&& isspace(*p
))
287 while (i
> 1 && isspace(p
[i
- 1]))
290 do_defaults(p
+ 1, &cfg
);
291 if (!*cfg
.host
&& !do_config()) {
295 } else if (*p
== '&') {
297 * An initial & means we've been given a command line
298 * containing the hex value of a HANDLE for a file
299 * mapping object, which we must then extract as a
304 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
305 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
306 0, 0, sizeof(Config
))) != NULL
) {
309 CloseHandle(filemap
);
310 } else if (!do_config()) {
316 * Otherwise, break up the command line and deal with
322 split_into_argv(cmdline
, &argc
, &argv
, NULL
);
324 for (i
= 0; i
< argc
; i
++) {
328 ret
= cmdline_process_param(p
, i
+1<argc?argv
[i
+1]:NULL
, 1);
330 cmdline_error("option \"%s\" requires an argument", p
);
331 } else if (ret
== 2) {
332 i
++; /* skip next argument */
333 } else if (ret
== 1) {
334 continue; /* nothing further needs doing */
335 } else if (!strcmp(p
, "-cleanup")) {
337 * `putty -cleanup'. Remove all registry
338 * entries associated with PuTTY, and also find
339 * and delete the random seed file.
342 "This procedure will remove ALL Registry\n"
343 "entries associated with PuTTY, and will\n"
344 "also remove the PuTTY random seed file.\n"
346 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
347 "SESSIONS. Are you really sure you want\n"
350 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
354 } else if (*p
!= '-') {
358 * If we already have a host name, treat
359 * this argument as a port number. NB we
360 * have to treat this as a saved -P
361 * argument, so that it will be deferred
362 * until it's a good moment to run it.
364 int ret
= cmdline_process_param("-P", p
, 1);
366 } else if (!strncmp(q
, "telnet:", 7)) {
368 * If the hostname starts with "telnet:",
369 * set the protocol to Telnet and process
370 * the string as a Telnet URL.
375 if (q
[0] == '/' && q
[1] == '/')
377 cfg
.protocol
= PROT_TELNET
;
379 while (*p
&& *p
!= ':' && *p
!= '/')
388 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
389 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
393 * Otherwise, treat this argument as a host
396 while (*p
&& !isspace(*p
))
400 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
401 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
410 if (!*cfg
.host
&& !do_config()) {
416 * Trim leading whitespace off the hostname if it's there.
419 int space
= strspn(cfg
.host
, " \t");
420 memmove(cfg
.host
, cfg
.host
+space
, 1+strlen(cfg
.host
)-space
);
423 /* See if host is of the form user@host */
424 if (cfg
.host
[0] != '\0') {
425 char *atsign
= strchr(cfg
.host
, '@');
426 /* Make sure we're not overflowing the user field */
428 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
429 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
430 cfg
.username
[atsign
- cfg
.host
] = '\0';
432 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
437 * Trim a colon suffix off the hostname if it's there.
439 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
442 * Remove any remaining whitespace from the hostname.
446 while (cfg
.host
[p2
] != '\0') {
447 if (cfg
.host
[p2
] != ' ' && cfg
.host
[p2
] != '\t') {
448 cfg
.host
[p1
] = cfg
.host
[p2
];
458 * Select protocol. This is farmed out into a table in a
459 * separate file to enable an ssh-free variant.
464 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
465 if (backends
[i
].protocol
== cfg
.protocol
) {
466 back
= backends
[i
].backend
;
470 MessageBox(NULL
, "Unsupported protocol number found",
471 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
477 /* Check for invalid Port number (i.e. zero) */
479 MessageBox(NULL
, "Invalid Port Number",
480 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
487 wndclass
.lpfnWndProc
= WndProc
;
488 wndclass
.cbClsExtra
= 0;
489 wndclass
.cbWndExtra
= 0;
490 wndclass
.hInstance
= inst
;
491 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
492 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
493 wndclass
.hbrBackground
= NULL
;
494 wndclass
.lpszMenuName
= NULL
;
495 wndclass
.lpszClassName
= appname
;
497 RegisterClass(&wndclass
);
507 * Guess some defaults for the window size. This all gets
508 * updated later, so we don't really care too much. However, we
509 * do want the font width/height guesses to correspond to a
510 * large font rather than a small one...
517 term_size(term
, cfg
.height
, cfg
.width
, cfg
.savelines
);
518 guess_width
= extra_width
+ font_width
* term
->cols
;
519 guess_height
= extra_height
+ font_height
* term
->rows
;
522 get_fullscreen_rect(&r
);
523 if (guess_width
> r
.right
- r
.left
)
524 guess_width
= r
.right
- r
.left
;
525 if (guess_height
> r
.bottom
- r
.top
)
526 guess_height
= r
.bottom
- r
.top
;
530 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
533 winmode
&= ~(WS_VSCROLL
);
534 if (cfg
.resize_action
== RESIZE_DISABLED
)
535 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
537 exwinmode
|= WS_EX_TOPMOST
;
539 exwinmode
|= WS_EX_CLIENTEDGE
;
540 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
541 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
542 guess_width
, guess_height
,
543 NULL
, NULL
, inst
, NULL
);
547 * Initialise the fonts, simultaneously correcting the guesses
548 * for font_{width,height}.
553 * Correct the guesses for extra_{width,height}.
557 GetWindowRect(hwnd
, &wr
);
558 GetClientRect(hwnd
, &cr
);
559 offset_width
= offset_height
= cfg
.window_border
;
560 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
561 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
565 * Resize the window, now we know what size we _really_ want it
568 guess_width
= extra_width
+ font_width
* term
->cols
;
569 guess_height
= extra_height
+ font_height
* term
->rows
;
570 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
571 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
574 * Set up a caret bitmap, with no content.
578 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
579 bits
= smalloc(size
);
580 memset(bits
, 0, size
);
581 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
584 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
587 * Initialise the scroll bar.
592 si
.cbSize
= sizeof(si
);
593 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
595 si
.nMax
= term
->rows
- 1;
596 si
.nPage
= term
->rows
;
598 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
602 * Start up the telnet connection.
606 char msg
[1024], *title
;
609 error
= back
->init((void *)term
, &backhandle
,
610 cfg
.host
, cfg
.port
, &realhost
, cfg
.tcp_nodelay
);
612 sprintf(msg
, "Unable to open connection to\n"
613 "%.800s\n" "%s", cfg
.host
, error
);
614 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
617 window_name
= icon_name
= NULL
;
619 title
= cfg
.wintitle
;
621 sprintf(msg
, "%s - PuTTY", realhost
);
630 * Connect the terminal to the backend for resize purposes.
632 term_provide_resize_fn(term
, back
->size
, backhandle
);
635 * Set up a line discipline.
637 ldisc
= ldisc_create(term
, back
, backhandle
, NULL
);
639 session_closed
= FALSE
;
642 * Prepare the mouse handler.
644 lastact
= MA_NOTHING
;
645 lastbtn
= MBT_NOTHING
;
646 dbltime
= GetDoubleClickTime();
649 * Set up the session-control options on the system menu.
652 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
656 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
657 if (cfg
.protocol
== PROT_TELNET
) {
659 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
660 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
661 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
662 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
663 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
664 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
665 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
666 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
667 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
668 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
669 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
670 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
671 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
672 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
673 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
674 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
675 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
677 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
679 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
680 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
681 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
682 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
685 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
686 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
688 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
689 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
690 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
691 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
692 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
693 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
694 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
695 AppendMenu(m
, (cfg
.resize_action
== RESIZE_DISABLED
) ?
696 MF_GRAYED
: MF_ENABLED
, IDM_FULLSCREEN
, "&Full Screen");
697 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
699 AppendMenu(m
, MF_ENABLED
, IDM_HELP
, "&Help");
700 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
704 * Set up the initial input locale.
706 set_input_locale(GetKeyboardLayout(0));
709 * Open the initial log file if there is one.
714 * Finally show the window!
716 ShowWindow(hwnd
, show
);
717 SetForegroundWindow(hwnd
);
720 * Set the palette up.
726 term
->has_focus
= (GetForegroundWindow() == hwnd
);
729 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
730 int timer_id
= 0, long_timer
= 0;
732 while (msg
.message
!= WM_QUIT
) {
733 /* Sometimes DispatchMessage calls routines that use their own
734 * GetMessage loop, setup this timer so we get some control back.
736 * Also call term_update() from the timer so that if the host
737 * is sending data flat out we still do redraws.
739 if (timer_id
&& long_timer
) {
740 KillTimer(hwnd
, timer_id
);
741 long_timer
= timer_id
= 0;
744 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
745 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
746 DispatchMessage(&msg
);
748 /* Make sure we blink everything that needs it. */
751 /* Send the paste buffer if there's anything to send */
754 /* If there's nothing new in the queue then we can do everything
755 * we've delayed, reading the socket, writing, and repainting
758 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
761 if (pending_netevent
) {
762 enact_pending_netevent();
764 /* Force the cursor blink on */
767 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
771 /* Okay there is now nothing to do so we make sure the screen is
772 * completely up to date then tell windows to call us in a little
776 KillTimer(hwnd
, timer_id
);
780 if (GetCapture() != hwnd
||
782 !(cfg
.mouse_override
&& is_shift_pressed())))
787 flash_window(1); /* maintain */
789 /* The messages seem unreliable; especially if we're being tricky */
790 term
->has_focus
= (GetForegroundWindow() == hwnd
);
793 /* Hmm, term_update didn't want to do an update too soon ... */
794 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
795 else if (!term
->has_focus
)
796 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
798 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
801 /* There's no point rescanning everything in the message queue
802 * so we do an apparently unnecessary wait here
805 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
810 cleanup_exit(msg
.wParam
); /* this doesn't return... */
811 return msg
.wParam
; /* ... but optimiser doesn't know */
817 void cleanup_exit(int code
)
830 if (cfg
.protocol
== PROT_SSH
) {
841 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
843 char *do_select(SOCKET skt
, int startup
)
848 events
= (FD_CONNECT
| FD_READ
| FD_WRITE
|
849 FD_OOB
| FD_CLOSE
| FD_ACCEPT
);
854 return "do_select(): internal error (hwnd==NULL)";
855 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
856 switch (WSAGetLastError()) {
858 return "Network is down";
860 return "WSAAsyncSelect(): unknown error";
867 * set or clear the "raw mouse message" mode
869 void set_raw_mouse_mode(int activate
)
871 activate
= activate
&& !cfg
.no_mouse_rep
;
872 send_raw_mouse
= activate
;
873 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
877 * Print a message box and close the connection.
879 void connection_fatal(char *fmt
, ...)
885 vsprintf(stuff
, fmt
, ap
);
887 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
888 if (cfg
.close_on_exit
== COE_ALWAYS
)
891 session_closed
= TRUE
;
892 SetWindowText(hwnd
, "PuTTY (inactive)");
897 * Report an error at the command-line parsing stage.
899 void cmdline_error(char *fmt
, ...)
905 vsprintf(stuff
, fmt
, ap
);
907 MessageBox(hwnd
, stuff
, "PuTTY Command Line Error", MB_ICONERROR
| MB_OK
);
912 * Actually do the job requested by a WM_NETEVENT
914 static void enact_pending_netevent(void)
916 static int reentering
= 0;
917 extern int select_result(WPARAM
, LPARAM
);
921 return; /* don't unpend the pending */
923 pending_netevent
= FALSE
;
926 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
929 if (ret
== 0 && !session_closed
) {
930 /* Abnormal exits will already have set session_closed and taken
931 * appropriate action. */
932 if (cfg
.close_on_exit
== COE_ALWAYS
||
933 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
935 session_closed
= TRUE
;
936 SetWindowText(hwnd
, "PuTTY (inactive)");
937 MessageBox(hwnd
, "Connection closed by remote host",
938 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
944 * Copy the colour palette from the configuration data into defpal.
945 * This is non-trivial because the colour indices are different.
947 static void cfgtopalette(void)
950 static const int ww
[] = {
951 6, 7, 8, 9, 10, 11, 12, 13,
952 14, 15, 16, 17, 18, 19, 20, 21,
953 0, 1, 2, 3, 4, 4, 5, 5
956 for (i
= 0; i
< 24; i
++) {
958 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
959 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
960 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
965 * Set up the colour palette.
967 static void init_palette(void)
970 HDC hdc
= GetDC(hwnd
);
972 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
973 logpal
= smalloc(sizeof(*logpal
)
974 - sizeof(logpal
->palPalEntry
)
975 + NCOLOURS
* sizeof(PALETTEENTRY
));
976 logpal
->palVersion
= 0x300;
977 logpal
->palNumEntries
= NCOLOURS
;
978 for (i
= 0; i
< NCOLOURS
; i
++) {
979 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
980 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
981 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
982 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
984 pal
= CreatePalette(logpal
);
986 SelectPalette(hdc
, pal
, FALSE
);
988 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
991 ReleaseDC(hwnd
, hdc
);
994 for (i
= 0; i
< NCOLOURS
; i
++)
995 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
999 for (i
= 0; i
< NCOLOURS
; i
++)
1000 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
1001 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
1005 * Initialise all the fonts we will need initially. There may be as many as
1006 * three or as few as one. The other (poentially) twentyone fonts are done
1007 * if/when they are needed.
1011 * - check the font width and height, correcting our guesses if
1014 * - verify that the bold font is the same width as the ordinary
1015 * one, and engage shadow bolding if not.
1017 * - verify that the underlined font is the same width as the
1018 * ordinary one (manual underlining by means of line drawing can
1019 * be done in a pinch).
1021 static void init_fonts(int pick_width
, int pick_height
)
1028 int fw_dontcare
, fw_bold
;
1030 for (i
= 0; i
< FONT_MAXNO
; i
++)
1033 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
1034 und_mode
= UND_FONT
;
1036 if (cfg
.fontisbold
) {
1037 fw_dontcare
= FW_BOLD
;
1040 fw_dontcare
= FW_DONTCARE
;
1047 font_height
= pick_height
;
1049 font_height
= cfg
.fontheight
;
1050 if (font_height
> 0) {
1052 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
1055 font_width
= pick_width
;
1057 #define f(i,c,w,u) \
1058 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
1059 c, OUT_DEFAULT_PRECIS, \
1060 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
1061 FIXED_PITCH | FF_DONTCARE, cfg.font)
1063 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
1065 lfont
.lfHeight
= font_height
;
1066 lfont
.lfWidth
= font_width
;
1067 lfont
.lfEscapement
= 0;
1068 lfont
.lfOrientation
= 0;
1069 lfont
.lfWeight
= fw_dontcare
;
1070 lfont
.lfItalic
= FALSE
;
1071 lfont
.lfUnderline
= FALSE
;
1072 lfont
.lfStrikeOut
= FALSE
;
1073 lfont
.lfCharSet
= cfg
.fontcharset
;
1074 lfont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
1075 lfont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
1076 lfont
.lfQuality
= DEFAULT_QUALITY
;
1077 lfont
.lfPitchAndFamily
= FIXED_PITCH
| FF_DONTCARE
;
1078 strncpy(lfont
.lfFaceName
, cfg
.font
, LF_FACESIZE
);
1080 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
1081 GetTextMetrics(hdc
, &tm
);
1083 if (pick_width
== 0 || pick_height
== 0) {
1084 font_height
= tm
.tmHeight
;
1085 font_width
= tm
.tmAveCharWidth
;
1087 font_dualwidth
= (tm
.tmAveCharWidth
!= tm
.tmMaxCharWidth
);
1089 #ifdef RDB_DEBUG_PATCH
1090 debug(23, "Primary font H=%d, AW=%d, MW=%d",
1091 tm
.tmHeight
, tm
.tmAveCharWidth
, tm
.tmMaxCharWidth
);
1096 DWORD cset
= tm
.tmCharSet
;
1097 memset(&info
, 0xFF, sizeof(info
));
1099 /* !!! Yes the next line is right */
1100 if (cset
== OEM_CHARSET
)
1101 font_codepage
= GetOEMCP();
1103 if (TranslateCharsetInfo
1104 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
1109 GetCPInfo(font_codepage
, &cpinfo
);
1110 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
1113 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
1116 * Some fonts, e.g. 9-pt Courier, draw their underlines
1117 * outside their character cell. We successfully prevent
1118 * screen corruption by clipping the text output, but then
1119 * we lose the underline completely. Here we try to work
1120 * out whether this is such a font, and if it is, we set a
1121 * flag that causes underlines to be drawn by hand.
1123 * Having tried other more sophisticated approaches (such
1124 * as examining the TEXTMETRIC structure or requesting the
1125 * height of a string), I think we'll do this the brute
1126 * force way: we create a small bitmap, draw an underlined
1127 * space on it, and test to see whether any pixels are
1128 * foreground-coloured. (Since we expect the underline to
1129 * go all the way across the character cell, we only search
1130 * down a single column of the bitmap, half way across.)
1134 HBITMAP und_bm
, und_oldbm
;
1138 und_dc
= CreateCompatibleDC(hdc
);
1139 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
1140 und_oldbm
= SelectObject(und_dc
, und_bm
);
1141 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
1142 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
1143 SetTextColor(und_dc
, RGB(255, 255, 255));
1144 SetBkColor(und_dc
, RGB(0, 0, 0));
1145 SetBkMode(und_dc
, OPAQUE
);
1146 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
1148 for (i
= 0; i
< font_height
; i
++) {
1149 c
= GetPixel(und_dc
, font_width
/ 2, i
);
1150 if (c
!= RGB(0, 0, 0))
1153 SelectObject(und_dc
, und_oldbm
);
1154 DeleteObject(und_bm
);
1157 und_mode
= UND_LINE
;
1158 DeleteObject(fonts
[FONT_UNDERLINE
]);
1159 fonts
[FONT_UNDERLINE
] = 0;
1163 if (bold_mode
== BOLD_FONT
) {
1164 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
1168 descent
= tm
.tmAscent
+ 1;
1169 if (descent
>= font_height
)
1170 descent
= font_height
- 1;
1172 for (i
= 0; i
< 3; i
++) {
1174 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
1175 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
1182 ReleaseDC(hwnd
, hdc
);
1184 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1185 und_mode
= UND_LINE
;
1186 DeleteObject(fonts
[FONT_UNDERLINE
]);
1187 fonts
[FONT_UNDERLINE
] = 0;
1190 if (bold_mode
== BOLD_FONT
&&
1191 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1192 bold_mode
= BOLD_SHADOW
;
1193 DeleteObject(fonts
[FONT_BOLD
]);
1194 fonts
[FONT_BOLD
] = 0;
1196 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1201 static void another_font(int fontno
)
1204 int fw_dontcare
, fw_bold
;
1208 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1211 basefont
= (fontno
& ~(FONT_BOLDUND
));
1212 if (basefont
!= fontno
&& !fontflag
[basefont
])
1213 another_font(basefont
);
1215 if (cfg
.fontisbold
) {
1216 fw_dontcare
= FW_BOLD
;
1219 fw_dontcare
= FW_DONTCARE
;
1223 c
= cfg
.fontcharset
;
1229 if (fontno
& FONT_WIDE
)
1231 if (fontno
& FONT_NARROW
)
1233 if (fontno
& FONT_OEM
)
1235 if (fontno
& FONT_BOLD
)
1237 if (fontno
& FONT_UNDERLINE
)
1241 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1242 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1243 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1244 FIXED_PITCH
| FF_DONTCARE
, s
);
1246 fontflag
[fontno
] = 1;
1249 static void deinit_fonts(void)
1252 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1254 DeleteObject(fonts
[i
]);
1260 void request_resize(int w
, int h
)
1264 /* If the window is maximized supress resizing attempts */
1265 if (IsZoomed(hwnd
)) {
1266 if (cfg
.resize_action
== RESIZE_TERM
)
1270 if (cfg
.resize_action
== RESIZE_DISABLED
) return;
1271 if (h
== term
->rows
&& w
== term
->cols
) return;
1273 /* Sanity checks ... */
1275 static int first_time
= 1;
1278 switch (first_time
) {
1280 /* Get the size of the screen */
1281 if (get_fullscreen_rect(&ss
))
1282 /* first_time = 0 */ ;
1288 /* Make sure the values are sane */
1289 width
= (ss
.right
- ss
.left
- extra_width
) / 4;
1290 height
= (ss
.bottom
- ss
.top
- extra_height
) / 6;
1292 if (w
> width
|| h
> height
)
1301 term_size(term
, h
, w
, cfg
.savelines
);
1303 if (cfg
.resize_action
!= RESIZE_FONT
&& !IsZoomed(hwnd
)) {
1304 width
= extra_width
+ font_width
* w
;
1305 height
= extra_height
+ font_height
* h
;
1307 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1308 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1309 SWP_NOMOVE
| SWP_NOZORDER
);
1313 InvalidateRect(hwnd
, NULL
, TRUE
);
1316 static void reset_window(int reinit
) {
1318 * This function decides how to resize or redraw when the
1319 * user changes something.
1321 * This function doesn't like to change the terminal size but if the
1322 * font size is locked that may be it's only soluion.
1324 int win_width
, win_height
;
1327 #ifdef RDB_DEBUG_PATCH
1328 debug((27, "reset_window()"));
1331 /* Current window sizes ... */
1332 GetWindowRect(hwnd
, &wr
);
1333 GetClientRect(hwnd
, &cr
);
1335 win_width
= cr
.right
- cr
.left
;
1336 win_height
= cr
.bottom
- cr
.top
;
1338 if (cfg
.resize_action
== RESIZE_DISABLED
) reinit
= 2;
1340 /* Are we being forced to reload the fonts ? */
1342 #ifdef RDB_DEBUG_PATCH
1343 debug((27, "reset_window() -- Forced deinit"));
1349 /* Oh, looks like we're minimised */
1350 if (win_width
== 0 || win_height
== 0)
1353 /* Is the window out of position ? */
1355 (offset_width
!= (win_width
-font_width
*term
->cols
)/2 ||
1356 offset_height
!= (win_height
-font_height
*term
->rows
)/2) ){
1357 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1358 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1359 InvalidateRect(hwnd
, NULL
, TRUE
);
1360 #ifdef RDB_DEBUG_PATCH
1361 debug((27, "reset_window() -> Reposition terminal"));
1365 if (IsZoomed(hwnd
)) {
1366 /* We're fullscreen, this means we must not change the size of
1367 * the window so it's the font size or the terminal itself.
1370 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1371 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1373 if (cfg
.resize_action
!= RESIZE_TERM
) {
1374 if ( font_width
!= win_width
/term
->cols
||
1375 font_height
!= win_height
/term
->rows
) {
1377 init_fonts(win_width
/term
->cols
, win_height
/term
->rows
);
1378 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1379 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1380 InvalidateRect(hwnd
, NULL
, TRUE
);
1381 #ifdef RDB_DEBUG_PATCH
1382 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1383 font_width
, font_height
));
1387 if ( font_width
!= win_width
/term
->cols
||
1388 font_height
!= win_height
/term
->rows
) {
1389 /* Our only choice at this point is to change the
1390 * size of the terminal; Oh well.
1392 term_size(term
, win_height
/font_height
, win_width
/font_width
,
1394 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1395 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1396 InvalidateRect(hwnd
, NULL
, TRUE
);
1397 #ifdef RDB_DEBUG_PATCH
1398 debug((27, "reset_window() -> Zoomed term_size"));
1405 /* Hmm, a force re-init means we should ignore the current window
1406 * so we resize to the default font size.
1409 #ifdef RDB_DEBUG_PATCH
1410 debug((27, "reset_window() -> Forced re-init"));
1413 offset_width
= offset_height
= cfg
.window_border
;
1414 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1415 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1417 if (win_width
!= font_width
*term
->cols
+ offset_width
*2 ||
1418 win_height
!= font_height
*term
->rows
+ offset_height
*2) {
1420 /* If this is too large windows will resize it to the maximum
1421 * allowed window size, we will then be back in here and resize
1422 * the font or terminal to fit.
1424 SetWindowPos(hwnd
, NULL
, 0, 0,
1425 font_width
*term
->cols
+ extra_width
,
1426 font_height
*term
->rows
+ extra_height
,
1427 SWP_NOMOVE
| SWP_NOZORDER
);
1430 InvalidateRect(hwnd
, NULL
, TRUE
);
1434 /* Okay the user doesn't want us to change the font so we try the
1435 * window. But that may be too big for the screen which forces us
1436 * to change the terminal.
1438 if ((cfg
.resize_action
== RESIZE_TERM
&& reinit
<=0) ||
1439 (cfg
.resize_action
== RESIZE_EITHER
&& reinit
<0) ||
1441 offset_width
= offset_height
= cfg
.window_border
;
1442 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1443 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1445 if (win_width
!= font_width
*term
->cols
+ offset_width
*2 ||
1446 win_height
!= font_height
*term
->rows
+ offset_height
*2) {
1451 get_fullscreen_rect(&ss
);
1453 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1454 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1457 if ( term
->rows
> height
|| term
->cols
> width
) {
1458 if (cfg
.resize_action
== RESIZE_EITHER
) {
1459 /* Make the font the biggest we can */
1460 if (term
->cols
> width
)
1461 font_width
= (ss
.right
- ss
.left
- extra_width
)
1463 if (term
->rows
> height
)
1464 font_height
= (ss
.bottom
- ss
.top
- extra_height
)
1468 init_fonts(font_width
, font_height
);
1470 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1471 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1473 if ( height
> term
->rows
) height
= term
->rows
;
1474 if ( width
> term
->cols
) width
= term
->cols
;
1475 term_size(term
, height
, width
, cfg
.savelines
);
1476 #ifdef RDB_DEBUG_PATCH
1477 debug((27, "reset_window() -> term resize to (%d,%d)",
1483 SetWindowPos(hwnd
, NULL
, 0, 0,
1484 font_width
*term
->cols
+ extra_width
,
1485 font_height
*term
->rows
+ extra_height
,
1486 SWP_NOMOVE
| SWP_NOZORDER
);
1488 InvalidateRect(hwnd
, NULL
, TRUE
);
1489 #ifdef RDB_DEBUG_PATCH
1490 debug((27, "reset_window() -> window resize to (%d,%d)",
1491 font_width
*term
->cols
+ extra_width
,
1492 font_height
*term
->rows
+ extra_height
));
1498 /* We're allowed to or must change the font but do we want to ? */
1500 if (font_width
!= (win_width
-cfg
.window_border
*2)/term
->cols
||
1501 font_height
!= (win_height
-cfg
.window_border
*2)/term
->rows
) {
1504 init_fonts((win_width
-cfg
.window_border
*2)/term
->cols
,
1505 (win_height
-cfg
.window_border
*2)/term
->rows
);
1506 offset_width
= (win_width
-font_width
*term
->cols
)/2;
1507 offset_height
= (win_height
-font_height
*term
->rows
)/2;
1509 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+offset_width
*2;
1510 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1512 InvalidateRect(hwnd
, NULL
, TRUE
);
1513 #ifdef RDB_DEBUG_PATCH
1514 debug((25, "reset_window() -> font resize to (%d,%d)",
1515 font_width
, font_height
));
1520 static void set_input_locale(HKL kl
)
1524 GetLocaleInfo(LOWORD(kl
), LOCALE_IDEFAULTANSICODEPAGE
,
1525 lbuf
, sizeof(lbuf
));
1527 kbd_codepage
= atoi(lbuf
);
1530 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
, int alt
)
1532 int thistime
= GetMessageTime();
1534 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1535 lastbtn
= MBT_NOTHING
;
1536 term_mouse(term
, b
, MA_CLICK
, x
, y
, shift
, ctrl
, alt
);
1540 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1541 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1542 lastact
== MA_2CLK ? MA_3CLK
:
1543 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1548 if (lastact
!= MA_NOTHING
)
1549 term_mouse(term
, b
, lastact
, x
, y
, shift
, ctrl
, alt
);
1550 lasttime
= thistime
;
1554 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1555 * into a cooked one (SELECT, EXTEND, PASTE).
1557 Mouse_Button
translate_button(Mouse_Button button
)
1559 if (button
== MBT_LEFT
)
1561 if (button
== MBT_MIDDLE
)
1562 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1563 if (button
== MBT_RIGHT
)
1564 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1565 return 0; /* shouldn't happen */
1568 static void show_mouseptr(int show
)
1570 static int cursor_visible
= 1;
1571 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1573 if (cursor_visible
&& !show
)
1575 else if (!cursor_visible
&& show
)
1577 cursor_visible
= show
;
1580 static int is_alt_pressed(void)
1583 int r
= GetKeyboardState(keystate
);
1586 if (keystate
[VK_MENU
] & 0x80)
1588 if (keystate
[VK_RMENU
] & 0x80)
1593 static int is_shift_pressed(void)
1596 int r
= GetKeyboardState(keystate
);
1599 if (keystate
[VK_SHIFT
] & 0x80)
1604 static int resizing
;
1606 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1607 WPARAM wParam
, LPARAM lParam
)
1610 static int ignore_clip
= FALSE
;
1611 static int need_backend_resize
= FALSE
;
1612 static int fullscr_on_max
= FALSE
;
1616 if (pending_netevent
)
1617 enact_pending_netevent();
1618 if (GetCapture() != hwnd
||
1619 (send_raw_mouse
&& !(cfg
.mouse_override
&& is_shift_pressed())))
1625 if (cfg
.ping_interval
> 0) {
1628 if (now
- last_movement
> cfg
.ping_interval
) {
1629 back
->special(backhandle
, TS_PING
);
1630 last_movement
= now
;
1633 net_pending_errors();
1639 if (!cfg
.warn_on_close
|| session_closed
||
1641 "Are you sure you want to close this session?",
1642 "PuTTY Exit Confirmation",
1643 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1644 DestroyWindow(hwnd
);
1651 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1663 PROCESS_INFORMATION pi
;
1664 HANDLE filemap
= NULL
;
1666 if (wParam
== IDM_DUPSESS
) {
1668 * Allocate a file-mapping memory chunk for the
1671 SECURITY_ATTRIBUTES sa
;
1674 sa
.nLength
= sizeof(sa
);
1675 sa
.lpSecurityDescriptor
= NULL
;
1676 sa
.bInheritHandle
= TRUE
;
1677 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1680 0, sizeof(Config
), NULL
);
1682 p
= (Config
*) MapViewOfFile(filemap
,
1684 0, 0, sizeof(Config
));
1686 *p
= cfg
; /* structure copy */
1690 sprintf(c
, "putty &%p", filemap
);
1692 } else if (wParam
== IDM_SAVEDSESS
) {
1693 if ((lParam
- IDM_SAVED_MIN
) / 16 < nsessions
) {
1695 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1696 cl
= smalloc(16 + strlen(session
));
1697 /* 8, but play safe */
1700 /* not a very important failure mode */
1702 sprintf(cl
, "putty @%s", session
);
1710 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1712 si
.lpReserved
= NULL
;
1713 si
.lpDesktop
= NULL
;
1717 si
.lpReserved2
= NULL
;
1718 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1719 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1722 CloseHandle(filemap
);
1732 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1735 if (!do_reconfig(hwnd
))
1739 /* Disable full-screen if resizing forbidden */
1740 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
1741 EnableMenuItem(m
, IDM_FULLSCREEN
, MF_BYCOMMAND
|
1742 (cfg
.resize_action
== RESIZE_DISABLED
)
1743 ? MF_GRAYED
: MF_ENABLED
);
1744 /* Gracefully unzoom if necessary */
1745 if (IsZoomed(hwnd
) &&
1746 (cfg
.resize_action
== RESIZE_DISABLED
)) {
1747 ShowWindow(hwnd
, SW_RESTORE
);
1751 if (strcmp(prev_cfg
.logfilename
, cfg
.logfilename
) ||
1752 prev_cfg
.logtype
!= cfg
.logtype
) {
1753 logfclose(); /* reset logging */
1759 * Flush the line discipline's edit buffer in the
1760 * case where local editing has just been disabled.
1762 ldisc_send(ldisc
, NULL
, 0, 0);
1770 /* Give terminal a heads-up on miscellaneous stuff */
1771 term_reconfig(term
);
1773 /* Screen size changed ? */
1774 if (cfg
.height
!= prev_cfg
.height
||
1775 cfg
.width
!= prev_cfg
.width
||
1776 cfg
.savelines
!= prev_cfg
.savelines
||
1777 cfg
.resize_action
== RESIZE_FONT
||
1778 (cfg
.resize_action
== RESIZE_EITHER
&& IsZoomed(hwnd
)) ||
1779 cfg
.resize_action
== RESIZE_DISABLED
)
1780 term_size(term
, cfg
.height
, cfg
.width
, cfg
.savelines
);
1782 /* Enable or disable the scroll bar, etc */
1784 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1785 LONG nexflag
, exflag
=
1786 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1789 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1790 if (cfg
.alwaysontop
) {
1791 nexflag
|= WS_EX_TOPMOST
;
1792 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1793 SWP_NOMOVE
| SWP_NOSIZE
);
1795 nexflag
&= ~(WS_EX_TOPMOST
);
1796 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1797 SWP_NOMOVE
| SWP_NOSIZE
);
1800 if (cfg
.sunken_edge
)
1801 nexflag
|= WS_EX_CLIENTEDGE
;
1803 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1806 if (is_full_screen() ?
1807 cfg
.scrollbar_in_fullscreen
: cfg
.scrollbar
)
1810 nflg
&= ~WS_VSCROLL
;
1812 if (cfg
.resize_action
== RESIZE_DISABLED
||
1814 nflg
&= ~WS_THICKFRAME
;
1816 nflg
|= WS_THICKFRAME
;
1818 if (cfg
.resize_action
== RESIZE_DISABLED
)
1819 nflg
&= ~WS_MAXIMIZEBOX
;
1821 nflg
|= WS_MAXIMIZEBOX
;
1823 if (nflg
!= flag
|| nexflag
!= exflag
) {
1825 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1826 if (nexflag
!= exflag
)
1827 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1829 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1830 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1831 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1839 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1844 set_title(cfg
.wintitle
);
1845 if (IsIconic(hwnd
)) {
1847 cfg
.win_name_always ? window_name
:
1851 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1852 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1853 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1854 cfg
.fontheight
!= prev_cfg
.fontheight
||
1855 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1856 cfg
.vtmode
!= prev_cfg
.vtmode
||
1857 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1858 cfg
.resize_action
== RESIZE_DISABLED
||
1859 cfg
.resize_action
== RESIZE_EITHER
||
1860 (cfg
.resize_action
!= prev_cfg
.resize_action
))
1863 InvalidateRect(hwnd
, NULL
, TRUE
);
1864 reset_window(init_lvl
);
1865 net_pending_errors();
1876 ldisc_send(ldisc
, NULL
, 0, 0);
1879 back
->special(backhandle
, TS_AYT
);
1880 net_pending_errors();
1883 back
->special(backhandle
, TS_BRK
);
1884 net_pending_errors();
1887 back
->special(backhandle
, TS_SYNCH
);
1888 net_pending_errors();
1891 back
->special(backhandle
, TS_EC
);
1892 net_pending_errors();
1895 back
->special(backhandle
, TS_EL
);
1896 net_pending_errors();
1899 back
->special(backhandle
, TS_GA
);
1900 net_pending_errors();
1903 back
->special(backhandle
, TS_NOP
);
1904 net_pending_errors();
1907 back
->special(backhandle
, TS_ABORT
);
1908 net_pending_errors();
1911 back
->special(backhandle
, TS_AO
);
1912 net_pending_errors();
1915 back
->special(backhandle
, TS_IP
);
1916 net_pending_errors();
1919 back
->special(backhandle
, TS_SUSP
);
1920 net_pending_errors();
1923 back
->special(backhandle
, TS_EOR
);
1924 net_pending_errors();
1927 back
->special(backhandle
, TS_EOF
);
1928 net_pending_errors();
1934 WinHelp(hwnd
, help_path
,
1935 help_has_contents ? HELP_FINDER
: HELP_CONTENTS
, 0);
1939 * We get this if the System menu has been activated
1946 * We get this if the System menu has been activated
1947 * using the keyboard. This might happen from within
1948 * TranslateKey, in which case it really wants to be
1949 * followed by a `space' character to actually _bring
1950 * the menu up_ rather than just sitting there in
1951 * `ready to appear' state.
1953 show_mouseptr(1); /* make sure pointer is visible */
1955 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1957 case IDM_FULLSCREEN
:
1961 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1962 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1967 #define X_POS(l) ((int)(short)LOWORD(l))
1968 #define Y_POS(l) ((int)(short)HIWORD(l))
1970 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1971 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1972 case WM_LBUTTONDOWN
:
1973 case WM_MBUTTONDOWN
:
1974 case WM_RBUTTONDOWN
:
1982 case WM_LBUTTONDOWN
:
1986 case WM_MBUTTONDOWN
:
1987 button
= MBT_MIDDLE
;
1990 case WM_RBUTTONDOWN
:
1999 button
= MBT_MIDDLE
;
2007 button
= press
= 0; /* shouldn't happen */
2011 * Special case: in full-screen mode, if the left
2012 * button is clicked in the very top left corner of the
2013 * window, we put up the System menu instead of doing
2016 if (is_full_screen() && press
&& button
== MBT_LEFT
&&
2017 X_POS(lParam
) == 0 && Y_POS(lParam
) == 0) {
2018 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_MOUSEMENU
, 0);
2023 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
2024 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
,
2028 term_mouse(term
, button
, MA_RELEASE
,
2029 TO_CHR_X(X_POS(lParam
)),
2030 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
2031 wParam
& MK_CONTROL
, is_alt_pressed());
2039 * Add the mouse position and message time to the random
2042 noise_ultralight(lParam
);
2044 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
) &&
2045 GetCapture() == hwnd
) {
2047 if (wParam
& MK_LBUTTON
)
2049 else if (wParam
& MK_MBUTTON
)
2053 term_mouse(term
, b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
2054 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
2055 wParam
& MK_CONTROL
, is_alt_pressed());
2058 case WM_NCMOUSEMOVE
:
2060 noise_ultralight(lParam
);
2062 case WM_IGNORE_CLIP
:
2063 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
2065 case WM_DESTROYCLIPBOARD
:
2067 term_deselect(term
);
2068 ignore_clip
= FALSE
;
2074 hdc
= BeginPaint(hwnd
, &p
);
2076 SelectPalette(hdc
, pal
, TRUE
);
2077 RealizePalette(hdc
);
2079 term_paint(term
, hdc
,
2080 (p
.rcPaint
.left
-offset_width
)/font_width
,
2081 (p
.rcPaint
.top
-offset_height
)/font_height
,
2082 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
2083 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
2086 p
.rcPaint
.left
< offset_width
||
2087 p
.rcPaint
.top
< offset_height
||
2088 p
.rcPaint
.right
>= offset_width
+ font_width
*term
->cols
||
2089 p
.rcPaint
.bottom
>= offset_height
+ font_height
*term
->rows
)
2091 HBRUSH fillcolour
, oldbrush
;
2093 fillcolour
= CreateSolidBrush (
2094 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
2095 oldbrush
= SelectObject(hdc
, fillcolour
);
2096 edge
= CreatePen(PS_SOLID
, 0,
2097 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
2098 oldpen
= SelectObject(hdc
, edge
);
2101 * Jordan Russell reports that this apparently
2102 * ineffectual IntersectClipRect() call masks a
2103 * Windows NT/2K bug causing strange display
2104 * problems when the PuTTY window is taller than
2105 * the primary monitor. It seems harmless enough...
2107 IntersectClipRect(hdc
,
2108 p
.rcPaint
.left
, p
.rcPaint
.top
,
2109 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
2111 ExcludeClipRect(hdc
,
2112 offset_width
, offset_height
,
2113 offset_width
+font_width
*term
->cols
,
2114 offset_height
+font_height
*term
->rows
);
2116 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
2117 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
2119 // SelectClipRgn(hdc, NULL);
2121 SelectObject(hdc
, oldbrush
);
2122 DeleteObject(fillcolour
);
2123 SelectObject(hdc
, oldpen
);
2126 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
2127 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
2133 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2134 * but the only one that's likely to try to overload us is FD_READ.
2135 * This means buffering just one is fine.
2137 if (pending_netevent
)
2138 enact_pending_netevent();
2140 pending_netevent
= TRUE
;
2141 pend_netevent_wParam
= wParam
;
2142 pend_netevent_lParam
= lParam
;
2143 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
2144 enact_pending_netevent();
2146 time(&last_movement
);
2149 term
->has_focus
= TRUE
;
2150 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
2152 flash_window(0); /* stop */
2159 term
->has_focus
= FALSE
;
2161 caret_x
= caret_y
= -1; /* ensure caret is replaced next time */
2165 case WM_ENTERSIZEMOVE
:
2166 #ifdef RDB_DEBUG_PATCH
2167 debug((27, "WM_ENTERSIZEMOVE"));
2171 need_backend_resize
= FALSE
;
2173 case WM_EXITSIZEMOVE
:
2176 #ifdef RDB_DEBUG_PATCH
2177 debug((27, "WM_EXITSIZEMOVE"));
2179 if (need_backend_resize
) {
2180 term_size(term
, cfg
.height
, cfg
.width
, cfg
.savelines
);
2181 InvalidateRect(hwnd
, NULL
, TRUE
);
2186 * This does two jobs:
2187 * 1) Keep the sizetip uptodate
2188 * 2) Make sure the window size is _stepped_ in units of the font size.
2190 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2191 int width
, height
, w
, h
, ew
, eh
;
2192 LPRECT r
= (LPRECT
) lParam
;
2194 if ( !need_backend_resize
&& cfg
.resize_action
== RESIZE_EITHER
&&
2195 (cfg
.height
!= term
->rows
|| cfg
.width
!= term
->cols
)) {
2197 * Great! It seems that both the terminal size and the
2198 * font size have been changed and the user is now dragging.
2200 * It will now be difficult to get back to the configured
2203 * This would be easier but it seems to be too confusing.
2205 term_size(term, cfg.height, cfg.width, cfg.savelines);
2208 cfg
.height
=term
->rows
; cfg
.width
=term
->cols
;
2210 InvalidateRect(hwnd
, NULL
, TRUE
);
2211 need_backend_resize
= TRUE
;
2214 width
= r
->right
- r
->left
- extra_width
;
2215 height
= r
->bottom
- r
->top
- extra_height
;
2216 w
= (width
+ font_width
/ 2) / font_width
;
2219 h
= (height
+ font_height
/ 2) / font_height
;
2222 UpdateSizeTip(hwnd
, w
, h
);
2223 ew
= width
- w
* font_width
;
2224 eh
= height
- h
* font_height
;
2226 if (wParam
== WMSZ_LEFT
||
2227 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2233 if (wParam
== WMSZ_TOP
||
2234 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2244 int width
, height
, w
, h
, rv
= 0;
2245 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
2246 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2247 LPRECT r
= (LPRECT
) lParam
;
2249 width
= r
->right
- r
->left
- ex_width
;
2250 height
= r
->bottom
- r
->top
- ex_height
;
2252 w
= (width
+ term
->cols
/2)/term
->cols
;
2253 h
= (height
+ term
->rows
/2)/term
->rows
;
2254 if ( r
->right
!= r
->left
+ w
*term
->cols
+ ex_width
)
2257 if (wParam
== WMSZ_LEFT
||
2258 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2259 r
->left
= r
->right
- w
*term
->cols
- ex_width
;
2261 r
->right
= r
->left
+ w
*term
->cols
+ ex_width
;
2263 if (r
->bottom
!= r
->top
+ h
*term
->rows
+ ex_height
)
2266 if (wParam
== WMSZ_TOP
||
2267 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2268 r
->top
= r
->bottom
- h
*term
->rows
- ex_height
;
2270 r
->bottom
= r
->top
+ h
*term
->rows
+ ex_height
;
2274 /* break; (never reached) */
2275 case WM_FULLSCR_ON_MAX
:
2276 fullscr_on_max
= TRUE
;
2279 sys_cursor_update();
2282 #ifdef RDB_DEBUG_PATCH
2283 debug((27, "WM_SIZE %s (%d,%d)",
2284 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2285 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2286 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2287 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2289 LOWORD(lParam
), HIWORD(lParam
)));
2291 if (wParam
== SIZE_MINIMIZED
)
2293 cfg
.win_name_always ? window_name
: icon_name
);
2294 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2295 SetWindowText(hwnd
, window_name
);
2296 if (wParam
== SIZE_RESTORED
)
2297 clear_full_screen();
2298 if (wParam
== SIZE_MAXIMIZED
&& fullscr_on_max
) {
2299 fullscr_on_max
= FALSE
;
2303 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2304 /* A resize, well it better be a minimize. */
2308 int width
, height
, w
, h
;
2310 width
= LOWORD(lParam
);
2311 height
= HIWORD(lParam
);
2314 if (wParam
== SIZE_MAXIMIZED
&& !was_zoomed
) {
2316 prev_rows
= term
->rows
;
2317 prev_cols
= term
->cols
;
2318 if (cfg
.resize_action
== RESIZE_TERM
) {
2319 w
= width
/ font_width
;
2321 h
= height
/ font_height
;
2324 term_size(term
, h
, w
, cfg
.savelines
);
2327 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2329 if (cfg
.resize_action
== RESIZE_TERM
)
2330 term_size(term
, prev_rows
, prev_cols
, cfg
.savelines
);
2331 if (cfg
.resize_action
!= RESIZE_FONT
)
2336 /* This is an unexpected resize, these will normally happen
2337 * if the window is too large. Probably either the user
2338 * selected a huge font or the screen size has changed.
2340 * This is also called with minimize.
2342 else reset_window(-1);
2346 * Don't call back->size in mid-resize. (To prevent
2347 * massive numbers of resize events getting sent
2348 * down the connection during an NT opaque drag.)
2351 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2352 need_backend_resize
= TRUE
;
2353 w
= (width
-cfg
.window_border
*2) / font_width
;
2355 h
= (height
-cfg
.window_border
*2) / font_height
;
2364 sys_cursor_update();
2367 switch (LOWORD(wParam
)) {
2369 term_scroll(term
, -1, 0);
2372 term_scroll(term
, +1, 0);
2375 term_scroll(term
, 0, +1);
2378 term_scroll(term
, 0, -1);
2381 term_scroll(term
, 0, +term
->rows
/ 2);
2384 term_scroll(term
, 0, -term
->rows
/ 2);
2386 case SB_THUMBPOSITION
:
2388 term_scroll(term
, 1, HIWORD(wParam
));
2392 case WM_PALETTECHANGED
:
2393 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2394 HDC hdc
= get_ctx();
2396 if (RealizePalette(hdc
) > 0)
2402 case WM_QUERYNEWPALETTE
:
2404 HDC hdc
= get_ctx();
2406 if (RealizePalette(hdc
) > 0)
2418 * Add the scan code and keypress timing to the random
2421 noise_ultralight(lParam
);
2424 * We don't do TranslateMessage since it disassociates the
2425 * resulting CHAR message from the KEYDOWN that sparked it,
2426 * which we occasionally don't want. Instead, we process
2427 * KEYDOWN, and call the Win32 translator functions so that
2428 * we get the translations under _our_ control.
2431 unsigned char buf
[20];
2434 if (wParam
== VK_PROCESSKEY
) {
2437 m
.message
= WM_KEYDOWN
;
2439 m
.lParam
= lParam
& 0xdfff;
2440 TranslateMessage(&m
);
2442 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2444 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2448 * Interrupt an ongoing paste. I'm not sure
2449 * this is sensible, but for the moment it's
2450 * preferable to having to faff about buffering
2456 * We need not bother about stdin backlogs
2457 * here, because in GUI PuTTY we can't do
2458 * anything about it anyway; there's no means
2459 * of asking Windows to hold off on KEYDOWN
2460 * messages. We _have_ to buffer everything
2463 term_seen_key_event(term
);
2464 ldisc_send(ldisc
, buf
, len
, 1);
2469 net_pending_errors();
2471 case WM_INPUTLANGCHANGE
:
2472 /* wParam == Font number */
2473 /* lParam == Locale */
2474 set_input_locale((HKL
)lParam
);
2475 sys_cursor_update();
2478 if(wParam
== IMN_SETOPENSTATUS
) {
2479 HIMC hImc
= ImmGetContext(hwnd
);
2480 ImmSetCompositionFont(hImc
, &lfont
);
2481 ImmReleaseContext(hwnd
, hImc
);
2485 case WM_IME_COMPOSITION
:
2491 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2492 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2494 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2495 break; /* fall back to DefWindowProc */
2497 hIMC
= ImmGetContext(hwnd
);
2498 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2502 buff
= (char*) smalloc(n
);
2503 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2505 * Jaeyoun Chung reports that Korean character
2506 * input doesn't work correctly if we do a single
2507 * luni_send() covering the whole of buff. So
2508 * instead we luni_send the characters one by one.
2510 term_seen_key_event(term
);
2511 for (i
= 0; i
< n
; i
+= 2) {
2512 luni_send(ldisc
, (unsigned short *)(buff
+i
), 1, 1);
2516 ImmReleaseContext(hwnd
, hIMC
);
2521 if (wParam
& 0xFF00) {
2522 unsigned char buf
[2];
2525 buf
[0] = wParam
>> 8;
2526 term_seen_key_event(term
);
2527 lpage_send(ldisc
, kbd_codepage
, buf
, 2, 1);
2529 char c
= (unsigned char) wParam
;
2530 term_seen_key_event(term
);
2531 lpage_send(ldisc
, kbd_codepage
, &c
, 1, 1);
2537 * Nevertheless, we are prepared to deal with WM_CHAR
2538 * messages, should they crop up. So if someone wants to
2539 * post the things to us as part of a macro manoeuvre,
2540 * we're ready to cope.
2543 char c
= (unsigned char)wParam
;
2544 term_seen_key_event(term
);
2545 lpage_send(ldisc
, CP_ACP
, &c
, 1, 1);
2549 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2550 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2554 if (message
== wm_mousewheel
|| message
== WM_MOUSEWHEEL
) {
2555 int shift_pressed
=0, control_pressed
=0, alt_pressed
=0;
2557 if (message
== WM_MOUSEWHEEL
) {
2558 wheel_accumulator
+= (short)HIWORD(wParam
);
2559 shift_pressed
=LOWORD(wParam
) & MK_SHIFT
;
2560 control_pressed
=LOWORD(wParam
) & MK_CONTROL
;
2563 wheel_accumulator
+= (int)wParam
;
2564 if (GetKeyboardState(keys
)!=0) {
2565 shift_pressed
=keys
[VK_SHIFT
]&0x80;
2566 control_pressed
=keys
[VK_CONTROL
]&0x80;
2570 /* process events when the threshold is reached */
2571 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
2574 /* reduce amount for next time */
2575 if (wheel_accumulator
> 0) {
2577 wheel_accumulator
-= WHEEL_DELTA
;
2578 } else if (wheel_accumulator
< 0) {
2580 wheel_accumulator
+= WHEEL_DELTA
;
2584 if (send_raw_mouse
&&
2585 !(cfg
.mouse_override
&& shift_pressed
)) {
2586 /* send a mouse-down followed by a mouse up */
2589 TO_CHR_X(X_POS(lParam
)),
2590 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2591 control_pressed
, is_alt_pressed());
2592 term_mouse(term
, b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
2593 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2594 control_pressed
, is_alt_pressed());
2596 /* trigger a scroll */
2597 term_scroll(term
, 0,
2599 -term
->rows
/ 2 : term
->rows
/ 2);
2606 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2610 * Move the system caret. (We maintain one, even though it's
2611 * invisible, for the benefit of blind people: apparently some
2612 * helper software tracks the system caret, so we should arrange to
2615 void sys_cursor(int x
, int y
)
2619 if (!term
->has_focus
) return;
2622 * Avoid gratuitously re-updating the cursor position and IMM
2623 * window if there's no actual change required.
2625 cx
= x
* font_width
+ offset_width
;
2626 cy
= y
* font_height
+ offset_height
;
2627 if (cx
== caret_x
&& cy
== caret_y
)
2632 sys_cursor_update();
2635 static void sys_cursor_update(void)
2640 if (!term
->has_focus
) return;
2642 if (caret_x
< 0 || caret_y
< 0)
2645 SetCaretPos(caret_x
, caret_y
);
2647 /* IMM calls on Win98 and beyond only */
2648 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2650 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2651 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2653 /* we should have the IMM functions */
2654 hIMC
= ImmGetContext(hwnd
);
2655 cf
.dwStyle
= CFS_POINT
;
2656 cf
.ptCurrentPos
.x
= caret_x
;
2657 cf
.ptCurrentPos
.y
= caret_y
;
2658 ImmSetCompositionWindow(hIMC
, &cf
);
2660 ImmReleaseContext(hwnd
, hIMC
);
2664 * Draw a line of text in the window, at given character
2665 * coordinates, in given attributes.
2667 * We are allowed to fiddle with the contents of `text'.
2669 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2670 unsigned long attr
, int lattr
)
2673 int nfg
, nbg
, nfont
;
2676 int force_manual_underline
= 0;
2677 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2678 int char_width
= fnt_width
;
2679 int text_adjust
= 0;
2680 static int *IpDx
= 0, IpDxLEN
= 0;
2682 if (attr
& ATTR_WIDE
)
2685 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2687 if (len
> IpDxLEN
) {
2689 IpDx
= smalloc((len
+ 16) * sizeof(int));
2690 IpDxLEN
= (len
+ 16);
2692 for (i
= 0; i
< IpDxLEN
; i
++)
2693 IpDx
[i
] = char_width
;
2696 /* Only want the left half of double width lines */
2697 if (lattr
!= LATTR_NORM
&& x
*2 >= term
->cols
)
2705 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || term
->big_cursor
)) {
2706 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2707 attr
^= ATTR_CUR_XOR
;
2711 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2712 /* Assume a poorman font is borken in other ways too. */
2722 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2725 if (attr
& ATTR_NARROW
)
2726 nfont
|= FONT_NARROW
;
2728 /* Special hack for the VT100 linedraw glyphs. */
2729 if ((attr
& CSET_MASK
) == 0x2300) {
2730 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2731 switch ((unsigned char) (text
[0])) {
2733 text_adjust
= -2 * font_height
/ 5;
2736 text_adjust
= -1 * font_height
/ 5;
2739 text_adjust
= font_height
/ 5;
2742 text_adjust
= 2 * font_height
/ 5;
2745 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2748 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2749 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2750 if (attr
& ATTR_UNDER
) {
2751 attr
&= ~ATTR_UNDER
;
2752 force_manual_underline
= 1;
2757 /* Anything left as an original character set is unprintable. */
2758 if (DIRECT_CHAR(attr
)) {
2761 memset(text
, 0xFD, len
);
2765 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2768 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2769 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2770 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2772 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2773 nfont
|= FONT_UNDERLINE
;
2774 another_font(nfont
);
2775 if (!fonts
[nfont
]) {
2776 if (nfont
& FONT_UNDERLINE
)
2777 force_manual_underline
= 1;
2778 /* Don't do the same for manual bold, it could be bad news. */
2780 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2782 another_font(nfont
);
2784 nfont
= FONT_NORMAL
;
2785 if (attr
& ATTR_REVERSE
) {
2790 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2792 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2796 SelectObject(hdc
, fonts
[nfont
]);
2797 SetTextColor(hdc
, fg
);
2798 SetBkColor(hdc
, bg
);
2799 SetBkMode(hdc
, OPAQUE
);
2802 line_box
.right
= x
+ char_width
* len
;
2803 line_box
.bottom
= y
+ font_height
;
2805 /* Only want the left half of double width lines */
2806 if (line_box
.right
> font_width
*term
->cols
+offset_width
)
2807 line_box
.right
= font_width
*term
->cols
+offset_width
;
2809 /* We're using a private area for direct to font. (512 chars.) */
2810 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2811 /* Ho Hum, dbcs fonts are a PITA! */
2812 /* To display on W9x I have to convert to UCS */
2813 static wchar_t *uni_buf
= 0;
2814 static int uni_len
= 0;
2816 if (len
> uni_len
) {
2818 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2821 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2822 uni_buf
[nlen
] = 0xFFFD;
2823 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2824 IpDx
[nlen
] += char_width
;
2825 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2826 text
+mptr
, 2, uni_buf
+nlen
, 1);
2831 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2832 text
+mptr
, 1, uni_buf
+nlen
, 1);
2840 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2841 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2842 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2843 SetBkMode(hdc
, TRANSPARENT
);
2844 ExtTextOutW(hdc
, x
- 1,
2845 y
- font_height
* (lattr
==
2846 LATTR_BOT
) + text_adjust
,
2847 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2851 } else if (DIRECT_FONT(attr
)) {
2853 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2854 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2855 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2856 SetBkMode(hdc
, TRANSPARENT
);
2858 /* GRR: This draws the character outside it's box and can leave
2859 * 'droppings' even with the clip box! I suppose I could loop it
2860 * one character at a time ... yuk.
2862 * Or ... I could do a test print with "W", and use +1 or -1 for this
2863 * shift depending on if the leftmost column is blank...
2865 ExtTextOut(hdc
, x
- 1,
2866 y
- font_height
* (lattr
==
2867 LATTR_BOT
) + text_adjust
,
2868 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2871 /* And 'normal' unicode characters */
2872 static WCHAR
*wbuf
= NULL
;
2873 static int wlen
= 0;
2878 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2880 for (i
= 0; i
< len
; i
++)
2881 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2884 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2885 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2887 /* And the shadow bold hack. */
2888 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2889 SetBkMode(hdc
, TRANSPARENT
);
2890 ExtTextOutW(hdc
, x
- 1,
2891 y
- font_height
* (lattr
==
2892 LATTR_BOT
) + text_adjust
,
2893 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2896 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2897 (und_mode
== UND_LINE
2898 && (attr
& ATTR_UNDER
)))) {
2901 if (lattr
== LATTR_BOT
)
2902 dec
= dec
* 2 - font_height
;
2904 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2905 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2906 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2907 oldpen
= SelectObject(hdc
, oldpen
);
2908 DeleteObject(oldpen
);
2912 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2913 unsigned long attr
, int lattr
)
2919 int ctype
= cfg
.cursor_type
;
2921 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || term
->big_cursor
)) {
2922 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2923 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2927 attr
|= TATTR_RIGHTCURS
;
2930 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2931 if (attr
& ATTR_WIDE
)
2938 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || term
->big_cursor
)) {
2941 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2942 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2943 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2944 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2945 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2946 Polyline(hdc
, pts
, 5);
2947 oldpen
= SelectObject(hdc
, oldpen
);
2948 DeleteObject(oldpen
);
2949 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2950 int startx
, starty
, dx
, dy
, length
, i
;
2953 starty
= y
+ descent
;
2956 length
= char_width
;
2959 if (attr
& TATTR_RIGHTCURS
)
2960 xadjust
= char_width
- 1;
2961 startx
= x
+ xadjust
;
2965 length
= font_height
;
2967 if (attr
& TATTR_ACTCURS
) {
2970 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2971 MoveToEx(hdc
, startx
, starty
, NULL
);
2972 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2973 oldpen
= SelectObject(hdc
, oldpen
);
2974 DeleteObject(oldpen
);
2976 for (i
= 0; i
< length
; i
++) {
2978 SetPixel(hdc
, startx
, starty
, colours
[23]);
2987 /* This function gets the actual width of a character in the normal font.
2989 int CharWidth(Context ctx
, int uc
) {
2993 /* If the font max is the same as the font ave width then this
2994 * function is a no-op.
2996 if (!font_dualwidth
) return 1;
2998 switch (uc
& CSET_MASK
) {
3000 uc
= unitab_line
[uc
& 0xFF];
3003 uc
= unitab_xterm
[uc
& 0xFF];
3006 uc
= unitab_scoacs
[uc
& 0xFF];
3009 if (DIRECT_FONT(uc
)) {
3010 if (dbcs_screenfont
) return 1;
3012 /* Speedup, I know of no font where ascii is the wrong width */
3013 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
3016 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
3017 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
3018 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
3019 another_font(FONT_OEM
);
3020 if (!fonts
[FONT_OEM
]) return 0;
3022 SelectObject(hdc
, fonts
[FONT_OEM
]);
3026 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
3027 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
3030 /* Speedup, I know of no font where ascii is the wrong width */
3031 if (uc
>= ' ' && uc
<= '~') return 1;
3033 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
3034 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
3035 /* Okay that one worked */ ;
3036 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
3037 /* This should work on 9x too, but it's "less accurate" */ ;
3042 ibuf
+= font_width
/ 2 -1;
3049 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
3050 * codes. Returns number of bytes used or zero to drop the message
3051 * or -1 to forward the message to windows.
3053 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
3054 unsigned char *output
)
3057 int scan
, left_alt
= 0, key_down
, shift_state
;
3059 unsigned char *p
= output
;
3060 static int alt_sum
= 0;
3062 HKL kbd_layout
= GetKeyboardLayout(0);
3064 static WORD keys
[3];
3065 static int compose_char
= 0;
3066 static WPARAM compose_key
= 0;
3068 r
= GetKeyboardState(keystate
);
3070 memset(keystate
, 0, sizeof(keystate
));
3073 #define SHOW_TOASCII_RESULT
3074 { /* Tell us all about key events */
3075 static BYTE oldstate
[256];
3076 static int first
= 1;
3080 memcpy(oldstate
, keystate
, sizeof(oldstate
));
3083 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
3085 } else if ((HIWORD(lParam
) & KF_UP
)
3086 && scan
== (HIWORD(lParam
) & 0xFF)) {
3090 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
3091 debug(("K_F%d", wParam
+ 1 - VK_F1
));
3104 debug(("VK_%02x", wParam
));
3106 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
3108 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
3110 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
3111 if (ch
>= ' ' && ch
<= '~')
3112 debug((", '%c'", ch
));
3114 debug((", $%02x", ch
));
3117 debug((", KB0=%02x", keys
[0]));
3119 debug((", KB1=%02x", keys
[1]));
3121 debug((", KB2=%02x", keys
[2]));
3123 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
3125 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
3127 if ((HIWORD(lParam
) & KF_EXTENDED
))
3129 if ((HIWORD(lParam
) & KF_UP
))
3133 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
3134 else if ((HIWORD(lParam
) & KF_UP
))
3135 oldstate
[wParam
& 0xFF] ^= 0x80;
3137 oldstate
[wParam
& 0xFF] ^= 0x81;
3139 for (ch
= 0; ch
< 256; ch
++)
3140 if (oldstate
[ch
] != keystate
[ch
])
3141 debug((", M%02x=%02x", ch
, keystate
[ch
]));
3143 memcpy(oldstate
, keystate
, sizeof(oldstate
));
3147 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
3148 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
3152 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
3153 if ((cfg
.funky_type
== 3 ||
3154 (cfg
.funky_type
<= 1 && term
->app_keypad_keys
&&
3156 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
3158 wParam
= VK_EXECUTE
;
3160 /* UnToggle NUMLock */
3161 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
3162 keystate
[VK_NUMLOCK
] ^= 1;
3165 /* And write back the 'adjusted' state */
3166 SetKeyboardState(keystate
);
3169 /* Disable Auto repeat if required */
3170 if (term
->repeat_off
&&
3171 (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
3174 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
3177 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
3179 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
3180 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
3181 if (cfg
.ctrlaltkeys
)
3182 keystate
[VK_MENU
] = 0;
3184 keystate
[VK_RMENU
] = 0x80;
3189 alt_pressed
= (left_alt
&& key_down
);
3191 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
3192 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
3193 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
3195 /* Note if AltGr was pressed and if it was used as a compose key */
3196 if (!compose_state
) {
3197 compose_key
= 0x100;
3198 if (cfg
.compose_key
) {
3199 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
3200 compose_key
= wParam
;
3202 if (wParam
== VK_APPS
)
3203 compose_key
= wParam
;
3206 if (wParam
== compose_key
) {
3207 if (compose_state
== 0
3208 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
3210 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
3214 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
3217 if (compose_state
> 1 && left_alt
)
3220 /* Sanitize the number pad if not using a PC NumPad */
3221 if (left_alt
|| (term
->app_keypad_keys
&& !cfg
.no_applic_k
3222 && cfg
.funky_type
!= 2)
3223 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
3224 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
3228 nParam
= VK_NUMPAD0
;
3231 nParam
= VK_NUMPAD1
;
3234 nParam
= VK_NUMPAD2
;
3237 nParam
= VK_NUMPAD3
;
3240 nParam
= VK_NUMPAD4
;
3243 nParam
= VK_NUMPAD5
;
3246 nParam
= VK_NUMPAD6
;
3249 nParam
= VK_NUMPAD7
;
3252 nParam
= VK_NUMPAD8
;
3255 nParam
= VK_NUMPAD9
;
3258 nParam
= VK_DECIMAL
;
3262 if (keystate
[VK_NUMLOCK
] & 1)
3269 /* If a key is pressed and AltGr is not active */
3270 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
3271 /* Okay, prepare for most alts then ... */
3275 /* Lets see if it's a pattern we know all about ... */
3276 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
3277 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
3280 if (wParam
== VK_NEXT
&& shift_state
== 1) {
3281 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
3284 if (wParam
== VK_INSERT
&& shift_state
== 1) {
3285 term_do_paste(term
);
3288 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
3291 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
3292 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
3295 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
&&
3296 (cfg
.resize_action
!= RESIZE_DISABLED
)) {
3297 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) != KF_REPEAT
)
3301 /* Control-Numlock for app-keypad mode switch */
3302 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
3303 term
->app_keypad_keys
^= 1;
3307 /* Nethack keypad */
3308 if (cfg
.nethack_keypad
&& !left_alt
) {
3311 *p
++ = shift_state ?
'B' : 'b';
3314 *p
++ = shift_state ?
'J' : 'j';
3317 *p
++ = shift_state ?
'N' : 'n';
3320 *p
++ = shift_state ?
'H' : 'h';
3323 *p
++ = shift_state ?
'.' : '.';
3326 *p
++ = shift_state ?
'L' : 'l';
3329 *p
++ = shift_state ?
'Y' : 'y';
3332 *p
++ = shift_state ?
'K' : 'k';
3335 *p
++ = shift_state ?
'U' : 'u';
3340 /* Application Keypad */
3344 if (cfg
.funky_type
== 3 ||
3345 (cfg
.funky_type
<= 1 &&
3346 term
->app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3360 if (term
->app_keypad_keys
&& !cfg
.no_applic_k
)
3397 if (cfg
.funky_type
== 2) {
3402 } else if (shift_state
)
3409 if (cfg
.funky_type
== 2)
3413 if (cfg
.funky_type
== 2)
3417 if (cfg
.funky_type
== 2)
3422 if (HIWORD(lParam
) & KF_EXTENDED
)
3427 if (term
->vt52_mode
) {
3428 if (xkey
>= 'P' && xkey
<= 'S')
3429 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3431 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3433 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3438 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3439 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3443 if (wParam
== VK_BACK
&& shift_state
== 1) { /* Shift Backspace */
3444 /* We do the opposite of what is configured */
3445 *p
++ = (cfg
.bksp_is_delete ?
0x08 : 0x7F);
3449 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3455 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3459 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3463 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3468 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3473 /* Control-2 to Control-8 are special */
3474 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3475 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3478 if (shift_state
== 2 && wParam
== 0xBD) {
3482 if (shift_state
== 2 && wParam
== 0xDF) {
3486 if (shift_state
== 0 && wParam
== VK_RETURN
&& term
->cr_lf_return
) {
3493 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3494 * for integer decimal nn.)
3496 * We also deal with the weird ones here. Linux VCs replace F1
3497 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3498 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3504 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3507 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3510 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3513 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3516 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3519 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3522 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3525 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3528 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3531 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3564 if ((shift_state
&2) == 0) switch (wParam
) {
3584 /* Reorder edit keys to physical order */
3585 if (cfg
.funky_type
== 3 && code
<= 6)
3586 code
= "\0\2\1\4\5\3\6"[code
];
3588 if (term
->vt52_mode
&& code
> 0 && code
<= 6) {
3589 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3593 if (cfg
.funky_type
== 5 && /* SCO function keys */
3594 code
>= 11 && code
<= 34) {
3595 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3598 case VK_F1
: index
= 0; break;
3599 case VK_F2
: index
= 1; break;
3600 case VK_F3
: index
= 2; break;
3601 case VK_F4
: index
= 3; break;
3602 case VK_F5
: index
= 4; break;
3603 case VK_F6
: index
= 5; break;
3604 case VK_F7
: index
= 6; break;
3605 case VK_F8
: index
= 7; break;
3606 case VK_F9
: index
= 8; break;
3607 case VK_F10
: index
= 9; break;
3608 case VK_F11
: index
= 10; break;
3609 case VK_F12
: index
= 11; break;
3611 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3612 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3613 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3616 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3617 code
>= 1 && code
<= 6) {
3618 char codes
[] = "HL.FIG";
3622 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3626 if ((term
->vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3632 if (term
->vt52_mode
)
3633 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3636 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3639 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3640 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3643 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3644 if (term
->vt52_mode
)
3645 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3647 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3650 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3651 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3655 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3660 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3661 * some reason seems to send VK_CLEAR to Windows...).
3683 if (term
->vt52_mode
)
3684 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3686 int app_flg
= (term
->app_cursor_keys
&& !cfg
.no_applic_c
);
3689 * RDB: VT100 & VT102 manuals both state the
3690 * app cursor keys only work if the app keypad
3693 * SGT: That may well be true, but xterm
3694 * disagrees and so does at least one
3695 * application, so I've #if'ed this out and the
3696 * behaviour is back to PuTTY's original: app
3697 * cursor and app keypad are independently
3698 * switchable modes. If anyone complains about
3699 * _this_ I'll have to put in a configurable
3702 if (!term
->app_keypad_keys
)
3705 /* Useful mapping of Ctrl-arrows */
3706 if (shift_state
== 2)
3710 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3712 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3719 * Finally, deal with Return ourselves. (Win95 seems to
3720 * foul it up when Alt is pressed, for some reason.)
3722 if (wParam
== VK_RETURN
) { /* Return */
3728 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3729 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3734 /* Okay we've done everything interesting; let windows deal with
3735 * the boring stuff */
3739 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3740 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3742 keystate
[VK_CAPITAL
] = 0;
3745 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3746 #ifdef SHOW_TOASCII_RESULT
3747 if (r
== 1 && !key_down
) {
3749 if (in_utf(term
) || dbcs_screenfont
)
3750 debug((", (U+%04x)", alt_sum
));
3752 debug((", LCH(%d)", alt_sum
));
3754 debug((", ACH(%d)", keys
[0]));
3759 for (r1
= 0; r1
< r
; r1
++) {
3760 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3769 * Interrupt an ongoing paste. I'm not sure this is
3770 * sensible, but for the moment it's preferable to
3771 * having to faff about buffering things.
3776 for (i
= 0; i
< r
; i
++) {
3777 unsigned char ch
= (unsigned char) keys
[i
];
3779 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3784 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3788 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3789 MessageBeep(MB_ICONHAND
);
3793 term_seen_key_event(term
);
3794 luni_send(ldisc
, &keybuf
, 1, 1);
3802 if (in_utf(term
) || dbcs_screenfont
) {
3804 term_seen_key_event(term
);
3805 luni_send(ldisc
, &keybuf
, 1, 1);
3807 ch
= (char) alt_sum
;
3809 * We need not bother about stdin
3810 * backlogs here, because in GUI PuTTY
3811 * we can't do anything about it
3812 * anyway; there's no means of asking
3813 * Windows to hold off on KEYDOWN
3814 * messages. We _have_ to buffer
3815 * everything we're sent.
3817 term_seen_key_event(term
);
3818 ldisc_send(ldisc
, &ch
, 1, 1);
3822 term_seen_key_event(term
);
3823 lpage_send(ldisc
, kbd_codepage
, &ch
, 1, 1);
3825 if(capsOn
&& ch
< 0x80) {
3828 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3829 term_seen_key_event(term
);
3830 luni_send(ldisc
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3835 term_seen_key_event(term
);
3836 lpage_send(ldisc
, kbd_codepage
,
3837 cbuf
+!left_alt
, 1+!!left_alt
, 1);
3843 /* This is so the ALT-Numpad and dead keys work correctly. */
3848 /* If we're definitly not building up an ALT-54321 then clear it */
3851 /* If we will be using alt_sum fix the 256s */
3852 else if (keys
[0] && (in_utf(term
) || dbcs_screenfont
))
3857 * ALT alone may or may not want to bring up the System menu.
3858 * If it's not meant to, we return 0 on presses or releases of
3859 * ALT, to show that we've swallowed the keystroke. Otherwise
3860 * we return -1, which means Windows will give the keystroke
3861 * its default handling (i.e. bring up the System menu).
3863 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3869 void request_paste(void)
3872 * In Windows, pasting is synchronous: we can read the
3873 * clipboard with no difficulty, so request_paste() can just go
3876 term_do_paste(term
);
3879 void set_title(char *title
)
3882 window_name
= smalloc(1 + strlen(title
));
3883 strcpy(window_name
, title
);
3884 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3885 SetWindowText(hwnd
, title
);
3888 void set_icon(char *title
)
3891 icon_name
= smalloc(1 + strlen(title
));
3892 strcpy(icon_name
, title
);
3893 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3894 SetWindowText(hwnd
, title
);
3897 void set_sbar(int total
, int start
, int page
)
3901 if (is_full_screen() ?
!cfg
.scrollbar_in_fullscreen
: !cfg
.scrollbar
)
3904 si
.cbSize
= sizeof(si
);
3905 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3907 si
.nMax
= total
- 1;
3911 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3914 Context
get_ctx(void)
3920 SelectPalette(hdc
, pal
, FALSE
);
3926 void free_ctx(Context ctx
)
3928 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3929 ReleaseDC(hwnd
, ctx
);
3932 static void real_palette_set(int n
, int r
, int g
, int b
)
3935 logpal
->palPalEntry
[n
].peRed
= r
;
3936 logpal
->palPalEntry
[n
].peGreen
= g
;
3937 logpal
->palPalEntry
[n
].peBlue
= b
;
3938 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3939 colours
[n
] = PALETTERGB(r
, g
, b
);
3940 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3942 colours
[n
] = RGB(r
, g
, b
);
3945 void palette_set(int n
, int r
, int g
, int b
)
3947 static const int first
[21] = {
3948 0, 2, 4, 6, 8, 10, 12, 14,
3949 1, 3, 5, 7, 9, 11, 13, 15,
3952 real_palette_set(first
[n
], r
, g
, b
);
3954 real_palette_set(first
[n
] + 1, r
, g
, b
);
3956 HDC hdc
= get_ctx();
3957 UnrealizeObject(pal
);
3958 RealizePalette(hdc
);
3963 void palette_reset(void)
3967 for (i
= 0; i
< NCOLOURS
; i
++) {
3969 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3970 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3971 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3972 logpal
->palPalEntry
[i
].peFlags
= 0;
3973 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3974 defpal
[i
].rgbtGreen
,
3975 defpal
[i
].rgbtBlue
);
3977 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3978 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3983 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3985 RealizePalette(hdc
);
3990 void write_aclip(char *data
, int len
, int must_deselect
)
3995 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3998 lock
= GlobalLock(clipdata
);
4001 memcpy(lock
, data
, len
);
4002 ((unsigned char *) lock
)[len
] = 0;
4003 GlobalUnlock(clipdata
);
4006 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
4008 if (OpenClipboard(hwnd
)) {
4010 SetClipboardData(CF_TEXT
, clipdata
);
4013 GlobalFree(clipdata
);
4016 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
4020 * Note: unlike write_aclip() this will not append a nul.
4022 void write_clip(wchar_t * data
, int len
, int must_deselect
)
4024 HGLOBAL clipdata
, clipdata2
, clipdata3
;
4026 void *lock
, *lock2
, *lock3
;
4028 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
4030 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
4031 len
* sizeof(wchar_t));
4032 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
4034 if (!clipdata
|| !clipdata2
) {
4036 GlobalFree(clipdata
);
4038 GlobalFree(clipdata2
);
4041 if (!(lock
= GlobalLock(clipdata
)))
4043 if (!(lock2
= GlobalLock(clipdata2
)))
4046 memcpy(lock
, data
, len
* sizeof(wchar_t));
4047 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
4049 if (cfg
.rtf_paste
) {
4050 wchar_t unitab
[256];
4052 unsigned char *tdata
= (unsigned char *)lock2
;
4053 wchar_t *udata
= (wchar_t *)lock
;
4054 int rtflen
= 0, uindex
= 0, tindex
= 0;
4056 int multilen
, blen
, alen
, totallen
, i
;
4057 char before
[16], after
[4];
4059 get_unitab(CP_ACP
, unitab
, 0);
4061 rtfsize
= 100 + strlen(cfg
.font
);
4062 rtf
= smalloc(rtfsize
);
4063 sprintf(rtf
, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
4064 GetACP(), cfg
.font
);
4065 rtflen
= strlen(rtf
);
4068 * We want to construct a piece of RTF that specifies the
4069 * same Unicode text. To do this we will read back in
4070 * parallel from the Unicode data in `udata' and the
4071 * non-Unicode data in `tdata'. For each character in
4072 * `tdata' which becomes the right thing in `udata' when
4073 * looked up in `unitab', we just copy straight over from
4074 * tdata. For each one that doesn't, we must WCToMB it
4075 * individually and produce a \u escape sequence.
4077 * It would probably be more robust to just bite the bullet
4078 * and WCToMB each individual Unicode character one by one,
4079 * then MBToWC each one back to see if it was an accurate
4080 * translation; but that strikes me as a horrifying number
4081 * of Windows API calls so I want to see if this faster way
4082 * will work. If it screws up badly we can always revert to
4083 * the simple and slow way.
4085 while (tindex
< len2
&& uindex
< len
&&
4086 tdata
[tindex
] && udata
[uindex
]) {
4087 if (tindex
+ 1 < len2
&&
4088 tdata
[tindex
] == '\r' &&
4089 tdata
[tindex
+1] == '\n') {
4093 if (unitab
[tdata
[tindex
]] == udata
[uindex
]) {
4099 multilen
= WideCharToMultiByte(CP_ACP
, 0, unitab
+uindex
, 1,
4100 NULL
, 0, NULL
, NULL
);
4101 if (multilen
!= 1) {
4102 blen
= sprintf(before
, "{\\uc%d\\u%d", multilen
,
4104 alen
= 1; strcpy(after
, "}");
4106 blen
= sprintf(before
, "\\u%d", udata
[uindex
]);
4107 alen
= 0; after
[0] = '\0';
4110 assert(tindex
+ multilen
<= len2
);
4111 totallen
= blen
+ alen
;
4112 for (i
= 0; i
< multilen
; i
++) {
4113 if (tdata
[tindex
+i
] == '\\' ||
4114 tdata
[tindex
+i
] == '{' ||
4115 tdata
[tindex
+i
] == '}')
4117 else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A)
4118 totallen
+= 6; /* \par\r\n */
4119 else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20)
4125 if (rtfsize
< rtflen
+ totallen
+ 3) {
4126 rtfsize
= rtflen
+ totallen
+ 512;
4127 rtf
= srealloc(rtf
, rtfsize
);
4130 strcpy(rtf
+ rtflen
, before
); rtflen
+= blen
;
4131 for (i
= 0; i
< multilen
; i
++) {
4132 if (tdata
[tindex
+i
] == '\\' ||
4133 tdata
[tindex
+i
] == '{' ||
4134 tdata
[tindex
+i
] == '}') {
4135 rtf
[rtflen
++] = '\\';
4136 rtf
[rtflen
++] = tdata
[tindex
+i
];
4137 } else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A) {
4138 rtflen
+= sprintf(rtf
+rtflen
, "\\par\r\n");
4139 } else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20) {
4140 rtflen
+= sprintf(rtf
+rtflen
, "\\'%02x", tdata
[tindex
+i
]);
4142 rtf
[rtflen
++] = tdata
[tindex
+i
];
4145 strcpy(rtf
+ rtflen
, after
); rtflen
+= alen
;
4151 strcpy(rtf
+ rtflen
, "}");
4154 clipdata3
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, rtflen
);
4155 if (clipdata3
&& (lock3
= GlobalLock(clipdata3
)) != NULL
) {
4157 GlobalUnlock(clipdata3
);
4163 GlobalUnlock(clipdata
);
4164 GlobalUnlock(clipdata2
);
4167 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
4169 if (OpenClipboard(hwnd
)) {
4171 SetClipboardData(CF_UNICODETEXT
, clipdata
);
4172 SetClipboardData(CF_TEXT
, clipdata2
);
4174 SetClipboardData(RegisterClipboardFormat(CF_RTF
), clipdata3
);
4177 GlobalFree(clipdata
);
4178 GlobalFree(clipdata2
);
4182 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
4185 void get_clip(wchar_t ** p
, int *len
)
4187 static HGLOBAL clipdata
= NULL
;
4188 static wchar_t *converted
= 0;
4197 GlobalUnlock(clipdata
);
4200 } else if (OpenClipboard(NULL
)) {
4201 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
4203 *p
= GlobalLock(clipdata
);
4205 for (p2
= *p
; *p2
; p2
++);
4209 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
4213 s
= GlobalLock(clipdata
);
4214 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
4215 *p
= converted
= smalloc(i
* sizeof(wchar_t));
4216 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
4229 * Move `lines' lines from position `from' to position `to' in the
4232 void optimised_move(int to
, int from
, int lines
)
4237 min
= (to
< from ? to
: from
);
4238 max
= to
+ from
- min
;
4240 r
.left
= offset_width
;
4241 r
.right
= offset_width
+ term
->cols
* font_width
;
4242 r
.top
= offset_height
+ min
* font_height
;
4243 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
4244 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
4249 * Print a message box and perform a fatal exit.
4251 void fatalbox(char *fmt
, ...)
4257 vsprintf(stuff
, fmt
, ap
);
4259 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
4264 * Print a modal (Really Bad) message box and perform a fatal exit.
4266 void modalfatalbox(char *fmt
, ...)
4272 vsprintf(stuff
, fmt
, ap
);
4274 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error",
4275 MB_SYSTEMMODAL
| MB_ICONERROR
| MB_OK
);
4280 * Manage window caption / taskbar flashing, if enabled.
4281 * 0 = stop, 1 = maintain, 2 = start
4283 static void flash_window(int mode
)
4285 static long last_flash
= 0;
4286 static int flashing
= 0;
4287 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
4290 FlashWindow(hwnd
, FALSE
);
4294 } else if (mode
== 2) {
4297 last_flash
= GetTickCount();
4299 FlashWindow(hwnd
, TRUE
);
4302 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
4305 long now
= GetTickCount();
4306 long fdiff
= now
- last_flash
;
4307 if (fdiff
< 0 || fdiff
> 450) {
4309 FlashWindow(hwnd
, TRUE
); /* toggle */
4320 if (mode
== BELL_DEFAULT
) {
4322 * For MessageBeep style bells, we want to be careful of
4323 * timing, because they don't have the nice property of
4324 * PlaySound bells that each one cancels the previous
4325 * active one. So we limit the rate to one per 50ms or so.
4327 static long lastbeep
= 0;
4330 beepdiff
= GetTickCount() - lastbeep
;
4331 if (beepdiff
>= 0 && beepdiff
< 50)
4335 * The above MessageBeep call takes time, so we record the
4336 * time _after_ it finishes rather than before it starts.
4338 lastbeep
= GetTickCount();
4339 } else if (mode
== BELL_WAVEFILE
) {
4340 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
4341 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
4342 sprintf(buf
, "Unable to play sound file\n%s\n"
4343 "Using default sound instead", cfg
.bell_wavefile
);
4344 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
4345 MB_OK
| MB_ICONEXCLAMATION
);
4346 cfg
.beep
= BELL_DEFAULT
;
4349 /* Otherwise, either visual bell or disabled; do nothing here */
4350 if (!term
->has_focus
) {
4351 flash_window(2); /* start */
4356 * Minimise or restore the window in response to a server-side
4359 void set_iconic(int iconic
)
4361 if (IsIconic(hwnd
)) {
4363 ShowWindow(hwnd
, SW_RESTORE
);
4366 ShowWindow(hwnd
, SW_MINIMIZE
);
4371 * Move the window in response to a server-side request.
4373 void move_window(int x
, int y
)
4375 if (cfg
.resize_action
== RESIZE_DISABLED
||
4376 cfg
.resize_action
== RESIZE_FONT
||
4380 SetWindowPos(hwnd
, NULL
, x
, y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
);
4384 * Move the window to the top or bottom of the z-order in response
4385 * to a server-side request.
4387 void set_zorder(int top
)
4389 if (cfg
.alwaysontop
)
4390 return; /* ignore */
4391 SetWindowPos(hwnd
, top ? HWND_TOP
: HWND_BOTTOM
, 0, 0, 0, 0,
4392 SWP_NOMOVE
| SWP_NOSIZE
);
4396 * Refresh the window in response to a server-side request.
4398 void refresh_window(void)
4400 InvalidateRect(hwnd
, NULL
, TRUE
);
4404 * Maximise or restore the window in response to a server-side
4407 void set_zoomed(int zoomed
)
4409 if (IsZoomed(hwnd
)) {
4411 ShowWindow(hwnd
, SW_RESTORE
);
4414 ShowWindow(hwnd
, SW_MAXIMIZE
);
4419 * Report whether the window is iconic, for terminal reports.
4423 return IsIconic(hwnd
);
4427 * Report the window's position, for terminal reports.
4429 void get_window_pos(int *x
, int *y
)
4432 GetWindowRect(hwnd
, &r
);
4438 * Report the window's pixel size, for terminal reports.
4440 void get_window_pixels(int *x
, int *y
)
4443 GetWindowRect(hwnd
, &r
);
4444 *x
= r
.right
- r
.left
;
4445 *y
= r
.bottom
- r
.top
;
4449 * Return the window or icon title.
4451 char *get_window_title(int icon
)
4453 return icon ? icon_name
: window_name
;
4457 * See if we're in full-screen mode.
4459 int is_full_screen()
4461 if (!IsZoomed(hwnd
))
4463 if (GetWindowLong(hwnd
, GWL_STYLE
) & WS_CAPTION
)
4468 /* Get the rect/size of a full screen window using the nearest available
4469 * monitor in multimon systems; default to something sensible if only
4470 * one monitor is present. */
4471 static int get_fullscreen_rect(RECT
* ss
)
4473 #ifdef MONITOR_DEFAULTTONEAREST
4476 mon
= MonitorFromWindow(hwnd
, MONITOR_DEFAULTTONEAREST
);
4477 mi
.cbSize
= sizeof(mi
);
4478 GetMonitorInfo(mon
, &mi
);
4480 /* structure copy */
4484 /* could also use code like this:
4485 ss->left = ss->top = 0;
4486 ss->right = GetSystemMetrics(SM_CXSCREEN);
4487 ss->bottom = GetSystemMetrics(SM_CYSCREEN);
4489 return GetClientRect(GetDesktopWindow(), ss
);
4495 * Go full-screen. This should only be called when we are already
4498 void make_full_screen()
4503 assert(IsZoomed(hwnd
));
4505 if (is_full_screen())
4508 /* Remove the window furniture. */
4509 style
= GetWindowLong(hwnd
, GWL_STYLE
);
4510 style
&= ~(WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
);
4511 if (cfg
.scrollbar_in_fullscreen
)
4512 style
|= WS_VSCROLL
;
4514 style
&= ~WS_VSCROLL
;
4515 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4517 /* Resize ourselves to exactly cover the nearest monitor. */
4518 get_fullscreen_rect(&ss
);
4519 SetWindowPos(hwnd
, HWND_TOP
, ss
.left
, ss
.top
,
4524 /* Tick the menu item in the System menu. */
4525 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4530 * Clear the full-screen attributes.
4532 void clear_full_screen()
4534 DWORD oldstyle
, style
;
4536 /* Reinstate the window furniture. */
4537 style
= oldstyle
= GetWindowLong(hwnd
, GWL_STYLE
);
4538 style
|= WS_CAPTION
| WS_BORDER
;
4539 if (cfg
.resize_action
== RESIZE_DISABLED
)
4540 style
&= ~WS_THICKFRAME
;
4542 style
|= WS_THICKFRAME
;
4544 style
|= WS_VSCROLL
;
4546 style
&= ~WS_VSCROLL
;
4547 if (style
!= oldstyle
) {
4548 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4549 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
4550 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
4554 /* Untick the menu item in the System menu. */
4555 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4560 * Toggle full-screen mode.
4562 void flip_full_screen()
4564 if (is_full_screen()) {
4565 ShowWindow(hwnd
, SW_RESTORE
);
4566 } else if (IsZoomed(hwnd
)) {
4569 SendMessage(hwnd
, WM_FULLSCR_ON_MAX
, 0, 0);
4570 ShowWindow(hwnd
, SW_MAXIMIZE
);
4574 void frontend_keypress(void *handle
)
4577 * Keypress termination in non-Close-On-Exit mode is not
4578 * currently supported in PuTTY proper, because the window
4579 * always has a perfectly good Close button anyway. So we do