16 #define COMPILE_MULTIMON_STUBS
27 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
33 #define IDM_SHOWLOG 0x0010
34 #define IDM_NEWSESS 0x0020
35 #define IDM_DUPSESS 0x0030
36 #define IDM_RECONF 0x0040
37 #define IDM_CLRSB 0x0050
38 #define IDM_RESET 0x0060
39 #define IDM_TEL_AYT 0x0070
40 #define IDM_TEL_BRK 0x0080
41 #define IDM_TEL_SYNCH 0x0090
42 #define IDM_TEL_EC 0x00a0
43 #define IDM_TEL_EL 0x00b0
44 #define IDM_TEL_GA 0x00c0
45 #define IDM_TEL_NOP 0x00d0
46 #define IDM_TEL_ABORT 0x00e0
47 #define IDM_TEL_AO 0x00f0
48 #define IDM_TEL_IP 0x0100
49 #define IDM_TEL_SUSP 0x0110
50 #define IDM_TEL_EOR 0x0120
51 #define IDM_TEL_EOF 0x0130
52 #define IDM_HELP 0x0140
53 #define IDM_ABOUT 0x0150
54 #define IDM_SAVEDSESS 0x0160
55 #define IDM_COPYALL 0x0170
56 #define IDM_FULLSCREEN 0x0180
58 #define IDM_SESSLGP 0x0250 /* log type printable */
59 #define IDM_SESSLGA 0x0260 /* log type all chars */
60 #define IDM_SESSLGE 0x0270 /* log end */
61 #define IDM_SAVED_MIN 0x1000
62 #define IDM_SAVED_MAX 0x2000
64 #define WM_IGNORE_CLIP (WM_XUSER + 2)
65 #define WM_FULLSCR_ON_MAX (WM_XUSER + 3)
67 /* Needed for Chinese support and apparently not always defined. */
69 #define VK_PROCESSKEY 0xE5
72 /* Mouse wheel support. */
74 #define WM_MOUSEWHEEL 0x020A /* not defined in earlier SDKs */
77 #define WHEEL_DELTA 120
80 static LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
81 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
82 unsigned char *output
);
83 static void cfgtopalette(void);
84 static void init_palette(void);
85 static void init_fonts(int, int);
86 static void another_font(int);
87 static void deinit_fonts(void);
88 static void set_input_locale(HKL
);
89 static int do_mouse_wheel_msg(UINT message
, WPARAM wParam
, LPARAM lParam
);
91 static int is_full_screen(void);
92 static void make_full_screen(void);
93 static void clear_full_screen(void);
94 static void flip_full_screen(void);
96 /* Window layout information */
97 static void reset_window(int);
98 static int extra_width
, extra_height
;
99 static int font_width
, font_height
, font_dualwidth
;
100 static int offset_width
, offset_height
;
101 static int was_zoomed
= 0;
102 static int prev_rows
, prev_cols
;
104 static int pending_netevent
= 0;
105 static WPARAM pend_netevent_wParam
= 0;
106 static LPARAM pend_netevent_lParam
= 0;
107 static void enact_pending_netevent(void);
108 static void flash_window(int mode
);
109 static void sys_cursor_update(void);
111 static time_t last_movement
= 0;
113 static int caret_x
= -1, caret_y
= -1;
115 #define FONT_NORMAL 0
117 #define FONT_UNDERLINE 2
118 #define FONT_BOLDUND 3
119 #define FONT_WIDE 0x04
120 #define FONT_HIGH 0x08
121 #define FONT_NARROW 0x10
123 #define FONT_OEM 0x20
124 #define FONT_OEMBOLD 0x21
125 #define FONT_OEMUND 0x22
126 #define FONT_OEMBOLDUND 0x23
128 #define FONT_MAXNO 0x2F
130 static HFONT fonts
[FONT_MAXNO
];
131 static LOGFONT lfont
;
132 static int fontflag
[FONT_MAXNO
];
134 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
142 static COLORREF colours
[NCOLOURS
];
144 static LPLOGPALETTE logpal
;
145 static RGBTRIPLE defpal
[NCOLOURS
];
149 static HBITMAP caretbm
;
151 static int dbltime
, lasttime
, lastact
;
152 static Mouse_Button lastbtn
;
154 /* this allows xterm-style mouse handling. */
155 static int send_raw_mouse
= 0;
156 static int wheel_accumulator
= 0;
158 static char *window_name
, *icon_name
;
160 static int compose_state
= 0;
162 static int wsa_started
= 0;
164 static OSVERSIONINFO osVersion
;
166 static UINT wm_mousewheel
= WM_MOUSEWHEEL
;
168 /* Dummy routine, only required in plink. */
169 void ldisc_update(int echo
, int edit
)
173 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
175 static char appname
[] = "PuTTY";
180 int guess_width
, guess_height
;
183 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
185 winsock_ver
= MAKEWORD(1, 1);
186 if (WSAStartup(winsock_ver
, &wsadata
)) {
187 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
188 MB_OK
| MB_ICONEXCLAMATION
);
191 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
192 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
193 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
198 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
201 InitCommonControls();
203 /* Ensure a Maximize setting in Explorer doesn't maximise the
208 ZeroMemory(&osVersion
, sizeof(osVersion
));
209 osVersion
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
210 if (!GetVersionEx ( (OSVERSIONINFO
*) &osVersion
)) {
211 MessageBox(NULL
, "Windows refuses to report a version",
212 "PuTTY Fatal Error", MB_OK
| MB_ICONEXCLAMATION
);
218 * If we're running a version of Windows that doesn't support
219 * WM_MOUSEWHEEL, find out what message number we should be
222 if (osVersion
.dwMajorVersion
< 4 ||
223 (osVersion
.dwMajorVersion
== 4 &&
224 osVersion
.dwPlatformId
!= VER_PLATFORM_WIN32_NT
))
225 wm_mousewheel
= RegisterWindowMessage("MSWHEEL_ROLLMSG");
228 * See if we can find our Help file.
231 char b
[2048], *p
, *q
, *r
;
233 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
235 p
= strrchr(b
, '\\');
236 if (p
&& p
>= r
) r
= p
+1;
238 if (q
&& q
>= r
) r
= q
+1;
239 strcpy(r
, "putty.hlp");
240 if ( (fp
= fopen(b
, "r")) != NULL
) {
241 help_path
= dupstr(b
);
245 strcpy(r
, "putty.cnt");
246 if ( (fp
= fopen(b
, "r")) != NULL
) {
247 help_has_contents
= TRUE
;
250 help_has_contents
= FALSE
;
254 * Process the command line.
259 default_protocol
= DEFAULT_PROTOCOL
;
260 default_port
= DEFAULT_PORT
;
261 cfg
.logtype
= LGTYP_NONE
;
263 do_defaults(NULL
, &cfg
);
266 while (*p
&& isspace(*p
))
270 * Process command line options first. Yes, this can be
271 * done better, and it will be as soon as I have the
275 char *q
= p
+ strcspn(p
, " \t");
278 tolower(p
[0]) == 's' &&
279 tolower(p
[1]) == 's' && tolower(p
[2]) == 'h') {
280 default_protocol
= cfg
.protocol
= PROT_SSH
;
281 default_port
= cfg
.port
= 22;
282 } else if (q
== p
+ 7 &&
283 tolower(p
[0]) == 'c' &&
284 tolower(p
[1]) == 'l' &&
285 tolower(p
[2]) == 'e' &&
286 tolower(p
[3]) == 'a' &&
287 tolower(p
[4]) == 'n' &&
288 tolower(p
[5]) == 'u' && tolower(p
[6]) == 'p') {
290 * `putty -cleanup'. Remove all registry entries
291 * associated with PuTTY, and also find and delete
292 * the random seed file.
295 "This procedure will remove ALL Registry\n"
296 "entries associated with PuTTY, and will\n"
297 "also remove the PuTTY random seed file.\n"
299 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
300 "SESSIONS. Are you really sure you want\n"
303 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
308 p
= q
+ strspn(q
, " \t");
312 * An initial @ means to activate a saved session.
316 while (i
> 1 && isspace(p
[i
- 1]))
319 do_defaults(p
+ 1, &cfg
);
320 if (!*cfg
.host
&& !do_config()) {
324 } else if (*p
== '&') {
326 * An initial & means we've been given a command line
327 * containing the hex value of a HANDLE for a file
328 * mapping object, which we must then extract as a
333 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
334 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
335 0, 0, sizeof(Config
))) != NULL
) {
338 CloseHandle(filemap
);
339 } else if (!do_config()) {
346 * If the hostname starts with "telnet:", set the
347 * protocol to Telnet and process the string as a
350 if (!strncmp(q
, "telnet:", 7)) {
354 if (q
[0] == '/' && q
[1] == '/')
356 cfg
.protocol
= PROT_TELNET
;
358 while (*p
&& *p
!= ':' && *p
!= '/')
367 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
368 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
370 while (*p
&& !isspace(*p
))
374 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
375 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
376 while (*p
&& isspace(*p
))
391 * Trim leading whitespace off the hostname if it's there.
394 int space
= strspn(cfg
.host
, " \t");
395 memmove(cfg
.host
, cfg
.host
+space
, 1+strlen(cfg
.host
)-space
);
398 /* See if host is of the form user@host */
399 if (cfg
.host
[0] != '\0') {
400 char *atsign
= strchr(cfg
.host
, '@');
401 /* Make sure we're not overflowing the user field */
403 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
404 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
405 cfg
.username
[atsign
- cfg
.host
] = '\0';
407 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
412 * Trim a colon suffix off the hostname if it's there.
414 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
418 * Select protocol. This is farmed out into a table in a
419 * separate file to enable an ssh-free variant.
424 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
425 if (backends
[i
].protocol
== cfg
.protocol
) {
426 back
= backends
[i
].backend
;
430 MessageBox(NULL
, "Unsupported protocol number found",
431 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
437 /* Check for invalid Port number (i.e. zero) */
439 MessageBox(NULL
, "Invalid Port Number",
440 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
447 wndclass
.lpfnWndProc
= WndProc
;
448 wndclass
.cbClsExtra
= 0;
449 wndclass
.cbWndExtra
= 0;
450 wndclass
.hInstance
= inst
;
451 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
452 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
453 wndclass
.hbrBackground
= NULL
;
454 wndclass
.lpszMenuName
= NULL
;
455 wndclass
.lpszClassName
= appname
;
457 RegisterClass(&wndclass
);
462 savelines
= cfg
.savelines
;
468 * Guess some defaults for the window size. This all gets
469 * updated later, so we don't really care too much. However, we
470 * do want the font width/height guesses to correspond to a
471 * large font rather than a small one...
478 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
479 guess_width
= extra_width
+ font_width
* cols
;
480 guess_height
= extra_height
+ font_height
* rows
;
483 HWND w
= GetDesktopWindow();
484 GetWindowRect(w
, &r
);
485 if (guess_width
> r
.right
- r
.left
)
486 guess_width
= r
.right
- r
.left
;
487 if (guess_height
> r
.bottom
- r
.top
)
488 guess_height
= r
.bottom
- r
.top
;
492 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
495 winmode
&= ~(WS_VSCROLL
);
496 if (cfg
.resize_action
== RESIZE_DISABLED
)
497 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
499 exwinmode
|= WS_EX_TOPMOST
;
501 exwinmode
|= WS_EX_CLIENTEDGE
;
502 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
503 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
504 guess_width
, guess_height
,
505 NULL
, NULL
, inst
, NULL
);
509 * Initialise the fonts, simultaneously correcting the guesses
510 * for font_{width,height}.
515 * Correct the guesses for extra_{width,height}.
519 GetWindowRect(hwnd
, &wr
);
520 GetClientRect(hwnd
, &cr
);
521 offset_width
= offset_height
= cfg
.window_border
;
522 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
523 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
527 * Resize the window, now we know what size we _really_ want it
530 guess_width
= extra_width
+ font_width
* cols
;
531 guess_height
= extra_height
+ font_height
* rows
;
532 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
533 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
536 * Set up a caret bitmap, with no content.
540 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
541 bits
= smalloc(size
);
542 memset(bits
, 0, size
);
543 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
546 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
549 * Initialise the scroll bar.
554 si
.cbSize
= sizeof(si
);
555 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
560 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
564 * Start up the telnet connection.
568 char msg
[1024], *title
;
571 error
= back
->init(cfg
.host
, cfg
.port
, &realhost
, cfg
.tcp_nodelay
);
573 sprintf(msg
, "Unable to open connection to\n"
574 "%.800s\n" "%s", cfg
.host
, error
);
575 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
578 window_name
= icon_name
= NULL
;
580 title
= cfg
.wintitle
;
582 sprintf(msg
, "%s - PuTTY", realhost
);
590 session_closed
= FALSE
;
593 * Prepare the mouse handler.
595 lastact
= MA_NOTHING
;
596 lastbtn
= MBT_NOTHING
;
597 dbltime
= GetDoubleClickTime();
600 * Set up the session-control options on the system menu.
603 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
607 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
608 if (cfg
.protocol
== PROT_TELNET
) {
610 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
611 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
612 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
613 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
614 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
615 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
616 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
617 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
618 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
619 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
620 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
621 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
622 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
623 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
624 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
625 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
626 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
628 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
630 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
631 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
632 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
633 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
636 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
637 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
639 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
640 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
641 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
642 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
643 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
644 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
645 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
646 AppendMenu(m
, (cfg
.resize_action
== RESIZE_DISABLED
) ?
647 MF_GRAYED
: MF_ENABLED
, IDM_FULLSCREEN
, "&Full Screen");
648 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
650 AppendMenu(m
, MF_ENABLED
, IDM_HELP
, "&Help");
651 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
655 * Set up the initial input locale.
657 set_input_locale(GetKeyboardLayout(0));
660 * Open the initial log file if there is one.
665 * Finally show the window!
667 ShowWindow(hwnd
, show
);
668 SetForegroundWindow(hwnd
);
671 * Set the palette up.
677 has_focus
= (GetForegroundWindow() == hwnd
);
680 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
681 int timer_id
= 0, long_timer
= 0;
683 while (msg
.message
!= WM_QUIT
) {
684 /* Sometimes DispatchMessage calls routines that use their own
685 * GetMessage loop, setup this timer so we get some control back.
687 * Also call term_update() from the timer so that if the host
688 * is sending data flat out we still do redraws.
690 if (timer_id
&& long_timer
) {
691 KillTimer(hwnd
, timer_id
);
692 long_timer
= timer_id
= 0;
695 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
696 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
697 DispatchMessage(&msg
);
699 /* Make sure we blink everything that needs it. */
702 /* Send the paste buffer if there's anything to send */
705 /* If there's nothing new in the queue then we can do everything
706 * we've delayed, reading the socket, writing, and repainting
709 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
712 if (pending_netevent
) {
713 enact_pending_netevent();
715 /* Force the cursor blink on */
718 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
722 /* Okay there is now nothing to do so we make sure the screen is
723 * completely up to date then tell windows to call us in a little
727 KillTimer(hwnd
, timer_id
);
731 if (GetCapture() != hwnd
)
736 flash_window(1); /* maintain */
738 /* The messages seem unreliable; especially if we're being tricky */
739 has_focus
= (GetForegroundWindow() == hwnd
);
742 /* Hmm, term_update didn't want to do an update too soon ... */
743 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
745 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
747 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
750 /* There's no point rescanning everything in the message queue
751 * so we do an apparently unnecessary wait here
754 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
759 cleanup_exit(msg
.wParam
);
765 void cleanup_exit(int code
)
778 if (cfg
.protocol
== PROT_SSH
) {
789 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
791 char *do_select(SOCKET skt
, int startup
)
796 events
= (FD_CONNECT
| FD_READ
| FD_WRITE
|
797 FD_OOB
| FD_CLOSE
| FD_ACCEPT
);
802 return "do_select(): internal error (hwnd==NULL)";
803 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
804 switch (WSAGetLastError()) {
806 return "Network is down";
808 return "WSAAsyncSelect(): unknown error";
815 * set or clear the "raw mouse message" mode
817 void set_raw_mouse_mode(int activate
)
819 send_raw_mouse
= activate
;
820 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
824 * Print a message box and close the connection.
826 void connection_fatal(char *fmt
, ...)
832 vsprintf(stuff
, fmt
, ap
);
834 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
835 if (cfg
.close_on_exit
== COE_ALWAYS
)
838 session_closed
= TRUE
;
839 SetWindowText(hwnd
, "PuTTY (inactive)");
844 * Actually do the job requested by a WM_NETEVENT
846 static void enact_pending_netevent(void)
848 static int reentering
= 0;
849 extern int select_result(WPARAM
, LPARAM
);
853 return; /* don't unpend the pending */
855 pending_netevent
= FALSE
;
858 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
861 if (ret
== 0 && !session_closed
) {
862 /* Abnormal exits will already have set session_closed and taken
863 * appropriate action. */
864 if (cfg
.close_on_exit
== COE_ALWAYS
||
865 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
867 session_closed
= TRUE
;
868 SetWindowText(hwnd
, "PuTTY (inactive)");
869 MessageBox(hwnd
, "Connection closed by remote host",
870 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
876 * Copy the colour palette from the configuration data into defpal.
877 * This is non-trivial because the colour indices are different.
879 static void cfgtopalette(void)
882 static const int ww
[] = {
883 6, 7, 8, 9, 10, 11, 12, 13,
884 14, 15, 16, 17, 18, 19, 20, 21,
885 0, 1, 2, 3, 4, 4, 5, 5
888 for (i
= 0; i
< 24; i
++) {
890 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
891 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
892 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
897 * Set up the colour palette.
899 static void init_palette(void)
902 HDC hdc
= GetDC(hwnd
);
904 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
905 logpal
= smalloc(sizeof(*logpal
)
906 - sizeof(logpal
->palPalEntry
)
907 + NCOLOURS
* sizeof(PALETTEENTRY
));
908 logpal
->palVersion
= 0x300;
909 logpal
->palNumEntries
= NCOLOURS
;
910 for (i
= 0; i
< NCOLOURS
; i
++) {
911 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
912 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
913 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
914 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
916 pal
= CreatePalette(logpal
);
918 SelectPalette(hdc
, pal
, FALSE
);
920 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
923 ReleaseDC(hwnd
, hdc
);
926 for (i
= 0; i
< NCOLOURS
; i
++)
927 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
931 for (i
= 0; i
< NCOLOURS
; i
++)
932 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
933 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
937 * Initialise all the fonts we will need initially. There may be as many as
938 * three or as few as one. The other (poentially) twentyone fonts are done
939 * if/when they are needed.
943 * - check the font width and height, correcting our guesses if
946 * - verify that the bold font is the same width as the ordinary
947 * one, and engage shadow bolding if not.
949 * - verify that the underlined font is the same width as the
950 * ordinary one (manual underlining by means of line drawing can
951 * be done in a pinch).
953 static void init_fonts(int pick_width
, int pick_height
)
960 int fw_dontcare
, fw_bold
;
962 for (i
= 0; i
< FONT_MAXNO
; i
++)
965 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
968 if (cfg
.fontisbold
) {
969 fw_dontcare
= FW_BOLD
;
972 fw_dontcare
= FW_DONTCARE
;
979 font_height
= pick_height
;
981 font_height
= cfg
.fontheight
;
982 if (font_height
> 0) {
984 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
987 font_width
= pick_width
;
990 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
991 c, OUT_DEFAULT_PRECIS, \
992 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
993 FIXED_PITCH | FF_DONTCARE, cfg.font)
995 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
997 lfont
.lfHeight
= font_height
;
998 lfont
.lfWidth
= font_width
;
999 lfont
.lfEscapement
= 0;
1000 lfont
.lfOrientation
= 0;
1001 lfont
.lfWeight
= fw_dontcare
;
1002 lfont
.lfItalic
= FALSE
;
1003 lfont
.lfUnderline
= FALSE
;
1004 lfont
.lfStrikeOut
= FALSE
;
1005 lfont
.lfCharSet
= cfg
.fontcharset
;
1006 lfont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
1007 lfont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
1008 lfont
.lfQuality
= DEFAULT_QUALITY
;
1009 lfont
.lfPitchAndFamily
= FIXED_PITCH
| FF_DONTCARE
;
1010 strncpy(lfont
.lfFaceName
, cfg
.font
, LF_FACESIZE
);
1012 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
1013 GetTextMetrics(hdc
, &tm
);
1015 if (pick_width
== 0 || pick_height
== 0) {
1016 font_height
= tm
.tmHeight
;
1017 font_width
= tm
.tmAveCharWidth
;
1019 font_dualwidth
= (tm
.tmAveCharWidth
!= tm
.tmMaxCharWidth
);
1021 #ifdef RDB_DEBUG_PATCH
1022 debug(23, "Primary font H=%d, AW=%d, MW=%d",
1023 tm
.tmHeight
, tm
.tmAveCharWidth
, tm
.tmMaxCharWidth
);
1028 DWORD cset
= tm
.tmCharSet
;
1029 memset(&info
, 0xFF, sizeof(info
));
1031 /* !!! Yes the next line is right */
1032 if (cset
== OEM_CHARSET
)
1033 font_codepage
= GetOEMCP();
1035 if (TranslateCharsetInfo
1036 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
1041 GetCPInfo(font_codepage
, &cpinfo
);
1042 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
1045 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
1048 * Some fonts, e.g. 9-pt Courier, draw their underlines
1049 * outside their character cell. We successfully prevent
1050 * screen corruption by clipping the text output, but then
1051 * we lose the underline completely. Here we try to work
1052 * out whether this is such a font, and if it is, we set a
1053 * flag that causes underlines to be drawn by hand.
1055 * Having tried other more sophisticated approaches (such
1056 * as examining the TEXTMETRIC structure or requesting the
1057 * height of a string), I think we'll do this the brute
1058 * force way: we create a small bitmap, draw an underlined
1059 * space on it, and test to see whether any pixels are
1060 * foreground-coloured. (Since we expect the underline to
1061 * go all the way across the character cell, we only search
1062 * down a single column of the bitmap, half way across.)
1066 HBITMAP und_bm
, und_oldbm
;
1070 und_dc
= CreateCompatibleDC(hdc
);
1071 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
1072 und_oldbm
= SelectObject(und_dc
, und_bm
);
1073 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
1074 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
1075 SetTextColor(und_dc
, RGB(255, 255, 255));
1076 SetBkColor(und_dc
, RGB(0, 0, 0));
1077 SetBkMode(und_dc
, OPAQUE
);
1078 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
1080 for (i
= 0; i
< font_height
; i
++) {
1081 c
= GetPixel(und_dc
, font_width
/ 2, i
);
1082 if (c
!= RGB(0, 0, 0))
1085 SelectObject(und_dc
, und_oldbm
);
1086 DeleteObject(und_bm
);
1089 und_mode
= UND_LINE
;
1090 DeleteObject(fonts
[FONT_UNDERLINE
]);
1091 fonts
[FONT_UNDERLINE
] = 0;
1095 if (bold_mode
== BOLD_FONT
) {
1096 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
1100 descent
= tm
.tmAscent
+ 1;
1101 if (descent
>= font_height
)
1102 descent
= font_height
- 1;
1104 for (i
= 0; i
< 3; i
++) {
1106 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
1107 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
1114 ReleaseDC(hwnd
, hdc
);
1116 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1117 und_mode
= UND_LINE
;
1118 DeleteObject(fonts
[FONT_UNDERLINE
]);
1119 fonts
[FONT_UNDERLINE
] = 0;
1122 if (bold_mode
== BOLD_FONT
&&
1123 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1124 bold_mode
= BOLD_SHADOW
;
1125 DeleteObject(fonts
[FONT_BOLD
]);
1126 fonts
[FONT_BOLD
] = 0;
1128 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1133 static void another_font(int fontno
)
1136 int fw_dontcare
, fw_bold
;
1140 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1143 basefont
= (fontno
& ~(FONT_BOLDUND
));
1144 if (basefont
!= fontno
&& !fontflag
[basefont
])
1145 another_font(basefont
);
1147 if (cfg
.fontisbold
) {
1148 fw_dontcare
= FW_BOLD
;
1151 fw_dontcare
= FW_DONTCARE
;
1155 c
= cfg
.fontcharset
;
1161 if (fontno
& FONT_WIDE
)
1163 if (fontno
& FONT_NARROW
)
1165 if (fontno
& FONT_OEM
)
1167 if (fontno
& FONT_BOLD
)
1169 if (fontno
& FONT_UNDERLINE
)
1173 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1174 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1175 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1176 FIXED_PITCH
| FF_DONTCARE
, s
);
1178 fontflag
[fontno
] = 1;
1181 static void deinit_fonts(void)
1184 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1186 DeleteObject(fonts
[i
]);
1192 void request_resize(int w
, int h
)
1196 /* If the window is maximized supress resizing attempts */
1197 if (IsZoomed(hwnd
)) {
1198 if (cfg
.resize_action
== RESIZE_TERM
)
1202 if (cfg
.resize_action
== RESIZE_DISABLED
) return;
1203 if (h
== rows
&& w
== cols
) return;
1205 /* Sanity checks ... */
1207 static int first_time
= 1;
1210 switch (first_time
) {
1212 /* Get the size of the screen */
1213 if (GetClientRect(GetDesktopWindow(), &ss
))
1214 /* first_time = 0 */ ;
1220 /* Make sure the values are sane */
1221 width
= (ss
.right
- ss
.left
- extra_width
) / 4;
1222 height
= (ss
.bottom
- ss
.top
- extra_height
) / 6;
1224 if (w
> width
|| h
> height
)
1233 term_size(h
, w
, cfg
.savelines
);
1235 if (cfg
.resize_action
!= RESIZE_FONT
&& !IsZoomed(hwnd
)) {
1236 width
= extra_width
+ font_width
* w
;
1237 height
= extra_height
+ font_height
* h
;
1239 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1240 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1241 SWP_NOMOVE
| SWP_NOZORDER
);
1245 InvalidateRect(hwnd
, NULL
, TRUE
);
1248 static void reset_window(int reinit
) {
1250 * This function decides how to resize or redraw when the
1251 * user changes something.
1253 * This function doesn't like to change the terminal size but if the
1254 * font size is locked that may be it's only soluion.
1256 int win_width
, win_height
;
1259 #ifdef RDB_DEBUG_PATCH
1260 debug((27, "reset_window()"));
1263 /* Current window sizes ... */
1264 GetWindowRect(hwnd
, &wr
);
1265 GetClientRect(hwnd
, &cr
);
1267 win_width
= cr
.right
- cr
.left
;
1268 win_height
= cr
.bottom
- cr
.top
;
1270 if (cfg
.resize_action
== RESIZE_DISABLED
) reinit
= 2;
1272 /* Are we being forced to reload the fonts ? */
1274 #ifdef RDB_DEBUG_PATCH
1275 debug((27, "reset_window() -- Forced deinit"));
1281 /* Oh, looks like we're minimised */
1282 if (win_width
== 0 || win_height
== 0)
1285 /* Is the window out of position ? */
1287 (offset_width
!= (win_width
-font_width
*cols
)/2 ||
1288 offset_height
!= (win_height
-font_height
*rows
)/2) ){
1289 offset_width
= (win_width
-font_width
*cols
)/2;
1290 offset_height
= (win_height
-font_height
*rows
)/2;
1291 InvalidateRect(hwnd
, NULL
, TRUE
);
1292 #ifdef RDB_DEBUG_PATCH
1293 debug((27, "reset_window() -> Reposition terminal"));
1297 if (IsZoomed(hwnd
)) {
1298 /* We're fullscreen, this means we must not change the size of
1299 * the window so it's the font size or the terminal itself.
1302 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1303 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1305 if (cfg
.resize_action
!= RESIZE_TERM
) {
1306 if ( font_width
!= win_width
/cols
||
1307 font_height
!= win_height
/rows
) {
1309 init_fonts(win_width
/cols
, win_height
/rows
);
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((25, "reset_window() -> Z font resize to (%d, %d)",
1315 font_width
, font_height
));
1319 if ( font_width
!= win_width
/cols
||
1320 font_height
!= win_height
/rows
) {
1321 /* Our only choice at this point is to change the
1322 * size of the terminal; Oh well.
1324 term_size( win_height
/font_height
, win_width
/font_width
,
1326 offset_width
= (win_width
-font_width
*cols
)/2;
1327 offset_height
= (win_height
-font_height
*rows
)/2;
1328 InvalidateRect(hwnd
, NULL
, TRUE
);
1329 #ifdef RDB_DEBUG_PATCH
1330 debug((27, "reset_window() -> Zoomed term_size"));
1337 /* Hmm, a force re-init means we should ignore the current window
1338 * so we resize to the default font size.
1341 #ifdef RDB_DEBUG_PATCH
1342 debug((27, "reset_window() -> Forced re-init"));
1345 offset_width
= offset_height
= cfg
.window_border
;
1346 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1347 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1349 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1350 win_height
!= font_height
*rows
+ offset_height
*2) {
1352 /* If this is too large windows will resize it to the maximum
1353 * allowed window size, we will then be back in here and resize
1354 * the font or terminal to fit.
1356 SetWindowPos(hwnd
, NULL
, 0, 0,
1357 font_width
*cols
+ extra_width
,
1358 font_height
*rows
+ extra_height
,
1359 SWP_NOMOVE
| SWP_NOZORDER
);
1362 InvalidateRect(hwnd
, NULL
, TRUE
);
1366 /* Okay the user doesn't want us to change the font so we try the
1367 * window. But that may be too big for the screen which forces us
1368 * to change the terminal.
1370 if ((cfg
.resize_action
== RESIZE_TERM
&& reinit
<=0) ||
1371 (cfg
.resize_action
== RESIZE_EITHER
&& reinit
<0) ||
1373 offset_width
= offset_height
= cfg
.window_border
;
1374 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1375 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1377 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1378 win_height
!= font_height
*rows
+ offset_height
*2) {
1383 GetClientRect(GetDesktopWindow(), &ss
);
1384 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1385 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1388 if ( rows
> height
|| cols
> width
) {
1389 if (cfg
.resize_action
== RESIZE_EITHER
) {
1390 /* Make the font the biggest we can */
1392 font_width
= (ss
.right
- ss
.left
- extra_width
)/cols
;
1394 font_height
= (ss
.bottom
- ss
.top
- extra_height
)/rows
;
1397 init_fonts(font_width
, font_height
);
1399 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1400 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1402 if ( height
> rows
) height
= rows
;
1403 if ( width
> cols
) width
= cols
;
1404 term_size(height
, width
, cfg
.savelines
);
1405 #ifdef RDB_DEBUG_PATCH
1406 debug((27, "reset_window() -> term resize to (%d,%d)",
1412 SetWindowPos(hwnd
, NULL
, 0, 0,
1413 font_width
*cols
+ extra_width
,
1414 font_height
*rows
+ extra_height
,
1415 SWP_NOMOVE
| SWP_NOZORDER
);
1417 InvalidateRect(hwnd
, NULL
, TRUE
);
1418 #ifdef RDB_DEBUG_PATCH
1419 debug((27, "reset_window() -> window resize to (%d,%d)",
1420 font_width
*cols
+ extra_width
,
1421 font_height
*rows
+ extra_height
));
1427 /* We're allowed to or must change the font but do we want to ? */
1429 if (font_width
!= (win_width
-cfg
.window_border
*2)/cols
||
1430 font_height
!= (win_height
-cfg
.window_border
*2)/rows
) {
1433 init_fonts((win_width
-cfg
.window_border
*2)/cols
,
1434 (win_height
-cfg
.window_border
*2)/rows
);
1435 offset_width
= (win_width
-font_width
*cols
)/2;
1436 offset_height
= (win_height
-font_height
*rows
)/2;
1438 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+offset_width
*2;
1439 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1441 InvalidateRect(hwnd
, NULL
, TRUE
);
1442 #ifdef RDB_DEBUG_PATCH
1443 debug((25, "reset_window() -> font resize to (%d,%d)",
1444 font_width
, font_height
));
1449 static void set_input_locale(HKL kl
)
1453 GetLocaleInfo(LOWORD(kl
), LOCALE_IDEFAULTANSICODEPAGE
,
1454 lbuf
, sizeof(lbuf
));
1456 kbd_codepage
= atoi(lbuf
);
1459 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
, int alt
)
1461 int thistime
= GetMessageTime();
1463 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1464 lastbtn
= MBT_NOTHING
;
1465 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
, alt
);
1469 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1470 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1471 lastact
== MA_2CLK ? MA_3CLK
:
1472 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1477 if (lastact
!= MA_NOTHING
)
1478 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
, alt
);
1479 lasttime
= thistime
;
1483 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1484 * into a cooked one (SELECT, EXTEND, PASTE).
1486 Mouse_Button
translate_button(Mouse_Button button
)
1488 if (button
== MBT_LEFT
)
1490 if (button
== MBT_MIDDLE
)
1491 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1492 if (button
== MBT_RIGHT
)
1493 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1494 return 0; /* shouldn't happen */
1497 static void show_mouseptr(int show
)
1499 static int cursor_visible
= 1;
1500 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1502 if (cursor_visible
&& !show
)
1504 else if (!cursor_visible
&& show
)
1506 cursor_visible
= show
;
1509 static int is_alt_pressed(void)
1512 int r
= GetKeyboardState(keystate
);
1515 if (keystate
[VK_MENU
] & 0x80)
1517 if (keystate
[VK_RMENU
] & 0x80)
1522 static int resizing
;
1524 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1525 WPARAM wParam
, LPARAM lParam
)
1528 static int ignore_clip
= FALSE
;
1529 static int need_backend_resize
= FALSE
;
1530 static int fullscr_on_max
= FALSE
;
1534 if (pending_netevent
)
1535 enact_pending_netevent();
1536 if (GetCapture() != hwnd
)
1542 if (cfg
.ping_interval
> 0) {
1545 if (now
- last_movement
> cfg
.ping_interval
) {
1546 back
->special(TS_PING
);
1547 last_movement
= now
;
1550 net_pending_errors();
1556 if (!cfg
.warn_on_close
|| session_closed
||
1558 "Are you sure you want to close this session?",
1559 "PuTTY Exit Confirmation",
1560 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1561 DestroyWindow(hwnd
);
1568 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1580 PROCESS_INFORMATION pi
;
1581 HANDLE filemap
= NULL
;
1583 if (wParam
== IDM_DUPSESS
) {
1585 * Allocate a file-mapping memory chunk for the
1588 SECURITY_ATTRIBUTES sa
;
1591 sa
.nLength
= sizeof(sa
);
1592 sa
.lpSecurityDescriptor
= NULL
;
1593 sa
.bInheritHandle
= TRUE
;
1594 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1597 0, sizeof(Config
), NULL
);
1599 p
= (Config
*) MapViewOfFile(filemap
,
1601 0, 0, sizeof(Config
));
1603 *p
= cfg
; /* structure copy */
1607 sprintf(c
, "putty &%p", filemap
);
1609 } else if (wParam
== IDM_SAVEDSESS
) {
1610 if ((lParam
- IDM_SAVED_MIN
) / 16 < nsessions
) {
1612 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1613 cl
= smalloc(16 + strlen(session
));
1614 /* 8, but play safe */
1617 /* not a very important failure mode */
1619 sprintf(cl
, "putty @%s", session
);
1627 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1629 si
.lpReserved
= NULL
;
1630 si
.lpDesktop
= NULL
;
1634 si
.lpReserved2
= NULL
;
1635 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1636 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1639 CloseHandle(filemap
);
1649 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1652 if (!do_reconfig(hwnd
))
1656 /* Disable full-screen if resizing forbidden */
1657 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
1658 EnableMenuItem(m
, IDM_FULLSCREEN
, MF_BYCOMMAND
|
1659 (cfg
.resize_action
== RESIZE_DISABLED
)
1660 ? MF_GRAYED
: MF_ENABLED
);
1661 /* Gracefully unzoom if necessary */
1662 if (IsZoomed(hwnd
) &&
1663 (cfg
.resize_action
== RESIZE_DISABLED
)) {
1664 ShowWindow(hwnd
, SW_RESTORE
);
1668 if (strcmp(prev_cfg
.logfilename
, cfg
.logfilename
) ||
1669 prev_cfg
.logtype
!= cfg
.logtype
) {
1670 logfclose(); /* reset logging */
1676 * Flush the line discipline's edit buffer in the
1677 * case where local editing has just been disabled.
1679 ldisc_send(NULL
, 0, 0);
1687 /* Give terminal a heads-up on miscellaneous stuff */
1690 /* Screen size changed ? */
1691 if (cfg
.height
!= prev_cfg
.height
||
1692 cfg
.width
!= prev_cfg
.width
||
1693 cfg
.savelines
!= prev_cfg
.savelines
||
1694 cfg
.resize_action
== RESIZE_FONT
||
1695 (cfg
.resize_action
== RESIZE_EITHER
&& IsZoomed(hwnd
)) ||
1696 cfg
.resize_action
== RESIZE_DISABLED
)
1697 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1699 /* Enable or disable the scroll bar, etc */
1701 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1702 LONG nexflag
, exflag
=
1703 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1706 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1707 if (cfg
.alwaysontop
) {
1708 nexflag
|= WS_EX_TOPMOST
;
1709 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1710 SWP_NOMOVE
| SWP_NOSIZE
);
1712 nexflag
&= ~(WS_EX_TOPMOST
);
1713 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1714 SWP_NOMOVE
| SWP_NOSIZE
);
1717 if (cfg
.sunken_edge
)
1718 nexflag
|= WS_EX_CLIENTEDGE
;
1720 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1723 if (is_full_screen() ?
1724 cfg
.scrollbar_in_fullscreen
: cfg
.scrollbar
)
1727 nflg
&= ~WS_VSCROLL
;
1729 if (cfg
.resize_action
== RESIZE_DISABLED
||
1731 nflg
&= ~WS_THICKFRAME
;
1733 nflg
|= WS_THICKFRAME
;
1735 if (cfg
.resize_action
== RESIZE_DISABLED
)
1736 nflg
&= ~WS_MAXIMIZEBOX
;
1738 nflg
|= WS_MAXIMIZEBOX
;
1740 if (nflg
!= flag
|| nexflag
!= exflag
) {
1742 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1743 if (nexflag
!= exflag
)
1744 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1746 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1747 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1748 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1756 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1761 set_title(cfg
.wintitle
);
1762 if (IsIconic(hwnd
)) {
1764 cfg
.win_name_always ? window_name
:
1768 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1769 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1770 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1771 cfg
.fontheight
!= prev_cfg
.fontheight
||
1772 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1773 cfg
.vtmode
!= prev_cfg
.vtmode
||
1774 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1775 cfg
.resize_action
== RESIZE_DISABLED
||
1776 cfg
.resize_action
== RESIZE_EITHER
||
1777 (cfg
.resize_action
!= prev_cfg
.resize_action
))
1780 InvalidateRect(hwnd
, NULL
, TRUE
);
1781 reset_window(init_lvl
);
1782 net_pending_errors();
1795 back
->special(TS_AYT
);
1796 net_pending_errors();
1799 back
->special(TS_BRK
);
1800 net_pending_errors();
1803 back
->special(TS_SYNCH
);
1804 net_pending_errors();
1807 back
->special(TS_EC
);
1808 net_pending_errors();
1811 back
->special(TS_EL
);
1812 net_pending_errors();
1815 back
->special(TS_GA
);
1816 net_pending_errors();
1819 back
->special(TS_NOP
);
1820 net_pending_errors();
1823 back
->special(TS_ABORT
);
1824 net_pending_errors();
1827 back
->special(TS_AO
);
1828 net_pending_errors();
1831 back
->special(TS_IP
);
1832 net_pending_errors();
1835 back
->special(TS_SUSP
);
1836 net_pending_errors();
1839 back
->special(TS_EOR
);
1840 net_pending_errors();
1843 back
->special(TS_EOF
);
1844 net_pending_errors();
1850 WinHelp(hwnd
, help_path
,
1851 help_has_contents ? HELP_FINDER
: HELP_CONTENTS
, 0);
1855 * We get this if the System menu has been activated
1862 * We get this if the System menu has been activated
1863 * using the keyboard. This might happen from within
1864 * TranslateKey, in which case it really wants to be
1865 * followed by a `space' character to actually _bring
1866 * the menu up_ rather than just sitting there in
1867 * `ready to appear' state.
1869 show_mouseptr(1); /* make sure pointer is visible */
1871 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1873 case IDM_FULLSCREEN
:
1877 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1878 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1883 #define X_POS(l) ((int)(short)LOWORD(l))
1884 #define Y_POS(l) ((int)(short)HIWORD(l))
1886 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1887 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1888 case WM_LBUTTONDOWN
:
1889 case WM_MBUTTONDOWN
:
1890 case WM_RBUTTONDOWN
:
1898 case WM_LBUTTONDOWN
:
1902 case WM_MBUTTONDOWN
:
1903 button
= MBT_MIDDLE
;
1906 case WM_RBUTTONDOWN
:
1915 button
= MBT_MIDDLE
;
1923 button
= press
= 0; /* shouldn't happen */
1927 * Special case: in full-screen mode, if the left
1928 * button is clicked in the very top left corner of the
1929 * window, we put up the System menu instead of doing
1932 if (is_full_screen() && press
&& button
== MBT_LEFT
&&
1933 X_POS(lParam
) == 0 && Y_POS(lParam
) == 0) {
1934 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_MOUSEMENU
, 0);
1939 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1940 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
,
1944 term_mouse(button
, MA_RELEASE
,
1945 TO_CHR_X(X_POS(lParam
)),
1946 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1947 wParam
& MK_CONTROL
, is_alt_pressed());
1955 * Add the mouse position and message time to the random
1958 noise_ultralight(lParam
);
1960 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
) &&
1961 GetCapture() == hwnd
) {
1963 if (wParam
& MK_LBUTTON
)
1965 else if (wParam
& MK_MBUTTON
)
1969 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1970 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1971 wParam
& MK_CONTROL
, is_alt_pressed());
1974 case WM_NCMOUSEMOVE
:
1976 noise_ultralight(lParam
);
1978 case WM_IGNORE_CLIP
:
1979 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1981 case WM_DESTROYCLIPBOARD
:
1984 ignore_clip
= FALSE
;
1990 hdc
= BeginPaint(hwnd
, &p
);
1992 SelectPalette(hdc
, pal
, TRUE
);
1993 RealizePalette(hdc
);
1996 (p
.rcPaint
.left
-offset_width
)/font_width
,
1997 (p
.rcPaint
.top
-offset_height
)/font_height
,
1998 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
1999 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
2002 p
.rcPaint
.left
< offset_width
||
2003 p
.rcPaint
.top
< offset_height
||
2004 p
.rcPaint
.right
>= offset_width
+ font_width
*cols
||
2005 p
.rcPaint
.bottom
>= offset_height
+ font_height
*rows
)
2007 HBRUSH fillcolour
, oldbrush
;
2009 fillcolour
= CreateSolidBrush (
2010 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
2011 oldbrush
= SelectObject(hdc
, fillcolour
);
2012 edge
= CreatePen(PS_SOLID
, 0,
2013 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
2014 oldpen
= SelectObject(hdc
, edge
);
2016 ExcludeClipRect(hdc
,
2017 offset_width
, offset_height
,
2018 offset_width
+font_width
*cols
,
2019 offset_height
+font_height
*rows
);
2021 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
2022 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
2024 // SelectClipRgn(hdc, NULL);
2026 SelectObject(hdc
, oldbrush
);
2027 DeleteObject(fillcolour
);
2028 SelectObject(hdc
, oldpen
);
2031 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
2032 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
2038 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2039 * but the only one that's likely to try to overload us is FD_READ.
2040 * This means buffering just one is fine.
2042 if (pending_netevent
)
2043 enact_pending_netevent();
2045 pending_netevent
= TRUE
;
2046 pend_netevent_wParam
= wParam
;
2047 pend_netevent_lParam
= lParam
;
2048 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
2049 enact_pending_netevent();
2051 time(&last_movement
);
2055 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
2057 flash_window(0); /* stop */
2066 caret_x
= caret_y
= -1; /* ensure caret is replaced next time */
2070 case WM_ENTERSIZEMOVE
:
2071 #ifdef RDB_DEBUG_PATCH
2072 debug((27, "WM_ENTERSIZEMOVE"));
2076 need_backend_resize
= FALSE
;
2078 case WM_EXITSIZEMOVE
:
2081 #ifdef RDB_DEBUG_PATCH
2082 debug((27, "WM_EXITSIZEMOVE"));
2084 if (need_backend_resize
) {
2085 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
2086 InvalidateRect(hwnd
, NULL
, TRUE
);
2091 * This does two jobs:
2092 * 1) Keep the sizetip uptodate
2093 * 2) Make sure the window size is _stepped_ in units of the font size.
2095 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2096 int width
, height
, w
, h
, ew
, eh
;
2097 LPRECT r
= (LPRECT
) lParam
;
2099 if ( !need_backend_resize
&& cfg
.resize_action
== RESIZE_EITHER
&&
2100 (cfg
.height
!= rows
|| cfg
.width
!= cols
)) {
2102 * Great! It seems that both the terminal size and the
2103 * font size have been changed and the user is now dragging.
2105 * It will now be difficult to get back to the configured
2108 * This would be easier but it seems to be too confusing.
2110 term_size(cfg.height, cfg.width, cfg.savelines);
2113 cfg
.height
=rows
; cfg
.width
=cols
;
2115 InvalidateRect(hwnd
, NULL
, TRUE
);
2116 need_backend_resize
= TRUE
;
2119 width
= r
->right
- r
->left
- extra_width
;
2120 height
= r
->bottom
- r
->top
- extra_height
;
2121 w
= (width
+ font_width
/ 2) / font_width
;
2124 h
= (height
+ font_height
/ 2) / font_height
;
2127 UpdateSizeTip(hwnd
, w
, h
);
2128 ew
= width
- w
* font_width
;
2129 eh
= height
- h
* font_height
;
2131 if (wParam
== WMSZ_LEFT
||
2132 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2138 if (wParam
== WMSZ_TOP
||
2139 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2149 int width
, height
, w
, h
, rv
= 0;
2150 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
2151 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2152 LPRECT r
= (LPRECT
) lParam
;
2154 width
= r
->right
- r
->left
- ex_width
;
2155 height
= r
->bottom
- r
->top
- ex_height
;
2157 w
= (width
+ cols
/2)/cols
;
2158 h
= (height
+ rows
/2)/rows
;
2159 if ( r
->right
!= r
->left
+ w
*cols
+ ex_width
)
2162 if (wParam
== WMSZ_LEFT
||
2163 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2164 r
->left
= r
->right
- w
*cols
- ex_width
;
2166 r
->right
= r
->left
+ w
*cols
+ ex_width
;
2168 if (r
->bottom
!= r
->top
+ h
*rows
+ ex_height
)
2171 if (wParam
== WMSZ_TOP
||
2172 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2173 r
->top
= r
->bottom
- h
*rows
- ex_height
;
2175 r
->bottom
= r
->top
+ h
*rows
+ ex_height
;
2179 /* break; (never reached) */
2180 case WM_FULLSCR_ON_MAX
:
2181 fullscr_on_max
= TRUE
;
2184 sys_cursor_update();
2187 #ifdef RDB_DEBUG_PATCH
2188 debug((27, "WM_SIZE %s (%d,%d)",
2189 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2190 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2191 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2192 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2194 LOWORD(lParam
), HIWORD(lParam
)));
2196 if (wParam
== SIZE_MINIMIZED
)
2198 cfg
.win_name_always ? window_name
: icon_name
);
2199 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2200 SetWindowText(hwnd
, window_name
);
2201 if (wParam
== SIZE_RESTORED
)
2202 clear_full_screen();
2203 if (wParam
== SIZE_MAXIMIZED
&& fullscr_on_max
) {
2205 fullscr_on_max
= FALSE
;
2208 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2209 /* A resize, well it better be a minimize. */
2213 int width
, height
, w
, h
;
2215 width
= LOWORD(lParam
);
2216 height
= HIWORD(lParam
);
2219 if (wParam
== SIZE_MAXIMIZED
&& !was_zoomed
) {
2223 if (cfg
.resize_action
== RESIZE_TERM
) {
2224 w
= width
/ font_width
;
2226 h
= height
/ font_height
;
2229 term_size(h
, w
, cfg
.savelines
);
2232 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2234 if (cfg
.resize_action
== RESIZE_TERM
)
2235 term_size(prev_rows
, prev_cols
, cfg
.savelines
);
2236 if (cfg
.resize_action
!= RESIZE_FONT
)
2241 /* This is an unexpected resize, these will normally happen
2242 * if the window is too large. Probably either the user
2243 * selected a huge font or the screen size has changed.
2245 * This is also called with minimize.
2247 else reset_window(-1);
2251 * Don't call back->size in mid-resize. (To prevent
2252 * massive numbers of resize events getting sent
2253 * down the connection during an NT opaque drag.)
2256 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2257 need_backend_resize
= TRUE
;
2258 w
= (width
-cfg
.window_border
*2) / font_width
;
2260 h
= (height
-cfg
.window_border
*2) / font_height
;
2269 sys_cursor_update();
2272 switch (LOWORD(wParam
)) {
2286 term_scroll(0, +rows
/ 2);
2289 term_scroll(0, -rows
/ 2);
2291 case SB_THUMBPOSITION
:
2293 term_scroll(1, HIWORD(wParam
));
2297 case WM_PALETTECHANGED
:
2298 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2299 HDC hdc
= get_ctx();
2301 if (RealizePalette(hdc
) > 0)
2307 case WM_QUERYNEWPALETTE
:
2309 HDC hdc
= get_ctx();
2311 if (RealizePalette(hdc
) > 0)
2323 * Add the scan code and keypress timing to the random
2326 noise_ultralight(lParam
);
2329 * We don't do TranslateMessage since it disassociates the
2330 * resulting CHAR message from the KEYDOWN that sparked it,
2331 * which we occasionally don't want. Instead, we process
2332 * KEYDOWN, and call the Win32 translator functions so that
2333 * we get the translations under _our_ control.
2336 unsigned char buf
[20];
2339 if (wParam
== VK_PROCESSKEY
) {
2342 m
.message
= WM_KEYDOWN
;
2344 m
.lParam
= lParam
& 0xdfff;
2345 TranslateMessage(&m
);
2347 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2349 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2353 * Interrupt an ongoing paste. I'm not sure
2354 * this is sensible, but for the moment it's
2355 * preferable to having to faff about buffering
2361 * We need not bother about stdin backlogs
2362 * here, because in GUI PuTTY we can't do
2363 * anything about it anyway; there's no means
2364 * of asking Windows to hold off on KEYDOWN
2365 * messages. We _have_ to buffer everything
2368 ldisc_send(buf
, len
, 1);
2373 net_pending_errors();
2375 case WM_INPUTLANGCHANGE
:
2376 /* wParam == Font number */
2377 /* lParam == Locale */
2378 set_input_locale((HKL
)lParam
);
2379 sys_cursor_update();
2382 if(wParam
== IMN_SETOPENSTATUS
) {
2383 HIMC hImc
= ImmGetContext(hwnd
);
2384 ImmSetCompositionFont(hImc
, &lfont
);
2385 ImmReleaseContext(hwnd
, hImc
);
2389 case WM_IME_COMPOSITION
:
2395 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2396 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2398 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2399 break; /* fall back to DefWindowProc */
2401 hIMC
= ImmGetContext(hwnd
);
2402 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2406 buff
= (char*) smalloc(n
);
2407 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2409 * Jaeyoun Chung reports that Korean character
2410 * input doesn't work correctly if we do a single
2411 * luni_send() covering the whole of buff. So
2412 * instead we luni_send the characters one by one.
2414 for (i
= 0; i
< n
; i
+= 2)
2415 luni_send((unsigned short *)(buff
+i
), 1, 1);
2418 ImmReleaseContext(hwnd
, hIMC
);
2423 if (wParam
& 0xFF00) {
2424 unsigned char buf
[2];
2427 buf
[0] = wParam
>> 8;
2428 lpage_send(kbd_codepage
, buf
, 2, 1);
2430 char c
= (unsigned char) wParam
;
2431 lpage_send(kbd_codepage
, &c
, 1, 1);
2437 * Nevertheless, we are prepared to deal with WM_CHAR
2438 * messages, should they crop up. So if someone wants to
2439 * post the things to us as part of a macro manoeuvre,
2440 * we're ready to cope.
2443 char c
= (unsigned char)wParam
;
2444 lpage_send(CP_ACP
, &c
, 1, 1);
2448 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2449 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2453 if (message
== wm_mousewheel
|| message
== WM_MOUSEWHEEL
) {
2454 int shift_pressed
=0, control_pressed
=0, alt_pressed
=0;
2456 if (message
== WM_MOUSEWHEEL
) {
2457 wheel_accumulator
+= (short)HIWORD(wParam
);
2458 shift_pressed
=LOWORD(wParam
) & MK_SHIFT
;
2459 control_pressed
=LOWORD(wParam
) & MK_CONTROL
;
2462 wheel_accumulator
+= (int)wParam
;
2463 if (GetKeyboardState(keys
)!=0) {
2464 shift_pressed
=keys
[VK_SHIFT
]&0x80;
2465 control_pressed
=keys
[VK_CONTROL
]&0x80;
2469 /* process events when the threshold is reached */
2470 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
2473 /* reduce amount for next time */
2474 if (wheel_accumulator
> 0) {
2476 wheel_accumulator
-= WHEEL_DELTA
;
2477 } else if (wheel_accumulator
< 0) {
2479 wheel_accumulator
+= WHEEL_DELTA
;
2483 if (send_raw_mouse
&&
2484 !(cfg
.mouse_override
&& shift_pressed
)) {
2485 /* send a mouse-down followed by a mouse up */
2488 TO_CHR_X(X_POS(lParam
)),
2489 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2490 control_pressed
, is_alt_pressed());
2491 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
2492 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2493 control_pressed
, is_alt_pressed());
2495 /* trigger a scroll */
2497 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
2504 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2508 * Move the system caret. (We maintain one, even though it's
2509 * invisible, for the benefit of blind people: apparently some
2510 * helper software tracks the system caret, so we should arrange to
2513 void sys_cursor(int x
, int y
)
2517 if (!has_focus
) return;
2520 * Avoid gratuitously re-updating the cursor position and IMM
2521 * window if there's no actual change required.
2523 cx
= x
* font_width
+ offset_width
;
2524 cy
= y
* font_height
+ offset_height
;
2525 if (cx
== caret_x
&& cy
== caret_y
)
2530 sys_cursor_update();
2533 static void sys_cursor_update(void)
2538 if (!has_focus
) return;
2540 if (caret_x
< 0 || caret_y
< 0)
2543 SetCaretPos(caret_x
, caret_y
);
2545 /* IMM calls on Win98 and beyond only */
2546 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2548 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2549 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2551 /* we should have the IMM functions */
2552 hIMC
= ImmGetContext(hwnd
);
2553 cf
.dwStyle
= CFS_POINT
;
2554 cf
.ptCurrentPos
.x
= caret_x
;
2555 cf
.ptCurrentPos
.y
= caret_y
;
2556 ImmSetCompositionWindow(hIMC
, &cf
);
2558 ImmReleaseContext(hwnd
, hIMC
);
2562 * Draw a line of text in the window, at given character
2563 * coordinates, in given attributes.
2565 * We are allowed to fiddle with the contents of `text'.
2567 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2568 unsigned long attr
, int lattr
)
2571 int nfg
, nbg
, nfont
;
2574 int force_manual_underline
= 0;
2575 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2576 int char_width
= fnt_width
;
2577 int text_adjust
= 0;
2578 static int *IpDx
= 0, IpDxLEN
= 0;
2580 if (attr
& ATTR_WIDE
)
2583 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2585 if (len
> IpDxLEN
) {
2587 IpDx
= smalloc((len
+ 16) * sizeof(int));
2588 IpDxLEN
= (len
+ 16);
2590 for (i
= 0; i
< IpDxLEN
; i
++)
2591 IpDx
[i
] = char_width
;
2594 /* Only want the left half of double width lines */
2595 if (lattr
!= LATTR_NORM
&& x
*2 >= cols
)
2603 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
2604 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2605 attr
^= ATTR_CUR_XOR
;
2609 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2610 /* Assume a poorman font is borken in other ways too. */
2620 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2623 if (attr
& ATTR_NARROW
)
2624 nfont
|= FONT_NARROW
;
2626 /* Special hack for the VT100 linedraw glyphs. */
2627 if ((attr
& CSET_MASK
) == 0x2300) {
2628 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2629 switch ((unsigned char) (text
[0])) {
2631 text_adjust
= -2 * font_height
/ 5;
2634 text_adjust
= -1 * font_height
/ 5;
2637 text_adjust
= font_height
/ 5;
2640 text_adjust
= 2 * font_height
/ 5;
2643 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2646 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2647 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2648 if (attr
& ATTR_UNDER
) {
2649 attr
&= ~ATTR_UNDER
;
2650 force_manual_underline
= 1;
2655 /* Anything left as an original character set is unprintable. */
2656 if (DIRECT_CHAR(attr
)) {
2659 memset(text
, 0xFD, len
);
2663 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2666 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2667 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2668 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2670 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2671 nfont
|= FONT_UNDERLINE
;
2672 another_font(nfont
);
2673 if (!fonts
[nfont
]) {
2674 if (nfont
& FONT_UNDERLINE
)
2675 force_manual_underline
= 1;
2676 /* Don't do the same for manual bold, it could be bad news. */
2678 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2680 another_font(nfont
);
2682 nfont
= FONT_NORMAL
;
2683 if (attr
& ATTR_REVERSE
) {
2688 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2690 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2694 SelectObject(hdc
, fonts
[nfont
]);
2695 SetTextColor(hdc
, fg
);
2696 SetBkColor(hdc
, bg
);
2697 SetBkMode(hdc
, OPAQUE
);
2700 line_box
.right
= x
+ char_width
* len
;
2701 line_box
.bottom
= y
+ font_height
;
2703 /* Only want the left half of double width lines */
2704 if (line_box
.right
> font_width
*cols
+offset_width
)
2705 line_box
.right
= font_width
*cols
+offset_width
;
2707 /* We're using a private area for direct to font. (512 chars.) */
2708 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2709 /* Ho Hum, dbcs fonts are a PITA! */
2710 /* To display on W9x I have to convert to UCS */
2711 static wchar_t *uni_buf
= 0;
2712 static int uni_len
= 0;
2714 if (len
> uni_len
) {
2716 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2719 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2720 uni_buf
[nlen
] = 0xFFFD;
2721 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2722 IpDx
[nlen
] += char_width
;
2723 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2724 text
+mptr
, 2, uni_buf
+nlen
, 1);
2729 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2730 text
+mptr
, 1, uni_buf
+nlen
, 1);
2738 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2739 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2740 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2741 SetBkMode(hdc
, TRANSPARENT
);
2742 ExtTextOutW(hdc
, x
- 1,
2743 y
- font_height
* (lattr
==
2744 LATTR_BOT
) + text_adjust
,
2745 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2749 } else if (DIRECT_FONT(attr
)) {
2751 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2752 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2753 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2754 SetBkMode(hdc
, TRANSPARENT
);
2756 /* GRR: This draws the character outside it's box and can leave
2757 * 'droppings' even with the clip box! I suppose I could loop it
2758 * one character at a time ... yuk.
2760 * Or ... I could do a test print with "W", and use +1 or -1 for this
2761 * shift depending on if the leftmost column is blank...
2763 ExtTextOut(hdc
, x
- 1,
2764 y
- font_height
* (lattr
==
2765 LATTR_BOT
) + text_adjust
,
2766 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2769 /* And 'normal' unicode characters */
2770 static WCHAR
*wbuf
= NULL
;
2771 static int wlen
= 0;
2776 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2778 for (i
= 0; i
< len
; i
++)
2779 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2782 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2783 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2785 /* And the shadow bold hack. */
2786 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2787 SetBkMode(hdc
, TRANSPARENT
);
2788 ExtTextOutW(hdc
, x
- 1,
2789 y
- font_height
* (lattr
==
2790 LATTR_BOT
) + text_adjust
,
2791 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2794 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2795 (und_mode
== UND_LINE
2796 && (attr
& ATTR_UNDER
)))) {
2799 if (lattr
== LATTR_BOT
)
2800 dec
= dec
* 2 - font_height
;
2802 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2803 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2804 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2805 oldpen
= SelectObject(hdc
, oldpen
);
2806 DeleteObject(oldpen
);
2810 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2811 unsigned long attr
, int lattr
)
2817 int ctype
= cfg
.cursor_type
;
2819 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2820 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2821 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2825 attr
|= TATTR_RIGHTCURS
;
2828 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2829 if (attr
& ATTR_WIDE
)
2836 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2839 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2840 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2841 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2842 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2843 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2844 Polyline(hdc
, pts
, 5);
2845 oldpen
= SelectObject(hdc
, oldpen
);
2846 DeleteObject(oldpen
);
2847 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2848 int startx
, starty
, dx
, dy
, length
, i
;
2851 starty
= y
+ descent
;
2854 length
= char_width
;
2857 if (attr
& TATTR_RIGHTCURS
)
2858 xadjust
= char_width
- 1;
2859 startx
= x
+ xadjust
;
2863 length
= font_height
;
2865 if (attr
& TATTR_ACTCURS
) {
2868 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2869 MoveToEx(hdc
, startx
, starty
, NULL
);
2870 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2871 oldpen
= SelectObject(hdc
, oldpen
);
2872 DeleteObject(oldpen
);
2874 for (i
= 0; i
< length
; i
++) {
2876 SetPixel(hdc
, startx
, starty
, colours
[23]);
2885 /* This function gets the actual width of a character in the normal font.
2887 int CharWidth(Context ctx
, int uc
) {
2891 /* If the font max is the same as the font ave width then this
2892 * function is a no-op.
2894 if (!font_dualwidth
) return 1;
2896 switch (uc
& CSET_MASK
) {
2898 uc
= unitab_line
[uc
& 0xFF];
2901 uc
= unitab_xterm
[uc
& 0xFF];
2904 uc
= unitab_scoacs
[uc
& 0xFF];
2907 if (DIRECT_FONT(uc
)) {
2908 if (dbcs_screenfont
) return 1;
2910 /* Speedup, I know of no font where ascii is the wrong width */
2911 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
2914 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
2915 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2916 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
2917 another_font(FONT_OEM
);
2918 if (!fonts
[FONT_OEM
]) return 0;
2920 SelectObject(hdc
, fonts
[FONT_OEM
]);
2924 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
2925 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
2928 /* Speedup, I know of no font where ascii is the wrong width */
2929 if (uc
>= ' ' && uc
<= '~') return 1;
2931 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2932 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
2933 /* Okay that one worked */ ;
2934 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
2935 /* This should work on 9x too, but it's "less accurate" */ ;
2940 ibuf
+= font_width
/ 2 -1;
2947 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2948 * codes. Returns number of bytes used or zero to drop the message
2949 * or -1 to forward the message to windows.
2951 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2952 unsigned char *output
)
2955 int scan
, left_alt
= 0, key_down
, shift_state
;
2957 unsigned char *p
= output
;
2958 static int alt_sum
= 0;
2960 HKL kbd_layout
= GetKeyboardLayout(0);
2962 static WORD keys
[3];
2963 static int compose_char
= 0;
2964 static WPARAM compose_key
= 0;
2966 r
= GetKeyboardState(keystate
);
2968 memset(keystate
, 0, sizeof(keystate
));
2971 #define SHOW_TOASCII_RESULT
2972 { /* Tell us all about key events */
2973 static BYTE oldstate
[256];
2974 static int first
= 1;
2978 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2981 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2983 } else if ((HIWORD(lParam
) & KF_UP
)
2984 && scan
== (HIWORD(lParam
) & 0xFF)) {
2988 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2989 debug(("K_F%d", wParam
+ 1 - VK_F1
));
3002 debug(("VK_%02x", wParam
));
3004 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
3006 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
3008 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
3009 if (ch
>= ' ' && ch
<= '~')
3010 debug((", '%c'", ch
));
3012 debug((", $%02x", ch
));
3015 debug((", KB0=%02x", keys
[0]));
3017 debug((", KB1=%02x", keys
[1]));
3019 debug((", KB2=%02x", keys
[2]));
3021 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
3023 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
3025 if ((HIWORD(lParam
) & KF_EXTENDED
))
3027 if ((HIWORD(lParam
) & KF_UP
))
3031 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
3032 else if ((HIWORD(lParam
) & KF_UP
))
3033 oldstate
[wParam
& 0xFF] ^= 0x80;
3035 oldstate
[wParam
& 0xFF] ^= 0x81;
3037 for (ch
= 0; ch
< 256; ch
++)
3038 if (oldstate
[ch
] != keystate
[ch
])
3039 debug((", M%02x=%02x", ch
, keystate
[ch
]));
3041 memcpy(oldstate
, keystate
, sizeof(oldstate
));
3045 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
3046 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
3050 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
3051 if ((cfg
.funky_type
== 3 ||
3052 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
3053 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
3055 wParam
= VK_EXECUTE
;
3057 /* UnToggle NUMLock */
3058 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
3059 keystate
[VK_NUMLOCK
] ^= 1;
3062 /* And write back the 'adjusted' state */
3063 SetKeyboardState(keystate
);
3066 /* Disable Auto repeat if required */
3067 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
3070 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
3073 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
3075 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
3076 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
3077 if (cfg
.ctrlaltkeys
)
3078 keystate
[VK_MENU
] = 0;
3080 keystate
[VK_RMENU
] = 0x80;
3085 alt_pressed
= (left_alt
&& key_down
);
3087 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
3088 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
3089 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
3091 /* Note if AltGr was pressed and if it was used as a compose key */
3092 if (!compose_state
) {
3093 compose_key
= 0x100;
3094 if (cfg
.compose_key
) {
3095 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
3096 compose_key
= wParam
;
3098 if (wParam
== VK_APPS
)
3099 compose_key
= wParam
;
3102 if (wParam
== compose_key
) {
3103 if (compose_state
== 0
3104 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
3106 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
3110 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
3114 * Record that we pressed key so the scroll window can be reset, but
3115 * be careful to avoid Shift-UP/Down
3117 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
&&
3118 wParam
!= VK_MENU
&& wParam
!= VK_CONTROL
) {
3122 if (compose_state
> 1 && left_alt
)
3125 /* Sanitize the number pad if not using a PC NumPad */
3126 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
3127 && cfg
.funky_type
!= 2)
3128 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
3129 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
3133 nParam
= VK_NUMPAD0
;
3136 nParam
= VK_NUMPAD1
;
3139 nParam
= VK_NUMPAD2
;
3142 nParam
= VK_NUMPAD3
;
3145 nParam
= VK_NUMPAD4
;
3148 nParam
= VK_NUMPAD5
;
3151 nParam
= VK_NUMPAD6
;
3154 nParam
= VK_NUMPAD7
;
3157 nParam
= VK_NUMPAD8
;
3160 nParam
= VK_NUMPAD9
;
3163 nParam
= VK_DECIMAL
;
3167 if (keystate
[VK_NUMLOCK
] & 1)
3174 /* If a key is pressed and AltGr is not active */
3175 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
3176 /* Okay, prepare for most alts then ... */
3180 /* Lets see if it's a pattern we know all about ... */
3181 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
3182 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
3185 if (wParam
== VK_NEXT
&& shift_state
== 1) {
3186 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
3189 if (wParam
== VK_INSERT
&& shift_state
== 1) {
3193 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
3196 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
3197 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
3200 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
&&
3201 (cfg
.resize_action
!= RESIZE_DISABLED
)) {
3202 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) != KF_REPEAT
)
3206 /* Control-Numlock for app-keypad mode switch */
3207 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
3208 app_keypad_keys
^= 1;
3212 /* Nethack keypad */
3213 if (cfg
.nethack_keypad
&& !left_alt
) {
3216 *p
++ = shift_state ?
'B' : 'b';
3219 *p
++ = shift_state ?
'J' : 'j';
3222 *p
++ = shift_state ?
'N' : 'n';
3225 *p
++ = shift_state ?
'H' : 'h';
3228 *p
++ = shift_state ?
'.' : '.';
3231 *p
++ = shift_state ?
'L' : 'l';
3234 *p
++ = shift_state ?
'Y' : 'y';
3237 *p
++ = shift_state ?
'K' : 'k';
3240 *p
++ = shift_state ?
'U' : 'u';
3245 /* Application Keypad */
3249 if (cfg
.funky_type
== 3 ||
3250 (cfg
.funky_type
<= 1 &&
3251 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3265 if (app_keypad_keys
&& !cfg
.no_applic_k
)
3302 if (cfg
.funky_type
== 2) {
3307 } else if (shift_state
)
3314 if (cfg
.funky_type
== 2)
3318 if (cfg
.funky_type
== 2)
3322 if (cfg
.funky_type
== 2)
3327 if (HIWORD(lParam
) & KF_EXTENDED
)
3333 if (xkey
>= 'P' && xkey
<= 'S')
3334 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3336 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3338 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3343 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3344 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3348 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3354 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3358 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3362 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3367 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3372 /* Control-2 to Control-8 are special */
3373 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3374 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3377 if (shift_state
== 2 && wParam
== 0xBD) {
3381 if (shift_state
== 2 && wParam
== 0xDF) {
3385 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
3392 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3393 * for integer decimal nn.)
3395 * We also deal with the weird ones here. Linux VCs replace F1
3396 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3397 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3403 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3406 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3409 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3412 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3415 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3418 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3421 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3424 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3427 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3430 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3463 if ((shift_state
&2) == 0) switch (wParam
) {
3483 /* Reorder edit keys to physical order */
3484 if (cfg
.funky_type
== 3 && code
<= 6)
3485 code
= "\0\2\1\4\5\3\6"[code
];
3487 if (vt52_mode
&& code
> 0 && code
<= 6) {
3488 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3492 if (cfg
.funky_type
== 5 && /* SCO function keys */
3493 code
>= 11 && code
<= 34) {
3494 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3497 case VK_F1
: index
= 0; break;
3498 case VK_F2
: index
= 1; break;
3499 case VK_F3
: index
= 2; break;
3500 case VK_F4
: index
= 3; break;
3501 case VK_F5
: index
= 4; break;
3502 case VK_F6
: index
= 5; break;
3503 case VK_F7
: index
= 6; break;
3504 case VK_F8
: index
= 7; break;
3505 case VK_F9
: index
= 8; break;
3506 case VK_F10
: index
= 9; break;
3507 case VK_F11
: index
= 10; break;
3508 case VK_F12
: index
= 11; break;
3510 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3511 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3512 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3515 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3516 code
>= 1 && code
<= 6) {
3517 char codes
[] = "HL.FIG";
3521 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3525 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3532 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3535 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3538 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3539 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3542 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3544 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3546 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3549 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3550 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3554 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3559 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3560 * some reason seems to send VK_CLEAR to Windows...).
3583 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3585 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
3588 * RDB: VT100 & VT102 manuals both state the
3589 * app cursor keys only work if the app keypad
3592 * SGT: That may well be true, but xterm
3593 * disagrees and so does at least one
3594 * application, so I've #if'ed this out and the
3595 * behaviour is back to PuTTY's original: app
3596 * cursor and app keypad are independently
3597 * switchable modes. If anyone complains about
3598 * _this_ I'll have to put in a configurable
3601 if (!app_keypad_keys
)
3604 /* Useful mapping of Ctrl-arrows */
3605 if (shift_state
== 2)
3609 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3611 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3618 * Finally, deal with Return ourselves. (Win95 seems to
3619 * foul it up when Alt is pressed, for some reason.)
3621 if (wParam
== VK_RETURN
) { /* Return */
3627 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3628 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3633 /* Okay we've done everything interesting; let windows deal with
3634 * the boring stuff */
3638 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3639 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3641 keystate
[VK_CAPITAL
] = 0;
3644 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3645 #ifdef SHOW_TOASCII_RESULT
3646 if (r
== 1 && !key_down
) {
3648 if (in_utf
|| dbcs_screenfont
)
3649 debug((", (U+%04x)", alt_sum
));
3651 debug((", LCH(%d)", alt_sum
));
3653 debug((", ACH(%d)", keys
[0]));
3658 for (r1
= 0; r1
< r
; r1
++) {
3659 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3668 * Interrupt an ongoing paste. I'm not sure this is
3669 * sensible, but for the moment it's preferable to
3670 * having to faff about buffering things.
3675 for (i
= 0; i
< r
; i
++) {
3676 unsigned char ch
= (unsigned char) keys
[i
];
3678 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3683 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3687 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3688 MessageBeep(MB_ICONHAND
);
3692 luni_send(&keybuf
, 1, 1);
3700 if (in_utf
|| dbcs_screenfont
) {
3702 luni_send(&keybuf
, 1, 1);
3704 ch
= (char) alt_sum
;
3706 * We need not bother about stdin
3707 * backlogs here, because in GUI PuTTY
3708 * we can't do anything about it
3709 * anyway; there's no means of asking
3710 * Windows to hold off on KEYDOWN
3711 * messages. We _have_ to buffer
3712 * everything we're sent.
3714 ldisc_send(&ch
, 1, 1);
3718 lpage_send(kbd_codepage
, &ch
, 1, 1);
3720 if(capsOn
&& ch
< 0x80) {
3723 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3724 luni_send(cbuf
+!left_alt
, 1+!!left_alt
, 1);
3729 lpage_send(kbd_codepage
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3735 /* This is so the ALT-Numpad and dead keys work correctly. */
3740 /* If we're definitly not building up an ALT-54321 then clear it */
3743 /* If we will be using alt_sum fix the 256s */
3744 else if (keys
[0] && (in_utf
|| dbcs_screenfont
))
3749 * ALT alone may or may not want to bring up the System menu.
3750 * If it's not meant to, we return 0 on presses or releases of
3751 * ALT, to show that we've swallowed the keystroke. Otherwise
3752 * we return -1, which means Windows will give the keystroke
3753 * its default handling (i.e. bring up the System menu).
3755 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3761 void set_title(char *title
)
3764 window_name
= smalloc(1 + strlen(title
));
3765 strcpy(window_name
, title
);
3766 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3767 SetWindowText(hwnd
, title
);
3770 void set_icon(char *title
)
3773 icon_name
= smalloc(1 + strlen(title
));
3774 strcpy(icon_name
, title
);
3775 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3776 SetWindowText(hwnd
, title
);
3779 void set_sbar(int total
, int start
, int page
)
3783 if (is_full_screen() ?
!cfg
.scrollbar_in_fullscreen
: !cfg
.scrollbar
)
3786 si
.cbSize
= sizeof(si
);
3787 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3789 si
.nMax
= total
- 1;
3793 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3796 Context
get_ctx(void)
3802 SelectPalette(hdc
, pal
, FALSE
);
3808 void free_ctx(Context ctx
)
3810 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3811 ReleaseDC(hwnd
, ctx
);
3814 static void real_palette_set(int n
, int r
, int g
, int b
)
3817 logpal
->palPalEntry
[n
].peRed
= r
;
3818 logpal
->palPalEntry
[n
].peGreen
= g
;
3819 logpal
->palPalEntry
[n
].peBlue
= b
;
3820 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3821 colours
[n
] = PALETTERGB(r
, g
, b
);
3822 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3824 colours
[n
] = RGB(r
, g
, b
);
3827 void palette_set(int n
, int r
, int g
, int b
)
3829 static const int first
[21] = {
3830 0, 2, 4, 6, 8, 10, 12, 14,
3831 1, 3, 5, 7, 9, 11, 13, 15,
3834 real_palette_set(first
[n
], r
, g
, b
);
3836 real_palette_set(first
[n
] + 1, r
, g
, b
);
3838 HDC hdc
= get_ctx();
3839 UnrealizeObject(pal
);
3840 RealizePalette(hdc
);
3845 void palette_reset(void)
3849 for (i
= 0; i
< NCOLOURS
; i
++) {
3851 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3852 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3853 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3854 logpal
->palPalEntry
[i
].peFlags
= 0;
3855 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3856 defpal
[i
].rgbtGreen
,
3857 defpal
[i
].rgbtBlue
);
3859 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3860 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3865 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3867 RealizePalette(hdc
);
3872 void write_aclip(char *data
, int len
, int must_deselect
)
3877 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3880 lock
= GlobalLock(clipdata
);
3883 memcpy(lock
, data
, len
);
3884 ((unsigned char *) lock
)[len
] = 0;
3885 GlobalUnlock(clipdata
);
3888 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3890 if (OpenClipboard(hwnd
)) {
3892 SetClipboardData(CF_TEXT
, clipdata
);
3895 GlobalFree(clipdata
);
3898 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3902 * Note: unlike write_aclip() this will not append a nul.
3904 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3906 HGLOBAL clipdata
, clipdata2
, clipdata3
;
3908 void *lock
, *lock2
, *lock3
;
3910 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3912 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3913 len
* sizeof(wchar_t));
3914 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3916 if (!clipdata
|| !clipdata2
) {
3918 GlobalFree(clipdata
);
3920 GlobalFree(clipdata2
);
3923 if (!(lock
= GlobalLock(clipdata
)))
3925 if (!(lock2
= GlobalLock(clipdata2
)))
3928 memcpy(lock
, data
, len
* sizeof(wchar_t));
3929 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3931 if (cfg
.rtf_paste
) {
3932 wchar_t unitab
[256];
3934 unsigned char *tdata
= (unsigned char *)lock2
;
3935 wchar_t *udata
= (wchar_t *)lock
;
3936 int rtflen
= 0, uindex
= 0, tindex
= 0;
3938 int multilen
, blen
, alen
, totallen
, i
;
3939 char before
[16], after
[4];
3941 get_unitab(CP_ACP
, unitab
, 0);
3943 rtfsize
= 100 + strlen(cfg
.font
);
3944 rtf
= smalloc(rtfsize
);
3945 sprintf(rtf
, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3946 GetACP(), cfg
.font
);
3947 rtflen
= strlen(rtf
);
3950 * We want to construct a piece of RTF that specifies the
3951 * same Unicode text. To do this we will read back in
3952 * parallel from the Unicode data in `udata' and the
3953 * non-Unicode data in `tdata'. For each character in
3954 * `tdata' which becomes the right thing in `udata' when
3955 * looked up in `unitab', we just copy straight over from
3956 * tdata. For each one that doesn't, we must WCToMB it
3957 * individually and produce a \u escape sequence.
3959 * It would probably be more robust to just bite the bullet
3960 * and WCToMB each individual Unicode character one by one,
3961 * then MBToWC each one back to see if it was an accurate
3962 * translation; but that strikes me as a horrifying number
3963 * of Windows API calls so I want to see if this faster way
3964 * will work. If it screws up badly we can always revert to
3965 * the simple and slow way.
3967 while (tindex
< len2
&& uindex
< len
&&
3968 tdata
[tindex
] && udata
[uindex
]) {
3969 if (tindex
+ 1 < len2
&&
3970 tdata
[tindex
] == '\r' &&
3971 tdata
[tindex
+1] == '\n') {
3975 if (unitab
[tdata
[tindex
]] == udata
[uindex
]) {
3981 multilen
= WideCharToMultiByte(CP_ACP
, 0, unitab
+uindex
, 1,
3982 NULL
, 0, NULL
, NULL
);
3983 if (multilen
!= 1) {
3984 blen
= sprintf(before
, "{\\uc%d\\u%d", multilen
,
3986 alen
= 1; strcpy(after
, "}");
3988 blen
= sprintf(before
, "\\u%d", udata
[uindex
]);
3989 alen
= 0; after
[0] = '\0';
3992 assert(tindex
+ multilen
<= len2
);
3993 totallen
= blen
+ alen
;
3994 for (i
= 0; i
< multilen
; i
++) {
3995 if (tdata
[tindex
+i
] == '\\' ||
3996 tdata
[tindex
+i
] == '{' ||
3997 tdata
[tindex
+i
] == '}')
3999 else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A)
4000 totallen
+= 6; /* \par\r\n */
4001 else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20)
4007 if (rtfsize
< rtflen
+ totallen
+ 3) {
4008 rtfsize
= rtflen
+ totallen
+ 512;
4009 rtf
= srealloc(rtf
, rtfsize
);
4012 strcpy(rtf
+ rtflen
, before
); rtflen
+= blen
;
4013 for (i
= 0; i
< multilen
; i
++) {
4014 if (tdata
[tindex
+i
] == '\\' ||
4015 tdata
[tindex
+i
] == '{' ||
4016 tdata
[tindex
+i
] == '}') {
4017 rtf
[rtflen
++] = '\\';
4018 rtf
[rtflen
++] = tdata
[tindex
+i
];
4019 } else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A) {
4020 rtflen
+= sprintf(rtf
+rtflen
, "\\par\r\n");
4021 } else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20) {
4022 rtflen
+= sprintf(rtf
+rtflen
, "\\'%02x", tdata
[tindex
+i
]);
4024 rtf
[rtflen
++] = tdata
[tindex
+i
];
4027 strcpy(rtf
+ rtflen
, after
); rtflen
+= alen
;
4033 strcpy(rtf
+ rtflen
, "}");
4036 clipdata3
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, rtflen
);
4037 if (clipdata3
&& (lock3
= GlobalLock(clipdata3
)) != NULL
) {
4039 GlobalUnlock(clipdata3
);
4045 GlobalUnlock(clipdata
);
4046 GlobalUnlock(clipdata2
);
4049 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
4051 if (OpenClipboard(hwnd
)) {
4053 SetClipboardData(CF_UNICODETEXT
, clipdata
);
4054 SetClipboardData(CF_TEXT
, clipdata2
);
4056 SetClipboardData(RegisterClipboardFormat(CF_RTF
), clipdata3
);
4059 GlobalFree(clipdata
);
4060 GlobalFree(clipdata2
);
4064 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
4067 void get_clip(wchar_t ** p
, int *len
)
4069 static HGLOBAL clipdata
= NULL
;
4070 static wchar_t *converted
= 0;
4079 GlobalUnlock(clipdata
);
4082 } else if (OpenClipboard(NULL
)) {
4083 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
4085 *p
= GlobalLock(clipdata
);
4087 for (p2
= *p
; *p2
; p2
++);
4091 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
4095 s
= GlobalLock(clipdata
);
4096 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
4097 *p
= converted
= smalloc(i
* sizeof(wchar_t));
4098 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
4111 * Move `lines' lines from position `from' to position `to' in the
4114 void optimised_move(int to
, int from
, int lines
)
4119 min
= (to
< from ? to
: from
);
4120 max
= to
+ from
- min
;
4122 r
.left
= offset_width
;
4123 r
.right
= offset_width
+ cols
* font_width
;
4124 r
.top
= offset_height
+ min
* font_height
;
4125 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
4126 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
4131 * Print a message box and perform a fatal exit.
4133 void fatalbox(char *fmt
, ...)
4139 vsprintf(stuff
, fmt
, ap
);
4141 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
4146 * Manage window caption / taskbar flashing, if enabled.
4147 * 0 = stop, 1 = maintain, 2 = start
4149 static void flash_window(int mode
)
4151 static long last_flash
= 0;
4152 static int flashing
= 0;
4153 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
4156 FlashWindow(hwnd
, FALSE
);
4160 } else if (mode
== 2) {
4163 last_flash
= GetTickCount();
4165 FlashWindow(hwnd
, TRUE
);
4168 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
4171 long now
= GetTickCount();
4172 long fdiff
= now
- last_flash
;
4173 if (fdiff
< 0 || fdiff
> 450) {
4175 FlashWindow(hwnd
, TRUE
); /* toggle */
4186 if (mode
== BELL_DEFAULT
) {
4188 * For MessageBeep style bells, we want to be careful of
4189 * timing, because they don't have the nice property of
4190 * PlaySound bells that each one cancels the previous
4191 * active one. So we limit the rate to one per 50ms or so.
4193 static long lastbeep
= 0;
4196 beepdiff
= GetTickCount() - lastbeep
;
4197 if (beepdiff
>= 0 && beepdiff
< 50)
4201 * The above MessageBeep call takes time, so we record the
4202 * time _after_ it finishes rather than before it starts.
4204 lastbeep
= GetTickCount();
4205 } else if (mode
== BELL_WAVEFILE
) {
4206 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
4207 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
4208 sprintf(buf
, "Unable to play sound file\n%s\n"
4209 "Using default sound instead", cfg
.bell_wavefile
);
4210 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
4211 MB_OK
| MB_ICONEXCLAMATION
);
4212 cfg
.beep
= BELL_DEFAULT
;
4215 /* Otherwise, either visual bell or disabled; do nothing here */
4217 flash_window(2); /* start */
4222 * Minimise or restore the window in response to a server-side
4225 void set_iconic(int iconic
)
4227 if (IsIconic(hwnd
)) {
4229 ShowWindow(hwnd
, SW_RESTORE
);
4232 ShowWindow(hwnd
, SW_MINIMIZE
);
4237 * Move the window in response to a server-side request.
4239 void move_window(int x
, int y
)
4241 if (cfg
.resize_action
== RESIZE_DISABLED
||
4242 cfg
.resize_action
== RESIZE_FONT
||
4246 SetWindowPos(hwnd
, NULL
, x
, y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
);
4250 * Move the window to the top or bottom of the z-order in response
4251 * to a server-side request.
4253 void set_zorder(int top
)
4255 if (cfg
.alwaysontop
)
4256 return; /* ignore */
4257 SetWindowPos(hwnd
, top ? HWND_TOP
: HWND_BOTTOM
, 0, 0, 0, 0,
4258 SWP_NOMOVE
| SWP_NOSIZE
);
4262 * Refresh the window in response to a server-side request.
4264 void refresh_window(void)
4266 InvalidateRect(hwnd
, NULL
, TRUE
);
4270 * Maximise or restore the window in response to a server-side
4273 void set_zoomed(int zoomed
)
4275 if (IsZoomed(hwnd
)) {
4277 ShowWindow(hwnd
, SW_RESTORE
);
4280 ShowWindow(hwnd
, SW_MAXIMIZE
);
4285 * Report whether the window is iconic, for terminal reports.
4289 return IsIconic(hwnd
);
4293 * Report the window's position, for terminal reports.
4295 void get_window_pos(int *x
, int *y
)
4298 GetWindowRect(hwnd
, &r
);
4304 * Report the window's pixel size, for terminal reports.
4306 void get_window_pixels(int *x
, int *y
)
4309 GetWindowRect(hwnd
, &r
);
4310 *x
= r
.right
- r
.left
;
4311 *y
= r
.bottom
- r
.top
;
4315 * Return the window or icon title.
4317 char *get_window_title(int icon
)
4319 return icon ? icon_name
: window_name
;
4323 * See if we're in full-screen mode.
4325 int is_full_screen()
4327 if (!IsZoomed(hwnd
))
4329 if (GetWindowLong(hwnd
, GWL_STYLE
) & WS_CAPTION
)
4335 * Go full-screen. This should only be called when we are already
4338 void make_full_screen()
4343 assert(IsZoomed(hwnd
));
4345 /* Remove the window furniture. */
4346 style
= GetWindowLong(hwnd
, GWL_STYLE
);
4347 style
&= ~(WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
);
4348 if (cfg
.scrollbar_in_fullscreen
)
4349 style
|= WS_VSCROLL
;
4351 style
&= ~WS_VSCROLL
;
4352 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4354 /* Resize ourselves to exactly cover the nearest monitor. */
4355 #ifdef MONITOR_DEFAULTTONEAREST
4359 mon
= MonitorFromWindow(hwnd
, MONITOR_DEFAULTTONEAREST
);
4360 mi
.cbSize
= sizeof(mi
);
4361 GetMonitorInfo(mon
, &mi
);
4362 x
= mi
.rcMonitor
.left
;
4363 y
= mi
.rcMonitor
.top
;
4364 w
= mi
.rcMonitor
.right
;
4365 h
= mi
.rcMonitor
.bottom
;
4369 w
= GetSystemMetrics(SM_CXSCREEN
);
4370 h
= GetSystemMetrics(SM_CYSCREEN
);
4372 SetWindowPos(hwnd
, HWND_TOP
, x
, y
, w
, h
, SWP_FRAMECHANGED
);
4374 /* Tick the menu item in the System menu. */
4375 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4380 * Clear the full-screen attributes.
4382 void clear_full_screen()
4384 DWORD oldstyle
, style
;
4386 /* Reinstate the window furniture. */
4387 style
= oldstyle
= GetWindowLong(hwnd
, GWL_STYLE
);
4388 style
|= WS_CAPTION
| WS_BORDER
;
4389 if (cfg
.resize_action
== RESIZE_DISABLED
)
4390 style
&= ~WS_THICKFRAME
;
4392 style
|= WS_THICKFRAME
;
4394 style
|= WS_VSCROLL
;
4396 style
&= ~WS_VSCROLL
;
4397 if (style
!= oldstyle
) {
4398 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4399 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
4400 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
4404 /* Untick the menu item in the System menu. */
4405 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4410 * Toggle full-screen mode.
4412 void flip_full_screen()
4414 if (is_full_screen()) {
4415 ShowWindow(hwnd
, SW_RESTORE
);
4416 } else if (IsZoomed(hwnd
)) {
4419 SendMessage(hwnd
, WM_FULLSCR_ON_MAX
, 0, 0);
4420 ShowWindow(hwnd
, SW_MAXIMIZE
);