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
);
726 if (GetCapture() != hwnd
)
731 flash_window(1); /* maintain */
733 /* The messages seem unreliable; especially if we're being tricky */
734 has_focus
= (GetForegroundWindow() == hwnd
);
737 /* Hmm, term_update didn't want to do an update too soon ... */
738 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
740 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
742 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
745 /* There's no point rescanning everything in the message queue
746 * so we do an apparently unnecessary wait here
749 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
763 if (cfg
.protocol
== PROT_SSH
) {
774 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
776 char *do_select(SOCKET skt
, int startup
)
781 events
= (FD_CONNECT
| FD_READ
| FD_WRITE
|
782 FD_OOB
| FD_CLOSE
| FD_ACCEPT
);
787 return "do_select(): internal error (hwnd==NULL)";
788 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
789 switch (WSAGetLastError()) {
791 return "Network is down";
793 return "WSAAsyncSelect(): unknown error";
800 * set or clear the "raw mouse message" mode
802 void set_raw_mouse_mode(int activate
)
804 send_raw_mouse
= activate
;
805 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
809 * Print a message box and close the connection.
811 void connection_fatal(char *fmt
, ...)
817 vsprintf(stuff
, fmt
, ap
);
819 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
820 if (cfg
.close_on_exit
== COE_ALWAYS
)
823 session_closed
= TRUE
;
824 SetWindowText(hwnd
, "PuTTY (inactive)");
829 * Actually do the job requested by a WM_NETEVENT
831 static void enact_pending_netevent(void)
833 static int reentering
= 0;
834 extern int select_result(WPARAM
, LPARAM
);
838 return; /* don't unpend the pending */
840 pending_netevent
= FALSE
;
843 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
846 if (ret
== 0 && !session_closed
) {
847 /* Abnormal exits will already have set session_closed and taken
848 * appropriate action. */
849 if (cfg
.close_on_exit
== COE_ALWAYS
||
850 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
852 session_closed
= TRUE
;
853 SetWindowText(hwnd
, "PuTTY (inactive)");
854 MessageBox(hwnd
, "Connection closed by remote host",
855 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
861 * Copy the colour palette from the configuration data into defpal.
862 * This is non-trivial because the colour indices are different.
864 static void cfgtopalette(void)
867 static const int ww
[] = {
868 6, 7, 8, 9, 10, 11, 12, 13,
869 14, 15, 16, 17, 18, 19, 20, 21,
870 0, 1, 2, 3, 4, 4, 5, 5
873 for (i
= 0; i
< 24; i
++) {
875 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
876 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
877 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
882 * Set up the colour palette.
884 static void init_palette(void)
887 HDC hdc
= GetDC(hwnd
);
889 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
890 logpal
= smalloc(sizeof(*logpal
)
891 - sizeof(logpal
->palPalEntry
)
892 + NCOLOURS
* sizeof(PALETTEENTRY
));
893 logpal
->palVersion
= 0x300;
894 logpal
->palNumEntries
= NCOLOURS
;
895 for (i
= 0; i
< NCOLOURS
; i
++) {
896 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
897 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
898 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
899 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
901 pal
= CreatePalette(logpal
);
903 SelectPalette(hdc
, pal
, FALSE
);
905 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
908 ReleaseDC(hwnd
, hdc
);
911 for (i
= 0; i
< NCOLOURS
; i
++)
912 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
916 for (i
= 0; i
< NCOLOURS
; i
++)
917 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
918 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
922 * Initialise all the fonts we will need initially. There may be as many as
923 * three or as few as one. The other (poentially) twentyone fonts are done
924 * if/when they are needed.
928 * - check the font width and height, correcting our guesses if
931 * - verify that the bold font is the same width as the ordinary
932 * one, and engage shadow bolding if not.
934 * - verify that the underlined font is the same width as the
935 * ordinary one (manual underlining by means of line drawing can
936 * be done in a pinch).
938 static void init_fonts(int pick_width
, int pick_height
)
945 int fw_dontcare
, fw_bold
;
947 for (i
= 0; i
< FONT_MAXNO
; i
++)
950 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
953 if (cfg
.fontisbold
) {
954 fw_dontcare
= FW_BOLD
;
957 fw_dontcare
= FW_DONTCARE
;
964 font_height
= pick_height
;
966 font_height
= cfg
.fontheight
;
967 if (font_height
> 0) {
969 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
972 font_width
= pick_width
;
975 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
976 c, OUT_DEFAULT_PRECIS, \
977 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
978 FIXED_PITCH | FF_DONTCARE, cfg.font)
980 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
982 lfont
.lfHeight
= font_height
;
983 lfont
.lfWidth
= font_width
;
984 lfont
.lfEscapement
= 0;
985 lfont
.lfOrientation
= 0;
986 lfont
.lfWeight
= fw_dontcare
;
987 lfont
.lfItalic
= FALSE
;
988 lfont
.lfUnderline
= FALSE
;
989 lfont
.lfStrikeOut
= FALSE
;
990 lfont
.lfCharSet
= cfg
.fontcharset
;
991 lfont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
992 lfont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
993 lfont
.lfQuality
= DEFAULT_QUALITY
;
994 lfont
.lfPitchAndFamily
= FIXED_PITCH
| FF_DONTCARE
;
995 strncpy(lfont
.lfFaceName
, cfg
.font
, LF_FACESIZE
);
997 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
998 GetTextMetrics(hdc
, &tm
);
1000 if (pick_width
== 0 || pick_height
== 0) {
1001 font_height
= tm
.tmHeight
;
1002 font_width
= tm
.tmAveCharWidth
;
1004 font_dualwidth
= (tm
.tmAveCharWidth
!= tm
.tmMaxCharWidth
);
1006 #ifdef RDB_DEBUG_PATCH
1007 debug(23, "Primary font H=%d, AW=%d, MW=%d",
1008 tm
.tmHeight
, tm
.tmAveCharWidth
, tm
.tmMaxCharWidth
);
1013 DWORD cset
= tm
.tmCharSet
;
1014 memset(&info
, 0xFF, sizeof(info
));
1016 /* !!! Yes the next line is right */
1017 if (cset
== OEM_CHARSET
)
1018 font_codepage
= GetOEMCP();
1020 if (TranslateCharsetInfo
1021 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
1026 GetCPInfo(font_codepage
, &cpinfo
);
1027 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
1030 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
1033 * Some fonts, e.g. 9-pt Courier, draw their underlines
1034 * outside their character cell. We successfully prevent
1035 * screen corruption by clipping the text output, but then
1036 * we lose the underline completely. Here we try to work
1037 * out whether this is such a font, and if it is, we set a
1038 * flag that causes underlines to be drawn by hand.
1040 * Having tried other more sophisticated approaches (such
1041 * as examining the TEXTMETRIC structure or requesting the
1042 * height of a string), I think we'll do this the brute
1043 * force way: we create a small bitmap, draw an underlined
1044 * space on it, and test to see whether any pixels are
1045 * foreground-coloured. (Since we expect the underline to
1046 * go all the way across the character cell, we only search
1047 * down a single column of the bitmap, half way across.)
1051 HBITMAP und_bm
, und_oldbm
;
1055 und_dc
= CreateCompatibleDC(hdc
);
1056 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
1057 und_oldbm
= SelectObject(und_dc
, und_bm
);
1058 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
1059 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
1060 SetTextColor(und_dc
, RGB(255, 255, 255));
1061 SetBkColor(und_dc
, RGB(0, 0, 0));
1062 SetBkMode(und_dc
, OPAQUE
);
1063 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
1065 for (i
= 0; i
< font_height
; i
++) {
1066 c
= GetPixel(und_dc
, font_width
/ 2, i
);
1067 if (c
!= RGB(0, 0, 0))
1070 SelectObject(und_dc
, und_oldbm
);
1071 DeleteObject(und_bm
);
1074 und_mode
= UND_LINE
;
1075 DeleteObject(fonts
[FONT_UNDERLINE
]);
1076 fonts
[FONT_UNDERLINE
] = 0;
1080 if (bold_mode
== BOLD_FONT
) {
1081 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
1085 descent
= tm
.tmAscent
+ 1;
1086 if (descent
>= font_height
)
1087 descent
= font_height
- 1;
1089 for (i
= 0; i
< 3; i
++) {
1091 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
1092 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
1099 ReleaseDC(hwnd
, hdc
);
1101 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1102 und_mode
= UND_LINE
;
1103 DeleteObject(fonts
[FONT_UNDERLINE
]);
1104 fonts
[FONT_UNDERLINE
] = 0;
1107 if (bold_mode
== BOLD_FONT
&&
1108 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1109 bold_mode
= BOLD_SHADOW
;
1110 DeleteObject(fonts
[FONT_BOLD
]);
1111 fonts
[FONT_BOLD
] = 0;
1113 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1118 static void another_font(int fontno
)
1121 int fw_dontcare
, fw_bold
;
1125 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1128 basefont
= (fontno
& ~(FONT_BOLDUND
));
1129 if (basefont
!= fontno
&& !fontflag
[basefont
])
1130 another_font(basefont
);
1132 if (cfg
.fontisbold
) {
1133 fw_dontcare
= FW_BOLD
;
1136 fw_dontcare
= FW_DONTCARE
;
1140 c
= cfg
.fontcharset
;
1146 if (fontno
& FONT_WIDE
)
1148 if (fontno
& FONT_NARROW
)
1150 if (fontno
& FONT_OEM
)
1152 if (fontno
& FONT_BOLD
)
1154 if (fontno
& FONT_UNDERLINE
)
1158 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1159 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1160 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1161 FIXED_PITCH
| FF_DONTCARE
, s
);
1163 fontflag
[fontno
] = 1;
1166 static void deinit_fonts(void)
1169 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1171 DeleteObject(fonts
[i
]);
1177 void request_resize(int w
, int h
)
1181 /* If the window is maximized supress resizing attempts */
1182 if (IsZoomed(hwnd
)) {
1183 if (cfg
.resize_action
== RESIZE_TERM
)
1187 if (cfg
.resize_action
== RESIZE_DISABLED
) return;
1188 if (h
== rows
&& w
== cols
) return;
1190 /* Sanity checks ... */
1192 static int first_time
= 1;
1195 switch (first_time
) {
1197 /* Get the size of the screen */
1198 if (GetClientRect(GetDesktopWindow(), &ss
))
1199 /* first_time = 0 */ ;
1205 /* Make sure the values are sane */
1206 width
= (ss
.right
- ss
.left
- extra_width
) / 4;
1207 height
= (ss
.bottom
- ss
.top
- extra_height
) / 6;
1209 if (w
> width
|| h
> height
)
1218 term_size(h
, w
, cfg
.savelines
);
1220 if (cfg
.resize_action
!= RESIZE_FONT
&& !IsZoomed(hwnd
)) {
1221 width
= extra_width
+ font_width
* w
;
1222 height
= extra_height
+ font_height
* h
;
1224 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1225 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1226 SWP_NOMOVE
| SWP_NOZORDER
);
1230 InvalidateRect(hwnd
, NULL
, TRUE
);
1233 static void reset_window(int reinit
) {
1235 * This function decides how to resize or redraw when the
1236 * user changes something.
1238 * This function doesn't like to change the terminal size but if the
1239 * font size is locked that may be it's only soluion.
1241 int win_width
, win_height
;
1244 #ifdef RDB_DEBUG_PATCH
1245 debug((27, "reset_window()"));
1248 /* Current window sizes ... */
1249 GetWindowRect(hwnd
, &wr
);
1250 GetClientRect(hwnd
, &cr
);
1252 win_width
= cr
.right
- cr
.left
;
1253 win_height
= cr
.bottom
- cr
.top
;
1255 if (cfg
.resize_action
== RESIZE_DISABLED
) reinit
= 2;
1257 /* Are we being forced to reload the fonts ? */
1259 #ifdef RDB_DEBUG_PATCH
1260 debug((27, "reset_window() -- Forced deinit"));
1266 /* Oh, looks like we're minimised */
1267 if (win_width
== 0 || win_height
== 0)
1270 /* Is the window out of position ? */
1272 (offset_width
!= (win_width
-font_width
*cols
)/2 ||
1273 offset_height
!= (win_height
-font_height
*rows
)/2) ){
1274 offset_width
= (win_width
-font_width
*cols
)/2;
1275 offset_height
= (win_height
-font_height
*rows
)/2;
1276 InvalidateRect(hwnd
, NULL
, TRUE
);
1277 #ifdef RDB_DEBUG_PATCH
1278 debug((27, "reset_window() -> Reposition terminal"));
1282 if (IsZoomed(hwnd
)) {
1283 /* We're fullscreen, this means we must not change the size of
1284 * the window so it's the font size or the terminal itself.
1287 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1288 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1290 if (cfg
.resize_action
!= RESIZE_TERM
) {
1291 if ( font_width
!= win_width
/cols
||
1292 font_height
!= win_height
/rows
) {
1294 init_fonts(win_width
/cols
, win_height
/rows
);
1295 offset_width
= (win_width
-font_width
*cols
)/2;
1296 offset_height
= (win_height
-font_height
*rows
)/2;
1297 InvalidateRect(hwnd
, NULL
, TRUE
);
1298 #ifdef RDB_DEBUG_PATCH
1299 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1300 font_width
, font_height
));
1304 if ( font_width
!= win_width
/cols
||
1305 font_height
!= win_height
/rows
) {
1306 /* Our only choice at this point is to change the
1307 * size of the terminal; Oh well.
1309 term_size( win_height
/font_height
, win_width
/font_width
,
1311 offset_width
= (win_width
-font_width
*cols
)/2;
1312 offset_height
= (win_height
-font_height
*rows
)/2;
1313 InvalidateRect(hwnd
, NULL
, TRUE
);
1314 #ifdef RDB_DEBUG_PATCH
1315 debug((27, "reset_window() -> Zoomed term_size"));
1322 /* Hmm, a force re-init means we should ignore the current window
1323 * so we resize to the default font size.
1326 #ifdef RDB_DEBUG_PATCH
1327 debug((27, "reset_window() -> Forced re-init"));
1330 offset_width
= offset_height
= cfg
.window_border
;
1331 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1332 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1334 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1335 win_height
!= font_height
*rows
+ offset_height
*2) {
1337 /* If this is too large windows will resize it to the maximum
1338 * allowed window size, we will then be back in here and resize
1339 * the font or terminal to fit.
1341 SetWindowPos(hwnd
, NULL
, 0, 0,
1342 font_width
*cols
+ extra_width
,
1343 font_height
*rows
+ extra_height
,
1344 SWP_NOMOVE
| SWP_NOZORDER
);
1347 InvalidateRect(hwnd
, NULL
, TRUE
);
1351 /* Okay the user doesn't want us to change the font so we try the
1352 * window. But that may be too big for the screen which forces us
1353 * to change the terminal.
1355 if ((cfg
.resize_action
== RESIZE_TERM
&& reinit
<=0) ||
1356 (cfg
.resize_action
== RESIZE_EITHER
&& reinit
<0) ||
1358 offset_width
= offset_height
= cfg
.window_border
;
1359 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1360 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1362 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1363 win_height
!= font_height
*rows
+ offset_height
*2) {
1368 GetClientRect(GetDesktopWindow(), &ss
);
1369 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1370 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1373 if ( rows
> height
|| cols
> width
) {
1374 if (cfg
.resize_action
== RESIZE_EITHER
) {
1375 /* Make the font the biggest we can */
1377 font_width
= (ss
.right
- ss
.left
- extra_width
)/cols
;
1379 font_height
= (ss
.bottom
- ss
.top
- extra_height
)/rows
;
1382 init_fonts(font_width
, font_height
);
1384 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1385 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1387 if ( height
> rows
) height
= rows
;
1388 if ( width
> cols
) width
= cols
;
1389 term_size(height
, width
, cfg
.savelines
);
1390 #ifdef RDB_DEBUG_PATCH
1391 debug((27, "reset_window() -> term resize to (%d,%d)",
1397 SetWindowPos(hwnd
, NULL
, 0, 0,
1398 font_width
*cols
+ extra_width
,
1399 font_height
*rows
+ extra_height
,
1400 SWP_NOMOVE
| SWP_NOZORDER
);
1402 InvalidateRect(hwnd
, NULL
, TRUE
);
1403 #ifdef RDB_DEBUG_PATCH
1404 debug((27, "reset_window() -> window resize to (%d,%d)",
1405 font_width
*cols
+ extra_width
,
1406 font_height
*rows
+ extra_height
));
1412 /* We're allowed to or must change the font but do we want to ? */
1414 if (font_width
!= (win_width
-cfg
.window_border
*2)/cols
||
1415 font_height
!= (win_height
-cfg
.window_border
*2)/rows
) {
1418 init_fonts((win_width
-cfg
.window_border
*2)/cols
,
1419 (win_height
-cfg
.window_border
*2)/rows
);
1420 offset_width
= (win_width
-font_width
*cols
)/2;
1421 offset_height
= (win_height
-font_height
*rows
)/2;
1423 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+offset_width
*2;
1424 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1426 InvalidateRect(hwnd
, NULL
, TRUE
);
1427 #ifdef RDB_DEBUG_PATCH
1428 debug((25, "reset_window() -> font resize to (%d,%d)",
1429 font_width
, font_height
));
1434 static void set_input_locale(HKL kl
)
1438 GetLocaleInfo(LOWORD(kl
), LOCALE_IDEFAULTANSICODEPAGE
,
1439 lbuf
, sizeof(lbuf
));
1441 kbd_codepage
= atoi(lbuf
);
1444 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
, int alt
)
1446 int thistime
= GetMessageTime();
1448 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1449 lastbtn
= MBT_NOTHING
;
1450 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
, alt
);
1454 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1455 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1456 lastact
== MA_2CLK ? MA_3CLK
:
1457 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1462 if (lastact
!= MA_NOTHING
)
1463 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
, alt
);
1464 lasttime
= thistime
;
1468 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1469 * into a cooked one (SELECT, EXTEND, PASTE).
1471 Mouse_Button
translate_button(Mouse_Button button
)
1473 if (button
== MBT_LEFT
)
1475 if (button
== MBT_MIDDLE
)
1476 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1477 if (button
== MBT_RIGHT
)
1478 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1479 return 0; /* shouldn't happen */
1482 static void show_mouseptr(int show
)
1484 static int cursor_visible
= 1;
1485 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1487 if (cursor_visible
&& !show
)
1489 else if (!cursor_visible
&& show
)
1491 cursor_visible
= show
;
1494 static int is_alt_pressed(void)
1497 int r
= GetKeyboardState(keystate
);
1500 if (keystate
[VK_MENU
] & 0x80)
1502 if (keystate
[VK_RMENU
] & 0x80)
1507 static int resizing
;
1509 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1510 WPARAM wParam
, LPARAM lParam
)
1513 static int ignore_clip
= FALSE
;
1514 static int need_backend_resize
= FALSE
;
1515 static int fullscr_on_max
= FALSE
;
1519 if (pending_netevent
)
1520 enact_pending_netevent();
1521 if (GetCapture() != hwnd
)
1527 if (cfg
.ping_interval
> 0) {
1530 if (now
- last_movement
> cfg
.ping_interval
) {
1531 back
->special(TS_PING
);
1532 last_movement
= now
;
1535 net_pending_errors();
1541 if (!cfg
.warn_on_close
|| session_closed
||
1543 "Are you sure you want to close this session?",
1544 "PuTTY Exit Confirmation",
1545 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1546 DestroyWindow(hwnd
);
1553 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1565 PROCESS_INFORMATION pi
;
1566 HANDLE filemap
= NULL
;
1568 if (wParam
== IDM_DUPSESS
) {
1570 * Allocate a file-mapping memory chunk for the
1573 SECURITY_ATTRIBUTES sa
;
1576 sa
.nLength
= sizeof(sa
);
1577 sa
.lpSecurityDescriptor
= NULL
;
1578 sa
.bInheritHandle
= TRUE
;
1579 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1582 0, sizeof(Config
), NULL
);
1584 p
= (Config
*) MapViewOfFile(filemap
,
1586 0, 0, sizeof(Config
));
1588 *p
= cfg
; /* structure copy */
1592 sprintf(c
, "putty &%p", filemap
);
1594 } else if (wParam
== IDM_SAVEDSESS
) {
1595 if ((lParam
- IDM_SAVED_MIN
) / 16 < nsessions
) {
1597 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1598 cl
= smalloc(16 + strlen(session
));
1599 /* 8, but play safe */
1602 /* not a very important failure mode */
1604 sprintf(cl
, "putty @%s", session
);
1612 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1614 si
.lpReserved
= NULL
;
1615 si
.lpDesktop
= NULL
;
1619 si
.lpReserved2
= NULL
;
1620 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1621 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1624 CloseHandle(filemap
);
1634 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1637 if (!do_reconfig(hwnd
))
1641 /* Disable full-screen if resizing forbidden */
1642 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
1643 EnableMenuItem(m
, IDM_FULLSCREEN
, MF_BYCOMMAND
|
1644 (cfg
.resize_action
== RESIZE_DISABLED
)
1645 ? MF_GRAYED
: MF_ENABLED
);
1646 /* Gracefully unzoom if necessary */
1647 if (IsZoomed(hwnd
) &&
1648 (cfg
.resize_action
== RESIZE_DISABLED
)) {
1649 ShowWindow(hwnd
, SW_RESTORE
);
1653 if (strcmp(prev_cfg
.logfilename
, cfg
.logfilename
) ||
1654 prev_cfg
.logtype
!= cfg
.logtype
) {
1655 logfclose(); /* reset logging */
1661 * Flush the line discipline's edit buffer in the
1662 * case where local editing has just been disabled.
1664 ldisc_send(NULL
, 0, 0);
1672 /* Screen size changed ? */
1673 if (cfg
.height
!= prev_cfg
.height
||
1674 cfg
.width
!= prev_cfg
.width
||
1675 cfg
.savelines
!= prev_cfg
.savelines
||
1676 cfg
.resize_action
== RESIZE_FONT
||
1677 (cfg
.resize_action
== RESIZE_EITHER
&& IsZoomed(hwnd
)) ||
1678 cfg
.resize_action
== RESIZE_DISABLED
)
1679 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1681 /* Enable or disable the scroll bar, etc */
1683 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1684 LONG nexflag
, exflag
=
1685 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1688 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1689 if (cfg
.alwaysontop
) {
1690 nexflag
|= WS_EX_TOPMOST
;
1691 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1692 SWP_NOMOVE
| SWP_NOSIZE
);
1694 nexflag
&= ~(WS_EX_TOPMOST
);
1695 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1696 SWP_NOMOVE
| SWP_NOSIZE
);
1699 if (cfg
.sunken_edge
)
1700 nexflag
|= WS_EX_CLIENTEDGE
;
1702 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1705 if (is_full_screen() ?
1706 cfg
.scrollbar_in_fullscreen
: cfg
.scrollbar
)
1709 nflg
&= ~WS_VSCROLL
;
1711 if (cfg
.resize_action
== RESIZE_DISABLED
||
1713 nflg
&= ~WS_THICKFRAME
;
1715 nflg
|= WS_THICKFRAME
;
1717 if (cfg
.resize_action
== RESIZE_DISABLED
)
1718 nflg
&= ~WS_MAXIMIZEBOX
;
1720 nflg
|= WS_MAXIMIZEBOX
;
1722 if (nflg
!= flag
|| nexflag
!= exflag
) {
1724 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1725 if (nexflag
!= exflag
)
1726 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1728 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1729 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1730 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1738 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1743 set_title(cfg
.wintitle
);
1744 if (IsIconic(hwnd
)) {
1746 cfg
.win_name_always ? window_name
:
1750 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1751 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1752 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1753 cfg
.fontheight
!= prev_cfg
.fontheight
||
1754 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1755 cfg
.vtmode
!= prev_cfg
.vtmode
||
1756 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1757 cfg
.resize_action
== RESIZE_DISABLED
||
1758 cfg
.resize_action
== RESIZE_EITHER
||
1759 (cfg
.resize_action
!= prev_cfg
.resize_action
))
1762 InvalidateRect(hwnd
, NULL
, TRUE
);
1763 reset_window(init_lvl
);
1764 net_pending_errors();
1777 back
->special(TS_AYT
);
1778 net_pending_errors();
1781 back
->special(TS_BRK
);
1782 net_pending_errors();
1785 back
->special(TS_SYNCH
);
1786 net_pending_errors();
1789 back
->special(TS_EC
);
1790 net_pending_errors();
1793 back
->special(TS_EL
);
1794 net_pending_errors();
1797 back
->special(TS_GA
);
1798 net_pending_errors();
1801 back
->special(TS_NOP
);
1802 net_pending_errors();
1805 back
->special(TS_ABORT
);
1806 net_pending_errors();
1809 back
->special(TS_AO
);
1810 net_pending_errors();
1813 back
->special(TS_IP
);
1814 net_pending_errors();
1817 back
->special(TS_SUSP
);
1818 net_pending_errors();
1821 back
->special(TS_EOR
);
1822 net_pending_errors();
1825 back
->special(TS_EOF
);
1826 net_pending_errors();
1832 WinHelp(hwnd
, help_path
,
1833 help_has_contents ? HELP_FINDER
: HELP_CONTENTS
, 0);
1837 * We get this if the System menu has been activated
1844 * We get this if the System menu has been activated
1845 * using the keyboard. This might happen from within
1846 * TranslateKey, in which case it really wants to be
1847 * followed by a `space' character to actually _bring
1848 * the menu up_ rather than just sitting there in
1849 * `ready to appear' state.
1851 show_mouseptr(1); /* make sure pointer is visible */
1853 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1855 case IDM_FULLSCREEN
:
1859 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1860 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1865 #define X_POS(l) ((int)(short)LOWORD(l))
1866 #define Y_POS(l) ((int)(short)HIWORD(l))
1868 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1869 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1870 case WM_LBUTTONDOWN
:
1871 case WM_MBUTTONDOWN
:
1872 case WM_RBUTTONDOWN
:
1880 case WM_LBUTTONDOWN
:
1884 case WM_MBUTTONDOWN
:
1885 button
= MBT_MIDDLE
;
1888 case WM_RBUTTONDOWN
:
1897 button
= MBT_MIDDLE
;
1905 button
= press
= 0; /* shouldn't happen */
1909 * Special case: in full-screen mode, if the left
1910 * button is clicked in the very top left corner of the
1911 * window, we put up the System menu instead of doing
1914 if (is_full_screen() && press
&& button
== MBT_LEFT
&&
1915 X_POS(lParam
) == 0 && Y_POS(lParam
) == 0) {
1916 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_MOUSEMENU
, 0);
1921 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1922 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
,
1926 term_mouse(button
, MA_RELEASE
,
1927 TO_CHR_X(X_POS(lParam
)),
1928 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1929 wParam
& MK_CONTROL
, is_alt_pressed());
1937 * Add the mouse position and message time to the random
1940 noise_ultralight(lParam
);
1942 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
) &&
1943 GetCapture() == hwnd
) {
1945 if (wParam
& MK_LBUTTON
)
1947 else if (wParam
& MK_MBUTTON
)
1951 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1952 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1953 wParam
& MK_CONTROL
, is_alt_pressed());
1956 case WM_NCMOUSEMOVE
:
1958 noise_ultralight(lParam
);
1960 case WM_IGNORE_CLIP
:
1961 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1963 case WM_DESTROYCLIPBOARD
:
1966 ignore_clip
= FALSE
;
1972 hdc
= BeginPaint(hwnd
, &p
);
1974 SelectPalette(hdc
, pal
, TRUE
);
1975 RealizePalette(hdc
);
1978 (p
.rcPaint
.left
-offset_width
)/font_width
,
1979 (p
.rcPaint
.top
-offset_height
)/font_height
,
1980 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
1981 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
1984 p
.rcPaint
.left
< offset_width
||
1985 p
.rcPaint
.top
< offset_height
||
1986 p
.rcPaint
.right
>= offset_width
+ font_width
*cols
||
1987 p
.rcPaint
.bottom
>= offset_height
+ font_height
*rows
)
1989 HBRUSH fillcolour
, oldbrush
;
1991 fillcolour
= CreateSolidBrush (
1992 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1993 oldbrush
= SelectObject(hdc
, fillcolour
);
1994 edge
= CreatePen(PS_SOLID
, 0,
1995 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1996 oldpen
= SelectObject(hdc
, edge
);
1998 ExcludeClipRect(hdc
,
1999 offset_width
, offset_height
,
2000 offset_width
+font_width
*cols
,
2001 offset_height
+font_height
*rows
);
2003 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
2004 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
2006 // SelectClipRgn(hdc, NULL);
2008 SelectObject(hdc
, oldbrush
);
2009 DeleteObject(fillcolour
);
2010 SelectObject(hdc
, oldpen
);
2013 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
2014 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
2020 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2021 * but the only one that's likely to try to overload us is FD_READ.
2022 * This means buffering just one is fine.
2024 if (pending_netevent
)
2025 enact_pending_netevent();
2027 pending_netevent
= TRUE
;
2028 pend_netevent_wParam
= wParam
;
2029 pend_netevent_lParam
= lParam
;
2030 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
2031 enact_pending_netevent();
2033 time(&last_movement
);
2037 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
2039 flash_window(0); /* stop */
2048 caret_x
= caret_y
= -1; /* ensure caret is replaced next time */
2052 case WM_ENTERSIZEMOVE
:
2053 #ifdef RDB_DEBUG_PATCH
2054 debug((27, "WM_ENTERSIZEMOVE"));
2058 need_backend_resize
= FALSE
;
2060 case WM_EXITSIZEMOVE
:
2063 #ifdef RDB_DEBUG_PATCH
2064 debug((27, "WM_EXITSIZEMOVE"));
2066 if (need_backend_resize
) {
2067 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
2068 InvalidateRect(hwnd
, NULL
, TRUE
);
2073 * This does two jobs:
2074 * 1) Keep the sizetip uptodate
2075 * 2) Make sure the window size is _stepped_ in units of the font size.
2077 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2078 int width
, height
, w
, h
, ew
, eh
;
2079 LPRECT r
= (LPRECT
) lParam
;
2081 if ( !need_backend_resize
&& cfg
.resize_action
== RESIZE_EITHER
&&
2082 (cfg
.height
!= rows
|| cfg
.width
!= cols
)) {
2084 * Great! It seems that both the terminal size and the
2085 * font size have been changed and the user is now dragging.
2087 * It will now be difficult to get back to the configured
2090 * This would be easier but it seems to be too confusing.
2092 term_size(cfg.height, cfg.width, cfg.savelines);
2095 cfg
.height
=rows
; cfg
.width
=cols
;
2097 InvalidateRect(hwnd
, NULL
, TRUE
);
2098 need_backend_resize
= TRUE
;
2101 width
= r
->right
- r
->left
- extra_width
;
2102 height
= r
->bottom
- r
->top
- extra_height
;
2103 w
= (width
+ font_width
/ 2) / font_width
;
2106 h
= (height
+ font_height
/ 2) / font_height
;
2109 UpdateSizeTip(hwnd
, w
, h
);
2110 ew
= width
- w
* font_width
;
2111 eh
= height
- h
* font_height
;
2113 if (wParam
== WMSZ_LEFT
||
2114 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2120 if (wParam
== WMSZ_TOP
||
2121 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2131 int width
, height
, w
, h
, rv
= 0;
2132 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
2133 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2134 LPRECT r
= (LPRECT
) lParam
;
2136 width
= r
->right
- r
->left
- ex_width
;
2137 height
= r
->bottom
- r
->top
- ex_height
;
2139 w
= (width
+ cols
/2)/cols
;
2140 h
= (height
+ rows
/2)/rows
;
2141 if ( r
->right
!= r
->left
+ w
*cols
+ ex_width
)
2144 if (wParam
== WMSZ_LEFT
||
2145 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2146 r
->left
= r
->right
- w
*cols
- ex_width
;
2148 r
->right
= r
->left
+ w
*cols
+ ex_width
;
2150 if (r
->bottom
!= r
->top
+ h
*rows
+ ex_height
)
2153 if (wParam
== WMSZ_TOP
||
2154 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2155 r
->top
= r
->bottom
- h
*rows
- ex_height
;
2157 r
->bottom
= r
->top
+ h
*rows
+ ex_height
;
2161 /* break; (never reached) */
2162 case WM_FULLSCR_ON_MAX
:
2163 fullscr_on_max
= TRUE
;
2166 sys_cursor_update();
2169 #ifdef RDB_DEBUG_PATCH
2170 debug((27, "WM_SIZE %s (%d,%d)",
2171 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2172 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2173 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2174 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2176 LOWORD(lParam
), HIWORD(lParam
)));
2178 if (wParam
== SIZE_MINIMIZED
)
2180 cfg
.win_name_always ? window_name
: icon_name
);
2181 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2182 SetWindowText(hwnd
, window_name
);
2183 if (wParam
== SIZE_RESTORED
)
2184 clear_full_screen();
2185 if (wParam
== SIZE_MAXIMIZED
&& fullscr_on_max
) {
2187 fullscr_on_max
= FALSE
;
2190 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2191 /* A resize, well it better be a minimize. */
2195 int width
, height
, w
, h
;
2197 width
= LOWORD(lParam
);
2198 height
= HIWORD(lParam
);
2201 if (wParam
== SIZE_MAXIMIZED
&& !was_zoomed
) {
2205 if (cfg
.resize_action
== RESIZE_TERM
) {
2206 w
= width
/ font_width
;
2208 h
= height
/ font_height
;
2211 term_size(h
, w
, cfg
.savelines
);
2214 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2216 if (cfg
.resize_action
== RESIZE_TERM
)
2217 term_size(prev_rows
, prev_cols
, cfg
.savelines
);
2218 if (cfg
.resize_action
!= RESIZE_FONT
)
2223 /* This is an unexpected resize, these will normally happen
2224 * if the window is too large. Probably either the user
2225 * selected a huge font or the screen size has changed.
2227 * This is also called with minimize.
2229 else reset_window(-1);
2233 * Don't call back->size in mid-resize. (To prevent
2234 * massive numbers of resize events getting sent
2235 * down the connection during an NT opaque drag.)
2238 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2239 need_backend_resize
= TRUE
;
2240 w
= (width
-cfg
.window_border
*2) / font_width
;
2242 h
= (height
-cfg
.window_border
*2) / font_height
;
2251 sys_cursor_update();
2254 switch (LOWORD(wParam
)) {
2268 term_scroll(0, +rows
/ 2);
2271 term_scroll(0, -rows
/ 2);
2273 case SB_THUMBPOSITION
:
2275 term_scroll(1, HIWORD(wParam
));
2279 case WM_PALETTECHANGED
:
2280 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2281 HDC hdc
= get_ctx();
2283 if (RealizePalette(hdc
) > 0)
2289 case WM_QUERYNEWPALETTE
:
2291 HDC hdc
= get_ctx();
2293 if (RealizePalette(hdc
) > 0)
2305 * Add the scan code and keypress timing to the random
2308 noise_ultralight(lParam
);
2311 * We don't do TranslateMessage since it disassociates the
2312 * resulting CHAR message from the KEYDOWN that sparked it,
2313 * which we occasionally don't want. Instead, we process
2314 * KEYDOWN, and call the Win32 translator functions so that
2315 * we get the translations under _our_ control.
2318 unsigned char buf
[20];
2321 if (wParam
== VK_PROCESSKEY
) {
2324 m
.message
= WM_KEYDOWN
;
2326 m
.lParam
= lParam
& 0xdfff;
2327 TranslateMessage(&m
);
2329 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2331 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2335 * Interrupt an ongoing paste. I'm not sure
2336 * this is sensible, but for the moment it's
2337 * preferable to having to faff about buffering
2343 * We need not bother about stdin backlogs
2344 * here, because in GUI PuTTY we can't do
2345 * anything about it anyway; there's no means
2346 * of asking Windows to hold off on KEYDOWN
2347 * messages. We _have_ to buffer everything
2350 ldisc_send(buf
, len
, 1);
2355 net_pending_errors();
2357 case WM_INPUTLANGCHANGE
:
2358 /* wParam == Font number */
2359 /* lParam == Locale */
2360 set_input_locale((HKL
)lParam
);
2361 sys_cursor_update();
2364 if(wParam
== IMN_SETOPENSTATUS
) {
2365 HIMC hImc
= ImmGetContext(hwnd
);
2366 ImmSetCompositionFont(hImc
, &lfont
);
2367 ImmReleaseContext(hwnd
, hImc
);
2371 case WM_IME_COMPOSITION
:
2377 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2378 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2380 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2381 break; /* fall back to DefWindowProc */
2383 hIMC
= ImmGetContext(hwnd
);
2384 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2388 buff
= (char*) smalloc(n
);
2389 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2391 * Jaeyoun Chung reports that Korean character
2392 * input doesn't work correctly if we do a single
2393 * luni_send() covering the whole of buff. So
2394 * instead we luni_send the characters one by one.
2396 for (i
= 0; i
< n
; i
+= 2)
2397 luni_send((unsigned short *)(buff
+i
), 1, 1);
2400 ImmReleaseContext(hwnd
, hIMC
);
2405 if (wParam
& 0xFF00) {
2406 unsigned char buf
[2];
2409 buf
[0] = wParam
>> 8;
2410 lpage_send(kbd_codepage
, buf
, 2, 1);
2412 char c
= (unsigned char) wParam
;
2413 lpage_send(kbd_codepage
, &c
, 1, 1);
2419 * Nevertheless, we are prepared to deal with WM_CHAR
2420 * messages, should they crop up. So if someone wants to
2421 * post the things to us as part of a macro manoeuvre,
2422 * we're ready to cope.
2425 char c
= (unsigned char)wParam
;
2426 lpage_send(CP_ACP
, &c
, 1, 1);
2430 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2431 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2435 if (message
== wm_mousewheel
) {
2436 int shift_pressed
=0, control_pressed
=0, alt_pressed
=0;
2438 if (message
== WM_MOUSEWHEEL
) {
2439 wheel_accumulator
+= (short)HIWORD(wParam
);
2440 shift_pressed
=LOWORD(wParam
) & MK_SHIFT
;
2441 control_pressed
=LOWORD(wParam
) & MK_CONTROL
;
2444 wheel_accumulator
+= (int)wParam
;
2445 if (GetKeyboardState(keys
)!=0) {
2446 shift_pressed
=keys
[VK_SHIFT
]&0x80;
2447 control_pressed
=keys
[VK_CONTROL
]&0x80;
2451 /* process events when the threshold is reached */
2452 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
2455 /* reduce amount for next time */
2456 if (wheel_accumulator
> 0) {
2458 wheel_accumulator
-= WHEEL_DELTA
;
2459 } else if (wheel_accumulator
< 0) {
2461 wheel_accumulator
+= WHEEL_DELTA
;
2465 if (send_raw_mouse
&&
2466 !(cfg
.mouse_override
&& shift_pressed
)) {
2467 /* send a mouse-down followed by a mouse up */
2470 TO_CHR_X(X_POS(lParam
)),
2471 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2472 control_pressed
, is_alt_pressed());
2473 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
2474 TO_CHR_Y(Y_POS(lParam
)), shift_pressed
,
2475 control_pressed
, is_alt_pressed());
2477 /* trigger a scroll */
2479 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
2486 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2490 * Move the system caret. (We maintain one, even though it's
2491 * invisible, for the benefit of blind people: apparently some
2492 * helper software tracks the system caret, so we should arrange to
2495 void sys_cursor(int x
, int y
)
2499 if (!has_focus
) return;
2502 * Avoid gratuitously re-updating the cursor position and IMM
2503 * window if there's no actual change required.
2505 cx
= x
* font_width
+ offset_width
;
2506 cy
= y
* font_height
+ offset_height
;
2507 if (cx
== caret_x
&& cy
== caret_y
)
2512 sys_cursor_update();
2515 static void sys_cursor_update(void)
2520 if (!has_focus
) return;
2522 if (caret_x
< 0 || caret_y
< 0)
2525 SetCaretPos(caret_x
, caret_y
);
2527 /* IMM calls on Win98 and beyond only */
2528 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2530 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2531 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2533 /* we should have the IMM functions */
2534 hIMC
= ImmGetContext(hwnd
);
2535 cf
.dwStyle
= CFS_POINT
;
2536 cf
.ptCurrentPos
.x
= caret_x
;
2537 cf
.ptCurrentPos
.y
= caret_y
;
2538 ImmSetCompositionWindow(hIMC
, &cf
);
2540 ImmReleaseContext(hwnd
, hIMC
);
2544 * Draw a line of text in the window, at given character
2545 * coordinates, in given attributes.
2547 * We are allowed to fiddle with the contents of `text'.
2549 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2550 unsigned long attr
, int lattr
)
2553 int nfg
, nbg
, nfont
;
2556 int force_manual_underline
= 0;
2557 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2558 int char_width
= fnt_width
;
2559 int text_adjust
= 0;
2560 static int *IpDx
= 0, IpDxLEN
= 0;
2562 if (attr
& ATTR_WIDE
)
2565 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2567 if (len
> IpDxLEN
) {
2569 IpDx
= smalloc((len
+ 16) * sizeof(int));
2570 IpDxLEN
= (len
+ 16);
2572 for (i
= 0; i
< IpDxLEN
; i
++)
2573 IpDx
[i
] = char_width
;
2576 /* Only want the left half of double width lines */
2577 if (lattr
!= LATTR_NORM
&& x
*2 >= cols
)
2585 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
2586 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2587 attr
^= ATTR_CUR_XOR
;
2591 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2592 /* Assume a poorman font is borken in other ways too. */
2602 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2605 if (attr
& ATTR_NARROW
)
2606 nfont
|= FONT_NARROW
;
2608 /* Special hack for the VT100 linedraw glyphs. */
2609 if ((attr
& CSET_MASK
) == 0x2300) {
2610 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2611 switch ((unsigned char) (text
[0])) {
2613 text_adjust
= -2 * font_height
/ 5;
2616 text_adjust
= -1 * font_height
/ 5;
2619 text_adjust
= font_height
/ 5;
2622 text_adjust
= 2 * font_height
/ 5;
2625 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2628 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2629 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2630 if (attr
& ATTR_UNDER
) {
2631 attr
&= ~ATTR_UNDER
;
2632 force_manual_underline
= 1;
2637 /* Anything left as an original character set is unprintable. */
2638 if (DIRECT_CHAR(attr
)) {
2641 memset(text
, 0xFD, len
);
2645 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2648 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2649 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2650 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2652 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2653 nfont
|= FONT_UNDERLINE
;
2654 another_font(nfont
);
2655 if (!fonts
[nfont
]) {
2656 if (nfont
& FONT_UNDERLINE
)
2657 force_manual_underline
= 1;
2658 /* Don't do the same for manual bold, it could be bad news. */
2660 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2662 another_font(nfont
);
2664 nfont
= FONT_NORMAL
;
2665 if (attr
& ATTR_REVERSE
) {
2670 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2672 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2676 SelectObject(hdc
, fonts
[nfont
]);
2677 SetTextColor(hdc
, fg
);
2678 SetBkColor(hdc
, bg
);
2679 SetBkMode(hdc
, OPAQUE
);
2682 line_box
.right
= x
+ char_width
* len
;
2683 line_box
.bottom
= y
+ font_height
;
2685 /* Only want the left half of double width lines */
2686 if (line_box
.right
> font_width
*cols
+offset_width
)
2687 line_box
.right
= font_width
*cols
+offset_width
;
2689 /* We're using a private area for direct to font. (512 chars.) */
2690 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2691 /* Ho Hum, dbcs fonts are a PITA! */
2692 /* To display on W9x I have to convert to UCS */
2693 static wchar_t *uni_buf
= 0;
2694 static int uni_len
= 0;
2696 if (len
> uni_len
) {
2698 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2701 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2702 uni_buf
[nlen
] = 0xFFFD;
2703 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2704 IpDx
[nlen
] += char_width
;
2705 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2706 text
+mptr
, 2, uni_buf
+nlen
, 1);
2711 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2712 text
+mptr
, 1, uni_buf
+nlen
, 1);
2720 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2721 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2722 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2723 SetBkMode(hdc
, TRANSPARENT
);
2724 ExtTextOutW(hdc
, x
- 1,
2725 y
- font_height
* (lattr
==
2726 LATTR_BOT
) + text_adjust
,
2727 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2731 } else if (DIRECT_FONT(attr
)) {
2733 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2734 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2735 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2736 SetBkMode(hdc
, TRANSPARENT
);
2738 /* GRR: This draws the character outside it's box and can leave
2739 * 'droppings' even with the clip box! I suppose I could loop it
2740 * one character at a time ... yuk.
2742 * Or ... I could do a test print with "W", and use +1 or -1 for this
2743 * shift depending on if the leftmost column is blank...
2745 ExtTextOut(hdc
, x
- 1,
2746 y
- font_height
* (lattr
==
2747 LATTR_BOT
) + text_adjust
,
2748 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2751 /* And 'normal' unicode characters */
2752 static WCHAR
*wbuf
= NULL
;
2753 static int wlen
= 0;
2758 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2760 for (i
= 0; i
< len
; i
++)
2761 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2764 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2765 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2767 /* And the shadow bold hack. */
2768 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2769 SetBkMode(hdc
, TRANSPARENT
);
2770 ExtTextOutW(hdc
, x
- 1,
2771 y
- font_height
* (lattr
==
2772 LATTR_BOT
) + text_adjust
,
2773 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2776 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2777 (und_mode
== UND_LINE
2778 && (attr
& ATTR_UNDER
)))) {
2781 if (lattr
== LATTR_BOT
)
2782 dec
= dec
* 2 - font_height
;
2784 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2785 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2786 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2787 oldpen
= SelectObject(hdc
, oldpen
);
2788 DeleteObject(oldpen
);
2792 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2793 unsigned long attr
, int lattr
)
2799 int ctype
= cfg
.cursor_type
;
2801 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2802 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2803 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2807 attr
|= TATTR_RIGHTCURS
;
2810 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2811 if (attr
& ATTR_WIDE
)
2818 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2821 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2822 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2823 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2824 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2825 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2826 Polyline(hdc
, pts
, 5);
2827 oldpen
= SelectObject(hdc
, oldpen
);
2828 DeleteObject(oldpen
);
2829 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2830 int startx
, starty
, dx
, dy
, length
, i
;
2833 starty
= y
+ descent
;
2836 length
= char_width
;
2839 if (attr
& TATTR_RIGHTCURS
)
2840 xadjust
= char_width
- 1;
2841 startx
= x
+ xadjust
;
2845 length
= font_height
;
2847 if (attr
& TATTR_ACTCURS
) {
2850 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2851 MoveToEx(hdc
, startx
, starty
, NULL
);
2852 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2853 oldpen
= SelectObject(hdc
, oldpen
);
2854 DeleteObject(oldpen
);
2856 for (i
= 0; i
< length
; i
++) {
2858 SetPixel(hdc
, startx
, starty
, colours
[23]);
2867 /* This function gets the actual width of a character in the normal font.
2869 int CharWidth(Context ctx
, int uc
) {
2873 /* If the font max is the same as the font ave width then this
2874 * function is a no-op.
2876 if (!font_dualwidth
) return 1;
2878 switch (uc
& CSET_MASK
) {
2880 uc
= unitab_line
[uc
& 0xFF];
2883 uc
= unitab_xterm
[uc
& 0xFF];
2886 uc
= unitab_scoacs
[uc
& 0xFF];
2889 if (DIRECT_FONT(uc
)) {
2890 if (dbcs_screenfont
) return 1;
2892 /* Speedup, I know of no font where ascii is the wrong width */
2893 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
2896 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
2897 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2898 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
2899 another_font(FONT_OEM
);
2900 if (!fonts
[FONT_OEM
]) return 0;
2902 SelectObject(hdc
, fonts
[FONT_OEM
]);
2906 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
2907 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
2910 /* Speedup, I know of no font where ascii is the wrong width */
2911 if (uc
>= ' ' && uc
<= '~') return 1;
2913 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2914 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
2915 /* Okay that one worked */ ;
2916 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
2917 /* This should work on 9x too, but it's "less accurate" */ ;
2922 ibuf
+= font_width
/ 2 -1;
2929 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2930 * codes. Returns number of bytes used or zero to drop the message
2931 * or -1 to forward the message to windows.
2933 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2934 unsigned char *output
)
2937 int scan
, left_alt
= 0, key_down
, shift_state
;
2939 unsigned char *p
= output
;
2940 static int alt_sum
= 0;
2942 HKL kbd_layout
= GetKeyboardLayout(0);
2944 static WORD keys
[3];
2945 static int compose_char
= 0;
2946 static WPARAM compose_key
= 0;
2948 r
= GetKeyboardState(keystate
);
2950 memset(keystate
, 0, sizeof(keystate
));
2953 #define SHOW_TOASCII_RESULT
2954 { /* Tell us all about key events */
2955 static BYTE oldstate
[256];
2956 static int first
= 1;
2960 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2963 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2965 } else if ((HIWORD(lParam
) & KF_UP
)
2966 && scan
== (HIWORD(lParam
) & 0xFF)) {
2970 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2971 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2984 debug(("VK_%02x", wParam
));
2986 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2988 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2990 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2991 if (ch
>= ' ' && ch
<= '~')
2992 debug((", '%c'", ch
));
2994 debug((", $%02x", ch
));
2997 debug((", KB0=%02x", keys
[0]));
2999 debug((", KB1=%02x", keys
[1]));
3001 debug((", KB2=%02x", keys
[2]));
3003 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
3005 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
3007 if ((HIWORD(lParam
) & KF_EXTENDED
))
3009 if ((HIWORD(lParam
) & KF_UP
))
3013 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
3014 else if ((HIWORD(lParam
) & KF_UP
))
3015 oldstate
[wParam
& 0xFF] ^= 0x80;
3017 oldstate
[wParam
& 0xFF] ^= 0x81;
3019 for (ch
= 0; ch
< 256; ch
++)
3020 if (oldstate
[ch
] != keystate
[ch
])
3021 debug((", M%02x=%02x", ch
, keystate
[ch
]));
3023 memcpy(oldstate
, keystate
, sizeof(oldstate
));
3027 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
3028 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
3032 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
3033 if ((cfg
.funky_type
== 3 ||
3034 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
3035 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
3037 wParam
= VK_EXECUTE
;
3039 /* UnToggle NUMLock */
3040 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
3041 keystate
[VK_NUMLOCK
] ^= 1;
3044 /* And write back the 'adjusted' state */
3045 SetKeyboardState(keystate
);
3048 /* Disable Auto repeat if required */
3049 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
3052 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
3055 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
3057 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
3058 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
3059 if (cfg
.ctrlaltkeys
)
3060 keystate
[VK_MENU
] = 0;
3062 keystate
[VK_RMENU
] = 0x80;
3067 alt_pressed
= (left_alt
&& key_down
);
3069 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
3070 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
3071 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
3073 /* Note if AltGr was pressed and if it was used as a compose key */
3074 if (!compose_state
) {
3075 compose_key
= 0x100;
3076 if (cfg
.compose_key
) {
3077 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
3078 compose_key
= wParam
;
3080 if (wParam
== VK_APPS
)
3081 compose_key
= wParam
;
3084 if (wParam
== compose_key
) {
3085 if (compose_state
== 0
3086 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
3088 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
3092 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
3096 * Record that we pressed key so the scroll window can be reset, but
3097 * be careful to avoid Shift-UP/Down
3099 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
&&
3100 wParam
!= VK_MENU
&& wParam
!= VK_CONTROL
) {
3104 if (compose_state
> 1 && left_alt
)
3107 /* Sanitize the number pad if not using a PC NumPad */
3108 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
3109 && cfg
.funky_type
!= 2)
3110 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
3111 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
3115 nParam
= VK_NUMPAD0
;
3118 nParam
= VK_NUMPAD1
;
3121 nParam
= VK_NUMPAD2
;
3124 nParam
= VK_NUMPAD3
;
3127 nParam
= VK_NUMPAD4
;
3130 nParam
= VK_NUMPAD5
;
3133 nParam
= VK_NUMPAD6
;
3136 nParam
= VK_NUMPAD7
;
3139 nParam
= VK_NUMPAD8
;
3142 nParam
= VK_NUMPAD9
;
3145 nParam
= VK_DECIMAL
;
3149 if (keystate
[VK_NUMLOCK
] & 1)
3156 /* If a key is pressed and AltGr is not active */
3157 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
3158 /* Okay, prepare for most alts then ... */
3162 /* Lets see if it's a pattern we know all about ... */
3163 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
3164 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
3167 if (wParam
== VK_NEXT
&& shift_state
== 1) {
3168 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
3171 if (wParam
== VK_INSERT
&& shift_state
== 1) {
3175 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
3178 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
3179 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
3182 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
&&
3183 (cfg
.resize_action
!= RESIZE_DISABLED
)) {
3184 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) != KF_REPEAT
)
3188 /* Control-Numlock for app-keypad mode switch */
3189 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
3190 app_keypad_keys
^= 1;
3194 /* Nethack keypad */
3195 if (cfg
.nethack_keypad
&& !left_alt
) {
3198 *p
++ = shift_state ?
'B' : 'b';
3201 *p
++ = shift_state ?
'J' : 'j';
3204 *p
++ = shift_state ?
'N' : 'n';
3207 *p
++ = shift_state ?
'H' : 'h';
3210 *p
++ = shift_state ?
'.' : '.';
3213 *p
++ = shift_state ?
'L' : 'l';
3216 *p
++ = shift_state ?
'Y' : 'y';
3219 *p
++ = shift_state ?
'K' : 'k';
3222 *p
++ = shift_state ?
'U' : 'u';
3227 /* Application Keypad */
3231 if (cfg
.funky_type
== 3 ||
3232 (cfg
.funky_type
<= 1 &&
3233 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3247 if (app_keypad_keys
&& !cfg
.no_applic_k
)
3284 if (cfg
.funky_type
== 2) {
3289 } else if (shift_state
)
3296 if (cfg
.funky_type
== 2)
3300 if (cfg
.funky_type
== 2)
3304 if (cfg
.funky_type
== 2)
3309 if (HIWORD(lParam
) & KF_EXTENDED
)
3315 if (xkey
>= 'P' && xkey
<= 'S')
3316 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3318 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3320 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3325 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3326 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3330 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3336 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3340 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3344 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3349 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3354 /* Control-2 to Control-8 are special */
3355 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3356 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3359 if (shift_state
== 2 && wParam
== 0xBD) {
3363 if (shift_state
== 2 && wParam
== 0xDF) {
3367 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
3374 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3375 * for integer decimal nn.)
3377 * We also deal with the weird ones here. Linux VCs replace F1
3378 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3379 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3385 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3388 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3391 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3394 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3397 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3400 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3403 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3406 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3409 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3412 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3445 if ((shift_state
&2) == 0) switch (wParam
) {
3465 /* Reorder edit keys to physical order */
3466 if (cfg
.funky_type
== 3 && code
<= 6)
3467 code
= "\0\2\1\4\5\3\6"[code
];
3469 if (vt52_mode
&& code
> 0 && code
<= 6) {
3470 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3474 if (cfg
.funky_type
== 5 && /* SCO function keys */
3475 code
>= 11 && code
<= 34) {
3476 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3479 case VK_F1
: index
= 0; break;
3480 case VK_F2
: index
= 1; break;
3481 case VK_F3
: index
= 2; break;
3482 case VK_F4
: index
= 3; break;
3483 case VK_F5
: index
= 4; break;
3484 case VK_F6
: index
= 5; break;
3485 case VK_F7
: index
= 6; break;
3486 case VK_F8
: index
= 7; break;
3487 case VK_F9
: index
= 8; break;
3488 case VK_F10
: index
= 9; break;
3489 case VK_F11
: index
= 10; break;
3490 case VK_F12
: index
= 11; break;
3492 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3493 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3494 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3497 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3498 code
>= 1 && code
<= 6) {
3499 char codes
[] = "HL.FIG";
3503 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3507 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3514 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3517 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3520 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3521 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3524 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3526 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3528 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3531 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3532 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3536 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3541 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3542 * some reason seems to send VK_CLEAR to Windows...).
3565 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3567 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
3570 * RDB: VT100 & VT102 manuals both state the
3571 * app cursor keys only work if the app keypad
3574 * SGT: That may well be true, but xterm
3575 * disagrees and so does at least one
3576 * application, so I've #if'ed this out and the
3577 * behaviour is back to PuTTY's original: app
3578 * cursor and app keypad are independently
3579 * switchable modes. If anyone complains about
3580 * _this_ I'll have to put in a configurable
3583 if (!app_keypad_keys
)
3586 /* Useful mapping of Ctrl-arrows */
3587 if (shift_state
== 2)
3591 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3593 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3600 * Finally, deal with Return ourselves. (Win95 seems to
3601 * foul it up when Alt is pressed, for some reason.)
3603 if (wParam
== VK_RETURN
) { /* Return */
3609 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3610 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3615 /* Okay we've done everything interesting; let windows deal with
3616 * the boring stuff */
3620 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3621 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3623 keystate
[VK_CAPITAL
] = 0;
3626 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3627 #ifdef SHOW_TOASCII_RESULT
3628 if (r
== 1 && !key_down
) {
3630 if (in_utf
|| dbcs_screenfont
)
3631 debug((", (U+%04x)", alt_sum
));
3633 debug((", LCH(%d)", alt_sum
));
3635 debug((", ACH(%d)", keys
[0]));
3640 for (r1
= 0; r1
< r
; r1
++) {
3641 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3650 * Interrupt an ongoing paste. I'm not sure this is
3651 * sensible, but for the moment it's preferable to
3652 * having to faff about buffering things.
3657 for (i
= 0; i
< r
; i
++) {
3658 unsigned char ch
= (unsigned char) keys
[i
];
3660 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3665 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3669 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3670 MessageBeep(MB_ICONHAND
);
3674 luni_send(&keybuf
, 1, 1);
3682 if (in_utf
|| dbcs_screenfont
) {
3684 luni_send(&keybuf
, 1, 1);
3686 ch
= (char) alt_sum
;
3688 * We need not bother about stdin
3689 * backlogs here, because in GUI PuTTY
3690 * we can't do anything about it
3691 * anyway; there's no means of asking
3692 * Windows to hold off on KEYDOWN
3693 * messages. We _have_ to buffer
3694 * everything we're sent.
3696 ldisc_send(&ch
, 1, 1);
3700 lpage_send(kbd_codepage
, &ch
, 1, 1);
3702 if(capsOn
&& ch
< 0x80) {
3705 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3706 luni_send(cbuf
+!left_alt
, 1+!!left_alt
, 1);
3711 lpage_send(kbd_codepage
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3717 /* This is so the ALT-Numpad and dead keys work correctly. */
3722 /* If we're definitly not building up an ALT-54321 then clear it */
3725 /* If we will be using alt_sum fix the 256s */
3726 else if (keys
[0] && (in_utf
|| dbcs_screenfont
))
3731 * ALT alone may or may not want to bring up the System menu.
3732 * If it's not meant to, we return 0 on presses or releases of
3733 * ALT, to show that we've swallowed the keystroke. Otherwise
3734 * we return -1, which means Windows will give the keystroke
3735 * its default handling (i.e. bring up the System menu).
3737 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3743 void set_title(char *title
)
3746 window_name
= smalloc(1 + strlen(title
));
3747 strcpy(window_name
, title
);
3748 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3749 SetWindowText(hwnd
, title
);
3752 void set_icon(char *title
)
3755 icon_name
= smalloc(1 + strlen(title
));
3756 strcpy(icon_name
, title
);
3757 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3758 SetWindowText(hwnd
, title
);
3761 void set_sbar(int total
, int start
, int page
)
3765 if (is_full_screen() ?
!cfg
.scrollbar_in_fullscreen
: !cfg
.scrollbar
)
3768 si
.cbSize
= sizeof(si
);
3769 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3771 si
.nMax
= total
- 1;
3775 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3778 Context
get_ctx(void)
3784 SelectPalette(hdc
, pal
, FALSE
);
3790 void free_ctx(Context ctx
)
3792 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3793 ReleaseDC(hwnd
, ctx
);
3796 static void real_palette_set(int n
, int r
, int g
, int b
)
3799 logpal
->palPalEntry
[n
].peRed
= r
;
3800 logpal
->palPalEntry
[n
].peGreen
= g
;
3801 logpal
->palPalEntry
[n
].peBlue
= b
;
3802 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3803 colours
[n
] = PALETTERGB(r
, g
, b
);
3804 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3806 colours
[n
] = RGB(r
, g
, b
);
3809 void palette_set(int n
, int r
, int g
, int b
)
3811 static const int first
[21] = {
3812 0, 2, 4, 6, 8, 10, 12, 14,
3813 1, 3, 5, 7, 9, 11, 13, 15,
3816 real_palette_set(first
[n
], r
, g
, b
);
3818 real_palette_set(first
[n
] + 1, r
, g
, b
);
3820 HDC hdc
= get_ctx();
3821 UnrealizeObject(pal
);
3822 RealizePalette(hdc
);
3827 void palette_reset(void)
3831 for (i
= 0; i
< NCOLOURS
; i
++) {
3833 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3834 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3835 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3836 logpal
->palPalEntry
[i
].peFlags
= 0;
3837 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3838 defpal
[i
].rgbtGreen
,
3839 defpal
[i
].rgbtBlue
);
3841 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3842 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3847 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3849 RealizePalette(hdc
);
3854 void write_aclip(char *data
, int len
, int must_deselect
)
3859 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3862 lock
= GlobalLock(clipdata
);
3865 memcpy(lock
, data
, len
);
3866 ((unsigned char *) lock
)[len
] = 0;
3867 GlobalUnlock(clipdata
);
3870 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3872 if (OpenClipboard(hwnd
)) {
3874 SetClipboardData(CF_TEXT
, clipdata
);
3877 GlobalFree(clipdata
);
3880 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3884 * Note: unlike write_aclip() this will not append a nul.
3886 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3888 HGLOBAL clipdata
, clipdata2
, clipdata3
;
3890 void *lock
, *lock2
, *lock3
;
3892 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3894 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3895 len
* sizeof(wchar_t));
3896 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3898 if (!clipdata
|| !clipdata2
) {
3900 GlobalFree(clipdata
);
3902 GlobalFree(clipdata2
);
3905 if (!(lock
= GlobalLock(clipdata
)))
3907 if (!(lock2
= GlobalLock(clipdata2
)))
3910 memcpy(lock
, data
, len
* sizeof(wchar_t));
3911 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3913 if (cfg
.rtf_paste
) {
3914 wchar_t unitab
[256];
3916 unsigned char *tdata
= (unsigned char *)lock2
;
3917 wchar_t *udata
= (wchar_t *)lock
;
3918 int rtflen
= 0, uindex
= 0, tindex
= 0;
3920 int multilen
, blen
, alen
, totallen
, i
;
3921 char before
[16], after
[4];
3923 get_unitab(CP_ACP
, unitab
, 0);
3925 rtfsize
= 100 + strlen(cfg
.font
);
3926 rtf
= smalloc(rtfsize
);
3927 sprintf(rtf
, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3928 GetACP(), cfg
.font
);
3929 rtflen
= strlen(rtf
);
3932 * We want to construct a piece of RTF that specifies the
3933 * same Unicode text. To do this we will read back in
3934 * parallel from the Unicode data in `udata' and the
3935 * non-Unicode data in `tdata'. For each character in
3936 * `tdata' which becomes the right thing in `udata' when
3937 * looked up in `unitab', we just copy straight over from
3938 * tdata. For each one that doesn't, we must WCToMB it
3939 * individually and produce a \u escape sequence.
3941 * It would probably be more robust to just bite the bullet
3942 * and WCToMB each individual Unicode character one by one,
3943 * then MBToWC each one back to see if it was an accurate
3944 * translation; but that strikes me as a horrifying number
3945 * of Windows API calls so I want to see if this faster way
3946 * will work. If it screws up badly we can always revert to
3947 * the simple and slow way.
3949 while (tindex
< len2
&& uindex
< len
&&
3950 tdata
[tindex
] && udata
[uindex
]) {
3951 if (tindex
+ 1 < len2
&&
3952 tdata
[tindex
] == '\r' &&
3953 tdata
[tindex
+1] == '\n') {
3957 if (unitab
[tdata
[tindex
]] == udata
[uindex
]) {
3963 multilen
= WideCharToMultiByte(CP_ACP
, 0, unitab
+uindex
, 1,
3964 NULL
, 0, NULL
, NULL
);
3965 if (multilen
!= 1) {
3966 blen
= sprintf(before
, "{\\uc%d\\u%d", multilen
,
3968 alen
= 1; strcpy(after
, "}");
3970 blen
= sprintf(before
, "\\u%d", udata
[uindex
]);
3971 alen
= 0; after
[0] = '\0';
3974 assert(tindex
+ multilen
<= len2
);
3975 totallen
= blen
+ alen
;
3976 for (i
= 0; i
< multilen
; i
++) {
3977 if (tdata
[tindex
+i
] == '\\' ||
3978 tdata
[tindex
+i
] == '{' ||
3979 tdata
[tindex
+i
] == '}')
3981 else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A)
3982 totallen
+= 6; /* \par\r\n */
3983 else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20)
3989 if (rtfsize
< rtflen
+ totallen
+ 3) {
3990 rtfsize
= rtflen
+ totallen
+ 512;
3991 rtf
= srealloc(rtf
, rtfsize
);
3994 strcpy(rtf
+ rtflen
, before
); rtflen
+= blen
;
3995 for (i
= 0; i
< multilen
; i
++) {
3996 if (tdata
[tindex
+i
] == '\\' ||
3997 tdata
[tindex
+i
] == '{' ||
3998 tdata
[tindex
+i
] == '}') {
3999 rtf
[rtflen
++] = '\\';
4000 rtf
[rtflen
++] = tdata
[tindex
+i
];
4001 } else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A) {
4002 rtflen
+= sprintf(rtf
+rtflen
, "\\par\r\n");
4003 } else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20) {
4004 rtflen
+= sprintf(rtf
+rtflen
, "\\'%02x", tdata
[tindex
+i
]);
4006 rtf
[rtflen
++] = tdata
[tindex
+i
];
4009 strcpy(rtf
+ rtflen
, after
); rtflen
+= alen
;
4015 strcpy(rtf
+ rtflen
, "}");
4018 clipdata3
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, rtflen
);
4019 if (clipdata3
&& (lock3
= GlobalLock(clipdata3
)) != NULL
) {
4021 GlobalUnlock(clipdata3
);
4027 GlobalUnlock(clipdata
);
4028 GlobalUnlock(clipdata2
);
4031 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
4033 if (OpenClipboard(hwnd
)) {
4035 SetClipboardData(CF_UNICODETEXT
, clipdata
);
4036 SetClipboardData(CF_TEXT
, clipdata2
);
4038 SetClipboardData(RegisterClipboardFormat(CF_RTF
), clipdata3
);
4041 GlobalFree(clipdata
);
4042 GlobalFree(clipdata2
);
4046 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
4049 void get_clip(wchar_t ** p
, int *len
)
4051 static HGLOBAL clipdata
= NULL
;
4052 static wchar_t *converted
= 0;
4061 GlobalUnlock(clipdata
);
4064 } else if (OpenClipboard(NULL
)) {
4065 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
4067 *p
= GlobalLock(clipdata
);
4069 for (p2
= *p
; *p2
; p2
++);
4073 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
4077 s
= GlobalLock(clipdata
);
4078 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
4079 *p
= converted
= smalloc(i
* sizeof(wchar_t));
4080 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
4093 * Move `lines' lines from position `from' to position `to' in the
4096 void optimised_move(int to
, int from
, int lines
)
4101 min
= (to
< from ? to
: from
);
4102 max
= to
+ from
- min
;
4104 r
.left
= offset_width
;
4105 r
.right
= offset_width
+ cols
* font_width
;
4106 r
.top
= offset_height
+ min
* font_height
;
4107 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
4108 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
4113 * Print a message box and perform a fatal exit.
4115 void fatalbox(char *fmt
, ...)
4121 vsprintf(stuff
, fmt
, ap
);
4123 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
4128 * Manage window caption / taskbar flashing, if enabled.
4129 * 0 = stop, 1 = maintain, 2 = start
4131 static void flash_window(int mode
)
4133 static long last_flash
= 0;
4134 static int flashing
= 0;
4135 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
4138 FlashWindow(hwnd
, FALSE
);
4142 } else if (mode
== 2) {
4145 last_flash
= GetTickCount();
4147 FlashWindow(hwnd
, TRUE
);
4150 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
4153 long now
= GetTickCount();
4154 long fdiff
= now
- last_flash
;
4155 if (fdiff
< 0 || fdiff
> 450) {
4157 FlashWindow(hwnd
, TRUE
); /* toggle */
4168 if (mode
== BELL_DEFAULT
) {
4170 * For MessageBeep style bells, we want to be careful of
4171 * timing, because they don't have the nice property of
4172 * PlaySound bells that each one cancels the previous
4173 * active one. So we limit the rate to one per 50ms or so.
4175 static long lastbeep
= 0;
4178 beepdiff
= GetTickCount() - lastbeep
;
4179 if (beepdiff
>= 0 && beepdiff
< 50)
4183 * The above MessageBeep call takes time, so we record the
4184 * time _after_ it finishes rather than before it starts.
4186 lastbeep
= GetTickCount();
4187 } else if (mode
== BELL_WAVEFILE
) {
4188 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
4189 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
4190 sprintf(buf
, "Unable to play sound file\n%s\n"
4191 "Using default sound instead", cfg
.bell_wavefile
);
4192 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
4193 MB_OK
| MB_ICONEXCLAMATION
);
4194 cfg
.beep
= BELL_DEFAULT
;
4197 /* Otherwise, either visual bell or disabled; do nothing here */
4199 flash_window(2); /* start */
4204 * Minimise or restore the window in response to a server-side
4207 void set_iconic(int iconic
)
4209 if (IsIconic(hwnd
)) {
4211 ShowWindow(hwnd
, SW_RESTORE
);
4214 ShowWindow(hwnd
, SW_MINIMIZE
);
4219 * Move the window in response to a server-side request.
4221 void move_window(int x
, int y
)
4223 SetWindowPos(hwnd
, NULL
, x
, y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
);
4227 * Move the window to the top or bottom of the z-order in response
4228 * to a server-side request.
4230 void set_zorder(int top
)
4232 if (cfg
.alwaysontop
)
4233 return; /* ignore */
4234 SetWindowPos(hwnd
, top ? HWND_TOP
: HWND_BOTTOM
, 0, 0, 0, 0,
4235 SWP_NOMOVE
| SWP_NOSIZE
);
4239 * Refresh the window in response to a server-side request.
4241 void refresh_window(void)
4243 InvalidateRect(hwnd
, NULL
, TRUE
);
4247 * Maximise or restore the window in response to a server-side
4250 void set_zoomed(int zoomed
)
4252 if (IsZoomed(hwnd
)) {
4254 ShowWindow(hwnd
, SW_RESTORE
);
4257 ShowWindow(hwnd
, SW_MAXIMIZE
);
4262 * Report whether the window is iconic, for terminal reports.
4266 return IsIconic(hwnd
);
4270 * Report the window's position, for terminal reports.
4272 void get_window_pos(int *x
, int *y
)
4275 GetWindowRect(hwnd
, &r
);
4281 * Report the window's pixel size, for terminal reports.
4283 void get_window_pixels(int *x
, int *y
)
4286 GetWindowRect(hwnd
, &r
);
4287 *x
= r
.right
- r
.left
;
4288 *y
= r
.bottom
- r
.top
;
4292 * Return the window or icon title.
4294 char *get_window_title(int icon
)
4296 return icon ? icon_name
: window_name
;
4300 * See if we're in full-screen mode.
4302 int is_full_screen()
4304 if (!IsZoomed(hwnd
))
4306 if (GetWindowLong(hwnd
, GWL_STYLE
) & WS_CAPTION
)
4312 * Go full-screen. This should only be called when we are already
4315 void make_full_screen()
4320 assert(IsZoomed(hwnd
));
4322 /* Remove the window furniture. */
4323 style
= GetWindowLong(hwnd
, GWL_STYLE
);
4324 style
&= ~(WS_CAPTION
| WS_BORDER
| WS_THICKFRAME
);
4325 if (cfg
.scrollbar_in_fullscreen
)
4326 style
|= WS_VSCROLL
;
4328 style
&= ~WS_VSCROLL
;
4329 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4331 /* Resize ourselves to exactly cover the nearest monitor. */
4332 #ifdef MONITOR_DEFAULTTONEAREST
4336 mon
= MonitorFromWindow(hwnd
, MONITOR_DEFAULTTONEAREST
);
4337 mi
.cbSize
= sizeof(mi
);
4338 GetMonitorInfo(mon
, &mi
);
4339 x
= mi
.rcMonitor
.left
;
4340 y
= mi
.rcMonitor
.top
;
4341 w
= mi
.rcMonitor
.right
;
4342 h
= mi
.rcMonitor
.bottom
;
4346 w
= GetSystemMetrics(SM_CXSCREEN
);
4347 h
= GetSystemMetrics(SM_CYSCREEN
);
4349 SetWindowPos(hwnd
, HWND_TOP
, x
, y
, w
, h
, SWP_FRAMECHANGED
);
4351 /* Tick the menu item in the System menu. */
4352 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4357 * Clear the full-screen attributes.
4359 void clear_full_screen()
4361 DWORD oldstyle
, style
;
4363 /* Reinstate the window furniture. */
4364 style
= oldstyle
= GetWindowLong(hwnd
, GWL_STYLE
);
4365 style
|= WS_CAPTION
| WS_BORDER
;
4366 if (cfg
.resize_action
== RESIZE_DISABLED
)
4367 style
&= ~WS_THICKFRAME
;
4369 style
|= WS_THICKFRAME
;
4371 style
|= WS_VSCROLL
;
4373 style
&= ~WS_VSCROLL
;
4374 if (style
!= oldstyle
) {
4375 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4376 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
4377 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
4381 /* Untick the menu item in the System menu. */
4382 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4387 * Toggle full-screen mode.
4389 void flip_full_screen()
4391 if (is_full_screen()) {
4392 ShowWindow(hwnd
, SW_RESTORE
);
4393 } else if (IsZoomed(hwnd
)) {
4396 SendMessage(hwnd
, WM_FULLSCR_ON_MAX
, 0, 0);
4397 ShowWindow(hwnd
, SW_MAXIMIZE
);