Glenn Maynard's IMM cursor position fix
[sgt/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;
0d6dcf38 1690 case SC_MOUSEMENU:
1691 /*
1692 * We get this if the System menu has been activated
1693 * using the mouse.
1694 */
1695 show_mouseptr(1);
1696 break;
dfca2656 1697 case SC_KEYMENU:
1698 /*
0d6dcf38 1699 * We get this if the System menu has been activated
1700 * using the keyboard. This might happen from within
1701 * TranslateKey, in which case it really wants to be
1702 * followed by a `space' character to actually _bring
1703 * the menu up_ rather than just sitting there in
1704 * `ready to appear' state.
dfca2656 1705 */
0d6dcf38 1706 show_mouseptr(1); /* make sure pointer is visible */
dfca2656 1707 if( lParam == 0 )
1708 PostMessage(hwnd, WM_CHAR, ' ', 0);
1709 break;
a401e5f3 1710 case IDM_FULLSCREEN:
1711 flip_full_screen();
1712 break;
32874aea 1713 default:
1714 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1715 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1716 }
374330e2 1717 }
1718 break;
37508af4 1719
1720#define X_POS(l) ((int)(short)LOWORD(l))
1721#define Y_POS(l) ((int)(short)HIWORD(l))
1722
5a73255e 1723#define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1724#define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
01c034ad 1725#define WHEEL_DELTA 120
1726 case WM_MOUSEWHEEL:
1727 {
32874aea 1728 wheel_accumulator += (short) HIWORD(wParam);
01c034ad 1729 wParam = LOWORD(wParam);
1730
1731 /* process events when the threshold is reached */
1732 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1733 int b;
fdedf2c8 1734
01c034ad 1735 /* reduce amount for next time */
1736 if (wheel_accumulator > 0) {
1737 b = MBT_WHEEL_UP;
1738 wheel_accumulator -= WHEEL_DELTA;
32874aea 1739 } else if (wheel_accumulator < 0) {
01c034ad 1740 b = MBT_WHEEL_DOWN;
1741 wheel_accumulator += WHEEL_DELTA;
32874aea 1742 } else
01c034ad 1743 break;
1744
1745 if (send_raw_mouse) {
1746 /* send a mouse-down followed by a mouse up */
6908fed7 1747
01c034ad 1748 term_mouse(b,
1749 MA_CLICK,
32874aea 1750 TO_CHR_X(X_POS(lParam)),
1751 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
6908fed7 1752 wParam & MK_CONTROL, is_alt_pressed());
32874aea 1753 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1754 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
6908fed7 1755 wParam & MK_CONTROL, is_alt_pressed());
01c034ad 1756 } else {
1757 /* trigger a scroll */
32874aea 1758 term_scroll(0,
1759 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
01c034ad 1760 }
1761 }
1762 return 0;
1763 }
374330e2 1764 case WM_LBUTTONDOWN:
374330e2 1765 case WM_MBUTTONDOWN:
374330e2 1766 case WM_RBUTTONDOWN:
01c034ad 1767 case WM_LBUTTONUP:
1768 case WM_MBUTTONUP:
374330e2 1769 case WM_RBUTTONUP:
01c034ad 1770 {
1771 int button, press;
6908fed7 1772
01c034ad 1773 switch (message) {
32874aea 1774 case WM_LBUTTONDOWN:
1775 button = MBT_LEFT;
1776 press = 1;
1777 break;
1778 case WM_MBUTTONDOWN:
1779 button = MBT_MIDDLE;
1780 press = 1;
1781 break;
1782 case WM_RBUTTONDOWN:
1783 button = MBT_RIGHT;
1784 press = 1;
1785 break;
1786 case WM_LBUTTONUP:
1787 button = MBT_LEFT;
1788 press = 0;
1789 break;
1790 case WM_MBUTTONUP:
1791 button = MBT_MIDDLE;
1792 press = 0;
1793 break;
1794 case WM_RBUTTONUP:
1795 button = MBT_RIGHT;
1796 press = 0;
1797 break;
2d466ffd 1798 default:
1799 button = press = 0; /* shouldn't happen */
01c034ad 1800 }
1801 show_mouseptr(1);
1802 if (press) {
32874aea 1803 click(button,
1804 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
6908fed7 1805 wParam & MK_SHIFT, wParam & MK_CONTROL,
1806 is_alt_pressed());
01c034ad 1807 SetCapture(hwnd);
1808 } else {
32874aea 1809 term_mouse(button, MA_RELEASE,
1810 TO_CHR_X(X_POS(lParam)),
1811 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
6908fed7 1812 wParam & MK_CONTROL, is_alt_pressed());
01c034ad 1813 ReleaseCapture();
1814 }
1815 }
374330e2 1816 return 0;
1817 case WM_MOUSEMOVE:
32874aea 1818 show_mouseptr(1);
374330e2 1819 /*
1820 * Add the mouse position and message time to the random
7d6ee6ff 1821 * number noise.
374330e2 1822 */
32874aea 1823 noise_ultralight(lParam);
374330e2 1824
1825 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1826 Mouse_Button b;
1827 if (wParam & MK_LBUTTON)
6c6e7711 1828 b = MBT_LEFT;
374330e2 1829 else if (wParam & MK_MBUTTON)
6c6e7711 1830 b = MBT_MIDDLE;
374330e2 1831 else
6c6e7711 1832 b = MBT_RIGHT;
32874aea 1833 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1834 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
6908fed7 1835 wParam & MK_CONTROL, is_alt_pressed());
374330e2 1836 }
374330e2 1837 return 0;
d318ef8e 1838 case WM_NCMOUSEMOVE:
1839 show_mouseptr(1);
32874aea 1840 noise_ultralight(lParam);
d318ef8e 1841 return 0;
374330e2 1842 case WM_IGNORE_CLIP:
1843 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1844 break;
1845 case WM_DESTROYCLIPBOARD:
1846 if (!ignore_clip)
1847 term_deselect();
1848 ignore_clip = FALSE;
1849 return 0;
1850 case WM_PAINT:
1851 {
1852 PAINTSTRUCT p;
32874aea 1853 HideCaret(hwnd);
1854 hdc = BeginPaint(hwnd, &p);
374330e2 1855 if (pal) {
32874aea 1856 SelectPalette(hdc, pal, TRUE);
1857 RealizePalette(hdc);
374330e2 1858 }
5a73255e 1859 term_paint(hdc,
1860 (p.rcPaint.left-offset_width)/font_width,
1861 (p.rcPaint.top-offset_height)/font_height,
1862 (p.rcPaint.right-offset_width-1)/font_width,
1863 (p.rcPaint.bottom-offset_height-1)/font_height);
1864
1865 if (p.fErase ||
1866 p.rcPaint.left < offset_width ||
1867 p.rcPaint.top < offset_height ||
1868 p.rcPaint.right >= offset_width + font_width*cols ||
1869 p.rcPaint.bottom>= offset_height + font_height*rows)
1870 {
1871 HBRUSH fillcolour, oldbrush;
1872 HPEN edge, oldpen;
1873 fillcolour = CreateSolidBrush (
1874 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1875 oldbrush = SelectObject(hdc, fillcolour);
1876 edge = CreatePen(PS_SOLID, 0,
1877 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1878 oldpen = SelectObject(hdc, edge);
1879
1880 ExcludeClipRect(hdc,
1881 offset_width, offset_height,
1882 offset_width+font_width*cols,
1883 offset_height+font_height*rows);
1884
1885 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
1886 p.rcPaint.right, p.rcPaint.bottom);
1887
1888 // SelectClipRgn(hdc, NULL);
1889
1890 SelectObject(hdc, oldbrush);
1891 DeleteObject(fillcolour);
1892 SelectObject(hdc, oldpen);
1893 DeleteObject(edge);
1894 }
32874aea 1895 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1896 SelectObject(hdc, GetStockObject(WHITE_PEN));
1897 EndPaint(hwnd, &p);
1898 ShowCaret(hwnd);
374330e2 1899 }
1900 return 0;
1901 case WM_NETEVENT:
59ad2c03 1902 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1903 * but the only one that's likely to try to overload us is FD_READ.
1904 * This means buffering just one is fine.
1905 */
1906 if (pending_netevent)
1907 enact_pending_netevent();
1908
1909 pending_netevent = TRUE;
32874aea 1910 pend_netevent_wParam = wParam;
1911 pend_netevent_lParam = lParam;
3ad9d396 1912 if (WSAGETSELECTEVENT(lParam) != FD_READ)
1913 enact_pending_netevent();
1914
ec55b220 1915 time(&last_movement);
374330e2 1916 return 0;
1917 case WM_SETFOCUS:
1918 has_focus = TRUE;
32874aea 1919 CreateCaret(hwnd, caretbm, font_width, font_height);
1920 ShowCaret(hwnd);
f8a28d1f 1921 flash_window(0); /* stop */
32874aea 1922 compose_state = 0;
374330e2 1923 term_out();
1924 term_update();
1925 break;
1926 case WM_KILLFOCUS:
32874aea 1927 show_mouseptr(1);
374330e2 1928 has_focus = FALSE;
32874aea 1929 DestroyCaret();
374330e2 1930 term_out();
1931 term_update();
1932 break;
73251d5d 1933 case WM_ENTERSIZEMOVE:
5a73255e 1934#ifdef RDB_DEBUG_PATCH
1935 debug((27, "WM_ENTERSIZEMOVE"));
1936#endif
32874aea 1937 EnableSizeTip(1);
1938 resizing = TRUE;
3ad8c6db 1939 need_backend_resize = FALSE;
32874aea 1940 break;
73251d5d 1941 case WM_EXITSIZEMOVE:
32874aea 1942 EnableSizeTip(0);
1943 resizing = FALSE;
5a73255e 1944#ifdef RDB_DEBUG_PATCH
1945 debug((27, "WM_EXITSIZEMOVE"));
1946#endif
1947 if (need_backend_resize) {
1948 term_size(cfg.height, cfg.width, cfg.savelines);
1949 InvalidateRect(hwnd, NULL, TRUE);
1950 }
32874aea 1951 break;
374330e2 1952 case WM_SIZING:
5a73255e 1953 /*
1954 * This does two jobs:
1955 * 1) Keep the sizetip uptodate
1956 * 2) Make sure the window size is _stepped_ in units of the font size.
1957 */
ad5c93cc 1958 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
374330e2 1959 int width, height, w, h, ew, eh;
32874aea 1960 LPRECT r = (LPRECT) lParam;
374330e2 1961
5a73255e 1962 if ( !need_backend_resize &&
1963 (cfg.height != rows || cfg.width != cols )) {
1964 /*
1965 * Great! It seems the host has been changing the terminal
1966 * size, well the user is now grabbing so this is probably
1967 * the least confusing solution in the long run even though
1968 * it a is suprise. Unfortunatly the only way to prevent
1969 * this seems to be to let the host change the window size
1970 * and as that's a user option we're still right back here.
1971 */
1972 term_size(cfg.height, cfg.width, cfg.savelines);
1973 reset_window(2);
1974 InvalidateRect(hwnd, NULL, TRUE);
1975 need_backend_resize = TRUE;
1976 }
1977
374330e2 1978 width = r->right - r->left - extra_width;
1979 height = r->bottom - r->top - extra_height;
32874aea 1980 w = (width + font_width / 2) / font_width;
1981 if (w < 1)
1982 w = 1;
1983 h = (height + font_height / 2) / font_height;
1984 if (h < 1)
1985 h = 1;
1986 UpdateSizeTip(hwnd, w, h);
374330e2 1987 ew = width - w * font_width;
1988 eh = height - h * font_height;
1989 if (ew != 0) {
1990 if (wParam == WMSZ_LEFT ||
32874aea 1991 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
374330e2 1992 r->left += ew;
1993 else
1994 r->right -= ew;
1995 }
1996 if (eh != 0) {
1997 if (wParam == WMSZ_TOP ||
32874aea 1998 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
374330e2 1999 r->top += eh;
2000 else
2001 r->bottom -= eh;
2002 }
2003 if (ew || eh)
2004 return 1;
2005 else
2006 return 0;
5a73255e 2007 } else {
2008 int width, height, w, h, rv = 0;
2009 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2010 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2011 LPRECT r = (LPRECT) lParam;
2012
2013 width = r->right - r->left - ex_width;
2014 height = r->bottom - r->top - ex_height;
2015
2016 w = (width + cols/2)/cols;
2017 h = (height + rows/2)/rows;
2018 if ( r->right != r->left + w*cols + ex_width)
2019 rv = 1;
2020
2021 if (wParam == WMSZ_LEFT ||
2022 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2023 r->left = r->right - w*cols - ex_width;
2024 else
2025 r->right = r->left + w*cols + ex_width;
2026
2027 if (r->bottom != r->top + h*rows + ex_height)
2028 rv = 1;
2029
2030 if (wParam == WMSZ_TOP ||
2031 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2032 r->top = r->bottom - h*rows - ex_height;
2033 else
2034 r->bottom = r->top + h*rows + ex_height;
2035
2036 return rv;
374330e2 2037 }
32874aea 2038 /* break; (never reached) */
374330e2 2039 case WM_SIZE:
5a73255e 2040#ifdef RDB_DEBUG_PATCH
2041 debug((27, "WM_SIZE %s (%d,%d)",
2042 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2043 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2044 (wParam == SIZE_RESTORED && resizing) ? "to":
2045 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2046 "...",
2047 LOWORD(lParam), HIWORD(lParam)));
2048#endif
374330e2 2049 if (wParam == SIZE_MINIMIZED) {
32874aea 2050 SetWindowText(hwnd,
2051 cfg.win_name_always ? window_name : icon_name);
374330e2 2052 break;
2053 }
2054 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
32874aea 2055 SetWindowText(hwnd, window_name);
5a73255e 2056
ad5c93cc 2057 if (cfg.resize_action == RESIZE_DISABLED) {
5a73255e 2058 /* A resize, well it better be a minimize. */
2059 reset_window(-1);
2060 } else {
2061
1a6f78fe 2062 int width, height, w, h;
374330e2 2063
2064 width = LOWORD(lParam);
2065 height = HIWORD(lParam);
5a73255e 2066
2067 if (!resizing) {
2068 if (wParam == SIZE_MAXIMIZED) {
2069 was_zoomed = 1;
2070 prev_rows = rows;
2071 prev_cols = cols;
ad5c93cc 2072 if (cfg.resize_action != RESIZE_FONT) {
5a73255e 2073 w = width / font_width;
2074 if (w < 1) w = 1;
2075 h = height / font_height;
2076 if (h < 1) h = 1;
2077
2078 term_size(h, w, cfg.savelines);
2079 }
2080 reset_window(0);
2081 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2082 was_zoomed = 0;
ad5c93cc 2083 if (cfg.resize_action != RESIZE_FONT)
5a73255e 2084 term_size(prev_rows, prev_cols, cfg.savelines);
2085 reset_window(0);
2086 }
2087 /* This is an unexpected resize, these will normally happen
2088 * if the window is too large. Probably either the user
2089 * selected a huge font or the screen size has changed.
2090 *
2091 * This is also called with minimize.
32874aea 2092 */
5a73255e 2093 else reset_window(-1);
2094 }
2095
2096 /*
2097 * Don't call back->size in mid-resize. (To prevent
2098 * massive numbers of resize events getting sent
2099 * down the connection during an NT opaque drag.)
2100 */
2101 if (resizing) {
ad5c93cc 2102 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
3ad8c6db 2103 need_backend_resize = TRUE;
5a73255e 2104 w = (width-cfg.window_border*2) / font_width;
2105 if (w < 1) w = 1;
2106 h = (height-cfg.window_border*2) / font_height;
2107 if (h < 1) h = 1;
2108
e44d78b6 2109 cfg.height = h;
2110 cfg.width = w;
5a73255e 2111 } else
2112 reset_window(0);
374330e2 2113 }
2114 }
374330e2 2115 return 0;
2116 case WM_VSCROLL:
2117 switch (LOWORD(wParam)) {
32874aea 2118 case SB_BOTTOM:
2119 term_scroll(-1, 0);
2120 break;
2121 case SB_TOP:
2122 term_scroll(+1, 0);
2123 break;
2124 case SB_LINEDOWN:
2125 term_scroll(0, +1);
2126 break;
2127 case SB_LINEUP:
2128 term_scroll(0, -1);
2129 break;
2130 case SB_PAGEDOWN:
2131 term_scroll(0, +rows / 2);
2132 break;
2133 case SB_PAGEUP:
2134 term_scroll(0, -rows / 2);
2135 break;
2136 case SB_THUMBPOSITION:
2137 case SB_THUMBTRACK:
2138 term_scroll(1, HIWORD(wParam));
2139 break;
2140 }
2141 break;
2142 case WM_PALETTECHANGED:
374330e2 2143 if ((HWND) wParam != hwnd && pal != NULL) {
2144 HDC hdc = get_ctx();
2145 if (hdc) {
32874aea 2146 if (RealizePalette(hdc) > 0)
2147 UpdateColors(hdc);
2148 free_ctx(hdc);
374330e2 2149 }
2150 }
2151 break;
2152 case WM_QUERYNEWPALETTE:
2153 if (pal != NULL) {
2154 HDC hdc = get_ctx();
2155 if (hdc) {
32874aea 2156 if (RealizePalette(hdc) > 0)
2157 UpdateColors(hdc);
2158 free_ctx(hdc);
374330e2 2159 return TRUE;
2160 }
2161 }
2162 return FALSE;
2163 case WM_KEYDOWN:
2164 case WM_SYSKEYDOWN:
c9def1b8 2165 case WM_KEYUP:
2166 case WM_SYSKEYUP:
374330e2 2167 /*
2168 * Add the scan code and keypress timing to the random
7d6ee6ff 2169 * number noise.
374330e2 2170 */
32874aea 2171 noise_ultralight(lParam);
374330e2 2172
2173 /*
2174 * We don't do TranslateMessage since it disassociates the
2175 * resulting CHAR message from the KEYDOWN that sparked it,
2176 * which we occasionally don't want. Instead, we process
2177 * KEYDOWN, and call the Win32 translator functions so that
2178 * we get the translations under _our_ control.
2179 */
2180 {
2181 unsigned char buf[20];
2182 int len;
2183
32874aea 2184 if (wParam == VK_PROCESSKEY) {
3cf144db 2185 MSG m;
32874aea 2186 m.hwnd = hwnd;
2187 m.message = WM_KEYDOWN;
2188 m.wParam = wParam;
2189 m.lParam = lParam & 0xdfff;
2190 TranslateMessage(&m);
2191 } else {
2192 len = TranslateKey(message, wParam, lParam, buf);
3cf144db 2193 if (len == -1)
32874aea 2194 return DefWindowProc(hwnd, message, wParam, lParam);
5471d09a 2195
b2a1eade 2196 if (len != 0) {
bca9517a 2197 /*
256cb87c 2198 * Interrupt an ongoing paste. I'm not sure
2199 * this is sensible, but for the moment it's
2200 * preferable to having to faff about buffering
2201 * things.
2202 */
2203 term_nopaste();
2204
2205 /*
bca9517a 2206 * We need not bother about stdin backlogs
2207 * here, because in GUI PuTTY we can't do
2208 * anything about it anyway; there's no means
2209 * of asking Windows to hold off on KEYDOWN
2210 * messages. We _have_ to buffer everything
2211 * we're sent.
2212 */
760e88b2 2213 ldisc_send(buf, len, 1);
32874aea 2214 show_mouseptr(0);
bca9517a 2215 }
3cf144db 2216 }
374330e2 2217 }
7732d38a 2218 net_pending_errors();
374330e2 2219 return 0;
4eeb7d09 2220 case WM_INPUTLANGCHANGE:
3cf144db 2221 {
4eeb7d09 2222 /* wParam == Font number */
2223 /* lParam == Locale */
2224 char lbuf[20];
2225 HKL NewInputLocale = (HKL) lParam;
2226
2227 // lParam == GetKeyboardLayout(0);
2228
2229 GetLocaleInfo(LOWORD(NewInputLocale),
2230 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
2231
2232 kbd_codepage = atoi(lbuf);
2233 }
2234 break;
88485e4d 2235 case WM_IME_COMPOSITION:
2236 {
2237 HIMC hIMC;
2238 int n;
2239 char *buff;
2240
2241 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2242 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2243
2244 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2245 break; /* fall back to DefWindowProc */
2246
2247 hIMC = ImmGetContext(hwnd);
2248 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2249
2250 if (n > 0) {
2251 buff = (char*) smalloc(n);
2252 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
760e88b2 2253 luni_send((unsigned short *)buff, n / 2, 1);
88485e4d 2254 free(buff);
2255 }
2256 ImmReleaseContext(hwnd, hIMC);
2257 return 1;
2258 }
2259
4eeb7d09 2260 case WM_IME_CHAR:
2261 if (wParam & 0xFF00) {
3cf144db 2262 unsigned char buf[2];
2263
2264 buf[1] = wParam;
2265 buf[0] = wParam >> 8;
760e88b2 2266 lpage_send(kbd_codepage, buf, 2, 1);
4eeb7d09 2267 } else {
2268 char c = (unsigned char) wParam;
760e88b2 2269 lpage_send(kbd_codepage, &c, 1, 1);
3cf144db 2270 }
4eeb7d09 2271 return (0);
374330e2 2272 case WM_CHAR:
2273 case WM_SYSCHAR:
2274 /*
2275 * Nevertheless, we are prepared to deal with WM_CHAR
2276 * messages, should they crop up. So if someone wants to
2277 * post the things to us as part of a macro manoeuvre,
2278 * we're ready to cope.
2279 */
32874aea 2280 {
4eeb7d09 2281 char c = (unsigned char)wParam;
760e88b2 2282 lpage_send(CP_ACP, &c, 1, 1);
374330e2 2283 }
2284 return 0;
32874aea 2285 case WM_SETCURSOR:
ca3ab3ee 2286 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
32874aea 2287 SetCursor(LoadCursor(NULL, IDC_ARROW));
2288 return TRUE;
2289 }
374330e2 2290 }
2291
32874aea 2292 return DefWindowProc(hwnd, message, wParam, lParam);
374330e2 2293}
2294
2295/*
ec8679e9 2296 * Move the system caret. (We maintain one, even though it's
2297 * invisible, for the benefit of blind people: apparently some
2298 * helper software tracks the system caret, so we should arrange to
2299 * have one.)
2300 */
32874aea 2301void sys_cursor(int x, int y)
2302{
88485e4d 2303 COMPOSITIONFORM cf;
2304 HIMC hIMC;
2305
2306 if (!has_focus) return;
2307
5a73255e 2308 SetCaretPos(x * font_width + offset_width,
2309 y * font_height + offset_height);
88485e4d 2310
2311 /* IMM calls on Win98 and beyond only */
2312 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2313
2314 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2315 osVersion.dwMinorVersion == 0) return; /* 95 */
2316
2317 /* we should have the IMM functions */
2318 hIMC = ImmGetContext(hwnd);
2319 cf.dwStyle = CFS_POINT;
808d8218 2320 cf.ptCurrentPos.x = x * font_width + offset_width;
2321 cf.ptCurrentPos.y = y * font_height + offset_height;
88485e4d 2322 ImmSetCompositionWindow(hIMC, &cf);
2323
2324 ImmReleaseContext(hwnd, hIMC);
ec8679e9 2325}
2326
2327/*
374330e2 2328 * Draw a line of text in the window, at given character
2329 * coordinates, in given attributes.
2330 *
2331 * We are allowed to fiddle with the contents of `text'.
2332 */
32874aea 2333void do_text(Context ctx, int x, int y, char *text, int len,
2334 unsigned long attr, int lattr)
2335{
374330e2 2336 COLORREF fg, bg, t;
2337 int nfg, nbg, nfont;
2338 HDC hdc = ctx;
59ad2c03 2339 RECT line_box;
2340 int force_manual_underline = 0;
32874aea 2341 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
4eeb7d09 2342 int char_width = fnt_width;
2343 int text_adjust = 0;
2344 static int *IpDx = 0, IpDxLEN = 0;
2345
2346 if (attr & ATTR_WIDE)
2347 char_width *= 2;
59ad2c03 2348
4eeb7d09 2349 if (len > IpDxLEN || IpDx[0] != char_width) {
59ad2c03 2350 int i;
32874aea 2351 if (len > IpDxLEN) {
59ad2c03 2352 sfree(IpDx);
32874aea 2353 IpDx = smalloc((len + 16) * sizeof(int));
2354 IpDxLEN = (len + 16);
59ad2c03 2355 }
32874aea 2356 for (i = 0; i < IpDxLEN; i++)
4eeb7d09 2357 IpDx[i] = char_width;
59ad2c03 2358 }
374330e2 2359
5a73255e 2360 /* Only want the left half of double width lines */
2361 if (lattr != LATTR_NORM && x*2 >= cols)
2362 return;
2363
c9def1b8 2364 x *= fnt_width;
374330e2 2365 y *= font_height;
5a73255e 2366 x += offset_width;
2367 y += offset_height;
374330e2 2368
4eeb7d09 2369 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2370 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
374330e2 2371 attr ^= ATTR_CUR_XOR;
2372 }
2373
2374 nfont = 0;
4eeb7d09 2375 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2376 /* Assume a poorman font is borken in other ways too. */
2377 lattr = LATTR_WIDE;
2378 } else
2379 switch (lattr) {
2380 case LATTR_NORM:
2381 break;
2382 case LATTR_WIDE:
2383 nfont |= FONT_WIDE;
374330e2 2384 break;
4eeb7d09 2385 default:
2386 nfont |= FONT_WIDE + FONT_HIGH;
2387 break;
2388 }
5a73255e 2389 if (attr & ATTR_NARROW)
2390 nfont |= FONT_NARROW;
4eeb7d09 2391
2392 /* Special hack for the VT100 linedraw glyphs. */
2393 if ((attr & CSET_MASK) == 0x2300) {
5a73255e 2394 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
4eeb7d09 2395 switch ((unsigned char) (text[0])) {
2396 case 0xBA:
2397 text_adjust = -2 * font_height / 5;
2398 break;
2399 case 0xBB:
2400 text_adjust = -1 * font_height / 5;
2401 break;
2402 case 0xBC:
2403 text_adjust = font_height / 5;
2404 break;
2405 case 0xBD:
2406 text_adjust = 2 * font_height / 5;
32874aea 2407 break;
59ad2c03 2408 }
4eeb7d09 2409 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2410 text_adjust *= 2;
2411 attr &= ~CSET_MASK;
2412 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2413 attr |= (unitab_xterm['q'] & CSET_MASK);
2414 if (attr & ATTR_UNDER) {
2415 attr &= ~ATTR_UNDER;
2416 force_manual_underline = 1;
2417 }
374330e2 2418 }
2419 }
2420
4eeb7d09 2421 /* Anything left as an original character set is unprintable. */
2422 if (DIRECT_CHAR(attr)) {
2423 attr &= ~CSET_MASK;
2424 attr |= 0xFF00;
5a73255e 2425 memset(text, 0xFD, len);
4eeb7d09 2426 }
2427
2428 /* OEM CP */
2429 if ((attr & CSET_MASK) == ATTR_OEMCP)
2430 nfont |= FONT_OEM;
2431
374330e2 2432 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2433 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2434 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2435 nfont |= FONT_BOLD;
2436 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2437 nfont |= FONT_UNDERLINE;
4eeb7d09 2438 another_font(nfont);
32874aea 2439 if (!fonts[nfont]) {
2440 if (nfont & FONT_UNDERLINE)
59ad2c03 2441 force_manual_underline = 1;
2442 /* Don't do the same for manual bold, it could be bad news. */
2443
32874aea 2444 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
59ad2c03 2445 }
4eeb7d09 2446 another_font(nfont);
2447 if (!fonts[nfont])
2448 nfont = FONT_NORMAL;
374330e2 2449 if (attr & ATTR_REVERSE) {
32874aea 2450 t = nfg;
2451 nfg = nbg;
2452 nbg = t;
374330e2 2453 }
2454 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2455 nfg++;
59ad2c03 2456 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2457 nbg++;
374330e2 2458 fg = colours[nfg];
2459 bg = colours[nbg];
32874aea 2460 SelectObject(hdc, fonts[nfont]);
2461 SetTextColor(hdc, fg);
2462 SetBkColor(hdc, bg);
2463 SetBkMode(hdc, OPAQUE);
2464 line_box.left = x;
2465 line_box.top = y;
4eeb7d09 2466 line_box.right = x + char_width * len;
32874aea 2467 line_box.bottom = y + font_height;
4eeb7d09 2468
5a73255e 2469 /* Only want the left half of double width lines */
2470 if (line_box.right > font_width*cols+offset_width)
2471 line_box.right = font_width*cols+offset_width;
2472
4eeb7d09 2473 /* We're using a private area for direct to font. (512 chars.) */
2474 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2475 /* Ho Hum, dbcs fonts are a PITA! */
2476 /* To display on W9x I have to convert to UCS */
2477 static wchar_t *uni_buf = 0;
2478 static int uni_len = 0;
5a73255e 2479 int nlen, mptr;
4eeb7d09 2480 if (len > uni_len) {
2481 sfree(uni_buf);
2482 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2483 }
4eeb7d09 2484
5a73255e 2485 for(nlen = mptr = 0; mptr<len; mptr++) {
2486 uni_buf[nlen] = 0xFFFD;
2487 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2488 IpDx[nlen] += char_width;
2489 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2490 text+mptr, 2, uni_buf+nlen, 1);
2491 mptr++;
2492 }
2493 else
2494 {
2495 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2496 text+mptr, 1, uni_buf+nlen, 1);
2497 }
2498 nlen++;
2499 }
4eeb7d09 2500 if (nlen <= 0)
2501 return; /* Eeek! */
2502
2503 ExtTextOutW(hdc, x,
2504 y - font_height * (lattr == LATTR_BOT) + text_adjust,
5a73255e 2505 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 2506 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2507 SetBkMode(hdc, TRANSPARENT);
2508 ExtTextOutW(hdc, x - 1,
2509 y - font_height * (lattr ==
2510 LATTR_BOT) + text_adjust,
5a73255e 2511 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 2512 }
5a73255e 2513
2514 IpDx[0] = -1;
4eeb7d09 2515 } else if (DIRECT_FONT(attr)) {
2516 ExtTextOut(hdc, x,
2517 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2518 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2519 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2520 SetBkMode(hdc, TRANSPARENT);
2521
2522 /* GRR: This draws the character outside it's box and can leave
2523 * 'droppings' even with the clip box! I suppose I could loop it
2524 * one character at a time ... yuk.
2525 *
2526 * Or ... I could do a test print with "W", and use +1 or -1 for this
2527 * shift depending on if the leftmost column is blank...
2528 */
2529 ExtTextOut(hdc, x - 1,
2530 y - font_height * (lattr ==
2531 LATTR_BOT) + text_adjust,
2532 ETO_CLIPPED, &line_box, text, len, IpDx);
2533 }
2534 } else {
2535 /* And 'normal' unicode characters */
2536 static WCHAR *wbuf = NULL;
2537 static int wlen = 0;
2538 int i;
2539 if (wlen < len) {
2540 sfree(wbuf);
2541 wlen = len;
2542 wbuf = smalloc(wlen * sizeof(WCHAR));
2543 }
2544 for (i = 0; i < len; i++)
2545 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2546
2547 ExtTextOutW(hdc, x,
2548 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2549 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2550
2551 /* And the shadow bold hack. */
5a73255e 2552 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
4eeb7d09 2553 SetBkMode(hdc, TRANSPARENT);
2554 ExtTextOutW(hdc, x - 1,
2555 y - font_height * (lattr ==
2556 LATTR_BOT) + text_adjust,
2557 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2558 }
374330e2 2559 }
4eeb7d09 2560 if (lattr != LATTR_TOP && (force_manual_underline ||
2561 (und_mode == UND_LINE
2562 && (attr & ATTR_UNDER)))) {
32874aea 2563 HPEN oldpen;
4eeb7d09 2564 int dec = descent;
2565 if (lattr == LATTR_BOT)
2566 dec = dec * 2 - font_height;
2567
32874aea 2568 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
4eeb7d09 2569 MoveToEx(hdc, x, y + dec, NULL);
2570 LineTo(hdc, x + len * char_width, y + dec);
32874aea 2571 oldpen = SelectObject(hdc, oldpen);
2572 DeleteObject(oldpen);
374330e2 2573 }
4eeb7d09 2574}
2575
2576void do_cursor(Context ctx, int x, int y, char *text, int len,
2577 unsigned long attr, int lattr)
2578{
2579
2580 int fnt_width;
2581 int char_width;
2582 HDC hdc = ctx;
2583 int ctype = cfg.cursor_type;
2584
2585 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2586 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2587 do_text(ctx, x, y, text, len, attr, lattr);
2588 return;
2589 }
2590 ctype = 2;
2591 attr |= TATTR_RIGHTCURS;
2592 }
2593
2594 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2595 if (attr & ATTR_WIDE)
2596 char_width *= 2;
2597 x *= fnt_width;
2598 y *= font_height;
5a73255e 2599 x += offset_width;
2600 y += offset_height;
4eeb7d09 2601
2602 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
374330e2 2603 POINT pts[5];
32874aea 2604 HPEN oldpen;
374330e2 2605 pts[0].x = pts[1].x = pts[4].x = x;
4eeb7d09 2606 pts[2].x = pts[3].x = x + char_width - 1;
374330e2 2607 pts[0].y = pts[3].y = pts[4].y = y;
32874aea 2608 pts[1].y = pts[2].y = y + font_height - 1;
2609 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2610 Polyline(hdc, pts, 5);
2611 oldpen = SelectObject(hdc, oldpen);
2612 DeleteObject(oldpen);
4eeb7d09 2613 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
32874aea 2614 int startx, starty, dx, dy, length, i;
4eeb7d09 2615 if (ctype == 1) {
32874aea 2616 startx = x;
2617 starty = y + descent;
2618 dx = 1;
2619 dy = 0;
4eeb7d09 2620 length = char_width;
32874aea 2621 } else {
4e30ff69 2622 int xadjust = 0;
4eeb7d09 2623 if (attr & TATTR_RIGHTCURS)
2624 xadjust = char_width - 1;
32874aea 2625 startx = x + xadjust;
2626 starty = y;
2627 dx = 0;
2628 dy = 1;
2629 length = font_height;
2630 }
4eeb7d09 2631 if (attr & TATTR_ACTCURS) {
32874aea 2632 HPEN oldpen;
2633 oldpen =
2634 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2635 MoveToEx(hdc, startx, starty, NULL);
2636 LineTo(hdc, startx + dx * length, starty + dy * length);
2637 oldpen = SelectObject(hdc, oldpen);
2638 DeleteObject(oldpen);
2639 } else {
2640 for (i = 0; i < length; i++) {
2641 if (i % 2 == 0) {
2642 SetPixel(hdc, startx, starty, colours[23]);
2643 }
2644 startx += dx;
2645 starty += dy;
2646 }
2647 }
4e30ff69 2648 }
374330e2 2649}
2650
5a73255e 2651/* This function gets the actual width of a character in the normal font.
2652 */
2653int CharWidth(Context ctx, int uc) {
2654 HDC hdc = ctx;
2655 int ibuf = 0;
2656
2657 /* If the font max is the same as the font ave width then this
2658 * function is a no-op.
2659 */
2660 if (!font_dualwidth) return 1;
2661
2662 switch (uc & CSET_MASK) {
2663 case ATTR_ASCII:
2664 uc = unitab_line[uc & 0xFF];
2665 break;
2666 case ATTR_LINEDRW:
2667 uc = unitab_xterm[uc & 0xFF];
2668 break;
2669 case ATTR_SCOACS:
2670 uc = unitab_scoacs[uc & 0xFF];
2671 break;
2672 }
2673 if (DIRECT_FONT(uc)) {
2674 if (dbcs_screenfont) return 1;
2675
2676 /* Speedup, I know of no font where ascii is the wrong width */
2677 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2678 return 1;
2679
2680 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2681 SelectObject(hdc, fonts[FONT_NORMAL]);
2682 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2683 another_font(FONT_OEM);
2684 if (!fonts[FONT_OEM]) return 0;
2685
2686 SelectObject(hdc, fonts[FONT_OEM]);
2687 } else
2688 return 0;
2689
2690 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2691 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2692 return 0;
2693 } else {
2694 /* Speedup, I know of no font where ascii is the wrong width */
2695 if (uc >= ' ' && uc <= '~') return 1;
2696
2697 SelectObject(hdc, fonts[FONT_NORMAL]);
2698 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2699 /* Okay that one worked */ ;
2700 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2701 /* This should work on 9x too, but it's "less accurate" */ ;
2702 else
2703 return 0;
2704 }
2705
2706 ibuf += font_width / 2 -1;
2707 ibuf /= font_width;
2708
2709 return ibuf;
2710}
2711
374330e2 2712/*
c9def1b8 2713 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2714 * codes. Returns number of bytes used or zero to drop the message
2715 * or -1 to forward the message to windows.
374330e2 2716 */
3cf144db 2717static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
32874aea 2718 unsigned char *output)
2719{
374330e2 2720 BYTE keystate[256];
32874aea 2721 int scan, left_alt = 0, key_down, shift_state;
2722 int r, i, code;
2723 unsigned char *p = output;
4eeb7d09 2724 static int alt_sum = 0;
374330e2 2725
00e3ba0f 2726 HKL kbd_layout = GetKeyboardLayout(0);
2727
0c50ef57 2728 static WORD keys[3];
0c50ef57 2729 static int compose_char = 0;
2730 static WPARAM compose_key = 0;
32874aea 2731
c9def1b8 2732 r = GetKeyboardState(keystate);
32874aea 2733 if (!r)
2734 memset(keystate, 0, sizeof(keystate));
2735 else {
ec55b220 2736#if 0
4eeb7d09 2737#define SHOW_TOASCII_RESULT
32874aea 2738 { /* Tell us all about key events */
2739 static BYTE oldstate[256];
2740 static int first = 1;
2741 static int scan;
2742 int ch;
2743 if (first)
2744 memcpy(oldstate, keystate, sizeof(oldstate));
2745 first = 0;
2746
2747 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2748 debug(("+"));
2749 } else if ((HIWORD(lParam) & KF_UP)
2750 && scan == (HIWORD(lParam) & 0xFF)) {
2751 debug((". U"));
2752 } else {
2753 debug((".\n"));
2754 if (wParam >= VK_F1 && wParam <= VK_F20)
2755 debug(("K_F%d", wParam + 1 - VK_F1));
2756 else
2757 switch (wParam) {
2758 case VK_SHIFT:
2759 debug(("SHIFT"));
2760 break;
2761 case VK_CONTROL:
2762 debug(("CTRL"));
2763 break;
2764 case VK_MENU:
2765 debug(("ALT"));
2766 break;
2767 default:
2768 debug(("VK_%02x", wParam));
2769 }
2770 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2771 debug(("*"));
2772 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2773
2774 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2775 if (ch >= ' ' && ch <= '~')
2776 debug((", '%c'", ch));
2777 else if (ch)
2778 debug((", $%02x", ch));
2779
2780 if (keys[0])
2781 debug((", KB0=%02x", keys[0]));
2782 if (keys[1])
2783 debug((", KB1=%02x", keys[1]));
2784 if (keys[2])
2785 debug((", KB2=%02x", keys[2]));
2786
2787 if ((keystate[VK_SHIFT] & 0x80) != 0)
2788 debug((", S"));
2789 if ((keystate[VK_CONTROL] & 0x80) != 0)
2790 debug((", C"));
2791 if ((HIWORD(lParam) & KF_EXTENDED))
2792 debug((", E"));
2793 if ((HIWORD(lParam) & KF_UP))
2794 debug((", U"));
2795 }
2796
2797 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2798 else if ((HIWORD(lParam) & KF_UP))
2799 oldstate[wParam & 0xFF] ^= 0x80;
2800 else
2801 oldstate[wParam & 0xFF] ^= 0x81;
2802
2803 for (ch = 0; ch < 256; ch++)
2804 if (oldstate[ch] != keystate[ch])
2805 debug((", M%02x=%02x", ch, keystate[ch]));
2806
2807 memcpy(oldstate, keystate, sizeof(oldstate));
2808 }
ec55b220 2809#endif
2810
32874aea 2811 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2cba1186 2812 keystate[VK_RMENU] = keystate[VK_MENU];
2813 }
2814
c9def1b8 2815
2816 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
32874aea 2817 if ((cfg.funky_type == 3 ||
2818 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2819 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
c9def1b8 2820
2821 wParam = VK_EXECUTE;
2822
2823 /* UnToggle NUMLock */
32874aea 2824 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2825 keystate[VK_NUMLOCK] ^= 1;
c9def1b8 2826 }
2827
2828 /* And write back the 'adjusted' state */
32874aea 2829 SetKeyboardState(keystate);
c9def1b8 2830 }
2831
2832 /* Disable Auto repeat if required */
32874aea 2833 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2834 return 0;
c9def1b8 2835
32874aea 2836 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
c9def1b8 2837 left_alt = 1;
2838
32874aea 2839 key_down = ((HIWORD(lParam) & KF_UP) == 0);
c9def1b8 2840
95bbe1ae 2841 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
32874aea 2842 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
95bbe1ae 2843 if (cfg.ctrlaltkeys)
2844 keystate[VK_MENU] = 0;
2845 else {
2846 keystate[VK_RMENU] = 0x80;
2847 left_alt = 0;
2848 }
2849 }
c9def1b8 2850
5a73255e 2851 alt_pressed = (left_alt && key_down);
2852
c9def1b8 2853 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
32874aea 2854 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2855 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
374330e2 2856
95bbe1ae 2857 /* Note if AltGr was pressed and if it was used as a compose key */
2858 if (!compose_state) {
159eba53 2859 compose_key = 0x100;
95bbe1ae 2860 if (cfg.compose_key) {
32874aea 2861 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
95bbe1ae 2862 compose_key = wParam;
2863 }
2864 if (wParam == VK_APPS)
2865 compose_key = wParam;
2866 }
2867
32874aea 2868 if (wParam == compose_key) {
2869 if (compose_state == 0
2870 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2871 1;
2872 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
95bbe1ae 2873 compose_state = 2;
2874 else
2875 compose_state = 0;
32874aea 2876 } else if (compose_state == 1 && wParam != VK_CONTROL)
95bbe1ae 2877 compose_state = 0;
2878
67c339f7 2879 /*
cabfd08c 2880 * Record that we pressed key so the scroll window can be reset, but
2881 * be careful to avoid Shift-UP/Down
2882 */
a910b704 2883 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
2884 wParam != VK_MENU && wParam != VK_CONTROL) {
32874aea 2885 seen_key_event = 1;
cabfd08c 2886 }
2887
32874aea 2888 if (compose_state > 1 && left_alt)
2889 compose_state = 0;
67c339f7 2890
c9def1b8 2891 /* Sanitize the number pad if not using a PC NumPad */
32874aea 2892 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2893 && cfg.funky_type != 2)
2894 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2895 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
c9def1b8 2896 int nParam = 0;
32874aea 2897 switch (wParam) {
2898 case VK_INSERT:
2899 nParam = VK_NUMPAD0;
2900 break;
2901 case VK_END:
2902 nParam = VK_NUMPAD1;
2903 break;
2904 case VK_DOWN:
2905 nParam = VK_NUMPAD2;
2906 break;
2907 case VK_NEXT:
2908 nParam = VK_NUMPAD3;
2909 break;
2910 case VK_LEFT:
2911 nParam = VK_NUMPAD4;
2912 break;
2913 case VK_CLEAR:
2914 nParam = VK_NUMPAD5;
2915 break;
2916 case VK_RIGHT:
2917 nParam = VK_NUMPAD6;
2918 break;
2919 case VK_HOME:
2920 nParam = VK_NUMPAD7;
2921 break;
2922 case VK_UP:
2923 nParam = VK_NUMPAD8;
2924 break;
2925 case VK_PRIOR:
2926 nParam = VK_NUMPAD9;
2927 break;
2928 case VK_DELETE:
2929 nParam = VK_DECIMAL;
2930 break;
c9def1b8 2931 }
32874aea 2932 if (nParam) {
2933 if (keystate[VK_NUMLOCK] & 1)
2934 shift_state |= 1;
c9def1b8 2935 wParam = nParam;
2936 }
25d39ef6 2937 }
2938 }
2939
c9def1b8 2940 /* If a key is pressed and AltGr is not active */
32874aea 2941 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2942 /* Okay, prepare for most alts then ... */
2943 if (left_alt)
2944 *p++ = '\033';
374330e2 2945
c9def1b8 2946 /* Lets see if it's a pattern we know all about ... */
2947 if (wParam == VK_PRIOR && shift_state == 1) {
32874aea 2948 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2949 return 0;
c9def1b8 2950 }
2951 if (wParam == VK_NEXT && shift_state == 1) {
32874aea 2952 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2953 return 0;
2954 }
2955 if (wParam == VK_INSERT && shift_state == 1) {
568dd02f 2956 term_do_paste();
32874aea 2957 return 0;
2958 }
c9def1b8 2959 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
32874aea 2960 return -1;
c9def1b8 2961 }
2962 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
32874aea 2963 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2964 return -1;
c9def1b8 2965 }
8f57d753 2966 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter) {
2967 flip_full_screen();
2968 return -1;
2969 }
ec55b220 2970 /* Control-Numlock for app-keypad mode switch */
2971 if (wParam == VK_PAUSE && shift_state == 2) {
2972 app_keypad_keys ^= 1;
2973 return 0;
2974 }
374330e2 2975
c9def1b8 2976 /* Nethack keypad */
2977 if (cfg.nethack_keypad && !left_alt) {
32874aea 2978 switch (wParam) {
2979 case VK_NUMPAD1:
2980 *p++ = shift_state ? 'B' : 'b';
2981 return p - output;
2982 case VK_NUMPAD2:
2983 *p++ = shift_state ? 'J' : 'j';
2984 return p - output;
2985 case VK_NUMPAD3:
2986 *p++ = shift_state ? 'N' : 'n';
2987 return p - output;
2988 case VK_NUMPAD4:
2989 *p++ = shift_state ? 'H' : 'h';
2990 return p - output;
2991 case VK_NUMPAD5:
2992 *p++ = shift_state ? '.' : '.';
2993 return p - output;
2994 case VK_NUMPAD6:
2995 *p++ = shift_state ? 'L' : 'l';
2996 return p - output;
2997 case VK_NUMPAD7:
2998 *p++ = shift_state ? 'Y' : 'y';
2999 return p - output;
3000 case VK_NUMPAD8:
3001 *p++ = shift_state ? 'K' : 'k';
3002 return p - output;
3003 case VK_NUMPAD9:
3004 *p++ = shift_state ? 'U' : 'u';
3005 return p - output;
3006 }
c9def1b8 3007 }
3008
3009 /* Application Keypad */
3010 if (!left_alt) {
32874aea 3011 int xkey = 0;
3012
3013 if (cfg.funky_type == 3 ||
3014 (cfg.funky_type <= 1 &&
3015 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3016 case VK_EXECUTE:
3017 xkey = 'P';
3018 break;
3019 case VK_DIVIDE:
3020 xkey = 'Q';
3021 break;
3022 case VK_MULTIPLY:
3023 xkey = 'R';
3024 break;
3025 case VK_SUBTRACT:
3026 xkey = 'S';
3027 break;
3028 }
3029 if (app_keypad_keys && !cfg.no_applic_k)
3030 switch (wParam) {
3031 case VK_NUMPAD0:
3032 xkey = 'p';
3033 break;
3034 case VK_NUMPAD1:
3035 xkey = 'q';
3036 break;
3037 case VK_NUMPAD2:
3038 xkey = 'r';
3039 break;
3040 case VK_NUMPAD3:
3041 xkey = 's';
3042 break;
3043 case VK_NUMPAD4:
3044 xkey = 't';
3045 break;
3046 case VK_NUMPAD5:
3047 xkey = 'u';
3048 break;
3049 case VK_NUMPAD6:
3050 xkey = 'v';
3051 break;
3052 case VK_NUMPAD7:
3053 xkey = 'w';
3054 break;
3055 case VK_NUMPAD8:
3056 xkey = 'x';
3057 break;
3058 case VK_NUMPAD9:
3059 xkey = 'y';
3060 break;
3061
3062 case VK_DECIMAL:
3063 xkey = 'n';
3064 break;
3065 case VK_ADD:
3066 if (cfg.funky_type == 2) {
3067 if (shift_state)
3068 xkey = 'l';
3069 else
3070 xkey = 'k';
3071 } else if (shift_state)
3072 xkey = 'm';
c9def1b8 3073 else
32874aea 3074 xkey = 'l';
3075 break;
3076
3077 case VK_DIVIDE:
3078 if (cfg.funky_type == 2)
3079 xkey = 'o';
3080 break;
3081 case VK_MULTIPLY:
3082 if (cfg.funky_type == 2)
3083 xkey = 'j';
3084 break;
3085 case VK_SUBTRACT:
3086 if (cfg.funky_type == 2)
3087 xkey = 'm';
3088 break;
3089
3090 case VK_RETURN:
3091 if (HIWORD(lParam) & KF_EXTENDED)
3092 xkey = 'M';
3093 break;
c9def1b8 3094 }
32874aea 3095 if (xkey) {
3096 if (vt52_mode) {
3097 if (xkey >= 'P' && xkey <= 'S')
3098 p += sprintf((char *) p, "\x1B%c", xkey);
3099 else
3100 p += sprintf((char *) p, "\x1B?%c", xkey);
3101 } else
3102 p += sprintf((char *) p, "\x1BO%c", xkey);
3103 return p - output;
c9def1b8 3104 }
3105 }
3106
32874aea 3107 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
c9def1b8 3108 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
a5f3e637 3109 *p++ = 0;
3110 return -2;
c9def1b8 3111 }
32874aea 3112 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3113 *p++ = 0x1B;
3114 *p++ = '[';
3115 *p++ = 'Z';
3116 return p - output;
c9def1b8 3117 }
32874aea 3118 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3119 *p++ = 0;
3120 return p - output;
c9def1b8 3121 }
32874aea 3122 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3123 *p++ = 160;
3124 return p - output;
c9def1b8 3125 }
32874aea 3126 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3127 *p++ = 3;
a5f3e637 3128 *p++ = 0;
3129 return -2;
c9def1b8 3130 }
32874aea 3131 if (wParam == VK_PAUSE) { /* Break/Pause */
3132 *p++ = 26;
3133 *p++ = 0;
3134 return -2;
95bbe1ae 3135 }
c9def1b8 3136 /* Control-2 to Control-8 are special */
32874aea 3137 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3138 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
c9def1b8 3139 return p - output;
3140 }
3141 if (shift_state == 2 && wParam == 0xBD) {
3142 *p++ = 0x1F;
3143 return p - output;
3144 }
3145 if (shift_state == 2 && wParam == 0xDF) {
3146 *p++ = 0x1C;
3147 return p - output;
3148 }
3149 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
32874aea 3150 *p++ = '\r';
3151 *p++ = '\n';
c9def1b8 3152 return p - output;
3153 }
374330e2 3154
c5e9c988 3155 /*
c9def1b8 3156 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3157 * for integer decimal nn.)
3158 *
3159 * We also deal with the weird ones here. Linux VCs replace F1
3160 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3161 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3162 * respectively.
c5e9c988 3163 */
c9def1b8 3164 code = 0;
3165 switch (wParam) {
32874aea 3166 case VK_F1:
3167 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3168 break;
3169 case VK_F2:
3170 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3171 break;
3172 case VK_F3:
3173 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3174 break;
3175 case VK_F4:
3176 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3177 break;
3178 case VK_F5:
3179 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3180 break;
3181 case VK_F6:
3182 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3183 break;
3184 case VK_F7:
3185 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3186 break;
3187 case VK_F8:
3188 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3189 break;
3190 case VK_F9:
3191 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3192 break;
3193 case VK_F10:
3194 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3195 break;
3196 case VK_F11:
3197 code = 23;
3198 break;
3199 case VK_F12:
3200 code = 24;
3201 break;
3202 case VK_F13:
3203 code = 25;
3204 break;
3205 case VK_F14:
3206 code = 26;
3207 break;
3208 case VK_F15:
3209 code = 28;
3210 break;
3211 case VK_F16:
3212 code = 29;
3213 break;
3214 case VK_F17:
3215 code = 31;
3216 break;
3217 case VK_F18:
3218 code = 32;
3219 break;
3220 case VK_F19:
3221 code = 33;
3222 break;
3223 case VK_F20:
3224 code = 34;
3225 break;
dfca2656 3226 }
3227 if ((shift_state&2) == 0) switch (wParam) {
32874aea 3228 case VK_HOME:
3229 code = 1;
3230 break;
3231 case VK_INSERT:
3232 code = 2;
3233 break;
3234 case VK_DELETE:
3235 code = 3;
3236 break;
3237 case VK_END:
3238 code = 4;
3239 break;
3240 case VK_PRIOR:
3241 code = 5;
3242 break;
3243 case VK_NEXT:
3244 code = 6;
3245 break;
374330e2 3246 }
ec55b220 3247 /* Reorder edit keys to physical order */
32874aea 3248 if (cfg.funky_type == 3 && code <= 6)
3249 code = "\0\2\1\4\5\3\6"[code];
ec55b220 3250
f37caa11 3251 if (vt52_mode && code > 0 && code <= 6) {
32874aea 3252 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
f37caa11 3253 return p - output;
3254 }
3255
9bc81a2c 3256 if (cfg.funky_type == 5 && /* SCO function keys */
3257 code >= 11 && code <= 34) {
e24b1972 3258 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3259 int index = 0;
3260 switch (wParam) {
3261 case VK_F1: index = 0; break;
3262 case VK_F2: index = 1; break;
3263 case VK_F3: index = 2; break;
3264 case VK_F4: index = 3; break;
3265 case VK_F5: index = 4; break;
3266 case VK_F6: index = 5; break;
3267 case VK_F7: index = 6; break;
3268 case VK_F8: index = 7; break;
3269 case VK_F9: index = 8; break;
3270 case VK_F10: index = 9; break;
3271 case VK_F11: index = 10; break;
3272 case VK_F12: index = 11; break;
3273 }
3274 if (keystate[VK_SHIFT] & 0x80) index += 12;
3275 if (keystate[VK_CONTROL] & 0x80) index += 24;
3276 p += sprintf((char *) p, "\x1B[%c", codes[index]);
f37caa11 3277 return p - output;
3278 }
9bc81a2c 3279 if (cfg.funky_type == 5 && /* SCO small keypad */
3280 code >= 1 && code <= 6) {
3281 char codes[] = "HL.FIG";
3282 if (code == 3) {
3283 *p++ = '\x7F';
3284 } else {
3285 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3286 }
3287 return p - output;
3288 }
f37caa11 3289 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3290 int offt = 0;
32874aea 3291 if (code > 15)
3292 offt++;
3293 if (code > 21)
3294 offt++;
f37caa11 3295 if (vt52_mode)
32874aea 3296 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
f37caa11 3297 else
32874aea 3298 p +=
3299 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
f37caa11 3300 return p - output;
3301 }
c9def1b8 3302 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
32874aea 3303 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
c9def1b8 3304 return p - output;
3305 }
3306 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
ec55b220 3307 if (vt52_mode)
32874aea 3308 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
ec55b220 3309 else
32874aea 3310 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
c9def1b8 3311 return p - output;
3312 }
3313 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
32874aea 3314 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
c9def1b8 3315 return p - output;
3316 }
3317 if (code) {
32874aea 3318 p += sprintf((char *) p, "\x1B[%d~", code);
374330e2 3319 return p - output;
374330e2 3320 }
45dabbc5 3321
c9def1b8 3322 /*
3323 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3324 * some reason seems to send VK_CLEAR to Windows...).
3325 */
3326 {
3327 char xkey = 0;
3328 switch (wParam) {
32874aea 3329 case VK_UP:
3330 xkey = 'A';
3331 break;
3332 case VK_DOWN:
3333 xkey = 'B';
3334 break;
3335 case VK_RIGHT:
3336 xkey = 'C';
3337 break;
3338 case VK_LEFT:
3339 xkey = 'D';
3340 break;
3341 case VK_CLEAR:
3342 xkey = 'G';
3343 break;
c9def1b8 3344 }
32874aea 3345 if (xkey) {
c9def1b8 3346 if (vt52_mode)
32874aea 3347 p += sprintf((char *) p, "\x1B%c", xkey);
e864f84f 3348 else {
3349 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3350 /* VT100 & VT102 manuals both state the app cursor keys
3351 * only work if the app keypad is on.
3352 */
3353 if (!app_keypad_keys)
3354 app_flg = 0;
3355 /* Useful mapping of Ctrl-arrows */
3356 if (shift_state == 2)
3357 app_flg = !app_flg;
3358
3359 if (app_flg)
3360 p += sprintf((char *) p, "\x1BO%c", xkey);
3361 else
3362 p += sprintf((char *) p, "\x1B[%c", xkey);
3363 }
c9def1b8 3364 return p - output;
3365 }
3366 }
0c50ef57 3367
3368 /*
3369 * Finally, deal with Return ourselves. (Win95 seems to
3370 * foul it up when Alt is pressed, for some reason.)
3371 */
32874aea 3372 if (wParam == VK_RETURN) { /* Return */
0c50ef57 3373 *p++ = 0x0D;
a5f3e637 3374 *p++ = 0;
3375 return -2;
0c50ef57 3376 }
4eeb7d09 3377
3378 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3379 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3380 else
3381 alt_sum = 0;
67c339f7 3382 }
374330e2 3383
c9def1b8 3384 /* Okay we've done everything interesting; let windows deal with
3385 * the boring stuff */
3386 {
a9c02454 3387 BOOL capsOn=0;
3388
3389 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3390 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3391 capsOn= !left_alt;
3392 keystate[VK_CAPITAL] = 0;
3393 }
3394
00e3ba0f 3395 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
4eeb7d09 3396#ifdef SHOW_TOASCII_RESULT
3397 if (r == 1 && !key_down) {
3398 if (alt_sum) {
8f22582c 3399 if (in_utf || dbcs_screenfont)
4eeb7d09 3400 debug((", (U+%04x)", alt_sum));
3401 else
3402 debug((", LCH(%d)", alt_sum));
3403 } else {
3404 debug((", ACH(%d)", keys[0]));
3405 }
3406 } else if (r > 0) {
3407 int r1;
3408 debug((", ASC("));
3409 for (r1 = 0; r1 < r; r1++) {
3410 debug(("%s%d", r1 ? "," : "", keys[r1]));
3411 }
3412 debug((")"));
3413 }
3414#endif
32874aea 3415 if (r > 0) {
4eeb7d09 3416 WCHAR keybuf;
256cb87c 3417
3418 /*
3419 * Interrupt an ongoing paste. I'm not sure this is
3420 * sensible, but for the moment it's preferable to
3421 * having to faff about buffering things.
3422 */
3423 term_nopaste();
3424
c9def1b8 3425 p = output;
32874aea 3426 for (i = 0; i < r; i++) {
3427 unsigned char ch = (unsigned char) keys[i];
14963b8f 3428
32874aea 3429 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 3430 compose_char = ch;
32874aea 3431 compose_state++;
c9def1b8 3432 continue;
3433 }
32874aea 3434 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 3435 int nc;
3436 compose_state = 0;
3437
32874aea 3438 if ((nc = check_compose(compose_char, ch)) == -1) {
fe50e814 3439 MessageBeep(MB_ICONHAND);
c9def1b8 3440 return 0;
3441 }
4eeb7d09 3442 keybuf = nc;
760e88b2 3443 luni_send(&keybuf, 1, 1);
4eeb7d09 3444 continue;
c9def1b8 3445 }
374330e2 3446
c9def1b8 3447 compose_state = 0;
374330e2 3448
4eeb7d09 3449 if (!key_down) {
3450 if (alt_sum) {
8f22582c 3451 if (in_utf || dbcs_screenfont) {
4eeb7d09 3452 keybuf = alt_sum;
760e88b2 3453 luni_send(&keybuf, 1, 1);
4eeb7d09 3454 } else {
3455 ch = (char) alt_sum;
5471d09a 3456 /*
3457 * We need not bother about stdin
3458 * backlogs here, because in GUI PuTTY
3459 * we can't do anything about it
3460 * anyway; there's no means of asking
3461 * Windows to hold off on KEYDOWN
3462 * messages. We _have_ to buffer
3463 * everything we're sent.
3464 */
760e88b2 3465 ldisc_send(&ch, 1, 1);
4eeb7d09 3466 }
3467 alt_sum = 0;
3468 } else
760e88b2 3469 lpage_send(kbd_codepage, &ch, 1, 1);
4eeb7d09 3470 } else {
a9c02454 3471 if(capsOn && ch < 0x80) {
3472 WCHAR cbuf[2];
3473 cbuf[0] = 27;
3474 cbuf[1] = xlat_uskbd2cyrllic(ch);
760e88b2 3475 luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 3476 } else {
3477 char cbuf[2];
3478 cbuf[0] = '\033';
3479 cbuf[1] = ch;
760e88b2 3480 lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 3481 }
c9def1b8 3482 }
bca9517a 3483 show_mouseptr(0);
c9def1b8 3484 }
374330e2 3485
c9def1b8 3486 /* This is so the ALT-Numpad and dead keys work correctly. */
3487 keys[0] = 0;
3488
32874aea 3489 return p - output;
c9def1b8 3490 }
159eba53 3491 /* If we're definitly not building up an ALT-54321 then clear it */
32874aea 3492 if (!left_alt)
3493 keys[0] = 0;
4eeb7d09 3494 /* If we will be using alt_sum fix the 256s */
8f22582c 3495 else if (keys[0] && (in_utf || dbcs_screenfont))
4eeb7d09 3496 keys[0] = 10;
374330e2 3497 }
3498
dfca2656 3499 /*
3500 * ALT alone may or may not want to bring up the System menu.
3501 * If it's not meant to, we return 0 on presses or releases of
3502 * ALT, to show that we've swallowed the keystroke. Otherwise
3503 * we return -1, which means Windows will give the keystroke
3504 * its default handling (i.e. bring up the System menu).
3505 */
3506 if (wParam == VK_MENU && !cfg.alt_only)
3507 return 0;
374330e2 3508
c9def1b8 3509 return -1;
374330e2 3510}
3511
32874aea 3512void set_title(char *title)
3513{
3514 sfree(window_name);
3515 window_name = smalloc(1 + strlen(title));
3516 strcpy(window_name, title);
37508af4 3517 if (cfg.win_name_always || !IsIconic(hwnd))
32874aea 3518 SetWindowText(hwnd, title);
374330e2 3519}
3520
32874aea 3521void set_icon(char *title)
3522{
3523 sfree(icon_name);
3524 icon_name = smalloc(1 + strlen(title));
3525 strcpy(icon_name, title);
37508af4 3526 if (!cfg.win_name_always && IsIconic(hwnd))
32874aea 3527 SetWindowText(hwnd, title);
374330e2 3528}
3529
32874aea 3530void set_sbar(int total, int start, int page)
3531{
374330e2 3532 SCROLLINFO si;
c9def1b8 3533
32874aea 3534 if (!cfg.scrollbar)
3535 return;
c9def1b8 3536
374330e2 3537 si.cbSize = sizeof(si);
c9def1b8 3538 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
374330e2 3539 si.nMin = 0;
3540 si.nMax = total - 1;
3541 si.nPage = page;
3542 si.nPos = start;
c1f5f956 3543 if (hwnd)
32874aea 3544 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
374330e2 3545}
3546
32874aea 3547Context get_ctx(void)
3548{
374330e2 3549 HDC hdc;
3550 if (hwnd) {
32874aea 3551 hdc = GetDC(hwnd);
374330e2 3552 if (hdc && pal)
32874aea 3553 SelectPalette(hdc, pal, FALSE);
374330e2 3554 return hdc;
3555 } else
3556 return NULL;
3557}
3558
32874aea 3559void free_ctx(Context ctx)
3560{
3561 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3562 ReleaseDC(hwnd, ctx);
374330e2 3563}
3564
32874aea 3565static void real_palette_set(int n, int r, int g, int b)
3566{
374330e2 3567 if (pal) {
3568 logpal->palPalEntry[n].peRed = r;
3569 logpal->palPalEntry[n].peGreen = g;
3570 logpal->palPalEntry[n].peBlue = b;
3571 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3572 colours[n] = PALETTERGB(r, g, b);
32874aea 3573 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 3574 } else
3575 colours[n] = RGB(r, g, b);
3576}
3577
32874aea 3578void palette_set(int n, int r, int g, int b)
3579{
374330e2 3580 static const int first[21] = {
3581 0, 2, 4, 6, 8, 10, 12, 14,
3582 1, 3, 5, 7, 9, 11, 13, 15,
3583 16, 17, 18, 20, 22
3584 };
32874aea 3585 real_palette_set(first[n], r, g, b);
374330e2 3586 if (first[n] >= 18)
32874aea 3587 real_palette_set(first[n] + 1, r, g, b);
374330e2 3588 if (pal) {
3589 HDC hdc = get_ctx();
32874aea 3590 UnrealizeObject(pal);
3591 RealizePalette(hdc);
3592 free_ctx(hdc);
374330e2 3593 }
3594}
3595
32874aea 3596void palette_reset(void)
3597{
374330e2 3598 int i;
3599
3600 for (i = 0; i < NCOLOURS; i++) {
3601 if (pal) {
3602 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3603 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3604 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3605 logpal->palPalEntry[i].peFlags = 0;
3606 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3607 defpal[i].rgbtGreen,
3608 defpal[i].rgbtBlue);
3609 } else
3610 colours[i] = RGB(defpal[i].rgbtRed,
32874aea 3611 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
374330e2 3612 }
3613
3614 if (pal) {
3615 HDC hdc;
32874aea 3616 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 3617 hdc = get_ctx();
32874aea 3618 RealizePalette(hdc);
3619 free_ctx(hdc);
374330e2 3620 }
3621}
3622
4eeb7d09 3623void write_aclip(char *data, int len, int must_deselect)
32874aea 3624{
374330e2 3625 HGLOBAL clipdata;
3626 void *lock;
3627
32874aea 3628 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
374330e2 3629 if (!clipdata)
3630 return;
32874aea 3631 lock = GlobalLock(clipdata);
374330e2 3632 if (!lock)
3633 return;
32874aea 3634 memcpy(lock, data, len);
3635 ((unsigned char *) lock)[len] = 0;
3636 GlobalUnlock(clipdata);
374330e2 3637
f0df44da 3638 if (!must_deselect)
32874aea 3639 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
f0df44da 3640
32874aea 3641 if (OpenClipboard(hwnd)) {
374330e2 3642 EmptyClipboard();
32874aea 3643 SetClipboardData(CF_TEXT, clipdata);
374330e2 3644 CloseClipboard();
3645 } else
32874aea 3646 GlobalFree(clipdata);
f0df44da 3647
3648 if (!must_deselect)
32874aea 3649 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
374330e2 3650}
3651
4eeb7d09 3652/*
3653 * Note: unlike write_aclip() this will not append a nul.
3654 */
3655void write_clip(wchar_t * data, int len, int must_deselect)
3656{
a7419ea4 3657 HGLOBAL clipdata, clipdata2, clipdata3;
4eeb7d09 3658 int len2;
a7419ea4 3659 void *lock, *lock2, *lock3;
4eeb7d09 3660
3661 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3662
3663 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3664 len * sizeof(wchar_t));
3665 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3666
a7419ea4 3667 if (!clipdata || !clipdata2 || !clipdata3) {
4eeb7d09 3668 if (clipdata)
3669 GlobalFree(clipdata);
3670 if (clipdata2)
3671 GlobalFree(clipdata2);
a7419ea4 3672 if (clipdata3)
3673 GlobalFree(clipdata3);
4eeb7d09 3674 return;
3675 }
3676 if (!(lock = GlobalLock(clipdata)))
3677 return;
3678 if (!(lock2 = GlobalLock(clipdata2)))
3679 return;
3680
3681 memcpy(lock, data, len * sizeof(wchar_t));
3682 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3683
a7419ea4 3684 if (cfg.rtf_paste) {
3685 wchar_t unitab[256];
3686 char *rtf = NULL;
3687 unsigned char *tdata = (unsigned char *)lock2;
3688 wchar_t *udata = (wchar_t *)lock;
3689 int rtflen = 0, uindex = 0, tindex = 0;
3690 int rtfsize = 0;
3691 int multilen, blen, alen, totallen, i;
3692 char before[16], after[4];
3693
3694 get_unitab(CP_ACP, unitab, 0);
3695
3696 rtfsize = 100 + strlen(cfg.font);
3697 rtf = smalloc(rtfsize);
3698 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3699 GetACP(), cfg.font);
3700 rtflen = strlen(rtf);
3701
3702 /*
3703 * We want to construct a piece of RTF that specifies the
3704 * same Unicode text. To do this we will read back in
3705 * parallel from the Unicode data in `udata' and the
3706 * non-Unicode data in `tdata'. For each character in
3707 * `tdata' which becomes the right thing in `udata' when
3708 * looked up in `unitab', we just copy straight over from
3709 * tdata. For each one that doesn't, we must WCToMB it
3710 * individually and produce a \u escape sequence.
3711 *
3712 * It would probably be more robust to just bite the bullet
3713 * and WCToMB each individual Unicode character one by one,
3714 * then MBToWC each one back to see if it was an accurate
3715 * translation; but that strikes me as a horrifying number
3716 * of Windows API calls so I want to see if this faster way
3717 * will work. If it screws up badly we can always revert to
3718 * the simple and slow way.
3719 */
3720 while (tindex < len2 && uindex < len &&
3721 tdata[tindex] && udata[uindex]) {
3722 if (tindex + 1 < len2 &&
3723 tdata[tindex] == '\r' &&
3724 tdata[tindex+1] == '\n') {
3725 tindex++;
3726 uindex++;
3727 }
3728 if (unitab[tdata[tindex]] == udata[uindex]) {
3729 multilen = 1;
3730 before[0] = '\0';
3731 after[0] = '\0';
3732 blen = alen = 0;
3733 } else {
3734 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
3735 NULL, 0, NULL, NULL);
3736 if (multilen != 1) {
c3b032a6 3737 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
3738 udata[uindex]);
a7419ea4 3739 alen = 1; strcpy(after, "}");
3740 } else {
3741 blen = sprintf(before, "\\u%d", udata[uindex]);
3742 alen = 0; after[0] = '\0';
3743 }
3744 }
3745 assert(tindex + multilen <= len2);
3746 totallen = blen + alen;
3747 for (i = 0; i < multilen; i++) {
3748 if (tdata[tindex+i] == '\\' ||
3749 tdata[tindex+i] == '{' ||
3750 tdata[tindex+i] == '}')
3751 totallen += 2;
3752 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
3753 totallen += 6; /* \par\r\n */
3754 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
3755 totallen += 4;
3756 else
3757 totallen++;
3758 }
3759
3760 if (rtfsize < rtflen + totallen + 3) {
3761 rtfsize = rtflen + totallen + 512;
3762 rtf = srealloc(rtf, rtfsize);
3763 }
3764
3765 strcpy(rtf + rtflen, before); rtflen += blen;
3766 for (i = 0; i < multilen; i++) {
3767 if (tdata[tindex+i] == '\\' ||
3768 tdata[tindex+i] == '{' ||
3769 tdata[tindex+i] == '}') {
3770 rtf[rtflen++] = '\\';
3771 rtf[rtflen++] = tdata[tindex+i];
3772 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
3773 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
3774 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
3775 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
3776 } else {
3777 rtf[rtflen++] = tdata[tindex+i];
3778 }
3779 }
3780 strcpy(rtf + rtflen, after); rtflen += alen;
3781
3782 tindex += multilen;
3783 uindex++;
3784 }
3785
3786 strcpy(rtf + rtflen, "}");
3787 rtflen += 2;
3788
3789 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
3790 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
3791 strcpy(lock3, rtf);
3792 GlobalUnlock(clipdata3);
3793 }
3794 sfree(rtf);
3795 } else
3796 clipdata3 = NULL;
3797
4eeb7d09 3798 GlobalUnlock(clipdata);
3799 GlobalUnlock(clipdata2);
3800
3801 if (!must_deselect)
3802 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3803
3804 if (OpenClipboard(hwnd)) {
3805 EmptyClipboard();
3806 SetClipboardData(CF_UNICODETEXT, clipdata);
3807 SetClipboardData(CF_TEXT, clipdata2);
a7419ea4 3808 if (clipdata3)
3809 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
4eeb7d09 3810 CloseClipboard();
3811 } else {
3812 GlobalFree(clipdata);
3813 GlobalFree(clipdata2);
3814 }
3815
3816 if (!must_deselect)
3817 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3818}
3819
3820void get_clip(wchar_t ** p, int *len)
32874aea 3821{
374330e2 3822 static HGLOBAL clipdata = NULL;
4eeb7d09 3823 static wchar_t *converted = 0;
3824 wchar_t *p2;
374330e2 3825
4eeb7d09 3826 if (converted) {
3827 sfree(converted);
3828 converted = 0;
3829 }
374330e2 3830 if (!p) {
3831 if (clipdata)
32874aea 3832 GlobalUnlock(clipdata);
374330e2 3833 clipdata = NULL;
3834 return;
4eeb7d09 3835 } else if (OpenClipboard(NULL)) {
2d466ffd 3836 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
374330e2 3837 CloseClipboard();
4eeb7d09 3838 *p = GlobalLock(clipdata);
3839 if (*p) {
3840 for (p2 = *p; *p2; p2++);
3841 *len = p2 - *p;
3842 return;
374330e2 3843 }
2d466ffd 3844 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4eeb7d09 3845 char *s;
3846 int i;
3847 CloseClipboard();
3848 s = GlobalLock(clipdata);
3849 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3850 *p = converted = smalloc(i * sizeof(wchar_t));
3851 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3852 *len = i - 1;
3853 return;
3854 } else
3855 CloseClipboard();
374330e2 3856 }
3857
3858 *p = NULL;
3859 *len = 0;
3860}
3861
4eeb7d09 3862#if 0
374330e2 3863/*
3864 * Move `lines' lines from position `from' to position `to' in the
3865 * window.
3866 */
32874aea 3867void optimised_move(int to, int from, int lines)
3868{
374330e2 3869 RECT r;
f67b4e85 3870 int min, max;
374330e2 3871
3872 min = (to < from ? to : from);
3873 max = to + from - min;
374330e2 3874
5a73255e 3875 r.left = offset_width;
3876 r.right = offset_width + cols * font_width;
3877 r.top = offset_height + min * font_height;
3878 r.bottom = offset_height + (max + lines) * font_height;
32874aea 3879 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
374330e2 3880}
4eeb7d09 3881#endif
374330e2 3882
3883/*
3884 * Print a message box and perform a fatal exit.
3885 */
32874aea 3886void fatalbox(char *fmt, ...)
3887{
374330e2 3888 va_list ap;
3889 char stuff[200];
3890
3891 va_start(ap, fmt);
3892 vsprintf(stuff, fmt, ap);
3893 va_end(ap);
3894 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3895 exit(1);
3896}
3897
3898/*
f8a28d1f 3899 * Manage window caption / taskbar flashing, if enabled.
3900 * 0 = stop, 1 = maintain, 2 = start
3901 */
3902static void flash_window(int mode)
3903{
3904 static long last_flash = 0;
3905 static int flashing = 0;
3906 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3907 /* stop */
3908 if (flashing) {
3909 FlashWindow(hwnd, FALSE);
3910 flashing = 0;
3911 }
3912
3913 } else if (mode == 2) {
3914 /* start */
3915 if (!flashing) {
3916 last_flash = GetTickCount();
3917 flashing = 1;
3918 FlashWindow(hwnd, TRUE);
3919 }
3920
3921 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3922 /* maintain */
3923 if (flashing) {
3924 long now = GetTickCount();
3925 long fdiff = now - last_flash;
3926 if (fdiff < 0 || fdiff > 450) {
3927 last_flash = now;
3928 FlashWindow(hwnd, TRUE); /* toggle */
3929 }
3930 }
3931 }
3932}
3933
3934/*
374330e2 3935 * Beep.
3936 */
32874aea 3937void beep(int mode)
3938{
03169ad0 3939 if (mode == BELL_DEFAULT) {
eb04402e 3940 /*
3941 * For MessageBeep style bells, we want to be careful of
3942 * timing, because they don't have the nice property of
3943 * PlaySound bells that each one cancels the previous
3944 * active one. So we limit the rate to one per 50ms or so.
3945 */
3946 static long lastbeep = 0;
d51cdf1e 3947 long beepdiff;
eb04402e 3948
d51cdf1e 3949 beepdiff = GetTickCount() - lastbeep;
eb04402e 3950 if (beepdiff >= 0 && beepdiff < 50)
3951 return;
156686ef 3952 MessageBeep(MB_OK);
d51cdf1e 3953 /*
3954 * The above MessageBeep call takes time, so we record the
3955 * time _after_ it finishes rather than before it starts.
3956 */
3957 lastbeep = GetTickCount();
03169ad0 3958 } else if (mode == BELL_WAVEFILE) {
3959 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
32874aea 3960 char buf[sizeof(cfg.bell_wavefile) + 80];
03169ad0 3961 sprintf(buf, "Unable to play sound file\n%s\n"
3962 "Using default sound instead", cfg.bell_wavefile);
32874aea 3963 MessageBox(hwnd, buf, "PuTTY Sound Error",
3964 MB_OK | MB_ICONEXCLAMATION);
03169ad0 3965 cfg.beep = BELL_DEFAULT;
3966 }
3967 }
f8a28d1f 3968 /* Otherwise, either visual bell or disabled; do nothing here */
3969 if (!has_focus) {
3970 flash_window(2); /* start */
3971 }
374330e2 3972}
8f57d753 3973
3974/*
3975 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3976 * implementation.
a401e5f3 3977 * Revised by <wez@thebrainroom.com>
8f57d753 3978 */
3979static void flip_full_screen(void)
3980{
6003660f 3981 WINDOWPLACEMENT wp;
3982 LONG style;
a401e5f3 3983
6003660f 3984 wp.length = sizeof(wp);
3985 GetWindowPlacement(hwnd, &wp);
a401e5f3 3986
6003660f 3987 full_screen = !full_screen;
a401e5f3 3988
3989 if (full_screen) {
6003660f 3990 if (wp.showCmd == SW_SHOWMAXIMIZED) {
3991 /* Ooops it was already 'zoomed' we have to unzoom it before
3992 * everything will work right.
3993 */
3994 wp.showCmd = SW_SHOWNORMAL;
3995 SetWindowPlacement(hwnd, &wp);
3996 }
3997
3998 style = GetWindowLong(hwnd, GWL_STYLE) & ~WS_CAPTION;
3999 style &= ~WS_VSCROLL;
4000 if (cfg.scrollbar_in_fullscreen)
4001 style |= WS_VSCROLL;
4002 SetWindowLong(hwnd, GWL_STYLE, style);
a401e5f3 4003
6003660f 4004 /* This seems to be needed otherwize explorer doesn't notice
4005 * we want to go fullscreen and it's bar is still visible
4006 */
4007 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4008 SWP_NOACTIVATE | SWP_NOCOPYBITS |
4009 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4010 SWP_FRAMECHANGED);
4011
4012 wp.showCmd = SW_SHOWMAXIMIZED;
4013 SetWindowPlacement(hwnd, &wp);
8f57d753 4014 } else {
6003660f 4015 style = GetWindowLong(hwnd, GWL_STYLE) | WS_CAPTION;
4016 style &= ~WS_VSCROLL;
4017 if (cfg.scrollbar)
4018 style |= WS_VSCROLL;
4019 SetWindowLong(hwnd, GWL_STYLE, style);
4020
4021 /* Don't need to do a SetWindowPos as the resize will force a
4022 * full redraw.
4023 */
4024 wp.showCmd = SW_SHOWNORMAL;
4025 SetWindowPlacement(hwnd, &wp);
8f57d753 4026 }
6003660f 4027
a401e5f3 4028 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4029 MF_BYCOMMAND| full_screen ? MF_CHECKED : MF_UNCHECKED);
8f57d753 4030}