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