20 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
26 #define IDM_SHOWLOG 0x0010
27 #define IDM_NEWSESS 0x0020
28 #define IDM_DUPSESS 0x0030
29 #define IDM_RECONF 0x0040
30 #define IDM_CLRSB 0x0050
31 #define IDM_RESET 0x0060
32 #define IDM_TEL_AYT 0x0070
33 #define IDM_TEL_BRK 0x0080
34 #define IDM_TEL_SYNCH 0x0090
35 #define IDM_TEL_EC 0x00a0
36 #define IDM_TEL_EL 0x00b0
37 #define IDM_TEL_GA 0x00c0
38 #define IDM_TEL_NOP 0x00d0
39 #define IDM_TEL_ABORT 0x00e0
40 #define IDM_TEL_AO 0x00f0
41 #define IDM_TEL_IP 0x0100
42 #define IDM_TEL_SUSP 0x0110
43 #define IDM_TEL_EOR 0x0120
44 #define IDM_TEL_EOF 0x0130
45 #define IDM_HELP 0x0140
46 #define IDM_ABOUT 0x0150
47 #define IDM_SAVEDSESS 0x0160
48 #define IDM_COPYALL 0x0170
49 #define IDM_FULLSCREEN 0x0180
51 #define IDM_SESSLGP 0x0250 /* log type printable */
52 #define IDM_SESSLGA 0x0260 /* log type all chars */
53 #define IDM_SESSLGE 0x0270 /* log end */
54 #define IDM_SAVED_MIN 0x1000
55 #define IDM_SAVED_MAX 0x2000
57 #define WM_IGNORE_CLIP (WM_XUSER + 2)
59 /* Needed for Chinese support and apparently not always defined. */
61 #define VK_PROCESSKEY 0xE5
64 /* Needed for mouse wheel support and not defined in earlier SDKs. */
66 #define WM_MOUSEWHEEL 0x020A
69 static LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
70 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
71 unsigned char *output
);
72 static void cfgtopalette(void);
73 static void init_palette(void);
74 static void init_fonts(int, int);
75 static void another_font(int);
76 static void deinit_fonts(void);
77 static void set_input_locale(HKL
);
79 /* Window layout information */
80 static void reset_window(int);
81 static int full_screen
= 0;
82 static int extra_width
, extra_height
;
83 static int font_width
, font_height
, font_dualwidth
;
84 static int offset_width
, offset_height
;
85 static int was_zoomed
= 0;
86 static int prev_rows
, prev_cols
;
88 static int pending_netevent
= 0;
89 static WPARAM pend_netevent_wParam
= 0;
90 static LPARAM pend_netevent_lParam
= 0;
91 static void enact_pending_netevent(void);
92 static void flash_window(int mode
);
93 static void flip_full_screen(void);
95 static time_t last_movement
= 0;
99 #define FONT_UNDERLINE 2
100 #define FONT_BOLDUND 3
101 #define FONT_WIDE 0x04
102 #define FONT_HIGH 0x08
103 #define FONT_NARROW 0x10
105 #define FONT_OEM 0x20
106 #define FONT_OEMBOLD 0x21
107 #define FONT_OEMUND 0x22
108 #define FONT_OEMBOLDUND 0x23
110 #define FONT_MAXNO 0x2F
112 static HFONT fonts
[FONT_MAXNO
];
113 static LOGFONT lfont
;
114 static int fontflag
[FONT_MAXNO
];
116 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
124 static COLORREF colours
[NCOLOURS
];
126 static LPLOGPALETTE logpal
;
127 static RGBTRIPLE defpal
[NCOLOURS
];
131 static HBITMAP caretbm
;
133 static int dbltime
, lasttime
, lastact
;
134 static Mouse_Button lastbtn
;
136 /* this allows xterm-style mouse handling. */
137 static int send_raw_mouse
= 0;
138 static int wheel_accumulator
= 0;
140 static char *window_name
, *icon_name
;
142 static int compose_state
= 0;
144 static OSVERSIONINFO osVersion
;
146 /* Dummy routine, only required in plink. */
147 void ldisc_update(int echo
, int edit
)
151 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
153 static char appname
[] = "PuTTY";
158 int guess_width
, guess_height
;
161 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
163 winsock_ver
= MAKEWORD(1, 1);
164 if (WSAStartup(winsock_ver
, &wsadata
)) {
165 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
166 MB_OK
| MB_ICONEXCLAMATION
);
169 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
170 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
171 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
175 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
178 InitCommonControls();
180 /* Ensure a Maximize setting in Explorer doesn't maximise the
185 ZeroMemory(&osVersion
, sizeof(osVersion
));
186 osVersion
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
187 if (!GetVersionEx ( (OSVERSIONINFO
*) &osVersion
)) {
188 MessageBox(NULL
, "Windows refuses to report a version",
189 "PuTTY Fatal Error", MB_OK
| MB_ICONEXCLAMATION
);
195 * See if we can find our Help file.
198 char b
[2048], *p
, *q
, *r
;
200 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
202 p
= strrchr(b
, '\\');
203 if (p
&& p
>= r
) r
= p
+1;
205 if (q
&& q
>= r
) r
= q
+1;
206 strcpy(r
, "putty.hlp");
207 if ( (fp
= fopen(b
, "r")) != NULL
) {
208 help_path
= dupstr(b
);
212 strcpy(r
, "putty.cnt");
213 if ( (fp
= fopen(b
, "r")) != NULL
) {
214 help_has_contents
= TRUE
;
217 help_has_contents
= FALSE
;
221 * Process the command line.
226 default_protocol
= DEFAULT_PROTOCOL
;
227 default_port
= DEFAULT_PORT
;
228 cfg
.logtype
= LGTYP_NONE
;
230 do_defaults(NULL
, &cfg
);
233 while (*p
&& isspace(*p
))
237 * Process command line options first. Yes, this can be
238 * done better, and it will be as soon as I have the
242 char *q
= p
+ strcspn(p
, " \t");
245 tolower(p
[0]) == 's' &&
246 tolower(p
[1]) == 's' && tolower(p
[2]) == 'h') {
247 default_protocol
= cfg
.protocol
= PROT_SSH
;
248 default_port
= cfg
.port
= 22;
249 } else if (q
== p
+ 7 &&
250 tolower(p
[0]) == 'c' &&
251 tolower(p
[1]) == 'l' &&
252 tolower(p
[2]) == 'e' &&
253 tolower(p
[3]) == 'a' &&
254 tolower(p
[4]) == 'n' &&
255 tolower(p
[5]) == 'u' && tolower(p
[6]) == 'p') {
257 * `putty -cleanup'. Remove all registry entries
258 * associated with PuTTY, and also find and delete
259 * the random seed file.
262 "This procedure will remove ALL Registry\n"
263 "entries associated with PuTTY, and will\n"
264 "also remove the PuTTY random seed file.\n"
266 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
267 "SESSIONS. Are you really sure you want\n"
270 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
275 p
= q
+ strspn(q
, " \t");
279 * An initial @ means to activate a saved session.
283 while (i
> 1 && isspace(p
[i
- 1]))
286 do_defaults(p
+ 1, &cfg
);
287 if (!*cfg
.host
&& !do_config()) {
291 } else if (*p
== '&') {
293 * An initial & means we've been given a command line
294 * containing the hex value of a HANDLE for a file
295 * mapping object, which we must then extract as a
300 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
301 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
302 0, 0, sizeof(Config
))) != NULL
) {
305 CloseHandle(filemap
);
306 } else if (!do_config()) {
313 * If the hostname starts with "telnet:", set the
314 * protocol to Telnet and process the string as a
317 if (!strncmp(q
, "telnet:", 7)) {
321 if (q
[0] == '/' && q
[1] == '/')
323 cfg
.protocol
= PROT_TELNET
;
325 while (*p
&& *p
!= ':' && *p
!= '/')
334 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
335 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
337 while (*p
&& !isspace(*p
))
341 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
342 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
343 while (*p
&& isspace(*p
))
358 * Trim leading whitespace off the hostname if it's there.
361 int space
= strspn(cfg
.host
, " \t");
362 memmove(cfg
.host
, cfg
.host
+space
, 1+strlen(cfg
.host
)-space
);
365 /* See if host is of the form user@host */
366 if (cfg
.host
[0] != '\0') {
367 char *atsign
= strchr(cfg
.host
, '@');
368 /* Make sure we're not overflowing the user field */
370 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
371 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
372 cfg
.username
[atsign
- cfg
.host
] = '\0';
374 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
379 * Trim a colon suffix off the hostname if it's there.
381 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
385 * Select protocol. This is farmed out into a table in a
386 * separate file to enable an ssh-free variant.
391 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
392 if (backends
[i
].protocol
== cfg
.protocol
) {
393 back
= backends
[i
].backend
;
397 MessageBox(NULL
, "Unsupported protocol number found",
398 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
404 /* Check for invalid Port number (i.e. zero) */
406 MessageBox(NULL
, "Invalid Port Number",
407 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
414 wndclass
.lpfnWndProc
= WndProc
;
415 wndclass
.cbClsExtra
= 0;
416 wndclass
.cbWndExtra
= 0;
417 wndclass
.hInstance
= inst
;
418 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
419 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
420 wndclass
.hbrBackground
= NULL
;
421 wndclass
.lpszMenuName
= NULL
;
422 wndclass
.lpszClassName
= appname
;
424 RegisterClass(&wndclass
);
429 savelines
= cfg
.savelines
;
435 * Guess some defaults for the window size. This all gets
436 * updated later, so we don't really care too much. However, we
437 * do want the font width/height guesses to correspond to a
438 * large font rather than a small one...
445 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
446 guess_width
= extra_width
+ font_width
* cols
;
447 guess_height
= extra_height
+ font_height
* rows
;
450 HWND w
= GetDesktopWindow();
451 GetWindowRect(w
, &r
);
452 if (guess_width
> r
.right
- r
.left
)
453 guess_width
= r
.right
- r
.left
;
454 if (guess_height
> r
.bottom
- r
.top
)
455 guess_height
= r
.bottom
- r
.top
;
459 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
462 winmode
&= ~(WS_VSCROLL
);
463 if (cfg
.resize_action
== RESIZE_DISABLED
)
464 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
466 exwinmode
|= WS_EX_TOPMOST
;
468 exwinmode
|= WS_EX_CLIENTEDGE
;
469 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
470 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
471 guess_width
, guess_height
,
472 NULL
, NULL
, inst
, NULL
);
476 * Initialise the fonts, simultaneously correcting the guesses
477 * for font_{width,height}.
482 * Correct the guesses for extra_{width,height}.
486 GetWindowRect(hwnd
, &wr
);
487 GetClientRect(hwnd
, &cr
);
488 offset_width
= offset_height
= cfg
.window_border
;
489 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
490 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
494 * Resize the window, now we know what size we _really_ want it
497 guess_width
= extra_width
+ font_width
* cols
;
498 guess_height
= extra_height
+ font_height
* rows
;
499 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
500 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
503 * Set up a caret bitmap, with no content.
507 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
508 bits
= smalloc(size
);
509 memset(bits
, 0, size
);
510 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
513 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
516 * Initialise the scroll bar.
521 si
.cbSize
= sizeof(si
);
522 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
527 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
531 * Start up the telnet connection.
535 char msg
[1024], *title
;
538 error
= back
->init(cfg
.host
, cfg
.port
, &realhost
, cfg
.tcp_nodelay
);
540 sprintf(msg
, "Unable to open connection to\n"
541 "%.800s\n" "%s", cfg
.host
, error
);
542 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
545 window_name
= icon_name
= NULL
;
547 title
= cfg
.wintitle
;
549 sprintf(msg
, "%s - PuTTY", realhost
);
557 session_closed
= FALSE
;
560 * Prepare the mouse handler.
562 lastact
= MA_NOTHING
;
563 lastbtn
= MBT_NOTHING
;
564 dbltime
= GetDoubleClickTime();
567 * Set up the session-control options on the system menu.
570 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
574 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
575 if (cfg
.protocol
== PROT_TELNET
) {
577 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
578 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
579 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
580 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
581 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
582 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
583 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
584 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
585 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
586 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
587 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
588 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
589 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
590 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
591 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
592 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
593 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
595 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
597 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
598 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
599 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
600 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
603 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
604 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
606 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
607 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
608 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
609 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
610 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
611 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
612 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
613 AppendMenu(m
, (cfg
.resize_action
== RESIZE_DISABLED
) ?
614 MF_GRAYED
: MF_ENABLED
, IDM_FULLSCREEN
, "&Full Screen");
615 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
617 AppendMenu(m
, MF_ENABLED
, IDM_HELP
, "&Help");
618 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
622 * Set up the initial input locale.
624 set_input_locale(GetKeyboardLayout(0));
627 * Finally show the window!
629 ShowWindow(hwnd
, show
);
630 SetForegroundWindow(hwnd
);
633 * Open the initial log file if there is one.
638 * Set the palette up.
644 has_focus
= (GetForegroundWindow() == hwnd
);
647 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
648 int timer_id
= 0, long_timer
= 0;
650 while (msg
.message
!= WM_QUIT
) {
651 /* Sometimes DispatchMessage calls routines that use their own
652 * GetMessage loop, setup this timer so we get some control back.
654 * Also call term_update() from the timer so that if the host
655 * is sending data flat out we still do redraws.
657 if (timer_id
&& long_timer
) {
658 KillTimer(hwnd
, timer_id
);
659 long_timer
= timer_id
= 0;
662 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
663 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
664 DispatchMessage(&msg
);
666 /* Make sure we blink everything that needs it. */
669 /* Send the paste buffer if there's anything to send */
672 /* If there's nothing new in the queue then we can do everything
673 * we've delayed, reading the socket, writing, and repainting
676 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
679 if (pending_netevent
) {
680 enact_pending_netevent();
682 /* Force the cursor blink on */
685 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
689 /* Okay there is now nothing to do so we make sure the screen is
690 * completely up to date then tell windows to call us in a little
694 KillTimer(hwnd
, timer_id
);
702 flash_window(1); /* maintain */
704 /* The messages seem unreliable; especially if we're being tricky */
705 has_focus
= (GetForegroundWindow() == hwnd
);
708 /* Hmm, term_update didn't want to do an update too soon ... */
709 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
711 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
713 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
716 /* There's no point rescanning everything in the message queue
717 * so we do an apparently unnecessary wait here
720 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
734 if (cfg
.protocol
== PROT_SSH
) {
745 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
747 char *do_select(SOCKET skt
, int startup
)
752 events
= (FD_CONNECT
| FD_READ
| FD_WRITE
|
753 FD_OOB
| FD_CLOSE
| FD_ACCEPT
);
758 return "do_select(): internal error (hwnd==NULL)";
759 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
760 switch (WSAGetLastError()) {
762 return "Network is down";
764 return "WSAAsyncSelect(): unknown error";
771 * set or clear the "raw mouse message" mode
773 void set_raw_mouse_mode(int activate
)
775 send_raw_mouse
= activate
;
776 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
780 * Print a message box and close the connection.
782 void connection_fatal(char *fmt
, ...)
788 vsprintf(stuff
, fmt
, ap
);
790 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
791 if (cfg
.close_on_exit
== COE_ALWAYS
)
794 session_closed
= TRUE
;
795 SetWindowText(hwnd
, "PuTTY (inactive)");
800 * Actually do the job requested by a WM_NETEVENT
802 static void enact_pending_netevent(void)
804 static int reentering
= 0;
805 extern int select_result(WPARAM
, LPARAM
);
809 return; /* don't unpend the pending */
811 pending_netevent
= FALSE
;
814 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
817 if (ret
== 0 && !session_closed
) {
818 /* Abnormal exits will already have set session_closed and taken
819 * appropriate action. */
820 if (cfg
.close_on_exit
== COE_ALWAYS
||
821 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
823 session_closed
= TRUE
;
824 SetWindowText(hwnd
, "PuTTY (inactive)");
825 MessageBox(hwnd
, "Connection closed by remote host",
826 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
832 * Copy the colour palette from the configuration data into defpal.
833 * This is non-trivial because the colour indices are different.
835 static void cfgtopalette(void)
838 static const int ww
[] = {
839 6, 7, 8, 9, 10, 11, 12, 13,
840 14, 15, 16, 17, 18, 19, 20, 21,
841 0, 1, 2, 3, 4, 4, 5, 5
844 for (i
= 0; i
< 24; i
++) {
846 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
847 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
848 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
853 * Set up the colour palette.
855 static void init_palette(void)
858 HDC hdc
= GetDC(hwnd
);
860 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
861 logpal
= smalloc(sizeof(*logpal
)
862 - sizeof(logpal
->palPalEntry
)
863 + NCOLOURS
* sizeof(PALETTEENTRY
));
864 logpal
->palVersion
= 0x300;
865 logpal
->palNumEntries
= NCOLOURS
;
866 for (i
= 0; i
< NCOLOURS
; i
++) {
867 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
868 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
869 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
870 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
872 pal
= CreatePalette(logpal
);
874 SelectPalette(hdc
, pal
, FALSE
);
876 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
879 ReleaseDC(hwnd
, hdc
);
882 for (i
= 0; i
< NCOLOURS
; i
++)
883 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
887 for (i
= 0; i
< NCOLOURS
; i
++)
888 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
889 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
893 * Initialise all the fonts we will need initially. There may be as many as
894 * three or as few as one. The other (poentially) twentyone fonts are done
895 * if/when they are needed.
899 * - check the font width and height, correcting our guesses if
902 * - verify that the bold font is the same width as the ordinary
903 * one, and engage shadow bolding if not.
905 * - verify that the underlined font is the same width as the
906 * ordinary one (manual underlining by means of line drawing can
907 * be done in a pinch).
909 static void init_fonts(int pick_width
, int pick_height
)
916 int fw_dontcare
, fw_bold
;
918 for (i
= 0; i
< FONT_MAXNO
; i
++)
921 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
924 if (cfg
.fontisbold
) {
925 fw_dontcare
= FW_BOLD
;
928 fw_dontcare
= FW_DONTCARE
;
935 font_height
= pick_height
;
937 font_height
= cfg
.fontheight
;
938 if (font_height
> 0) {
940 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
943 font_width
= pick_width
;
946 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
947 c, OUT_DEFAULT_PRECIS, \
948 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
949 FIXED_PITCH | FF_DONTCARE, cfg.font)
951 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
953 lfont
.lfHeight
= font_height
;
954 lfont
.lfWidth
= font_width
;
955 lfont
.lfEscapement
= 0;
956 lfont
.lfOrientation
= 0;
957 lfont
.lfWeight
= fw_dontcare
;
958 lfont
.lfItalic
= FALSE
;
959 lfont
.lfUnderline
= FALSE
;
960 lfont
.lfStrikeOut
= FALSE
;
961 lfont
.lfCharSet
= cfg
.fontcharset
;
962 lfont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
963 lfont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
964 lfont
.lfQuality
= DEFAULT_QUALITY
;
965 lfont
.lfPitchAndFamily
= FIXED_PITCH
| FF_DONTCARE
;
966 strncpy(lfont
.lfFaceName
, cfg
.font
, LF_FACESIZE
);
968 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
969 GetTextMetrics(hdc
, &tm
);
971 if (pick_width
== 0 || pick_height
== 0) {
972 font_height
= tm
.tmHeight
;
973 font_width
= tm
.tmAveCharWidth
;
975 font_dualwidth
= (tm
.tmAveCharWidth
!= tm
.tmMaxCharWidth
);
977 #ifdef RDB_DEBUG_PATCH
978 debug(23, "Primary font H=%d, AW=%d, MW=%d",
979 tm
.tmHeight
, tm
.tmAveCharWidth
, tm
.tmMaxCharWidth
);
984 DWORD cset
= tm
.tmCharSet
;
985 memset(&info
, 0xFF, sizeof(info
));
987 /* !!! Yes the next line is right */
988 if (cset
== OEM_CHARSET
)
989 font_codepage
= GetOEMCP();
991 if (TranslateCharsetInfo
992 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
997 GetCPInfo(font_codepage
, &cpinfo
);
998 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
1001 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
1004 * Some fonts, e.g. 9-pt Courier, draw their underlines
1005 * outside their character cell. We successfully prevent
1006 * screen corruption by clipping the text output, but then
1007 * we lose the underline completely. Here we try to work
1008 * out whether this is such a font, and if it is, we set a
1009 * flag that causes underlines to be drawn by hand.
1011 * Having tried other more sophisticated approaches (such
1012 * as examining the TEXTMETRIC structure or requesting the
1013 * height of a string), I think we'll do this the brute
1014 * force way: we create a small bitmap, draw an underlined
1015 * space on it, and test to see whether any pixels are
1016 * foreground-coloured. (Since we expect the underline to
1017 * go all the way across the character cell, we only search
1018 * down a single column of the bitmap, half way across.)
1022 HBITMAP und_bm
, und_oldbm
;
1026 und_dc
= CreateCompatibleDC(hdc
);
1027 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
1028 und_oldbm
= SelectObject(und_dc
, und_bm
);
1029 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
1030 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
1031 SetTextColor(und_dc
, RGB(255, 255, 255));
1032 SetBkColor(und_dc
, RGB(0, 0, 0));
1033 SetBkMode(und_dc
, OPAQUE
);
1034 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
1036 for (i
= 0; i
< font_height
; i
++) {
1037 c
= GetPixel(und_dc
, font_width
/ 2, i
);
1038 if (c
!= RGB(0, 0, 0))
1041 SelectObject(und_dc
, und_oldbm
);
1042 DeleteObject(und_bm
);
1045 und_mode
= UND_LINE
;
1046 DeleteObject(fonts
[FONT_UNDERLINE
]);
1047 fonts
[FONT_UNDERLINE
] = 0;
1051 if (bold_mode
== BOLD_FONT
) {
1052 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
1056 descent
= tm
.tmAscent
+ 1;
1057 if (descent
>= font_height
)
1058 descent
= font_height
- 1;
1060 for (i
= 0; i
< 3; i
++) {
1062 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
1063 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
1070 ReleaseDC(hwnd
, hdc
);
1072 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1073 und_mode
= UND_LINE
;
1074 DeleteObject(fonts
[FONT_UNDERLINE
]);
1075 fonts
[FONT_UNDERLINE
] = 0;
1078 if (bold_mode
== BOLD_FONT
&&
1079 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1080 bold_mode
= BOLD_SHADOW
;
1081 DeleteObject(fonts
[FONT_BOLD
]);
1082 fonts
[FONT_BOLD
] = 0;
1084 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1089 static void another_font(int fontno
)
1092 int fw_dontcare
, fw_bold
;
1096 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1099 basefont
= (fontno
& ~(FONT_BOLDUND
));
1100 if (basefont
!= fontno
&& !fontflag
[basefont
])
1101 another_font(basefont
);
1103 if (cfg
.fontisbold
) {
1104 fw_dontcare
= FW_BOLD
;
1107 fw_dontcare
= FW_DONTCARE
;
1111 c
= cfg
.fontcharset
;
1117 if (fontno
& FONT_WIDE
)
1119 if (fontno
& FONT_NARROW
)
1121 if (fontno
& FONT_OEM
)
1123 if (fontno
& FONT_BOLD
)
1125 if (fontno
& FONT_UNDERLINE
)
1129 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1130 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1131 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1132 FIXED_PITCH
| FF_DONTCARE
, s
);
1134 fontflag
[fontno
] = 1;
1137 static void deinit_fonts(void)
1140 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1142 DeleteObject(fonts
[i
]);
1148 void request_resize(int w
, int h
)
1152 /* If the window is maximized supress resizing attempts */
1153 if (IsZoomed(hwnd
)) {
1154 if (cfg
.resize_action
== RESIZE_TERM
)
1158 if (cfg
.resize_action
== RESIZE_DISABLED
) return;
1159 if (h
== rows
&& w
== cols
) return;
1161 /* Sanity checks ... */
1163 static int first_time
= 1;
1166 switch (first_time
) {
1168 /* Get the size of the screen */
1169 if (GetClientRect(GetDesktopWindow(), &ss
))
1170 /* first_time = 0 */ ;
1176 /* Make sure the values are sane */
1177 width
= (ss
.right
- ss
.left
- extra_width
) / 4;
1178 height
= (ss
.bottom
- ss
.top
- extra_height
) / 6;
1180 if (w
> width
|| h
> height
)
1189 term_size(h
, w
, cfg
.savelines
);
1191 if (cfg
.resize_action
!= RESIZE_FONT
&& !IsZoomed(hwnd
)) {
1192 width
= extra_width
+ font_width
* w
;
1193 height
= extra_height
+ font_height
* h
;
1195 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1196 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1197 SWP_NOMOVE
| SWP_NOZORDER
);
1201 InvalidateRect(hwnd
, NULL
, TRUE
);
1204 static void reset_window(int reinit
) {
1206 * This function decides how to resize or redraw when the
1207 * user changes something.
1209 * This function doesn't like to change the terminal size but if the
1210 * font size is locked that may be it's only soluion.
1212 int win_width
, win_height
;
1215 #ifdef RDB_DEBUG_PATCH
1216 debug((27, "reset_window()"));
1219 /* Current window sizes ... */
1220 GetWindowRect(hwnd
, &wr
);
1221 GetClientRect(hwnd
, &cr
);
1223 win_width
= cr
.right
- cr
.left
;
1224 win_height
= cr
.bottom
- cr
.top
;
1226 if (cfg
.resize_action
== RESIZE_DISABLED
) reinit
= 2;
1228 /* Are we being forced to reload the fonts ? */
1230 #ifdef RDB_DEBUG_PATCH
1231 debug((27, "reset_window() -- Forced deinit"));
1237 /* Oh, looks like we're minimised */
1238 if (win_width
== 0 || win_height
== 0)
1241 /* Is the window out of position ? */
1243 (offset_width
!= (win_width
-font_width
*cols
)/2 ||
1244 offset_height
!= (win_height
-font_height
*rows
)/2) ){
1245 offset_width
= (win_width
-font_width
*cols
)/2;
1246 offset_height
= (win_height
-font_height
*rows
)/2;
1247 InvalidateRect(hwnd
, NULL
, TRUE
);
1248 #ifdef RDB_DEBUG_PATCH
1249 debug((27, "reset_window() -> Reposition terminal"));
1253 if (IsZoomed(hwnd
)) {
1254 /* We're fullscreen, this means we must not change the size of
1255 * the window so it's the font size or the terminal itself.
1258 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1259 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1261 if (cfg
.resize_action
!= RESIZE_TERM
) {
1262 if ( font_width
!= win_width
/cols
||
1263 font_height
!= win_height
/rows
) {
1265 init_fonts(win_width
/cols
, win_height
/rows
);
1266 offset_width
= (win_width
-font_width
*cols
)/2;
1267 offset_height
= (win_height
-font_height
*rows
)/2;
1268 InvalidateRect(hwnd
, NULL
, TRUE
);
1269 #ifdef RDB_DEBUG_PATCH
1270 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1271 font_width
, font_height
));
1275 if ( font_width
!= win_width
/cols
||
1276 font_height
!= win_height
/rows
) {
1277 /* Our only choice at this point is to change the
1278 * size of the terminal; Oh well.
1280 term_size( win_height
/font_height
, win_width
/font_width
,
1282 offset_width
= (win_width
-font_width
*cols
)/2;
1283 offset_height
= (win_height
-font_height
*rows
)/2;
1284 InvalidateRect(hwnd
, NULL
, TRUE
);
1285 #ifdef RDB_DEBUG_PATCH
1286 debug((27, "reset_window() -> Zoomed term_size"));
1293 /* Hmm, a force re-init means we should ignore the current window
1294 * so we resize to the default font size.
1297 #ifdef RDB_DEBUG_PATCH
1298 debug((27, "reset_window() -> Forced re-init"));
1301 offset_width
= offset_height
= cfg
.window_border
;
1302 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1303 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1305 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1306 win_height
!= font_height
*rows
+ offset_height
*2) {
1308 /* If this is too large windows will resize it to the maximum
1309 * allowed window size, we will then be back in here and resize
1310 * the font or terminal to fit.
1312 SetWindowPos(hwnd
, NULL
, 0, 0,
1313 font_width
*cols
+ extra_width
,
1314 font_height
*rows
+ extra_height
,
1315 SWP_NOMOVE
| SWP_NOZORDER
);
1318 InvalidateRect(hwnd
, NULL
, TRUE
);
1322 /* Okay the user doesn't want us to change the font so we try the
1323 * window. But that may be too big for the screen which forces us
1324 * to change the terminal.
1326 if ((cfg
.resize_action
== RESIZE_TERM
&& reinit
<=0) ||
1327 (cfg
.resize_action
== RESIZE_EITHER
&& reinit
<0) ||
1329 offset_width
= offset_height
= cfg
.window_border
;
1330 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1331 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1333 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1334 win_height
!= font_height
*rows
+ offset_height
*2) {
1339 GetClientRect(GetDesktopWindow(), &ss
);
1340 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1341 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1344 if ( rows
> height
|| cols
> width
) {
1345 if (cfg
.resize_action
== RESIZE_EITHER
) {
1346 /* Make the font the biggest we can */
1348 font_width
= (ss
.right
- ss
.left
- extra_width
)/cols
;
1350 font_height
= (ss
.bottom
- ss
.top
- extra_height
)/rows
;
1353 init_fonts(font_width
, font_height
);
1355 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1356 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1358 if ( height
> rows
) height
= rows
;
1359 if ( width
> cols
) width
= cols
;
1360 term_size(height
, width
, cfg
.savelines
);
1361 #ifdef RDB_DEBUG_PATCH
1362 debug((27, "reset_window() -> term resize to (%d,%d)",
1368 SetWindowPos(hwnd
, NULL
, 0, 0,
1369 font_width
*cols
+ extra_width
,
1370 font_height
*rows
+ extra_height
,
1371 SWP_NOMOVE
| SWP_NOZORDER
);
1373 InvalidateRect(hwnd
, NULL
, TRUE
);
1374 #ifdef RDB_DEBUG_PATCH
1375 debug((27, "reset_window() -> window resize to (%d,%d)",
1376 font_width
*cols
+ extra_width
,
1377 font_height
*rows
+ extra_height
));
1383 /* We're allowed to or must change the font but do we want to ? */
1385 if (font_width
!= (win_width
-cfg
.window_border
*2)/cols
||
1386 font_height
!= (win_height
-cfg
.window_border
*2)/rows
) {
1389 init_fonts((win_width
-cfg
.window_border
*2)/cols
,
1390 (win_height
-cfg
.window_border
*2)/rows
);
1391 offset_width
= (win_width
-font_width
*cols
)/2;
1392 offset_height
= (win_height
-font_height
*rows
)/2;
1394 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+offset_width
*2;
1395 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1397 InvalidateRect(hwnd
, NULL
, TRUE
);
1398 #ifdef RDB_DEBUG_PATCH
1399 debug((25, "reset_window() -> font resize to (%d,%d)",
1400 font_width
, font_height
));
1405 static void set_input_locale(HKL kl
)
1409 GetLocaleInfo(LOWORD(kl
), LOCALE_IDEFAULTANSICODEPAGE
,
1410 lbuf
, sizeof(lbuf
));
1412 kbd_codepage
= atoi(lbuf
);
1415 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
, int alt
)
1417 int thistime
= GetMessageTime();
1419 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1420 lastbtn
= MBT_NOTHING
;
1421 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
, alt
);
1425 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1426 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1427 lastact
== MA_2CLK ? MA_3CLK
:
1428 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1433 if (lastact
!= MA_NOTHING
)
1434 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
, alt
);
1435 lasttime
= thistime
;
1439 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1440 * into a cooked one (SELECT, EXTEND, PASTE).
1442 Mouse_Button
translate_button(Mouse_Button button
)
1444 if (button
== MBT_LEFT
)
1446 if (button
== MBT_MIDDLE
)
1447 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1448 if (button
== MBT_RIGHT
)
1449 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1450 return 0; /* shouldn't happen */
1453 static void show_mouseptr(int show
)
1455 static int cursor_visible
= 1;
1456 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1458 if (cursor_visible
&& !show
)
1460 else if (!cursor_visible
&& show
)
1462 cursor_visible
= show
;
1465 static int is_alt_pressed(void)
1468 int r
= GetKeyboardState(keystate
);
1471 if (keystate
[VK_MENU
] & 0x80)
1473 if (keystate
[VK_RMENU
] & 0x80)
1478 static int resizing
;
1480 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1481 WPARAM wParam
, LPARAM lParam
)
1484 static int ignore_clip
= FALSE
;
1485 static int need_backend_resize
= FALSE
;
1489 if (pending_netevent
)
1490 enact_pending_netevent();
1496 if (cfg
.ping_interval
> 0) {
1499 if (now
- last_movement
> cfg
.ping_interval
) {
1500 back
->special(TS_PING
);
1501 last_movement
= now
;
1504 net_pending_errors();
1510 if (!cfg
.warn_on_close
|| session_closed
||
1512 "Are you sure you want to close this session?",
1513 "PuTTY Exit Confirmation",
1514 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1515 DestroyWindow(hwnd
);
1522 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1534 PROCESS_INFORMATION pi
;
1535 HANDLE filemap
= NULL
;
1537 if (wParam
== IDM_DUPSESS
) {
1539 * Allocate a file-mapping memory chunk for the
1542 SECURITY_ATTRIBUTES sa
;
1545 sa
.nLength
= sizeof(sa
);
1546 sa
.lpSecurityDescriptor
= NULL
;
1547 sa
.bInheritHandle
= TRUE
;
1548 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1551 0, sizeof(Config
), NULL
);
1553 p
= (Config
*) MapViewOfFile(filemap
,
1555 0, 0, sizeof(Config
));
1557 *p
= cfg
; /* structure copy */
1561 sprintf(c
, "putty &%p", filemap
);
1563 } else if (wParam
== IDM_SAVEDSESS
) {
1564 if ((lParam
- IDM_SAVED_MIN
) / 16 < nsessions
) {
1566 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1567 cl
= smalloc(16 + strlen(session
));
1568 /* 8, but play safe */
1571 /* not a very important failure mode */
1573 sprintf(cl
, "putty @%s", session
);
1581 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1583 si
.lpReserved
= NULL
;
1584 si
.lpDesktop
= NULL
;
1588 si
.lpReserved2
= NULL
;
1589 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1590 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1593 CloseHandle(filemap
);
1603 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1606 if (!do_reconfig(hwnd
))
1610 /* Disable full-screen if resizing forbidden */
1611 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
1612 EnableMenuItem(m
, IDM_FULLSCREEN
, MF_BYCOMMAND
|
1613 (cfg
.resize_action
== RESIZE_DISABLED
)
1614 ? MF_GRAYED
: MF_ENABLED
);
1615 /* Gracefully unzoom if necessary */
1617 (cfg
.resize_action
== RESIZE_DISABLED
)) {
1622 if (strcmp(prev_cfg
.logfilename
, cfg
.logfilename
) ||
1623 prev_cfg
.logtype
!= cfg
.logtype
) {
1624 logfclose(); /* reset logging */
1630 * Flush the line discipline's edit buffer in the
1631 * case where local editing has just been disabled.
1633 ldisc_send(NULL
, 0, 0);
1641 /* Screen size changed ? */
1642 if (cfg
.height
!= prev_cfg
.height
||
1643 cfg
.width
!= prev_cfg
.width
||
1644 cfg
.savelines
!= prev_cfg
.savelines
||
1645 cfg
.resize_action
== RESIZE_FONT
||
1646 cfg
.resize_action
== RESIZE_DISABLED
)
1647 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1649 /* Enable or disable the scroll bar, etc */
1651 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1652 LONG nexflag
, exflag
=
1653 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1656 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1657 if (cfg
.alwaysontop
) {
1658 nexflag
|= WS_EX_TOPMOST
;
1659 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1660 SWP_NOMOVE
| SWP_NOSIZE
);
1662 nexflag
&= ~(WS_EX_TOPMOST
);
1663 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1664 SWP_NOMOVE
| SWP_NOSIZE
);
1667 if (cfg
.sunken_edge
)
1668 nexflag
|= WS_EX_CLIENTEDGE
;
1670 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1674 cfg
.scrollbar_in_fullscreen
: cfg
.scrollbar
)
1677 nflg
&= ~WS_VSCROLL
;
1678 if (cfg
.resize_action
== RESIZE_DISABLED
)
1679 nflg
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1681 nflg
|= (WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1683 if (nflg
!= flag
|| nexflag
!= exflag
) {
1685 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1686 if (nexflag
!= exflag
)
1687 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1689 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1690 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1691 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1699 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1704 set_title(cfg
.wintitle
);
1705 if (IsIconic(hwnd
)) {
1707 cfg
.win_name_always ? window_name
:
1711 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1712 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1713 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1714 cfg
.fontheight
!= prev_cfg
.fontheight
||
1715 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1716 cfg
.vtmode
!= prev_cfg
.vtmode
||
1717 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1718 cfg
.resize_action
== RESIZE_DISABLED
||
1719 cfg
.resize_action
== RESIZE_EITHER
||
1720 (cfg
.resize_action
!= prev_cfg
.resize_action
))
1723 InvalidateRect(hwnd
, NULL
, TRUE
);
1724 reset_window(init_lvl
);
1725 net_pending_errors();
1738 back
->special(TS_AYT
);
1739 net_pending_errors();
1742 back
->special(TS_BRK
);
1743 net_pending_errors();
1746 back
->special(TS_SYNCH
);
1747 net_pending_errors();
1750 back
->special(TS_EC
);
1751 net_pending_errors();
1754 back
->special(TS_EL
);
1755 net_pending_errors();
1758 back
->special(TS_GA
);
1759 net_pending_errors();
1762 back
->special(TS_NOP
);
1763 net_pending_errors();
1766 back
->special(TS_ABORT
);
1767 net_pending_errors();
1770 back
->special(TS_AO
);
1771 net_pending_errors();
1774 back
->special(TS_IP
);
1775 net_pending_errors();
1778 back
->special(TS_SUSP
);
1779 net_pending_errors();
1782 back
->special(TS_EOR
);
1783 net_pending_errors();
1786 back
->special(TS_EOF
);
1787 net_pending_errors();
1793 WinHelp(hwnd
, help_path
,
1794 help_has_contents ? HELP_FINDER
: HELP_CONTENTS
, 0);
1798 * We get this if the System menu has been activated
1805 * We get this if the System menu has been activated
1806 * using the keyboard. This might happen from within
1807 * TranslateKey, in which case it really wants to be
1808 * followed by a `space' character to actually _bring
1809 * the menu up_ rather than just sitting there in
1810 * `ready to appear' state.
1812 show_mouseptr(1); /* make sure pointer is visible */
1814 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1816 case IDM_FULLSCREEN
:
1820 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1821 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1826 #define X_POS(l) ((int)(short)LOWORD(l))
1827 #define Y_POS(l) ((int)(short)HIWORD(l))
1829 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1830 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1831 #define WHEEL_DELTA 120
1834 wheel_accumulator
+= (short) HIWORD(wParam
);
1835 wParam
= LOWORD(wParam
);
1837 /* process events when the threshold is reached */
1838 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
1841 /* reduce amount for next time */
1842 if (wheel_accumulator
> 0) {
1844 wheel_accumulator
-= WHEEL_DELTA
;
1845 } else if (wheel_accumulator
< 0) {
1847 wheel_accumulator
+= WHEEL_DELTA
;
1851 if (send_raw_mouse
) {
1852 /* send a mouse-down followed by a mouse up */
1856 TO_CHR_X(X_POS(lParam
)),
1857 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1858 wParam
& MK_CONTROL
, is_alt_pressed());
1859 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1860 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1861 wParam
& MK_CONTROL
, is_alt_pressed());
1863 /* trigger a scroll */
1865 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
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 (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
)) {
1944 if (wParam
& MK_LBUTTON
)
1946 else if (wParam
& MK_MBUTTON
)
1950 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1951 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1952 wParam
& MK_CONTROL
, is_alt_pressed());
1955 case WM_NCMOUSEMOVE
:
1957 noise_ultralight(lParam
);
1959 case WM_IGNORE_CLIP
:
1960 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1962 case WM_DESTROYCLIPBOARD
:
1965 ignore_clip
= FALSE
;
1971 hdc
= BeginPaint(hwnd
, &p
);
1973 SelectPalette(hdc
, pal
, TRUE
);
1974 RealizePalette(hdc
);
1977 (p
.rcPaint
.left
-offset_width
)/font_width
,
1978 (p
.rcPaint
.top
-offset_height
)/font_height
,
1979 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
1980 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
1983 p
.rcPaint
.left
< offset_width
||
1984 p
.rcPaint
.top
< offset_height
||
1985 p
.rcPaint
.right
>= offset_width
+ font_width
*cols
||
1986 p
.rcPaint
.bottom
>= offset_height
+ font_height
*rows
)
1988 HBRUSH fillcolour
, oldbrush
;
1990 fillcolour
= CreateSolidBrush (
1991 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1992 oldbrush
= SelectObject(hdc
, fillcolour
);
1993 edge
= CreatePen(PS_SOLID
, 0,
1994 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1995 oldpen
= SelectObject(hdc
, edge
);
1997 ExcludeClipRect(hdc
,
1998 offset_width
, offset_height
,
1999 offset_width
+font_width
*cols
,
2000 offset_height
+font_height
*rows
);
2002 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
2003 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
2005 // SelectClipRgn(hdc, NULL);
2007 SelectObject(hdc
, oldbrush
);
2008 DeleteObject(fillcolour
);
2009 SelectObject(hdc
, oldpen
);
2012 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
2013 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
2019 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2020 * but the only one that's likely to try to overload us is FD_READ.
2021 * This means buffering just one is fine.
2023 if (pending_netevent
)
2024 enact_pending_netevent();
2026 pending_netevent
= TRUE
;
2027 pend_netevent_wParam
= wParam
;
2028 pend_netevent_lParam
= lParam
;
2029 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
2030 enact_pending_netevent();
2032 time(&last_movement
);
2036 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
2038 flash_window(0); /* stop */
2044 if (full_screen
) flip_full_screen();
2051 case WM_ENTERSIZEMOVE
:
2052 #ifdef RDB_DEBUG_PATCH
2053 debug((27, "WM_ENTERSIZEMOVE"));
2057 need_backend_resize
= FALSE
;
2059 case WM_EXITSIZEMOVE
:
2062 #ifdef RDB_DEBUG_PATCH
2063 debug((27, "WM_EXITSIZEMOVE"));
2065 if (need_backend_resize
) {
2066 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
2067 InvalidateRect(hwnd
, NULL
, TRUE
);
2072 * This does two jobs:
2073 * 1) Keep the sizetip uptodate
2074 * 2) Make sure the window size is _stepped_ in units of the font size.
2076 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2077 int width
, height
, w
, h
, ew
, eh
;
2078 LPRECT r
= (LPRECT
) lParam
;
2080 if ( !need_backend_resize
&& cfg
.resize_action
== RESIZE_EITHER
&&
2081 (cfg
.height
!= rows
|| cfg
.width
!= cols
)) {
2083 * Great! It seems that both the terminal size and the
2084 * font size have been changed and the user is now dragging.
2086 * It will now be difficult to get back to the configured
2089 * This would be easier but it seems to be too confusing.
2091 term_size(cfg.height, cfg.width, cfg.savelines);
2094 cfg
.height
=rows
; cfg
.width
=cols
;
2096 InvalidateRect(hwnd
, NULL
, TRUE
);
2097 need_backend_resize
= TRUE
;
2100 width
= r
->right
- r
->left
- extra_width
;
2101 height
= r
->bottom
- r
->top
- extra_height
;
2102 w
= (width
+ font_width
/ 2) / font_width
;
2105 h
= (height
+ font_height
/ 2) / font_height
;
2108 UpdateSizeTip(hwnd
, w
, h
);
2109 ew
= width
- w
* font_width
;
2110 eh
= height
- h
* font_height
;
2112 if (wParam
== WMSZ_LEFT
||
2113 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2119 if (wParam
== WMSZ_TOP
||
2120 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2130 int width
, height
, w
, h
, rv
= 0;
2131 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
2132 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2133 LPRECT r
= (LPRECT
) lParam
;
2135 width
= r
->right
- r
->left
- ex_width
;
2136 height
= r
->bottom
- r
->top
- ex_height
;
2138 w
= (width
+ cols
/2)/cols
;
2139 h
= (height
+ rows
/2)/rows
;
2140 if ( r
->right
!= r
->left
+ w
*cols
+ ex_width
)
2143 if (wParam
== WMSZ_LEFT
||
2144 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2145 r
->left
= r
->right
- w
*cols
- ex_width
;
2147 r
->right
= r
->left
+ w
*cols
+ ex_width
;
2149 if (r
->bottom
!= r
->top
+ h
*rows
+ ex_height
)
2152 if (wParam
== WMSZ_TOP
||
2153 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2154 r
->top
= r
->bottom
- h
*rows
- ex_height
;
2156 r
->bottom
= r
->top
+ h
*rows
+ ex_height
;
2160 /* break; (never reached) */
2162 #ifdef RDB_DEBUG_PATCH
2163 debug((27, "WM_SIZE %s (%d,%d)",
2164 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2165 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2166 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2167 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2169 LOWORD(lParam
), HIWORD(lParam
)));
2171 if (wParam
== SIZE_MINIMIZED
)
2173 cfg
.win_name_always ? window_name
: icon_name
);
2174 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2175 SetWindowText(hwnd
, window_name
);
2177 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2178 /* A resize, well it better be a minimize. */
2182 int width
, height
, w
, h
;
2184 width
= LOWORD(lParam
);
2185 height
= HIWORD(lParam
);
2188 if (wParam
== SIZE_MAXIMIZED
&& !was_zoomed
) {
2192 if (cfg
.resize_action
== RESIZE_TERM
) {
2193 w
= width
/ font_width
;
2195 h
= height
/ font_height
;
2198 term_size(h
, w
, cfg
.savelines
);
2201 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2203 if (cfg
.resize_action
== RESIZE_TERM
)
2204 term_size(prev_rows
, prev_cols
, cfg
.savelines
);
2205 if (cfg
.resize_action
!= RESIZE_FONT
)
2210 /* This is an unexpected resize, these will normally happen
2211 * if the window is too large. Probably either the user
2212 * selected a huge font or the screen size has changed.
2214 * This is also called with minimize.
2216 else reset_window(-1);
2220 * Don't call back->size in mid-resize. (To prevent
2221 * massive numbers of resize events getting sent
2222 * down the connection during an NT opaque drag.)
2225 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2226 need_backend_resize
= TRUE
;
2227 w
= (width
-cfg
.window_border
*2) / font_width
;
2229 h
= (height
-cfg
.window_border
*2) / font_height
;
2240 switch (LOWORD(wParam
)) {
2254 term_scroll(0, +rows
/ 2);
2257 term_scroll(0, -rows
/ 2);
2259 case SB_THUMBPOSITION
:
2261 term_scroll(1, HIWORD(wParam
));
2265 case WM_PALETTECHANGED
:
2266 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2267 HDC hdc
= get_ctx();
2269 if (RealizePalette(hdc
) > 0)
2275 case WM_QUERYNEWPALETTE
:
2277 HDC hdc
= get_ctx();
2279 if (RealizePalette(hdc
) > 0)
2291 * Add the scan code and keypress timing to the random
2294 noise_ultralight(lParam
);
2297 * We don't do TranslateMessage since it disassociates the
2298 * resulting CHAR message from the KEYDOWN that sparked it,
2299 * which we occasionally don't want. Instead, we process
2300 * KEYDOWN, and call the Win32 translator functions so that
2301 * we get the translations under _our_ control.
2304 unsigned char buf
[20];
2307 if (wParam
== VK_PROCESSKEY
) {
2310 m
.message
= WM_KEYDOWN
;
2312 m
.lParam
= lParam
& 0xdfff;
2313 TranslateMessage(&m
);
2315 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2317 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2321 * Interrupt an ongoing paste. I'm not sure
2322 * this is sensible, but for the moment it's
2323 * preferable to having to faff about buffering
2329 * We need not bother about stdin backlogs
2330 * here, because in GUI PuTTY we can't do
2331 * anything about it anyway; there's no means
2332 * of asking Windows to hold off on KEYDOWN
2333 * messages. We _have_ to buffer everything
2336 ldisc_send(buf
, len
, 1);
2341 net_pending_errors();
2343 case WM_INPUTLANGCHANGE
:
2344 /* wParam == Font number */
2345 /* lParam == Locale */
2346 set_input_locale((HKL
)lParam
);
2349 if(wParam
== IMN_SETOPENSTATUS
) {
2350 HIMC hImc
= ImmGetContext(hwnd
);
2351 ImmSetCompositionFont(hImc
, &lfont
);
2352 ImmReleaseContext(hwnd
, hImc
);
2356 case WM_IME_COMPOSITION
:
2362 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2363 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2365 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2366 break; /* fall back to DefWindowProc */
2368 hIMC
= ImmGetContext(hwnd
);
2369 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2372 buff
= (char*) smalloc(n
);
2373 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2374 luni_send((unsigned short *)buff
, n
/ 2, 1);
2377 ImmReleaseContext(hwnd
, hIMC
);
2382 if (wParam
& 0xFF00) {
2383 unsigned char buf
[2];
2386 buf
[0] = wParam
>> 8;
2387 lpage_send(kbd_codepage
, buf
, 2, 1);
2389 char c
= (unsigned char) wParam
;
2390 lpage_send(kbd_codepage
, &c
, 1, 1);
2396 * Nevertheless, we are prepared to deal with WM_CHAR
2397 * messages, should they crop up. So if someone wants to
2398 * post the things to us as part of a macro manoeuvre,
2399 * we're ready to cope.
2402 char c
= (unsigned char)wParam
;
2403 lpage_send(CP_ACP
, &c
, 1, 1);
2407 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2408 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2413 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2417 * Move the system caret. (We maintain one, even though it's
2418 * invisible, for the benefit of blind people: apparently some
2419 * helper software tracks the system caret, so we should arrange to
2422 void sys_cursor(int x
, int y
)
2427 if (!has_focus
) return;
2429 SetCaretPos(x
* font_width
+ offset_width
,
2430 y
* font_height
+ offset_height
);
2432 /* IMM calls on Win98 and beyond only */
2433 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2435 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2436 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2438 /* we should have the IMM functions */
2439 hIMC
= ImmGetContext(hwnd
);
2440 cf
.dwStyle
= CFS_POINT
;
2441 cf
.ptCurrentPos
.x
= x
* font_width
+ offset_width
;
2442 cf
.ptCurrentPos
.y
= y
* font_height
+ offset_height
;
2443 ImmSetCompositionWindow(hIMC
, &cf
);
2445 ImmReleaseContext(hwnd
, hIMC
);
2449 * Draw a line of text in the window, at given character
2450 * coordinates, in given attributes.
2452 * We are allowed to fiddle with the contents of `text'.
2454 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2455 unsigned long attr
, int lattr
)
2458 int nfg
, nbg
, nfont
;
2461 int force_manual_underline
= 0;
2462 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2463 int char_width
= fnt_width
;
2464 int text_adjust
= 0;
2465 static int *IpDx
= 0, IpDxLEN
= 0;
2467 if (attr
& ATTR_WIDE
)
2470 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2472 if (len
> IpDxLEN
) {
2474 IpDx
= smalloc((len
+ 16) * sizeof(int));
2475 IpDxLEN
= (len
+ 16);
2477 for (i
= 0; i
< IpDxLEN
; i
++)
2478 IpDx
[i
] = char_width
;
2481 /* Only want the left half of double width lines */
2482 if (lattr
!= LATTR_NORM
&& x
*2 >= cols
)
2490 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
2491 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2492 attr
^= ATTR_CUR_XOR
;
2496 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2497 /* Assume a poorman font is borken in other ways too. */
2507 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2510 if (attr
& ATTR_NARROW
)
2511 nfont
|= FONT_NARROW
;
2513 /* Special hack for the VT100 linedraw glyphs. */
2514 if ((attr
& CSET_MASK
) == 0x2300) {
2515 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2516 switch ((unsigned char) (text
[0])) {
2518 text_adjust
= -2 * font_height
/ 5;
2521 text_adjust
= -1 * font_height
/ 5;
2524 text_adjust
= font_height
/ 5;
2527 text_adjust
= 2 * font_height
/ 5;
2530 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2533 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2534 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2535 if (attr
& ATTR_UNDER
) {
2536 attr
&= ~ATTR_UNDER
;
2537 force_manual_underline
= 1;
2542 /* Anything left as an original character set is unprintable. */
2543 if (DIRECT_CHAR(attr
)) {
2546 memset(text
, 0xFD, len
);
2550 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2553 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2554 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2555 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2557 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2558 nfont
|= FONT_UNDERLINE
;
2559 another_font(nfont
);
2560 if (!fonts
[nfont
]) {
2561 if (nfont
& FONT_UNDERLINE
)
2562 force_manual_underline
= 1;
2563 /* Don't do the same for manual bold, it could be bad news. */
2565 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2567 another_font(nfont
);
2569 nfont
= FONT_NORMAL
;
2570 if (attr
& ATTR_REVERSE
) {
2575 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2577 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2581 SelectObject(hdc
, fonts
[nfont
]);
2582 SetTextColor(hdc
, fg
);
2583 SetBkColor(hdc
, bg
);
2584 SetBkMode(hdc
, OPAQUE
);
2587 line_box
.right
= x
+ char_width
* len
;
2588 line_box
.bottom
= y
+ font_height
;
2590 /* Only want the left half of double width lines */
2591 if (line_box
.right
> font_width
*cols
+offset_width
)
2592 line_box
.right
= font_width
*cols
+offset_width
;
2594 /* We're using a private area for direct to font. (512 chars.) */
2595 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2596 /* Ho Hum, dbcs fonts are a PITA! */
2597 /* To display on W9x I have to convert to UCS */
2598 static wchar_t *uni_buf
= 0;
2599 static int uni_len
= 0;
2601 if (len
> uni_len
) {
2603 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2606 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2607 uni_buf
[nlen
] = 0xFFFD;
2608 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2609 IpDx
[nlen
] += char_width
;
2610 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2611 text
+mptr
, 2, uni_buf
+nlen
, 1);
2616 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2617 text
+mptr
, 1, uni_buf
+nlen
, 1);
2625 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2626 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2627 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2628 SetBkMode(hdc
, TRANSPARENT
);
2629 ExtTextOutW(hdc
, x
- 1,
2630 y
- font_height
* (lattr
==
2631 LATTR_BOT
) + text_adjust
,
2632 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2636 } else if (DIRECT_FONT(attr
)) {
2638 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2639 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2640 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2641 SetBkMode(hdc
, TRANSPARENT
);
2643 /* GRR: This draws the character outside it's box and can leave
2644 * 'droppings' even with the clip box! I suppose I could loop it
2645 * one character at a time ... yuk.
2647 * Or ... I could do a test print with "W", and use +1 or -1 for this
2648 * shift depending on if the leftmost column is blank...
2650 ExtTextOut(hdc
, x
- 1,
2651 y
- font_height
* (lattr
==
2652 LATTR_BOT
) + text_adjust
,
2653 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2656 /* And 'normal' unicode characters */
2657 static WCHAR
*wbuf
= NULL
;
2658 static int wlen
= 0;
2663 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2665 for (i
= 0; i
< len
; i
++)
2666 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2669 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2670 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2672 /* And the shadow bold hack. */
2673 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2674 SetBkMode(hdc
, TRANSPARENT
);
2675 ExtTextOutW(hdc
, x
- 1,
2676 y
- font_height
* (lattr
==
2677 LATTR_BOT
) + text_adjust
,
2678 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2681 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2682 (und_mode
== UND_LINE
2683 && (attr
& ATTR_UNDER
)))) {
2686 if (lattr
== LATTR_BOT
)
2687 dec
= dec
* 2 - font_height
;
2689 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2690 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2691 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2692 oldpen
= SelectObject(hdc
, oldpen
);
2693 DeleteObject(oldpen
);
2697 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2698 unsigned long attr
, int lattr
)
2704 int ctype
= cfg
.cursor_type
;
2706 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2707 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2708 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2712 attr
|= TATTR_RIGHTCURS
;
2715 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2716 if (attr
& ATTR_WIDE
)
2723 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2726 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2727 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2728 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2729 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2730 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2731 Polyline(hdc
, pts
, 5);
2732 oldpen
= SelectObject(hdc
, oldpen
);
2733 DeleteObject(oldpen
);
2734 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2735 int startx
, starty
, dx
, dy
, length
, i
;
2738 starty
= y
+ descent
;
2741 length
= char_width
;
2744 if (attr
& TATTR_RIGHTCURS
)
2745 xadjust
= char_width
- 1;
2746 startx
= x
+ xadjust
;
2750 length
= font_height
;
2752 if (attr
& TATTR_ACTCURS
) {
2755 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2756 MoveToEx(hdc
, startx
, starty
, NULL
);
2757 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2758 oldpen
= SelectObject(hdc
, oldpen
);
2759 DeleteObject(oldpen
);
2761 for (i
= 0; i
< length
; i
++) {
2763 SetPixel(hdc
, startx
, starty
, colours
[23]);
2772 /* This function gets the actual width of a character in the normal font.
2774 int CharWidth(Context ctx
, int uc
) {
2778 /* If the font max is the same as the font ave width then this
2779 * function is a no-op.
2781 if (!font_dualwidth
) return 1;
2783 switch (uc
& CSET_MASK
) {
2785 uc
= unitab_line
[uc
& 0xFF];
2788 uc
= unitab_xterm
[uc
& 0xFF];
2791 uc
= unitab_scoacs
[uc
& 0xFF];
2794 if (DIRECT_FONT(uc
)) {
2795 if (dbcs_screenfont
) return 1;
2797 /* Speedup, I know of no font where ascii is the wrong width */
2798 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
2801 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
2802 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2803 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
2804 another_font(FONT_OEM
);
2805 if (!fonts
[FONT_OEM
]) return 0;
2807 SelectObject(hdc
, fonts
[FONT_OEM
]);
2811 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
2812 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
2815 /* Speedup, I know of no font where ascii is the wrong width */
2816 if (uc
>= ' ' && uc
<= '~') return 1;
2818 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2819 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
2820 /* Okay that one worked */ ;
2821 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
2822 /* This should work on 9x too, but it's "less accurate" */ ;
2827 ibuf
+= font_width
/ 2 -1;
2834 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2835 * codes. Returns number of bytes used or zero to drop the message
2836 * or -1 to forward the message to windows.
2838 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2839 unsigned char *output
)
2842 int scan
, left_alt
= 0, key_down
, shift_state
;
2844 unsigned char *p
= output
;
2845 static int alt_sum
= 0;
2847 HKL kbd_layout
= GetKeyboardLayout(0);
2849 static WORD keys
[3];
2850 static int compose_char
= 0;
2851 static WPARAM compose_key
= 0;
2853 r
= GetKeyboardState(keystate
);
2855 memset(keystate
, 0, sizeof(keystate
));
2858 #define SHOW_TOASCII_RESULT
2859 { /* Tell us all about key events */
2860 static BYTE oldstate
[256];
2861 static int first
= 1;
2865 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2868 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2870 } else if ((HIWORD(lParam
) & KF_UP
)
2871 && scan
== (HIWORD(lParam
) & 0xFF)) {
2875 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2876 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2889 debug(("VK_%02x", wParam
));
2891 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2893 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2895 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2896 if (ch
>= ' ' && ch
<= '~')
2897 debug((", '%c'", ch
));
2899 debug((", $%02x", ch
));
2902 debug((", KB0=%02x", keys
[0]));
2904 debug((", KB1=%02x", keys
[1]));
2906 debug((", KB2=%02x", keys
[2]));
2908 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2910 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2912 if ((HIWORD(lParam
) & KF_EXTENDED
))
2914 if ((HIWORD(lParam
) & KF_UP
))
2918 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2919 else if ((HIWORD(lParam
) & KF_UP
))
2920 oldstate
[wParam
& 0xFF] ^= 0x80;
2922 oldstate
[wParam
& 0xFF] ^= 0x81;
2924 for (ch
= 0; ch
< 256; ch
++)
2925 if (oldstate
[ch
] != keystate
[ch
])
2926 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2928 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2932 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2933 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2937 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2938 if ((cfg
.funky_type
== 3 ||
2939 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2940 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2942 wParam
= VK_EXECUTE
;
2944 /* UnToggle NUMLock */
2945 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2946 keystate
[VK_NUMLOCK
] ^= 1;
2949 /* And write back the 'adjusted' state */
2950 SetKeyboardState(keystate
);
2953 /* Disable Auto repeat if required */
2954 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2957 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2960 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2962 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2963 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2964 if (cfg
.ctrlaltkeys
)
2965 keystate
[VK_MENU
] = 0;
2967 keystate
[VK_RMENU
] = 0x80;
2972 alt_pressed
= (left_alt
&& key_down
);
2974 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2975 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2976 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2978 /* Note if AltGr was pressed and if it was used as a compose key */
2979 if (!compose_state
) {
2980 compose_key
= 0x100;
2981 if (cfg
.compose_key
) {
2982 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2983 compose_key
= wParam
;
2985 if (wParam
== VK_APPS
)
2986 compose_key
= wParam
;
2989 if (wParam
== compose_key
) {
2990 if (compose_state
== 0
2991 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2993 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2997 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
3001 * Record that we pressed key so the scroll window can be reset, but
3002 * be careful to avoid Shift-UP/Down
3004 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
&&
3005 wParam
!= VK_MENU
&& wParam
!= VK_CONTROL
) {
3009 if (compose_state
> 1 && left_alt
)
3012 /* Sanitize the number pad if not using a PC NumPad */
3013 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
3014 && cfg
.funky_type
!= 2)
3015 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
3016 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
3020 nParam
= VK_NUMPAD0
;
3023 nParam
= VK_NUMPAD1
;
3026 nParam
= VK_NUMPAD2
;
3029 nParam
= VK_NUMPAD3
;
3032 nParam
= VK_NUMPAD4
;
3035 nParam
= VK_NUMPAD5
;
3038 nParam
= VK_NUMPAD6
;
3041 nParam
= VK_NUMPAD7
;
3044 nParam
= VK_NUMPAD8
;
3047 nParam
= VK_NUMPAD9
;
3050 nParam
= VK_DECIMAL
;
3054 if (keystate
[VK_NUMLOCK
] & 1)
3061 /* If a key is pressed and AltGr is not active */
3062 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
3063 /* Okay, prepare for most alts then ... */
3067 /* Lets see if it's a pattern we know all about ... */
3068 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
3069 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
3072 if (wParam
== VK_NEXT
&& shift_state
== 1) {
3073 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
3076 if (wParam
== VK_INSERT
&& shift_state
== 1) {
3080 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
3083 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
3084 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
3087 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
&&
3088 (cfg
.resize_action
!= RESIZE_DISABLED
)) {
3089 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) != KF_REPEAT
)
3093 /* Control-Numlock for app-keypad mode switch */
3094 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
3095 app_keypad_keys
^= 1;
3099 /* Nethack keypad */
3100 if (cfg
.nethack_keypad
&& !left_alt
) {
3103 *p
++ = shift_state ?
'B' : 'b';
3106 *p
++ = shift_state ?
'J' : 'j';
3109 *p
++ = shift_state ?
'N' : 'n';
3112 *p
++ = shift_state ?
'H' : 'h';
3115 *p
++ = shift_state ?
'.' : '.';
3118 *p
++ = shift_state ?
'L' : 'l';
3121 *p
++ = shift_state ?
'Y' : 'y';
3124 *p
++ = shift_state ?
'K' : 'k';
3127 *p
++ = shift_state ?
'U' : 'u';
3132 /* Application Keypad */
3136 if (cfg
.funky_type
== 3 ||
3137 (cfg
.funky_type
<= 1 &&
3138 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3152 if (app_keypad_keys
&& !cfg
.no_applic_k
)
3189 if (cfg
.funky_type
== 2) {
3194 } else if (shift_state
)
3201 if (cfg
.funky_type
== 2)
3205 if (cfg
.funky_type
== 2)
3209 if (cfg
.funky_type
== 2)
3214 if (HIWORD(lParam
) & KF_EXTENDED
)
3220 if (xkey
>= 'P' && xkey
<= 'S')
3221 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3223 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3225 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3230 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3231 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3235 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3241 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3245 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3249 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3254 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3259 /* Control-2 to Control-8 are special */
3260 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3261 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3264 if (shift_state
== 2 && wParam
== 0xBD) {
3268 if (shift_state
== 2 && wParam
== 0xDF) {
3272 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
3279 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3280 * for integer decimal nn.)
3282 * We also deal with the weird ones here. Linux VCs replace F1
3283 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3284 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3290 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3293 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3296 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3299 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3302 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3305 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3308 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3311 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3314 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3317 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3350 if ((shift_state
&2) == 0) switch (wParam
) {
3370 /* Reorder edit keys to physical order */
3371 if (cfg
.funky_type
== 3 && code
<= 6)
3372 code
= "\0\2\1\4\5\3\6"[code
];
3374 if (vt52_mode
&& code
> 0 && code
<= 6) {
3375 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3379 if (cfg
.funky_type
== 5 && /* SCO function keys */
3380 code
>= 11 && code
<= 34) {
3381 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3384 case VK_F1
: index
= 0; break;
3385 case VK_F2
: index
= 1; break;
3386 case VK_F3
: index
= 2; break;
3387 case VK_F4
: index
= 3; break;
3388 case VK_F5
: index
= 4; break;
3389 case VK_F6
: index
= 5; break;
3390 case VK_F7
: index
= 6; break;
3391 case VK_F8
: index
= 7; break;
3392 case VK_F9
: index
= 8; break;
3393 case VK_F10
: index
= 9; break;
3394 case VK_F11
: index
= 10; break;
3395 case VK_F12
: index
= 11; break;
3397 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3398 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3399 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3402 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3403 code
>= 1 && code
<= 6) {
3404 char codes
[] = "HL.FIG";
3408 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3412 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3419 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3422 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3425 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3426 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3429 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3431 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3433 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3436 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3437 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3441 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3446 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3447 * some reason seems to send VK_CLEAR to Windows...).
3470 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3472 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
3473 /* VT100 & VT102 manuals both state the app cursor keys
3474 * only work if the app keypad is on.
3476 if (!app_keypad_keys
)
3478 /* Useful mapping of Ctrl-arrows */
3479 if (shift_state
== 2)
3483 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3485 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3492 * Finally, deal with Return ourselves. (Win95 seems to
3493 * foul it up when Alt is pressed, for some reason.)
3495 if (wParam
== VK_RETURN
) { /* Return */
3501 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3502 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3507 /* Okay we've done everything interesting; let windows deal with
3508 * the boring stuff */
3512 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3513 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3515 keystate
[VK_CAPITAL
] = 0;
3518 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3519 #ifdef SHOW_TOASCII_RESULT
3520 if (r
== 1 && !key_down
) {
3522 if (in_utf
|| dbcs_screenfont
)
3523 debug((", (U+%04x)", alt_sum
));
3525 debug((", LCH(%d)", alt_sum
));
3527 debug((", ACH(%d)", keys
[0]));
3532 for (r1
= 0; r1
< r
; r1
++) {
3533 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3542 * Interrupt an ongoing paste. I'm not sure this is
3543 * sensible, but for the moment it's preferable to
3544 * having to faff about buffering things.
3549 for (i
= 0; i
< r
; i
++) {
3550 unsigned char ch
= (unsigned char) keys
[i
];
3552 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3557 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3561 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3562 MessageBeep(MB_ICONHAND
);
3566 luni_send(&keybuf
, 1, 1);
3574 if (in_utf
|| dbcs_screenfont
) {
3576 luni_send(&keybuf
, 1, 1);
3578 ch
= (char) alt_sum
;
3580 * We need not bother about stdin
3581 * backlogs here, because in GUI PuTTY
3582 * we can't do anything about it
3583 * anyway; there's no means of asking
3584 * Windows to hold off on KEYDOWN
3585 * messages. We _have_ to buffer
3586 * everything we're sent.
3588 ldisc_send(&ch
, 1, 1);
3592 lpage_send(kbd_codepage
, &ch
, 1, 1);
3594 if(capsOn
&& ch
< 0x80) {
3597 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3598 luni_send(cbuf
+!left_alt
, 1+!!left_alt
, 1);
3603 lpage_send(kbd_codepage
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3609 /* This is so the ALT-Numpad and dead keys work correctly. */
3614 /* If we're definitly not building up an ALT-54321 then clear it */
3617 /* If we will be using alt_sum fix the 256s */
3618 else if (keys
[0] && (in_utf
|| dbcs_screenfont
))
3623 * ALT alone may or may not want to bring up the System menu.
3624 * If it's not meant to, we return 0 on presses or releases of
3625 * ALT, to show that we've swallowed the keystroke. Otherwise
3626 * we return -1, which means Windows will give the keystroke
3627 * its default handling (i.e. bring up the System menu).
3629 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3635 void set_title(char *title
)
3638 window_name
= smalloc(1 + strlen(title
));
3639 strcpy(window_name
, title
);
3640 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3641 SetWindowText(hwnd
, title
);
3644 void set_icon(char *title
)
3647 icon_name
= smalloc(1 + strlen(title
));
3648 strcpy(icon_name
, title
);
3649 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3650 SetWindowText(hwnd
, title
);
3653 void set_sbar(int total
, int start
, int page
)
3657 if ((full_screen
&& !cfg
.scrollbar_in_fullscreen
) ||
3658 (!full_screen
&& !cfg
.scrollbar
))
3661 si
.cbSize
= sizeof(si
);
3662 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3664 si
.nMax
= total
- 1;
3668 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3671 Context
get_ctx(void)
3677 SelectPalette(hdc
, pal
, FALSE
);
3683 void free_ctx(Context ctx
)
3685 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3686 ReleaseDC(hwnd
, ctx
);
3689 static void real_palette_set(int n
, int r
, int g
, int b
)
3692 logpal
->palPalEntry
[n
].peRed
= r
;
3693 logpal
->palPalEntry
[n
].peGreen
= g
;
3694 logpal
->palPalEntry
[n
].peBlue
= b
;
3695 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3696 colours
[n
] = PALETTERGB(r
, g
, b
);
3697 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3699 colours
[n
] = RGB(r
, g
, b
);
3702 void palette_set(int n
, int r
, int g
, int b
)
3704 static const int first
[21] = {
3705 0, 2, 4, 6, 8, 10, 12, 14,
3706 1, 3, 5, 7, 9, 11, 13, 15,
3709 real_palette_set(first
[n
], r
, g
, b
);
3711 real_palette_set(first
[n
] + 1, r
, g
, b
);
3713 HDC hdc
= get_ctx();
3714 UnrealizeObject(pal
);
3715 RealizePalette(hdc
);
3720 void palette_reset(void)
3724 for (i
= 0; i
< NCOLOURS
; i
++) {
3726 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3727 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3728 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3729 logpal
->palPalEntry
[i
].peFlags
= 0;
3730 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3731 defpal
[i
].rgbtGreen
,
3732 defpal
[i
].rgbtBlue
);
3734 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3735 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3740 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3742 RealizePalette(hdc
);
3747 void write_aclip(char *data
, int len
, int must_deselect
)
3752 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3755 lock
= GlobalLock(clipdata
);
3758 memcpy(lock
, data
, len
);
3759 ((unsigned char *) lock
)[len
] = 0;
3760 GlobalUnlock(clipdata
);
3763 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3765 if (OpenClipboard(hwnd
)) {
3767 SetClipboardData(CF_TEXT
, clipdata
);
3770 GlobalFree(clipdata
);
3773 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3777 * Note: unlike write_aclip() this will not append a nul.
3779 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3781 HGLOBAL clipdata
, clipdata2
, clipdata3
;
3783 void *lock
, *lock2
, *lock3
;
3785 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3787 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3788 len
* sizeof(wchar_t));
3789 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3791 if (!clipdata
|| !clipdata2
|| !clipdata3
) {
3793 GlobalFree(clipdata
);
3795 GlobalFree(clipdata2
);
3797 GlobalFree(clipdata3
);
3800 if (!(lock
= GlobalLock(clipdata
)))
3802 if (!(lock2
= GlobalLock(clipdata2
)))
3805 memcpy(lock
, data
, len
* sizeof(wchar_t));
3806 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3808 if (cfg
.rtf_paste
) {
3809 wchar_t unitab
[256];
3811 unsigned char *tdata
= (unsigned char *)lock2
;
3812 wchar_t *udata
= (wchar_t *)lock
;
3813 int rtflen
= 0, uindex
= 0, tindex
= 0;
3815 int multilen
, blen
, alen
, totallen
, i
;
3816 char before
[16], after
[4];
3818 get_unitab(CP_ACP
, unitab
, 0);
3820 rtfsize
= 100 + strlen(cfg
.font
);
3821 rtf
= smalloc(rtfsize
);
3822 sprintf(rtf
, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3823 GetACP(), cfg
.font
);
3824 rtflen
= strlen(rtf
);
3827 * We want to construct a piece of RTF that specifies the
3828 * same Unicode text. To do this we will read back in
3829 * parallel from the Unicode data in `udata' and the
3830 * non-Unicode data in `tdata'. For each character in
3831 * `tdata' which becomes the right thing in `udata' when
3832 * looked up in `unitab', we just copy straight over from
3833 * tdata. For each one that doesn't, we must WCToMB it
3834 * individually and produce a \u escape sequence.
3836 * It would probably be more robust to just bite the bullet
3837 * and WCToMB each individual Unicode character one by one,
3838 * then MBToWC each one back to see if it was an accurate
3839 * translation; but that strikes me as a horrifying number
3840 * of Windows API calls so I want to see if this faster way
3841 * will work. If it screws up badly we can always revert to
3842 * the simple and slow way.
3844 while (tindex
< len2
&& uindex
< len
&&
3845 tdata
[tindex
] && udata
[uindex
]) {
3846 if (tindex
+ 1 < len2
&&
3847 tdata
[tindex
] == '\r' &&
3848 tdata
[tindex
+1] == '\n') {
3852 if (unitab
[tdata
[tindex
]] == udata
[uindex
]) {
3858 multilen
= WideCharToMultiByte(CP_ACP
, 0, unitab
+uindex
, 1,
3859 NULL
, 0, NULL
, NULL
);
3860 if (multilen
!= 1) {
3861 blen
= sprintf(before
, "{\\uc%d\\u%d", multilen
,
3863 alen
= 1; strcpy(after
, "}");
3865 blen
= sprintf(before
, "\\u%d", udata
[uindex
]);
3866 alen
= 0; after
[0] = '\0';
3869 assert(tindex
+ multilen
<= len2
);
3870 totallen
= blen
+ alen
;
3871 for (i
= 0; i
< multilen
; i
++) {
3872 if (tdata
[tindex
+i
] == '\\' ||
3873 tdata
[tindex
+i
] == '{' ||
3874 tdata
[tindex
+i
] == '}')
3876 else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A)
3877 totallen
+= 6; /* \par\r\n */
3878 else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20)
3884 if (rtfsize
< rtflen
+ totallen
+ 3) {
3885 rtfsize
= rtflen
+ totallen
+ 512;
3886 rtf
= srealloc(rtf
, rtfsize
);
3889 strcpy(rtf
+ rtflen
, before
); rtflen
+= blen
;
3890 for (i
= 0; i
< multilen
; i
++) {
3891 if (tdata
[tindex
+i
] == '\\' ||
3892 tdata
[tindex
+i
] == '{' ||
3893 tdata
[tindex
+i
] == '}') {
3894 rtf
[rtflen
++] = '\\';
3895 rtf
[rtflen
++] = tdata
[tindex
+i
];
3896 } else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A) {
3897 rtflen
+= sprintf(rtf
+rtflen
, "\\par\r\n");
3898 } else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20) {
3899 rtflen
+= sprintf(rtf
+rtflen
, "\\'%02x", tdata
[tindex
+i
]);
3901 rtf
[rtflen
++] = tdata
[tindex
+i
];
3904 strcpy(rtf
+ rtflen
, after
); rtflen
+= alen
;
3910 strcpy(rtf
+ rtflen
, "}");
3913 clipdata3
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, rtflen
);
3914 if (clipdata3
&& (lock3
= GlobalLock(clipdata3
)) != NULL
) {
3916 GlobalUnlock(clipdata3
);
3922 GlobalUnlock(clipdata
);
3923 GlobalUnlock(clipdata2
);
3926 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3928 if (OpenClipboard(hwnd
)) {
3930 SetClipboardData(CF_UNICODETEXT
, clipdata
);
3931 SetClipboardData(CF_TEXT
, clipdata2
);
3933 SetClipboardData(RegisterClipboardFormat(CF_RTF
), clipdata3
);
3936 GlobalFree(clipdata
);
3937 GlobalFree(clipdata2
);
3941 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3944 void get_clip(wchar_t ** p
, int *len
)
3946 static HGLOBAL clipdata
= NULL
;
3947 static wchar_t *converted
= 0;
3956 GlobalUnlock(clipdata
);
3959 } else if (OpenClipboard(NULL
)) {
3960 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
3962 *p
= GlobalLock(clipdata
);
3964 for (p2
= *p
; *p2
; p2
++);
3968 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
3972 s
= GlobalLock(clipdata
);
3973 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
3974 *p
= converted
= smalloc(i
* sizeof(wchar_t));
3975 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
3988 * Move `lines' lines from position `from' to position `to' in the
3991 void optimised_move(int to
, int from
, int lines
)
3996 min
= (to
< from ? to
: from
);
3997 max
= to
+ from
- min
;
3999 r
.left
= offset_width
;
4000 r
.right
= offset_width
+ cols
* font_width
;
4001 r
.top
= offset_height
+ min
* font_height
;
4002 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
4003 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
4008 * Print a message box and perform a fatal exit.
4010 void fatalbox(char *fmt
, ...)
4016 vsprintf(stuff
, fmt
, ap
);
4018 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
4023 * Manage window caption / taskbar flashing, if enabled.
4024 * 0 = stop, 1 = maintain, 2 = start
4026 static void flash_window(int mode
)
4028 static long last_flash
= 0;
4029 static int flashing
= 0;
4030 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
4033 FlashWindow(hwnd
, FALSE
);
4037 } else if (mode
== 2) {
4040 last_flash
= GetTickCount();
4042 FlashWindow(hwnd
, TRUE
);
4045 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
4048 long now
= GetTickCount();
4049 long fdiff
= now
- last_flash
;
4050 if (fdiff
< 0 || fdiff
> 450) {
4052 FlashWindow(hwnd
, TRUE
); /* toggle */
4063 if (mode
== BELL_DEFAULT
) {
4065 * For MessageBeep style bells, we want to be careful of
4066 * timing, because they don't have the nice property of
4067 * PlaySound bells that each one cancels the previous
4068 * active one. So we limit the rate to one per 50ms or so.
4070 static long lastbeep
= 0;
4073 beepdiff
= GetTickCount() - lastbeep
;
4074 if (beepdiff
>= 0 && beepdiff
< 50)
4078 * The above MessageBeep call takes time, so we record the
4079 * time _after_ it finishes rather than before it starts.
4081 lastbeep
= GetTickCount();
4082 } else if (mode
== BELL_WAVEFILE
) {
4083 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
4084 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
4085 sprintf(buf
, "Unable to play sound file\n%s\n"
4086 "Using default sound instead", cfg
.bell_wavefile
);
4087 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
4088 MB_OK
| MB_ICONEXCLAMATION
);
4089 cfg
.beep
= BELL_DEFAULT
;
4092 /* Otherwise, either visual bell or disabled; do nothing here */
4094 flash_window(2); /* start */
4099 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
4101 * Revised by <wez@thebrainroom.com>
4103 static void flip_full_screen(void)
4108 wp
.length
= sizeof(wp
);
4109 GetWindowPlacement(hwnd
, &wp
);
4111 full_screen
= !full_screen
;
4114 if (wp
.showCmd
== SW_SHOWMAXIMIZED
) {
4115 /* Ooops it was already 'zoomed' we have to unzoom it before
4116 * everything will work right.
4118 wp
.showCmd
= SW_SHOWNORMAL
;
4119 SetWindowPlacement(hwnd
, &wp
);
4122 style
= GetWindowLong(hwnd
, GWL_STYLE
) & ~(WS_CAPTION
|WS_THICKFRAME
);
4123 style
&= ~WS_VSCROLL
;
4124 if (cfg
.scrollbar_in_fullscreen
)
4125 style
|= WS_VSCROLL
;
4126 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4128 /* Some versions of explorer get confused and don't take
4129 * notice of us going fullscreen, so go topmost too.
4131 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
4132 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
4133 SWP_NOMOVE
| SWP_NOSIZE
|
4136 wp
.showCmd
= SW_SHOWMAXIMIZED
;
4137 SetWindowPlacement(hwnd
, &wp
);
4139 style
= GetWindowLong(hwnd
, GWL_STYLE
) | WS_CAPTION
;
4140 if (cfg
.resize_action
!= RESIZE_DISABLED
)
4141 style
|= WS_THICKFRAME
;
4142 style
&= ~WS_VSCROLL
;
4144 style
|= WS_VSCROLL
;
4145 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4147 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
4148 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
4149 SWP_NOMOVE
| SWP_NOSIZE
|
4152 wp
.showCmd
= SW_SHOWNORMAL
;
4153 SetWindowPlacement(hwnd
, &wp
);
4156 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4157 MF_BYCOMMAND
| full_screen ? MF_CHECKED
: MF_UNCHECKED
);
4161 * Minimise or restore the window in response to a server-side
4164 void set_iconic(int iconic
)
4166 if (IsIconic(hwnd
)) {
4168 ShowWindow(hwnd
, SW_RESTORE
);
4171 ShowWindow(hwnd
, SW_MINIMIZE
);
4176 * Move the window in response to a server-side request.
4178 void move_window(int x
, int y
)
4180 SetWindowPos(hwnd
, NULL
, x
, y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
);
4184 * Move the window to the top or bottom of the z-order in response
4185 * to a server-side request.
4187 void set_zorder(int top
)
4189 if (cfg
.alwaysontop
|| full_screen
)
4190 return; /* ignore */
4191 SetWindowPos(hwnd
, top ? HWND_TOP
: HWND_BOTTOM
, 0, 0, 0, 0,
4192 SWP_NOMOVE
| SWP_NOSIZE
);
4196 * Refresh the window in response to a server-side request.
4198 void refresh_window(void)
4200 InvalidateRect(hwnd
, NULL
, TRUE
);
4204 * Maximise or restore the window in response to a server-side
4207 void set_zoomed(int zoomed
)
4209 if (IsZoomed(hwnd
) || full_screen
) {
4214 ShowWindow(hwnd
, SW_RESTORE
);
4218 ShowWindow(hwnd
, SW_MAXIMIZE
);
4223 * Report whether the window is iconic, for terminal reports.
4227 return IsIconic(hwnd
);
4231 * Report the window's position, for terminal reports.
4233 void get_window_pos(int *x
, int *y
)
4236 GetWindowRect(hwnd
, &r
);
4242 * Report the window's pixel size, for terminal reports.
4244 void get_window_pixels(int *x
, int *y
)
4247 GetWindowRect(hwnd
, &r
);
4248 *x
= r
.right
- r
.left
;
4249 *y
= r
.bottom
- r
.top
;
4253 * Return the window or icon title.
4255 char *get_window_title(int icon
)
4257 return icon ? icon_name
: window_name
;