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