Try to improve the caret usage
[u/mdw/putty] / window.c
Content-type: text/html git.distorted.org.uk Git - u/mdw/putty/blame - window.c


500 - Internal Server Error

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