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