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