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