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