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