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