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