Improve SSH2 host key abstraction into a generic `signing key'
[sgt/putty] / window.c
Content-type: text/html mdw@git.distorted.org.uk Git - sgt/putty/blame - window.c


500 - Internal Server Error

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