Make use of the new section-relabelling in Buttress to arrange for
[u/mdw/putty] / window.c
1 #include <windows.h>
2 #include <imm.h>
3 #include <commctrl.h>
4 #include <richedit.h>
5 #include <mmsystem.h>
6 #ifndef AUTO_WINSOCK
7 #ifdef WINSOCK_TWO
8 #include <winsock2.h>
9 #else
10 #include <winsock.h>
11 #endif
12 #endif
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <ctype.h>
17 #include <time.h>
18 #include <assert.h>
19
20 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
21 #include "putty.h"
22 #include "winstuff.h"
23 #include "storage.h"
24 #include "win_res.h"
25
26 #define IDM_SHOWLOG 0x0010
27 #define IDM_NEWSESS 0x0020
28 #define IDM_DUPSESS 0x0030
29 #define IDM_RECONF 0x0040
30 #define IDM_CLRSB 0x0050
31 #define IDM_RESET 0x0060
32 #define IDM_TEL_AYT 0x0070
33 #define IDM_TEL_BRK 0x0080
34 #define IDM_TEL_SYNCH 0x0090
35 #define IDM_TEL_EC 0x00a0
36 #define IDM_TEL_EL 0x00b0
37 #define IDM_TEL_GA 0x00c0
38 #define IDM_TEL_NOP 0x00d0
39 #define IDM_TEL_ABORT 0x00e0
40 #define IDM_TEL_AO 0x00f0
41 #define IDM_TEL_IP 0x0100
42 #define IDM_TEL_SUSP 0x0110
43 #define IDM_TEL_EOR 0x0120
44 #define IDM_TEL_EOF 0x0130
45 #define IDM_ABOUT 0x0140
46 #define IDM_SAVEDSESS 0x0150
47 #define IDM_COPYALL 0x0160
48 #define IDM_FULLSCREEN 0x0170
49
50 #define IDM_SESSLGP 0x0250 /* log type printable */
51 #define IDM_SESSLGA 0x0260 /* log type all chars */
52 #define IDM_SESSLGE 0x0270 /* log end */
53 #define IDM_SAVED_MIN 0x1000
54 #define IDM_SAVED_MAX 0x2000
55
56 #define WM_IGNORE_CLIP (WM_XUSER + 2)
57
58 /* Needed for Chinese support and apparently not always defined. */
59 #ifndef VK_PROCESSKEY
60 #define VK_PROCESSKEY 0xE5
61 #endif
62
63 /* Needed for mouse wheel support and not defined in earlier SDKs. */
64 #ifndef WM_MOUSEWHEEL
65 #define WM_MOUSEWHEEL 0x020A
66 #endif
67
68 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
69 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
70 unsigned char *output);
71 static void cfgtopalette(void);
72 static void init_palette(void);
73 static void init_fonts(int, int);
74 static void another_font(int);
75 static void deinit_fonts(void);
76
77 /* Window layout information */
78 static void reset_window(int);
79 static int full_screen = 0;
80 static int extra_width, extra_height;
81 static int font_width, font_height, font_dualwidth;
82 static int offset_width, offset_height;
83 static int was_zoomed = 0;
84 static int prev_rows, prev_cols;
85
86 static int pending_netevent = 0;
87 static WPARAM pend_netevent_wParam = 0;
88 static LPARAM pend_netevent_lParam = 0;
89 static void enact_pending_netevent(void);
90 static void flash_window(int mode);
91 static void flip_full_screen(void);
92
93 static time_t last_movement = 0;
94
95 #define FONT_NORMAL 0
96 #define FONT_BOLD 1
97 #define FONT_UNDERLINE 2
98 #define FONT_BOLDUND 3
99 #define FONT_WIDE 0x04
100 #define FONT_HIGH 0x08
101 #define FONT_NARROW 0x10
102
103 #define FONT_OEM 0x20
104 #define FONT_OEMBOLD 0x21
105 #define FONT_OEMUND 0x22
106 #define FONT_OEMBOLDUND 0x23
107
108 #define FONT_MAXNO 0x2F
109 #define FONT_SHIFT 5
110 static HFONT fonts[FONT_MAXNO];
111 static LOGFONT lfont;
112 static int fontflag[FONT_MAXNO];
113 static enum {
114 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
115 } bold_mode;
116 static enum {
117 UND_LINE, UND_FONT
118 } und_mode;
119 static int descent;
120
121 #define NCOLOURS 24
122 static COLORREF colours[NCOLOURS];
123 static HPALETTE pal;
124 static LPLOGPALETTE logpal;
125 static RGBTRIPLE defpal[NCOLOURS];
126
127 static HWND hwnd;
128
129 static HBITMAP caretbm;
130
131 static int dbltime, lasttime, lastact;
132 static Mouse_Button lastbtn;
133
134 /* this allows xterm-style mouse handling. */
135 static int send_raw_mouse = 0;
136 static int wheel_accumulator = 0;
137
138 static char *window_name, *icon_name;
139
140 static int compose_state = 0;
141
142 static OSVERSIONINFO osVersion;
143
144 /* Dummy routine, only required in plink. */
145 void ldisc_update(int echo, int edit)
146 {
147 }
148
149 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
150 {
151 static char appname[] = "PuTTY";
152 WORD winsock_ver;
153 WSADATA wsadata;
154 WNDCLASS wndclass;
155 MSG msg;
156 int guess_width, guess_height;
157
158 hinst = inst;
159 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
160
161 winsock_ver = MAKEWORD(1, 1);
162 if (WSAStartup(winsock_ver, &wsadata)) {
163 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
164 MB_OK | MB_ICONEXCLAMATION);
165 return 1;
166 }
167 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
168 MessageBox(NULL, "WinSock version is incompatible with 1.1",
169 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
170 WSACleanup();
171 return 1;
172 }
173 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
174 sk_init();
175
176 InitCommonControls();
177
178 /* Ensure a Maximize setting in Explorer doesn't maximise the
179 * config box. */
180 defuse_showwindow();
181
182 {
183 ZeroMemory(&osVersion, sizeof(osVersion));
184 osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
185 if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
186 MessageBox(NULL, "Windows refuses to report a version",
187 "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
188 return 1;
189 }
190 }
191
192 /*
193 * Process the command line.
194 */
195 {
196 char *p;
197
198 default_protocol = DEFAULT_PROTOCOL;
199 default_port = DEFAULT_PORT;
200 cfg.logtype = LGTYP_NONE;
201
202 do_defaults(NULL, &cfg);
203
204 p = cmdline;
205 while (*p && isspace(*p))
206 p++;
207
208 /*
209 * Process command line options first. Yes, this can be
210 * done better, and it will be as soon as I have the
211 * energy...
212 */
213 while (*p == '-') {
214 char *q = p + strcspn(p, " \t");
215 p++;
216 if (q == p + 3 &&
217 tolower(p[0]) == 's' &&
218 tolower(p[1]) == 's' && tolower(p[2]) == 'h') {
219 default_protocol = cfg.protocol = PROT_SSH;
220 default_port = cfg.port = 22;
221 } else if (q == p + 7 &&
222 tolower(p[0]) == 'c' &&
223 tolower(p[1]) == 'l' &&
224 tolower(p[2]) == 'e' &&
225 tolower(p[3]) == 'a' &&
226 tolower(p[4]) == 'n' &&
227 tolower(p[5]) == 'u' && tolower(p[6]) == 'p') {
228 /*
229 * `putty -cleanup'. Remove all registry entries
230 * associated with PuTTY, and also find and delete
231 * the random seed file.
232 */
233 if (MessageBox(NULL,
234 "This procedure will remove ALL Registry\n"
235 "entries associated with PuTTY, and will\n"
236 "also remove the PuTTY random seed file.\n"
237 "\n"
238 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
239 "SESSIONS. Are you really sure you want\n"
240 "to continue?",
241 "PuTTY Warning",
242 MB_YESNO | MB_ICONWARNING) == IDYES) {
243 cleanup_all();
244 }
245 exit(0);
246 }
247 p = q + strspn(q, " \t");
248 }
249
250 /*
251 * An initial @ means to activate a saved session.
252 */
253 if (*p == '@') {
254 int i = strlen(p);
255 while (i > 1 && isspace(p[i - 1]))
256 i--;
257 p[i] = '\0';
258 do_defaults(p + 1, &cfg);
259 if (!*cfg.host && !do_config()) {
260 WSACleanup();
261 return 0;
262 }
263 } else if (*p == '&') {
264 /*
265 * An initial & means we've been given a command line
266 * containing the hex value of a HANDLE for a file
267 * mapping object, which we must then extract as a
268 * config.
269 */
270 HANDLE filemap;
271 Config *cp;
272 if (sscanf(p + 1, "%p", &filemap) == 1 &&
273 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
274 0, 0, sizeof(Config))) != NULL) {
275 cfg = *cp;
276 UnmapViewOfFile(cp);
277 CloseHandle(filemap);
278 } else if (!do_config()) {
279 WSACleanup();
280 return 0;
281 }
282 } else if (*p) {
283 char *q = p;
284 /*
285 * If the hostname starts with "telnet:", set the
286 * protocol to Telnet and process the string as a
287 * Telnet URL.
288 */
289 if (!strncmp(q, "telnet:", 7)) {
290 char c;
291
292 q += 7;
293 if (q[0] == '/' && q[1] == '/')
294 q += 2;
295 cfg.protocol = PROT_TELNET;
296 p = q;
297 while (*p && *p != ':' && *p != '/')
298 p++;
299 c = *p;
300 if (*p)
301 *p++ = '\0';
302 if (c == ':')
303 cfg.port = atoi(p);
304 else
305 cfg.port = -1;
306 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
307 cfg.host[sizeof(cfg.host) - 1] = '\0';
308 } else {
309 while (*p && !isspace(*p))
310 p++;
311 if (*p)
312 *p++ = '\0';
313 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
314 cfg.host[sizeof(cfg.host) - 1] = '\0';
315 while (*p && isspace(*p))
316 p++;
317 if (*p)
318 cfg.port = atoi(p);
319 else
320 cfg.port = -1;
321 }
322 } else {
323 if (!do_config()) {
324 WSACleanup();
325 return 0;
326 }
327 }
328
329 /*
330 * Trim leading whitespace off the hostname if it's there.
331 */
332 {
333 int space = strspn(cfg.host, " \t");
334 memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
335 }
336
337 /* See if host is of the form user@host */
338 if (cfg.host[0] != '\0') {
339 char *atsign = strchr(cfg.host, '@');
340 /* Make sure we're not overflowing the user field */
341 if (atsign) {
342 if (atsign - cfg.host < sizeof cfg.username) {
343 strncpy(cfg.username, cfg.host, atsign - cfg.host);
344 cfg.username[atsign - cfg.host] = '\0';
345 }
346 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
347 }
348 }
349
350 /*
351 * Trim a colon suffix off the hostname if it's there.
352 */
353 cfg.host[strcspn(cfg.host, ":")] = '\0';
354 }
355
356 /*
357 * Select protocol. This is farmed out into a table in a
358 * separate file to enable an ssh-free variant.
359 */
360 {
361 int i;
362 back = NULL;
363 for (i = 0; backends[i].backend != NULL; i++)
364 if (backends[i].protocol == cfg.protocol) {
365 back = backends[i].backend;
366 break;
367 }
368 if (back == NULL) {
369 MessageBox(NULL, "Unsupported protocol number found",
370 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
371 WSACleanup();
372 return 1;
373 }
374 }
375
376 /* Check for invalid Port number (i.e. zero) */
377 if (cfg.port == 0) {
378 MessageBox(NULL, "Invalid Port Number",
379 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
380 WSACleanup();
381 return 1;
382 }
383
384 if (!prev) {
385 wndclass.style = 0;
386 wndclass.lpfnWndProc = WndProc;
387 wndclass.cbClsExtra = 0;
388 wndclass.cbWndExtra = 0;
389 wndclass.hInstance = inst;
390 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
391 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
392 wndclass.hbrBackground = NULL;
393 wndclass.lpszMenuName = NULL;
394 wndclass.lpszClassName = appname;
395
396 RegisterClass(&wndclass);
397 }
398
399 hwnd = NULL;
400
401 savelines = cfg.savelines;
402 term_init();
403
404 cfgtopalette();
405
406 /*
407 * Guess some defaults for the window size. This all gets
408 * updated later, so we don't really care too much. However, we
409 * do want the font width/height guesses to correspond to a
410 * large font rather than a small one...
411 */
412
413 font_width = 10;
414 font_height = 20;
415 extra_width = 25;
416 extra_height = 28;
417 term_size(cfg.height, cfg.width, cfg.savelines);
418 guess_width = extra_width + font_width * cols;
419 guess_height = extra_height + font_height * rows;
420 {
421 RECT r;
422 HWND w = GetDesktopWindow();
423 GetWindowRect(w, &r);
424 if (guess_width > r.right - r.left)
425 guess_width = r.right - r.left;
426 if (guess_height > r.bottom - r.top)
427 guess_height = r.bottom - r.top;
428 }
429
430 {
431 int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
432 int exwinmode = 0;
433 if (!cfg.scrollbar)
434 winmode &= ~(WS_VSCROLL);
435 if (cfg.resize_action == RESIZE_DISABLED)
436 winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
437 if (cfg.alwaysontop)
438 exwinmode |= WS_EX_TOPMOST;
439 if (cfg.sunken_edge)
440 exwinmode |= WS_EX_CLIENTEDGE;
441 hwnd = CreateWindowEx(exwinmode, appname, appname,
442 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
443 guess_width, guess_height,
444 NULL, NULL, inst, NULL);
445 }
446
447 /*
448 * Initialise the fonts, simultaneously correcting the guesses
449 * for font_{width,height}.
450 */
451 init_fonts(0,0);
452
453 /*
454 * Correct the guesses for extra_{width,height}.
455 */
456 {
457 RECT cr, wr;
458 GetWindowRect(hwnd, &wr);
459 GetClientRect(hwnd, &cr);
460 offset_width = offset_height = cfg.window_border;
461 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
462 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
463 }
464
465 /*
466 * Resize the window, now we know what size we _really_ want it
467 * to be.
468 */
469 guess_width = extra_width + font_width * cols;
470 guess_height = extra_height + font_height * rows;
471 SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
472 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
473
474 /*
475 * Set up a caret bitmap, with no content.
476 */
477 {
478 char *bits;
479 int size = (font_width + 15) / 16 * 2 * font_height;
480 bits = smalloc(size);
481 memset(bits, 0, size);
482 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
483 sfree(bits);
484 }
485 CreateCaret(hwnd, caretbm, font_width, font_height);
486
487 /*
488 * Initialise the scroll bar.
489 */
490 {
491 SCROLLINFO si;
492
493 si.cbSize = sizeof(si);
494 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
495 si.nMin = 0;
496 si.nMax = rows - 1;
497 si.nPage = rows;
498 si.nPos = 0;
499 SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
500 }
501
502 /*
503 * Start up the telnet connection.
504 */
505 {
506 char *error;
507 char msg[1024], *title;
508 char *realhost;
509
510 error = back->init(cfg.host, cfg.port, &realhost);
511 if (error) {
512 sprintf(msg, "Unable to open connection to\n"
513 "%.800s\n" "%s", cfg.host, error);
514 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
515 return 0;
516 }
517 window_name = icon_name = NULL;
518 if (*cfg.wintitle) {
519 title = cfg.wintitle;
520 } else {
521 sprintf(msg, "%s - PuTTY", realhost);
522 title = msg;
523 }
524 sfree(realhost);
525 set_title(title);
526 set_icon(title);
527 }
528
529 session_closed = FALSE;
530
531 /*
532 * Prepare the mouse handler.
533 */
534 lastact = MA_NOTHING;
535 lastbtn = MBT_NOTHING;
536 dbltime = GetDoubleClickTime();
537
538 /*
539 * Set up the session-control options on the system menu.
540 */
541 {
542 HMENU m = GetSystemMenu(hwnd, FALSE);
543 HMENU p, s;
544 int i;
545
546 AppendMenu(m, MF_SEPARATOR, 0, 0);
547 if (cfg.protocol == PROT_TELNET) {
548 p = CreateMenu();
549 AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
550 AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
551 AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
552 AppendMenu(p, MF_SEPARATOR, 0, 0);
553 AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
554 AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
555 AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
556 AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
557 AppendMenu(p, MF_SEPARATOR, 0, 0);
558 AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
559 AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
560 AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
561 AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
562 AppendMenu(p, MF_SEPARATOR, 0, 0);
563 AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
564 AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
565 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
566 "Telnet Command");
567 AppendMenu(m, MF_SEPARATOR, 0, 0);
568 }
569 AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
570 AppendMenu(m, MF_SEPARATOR, 0, 0);
571 AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
572 AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
573 s = CreateMenu();
574 get_sesslist(TRUE);
575 for (i = 1; i < ((nsessions < 256) ? nsessions : 256); i++)
576 AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
577 sessions[i]);
578 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
579 AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
580 AppendMenu(m, MF_SEPARATOR, 0, 0);
581 AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
582 AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
583 AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
584 AppendMenu(m, MF_SEPARATOR, 0, 0);
585 AppendMenu(m, MF_ENABLED, IDM_FULLSCREEN, "&Full Screen");
586 AppendMenu(m, MF_SEPARATOR, 0, 0);
587 AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
588 }
589
590 /*
591 * Finally show the window!
592 */
593 ShowWindow(hwnd, show);
594 SetForegroundWindow(hwnd);
595
596 /*
597 * Open the initial log file if there is one.
598 */
599 logfopen();
600
601 /*
602 * Set the palette up.
603 */
604 pal = NULL;
605 logpal = NULL;
606 init_palette();
607
608 has_focus = (GetForegroundWindow() == hwnd);
609 UpdateWindow(hwnd);
610
611 if (GetMessage(&msg, NULL, 0, 0) == 1) {
612 int timer_id = 0, long_timer = 0;
613
614 while (msg.message != WM_QUIT) {
615 /* Sometimes DispatchMessage calls routines that use their own
616 * GetMessage loop, setup this timer so we get some control back.
617 *
618 * Also call term_update() from the timer so that if the host
619 * is sending data flat out we still do redraws.
620 */
621 if (timer_id && long_timer) {
622 KillTimer(hwnd, timer_id);
623 long_timer = timer_id = 0;
624 }
625 if (!timer_id)
626 timer_id = SetTimer(hwnd, 1, 20, NULL);
627 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
628 DispatchMessage(&msg);
629
630 /* Make sure we blink everything that needs it. */
631 term_blink(0);
632
633 /* Send the paste buffer if there's anything to send */
634 term_paste();
635
636 /* If there's nothing new in the queue then we can do everything
637 * we've delayed, reading the socket, writing, and repainting
638 * the window.
639 */
640 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
641 continue;
642
643 if (pending_netevent) {
644 enact_pending_netevent();
645
646 /* Force the cursor blink on */
647 term_blink(1);
648
649 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
650 continue;
651 }
652
653 /* Okay there is now nothing to do so we make sure the screen is
654 * completely up to date then tell windows to call us in a little
655 * while.
656 */
657 if (timer_id) {
658 KillTimer(hwnd, timer_id);
659 timer_id = 0;
660 }
661 HideCaret(hwnd);
662 term_out();
663 term_update();
664 ShowCaret(hwnd);
665
666 flash_window(1); /* maintain */
667
668 if (in_vbell)
669 /* Hmm, term_update didn't want to do an update too soon ... */
670 timer_id = SetTimer(hwnd, 1, 50, NULL);
671 else if (!has_focus)
672 timer_id = SetTimer(hwnd, 1, 500, NULL);
673 else
674 timer_id = SetTimer(hwnd, 1, 100, NULL);
675 long_timer = 1;
676
677 /* There's no point rescanning everything in the message queue
678 * so we do an apparently unnecessary wait here
679 */
680 WaitMessage();
681 if (GetMessage(&msg, NULL, 0, 0) != 1)
682 break;
683 }
684 }
685
686 /*
687 * Clean up.
688 */
689 deinit_fonts();
690 sfree(logpal);
691 if (pal)
692 DeleteObject(pal);
693 WSACleanup();
694
695 if (cfg.protocol == PROT_SSH) {
696 random_save_seed();
697 #ifdef MSCRYPTOAPI
698 crypto_wrapup();
699 #endif
700 }
701
702 return msg.wParam;
703 }
704
705 /*
706 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
707 */
708 char *do_select(SOCKET skt, int startup)
709 {
710 int msg, events;
711 if (startup) {
712 msg = WM_NETEVENT;
713 events = (FD_CONNECT | FD_READ | FD_WRITE |
714 FD_OOB | FD_CLOSE | FD_ACCEPT);
715 } else {
716 msg = events = 0;
717 }
718 if (!hwnd)
719 return "do_select(): internal error (hwnd==NULL)";
720 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
721 switch (WSAGetLastError()) {
722 case WSAENETDOWN:
723 return "Network is down";
724 default:
725 return "WSAAsyncSelect(): unknown error";
726 }
727 }
728 return NULL;
729 }
730
731 /*
732 * set or clear the "raw mouse message" mode
733 */
734 void set_raw_mouse_mode(int activate)
735 {
736 send_raw_mouse = activate;
737 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
738 }
739
740 /*
741 * Print a message box and close the connection.
742 */
743 void connection_fatal(char *fmt, ...)
744 {
745 va_list ap;
746 char stuff[200];
747
748 va_start(ap, fmt);
749 vsprintf(stuff, fmt, ap);
750 va_end(ap);
751 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
752 if (cfg.close_on_exit == COE_ALWAYS)
753 PostQuitMessage(1);
754 else {
755 session_closed = TRUE;
756 SetWindowText(hwnd, "PuTTY (inactive)");
757 }
758 }
759
760 /*
761 * Actually do the job requested by a WM_NETEVENT
762 */
763 static void enact_pending_netevent(void)
764 {
765 static int reentering = 0;
766 extern int select_result(WPARAM, LPARAM);
767 int ret;
768
769 if (reentering)
770 return; /* don't unpend the pending */
771
772 pending_netevent = FALSE;
773
774 reentering = 1;
775 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
776 reentering = 0;
777
778 if (ret == 0 && !session_closed) {
779 /* Abnormal exits will already have set session_closed and taken
780 * appropriate action. */
781 if (cfg.close_on_exit == COE_ALWAYS ||
782 cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
783 else {
784 session_closed = TRUE;
785 SetWindowText(hwnd, "PuTTY (inactive)");
786 MessageBox(hwnd, "Connection closed by remote host",
787 "PuTTY", MB_OK | MB_ICONINFORMATION);
788 }
789 }
790 }
791
792 /*
793 * Copy the colour palette from the configuration data into defpal.
794 * This is non-trivial because the colour indices are different.
795 */
796 static void cfgtopalette(void)
797 {
798 int i;
799 static const int ww[] = {
800 6, 7, 8, 9, 10, 11, 12, 13,
801 14, 15, 16, 17, 18, 19, 20, 21,
802 0, 1, 2, 3, 4, 4, 5, 5
803 };
804
805 for (i = 0; i < 24; i++) {
806 int w = ww[i];
807 defpal[i].rgbtRed = cfg.colours[w][0];
808 defpal[i].rgbtGreen = cfg.colours[w][1];
809 defpal[i].rgbtBlue = cfg.colours[w][2];
810 }
811 }
812
813 /*
814 * Set up the colour palette.
815 */
816 static void init_palette(void)
817 {
818 int i;
819 HDC hdc = GetDC(hwnd);
820 if (hdc) {
821 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
822 logpal = smalloc(sizeof(*logpal)
823 - sizeof(logpal->palPalEntry)
824 + NCOLOURS * sizeof(PALETTEENTRY));
825 logpal->palVersion = 0x300;
826 logpal->palNumEntries = NCOLOURS;
827 for (i = 0; i < NCOLOURS; i++) {
828 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
829 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
830 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
831 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
832 }
833 pal = CreatePalette(logpal);
834 if (pal) {
835 SelectPalette(hdc, pal, FALSE);
836 RealizePalette(hdc);
837 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
838 }
839 }
840 ReleaseDC(hwnd, hdc);
841 }
842 if (pal)
843 for (i = 0; i < NCOLOURS; i++)
844 colours[i] = PALETTERGB(defpal[i].rgbtRed,
845 defpal[i].rgbtGreen,
846 defpal[i].rgbtBlue);
847 else
848 for (i = 0; i < NCOLOURS; i++)
849 colours[i] = RGB(defpal[i].rgbtRed,
850 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
851 }
852
853 /*
854 * Initialise all the fonts we will need initially. There may be as many as
855 * three or as few as one. The other (poentially) twentyone fonts are done
856 * if/when they are needed.
857 *
858 * We also:
859 *
860 * - check the font width and height, correcting our guesses if
861 * necessary.
862 *
863 * - verify that the bold font is the same width as the ordinary
864 * one, and engage shadow bolding if not.
865 *
866 * - verify that the underlined font is the same width as the
867 * ordinary one (manual underlining by means of line drawing can
868 * be done in a pinch).
869 */
870 static void init_fonts(int pick_width, int pick_height)
871 {
872 TEXTMETRIC tm;
873 CPINFO cpinfo;
874 int fontsize[3];
875 int i;
876 HDC hdc;
877 int fw_dontcare, fw_bold;
878
879 for (i = 0; i < FONT_MAXNO; i++)
880 fonts[i] = NULL;
881
882 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
883 und_mode = UND_FONT;
884
885 if (cfg.fontisbold) {
886 fw_dontcare = FW_BOLD;
887 fw_bold = FW_HEAVY;
888 } else {
889 fw_dontcare = FW_DONTCARE;
890 fw_bold = FW_BOLD;
891 }
892
893 hdc = GetDC(hwnd);
894
895 if (pick_height)
896 font_height = pick_height;
897 else {
898 font_height = cfg.fontheight;
899 if (font_height > 0) {
900 font_height =
901 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
902 }
903 }
904 font_width = pick_width;
905
906 #define f(i,c,w,u) \
907 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
908 c, OUT_DEFAULT_PRECIS, \
909 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
910 FIXED_PITCH | FF_DONTCARE, cfg.font)
911
912 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
913
914 lfont.lfHeight = font_height;
915 lfont.lfWidth = font_width;
916 lfont.lfEscapement = 0;
917 lfont.lfOrientation = 0;
918 lfont.lfWeight = fw_dontcare;
919 lfont.lfItalic = FALSE;
920 lfont.lfUnderline = FALSE;
921 lfont.lfStrikeOut = FALSE;
922 lfont.lfCharSet = cfg.fontcharset;
923 lfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
924 lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
925 lfont.lfQuality = DEFAULT_QUALITY;
926 lfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
927 strncpy(lfont.lfFaceName, cfg.font, LF_FACESIZE);
928
929 SelectObject(hdc, fonts[FONT_NORMAL]);
930 GetTextMetrics(hdc, &tm);
931
932 if (pick_width == 0 || pick_height == 0) {
933 font_height = tm.tmHeight;
934 font_width = tm.tmAveCharWidth;
935 }
936 font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth);
937
938 #ifdef RDB_DEBUG_PATCH
939 debug(23, "Primary font H=%d, AW=%d, MW=%d",
940 tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth);
941 #endif
942
943 {
944 CHARSETINFO info;
945 DWORD cset = tm.tmCharSet;
946 memset(&info, 0xFF, sizeof(info));
947
948 /* !!! Yes the next line is right */
949 if (cset == OEM_CHARSET)
950 font_codepage = GetOEMCP();
951 else
952 if (TranslateCharsetInfo
953 ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
954 info.ciACP;
955 else
956 font_codepage = -1;
957
958 GetCPInfo(font_codepage, &cpinfo);
959 dbcs_screenfont = (cpinfo.MaxCharSize > 1);
960 }
961
962 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
963
964 /*
965 * Some fonts, e.g. 9-pt Courier, draw their underlines
966 * outside their character cell. We successfully prevent
967 * screen corruption by clipping the text output, but then
968 * we lose the underline completely. Here we try to work
969 * out whether this is such a font, and if it is, we set a
970 * flag that causes underlines to be drawn by hand.
971 *
972 * Having tried other more sophisticated approaches (such
973 * as examining the TEXTMETRIC structure or requesting the
974 * height of a string), I think we'll do this the brute
975 * force way: we create a small bitmap, draw an underlined
976 * space on it, and test to see whether any pixels are
977 * foreground-coloured. (Since we expect the underline to
978 * go all the way across the character cell, we only search
979 * down a single column of the bitmap, half way across.)
980 */
981 {
982 HDC und_dc;
983 HBITMAP und_bm, und_oldbm;
984 int i, gotit;
985 COLORREF c;
986
987 und_dc = CreateCompatibleDC(hdc);
988 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
989 und_oldbm = SelectObject(und_dc, und_bm);
990 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
991 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
992 SetTextColor(und_dc, RGB(255, 255, 255));
993 SetBkColor(und_dc, RGB(0, 0, 0));
994 SetBkMode(und_dc, OPAQUE);
995 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
996 gotit = FALSE;
997 for (i = 0; i < font_height; i++) {
998 c = GetPixel(und_dc, font_width / 2, i);
999 if (c != RGB(0, 0, 0))
1000 gotit = TRUE;
1001 }
1002 SelectObject(und_dc, und_oldbm);
1003 DeleteObject(und_bm);
1004 DeleteDC(und_dc);
1005 if (!gotit) {
1006 und_mode = UND_LINE;
1007 DeleteObject(fonts[FONT_UNDERLINE]);
1008 fonts[FONT_UNDERLINE] = 0;
1009 }
1010 }
1011
1012 if (bold_mode == BOLD_FONT) {
1013 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
1014 }
1015 #undef f
1016
1017 descent = tm.tmAscent + 1;
1018 if (descent >= font_height)
1019 descent = font_height - 1;
1020
1021 for (i = 0; i < 3; i++) {
1022 if (fonts[i]) {
1023 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
1024 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
1025 else
1026 fontsize[i] = -i;
1027 } else
1028 fontsize[i] = -i;
1029 }
1030
1031 ReleaseDC(hwnd, hdc);
1032
1033 if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
1034 und_mode = UND_LINE;
1035 DeleteObject(fonts[FONT_UNDERLINE]);
1036 fonts[FONT_UNDERLINE] = 0;
1037 }
1038
1039 if (bold_mode == BOLD_FONT &&
1040 fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
1041 bold_mode = BOLD_SHADOW;
1042 DeleteObject(fonts[FONT_BOLD]);
1043 fonts[FONT_BOLD] = 0;
1044 }
1045 fontflag[0] = fontflag[1] = fontflag[2] = 1;
1046
1047 init_ucs_tables();
1048 }
1049
1050 static void another_font(int fontno)
1051 {
1052 int basefont;
1053 int fw_dontcare, fw_bold;
1054 int c, u, w, x;
1055 char *s;
1056
1057 if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1058 return;
1059
1060 basefont = (fontno & ~(FONT_BOLDUND));
1061 if (basefont != fontno && !fontflag[basefont])
1062 another_font(basefont);
1063
1064 if (cfg.fontisbold) {
1065 fw_dontcare = FW_BOLD;
1066 fw_bold = FW_HEAVY;
1067 } else {
1068 fw_dontcare = FW_DONTCARE;
1069 fw_bold = FW_BOLD;
1070 }
1071
1072 c = cfg.fontcharset;
1073 w = fw_dontcare;
1074 u = FALSE;
1075 s = cfg.font;
1076 x = font_width;
1077
1078 if (fontno & FONT_WIDE)
1079 x *= 2;
1080 if (fontno & FONT_NARROW)
1081 x = (x+1)/2;
1082 if (fontno & FONT_OEM)
1083 c = OEM_CHARSET;
1084 if (fontno & FONT_BOLD)
1085 w = fw_bold;
1086 if (fontno & FONT_UNDERLINE)
1087 u = TRUE;
1088
1089 fonts[fontno] =
1090 CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1091 FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1092 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1093 FIXED_PITCH | FF_DONTCARE, s);
1094
1095 fontflag[fontno] = 1;
1096 }
1097
1098 static void deinit_fonts(void)
1099 {
1100 int i;
1101 for (i = 0; i < FONT_MAXNO; i++) {
1102 if (fonts[i])
1103 DeleteObject(fonts[i]);
1104 fonts[i] = 0;
1105 fontflag[i] = 0;
1106 }
1107 }
1108
1109 void request_resize(int w, int h)
1110 {
1111 int width, height;
1112
1113 /* If the window is maximized supress resizing attempts */
1114 if (IsZoomed(hwnd)) {
1115 if (cfg.resize_action != RESIZE_FONT)
1116 return;
1117 }
1118
1119 if (cfg.resize_action == RESIZE_DISABLED) return;
1120 if (h == rows && w == cols) return;
1121
1122 /* Sanity checks ... */
1123 {
1124 static int first_time = 1;
1125 static RECT ss;
1126
1127 switch (first_time) {
1128 case 1:
1129 /* Get the size of the screen */
1130 if (GetClientRect(GetDesktopWindow(), &ss))
1131 /* first_time = 0 */ ;
1132 else {
1133 first_time = 2;
1134 break;
1135 }
1136 case 0:
1137 /* Make sure the values are sane */
1138 width = (ss.right - ss.left - extra_width) / 4;
1139 height = (ss.bottom - ss.top - extra_height) / 6;
1140
1141 if (w > width || h > height)
1142 return;
1143 if (w < 15)
1144 w = 15;
1145 if (h < 1)
1146 h = 1;
1147 }
1148 }
1149
1150 term_size(h, w, cfg.savelines);
1151
1152 if (cfg.resize_action != RESIZE_FONT) {
1153 width = extra_width + font_width * w;
1154 height = extra_height + font_height * h;
1155
1156 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1157 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1158 SWP_NOMOVE | SWP_NOZORDER);
1159 } else
1160 reset_window(0);
1161
1162 InvalidateRect(hwnd, NULL, TRUE);
1163 }
1164
1165 static void reset_window(int reinit) {
1166 /*
1167 * This function decides how to resize or redraw when the
1168 * user changes something.
1169 *
1170 * This function doesn't like to change the terminal size but if the
1171 * font size is locked that may be it's only soluion.
1172 */
1173 int win_width, win_height;
1174 RECT cr, wr;
1175
1176 #ifdef RDB_DEBUG_PATCH
1177 debug((27, "reset_window()"));
1178 #endif
1179
1180 /* Current window sizes ... */
1181 GetWindowRect(hwnd, &wr);
1182 GetClientRect(hwnd, &cr);
1183
1184 win_width = cr.right - cr.left;
1185 win_height = cr.bottom - cr.top;
1186
1187 /* Are we being forced to reload the fonts ? */
1188 if (reinit>1) {
1189 #ifdef RDB_DEBUG_PATCH
1190 debug((27, "reset_window() -- Forced deinit"));
1191 #endif
1192 deinit_fonts();
1193 init_fonts(0,0);
1194 }
1195
1196 /* Oh, looks like we're minimised */
1197 if (win_width == 0 || win_height == 0)
1198 return;
1199
1200 /* Is the window out of position ? */
1201 if ( !reinit &&
1202 (offset_width != (win_width-font_width*cols)/2 ||
1203 offset_height != (win_height-font_height*rows)/2) ){
1204 offset_width = (win_width-font_width*cols)/2;
1205 offset_height = (win_height-font_height*rows)/2;
1206 InvalidateRect(hwnd, NULL, TRUE);
1207 #ifdef RDB_DEBUG_PATCH
1208 debug((27, "reset_window() -> Reposition terminal"));
1209 #endif
1210 }
1211
1212 if (IsZoomed(hwnd)) {
1213 /* We're fullscreen, this means we must not change the size of
1214 * the window so it's the font size or the terminal itself.
1215 */
1216
1217 extra_width = wr.right - wr.left - cr.right + cr.left;
1218 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1219
1220 if (cfg.resize_action == RESIZE_FONT) {
1221 if ( font_width != win_width/cols ||
1222 font_height != win_height/rows) {
1223 deinit_fonts();
1224 init_fonts(win_width/cols, win_height/rows);
1225 offset_width = (win_width-font_width*cols)/2;
1226 offset_height = (win_height-font_height*rows)/2;
1227 InvalidateRect(hwnd, NULL, TRUE);
1228 #ifdef RDB_DEBUG_PATCH
1229 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1230 font_width, font_height));
1231 #endif
1232 }
1233 } else {
1234 if ( font_width != win_width/cols ||
1235 font_height != win_height/rows) {
1236 /* Our only choice at this point is to change the
1237 * size of the terminal; Oh well.
1238 */
1239 term_size( win_height/font_height, win_width/font_width,
1240 cfg.savelines);
1241 offset_width = (win_width-font_width*cols)/2;
1242 offset_height = (win_height-font_height*rows)/2;
1243 InvalidateRect(hwnd, NULL, TRUE);
1244 #ifdef RDB_DEBUG_PATCH
1245 debug((27, "reset_window() -> Zoomed term_size"));
1246 #endif
1247 }
1248 }
1249 return;
1250 }
1251
1252 /* Hmm, a force re-init means we should ignore the current window
1253 * so we resize to the default font size.
1254 */
1255 if (reinit>0) {
1256 #ifdef RDB_DEBUG_PATCH
1257 debug((27, "reset_window() -> Forced re-init"));
1258 #endif
1259
1260 offset_width = offset_height = cfg.window_border;
1261 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1262 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1263
1264 if (win_width != font_width*cols + offset_width*2 ||
1265 win_height != font_height*rows + offset_height*2) {
1266
1267 /* If this is too large windows will resize it to the maximum
1268 * allowed window size, we will then be back in here and resize
1269 * the font or terminal to fit.
1270 */
1271 SetWindowPos(hwnd, NULL, 0, 0,
1272 font_width*cols + extra_width,
1273 font_height*rows + extra_height,
1274 SWP_NOMOVE | SWP_NOZORDER);
1275 }
1276 return;
1277 }
1278
1279 /* Okay the user doesn't want us to change the font so we try the
1280 * window. But that may be too big for the screen which forces us
1281 * to change the terminal.
1282 */
1283 if ((cfg.resize_action != RESIZE_FONT && reinit==0) || reinit>0) {
1284 offset_width = offset_height = cfg.window_border;
1285 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1286 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1287
1288 if (win_width != font_width*cols + offset_width*2 ||
1289 win_height != font_height*rows + offset_height*2) {
1290
1291 static RECT ss;
1292 int width, height;
1293
1294 GetClientRect(GetDesktopWindow(), &ss);
1295 width = (ss.right - ss.left - extra_width) / font_width;
1296 height = (ss.bottom - ss.top - extra_height) / font_height;
1297
1298 /* Grrr too big */
1299 if ( rows > height || cols > width ) {
1300 if ( height > rows ) height = rows;
1301 if ( width > cols ) width = cols;
1302 term_size(height, width, cfg.savelines);
1303 #ifdef RDB_DEBUG_PATCH
1304 debug((27, "reset_window() -> term resize to (%d,%d)",
1305 height, width));
1306 #endif
1307 }
1308
1309 SetWindowPos(hwnd, NULL, 0, 0,
1310 font_width*cols + extra_width,
1311 font_height*rows + extra_height,
1312 SWP_NOMOVE | SWP_NOZORDER);
1313
1314 InvalidateRect(hwnd, NULL, TRUE);
1315 #ifdef RDB_DEBUG_PATCH
1316 debug((27, "reset_window() -> window resize to (%d,%d)",
1317 font_width*cols + extra_width,
1318 font_height*rows + extra_height));
1319 #endif
1320 }
1321 return;
1322 }
1323
1324 /* We're allowed to or must change the font but do we want to ? */
1325
1326 if (font_width != (win_width-cfg.window_border*2)/cols ||
1327 font_height != (win_height-cfg.window_border*2)/rows) {
1328
1329 deinit_fonts();
1330 init_fonts((win_width-cfg.window_border*2)/cols,
1331 (win_height-cfg.window_border*2)/rows);
1332 offset_width = (win_width-font_width*cols)/2;
1333 offset_height = (win_height-font_height*rows)/2;
1334
1335 extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2;
1336 extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2;
1337
1338 InvalidateRect(hwnd, NULL, TRUE);
1339 #ifdef RDB_DEBUG_PATCH
1340 debug((25, "reset_window() -> font resize to (%d,%d)",
1341 font_width, font_height));
1342 #endif
1343 }
1344 }
1345
1346 static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
1347 {
1348 int thistime = GetMessageTime();
1349
1350 if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1351 lastbtn = MBT_NOTHING;
1352 term_mouse(b, MA_CLICK, x, y, shift, ctrl, alt);
1353 return;
1354 }
1355
1356 if (lastbtn == b && thistime - lasttime < dbltime) {
1357 lastact = (lastact == MA_CLICK ? MA_2CLK :
1358 lastact == MA_2CLK ? MA_3CLK :
1359 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1360 } else {
1361 lastbtn = b;
1362 lastact = MA_CLICK;
1363 }
1364 if (lastact != MA_NOTHING)
1365 term_mouse(b, lastact, x, y, shift, ctrl, alt);
1366 lasttime = thistime;
1367 }
1368
1369 /*
1370 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1371 * into a cooked one (SELECT, EXTEND, PASTE).
1372 */
1373 Mouse_Button translate_button(Mouse_Button button)
1374 {
1375 if (button == MBT_LEFT)
1376 return MBT_SELECT;
1377 if (button == MBT_MIDDLE)
1378 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1379 if (button == MBT_RIGHT)
1380 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1381 return 0; /* shouldn't happen */
1382 }
1383
1384 static void show_mouseptr(int show)
1385 {
1386 static int cursor_visible = 1;
1387 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1388 show = 1;
1389 if (cursor_visible && !show)
1390 ShowCursor(FALSE);
1391 else if (!cursor_visible && show)
1392 ShowCursor(TRUE);
1393 cursor_visible = show;
1394 }
1395
1396 static int is_alt_pressed(void)
1397 {
1398 BYTE keystate[256];
1399 int r = GetKeyboardState(keystate);
1400 if (!r)
1401 return FALSE;
1402 if (keystate[VK_MENU] & 0x80)
1403 return TRUE;
1404 if (keystate[VK_RMENU] & 0x80)
1405 return TRUE;
1406 return FALSE;
1407 }
1408
1409 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1410 WPARAM wParam, LPARAM lParam)
1411 {
1412 HDC hdc;
1413 static int ignore_clip = FALSE;
1414 static int resizing = FALSE;
1415 static int need_backend_resize = FALSE;
1416
1417 switch (message) {
1418 case WM_TIMER:
1419 if (pending_netevent)
1420 enact_pending_netevent();
1421 term_out();
1422 noise_regular();
1423 HideCaret(hwnd);
1424 term_update();
1425 ShowCaret(hwnd);
1426 if (cfg.ping_interval > 0) {
1427 time_t now;
1428 time(&now);
1429 if (now - last_movement > cfg.ping_interval) {
1430 back->special(TS_PING);
1431 last_movement = now;
1432 }
1433 }
1434 net_pending_errors();
1435 return 0;
1436 case WM_CREATE:
1437 break;
1438 case WM_CLOSE:
1439 show_mouseptr(1);
1440 if (!cfg.warn_on_close || session_closed ||
1441 MessageBox(hwnd,
1442 "Are you sure you want to close this session?",
1443 "PuTTY Exit Confirmation",
1444 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1445 DestroyWindow(hwnd);
1446 return 0;
1447 case WM_DESTROY:
1448 show_mouseptr(1);
1449 PostQuitMessage(0);
1450 return 0;
1451 case WM_SYSCOMMAND:
1452 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1453 case IDM_SHOWLOG:
1454 showeventlog(hwnd);
1455 break;
1456 case IDM_NEWSESS:
1457 case IDM_DUPSESS:
1458 case IDM_SAVEDSESS:
1459 {
1460 char b[2048];
1461 char c[30], *cl;
1462 int freecl = FALSE;
1463 STARTUPINFO si;
1464 PROCESS_INFORMATION pi;
1465 HANDLE filemap = NULL;
1466
1467 if (wParam == IDM_DUPSESS) {
1468 /*
1469 * Allocate a file-mapping memory chunk for the
1470 * config structure.
1471 */
1472 SECURITY_ATTRIBUTES sa;
1473 Config *p;
1474
1475 sa.nLength = sizeof(sa);
1476 sa.lpSecurityDescriptor = NULL;
1477 sa.bInheritHandle = TRUE;
1478 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1479 &sa,
1480 PAGE_READWRITE,
1481 0, sizeof(Config), NULL);
1482 if (filemap) {
1483 p = (Config *) MapViewOfFile(filemap,
1484 FILE_MAP_WRITE,
1485 0, 0, sizeof(Config));
1486 if (p) {
1487 *p = cfg; /* structure copy */
1488 UnmapViewOfFile(p);
1489 }
1490 }
1491 sprintf(c, "putty &%p", filemap);
1492 cl = c;
1493 } else if (wParam == IDM_SAVEDSESS) {
1494 char *session =
1495 sessions[(lParam - IDM_SAVED_MIN) / 16];
1496 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1497 if (!cl)
1498 cl = NULL; /* not a very important failure mode */
1499 else {
1500 sprintf(cl, "putty @%s", session);
1501 freecl = TRUE;
1502 }
1503 } else
1504 cl = NULL;
1505
1506 GetModuleFileName(NULL, b, sizeof(b) - 1);
1507 si.cb = sizeof(si);
1508 si.lpReserved = NULL;
1509 si.lpDesktop = NULL;
1510 si.lpTitle = NULL;
1511 si.dwFlags = 0;
1512 si.cbReserved2 = 0;
1513 si.lpReserved2 = NULL;
1514 CreateProcess(b, cl, NULL, NULL, TRUE,
1515 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1516
1517 if (filemap)
1518 CloseHandle(filemap);
1519 if (freecl)
1520 sfree(cl);
1521 }
1522 break;
1523 case IDM_RECONF:
1524 {
1525 Config prev_cfg;
1526 int init_lvl = 1;
1527
1528 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1529 prev_cfg = cfg;
1530
1531 if (!do_reconfig(hwnd))
1532 break;
1533
1534 /* If user forcibly disables full-screen, gracefully unzoom */
1535 if (full_screen && !cfg.fullscreenonaltenter) {
1536 flip_full_screen();
1537 }
1538
1539 if (strcmp(prev_cfg.logfilename, cfg.logfilename) ||
1540 prev_cfg.logtype != cfg.logtype) {
1541 logfclose(); /* reset logging */
1542 logfopen();
1543 }
1544
1545 sfree(logpal);
1546 /*
1547 * Flush the line discipline's edit buffer in the
1548 * case where local editing has just been disabled.
1549 */
1550 ldisc_send(NULL, 0, 0);
1551 if (pal)
1552 DeleteObject(pal);
1553 logpal = NULL;
1554 pal = NULL;
1555 cfgtopalette();
1556 init_palette();
1557
1558 /* Screen size changed ? */
1559 if (cfg.height != prev_cfg.height ||
1560 cfg.width != prev_cfg.width ||
1561 cfg.savelines != prev_cfg.savelines ||
1562 cfg.resize_action != RESIZE_TERM)
1563 term_size(cfg.height, cfg.width, cfg.savelines);
1564
1565 /* Enable or disable the scroll bar, etc */
1566 {
1567 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1568 LONG nexflag, exflag =
1569 GetWindowLong(hwnd, GWL_EXSTYLE);
1570
1571 nexflag = exflag;
1572 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
1573 if (cfg.alwaysontop) {
1574 nexflag |= WS_EX_TOPMOST;
1575 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1576 SWP_NOMOVE | SWP_NOSIZE);
1577 } else {
1578 nexflag &= ~(WS_EX_TOPMOST);
1579 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1580 SWP_NOMOVE | SWP_NOSIZE);
1581 }
1582 }
1583 if (cfg.sunken_edge)
1584 nexflag |= WS_EX_CLIENTEDGE;
1585 else
1586 nexflag &= ~(WS_EX_CLIENTEDGE);
1587
1588 nflg = flag;
1589 if (cfg.scrollbar)
1590 nflg |= WS_VSCROLL;
1591 else
1592 nflg &= ~WS_VSCROLL;
1593 if (cfg.resize_action == RESIZE_DISABLED)
1594 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1595 else
1596 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1597
1598 if (nflg != flag || nexflag != exflag) {
1599 if (nflg != flag)
1600 SetWindowLong(hwnd, GWL_STYLE, nflg);
1601 if (nexflag != exflag)
1602 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1603
1604 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1605 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1606 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1607 SWP_FRAMECHANGED);
1608
1609 init_lvl = 2;
1610 }
1611 }
1612
1613 /* Oops */
1614 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
1615 force_normal(hwnd);
1616 init_lvl = 2;
1617 }
1618
1619 set_title(cfg.wintitle);
1620 if (IsIconic(hwnd)) {
1621 SetWindowText(hwnd,
1622 cfg.win_name_always ? window_name :
1623 icon_name);
1624 }
1625
1626 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1627 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1628 cfg.fontisbold != prev_cfg.fontisbold ||
1629 cfg.fontheight != prev_cfg.fontheight ||
1630 cfg.fontcharset != prev_cfg.fontcharset ||
1631 cfg.vtmode != prev_cfg.vtmode ||
1632 cfg.bold_colour != prev_cfg.bold_colour ||
1633 (cfg.resize_action != RESIZE_FONT &&
1634 prev_cfg.resize_action == RESIZE_FONT))
1635 init_lvl = 2;
1636
1637 InvalidateRect(hwnd, NULL, TRUE);
1638 reset_window(init_lvl);
1639 net_pending_errors();
1640 }
1641 break;
1642 case IDM_COPYALL:
1643 term_copyall();
1644 break;
1645 case IDM_CLRSB:
1646 term_clrsb();
1647 break;
1648 case IDM_RESET:
1649 term_pwron();
1650 break;
1651 case IDM_TEL_AYT:
1652 back->special(TS_AYT);
1653 net_pending_errors();
1654 break;
1655 case IDM_TEL_BRK:
1656 back->special(TS_BRK);
1657 net_pending_errors();
1658 break;
1659 case IDM_TEL_SYNCH:
1660 back->special(TS_SYNCH);
1661 net_pending_errors();
1662 break;
1663 case IDM_TEL_EC:
1664 back->special(TS_EC);
1665 net_pending_errors();
1666 break;
1667 case IDM_TEL_EL:
1668 back->special(TS_EL);
1669 net_pending_errors();
1670 break;
1671 case IDM_TEL_GA:
1672 back->special(TS_GA);
1673 net_pending_errors();
1674 break;
1675 case IDM_TEL_NOP:
1676 back->special(TS_NOP);
1677 net_pending_errors();
1678 break;
1679 case IDM_TEL_ABORT:
1680 back->special(TS_ABORT);
1681 net_pending_errors();
1682 break;
1683 case IDM_TEL_AO:
1684 back->special(TS_AO);
1685 net_pending_errors();
1686 break;
1687 case IDM_TEL_IP:
1688 back->special(TS_IP);
1689 net_pending_errors();
1690 break;
1691 case IDM_TEL_SUSP:
1692 back->special(TS_SUSP);
1693 net_pending_errors();
1694 break;
1695 case IDM_TEL_EOR:
1696 back->special(TS_EOR);
1697 net_pending_errors();
1698 break;
1699 case IDM_TEL_EOF:
1700 back->special(TS_EOF);
1701 net_pending_errors();
1702 break;
1703 case IDM_ABOUT:
1704 showabout(hwnd);
1705 break;
1706 case SC_MOUSEMENU:
1707 /*
1708 * We get this if the System menu has been activated
1709 * using the mouse.
1710 */
1711 show_mouseptr(1);
1712 break;
1713 case SC_KEYMENU:
1714 /*
1715 * We get this if the System menu has been activated
1716 * using the keyboard. This might happen from within
1717 * TranslateKey, in which case it really wants to be
1718 * followed by a `space' character to actually _bring
1719 * the menu up_ rather than just sitting there in
1720 * `ready to appear' state.
1721 */
1722 show_mouseptr(1); /* make sure pointer is visible */
1723 if( lParam == 0 )
1724 PostMessage(hwnd, WM_CHAR, ' ', 0);
1725 break;
1726 case IDM_FULLSCREEN:
1727 flip_full_screen();
1728 break;
1729 default:
1730 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1731 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1732 }
1733 }
1734 break;
1735
1736 #define X_POS(l) ((int)(short)LOWORD(l))
1737 #define Y_POS(l) ((int)(short)HIWORD(l))
1738
1739 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1740 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1741 #define WHEEL_DELTA 120
1742 case WM_MOUSEWHEEL:
1743 {
1744 wheel_accumulator += (short) HIWORD(wParam);
1745 wParam = LOWORD(wParam);
1746
1747 /* process events when the threshold is reached */
1748 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1749 int b;
1750
1751 /* reduce amount for next time */
1752 if (wheel_accumulator > 0) {
1753 b = MBT_WHEEL_UP;
1754 wheel_accumulator -= WHEEL_DELTA;
1755 } else if (wheel_accumulator < 0) {
1756 b = MBT_WHEEL_DOWN;
1757 wheel_accumulator += WHEEL_DELTA;
1758 } else
1759 break;
1760
1761 if (send_raw_mouse) {
1762 /* send a mouse-down followed by a mouse up */
1763
1764 term_mouse(b,
1765 MA_CLICK,
1766 TO_CHR_X(X_POS(lParam)),
1767 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1768 wParam & MK_CONTROL, is_alt_pressed());
1769 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1770 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1771 wParam & MK_CONTROL, is_alt_pressed());
1772 } else {
1773 /* trigger a scroll */
1774 term_scroll(0,
1775 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1776 }
1777 }
1778 return 0;
1779 }
1780 case WM_LBUTTONDOWN:
1781 case WM_MBUTTONDOWN:
1782 case WM_RBUTTONDOWN:
1783 case WM_LBUTTONUP:
1784 case WM_MBUTTONUP:
1785 case WM_RBUTTONUP:
1786 {
1787 int button, press;
1788
1789 switch (message) {
1790 case WM_LBUTTONDOWN:
1791 button = MBT_LEFT;
1792 press = 1;
1793 break;
1794 case WM_MBUTTONDOWN:
1795 button = MBT_MIDDLE;
1796 press = 1;
1797 break;
1798 case WM_RBUTTONDOWN:
1799 button = MBT_RIGHT;
1800 press = 1;
1801 break;
1802 case WM_LBUTTONUP:
1803 button = MBT_LEFT;
1804 press = 0;
1805 break;
1806 case WM_MBUTTONUP:
1807 button = MBT_MIDDLE;
1808 press = 0;
1809 break;
1810 case WM_RBUTTONUP:
1811 button = MBT_RIGHT;
1812 press = 0;
1813 break;
1814 default:
1815 button = press = 0; /* shouldn't happen */
1816 }
1817 show_mouseptr(1);
1818 if (press) {
1819 click(button,
1820 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1821 wParam & MK_SHIFT, wParam & MK_CONTROL,
1822 is_alt_pressed());
1823 SetCapture(hwnd);
1824 } else {
1825 term_mouse(button, MA_RELEASE,
1826 TO_CHR_X(X_POS(lParam)),
1827 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1828 wParam & MK_CONTROL, is_alt_pressed());
1829 ReleaseCapture();
1830 }
1831 }
1832 return 0;
1833 case WM_MOUSEMOVE:
1834 show_mouseptr(1);
1835 /*
1836 * Add the mouse position and message time to the random
1837 * number noise.
1838 */
1839 noise_ultralight(lParam);
1840
1841 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1842 Mouse_Button b;
1843 if (wParam & MK_LBUTTON)
1844 b = MBT_LEFT;
1845 else if (wParam & MK_MBUTTON)
1846 b = MBT_MIDDLE;
1847 else
1848 b = MBT_RIGHT;
1849 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1850 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1851 wParam & MK_CONTROL, is_alt_pressed());
1852 }
1853 return 0;
1854 case WM_NCMOUSEMOVE:
1855 show_mouseptr(1);
1856 noise_ultralight(lParam);
1857 return 0;
1858 case WM_IGNORE_CLIP:
1859 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1860 break;
1861 case WM_DESTROYCLIPBOARD:
1862 if (!ignore_clip)
1863 term_deselect();
1864 ignore_clip = FALSE;
1865 return 0;
1866 case WM_PAINT:
1867 {
1868 PAINTSTRUCT p;
1869 HideCaret(hwnd);
1870 hdc = BeginPaint(hwnd, &p);
1871 if (pal) {
1872 SelectPalette(hdc, pal, TRUE);
1873 RealizePalette(hdc);
1874 }
1875 term_paint(hdc,
1876 (p.rcPaint.left-offset_width)/font_width,
1877 (p.rcPaint.top-offset_height)/font_height,
1878 (p.rcPaint.right-offset_width-1)/font_width,
1879 (p.rcPaint.bottom-offset_height-1)/font_height);
1880
1881 if (p.fErase ||
1882 p.rcPaint.left < offset_width ||
1883 p.rcPaint.top < offset_height ||
1884 p.rcPaint.right >= offset_width + font_width*cols ||
1885 p.rcPaint.bottom>= offset_height + font_height*rows)
1886 {
1887 HBRUSH fillcolour, oldbrush;
1888 HPEN edge, oldpen;
1889 fillcolour = CreateSolidBrush (
1890 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1891 oldbrush = SelectObject(hdc, fillcolour);
1892 edge = CreatePen(PS_SOLID, 0,
1893 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1894 oldpen = SelectObject(hdc, edge);
1895
1896 ExcludeClipRect(hdc,
1897 offset_width, offset_height,
1898 offset_width+font_width*cols,
1899 offset_height+font_height*rows);
1900
1901 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
1902 p.rcPaint.right, p.rcPaint.bottom);
1903
1904 // SelectClipRgn(hdc, NULL);
1905
1906 SelectObject(hdc, oldbrush);
1907 DeleteObject(fillcolour);
1908 SelectObject(hdc, oldpen);
1909 DeleteObject(edge);
1910 }
1911 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1912 SelectObject(hdc, GetStockObject(WHITE_PEN));
1913 EndPaint(hwnd, &p);
1914 ShowCaret(hwnd);
1915 }
1916 return 0;
1917 case WM_NETEVENT:
1918 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1919 * but the only one that's likely to try to overload us is FD_READ.
1920 * This means buffering just one is fine.
1921 */
1922 if (pending_netevent)
1923 enact_pending_netevent();
1924
1925 pending_netevent = TRUE;
1926 pend_netevent_wParam = wParam;
1927 pend_netevent_lParam = lParam;
1928 if (WSAGETSELECTEVENT(lParam) != FD_READ)
1929 enact_pending_netevent();
1930
1931 time(&last_movement);
1932 return 0;
1933 case WM_SETFOCUS:
1934 has_focus = TRUE;
1935 CreateCaret(hwnd, caretbm, font_width, font_height);
1936 ShowCaret(hwnd);
1937 flash_window(0); /* stop */
1938 compose_state = 0;
1939 term_out();
1940 term_update();
1941 break;
1942 case WM_KILLFOCUS:
1943 show_mouseptr(1);
1944 has_focus = FALSE;
1945 DestroyCaret();
1946 term_out();
1947 term_update();
1948 break;
1949 case WM_ENTERSIZEMOVE:
1950 #ifdef RDB_DEBUG_PATCH
1951 debug((27, "WM_ENTERSIZEMOVE"));
1952 #endif
1953 EnableSizeTip(1);
1954 resizing = TRUE;
1955 need_backend_resize = FALSE;
1956 break;
1957 case WM_EXITSIZEMOVE:
1958 EnableSizeTip(0);
1959 resizing = FALSE;
1960 #ifdef RDB_DEBUG_PATCH
1961 debug((27, "WM_EXITSIZEMOVE"));
1962 #endif
1963 if (need_backend_resize) {
1964 term_size(cfg.height, cfg.width, cfg.savelines);
1965 InvalidateRect(hwnd, NULL, TRUE);
1966 }
1967 break;
1968 case WM_SIZING:
1969 /*
1970 * This does two jobs:
1971 * 1) Keep the sizetip uptodate
1972 * 2) Make sure the window size is _stepped_ in units of the font size.
1973 */
1974 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
1975 int width, height, w, h, ew, eh;
1976 LPRECT r = (LPRECT) lParam;
1977
1978 if ( !need_backend_resize &&
1979 (cfg.height != rows || cfg.width != cols )) {
1980 /*
1981 * Great! It seems the host has been changing the terminal
1982 * size, well the user is now grabbing so this is probably
1983 * the least confusing solution in the long run even though
1984 * it a is suprise. Unfortunatly the only way to prevent
1985 * this seems to be to let the host change the window size
1986 * and as that's a user option we're still right back here.
1987 */
1988 term_size(cfg.height, cfg.width, cfg.savelines);
1989 reset_window(2);
1990 InvalidateRect(hwnd, NULL, TRUE);
1991 need_backend_resize = TRUE;
1992 }
1993
1994 width = r->right - r->left - extra_width;
1995 height = r->bottom - r->top - extra_height;
1996 w = (width + font_width / 2) / font_width;
1997 if (w < 1)
1998 w = 1;
1999 h = (height + font_height / 2) / font_height;
2000 if (h < 1)
2001 h = 1;
2002 UpdateSizeTip(hwnd, w, h);
2003 ew = width - w * font_width;
2004 eh = height - h * font_height;
2005 if (ew != 0) {
2006 if (wParam == WMSZ_LEFT ||
2007 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2008 r->left += ew;
2009 else
2010 r->right -= ew;
2011 }
2012 if (eh != 0) {
2013 if (wParam == WMSZ_TOP ||
2014 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2015 r->top += eh;
2016 else
2017 r->bottom -= eh;
2018 }
2019 if (ew || eh)
2020 return 1;
2021 else
2022 return 0;
2023 } else {
2024 int width, height, w, h, rv = 0;
2025 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2026 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2027 LPRECT r = (LPRECT) lParam;
2028
2029 width = r->right - r->left - ex_width;
2030 height = r->bottom - r->top - ex_height;
2031
2032 w = (width + cols/2)/cols;
2033 h = (height + rows/2)/rows;
2034 if ( r->right != r->left + w*cols + ex_width)
2035 rv = 1;
2036
2037 if (wParam == WMSZ_LEFT ||
2038 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2039 r->left = r->right - w*cols - ex_width;
2040 else
2041 r->right = r->left + w*cols + ex_width;
2042
2043 if (r->bottom != r->top + h*rows + ex_height)
2044 rv = 1;
2045
2046 if (wParam == WMSZ_TOP ||
2047 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2048 r->top = r->bottom - h*rows - ex_height;
2049 else
2050 r->bottom = r->top + h*rows + ex_height;
2051
2052 return rv;
2053 }
2054 /* break; (never reached) */
2055 case WM_SIZE:
2056 #ifdef RDB_DEBUG_PATCH
2057 debug((27, "WM_SIZE %s (%d,%d)",
2058 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2059 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2060 (wParam == SIZE_RESTORED && resizing) ? "to":
2061 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2062 "...",
2063 LOWORD(lParam), HIWORD(lParam)));
2064 #endif
2065 if (wParam == SIZE_MINIMIZED) {
2066 SetWindowText(hwnd,
2067 cfg.win_name_always ? window_name : icon_name);
2068 break;
2069 }
2070 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2071 SetWindowText(hwnd, window_name);
2072
2073 if (cfg.resize_action == RESIZE_DISABLED) {
2074 /* A resize, well it better be a minimize. */
2075 reset_window(-1);
2076 } else {
2077
2078 int width, height, w, h;
2079
2080 width = LOWORD(lParam);
2081 height = HIWORD(lParam);
2082
2083 if (!resizing) {
2084 if (wParam == SIZE_MAXIMIZED) {
2085 was_zoomed = 1;
2086 prev_rows = rows;
2087 prev_cols = cols;
2088 if (cfg.resize_action != RESIZE_FONT) {
2089 w = width / font_width;
2090 if (w < 1) w = 1;
2091 h = height / font_height;
2092 if (h < 1) h = 1;
2093
2094 term_size(h, w, cfg.savelines);
2095 }
2096 reset_window(0);
2097 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2098 was_zoomed = 0;
2099 if (cfg.resize_action != RESIZE_FONT)
2100 term_size(prev_rows, prev_cols, cfg.savelines);
2101 reset_window(0);
2102 }
2103 /* This is an unexpected resize, these will normally happen
2104 * if the window is too large. Probably either the user
2105 * selected a huge font or the screen size has changed.
2106 *
2107 * This is also called with minimize.
2108 */
2109 else reset_window(-1);
2110 }
2111
2112 /*
2113 * Don't call back->size in mid-resize. (To prevent
2114 * massive numbers of resize events getting sent
2115 * down the connection during an NT opaque drag.)
2116 */
2117 if (resizing) {
2118 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
2119 need_backend_resize = TRUE;
2120 w = (width-cfg.window_border*2) / font_width;
2121 if (w < 1) w = 1;
2122 h = (height-cfg.window_border*2) / font_height;
2123 if (h < 1) h = 1;
2124
2125 cfg.height = h;
2126 cfg.width = w;
2127 } else
2128 reset_window(0);
2129 }
2130 }
2131 return 0;
2132 case WM_VSCROLL:
2133 switch (LOWORD(wParam)) {
2134 case SB_BOTTOM:
2135 term_scroll(-1, 0);
2136 break;
2137 case SB_TOP:
2138 term_scroll(+1, 0);
2139 break;
2140 case SB_LINEDOWN:
2141 term_scroll(0, +1);
2142 break;
2143 case SB_LINEUP:
2144 term_scroll(0, -1);
2145 break;
2146 case SB_PAGEDOWN:
2147 term_scroll(0, +rows / 2);
2148 break;
2149 case SB_PAGEUP:
2150 term_scroll(0, -rows / 2);
2151 break;
2152 case SB_THUMBPOSITION:
2153 case SB_THUMBTRACK:
2154 term_scroll(1, HIWORD(wParam));
2155 break;
2156 }
2157 break;
2158 case WM_PALETTECHANGED:
2159 if ((HWND) wParam != hwnd && pal != NULL) {
2160 HDC hdc = get_ctx();
2161 if (hdc) {
2162 if (RealizePalette(hdc) > 0)
2163 UpdateColors(hdc);
2164 free_ctx(hdc);
2165 }
2166 }
2167 break;
2168 case WM_QUERYNEWPALETTE:
2169 if (pal != NULL) {
2170 HDC hdc = get_ctx();
2171 if (hdc) {
2172 if (RealizePalette(hdc) > 0)
2173 UpdateColors(hdc);
2174 free_ctx(hdc);
2175 return TRUE;
2176 }
2177 }
2178 return FALSE;
2179 case WM_KEYDOWN:
2180 case WM_SYSKEYDOWN:
2181 case WM_KEYUP:
2182 case WM_SYSKEYUP:
2183 /*
2184 * Add the scan code and keypress timing to the random
2185 * number noise.
2186 */
2187 noise_ultralight(lParam);
2188
2189 /*
2190 * We don't do TranslateMessage since it disassociates the
2191 * resulting CHAR message from the KEYDOWN that sparked it,
2192 * which we occasionally don't want. Instead, we process
2193 * KEYDOWN, and call the Win32 translator functions so that
2194 * we get the translations under _our_ control.
2195 */
2196 {
2197 unsigned char buf[20];
2198 int len;
2199
2200 if (wParam == VK_PROCESSKEY) {
2201 MSG m;
2202 m.hwnd = hwnd;
2203 m.message = WM_KEYDOWN;
2204 m.wParam = wParam;
2205 m.lParam = lParam & 0xdfff;
2206 TranslateMessage(&m);
2207 } else {
2208 len = TranslateKey(message, wParam, lParam, buf);
2209 if (len == -1)
2210 return DefWindowProc(hwnd, message, wParam, lParam);
2211
2212 if (len != 0) {
2213 /*
2214 * Interrupt an ongoing paste. I'm not sure
2215 * this is sensible, but for the moment it's
2216 * preferable to having to faff about buffering
2217 * things.
2218 */
2219 term_nopaste();
2220
2221 /*
2222 * We need not bother about stdin backlogs
2223 * here, because in GUI PuTTY we can't do
2224 * anything about it anyway; there's no means
2225 * of asking Windows to hold off on KEYDOWN
2226 * messages. We _have_ to buffer everything
2227 * we're sent.
2228 */
2229 ldisc_send(buf, len, 1);
2230 show_mouseptr(0);
2231 }
2232 }
2233 }
2234 net_pending_errors();
2235 return 0;
2236 case WM_INPUTLANGCHANGE:
2237 {
2238 /* wParam == Font number */
2239 /* lParam == Locale */
2240 char lbuf[20];
2241 HKL NewInputLocale = (HKL) lParam;
2242
2243 // lParam == GetKeyboardLayout(0);
2244
2245 GetLocaleInfo(LOWORD(NewInputLocale),
2246 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
2247
2248 kbd_codepage = atoi(lbuf);
2249 }
2250 break;
2251 case WM_IME_NOTIFY:
2252 if(wParam == IMN_SETOPENSTATUS) {
2253 HIMC hImc = ImmGetContext(hwnd);
2254 ImmSetCompositionFont(hImc, &lfont);
2255 ImmReleaseContext(hwnd, hImc);
2256 return 0;
2257 }
2258 break;
2259 case WM_IME_COMPOSITION:
2260 {
2261 HIMC hIMC;
2262 int n;
2263 char *buff;
2264
2265 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2266 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2267
2268 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2269 break; /* fall back to DefWindowProc */
2270
2271 hIMC = ImmGetContext(hwnd);
2272 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2273
2274 if (n > 0) {
2275 buff = (char*) smalloc(n);
2276 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2277 luni_send((unsigned short *)buff, n / 2, 1);
2278 free(buff);
2279 }
2280 ImmReleaseContext(hwnd, hIMC);
2281 return 1;
2282 }
2283
2284 case WM_IME_CHAR:
2285 if (wParam & 0xFF00) {
2286 unsigned char buf[2];
2287
2288 buf[1] = wParam;
2289 buf[0] = wParam >> 8;
2290 lpage_send(kbd_codepage, buf, 2, 1);
2291 } else {
2292 char c = (unsigned char) wParam;
2293 lpage_send(kbd_codepage, &c, 1, 1);
2294 }
2295 return (0);
2296 case WM_CHAR:
2297 case WM_SYSCHAR:
2298 /*
2299 * Nevertheless, we are prepared to deal with WM_CHAR
2300 * messages, should they crop up. So if someone wants to
2301 * post the things to us as part of a macro manoeuvre,
2302 * we're ready to cope.
2303 */
2304 {
2305 char c = (unsigned char)wParam;
2306 lpage_send(CP_ACP, &c, 1, 1);
2307 }
2308 return 0;
2309 case WM_SETCURSOR:
2310 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2311 SetCursor(LoadCursor(NULL, IDC_ARROW));
2312 return TRUE;
2313 }
2314 }
2315
2316 return DefWindowProc(hwnd, message, wParam, lParam);
2317 }
2318
2319 /*
2320 * Move the system caret. (We maintain one, even though it's
2321 * invisible, for the benefit of blind people: apparently some
2322 * helper software tracks the system caret, so we should arrange to
2323 * have one.)
2324 */
2325 void sys_cursor(int x, int y)
2326 {
2327 COMPOSITIONFORM cf;
2328 HIMC hIMC;
2329
2330 if (!has_focus) return;
2331
2332 SetCaretPos(x * font_width + offset_width,
2333 y * font_height + offset_height);
2334
2335 /* IMM calls on Win98 and beyond only */
2336 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2337
2338 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2339 osVersion.dwMinorVersion == 0) return; /* 95 */
2340
2341 /* we should have the IMM functions */
2342 hIMC = ImmGetContext(hwnd);
2343 cf.dwStyle = CFS_POINT;
2344 cf.ptCurrentPos.x = x * font_width + offset_width;
2345 cf.ptCurrentPos.y = y * font_height + offset_height;
2346 ImmSetCompositionWindow(hIMC, &cf);
2347
2348 ImmReleaseContext(hwnd, hIMC);
2349 }
2350
2351 /*
2352 * Draw a line of text in the window, at given character
2353 * coordinates, in given attributes.
2354 *
2355 * We are allowed to fiddle with the contents of `text'.
2356 */
2357 void do_text(Context ctx, int x, int y, char *text, int len,
2358 unsigned long attr, int lattr)
2359 {
2360 COLORREF fg, bg, t;
2361 int nfg, nbg, nfont;
2362 HDC hdc = ctx;
2363 RECT line_box;
2364 int force_manual_underline = 0;
2365 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2366 int char_width = fnt_width;
2367 int text_adjust = 0;
2368 static int *IpDx = 0, IpDxLEN = 0;
2369
2370 if (attr & ATTR_WIDE)
2371 char_width *= 2;
2372
2373 if (len > IpDxLEN || IpDx[0] != char_width) {
2374 int i;
2375 if (len > IpDxLEN) {
2376 sfree(IpDx);
2377 IpDx = smalloc((len + 16) * sizeof(int));
2378 IpDxLEN = (len + 16);
2379 }
2380 for (i = 0; i < IpDxLEN; i++)
2381 IpDx[i] = char_width;
2382 }
2383
2384 /* Only want the left half of double width lines */
2385 if (lattr != LATTR_NORM && x*2 >= cols)
2386 return;
2387
2388 x *= fnt_width;
2389 y *= font_height;
2390 x += offset_width;
2391 y += offset_height;
2392
2393 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2394 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2395 attr ^= ATTR_CUR_XOR;
2396 }
2397
2398 nfont = 0;
2399 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2400 /* Assume a poorman font is borken in other ways too. */
2401 lattr = LATTR_WIDE;
2402 } else
2403 switch (lattr) {
2404 case LATTR_NORM:
2405 break;
2406 case LATTR_WIDE:
2407 nfont |= FONT_WIDE;
2408 break;
2409 default:
2410 nfont |= FONT_WIDE + FONT_HIGH;
2411 break;
2412 }
2413 if (attr & ATTR_NARROW)
2414 nfont |= FONT_NARROW;
2415
2416 /* Special hack for the VT100 linedraw glyphs. */
2417 if ((attr & CSET_MASK) == 0x2300) {
2418 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2419 switch ((unsigned char) (text[0])) {
2420 case 0xBA:
2421 text_adjust = -2 * font_height / 5;
2422 break;
2423 case 0xBB:
2424 text_adjust = -1 * font_height / 5;
2425 break;
2426 case 0xBC:
2427 text_adjust = font_height / 5;
2428 break;
2429 case 0xBD:
2430 text_adjust = 2 * font_height / 5;
2431 break;
2432 }
2433 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2434 text_adjust *= 2;
2435 attr &= ~CSET_MASK;
2436 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2437 attr |= (unitab_xterm['q'] & CSET_MASK);
2438 if (attr & ATTR_UNDER) {
2439 attr &= ~ATTR_UNDER;
2440 force_manual_underline = 1;
2441 }
2442 }
2443 }
2444
2445 /* Anything left as an original character set is unprintable. */
2446 if (DIRECT_CHAR(attr)) {
2447 attr &= ~CSET_MASK;
2448 attr |= 0xFF00;
2449 memset(text, 0xFD, len);
2450 }
2451
2452 /* OEM CP */
2453 if ((attr & CSET_MASK) == ATTR_OEMCP)
2454 nfont |= FONT_OEM;
2455
2456 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2457 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2458 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2459 nfont |= FONT_BOLD;
2460 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2461 nfont |= FONT_UNDERLINE;
2462 another_font(nfont);
2463 if (!fonts[nfont]) {
2464 if (nfont & FONT_UNDERLINE)
2465 force_manual_underline = 1;
2466 /* Don't do the same for manual bold, it could be bad news. */
2467
2468 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2469 }
2470 another_font(nfont);
2471 if (!fonts[nfont])
2472 nfont = FONT_NORMAL;
2473 if (attr & ATTR_REVERSE) {
2474 t = nfg;
2475 nfg = nbg;
2476 nbg = t;
2477 }
2478 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2479 nfg++;
2480 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2481 nbg++;
2482 fg = colours[nfg];
2483 bg = colours[nbg];
2484 SelectObject(hdc, fonts[nfont]);
2485 SetTextColor(hdc, fg);
2486 SetBkColor(hdc, bg);
2487 SetBkMode(hdc, OPAQUE);
2488 line_box.left = x;
2489 line_box.top = y;
2490 line_box.right = x + char_width * len;
2491 line_box.bottom = y + font_height;
2492
2493 /* Only want the left half of double width lines */
2494 if (line_box.right > font_width*cols+offset_width)
2495 line_box.right = font_width*cols+offset_width;
2496
2497 /* We're using a private area for direct to font. (512 chars.) */
2498 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2499 /* Ho Hum, dbcs fonts are a PITA! */
2500 /* To display on W9x I have to convert to UCS */
2501 static wchar_t *uni_buf = 0;
2502 static int uni_len = 0;
2503 int nlen, mptr;
2504 if (len > uni_len) {
2505 sfree(uni_buf);
2506 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2507 }
2508
2509 for(nlen = mptr = 0; mptr<len; mptr++) {
2510 uni_buf[nlen] = 0xFFFD;
2511 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2512 IpDx[nlen] += char_width;
2513 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2514 text+mptr, 2, uni_buf+nlen, 1);
2515 mptr++;
2516 }
2517 else
2518 {
2519 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2520 text+mptr, 1, uni_buf+nlen, 1);
2521 }
2522 nlen++;
2523 }
2524 if (nlen <= 0)
2525 return; /* Eeek! */
2526
2527 ExtTextOutW(hdc, x,
2528 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2529 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2530 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2531 SetBkMode(hdc, TRANSPARENT);
2532 ExtTextOutW(hdc, x - 1,
2533 y - font_height * (lattr ==
2534 LATTR_BOT) + text_adjust,
2535 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2536 }
2537
2538 IpDx[0] = -1;
2539 } else if (DIRECT_FONT(attr)) {
2540 ExtTextOut(hdc, x,
2541 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2542 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2543 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2544 SetBkMode(hdc, TRANSPARENT);
2545
2546 /* GRR: This draws the character outside it's box and can leave
2547 * 'droppings' even with the clip box! I suppose I could loop it
2548 * one character at a time ... yuk.
2549 *
2550 * Or ... I could do a test print with "W", and use +1 or -1 for this
2551 * shift depending on if the leftmost column is blank...
2552 */
2553 ExtTextOut(hdc, x - 1,
2554 y - font_height * (lattr ==
2555 LATTR_BOT) + text_adjust,
2556 ETO_CLIPPED, &line_box, text, len, IpDx);
2557 }
2558 } else {
2559 /* And 'normal' unicode characters */
2560 static WCHAR *wbuf = NULL;
2561 static int wlen = 0;
2562 int i;
2563 if (wlen < len) {
2564 sfree(wbuf);
2565 wlen = len;
2566 wbuf = smalloc(wlen * sizeof(WCHAR));
2567 }
2568 for (i = 0; i < len; i++)
2569 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2570
2571 ExtTextOutW(hdc, x,
2572 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2573 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2574
2575 /* And the shadow bold hack. */
2576 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2577 SetBkMode(hdc, TRANSPARENT);
2578 ExtTextOutW(hdc, x - 1,
2579 y - font_height * (lattr ==
2580 LATTR_BOT) + text_adjust,
2581 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2582 }
2583 }
2584 if (lattr != LATTR_TOP && (force_manual_underline ||
2585 (und_mode == UND_LINE
2586 && (attr & ATTR_UNDER)))) {
2587 HPEN oldpen;
2588 int dec = descent;
2589 if (lattr == LATTR_BOT)
2590 dec = dec * 2 - font_height;
2591
2592 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2593 MoveToEx(hdc, x, y + dec, NULL);
2594 LineTo(hdc, x + len * char_width, y + dec);
2595 oldpen = SelectObject(hdc, oldpen);
2596 DeleteObject(oldpen);
2597 }
2598 }
2599
2600 void do_cursor(Context ctx, int x, int y, char *text, int len,
2601 unsigned long attr, int lattr)
2602 {
2603
2604 int fnt_width;
2605 int char_width;
2606 HDC hdc = ctx;
2607 int ctype = cfg.cursor_type;
2608
2609 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2610 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2611 do_text(ctx, x, y, text, len, attr, lattr);
2612 return;
2613 }
2614 ctype = 2;
2615 attr |= TATTR_RIGHTCURS;
2616 }
2617
2618 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2619 if (attr & ATTR_WIDE)
2620 char_width *= 2;
2621 x *= fnt_width;
2622 y *= font_height;
2623 x += offset_width;
2624 y += offset_height;
2625
2626 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2627 POINT pts[5];
2628 HPEN oldpen;
2629 pts[0].x = pts[1].x = pts[4].x = x;
2630 pts[2].x = pts[3].x = x + char_width - 1;
2631 pts[0].y = pts[3].y = pts[4].y = y;
2632 pts[1].y = pts[2].y = y + font_height - 1;
2633 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2634 Polyline(hdc, pts, 5);
2635 oldpen = SelectObject(hdc, oldpen);
2636 DeleteObject(oldpen);
2637 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2638 int startx, starty, dx, dy, length, i;
2639 if (ctype == 1) {
2640 startx = x;
2641 starty = y + descent;
2642 dx = 1;
2643 dy = 0;
2644 length = char_width;
2645 } else {
2646 int xadjust = 0;
2647 if (attr & TATTR_RIGHTCURS)
2648 xadjust = char_width - 1;
2649 startx = x + xadjust;
2650 starty = y;
2651 dx = 0;
2652 dy = 1;
2653 length = font_height;
2654 }
2655 if (attr & TATTR_ACTCURS) {
2656 HPEN oldpen;
2657 oldpen =
2658 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2659 MoveToEx(hdc, startx, starty, NULL);
2660 LineTo(hdc, startx + dx * length, starty + dy * length);
2661 oldpen = SelectObject(hdc, oldpen);
2662 DeleteObject(oldpen);
2663 } else {
2664 for (i = 0; i < length; i++) {
2665 if (i % 2 == 0) {
2666 SetPixel(hdc, startx, starty, colours[23]);
2667 }
2668 startx += dx;
2669 starty += dy;
2670 }
2671 }
2672 }
2673 }
2674
2675 /* This function gets the actual width of a character in the normal font.
2676 */
2677 int CharWidth(Context ctx, int uc) {
2678 HDC hdc = ctx;
2679 int ibuf = 0;
2680
2681 /* If the font max is the same as the font ave width then this
2682 * function is a no-op.
2683 */
2684 if (!font_dualwidth) return 1;
2685
2686 switch (uc & CSET_MASK) {
2687 case ATTR_ASCII:
2688 uc = unitab_line[uc & 0xFF];
2689 break;
2690 case ATTR_LINEDRW:
2691 uc = unitab_xterm[uc & 0xFF];
2692 break;
2693 case ATTR_SCOACS:
2694 uc = unitab_scoacs[uc & 0xFF];
2695 break;
2696 }
2697 if (DIRECT_FONT(uc)) {
2698 if (dbcs_screenfont) return 1;
2699
2700 /* Speedup, I know of no font where ascii is the wrong width */
2701 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2702 return 1;
2703
2704 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2705 SelectObject(hdc, fonts[FONT_NORMAL]);
2706 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2707 another_font(FONT_OEM);
2708 if (!fonts[FONT_OEM]) return 0;
2709
2710 SelectObject(hdc, fonts[FONT_OEM]);
2711 } else
2712 return 0;
2713
2714 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2715 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2716 return 0;
2717 } else {
2718 /* Speedup, I know of no font where ascii is the wrong width */
2719 if (uc >= ' ' && uc <= '~') return 1;
2720
2721 SelectObject(hdc, fonts[FONT_NORMAL]);
2722 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2723 /* Okay that one worked */ ;
2724 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2725 /* This should work on 9x too, but it's "less accurate" */ ;
2726 else
2727 return 0;
2728 }
2729
2730 ibuf += font_width / 2 -1;
2731 ibuf /= font_width;
2732
2733 return ibuf;
2734 }
2735
2736 /*
2737 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2738 * codes. Returns number of bytes used or zero to drop the message
2739 * or -1 to forward the message to windows.
2740 */
2741 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2742 unsigned char *output)
2743 {
2744 BYTE keystate[256];
2745 int scan, left_alt = 0, key_down, shift_state;
2746 int r, i, code;
2747 unsigned char *p = output;
2748 static int alt_sum = 0;
2749
2750 HKL kbd_layout = GetKeyboardLayout(0);
2751
2752 static WORD keys[3];
2753 static int compose_char = 0;
2754 static WPARAM compose_key = 0;
2755
2756 r = GetKeyboardState(keystate);
2757 if (!r)
2758 memset(keystate, 0, sizeof(keystate));
2759 else {
2760 #if 0
2761 #define SHOW_TOASCII_RESULT
2762 { /* Tell us all about key events */
2763 static BYTE oldstate[256];
2764 static int first = 1;
2765 static int scan;
2766 int ch;
2767 if (first)
2768 memcpy(oldstate, keystate, sizeof(oldstate));
2769 first = 0;
2770
2771 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2772 debug(("+"));
2773 } else if ((HIWORD(lParam) & KF_UP)
2774 && scan == (HIWORD(lParam) & 0xFF)) {
2775 debug((". U"));
2776 } else {
2777 debug((".\n"));
2778 if (wParam >= VK_F1 && wParam <= VK_F20)
2779 debug(("K_F%d", wParam + 1 - VK_F1));
2780 else
2781 switch (wParam) {
2782 case VK_SHIFT:
2783 debug(("SHIFT"));
2784 break;
2785 case VK_CONTROL:
2786 debug(("CTRL"));
2787 break;
2788 case VK_MENU:
2789 debug(("ALT"));
2790 break;
2791 default:
2792 debug(("VK_%02x", wParam));
2793 }
2794 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2795 debug(("*"));
2796 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2797
2798 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2799 if (ch >= ' ' && ch <= '~')
2800 debug((", '%c'", ch));
2801 else if (ch)
2802 debug((", $%02x", ch));
2803
2804 if (keys[0])
2805 debug((", KB0=%02x", keys[0]));
2806 if (keys[1])
2807 debug((", KB1=%02x", keys[1]));
2808 if (keys[2])
2809 debug((", KB2=%02x", keys[2]));
2810
2811 if ((keystate[VK_SHIFT] & 0x80) != 0)
2812 debug((", S"));
2813 if ((keystate[VK_CONTROL] & 0x80) != 0)
2814 debug((", C"));
2815 if ((HIWORD(lParam) & KF_EXTENDED))
2816 debug((", E"));
2817 if ((HIWORD(lParam) & KF_UP))
2818 debug((", U"));
2819 }
2820
2821 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2822 else if ((HIWORD(lParam) & KF_UP))
2823 oldstate[wParam & 0xFF] ^= 0x80;
2824 else
2825 oldstate[wParam & 0xFF] ^= 0x81;
2826
2827 for (ch = 0; ch < 256; ch++)
2828 if (oldstate[ch] != keystate[ch])
2829 debug((", M%02x=%02x", ch, keystate[ch]));
2830
2831 memcpy(oldstate, keystate, sizeof(oldstate));
2832 }
2833 #endif
2834
2835 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2836 keystate[VK_RMENU] = keystate[VK_MENU];
2837 }
2838
2839
2840 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2841 if ((cfg.funky_type == 3 ||
2842 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2843 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2844
2845 wParam = VK_EXECUTE;
2846
2847 /* UnToggle NUMLock */
2848 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2849 keystate[VK_NUMLOCK] ^= 1;
2850 }
2851
2852 /* And write back the 'adjusted' state */
2853 SetKeyboardState(keystate);
2854 }
2855
2856 /* Disable Auto repeat if required */
2857 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2858 return 0;
2859
2860 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2861 left_alt = 1;
2862
2863 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2864
2865 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2866 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2867 if (cfg.ctrlaltkeys)
2868 keystate[VK_MENU] = 0;
2869 else {
2870 keystate[VK_RMENU] = 0x80;
2871 left_alt = 0;
2872 }
2873 }
2874
2875 alt_pressed = (left_alt && key_down);
2876
2877 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2878 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2879 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2880
2881 /* Note if AltGr was pressed and if it was used as a compose key */
2882 if (!compose_state) {
2883 compose_key = 0x100;
2884 if (cfg.compose_key) {
2885 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2886 compose_key = wParam;
2887 }
2888 if (wParam == VK_APPS)
2889 compose_key = wParam;
2890 }
2891
2892 if (wParam == compose_key) {
2893 if (compose_state == 0
2894 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2895 1;
2896 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2897 compose_state = 2;
2898 else
2899 compose_state = 0;
2900 } else if (compose_state == 1 && wParam != VK_CONTROL)
2901 compose_state = 0;
2902
2903 /*
2904 * Record that we pressed key so the scroll window can be reset, but
2905 * be careful to avoid Shift-UP/Down
2906 */
2907 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
2908 wParam != VK_MENU && wParam != VK_CONTROL) {
2909 seen_key_event = 1;
2910 }
2911
2912 if (compose_state > 1 && left_alt)
2913 compose_state = 0;
2914
2915 /* Sanitize the number pad if not using a PC NumPad */
2916 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2917 && cfg.funky_type != 2)
2918 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2919 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2920 int nParam = 0;
2921 switch (wParam) {
2922 case VK_INSERT:
2923 nParam = VK_NUMPAD0;
2924 break;
2925 case VK_END:
2926 nParam = VK_NUMPAD1;
2927 break;
2928 case VK_DOWN:
2929 nParam = VK_NUMPAD2;
2930 break;
2931 case VK_NEXT:
2932 nParam = VK_NUMPAD3;
2933 break;
2934 case VK_LEFT:
2935 nParam = VK_NUMPAD4;
2936 break;
2937 case VK_CLEAR:
2938 nParam = VK_NUMPAD5;
2939 break;
2940 case VK_RIGHT:
2941 nParam = VK_NUMPAD6;
2942 break;
2943 case VK_HOME:
2944 nParam = VK_NUMPAD7;
2945 break;
2946 case VK_UP:
2947 nParam = VK_NUMPAD8;
2948 break;
2949 case VK_PRIOR:
2950 nParam = VK_NUMPAD9;
2951 break;
2952 case VK_DELETE:
2953 nParam = VK_DECIMAL;
2954 break;
2955 }
2956 if (nParam) {
2957 if (keystate[VK_NUMLOCK] & 1)
2958 shift_state |= 1;
2959 wParam = nParam;
2960 }
2961 }
2962 }
2963
2964 /* If a key is pressed and AltGr is not active */
2965 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2966 /* Okay, prepare for most alts then ... */
2967 if (left_alt)
2968 *p++ = '\033';
2969
2970 /* Lets see if it's a pattern we know all about ... */
2971 if (wParam == VK_PRIOR && shift_state == 1) {
2972 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2973 return 0;
2974 }
2975 if (wParam == VK_NEXT && shift_state == 1) {
2976 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2977 return 0;
2978 }
2979 if (wParam == VK_INSERT && shift_state == 1) {
2980 term_do_paste();
2981 return 0;
2982 }
2983 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2984 return -1;
2985 }
2986 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2987 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2988 return -1;
2989 }
2990 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter) {
2991 flip_full_screen();
2992 return -1;
2993 }
2994 /* Control-Numlock for app-keypad mode switch */
2995 if (wParam == VK_PAUSE && shift_state == 2) {
2996 app_keypad_keys ^= 1;
2997 return 0;
2998 }
2999
3000 /* Nethack keypad */
3001 if (cfg.nethack_keypad && !left_alt) {
3002 switch (wParam) {
3003 case VK_NUMPAD1:
3004 *p++ = shift_state ? 'B' : 'b';
3005 return p - output;
3006 case VK_NUMPAD2:
3007 *p++ = shift_state ? 'J' : 'j';
3008 return p - output;
3009 case VK_NUMPAD3:
3010 *p++ = shift_state ? 'N' : 'n';
3011 return p - output;
3012 case VK_NUMPAD4:
3013 *p++ = shift_state ? 'H' : 'h';
3014 return p - output;
3015 case VK_NUMPAD5:
3016 *p++ = shift_state ? '.' : '.';
3017 return p - output;
3018 case VK_NUMPAD6:
3019 *p++ = shift_state ? 'L' : 'l';
3020 return p - output;
3021 case VK_NUMPAD7:
3022 *p++ = shift_state ? 'Y' : 'y';
3023 return p - output;
3024 case VK_NUMPAD8:
3025 *p++ = shift_state ? 'K' : 'k';
3026 return p - output;
3027 case VK_NUMPAD9:
3028 *p++ = shift_state ? 'U' : 'u';
3029 return p - output;
3030 }
3031 }
3032
3033 /* Application Keypad */
3034 if (!left_alt) {
3035 int xkey = 0;
3036
3037 if (cfg.funky_type == 3 ||
3038 (cfg.funky_type <= 1 &&
3039 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3040 case VK_EXECUTE:
3041 xkey = 'P';
3042 break;
3043 case VK_DIVIDE:
3044 xkey = 'Q';
3045 break;
3046 case VK_MULTIPLY:
3047 xkey = 'R';
3048 break;
3049 case VK_SUBTRACT:
3050 xkey = 'S';
3051 break;
3052 }
3053 if (app_keypad_keys && !cfg.no_applic_k)
3054 switch (wParam) {
3055 case VK_NUMPAD0:
3056 xkey = 'p';
3057 break;
3058 case VK_NUMPAD1:
3059 xkey = 'q';
3060 break;
3061 case VK_NUMPAD2:
3062 xkey = 'r';
3063 break;
3064 case VK_NUMPAD3:
3065 xkey = 's';
3066 break;
3067 case VK_NUMPAD4:
3068 xkey = 't';
3069 break;
3070 case VK_NUMPAD5:
3071 xkey = 'u';
3072 break;
3073 case VK_NUMPAD6:
3074 xkey = 'v';
3075 break;
3076 case VK_NUMPAD7:
3077 xkey = 'w';
3078 break;
3079 case VK_NUMPAD8:
3080 xkey = 'x';
3081 break;
3082 case VK_NUMPAD9:
3083 xkey = 'y';
3084 break;
3085
3086 case VK_DECIMAL:
3087 xkey = 'n';
3088 break;
3089 case VK_ADD:
3090 if (cfg.funky_type == 2) {
3091 if (shift_state)
3092 xkey = 'l';
3093 else
3094 xkey = 'k';
3095 } else if (shift_state)
3096 xkey = 'm';
3097 else
3098 xkey = 'l';
3099 break;
3100
3101 case VK_DIVIDE:
3102 if (cfg.funky_type == 2)
3103 xkey = 'o';
3104 break;
3105 case VK_MULTIPLY:
3106 if (cfg.funky_type == 2)
3107 xkey = 'j';
3108 break;
3109 case VK_SUBTRACT:
3110 if (cfg.funky_type == 2)
3111 xkey = 'm';
3112 break;
3113
3114 case VK_RETURN:
3115 if (HIWORD(lParam) & KF_EXTENDED)
3116 xkey = 'M';
3117 break;
3118 }
3119 if (xkey) {
3120 if (vt52_mode) {
3121 if (xkey >= 'P' && xkey <= 'S')
3122 p += sprintf((char *) p, "\x1B%c", xkey);
3123 else
3124 p += sprintf((char *) p, "\x1B?%c", xkey);
3125 } else
3126 p += sprintf((char *) p, "\x1BO%c", xkey);
3127 return p - output;
3128 }
3129 }
3130
3131 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
3132 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3133 *p++ = 0;
3134 return -2;
3135 }
3136 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3137 *p++ = 0x1B;
3138 *p++ = '[';
3139 *p++ = 'Z';
3140 return p - output;
3141 }
3142 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3143 *p++ = 0;
3144 return p - output;
3145 }
3146 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3147 *p++ = 160;
3148 return p - output;
3149 }
3150 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3151 *p++ = 3;
3152 *p++ = 0;
3153 return -2;
3154 }
3155 if (wParam == VK_PAUSE) { /* Break/Pause */
3156 *p++ = 26;
3157 *p++ = 0;
3158 return -2;
3159 }
3160 /* Control-2 to Control-8 are special */
3161 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3162 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3163 return p - output;
3164 }
3165 if (shift_state == 2 && wParam == 0xBD) {
3166 *p++ = 0x1F;
3167 return p - output;
3168 }
3169 if (shift_state == 2 && wParam == 0xDF) {
3170 *p++ = 0x1C;
3171 return p - output;
3172 }
3173 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3174 *p++ = '\r';
3175 *p++ = '\n';
3176 return p - output;
3177 }
3178
3179 /*
3180 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3181 * for integer decimal nn.)
3182 *
3183 * We also deal with the weird ones here. Linux VCs replace F1
3184 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3185 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3186 * respectively.
3187 */
3188 code = 0;
3189 switch (wParam) {
3190 case VK_F1:
3191 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3192 break;
3193 case VK_F2:
3194 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3195 break;
3196 case VK_F3:
3197 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3198 break;
3199 case VK_F4:
3200 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3201 break;
3202 case VK_F5:
3203 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3204 break;
3205 case VK_F6:
3206 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3207 break;
3208 case VK_F7:
3209 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3210 break;
3211 case VK_F8:
3212 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3213 break;
3214 case VK_F9:
3215 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3216 break;
3217 case VK_F10:
3218 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3219 break;
3220 case VK_F11:
3221 code = 23;
3222 break;
3223 case VK_F12:
3224 code = 24;
3225 break;
3226 case VK_F13:
3227 code = 25;
3228 break;
3229 case VK_F14:
3230 code = 26;
3231 break;
3232 case VK_F15:
3233 code = 28;
3234 break;
3235 case VK_F16:
3236 code = 29;
3237 break;
3238 case VK_F17:
3239 code = 31;
3240 break;
3241 case VK_F18:
3242 code = 32;
3243 break;
3244 case VK_F19:
3245 code = 33;
3246 break;
3247 case VK_F20:
3248 code = 34;
3249 break;
3250 }
3251 if ((shift_state&2) == 0) switch (wParam) {
3252 case VK_HOME:
3253 code = 1;
3254 break;
3255 case VK_INSERT:
3256 code = 2;
3257 break;
3258 case VK_DELETE:
3259 code = 3;
3260 break;
3261 case VK_END:
3262 code = 4;
3263 break;
3264 case VK_PRIOR:
3265 code = 5;
3266 break;
3267 case VK_NEXT:
3268 code = 6;
3269 break;
3270 }
3271 /* Reorder edit keys to physical order */
3272 if (cfg.funky_type == 3 && code <= 6)
3273 code = "\0\2\1\4\5\3\6"[code];
3274
3275 if (vt52_mode && code > 0 && code <= 6) {
3276 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3277 return p - output;
3278 }
3279
3280 if (cfg.funky_type == 5 && /* SCO function keys */
3281 code >= 11 && code <= 34) {
3282 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3283 int index = 0;
3284 switch (wParam) {
3285 case VK_F1: index = 0; break;
3286 case VK_F2: index = 1; break;
3287 case VK_F3: index = 2; break;
3288 case VK_F4: index = 3; break;
3289 case VK_F5: index = 4; break;
3290 case VK_F6: index = 5; break;
3291 case VK_F7: index = 6; break;
3292 case VK_F8: index = 7; break;
3293 case VK_F9: index = 8; break;
3294 case VK_F10: index = 9; break;
3295 case VK_F11: index = 10; break;
3296 case VK_F12: index = 11; break;
3297 }
3298 if (keystate[VK_SHIFT] & 0x80) index += 12;
3299 if (keystate[VK_CONTROL] & 0x80) index += 24;
3300 p += sprintf((char *) p, "\x1B[%c", codes[index]);
3301 return p - output;
3302 }
3303 if (cfg.funky_type == 5 && /* SCO small keypad */
3304 code >= 1 && code <= 6) {
3305 char codes[] = "HL.FIG";
3306 if (code == 3) {
3307 *p++ = '\x7F';
3308 } else {
3309 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3310 }
3311 return p - output;
3312 }
3313 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3314 int offt = 0;
3315 if (code > 15)
3316 offt++;
3317 if (code > 21)
3318 offt++;
3319 if (vt52_mode)
3320 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3321 else
3322 p +=
3323 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3324 return p - output;
3325 }
3326 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3327 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3328 return p - output;
3329 }
3330 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3331 if (vt52_mode)
3332 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3333 else
3334 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3335 return p - output;
3336 }
3337 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3338 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3339 return p - output;
3340 }
3341 if (code) {
3342 p += sprintf((char *) p, "\x1B[%d~", code);
3343 return p - output;
3344 }
3345
3346 /*
3347 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3348 * some reason seems to send VK_CLEAR to Windows...).
3349 */
3350 {
3351 char xkey = 0;
3352 switch (wParam) {
3353 case VK_UP:
3354 xkey = 'A';
3355 break;
3356 case VK_DOWN:
3357 xkey = 'B';
3358 break;
3359 case VK_RIGHT:
3360 xkey = 'C';
3361 break;
3362 case VK_LEFT:
3363 xkey = 'D';
3364 break;
3365 case VK_CLEAR:
3366 xkey = 'G';
3367 break;
3368 }
3369 if (xkey) {
3370 if (vt52_mode)
3371 p += sprintf((char *) p, "\x1B%c", xkey);
3372 else {
3373 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3374 /* VT100 & VT102 manuals both state the app cursor keys
3375 * only work if the app keypad is on.
3376 */
3377 if (!app_keypad_keys)
3378 app_flg = 0;
3379 /* Useful mapping of Ctrl-arrows */
3380 if (shift_state == 2)
3381 app_flg = !app_flg;
3382
3383 if (app_flg)
3384 p += sprintf((char *) p, "\x1BO%c", xkey);
3385 else
3386 p += sprintf((char *) p, "\x1B[%c", xkey);
3387 }
3388 return p - output;
3389 }
3390 }
3391
3392 /*
3393 * Finally, deal with Return ourselves. (Win95 seems to
3394 * foul it up when Alt is pressed, for some reason.)
3395 */
3396 if (wParam == VK_RETURN) { /* Return */
3397 *p++ = 0x0D;
3398 *p++ = 0;
3399 return -2;
3400 }
3401
3402 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3403 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3404 else
3405 alt_sum = 0;
3406 }
3407
3408 /* Okay we've done everything interesting; let windows deal with
3409 * the boring stuff */
3410 {
3411 BOOL capsOn=0;
3412
3413 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3414 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3415 capsOn= !left_alt;
3416 keystate[VK_CAPITAL] = 0;
3417 }
3418
3419 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3420 #ifdef SHOW_TOASCII_RESULT
3421 if (r == 1 && !key_down) {
3422 if (alt_sum) {
3423 if (in_utf || dbcs_screenfont)
3424 debug((", (U+%04x)", alt_sum));
3425 else
3426 debug((", LCH(%d)", alt_sum));
3427 } else {
3428 debug((", ACH(%d)", keys[0]));
3429 }
3430 } else if (r > 0) {
3431 int r1;
3432 debug((", ASC("));
3433 for (r1 = 0; r1 < r; r1++) {
3434 debug(("%s%d", r1 ? "," : "", keys[r1]));
3435 }
3436 debug((")"));
3437 }
3438 #endif
3439 if (r > 0) {
3440 WCHAR keybuf;
3441
3442 /*
3443 * Interrupt an ongoing paste. I'm not sure this is
3444 * sensible, but for the moment it's preferable to
3445 * having to faff about buffering things.
3446 */
3447 term_nopaste();
3448
3449 p = output;
3450 for (i = 0; i < r; i++) {
3451 unsigned char ch = (unsigned char) keys[i];
3452
3453 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3454 compose_char = ch;
3455 compose_state++;
3456 continue;
3457 }
3458 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3459 int nc;
3460 compose_state = 0;
3461
3462 if ((nc = check_compose(compose_char, ch)) == -1) {
3463 MessageBeep(MB_ICONHAND);
3464 return 0;
3465 }
3466 keybuf = nc;
3467 luni_send(&keybuf, 1, 1);
3468 continue;
3469 }
3470
3471 compose_state = 0;
3472
3473 if (!key_down) {
3474 if (alt_sum) {
3475 if (in_utf || dbcs_screenfont) {
3476 keybuf = alt_sum;
3477 luni_send(&keybuf, 1, 1);
3478 } else {
3479 ch = (char) alt_sum;
3480 /*
3481 * We need not bother about stdin
3482 * backlogs here, because in GUI PuTTY
3483 * we can't do anything about it
3484 * anyway; there's no means of asking
3485 * Windows to hold off on KEYDOWN
3486 * messages. We _have_ to buffer
3487 * everything we're sent.
3488 */
3489 ldisc_send(&ch, 1, 1);
3490 }
3491 alt_sum = 0;
3492 } else
3493 lpage_send(kbd_codepage, &ch, 1, 1);
3494 } else {
3495 if(capsOn && ch < 0x80) {
3496 WCHAR cbuf[2];
3497 cbuf[0] = 27;
3498 cbuf[1] = xlat_uskbd2cyrllic(ch);
3499 luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3500 } else {
3501 char cbuf[2];
3502 cbuf[0] = '\033';
3503 cbuf[1] = ch;
3504 lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3505 }
3506 }
3507 show_mouseptr(0);
3508 }
3509
3510 /* This is so the ALT-Numpad and dead keys work correctly. */
3511 keys[0] = 0;
3512
3513 return p - output;
3514 }
3515 /* If we're definitly not building up an ALT-54321 then clear it */
3516 if (!left_alt)
3517 keys[0] = 0;
3518 /* If we will be using alt_sum fix the 256s */
3519 else if (keys[0] && (in_utf || dbcs_screenfont))
3520 keys[0] = 10;
3521 }
3522
3523 /*
3524 * ALT alone may or may not want to bring up the System menu.
3525 * If it's not meant to, we return 0 on presses or releases of
3526 * ALT, to show that we've swallowed the keystroke. Otherwise
3527 * we return -1, which means Windows will give the keystroke
3528 * its default handling (i.e. bring up the System menu).
3529 */
3530 if (wParam == VK_MENU && !cfg.alt_only)
3531 return 0;
3532
3533 return -1;
3534 }
3535
3536 void set_title(char *title)
3537 {
3538 sfree(window_name);
3539 window_name = smalloc(1 + strlen(title));
3540 strcpy(window_name, title);
3541 if (cfg.win_name_always || !IsIconic(hwnd))
3542 SetWindowText(hwnd, title);
3543 }
3544
3545 void set_icon(char *title)
3546 {
3547 sfree(icon_name);
3548 icon_name = smalloc(1 + strlen(title));
3549 strcpy(icon_name, title);
3550 if (!cfg.win_name_always && IsIconic(hwnd))
3551 SetWindowText(hwnd, title);
3552 }
3553
3554 void set_sbar(int total, int start, int page)
3555 {
3556 SCROLLINFO si;
3557
3558 if (!cfg.scrollbar)
3559 return;
3560
3561 si.cbSize = sizeof(si);
3562 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3563 si.nMin = 0;
3564 si.nMax = total - 1;
3565 si.nPage = page;
3566 si.nPos = start;
3567 if (hwnd)
3568 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3569 }
3570
3571 Context get_ctx(void)
3572 {
3573 HDC hdc;
3574 if (hwnd) {
3575 hdc = GetDC(hwnd);
3576 if (hdc && pal)
3577 SelectPalette(hdc, pal, FALSE);
3578 return hdc;
3579 } else
3580 return NULL;
3581 }
3582
3583 void free_ctx(Context ctx)
3584 {
3585 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3586 ReleaseDC(hwnd, ctx);
3587 }
3588
3589 static void real_palette_set(int n, int r, int g, int b)
3590 {
3591 if (pal) {
3592 logpal->palPalEntry[n].peRed = r;
3593 logpal->palPalEntry[n].peGreen = g;
3594 logpal->palPalEntry[n].peBlue = b;
3595 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3596 colours[n] = PALETTERGB(r, g, b);
3597 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3598 } else
3599 colours[n] = RGB(r, g, b);
3600 }
3601
3602 void palette_set(int n, int r, int g, int b)
3603 {
3604 static const int first[21] = {
3605 0, 2, 4, 6, 8, 10, 12, 14,
3606 1, 3, 5, 7, 9, 11, 13, 15,
3607 16, 17, 18, 20, 22
3608 };
3609 real_palette_set(first[n], r, g, b);
3610 if (first[n] >= 18)
3611 real_palette_set(first[n] + 1, r, g, b);
3612 if (pal) {
3613 HDC hdc = get_ctx();
3614 UnrealizeObject(pal);
3615 RealizePalette(hdc);
3616 free_ctx(hdc);
3617 }
3618 }
3619
3620 void palette_reset(void)
3621 {
3622 int i;
3623
3624 for (i = 0; i < NCOLOURS; i++) {
3625 if (pal) {
3626 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3627 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3628 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3629 logpal->palPalEntry[i].peFlags = 0;
3630 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3631 defpal[i].rgbtGreen,
3632 defpal[i].rgbtBlue);
3633 } else
3634 colours[i] = RGB(defpal[i].rgbtRed,
3635 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3636 }
3637
3638 if (pal) {
3639 HDC hdc;
3640 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3641 hdc = get_ctx();
3642 RealizePalette(hdc);
3643 free_ctx(hdc);
3644 }
3645 }
3646
3647 void write_aclip(char *data, int len, int must_deselect)
3648 {
3649 HGLOBAL clipdata;
3650 void *lock;
3651
3652 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3653 if (!clipdata)
3654 return;
3655 lock = GlobalLock(clipdata);
3656 if (!lock)
3657 return;
3658 memcpy(lock, data, len);
3659 ((unsigned char *) lock)[len] = 0;
3660 GlobalUnlock(clipdata);
3661
3662 if (!must_deselect)
3663 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3664
3665 if (OpenClipboard(hwnd)) {
3666 EmptyClipboard();
3667 SetClipboardData(CF_TEXT, clipdata);
3668 CloseClipboard();
3669 } else
3670 GlobalFree(clipdata);
3671
3672 if (!must_deselect)
3673 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3674 }
3675
3676 /*
3677 * Note: unlike write_aclip() this will not append a nul.
3678 */
3679 void write_clip(wchar_t * data, int len, int must_deselect)
3680 {
3681 HGLOBAL clipdata, clipdata2, clipdata3;
3682 int len2;
3683 void *lock, *lock2, *lock3;
3684
3685 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3686
3687 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3688 len * sizeof(wchar_t));
3689 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3690
3691 if (!clipdata || !clipdata2 || !clipdata3) {
3692 if (clipdata)
3693 GlobalFree(clipdata);
3694 if (clipdata2)
3695 GlobalFree(clipdata2);
3696 if (clipdata3)
3697 GlobalFree(clipdata3);
3698 return;
3699 }
3700 if (!(lock = GlobalLock(clipdata)))
3701 return;
3702 if (!(lock2 = GlobalLock(clipdata2)))
3703 return;
3704
3705 memcpy(lock, data, len * sizeof(wchar_t));
3706 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3707
3708 if (cfg.rtf_paste) {
3709 wchar_t unitab[256];
3710 char *rtf = NULL;
3711 unsigned char *tdata = (unsigned char *)lock2;
3712 wchar_t *udata = (wchar_t *)lock;
3713 int rtflen = 0, uindex = 0, tindex = 0;
3714 int rtfsize = 0;
3715 int multilen, blen, alen, totallen, i;
3716 char before[16], after[4];
3717
3718 get_unitab(CP_ACP, unitab, 0);
3719
3720 rtfsize = 100 + strlen(cfg.font);
3721 rtf = smalloc(rtfsize);
3722 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3723 GetACP(), cfg.font);
3724 rtflen = strlen(rtf);
3725
3726 /*
3727 * We want to construct a piece of RTF that specifies the
3728 * same Unicode text. To do this we will read back in
3729 * parallel from the Unicode data in `udata' and the
3730 * non-Unicode data in `tdata'. For each character in
3731 * `tdata' which becomes the right thing in `udata' when
3732 * looked up in `unitab', we just copy straight over from
3733 * tdata. For each one that doesn't, we must WCToMB it
3734 * individually and produce a \u escape sequence.
3735 *
3736 * It would probably be more robust to just bite the bullet
3737 * and WCToMB each individual Unicode character one by one,
3738 * then MBToWC each one back to see if it was an accurate
3739 * translation; but that strikes me as a horrifying number
3740 * of Windows API calls so I want to see if this faster way
3741 * will work. If it screws up badly we can always revert to
3742 * the simple and slow way.
3743 */
3744 while (tindex < len2 && uindex < len &&
3745 tdata[tindex] && udata[uindex]) {
3746 if (tindex + 1 < len2 &&
3747 tdata[tindex] == '\r' &&
3748 tdata[tindex+1] == '\n') {
3749 tindex++;
3750 uindex++;
3751 }
3752 if (unitab[tdata[tindex]] == udata[uindex]) {
3753 multilen = 1;
3754 before[0] = '\0';
3755 after[0] = '\0';
3756 blen = alen = 0;
3757 } else {
3758 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
3759 NULL, 0, NULL, NULL);
3760 if (multilen != 1) {
3761 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
3762 udata[uindex]);
3763 alen = 1; strcpy(after, "}");
3764 } else {
3765 blen = sprintf(before, "\\u%d", udata[uindex]);
3766 alen = 0; after[0] = '\0';
3767 }
3768 }
3769 assert(tindex + multilen <= len2);
3770 totallen = blen + alen;
3771 for (i = 0; i < multilen; i++) {
3772 if (tdata[tindex+i] == '\\' ||
3773 tdata[tindex+i] == '{' ||
3774 tdata[tindex+i] == '}')
3775 totallen += 2;
3776 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
3777 totallen += 6; /* \par\r\n */
3778 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
3779 totallen += 4;
3780 else
3781 totallen++;
3782 }
3783
3784 if (rtfsize < rtflen + totallen + 3) {
3785 rtfsize = rtflen + totallen + 512;
3786 rtf = srealloc(rtf, rtfsize);
3787 }
3788
3789 strcpy(rtf + rtflen, before); rtflen += blen;
3790 for (i = 0; i < multilen; i++) {
3791 if (tdata[tindex+i] == '\\' ||
3792 tdata[tindex+i] == '{' ||
3793 tdata[tindex+i] == '}') {
3794 rtf[rtflen++] = '\\';
3795 rtf[rtflen++] = tdata[tindex+i];
3796 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
3797 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
3798 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
3799 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
3800 } else {
3801 rtf[rtflen++] = tdata[tindex+i];
3802 }
3803 }
3804 strcpy(rtf + rtflen, after); rtflen += alen;
3805
3806 tindex += multilen;
3807 uindex++;
3808 }
3809
3810 strcpy(rtf + rtflen, "}");
3811 rtflen += 2;
3812
3813 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
3814 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
3815 strcpy(lock3, rtf);
3816 GlobalUnlock(clipdata3);
3817 }
3818 sfree(rtf);
3819 } else
3820 clipdata3 = NULL;
3821
3822 GlobalUnlock(clipdata);
3823 GlobalUnlock(clipdata2);
3824
3825 if (!must_deselect)
3826 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3827
3828 if (OpenClipboard(hwnd)) {
3829 EmptyClipboard();
3830 SetClipboardData(CF_UNICODETEXT, clipdata);
3831 SetClipboardData(CF_TEXT, clipdata2);
3832 if (clipdata3)
3833 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
3834 CloseClipboard();
3835 } else {
3836 GlobalFree(clipdata);
3837 GlobalFree(clipdata2);
3838 }
3839
3840 if (!must_deselect)
3841 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3842 }
3843
3844 void get_clip(wchar_t ** p, int *len)
3845 {
3846 static HGLOBAL clipdata = NULL;
3847 static wchar_t *converted = 0;
3848 wchar_t *p2;
3849
3850 if (converted) {
3851 sfree(converted);
3852 converted = 0;
3853 }
3854 if (!p) {
3855 if (clipdata)
3856 GlobalUnlock(clipdata);
3857 clipdata = NULL;
3858 return;
3859 } else if (OpenClipboard(NULL)) {
3860 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3861 CloseClipboard();
3862 *p = GlobalLock(clipdata);
3863 if (*p) {
3864 for (p2 = *p; *p2; p2++);
3865 *len = p2 - *p;
3866 return;
3867 }
3868 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3869 char *s;
3870 int i;
3871 CloseClipboard();
3872 s = GlobalLock(clipdata);
3873 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3874 *p = converted = smalloc(i * sizeof(wchar_t));
3875 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3876 *len = i - 1;
3877 return;
3878 } else
3879 CloseClipboard();
3880 }
3881
3882 *p = NULL;
3883 *len = 0;
3884 }
3885
3886 #if 0
3887 /*
3888 * Move `lines' lines from position `from' to position `to' in the
3889 * window.
3890 */
3891 void optimised_move(int to, int from, int lines)
3892 {
3893 RECT r;
3894 int min, max;
3895
3896 min = (to < from ? to : from);
3897 max = to + from - min;
3898
3899 r.left = offset_width;
3900 r.right = offset_width + cols * font_width;
3901 r.top = offset_height + min * font_height;
3902 r.bottom = offset_height + (max + lines) * font_height;
3903 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3904 }
3905 #endif
3906
3907 /*
3908 * Print a message box and perform a fatal exit.
3909 */
3910 void fatalbox(char *fmt, ...)
3911 {
3912 va_list ap;
3913 char stuff[200];
3914
3915 va_start(ap, fmt);
3916 vsprintf(stuff, fmt, ap);
3917 va_end(ap);
3918 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3919 exit(1);
3920 }
3921
3922 /*
3923 * Manage window caption / taskbar flashing, if enabled.
3924 * 0 = stop, 1 = maintain, 2 = start
3925 */
3926 static void flash_window(int mode)
3927 {
3928 static long last_flash = 0;
3929 static int flashing = 0;
3930 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3931 /* stop */
3932 if (flashing) {
3933 FlashWindow(hwnd, FALSE);
3934 flashing = 0;
3935 }
3936
3937 } else if (mode == 2) {
3938 /* start */
3939 if (!flashing) {
3940 last_flash = GetTickCount();
3941 flashing = 1;
3942 FlashWindow(hwnd, TRUE);
3943 }
3944
3945 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3946 /* maintain */
3947 if (flashing) {
3948 long now = GetTickCount();
3949 long fdiff = now - last_flash;
3950 if (fdiff < 0 || fdiff > 450) {
3951 last_flash = now;
3952 FlashWindow(hwnd, TRUE); /* toggle */
3953 }
3954 }
3955 }
3956 }
3957
3958 /*
3959 * Beep.
3960 */
3961 void beep(int mode)
3962 {
3963 if (mode == BELL_DEFAULT) {
3964 /*
3965 * For MessageBeep style bells, we want to be careful of
3966 * timing, because they don't have the nice property of
3967 * PlaySound bells that each one cancels the previous
3968 * active one. So we limit the rate to one per 50ms or so.
3969 */
3970 static long lastbeep = 0;
3971 long beepdiff;
3972
3973 beepdiff = GetTickCount() - lastbeep;
3974 if (beepdiff >= 0 && beepdiff < 50)
3975 return;
3976 MessageBeep(MB_OK);
3977 /*
3978 * The above MessageBeep call takes time, so we record the
3979 * time _after_ it finishes rather than before it starts.
3980 */
3981 lastbeep = GetTickCount();
3982 } else if (mode == BELL_WAVEFILE) {
3983 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3984 char buf[sizeof(cfg.bell_wavefile) + 80];
3985 sprintf(buf, "Unable to play sound file\n%s\n"
3986 "Using default sound instead", cfg.bell_wavefile);
3987 MessageBox(hwnd, buf, "PuTTY Sound Error",
3988 MB_OK | MB_ICONEXCLAMATION);
3989 cfg.beep = BELL_DEFAULT;
3990 }
3991 }
3992 /* Otherwise, either visual bell or disabled; do nothing here */
3993 if (!has_focus) {
3994 flash_window(2); /* start */
3995 }
3996 }
3997
3998 /*
3999 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
4000 * implementation.
4001 * Revised by <wez@thebrainroom.com>
4002 */
4003 static void flip_full_screen(void)
4004 {
4005 WINDOWPLACEMENT wp;
4006 LONG style;
4007
4008 wp.length = sizeof(wp);
4009 GetWindowPlacement(hwnd, &wp);
4010
4011 full_screen = !full_screen;
4012
4013 if (full_screen) {
4014 if (wp.showCmd == SW_SHOWMAXIMIZED) {
4015 /* Ooops it was already 'zoomed' we have to unzoom it before
4016 * everything will work right.
4017 */
4018 wp.showCmd = SW_SHOWNORMAL;
4019 SetWindowPlacement(hwnd, &wp);
4020 }
4021
4022 style = GetWindowLong(hwnd, GWL_STYLE) & ~WS_CAPTION;
4023 style &= ~WS_VSCROLL;
4024 if (cfg.scrollbar_in_fullscreen)
4025 style |= WS_VSCROLL;
4026 SetWindowLong(hwnd, GWL_STYLE, style);
4027
4028 /* This seems to be needed otherwize explorer doesn't notice
4029 * we want to go fullscreen and it's bar is still visible
4030 */
4031 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4032 SWP_NOACTIVATE | SWP_NOCOPYBITS |
4033 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4034 SWP_FRAMECHANGED);
4035
4036 wp.showCmd = SW_SHOWMAXIMIZED;
4037 SetWindowPlacement(hwnd, &wp);
4038 } else {
4039 style = GetWindowLong(hwnd, GWL_STYLE) | WS_CAPTION;
4040 style &= ~WS_VSCROLL;
4041 if (cfg.scrollbar)
4042 style |= WS_VSCROLL;
4043 SetWindowLong(hwnd, GWL_STYLE, style);
4044
4045 /* Don't need to do a SetWindowPos as the resize will force a
4046 * full redraw.
4047 */
4048 wp.showCmd = SW_SHOWNORMAL;
4049 SetWindowPlacement(hwnd, &wp);
4050 }
4051
4052 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4053 MF_BYCOMMAND| full_screen ? MF_CHECKED : MF_UNCHECKED);
4054 }