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