Add a config option to emulate the HMAC bug in commercial SSH v2.3.x
[sgt/putty] / window.c
Content-type: text/html mdw@git.distorted.org.uk Git - sgt/putty/blame - window.c


500 - Internal Server Error

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