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
) &&
1941 GetCapture() == hwnd
) {
1943 if (wParam
& MK_LBUTTON
)
1945 else if (wParam
& MK_MBUTTON
)
1949 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1950 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1951 wParam
& MK_CONTROL
, is_alt_pressed());
1954 case WM_NCMOUSEMOVE
:
1956 noise_ultralight(lParam
);
1958 case WM_IGNORE_CLIP
:
1959 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1961 case WM_DESTROYCLIPBOARD
:
1964 ignore_clip
= FALSE
;
1970 hdc
= BeginPaint(hwnd
, &p
);
1972 SelectPalette(hdc
, pal
, TRUE
);
1973 RealizePalette(hdc
);
1976 (p
.rcPaint
.left
-offset_width
)/font_width
,
1977 (p
.rcPaint
.top
-offset_height
)/font_height
,
1978 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
1979 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
1982 p
.rcPaint
.left
< offset_width
||
1983 p
.rcPaint
.top
< offset_height
||
1984 p
.rcPaint
.right
>= offset_width
+ font_width
*cols
||
1985 p
.rcPaint
.bottom
>= offset_height
+ font_height
*rows
)
1987 HBRUSH fillcolour
, oldbrush
;
1989 fillcolour
= CreateSolidBrush (
1990 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1991 oldbrush
= SelectObject(hdc
, fillcolour
);
1992 edge
= CreatePen(PS_SOLID
, 0,
1993 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1994 oldpen
= SelectObject(hdc
, edge
);
1996 ExcludeClipRect(hdc
,
1997 offset_width
, offset_height
,
1998 offset_width
+font_width
*cols
,
1999 offset_height
+font_height
*rows
);
2001 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
2002 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
2004 // SelectClipRgn(hdc, NULL);
2006 SelectObject(hdc
, oldbrush
);
2007 DeleteObject(fillcolour
);
2008 SelectObject(hdc
, oldpen
);
2011 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
2012 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
2018 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2019 * but the only one that's likely to try to overload us is FD_READ.
2020 * This means buffering just one is fine.
2022 if (pending_netevent
)
2023 enact_pending_netevent();
2025 pending_netevent
= TRUE
;
2026 pend_netevent_wParam
= wParam
;
2027 pend_netevent_lParam
= lParam
;
2028 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
2029 enact_pending_netevent();
2031 time(&last_movement
);
2035 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
2037 flash_window(0); /* stop */
2046 caret_x
= caret_y
= -1; /* ensure caret is replaced next time */
2050 case WM_ENTERSIZEMOVE
:
2051 #ifdef RDB_DEBUG_PATCH
2052 debug((27, "WM_ENTERSIZEMOVE"));
2056 need_backend_resize
= FALSE
;
2058 case WM_EXITSIZEMOVE
:
2061 #ifdef RDB_DEBUG_PATCH
2062 debug((27, "WM_EXITSIZEMOVE"));
2064 if (need_backend_resize
) {
2065 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
2066 InvalidateRect(hwnd
, NULL
, TRUE
);
2071 * This does two jobs:
2072 * 1) Keep the sizetip uptodate
2073 * 2) Make sure the window size is _stepped_ in units of the font size.
2075 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2076 int width
, height
, w
, h
, ew
, eh
;
2077 LPRECT r
= (LPRECT
) lParam
;
2079 if ( !need_backend_resize
&& cfg
.resize_action
== RESIZE_EITHER
&&
2080 (cfg
.height
!= rows
|| cfg
.width
!= cols
)) {
2082 * Great! It seems that both the terminal size and the
2083 * font size have been changed and the user is now dragging.
2085 * It will now be difficult to get back to the configured
2088 * This would be easier but it seems to be too confusing.
2090 term_size(cfg.height, cfg.width, cfg.savelines);
2093 cfg
.height
=rows
; cfg
.width
=cols
;
2095 InvalidateRect(hwnd
, NULL
, TRUE
);
2096 need_backend_resize
= TRUE
;
2099 width
= r
->right
- r
->left
- extra_width
;
2100 height
= r
->bottom
- r
->top
- extra_height
;
2101 w
= (width
+ font_width
/ 2) / font_width
;
2104 h
= (height
+ font_height
/ 2) / font_height
;
2107 UpdateSizeTip(hwnd
, w
, h
);
2108 ew
= width
- w
* font_width
;
2109 eh
= height
- h
* font_height
;
2111 if (wParam
== WMSZ_LEFT
||
2112 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2118 if (wParam
== WMSZ_TOP
||
2119 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2129 int width
, height
, w
, h
, rv
= 0;
2130 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
2131 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2132 LPRECT r
= (LPRECT
) lParam
;
2134 width
= r
->right
- r
->left
- ex_width
;
2135 height
= r
->bottom
- r
->top
- ex_height
;
2137 w
= (width
+ cols
/2)/cols
;
2138 h
= (height
+ rows
/2)/rows
;
2139 if ( r
->right
!= r
->left
+ w
*cols
+ ex_width
)
2142 if (wParam
== WMSZ_LEFT
||
2143 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2144 r
->left
= r
->right
- w
*cols
- ex_width
;
2146 r
->right
= r
->left
+ w
*cols
+ ex_width
;
2148 if (r
->bottom
!= r
->top
+ h
*rows
+ ex_height
)
2151 if (wParam
== WMSZ_TOP
||
2152 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2153 r
->top
= r
->bottom
- h
*rows
- ex_height
;
2155 r
->bottom
= r
->top
+ h
*rows
+ ex_height
;
2159 /* break; (never reached) */
2160 case WM_FULLSCR_ON_MAX
:
2161 fullscr_on_max
= TRUE
;
2164 sys_cursor_update();
2167 #ifdef RDB_DEBUG_PATCH
2168 debug((27, "WM_SIZE %s (%d,%d)",
2169 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2170 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2171 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2172 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2174 LOWORD(lParam
), HIWORD(lParam
)));
2176 if (wParam
== SIZE_MINIMIZED
)
2178 cfg
.win_name_always ? window_name
: icon_name
);
2179 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2180 SetWindowText(hwnd
, window_name
);
2181 if (wParam
== SIZE_RESTORED
)
2182 clear_full_screen();
2183 if (wParam
== SIZE_MAXIMIZED
&& fullscr_on_max
) {
2185 fullscr_on_max
= FALSE
;
2188 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2189 /* A resize, well it better be a minimize. */
2193 int width
, height
, w
, h
;
2195 width
= LOWORD(lParam
);
2196 height
= HIWORD(lParam
);
2199 if (wParam
== SIZE_MAXIMIZED
&& !was_zoomed
) {
2203 if (cfg
.resize_action
== RESIZE_TERM
) {
2204 w
= width
/ font_width
;
2206 h
= height
/ font_height
;
2209 term_size(h
, w
, cfg
.savelines
);
2212 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2214 if (cfg
.resize_action
== RESIZE_TERM
)
2215 term_size(prev_rows
, prev_cols
, cfg
.savelines
);
2216 if (cfg
.resize_action
!= RESIZE_FONT
)
2221 /* This is an unexpected resize, these will normally happen
2222 * if the window is too large. Probably either the user
2223 * selected a huge font or the screen size has changed.
2225 * This is also called with minimize.
2227 else reset_window(-1);
2231 * Don't call back->size in mid-resize. (To prevent
2232 * massive numbers of resize events getting sent
2233 * down the connection during an NT opaque drag.)
2236 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2237 need_backend_resize
= TRUE
;
2238 w
= (width
-cfg
.window_border
*2) / font_width
;
2240 h
= (height
-cfg
.window_border
*2) / font_height
;
2249 sys_cursor_update();
2252 switch (LOWORD(wParam
)) {
2266 term_scroll(0, +rows
/ 2);
2269 term_scroll(0, -rows
/ 2);
2271 case SB_THUMBPOSITION
:
2273 term_scroll(1, HIWORD(wParam
));
2277 case WM_PALETTECHANGED
:
2278 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2279 HDC hdc
= get_ctx();
2281 if (RealizePalette(hdc
) > 0)
2287 case WM_QUERYNEWPALETTE
:
2289 HDC hdc
= get_ctx();
2291 if (RealizePalette(hdc
) > 0)
2303 * Add the scan code and keypress timing to the random
2306 noise_ultralight(lParam
);
2309 * We don't do TranslateMessage since it disassociates the
2310 * resulting CHAR message from the KEYDOWN that sparked it,
2311 * which we occasionally don't want. Instead, we process
2312 * KEYDOWN, and call the Win32 translator functions so that
2313 * we get the translations under _our_ control.
2316 unsigned char buf
[20];
2319 if (wParam
== VK_PROCESSKEY
) {
2322 m
.message
= WM_KEYDOWN
;
2324 m
.lParam
= lParam
& 0xdfff;
2325 TranslateMessage(&m
);
2327 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2329 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2333 * Interrupt an ongoing paste. I'm not sure
2334 * this is sensible, but for the moment it's
2335 * preferable to having to faff about buffering
2341 * We need not bother about stdin backlogs
2342 * here, because in GUI PuTTY we can't do
2343 * anything about it anyway; there's no means
2344 * of asking Windows to hold off on KEYDOWN
2345 * messages. We _have_ to buffer everything
2348 ldisc_send(buf
, len
, 1);
2353 net_pending_errors();
2355 case WM_INPUTLANGCHANGE
:
2356 /* wParam == Font number */
2357 /* lParam == Locale */
2358 set_input_locale((HKL
)lParam
);
2359 sys_cursor_update();
2362 if(wParam
== IMN_SETOPENSTATUS
) {
2363 HIMC hImc
= ImmGetContext(hwnd
);
2364 ImmSetCompositionFont(hImc
, &lfont
);
2365 ImmReleaseContext(hwnd
, hImc
);
2369 case WM_IME_COMPOSITION
:
2375 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2376 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2378 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2379 break; /* fall back to DefWindowProc */
2381 hIMC
= ImmGetContext(hwnd
);
2382 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2386 buff
= (char*) smalloc(n
);
2387 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2389 * Jaeyoun Chung reports that Korean character
2390 * input doesn't work correctly if we do a single
2391 * luni_send() covering the whole of buff. So
2392 * instead we luni_send the characters one by one.
2394 for (i
= 0; i
< n
; i
+= 2)
2395 luni_send((unsigned short *)(buff
+i
), 1, 1);
2398 ImmReleaseContext(hwnd
, hIMC
);
2403 if (wParam
& 0xFF00) {
2404 unsigned char buf
[2];
2407 buf
[0] = wParam
>> 8;
2408 lpage_send(kbd_codepage
, buf
, 2, 1);
2410 char c
= (unsigned char) wParam
;
2411 lpage_send(kbd_codepage
, &c
, 1, 1);
2417 * Nevertheless, we are prepared to deal with WM_CHAR
2418 * messages, should they crop up. So if someone wants to
2419 * post the things to us as part of a macro manoeuvre,
2420 * we're ready to cope.
2423 char c
= (unsigned char)wParam
;
2424 lpage_send(CP_ACP
, &c
, 1, 1);
2428 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2429 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2433 if (message
== wm_mousewheel
) {
2434 int shift_pressed
=0, control_pressed
=0, alt_pressed
=0;
2436 if (message
== WM_MOUSEWHEEL
) {
2437 wheel_accumulator
+= (short)HIWORD(wParam
);
2438 shift_pressed
=LOWORD(wParam
) & MK_SHIFT
;
2439 control_pressed
=LOWORD(wParam
) & MK_CONTROL
;
2442 wheel_accumulator
+= (int)wParam
;
2443 if (GetKeyboardState(keys
)!=0) {
2444 shift_pressed
=keys
[VK_SHIFT
]&0x80;
2445 control_pressed
=keys
[VK_CONTROL
]&0x80;
2449 /* process events when the threshold is reached */
2450 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
2453 /* reduce amount for next time */
2454 if (wheel_accumulator
> 0) {
2456 wheel_accumulator
-= WHEEL_DELTA
;
2457 } else if (wheel_accumulator
< 0) {
2459 wheel_accumulator
+= WHEEL_DELTA
;
2463 if (send_raw_mouse
&&
2464 !(cfg
.mouse_override
&& shift_pressed
)) {
2465 /* send a mouse-down followed by a mouse up */
2468 TO_CHR_X(X_POS(lParam
)),
2469 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2470 control_pressed
, is_alt_pressed());
2471 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
2472 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2473 control_pressed
, is_alt_pressed());
2475 /* trigger a scroll */
2477 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
2484 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2488 * Move the system caret. (We maintain one, even though it's
2489 * invisible, for the benefit of blind people: apparently some
2490 * helper software tracks the system caret, so we should arrange to
2493 void sys_cursor(int x
, int y
)
2497 if (!has_focus
) return;
2500 * Avoid gratuitously re-updating the cursor position and IMM
2501 * window if there's no actual change required.
2503 cx
= x
* font_width
+ offset_width
;
2504 cy
= y
* font_height
+ offset_height
;
2505 if (cx
== caret_x
&& cy
== caret_y
)
2510 sys_cursor_update();
2513 static void sys_cursor_update(void)
2518 if (!has_focus
) return;
2520 if (caret_x
< 0 || caret_y
< 0)
2523 SetCaretPos(caret_x
, caret_y
);
2525 /* IMM calls on Win98 and beyond only */
2526 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2528 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2529 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2531 /* we should have the IMM functions */
2532 hIMC
= ImmGetContext(hwnd
);
2533 cf
.dwStyle
= CFS_POINT
;
2534 cf
.ptCurrentPos
.x
= caret_x
;
2535 cf
.ptCurrentPos
.y
= caret_y
;
2536 ImmSetCompositionWindow(hIMC
, &cf
);
2538 ImmReleaseContext(hwnd
, hIMC
);
2542 * Draw a line of text in the window, at given character
2543 * coordinates, in given attributes.
2545 * We are allowed to fiddle with the contents of `text'.
2547 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2548 unsigned long attr
, int lattr
)
2551 int nfg
, nbg
, nfont
;
2554 int force_manual_underline
= 0;
2555 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2556 int char_width
= fnt_width
;
2557 int text_adjust
= 0;
2558 static int *IpDx
= 0, IpDxLEN
= 0;
2560 if (attr
& ATTR_WIDE
)
2563 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2565 if (len
> IpDxLEN
) {
2567 IpDx
= smalloc((len
+ 16) * sizeof(int));
2568 IpDxLEN
= (len
+ 16);
2570 for (i
= 0; i
< IpDxLEN
; i
++)
2571 IpDx
[i
] = char_width
;
2574 /* Only want the left half of double width lines */
2575 if (lattr
!= LATTR_NORM
&& x
*2 >= cols
)
2583 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
2584 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2585 attr
^= ATTR_CUR_XOR
;
2589 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2590 /* Assume a poorman font is borken in other ways too. */
2600 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2603 if (attr
& ATTR_NARROW
)
2604 nfont
|= FONT_NARROW
;
2606 /* Special hack for the VT100 linedraw glyphs. */
2607 if ((attr
& CSET_MASK
) == 0x2300) {
2608 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2609 switch ((unsigned char) (text
[0])) {
2611 text_adjust
= -2 * font_height
/ 5;
2614 text_adjust
= -1 * font_height
/ 5;
2617 text_adjust
= font_height
/ 5;
2620 text_adjust
= 2 * font_height
/ 5;
2623 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2626 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2627 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2628 if (attr
& ATTR_UNDER
) {
2629 attr
&= ~ATTR_UNDER
;
2630 force_manual_underline
= 1;
2635 /* Anything left as an original character set is unprintable. */
2636 if (DIRECT_CHAR(attr
)) {
2639 memset(text
, 0xFD, len
);
2643 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2646 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2647 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2648 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2650 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2651 nfont
|= FONT_UNDERLINE
;
2652 another_font(nfont
);
2653 if (!fonts
[nfont
]) {
2654 if (nfont
& FONT_UNDERLINE
)
2655 force_manual_underline
= 1;
2656 /* Don't do the same for manual bold, it could be bad news. */
2658 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2660 another_font(nfont
);
2662 nfont
= FONT_NORMAL
;
2663 if (attr
& ATTR_REVERSE
) {
2668 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2670 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2674 SelectObject(hdc
, fonts
[nfont
]);
2675 SetTextColor(hdc
, fg
);
2676 SetBkColor(hdc
, bg
);
2677 SetBkMode(hdc
, OPAQUE
);
2680 line_box
.right
= x
+ char_width
* len
;
2681 line_box
.bottom
= y
+ font_height
;
2683 /* Only want the left half of double width lines */
2684 if (line_box
.right
> font_width
*cols
+offset_width
)
2685 line_box
.right
= font_width
*cols
+offset_width
;
2687 /* We're using a private area for direct to font. (512 chars.) */
2688 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2689 /* Ho Hum, dbcs fonts are a PITA! */
2690 /* To display on W9x I have to convert to UCS */
2691 static wchar_t *uni_buf
= 0;
2692 static int uni_len
= 0;
2694 if (len
> uni_len
) {
2696 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2699 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2700 uni_buf
[nlen
] = 0xFFFD;
2701 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2702 IpDx
[nlen
] += char_width
;
2703 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2704 text
+mptr
, 2, uni_buf
+nlen
, 1);
2709 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2710 text
+mptr
, 1, uni_buf
+nlen
, 1);
2718 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2719 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2720 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2721 SetBkMode(hdc
, TRANSPARENT
);
2722 ExtTextOutW(hdc
, x
- 1,
2723 y
- font_height
* (lattr
==
2724 LATTR_BOT
) + text_adjust
,
2725 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2729 } else if (DIRECT_FONT(attr
)) {
2731 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2732 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2733 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2734 SetBkMode(hdc
, TRANSPARENT
);
2736 /* GRR: This draws the character outside it's box and can leave
2737 * 'droppings' even with the clip box! I suppose I could loop it
2738 * one character at a time ... yuk.
2740 * Or ... I could do a test print with "W", and use +1 or -1 for this
2741 * shift depending on if the leftmost column is blank...
2743 ExtTextOut(hdc
, x
- 1,
2744 y
- font_height
* (lattr
==
2745 LATTR_BOT
) + text_adjust
,
2746 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2749 /* And 'normal' unicode characters */
2750 static WCHAR
*wbuf
= NULL
;
2751 static int wlen
= 0;
2756 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2758 for (i
= 0; i
< len
; i
++)
2759 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2762 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2763 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2765 /* And the shadow bold hack. */
2766 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2767 SetBkMode(hdc
, TRANSPARENT
);
2768 ExtTextOutW(hdc
, x
- 1,
2769 y
- font_height
* (lattr
==
2770 LATTR_BOT
) + text_adjust
,
2771 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2774 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2775 (und_mode
== UND_LINE
2776 && (attr
& ATTR_UNDER
)))) {
2779 if (lattr
== LATTR_BOT
)
2780 dec
= dec
* 2 - font_height
;
2782 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2783 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2784 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2785 oldpen
= SelectObject(hdc
, oldpen
);
2786 DeleteObject(oldpen
);
2790 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2791 unsigned long attr
, int lattr
)
2797 int ctype
= cfg
.cursor_type
;
2799 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2800 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2801 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2805 attr
|= TATTR_RIGHTCURS
;
2808 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2809 if (attr
& ATTR_WIDE
)
2816 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2819 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2820 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2821 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2822 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2823 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2824 Polyline(hdc
, pts
, 5);
2825 oldpen
= SelectObject(hdc
, oldpen
);
2826 DeleteObject(oldpen
);
2827 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2828 int startx
, starty
, dx
, dy
, length
, i
;
2831 starty
= y
+ descent
;
2834 length
= char_width
;
2837 if (attr
& TATTR_RIGHTCURS
)
2838 xadjust
= char_width
- 1;
2839 startx
= x
+ xadjust
;
2843 length
= font_height
;
2845 if (attr
& TATTR_ACTCURS
) {
2848 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2849 MoveToEx(hdc
, startx
, starty
, NULL
);
2850 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2851 oldpen
= SelectObject(hdc
, oldpen
);
2852 DeleteObject(oldpen
);
2854 for (i
= 0; i
< length
; i
++) {
2856 SetPixel(hdc
, startx
, starty
, colours
[23]);
2865 /* This function gets the actual width of a character in the normal font.
2867 int CharWidth(Context ctx
, int uc
) {
2871 /* If the font max is the same as the font ave width then this
2872 * function is a no-op.
2874 if (!font_dualwidth
) return 1;
2876 switch (uc
& CSET_MASK
) {
2878 uc
= unitab_line
[uc
& 0xFF];
2881 uc
= unitab_xterm
[uc
& 0xFF];
2884 uc
= unitab_scoacs
[uc
& 0xFF];
2887 if (DIRECT_FONT(uc
)) {
2888 if (dbcs_screenfont
) return 1;
2890 /* Speedup, I know of no font where ascii is the wrong width */
2891 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
2894 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
2895 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2896 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
2897 another_font(FONT_OEM
);
2898 if (!fonts
[FONT_OEM
]) return 0;
2900 SelectObject(hdc
, fonts
[FONT_OEM
]);
2904 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
2905 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
2908 /* Speedup, I know of no font where ascii is the wrong width */
2909 if (uc
>= ' ' && uc
<= '~') return 1;
2911 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2912 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
2913 /* Okay that one worked */ ;
2914 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
2915 /* This should work on 9x too, but it's "less accurate" */ ;
2920 ibuf
+= font_width
/ 2 -1;
2927 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2928 * codes. Returns number of bytes used or zero to drop the message
2929 * or -1 to forward the message to windows.
2931 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2932 unsigned char *output
)
2935 int scan
, left_alt
= 0, key_down
, shift_state
;
2937 unsigned char *p
= output
;
2938 static int alt_sum
= 0;
2940 HKL kbd_layout
= GetKeyboardLayout(0);
2942 static WORD keys
[3];
2943 static int compose_char
= 0;
2944 static WPARAM compose_key
= 0;
2946 r
= GetKeyboardState(keystate
);
2948 memset(keystate
, 0, sizeof(keystate
));
2951 #define SHOW_TOASCII_RESULT
2952 { /* Tell us all about key events */
2953 static BYTE oldstate
[256];
2954 static int first
= 1;
2958 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2961 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2963 } else if ((HIWORD(lParam
) & KF_UP
)
2964 && scan
== (HIWORD(lParam
) & 0xFF)) {
2968 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2969 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2982 debug(("VK_%02x", wParam
));
2984 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2986 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2988 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2989 if (ch
>= ' ' && ch
<= '~')
2990 debug((", '%c'", ch
));
2992 debug((", $%02x", ch
));
2995 debug((", KB0=%02x", keys
[0]));
2997 debug((", KB1=%02x", keys
[1]));
2999 debug((", KB2=%02x", keys
[2]));
3001 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
3003 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
3005 if ((HIWORD(lParam
) & KF_EXTENDED
))
3007 if ((HIWORD(lParam
) & KF_UP
))
3011 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
3012 else if ((HIWORD(lParam
) & KF_UP
))
3013 oldstate
[wParam
& 0xFF] ^= 0x80;
3015 oldstate
[wParam
& 0xFF] ^= 0x81;
3017 for (ch
= 0; ch
< 256; ch
++)
3018 if (oldstate
[ch
] != keystate
[ch
])
3019 debug((", M%02x=%02x", ch
, keystate
[ch
]));
3021 memcpy(oldstate
, keystate
, sizeof(oldstate
));
3025 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
3026 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
3030 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
3031 if ((cfg
.funky_type
== 3 ||
3032 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
3033 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
3035 wParam
= VK_EXECUTE
;
3037 /* UnToggle NUMLock */
3038 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
3039 keystate
[VK_NUMLOCK
] ^= 1;
3042 /* And write back the 'adjusted' state */
3043 SetKeyboardState(keystate
);
3046 /* Disable Auto repeat if required */
3047 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
3050 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
3053 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
3055 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
3056 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
3057 if (cfg
.ctrlaltkeys
)
3058 keystate
[VK_MENU
] = 0;
3060 keystate
[VK_RMENU
] = 0x80;
3065 alt_pressed
= (left_alt
&& key_down
);
3067 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
3068 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
3069 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
3071 /* Note if AltGr was pressed and if it was used as a compose key */
3072 if (!compose_state
) {
3073 compose_key
= 0x100;
3074 if (cfg
.compose_key
) {
3075 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
3076 compose_key
= wParam
;
3078 if (wParam
== VK_APPS
)
3079 compose_key
= wParam
;
3082 if (wParam
== compose_key
) {
3083 if (compose_state
== 0
3084 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
3086 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
3090 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
3094 * Record that we pressed key so the scroll window can be reset, but
3095 * be careful to avoid Shift-UP/Down
3097 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
&&
3098 wParam
!= VK_MENU
&& wParam
!= VK_CONTROL
) {
3102 if (compose_state
> 1 && left_alt
)
3105 /* Sanitize the number pad if not using a PC NumPad */
3106 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
3107 && cfg
.funky_type
!= 2)
3108 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
3109 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
3113 nParam
= VK_NUMPAD0
;
3116 nParam
= VK_NUMPAD1
;
3119 nParam
= VK_NUMPAD2
;
3122 nParam
= VK_NUMPAD3
;
3125 nParam
= VK_NUMPAD4
;
3128 nParam
= VK_NUMPAD5
;
3131 nParam
= VK_NUMPAD6
;
3134 nParam
= VK_NUMPAD7
;
3137 nParam
= VK_NUMPAD8
;
3140 nParam
= VK_NUMPAD9
;
3143 nParam
= VK_DECIMAL
;
3147 if (keystate
[VK_NUMLOCK
] & 1)
3154 /* If a key is pressed and AltGr is not active */
3155 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
3156 /* Okay, prepare for most alts then ... */
3160 /* Lets see if it's a pattern we know all about ... */
3161 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
3162 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
3165 if (wParam
== VK_NEXT
&& shift_state
== 1) {
3166 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
3169 if (wParam
== VK_INSERT
&& shift_state
== 1) {
3173 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
3176 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
3177 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
3180 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
&&
3181 (cfg
.resize_action
!= RESIZE_DISABLED
)) {
3182 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) != KF_REPEAT
)
3186 /* Control-Numlock for app-keypad mode switch */
3187 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
3188 app_keypad_keys
^= 1;
3192 /* Nethack keypad */
3193 if (cfg
.nethack_keypad
&& !left_alt
) {
3196 *p
++ = shift_state ?
'B' : 'b';
3199 *p
++ = shift_state ?
'J' : 'j';
3202 *p
++ = shift_state ?
'N' : 'n';
3205 *p
++ = shift_state ?
'H' : 'h';
3208 *p
++ = shift_state ?
'.' : '.';
3211 *p
++ = shift_state ?
'L' : 'l';
3214 *p
++ = shift_state ?
'Y' : 'y';
3217 *p
++ = shift_state ?
'K' : 'k';
3220 *p
++ = shift_state ?
'U' : 'u';
3225 /* Application Keypad */
3229 if (cfg
.funky_type
== 3 ||
3230 (cfg
.funky_type
<= 1 &&
3231 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3245 if (app_keypad_keys
&& !cfg
.no_applic_k
)
3282 if (cfg
.funky_type
== 2) {
3287 } else if (shift_state
)
3294 if (cfg
.funky_type
== 2)
3298 if (cfg
.funky_type
== 2)
3302 if (cfg
.funky_type
== 2)
3307 if (HIWORD(lParam
) & KF_EXTENDED
)
3313 if (xkey
>= 'P' && xkey
<= 'S')
3314 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3316 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3318 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3323 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3324 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3328 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3334 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3338 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3342 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3347 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3352 /* Control-2 to Control-8 are special */
3353 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3354 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3357 if (shift_state
== 2 && wParam
== 0xBD) {
3361 if (shift_state
== 2 && wParam
== 0xDF) {
3365 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
3372 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3373 * for integer decimal nn.)
3375 * We also deal with the weird ones here. Linux VCs replace F1
3376 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3377 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3383 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3386 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3389 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3392 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3395 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3398 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3401 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3404 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3407 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3410 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3443 if ((shift_state
&2) == 0) switch (wParam
) {
3463 /* Reorder edit keys to physical order */
3464 if (cfg
.funky_type
== 3 && code
<= 6)
3465 code
= "\0\2\1\4\5\3\6"[code
];
3467 if (vt52_mode
&& code
> 0 && code
<= 6) {
3468 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3472 if (cfg
.funky_type
== 5 && /* SCO function keys */
3473 code
>= 11 && code
<= 34) {
3474 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3477 case VK_F1
: index
= 0; break;
3478 case VK_F2
: index
= 1; break;
3479 case VK_F3
: index
= 2; break;
3480 case VK_F4
: index
= 3; break;
3481 case VK_F5
: index
= 4; break;
3482 case VK_F6
: index
= 5; break;
3483 case VK_F7
: index
= 6; break;
3484 case VK_F8
: index
= 7; break;
3485 case VK_F9
: index
= 8; break;
3486 case VK_F10
: index
= 9; break;
3487 case VK_F11
: index
= 10; break;
3488 case VK_F12
: index
= 11; break;
3490 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3491 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3492 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3495 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3496 code
>= 1 && code
<= 6) {
3497 char codes
[] = "HL.FIG";
3501 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3505 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3512 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3515 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3518 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3519 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3522 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3524 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3526 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3529 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3530 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3534 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3539 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3540 * some reason seems to send VK_CLEAR to Windows...).
3563 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3565 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
3568 * RDB: VT100 & VT102 manuals both state the
3569 * app cursor keys only work if the app keypad
3572 * SGT: That may well be true, but xterm
3573 * disagrees and so does at least one
3574 * application, so I've #if'ed this out and the
3575 * behaviour is back to PuTTY's original: app
3576 * cursor and app keypad are independently
3577 * switchable modes. If anyone complains about
3578 * _this_ I'll have to put in a configurable
3581 if (!app_keypad_keys
)
3584 /* Useful mapping of Ctrl-arrows */
3585 if (shift_state
== 2)
3589 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3591 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3598 * Finally, deal with Return ourselves. (Win95 seems to
3599 * foul it up when Alt is pressed, for some reason.)
3601 if (wParam
== VK_RETURN
) { /* Return */
3607 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3608 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3613 /* Okay we've done everything interesting; let windows deal with
3614 * the boring stuff */
3618 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3619 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3621 keystate
[VK_CAPITAL
] = 0;
3624 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3625 #ifdef SHOW_TOASCII_RESULT
3626 if (r
== 1 && !key_down
) {
3628 if (in_utf
|| dbcs_screenfont
)
3629 debug((", (U+%04x)", alt_sum
));
3631 debug((", LCH(%d)", alt_sum
));
3633 debug((", ACH(%d)", keys
[0]));
3638 for (r1
= 0; r1
< r
; r1
++) {
3639 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3648 * Interrupt an ongoing paste. I'm not sure this is
3649 * sensible, but for the moment it's preferable to
3650 * having to faff about buffering things.
3655 for (i
= 0; i
< r
; i
++) {
3656 unsigned char ch
= (unsigned char) keys
[i
];
3658 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3663 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3667 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3668 MessageBeep(MB_ICONHAND
);
3672 luni_send(&keybuf
, 1, 1);
3680 if (in_utf
|| dbcs_screenfont
) {
3682 luni_send(&keybuf
, 1, 1);
3684 ch
= (char) alt_sum
;
3686 * We need not bother about stdin
3687 * backlogs here, because in GUI PuTTY
3688 * we can't do anything about it
3689 * anyway; there's no means of asking
3690 * Windows to hold off on KEYDOWN
3691 * messages. We _have_ to buffer
3692 * everything we're sent.
3694 ldisc_send(&ch
, 1, 1);
3698 lpage_send(kbd_codepage
, &ch
, 1, 1);
3700 if(capsOn
&& ch
< 0x80) {
3703 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3704 luni_send(cbuf
+!left_alt
, 1+!!left_alt
, 1);
3709 lpage_send(kbd_codepage
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3715 /* This is so the ALT-Numpad and dead keys work correctly. */
3720 /* If we're definitly not building up an ALT-54321 then clear it */
3723 /* If we will be using alt_sum fix the 256s */
3724 else if (keys
[0] && (in_utf
|| dbcs_screenfont
))
3729 * ALT alone may or may not want to bring up the System menu.
3730 * If it's not meant to, we return 0 on presses or releases of
3731 * ALT, to show that we've swallowed the keystroke. Otherwise
3732 * we return -1, which means Windows will give the keystroke
3733 * its default handling (i.e. bring up the System menu).
3735 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3741 void set_title(char *title
)
3744 window_name
= smalloc(1 + strlen(title
));
3745 strcpy(window_name
, title
);
3746 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3747 SetWindowText(hwnd
, title
);
3750 void set_icon(char *title
)
3753 icon_name
= smalloc(1 + strlen(title
));
3754 strcpy(icon_name
, title
);
3755 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3756 SetWindowText(hwnd
, title
);
3759 void set_sbar(int total
, int start
, int page
)
3763 if (is_full_screen() ?
!cfg
.scrollbar_in_fullscreen
: !cfg
.scrollbar
)
3766 si
.cbSize
= sizeof(si
);
3767 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3769 si
.nMax
= total
- 1;
3773 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3776 Context
get_ctx(void)
3782 SelectPalette(hdc
, pal
, FALSE
);
3788 void free_ctx(Context ctx
)
3790 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3791 ReleaseDC(hwnd
, ctx
);
3794 static void real_palette_set(int n
, int r
, int g
, int b
)
3797 logpal
->palPalEntry
[n
].peRed
= r
;
3798 logpal
->palPalEntry
[n
].peGreen
= g
;
3799 logpal
->palPalEntry
[n
].peBlue
= b
;
3800 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3801 colours
[n
] = PALETTERGB(r
, g
, b
);
3802 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3804 colours
[n
] = RGB(r
, g
, b
);
3807 void palette_set(int n
, int r
, int g
, int b
)
3809 static const int first
[21] = {
3810 0, 2, 4, 6, 8, 10, 12, 14,
3811 1, 3, 5, 7, 9, 11, 13, 15,
3814 real_palette_set(first
[n
], r
, g
, b
);
3816 real_palette_set(first
[n
] + 1, r
, g
, b
);
3818 HDC hdc
= get_ctx();
3819 UnrealizeObject(pal
);
3820 RealizePalette(hdc
);
3825 void palette_reset(void)
3829 for (i
= 0; i
< NCOLOURS
; i
++) {
3831 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3832 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3833 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3834 logpal
->palPalEntry
[i
].peFlags
= 0;
3835 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3836 defpal
[i
].rgbtGreen
,
3837 defpal
[i
].rgbtBlue
);
3839 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3840 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3845 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3847 RealizePalette(hdc
);
3852 void write_aclip(char *data
, int len
, int must_deselect
)
3857 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3860 lock
= GlobalLock(clipdata
);
3863 memcpy(lock
, data
, len
);
3864 ((unsigned char *) lock
)[len
] = 0;
3865 GlobalUnlock(clipdata
);
3868 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3870 if (OpenClipboard(hwnd
)) {
3872 SetClipboardData(CF_TEXT
, clipdata
);
3875 GlobalFree(clipdata
);
3878 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3882 * Note: unlike write_aclip() this will not append a nul.
3884 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3886 HGLOBAL clipdata
, clipdata2
, clipdata3
;
3888 void *lock
, *lock2
, *lock3
;
3890 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3892 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3893 len
* sizeof(wchar_t));
3894 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3896 if (!clipdata
|| !clipdata2
) {
3898 GlobalFree(clipdata
);
3900 GlobalFree(clipdata2
);
3903 if (!(lock
= GlobalLock(clipdata
)))
3905 if (!(lock2
= GlobalLock(clipdata2
)))
3908 memcpy(lock
, data
, len
* sizeof(wchar_t));
3909 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3911 if (cfg
.rtf_paste
) {
3912 wchar_t unitab
[256];
3914 unsigned char *tdata
= (unsigned char *)lock2
;
3915 wchar_t *udata
= (wchar_t *)lock
;
3916 int rtflen
= 0, uindex
= 0, tindex
= 0;
3918 int multilen
, blen
, alen
, totallen
, i
;
3919 char before
[16], after
[4];
3921 get_unitab(CP_ACP
, unitab
, 0);
3923 rtfsize
= 100 + strlen(cfg
.font
);
3924 rtf
= smalloc(rtfsize
);
3925 sprintf(rtf
, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3926 GetACP(), cfg
.font
);
3927 rtflen
= strlen(rtf
);
3930 * We want to construct a piece of RTF that specifies the
3931 * same Unicode text. To do this we will read back in
3932 * parallel from the Unicode data in `udata' and the
3933 * non-Unicode data in `tdata'. For each character in
3934 * `tdata' which becomes the right thing in `udata' when
3935 * looked up in `unitab', we just copy straight over from
3936 * tdata. For each one that doesn't, we must WCToMB it
3937 * individually and produce a \u escape sequence.
3939 * It would probably be more robust to just bite the bullet
3940 * and WCToMB each individual Unicode character one by one,
3941 * then MBToWC each one back to see if it was an accurate
3942 * translation; but that strikes me as a horrifying number
3943 * of Windows API calls so I want to see if this faster way
3944 * will work. If it screws up badly we can always revert to
3945 * the simple and slow way.
3947 while (tindex
< len2
&& uindex
< len
&&
3948 tdata
[tindex
] && udata
[uindex
]) {
3949 if (tindex
+ 1 < len2
&&
3950 tdata
[tindex
] == '\r' &&
3951 tdata
[tindex
+1] == '\n') {
3955 if (unitab
[tdata
[tindex
]] == udata
[uindex
]) {
3961 multilen
= WideCharToMultiByte(CP_ACP
, 0, unitab
+uindex
, 1,
3962 NULL
, 0, NULL
, NULL
);
3963 if (multilen
!= 1) {
3964 blen
= sprintf(before
, "{\\uc%d\\u%d", multilen
,
3966 alen
= 1; strcpy(after
, "}");
3968 blen
= sprintf(before
, "\\u%d", udata
[uindex
]);
3969 alen
= 0; after
[0] = '\0';
3972 assert(tindex
+ multilen
<= len2
);
3973 totallen
= blen
+ alen
;
3974 for (i
= 0; i
< multilen
; i
++) {
3975 if (tdata
[tindex
+i
] == '\\' ||
3976 tdata
[tindex
+i
] == '{' ||
3977 tdata
[tindex
+i
] == '}')
3979 else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A)
3980 totallen
+= 6; /* \par\r\n */
3981 else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20)
3987 if (rtfsize
< rtflen
+ totallen
+ 3) {
3988 rtfsize
= rtflen
+ totallen
+ 512;
3989 rtf
= srealloc(rtf
, rtfsize
);
3992 strcpy(rtf
+ rtflen
, before
); rtflen
+= blen
;
3993 for (i
= 0; i
< multilen
; i
++) {
3994 if (tdata
[tindex
+i
] == '\\' ||
3995 tdata
[tindex
+i
] == '{' ||
3996 tdata
[tindex
+i
] == '}') {
3997 rtf
[rtflen
++] = '\\';
3998 rtf
[rtflen
++] = tdata
[tindex
+i
];
3999 } else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A) {
4000 rtflen
+= sprintf(rtf
+rtflen
, "\\par\r\n");
4001 } else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20) {
4002 rtflen
+= sprintf(rtf
+rtflen
, "\\'%02x", tdata
[tindex
+i
]);
4004 rtf
[rtflen
++] = tdata
[tindex
+i
];
4007 strcpy(rtf
+ rtflen
, after
); rtflen
+= alen
;
4013 strcpy(rtf
+ rtflen
, "}");
4016 clipdata3
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, rtflen
);
4017 if (clipdata3
&& (lock3
= GlobalLock(clipdata3
)) != NULL
) {
4019 GlobalUnlock(clipdata3
);
4025 GlobalUnlock(clipdata
);
4026 GlobalUnlock(clipdata2
);
4029 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
4031 if (OpenClipboard(hwnd
)) {
4033 SetClipboardData(CF_UNICODETEXT
, clipdata
);
4034 SetClipboardData(CF_TEXT
, clipdata2
);
4036 SetClipboardData(RegisterClipboardFormat(CF_RTF
), clipdata3
);
4039 GlobalFree(clipdata
);
4040 GlobalFree(clipdata2
);
4044 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
4047 void get_clip(wchar_t ** p
, int *len
)
4049 static HGLOBAL clipdata
= NULL
;
4050 static wchar_t *converted
= 0;
4059 GlobalUnlock(clipdata
);
4062 } else if (OpenClipboard(NULL
)) {
4063 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
4065 *p
= GlobalLock(clipdata
);
4067 for (p2
= *p
; *p2
; p2
++);
4071 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
4075 s
= GlobalLock(clipdata
);
4076 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
4077 *p
= converted
= smalloc(i
* sizeof(wchar_t));
4078 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
4091 * Move `lines' lines from position `from' to position `to' in the
4094 void optimised_move(int to
, int from
, int lines
)
4099 min
= (to
< from ? to
: from
);
4100 max
= to
+ from
- min
;
4102 r
.left
= offset_width
;
4103 r
.right
= offset_width
+ cols
* font_width
;
4104 r
.top
= offset_height
+ min
* font_height
;
4105 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
4106 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
4111 * Print a message box and perform a fatal exit.
4113 void fatalbox(char *fmt
, ...)
4119 vsprintf(stuff
, fmt
, ap
);
4121 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
4126 * Manage window caption / taskbar flashing, if enabled.
4127 * 0 = stop, 1 = maintain, 2 = start
4129 static void flash_window(int mode
)
4131 static long last_flash
= 0;
4132 static int flashing
= 0;
4133 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
4136 FlashWindow(hwnd
, FALSE
);
4140 } else if (mode
== 2) {
4143 last_flash
= GetTickCount();
4145 FlashWindow(hwnd
, TRUE
);
4148 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
4151 long now
= GetTickCount();
4152 long fdiff
= now
- last_flash
;
4153 if (fdiff
< 0 || fdiff
> 450) {
4155 FlashWindow(hwnd
, TRUE
); /* toggle */
4166 if (mode
== BELL_DEFAULT
) {
4168 * For MessageBeep style bells, we want to be careful of
4169 * timing, because they don't have the nice property of
4170 * PlaySound bells that each one cancels the previous
4171 * active one. So we limit the rate to one per 50ms or so.
4173 static long lastbeep
= 0;
4176 beepdiff
= GetTickCount() - lastbeep
;
4177 if (beepdiff
>= 0 && beepdiff
< 50)
4181 * The above MessageBeep call takes time, so we record the
4182 * time _after_ it finishes rather than before it starts.
4184 lastbeep
= GetTickCount();
4185 } else if (mode
== BELL_WAVEFILE
) {
4186 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
4187 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
4188 sprintf(buf
, "Unable to play sound file\n%s\n"
4189 "Using default sound instead", cfg
.bell_wavefile
);
4190 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
4191 MB_OK
| MB_ICONEXCLAMATION
);
4192 cfg
.beep
= BELL_DEFAULT
;
4195 /* Otherwise, either visual bell or disabled; do nothing here */
4197 flash_window(2); /* start */
4202 * Minimise or restore the window in response to a server-side
4205 void set_iconic(int iconic
)
4207 if (IsIconic(hwnd
)) {
4209 ShowWindow(hwnd
, SW_RESTORE
);
4212 ShowWindow(hwnd
, SW_MINIMIZE
);
4217 * Move the window in response to a server-side request.
4219 void move_window(int x
, int y
)
4221 SetWindowPos(hwnd
, NULL
, x
, y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
);
4225 * Move the window to the top or bottom of the z-order in response
4226 * to a server-side request.
4228 void set_zorder(int top
)
4230 if (cfg
.alwaysontop
)
4231 return; /* ignore */
4232 SetWindowPos(hwnd
, top ? HWND_TOP
: HWND_BOTTOM
, 0, 0, 0, 0,
4233 SWP_NOMOVE
| SWP_NOSIZE
);
4237 * Refresh the window in response to a server-side request.
4239 void refresh_window(void)
4241 InvalidateRect(hwnd
, NULL
, TRUE
);
4245 * Maximise or restore the window in response to a server-side
4248 void set_zoomed(int zoomed
)
4250 if (IsZoomed(hwnd
)) {
4252 ShowWindow(hwnd
, SW_RESTORE
);
4255 ShowWindow(hwnd
, SW_MAXIMIZE
);
4260 * Report whether the window is iconic, for terminal reports.
4264 return IsIconic(hwnd
);
4268 * Report the window's position, for terminal reports.
4270 void get_window_pos(int *x
, int *y
)
4273 GetWindowRect(hwnd
, &r
);
4279 * Report the window's pixel size, for terminal reports.
4281 void get_window_pixels(int *x
, int *y
)
4284 GetWindowRect(hwnd
, &r
);
4285 *x
= r
.right
- r
.left
;
4286 *y
= r
.bottom
- r
.top
;
4290 * Return the window or icon title.
4292 char *get_window_title(int icon
)
4294 return icon ? icon_name
: window_name
;
4298 * See if we're in full-screen mode.
4300 int is_full_screen()
4302 if (!IsZoomed(hwnd
))
4304 if (GetWindowLong(hwnd
, GWL_STYLE
) & WS_CAPTION
)
4310 * Go full-screen. This should only be called when we are already
4313 void make_full_screen()
4318 assert(IsZoomed(hwnd
));
4320 /* Remove the window furniture. */
4321 style
= GetWindowLong(hwnd
, GWL_STYLE
);
4322 style
&= ~(WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
);
4323 if (cfg
.scrollbar_in_fullscreen
)
4324 style
|= WS_VSCROLL
;
4326 style
&= ~WS_VSCROLL
;
4327 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4329 /* Resize ourselves to exactly cover the nearest monitor. */
4330 #ifdef MONITOR_DEFAULTTONEAREST
4334 mon
= MonitorFromWindow(hwnd
, MONITOR_DEFAULTTONEAREST
);
4335 mi
.cbSize
= sizeof(mi
);
4336 GetMonitorInfo(mon
, &mi
);
4337 x
= mi
.rcMonitor
.left
;
4338 y
= mi
.rcMonitor
.top
;
4339 w
= mi
.rcMonitor
.right
;
4340 h
= mi
.rcMonitor
.bottom
;
4344 w
= GetSystemMetrics(SM_CXSCREEN
);
4345 h
= GetSystemMetrics(SM_CYSCREEN
);
4347 SetWindowPos(hwnd
, HWND_TOP
, x
, y
, w
, h
, SWP_FRAMECHANGED
);
4349 /* Tick the menu item in the System menu. */
4350 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4355 * Clear the full-screen attributes.
4357 void clear_full_screen()
4359 DWORD oldstyle
, style
;
4361 /* Reinstate the window furniture. */
4362 style
= oldstyle
= GetWindowLong(hwnd
, GWL_STYLE
);
4363 style
|= WS_CAPTION
| WS_BORDER
;
4364 if (cfg
.resize_action
== RESIZE_DISABLED
)
4365 style
&= ~WS_THICKFRAME
;
4367 style
|= WS_THICKFRAME
;
4369 style
|= WS_VSCROLL
;
4371 style
&= ~WS_VSCROLL
;
4372 if (style
!= oldstyle
) {
4373 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4374 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
4375 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
4379 /* Untick the menu item in the System menu. */
4380 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4385 * Toggle full-screen mode.
4387 void flip_full_screen()
4389 if (is_full_screen()) {
4390 ShowWindow(hwnd
, SW_RESTORE
);
4391 } else if (IsZoomed(hwnd
)) {
4394 SendMessage(hwnd
, WM_FULLSCR_ON_MAX
, 0, 0);
4395 ShowWindow(hwnd
, SW_MAXIMIZE
);