Sprinkle some header comments in various files in an attempt to explain what
[u/mdw/putty] / windows / window.c
index aa936f3..e997ff0 100644 (file)
@@ -1,3 +1,8 @@
+/*
+ * window.c - the PuTTY(tel) main program, which runs a PuTTY terminal
+ * emulator and backend in a window.
+ */
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
@@ -40,6 +45,7 @@
 #define IDM_COPYALL   0x0170
 #define IDM_FULLSCREEN 0x0180
 #define IDM_PASTE     0x0190
+#define IDM_SPECIALSEP 0x0200
 
 #define IDM_SPECIAL_MIN 0x0400
 #define IDM_SPECIAL_MAX 0x0800
@@ -111,15 +117,15 @@ static struct unicode_data ucsdata;
 static int session_closed;
 static int reconfiguring = FALSE;
 
-static const struct telnet_special *specials;
-static int n_specials;
+static const struct telnet_special *specials = NULL;
+static HMENU specials_menu = NULL;
+static int n_specials = 0;
 
 #define TIMING_TIMER_ID 1234
 static long timing_next_time;
 
 static struct {
     HMENU menu;
-    int specials_submenu_pos;
 } popup_menus[2];
 enum { SYSMENU, CTXMENU };
 static HMENU savedsess_menu;
@@ -764,7 +770,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
            m = popup_menus[j].menu;
 
            AppendMenu(m, MF_SEPARATOR, 0, 0);
-           popup_menus[j].specials_submenu_pos = GetMenuItemCount(m);
            AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
            AppendMenu(m, MF_SEPARATOR, 0, 0);
            AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
@@ -796,11 +801,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
     set_input_locale(GetKeyboardLayout(0));
 
     /*
-     * Open the initial log file if there is one.
-     */
-    logfopen(logctx);
-
-    /*
      * Finally show the window!
      */
     ShowWindow(hwnd, show);
