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