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