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