Wez Furlong's patch to add xterm mouse reporting and proper mouse
[u/mdw/putty] / window.c
Content-type: text/html git.distorted.org.uk Git - u/mdw/putty/blame - window.c


500 - Internal Server Error

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