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