Turn off paste-as-poorman for line drawing chars until it's configurable
[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 3981.
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");
652 break;
653 default:
654 sprintf(buf, "Unexpected network error %d", -i);
655 break;
656 }
8d5de777 657 connection_fatal(buf);
658 }
659 if (i <= 0) {
59ad2c03 660 if (cfg.close_on_exit)
661 PostQuitMessage(0);
662 else {
663 session_closed = TRUE;
664 MessageBox(hwnd, "Connection closed by remote host",
665 "PuTTY", MB_OK | MB_ICONINFORMATION);
666 SetWindowText (hwnd, "PuTTY (inactive)");
667 }
668 }
669}
670
671/*
374330e2 672 * Copy the colour palette from the configuration data into defpal.
673 * This is non-trivial because the colour indices are different.
674 */
675static void cfgtopalette(void) {
676 int i;
677 static const int ww[] = {
678 6, 7, 8, 9, 10, 11, 12, 13,
679 14, 15, 16, 17, 18, 19, 20, 21,
680 0, 1, 2, 3, 4, 4, 5, 5
681 };
682
683 for (i=0; i<24; i++) {
684 int w = ww[i];
685 defpal[i].rgbtRed = cfg.colours[w][0];
686 defpal[i].rgbtGreen = cfg.colours[w][1];
687 defpal[i].rgbtBlue = cfg.colours[w][2];
688 }
689}
690
691/*
692 * Set up the colour palette.
693 */
694static void init_palette(void) {
695 int i;
696 HDC hdc = GetDC (hwnd);
697 if (hdc) {
698 if (cfg.try_palette &&
699 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
700 logpal = smalloc(sizeof(*logpal)
701 - sizeof(logpal->palPalEntry)
702 + NCOLOURS * sizeof(PALETTEENTRY));
703 logpal->palVersion = 0x300;
704 logpal->palNumEntries = NCOLOURS;
705 for (i = 0; i < NCOLOURS; i++) {
706 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
707 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
708 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
709 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
710 }
711 pal = CreatePalette (logpal);
712 if (pal) {
713 SelectPalette (hdc, pal, FALSE);
714 RealizePalette (hdc);
715 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
716 FALSE);
717 }
718 }
719 ReleaseDC (hwnd, hdc);
720 }
721 if (pal)
722 for (i=0; i<NCOLOURS; i++)
723 colours[i] = PALETTERGB(defpal[i].rgbtRed,
724 defpal[i].rgbtGreen,
725 defpal[i].rgbtBlue);
726 else
727 for(i=0; i<NCOLOURS; i++)
728 colours[i] = RGB(defpal[i].rgbtRed,
729 defpal[i].rgbtGreen,
730 defpal[i].rgbtBlue);
731}
732
733/*
734 * Initialise all the fonts we will need. There may be as many as
735 * eight or as few as one. We also:
736 *
737 * - check the font width and height, correcting our guesses if
738 * necessary.
739 *
740 * - verify that the bold font is the same width as the ordinary
741 * one, and engage shadow bolding if not.
742 *
743 * - verify that the underlined font is the same width as the
744 * ordinary one (manual underlining by means of line drawing can
745 * be done in a pinch).
374330e2 746 */
59ad2c03 747static void init_fonts(int pick_width) {
374330e2 748 TEXTMETRIC tm;
97fc891e 749 int i;
59ad2c03 750 int fsize[8];
374330e2 751 HDC hdc;
752 int fw_dontcare, fw_bold;
97fc891e 753 int firstchar = ' ';
374330e2 754
59ad2c03 755#ifdef CHECKOEMFONT
97fc891e 756font_messup:
59ad2c03 757#endif
374330e2 758 for (i=0; i<8; i++)
759 fonts[i] = NULL;
760
761 if (cfg.fontisbold) {
762 fw_dontcare = FW_BOLD;
5b80d07f 763 fw_bold = FW_HEAVY;
374330e2 764 } else {
765 fw_dontcare = FW_DONTCARE;
766 fw_bold = FW_BOLD;
767 }
768
97fc891e 769 hdc = GetDC(hwnd);
770
771 font_height = cfg.fontheight;
59ad2c03 772 font_width = pick_width;
97fc891e 773
374330e2 774#define f(i,c,w,u) \
97fc891e 775 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
374330e2 776 c, OUT_DEFAULT_PRECIS, \
777 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
778 FIXED_PITCH | FF_DONTCARE, cfg.font)
97fc891e 779
374330e2 780 if (cfg.vtmode != VT_OEMONLY) {
14963b8f 781 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
97fc891e 782
783 SelectObject (hdc, fonts[FONT_NORMAL]);
784 GetTextMetrics(hdc, &tm);
785 font_height = tm.tmHeight;
786 font_width = tm.tmAveCharWidth;
787
14963b8f 788 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
97fc891e 789
09798031 790 /*
791 * Some fonts, e.g. 9-pt Courier, draw their underlines
792 * outside their character cell. We successfully prevent
793 * screen corruption by clipping the text output, but then
794 * we lose the underline completely. Here we try to work
795 * out whether this is such a font, and if it is, we set a
796 * flag that causes underlines to be drawn by hand.
797 *
798 * Having tried other more sophisticated approaches (such
799 * as examining the TEXTMETRIC structure or requesting the
800 * height of a string), I think we'll do this the brute
801 * force way: we create a small bitmap, draw an underlined
802 * space on it, and test to see whether any pixels are
803 * foreground-coloured. (Since we expect the underline to
804 * go all the way across the character cell, we only search
805 * down a single column of the bitmap, half way across.)
806 */
807 {
808 HDC und_dc;
809 HBITMAP und_bm, und_oldbm;
810 int i, gotit;
811 COLORREF c;
812
813 und_dc = CreateCompatibleDC(hdc);
814 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
815 und_oldbm = SelectObject(und_dc, und_bm);
816 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
817 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
818 SetTextColor (und_dc, RGB(255,255,255));
819 SetBkColor (und_dc, RGB(0,0,0));
820 SetBkMode (und_dc, OPAQUE);
821 ExtTextOut (und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
822 gotit = FALSE;
823 for (i = 0; i < font_height; i++) {
824 c = GetPixel(und_dc, font_width/2, i);
825 if (c != RGB(0,0,0))
826 gotit = TRUE;
827 }
828 SelectObject(und_dc, und_oldbm);
829 DeleteObject(und_bm);
830 DeleteDC(und_dc);
831 font_needs_hand_underlining = !gotit;
832 }
833
97fc891e 834 if (bold_mode == BOLD_FONT) {
14963b8f 835 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
836 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
374330e2 837 }
97fc891e 838
839 if (cfg.vtmode == VT_OEMANSI) {
840 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
841 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
842
843 if (bold_mode == BOLD_FONT) {
844 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
845 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
846 }
847 }
848 }
849 else
850 {
851 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
852
853 SelectObject (hdc, fonts[FONT_OEM]);
854 GetTextMetrics(hdc, &tm);
855 font_height = tm.tmHeight;
856 font_width = tm.tmAveCharWidth;
857
858 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
859
860 if (bold_mode == BOLD_FONT) {
861 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
862 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
374330e2 863 }
374330e2 864 }
865#undef f
866
97fc891e 867 descent = tm.tmAscent + 1;
868 if (descent >= font_height)
869 descent = font_height - 1;
870 firstchar = tm.tmFirstChar;
374330e2 871
97fc891e 872 for (i=0; i<8; i++) {
873 if (fonts[i]) {
59ad2c03 874 if (SelectObject (hdc, fonts[i]) &&
875 GetTextMetrics(hdc, &tm) )
876 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
877 else fsize[i] = -i;
374330e2 878 }
59ad2c03 879 else fsize[i] = -i;
374330e2 880 }
881
882 ReleaseDC (hwnd, hdc);
883
59ad2c03 884 /* ... This is wrong in OEM only mode */
97fc891e 885 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
374330e2 886 (bold_mode == BOLD_FONT &&
97fc891e 887 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
374330e2 888 und_mode = UND_LINE;
889 DeleteObject (fonts[FONT_UNDERLINE]);
890 if (bold_mode == BOLD_FONT)
891 DeleteObject (fonts[FONT_BOLDUND]);
892 }
893
894 if (bold_mode == BOLD_FONT &&
97fc891e 895 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
374330e2 896 bold_mode = BOLD_SHADOW;
897 DeleteObject (fonts[FONT_BOLD]);
898 if (und_mode == UND_FONT)
899 DeleteObject (fonts[FONT_BOLDUND]);
900 }
901
59ad2c03 902#ifdef CHECKOEMFONT
3cfb9f1c 903 /* With the fascist font painting it doesn't matter if the linedraw font
59ad2c03 904 * isn't exactly the right size anymore so we don't have to check this.
905 */
97fc891e 906 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
907 if( cfg.fontcharset == OEM_CHARSET )
908 {
909 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
374330e2 910 "different sizes. Using OEM-only mode instead",
911 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
97fc891e 912 cfg.vtmode = VT_OEMONLY;
913 }
914 else if( firstchar < ' ' )
915 {
916 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
917 "different sizes. Using XTerm mode instead",
918 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
919 cfg.vtmode = VT_XWINDOWS;
920 }
921 else
922 {
923 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
924 "different sizes. Using ISO8859-1 mode instead",
925 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
926 cfg.vtmode = VT_POORMAN;
927 }
928
929 for (i=0; i<8; i++)
374330e2 930 if (fonts[i])
931 DeleteObject (fonts[i]);
97fc891e 932 goto font_messup;
374330e2 933 }
59ad2c03 934#endif
374330e2 935}
936
59ad2c03 937void request_resize (int w, int h, int refont) {
938 int width, height;
c9def1b8 939
940 /* If the window is maximized supress resizing attempts */
941 if(IsZoomed(hwnd)) return;
59ad2c03 942
943#ifdef CHECKOEMFONT
944 /* Don't do this in OEMANSI, you may get disable messages */
945 if (refont && w != cols && (cols==80 || cols==132)
946 && cfg.vtmode != VT_OEMANSI)
947#else
948 if (refont && w != cols && (cols==80 || cols==132))
949#endif
950 {
951 /* If font width too big for screen should we shrink the font more ? */
952 if (w==132)
953 font_width = ((font_width*cols+w/2)/w);
954 else
955 font_width = 0;
956 {
957 int i;
958 for (i=0; i<8; i++)
959 if (fonts[i])
960 DeleteObject(fonts[i]);
961 }
962 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
963 und_mode = UND_FONT;
964 init_fonts(font_width);
965 }
c9def1b8 966 else
967 {
968 static int first_time = 1;
969 static RECT ss;
970
971 switch(first_time)
972 {
973 case 1:
974 /* Get the size of the screen */
975 if (GetClientRect(GetDesktopWindow(),&ss))
976 /* first_time = 0 */;
977 else { first_time = 2; break; }
978 case 0:
979 /* Make sure the values are sane */
980 width = (ss.right-ss.left-extra_width ) / font_width;
981 height = (ss.bottom-ss.top-extra_height ) / font_height;
982
983 if (w>width) w=width;
984 if (h>height) h=height;
985 if (w<15) w = 15;
986 if (h<1) w = 1;
987 }
988 }
59ad2c03 989
990 width = extra_width + font_width * w;
991 height = extra_height + font_height * h;
374330e2 992
993 SetWindowPos (hwnd, NULL, 0, 0, width, height,
994 SWP_NOACTIVATE | SWP_NOCOPYBITS |
995 SWP_NOMOVE | SWP_NOZORDER);
996}
997
998static void click (Mouse_Button b, int x, int y) {
fdedf2c8 999 int thistime = GetMessageTime();
1000
1001 if (lastbtn == b && thistime - lasttime < dbltime) {
374330e2 1002 lastact = (lastact == MA_CLICK ? MA_2CLK :
1003 lastact == MA_2CLK ? MA_3CLK :
1004 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1005 } else {
1006 lastbtn = b;
1007 lastact = MA_CLICK;
1008 }
1009 if (lastact != MA_NOTHING)
1010 term_mouse (b, lastact, x, y);
fdedf2c8 1011 lasttime = thistime;
374330e2 1012}
1013
996c8c3b 1014static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
1015 WPARAM wParam, LPARAM lParam) {
374330e2 1016 HDC hdc;
1017 static int ignore_size = FALSE;
1018 static int ignore_clip = FALSE;
1019 static int just_reconfigged = FALSE;
ffc31afe 1020 static int resizing = FALSE;
374330e2 1021
1022 switch (message) {
59ad2c03 1023 case WM_TIMER:
1024 if (pending_netevent)
1025 enact_pending_netevent();
c9def1b8 1026 if (inbuf_head)
59ad2c03 1027 term_out();
934c0b7a 1028 HideCaret(hwnd);
59ad2c03 1029 term_update();
934c0b7a 1030 ShowCaret(hwnd);
ec55b220 1031 if (cfg.ping_interval > 0)
1032 {
1033 time_t now;
1034 time(&now);
1035 if (now-last_movement > cfg.ping_interval * 60 - 10)
1036 {
1037 back->special(TS_PING);
1038 last_movement = now;
1039 }
1040 }
59ad2c03 1041 return 0;
374330e2 1042 case WM_CREATE:
1043 break;
68130d34 1044 case WM_CLOSE:
d85548fe 1045 if (!cfg.warn_on_close || session_closed ||
9ef49106 1046 MessageBox(hwnd, "Are you sure you want to close this session?",
68130d34 1047 "PuTTY Exit Confirmation",
1048 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1049 DestroyWindow(hwnd);
1050 return 0;
374330e2 1051 case WM_DESTROY:
1052 PostQuitMessage (0);
1053 return 0;
6833a413 1054 case WM_SYSCOMMAND:
1055 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
374330e2 1056 case IDM_SHOWLOG:
c5e9c988 1057 showeventlog(hwnd);
374330e2 1058 break;
1059 case IDM_NEWSESS:
1060 case IDM_DUPSESS:
6833a413 1061 case IDM_SAVEDSESS:
374330e2 1062 {
1063 char b[2048];
1064 char c[30], *cl;
e4e4cc7e 1065 int freecl = FALSE;
374330e2 1066 STARTUPINFO si;
1067 PROCESS_INFORMATION pi;
1068 HANDLE filemap = NULL;
1069
1070 if (wParam == IDM_DUPSESS) {
1071 /*
1072 * Allocate a file-mapping memory chunk for the
1073 * config structure.
1074 */
1075 SECURITY_ATTRIBUTES sa;
1076 Config *p;
1077
1078 sa.nLength = sizeof(sa);
1079 sa.lpSecurityDescriptor = NULL;
1080 sa.bInheritHandle = TRUE;
1081 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
1082 &sa,
1083 PAGE_READWRITE,
1084 0,
1085 sizeof(Config),
1086 NULL);
1087 if (filemap) {
1088 p = (Config *)MapViewOfFile(filemap,
1089 FILE_MAP_WRITE,
1090 0, 0, sizeof(Config));
1091 if (p) {
1092 *p = cfg; /* structure copy */
1093 UnmapViewOfFile(p);
1094 }
1095 }
1d470ad2 1096 sprintf(c, "putty &%p", filemap);
374330e2 1097 cl = c;
0a4aa984 1098 } else if (wParam == IDM_SAVEDSESS) {
e4e4cc7e 1099 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
1100 cl = malloc(16 + strlen(session)); /* 8, but play safe */
1101 if (!cl)
1102 cl = NULL; /* not a very important failure mode */
94e6450e 1103 else {
1104 sprintf(cl, "putty @%s", session);
1105 freecl = TRUE;
1106 }
374330e2 1107 } else
6833a413 1108 cl = NULL;
374330e2 1109
1110 GetModuleFileName (NULL, b, sizeof(b)-1);
1111 si.cb = sizeof(si);
1112 si.lpReserved = NULL;
1113 si.lpDesktop = NULL;
1114 si.lpTitle = NULL;
1115 si.dwFlags = 0;
1116 si.cbReserved2 = 0;
1117 si.lpReserved2 = NULL;
1118 CreateProcess (b, cl, NULL, NULL, TRUE,
1119 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1120
1121 if (filemap)
1122 CloseHandle(filemap);
e4e4cc7e 1123 if (freecl)
1124 free(cl);
374330e2 1125 }
1126 break;
1127 case IDM_RECONF:
1128 if (!do_reconfig(hwnd))
1129 break;
1130 just_reconfigged = TRUE;
1131 {
1132 int i;
1133 for (i=0; i<8; i++)
1134 if (fonts[i])
1135 DeleteObject(fonts[i]);
1136 }
1137 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1138 und_mode = UND_FONT;
59ad2c03 1139 init_fonts(0);
374330e2 1140 sfree(logpal);
59ad2c03 1141 /* Telnet will change local echo -> remote if the remote asks */
1142 if (cfg.protocol != PROT_TELNET)
1143 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
374330e2 1144 if (pal)
1145 DeleteObject(pal);
1146 logpal = NULL;
1147 pal = NULL;
1148 cfgtopalette();
1149 init_palette();
c9def1b8 1150
1151 /* Enable or disable the scroll bar, etc */
1152 {
1153 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1154
1155 nflg = flag;
1156 if (cfg.scrollbar) nflg |= WS_VSCROLL;
1157 else nflg &= ~WS_VSCROLL;
1158 if (cfg.locksize)
1159 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1160 else
1161 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1162
1163 if (nflg != flag)
1164 {
1165 RECT cr, wr;
1166
1167 SetWindowLong(hwnd, GWL_STYLE, nflg);
1168 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1169 SetWindowPos(hwnd, NULL, 0,0,0,0,
1170 SWP_NOACTIVATE|SWP_NOCOPYBITS|
1171 SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER|
1172 SWP_FRAMECHANGED);
1173
1174 GetWindowRect (hwnd, &wr);
1175 GetClientRect (hwnd, &cr);
1176 extra_width = wr.right - wr.left - cr.right + cr.left;
1177 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1178 }
1179 }
1180
374330e2 1181 term_size(cfg.height, cfg.width, cfg.savelines);
1182 InvalidateRect(hwnd, NULL, TRUE);
1183 SetWindowPos (hwnd, NULL, 0, 0,
1184 extra_width + font_width * cfg.width,
1185 extra_height + font_height * cfg.height,
1186 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1187 SWP_NOMOVE | SWP_NOZORDER);
1188 if (IsIconic(hwnd)) {
1189 SetWindowText (hwnd,
1190 cfg.win_name_always ? window_name : icon_name);
1191 }
1192 break;
1193 case IDM_CLRSB:
1194 term_clrsb();
1195 break;
1196 case IDM_RESET:
1197 term_pwron();
1198 break;
1199 case IDM_TEL_AYT: back->special (TS_AYT); break;
1200 case IDM_TEL_BRK: back->special (TS_BRK); break;
1201 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1202 case IDM_TEL_EC: back->special (TS_EC); break;
1203 case IDM_TEL_EL: back->special (TS_EL); break;
1204 case IDM_TEL_GA: back->special (TS_GA); break;
1205 case IDM_TEL_NOP: back->special (TS_NOP); break;
1206 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1207 case IDM_TEL_AO: back->special (TS_AO); break;
1208 case IDM_TEL_IP: back->special (TS_IP); break;
1209 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1210 case IDM_TEL_EOR: back->special (TS_EOR); break;
1211 case IDM_TEL_EOF: back->special (TS_EOF); break;
1212 case IDM_ABOUT:
1213 showabout (hwnd);
1214 break;
0a4aa984 1215 default:
1216 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1217 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1218 }
374330e2 1219 }
1220 break;
37508af4 1221
1222#define X_POS(l) ((int)(short)LOWORD(l))
1223#define Y_POS(l) ((int)(short)HIWORD(l))
1224
fdedf2c8 1225#define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1226#define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1227
374330e2 1228 case WM_LBUTTONDOWN:
fdedf2c8 1229 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1230 TO_CHR_Y(Y_POS(lParam)));
fef97f43 1231 SetCapture(hwnd);
374330e2 1232 return 0;
1233 case WM_LBUTTONUP:
fdedf2c8 1234 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1235 TO_CHR_Y(Y_POS(lParam)));
37508af4 1236 ReleaseCapture();
374330e2 1237 return 0;
1238 case WM_MBUTTONDOWN:
37508af4 1239 SetCapture(hwnd);
374330e2 1240 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
fdedf2c8 1241 TO_CHR_X(X_POS(lParam)),
1242 TO_CHR_Y(Y_POS(lParam)));
374330e2 1243 return 0;
1244 case WM_MBUTTONUP:
1245 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
fdedf2c8 1246 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1247 TO_CHR_Y(Y_POS(lParam)));
37508af4 1248 ReleaseCapture();
a0b1cefc 1249 return 0;
374330e2 1250 case WM_RBUTTONDOWN:
37508af4 1251 SetCapture(hwnd);
374330e2 1252 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
fdedf2c8 1253 TO_CHR_X(X_POS(lParam)),
1254 TO_CHR_Y(Y_POS(lParam)));
374330e2 1255 return 0;
1256 case WM_RBUTTONUP:
1257 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
fdedf2c8 1258 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1259 TO_CHR_Y(Y_POS(lParam)));
37508af4 1260 ReleaseCapture();
374330e2 1261 return 0;
1262 case WM_MOUSEMOVE:
1263 /*
1264 * Add the mouse position and message time to the random
1265 * number noise, if we're using ssh.
1266 */
1267 if (cfg.protocol == PROT_SSH)
1268 noise_ultralight(lParam);
1269
1270 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1271 Mouse_Button b;
1272 if (wParam & MK_LBUTTON)
1273 b = MB_SELECT;
1274 else if (wParam & MK_MBUTTON)
1275 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1276 else
1277 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
fdedf2c8 1278 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1279 TO_CHR_Y(Y_POS(lParam)));
374330e2 1280 }
374330e2 1281 return 0;
1282 case WM_IGNORE_CLIP:
1283 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1284 break;
1285 case WM_DESTROYCLIPBOARD:
1286 if (!ignore_clip)
1287 term_deselect();
1288 ignore_clip = FALSE;
1289 return 0;
1290 case WM_PAINT:
1291 {
1292 PAINTSTRUCT p;
934c0b7a 1293 HideCaret(hwnd);
374330e2 1294 hdc = BeginPaint (hwnd, &p);
1295 if (pal) {
1296 SelectPalette (hdc, pal, TRUE);
1297 RealizePalette (hdc);
1298 }
1299 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1300 p.rcPaint.right, p.rcPaint.bottom);
1301 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1302 SelectObject (hdc, GetStockObject(WHITE_PEN));
1303 EndPaint (hwnd, &p);
934c0b7a 1304 ShowCaret(hwnd);
374330e2 1305 }
1306 return 0;
1307 case WM_NETEVENT:
59ad2c03 1308 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1309 * but the only one that's likely to try to overload us is FD_READ.
1310 * This means buffering just one is fine.
1311 */
1312 if (pending_netevent)
1313 enact_pending_netevent();
1314
1315 pending_netevent = TRUE;
1316 pend_netevent_wParam=wParam;
1317 pend_netevent_lParam=lParam;
ec55b220 1318 time(&last_movement);
374330e2 1319 return 0;
1320 case WM_SETFOCUS:
1321 has_focus = TRUE;
934c0b7a 1322 CreateCaret(hwnd, caretbm, 0, 0);
1323 ShowCaret(hwnd);
374330e2 1324 term_out();
1325 term_update();
1326 break;
1327 case WM_KILLFOCUS:
1328 has_focus = FALSE;
ec8679e9 1329 DestroyCaret();
374330e2 1330 term_out();
1331 term_update();
1332 break;
1333 case WM_IGNORE_SIZE:
1334 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1335 break;
73251d5d 1336 case WM_ENTERSIZEMOVE:
996c8c3b 1337 EnableSizeTip(1);
ffc31afe 1338 resizing = TRUE;
996c8c3b 1339 break;
73251d5d 1340 case WM_EXITSIZEMOVE:
996c8c3b 1341 EnableSizeTip(0);
ffc31afe 1342 resizing = FALSE;
1343 back->size();
996c8c3b 1344 break;
374330e2 1345 case WM_SIZING:
1346 {
1347 int width, height, w, h, ew, eh;
1348 LPRECT r = (LPRECT)lParam;
1349
1350 width = r->right - r->left - extra_width;
1351 height = r->bottom - r->top - extra_height;
1352 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1353 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
996c8c3b 1354 UpdateSizeTip(hwnd, w, h);
374330e2 1355 ew = width - w * font_width;
1356 eh = height - h * font_height;
1357 if (ew != 0) {
1358 if (wParam == WMSZ_LEFT ||
1359 wParam == WMSZ_BOTTOMLEFT ||
1360 wParam == WMSZ_TOPLEFT)
1361 r->left += ew;
1362 else
1363 r->right -= ew;
1364 }
1365 if (eh != 0) {
1366 if (wParam == WMSZ_TOP ||
1367 wParam == WMSZ_TOPRIGHT ||
1368 wParam == WMSZ_TOPLEFT)
1369 r->top += eh;
1370 else
1371 r->bottom -= eh;
1372 }
1373 if (ew || eh)
1374 return 1;
1375 else
1376 return 0;
1377 }
996c8c3b 1378 /* break; (never reached) */
374330e2 1379 case WM_SIZE:
1380 if (wParam == SIZE_MINIMIZED) {
1381 SetWindowText (hwnd,
1382 cfg.win_name_always ? window_name : icon_name);
1383 break;
1384 }
1385 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1386 SetWindowText (hwnd, window_name);
1387 if (!ignore_size) {
1a6f78fe 1388 int width, height, w, h;
1389#if 0 /* we have fixed this using WM_SIZING now */
1390 int ew, eh;
1391#endif
374330e2 1392
1393 width = LOWORD(lParam);
1394 height = HIWORD(lParam);
1395 w = width / font_width; if (w < 1) w = 1;
1396 h = height / font_height; if (h < 1) h = 1;
1397#if 0 /* we have fixed this using WM_SIZING now */
1398 ew = width - w * font_width;
1399 eh = height - h * font_height;
1400 if (ew != 0 || eh != 0) {
1401 RECT r;
1402 GetWindowRect (hwnd, &r);
1403 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1404 SetWindowPos (hwnd, NULL, 0, 0,
1405 r.right - r.left - ew, r.bottom - r.top - eh,
1406 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1407 }
1408#endif
1409 if (w != cols || h != rows || just_reconfigged) {
1410 term_invalidate();
1411 term_size (h, w, cfg.savelines);
ffc31afe 1412 /*
1413 * Don't call back->size in mid-resize. (To prevent
1414 * massive numbers of resize events getting sent
1415 * down the connection during an NT opaque drag.)
1416 */
1417 if (!resizing)
1418 back->size();
374330e2 1419 just_reconfigged = FALSE;
1420 }
1421 }
1422 ignore_size = FALSE;
1423 return 0;
1424 case WM_VSCROLL:
1425 switch (LOWORD(wParam)) {
1426 case SB_BOTTOM: term_scroll(-1, 0); break;
1427 case SB_TOP: term_scroll(+1, 0); break;
1428 case SB_LINEDOWN: term_scroll (0, +1); break;
1429 case SB_LINEUP: term_scroll (0, -1); break;
1430 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1431 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1432 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1433 term_scroll (1, HIWORD(wParam)); break;
1434 }
1435 break;
1436 case WM_PALETTECHANGED:
1437 if ((HWND) wParam != hwnd && pal != NULL) {
1438 HDC hdc = get_ctx();
1439 if (hdc) {
1440 if (RealizePalette (hdc) > 0)
1441 UpdateColors (hdc);
1442 free_ctx (hdc);
1443 }
1444 }
1445 break;
1446 case WM_QUERYNEWPALETTE:
1447 if (pal != NULL) {
1448 HDC hdc = get_ctx();
1449 if (hdc) {
1450 if (RealizePalette (hdc) > 0)
1451 UpdateColors (hdc);
1452 free_ctx (hdc);
1453 return TRUE;
1454 }
1455 }
1456 return FALSE;
1457 case WM_KEYDOWN:
1458 case WM_SYSKEYDOWN:
c9def1b8 1459 case WM_KEYUP:
1460 case WM_SYSKEYUP:
374330e2 1461 /*
1462 * Add the scan code and keypress timing to the random
1463 * number noise, if we're using ssh.
1464 */
1465 if (cfg.protocol == PROT_SSH)
1466 noise_ultralight(lParam);
1467
1468 /*
1469 * We don't do TranslateMessage since it disassociates the
1470 * resulting CHAR message from the KEYDOWN that sparked it,
1471 * which we occasionally don't want. Instead, we process
1472 * KEYDOWN, and call the Win32 translator functions so that
1473 * we get the translations under _our_ control.
1474 */
1475 {
1476 unsigned char buf[20];
1477 int len;
1478
c9def1b8 1479 len = TranslateKey (message, wParam, lParam, buf);
c5e9c988 1480 if (len == -1)
1481 return DefWindowProc (hwnd, message, wParam, lParam);
5bc238bb 1482 ldisc->send (buf, len);
374330e2 1483 }
1484 return 0;
1485 case WM_CHAR:
1486 case WM_SYSCHAR:
1487 /*
1488 * Nevertheless, we are prepared to deal with WM_CHAR
1489 * messages, should they crop up. So if someone wants to
1490 * post the things to us as part of a macro manoeuvre,
1491 * we're ready to cope.
1492 */
1493 {
14963b8f 1494 char c = xlat_kbd2tty((unsigned char)wParam);
5bc238bb 1495 ldisc->send (&c, 1);
374330e2 1496 }
1497 return 0;
1498 }
1499
1500 return DefWindowProc (hwnd, message, wParam, lParam);
1501}
1502
1503/*
ec8679e9 1504 * Move the system caret. (We maintain one, even though it's
1505 * invisible, for the benefit of blind people: apparently some
1506 * helper software tracks the system caret, so we should arrange to
1507 * have one.)
1508 */
1509void sys_cursor(int x, int y) {
1510 SetCaretPos(x * font_width, y * font_height);
1511}
1512
1513/*
374330e2 1514 * Draw a line of text in the window, at given character
1515 * coordinates, in given attributes.
1516 *
1517 * We are allowed to fiddle with the contents of `text'.
1518 */
1519void do_text (Context ctx, int x, int y, char *text, int len,
c9def1b8 1520 unsigned long attr, int lattr) {
374330e2 1521 COLORREF fg, bg, t;
1522 int nfg, nbg, nfont;
1523 HDC hdc = ctx;
59ad2c03 1524 RECT line_box;
1525 int force_manual_underline = 0;
c9def1b8 1526 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
09798031 1527 static int *IpDx = 0, IpDxLEN = 0;;
59ad2c03 1528
c9def1b8 1529 if (len>IpDxLEN || IpDx[0] != fnt_width) {
59ad2c03 1530 int i;
1531 if (len>IpDxLEN) {
1532 sfree(IpDx);
1533 IpDx = smalloc((len+16)*sizeof(int));
1534 IpDxLEN = (len+16);
1535 }
c9def1b8 1536 for(i=0; i<IpDxLEN; i++)
1537 IpDx[i] = fnt_width;
59ad2c03 1538 }
374330e2 1539
c9def1b8 1540 x *= fnt_width;
374330e2 1541 y *= font_height;
1542
1543 if (attr & ATTR_ACTCURS) {
c9def1b8 1544 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
374330e2 1545 attr ^= ATTR_CUR_XOR;
1546 }
1547
1548 nfont = 0;
1549 if (cfg.vtmode == VT_OEMONLY)
1550 nfont |= FONT_OEM;
1551
1552 /*
1553 * Map high-half characters in order to approximate ISO using
59ad2c03 1554 * OEM character set. No characters are missing if the OEM codepage
1555 * is CP850.
374330e2 1556 */
1557 if (nfont & FONT_OEM) {
1558 int i;
1559 for (i=0; i<len; i++)
1560 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
59ad2c03 1561#if 0
1562 /* This is CP850 ... perfect translation */
374330e2 1563 static const char oemhighhalf[] =
1564 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1565 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1566 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1567 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
59ad2c03 1568 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1569 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
374330e2 1570 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1571 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1572 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1573 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1574 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1575 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1576 ;
59ad2c03 1577#endif
1578 /* This is CP437 ... junk translation */
1579 static const unsigned char oemhighhalf[] = {
1580 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1581 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1582 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1583 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1584 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1585 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1586 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1587 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1588 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1589 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1590 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1591 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1592 };
1593
374330e2 1594 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1595 }
1596 }
1597
ec55b220 1598 if (attr & ATTR_LINEDRW) {
374330e2 1599 int i;
59ad2c03 1600 /* ISO 8859-1 */
374330e2 1601 static const char poorman[] =
1602 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
59ad2c03 1603
1604 /* CP437 */
1605 static const char oemmap_437[] =
ed91c385 1606 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1607 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
374330e2 1608
59ad2c03 1609 /* CP850 */
1610 static const char oemmap_850[] =
1611 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1612 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1613
1614 /* Poor windows font ... eg: windows courier */
1615 static const char oemmap[] =
1616 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1617 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1618
374330e2 1619 /*
1620 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1621 * VT100 line drawing chars; everything else stays normal.
ec55b220 1622 *
1623 * Actually '_' maps to space too, but that's done before.
374330e2 1624 */
1625 switch (cfg.vtmode) {
1626 case VT_XWINDOWS:
1627 for (i=0; i<len; i++)
1628 if (text[i] >= '\x60' && text[i] <= '\x7E')
1629 text[i] += '\x01' - '\x60';
1630 break;
1631 case VT_OEMANSI:
59ad2c03 1632 /* Make sure we actually have an OEM font */
1633 if (fonts[nfont|FONT_OEM]) {
374330e2 1634 case VT_OEMONLY:
59ad2c03 1635 nfont |= FONT_OEM;
1636 for (i=0; i<len; i++)
1637 if (text[i] >= '\x60' && text[i] <= '\x7E')
1638 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1639 break;
1640 }
374330e2 1641 case VT_POORMAN:
1642 for (i=0; i<len; i++)
1643 if (text[i] >= '\x60' && text[i] <= '\x7E')
1644 text[i] = poorman[(unsigned char)text[i] - 0x60];
1645 break;
1646 }
1647 }
1648
1649 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1650 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1651 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1652 nfont |= FONT_BOLD;
1653 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1654 nfont |= FONT_UNDERLINE;
59ad2c03 1655 if (!fonts[nfont])
1656 {
1657 if (nfont&FONT_UNDERLINE)
1658 force_manual_underline = 1;
1659 /* Don't do the same for manual bold, it could be bad news. */
1660
1661 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1662 }
09798031 1663 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1664 force_manual_underline = 1;
374330e2 1665 if (attr & ATTR_REVERSE) {
1666 t = nfg; nfg = nbg; nbg = t;
1667 }
1668 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1669 nfg++;
59ad2c03 1670 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1671 nbg++;
374330e2 1672 fg = colours[nfg];
1673 bg = colours[nbg];
1674 SelectObject (hdc, fonts[nfont]);
1675 SetTextColor (hdc, fg);
1676 SetBkColor (hdc, bg);
1677 SetBkMode (hdc, OPAQUE);
59ad2c03 1678 line_box.left = x;
1679 line_box.top = y;
c9def1b8 1680 line_box.right = x+fnt_width*len;
59ad2c03 1681 line_box.bottom = y+font_height;
1682 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
374330e2 1683 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1684 SetBkMode (hdc, TRANSPARENT);
59ad2c03 1685
1686 /* GRR: This draws the character outside it's box and can leave
1687 * 'droppings' even with the clip box! I suppose I could loop it
c9def1b8 1688 * one character at a time ... yuk.
1689 *
1690 * Or ... I could do a test print with "W", and use +1 or -1 for this
1691 * shift depending on if the leftmost column is blank...
1692 */
59ad2c03 1693 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
374330e2 1694 }
59ad2c03 1695 if (force_manual_underline ||
1696 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1a6f78fe 1697 HPEN oldpen;
1698 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
374330e2 1699 MoveToEx (hdc, x, y+descent, NULL);
c9def1b8 1700 LineTo (hdc, x+len*fnt_width, y+descent);
1a6f78fe 1701 oldpen = SelectObject (hdc, oldpen);
1702 DeleteObject (oldpen);
374330e2 1703 }
1704 if (attr & ATTR_PASCURS) {
1705 POINT pts[5];
1a6f78fe 1706 HPEN oldpen;
374330e2 1707 pts[0].x = pts[1].x = pts[4].x = x;
c9def1b8 1708 pts[2].x = pts[3].x = x+fnt_width-1;
374330e2 1709 pts[0].y = pts[3].y = pts[4].y = y;
1710 pts[1].y = pts[2].y = y+font_height-1;
1a6f78fe 1711 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
374330e2 1712 Polyline (hdc, pts, 5);
1a6f78fe 1713 oldpen = SelectObject (hdc, oldpen);
1714 DeleteObject (oldpen);
374330e2 1715 }
1716}
1717
c9def1b8 1718static int check_compose(int first, int second) {
1719
1720 static char * composetbl[] = {
1721