Ooh. Actually, that vulnerability is further-reaching than I
[sgt/putty] / window.c
... / ...
Content-type: text/html mdw@git.distorted.org.uk Git - sgt/putty/blame_incremental - window.c


500 - Internal Server Error

Malformed UTF-8 character (fatal) at (eval 5) line 1, <$fd> line 1755.
CommitLineData
1#include <windows.h>
2#include <commctrl.h>
3#ifndef AUTO_WINSOCK
4#ifdef WINSOCK_TWO
5#include <winsock2.h>
6#else
7#include <winsock.h>
8#endif
9#endif
10#include <stdio.h>
11#include <stdlib.h>
12#include <ctype.h>
13#include <time.h>
14
15#define PUTTY_DO_GLOBALS /* actually _define_ globals */
16#include "putty.h"
17#include "winstuff.h"
18#include "storage.h"
19#include "win_res.h"
20
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
45
46#define WM_IGNORE_SIZE (WM_XUSER + 1)
47#define WM_IGNORE_CLIP (WM_XUSER + 2)
48
49/* Needed for Chinese support and apparently not always defined. */
50#ifndef VK_PROCESSKEY
51#define VK_PROCESSKEY 0xE5
52#endif
53
54static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
55static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
56static void cfgtopalette(void);
57static void init_palette(void);
58static void init_fonts(int);
59
60static int extra_width, extra_height;
61
62static int pending_netevent = 0;
63static WPARAM pend_netevent_wParam = 0;
64static LPARAM pend_netevent_lParam = 0;
65static void enact_pending_netevent(void);
66
67static time_t last_movement = 0;
68
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];
78static int font_needs_hand_underlining;
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
95static HBITMAP caretbm;
96
97static int dbltime, lasttime, lastact;
98static Mouse_Button lastbtn;
99
100static char *window_name, *icon_name;
101
102static Ldisc *real_ldisc;
103
104void begin_session(void) {
105 ldisc = real_ldisc;
106}
107
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
116 hinst = inst;
117 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
118
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
135 /* Ensure a Maximize setting in Explorer doesn't maximise the
136 * config box. */
137 defuse_showwindow();
138
139 /*
140 * Process the command line.
141 */
142 {
143 char *p;
144
145 default_protocol = DEFAULT_PROTOCOL;
146 default_port = DEFAULT_PORT;
147
148 do_defaults(NULL, &cfg);
149
150 p = cmdline;
151 while (*p && isspace(*p)) p++;
152
153 /*
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;
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";
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) {
195 cleanup_all();
196 }
197 exit(0);
198 }
199 p = q + strspn(q, " \t");
200 }
201
202 /*
203 * An initial @ means to activate a saved session.
204 */
205 if (*p == '@') {
206 int i = strlen(p);
207 while (i > 1 && isspace(p[i-1]))
208 i--;
209 p[i] = '\0';
210 do_defaults (p+1, &cfg);
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;
224 if (sscanf(p+1, "%p", &filemap) == 1 &&
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;
236 /*
237 * If the hostname starts with "telnet:", set the
238 * protocol to Telnet and process the string as a
239 * Telnet URL.
240 */
241 if (!strncmp(q, "telnet:", 7)) {
242 char c;
243
244 q += 7;
245 if (q[0] == '/' && q[1] == '/')
246 q += 2;
247 cfg.protocol = PROT_TELNET;
248 p = q;
249 while (*p && *p != ':' && *p != '/') p++;
250 c = *p;
251 if (*p)
252 *p++ = '\0';
253 if (c == ':')
254 cfg.port = atoi(p);
255 else
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 }
271 } else {
272 if (!do_config()) {
273 WSACleanup();
274 return 0;
275 }
276 }
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 }
290 }
291
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 }
311
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;
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));
325 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
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
364 {
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);
373 }
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;
381 init_fonts(0);
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 /*
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);
411 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
412 free(bits);
413 }
414
415 /*
416 * Initialise the scroll bar.
417 */
418 {
419 SCROLLINFO si;
420
421 si.cbSize = sizeof(si);
422 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
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;
435 char msg[1024], *title;
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;
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);
453 }
454
455 session_closed = FALSE;
456
457 /*
458 * Set up the input and output buffers.
459 */
460 inbuf_head = 0;
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);
475 HMENU p,s;
476 int i;
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");
498 AppendMenu (m, MF_SEPARATOR, 0, 0);
499 }
500 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
501 AppendMenu (m, MF_SEPARATOR, 0, 0);
502 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
503 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
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]);
508 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
509 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
510 AppendMenu (m, MF_SEPARATOR, 0, 0);
511 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
512 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
513 AppendMenu (m, MF_SEPARATOR, 0, 0);
514 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
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
532 if (GetMessage (&msg, NULL, 0, 0) == 1)
533 {
534 int timer_id = 0, long_timer = 0;
535
536 while (msg.message != WM_QUIT) {
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
551 /* Make sure we blink everything that needs it. */
552 term_blink(0);
553
554 /* Send the paste buffer if there's anything to send */
555 term_paste();
556
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 */
561 if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
562 continue;
563
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;
572 }
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;
599 }
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
616 if (cfg.protocol == PROT_SSH) {
617 random_save_seed();
618#ifdef MSCRYPTOAPI
619 crypto_wrapup();
620#endif
621 }
622
623 return msg.wParam;
624}
625
626/*
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/*
646 * Actually do the job requested by a WM_NETEVENT
647 */
648static void enact_pending_netevent(void) {
649 int i;
650 static int reentering = 0;
651
652 if (reentering)
653 return; /* don't unpend the pending */
654
655 pending_netevent = FALSE;
656
657 reentering = 1;
658 i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
659 reentering = 0;
660
661 if (i < 0) {
662 char buf[1024];
663 switch (WSABASEERR + (-i) % 10000) {
664 case WSAECONNRESET:
665 sprintf(buf, "Connection reset by peer");
666 break;
667 case WSAECONNABORTED:
668 sprintf(buf, "Connection aborted");
669 break;
670 default:
671 sprintf(buf, "Unexpected network error %d", -i);
672 break;
673 }
674 connection_fatal(buf);
675 }
676 if (i <= 0) {
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/*
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).
763 */
764static void init_fonts(int pick_width) {
765 TEXTMETRIC tm;
766 int i;
767 int fsize[8];
768 HDC hdc;
769 int fw_dontcare, fw_bold;
770 int firstchar = ' ';
771
772#ifdef CHECKOEMFONT
773font_messup:
774#endif
775 for (i=0; i<8; i++)
776 fonts[i] = NULL;
777
778 if (cfg.fontisbold) {
779 fw_dontcare = FW_BOLD;
780 fw_bold = FW_HEAVY;
781 } else {
782 fw_dontcare = FW_DONTCARE;
783 fw_bold = FW_BOLD;
784 }
785
786 hdc = GetDC(hwnd);
787
788 font_height = cfg.fontheight;
789 font_width = pick_width;
790
791#define f(i,c,w,u) \
792 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
793 c, OUT_DEFAULT_PRECIS, \
794 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
795 FIXED_PITCH | FF_DONTCARE, cfg.font)
796
797 if (cfg.vtmode != VT_OEMONLY) {
798 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
799
800 SelectObject (hdc, fonts[FONT_NORMAL]);
801 GetTextMetrics(hdc, &tm);
802 font_height = tm.tmHeight;
803 font_width = tm.tmAveCharWidth;
804
805 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
806
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
851 if (bold_mode == BOLD_FONT) {
852 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
853 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
854 }
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);
880 }
881 }
882#undef f
883
884 descent = tm.tmAscent + 1;
885 if (descent >= font_height)
886 descent = font_height - 1;
887 firstchar = tm.tmFirstChar;
888
889 for (i=0; i<8; i++) {
890 if (fonts[i]) {
891 if (SelectObject (hdc, fonts[i]) &&
892 GetTextMetrics(hdc, &tm) )
893 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
894 else fsize[i] = -i;
895 }
896 else fsize[i] = -i;
897 }
898
899 ReleaseDC (hwnd, hdc);
900
901 /* ... This is wrong in OEM only mode */
902 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
903 (bold_mode == BOLD_FONT &&
904 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
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 &&
912 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
913 bold_mode = BOLD_SHADOW;
914 DeleteObject (fonts[FONT_BOLD]);
915 if (und_mode == UND_FONT)
916 DeleteObject (fonts[FONT_BOLDUND]);
917 }
918
919#ifdef CHECKOEMFONT
920 /* With the fascist font painting it doesn't matter if the linedraw font
921 * isn't exactly the right size anymore so we don't have to check this.
922 */
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"
927 "different sizes. Using OEM-only mode instead",
928 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
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++)
947 if (fonts[i])
948 DeleteObject (fonts[i]);
949 goto font_messup;
950 }
951#endif
952}
953
954void request_resize (int w, int h, int refont) {
955 int width, height;
956
957 /* If the window is maximized supress resizing attempts */
958 if(IsZoomed(hwnd)) return;
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 }
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 }
1006
1007 width = extra_width + font_width * w;
1008 height = extra_height + font_height * h;
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) {
1016 int thistime = GetMessageTime();
1017
1018 if (lastbtn == b && thistime - lasttime < dbltime) {
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);
1028 lasttime = thistime;
1029}
1030
1031static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
1032 WPARAM wParam, LPARAM lParam) {
1033 HDC hdc;
1034 static int ignore_size = FALSE;
1035 static int ignore_clip = FALSE;
1036 static int just_reconfigged = FALSE;
1037 static int resizing = FALSE;
1038
1039 switch (message) {
1040 case WM_TIMER:
1041 if (pending_netevent)
1042 enact_pending_netevent();
1043 if (inbuf_head)
1044 term_out();
1045 HideCaret(hwnd);
1046 term_update();
1047 ShowCaret(hwnd);
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 }
1058 return 0;
1059 case WM_CREATE:
1060 break;
1061 case WM_CLOSE:
1062 if (!cfg.warn_on_close || session_closed ||
1063 MessageBox(hwnd, "Are you sure you want to close this session?",
1064 "PuTTY Exit Confirmation",
1065 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1066 DestroyWindow(hwnd);
1067 return 0;
1068 case WM_DESTROY:
1069 PostQuitMessage (0);
1070 return 0;
1071 case WM_SYSCOMMAND:
1072 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1073 case IDM_SHOWLOG:
1074 showeventlog(hwnd);
1075 break;
1076 case IDM_NEWSESS:
1077 case IDM_DUPSESS:
1078 case IDM_SAVEDSESS:
1079 {
1080 char b[2048];
1081 char c[30], *cl;
1082 int freecl = FALSE;
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 }
1113 sprintf(c, "putty &%p", filemap);
1114 cl = c;
1115 } else if (wParam == IDM_SAVEDSESS) {
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 */
1120 else {
1121 sprintf(cl, "putty @%s", session);
1122 freecl = TRUE;
1123 }
1124 } else
1125 cl = NULL;
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);
1140 if (freecl)
1141 free(cl);
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;
1156 init_fonts(0);
1157 sfree(logpal);
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);
1161 if (pal)
1162 DeleteObject(pal);
1163 logpal = NULL;
1164 pal = NULL;
1165 cfgtopalette();
1166 init_palette();
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
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;
1232 default:
1233 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1234 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1235 }
1236 }
1237 break;
1238
1239#define X_POS(l) ((int)(short)LOWORD(l))
1240#define Y_POS(l) ((int)(short)HIWORD(l))
1241
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
1245 case WM_LBUTTONDOWN:
1246 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1247 TO_CHR_Y(Y_POS(lParam)));
1248 SetCapture(hwnd);
1249 return 0;
1250 case WM_LBUTTONUP:
1251 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1252 TO_CHR_Y(Y_POS(lParam)));
1253 ReleaseCapture();
1254 return 0;
1255 case WM_MBUTTONDOWN:
1256 SetCapture(hwnd);
1257 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1258 TO_CHR_X(X_POS(lParam)),
1259 TO_CHR_Y(Y_POS(lParam)));
1260 return 0;
1261 case WM_MBUTTONUP:
1262 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1263 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1264 TO_CHR_Y(Y_POS(lParam)));
1265 ReleaseCapture();
1266 return 0;
1267 case WM_RBUTTONDOWN:
1268 SetCapture(hwnd);
1269 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1270 TO_CHR_X(X_POS(lParam)),
1271 TO_CHR_Y(Y_POS(lParam)));
1272 return 0;
1273 case WM_RBUTTONUP:
1274 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1275 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1276 TO_CHR_Y(Y_POS(lParam)));
1277 ReleaseCapture();
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;
1295 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1296 TO_CHR_Y(Y_POS(lParam)));
1297 }
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;
1310 HideCaret(hwnd);
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);
1321 ShowCaret(hwnd);
1322 }
1323 return 0;
1324 case WM_NETEVENT:
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;
1335 time(&last_movement);
1336 return 0;
1337 case WM_SETFOCUS:
1338 has_focus = TRUE;
1339 CreateCaret(hwnd, caretbm, 0, 0);
1340 ShowCaret(hwnd);
1341 term_out();
1342 term_update();
1343 break;
1344 case WM_KILLFOCUS:
1345 has_focus = FALSE;
1346 DestroyCaret();
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;
1353 case WM_ENTERSIZEMOVE:
1354 EnableSizeTip(1);
1355 resizing = TRUE;
1356 break;
1357 case WM_EXITSIZEMOVE:
1358 EnableSizeTip(0);
1359 resizing = FALSE;
1360 back->size();
1361 break;
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;
1371 UpdateSizeTip(hwnd, w, h);
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 }
1395 /* break; (never reached) */
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) {
1405 int width, height, w, h;
1406#if 0 /* we have fixed this using WM_SIZING now */
1407 int ew, eh;
1408#endif
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);
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();
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:
1476 case WM_KEYUP:
1477 case WM_SYSKEYUP:
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
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 }
1509 }
1510 return 0;
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 }
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 {
1528 char c = xlat_kbd2tty((unsigned char)wParam);
1529 ldisc->send (&c, 1);
1530 }
1531 return 0;
1532 }
1533
1534 return DefWindowProc (hwnd, message, wParam, lParam);
1535}
1536
1537/*
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/*
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,
1554 unsigned long attr, int lattr) {
1555 COLORREF fg, bg, t;
1556 int nfg, nbg, nfont;
1557 HDC hdc = ctx;
1558 RECT line_box;
1559 int force_manual_underline = 0;
1560 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1561 static int *IpDx = 0, IpDxLEN = 0;;
1562
1563 if (len>IpDxLEN || IpDx[0] != fnt_width) {
1564 int i;
1565 if (len>IpDxLEN) {
1566 sfree(IpDx);
1567 IpDx = smalloc((len+16)*sizeof(int));
1568 IpDxLEN = (len+16);
1569 }
1570 for(i=0; i<IpDxLEN; i++)
1571 IpDx[i] = fnt_width;
1572 }
1573
1574 x *= fnt_width;
1575 y *= font_height;
1576
1577 if (attr & ATTR_ACTCURS) {
1578 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
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
1588 * OEM character set. No characters are missing if the OEM codepage
1589 * is CP850.
1590 */
1591 if (nfont & FONT_OEM) {
1592 int i;
1593 for (i=0; i<len; i++)
1594 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1595#if 0
1596 /* This is CP850 ... perfect translation */
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 */
1602 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1603 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
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 ;
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
1628 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1629 }
1630 }
1631
1632 if (attr & ATTR_LINEDRW) {
1633 int i;
1634 /* ISO 8859-1 */
1635 static const char poorman[] =
1636 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1637
1638 /* CP437 */
1639 static const char oemmap_437[] =
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";
1642
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
1653 /*
1654 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1655 * VT100 line drawing chars; everything else stays normal.
1656 *
1657 * Actually '_' maps to space too, but that's done before.
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:
1666 /* Make sure we actually have an OEM font */
1667 if (fonts[nfont|FONT_OEM]) {
1668 case VT_OEMONLY:
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 }
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;
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 }
1697 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1698 force_manual_underline = 1;
1699 if (attr & ATTR_REVERSE) {
1700 t = nfg; nfg = nbg; nbg = t;
1701 }
1702 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1703 nfg++;
1704 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1705 nbg++;
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);
1712 line_box.left = x;
1713 line_box.top = y;
1714 line_box.right = x+fnt_width*len;
1715 line_box.bottom = y+font_height;
1716 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1717 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1718 SetBkMode (hdc, TRANSPARENT);
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
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 */
1727 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1728 }
1729 if (force_manual_underline ||
1730 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1731 HPEN oldpen;
1732 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1733 MoveToEx (hdc, x, y+descent, NULL);
1734 LineTo (hdc, x+len*fnt_width, y+descent);
1735 oldpen = SelectObject (hdc, oldpen);
1736 DeleteObject (oldpen);
1737 }
1738 if (attr & ATTR_PASCURS) {
1739 POINT pts[5];
1740 HPEN oldpen;
1741 pts[0].x = pts[1].x = pts[4].x = x;
1742 pts[2].x = pts[3].x = x+fnt_width-1;
1743 pts[0].y = pts[3].y = pts[4].y = y;
1744 pts[1].y = pts[2].y = y+font_height-1;
1745 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1746 Polyline (hdc, pts, 5);
1747 oldpen = SelectObject (hdc, oldpen);
1748 DeleteObject (oldpen);
1749 }
1750}
1751
1752static int check_compose(int first, int second) {
1753
1754 static char * composetbl[] = {
1755