Prevent network errors from summarily closing the window when CoE is off
[u/mdw/putty] / window.c
... / ...
Content-type: text/html git.distorted.org.uk Git - u/mdw/putty/blame_incremental - window.c


500 - Internal Server Error

Malformed UTF-8 character (fatal) at (eval 5) line 1, <$fd> line 1609.
CommitLineData
1#include <windows.h>
2#include <commctrl.h>
3#include <winsock.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <ctype.h>
7
8#define PUTTY_DO_GLOBALS /* actually _define_ globals */
9#include "putty.h"
10#include "win_res.h"
11
12#define IDM_SHOWLOG 0x0010
13#define IDM_NEWSESS 0x0020
14#define IDM_DUPSESS 0x0030
15#define IDM_RECONF 0x0040
16#define IDM_CLRSB 0x0050
17#define IDM_RESET 0x0060
18#define IDM_TEL_AYT 0x0070
19#define IDM_TEL_BRK 0x0080
20#define IDM_TEL_SYNCH 0x0090
21#define IDM_TEL_EC 0x00a0
22#define IDM_TEL_EL 0x00b0
23#define IDM_TEL_GA 0x00c0
24#define IDM_TEL_NOP 0x00d0
25#define IDM_TEL_ABORT 0x00e0
26#define IDM_TEL_AO 0x00f0
27#define IDM_TEL_IP 0x0100
28#define IDM_TEL_SUSP 0x0110
29#define IDM_TEL_EOR 0x0120
30#define IDM_TEL_EOF 0x0130
31#define IDM_ABOUT 0x0140
32#define IDM_SAVEDSESS 0x0150
33
34#define IDM_SAVED_MIN 0x1000
35#define IDM_SAVED_MAX 0x2000
36
37#define WM_IGNORE_SIZE (WM_XUSER + 1)
38#define WM_IGNORE_CLIP (WM_XUSER + 2)
39#define WM_IGNORE_KEYMENU (WM_XUSER + 3)
40
41static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
42static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
43static void cfgtopalette(void);
44static void init_palette(void);
45static void init_fonts(int);
46
47static int extra_width, extra_height;
48
49static int pending_netevent = 0;
50static WPARAM pend_netevent_wParam = 0;
51static LPARAM pend_netevent_lParam = 0;
52static void enact_pending_netevent(void);
53
54#define FONT_NORMAL 0
55#define FONT_BOLD 1
56#define FONT_UNDERLINE 2
57#define FONT_BOLDUND 3
58#define FONT_OEM 4
59#define FONT_OEMBOLD 5
60#define FONT_OEMBOLDUND 6
61#define FONT_OEMUND 7
62static HFONT fonts[8];
63static int font_needs_hand_underlining;
64static enum {
65 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
66} bold_mode;
67static enum {
68 UND_LINE, UND_FONT
69} und_mode;
70static int descent;
71
72#define NCOLOURS 24
73static COLORREF colours[NCOLOURS];
74static HPALETTE pal;
75static LPLOGPALETTE logpal;
76static RGBTRIPLE defpal[NCOLOURS];
77
78static HWND hwnd;
79
80static int dbltime, lasttime, lastact;
81static Mouse_Button lastbtn;
82
83static char *window_name, *icon_name;
84
85int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
86 static char appname[] = "PuTTY";
87 WORD winsock_ver;
88 WSADATA wsadata;
89 WNDCLASS wndclass;
90 MSG msg;
91 int guess_width, guess_height;
92
93 putty_inst = inst;
94 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
95
96 winsock_ver = MAKEWORD(1, 1);
97 if (WSAStartup(winsock_ver, &wsadata)) {
98 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
99 MB_OK | MB_ICONEXCLAMATION);
100 return 1;
101 }
102 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
103 MessageBox(NULL, "WinSock version is incompatible with 1.1",
104 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
105 WSACleanup();
106 return 1;
107 }
108 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
109
110 InitCommonControls();
111
112 /*
113 * Process the command line.
114 */
115 {
116 char *p;
117
118 default_protocol = DEFAULT_PROTOCOL;
119 default_port = DEFAULT_PORT;
120
121 do_defaults(NULL);
122
123 p = cmdline;
124 while (*p && isspace(*p)) p++;
125
126 /*
127 * Process command line options first. Yes, this can be
128 * done better, and it will be as soon as I have the
129 * energy...
130 */
131 while (*p == '-') {
132 char *q = p + strcspn(p, " \t");
133 p++;
134 if (q == p + 3 &&
135 tolower(p[0]) == 's' &&
136 tolower(p[1]) == 's' &&
137 tolower(p[2]) == 'h') {
138 default_protocol = cfg.protocol = PROT_SSH;
139 default_port = cfg.port = 22;
140 } else if (q == p + 3 &&
141 tolower(p[0]) == 'l' &&
142 tolower(p[1]) == 'o' &&
143 tolower(p[2]) == 'g') {
144 logfile = "putty.log";
145 }
146 p = q + strspn(q, " \t");
147 }
148
149 /*
150 * An initial @ means to activate a saved session.
151 */
152 if (*p == '@') {
153 do_defaults (p+1);
154 if (!*cfg.host && !do_config()) {
155 WSACleanup();
156 return 0;
157 }
158 } else if (*p == '&') {
159 /*
160 * An initial & means we've been given a command line
161 * containing the hex value of a HANDLE for a file
162 * mapping object, which we must then extract as a
163 * config.
164 */
165 HANDLE filemap;
166 Config *cp;
167 if (sscanf(p+1, "%p", &filemap) == 1 &&
168 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
169 0, 0, sizeof(Config))) != NULL) {
170 cfg = *cp;
171 UnmapViewOfFile(cp);
172 CloseHandle(filemap);
173 } else if (!do_config()) {
174 WSACleanup();
175 return 0;
176 }
177 } else if (*p) {
178 char *q = p;
179 /*
180 * If the hostname starts with "telnet:", set the
181 * protocol to Telnet and process the string as a
182 * Telnet URL.
183 */
184 if (!strncmp(q, "telnet:", 7)) {
185 char c;
186
187 q += 7;
188 if (q[0] == '/' && q[1] == '/')
189 q += 2;
190 cfg.protocol = PROT_TELNET;
191 p = q;
192 while (*p && *p != ':' && *p != '/') p++;
193 c = *p;
194 if (*p)
195 *p++ = '\0';
196 if (c == ':')
197 cfg.port = atoi(p);
198 else
199 cfg.port = -1;
200 strncpy (cfg.host, q, sizeof(cfg.host)-1);
201 cfg.host[sizeof(cfg.host)-1] = '\0';
202 } else {
203 while (*p && !isspace(*p)) p++;
204 if (*p)
205 *p++ = '\0';
206 strncpy (cfg.host, q, sizeof(cfg.host)-1);
207 cfg.host[sizeof(cfg.host)-1] = '\0';
208 while (*p && isspace(*p)) p++;
209 if (*p)
210 cfg.port = atoi(p);
211 else
212 cfg.port = -1;
213 }
214 } else {
215 if (!do_config()) {
216 WSACleanup();
217 return 0;
218 }
219 }
220 }
221
222 /*
223 * Select protocol. This is farmed out into a table in a
224 * separate file to enable an ssh-free variant.
225 */
226 {
227 int i;
228 back = NULL;
229 for (i = 0; backends[i].backend != NULL; i++)
230 if (backends[i].protocol == cfg.protocol) {
231 back = backends[i].backend;
232 break;
233 }
234 if (back == NULL) {
235 MessageBox(NULL, "Unsupported protocol number found",
236 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
237 WSACleanup();
238 return 1;
239 }
240 }
241
242 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
243
244 if (!prev) {
245 wndclass.style = 0;
246 wndclass.lpfnWndProc = WndProc;
247 wndclass.cbClsExtra = 0;
248 wndclass.cbWndExtra = 0;
249 wndclass.hInstance = inst;
250 wndclass.hIcon = LoadIcon (inst,
251 MAKEINTRESOURCE(IDI_MAINICON));
252 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
253 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
254 wndclass.lpszMenuName = NULL;
255 wndclass.lpszClassName = appname;
256
257 RegisterClass (&wndclass);
258 }
259
260 hwnd = NULL;
261
262 savelines = cfg.savelines;
263 term_init();
264
265 cfgtopalette();
266
267 /*
268 * Guess some defaults for the window size. This all gets
269 * updated later, so we don't really care too much. However, we
270 * do want the font width/height guesses to correspond to a
271 * large font rather than a small one...
272 */
273
274 font_width = 10;
275 font_height = 20;
276 extra_width = 25;
277 extra_height = 28;
278 term_size (cfg.height, cfg.width, cfg.savelines);
279 guess_width = extra_width + font_width * cols;
280 guess_height = extra_height + font_height * rows;
281 {
282 RECT r;
283 HWND w = GetDesktopWindow();
284 GetWindowRect (w, &r);
285 if (guess_width > r.right - r.left)
286 guess_width = r.right - r.left;
287 if (guess_height > r.bottom - r.top)
288 guess_height = r.bottom - r.top;
289 }
290
291 {
292 int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
293 if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
294 if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
295 hwnd = CreateWindow (appname, appname,
296 winmode,
297 CW_USEDEFAULT, CW_USEDEFAULT,
298 guess_width, guess_height,
299 NULL, NULL, inst, NULL);
300 }
301
302 /*
303 * Initialise the fonts, simultaneously correcting the guesses
304 * for font_{width,height}.
305 */
306 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
307 und_mode = UND_FONT;
308 init_fonts(0);
309
310 /*
311 * Correct the guesses for extra_{width,height}.
312 */
313 {
314 RECT cr, wr;
315 GetWindowRect (hwnd, &wr);
316 GetClientRect (hwnd, &cr);
317 extra_width = wr.right - wr.left - cr.right + cr.left;
318 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
319 }
320
321 /*
322 * Resize the window, now we know what size we _really_ want it
323 * to be.
324 */
325 guess_width = extra_width + font_width * cols;
326 guess_height = extra_height + font_height * rows;
327 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
328 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
329 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
330
331 /*
332 * Initialise the scroll bar.
333 */
334 {
335 SCROLLINFO si;
336
337 si.cbSize = sizeof(si);
338 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
339 si.nMin = 0;
340 si.nMax = rows-1;
341 si.nPage = rows;
342 si.nPos = 0;
343 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
344 }
345
346 /*
347 * Start up the telnet connection.
348 */
349 {
350 char *error;
351 char msg[1024];
352 char *realhost;
353
354 error = back->init (hwnd, cfg.host, cfg.port, &realhost);
355 if (error) {
356 sprintf(msg, "Unable to open connection:\n%s", error);
357 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
358 return 0;
359 }
360 window_name = icon_name = NULL;
361 sprintf(msg, "%s - PuTTY", realhost);
362 set_title (msg);
363 set_icon (msg);
364 }
365
366 session_closed = FALSE;
367
368 /*
369 * Set up the input and output buffers.
370 */
371 inbuf_head = 0;
372 outbuf_reap = outbuf_head = 0;
373
374 /*
375 * Prepare the mouse handler.
376 */
377 lastact = MA_NOTHING;
378 lastbtn = MB_NOTHING;
379 dbltime = GetDoubleClickTime();
380
381 /*
382 * Set up the session-control options on the system menu.
383 */
384 {
385 HMENU m = GetSystemMenu (hwnd, FALSE);
386 HMENU p,s;
387 int i;
388
389 AppendMenu (m, MF_SEPARATOR, 0, 0);
390 if (cfg.protocol == PROT_TELNET) {
391 p = CreateMenu();
392 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
393 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
394 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
395 AppendMenu (p, MF_SEPARATOR, 0, 0);
396 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
397 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
398 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
399 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
400 AppendMenu (p, MF_SEPARATOR, 0, 0);
401 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
402 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
403 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
404 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
405 AppendMenu (p, MF_SEPARATOR, 0, 0);
406 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
407 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
408 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
409 AppendMenu (m, MF_SEPARATOR, 0, 0);
410 }
411 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
412 AppendMenu (m, MF_SEPARATOR, 0, 0);
413 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
414 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
415 s = CreateMenu();
416 get_sesslist(TRUE);
417 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
418 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
419 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
420 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
421 AppendMenu (m, MF_SEPARATOR, 0, 0);
422 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
423 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
424 AppendMenu (m, MF_SEPARATOR, 0, 0);
425 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
426 }
427
428 /*
429 * Finally show the window!
430 */
431 ShowWindow (hwnd, show);
432
433 /*
434 * Set the palette up.
435 */
436 pal = NULL;
437 logpal = NULL;
438 init_palette();
439
440 has_focus = (GetForegroundWindow() == hwnd);
441 UpdateWindow (hwnd);
442
443 {
444 int timer_id = 0, long_timer = 0;
445
446 while (GetMessage (&msg, NULL, 0, 0) == 1) {
447 /* Sometimes DispatchMessage calls routines that use their own
448 * GetMessage loop, setup this timer so we get some control back.
449 *
450 * Also call term_update() from the timer so that if the host
451 * is sending data flat out we still do redraws.
452 */
453 if(timer_id && long_timer) {
454 KillTimer(hwnd, timer_id);
455 long_timer = timer_id = 0;
456 }
457 if(!timer_id)
458 timer_id = SetTimer(hwnd, 1, 20, NULL);
459 DispatchMessage (&msg);
460
461 /* This is too fast, but I'll leave it for now 'cause it shows
462 * how often term_update is called (far too often at times!)
463 */
464 term_blink(0);
465
466 /* Send the paste buffer if there's anything to send */
467 term_paste();
468
469 /* If there's nothing new in the queue then we can do everything
470 * we've delayed, reading the socket, writing, and repainting
471 * the window.
472 */
473 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
474 if (pending_netevent) {
475 enact_pending_netevent();
476
477 term_blink(1);
478 }
479 } else continue;
480 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
481 if (timer_id) {
482 KillTimer(hwnd, timer_id);
483 timer_id = 0;
484 }
485 if (inbuf_head)
486 term_out();
487 term_update();
488 if (!has_focus)
489 timer_id = SetTimer(hwnd, 1, 2000, NULL);
490 else if (cfg.blinktext)
491 timer_id = SetTimer(hwnd, 1, 250, NULL);
492 else
493 timer_id = SetTimer(hwnd, 1, 500, NULL);
494 long_timer = 1;
495 }
496 }
497 }
498
499 /*
500 * Clean up.
501 */
502 {
503 int i;
504 for (i=0; i<8; i++)
505 if (fonts[i])
506 DeleteObject(fonts[i]);
507 }
508 sfree(logpal);
509 if (pal)
510 DeleteObject(pal);
511 WSACleanup();
512
513 if (cfg.protocol == PROT_SSH) {
514 random_save_seed();
515#ifdef MSCRYPTOAPI
516 crypto_wrapup();
517#endif
518 }
519
520 return msg.wParam;
521}
522
523/*
524 * Print a message box and close the connection.
525 */
526void connection_fatal(char *fmt, ...) {
527 va_list ap;
528 char stuff[200];
529
530 va_start(ap, fmt);
531 vsprintf(stuff, fmt, ap);
532 va_end(ap);
533 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
534 if (cfg.close_on_exit)
535 PostQuitMessage(1);
536 else {
537 session_closed = TRUE;
538 SetWindowText (hwnd, "PuTTY (inactive)");
539 }
540}
541
542/*
543 * Actually do the job requested by a WM_NETEVENT
544 */
545static void enact_pending_netevent(void) {
546 int i;
547 static int reentering = 0;
548
549 if (reentering)
550 return; /* don't unpend the pending */
551
552 pending_netevent = FALSE;
553
554 reentering = 1;
555 i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
556 reentering = 0;
557
558 if (i < 0) {
559 char buf[1024];
560 switch (WSABASEERR + (-i) % 10000) {
561 case WSAECONNRESET:
562 sprintf(buf, "Connection reset by peer");
563 break;
564 default:
565 sprintf(buf, "Unexpected network error %d", -i);
566 break;
567 }
568 connection_fatal(buf);
569 }
570 if (i <= 0) {
571 if (cfg.close_on_exit)
572 PostQuitMessage(0);
573 else {
574 session_closed = TRUE;
575 MessageBox(hwnd, "Connection closed by remote host",
576 "PuTTY", MB_OK | MB_ICONINFORMATION);
577 SetWindowText (hwnd, "PuTTY (inactive)");
578 }
579 }
580}
581
582/*
583 * Copy the colour palette from the configuration data into defpal.
584 * This is non-trivial because the colour indices are different.
585 */
586static void cfgtopalette(void) {
587 int i;
588 static const int ww[] = {
589 6, 7, 8, 9, 10, 11, 12, 13,
590 14, 15, 16, 17, 18, 19, 20, 21,
591 0, 1, 2, 3, 4, 4, 5, 5
592 };
593
594 for (i=0; i<24; i++) {
595 int w = ww[i];
596 defpal[i].rgbtRed = cfg.colours[w][0];
597 defpal[i].rgbtGreen = cfg.colours[w][1];
598 defpal[i].rgbtBlue = cfg.colours[w][2];
599 }
600}
601
602/*
603 * Set up the colour palette.
604 */
605static void init_palette(void) {
606 int i;
607 HDC hdc = GetDC (hwnd);
608 if (hdc) {
609 if (cfg.try_palette &&
610 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
611 logpal = smalloc(sizeof(*logpal)
612 - sizeof(logpal->palPalEntry)
613 + NCOLOURS * sizeof(PALETTEENTRY));
614 logpal->palVersion = 0x300;
615 logpal->palNumEntries = NCOLOURS;
616 for (i = 0; i < NCOLOURS; i++) {
617 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
618 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
619 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
620 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
621 }
622 pal = CreatePalette (logpal);
623 if (pal) {
624 SelectPalette (hdc, pal, FALSE);
625 RealizePalette (hdc);
626 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
627 FALSE);
628 }
629 }
630 ReleaseDC (hwnd, hdc);
631 }
632 if (pal)
633 for (i=0; i<NCOLOURS; i++)
634 colours[i] = PALETTERGB(defpal[i].rgbtRed,
635 defpal[i].rgbtGreen,
636 defpal[i].rgbtBlue);
637 else
638 for(i=0; i<NCOLOURS; i++)
639 colours[i] = RGB(defpal[i].rgbtRed,
640 defpal[i].rgbtGreen,
641 defpal[i].rgbtBlue);
642}
643
644/*
645 * Initialise all the fonts we will need. There may be as many as
646 * eight or as few as one. We also:
647 *
648 * - check the font width and height, correcting our guesses if
649 * necessary.
650 *
651 * - verify that the bold font is the same width as the ordinary
652 * one, and engage shadow bolding if not.
653 *
654 * - verify that the underlined font is the same width as the
655 * ordinary one (manual underlining by means of line drawing can
656 * be done in a pinch).
657 */
658static void init_fonts(int pick_width) {
659 TEXTMETRIC tm;
660 int i;
661 int fsize[8];
662 HDC hdc;
663 int fw_dontcare, fw_bold;
664 int firstchar = ' ';
665
666#ifdef CHECKOEMFONT
667font_messup:
668#endif
669 for (i=0; i<8; i++)
670 fonts[i] = NULL;
671
672 if (cfg.fontisbold) {
673 fw_dontcare = FW_BOLD;
674 fw_bold = FW_BLACK;
675 } else {
676 fw_dontcare = FW_DONTCARE;
677 fw_bold = FW_BOLD;
678 }
679
680 hdc = GetDC(hwnd);
681
682 font_height = cfg.fontheight;
683 font_width = pick_width;
684
685#define f(i,c,w,u) \
686 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
687 c, OUT_DEFAULT_PRECIS, \
688 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
689 FIXED_PITCH | FF_DONTCARE, cfg.font)
690
691 if (cfg.vtmode != VT_OEMONLY) {
692 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
693
694 SelectObject (hdc, fonts[FONT_NORMAL]);
695 GetTextMetrics(hdc, &tm);
696 font_height = tm.tmHeight;
697 font_width = tm.tmAveCharWidth;
698
699 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
700
701 /*
702 * Some fonts, e.g. 9-pt Courier, draw their underlines
703 * outside their character cell. We successfully prevent
704 * screen corruption by clipping the text output, but then
705 * we lose the underline completely. Here we try to work
706 * out whether this is such a font, and if it is, we set a
707 * flag that causes underlines to be drawn by hand.
708 *
709 * Having tried other more sophisticated approaches (such
710 * as examining the TEXTMETRIC structure or requesting the
711 * height of a string), I think we'll do this the brute
712 * force way: we create a small bitmap, draw an underlined
713 * space on it, and test to see whether any pixels are
714 * foreground-coloured. (Since we expect the underline to
715 * go all the way across the character cell, we only search
716 * down a single column of the bitmap, half way across.)
717 */
718 {
719 HDC und_dc;
720 HBITMAP und_bm, und_oldbm;
721 int i, gotit;
722 COLORREF c;
723
724 und_dc = CreateCompatibleDC(hdc);
725 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
726 und_oldbm = SelectObject(und_dc, und_bm);
727 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
728 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
729 SetTextColor (und_dc, RGB(255,255,255));
730 SetBkColor (und_dc, RGB(0,0,0));
731 SetBkMode (und_dc, OPAQUE);
732 ExtTextOut (und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
733 gotit = FALSE;
734 for (i = 0; i < font_height; i++) {
735 c = GetPixel(und_dc, font_width/2, i);
736 if (c != RGB(0,0,0))
737 gotit = TRUE;
738 }
739 SelectObject(und_dc, und_oldbm);
740 DeleteObject(und_bm);
741 DeleteDC(und_dc);
742 font_needs_hand_underlining = !gotit;
743 }
744
745 if (bold_mode == BOLD_FONT) {
746 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
747 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
748 }
749
750 if (cfg.vtmode == VT_OEMANSI) {
751 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
752 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
753
754 if (bold_mode == BOLD_FONT) {
755 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
756 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
757 }
758 }
759 }
760 else
761 {
762 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
763
764 SelectObject (hdc, fonts[FONT_OEM]);
765 GetTextMetrics(hdc, &tm);
766 font_height = tm.tmHeight;
767 font_width = tm.tmAveCharWidth;
768
769 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
770
771 if (bold_mode == BOLD_FONT) {
772 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
773 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
774 }
775 }
776#undef f
777
778 descent = tm.tmAscent + 1;
779 if (descent >= font_height)
780 descent = font_height - 1;
781 firstchar = tm.tmFirstChar;
782
783 for (i=0; i<8; i++) {
784 if (fonts[i]) {
785 if (SelectObject (hdc, fonts[i]) &&
786 GetTextMetrics(hdc, &tm) )
787 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
788 else fsize[i] = -i;
789 }
790 else fsize[i] = -i;
791 }
792
793 ReleaseDC (hwnd, hdc);
794
795 /* ... This is wrong in OEM only mode */
796 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
797 (bold_mode == BOLD_FONT &&
798 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
799 und_mode = UND_LINE;
800 DeleteObject (fonts[FONT_UNDERLINE]);
801 if (bold_mode == BOLD_FONT)
802 DeleteObject (fonts[FONT_BOLDUND]);
803 }
804
805 if (bold_mode == BOLD_FONT &&
806 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
807 bold_mode = BOLD_SHADOW;
808 DeleteObject (fonts[FONT_BOLD]);
809 if (und_mode == UND_FONT)
810 DeleteObject (fonts[FONT_BOLDUND]);
811 }
812
813#ifdef CHECKOEMFONT
814 /* With the fascist font painting it doesn't matter if the linedraw font
815 * isn't exactly the right size anymore so we don't have to check this.
816 */
817 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
818 if( cfg.fontcharset == OEM_CHARSET )
819 {
820 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
821 "different sizes. Using OEM-only mode instead",
822 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
823 cfg.vtmode = VT_OEMONLY;
824 }
825 else if( firstchar < ' ' )
826 {
827 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
828 "different sizes. Using XTerm mode instead",
829 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
830 cfg.vtmode = VT_XWINDOWS;
831 }
832 else
833 {
834 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
835 "different sizes. Using ISO8859-1 mode instead",
836 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
837 cfg.vtmode = VT_POORMAN;
838 }
839
840 for (i=0; i<8; i++)
841 if (fonts[i])
842 DeleteObject (fonts[i]);
843 goto font_messup;
844 }
845#endif
846}
847
848void request_resize (int w, int h, int refont) {
849 int width, height;
850
851 /* If the window is maximized supress resizing attempts */
852 if(IsZoomed(hwnd)) return;
853
854#ifdef CHECKOEMFONT
855 /* Don't do this in OEMANSI, you may get disable messages */
856 if (refont && w != cols && (cols==80 || cols==132)
857 && cfg.vtmode != VT_OEMANSI)
858#else
859 if (refont && w != cols && (cols==80 || cols==132))
860#endif
861 {
862 /* If font width too big for screen should we shrink the font more ? */
863 if (w==132)
864 font_width = ((font_width*cols+w/2)/w);
865 else
866 font_width = 0;
867 {
868 int i;
869 for (i=0; i<8; i++)
870 if (fonts[i])
871 DeleteObject(fonts[i]);
872 }
873 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
874 und_mode = UND_FONT;
875 init_fonts(font_width);
876 }
877 else
878 {
879 static int first_time = 1;
880 static RECT ss;
881
882 switch(first_time)
883 {
884 case 1:
885 /* Get the size of the screen */
886 if (GetClientRect(GetDesktopWindow(),&ss))
887 /* first_time = 0 */;
888 else { first_time = 2; break; }
889 case 0:
890 /* Make sure the values are sane */
891 width = (ss.right-ss.left-extra_width ) / font_width;
892 height = (ss.bottom-ss.top-extra_height ) / font_height;
893
894 if (w>width) w=width;
895 if (h>height) h=height;
896 if (w<15) w = 15;
897 if (h<1) w = 1;
898 }
899 }
900
901 width = extra_width + font_width * w;
902 height = extra_height + font_height * h;
903
904 SetWindowPos (hwnd, NULL, 0, 0, width, height,
905 SWP_NOACTIVATE | SWP_NOCOPYBITS |
906 SWP_NOMOVE | SWP_NOZORDER);
907}
908
909static void click (Mouse_Button b, int x, int y) {
910 int thistime = GetMessageTime();
911
912 if (lastbtn == b && thistime - lasttime < dbltime) {
913 lastact = (lastact == MA_CLICK ? MA_2CLK :
914 lastact == MA_2CLK ? MA_3CLK :
915 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
916 } else {
917 lastbtn = b;
918 lastact = MA_CLICK;
919 }
920 if (lastact != MA_NOTHING)
921 term_mouse (b, lastact, x, y);
922 lasttime = thistime;
923}
924
925static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
926 WPARAM wParam, LPARAM lParam) {
927 HDC hdc;
928 static int ignore_size = FALSE;
929 static int ignore_clip = FALSE;
930 static int ignore_keymenu = TRUE;
931 static int just_reconfigged = FALSE;
932
933 switch (message) {
934 case WM_TIMER:
935 if (pending_netevent)
936 enact_pending_netevent();
937 if (inbuf_head)
938 term_out();
939 term_update();
940 return 0;
941 case WM_CREATE:
942 break;
943 case WM_CLOSE:
944 if (!cfg.warn_on_close || session_closed ||
945 MessageBox(hwnd, "Are you sure you want to close this session?",
946 "PuTTY Exit Confirmation",
947 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
948 DestroyWindow(hwnd);
949 return 0;
950 case WM_DESTROY:
951 PostQuitMessage (0);
952 return 0;
953 case WM_SYSCOMMAND:
954 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
955 case SC_KEYMENU:
956 if (ignore_keymenu)
957 return 0; /* don't put up system menu on Alt */
958 break;
959 case IDM_SHOWLOG:
960 showeventlog(hwnd);
961 break;
962 case IDM_NEWSESS:
963 case IDM_DUPSESS:
964 case IDM_SAVEDSESS:
965 {
966 char b[2048];
967 char c[30], *cl;
968 int freecl = FALSE;
969 STARTUPINFO si;
970 PROCESS_INFORMATION pi;
971 HANDLE filemap = NULL;
972
973 if (wParam == IDM_DUPSESS) {
974 /*
975 * Allocate a file-mapping memory chunk for the
976 * config structure.
977 */
978 SECURITY_ATTRIBUTES sa;
979 Config *p;
980
981 sa.nLength = sizeof(sa);
982 sa.lpSecurityDescriptor = NULL;
983 sa.bInheritHandle = TRUE;
984 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
985 &sa,
986 PAGE_READWRITE,
987 0,
988 sizeof(Config),
989 NULL);
990 if (filemap) {
991 p = (Config *)MapViewOfFile(filemap,
992 FILE_MAP_WRITE,
993 0, 0, sizeof(Config));
994 if (p) {
995 *p = cfg; /* structure copy */
996 UnmapViewOfFile(p);
997 }
998 }
999 sprintf(c, "putty &%p", filemap);
1000 cl = c;
1001 } else if (wParam == IDM_SAVEDSESS) {
1002 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
1003 cl = malloc(16 + strlen(session)); /* 8, but play safe */
1004 if (!cl)
1005 cl = NULL; /* not a very important failure mode */
1006 else {
1007 sprintf(cl, "putty @%s", session);
1008 freecl = TRUE;
1009 }
1010 } else
1011 cl = NULL;
1012
1013 GetModuleFileName (NULL, b, sizeof(b)-1);
1014 si.cb = sizeof(si);
1015 si.lpReserved = NULL;
1016 si.lpDesktop = NULL;
1017 si.lpTitle = NULL;
1018 si.dwFlags = 0;
1019 si.cbReserved2 = 0;
1020 si.lpReserved2 = NULL;
1021 CreateProcess (b, cl, NULL, NULL, TRUE,
1022 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1023
1024 if (filemap)
1025 CloseHandle(filemap);
1026 if (freecl)
1027 free(cl);
1028 }
1029 break;
1030 case IDM_RECONF:
1031 if (!do_reconfig(hwnd))
1032 break;
1033 just_reconfigged = TRUE;
1034 {
1035 int i;
1036 for (i=0; i<8; i++)
1037 if (fonts[i])
1038 DeleteObject(fonts[i]);
1039 }
1040 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1041 und_mode = UND_FONT;
1042 init_fonts(0);
1043 sfree(logpal);
1044 /* Telnet will change local echo -> remote if the remote asks */
1045 if (cfg.protocol != PROT_TELNET)
1046 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
1047 if (pal)
1048 DeleteObject(pal);
1049 logpal = NULL;
1050 pal = NULL;
1051 cfgtopalette();
1052 init_palette();
1053
1054 /* Enable or disable the scroll bar, etc */
1055 {
1056 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1057
1058 nflg = flag;
1059 if (cfg.scrollbar) nflg |= WS_VSCROLL;
1060 else nflg &= ~WS_VSCROLL;
1061 if (cfg.locksize)
1062 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1063 else
1064 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1065
1066 if (nflg != flag)
1067 {
1068 RECT cr, wr;
1069
1070 SetWindowLong(hwnd, GWL_STYLE, nflg);
1071 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1072 SetWindowPos(hwnd, NULL, 0,0,0,0,
1073 SWP_NOACTIVATE|SWP_NOCOPYBITS|
1074 SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER|
1075 SWP_FRAMECHANGED);
1076
1077 GetWindowRect (hwnd, &wr);
1078 GetClientRect (hwnd, &cr);
1079 extra_width = wr.right - wr.left - cr.right + cr.left;
1080 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1081 }
1082 }
1083
1084 term_size(cfg.height, cfg.width, cfg.savelines);
1085 InvalidateRect(hwnd, NULL, TRUE);
1086 SetWindowPos (hwnd, NULL, 0, 0,
1087 extra_width + font_width * cfg.width,
1088 extra_height + font_height * cfg.height,
1089 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1090 SWP_NOMOVE | SWP_NOZORDER);
1091 if (IsIconic(hwnd)) {
1092 SetWindowText (hwnd,
1093 cfg.win_name_always ? window_name : icon_name);
1094 }
1095 break;
1096 case IDM_CLRSB:
1097 term_clrsb();
1098 break;
1099 case IDM_RESET:
1100 term_pwron();
1101 break;
1102 case IDM_TEL_AYT: back->special (TS_AYT); break;
1103 case IDM_TEL_BRK: back->special (TS_BRK); break;
1104 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1105 case IDM_TEL_EC: back->special (TS_EC); break;
1106 case IDM_TEL_EL: back->special (TS_EL); break;
1107 case IDM_TEL_GA: back->special (TS_GA); break;
1108 case IDM_TEL_NOP: back->special (TS_NOP); break;
1109 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1110 case IDM_TEL_AO: back->special (TS_AO); break;
1111 case IDM_TEL_IP: back->special (TS_IP); break;
1112 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1113 case IDM_TEL_EOR: back->special (TS_EOR); break;
1114 case IDM_TEL_EOF: back->special (TS_EOF); break;
1115 case IDM_ABOUT:
1116 showabout (hwnd);
1117 break;
1118 default:
1119 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1120 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1121 }
1122 }
1123 break;
1124
1125#define X_POS(l) ((int)(short)LOWORD(l))
1126#define Y_POS(l) ((int)(short)HIWORD(l))
1127
1128#define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1129#define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1130
1131 case WM_LBUTTONDOWN:
1132 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1133 TO_CHR_Y(Y_POS(lParam)));
1134 SetCapture(hwnd);
1135 return 0;
1136 case WM_LBUTTONUP:
1137 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1138 TO_CHR_Y(Y_POS(lParam)));
1139 ReleaseCapture();
1140 return 0;
1141 case WM_MBUTTONDOWN:
1142 SetCapture(hwnd);
1143 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1144 TO_CHR_X(X_POS(lParam)),
1145 TO_CHR_Y(Y_POS(lParam)));
1146 return 0;
1147 case WM_MBUTTONUP:
1148 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1149 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1150 TO_CHR_Y(Y_POS(lParam)));
1151 ReleaseCapture();
1152 return 0;
1153 case WM_RBUTTONDOWN:
1154 SetCapture(hwnd);
1155 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1156 TO_CHR_X(X_POS(lParam)),
1157 TO_CHR_Y(Y_POS(lParam)));
1158 return 0;
1159 case WM_RBUTTONUP:
1160 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1161 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1162 TO_CHR_Y(Y_POS(lParam)));
1163 ReleaseCapture();
1164 return 0;
1165 case WM_MOUSEMOVE:
1166 /*
1167 * Add the mouse position and message time to the random
1168 * number noise, if we're using ssh.
1169 */
1170 if (cfg.protocol == PROT_SSH)
1171 noise_ultralight(lParam);
1172
1173 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1174 Mouse_Button b;
1175 if (wParam & MK_LBUTTON)
1176 b = MB_SELECT;
1177 else if (wParam & MK_MBUTTON)
1178 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1179 else
1180 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1181 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1182 TO_CHR_Y(Y_POS(lParam)));
1183 }
1184 return 0;
1185 case WM_IGNORE_CLIP:
1186 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1187 break;
1188 case WM_IGNORE_KEYMENU:
1189 ignore_keymenu = wParam; /* do or don't ignore SC_KEYMENU */
1190 break;
1191 case WM_DESTROYCLIPBOARD:
1192 if (!ignore_clip)
1193 term_deselect();
1194 ignore_clip = FALSE;
1195 return 0;
1196 case WM_PAINT:
1197 {
1198 PAINTSTRUCT p;
1199 hdc = BeginPaint (hwnd, &p);
1200 if (pal) {
1201 SelectPalette (hdc, pal, TRUE);
1202 RealizePalette (hdc);
1203 }
1204 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1205 p.rcPaint.right, p.rcPaint.bottom);
1206 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1207 SelectObject (hdc, GetStockObject(WHITE_PEN));
1208 EndPaint (hwnd, &p);
1209 }
1210 return 0;
1211 case WM_NETEVENT:
1212 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1213 * but the only one that's likely to try to overload us is FD_READ.
1214 * This means buffering just one is fine.
1215 */
1216 if (pending_netevent)
1217 enact_pending_netevent();
1218
1219 pending_netevent = TRUE;
1220 pend_netevent_wParam=wParam;
1221 pend_netevent_lParam=lParam;
1222 return 0;
1223 case WM_SETFOCUS:
1224 has_focus = TRUE;
1225 term_out();
1226 term_update();
1227 break;
1228 case WM_KILLFOCUS:
1229 has_focus = FALSE;
1230 term_out();
1231 term_update();
1232 break;
1233 case WM_IGNORE_SIZE:
1234 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1235 break;
1236 case WM_ENTERSIZEMOVE:
1237 EnableSizeTip(1);
1238 break;
1239 case WM_EXITSIZEMOVE:
1240 EnableSizeTip(0);
1241 break;
1242 case WM_SIZING:
1243 {
1244 int width, height, w, h, ew, eh;
1245 LPRECT r = (LPRECT)lParam;
1246
1247 width = r->right - r->left - extra_width;
1248 height = r->bottom - r->top - extra_height;
1249 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1250 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1251 UpdateSizeTip(hwnd, w, h);
1252 ew = width - w * font_width;
1253 eh = height - h * font_height;
1254 if (ew != 0) {
1255 if (wParam == WMSZ_LEFT ||
1256 wParam == WMSZ_BOTTOMLEFT ||
1257 wParam == WMSZ_TOPLEFT)
1258 r->left += ew;
1259 else
1260 r->right -= ew;
1261 }
1262 if (eh != 0) {
1263 if (wParam == WMSZ_TOP ||
1264 wParam == WMSZ_TOPRIGHT ||
1265 wParam == WMSZ_TOPLEFT)
1266 r->top += eh;
1267 else
1268 r->bottom -= eh;
1269 }
1270 if (ew || eh)
1271 return 1;
1272 else
1273 return 0;
1274 }
1275 /* break; (never reached) */
1276 case WM_SIZE:
1277 if (wParam == SIZE_MINIMIZED) {
1278 SetWindowText (hwnd,
1279 cfg.win_name_always ? window_name : icon_name);
1280 break;
1281 }
1282 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1283 SetWindowText (hwnd, window_name);
1284 if (!ignore_size) {
1285 int width, height, w, h;
1286#if 0 /* we have fixed this using WM_SIZING now */
1287 int ew, eh;
1288#endif
1289
1290 width = LOWORD(lParam);
1291 height = HIWORD(lParam);
1292 w = width / font_width; if (w < 1) w = 1;
1293 h = height / font_height; if (h < 1) h = 1;
1294#if 0 /* we have fixed this using WM_SIZING now */
1295 ew = width - w * font_width;
1296 eh = height - h * font_height;
1297 if (ew != 0 || eh != 0) {
1298 RECT r;
1299 GetWindowRect (hwnd, &r);
1300 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1301 SetWindowPos (hwnd, NULL, 0, 0,
1302 r.right - r.left - ew, r.bottom - r.top - eh,
1303 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1304 }
1305#endif
1306 if (w != cols || h != rows || just_reconfigged) {
1307 term_invalidate();
1308 term_size (h, w, cfg.savelines);
1309 back->size();
1310 just_reconfigged = FALSE;
1311 }
1312 }
1313 ignore_size = FALSE;
1314 return 0;
1315 case WM_VSCROLL:
1316 switch (LOWORD(wParam)) {
1317 case SB_BOTTOM: term_scroll(-1, 0); break;
1318 case SB_TOP: term_scroll(+1, 0); break;
1319 case SB_LINEDOWN: term_scroll (0, +1); break;
1320 case SB_LINEUP: term_scroll (0, -1); break;
1321 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1322 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1323 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1324 term_scroll (1, HIWORD(wParam)); break;
1325 }
1326 break;
1327 case WM_PALETTECHANGED:
1328 if ((HWND) wParam != hwnd && pal != NULL) {
1329 HDC hdc = get_ctx();
1330 if (hdc) {
1331 if (RealizePalette (hdc) > 0)
1332 UpdateColors (hdc);
1333 free_ctx (hdc);
1334 }
1335 }
1336 break;
1337 case WM_QUERYNEWPALETTE:
1338 if (pal != NULL) {
1339 HDC hdc = get_ctx();
1340 if (hdc) {
1341 if (RealizePalette (hdc) > 0)
1342 UpdateColors (hdc);
1343 free_ctx (hdc);
1344 return TRUE;
1345 }
1346 }
1347 return FALSE;
1348 case WM_KEYDOWN:
1349 case WM_SYSKEYDOWN:
1350 case WM_KEYUP:
1351 case WM_SYSKEYUP:
1352 /*
1353 * Add the scan code and keypress timing to the random
1354 * number noise, if we're using ssh.
1355 */
1356 if (cfg.protocol == PROT_SSH)
1357 noise_ultralight(lParam);
1358
1359 /*
1360 * We don't do TranslateMessage since it disassociates the
1361 * resulting CHAR message from the KEYDOWN that sparked it,
1362 * which we occasionally don't want. Instead, we process
1363 * KEYDOWN, and call the Win32 translator functions so that
1364 * we get the translations under _our_ control.
1365 */
1366 {
1367 unsigned char buf[20];
1368 int len;
1369
1370 len = TranslateKey (message, wParam, lParam, buf);
1371 if (len == -1)
1372 return DefWindowProc (hwnd, message, wParam, lParam);
1373 ldisc->send (buf, len);
1374 }
1375 return 0;
1376 case WM_CHAR:
1377 case WM_SYSCHAR:
1378 /*
1379 * Nevertheless, we are prepared to deal with WM_CHAR
1380 * messages, should they crop up. So if someone wants to
1381 * post the things to us as part of a macro manoeuvre,
1382 * we're ready to cope.
1383 */
1384 {
1385 char c = xlat_kbd2tty((unsigned char)wParam);
1386 ldisc->send (&c, 1);
1387 }
1388 return 0;
1389 }
1390
1391 return DefWindowProc (hwnd, message, wParam, lParam);
1392}
1393
1394/*
1395 * Draw a line of text in the window, at given character
1396 * coordinates, in given attributes.
1397 *
1398 * We are allowed to fiddle with the contents of `text'.
1399 */
1400void do_text (Context ctx, int x, int y, char *text, int len,
1401 unsigned long attr, int lattr) {
1402 COLORREF fg, bg, t;
1403 int nfg, nbg, nfont;
1404 HDC hdc = ctx;
1405 RECT line_box;
1406 int force_manual_underline = 0;
1407 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1408 static int *IpDx = 0, IpDxLEN = 0;;
1409
1410 if (len>IpDxLEN || IpDx[0] != fnt_width) {
1411 int i;
1412 if (len>IpDxLEN) {
1413 sfree(IpDx);
1414 IpDx = smalloc((len+16)*sizeof(int));
1415 IpDxLEN = (len+16);
1416 }
1417 for(i=0; i<IpDxLEN; i++)
1418 IpDx[i] = fnt_width;
1419 }
1420
1421 x *= fnt_width;
1422 y *= font_height;
1423
1424 if (attr & ATTR_ACTCURS) {
1425 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1426 attr ^= ATTR_CUR_XOR;
1427 }
1428
1429 nfont = 0;
1430 if (cfg.vtmode == VT_OEMONLY)
1431 nfont |= FONT_OEM;
1432
1433 /*
1434 * Map high-half characters in order to approximate ISO using
1435 * OEM character set. No characters are missing if the OEM codepage
1436 * is CP850.
1437 */
1438 if (nfont & FONT_OEM) {
1439 int i;
1440 for (i=0; i<len; i++)
1441 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1442#if 0
1443 /* This is CP850 ... perfect translation */
1444 static const char oemhighhalf[] =
1445 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1446 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1447 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1448 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1449 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1450 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1451 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1452 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1453 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1454 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1455 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1456 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1457 ;
1458#endif
1459 /* This is CP437 ... junk translation */
1460 static const unsigned char oemhighhalf[] = {
1461 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1462 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1463 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1464 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1465 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1466 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1467 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1468 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1469 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1470 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1471 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1472 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1473 };
1474
1475 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1476 }
1477 }
1478
1479 if (attr & ATTR_GBCHR) {
1480 int i;
1481 /*
1482 * GB mapping: map # to pound, and everything else stays
1483 * normal.
1484 */
1485 for (i=0; i<len; i++)
1486 if (text[i] == '#')
1487 text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
1488 } else if (attr & ATTR_LINEDRW) {
1489 int i;
1490 /* ISO 8859-1 */
1491 static const char poorman[] =
1492 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1493
1494 /* CP437 */
1495 static const char oemmap_437[] =
1496 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1497 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1498
1499 /* CP850 */
1500 static const char oemmap_850[] =
1501 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1502 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1503
1504 /* Poor windows font ... eg: windows courier */
1505 static const char oemmap[] =
1506 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1507 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1508
1509 /*
1510 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1511 * VT100 line drawing chars; everything else stays normal.
1512 */
1513 switch (cfg.vtmode) {
1514 case VT_XWINDOWS:
1515 for (i=0; i<len; i++)
1516 if (text[i] >= '\x60' && text[i] <= '\x7E')
1517 text[i] += '\x01' - '\x60';
1518 break;
1519 case VT_OEMANSI:
1520 /* Make sure we actually have an OEM font */
1521 if (fonts[nfont|FONT_OEM]) {
1522 case VT_OEMONLY:
1523 nfont |= FONT_OEM;
1524 for (i=0; i<len; i++)
1525 if (text[i] >= '\x60' && text[i] <= '\x7E')
1526 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1527 break;
1528 }
1529 case VT_POORMAN:
1530 for (i=0; i<len; i++)
1531 if (text[i] >= '\x60' && text[i] <= '\x7E')
1532 text[i] = poorman[(unsigned char)text[i] - 0x60];
1533 break;
1534 }
1535 }
1536
1537 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1538 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1539 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1540 nfont |= FONT_BOLD;
1541 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1542 nfont |= FONT_UNDERLINE;
1543 if (!fonts[nfont])
1544 {
1545 if (nfont&FONT_UNDERLINE)
1546 force_manual_underline = 1;
1547 /* Don't do the same for manual bold, it could be bad news. */
1548
1549 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1550 }
1551 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1552 force_manual_underline = 1;
1553 if (attr & ATTR_REVERSE) {
1554 t = nfg; nfg = nbg; nbg = t;
1555 }
1556 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1557 nfg++;
1558 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1559 nbg++;
1560 fg = colours[nfg];
1561 bg = colours[nbg];
1562 SelectObject (hdc, fonts[nfont]);
1563 SetTextColor (hdc, fg);
1564 SetBkColor (hdc, bg);
1565 SetBkMode (hdc, OPAQUE);
1566 line_box.left = x;
1567 line_box.top = y;
1568 line_box.right = x+fnt_width*len;
1569 line_box.bottom = y+font_height;
1570 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1571 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1572 SetBkMode (hdc, TRANSPARENT);
1573
1574 /* GRR: This draws the character outside it's box and can leave
1575 * 'droppings' even with the clip box! I suppose I could loop it
1576 * one character at a time ... yuk.
1577 *
1578 * Or ... I could do a test print with "W", and use +1 or -1 for this
1579 * shift depending on if the leftmost column is blank...
1580 */
1581 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1582 }
1583 if (force_manual_underline ||
1584 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1585 HPEN oldpen;
1586 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1587 MoveToEx (hdc, x, y+descent, NULL);
1588 LineTo (hdc, x+len*fnt_width, y+descent);
1589 oldpen = SelectObject (hdc, oldpen);
1590 DeleteObject (oldpen);
1591 }
1592 if (attr & ATTR_PASCURS) {
1593 POINT pts[5];
1594 HPEN oldpen;
1595 pts[0].x = pts[1].x = pts[4].x = x;
1596 pts[2].x = pts[3].x = x+fnt_width-1;
1597 pts[0].y = pts[3].y = pts[4].y = y;
1598 pts[1].y = pts[2].y = y+font_height-1;
1599 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1600 Polyline (hdc, pts, 5);
1601 oldpen = SelectObject (hdc, oldpen);
1602 DeleteObject (oldpen);
1603 }
1604}
1605
1606static int check_compose(int first, int second) {
1607
1608 static char * composetbl[] = {
1609