Remove the hierarchical dialog structure
[u/mdw/putty] / window.c
Content-type: text/html git.distorted.org.uk Git - u/mdw/putty/blame - window.c


500 - Internal Server Error

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