bug-gnustep
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Full unicode support for back-xlib(4) (was Re: Full unicode support for


From: Kazunobu Kuriyama
Subject: Full unicode support for back-xlib(4) (was Re: Full unicode support for back-xlib (2))
Date: Tue, 22 Jul 2003 18:32:37 +0900
User-agent: Mozilla/5.0 (X11; U; Linux i686; ja-JP; rv:1.4) Gecko/20030624 Netscape/7.1

Adam Fedor wrote:

Fred Kiefer wrote:

Hi Kazu,

we now have reached a disagreement and need some more views on the remaining problems. Perhaps Nicola, Adam, Wim or Alex could join in and state their views on the use font sets.
I am away for a few days anyway.


Is this just about strtok or something else? It might be easier, to just write a quick parsing function in C that shows more transparently what is hapenning...

Though I'm still not in favor of the dogmatic assertion of the man page, I modified that part of the code
in hope of this making everyone happy. :-)

I can't say I know a lot about the font issue, being a uni-lingual American (well I sort-of know some other languages, but living 1000 miles (er.. 1600km) from the nearest ocean makes it hard to find someone to practice with... I'd have to assume it works well for the people who use it.

The code works fine even for the Latin characters at the (possible) cost of beautiful kerning one expects with proportional fonts. Again, it never incorrectly mixes up fonts together. Moreover, it doesn't come
up with the misalignment strings that I noted previously.

Note to Kazu: If you have CVS, it would be nice to have all the patches rolled into one instead of having a patch for each file.

Though it may sound strange to you, I couldn't make an integrated patch via 'cvs diff -R -Nu'; the CVS ignored the option -N or --new-file. Am I wrong? Anyway, I made an all-in-one patch through the classical
'diff -Naru back back.rev'.  Attached is it.

- Kazu
2003-07-22  Kazunobu Kuriyama <kazunobu.kuriyama@nifty.com>

        * Headers/xlib/XGFontSetFontInfo.h: New file.
        * Source/xlib/XGFontSetFontInfo.m: New file.
        * Source/xlib/XGContext.m:
        ([XGContext +initializeBackend]): Modified to use XGFontSetFontInfo. 
        * Source/xlib/GNUmakefile: Modified to include XGFontSetFontInfo.m.
        
diff -Naru back/Headers/xlib/XGFontSetFontInfo.h 
back.rev/Headers/xlib/XGFontSetFontInfo.h
--- back/Headers/xlib/XGFontSetFontInfo.h       1970-01-01 09:00:00.000000000 
+0900
+++ back.rev/Headers/xlib/XGFontSetFontInfo.h   2003-07-22 16:00:29.000000000 
+0900
@@ -0,0 +1,67 @@
+/*
+    XGFontSetFontInfo.h
+
+    NSFont helper for GNUstep X/GPS Backend
+
+    Author: Kazunobu Kuriyama <kazunobu.kuriyama@nifty.com>
+    Date: July 2003
+
+    This file is NOT OFFICIAL part of the GNUstep X/GPS Backend.
+
+ */
+
+#ifndef __XGFontSetFontInfo_h
+#define __XGFontSetFontInfo_h
+
+#include <X11/Xlib.h>
+#include <AppKit/GSFontInfo.h>
+
+#ifdef X_HAVE_UTF8_STRING
+
+#if 0 // Commented out till the implementation completes.
+// ----------------------------------------------------------------------------
+//  XGFontSetEnumerator
+// ----------------------------------------------------------------------------
+@interface XGFontSetEnumerator : GSFontEnumerator
+{
+}
+
+- (void) enumerateFontsAndFamilies;
+
+@end // XGFontSetEnumerator : GSFontEnumerator
+#endif // #if 0
+
+
+// ----------------------------------------------------------------------------
+//  XGFontSetFontInfo
+// ----------------------------------------------------------------------------
+@interface XGFontSetFontInfo : GSFontInfo
+{
+    XFontSet   _font_set;
+    XFontStruct        **_fonts;
+    int                _num_fonts;
+}
+
+- (id) initWithFontName: (NSString *)name
+                matrix: (const float *)matrix
+            screenFont: (BOOL)screenFont;
+- (void) dealloc;
+- (NSSize) advancementForGlyph: (NSGlyph)glyph;
+- (NSRect) boundingRectForGlyph: (NSGlyph)glyph;
+- (BOOL) glyphIsEncoded: (NSGlyph)glyph;
+- (NSGlyph) glyphWithName: (NSString *)glyphName;
+- (void) drawGlyphs: (const NSGlyph *)glyphs
+             lenght: (int)len
+          onDisplay: (Display *)dpy
+          drawable: (Drawable)win
+              with: (GC)gc
+                at: (XPoint)xp;
+- (float) widthOfGlyphs: (const NSGlyph *)glyphs
+                 lenght: (int)len;
+- (void) setActiveFor: (Display *)dpy
+                   gc: (GC)gc;
+
+@end // XGFontSetFontInfo : GSFontInfo
+
+#endif // X_HAVE_UTF8_STRING defined
+#endif // __XGFontSetFontInfo_h
diff -Naru back/Source/xlib/GNUmakefile back.rev/Source/xlib/GNUmakefile
--- back/Source/xlib/GNUmakefile        2003-07-22 17:07:48.000000000 +0900
+++ back.rev/Source/xlib/GNUmakefile    2003-07-22 15:58:16.000000000 +0900
@@ -43,6 +43,7 @@
 XGContext.m \
 XGGState.m \
 XGGeometry.m \
