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