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