+XGFontSetFontInfo.m \
 linking.m
 
 ifeq ($(WITH_XFT),yes)
diff -Naru back/Source/xlib/XGContext.m back.rev/Source/xlib/XGContext.m
--- back/Source/xlib/XGContext.m        2003-07-22 17:07:48.000000000 +0900
+++ back.rev/Source/xlib/XGContext.m    2003-07-22 15:58:40.000000000 +0900
@@ -47,6 +47,8 @@
 #include "xlib/GSXftFontInfo.h"
 #endif
 
+#include "xlib/XGFontSetFontInfo.h"
+
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include <X11/keysym.h>
@@ -70,6 +72,7 @@
 {
   Class fontClass = Nil;
   Class fontEnumerator = Nil;
+  BOOL  enableFontSet;
 
   NSDebugLog(@"Initializing GNUstep xlib backend.\n");
 
@@ -84,14 +87,43 @@
 #endif
     }
 #endif
+  enableFontSet = [[NSUserDefaults standardUserDefaults] boolForKey:
+                                                          @"GSXEnableFontSet"];
   if (fontClass == Nil)
     {
-      fontClass = [XGFontInfo class];
+      if (enableFontSet == NO)
+       {
+         fontClass = [XGFontInfo class];
+       }
+      else
+       {
+#ifdef X_HAVE_UTF8_STRING
+         fontClass = [XGFontSetFontInfo class];
+#else
+         NSLog("Can't use GSXEnableFontSet: You need XFree86 >= 4.0.2");
+         fontClass = [XGFontInfo class];
+#endif
+       }
     }
   [GSFontInfo setDefaultClass: fontClass];
+
   if (fontEnumerator == Nil)
     {
-      fontEnumerator = [XGFontEnumerator class];
+      if (enableFontSet == NO)
+       {
+         fontEnumerator = [XGFontEnumerator class];
+       }
+      else
+       {
+#ifdef X_HAVE_UTF8_STRING
+         // Commented out till the implementation of XGFontSetEnumerator
+         // completes.
+         //fontEnumerator = [XGFontSetEnumerator class];
+         fontEnumerator = [XGFontEnumerator class];
+#else
+         fontEnumerator = [XGFontEnumerator class];
+#endif
+       }
     }
   [GSFontEnumerator setDefaultClass: fontEnumerator];
 }
