Fix Blowfish-with-SSH2 combination, and enable user cipher
[sgt/putty] / window.c
Content-type: text/html mdw@git.distorted.org.uk Git - sgt/putty/blame - window.c


500 - Internal Server Error

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