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