Subject: Re: bug#8562: Emacs 23.1 and later don't work in windows 98

From: Eli Zaretskii <[email-masked]>
Date: Sat, 01 Oct 2011 14:03:11 +0300

> Date: Tue, 07 Jun 2011 23:32:14 +0300
> From: Eli Zaretskii <[email-masked]>
> Cc: [email-masked]
> 
> > The binary works without any noticeable problem.
> 
> Thank you.  I will prepare an official patch for that, which should
> work both on Windows 9X and on the NT family of Windows, and will ask
> you to test it.

Sorry for such a long delay, I got distracted by issues that needed to
be resolved before Emacs 24 enters the pretest.

Please find below a patch that should let Emacs work correctly on
Windows 9X systems that have UNICOWS.DLL installed.  If UNICOWS.DLL is
not installed, Emacs should pop up an error dialog to that effect, and
refuse to start up.  "emacs -nw" should be possible even if that DLL
is not available.

Please give this a try, both with and without UNICOWS.DLL available,
and see if the results are good.  I will then install this in the
development trunk, and this will be available as part of Emacs 24.1.

It is best to try this in the current development code, or with the
emacs-24.0.90 pretest (which you can find on alpha.gnu.org).

Thanks again for your great help in resolving this issue.

Here's the patch to apply:

