Added a fourth application: plink, a command line connection utility
[u/mdw/putty] / window.c
... / ...
Content-type: text/html git.distorted.org.uk Git - u/mdw/putty/blame_incremental - window.c


500 - Internal Server Error

Malformed UTF-8 character (fatal) at (eval 5) line 1, <$fd> line 1527.
CommitLineData
1#include <windows.h>
2#include <commctrl.h>
3#include <winsock.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <ctype.h>
7
8#define PUTTY_DO_GLOBALS /* actually _define_ globals */
9#include "putty.h"
10#include "win_res.h"
11
12#define IDM_SHOWLOG 0x0010
13#define IDM_NEWSESS 0x0020
14#define IDM_DUPSESS 0x0030
15#define IDM_RECONF 0x0040
16#define IDM_CLRSB 0x0050
17#define IDM_RESET 0x0060
18#define IDM_TEL_AYT 0x0070
19#define IDM_TEL_BRK 0x0080
20#define IDM_TEL_SYNCH 0x0090
21#define IDM_TEL_EC 0x00a0
22#define IDM_TEL_EL 0x00b0
23#define IDM_TEL_GA 0x00c0
24#define IDM_TEL_NOP 0x00d0
25#define IDM_TEL_ABORT 0x00e0
26#define IDM_TEL_AO 0x00f0
27#define IDM_TEL_IP 0x0100
28#define IDM_TEL_SUSP 0x0110
29#define IDM_TEL_EOR 0x0120
30#define IDM_TEL_EOF 0x0130
31#define IDM_ABOUT 0x0140
32#define IDM_SAVEDSESS 0x0150
33
34#define IDM_SAVED_MIN 0x1000
35#define IDM_SAVED_MAX 0x2000
36
37#define WM_IGNORE_SIZE (WM_XUSER + 1)
38#define WM_IGNORE_CLIP (WM_XUSER + 2)
39
40static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
41static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
42static void cfgtopalette(void);
43static void init_palette(void);
44static void init_fonts(int);
45
46static int extra_width, extra_height;
47
48static int pending_netevent = 0;
49static WPARAM pend_netevent_wParam = 0;
50static LPARAM pend_netevent_lParam = 0;
51static void enact_pending_netevent(void);
52
53#define FONT_NORMAL 0
54#define FONT_BOLD 1
55#define FONT_UNDERLINE 2
56#define FONT_BOLDUND 3
57#define FONT_OEM 4
58#define FONT_OEMBOLD 5
59#define FONT_OEMBOLDUND 6
60#define FONT_OEMUND 7
61static HFONT fonts[8];
62static enum {
63 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
64} bold_mode;
65static enum {
66 UND_LINE, UND_FONT
67} und_mode;
68static int descent;
69
70#define NCOLOURS 24
71static COLORREF colours[NCOLOURS];
72static HPALETTE pal;
73static LPLOGPALETTE logpal;
74static RGBTRIPLE defpal[NCOLOURS];
75
76static HWND hwnd;
77
78static int dbltime, lasttime, lastact;
79static Mouse_Button lastbtn;
80
81static char *window_name, *icon_name;
82
83int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
84 static char appname[] = "PuTTY";
85 WORD winsock_ver;
86 WSADATA wsadata;
87 WNDCLASS wndclass;
88 MSG msg;
89 int guess_width, guess_height;
90
91 putty_inst = inst;
92 flags = FLAG_VERBOSE | FLAG_WINDOWED | FLAG_CONNECTION;
93
94 winsock_ver = MAKEWORD(1, 1);
95 if (WSAStartup(winsock_ver, &wsadata)) {
96 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
97 MB_OK | MB_ICONEXCLAMATION);
98 return 1;
99 }
100 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
101 MessageBox(NULL, "WinSock version is incompatible with 1.1",
102 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
103 WSACleanup();
104 return 1;
105 }
106 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
107
108 InitCommonControls();
109
110 /*
111 * Process the command line.
112 */
113 {
114 char *p;
115
116 default_protocol = DEFAULT_PROTOCOL;
117 default_port = DEFAULT_PORT;
118
119 do_defaults(NULL);
120
121 p = cmdline;
122 while (*p && isspace(*p)) p++;
123
124 /*
125 * Process command line options first. Yes, this can be
126 * done better, and it will be as soon as I have the
127 * energy...
128 */
129 while (*p == '-') {
130 char *q = p + strcspn(p, " \t");
131 p++;
132 if (q == p + 3 &&
133 tolower(p[0]) == 's' &&
134 tolower(p[1]) == 's' &&
135 tolower(p[2]) == 'h') {
136 default_protocol = cfg.protocol = PROT_SSH;
137 default_port = cfg.port = 22;
138 } else if (q == p + 3 &&
139 tolower(p[0]) == 'l' &&
140 tolower(p[1]) == 'o' &&
141 tolower(p[2]) == 'g') {
142 logfile = "putty.log";
143 }
144 p = q + strspn(q, " \t");
145 }
146
147 /*
148 * An initial @ means to activate a saved session.
149 */
150 if (*p == '@') {
151 do_defaults (p+1);
152 if (!*cfg.host && !do_config()) {
153 WSACleanup();
154 return 0;
155 }
156 } else if (*p == '&') {
157 /*
158 * An initial & means we've been given a command line
159 * containing the hex value of a HANDLE for a file
160 * mapping object, which we must then extract as a
161 * config.
162 */
163 HANDLE filemap;
164 Config *cp;
165 if (sscanf(p+1, "%p", &filemap) == 1 &&
166 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
167 0, 0, sizeof(Config))) != NULL) {
168 cfg = *cp;
169 UnmapViewOfFile(cp);
170 CloseHandle(filemap);
171 } else if (!do_config()) {
172 WSACleanup();
173 return 0;
174 }
175 } else if (*p) {
176 char *q = p;
177 /*
178 * If the hostname starts with "telnet:", set the
179 * protocol to Telnet and process the string as a
180 * Telnet URL.
181 */
182 if (!strncmp(q, "telnet:", 7)) {
183 char c;
184
185 q += 7;
186 if (q[0] == '/' && q[1] == '/')
187 q += 2;
188 cfg.protocol = PROT_TELNET;
189 p = q;
190 while (*p && *p != ':' && *p != '/') p++;
191 c = *p;
192 if (*p)
193 *p++ = '\0';
194 if (c == ':')
195 cfg.port = atoi(p);
196 else
197 cfg.port = -1;
198 strncpy (cfg.host, q, sizeof(cfg.host)-1);
199 cfg.host[sizeof(cfg.host)-1] = '\0';
200 } else {
201 while (*p && !isspace(*p)) p++;
202 if (*p)
203 *p++ = '\0';
204 strncpy (cfg.host, q, sizeof(cfg.host)-1);
205 cfg.host[sizeof(cfg.host)-1] = '\0';
206 while (*p && isspace(*p)) p++;
207 if (*p)
208 cfg.port = atoi(p);
209 else
210 cfg.port = -1;
211 }
212 } else {
213 if (!do_config()) {
214 WSACleanup();
215 return 0;
216 }
217 }
218 }
219
220 /*
221 * Select protocol. This is farmed out into a table in a
222 * separate file to enable an ssh-free variant.
223 */
224 {
225 int i;
226 back = NULL;
227 for (i = 0; backends[i].backend != NULL; i++)
228 if (backends[i].protocol == cfg.protocol) {
229 back = backends[i].backend;
230 break;
231 }
232 if (back == NULL) {
233 MessageBox(NULL, "Unsupported protocol number found",
234 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
235 WSACleanup();
236 return 1;
237 }
238 }
239
240 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
241
242 if (!prev) {
243 wndclass.style = 0;
244 wndclass.lpfnWndProc = WndProc;
245 wndclass.cbClsExtra = 0;
246 wndclass.cbWndExtra = 0;
247 wndclass.hInstance = inst;
248 wndclass.hIcon = LoadIcon (inst,
249 MAKEINTRESOURCE(IDI_MAINICON));
250 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
251 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
252 wndclass.lpszMenuName = NULL;
253 wndclass.lpszClassName = appname;
254
255 RegisterClass (&wndclass);
256 }
257
258 hwnd = NULL;
259
260 savelines = cfg.savelines;
261 term_init();
262
263 cfgtopalette();
264
265 /*
266 * Guess some defaults for the window size. This all gets
267 * updated later, so we don't really care too much. However, we
268 * do want the font width/height guesses to correspond to a
269 * large font rather than a small one...
270 */
271
272 font_width = 10;
273 font_height = 20;
274 extra_width = 25;
275 extra_height = 28;
276 term_size (cfg.height, cfg.width, cfg.savelines);
277 guess_width = extra_width + font_width * cols;
278 guess_height = extra_height + font_height * rows;
279 {
280 RECT r;
281 HWND w = GetDesktopWindow();
282 GetWindowRect (w, &r);
283 if (guess_width > r.right - r.left)
284 guess_width = r.right - r.left;
285 if (guess_height > r.bottom - r.top)
286 guess_height = r.bottom - r.top;
287 }
288
289 {
290 int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
291 if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
292 if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
293 hwnd = CreateWindow (appname, appname,
294 winmode,
295 CW_USEDEFAULT, CW_USEDEFAULT,
296 guess_width, guess_height,
297 NULL, NULL, inst, NULL);
298 }
299
300 /*
301 * Initialise the fonts, simultaneously correcting the guesses
302 * for font_{width,height}.
303 */
304 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
305 und_mode = UND_FONT;
306 init_fonts(0);
307
308 /*
309 * Correct the guesses for extra_{width,height}.
310 */
311 {
312 RECT cr, wr;
313 GetWindowRect (hwnd, &wr);
314 GetClientRect (hwnd, &cr);
315 extra_width = wr.right - wr.left - cr.right + cr.left;
316 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
317 }
318
319 /*
320 * Resize the window, now we know what size we _really_ want it
321 * to be.
322 */
323 guess_width = extra_width + font_width * cols;
324 guess_height = extra_height + font_height * rows;
325 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
326 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
327 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
328
329 /*
330 * Initialise the scroll bar.
331 */
332 {
333 SCROLLINFO si;
334
335 si.cbSize = sizeof(si);
336 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
337 si.nMin = 0;
338 si.nMax = rows-1;
339 si.nPage = rows;
340 si.nPos = 0;
341 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
342 }
343
344 /*
345 * Start up the telnet connection.
346 */
347 {
348 char *error;
349 char msg[1024];
350 char *realhost;
351
352 error = back->init (hwnd, cfg.host, cfg.port, &realhost);
353 if (error) {
354 sprintf(msg, "Unable to open connection:\n%s", error);
355 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
356 return 0;
357 }
358 window_name = icon_name = NULL;
359 sprintf(msg, "%s - PuTTY", realhost);
360 set_title (msg);
361 set_icon (msg);
362 }
363
364 session_closed = FALSE;
365
366 /*
367 * Set up the input and output buffers.
368 */
369 inbuf_head = 0;
370 outbuf_reap = outbuf_head = 0;
371
372 /*
373 * Prepare the mouse handler.
374 */
375 lastact = MA_NOTHING;
376 lastbtn = MB_NOTHING;
377 dbltime = GetDoubleClickTime();
378
379 /*
380 * Set up the session-control options on the system menu.
381 */
382 {
383 HMENU m = GetSystemMenu (hwnd, FALSE);
384 HMENU p,s;
385 int i;
386
387 AppendMenu (m, MF_SEPARATOR, 0, 0);
388 if (cfg.protocol == PROT_TELNET) {
389 p = CreateMenu();
390 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
391 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
392 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
393 AppendMenu (p, MF_SEPARATOR, 0, 0);
394 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
395 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
396 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
397 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
398 AppendMenu (p, MF_SEPARATOR, 0, 0);
399 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
400 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
401 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
402 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
403 AppendMenu (p, MF_SEPARATOR, 0, 0);
404 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
405 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
406 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
407 AppendMenu (m, MF_SEPARATOR, 0, 0);
408 }
409 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
410 AppendMenu (m, MF_SEPARATOR, 0, 0);
411 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
412 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
413 s = CreateMenu();
414 get_sesslist(TRUE);
415 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
416 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
417 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
418 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
419 AppendMenu (m, MF_SEPARATOR, 0, 0);
420 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
421 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
422 AppendMenu (m, MF_SEPARATOR, 0, 0);
423 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
424 }
425
426 /*
427 * Finally show the window!
428 */
429 ShowWindow (hwnd, show);
430
431 /*
432 * Set the palette up.
433 */
434 pal = NULL;
435 logpal = NULL;
436 init_palette();
437
438 has_focus = (GetForegroundWindow() == hwnd);
439 UpdateWindow (hwnd);
440
441 {
442 int timer_id = 0, long_timer = 0;
443
444 while (GetMessage (&msg, NULL, 0, 0) == 1) {
445 /* Sometimes DispatchMessage calls routines that use their own
446 * GetMessage loop, setup this timer so we get some control back.
447 *
448 * Also call term_update() from the timer so that if the host
449 * is sending data flat out we still do redraws.
450 */
451 if(timer_id && long_timer) {
452 KillTimer(hwnd, timer_id);
453 long_timer = timer_id = 0;
454 }
455 if(!timer_id)
456 timer_id = SetTimer(hwnd, 1, 20, NULL);
457 DispatchMessage (&msg);
458
459 /* This is too fast, but I'll leave it for now 'cause it shows
460 * how often term_update is called (far too often at times!)
461 */
462 term_blink(0);
463
464 /* Send the paste buffer if there's anything to send */
465 term_paste();
466
467 /* If there's nothing new in the queue then we can do everything
468 * we've delayed, reading the socket, writing, and repainting
469 * the window.
470 */
471 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
472 if (pending_netevent) {
473 enact_pending_netevent();
474
475 term_blink(1);
476 }
477 } else continue;
478 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
479 if (timer_id) {
480 KillTimer(hwnd, timer_id);
481 timer_id = 0;
482 }
483 if (inbuf_head)
484 term_out();
485 term_update();
486 if (!has_focus)
487 timer_id = SetTimer(hwnd, 1, 2000, NULL);
488 else if (cfg.blinktext)
489 timer_id = SetTimer(hwnd, 1, 250, NULL);
490 else
491 timer_id = SetTimer(hwnd, 1, 500, NULL);
492 long_timer = 1;
493 }
494 }
495 }
496
497 /*
498 * Clean up.
499 */
500 {
501 int i;
502 for (i=0; i<8; i++)
503 if (fonts[i])
504 DeleteObject(fonts[i]);
505 }
506 sfree(logpal);
507 if (pal)
508 DeleteObject(pal);
509 WSACleanup();
510
511 if (cfg.protocol == PROT_SSH) {
512 random_save_seed();
513#ifdef MSCRYPTOAPI
514 crypto_wrapup();
515#endif
516 }
517
518 return msg.wParam;
519}
520
521/*
522 * Actually do the job requested by a WM_NETEVENT
523 */
524static void enact_pending_netevent(void) {
525 int i;
526 pending_netevent = FALSE;
527 i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
528
529 if (i < 0) {
530 char buf[1024];
531 switch (WSABASEERR + (-i) % 10000) {
532 case WSAECONNRESET:
533 sprintf(buf, "Connection reset by peer");
534 break;
535 default:
536 sprintf(buf, "Unexpected network error %d", -i);
537 break;
538 }
539 MessageBox(hwnd, buf, "PuTTY Fatal Error",
540 MB_ICONERROR | MB_OK);
541 PostQuitMessage(1);
542 } else if (i == 0) {
543 if (cfg.close_on_exit)
544 PostQuitMessage(0);
545 else {
546 session_closed = TRUE;
547 MessageBox(hwnd, "Connection closed by remote host",
548 "PuTTY", MB_OK | MB_ICONINFORMATION);
549 SetWindowText (hwnd, "PuTTY (inactive)");
550 }
551 }
552}
553
554/*
555 * Copy the colour palette from the configuration data into defpal.
556 * This is non-trivial because the colour indices are different.
557 */
558static void cfgtopalette(void) {
559 int i;
560 static const int ww[] = {
561 6, 7, 8, 9, 10, 11, 12, 13,
562 14, 15, 16, 17, 18, 19, 20, 21,
563 0, 1, 2, 3, 4, 4, 5, 5
564 };
565
566 for (i=0; i<24; i++) {
567 int w = ww[i];
568 defpal[i].rgbtRed = cfg.colours[w][0];
569 defpal[i].rgbtGreen = cfg.colours[w][1];
570 defpal[i].rgbtBlue = cfg.colours[w][2];
571 }
572}
573
574/*
575 * Set up the colour palette.
576 */
577static void init_palette(void) {
578 int i;
579 HDC hdc = GetDC (hwnd);
580 if (hdc) {
581 if (cfg.try_palette &&
582 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
583 logpal = smalloc(sizeof(*logpal)
584 - sizeof(logpal->palPalEntry)
585 + NCOLOURS * sizeof(PALETTEENTRY));
586 logpal->palVersion = 0x300;
587 logpal->palNumEntries = NCOLOURS;
588 for (i = 0; i < NCOLOURS; i++) {
589 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
590 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
591 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
592 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
593 }
594 pal = CreatePalette (logpal);
595 if (pal) {
596 SelectPalette (hdc, pal, FALSE);
597 RealizePalette (hdc);
598 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
599 FALSE);
600 }
601 }
602 ReleaseDC (hwnd, hdc);
603 }
604 if (pal)
605 for (i=0; i<NCOLOURS; i++)
606 colours[i] = PALETTERGB(defpal[i].rgbtRed,
607 defpal[i].rgbtGreen,
608 defpal[i].rgbtBlue);
609 else
610 for(i=0; i<NCOLOURS; i++)
611 colours[i] = RGB(defpal[i].rgbtRed,
612 defpal[i].rgbtGreen,
613 defpal[i].rgbtBlue);
614}
615
616/*
617 * Initialise all the fonts we will need. There may be as many as
618 * eight or as few as one. We also:
619 *
620 * - check the font width and height, correcting our guesses if
621 * necessary.
622 *
623 * - verify that the bold font is the same width as the ordinary
624 * one, and engage shadow bolding if not.
625 *
626 * - verify that the underlined font is the same width as the
627 * ordinary one (manual underlining by means of line drawing can
628 * be done in a pinch).
629 */
630static void init_fonts(int pick_width) {
631 TEXTMETRIC tm;
632 int i;
633 int fsize[8];
634 HDC hdc;
635 int fw_dontcare, fw_bold;
636 int firstchar = ' ';
637
638#ifdef CHECKOEMFONT
639font_messup:
640#endif
641 for (i=0; i<8; i++)
642 fonts[i] = NULL;
643
644 if (cfg.fontisbold) {
645 fw_dontcare = FW_BOLD;
646 fw_bold = FW_BLACK;
647 } else {
648 fw_dontcare = FW_DONTCARE;
649 fw_bold = FW_BOLD;
650 }
651
652 hdc = GetDC(hwnd);
653
654 font_height = cfg.fontheight;
655 font_width = pick_width;
656
657#define f(i,c,w,u) \
658 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
659 c, OUT_DEFAULT_PRECIS, \
660 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
661 FIXED_PITCH | FF_DONTCARE, cfg.font)
662
663 if (cfg.vtmode != VT_OEMONLY) {
664 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
665
666 SelectObject (hdc, fonts[FONT_NORMAL]);
667 GetTextMetrics(hdc, &tm);
668 font_height = tm.tmHeight;
669 font_width = tm.tmAveCharWidth;
670
671 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
672
673 if (bold_mode == BOLD_FONT) {
674 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
675 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
676 }
677
678 if (cfg.vtmode == VT_OEMANSI) {
679 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
680 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
681
682 if (bold_mode == BOLD_FONT) {
683 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
684 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
685 }
686 }
687 }
688 else
689 {
690 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
691
692 SelectObject (hdc, fonts[FONT_OEM]);
693 GetTextMetrics(hdc, &tm);
694 font_height = tm.tmHeight;
695 font_width = tm.tmAveCharWidth;
696
697 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
698
699 if (bold_mode == BOLD_FONT) {
700 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
701 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
702 }
703 }
704#undef f
705
706 descent = tm.tmAscent + 1;
707 if (descent >= font_height)
708 descent = font_height - 1;
709 firstchar = tm.tmFirstChar;
710
711 for (i=0; i<8; i++) {
712 if (fonts[i]) {
713 if (SelectObject (hdc, fonts[i]) &&
714 GetTextMetrics(hdc, &tm) )
715 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
716 else fsize[i] = -i;
717 }
718 else fsize[i] = -i;
719 }
720
721 ReleaseDC (hwnd, hdc);
722
723 /* ... This is wrong in OEM only mode */
724 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
725 (bold_mode == BOLD_FONT &&
726 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
727 und_mode = UND_LINE;
728 DeleteObject (fonts[FONT_UNDERLINE]);
729 if (bold_mode == BOLD_FONT)
730 DeleteObject (fonts[FONT_BOLDUND]);
731 }
732
733 if (bold_mode == BOLD_FONT &&
734 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
735 bold_mode = BOLD_SHADOW;
736 DeleteObject (fonts[FONT_BOLD]);
737 if (und_mode == UND_FONT)
738 DeleteObject (fonts[FONT_BOLDUND]);
739 }
740
741#ifdef CHECKOEMFONT
742 /* With the fascist font painting it doesn't matter if the linedraw font
743 * isn't exactly the right size anymore so we don't have to check this.
744 */
745 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
746 if( cfg.fontcharset == OEM_CHARSET )
747 {
748 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
749 "different sizes. Using OEM-only mode instead",
750 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
751 cfg.vtmode = VT_OEMONLY;
752 }
753 else if( firstchar < ' ' )
754 {
755 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
756 "different sizes. Using XTerm mode instead",
757 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
758 cfg.vtmode = VT_XWINDOWS;
759 }
760 else
761 {
762 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
763 "different sizes. Using ISO8859-1 mode instead",
764 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
765 cfg.vtmode = VT_POORMAN;
766 }
767
768 for (i=0; i<8; i++)
769 if (fonts[i])
770 DeleteObject (fonts[i]);
771 goto font_messup;
772 }
773#endif
774}
775
776void request_resize (int w, int h, int refont) {
777 int width, height;
778
779 /* If the window is maximized supress resizing attempts */
780 if(IsZoomed(hwnd)) return;
781
782#ifdef CHECKOEMFONT
783 /* Don't do this in OEMANSI, you may get disable messages */
784 if (refont && w != cols && (cols==80 || cols==132)
785 && cfg.vtmode != VT_OEMANSI)
786#else
787 if (refont && w != cols && (cols==80 || cols==132))
788#endif
789 {
790 /* If font width too big for screen should we shrink the font more ? */
791 if (w==132)
792 font_width = ((font_width*cols+w/2)/w);
793 else
794 font_width = 0;
795 {
796 int i;
797 for (i=0; i<8; i++)
798 if (fonts[i])
799 DeleteObject(fonts[i]);
800 }
801 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
802 und_mode = UND_FONT;
803 init_fonts(font_width);
804 }
805 else
806 {
807 static int first_time = 1;
808 static RECT ss;
809
810 switch(first_time)
811 {
812 case 1:
813 /* Get the size of the screen */
814 if (GetClientRect(GetDesktopWindow(),&ss))
815 /* first_time = 0 */;
816 else { first_time = 2; break; }
817 case 0:
818 /* Make sure the values are sane */
819 width = (ss.right-ss.left-extra_width ) / font_width;
820 height = (ss.bottom-ss.top-extra_height ) / font_height;
821
822 if (w>width) w=width;
823 if (h>height) h=height;
824 if (w<15) w = 15;
825 if (h<1) w = 1;
826 }
827 }
828
829 width = extra_width + font_width * w;
830 height = extra_height + font_height * h;
831
832 SetWindowPos (hwnd, NULL, 0, 0, width, height,
833 SWP_NOACTIVATE | SWP_NOCOPYBITS |
834 SWP_NOMOVE | SWP_NOZORDER);
835}
836
837static void click (Mouse_Button b, int x, int y) {
838 int thistime = GetMessageTime();
839
840 if (lastbtn == b && thistime - lasttime < dbltime) {
841 lastact = (lastact == MA_CLICK ? MA_2CLK :
842 lastact == MA_2CLK ? MA_3CLK :
843 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
844 } else {
845 lastbtn = b;
846 lastact = MA_CLICK;
847 }
848 if (lastact != MA_NOTHING)
849 term_mouse (b, lastact, x, y);
850 lasttime = thistime;
851}
852
853static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
854 WPARAM wParam, LPARAM lParam) {
855 HDC hdc;
856 static int ignore_size = FALSE;
857 static int ignore_clip = FALSE;
858 static int just_reconfigged = FALSE;
859
860 switch (message) {
861 case WM_TIMER:
862 if (pending_netevent)
863 enact_pending_netevent();
864 if (inbuf_head)
865 term_out();
866 term_update();
867 return 0;
868 case WM_CREATE:
869 break;
870 case WM_CLOSE:
871 if (!cfg.warn_on_close || session_closed ||
872 MessageBox(hwnd, "Are you sure you want to close this session?",
873 "PuTTY Exit Confirmation",
874 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
875 DestroyWindow(hwnd);
876 return 0;
877 case WM_DESTROY:
878 PostQuitMessage (0);
879 return 0;
880 case WM_SYSCOMMAND:
881 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
882 case IDM_SHOWLOG:
883 showeventlog(hwnd);
884 break;
885 case IDM_NEWSESS:
886 case IDM_DUPSESS:
887 case IDM_SAVEDSESS:
888 {
889 char b[2048];
890 char c[30], *cl;
891 int freecl = FALSE;
892 STARTUPINFO si;
893 PROCESS_INFORMATION pi;
894 HANDLE filemap = NULL;
895
896 if (wParam == IDM_DUPSESS) {
897 /*
898 * Allocate a file-mapping memory chunk for the
899 * config structure.
900 */
901 SECURITY_ATTRIBUTES sa;
902 Config *p;
903
904 sa.nLength = sizeof(sa);
905 sa.lpSecurityDescriptor = NULL;
906 sa.bInheritHandle = TRUE;
907 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
908 &sa,
909 PAGE_READWRITE,
910 0,
911 sizeof(Config),
912 NULL);
913 if (filemap) {
914 p = (Config *)MapViewOfFile(filemap,
915 FILE_MAP_WRITE,
916 0, 0, sizeof(Config));
917 if (p) {
918 *p = cfg; /* structure copy */
919 UnmapViewOfFile(p);
920 }
921 }
922 sprintf(c, "putty &%p", filemap);
923 cl = c;
924 } else if (wParam == IDM_SAVEDSESS) {
925 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
926 cl = malloc(16 + strlen(session)); /* 8, but play safe */
927 if (!cl)
928 cl = NULL; /* not a very important failure mode */
929 else {
930 sprintf(cl, "putty @%s", session);
931 freecl = TRUE;
932 }
933 } else
934 cl = NULL;
935
936 GetModuleFileName (NULL, b, sizeof(b)-1);
937 si.cb = sizeof(si);
938 si.lpReserved = NULL;
939 si.lpDesktop = NULL;
940 si.lpTitle = NULL;
941 si.dwFlags = 0;
942 si.cbReserved2 = 0;
943 si.lpReserved2 = NULL;
944 CreateProcess (b, cl, NULL, NULL, TRUE,
945 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
946
947 if (filemap)
948 CloseHandle(filemap);
949 if (freecl)
950 free(cl);
951 }
952 break;
953 case IDM_RECONF:
954 if (!do_reconfig(hwnd))
955 break;
956 just_reconfigged = TRUE;
957 {
958 int i;
959 for (i=0; i<8; i++)
960 if (fonts[i])
961 DeleteObject(fonts[i]);
962 }
963 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
964 und_mode = UND_FONT;
965 init_fonts(0);
966 sfree(logpal);
967 /* Telnet will change local echo -> remote if the remote asks */
968 if (cfg.protocol != PROT_TELNET)
969 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
970 if (pal)
971 DeleteObject(pal);
972 logpal = NULL;
973 pal = NULL;
974 cfgtopalette();
975 init_palette();
976
977 /* Enable or disable the scroll bar, etc */
978 {
979 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
980
981 nflg = flag;
982 if (cfg.scrollbar) nflg |= WS_VSCROLL;
983 else nflg &= ~WS_VSCROLL;
984 if (cfg.locksize)
985 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
986 else
987 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
988
989 if (nflg != flag)
990 {
991 RECT cr, wr;
992
993 SetWindowLong(hwnd, GWL_STYLE, nflg);
994 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
995 SetWindowPos(hwnd, NULL, 0,0,0,0,
996 SWP_NOACTIVATE|SWP_NOCOPYBITS|
997 SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER|
998 SWP_FRAMECHANGED);
999
1000 GetWindowRect (hwnd, &wr);
1001 GetClientRect (hwnd, &cr);
1002 extra_width = wr.right - wr.left - cr.right + cr.left;
1003 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1004 }
1005 }
1006
1007 term_size(cfg.height, cfg.width, cfg.savelines);
1008 InvalidateRect(hwnd, NULL, TRUE);
1009 SetWindowPos (hwnd, NULL, 0, 0,
1010 extra_width + font_width * cfg.width,
1011 extra_height + font_height * cfg.height,
1012 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1013 SWP_NOMOVE | SWP_NOZORDER);
1014 if (IsIconic(hwnd)) {
1015 SetWindowText (hwnd,
1016 cfg.win_name_always ? window_name : icon_name);
1017 }
1018 break;
1019 case IDM_CLRSB:
1020 term_clrsb();
1021 break;
1022 case IDM_RESET:
1023 term_pwron();
1024 break;
1025 case IDM_TEL_AYT: back->special (TS_AYT); break;
1026 case IDM_TEL_BRK: back->special (TS_BRK); break;
1027 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1028 case IDM_TEL_EC: back->special (TS_EC); break;
1029 case IDM_TEL_EL: back->special (TS_EL); break;
1030 case IDM_TEL_GA: back->special (TS_GA); break;
1031 case IDM_TEL_NOP: back->special (TS_NOP); break;
1032 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1033 case IDM_TEL_AO: back->special (TS_AO); break;
1034 case IDM_TEL_IP: back->special (TS_IP); break;
1035 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1036 case IDM_TEL_EOR: back->special (TS_EOR); break;
1037 case IDM_TEL_EOF: back->special (TS_EOF); break;
1038 case IDM_ABOUT:
1039 showabout (hwnd);
1040 break;
1041 default:
1042 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1043 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1044 }
1045 }
1046 break;
1047
1048#define X_POS(l) ((int)(short)LOWORD(l))
1049#define Y_POS(l) ((int)(short)HIWORD(l))
1050
1051#define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1052#define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1053
1054 case WM_LBUTTONDOWN:
1055 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1056 TO_CHR_Y(Y_POS(lParam)));
1057 SetCapture(hwnd);
1058 return 0;
1059 case WM_LBUTTONUP:
1060 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1061 TO_CHR_Y(Y_POS(lParam)));
1062 ReleaseCapture();
1063 return 0;
1064 case WM_MBUTTONDOWN:
1065 SetCapture(hwnd);
1066 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1067 TO_CHR_X(X_POS(lParam)),
1068 TO_CHR_Y(Y_POS(lParam)));
1069 return 0;
1070 case WM_MBUTTONUP:
1071 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1072 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1073 TO_CHR_Y(Y_POS(lParam)));
1074 ReleaseCapture();
1075 return 0;
1076 case WM_RBUTTONDOWN:
1077 SetCapture(hwnd);
1078 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1079 TO_CHR_X(X_POS(lParam)),
1080 TO_CHR_Y(Y_POS(lParam)));
1081 return 0;
1082 case WM_RBUTTONUP:
1083 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1084 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1085 TO_CHR_Y(Y_POS(lParam)));
1086 ReleaseCapture();
1087 return 0;
1088 case WM_MOUSEMOVE:
1089 /*
1090 * Add the mouse position and message time to the random
1091 * number noise, if we're using ssh.
1092 */
1093 if (cfg.protocol == PROT_SSH)
1094 noise_ultralight(lParam);
1095
1096 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1097 Mouse_Button b;
1098 if (wParam & MK_LBUTTON)
1099 b = MB_SELECT;
1100 else if (wParam & MK_MBUTTON)
1101 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1102 else
1103 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1104 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1105 TO_CHR_Y(Y_POS(lParam)));
1106 }
1107 return 0;
1108 case WM_IGNORE_CLIP:
1109 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1110 break;
1111 case WM_DESTROYCLIPBOARD:
1112 if (!ignore_clip)
1113 term_deselect();
1114 ignore_clip = FALSE;
1115 return 0;
1116 case WM_PAINT:
1117 {
1118 PAINTSTRUCT p;
1119 hdc = BeginPaint (hwnd, &p);
1120 if (pal) {
1121 SelectPalette (hdc, pal, TRUE);
1122 RealizePalette (hdc);
1123 }
1124 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1125 p.rcPaint.right, p.rcPaint.bottom);
1126 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1127 SelectObject (hdc, GetStockObject(WHITE_PEN));
1128 EndPaint (hwnd, &p);
1129 }
1130 return 0;
1131 case WM_NETEVENT:
1132 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1133 * but the only one that's likely to try to overload us is FD_READ.
1134 * This means buffering just one is fine.
1135 */
1136 if (pending_netevent)
1137 enact_pending_netevent();
1138
1139 pending_netevent = TRUE;
1140 pend_netevent_wParam=wParam;
1141 pend_netevent_lParam=lParam;
1142 return 0;
1143 case WM_SETFOCUS:
1144 has_focus = TRUE;
1145 term_out();
1146 term_update();
1147 break;
1148 case WM_KILLFOCUS:
1149 has_focus = FALSE;
1150 term_out();
1151 term_update();
1152 break;
1153 case WM_IGNORE_SIZE:
1154 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1155 break;
1156 case WM_ENTERSIZEMOVE:
1157 EnableSizeTip(1);
1158 break;
1159 case WM_EXITSIZEMOVE:
1160 EnableSizeTip(0);
1161 break;
1162 case WM_SIZING:
1163 {
1164 int width, height, w, h, ew, eh;
1165 LPRECT r = (LPRECT)lParam;
1166
1167 width = r->right - r->left - extra_width;
1168 height = r->bottom - r->top - extra_height;
1169 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1170 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1171 UpdateSizeTip(hwnd, w, h);
1172 ew = width - w * font_width;
1173 eh = height - h * font_height;
1174 if (ew != 0) {
1175 if (wParam == WMSZ_LEFT ||
1176 wParam == WMSZ_BOTTOMLEFT ||
1177 wParam == WMSZ_TOPLEFT)
1178 r->left += ew;
1179 else
1180 r->right -= ew;
1181 }
1182 if (eh != 0) {
1183 if (wParam == WMSZ_TOP ||
1184 wParam == WMSZ_TOPRIGHT ||
1185 wParam == WMSZ_TOPLEFT)
1186 r->top += eh;
1187 else
1188 r->bottom -= eh;
1189 }
1190 if (ew || eh)
1191 return 1;
1192 else
1193 return 0;
1194 }
1195 /* break; (never reached) */
1196 case WM_SIZE:
1197 if (wParam == SIZE_MINIMIZED) {
1198 SetWindowText (hwnd,
1199 cfg.win_name_always ? window_name : icon_name);
1200 break;
1201 }
1202 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1203 SetWindowText (hwnd, window_name);
1204 if (!ignore_size) {
1205 int width, height, w, h;
1206#if 0 /* we have fixed this using WM_SIZING now */
1207 int ew, eh;
1208#endif
1209
1210 width = LOWORD(lParam);
1211 height = HIWORD(lParam);
1212 w = width / font_width; if (w < 1) w = 1;
1213 h = height / font_height; if (h < 1) h = 1;
1214#if 0 /* we have fixed this using WM_SIZING now */
1215 ew = width - w * font_width;
1216 eh = height - h * font_height;
1217 if (ew != 0 || eh != 0) {
1218 RECT r;
1219 GetWindowRect (hwnd, &r);
1220 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1221 SetWindowPos (hwnd, NULL, 0, 0,
1222 r.right - r.left - ew, r.bottom - r.top - eh,
1223 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1224 }
1225#endif
1226 if (w != cols || h != rows || just_reconfigged) {
1227 term_invalidate();
1228 term_size (h, w, cfg.savelines);
1229 back->size();
1230 just_reconfigged = FALSE;
1231 }
1232 }
1233 ignore_size = FALSE;
1234 return 0;
1235 case WM_VSCROLL:
1236 switch (LOWORD(wParam)) {
1237 case SB_BOTTOM: term_scroll(-1, 0); break;
1238 case SB_TOP: term_scroll(+1, 0); break;
1239 case SB_LINEDOWN: term_scroll (0, +1); break;
1240 case SB_LINEUP: term_scroll (0, -1); break;
1241 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1242 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1243 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1244 term_scroll (1, HIWORD(wParam)); break;
1245 }
1246 break;
1247 case WM_PALETTECHANGED:
1248 if ((HWND) wParam != hwnd && pal != NULL) {
1249 HDC hdc = get_ctx();
1250 if (hdc) {
1251 if (RealizePalette (hdc) > 0)
1252 UpdateColors (hdc);
1253 free_ctx (hdc);
1254 }
1255 }
1256 break;
1257 case WM_QUERYNEWPALETTE:
1258 if (pal != NULL) {
1259 HDC hdc = get_ctx();
1260 if (hdc) {
1261 if (RealizePalette (hdc) > 0)
1262 UpdateColors (hdc);
1263 free_ctx (hdc);
1264 return TRUE;
1265 }
1266 }
1267 return FALSE;
1268 case WM_KEYDOWN:
1269 case WM_SYSKEYDOWN:
1270 case WM_KEYUP:
1271 case WM_SYSKEYUP:
1272 /*
1273 * Add the scan code and keypress timing to the random
1274 * number noise, if we're using ssh.
1275 */
1276 if (cfg.protocol == PROT_SSH)
1277 noise_ultralight(lParam);
1278
1279 /*
1280 * We don't do TranslateMessage since it disassociates the
1281 * resulting CHAR message from the KEYDOWN that sparked it,
1282 * which we occasionally don't want. Instead, we process
1283 * KEYDOWN, and call the Win32 translator functions so that
1284 * we get the translations under _our_ control.
1285 */
1286 {
1287 unsigned char buf[20];
1288 int len;
1289
1290 len = TranslateKey (message, wParam, lParam, buf);
1291 if (len == -1)
1292 return DefWindowProc (hwnd, message, wParam, lParam);
1293 ldisc->send (buf, len);
1294 }
1295 return 0;
1296 case WM_CHAR:
1297 case WM_SYSCHAR:
1298 /*
1299 * Nevertheless, we are prepared to deal with WM_CHAR
1300 * messages, should they crop up. So if someone wants to
1301 * post the things to us as part of a macro manoeuvre,
1302 * we're ready to cope.
1303 */
1304 {
1305 char c = xlat_kbd2tty((unsigned char)wParam);
1306 ldisc->send (&c, 1);
1307 }
1308 return 0;
1309 }
1310
1311 return DefWindowProc (hwnd, message, wParam, lParam);
1312}
1313
1314/*
1315 * Draw a line of text in the window, at given character
1316 * coordinates, in given attributes.
1317 *
1318 * We are allowed to fiddle with the contents of `text'.
1319 */
1320void do_text (Context ctx, int x, int y, char *text, int len,
1321 unsigned long attr, int lattr) {
1322 COLORREF fg, bg, t;
1323 int nfg, nbg, nfont;
1324 HDC hdc = ctx;
1325 RECT line_box;
1326 int force_manual_underline = 0;
1327 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1328static int *IpDx = 0, IpDxLEN = 0;;
1329
1330 if (len>IpDxLEN || IpDx[0] != fnt_width) {
1331 int i;
1332 if (len>IpDxLEN) {
1333 sfree(IpDx);
1334 IpDx = smalloc((len+16)*sizeof(int));
1335 IpDxLEN = (len+16);
1336 }
1337 for(i=0; i<IpDxLEN; i++)
1338 IpDx[i] = fnt_width;
1339 }
1340
1341 x *= fnt_width;
1342 y *= font_height;
1343
1344 if (attr & ATTR_ACTCURS) {
1345 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1346 attr ^= ATTR_CUR_XOR;
1347 }
1348
1349 nfont = 0;
1350 if (cfg.vtmode == VT_OEMONLY)
1351 nfont |= FONT_OEM;
1352
1353 /*
1354 * Map high-half characters in order to approximate ISO using
1355 * OEM character set. No characters are missing if the OEM codepage
1356 * is CP850.
1357 */
1358 if (nfont & FONT_OEM) {
1359 int i;
1360 for (i=0; i<len; i++)
1361 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1362#if 0
1363 /* This is CP850 ... perfect translation */
1364 static const char oemhighhalf[] =
1365 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1366 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1367 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1368 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1369 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1370 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1371 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1372 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1373 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1374 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1375 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1376 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1377 ;
1378#endif
1379 /* This is CP437 ... junk translation */
1380 static const unsigned char oemhighhalf[] = {
1381 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1382 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1383 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1384 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1385 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1386 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1387 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1388 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1389 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1390 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1391 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1392 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1393 };
1394
1395 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1396 }
1397 }
1398
1399 if (attr & ATTR_GBCHR) {
1400 int i;
1401 /*
1402 * GB mapping: map # to pound, and everything else stays
1403 * normal.
1404 */
1405 for (i=0; i<len; i++)
1406 if (text[i] == '#')
1407 text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
1408 } else if (attr & ATTR_LINEDRW) {
1409 int i;
1410 /* ISO 8859-1 */
1411 static const char poorman[] =
1412 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1413
1414 /* CP437 */
1415 static const char oemmap_437[] =
1416 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1417 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1418
1419 /* CP850 */
1420 static const char oemmap_850[] =
1421 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1422 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1423
1424 /* Poor windows font ... eg: windows courier */
1425 static const char oemmap[] =
1426 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1427 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1428
1429 /*
1430 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1431 * VT100 line drawing chars; everything else stays normal.
1432 */
1433 switch (cfg.vtmode) {
1434 case VT_XWINDOWS:
1435 for (i=0; i<len; i++)
1436 if (text[i] >= '\x60' && text[i] <= '\x7E')
1437 text[i] += '\x01' - '\x60';
1438 break;
1439 case VT_OEMANSI:
1440 /* Make sure we actually have an OEM font */
1441 if (fonts[nfont|FONT_OEM]) {
1442 case VT_OEMONLY:
1443 nfont |= FONT_OEM;
1444 for (i=0; i<len; i++)
1445 if (text[i] >= '\x60' && text[i] <= '\x7E')
1446 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1447 break;
1448 }
1449 case VT_POORMAN:
1450 for (i=0; i<len; i++)
1451 if (text[i] >= '\x60' && text[i] <= '\x7E')
1452 text[i] = poorman[(unsigned char)text[i] - 0x60];
1453 break;
1454 }
1455 }
1456
1457 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1458 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1459 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1460 nfont |= FONT_BOLD;
1461 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1462 nfont |= FONT_UNDERLINE;
1463 if (!fonts[nfont])
1464 {
1465 if (nfont&FONT_UNDERLINE)
1466 force_manual_underline = 1;
1467 /* Don't do the same for manual bold, it could be bad news. */
1468
1469 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1470 }
1471 if (attr & ATTR_REVERSE) {
1472 t = nfg; nfg = nbg; nbg = t;
1473 }
1474 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1475 nfg++;
1476 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1477 nbg++;
1478 fg = colours[nfg];
1479 bg = colours[nbg];
1480 SelectObject (hdc, fonts[nfont]);
1481 SetTextColor (hdc, fg);
1482 SetBkColor (hdc, bg);
1483 SetBkMode (hdc, OPAQUE);
1484 line_box.left = x;
1485 line_box.top = y;
1486 line_box.right = x+fnt_width*len;
1487 line_box.bottom = y+font_height;
1488 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1489 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1490 SetBkMode (hdc, TRANSPARENT);
1491
1492 /* GRR: This draws the character outside it's box and can leave
1493 * 'droppings' even with the clip box! I suppose I could loop it
1494 * one character at a time ... yuk.
1495 *
1496 * Or ... I could do a test print with "W", and use +1 or -1 for this
1497 * shift depending on if the leftmost column is blank...
1498 */
1499 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1500 }
1501 if (force_manual_underline ||
1502 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1503 HPEN oldpen;
1504 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1505 MoveToEx (hdc, x, y+descent, NULL);
1506 LineTo (hdc, x+len*fnt_width, y+descent);
1507 oldpen = SelectObject (hdc, oldpen);
1508 DeleteObject (oldpen);
1509 }
1510 if (attr & ATTR_PASCURS) {
1511 POINT pts[5];
1512 HPEN oldpen;
1513 pts[0].x = pts[1].x = pts[4].x = x;
1514 pts[2].x = pts[3].x = x+fnt_width-1;
1515 pts[0].y = pts[3].y = pts[4].y = y;
1516 pts[1].y = pts[2].y = y+font_height-1;
1517 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1518 Polyline (hdc, pts, 5);
1519 oldpen = SelectObject (hdc, oldpen);
1520 DeleteObject (oldpen);
1521 }
1522}
1523
1524static int check_compose(int first, int second) {
1525
1526 static char * composetbl[] = {
1527