15 #define COMPILE_MULTIMON_STUBS
25 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
31 #define IDM_SHOWLOG 0x0010
32 #define IDM_NEWSESS 0x0020
33 #define IDM_DUPSESS 0x0030
34 #define IDM_RECONF 0x0040
35 #define IDM_CLRSB 0x0050
36 #define IDM_RESET 0x0060
37 #define IDM_TEL_AYT 0x0070
38 #define IDM_TEL_BRK 0x0080
39 #define IDM_TEL_SYNCH 0x0090
40 #define IDM_TEL_EC 0x00a0
41 #define IDM_TEL_EL 0x00b0
42 #define IDM_TEL_GA 0x00c0
43 #define IDM_TEL_NOP 0x00d0
44 #define IDM_TEL_ABORT 0x00e0
45 #define IDM_TEL_AO 0x00f0
46 #define IDM_TEL_IP 0x0100
47 #define IDM_TEL_SUSP 0x0110
48 #define IDM_TEL_EOR 0x0120
49 #define IDM_TEL_EOF 0x0130
50 #define IDM_HELP 0x0140
51 #define IDM_ABOUT 0x0150
52 #define IDM_SAVEDSESS 0x0160
53 #define IDM_COPYALL 0x0170
54 #define IDM_FULLSCREEN 0x0180
56 #define IDM_SESSLGP 0x0250 /* log type printable */
57 #define IDM_SESSLGA 0x0260 /* log type all chars */
58 #define IDM_SESSLGE 0x0270 /* log end */
59 #define IDM_SAVED_MIN 0x1000
60 #define IDM_SAVED_MAX 0x2000
62 #define WM_IGNORE_CLIP (WM_XUSER + 2)
63 #define WM_FULLSCR_ON_MAX (WM_XUSER + 3)
65 /* Needed for Chinese support and apparently not always defined. */
67 #define VK_PROCESSKEY 0xE5
70 /* Needed for mouse wheel support and not defined in earlier SDKs. */
72 #define WM_MOUSEWHEEL 0x020A
75 static LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
76 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
77 unsigned char *output
);
78 static void cfgtopalette(void);
79 static void init_palette(void);
80 static void init_fonts(int, int);
81 static void another_font(int);
82 static void deinit_fonts(void);
83 static void set_input_locale(HKL
);
84 static int do_mouse_wheel_msg(UINT message
, WPARAM wParam
, LPARAM lParam
);
86 static int is_full_screen(void);
87 static void make_full_screen(void);
88 static void clear_full_screen(void);
89 static void flip_full_screen(void);
91 /* Window layout information */
92 static void reset_window(int);
93 static int extra_width
, extra_height
;
94 static int font_width
, font_height
, font_dualwidth
;
95 static int offset_width
, offset_height
;
96 static int was_zoomed
= 0;
97 static int prev_rows
, prev_cols
;
99 static int pending_netevent
= 0;
100 static WPARAM pend_netevent_wParam
= 0;
101 static LPARAM pend_netevent_lParam
= 0;
102 static void enact_pending_netevent(void);
103 static void flash_window(int mode
);
105 static time_t last_movement
= 0;
107 #define FONT_NORMAL 0
109 #define FONT_UNDERLINE 2
110 #define FONT_BOLDUND 3
111 #define FONT_WIDE 0x04
112 #define FONT_HIGH 0x08
113 #define FONT_NARROW 0x10
115 #define FONT_OEM 0x20
116 #define FONT_OEMBOLD 0x21
117 #define FONT_OEMUND 0x22
118 #define FONT_OEMBOLDUND 0x23
120 #define FONT_MAXNO 0x2F
122 static HFONT fonts
[FONT_MAXNO
];
123 static LOGFONT lfont
;
124 static int fontflag
[FONT_MAXNO
];
126 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
134 static COLORREF colours
[NCOLOURS
];
136 static LPLOGPALETTE logpal
;
137 static RGBTRIPLE defpal
[NCOLOURS
];
141 static HBITMAP caretbm
;
143 static int dbltime
, lasttime
, lastact
;
144 static Mouse_Button lastbtn
;
146 /* this allows xterm-style mouse handling. */
147 static int send_raw_mouse
= 0;
148 static int wheel_accumulator
= 0;
150 static char *window_name
, *icon_name
;
152 static int compose_state
= 0;
154 static OSVERSIONINFO osVersion
;
156 static UINT wm_mousewheel
= WM_MOUSEWHEEL
;
158 /* Dummy routine, only required in plink. */
159 void ldisc_update(int echo
, int edit
)
163 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
165 static char appname
[] = "PuTTY";
170 int guess_width
, guess_height
;
173 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
175 winsock_ver
= MAKEWORD(1, 1);
176 if (WSAStartup(winsock_ver
, &wsadata
)) {
177 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
178 MB_OK
| MB_ICONEXCLAMATION
);
181 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
182 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
183 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
187 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
190 InitCommonControls();
192 /* Ensure a Maximize setting in Explorer doesn't maximise the
197 ZeroMemory(&osVersion
, sizeof(osVersion
));
198 osVersion
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
199 if (!GetVersionEx ( (OSVERSIONINFO
*) &osVersion
)) {
200 MessageBox(NULL
, "Windows refuses to report a version",
201 "PuTTY Fatal Error", MB_OK
| MB_ICONEXCLAMATION
);
207 * If we're running a version of Windows that doesn't support
208 * WM_MOUSEWHEEL, find out what message number we should be
211 if (osVersion
.dwMajorVersion
< 4 ||
212 (osVersion
.dwMajorVersion
== 4 &&
213 osVersion
.dwPlatformId
!= VER_PLATFORM_WIN32_NT
))
214 wm_mousewheel
= RegisterWindowMessage("MSWHEEL_ROLLMSG");
217 * See if we can find our Help file.
220 char b
[2048], *p
, *q
, *r
;
222 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
224 p
= strrchr(b
, '\\');
225 if (p
&& p
>= r
) r
= p
+1;
227 if (q
&& q
>= r
) r
= q
+1;
228 strcpy(r
, "putty.hlp");
229 if ( (fp
= fopen(b
, "r")) != NULL
) {
230 help_path
= dupstr(b
);
234 strcpy(r
, "putty.cnt");
235 if ( (fp
= fopen(b
, "r")) != NULL
) {
236 help_has_contents
= TRUE
;
239 help_has_contents
= FALSE
;
243 * Process the command line.
248 default_protocol
= DEFAULT_PROTOCOL
;
249 default_port
= DEFAULT_PORT
;
250 cfg
.logtype
= LGTYP_NONE
;
252 do_defaults(NULL
, &cfg
);
255 while (*p
&& isspace(*p
))
259 * Process command line options first. Yes, this can be
260 * done better, and it will be as soon as I have the
264 char *q
= p
+ strcspn(p
, " \t");
267 tolower(p
[0]) == 's' &&
268 tolower(p
[1]) == 's' && tolower(p
[2]) == 'h') {
269 default_protocol
= cfg
.protocol
= PROT_SSH
;
270 default_port
= cfg
.port
= 22;
271 } else if (q
== p
+ 7 &&
272 tolower(p
[0]) == 'c' &&
273 tolower(p
[1]) == 'l' &&
274 tolower(p
[2]) == 'e' &&
275 tolower(p
[3]) == 'a' &&
276 tolower(p
[4]) == 'n' &&
277 tolower(p
[5]) == 'u' && tolower(p
[6]) == 'p') {
279 * `putty -cleanup'. Remove all registry entries
280 * associated with PuTTY, and also find and delete
281 * the random seed file.
284 "This procedure will remove ALL Registry\n"
285 "entries associated with PuTTY, and will\n"
286 "also remove the PuTTY random seed file.\n"
288 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
289 "SESSIONS. Are you really sure you want\n"
292 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
297 p
= q
+ strspn(q
, " \t");
301 * An initial @ means to activate a saved session.
305 while (i
> 1 && isspace(p
[i
- 1]))
308 do_defaults(p
+ 1, &cfg
);
309 if (!*cfg
.host
&& !do_config()) {
313 } else if (*p
== '&') {
315 * An initial & means we've been given a command line
316 * containing the hex value of a HANDLE for a file
317 * mapping object, which we must then extract as a
322 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
323 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
324 0, 0, sizeof(Config
))) != NULL
) {
327 CloseHandle(filemap
);
328 } else if (!do_config()) {
335 * If the hostname starts with "telnet:", set the
336 * protocol to Telnet and process the string as a
339 if (!strncmp(q
, "telnet:", 7)) {
343 if (q
[0] == '/' && q
[1] == '/')
345 cfg
.protocol
= PROT_TELNET
;
347 while (*p
&& *p
!= ':' && *p
!= '/')
356 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
357 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
359 while (*p
&& !isspace(*p
))
363 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
364 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
365 while (*p
&& isspace(*p
))
380 * Trim leading whitespace off the hostname if it's there.
383 int space
= strspn(cfg
.host
, " \t");
384 memmove(cfg
.host
, cfg
.host
+space
, 1+strlen(cfg
.host
)-space
);
387 /* See if host is of the form user@host */
388 if (cfg
.host
[0] != '\0') {
389 char *atsign
= strchr(cfg
.host
, '@');
390 /* Make sure we're not overflowing the user field */
392 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
393 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
394 cfg
.username
[atsign
- cfg
.host
] = '\0';
396 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
401 * Trim a colon suffix off the hostname if it's there.
403 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
407 * Select protocol. This is farmed out into a table in a
408 * separate file to enable an ssh-free variant.
413 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
414 if (backends
[i
].protocol
== cfg
.protocol
) {
415 back
= backends
[i
].backend
;
419 MessageBox(NULL
, "Unsupported protocol number found",
420 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
426 /* Check for invalid Port number (i.e. zero) */
428 MessageBox(NULL
, "Invalid Port Number",
429 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
436 wndclass
.lpfnWndProc
= WndProc
;
437 wndclass
.cbClsExtra
= 0;
438 wndclass
.cbWndExtra
= 0;
439 wndclass
.hInstance
= inst
;
440 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
441 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
442 wndclass
.hbrBackground
= NULL
;
443 wndclass
.lpszMenuName
= NULL
;
444 wndclass
.lpszClassName
= appname
;
446 RegisterClass(&wndclass
);
451 savelines
= cfg
.savelines
;
457 * Guess some defaults for the window size. This all gets
458 * updated later, so we don't really care too much. However, we
459 * do want the font width/height guesses to correspond to a
460 * large font rather than a small one...
467 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
468 guess_width
= extra_width
+ font_width
* cols
;
469 guess_height
= extra_height
+ font_height
* rows
;
472 HWND w
= GetDesktopWindow();
473 GetWindowRect(w
, &r
);
474 if (guess_width
> r
.right
- r
.left
)
475 guess_width
= r
.right
- r
.left
;
476 if (guess_height
> r
.bottom
- r
.top
)
477 guess_height
= r
.bottom
- r
.top
;
481 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
484 winmode
&= ~(WS_VSCROLL
);
485 if (cfg
.resize_action
== RESIZE_DISABLED
)
486 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
488 exwinmode
|= WS_EX_TOPMOST
;
490 exwinmode
|= WS_EX_CLIENTEDGE
;
491 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
492 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
493 guess_width
, guess_height
,
494 NULL
, NULL
, inst
, NULL
);
498 * Initialise the fonts, simultaneously correcting the guesses
499 * for font_{width,height}.
504 * Correct the guesses for extra_{width,height}.
508 GetWindowRect(hwnd
, &wr
);
509 GetClientRect(hwnd
, &cr
);
510 offset_width
= offset_height
= cfg
.window_border
;
511 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
512 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
516 * Resize the window, now we know what size we _really_ want it
519 guess_width
= extra_width
+ font_width
* cols
;
520 guess_height
= extra_height
+ font_height
* rows
;
521 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
522 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
525 * Set up a caret bitmap, with no content.
529 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
530 bits
= smalloc(size
);
531 memset(bits
, 0, size
);
532 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
535 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
538 * Initialise the scroll bar.
543 si
.cbSize
= sizeof(si
);
544 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
549 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
553 * Start up the telnet connection.
557 char msg
[1024], *title
;
560 error
= back
->init(cfg
.host
, cfg
.port
, &realhost
, cfg
.tcp_nodelay
);
562 sprintf(msg
, "Unable to open connection to\n"
563 "%.800s\n" "%s", cfg
.host
, error
);
564 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
567 window_name
= icon_name
= NULL
;
569 title
= cfg
.wintitle
;
571 sprintf(msg
, "%s - PuTTY", realhost
);
579 session_closed
= FALSE
;
582 * Prepare the mouse handler.
584 lastact
= MA_NOTHING
;
585 lastbtn
= MBT_NOTHING
;
586 dbltime
= GetDoubleClickTime();
589 * Set up the session-control options on the system menu.
592 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
596 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
597 if (cfg
.protocol
== PROT_TELNET
) {
599 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
600 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
601 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
602 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
603 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
604 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
605 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
606 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
607 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
608 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
609 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
610 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
611 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
612 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
613 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
614 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
615 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
617 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
619 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
620 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
621 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
622 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
625 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
626 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
628 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
629 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
630 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
631 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
632 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
633 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
634 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
635 AppendMenu(m
, (cfg
.resize_action
== RESIZE_DISABLED
) ?
636 MF_GRAYED
: MF_ENABLED
, IDM_FULLSCREEN
, "&Full Screen");
637 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
639 AppendMenu(m
, MF_ENABLED
, IDM_HELP
, "&Help");
640 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
644 * Set up the initial input locale.
646 set_input_locale(GetKeyboardLayout(0));
649 * Open the initial log file if there is one.
654 * Finally show the window!
656 ShowWindow(hwnd
, show
);
657 SetForegroundWindow(hwnd
);
660 * Set the palette up.
666 has_focus
= (GetForegroundWindow() == hwnd
);
669 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
670 int timer_id
= 0, long_timer
= 0;
672 while (msg
.message
!= WM_QUIT
) {
673 /* Sometimes DispatchMessage calls routines that use their own
674 * GetMessage loop, setup this timer so we get some control back.
676 * Also call term_update() from the timer so that if the host
677 * is sending data flat out we still do redraws.
679 if (timer_id
&& long_timer
) {
680 KillTimer(hwnd
, timer_id
);
681 long_timer
= timer_id
= 0;
684 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
685 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
686 DispatchMessage(&msg
);
688 /* Make sure we blink everything that needs it. */
691 /* Send the paste buffer if there's anything to send */
694 /* If there's nothing new in the queue then we can do everything
695 * we've delayed, reading the socket, writing, and repainting
698 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
701 if (pending_netevent
) {
702 enact_pending_netevent();
704 /* Force the cursor blink on */
707 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
711 /* Okay there is now nothing to do so we make sure the screen is
712 * completely up to date then tell windows to call us in a little
716 KillTimer(hwnd
, timer_id
);
724 flash_window(1); /* maintain */
726 /* The messages seem unreliable; especially if we're being tricky */
727 has_focus
= (GetForegroundWindow() == hwnd
);
730 /* Hmm, term_update didn't want to do an update too soon ... */
731 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
733 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
735 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
738 /* There's no point rescanning everything in the message queue
739 * so we do an apparently unnecessary wait here
742 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
756 if (cfg
.protocol
== PROT_SSH
) {
767 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
769 char *do_select(SOCKET skt
, int startup
)
774 events
= (FD_CONNECT
| FD_READ
| FD_WRITE
|
775 FD_OOB
| FD_CLOSE
| FD_ACCEPT
);
780 return "do_select(): internal error (hwnd==NULL)";
781 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
782 switch (WSAGetLastError()) {
784 return "Network is down";
786 return "WSAAsyncSelect(): unknown error";
793 * set or clear the "raw mouse message" mode
795 void set_raw_mouse_mode(int activate
)
797 send_raw_mouse
= activate
;
798 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
802 * Print a message box and close the connection.
804 void connection_fatal(char *fmt
, ...)
810 vsprintf(stuff
, fmt
, ap
);
812 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
813 if (cfg
.close_on_exit
== COE_ALWAYS
)
816 session_closed
= TRUE
;
817 SetWindowText(hwnd
, "PuTTY (inactive)");
822 * Actually do the job requested by a WM_NETEVENT
824 static void enact_pending_netevent(void)
826 static int reentering
= 0;
827 extern int select_result(WPARAM
, LPARAM
);
831 return; /* don't unpend the pending */
833 pending_netevent
= FALSE
;
836 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
839 if (ret
== 0 && !session_closed
) {
840 /* Abnormal exits will already have set session_closed and taken
841 * appropriate action. */
842 if (cfg
.close_on_exit
== COE_ALWAYS
||
843 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
845 session_closed
= TRUE
;
846 SetWindowText(hwnd
, "PuTTY (inactive)");
847 MessageBox(hwnd
, "Connection closed by remote host",
848 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
854 * Copy the colour palette from the configuration data into defpal.
855 * This is non-trivial because the colour indices are different.
857 static void cfgtopalette(void)
860 static const int ww
[] = {
861 6, 7, 8, 9, 10, 11, 12, 13,
862 14, 15, 16, 17, 18, 19, 20, 21,
863 0, 1, 2, 3, 4, 4, 5, 5
866 for (i
= 0; i
< 24; i
++) {
868 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
869 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
870 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
875 * Set up the colour palette.
877 static void init_palette(void)
880 HDC hdc
= GetDC(hwnd
);
882 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
883 logpal
= smalloc(sizeof(*logpal
)
884 - sizeof(logpal
->palPalEntry
)
885 + NCOLOURS
* sizeof(PALETTEENTRY
));
886 logpal
->palVersion
= 0x300;
887 logpal
->palNumEntries
= NCOLOURS
;
888 for (i
= 0; i
< NCOLOURS
; i
++) {
889 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
890 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
891 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
892 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
894 pal
= CreatePalette(logpal
);
896 SelectPalette(hdc
, pal
, FALSE
);
898 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
901 ReleaseDC(hwnd
, hdc
);
904 for (i
= 0; i
< NCOLOURS
; i
++)
905 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
909 for (i
= 0; i
< NCOLOURS
; i
++)
910 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
911 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
915 * Initialise all the fonts we will need initially. There may be as many as
916 * three or as few as one. The other (poentially) twentyone fonts are done
917 * if/when they are needed.
921 * - check the font width and height, correcting our guesses if
924 * - verify that the bold font is the same width as the ordinary
925 * one, and engage shadow bolding if not.
927 * - verify that the underlined font is the same width as the
928 * ordinary one (manual underlining by means of line drawing can
929 * be done in a pinch).
931 static void init_fonts(int pick_width
, int pick_height
)
938 int fw_dontcare
, fw_bold
;
940 for (i
= 0; i
< FONT_MAXNO
; i
++)
943 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
946 if (cfg
.fontisbold
) {
947 fw_dontcare
= FW_BOLD
;
950 fw_dontcare
= FW_DONTCARE
;
957 font_height
= pick_height
;
959 font_height
= cfg
.fontheight
;
960 if (font_height
> 0) {
962 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
965 font_width
= pick_width
;
968 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
969 c, OUT_DEFAULT_PRECIS, \
970 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
971 FIXED_PITCH | FF_DONTCARE, cfg.font)
973 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
975 lfont
.lfHeight
= font_height
;
976 lfont
.lfWidth
= font_width
;
977 lfont
.lfEscapement
= 0;
978 lfont
.lfOrientation
= 0;
979 lfont
.lfWeight
= fw_dontcare
;
980 lfont
.lfItalic
= FALSE
;
981 lfont
.lfUnderline
= FALSE
;
982 lfont
.lfStrikeOut
= FALSE
;
983 lfont
.lfCharSet
= cfg
.fontcharset
;
984 lfont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
985 lfont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
986 lfont
.lfQuality
= DEFAULT_QUALITY
;
987 lfont
.lfPitchAndFamily
= FIXED_PITCH
| FF_DONTCARE
;
988 strncpy(lfont
.lfFaceName
, cfg
.font
, LF_FACESIZE
);
990 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
991 GetTextMetrics(hdc
, &tm
);
993 if (pick_width
== 0 || pick_height
== 0) {
994 font_height
= tm
.tmHeight
;
995 font_width
= tm
.tmAveCharWidth
;
997 font_dualwidth
= (tm
.tmAveCharWidth
!= tm
.tmMaxCharWidth
);
999 #ifdef RDB_DEBUG_PATCH
1000 debug(23, "Primary font H=%d, AW=%d, MW=%d",
1001 tm
.tmHeight
, tm
.tmAveCharWidth
, tm
.tmMaxCharWidth
);
1006 DWORD cset
= tm
.tmCharSet
;
1007 memset(&info
, 0xFF, sizeof(info
));
1009 /* !!! Yes the next line is right */
1010 if (cset
== OEM_CHARSET
)
1011 font_codepage
= GetOEMCP();
1013 if (TranslateCharsetInfo
1014 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
1019 GetCPInfo(font_codepage
, &cpinfo
);
1020 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
1023 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
1026 * Some fonts, e.g. 9-pt Courier, draw their underlines
1027 * outside their character cell. We successfully prevent
1028 * screen corruption by clipping the text output, but then
1029 * we lose the underline completely. Here we try to work
1030 * out whether this is such a font, and if it is, we set a
1031 * flag that causes underlines to be drawn by hand.
1033 * Having tried other more sophisticated approaches (such
1034 * as examining the TEXTMETRIC structure or requesting the
1035 * height of a string), I think we'll do this the brute
1036 * force way: we create a small bitmap, draw an underlined
1037 * space on it, and test to see whether any pixels are
1038 * foreground-coloured. (Since we expect the underline to
1039 * go all the way across the character cell, we only search
1040 * down a single column of the bitmap, half way across.)
1044 HBITMAP und_bm
, und_oldbm
;
1048 und_dc
= CreateCompatibleDC(hdc
);
1049 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
1050 und_oldbm
= SelectObject(und_dc
, und_bm
);
1051 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
1052 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
1053 SetTextColor(und_dc
, RGB(255, 255, 255));
1054 SetBkColor(und_dc
, RGB(0, 0, 0));
1055 SetBkMode(und_dc
, OPAQUE
);
1056 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
1058 for (i
= 0; i
< font_height
; i
++) {
1059 c
= GetPixel(und_dc
, font_width
/ 2, i
);
1060 if (c
!= RGB(0, 0, 0))
1063 SelectObject(und_dc
, und_oldbm
);
1064 DeleteObject(und_bm
);
1067 und_mode
= UND_LINE
;
1068 DeleteObject(fonts
[FONT_UNDERLINE
]);
1069 fonts
[FONT_UNDERLINE
] = 0;
1073 if (bold_mode
== BOLD_FONT
) {
1074 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
1078 descent
= tm
.tmAscent
+ 1;
1079 if (descent
>= font_height
)
1080 descent
= font_height
- 1;
1082 for (i
= 0; i
< 3; i
++) {
1084 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
1085 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
1092 ReleaseDC(hwnd
, hdc
);
1094 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1095 und_mode
= UND_LINE
;
1096 DeleteObject(fonts
[FONT_UNDERLINE
]);
1097 fonts
[FONT_UNDERLINE
] = 0;
1100 if (bold_mode
== BOLD_FONT
&&
1101 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1102 bold_mode
= BOLD_SHADOW
;
1103 DeleteObject(fonts
[FONT_BOLD
]);
1104 fonts
[FONT_BOLD
] = 0;
1106 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1111 static void another_font(int fontno
)
1114 int fw_dontcare
, fw_bold
;
1118 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1121 basefont
= (fontno
& ~(FONT_BOLDUND
));
1122 if (basefont
!= fontno
&& !fontflag
[basefont
])
1123 another_font(basefont
);
1125 if (cfg
.fontisbold
) {
1126 fw_dontcare
= FW_BOLD
;
1129 fw_dontcare
= FW_DONTCARE
;
1133 c
= cfg
.fontcharset
;
1139 if (fontno
& FONT_WIDE
)
1141 if (fontno
& FONT_NARROW
)
1143 if (fontno
& FONT_OEM
)
1145 if (fontno
& FONT_BOLD
)
1147 if (fontno
& FONT_UNDERLINE
)
1151 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1152 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1153 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1154 FIXED_PITCH
| FF_DONTCARE
, s
);
1156 fontflag
[fontno
] = 1;
1159 static void deinit_fonts(void)
1162 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1164 DeleteObject(fonts
[i
]);
1170 void request_resize(int w
, int h
)
1174 /* If the window is maximized supress resizing attempts */
1175 if (IsZoomed(hwnd
)) {
1176 if (cfg
.resize_action
== RESIZE_TERM
)
1180 if (cfg
.resize_action
== RESIZE_DISABLED
) return;
1181 if (h
== rows
&& w
== cols
) return;
1183 /* Sanity checks ... */
1185 static int first_time
= 1;
1188 switch (first_time
) {
1190 /* Get the size of the screen */
1191 if (GetClientRect(GetDesktopWindow(), &ss
))
1192 /* first_time = 0 */ ;
1198 /* Make sure the values are sane */
1199 width
= (ss
.right
- ss
.left
- extra_width
) / 4;
1200 height
= (ss
.bottom
- ss
.top
- extra_height
) / 6;
1202 if (w
> width
|| h
> height
)
1211 term_size(h
, w
, cfg
.savelines
);
1213 if (cfg
.resize_action
!= RESIZE_FONT
&& !IsZoomed(hwnd
)) {
1214 width
= extra_width
+ font_width
* w
;
1215 height
= extra_height
+ font_height
* h
;
1217 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1218 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1219 SWP_NOMOVE
| SWP_NOZORDER
);
1223 InvalidateRect(hwnd
, NULL
, TRUE
);
1226 static void reset_window(int reinit
) {
1228 * This function decides how to resize or redraw when the
1229 * user changes something.
1231 * This function doesn't like to change the terminal size but if the
1232 * font size is locked that may be it's only soluion.
1234 int win_width
, win_height
;
1237 #ifdef RDB_DEBUG_PATCH
1238 debug((27, "reset_window()"));
1241 /* Current window sizes ... */
1242 GetWindowRect(hwnd
, &wr
);
1243 GetClientRect(hwnd
, &cr
);
1245 win_width
= cr
.right
- cr
.left
;
1246 win_height
= cr
.bottom
- cr
.top
;
1248 if (cfg
.resize_action
== RESIZE_DISABLED
) reinit
= 2;
1250 /* Are we being forced to reload the fonts ? */
1252 #ifdef RDB_DEBUG_PATCH
1253 debug((27, "reset_window() -- Forced deinit"));
1259 /* Oh, looks like we're minimised */
1260 if (win_width
== 0 || win_height
== 0)
1263 /* Is the window out of position ? */
1265 (offset_width
!= (win_width
-font_width
*cols
)/2 ||
1266 offset_height
!= (win_height
-font_height
*rows
)/2) ){
1267 offset_width
= (win_width
-font_width
*cols
)/2;
1268 offset_height
= (win_height
-font_height
*rows
)/2;
1269 InvalidateRect(hwnd
, NULL
, TRUE
);
1270 #ifdef RDB_DEBUG_PATCH
1271 debug((27, "reset_window() -> Reposition terminal"));
1275 if (IsZoomed(hwnd
)) {
1276 /* We're fullscreen, this means we must not change the size of
1277 * the window so it's the font size or the terminal itself.
1280 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1281 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1283 if (cfg
.resize_action
!= RESIZE_TERM
) {
1284 if ( font_width
!= win_width
/cols
||
1285 font_height
!= win_height
/rows
) {
1287 init_fonts(win_width
/cols
, win_height
/rows
);
1288 offset_width
= (win_width
-font_width
*cols
)/2;
1289 offset_height
= (win_height
-font_height
*rows
)/2;
1290 InvalidateRect(hwnd
, NULL
, TRUE
);
1291 #ifdef RDB_DEBUG_PATCH
1292 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1293 font_width
, font_height
));
1297 if ( font_width
!= win_width
/cols
||
1298 font_height
!= win_height
/rows
) {
1299 /* Our only choice at this point is to change the
1300 * size of the terminal; Oh well.
1302 term_size( win_height
/font_height
, win_width
/font_width
,
1304 offset_width
= (win_width
-font_width
*cols
)/2;
1305 offset_height
= (win_height
-font_height
*rows
)/2;
1306 InvalidateRect(hwnd
, NULL
, TRUE
);
1307 #ifdef RDB_DEBUG_PATCH
1308 debug((27, "reset_window() -> Zoomed term_size"));
1315 /* Hmm, a force re-init means we should ignore the current window
1316 * so we resize to the default font size.
1319 #ifdef RDB_DEBUG_PATCH
1320 debug((27, "reset_window() -> Forced re-init"));
1323 offset_width
= offset_height
= cfg
.window_border
;
1324 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1325 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1327 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1328 win_height
!= font_height
*rows
+ offset_height
*2) {
1330 /* If this is too large windows will resize it to the maximum
1331 * allowed window size, we will then be back in here and resize
1332 * the font or terminal to fit.
1334 SetWindowPos(hwnd
, NULL
, 0, 0,
1335 font_width
*cols
+ extra_width
,
1336 font_height
*rows
+ extra_height
,
1337 SWP_NOMOVE
| SWP_NOZORDER
);
1340 InvalidateRect(hwnd
, NULL
, TRUE
);
1344 /* Okay the user doesn't want us to change the font so we try the
1345 * window. But that may be too big for the screen which forces us
1346 * to change the terminal.
1348 if ((cfg
.resize_action
== RESIZE_TERM
&& reinit
<=0) ||
1349 (cfg
.resize_action
== RESIZE_EITHER
&& reinit
<0) ||
1351 offset_width
= offset_height
= cfg
.window_border
;
1352 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1353 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1355 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1356 win_height
!= font_height
*rows
+ offset_height
*2) {
1361 GetClientRect(GetDesktopWindow(), &ss
);
1362 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1363 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1366 if ( rows
> height
|| cols
> width
) {
1367 if (cfg
.resize_action
== RESIZE_EITHER
) {
1368 /* Make the font the biggest we can */
1370 font_width
= (ss
.right
- ss
.left
- extra_width
)/cols
;
1372 font_height
= (ss
.bottom
- ss
.top
- extra_height
)/rows
;
1375 init_fonts(font_width
, font_height
);
1377 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1378 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1380 if ( height
> rows
) height
= rows
;
1381 if ( width
> cols
) width
= cols
;
1382 term_size(height
, width
, cfg
.savelines
);
1383 #ifdef RDB_DEBUG_PATCH
1384 debug((27, "reset_window() -> term resize to (%d,%d)",
1390 SetWindowPos(hwnd
, NULL
, 0, 0,
1391 font_width
*cols
+ extra_width
,
1392 font_height
*rows
+ extra_height
,
1393 SWP_NOMOVE
| SWP_NOZORDER
);
1395 InvalidateRect(hwnd
, NULL
, TRUE
);
1396 #ifdef RDB_DEBUG_PATCH
1397 debug((27, "reset_window() -> window resize to (%d,%d)",
1398 font_width
*cols
+ extra_width
,
1399 font_height
*rows
+ extra_height
));
1405 /* We're allowed to or must change the font but do we want to ? */
1407 if (font_width
!= (win_width
-cfg
.window_border
*2)/cols
||
1408 font_height
!= (win_height
-cfg
.window_border
*2)/rows
) {
1411 init_fonts((win_width
-cfg
.window_border
*2)/cols
,
1412 (win_height
-cfg
.window_border
*2)/rows
);
1413 offset_width
= (win_width
-font_width
*cols
)/2;
1414 offset_height
= (win_height
-font_height
*rows
)/2;
1416 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+offset_width
*2;
1417 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1419 InvalidateRect(hwnd
, NULL
, TRUE
);
1420 #ifdef RDB_DEBUG_PATCH
1421 debug((25, "reset_window() -> font resize to (%d,%d)",
1422 font_width
, font_height
));
1427 static void set_input_locale(HKL kl
)
1431 GetLocaleInfo(LOWORD(kl
), LOCALE_IDEFAULTANSICODEPAGE
,
1432 lbuf
, sizeof(lbuf
));
1434 kbd_codepage
= atoi(lbuf
);
1437 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
, int alt
)
1439 int thistime
= GetMessageTime();
1441 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1442 lastbtn
= MBT_NOTHING
;
1443 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
, alt
);
1447 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1448 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1449 lastact
== MA_2CLK ? MA_3CLK
:
1450 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1455 if (lastact
!= MA_NOTHING
)
1456 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
, alt
);
1457 lasttime
= thistime
;
1461 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1462 * into a cooked one (SELECT, EXTEND, PASTE).
1464 Mouse_Button
translate_button(Mouse_Button button
)
1466 if (button
== MBT_LEFT
)
1468 if (button
== MBT_MIDDLE
)
1469 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1470 if (button
== MBT_RIGHT
)
1471 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1472 return 0; /* shouldn't happen */
1475 static void show_mouseptr(int show
)
1477 static int cursor_visible
= 1;
1478 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1480 if (cursor_visible
&& !show
)
1482 else if (!cursor_visible
&& show
)
1484 cursor_visible
= show
;
1487 static int is_alt_pressed(void)
1490 int r
= GetKeyboardState(keystate
);
1493 if (keystate
[VK_MENU
] & 0x80)
1495 if (keystate
[VK_RMENU
] & 0x80)
1500 static int resizing
;
1502 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1503 WPARAM wParam
, LPARAM lParam
)
1506 static int ignore_clip
= FALSE
;
1507 static int need_backend_resize
= FALSE
;
1508 static int fullscr_on_max
= FALSE
;
1512 if (pending_netevent
)
1513 enact_pending_netevent();
1519 if (cfg
.ping_interval
> 0) {
1522 if (now
- last_movement
> cfg
.ping_interval
) {
1523 back
->special(TS_PING
);
1524 last_movement
= now
;
1527 net_pending_errors();
1533 if (!cfg
.warn_on_close
|| session_closed
||
1535 "Are you sure you want to close this session?",
1536 "PuTTY Exit Confirmation",
1537 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1538 DestroyWindow(hwnd
);
1545 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1557 PROCESS_INFORMATION pi
;
1558 HANDLE filemap
= NULL
;
1560 if (wParam
== IDM_DUPSESS
) {
1562 * Allocate a file-mapping memory chunk for the
1565 SECURITY_ATTRIBUTES sa
;
1568 sa
.nLength
= sizeof(sa
);
1569 sa
.lpSecurityDescriptor
= NULL
;
1570 sa
.bInheritHandle
= TRUE
;
1571 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1574 0, sizeof(Config
), NULL
);
1576 p
= (Config
*) MapViewOfFile(filemap
,
1578 0, 0, sizeof(Config
));
1580 *p
= cfg
; /* structure copy */
1584 sprintf(c
, "putty &%p", filemap
);
1586 } else if (wParam
== IDM_SAVEDSESS
) {
1587 if ((lParam
- IDM_SAVED_MIN
) / 16 < nsessions
) {
1589 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1590 cl
= smalloc(16 + strlen(session
));
1591 /* 8, but play safe */
1594 /* not a very important failure mode */
1596 sprintf(cl
, "putty @%s", session
);
1604 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1606 si
.lpReserved
= NULL
;
1607 si
.lpDesktop
= NULL
;
1611 si
.lpReserved2
= NULL
;
1612 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1613 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1616 CloseHandle(filemap
);
1626 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1629 if (!do_reconfig(hwnd
))
1633 /* Disable full-screen if resizing forbidden */
1634 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
1635 EnableMenuItem(m
, IDM_FULLSCREEN
, MF_BYCOMMAND
|
1636 (cfg
.resize_action
== RESIZE_DISABLED
)
1637 ? MF_GRAYED
: MF_ENABLED
);
1638 /* Gracefully unzoom if necessary */
1639 if (IsZoomed(hwnd
) &&
1640 (cfg
.resize_action
== RESIZE_DISABLED
)) {
1641 ShowWindow(hwnd
, SW_RESTORE
);
1645 if (strcmp(prev_cfg
.logfilename
, cfg
.logfilename
) ||
1646 prev_cfg
.logtype
!= cfg
.logtype
) {
1647 logfclose(); /* reset logging */
1653 * Flush the line discipline's edit buffer in the
1654 * case where local editing has just been disabled.
1656 ldisc_send(NULL
, 0, 0);
1664 /* Screen size changed ? */
1665 if (cfg
.height
!= prev_cfg
.height
||
1666 cfg
.width
!= prev_cfg
.width
||
1667 cfg
.savelines
!= prev_cfg
.savelines
||
1668 cfg
.resize_action
== RESIZE_FONT
||
1669 (cfg
.resize_action
== RESIZE_EITHER
&& IsZoomed(hwnd
)) ||
1670 cfg
.resize_action
== RESIZE_DISABLED
)
1671 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1673 /* Enable or disable the scroll bar, etc */
1675 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1676 LONG nexflag
, exflag
=
1677 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1680 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1681 if (cfg
.alwaysontop
) {
1682 nexflag
|= WS_EX_TOPMOST
;
1683 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1684 SWP_NOMOVE
| SWP_NOSIZE
);
1686 nexflag
&= ~(WS_EX_TOPMOST
);
1687 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1688 SWP_NOMOVE
| SWP_NOSIZE
);
1691 if (cfg
.sunken_edge
)
1692 nexflag
|= WS_EX_CLIENTEDGE
;
1694 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1697 if (is_full_screen() ?
1698 cfg
.scrollbar_in_fullscreen
: cfg
.scrollbar
)
1701 nflg
&= ~WS_VSCROLL
;
1703 if (cfg
.resize_action
== RESIZE_DISABLED
||
1705 nflg
&= ~WS_THICKFRAME
;
1707 nflg
|= WS_THICKFRAME
;
1709 if (cfg
.resize_action
== RESIZE_DISABLED
)
1710 nflg
&= ~WS_MAXIMIZEBOX
;
1712 nflg
|= WS_MAXIMIZEBOX
;
1714 if (nflg
!= flag
|| nexflag
!= exflag
) {
1716 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1717 if (nexflag
!= exflag
)
1718 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1720 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1721 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1722 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1730 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1735 set_title(cfg
.wintitle
);
1736 if (IsIconic(hwnd
)) {
1738 cfg
.win_name_always ? window_name
:
1742 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1743 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1744 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1745 cfg
.fontheight
!= prev_cfg
.fontheight
||
1746 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1747 cfg
.vtmode
!= prev_cfg
.vtmode
||
1748 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1749 cfg
.resize_action
== RESIZE_DISABLED
||
1750 cfg
.resize_action
== RESIZE_EITHER
||
1751 (cfg
.resize_action
!= prev_cfg
.resize_action
))
1754 InvalidateRect(hwnd
, NULL
, TRUE
);
1755 reset_window(init_lvl
);
1756 net_pending_errors();
1769 back
->special(TS_AYT
);
1770 net_pending_errors();
1773 back
->special(TS_BRK
);
1774 net_pending_errors();
1777 back
->special(TS_SYNCH
);
1778 net_pending_errors();
1781 back
->special(TS_EC
);
1782 net_pending_errors();
1785 back
->special(TS_EL
);
1786 net_pending_errors();
1789 back
->special(TS_GA
);
1790 net_pending_errors();
1793 back
->special(TS_NOP
);
1794 net_pending_errors();
1797 back
->special(TS_ABORT
);
1798 net_pending_errors();
1801 back
->special(TS_AO
);
1802 net_pending_errors();
1805 back
->special(TS_IP
);
1806 net_pending_errors();
1809 back
->special(TS_SUSP
);
1810 net_pending_errors();
1813 back
->special(TS_EOR
);
1814 net_pending_errors();
1817 back
->special(TS_EOF
);
1818 net_pending_errors();
1824 WinHelp(hwnd
, help_path
,
1825 help_has_contents ? HELP_FINDER
: HELP_CONTENTS
, 0);
1829 * We get this if the System menu has been activated
1836 * We get this if the System menu has been activated
1837 * using the keyboard. This might happen from within
1838 * TranslateKey, in which case it really wants to be
1839 * followed by a `space' character to actually _bring
1840 * the menu up_ rather than just sitting there in
1841 * `ready to appear' state.
1843 show_mouseptr(1); /* make sure pointer is visible */
1845 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1847 case IDM_FULLSCREEN
:
1851 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1852 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1857 #define X_POS(l) ((int)(short)LOWORD(l))
1858 #define Y_POS(l) ((int)(short)HIWORD(l))
1860 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1861 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1862 case WM_LBUTTONDOWN
:
1863 case WM_MBUTTONDOWN
:
1864 case WM_RBUTTONDOWN
:
1872 case WM_LBUTTONDOWN
:
1876 case WM_MBUTTONDOWN
:
1877 button
= MBT_MIDDLE
;
1880 case WM_RBUTTONDOWN
:
1889 button
= MBT_MIDDLE
;
1897 button
= press
= 0; /* shouldn't happen */
1901 * Special case: in full-screen mode, if the left
1902 * button is clicked in the very top left corner of the
1903 * window, we put up the System menu instead of doing
1906 if (is_full_screen() && press
&& button
== MBT_LEFT
&&
1907 X_POS(lParam
) == 0 && Y_POS(lParam
) == 0) {
1908 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_MOUSEMENU
, 0);
1913 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1914 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
,
1918 term_mouse(button
, MA_RELEASE
,
1919 TO_CHR_X(X_POS(lParam
)),
1920 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1921 wParam
& MK_CONTROL
, is_alt_pressed());
1929 * Add the mouse position and message time to the random
1932 noise_ultralight(lParam
);
1934 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1936 if (wParam
& MK_LBUTTON
)
1938 else if (wParam
& MK_MBUTTON
)
1942 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1943 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1944 wParam
& MK_CONTROL
, is_alt_pressed());
1947 case WM_NCMOUSEMOVE
:
1949 noise_ultralight(lParam
);
1951 case WM_IGNORE_CLIP
:
1952 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1954 case WM_DESTROYCLIPBOARD
:
1957 ignore_clip
= FALSE
;
1963 hdc
= BeginPaint(hwnd
, &p
);
1965 SelectPalette(hdc
, pal
, TRUE
);
1966 RealizePalette(hdc
);
1969 (p
.rcPaint
.left
-offset_width
)/font_width
,
1970 (p
.rcPaint
.top
-offset_height
)/font_height
,
1971 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
1972 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
1975 p
.rcPaint
.left
< offset_width
||
1976 p
.rcPaint
.top
< offset_height
||
1977 p
.rcPaint
.right
>= offset_width
+ font_width
*cols
||
1978 p
.rcPaint
.bottom
>= offset_height
+ font_height
*rows
)
1980 HBRUSH fillcolour
, oldbrush
;
1982 fillcolour
= CreateSolidBrush (
1983 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1984 oldbrush
= SelectObject(hdc
, fillcolour
);
1985 edge
= CreatePen(PS_SOLID
, 0,
1986 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1987 oldpen
= SelectObject(hdc
, edge
);
1989 ExcludeClipRect(hdc
,
1990 offset_width
, offset_height
,
1991 offset_width
+font_width
*cols
,
1992 offset_height
+font_height
*rows
);
1994 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1995 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1997 // SelectClipRgn(hdc, NULL);
1999 SelectObject(hdc
, oldbrush
);
2000 DeleteObject(fillcolour
);
2001 SelectObject(hdc
, oldpen
);
2004 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
2005 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
2011 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2012 * but the only one that's likely to try to overload us is FD_READ.
2013 * This means buffering just one is fine.
2015 if (pending_netevent
)
2016 enact_pending_netevent();
2018 pending_netevent
= TRUE
;
2019 pend_netevent_wParam
= wParam
;
2020 pend_netevent_lParam
= lParam
;
2021 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
2022 enact_pending_netevent();
2024 time(&last_movement
);
2028 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
2030 flash_window(0); /* stop */
2042 case WM_ENTERSIZEMOVE
:
2043 #ifdef RDB_DEBUG_PATCH
2044 debug((27, "WM_ENTERSIZEMOVE"));
2048 need_backend_resize
= FALSE
;
2050 case WM_EXITSIZEMOVE
:
2053 #ifdef RDB_DEBUG_PATCH
2054 debug((27, "WM_EXITSIZEMOVE"));
2056 if (need_backend_resize
) {
2057 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
2058 InvalidateRect(hwnd
, NULL
, TRUE
);
2063 * This does two jobs:
2064 * 1) Keep the sizetip uptodate
2065 * 2) Make sure the window size is _stepped_ in units of the font size.
2067 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2068 int width
, height
, w
, h
, ew
, eh
;
2069 LPRECT r
= (LPRECT
) lParam
;
2071 if ( !need_backend_resize
&& cfg
.resize_action
== RESIZE_EITHER
&&
2072 (cfg
.height
!= rows
|| cfg
.width
!= cols
)) {
2074 * Great! It seems that both the terminal size and the
2075 * font size have been changed and the user is now dragging.
2077 * It will now be difficult to get back to the configured
2080 * This would be easier but it seems to be too confusing.
2082 term_size(cfg.height, cfg.width, cfg.savelines);
2085 cfg
.height
=rows
; cfg
.width
=cols
;
2087 InvalidateRect(hwnd
, NULL
, TRUE
);
2088 need_backend_resize
= TRUE
;
2091 width
= r
->right
- r
->left
- extra_width
;
2092 height
= r
->bottom
- r
->top
- extra_height
;
2093 w
= (width
+ font_width
/ 2) / font_width
;
2096 h
= (height
+ font_height
/ 2) / font_height
;
2099 UpdateSizeTip(hwnd
, w
, h
);
2100 ew
= width
- w
* font_width
;
2101 eh
= height
- h
* font_height
;
2103 if (wParam
== WMSZ_LEFT
||
2104 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2110 if (wParam
== WMSZ_TOP
||
2111 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2121 int width
, height
, w
, h
, rv
= 0;
2122 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
2123 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2124 LPRECT r
= (LPRECT
) lParam
;
2126 width
= r
->right
- r
->left
- ex_width
;
2127 height
= r
->bottom
- r
->top
- ex_height
;
2129 w
= (width
+ cols
/2)/cols
;
2130 h
= (height
+ rows
/2)/rows
;
2131 if ( r
->right
!= r
->left
+ w
*cols
+ ex_width
)
2134 if (wParam
== WMSZ_LEFT
||
2135 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2136 r
->left
= r
->right
- w
*cols
- ex_width
;
2138 r
->right
= r
->left
+ w
*cols
+ ex_width
;
2140 if (r
->bottom
!= r
->top
+ h
*rows
+ ex_height
)
2143 if (wParam
== WMSZ_TOP
||
2144 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2145 r
->top
= r
->bottom
- h
*rows
- ex_height
;
2147 r
->bottom
= r
->top
+ h
*rows
+ ex_height
;
2151 /* break; (never reached) */
2152 case WM_FULLSCR_ON_MAX
:
2153 fullscr_on_max
= TRUE
;
2156 #ifdef RDB_DEBUG_PATCH
2157 debug((27, "WM_SIZE %s (%d,%d)",
2158 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2159 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2160 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2161 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2163 LOWORD(lParam
), HIWORD(lParam
)));
2165 if (wParam
== SIZE_MINIMIZED
)
2167 cfg
.win_name_always ? window_name
: icon_name
);
2168 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2169 SetWindowText(hwnd
, window_name
);
2170 if (wParam
== SIZE_RESTORED
)
2171 clear_full_screen();
2172 if (wParam
== SIZE_MAXIMIZED
&& fullscr_on_max
) {
2174 fullscr_on_max
= FALSE
;
2177 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2178 /* A resize, well it better be a minimize. */
2182 int width
, height
, w
, h
;
2184 width
= LOWORD(lParam
);
2185 height
= HIWORD(lParam
);
2188 if (wParam
== SIZE_MAXIMIZED
&& !was_zoomed
) {
2192 if (cfg
.resize_action
== RESIZE_TERM
) {
2193 w
= width
/ font_width
;
2195 h
= height
/ font_height
;
2198 term_size(h
, w
, cfg
.savelines
);
2201 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2203 if (cfg
.resize_action
== RESIZE_TERM
)
2204 term_size(prev_rows
, prev_cols
, cfg
.savelines
);
2205 if (cfg
.resize_action
!= RESIZE_FONT
)
2210 /* This is an unexpected resize, these will normally happen
2211 * if the window is too large. Probably either the user
2212 * selected a huge font or the screen size has changed.
2214 * This is also called with minimize.
2216 else reset_window(-1);
2220 * Don't call back->size in mid-resize. (To prevent
2221 * massive numbers of resize events getting sent
2222 * down the connection during an NT opaque drag.)
2225 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2226 need_backend_resize
= TRUE
;
2227 w
= (width
-cfg
.window_border
*2) / font_width
;
2229 h
= (height
-cfg
.window_border
*2) / font_height
;
2240 switch (LOWORD(wParam
)) {
2254 term_scroll(0, +rows
/ 2);
2257 term_scroll(0, -rows
/ 2);
2259 case SB_THUMBPOSITION
:
2261 term_scroll(1, HIWORD(wParam
));
2265 case WM_PALETTECHANGED
:
2266 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2267 HDC hdc
= get_ctx();
2269 if (RealizePalette(hdc
) > 0)
2275 case WM_QUERYNEWPALETTE
:
2277 HDC hdc
= get_ctx();
2279 if (RealizePalette(hdc
) > 0)
2291 * Add the scan code and keypress timing to the random
2294 noise_ultralight(lParam
);
2297 * We don't do TranslateMessage since it disassociates the
2298 * resulting CHAR message from the KEYDOWN that sparked it,
2299 * which we occasionally don't want. Instead, we process
2300 * KEYDOWN, and call the Win32 translator functions so that
2301 * we get the translations under _our_ control.
2304 unsigned char buf
[20];
2307 if (wParam
== VK_PROCESSKEY
) {
2310 m
.message
= WM_KEYDOWN
;
2312 m
.lParam
= lParam
& 0xdfff;
2313 TranslateMessage(&m
);
2315 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2317 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2321 * Interrupt an ongoing paste. I'm not sure
2322 * this is sensible, but for the moment it's
2323 * preferable to having to faff about buffering
2329 * We need not bother about stdin backlogs
2330 * here, because in GUI PuTTY we can't do
2331 * anything about it anyway; there's no means
2332 * of asking Windows to hold off on KEYDOWN
2333 * messages. We _have_ to buffer everything
2336 ldisc_send(buf
, len
, 1);
2341 net_pending_errors();
2343 case WM_INPUTLANGCHANGE
:
2344 /* wParam == Font number */
2345 /* lParam == Locale */
2346 set_input_locale((HKL
)lParam
);
2349 if(wParam
== IMN_SETOPENSTATUS
) {
2350 HIMC hImc
= ImmGetContext(hwnd
);
2351 ImmSetCompositionFont(hImc
, &lfont
);
2352 ImmReleaseContext(hwnd
, hImc
);
2356 case WM_IME_COMPOSITION
:
2362 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2363 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2365 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2366 break; /* fall back to DefWindowProc */
2368 hIMC
= ImmGetContext(hwnd
);
2369 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2373 buff
= (char*) smalloc(n
);
2374 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2376 * Jaeyoun Chung reports that Korean character
2377 * input doesn't work correctly if we do a single
2378 * luni_send() covering the whole of buff. So
2379 * instead we luni_send the characters one by one.
2381 for (i
= 0; i
< n
; i
+= 2)
2382 luni_send((unsigned short *)(buff
+i
), 1, 1);
2385 ImmReleaseContext(hwnd
, hIMC
);
2390 if (wParam
& 0xFF00) {
2391 unsigned char buf
[2];
2394 buf
[0] = wParam
>> 8;
2395 lpage_send(kbd_codepage
, buf
, 2, 1);
2397 char c
= (unsigned char) wParam
;
2398 lpage_send(kbd_codepage
, &c
, 1, 1);
2404 * Nevertheless, we are prepared to deal with WM_CHAR
2405 * messages, should they crop up. So if someone wants to
2406 * post the things to us as part of a macro manoeuvre,
2407 * we're ready to cope.
2410 char c
= (unsigned char)wParam
;
2411 lpage_send(CP_ACP
, &c
, 1, 1);
2415 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2416 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2420 if (message
== wm_mousewheel
) {
2421 int shift_pressed
=0, control_pressed
=0, alt_pressed
=0;
2423 if (message
== WM_MOUSEWHEEL
) {
2424 wheel_accumulator
+= (short)HIWORD(wParam
);
2425 shift_pressed
=LOWORD(wParam
) & MK_SHIFT
;
2426 control_pressed
=LOWORD(wParam
) & MK_CONTROL
;
2429 wheel_accumulator
+= (int)wParam
;
2430 if (GetKeyboardState(keys
)!=0) {
2431 shift_pressed
=keys
[VK_SHIFT
]&0x80;
2432 control_pressed
=keys
[VK_CONTROL
]&0x80;
2436 /* process events when the threshold is reached */
2437 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
2440 /* reduce amount for next time */
2441 if (wheel_accumulator
> 0) {
2443 wheel_accumulator
-= WHEEL_DELTA
;
2444 } else if (wheel_accumulator
< 0) {
2446 wheel_accumulator
+= WHEEL_DELTA
;
2450 if (send_raw_mouse
&&
2451 !(cfg
.mouse_override
&& shift_pressed
)) {
2452 /* send a mouse-down followed by a mouse up */
2455 TO_CHR_X(X_POS(lParam
)),
2456 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2457 control_pressed
, is_alt_pressed());
2458 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
2459 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2460 control_pressed
, is_alt_pressed());
2462 /* trigger a scroll */
2464 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
2471 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2475 * Move the system caret. (We maintain one, even though it's
2476 * invisible, for the benefit of blind people: apparently some
2477 * helper software tracks the system caret, so we should arrange to
2480 void sys_cursor(int x
, int y
)
2485 if (!has_focus
) return;
2487 SetCaretPos(x
* font_width
+ offset_width
,
2488 y
* font_height
+ offset_height
);
2490 /* IMM calls on Win98 and beyond only */
2491 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2493 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2494 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2496 /* we should have the IMM functions */
2497 hIMC
= ImmGetContext(hwnd
);
2498 cf
.dwStyle
= CFS_POINT
;
2499 cf
.ptCurrentPos
.x
= x
* font_width
+ offset_width
;
2500 cf
.ptCurrentPos
.y
= y
* font_height
+ offset_height
;
2501 ImmSetCompositionWindow(hIMC
, &cf
);
2503 ImmReleaseContext(hwnd
, hIMC
);
2507 * Draw a line of text in the window, at given character
2508 * coordinates, in given attributes.
2510 * We are allowed to fiddle with the contents of `text'.
2512 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2513 unsigned long attr
, int lattr
)
2516 int nfg
, nbg
, nfont
;
2519 int force_manual_underline
= 0;
2520 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2521 int char_width
= fnt_width
;
2522 int text_adjust
= 0;
2523 static int *IpDx
= 0, IpDxLEN
= 0;
2525 if (attr
& ATTR_WIDE
)
2528 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2530 if (len
> IpDxLEN
) {
2532 IpDx
= smalloc((len
+ 16) * sizeof(int));
2533 IpDxLEN
= (len
+ 16);
2535 for (i
= 0; i
< IpDxLEN
; i
++)
2536 IpDx
[i
] = char_width
;
2539 /* Only want the left half of double width lines */
2540 if (lattr
!= LATTR_NORM
&& x
*2 >= cols
)
2548 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
2549 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2550 attr
^= ATTR_CUR_XOR
;
2554 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2555 /* Assume a poorman font is borken in other ways too. */
2565 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2568 if (attr
& ATTR_NARROW
)
2569 nfont
|= FONT_NARROW
;
2571 /* Special hack for the VT100 linedraw glyphs. */
2572 if ((attr
& CSET_MASK
) == 0x2300) {
2573 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2574 switch ((unsigned char) (text
[0])) {
2576 text_adjust
= -2 * font_height
/ 5;
2579 text_adjust
= -1 * font_height
/ 5;
2582 text_adjust
= font_height
/ 5;
2585 text_adjust
= 2 * font_height
/ 5;
2588 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2591 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2592 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2593 if (attr
& ATTR_UNDER
) {
2594 attr
&= ~ATTR_UNDER
;
2595 force_manual_underline
= 1;
2600 /* Anything left as an original character set is unprintable. */
2601 if (DIRECT_CHAR(attr
)) {
2604 memset(text
, 0xFD, len
);
2608 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2611 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2612 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2613 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2615 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2616 nfont
|= FONT_UNDERLINE
;
2617 another_font(nfont
);
2618 if (!fonts
[nfont
]) {
2619 if (nfont
& FONT_UNDERLINE
)
2620 force_manual_underline
= 1;
2621 /* Don't do the same for manual bold, it could be bad news. */
2623 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2625 another_font(nfont
);
2627 nfont
= FONT_NORMAL
;
2628 if (attr
& ATTR_REVERSE
) {
2633 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2635 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2639 SelectObject(hdc
, fonts
[nfont
]);
2640 SetTextColor(hdc
, fg
);
2641 SetBkColor(hdc
, bg
);
2642 SetBkMode(hdc
, OPAQUE
);
2645 line_box
.right
= x
+ char_width
* len
;
2646 line_box
.bottom
= y
+ font_height
;
2648 /* Only want the left half of double width lines */
2649 if (line_box
.right
> font_width
*cols
+offset_width
)
2650 line_box
.right
= font_width
*cols
+offset_width
;
2652 /* We're using a private area for direct to font. (512 chars.) */
2653 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2654 /* Ho Hum, dbcs fonts are a PITA! */
2655 /* To display on W9x I have to convert to UCS */
2656 static wchar_t *uni_buf
= 0;
2657 static int uni_len
= 0;
2659 if (len
> uni_len
) {
2661 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2664 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2665 uni_buf
[nlen
] = 0xFFFD;
2666 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2667 IpDx
[nlen
] += char_width
;
2668 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2669 text
+mptr
, 2, uni_buf
+nlen
, 1);
2674 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2675 text
+mptr
, 1, uni_buf
+nlen
, 1);
2683 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2684 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2685 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2686 SetBkMode(hdc
, TRANSPARENT
);
2687 ExtTextOutW(hdc
, x
- 1,
2688 y
- font_height
* (lattr
==
2689 LATTR_BOT
) + text_adjust
,
2690 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2694 } else if (DIRECT_FONT(attr
)) {
2696 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2697 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2698 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2699 SetBkMode(hdc
, TRANSPARENT
);
2701 /* GRR: This draws the character outside it's box and can leave
2702 * 'droppings' even with the clip box! I suppose I could loop it
2703 * one character at a time ... yuk.
2705 * Or ... I could do a test print with "W", and use +1 or -1 for this
2706 * shift depending on if the leftmost column is blank...
2708 ExtTextOut(hdc
, x
- 1,
2709 y
- font_height
* (lattr
==
2710 LATTR_BOT
) + text_adjust
,
2711 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2714 /* And 'normal' unicode characters */
2715 static WCHAR
*wbuf
= NULL
;
2716 static int wlen
= 0;
2721 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2723 for (i
= 0; i
< len
; i
++)
2724 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2727 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2728 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2730 /* And the shadow bold hack. */
2731 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2732 SetBkMode(hdc
, TRANSPARENT
);
2733 ExtTextOutW(hdc
, x
- 1,
2734 y
- font_height
* (lattr
==
2735 LATTR_BOT
) + text_adjust
,
2736 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2739 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2740 (und_mode
== UND_LINE
2741 && (attr
& ATTR_UNDER
)))) {
2744 if (lattr
== LATTR_BOT
)
2745 dec
= dec
* 2 - font_height
;
2747 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2748 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2749 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2750 oldpen
= SelectObject(hdc
, oldpen
);
2751 DeleteObject(oldpen
);
2755 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2756 unsigned long attr
, int lattr
)
2762 int ctype
= cfg
.cursor_type
;
2764 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2765 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2766 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2770 attr
|= TATTR_RIGHTCURS
;
2773 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2774 if (attr
& ATTR_WIDE
)
2781 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2784 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2785 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2786 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2787 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2788 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2789 Polyline(hdc
, pts
, 5);
2790 oldpen
= SelectObject(hdc
, oldpen
);
2791 DeleteObject(oldpen
);
2792 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2793 int startx
, starty
, dx
, dy
, length
, i
;
2796 starty
= y
+ descent
;
2799 length
= char_width
;
2802 if (attr
& TATTR_RIGHTCURS
)
2803 xadjust
= char_width
- 1;
2804 startx
= x
+ xadjust
;
2808 length
= font_height
;
2810 if (attr
& TATTR_ACTCURS
) {
2813 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2814 MoveToEx(hdc
, startx
, starty
, NULL
);
2815 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2816 oldpen
= SelectObject(hdc
, oldpen
);
2817 DeleteObject(oldpen
);
2819 for (i
= 0; i
< length
; i
++) {
2821 SetPixel(hdc
, startx
, starty
, colours
[23]);
2830 /* This function gets the actual width of a character in the normal font.
2832 int CharWidth(Context ctx
, int uc
) {
2836 /* If the font max is the same as the font ave width then this
2837 * function is a no-op.
2839 if (!font_dualwidth
) return 1;
2841 switch (uc
& CSET_MASK
) {
2843 uc
= unitab_line
[uc
& 0xFF];
2846 uc
= unitab_xterm
[uc
& 0xFF];
2849 uc
= unitab_scoacs
[uc
& 0xFF];
2852 if (DIRECT_FONT(uc
)) {
2853 if (dbcs_screenfont
) return 1;
2855 /* Speedup, I know of no font where ascii is the wrong width */
2856 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
2859 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
2860 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2861 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
2862 another_font(FONT_OEM
);
2863 if (!fonts
[FONT_OEM
]) return 0;
2865 SelectObject(hdc
, fonts
[FONT_OEM
]);
2869 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
2870 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
2873 /* Speedup, I know of no font where ascii is the wrong width */
2874 if (uc
>= ' ' && uc
<= '~') return 1;
2876 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2877 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
2878 /* Okay that one worked */ ;
2879 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
2880 /* This should work on 9x too, but it's "less accurate" */ ;
2885 ibuf
+= font_width
/ 2 -1;
2892 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2893 * codes. Returns number of bytes used or zero to drop the message
2894 * or -1 to forward the message to windows.
2896 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2897 unsigned char *output
)
2900 int scan
, left_alt
= 0, key_down
, shift_state
;
2902 unsigned char *p
= output
;
2903 static int alt_sum
= 0;
2905 HKL kbd_layout
= GetKeyboardLayout(0);
2907 static WORD keys
[3];
2908 static int compose_char
= 0;
2909 static WPARAM compose_key
= 0;
2911 r
= GetKeyboardState(keystate
);
2913 memset(keystate
, 0, sizeof(keystate
));
2916 #define SHOW_TOASCII_RESULT
2917 { /* Tell us all about key events */
2918 static BYTE oldstate
[256];
2919 static int first
= 1;
2923 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2926 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2928 } else if ((HIWORD(lParam
) & KF_UP
)
2929 && scan
== (HIWORD(lParam
) & 0xFF)) {
2933 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2934 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2947 debug(("VK_%02x", wParam
));
2949 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2951 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2953 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2954 if (ch
>= ' ' && ch
<= '~')
2955 debug((", '%c'", ch
));
2957 debug((", $%02x", ch
));
2960 debug((", KB0=%02x", keys
[0]));
2962 debug((", KB1=%02x", keys
[1]));
2964 debug((", KB2=%02x", keys
[2]));
2966 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2968 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2970 if ((HIWORD(lParam
) & KF_EXTENDED
))
2972 if ((HIWORD(lParam
) & KF_UP
))
2976 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2977 else if ((HIWORD(lParam
) & KF_UP
))
2978 oldstate
[wParam
& 0xFF] ^= 0x80;
2980 oldstate
[wParam
& 0xFF] ^= 0x81;
2982 for (ch
= 0; ch
< 256; ch
++)
2983 if (oldstate
[ch
] != keystate
[ch
])
2984 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2986 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2990 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2991 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2995 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2996 if ((cfg
.funky_type
== 3 ||
2997 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2998 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
3000 wParam
= VK_EXECUTE
;
3002 /* UnToggle NUMLock */
3003 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
3004 keystate
[VK_NUMLOCK
] ^= 1;
3007 /* And write back the 'adjusted' state */
3008 SetKeyboardState(keystate
);
3011 /* Disable Auto repeat if required */
3012 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
3015 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
3018 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
3020 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
3021 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
3022 if (cfg
.ctrlaltkeys
)
3023 keystate
[VK_MENU
] = 0;
3025 keystate
[VK_RMENU
] = 0x80;
3030 alt_pressed
= (left_alt
&& key_down
);
3032 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
3033 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
3034 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
3036 /* Note if AltGr was pressed and if it was used as a compose key */
3037 if (!compose_state
) {
3038 compose_key
= 0x100;
3039 if (cfg
.compose_key
) {
3040 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
3041 compose_key
= wParam
;
3043 if (wParam
== VK_APPS
)
3044 compose_key
= wParam
;
3047 if (wParam
== compose_key
) {
3048 if (compose_state
== 0
3049 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
3051 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
3055 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
3059 * Record that we pressed key so the scroll window can be reset, but
3060 * be careful to avoid Shift-UP/Down
3062 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
&&
3063 wParam
!= VK_MENU
&& wParam
!= VK_CONTROL
) {
3067 if (compose_state
> 1 && left_alt
)
3070 /* Sanitize the number pad if not using a PC NumPad */
3071 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
3072 && cfg
.funky_type
!= 2)
3073 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
3074 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
3078 nParam
= VK_NUMPAD0
;
3081 nParam
= VK_NUMPAD1
;
3084 nParam
= VK_NUMPAD2
;
3087 nParam
= VK_NUMPAD3
;
3090 nParam
= VK_NUMPAD4
;
3093 nParam
= VK_NUMPAD5
;
3096 nParam
= VK_NUMPAD6
;
3099 nParam
= VK_NUMPAD7
;
3102 nParam
= VK_NUMPAD8
;
3105 nParam
= VK_NUMPAD9
;
3108 nParam
= VK_DECIMAL
;
3112 if (keystate
[VK_NUMLOCK
] & 1)
3119 /* If a key is pressed and AltGr is not active */
3120 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
3121 /* Okay, prepare for most alts then ... */
3125 /* Lets see if it's a pattern we know all about ... */
3126 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
3127 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
3130 if (wParam
== VK_NEXT
&& shift_state
== 1) {
3131 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
3134 if (wParam
== VK_INSERT
&& shift_state
== 1) {
3138 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
3141 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
3142 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
3145 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
&&
3146 (cfg
.resize_action
!= RESIZE_DISABLED
)) {
3147 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) != KF_REPEAT
)
3151 /* Control-Numlock for app-keypad mode switch */
3152 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
3153 app_keypad_keys
^= 1;
3157 /* Nethack keypad */
3158 if (cfg
.nethack_keypad
&& !left_alt
) {
3161 *p
++ = shift_state ?
'B' : 'b';
3164 *p
++ = shift_state ?
'J' : 'j';
3167 *p
++ = shift_state ?
'N' : 'n';
3170 *p
++ = shift_state ?
'H' : 'h';
3173 *p
++ = shift_state ?
'.' : '.';
3176 *p
++ = shift_state ?
'L' : 'l';
3179 *p
++ = shift_state ?
'Y' : 'y';
3182 *p
++ = shift_state ?
'K' : 'k';
3185 *p
++ = shift_state ?
'U' : 'u';
3190 /* Application Keypad */
3194 if (cfg
.funky_type
== 3 ||
3195 (cfg
.funky_type
<= 1 &&
3196 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3210 if (app_keypad_keys
&& !cfg
.no_applic_k
)
3247 if (cfg
.funky_type
== 2) {
3252 } else if (shift_state
)
3259 if (cfg
.funky_type
== 2)
3263 if (cfg
.funky_type
== 2)
3267 if (cfg
.funky_type
== 2)
3272 if (HIWORD(lParam
) & KF_EXTENDED
)
3278 if (xkey
>= 'P' && xkey
<= 'S')
3279 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3281 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3283 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3288 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3289 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3293 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3299 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3303 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3307 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3312 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3317 /* Control-2 to Control-8 are special */
3318 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3319 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3322 if (shift_state
== 2 && wParam
== 0xBD) {
3326 if (shift_state
== 2 && wParam
== 0xDF) {
3330 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
3337 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3338 * for integer decimal nn.)
3340 * We also deal with the weird ones here. Linux VCs replace F1
3341 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3342 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3348 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3351 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3354 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3357 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3360 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3363 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3366 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3369 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3372 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3375 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3408 if ((shift_state
&2) == 0) switch (wParam
) {
3428 /* Reorder edit keys to physical order */
3429 if (cfg
.funky_type
== 3 && code
<= 6)
3430 code
= "\0\2\1\4\5\3\6"[code
];
3432 if (vt52_mode
&& code
> 0 && code
<= 6) {
3433 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3437 if (cfg
.funky_type
== 5 && /* SCO function keys */
3438 code
>= 11 && code
<= 34) {
3439 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3442 case VK_F1
: index
= 0; break;
3443 case VK_F2
: index
= 1; break;
3444 case VK_F3
: index
= 2; break;
3445 case VK_F4
: index
= 3; break;
3446 case VK_F5
: index
= 4; break;
3447 case VK_F6
: index
= 5; break;
3448 case VK_F7
: index
= 6; break;
3449 case VK_F8
: index
= 7; break;
3450 case VK_F9
: index
= 8; break;
3451 case VK_F10
: index
= 9; break;
3452 case VK_F11
: index
= 10; break;
3453 case VK_F12
: index
= 11; break;
3455 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3456 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3457 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3460 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3461 code
>= 1 && code
<= 6) {
3462 char codes
[] = "HL.FIG";
3466 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3470 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3477 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3480 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3483 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3484 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3487 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3489 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3491 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3494 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3495 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3499 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3504 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3505 * some reason seems to send VK_CLEAR to Windows...).
3528 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3530 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
3533 * RDB: VT100 & VT102 manuals both state the
3534 * app cursor keys only work if the app keypad
3537 * SGT: That may well be true, but xterm
3538 * disagrees and so does at least one
3539 * application, so I've #if'ed this out and the
3540 * behaviour is back to PuTTY's original: app
3541 * cursor and app keypad are independently
3542 * switchable modes. If anyone complains about
3543 * _this_ I'll have to put in a configurable
3546 if (!app_keypad_keys
)
3549 /* Useful mapping of Ctrl-arrows */
3550 if (shift_state
== 2)
3554 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3556 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3563 * Finally, deal with Return ourselves. (Win95 seems to
3564 * foul it up when Alt is pressed, for some reason.)
3566 if (wParam
== VK_RETURN
) { /* Return */
3572 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3573 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3578 /* Okay we've done everything interesting; let windows deal with
3579 * the boring stuff */
3583 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3584 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3586 keystate
[VK_CAPITAL
] = 0;
3589 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3590 #ifdef SHOW_TOASCII_RESULT
3591 if (r
== 1 && !key_down
) {
3593 if (in_utf
|| dbcs_screenfont
)
3594 debug((", (U+%04x)", alt_sum
));
3596 debug((", LCH(%d)", alt_sum
));
3598 debug((", ACH(%d)", keys
[0]));
3603 for (r1
= 0; r1
< r
; r1
++) {
3604 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3613 * Interrupt an ongoing paste. I'm not sure this is
3614 * sensible, but for the moment it's preferable to
3615 * having to faff about buffering things.
3620 for (i
= 0; i
< r
; i
++) {
3621 unsigned char ch
= (unsigned char) keys
[i
];
3623 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3628 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3632 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3633 MessageBeep(MB_ICONHAND
);
3637 luni_send(&keybuf
, 1, 1);
3645 if (in_utf
|| dbcs_screenfont
) {
3647 luni_send(&keybuf
, 1, 1);
3649 ch
= (char) alt_sum
;
3651 * We need not bother about stdin
3652 * backlogs here, because in GUI PuTTY
3653 * we can't do anything about it
3654 * anyway; there's no means of asking
3655 * Windows to hold off on KEYDOWN
3656 * messages. We _have_ to buffer
3657 * everything we're sent.
3659 ldisc_send(&ch
, 1, 1);
3663 lpage_send(kbd_codepage
, &ch
, 1, 1);
3665 if(capsOn
&& ch
< 0x80) {
3668 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3669 luni_send(cbuf
+!left_alt
, 1+!!left_alt
, 1);
3674 lpage_send(kbd_codepage
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3680 /* This is so the ALT-Numpad and dead keys work correctly. */
3685 /* If we're definitly not building up an ALT-54321 then clear it */
3688 /* If we will be using alt_sum fix the 256s */
3689 else if (keys
[0] && (in_utf
|| dbcs_screenfont
))
3694 * ALT alone may or may not want to bring up the System menu.
3695 * If it's not meant to, we return 0 on presses or releases of
3696 * ALT, to show that we've swallowed the keystroke. Otherwise
3697 * we return -1, which means Windows will give the keystroke
3698 * its default handling (i.e. bring up the System menu).
3700 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3706 void set_title(char *title
)
3709 window_name
= smalloc(1 + strlen(title
));
3710 strcpy(window_name
, title
);
3711 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3712 SetWindowText(hwnd
, title
);
3715 void set_icon(char *title
)
3718 icon_name
= smalloc(1 + strlen(title
));
3719 strcpy(icon_name
, title
);
3720 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3721 SetWindowText(hwnd
, title
);
3724 void set_sbar(int total
, int start
, int page
)
3728 if (is_full_screen() ?
!cfg
.scrollbar_in_fullscreen
: !cfg
.scrollbar
)
3731 si
.cbSize
= sizeof(si
);
3732 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3734 si
.nMax
= total
- 1;
3738 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3741 Context
get_ctx(void)
3747 SelectPalette(hdc
, pal
, FALSE
);
3753 void free_ctx(Context ctx
)
3755 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3756 ReleaseDC(hwnd
, ctx
);
3759 static void real_palette_set(int n
, int r
, int g
, int b
)
3762 logpal
->palPalEntry
[n
].peRed
= r
;
3763 logpal
->palPalEntry
[n
].peGreen
= g
;
3764 logpal
->palPalEntry
[n
].peBlue
= b
;
3765 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3766 colours
[n
] = PALETTERGB(r
, g
, b
);
3767 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3769 colours
[n
] = RGB(r
, g
, b
);
3772 void palette_set(int n
, int r
, int g
, int b
)
3774 static const int first
[21] = {
3775 0, 2, 4, 6, 8, 10, 12, 14,
3776 1, 3, 5, 7, 9, 11, 13, 15,
3779 real_palette_set(first
[n
], r
, g
, b
);
3781 real_palette_set(first
[n
] + 1, r
, g
, b
);
3783 HDC hdc
= get_ctx();
3784 UnrealizeObject(pal
);
3785 RealizePalette(hdc
);
3790 void palette_reset(void)
3794 for (i
= 0; i
< NCOLOURS
; i
++) {
3796 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3797 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3798 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3799 logpal
->palPalEntry
[i
].peFlags
= 0;
3800 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3801 defpal
[i
].rgbtGreen
,
3802 defpal
[i
].rgbtBlue
);
3804 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3805 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3810 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3812 RealizePalette(hdc
);
3817 void write_aclip(char *data
, int len
, int must_deselect
)
3822 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3825 lock
= GlobalLock(clipdata
);
3828 memcpy(lock
, data
, len
);
3829 ((unsigned char *) lock
)[len
] = 0;
3830 GlobalUnlock(clipdata
);
3833 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3835 if (OpenClipboard(hwnd
)) {
3837 SetClipboardData(CF_TEXT
, clipdata
);
3840 GlobalFree(clipdata
);
3843 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3847 * Note: unlike write_aclip() this will not append a nul.
3849 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3851 HGLOBAL clipdata
, clipdata2
, clipdata3
;
3853 void *lock
, *lock2
, *lock3
;
3855 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3857 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3858 len
* sizeof(wchar_t));
3859 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3861 if (!clipdata
|| !clipdata2
) {
3863 GlobalFree(clipdata
);
3865 GlobalFree(clipdata2
);
3868 if (!(lock
= GlobalLock(clipdata
)))
3870 if (!(lock2
= GlobalLock(clipdata2
)))
3873 memcpy(lock
, data
, len
* sizeof(wchar_t));
3874 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3876 if (cfg
.rtf_paste
) {
3877 wchar_t unitab
[256];
3879 unsigned char *tdata
= (unsigned char *)lock2
;
3880 wchar_t *udata
= (wchar_t *)lock
;
3881 int rtflen
= 0, uindex
= 0, tindex
= 0;
3883 int multilen
, blen
, alen
, totallen
, i
;
3884 char before
[16], after
[4];
3886 get_unitab(CP_ACP
, unitab
, 0);
3888 rtfsize
= 100 + strlen(cfg
.font
);
3889 rtf
= smalloc(rtfsize
);
3890 sprintf(rtf
, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3891 GetACP(), cfg
.font
);
3892 rtflen
= strlen(rtf
);
3895 * We want to construct a piece of RTF that specifies the
3896 * same Unicode text. To do this we will read back in
3897 * parallel from the Unicode data in `udata' and the
3898 * non-Unicode data in `tdata'. For each character in
3899 * `tdata' which becomes the right thing in `udata' when
3900 * looked up in `unitab', we just copy straight over from
3901 * tdata. For each one that doesn't, we must WCToMB it
3902 * individually and produce a \u escape sequence.
3904 * It would probably be more robust to just bite the bullet
3905 * and WCToMB each individual Unicode character one by one,
3906 * then MBToWC each one back to see if it was an accurate
3907 * translation; but that strikes me as a horrifying number
3908 * of Windows API calls so I want to see if this faster way
3909 * will work. If it screws up badly we can always revert to
3910 * the simple and slow way.
3912 while (tindex
< len2
&& uindex
< len
&&
3913 tdata
[tindex
] && udata
[uindex
]) {
3914 if (tindex
+ 1 < len2
&&
3915 tdata
[tindex
] == '\r' &&
3916 tdata
[tindex
+1] == '\n') {
3920 if (unitab
[tdata
[tindex
]] == udata
[uindex
]) {
3926 multilen
= WideCharToMultiByte(CP_ACP
, 0, unitab
+uindex
, 1,
3927 NULL
, 0, NULL
, NULL
);
3928 if (multilen
!= 1) {
3929 blen
= sprintf(before
, "{\\uc%d\\u%d", multilen
,
3931 alen
= 1; strcpy(after
, "}");
3933 blen
= sprintf(before
, "\\u%d", udata
[uindex
]);
3934 alen
= 0; after
[0] = '\0';
3937 assert(tindex
+ multilen
<= len2
);
3938 totallen
= blen
+ alen
;
3939 for (i
= 0; i
< multilen
; i
++) {
3940 if (tdata
[tindex
+i
] == '\\' ||
3941 tdata
[tindex
+i
] == '{' ||
3942 tdata
[tindex
+i
] == '}')
3944 else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A)
3945 totallen
+= 6; /* \par\r\n */
3946 else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20)
3952 if (rtfsize
< rtflen
+ totallen
+ 3) {
3953 rtfsize
= rtflen
+ totallen
+ 512;
3954 rtf
= srealloc(rtf
, rtfsize
);
3957 strcpy(rtf
+ rtflen
, before
); rtflen
+= blen
;
3958 for (i
= 0; i
< multilen
; i
++) {
3959 if (tdata
[tindex
+i
] == '\\' ||
3960 tdata
[tindex
+i
] == '{' ||
3961 tdata
[tindex
+i
] == '}') {
3962 rtf
[rtflen
++] = '\\';
3963 rtf
[rtflen
++] = tdata
[tindex
+i
];
3964 } else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A) {
3965 rtflen
+= sprintf(rtf
+rtflen
, "\\par\r\n");
3966 } else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20) {
3967 rtflen
+= sprintf(rtf
+rtflen
, "\\'%02x", tdata
[tindex
+i
]);
3969 rtf
[rtflen
++] = tdata
[tindex
+i
];
3972 strcpy(rtf
+ rtflen
, after
); rtflen
+= alen
;
3978 strcpy(rtf
+ rtflen
, "}");
3981 clipdata3
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, rtflen
);
3982 if (clipdata3
&& (lock3
= GlobalLock(clipdata3
)) != NULL
) {
3984 GlobalUnlock(clipdata3
);
3990 GlobalUnlock(clipdata
);
3991 GlobalUnlock(clipdata2
);
3994 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3996 if (OpenClipboard(hwnd
)) {
3998 SetClipboardData(CF_UNICODETEXT
, clipdata
);
3999 SetClipboardData(CF_TEXT
, clipdata2
);
4001 SetClipboardData(RegisterClipboardFormat(CF_RTF
), clipdata3
);
4004 GlobalFree(clipdata
);
4005 GlobalFree(clipdata2
);
4009 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
4012 void get_clip(wchar_t ** p
, int *len
)
4014 static HGLOBAL clipdata
= NULL
;
4015 static wchar_t *converted
= 0;
4024 GlobalUnlock(clipdata
);
4027 } else if (OpenClipboard(NULL
)) {
4028 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
4030 *p
= GlobalLock(clipdata
);
4032 for (p2
= *p
; *p2
; p2
++);
4036 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
4040 s
= GlobalLock(clipdata
);
4041 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
4042 *p
= converted
= smalloc(i
* sizeof(wchar_t));
4043 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
4056 * Move `lines' lines from position `from' to position `to' in the
4059 void optimised_move(int to
, int from
, int lines
)
4064 min
= (to
< from ? to
: from
);
4065 max
= to
+ from
- min
;
4067 r
.left
= offset_width
;
4068 r
.right
= offset_width
+ cols
* font_width
;
4069 r
.top
= offset_height
+ min
* font_height
;
4070 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
4071 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
4076 * Print a message box and perform a fatal exit.
4078 void fatalbox(char *fmt
, ...)
4084 vsprintf(stuff
, fmt
, ap
);
4086 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
4091 * Manage window caption / taskbar flashing, if enabled.
4092 * 0 = stop, 1 = maintain, 2 = start
4094 static void flash_window(int mode
)
4096 static long last_flash
= 0;
4097 static int flashing
= 0;
4098 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
4101 FlashWindow(hwnd
, FALSE
);
4105 } else if (mode
== 2) {
4108 last_flash
= GetTickCount();
4110 FlashWindow(hwnd
, TRUE
);
4113 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
4116 long now
= GetTickCount();
4117 long fdiff
= now
- last_flash
;
4118 if (fdiff
< 0 || fdiff
> 450) {
4120 FlashWindow(hwnd
, TRUE
); /* toggle */
4131 if (mode
== BELL_DEFAULT
) {
4133 * For MessageBeep style bells, we want to be careful of
4134 * timing, because they don't have the nice property of
4135 * PlaySound bells that each one cancels the previous
4136 * active one. So we limit the rate to one per 50ms or so.
4138 static long lastbeep
= 0;
4141 beepdiff
= GetTickCount() - lastbeep
;
4142 if (beepdiff
>= 0 && beepdiff
< 50)
4146 * The above MessageBeep call takes time, so we record the
4147 * time _after_ it finishes rather than before it starts.
4149 lastbeep
= GetTickCount();
4150 } else if (mode
== BELL_WAVEFILE
) {
4151 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
4152 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
4153 sprintf(buf
, "Unable to play sound file\n%s\n"
4154 "Using default sound instead", cfg
.bell_wavefile
);
4155 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
4156 MB_OK
| MB_ICONEXCLAMATION
);
4157 cfg
.beep
= BELL_DEFAULT
;
4160 /* Otherwise, either visual bell or disabled; do nothing here */
4162 flash_window(2); /* start */
4167 * Minimise or restore the window in response to a server-side
4170 void set_iconic(int iconic
)
4172 if (IsIconic(hwnd
)) {
4174 ShowWindow(hwnd
, SW_RESTORE
);
4177 ShowWindow(hwnd
, SW_MINIMIZE
);
4182 * Move the window in response to a server-side request.
4184 void move_window(int x
, int y
)
4186 SetWindowPos(hwnd
, NULL
, x
, y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
);
4190 * Move the window to the top or bottom of the z-order in response
4191 * to a server-side request.
4193 void set_zorder(int top
)
4195 if (cfg
.alwaysontop
)
4196 return; /* ignore */
4197 SetWindowPos(hwnd
, top ? HWND_TOP
: HWND_BOTTOM
, 0, 0, 0, 0,
4198 SWP_NOMOVE
| SWP_NOSIZE
);
4202 * Refresh the window in response to a server-side request.
4204 void refresh_window(void)
4206 InvalidateRect(hwnd
, NULL
, TRUE
);
4210 * Maximise or restore the window in response to a server-side
4213 void set_zoomed(int zoomed
)
4215 if (IsZoomed(hwnd
)) {
4217 ShowWindow(hwnd
, SW_RESTORE
);
4220 ShowWindow(hwnd
, SW_MAXIMIZE
);
4225 * Report whether the window is iconic, for terminal reports.
4229 return IsIconic(hwnd
);
4233 * Report the window's position, for terminal reports.
4235 void get_window_pos(int *x
, int *y
)
4238 GetWindowRect(hwnd
, &r
);
4244 * Report the window's pixel size, for terminal reports.
4246 void get_window_pixels(int *x
, int *y
)
4249 GetWindowRect(hwnd
, &r
);
4250 *x
= r
.right
- r
.left
;
4251 *y
= r
.bottom
- r
.top
;
4255 * Return the window or icon title.
4257 char *get_window_title(int icon
)
4259 return icon ? icon_name
: window_name
;
4263 * See if we're in full-screen mode.
4265 int is_full_screen()
4267 if (!IsZoomed(hwnd
))
4269 if (GetWindowLong(hwnd
, GWL_STYLE
) & WS_CAPTION
)
4275 * Go full-screen. This should only be called when we are already
4278 void make_full_screen()
4283 assert(IsZoomed(hwnd
));
4285 /* Remove the window furniture. */
4286 style
= GetWindowLong(hwnd
, GWL_STYLE
);
4287 style
&= ~(WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
);
4288 if (cfg
.scrollbar_in_fullscreen
)
4289 style
|= WS_VSCROLL
;
4291 style
&= ~WS_VSCROLL
;
4292 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4294 /* Resize ourselves to exactly cover the nearest monitor. */
4295 #ifdef MONITOR_DEFAULTTONEAREST
4299 mon
= MonitorFromWindow(hwnd
, MONITOR_DEFAULTTONEAREST
);
4300 mi
.cbSize
= sizeof(mi
);
4301 GetMonitorInfo(mon
, &mi
);
4302 x
= mi
.rcMonitor
.left
;
4303 y
= mi
.rcMonitor
.top
;
4304 w
= mi
.rcMonitor
.right
;
4305 h
= mi
.rcMonitor
.bottom
;
4309 w
= GetSystemMetrics(SM_CXSCREEN
);
4310 h
= GetSystemMetrics(SM_CYSCREEN
);
4312 SetWindowPos(hwnd
, HWND_TOP
, x
, y
, w
, h
, SWP_FRAMECHANGED
);
4314 /* Tick the menu item in the System menu. */
4315 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4320 * Clear the full-screen attributes.
4322 void clear_full_screen()
4324 DWORD oldstyle
, style
;
4326 /* Reinstate the window furniture. */
4327 style
= oldstyle
= GetWindowLong(hwnd
, GWL_STYLE
);
4328 style
|= WS_CAPTION
| WS_BORDER
;
4329 if (cfg
.resize_action
== RESIZE_DISABLED
)
4330 style
&= ~WS_THICKFRAME
;
4332 style
|= WS_THICKFRAME
;
4334 style
|= WS_VSCROLL
;
4336 style
&= ~WS_VSCROLL
;
4337 if (style
!= oldstyle
) {
4338 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4339 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
4340 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
4344 /* Untick the menu item in the System menu. */
4345 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4350 * Toggle full-screen mode.
4352 void flip_full_screen()
4354 if (is_full_screen()) {
4355 ShowWindow(hwnd
, SW_RESTORE
);
4356 } else if (IsZoomed(hwnd
)) {
4359 SendMessage(hwnd
, WM_FULLSCR_ON_MAX
, 0, 0);
4360 ShowWindow(hwnd
, SW_MAXIMIZE
);