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