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 - window.c


500 - Internal Server Error

Malformed UTF-8 character (fatal) at (eval 5) line 1, <$fd> line 3647.
CommitLineData
374330e2 1#include <windows.h>
2#include <commctrl.h>
3#include <winsock.h>
4#include <stdio.h>
5#include <stdlib.h>
1d470ad2 6#include <ctype.h>
374330e2 7
8#define PUTTY_DO_GLOBALS /* actually _define_ globals */
9#include "putty.h"
10#include "win_res.h"
11
6833a413 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
374330e2 36
59ad2c03 37#define WM_IGNORE_SIZE (WM_XUSER + 1)
38#define WM_IGNORE_CLIP (WM_XUSER + 2)
ca7b9fe5 39#define WM_IGNORE_KEYMENU (WM_XUSER + 3)
374330e2 40
996c8c3b 41static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
c9def1b8 42static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
374330e2 43static void cfgtopalette(void);
44static void init_palette(void);
59ad2c03 45static void init_fonts(int);
374330e2 46
47static int extra_width, extra_height;
48
59ad2c03 49static int pending_netevent = 0;
50static WPARAM pend_netevent_wParam = 0;
51static LPARAM pend_netevent_lParam = 0;
52static void enact_pending_netevent(void);
53
374330e2 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];
09798031 63static int font_needs_hand_underlining;
374330e2 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
73251d5d 93 putty_inst = inst;
67779be7 94 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
73251d5d 95
374330e2 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
e277c42d 118 default_protocol = DEFAULT_PROTOCOL;
119 default_port = DEFAULT_PORT;
120
374330e2 121 do_defaults(NULL);
122
123 p = cmdline;
124 while (*p && isspace(*p)) p++;
125
126 /*
e277c42d 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;
5fd04f07 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";
e277c42d 145 }
146 p = q + strspn(q, " \t");
147 }
148
149 /*
374330e2 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;
1d470ad2 167 if (sscanf(p+1, "%p", &filemap) == 1 &&
374330e2 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;
4e8bc59f 179 /*
70887be9 180 * If the hostname starts with "telnet:", set the
4e8bc59f 181 * protocol to Telnet and process the string as a
182 * Telnet URL.
183 */
70887be9 184 if (!strncmp(q, "telnet:", 7)) {
ab21de77 185 char c;
186
70887be9 187 q += 7;
188 if (q[0] == '/' && q[1] == '/')
189 q += 2;
4e8bc59f 190 cfg.protocol = PROT_TELNET;
191 p = q;
ab21de77 192 while (*p && *p != ':' && *p != '/') p++;
193 c = *p;
194 if (*p)
4e8bc59f 195 *p++ = '\0';
ab21de77 196 if (c == ':')
4e8bc59f 197 cfg.port = atoi(p);
ab21de77 198 else
4e8bc59f 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 }
374330e2 214 } else {
215 if (!do_config()) {
216 WSACleanup();
217 return 0;
218 }
219 }
220 }
221
89ee5268 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 }
5bc238bb 241
242 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
374330e2 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));
1bb542b2 252 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
374330e2 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
c9def1b8 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 }
374330e2 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;
59ad2c03 308 init_fonts(0);
374330e2 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);
c9def1b8 338 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
374330e2 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;
5ac36532 361 sprintf(msg, "%s - PuTTY", realhost);
374330e2 362 set_title (msg);
363 set_icon (msg);
364 }
365
d85548fe 366 session_closed = FALSE;
367
374330e2 368 /*
369 * Set up the input and output buffers.
370 */
c9def1b8 371 inbuf_head = 0;
374330e2 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);
0a4aa984 386 HMENU p,s;
387 int i;
374330e2 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");
374330e2 409 AppendMenu (m, MF_SEPARATOR, 0, 0);
410 }
45c4ea18 411 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
c5e9c988 412 AppendMenu (m, MF_SEPARATOR, 0, 0);
45c4ea18 413 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
414 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
0a4aa984 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]);
45c4ea18 419 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
420 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
374330e2 421 AppendMenu (m, MF_SEPARATOR, 0, 0);
45c4ea18 422 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
423 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
374330e2 424 AppendMenu (m, MF_SEPARATOR, 0, 0);
45c4ea18 425 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
374330e2 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
59ad2c03 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
c9def1b8 466 /* Send the paste buffer if there's anything to send */
467 term_paste();
468
59ad2c03 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 }
c9def1b8 485 if (inbuf_head)
59ad2c03 486 term_out();
487 term_update();
c9def1b8 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);
59ad2c03 494 long_timer = 1;
495 }
496 }
374330e2 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
8f203108 513 if (cfg.protocol == PROT_SSH) {
374330e2 514 random_save_seed();
8f203108 515#ifdef MSCRYPTOAPI
516 crypto_wrapup();
517#endif
518 }
374330e2 519
520 return msg.wParam;
521}
522
523/*
8d5de777 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/*
59ad2c03 543 * Actually do the job requested by a WM_NETEVENT
544 */
545static void enact_pending_netevent(void) {
546 int i;
9dde0b46 547 static int reentering = 0;
548
549 if (reentering)
550 return; /* don't unpend the pending */
551
59ad2c03 552 pending_netevent = FALSE;
9dde0b46 553
554 reentering = 1;
59ad2c03 555 i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
9dde0b46 556 reentering = 0;
59ad2c03 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 }
8d5de777 568 connection_fatal(buf);
569 }
570 if (i <= 0) {
59ad2c03 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/*
374330e2 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).
374330e2 657 */
59ad2c03 658static void init_fonts(int pick_width) {
374330e2 659 TEXTMETRIC tm;
97fc891e 660 int i;
59ad2c03 661 int fsize[8];
374330e2 662 HDC hdc;
663 int fw_dontcare, fw_bold;
97fc891e 664 int firstchar = ' ';
374330e2 665
59ad2c03 666#ifdef CHECKOEMFONT
97fc891e 667font_messup:
59ad2c03 668#endif
374330e2 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
97fc891e 680 hdc = GetDC(hwnd);
681
682 font_height = cfg.fontheight;
59ad2c03 683 font_width = pick_width;
97fc891e 684
374330e2 685#define f(i,c,w,u) \
97fc891e 686 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
374330e2 687 c, OUT_DEFAULT_PRECIS, \
688 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
689 FIXED_PITCH | FF_DONTCARE, cfg.font)
97fc891e 690
374330e2 691 if (cfg.vtmode != VT_OEMONLY) {
14963b8f 692 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
97fc891e 693
694 SelectObject (hdc, fonts[FONT_NORMAL]);
695 GetTextMetrics(hdc, &tm);
696 font_height = tm.tmHeight;
697 font_width = tm.tmAveCharWidth;
698
14963b8f 699 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
97fc891e 700
09798031 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
97fc891e 745 if (bold_mode == BOLD_FONT) {
14963b8f 746 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
747 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
374330e2 748 }
97fc891e 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);
374330e2 774 }
374330e2 775 }
776#undef f
777
97fc891e 778 descent = tm.tmAscent + 1;
779 if (descent >= font_height)
780 descent = font_height - 1;
781 firstchar = tm.tmFirstChar;
374330e2 782
97fc891e 783 for (i=0; i<8; i++) {
784 if (fonts[i]) {
59ad2c03 785 if (SelectObject (hdc, fonts[i]) &&
786 GetTextMetrics(hdc, &tm) )
787 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
788 else fsize[i] = -i;
374330e2 789 }
59ad2c03 790 else fsize[i] = -i;
374330e2 791 }
792
793 ReleaseDC (hwnd, hdc);
794
59ad2c03 795 /* ... This is wrong in OEM only mode */
97fc891e 796 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
374330e2 797 (bold_mode == BOLD_FONT &&
97fc891e 798 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
374330e2 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 &&
97fc891e 806 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
374330e2 807 bold_mode = BOLD_SHADOW;
808 DeleteObject (fonts[FONT_BOLD]);
809 if (und_mode == UND_FONT)
810 DeleteObject (fonts[FONT_BOLDUND]);
811 }
812
59ad2c03 813#ifdef CHECKOEMFONT
3cfb9f1c 814 /* With the fascist font painting it doesn't matter if the linedraw font
59ad2c03 815 * isn't exactly the right size anymore so we don't have to check this.
816 */
97fc891e 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"
374330e2 821 "different sizes. Using OEM-only mode instead",
822 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
97fc891e 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++)
374330e2 841 if (fonts[i])
842 DeleteObject (fonts[i]);
97fc891e 843 goto font_messup;
374330e2 844 }
59ad2c03 845#endif
374330e2 846}
847
59ad2c03 848void request_resize (int w, int h, int refont) {
849 int width, height;
c9def1b8 850
851 /* If the window is maximized supress resizing attempts */
852 if(IsZoomed(hwnd)) return;
59ad2c03 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 }
c9def1b8 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 }
59ad2c03 900
901 width = extra_width + font_width * w;
902 height = extra_height + font_height * h;
374330e2 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) {
fdedf2c8 910 int thistime = GetMessageTime();
911
912 if (lastbtn == b && thistime - lasttime < dbltime) {
374330e2 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);
fdedf2c8 922 lasttime = thistime;
374330e2 923}
924
996c8c3b 925static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
926 WPARAM wParam, LPARAM lParam) {
374330e2 927 HDC hdc;
928 static int ignore_size = FALSE;
929 static int ignore_clip = FALSE;
ca7b9fe5 930 static int ignore_keymenu = TRUE;
374330e2 931 static int just_reconfigged = FALSE;
932
933 switch (message) {
59ad2c03 934 case WM_TIMER:
935 if (pending_netevent)
936 enact_pending_netevent();
c9def1b8 937 if (inbuf_head)
59ad2c03 938 term_out();
939 term_update();
940 return 0;
374330e2 941 case WM_CREATE:
942 break;
68130d34 943 case WM_CLOSE:
d85548fe 944 if (!cfg.warn_on_close || session_closed ||
9ef49106 945 MessageBox(hwnd, "Are you sure you want to close this session?",
68130d34 946 "PuTTY Exit Confirmation",
947 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
948 DestroyWindow(hwnd);
949 return 0;
374330e2 950 case WM_DESTROY:
951 PostQuitMessage (0);
952 return 0;
6833a413 953 case WM_SYSCOMMAND:
954 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
ca7b9fe5 955 case SC_KEYMENU:
956 if (ignore_keymenu)
957 return 0; /* don't put up system menu on Alt */
958 break;
374330e2 959 case IDM_SHOWLOG:
c5e9c988 960 showeventlog(hwnd);
374330e2 961 break;
962 case IDM_NEWSESS:
963 case IDM_DUPSESS:
6833a413 964 case IDM_SAVEDSESS:
374330e2 965 {
966 char b[2048];
967 char c[30], *cl;
e4e4cc7e 968 int freecl = FALSE;
374330e2 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 }
1d470ad2 999 sprintf(c, "putty &%p", filemap);
374330e2 1000 cl = c;
0a4aa984 1001 } else if (wParam == IDM_SAVEDSESS) {
e4e4cc7e 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 */
94e6450e 1006 else {
1007 sprintf(cl, "putty @%s", session);
1008 freecl = TRUE;
1009 }
374330e2 1010 } else
6833a413 1011 cl = NULL;
374330e2 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);
e4e4cc7e 1026 if (freecl)
1027 free(cl);
374330e2 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;
59ad2c03 1042 init_fonts(0);
374330e2 1043 sfree(logpal);
59ad2c03 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);
374330e2 1047 if (pal)
1048 DeleteObject(pal);
1049 logpal = NULL;
1050 pal = NULL;
1051 cfgtopalette();
1052 init_palette();
c9def1b8 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
374330e2 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;
0a4aa984 1118 default:
1119 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1120 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1121 }
374330e2 1122 }
1123 break;
37508af4 1124
1125#define X_POS(l) ((int)(short)LOWORD(l))
1126#define Y_POS(l) ((int)(short)HIWORD(l))
1127
fdedf2c8 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
374330e2 1131 case WM_LBUTTONDOWN:
fdedf2c8 1132 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1133 TO_CHR_Y(Y_POS(lParam)));
fef97f43 1134 SetCapture(hwnd);
374330e2 1135 return 0;
1136 case WM_LBUTTONUP:
fdedf2c8 1137 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1138 TO_CHR_Y(Y_POS(lParam)));
37508af4 1139 ReleaseCapture();
374330e2 1140 return 0;
1141 case WM_MBUTTONDOWN:
37508af4 1142 SetCapture(hwnd);
374330e2 1143 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
fdedf2c8 1144 TO_CHR_X(X_POS(lParam)),
1145 TO_CHR_Y(Y_POS(lParam)));
374330e2 1146 return 0;
1147 case WM_MBUTTONUP:
1148 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
fdedf2c8 1149 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1150 TO_CHR_Y(Y_POS(lParam)));
37508af4 1151 ReleaseCapture();
a0b1cefc 1152 return 0;
374330e2 1153 case WM_RBUTTONDOWN:
37508af4 1154 SetCapture(hwnd);
374330e2 1155 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
fdedf2c8 1156 TO_CHR_X(X_POS(lParam)),
1157 TO_CHR_Y(Y_POS(lParam)));
374330e2 1158 return 0;
1159 case WM_RBUTTONUP:
1160 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
fdedf2c8 1161 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1162 TO_CHR_Y(Y_POS(lParam)));
37508af4 1163 ReleaseCapture();
374330e2 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;
fdedf2c8 1181 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1182 TO_CHR_Y(Y_POS(lParam)));
374330e2 1183 }
374330e2 1184 return 0;
1185 case WM_IGNORE_CLIP:
1186 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1187 break;
ca7b9fe5 1188 case WM_IGNORE_KEYMENU:
1189 ignore_keymenu = wParam; /* do or don't ignore SC_KEYMENU */
1190 break;
374330e2 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:
59ad2c03 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;
374330e2 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;
73251d5d 1236 case WM_ENTERSIZEMOVE:
996c8c3b 1237 EnableSizeTip(1);
1238 break;
73251d5d 1239 case WM_EXITSIZEMOVE:
996c8c3b 1240 EnableSizeTip(0);
1241 break;
374330e2 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;
996c8c3b 1251 UpdateSizeTip(hwnd, w, h);
374330e2 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 }
996c8c3b 1275 /* break; (never reached) */
374330e2 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) {
1a6f78fe 1285 int width, height, w, h;
1286#if 0 /* we have fixed this using WM_SIZING now */
1287 int ew, eh;
1288#endif
374330e2 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:
c9def1b8 1350 case WM_KEYUP:
1351 case WM_SYSKEYUP:
374330e2 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
c9def1b8 1370 len = TranslateKey (message, wParam, lParam, buf);
c5e9c988 1371 if (len == -1)
1372 return DefWindowProc (hwnd, message, wParam, lParam);
5bc238bb 1373 ldisc->send (buf, len);
374330e2 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 {
14963b8f 1385 char c = xlat_kbd2tty((unsigned char)wParam);
5bc238bb 1386 ldisc->send (&c, 1);
374330e2 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,
c9def1b8 1401 unsigned long attr, int lattr) {
374330e2 1402 COLORREF fg, bg, t;
1403 int nfg, nbg, nfont;
1404 HDC hdc = ctx;
59ad2c03 1405 RECT line_box;
1406 int force_manual_underline = 0;
c9def1b8 1407 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
09798031 1408 static int *IpDx = 0, IpDxLEN = 0;;
59ad2c03 1409
c9def1b8 1410 if (len>IpDxLEN || IpDx[0] != fnt_width) {
59ad2c03 1411 int i;
1412 if (len>IpDxLEN) {
1413 sfree(IpDx);
1414 IpDx = smalloc((len+16)*sizeof(int));
1415 IpDxLEN = (len+16);
1416 }
c9def1b8 1417 for(i=0; i<IpDxLEN; i++)
1418 IpDx[i] = fnt_width;
59ad2c03 1419 }
374330e2 1420
c9def1b8 1421 x *= fnt_width;
374330e2 1422 y *= font_height;
1423
1424 if (attr & ATTR_ACTCURS) {
c9def1b8 1425 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
374330e2 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
59ad2c03 1435 * OEM character set. No characters are missing if the OEM codepage
1436 * is CP850.
374330e2 1437 */
1438 if (nfont & FONT_OEM) {
1439 int i;
1440 for (i=0; i<len; i++)
1441 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
59ad2c03 1442#if 0
1443 /* This is CP850 ... perfect translation */
374330e2 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 */
59ad2c03 1449 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1450 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
374330e2 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 ;
59ad2c03 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
374330e2 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;
59ad2c03 1490 /* ISO 8859-1 */
374330e2 1491 static const char poorman[] =
1492 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
59ad2c03 1493
1494 /* CP437 */
1495 static const char oemmap_437[] =
ed91c385 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";
374330e2 1498
59ad2c03 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
374330e2 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:
59ad2c03 1520 /* Make sure we actually have an OEM font */
1521 if (fonts[nfont|FONT_OEM]) {
374330e2 1522 case VT_OEMONLY:
59ad2c03 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 }
374330e2 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;
59ad2c03 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 }
09798031 1551 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1552 force_manual_underline = 1;
374330e2 1553 if (attr & ATTR_REVERSE) {
1554 t = nfg; nfg = nbg; nbg = t;
1555 }
1556 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1557 nfg++;
59ad2c03 1558 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1559 nbg++;
374330e2 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);
59ad2c03 1566 line_box.left = x;
1567 line_box.top = y;
c9def1b8 1568 line_box.right = x+fnt_width*len;
59ad2c03 1569 line_box.bottom = y+font_height;
1570 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
374330e2 1571 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1572 SetBkMode (hdc, TRANSPARENT);
59ad2c03 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
c9def1b8 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 */
59ad2c03 1581 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
374330e2 1582 }
59ad2c03 1583 if (force_manual_underline ||
1584 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1a6f78fe 1585 HPEN oldpen;
1586 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
374330e2 1587 MoveToEx (hdc, x, y+descent, NULL);
c9def1b8 1588 LineTo (hdc, x+len*fnt_width, y+descent);
1a6f78fe 1589 oldpen = SelectObject (hdc, oldpen);
1590 DeleteObject (oldpen);
374330e2 1591 }
1592 if (attr & ATTR_PASCURS) {
1593 POINT pts[5];
1a6f78fe 1594 HPEN oldpen;
374330e2 1595 pts[0].x = pts[1].x = pts[4].x = x;
c9def1b8 1596 pts[2].x = pts[3].x = x+fnt_width-1;
374330e2 1597 pts[0].y = pts[3].y = pts[4].y = y;
1598 pts[1].y = pts[2].y = y+font_height-1;
1a6f78fe 1599 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
374330e2 1600 Polyline (hdc, pts, 5);
1a6f78fe 1601 oldpen = SelectObject (hdc, oldpen);
1602 DeleteObject (oldpen);
374330e2 1603 }
1604}
1605
c9def1b8 1606static int check_compose(int first, int second) {
1607
1608 static char * composetbl[] = {
1609