=== modified file 'src/ChangeLog'
--- src/ChangeLog	2011-09-30 20:22:01 +0000
+++ src/ChangeLog	2011-10-01 10:55:28 +0000
@@ -1,3 +1,24 @@
+2011-10-01  Eli Zaretskii  <[email-masked]>
+
+	Fix Emacs on Windows 9X (bug#8562).
+	Thanks to oslsachem <[email-masked]> for debugging this.
+
+	* w32font.c (g_b_init_is_w9x, g_b_init_get_outline_metrics_w)
+	(g_b_init_get_text_metrics_w, g_b_init_get_glyph_outline_w)
+	(g_b_init_get_glyph_outline_w): New static variables.
+	(GetOutlineTextMetricsW_Proc, GetTextMetricsW_Proc)
+	(GetGlyphOutlineW_Proc): New typedefs.
+	(w32_load_unicows_or_gdi32, get_outline_metrics_w)
+	(get_text_metrics_w, get_glyph_outline_w, globals_of_w32font): New
+	functions.
+	(w32font_open_internal, compute_metrics): Call
+	get_outline_metrics_w, get_text_metrics_w, and get_glyph_outline_w
+	instead of calling the "wide" APIs directly.
+
+	* emacs.c (main) [HAVE_NTGUI]: Call globals_of_w32font.
+
+	* w32.h (syms_of_w32font): Add prototype.
+
 2011-09-30  Paul Eggert  <[email-masked]>
 
 	* buffer.h (struct buffer): Use time_t, not int, for a time stamp.

=== modified file 'src/emacs.c'
--- src/emacs.c	2011-09-24 09:22:06 +0000
+++ src/emacs.c	2011-10-01 10:06:12 +0000
@@ -1591,6 +1591,7 @@ Using an Emacs configured with --with-x-
       /* Initialization that must be done even if the global variable
 	 initialized is non zero.  */
 #ifdef HAVE_NTGUI
+      globals_of_w32font ();
       globals_of_w32fns ();
       globals_of_w32menu ();
       globals_of_w32select ();

=== modified file 'src/w32.h'
--- src/w32.h	2011-05-04 14:03:16 +0000
+++ src/w32.h	2011-10-01 10:11:20 +0000
@@ -139,6 +139,7 @@ extern void term_w32select (void);
 extern void syms_of_w32menu (void);
 extern void globals_of_w32menu (void);
 extern void syms_of_fontset (void);
+extern void syms_of_w32font (void);
 
 extern int _sys_read_ahead (int fd);
 extern int _sys_wait_accept (int fd);

=== modified file 'src/w32font.c'
--- src/w32font.c	2011-05-12 07:07:06 +0000
+++ src/w32font.c	2011-10-01 10:43:40 +0000
@@ -145,6 +145,137 @@ struct font_callback_data
    style variations if the font name is not specified.  */
 static void list_all_matching_fonts (struct font_callback_data *);
 
+static BOOL g_b_init_is_w9x;
+static BOOL g_b_init_get_outline_metrics_w;
+static BOOL g_b_init_get_text_metrics_w;
+static BOOL g_b_init_get_glyph_outline_w;
+static BOOL g_b_init_get_glyph_outline_w;
+
+typedef UINT (WINAPI * GetOutlineTextMetricsW_Proc) (
+   HDC hdc,
+   UINT cbData,
+   LPOUTLINETEXTMETRICW lpotmw);
+typedef BOOL (WINAPI * GetTextMetricsW_Proc) (
+   HDC hdc,
+   LPTEXTMETRICW lptmw);
+typedef DWORD (WINAPI * GetGlyphOutlineW_Proc) (
+   HDC hdc,
+   UINT uChar,
+   UINT uFormat,
+   LPGLYPHMETRICS lpgm,
+   DWORD cbBuffer,
+   LPVOID lpvBuffer,
+   const MAT2 *lpmat2);
+
+/* Several "wide" functions we use to support the font backends are
+   unavailable on Windows 9X, unless UNICOWS.DLL is installed (their
+   versions in the default libraries are non-functional stubs).  On NT
+   and later systems, these functions are in GDI32.DLL.  The following
+   helper function attempts to load UNICOWS.DLL on Windows 9X, and
+   refuses to let Emacs start up if that library is not found.  On NT
+   and later versions, it simply loads GDI32.DLL, which should always
+   be available.  */
+static HMODULE
+w32_load_unicows_or_gdi32 (void)
+{
+  static BOOL is_9x = 0;
+  OSVERSIONINFO os_ver;
+  HMODULE ret;
+  if (g_b_init_is_w9x == 0)
+    {
+      g_b_init_is_w9x = 1;
+      ZeroMemory (&os_ver, sizeof (OSVERSIONINFO));
+      os_ver.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+      if (GetVersionEx (&os_ver))
+	is_9x = (os_ver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
+    }
+  if (is_9x)
+    {
+      ret = LoadLibrary ("Unicows.dll");
+      if (!ret)
+	{
+	  int button;
+
+	  button = MessageBox (NULL,
+			       "Emacs cannot load the UNICOWS.DLL library.\n"
+			       "This library is essential for using Emacs\n"
+			       "on this system.  You need to install it.\n\n"
+			       "However, you can still use Emacs by invoking\n"
+			       "it with the '-nw' command-line option.\n\n"
+			       "Emacs will exit when you click OK.",
+			       "Emacs cannot load UNICOWS.DLL",
+			       MB_ICONERROR | MB_TASKMODAL
+			       | MB_SETFOREGROUND | MB_OK);
+	  switch (button)
+	    {
+	    case IDOK:
+	    default:
+	      exit (1);
+	    }
+	}
+    }
+  else
+    ret = LoadLibrary ("Gdi32.dll");
+}
+
+/* The following 3 functions call the problematic "wide" APIs via
+   function pointers, to avoid linking against the non-standard
+   libunicows on W9X.  */
+static UINT WINAPI
+get_outline_metrics_w(HDC hdc, UINT cbData, LPOUTLINETEXTMETRICW lpotmw)
+{
+  static GetOutlineTextMetricsW_Proc s_pfn_Get_Outline_Text_MetricsW = NULL;
+  HMODULE hm_unicows = NULL;
+  if (g_b_init_get_outline_metrics_w == 0)
+    {
+      g_b_init_get_outline_metrics_w = 1;
+      hm_unicows = w32_load_unicows_or_gdi32 ();
+      if (hm_unicows)
+	s_pfn_Get_Outline_Text_MetricsW = (GetOutlineTextMetricsW_Proc)
+	  GetProcAddress (hm_unicows, "GetOutlineTextMetricsW");
+    }
+  if (s_pfn_Get_Outline_Text_MetricsW == NULL)
+    abort ();	/* cannot happen */
+  return s_pfn_Get_Outline_Text_MetricsW (hdc, cbData, lpotmw);
+}
+
+static BOOL WINAPI
+get_text_metrics_w(HDC hdc, LPTEXTMETRICW lptmw)
+{
+  static GetTextMetricsW_Proc s_pfn_Get_Text_MetricsW = NULL;
+  HMODULE hm_unicows = NULL;
+  if (g_b_init_get_text_metrics_w == 0)
+    {
+      g_b_init_get_text_metrics_w = 1;
+      hm_unicows = w32_load_unicows_or_gdi32 ();
+      if (hm_unicows)
+	s_pfn_Get_Text_MetricsW = (GetTextMetricsW_Proc)
+	  GetProcAddress (hm_unicows, "GetTextMetricsW");
+    }
+  if (s_pfn_Get_Text_MetricsW == NULL)
+    abort ();	/* cannot happen */
+  return s_pfn_Get_Text_MetricsW (hdc, lptmw);
+}
+
+static DWORD WINAPI
+get_glyph_outline_w (HDC hdc, UINT uChar, UINT uFormat, LPGLYPHMETRICS lpgm,
+		     DWORD cbBuffer, LPVOID lpvBuffer, const MAT2 *lpmat2)
+{
+  static GetGlyphOutlineW_Proc s_pfn_Get_Glyph_OutlineW = NULL;
+  HMODULE hm_unicows = NULL;
+  if (g_b_init_get_glyph_outline_w == 0)
+    {
+      g_b_init_get_glyph_outline_w = 1;
+      hm_unicows = w32_load_unicows_or_gdi32 ();
+      if (hm_unicows)
+	s_pfn_Get_Glyph_OutlineW = (GetGlyphOutlineW_Proc)
+	  GetProcAddress (hm_unicows, "GetGlyphOutlineW");
+    }
+  if (s_pfn_Get_Glyph_OutlineW == NULL)
+    abort ();	/* cannot happen */
+  return s_pfn_Get_Glyph_OutlineW (hdc, uChar, uFormat, lpgm, cbBuffer,
+				   lpvBuffer, lpmat2);
+}
 
 static int
 memq_no_quit (Lisp_Object elt, Lisp_Object list)
@@ -816,11 +947,11 @@ w32font_open_internal (FRAME_PTR f, Lisp
   old_font = SelectObject (dc, hfont);
 
   /* Try getting the outline metrics (only works for truetype fonts).  */
-  len = GetOutlineTextMetricsW (dc, 0, NULL);
+  len = get_outline_metrics_w (dc, 0, NULL);
   if (len)
     {
       metrics = (OUTLINETEXTMETRICW *) alloca (len);
-      if (GetOutlineTextMetricsW (dc, len, metrics))
+      if (get_outline_metrics_w (dc, len, metrics))
         memcpy (&w32_font->metrics, &metrics->otmTextMetrics,
 		sizeof (TEXTMETRICW));
       else
@@ -828,7 +959,7 @@ w32font_open_internal (FRAME_PTR f, Lisp
     }
 
   if (!metrics)
-    GetTextMetricsW (dc, &w32_font->metrics);
+    get_text_metrics_w (dc, &w32_font->metrics);
 
   w32_font->cached_metrics = NULL;
   w32_font->n_cache_blocks = 0;
@@ -2306,7 +2437,7 @@ compute_metrics (HDC dc, struct w32font_
   transform.eM11.value = 1;
   transform.eM22.value = 1;
 
-  if (GetGlyphOutlineW (dc, code, options, &gm, 0, NULL, &transform)
+  if (get_glyph_outline_w (dc, code, options, &gm, 0, NULL, &transform)
       != GDI_ERROR)
     {
       metrics->lbearing = gm.gmptGlyphOrigin.x;
@@ -2581,3 +2712,13 @@ versions of Windows) characters.  */);
   w32font_driver.type = Qgdi;
   register_font_driver (&w32font_driver, NULL);
 }
+
+void
+globals_of_w32font (void)
+{
+  g_b_init_is_w9x = 0;
+  g_b_init_get_outline_metrics_w = 0;
+  g_b_init_get_text_metrics_w = 0;
+  g_b_init_get_glyph_outline_w = 0;
+}
+