Fix due to Robert de Bath: backspace should not cross line
[u/mdw/putty] / window.c
CommitLineData
374330e2 1#include <windows.h>
49bad831 2#include <imm.h>
374330e2 3#include <commctrl.h>
f0a70059 4#include <mmsystem.h>
4d331a77 5#ifndef AUTO_WINSOCK
6#ifdef WINSOCK_TWO
7#include <winsock2.h>
8#else
374330e2 9#include <winsock.h>
4d331a77 10#endif
11#endif
374330e2 12#include <stdio.h>
13#include <stdlib.h>
1d470ad2 14#include <ctype.h>
ec55b220 15#include <time.h>
374330e2 16
32874aea 17#define PUTTY_DO_GLOBALS /* actually _define_ globals */
374330e2 18#include "putty.h"
8c3cd914 19#include "winstuff.h"
d5859615 20#include "storage.h"
374330e2 21#include "win_res.h"
22
6833a413 23#define IDM_SHOWLOG 0x0010
24#define IDM_NEWSESS 0x0020
25#define IDM_DUPSESS 0x0030
26#define IDM_RECONF 0x0040
27#define IDM_CLRSB 0x0050
28#define IDM_RESET 0x0060
29#define IDM_TEL_AYT 0x0070
30#define IDM_TEL_BRK 0x0080
31#define IDM_TEL_SYNCH 0x0090
32#define IDM_TEL_EC 0x00a0
33#define IDM_TEL_EL 0x00b0
34#define IDM_TEL_GA 0x00c0
35#define IDM_TEL_NOP 0x00d0
36#define IDM_TEL_ABORT 0x00e0
37#define IDM_TEL_AO 0x00f0
38#define IDM_TEL_IP 0x0100
39#define IDM_TEL_SUSP 0x0110
40#define IDM_TEL_EOR 0x0120
41#define IDM_TEL_EOF 0x0130
42#define IDM_ABOUT 0x0140
43#define IDM_SAVEDSESS 0x0150
bc1235d4 44#define IDM_COPYALL 0x0160
6833a413 45
32874aea 46#define IDM_SESSLGP 0x0250 /* log type printable */
47#define IDM_SESSLGA 0x0260 /* log type all chars */
48#define IDM_SESSLGE 0x0270 /* log end */
6833a413 49#define IDM_SAVED_MIN 0x1000
50#define IDM_SAVED_MAX 0x2000
374330e2 51
59ad2c03 52#define WM_IGNORE_SIZE (WM_XUSER + 1)
53#define WM_IGNORE_CLIP (WM_XUSER + 2)
374330e2 54
3cf144db 55/* Needed for Chinese support and apparently not always defined. */
56#ifndef VK_PROCESSKEY
57#define VK_PROCESSKEY 0xE5
58#endif
59
32874aea 60static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
61static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
62 unsigned char *output);
374330e2 63static void cfgtopalette(void);
64static void init_palette(void);
59ad2c03 65static void init_fonts(int);
374330e2 66
67static int extra_width, extra_height;
68
59ad2c03 69static int pending_netevent = 0;
70static WPARAM pend_netevent_wParam = 0;
71static LPARAM pend_netevent_lParam = 0;
72static void enact_pending_netevent(void);
73
ec55b220 74static time_t last_movement = 0;
75
374330e2 76#define FONT_NORMAL 0
77#define FONT_BOLD 1
78#define FONT_UNDERLINE 2
79#define FONT_BOLDUND 3
80#define FONT_OEM 4
81#define FONT_OEMBOLD 5
82#define FONT_OEMBOLDUND 6
83#define FONT_OEMUND 7
84static HFONT fonts[8];
09798031 85static int font_needs_hand_underlining;
374330e2 86static enum {
87 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
88} bold_mode;
89static enum {
90 UND_LINE, UND_FONT
91} und_mode;
92static int descent;
93
94#define NCOLOURS 24
95static COLORREF colours[NCOLOURS];
96static HPALETTE pal;
97static LPLOGPALETTE logpal;
98static RGBTRIPLE defpal[NCOLOURS];
99
100static HWND hwnd;
101
934c0b7a 102static HBITMAP caretbm;
103
374330e2 104static int dbltime, lasttime, lastact;
105static Mouse_Button lastbtn;
106
01c034ad 107/* this allows xterm-style mouse handling. */
108static int send_raw_mouse = 0;
109static int wheel_accumulator = 0;
110
374330e2 111static char *window_name, *icon_name;
112
03f23ad6 113static int compose_state = 0;
114
0965bee0 115/* Dummy routine, only required in plink. */
32874aea 116void ldisc_update(int echo, int edit)
117{
118}
6f34e365 119
32874aea 120int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
121{
374330e2 122 static char appname[] = "PuTTY";
123 WORD winsock_ver;
124 WSADATA wsadata;
125 WNDCLASS wndclass;
126 MSG msg;
127 int guess_width, guess_height;
128
8c3cd914 129 hinst = inst;
67779be7 130 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
73251d5d 131
374330e2 132 winsock_ver = MAKEWORD(1, 1);
133 if (WSAStartup(winsock_ver, &wsadata)) {
134 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
135 MB_OK | MB_ICONEXCLAMATION);
136 return 1;
137 }
138 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
139 MessageBox(NULL, "WinSock version is incompatible with 1.1",
140 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
141 WSACleanup();
142 return 1;
143 }
144 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
8df7a775 145 sk_init();
374330e2 146
147 InitCommonControls();
148
301b66db 149 /* Ensure a Maximize setting in Explorer doesn't maximise the
150 * config box. */
151 defuse_showwindow();
152
374330e2 153 /*
154 * Process the command line.
155 */
156 {
157 char *p;
158
e277c42d 159 default_protocol = DEFAULT_PROTOCOL;
160 default_port = DEFAULT_PORT;
e1c8e0ed 161 cfg.logtype = LGTYP_NONE;
e277c42d 162
a9422f39 163 do_defaults(NULL, &cfg);
374330e2 164
165 p = cmdline;
32874aea 166 while (*p && isspace(*p))
167 p++;
374330e2 168
169 /*
e277c42d 170 * Process command line options first. Yes, this can be
171 * done better, and it will be as soon as I have the
172 * energy...
173 */
174 while (*p == '-') {
175 char *q = p + strcspn(p, " \t");
176 p++;
177 if (q == p + 3 &&
178 tolower(p[0]) == 's' &&
32874aea 179 tolower(p[1]) == 's' && tolower(p[2]) == 'h') {
e277c42d 180 default_protocol = cfg.protocol = PROT_SSH;
181 default_port = cfg.port = 22;
de3df031 182 } else if (q == p + 7 &&
32874aea 183 tolower(p[0]) == 'c' &&
184 tolower(p[1]) == 'l' &&
185 tolower(p[2]) == 'e' &&
186 tolower(p[3]) == 'a' &&
187 tolower(p[4]) == 'n' &&
188 tolower(p[5]) == 'u' && tolower(p[6]) == 'p') {
189 /*
190 * `putty -cleanup'. Remove all registry entries
191 * associated with PuTTY, and also find and delete
192 * the random seed file.
193 */
194 if (MessageBox(NULL,
195 "This procedure will remove ALL Registry\n"
196 "entries associated with PuTTY, and will\n"
197 "also remove the PuTTY random seed file.\n"
198 "\n"
199 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
200 "SESSIONS. Are you really sure you want\n"
201 "to continue?",
202 "PuTTY Warning",
203 MB_YESNO | MB_ICONWARNING) == IDYES) {
204 cleanup_all();
205 }
206 exit(0);
e277c42d 207 }
208 p = q + strspn(q, " \t");
209 }
210
211 /*
374330e2 212 * An initial @ means to activate a saved session.
213 */
214 if (*p == '@') {
f317611e 215 int i = strlen(p);
32874aea 216 while (i > 1 && isspace(p[i - 1]))
f317611e 217 i--;
218 p[i] = '\0';
32874aea 219 do_defaults(p + 1, &cfg);
374330e2 220 if (!*cfg.host && !do_config()) {
221 WSACleanup();
222 return 0;
223 }
224 } else if (*p == '&') {
225 /*
226 * An initial & means we've been given a command line
227 * containing the hex value of a HANDLE for a file
228 * mapping object, which we must then extract as a
229 * config.
230 */
231 HANDLE filemap;
232 Config *cp;
32874aea 233 if (sscanf(p + 1, "%p", &filemap) == 1 &&
374330e2 234 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
235 0, 0, sizeof(Config))) != NULL) {
236 cfg = *cp;
237 UnmapViewOfFile(cp);
238 CloseHandle(filemap);
239 } else if (!do_config()) {
240 WSACleanup();
241 return 0;
242 }
243 } else if (*p) {
244 char *q = p;
32874aea 245 /*
246 * If the hostname starts with "telnet:", set the
247 * protocol to Telnet and process the string as a
248 * Telnet URL.
249 */
250 if (!strncmp(q, "telnet:", 7)) {
251 char c;
252
253 q += 7;
70887be9 254 if (q[0] == '/' && q[1] == '/')
255 q += 2;
32874aea 256 cfg.protocol = PROT_TELNET;
257 p = q;
258 while (*p && *p != ':' && *p != '/')
259 p++;
260 c = *p;
261 if (*p)
262 *p++ = '\0';
263 if (c == ':')
264 cfg.port = atoi(p);
265 else
266 cfg.port = -1;
267 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
268 cfg.host[sizeof(cfg.host) - 1] = '\0';
269 } else {
270 while (*p && !isspace(*p))
271 p++;
272 if (*p)
273 *p++ = '\0';
274 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
275 cfg.host[sizeof(cfg.host) - 1] = '\0';
276 while (*p && isspace(*p))
277 p++;
278 if (*p)
279 cfg.port = atoi(p);
280 else
281 cfg.port = -1;
282 }
374330e2 283 } else {
284 if (!do_config()) {
285 WSACleanup();
286 return 0;
287 }
288 }
13eafebf 289
290 /* See if host is of the form user@host */
291 if (cfg.host[0] != '\0') {
292 char *atsign = strchr(cfg.host, '@');
293 /* Make sure we're not overflowing the user field */
294 if (atsign) {
32874aea 295 if (atsign - cfg.host < sizeof cfg.username) {
296 strncpy(cfg.username, cfg.host, atsign - cfg.host);
297 cfg.username[atsign - cfg.host] = '\0';
13eafebf 298 }
32874aea 299 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
13eafebf 300 }
301 }
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;
374330e2 1136
1137 switch (message) {
59ad2c03 1138 case WM_TIMER:
1139 if (pending_netevent)
1140 enact_pending_netevent();
c9def1b8 1141 if (inbuf_head)
59ad2c03 1142 term_out();
32874aea 1143 noise_regular();
1144 HideCaret(hwnd);
59ad2c03 1145 term_update();
32874aea 1146 ShowCaret(hwnd);
1147 if (cfg.ping_interval > 0) {
1148 time_t now;
1149 time(&now);
1150 if (now - last_movement > cfg.ping_interval) {
1151 back->special(TS_PING);
1152 last_movement = now;
1153 }
1154 }
59ad2c03 1155 return 0;
374330e2 1156 case WM_CREATE:
1157 break;
68130d34 1158 case WM_CLOSE:
32874aea 1159 show_mouseptr(1);
d85548fe 1160 if (!cfg.warn_on_close || session_closed ||
32874aea 1161 MessageBox(hwnd,
1162 "Are you sure you want to close this session?",
68130d34 1163 "PuTTY Exit Confirmation",
1164 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1165 DestroyWindow(hwnd);
1166 return 0;
374330e2 1167 case WM_DESTROY:
32874aea 1168 show_mouseptr(1);
1169 PostQuitMessage(0);
374330e2 1170 return 0;
6833a413 1171 case WM_SYSCOMMAND:
1172 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
374330e2 1173 case IDM_SHOWLOG:
c5e9c988 1174 showeventlog(hwnd);
374330e2 1175 break;
1176 case IDM_NEWSESS:
1177 case IDM_DUPSESS:
6833a413 1178 case IDM_SAVEDSESS:
374330e2 1179 {
1180 char b[2048];
1181 char c[30], *cl;
e4e4cc7e 1182 int freecl = FALSE;
374330e2 1183 STARTUPINFO si;
1184 PROCESS_INFORMATION pi;
1185 HANDLE filemap = NULL;
1186
1187 if (wParam == IDM_DUPSESS) {
1188 /*
1189 * Allocate a file-mapping memory chunk for the
1190 * config structure.
1191 */
1192 SECURITY_ATTRIBUTES sa;
1193 Config *p;
1194
1195 sa.nLength = sizeof(sa);
1196 sa.lpSecurityDescriptor = NULL;
1197 sa.bInheritHandle = TRUE;
32874aea 1198 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
374330e2 1199 &sa,
1200 PAGE_READWRITE,
32874aea 1201 0, sizeof(Config), NULL);
374330e2 1202 if (filemap) {
32874aea 1203 p = (Config *) MapViewOfFile(filemap,
1204 FILE_MAP_WRITE,
1205 0, 0, sizeof(Config));
374330e2 1206 if (p) {
1207 *p = cfg; /* structure copy */
1208 UnmapViewOfFile(p);
1209 }
1210 }
1d470ad2 1211 sprintf(c, "putty &%p", filemap);
374330e2 1212 cl = c;
0a4aa984 1213 } else if (wParam == IDM_SAVEDSESS) {
32874aea 1214 char *session =
1215 sessions[(lParam - IDM_SAVED_MIN) / 16];
1216 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
e4e4cc7e 1217 if (!cl)
1218 cl = NULL; /* not a very important failure mode */
94e6450e 1219 else {
1220 sprintf(cl, "putty @%s", session);
1221 freecl = TRUE;
1222 }
374330e2 1223 } else
6833a413 1224 cl = NULL;
374330e2 1225
32874aea 1226 GetModuleFileName(NULL, b, sizeof(b) - 1);
374330e2 1227 si.cb = sizeof(si);
1228 si.lpReserved = NULL;
1229 si.lpDesktop = NULL;
1230 si.lpTitle = NULL;
1231 si.dwFlags = 0;
1232 si.cbReserved2 = 0;
1233 si.lpReserved2 = NULL;
32874aea 1234 CreateProcess(b, cl, NULL, NULL, TRUE,
1235 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
374330e2 1236
1237 if (filemap)
1238 CloseHandle(filemap);
e4e4cc7e 1239 if (freecl)
dcbde236 1240 sfree(cl);
374330e2 1241 }
1242 break;
32874aea 1243 case IDM_RECONF:
1244 {
1245 int prev_alwaysontop = cfg.alwaysontop;
1246 int prev_sunken_edge = cfg.sunken_edge;
e1c8e0ed 1247 char oldlogfile[FILENAME_MAX];
1248 int oldlogtype;
3da0b1d2 1249 int need_setwpos = FALSE;
cbc3272b 1250 int old_fwidth, old_fheight;
e1c8e0ed 1251
1252 strcpy(oldlogfile, cfg.logfilename);
1253 oldlogtype = cfg.logtype;
3da0b1d2 1254 cfg.width = cols;
1255 cfg.height = rows;
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;
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;
1356 term_size(cfg.height, cfg.width, cfg.savelines);
1357 InvalidateRect(hwnd, NULL, TRUE);
1358 if (need_setwpos) {
3da0b1d2 1359 force_normal(hwnd);
32874aea 1360 SetWindowPos(hwnd, NULL, 0, 0,
1361 extra_width + font_width * cfg.width,
1362 extra_height + font_height * cfg.height,
1363 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1364 SWP_NOMOVE | SWP_NOZORDER);
1365 }
1366 set_title(cfg.wintitle);
1367 if (IsIconic(hwnd)) {
1368 SetWindowText(hwnd,
1369 cfg.win_name_always ? window_name :
1370 icon_name);
3da0b1d2 1371 }
32874aea 1372 }
1373 break;
bc1235d4 1374 case IDM_COPYALL:
1375 term_copyall();
1376 break;
32874aea 1377 case IDM_CLRSB:
1378 term_clrsb();
1379 break;
1380 case IDM_RESET:
1381 term_pwron();
1382 break;
1383 case IDM_TEL_AYT:
1384 back->special(TS_AYT);
1385 break;
1386 case IDM_TEL_BRK:
1387 back->special(TS_BRK);
1388 break;
1389 case IDM_TEL_SYNCH:
1390 back->special(TS_SYNCH);
1391 break;
1392 case IDM_TEL_EC:
1393 back->special(TS_EC);
1394 break;
1395 case IDM_TEL_EL:
1396 back->special(TS_EL);
1397 break;
1398 case IDM_TEL_GA:
1399 back->special(TS_GA);
1400 break;
1401 case IDM_TEL_NOP:
1402 back->special(TS_NOP);
1403 break;
1404 case IDM_TEL_ABORT:
1405 back->special(TS_ABORT);
1406 break;
1407 case IDM_TEL_AO:
1408 back->special(TS_AO);
1409 break;
1410 case IDM_TEL_IP:
1411 back->special(TS_IP);
1412 break;
1413 case IDM_TEL_SUSP:
1414 back->special(TS_SUSP);
1415 break;
1416 case IDM_TEL_EOR:
1417 back->special(TS_EOR);
1418 break;
1419 case IDM_TEL_EOF:
1420 back->special(TS_EOF);
1421 break;
374330e2 1422 case IDM_ABOUT:
32874aea 1423 showabout(hwnd);
374330e2 1424 break;
32874aea 1425 default:
1426 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1427 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1428 }
374330e2 1429 }
1430 break;
37508af4 1431
1432#define X_POS(l) ((int)(short)LOWORD(l))
1433#define Y_POS(l) ((int)(short)HIWORD(l))
1434
fdedf2c8 1435#define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1436#define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
01c034ad 1437#define WHEEL_DELTA 120
1438 case WM_MOUSEWHEEL:
1439 {
32874aea 1440 wheel_accumulator += (short) HIWORD(wParam);
01c034ad 1441 wParam = LOWORD(wParam);
1442
1443 /* process events when the threshold is reached */
1444 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1445 int b;
fdedf2c8 1446
01c034ad 1447 /* reduce amount for next time */
1448 if (wheel_accumulator > 0) {
1449 b = MBT_WHEEL_UP;
1450 wheel_accumulator -= WHEEL_DELTA;
32874aea 1451 } else if (wheel_accumulator < 0) {
01c034ad 1452 b = MBT_WHEEL_DOWN;
1453 wheel_accumulator += WHEEL_DELTA;
32874aea 1454 } else
01c034ad 1455 break;
1456
1457 if (send_raw_mouse) {
1458 /* send a mouse-down followed by a mouse up */
1459 term_mouse(b,
1460 MA_CLICK,
32874aea 1461 TO_CHR_X(X_POS(lParam)),
1462 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1463 wParam & MK_CONTROL);
1464 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1465 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1466 wParam & MK_CONTROL);
01c034ad 1467 } else {
1468 /* trigger a scroll */
32874aea 1469 term_scroll(0,
1470 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
01c034ad 1471 }
1472 }
1473 return 0;
1474 }
374330e2 1475 case WM_LBUTTONDOWN:
374330e2 1476 case WM_MBUTTONDOWN:
374330e2 1477 case WM_RBUTTONDOWN:
01c034ad 1478 case WM_LBUTTONUP:
1479 case WM_MBUTTONUP:
374330e2 1480 case WM_RBUTTONUP:
01c034ad 1481 {
1482 int button, press;
1483 switch (message) {
32874aea 1484 case WM_LBUTTONDOWN:
1485 button = MBT_LEFT;
1486 press = 1;
1487 break;
1488 case WM_MBUTTONDOWN:
1489 button = MBT_MIDDLE;
1490 press = 1;
1491 break;
1492 case WM_RBUTTONDOWN:
1493 button = MBT_RIGHT;
1494 press = 1;
1495 break;
1496 case WM_LBUTTONUP:
1497 button = MBT_LEFT;
1498 press = 0;
1499 break;
1500 case WM_MBUTTONUP:
1501 button = MBT_MIDDLE;
1502 press = 0;
1503 break;
1504 case WM_RBUTTONUP:
1505 button = MBT_RIGHT;
1506 press = 0;
1507 break;
01c034ad 1508 }
1509 show_mouseptr(1);
1510 if (press) {
32874aea 1511 click(button,
1512 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1513 wParam & MK_SHIFT, wParam & MK_CONTROL);
01c034ad 1514 SetCapture(hwnd);
1515 } else {
32874aea 1516 term_mouse(button, MA_RELEASE,
1517 TO_CHR_X(X_POS(lParam)),
1518 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1519 wParam & MK_CONTROL);
01c034ad 1520 ReleaseCapture();
1521 }
1522 }
374330e2 1523 return 0;
1524 case WM_MOUSEMOVE:
32874aea 1525 show_mouseptr(1);
374330e2 1526 /*
1527 * Add the mouse position and message time to the random
7d6ee6ff 1528 * number noise.
374330e2 1529 */
32874aea 1530 noise_ultralight(lParam);
374330e2 1531
1532 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1533 Mouse_Button b;
1534 if (wParam & MK_LBUTTON)
01c034ad 1535 b = MBT_SELECT;
374330e2 1536 else if (wParam & MK_MBUTTON)
01c034ad 1537 b = cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
374330e2 1538 else
01c034ad 1539 b = cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
32874aea 1540 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1541 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1542 wParam & MK_CONTROL);
374330e2 1543 }
374330e2 1544 return 0;
d318ef8e 1545 case WM_NCMOUSEMOVE:
1546 show_mouseptr(1);
32874aea 1547 noise_ultralight(lParam);
d318ef8e 1548 return 0;
374330e2 1549 case WM_IGNORE_CLIP:
1550 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1551 break;
1552 case WM_DESTROYCLIPBOARD:
1553 if (!ignore_clip)
1554 term_deselect();
1555 ignore_clip = FALSE;
1556 return 0;
1557 case WM_PAINT:
1558 {
1559 PAINTSTRUCT p;
32874aea 1560 HideCaret(hwnd);
1561 hdc = BeginPaint(hwnd, &p);
374330e2 1562 if (pal) {
32874aea 1563 SelectPalette(hdc, pal, TRUE);
1564 RealizePalette(hdc);
374330e2 1565 }
32874aea 1566 term_paint(hdc, p.rcPaint.left, p.rcPaint.top,
1567 p.rcPaint.right, p.rcPaint.bottom);
1568 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1569 SelectObject(hdc, GetStockObject(WHITE_PEN));
1570 EndPaint(hwnd, &p);
1571 ShowCaret(hwnd);
374330e2 1572 }
1573 return 0;
1574 case WM_NETEVENT:
59ad2c03 1575 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1576 * but the only one that's likely to try to overload us is FD_READ.
1577 * This means buffering just one is fine.
1578 */
1579 if (pending_netevent)
1580 enact_pending_netevent();
1581
1582 pending_netevent = TRUE;
32874aea 1583 pend_netevent_wParam = wParam;
1584 pend_netevent_lParam = lParam;
ec55b220 1585 time(&last_movement);
374330e2 1586 return 0;
1587 case WM_SETFOCUS:
1588 has_focus = TRUE;
32874aea 1589 CreateCaret(hwnd, caretbm, font_width, font_height);
1590 ShowCaret(hwnd);
1591 compose_state = 0;
374330e2 1592 term_out();
1593 term_update();
1594 break;
1595 case WM_KILLFOCUS:
32874aea 1596 show_mouseptr(1);
374330e2 1597 has_focus = FALSE;
32874aea 1598 DestroyCaret();
374330e2 1599 term_out();
1600 term_update();
1601 break;
1602 case WM_IGNORE_SIZE:
1603 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1604 break;
73251d5d 1605 case WM_ENTERSIZEMOVE:
32874aea 1606 EnableSizeTip(1);
1607 resizing = TRUE;
3ad8c6db 1608 need_backend_resize = FALSE;
32874aea 1609 break;
73251d5d 1610 case WM_EXITSIZEMOVE:
32874aea 1611 EnableSizeTip(0);
1612 resizing = FALSE;
3ad8c6db 1613 if (need_backend_resize)
1614 back->size();
32874aea 1615 break;
374330e2 1616 case WM_SIZING:
1617 {
1618 int width, height, w, h, ew, eh;
32874aea 1619 LPRECT r = (LPRECT) lParam;
374330e2 1620
1621 width = r->right - r->left - extra_width;
1622 height = r->bottom - r->top - extra_height;
32874aea 1623 w = (width + font_width / 2) / font_width;
1624 if (w < 1)
1625 w = 1;
1626 h = (height + font_height / 2) / font_height;
1627 if (h < 1)
1628 h = 1;
1629 UpdateSizeTip(hwnd, w, h);
374330e2 1630 ew = width - w * font_width;
1631 eh = height - h * font_height;
1632 if (ew != 0) {
1633 if (wParam == WMSZ_LEFT ||
32874aea 1634 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
374330e2 1635 r->left += ew;
1636 else
1637 r->right -= ew;
1638 }
1639 if (eh != 0) {
1640 if (wParam == WMSZ_TOP ||
32874aea 1641 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
374330e2 1642 r->top += eh;
1643 else
1644 r->bottom -= eh;
1645 }
1646 if (ew || eh)
1647 return 1;
1648 else
1649 return 0;
1650 }
32874aea 1651 /* break; (never reached) */
374330e2 1652 case WM_SIZE:
1653 if (wParam == SIZE_MINIMIZED) {
32874aea 1654 SetWindowText(hwnd,
1655 cfg.win_name_always ? window_name : icon_name);
374330e2 1656 break;
1657 }
1658 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
32874aea 1659 SetWindowText(hwnd, window_name);
374330e2 1660 if (!ignore_size) {
1a6f78fe 1661 int width, height, w, h;
32874aea 1662#if 0 /* we have fixed this using WM_SIZING now */
1663 int ew, eh;
1a6f78fe 1664#endif
374330e2 1665
1666 width = LOWORD(lParam);
1667 height = HIWORD(lParam);
32874aea 1668 w = width / font_width;
1669 if (w < 1)
1670 w = 1;
1671 h = height / font_height;
1672 if (h < 1)
1673 h = 1;
1674#if 0 /* we have fixed this using WM_SIZING now */
374330e2 1675 ew = width - w * font_width;
1676 eh = height - h * font_height;
1677 if (ew != 0 || eh != 0) {
1678 RECT r;
32874aea 1679 GetWindowRect(hwnd, &r);
1680 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1681 SetWindowPos(hwnd, NULL, 0, 0,
1682 r.right - r.left - ew, r.bottom - r.top - eh,
1683 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
374330e2 1684 }
1685#endif
1686 if (w != cols || h != rows || just_reconfigged) {
1687 term_invalidate();
32874aea 1688 term_size(h, w, cfg.savelines);
1689 /*
1690 * Don't call back->size in mid-resize. (To prevent
1691 * massive numbers of resize events getting sent
1692 * down the connection during an NT opaque drag.)
1693 */
1694 if (!resizing)
1695 back->size();
3ad8c6db 1696 else
1697 need_backend_resize = TRUE;
374330e2 1698 just_reconfigged = FALSE;
1699 }
1700 }
1701 ignore_size = FALSE;
1702 return 0;
1703 case WM_VSCROLL:
1704 switch (LOWORD(wParam)) {
32874aea 1705 case SB_BOTTOM:
1706 term_scroll(-1, 0);
1707 break;
1708 case SB_TOP:
1709 term_scroll(+1, 0);
1710 break;
1711 case SB_LINEDOWN:
1712 term_scroll(0, +1);
1713 break;
1714 case SB_LINEUP:
1715 term_scroll(0, -1);
1716 break;
1717 case SB_PAGEDOWN:
1718 term_scroll(0, +rows / 2);
1719 break;
1720 case SB_PAGEUP:
1721 term_scroll(0, -rows / 2);
1722 break;
1723 case SB_THUMBPOSITION:
1724 case SB_THUMBTRACK:
1725 term_scroll(1, HIWORD(wParam));
1726 break;
1727 }
1728 break;
1729 case WM_PALETTECHANGED:
374330e2 1730 if ((HWND) wParam != hwnd && pal != NULL) {
1731 HDC hdc = get_ctx();
1732 if (hdc) {
32874aea 1733 if (RealizePalette(hdc) > 0)
1734 UpdateColors(hdc);
1735 free_ctx(hdc);
374330e2 1736 }
1737 }
1738 break;
1739 case WM_QUERYNEWPALETTE:
1740 if (pal != NULL) {
1741 HDC hdc = get_ctx();
1742 if (hdc) {
32874aea 1743 if (RealizePalette(hdc) > 0)
1744 UpdateColors(hdc);
1745 free_ctx(hdc);
374330e2 1746 return TRUE;
1747 }
1748 }
1749 return FALSE;
1750 case WM_KEYDOWN:
1751 case WM_SYSKEYDOWN:
c9def1b8 1752 case WM_KEYUP:
1753 case WM_SYSKEYUP:
374330e2 1754 /*
1755 * Add the scan code and keypress timing to the random
7d6ee6ff 1756 * number noise.
374330e2 1757 */
32874aea 1758 noise_ultralight(lParam);
374330e2 1759
1760 /*
1761 * We don't do TranslateMessage since it disassociates the
1762 * resulting CHAR message from the KEYDOWN that sparked it,
1763 * which we occasionally don't want. Instead, we process
1764 * KEYDOWN, and call the Win32 translator functions so that
1765 * we get the translations under _our_ control.
1766 */
1767 {
1768 unsigned char buf[20];
1769 int len;
1770
32874aea 1771 if (wParam == VK_PROCESSKEY) {
3cf144db 1772 MSG m;
32874aea 1773 m.hwnd = hwnd;
1774 m.message = WM_KEYDOWN;
1775 m.wParam = wParam;
1776 m.lParam = lParam & 0xdfff;
1777 TranslateMessage(&m);
1778 } else {
1779 len = TranslateKey(message, wParam, lParam, buf);
3cf144db 1780 if (len == -1)
32874aea 1781 return DefWindowProc(hwnd, message, wParam, lParam);
1782 ldisc_send(buf, len);
554c540d 1783
32874aea 1784 if (len > 0)
1785 show_mouseptr(0);
3cf144db 1786 }
374330e2 1787 }
1788 return 0;
3cf144db 1789 case WM_IME_CHAR:
1790 {
1791 unsigned char buf[2];
1792
1793 buf[1] = wParam;
1794 buf[0] = wParam >> 8;
32874aea 1795 ldisc_send(buf, 2);
3cf144db 1796 }
374330e2 1797 case WM_CHAR:
1798 case WM_SYSCHAR:
1799 /*
1800 * Nevertheless, we are prepared to deal with WM_CHAR
1801 * messages, should they crop up. So if someone wants to
1802 * post the things to us as part of a macro manoeuvre,
1803 * we're ready to cope.
1804 */
32874aea 1805 {
1806 char c = xlat_kbd2tty((unsigned char) wParam);
1807 ldisc_send(&c, 1);
374330e2 1808 }
1809 return 0;
32874aea 1810 case WM_SETCURSOR:
1811 if (send_raw_mouse) {
1812 SetCursor(LoadCursor(NULL, IDC_ARROW));
1813 return TRUE;
1814 }
374330e2 1815 }
1816
32874aea 1817 return DefWindowProc(hwnd, message, wParam, lParam);
374330e2 1818}
1819
1820/*
ec8679e9 1821 * Move the system caret. (We maintain one, even though it's
1822 * invisible, for the benefit of blind people: apparently some
1823 * helper software tracks the system caret, so we should arrange to
1824 * have one.)
1825 */
32874aea 1826void sys_cursor(int x, int y)
1827{
a21ea6e7 1828 if (has_focus)
1829 SetCaretPos(x * font_width, y * font_height);
ec8679e9 1830}
1831
1832/*
374330e2 1833 * Draw a line of text in the window, at given character
1834 * coordinates, in given attributes.
1835 *
1836 * We are allowed to fiddle with the contents of `text'.
1837 */
32874aea 1838void do_text(Context ctx, int x, int y, char *text, int len,
1839 unsigned long attr, int lattr)
1840{
374330e2 1841 COLORREF fg, bg, t;
1842 int nfg, nbg, nfont;
1843 HDC hdc = ctx;
59ad2c03 1844 RECT line_box;
1845 int force_manual_underline = 0;
32874aea 1846 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
09798031 1847 static int *IpDx = 0, IpDxLEN = 0;;
59ad2c03 1848
32874aea 1849 if (len > IpDxLEN || IpDx[0] != fnt_width) {
59ad2c03 1850 int i;
32874aea 1851 if (len > IpDxLEN) {
59ad2c03 1852 sfree(IpDx);
32874aea 1853 IpDx = smalloc((len + 16) * sizeof(int));
1854 IpDxLEN = (len + 16);
59ad2c03 1855 }
32874aea 1856 for (i = 0; i < IpDxLEN; i++)
c9def1b8 1857 IpDx[i] = fnt_width;
59ad2c03 1858 }
374330e2 1859
c9def1b8 1860 x *= fnt_width;
374330e2 1861 y *= font_height;
1862
4e30ff69 1863 if ((attr & ATTR_ACTCURS) && cfg.cursor_type == 0) {
c9def1b8 1864 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
374330e2 1865 attr ^= ATTR_CUR_XOR;
1866 }
1867
1868 nfont = 0;
1869 if (cfg.vtmode == VT_OEMONLY)
1870 nfont |= FONT_OEM;
1871
1872 /*
1873 * Map high-half characters in order to approximate ISO using
59ad2c03 1874 * OEM character set. No characters are missing if the OEM codepage
1875 * is CP850.
374330e2 1876 */
1877 if (nfont & FONT_OEM) {
1878 int i;
32874aea 1879 for (i = 0; i < len; i++)
374330e2 1880 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
59ad2c03 1881#if 0
1882 /* This is CP850 ... perfect translation */
32874aea 1883 static const char oemhighhalf[] = "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1884 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1885 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1886 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1887 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1888 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1889 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1890 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1891 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1892 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1893 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1894 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1895 ;
59ad2c03 1896#endif
1897 /* This is CP437 ... junk translation */
1898 static const unsigned char oemhighhalf[] = {
5350aeb9 1899 0x20, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
59ad2c03 1900 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1901 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1902 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1903 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1904 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1905 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1906 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1907 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1908 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1909 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1910 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1911 };
1912
32874aea 1913 text[i] = oemhighhalf[(unsigned char) text[i] - 0xA0];
374330e2 1914 }
1915 }
1916
ec55b220 1917 if (attr & ATTR_LINEDRW) {
374330e2 1918 int i;
59ad2c03 1919 /* ISO 8859-1 */
374330e2 1920 static const char poorman[] =
1921 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
59ad2c03 1922
1923 /* CP437 */
1924 static const char oemmap_437[] =
ed91c385 1925 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1926 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
374330e2 1927
59ad2c03 1928 /* CP850 */
1929 static const char oemmap_850[] =
1930 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1931 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1932
1933 /* Poor windows font ... eg: windows courier */
1934 static const char oemmap[] =
1935 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1936 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1937
374330e2 1938 /*
1939 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1940 * VT100 line drawing chars; everything else stays normal.
ec55b220 1941 *
1942 * Actually '_' maps to space too, but that's done before.
374330e2 1943 */
1944 switch (cfg.vtmode) {
1945 case VT_XWINDOWS:
32874aea 1946 for (i = 0; i < len; i++)
374330e2 1947 if (text[i] >= '\x60' && text[i] <= '\x7E')
1948 text[i] += '\x01' - '\x60';
1949 break;
1950 case VT_OEMANSI:
59ad2c03 1951 /* Make sure we actually have an OEM font */
32874aea 1952 if (fonts[nfont | FONT_OEM]) {
374330e2 1953 case VT_OEMONLY:
32874aea 1954 nfont |= FONT_OEM;
1955 for (i = 0; i < len; i++)
59ad2c03 1956 if (text[i] >= '\x60' && text[i] <= '\x7E')
32874aea 1957 text[i] = oemmap[(unsigned char) text[i] - 0x60];
1958 break;
59ad2c03 1959 }
374330e2 1960 case VT_POORMAN:
32874aea 1961 for (i = 0; i < len; i++)
374330e2 1962 if (text[i] >= '\x60' && text[i] <= '\x7E')
32874aea 1963 text[i] = poorman[(unsigned char) text[i] - 0x60];
374330e2 1964 break;
1965 }
1966 }
1967
1968 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1969 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1970 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1971 nfont |= FONT_BOLD;
1972 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1973 nfont |= FONT_UNDERLINE;
32874aea 1974 if (!fonts[nfont]) {
1975 if (nfont & FONT_UNDERLINE)
59ad2c03 1976 force_manual_underline = 1;
1977 /* Don't do the same for manual bold, it could be bad news. */
1978
32874aea 1979 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
59ad2c03 1980 }
09798031 1981 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
32874aea 1982 force_manual_underline = 1;
374330e2 1983 if (attr & ATTR_REVERSE) {
32874aea 1984 t = nfg;
1985 nfg = nbg;
1986 nbg = t;
374330e2 1987 }
1988 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1989 nfg++;
59ad2c03 1990 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1991 nbg++;
374330e2 1992 fg = colours[nfg];
1993 bg = colours[nbg];
32874aea 1994 SelectObject(hdc, fonts[nfont]);
1995 SetTextColor(hdc, fg);
1996 SetBkColor(hdc, bg);
1997 SetBkMode(hdc, OPAQUE);
1998 line_box.left = x;
1999 line_box.top = y;
2000 line_box.right = x + fnt_width * len;
2001 line_box.bottom = y + font_height;
2002 ExtTextOut(hdc, x, y, ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len,
2003 IpDx);
374330e2 2004 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
32874aea 2005 SetBkMode(hdc, TRANSPARENT);
2006
2007 /* GRR: This draws the character outside it's box and can leave
2008 * 'droppings' even with the clip box! I suppose I could loop it
2009 * one character at a time ... yuk.
2010 *
2011 * Or ... I could do a test print with "W", and use +1 or -1 for this
2012 * shift depending on if the leftmost column is blank...
2013 */
2014 ExtTextOut(hdc, x - 1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
374330e2 2015 }
32874aea 2016 if (force_manual_underline ||
2017 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
2018 HPEN oldpen;
2019 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2020 MoveToEx(hdc, x, y + descent, NULL);
2021 LineTo(hdc, x + len * fnt_width, y + descent);
2022 oldpen = SelectObject(hdc, oldpen);
2023 DeleteObject(oldpen);
374330e2 2024 }
2d3411e9 2025 if ((attr & ATTR_PASCURS) && cfg.cursor_type == 0) {
374330e2 2026 POINT pts[5];
32874aea 2027 HPEN oldpen;
374330e2 2028 pts[0].x = pts[1].x = pts[4].x = x;
32874aea 2029 pts[2].x = pts[3].x = x + fnt_width - 1;
374330e2 2030 pts[0].y = pts[3].y = pts[4].y = y;
32874aea 2031 pts[1].y = pts[2].y = y + font_height - 1;
2032 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2033 Polyline(hdc, pts, 5);
2034 oldpen = SelectObject(hdc, oldpen);
2035 DeleteObject(oldpen);
374330e2 2036 }
4e30ff69 2037 if ((attr & (ATTR_ACTCURS | ATTR_PASCURS)) && cfg.cursor_type != 0) {
32874aea 2038 int startx, starty, dx, dy, length, i;
4e30ff69 2039 if (cfg.cursor_type == 1) {
32874aea 2040 startx = x;
2041 starty = y + descent;
2042 dx = 1;
2043 dy = 0;
2044 length = fnt_width;
2045 } else {
4e30ff69 2046 int xadjust = 0;
2047 if (attr & ATTR_RIGHTCURS)
32874aea 2048 xadjust = fnt_width - 1;
2049 startx = x + xadjust;
2050 starty = y;
2051 dx = 0;
2052 dy = 1;
2053 length = font_height;
2054 }
2055 if (attr & ATTR_ACTCURS) {
2056 HPEN oldpen;
2057 oldpen =
2058 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2059 MoveToEx(hdc, startx, starty, NULL);
2060 LineTo(hdc, startx + dx * length, starty + dy * length);
2061 oldpen = SelectObject(hdc, oldpen);
2062 DeleteObject(oldpen);
2063 } else {
2064 for (i = 0; i < length; i++) {
2065 if (i % 2 == 0) {
2066 SetPixel(hdc, startx, starty, colours[23]);
2067 }
2068 startx += dx;
2069 starty += dy;
2070 }
2071 }
4e30ff69 2072 }
374330e2 2073}
2074
32874aea 2075static int check_compose(int first, int second)
2076{
2077
2078 static char *composetbl[] = {
2079