Jordan Russell's patch (again): a couple of registry read operations
[u/mdw/putty] / window.c
CommitLineData
374330e2 1#include <windows.h>
49bad831 2#include <imm.h>
374330e2 3#include <commctrl.h>
a7419ea4 4#include <richedit.h>
f0a70059 5#include <mmsystem.h>
4d331a77 6#ifndef AUTO_WINSOCK
7#ifdef WINSOCK_TWO
8#include <winsock2.h>
9#else
374330e2 10#include <winsock.h>
4d331a77 11#endif
12#endif
a401e5f3 13
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
319bfe5a 1940 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON) &&
1941 GetCapture() == hwnd) {
374330e2 1942 Mouse_Button b;
1943 if (wParam & MK_LBUTTON)
6c6e7711 1944 b = MBT_LEFT;
374330e2 1945 else if (wParam & MK_MBUTTON)
6c6e7711 1946 b = MBT_MIDDLE;
374330e2 1947 else
6c6e7711 1948 b = MBT_RIGHT;
32874aea 1949 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1950 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
6908fed7 1951 wParam & MK_CONTROL, is_alt_pressed());
374330e2 1952 }
374330e2 1953 return 0;
d318ef8e 1954 case WM_NCMOUSEMOVE:
1955 show_mouseptr(1);
32874aea 1956 noise_ultralight(lParam);
d318ef8e 1957 return 0;
374330e2 1958 case WM_IGNORE_CLIP:
1959 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1960 break;
1961 case WM_DESTROYCLIPBOARD:
1962 if (!ignore_clip)
1963 term_deselect();
1964 ignore_clip = FALSE;
1965 return 0;
1966 case WM_PAINT:
1967 {
1968 PAINTSTRUCT p;
32874aea 1969 HideCaret(hwnd);
1970 hdc = BeginPaint(hwnd, &p);
374330e2 1971 if (pal) {
32874aea 1972 SelectPalette(hdc, pal, TRUE);
1973 RealizePalette(hdc);
374330e2 1974 }
5a73255e 1975 term_paint(hdc,
1976 (p.rcPaint.left-offset_width)/font_width,
1977 (p.rcPaint.top-offset_height)/font_height,
1978 (p.rcPaint.right-offset_width-1)/font_width,
1979 (p.rcPaint.bottom-offset_height-1)/font_height);
1980
1981 if (p.fErase ||
1982 p.rcPaint.left < offset_width ||
1983 p.rcPaint.top < offset_height ||
1984 p.rcPaint.right >= offset_width + font_width*cols ||
1985 p.rcPaint.bottom>= offset_height + font_height*rows)
1986 {
1987 HBRUSH fillcolour, oldbrush;
1988 HPEN edge, oldpen;
1989 fillcolour = CreateSolidBrush (
1990 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1991 oldbrush = SelectObject(hdc, fillcolour);
1992 edge = CreatePen(PS_SOLID, 0,
1993 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1994 oldpen = SelectObject(hdc, edge);
1995
1996 ExcludeClipRect(hdc,
1997 offset_width, offset_height,
1998 offset_width+font_width*cols,
1999 offset_height+font_height*rows);
2000
2001 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
2002 p.rcPaint.right, p.rcPaint.bottom);
2003
2004 // SelectClipRgn(hdc, NULL);
2005
2006 SelectObject(hdc, oldbrush);
2007 DeleteObject(fillcolour);
2008 SelectObject(hdc, oldpen);
2009 DeleteObject(edge);
2010 }
32874aea 2011 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
2012 SelectObject(hdc, GetStockObject(WHITE_PEN));
2013 EndPaint(hwnd, &p);
2014 ShowCaret(hwnd);
374330e2 2015 }
2016 return 0;
2017 case WM_NETEVENT:
59ad2c03 2018 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2019 * but the only one that's likely to try to overload us is FD_READ.
2020 * This means buffering just one is fine.
2021 */
2022 if (pending_netevent)
2023 enact_pending_netevent();
2024
2025 pending_netevent = TRUE;
32874aea 2026 pend_netevent_wParam = wParam;
2027 pend_netevent_lParam = lParam;
3ad9d396 2028 if (WSAGETSELECTEVENT(lParam) != FD_READ)
2029 enact_pending_netevent();
2030
ec55b220 2031 time(&last_movement);
374330e2 2032 return 0;
2033 case WM_SETFOCUS:
2034 has_focus = TRUE;
32874aea 2035 CreateCaret(hwnd, caretbm, font_width, font_height);
2036 ShowCaret(hwnd);
f8a28d1f 2037 flash_window(0); /* stop */
32874aea 2038 compose_state = 0;
374330e2 2039 term_out();
2040 term_update();
2041 break;
2042 case WM_KILLFOCUS:
32874aea 2043 show_mouseptr(1);
374330e2 2044 has_focus = FALSE;
32874aea 2045 DestroyCaret();
c1da5066 2046 caret_x = caret_y = -1; /* ensure caret is replaced next time */
374330e2 2047 term_out();
2048 term_update();
2049 break;
73251d5d 2050 case WM_ENTERSIZEMOVE:
5a73255e 2051#ifdef RDB_DEBUG_PATCH
2052 debug((27, "WM_ENTERSIZEMOVE"));
2053#endif
32874aea 2054 EnableSizeTip(1);
2055 resizing = TRUE;
3ad8c6db 2056 need_backend_resize = FALSE;
32874aea 2057 break;
73251d5d 2058 case WM_EXITSIZEMOVE:
32874aea 2059 EnableSizeTip(0);
2060 resizing = FALSE;
5a73255e 2061#ifdef RDB_DEBUG_PATCH
2062 debug((27, "WM_EXITSIZEMOVE"));
2063#endif
2064 if (need_backend_resize) {
2065 term_size(cfg.height, cfg.width, cfg.savelines);
2066 InvalidateRect(hwnd, NULL, TRUE);
2067 }
32874aea 2068 break;
374330e2 2069 case WM_SIZING:
5a73255e 2070 /*
2071 * This does two jobs:
2072 * 1) Keep the sizetip uptodate
2073 * 2) Make sure the window size is _stepped_ in units of the font size.
2074 */
0ed2f48e 2075 if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
374330e2 2076 int width, height, w, h, ew, eh;
32874aea 2077 LPRECT r = (LPRECT) lParam;
374330e2 2078
0ed2f48e 2079 if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
5a73255e 2080 (cfg.height != rows || cfg.width != cols )) {
2081 /*
0ed2f48e 2082 * Great! It seems that both the terminal size and the
2083 * font size have been changed and the user is now dragging.
2084 *
2085 * It will now be difficult to get back to the configured
2086 * font size!
2087 *
2088 * This would be easier but it seems to be too confusing.
2089
5a73255e 2090 term_size(cfg.height, cfg.width, cfg.savelines);
2091 reset_window(2);
0ed2f48e 2092 */
2093 cfg.height=rows; cfg.width=cols;
2094
5a73255e 2095 InvalidateRect(hwnd, NULL, TRUE);
2096 need_backend_resize = TRUE;
2097 }
2098
374330e2 2099 width = r->right - r->left - extra_width;
2100 height = r->bottom - r->top - extra_height;
32874aea 2101 w = (width + font_width / 2) / font_width;
2102 if (w < 1)
2103 w = 1;
2104 h = (height + font_height / 2) / font_height;
2105 if (h < 1)
2106 h = 1;
2107 UpdateSizeTip(hwnd, w, h);
374330e2 2108 ew = width - w * font_width;
2109 eh = height - h * font_height;
2110 if (ew != 0) {
2111 if (wParam == WMSZ_LEFT ||
32874aea 2112 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
374330e2 2113 r->left += ew;
2114 else
2115 r->right -= ew;
2116 }
2117 if (eh != 0) {
2118 if (wParam == WMSZ_TOP ||
32874aea 2119 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
374330e2 2120 r->top += eh;
2121 else
2122 r->bottom -= eh;
2123 }
2124 if (ew || eh)
2125 return 1;
2126 else
2127 return 0;
5a73255e 2128 } else {
2129 int width, height, w, h, rv = 0;
2130 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2131 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2132 LPRECT r = (LPRECT) lParam;
2133
2134 width = r->right - r->left - ex_width;
2135 height = r->bottom - r->top - ex_height;
2136
2137 w = (width + cols/2)/cols;
2138 h = (height + rows/2)/rows;
2139 if ( r->right != r->left + w*cols + ex_width)
2140 rv = 1;
2141
2142 if (wParam == WMSZ_LEFT ||
2143 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2144 r->left = r->right - w*cols - ex_width;
2145 else
2146 r->right = r->left + w*cols + ex_width;
2147
2148 if (r->bottom != r->top + h*rows + ex_height)
2149 rv = 1;
2150
2151 if (wParam == WMSZ_TOP ||
2152 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2153 r->top = r->bottom - h*rows - ex_height;
2154 else
2155 r->bottom = r->top + h*rows + ex_height;
2156
2157 return rv;
374330e2 2158 }
32874aea 2159 /* break; (never reached) */
1ba99d2c 2160 case WM_FULLSCR_ON_MAX:
2161 fullscr_on_max = TRUE;
2162 break;
c1da5066 2163 case WM_MOVE:
2164 sys_cursor_update();
2165 break;
374330e2 2166 case WM_SIZE:
5a73255e 2167#ifdef RDB_DEBUG_PATCH
2168 debug((27, "WM_SIZE %s (%d,%d)",
2169 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2170 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2171 (wParam == SIZE_RESTORED && resizing) ? "to":
2172 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2173 "...",
2174 LOWORD(lParam), HIWORD(lParam)));
2175#endif
0ed2f48e 2176 if (wParam == SIZE_MINIMIZED)
32874aea 2177 SetWindowText(hwnd,
2178 cfg.win_name_always ? window_name : icon_name);
374330e2 2179 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
32874aea 2180 SetWindowText(hwnd, window_name);
811f10b3 2181 if (wParam == SIZE_RESTORED)
2182 clear_full_screen();
2183 if (wParam == SIZE_MAXIMIZED && fullscr_on_max) {
2184 make_full_screen();
2185 fullscr_on_max = FALSE;
2186 }
5a73255e 2187
ad5c93cc 2188 if (cfg.resize_action == RESIZE_DISABLED) {
5a73255e 2189 /* A resize, well it better be a minimize. */
2190 reset_window(-1);
2191 } else {
2192
1a6f78fe 2193 int width, height, w, h;
374330e2 2194
2195 width = LOWORD(lParam);
2196 height = HIWORD(lParam);
5a73255e 2197
2198 if (!resizing) {
0ed2f48e 2199 if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
5a73255e 2200 was_zoomed = 1;
2201 prev_rows = rows;
2202 prev_cols = cols;
0ed2f48e 2203 if (cfg.resize_action == RESIZE_TERM) {
5a73255e 2204 w = width / font_width;
2205 if (w < 1) w = 1;
2206 h = height / font_height;
2207 if (h < 1) h = 1;
2208
2209 term_size(h, w, cfg.savelines);
2210 }
2211 reset_window(0);
2212 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2213 was_zoomed = 0;
0ed2f48e 2214 if (cfg.resize_action == RESIZE_TERM)
5a73255e 2215 term_size(prev_rows, prev_cols, cfg.savelines);
0ed2f48e 2216 if (cfg.resize_action != RESIZE_FONT)
2217 reset_window(2);
2218 else
2219 reset_window(0);
5a73255e 2220 }
2221 /* This is an unexpected resize, these will normally happen
2222 * if the window is too large. Probably either the user
2223 * selected a huge font or the screen size has changed.
2224 *
2225 * This is also called with minimize.
32874aea 2226 */
5a73255e 2227 else reset_window(-1);
2228 }
2229
2230 /*
2231 * Don't call back->size in mid-resize. (To prevent
2232 * massive numbers of resize events getting sent
2233 * down the connection during an NT opaque drag.)
2234 */
2235 if (resizing) {
0ed2f48e 2236 if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
3ad8c6db 2237 need_backend_resize = TRUE;
5a73255e 2238 w = (width-cfg.window_border*2) / font_width;
2239 if (w < 1) w = 1;
2240 h = (height-cfg.window_border*2) / font_height;
2241 if (h < 1) h = 1;
2242
e44d78b6 2243 cfg.height = h;
2244 cfg.width = w;
5a73255e 2245 } else
2246 reset_window(0);
374330e2 2247 }
2248 }
c1da5066 2249 sys_cursor_update();
374330e2 2250 return 0;
2251 case WM_VSCROLL:
2252 switch (LOWORD(wParam)) {
32874aea 2253 case SB_BOTTOM:
2254 term_scroll(-1, 0);
2255 break;
2256 case SB_TOP:
2257 term_scroll(+1, 0);
2258 break;
2259 case SB_LINEDOWN:
2260 term_scroll(0, +1);
2261 break;
2262 case SB_LINEUP:
2263 term_scroll(0, -1);
2264 break;
2265 case SB_PAGEDOWN:
2266 term_scroll(0, +rows / 2);
2267 break;
2268 case SB_PAGEUP:
2269 term_scroll(0, -rows / 2);
2270 break;
2271 case SB_THUMBPOSITION:
2272 case SB_THUMBTRACK:
2273 term_scroll(1, HIWORD(wParam));
2274 break;
2275 }
2276 break;
2277 case WM_PALETTECHANGED:
374330e2 2278 if ((HWND) wParam != hwnd && pal != NULL) {
2279 HDC hdc = get_ctx();
2280 if (hdc) {
32874aea 2281 if (RealizePalette(hdc) > 0)
2282 UpdateColors(hdc);
2283 free_ctx(hdc);
374330e2 2284 }
2285 }
2286 break;
2287 case WM_QUERYNEWPALETTE:
2288 if (pal != NULL) {
2289 HDC hdc = get_ctx();
2290 if (hdc) {
32874aea 2291 if (RealizePalette(hdc) > 0)
2292 UpdateColors(hdc);
2293 free_ctx(hdc);
374330e2 2294 return TRUE;
2295 }
2296 }
2297 return FALSE;
2298 case WM_KEYDOWN:
2299 case WM_SYSKEYDOWN:
c9def1b8 2300 case WM_KEYUP:
2301 case WM_SYSKEYUP:
374330e2 2302 /*
2303 * Add the scan code and keypress timing to the random
7d6ee6ff 2304 * number noise.
374330e2 2305 */
32874aea 2306 noise_ultralight(lParam);
374330e2 2307
2308 /*
2309 * We don't do TranslateMessage since it disassociates the
2310 * resulting CHAR message from the KEYDOWN that sparked it,
2311 * which we occasionally don't want. Instead, we process
2312 * KEYDOWN, and call the Win32 translator functions so that
2313 * we get the translations under _our_ control.
2314 */
2315 {
2316 unsigned char buf[20];
2317 int len;
2318
32874aea 2319 if (wParam == VK_PROCESSKEY) {
3cf144db 2320 MSG m;
32874aea 2321 m.hwnd = hwnd;
2322 m.message = WM_KEYDOWN;
2323 m.wParam = wParam;
2324 m.lParam = lParam & 0xdfff;
2325 TranslateMessage(&m);
2326 } else {
2327 len = TranslateKey(message, wParam, lParam, buf);
3cf144db 2328 if (len == -1)
32874aea 2329 return DefWindowProc(hwnd, message, wParam, lParam);
5471d09a 2330
b2a1eade 2331 if (len != 0) {
bca9517a 2332 /*
256cb87c 2333 * Interrupt an ongoing paste. I'm not sure
2334 * this is sensible, but for the moment it's
2335 * preferable to having to faff about buffering
2336 * things.
2337 */
2338 term_nopaste();
2339
2340 /*
bca9517a 2341 * We need not bother about stdin backlogs
2342 * here, because in GUI PuTTY we can't do
2343 * anything about it anyway; there's no means
2344 * of asking Windows to hold off on KEYDOWN
2345 * messages. We _have_ to buffer everything
2346 * we're sent.
2347 */
760e88b2 2348 ldisc_send(buf, len, 1);
32874aea 2349 show_mouseptr(0);
bca9517a 2350 }
3cf144db 2351 }
374330e2 2352 }
7732d38a 2353 net_pending_errors();
374330e2 2354 return 0;
4eeb7d09 2355 case WM_INPUTLANGCHANGE:
a5ce2e9f 2356 /* wParam == Font number */
2357 /* lParam == Locale */
2358 set_input_locale((HKL)lParam);
c1da5066 2359 sys_cursor_update();
4eeb7d09 2360 break;
71f6951d 2361 case WM_IME_NOTIFY:
2362 if(wParam == IMN_SETOPENSTATUS) {
2363 HIMC hImc = ImmGetContext(hwnd);
2364 ImmSetCompositionFont(hImc, &lfont);
2365 ImmReleaseContext(hwnd, hImc);
2366 return 0;
2367 }
2368 break;
88485e4d 2369 case WM_IME_COMPOSITION:
2370 {
2371 HIMC hIMC;
2372 int n;
2373 char *buff;
a3cfd0c8 2374
88485e4d 2375 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2376 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2377
2378 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2379 break; /* fall back to DefWindowProc */
2380
2381 hIMC = ImmGetContext(hwnd);
2382 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2383
2384 if (n > 0) {
a3cfd0c8 2385 int i;
88485e4d 2386 buff = (char*) smalloc(n);
2387 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
a3cfd0c8 2388 /*
2389 * Jaeyoun Chung reports that Korean character
2390 * input doesn't work correctly if we do a single
2391 * luni_send() covering the whole of buff. So
2392 * instead we luni_send the characters one by one.
2393 */
2394 for (i = 0; i < n; i += 2)
2395 luni_send((unsigned short *)(buff+i), 1, 1);
88485e4d 2396 free(buff);
2397 }
2398 ImmReleaseContext(hwnd, hIMC);
2399 return 1;
2400 }
2401
4eeb7d09 2402 case WM_IME_CHAR:
2403 if (wParam & 0xFF00) {
3cf144db 2404 unsigned char buf[2];
2405
2406 buf[1] = wParam;
2407 buf[0] = wParam >> 8;
760e88b2 2408 lpage_send(kbd_codepage, buf, 2, 1);
4eeb7d09 2409 } else {
2410 char c = (unsigned char) wParam;
760e88b2 2411 lpage_send(kbd_codepage, &c, 1, 1);
3cf144db 2412 }
4eeb7d09 2413 return (0);
374330e2 2414 case WM_CHAR:
2415 case WM_SYSCHAR:
2416 /*
2417 * Nevertheless, we are prepared to deal with WM_CHAR
2418 * messages, should they crop up. So if someone wants to
2419 * post the things to us as part of a macro manoeuvre,
2420 * we're ready to cope.
2421 */
32874aea 2422 {
4eeb7d09 2423 char c = (unsigned char)wParam;
760e88b2 2424 lpage_send(CP_ACP, &c, 1, 1);
374330e2 2425 }
2426 return 0;
32874aea 2427 case WM_SETCURSOR:
ca3ab3ee 2428 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
32874aea 2429 SetCursor(LoadCursor(NULL, IDC_ARROW));
2430 return TRUE;
2431 }
011b0e33 2432 default:
2433 if (message == wm_mousewheel) {
2434 int shift_pressed=0, control_pressed=0, alt_pressed=0;
2435
2436 if (message == WM_MOUSEWHEEL) {
2437 wheel_accumulator += (short)HIWORD(wParam);
2438 shift_pressed=LOWORD(wParam) & MK_SHIFT;
2439 control_pressed=LOWORD(wParam) & MK_CONTROL;
2440 } else {
2441 BYTE keys[256];
2442 wheel_accumulator += (int)wParam;
2443 if (GetKeyboardState(keys)!=0) {
2444 shift_pressed=keys[VK_SHIFT]&0x80;
2445 control_pressed=keys[VK_CONTROL]&0x80;
2446 }
2447 }
2448
2449 /* process events when the threshold is reached */
2450 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
2451 int b;
2452
2453 /* reduce amount for next time */
2454 if (wheel_accumulator > 0) {
2455 b = MBT_WHEEL_UP;
2456 wheel_accumulator -= WHEEL_DELTA;
2457 } else if (wheel_accumulator < 0) {
2458 b = MBT_WHEEL_DOWN;
2459 wheel_accumulator += WHEEL_DELTA;
2460 } else
2461 break;
2462
2463 if (send_raw_mouse &&
2464 !(cfg.mouse_override && shift_pressed)) {
2465 /* send a mouse-down followed by a mouse up */
2466 term_mouse(b,
2467 MA_CLICK,
2468 TO_CHR_X(X_POS(lParam)),
2469 TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2470 control_pressed, is_alt_pressed());
2471 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
2472 TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2473 control_pressed, is_alt_pressed());
2474 } else {
2475 /* trigger a scroll */
2476 term_scroll(0,
2477 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
2478 }
2479 }
2480 return 0;
2481 }
374330e2 2482 }
2483
32874aea 2484 return DefWindowProc(hwnd, message, wParam, lParam);
374330e2 2485}
2486
2487/*
ec8679e9 2488 * Move the system caret. (We maintain one, even though it's
2489 * invisible, for the benefit of blind people: apparently some
2490 * helper software tracks the system caret, so we should arrange to
2491 * have one.)
2492 */
32874aea 2493void sys_cursor(int x, int y)
2494{
c1da5066 2495 int cx, cy;
2496
2497 if (!has_focus) return;
2498
2499 /*
2500 * Avoid gratuitously re-updating the cursor position and IMM
2501 * window if there's no actual change required.
2502 */
2503 cx = x * font_width + offset_width;
2504 cy = y * font_height + offset_height;
2505 if (cx == caret_x && cy == caret_y)
2506 return;
2507 caret_x = cx;
2508 caret_y = cy;
2509
2510 sys_cursor_update();
2511}
2512
2513static void sys_cursor_update(void)
2514{
88485e4d 2515 COMPOSITIONFORM cf;
2516 HIMC hIMC;
2517
2518 if (!has_focus) return;
c1da5066 2519
2520 if (caret_x < 0 || caret_y < 0)
2521 return;
2522
2523 SetCaretPos(caret_x, caret_y);
88485e4d 2524
2525 /* IMM calls on Win98 and beyond only */
2526 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2527
2528 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2529 osVersion.dwMinorVersion == 0) return; /* 95 */
2530
2531 /* we should have the IMM functions */
2532 hIMC = ImmGetContext(hwnd);
2533 cf.dwStyle = CFS_POINT;
c1da5066 2534 cf.ptCurrentPos.x = caret_x;
2535 cf.ptCurrentPos.y = caret_y;
88485e4d 2536 ImmSetCompositionWindow(hIMC, &cf);
2537
2538 ImmReleaseContext(hwnd, hIMC);
ec8679e9 2539}
2540
2541/*
374330e2 2542 * Draw a line of text in the window, at given character
2543 * coordinates, in given attributes.
2544 *
2545 * We are allowed to fiddle with the contents of `text'.
2546 */
32874aea 2547void do_text(Context ctx, int x, int y, char *text, int len,
2548 unsigned long attr, int lattr)
2549{
374330e2 2550 COLORREF fg, bg, t;
2551 int nfg, nbg, nfont;
2552 HDC hdc = ctx;
59ad2c03 2553 RECT line_box;
2554 int force_manual_underline = 0;
32874aea 2555 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
4eeb7d09 2556 int char_width = fnt_width;
2557 int text_adjust = 0;
2558 static int *IpDx = 0, IpDxLEN = 0;
2559
2560 if (attr & ATTR_WIDE)
2561 char_width *= 2;
59ad2c03 2562
4eeb7d09 2563 if (len > IpDxLEN || IpDx[0] != char_width) {
59ad2c03 2564 int i;
32874aea 2565 if (len > IpDxLEN) {
59ad2c03 2566 sfree(IpDx);
32874aea 2567 IpDx = smalloc((len + 16) * sizeof(int));
2568 IpDxLEN = (len + 16);
59ad2c03 2569 }
32874aea 2570 for (i = 0; i < IpDxLEN; i++)
4eeb7d09 2571 IpDx[i] = char_width;
59ad2c03 2572 }
374330e2 2573
5a73255e 2574 /* Only want the left half of double width lines */
2575 if (lattr != LATTR_NORM && x*2 >= cols)
2576 return;
2577
c9def1b8 2578 x *= fnt_width;
374330e2 2579 y *= font_height;
5a73255e 2580 x += offset_width;
2581 y += offset_height;
374330e2 2582
4eeb7d09 2583 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2584 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
374330e2 2585 attr ^= ATTR_CUR_XOR;
2586 }
2587
2588 nfont = 0;
4eeb7d09 2589 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2590 /* Assume a poorman font is borken in other ways too. */
2591 lattr = LATTR_WIDE;
2592 } else
2593 switch (lattr) {
2594 case LATTR_NORM:
2595 break;
2596 case LATTR_WIDE:
2597 nfont |= FONT_WIDE;
374330e2 2598 break;
4eeb7d09 2599 default:
2600 nfont |= FONT_WIDE + FONT_HIGH;
2601 break;
2602 }
5a73255e 2603 if (attr & ATTR_NARROW)
2604 nfont |= FONT_NARROW;
4eeb7d09 2605
2606 /* Special hack for the VT100 linedraw glyphs. */
2607 if ((attr & CSET_MASK) == 0x2300) {
5a73255e 2608 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
4eeb7d09 2609 switch ((unsigned char) (text[0])) {
2610 case 0xBA:
2611 text_adjust = -2 * font_height / 5;
2612 break;
2613 case 0xBB:
2614 text_adjust = -1 * font_height / 5;
2615 break;
2616 case 0xBC:
2617 text_adjust = font_height / 5;
2618 break;
2619 case 0xBD:
2620 text_adjust = 2 * font_height / 5;
32874aea 2621 break;
59ad2c03 2622 }
4eeb7d09 2623 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2624 text_adjust *= 2;
2625 attr &= ~CSET_MASK;
2626 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2627 attr |= (unitab_xterm['q'] & CSET_MASK);
2628 if (attr & ATTR_UNDER) {
2629 attr &= ~ATTR_UNDER;
2630 force_manual_underline = 1;
2631 }
374330e2 2632 }
2633 }
2634
4eeb7d09 2635 /* Anything left as an original character set is unprintable. */
2636 if (DIRECT_CHAR(attr)) {
2637 attr &= ~CSET_MASK;
2638 attr |= 0xFF00;
5a73255e 2639 memset(text, 0xFD, len);
4eeb7d09 2640 }
2641
2642 /* OEM CP */
2643 if ((attr & CSET_MASK) == ATTR_OEMCP)
2644 nfont |= FONT_OEM;
2645
374330e2 2646 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2647 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2648 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2649 nfont |= FONT_BOLD;
2650 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2651 nfont |= FONT_UNDERLINE;
4eeb7d09 2652 another_font(nfont);
32874aea 2653 if (!fonts[nfont]) {
2654 if (nfont & FONT_UNDERLINE)
59ad2c03 2655 force_manual_underline = 1;
2656 /* Don't do the same for manual bold, it could be bad news. */
2657
32874aea 2658 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
59ad2c03 2659 }
4eeb7d09 2660 another_font(nfont);
2661 if (!fonts[nfont])
2662 nfont = FONT_NORMAL;
374330e2 2663 if (attr & ATTR_REVERSE) {
32874aea 2664 t = nfg;
2665 nfg = nbg;
2666 nbg = t;
374330e2 2667 }
2668 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2669 nfg++;
59ad2c03 2670 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2671 nbg++;
374330e2 2672 fg = colours[nfg];
2673 bg = colours[nbg];
32874aea 2674 SelectObject(hdc, fonts[nfont]);
2675 SetTextColor(hdc, fg);
2676 SetBkColor(hdc, bg);
2677 SetBkMode(hdc, OPAQUE);
2678 line_box.left = x;
2679 line_box.top = y;
4eeb7d09 2680 line_box.right = x + char_width * len;
32874aea 2681 line_box.bottom = y + font_height;
4eeb7d09 2682
5a73255e 2683 /* Only want the left half of double width lines */
2684 if (line_box.right > font_width*cols+offset_width)
2685 line_box.right = font_width*cols+offset_width;
2686
4eeb7d09 2687 /* We're using a private area for direct to font. (512 chars.) */
2688 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2689 /* Ho Hum, dbcs fonts are a PITA! */
2690 /* To display on W9x I have to convert to UCS */
2691 static wchar_t *uni_buf = 0;
2692 static int uni_len = 0;
5a73255e 2693 int nlen, mptr;
4eeb7d09 2694 if (len > uni_len) {
2695 sfree(uni_buf);
2696 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2697 }
4eeb7d09 2698
5a73255e 2699 for(nlen = mptr = 0; mptr<len; mptr++) {
2700 uni_buf[nlen] = 0xFFFD;
2701 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2702 IpDx[nlen] += char_width;
2703 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2704 text+mptr, 2, uni_buf+nlen, 1);
2705 mptr++;
2706 }
2707 else
2708 {
2709 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2710 text+mptr, 1, uni_buf+nlen, 1);
2711 }
2712 nlen++;
2713 }
4eeb7d09 2714 if (nlen <= 0)
2715 return; /* Eeek! */
2716
2717 ExtTextOutW(hdc, x,
2718 y - font_height * (lattr == LATTR_BOT) + text_adjust,
5a73255e 2719 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 2720 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2721 SetBkMode(hdc, TRANSPARENT);
2722 ExtTextOutW(hdc, x - 1,
2723 y - font_height * (lattr ==
2724 LATTR_BOT) + text_adjust,
5a73255e 2725 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 2726 }
5a73255e 2727
2728 IpDx[0] = -1;
4eeb7d09 2729 } else if (DIRECT_FONT(attr)) {
2730 ExtTextOut(hdc, x,
2731 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2732 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2733 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2734 SetBkMode(hdc, TRANSPARENT);
2735
2736 /* GRR: This draws the character outside it's box and can leave
2737 * 'droppings' even with the clip box! I suppose I could loop it
2738 * one character at a time ... yuk.
2739 *
2740 * Or ... I could do a test print with "W", and use +1 or -1 for this
2741 * shift depending on if the leftmost column is blank...
2742 */
2743 ExtTextOut(hdc, x - 1,
2744 y - font_height * (lattr ==
2745 LATTR_BOT) + text_adjust,
2746 ETO_CLIPPED, &line_box, text, len, IpDx);
2747 }
2748 } else {
2749 /* And 'normal' unicode characters */
2750 static WCHAR *wbuf = NULL;
2751 static int wlen = 0;
2752 int i;
2753 if (wlen < len) {
2754 sfree(wbuf);
2755 wlen = len;
2756 wbuf = smalloc(wlen * sizeof(WCHAR));
2757 }
2758 for (i = 0; i < len; i++)
2759 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2760
2761 ExtTextOutW(hdc, x,
2762 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2763 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2764
2765 /* And the shadow bold hack. */
5a73255e 2766 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
4eeb7d09 2767 SetBkMode(hdc, TRANSPARENT);
2768 ExtTextOutW(hdc, x - 1,
2769 y - font_height * (lattr ==
2770 LATTR_BOT) + text_adjust,
2771 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2772 }
374330e2 2773 }
4eeb7d09 2774 if (lattr != LATTR_TOP && (force_manual_underline ||
2775 (und_mode == UND_LINE
2776 && (attr & ATTR_UNDER)))) {
32874aea 2777 HPEN oldpen;
4eeb7d09 2778 int dec = descent;
2779 if (lattr == LATTR_BOT)
2780 dec = dec * 2 - font_height;
2781
32874aea 2782 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
4eeb7d09 2783 MoveToEx(hdc, x, y + dec, NULL);
2784 LineTo(hdc, x + len * char_width, y + dec);
32874aea 2785 oldpen = SelectObject(hdc, oldpen);
2786 DeleteObject(oldpen);
374330e2 2787 }
4eeb7d09 2788}
2789
2790void do_cursor(Context ctx, int x, int y, char *text, int len,
2791 unsigned long attr, int lattr)
2792{
2793
2794 int fnt_width;
2795 int char_width;
2796 HDC hdc = ctx;
2797 int ctype = cfg.cursor_type;
2798
2799 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2800 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2801 do_text(ctx, x, y, text, len, attr, lattr);
2802 return;
2803 }
2804 ctype = 2;
2805 attr |= TATTR_RIGHTCURS;
2806 }
2807
2808 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2809 if (attr & ATTR_WIDE)
2810 char_width *= 2;
2811 x *= fnt_width;
2812 y *= font_height;
5a73255e 2813 x += offset_width;
2814 y += offset_height;
4eeb7d09 2815
2816 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
374330e2 2817 POINT pts[5];
32874aea 2818 HPEN oldpen;
374330e2 2819 pts[0].x = pts[1].x = pts[4].x = x;
4eeb7d09 2820 pts[2].x = pts[3].x = x + char_width - 1;
374330e2 2821 pts[0].y = pts[3].y = pts[4].y = y;
32874aea 2822 pts[1].y = pts[2].y = y + font_height - 1;
2823 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2824 Polyline(hdc, pts, 5);
2825 oldpen = SelectObject(hdc, oldpen);
2826 DeleteObject(oldpen);
4eeb7d09 2827 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
32874aea 2828 int startx, starty, dx, dy, length, i;
4eeb7d09 2829 if (ctype == 1) {
32874aea 2830 startx = x;
2831 starty = y + descent;
2832 dx = 1;
2833 dy = 0;
4eeb7d09 2834 length = char_width;
32874aea 2835 } else {
4e30ff69 2836 int xadjust = 0;
4eeb7d09 2837 if (attr & TATTR_RIGHTCURS)
2838 xadjust = char_width - 1;
32874aea 2839 startx = x + xadjust;
2840 starty = y;
2841 dx = 0;
2842 dy = 1;
2843 length = font_height;
2844 }
4eeb7d09 2845 if (attr & TATTR_ACTCURS) {
32874aea 2846 HPEN oldpen;
2847 oldpen =
2848 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2849 MoveToEx(hdc, startx, starty, NULL);
2850 LineTo(hdc, startx + dx * length, starty + dy * length);
2851 oldpen = SelectObject(hdc, oldpen);
2852 DeleteObject(oldpen);
2853 } else {
2854 for (i = 0; i < length; i++) {
2855 if (i % 2 == 0) {
2856 SetPixel(hdc, startx, starty, colours[23]);
2857 }
2858 startx += dx;
2859 starty += dy;
2860 }
2861 }
4e30ff69 2862 }
374330e2 2863}
2864
5a73255e 2865/* This function gets the actual width of a character in the normal font.
2866 */
2867int CharWidth(Context ctx, int uc) {
2868 HDC hdc = ctx;
2869 int ibuf = 0;
2870
2871 /* If the font max is the same as the font ave width then this
2872 * function is a no-op.
2873 */
2874 if (!font_dualwidth) return 1;
2875
2876 switch (uc & CSET_MASK) {
2877 case ATTR_ASCII:
2878 uc = unitab_line[uc & 0xFF];
2879 break;
2880 case ATTR_LINEDRW:
2881 uc = unitab_xterm[uc & 0xFF];
2882 break;
2883 case ATTR_SCOACS:
2884 uc = unitab_scoacs[uc & 0xFF];
2885 break;
2886 }
2887 if (DIRECT_FONT(uc)) {
2888 if (dbcs_screenfont) return 1;
2889
2890 /* Speedup, I know of no font where ascii is the wrong width */
2891 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2892 return 1;
2893
2894 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2895 SelectObject(hdc, fonts[FONT_NORMAL]);
2896 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2897 another_font(FONT_OEM);
2898 if (!fonts[FONT_OEM]) return 0;
2899
2900 SelectObject(hdc, fonts[FONT_OEM]);
2901 } else
2902 return 0;
2903
2904 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2905 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2906 return 0;
2907 } else {
2908 /* Speedup, I know of no font where ascii is the wrong width */
2909 if (uc >= ' ' && uc <= '~') return 1;
2910
2911 SelectObject(hdc, fonts[FONT_NORMAL]);
2912 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2913 /* Okay that one worked */ ;
2914 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2915 /* This should work on 9x too, but it's "less accurate" */ ;
2916 else
2917 return 0;
2918 }
2919
2920 ibuf += font_width / 2 -1;
2921 ibuf /= font_width;
2922
2923 return ibuf;
2924}
2925
374330e2 2926/*
c9def1b8 2927 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2928 * codes. Returns number of bytes used or zero to drop the message
2929 * or -1 to forward the message to windows.
374330e2 2930 */
3cf144db 2931static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
32874aea 2932 unsigned char *output)
2933{
374330e2 2934 BYTE keystate[256];
32874aea 2935 int scan, left_alt = 0, key_down, shift_state;
2936 int r, i, code;
2937 unsigned char *p = output;
4eeb7d09 2938 static int alt_sum = 0;
374330e2 2939
00e3ba0f 2940 HKL kbd_layout = GetKeyboardLayout(0);
2941
0c50ef57 2942 static WORD keys[3];
0c50ef57 2943 static int compose_char = 0;
2944 static WPARAM compose_key = 0;
32874aea 2945
c9def1b8 2946 r = GetKeyboardState(keystate);
32874aea 2947 if (!r)
2948 memset(keystate, 0, sizeof(keystate));
2949 else {
ec55b220 2950#if 0
4eeb7d09 2951#define SHOW_TOASCII_RESULT
32874aea 2952 { /* Tell us all about key events */
2953 static BYTE oldstate[256];
2954 static int first = 1;
2955 static int scan;
2956 int ch;
2957 if (first)
2958 memcpy(oldstate, keystate, sizeof(oldstate));
2959 first = 0;
2960
2961 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2962 debug(("+"));
2963 } else if ((HIWORD(lParam) & KF_UP)
2964 && scan == (HIWORD(lParam) & 0xFF)) {
2965 debug((". U"));
2966 } else {
2967 debug((".\n"));
2968 if (wParam >= VK_F1 && wParam <= VK_F20)
2969 debug(("K_F%d", wParam + 1 - VK_F1));
2970 else
2971 switch (wParam) {
2972 case VK_SHIFT:
2973 debug(("SHIFT"));
2974 break;
2975 case VK_CONTROL:
2976 debug(("CTRL"));
2977 break;
2978 case VK_MENU:
2979 debug(("ALT"));
2980 break;
2981 default:
2982 debug(("VK_%02x", wParam));
2983 }
2984 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2985 debug(("*"));
2986 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2987
2988 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2989 if (ch >= ' ' && ch <= '~')
2990 debug((", '%c'", ch));
2991 else if (ch)
2992 debug((", $%02x", ch));
2993
2994 if (keys[0])
2995 debug((", KB0=%02x", keys[0]));
2996 if (keys[1])
2997 debug((", KB1=%02x", keys[1]));
2998 if (keys[2])
2999 debug((", KB2=%02x", keys[2]));
3000
3001 if ((keystate[VK_SHIFT] & 0x80) != 0)
3002 debug((", S"));
3003 if ((keystate[VK_CONTROL] & 0x80) != 0)
3004 debug((", C"));
3005 if ((HIWORD(lParam) & KF_EXTENDED))
3006 debug((", E"));
3007 if ((HIWORD(lParam) & KF_UP))
3008 debug((", U"));
3009 }
3010
3011 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
3012 else if ((HIWORD(lParam) & KF_UP))
3013 oldstate[wParam & 0xFF] ^= 0x80;
3014 else
3015 oldstate[wParam & 0xFF] ^= 0x81;
3016
3017 for (ch = 0; ch < 256; ch++)
3018 if (oldstate[ch] != keystate[ch])
3019 debug((", M%02x=%02x", ch, keystate[ch]));
3020
3021 memcpy(oldstate, keystate, sizeof(oldstate));
3022 }
ec55b220 3023#endif
3024
32874aea 3025 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2cba1186 3026 keystate[VK_RMENU] = keystate[VK_MENU];
3027 }
3028
c9def1b8 3029
3030 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
32874aea 3031 if ((cfg.funky_type == 3 ||
3032 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
3033 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
c9def1b8 3034
3035 wParam = VK_EXECUTE;
3036
3037 /* UnToggle NUMLock */
32874aea 3038 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
3039 keystate[VK_NUMLOCK] ^= 1;
c9def1b8 3040 }
3041
3042 /* And write back the 'adjusted' state */
32874aea 3043 SetKeyboardState(keystate);
c9def1b8 3044 }
3045
3046 /* Disable Auto repeat if required */
32874aea 3047 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
3048 return 0;
c9def1b8 3049
32874aea 3050 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
c9def1b8 3051 left_alt = 1;
3052
32874aea 3053 key_down = ((HIWORD(lParam) & KF_UP) == 0);
c9def1b8 3054
95bbe1ae 3055 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
32874aea 3056 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
95bbe1ae 3057 if (cfg.ctrlaltkeys)
3058 keystate[VK_MENU] = 0;
3059 else {
3060 keystate[VK_RMENU] = 0x80;
3061 left_alt = 0;
3062 }
3063 }
c9def1b8 3064
5a73255e 3065 alt_pressed = (left_alt && key_down);
3066
c9def1b8 3067 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
32874aea 3068 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
3069 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
374330e2 3070
95bbe1ae 3071 /* Note if AltGr was pressed and if it was used as a compose key */
3072 if (!compose_state) {
159eba53 3073 compose_key = 0x100;
95bbe1ae 3074 if (cfg.compose_key) {
32874aea 3075 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
95bbe1ae 3076 compose_key = wParam;
3077 }
3078 if (wParam == VK_APPS)
3079 compose_key = wParam;
3080 }
3081
32874aea 3082 if (wParam == compose_key) {
3083 if (compose_state == 0
3084 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
3085 1;
3086 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
95bbe1ae 3087 compose_state = 2;
3088 else
3089 compose_state = 0;
32874aea 3090 } else if (compose_state == 1 && wParam != VK_CONTROL)
95bbe1ae 3091 compose_state = 0;
3092
67c339f7 3093 /*
cabfd08c 3094 * Record that we pressed key so the scroll window can be reset, but
3095 * be careful to avoid Shift-UP/Down
3096 */
a910b704 3097 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
3098 wParam != VK_MENU && wParam != VK_CONTROL) {
32874aea 3099 seen_key_event = 1;
cabfd08c 3100 }
3101
32874aea 3102 if (compose_state > 1 && left_alt)
3103 compose_state = 0;
67c339f7 3104
c9def1b8 3105 /* Sanitize the number pad if not using a PC NumPad */
32874aea 3106 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
3107 && cfg.funky_type != 2)
3108 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
3109 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
c9def1b8 3110 int nParam = 0;
32874aea 3111 switch (wParam) {
3112 case VK_INSERT:
3113 nParam = VK_NUMPAD0;
3114 break;
3115 case VK_END:
3116 nParam = VK_NUMPAD1;
3117 break;
3118 case VK_DOWN:
3119 nParam = VK_NUMPAD2;
3120 break;
3121 case VK_NEXT:
3122 nParam = VK_NUMPAD3;
3123 break;
3124 case VK_LEFT:
3125 nParam = VK_NUMPAD4;
3126 break;
3127 case VK_CLEAR:
3128 nParam = VK_NUMPAD5;
3129 break;
3130 case VK_RIGHT:
3131 nParam = VK_NUMPAD6;
3132 break;
3133 case VK_HOME:
3134 nParam = VK_NUMPAD7;
3135 break;
3136 case VK_UP:
3137 nParam = VK_NUMPAD8;
3138 break;
3139 case VK_PRIOR:
3140 nParam = VK_NUMPAD9;
3141 break;
3142 case VK_DELETE:
3143 nParam = VK_DECIMAL;
3144 break;
c9def1b8 3145 }
32874aea 3146 if (nParam) {
3147 if (keystate[VK_NUMLOCK] & 1)
3148 shift_state |= 1;
c9def1b8 3149 wParam = nParam;
3150 }
25d39ef6 3151 }
3152 }
3153
c9def1b8 3154 /* If a key is pressed and AltGr is not active */
32874aea 3155 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3156 /* Okay, prepare for most alts then ... */
3157 if (left_alt)
3158 *p++ = '\033';
374330e2 3159
c9def1b8 3160 /* Lets see if it's a pattern we know all about ... */
3161 if (wParam == VK_PRIOR && shift_state == 1) {
32874aea 3162 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3163 return 0;
c9def1b8 3164 }
3165 if (wParam == VK_NEXT && shift_state == 1) {
32874aea 3166 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3167 return 0;
3168 }
3169 if (wParam == VK_INSERT && shift_state == 1) {
568dd02f 3170 term_do_paste();
32874aea 3171 return 0;
3172 }
c9def1b8 3173 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
32874aea 3174 return -1;
c9def1b8 3175 }
3176 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
32874aea 3177 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3178 return -1;
c9def1b8 3179 }
2cae8529 3180 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3181 (cfg.resize_action != RESIZE_DISABLED)) {
0ed2f48e 3182 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3183 flip_full_screen();
8f57d753 3184 return -1;
3185 }
ec55b220 3186 /* Control-Numlock for app-keypad mode switch */
3187 if (wParam == VK_PAUSE && shift_state == 2) {
3188 app_keypad_keys ^= 1;
3189 return 0;
3190 }
374330e2 3191
c9def1b8 3192 /* Nethack keypad */
3193 if (cfg.nethack_keypad && !left_alt) {
32874aea 3194 switch (wParam) {
3195 case VK_NUMPAD1:
3196 *p++ = shift_state ? 'B' : 'b';
3197 return p - output;
3198 case VK_NUMPAD2:
3199 *p++ = shift_state ? 'J' : 'j';
3200 return p - output;
3201 case VK_NUMPAD3:
3202 *p++ = shift_state ? 'N' : 'n';
3203 return p - output;
3204 case VK_NUMPAD4:
3205 *p++ = shift_state ? 'H' : 'h';
3206 return p - output;
3207 case VK_NUMPAD5:
3208 *p++ = shift_state ? '.' : '.';
3209 return p - output;
3210 case VK_NUMPAD6:
3211 *p++ = shift_state ? 'L' : 'l';
3212 return p - output;
3213 case VK_NUMPAD7:
3214 *p++ = shift_state ? 'Y' : 'y';
3215 return p - output;
3216 case VK_NUMPAD8:
3217 *p++ = shift_state ? 'K' : 'k';
3218 return p - output;
3219 case VK_NUMPAD9:
3220 *p++ = shift_state ? 'U' : 'u';
3221 return p - output;
3222 }
c9def1b8 3223 }
3224
3225 /* Application Keypad */
3226 if (!left_alt) {
32874aea 3227 int xkey = 0;
3228
3229 if (cfg.funky_type == 3 ||
3230 (cfg.funky_type <= 1 &&
3231 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3232 case VK_EXECUTE:
3233 xkey = 'P';
3234 break;
3235 case VK_DIVIDE:
3236 xkey = 'Q';
3237 break;
3238 case VK_MULTIPLY:
3239 xkey = 'R';
3240 break;
3241 case VK_SUBTRACT:
3242 xkey = 'S';
3243 break;
3244 }
3245 if (app_keypad_keys && !cfg.no_applic_k)
3246 switch (wParam) {
3247 case VK_NUMPAD0:
3248 xkey = 'p';
3249 break;
3250 case VK_NUMPAD1:
3251 xkey = 'q';
3252 break;
3253 case VK_NUMPAD2:
3254 xkey = 'r';
3255 break;
3256 case VK_NUMPAD3:
3257 xkey = 's';
3258 break;
3259 case VK_NUMPAD4:
3260 xkey = 't';
3261 break;
3262 case VK_NUMPAD5:
3263 xkey = 'u';
3264 break;
3265 case VK_NUMPAD6:
3266 xkey = 'v';
3267 break;
3268 case VK_NUMPAD7:
3269 xkey = 'w';
3270 break;
3271 case VK_NUMPAD8:
3272 xkey = 'x';
3273 break;
3274 case VK_NUMPAD9:
3275 xkey = 'y';
3276 break;
3277
3278 case VK_DECIMAL:
3279 xkey = 'n';
3280 break;
3281 case VK_ADD:
3282 if (cfg.funky_type == 2) {
3283 if (shift_state)
3284 xkey = 'l';
3285 else
3286 xkey = 'k';
3287 } else if (shift_state)
3288 xkey = 'm';
c9def1b8 3289 else
32874aea 3290 xkey = 'l';
3291 break;
3292
3293 case VK_DIVIDE:
3294 if (cfg.funky_type == 2)
3295 xkey = 'o';
3296 break;
3297 case VK_MULTIPLY:
3298 if (cfg.funky_type == 2)
3299 xkey = 'j';
3300 break;
3301 case VK_SUBTRACT:
3302 if (cfg.funky_type == 2)
3303 xkey = 'm';
3304 break;
3305
3306 case VK_RETURN:
3307 if (HIWORD(lParam) & KF_EXTENDED)
3308 xkey = 'M';
3309 break;
c9def1b8 3310 }
32874aea 3311 if (xkey) {
3312 if (vt52_mode) {
3313 if (xkey >= 'P' && xkey <= 'S')
3314 p += sprintf((char *) p, "\x1B%c", xkey);
3315 else
3316 p += sprintf((char *) p, "\x1B?%c", xkey);
3317 } else
3318 p += sprintf((char *) p, "\x1BO%c", xkey);
3319 return p - output;
c9def1b8 3320 }
3321 }
3322
32874aea 3323 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
c9def1b8 3324 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
a5f3e637 3325 *p++ = 0;
3326 return -2;
c9def1b8 3327 }
32874aea 3328 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3329 *p++ = 0x1B;
3330 *p++ = '[';
3331 *p++ = 'Z';
3332 return p - output;
c9def1b8 3333 }
32874aea 3334 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3335 *p++ = 0;
3336 return p - output;
c9def1b8 3337 }
32874aea 3338 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3339 *p++ = 160;
3340 return p - output;
c9def1b8 3341 }
32874aea 3342 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3343 *p++ = 3;
a5f3e637 3344 *p++ = 0;
3345 return -2;
c9def1b8 3346 }
32874aea 3347 if (wParam == VK_PAUSE) { /* Break/Pause */
3348 *p++ = 26;
3349 *p++ = 0;
3350 return -2;
95bbe1ae 3351 }
c9def1b8 3352 /* Control-2 to Control-8 are special */
32874aea 3353 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3354 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
c9def1b8 3355 return p - output;
3356 }
3357 if (shift_state == 2 && wParam == 0xBD) {
3358 *p++ = 0x1F;
3359 return p - output;
3360 }
3361 if (shift_state == 2 && wParam == 0xDF) {
3362 *p++ = 0x1C;
3363 return p - output;
3364 }
3365 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
32874aea 3366 *p++ = '\r';
3367 *p++ = '\n';
c9def1b8 3368 return p - output;
3369 }
374330e2 3370
c5e9c988 3371 /*
c9def1b8 3372 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3373 * for integer decimal nn.)
3374 *
3375 * We also deal with the weird ones here. Linux VCs replace F1
3376 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3377 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3378 * respectively.
c5e9c988 3379 */
c9def1b8 3380 code = 0;
3381 switch (wParam) {
32874aea 3382 case VK_F1:
3383 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3384 break;
3385 case VK_F2:
3386 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3387 break;
3388 case VK_F3:
3389 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3390 break;
3391 case VK_F4:
3392 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3393 break;
3394 case VK_F5:
3395 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3396 break;
3397 case VK_F6:
3398 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3399 break;
3400 case VK_F7:
3401 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3402 break;
3403 case VK_F8:
3404 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3405 break;
3406 case VK_F9:
3407 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3408 break;
3409 case VK_F10:
3410 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3411 break;
3412 case VK_F11:
3413 code = 23;
3414 break;
3415 case VK_F12:
3416 code = 24;
3417 break;
3418 case VK_F13:
3419 code = 25;
3420 break;
3421 case VK_F14:
3422 code = 26;
3423 break;
3424 case VK_F15:
3425 code = 28;
3426 break;
3427 case VK_F16:
3428 code = 29;
3429 break;
3430 case VK_F17:
3431 code = 31;
3432 break;
3433 case VK_F18:
3434 code = 32;
3435 break;
3436 case VK_F19:
3437 code = 33;
3438 break;
3439 case VK_F20:
3440 code = 34;
3441 break;
dfca2656 3442 }
3443 if ((shift_state&2) == 0) switch (wParam) {
32874aea 3444 case VK_HOME:
3445 code = 1;
3446 break;
3447 case VK_INSERT:
3448 code = 2;
3449 break;
3450 case VK_DELETE:
3451 code = 3;
3452 break;
3453 case VK_END:
3454 code = 4;
3455 break;
3456 case VK_PRIOR:
3457 code = 5;
3458 break;
3459 case VK_NEXT:
3460 code = 6;
3461 break;
374330e2 3462 }
ec55b220 3463 /* Reorder edit keys to physical order */
32874aea 3464 if (cfg.funky_type == 3 && code <= 6)
3465 code = "\0\2\1\4\5\3\6"[code];
ec55b220 3466
f37caa11 3467 if (vt52_mode && code > 0 && code <= 6) {
32874aea 3468 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
f37caa11 3469 return p - output;
3470 }
3471
9bc81a2c 3472 if (cfg.funky_type == 5 && /* SCO function keys */
3473 code >= 11 && code <= 34) {
e24b1972 3474 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3475 int index = 0;
3476 switch (wParam) {
3477 case VK_F1: index = 0; break;
3478 case VK_F2: index = 1; break;
3479 case VK_F3: index = 2; break;
3480 case VK_F4: index = 3; break;
3481 case VK_F5: index = 4; break;
3482 case VK_F6: index = 5; break;
3483 case VK_F7: index = 6; break;
3484 case VK_F8: index = 7; break;
3485 case VK_F9: index = 8; break;
3486 case VK_F10: index = 9; break;
3487 case VK_F11: index = 10; break;
3488 case VK_F12: index = 11; break;
3489 }
3490 if (keystate[VK_SHIFT] & 0x80) index += 12;
3491 if (keystate[VK_CONTROL] & 0x80) index += 24;
3492 p += sprintf((char *) p, "\x1B[%c", codes[index]);
f37caa11 3493 return p - output;
3494 }
9bc81a2c 3495 if (cfg.funky_type == 5 && /* SCO small keypad */
3496 code >= 1 && code <= 6) {
3497 char codes[] = "HL.FIG";
3498 if (code == 3) {
3499 *p++ = '\x7F';
3500 } else {
3501 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3502 }
3503 return p - output;
3504 }
f37caa11 3505 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3506 int offt = 0;
32874aea 3507 if (code > 15)
3508 offt++;
3509 if (code > 21)
3510 offt++;
f37caa11 3511 if (vt52_mode)
32874aea 3512 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
f37caa11 3513 else
32874aea 3514 p +=
3515 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
f37caa11 3516 return p - output;
3517 }
c9def1b8 3518 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
32874aea 3519 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
c9def1b8 3520 return p - output;
3521 }
3522 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
ec55b220 3523 if (vt52_mode)
32874aea 3524 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
ec55b220 3525 else
32874aea 3526 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
c9def1b8 3527 return p - output;
3528 }
3529 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
32874aea 3530 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
c9def1b8 3531 return p - output;
3532 }
3533 if (code) {
32874aea 3534 p += sprintf((char *) p, "\x1B[%d~", code);
374330e2 3535 return p - output;
374330e2 3536 }
45dabbc5 3537
c9def1b8 3538 /*
3539 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3540 * some reason seems to send VK_CLEAR to Windows...).
3541 */
3542 {
3543 char xkey = 0;
3544 switch (wParam) {
32874aea 3545 case VK_UP:
3546 xkey = 'A';
3547 break;
3548 case VK_DOWN:
3549 xkey = 'B';
3550 break;
3551 case VK_RIGHT:
3552 xkey = 'C';
3553 break;
3554 case VK_LEFT:
3555 xkey = 'D';
3556 break;
3557 case VK_CLEAR:
3558 xkey = 'G';
3559 break;
c9def1b8 3560 }
32874aea 3561 if (xkey) {
c9def1b8 3562 if (vt52_mode)
32874aea 3563 p += sprintf((char *) p, "\x1B%c", xkey);
e864f84f 3564 else {
3565 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3a953121 3566#if 0
3567 /*
3568 * RDB: VT100 & VT102 manuals both state the
3569 * app cursor keys only work if the app keypad
3570 * is on.
3571 *
3572 * SGT: That may well be true, but xterm
3573 * disagrees and so does at least one
3574 * application, so I've #if'ed this out and the
3575 * behaviour is back to PuTTY's original: app
3576 * cursor and app keypad are independently
3577 * switchable modes. If anyone complains about
3578 * _this_ I'll have to put in a configurable
3579 * option.
e864f84f 3580 */
3581 if (!app_keypad_keys)
3582 app_flg = 0;
3a953121 3583#endif
e864f84f 3584 /* Useful mapping of Ctrl-arrows */
3585 if (shift_state == 2)
3586 app_flg = !app_flg;
3587
3588 if (app_flg)
3589 p += sprintf((char *) p, "\x1BO%c", xkey);
3590 else
3591 p += sprintf((char *) p, "\x1B[%c", xkey);
3592 }
c9def1b8 3593 return p - output;
3594 }
3595 }
0c50ef57 3596
3597 /*
3598 * Finally, deal with Return ourselves. (Win95 seems to
3599 * foul it up when Alt is pressed, for some reason.)
3600 */
32874aea 3601 if (wParam == VK_RETURN) { /* Return */
0c50ef57 3602 *p++ = 0x0D;
a5f3e637 3603 *p++ = 0;
3604 return -2;
0c50ef57 3605 }
4eeb7d09 3606
3607 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3608 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3609 else
3610 alt_sum = 0;
67c339f7 3611 }
374330e2 3612
c9def1b8 3613 /* Okay we've done everything interesting; let windows deal with
3614 * the boring stuff */
3615 {
a9c02454 3616 BOOL capsOn=0;
3617
3618 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3619 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3620 capsOn= !left_alt;
3621 keystate[VK_CAPITAL] = 0;
3622 }
3623
00e3ba0f 3624 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
4eeb7d09 3625#ifdef SHOW_TOASCII_RESULT
3626 if (r == 1 && !key_down) {
3627 if (alt_sum) {
8f22582c 3628 if (in_utf || dbcs_screenfont)
4eeb7d09 3629 debug((", (U+%04x)", alt_sum));
3630 else
3631 debug((", LCH(%d)", alt_sum));
3632 } else {
3633 debug((", ACH(%d)", keys[0]));
3634 }
3635 } else if (r > 0) {
3636 int r1;
3637 debug((", ASC("));
3638 for (r1 = 0; r1 < r; r1++) {
3639 debug(("%s%d", r1 ? "," : "", keys[r1]));
3640 }
3641 debug((")"));
3642 }
3643#endif
32874aea 3644 if (r > 0) {
4eeb7d09 3645 WCHAR keybuf;
256cb87c 3646
3647 /*
3648 * Interrupt an ongoing paste. I'm not sure this is
3649 * sensible, but for the moment it's preferable to
3650 * having to faff about buffering things.
3651 */
3652 term_nopaste();
3653
c9def1b8 3654 p = output;
32874aea 3655 for (i = 0; i < r; i++) {
3656 unsigned char ch = (unsigned char) keys[i];
14963b8f 3657
32874aea 3658 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 3659 compose_char = ch;
32874aea 3660 compose_state++;
c9def1b8 3661 continue;
3662 }
32874aea 3663 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 3664 int nc;
3665 compose_state = 0;
3666
32874aea 3667 if ((nc = check_compose(compose_char, ch)) == -1) {
fe50e814 3668 MessageBeep(MB_ICONHAND);
c9def1b8 3669 return 0;
3670 }
4eeb7d09 3671 keybuf = nc;
760e88b2 3672 luni_send(&keybuf, 1, 1);
4eeb7d09 3673 continue;
c9def1b8 3674 }
374330e2 3675
c9def1b8 3676 compose_state = 0;
374330e2 3677
4eeb7d09 3678 if (!key_down) {
3679 if (alt_sum) {
8f22582c 3680 if (in_utf || dbcs_screenfont) {
4eeb7d09 3681 keybuf = alt_sum;
760e88b2 3682 luni_send(&keybuf, 1, 1);
4eeb7d09 3683 } else {
3684 ch = (char) alt_sum;
5471d09a 3685 /*
3686 * We need not bother about stdin
3687 * backlogs here, because in GUI PuTTY
3688 * we can't do anything about it
3689 * anyway; there's no means of asking
3690 * Windows to hold off on KEYDOWN
3691 * messages. We _have_ to buffer
3692 * everything we're sent.
3693 */
760e88b2 3694 ldisc_send(&ch, 1, 1);
4eeb7d09 3695 }
3696 alt_sum = 0;
3697 } else
760e88b2 3698 lpage_send(kbd_codepage, &ch, 1, 1);
4eeb7d09 3699 } else {
a9c02454 3700 if(capsOn && ch < 0x80) {
3701 WCHAR cbuf[2];
3702 cbuf[0] = 27;
3703 cbuf[1] = xlat_uskbd2cyrllic(ch);
760e88b2 3704 luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 3705 } else {
3706 char cbuf[2];
3707 cbuf[0] = '\033';
3708 cbuf[1] = ch;
760e88b2 3709 lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 3710 }
c9def1b8 3711 }
bca9517a 3712 show_mouseptr(0);
c9def1b8 3713 }
374330e2 3714
c9def1b8 3715 /* This is so the ALT-Numpad and dead keys work correctly. */
3716 keys[0] = 0;
3717
32874aea 3718 return p - output;
c9def1b8 3719 }
159eba53 3720 /* If we're definitly not building up an ALT-54321 then clear it */
32874aea 3721 if (!left_alt)
3722 keys[0] = 0;
4eeb7d09 3723 /* If we will be using alt_sum fix the 256s */
8f22582c 3724 else if (keys[0] && (in_utf || dbcs_screenfont))
4eeb7d09 3725 keys[0] = 10;
374330e2 3726 }
3727
dfca2656 3728 /*
3729 * ALT alone may or may not want to bring up the System menu.
3730 * If it's not meant to, we return 0 on presses or releases of
3731 * ALT, to show that we've swallowed the keystroke. Otherwise
3732 * we return -1, which means Windows will give the keystroke
3733 * its default handling (i.e. bring up the System menu).
3734 */
3735 if (wParam == VK_MENU && !cfg.alt_only)
3736 return 0;
374330e2 3737
c9def1b8 3738 return -1;
374330e2 3739}
3740
32874aea 3741void set_title(char *title)
3742{
3743 sfree(window_name);
3744 window_name = smalloc(1 + strlen(title));
3745 strcpy(window_name, title);
37508af4 3746 if (cfg.win_name_always || !IsIconic(hwnd))
32874aea 3747 SetWindowText(hwnd, title);
374330e2 3748}
3749
32874aea 3750void set_icon(char *title)
3751{
3752 sfree(icon_name);
3753 icon_name = smalloc(1 + strlen(title));
3754 strcpy(icon_name, title);
37508af4 3755 if (!cfg.win_name_always && IsIconic(hwnd))
32874aea 3756 SetWindowText(hwnd, title);
374330e2 3757}
3758
32874aea 3759void set_sbar(int total, int start, int page)
3760{
374330e2 3761 SCROLLINFO si;
c9def1b8 3762
1ba99d2c 3763 if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
32874aea 3764 return;
c9def1b8 3765
374330e2 3766 si.cbSize = sizeof(si);
c9def1b8 3767 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
374330e2 3768 si.nMin = 0;
3769 si.nMax = total - 1;
3770 si.nPage = page;
3771 si.nPos = start;
c1f5f956 3772 if (hwnd)
32874aea 3773 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
374330e2 3774}
3775
32874aea 3776Context get_ctx(void)
3777{
374330e2 3778 HDC hdc;
3779 if (hwnd) {
32874aea 3780 hdc = GetDC(hwnd);
374330e2 3781 if (hdc && pal)
32874aea 3782 SelectPalette(hdc, pal, FALSE);
374330e2 3783 return hdc;
3784 } else
3785 return NULL;
3786}
3787
32874aea 3788void free_ctx(Context ctx)
3789{
3790 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3791 ReleaseDC(hwnd, ctx);
374330e2 3792}
3793
32874aea 3794static void real_palette_set(int n, int r, int g, int b)
3795{
374330e2 3796 if (pal) {
3797 logpal->palPalEntry[n].peRed = r;
3798 logpal->palPalEntry[n].peGreen = g;
3799 logpal->palPalEntry[n].peBlue = b;
3800 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3801 colours[n] = PALETTERGB(r, g, b);
32874aea 3802 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 3803 } else
3804 colours[n] = RGB(r, g, b);
3805}
3806
32874aea 3807void palette_set(int n, int r, int g, int b)
3808{
374330e2 3809 static const int first[21] = {
3810 0, 2, 4, 6, 8, 10, 12, 14,
3811 1, 3, 5, 7, 9, 11, 13, 15,
3812 16, 17, 18, 20, 22
3813 };
32874aea 3814 real_palette_set(first[n], r, g, b);
374330e2 3815 if (first[n] >= 18)
32874aea 3816 real_palette_set(first[n] + 1, r, g, b);
374330e2 3817 if (pal) {
3818 HDC hdc = get_ctx();
32874aea 3819 UnrealizeObject(pal);
3820 RealizePalette(hdc);
3821 free_ctx(hdc);
374330e2 3822 }
3823}
3824
32874aea 3825void palette_reset(void)
3826{
374330e2 3827 int i;
3828
3829 for (i = 0; i < NCOLOURS; i++) {
3830 if (pal) {
3831 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3832 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3833 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3834 logpal->palPalEntry[i].peFlags = 0;
3835 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3836 defpal[i].rgbtGreen,
3837 defpal[i].rgbtBlue);
3838 } else
3839 colours[i] = RGB(defpal[i].rgbtRed,
32874aea 3840 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
374330e2 3841 }
3842
3843 if (pal) {
3844 HDC hdc;
32874aea 3845 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 3846 hdc = get_ctx();
32874aea 3847 RealizePalette(hdc);
3848 free_ctx(hdc);
374330e2 3849 }
3850}
3851
4eeb7d09 3852void write_aclip(char *data, int len, int must_deselect)
32874aea 3853{
374330e2 3854 HGLOBAL clipdata;
3855 void *lock;
3856
32874aea 3857 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
374330e2 3858 if (!clipdata)
3859 return;
32874aea 3860 lock = GlobalLock(clipdata);
374330e2 3861 if (!lock)
3862 return;
32874aea 3863 memcpy(lock, data, len);
3864 ((unsigned char *) lock)[len] = 0;
3865 GlobalUnlock(clipdata);
374330e2 3866
f0df44da 3867 if (!must_deselect)
32874aea 3868 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
f0df44da 3869
32874aea 3870 if (OpenClipboard(hwnd)) {
374330e2 3871 EmptyClipboard();
32874aea 3872 SetClipboardData(CF_TEXT, clipdata);
374330e2 3873 CloseClipboard();
3874 } else
32874aea 3875 GlobalFree(clipdata);
f0df44da 3876
3877 if (!must_deselect)
32874aea 3878 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
374330e2 3879}
3880
4eeb7d09 3881/*
3882 * Note: unlike write_aclip() this will not append a nul.
3883 */
3884void write_clip(wchar_t * data, int len, int must_deselect)
3885{
a7419ea4 3886 HGLOBAL clipdata, clipdata2, clipdata3;
4eeb7d09 3887 int len2;
a7419ea4 3888 void *lock, *lock2, *lock3;
4eeb7d09 3889
3890 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3891
3892 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3893 len * sizeof(wchar_t));
3894 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3895
0e5e7f46 3896 if (!clipdata || !clipdata2) {
4eeb7d09 3897 if (clipdata)
3898 GlobalFree(clipdata);
3899 if (clipdata2)
3900 GlobalFree(clipdata2);
3901 return;
3902 }
3903 if (!(lock = GlobalLock(clipdata)))
3904 return;
3905 if (!(lock2 = GlobalLock(clipdata2)))
3906 return;
3907
3908 memcpy(lock, data, len * sizeof(wchar_t));
3909 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3910
a7419ea4 3911 if (cfg.rtf_paste) {
3912 wchar_t unitab[256];
3913 char *rtf = NULL;
3914 unsigned char *tdata = (unsigned char *)lock2;
3915 wchar_t *udata = (wchar_t *)lock;
3916 int rtflen = 0, uindex = 0, tindex = 0;
3917 int rtfsize = 0;
3918 int multilen, blen, alen, totallen, i;
3919 char before[16], after[4];
3920
3921 get_unitab(CP_ACP, unitab, 0);
3922
3923 rtfsize = 100 + strlen(cfg.font);
3924 rtf = smalloc(rtfsize);
3925 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3926 GetACP(), cfg.font);
3927 rtflen = strlen(rtf);
3928
3929 /*
3930 * We want to construct a piece of RTF that specifies the
3931 * same Unicode text. To do this we will read back in
3932 * parallel from the Unicode data in `udata' and the
3933 * non-Unicode data in `tdata'. For each character in
3934 * `tdata' which becomes the right thing in `udata' when
3935 * looked up in `unitab', we just copy straight over from
3936 * tdata. For each one that doesn't, we must WCToMB it
3937 * individually and produce a \u escape sequence.
3938 *
3939 * It would probably be more robust to just bite the bullet
3940 * and WCToMB each individual Unicode character one by one,
3941 * then MBToWC each one back to see if it was an accurate
3942 * translation; but that strikes me as a horrifying number
3943 * of Windows API calls so I want to see if this faster way
3944 * will work. If it screws up badly we can always revert to
3945 * the simple and slow way.
3946 */
3947 while (tindex < len2 && uindex < len &&
3948 tdata[tindex] && udata[uindex]) {
3949 if (tindex + 1 < len2 &&
3950 tdata[tindex] == '\r' &&
3951 tdata[tindex+1] == '\n') {
3952 tindex++;
3953 uindex++;
3954 }
3955 if (unitab[tdata[tindex]] == udata[uindex]) {
3956 multilen = 1;
3957 before[0] = '\0';
3958 after[0] = '\0';
3959 blen = alen = 0;
3960 } else {
3961 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
3962 NULL, 0, NULL, NULL);
3963 if (multilen != 1) {
c3b032a6 3964 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
3965 udata[uindex]);
a7419ea4 3966 alen = 1; strcpy(after, "}");
3967 } else {
3968 blen = sprintf(before, "\\u%d", udata[uindex]);
3969 alen = 0; after[0] = '\0';
3970 }
3971 }
3972 assert(tindex + multilen <= len2);
3973 totallen = blen + alen;
3974 for (i = 0; i < multilen; i++) {
3975 if (tdata[tindex+i] == '\\' ||
3976 tdata[tindex+i] == '{' ||
3977 tdata[tindex+i] == '}')
3978 totallen += 2;
3979 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
3980 totallen += 6; /* \par\r\n */
3981 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
3982 totallen += 4;
3983 else
3984 totallen++;
3985 }
3986
3987 if (rtfsize < rtflen + totallen + 3) {
3988 rtfsize = rtflen + totallen + 512;
3989 rtf = srealloc(rtf, rtfsize);
3990 }
3991
3992 strcpy(rtf + rtflen, before); rtflen += blen;
3993 for (i = 0; i < multilen; i++) {
3994 if (tdata[tindex+i] == '\\' ||
3995 tdata[tindex+i] == '{' ||
3996 tdata[tindex+i] == '}') {
3997 rtf[rtflen++] = '\\';
3998 rtf[rtflen++] = tdata[tindex+i];
3999 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
4000 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
4001 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
4002 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
4003 } else {
4004 rtf[rtflen++] = tdata[tindex+i];
4005 }
4006 }
4007 strcpy(rtf + rtflen, after); rtflen += alen;
4008
4009 tindex += multilen;
4010 uindex++;
4011 }
4012
4013 strcpy(rtf + rtflen, "}");
4014 rtflen += 2;
4015
4016 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
4017 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
4018 strcpy(lock3, rtf);
4019 GlobalUnlock(clipdata3);
4020 }
4021 sfree(rtf);
4022 } else
4023 clipdata3 = NULL;
4024
4eeb7d09 4025 GlobalUnlock(clipdata);
4026 GlobalUnlock(clipdata2);
4027
4028 if (!must_deselect)
4029 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
4030
4031 if (OpenClipboard(hwnd)) {
4032 EmptyClipboard();
4033 SetClipboardData(CF_UNICODETEXT, clipdata);
4034 SetClipboardData(CF_TEXT, clipdata2);
a7419ea4 4035 if (clipdata3)
4036 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
4eeb7d09 4037 CloseClipboard();
4038 } else {
4039 GlobalFree(clipdata);
4040 GlobalFree(clipdata2);
4041 }
4042
4043 if (!must_deselect)
4044 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
4045}
4046
4047void get_clip(wchar_t ** p, int *len)
32874aea 4048{
374330e2 4049 static HGLOBAL clipdata = NULL;
4eeb7d09 4050 static wchar_t *converted = 0;
4051 wchar_t *p2;
374330e2 4052
4eeb7d09 4053 if (converted) {
4054 sfree(converted);
4055 converted = 0;
4056 }
374330e2 4057 if (!p) {
4058 if (clipdata)
32874aea 4059 GlobalUnlock(clipdata);
374330e2 4060 clipdata = NULL;
4061 return;
4eeb7d09 4062 } else if (OpenClipboard(NULL)) {
2d466ffd 4063 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
374330e2 4064 CloseClipboard();
4eeb7d09 4065 *p = GlobalLock(clipdata);
4066 if (*p) {
4067 for (p2 = *p; *p2; p2++);
4068 *len = p2 - *p;
4069 return;
374330e2 4070 }
2d466ffd 4071 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4eeb7d09 4072 char *s;
4073 int i;
4074 CloseClipboard();
4075 s = GlobalLock(clipdata);
4076 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
4077 *p = converted = smalloc(i * sizeof(wchar_t));
4078 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
4079 *len = i - 1;
4080 return;
4081 } else
4082 CloseClipboard();
374330e2 4083 }
4084
4085 *p = NULL;
4086 *len = 0;
4087}
4088
4eeb7d09 4089#if 0
374330e2 4090/*
4091 * Move `lines' lines from position `from' to position `to' in the
4092 * window.
4093 */
32874aea 4094void optimised_move(int to, int from, int lines)
4095{
374330e2 4096 RECT r;
f67b4e85 4097 int min, max;
374330e2 4098
4099 min = (to < from ? to : from);
4100 max = to + from - min;
374330e2 4101
5a73255e 4102 r.left = offset_width;
4103 r.right = offset_width + cols * font_width;
4104 r.top = offset_height + min * font_height;
4105 r.bottom = offset_height + (max + lines) * font_height;
32874aea 4106 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
374330e2 4107}
4eeb7d09 4108#endif
374330e2 4109
4110/*
4111 * Print a message box and perform a fatal exit.
4112 */
32874aea 4113void fatalbox(char *fmt, ...)
4114{
374330e2 4115 va_list ap;
4116 char stuff[200];
4117
4118 va_start(ap, fmt);
4119 vsprintf(stuff, fmt, ap);
4120 va_end(ap);
4121 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
4122 exit(1);
4123}
4124
4125/*
f8a28d1f 4126 * Manage window caption / taskbar flashing, if enabled.
4127 * 0 = stop, 1 = maintain, 2 = start
4128 */
4129static void flash_window(int mode)
4130{
4131 static long last_flash = 0;
4132 static int flashing = 0;
4133 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
4134 /* stop */
4135 if (flashing) {
4136 FlashWindow(hwnd, FALSE);
4137 flashing = 0;
4138 }
4139
4140 } else if (mode == 2) {
4141 /* start */
4142 if (!flashing) {
4143 last_flash = GetTickCount();
4144 flashing = 1;
4145 FlashWindow(hwnd, TRUE);
4146 }
4147
4148 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4149 /* maintain */
4150 if (flashing) {
4151 long now = GetTickCount();
4152 long fdiff = now - last_flash;
4153 if (fdiff < 0 || fdiff > 450) {
4154 last_flash = now;
4155 FlashWindow(hwnd, TRUE); /* toggle */
4156 }
4157 }
4158 }
4159}
4160
4161/*
374330e2 4162 * Beep.
4163 */
32874aea 4164void beep(int mode)
4165{
03169ad0 4166 if (mode == BELL_DEFAULT) {
eb04402e 4167 /*
4168 * For MessageBeep style bells, we want to be careful of
4169 * timing, because they don't have the nice property of
4170 * PlaySound bells that each one cancels the previous
4171 * active one. So we limit the rate to one per 50ms or so.
4172 */
4173 static long lastbeep = 0;
d51cdf1e 4174 long beepdiff;
eb04402e 4175
d51cdf1e 4176 beepdiff = GetTickCount() - lastbeep;
eb04402e 4177 if (beepdiff >= 0 && beepdiff < 50)
4178 return;
156686ef 4179 MessageBeep(MB_OK);
d51cdf1e 4180 /*
4181 * The above MessageBeep call takes time, so we record the
4182 * time _after_ it finishes rather than before it starts.
4183 */
4184 lastbeep = GetTickCount();
03169ad0 4185 } else if (mode == BELL_WAVEFILE) {
4186 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
32874aea 4187 char buf[sizeof(cfg.bell_wavefile) + 80];
03169ad0 4188 sprintf(buf, "Unable to play sound file\n%s\n"
4189 "Using default sound instead", cfg.bell_wavefile);
32874aea 4190 MessageBox(hwnd, buf, "PuTTY Sound Error",
4191 MB_OK | MB_ICONEXCLAMATION);
03169ad0 4192 cfg.beep = BELL_DEFAULT;
4193 }
4194 }
f8a28d1f 4195 /* Otherwise, either visual bell or disabled; do nothing here */
4196 if (!has_focus) {
4197 flash_window(2); /* start */
4198 }
374330e2 4199}
8f57d753 4200
4201/*
68f9b3d9 4202 * Minimise or restore the window in response to a server-side
4203 * request.
4204 */
4205void set_iconic(int iconic)
4206{
4207 if (IsIconic(hwnd)) {
4208 if (!iconic)
4209 ShowWindow(hwnd, SW_RESTORE);
4210 } else {
4211 if (iconic)
4212 ShowWindow(hwnd, SW_MINIMIZE);
4213 }
4214}
4215
4216/*
4217 * Move the window in response to a server-side request.
4218 */
4219void move_window(int x, int y)
4220{
4221 SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4222}
4223
4224/*
4225 * Move the window to the top or bottom of the z-order in response
4226 * to a server-side request.
4227 */
4228void set_zorder(int top)
4229{
1ba99d2c 4230 if (cfg.alwaysontop)
68f9b3d9 4231 return; /* ignore */
4232 SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4233 SWP_NOMOVE | SWP_NOSIZE);
4234}
4235
4236/*
4237 * Refresh the window in response to a server-side request.
4238 */
4239void refresh_window(void)
4240{
4241 InvalidateRect(hwnd, NULL, TRUE);
4242}
4243
4244/*
4245 * Maximise or restore the window in response to a server-side
4246 * request.
4247 */
4248void set_zoomed(int zoomed)
4249{
1ba99d2c 4250 if (IsZoomed(hwnd)) {
4251 if (!zoomed)
4252 ShowWindow(hwnd, SW_RESTORE);
68f9b3d9 4253 } else {
4254 if (zoomed)
4255 ShowWindow(hwnd, SW_MAXIMIZE);
4256 }
4257}
4258
4259/*
4260 * Report whether the window is iconic, for terminal reports.
4261 */
4262int is_iconic(void)
4263{
4264 return IsIconic(hwnd);
4265}
4266
4267/*
4268 * Report the window's position, for terminal reports.
4269 */
4270void get_window_pos(int *x, int *y)
4271{
4272 RECT r;
4273 GetWindowRect(hwnd, &r);
4274 *x = r.left;
4275 *y = r.top;
4276}
4277
4278/*
4279 * Report the window's pixel size, for terminal reports.
4280 */
4281void get_window_pixels(int *x, int *y)
4282{
4283 RECT r;
4284 GetWindowRect(hwnd, &r);
4285 *x = r.right - r.left;
4286 *y = r.bottom - r.top;
4287}
4288
4289/*
4290 * Return the window or icon title.
4291 */
4292char *get_window_title(int icon)
4293{
4294 return icon ? icon_name : window_name;
4295}
1ba99d2c 4296
4297/*
4298 * See if we're in full-screen mode.
4299 */
4300int is_full_screen()
4301{
4302 if (!IsZoomed(hwnd))
4303 return FALSE;
4304 if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
4305 return FALSE;
4306 return TRUE;
4307}
4308
4309/*
4310 * Go full-screen. This should only be called when we are already
4311 * maximised.
4312 */
4313void make_full_screen()
4314{
4315 DWORD style;
4316 int x, y, w, h;
4317
4318 assert(IsZoomed(hwnd));
4319
4320 /* Remove the window furniture. */
4321 style = GetWindowLong(hwnd, GWL_STYLE);
4322 style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
4323 if (cfg.scrollbar_in_fullscreen)
4324 style |= WS_VSCROLL;
4325 else
4326 style &= ~WS_VSCROLL;
4327 SetWindowLong(hwnd, GWL_STYLE, style);
4328
4329 /* Resize ourselves to exactly cover the nearest monitor. */
4330#ifdef MONITOR_DEFAULTTONEAREST
4331 {
4332 HMONITOR mon;
4333 MONITORINFO mi;
4334 mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
4335 mi.cbSize = sizeof(mi);
4336 GetMonitorInfo(mon, &mi);
4337 x = mi.rcMonitor.left;
4338 y = mi.rcMonitor.top;
4339 w = mi.rcMonitor.right;
4340 h = mi.rcMonitor.bottom;
4341 }
4342#else
4343 x = y = 0;
4344 w = GetSystemMetrics(SM_CXSCREEN);
4345 h = GetSystemMetrics(SM_CYSCREEN);
4346#endif
4347 SetWindowPos(hwnd, HWND_TOP, x, y, w, h, SWP_FRAMECHANGED);
4348
4349 /* Tick the menu item in the System menu. */
4350 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4351 MF_CHECKED);
4352}
4353
4354/*
4355 * Clear the full-screen attributes.
4356 */
4357void clear_full_screen()
4358{
4359 DWORD oldstyle, style;
4360
4361 /* Reinstate the window furniture. */
4362 style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
811f10b3 4363 style |= WS_CAPTION | WS_BORDER;
4364 if (cfg.resize_action == RESIZE_DISABLED)
4365 style &= ~WS_THICKFRAME;
4366 else
4367 style |= WS_THICKFRAME;
1ba99d2c 4368 if (cfg.scrollbar)
4369 style |= WS_VSCROLL;
4370 else
4371 style &= ~WS_VSCROLL;
4372 if (style != oldstyle) {
4373 SetWindowLong(hwnd, GWL_STYLE, style);
4374 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4375 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4376 SWP_FRAMECHANGED);
4377 }
4378
4379 /* Untick the menu item in the System menu. */
4380 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4381 MF_UNCHECKED);
4382}
4383
4384/*
4385 * Toggle full-screen mode.
4386 */
4387void flip_full_screen()
4388{
4389 if (is_full_screen()) {
4390 ShowWindow(hwnd, SW_RESTORE);
4391 } else if (IsZoomed(hwnd)) {
4392 make_full_screen();
4393 } else {
4394 SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
4395 ShowWindow(hwnd, SW_MAXIMIZE);
4396 }
4397}