#include <windows.h>
#include <imm.h>
#include <commctrl.h>
+#include <richedit.h>
#include <mmsystem.h>
#ifndef AUTO_WINSOCK
#ifdef WINSOCK_TWO
#include <winsock.h>
#endif
#endif
+
+#if WINVER < 0x0500
+#define COMPILE_MULTIMON_STUBS
+#include <multimon.h>
+#endif
+
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
+#include <assert.h>
#define PUTTY_DO_GLOBALS /* actually _define_ globals */
#include "putty.h"
#define IDM_TEL_SUSP 0x0110
#define IDM_TEL_EOR 0x0120
#define IDM_TEL_EOF 0x0130
-#define IDM_ABOUT 0x0140
-#define IDM_SAVEDSESS 0x0150
-#define IDM_COPYALL 0x0160
+#define IDM_HELP 0x0140
+#define IDM_ABOUT 0x0150
+#define IDM_SAVEDSESS 0x0160
+#define IDM_COPYALL 0x0170
+#define IDM_FULLSCREEN 0x0180
#define IDM_SESSLGP 0x0250 /* log type printable */
#define IDM_SESSLGA 0x0260 /* log type all chars */
#define IDM_SAVED_MAX 0x2000
#define WM_IGNORE_CLIP (WM_XUSER + 2)
+#define WM_FULLSCR_ON_MAX (WM_XUSER + 3)
/* Needed for Chinese support and apparently not always defined. */
#ifndef VK_PROCESSKEY
static void init_fonts(int, int);
static void another_font(int);
static void deinit_fonts(void);
+static void set_input_locale(HKL);
+
+static int is_full_screen(void);
+static void make_full_screen(void);
+static void clear_full_screen(void);
+static void flip_full_screen(void);
/* Window layout information */
static void reset_window(int);
-static int full_screen = 0, extra_width, extra_height;
+static int extra_width, extra_height;
static int font_width, font_height, font_dualwidth;
static int offset_width, offset_height;
static int was_zoomed = 0;
static int prev_rows, prev_cols;
-static LONG old_wind_style;
-static WINDOWPLACEMENT old_wind_placement;
-
static int pending_netevent = 0;
static WPARAM pend_netevent_wParam = 0;
static LPARAM pend_netevent_lParam = 0;
static void enact_pending_netevent(void);
static void flash_window(int mode);
-static void flip_full_screen(void);
static time_t last_movement = 0;
#define FONT_MAXNO 0x2F
#define FONT_SHIFT 5
static HFONT fonts[FONT_MAXNO];
+static LOGFONT lfont;
static int fontflag[FONT_MAXNO];
static enum {
BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
}
}
+ /*
+ * See if we can find our Help file.
+ */
+ {
+ char b[2048], *p, *q, *r;
+ FILE *fp;
+ GetModuleFileName(NULL, b, sizeof(b) - 1);
+ r = b;
+ p = strrchr(b, '\\');
+ if (p && p >= r) r = p+1;
+ q = strrchr(b, ':');
+ if (q && q >= r) r = q+1;
+ strcpy(r, "putty.hlp");
+ if ( (fp = fopen(b, "r")) != NULL) {
+ help_path = dupstr(b);
+ fclose(fp);
+ } else
+ help_path = NULL;
+ strcpy(r, "putty.cnt");
+ if ( (fp = fopen(b, "r")) != NULL) {
+ help_has_contents = TRUE;
+ fclose(fp);
+ } else
+ help_has_contents = FALSE;
+ }
+
/*
* Process the command line.
*/
int exwinmode = 0;
if (!cfg.scrollbar)
winmode &= ~(WS_VSCROLL);
- if (cfg.locksize && cfg.lockfont)
+ if (cfg.resize_action == RESIZE_DISABLED)
winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
if (cfg.alwaysontop)
exwinmode |= WS_EX_TOPMOST;
char msg[1024], *title;
char *realhost;
- error = back->init(cfg.host, cfg.port, &realhost);
+ error = back->init(cfg.host, cfg.port, &realhost, cfg.tcp_nodelay);
if (error) {
sprintf(msg, "Unable to open connection to\n"
"%.800s\n" "%s", cfg.host, error);
session_closed = FALSE;
- /*
- * Set up the input and output buffers.
- */
- inbuf_head = 0;
- outbuf_reap = outbuf_head = 0;
-
/*
* Prepare the mouse handler.
*/
AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
AppendMenu(m, MF_SEPARATOR, 0, 0);
+ AppendMenu(m, (cfg.resize_action == RESIZE_DISABLED) ?
+ MF_GRAYED : MF_ENABLED, IDM_FULLSCREEN, "&Full Screen");
+ AppendMenu(m, MF_SEPARATOR, 0, 0);
+ if (help_path)
+ AppendMenu(m, MF_ENABLED, IDM_HELP, "&Help");
AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
}
+ /*
+ * Set up the initial input locale.
+ */
+ set_input_locale(GetKeyboardLayout(0));
+
/*
* Finally show the window!
*/
timer_id = 0;
}
HideCaret(hwnd);
- if (inbuf_head)
- term_out();
+ term_out();
term_update();
ShowCaret(hwnd);
flash_window(1); /* maintain */
+ /* The messages seem unreliable; especially if we're being tricky */
+ has_focus = (GetForegroundWindow() == hwnd);
+
if (in_vbell)
/* Hmm, term_update didn't want to do an update too soon ... */
timer_id = SetTimer(hwnd, 1, 50, NULL);
f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
+ lfont.lfHeight = font_height;
+ lfont.lfWidth = font_width;
+ lfont.lfEscapement = 0;
+ lfont.lfOrientation = 0;
+ lfont.lfWeight = fw_dontcare;
+ lfont.lfItalic = FALSE;
+ lfont.lfUnderline = FALSE;
+ lfont.lfStrikeOut = FALSE;
+ lfont.lfCharSet = cfg.fontcharset;
+ lfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
+ lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ lfont.lfQuality = DEFAULT_QUALITY;
+ lfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
+ strncpy(lfont.lfFaceName, cfg.font, LF_FACESIZE);
+
SelectObject(hdc, fonts[FONT_NORMAL]);
GetTextMetrics(hdc, &tm);
/* If the window is maximized supress resizing attempts */
if (IsZoomed(hwnd)) {
- if (cfg.lockfont)
+ if (cfg.resize_action == RESIZE_TERM)
return;
}
- if (cfg.lockfont && cfg.locksize) return;
+ if (cfg.resize_action == RESIZE_DISABLED) return;
if (h == rows && w == cols) return;
/* Sanity checks ... */
term_size(h, w, cfg.savelines);
- if (cfg.lockfont) {
+ if (cfg.resize_action != RESIZE_FONT && !IsZoomed(hwnd)) {
width = extra_width + font_width * w;
height = extra_height + font_height * h;
win_width = cr.right - cr.left;
win_height = cr.bottom - cr.top;
+ if (cfg.resize_action == RESIZE_DISABLED) reinit = 2;
+
/* Are we being forced to reload the fonts ? */
if (reinit>1) {
#ifdef RDB_DEBUG_PATCH
extra_width = wr.right - wr.left - cr.right + cr.left;
extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
- if (!cfg.lockfont) {
+ if (cfg.resize_action != RESIZE_TERM) {
if ( font_width != win_width/cols ||
font_height != win_height/rows) {
deinit_fonts();
font_height*rows + extra_height,
SWP_NOMOVE | SWP_NOZORDER);
}
+
+ InvalidateRect(hwnd, NULL, TRUE);
return;
}
* window. But that may be too big for the screen which forces us
* to change the terminal.
*/
- if ((cfg.lockfont && reinit==0) || reinit>0) {
+ if ((cfg.resize_action == RESIZE_TERM && reinit<=0) ||
+ (cfg.resize_action == RESIZE_EITHER && reinit<0) ||
+ reinit>0) {
offset_width = offset_height = cfg.window_border;
extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
/* Grrr too big */
if ( rows > height || cols > width ) {
- if ( height > rows ) height = rows;
- if ( width > cols ) width = cols;
- term_size(height, width, cfg.savelines);
+ if (cfg.resize_action == RESIZE_EITHER) {
+ /* Make the font the biggest we can */
+ if (cols > width)
+ font_width = (ss.right - ss.left - extra_width)/cols;
+ if (rows > height)
+ font_height = (ss.bottom - ss.top - extra_height)/rows;
+
+ deinit_fonts();
+ init_fonts(font_width, font_height);
+
+ width = (ss.right - ss.left - extra_width) / font_width;
+ height = (ss.bottom - ss.top - extra_height) / font_height;
+ } else {
+ if ( height > rows ) height = rows;
+ if ( width > cols ) width = cols;
+ term_size(height, width, cfg.savelines);
#ifdef RDB_DEBUG_PATCH
- debug((27, "reset_window() -> term resize to (%d,%d)",
- height, width));
+ debug((27, "reset_window() -> term resize to (%d,%d)",
+ height, width));
#endif
+ }
}
SetWindowPos(hwnd, NULL, 0, 0,
}
}
-static void click(Mouse_Button b, int x, int y, int shift, int ctrl)
+static void set_input_locale(HKL kl)
+{
+ char lbuf[20];
+
+ GetLocaleInfo(LOWORD(kl), LOCALE_IDEFAULTANSICODEPAGE,
+ lbuf, sizeof(lbuf));
+
+ kbd_codepage = atoi(lbuf);
+}
+
+static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
{
int thistime = GetMessageTime();
if (send_raw_mouse && !(cfg.mouse_override && shift)) {
lastbtn = MBT_NOTHING;
- term_mouse(b, MA_CLICK, x, y, shift, ctrl);
+ term_mouse(b, MA_CLICK, x, y, shift, ctrl, alt);
return;
}
lastact = MA_CLICK;
}
if (lastact != MA_NOTHING)
- term_mouse(b, lastact, x, y, shift, ctrl);
+ term_mouse(b, lastact, x, y, shift, ctrl, alt);
lasttime = thistime;
}
cursor_visible = show;
}
+static int is_alt_pressed(void)
+{
+ BYTE keystate[256];
+ int r = GetKeyboardState(keystate);
+ if (!r)
+ return FALSE;
+ if (keystate[VK_MENU] & 0x80)
+ return TRUE;
+ if (keystate[VK_RMENU] & 0x80)
+ return TRUE;
+ return FALSE;
+}
+
+static int resizing;
+
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
HDC hdc;
static int ignore_clip = FALSE;
- static int resizing = FALSE;
static int need_backend_resize = FALSE;
+ static int fullscr_on_max = FALSE;
switch (message) {
case WM_TIMER:
if (pending_netevent)
enact_pending_netevent();
- if (inbuf_head)
- term_out();
+ term_out();
noise_regular();
HideCaret(hwnd);
term_update();
last_movement = now;
}
}
+ net_pending_errors();
return 0;
case WM_CREATE:
break;
sprintf(c, "putty &%p", filemap);
cl = c;
} else if (wParam == IDM_SAVEDSESS) {
- char *session =
- sessions[(lParam - IDM_SAVED_MIN) / 16];
- cl = smalloc(16 + strlen(session)); /* 8, but play safe */
- if (!cl)
- cl = NULL; /* not a very important failure mode */
- else {
- sprintf(cl, "putty @%s", session);
- freecl = TRUE;
- }
+ if ((lParam - IDM_SAVED_MIN) / 16 < nsessions) {
+ char *session =
+ sessions[(lParam - IDM_SAVED_MIN) / 16];
+ cl = smalloc(16 + strlen(session));
+ /* 8, but play safe */
+ if (!cl)
+ cl = NULL;
+ /* not a very important failure mode */
+ else {
+ sprintf(cl, "putty @%s", session);
+ freecl = TRUE;
+ }
+ } else
+ break;
} else
cl = NULL;
if (!do_reconfig(hwnd))
break;
+ {
+ /* Disable full-screen if resizing forbidden */
+ HMENU m = GetSystemMenu (hwnd, FALSE);
+ EnableMenuItem(m, IDM_FULLSCREEN, MF_BYCOMMAND |
+ (cfg.resize_action == RESIZE_DISABLED)
+ ? MF_GRAYED : MF_ENABLED);
+ /* Gracefully unzoom if necessary */
+ if (IsZoomed(hwnd) &&
+ (cfg.resize_action == RESIZE_DISABLED)) {
+ ShowWindow(hwnd, SW_RESTORE);
+ }
+ }
+
if (strcmp(prev_cfg.logfilename, cfg.logfilename) ||
prev_cfg.logtype != cfg.logtype) {
logfclose(); /* reset logging */
* Flush the line discipline's edit buffer in the
* case where local editing has just been disabled.
*/
- ldisc_send(NULL, 0);
+ ldisc_send(NULL, 0, 0);
if (pal)
DeleteObject(pal);
logpal = NULL;
if (cfg.height != prev_cfg.height ||
cfg.width != prev_cfg.width ||
cfg.savelines != prev_cfg.savelines ||
- cfg.locksize )
+ cfg.resize_action == RESIZE_FONT ||
+ (cfg.resize_action == RESIZE_EITHER && IsZoomed(hwnd)) ||
+ cfg.resize_action == RESIZE_DISABLED)
term_size(cfg.height, cfg.width, cfg.savelines);
/* Enable or disable the scroll bar, etc */
nexflag &= ~(WS_EX_CLIENTEDGE);
nflg = flag;
- if (cfg.scrollbar)
+ if (is_full_screen() ?
+ cfg.scrollbar_in_fullscreen : cfg.scrollbar)
nflg |= WS_VSCROLL;
else
nflg &= ~WS_VSCROLL;
- if (cfg.locksize && cfg.lockfont)
- nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
+
+ if (cfg.resize_action == RESIZE_DISABLED ||
+ is_full_screen())
+ nflg &= ~WS_THICKFRAME;
+ else
+ nflg |= WS_THICKFRAME;
+
+ if (cfg.resize_action == RESIZE_DISABLED)
+ nflg &= ~WS_MAXIMIZEBOX;
else
- nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
+ nflg |= WS_MAXIMIZEBOX;
if (nflg != flag || nexflag != exflag) {
if (nflg != flag)
}
/* Oops */
- if (cfg.locksize && cfg.lockfont && IsZoomed(hwnd)) {
+ if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
force_normal(hwnd);
init_lvl = 2;
}
cfg.fontcharset != prev_cfg.fontcharset ||
cfg.vtmode != prev_cfg.vtmode ||
cfg.bold_colour != prev_cfg.bold_colour ||
- (cfg.lockfont && !prev_cfg.lockfont))
+ cfg.resize_action == RESIZE_DISABLED ||
+ cfg.resize_action == RESIZE_EITHER ||
+ (cfg.resize_action != prev_cfg.resize_action))
init_lvl = 2;
InvalidateRect(hwnd, NULL, TRUE);
reset_window(init_lvl);
+ net_pending_errors();
}
break;
case IDM_COPYALL:
break;
case IDM_TEL_AYT:
back->special(TS_AYT);
+ net_pending_errors();
break;
case IDM_TEL_BRK:
back->special(TS_BRK);
+ net_pending_errors();
break;
case IDM_TEL_SYNCH:
back->special(TS_SYNCH);
+ net_pending_errors();
break;
case IDM_TEL_EC:
back->special(TS_EC);
+ net_pending_errors();
break;
case IDM_TEL_EL:
back->special(TS_EL);
+ net_pending_errors();
break;
case IDM_TEL_GA:
back->special(TS_GA);
+ net_pending_errors();
break;
case IDM_TEL_NOP:
back->special(TS_NOP);
+ net_pending_errors();
break;
case IDM_TEL_ABORT:
back->special(TS_ABORT);
+ net_pending_errors();
break;
case IDM_TEL_AO:
back->special(TS_AO);
+ net_pending_errors();
break;
case IDM_TEL_IP:
back->special(TS_IP);
+ net_pending_errors();
break;
case IDM_TEL_SUSP:
back->special(TS_SUSP);
+ net_pending_errors();
break;
case IDM_TEL_EOR:
back->special(TS_EOR);
+ net_pending_errors();
break;
case IDM_TEL_EOF:
back->special(TS_EOF);
+ net_pending_errors();
break;
case IDM_ABOUT:
showabout(hwnd);
break;
+ case IDM_HELP:
+ WinHelp(hwnd, help_path,
+ help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0);
+ break;
+ case SC_MOUSEMENU:
+ /*
+ * We get this if the System menu has been activated
+ * using the mouse.
+ */
+ show_mouseptr(1);
+ break;
case SC_KEYMENU:
/*
- * We get this if the System menu has been activated.
- * This might happen from within TranslateKey, in which
- * case it really wants to be followed by a `space'
- * character to actually _bring the menu up_ rather
- * than just sitting there in `ready to appear' state.
+ * We get this if the System menu has been activated
+ * using the keyboard. This might happen from within
+ * TranslateKey, in which case it really wants to be
+ * followed by a `space' character to actually _bring
+ * the menu up_ rather than just sitting there in
+ * `ready to appear' state.
*/
+ show_mouseptr(1); /* make sure pointer is visible */
if( lParam == 0 )
PostMessage(hwnd, WM_CHAR, ' ', 0);
break;
+ case IDM_FULLSCREEN:
+ flip_full_screen();
+ break;
default:
if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
if (send_raw_mouse) {
/* send a mouse-down followed by a mouse up */
+
term_mouse(b,
MA_CLICK,
TO_CHR_X(X_POS(lParam)),
TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
- wParam & MK_CONTROL);
+ wParam & MK_CONTROL, is_alt_pressed());
term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
- wParam & MK_CONTROL);
+ wParam & MK_CONTROL, is_alt_pressed());
} else {
/* trigger a scroll */
term_scroll(0,
case WM_RBUTTONUP:
{
int button, press;
+
switch (message) {
case WM_LBUTTONDOWN:
button = MBT_LEFT;
button = press = 0; /* shouldn't happen */
}
show_mouseptr(1);
+ /*
+ * Special case: in full-screen mode, if the left
+ * button is clicked in the very top left corner of the
+ * window, we put up the System menu instead of doing
+ * selection.
+ */
+ if (is_full_screen() && press && button == MBT_LEFT &&
+ X_POS(lParam) == 0 && Y_POS(lParam) == 0) {
+ SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0);
+ return 0;
+ }
if (press) {
click(button,
TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
- wParam & MK_SHIFT, wParam & MK_CONTROL);
+ wParam & MK_SHIFT, wParam & MK_CONTROL,
+ is_alt_pressed());
SetCapture(hwnd);
} else {
term_mouse(button, MA_RELEASE,
TO_CHR_X(X_POS(lParam)),
TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
- wParam & MK_CONTROL);
+ wParam & MK_CONTROL, is_alt_pressed());
ReleaseCapture();
}
}
b = MBT_RIGHT;
term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
- wParam & MK_CONTROL);
+ wParam & MK_CONTROL, is_alt_pressed());
}
return 0;
case WM_NCMOUSEMOVE:
* 1) Keep the sizetip uptodate
* 2) Make sure the window size is _stepped_ in units of the font size.
*/
- if (!cfg.locksize && !alt_pressed) {
+ if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
int width, height, w, h, ew, eh;
LPRECT r = (LPRECT) lParam;
- if ( !need_backend_resize &&
+ if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
(cfg.height != rows || cfg.width != cols )) {
/*
- * Great! It seems the host has been changing the terminal
- * size, well the user is now grabbing so this is probably
- * the least confusing solution in the long run even though
- * it a is suprise. Unfortunatly the only way to prevent
- * this seems to be to let the host change the window size
- * and as that's a user option we're still right back here.
- */
+ * Great! It seems that both the terminal size and the
+ * font size have been changed and the user is now dragging.
+ *
+ * It will now be difficult to get back to the configured
+ * font size!
+ *
+ * This would be easier but it seems to be too confusing.
+
term_size(cfg.height, cfg.width, cfg.savelines);
reset_window(2);
+ */
+ cfg.height=rows; cfg.width=cols;
+
InvalidateRect(hwnd, NULL, TRUE);
need_backend_resize = TRUE;
}
return rv;
}
- break;
/* break; (never reached) */
+ case WM_FULLSCR_ON_MAX:
+ fullscr_on_max = TRUE;
+ break;
case WM_SIZE:
#ifdef RDB_DEBUG_PATCH
debug((27, "WM_SIZE %s (%d,%d)",
"...",
LOWORD(lParam), HIWORD(lParam)));
#endif
- if (wParam == SIZE_MINIMIZED) {
+ if (wParam == SIZE_MINIMIZED)
SetWindowText(hwnd,
cfg.win_name_always ? window_name : icon_name);
- break;
- }
if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
SetWindowText(hwnd, window_name);
+ if (wParam == SIZE_RESTORED)
+ clear_full_screen();
+ if (wParam == SIZE_MAXIMIZED && fullscr_on_max) {
+ make_full_screen();
+ fullscr_on_max = FALSE;
+ }
- if (cfg.lockfont && cfg.locksize) {
+ if (cfg.resize_action == RESIZE_DISABLED) {
/* A resize, well it better be a minimize. */
reset_window(-1);
} else {
height = HIWORD(lParam);
if (!resizing) {
- if (wParam == SIZE_MAXIMIZED) {
+ if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
was_zoomed = 1;
prev_rows = rows;
prev_cols = cols;
- if (cfg.lockfont) {
+ if (cfg.resize_action == RESIZE_TERM) {
w = width / font_width;
if (w < 1) w = 1;
h = height / font_height;
reset_window(0);
} else if (wParam == SIZE_RESTORED && was_zoomed) {
was_zoomed = 0;
- if (cfg.lockfont)
+ if (cfg.resize_action == RESIZE_TERM)
term_size(prev_rows, prev_cols, cfg.savelines);
- reset_window(0);
+ if (cfg.resize_action != RESIZE_FONT)
+ reset_window(2);
+ else
+ reset_window(0);
}
/* This is an unexpected resize, these will normally happen
* if the window is too large. Probably either the user
* down the connection during an NT opaque drag.)
*/
if (resizing) {
- if (!cfg.locksize && !alt_pressed) {
+ if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
need_backend_resize = TRUE;
w = (width-cfg.window_border*2) / font_width;
if (w < 1) w = 1;
return DefWindowProc(hwnd, message, wParam, lParam);
if (len != 0) {
+ /*
+ * Interrupt an ongoing paste. I'm not sure
+ * this is sensible, but for the moment it's
+ * preferable to having to faff about buffering
+ * things.
+ */
+ term_nopaste();
+
/*
* We need not bother about stdin backlogs
* here, because in GUI PuTTY we can't do
* messages. We _have_ to buffer everything
* we're sent.
*/
- ldisc_send(buf, len);
+ ldisc_send(buf, len, 1);
show_mouseptr(0);
}
}
}
+ net_pending_errors();
return 0;
case WM_INPUTLANGCHANGE:
- {
- /* wParam == Font number */
- /* lParam == Locale */
- char lbuf[20];
- HKL NewInputLocale = (HKL) lParam;
-
- // lParam == GetKeyboardLayout(0);
-
- GetLocaleInfo(LOWORD(NewInputLocale),
- LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
-
- kbd_codepage = atoi(lbuf);
+ /* wParam == Font number */
+ /* lParam == Locale */
+ set_input_locale((HKL)lParam);
+ break;
+ case WM_IME_NOTIFY:
+ if(wParam == IMN_SETOPENSTATUS) {
+ HIMC hImc = ImmGetContext(hwnd);
+ ImmSetCompositionFont(hImc, &lfont);
+ ImmReleaseContext(hwnd, hImc);
+ return 0;
}
break;
case WM_IME_COMPOSITION:
if (n > 0) {
buff = (char*) smalloc(n);
ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
- luni_send((unsigned short *)buff, n / 2);
+ luni_send((unsigned short *)buff, n / 2, 1);
free(buff);
}
ImmReleaseContext(hwnd, hIMC);
buf[1] = wParam;
buf[0] = wParam >> 8;
- lpage_send(kbd_codepage, buf, 2);
+ lpage_send(kbd_codepage, buf, 2, 1);
} else {
char c = (unsigned char) wParam;
- lpage_send(kbd_codepage, &c, 1);
+ lpage_send(kbd_codepage, &c, 1, 1);
}
return (0);
case WM_CHAR:
*/
{
char c = (unsigned char)wParam;
- lpage_send(CP_ACP, &c, 1);
+ lpage_send(CP_ACP, &c, 1, 1);
}
return 0;
case WM_SETCURSOR:
/* we should have the IMM functions */
hIMC = ImmGetContext(hwnd);
cf.dwStyle = CFS_POINT;
- cf.ptCurrentPos.x = x * font_width;
- cf.ptCurrentPos.y = y * font_height;
+ cf.ptCurrentPos.x = x * font_width + offset_width;
+ cf.ptCurrentPos.y = y * font_height + offset_height;
ImmSetCompositionWindow(hIMC, &cf);
ImmReleaseContext(hwnd, hIMC);
* Record that we pressed key so the scroll window can be reset, but
* be careful to avoid Shift-UP/Down
*/
- if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
+ if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
+ wParam != VK_MENU && wParam != VK_CONTROL) {
seen_key_event = 1;
}
- /* Make sure we're not pasting */
- if (key_down)
- term_nopaste();
-
if (compose_state > 1 && left_alt)
compose_state = 0;
SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
return -1;
}
- if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter) {
- flip_full_screen();
+ if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
+ (cfg.resize_action != RESIZE_DISABLED)) {
+ if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
+ flip_full_screen();
return -1;
}
/* Control-Numlock for app-keypad mode switch */
/* Okay we've done everything interesting; let windows deal with
* the boring stuff */
{
+ BOOL capsOn=0;
+
+ /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
+ if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
+ capsOn= !left_alt;
+ keystate[VK_CAPITAL] = 0;
+ }
+
r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
#ifdef SHOW_TOASCII_RESULT
if (r == 1 && !key_down) {
#endif
if (r > 0) {
WCHAR keybuf;
+
+ /*
+ * Interrupt an ongoing paste. I'm not sure this is
+ * sensible, but for the moment it's preferable to
+ * having to faff about buffering things.
+ */
+ term_nopaste();
+
p = output;
for (i = 0; i < r; i++) {
unsigned char ch = (unsigned char) keys[i];
return 0;
}
keybuf = nc;
- luni_send(&keybuf, 1);
+ luni_send(&keybuf, 1, 1);
continue;
}
if (alt_sum) {
if (in_utf || dbcs_screenfont) {
keybuf = alt_sum;
- luni_send(&keybuf, 1);
+ luni_send(&keybuf, 1, 1);
} else {
ch = (char) alt_sum;
/*
* messages. We _have_ to buffer
* everything we're sent.
*/
- ldisc_send(&ch, 1);
+ ldisc_send(&ch, 1, 1);
}
alt_sum = 0;
} else
- lpage_send(kbd_codepage, &ch, 1);
+ lpage_send(kbd_codepage, &ch, 1, 1);
} else {
- static char cbuf[] = "\033 ";
- cbuf[1] = ch;
- lpage_send(kbd_codepage, cbuf + !left_alt,
- 1 + !!left_alt);
+ if(capsOn && ch < 0x80) {
+ WCHAR cbuf[2];
+ cbuf[0] = 27;
+ cbuf[1] = xlat_uskbd2cyrllic(ch);
+ luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
+ } else {
+ char cbuf[2];
+ cbuf[0] = '\033';
+ cbuf[1] = ch;
+ lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
+ }
}
show_mouseptr(0);
}
{
SCROLLINFO si;
- if (!cfg.scrollbar)
+ if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
return;
si.cbSize = sizeof(si);
*/
void write_clip(wchar_t * data, int len, int must_deselect)
{
- HGLOBAL clipdata;
- HGLOBAL clipdata2;
+ HGLOBAL clipdata, clipdata2, clipdata3;
int len2;
- void *lock, *lock2;
+ void *lock, *lock2, *lock3;
len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
memcpy(lock, data, len * sizeof(wchar_t));
WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
+ if (cfg.rtf_paste) {
+ wchar_t unitab[256];
+ char *rtf = NULL;
+ unsigned char *tdata = (unsigned char *)lock2;
+ wchar_t *udata = (wchar_t *)lock;
+ int rtflen = 0, uindex = 0, tindex = 0;
+ int rtfsize = 0;
+ int multilen, blen, alen, totallen, i;
+ char before[16], after[4];
+
+ get_unitab(CP_ACP, unitab, 0);
+
+ rtfsize = 100 + strlen(cfg.font);
+ rtf = smalloc(rtfsize);
+ sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
+ GetACP(), cfg.font);
+ rtflen = strlen(rtf);
+
+ /*
+ * We want to construct a piece of RTF that specifies the
+ * same Unicode text. To do this we will read back in
+ * parallel from the Unicode data in `udata' and the
+ * non-Unicode data in `tdata'. For each character in
+ * `tdata' which becomes the right thing in `udata' when
+ * looked up in `unitab', we just copy straight over from
+ * tdata. For each one that doesn't, we must WCToMB it
+ * individually and produce a \u escape sequence.
+ *
+ * It would probably be more robust to just bite the bullet
+ * and WCToMB each individual Unicode character one by one,
+ * then MBToWC each one back to see if it was an accurate
+ * translation; but that strikes me as a horrifying number
+ * of Windows API calls so I want to see if this faster way
+ * will work. If it screws up badly we can always revert to
+ * the simple and slow way.
+ */
+ while (tindex < len2 && uindex < len &&
+ tdata[tindex] && udata[uindex]) {
+ if (tindex + 1 < len2 &&
+ tdata[tindex] == '\r' &&
+ tdata[tindex+1] == '\n') {
+ tindex++;
+ uindex++;
+ }
+ if (unitab[tdata[tindex]] == udata[uindex]) {
+ multilen = 1;
+ before[0] = '\0';
+ after[0] = '\0';
+ blen = alen = 0;
+ } else {
+ multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
+ NULL, 0, NULL, NULL);
+ if (multilen != 1) {
+ blen = sprintf(before, "{\\uc%d\\u%d", multilen,
+ udata[uindex]);
+ alen = 1; strcpy(after, "}");
+ } else {
+ blen = sprintf(before, "\\u%d", udata[uindex]);
+ alen = 0; after[0] = '\0';
+ }
+ }
+ assert(tindex + multilen <= len2);
+ totallen = blen + alen;
+ for (i = 0; i < multilen; i++) {
+ if (tdata[tindex+i] == '\\' ||
+ tdata[tindex+i] == '{' ||
+ tdata[tindex+i] == '}')
+ totallen += 2;
+ else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
+ totallen += 6; /* \par\r\n */
+ else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
+ totallen += 4;
+ else
+ totallen++;
+ }
+
+ if (rtfsize < rtflen + totallen + 3) {
+ rtfsize = rtflen + totallen + 512;
+ rtf = srealloc(rtf, rtfsize);
+ }
+
+ strcpy(rtf + rtflen, before); rtflen += blen;
+ for (i = 0; i < multilen; i++) {
+ if (tdata[tindex+i] == '\\' ||
+ tdata[tindex+i] == '{' ||
+ tdata[tindex+i] == '}') {
+ rtf[rtflen++] = '\\';
+ rtf[rtflen++] = tdata[tindex+i];
+ } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
+ rtflen += sprintf(rtf+rtflen, "\\par\r\n");
+ } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
+ rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
+ } else {
+ rtf[rtflen++] = tdata[tindex+i];
+ }
+ }
+ strcpy(rtf + rtflen, after); rtflen += alen;
+
+ tindex += multilen;
+ uindex++;
+ }
+
+ strcpy(rtf + rtflen, "}");
+ rtflen += 2;
+
+ clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
+ if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
+ strcpy(lock3, rtf);
+ GlobalUnlock(clipdata3);
+ }
+ sfree(rtf);
+ } else
+ clipdata3 = NULL;
+
GlobalUnlock(clipdata);
GlobalUnlock(clipdata2);
EmptyClipboard();
SetClipboardData(CF_UNICODETEXT, clipdata);
SetClipboardData(CF_TEXT, clipdata2);
+ if (clipdata3)
+ SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
CloseClipboard();
} else {
GlobalFree(clipdata);
}
/*
- * Toggle full screen mode. Thanks to cwis@nerim.fr for the
- * implementation.
+ * Minimise or restore the window in response to a server-side
+ * request.
+ */
+void set_iconic(int iconic)
+{
+ if (IsIconic(hwnd)) {
+ if (!iconic)
+ ShowWindow(hwnd, SW_RESTORE);
+ } else {
+ if (iconic)
+ ShowWindow(hwnd, SW_MINIMIZE);
+ }
+}
+
+/*
+ * Move the window in response to a server-side request.
+ */
+void move_window(int x, int y)
+{
+ SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
+}
+
+/*
+ * Move the window to the top or bottom of the z-order in response
+ * to a server-side request.
+ */
+void set_zorder(int top)
+{
+ if (cfg.alwaysontop)
+ return; /* ignore */
+ SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE);
+}
+
+/*
+ * Refresh the window in response to a server-side request.
+ */
+void refresh_window(void)
+{
+ InvalidateRect(hwnd, NULL, TRUE);
+}
+
+/*
+ * Maximise or restore the window in response to a server-side
+ * request.
+ */
+void set_zoomed(int zoomed)
+{
+ if (IsZoomed(hwnd)) {
+ if (!zoomed)
+ ShowWindow(hwnd, SW_RESTORE);
+ } else {
+ if (zoomed)
+ ShowWindow(hwnd, SW_MAXIMIZE);
+ }
+}
+
+/*
+ * Report whether the window is iconic, for terminal reports.
+ */
+int is_iconic(void)
+{
+ return IsIconic(hwnd);
+}
+
+/*
+ * Report the window's position, for terminal reports.
+ */
+void get_window_pos(int *x, int *y)
+{
+ RECT r;
+ GetWindowRect(hwnd, &r);
+ *x = r.left;
+ *y = r.top;
+}
+
+/*
+ * Report the window's pixel size, for terminal reports.
+ */
+void get_window_pixels(int *x, int *y)
+{
+ RECT r;
+ GetWindowRect(hwnd, &r);
+ *x = r.right - r.left;
+ *y = r.bottom - r.top;
+}
+
+/*
+ * Return the window or icon title.
+ */
+char *get_window_title(int icon)
+{
+ return icon ? icon_name : window_name;
+}
+
+/*
+ * See if we're in full-screen mode.
+ */
+int is_full_screen()
+{
+ if (!IsZoomed(hwnd))
+ return FALSE;
+ if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * Go full-screen. This should only be called when we are already
+ * maximised.
+ */
+void make_full_screen()
+{
+ DWORD style;
+ int x, y, w, h;
+
+ assert(IsZoomed(hwnd));
+
+ /* Remove the window furniture. */
+ style = GetWindowLong(hwnd, GWL_STYLE);
+ style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
+ if (cfg.scrollbar_in_fullscreen)
+ style |= WS_VSCROLL;
+ else
+ style &= ~WS_VSCROLL;
+ SetWindowLong(hwnd, GWL_STYLE, style);
+
+ /* Resize ourselves to exactly cover the nearest monitor. */
+#ifdef MONITOR_DEFAULTTONEAREST
+ {
+ HMONITOR mon;
+ MONITORINFO mi;
+ mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
+ mi.cbSize = sizeof(mi);
+ GetMonitorInfo(mon, &mi);
+ x = mi.rcMonitor.left;
+ y = mi.rcMonitor.top;
+ w = mi.rcMonitor.right;
+ h = mi.rcMonitor.bottom;
+ }
+#else
+ x = y = 0;
+ w = GetSystemMetrics(SM_CXSCREEN);
+ h = GetSystemMetrics(SM_CYSCREEN);
+#endif
+ SetWindowPos(hwnd, HWND_TOP, x, y, w, h, SWP_FRAMECHANGED);
+
+ /* Tick the menu item in the System menu. */
+ CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
+ MF_CHECKED);
+}
+
+/*
+ * Clear the full-screen attributes.
+ */
+void clear_full_screen()
+{
+ DWORD oldstyle, style;
+
+ /* Reinstate the window furniture. */
+ style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
+ style |= WS_CAPTION | WS_BORDER;
+ if (cfg.resize_action == RESIZE_DISABLED)
+ style &= ~WS_THICKFRAME;
+ else
+ style |= WS_THICKFRAME;
+ if (cfg.scrollbar)
+ style |= WS_VSCROLL;
+ else
+ style &= ~WS_VSCROLL;
+ if (style != oldstyle) {
+ SetWindowLong(hwnd, GWL_STYLE, style);
+ SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
+ SWP_FRAMECHANGED);
+ }
+
+ /* Untick the menu item in the System menu. */
+ CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
+ MF_UNCHECKED);
+}
+
+/*
+ * Toggle full-screen mode.
*/
-static void flip_full_screen(void)
+void flip_full_screen()
{
- if (!full_screen) {
- int cx, cy;
-
- cx = GetSystemMetrics(SM_CXSCREEN);
- cy = GetSystemMetrics(SM_CYSCREEN);
- GetWindowPlacement(hwnd, &old_wind_placement);
- old_wind_style = GetWindowLong(hwnd, GWL_STYLE);
- SetWindowLong(hwnd, GWL_STYLE,
- old_wind_style & ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME));
- SetWindowPos(hwnd, HWND_TOP, 0, 0, cx, cy, SWP_SHOWWINDOW);
- full_screen = 1;
+ if (is_full_screen()) {
+ ShowWindow(hwnd, SW_RESTORE);
+ } else if (IsZoomed(hwnd)) {
+ make_full_screen();
} else {
- SetWindowLong(hwnd, GWL_STYLE, old_wind_style);
- SetWindowPlacement(hwnd,&old_wind_placement);
- full_screen = 0;
+ SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
+ ShowWindow(hwnd, SW_MAXIMIZE);
}
}