Improve SSH2 host key abstraction into a generic `signing key'
[u/mdw/putty] / window.c
... / ...
Content-type: text/html git.distorted.org.uk Git - u/mdw/putty/blame_incremental - window.c


500 - Internal Server Error

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