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