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