16 #define COMPILE_MULTIMON_STUBS
27 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
33 #define IDM_SHOWLOG 0x0010
34 #define IDM_NEWSESS 0x0020
35 #define IDM_DUPSESS 0x0030
36 #define IDM_RECONF 0x0040
37 #define IDM_CLRSB 0x0050
38 #define IDM_RESET 0x0060
39 #define IDM_TEL_AYT 0x0070
40 #define IDM_TEL_BRK 0x0080
41 #define IDM_TEL_SYNCH 0x0090
42 #define IDM_TEL_EC 0x00a0
43 #define IDM_TEL_EL 0x00b0
44 #define IDM_TEL_GA 0x00c0
45 #define IDM_TEL_NOP 0x00d0
46 #define IDM_TEL_ABORT 0x00e0
47 #define IDM_TEL_AO 0x00f0
48 #define IDM_TEL_IP 0x0100
49 #define IDM_TEL_SUSP 0x0110
50 #define IDM_TEL_EOR 0x0120
51 #define IDM_TEL_EOF 0x0130
52 #define IDM_HELP 0x0140
53 #define IDM_ABOUT 0x0150
54 #define IDM_SAVEDSESS 0x0160
55 #define IDM_COPYALL 0x0170
56 #define IDM_FULLSCREEN 0x0180
58 #define IDM_SESSLGP 0x0250 /* log type printable */
59 #define IDM_SESSLGA 0x0260 /* log type all chars */
60 #define IDM_SESSLGE 0x0270 /* log end */
61 #define IDM_SAVED_MIN 0x1000
62 #define IDM_SAVED_MAX 0x2000
64 #define WM_IGNORE_CLIP (WM_XUSER + 2)
65 #define WM_FULLSCR_ON_MAX (WM_XUSER + 3)
67 /* Needed for Chinese support and apparently not always defined. */
69 #define VK_PROCESSKEY 0xE5
72 /* Mouse wheel support. */
74 #define WM_MOUSEWHEEL 0x020A /* not defined in earlier SDKs */
77 #define WHEEL_DELTA 120
80 static LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
81 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
82 unsigned char *output
);
83 static void cfgtopalette(void);
84 static void init_palette(void);
85 static void init_fonts(int, int);
86 static void another_font(int);
87 static void deinit_fonts(void);
88 static void set_input_locale(HKL
);
89 static int do_mouse_wheel_msg(UINT message
, WPARAM wParam
, LPARAM lParam
);
91 static int is_full_screen(void);
92 static void make_full_screen(void);
93 static void clear_full_screen(void);
94 static void flip_full_screen(void);
96 /* Window layout information */
97 static void reset_window(int);
98 static int extra_width
, extra_height
;
99 static int font_width
, font_height
, font_dualwidth
;
100 static int offset_width
, offset_height
;
101 static int was_zoomed
= 0;
102 static int prev_rows
, prev_cols
;
104 static int pending_netevent
= 0;
105 static WPARAM pend_netevent_wParam
= 0;
106 static LPARAM pend_netevent_lParam
= 0;
107 static void enact_pending_netevent(void);
108 static void flash_window(int mode
);
109 static void sys_cursor_update(void);
111 static time_t last_movement
= 0;
113 static int caret_x
= -1, caret_y
= -1;
115 #define FONT_NORMAL 0
117 #define FONT_UNDERLINE 2
118 #define FONT_BOLDUND 3
119 #define FONT_WIDE 0x04
120 #define FONT_HIGH 0x08
121 #define FONT_NARROW 0x10
123 #define FONT_OEM 0x20
124 #define FONT_OEMBOLD 0x21
125 #define FONT_OEMUND 0x22
126 #define FONT_OEMBOLDUND 0x23
128 #define FONT_MAXNO 0x2F
130 static HFONT fonts
[FONT_MAXNO
];
131 static LOGFONT lfont
;
132 static int fontflag
[FONT_MAXNO
];
134 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
142 static COLORREF colours
[NCOLOURS
];
144 static LPLOGPALETTE logpal
;
145 static RGBTRIPLE defpal
[NCOLOURS
];
149 static HBITMAP caretbm
;
151 static int dbltime
, lasttime
, lastact
;
152 static Mouse_Button lastbtn
;
154 /* this allows xterm-style mouse handling. */
155 static int send_raw_mouse
= 0;
156 static int wheel_accumulator
= 0;
158 static char *window_name
, *icon_name
;
160 static int compose_state
= 0;
162 static int wsa_started
= 0;
164 static OSVERSIONINFO osVersion
;
166 static UINT wm_mousewheel
= WM_MOUSEWHEEL
;
168 /* Dummy routine, only required in plink. */
169 void ldisc_update(int echo
, int edit
)
173 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
175 static char appname
[] = "PuTTY";
180 int guess_width
, guess_height
;
183 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
185 winsock_ver
= MAKEWORD(1, 1);
186 if (WSAStartup(winsock_ver
, &wsadata
)) {
187 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
188 MB_OK
| MB_ICONEXCLAMATION
);
191 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
192 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
193 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
198 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
201 InitCommonControls();
203 /* Ensure a Maximize setting in Explorer doesn't maximise the
208 ZeroMemory(&osVersion
, sizeof(osVersion
));
209 osVersion
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
210 if (!GetVersionEx ( (OSVERSIONINFO
*) &osVersion
)) {
211 MessageBox(NULL
, "Windows refuses to report a version",
212 "PuTTY Fatal Error", MB_OK
| MB_ICONEXCLAMATION
);
218 * If we're running a version of Windows that doesn't support
219 * WM_MOUSEWHEEL, find out what message number we should be
222 if (osVersion
.dwMajorVersion
< 4 ||
223 (osVersion
.dwMajorVersion
== 4 &&
224 osVersion
.dwPlatformId
!= VER_PLATFORM_WIN32_NT
))
225 wm_mousewheel
= RegisterWindowMessage("MSWHEEL_ROLLMSG");
228 * See if we can find our Help file.
231 char b
[2048], *p
, *q
, *r
;
233 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
235 p
= strrchr(b
, '\\');
236 if (p
&& p
>= r
) r
= p
+1;
238 if (q
&& q
>= r
) r
= q
+1;
239 strcpy(r
, "putty.hlp");
240 if ( (fp
= fopen(b
, "r")) != NULL
) {
241 help_path
= dupstr(b
);
245 strcpy(r
, "putty.cnt");
246 if ( (fp
= fopen(b
, "r")) != NULL
) {
247 help_has_contents
= TRUE
;
250 help_has_contents
= FALSE
;
254 * Process the command line.
259 default_protocol
= DEFAULT_PROTOCOL
;
260 default_port
= DEFAULT_PORT
;
261 cfg
.logtype
= LGTYP_NONE
;
263 do_defaults(NULL
, &cfg
);
266 while (*p
&& isspace(*p
))
270 * Process command line options first. Yes, this can be
271 * done better, and it will be as soon as I have the
275 char *q
= p
+ strcspn(p
, " \t");
278 tolower(p
[0]) == 's' &&
279 tolower(p
[1]) == 's' && tolower(p
[2]) == 'h') {
280 default_protocol
= cfg
.protocol
= PROT_SSH
;
281 default_port
= cfg
.port
= 22;
282 } else if (q
== p
+ 7 &&
283 tolower(p
[0]) == 'c' &&
284 tolower(p
[1]) == 'l' &&
285 tolower(p
[2]) == 'e' &&
286 tolower(p
[3]) == 'a' &&
287 tolower(p
[4]) == 'n' &&
288 tolower(p
[5]) == 'u' && tolower(p
[6]) == 'p') {
290 * `putty -cleanup'. Remove all registry entries
291 * associated with PuTTY, and also find and delete
292 * the random seed file.
295 "This procedure will remove ALL Registry\n"
296 "entries associated with PuTTY, and will\n"
297 "also remove the PuTTY random seed file.\n"
299 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
300 "SESSIONS. Are you really sure you want\n"
303 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
308 p
= q
+ strspn(q
, " \t");
312 * An initial @ means to activate a saved session.
316 while (i
> 1 && isspace(p
[i
- 1]))
319 do_defaults(p
+ 1, &cfg
);
320 if (!*cfg
.host
&& !do_config()) {
324 } else if (*p
== '&') {
326 * An initial & means we've been given a command line
327 * containing the hex value of a HANDLE for a file
328 * mapping object, which we must then extract as a
333 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
334 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
335 0, 0, sizeof(Config
))) != NULL
) {
338 CloseHandle(filemap
);
339 } else if (!do_config()) {
346 * If the hostname starts with "telnet:", set the
347 * protocol to Telnet and process the string as a
350 if (!strncmp(q
, "telnet:", 7)) {
354 if (q
[0] == '/' && q
[1] == '/')
356 cfg
.protocol
= PROT_TELNET
;
358 while (*p
&& *p
!= ':' && *p
!= '/')
367 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
368 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
370 while (*p
&& !isspace(*p
))
374 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
375 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
376 while (*p
&& isspace(*p
))
391 * Trim leading whitespace off the hostname if it's there.
394 int space
= strspn(cfg
.host
, " \t");
395 memmove(cfg
.host
, cfg
.host
+space
, 1+strlen(cfg
.host
)-space
);
398 /* See if host is of the form user@host */
399 if (cfg
.host
[0] != '\0') {
400 char *atsign
= strchr(cfg
.host
, '@');
401 /* Make sure we're not overflowing the user field */
403 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
404 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
405 cfg
.username
[atsign
- cfg
.host
] = '\0';
407 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
412 * Trim a colon suffix off the hostname if it's there.
414 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
418 * Select protocol. This is farmed out into a table in a
419 * separate file to enable an ssh-free variant.
424 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
425 if (backends
[i
].protocol
== cfg
.protocol
) {
426 back
= backends
[i
].backend
;
430 MessageBox(NULL
, "Unsupported protocol number found",
431 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
437 /* Check for invalid Port number (i.e. zero) */
439 MessageBox(NULL
, "Invalid Port Number",
440 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
447 wndclass
.lpfnWndProc
= WndProc
;
448 wndclass
.cbClsExtra
= 0;
449 wndclass
.cbWndExtra
= 0;
450 wndclass
.hInstance
= inst
;
451 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
452 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
453 wndclass
.hbrBackground
= NULL
;
454 wndclass
.lpszMenuName
= NULL
;
455 wndclass
.lpszClassName
= appname
;
457 RegisterClass(&wndclass
);
462 savelines
= cfg
.savelines
;
468 * Guess some defaults for the window size. This all gets
469 * updated later, so we don't really care too much. However, we
470 * do want the font width/height guesses to correspond to a
471 * large font rather than a small one...
478 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
479 guess_width
= extra_width
+ font_width
* cols
;
480 guess_height
= extra_height
+ font_height
* rows
;
483 HWND w
= GetDesktopWindow();
484 GetWindowRect(w
, &r
);
485 if (guess_width
> r
.right
- r
.left
)
486 guess_width
= r
.right
- r
.left
;
487 if (guess_height
> r
.bottom
- r
.top
)
488 guess_height
= r
.bottom
- r
.top
;
492 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
495 winmode
&= ~(WS_VSCROLL
);
496 if (cfg
.resize_action
== RESIZE_DISABLED
)
497 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
499 exwinmode
|= WS_EX_TOPMOST
;
501 exwinmode
|= WS_EX_CLIENTEDGE
;
502 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
503 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
504 guess_width
, guess_height
,
505 NULL
, NULL
, inst
, NULL
);
509 * Initialise the fonts, simultaneously correcting the guesses
510 * for font_{width,height}.
515 * Correct the guesses for extra_{width,height}.
519 GetWindowRect(hwnd
, &wr
);
520 GetClientRect(hwnd
, &cr
);
521 offset_width
= offset_height
= cfg
.window_border
;
522 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
523 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
527 * Resize the window, now we know what size we _really_ want it
530 guess_width
= extra_width
+ font_width
* cols
;
531 guess_height
= extra_height
+ font_height
* rows
;
532 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
533 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
536 * Set up a caret bitmap, with no content.
540 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
541 bits
= smalloc(size
);
542 memset(bits
, 0, size
);
543 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
546 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
549 * Initialise the scroll bar.
554 si
.cbSize
= sizeof(si
);
555 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
560 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
564 * Start up the telnet connection.
568 char msg
[1024], *title
;
571 error
= back
->init(cfg
.host
, cfg
.port
, &realhost
, cfg
.tcp_nodelay
);
573 sprintf(msg
, "Unable to open connection to\n"
574 "%.800s\n" "%s", cfg
.host
, error
);
575 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
578 window_name
= icon_name
= NULL
;
580 title
= cfg
.wintitle
;
582 sprintf(msg
, "%s - PuTTY", realhost
);
590 session_closed
= FALSE
;
593 * Prepare the mouse handler.
595 lastact
= MA_NOTHING
;
596 lastbtn
= MBT_NOTHING
;
597 dbltime
= GetDoubleClickTime();
600 * Set up the session-control options on the system menu.
603 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
607 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
608 if (cfg
.protocol
== PROT_TELNET
) {
610 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
611 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
612 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
613 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
614 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
615 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
616 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
617 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
618 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
619 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
620 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
621 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
622 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
623 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
624 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
625 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
626 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
628 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
630 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
631 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
632 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
633 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
636 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
637 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
639 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
640 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
641 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
642 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
643 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
644 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
645 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
646 AppendMenu(m
, (cfg
.resize_action
== RESIZE_DISABLED
) ?
647 MF_GRAYED
: MF_ENABLED
, IDM_FULLSCREEN
, "&Full Screen");
648 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
650 AppendMenu(m
, MF_ENABLED
, IDM_HELP
, "&Help");
651 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
655 * Set up the initial input locale.
657 set_input_locale(GetKeyboardLayout(0));
660 * Open the initial log file if there is one.
665 * Finally show the window!
667 ShowWindow(hwnd
, show
);
668 SetForegroundWindow(hwnd
);
671 * Set the palette up.
677 has_focus
= (GetForegroundWindow() == hwnd
);
680 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
681 int timer_id
= 0, long_timer
= 0;
683 while (msg
.message
!= WM_QUIT
) {
684 /* Sometimes DispatchMessage calls routines that use their own
685 * GetMessage loop, setup this timer so we get some control back.
687 * Also call term_update() from the timer so that if the host
688 * is sending data flat out we still do redraws.
690 if (timer_id
&& long_timer
) {
691 KillTimer(hwnd
, timer_id
);
692 long_timer
= timer_id
= 0;
695 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
696 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
697 DispatchMessage(&msg
);
699 /* Make sure we blink everything that needs it. */
702 /* Send the paste buffer if there's anything to send */
705 /* If there's nothing new in the queue then we can do everything
706 * we've delayed, reading the socket, writing, and repainting
709 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
712 if (pending_netevent
) {
713 enact_pending_netevent();
715 /* Force the cursor blink on */
718 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
722 /* Okay there is now nothing to do so we make sure the screen is
723 * completely up to date then tell windows to call us in a little
727 KillTimer(hwnd
, timer_id
);
731 if (GetCapture() != hwnd
||
732 (send_raw_mouse
&& !(cfg
.mouse_override
&& is_shift_pressed())))
737 flash_window(1); /* maintain */
739 /* The messages seem unreliable; especially if we're being tricky */
740 has_focus
= (GetForegroundWindow() == hwnd
);
743 /* Hmm, term_update didn't want to do an update too soon ... */
744 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
746 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
748 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
751 /* There's no point rescanning everything in the message queue
752 * so we do an apparently unnecessary wait here
755 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
760 cleanup_exit(msg
.wParam
); /* this doesn't return... */
761 return msg
.wParam
; /* ... but optimiser doesn't know */
767 void cleanup_exit(int code
)
780 if (cfg
.protocol
== PROT_SSH
) {
791 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
793 char *do_select(SOCKET skt
, int startup
)
798 events
= (FD_CONNECT
| FD_READ
| FD_WRITE
|
799 FD_OOB
| FD_CLOSE
| FD_ACCEPT
);
804 return "do_select(): internal error (hwnd==NULL)";
805 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
806 switch (WSAGetLastError()) {
808 return "Network is down";
810 return "WSAAsyncSelect(): unknown error";
817 * set or clear the "raw mouse message" mode
819 void set_raw_mouse_mode(int activate
)
821 activate
= activate
&& !cfg
.no_mouse_rep
;
822 send_raw_mouse
= activate
;
823 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
827 * Print a message box and close the connection.
829 void connection_fatal(char *fmt
, ...)
835 vsprintf(stuff
, fmt
, ap
);
837 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
838 if (cfg
.close_on_exit
== COE_ALWAYS
)
841 session_closed
= TRUE
;
842 SetWindowText(hwnd
, "PuTTY (inactive)");
847 * Actually do the job requested by a WM_NETEVENT
849 static void enact_pending_netevent(void)
851 static int reentering
= 0;
852 extern int select_result(WPARAM
, LPARAM
);
856 return; /* don't unpend the pending */
858 pending_netevent
= FALSE
;
861 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
864 if (ret
== 0 && !session_closed
) {
865 /* Abnormal exits will already have set session_closed and taken
866 * appropriate action. */
867 if (cfg
.close_on_exit
== COE_ALWAYS
||
868 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
870 session_closed
= TRUE
;
871 SetWindowText(hwnd
, "PuTTY (inactive)");
872 MessageBox(hwnd
, "Connection closed by remote host",
873 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
879 * Copy the colour palette from the configuration data into defpal.
880 * This is non-trivial because the colour indices are different.
882 static void cfgtopalette(void)
885 static const int ww
[] = {
886 6, 7, 8, 9, 10, 11, 12, 13,
887 14, 15, 16, 17, 18, 19, 20, 21,
888 0, 1, 2, 3, 4, 4, 5, 5
891 for (i
= 0; i
< 24; i
++) {
893 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
894 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
895 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
900 * Set up the colour palette.
902 static void init_palette(void)
905 HDC hdc
= GetDC(hwnd
);
907 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
908 logpal
= smalloc(sizeof(*logpal
)
909 - sizeof(logpal
->palPalEntry
)
910 + NCOLOURS
* sizeof(PALETTEENTRY
));
911 logpal
->palVersion
= 0x300;
912 logpal
->palNumEntries
= NCOLOURS
;
913 for (i
= 0; i
< NCOLOURS
; i
++) {
914 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
915 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
916 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
917 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
919 pal
= CreatePalette(logpal
);
921 SelectPalette(hdc
, pal
, FALSE
);
923 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
926 ReleaseDC(hwnd
, hdc
);
929 for (i
= 0; i
< NCOLOURS
; i
++)
930 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
934 for (i
= 0; i
< NCOLOURS
; i
++)
935 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
936 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
940 * Initialise all the fonts we will need initially. There may be as many as
941 * three or as few as one. The other (poentially) twentyone fonts are done
942 * if/when they are needed.
946 * - check the font width and height, correcting our guesses if
949 * - verify that the bold font is the same width as the ordinary
950 * one, and engage shadow bolding if not.
952 * - verify that the underlined font is the same width as the
953 * ordinary one (manual underlining by means of line drawing can
954 * be done in a pinch).
956 static void init_fonts(int pick_width
, int pick_height
)
963 int fw_dontcare
, fw_bold
;
965 for (i
= 0; i
< FONT_MAXNO
; i
++)
968 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
971 if (cfg
.fontisbold
) {
972 fw_dontcare
= FW_BOLD
;
975 fw_dontcare
= FW_DONTCARE
;
982 font_height
= pick_height
;
984 font_height
= cfg
.fontheight
;
985 if (font_height
> 0) {
987 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
990 font_width
= pick_width
;
993 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
994 c, OUT_DEFAULT_PRECIS, \
995 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
996 FIXED_PITCH | FF_DONTCARE, cfg.font)
998 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
1000 lfont
.lfHeight
= font_height
;
1001 lfont
.lfWidth
= font_width
;
1002 lfont
.lfEscapement
= 0;
1003 lfont
.lfOrientation
= 0;
1004 lfont
.lfWeight
= fw_dontcare
;
1005 lfont
.lfItalic
= FALSE
;
1006 lfont
.lfUnderline
= FALSE
;
1007 lfont
.lfStrikeOut
= FALSE
;
1008 lfont
.lfCharSet
= cfg
.fontcharset
;
1009 lfont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
1010 lfont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
1011 lfont
.lfQuality
= DEFAULT_QUALITY
;
1012 lfont
.lfPitchAndFamily
= FIXED_PITCH
| FF_DONTCARE
;
1013 strncpy(lfont
.lfFaceName
, cfg
.font
, LF_FACESIZE
);
1015 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
1016 GetTextMetrics(hdc
, &tm
);
1018 if (pick_width
== 0 || pick_height
== 0) {
1019 font_height
= tm
.tmHeight
;
1020 font_width
= tm
.tmAveCharWidth
;
1022 font_dualwidth
= (tm
.tmAveCharWidth
!= tm
.tmMaxCharWidth
);
1024 #ifdef RDB_DEBUG_PATCH
1025 debug(23, "Primary font H=%d, AW=%d, MW=%d",
1026 tm
.tmHeight
, tm
.tmAveCharWidth
, tm
.tmMaxCharWidth
);
1031 DWORD cset
= tm
.tmCharSet
;
1032 memset(&info
, 0xFF, sizeof(info
));
1034 /* !!! Yes the next line is right */
1035 if (cset
== OEM_CHARSET
)
1036 font_codepage
= GetOEMCP();
1038 if (TranslateCharsetInfo
1039 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
1044 GetCPInfo(font_codepage
, &cpinfo
);
1045 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
1048 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
1051 * Some fonts, e.g. 9-pt Courier, draw their underlines
1052 * outside their character cell. We successfully prevent
1053 * screen corruption by clipping the text output, but then
1054 * we lose the underline completely. Here we try to work
1055 * out whether this is such a font, and if it is, we set a
1056 * flag that causes underlines to be drawn by hand.
1058 * Having tried other more sophisticated approaches (such
1059 * as examining the TEXTMETRIC structure or requesting the
1060 * height of a string), I think we'll do this the brute
1061 * force way: we create a small bitmap, draw an underlined
1062 * space on it, and test to see whether any pixels are
1063 * foreground-coloured. (Since we expect the underline to
1064 * go all the way across the character cell, we only search
1065 * down a single column of the bitmap, half way across.)
1069 HBITMAP und_bm
, und_oldbm
;
1073 und_dc
= CreateCompatibleDC(hdc
);
1074 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
1075 und_oldbm
= SelectObject(und_dc
, und_bm
);
1076 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
1077 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
1078 SetTextColor(und_dc
, RGB(255, 255, 255));
1079 SetBkColor(und_dc
, RGB(0, 0, 0));
1080 SetBkMode(und_dc
, OPAQUE
);
1081 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
1083 for (i
= 0; i
< font_height
; i
++) {
1084 c
= GetPixel(und_dc
, font_width
/ 2, i
);
1085 if (c
!= RGB(0, 0, 0))
1088 SelectObject(und_dc
, und_oldbm
);
1089 DeleteObject(und_bm
);
1092 und_mode
= UND_LINE
;
1093 DeleteObject(fonts
[FONT_UNDERLINE
]);
1094 fonts
[FONT_UNDERLINE
] = 0;
1098 if (bold_mode
== BOLD_FONT
) {
1099 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
1103 descent
= tm
.tmAscent
+ 1;
1104 if (descent
>= font_height
)
1105 descent
= font_height
- 1;
1107 for (i
= 0; i
< 3; i
++) {
1109 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
1110 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
1117 ReleaseDC(hwnd
, hdc
);
1119 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1120 und_mode
= UND_LINE
;
1121 DeleteObject(fonts
[FONT_UNDERLINE
]);
1122 fonts
[FONT_UNDERLINE
] = 0;
1125 if (bold_mode
== BOLD_FONT
&&
1126 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1127 bold_mode
= BOLD_SHADOW
;
1128 DeleteObject(fonts
[FONT_BOLD
]);
1129 fonts
[FONT_BOLD
] = 0;
1131 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1136 static void another_font(int fontno
)
1139 int fw_dontcare
, fw_bold
;
1143 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1146 basefont
= (fontno
& ~(FONT_BOLDUND
));
1147 if (basefont
!= fontno
&& !fontflag
[basefont
])
1148 another_font(basefont
);
1150 if (cfg
.fontisbold
) {
1151 fw_dontcare
= FW_BOLD
;
1154 fw_dontcare
= FW_DONTCARE
;
1158 c
= cfg
.fontcharset
;
1164 if (fontno
& FONT_WIDE
)
1166 if (fontno
& FONT_NARROW
)
1168 if (fontno
& FONT_OEM
)
1170 if (fontno
& FONT_BOLD
)
1172 if (fontno
& FONT_UNDERLINE
)
1176 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1177 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1178 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1179 FIXED_PITCH
| FF_DONTCARE
, s
);
1181 fontflag
[fontno
] = 1;
1184 static void deinit_fonts(void)
1187 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1189 DeleteObject(fonts
[i
]);
1195 void request_resize(int w
, int h
)
1199 /* If the window is maximized supress resizing attempts */
1200 if (IsZoomed(hwnd
)) {
1201 if (cfg
.resize_action
== RESIZE_TERM
)
1205 if (cfg
.resize_action
== RESIZE_DISABLED
) return;
1206 if (h
== rows
&& w
== cols
) return;
1208 /* Sanity checks ... */
1210 static int first_time
= 1;
1213 switch (first_time
) {
1215 /* Get the size of the screen */
1216 if (GetClientRect(GetDesktopWindow(), &ss
))
1217 /* first_time = 0 */ ;
1223 /* Make sure the values are sane */
1224 width
= (ss
.right
- ss
.left
- extra_width
) / 4;
1225 height
= (ss
.bottom
- ss
.top
- extra_height
) / 6;
1227 if (w
> width
|| h
> height
)
1236 term_size(h
, w
, cfg
.savelines
);
1238 if (cfg
.resize_action
!= RESIZE_FONT
&& !IsZoomed(hwnd
)) {
1239 width
= extra_width
+ font_width
* w
;
1240 height
= extra_height
+ font_height
* h
;
1242 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1243 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1244 SWP_NOMOVE
| SWP_NOZORDER
);
1248 InvalidateRect(hwnd
, NULL
, TRUE
);
1251 static void reset_window(int reinit
) {
1253 * This function decides how to resize or redraw when the
1254 * user changes something.
1256 * This function doesn't like to change the terminal size but if the
1257 * font size is locked that may be it's only soluion.
1259 int win_width
, win_height
;
1262 #ifdef RDB_DEBUG_PATCH
1263 debug((27, "reset_window()"));
1266 /* Current window sizes ... */
1267 GetWindowRect(hwnd
, &wr
);
1268 GetClientRect(hwnd
, &cr
);
1270 win_width
= cr
.right
- cr
.left
;
1271 win_height
= cr
.bottom
- cr
.top
;
1273 if (cfg
.resize_action
== RESIZE_DISABLED
) reinit
= 2;
1275 /* Are we being forced to reload the fonts ? */
1277 #ifdef RDB_DEBUG_PATCH
1278 debug((27, "reset_window() -- Forced deinit"));
1284 /* Oh, looks like we're minimised */
1285 if (win_width
== 0 || win_height
== 0)
1288 /* Is the window out of position ? */
1290 (offset_width
!= (win_width
-font_width
*cols
)/2 ||
1291 offset_height
!= (win_height
-font_height
*rows
)/2) ){
1292 offset_width
= (win_width
-font_width
*cols
)/2;
1293 offset_height
= (win_height
-font_height
*rows
)/2;
1294 InvalidateRect(hwnd
, NULL
, TRUE
);
1295 #ifdef RDB_DEBUG_PATCH
1296 debug((27, "reset_window() -> Reposition terminal"));
1300 if (IsZoomed(hwnd
)) {
1301 /* We're fullscreen, this means we must not change the size of
1302 * the window so it's the font size or the terminal itself.
1305 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1306 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1308 if (cfg
.resize_action
!= RESIZE_TERM
) {
1309 if ( font_width
!= win_width
/cols
||
1310 font_height
!= win_height
/rows
) {
1312 init_fonts(win_width
/cols
, win_height
/rows
);
1313 offset_width
= (win_width
-font_width
*cols
)/2;
1314 offset_height
= (win_height
-font_height
*rows
)/2;
1315 InvalidateRect(hwnd
, NULL
, TRUE
);
1316 #ifdef RDB_DEBUG_PATCH
1317 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1318 font_width
, font_height
));
1322 if ( font_width
!= win_width
/cols
||
1323 font_height
!= win_height
/rows
) {
1324 /* Our only choice at this point is to change the
1325 * size of the terminal; Oh well.
1327 term_size( win_height
/font_height
, win_width
/font_width
,
1329 offset_width
= (win_width
-font_width
*cols
)/2;
1330 offset_height
= (win_height
-font_height
*rows
)/2;
1331 InvalidateRect(hwnd
, NULL
, TRUE
);
1332 #ifdef RDB_DEBUG_PATCH
1333 debug((27, "reset_window() -> Zoomed term_size"));
1340 /* Hmm, a force re-init means we should ignore the current window
1341 * so we resize to the default font size.
1344 #ifdef RDB_DEBUG_PATCH
1345 debug((27, "reset_window() -> Forced re-init"));
1348 offset_width
= offset_height
= cfg
.window_border
;
1349 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1350 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1352 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1353 win_height
!= font_height
*rows
+ offset_height
*2) {
1355 /* If this is too large windows will resize it to the maximum
1356 * allowed window size, we will then be back in here and resize
1357 * the font or terminal to fit.
1359 SetWindowPos(hwnd
, NULL
, 0, 0,
1360 font_width
*cols
+ extra_width
,
1361 font_height
*rows
+ extra_height
,
1362 SWP_NOMOVE
| SWP_NOZORDER
);
1365 InvalidateRect(hwnd
, NULL
, TRUE
);
1369 /* Okay the user doesn't want us to change the font so we try the
1370 * window. But that may be too big for the screen which forces us
1371 * to change the terminal.
1373 if ((cfg
.resize_action
== RESIZE_TERM
&& reinit
<=0) ||
1374 (cfg
.resize_action
== RESIZE_EITHER
&& reinit
<0) ||
1376 offset_width
= offset_height
= cfg
.window_border
;
1377 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1378 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1380 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1381 win_height
!= font_height
*rows
+ offset_height
*2) {
1386 GetClientRect(GetDesktopWindow(), &ss
);
1387 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1388 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1391 if ( rows
> height
|| cols
> width
) {
1392 if (cfg
.resize_action
== RESIZE_EITHER
) {
1393 /* Make the font the biggest we can */
1395 font_width
= (ss
.right
- ss
.left
- extra_width
)/cols
;
1397 font_height
= (ss
.bottom
- ss
.top
- extra_height
)/rows
;
1400 init_fonts(font_width
, font_height
);
1402 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1403 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1405 if ( height
> rows
) height
= rows
;
1406 if ( width
> cols
) width
= cols
;
1407 term_size(height
, width
, cfg
.savelines
);
1408 #ifdef RDB_DEBUG_PATCH
1409 debug((27, "reset_window() -> term resize to (%d,%d)",
1415 SetWindowPos(hwnd
, NULL
, 0, 0,
1416 font_width
*cols
+ extra_width
,
1417 font_height
*rows
+ extra_height
,
1418 SWP_NOMOVE
| SWP_NOZORDER
);
1420 InvalidateRect(hwnd
, NULL
, TRUE
);
1421 #ifdef RDB_DEBUG_PATCH
1422 debug((27, "reset_window() -> window resize to (%d,%d)",
1423 font_width
*cols
+ extra_width
,
1424 font_height
*rows
+ extra_height
));
1430 /* We're allowed to or must change the font but do we want to ? */
1432 if (font_width
!= (win_width
-cfg
.window_border
*2)/cols
||
1433 font_height
!= (win_height
-cfg
.window_border
*2)/rows
) {
1436 init_fonts((win_width
-cfg
.window_border
*2)/cols
,
1437 (win_height
-cfg
.window_border
*2)/rows
);
1438 offset_width
= (win_width
-font_width
*cols
)/2;
1439 offset_height
= (win_height
-font_height
*rows
)/2;
1441 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+offset_width
*2;
1442 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1444 InvalidateRect(hwnd
, NULL
, TRUE
);
1445 #ifdef RDB_DEBUG_PATCH
1446 debug((25, "reset_window() -> font resize to (%d,%d)",
1447 font_width
, font_height
));
1452 static void set_input_locale(HKL kl
)
1456 GetLocaleInfo(LOWORD(kl
), LOCALE_IDEFAULTANSICODEPAGE
,
1457 lbuf
, sizeof(lbuf
));
1459 kbd_codepage
= atoi(lbuf
);
1462 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
, int alt
)
1464 int thistime
= GetMessageTime();
1466 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1467 lastbtn
= MBT_NOTHING
;
1468 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
, alt
);
1472 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1473 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1474 lastact
== MA_2CLK ? MA_3CLK
:
1475 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1480 if (lastact
!= MA_NOTHING
)
1481 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
, alt
);
1482 lasttime
= thistime
;
1486 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1487 * into a cooked one (SELECT, EXTEND, PASTE).
1489 Mouse_Button
translate_button(Mouse_Button button
)
1491 if (button
== MBT_LEFT
)
1493 if (button
== MBT_MIDDLE
)
1494 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1495 if (button
== MBT_RIGHT
)
1496 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1497 return 0; /* shouldn't happen */
1500 static void show_mouseptr(int show
)
1502 static int cursor_visible
= 1;
1503 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1505 if (cursor_visible
&& !show
)
1507 else if (!cursor_visible
&& show
)
1509 cursor_visible
= show
;
1512 static int is_alt_pressed(void)
1515 int r
= GetKeyboardState(keystate
);
1518 if (keystate
[VK_MENU
] & 0x80)
1520 if (keystate
[VK_RMENU
] & 0x80)
1525 static int is_shift_pressed(void)
1528 int r
= GetKeyboardState(keystate
);
1531 if (keystate
[VK_SHIFT
] & 0x80)
1536 static int resizing
;
1538 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1539 WPARAM wParam
, LPARAM lParam
)
1542 static int ignore_clip
= FALSE
;
1543 static int need_backend_resize
= FALSE
;
1544 static int fullscr_on_max
= FALSE
;
1548 if (pending_netevent
)
1549 enact_pending_netevent();
1550 if (GetCapture() != hwnd
||
1551 (send_raw_mouse
&& !(cfg
.mouse_override
&& is_shift_pressed())))
1557 if (cfg
.ping_interval
> 0) {
1560 if (now
- last_movement
> cfg
.ping_interval
) {
1561 back
->special(TS_PING
);
1562 last_movement
= now
;
1565 net_pending_errors();
1571 if (!cfg
.warn_on_close
|| session_closed
||
1573 "Are you sure you want to close this session?",
1574 "PuTTY Exit Confirmation",
1575 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1576 DestroyWindow(hwnd
);
1583 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1595 PROCESS_INFORMATION pi
;
1596 HANDLE filemap
= NULL
;
1598 if (wParam
== IDM_DUPSESS
) {
1600 * Allocate a file-mapping memory chunk for the
1603 SECURITY_ATTRIBUTES sa
;
1606 sa
.nLength
= sizeof(sa
);
1607 sa
.lpSecurityDescriptor
= NULL
;
1608 sa
.bInheritHandle
= TRUE
;
1609 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1612 0, sizeof(Config
), NULL
);
1614 p
= (Config
*) MapViewOfFile(filemap
,
1616 0, 0, sizeof(Config
));
1618 *p
= cfg
; /* structure copy */
1622 sprintf(c
, "putty &%p", filemap
);
1624 } else if (wParam
== IDM_SAVEDSESS
) {
1625 if ((lParam
- IDM_SAVED_MIN
) / 16 < nsessions
) {
1627 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1628 cl
= smalloc(16 + strlen(session
));
1629 /* 8, but play safe */
1632 /* not a very important failure mode */
1634 sprintf(cl
, "putty @%s", session
);
1642 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1644 si
.lpReserved
= NULL
;
1645 si
.lpDesktop
= NULL
;
1649 si
.lpReserved2
= NULL
;
1650 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1651 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1654 CloseHandle(filemap
);
1664 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1667 if (!do_reconfig(hwnd
))
1671 /* Disable full-screen if resizing forbidden */
1672 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
1673 EnableMenuItem(m
, IDM_FULLSCREEN
, MF_BYCOMMAND
|
1674 (cfg
.resize_action
== RESIZE_DISABLED
)
1675 ? MF_GRAYED
: MF_ENABLED
);
1676 /* Gracefully unzoom if necessary */
1677 if (IsZoomed(hwnd
) &&
1678 (cfg
.resize_action
== RESIZE_DISABLED
)) {
1679 ShowWindow(hwnd
, SW_RESTORE
);
1683 if (strcmp(prev_cfg
.logfilename
, cfg
.logfilename
) ||
1684 prev_cfg
.logtype
!= cfg
.logtype
) {
1685 logfclose(); /* reset logging */
1691 * Flush the line discipline's edit buffer in the
1692 * case where local editing has just been disabled.
1694 ldisc_send(NULL
, 0, 0);
1702 /* Give terminal a heads-up on miscellaneous stuff */
1705 /* Screen size changed ? */
1706 if (cfg
.height
!= prev_cfg
.height
||
1707 cfg
.width
!= prev_cfg
.width
||
1708 cfg
.savelines
!= prev_cfg
.savelines
||
1709 cfg
.resize_action
== RESIZE_FONT
||
1710 (cfg
.resize_action
== RESIZE_EITHER
&& IsZoomed(hwnd
)) ||
1711 cfg
.resize_action
== RESIZE_DISABLED
)
1712 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1714 /* Enable or disable the scroll bar, etc */
1716 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1717 LONG nexflag
, exflag
=
1718 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1721 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1722 if (cfg
.alwaysontop
) {
1723 nexflag
|= WS_EX_TOPMOST
;
1724 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1725 SWP_NOMOVE
| SWP_NOSIZE
);
1727 nexflag
&= ~(WS_EX_TOPMOST
);
1728 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1729 SWP_NOMOVE
| SWP_NOSIZE
);
1732 if (cfg
.sunken_edge
)
1733 nexflag
|= WS_EX_CLIENTEDGE
;
1735 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1738 if (is_full_screen() ?
1739 cfg
.scrollbar_in_fullscreen
: cfg
.scrollbar
)
1742 nflg
&= ~WS_VSCROLL
;
1744 if (cfg
.resize_action
== RESIZE_DISABLED
||
1746 nflg
&= ~WS_THICKFRAME
;
1748 nflg
|= WS_THICKFRAME
;
1750 if (cfg
.resize_action
== RESIZE_DISABLED
)
1751 nflg
&= ~WS_MAXIMIZEBOX
;
1753 nflg
|= WS_MAXIMIZEBOX
;
1755 if (nflg
!= flag
|| nexflag
!= exflag
) {
1757 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1758 if (nexflag
!= exflag
)
1759 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1761 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1762 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1763 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1771 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1776 set_title(cfg
.wintitle
);
1777 if (IsIconic(hwnd
)) {
1779 cfg
.win_name_always ? window_name
:
1783 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1784 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1785 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1786 cfg
.fontheight
!= prev_cfg
.fontheight
||
1787 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1788 cfg
.vtmode
!= prev_cfg
.vtmode
||
1789 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1790 cfg
.resize_action
== RESIZE_DISABLED
||
1791 cfg
.resize_action
== RESIZE_EITHER
||
1792 (cfg
.resize_action
!= prev_cfg
.resize_action
))
1795 InvalidateRect(hwnd
, NULL
, TRUE
);
1796 reset_window(init_lvl
);
1797 net_pending_errors();
1810 back
->special(TS_AYT
);
1811 net_pending_errors();
1814 back
->special(TS_BRK
);
1815 net_pending_errors();
1818 back
->special(TS_SYNCH
);
1819 net_pending_errors();
1822 back
->special(TS_EC
);
1823 net_pending_errors();
1826 back
->special(TS_EL
);
1827 net_pending_errors();
1830 back
->special(TS_GA
);
1831 net_pending_errors();
1834 back
->special(TS_NOP
);
1835 net_pending_errors();
1838 back
->special(TS_ABORT
);
1839 net_pending_errors();
1842 back
->special(TS_AO
);
1843 net_pending_errors();
1846 back
->special(TS_IP
);
1847 net_pending_errors();
1850 back
->special(TS_SUSP
);
1851 net_pending_errors();
1854 back
->special(TS_EOR
);
1855 net_pending_errors();
1858 back
->special(TS_EOF
);
1859 net_pending_errors();
1865 WinHelp(hwnd
, help_path
,
1866 help_has_contents ? HELP_FINDER
: HELP_CONTENTS
, 0);
1870 * We get this if the System menu has been activated
1877 * We get this if the System menu has been activated
1878 * using the keyboard. This might happen from within
1879 * TranslateKey, in which case it really wants to be
1880 * followed by a `space' character to actually _bring
1881 * the menu up_ rather than just sitting there in
1882 * `ready to appear' state.
1884 show_mouseptr(1); /* make sure pointer is visible */
1886 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1888 case IDM_FULLSCREEN
:
1892 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1893 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1898 #define X_POS(l) ((int)(short)LOWORD(l))
1899 #define Y_POS(l) ((int)(short)HIWORD(l))
1901 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1902 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1903 case WM_LBUTTONDOWN
:
1904 case WM_MBUTTONDOWN
:
1905 case WM_RBUTTONDOWN
:
1913 case WM_LBUTTONDOWN
:
1917 case WM_MBUTTONDOWN
:
1918 button
= MBT_MIDDLE
;
1921 case WM_RBUTTONDOWN
:
1930 button
= MBT_MIDDLE
;
1938 button
= press
= 0; /* shouldn't happen */
1942 * Special case: in full-screen mode, if the left
1943 * button is clicked in the very top left corner of the
1944 * window, we put up the System menu instead of doing
1947 if (is_full_screen() && press
&& button
== MBT_LEFT
&&
1948 X_POS(lParam
) == 0 && Y_POS(lParam
) == 0) {
1949 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_MOUSEMENU
, 0);
1954 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1955 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
,
1959 term_mouse(button
, MA_RELEASE
,
1960 TO_CHR_X(X_POS(lParam
)),
1961 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1962 wParam
& MK_CONTROL
, is_alt_pressed());
1970 * Add the mouse position and message time to the random
1973 noise_ultralight(lParam
);
1975 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
) &&
1976 GetCapture() == hwnd
) {
1978 if (wParam
& MK_LBUTTON
)
1980 else if (wParam
& MK_MBUTTON
)
1984 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1985 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1986 wParam
& MK_CONTROL
, is_alt_pressed());
1989 case WM_NCMOUSEMOVE
:
1991 noise_ultralight(lParam
);
1993 case WM_IGNORE_CLIP
:
1994 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1996 case WM_DESTROYCLIPBOARD
:
1999 ignore_clip
= FALSE
;
2005 hdc
= BeginPaint(hwnd
, &p
);
2007 SelectPalette(hdc
, pal
, TRUE
);
2008 RealizePalette(hdc
);
2011 (p
.rcPaint
.left
-offset_width
)/font_width
,
2012 (p
.rcPaint
.top
-offset_height
)/font_height
,
2013 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
2014 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
2017 p
.rcPaint
.left
< offset_width
||
2018 p
.rcPaint
.top
< offset_height
||
2019 p
.rcPaint
.right
>= offset_width
+ font_width
*cols
||
2020 p
.rcPaint
.bottom
>= offset_height
+ font_height
*rows
)
2022 HBRUSH fillcolour
, oldbrush
;
2024 fillcolour
= CreateSolidBrush (
2025 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
2026 oldbrush
= SelectObject(hdc
, fillcolour
);
2027 edge
= CreatePen(PS_SOLID
, 0,
2028 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
2029 oldpen
= SelectObject(hdc
, edge
);
2031 ExcludeClipRect(hdc
,
2032 offset_width
, offset_height
,
2033 offset_width
+font_width
*cols
,
2034 offset_height
+font_height
*rows
);
2036 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
2037 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
2039 // SelectClipRgn(hdc, NULL);
2041 SelectObject(hdc
, oldbrush
);
2042 DeleteObject(fillcolour
);
2043 SelectObject(hdc
, oldpen
);
2046 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
2047 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
2053 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2054 * but the only one that's likely to try to overload us is FD_READ.
2055 * This means buffering just one is fine.
2057 if (pending_netevent
)
2058 enact_pending_netevent();
2060 pending_netevent
= TRUE
;
2061 pend_netevent_wParam
= wParam
;
2062 pend_netevent_lParam
= lParam
;
2063 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
2064 enact_pending_netevent();
2066 time(&last_movement
);
2070 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
2072 flash_window(0); /* stop */
2081 caret_x
= caret_y
= -1; /* ensure caret is replaced next time */
2085 case WM_ENTERSIZEMOVE
:
2086 #ifdef RDB_DEBUG_PATCH
2087 debug((27, "WM_ENTERSIZEMOVE"));
2091 need_backend_resize
= FALSE
;
2093 case WM_EXITSIZEMOVE
:
2096 #ifdef RDB_DEBUG_PATCH
2097 debug((27, "WM_EXITSIZEMOVE"));
2099 if (need_backend_resize
) {
2100 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
2101 InvalidateRect(hwnd
, NULL
, TRUE
);
2106 * This does two jobs:
2107 * 1) Keep the sizetip uptodate
2108 * 2) Make sure the window size is _stepped_ in units of the font size.
2110 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2111 int width
, height
, w
, h
, ew
, eh
;
2112 LPRECT r
= (LPRECT
) lParam
;
2114 if ( !need_backend_resize
&& cfg
.resize_action
== RESIZE_EITHER
&&
2115 (cfg
.height
!= rows
|| cfg
.width
!= cols
)) {
2117 * Great! It seems that both the terminal size and the
2118 * font size have been changed and the user is now dragging.
2120 * It will now be difficult to get back to the configured
2123 * This would be easier but it seems to be too confusing.
2125 term_size(cfg.height, cfg.width, cfg.savelines);
2128 cfg
.height
=rows
; cfg
.width
=cols
;
2130 InvalidateRect(hwnd
, NULL
, TRUE
);
2131 need_backend_resize
= TRUE
;
2134 width
= r
->right
- r
->left
- extra_width
;
2135 height
= r
->bottom
- r
->top
- extra_height
;
2136 w
= (width
+ font_width
/ 2) / font_width
;
2139 h
= (height
+ font_height
/ 2) / font_height
;
2142 UpdateSizeTip(hwnd
, w
, h
);
2143 ew
= width
- w
* font_width
;
2144 eh
= height
- h
* font_height
;
2146 if (wParam
== WMSZ_LEFT
||
2147 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2153 if (wParam
== WMSZ_TOP
||
2154 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2164 int width
, height
, w
, h
, rv
= 0;
2165 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
2166 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2167 LPRECT r
= (LPRECT
) lParam
;
2169 width
= r
->right
- r
->left
- ex_width
;
2170 height
= r
->bottom
- r
->top
- ex_height
;
2172 w
= (width
+ cols
/2)/cols
;
2173 h
= (height
+ rows
/2)/rows
;
2174 if ( r
->right
!= r
->left
+ w
*cols
+ ex_width
)
2177 if (wParam
== WMSZ_LEFT
||
2178 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2179 r
->left
= r
->right
- w
*cols
- ex_width
;
2181 r
->right
= r
->left
+ w
*cols
+ ex_width
;
2183 if (r
->bottom
!= r
->top
+ h
*rows
+ ex_height
)
2186 if (wParam
== WMSZ_TOP
||
2187 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2188 r
->top
= r
->bottom
- h
*rows
- ex_height
;
2190 r
->bottom
= r
->top
+ h
*rows
+ ex_height
;
2194 /* break; (never reached) */
2195 case WM_FULLSCR_ON_MAX
:
2196 fullscr_on_max
= TRUE
;
2199 sys_cursor_update();
2202 #ifdef RDB_DEBUG_PATCH
2203 debug((27, "WM_SIZE %s (%d,%d)",
2204 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2205 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2206 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2207 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2209 LOWORD(lParam
), HIWORD(lParam
)));
2211 if (wParam
== SIZE_MINIMIZED
)
2213 cfg
.win_name_always ? window_name
: icon_name
);
2214 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2215 SetWindowText(hwnd
, window_name
);
2216 if (wParam
== SIZE_RESTORED
)
2217 clear_full_screen();
2218 if (wParam
== SIZE_MAXIMIZED
&& fullscr_on_max
) {
2220 fullscr_on_max
= FALSE
;
2223 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2224 /* A resize, well it better be a minimize. */
2228 int width
, height
, w
, h
;
2230 width
= LOWORD(lParam
);
2231 height
= HIWORD(lParam
);
2234 if (wParam
== SIZE_MAXIMIZED
&& !was_zoomed
) {
2238 if (cfg
.resize_action
== RESIZE_TERM
) {
2239 w
= width
/ font_width
;
2241 h
= height
/ font_height
;
2244 term_size(h
, w
, cfg
.savelines
);
2247 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2249 if (cfg
.resize_action
== RESIZE_TERM
)
2250 term_size(prev_rows
, prev_cols
, cfg
.savelines
);
2251 if (cfg
.resize_action
!= RESIZE_FONT
)
2256 /* This is an unexpected resize, these will normally happen
2257 * if the window is too large. Probably either the user
2258 * selected a huge font or the screen size has changed.
2260 * This is also called with minimize.
2262 else reset_window(-1);
2266 * Don't call back->size in mid-resize. (To prevent
2267 * massive numbers of resize events getting sent
2268 * down the connection during an NT opaque drag.)
2271 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2272 need_backend_resize
= TRUE
;
2273 w
= (width
-cfg
.window_border
*2) / font_width
;
2275 h
= (height
-cfg
.window_border
*2) / font_height
;
2284 sys_cursor_update();
2287 switch (LOWORD(wParam
)) {
2301 term_scroll(0, +rows
/ 2);
2304 term_scroll(0, -rows
/ 2);
2306 case SB_THUMBPOSITION
:
2308 term_scroll(1, HIWORD(wParam
));
2312 case WM_PALETTECHANGED
:
2313 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2314 HDC hdc
= get_ctx();
2316 if (RealizePalette(hdc
) > 0)
2322 case WM_QUERYNEWPALETTE
:
2324 HDC hdc
= get_ctx();
2326 if (RealizePalette(hdc
) > 0)
2338 * Add the scan code and keypress timing to the random
2341 noise_ultralight(lParam
);
2344 * We don't do TranslateMessage since it disassociates the
2345 * resulting CHAR message from the KEYDOWN that sparked it,
2346 * which we occasionally don't want. Instead, we process
2347 * KEYDOWN, and call the Win32 translator functions so that
2348 * we get the translations under _our_ control.
2351 unsigned char buf
[20];
2354 if (wParam
== VK_PROCESSKEY
) {
2357 m
.message
= WM_KEYDOWN
;
2359 m
.lParam
= lParam
& 0xdfff;
2360 TranslateMessage(&m
);
2362 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2364 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2368 * Interrupt an ongoing paste. I'm not sure
2369 * this is sensible, but for the moment it's
2370 * preferable to having to faff about buffering
2376 * We need not bother about stdin backlogs
2377 * here, because in GUI PuTTY we can't do
2378 * anything about it anyway; there's no means
2379 * of asking Windows to hold off on KEYDOWN
2380 * messages. We _have_ to buffer everything
2383 ldisc_send(buf
, len
, 1);
2388 net_pending_errors();
2390 case WM_INPUTLANGCHANGE
:
2391 /* wParam == Font number */
2392 /* lParam == Locale */
2393 set_input_locale((HKL
)lParam
);
2394 sys_cursor_update();
2397 if(wParam
== IMN_SETOPENSTATUS
) {
2398 HIMC hImc
= ImmGetContext(hwnd
);
2399 ImmSetCompositionFont(hImc
, &lfont
);
2400 ImmReleaseContext(hwnd
, hImc
);
2404 case WM_IME_COMPOSITION
:
2410 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2411 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2413 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2414 break; /* fall back to DefWindowProc */
2416 hIMC
= ImmGetContext(hwnd
);
2417 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2421 buff
= (char*) smalloc(n
);
2422 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2424 * Jaeyoun Chung reports that Korean character
2425 * input doesn't work correctly if we do a single
2426 * luni_send() covering the whole of buff. So
2427 * instead we luni_send the characters one by one.
2429 for (i
= 0; i
< n
; i
+= 2)
2430 luni_send((unsigned short *)(buff
+i
), 1, 1);
2433 ImmReleaseContext(hwnd
, hIMC
);
2438 if (wParam
& 0xFF00) {
2439 unsigned char buf
[2];
2442 buf
[0] = wParam
>> 8;
2443 lpage_send(kbd_codepage
, buf
, 2, 1);
2445 char c
= (unsigned char) wParam
;
2446 lpage_send(kbd_codepage
, &c
, 1, 1);
2452 * Nevertheless, we are prepared to deal with WM_CHAR
2453 * messages, should they crop up. So if someone wants to
2454 * post the things to us as part of a macro manoeuvre,
2455 * we're ready to cope.
2458 char c
= (unsigned char)wParam
;
2459 lpage_send(CP_ACP
, &c
, 1, 1);
2463 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2464 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2468 if (message
== wm_mousewheel
|| message
== WM_MOUSEWHEEL
) {
2469 int shift_pressed
=0, control_pressed
=0, alt_pressed
=0;
2471 if (message
== WM_MOUSEWHEEL
) {
2472 wheel_accumulator
+= (short)HIWORD(wParam
);
2473 shift_pressed
=LOWORD(wParam
) & MK_SHIFT
;
2474 control_pressed
=LOWORD(wParam
) & MK_CONTROL
;
2477 wheel_accumulator
+= (int)wParam
;
2478 if (GetKeyboardState(keys
)!=0) {
2479 shift_pressed
=keys
[VK_SHIFT
]&0x80;
2480 control_pressed
=keys
[VK_CONTROL
]&0x80;
2484 /* process events when the threshold is reached */
2485 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
2488 /* reduce amount for next time */
2489 if (wheel_accumulator
> 0) {
2491 wheel_accumulator
-= WHEEL_DELTA
;
2492 } else if (wheel_accumulator
< 0) {
2494 wheel_accumulator
+= WHEEL_DELTA
;
2498 if (send_raw_mouse
&&
2499 !(cfg
.mouse_override
&& shift_pressed
)) {
2500 /* send a mouse-down followed by a mouse up */
2503 TO_CHR_X(X_POS(lParam
)),
2504 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2505 control_pressed
, is_alt_pressed());
2506 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
2507 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2508 control_pressed
, is_alt_pressed());
2510 /* trigger a scroll */
2512 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
2519 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2523 * Move the system caret. (We maintain one, even though it's
2524 * invisible, for the benefit of blind people: apparently some
2525 * helper software tracks the system caret, so we should arrange to
2528 void sys_cursor(int x
, int y
)
2532 if (!has_focus
) return;
2535 * Avoid gratuitously re-updating the cursor position and IMM
2536 * window if there's no actual change required.
2538 cx
= x
* font_width
+ offset_width
;
2539 cy
= y
* font_height
+ offset_height
;
2540 if (cx
== caret_x
&& cy
== caret_y
)
2545 sys_cursor_update();
2548 static void sys_cursor_update(void)
2553 if (!has_focus
) return;
2555 if (caret_x
< 0 || caret_y
< 0)
2558 SetCaretPos(caret_x
, caret_y
);
2560 /* IMM calls on Win98 and beyond only */
2561 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2563 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2564 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2566 /* we should have the IMM functions */
2567 hIMC
= ImmGetContext(hwnd
);
2568 cf
.dwStyle
= CFS_POINT
;
2569 cf
.ptCurrentPos
.x
= caret_x
;
2570 cf
.ptCurrentPos
.y
= caret_y
;
2571 ImmSetCompositionWindow(hIMC
, &cf
);
2573 ImmReleaseContext(hwnd
, hIMC
);
2577 * Draw a line of text in the window, at given character
2578 * coordinates, in given attributes.
2580 * We are allowed to fiddle with the contents of `text'.
2582 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2583 unsigned long attr
, int lattr
)
2586 int nfg
, nbg
, nfont
;
2589 int force_manual_underline
= 0;
2590 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2591 int char_width
= fnt_width
;
2592 int text_adjust
= 0;
2593 static int *IpDx
= 0, IpDxLEN
= 0;
2595 if (attr
& ATTR_WIDE
)
2598 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2600 if (len
> IpDxLEN
) {
2602 IpDx
= smalloc((len
+ 16) * sizeof(int));
2603 IpDxLEN
= (len
+ 16);
2605 for (i
= 0; i
< IpDxLEN
; i
++)
2606 IpDx
[i
] = char_width
;
2609 /* Only want the left half of double width lines */
2610 if (lattr
!= LATTR_NORM
&& x
*2 >= cols
)
2618 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
2619 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2620 attr
^= ATTR_CUR_XOR
;
2624 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2625 /* Assume a poorman font is borken in other ways too. */
2635 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2638 if (attr
& ATTR_NARROW
)
2639 nfont
|= FONT_NARROW
;
2641 /* Special hack for the VT100 linedraw glyphs. */
2642 if ((attr
& CSET_MASK
) == 0x2300) {
2643 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2644 switch ((unsigned char) (text
[0])) {
2646 text_adjust
= -2 * font_height
/ 5;
2649 text_adjust
= -1 * font_height
/ 5;
2652 text_adjust
= font_height
/ 5;
2655 text_adjust
= 2 * font_height
/ 5;
2658 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2661 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2662 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2663 if (attr
& ATTR_UNDER
) {
2664 attr
&= ~ATTR_UNDER
;
2665 force_manual_underline
= 1;
2670 /* Anything left as an original character set is unprintable. */
2671 if (DIRECT_CHAR(attr
)) {
2674 memset(text
, 0xFD, len
);
2678 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2681 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2682 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2683 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2685 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2686 nfont
|= FONT_UNDERLINE
;
2687 another_font(nfont
);
2688 if (!fonts
[nfont
]) {
2689 if (nfont
& FONT_UNDERLINE
)
2690 force_manual_underline
= 1;
2691 /* Don't do the same for manual bold, it could be bad news. */
2693 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2695 another_font(nfont
);
2697 nfont
= FONT_NORMAL
;
2698 if (attr
& ATTR_REVERSE
) {
2703 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2705 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2709 SelectObject(hdc
, fonts
[nfont
]);
2710 SetTextColor(hdc
, fg
);
2711 SetBkColor(hdc
, bg
);
2712 SetBkMode(hdc
, OPAQUE
);
2715 line_box
.right
= x
+ char_width
* len
;
2716 line_box
.bottom
= y
+ font_height
;
2718 /* Only want the left half of double width lines */
2719 if (line_box
.right
> font_width
*cols
+offset_width
)
2720 line_box
.right
= font_width
*cols
+offset_width
;
2722 /* We're using a private area for direct to font. (512 chars.) */
2723 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2724 /* Ho Hum, dbcs fonts are a PITA! */
2725 /* To display on W9x I have to convert to UCS */
2726 static wchar_t *uni_buf
= 0;
2727 static int uni_len
= 0;
2729 if (len
> uni_len
) {
2731 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2734 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2735 uni_buf
[nlen
] = 0xFFFD;
2736 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2737 IpDx
[nlen
] += char_width
;
2738 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2739 text
+mptr
, 2, uni_buf
+nlen
, 1);
2744 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2745 text
+mptr
, 1, uni_buf
+nlen
, 1);
2753 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2754 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2755 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2756 SetBkMode(hdc
, TRANSPARENT
);
2757 ExtTextOutW(hdc
, x
- 1,
2758 y
- font_height
* (lattr
==
2759 LATTR_BOT
) + text_adjust
,
2760 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2764 } else if (DIRECT_FONT(attr
)) {
2766 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2767 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2768 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2769 SetBkMode(hdc
, TRANSPARENT
);
2771 /* GRR: This draws the character outside it's box and can leave
2772 * 'droppings' even with the clip box! I suppose I could loop it
2773 * one character at a time ... yuk.
2775 * Or ... I could do a test print with "W", and use +1 or -1 for this
2776 * shift depending on if the leftmost column is blank...
2778 ExtTextOut(hdc
, x
- 1,
2779 y
- font_height
* (lattr
==
2780 LATTR_BOT
) + text_adjust
,
2781 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2784 /* And 'normal' unicode characters */
2785 static WCHAR
*wbuf
= NULL
;
2786 static int wlen
= 0;
2791 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2793 for (i
= 0; i
< len
; i
++)
2794 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2797 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2798 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2800 /* And the shadow bold hack. */
2801 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2802 SetBkMode(hdc
, TRANSPARENT
);
2803 ExtTextOutW(hdc
, x
- 1,
2804 y
- font_height
* (lattr
==
2805 LATTR_BOT
) + text_adjust
,
2806 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2809 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2810 (und_mode
== UND_LINE
2811 && (attr
& ATTR_UNDER
)))) {
2814 if (lattr
== LATTR_BOT
)
2815 dec
= dec
* 2 - font_height
;
2817 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2818 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2819 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2820 oldpen
= SelectObject(hdc
, oldpen
);
2821 DeleteObject(oldpen
);
2825 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2826 unsigned long attr
, int lattr
)
2832 int ctype
= cfg
.cursor_type
;
2834 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2835 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2836 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2840 attr
|= TATTR_RIGHTCURS
;
2843 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2844 if (attr
& ATTR_WIDE
)
2851 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2854 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2855 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2856 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2857 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2858 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2859 Polyline(hdc
, pts
, 5);
2860 oldpen
= SelectObject(hdc
, oldpen
);
2861 DeleteObject(oldpen
);
2862 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2863 int startx
, starty
, dx
, dy
, length
, i
;
2866 starty
= y
+ descent
;
2869 length
= char_width
;
2872 if (attr
& TATTR_RIGHTCURS
)
2873 xadjust
= char_width
- 1;
2874 startx
= x
+ xadjust
;
2878 length
= font_height
;
2880 if (attr
& TATTR_ACTCURS
) {
2883 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2884 MoveToEx(hdc
, startx
, starty
, NULL
);
2885 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2886 oldpen
= SelectObject(hdc
, oldpen
);
2887 DeleteObject(oldpen
);
2889 for (i
= 0; i
< length
; i
++) {
2891 SetPixel(hdc
, startx
, starty
, colours
[23]);
2900 /* This function gets the actual width of a character in the normal font.
2902 int CharWidth(Context ctx
, int uc
) {
2906 /* If the font max is the same as the font ave width then this
2907 * function is a no-op.
2909 if (!font_dualwidth
) return 1;
2911 switch (uc
& CSET_MASK
) {
2913 uc
= unitab_line
[uc
& 0xFF];
2916 uc
= unitab_xterm
[uc
& 0xFF];
2919 uc
= unitab_scoacs
[uc
& 0xFF];
2922 if (DIRECT_FONT(uc
)) {
2923 if (dbcs_screenfont
) return 1;
2925 /* Speedup, I know of no font where ascii is the wrong width */
2926 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
2929 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
2930 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2931 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
2932 another_font(FONT_OEM
);
2933 if (!fonts
[FONT_OEM
]) return 0;
2935 SelectObject(hdc
, fonts
[FONT_OEM
]);
2939 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
2940 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
2943 /* Speedup, I know of no font where ascii is the wrong width */
2944 if (uc
>= ' ' && uc
<= '~') return 1;
2946 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2947 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
2948 /* Okay that one worked */ ;
2949 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
2950 /* This should work on 9x too, but it's "less accurate" */ ;
2955 ibuf
+= font_width
/ 2 -1;
2962 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2963 * codes. Returns number of bytes used or zero to drop the message
2964 * or -1 to forward the message to windows.
2966 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2967 unsigned char *output
)
2970 int scan
, left_alt
= 0, key_down
, shift_state
;
2972 unsigned char *p
= output
;
2973 static int alt_sum
= 0;
2975 HKL kbd_layout
= GetKeyboardLayout(0);
2977 static WORD keys
[3];
2978 static int compose_char
= 0;
2979 static WPARAM compose_key
= 0;
2981 r
= GetKeyboardState(keystate
);
2983 memset(keystate
, 0, sizeof(keystate
));
2986 #define SHOW_TOASCII_RESULT
2987 { /* Tell us all about key events */
2988 static BYTE oldstate
[256];
2989 static int first
= 1;
2993 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2996 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2998 } else if ((HIWORD(lParam
) & KF_UP
)
2999 && scan
== (HIWORD(lParam
) & 0xFF)) {
3003 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
3004 debug(("K_F%d", wParam
+ 1 - VK_F1
));
3017 debug(("VK_%02x", wParam
));
3019 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
3021 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
3023 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
3024 if (ch
>= ' ' && ch
<= '~')
3025 debug((", '%c'", ch
));
3027 debug((", $%02x", ch
));
3030 debug((", KB0=%02x", keys
[0]));
3032 debug((", KB1=%02x", keys
[1]));
3034 debug((", KB2=%02x", keys
[2]));
3036 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
3038 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
3040 if ((HIWORD(lParam
) & KF_EXTENDED
))
3042 if ((HIWORD(lParam
) & KF_UP
))
3046 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
3047 else if ((HIWORD(lParam
) & KF_UP
))
3048 oldstate
[wParam
& 0xFF] ^= 0x80;
3050 oldstate
[wParam
& 0xFF] ^= 0x81;
3052 for (ch
= 0; ch
< 256; ch
++)
3053 if (oldstate
[ch
] != keystate
[ch
])
3054 debug((", M%02x=%02x", ch
, keystate
[ch
]));
3056 memcpy(oldstate
, keystate
, sizeof(oldstate
));
3060 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
3061 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
3065 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
3066 if ((cfg
.funky_type
== 3 ||
3067 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
3068 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
3070 wParam
= VK_EXECUTE
;
3072 /* UnToggle NUMLock */
3073 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
3074 keystate
[VK_NUMLOCK
] ^= 1;
3077 /* And write back the 'adjusted' state */
3078 SetKeyboardState(keystate
);
3081 /* Disable Auto repeat if required */
3082 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
3085 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
3088 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
3090 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
3091 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
3092 if (cfg
.ctrlaltkeys
)
3093 keystate
[VK_MENU
] = 0;
3095 keystate
[VK_RMENU
] = 0x80;
3100 alt_pressed
= (left_alt
&& key_down
);
3102 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
3103 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
3104 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
3106 /* Note if AltGr was pressed and if it was used as a compose key */
3107 if (!compose_state
) {
3108 compose_key
= 0x100;
3109 if (cfg
.compose_key
) {
3110 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
3111 compose_key
= wParam
;
3113 if (wParam
== VK_APPS
)
3114 compose_key
= wParam
;
3117 if (wParam
== compose_key
) {
3118 if (compose_state
== 0
3119 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
3121 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
3125 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
3129 * Record that we pressed key so the scroll window can be reset, but
3130 * be careful to avoid Shift-UP/Down
3132 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
&&
3133 wParam
!= VK_MENU
&& wParam
!= VK_CONTROL
) {
3137 if (compose_state
> 1 && left_alt
)
3140 /* Sanitize the number pad if not using a PC NumPad */
3141 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
3142 && cfg
.funky_type
!= 2)
3143 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
3144 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
3148 nParam
= VK_NUMPAD0
;
3151 nParam
= VK_NUMPAD1
;
3154 nParam
= VK_NUMPAD2
;
3157 nParam
= VK_NUMPAD3
;
3160 nParam
= VK_NUMPAD4
;
3163 nParam
= VK_NUMPAD5
;
3166 nParam
= VK_NUMPAD6
;
3169 nParam
= VK_NUMPAD7
;
3172 nParam
= VK_NUMPAD8
;
3175 nParam
= VK_NUMPAD9
;
3178 nParam
= VK_DECIMAL
;
3182 if (keystate
[VK_NUMLOCK
] & 1)
3189 /* If a key is pressed and AltGr is not active */
3190 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
3191 /* Okay, prepare for most alts then ... */
3195 /* Lets see if it's a pattern we know all about ... */
3196 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
3197 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
3200 if (wParam
== VK_NEXT
&& shift_state
== 1) {
3201 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
3204 if (wParam
== VK_INSERT
&& shift_state
== 1) {
3208 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
3211 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
3212 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
3215 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
&&
3216 (cfg
.resize_action
!= RESIZE_DISABLED
)) {
3217 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) != KF_REPEAT
)
3221 /* Control-Numlock for app-keypad mode switch */
3222 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
3223 app_keypad_keys
^= 1;
3227 /* Nethack keypad */
3228 if (cfg
.nethack_keypad
&& !left_alt
) {
3231 *p
++ = shift_state ?
'B' : 'b';
3234 *p
++ = shift_state ?
'J' : 'j';
3237 *p
++ = shift_state ?
'N' : 'n';
3240 *p
++ = shift_state ?
'H' : 'h';
3243 *p
++ = shift_state ?
'.' : '.';
3246 *p
++ = shift_state ?
'L' : 'l';
3249 *p
++ = shift_state ?
'Y' : 'y';
3252 *p
++ = shift_state ?
'K' : 'k';
3255 *p
++ = shift_state ?
'U' : 'u';
3260 /* Application Keypad */
3264 if (cfg
.funky_type
== 3 ||
3265 (cfg
.funky_type
<= 1 &&
3266 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3280 if (app_keypad_keys
&& !cfg
.no_applic_k
)
3317 if (cfg
.funky_type
== 2) {
3322 } else if (shift_state
)
3329 if (cfg
.funky_type
== 2)
3333 if (cfg
.funky_type
== 2)
3337 if (cfg
.funky_type
== 2)
3342 if (HIWORD(lParam
) & KF_EXTENDED
)
3348 if (xkey
>= 'P' && xkey
<= 'S')
3349 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3351 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3353 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3358 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3359 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3363 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3369 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3373 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3377 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3382 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3387 /* Control-2 to Control-8 are special */
3388 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3389 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3392 if (shift_state
== 2 && wParam
== 0xBD) {
3396 if (shift_state
== 2 && wParam
== 0xDF) {
3400 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
3407 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3408 * for integer decimal nn.)
3410 * We also deal with the weird ones here. Linux VCs replace F1
3411 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3412 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3418 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3421 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3424 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3427 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3430 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3433 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3436 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3439 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3442 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3445 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3478 if ((shift_state
&2) == 0) switch (wParam
) {
3498 /* Reorder edit keys to physical order */
3499 if (cfg
.funky_type
== 3 && code
<= 6)
3500 code
= "\0\2\1\4\5\3\6"[code
];
3502 if (vt52_mode
&& code
> 0 && code
<= 6) {
3503 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3507 if (cfg
.funky_type
== 5 && /* SCO function keys */
3508 code
>= 11 && code
<= 34) {
3509 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3512 case VK_F1
: index
= 0; break;
3513 case VK_F2
: index
= 1; break;
3514 case VK_F3
: index
= 2; break;
3515 case VK_F4
: index
= 3; break;
3516 case VK_F5
: index
= 4; break;
3517 case VK_F6
: index
= 5; break;
3518 case VK_F7
: index
= 6; break;
3519 case VK_F8
: index
= 7; break;
3520 case VK_F9
: index
= 8; break;
3521 case VK_F10
: index
= 9; break;
3522 case VK_F11
: index
= 10; break;
3523 case VK_F12
: index
= 11; break;
3525 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3526 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3527 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3530 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3531 code
>= 1 && code
<= 6) {
3532 char codes
[] = "HL.FIG";
3536 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3540 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3547 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3550 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3553 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3554 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3557 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3559 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3561 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3564 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3565 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3569 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3574 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3575 * some reason seems to send VK_CLEAR to Windows...).
3598 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3600 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
3603 * RDB: VT100 & VT102 manuals both state the
3604 * app cursor keys only work if the app keypad
3607 * SGT: That may well be true, but xterm
3608 * disagrees and so does at least one
3609 * application, so I've #if'ed this out and the
3610 * behaviour is back to PuTTY's original: app
3611 * cursor and app keypad are independently
3612 * switchable modes. If anyone complains about
3613 * _this_ I'll have to put in a configurable
3616 if (!app_keypad_keys
)
3619 /* Useful mapping of Ctrl-arrows */
3620 if (shift_state
== 2)
3624 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3626 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3633 * Finally, deal with Return ourselves. (Win95 seems to
3634 * foul it up when Alt is pressed, for some reason.)
3636 if (wParam
== VK_RETURN
) { /* Return */
3642 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3643 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3648 /* Okay we've done everything interesting; let windows deal with
3649 * the boring stuff */
3653 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3654 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3656 keystate
[VK_CAPITAL
] = 0;
3659 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3660 #ifdef SHOW_TOASCII_RESULT
3661 if (r
== 1 && !key_down
) {
3663 if (in_utf
|| dbcs_screenfont
)
3664 debug((", (U+%04x)", alt_sum
));
3666 debug((", LCH(%d)", alt_sum
));
3668 debug((", ACH(%d)", keys
[0]));
3673 for (r1
= 0; r1
< r
; r1
++) {
3674 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3683 * Interrupt an ongoing paste. I'm not sure this is
3684 * sensible, but for the moment it's preferable to
3685 * having to faff about buffering things.
3690 for (i
= 0; i
< r
; i
++) {
3691 unsigned char ch
= (unsigned char) keys
[i
];
3693 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3698 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3702 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3703 MessageBeep(MB_ICONHAND
);
3707 luni_send(&keybuf
, 1, 1);
3715 if (in_utf
|| dbcs_screenfont
) {
3717 luni_send(&keybuf
, 1, 1);
3719 ch
= (char) alt_sum
;
3721 * We need not bother about stdin
3722 * backlogs here, because in GUI PuTTY
3723 * we can't do anything about it
3724 * anyway; there's no means of asking
3725 * Windows to hold off on KEYDOWN
3726 * messages. We _have_ to buffer
3727 * everything we're sent.
3729 ldisc_send(&ch
, 1, 1);
3733 lpage_send(kbd_codepage
, &ch
, 1, 1);
3735 if(capsOn
&& ch
< 0x80) {
3738 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3739 luni_send(cbuf
+!left_alt
, 1+!!left_alt
, 1);
3744 lpage_send(kbd_codepage
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3750 /* This is so the ALT-Numpad and dead keys work correctly. */
3755 /* If we're definitly not building up an ALT-54321 then clear it */
3758 /* If we will be using alt_sum fix the 256s */
3759 else if (keys
[0] && (in_utf
|| dbcs_screenfont
))
3764 * ALT alone may or may not want to bring up the System menu.
3765 * If it's not meant to, we return 0 on presses or releases of
3766 * ALT, to show that we've swallowed the keystroke. Otherwise
3767 * we return -1, which means Windows will give the keystroke
3768 * its default handling (i.e. bring up the System menu).
3770 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3776 void set_title(char *title
)
3779 window_name
= smalloc(1 + strlen(title
));
3780 strcpy(window_name
, title
);
3781 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3782 SetWindowText(hwnd
, title
);
3785 void set_icon(char *title
)
3788 icon_name
= smalloc(1 + strlen(title
));
3789 strcpy(icon_name
, title
);
3790 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3791 SetWindowText(hwnd
, title
);
3794 void set_sbar(int total
, int start
, int page
)
3798 if (is_full_screen() ?
!cfg
.scrollbar_in_fullscreen
: !cfg
.scrollbar
)
3801 si
.cbSize
= sizeof(si
);
3802 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3804 si
.nMax
= total
- 1;
3808 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3811 Context
get_ctx(void)
3817 SelectPalette(hdc
, pal
, FALSE
);
3823 void free_ctx(Context ctx
)
3825 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3826 ReleaseDC(hwnd
, ctx
);
3829 static void real_palette_set(int n
, int r
, int g
, int b
)
3832 logpal
->palPalEntry
[n
].peRed
= r
;
3833 logpal
->palPalEntry
[n
].peGreen
= g
;
3834 logpal
->palPalEntry
[n
].peBlue
= b
;
3835 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3836 colours
[n
] = PALETTERGB(r
, g
, b
);
3837 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3839 colours
[n
] = RGB(r
, g
, b
);
3842 void palette_set(int n
, int r
, int g
, int b
)
3844 static const int first
[21] = {
3845 0, 2, 4, 6, 8, 10, 12, 14,
3846 1, 3, 5, 7, 9, 11, 13, 15,
3849 real_palette_set(first
[n
], r
, g
, b
);
3851 real_palette_set(first
[n
] + 1, r
, g
, b
);
3853 HDC hdc
= get_ctx();
3854 UnrealizeObject(pal
);
3855 RealizePalette(hdc
);
3860 void palette_reset(void)
3864 for (i
= 0; i
< NCOLOURS
; i
++) {
3866 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3867 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3868 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3869 logpal
->palPalEntry
[i
].peFlags
= 0;
3870 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3871 defpal
[i
].rgbtGreen
,
3872 defpal
[i
].rgbtBlue
);
3874 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3875 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3880 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3882 RealizePalette(hdc
);
3887 void write_aclip(char *data
, int len
, int must_deselect
)
3892 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3895 lock
= GlobalLock(clipdata
);
3898 memcpy(lock
, data
, len
);
3899 ((unsigned char *) lock
)[len
] = 0;
3900 GlobalUnlock(clipdata
);
3903 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3905 if (OpenClipboard(hwnd
)) {
3907 SetClipboardData(CF_TEXT
, clipdata
);
3910 GlobalFree(clipdata
);
3913 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3917 * Note: unlike write_aclip() this will not append a nul.
3919 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3921 HGLOBAL clipdata
, clipdata2
, clipdata3
;
3923 void *lock
, *lock2
, *lock3
;
3925 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3927 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3928 len
* sizeof(wchar_t));
3929 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3931 if (!clipdata
|| !clipdata2
) {
3933 GlobalFree(clipdata
);
3935 GlobalFree(clipdata2
);
3938 if (!(lock
= GlobalLock(clipdata
)))
3940 if (!(lock2
= GlobalLock(clipdata2
)))
3943 memcpy(lock
, data
, len
* sizeof(wchar_t));
3944 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3946 if (cfg
.rtf_paste
) {
3947 wchar_t unitab
[256];
3949 unsigned char *tdata
= (unsigned char *)lock2
;
3950 wchar_t *udata
= (wchar_t *)lock
;
3951 int rtflen
= 0, uindex
= 0, tindex
= 0;
3953 int multilen
, blen
, alen
, totallen
, i
;
3954 char before
[16], after
[4];
3956 get_unitab(CP_ACP
, unitab
, 0);
3958 rtfsize
= 100 + strlen(cfg
.font
);
3959 rtf
= smalloc(rtfsize
);
3960 sprintf(rtf
, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3961 GetACP(), cfg
.font
);
3962 rtflen
= strlen(rtf
);
3965 * We want to construct a piece of RTF that specifies the
3966 * same Unicode text. To do this we will read back in
3967 * parallel from the Unicode data in `udata' and the
3968 * non-Unicode data in `tdata'. For each character in
3969 * `tdata' which becomes the right thing in `udata' when
3970 * looked up in `unitab', we just copy straight over from
3971 * tdata. For each one that doesn't, we must WCToMB it
3972 * individually and produce a \u escape sequence.
3974 * It would probably be more robust to just bite the bullet
3975 * and WCToMB each individual Unicode character one by one,
3976 * then MBToWC each one back to see if it was an accurate
3977 * translation; but that strikes me as a horrifying number
3978 * of Windows API calls so I want to see if this faster way
3979 * will work. If it screws up badly we can always revert to
3980 * the simple and slow way.
3982 while (tindex
< len2
&& uindex
< len
&&
3983 tdata
[tindex
] && udata
[uindex
]) {
3984 if (tindex
+ 1 < len2
&&
3985 tdata
[tindex
] == '\r' &&
3986 tdata
[tindex
+1] == '\n') {
3990 if (unitab
[tdata
[tindex
]] == udata
[uindex
]) {
3996 multilen
= WideCharToMultiByte(CP_ACP
, 0, unitab
+uindex
, 1,
3997 NULL
, 0, NULL
, NULL
);
3998 if (multilen
!= 1) {
3999 blen
= sprintf(before
, "{\\uc%d\\u%d", multilen
,
4001 alen
= 1; strcpy(after
, "}");
4003 blen
= sprintf(before
, "\\u%d", udata
[uindex
]);
4004 alen
= 0; after
[0] = '\0';
4007 assert(tindex
+ multilen
<= len2
);
4008 totallen
= blen
+ alen
;
4009 for (i
= 0; i
< multilen
; i
++) {
4010 if (tdata
[tindex
+i
] == '\\' ||
4011 tdata
[tindex
+i
] == '{' ||
4012 tdata
[tindex
+i
] == '}')
4014 else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A)
4015 totallen
+= 6; /* \par\r\n */
4016 else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20)
4022 if (rtfsize
< rtflen
+ totallen
+ 3) {
4023 rtfsize
= rtflen
+ totallen
+ 512;
4024 rtf
= srealloc(rtf
, rtfsize
);
4027 strcpy(rtf
+ rtflen
, before
); rtflen
+= blen
;
4028 for (i
= 0; i
< multilen
; i
++) {
4029 if (tdata
[tindex
+i
] == '\\' ||
4030 tdata
[tindex
+i
] == '{' ||
4031 tdata
[tindex
+i
] == '}') {
4032 rtf
[rtflen
++] = '\\';
4033 rtf
[rtflen
++] = tdata
[tindex
+i
];
4034 } else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A) {
4035 rtflen
+= sprintf(rtf
+rtflen
, "\\par\r\n");
4036 } else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20) {
4037 rtflen
+= sprintf(rtf
+rtflen
, "\\'%02x", tdata
[tindex
+i
]);
4039 rtf
[rtflen
++] = tdata
[tindex
+i
];
4042 strcpy(rtf
+ rtflen
, after
); rtflen
+= alen
;
4048 strcpy(rtf
+ rtflen
, "}");
4051 clipdata3
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, rtflen
);
4052 if (clipdata3
&& (lock3
= GlobalLock(clipdata3
)) != NULL
) {
4054 GlobalUnlock(clipdata3
);
4060 GlobalUnlock(clipdata
);
4061 GlobalUnlock(clipdata2
);
4064 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
4066 if (OpenClipboard(hwnd
)) {
4068 SetClipboardData(CF_UNICODETEXT
, clipdata
);
4069 SetClipboardData(CF_TEXT
, clipdata2
);
4071 SetClipboardData(RegisterClipboardFormat(CF_RTF
), clipdata3
);
4074 GlobalFree(clipdata
);
4075 GlobalFree(clipdata2
);
4079 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
4082 void get_clip(wchar_t ** p
, int *len
)
4084 static HGLOBAL clipdata
= NULL
;
4085 static wchar_t *converted
= 0;
4094 GlobalUnlock(clipdata
);
4097 } else if (OpenClipboard(NULL
)) {
4098 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
4100 *p
= GlobalLock(clipdata
);
4102 for (p2
= *p
; *p2
; p2
++);
4106 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
4110 s
= GlobalLock(clipdata
);
4111 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
4112 *p
= converted
= smalloc(i
* sizeof(wchar_t));
4113 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
4126 * Move `lines' lines from position `from' to position `to' in the
4129 void optimised_move(int to
, int from
, int lines
)
4134 min
= (to
< from ? to
: from
);
4135 max
= to
+ from
- min
;
4137 r
.left
= offset_width
;
4138 r
.right
= offset_width
+ cols
* font_width
;
4139 r
.top
= offset_height
+ min
* font_height
;
4140 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
4141 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
4146 * Print a message box and perform a fatal exit.
4148 void fatalbox(char *fmt
, ...)
4154 vsprintf(stuff
, fmt
, ap
);
4156 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
4161 * Manage window caption / taskbar flashing, if enabled.
4162 * 0 = stop, 1 = maintain, 2 = start
4164 static void flash_window(int mode
)
4166 static long last_flash
= 0;
4167 static int flashing
= 0;
4168 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
4171 FlashWindow(hwnd
, FALSE
);
4175 } else if (mode
== 2) {
4178 last_flash
= GetTickCount();
4180 FlashWindow(hwnd
, TRUE
);
4183 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
4186 long now
= GetTickCount();
4187 long fdiff
= now
- last_flash
;
4188 if (fdiff
< 0 || fdiff
> 450) {
4190 FlashWindow(hwnd
, TRUE
); /* toggle */
4201 if (mode
== BELL_DEFAULT
) {
4203 * For MessageBeep style bells, we want to be careful of
4204 * timing, because they don't have the nice property of
4205 * PlaySound bells that each one cancels the previous
4206 * active one. So we limit the rate to one per 50ms or so.
4208 static long lastbeep
= 0;
4211 beepdiff
= GetTickCount() - lastbeep
;
4212 if (beepdiff
>= 0 && beepdiff
< 50)
4216 * The above MessageBeep call takes time, so we record the
4217 * time _after_ it finishes rather than before it starts.
4219 lastbeep
= GetTickCount();
4220 } else if (mode
== BELL_WAVEFILE
) {
4221 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
4222 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
4223 sprintf(buf
, "Unable to play sound file\n%s\n"
4224 "Using default sound instead", cfg
.bell_wavefile
);
4225 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
4226 MB_OK
| MB_ICONEXCLAMATION
);
4227 cfg
.beep
= BELL_DEFAULT
;
4230 /* Otherwise, either visual bell or disabled; do nothing here */
4232 flash_window(2); /* start */
4237 * Minimise or restore the window in response to a server-side
4240 void set_iconic(int iconic
)
4242 if (IsIconic(hwnd
)) {
4244 ShowWindow(hwnd
, SW_RESTORE
);
4247 ShowWindow(hwnd
, SW_MINIMIZE
);
4252 * Move the window in response to a server-side request.
4254 void move_window(int x
, int y
)
4256 if (cfg
.resize_action
== RESIZE_DISABLED
||
4257 cfg
.resize_action
== RESIZE_FONT
||
4261 SetWindowPos(hwnd
, NULL
, x
, y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
);
4265 * Move the window to the top or bottom of the z-order in response
4266 * to a server-side request.
4268 void set_zorder(int top
)
4270 if (cfg
.alwaysontop
)
4271 return; /* ignore */
4272 SetWindowPos(hwnd
, top ? HWND_TOP
: HWND_BOTTOM
, 0, 0, 0, 0,
4273 SWP_NOMOVE
| SWP_NOSIZE
);
4277 * Refresh the window in response to a server-side request.
4279 void refresh_window(void)
4281 InvalidateRect(hwnd
, NULL
, TRUE
);
4285 * Maximise or restore the window in response to a server-side
4288 void set_zoomed(int zoomed
)
4290 if (IsZoomed(hwnd
)) {
4292 ShowWindow(hwnd
, SW_RESTORE
);
4295 ShowWindow(hwnd
, SW_MAXIMIZE
);
4300 * Report whether the window is iconic, for terminal reports.
4304 return IsIconic(hwnd
);
4308 * Report the window's position, for terminal reports.
4310 void get_window_pos(int *x
, int *y
)
4313 GetWindowRect(hwnd
, &r
);
4319 * Report the window's pixel size, for terminal reports.
4321 void get_window_pixels(int *x
, int *y
)
4324 GetWindowRect(hwnd
, &r
);
4325 *x
= r
.right
- r
.left
;
4326 *y
= r
.bottom
- r
.top
;
4330 * Return the window or icon title.
4332 char *get_window_title(int icon
)
4334 return icon ? icon_name
: window_name
;
4338 * See if we're in full-screen mode.
4340 int is_full_screen()
4342 if (!IsZoomed(hwnd
))
4344 if (GetWindowLong(hwnd
, GWL_STYLE
) & WS_CAPTION
)
4350 * Go full-screen. This should only be called when we are already
4353 void make_full_screen()
4358 assert(IsZoomed(hwnd
));
4360 /* Remove the window furniture. */
4361 style
= GetWindowLong(hwnd
, GWL_STYLE
);
4362 style
&= ~(WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
);
4363 if (cfg
.scrollbar_in_fullscreen
)
4364 style
|= WS_VSCROLL
;
4366 style
&= ~WS_VSCROLL
;
4367 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4369 /* Resize ourselves to exactly cover the nearest monitor. */
4370 #ifdef MONITOR_DEFAULTTONEAREST
4374 mon
= MonitorFromWindow(hwnd
, MONITOR_DEFAULTTONEAREST
);
4375 mi
.cbSize
= sizeof(mi
);
4376 GetMonitorInfo(mon
, &mi
);
4377 x
= mi
.rcMonitor
.left
;
4378 y
= mi
.rcMonitor
.top
;
4379 w
= mi
.rcMonitor
.right
;
4380 h
= mi
.rcMonitor
.bottom
;
4384 w
= GetSystemMetrics(SM_CXSCREEN
);
4385 h
= GetSystemMetrics(SM_CYSCREEN
);
4387 SetWindowPos(hwnd
, HWND_TOP
, x
, y
, w
, h
, SWP_FRAMECHANGED
);
4389 /* Tick the menu item in the System menu. */
4390 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4395 * Clear the full-screen attributes.
4397 void clear_full_screen()
4399 DWORD oldstyle
, style
;
4401 /* Reinstate the window furniture. */
4402 style
= oldstyle
= GetWindowLong(hwnd
, GWL_STYLE
);
4403 style
|= WS_CAPTION
| WS_BORDER
;
4404 if (cfg
.resize_action
== RESIZE_DISABLED
)
4405 style
&= ~WS_THICKFRAME
;
4407 style
|= WS_THICKFRAME
;
4409 style
|= WS_VSCROLL
;
4411 style
&= ~WS_VSCROLL
;
4412 if (style
!= oldstyle
) {
4413 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4414 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
4415 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
4419 /* Untick the menu item in the System menu. */
4420 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4425 * Toggle full-screen mode.
4427 void flip_full_screen()
4429 if (is_full_screen()) {
4430 ShowWindow(hwnd
, SW_RESTORE
);
4431 } else if (IsZoomed(hwnd
)) {
4434 SendMessage(hwnd
, WM_FULLSCR_ON_MAX
, 0, 0);
4435 ShowWindow(hwnd
, SW_MAXIMIZE
);