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