Some Windows keymaps, it turns out, don't translate the key
[u/mdw/putty] / windows / window.c
index ed6914c..86cece8 100644 (file)
 #include <limits.h>
 #include <assert.h>
 
+#ifndef NO_MULTIMON
+#define COMPILE_MULTIMON_STUBS
+#endif
+
 #define PUTTY_DO_GLOBALS              /* actually _define_ globals */
 #include "putty.h"
 #include "terminal.h"
 #include "win_res.h"
 
 #ifndef NO_MULTIMON
-#if WINVER < 0x0500
-#define COMPILE_MULTIMON_STUBS
 #include <multimon.h>
 #endif
-#endif
 
 #include <imm.h>
 #include <commctrl.h>
@@ -86,6 +87,7 @@ static void another_font(int);
 static void deinit_fonts(void);
 static void set_input_locale(HKL);
 static void update_savedsess_menu(void);
+static void init_flashwindow(void);
 
 static int is_full_screen(void);
 static void make_full_screen(void);
@@ -100,7 +102,10 @@ static int offset_width, offset_height;
 static int was_zoomed = 0;
 static int prev_rows, prev_cols;
   
-static void enact_netevent(WPARAM, LPARAM);
+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 sys_cursor_update(void);
 static int is_shift_pressed(void);
@@ -214,12 +219,7 @@ static void start_backend(void)
      * Select protocol. This is farmed out into a table in a
      * separate file to enable an ssh-free variant.
      */
-    back = NULL;
-    for (i = 0; backends[i].backend != NULL; i++)
-       if (backends[i].protocol == cfg.protocol) {
-           back = backends[i].backend;
-           break;
-       }
+    back = backend_from_proto(cfg.protocol);
     if (back == NULL) {
        char *str = dupprintf("%s Internal Error", appname);
        MessageBox(NULL, "Unsupported protocol number found",
@@ -294,6 +294,7 @@ static void close_session(void)
        back->free(backhandle);
        backhandle = NULL;
        back = NULL;
+        term_provide_resize_fn(term, NULL, NULL);
        update_specials_menu(NULL);
     }
 
@@ -347,23 +348,26 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 
     init_help();
 
+    init_flashwindow();
+
     /*
      * Process the command line.
      */
     {
        char *p;
        int got_host = 0;
+       /* By default, we bring up the config dialog, rather than launching
+        * a session. This gets set to TRUE if something happens to change
+        * that (e.g., a hostname is specified on the command-line). */
+       int allow_launch = FALSE;
 
        default_protocol = be_default_protocol;
        /* Find the appropriate default port. */
        {
-           int i;
+           Backend *b = backend_from_proto(default_protocol);
            default_port = 0; /* illegal */
-           for (i = 0; backends[i].backend != NULL; i++)
-               if (backends[i].protocol == default_protocol) {
-                   default_port = backends[i].backend->default_port;
-                   break;
-               }
+           if (b)
+               default_port = b->default_port;
        }
        cfg.logtype = LGTYP_NONE;
 
@@ -389,6 +393,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
            if (!cfg_launchable(&cfg) && !do_config()) {
                cleanup_exit(0);
            }
+           allow_launch = TRUE;    /* allow it to be launched directly */
        } else if (*p == '&') {
            /*
             * An initial & means we've been given a command line
@@ -407,6 +412,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
            } else if (!do_config()) {
                cleanup_exit(0);
            }
+           allow_launch = TRUE;
        } else {
            /*
             * Otherwise, break up the command line and deal with
@@ -531,7 +537,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 
        cmdline_run_saved(&cfg);
 
-       if (!cfg_launchable(&cfg) && !do_config()) {
+       if (loaded_session || got_host)
+           allow_launch = TRUE;
+
+       if ((!allow_launch || !cfg_launchable(&cfg)) && !do_config()) {
            cleanup_exit(0);
        }
 
@@ -588,15 +597,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        }
     }
 
-    /* Check for invalid Port number (i.e. zero) */
-    if (cfg.port == 0) {
-       char *str = dupprintf("%s Internal Error", appname);
-       MessageBox(NULL, "Invalid Port Number",
-                  str, MB_OK | MB_ICONEXCLAMATION);
-       sfree(str);
-       cleanup_exit(1);
-    }
-
     if (!prev) {
        wndclass.style = 0;
        wndclass.lpfnWndProc = WndProc;
@@ -808,10 +808,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
            sfree(handles);
            if (must_close_session)
                close_session();
-           continue;
-       }
-
-       sfree(handles);
+       } else
+           sfree(handles);
 
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            if (msg.message == WM_QUIT)
@@ -832,6 +830,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        /* The messages seem unreliable; especially if we're being tricky */
        term_set_focus(term, GetForegroundWindow() == hwnd);
 
+       if (pending_netevent)
+           enact_pending_netevent();
+
        net_pending_errors();
     }
 
