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