@@ -919,8 +919,7 @@ static void update_savedsess_menu(void)
  */
 void update_specials_menu(void *frontend)
 {
-    HMENU p;
-    int menu_already_exists = (specials != NULL);
+    HMENU new_menu;
     int i, j;
 
     if (back)
@@ -933,30 +932,30 @@ void update_specials_menu(void *frontend)
         * here's a lame "stack" that will do for now. */
        HMENU saved_menu = NULL;
        int nesting = 1;
-       p = CreatePopupMenu();
+       new_menu = CreatePopupMenu();
        for (i = 0; nesting > 0; i++) {
            assert(IDM_SPECIAL_MIN + 0x10 * i < IDM_SPECIAL_MAX);
            switch (specials[i].code) {
              case TS_SEP:
-               AppendMenu(p, MF_SEPARATOR, 0, 0);
+               AppendMenu(new_menu, MF_SEPARATOR, 0, 0);
                break;
              case TS_SUBMENU:
                assert(nesting < 2);
                nesting++;
-               saved_menu = p; /* XXX lame stacking */
-               p = CreatePopupMenu();
+               saved_menu = new_menu; /* XXX lame stacking */
+               new_menu = CreatePopupMenu();
                AppendMenu(saved_menu, MF_POPUP | MF_ENABLED,
-                          (UINT) p, specials[i].name);
+                          (UINT) new_menu, specials[i].name);
                break;
              case TS_EXITMENU:
                nesting--;
                if (nesting) {
-                   p = saved_menu; /* XXX lame stacking */
+                   new_menu = saved_menu; /* XXX lame stacking */
                    saved_menu = NULL;
                }
                break;
              default:
-               AppendMenu(p, MF_ENABLED, IDM_SPECIAL_MIN + 0x10 * i,
+               AppendMenu(new_menu, MF_ENABLED, IDM_SPECIAL_MIN + 0x10 * i,
                           specials[i].name);
                break;
            }
@@ -964,30 +963,25 @@ void update_specials_menu(void *frontend)
        /* Squirrel the highest special. */
        n_specials = i - 1;
     } else {
-       p = NULL;
+       new_menu = NULL;
        n_specials = 0;
     }
 
     for (j = 0; j < lenof(popup_menus); j++) {
-       if (menu_already_exists) {
+       if (specials_menu) {
            /* XXX does this free up all submenus? */
-           DeleteMenu(popup_menus[j].menu,
-                      popup_menus[j].specials_submenu_pos,
-                      MF_BYPOSITION);
-           DeleteMenu(popup_menus[j].menu,
-                      popup_menus[j].specials_submenu_pos,
-                      MF_BYPOSITION);
+           DeleteMenu(popup_menus[j].menu, specials_menu, MF_BYCOMMAND);
+           DeleteMenu(popup_menus[j].menu, IDM_SPECIALSEP, MF_BYCOMMAND);
        }
-       if (specials) {
-           InsertMenu(popup_menus[j].menu,
-                      popup_menus[j].specials_submenu_pos,
-                      MF_BYPOSITION | MF_SEPARATOR, 0, 0);
-           InsertMenu(popup_menus[j].menu,
-                      popup_menus[j].specials_submenu_pos,
-                      MF_BYPOSITION | MF_POPUP | MF_ENABLED,
-                      (UINT) p, "S&pecial Command");
+       if (new_menu) {
+           InsertMenu(popup_menus[j].menu, IDM_SHOWLOG,
+                      MF_BYCOMMAND | MF_POPUP | MF_ENABLED,
+                      (UINT) new_menu, "S&pecial Command");
+           InsertMenu(popup_menus[j].menu, IDM_SHOWLOG,
+                      MF_BYCOMMAND | MF_SEPARATOR, IDM_SPECIALSEP, 0);
        }
     }
+    specials_menu = new_menu;
 }
 
 static void update_mouse_pointer(void)
@@ -1015,7 +1009,7 @@ static void update_mouse_pointer(void)
     }
     {
        HCURSOR cursor = LoadCursor(NULL, curstype);
-       SetClassLong(hwnd, GCL_HCURSOR, (LONG)cursor);
+       SetClassLongPtr(hwnd, GCLP_HCURSOR, (LONG_PTR)cursor);
        SetCursor(cursor); /* force redraw of cursor at current posn */
     }
     if (force_visible != forced_visible) {
@@ -1632,8 +1626,8 @@ static void reset_window(int reinit) {
 #endif
            }
        } else {
-           if (  font_width != win_width/term->cols || 
-                 font_height != win_height/term->rows) {
+           if (  font_width * term->cols != win_width || 
+                 font_height * term->rows != win_height) {
                /* Our only choice at this point is to change the 
                 * size of the terminal; Oh well.
                 */
@@ -1998,6 +1992,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
          case IDM_RESTART:
            if (!back) {
                logevent(NULL, "----- Session restarted -----");
+               term_pwron(term, FALSE);
                start_backend();
            }
 
@@ -2165,7 +2160,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            term_clrsb(term);
            break;
          case IDM_RESET:
-           term_pwron(term);
+           term_pwron(term, TRUE);
            if (ldisc)
                ldisc_send(ldisc, NULL, 0, 0);
            break;
@@ -3733,31 +3728,31 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        if (cfg.nethack_keypad && !left_alt) {
            switch (wParam) {
              case VK_NUMPAD1:
-               *p++ = shift_state ? 'B' : 'b';
+               *p++ = "bB\002\002"[shift_state & 3];
                return p - output;
              case VK_NUMPAD2:
-               *p++ = shift_state ? 'J' : 'j';
+               *p++ = "jJ\012\012"[shift_state & 3];
                return p - output;
              case VK_NUMPAD3:
-               *p++ = shift_state ? 'N' : 'n';
+               *p++ = "nN\016\016"[shift_state & 3];
                return p - output;
              case VK_NUMPAD4:
-               *p++ = shift_state ? 'H' : 'h';
+               *p++ = "hH\010\010"[shift_state & 3];
                return p - output;
              case VK_NUMPAD5:
                *p++ = shift_state ? '.' : '.';
                return p - output;
              case VK_NUMPAD6:
-               *p++ = shift_state ? 'L' : 'l';
+               *p++ = "lL\014\014"[shift_state & 3];
                return p - output;
              case VK_NUMPAD7:
-               *p++ = shift_state ? 'Y' : 'y';
+               *p++ = "yY\031\031"[shift_state & 3];
                return p - output;
              case VK_NUMPAD8:
-               *p++ = shift_state ? 'K' : 'k';
+               *p++ = "kK\013\013"[shift_state & 3];
                return p - output;
              case VK_NUMPAD9:
-               *p++ = shift_state ? 'U' : 'u';
+               *p++ = "uU\025\025"[shift_state & 3];
                return p - output;
            }
        }
@@ -4410,6 +4405,12 @@ void palette_set(void *frontend, int n, int r, int g, int b)
        UnrealizeObject(pal);
        RealizePalette(hdc);
        free_ctx(hdc);
+    } else {
+       if (n == (ATTR_DEFBG>>ATTR_BGSHIFT))
+           /* If Default Background changes, we need to ensure any
+            * space between the text area and the window border is
+            * redrawn. */
+           InvalidateRect(hwnd, NULL, TRUE);
     }
 }
 
@@ -4438,6 +4439,10 @@ void palette_reset(void *frontend)
        hdc = get_ctx(frontend);
        RealizePalette(hdc);
        free_ctx(hdc);
+    } else {
+       /* Default Background may have changed. Ensure any space between
+        * text area and window border is redrawn. */
+       InvalidateRect(hwnd, NULL, TRUE);
     }
 }
 