@@ -964,7 +965,7 @@ void update_specials_menu(void *frontend)
     for (j = 0; j < lenof(popup_menus); j++) {
        if (specials_menu) {
            /* XXX does this free up all submenus? */
-           DeleteMenu(popup_menus[j].menu, specials_menu, MF_BYCOMMAND);
+           DeleteMenu(popup_menus[j].menu, (UINT)specials_menu, MF_BYCOMMAND);
            DeleteMenu(popup_menus[j].menu, IDM_SPECIALSEP, MF_BYCOMMAND);
        }
        if (new_menu) {
@@ -1074,7 +1075,7 @@ void cmdline_error(char *fmt, ...)
 /*
  * Actually do the job requested by a WM_NETEVENT
  */
-static void enact_netevent(WPARAM wParam, LPARAM lParam)
+static void enact_pending_netevent(void)
 {
     static int reentering = 0;
     extern int select_result(WPARAM, LPARAM);
@@ -1082,8 +1083,10 @@ static void enact_netevent(WPARAM wParam, LPARAM lParam)
     if (reentering)
        return;                        /* don't unpend the pending */
 
+    pending_netevent = FALSE;
+
     reentering = 1;
-    select_result(wParam, lParam);
+    select_result(pend_netevent_wParam, pend_netevent_lParam);
     reentering = 0;
 }
 
@@ -1226,7 +1229,7 @@ static void exact_textout(HDC hdc, int x, int y, CONST RECT *lprc,
 
     gcpr.lStructSize = sizeof(gcpr);
     gcpr.lpGlyphs = (void *)buffer;
-    gcpr.lpClass = classbuffer;
+    gcpr.lpClass = (void *)classbuffer;
     gcpr.nGlyphs = cbCount;
 
     GetCharacterPlacementW(hdc, lpString, cbCount, 0, &gcpr,
@@ -1263,12 +1266,12 @@ debug(("\n"));
 
     xp = xn = x;
 
-    for (i = 0; i < cbCount ;) {
+    for (i = 0; i < (int)cbCount ;) {
        int rtl = is_rtl(lpString[i]);
 
        xn += lpDx[i];
 
-       for (j = i+1; j < cbCount; j++) {
+       for (j = i+1; j < (int)cbCount; j++) {
            if (rtl != is_rtl(lpString[j]))
                break;
            xn += lpDx[j];
@@ -2044,7 +2047,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                } else if (wParam == IDM_SAVEDSESS) {
                    unsigned int sessno = ((lParam - IDM_SAVED_MIN)
                                           / MENU_SAVED_STEP) + 1;
-                   if (sessno < sesslist.nsessions) {
+                   if (sessno < (unsigned)sesslist.nsessions) {
                        char *session = sesslist.sessions[sessno];
                        /* XXX spaces? quotes? "-load"? */
                        cl = dupprintf("putty @%s", session);
@@ -2565,7 +2568,19 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
        }
        return 0;
       case WM_NETEVENT:
-       enact_netevent(wParam, lParam);
+       /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
+        * but the only one that's likely to try to overload us is FD_READ.
+        * This means buffering just one is fine.
+        */
+       if (pending_netevent)
+           enact_pending_netevent();
+
+       pending_netevent = TRUE;
+       pend_netevent_wParam = wParam;
+       pend_netevent_lParam = lParam;
+       if (WSAGETSELECTEVENT(lParam) != FD_READ)
+           enact_pending_netevent();
+
        net_pending_errors();
        return 0;
       case WM_SETFOCUS:
@@ -3540,8 +3555,9 @@ int char_width(Context ctx, int uc) {
 
 /*
  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
- * codes. Returns number of bytes used or zero to drop the message
- * or -1 to forward the message to windows.
+ * codes. Returns number of bytes used, zero to drop the message,
+ * -1 to forward the message to Windows, or another negative number
+ * to indicate a NUL-terminated "special" string.
  */
 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                        unsigned char *output)
@@ -3961,9 +3977,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            return p - output;
        }
        if (wParam == VK_CANCEL && shift_state == 2) {  /* Ctrl-Break */
-           *p++ = 3;
-           *p++ = 0;
-           return -2;
+           if (back)
+               back->special(backhandle, TS_BRK);
+           return 0;
        }
        if (wParam == VK_PAUSE) {      /* Break/Pause */
            *p++ = 26;
@@ -3979,7 +3995,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            *p++ = 0x1F;
            return p - output;
        }
-       if (shift_state == 2 && wParam == 0xDF) {
+       if (shift_state == 2 && (wParam == 0xDF || wParam == 0xDC)) {
            *p++ = 0x1C;
            return p - output;
        }
@@ -4978,10 +4994,41 @@ void modalfatalbox(char *fmt, ...)
     cleanup_exit(1);
 }
 
+typedef BOOL (WINAPI *p_FlashWindowEx_t)(PFLASHWINFO);
+static p_FlashWindowEx_t p_FlashWindowEx = NULL;
+
+static void init_flashwindow(void)
+{
+    HMODULE user32_module = LoadLibrary("USER32.DLL");
+    if (user32_module) {
+       p_FlashWindowEx = (p_FlashWindowEx_t)
+           GetProcAddress(user32_module, "FlashWindowEx");
+    }
+}
+
+static BOOL flash_window_ex(DWORD dwFlags, UINT uCount, DWORD dwTimeout)
+{
+    if (p_FlashWindowEx) {
+       FLASHWINFO fi;
+       fi.cbSize = sizeof(fi);
+       fi.hwnd = hwnd;
+       fi.dwFlags = dwFlags;
+       fi.uCount = uCount;
+       fi.dwTimeout = dwTimeout;
+       return (*p_FlashWindowEx)(&fi);
+    }
+    else
+       return FALSE; /* shrug */
+}
+
 static void flash_window(int mode);
 static long next_flash;
 static int flashing = 0;
 
+/*
+ * Timer for platforms where we must maintain window flashing manually
+ * (e.g., Win95).
+ */
 static void flash_window_timer(void *ctx, long now)
 {
     if (flashing && now - next_flash >= 0) {
@@ -4998,21 +5045,37 @@ static void flash_window(int mode)
     if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
        /* stop */
        if (flashing) {
-           FlashWindow(hwnd, FALSE);
            flashing = 0;
+           if (p_FlashWindowEx)
+               flash_window_ex(FLASHW_STOP, 0, 0);
+           else
+               FlashWindow(hwnd, FALSE);
        }
 
     } else if (mode == 2) {
        /* start */
        if (!flashing) {
            flashing = 1;
-           FlashWindow(hwnd, TRUE);
-           next_flash = schedule_timer(450, flash_window_timer, hwnd);
+           if (p_FlashWindowEx) {
+               /* For so-called "steady" mode, we use uCount=2, which
+                * seems to be the traditional number of flashes used
+                * by user notifications (e.g., by Explorer).
+                * uCount=0 appears to enable continuous flashing, per
+                * "flashing" mode, although I haven't seen this
+                * documented. */
+               flash_window_ex(FLASHW_ALL | FLASHW_TIMER,
+                               (cfg.beep_ind == B_IND_FLASH ? 0 : 2),
+                               0 /* system cursor blink rate */);
+               /* No need to schedule timer */
+           } else {
+               FlashWindow(hwnd, TRUE);
+               next_flash = schedule_timer(450, flash_window_timer, hwnd);
+           }
        }
 
     } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
        /* maintain */
-       if (flashing) {
+       if (flashing && !p_FlashWindowEx) {
            FlashWindow(hwnd, TRUE);    /* toggle */
            next_flash = schedule_timer(450, flash_window_timer, hwnd);
        }