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