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