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