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