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