diff -Naru back/Source/xlib/XGFontSetFontInfo.m 
back.rev/Source/xlib/XGFontSetFontInfo.m
--- back/Source/xlib/XGFontSetFontInfo.m        1970-01-01 09:00:00.000000000 
+0900
+++ back.rev/Source/xlib/XGFontSetFontInfo.m    2003-07-22 15:59:19.000000000 
+0900
@@ -0,0 +1,510 @@
+/*
+    XGFontSetFontInfo.m
+
+    NSFont helper for GNUstep X/GPS Backend
+
+    Author: Kazunobu Kuriyama <kazunobu.kuriyama@nifty.com>
+    Date: July 2003
+
+    This file is NOT OFFICIAL part of the GNUstep GUI X/GPS Backend.
+
+ */
+
+#include "x11/XGServer.h"
+#include "xlib/XGPrivate.h"
+#include "xlib/XGFontSetFontInfo.h"
+
+#ifdef X_HAVE_UTF8_STRING
+
+#define XSERVER [XGServer currentXDisplay]
+
+typedef struct _UTF8Str {
+    char    *data;
+    int            size;
+} UTF8Str;
+
+#define UTF8StrData(x) ((x)->data)
+#define UTF8StrSize(x) ((x)->size)
+#define UTF8StrFree(x) \
+do { \
+  if ((x)->data) \
+    { \
+      free((x)->data); \
+      (x)->data = NULL; \
+      (x)->size = 0; \
+    } \
+} while (0)
+#define UTF8StrAlloc(x, length) \
+do { (x)->data = malloc(6 * (length)); } while (0)
+#define UTF8StrUsable(x) ((x)->data != NULL)
+
+
+// Forward declarations
+static BOOL load_font_set(Display *dpy, const char *given_font_name,
+                         XFontSet *font_set,
+                         XFontStruct ***fonts, int *num_fonts);
+static BOOL glyphs2utf8(const NSGlyph* glyphs, int length, UTF8Str* ustr);
+static BOOL char_struct_for_glyph(NSGlyph glyph, XFontSet font_set,
+                                 XFontStruct **fonts, int num_fonts,
+                                 XCharStruct *cs);
+
+
+#if 0 // Commented out till the implementation completes.
+// ----------------------------------------------------------------------------
+//  XGFontSetEnumerator
+// ----------------------------------------------------------------------------
+@implementation XGFontSetEnumerator : GSFontEnumerator
+
+- (void) enumerateFontsAndFamilies
+{ }
+
+@end // XGFontSetEnumerator : GSFontEnumerator
+#endif // #if 0
+
+
+// ----------------------------------------------------------------------------
+//  XGFontSetFontInfo
+// ----------------------------------------------------------------------------
+@implementation XGFontSetFontInfo : GSFontInfo
+
+- (id) initWithFontName: (NSString *)name
+                matrix: (const float *)fmatrix
+            screenFont: (BOOL)screenFont
+{
+  Display      *dpy;
+  XFontSet     font_set;
+  XFontStruct  **fonts;
+  XFontStruct  *base;
+  int          num_fonts;
+
+  if (screenFont)
+    {
+      RELEASE(self);
+      return nil;
+    }
+  if (!name || [name length] == 0 || (dpy = XSERVER) == NULL)
+    {
+      RELEASE(self);
+      return nil;
+    }
+
+  if (!load_font_set(dpy, [XGXFontName(name, fmatrix[0]) cString],
+                    &font_set, &fonts, &num_fonts))
+    {
+      RELEASE(self);
+      return nil;
+    }
+  base = fonts[0];
+
+  // GSFontInfo part
+  [super init];
+  ASSIGN(fontName, name);
+  ASSIGN(familyName, XGFontFamily(dpy, base));
+  memcpy(matrix, fmatrix, sizeof(matrix));
+  italicAngle          = 0;
+  underlinePosition    = 0;
+  underlineThickness   = 0;
+  capHeight            = 0;
+  xHeight              = 0;
+  descender            = -base->descent;
+  ascender             = base->ascent;
+  maximumAdvancement   =
+    NSMakeSize(base->max_bounds.width,
+              base->max_bounds.ascent + base->max_bounds.descent);
+  minimumAdvancement   = NSMakeSize(0, 0);
+  ASSIGN(encodingScheme, @"");
+  mostCompatibleStringEncoding = NSASCIIStringEncoding;
+  fontBBox             =
+    NSMakeRect(base->min_bounds.lbearing,
+              -base->max_bounds.ascent,
+              base->max_bounds.rbearing - base->max_bounds.lbearing,
+              base->max_bounds.ascent + base->max_bounds.descent);
+  isFixedPitch         = XGFontIsFixedPitch(dpy, base);
+  isBaseFont           = NO;
+  weight               = XGWeightOfFont(dpy, base);
+  traits               = XGTraitsOfFont(dpy, base);
+
+  // XGFontSetFontInfo part
+  _font_set    = font_set;
+  _fonts       = fonts;
+  _num_fonts   = num_fonts;
+
+  return self;
+}
+
+- (void) dealloc
+{
+  if (_font_set)
+    {
+      XFreeFontSet(XSERVER, _font_set);
+      _font_set = NULL;
+    }
+}
+
+- (NSSize) advancementForGlyph: (NSGlyph)glyph
+{
+  XCharStruct cs;
+
+  if (!char_struct_for_glyph(glyph, _font_set, _fonts, _num_fonts, &cs))
+    {
+      cs.width = _fonts[0]->max_bounds.width;
+    }
+  return NSMakeSize((float)cs.width, 0);
+}
+
+- (NSRect) boundingRectForGlyph: (NSGlyph)glyph
+{
+  XCharStruct cs;
+
+  if (!char_struct_for_glyph(glyph, _font_set, _fonts, _num_fonts, &cs))
+    {
+      return fontBBox;
+    }
+  return NSMakeRect((float)cs.lbearing, (float)-cs.descent,
+                   (float)(cs.rbearing - cs.lbearing),
+                   (float)(cs.ascent + cs.descent));
+}
+
+- (BOOL) glyphIsEncoded: (NSGlyph)glyph
+{
+  XCharStruct cs;
+
+  return char_struct_for_glyph(glyph, _font_set, _fonts, _num_fonts, &cs);
+}
+
+- (NSGlyph) glyphWithName: (NSString *)glyphName
+{
+  KeySym k;
+
+  k = XStringToKeysym([glyphName cString]);
+  if (k == NoSymbol)
+    return 0;
+  else
+    return (NSGlyph)k;
+}
+
+- (void) drawGlyphs: (const NSGlyph *)glyphs
+             lenght: (int)len
+         onDisplay: (Display *)dpy
+          drawable: (Drawable)win
+              with: (GC)gc
+                at: (XPoint)xp
+{
+  UTF8Str ustr;
+
+  if (glyphs2utf8(glyphs, len, &ustr))
+    {
+      Xutf8DrawString(dpy, win, _font_set, gc, xp.x, xp.y,
+                     UTF8StrData(&ustr), UTF8StrSize(&ustr));
+      UTF8StrFree(&ustr);
+    }
+}
+
+- (float) widthOfGlyphs: (const NSGlyph *)glyphs
+                 lenght: (int)len
+{
+  UTF8Str   ustr;
+  float            val;
+
+  if (glyphs2utf8(glyphs, len, &ustr))
+    {
+      XRectangle logical;
+
+      Xutf8TextExtents(_font_set, UTF8StrData(&ustr), UTF8StrSize(&ustr),
+                      NULL, &logical);
+      UTF8StrFree(&ustr);
+      val = logical.width;
+    }
+  else
+    {
+      val = 0.0;
+    }
+  return val;
+}
+
+- (void) setActiveFor: (Display *)dpy
+                   gc: (GC)gc
+{
+  // Do nothing.
+}
+
+@end // XGFontSetFontInfo : GSFontInfo
+
+
+// ----------------------------------------------------------------------------
+//  Static Functions
+// ----------------------------------------------------------------------------
+static BOOL
+load_font_set(Display *dpy, const char *given_font_name,
+             XFontSet *font_set, XFontStruct ***fonts, int *num_fonts)
+{
+  int  i;
+  char xlfd[256];
+#ifndef STATIC_BUFFER_IS_RELIABLE
+  char *p;
+#endif
+  int  xlfd_num_elms;
+  BOOL has_add_style;
+  char *xlfd_elms[14];
+  char base_font_name[256];
+
+  char **missing_charsets;
+  int  num_missing_charsets;
+  char *def_string;
+
+  char         **font_names;
+  int          num_font_names;
+  XFontStruct  **font_structs;
+
+  if (!dpy || !given_font_name)
+    {
+      return NO;
+    }
+
+  strcpy(xlfd, given_font_name);
+  xlfd_num_elms = 14;
+  has_add_style = YES;
+  // Both of the following compilation branches, based on the switch
+  // 'STATIC_BUFFER_IS_RELIABLE', basically do the same thing.  Because some
+  // people worry about making use of strtok(),  the latter branch is used
+  // primarily unless the switch is explicitly defined somewhere.  The former
+  // branch should document what the latter one does.
+#ifdef STATIC_BUFFER_IS_RELIABLE
+  if (strstr(xlfd, "--"))
+    {
+      --xlfd_num_elms;
+      has_add_style = NO;
+    }
+
+  i = 0;
+  xlfd_elms[i++] = strtok(xlfd, "-");
+  while (i < xlfd_num_elms)
+    {
+      xlfd_elms[i++] = strtok(NULL, "-");
+    }
+#else // 'STATIC_BUFFER_IS_RELIABLE' not defined
+  i = 0;
+  p = xlfd;
+  do
+    {
+      while (*p != '-')
+       ++p;
+      *p = '\0';
+      if (*++p == '-') // The token is in the form of '--'.
+       {
+         *p++ = '\0';
+         --xlfd_num_elms;
+         has_add_style = NO;
+       }
+      xlfd_elms[i] = p;
+    }
+  while (++i < xlfd_num_elms && *p != '\0');
+#endif // 'STATIC_BUFFER_IS_RELIABLE' not defined
+
+  // To let the X server determine a font set automatically, some elements
+  // of the XLFD should be replaced with the wild card.
+  //
+  // N.B. Rigorously speaking, to make use of proportional fonts, we need to
+  // define a mapping from NSGlyph to the XFontStruct's field 'per_char'.
+  // The property 'spacing' is set to "*" until such a mapping is given.
+  // (see also char_struct_for_glyph()).
+  if (has_add_style)
+    {
+      sprintf(base_font_name,
+             "-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s",
+             xlfd_elms[0],     // foundry
+             "*",              // family
+             xlfd_elms[2],     // weight
+             xlfd_elms[3],     // slant
+             xlfd_elms[4],     // set width
+             xlfd_elms[5],     // add style
+             xlfd_elms[6],     // pixel size
+             xlfd_elms[7],     // point size
+             xlfd_elms[8],     // resolutionX
+             xlfd_elms[9],     // resolutionY
+             "*",              // spacing
+             xlfd_elms[11],    // avg width
+             "*",              // registry
+             "*"               // encoding
+            );
+    }
+  else
+    {
+      sprintf(base_font_name,
+             "-%s-%s-%s-%s-%s--%s-%s-%s-%s-%s-%s-%s-%s",
+             xlfd_elms[0],     // foundry
+             "*",              // family
+             xlfd_elms[2],     // weight
+             xlfd_elms[3],     // slant
+             xlfd_elms[4],     // set width
+             xlfd_elms[5],     // pixel size
+             xlfd_elms[6],     // point size
+             xlfd_elms[7],     // resolutionX
+             xlfd_elms[8],     // resolutionY
+             "*",              // spacing
+             xlfd_elms[10],    // avg width
+             "*",              // registry
+             "*"               // encoding
+             );
+    }
+
+  // N.B. def_string is owned by the X server: Don't release it.
+  missing_charsets     = NULL;
+  num_missing_charsets = 0;
+  def_string           = NULL;
+  *font_set            = NULL;
+  *font_set = XCreateFontSet(dpy, base_font_name,
+                            &missing_charsets,
+                            &num_missing_charsets,
+                            &def_string);
+  if (!*font_set)
+    {
+      NSLog(@"XGFontSetFontInfo: Can't create a font set\n");
+      return NO;
+    }
+  if (num_missing_charsets > 0)
+    {
+      for (i = 0; i < num_missing_charsets; ++i)
+       {
+         NSLog(@"XGFontSetFontInfo: Charset %s is not available\n",
+               missing_charsets[i]);
+       }
+      XFreeStringList(missing_charsets);
+      missing_charsets = NULL;
+      num_missing_charsets = 0;
+    }
+
+  // N.B. font_structs and font_names are owned by the X server: Don't
+  // release them.
+  num_font_names    = 0;
+  font_structs     = NULL;
+  font_names       = NULL;
+  num_font_names = XFontsOfFontSet(*font_set, &font_structs, &font_names);
+  if (!num_font_names)
+    {
+      NSLog(@"XGFontSetFontInfo: "
+           "Can't get any information from the font set\n");
+      return NO;
+    }
+
+  *fonts = font_structs;
+  *num_fonts = num_font_names;
+
+  return YES;
+}
+
+// N.B. Use UTF8StrFree() to release the space pointed to by 'ustr'.
+static BOOL
+glyphs2utf8(const NSGlyph* glyphs, int length, UTF8Str* ustr)
+{
+  int      i;
+  NSGlyph   *g;
+  NSGlyph   *end;
+  char     *p;
+
+  if (!glyphs || !length)
+    {
+      return NO;
+    }
+
+  UTF8StrAlloc(ustr, length);
+  if (!UTF8StrUsable(ustr))
+    {
+      return NO;
+    }
+
+  p = UTF8StrData(ustr);
+  i = 0;
+  for (g = (NSGlyph *)glyphs, end = (NSGlyph *)glyphs + length; g < end; ++g)
+    {
+      if (*g < 0x00000080)
+       {
+         p[i++] = *g;
+       }
+      else if (*g < 0x00000800)
+       {
+         p[i++] = 0xc0 | ((*g >>  6) & 0x1f);
+         p[i++] = 0x80 | ( *g        & 0x3f);
+       }
+      else if (*g < 0x00010000)
+       {
+         p[i++] = 0xe0 | ((*g >> 12) & 0x0f);
+         p[i++] = 0x80 | ((*g >>  6) & 0x3f);
+         p[i++] = 0x80 | ( *g        & 0x3f);
+       }
+      else if (*g < 0x00200000)
+       {
+         p[i++] = 0xf0 | ((*g >> 18) & 0x07);
+         p[i++] = 0x80 | ((*g >> 12) & 0x3f);
+         p[i++] = 0x80 | ((*g >>  6) & 0x3f);
+         p[i++] = 0x80 | ( *g        & 0x3f);
+       }
+      else if (*g < 0x04000000)
+       {
+         p[i++] = 0xf8 | ((*g >> 24) & 0x03);
+         p[i++] = 0x80 | ((*g >> 18) & 0x3f);
+         p[i++] = 0x80 | ((*g >> 12) & 0x3f);
+         p[i++] = 0x80 | ((*g >>  6) & 0x3f);
+         p[i++] = 0x80 | ( *g        & 0x3f);
+       }
+      else if (*g < 0x80000000)
+       {
+         p[i++] = 0xfc | ((*g >> 30) & 0x01);
+         p[i++] = 0x80 | ((*g >> 24) & 0x3f);
+         p[i++] = 0x80 | ((*g >> 18) & 0x3f);
+         p[i++] = 0x80 | ((*g >> 12) & 0x3f);
+         p[i++] = 0x80 | ((*g >>  6) & 0x3f);
+         p[i++] = 0x80 | ( *g        & 0x3f);
+       }
+      else
+       {
+         // Out of range
+         UTF8StrFree(ustr);
+         return NO;
+       }
+    }
+  UTF8StrSize(ustr) = i;
+
+  return YES;
+}
+
+static BOOL
+char_struct_for_glyph(NSGlyph glyph, XFontSet font_set,
+                     XFontStruct **fonts, int num_fonts,
+                     XCharStruct *cs)
+{
+  UTF8Str utf8char;
+
+  if (glyphs2utf8(&glyph, 1, &utf8char))
+    {
+      XRectangle    ink, logical;
+      int          num_chars;
+
+      Xutf8TextPerCharExtents(font_set,
+                             UTF8StrData(&utf8char), UTF8StrSize(&utf8char),
+                             &ink, &logical, 1, &num_chars, NULL, NULL);
+      UTF8StrFree(&utf8char);
+
+      if (num_chars != 1)
+       {
+         return NO;
+       }
+
+      // When the font in use is proportional, the following variables should
+      // be tuned finer based on the the XFontStruct's field 'per_char'.
+      cs->lbearing     = 0;
+      cs->rbearing     = 0;
+      cs->width                = logical.width;
+      cs->ascent       = fonts[0]->max_bounds.ascent;
+      cs->descent      = fonts[0]->max_bounds.descent;
+      cs->attributes   = 0;
+
+      return YES;
+    }
+  else
+    {
+      return NO;
+    }
+}
+
+#endif // X_HAVE_UTF8_STRING defined

reply via email to

[Prev in Thread] Current Thread [Next in Thread]