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_ABOUT 0x0140
46 #define IDM_SAVEDSESS 0x0150
47 #define IDM_COPYALL 0x0160
48 #define IDM_FULLSCREEN 0x0170
50 #define IDM_SESSLGP 0x0250 /* log type printable */
51 #define IDM_SESSLGA 0x0260 /* log type all chars */
52 #define IDM_SESSLGE 0x0270 /* log end */
53 #define IDM_SAVED_MIN 0x1000
54 #define IDM_SAVED_MAX 0x2000
56 #define WM_IGNORE_CLIP (WM_XUSER + 2)
58 /* Needed for Chinese support and apparently not always defined. */
60 #define VK_PROCESSKEY 0xE5
63 /* Needed for mouse wheel support and not defined in earlier SDKs. */
65 #define WM_MOUSEWHEEL 0x020A
68 static LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
69 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
70 unsigned char *output
);
71 static void cfgtopalette(void);
72 static void init_palette(void);
73 static void init_fonts(int, int);
74 static void another_font(int);
75 static void deinit_fonts(void);
76 static void set_input_locale(HKL
);
78 /* Window layout information */
79 static void reset_window(int);
80 static int full_screen
= 0;
81 static int extra_width
, extra_height
;
82 static int font_width
, font_height
, font_dualwidth
;
83 static int offset_width
, offset_height
;
84 static int was_zoomed
= 0;
85 static int prev_rows
, prev_cols
;
87 static int pending_netevent
= 0;
88 static WPARAM pend_netevent_wParam
= 0;
89 static LPARAM pend_netevent_lParam
= 0;
90 static void enact_pending_netevent(void);
91 static void flash_window(int mode
);
92 static void flip_full_screen(void);
94 static time_t last_movement
= 0;
98 #define FONT_UNDERLINE 2
99 #define FONT_BOLDUND 3
100 #define FONT_WIDE 0x04
101 #define FONT_HIGH 0x08
102 #define FONT_NARROW 0x10
104 #define FONT_OEM 0x20
105 #define FONT_OEMBOLD 0x21
106 #define FONT_OEMUND 0x22
107 #define FONT_OEMBOLDUND 0x23
109 #define FONT_MAXNO 0x2F
111 static HFONT fonts
[FONT_MAXNO
];
112 static LOGFONT lfont
;
113 static int fontflag
[FONT_MAXNO
];
115 BOLD_COLOURS
, BOLD_SHADOW
, BOLD_FONT
123 static COLORREF colours
[NCOLOURS
];
125 static LPLOGPALETTE logpal
;
126 static RGBTRIPLE defpal
[NCOLOURS
];
130 static HBITMAP caretbm
;
132 static int dbltime
, lasttime
, lastact
;
133 static Mouse_Button lastbtn
;
135 /* this allows xterm-style mouse handling. */
136 static int send_raw_mouse
= 0;
137 static int wheel_accumulator
= 0;
139 static char *window_name
, *icon_name
;
141 static int compose_state
= 0;
143 static OSVERSIONINFO osVersion
;
145 /* Dummy routine, only required in plink. */
146 void ldisc_update(int echo
, int edit
)
150 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
152 static char appname
[] = "PuTTY";
157 int guess_width
, guess_height
;
160 flags
= FLAG_VERBOSE
| FLAG_INTERACTIVE
;
162 winsock_ver
= MAKEWORD(1, 1);
163 if (WSAStartup(winsock_ver
, &wsadata
)) {
164 MessageBox(NULL
, "Unable to initialise WinSock", "WinSock Error",
165 MB_OK
| MB_ICONEXCLAMATION
);
168 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1) {
169 MessageBox(NULL
, "WinSock version is incompatible with 1.1",
170 "WinSock Error", MB_OK
| MB_ICONEXCLAMATION
);
174 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
177 InitCommonControls();
179 /* Ensure a Maximize setting in Explorer doesn't maximise the
184 ZeroMemory(&osVersion
, sizeof(osVersion
));
185 osVersion
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
186 if (!GetVersionEx ( (OSVERSIONINFO
*) &osVersion
)) {
187 MessageBox(NULL
, "Windows refuses to report a version",
188 "PuTTY Fatal Error", MB_OK
| MB_ICONEXCLAMATION
);
194 * Process the command line.
199 default_protocol
= DEFAULT_PROTOCOL
;
200 default_port
= DEFAULT_PORT
;
201 cfg
.logtype
= LGTYP_NONE
;
203 do_defaults(NULL
, &cfg
);
206 while (*p
&& isspace(*p
))
210 * Process command line options first. Yes, this can be
211 * done better, and it will be as soon as I have the
215 char *q
= p
+ strcspn(p
, " \t");
218 tolower(p
[0]) == 's' &&
219 tolower(p
[1]) == 's' && tolower(p
[2]) == 'h') {
220 default_protocol
= cfg
.protocol
= PROT_SSH
;
221 default_port
= cfg
.port
= 22;
222 } else if (q
== p
+ 7 &&
223 tolower(p
[0]) == 'c' &&
224 tolower(p
[1]) == 'l' &&
225 tolower(p
[2]) == 'e' &&
226 tolower(p
[3]) == 'a' &&
227 tolower(p
[4]) == 'n' &&
228 tolower(p
[5]) == 'u' && tolower(p
[6]) == 'p') {
230 * `putty -cleanup'. Remove all registry entries
231 * associated with PuTTY, and also find and delete
232 * the random seed file.
235 "This procedure will remove ALL Registry\n"
236 "entries associated with PuTTY, and will\n"
237 "also remove the PuTTY random seed file.\n"
239 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
240 "SESSIONS. Are you really sure you want\n"
243 MB_YESNO
| MB_ICONWARNING
) == IDYES
) {
248 p
= q
+ strspn(q
, " \t");
252 * An initial @ means to activate a saved session.
256 while (i
> 1 && isspace(p
[i
- 1]))
259 do_defaults(p
+ 1, &cfg
);
260 if (!*cfg
.host
&& !do_config()) {
264 } else if (*p
== '&') {
266 * An initial & means we've been given a command line
267 * containing the hex value of a HANDLE for a file
268 * mapping object, which we must then extract as a
273 if (sscanf(p
+ 1, "%p", &filemap
) == 1 &&
274 (cp
= MapViewOfFile(filemap
, FILE_MAP_READ
,
275 0, 0, sizeof(Config
))) != NULL
) {
278 CloseHandle(filemap
);
279 } else if (!do_config()) {
286 * If the hostname starts with "telnet:", set the
287 * protocol to Telnet and process the string as a
290 if (!strncmp(q
, "telnet:", 7)) {
294 if (q
[0] == '/' && q
[1] == '/')
296 cfg
.protocol
= PROT_TELNET
;
298 while (*p
&& *p
!= ':' && *p
!= '/')
307 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
308 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
310 while (*p
&& !isspace(*p
))
314 strncpy(cfg
.host
, q
, sizeof(cfg
.host
) - 1);
315 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
316 while (*p
&& isspace(*p
))
331 * Trim leading whitespace off the hostname if it's there.
334 int space
= strspn(cfg
.host
, " \t");
335 memmove(cfg
.host
, cfg
.host
+space
, 1+strlen(cfg
.host
)-space
);
338 /* See if host is of the form user@host */
339 if (cfg
.host
[0] != '\0') {
340 char *atsign
= strchr(cfg
.host
, '@');
341 /* Make sure we're not overflowing the user field */
343 if (atsign
- cfg
.host
< sizeof cfg
.username
) {
344 strncpy(cfg
.username
, cfg
.host
, atsign
- cfg
.host
);
345 cfg
.username
[atsign
- cfg
.host
] = '\0';
347 memmove(cfg
.host
, atsign
+ 1, 1 + strlen(atsign
+ 1));
352 * Trim a colon suffix off the hostname if it's there.
354 cfg
.host
[strcspn(cfg
.host
, ":")] = '\0';
358 * Select protocol. This is farmed out into a table in a
359 * separate file to enable an ssh-free variant.
364 for (i
= 0; backends
[i
].backend
!= NULL
; i
++)
365 if (backends
[i
].protocol
== cfg
.protocol
) {
366 back
= backends
[i
].backend
;
370 MessageBox(NULL
, "Unsupported protocol number found",
371 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
377 /* Check for invalid Port number (i.e. zero) */
379 MessageBox(NULL
, "Invalid Port Number",
380 "PuTTY Internal Error", MB_OK
| MB_ICONEXCLAMATION
);
387 wndclass
.lpfnWndProc
= WndProc
;
388 wndclass
.cbClsExtra
= 0;
389 wndclass
.cbWndExtra
= 0;
390 wndclass
.hInstance
= inst
;
391 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
392 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
393 wndclass
.hbrBackground
= NULL
;
394 wndclass
.lpszMenuName
= NULL
;
395 wndclass
.lpszClassName
= appname
;
397 RegisterClass(&wndclass
);
402 savelines
= cfg
.savelines
;
408 * Guess some defaults for the window size. This all gets
409 * updated later, so we don't really care too much. However, we
410 * do want the font width/height guesses to correspond to a
411 * large font rather than a small one...
418 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
419 guess_width
= extra_width
+ font_width
* cols
;
420 guess_height
= extra_height
+ font_height
* rows
;
423 HWND w
= GetDesktopWindow();
424 GetWindowRect(w
, &r
);
425 if (guess_width
> r
.right
- r
.left
)
426 guess_width
= r
.right
- r
.left
;
427 if (guess_height
> r
.bottom
- r
.top
)
428 guess_height
= r
.bottom
- r
.top
;
432 int winmode
= WS_OVERLAPPEDWINDOW
| WS_VSCROLL
;
435 winmode
&= ~(WS_VSCROLL
);
436 if (cfg
.resize_action
== RESIZE_DISABLED
)
437 winmode
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
439 exwinmode
|= WS_EX_TOPMOST
;
441 exwinmode
|= WS_EX_CLIENTEDGE
;
442 hwnd
= CreateWindowEx(exwinmode
, appname
, appname
,
443 winmode
, CW_USEDEFAULT
, CW_USEDEFAULT
,
444 guess_width
, guess_height
,
445 NULL
, NULL
, inst
, NULL
);
449 * Initialise the fonts, simultaneously correcting the guesses
450 * for font_{width,height}.
455 * Correct the guesses for extra_{width,height}.
459 GetWindowRect(hwnd
, &wr
);
460 GetClientRect(hwnd
, &cr
);
461 offset_width
= offset_height
= cfg
.window_border
;
462 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
463 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
467 * Resize the window, now we know what size we _really_ want it
470 guess_width
= extra_width
+ font_width
* cols
;
471 guess_height
= extra_height
+ font_height
* rows
;
472 SetWindowPos(hwnd
, NULL
, 0, 0, guess_width
, guess_height
,
473 SWP_NOMOVE
| SWP_NOREDRAW
| SWP_NOZORDER
);
476 * Set up a caret bitmap, with no content.
480 int size
= (font_width
+ 15) / 16 * 2 * font_height
;
481 bits
= smalloc(size
);
482 memset(bits
, 0, size
);
483 caretbm
= CreateBitmap(font_width
, font_height
, 1, 1, bits
);
486 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
489 * Initialise the scroll bar.
494 si
.cbSize
= sizeof(si
);
495 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
500 SetScrollInfo(hwnd
, SB_VERT
, &si
, FALSE
);
504 * Start up the telnet connection.
508 char msg
[1024], *title
;
511 error
= back
->init(cfg
.host
, cfg
.port
, &realhost
, cfg
.tcp_nodelay
);
513 sprintf(msg
, "Unable to open connection to\n"
514 "%.800s\n" "%s", cfg
.host
, error
);
515 MessageBox(NULL
, msg
, "PuTTY Error", MB_ICONERROR
| MB_OK
);
518 window_name
= icon_name
= NULL
;
520 title
= cfg
.wintitle
;
522 sprintf(msg
, "%s - PuTTY", realhost
);
530 session_closed
= FALSE
;
533 * Prepare the mouse handler.
535 lastact
= MA_NOTHING
;
536 lastbtn
= MBT_NOTHING
;
537 dbltime
= GetDoubleClickTime();
540 * Set up the session-control options on the system menu.
543 HMENU m
= GetSystemMenu(hwnd
, FALSE
);
547 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
548 if (cfg
.protocol
== PROT_TELNET
) {
550 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AYT
, "Are You There");
551 AppendMenu(p
, MF_ENABLED
, IDM_TEL_BRK
, "Break");
552 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SYNCH
, "Synch");
553 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
554 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EC
, "Erase Character");
555 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EL
, "Erase Line");
556 AppendMenu(p
, MF_ENABLED
, IDM_TEL_GA
, "Go Ahead");
557 AppendMenu(p
, MF_ENABLED
, IDM_TEL_NOP
, "No Operation");
558 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
559 AppendMenu(p
, MF_ENABLED
, IDM_TEL_ABORT
, "Abort Process");
560 AppendMenu(p
, MF_ENABLED
, IDM_TEL_AO
, "Abort Output");
561 AppendMenu(p
, MF_ENABLED
, IDM_TEL_IP
, "Interrupt Process");
562 AppendMenu(p
, MF_ENABLED
, IDM_TEL_SUSP
, "Suspend Process");
563 AppendMenu(p
, MF_SEPARATOR
, 0, 0);
564 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOR
, "End Of Record");
565 AppendMenu(p
, MF_ENABLED
, IDM_TEL_EOF
, "End Of File");
566 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) p
,
568 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
570 AppendMenu(m
, MF_ENABLED
, IDM_SHOWLOG
, "&Event Log");
571 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
572 AppendMenu(m
, MF_ENABLED
, IDM_NEWSESS
, "Ne&w Session...");
573 AppendMenu(m
, MF_ENABLED
, IDM_DUPSESS
, "&Duplicate Session");
576 for (i
= 1; i
< ((nsessions
< 256) ? nsessions
: 256); i
++)
577 AppendMenu(s
, MF_ENABLED
, IDM_SAVED_MIN
+ (16 * i
),
579 AppendMenu(m
, MF_POPUP
| MF_ENABLED
, (UINT
) s
, "Sa&ved Sessions");
580 AppendMenu(m
, MF_ENABLED
, IDM_RECONF
, "Chan&ge Settings...");
581 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
582 AppendMenu(m
, MF_ENABLED
, IDM_COPYALL
, "C&opy All to Clipboard");
583 AppendMenu(m
, MF_ENABLED
, IDM_CLRSB
, "C&lear Scrollback");
584 AppendMenu(m
, MF_ENABLED
, IDM_RESET
, "Rese&t Terminal");
585 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
586 AppendMenu(m
, (cfg
.resize_action
== RESIZE_DISABLED
) ?
587 MF_GRAYED
: MF_ENABLED
, IDM_FULLSCREEN
, "&Full Screen");
588 AppendMenu(m
, MF_SEPARATOR
, 0, 0);
589 AppendMenu(m
, MF_ENABLED
, IDM_ABOUT
, "&About PuTTY");
593 * Set up the initial input locale.
595 set_input_locale(GetKeyboardLayout(0));
598 * Finally show the window!
600 ShowWindow(hwnd
, show
);
601 SetForegroundWindow(hwnd
);
604 * Open the initial log file if there is one.
609 * Set the palette up.
615 has_focus
= (GetForegroundWindow() == hwnd
);
618 if (GetMessage(&msg
, NULL
, 0, 0) == 1) {
619 int timer_id
= 0, long_timer
= 0;
621 while (msg
.message
!= WM_QUIT
) {
622 /* Sometimes DispatchMessage calls routines that use their own
623 * GetMessage loop, setup this timer so we get some control back.
625 * Also call term_update() from the timer so that if the host
626 * is sending data flat out we still do redraws.
628 if (timer_id
&& long_timer
) {
629 KillTimer(hwnd
, timer_id
);
630 long_timer
= timer_id
= 0;
633 timer_id
= SetTimer(hwnd
, 1, 20, NULL
);
634 if (!(IsWindow(logbox
) && IsDialogMessage(logbox
, &msg
)))
635 DispatchMessage(&msg
);
637 /* Make sure we blink everything that needs it. */
640 /* Send the paste buffer if there's anything to send */
643 /* If there's nothing new in the queue then we can do everything
644 * we've delayed, reading the socket, writing, and repainting
647 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
650 if (pending_netevent
) {
651 enact_pending_netevent();
653 /* Force the cursor blink on */
656 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
660 /* Okay there is now nothing to do so we make sure the screen is
661 * completely up to date then tell windows to call us in a little
665 KillTimer(hwnd
, timer_id
);
673 flash_window(1); /* maintain */
675 /* The messages seem unreliable; especially if we're being tricky */
676 has_focus
= (GetForegroundWindow() == hwnd
);
679 /* Hmm, term_update didn't want to do an update too soon ... */
680 timer_id
= SetTimer(hwnd
, 1, 50, NULL
);
682 timer_id
= SetTimer(hwnd
, 1, 500, NULL
);
684 timer_id
= SetTimer(hwnd
, 1, 100, NULL
);
687 /* There's no point rescanning everything in the message queue
688 * so we do an apparently unnecessary wait here
691 if (GetMessage(&msg
, NULL
, 0, 0) != 1)
705 if (cfg
.protocol
== PROT_SSH
) {
716 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
718 char *do_select(SOCKET skt
, int startup
)
723 events
= (FD_CONNECT
| FD_READ
| FD_WRITE
|
724 FD_OOB
| FD_CLOSE
| FD_ACCEPT
);
729 return "do_select(): internal error (hwnd==NULL)";
730 if (WSAAsyncSelect(skt
, hwnd
, msg
, events
) == SOCKET_ERROR
) {
731 switch (WSAGetLastError()) {
733 return "Network is down";
735 return "WSAAsyncSelect(): unknown error";
742 * set or clear the "raw mouse message" mode
744 void set_raw_mouse_mode(int activate
)
746 send_raw_mouse
= activate
;
747 SetCursor(LoadCursor(NULL
, activate ? IDC_ARROW
: IDC_IBEAM
));
751 * Print a message box and close the connection.
753 void connection_fatal(char *fmt
, ...)
759 vsprintf(stuff
, fmt
, ap
);
761 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
762 if (cfg
.close_on_exit
== COE_ALWAYS
)
765 session_closed
= TRUE
;
766 SetWindowText(hwnd
, "PuTTY (inactive)");
771 * Actually do the job requested by a WM_NETEVENT
773 static void enact_pending_netevent(void)
775 static int reentering
= 0;
776 extern int select_result(WPARAM
, LPARAM
);
780 return; /* don't unpend the pending */
782 pending_netevent
= FALSE
;
785 ret
= select_result(pend_netevent_wParam
, pend_netevent_lParam
);
788 if (ret
== 0 && !session_closed
) {
789 /* Abnormal exits will already have set session_closed and taken
790 * appropriate action. */
791 if (cfg
.close_on_exit
== COE_ALWAYS
||
792 cfg
.close_on_exit
== COE_NORMAL
) PostQuitMessage(0);
794 session_closed
= TRUE
;
795 SetWindowText(hwnd
, "PuTTY (inactive)");
796 MessageBox(hwnd
, "Connection closed by remote host",
797 "PuTTY", MB_OK
| MB_ICONINFORMATION
);
803 * Copy the colour palette from the configuration data into defpal.
804 * This is non-trivial because the colour indices are different.
806 static void cfgtopalette(void)
809 static const int ww
[] = {
810 6, 7, 8, 9, 10, 11, 12, 13,
811 14, 15, 16, 17, 18, 19, 20, 21,
812 0, 1, 2, 3, 4, 4, 5, 5
815 for (i
= 0; i
< 24; i
++) {
817 defpal
[i
].rgbtRed
= cfg
.colours
[w
][0];
818 defpal
[i
].rgbtGreen
= cfg
.colours
[w
][1];
819 defpal
[i
].rgbtBlue
= cfg
.colours
[w
][2];
824 * Set up the colour palette.
826 static void init_palette(void)
829 HDC hdc
= GetDC(hwnd
);
831 if (cfg
.try_palette
&& GetDeviceCaps(hdc
, RASTERCAPS
) & RC_PALETTE
) {
832 logpal
= smalloc(sizeof(*logpal
)
833 - sizeof(logpal
->palPalEntry
)
834 + NCOLOURS
* sizeof(PALETTEENTRY
));
835 logpal
->palVersion
= 0x300;
836 logpal
->palNumEntries
= NCOLOURS
;
837 for (i
= 0; i
< NCOLOURS
; i
++) {
838 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
839 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
840 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
841 logpal
->palPalEntry
[i
].peFlags
= PC_NOCOLLAPSE
;
843 pal
= CreatePalette(logpal
);
845 SelectPalette(hdc
, pal
, FALSE
);
847 SelectPalette(hdc
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
850 ReleaseDC(hwnd
, hdc
);
853 for (i
= 0; i
< NCOLOURS
; i
++)
854 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
858 for (i
= 0; i
< NCOLOURS
; i
++)
859 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
860 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
864 * Initialise all the fonts we will need initially. There may be as many as
865 * three or as few as one. The other (poentially) twentyone fonts are done
866 * if/when they are needed.
870 * - check the font width and height, correcting our guesses if
873 * - verify that the bold font is the same width as the ordinary
874 * one, and engage shadow bolding if not.
876 * - verify that the underlined font is the same width as the
877 * ordinary one (manual underlining by means of line drawing can
878 * be done in a pinch).
880 static void init_fonts(int pick_width
, int pick_height
)
887 int fw_dontcare
, fw_bold
;
889 for (i
= 0; i
< FONT_MAXNO
; i
++)
892 bold_mode
= cfg
.bold_colour ? BOLD_COLOURS
: BOLD_FONT
;
895 if (cfg
.fontisbold
) {
896 fw_dontcare
= FW_BOLD
;
899 fw_dontcare
= FW_DONTCARE
;
906 font_height
= pick_height
;
908 font_height
= cfg
.fontheight
;
909 if (font_height
> 0) {
911 -MulDiv(font_height
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
914 font_width
= pick_width
;
917 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
918 c, OUT_DEFAULT_PRECIS, \
919 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
920 FIXED_PITCH | FF_DONTCARE, cfg.font)
922 f(FONT_NORMAL
, cfg
.fontcharset
, fw_dontcare
, FALSE
);
924 lfont
.lfHeight
= font_height
;
925 lfont
.lfWidth
= font_width
;
926 lfont
.lfEscapement
= 0;
927 lfont
.lfOrientation
= 0;
928 lfont
.lfWeight
= fw_dontcare
;
929 lfont
.lfItalic
= FALSE
;
930 lfont
.lfUnderline
= FALSE
;
931 lfont
.lfStrikeOut
= FALSE
;
932 lfont
.lfCharSet
= cfg
.fontcharset
;
933 lfont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
934 lfont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
935 lfont
.lfQuality
= DEFAULT_QUALITY
;
936 lfont
.lfPitchAndFamily
= FIXED_PITCH
| FF_DONTCARE
;
937 strncpy(lfont
.lfFaceName
, cfg
.font
, LF_FACESIZE
);
939 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
940 GetTextMetrics(hdc
, &tm
);
942 if (pick_width
== 0 || pick_height
== 0) {
943 font_height
= tm
.tmHeight
;
944 font_width
= tm
.tmAveCharWidth
;
946 font_dualwidth
= (tm
.tmAveCharWidth
!= tm
.tmMaxCharWidth
);
948 #ifdef RDB_DEBUG_PATCH
949 debug(23, "Primary font H=%d, AW=%d, MW=%d",
950 tm
.tmHeight
, tm
.tmAveCharWidth
, tm
.tmMaxCharWidth
);
955 DWORD cset
= tm
.tmCharSet
;
956 memset(&info
, 0xFF, sizeof(info
));
958 /* !!! Yes the next line is right */
959 if (cset
== OEM_CHARSET
)
960 font_codepage
= GetOEMCP();
962 if (TranslateCharsetInfo
963 ((DWORD
*) cset
, &info
, TCI_SRCCHARSET
)) font_codepage
=
968 GetCPInfo(font_codepage
, &cpinfo
);
969 dbcs_screenfont
= (cpinfo
.MaxCharSize
> 1);
972 f(FONT_UNDERLINE
, cfg
.fontcharset
, fw_dontcare
, TRUE
);
975 * Some fonts, e.g. 9-pt Courier, draw their underlines
976 * outside their character cell. We successfully prevent
977 * screen corruption by clipping the text output, but then
978 * we lose the underline completely. Here we try to work
979 * out whether this is such a font, and if it is, we set a
980 * flag that causes underlines to be drawn by hand.
982 * Having tried other more sophisticated approaches (such
983 * as examining the TEXTMETRIC structure or requesting the
984 * height of a string), I think we'll do this the brute
985 * force way: we create a small bitmap, draw an underlined
986 * space on it, and test to see whether any pixels are
987 * foreground-coloured. (Since we expect the underline to
988 * go all the way across the character cell, we only search
989 * down a single column of the bitmap, half way across.)
993 HBITMAP und_bm
, und_oldbm
;
997 und_dc
= CreateCompatibleDC(hdc
);
998 und_bm
= CreateCompatibleBitmap(hdc
, font_width
, font_height
);
999 und_oldbm
= SelectObject(und_dc
, und_bm
);
1000 SelectObject(und_dc
, fonts
[FONT_UNDERLINE
]);
1001 SetTextAlign(und_dc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
1002 SetTextColor(und_dc
, RGB(255, 255, 255));
1003 SetBkColor(und_dc
, RGB(0, 0, 0));
1004 SetBkMode(und_dc
, OPAQUE
);
1005 ExtTextOut(und_dc
, 0, 0, ETO_OPAQUE
, NULL
, " ", 1, NULL
);
1007 for (i
= 0; i
< font_height
; i
++) {
1008 c
= GetPixel(und_dc
, font_width
/ 2, i
);
1009 if (c
!= RGB(0, 0, 0))
1012 SelectObject(und_dc
, und_oldbm
);
1013 DeleteObject(und_bm
);
1016 und_mode
= UND_LINE
;
1017 DeleteObject(fonts
[FONT_UNDERLINE
]);
1018 fonts
[FONT_UNDERLINE
] = 0;
1022 if (bold_mode
== BOLD_FONT
) {
1023 f(FONT_BOLD
, cfg
.fontcharset
, fw_bold
, FALSE
);
1027 descent
= tm
.tmAscent
+ 1;
1028 if (descent
>= font_height
)
1029 descent
= font_height
- 1;
1031 for (i
= 0; i
< 3; i
++) {
1033 if (SelectObject(hdc
, fonts
[i
]) && GetTextMetrics(hdc
, &tm
))
1034 fontsize
[i
] = tm
.tmAveCharWidth
+ 256 * tm
.tmHeight
;
1041 ReleaseDC(hwnd
, hdc
);
1043 if (fontsize
[FONT_UNDERLINE
] != fontsize
[FONT_NORMAL
]) {
1044 und_mode
= UND_LINE
;
1045 DeleteObject(fonts
[FONT_UNDERLINE
]);
1046 fonts
[FONT_UNDERLINE
] = 0;
1049 if (bold_mode
== BOLD_FONT
&&
1050 fontsize
[FONT_BOLD
] != fontsize
[FONT_NORMAL
]) {
1051 bold_mode
= BOLD_SHADOW
;
1052 DeleteObject(fonts
[FONT_BOLD
]);
1053 fonts
[FONT_BOLD
] = 0;
1055 fontflag
[0] = fontflag
[1] = fontflag
[2] = 1;
1060 static void another_font(int fontno
)
1063 int fw_dontcare
, fw_bold
;
1067 if (fontno
< 0 || fontno
>= FONT_MAXNO
|| fontflag
[fontno
])
1070 basefont
= (fontno
& ~(FONT_BOLDUND
));
1071 if (basefont
!= fontno
&& !fontflag
[basefont
])
1072 another_font(basefont
);
1074 if (cfg
.fontisbold
) {
1075 fw_dontcare
= FW_BOLD
;
1078 fw_dontcare
= FW_DONTCARE
;
1082 c
= cfg
.fontcharset
;
1088 if (fontno
& FONT_WIDE
)
1090 if (fontno
& FONT_NARROW
)
1092 if (fontno
& FONT_OEM
)
1094 if (fontno
& FONT_BOLD
)
1096 if (fontno
& FONT_UNDERLINE
)
1100 CreateFont(font_height
* (1 + !!(fontno
& FONT_HIGH
)), x
, 0, 0, w
,
1101 FALSE
, u
, FALSE
, c
, OUT_DEFAULT_PRECIS
,
1102 CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
,
1103 FIXED_PITCH
| FF_DONTCARE
, s
);
1105 fontflag
[fontno
] = 1;
1108 static void deinit_fonts(void)
1111 for (i
= 0; i
< FONT_MAXNO
; i
++) {
1113 DeleteObject(fonts
[i
]);
1119 void request_resize(int w
, int h
)
1123 /* If the window is maximized supress resizing attempts */
1124 if (IsZoomed(hwnd
)) {
1125 if (cfg
.resize_action
== RESIZE_TERM
)
1129 if (cfg
.resize_action
== RESIZE_DISABLED
) return;
1130 if (h
== rows
&& w
== cols
) return;
1132 /* Sanity checks ... */
1134 static int first_time
= 1;
1137 switch (first_time
) {
1139 /* Get the size of the screen */
1140 if (GetClientRect(GetDesktopWindow(), &ss
))
1141 /* first_time = 0 */ ;
1147 /* Make sure the values are sane */
1148 width
= (ss
.right
- ss
.left
- extra_width
) / 4;
1149 height
= (ss
.bottom
- ss
.top
- extra_height
) / 6;
1151 if (w
> width
|| h
> height
)
1160 term_size(h
, w
, cfg
.savelines
);
1162 if (cfg
.resize_action
!= RESIZE_FONT
&& !IsZoomed(hwnd
)) {
1163 width
= extra_width
+ font_width
* w
;
1164 height
= extra_height
+ font_height
* h
;
1166 SetWindowPos(hwnd
, NULL
, 0, 0, width
, height
,
1167 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1168 SWP_NOMOVE
| SWP_NOZORDER
);
1172 InvalidateRect(hwnd
, NULL
, TRUE
);
1175 static void reset_window(int reinit
) {
1177 * This function decides how to resize or redraw when the
1178 * user changes something.
1180 * This function doesn't like to change the terminal size but if the
1181 * font size is locked that may be it's only soluion.
1183 int win_width
, win_height
;
1186 #ifdef RDB_DEBUG_PATCH
1187 debug((27, "reset_window()"));
1190 /* Current window sizes ... */
1191 GetWindowRect(hwnd
, &wr
);
1192 GetClientRect(hwnd
, &cr
);
1194 win_width
= cr
.right
- cr
.left
;
1195 win_height
= cr
.bottom
- cr
.top
;
1197 if (cfg
.resize_action
== RESIZE_DISABLED
) reinit
= 2;
1199 /* Are we being forced to reload the fonts ? */
1201 #ifdef RDB_DEBUG_PATCH
1202 debug((27, "reset_window() -- Forced deinit"));
1208 /* Oh, looks like we're minimised */
1209 if (win_width
== 0 || win_height
== 0)
1212 /* Is the window out of position ? */
1214 (offset_width
!= (win_width
-font_width
*cols
)/2 ||
1215 offset_height
!= (win_height
-font_height
*rows
)/2) ){
1216 offset_width
= (win_width
-font_width
*cols
)/2;
1217 offset_height
= (win_height
-font_height
*rows
)/2;
1218 InvalidateRect(hwnd
, NULL
, TRUE
);
1219 #ifdef RDB_DEBUG_PATCH
1220 debug((27, "reset_window() -> Reposition terminal"));
1224 if (IsZoomed(hwnd
)) {
1225 /* We're fullscreen, this means we must not change the size of
1226 * the window so it's the font size or the terminal itself.
1229 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
;
1230 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
;
1232 if (cfg
.resize_action
!= RESIZE_TERM
) {
1233 if ( font_width
!= win_width
/cols
||
1234 font_height
!= win_height
/rows
) {
1236 init_fonts(win_width
/cols
, win_height
/rows
);
1237 offset_width
= (win_width
-font_width
*cols
)/2;
1238 offset_height
= (win_height
-font_height
*rows
)/2;
1239 InvalidateRect(hwnd
, NULL
, TRUE
);
1240 #ifdef RDB_DEBUG_PATCH
1241 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1242 font_width
, font_height
));
1246 if ( font_width
!= win_width
/cols
||
1247 font_height
!= win_height
/rows
) {
1248 /* Our only choice at this point is to change the
1249 * size of the terminal; Oh well.
1251 term_size( win_height
/font_height
, win_width
/font_width
,
1253 offset_width
= (win_width
-font_width
*cols
)/2;
1254 offset_height
= (win_height
-font_height
*rows
)/2;
1255 InvalidateRect(hwnd
, NULL
, TRUE
);
1256 #ifdef RDB_DEBUG_PATCH
1257 debug((27, "reset_window() -> Zoomed term_size"));
1264 /* Hmm, a force re-init means we should ignore the current window
1265 * so we resize to the default font size.
1268 #ifdef RDB_DEBUG_PATCH
1269 debug((27, "reset_window() -> Forced re-init"));
1272 offset_width
= offset_height
= cfg
.window_border
;
1273 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1274 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1276 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1277 win_height
!= font_height
*rows
+ offset_height
*2) {
1279 /* If this is too large windows will resize it to the maximum
1280 * allowed window size, we will then be back in here and resize
1281 * the font or terminal to fit.
1283 SetWindowPos(hwnd
, NULL
, 0, 0,
1284 font_width
*cols
+ extra_width
,
1285 font_height
*rows
+ extra_height
,
1286 SWP_NOMOVE
| SWP_NOZORDER
);
1289 InvalidateRect(hwnd
, NULL
, TRUE
);
1293 /* Okay the user doesn't want us to change the font so we try the
1294 * window. But that may be too big for the screen which forces us
1295 * to change the terminal.
1297 if ((cfg
.resize_action
== RESIZE_TERM
&& reinit
<=0) ||
1298 (cfg
.resize_action
== RESIZE_EITHER
&& reinit
<0) ||
1300 offset_width
= offset_height
= cfg
.window_border
;
1301 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+ offset_width
*2;
1302 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1304 if (win_width
!= font_width
*cols
+ offset_width
*2 ||
1305 win_height
!= font_height
*rows
+ offset_height
*2) {
1310 GetClientRect(GetDesktopWindow(), &ss
);
1311 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1312 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1315 if ( rows
> height
|| cols
> width
) {
1316 if (cfg
.resize_action
== RESIZE_EITHER
) {
1317 /* Make the font the biggest we can */
1319 font_width
= (ss
.right
- ss
.left
- extra_width
)/cols
;
1321 font_height
= (ss
.bottom
- ss
.top
- extra_height
)/rows
;
1324 init_fonts(font_width
, font_height
);
1326 width
= (ss
.right
- ss
.left
- extra_width
) / font_width
;
1327 height
= (ss
.bottom
- ss
.top
- extra_height
) / font_height
;
1329 if ( height
> rows
) height
= rows
;
1330 if ( width
> cols
) width
= cols
;
1331 term_size(height
, width
, cfg
.savelines
);
1332 #ifdef RDB_DEBUG_PATCH
1333 debug((27, "reset_window() -> term resize to (%d,%d)",
1339 SetWindowPos(hwnd
, NULL
, 0, 0,
1340 font_width
*cols
+ extra_width
,
1341 font_height
*rows
+ extra_height
,
1342 SWP_NOMOVE
| SWP_NOZORDER
);
1344 InvalidateRect(hwnd
, NULL
, TRUE
);
1345 #ifdef RDB_DEBUG_PATCH
1346 debug((27, "reset_window() -> window resize to (%d,%d)",
1347 font_width
*cols
+ extra_width
,
1348 font_height
*rows
+ extra_height
));
1354 /* We're allowed to or must change the font but do we want to ? */
1356 if (font_width
!= (win_width
-cfg
.window_border
*2)/cols
||
1357 font_height
!= (win_height
-cfg
.window_border
*2)/rows
) {
1360 init_fonts((win_width
-cfg
.window_border
*2)/cols
,
1361 (win_height
-cfg
.window_border
*2)/rows
);
1362 offset_width
= (win_width
-font_width
*cols
)/2;
1363 offset_height
= (win_height
-font_height
*rows
)/2;
1365 extra_width
= wr
.right
- wr
.left
- cr
.right
+ cr
.left
+offset_width
*2;
1366 extra_height
= wr
.bottom
- wr
.top
- cr
.bottom
+ cr
.top
+offset_height
*2;
1368 InvalidateRect(hwnd
, NULL
, TRUE
);
1369 #ifdef RDB_DEBUG_PATCH
1370 debug((25, "reset_window() -> font resize to (%d,%d)",
1371 font_width
, font_height
));
1376 static void set_input_locale(HKL kl
)
1380 GetLocaleInfo(LOWORD(kl
), LOCALE_IDEFAULTANSICODEPAGE
,
1381 lbuf
, sizeof(lbuf
));
1383 kbd_codepage
= atoi(lbuf
);
1386 static void click(Mouse_Button b
, int x
, int y
, int shift
, int ctrl
, int alt
)
1388 int thistime
= GetMessageTime();
1390 if (send_raw_mouse
&& !(cfg
.mouse_override
&& shift
)) {
1391 lastbtn
= MBT_NOTHING
;
1392 term_mouse(b
, MA_CLICK
, x
, y
, shift
, ctrl
, alt
);
1396 if (lastbtn
== b
&& thistime
- lasttime
< dbltime
) {
1397 lastact
= (lastact
== MA_CLICK ? MA_2CLK
:
1398 lastact
== MA_2CLK ? MA_3CLK
:
1399 lastact
== MA_3CLK ? MA_CLICK
: MA_NOTHING
);
1404 if (lastact
!= MA_NOTHING
)
1405 term_mouse(b
, lastact
, x
, y
, shift
, ctrl
, alt
);
1406 lasttime
= thistime
;
1410 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1411 * into a cooked one (SELECT, EXTEND, PASTE).
1413 Mouse_Button
translate_button(Mouse_Button button
)
1415 if (button
== MBT_LEFT
)
1417 if (button
== MBT_MIDDLE
)
1418 return cfg
.mouse_is_xterm ? MBT_PASTE
: MBT_EXTEND
;
1419 if (button
== MBT_RIGHT
)
1420 return cfg
.mouse_is_xterm ? MBT_EXTEND
: MBT_PASTE
;
1421 return 0; /* shouldn't happen */
1424 static void show_mouseptr(int show
)
1426 static int cursor_visible
= 1;
1427 if (!cfg
.hide_mouseptr
) /* override if this feature disabled */
1429 if (cursor_visible
&& !show
)
1431 else if (!cursor_visible
&& show
)
1433 cursor_visible
= show
;
1436 static int is_alt_pressed(void)
1439 int r
= GetKeyboardState(keystate
);
1442 if (keystate
[VK_MENU
] & 0x80)
1444 if (keystate
[VK_RMENU
] & 0x80)
1449 static int resizing
;
1451 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1452 WPARAM wParam
, LPARAM lParam
)
1455 static int ignore_clip
= FALSE
;
1456 static int need_backend_resize
= FALSE
;
1460 if (pending_netevent
)
1461 enact_pending_netevent();
1467 if (cfg
.ping_interval
> 0) {
1470 if (now
- last_movement
> cfg
.ping_interval
) {
1471 back
->special(TS_PING
);
1472 last_movement
= now
;
1475 net_pending_errors();
1481 if (!cfg
.warn_on_close
|| session_closed
||
1483 "Are you sure you want to close this session?",
1484 "PuTTY Exit Confirmation",
1485 MB_ICONWARNING
| MB_OKCANCEL
) == IDOK
)
1486 DestroyWindow(hwnd
);
1493 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1505 PROCESS_INFORMATION pi
;
1506 HANDLE filemap
= NULL
;
1508 if (wParam
== IDM_DUPSESS
) {
1510 * Allocate a file-mapping memory chunk for the
1513 SECURITY_ATTRIBUTES sa
;
1516 sa
.nLength
= sizeof(sa
);
1517 sa
.lpSecurityDescriptor
= NULL
;
1518 sa
.bInheritHandle
= TRUE
;
1519 filemap
= CreateFileMapping((HANDLE
) 0xFFFFFFFF,
1522 0, sizeof(Config
), NULL
);
1524 p
= (Config
*) MapViewOfFile(filemap
,
1526 0, 0, sizeof(Config
));
1528 *p
= cfg
; /* structure copy */
1532 sprintf(c
, "putty &%p", filemap
);
1534 } else if (wParam
== IDM_SAVEDSESS
) {
1536 sessions
[(lParam
- IDM_SAVED_MIN
) / 16];
1537 cl
= smalloc(16 + strlen(session
)); /* 8, but play safe */
1539 cl
= NULL
; /* not a very important failure mode */
1541 sprintf(cl
, "putty @%s", session
);
1547 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1549 si
.lpReserved
= NULL
;
1550 si
.lpDesktop
= NULL
;
1554 si
.lpReserved2
= NULL
;
1555 CreateProcess(b
, cl
, NULL
, NULL
, TRUE
,
1556 NORMAL_PRIORITY_CLASS
, NULL
, NULL
, &si
, &pi
);
1559 CloseHandle(filemap
);
1569 GetWindowText(hwnd
, cfg
.wintitle
, sizeof(cfg
.wintitle
));
1572 if (!do_reconfig(hwnd
))
1576 /* Disable full-screen if resizing forbidden */
1577 HMENU m
= GetSystemMenu (hwnd
, FALSE
);
1578 EnableMenuItem(m
, IDM_FULLSCREEN
, MF_BYCOMMAND
|
1579 (cfg
.resize_action
== RESIZE_DISABLED
)
1580 ? MF_GRAYED
: MF_ENABLED
);
1581 /* Gracefully unzoom if necessary */
1583 (cfg
.resize_action
== RESIZE_DISABLED
)) {
1588 if (strcmp(prev_cfg
.logfilename
, cfg
.logfilename
) ||
1589 prev_cfg
.logtype
!= cfg
.logtype
) {
1590 logfclose(); /* reset logging */
1596 * Flush the line discipline's edit buffer in the
1597 * case where local editing has just been disabled.
1599 ldisc_send(NULL
, 0, 0);
1607 /* Screen size changed ? */
1608 if (cfg
.height
!= prev_cfg
.height
||
1609 cfg
.width
!= prev_cfg
.width
||
1610 cfg
.savelines
!= prev_cfg
.savelines
||
1611 cfg
.resize_action
== RESIZE_FONT
||
1612 cfg
.resize_action
== RESIZE_DISABLED
)
1613 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
1615 /* Enable or disable the scroll bar, etc */
1617 LONG nflg
, flag
= GetWindowLong(hwnd
, GWL_STYLE
);
1618 LONG nexflag
, exflag
=
1619 GetWindowLong(hwnd
, GWL_EXSTYLE
);
1622 if (cfg
.alwaysontop
!= prev_cfg
.alwaysontop
) {
1623 if (cfg
.alwaysontop
) {
1624 nexflag
|= WS_EX_TOPMOST
;
1625 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1626 SWP_NOMOVE
| SWP_NOSIZE
);
1628 nexflag
&= ~(WS_EX_TOPMOST
);
1629 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
1630 SWP_NOMOVE
| SWP_NOSIZE
);
1633 if (cfg
.sunken_edge
)
1634 nexflag
|= WS_EX_CLIENTEDGE
;
1636 nexflag
&= ~(WS_EX_CLIENTEDGE
);
1640 cfg
.scrollbar_in_fullscreen
: cfg
.scrollbar
)
1643 nflg
&= ~WS_VSCROLL
;
1644 if (cfg
.resize_action
== RESIZE_DISABLED
)
1645 nflg
&= ~(WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1647 nflg
|= (WS_THICKFRAME
| WS_MAXIMIZEBOX
);
1649 if (nflg
!= flag
|| nexflag
!= exflag
) {
1651 SetWindowLong(hwnd
, GWL_STYLE
, nflg
);
1652 if (nexflag
!= exflag
)
1653 SetWindowLong(hwnd
, GWL_EXSTYLE
, nexflag
);
1655 SetWindowPos(hwnd
, NULL
, 0, 0, 0, 0,
1656 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
1657 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
|
1665 if (cfg
.resize_action
== RESIZE_DISABLED
&& IsZoomed(hwnd
)) {
1670 set_title(cfg
.wintitle
);
1671 if (IsIconic(hwnd
)) {
1673 cfg
.win_name_always ? window_name
:
1677 if (strcmp(cfg
.font
, prev_cfg
.font
) != 0 ||
1678 strcmp(cfg
.line_codepage
, prev_cfg
.line_codepage
) != 0 ||
1679 cfg
.fontisbold
!= prev_cfg
.fontisbold
||
1680 cfg
.fontheight
!= prev_cfg
.fontheight
||
1681 cfg
.fontcharset
!= prev_cfg
.fontcharset
||
1682 cfg
.vtmode
!= prev_cfg
.vtmode
||
1683 cfg
.bold_colour
!= prev_cfg
.bold_colour
||
1684 cfg
.resize_action
== RESIZE_DISABLED
||
1685 cfg
.resize_action
== RESIZE_EITHER
||
1686 (cfg
.resize_action
!= prev_cfg
.resize_action
))
1689 InvalidateRect(hwnd
, NULL
, TRUE
);
1690 reset_window(init_lvl
);
1691 net_pending_errors();
1704 back
->special(TS_AYT
);
1705 net_pending_errors();
1708 back
->special(TS_BRK
);
1709 net_pending_errors();
1712 back
->special(TS_SYNCH
);
1713 net_pending_errors();
1716 back
->special(TS_EC
);
1717 net_pending_errors();
1720 back
->special(TS_EL
);
1721 net_pending_errors();
1724 back
->special(TS_GA
);
1725 net_pending_errors();
1728 back
->special(TS_NOP
);
1729 net_pending_errors();
1732 back
->special(TS_ABORT
);
1733 net_pending_errors();
1736 back
->special(TS_AO
);
1737 net_pending_errors();
1740 back
->special(TS_IP
);
1741 net_pending_errors();
1744 back
->special(TS_SUSP
);
1745 net_pending_errors();
1748 back
->special(TS_EOR
);
1749 net_pending_errors();
1752 back
->special(TS_EOF
);
1753 net_pending_errors();
1760 * We get this if the System menu has been activated
1767 * We get this if the System menu has been activated
1768 * using the keyboard. This might happen from within
1769 * TranslateKey, in which case it really wants to be
1770 * followed by a `space' character to actually _bring
1771 * the menu up_ rather than just sitting there in
1772 * `ready to appear' state.
1774 show_mouseptr(1); /* make sure pointer is visible */
1776 PostMessage(hwnd
, WM_CHAR
, ' ', 0);
1778 case IDM_FULLSCREEN
:
1782 if (wParam
>= IDM_SAVED_MIN
&& wParam
<= IDM_SAVED_MAX
) {
1783 SendMessage(hwnd
, WM_SYSCOMMAND
, IDM_SAVEDSESS
, wParam
);
1788 #define X_POS(l) ((int)(short)LOWORD(l))
1789 #define Y_POS(l) ((int)(short)HIWORD(l))
1791 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1792 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1793 #define WHEEL_DELTA 120
1796 wheel_accumulator
+= (short) HIWORD(wParam
);
1797 wParam
= LOWORD(wParam
);
1799 /* process events when the threshold is reached */
1800 while (abs(wheel_accumulator
) >= WHEEL_DELTA
) {
1803 /* reduce amount for next time */
1804 if (wheel_accumulator
> 0) {
1806 wheel_accumulator
-= WHEEL_DELTA
;
1807 } else if (wheel_accumulator
< 0) {
1809 wheel_accumulator
+= WHEEL_DELTA
;
1813 if (send_raw_mouse
) {
1814 /* send a mouse-down followed by a mouse up */
1818 TO_CHR_X(X_POS(lParam
)),
1819 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1820 wParam
& MK_CONTROL
, is_alt_pressed());
1821 term_mouse(b
, MA_RELEASE
, TO_CHR_X(X_POS(lParam
)),
1822 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1823 wParam
& MK_CONTROL
, is_alt_pressed());
1825 /* trigger a scroll */
1827 b
== MBT_WHEEL_UP ?
-rows
/ 2 : rows
/ 2);
1832 case WM_LBUTTONDOWN
:
1833 case WM_MBUTTONDOWN
:
1834 case WM_RBUTTONDOWN
:
1842 case WM_LBUTTONDOWN
:
1846 case WM_MBUTTONDOWN
:
1847 button
= MBT_MIDDLE
;
1850 case WM_RBUTTONDOWN
:
1859 button
= MBT_MIDDLE
;
1867 button
= press
= 0; /* shouldn't happen */
1871 * Special case: in full-screen mode, if the left
1872 * button is clicked in the very top left corner of the
1873 * window, we put up the System menu instead of doing
1876 if (full_screen
&& press
&& button
== MBT_LEFT
&&
1877 X_POS(lParam
) == 0 && Y_POS(lParam
) == 0) {
1878 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_MOUSEMENU
, 0);
1883 TO_CHR_X(X_POS(lParam
)), TO_CHR_Y(Y_POS(lParam
)),
1884 wParam
& MK_SHIFT
, wParam
& MK_CONTROL
,
1888 term_mouse(button
, MA_RELEASE
,
1889 TO_CHR_X(X_POS(lParam
)),
1890 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1891 wParam
& MK_CONTROL
, is_alt_pressed());
1899 * Add the mouse position and message time to the random
1902 noise_ultralight(lParam
);
1904 if (wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
1906 if (wParam
& MK_LBUTTON
)
1908 else if (wParam
& MK_MBUTTON
)
1912 term_mouse(b
, MA_DRAG
, TO_CHR_X(X_POS(lParam
)),
1913 TO_CHR_Y(Y_POS(lParam
)), wParam
& MK_SHIFT
,
1914 wParam
& MK_CONTROL
, is_alt_pressed());
1917 case WM_NCMOUSEMOVE
:
1919 noise_ultralight(lParam
);
1921 case WM_IGNORE_CLIP
:
1922 ignore_clip
= wParam
; /* don't panic on DESTROYCLIPBOARD */
1924 case WM_DESTROYCLIPBOARD
:
1927 ignore_clip
= FALSE
;
1933 hdc
= BeginPaint(hwnd
, &p
);
1935 SelectPalette(hdc
, pal
, TRUE
);
1936 RealizePalette(hdc
);
1939 (p
.rcPaint
.left
-offset_width
)/font_width
,
1940 (p
.rcPaint
.top
-offset_height
)/font_height
,
1941 (p
.rcPaint
.right
-offset_width
-1)/font_width
,
1942 (p
.rcPaint
.bottom
-offset_height
-1)/font_height
);
1945 p
.rcPaint
.left
< offset_width
||
1946 p
.rcPaint
.top
< offset_height
||
1947 p
.rcPaint
.right
>= offset_width
+ font_width
*cols
||
1948 p
.rcPaint
.bottom
>= offset_height
+ font_height
*rows
)
1950 HBRUSH fillcolour
, oldbrush
;
1952 fillcolour
= CreateSolidBrush (
1953 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1954 oldbrush
= SelectObject(hdc
, fillcolour
);
1955 edge
= CreatePen(PS_SOLID
, 0,
1956 colours
[(ATTR_DEFBG
>>ATTR_BGSHIFT
)*2]);
1957 oldpen
= SelectObject(hdc
, edge
);
1959 ExcludeClipRect(hdc
,
1960 offset_width
, offset_height
,
1961 offset_width
+font_width
*cols
,
1962 offset_height
+font_height
*rows
);
1964 Rectangle(hdc
, p
.rcPaint
.left
, p
.rcPaint
.top
,
1965 p
.rcPaint
.right
, p
.rcPaint
.bottom
);
1967 // SelectClipRgn(hdc, NULL);
1969 SelectObject(hdc
, oldbrush
);
1970 DeleteObject(fillcolour
);
1971 SelectObject(hdc
, oldpen
);
1974 SelectObject(hdc
, GetStockObject(SYSTEM_FONT
));
1975 SelectObject(hdc
, GetStockObject(WHITE_PEN
));
1981 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1982 * but the only one that's likely to try to overload us is FD_READ.
1983 * This means buffering just one is fine.
1985 if (pending_netevent
)
1986 enact_pending_netevent();
1988 pending_netevent
= TRUE
;
1989 pend_netevent_wParam
= wParam
;
1990 pend_netevent_lParam
= lParam
;
1991 if (WSAGETSELECTEVENT(lParam
) != FD_READ
)
1992 enact_pending_netevent();
1994 time(&last_movement
);
1998 CreateCaret(hwnd
, caretbm
, font_width
, font_height
);
2000 flash_window(0); /* stop */
2006 if (full_screen
) flip_full_screen();
2013 case WM_ENTERSIZEMOVE
:
2014 #ifdef RDB_DEBUG_PATCH
2015 debug((27, "WM_ENTERSIZEMOVE"));
2019 need_backend_resize
= FALSE
;
2021 case WM_EXITSIZEMOVE
:
2024 #ifdef RDB_DEBUG_PATCH
2025 debug((27, "WM_EXITSIZEMOVE"));
2027 if (need_backend_resize
) {
2028 term_size(cfg
.height
, cfg
.width
, cfg
.savelines
);
2029 InvalidateRect(hwnd
, NULL
, TRUE
);
2034 * This does two jobs:
2035 * 1) Keep the sizetip uptodate
2036 * 2) Make sure the window size is _stepped_ in units of the font size.
2038 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2039 int width
, height
, w
, h
, ew
, eh
;
2040 LPRECT r
= (LPRECT
) lParam
;
2042 if ( !need_backend_resize
&& cfg
.resize_action
== RESIZE_EITHER
&&
2043 (cfg
.height
!= rows
|| cfg
.width
!= cols
)) {
2045 * Great! It seems that both the terminal size and the
2046 * font size have been changed and the user is now dragging.
2048 * It will now be difficult to get back to the configured
2051 * This would be easier but it seems to be too confusing.
2053 term_size(cfg.height, cfg.width, cfg.savelines);
2056 cfg
.height
=rows
; cfg
.width
=cols
;
2058 InvalidateRect(hwnd
, NULL
, TRUE
);
2059 need_backend_resize
= TRUE
;
2062 width
= r
->right
- r
->left
- extra_width
;
2063 height
= r
->bottom
- r
->top
- extra_height
;
2064 w
= (width
+ font_width
/ 2) / font_width
;
2067 h
= (height
+ font_height
/ 2) / font_height
;
2070 UpdateSizeTip(hwnd
, w
, h
);
2071 ew
= width
- w
* font_width
;
2072 eh
= height
- h
* font_height
;
2074 if (wParam
== WMSZ_LEFT
||
2075 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2081 if (wParam
== WMSZ_TOP
||
2082 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2092 int width
, height
, w
, h
, rv
= 0;
2093 int ex_width
= extra_width
+ (cfg
.window_border
- offset_width
) * 2;
2094 int ex_height
= extra_height
+ (cfg
.window_border
- offset_height
) * 2;
2095 LPRECT r
= (LPRECT
) lParam
;
2097 width
= r
->right
- r
->left
- ex_width
;
2098 height
= r
->bottom
- r
->top
- ex_height
;
2100 w
= (width
+ cols
/2)/cols
;
2101 h
= (height
+ rows
/2)/rows
;
2102 if ( r
->right
!= r
->left
+ w
*cols
+ ex_width
)
2105 if (wParam
== WMSZ_LEFT
||
2106 wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_TOPLEFT
)
2107 r
->left
= r
->right
- w
*cols
- ex_width
;
2109 r
->right
= r
->left
+ w
*cols
+ ex_width
;
2111 if (r
->bottom
!= r
->top
+ h
*rows
+ ex_height
)
2114 if (wParam
== WMSZ_TOP
||
2115 wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_TOPLEFT
)
2116 r
->top
= r
->bottom
- h
*rows
- ex_height
;
2118 r
->bottom
= r
->top
+ h
*rows
+ ex_height
;
2122 /* break; (never reached) */
2124 #ifdef RDB_DEBUG_PATCH
2125 debug((27, "WM_SIZE %s (%d,%d)",
2126 (wParam
== SIZE_MINIMIZED
) ?
"SIZE_MINIMIZED":
2127 (wParam
== SIZE_MAXIMIZED
) ?
"SIZE_MAXIMIZED":
2128 (wParam
== SIZE_RESTORED
&& resizing
) ?
"to":
2129 (wParam
== SIZE_RESTORED
) ?
"SIZE_RESTORED":
2131 LOWORD(lParam
), HIWORD(lParam
)));
2133 if (wParam
== SIZE_MINIMIZED
)
2135 cfg
.win_name_always ? window_name
: icon_name
);
2136 if (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
)
2137 SetWindowText(hwnd
, window_name
);
2139 if (cfg
.resize_action
== RESIZE_DISABLED
) {
2140 /* A resize, well it better be a minimize. */
2144 int width
, height
, w
, h
;
2146 width
= LOWORD(lParam
);
2147 height
= HIWORD(lParam
);
2150 if (wParam
== SIZE_MAXIMIZED
&& !was_zoomed
) {
2154 if (cfg
.resize_action
== RESIZE_TERM
) {
2155 w
= width
/ font_width
;
2157 h
= height
/ font_height
;
2160 term_size(h
, w
, cfg
.savelines
);
2163 } else if (wParam
== SIZE_RESTORED
&& was_zoomed
) {
2165 if (cfg
.resize_action
== RESIZE_TERM
)
2166 term_size(prev_rows
, prev_cols
, cfg
.savelines
);
2167 if (cfg
.resize_action
!= RESIZE_FONT
)
2172 /* This is an unexpected resize, these will normally happen
2173 * if the window is too large. Probably either the user
2174 * selected a huge font or the screen size has changed.
2176 * This is also called with minimize.
2178 else reset_window(-1);
2182 * Don't call back->size in mid-resize. (To prevent
2183 * massive numbers of resize events getting sent
2184 * down the connection during an NT opaque drag.)
2187 if (cfg
.resize_action
!= RESIZE_FONT
&& !alt_pressed
) {
2188 need_backend_resize
= TRUE
;
2189 w
= (width
-cfg
.window_border
*2) / font_width
;
2191 h
= (height
-cfg
.window_border
*2) / font_height
;
2202 switch (LOWORD(wParam
)) {
2216 term_scroll(0, +rows
/ 2);
2219 term_scroll(0, -rows
/ 2);
2221 case SB_THUMBPOSITION
:
2223 term_scroll(1, HIWORD(wParam
));
2227 case WM_PALETTECHANGED
:
2228 if ((HWND
) wParam
!= hwnd
&& pal
!= NULL
) {
2229 HDC hdc
= get_ctx();
2231 if (RealizePalette(hdc
) > 0)
2237 case WM_QUERYNEWPALETTE
:
2239 HDC hdc
= get_ctx();
2241 if (RealizePalette(hdc
) > 0)
2253 * Add the scan code and keypress timing to the random
2256 noise_ultralight(lParam
);
2259 * We don't do TranslateMessage since it disassociates the
2260 * resulting CHAR message from the KEYDOWN that sparked it,
2261 * which we occasionally don't want. Instead, we process
2262 * KEYDOWN, and call the Win32 translator functions so that
2263 * we get the translations under _our_ control.
2266 unsigned char buf
[20];
2269 if (wParam
== VK_PROCESSKEY
) {
2272 m
.message
= WM_KEYDOWN
;
2274 m
.lParam
= lParam
& 0xdfff;
2275 TranslateMessage(&m
);
2277 len
= TranslateKey(message
, wParam
, lParam
, buf
);
2279 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2283 * Interrupt an ongoing paste. I'm not sure
2284 * this is sensible, but for the moment it's
2285 * preferable to having to faff about buffering
2291 * We need not bother about stdin backlogs
2292 * here, because in GUI PuTTY we can't do
2293 * anything about it anyway; there's no means
2294 * of asking Windows to hold off on KEYDOWN
2295 * messages. We _have_ to buffer everything
2298 ldisc_send(buf
, len
, 1);
2303 net_pending_errors();
2305 case WM_INPUTLANGCHANGE
:
2306 /* wParam == Font number */
2307 /* lParam == Locale */
2308 set_input_locale((HKL
)lParam
);
2311 if(wParam
== IMN_SETOPENSTATUS
) {
2312 HIMC hImc
= ImmGetContext(hwnd
);
2313 ImmSetCompositionFont(hImc
, &lfont
);
2314 ImmReleaseContext(hwnd
, hImc
);
2318 case WM_IME_COMPOSITION
:
2324 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
||
2325 osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) break; /* no Unicode */
2327 if ((lParam
& GCS_RESULTSTR
) == 0) /* Composition unfinished. */
2328 break; /* fall back to DefWindowProc */
2330 hIMC
= ImmGetContext(hwnd
);
2331 n
= ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, NULL
, 0);
2334 buff
= (char*) smalloc(n
);
2335 ImmGetCompositionStringW(hIMC
, GCS_RESULTSTR
, buff
, n
);
2336 luni_send((unsigned short *)buff
, n
/ 2, 1);
2339 ImmReleaseContext(hwnd
, hIMC
);
2344 if (wParam
& 0xFF00) {
2345 unsigned char buf
[2];
2348 buf
[0] = wParam
>> 8;
2349 lpage_send(kbd_codepage
, buf
, 2, 1);
2351 char c
= (unsigned char) wParam
;
2352 lpage_send(kbd_codepage
, &c
, 1, 1);
2358 * Nevertheless, we are prepared to deal with WM_CHAR
2359 * messages, should they crop up. So if someone wants to
2360 * post the things to us as part of a macro manoeuvre,
2361 * we're ready to cope.
2364 char c
= (unsigned char)wParam
;
2365 lpage_send(CP_ACP
, &c
, 1, 1);
2369 if (send_raw_mouse
&& LOWORD(lParam
) == HTCLIENT
) {
2370 SetCursor(LoadCursor(NULL
, IDC_ARROW
));
2375 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
2379 * Move the system caret. (We maintain one, even though it's
2380 * invisible, for the benefit of blind people: apparently some
2381 * helper software tracks the system caret, so we should arrange to
2384 void sys_cursor(int x
, int y
)
2389 if (!has_focus
) return;
2391 SetCaretPos(x
* font_width
+ offset_width
,
2392 y
* font_height
+ offset_height
);
2394 /* IMM calls on Win98 and beyond only */
2395 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
) return; /* 3.11 */
2397 if(osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
&&
2398 osVersion
.dwMinorVersion
== 0) return; /* 95 */
2400 /* we should have the IMM functions */
2401 hIMC
= ImmGetContext(hwnd
);
2402 cf
.dwStyle
= CFS_POINT
;
2403 cf
.ptCurrentPos
.x
= x
* font_width
+ offset_width
;
2404 cf
.ptCurrentPos
.y
= y
* font_height
+ offset_height
;
2405 ImmSetCompositionWindow(hIMC
, &cf
);
2407 ImmReleaseContext(hwnd
, hIMC
);
2411 * Draw a line of text in the window, at given character
2412 * coordinates, in given attributes.
2414 * We are allowed to fiddle with the contents of `text'.
2416 void do_text(Context ctx
, int x
, int y
, char *text
, int len
,
2417 unsigned long attr
, int lattr
)
2420 int nfg
, nbg
, nfont
;
2423 int force_manual_underline
= 0;
2424 int fnt_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2425 int char_width
= fnt_width
;
2426 int text_adjust
= 0;
2427 static int *IpDx
= 0, IpDxLEN
= 0;
2429 if (attr
& ATTR_WIDE
)
2432 if (len
> IpDxLEN
|| IpDx
[0] != char_width
) {
2434 if (len
> IpDxLEN
) {
2436 IpDx
= smalloc((len
+ 16) * sizeof(int));
2437 IpDxLEN
= (len
+ 16);
2439 for (i
= 0; i
< IpDxLEN
; i
++)
2440 IpDx
[i
] = char_width
;
2443 /* Only want the left half of double width lines */
2444 if (lattr
!= LATTR_NORM
&& x
*2 >= cols
)
2452 if ((attr
& TATTR_ACTCURS
) && (cfg
.cursor_type
== 0 || big_cursor
)) {
2453 attr
&= ATTR_CUR_AND
| (bold_mode
!= BOLD_COLOURS ? ATTR_BOLD
: 0);
2454 attr
^= ATTR_CUR_XOR
;
2458 if (cfg
.vtmode
== VT_POORMAN
&& lattr
!= LATTR_NORM
) {
2459 /* Assume a poorman font is borken in other ways too. */
2469 nfont
|= FONT_WIDE
+ FONT_HIGH
;
2472 if (attr
& ATTR_NARROW
)
2473 nfont
|= FONT_NARROW
;
2475 /* Special hack for the VT100 linedraw glyphs. */
2476 if ((attr
& CSET_MASK
) == 0x2300) {
2477 if (text
[0] >= (char) 0xBA && text
[0] <= (char) 0xBD) {
2478 switch ((unsigned char) (text
[0])) {
2480 text_adjust
= -2 * font_height
/ 5;
2483 text_adjust
= -1 * font_height
/ 5;
2486 text_adjust
= font_height
/ 5;
2489 text_adjust
= 2 * font_height
/ 5;
2492 if (lattr
== LATTR_TOP
|| lattr
== LATTR_BOT
)
2495 text
[0] = (char) (unitab_xterm
['q'] & CHAR_MASK
);
2496 attr
|= (unitab_xterm
['q'] & CSET_MASK
);
2497 if (attr
& ATTR_UNDER
) {
2498 attr
&= ~ATTR_UNDER
;
2499 force_manual_underline
= 1;
2504 /* Anything left as an original character set is unprintable. */
2505 if (DIRECT_CHAR(attr
)) {
2508 memset(text
, 0xFD, len
);
2512 if ((attr
& CSET_MASK
) == ATTR_OEMCP
)
2515 nfg
= 2 * ((attr
& ATTR_FGMASK
) >> ATTR_FGSHIFT
);
2516 nbg
= 2 * ((attr
& ATTR_BGMASK
) >> ATTR_BGSHIFT
);
2517 if (bold_mode
== BOLD_FONT
&& (attr
& ATTR_BOLD
))
2519 if (und_mode
== UND_FONT
&& (attr
& ATTR_UNDER
))
2520 nfont
|= FONT_UNDERLINE
;
2521 another_font(nfont
);
2522 if (!fonts
[nfont
]) {
2523 if (nfont
& FONT_UNDERLINE
)
2524 force_manual_underline
= 1;
2525 /* Don't do the same for manual bold, it could be bad news. */
2527 nfont
&= ~(FONT_BOLD
| FONT_UNDERLINE
);
2529 another_font(nfont
);
2531 nfont
= FONT_NORMAL
;
2532 if (attr
& ATTR_REVERSE
) {
2537 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BOLD
))
2539 if (bold_mode
== BOLD_COLOURS
&& (attr
& ATTR_BLINK
))
2543 SelectObject(hdc
, fonts
[nfont
]);
2544 SetTextColor(hdc
, fg
);
2545 SetBkColor(hdc
, bg
);
2546 SetBkMode(hdc
, OPAQUE
);
2549 line_box
.right
= x
+ char_width
* len
;
2550 line_box
.bottom
= y
+ font_height
;
2552 /* Only want the left half of double width lines */
2553 if (line_box
.right
> font_width
*cols
+offset_width
)
2554 line_box
.right
= font_width
*cols
+offset_width
;
2556 /* We're using a private area for direct to font. (512 chars.) */
2557 if (dbcs_screenfont
&& (attr
& CSET_MASK
) == ATTR_ACP
) {
2558 /* Ho Hum, dbcs fonts are a PITA! */
2559 /* To display on W9x I have to convert to UCS */
2560 static wchar_t *uni_buf
= 0;
2561 static int uni_len
= 0;
2563 if (len
> uni_len
) {
2565 uni_buf
= smalloc((uni_len
= len
) * sizeof(wchar_t));
2568 for(nlen
= mptr
= 0; mptr
<len
; mptr
++) {
2569 uni_buf
[nlen
] = 0xFFFD;
2570 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) text
[mptr
])) {
2571 IpDx
[nlen
] += char_width
;
2572 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2573 text
+mptr
, 2, uni_buf
+nlen
, 1);
2578 MultiByteToWideChar(font_codepage
, MB_USEGLYPHCHARS
,
2579 text
+mptr
, 1, uni_buf
+nlen
, 1);
2587 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2588 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, uni_buf
, nlen
, IpDx
);
2589 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2590 SetBkMode(hdc
, TRANSPARENT
);
2591 ExtTextOutW(hdc
, x
- 1,
2592 y
- font_height
* (lattr
==
2593 LATTR_BOT
) + text_adjust
,
2594 ETO_CLIPPED
, &line_box
, uni_buf
, nlen
, IpDx
);
2598 } else if (DIRECT_FONT(attr
)) {
2600 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2601 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, text
, len
, IpDx
);
2602 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2603 SetBkMode(hdc
, TRANSPARENT
);
2605 /* GRR: This draws the character outside it's box and can leave
2606 * 'droppings' even with the clip box! I suppose I could loop it
2607 * one character at a time ... yuk.
2609 * Or ... I could do a test print with "W", and use +1 or -1 for this
2610 * shift depending on if the leftmost column is blank...
2612 ExtTextOut(hdc
, x
- 1,
2613 y
- font_height
* (lattr
==
2614 LATTR_BOT
) + text_adjust
,
2615 ETO_CLIPPED
, &line_box
, text
, len
, IpDx
);
2618 /* And 'normal' unicode characters */
2619 static WCHAR
*wbuf
= NULL
;
2620 static int wlen
= 0;
2625 wbuf
= smalloc(wlen
* sizeof(WCHAR
));
2627 for (i
= 0; i
< len
; i
++)
2628 wbuf
[i
] = (WCHAR
) ((attr
& CSET_MASK
) + (text
[i
] & CHAR_MASK
));
2631 y
- font_height
* (lattr
== LATTR_BOT
) + text_adjust
,
2632 ETO_CLIPPED
| ETO_OPAQUE
, &line_box
, wbuf
, len
, IpDx
);
2634 /* And the shadow bold hack. */
2635 if (bold_mode
== BOLD_SHADOW
&& (attr
& ATTR_BOLD
)) {
2636 SetBkMode(hdc
, TRANSPARENT
);
2637 ExtTextOutW(hdc
, x
- 1,
2638 y
- font_height
* (lattr
==
2639 LATTR_BOT
) + text_adjust
,
2640 ETO_CLIPPED
, &line_box
, wbuf
, len
, IpDx
);
2643 if (lattr
!= LATTR_TOP
&& (force_manual_underline
||
2644 (und_mode
== UND_LINE
2645 && (attr
& ATTR_UNDER
)))) {
2648 if (lattr
== LATTR_BOT
)
2649 dec
= dec
* 2 - font_height
;
2651 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, fg
));
2652 MoveToEx(hdc
, x
, y
+ dec
, NULL
);
2653 LineTo(hdc
, x
+ len
* char_width
, y
+ dec
);
2654 oldpen
= SelectObject(hdc
, oldpen
);
2655 DeleteObject(oldpen
);
2659 void do_cursor(Context ctx
, int x
, int y
, char *text
, int len
,
2660 unsigned long attr
, int lattr
)
2666 int ctype
= cfg
.cursor_type
;
2668 if ((attr
& TATTR_ACTCURS
) && (ctype
== 0 || big_cursor
)) {
2669 if (((attr
& CSET_MASK
) | (unsigned char) *text
) != UCSWIDE
) {
2670 do_text(ctx
, x
, y
, text
, len
, attr
, lattr
);
2674 attr
|= TATTR_RIGHTCURS
;
2677 fnt_width
= char_width
= font_width
* (1 + (lattr
!= LATTR_NORM
));
2678 if (attr
& ATTR_WIDE
)
2685 if ((attr
& TATTR_PASCURS
) && (ctype
== 0 || big_cursor
)) {
2688 pts
[0].x
= pts
[1].x
= pts
[4].x
= x
;
2689 pts
[2].x
= pts
[3].x
= x
+ char_width
- 1;
2690 pts
[0].y
= pts
[3].y
= pts
[4].y
= y
;
2691 pts
[1].y
= pts
[2].y
= y
+ font_height
- 1;
2692 oldpen
= SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2693 Polyline(hdc
, pts
, 5);
2694 oldpen
= SelectObject(hdc
, oldpen
);
2695 DeleteObject(oldpen
);
2696 } else if ((attr
& (TATTR_ACTCURS
| TATTR_PASCURS
)) && ctype
!= 0) {
2697 int startx
, starty
, dx
, dy
, length
, i
;
2700 starty
= y
+ descent
;
2703 length
= char_width
;
2706 if (attr
& TATTR_RIGHTCURS
)
2707 xadjust
= char_width
- 1;
2708 startx
= x
+ xadjust
;
2712 length
= font_height
;
2714 if (attr
& TATTR_ACTCURS
) {
2717 SelectObject(hdc
, CreatePen(PS_SOLID
, 0, colours
[23]));
2718 MoveToEx(hdc
, startx
, starty
, NULL
);
2719 LineTo(hdc
, startx
+ dx
* length
, starty
+ dy
* length
);
2720 oldpen
= SelectObject(hdc
, oldpen
);
2721 DeleteObject(oldpen
);
2723 for (i
= 0; i
< length
; i
++) {
2725 SetPixel(hdc
, startx
, starty
, colours
[23]);
2734 /* This function gets the actual width of a character in the normal font.
2736 int CharWidth(Context ctx
, int uc
) {
2740 /* If the font max is the same as the font ave width then this
2741 * function is a no-op.
2743 if (!font_dualwidth
) return 1;
2745 switch (uc
& CSET_MASK
) {
2747 uc
= unitab_line
[uc
& 0xFF];
2750 uc
= unitab_xterm
[uc
& 0xFF];
2753 uc
= unitab_scoacs
[uc
& 0xFF];
2756 if (DIRECT_FONT(uc
)) {
2757 if (dbcs_screenfont
) return 1;
2759 /* Speedup, I know of no font where ascii is the wrong width */
2760 if ((uc
&CHAR_MASK
) >= ' ' && (uc
&CHAR_MASK
)<= '~')
2763 if ( (uc
& CSET_MASK
) == ATTR_ACP
) {
2764 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2765 } else if ( (uc
& CSET_MASK
) == ATTR_OEMCP
) {
2766 another_font(FONT_OEM
);
2767 if (!fonts
[FONT_OEM
]) return 0;
2769 SelectObject(hdc
, fonts
[FONT_OEM
]);
2773 if ( GetCharWidth32(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1 &&
2774 GetCharWidth(hdc
, uc
&CHAR_MASK
, uc
&CHAR_MASK
, &ibuf
) != 1)
2777 /* Speedup, I know of no font where ascii is the wrong width */
2778 if (uc
>= ' ' && uc
<= '~') return 1;
2780 SelectObject(hdc
, fonts
[FONT_NORMAL
]);
2781 if ( GetCharWidth32W(hdc
, uc
, uc
, &ibuf
) == 1 )
2782 /* Okay that one worked */ ;
2783 else if ( GetCharWidthW(hdc
, uc
, uc
, &ibuf
) == 1 )
2784 /* This should work on 9x too, but it's "less accurate" */ ;
2789 ibuf
+= font_width
/ 2 -1;
2796 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2797 * codes. Returns number of bytes used or zero to drop the message
2798 * or -1 to forward the message to windows.
2800 static int TranslateKey(UINT message
, WPARAM wParam
, LPARAM lParam
,
2801 unsigned char *output
)
2804 int scan
, left_alt
= 0, key_down
, shift_state
;
2806 unsigned char *p
= output
;
2807 static int alt_sum
= 0;
2809 HKL kbd_layout
= GetKeyboardLayout(0);
2811 static WORD keys
[3];
2812 static int compose_char
= 0;
2813 static WPARAM compose_key
= 0;
2815 r
= GetKeyboardState(keystate
);
2817 memset(keystate
, 0, sizeof(keystate
));
2820 #define SHOW_TOASCII_RESULT
2821 { /* Tell us all about key events */
2822 static BYTE oldstate
[256];
2823 static int first
= 1;
2827 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2830 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
) {
2832 } else if ((HIWORD(lParam
) & KF_UP
)
2833 && scan
== (HIWORD(lParam
) & 0xFF)) {
2837 if (wParam
>= VK_F1
&& wParam
<= VK_F20
)
2838 debug(("K_F%d", wParam
+ 1 - VK_F1
));
2851 debug(("VK_%02x", wParam
));
2853 if (message
== WM_SYSKEYDOWN
|| message
== WM_SYSKEYUP
)
2855 debug((", S%02x", scan
= (HIWORD(lParam
) & 0xFF)));
2857 ch
= MapVirtualKeyEx(wParam
, 2, kbd_layout
);
2858 if (ch
>= ' ' && ch
<= '~')
2859 debug((", '%c'", ch
));
2861 debug((", $%02x", ch
));
2864 debug((", KB0=%02x", keys
[0]));
2866 debug((", KB1=%02x", keys
[1]));
2868 debug((", KB2=%02x", keys
[2]));
2870 if ((keystate
[VK_SHIFT
] & 0x80) != 0)
2872 if ((keystate
[VK_CONTROL
] & 0x80) != 0)
2874 if ((HIWORD(lParam
) & KF_EXTENDED
))
2876 if ((HIWORD(lParam
) & KF_UP
))
2880 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
);
2881 else if ((HIWORD(lParam
) & KF_UP
))
2882 oldstate
[wParam
& 0xFF] ^= 0x80;
2884 oldstate
[wParam
& 0xFF] ^= 0x81;
2886 for (ch
= 0; ch
< 256; ch
++)
2887 if (oldstate
[ch
] != keystate
[ch
])
2888 debug((", M%02x=%02x", ch
, keystate
[ch
]));
2890 memcpy(oldstate
, keystate
, sizeof(oldstate
));
2894 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
2895 keystate
[VK_RMENU
] = keystate
[VK_MENU
];
2899 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2900 if ((cfg
.funky_type
== 3 ||
2901 (cfg
.funky_type
<= 1 && app_keypad_keys
&& !cfg
.no_applic_k
))
2902 && wParam
== VK_NUMLOCK
&& !(keystate
[VK_SHIFT
] & 0x80)) {
2904 wParam
= VK_EXECUTE
;
2906 /* UnToggle NUMLock */
2907 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0)
2908 keystate
[VK_NUMLOCK
] ^= 1;
2911 /* And write back the 'adjusted' state */
2912 SetKeyboardState(keystate
);
2915 /* Disable Auto repeat if required */
2916 if (repeat_off
&& (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == KF_REPEAT
)
2919 if ((HIWORD(lParam
) & KF_ALTDOWN
) && (keystate
[VK_RMENU
] & 0x80) == 0)
2922 key_down
= ((HIWORD(lParam
) & KF_UP
) == 0);
2924 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2925 if (left_alt
&& (keystate
[VK_CONTROL
] & 0x80)) {
2926 if (cfg
.ctrlaltkeys
)
2927 keystate
[VK_MENU
] = 0;
2929 keystate
[VK_RMENU
] = 0x80;
2934 alt_pressed
= (left_alt
&& key_down
);
2936 scan
= (HIWORD(lParam
) & (KF_UP
| KF_EXTENDED
| 0xFF));
2937 shift_state
= ((keystate
[VK_SHIFT
] & 0x80) != 0)
2938 + ((keystate
[VK_CONTROL
] & 0x80) != 0) * 2;
2940 /* Note if AltGr was pressed and if it was used as a compose key */
2941 if (!compose_state
) {
2942 compose_key
= 0x100;
2943 if (cfg
.compose_key
) {
2944 if (wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
))
2945 compose_key
= wParam
;
2947 if (wParam
== VK_APPS
)
2948 compose_key
= wParam
;
2951 if (wParam
== compose_key
) {
2952 if (compose_state
== 0
2953 && (HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) == 0) compose_state
=
2955 else if (compose_state
== 1 && (HIWORD(lParam
) & KF_UP
))
2959 } else if (compose_state
== 1 && wParam
!= VK_CONTROL
)
2963 * Record that we pressed key so the scroll window can be reset, but
2964 * be careful to avoid Shift-UP/Down
2966 if (wParam
!= VK_SHIFT
&& wParam
!= VK_PRIOR
&& wParam
!= VK_NEXT
&&
2967 wParam
!= VK_MENU
&& wParam
!= VK_CONTROL
) {
2971 if (compose_state
> 1 && left_alt
)
2974 /* Sanitize the number pad if not using a PC NumPad */
2975 if (left_alt
|| (app_keypad_keys
&& !cfg
.no_applic_k
2976 && cfg
.funky_type
!= 2)
2977 || cfg
.funky_type
== 3 || cfg
.nethack_keypad
|| compose_state
) {
2978 if ((HIWORD(lParam
) & KF_EXTENDED
) == 0) {
2982 nParam
= VK_NUMPAD0
;
2985 nParam
= VK_NUMPAD1
;
2988 nParam
= VK_NUMPAD2
;
2991 nParam
= VK_NUMPAD3
;
2994 nParam
= VK_NUMPAD4
;
2997 nParam
= VK_NUMPAD5
;
3000 nParam
= VK_NUMPAD6
;
3003 nParam
= VK_NUMPAD7
;
3006 nParam
= VK_NUMPAD8
;
3009 nParam
= VK_NUMPAD9
;
3012 nParam
= VK_DECIMAL
;
3016 if (keystate
[VK_NUMLOCK
] & 1)
3023 /* If a key is pressed and AltGr is not active */
3024 if (key_down
&& (keystate
[VK_RMENU
] & 0x80) == 0 && !compose_state
) {
3025 /* Okay, prepare for most alts then ... */
3029 /* Lets see if it's a pattern we know all about ... */
3030 if (wParam
== VK_PRIOR
&& shift_state
== 1) {
3031 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEUP
, 0);
3034 if (wParam
== VK_NEXT
&& shift_state
== 1) {
3035 SendMessage(hwnd
, WM_VSCROLL
, SB_PAGEDOWN
, 0);
3038 if (wParam
== VK_INSERT
&& shift_state
== 1) {
3042 if (left_alt
&& wParam
== VK_F4
&& cfg
.alt_f4
) {
3045 if (left_alt
&& wParam
== VK_SPACE
&& cfg
.alt_space
) {
3046 SendMessage(hwnd
, WM_SYSCOMMAND
, SC_KEYMENU
, 0);
3049 if (left_alt
&& wParam
== VK_RETURN
&& cfg
.fullscreenonaltenter
&&
3050 (cfg
.resize_action
!= RESIZE_DISABLED
)) {
3051 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) != KF_REPEAT
)
3055 /* Control-Numlock for app-keypad mode switch */
3056 if (wParam
== VK_PAUSE
&& shift_state
== 2) {
3057 app_keypad_keys
^= 1;
3061 /* Nethack keypad */
3062 if (cfg
.nethack_keypad
&& !left_alt
) {
3065 *p
++ = shift_state ?
'B' : 'b';
3068 *p
++ = shift_state ?
'J' : 'j';
3071 *p
++ = shift_state ?
'N' : 'n';
3074 *p
++ = shift_state ?
'H' : 'h';
3077 *p
++ = shift_state ?
'.' : '.';
3080 *p
++ = shift_state ?
'L' : 'l';
3083 *p
++ = shift_state ?
'Y' : 'y';
3086 *p
++ = shift_state ?
'K' : 'k';
3089 *p
++ = shift_state ?
'U' : 'u';
3094 /* Application Keypad */
3098 if (cfg
.funky_type
== 3 ||
3099 (cfg
.funky_type
<= 1 &&
3100 app_keypad_keys
&& !cfg
.no_applic_k
)) switch (wParam
) {
3114 if (app_keypad_keys
&& !cfg
.no_applic_k
)
3151 if (cfg
.funky_type
== 2) {
3156 } else if (shift_state
)
3163 if (cfg
.funky_type
== 2)
3167 if (cfg
.funky_type
== 2)
3171 if (cfg
.funky_type
== 2)
3176 if (HIWORD(lParam
) & KF_EXTENDED
)
3182 if (xkey
>= 'P' && xkey
<= 'S')
3183 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3185 p
+= sprintf((char *) p
, "\x1B?%c", xkey
);
3187 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3192 if (wParam
== VK_BACK
&& shift_state
== 0) { /* Backspace */
3193 *p
++ = (cfg
.bksp_is_delete ?
0x7F : 0x08);
3197 if (wParam
== VK_TAB
&& shift_state
== 1) { /* Shift tab */
3203 if (wParam
== VK_SPACE
&& shift_state
== 2) { /* Ctrl-Space */
3207 if (wParam
== VK_SPACE
&& shift_state
== 3) { /* Ctrl-Shift-Space */
3211 if (wParam
== VK_CANCEL
&& shift_state
== 2) { /* Ctrl-Break */
3216 if (wParam
== VK_PAUSE
) { /* Break/Pause */
3221 /* Control-2 to Control-8 are special */
3222 if (shift_state
== 2 && wParam
>= '2' && wParam
<= '8') {
3223 *p
++ = "\000\033\034\035\036\037\177"[wParam
- '2'];
3226 if (shift_state
== 2 && wParam
== 0xBD) {
3230 if (shift_state
== 2 && wParam
== 0xDF) {
3234 if (shift_state
== 0 && wParam
== VK_RETURN
&& cr_lf_return
) {
3241 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3242 * for integer decimal nn.)
3244 * We also deal with the weird ones here. Linux VCs replace F1
3245 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3246 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3252 code
= (keystate
[VK_SHIFT
] & 0x80 ?
23 : 11);
3255 code
= (keystate
[VK_SHIFT
] & 0x80 ?
24 : 12);
3258 code
= (keystate
[VK_SHIFT
] & 0x80 ?
25 : 13);
3261 code
= (keystate
[VK_SHIFT
] & 0x80 ?
26 : 14);
3264 code
= (keystate
[VK_SHIFT
] & 0x80 ?
28 : 15);
3267 code
= (keystate
[VK_SHIFT
] & 0x80 ?
29 : 17);
3270 code
= (keystate
[VK_SHIFT
] & 0x80 ?
31 : 18);
3273 code
= (keystate
[VK_SHIFT
] & 0x80 ?
32 : 19);
3276 code
= (keystate
[VK_SHIFT
] & 0x80 ?
33 : 20);
3279 code
= (keystate
[VK_SHIFT
] & 0x80 ?
34 : 21);
3312 if ((shift_state
&2) == 0) switch (wParam
) {
3332 /* Reorder edit keys to physical order */
3333 if (cfg
.funky_type
== 3 && code
<= 6)
3334 code
= "\0\2\1\4\5\3\6"[code
];
3336 if (vt52_mode
&& code
> 0 && code
<= 6) {
3337 p
+= sprintf((char *) p
, "\x1B%c", " HLMEIG"[code
]);
3341 if (cfg
.funky_type
== 5 && /* SCO function keys */
3342 code
>= 11 && code
<= 34) {
3343 char codes
[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3346 case VK_F1
: index
= 0; break;
3347 case VK_F2
: index
= 1; break;
3348 case VK_F3
: index
= 2; break;
3349 case VK_F4
: index
= 3; break;
3350 case VK_F5
: index
= 4; break;
3351 case VK_F6
: index
= 5; break;
3352 case VK_F7
: index
= 6; break;
3353 case VK_F8
: index
= 7; break;
3354 case VK_F9
: index
= 8; break;
3355 case VK_F10
: index
= 9; break;
3356 case VK_F11
: index
= 10; break;
3357 case VK_F12
: index
= 11; break;
3359 if (keystate
[VK_SHIFT
] & 0x80) index
+= 12;
3360 if (keystate
[VK_CONTROL
] & 0x80) index
+= 24;
3361 p
+= sprintf((char *) p
, "\x1B[%c", codes
[index
]);
3364 if (cfg
.funky_type
== 5 && /* SCO small keypad */
3365 code
>= 1 && code
<= 6) {
3366 char codes
[] = "HL.FIG";
3370 p
+= sprintf((char *) p
, "\x1B[%c", codes
[code
-1]);
3374 if ((vt52_mode
|| cfg
.funky_type
== 4) && code
>= 11 && code
<= 24) {
3381 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11 - offt
);
3384 sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11 - offt
);
3387 if (cfg
.funky_type
== 1 && code
>= 11 && code
<= 15) {
3388 p
+= sprintf((char *) p
, "\x1B[[%c", code
+ 'A' - 11);
3391 if (cfg
.funky_type
== 2 && code
>= 11 && code
<= 14) {
3393 p
+= sprintf((char *) p
, "\x1B%c", code
+ 'P' - 11);
3395 p
+= sprintf((char *) p
, "\x1BO%c", code
+ 'P' - 11);
3398 if (cfg
.rxvt_homeend
&& (code
== 1 || code
== 4)) {
3399 p
+= sprintf((char *) p
, code
== 1 ?
"\x1B[H" : "\x1BOw");
3403 p
+= sprintf((char *) p
, "\x1B[%d~", code
);
3408 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3409 * some reason seems to send VK_CLEAR to Windows...).
3432 p
+= sprintf((char *) p
, "\x1B%c", xkey
);
3434 int app_flg
= (app_cursor_keys
&& !cfg
.no_applic_c
);
3435 /* VT100 & VT102 manuals both state the app cursor keys
3436 * only work if the app keypad is on.
3438 if (!app_keypad_keys
)
3440 /* Useful mapping of Ctrl-arrows */
3441 if (shift_state
== 2)
3445 p
+= sprintf((char *) p
, "\x1BO%c", xkey
);
3447 p
+= sprintf((char *) p
, "\x1B[%c", xkey
);
3454 * Finally, deal with Return ourselves. (Win95 seems to
3455 * foul it up when Alt is pressed, for some reason.)
3457 if (wParam
== VK_RETURN
) { /* Return */
3463 if (left_alt
&& wParam
>= VK_NUMPAD0
&& wParam
<= VK_NUMPAD9
)
3464 alt_sum
= alt_sum
* 10 + wParam
- VK_NUMPAD0
;
3469 /* Okay we've done everything interesting; let windows deal with
3470 * the boring stuff */
3474 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3475 if(cfg
.xlat_capslockcyr
&& keystate
[VK_CAPITAL
] != 0) {
3477 keystate
[VK_CAPITAL
] = 0;
3480 r
= ToAsciiEx(wParam
, scan
, keystate
, keys
, 0, kbd_layout
);
3481 #ifdef SHOW_TOASCII_RESULT
3482 if (r
== 1 && !key_down
) {
3484 if (in_utf
|| dbcs_screenfont
)
3485 debug((", (U+%04x)", alt_sum
));
3487 debug((", LCH(%d)", alt_sum
));
3489 debug((", ACH(%d)", keys
[0]));
3494 for (r1
= 0; r1
< r
; r1
++) {
3495 debug(("%s%d", r1 ?
"," : "", keys
[r1
]));
3504 * Interrupt an ongoing paste. I'm not sure this is
3505 * sensible, but for the moment it's preferable to
3506 * having to faff about buffering things.
3511 for (i
= 0; i
< r
; i
++) {
3512 unsigned char ch
= (unsigned char) keys
[i
];
3514 if (compose_state
== 2 && (ch
& 0x80) == 0 && ch
> ' ') {
3519 if (compose_state
== 3 && (ch
& 0x80) == 0 && ch
> ' ') {
3523 if ((nc
= check_compose(compose_char
, ch
)) == -1) {
3524 MessageBeep(MB_ICONHAND
);
3528 luni_send(&keybuf
, 1, 1);
3536 if (in_utf
|| dbcs_screenfont
) {
3538 luni_send(&keybuf
, 1, 1);
3540 ch
= (char) alt_sum
;
3542 * We need not bother about stdin
3543 * backlogs here, because in GUI PuTTY
3544 * we can't do anything about it
3545 * anyway; there's no means of asking
3546 * Windows to hold off on KEYDOWN
3547 * messages. We _have_ to buffer
3548 * everything we're sent.
3550 ldisc_send(&ch
, 1, 1);
3554 lpage_send(kbd_codepage
, &ch
, 1, 1);
3556 if(capsOn
&& ch
< 0x80) {
3559 cbuf
[1] = xlat_uskbd2cyrllic(ch
);
3560 luni_send(cbuf
+!left_alt
, 1+!!left_alt
, 1);
3565 lpage_send(kbd_codepage
, cbuf
+!left_alt
, 1+!!left_alt
, 1);
3571 /* This is so the ALT-Numpad and dead keys work correctly. */
3576 /* If we're definitly not building up an ALT-54321 then clear it */
3579 /* If we will be using alt_sum fix the 256s */
3580 else if (keys
[0] && (in_utf
|| dbcs_screenfont
))
3585 * ALT alone may or may not want to bring up the System menu.
3586 * If it's not meant to, we return 0 on presses or releases of
3587 * ALT, to show that we've swallowed the keystroke. Otherwise
3588 * we return -1, which means Windows will give the keystroke
3589 * its default handling (i.e. bring up the System menu).
3591 if (wParam
== VK_MENU
&& !cfg
.alt_only
)
3597 void set_title(char *title
)
3600 window_name
= smalloc(1 + strlen(title
));
3601 strcpy(window_name
, title
);
3602 if (cfg
.win_name_always
|| !IsIconic(hwnd
))
3603 SetWindowText(hwnd
, title
);
3606 void set_icon(char *title
)
3609 icon_name
= smalloc(1 + strlen(title
));
3610 strcpy(icon_name
, title
);
3611 if (!cfg
.win_name_always
&& IsIconic(hwnd
))
3612 SetWindowText(hwnd
, title
);
3615 void set_sbar(int total
, int start
, int page
)
3619 if ((full_screen
&& !cfg
.scrollbar_in_fullscreen
) ||
3620 (!full_screen
&& !cfg
.scrollbar
))
3623 si
.cbSize
= sizeof(si
);
3624 si
.fMask
= SIF_ALL
| SIF_DISABLENOSCROLL
;
3626 si
.nMax
= total
- 1;
3630 SetScrollInfo(hwnd
, SB_VERT
, &si
, TRUE
);
3633 Context
get_ctx(void)
3639 SelectPalette(hdc
, pal
, FALSE
);
3645 void free_ctx(Context ctx
)
3647 SelectPalette(ctx
, GetStockObject(DEFAULT_PALETTE
), FALSE
);
3648 ReleaseDC(hwnd
, ctx
);
3651 static void real_palette_set(int n
, int r
, int g
, int b
)
3654 logpal
->palPalEntry
[n
].peRed
= r
;
3655 logpal
->palPalEntry
[n
].peGreen
= g
;
3656 logpal
->palPalEntry
[n
].peBlue
= b
;
3657 logpal
->palPalEntry
[n
].peFlags
= PC_NOCOLLAPSE
;
3658 colours
[n
] = PALETTERGB(r
, g
, b
);
3659 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3661 colours
[n
] = RGB(r
, g
, b
);
3664 void palette_set(int n
, int r
, int g
, int b
)
3666 static const int first
[21] = {
3667 0, 2, 4, 6, 8, 10, 12, 14,
3668 1, 3, 5, 7, 9, 11, 13, 15,
3671 real_palette_set(first
[n
], r
, g
, b
);
3673 real_palette_set(first
[n
] + 1, r
, g
, b
);
3675 HDC hdc
= get_ctx();
3676 UnrealizeObject(pal
);
3677 RealizePalette(hdc
);
3682 void palette_reset(void)
3686 for (i
= 0; i
< NCOLOURS
; i
++) {
3688 logpal
->palPalEntry
[i
].peRed
= defpal
[i
].rgbtRed
;
3689 logpal
->palPalEntry
[i
].peGreen
= defpal
[i
].rgbtGreen
;
3690 logpal
->palPalEntry
[i
].peBlue
= defpal
[i
].rgbtBlue
;
3691 logpal
->palPalEntry
[i
].peFlags
= 0;
3692 colours
[i
] = PALETTERGB(defpal
[i
].rgbtRed
,
3693 defpal
[i
].rgbtGreen
,
3694 defpal
[i
].rgbtBlue
);
3696 colours
[i
] = RGB(defpal
[i
].rgbtRed
,
3697 defpal
[i
].rgbtGreen
, defpal
[i
].rgbtBlue
);
3702 SetPaletteEntries(pal
, 0, NCOLOURS
, logpal
->palPalEntry
);
3704 RealizePalette(hdc
);
3709 void write_aclip(char *data
, int len
, int must_deselect
)
3714 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len
+ 1);
3717 lock
= GlobalLock(clipdata
);
3720 memcpy(lock
, data
, len
);
3721 ((unsigned char *) lock
)[len
] = 0;
3722 GlobalUnlock(clipdata
);
3725 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3727 if (OpenClipboard(hwnd
)) {
3729 SetClipboardData(CF_TEXT
, clipdata
);
3732 GlobalFree(clipdata
);
3735 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3739 * Note: unlike write_aclip() this will not append a nul.
3741 void write_clip(wchar_t * data
, int len
, int must_deselect
)
3743 HGLOBAL clipdata
, clipdata2
, clipdata3
;
3745 void *lock
, *lock2
, *lock3
;
3747 len2
= WideCharToMultiByte(CP_ACP
, 0, data
, len
, 0, 0, NULL
, NULL
);
3749 clipdata
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
,
3750 len
* sizeof(wchar_t));
3751 clipdata2
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, len2
);
3753 if (!clipdata
|| !clipdata2
|| !clipdata3
) {
3755 GlobalFree(clipdata
);
3757 GlobalFree(clipdata2
);
3759 GlobalFree(clipdata3
);
3762 if (!(lock
= GlobalLock(clipdata
)))
3764 if (!(lock2
= GlobalLock(clipdata2
)))
3767 memcpy(lock
, data
, len
* sizeof(wchar_t));
3768 WideCharToMultiByte(CP_ACP
, 0, data
, len
, lock2
, len2
, NULL
, NULL
);
3770 if (cfg
.rtf_paste
) {
3771 wchar_t unitab
[256];
3773 unsigned char *tdata
= (unsigned char *)lock2
;
3774 wchar_t *udata
= (wchar_t *)lock
;
3775 int rtflen
= 0, uindex
= 0, tindex
= 0;
3777 int multilen
, blen
, alen
, totallen
, i
;
3778 char before
[16], after
[4];
3780 get_unitab(CP_ACP
, unitab
, 0);
3782 rtfsize
= 100 + strlen(cfg
.font
);
3783 rtf
= smalloc(rtfsize
);
3784 sprintf(rtf
, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3785 GetACP(), cfg
.font
);
3786 rtflen
= strlen(rtf
);
3789 * We want to construct a piece of RTF that specifies the
3790 * same Unicode text. To do this we will read back in
3791 * parallel from the Unicode data in `udata' and the
3792 * non-Unicode data in `tdata'. For each character in
3793 * `tdata' which becomes the right thing in `udata' when
3794 * looked up in `unitab', we just copy straight over from
3795 * tdata. For each one that doesn't, we must WCToMB it
3796 * individually and produce a \u escape sequence.
3798 * It would probably be more robust to just bite the bullet
3799 * and WCToMB each individual Unicode character one by one,
3800 * then MBToWC each one back to see if it was an accurate
3801 * translation; but that strikes me as a horrifying number
3802 * of Windows API calls so I want to see if this faster way
3803 * will work. If it screws up badly we can always revert to
3804 * the simple and slow way.
3806 while (tindex
< len2
&& uindex
< len
&&
3807 tdata
[tindex
] && udata
[uindex
]) {
3808 if (tindex
+ 1 < len2
&&
3809 tdata
[tindex
] == '\r' &&
3810 tdata
[tindex
+1] == '\n') {
3814 if (unitab
[tdata
[tindex
]] == udata
[uindex
]) {
3820 multilen
= WideCharToMultiByte(CP_ACP
, 0, unitab
+uindex
, 1,
3821 NULL
, 0, NULL
, NULL
);
3822 if (multilen
!= 1) {
3823 blen
= sprintf(before
, "{\\uc%d\\u%d", multilen
,
3825 alen
= 1; strcpy(after
, "}");
3827 blen
= sprintf(before
, "\\u%d", udata
[uindex
]);
3828 alen
= 0; after
[0] = '\0';
3831 assert(tindex
+ multilen
<= len2
);
3832 totallen
= blen
+ alen
;
3833 for (i
= 0; i
< multilen
; i
++) {
3834 if (tdata
[tindex
+i
] == '\\' ||
3835 tdata
[tindex
+i
] == '{' ||
3836 tdata
[tindex
+i
] == '}')
3838 else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A)
3839 totallen
+= 6; /* \par\r\n */
3840 else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20)
3846 if (rtfsize
< rtflen
+ totallen
+ 3) {
3847 rtfsize
= rtflen
+ totallen
+ 512;
3848 rtf
= srealloc(rtf
, rtfsize
);
3851 strcpy(rtf
+ rtflen
, before
); rtflen
+= blen
;
3852 for (i
= 0; i
< multilen
; i
++) {
3853 if (tdata
[tindex
+i
] == '\\' ||
3854 tdata
[tindex
+i
] == '{' ||
3855 tdata
[tindex
+i
] == '}') {
3856 rtf
[rtflen
++] = '\\';
3857 rtf
[rtflen
++] = tdata
[tindex
+i
];
3858 } else if (tdata
[tindex
+i
] == 0x0D || tdata
[tindex
+i
] == 0x0A) {
3859 rtflen
+= sprintf(rtf
+rtflen
, "\\par\r\n");
3860 } else if (tdata
[tindex
+i
] > 0x7E || tdata
[tindex
+i
] < 0x20) {
3861 rtflen
+= sprintf(rtf
+rtflen
, "\\'%02x", tdata
[tindex
+i
]);
3863 rtf
[rtflen
++] = tdata
[tindex
+i
];
3866 strcpy(rtf
+ rtflen
, after
); rtflen
+= alen
;
3872 strcpy(rtf
+ rtflen
, "}");
3875 clipdata3
= GlobalAlloc(GMEM_DDESHARE
| GMEM_MOVEABLE
, rtflen
);
3876 if (clipdata3
&& (lock3
= GlobalLock(clipdata3
)) != NULL
) {
3878 GlobalUnlock(clipdata3
);
3884 GlobalUnlock(clipdata
);
3885 GlobalUnlock(clipdata2
);
3888 SendMessage(hwnd
, WM_IGNORE_CLIP
, TRUE
, 0);
3890 if (OpenClipboard(hwnd
)) {
3892 SetClipboardData(CF_UNICODETEXT
, clipdata
);
3893 SetClipboardData(CF_TEXT
, clipdata2
);
3895 SetClipboardData(RegisterClipboardFormat(CF_RTF
), clipdata3
);
3898 GlobalFree(clipdata
);
3899 GlobalFree(clipdata2
);
3903 SendMessage(hwnd
, WM_IGNORE_CLIP
, FALSE
, 0);
3906 void get_clip(wchar_t ** p
, int *len
)
3908 static HGLOBAL clipdata
= NULL
;
3909 static wchar_t *converted
= 0;
3918 GlobalUnlock(clipdata
);
3921 } else if (OpenClipboard(NULL
)) {
3922 if ((clipdata
= GetClipboardData(CF_UNICODETEXT
))) {
3924 *p
= GlobalLock(clipdata
);
3926 for (p2
= *p
; *p2
; p2
++);
3930 } else if ( (clipdata
= GetClipboardData(CF_TEXT
)) ) {
3934 s
= GlobalLock(clipdata
);
3935 i
= MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, 0, 0);
3936 *p
= converted
= smalloc(i
* sizeof(wchar_t));
3937 MultiByteToWideChar(CP_ACP
, 0, s
, strlen(s
) + 1, converted
, i
);
3950 * Move `lines' lines from position `from' to position `to' in the
3953 void optimised_move(int to
, int from
, int lines
)
3958 min
= (to
< from ? to
: from
);
3959 max
= to
+ from
- min
;
3961 r
.left
= offset_width
;
3962 r
.right
= offset_width
+ cols
* font_width
;
3963 r
.top
= offset_height
+ min
* font_height
;
3964 r
.bottom
= offset_height
+ (max
+ lines
) * font_height
;
3965 ScrollWindow(hwnd
, 0, (to
- from
) * font_height
, &r
, &r
);
3970 * Print a message box and perform a fatal exit.
3972 void fatalbox(char *fmt
, ...)
3978 vsprintf(stuff
, fmt
, ap
);
3980 MessageBox(hwnd
, stuff
, "PuTTY Fatal Error", MB_ICONERROR
| MB_OK
);
3985 * Manage window caption / taskbar flashing, if enabled.
3986 * 0 = stop, 1 = maintain, 2 = start
3988 static void flash_window(int mode
)
3990 static long last_flash
= 0;
3991 static int flashing
= 0;
3992 if ((mode
== 0) || (cfg
.beep_ind
== B_IND_DISABLED
)) {
3995 FlashWindow(hwnd
, FALSE
);
3999 } else if (mode
== 2) {
4002 last_flash
= GetTickCount();
4004 FlashWindow(hwnd
, TRUE
);
4007 } else if ((mode
== 1) && (cfg
.beep_ind
== B_IND_FLASH
)) {
4010 long now
= GetTickCount();
4011 long fdiff
= now
- last_flash
;
4012 if (fdiff
< 0 || fdiff
> 450) {
4014 FlashWindow(hwnd
, TRUE
); /* toggle */
4025 if (mode
== BELL_DEFAULT
) {
4027 * For MessageBeep style bells, we want to be careful of
4028 * timing, because they don't have the nice property of
4029 * PlaySound bells that each one cancels the previous
4030 * active one. So we limit the rate to one per 50ms or so.
4032 static long lastbeep
= 0;
4035 beepdiff
= GetTickCount() - lastbeep
;
4036 if (beepdiff
>= 0 && beepdiff
< 50)
4040 * The above MessageBeep call takes time, so we record the
4041 * time _after_ it finishes rather than before it starts.
4043 lastbeep
= GetTickCount();
4044 } else if (mode
== BELL_WAVEFILE
) {
4045 if (!PlaySound(cfg
.bell_wavefile
, NULL
, SND_ASYNC
| SND_FILENAME
)) {
4046 char buf
[sizeof(cfg
.bell_wavefile
) + 80];
4047 sprintf(buf
, "Unable to play sound file\n%s\n"
4048 "Using default sound instead", cfg
.bell_wavefile
);
4049 MessageBox(hwnd
, buf
, "PuTTY Sound Error",
4050 MB_OK
| MB_ICONEXCLAMATION
);
4051 cfg
.beep
= BELL_DEFAULT
;
4054 /* Otherwise, either visual bell or disabled; do nothing here */
4056 flash_window(2); /* start */
4061 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
4063 * Revised by <wez@thebrainroom.com>
4065 static void flip_full_screen(void)
4070 wp
.length
= sizeof(wp
);
4071 GetWindowPlacement(hwnd
, &wp
);
4073 full_screen
= !full_screen
;
4076 if (wp
.showCmd
== SW_SHOWMAXIMIZED
) {
4077 /* Ooops it was already 'zoomed' we have to unzoom it before
4078 * everything will work right.
4080 wp
.showCmd
= SW_SHOWNORMAL
;
4081 SetWindowPlacement(hwnd
, &wp
);
4084 style
= GetWindowLong(hwnd
, GWL_STYLE
) & ~(WS_CAPTION
|WS_THICKFRAME
);
4085 style
&= ~WS_VSCROLL
;
4086 if (cfg
.scrollbar_in_fullscreen
)
4087 style
|= WS_VSCROLL
;
4088 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4090 /* Some versions of explorer get confused and don't take
4091 * notice of us going fullscreen, so go topmost too.
4093 SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0,
4094 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
4095 SWP_NOMOVE
| SWP_NOSIZE
|
4098 wp
.showCmd
= SW_SHOWMAXIMIZED
;
4099 SetWindowPlacement(hwnd
, &wp
);
4101 style
= GetWindowLong(hwnd
, GWL_STYLE
) | WS_CAPTION
;
4102 if (cfg
.resize_action
!= RESIZE_DISABLED
)
4103 style
|= WS_THICKFRAME
;
4104 style
&= ~WS_VSCROLL
;
4106 style
|= WS_VSCROLL
;
4107 SetWindowLong(hwnd
, GWL_STYLE
, style
);
4109 SetWindowPos(hwnd
, HWND_NOTOPMOST
, 0, 0, 0, 0,
4110 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
4111 SWP_NOMOVE
| SWP_NOSIZE
|
4114 wp
.showCmd
= SW_SHOWNORMAL
;
4115 SetWindowPlacement(hwnd
, &wp
);
4118 CheckMenuItem(GetSystemMenu(hwnd
, FALSE
), IDM_FULLSCREEN
,
4119 MF_BYCOMMAND
| full_screen ? MF_CHECKED
: MF_UNCHECKED
);
4123 * Minimise or restore the window in response to a server-side
4126 void set_iconic(int iconic
)
4128 if (IsIconic(hwnd
)) {
4130 ShowWindow(hwnd
, SW_RESTORE
);
4133 ShowWindow(hwnd
, SW_MINIMIZE
);
4138 * Move the window in response to a server-side request.
4140 void move_window(int x
, int y
)
4142 SetWindowPos(hwnd
, NULL
, x
, y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
);
4146 * Move the window to the top or bottom of the z-order in response
4147 * to a server-side request.
4149 void set_zorder(int top
)
4151 if (cfg
.alwaysontop
|| full_screen
)
4152 return; /* ignore */
4153 SetWindowPos(hwnd
, top ? HWND_TOP
: HWND_BOTTOM
, 0, 0, 0, 0,
4154 SWP_NOMOVE
| SWP_NOSIZE
);
4158 * Refresh the window in response to a server-side request.
4160 void refresh_window(void)
4162 InvalidateRect(hwnd
, NULL
, TRUE
);
4166 * Maximise or restore the window in response to a server-side
4169 void set_zoomed(int zoomed
)
4171 if (IsZoomed(hwnd
) || full_screen
) {
4176 ShowWindow(hwnd
, SW_RESTORE
);
4180 ShowWindow(hwnd
, SW_MAXIMIZE
);
4185 * Report whether the window is iconic, for terminal reports.
4189 return IsIconic(hwnd
);
4193 * Report the window's position, for terminal reports.
4195 void get_window_pos(int *x
, int *y
)
4198 GetWindowRect(hwnd
, &r
);
4204 * Report the window's pixel size, for terminal reports.
4206 void get_window_pixels(int *x
, int *y
)
4209 GetWindowRect(hwnd
, &r
);
4210 *x
= r
.right
- r
.left
;
4211 *y
= r
.bottom
- r
.top
;
4215 * Return the window or icon title.
4217 char *get_window_title(int icon
)
4219 return icon ? icon_name
: window_name
;