Terminal now uses unsorted counted tree234 as its basic data type.
[u/mdw/putty] / window.c
1 #include <windows.h>
2 #include <imm.h>
3 #include <commctrl.h>
4 #ifndef AUTO_WINSOCK
5 #ifdef WINSOCK_TWO
6 #include <winsock2.h>
7 #else
8 #include <winsock.h>
9 #endif
10 #endif
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <ctype.h>
14 #include <time.h>
15
16 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
17 #include "putty.h"
18 #include "winstuff.h"
19 #include "storage.h"
20 #include "win_res.h"
21
22 #define IDM_SHOWLOG 0x0010
23 #define IDM_NEWSESS 0x0020
24 #define IDM_DUPSESS 0x0030
25 #define IDM_RECONF 0x0040
26 #define IDM_CLRSB 0x0050
27 #define IDM_RESET 0x0060
28 #define IDM_TEL_AYT 0x0070
29 #define IDM_TEL_BRK 0x0080
30 #define IDM_TEL_SYNCH 0x0090
31 #define IDM_TEL_EC 0x00a0
32 #define IDM_TEL_EL 0x00b0
33 #define IDM_TEL_GA 0x00c0
34 #define IDM_TEL_NOP 0x00d0
35 #define IDM_TEL_ABORT 0x00e0
36 #define IDM_TEL_AO 0x00f0
37 #define IDM_TEL_IP 0x0100
38 #define IDM_TEL_SUSP 0x0110
39 #define IDM_TEL_EOR 0x0120
40 #define IDM_TEL_EOF 0x0130
41 #define IDM_ABOUT 0x0140
42 #define IDM_SAVEDSESS 0x0150
43 #define IDM_COPYALL 0x0160
44
45 #define IDM_SESSLGP 0x0250 /* log type printable */
46 #define IDM_SESSLGA 0x0260 /* log type all chars */
47 #define IDM_SESSLGE 0x0270 /* log end */
48 #define IDM_SAVED_MIN 0x1000
49 #define IDM_SAVED_MAX 0x2000
50
51 #define WM_IGNORE_SIZE (WM_XUSER + 1)
52 #define WM_IGNORE_CLIP (WM_XUSER + 2)
53
54 /* Needed for Chinese support and apparently not always defined. */
55 #ifndef VK_PROCESSKEY
56 #define VK_PROCESSKEY 0xE5
57 #endif
58
59 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
60 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
61 static void cfgtopalette(void);
62 static void init_palette(void);
63 static void init_fonts(int);
64
65 static int extra_width, extra_height;
66
67 static int pending_netevent = 0;
68 static WPARAM pend_netevent_wParam = 0;
69 static LPARAM pend_netevent_lParam = 0;
70 static void enact_pending_netevent(void);
71
72 static time_t last_movement = 0;
73
74 #define FONT_NORMAL 0
75 #define FONT_BOLD 1
76 #define FONT_UNDERLINE 2
77 #define FONT_BOLDUND 3
78 #define FONT_OEM 4
79 #define FONT_OEMBOLD 5
80 #define FONT_OEMBOLDUND 6
81 #define FONT_OEMUND 7
82 static HFONT fonts[8];
83 static int font_needs_hand_underlining;
84 static enum {
85 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
86 } bold_mode;
87 static enum {
88 UND_LINE, UND_FONT
89 } und_mode;
90 static int descent;
91
92 #define NCOLOURS 24
93 static COLORREF colours[NCOLOURS];
94 static HPALETTE pal;
95 static LPLOGPALETTE logpal;
96 static RGBTRIPLE defpal[NCOLOURS];
97
98 static HWND hwnd;
99
100 static HBITMAP caretbm;
101
102 static int dbltime, lasttime, lastact;
103 static Mouse_Button lastbtn;
104
105 static char *window_name, *icon_name;
106
107 static int compose_state = 0;
108
109 /* Dummy routine, only required in plink. */
110 void ldisc_update(int echo, int edit) {}
111
112 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
113 static char appname[] = "PuTTY";
114 WORD winsock_ver;
115 WSADATA wsadata;
116 WNDCLASS wndclass;
117 MSG msg;
118 int guess_width, guess_height;
119
120 hinst = inst;
121 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
122
123 winsock_ver = MAKEWORD(1, 1);
124 if (WSAStartup(winsock_ver, &wsadata)) {
125 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
126 MB_OK | MB_ICONEXCLAMATION);
127 return 1;
128 }
129 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
130 MessageBox(NULL, "WinSock version is incompatible with 1.1",
131 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
132 WSACleanup();
133 return 1;
134 }
135 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
136 sk_init();
137
138 InitCommonControls();
139
140 /* Ensure a Maximize setting in Explorer doesn't maximise the
141 * config box. */
142 defuse_showwindow();
143
144 /*
145 * Process the command line.
146 */
147 {
148 char *p;
149
150 default_protocol = DEFAULT_PROTOCOL;
151 default_port = DEFAULT_PORT;
152 cfg.logtype = LGTYP_NONE;
153
154 do_defaults(NULL, &cfg);
155
156 p = cmdline;
157 while (*p && isspace(*p)) p++;
158
159 /*
160 * Process command line options first. Yes, this can be
161 * done better, and it will be as soon as I have the
162 * energy...
163 */
164 while (*p == '-') {
165 char *q = p + strcspn(p, " \t");
166 p++;
167 if (q == p + 3 &&
168 tolower(p[0]) == 's' &&
169 tolower(p[1]) == 's' &&
170 tolower(p[2]) == 'h') {
171 default_protocol = cfg.protocol = PROT_SSH;
172 default_port = cfg.port = 22;
173 } else if (q == p + 7 &&
174 tolower(p[0]) == 'c' &&
175 tolower(p[1]) == 'l' &&
176 tolower(p[2]) == 'e' &&
177 tolower(p[3]) == 'a' &&
178 tolower(p[4]) == 'n' &&
179 tolower(p[5]) == 'u' &&
180 tolower(p[6]) == 'p') {
181 /*
182 * `putty -cleanup'. Remove all registry entries
183 * associated with PuTTY, and also find and delete
184 * the random seed file.
185 */
186 if (MessageBox(NULL,
187 "This procedure will remove ALL Registry\n"
188 "entries associated with PuTTY, and will\n"
189 "also remove the PuTTY random seed file.\n"
190 "\n"
191 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
192 "SESSIONS. Are you really sure you want\n"
193 "to continue?",
194 "PuTTY Warning",
195 MB_YESNO | MB_ICONWARNING) == IDYES) {
196 cleanup_all();
197 }
198 exit(0);
199 }
200 p = q + strspn(q, " \t");
201 }
202
203 /*
204 * An initial @ means to activate a saved session.
205 */
206 if (*p == '@') {
207 int i = strlen(p);
208 while (i > 1 && isspace(p[i-1]))
209 i--;
210 p[i] = '\0';
211 do_defaults (p+1, &cfg);
212 if (!*cfg.host && !do_config()) {
213 WSACleanup();
214 return 0;
215 }
216 } else if (*p == '&') {
217 /*
218 * An initial & means we've been given a command line
219 * containing the hex value of a HANDLE for a file
220 * mapping object, which we must then extract as a
221 * config.
222 */
223 HANDLE filemap;
224 Config *cp;
225 if (sscanf(p+1, "%p", &filemap) == 1 &&
226 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
227 0, 0, sizeof(Config))) != NULL) {
228 cfg = *cp;
229 UnmapViewOfFile(cp);
230 CloseHandle(filemap);
231 } else if (!do_config()) {
232 WSACleanup();
233 return 0;
234 }
235 } else if (*p) {
236 char *q = p;
237 /*
238 * If the hostname starts with "telnet:", set the
239 * protocol to Telnet and process the string as a
240 * Telnet URL.
241 */
242 if (!strncmp(q, "telnet:", 7)) {
243 char c;
244
245 q += 7;
246 if (q[0] == '/' && q[1] == '/')
247 q += 2;
248 cfg.protocol = PROT_TELNET;
249 p = q;
250 while (*p && *p != ':' && *p != '/') p++;
251 c = *p;
252 if (*p)
253 *p++ = '\0';
254 if (c == ':')
255 cfg.port = atoi(p);
256 else
257 cfg.port = -1;
258 strncpy (cfg.host, q, sizeof(cfg.host)-1);
259 cfg.host[sizeof(cfg.host)-1] = '\0';
260 } else {
261 while (*p && !isspace(*p)) p++;
262 if (*p)
263 *p++ = '\0';
264 strncpy (cfg.host, q, sizeof(cfg.host)-1);
265 cfg.host[sizeof(cfg.host)-1] = '\0';
266 while (*p && isspace(*p)) p++;
267 if (*p)
268 cfg.port = atoi(p);
269 else
270 cfg.port = -1;
271 }
272 } else {
273 if (!do_config()) {
274 WSACleanup();
275 return 0;
276 }
277 }
278
279 /* See if host is of the form user@host */
280 if (cfg.host[0] != '\0') {
281 char *atsign = strchr(cfg.host, '@');
282 /* Make sure we're not overflowing the user field */
283 if (atsign) {
284 if (atsign-cfg.host < sizeof cfg.username) {
285 strncpy (cfg.username, cfg.host, atsign-cfg.host);
286 cfg.username[atsign-cfg.host] = '\0';
287 }
288 memmove(cfg.host, atsign+1, 1+strlen(atsign+1));
289 }
290 }
291 }
292
293 /*
294 * Select protocol. This is farmed out into a table in a
295 * separate file to enable an ssh-free variant.
296 */
297 {
298 int i;
299 back = NULL;
300 for (i = 0; backends[i].backend != NULL; i++)
301 if (backends[i].protocol == cfg.protocol) {
302 back = backends[i].backend;
303 break;
304 }
305 if (back == NULL) {
306 MessageBox(NULL, "Unsupported protocol number found",
307 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
308 WSACleanup();
309 return 1;
310 }
311 }
312
313 /* Check for invalid Port number (i.e. zero) */
314 if (cfg.port == 0) {
315 MessageBox(NULL, "Invalid Port Number",
316 "PuTTY Internal Error", MB_OK |MB_ICONEXCLAMATION);
317 WSACleanup();
318 return 1;
319 }
320
321 if (!prev) {
322 wndclass.style = 0;
323 wndclass.lpfnWndProc = WndProc;
324 wndclass.cbClsExtra = 0;
325 wndclass.cbWndExtra = 0;
326 wndclass.hInstance = inst;
327 wndclass.hIcon = LoadIcon (inst,
328 MAKEINTRESOURCE(IDI_MAINICON));
329 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
330 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
331 wndclass.lpszMenuName = NULL;
332 wndclass.lpszClassName = appname;
333
334 RegisterClass (&wndclass);
335 }
336
337 hwnd = NULL;
338
339 savelines = cfg.savelines;
340 term_init();
341
342 cfgtopalette();
343
344 /*
345 * Guess some defaults for the window size. This all gets
346 * updated later, so we don't really care too much. However, we
347 * do want the font width/height guesses to correspond to a
348 * large font rather than a small one...
349 */
350
351 font_width = 10;
352 font_height = 20;
353 extra_width = 25;
354 extra_height = 28;
355 term_size (cfg.height, cfg.width, cfg.savelines);
356 guess_width = extra_width + font_width * cols;
357 guess_height = extra_height + font_height * rows;
358 {
359 RECT r;
360 HWND w = GetDesktopWindow();
361 GetWindowRect (w, &r);
362 if (guess_width > r.right - r.left)
363 guess_width = r.right - r.left;
364 if (guess_height > r.bottom - r.top)
365 guess_height = r.bottom - r.top;
366 }
367
368 {
369 int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
370 int exwinmode = 0;
371 if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
372 if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
373 if (cfg.alwaysontop) exwinmode = WS_EX_TOPMOST;
374 hwnd = CreateWindowEx (exwinmode, appname, appname,
375 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
376 guess_width, guess_height,
377 NULL, NULL, inst, NULL);
378 }
379
380 /*
381 * Initialise the fonts, simultaneously correcting the guesses
382 * for font_{width,height}.
383 */
384 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
385 und_mode = UND_FONT;
386 init_fonts(0);
387
388 /*
389 * Correct the guesses for extra_{width,height}.
390 */
391 {
392 RECT cr, wr;
393 GetWindowRect (hwnd, &wr);
394 GetClientRect (hwnd, &cr);
395 extra_width = wr.right - wr.left - cr.right + cr.left;
396 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
397 }
398
399 /*
400 * Resize the window, now we know what size we _really_ want it
401 * to be.
402 */
403 guess_width = extra_width + font_width * cols;
404 guess_height = extra_height + font_height * rows;
405 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
406 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
407 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
408
409 /*
410 * Set up a caret bitmap, with no content.
411 */
412 {
413 char *bits;
414 int size = (font_width+15)/16 * 2 * font_height;
415 bits = smalloc(size);
416 memset(bits, 0, size);
417 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
418 sfree(bits);
419 }
420 CreateCaret(hwnd, caretbm, font_width, font_height);
421
422 /*
423 * Initialise the scroll bar.
424 */
425 {
426 SCROLLINFO si;
427
428 si.cbSize = sizeof(si);
429 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
430 si.nMin = 0;
431 si.nMax = rows-1;
432 si.nPage = rows;
433 si.nPos = 0;
434 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
435 }
436
437 /*
438 * Start up the telnet connection.
439 */
440 {
441 char *error;
442 char msg[1024], *title;
443 char *realhost;
444
445 error = back->init (cfg.host, cfg.port, &realhost);
446 if (error) {
447 sprintf(msg, "Unable to open connection to\n"
448 "%.800s\n"
449 "%s", cfg.host, error);
450 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
451 return 0;
452 }
453 window_name = icon_name = NULL;
454 if (*cfg.wintitle) {
455 title = cfg.wintitle;
456 } else {
457 sprintf(msg, "%s - PuTTY", realhost);
458 title = msg;
459 }
460 set_title (title);
461 set_icon (title);
462 }
463
464 session_closed = FALSE;
465
466 /*
467 * Set up the input and output buffers.
468 */
469 inbuf_head = 0;
470 outbuf_reap = outbuf_head = 0;
471
472 /*
473 * Prepare the mouse handler.
474 */
475 lastact = MA_NOTHING;
476 lastbtn = MB_NOTHING;
477 dbltime = GetDoubleClickTime();
478
479 /*
480 * Set up the session-control options on the system menu.
481 */
482 {
483 HMENU m = GetSystemMenu (hwnd, FALSE);
484 HMENU p,s;
485 int i;
486
487 AppendMenu (m, MF_SEPARATOR, 0, 0);
488 if (cfg.protocol == PROT_TELNET) {
489 p = CreateMenu();
490 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
491 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
492 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
493 AppendMenu (p, MF_SEPARATOR, 0, 0);
494 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
495 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
496 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
497 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
498 AppendMenu (p, MF_SEPARATOR, 0, 0);
499 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
500 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
501 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
502 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
503 AppendMenu (p, MF_SEPARATOR, 0, 0);
504 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
505 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
506 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
507 AppendMenu (m, MF_SEPARATOR, 0, 0);
508 }
509 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
510 AppendMenu (m, MF_SEPARATOR, 0, 0);
511 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
512 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
513 s = CreateMenu();
514 get_sesslist(TRUE);
515 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
516 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
517 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
518 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
519 AppendMenu (m, MF_SEPARATOR, 0, 0);
520 AppendMenu (m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
521 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
522 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
523 AppendMenu (m, MF_SEPARATOR, 0, 0);
524 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
525 }
526
527 /*
528 * Finally show the window!
529 */
530 ShowWindow (hwnd, show);
531
532 /*
533 * Open the initial log file if there is one.
534 */
535 logfopen();
536
537 /*
538 * Set the palette up.
539 */
540 pal = NULL;
541 logpal = NULL;
542 init_palette();
543
544 has_focus = (GetForegroundWindow() == hwnd);
545 UpdateWindow (hwnd);
546
547 if (GetMessage (&msg, NULL, 0, 0) == 1)
548 {
549 int timer_id = 0, long_timer = 0;
550
551 while (msg.message != WM_QUIT) {
552 /* Sometimes DispatchMessage calls routines that use their own
553 * GetMessage loop, setup this timer so we get some control back.
554 *
555 * Also call term_update() from the timer so that if the host
556 * is sending data flat out we still do redraws.
557 */
558 if(timer_id && long_timer) {
559 KillTimer(hwnd, timer_id);
560 long_timer = timer_id = 0;
561 }
562 if(!timer_id)
563 timer_id = SetTimer(hwnd, 1, 20, NULL);
564 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
565 DispatchMessage (&msg);
566
567 /* Make sure we blink everything that needs it. */
568 term_blink(0);
569
570 /* Send the paste buffer if there's anything to send */
571 term_paste();
572
573 /* If there's nothing new in the queue then we can do everything
574 * we've delayed, reading the socket, writing, and repainting
575 * the window.
576 */
577 if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
578 continue;
579
580 if (pending_netevent) {
581 enact_pending_netevent();
582
583 /* Force the cursor blink on */
584 term_blink(1);
585
586 if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
587 continue;
588 }
589
590 /* Okay there is now nothing to do so we make sure the screen is
591 * completely up to date then tell windows to call us in a little
592 * while.
593 */
594 if (timer_id) {
595 KillTimer(hwnd, timer_id);
596 timer_id = 0;
597 }
598 HideCaret(hwnd);
599 if (inbuf_head)
600 term_out();
601 term_update();
602 ShowCaret(hwnd);
603 if (in_vbell)
604 /* Hmm, term_update didn't want to do an update too soon ... */
605 timer_id = SetTimer(hwnd, 1, 50, NULL);
606 else if (!has_focus)
607 timer_id = SetTimer(hwnd, 1, 59500, NULL);
608 else
609 timer_id = SetTimer(hwnd, 1, 100, NULL);
610 long_timer = 1;
611
612 /* There's no point rescanning everything in the message queue
613 * so we do an apparently unnecessary wait here
614 */
615 WaitMessage();
616 if (GetMessage (&msg, NULL, 0, 0) != 1)
617 break;
618 }
619 }
620
621 /*
622 * Clean up.
623 */
624 {
625 int i;
626 for (i=0; i<8; i++)
627 if (fonts[i])
628 DeleteObject(fonts[i]);
629 }
630 sfree(logpal);
631 if (pal)
632 DeleteObject(pal);
633 WSACleanup();
634
635 if (cfg.protocol == PROT_SSH) {
636 random_save_seed();
637 #ifdef MSCRYPTOAPI
638 crypto_wrapup();
639 #endif
640 }
641
642 return msg.wParam;
643 }
644
645 /*
646 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
647 */
648 char *do_select(SOCKET skt, int startup) {
649 int msg, events;
650 if (startup) {
651 msg = WM_NETEVENT;
652 events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE;
653 } else {
654 msg = events = 0;
655 }
656 if (!hwnd)
657 return "do_select(): internal error (hwnd==NULL)";
658 if (WSAAsyncSelect (skt, hwnd, msg, events) == SOCKET_ERROR) {
659 switch (WSAGetLastError()) {
660 case WSAENETDOWN: return "Network is down";
661 default: return "WSAAsyncSelect(): unknown error";
662 }
663 }
664 return NULL;
665 }
666
667 /*
668 * Print a message box and close the connection.
669 */
670 void connection_fatal(char *fmt, ...) {
671 va_list ap;
672 char stuff[200];
673
674 va_start(ap, fmt);
675 vsprintf(stuff, fmt, ap);
676 va_end(ap);
677 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
678 if (cfg.close_on_exit == COE_ALWAYS)
679 PostQuitMessage(1);
680 else {
681 session_closed = TRUE;
682 SetWindowText (hwnd, "PuTTY (inactive)");
683 }
684 }
685
686 /*
687 * Actually do the job requested by a WM_NETEVENT
688 */
689 static void enact_pending_netevent(void) {
690 static int reentering = 0;
691 extern int select_result(WPARAM, LPARAM);
692 int ret;
693
694 if (reentering)
695 return; /* don't unpend the pending */
696
697 pending_netevent = FALSE;
698
699 reentering = 1;
700 ret = select_result (pend_netevent_wParam, pend_netevent_lParam);
701 reentering = 0;
702
703 if (ret == 0 && !session_closed) {
704 /* Abnormal exits will already have set session_closed and taken
705 * appropriate action. */
706 if (cfg.close_on_exit == COE_ALWAYS ||
707 cfg.close_on_exit == COE_NORMAL)
708 PostQuitMessage(0);
709 else {
710 session_closed = TRUE;
711 SetWindowText (hwnd, "PuTTY (inactive)");
712 MessageBox(hwnd, "Connection closed by remote host",
713 "PuTTY", MB_OK | MB_ICONINFORMATION);
714 }
715 }
716 }
717
718 /*
719 * Copy the colour palette from the configuration data into defpal.
720 * This is non-trivial because the colour indices are different.
721 */
722 static void cfgtopalette(void) {
723 int i;
724 static const int ww[] = {
725 6, 7, 8, 9, 10, 11, 12, 13,
726 14, 15, 16, 17, 18, 19, 20, 21,
727 0, 1, 2, 3, 4, 4, 5, 5
728 };
729
730 for (i=0; i<24; i++) {
731 int w = ww[i];
732 defpal[i].rgbtRed = cfg.colours[w][0];
733 defpal[i].rgbtGreen = cfg.colours[w][1];
734 defpal[i].rgbtBlue = cfg.colours[w][2];
735 }
736 }
737
738 /*
739 * Set up the colour palette.
740 */
741 static void init_palette(void) {
742 int i;
743 HDC hdc = GetDC (hwnd);
744 if (hdc) {
745 if (cfg.try_palette &&
746 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
747 logpal = smalloc(sizeof(*logpal)
748 - sizeof(logpal->palPalEntry)
749 + NCOLOURS * sizeof(PALETTEENTRY));
750 logpal->palVersion = 0x300;
751 logpal->palNumEntries = NCOLOURS;
752 for (i = 0; i < NCOLOURS; i++) {
753 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
754 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
755 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
756 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
757 }
758 pal = CreatePalette (logpal);
759 if (pal) {
760 SelectPalette (hdc, pal, FALSE);
761 RealizePalette (hdc);
762 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
763 FALSE);
764 }
765 }
766 ReleaseDC (hwnd, hdc);
767 }
768 if (pal)
769 for (i=0; i<NCOLOURS; i++)
770 colours[i] = PALETTERGB(defpal[i].rgbtRed,
771 defpal[i].rgbtGreen,
772 defpal[i].rgbtBlue);
773 else
774 for(i=0; i<NCOLOURS; i++)
775 colours[i] = RGB(defpal[i].rgbtRed,
776 defpal[i].rgbtGreen,
777 defpal[i].rgbtBlue);
778 }
779
780 /*
781 * Initialise all the fonts we will need. There may be as many as
782 * eight or as few as one. We also:
783 *
784 * - check the font width and height, correcting our guesses if
785 * necessary.
786 *
787 * - verify that the bold font is the same width as the ordinary
788 * one, and engage shadow bolding if not.
789 *
790 * - verify that the underlined font is the same width as the
791 * ordinary one (manual underlining by means of line drawing can
792 * be done in a pinch).
793 */
794 static void init_fonts(int pick_width) {
795 TEXTMETRIC tm;
796 int i;
797 int fsize[8];
798 HDC hdc;
799 int fw_dontcare, fw_bold;
800 int firstchar = ' ';
801
802 #ifdef CHECKOEMFONT
803 font_messup:
804 #endif
805 for (i=0; i<8; i++)
806 fonts[i] = NULL;
807
808 if (cfg.fontisbold) {
809 fw_dontcare = FW_BOLD;
810 fw_bold = FW_HEAVY;
811 } else {
812 fw_dontcare = FW_DONTCARE;
813 fw_bold = FW_BOLD;
814 }
815
816 hdc = GetDC(hwnd);
817
818 font_height = cfg.fontheight;
819 if (font_height > 0) {
820 font_height = -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
821 }
822 font_width = pick_width;
823
824 #define f(i,c,w,u) \
825 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
826 c, OUT_DEFAULT_PRECIS, \
827 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
828 FIXED_PITCH | FF_DONTCARE, cfg.font)
829
830 if (cfg.vtmode != VT_OEMONLY) {
831 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
832
833 SelectObject (hdc, fonts[FONT_NORMAL]);
834 GetTextMetrics(hdc, &tm);
835 font_height = tm.tmHeight;
836 font_width = tm.tmAveCharWidth;
837
838 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
839
840 /*
841 * Some fonts, e.g. 9-pt Courier, draw their underlines
842 * outside their character cell. We successfully prevent
843 * screen corruption by clipping the text output, but then
844 * we lose the underline completely. Here we try to work
845 * out whether this is such a font, and if it is, we set a
846 * flag that causes underlines to be drawn by hand.
847 *
848 * Having tried other more sophisticated approaches (such
849 * as examining the TEXTMETRIC structure or requesting the
850 * height of a string), I think we'll do this the brute
851 * force way: we create a small bitmap, draw an underlined
852 * space on it, and test to see whether any pixels are
853 * foreground-coloured. (Since we expect the underline to
854 * go all the way across the character cell, we only search
855 * down a single column of the bitmap, half way across.)
856 */
857 {
858 HDC und_dc;
859 HBITMAP und_bm, und_oldbm;
860 int i, gotit;
861 COLORREF c;
862
863 und_dc = CreateCompatibleDC(hdc);
864 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
865 und_oldbm = SelectObject(und_dc, und_bm);
866 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
867 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
868 SetTextColor (und_dc, RGB(255,255,255));
869 SetBkColor (und_dc, RGB(0,0,0));
870 SetBkMode (und_dc, OPAQUE);
871 ExtTextOut (und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
872 gotit = FALSE;
873 for (i = 0; i < font_height; i++) {
874 c = GetPixel(und_dc, font_width/2, i);
875 if (c != RGB(0,0,0))
876 gotit = TRUE;
877 }
878 SelectObject(und_dc, und_oldbm);
879 DeleteObject(und_bm);
880 DeleteDC(und_dc);
881 font_needs_hand_underlining = !gotit;
882 }
883
884 if (bold_mode == BOLD_FONT) {
885 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
886 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
887 }
888
889 if (cfg.vtmode == VT_OEMANSI) {
890 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
891 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
892
893 if (bold_mode == BOLD_FONT) {
894 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
895 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
896 }
897 }
898 }
899 else
900 {
901 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
902
903 SelectObject (hdc, fonts[FONT_OEM]);
904 GetTextMetrics(hdc, &tm);
905 font_height = tm.tmHeight;
906 font_width = tm.tmAveCharWidth;
907
908 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
909
910 if (bold_mode == BOLD_FONT) {
911 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
912 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
913 }
914 }
915 #undef f
916
917 descent = tm.tmAscent + 1;
918 if (descent >= font_height)
919 descent = font_height - 1;
920 firstchar = tm.tmFirstChar;
921
922 for (i=0; i<8; i++) {
923 if (fonts[i]) {
924 if (SelectObject (hdc, fonts[i]) &&
925 GetTextMetrics(hdc, &tm) )
926 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
927 else fsize[i] = -i;
928 }
929 else fsize[i] = -i;
930 }
931
932 ReleaseDC (hwnd, hdc);
933
934 /* ... This is wrong in OEM only mode */
935 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
936 (bold_mode == BOLD_FONT &&
937 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
938 und_mode = UND_LINE;
939 DeleteObject (fonts[FONT_UNDERLINE]);
940 if (bold_mode == BOLD_FONT)
941 DeleteObject (fonts[FONT_BOLDUND]);
942 }
943
944 if (bold_mode == BOLD_FONT &&
945 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
946 bold_mode = BOLD_SHADOW;
947 DeleteObject (fonts[FONT_BOLD]);
948 if (und_mode == UND_FONT)
949 DeleteObject (fonts[FONT_BOLDUND]);
950 }
951
952 #ifdef CHECKOEMFONT
953 /* With the fascist font painting it doesn't matter if the linedraw font
954 * isn't exactly the right size anymore so we don't have to check this.
955 */
956 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
957 if( cfg.fontcharset == OEM_CHARSET )
958 {
959 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
960 "different sizes. Using OEM-only mode instead",
961 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
962 cfg.vtmode = VT_OEMONLY;
963 }
964 else if( firstchar < ' ' )
965 {
966 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
967 "different sizes. Using XTerm mode instead",
968 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
969 cfg.vtmode = VT_XWINDOWS;
970 }
971 else
972 {
973 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
974 "different sizes. Using ISO8859-1 mode instead",
975 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
976 cfg.vtmode = VT_POORMAN;
977 }
978
979 for (i=0; i<8; i++)
980 if (fonts[i])
981 DeleteObject (fonts[i]);
982 goto font_messup;
983 }
984 #endif
985 }
986
987 void request_resize (int w, int h, int refont) {
988 int width, height;
989
990 /* If the window is maximized supress resizing attempts */
991 if(IsZoomed(hwnd)) return;
992
993 #ifdef CHECKOEMFONT
994 /* Don't do this in OEMANSI, you may get disable messages */
995 if (refont && w != cols && (cols==80 || cols==132)
996 && cfg.vtmode != VT_OEMANSI)
997 #else
998 if (refont && w != cols && (cols==80 || cols==132))
999 #endif
1000 {
1001 /* If font width too big for screen should we shrink the font more ? */
1002 if (w==132)
1003 font_width = ((font_width*cols+w/2)/w);
1004 else
1005 font_width = 0;
1006 {
1007 int i;
1008 for (i=0; i<8; i++)
1009 if (fonts[i])
1010 DeleteObject(fonts[i]);
1011 }
1012 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1013 und_mode = UND_FONT;
1014 init_fonts(font_width);
1015 }
1016 else
1017 {
1018 static int first_time = 1;
1019 static RECT ss;
1020
1021 switch(first_time)
1022 {
1023 case 1:
1024 /* Get the size of the screen */
1025 if (GetClientRect(GetDesktopWindow(),&ss))
1026 /* first_time = 0 */;
1027 else { first_time = 2; break; }
1028 case 0:
1029 /* Make sure the values are sane */
1030 width = (ss.right-ss.left-extra_width ) / font_width;
1031 height = (ss.bottom-ss.top-extra_height ) / font_height;
1032
1033 if (w>width) w=width;
1034 if (h>height) h=height;
1035 if (w<15) w = 15;
1036 if (h<1) w = 1;
1037 }
1038 }
1039
1040 width = extra_width + font_width * w;
1041 height = extra_height + font_height * h;
1042
1043 SetWindowPos (hwnd, NULL, 0, 0, width, height,
1044 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1045 SWP_NOMOVE | SWP_NOZORDER);
1046 }
1047
1048 static void click (Mouse_Button b, int x, int y) {
1049 int thistime = GetMessageTime();
1050
1051 if (lastbtn == b && thistime - lasttime < dbltime) {
1052 lastact = (lastact == MA_CLICK ? MA_2CLK :
1053 lastact == MA_2CLK ? MA_3CLK :
1054 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1055 } else {
1056 lastbtn = b;
1057 lastact = MA_CLICK;
1058 }
1059 if (lastact != MA_NOTHING)
1060 term_mouse (b, lastact, x, y);
1061 lasttime = thistime;
1062 }
1063
1064 static void show_mouseptr(int show) {
1065 static int cursor_visible = 1;
1066 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1067 show = 1;
1068 if (cursor_visible && !show)
1069 ShowCursor(FALSE);
1070 else if (!cursor_visible && show)
1071 ShowCursor(TRUE);
1072 cursor_visible = show;
1073 }
1074
1075 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
1076 WPARAM wParam, LPARAM lParam) {
1077 HDC hdc;
1078 static int ignore_size = FALSE;
1079 static int ignore_clip = FALSE;
1080 static int just_reconfigged = FALSE;
1081 static int resizing = FALSE;
1082 static int need_backend_resize = FALSE;
1083
1084 switch (message) {
1085 case WM_TIMER:
1086 if (pending_netevent)
1087 enact_pending_netevent();
1088 if (inbuf_head)
1089 term_out();
1090 noise_regular();
1091 HideCaret(hwnd);
1092 term_update();
1093 ShowCaret(hwnd);
1094 if (cfg.ping_interval > 0)
1095 {
1096 time_t now;
1097 time(&now);
1098 if (now-last_movement > cfg.ping_interval)
1099 {
1100 back->special(TS_PING);
1101 last_movement = now;
1102 }
1103 }
1104 return 0;
1105 case WM_CREATE:
1106 break;
1107 case WM_CLOSE:
1108 show_mouseptr(1);
1109 if (!cfg.warn_on_close || session_closed ||
1110 MessageBox(hwnd, "Are you sure you want to close this session?",
1111 "PuTTY Exit Confirmation",
1112 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1113 DestroyWindow(hwnd);
1114 return 0;
1115 case WM_DESTROY:
1116 show_mouseptr(1);
1117 PostQuitMessage (0);
1118 return 0;
1119 case WM_SYSCOMMAND:
1120 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1121 case IDM_SHOWLOG:
1122 showeventlog(hwnd);
1123 break;
1124 case IDM_NEWSESS:
1125 case IDM_DUPSESS:
1126 case IDM_SAVEDSESS:
1127 {
1128 char b[2048];
1129 char c[30], *cl;
1130 int freecl = FALSE;
1131 STARTUPINFO si;
1132 PROCESS_INFORMATION pi;
1133 HANDLE filemap = NULL;
1134
1135 if (wParam == IDM_DUPSESS) {
1136 /*
1137 * Allocate a file-mapping memory chunk for the
1138 * config structure.
1139 */
1140 SECURITY_ATTRIBUTES sa;
1141 Config *p;
1142
1143 sa.nLength = sizeof(sa);
1144 sa.lpSecurityDescriptor = NULL;
1145 sa.bInheritHandle = TRUE;
1146 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
1147 &sa,
1148 PAGE_READWRITE,
1149 0,
1150 sizeof(Config),
1151 NULL);
1152 if (filemap) {
1153 p = (Config *)MapViewOfFile(filemap,
1154 FILE_MAP_WRITE,
1155 0, 0, sizeof(Config));
1156 if (p) {
1157 *p = cfg; /* structure copy */
1158 UnmapViewOfFile(p);
1159 }
1160 }
1161 sprintf(c, "putty &%p", filemap);
1162 cl = c;
1163 } else if (wParam == IDM_SAVEDSESS) {
1164 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
1165 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1166 if (!cl)
1167 cl = NULL; /* not a very important failure mode */
1168 else {
1169 sprintf(cl, "putty @%s", session);
1170 freecl = TRUE;
1171 }
1172 } else
1173 cl = NULL;
1174
1175 GetModuleFileName (NULL, b, sizeof(b)-1);
1176 si.cb = sizeof(si);
1177 si.lpReserved = NULL;
1178 si.lpDesktop = NULL;
1179 si.lpTitle = NULL;
1180 si.dwFlags = 0;
1181 si.cbReserved2 = 0;
1182 si.lpReserved2 = NULL;
1183 CreateProcess (b, cl, NULL, NULL, TRUE,
1184 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1185
1186 if (filemap)
1187 CloseHandle(filemap);
1188 if (freecl)
1189 sfree(cl);
1190 }
1191 break;
1192 case IDM_RECONF:
1193 {
1194 int prev_alwaysontop = cfg.alwaysontop;
1195 char oldlogfile[FILENAME_MAX];
1196 int oldlogtype;
1197 int need_setwpos = FALSE;
1198 int old_fwidth, old_fheight;
1199
1200 strcpy(oldlogfile, cfg.logfilename);
1201 oldlogtype = cfg.logtype;
1202 cfg.width = cols;
1203 cfg.height = rows;
1204 old_fwidth = font_width;
1205 old_fheight = font_height;
1206 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1207
1208 if (!do_reconfig(hwnd))
1209 break;
1210
1211 if (strcmp(oldlogfile, cfg.logfilename) ||
1212 oldlogtype != cfg.logtype) {
1213 logfclose(); /* reset logging */
1214 logfopen();
1215 }
1216
1217 just_reconfigged = TRUE;
1218 {
1219 int i;
1220 for (i=0; i<8; i++)
1221 if (fonts[i])
1222 DeleteObject(fonts[i]);
1223 }
1224 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1225 und_mode = UND_FONT;
1226 init_fonts(0);
1227 sfree(logpal);
1228 /*
1229 * Flush the line discipline's edit buffer in the
1230 * case where local editing has just been disabled.
1231 */
1232 ldisc_send(NULL, 0);
1233 if (pal)
1234 DeleteObject(pal);
1235 logpal = NULL;
1236 pal = NULL;
1237 cfgtopalette();
1238 init_palette();
1239
1240 /* Enable or disable the scroll bar, etc */
1241 {
1242 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1243 LONG nexflag, exflag = GetWindowLong(hwnd, GWL_EXSTYLE);
1244
1245 nexflag = exflag;
1246 if (cfg.alwaysontop != prev_alwaysontop) {
1247 if (cfg.alwaysontop) {
1248 nexflag = WS_EX_TOPMOST;
1249 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1250 SWP_NOMOVE | SWP_NOSIZE);
1251 } else {
1252 nexflag = 0;
1253 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1254 SWP_NOMOVE | SWP_NOSIZE);
1255 }
1256 }
1257
1258 nflg = flag;
1259 if (cfg.scrollbar) nflg |= WS_VSCROLL;
1260 else nflg &= ~WS_VSCROLL;
1261 if (cfg.locksize)
1262 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1263 else
1264 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1265
1266 if (nflg != flag || nexflag != exflag)
1267 {
1268 RECT cr, wr;
1269
1270 if (nflg != flag)
1271 SetWindowLong(hwnd, GWL_STYLE, nflg);
1272 if (nexflag != exflag)
1273 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1274
1275 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1276
1277 SetWindowPos(hwnd, NULL, 0,0,0,0,
1278 SWP_NOACTIVATE|SWP_NOCOPYBITS|
1279 SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|
1280 SWP_FRAMECHANGED);
1281
1282 GetWindowRect (hwnd, &wr);
1283 GetClientRect (hwnd, &cr);
1284 extra_width = wr.right - wr.left - cr.right + cr.left;
1285 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1286 }
1287 }
1288
1289 if (cfg.height != rows ||
1290 cfg.width != cols ||
1291 old_fwidth != font_width ||
1292 old_fheight != font_height ||
1293 cfg.savelines != savelines)
1294 need_setwpos = TRUE;
1295 term_size(cfg.height, cfg.width, cfg.savelines);
1296 InvalidateRect(hwnd, NULL, TRUE);
1297 if (need_setwpos) {
1298 force_normal(hwnd);
1299 SetWindowPos (hwnd, NULL, 0, 0,
1300 extra_width + font_width * cfg.width,
1301 extra_height + font_height * cfg.height,
1302 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1303 SWP_NOMOVE | SWP_NOZORDER);
1304 }
1305 set_title(cfg.wintitle);
1306 if (IsIconic(hwnd)) {
1307 SetWindowText (hwnd,
1308 cfg.win_name_always ? window_name : icon_name);
1309 }
1310 }
1311 break;
1312 case IDM_COPYALL:
1313 term_copyall();
1314 break;
1315 case IDM_CLRSB:
1316 term_clrsb();
1317 break;
1318 case IDM_RESET:
1319 term_pwron();
1320 break;
1321 case IDM_TEL_AYT: back->special (TS_AYT); break;
1322 case IDM_TEL_BRK: back->special (TS_BRK); break;
1323 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1324 case IDM_TEL_EC: back->special (TS_EC); break;
1325 case IDM_TEL_EL: back->special (TS_EL); break;
1326 case IDM_TEL_GA: back->special (TS_GA); break;
1327 case IDM_TEL_NOP: back->special (TS_NOP); break;
1328 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1329 case IDM_TEL_AO: back->special (TS_AO); break;
1330 case IDM_TEL_IP: back->special (TS_IP); break;
1331 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1332 case IDM_TEL_EOR: back->special (TS_EOR); break;
1333 case IDM_TEL_EOF: back->special (TS_EOF); break;
1334 case IDM_ABOUT:
1335 showabout (hwnd);
1336 break;
1337 default:
1338 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1339 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1340 }
1341 }
1342 break;
1343
1344 #define X_POS(l) ((int)(short)LOWORD(l))
1345 #define Y_POS(l) ((int)(short)HIWORD(l))
1346
1347 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1348 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1349
1350 case WM_LBUTTONDOWN:
1351 show_mouseptr(1);
1352 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1353 TO_CHR_Y(Y_POS(lParam)));
1354 SetCapture(hwnd);
1355 return 0;
1356 case WM_LBUTTONUP:
1357 show_mouseptr(1);
1358 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1359 TO_CHR_Y(Y_POS(lParam)));
1360 ReleaseCapture();
1361 return 0;
1362 case WM_MBUTTONDOWN:
1363 show_mouseptr(1);
1364 SetCapture(hwnd);
1365 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1366 TO_CHR_X(X_POS(lParam)),
1367 TO_CHR_Y(Y_POS(lParam)));
1368 return 0;
1369 case WM_MBUTTONUP:
1370 show_mouseptr(1);
1371 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1372 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1373 TO_CHR_Y(Y_POS(lParam)));
1374 ReleaseCapture();
1375 return 0;
1376 case WM_RBUTTONDOWN:
1377 show_mouseptr(1);
1378 SetCapture(hwnd);
1379 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1380 TO_CHR_X(X_POS(lParam)),
1381 TO_CHR_Y(Y_POS(lParam)));
1382 return 0;
1383 case WM_RBUTTONUP:
1384 show_mouseptr(1);
1385 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1386 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1387 TO_CHR_Y(Y_POS(lParam)));
1388 ReleaseCapture();
1389 return 0;
1390 case WM_MOUSEMOVE:
1391 show_mouseptr(1);
1392 /*
1393 * Add the mouse position and message time to the random
1394 * number noise.
1395 */
1396 noise_ultralight(lParam);
1397
1398 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1399 Mouse_Button b;
1400 if (wParam & MK_LBUTTON)
1401 b = MB_SELECT;
1402 else if (wParam & MK_MBUTTON)
1403 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1404 else
1405 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1406 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1407 TO_CHR_Y(Y_POS(lParam)));
1408 }
1409 return 0;
1410 case WM_NCMOUSEMOVE:
1411 show_mouseptr(1);
1412 noise_ultralight(lParam);
1413 return 0;
1414 case WM_IGNORE_CLIP:
1415 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1416 break;
1417 case WM_DESTROYCLIPBOARD:
1418 if (!ignore_clip)
1419 term_deselect();
1420 ignore_clip = FALSE;
1421 return 0;
1422 case WM_PAINT:
1423 {
1424 PAINTSTRUCT p;
1425 HideCaret(hwnd);
1426 hdc = BeginPaint (hwnd, &p);
1427 if (pal) {
1428 SelectPalette (hdc, pal, TRUE);
1429 RealizePalette (hdc);
1430 }
1431 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1432 p.rcPaint.right, p.rcPaint.bottom);
1433 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1434 SelectObject (hdc, GetStockObject(WHITE_PEN));
1435 EndPaint (hwnd, &p);
1436 ShowCaret(hwnd);
1437 }
1438 return 0;
1439 case WM_NETEVENT:
1440 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1441 * but the only one that's likely to try to overload us is FD_READ.
1442 * This means buffering just one is fine.
1443 */
1444 if (pending_netevent)
1445 enact_pending_netevent();
1446
1447 pending_netevent = TRUE;
1448 pend_netevent_wParam=wParam;
1449 pend_netevent_lParam=lParam;
1450 time(&last_movement);
1451 return 0;
1452 case WM_SETFOCUS:
1453 has_focus = TRUE;
1454 CreateCaret(hwnd, caretbm, font_width, font_height);
1455 ShowCaret(hwnd);
1456 compose_state = 0;
1457 term_out();
1458 term_update();
1459 break;
1460 case WM_KILLFOCUS:
1461 show_mouseptr(1);
1462 has_focus = FALSE;
1463 DestroyCaret();
1464 term_out();
1465 term_update();
1466 break;
1467 case WM_IGNORE_SIZE:
1468 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1469 break;
1470 case WM_ENTERSIZEMOVE:
1471 EnableSizeTip(1);
1472 resizing = TRUE;
1473 need_backend_resize = FALSE;
1474 break;
1475 case WM_EXITSIZEMOVE:
1476 EnableSizeTip(0);
1477 resizing = FALSE;
1478 if (need_backend_resize)
1479 back->size();
1480 break;
1481 case WM_SIZING:
1482 {
1483 int width, height, w, h, ew, eh;
1484 LPRECT r = (LPRECT)lParam;
1485
1486 width = r->right - r->left - extra_width;
1487 height = r->bottom - r->top - extra_height;
1488 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1489 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1490 UpdateSizeTip(hwnd, w, h);
1491 ew = width - w * font_width;
1492 eh = height - h * font_height;
1493 if (ew != 0) {
1494 if (wParam == WMSZ_LEFT ||
1495 wParam == WMSZ_BOTTOMLEFT ||
1496 wParam == WMSZ_TOPLEFT)
1497 r->left += ew;
1498 else
1499 r->right -= ew;
1500 }
1501 if (eh != 0) {
1502 if (wParam == WMSZ_TOP ||
1503 wParam == WMSZ_TOPRIGHT ||
1504 wParam == WMSZ_TOPLEFT)
1505 r->top += eh;
1506 else
1507 r->bottom -= eh;
1508 }
1509 if (ew || eh)
1510 return 1;
1511 else
1512 return 0;
1513 }
1514 /* break; (never reached) */
1515 case WM_SIZE:
1516 if (wParam == SIZE_MINIMIZED) {
1517 SetWindowText (hwnd,
1518 cfg.win_name_always ? window_name : icon_name);
1519 break;
1520 }
1521 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1522 SetWindowText (hwnd, window_name);
1523 if (!ignore_size) {
1524 int width, height, w, h;
1525 #if 0 /* we have fixed this using WM_SIZING now */
1526 int ew, eh;
1527 #endif
1528
1529 width = LOWORD(lParam);
1530 height = HIWORD(lParam);
1531 w = width / font_width; if (w < 1) w = 1;
1532 h = height / font_height; if (h < 1) h = 1;
1533 #if 0 /* we have fixed this using WM_SIZING now */
1534 ew = width - w * font_width;
1535 eh = height - h * font_height;
1536 if (ew != 0 || eh != 0) {
1537 RECT r;
1538 GetWindowRect (hwnd, &r);
1539 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1540 SetWindowPos (hwnd, NULL, 0, 0,
1541 r.right - r.left - ew, r.bottom - r.top - eh,
1542 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1543 }
1544 #endif
1545 if (w != cols || h != rows || just_reconfigged) {
1546 term_invalidate();
1547 term_size (h, w, cfg.savelines);
1548 /*
1549 * Don't call back->size in mid-resize. (To prevent
1550 * massive numbers of resize events getting sent
1551 * down the connection during an NT opaque drag.)
1552 */
1553 if (!resizing)
1554 back->size();
1555 else
1556 need_backend_resize = TRUE;
1557 just_reconfigged = FALSE;
1558 }
1559 }
1560 ignore_size = FALSE;
1561 return 0;
1562 case WM_VSCROLL:
1563 switch (LOWORD(wParam)) {
1564 case SB_BOTTOM: term_scroll(-1, 0); break;
1565 case SB_TOP: term_scroll(+1, 0); break;
1566 case SB_LINEDOWN: term_scroll (0, +1); break;
1567 case SB_LINEUP: term_scroll (0, -1); break;
1568 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1569 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1570 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1571 term_scroll (1, HIWORD(wParam)); break;
1572 }
1573 break;
1574 case WM_PALETTECHANGED:
1575 if ((HWND) wParam != hwnd && pal != NULL) {
1576 HDC hdc = get_ctx();
1577 if (hdc) {
1578 if (RealizePalette (hdc) > 0)
1579 UpdateColors (hdc);
1580 free_ctx (hdc);
1581 }
1582 }
1583 break;
1584 case WM_QUERYNEWPALETTE:
1585 if (pal != NULL) {
1586 HDC hdc = get_ctx();
1587 if (hdc) {
1588 if (RealizePalette (hdc) > 0)
1589 UpdateColors (hdc);
1590 free_ctx (hdc);
1591 return TRUE;
1592 }
1593 }
1594 return FALSE;
1595 case WM_KEYDOWN:
1596 case WM_SYSKEYDOWN:
1597 case WM_KEYUP:
1598 case WM_SYSKEYUP:
1599 /*
1600 * Add the scan code and keypress timing to the random
1601 * number noise.
1602 */
1603 noise_ultralight(lParam);
1604
1605 /*
1606 * We don't do TranslateMessage since it disassociates the
1607 * resulting CHAR message from the KEYDOWN that sparked it,
1608 * which we occasionally don't want. Instead, we process
1609 * KEYDOWN, and call the Win32 translator functions so that
1610 * we get the translations under _our_ control.
1611 */
1612 {
1613 unsigned char buf[20];
1614 int len;
1615
1616 if (wParam==VK_PROCESSKEY) {
1617 MSG m;
1618 m.hwnd = hwnd;
1619 m.message = WM_KEYDOWN;
1620 m.wParam = wParam;
1621 m.lParam = lParam & 0xdfff;
1622 TranslateMessage(&m);
1623 } else {
1624 len = TranslateKey (message, wParam, lParam, buf);
1625 if (len == -1)
1626 return DefWindowProc (hwnd, message, wParam, lParam);
1627 ldisc_send (buf, len);
1628
1629 if (len > 0)
1630 show_mouseptr(0);
1631 }
1632 }
1633 return 0;
1634 case WM_IME_CHAR:
1635 {
1636 unsigned char buf[2];
1637
1638 buf[1] = wParam;
1639 buf[0] = wParam >> 8;
1640 ldisc_send (buf, 2);
1641 }
1642 case WM_CHAR:
1643 case WM_SYSCHAR:
1644 /*
1645 * Nevertheless, we are prepared to deal with WM_CHAR
1646 * messages, should they crop up. So if someone wants to
1647 * post the things to us as part of a macro manoeuvre,
1648 * we're ready to cope.
1649 */
1650 {
1651 char c = xlat_kbd2tty((unsigned char)wParam);
1652 ldisc_send (&c, 1);
1653 }
1654 return 0;
1655 }
1656
1657 return DefWindowProc (hwnd, message, wParam, lParam);
1658 }
1659
1660 /*
1661 * Move the system caret. (We maintain one, even though it's
1662 * invisible, for the benefit of blind people: apparently some
1663 * helper software tracks the system caret, so we should arrange to
1664 * have one.)
1665 */
1666 void sys_cursor(int x, int y) {
1667 SetCaretPos(x * font_width, y * font_height);
1668 }
1669
1670 /*
1671 * Draw a line of text in the window, at given character
1672 * coordinates, in given attributes.
1673 *
1674 * We are allowed to fiddle with the contents of `text'.
1675 */
1676 void do_text (Context ctx, int x, int y, char *text, int len,
1677 unsigned long attr, int lattr) {
1678 COLORREF fg, bg, t;
1679 int nfg, nbg, nfont;
1680 HDC hdc = ctx;
1681 RECT line_box;
1682 int force_manual_underline = 0;
1683 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1684 static int *IpDx = 0, IpDxLEN = 0;;
1685
1686 if (len>IpDxLEN || IpDx[0] != fnt_width) {
1687 int i;
1688 if (len>IpDxLEN) {
1689 sfree(IpDx);
1690 IpDx = smalloc((len+16)*sizeof(int));
1691 IpDxLEN = (len+16);
1692 }
1693 for(i=0; i<IpDxLEN; i++)
1694 IpDx[i] = fnt_width;
1695 }
1696
1697 x *= fnt_width;
1698 y *= font_height;
1699
1700 if ((attr & ATTR_ACTCURS) && cfg.cursor_type == 0) {
1701 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1702 attr ^= ATTR_CUR_XOR;
1703 }
1704
1705 nfont = 0;
1706 if (cfg.vtmode == VT_OEMONLY)
1707 nfont |= FONT_OEM;
1708
1709 /*
1710 * Map high-half characters in order to approximate ISO using
1711 * OEM character set. No characters are missing if the OEM codepage
1712 * is CP850.
1713 */
1714 if (nfont & FONT_OEM) {
1715 int i;
1716 for (i=0; i<len; i++)
1717 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1718 #if 0
1719 /* This is CP850 ... perfect translation */
1720 static const char oemhighhalf[] =
1721 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1722 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1723 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1724 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1725 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1726 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1727 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1728 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1729 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1730 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1731 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1732 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1733 ;
1734 #endif
1735 /* This is CP437 ... junk translation */
1736 static const unsigned char oemhighhalf[] = {
1737 0x20, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1738 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1739 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1740 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1741 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1742 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1743 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1744 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1745 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1746 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1747 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1748 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1749 };
1750
1751 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1752 }
1753 }
1754
1755 if (attr & ATTR_LINEDRW) {
1756 int i;
1757 /* ISO 8859-1 */
1758 static const char poorman[] =
1759 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1760
1761 /* CP437 */
1762 static const char oemmap_437[] =
1763 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1764 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1765
1766 /* CP850 */
1767 static const char oemmap_850[] =
1768 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1769 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1770
1771 /* Poor windows font ... eg: windows courier */
1772 static const char oemmap[] =
1773 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1774 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1775
1776 /*
1777 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1778 * VT100 line drawing chars; everything else stays normal.
1779 *
1780 * Actually '_' maps to space too, but that's done before.
1781 */
1782 switch (cfg.vtmode) {
1783 case VT_XWINDOWS:
1784 for (i=0; i<len; i++)
1785 if (text[i] >= '\x60' && text[i] <= '\x7E')
1786 text[i] += '\x01' - '\x60';
1787 break;
1788 case VT_OEMANSI:
1789 /* Make sure we actually have an OEM font */
1790 if (fonts[nfont|FONT_OEM]) {
1791 case VT_OEMONLY:
1792 nfont |= FONT_OEM;
1793 for (i=0; i<len; i++)
1794 if (text[i] >= '\x60' && text[i] <= '\x7E')
1795 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1796 break;
1797 }
1798 case VT_POORMAN:
1799 for (i=0; i<len; i++)
1800 if (text[i] >= '\x60' && text[i] <= '\x7E')
1801 text[i] = poorman[(unsigned char)text[i] - 0x60];
1802 break;
1803 }
1804 }
1805
1806 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1807 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1808 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1809 nfont |= FONT_BOLD;
1810 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1811 nfont |= FONT_UNDERLINE;
1812 if (!fonts[nfont])
1813 {
1814 if (nfont&FONT_UNDERLINE)
1815 force_manual_underline = 1;
1816 /* Don't do the same for manual bold, it could be bad news. */
1817
1818 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1819 }
1820 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1821 force_manual_underline = 1;
1822 if (attr & ATTR_REVERSE) {
1823 t = nfg; nfg = nbg; nbg = t;
1824 }
1825 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1826 nfg++;
1827 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1828 nbg++;
1829 fg = colours[nfg];
1830 bg = colours[nbg];
1831 SelectObject (hdc, fonts[nfont]);
1832 SetTextColor (hdc, fg);
1833 SetBkColor (hdc, bg);
1834 SetBkMode (hdc, OPAQUE);
1835 line_box.left = x;
1836 line_box.top = y;
1837 line_box.right = x+fnt_width*len;
1838 line_box.bottom = y+font_height;
1839 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1840 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1841 SetBkMode (hdc, TRANSPARENT);
1842
1843 /* GRR: This draws the character outside it's box and can leave
1844 * 'droppings' even with the clip box! I suppose I could loop it
1845 * one character at a time ... yuk.
1846 *
1847 * Or ... I could do a test print with "W", and use +1 or -1 for this
1848 * shift depending on if the leftmost column is blank...
1849 */
1850 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1851 }
1852 if (force_manual_underline ||
1853 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1854 HPEN oldpen;
1855 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1856 MoveToEx (hdc, x, y+descent, NULL);
1857 LineTo (hdc, x+len*fnt_width, y+descent);
1858 oldpen = SelectObject (hdc, oldpen);
1859 DeleteObject (oldpen);
1860 }
1861 if ((attr & ATTR_PASCURS) && cfg.cursor_type == 0) {
1862 POINT pts[5];
1863 HPEN oldpen;
1864 pts[0].x = pts[1].x = pts[4].x = x;
1865 pts[2].x = pts[3].x = x+fnt_width-1;
1866 pts[0].y = pts[3].y = pts[4].y = y;
1867 pts[1].y = pts[2].y = y+font_height-1;
1868 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1869 Polyline (hdc, pts, 5);
1870 oldpen = SelectObject (hdc, oldpen);
1871 DeleteObject (oldpen);
1872 }
1873 if ((attr & (ATTR_ACTCURS | ATTR_PASCURS)) && cfg.cursor_type != 0) {
1874 int startx, starty, dx, dy, length, i;
1875 if (cfg.cursor_type == 1) {
1876 startx = x; starty = y+descent;
1877 dx = 1; dy = 0; length = fnt_width;
1878 } else {
1879 int xadjust = 0;
1880 if (attr & ATTR_RIGHTCURS)
1881 xadjust = fnt_width-1;
1882 startx = x+xadjust; starty = y;
1883 dx = 0; dy = 1; length = font_height;
1884 }
1885 if (attr & ATTR_ACTCURS) {
1886 HPEN oldpen;
1887 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1888 MoveToEx (hdc, startx, starty, NULL);
1889 LineTo (hdc, startx+dx*length, starty+dy*length);
1890 oldpen = SelectObject (hdc, oldpen);
1891 DeleteObject (oldpen);
1892 } else {
1893 for (i = 0; i < length; i++) {
1894 if (i % 2 == 0) {
1895 SetPixel(hdc, startx, starty, colours[23]);
1896 }
1897 startx += dx; starty += dy;
1898 }
1899 }
1900 }
1901 }
1902
1903 static int check_compose(int first, int second) {
1904
1905 static char * composetbl[] = {
1906 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1907 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1908 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--­", "RO®",
1909 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1910 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1911 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1912 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1913 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1914 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1915 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1916 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1917 "\"uü", "'yý", "htþ", "\"yÿ",
1918 0};
1919
1920 char ** c;
1921 static int recurse = 0;
1922 int nc = -1;
1923
1924 for(c=composetbl; *c; c++) {
1925 if( (*c)[0] == first && (*c)[1] == second)
1926 {
1927 return (*c)[2] & 0xFF;
1928 }
1929 }
1930
1931 if(recurse==0)
1932 {
1933 recurse=1;
1934 nc = check_compose(second, first);
1935 if(nc == -1)
1936 nc = check_compose(toupper(first), toupper(second));
1937 if(nc == -1)
1938 nc = check_compose(toupper(second), toupper(first));
1939 recurse=0;
1940 }
1941 return nc;
1942 }
1943
1944
1945 /*
1946 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1947 * codes. Returns number of bytes used or zero to drop the message
1948 * or -1 to forward the message to windows.
1949 */
1950 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
1951 unsigned char *output) {
1952 BYTE keystate[256];
1953 int scan, left_alt = 0, key_down, shift_state;
1954 int r, i, code;
1955 unsigned char * p = output;
1956 static int alt_state = 0;
1957
1958 HKL kbd_layout = GetKeyboardLayout(0);
1959
1960 static WORD keys[3];
1961 static int compose_char = 0;
1962 static WPARAM compose_key = 0;
1963
1964 r = GetKeyboardState(keystate);
1965 if (!r) memset(keystate, 0, sizeof(keystate));
1966 else
1967 {
1968 #if 0
1969 { /* Tell us all about key events */
1970 static BYTE oldstate[256];
1971 static int first = 1;
1972 static int scan;
1973 int ch;
1974 if(first) memcpy(oldstate, keystate, sizeof(oldstate));
1975 first=0;
1976
1977 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT) {
1978 debug(("+"));
1979 } else if ((HIWORD(lParam)&KF_UP) && scan==(HIWORD(lParam) & 0xFF) ) {
1980 debug((". U"));
1981 } else {
1982 debug((".\n"));
1983 if (wParam >= VK_F1 && wParam <= VK_F20 )
1984 debug(("K_F%d", wParam+1-VK_F1));
1985 else switch(wParam)
1986 {
1987 case VK_SHIFT: debug(("SHIFT")); break;
1988 case VK_CONTROL: debug(("CTRL")); break;
1989 case VK_MENU: debug(("ALT")); break;
1990 default: debug(("VK_%02x", wParam));
1991 }
1992 if(message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
1993 debug(("*"));
1994 debug((", S%02x", scan=(HIWORD(lParam) & 0xFF) ));
1995
1996 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
1997 if (ch>=' ' && ch<='~') debug((", '%c'", ch));
1998 else if (ch) debug((", $%02x", ch));
1999
2000 if (keys[0]) debug((", KB0=%02x", keys[0]));
2001 if (keys[1]) debug((", KB1=%02x", keys[1]));
2002 if (keys[2]) debug((", KB2=%02x", keys[2]));
2003
2004 if ( (keystate[VK_SHIFT]&0x80)!=0) debug((", S"));
2005 if ( (keystate[VK_CONTROL]&0x80)!=0) debug((", C"));
2006 if ( (HIWORD(lParam)&KF_EXTENDED) ) debug((", E"));
2007 if ( (HIWORD(lParam)&KF_UP) ) debug((", U"));
2008 }
2009
2010 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
2011 ;
2012 else if ( (HIWORD(lParam)&KF_UP) )
2013 oldstate[wParam&0xFF] ^= 0x80;
2014 else
2015 oldstate[wParam&0xFF] ^= 0x81;
2016
2017 for(ch=0; ch<256; ch++)
2018 if (oldstate[ch] != keystate[ch])
2019 debug((", M%02x=%02x", ch, keystate[ch]));
2020
2021 memcpy(oldstate, keystate, sizeof(oldstate));
2022 }
2023 #endif
2024
2025 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED)) {
2026 keystate[VK_RMENU] = keystate[VK_MENU];
2027 }
2028
2029
2030 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2031 if ( (cfg.funky_type == 3 ||
2032 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2033 && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
2034
2035 wParam = VK_EXECUTE;
2036
2037 /* UnToggle NUMLock */
2038 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
2039 keystate[VK_NUMLOCK] ^= 1;
2040 }
2041
2042 /* And write back the 'adjusted' state */
2043 SetKeyboardState (keystate);
2044 }
2045
2046 /* Disable Auto repeat if required */
2047 if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
2048 return 0;
2049
2050 if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
2051 left_alt = 1;
2052
2053 key_down = ((HIWORD(lParam)&KF_UP)==0);
2054
2055 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2056 if (left_alt && (keystate[VK_CONTROL]&0x80)) {
2057 if (cfg.ctrlaltkeys)
2058 keystate[VK_MENU] = 0;
2059 else {
2060 keystate[VK_RMENU] = 0x80;
2061 left_alt = 0;
2062 }
2063 }
2064
2065 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2066 shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
2067 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
2068
2069 /* Note if AltGr was pressed and if it was used as a compose key */
2070 if (!compose_state) {
2071 compose_key = 0x100;
2072 if (cfg.compose_key) {
2073 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
2074 compose_key = wParam;
2075 }
2076 if (wParam == VK_APPS)
2077 compose_key = wParam;
2078 }
2079
2080 if (wParam == compose_key)
2081 {
2082 if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
2083 compose_state = 1;
2084 else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
2085 compose_state = 2;
2086 else
2087 compose_state = 0;
2088 }
2089 else if (compose_state==1 && wParam != VK_CONTROL)
2090 compose_state = 0;
2091
2092 /*
2093 * Record that we pressed key so the scroll window can be reset, but
2094 * be careful to avoid Shift-UP/Down
2095 */
2096 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
2097 seen_key_event = 1;
2098 }
2099
2100 /* Make sure we're not pasting */
2101 if (key_down) term_nopaste();
2102
2103 if (compose_state>1 && left_alt) compose_state = 0;
2104
2105 /* Sanitize the number pad if not using a PC NumPad */
2106 if( left_alt || (app_keypad_keys && !cfg.no_applic_k
2107 && cfg.funky_type != 2)
2108 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state )
2109 {
2110 if ((HIWORD(lParam)&KF_EXTENDED) == 0)
2111 {
2112 int nParam = 0;
2113 switch(wParam)
2114 {
2115 case VK_INSERT: nParam = VK_NUMPAD0; break;
2116 case VK_END: nParam = VK_NUMPAD1; break;
2117 case VK_DOWN: nParam = VK_NUMPAD2; break;
2118 case VK_NEXT: nParam = VK_NUMPAD3; break;
2119 case VK_LEFT: nParam = VK_NUMPAD4; break;
2120 case VK_CLEAR: nParam = VK_NUMPAD5; break;
2121 case VK_RIGHT: nParam = VK_NUMPAD6; break;
2122 case VK_HOME: nParam = VK_NUMPAD7; break;
2123 case VK_UP: nParam = VK_NUMPAD8; break;
2124 case VK_PRIOR: nParam = VK_NUMPAD9; break;
2125 case VK_DELETE: nParam = VK_DECIMAL; break;
2126 }
2127 if (nParam)
2128 {
2129 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
2130 wParam = nParam;
2131 }
2132 }
2133 }
2134
2135 /* If a key is pressed and AltGr is not active */
2136 if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
2137 {
2138 /* Okay, prepare for most alts then ...*/
2139 if (left_alt) *p++ = '\033';
2140
2141 /* Lets see if it's a pattern we know all about ... */
2142 if (wParam == VK_PRIOR && shift_state == 1) {
2143 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2144 return 0;
2145 }
2146 if (wParam == VK_NEXT && shift_state == 1) {
2147 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2148 return 0;
2149 }
2150 if (wParam == VK_INSERT && shift_state == 1) {
2151 term_mouse (MB_PASTE, MA_CLICK, 0, 0);
2152 term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
2153 return 0;
2154 }
2155 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2156 return -1;
2157 }
2158 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2159 alt_state = 0;
2160 PostMessage(hwnd, WM_CHAR, ' ', 0);
2161 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2162 return -1;
2163 }
2164 /* Control-Numlock for app-keypad mode switch */
2165 if (wParam == VK_PAUSE && shift_state == 2) {
2166 app_keypad_keys ^= 1;
2167 return 0;
2168 }
2169
2170 /* Nethack keypad */
2171 if (cfg.nethack_keypad && !left_alt) {
2172 switch(wParam) {
2173 case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
2174 case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
2175 case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
2176 case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
2177 case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
2178 case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
2179 case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
2180 case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
2181 case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
2182 }
2183 }
2184
2185 /* Application Keypad */
2186 if (!left_alt) {
2187 int xkey = 0;
2188
2189 if ( cfg.funky_type == 3 ||
2190 ( cfg.funky_type <= 1 &&
2191 app_keypad_keys && !cfg.no_applic_k)) switch(wParam) {
2192 case VK_EXECUTE: xkey = 'P'; break;
2193 case VK_DIVIDE: xkey = 'Q'; break;
2194 case VK_MULTIPLY:xkey = 'R'; break;
2195 case VK_SUBTRACT:xkey = 'S'; break;
2196 }
2197 if(app_keypad_keys && !cfg.no_applic_k) switch(wParam) {
2198 case VK_NUMPAD0: xkey = 'p'; break;
2199 case VK_NUMPAD1: xkey = 'q'; break;
2200 case VK_NUMPAD2: xkey = 'r'; break;
2201 case VK_NUMPAD3: xkey = 's'; break;
2202 case VK_NUMPAD4: xkey = 't'; break;
2203 case VK_NUMPAD5: xkey = 'u'; break;
2204 case VK_NUMPAD6: xkey = 'v'; break;
2205 case VK_NUMPAD7: xkey = 'w'; break;
2206 case VK_NUMPAD8: xkey = 'x'; break;
2207 case VK_NUMPAD9: xkey = 'y'; break;
2208
2209 case VK_DECIMAL: xkey = 'n'; break;
2210 case VK_ADD: if(cfg.funky_type==2) {
2211 if(shift_state) xkey = 'l';
2212 else xkey = 'k';
2213 } else if(shift_state) xkey = 'm';
2214 else xkey = 'l';
2215 break;
2216
2217 case VK_DIVIDE: if(cfg.funky_type==2) xkey = 'o'; break;
2218 case VK_MULTIPLY:if(cfg.funky_type==2) xkey = 'j'; break;
2219 case VK_SUBTRACT:if(cfg.funky_type==2) xkey = 'm'; break;
2220
2221 case VK_RETURN:
2222 if (HIWORD(lParam)&KF_EXTENDED)
2223 xkey = 'M';
2224 break;
2225 }
2226 if(xkey)
2227 {
2228 if (vt52_mode)
2229 {
2230 if (xkey>='P' && xkey<='S')
2231 p += sprintf((char *)p, "\x1B%c", xkey);
2232 else
2233 p += sprintf((char *)p, "\x1B?%c", xkey);
2234 }
2235 else
2236 p += sprintf((char *)p, "\x1BO%c", xkey);
2237 return p - output;
2238 }
2239 }
2240
2241 if (wParam == VK_BACK && shift_state == 0 ) /* Backspace */
2242 {
2243 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2244 return p-output;
2245 }
2246 if (wParam == VK_TAB && shift_state == 1 ) /* Shift tab */
2247 {
2248 *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
2249 }
2250 if (wParam == VK_SPACE && shift_state == 2 ) /* Ctrl-Space */
2251 {
2252 *p++ = 0; return p - output;
2253 }
2254 if (wParam == VK_SPACE && shift_state == 3 ) /* Ctrl-Shift-Space */
2255 {
2256 *p++ = 160; return p - output;
2257 }
2258 if (wParam == VK_CANCEL && shift_state == 2 ) /* Ctrl-Break */
2259 {
2260 *p++ = 3; return p - output;
2261 }
2262 if (wParam == VK_PAUSE) /* Break/Pause */
2263 {
2264 *p++ = 26; *p++ = 0; return -2;
2265 }
2266 /* Control-2 to Control-8 are special */
2267 if (shift_state == 2 && wParam >= '2' && wParam <= '8')
2268 {
2269 *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
2270 return p - output;
2271 }
2272 if (shift_state == 2 && wParam == 0xBD) {
2273 *p++ = 0x1F;
2274 return p - output;
2275 }
2276 if (shift_state == 2 && wParam == 0xDF) {
2277 *p++ = 0x1C;
2278 return p - output;
2279 }
2280 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2281 *p++ = '\r'; *p++ = '\n';
2282 return p - output;
2283 }
2284
2285 /*
2286 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2287 * for integer decimal nn.)
2288 *
2289 * We also deal with the weird ones here. Linux VCs replace F1
2290 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2291 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2292 * respectively.
2293 */
2294 code = 0;
2295 switch (wParam) {
2296 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
2297 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
2298 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
2299 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
2300 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
2301 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
2302 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
2303 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
2304 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
2305 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
2306 case VK_F11: code = 23; break;
2307 case VK_F12: code = 24; break;
2308 case VK_F13: code = 25; break;
2309 case VK_F14: code = 26; break;
2310 case VK_F15: code = 28; break;
2311 case VK_F16: code = 29; break;
2312 case VK_F17: code = 31; break;
2313 case VK_F18: code = 32; break;
2314 case VK_F19: code = 33; break;
2315 case VK_F20: code = 34; break;
2316 case VK_HOME: code = 1; break;
2317 case VK_INSERT: code = 2; break;
2318 case VK_DELETE: code = 3; break;
2319 case VK_END: code = 4; break;
2320 case VK_PRIOR: code = 5; break;
2321 case VK_NEXT: code = 6; break;
2322 }
2323 /* Reorder edit keys to physical order */
2324 if (cfg.funky_type == 3 && code <= 6 ) code = "\0\2\1\4\5\3\6"[code];
2325
2326 if (vt52_mode && code > 0 && code <= 6) {
2327 p += sprintf((char *)p, "\x1B%c", " HLMEIG"[code]);
2328 return p - output;
2329 }
2330
2331 if (cfg.funky_type == 5 && code >= 11 && code <= 24) {
2332 p += sprintf((char *)p, "\x1B[%c", code + 'M' - 11);
2333 return p - output;
2334 }
2335 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
2336 int offt = 0;
2337 if (code>15) offt++; if (code>21) offt++;
2338 if (vt52_mode)
2339 p += sprintf((char *)p, "\x1B%c", code + 'P' - 11 - offt);
2340 else
2341 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11 - offt);
2342 return p - output;
2343 }
2344 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2345 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
2346 return p - output;
2347 }
2348 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2349 if (vt52_mode)
2350 p += sprintf((char *)p, "\x1B%c", code + 'P' - 11);
2351 else
2352 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
2353 return p - output;
2354 }
2355 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2356 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
2357 return p - output;
2358 }
2359 if (code) {
2360 p += sprintf((char *)p, "\x1B[%d~", code);
2361 return p - output;
2362 }
2363
2364 /*
2365 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2366 * some reason seems to send VK_CLEAR to Windows...).
2367 */
2368 {
2369 char xkey = 0;
2370 switch (wParam) {
2371 case VK_UP: xkey = 'A'; break;
2372 case VK_DOWN: xkey = 'B'; break;
2373 case VK_RIGHT: xkey = 'C'; break;
2374 case VK_LEFT: xkey = 'D'; break;
2375 case VK_CLEAR: xkey = 'G'; break;
2376 }
2377 if (xkey)
2378 {
2379 if (vt52_mode)
2380 p += sprintf((char *)p, "\x1B%c", xkey);
2381 else if (app_cursor_keys && !cfg.no_applic_c)
2382 p += sprintf((char *)p, "\x1BO%c", xkey);
2383 else
2384 p += sprintf((char *)p, "\x1B[%c", xkey);
2385 return p - output;
2386 }
2387 }
2388
2389 /*
2390 * Finally, deal with Return ourselves. (Win95 seems to
2391 * foul it up when Alt is pressed, for some reason.)
2392 */
2393 if (wParam == VK_RETURN) /* Return */
2394 {
2395 *p++ = 0x0D;
2396 return p-output;
2397 }
2398 }
2399
2400 /* Okay we've done everything interesting; let windows deal with
2401 * the boring stuff */
2402 {
2403 BOOL capsOn=keystate[VK_CAPITAL] !=0;
2404
2405 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2406 if(cfg.xlat_capslockcyr)
2407 keystate[VK_CAPITAL] = 0;
2408
2409 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
2410 if(r>0)
2411 {
2412 p = output;
2413 for(i=0; i<r; i++)
2414 {
2415 unsigned char ch = (unsigned char)keys[i];
2416
2417 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
2418 compose_char = ch;
2419 compose_state ++;
2420 continue;
2421 }
2422 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
2423 int nc;
2424 compose_state = 0;
2425
2426 if ((nc=check_compose(compose_char,ch)) == -1)
2427 {
2428 MessageBeep(MB_ICONHAND);
2429 return 0;
2430 }
2431 *p++ = xlat_kbd2tty((unsigned char)nc);
2432 return p-output;
2433 }
2434
2435 compose_state = 0;
2436
2437 if( left_alt && key_down ) *p++ = '\033';
2438 if (!key_down)
2439 *p++ = ch;
2440 else
2441 {
2442 if(capsOn)
2443 ch = xlat_latkbd2win(ch);
2444 *p++ = xlat_kbd2tty(ch);
2445 }
2446 }
2447
2448 /* This is so the ALT-Numpad and dead keys work correctly. */
2449 keys[0] = 0;
2450
2451 return p-output;
2452 }
2453 /* If we're definitly not building up an ALT-54321 then clear it */
2454 if (!left_alt) keys[0] = 0;
2455 }
2456
2457 /* ALT alone may or may not want to bring up the System menu */
2458 if (wParam == VK_MENU) {
2459 if (cfg.alt_only) {
2460 if (message == WM_SYSKEYDOWN)
2461 alt_state = 1;
2462 else if (message == WM_SYSKEYUP && alt_state)
2463 PostMessage(hwnd, WM_CHAR, ' ', 0);
2464 if (message == WM_SYSKEYUP)
2465 alt_state = 0;
2466 } else
2467 return 0;
2468 }
2469 else alt_state = 0;
2470
2471 return -1;
2472 }
2473
2474 void set_title (char *title) {
2475 sfree (window_name);
2476 window_name = smalloc(1+strlen(title));
2477 strcpy (window_name, title);
2478 if (cfg.win_name_always || !IsIconic(hwnd))
2479 SetWindowText (hwnd, title);
2480 }
2481
2482 void set_icon (char *title) {
2483 sfree (icon_name);
2484 icon_name = smalloc(1+strlen(title));
2485 strcpy (icon_name, title);
2486 if (!cfg.win_name_always && IsIconic(hwnd))
2487 SetWindowText (hwnd, title);
2488 }
2489
2490 void set_sbar (int total, int start, int page) {
2491 SCROLLINFO si;
2492
2493 if (!cfg.scrollbar) return;
2494
2495 si.cbSize = sizeof(si);
2496 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2497 si.nMin = 0;
2498 si.nMax = total - 1;
2499 si.nPage = page;
2500 si.nPos = start;
2501 if (hwnd)
2502 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2503 }
2504
2505 Context get_ctx(void) {
2506 HDC hdc;
2507 if (hwnd) {
2508 hdc = GetDC (hwnd);
2509 if (hdc && pal)
2510 SelectPalette (hdc, pal, FALSE);
2511 return hdc;
2512 } else
2513 return NULL;
2514 }
2515
2516 void free_ctx (Context ctx) {
2517 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2518 ReleaseDC (hwnd, ctx);
2519 }
2520
2521 static void real_palette_set (int n, int r, int g, int b) {
2522 if (pal) {
2523 logpal->palPalEntry[n].peRed = r;
2524 logpal->palPalEntry[n].peGreen = g;
2525 logpal->palPalEntry[n].peBlue = b;
2526 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2527 colours[n] = PALETTERGB(r, g, b);
2528 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2529 } else
2530 colours[n] = RGB(r, g, b);
2531 }
2532
2533 void palette_set (int n, int r, int g, int b) {
2534 static const int first[21] = {
2535 0, 2, 4, 6, 8, 10, 12, 14,
2536 1, 3, 5, 7, 9, 11, 13, 15,
2537 16, 17, 18, 20, 22
2538 };
2539 real_palette_set (first[n], r, g, b);
2540 if (first[n] >= 18)
2541 real_palette_set (first[n]+1, r, g, b);
2542 if (pal) {
2543 HDC hdc = get_ctx();
2544 UnrealizeObject (pal);
2545 RealizePalette (hdc);
2546 free_ctx (hdc);
2547 }
2548 }
2549
2550 void palette_reset (void) {
2551 int i;
2552
2553 for (i = 0; i < NCOLOURS; i++) {
2554 if (pal) {
2555 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2556 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2557 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2558 logpal->palPalEntry[i].peFlags = 0;
2559 colours[i] = PALETTERGB(defpal[i].rgbtRed,
2560 defpal[i].rgbtGreen,
2561 defpal[i].rgbtBlue);
2562 } else
2563 colours[i] = RGB(defpal[i].rgbtRed,
2564 defpal[i].rgbtGreen,
2565 defpal[i].rgbtBlue);
2566 }
2567
2568 if (pal) {
2569 HDC hdc;
2570 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2571 hdc = get_ctx();
2572 RealizePalette (hdc);
2573 free_ctx (hdc);
2574 }
2575 }
2576
2577 void write_clip (void *data, int len, int must_deselect) {
2578 HGLOBAL clipdata;
2579 void *lock;
2580
2581 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2582 if (!clipdata)
2583 return;
2584 lock = GlobalLock (clipdata);
2585 if (!lock)
2586 return;
2587 memcpy (lock, data, len);
2588 ((unsigned char *) lock) [len] = 0;
2589 GlobalUnlock (clipdata);
2590
2591 if (!must_deselect)
2592 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2593
2594 if (OpenClipboard (hwnd)) {
2595 EmptyClipboard();
2596 SetClipboardData (CF_TEXT, clipdata);
2597 CloseClipboard();
2598 } else
2599 GlobalFree (clipdata);
2600
2601 if (!must_deselect)
2602 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2603 }
2604
2605 void get_clip (void **p, int *len) {
2606 static HGLOBAL clipdata = NULL;
2607
2608 if (!p) {
2609 if (clipdata)
2610 GlobalUnlock (clipdata);
2611 clipdata = NULL;
2612 return;
2613 } else {
2614 if (OpenClipboard (NULL)) {
2615 clipdata = GetClipboardData (CF_TEXT);
2616 CloseClipboard();
2617 if (clipdata) {
2618 *p = GlobalLock (clipdata);
2619 if (*p) {
2620 *len = strlen(*p);
2621 return;
2622 }
2623 }
2624 }
2625 }
2626
2627 *p = NULL;
2628 *len = 0;
2629 }
2630
2631 /*
2632 * Move `lines' lines from position `from' to position `to' in the
2633 * window.
2634 */
2635 void optimised_move (int to, int from, int lines) {
2636 RECT r;
2637 int min, max;
2638
2639 min = (to < from ? to : from);
2640 max = to + from - min;
2641
2642 r.left = 0; r.right = cols * font_width;
2643 r.top = min * font_height; r.bottom = (max+lines) * font_height;
2644 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2645 }
2646
2647 /*
2648 * Print a message box and perform a fatal exit.
2649 */
2650 void fatalbox(char *fmt, ...) {
2651 va_list ap;
2652 char stuff[200];
2653
2654 va_start(ap, fmt);
2655 vsprintf(stuff, fmt, ap);
2656 va_end(ap);
2657 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2658 exit(1);
2659 }
2660
2661 /*
2662 * Beep.
2663 */
2664 void beep(int mode) {
2665 if (mode == BELL_DEFAULT) {
2666 MessageBeep(MB_OK);
2667 } else if (mode == BELL_WAVEFILE) {
2668 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
2669 char buf[sizeof(cfg.bell_wavefile)+80];
2670 sprintf(buf, "Unable to play sound file\n%s\n"
2671 "Using default sound instead", cfg.bell_wavefile);
2672 MessageBox(hwnd, buf, "PuTTY Sound Error", MB_OK | MB_ICONEXCLAMATION);
2673 cfg.beep = BELL_DEFAULT;
2674 }
2675 }
2676 }