Introduce a sane interface function, from_backend(), for backends to
[u/mdw/putty] / window.c
... / ...
Content-type: text/html git.distorted.org.uk Git - u/mdw/putty/blame_incremental - window.c


500 - Internal Server Error

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