Deletion case 2c can shift the root; case 3b is not the only case that
[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 3900.
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
374330e2 135 do_defaults(NULL);
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 == '@') {
193 do_defaults (p+1);
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;
418 char msg[1024];
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;
5ac36532 428 sprintf(msg, "%s - PuTTY", realhost);
374330e2 429 set_title (msg);
430 set_icon (msg);
431 }
432
d85548fe 433 session_closed = FALSE;
434
374330e2 435 /*
436 * Set up the input and output buffers.
437 */
c9def1b8 438 inbuf_head = 0;
374330e2 439 outbuf_reap = outbuf_head = 0;
440
441 /*
442 * Prepare the mouse handler.
443 */
444 lastact = MA_NOTHING;
445 lastbtn = MB_NOTHING;
446 dbltime = GetDoubleClickTime();
447
448 /*
449 * Set up the session-control options on the system menu.
450 */
451 {
452 HMENU m = GetSystemMenu (hwnd, FALSE);
0a4aa984 453 HMENU p,s;
454 int i;
374330e2 455
456 AppendMenu (m, MF_SEPARATOR, 0, 0);
457 if (cfg.protocol == PROT_TELNET) {
458 p = CreateMenu();
459 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
460 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
461 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
462 AppendMenu (p, MF_SEPARATOR, 0, 0);
463 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
464 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
465 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
466 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
467 AppendMenu (p, MF_SEPARATOR, 0, 0);
468 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
469 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
470 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
471 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
472 AppendMenu (p, MF_SEPARATOR, 0, 0);
473 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
474 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
475 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
374330e2 476 AppendMenu (m, MF_SEPARATOR, 0, 0);
477 }
45c4ea18 478 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
c5e9c988 479 AppendMenu (m, MF_SEPARATOR, 0, 0);
45c4ea18 480 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
481 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
0a4aa984 482 s = CreateMenu();
483 get_sesslist(TRUE);
484 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
485 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
45c4ea18 486 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
487 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
374330e2 488 AppendMenu (m, MF_SEPARATOR, 0, 0);
45c4ea18 489 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
490 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
374330e2 491 AppendMenu (m, MF_SEPARATOR, 0, 0);
45c4ea18 492 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
374330e2 493 }
494
495 /*
496 * Finally show the window!
497 */
498 ShowWindow (hwnd, show);
499
500 /*
501 * Set the palette up.
502 */
503 pal = NULL;
504 logpal = NULL;
505 init_palette();
506
507 has_focus = (GetForegroundWindow() == hwnd);
508 UpdateWindow (hwnd);
509
59ad2c03 510 {
511 int timer_id = 0, long_timer = 0;
512
513 while (GetMessage (&msg, NULL, 0, 0) == 1) {
514 /* Sometimes DispatchMessage calls routines that use their own
515 * GetMessage loop, setup this timer so we get some control back.
516 *
517 * Also call term_update() from the timer so that if the host
518 * is sending data flat out we still do redraws.
519 */
520 if(timer_id && long_timer) {
521 KillTimer(hwnd, timer_id);
522 long_timer = timer_id = 0;
523 }
524 if(!timer_id)
525 timer_id = SetTimer(hwnd, 1, 20, NULL);
526 DispatchMessage (&msg);
527
528 /* This is too fast, but I'll leave it for now 'cause it shows
529 * how often term_update is called (far too often at times!)
530 */
531 term_blink(0);
532
c9def1b8 533 /* Send the paste buffer if there's anything to send */
534 term_paste();
535
59ad2c03 536 /* If there's nothing new in the queue then we can do everything
537 * we've delayed, reading the socket, writing, and repainting
538 * the window.
539 */
540 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
541 if (pending_netevent) {
542 enact_pending_netevent();
543
544 term_blink(1);
545 }
546 } else continue;
547 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
548 if (timer_id) {
549 KillTimer(hwnd, timer_id);
550 timer_id = 0;
551 }
934c0b7a 552 HideCaret(hwnd);
c9def1b8 553 if (inbuf_head)
59ad2c03 554 term_out();
555 term_update();
934c0b7a 556 ShowCaret(hwnd);
c9def1b8 557 if (!has_focus)
558 timer_id = SetTimer(hwnd, 1, 2000, NULL);
559 else if (cfg.blinktext)
560 timer_id = SetTimer(hwnd, 1, 250, NULL);
561 else
562 timer_id = SetTimer(hwnd, 1, 500, NULL);
59ad2c03 563 long_timer = 1;
564 }
565 }
374330e2 566 }
567
568 /*
569 * Clean up.
570 */
571 {
572 int i;
573 for (i=0; i<8; i++)
574 if (fonts[i])
575 DeleteObject(fonts[i]);
576 }
577 sfree(logpal);
578 if (pal)
579 DeleteObject(pal);
580 WSACleanup();
581
8f203108 582 if (cfg.protocol == PROT_SSH) {
374330e2 583 random_save_seed();
8f203108 584#ifdef MSCRYPTOAPI
585 crypto_wrapup();
586#endif
587 }
374330e2 588
589 return msg.wParam;
590}
591
592/*
8d5de777 593 * Print a message box and close the connection.
594 */
595void connection_fatal(char *fmt, ...) {
596 va_list ap;
597 char stuff[200];
598
599 va_start(ap, fmt);
600 vsprintf(stuff, fmt, ap);
601 va_end(ap);
602 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
603 if (cfg.close_on_exit)
604 PostQuitMessage(1);
605 else {
606 session_closed = TRUE;
607 SetWindowText (hwnd, "PuTTY (inactive)");
608 }
609}
610
611/*
59ad2c03 612 * Actually do the job requested by a WM_NETEVENT
613 */
614static void enact_pending_netevent(void) {
615 int i;
9dde0b46 616 static int reentering = 0;
617
618 if (reentering)
619 return; /* don't unpend the pending */
620
59ad2c03 621 pending_netevent = FALSE;
9dde0b46 622
623 reentering = 1;
59ad2c03 624 i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
9dde0b46 625 reentering = 0;
59ad2c03 626
627 if (i < 0) {
628 char buf[1024];
629 switch (WSABASEERR + (-i) % 10000) {
630 case WSAECONNRESET:
631 sprintf(buf, "Connection reset by peer");
632 break;
633 default:
634 sprintf(buf, "Unexpected network error %d", -i);
635 break;
636 }
8d5de777 637 connection_fatal(buf);
638 }
639 if (i <= 0) {
59ad2c03 640 if (cfg.close_on_exit)
641 PostQuitMessage(0);
642 else {
643 session_closed = TRUE;
644 MessageBox(hwnd, "Connection closed by remote host",
645 "PuTTY", MB_OK | MB_ICONINFORMATION);
646 SetWindowText (hwnd, "PuTTY (inactive)");
647 }
648 }
649}
650
651/*
374330e2 652 * Copy the colour palette from the configuration data into defpal.
653 * This is non-trivial because the colour indices are different.
654 */
655static void cfgtopalette(void) {
656 int i;
657 static const int ww[] = {
658 6, 7, 8, 9, 10, 11, 12, 13,
659 14, 15, 16, 17, 18, 19, 20, 21,
660 0, 1, 2, 3, 4, 4, 5, 5
661 };
662
663 for (i=0; i<24; i++) {
664 int w = ww[i];
665 defpal[i].rgbtRed = cfg.colours[w][0];
666 defpal[i].rgbtGreen = cfg.colours[w][1];
667 defpal[i].rgbtBlue = cfg.colours[w][2];
668 }
669}
670
671/*
672 * Set up the colour palette.
673 */
674static void init_palette(void) {
675 int i;
676 HDC hdc = GetDC (hwnd);
677 if (hdc) {
678 if (cfg.try_palette &&
679 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
680 logpal = smalloc(sizeof(*logpal)
681 - sizeof(logpal->palPalEntry)
682 + NCOLOURS * sizeof(PALETTEENTRY));
683 logpal->palVersion = 0x300;
684 logpal->palNumEntries = NCOLOURS;
685 for (i = 0; i < NCOLOURS; i++) {
686 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
687 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
688 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
689 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
690 }
691 pal = CreatePalette (logpal);
692 if (pal) {
693 SelectPalette (hdc, pal, FALSE);
694 RealizePalette (hdc);
695 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
696 FALSE);
697 }
698 }
699 ReleaseDC (hwnd, hdc);
700 }
701 if (pal)
702 for (i=0; i<NCOLOURS; i++)
703 colours[i] = PALETTERGB(defpal[i].rgbtRed,
704 defpal[i].rgbtGreen,
705 defpal[i].rgbtBlue);
706 else
707 for(i=0; i<NCOLOURS; i++)
708 colours[i] = RGB(defpal[i].rgbtRed,
709 defpal[i].rgbtGreen,
710 defpal[i].rgbtBlue);
711}
712
713/*
714 * Initialise all the fonts we will need. There may be as many as
715 * eight or as few as one. We also:
716 *
717 * - check the font width and height, correcting our guesses if
718 * necessary.
719 *
720 * - verify that the bold font is the same width as the ordinary
721 * one, and engage shadow bolding if not.
722 *
723 * - verify that the underlined font is the same width as the
724 * ordinary one (manual underlining by means of line drawing can
725 * be done in a pinch).
374330e2 726 */
59ad2c03 727static void init_fonts(int pick_width) {
374330e2 728 TEXTMETRIC tm;
97fc891e 729 int i;
59ad2c03 730 int fsize[8];
374330e2 731 HDC hdc;
732 int fw_dontcare, fw_bold;
97fc891e 733 int firstchar = ' ';
374330e2 734
59ad2c03 735#ifdef CHECKOEMFONT
97fc891e 736font_messup:
59ad2c03 737#endif
374330e2 738 for (i=0; i<8; i++)
739 fonts[i] = NULL;
740
741 if (cfg.fontisbold) {
742 fw_dontcare = FW_BOLD;
743 fw_bold = FW_BLACK;
744 } else {
745 fw_dontcare = FW_DONTCARE;
746 fw_bold = FW_BOLD;
747 }
748
97fc891e 749 hdc = GetDC(hwnd);
750
751 font_height = cfg.fontheight;
59ad2c03 752 font_width = pick_width;
97fc891e 753
374330e2 754#define f(i,c,w,u) \
97fc891e 755 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
374330e2 756 c, OUT_DEFAULT_PRECIS, \
757 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
758 FIXED_PITCH | FF_DONTCARE, cfg.font)
97fc891e 759
374330e2 760 if (cfg.vtmode != VT_OEMONLY) {
14963b8f 761 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
97fc891e 762
763 SelectObject (hdc, fonts[FONT_NORMAL]);
764 GetTextMetrics(hdc, &tm);
765 font_height = tm.tmHeight;
766 font_width = tm.tmAveCharWidth;
767
14963b8f 768 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
97fc891e 769
09798031 770 /*
771 * Some fonts, e.g. 9-pt Courier, draw their underlines
772 * outside their character cell. We successfully prevent
773 * screen corruption by clipping the text output, but then
774 * we lose the underline completely. Here we try to work
775 * out whether this is such a font, and if it is, we set a
776 * flag that causes underlines to be drawn by hand.
777 *
778 * Having tried other more sophisticated approaches (such
779 * as examining the TEXTMETRIC structure or requesting the
780 * height of a string), I think we'll do this the brute
781 * force way: we create a small bitmap, draw an underlined
782 * space on it, and test to see whether any pixels are
783 * foreground-coloured. (Since we expect the underline to
784 * go all the way across the character cell, we only search
785 * down a single column of the bitmap, half way across.)
786 */
787 {
788 HDC und_dc;
789 HBITMAP und_bm, und_oldbm;
790 int i, gotit;
791 COLORREF c;
792
793 und_dc = CreateCompatibleDC(hdc);
794 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
795 und_oldbm = SelectObject(und_dc, und_bm);
796 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
797 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
798 SetTextColor (und_dc, RGB(255,255,255));
799 SetBkColor (und_dc, RGB(0,0,0));
800 SetBkMode (und_dc, OPAQUE);
801 ExtTextOut (und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
802 gotit = FALSE;
803 for (i = 0; i < font_height; i++) {
804 c = GetPixel(und_dc, font_width/2, i);
805 if (c != RGB(0,0,0))
806 gotit = TRUE;
807 }
808 SelectObject(und_dc, und_oldbm);
809 DeleteObject(und_bm);
810 DeleteDC(und_dc);
811 font_needs_hand_underlining = !gotit;
812 }
813
97fc891e 814 if (bold_mode == BOLD_FONT) {
14963b8f 815 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
816 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
374330e2 817 }
97fc891e 818
819 if (cfg.vtmode == VT_OEMANSI) {
820 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
821 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
822
823 if (bold_mode == BOLD_FONT) {
824 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
825 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
826 }
827 }
828 }
829 else
830 {
831 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
832
833 SelectObject (hdc, fonts[FONT_OEM]);
834 GetTextMetrics(hdc, &tm);
835 font_height = tm.tmHeight;
836 font_width = tm.tmAveCharWidth;
837
838 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
839
840 if (bold_mode == BOLD_FONT) {
841 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
842 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
374330e2 843 }
374330e2 844 }
845#undef f
846
97fc891e 847 descent = tm.tmAscent + 1;
848 if (descent >= font_height)
849 descent = font_height - 1;
850 firstchar = tm.tmFirstChar;
374330e2 851
97fc891e 852 for (i=0; i<8; i++) {
853 if (fonts[i]) {
59ad2c03 854 if (SelectObject (hdc, fonts[i]) &&
855 GetTextMetrics(hdc, &tm) )
856 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
857 else fsize[i] = -i;
374330e2 858 }
59ad2c03 859 else fsize[i] = -i;
374330e2 860 }
861
862 ReleaseDC (hwnd, hdc);
863
59ad2c03 864 /* ... This is wrong in OEM only mode */
97fc891e 865 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
374330e2 866 (bold_mode == BOLD_FONT &&
97fc891e 867 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
374330e2 868 und_mode = UND_LINE;
869 DeleteObject (fonts[FONT_UNDERLINE]);
870 if (bold_mode == BOLD_FONT)
871 DeleteObject (fonts[FONT_BOLDUND]);
872 }
873
874 if (bold_mode == BOLD_FONT &&
97fc891e 875 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
374330e2 876 bold_mode = BOLD_SHADOW;
877 DeleteObject (fonts[FONT_BOLD]);
878 if (und_mode == UND_FONT)
879 DeleteObject (fonts[FONT_BOLDUND]);
880 }
881
59ad2c03 882#ifdef CHECKOEMFONT
3cfb9f1c 883 /* With the fascist font painting it doesn't matter if the linedraw font
59ad2c03 884 * isn't exactly the right size anymore so we don't have to check this.
885 */
97fc891e 886 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
887 if( cfg.fontcharset == OEM_CHARSET )
888 {
889 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
374330e2 890 "different sizes. Using OEM-only mode instead",
891 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
97fc891e 892 cfg.vtmode = VT_OEMONLY;
893 }
894 else if( firstchar < ' ' )
895 {
896 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
897 "different sizes. Using XTerm mode instead",
898 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
899 cfg.vtmode = VT_XWINDOWS;
900 }
901 else
902 {
903 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
904 "different sizes. Using ISO8859-1 mode instead",
905 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
906 cfg.vtmode = VT_POORMAN;
907 }
908
909 for (i=0; i<8; i++)
374330e2 910 if (fonts[i])
911 DeleteObject (fonts[i]);
97fc891e 912 goto font_messup;
374330e2 913 }
59ad2c03 914#endif
374330e2 915}
916
59ad2c03 917void request_resize (int w, int h, int refont) {
918 int width, height;
c9def1b8 919
920 /* If the window is maximized supress resizing attempts */
921 if(IsZoomed(hwnd)) return;
59ad2c03 922
923#ifdef CHECKOEMFONT
924 /* Don't do this in OEMANSI, you may get disable messages */
925 if (refont && w != cols && (cols==80 || cols==132)
926 && cfg.vtmode != VT_OEMANSI)
927#else
928 if (refont && w != cols && (cols==80 || cols==132))
929#endif
930 {
931 /* If font width too big for screen should we shrink the font more ? */
932 if (w==132)
933 font_width = ((font_width*cols+w/2)/w);
934 else
935 font_width = 0;
936 {
937 int i;
938 for (i=0; i<8; i++)
939 if (fonts[i])
940 DeleteObject(fonts[i]);
941 }
942 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
943 und_mode = UND_FONT;
944 init_fonts(font_width);
945 }
c9def1b8 946 else
947 {
948 static int first_time = 1;
949 static RECT ss;
950
951 switch(first_time)
952 {
953 case 1:
954 /* Get the size of the screen */
955 if (GetClientRect(GetDesktopWindow(),&ss))
956 /* first_time = 0 */;
957 else { first_time = 2; break; }
958 case 0:
959 /* Make sure the values are sane */
960 width = (ss.right-ss.left-extra_width ) / font_width;
961 height = (ss.bottom-ss.top-extra_height ) / font_height;
962
963 if (w>width) w=width;
964 if (h>height) h=height;
965 if (w<15) w = 15;
966 if (h<1) w = 1;
967 }
968 }
59ad2c03 969
970 width = extra_width + font_width * w;
971 height = extra_height + font_height * h;
374330e2 972
973 SetWindowPos (hwnd, NULL, 0, 0, width, height,
974 SWP_NOACTIVATE | SWP_NOCOPYBITS |
975 SWP_NOMOVE | SWP_NOZORDER);
976}
977
978static void click (Mouse_Button b, int x, int y) {
fdedf2c8 979 int thistime = GetMessageTime();
980
981 if (lastbtn == b && thistime - lasttime < dbltime) {
374330e2 982 lastact = (lastact == MA_CLICK ? MA_2CLK :
983 lastact == MA_2CLK ? MA_3CLK :
984 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
985 } else {
986 lastbtn = b;
987 lastact = MA_CLICK;
988 }
989 if (lastact != MA_NOTHING)
990 term_mouse (b, lastact, x, y);
fdedf2c8 991 lasttime = thistime;
374330e2 992}
993
996c8c3b 994static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
995 WPARAM wParam, LPARAM lParam) {
374330e2 996 HDC hdc;
997 static int ignore_size = FALSE;
998 static int ignore_clip = FALSE;
999 static int just_reconfigged = FALSE;
ffc31afe 1000 static int resizing = FALSE;
374330e2 1001
1002 switch (message) {
59ad2c03 1003 case WM_TIMER:
1004 if (pending_netevent)
1005 enact_pending_netevent();
c9def1b8 1006 if (inbuf_head)
59ad2c03 1007 term_out();
934c0b7a 1008 HideCaret(hwnd);
59ad2c03 1009 term_update();
934c0b7a 1010 ShowCaret(hwnd);
59ad2c03 1011 return 0;
374330e2 1012 case WM_CREATE:
1013 break;
68130d34 1014 case WM_CLOSE:
d85548fe 1015 if (!cfg.warn_on_close || session_closed ||
9ef49106 1016 MessageBox(hwnd, "Are you sure you want to close this session?",
68130d34 1017 "PuTTY Exit Confirmation",
1018 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1019 DestroyWindow(hwnd);
1020 return 0;
374330e2 1021 case WM_DESTROY:
1022 PostQuitMessage (0);
1023 return 0;
6833a413 1024 case WM_SYSCOMMAND:
1025 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
374330e2 1026 case IDM_SHOWLOG:
c5e9c988 1027 showeventlog(hwnd);
374330e2 1028 break;
1029 case IDM_NEWSESS:
1030 case IDM_DUPSESS:
6833a413 1031 case IDM_SAVEDSESS:
374330e2 1032 {
1033 char b[2048];
1034 char c[30], *cl;
e4e4cc7e 1035 int freecl = FALSE;
374330e2 1036 STARTUPINFO si;
1037 PROCESS_INFORMATION pi;
1038 HANDLE filemap = NULL;
1039
1040 if (wParam == IDM_DUPSESS) {
1041 /*
1042 * Allocate a file-mapping memory chunk for the
1043 * config structure.
1044 */
1045 SECURITY_ATTRIBUTES sa;
1046 Config *p;
1047
1048 sa.nLength = sizeof(sa);
1049 sa.lpSecurityDescriptor = NULL;
1050 sa.bInheritHandle = TRUE;
1051 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
1052 &sa,
1053 PAGE_READWRITE,
1054 0,
1055 sizeof(Config),
1056 NULL);
1057 if (filemap) {
1058 p = (Config *)MapViewOfFile(filemap,
1059 FILE_MAP_WRITE,
1060 0, 0, sizeof(Config));
1061 if (p) {
1062 *p = cfg; /* structure copy */
1063 UnmapViewOfFile(p);
1064 }
1065 }
1d470ad2 1066 sprintf(c, "putty &%p", filemap);
374330e2 1067 cl = c;
0a4aa984 1068 } else if (wParam == IDM_SAVEDSESS) {
e4e4cc7e 1069 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
1070 cl = malloc(16 + strlen(session)); /* 8, but play safe */
1071 if (!cl)
1072 cl = NULL; /* not a very important failure mode */
94e6450e 1073 else {
1074 sprintf(cl, "putty @%s", session);
1075 freecl = TRUE;
1076 }
374330e2 1077 } else
6833a413 1078 cl = NULL;
374330e2 1079
1080 GetModuleFileName (NULL, b, sizeof(b)-1);
1081 si.cb = sizeof(si);
1082 si.lpReserved = NULL;
1083 si.lpDesktop = NULL;
1084 si.lpTitle = NULL;
1085 si.dwFlags = 0;
1086 si.cbReserved2 = 0;
1087 si.lpReserved2 = NULL;
1088 CreateProcess (b, cl, NULL, NULL, TRUE,
1089 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1090
1091 if (filemap)
1092 CloseHandle(filemap);
e4e4cc7e 1093 if (freecl)
1094 free(cl);
374330e2 1095 }
1096 break;
1097 case IDM_RECONF:
1098 if (!do_reconfig(hwnd))
1099 break;
1100 just_reconfigged = TRUE;
1101 {
1102 int i;
1103 for (i=0; i<8; i++)
1104 if (fonts[i])
1105 DeleteObject(fonts[i]);
1106 }
1107 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1108 und_mode = UND_FONT;
59ad2c03 1109 init_fonts(0);
374330e2 1110 sfree(logpal);
59ad2c03 1111 /* Telnet will change local echo -> remote if the remote asks */
1112 if (cfg.protocol != PROT_TELNET)
1113 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
374330e2 1114 if (pal)
1115 DeleteObject(pal);
1116 logpal = NULL;
1117 pal = NULL;
1118 cfgtopalette();
1119 init_palette();
c9def1b8 1120
1121 /* Enable or disable the scroll bar, etc */
1122 {
1123 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1124
1125 nflg = flag;
1126 if (cfg.scrollbar) nflg |= WS_VSCROLL;
1127 else nflg &= ~WS_VSCROLL;
1128 if (cfg.locksize)
1129 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1130 else
1131 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1132
1133 if (nflg != flag)
1134 {
1135 RECT cr, wr;
1136
1137 SetWindowLong(hwnd, GWL_STYLE, nflg);
1138 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1139 SetWindowPos(hwnd, NULL, 0,0,0,0,
1140 SWP_NOACTIVATE|SWP_NOCOPYBITS|
1141 SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER|
1142 SWP_FRAMECHANGED);
1143
1144 GetWindowRect (hwnd, &wr);
1145 GetClientRect (hwnd, &cr);
1146 extra_width = wr.right - wr.left - cr.right + cr.left;
1147 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1148 }
1149 }
1150
374330e2 1151 term_size(cfg.height, cfg.width, cfg.savelines);
1152 InvalidateRect(hwnd, NULL, TRUE);
1153 SetWindowPos (hwnd, NULL, 0, 0,
1154 extra_width + font_width * cfg.width,
1155 extra_height + font_height * cfg.height,
1156 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1157 SWP_NOMOVE | SWP_NOZORDER);
1158 if (IsIconic(hwnd)) {
1159 SetWindowText (hwnd,
1160 cfg.win_name_always ? window_name : icon_name);
1161 }
1162 break;
1163 case IDM_CLRSB:
1164 term_clrsb();
1165 break;
1166 case IDM_RESET:
1167 term_pwron();
1168 break;
1169 case IDM_TEL_AYT: back->special (TS_AYT); break;
1170 case IDM_TEL_BRK: back->special (TS_BRK); break;
1171 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1172 case IDM_TEL_EC: back->special (TS_EC); break;
1173 case IDM_TEL_EL: back->special (TS_EL); break;
1174 case IDM_TEL_GA: back->special (TS_GA); break;
1175 case IDM_TEL_NOP: back->special (TS_NOP); break;
1176 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1177 case IDM_TEL_AO: back->special (TS_AO); break;
1178 case IDM_TEL_IP: back->special (TS_IP); break;
1179 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1180 case IDM_TEL_EOR: back->special (TS_EOR); break;
1181 case IDM_TEL_EOF: back->special (TS_EOF); break;
1182 case IDM_ABOUT:
1183 showabout (hwnd);
1184 break;
0a4aa984 1185 default:
1186 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1187 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1188 }
374330e2 1189 }
1190 break;
37508af4 1191
1192#define X_POS(l) ((int)(short)LOWORD(l))
1193#define Y_POS(l) ((int)(short)HIWORD(l))
1194
fdedf2c8 1195#define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1196#define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1197
374330e2 1198 case WM_LBUTTONDOWN:
fdedf2c8 1199 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1200 TO_CHR_Y(Y_POS(lParam)));
fef97f43 1201 SetCapture(hwnd);
374330e2 1202 return 0;
1203 case WM_LBUTTONUP:
fdedf2c8 1204 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1205 TO_CHR_Y(Y_POS(lParam)));
37508af4 1206 ReleaseCapture();
374330e2 1207 return 0;
1208 case WM_MBUTTONDOWN:
37508af4 1209 SetCapture(hwnd);
374330e2 1210 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
fdedf2c8 1211 TO_CHR_X(X_POS(lParam)),
1212 TO_CHR_Y(Y_POS(lParam)));
374330e2 1213 return 0;
1214 case WM_MBUTTONUP:
1215 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
fdedf2c8 1216 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1217 TO_CHR_Y(Y_POS(lParam)));
37508af4 1218 ReleaseCapture();
a0b1cefc 1219 return 0;
374330e2 1220 case WM_RBUTTONDOWN:
37508af4 1221 SetCapture(hwnd);
374330e2 1222 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
fdedf2c8 1223 TO_CHR_X(X_POS(lParam)),
1224 TO_CHR_Y(Y_POS(lParam)));
374330e2 1225 return 0;
1226 case WM_RBUTTONUP:
1227 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
fdedf2c8 1228 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1229 TO_CHR_Y(Y_POS(lParam)));
37508af4 1230 ReleaseCapture();
374330e2 1231 return 0;
1232 case WM_MOUSEMOVE:
1233 /*
1234 * Add the mouse position and message time to the random
1235 * number noise, if we're using ssh.
1236 */
1237 if (cfg.protocol == PROT_SSH)
1238 noise_ultralight(lParam);
1239
1240 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1241 Mouse_Button b;
1242 if (wParam & MK_LBUTTON)
1243 b = MB_SELECT;
1244 else if (wParam & MK_MBUTTON)
1245 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1246 else
1247 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
fdedf2c8 1248 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1249 TO_CHR_Y(Y_POS(lParam)));
374330e2 1250 }
374330e2 1251 return 0;
1252 case WM_IGNORE_CLIP:
1253 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1254 break;
1255 case WM_DESTROYCLIPBOARD:
1256 if (!ignore_clip)
1257 term_deselect();
1258 ignore_clip = FALSE;
1259 return 0;
1260 case WM_PAINT:
1261 {
1262 PAINTSTRUCT p;
934c0b7a 1263 HideCaret(hwnd);
374330e2 1264 hdc = BeginPaint (hwnd, &p);
1265 if (pal) {
1266 SelectPalette (hdc, pal, TRUE);
1267 RealizePalette (hdc);
1268 }
1269 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1270 p.rcPaint.right, p.rcPaint.bottom);
1271 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1272 SelectObject (hdc, GetStockObject(WHITE_PEN));
1273 EndPaint (hwnd, &p);
934c0b7a 1274 ShowCaret(hwnd);
374330e2 1275 }
1276 return 0;
1277 case WM_NETEVENT:
59ad2c03 1278 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1279 * but the only one that's likely to try to overload us is FD_READ.
1280 * This means buffering just one is fine.
1281 */
1282 if (pending_netevent)
1283 enact_pending_netevent();
1284
1285 pending_netevent = TRUE;
1286 pend_netevent_wParam=wParam;
1287 pend_netevent_lParam=lParam;
374330e2 1288 return 0;
1289 case WM_SETFOCUS:
1290 has_focus = TRUE;
934c0b7a 1291 CreateCaret(hwnd, caretbm, 0, 0);
1292 ShowCaret(hwnd);
374330e2 1293 term_out();
1294 term_update();
1295 break;
1296 case WM_KILLFOCUS:
1297 has_focus = FALSE;
ec8679e9 1298 DestroyCaret();
374330e2 1299 term_out();
1300 term_update();
1301 break;
1302 case WM_IGNORE_SIZE:
1303 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1304 break;
73251d5d 1305 case WM_ENTERSIZEMOVE:
996c8c3b 1306 EnableSizeTip(1);
ffc31afe 1307 resizing = TRUE;
996c8c3b 1308 break;
73251d5d 1309 case WM_EXITSIZEMOVE:
996c8c3b 1310 EnableSizeTip(0);
ffc31afe 1311 resizing = FALSE;
1312 back->size();
996c8c3b 1313 break;
374330e2 1314 case WM_SIZING:
1315 {
1316 int width, height, w, h, ew, eh;
1317 LPRECT r = (LPRECT)lParam;
1318
1319 width = r->right - r->left - extra_width;
1320 height = r->bottom - r->top - extra_height;
1321 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1322 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
996c8c3b 1323 UpdateSizeTip(hwnd, w, h);
374330e2 1324 ew = width - w * font_width;
1325 eh = height - h * font_height;
1326 if (ew != 0) {
1327 if (wParam == WMSZ_LEFT ||
1328 wParam == WMSZ_BOTTOMLEFT ||
1329 wParam == WMSZ_TOPLEFT)
1330 r->left += ew;
1331 else
1332 r->right -= ew;
1333 }
1334 if (eh != 0) {
1335 if (wParam == WMSZ_TOP ||
1336 wParam == WMSZ_TOPRIGHT ||
1337 wParam == WMSZ_TOPLEFT)
1338 r->top += eh;
1339 else
1340 r->bottom -= eh;
1341 }
1342 if (ew || eh)
1343 return 1;
1344 else
1345 return 0;
1346 }
996c8c3b 1347 /* break; (never reached) */
374330e2 1348 case WM_SIZE:
1349 if (wParam == SIZE_MINIMIZED) {
1350 SetWindowText (hwnd,
1351 cfg.win_name_always ? window_name : icon_name);
1352 break;
1353 }
1354 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1355 SetWindowText (hwnd, window_name);
1356 if (!ignore_size) {
1a6f78fe 1357 int width, height, w, h;
1358#if 0 /* we have fixed this using WM_SIZING now */
1359 int ew, eh;
1360#endif
374330e2 1361
1362 width = LOWORD(lParam);
1363 height = HIWORD(lParam);
1364 w = width / font_width; if (w < 1) w = 1;
1365 h = height / font_height; if (h < 1) h = 1;
1366#if 0 /* we have fixed this using WM_SIZING now */
1367 ew = width - w * font_width;
1368 eh = height - h * font_height;
1369 if (ew != 0 || eh != 0) {
1370 RECT r;
1371 GetWindowRect (hwnd, &r);
1372 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1373 SetWindowPos (hwnd, NULL, 0, 0,
1374 r.right - r.left - ew, r.bottom - r.top - eh,
1375 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1376 }
1377#endif
1378 if (w != cols || h != rows || just_reconfigged) {
1379 term_invalidate();
1380 term_size (h, w, cfg.savelines);
ffc31afe 1381 /*
1382 * Don't call back->size in mid-resize. (To prevent
1383 * massive numbers of resize events getting sent
1384 * down the connection during an NT opaque drag.)
1385 */
1386 if (!resizing)
1387 back->size();
374330e2 1388 just_reconfigged = FALSE;
1389 }
1390 }
1391 ignore_size = FALSE;
1392 return 0;
1393 case WM_VSCROLL:
1394 switch (LOWORD(wParam)) {
1395 case SB_BOTTOM: term_scroll(-1, 0); break;
1396 case SB_TOP: term_scroll(+1, 0); break;
1397 case SB_LINEDOWN: term_scroll (0, +1); break;
1398 case SB_LINEUP: term_scroll (0, -1); break;
1399 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1400 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1401 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1402 term_scroll (1, HIWORD(wParam)); break;
1403 }
1404 break;
1405 case WM_PALETTECHANGED:
1406 if ((HWND) wParam != hwnd && pal != NULL) {
1407 HDC hdc = get_ctx();
1408 if (hdc) {
1409 if (RealizePalette (hdc) > 0)
1410 UpdateColors (hdc);
1411 free_ctx (hdc);
1412 }
1413 }
1414 break;
1415 case WM_QUERYNEWPALETTE:
1416 if (pal != NULL) {
1417 HDC hdc = get_ctx();
1418 if (hdc) {
1419 if (RealizePalette (hdc) > 0)
1420 UpdateColors (hdc);
1421 free_ctx (hdc);
1422 return TRUE;
1423 }
1424 }
1425 return FALSE;
1426 case WM_KEYDOWN:
1427 case WM_SYSKEYDOWN:
c9def1b8 1428 case WM_KEYUP:
1429 case WM_SYSKEYUP:
374330e2 1430 /*
1431 * Add the scan code and keypress timing to the random
1432 * number noise, if we're using ssh.
1433 */
1434 if (cfg.protocol == PROT_SSH)
1435 noise_ultralight(lParam);
1436
1437 /*
1438 * We don't do TranslateMessage since it disassociates the
1439 * resulting CHAR message from the KEYDOWN that sparked it,
1440 * which we occasionally don't want. Instead, we process
1441 * KEYDOWN, and call the Win32 translator functions so that
1442 * we get the translations under _our_ control.
1443 */
1444 {
1445 unsigned char buf[20];
1446 int len;
1447
c9def1b8 1448 len = TranslateKey (message, wParam, lParam, buf);
c5e9c988 1449 if (len == -1)
1450 return DefWindowProc (hwnd, message, wParam, lParam);
5bc238bb 1451 ldisc->send (buf, len);
374330e2 1452 }
1453 return 0;
1454 case WM_CHAR:
1455 case WM_SYSCHAR:
1456 /*
1457 * Nevertheless, we are prepared to deal with WM_CHAR
1458 * messages, should they crop up. So if someone wants to
1459 * post the things to us as part of a macro manoeuvre,
1460 * we're ready to cope.
1461 */
1462 {
14963b8f 1463 char c = xlat_kbd2tty((unsigned char)wParam);
5bc238bb 1464 ldisc->send (&c, 1);
374330e2 1465 }
1466 return 0;
1467 }
1468
1469 return DefWindowProc (hwnd, message, wParam, lParam);
1470}
1471
1472/*
ec8679e9 1473 * Move the system caret. (We maintain one, even though it's
1474 * invisible, for the benefit of blind people: apparently some
1475 * helper software tracks the system caret, so we should arrange to
1476 * have one.)
1477 */
1478void sys_cursor(int x, int y) {
1479 SetCaretPos(x * font_width, y * font_height);
1480}
1481
1482/*
374330e2 1483 * Draw a line of text in the window, at given character
1484 * coordinates, in given attributes.
1485 *
1486 * We are allowed to fiddle with the contents of `text'.
1487 */
1488void do_text (Context ctx, int x, int y, char *text, int len,
c9def1b8 1489 unsigned long attr, int lattr) {
374330e2 1490 COLORREF fg, bg, t;
1491 int nfg, nbg, nfont;
1492 HDC hdc = ctx;
59ad2c03 1493 RECT line_box;
1494 int force_manual_underline = 0;
c9def1b8 1495 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
09798031 1496 static int *IpDx = 0, IpDxLEN = 0;;
59ad2c03 1497
c9def1b8 1498 if (len>IpDxLEN || IpDx[0] != fnt_width) {
59ad2c03 1499 int i;
1500 if (len>IpDxLEN) {
1501 sfree(IpDx);
1502 IpDx = smalloc((len+16)*sizeof(int));
1503 IpDxLEN = (len+16);
1504 }
c9def1b8 1505 for(i=0; i<IpDxLEN; i++)
1506 IpDx[i] = fnt_width;
59ad2c03 1507 }
374330e2 1508
c9def1b8 1509 x *= fnt_width;
374330e2 1510 y *= font_height;
1511
1512 if (attr & ATTR_ACTCURS) {
c9def1b8 1513 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
374330e2 1514 attr ^= ATTR_CUR_XOR;
1515 }
1516
1517 nfont = 0;
1518 if (cfg.vtmode == VT_OEMONLY)
1519 nfont |= FONT_OEM;
1520
1521 /*
1522 * Map high-half characters in order to approximate ISO using
59ad2c03 1523 * OEM character set. No characters are missing if the OEM codepage
1524 * is CP850.
374330e2 1525 */
1526 if (nfont & FONT_OEM) {
1527 int i;
1528 for (i=0; i<len; i++)
1529 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
59ad2c03 1530#if 0
1531 /* This is CP850 ... perfect translation */
374330e2 1532 static const char oemhighhalf[] =
1533 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1534 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1535 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1536 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
59ad2c03 1537 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1538 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
374330e2 1539 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1540 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1541 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1542 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1543 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1544 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1545 ;
59ad2c03 1546#endif
1547 /* This is CP437 ... junk translation */
1548 static const unsigned char oemhighhalf[] = {
1549 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1550 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1551 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1552 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1553 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1554 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1555 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1556 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1557 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1558 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1559 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1560 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1561 };
1562
374330e2 1563 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1564 }
1565 }
1566
1567 if (attr & ATTR_GBCHR) {
1568 int i;
1569 /*
1570 * GB mapping: map # to pound, and everything else stays
1571 * normal.
1572 */
1573 for (i=0; i<len; i++)
1574 if (text[i] == '#')
1575 text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
1576 } else if (attr & ATTR_LINEDRW) {
1577 int i;
59ad2c03 1578 /* ISO 8859-1 */
374330e2 1579 static const char poorman[] =
1580 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
59ad2c03 1581
1582 /* CP437 */
1583 static const char oemmap_437[] =
ed91c385 1584 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1585 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
374330e2 1586
59ad2c03 1587 /* CP850 */
1588 static const char oemmap_850[] =
1589 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1590 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1591
1592 /* Poor windows font ... eg: windows courier */
1593 static const char oemmap[] =
1594 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1595 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1596
374330e2 1597 /*
1598 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1599 * VT100 line drawing chars; everything else stays normal.
1600 */
1601 switch (cfg.vtmode) {
1602 case VT_XWINDOWS:
1603 for (i=0; i<len; i++)
1604 if (text[i] >= '\x60' && text[i] <= '\x7E')
1605 text[i] += '\x01' - '\x60';
1606 break;
1607 case VT_OEMANSI:
59ad2c03 1608 /* Make sure we actually have an OEM font */
1609 if (fonts[nfont|FONT_OEM]) {
374330e2 1610 case VT_OEMONLY:
59ad2c03 1611 nfont |= FONT_OEM;
1612 for (i=0; i<len; i++)
1613 if (text[i] >= '\x60' && text[i] <= '\x7E')
1614 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1615 break;
1616 }
374330e2 1617 case VT_POORMAN:
1618 for (i=0; i<len; i++)
1619 if (text[i] >= '\x60' && text[i] <= '\x7E')
1620 text[i] = poorman[(unsigned char)text[i] - 0x60];
1621 break;
1622 }
1623 }
1624
1625 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1626 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1627 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1628 nfont |= FONT_BOLD;
1629 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1630 nfont |= FONT_UNDERLINE;
59ad2c03 1631 if (!fonts[nfont])
1632 {
1633 if (nfont&FONT_UNDERLINE)
1634 force_manual_underline = 1;
1635 /* Don't do the same for manual bold, it could be bad news. */
1636
1637 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1638 }
09798031 1639 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1640 force_manual_underline = 1;
374330e2 1641 if (attr & ATTR_REVERSE) {
1642 t = nfg; nfg = nbg; nbg = t;
1643 }
1644 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1645 nfg++;
59ad2c03 1646 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1647 nbg++;
374330e2 1648 fg = colours[nfg];
1649 bg = colours[nbg];
1650 SelectObject (hdc, fonts[nfont]);
1651 SetTextColor (hdc, fg);
1652 SetBkColor (hdc, bg);
1653 SetBkMode (hdc, OPAQUE);
59ad2c03 1654 line_box.left = x;
1655 line_box.top = y;
c9def1b8 1656 line_box.right = x+fnt_width*len;
59ad2c03 1657 line_box.bottom = y+font_height;
1658 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
374330e2 1659 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1660 SetBkMode (hdc, TRANSPARENT);
59ad2c03 1661
1662 /* GRR: This draws the character outside it's box and can leave
1663 * 'droppings' even with the clip box! I suppose I could loop it
c9def1b8 1664 * one character at a time ... yuk.
1665 *
1666 * Or ... I could do a test print with "W", and use +1 or -1 for this
1667 * shift depending on if the leftmost column is blank...
1668 */
59ad2c03 1669 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
374330e2 1670 }
59ad2c03 1671 if (force_manual_underline ||
1672 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1a6f78fe 1673 HPEN oldpen;
1674 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
374330e2 1675 MoveToEx (hdc, x, y+descent, NULL);
c9def1b8 1676 LineTo (hdc, x+len*fnt_width, y+descent);
1a6f78fe 1677 oldpen = SelectObject (hdc, oldpen);
1678 DeleteObject (oldpen);
374330e2 1679 }
1680 if (attr & ATTR_PASCURS) {
1681 POINT pts[5];
1a6f78fe 1682 HPEN oldpen;
374330e2 1683 pts[0].x = pts[1].x = pts[4].x = x;
c9def1b8 1684 pts[2].x = pts[3].x = x+fnt_width-1;
374330e2 1685 pts[0].y = pts[3].y = pts[4].y = y;
1686 pts[1].y = pts[2].y = y+font_height-1;
1a6f78fe 1687 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
374330e2 1688 Polyline (hdc, pts, 5);
1a6f78fe 1689 oldpen = SelectObject (hdc, oldpen);
1690 DeleteObject (oldpen);
374330e2 1691 }
1692}
1693
c9def1b8 1694static int check_compose(int first, int second) {
1695
1696 static char * composetbl[] = {
1697