Public key authentication now works in pscp too - oops
[u/mdw/putty] / window.c
Content-type: text/html git.distorted.org.uk Git - u/mdw/putty/blame - window.c


500 - Internal Server Error

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