A couple of SSH2 fixes
[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 3468.
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;
527 pending_netevent = FALSE;
528 i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
529
530 if (i < 0) {
531 char buf[1024];
532 switch (WSABASEERR + (-i) % 10000) {
533 case WSAECONNRESET:
534 sprintf(buf, "Connection reset by peer");
535 break;
536 default:
537 sprintf(buf, "Unexpected network error %d", -i);
538 break;
539 }
540 MessageBox(hwnd, buf, "PuTTY Fatal Error",
541 MB_ICONERROR | MB_OK);
542 PostQuitMessage(1);
543 } else if (i == 0) {
544 if (cfg.close_on_exit)
545 PostQuitMessage(0);
546 else {
547 session_closed = TRUE;
548 MessageBox(hwnd, "Connection closed by remote host",
549 "PuTTY", MB_OK | MB_ICONINFORMATION);
550 SetWindowText (hwnd, "PuTTY (inactive)");
551 }
552 }
553}
554
555/*
374330e2 556 * Copy the colour palette from the configuration data into defpal.
557 * This is non-trivial because the colour indices are different.
558 */
559static void cfgtopalette(void) {
560 int i;
561 static const int ww[] = {
562 6, 7, 8, 9, 10, 11, 12, 13,
563 14, 15, 16, 17, 18, 19, 20, 21,
564 0, 1, 2, 3, 4, 4, 5, 5
565 };
566
567 for (i=0; i<24; i++) {
568 int w = ww[i];
569 defpal[i].rgbtRed = cfg.colours[w][0];
570 defpal[i].rgbtGreen = cfg.colours[w][1];
571 defpal[i].rgbtBlue = cfg.colours[w][2];
572 }
573}
574
575/*
576 * Set up the colour palette.
577 */
578static void init_palette(void) {
579 int i;
580 HDC hdc = GetDC (hwnd);
581 if (hdc) {
582 if (cfg.try_palette &&
583 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
584 logpal = smalloc(sizeof(*logpal)
585 - sizeof(logpal->palPalEntry)
586 + NCOLOURS * sizeof(PALETTEENTRY));
587 logpal->palVersion = 0x300;
588 logpal->palNumEntries = NCOLOURS;
589 for (i = 0; i < NCOLOURS; i++) {
590 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
591 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
592 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
593 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
594 }
595 pal = CreatePalette (logpal);
596 if (pal) {
597 SelectPalette (hdc, pal, FALSE);
598 RealizePalette (hdc);
599 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
600 FALSE);
601 }
602 }
603 ReleaseDC (hwnd, hdc);
604 }
605 if (pal)
606 for (i=0; i<NCOLOURS; i++)
607 colours[i] = PALETTERGB(defpal[i].rgbtRed,
608 defpal[i].rgbtGreen,
609 defpal[i].rgbtBlue);
610 else
611 for(i=0; i<NCOLOURS; i++)
612 colours[i] = RGB(defpal[i].rgbtRed,
613 defpal[i].rgbtGreen,
614 defpal[i].rgbtBlue);
615}
616
617/*
618 * Initialise all the fonts we will need. There may be as many as
619 * eight or as few as one. We also:
620 *
621 * - check the font width and height, correcting our guesses if
622 * necessary.
623 *
624 * - verify that the bold font is the same width as the ordinary
625 * one, and engage shadow bolding if not.
626 *
627 * - verify that the underlined font is the same width as the
628 * ordinary one (manual underlining by means of line drawing can
629 * be done in a pinch).
374330e2 630 */
59ad2c03 631static void init_fonts(int pick_width) {
374330e2 632 TEXTMETRIC tm;
97fc891e 633 int i;
59ad2c03 634 int fsize[8];
374330e2 635 HDC hdc;
636 int fw_dontcare, fw_bold;
97fc891e 637 int firstchar = ' ';
374330e2 638
59ad2c03 639#ifdef CHECKOEMFONT
97fc891e 640font_messup:
59ad2c03 641#endif
374330e2 642 for (i=0; i<8; i++)
643 fonts[i] = NULL;
644
645 if (cfg.fontisbold) {
646 fw_dontcare = FW_BOLD;
647 fw_bold = FW_BLACK;
648 } else {
649 fw_dontcare = FW_DONTCARE;
650 fw_bold = FW_BOLD;
651 }
652
97fc891e 653 hdc = GetDC(hwnd);
654
655 font_height = cfg.fontheight;
59ad2c03 656 font_width = pick_width;
97fc891e 657
374330e2 658#define f(i,c,w,u) \
97fc891e 659 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
374330e2 660 c, OUT_DEFAULT_PRECIS, \
661 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
662 FIXED_PITCH | FF_DONTCARE, cfg.font)
97fc891e 663
374330e2 664 if (cfg.vtmode != VT_OEMONLY) {
14963b8f 665 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
97fc891e 666
667 SelectObject (hdc, fonts[FONT_NORMAL]);
668 GetTextMetrics(hdc, &tm);
669 font_height = tm.tmHeight;
670 font_width = tm.tmAveCharWidth;
671
14963b8f 672 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
97fc891e 673
674 if (bold_mode == BOLD_FONT) {
14963b8f 675 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
676 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
374330e2 677 }
97fc891e 678
679 if (cfg.vtmode == VT_OEMANSI) {
680 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
681 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
682
683 if (bold_mode == BOLD_FONT) {
684 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
685 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
686 }
687 }
688 }
689 else
690 {
691 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
692
693 SelectObject (hdc, fonts[FONT_OEM]);
694 GetTextMetrics(hdc, &tm);
695 font_height = tm.tmHeight;
696 font_width = tm.tmAveCharWidth;
697
698 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
699
700 if (bold_mode == BOLD_FONT) {
701 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
702 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
374330e2 703 }
374330e2 704 }
705#undef f
706
97fc891e 707 descent = tm.tmAscent + 1;
708 if (descent >= font_height)
709 descent = font_height - 1;
710 firstchar = tm.tmFirstChar;
374330e2 711
97fc891e 712 for (i=0; i<8; i++) {
713 if (fonts[i]) {
59ad2c03 714 if (SelectObject (hdc, fonts[i]) &&
715 GetTextMetrics(hdc, &tm) )
716 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
717 else fsize[i] = -i;
374330e2 718 }
59ad2c03 719 else fsize[i] = -i;
374330e2 720 }
721
722 ReleaseDC (hwnd, hdc);
723
59ad2c03 724 /* ... This is wrong in OEM only mode */
97fc891e 725 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
374330e2 726 (bold_mode == BOLD_FONT &&
97fc891e 727 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
374330e2 728 und_mode = UND_LINE;
729 DeleteObject (fonts[FONT_UNDERLINE]);
730 if (bold_mode == BOLD_FONT)
731 DeleteObject (fonts[FONT_BOLDUND]);
732 }
733
734 if (bold_mode == BOLD_FONT &&
97fc891e 735 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
374330e2 736 bold_mode = BOLD_SHADOW;
737 DeleteObject (fonts[FONT_BOLD]);
738 if (und_mode == UND_FONT)
739 DeleteObject (fonts[FONT_BOLDUND]);
740 }
741
59ad2c03 742#ifdef CHECKOEMFONT
3cfb9f1c 743 /* With the fascist font painting it doesn't matter if the linedraw font
59ad2c03 744 * isn't exactly the right size anymore so we don't have to check this.
745 */
97fc891e 746 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
747 if( cfg.fontcharset == OEM_CHARSET )
748 {
749 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
374330e2 750 "different sizes. Using OEM-only mode instead",
751 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
97fc891e 752 cfg.vtmode = VT_OEMONLY;
753 }
754 else if( firstchar < ' ' )
755 {
756 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
757 "different sizes. Using XTerm mode instead",
758 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
759 cfg.vtmode = VT_XWINDOWS;
760 }
761 else
762 {
763 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
764 "different sizes. Using ISO8859-1 mode instead",
765 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
766 cfg.vtmode = VT_POORMAN;
767 }
768
769 for (i=0; i<8; i++)
374330e2 770 if (fonts[i])
771 DeleteObject (fonts[i]);
97fc891e 772 goto font_messup;
374330e2 773 }
59ad2c03 774#endif
374330e2 775}
776
59ad2c03 777void request_resize (int w, int h, int refont) {
778 int width, height;
c9def1b8 779
780 /* If the window is maximized supress resizing attempts */
781 if(IsZoomed(hwnd)) return;
59ad2c03 782
783#ifdef CHECKOEMFONT
784 /* Don't do this in OEMANSI, you may get disable messages */
785 if (refont && w != cols && (cols==80 || cols==132)
786 && cfg.vtmode != VT_OEMANSI)
787#else
788 if (refont && w != cols && (cols==80 || cols==132))
789#endif
790 {
791 /* If font width too big for screen should we shrink the font more ? */
792 if (w==132)
793 font_width = ((font_width*cols+w/2)/w);
794 else
795 font_width = 0;
796 {
797 int i;
798 for (i=0; i<8; i++)
799 if (fonts[i])
800 DeleteObject(fonts[i]);
801 }
802 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
803 und_mode = UND_FONT;
804 init_fonts(font_width);
805 }
c9def1b8 806 else
807 {
808 static int first_time = 1;
809 static RECT ss;
810
811 switch(first_time)
812 {
813 case 1:
814 /* Get the size of the screen */
815 if (GetClientRect(GetDesktopWindow(),&ss))
816 /* first_time = 0 */;
817 else { first_time = 2; break; }
818 case 0:
819 /* Make sure the values are sane */
820 width = (ss.right-ss.left-extra_width ) / font_width;
821 height = (ss.bottom-ss.top-extra_height ) / font_height;
822
823 if (w>width) w=width;
824 if (h>height) h=height;
825 if (w<15) w = 15;
826 if (h<1) w = 1;
827 }
828 }
59ad2c03 829
830 width = extra_width + font_width * w;
831 height = extra_height + font_height * h;
374330e2 832
833 SetWindowPos (hwnd, NULL, 0, 0, width, height,
834 SWP_NOACTIVATE | SWP_NOCOPYBITS |
835 SWP_NOMOVE | SWP_NOZORDER);
836}
837
838static void click (Mouse_Button b, int x, int y) {
fdedf2c8 839 int thistime = GetMessageTime();
840
841 if (lastbtn == b && thistime - lasttime < dbltime) {
374330e2 842 lastact = (lastact == MA_CLICK ? MA_2CLK :
843 lastact == MA_2CLK ? MA_3CLK :
844 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
845 } else {
846 lastbtn = b;
847 lastact = MA_CLICK;
848 }
849 if (lastact != MA_NOTHING)
850 term_mouse (b, lastact, x, y);
fdedf2c8 851 lasttime = thistime;
374330e2 852}
853
996c8c3b 854static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
855 WPARAM wParam, LPARAM lParam) {
374330e2 856 HDC hdc;
857 static int ignore_size = FALSE;
858 static int ignore_clip = FALSE;
ca7b9fe5 859 static int ignore_keymenu = TRUE;
374330e2 860 static int just_reconfigged = FALSE;
861
862 switch (message) {
59ad2c03 863 case WM_TIMER:
864 if (pending_netevent)
865 enact_pending_netevent();
c9def1b8 866 if (inbuf_head)
59ad2c03 867 term_out();
868 term_update();
869 return 0;
374330e2 870 case WM_CREATE:
871 break;
68130d34 872 case WM_CLOSE:
d85548fe 873 if (!cfg.warn_on_close || session_closed ||
9ef49106 874 MessageBox(hwnd, "Are you sure you want to close this session?",
68130d34 875 "PuTTY Exit Confirmation",
876 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
877 DestroyWindow(hwnd);
878 return 0;
374330e2 879 case WM_DESTROY:
880 PostQuitMessage (0);
881 return 0;
6833a413 882 case WM_SYSCOMMAND:
883 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
ca7b9fe5 884 case SC_KEYMENU:
885 if (ignore_keymenu)
886 return 0; /* don't put up system menu on Alt */
887 break;
374330e2 888 case IDM_SHOWLOG:
c5e9c988 889 showeventlog(hwnd);
374330e2 890 break;
891 case IDM_NEWSESS:
892 case IDM_DUPSESS:
6833a413 893 case IDM_SAVEDSESS:
374330e2 894 {
895 char b[2048];
896 char c[30], *cl;
e4e4cc7e 897 int freecl = FALSE;
374330e2 898 STARTUPINFO si;
899 PROCESS_INFORMATION pi;
900 HANDLE filemap = NULL;
901
902 if (wParam == IDM_DUPSESS) {
903 /*
904 * Allocate a file-mapping memory chunk for the
905 * config structure.
906 */
907 SECURITY_ATTRIBUTES sa;
908 Config *p;
909
910 sa.nLength = sizeof(sa);
911 sa.lpSecurityDescriptor = NULL;
912 sa.bInheritHandle = TRUE;
913 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
914 &sa,
915 PAGE_READWRITE,
916 0,
917 sizeof(Config),
918 NULL);
919 if (filemap) {
920 p = (Config *)MapViewOfFile(filemap,
921 FILE_MAP_WRITE,
922 0, 0, sizeof(Config));
923 if (p) {
924 *p = cfg; /* structure copy */
925 UnmapViewOfFile(p);
926 }
927 }
1d470ad2 928 sprintf(c, "putty &%p", filemap);
374330e2 929 cl = c;
0a4aa984 930 } else if (wParam == IDM_SAVEDSESS) {
e4e4cc7e 931 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
932 cl = malloc(16 + strlen(session)); /* 8, but play safe */
933 if (!cl)
934 cl = NULL; /* not a very important failure mode */
94e6450e 935 else {
936 sprintf(cl, "putty @%s", session);
937 freecl = TRUE;
938 }
374330e2 939 } else
6833a413 940 cl = NULL;
374330e2 941
942 GetModuleFileName (NULL, b, sizeof(b)-1);
943 si.cb = sizeof(si);
944 si.lpReserved = NULL;
945 si.lpDesktop = NULL;
946 si.lpTitle = NULL;
947 si.dwFlags = 0;
948 si.cbReserved2 = 0;
949 si.lpReserved2 = NULL;
950 CreateProcess (b, cl, NULL, NULL, TRUE,
951 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
952
953 if (filemap)
954 CloseHandle(filemap);
e4e4cc7e 955 if (freecl)
956 free(cl);
374330e2 957 }
958 break;
959 case IDM_RECONF:
960 if (!do_reconfig(hwnd))
961 break;
962 just_reconfigged = TRUE;
963 {
964 int i;
965 for (i=0; i<8; i++)
966 if (fonts[i])
967 DeleteObject(fonts[i]);
968 }
969 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
970 und_mode = UND_FONT;
59ad2c03 971 init_fonts(0);
374330e2 972 sfree(logpal);
59ad2c03 973 /* Telnet will change local echo -> remote if the remote asks */
974 if (cfg.protocol != PROT_TELNET)
975 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
374330e2 976 if (pal)
977 DeleteObject(pal);
978 logpal = NULL;
979 pal = NULL;
980 cfgtopalette();
981 init_palette();
c9def1b8 982
983 /* Enable or disable the scroll bar, etc */
984 {
985 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
986
987 nflg = flag;
988 if (cfg.scrollbar) nflg |= WS_VSCROLL;
989 else nflg &= ~WS_VSCROLL;
990 if (cfg.locksize)
991 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
992 else
993 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
994
995 if (nflg != flag)
996 {
997 RECT cr, wr;
998
999 SetWindowLong(hwnd, GWL_STYLE, nflg);
1000 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1001 SetWindowPos(hwnd, NULL, 0,0,0,0,
1002 SWP_NOACTIVATE|SWP_NOCOPYBITS|
1003 SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER|
1004 SWP_FRAMECHANGED);
1005
1006 GetWindowRect (hwnd, &wr);
1007 GetClientRect (hwnd, &cr);
1008 extra_width = wr.right - wr.left - cr.right + cr.left;
1009 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1010 }
1011 }
1012
374330e2 1013 term_size(cfg.height, cfg.width, cfg.savelines);
1014 InvalidateRect(hwnd, NULL, TRUE);
1015 SetWindowPos (hwnd, NULL, 0, 0,
1016 extra_width + font_width * cfg.width,
1017 extra_height + font_height * cfg.height,
1018 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1019 SWP_NOMOVE | SWP_NOZORDER);
1020 if (IsIconic(hwnd)) {
1021 SetWindowText (hwnd,
1022 cfg.win_name_always ? window_name : icon_name);
1023 }
1024 break;
1025 case IDM_CLRSB:
1026 term_clrsb();
1027 break;
1028 case IDM_RESET:
1029 term_pwron();
1030 break;
1031 case IDM_TEL_AYT: back->special (TS_AYT); break;
1032 case IDM_TEL_BRK: back->special (TS_BRK); break;
1033 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1034 case IDM_TEL_EC: back->special (TS_EC); break;
1035 case IDM_TEL_EL: back->special (TS_EL); break;
1036 case IDM_TEL_GA: back->special (TS_GA); break;
1037 case IDM_TEL_NOP: back->special (TS_NOP); break;
1038 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1039 case IDM_TEL_AO: back->special (TS_AO); break;
1040 case IDM_TEL_IP: back->special (TS_IP); break;
1041 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1042 case IDM_TEL_EOR: back->special (TS_EOR); break;
1043 case IDM_TEL_EOF: back->special (TS_EOF); break;
1044 case IDM_ABOUT:
1045 showabout (hwnd);
1046 break;
0a4aa984 1047 default:
1048 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1049 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1050 }
374330e2 1051 }
1052 break;
37508af4 1053
1054#define X_POS(l) ((int)(short)LOWORD(l))
1055#define Y_POS(l) ((int)(short)HIWORD(l))
1056
fdedf2c8 1057#define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1058#define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1059
374330e2 1060 case WM_LBUTTONDOWN:
fdedf2c8 1061 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1062 TO_CHR_Y(Y_POS(lParam)));
fef97f43 1063 SetCapture(hwnd);
374330e2 1064 return 0;
1065 case WM_LBUTTONUP:
fdedf2c8 1066 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1067 TO_CHR_Y(Y_POS(lParam)));
37508af4 1068 ReleaseCapture();
374330e2 1069 return 0;
1070 case WM_MBUTTONDOWN:
37508af4 1071 SetCapture(hwnd);
374330e2 1072 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
fdedf2c8 1073 TO_CHR_X(X_POS(lParam)),
1074 TO_CHR_Y(Y_POS(lParam)));
374330e2 1075 return 0;
1076 case WM_MBUTTONUP:
1077 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
fdedf2c8 1078 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1079 TO_CHR_Y(Y_POS(lParam)));
37508af4 1080 ReleaseCapture();
a0b1cefc 1081 return 0;
374330e2 1082 case WM_RBUTTONDOWN:
37508af4 1083 SetCapture(hwnd);
374330e2 1084 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
fdedf2c8 1085 TO_CHR_X(X_POS(lParam)),
1086 TO_CHR_Y(Y_POS(lParam)));
374330e2 1087 return 0;
1088 case WM_RBUTTONUP:
1089 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
fdedf2c8 1090 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1091 TO_CHR_Y(Y_POS(lParam)));
37508af4 1092 ReleaseCapture();
374330e2 1093 return 0;
1094 case WM_MOUSEMOVE:
1095 /*
1096 * Add the mouse position and message time to the random
1097 * number noise, if we're using ssh.
1098 */
1099 if (cfg.protocol == PROT_SSH)
1100 noise_ultralight(lParam);
1101
1102 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1103 Mouse_Button b;
1104 if (wParam & MK_LBUTTON)
1105 b = MB_SELECT;
1106 else if (wParam & MK_MBUTTON)
1107 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1108 else
1109 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
fdedf2c8 1110 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1111 TO_CHR_Y(Y_POS(lParam)));
374330e2 1112 }
374330e2 1113 return 0;
1114 case WM_IGNORE_CLIP:
1115 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1116 break;
ca7b9fe5 1117 case WM_IGNORE_KEYMENU:
1118 ignore_keymenu = wParam; /* do or don't ignore SC_KEYMENU */
1119 break;
374330e2 1120 case WM_DESTROYCLIPBOARD:
1121 if (!ignore_clip)
1122 term_deselect();
1123 ignore_clip = FALSE;
1124 return 0;
1125 case WM_PAINT:
1126 {
1127 PAINTSTRUCT p;
1128 hdc = BeginPaint (hwnd, &p);
1129 if (pal) {
1130 SelectPalette (hdc, pal, TRUE);
1131 RealizePalette (hdc);
1132 }
1133 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1134 p.rcPaint.right, p.rcPaint.bottom);
1135 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1136 SelectObject (hdc, GetStockObject(WHITE_PEN));
1137 EndPaint (hwnd, &p);
1138 }
1139 return 0;
1140 case WM_NETEVENT:
59ad2c03 1141 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1142 * but the only one that's likely to try to overload us is FD_READ.
1143 * This means buffering just one is fine.
1144 */
1145 if (pending_netevent)
1146 enact_pending_netevent();
1147
1148 pending_netevent = TRUE;
1149 pend_netevent_wParam=wParam;
1150 pend_netevent_lParam=lParam;
374330e2 1151 return 0;
1152 case WM_SETFOCUS:
1153 has_focus = TRUE;
1154 term_out();
1155 term_update();
1156 break;
1157 case WM_KILLFOCUS:
1158 has_focus = FALSE;
1159 term_out();
1160 term_update();
1161 break;
1162 case WM_IGNORE_SIZE:
1163 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1164 break;
73251d5d 1165 case WM_ENTERSIZEMOVE:
996c8c3b 1166 EnableSizeTip(1);
1167 break;
73251d5d 1168 case WM_EXITSIZEMOVE:
996c8c3b 1169 EnableSizeTip(0);
1170 break;
374330e2 1171 case WM_SIZING:
1172 {
1173 int width, height, w, h, ew, eh;
1174 LPRECT r = (LPRECT)lParam;
1175
1176 width = r->right - r->left - extra_width;
1177 height = r->bottom - r->top - extra_height;
1178 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1179 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
996c8c3b 1180 UpdateSizeTip(hwnd, w, h);
374330e2 1181 ew = width - w * font_width;
1182 eh = height - h * font_height;
1183 if (ew != 0) {
1184 if (wParam == WMSZ_LEFT ||
1185 wParam == WMSZ_BOTTOMLEFT ||
1186 wParam == WMSZ_TOPLEFT)
1187 r->left += ew;
1188 else
1189 r->right -= ew;
1190 }
1191 if (eh != 0) {
1192 if (wParam == WMSZ_TOP ||
1193 wParam == WMSZ_TOPRIGHT ||
1194 wParam == WMSZ_TOPLEFT)
1195 r->top += eh;
1196 else
1197 r->bottom -= eh;
1198 }
1199 if (ew || eh)
1200 return 1;
1201 else
1202 return 0;
1203 }
996c8c3b 1204 /* break; (never reached) */
374330e2 1205 case WM_SIZE:
1206 if (wParam == SIZE_MINIMIZED) {
1207 SetWindowText (hwnd,
1208 cfg.win_name_always ? window_name : icon_name);
1209 break;
1210 }
1211 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1212 SetWindowText (hwnd, window_name);
1213 if (!ignore_size) {
1a6f78fe 1214 int width, height, w, h;
1215#if 0 /* we have fixed this using WM_SIZING now */
1216 int ew, eh;
1217#endif
374330e2 1218
1219 width = LOWORD(lParam);
1220 height = HIWORD(lParam);
1221 w = width / font_width; if (w < 1) w = 1;
1222 h = height / font_height; if (h < 1) h = 1;
1223#if 0 /* we have fixed this using WM_SIZING now */
1224 ew = width - w * font_width;
1225 eh = height - h * font_height;
1226 if (ew != 0 || eh != 0) {
1227 RECT r;
1228 GetWindowRect (hwnd, &r);
1229 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1230 SetWindowPos (hwnd, NULL, 0, 0,
1231 r.right - r.left - ew, r.bottom - r.top - eh,
1232 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1233 }
1234#endif
1235 if (w != cols || h != rows || just_reconfigged) {
1236 term_invalidate();
1237 term_size (h, w, cfg.savelines);
1238 back->size();
1239 just_reconfigged = FALSE;
1240 }
1241 }
1242 ignore_size = FALSE;
1243 return 0;
1244 case WM_VSCROLL:
1245 switch (LOWORD(wParam)) {
1246 case SB_BOTTOM: term_scroll(-1, 0); break;
1247 case SB_TOP: term_scroll(+1, 0); break;
1248 case SB_LINEDOWN: term_scroll (0, +1); break;
1249 case SB_LINEUP: term_scroll (0, -1); break;
1250 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1251 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1252 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1253 term_scroll (1, HIWORD(wParam)); break;
1254 }
1255 break;
1256 case WM_PALETTECHANGED:
1257 if ((HWND) wParam != hwnd && pal != NULL) {
1258 HDC hdc = get_ctx();
1259 if (hdc) {
1260 if (RealizePalette (hdc) > 0)
1261 UpdateColors (hdc);
1262 free_ctx (hdc);
1263 }
1264 }
1265 break;
1266 case WM_QUERYNEWPALETTE:
1267 if (pal != NULL) {
1268 HDC hdc = get_ctx();
1269 if (hdc) {
1270 if (RealizePalette (hdc) > 0)
1271 UpdateColors (hdc);
1272 free_ctx (hdc);
1273 return TRUE;
1274 }
1275 }
1276 return FALSE;
1277 case WM_KEYDOWN:
1278 case WM_SYSKEYDOWN:
c9def1b8 1279 case WM_KEYUP:
1280 case WM_SYSKEYUP:
374330e2 1281 /*
1282 * Add the scan code and keypress timing to the random
1283 * number noise, if we're using ssh.
1284 */
1285 if (cfg.protocol == PROT_SSH)
1286 noise_ultralight(lParam);
1287
1288 /*
1289 * We don't do TranslateMessage since it disassociates the
1290 * resulting CHAR message from the KEYDOWN that sparked it,
1291 * which we occasionally don't want. Instead, we process
1292 * KEYDOWN, and call the Win32 translator functions so that
1293 * we get the translations under _our_ control.
1294 */
1295 {
1296 unsigned char buf[20];
1297 int len;
1298
c9def1b8 1299 len = TranslateKey (message, wParam, lParam, buf);
c5e9c988 1300 if (len == -1)
1301 return DefWindowProc (hwnd, message, wParam, lParam);
5bc238bb 1302 ldisc->send (buf, len);
374330e2 1303 }
1304 return 0;
1305 case WM_CHAR:
1306 case WM_SYSCHAR:
1307 /*
1308 * Nevertheless, we are prepared to deal with WM_CHAR
1309 * messages, should they crop up. So if someone wants to
1310 * post the things to us as part of a macro manoeuvre,
1311 * we're ready to cope.
1312 */
1313 {
14963b8f 1314 char c = xlat_kbd2tty((unsigned char)wParam);
5bc238bb 1315 ldisc->send (&c, 1);
374330e2 1316 }
1317 return 0;
1318 }
1319
1320 return DefWindowProc (hwnd, message, wParam, lParam);
1321}
1322
1323/*
1324 * Draw a line of text in the window, at given character
1325 * coordinates, in given attributes.
1326 *
1327 * We are allowed to fiddle with the contents of `text'.
1328 */
1329void do_text (Context ctx, int x, int y, char *text, int len,
c9def1b8 1330 unsigned long attr, int lattr) {
374330e2 1331 COLORREF fg, bg, t;
1332 int nfg, nbg, nfont;
1333 HDC hdc = ctx;
59ad2c03 1334 RECT line_box;
1335 int force_manual_underline = 0;
c9def1b8 1336 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
59ad2c03 1337static int *IpDx = 0, IpDxLEN = 0;;
1338
c9def1b8 1339 if (len>IpDxLEN || IpDx[0] != fnt_width) {
59ad2c03 1340 int i;
1341 if (len>IpDxLEN) {
1342 sfree(IpDx);
1343 IpDx = smalloc((len+16)*sizeof(int));
1344 IpDxLEN = (len+16);
1345 }
c9def1b8 1346 for(i=0; i<IpDxLEN; i++)
1347 IpDx[i] = fnt_width;
59ad2c03 1348 }
374330e2 1349
c9def1b8 1350 x *= fnt_width;
374330e2 1351 y *= font_height;
1352
1353 if (attr & ATTR_ACTCURS) {
c9def1b8 1354 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
374330e2 1355 attr ^= ATTR_CUR_XOR;
1356 }
1357
1358 nfont = 0;
1359 if (cfg.vtmode == VT_OEMONLY)
1360 nfont |= FONT_OEM;
1361
1362 /*
1363 * Map high-half characters in order to approximate ISO using
59ad2c03 1364 * OEM character set. No characters are missing if the OEM codepage
1365 * is CP850.
374330e2 1366 */
1367 if (nfont & FONT_OEM) {
1368 int i;
1369 for (i=0; i<len; i++)
1370 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
59ad2c03 1371#if 0
1372 /* This is CP850 ... perfect translation */
374330e2 1373 static const char oemhighhalf[] =
1374 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1375 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1376 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1377 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
59ad2c03 1378 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1379 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
374330e2 1380 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1381 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1382 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1383 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1384 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1385 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1386 ;
59ad2c03 1387#endif
1388 /* This is CP437 ... junk translation */
1389 static const unsigned char oemhighhalf[] = {
1390 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1391 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1392 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1393 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1394 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1395 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1396 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1397 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1398 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1399 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1400 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1401 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1402 };
1403
374330e2 1404 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1405 }
1406 }
1407
1408 if (attr & ATTR_GBCHR) {
1409 int i;
1410 /*
1411 * GB mapping: map # to pound, and everything else stays
1412 * normal.
1413 */
1414 for (i=0; i<len; i++)
1415 if (text[i] == '#')
1416 text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
1417 } else if (attr & ATTR_LINEDRW) {
1418 int i;
59ad2c03 1419 /* ISO 8859-1 */
374330e2 1420 static const char poorman[] =
1421 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
59ad2c03 1422
1423 /* CP437 */
1424 static const char oemmap_437[] =
ed91c385 1425 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1426 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
374330e2 1427
59ad2c03 1428 /* CP850 */
1429 static const char oemmap_850[] =
1430 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1431 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1432
1433 /* Poor windows font ... eg: windows courier */
1434 static const char oemmap[] =
1435 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1436 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1437
374330e2 1438 /*
1439 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1440 * VT100 line drawing chars; everything else stays normal.
1441 */
1442 switch (cfg.vtmode) {
1443 case VT_XWINDOWS:
1444 for (i=0; i<len; i++)
1445 if (text[i] >= '\x60' && text[i] <= '\x7E')
1446 text[i] += '\x01' - '\x60';
1447 break;
1448 case VT_OEMANSI:
59ad2c03 1449 /* Make sure we actually have an OEM font */
1450 if (fonts[nfont|FONT_OEM]) {
374330e2 1451 case VT_OEMONLY:
59ad2c03 1452 nfont |= FONT_OEM;
1453 for (i=0; i<len; i++)
1454 if (text[i] >= '\x60' && text[i] <= '\x7E')
1455 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1456 break;
1457 }
374330e2 1458 case VT_POORMAN:
1459 for (i=0; i<len; i++)
1460 if (text[i] >= '\x60' && text[i] <= '\x7E')
1461 text[i] = poorman[(unsigned char)text[i] - 0x60];
1462 break;
1463 }
1464 }
1465
1466 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1467 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1468 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1469 nfont |= FONT_BOLD;
1470 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1471 nfont |= FONT_UNDERLINE;
59ad2c03 1472 if (!fonts[nfont])
1473 {
1474 if (nfont&FONT_UNDERLINE)
1475 force_manual_underline = 1;
1476 /* Don't do the same for manual bold, it could be bad news. */
1477
1478 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1479 }
374330e2 1480 if (attr & ATTR_REVERSE) {
1481 t = nfg; nfg = nbg; nbg = t;
1482 }
1483 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1484 nfg++;
59ad2c03 1485 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1486 nbg++;
374330e2 1487 fg = colours[nfg];
1488 bg = colours[nbg];
1489 SelectObject (hdc, fonts[nfont]);
1490 SetTextColor (hdc, fg);
1491 SetBkColor (hdc, bg);
1492 SetBkMode (hdc, OPAQUE);
59ad2c03 1493 line_box.left = x;
1494 line_box.top = y;
c9def1b8 1495 line_box.right = x+fnt_width*len;
59ad2c03 1496 line_box.bottom = y+font_height;
1497 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
374330e2 1498 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1499 SetBkMode (hdc, TRANSPARENT);
59ad2c03 1500
1501 /* GRR: This draws the character outside it's box and can leave
1502 * 'droppings' even with the clip box! I suppose I could loop it
c9def1b8 1503 * one character at a time ... yuk.
1504 *
1505 * Or ... I could do a test print with "W", and use +1 or -1 for this
1506 * shift depending on if the leftmost column is blank...
1507 */
59ad2c03 1508 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
374330e2 1509 }
59ad2c03 1510 if (force_manual_underline ||
1511 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1a6f78fe 1512 HPEN oldpen;
1513 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
374330e2 1514 MoveToEx (hdc, x, y+descent, NULL);
c9def1b8 1515 LineTo (hdc, x+len*fnt_width, y+descent);
1a6f78fe 1516 oldpen = SelectObject (hdc, oldpen);
1517 DeleteObject (oldpen);
374330e2 1518 }
1519 if (attr & ATTR_PASCURS) {
1520 POINT pts[5];
1a6f78fe 1521 HPEN oldpen;
374330e2 1522 pts[0].x = pts[1].x = pts[4].x = x;
c9def1b8 1523 pts[2].x = pts[3].x = x+fnt_width-1;
374330e2 1524 pts[0].y = pts[3].y = pts[4].y = y;
1525 pts[1].y = pts[2].y = y+font_height-1;
1a6f78fe 1526 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
374330e2 1527 Polyline (hdc, pts, 5);
1a6f78fe 1528 oldpen = SelectObject (hdc, oldpen);
1529 DeleteObject (oldpen);
374330e2 1530 }
1531}
1532
c9def1b8 1533static int check_compose(int first, int second) {
1534
1535 static char * composetbl[] = {
1536