+ /*
+ * The above MessageBeep call takes time, so we record the
+ * time _after_ it finishes rather than before it starts.
+ */
+ lastbeep = GetTickCount();
+ } else if (mode == BELL_WAVEFILE) {
+ if (!PlaySound(cfg.bell_wavefile.path, NULL,
+ SND_ASYNC | SND_FILENAME)) {
+ char buf[sizeof(cfg.bell_wavefile.path) + 80];
+ char otherbuf[100];
+ sprintf(buf, "Unable to play sound file\n%s\n"
+ "Using default sound instead", cfg.bell_wavefile.path);
+ sprintf(otherbuf, "%.70s Sound Error", appname);
+ MessageBox(hwnd, buf, otherbuf,
+ MB_OK | MB_ICONEXCLAMATION);
+ cfg.beep = BELL_DEFAULT;
+ }
+ } else if (mode == BELL_PCSPEAKER) {
+ static long lastbeep = 0;
+ long beepdiff;
+
+ beepdiff = GetTickCount() - lastbeep;
+ if (beepdiff >= 0 && beepdiff < 50)
+ return;
+
+ /*
+ * We must beep in different ways depending on whether this
+ * is a 95-series or NT-series OS.
+ */
+ if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT)
+ Beep(800, 100);
+ else
+ MessageBeep(-1);
+ lastbeep = GetTickCount();
+ }
+ /* Otherwise, either visual bell or disabled; do nothing here */
+ if (!term->has_focus) {
+ flash_window(2); /* start */
+ }
+}
+
+/*
+ * Minimise or restore the window in response to a server-side
+ * request.
+ */
+void set_iconic(void *frontend, 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(void *frontend, int x, int y)
+{
+ if (cfg.resize_action == RESIZE_DISABLED ||
+ cfg.resize_action == RESIZE_FONT ||
+ IsZoomed(hwnd))
+ return;
+
+ 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(void *frontend, 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 *frontend)
+{
+ InvalidateRect(hwnd, NULL, TRUE);
+}
+
+/*
+ * Maximise or restore the window in response to a server-side
+ * request.
+ */
+void set_zoomed(void *frontend, 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 *frontend)
+{
+ return IsIconic(hwnd);
+}
+
+/*
+ * Report the window's position, for terminal reports.
+ */
+void get_window_pos(void *frontend, 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(void *frontend, 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(void *frontend, int icon)
+{
+ return icon ? icon_name : window_name;
+}
+
+/*
+ * See if we're in full-screen mode.
+ */
+static int is_full_screen()
+{
+ if (!IsZoomed(hwnd))
+ return FALSE;
+ if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
+ return FALSE;
+ return TRUE;
+}
+
+/* Get the rect/size of a full screen window using the nearest available
+ * monitor in multimon systems; default to something sensible if only
+ * one monitor is present. */
+static int get_fullscreen_rect(RECT * ss)
+{
+#if defined(MONITOR_DEFAULTTONEAREST) && !defined(NO_MULTIMON)
+ HMONITOR mon;
+ MONITORINFO mi;
+ mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
+ mi.cbSize = sizeof(mi);
+ GetMonitorInfo(mon, &mi);
+
+ /* structure copy */
+ *ss = mi.rcMonitor;
+ return TRUE;
+#else
+/* could also use code like this:
+ ss->left = ss->top = 0;
+ ss->right = GetSystemMetrics(SM_CXSCREEN);
+ ss->bottom = GetSystemMetrics(SM_CYSCREEN);
+*/
+ return GetClientRect(GetDesktopWindow(), ss);
+#endif
+}
+
+
+/*
+ * Go full-screen. This should only be called when we are already
+ * maximised.
+ */
+static void make_full_screen()
+{
+ DWORD style;
+ RECT ss;
+
+ assert(IsZoomed(hwnd));
+
+ if (is_full_screen())
+ return;
+
+ /* 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. */
+ get_fullscreen_rect(&ss);
+ SetWindowPos(hwnd, HWND_TOP, ss.left, ss.top,
+ ss.right - ss.left,
+ ss.bottom - ss.top,
+ SWP_FRAMECHANGED);
+
+ /* Tick the menu item in the System menu. */
+ CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
+ MF_CHECKED);
+}
+
+/*
+ * Clear the full-screen attributes.
+ */
+static 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()
+{
+ if (is_full_screen()) {
+ ShowWindow(hwnd, SW_RESTORE);
+ } else if (IsZoomed(hwnd)) {
+ make_full_screen();
+ } else {
+ SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
+ ShowWindow(hwnd, SW_MAXIMIZE);
+ }
+}
+
+void frontend_keypress(void *handle)
+{
+ /*
+ * Keypress termination in non-Close-On-Exit mode is not
+ * currently supported in PuTTY proper, because the window
+ * always has a perfectly good Close button anyway. So we do
+ * nothing here.
+ */
+ return;
+}
+
+int from_backend(void *frontend, int is_stderr, const char *data, int len)
+{
+ return term_data(term, is_stderr, data, len);
+}
+
+void agent_schedule_callback(void (*callback)(void *, void *, int),
+ void *callback_ctx, void *data, int len)
+{
+ struct agent_callback *c = snew(struct agent_callback);
+ c->callback = callback;
+ c->callback_ctx = callback_ctx;
+ c->data = data;
+ c->len = len;
+ PostMessage(hwnd, WM_AGENT_CALLBACK, 0, (LPARAM)c);