@@ -4473,7 +4478,7 @@ void write_aclip(void *frontend, char *data, int len, int must_deselect)
 /*
  * Note: unlike write_aclip() this will not append a nul.
  */
-void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
+void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_deselect)
 {
     HGLOBAL clipdata, clipdata2, clipdata3;
     int len2;
@@ -4509,14 +4514,83 @@ void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
        int rtfsize = 0;
        int multilen, blen, alen, totallen, i;
        char before[16], after[4];
+       int fgcolour,  lastfgcolour  = 0;
+       int bgcolour,  lastbgcolour  = 0;
+       int attrBold,  lastAttrBold  = 0;
+       int attrUnder, lastAttrUnder = 0;
+       int palette[NALLCOLOURS];
+       int numcolours;
 
        get_unitab(CP_ACP, unitab, 0);
 
        rtfsize = 100 + strlen(cfg.font.name);
        rtf = snewn(rtfsize, char);
-       sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0 ",
-               GetACP(), cfg.font.name);
-       rtflen = strlen(rtf);
+       rtflen = sprintf(rtf, "{\\rtf1\\ansi\\deff0{\\fonttbl\\f0\\fmodern %s;}\\f0\\fs%d",
+                        cfg.font.name, cfg.font.height*2);
+
+       /*
+        * Add colour palette
+        * {\colortbl ;\red255\green0\blue0;\red0\green0\blue128;}
+        */
+
+       /*
+        * First - Determine all colours in use
+        *    o  Foregound and background colours share the same palette
+        */
+       if (attr) {
+           memset(palette, 0, sizeof(palette));
+           for (i = 0; i < (len-1); i++) {
+               fgcolour = ((attr[i] & ATTR_FGMASK) >> ATTR_FGSHIFT);
+               bgcolour = ((attr[i] & ATTR_BGMASK) >> ATTR_BGSHIFT);
+
+               if (attr[i] & ATTR_REVERSE) {
+                   int tmpcolour = fgcolour;   /* Swap foreground and background */
+                   fgcolour = bgcolour;
+                   bgcolour = tmpcolour;
+               }
+
+               if (bold_mode == BOLD_COLOURS && (attr[i] & ATTR_BOLD)) {
+                   if (fgcolour  <   8)        /* ANSI colours */
+                       fgcolour +=   8;
+                   else if (fgcolour >= 256)   /* Default colours */
+                       fgcolour ++;
+               }
+
+               if (attr[i] & ATTR_BLINK) {
+                   if (bgcolour  <   8)        /* ANSI colours */
+                       bgcolour +=   8;
+                   else if (bgcolour >= 256)   /* Default colours */
+                       bgcolour ++;
+               }
+
+               palette[fgcolour]++;
+               palette[bgcolour]++;
+           }
+
+           /*
+            * Next - Create a reduced palette
+            */
+           numcolours = 0;
+           for (i = 0; i < NALLCOLOURS; i++) {
+               if (palette[i] != 0)
+                   palette[i]  = ++numcolours;
+           }
+
+           /*
+            * Finally - Write the colour table
+            */
+           rtf = sresize(rtf, rtfsize + (numcolours * 25), char);
+           strcat(rtf, "{\\colortbl ;");
+           rtflen = strlen(rtf);
+
+           for (i = 0; i < NALLCOLOURS; i++) {
+               if (palette[i] != 0) {
+                   rtflen += sprintf(&rtf[rtflen], "\\red%d\\green%d\\blue%d;", defpal[i].rgbtRed, defpal[i].rgbtGreen, defpal[i].rgbtBlue);
+               }
+           }
+           strcpy(&rtf[rtflen], "}");
+           rtflen ++;
+       }
 
        /*
         * We want to construct a piece of RTF that specifies the
@@ -4543,7 +4617,96 @@ void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
                tdata[tindex+1] == '\n') {
                tindex++;
                uindex++;
+            }
+
+            /*
+             * Set text attributes
+             */
+            if (attr) {
+                if (rtfsize < rtflen + 64) {
+                   rtfsize = rtflen + 512;
+                   rtf = sresize(rtf, rtfsize, char);
+                }
+
+                /*
+                 * Determine foreground and background colours
+                 */
+                fgcolour = ((attr[tindex] & ATTR_FGMASK) >> ATTR_FGSHIFT);
+                bgcolour = ((attr[tindex] & ATTR_BGMASK) >> ATTR_BGSHIFT);
+
+               if (attr[tindex] & ATTR_REVERSE) {
+                   int tmpcolour = fgcolour;       /* Swap foreground and background */
+                   fgcolour = bgcolour;
+                   bgcolour = tmpcolour;
+               }
+
+               if (bold_mode == BOLD_COLOURS && (attr[tindex] & ATTR_BOLD)) {
+                   if (fgcolour  <   8)            /* ANSI colours */
+                       fgcolour +=   8;
+                   else if (fgcolour >= 256)       /* Default colours */
+                       fgcolour ++;
+                }
+
+               if (attr[tindex] & ATTR_BLINK) {
+                   if (bgcolour  <   8)            /* ANSI colours */
+                       bgcolour +=   8;
+                   else if (bgcolour >= 256)       /* Default colours */
+                       bgcolour ++;
+                }
+
+                /*
+                 * Collect other attributes
+                 */
+               if (bold_mode != BOLD_COLOURS)
+                   attrBold  = attr[tindex] & ATTR_BOLD;
+               else
+                   attrBold  = 0;
+                
+               attrUnder = attr[tindex] & ATTR_UNDER;
+
+                /*
+                 * Reverse video
+                *   o  If video isn't reversed, ignore colour attributes for default foregound
+                *      or background.
+                *   o  Special case where bolded text is displayed using the default foregound
+                *      and background colours - force to bolded RTF.
+                 */
+               if (!(attr[tindex] & ATTR_REVERSE)) {
+                   if (bgcolour >= 256)            /* Default color */
+                       bgcolour  = -1;             /* No coloring */
+
+                   if (fgcolour >= 256) {          /* Default colour */
+                       if (bold_mode == BOLD_COLOURS && (fgcolour & 1) && bgcolour == -1)
+                           attrBold = ATTR_BOLD;   /* Emphasize text with bold attribute */
+
+                       fgcolour  = -1;             /* No coloring */
+                   }
+               }
+
+                /*
+                 * Write RTF text attributes
+                 */
+               if (lastfgcolour != fgcolour) {
+                    lastfgcolour  = fgcolour;
+                   rtflen       += sprintf(&rtf[rtflen], "\\cf%d ", (fgcolour >= 0) ? palette[fgcolour] : 0);
+                }
+
+                if (lastbgcolour != bgcolour) {
+                    lastbgcolour  = bgcolour;
+                    rtflen       += sprintf(&rtf[rtflen], "\\highlight%d ", (bgcolour >= 0) ? palette[bgcolour] : 0);
+                }
+
+               if (lastAttrBold != attrBold) {
+                   lastAttrBold  = attrBold;
+                   rtflen       += sprintf(&rtf[rtflen], "%s", attrBold ? "\\b " : "\\b0 ");
+               }
+
+                if (lastAttrUnder != attrUnder) {
+                    lastAttrUnder  = attrUnder;
+                    rtflen        += sprintf(&rtf[rtflen], "%s", attrUnder ? "\\ul " : "\\ulnone ");
+                }
            }
+
            if (unitab[tdata[tindex]] == udata[uindex]) {
                multilen = 1;
                before[0] = '\0';
@@ -4602,12 +4765,13 @@ void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
            uindex++;
        }
 
-       strcpy(rtf + rtflen, "}");
-       rtflen += 2;
+        rtf[rtflen++] = '}';          /* Terminate RTF stream */
+        rtf[rtflen++] = '\0';
+        rtf[rtflen++] = '\0';
 
        clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
        if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
-           strcpy(lock3, rtf);
+           memcpy(lock3, rtf, rtflen);
            GlobalUnlock(clipdata3);
        }
        sfree(rtf);
@@ -5004,6 +5168,10 @@ static void make_full_screen()
                        ss.bottom - ss.top,
                        SWP_FRAMECHANGED);
 
+    /* We may have changed size as a result */
+
+    reset_window(0);
+
     /* Tick the menu item in the System menu. */
     CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
                  MF_CHECKED);