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 /* Mouse wheel support. */
72 #define WM_MOUSEWHEEL 0x020A /* not defined in earlier SDKs */
75 #define WHEEL_DELTA 120
78 static LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
79 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
80 unsigned char *output
);
81 static void cfgtopalette(void);
82 static void init_palette(void);
83 static void init_fonts(int, int);
84 static void another_font(int);
85 static void deinit_fonts(void);
86 static void set_input_locale(HKL
);
87 static int do_mouse_wheel_msg(UINT message
, WPARAM wParam
, LPARAM lParam
);
89 static int is_full_screen(void);
90 static void make_full_screen(void);
91 static void clear_full_screen(void);
92 static void flip_full_screen(void);
94 /* Window layout information */
95 static void reset_window(int);
96 static int extra_width
, extra_height
;
97 static int font_width
, font_height
, font_dualwidth
;
98 static int offset_width
, offset_height
;
99 static int was_zoomed
= 0;
100 static int prev_rows
, prev_cols
;
102 static int pending_netevent
= 0;
103 static WPARAM pend_netevent_wParam
= 0;
104 static LPARAM pend_netevent_lParam
= 0;
105 static void enact_pending_netevent(void);
106 static void flash_window(int mode
);
107 static void sys_cursor_update(void);
109 static time_t last_movement
= 0;
111 static int caret_x
= -1, caret_y
= -1;
113 #define FONT_NORMAL 0
115 #define FONT_UNDERLINE 2
116 #define FONT_BOLDUND 3
117 #define FONT_WIDE 0x04
118 #define FONT_HIGH 0x08
119 #define FONT_NARROW 0x10
121 #define FONT_OEM 0x20
122 #define FONT_OEMBOLD 0x21
123 #define FONT_OEMUND 0x22
124 #define FONT_OEMBOLDUND 0x23
126 #define FONT_MAXNO 0x2F
128 static HFONT fonts
[FONT_MAXNO
];
129 static LOGFONT lfont
;
130 static int fontflag
[FONT_MAXNO
];
132 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
140 static COLORREF colours
[NCOLOURS
];
142 static LPLOGPALETTE logpal
;
143 static RGBTRIPLE defpal
[NCOLOURS
];
147 static HBITMAP caretbm
;
149 static int dbltime
, lasttime
, lastact
;
150 static Mouse_Button lastbtn
;
152 /* this allows xterm-style mouse handling. */
153 static int send_raw_mouse
= 0;
154 static int wheel_accumulator
= 0;
156 static char *window_name
, *icon_name
;
158 static int compose_state
= 0;
160 static OSVERSIONINFO osVersion
;
162 static UINT wm_mousewheel
= WM_MOUSEWHEEL
;
164 /* Dummy routine, only required in plink. */
165 void ldisc_update(int echo
, int edit
)
169 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
171 static char appname
[] = "PuTTY";
176 int guess_width
, guess_height
;
179 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
181 winsock_ver
= MAKEWORD(1, 1);
182 if (WSAStartup(winsock_ver
, &wsadata
)) {
183 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
184 MB_OK
| MB_ICONEXCLAMATION
);
187 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
188 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
189 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
193 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
196 InitCommonControls();
198 /* Ensure a Maximize setting in Explorer doesn't maximise the
203 ZeroMemory(&osVersion
, sizeof(osVersion
));
204 osVersion
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
205 if (!GetVersionEx ( (OSVERSIONINFO
*) &osVersion
)) {
206 MessageBox(NULL
, "Windows refuses to report a version",
207 "PuTTY Fatal Error", MB_OK
| MB_ICONEXCLAMATION
);
213 * If we're running a version of Windows that doesn't support
214 * WM_MOUSEWHEEL, find out what message number we should be
217 if (osVersion
.dwMajorVersion
< 4 ||
218 (osVersion
.dwMajorVersion
== 4 &&
219 osVersion
.dwPlatformId
!= VER_PLATFORM_WIN32_NT
))
220 wm_mousewheel
= RegisterWindowMessage("MSWHEEL_ROLLMSG");
223 * See if we can find our Help file.
226 char b
[2048], *p
, *q
, *r
;
228 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
230 p
= strrchr(b
, '\\');
231 if (p
&& p
>= r
) r
= p
+1;
233 if (q
&& q
>= r
) r
= q
+1;
234 strcpy(r
, "putty.hlp");
235 if ( (fp
= fopen(b
, "r")) != NULL
) {
236 help_path
= dupstr(b
);
240 strcpy(r
, "putty.cnt");
241 if ( (fp
= fopen(b
, "r")) != NULL
) {
242 help_has_contents
= TRUE
;
245 help_has_contents
= FALSE
;
249 * Process the command line.
254 default_protocol
= DEFAULT_PROTOCOL
;
255 default_port
= DEFAULT_PORT
;
256 cfg
.logtype
= LGTYP_NONE
;
258 do_defaults(NULL
, &cfg
);
261 while (*p
&& isspace(*p
))
265 * Process command line options first. Yes, this can be
266 * done better, and it will be as soon as I have the
270 char *q
= p
+ strcspn(p
, " \t");
273 tolower(p
[0]) == 's' &&
274 tolower(p
[1]) == 's' && tolower(p
[2]) == 'h') {
275 default_protocol
= cfg
.protocol
= PROT_SSH
;
276 default_port
= cfg
.port
= 22;
277 } else if (q
== p
+ 7 &&
278 tolower(p
[0]) == 'c' &&
279 tolower(p
[1]) == 'l' &&
280 tolower(p
[2]) == 'e' &&
281 tolower(p
[3]) == 'a' &&
282 tolower(p
[4]) == 'n' &&
283 tolower(p
[5]) == 'u' && tolower(p
[6]) == 'p') {
285 * `putty -cleanup'. Remove all registry entries
286 * associated with PuTTY, and also find and delete
287 * the random seed file.
290 "This procedure will remove ALL Registry\n"
291 "entries associated with PuTTY, and will\n"
292 "also remove the PuTTY random seed file.\n"
294 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
295 "SESSIONS. Are you really sure you want\n"
298 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
303 p
= q
+ strspn(q
, " \t");
307 * An initial @ means to activate a saved session.
311 while (i
> 1 && isspace(p
[i
- 1]))
314 do_defaults(p
+ 1, &cfg
);
315 if (!*cfg
.host
&& !do_config()) {
319 } else if (*p
== '&') {
321 * An initial & means we've been given a command line
322 * containing the hex value of a HANDLE for a file
323 * mapping object, which we must then extract as a
328 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
329 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
330 0, 0, sizeof(Config
))) != NULL
) {
333 CloseHandle(filemap
);
334 } else if (!do_config()) {
341 * If the hostname starts with "telnet:", set the
342 * protocol to Telnet and process the string as a
345 if (!strncmp(q
, "telnet:", 7)) {
349 if (q
[0] == '/' && q
[1] == '/')
351 cfg
.protocol
= PROT_TELNET
;
353 while (*p
&& *p
!= ':' && *p
!= '/')
362 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
363 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
365 while (*p
&& !isspace(*p
))
369 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
370 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
371 while (*p
&& isspace(*p
))
386 * Trim leading whitespace off the hostname if it's there.
389 int space
= strspn(cfg
.host
, " \t");
390 memmove(cfg
.host
, cfg
.host
+space
, 1+strlen(cfg
.host
)-space
);
393 /* See if host is of the form user@host */
394 if (cfg
.host
[0] != '\0') {
395 char *atsign
= strchr(cfg
.host
, '@');
396 /* Make sure we're not overflowing the user field */
398 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
399 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
400 cfg
.username
[atsign
- cfg
.host
] = '\0';
402 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
407 * Trim a colon suffix off the hostname if it's there.
409 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
413 * Select protocol. This is farmed out into a table in a
414 * separate file to enable an ssh-free variant.
419 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
420 if (backends
[i
].protocol
== cfg
.protocol
) {
421 back
= backends
[i
].backend
;
425 MessageBox(NULL
, "Unsupported protocol number found",
426 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
432 /* Check for invalid Port number (i.e. zero) */
434 MessageBox(NULL
, "Invalid Port Number",
435 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
442 wndclass
.lpfnWndProc
= WndProc
;
443 wndclass
.cbClsExtra
= 0;
444 wndclass
.cbWndExtra
= 0;
445 wndclass
.hInstance
= inst
;
446 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
447 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
448 wndclass
.hbrBackground
= NULL
;
449 wndclass
.lpszMenuName
= NULL
;
450 wndclass
.lpszClassName
= appname
;
452 RegisterClass(&wndclass
);
457 savelines
= cfg
.savelines
;
463 * Guess some defaults for the window size. This all gets
464 * updated later, so we don't really care too much. However, we
465 * do want the font width/height guesses to correspond to a
466 * large font rather than a small one...
473 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
474 guess_width
= extra_width
+ font_width
* cols
;
475 guess_height
= extra_height
+ font_height
* rows
;
478 HWND w
= GetDesktopWindow();
479 GetWindowRect(w
, &r
);
480 if (guess_width
> r
.right
- r
.left
)
481 guess_width
= r
.right
- r
.left
;
482 if (guess_height
> r
.bottom
- r
.top
)
483 guess_height
= r
.bottom
- r
.top
;
487 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
490 winmode
&= ~(WS_VSCROLL
);
491 if (cfg
.resize_action
== RESIZE_DISABLED
)
492 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
494 exwinmode
|= WS_EX_TOPMOST
;
496 exwinmode
|= WS_EX_CLIENTEDGE
;
497 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
498 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
499 guess_width
, guess_height
,
500 NULL
, NULL
, inst
, NULL
);
504 * Initialise the fonts, simultaneously correcting the guesses
505 * for font_{width,height}.
510 * Correct the guesses for extra_{width,height}.
514 GetWindowRect(hwnd
, &wr
);
515 GetClientRect(hwnd
, &cr
);
516 offset_width
= offset_height
= cfg
.window_border
;
517 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
518 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
522 * Resize the window, now we know what size we _really_ want it
525 guess_width
= extra_width
+ font_width
* cols
;
526 guess_height
= extra_height
+ font_height
* rows
;
527 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
528 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
531 * Set up a caret bitmap, with no content.
535 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
536 bits
= smalloc(size
);
537 memset(bits
, 0, size
);
538 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
541 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
544 * Initialise the scroll bar.
549 si
.cbSize
= sizeof(si
);
550 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
555 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
559 * Start up the telnet connection.
563 char msg
[1024], *title
;
566 error
= back
->init(cfg
.host
, cfg
.port
, &realhost
, cfg
.tcp_nodelay
);
568 sprintf(msg
, "Unable to open connection to\n"
569 "%.800s\n" "%s", cfg
.host
, error
);
570 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
573 window_name
= icon_name
= NULL
;
575 title
= cfg
.wintitle
;
577 sprintf(msg
, "%s - PuTTY", realhost
);
585 session_closed
= FALSE
;
588 * Prepare the mouse handler.
590 lastact
= MA_NOTHING
;
591 lastbtn
= MBT_NOTHING
;
592 dbltime
= GetDoubleClickTime();
595 * Set up the session-control options on the system menu.
598 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
602 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
603 if (cfg
.protocol
== PROT_TELNET
) {
605 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
606 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
607 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
608 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
609 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
610 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
611 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
612 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
613 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
614 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
615 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
616 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
617 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
618 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
619 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
620 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
621 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
623 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
625 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
626 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
627 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
628 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
631 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
632 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
634 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
635 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
636 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
637 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
638 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
639 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
640 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
641 AppendMenu(m
, (cfg
.resize_action
== RESIZE_DISABLED
) ?
642 MF_GRAYED
: MF_ENABLED
, IDM_FULLSCREEN
, "&Full Screen");
643 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
645 AppendMenu(m
, MF_ENABLED
, IDM_HELP
, "&Help");
646 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
650 * Set up the initial input locale.
652 set_input_locale(GetKeyboardLayout(0));
655 * Open the initial log file if there is one.
660 * Finally show the window!
662 ShowWindow(hwnd
, show
);
663 SetForegroundWindow(hwnd
);
666 * Set the palette up.
672 has_focus
= (GetForegroundWindow() == hwnd
);
675 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
676 int timer_id
= 0, long_timer
= 0;
678 while (msg
.message
!= WM_QUIT
) {
679 /* Sometimes DispatchMessage calls routines that use their own
680 * GetMessage loop, setup this timer so we get some control back.
682 * Also call term_update() from the timer so that if the host
683 * is sending data flat out we still do redraws.
685 if (timer_id
&& long_timer
) {
686 KillTimer(hwnd
, timer_id
);
687 long_timer
= timer_id
= 0;
690 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
691 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
692 DispatchMessage(&msg
);
694 /* Make sure we blink everything that needs it. */
697 /* Send the paste buffer if there's anything to send */
700 /* If there's nothing new in the queue then we can do everything
701 * we've delayed, reading the socket, writing, and repainting
704 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
707 if (pending_netevent
) {
708 enact_pending_netevent();
710 /* Force the cursor blink on */
713 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
717 /* Okay there is now nothing to do so we make sure the screen is
718 * completely up to date then tell windows to call us in a little
722 KillTimer(hwnd
, timer_id
);
730 flash_window(1); /* maintain */
732 /* The messages seem unreliable; especially if we're being tricky */
733 has_focus
= (GetForegroundWindow() == hwnd
);
736 /* Hmm, term_update didn't want to do an update too soon ... */
737 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
739 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
741 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
744 /* There's no point rescanning everything in the message queue
745 * so we do an apparently unnecessary wait here
748 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
762 if (cfg
.protocol
== PROT_SSH
) {
773 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
775 char *do_select(SOCKET skt
, int startup
)
780 events
= (FD_CONNECT
| FD_READ
| FD_WRITE
|
781 FD_OOB
| FD_CLOSE
| FD_ACCEPT
);
786 return "do_select(): internal error (hwnd==NULL)";
787 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
788 switch (WSAGetLastError()) {
790 return "Network is down";
792 return "WSAAsyncSelect(): unknown error";
799 * set or clear the "raw mouse message" mode
801 void set_raw_mouse_mode(int activate
)
803 send_raw_mouse
= activate
;
804 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
808 * Print a message box and close the connection.
810 void connection_fatal(char *fmt
, ...)
816 vsprintf(stuff
, fmt
, ap
);
818 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
819 if (cfg
.close_on_exit
== COE_ALWAYS
)
822 session_closed
= TRUE
;
823 SetWindowText(hwnd
, "PuTTY (inactive)");
828 * Actually do the job requested by a WM_NETEVENT
830 static void enact_pending_netevent(void)
832 static int reentering
= 0;
833 extern int select_result(WPARAM
, LPARAM
);
837 return; /* don't unpend the pending */
839 pending_netevent
= FALSE
;
842 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
845 if (ret
== 0 && !session_closed
) {
846 /* Abnormal exits will already have set session_closed and taken
847 * appropriate action. */
848 if (cfg
.close_on_exit
== COE_ALWAYS
||
849 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
851 session_closed
= TRUE
;
852 SetWindowText(hwnd
, "PuTTY (inactive)");
853 MessageBox(hwnd
, "Connection closed by remote host",
854 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
860 * Copy the colour palette from the configuration data into defpal.
861 * This is non-trivial because the colour indices are different.
863 static void cfgtopalette(void)
866 static const int ww
[] = {
867 6, 7, 8, 9, 10, 11, 12, 13,
868 14, 15, 16, 17, 18, 19, 20, 21,
869 0, 1, 2, 3, 4, 4, 5, 5
872 for (i
= 0; i
< 24; i
++) {
874 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
875 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
876 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
881 * Set up the colour palette.
883 static void init_palette(void)
886 HDC hdc
= GetDC(hwnd
);
888 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
889 logpal
= smalloc(sizeof(*logpal
)
890 - sizeof(logpal
->palPalEntry
)
891 + NCOLOURS
* sizeof(PALETTEENTRY
));
892 logpal
->palVersion
= 0x300;
893 logpal
->palNumEntries
= NCOLOURS
;
894 for (i
= 0; i
< NCOLOURS
; i
++) {
895 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
896 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
897 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
898 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
900 pal
= CreatePalette(logpal
);
902 SelectPalette(hdc
, pal
, FALSE
);
904 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
907 ReleaseDC(hwnd
, hdc
);
910 for (i
= 0; i
< NCOLOURS
; i
++)
911 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
915 for (i
= 0; i
< NCOLOURS
; i
++)
916 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
917 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
921 * Initialise all the fonts we will need initially. There may be as many as
922 * three or as few as one. The other (poentially) twentyone fonts are done
923 * if/when they are needed.
927 * - check the font width and height, correcting our guesses if
930 * - verify that the bold font is the same width as the ordinary
931 * one, and engage shadow bolding if not.
933 * - verify that the underlined font is the same width as the
934 * ordinary one (manual underlining by means of line drawing can
935 * be done in a pinch).
937 static void init_fonts(int pick_width
, int pick_height
)
944 int fw_dontcare
, fw_bold
;
946 for (i
= 0; i
< FONT_MAXNO
; i
++)
949 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
952 if (cfg
.fontisbold
) {
953 fw_dontcare
= FW_BOLD
;
956 fw_dontcare
= FW_DONTCARE
;
963 font_height
= pick_height
;
965 font_height
= cfg
.fontheight
;
966 if (font_height
> 0) {
968 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
971 font_width
= pick_width
;
974 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
975 c, OUT_DEFAULT_PRECIS, \
976 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
977 FIXED_PITCH | FF_DONTCARE, cfg.font)
979 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
981 lfont
.lfHeight
= font_height
;
982 lfont
.lfWidth
= font_width
;
983 lfont
.lfEscapement
= 0;
984 lfont
.lfOrientation
= 0;
985 lfont
.lfWeight
= fw_dontcare
;
986 lfont
.lfItalic
= FALSE
;
987 lfont
.lfUnderline
= FALSE
;
988 lfont
.lfStrikeOut
= FALSE
;
989 lfont
.lfCharSet
= cfg
.fontcharset
;
990 lfont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
991 lfont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
992 lfont
.lfQuality
= DEFAULT_QUALITY
;
993 lfont
.lfPitchAndFamily
= FIXED_PITCH
| FF_DONTCARE
;
994 strncpy(lfont
.lfFaceName
, cfg
.font
, LF_FACESIZE
);
996 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
997 GetTextMetrics(hdc
, &tm
);
999 if (pick_width
== 0 || pick_height
== 0) {
1000 font_height
= tm
.tmHeight
;
1001 font_width
= tm
.tmAveCharWidth
;
1003 font_dualwidth
= (tm
.tmAveCharWidth
!= tm
.tmMaxCharWidth
);
1005 #ifdef RDB_DEBUG_PATCH
1006 debug(23, "Primary font H=%d, AW=%d, MW=%d",
1007 tm
.tmHeight
, tm
.tmAveCharWidth
, tm
.tmMaxCharWidth
);
1012 DWORD cset
= tm
.tmCharSet
;
1013 memset(&info
, 0xFF, sizeof(info
));
1015 /* !!! Yes the next line is right */
1016 if (cset
== OEM_CHARSET
)
1017 font_codepage
= GetOEMCP();
1019 if (TranslateCharsetInfo
1020 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
1025 GetCPInfo(font_codepage
, &cpinfo
);
1026 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
1029 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
1032 * Some fonts, e.g. 9-pt Courier, draw their underlines
1033 * outside their character cell. We successfully prevent
1034 * screen corruption by clipping the text output, but then
1035 * we lose the underline completely. Here we try to work
1036 * out whether this is such a font, and if it is, we set a
1037 * flag that causes underlines to be drawn by hand.
1039 * Having tried other more sophisticated approaches (such
1040 * as examining the TEXTMETRIC structure or requesting the
1041 * height of a string), I think we'll do this the brute
1042 * force way: we create a small bitmap, draw an underlined
1043 * space on it, and test to see whether any pixels are
1044 * foreground-coloured. (Since we expect the underline to
1045 * go all the way across the character cell, we only search
1046 * down a single column of the bitmap, half way across.)
1050 HBITMAP und_bm
, und_oldbm
;
1054 und_dc
= CreateCompatibleDC(hdc
);
1055 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
1056 und_oldbm
= SelectObject(und_dc
, und_bm
);
1057 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
1058 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
1059 SetTextColor(und_dc
, RGB(255, 255, 255));
1060 SetBkColor(und_dc
, RGB(0, 0, 0));
1061 SetBkMode(und_dc
, OPAQUE
);
1062 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
1064 for (i
= 0; i
< font_height
; i
++) {
1065 c
= GetPixel(und_dc
, font_width
/ 2, i
);
1066 if (c
!= RGB(0, 0, 0))
1069 SelectObject(und_dc
, und_oldbm
);
1070 DeleteObject(und_bm
);
1073 und_mode
= UND_LINE
;
1074 DeleteObject(fonts
[FONT_UNDERLINE
]);
1075 fonts
[FONT_UNDERLINE
] = 0;
1079 if (bold_mode
== BOLD_FONT
) {
1080 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
1084 descent
= tm
.tmAscent
+ 1;
1085 if (descent
>= font_height
)
1086 descent
= font_height
- 1;
1088 for (i
= 0; i
< 3; i
++) {
1090 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
1091 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
1098 ReleaseDC(hwnd
, hdc
);
1100 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1101 und_mode
= UND_LINE
;
1102 DeleteObject(fonts
[FONT_UNDERLINE
]);
1103 fonts
[FONT_UNDERLINE
] = 0;
1106 if (bold_mode
== BOLD_FONT
&&
1107 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1108 bold_mode
= BOLD_SHADOW
;
1109 DeleteObject(fonts
[FONT_BOLD
]);
1110 fonts
[FONT_BOLD
] = 0;
1112 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1117 static void another_font(int fontno
)
1120 int fw_dontcare
, fw_bold
;
1124 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1127 basefont
= (fontno
& ~(FONT_BOLDUND
));
1128 if (basefont
!= fontno
&& !fontflag
[basefont
])
1129 another_font(basefont
);
1131 if (cfg
.fontisbold
) {
1132 fw_dontcare
= FW_BOLD
;
1135 fw_dontcare
= FW_DONTCARE
;
1139 c
= cfg
.fontcharset
;
1145 if (fontno
& FONT_WIDE
)
1147 if (fontno
& FONT_NARROW
)
1149 if (fontno
& FONT_OEM
)
1151 if (fontno
& FONT_BOLD
)
1153 if (fontno
& FONT_UNDERLINE
)
1157 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1158 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1159 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1160 FIXED_PITCH
| FF_DONTCARE
, s
);
1162 fontflag
[fontno
] = 1;
1165 static void deinit_fonts(void)
1168 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1170 DeleteObject(fonts
[i
]);
1176 void request_resize(int w
, int h
)
1180 /* If the window is maximized supress resizing attempts */
1181 if (IsZoomed(hwnd
)) {
1182 if (cfg
.resize_action
== RESIZE_TERM
)
1186 if (cfg
.resize_action
== RESIZE_DISABLED
) return;
1187 if (h
== rows
&& w
== cols
) return;
1189 /* Sanity checks ... */
1191 static int first_time
= 1;
1194 switch (first_time
) {
1196 /* Get the size of the screen */
1197 if (GetClientRect(GetDesktopWindow(), &ss
))
1198 /* first_time = 0 */ ;
1204 /* Make sure the values are sane */
1205 width
= (ss
.right
- ss
.left
- extra_width
) / 4;
1206 height
= (ss
.bottom
- ss
.top
- extra_height
) / 6;
1208 if (w
> width
|| h
> height
)
1217 term_size(h
, w
, cfg
.savelines
);
1219 if (cfg
.resize_action
!= RESIZE_FONT
&& !IsZoomed(hwnd
)) {
1220 width
= extra_width
+ font_width
* w
;
1221 height
= extra_height
+ font_height
* h
;
1223 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1224 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1225 SWP_NOMOVE
| SWP_NOZORDER
);
1229 InvalidateRect(hwnd
, NULL
, TRUE
);
1232 static void reset_window(int reinit
) {
1234 * This function decides how to resize or redraw when the
1235 * user changes something.
1237 * This function doesn't like to change the terminal size but if the
1238 * font size is locked that may be it's only soluion.
1240 int win_width
, win_height
;
1243 #ifdef RDB_DEBUG_PATCH
1244 debug((27, "reset_window()"));
1247 /* Current window sizes ... */
1248 GetWindowRect(hwnd
, &wr
);
1249 GetClientRect(hwnd
, &cr
);
1251 win_width
= cr
.right
- cr
.left
;
1252 win_height
= cr
.bottom
- cr
.top
;
1254 if (cfg
.resize_action
== RESIZE_DISABLED
) reinit
= 2;
1256 /* Are we being forced to reload the fonts ? */
1258 #ifdef RDB_DEBUG_PATCH
1259 debug((27, "reset_window() -- Forced deinit"));
1265 /* Oh, looks like we're minimised */
1266 if (win_width
== 0 || win_height
== 0)
1269 /* Is the window out of position ? */
1271 (offset_width
!= (win_width
-font_width
*cols
)/2 ||
1272 offset_height
!= (win_height
-font_height
*rows
)/2) ){
1273 offset_width
= (win_width
-font_width
*cols
)/2;
1274 offset_height
= (win_height
-font_height
*rows
)/2;
1275 InvalidateRect(hwnd
, NULL
, TRUE
);
1276 #ifdef RDB_DEBUG_PATCH
1277 debug((27, "reset_window() -> Reposition terminal"));
1281 if (IsZoomed(hwnd
)) {
1282 /* We're fullscreen, this means we must not change the size of
1283 * the window so it's the font size or the terminal itself.
1286 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1287 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1289 if (cfg
.resize_action
!= RESIZE_TERM
) {
1290 if ( font_width
!= win_width
/cols
||
1291 font_height
!= win_height
/rows
) {
1293 init_fonts(win_width
/cols
, win_height
/rows
);
1294 offset_width
= (win_width
-font_width
*cols
)/2;
1295 offset_height
= (win_height
-font_height
*rows
)/2;
1296 InvalidateRect(hwnd
, NULL
, TRUE
);
1297 #ifdef RDB_DEBUG_PATCH
1298 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1299 font_width
, font_height
));
1303 if ( font_width
!= win_width
/cols
||
1304 font_height
!= win_height
/rows
) {
1305 /* Our only choice at this point is to change the
1306 * size of the terminal; Oh well.
1308 term_size( win_height
/font_height
, win_width
/font_width
,
1310 offset_width
= (win_width
-font_width
*cols
)/2;
1311 offset_height
= (win_height
-font_height
*rows
)/2;
1312 InvalidateRect(hwnd
, NULL
, TRUE
);
1313 #ifdef RDB_DEBUG_PATCH
1314 debug((27, "reset_window() -> Zoomed term_size"));
1321 /* Hmm, a force re-init means we should ignore the current window
1322 * so we resize to the default font size.
1325 #ifdef RDB_DEBUG_PATCH
1326 debug((27, "reset_window() -> Forced re-init"));
1329 offset_width
= offset_height
= cfg
.window_border
;
1330 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1331 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1333 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1334 win_height
!= font_height
*rows
+ offset_height
*2) {
1336 /* If this is too large windows will resize it to the maximum
1337 * allowed window size, we will then be back in here and resize
1338 * the font or terminal to fit.
1340 SetWindowPos(hwnd
, NULL
, 0, 0,
1341 font_width
*cols
+ extra_width
,
1342 font_height
*rows
+ extra_height
,
1343 SWP_NOMOVE
| SWP_NOZORDER
);
1346 InvalidateRect(hwnd
, NULL
, TRUE
);
1350 /* Okay the user doesn't want us to change the font so we try the
1351 * window. But that may be too big for the screen which forces us
1352 * to change the terminal.
1354 if ((cfg
.resize_action
== RESIZE_TERM
&& reinit
<=0) ||
1355 (cfg
.resize_action
== RESIZE_EITHER
&& reinit
<0) ||
1357 offset_width
= offset_height
= cfg
.window_border
;
1358 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1359 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1361 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1362 win_height
!= font_height
*rows
+ offset_height
*2) {
1367 GetClientRect(GetDesktopWindow(), &ss
);
1368 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1369 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1372 if ( rows
> height
|| cols
> width
) {
1373 if (cfg
.resize_action
== RESIZE_EITHER
) {
1374 /* Make the font the biggest we can */
1376 font_width
= (ss
.right
- ss
.left
- extra_width
)/cols
;
1378 font_height
= (ss
.bottom
- ss
.top
- extra_height
)/rows
;
1381 init_fonts(font_width
, font_height
);
1383 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1384 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1386 if ( height
> rows
) height
= rows
;
1387 if ( width
> cols
) width
= cols
;
1388 term_size(height
, width
, cfg
.savelines
);
1389 #ifdef RDB_DEBUG_PATCH
1390 debug((27, "reset_window() -> term resize to (%d,%d)",
1396 SetWindowPos(hwnd
, NULL
, 0, 0,
1397 font_width
*cols
+ extra_width
,
1398 font_height
*rows
+ extra_height
,
1399 SWP_NOMOVE
| SWP_NOZORDER
);
1401 InvalidateRect(hwnd
, NULL
, TRUE
);
1402 #ifdef RDB_DEBUG_PATCH
1403 debug((27, "reset_window() -> window resize to (%d,%d)",
1404 font_width
*cols
+ extra_width
,
1405 font_height
*rows
+ extra_height
));
1411 /* We're allowed to or must change the font but do we want to ? */
1413 if (font_width
!= (win_width
-cfg
.window_border
*2)/cols
||
1414 font_height
!= (win_height
-cfg
.window_border
*2)/rows
) {
1417 init_fonts((win_width
-cfg
.window_border
*2)/cols
,
1418 (win_height
-cfg
.window_border
*2)/rows
);
1419 offset_width
= (win_width
-font_width
*cols
)/2;
1420 offset_height
= (win_height
-font_height
*rows
)/2;
1422 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+offset_width
*2;
1423 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1425 InvalidateRect(hwnd
, NULL
, TRUE
);
1426 #ifdef RDB_DEBUG_PATCH
1427 debug((25, "reset_window() -> font resize to (%d,%d)",
1428 font_width
, font_height
));
1433 static void set_input_locale(HKL kl
)
1437 GetLocaleInfo(LOWORD(kl
), LOCALE_IDEFAULTANSICODEPAGE
,
1438 lbuf
, sizeof(lbuf
));
1440 kbd_codepage
= atoi(lbuf
);
1443 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
, int alt
)
1445 int thistime
= GetMessageTime();
1447 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1448 lastbtn
= MBT_NOTHING
;
1449 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
, alt
);
1453 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1454 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1455 lastact
== MA_2CLK ? MA_3CLK
:
1456 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1461 if (lastact
!= MA_NOTHING
)
1462 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
, alt
);
1463 lasttime
= thistime
;
1467 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1468 * into a cooked one (SELECT, EXTEND, PASTE).
1470 Mouse_Button
translate_button(Mouse_Button button
)
1472 if (button
== MBT_LEFT
)
1474 if (button
== MBT_MIDDLE
)
1475 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1476 if (button
== MBT_RIGHT
)
1477 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1478 return 0; /* shouldn't happen */
1481 static void show_mouseptr(int show
)
1483 static int cursor_visible
= 1;
1484 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1486 if (cursor_visible
&& !show
)
1488 else if (!cursor_visible
&& show
)
1490 cursor_visible
= show
;
1493 static int is_alt_pressed(void)
1496 int r
= GetKeyboardState(keystate
);
1499 if (keystate
[VK_MENU
] & 0x80)
1501 if (keystate
[VK_RMENU
] & 0x80)
1506 static int resizing
;
1508 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1509 WPARAM wParam
, LPARAM lParam
)
1512 static int ignore_clip
= FALSE
;
1513 static int need_backend_resize
= FALSE
;
1514 static int fullscr_on_max
= FALSE
;
1518 if (pending_netevent
)
1519 enact_pending_netevent();
1525 if (cfg
.ping_interval
> 0) {
1528 if (now
- last_movement
> cfg
.ping_interval
) {
1529 back
->special(TS_PING
);
1530 last_movement
= now
;
1533 net_pending_errors();
1539 if (!cfg
.warn_on_close
|| session_closed
||
1541 "Are you sure you want to close this session?",
1542 "PuTTY Exit Confirmation",
1543 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1544 DestroyWindow(hwnd
);
1551 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1563 PROCESS_INFORMATION pi
;
1564 HANDLE filemap
= NULL
;
1566 if (wParam
== IDM_DUPSESS
) {
1568 * Allocate a file-mapping memory chunk for the
1571 SECURITY_ATTRIBUTES sa
;
1574 sa
.nLength
= sizeof(sa
);
1575 sa
.lpSecurityDescriptor
= NULL
;
1576 sa
.bInheritHandle
= TRUE
;
1577 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1580 0, sizeof(Config
), NULL
);
1582 p
= (Config
*) MapViewOfFile(filemap
,
1584 0, 0, sizeof(Config
));
1586 *p
= cfg
; /* structure copy */
1590 sprintf(c
, "putty &%p", filemap
);
1592 } else if (wParam
== IDM_SAVEDSESS
) {
1593 if ((lParam
- IDM_SAVED_MIN
) / 16 < nsessions
) {
1595 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1596 cl
= smalloc(16 + strlen(session
));
1597 /* 8, but play safe */
1600 /* not a very important failure mode */
1602 sprintf(cl
, "putty @%s", session
);
1610 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1612 si
.lpReserved
= NULL
;
1613 si
.lpDesktop
= NULL
;
1617 si
.lpReserved2
= NULL
;
1618 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1619 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1622 CloseHandle(filemap
);
1632 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1635 if (!do_reconfig(hwnd
))
1639 /* Disable full-screen if resizing forbidden */
1640 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
1641 EnableMenuItem(m
, IDM_FULLSCREEN
, MF_BYCOMMAND
|
1642 (cfg
.resize_action
== RESIZE_DISABLED
)
1643 ? MF_GRAYED
: MF_ENABLED
);
1644 /* Gracefully unzoom if necessary */
1645 if (IsZoomed(hwnd
) &&
1646 (cfg
.resize_action
== RESIZE_DISABLED
)) {
1647 ShowWindow(hwnd
, SW_RESTORE
);
1651 if (strcmp(prev_cfg
.logfilename
, cfg
.logfilename
) ||
1652 prev_cfg
.logtype
!= cfg
.logtype
) {
1653 logfclose(); /* reset logging */
1659 * Flush the line discipline's edit buffer in the
1660 * case where local editing has just been disabled.
1662 ldisc_send(NULL
, 0, 0);
1670 /* Screen size changed ? */
1671 if (cfg
.height
!= prev_cfg
.height
||
1672 cfg
.width
!= prev_cfg
.width
||
1673 cfg
.savelines
!= prev_cfg
.savelines
||
1674 cfg
.resize_action
== RESIZE_FONT
||
1675 (cfg
.resize_action
== RESIZE_EITHER
&& IsZoomed(hwnd
)) ||
1676 cfg
.resize_action
== RESIZE_DISABLED
)
1677 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1679 /* Enable or disable the scroll bar, etc */
1681 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1682 LONG nexflag
, exflag
=
1683 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1686 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1687 if (cfg
.alwaysontop
) {
1688 nexflag
|= WS_EX_TOPMOST
;
1689 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1690 SWP_NOMOVE
| SWP_NOSIZE
);
1692 nexflag
&= ~(WS_EX_TOPMOST
);
1693 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1694 SWP_NOMOVE
| SWP_NOSIZE
);
1697 if (cfg
.sunken_edge
)
1698 nexflag
|= WS_EX_CLIENTEDGE
;
1700 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1703 if (is_full_screen() ?
1704 cfg
.scrollbar_in_fullscreen
: cfg
.scrollbar
)
1707 nflg
&= ~WS_VSCROLL
;
1709 if (cfg
.resize_action
== RESIZE_DISABLED
||
1711 nflg
&= ~WS_THICKFRAME
;
1713 nflg
|= WS_THICKFRAME
;
1715 if (cfg
.resize_action
== RESIZE_DISABLED
)
1716 nflg
&= ~WS_MAXIMIZEBOX
;
1718 nflg
|= WS_MAXIMIZEBOX
;
1720 if (nflg
!= flag
|| nexflag
!= exflag
) {
1722 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1723 if (nexflag
!= exflag
)
1724 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1726 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1727 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1728 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1736 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1741 set_title(cfg
.wintitle
);
1742 if (IsIconic(hwnd
)) {
1744 cfg
.win_name_always ? window_name
:
1748 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1749 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1750 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1751 cfg
.fontheight
!= prev_cfg
.fontheight
||
1752 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1753 cfg
.vtmode
!= prev_cfg
.vtmode
||
1754 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1755 cfg
.resize_action
== RESIZE_DISABLED
||
1756 cfg
.resize_action
== RESIZE_EITHER
||
1757 (cfg
.resize_action
!= prev_cfg
.resize_action
))
1760 InvalidateRect(hwnd
, NULL
, TRUE
);
1761 reset_window(init_lvl
);
1762 net_pending_errors();
1775 back
->special(TS_AYT
);
1776 net_pending_errors();
1779 back
->special(TS_BRK
);
1780 net_pending_errors();
1783 back
->special(TS_SYNCH
);
1784 net_pending_errors();
1787 back
->special(TS_EC
);
1788 net_pending_errors();
1791 back
->special(TS_EL
);
1792 net_pending_errors();
1795 back
->special(TS_GA
);
1796 net_pending_errors();
1799 back
->special(TS_NOP
);
1800 net_pending_errors();
1803 back
->special(TS_ABORT
);
1804 net_pending_errors();
1807 back
->special(TS_AO
);
1808 net_pending_errors();
1811 back
->special(TS_IP
);
1812 net_pending_errors();
1815 back
->special(TS_SUSP
);
1816 net_pending_errors();
1819 back
->special(TS_EOR
);
1820 net_pending_errors();
1823 back
->special(TS_EOF
);
1824 net_pending_errors();
1830 WinHelp(hwnd
, help_path
,
1831 help_has_contents ? HELP_FINDER
: HELP_CONTENTS
, 0);
1835 * We get this if the System menu has been activated
1842 * We get this if the System menu has been activated
1843 * using the keyboard. This might happen from within
1844 * TranslateKey, in which case it really wants to be
1845 * followed by a `space' character to actually _bring
1846 * the menu up_ rather than just sitting there in
1847 * `ready to appear' state.
1849 show_mouseptr(1); /* make sure pointer is visible */
1851 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1853 case IDM_FULLSCREEN
:
1857 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1858 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1863 #define X_POS(l) ((int)(short)LOWORD(l))
1864 #define Y_POS(l) ((int)(short)HIWORD(l))
1866 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1867 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1868 case WM_LBUTTONDOWN
:
1869 case WM_MBUTTONDOWN
:
1870 case WM_RBUTTONDOWN
:
1878 case WM_LBUTTONDOWN
:
1882 case WM_MBUTTONDOWN
:
1883 button
= MBT_MIDDLE
;
1886 case WM_RBUTTONDOWN
:
1895 button
= MBT_MIDDLE
;
1903 button
= press
= 0; /* shouldn't happen */
1907 * Special case: in full-screen mode, if the left
1908 * button is clicked in the very top left corner of the
1909 * window, we put up the System menu instead of doing
1912 if (is_full_screen() && press
&& button
== MBT_LEFT
&&
1913 X_POS(lParam
) == 0 && Y_POS(lParam
) == 0) {
1914 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_MOUSEMENU
, 0);
1919 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1920 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
,
1924 term_mouse(button
, MA_RELEASE
,
1925 TO_CHR_X(X_POS(lParam
)),
1926 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1927 wParam
& MK_CONTROL
, is_alt_pressed());
1935 * Add the mouse position and message time to the random
1938 noise_ultralight(lParam
);
1940 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1942 if (wParam
& MK_LBUTTON
)
1944 else if (wParam
& MK_MBUTTON
)
1948 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1949 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1950 wParam
& MK_CONTROL
, is_alt_pressed());
1953 case WM_NCMOUSEMOVE
:
1955 noise_ultralight(lParam
);
1957 case WM_IGNORE_CLIP
:
1958 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1960 case WM_DESTROYCLIPBOARD
:
1963 ignore_clip
= FALSE
;
1969 hdc
= BeginPaint(hwnd
, &p
);
1971 SelectPalette(hdc
, pal
, TRUE
);
1972 RealizePalette(hdc
);
1975 (p
.rcPaint
.left
-offset_width
)/font_width
,
1976 (p
.rcPaint
.top
-offset_height
)/font_height
,
1977 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
1978 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
1981 p
.rcPaint
.left
< offset_width
||
1982 p
.rcPaint
.top
< offset_height
||
1983 p
.rcPaint
.right
>= offset_width
+ font_width
*cols
||
1984 p
.rcPaint
.bottom
>= offset_height
+ font_height
*rows
)
1986 HBRUSH fillcolour
, oldbrush
;
1988 fillcolour
= CreateSolidBrush (
1989 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1990 oldbrush
= SelectObject(hdc
, fillcolour
);
1991 edge
= CreatePen(PS_SOLID
, 0,
1992 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1993 oldpen
= SelectObject(hdc
, edge
);
1995 ExcludeClipRect(hdc
,
1996 offset_width
, offset_height
,
1997 offset_width
+font_width
*cols
,
1998 offset_height
+font_height
*rows
);
2000 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
2001 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
2003 // SelectClipRgn(hdc, NULL);
2005 SelectObject(hdc
, oldbrush
);
2006 DeleteObject(fillcolour
);
2007 SelectObject(hdc
, oldpen
);
2010 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
2011 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
2017 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2018 * but the only one that's likely to try to overload us is FD_READ.
2019 * This means buffering just one is fine.
2021 if (pending_netevent
)
2022 enact_pending_netevent();
2024 pending_netevent
= TRUE
;
2025 pend_netevent_wParam
= wParam
;
2026 pend_netevent_lParam
= lParam
;
2027 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
2028 enact_pending_netevent();
2030 time(&last_movement
);
2034 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
2036 flash_window(0); /* stop */
2045 caret_x
= caret_y
= -1; /* ensure caret is replaced next time */
2049 case WM_ENTERSIZEMOVE
:
2050 #ifdef RDB_DEBUG_PATCH
2051 debug((27, "WM_ENTERSIZEMOVE"));
2055 need_backend_resize
= FALSE
;
2057 case WM_EXITSIZEMOVE
:
2060 #ifdef RDB_DEBUG_PATCH
2061 debug((27, "WM_EXITSIZEMOVE"));
2063 if (need_backend_resize
) {
2064 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
2065 InvalidateRect(hwnd
, NULL
, TRUE
);
2070 * This does two jobs:
2071 * 1) Keep the sizetip uptodate
2072 * 2) Make sure the window size is _stepped_ in units of the font size.
2074 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2075 int width
, height
, w
, h
, ew
, eh
;
2076 LPRECT r
= (LPRECT
) lParam
;
2078 if ( !need_backend_resize
&& cfg
.resize_action
== RESIZE_EITHER
&&
2079 (cfg
.height
!= rows
|| cfg
.width
!= cols
)) {
2081 * Great! It seems that both the terminal size and the
2082 * font size have been changed and the user is now dragging.
2084 * It will now be difficult to get back to the configured
2087 * This would be easier but it seems to be too confusing.
2089 term_size(cfg.height, cfg.width, cfg.savelines);
2092 cfg
.height
=rows
; cfg
.width
=cols
;
2094 InvalidateRect(hwnd
, NULL
, TRUE
);
2095 need_backend_resize
= TRUE
;
2098 width
= r
->right
- r
->left
- extra_width
;
2099 height
= r
->bottom
- r
->top
- extra_height
;
2100 w
= (width
+ font_width
/ 2) / font_width
;
2103 h
= (height
+ font_height
/ 2) / font_height
;
2106 UpdateSizeTip(hwnd
, w
, h
);
2107 ew
= width
- w
* font_width
;
2108 eh
= height
- h
* font_height
;
2110 if (wParam
== WMSZ_LEFT
||
2111 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2117 if (wParam
== WMSZ_TOP
||
2118 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2128 int width
, height
, w
, h
, rv
= 0;
2129 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
2130 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2131 LPRECT r
= (LPRECT
) lParam
;
2133 width
= r
->right
- r
->left
- ex_width
;
2134 height
= r
->bottom
- r
->top
- ex_height
;
2136 w
= (width
+ cols
/2)/cols
;
2137 h
= (height
+ rows
/2)/rows
;
2138 if ( r
->right
!= r
->left
+ w
*cols
+ ex_width
)
2141 if (wParam
== WMSZ_LEFT
||
2142 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2143 r
->left
= r
->right
- w
*cols
- ex_width
;
2145 r
->right
= r
->left
+ w
*cols
+ ex_width
;
2147 if (r
->bottom
!= r
->top
+ h
*rows
+ ex_height
)
2150 if (wParam
== WMSZ_TOP
||
2151 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2152 r
->top
= r
->bottom
- h
*rows
- ex_height
;
2154 r
->bottom
= r
->top
+ h
*rows
+ ex_height
;
2158 /* break; (never reached) */
2159 case WM_FULLSCR_ON_MAX
:
2160 fullscr_on_max
= TRUE
;
2163 sys_cursor_update();
2166 #ifdef RDB_DEBUG_PATCH
2167 debug((27, "WM_SIZE %s (%d,%d)",
2168 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2169 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2170 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2171 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2173 LOWORD(lParam
), HIWORD(lParam
)));
2175 if (wParam
== SIZE_MINIMIZED
)
2177 cfg
.win_name_always ? window_name
: icon_name
);
2178 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2179 SetWindowText(hwnd
, window_name
);
2180 if (wParam
== SIZE_RESTORED
)
2181 clear_full_screen();
2182 if (wParam
== SIZE_MAXIMIZED
&& fullscr_on_max
) {
2184 fullscr_on_max
= FALSE
;
2187 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2188 /* A resize, well it better be a minimize. */
2192 int width
, height
, w
, h
;
2194 width
= LOWORD(lParam
);
2195 height
= HIWORD(lParam
);
2198 if (wParam
== SIZE_MAXIMIZED
&& !was_zoomed
) {
2202 if (cfg
.resize_action
== RESIZE_TERM
) {
2203 w
= width
/ font_width
;
2205 h
= height
/ font_height
;
2208 term_size(h
, w
, cfg
.savelines
);
2211 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2213 if (cfg
.resize_action
== RESIZE_TERM
)
2214 term_size(prev_rows
, prev_cols
, cfg
.savelines
);
2215 if (cfg
.resize_action
!= RESIZE_FONT
)
2220 /* This is an unexpected resize, these will normally happen
2221 * if the window is too large. Probably either the user
2222 * selected a huge font or the screen size has changed.
2224 * This is also called with minimize.
2226 else reset_window(-1);
2230 * Don't call back->size in mid-resize. (To prevent
2231 * massive numbers of resize events getting sent
2232 * down the connection during an NT opaque drag.)
2235 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2236 need_backend_resize
= TRUE
;
2237 w
= (width
-cfg
.window_border
*2) / font_width
;
2239 h
= (height
-cfg
.window_border
*2) / font_height
;
2248 sys_cursor_update();
2251 switch (LOWORD(wParam
)) {
2265 term_scroll(0, +rows
/ 2);
2268 term_scroll(0, -rows
/ 2);
2270 case SB_THUMBPOSITION
:
2272 term_scroll(1, HIWORD(wParam
));
2276 case WM_PALETTECHANGED
:
2277 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2278 HDC hdc
= get_ctx();
2280 if (RealizePalette(hdc
) > 0)
2286 case WM_QUERYNEWPALETTE
:
2288 HDC hdc
= get_ctx();
2290 if (RealizePalette(hdc
) > 0)
2302 * Add the scan code and keypress timing to the random
2305 noise_ultralight(lParam
);
2308 * We don't do TranslateMessage since it disassociates the
2309 * resulting CHAR message from the KEYDOWN that sparked it,
2310 * which we occasionally don't want. Instead, we process
2311 * KEYDOWN, and call the Win32 translator functions so that
2312 * we get the translations under _our_ control.
2315 unsigned char buf
[20];
2318 if (wParam
== VK_PROCESSKEY
) {
2321 m
.message
= WM_KEYDOWN
;
2323 m
.lParam
= lParam
& 0xdfff;
2324 TranslateMessage(&m
);
2326 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2328 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2332 * Interrupt an ongoing paste. I'm not sure
2333 * this is sensible, but for the moment it's
2334 * preferable to having to faff about buffering
2340 * We need not bother about stdin backlogs
2341 * here, because in GUI PuTTY we can't do
2342 * anything about it anyway; there's no means
2343 * of asking Windows to hold off on KEYDOWN
2344 * messages. We _have_ to buffer everything
2347 ldisc_send(buf
, len
, 1);
2352 net_pending_errors();
2354 case WM_INPUTLANGCHANGE
:
2355 /* wParam == Font number */
2356 /* lParam == Locale */
2357 set_input_locale((HKL
)lParam
);
2358 sys_cursor_update();
2361 if(wParam
== IMN_SETOPENSTATUS
) {
2362 HIMC hImc
= ImmGetContext(hwnd
);
2363 ImmSetCompositionFont(hImc
, &lfont
);
2364 ImmReleaseContext(hwnd
, hImc
);
2368 case WM_IME_COMPOSITION
:
2374 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2375 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2377 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2378 break; /* fall back to DefWindowProc */
2380 hIMC
= ImmGetContext(hwnd
);
2381 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2385 buff
= (char*) smalloc(n
);
2386 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2388 * Jaeyoun Chung reports that Korean character
2389 * input doesn't work correctly if we do a single
2390 * luni_send() covering the whole of buff. So
2391 * instead we luni_send the characters one by one.
2393 for (i
= 0; i
< n
; i
+= 2)
2394 luni_send((unsigned short *)(buff
+i
), 1, 1);
2397 ImmReleaseContext(hwnd
, hIMC
);
2402 if (wParam
& 0xFF00) {
2403 unsigned char buf
[2];
2406 buf
[0] = wParam
>> 8;
2407 lpage_send(kbd_codepage
, buf
, 2, 1);
2409 char c
= (unsigned char) wParam
;
2410 lpage_send(kbd_codepage
, &c
, 1, 1);
2416 * Nevertheless, we are prepared to deal with WM_CHAR
2417 * messages, should they crop up. So if someone wants to
2418 * post the things to us as part of a macro manoeuvre,
2419 * we're ready to cope.
2422 char c
= (unsigned char)wParam
;
2423 lpage_send(CP_ACP
, &c
, 1, 1);
2427 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2428 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2432 if (message
== wm_mousewheel
) {
2433 int shift_pressed
=0, control_pressed
=0, alt_pressed
=0;
2435 if (message
== WM_MOUSEWHEEL
) {
2436 wheel_accumulator
+= (short)HIWORD(wParam
);
2437 shift_pressed
=LOWORD(wParam
) & MK_SHIFT
;
2438 control_pressed
=LOWORD(wParam
) & MK_CONTROL
;
2441 wheel_accumulator
+= (int)wParam
;
2442 if (GetKeyboardState(keys
)!=0) {
2443 shift_pressed
=keys
[VK_SHIFT
]&0x80;
2444 control_pressed
=keys
[VK_CONTROL
]&0x80;
2448 /* process events when the threshold is reached */
2449 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
2452 /* reduce amount for next time */
2453 if (wheel_accumulator
> 0) {
2455 wheel_accumulator
-= WHEEL_DELTA
;
2456 } else if (wheel_accumulator
< 0) {
2458 wheel_accumulator
+= WHEEL_DELTA
;
2462 if (send_raw_mouse
&&
2463 !(cfg
.mouse_override
&& shift_pressed
)) {
2464 /* send a mouse-down followed by a mouse up */
2467 TO_CHR_X(X_POS(lParam
)),
2468 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2469 control_pressed
, is_alt_pressed());
2470 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
2471 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2472 control_pressed
, is_alt_pressed());
2474 /* trigger a scroll */
2476 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
2483 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2487 * Move the system caret. (We maintain one, even though it's
2488 * invisible, for the benefit of blind people: apparently some
2489 * helper software tracks the system caret, so we should arrange to
2492 void sys_cursor(int x
, int y
)
2496 if (!has_focus
) return;
2499 * Avoid gratuitously re-updating the cursor position and IMM
2500 * window if there's no actual change required.
2502 cx
= x
* font_width
+ offset_width
;
2503 cy
= y
* font_height
+ offset_height
;
2504 if (cx
== caret_x
&& cy
== caret_y
)
2509 sys_cursor_update();
2512 static void sys_cursor_update(void)
2517 if (!has_focus
) return;
2519 if (caret_x
< 0 || caret_y
< 0)
2522 SetCaretPos(caret_x
, caret_y
);
2524 /* IMM calls on Win98 and beyond only */
2525 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2527 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2528 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2530 /* we should have the IMM functions */
2531 hIMC
= ImmGetContext(hwnd
);
2532 cf
.dwStyle
= CFS_POINT
;
2533 cf
.ptCurrentPos
.x
= caret_x
;
2534 cf
.ptCurrentPos
.y
= caret_y
;
2535 ImmSetCompositionWindow(hIMC
, &cf
);
2537 ImmReleaseContext(hwnd
, hIMC
);
2541 * Draw a line of text in the window, at given character
2542 * coordinates, in given attributes.
2544 * We are allowed to fiddle with the contents of `text'.
2546 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2547 unsigned long attr
, int lattr
)
2550 int nfg
, nbg
, nfont
;
2553 int force_manual_underline
= 0;
2554 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2555 int char_width
= fnt_width
;
2556 int text_adjust
= 0;
2557 static int *IpDx
= 0, IpDxLEN
= 0;
2559 if (attr
& ATTR_WIDE
)
2562 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2564 if (len
> IpDxLEN
) {
2566 IpDx
= smalloc((len
+ 16) * sizeof(int));
2567 IpDxLEN
= (len
+ 16);
2569 for (i
= 0; i
< IpDxLEN
; i
++)
2570 IpDx
[i
] = char_width
;
2573 /* Only want the left half of double width lines */
2574 if (lattr
!= LATTR_NORM
&& x
*2 >= cols
)
2582 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
2583 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2584 attr
^= ATTR_CUR_XOR
;
2588 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2589 /* Assume a poorman font is borken in other ways too. */
2599 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2602 if (attr
& ATTR_NARROW
)
2603 nfont
|= FONT_NARROW
;
2605 /* Special hack for the VT100 linedraw glyphs. */
2606 if ((attr
& CSET_MASK
) == 0x2300) {
2607 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2608 switch ((unsigned char) (text
[0])) {
2610 text_adjust
= -2 * font_height
/ 5;
2613 text_adjust
= -1 * font_height
/ 5;
2616 text_adjust
= font_height
/ 5;
2619 text_adjust
= 2 * font_height
/ 5;
2622 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2625 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2626 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2627 if (attr
& ATTR_UNDER
) {
2628 attr
&= ~ATTR_UNDER
;
2629 force_manual_underline
= 1;
2634 /* Anything left as an original character set is unprintable. */
2635 if (DIRECT_CHAR(attr
)) {
2638 memset(text
, 0xFD, len
);
2642 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2645 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2646 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2647 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2649 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2650 nfont
|= FONT_UNDERLINE
;
2651 another_font(nfont
);
2652 if (!fonts
[nfont
]) {
2653 if (nfont
& FONT_UNDERLINE
)
2654 force_manual_underline
= 1;
2655 /* Don't do the same for manual bold, it could be bad news. */
2657 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2659 another_font(nfont
);
2661 nfont
= FONT_NORMAL
;
2662 if (attr
& ATTR_REVERSE
) {
2667 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2669 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2673 SelectObject(hdc
, fonts
[nfont
]);
2674 SetTextColor(hdc
, fg
);
2675 SetBkColor(hdc
, bg
);
2676 SetBkMode(hdc
, OPAQUE
);
2679 line_box
.right
= x
+ char_width
* len
;
2680 line_box
.bottom
= y
+ font_height
;
2682 /* Only want the left half of double width lines */
2683 if (line_box
.right
> font_width
*cols
+offset_width
)
2684 line_box
.right
= font_width
*cols
+offset_width
;
2686 /* We're using a private area for direct to font. (512 chars.) */
2687 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2688 /* Ho Hum, dbcs fonts are a PITA! */
2689 /* To display on W9x I have to convert to UCS */
2690 static wchar_t *uni_buf
= 0;
2691 static int uni_len
= 0;
2693 if (len
> uni_len
) {
2695 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2698 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2699 uni_buf
[nlen
] = 0xFFFD;
2700 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2701 IpDx
[nlen
] += char_width
;
2702 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2703 text
+mptr
, 2, uni_buf
+nlen
, 1);
2708 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2709 text
+mptr
, 1, uni_buf
+nlen
, 1);
2717 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2718 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2719 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2720 SetBkMode(hdc
, TRANSPARENT
);
2721 ExtTextOutW(hdc
, x
- 1,
2722 y
- font_height
* (lattr
==
2723 LATTR_BOT
) + text_adjust
,
2724 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2728 } else if (DIRECT_FONT(attr
)) {
2730 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2731 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2732 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2733 SetBkMode(hdc
, TRANSPARENT
);
2735 /* GRR: This draws the character outside it's box and can leave
2736 * 'droppings' even with the clip box! I suppose I could loop it
2737 * one character at a time ... yuk.
2739 * Or ... I could do a test print with "W", and use +1 or -1 for this
2740 * shift depending on if the leftmost column is blank...
2742 ExtTextOut(hdc
, x
- 1,
2743 y
- font_height
* (lattr
==
2744 LATTR_BOT
) + text_adjust
,
2745 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2748 /* And 'normal' unicode characters */
2749 static WCHAR
*wbuf
= NULL
;
2750 static int wlen
= 0;
2755 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2757 for (i
= 0; i
< len
; i
++)
2758 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2761 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2762 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2764 /* And the shadow bold hack. */
2765 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2766 SetBkMode(hdc
, TRANSPARENT
);
2767 ExtTextOutW(hdc
, x
- 1,
2768 y
- font_height
* (lattr
==
2769 LATTR_BOT
) + text_adjust
,
2770 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2773 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2774 (und_mode
== UND_LINE
2775 && (attr
& ATTR_UNDER
)))) {
2778 if (lattr
== LATTR_BOT
)
2779 dec
= dec
* 2 - font_height
;
2781 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2782 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2783 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2784 oldpen
= SelectObject(hdc
, oldpen
);
2785 DeleteObject(oldpen
);
2789 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2790 unsigned long attr
, int lattr
)
2796 int ctype
= cfg
.cursor_type
;
2798 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2799 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2800 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2804 attr
|= TATTR_RIGHTCURS
;
2807 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2808 if (attr
& ATTR_WIDE
)
2815 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2818 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2819 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2820 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2821 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2822 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2823 Polyline(hdc
, pts
, 5);
2824 oldpen
= SelectObject(hdc
, oldpen
);
2825 DeleteObject(oldpen
);
2826 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2827 int startx
, starty
, dx
, dy
, length
, i
;
2830 starty
= y
+ descent
;
2833 length
= char_width
;
2836 if (attr
& TATTR_RIGHTCURS
)
2837 xadjust
= char_width
- 1;
2838 startx
= x
+ xadjust
;
2842 length
= font_height
;
2844 if (attr
& TATTR_ACTCURS
) {
2847 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2848 MoveToEx(hdc
, startx
, starty
, NULL
);
2849 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2850 oldpen
= SelectObject(hdc
, oldpen
);
2851 DeleteObject(oldpen
);
2853 for (i
= 0; i
< length
; i
++) {
2855 SetPixel(hdc
, startx
, starty
, colours
[23]);
2864 /* This function gets the actual width of a character in the normal font.
2866 int CharWidth(Context ctx
, int uc
) {
2870 /* If the font max is the same as the font ave width then this
2871 * function is a no-op.
2873 if (!font_dualwidth
) return 1;
2875 switch (uc
& CSET_MASK
) {
2877 uc
= unitab_line
[uc
& 0xFF];
2880 uc
= unitab_xterm
[uc
& 0xFF];
2883 uc
= unitab_scoacs
[uc
& 0xFF];
2886 if (DIRECT_FONT(uc
)) {
2887 if (dbcs_screenfont
) return 1;
2889 /* Speedup, I know of no font where ascii is the wrong width */
2890 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
2893 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
2894 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2895 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
2896 another_font(FONT_OEM
);
2897 if (!fonts
[FONT_OEM
]) return 0;
2899 SelectObject(hdc
, fonts
[FONT_OEM
]);
2903 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
2904 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
2907 /* Speedup, I know of no font where ascii is the wrong width */
2908 if (uc
>= ' ' && uc
<= '~') return 1;
2910 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2911 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
2912 /* Okay that one worked */ ;
2913 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
2914 /* This should work on 9x too, but it's "less accurate" */ ;
2919 ibuf
+= font_width
/ 2 -1;
2926 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2927 * codes. Returns number of bytes used or zero to drop the message
2928 * or -1 to forward the message to windows.
2930 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2931 unsigned char *output
)
2934 int scan
, left_alt
= 0, key_down
, shift_state
;
2936 unsigned char *p
= output
;
2937 static int alt_sum
= 0;
2939 HKL kbd_layout
= GetKeyboardLayout(0);
2941 static WORD keys
[3];
2942 static int compose_char
= 0;
2943 static WPARAM compose_key
= 0;
2945 r
= GetKeyboardState(keystate
);
2947 memset(keystate
, 0, sizeof(keystate
));
2950 #define SHOW_TOASCII_RESULT
2951 { /* Tell us all about key events */
2952 static BYTE oldstate
[256];
2953 static int first
= 1;
2957 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2960 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2962 } else if ((HIWORD(lParam
) & KF_UP
)
2963 && scan
== (HIWORD(lParam
) & 0xFF)) {
2967 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2968 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2981 debug(("VK_%02x", wParam
));
2983 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2985 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2987 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2988 if (ch
>= ' ' && ch
<= '~')
2989 debug((", '%c'", ch
));
2991 debug((", $%02x", ch
));
2994 debug((", KB0=%02x", keys
[0]));
2996 debug((", KB1=%02x", keys
[1]));
2998 debug((", KB2=%02x", keys
[2]));
3000 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
3002 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
3004 if ((HIWORD(lParam
) & KF_EXTENDED
))
3006 if ((HIWORD(lParam
) & KF_UP
))
3010 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
3011 else if ((HIWORD(lParam
) & KF_UP
))
3012 oldstate
[wParam
& 0xFF] ^= 0x80;
3014 oldstate
[wParam
& 0xFF] ^= 0x81;
3016 for (ch
= 0; ch
< 256; ch
++)
3017 if (oldstate
[ch
] != keystate
[ch
])
3018 debug((", M%02x=%02x", ch
, keystate
[ch
]));
3020 memcpy(oldstate
, keystate
, sizeof(oldstate
));
3024 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
3025 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
3029 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
3030 if ((cfg
.funky_type
== 3 ||
3031 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
3032 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
3034 wParam
= VK_EXECUTE
;
3036 /* UnToggle NUMLock */
3037 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
3038 keystate
[VK_NUMLOCK
] ^= 1;
3041 /* And write back the 'adjusted' state */
3042 SetKeyboardState(keystate
);
3045 /* Disable Auto repeat if required */
3046 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
3049 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
3052 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
3054 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
3055 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
3056 if (cfg
.ctrlaltkeys
)
3057 keystate
[VK_MENU
] = 0;
3059 keystate
[VK_RMENU
] = 0x80;
3064 alt_pressed
= (left_alt
&& key_down
);
3066 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
3067 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
3068 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
3070 /* Note if AltGr was pressed and if it was used as a compose key */
3071 if (!compose_state
) {
3072 compose_key
= 0x100;
3073 if (cfg
.compose_key
) {
3074 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
3075 compose_key
= wParam
;
3077 if (wParam
== VK_APPS
)
3078 compose_key
= wParam
;
3081 if (wParam
== compose_key
) {
3082 if (compose_state
== 0
3083 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
3085 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
3089 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
3093 * Record that we pressed key so the scroll window can be reset, but
3094 * be careful to avoid Shift-UP/Down
3096 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
&&
3097 wParam
!= VK_MENU
&& wParam
!= VK_CONTROL
) {
3101 if (compose_state
> 1 && left_alt
)
3104 /* Sanitize the number pad if not using a PC NumPad */
3105 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
3106 && cfg
.funky_type
!= 2)
3107 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
3108 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
3112 nParam
= VK_NUMPAD0
;
3115 nParam
= VK_NUMPAD1
;
3118 nParam
= VK_NUMPAD2
;
3121 nParam
= VK_NUMPAD3
;
3124 nParam
= VK_NUMPAD4
;
3127 nParam
= VK_NUMPAD5
;
3130 nParam
= VK_NUMPAD6
;
3133 nParam
= VK_NUMPAD7
;
3136 nParam
= VK_NUMPAD8
;
3139 nParam
= VK_NUMPAD9
;
3142 nParam
= VK_DECIMAL
;
3146 if (keystate
[VK_NUMLOCK
] & 1)
3153 /* If a key is pressed and AltGr is not active */
3154 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
3155 /* Okay, prepare for most alts then ... */
3159 /* Lets see if it's a pattern we know all about ... */
3160 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
3161 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
3164 if (wParam
== VK_NEXT
&& shift_state
== 1) {
3165 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
3168 if (wParam
== VK_INSERT
&& shift_state
== 1) {
3172 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
3175 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
3176 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
3179 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
&&
3180 (cfg
.resize_action
!= RESIZE_DISABLED
)) {
3181 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) != KF_REPEAT
)
3185 /* Control-Numlock for app-keypad mode switch */
3186 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
3187 app_keypad_keys
^= 1;
3191 /* Nethack keypad */
3192 if (cfg
.nethack_keypad
&& !left_alt
) {
3195 *p
++ = shift_state ?
'B' : 'b';
3198 *p
++ = shift_state ?
'J' : 'j';
3201 *p
++ = shift_state ?
'N' : 'n';
3204 *p
++ = shift_state ?
'H' : 'h';
3207 *p
++ = shift_state ?
'.' : '.';
3210 *p
++ = shift_state ?
'L' : 'l';
3213 *p
++ = shift_state ?
'Y' : 'y';
3216 *p
++ = shift_state ?
'K' : 'k';
3219 *p
++ = shift_state ?
'U' : 'u';
3224 /* Application Keypad */
3228 if (cfg
.funky_type
== 3 ||
3229 (cfg
.funky_type
<= 1 &&
3230 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3244 if (app_keypad_keys
&& !cfg
.no_applic_k
)
3281 if (cfg
.funky_type
== 2) {
3286 } else if (shift_state
)
3293 if (cfg
.funky_type
== 2)
3297 if (cfg
.funky_type
== 2)
3301 if (cfg
.funky_type
== 2)
3306 if (HIWORD(lParam
) & KF_EXTENDED
)
3312 if (xkey
>= 'P' && xkey
<= 'S')
3313 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3315 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3317 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3322 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3323 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3327 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3333 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3337 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3341 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3346 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3351 /* Control-2 to Control-8 are special */
3352 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3353 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3356 if (shift_state
== 2 && wParam
== 0xBD) {
3360 if (shift_state
== 2 && wParam
== 0xDF) {
3364 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
3371 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3372 * for integer decimal nn.)
3374 * We also deal with the weird ones here. Linux VCs replace F1
3375 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3376 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3382 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3385 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3388 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3391 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3394 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3397 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3400 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3403 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3406 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3409 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3442 if ((shift_state
&2) == 0) switch (wParam
) {
3462 /* Reorder edit keys to physical order */
3463 if (cfg
.funky_type
== 3 && code
<= 6)
3464 code
= "\0\2\1\4\5\3\6"[code
];
3466 if (vt52_mode
&& code
> 0 && code
<= 6) {
3467 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3471 if (cfg
.funky_type
== 5 && /* SCO function keys */
3472 code
>= 11 && code
<= 34) {
3473 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3476 case VK_F1
: index
= 0; break;
3477 case VK_F2
: index
= 1; break;
3478 case VK_F3
: index
= 2; break;
3479 case VK_F4
: index
= 3; break;
3480 case VK_F5
: index
= 4; break;
3481 case VK_F6
: index
= 5; break;
3482 case VK_F7
: index
= 6; break;
3483 case VK_F8
: index
= 7; break;
3484 case VK_F9
: index
= 8; break;
3485 case VK_F10
: index
= 9; break;
3486 case VK_F11
: index
= 10; break;
3487 case VK_F12
: index
= 11; break;
3489 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3490 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3491 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3494 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3495 code
>= 1 && code
<= 6) {
3496 char codes
[] = "HL.FIG";
3500 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3504 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3511 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3514 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3517 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3518 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3521 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3523 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3525 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3528 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3529 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3533 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3538 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3539 * some reason seems to send VK_CLEAR to Windows...).
3562 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3564 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
3567 * RDB: VT100 & VT102 manuals both state the
3568 * app cursor keys only work if the app keypad
3571 * SGT: That may well be true, but xterm
3572 * disagrees and so does at least one
3573 * application, so I've #if'ed this out and the
3574 * behaviour is back to PuTTY's original: app
3575 * cursor and app keypad are independently
3576 * switchable modes. If anyone complains about
3577 * _this_ I'll have to put in a configurable
3580 if (!app_keypad_keys
)
3583 /* Useful mapping of Ctrl-arrows */
3584 if (shift_state
== 2)
3588 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3590 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3597 * Finally, deal with Return ourselves. (Win95 seems to
3598 * foul it up when Alt is pressed, for some reason.)
3600 if (wParam
== VK_RETURN
) { /* Return */
3606 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3607 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3612 /* Okay we've done everything interesting; let windows deal with
3613 * the boring stuff */
3617 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3618 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3620 keystate
[VK_CAPITAL
] = 0;
3623 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3624 #ifdef SHOW_TOASCII_RESULT
3625 if (r
== 1 && !key_down
) {
3627 if (in_utf
|| dbcs_screenfont
)
3628 debug((", (U+%04x)", alt_sum
));
3630 debug((", LCH(%d)", alt_sum
));
3632 debug((", ACH(%d)", keys
[0]));
3637 for (r1
= 0; r1
< r
; r1
++) {
3638 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3647 * Interrupt an ongoing paste. I'm not sure this is
3648 * sensible, but for the moment it's preferable to
3649 * having to faff about buffering things.
3654 for (i
= 0; i
< r
; i
++) {
3655 unsigned char ch
= (unsigned char) keys
[i
];
3657 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3662 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3666 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3667 MessageBeep(MB_ICONHAND
);
3671 luni_send(&keybuf
, 1, 1);
3679 if (in_utf
|| dbcs_screenfont
) {
3681 luni_send(&keybuf
, 1, 1);
3683 ch
= (char) alt_sum
;
3685 * We need not bother about stdin
3686 * backlogs here, because in GUI PuTTY
3687 * we can't do anything about it
3688 * anyway; there's no means of asking
3689 * Windows to hold off on KEYDOWN
3690 * messages. We _have_ to buffer
3691 * everything we're sent.
3693 ldisc_send(&ch
, 1, 1);
3697 lpage_send(kbd_codepage
, &ch
, 1, 1);
3699 if(capsOn
&& ch
< 0x80) {
3702 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3703 luni_send(cbuf
+!left_alt
, 1+!!left_alt
, 1);
3708 lpage_send(kbd_codepage
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3714 /* This is so the ALT-Numpad and dead keys work correctly. */
3719 /* If we're definitly not building up an ALT-54321 then clear it */
3722 /* If we will be using alt_sum fix the 256s */
3723 else if (keys
[0] && (in_utf
|| dbcs_screenfont
))
3728 * ALT alone may or may not want to bring up the System menu.
3729 * If it's not meant to, we return 0 on presses or releases of
3730 * ALT, to show that we've swallowed the keystroke. Otherwise
3731 * we return -1, which means Windows will give the keystroke
3732 * its default handling (i.e. bring up the System menu).
3734 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3740 void set_title(char *title
)
3743 window_name
= smalloc(1 + strlen(title
));
3744 strcpy(window_name
, title
);
3745 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3746 SetWindowText(hwnd
, title
);
3749 void set_icon(char *title
)
3752 icon_name
= smalloc(1 + strlen(title
));
3753 strcpy(icon_name
, title
);
3754 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3755 SetWindowText(hwnd
, title
);
3758 void set_sbar(int total
, int start
, int page
)
3762 if (is_full_screen() ?
!cfg
.scrollbar_in_fullscreen
: !cfg
.scrollbar
)
3765 si
.cbSize
= sizeof(si
);
3766 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3768 si
.nMax
= total
- 1;
3772 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3775 Context
get_ctx(void)
3781 SelectPalette(hdc
, pal
, FALSE
);
3787 void free_ctx(Context ctx
)
3789 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3790 ReleaseDC(hwnd
, ctx
);
3793 static void real_palette_set(int n
, int r
, int g
, int b
)
3796 logpal
->palPalEntry
[n
].peRed
= r
;
3797 logpal
->palPalEntry
[n
].peGreen
= g
;
3798 logpal
->palPalEntry
[n
].peBlue
= b
;
3799 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3800 colours
[n
] = PALETTERGB(r
, g
, b
);
3801 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3803 colours
[n
] = RGB(r
, g
, b
);
3806 void palette_set(int n
, int r
, int g
, int b
)
3808 static const int first
[21] = {
3809 0, 2, 4, 6, 8, 10, 12, 14,
3810 1, 3, 5, 7, 9, 11, 13, 15,
3813 real_palette_set(first
[n
], r
, g
, b
);
3815 real_palette_set(first
[n
] + 1, r
, g
, b
);
3817 HDC hdc
= get_ctx();
3818 UnrealizeObject(pal
);
3819 RealizePalette(hdc
);
3824 void palette_reset(void)
3828 for (i
= 0; i
< NCOLOURS
; i
++) {
3830 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3831 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3832 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3833 logpal
->palPalEntry
[i
].peFlags
= 0;
3834 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3835 defpal
[i
].rgbtGreen
,
3836 defpal
[i
].rgbtBlue
);
3838 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3839 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3844 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3846 RealizePalette(hdc
);
3851 void write_aclip(char *data
, int len
, int must_deselect
)
3856 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3859 lock
= GlobalLock(clipdata
);
3862 memcpy(lock
, data
, len
);
3863 ((unsigned char *) lock
)[len
] = 0;
3864 GlobalUnlock(clipdata
);
3867 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3869 if (OpenClipboard(hwnd
)) {
3871 SetClipboardData(CF_TEXT
, clipdata
);
3874 GlobalFree(clipdata
);
3877 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3881 * Note: unlike write_aclip() this will not append a nul.
3883 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3885 HGLOBAL clipdata
, clipdata2
, clipdata3
;
3887 void *lock
, *lock2
, *lock3
;
3889 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3891 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3892 len
* sizeof(wchar_t));
3893 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3895 if (!clipdata
|| !clipdata2
) {
3897 GlobalFree(clipdata
);
3899 GlobalFree(clipdata2
);
3902 if (!(lock
= GlobalLock(clipdata
)))
3904 if (!(lock2
= GlobalLock(clipdata2
)))
3907 memcpy(lock
, data
, len
* sizeof(wchar_t));
3908 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3910 if (cfg
.rtf_paste
) {
3911 wchar_t unitab
[256];
3913 unsigned char *tdata
= (unsigned char *)lock2
;
3914 wchar_t *udata
= (wchar_t *)lock
;
3915 int rtflen
= 0, uindex
= 0, tindex
= 0;
3917 int multilen
, blen
, alen
, totallen
, i
;
3918 char before
[16], after
[4];
3920 get_unitab(CP_ACP
, unitab
, 0);
3922 rtfsize
= 100 + strlen(cfg
.font
);
3923 rtf
= smalloc(rtfsize
);
3924 sprintf(rtf
, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3925 GetACP(), cfg
.font
);
3926 rtflen
= strlen(rtf
);
3929 * We want to construct a piece of RTF that specifies the
3930 * same Unicode text. To do this we will read back in
3931 * parallel from the Unicode data in `udata' and the
3932 * non-Unicode data in `tdata'. For each character in
3933 * `tdata' which becomes the right thing in `udata' when
3934 * looked up in `unitab', we just copy straight over from
3935 * tdata. For each one that doesn't, we must WCToMB it
3936 * individually and produce a \u escape sequence.
3938 * It would probably be more robust to just bite the bullet
3939 * and WCToMB each individual Unicode character one by one,
3940 * then MBToWC each one back to see if it was an accurate
3941 * translation; but that strikes me as a horrifying number
3942 * of Windows API calls so I want to see if this faster way
3943 * will work. If it screws up badly we can always revert to
3944 * the simple and slow way.
3946 while (tindex
< len2
&& uindex
< len
&&
3947 tdata
[tindex
] && udata
[uindex
]) {
3948 if (tindex
+ 1 < len2
&&
3949 tdata
[tindex
] == '\r' &&
3950 tdata
[tindex
+1] == '\n') {
3954 if (unitab
[tdata
[tindex
]] == udata
[uindex
]) {
3960 multilen
= WideCharToMultiByte(CP_ACP
, 0, unitab
+uindex
, 1,
3961 NULL
, 0, NULL
, NULL
);
3962 if (multilen
!= 1) {
3963 blen
= sprintf(before
, "{\\uc%d\\u%d", multilen
,
3965 alen
= 1; strcpy(after
, "}");
3967 blen
= sprintf(before
, "\\u%d", udata
[uindex
]);
3968 alen
= 0; after
[0] = '\0';
3971 assert(tindex
+ multilen
<= len2
);
3972 totallen
= blen
+ alen
;
3973 for (i
= 0; i
< multilen
; i
++) {
3974 if (tdata
[tindex
+i
] == '\\' ||
3975 tdata
[tindex
+i
] == '{' ||
3976 tdata
[tindex
+i
] == '}')
3978 else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A)
3979 totallen
+= 6; /* \par\r\n */
3980 else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20)
3986 if (rtfsize
< rtflen
+ totallen
+ 3) {
3987 rtfsize
= rtflen
+ totallen
+ 512;
3988 rtf
= srealloc(rtf
, rtfsize
);
3991 strcpy(rtf
+ rtflen
, before
); rtflen
+= blen
;
3992 for (i
= 0; i
< multilen
; i
++) {
3993 if (tdata
[tindex
+i
] == '\\' ||
3994 tdata
[tindex
+i
] == '{' ||
3995 tdata
[tindex
+i
] == '}') {
3996 rtf
[rtflen
++] = '\\';
3997 rtf
[rtflen
++] = tdata
[tindex
+i
];
3998 } else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A) {
3999 rtflen
+= sprintf(rtf
+rtflen
, "\\par\r\n");
4000 } else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20) {
4001 rtflen
+= sprintf(rtf
+rtflen
, "\\'%02x", tdata
[tindex
+i
]);
4003 rtf
[rtflen
++] = tdata
[tindex
+i
];
4006 strcpy(rtf
+ rtflen
, after
); rtflen
+= alen
;
4012 strcpy(rtf
+ rtflen
, "}");
4015 clipdata3
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, rtflen
);
4016 if (clipdata3
&& (lock3
= GlobalLock(clipdata3
)) != NULL
) {
4018 GlobalUnlock(clipdata3
);
4024 GlobalUnlock(clipdata
);
4025 GlobalUnlock(clipdata2
);
4028 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
4030 if (OpenClipboard(hwnd
)) {
4032 SetClipboardData(CF_UNICODETEXT
, clipdata
);
4033 SetClipboardData(CF_TEXT
, clipdata2
);
4035 SetClipboardData(RegisterClipboardFormat(CF_RTF
), clipdata3
);
4038 GlobalFree(clipdata
);
4039 GlobalFree(clipdata2
);
4043 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
4046 void get_clip(wchar_t ** p
, int *len
)
4048 static HGLOBAL clipdata
= NULL
;
4049 static wchar_t *converted
= 0;
4058 GlobalUnlock(clipdata
);
4061 } else if (OpenClipboard(NULL
)) {
4062 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
4064 *p
= GlobalLock(clipdata
);
4066 for (p2
= *p
; *p2
; p2
++);
4070 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
4074 s
= GlobalLock(clipdata
);
4075 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
4076 *p
= converted
= smalloc(i
* sizeof(wchar_t));
4077 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
4090 * Move `lines' lines from position `from' to position `to' in the
4093 void optimised_move(int to
, int from
, int lines
)
4098 min
= (to
< from ? to
: from
);
4099 max
= to
+ from
- min
;
4101 r
.left
= offset_width
;
4102 r
.right
= offset_width
+ cols
* font_width
;
4103 r
.top
= offset_height
+ min
* font_height
;
4104 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
4105 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
4110 * Print a message box and perform a fatal exit.
4112 void fatalbox(char *fmt
, ...)
4118 vsprintf(stuff
, fmt
, ap
);
4120 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
4125 * Manage window caption / taskbar flashing, if enabled.
4126 * 0 = stop, 1 = maintain, 2 = start
4128 static void flash_window(int mode
)
4130 static long last_flash
= 0;
4131 static int flashing
= 0;
4132 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
4135 FlashWindow(hwnd
, FALSE
);
4139 } else if (mode
== 2) {
4142 last_flash
= GetTickCount();
4144 FlashWindow(hwnd
, TRUE
);
4147 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
4150 long now
= GetTickCount();
4151 long fdiff
= now
- last_flash
;
4152 if (fdiff
< 0 || fdiff
> 450) {
4154 FlashWindow(hwnd
, TRUE
); /* toggle */
4165 if (mode
== BELL_DEFAULT
) {
4167 * For MessageBeep style bells, we want to be careful of
4168 * timing, because they don't have the nice property of
4169 * PlaySound bells that each one cancels the previous
4170 * active one. So we limit the rate to one per 50ms or so.
4172 static long lastbeep
= 0;
4175 beepdiff
= GetTickCount() - lastbeep
;
4176 if (beepdiff
>= 0 && beepdiff
< 50)
4180 * The above MessageBeep call takes time, so we record the
4181 * time _after_ it finishes rather than before it starts.
4183 lastbeep
= GetTickCount();
4184 } else if (mode
== BELL_WAVEFILE
) {
4185 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
4186 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
4187 sprintf(buf
, "Unable to play sound file\n%s\n"
4188 "Using default sound instead", cfg
.bell_wavefile
);
4189 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
4190 MB_OK
| MB_ICONEXCLAMATION
);
4191 cfg
.beep
= BELL_DEFAULT
;
4194 /* Otherwise, either visual bell or disabled; do nothing here */
4196 flash_window(2); /* start */
4201 * Minimise or restore the window in response to a server-side
4204 void set_iconic(int iconic
)
4206 if (IsIconic(hwnd
)) {
4208 ShowWindow(hwnd
, SW_RESTORE
);
4211 ShowWindow(hwnd
, SW_MINIMIZE
);
4216 * Move the window in response to a server-side request.
4218 void move_window(int x
, int y
)
4220 SetWindowPos(hwnd
, NULL
, x
, y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
);
4224 * Move the window to the top or bottom of the z-order in response
4225 * to a server-side request.
4227 void set_zorder(int top
)
4229 if (cfg
.alwaysontop
)
4230 return; /* ignore */
4231 SetWindowPos(hwnd
, top ? HWND_TOP
: HWND_BOTTOM
, 0, 0, 0, 0,
4232 SWP_NOMOVE
| SWP_NOSIZE
);
4236 * Refresh the window in response to a server-side request.
4238 void refresh_window(void)
4240 InvalidateRect(hwnd
, NULL
, TRUE
);
4244 * Maximise or restore the window in response to a server-side
4247 void set_zoomed(int zoomed
)
4249 if (IsZoomed(hwnd
)) {
4251 ShowWindow(hwnd
, SW_RESTORE
);
4254 ShowWindow(hwnd
, SW_MAXIMIZE
);
4259 * Report whether the window is iconic, for terminal reports.
4263 return IsIconic(hwnd
);
4267 * Report the window's position, for terminal reports.
4269 void get_window_pos(int *x
, int *y
)
4272 GetWindowRect(hwnd
, &r
);
4278 * Report the window's pixel size, for terminal reports.
4280 void get_window_pixels(int *x
, int *y
)
4283 GetWindowRect(hwnd
, &r
);
4284 *x
= r
.right
- r
.left
;
4285 *y
= r
.bottom
- r
.top
;
4289 * Return the window or icon title.
4291 char *get_window_title(int icon
)
4293 return icon ? icon_name
: window_name
;
4297 * See if we're in full-screen mode.
4299 int is_full_screen()
4301 if (!IsZoomed(hwnd
))
4303 if (GetWindowLong(hwnd
, GWL_STYLE
) & WS_CAPTION
)
4309 * Go full-screen. This should only be called when we are already
4312 void make_full_screen()
4317 assert(IsZoomed(hwnd
));
4319 /* Remove the window furniture. */
4320 style
= GetWindowLong(hwnd
, GWL_STYLE
);
4321 style
&= ~(WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
);
4322 if (cfg
.scrollbar_in_fullscreen
)
4323 style
|= WS_VSCROLL
;
4325 style
&= ~WS_VSCROLL
;
4326 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4328 /* Resize ourselves to exactly cover the nearest monitor. */
4329 #ifdef MONITOR_DEFAULTTONEAREST
4333 mon
= MonitorFromWindow(hwnd
, MONITOR_DEFAULTTONEAREST
);
4334 mi
.cbSize
= sizeof(mi
);
4335 GetMonitorInfo(mon
, &mi
);
4336 x
= mi
.rcMonitor
.left
;
4337 y
= mi
.rcMonitor
.top
;
4338 w
= mi
.rcMonitor
.right
;
4339 h
= mi
.rcMonitor
.bottom
;
4343 w
= GetSystemMetrics(SM_CXSCREEN
);
4344 h
= GetSystemMetrics(SM_CYSCREEN
);
4346 SetWindowPos(hwnd
, HWND_TOP
, x
, y
, w
, h
, SWP_FRAMECHANGED
);
4348 /* Tick the menu item in the System menu. */
4349 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4354 * Clear the full-screen attributes.
4356 void clear_full_screen()
4358 DWORD oldstyle
, style
;
4360 /* Reinstate the window furniture. */
4361 style
= oldstyle
= GetWindowLong(hwnd
, GWL_STYLE
);
4362 style
|= WS_CAPTION
| WS_BORDER
;
4363 if (cfg
.resize_action
== RESIZE_DISABLED
)
4364 style
&= ~WS_THICKFRAME
;
4366 style
|= WS_THICKFRAME
;
4368 style
|= WS_VSCROLL
;
4370 style
&= ~WS_VSCROLL
;
4371 if (style
!= oldstyle
) {
4372 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4373 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
4374 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
4378 /* Untick the menu item in the System menu. */
4379 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4384 * Toggle full-screen mode.
4386 void flip_full_screen()
4388 if (is_full_screen()) {
4389 ShowWindow(hwnd
, SW_RESTORE
);
4390 } else if (IsZoomed(hwnd
)) {
4393 SendMessage(hwnd
, WM_FULLSCR_ON_MAX
, 0, 0);
4394 ShowWindow(hwnd
, SW_MAXIMIZE
);