/[ddp]/manuals/trunk/intro-i18n/twm.sgml
ViewVC logotype

Contents of /manuals/trunk/intro-i18n/twm.sgml

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1219 - (show annotations) (download) (as text)
Thu Jul 26 23:07:22 2001 UTC (11 years, 10 months ago) by kubota
File MIME type: text/x-sgml
File size: 18372 byte(s)
Bug fix for I18N_FetchName() and I18N_GetIconName()
1 <sect><heading>TWM -- usage of XFontSet instead of XFontStruct</heading>
2
3 <P>
4 The author of this section is Tomohiro KUBOTA
5 (<email>kubota@debian.org</email>).
6 </P>
7
8
9 <sect1><heading>Introduction</heading>
10
11 <P>
12 TWM is Tabbed (or Tom's) Window Manager, one of the most well-known
13 window managers in the world. It is included in the XFree86 distribution.
14 Since it was not internationalized, I wrote a patch for TWM included in
15 XFree86 version 4.0.1. The patch was adopted in XFree86 version 4.0.1d.
16 </P>
17
18 <P>
19 Note: a bug is found for <tt>I18N_FetchName()</tt> and
20 <tt>I18N_GetIconName()</tt> of my patch. The bug is fixed
21 since XFree86 version 4.1.0. This document is also fixed.
22 </P>
23
24 <P>
25 The contents of the internationalization are:
26 <list>
27 <item>Usage of <tt>XFontSet</tt>-related functions instead of
28 <tt>XFontStruct</tt>, so that font handling will be
29 locale-sensible. This is the main part of the patch.
30 <item>Addition of automatic font guessing mechanism (the simplest
31 version). This avoids lack of font caused by
32 ISO8859-1-based font specification in configuration files.
33 <item>Usage of <tt>XGetWMName()</tt> and <tt>XmbTextPropertyToTextList()</tt>
34 instead of <tt>XFetchName()</tt>, so that Compound Text can be
35 used for inter-client communication of window title names.
36 This enables TWM to properly receive the internationalized
37 window text names from X clients.
38 <item>Usage of <tt>XGetWMIconName()</tt> and
39 <tt>XmbTextPropertyToTextList()</tt> for inter-client communication
40 of window icon names. This enables TWM to properly receive the
41 internationalized window icon names from X clients.
42 <item>8bit-cleanization of the configuration file parser. This enables
43 usage of internationalized texts for menus and so on.
44 </list>
45 The following will present these items.
46 </P>
47
48
49 <sect1><heading>Locale Setting - A Routine Work</heading>
50
51 <P>
52 At first, I added a small part to call <tt>setlocale()</tt> at the
53 beginning of <tt>main()</tt> function.
54 <example>
55 loc = setlocale(LC_ALL, "");
56 if (!loc || !strcmp(loc, "C") || !strcmp(loc, "POSIX") ||
57 !XSupportsLocale()) {
58 use_fontset = False;
59 } else {
60 use_fontset = True;
61 }
62 </example>
63 <tt>loc</tt> is <tt>char *</tt>-type auto (local) variable.
64 <tt>use_fontset</tt> is <tt>Bool</tt>-type global variable,
65 for which I wrote a declaration in <tt>twm.h</tt>.
66 <example>
67 extern Bool use_fontset;
68 </example>
69 I also added inclusion of <tt>X11/Xlocale.h</tt> header file.
70 By including of this header file, locale feature of X11 will
71 be used when compiled in OS without locale features. Otherwise,
72 X11/Xlocale will use locale features of the OS. Thus, you can
73 include <tt>X11/Xlocale.h</tt> regardless of whether the OS
74 support locale.
75 </P>
76
77 <P>
78 Checking of NULL, "C", and "POSIX" locales will enable TWM to work
79 8bit through when the user does not configure locale properly.
80 Under "C" or "POSIX" locale, or without proper configuration of locale,
81 <tt>XFontSet</tt>-related functions will work under 7bit ASCII encoding
82 and these functions will regard all 8bit characters as invalid.
83 In such cases, my patch won't use <tt>XFontSet</tt>-related functions
84 by checking the value of <tt>use_fontset</tt>.
85 Checking of <tt>XSupportLocale()</tt> is needed for cases when
86 the OS support the locale while X doesn't support the locale.
87 </P>
88
89
90 <sect1><heading>Font Preparation</heading>
91
92 <P>
93 Almost functions related to <tt>XFontStruct</tt> can be easily
94 substituted by <tt>XFontSet</tt>-related functions.
95 </P>
96
97 <P>
98 Fortunately, TWM used a tailored <tt>MyFont</tt> type for font
99 handling. Thus the amount of labor was decreased. The original
100 <tt>MyFont</tt> definition was:
101 <example>
102 typedef struct MyFont
103 {
104 char *name; /* name of the font */
105 XFontStruct *font; /* font structure */
106 int height; /* height of the font */
107 int y; /* Y coordinate to draw characters */
108 } MyFont;
109 </example>
110 I added a few lines.
111 <example>
112 typedef struct MyFont
113 {
114 char *name; /* name of the font */
115 XFontStruct *font; /* font structure */
116 XFontSet fontset; /* fontset structure */
117 int height; /* height of the font */
118 int y; /* Y coordinate to draw characters */
119 int ascent;
120 int descent;
121 } MyFont;
122 </example>
123 </P>
124
125 <P>
126 Then one of the main part of this patch -- font preparation.
127 The font preparation is done in the <tt>GetFont()</tt> function
128 in <tt>util.c</tt>. This function is almost entirely rewritten.
129 <example>
130 void
131 GetFont(font)
132 MyFont *font;
133 {
134 char *deffontname = "fixed";
135 char **missing_charset_list_return;
136 int missing_charset_count_return;
137 char *def_string_return;
138 XFontSetExtents *font_extents;
139 XFontStruct **xfonts;
140 char **font_names;
141 register int i;
142 int ascent;
143 int descent;
144 int fnum;
145 char *basename2;
146
147 if (use_fontset) {
148 if (font-&gt;fontset != NULL){
149 XFreeFontSet(dpy, font-&gt;fontset);
150 }
151
152 basename2 = (char *)malloc(strlen(font-&gt;name) + 3);
153 if (basename2) sprintf(basename2, "%s,*", font-&gt;name);
154 else basename2 = font-&gt;name;
155 if( (font-&gt;fontset = XCreateFontSet(dpy, basename2,
156 &amp;missing_charset_list_return,
157 &amp;missing_charset_count_return,
158 &amp;def_string_return)) == NULL) {
159 fprintf (stderr, "%s: unable to open fontset \"%s\"\n",
160 ProgramName, font-&gt;name);
161 exit(1);
162 }
163 if (basename2 != font-&gt;name) free(basename2);
164 for(i=0; i&lt;missing_charset_count_return; i++){
165 printf("%s: warning: font for charset %s is lacking.\n",
166 ProgramName, missing_charset_list_return[i]);
167 }
168
169 font_extents = XExtentsOfFontSet(font-&gt;fontset);
170 fnum = XFontsOfFontSet(font-&gt;fontset, &amp;xfonts, &amp;font_names);
171 for( i = 0, ascent = 0, descent = 0; i&lt;fnum; i++){
172 if (ascent &lt; (*xfonts)-&gt;ascent) ascent = (*xfonts)-&gt;ascent;
173 if (descent &lt; (*xfonts)-&gt;descent) descent = (*xfonts)-&gt;descent;
174 xfonts++;
175 }
176 font-&gt;height = font_extents-&gt;max_logical_extent.height;
177 font-&gt;y = ascent;
178 font-&gt;ascent = ascent;
179 font-&gt;descent = descent;
180 return;
181 }
182
183 if (font-&gt;font != NULL)
184 XFreeFont(dpy, font-&gt;font);
185
186 if ((font-&gt;font = XLoadQueryFont(dpy, font-&gt;name)) == NULL)
187 {
188 if (Scr-&gt;DefaultFont.name) {
189 deffontname = Scr-&gt;DefaultFont.name;
190 }
191 if ((font-&gt;font = XLoadQueryFont(dpy, deffontname)) == NULL)
192 {
193 fprintf (stderr, "%s: unable to open fonts \"%s\" or \"%s\"\n",
194 ProgramName, font-&gt;name, deffontname);
195 exit(1);
196 }
197
198 }
199 font-&gt;height = font-&gt;font-&gt;ascent + font-&gt;font-&gt;descent;
200 font-&gt;y = font-&gt;font-&gt;ascent;
201 font-&gt;ascent = font-&gt;font-&gt;ascent;
202 font-&gt;descent = font-&gt;font-&gt;descent;
203 }
204 </example>
205 This function can be divided into two large parts by
206 <tt>if (use_fontset)</tt>. The part inside the <tt>if</tt> is
207 for internationalized version and other part is for conventional
208 version. Conventional version is used when <tt>use_fontset</tt>
209 is false, as you can see. This part is almost the same as the
210 original TWM.
211 </P>
212
213 <P>
214 Now let's study the internationalized part of <tt>GetFont()</tt>.
215 It is convenient to compare the internationalized part and conventional
216 part, to study it.
217 The first check and <tt>XFreeFontSet()</tt> is a replacement
218 of <tt>XFreeFont()</tt>. The next several lines is the
219 <em>automatic font guessing mechanism (the simplest version)</em>,
220 the second item of the whole patch. It only adds ",*" to the
221 font query string. Then the added string is passed into
222 <tt>XCreateFontSet()</tt>, the key function of font preparation.
223 </P>
224
225 <sect1><heading>Automatic Font Guessing</heading>
226
227 <P>
228 Let's imagine how this ",*" works. Assume <tt>ja_JP.eucJP</tt> locale,
229 where EUC-JP encoding is used. In EUC-JP encoding, three fonts of
230 <list>
231 <item>a font with <em>charset</em> (in XLFD meaning) of ISO8859-1 or JISX0201.1976-0,
232 <item>a font with <em>charset</em> of JISX0208.1983-0 or JISX0208.1990-0, and
233 <item>a font with <em>charset</em> of JISX0201.1976-0
234 </list>
235 are used.
236 <footnote>
237 Read <tt>/usr/X11R6/lib/X11/locale/ja/XLC_LOCALE</tt> for detail.
238 </footnote>
239 Again assume that <tt>GetFont</tt> received a string of
240 "-adobe-helvetica-bold-r-normal--*-120-*-*-*-*-*-*" as
241 <tt>font-&gt;name</tt>. This string is a very likely specification
242 of font. Actually, I got the example from the default title font for TWM.
243 Now review the behavior of <tt>XLoadQueryFont()</tt>. Since it always
244 gets at most one font, it can succeed or fail. However, since
245 <tt>XCreateFontSet()</tt> may get multiple fonts, it may success only
246 to get a part of the set of requred fonts. The assumed calling of
247 <tt>XCreateFontSet()</tt> with the <tt>font-&gt;name</tt> in
248 <tt>ja_JP.eucJP</tt> locale goes into just such a situation.
249 For usual systems, only a font for ISO8859-1 or JISX0201.1976-0 is
250 available.
251 <footnote>
252 In such a case, <tt>XCreateFontSet()</tt> does not fail.
253 Instead, it returns informations on missing fonts.
254 </footnote>
255 It is easy to solve this situation. Unlike <tt>XLoadQueryFont()</tt>,
256 <tt>XCreateFontSet()</tt> can take a <em>list of patterns</em> of fonts
257 with wildcards. <tt>XCreateFontSet()</tt> chooses necessary fonts
258 from the set of fonts which match the patterns.
259 "*" can match all fonts. This works for character sets for which the
260 given <tt>font-&gt;name</tt> failed to match any fonts.
261 </P>
262
263 <P>
264 There were two solutions I imagined.
265 <list>
266 <item>Adding ",*" for all font specifications in the configuration
267 file.
268 <item>Adding ",*" just before calling <tt>XCreateFontSet()</tt>.
269 (the solution I took.)
270 </list>
271 The first solution may fail because users can rewrite the configuration
272 file. Though it is likely that a user knows necessary character sets
273 for the encoding (s)he uses, the second way is safer. And more, recent
274 window managers are coming to support <em>themes</em> where a set of
275 configuration is packaged and distributed, just as in
276 <url id="http://www.themes.org/">. It is very unlikely that all
277 developers of these themes know this problem and adds ",*" for every font
278 specifications. Thus, window managers which support themes must take
279 the 2nd solution, though TWM does not support themes.
280 </P>
281
282 <P>
283 Which font exactly is choosed for wild cards? It depends on the
284 configuration of X Window System. I imagine that the first font
285 in the list generated by <prgn>xlsfonts</prgn>.
286 You may think the choice of the font should be cleverer.
287 It would be adequate to say that ",*" mechanism is not less cleverer;
288 it has entirely no intelligence. It is not clever at all.
289 Yes, though I didn't implement it to TWM, I also wrote a cleverer
290 guessing mechanism.
291 <footnote>
292 I implemented cleverer mechanism to window managers such as Blackbox,
293 Sawfish, and so on where I think beauty is important than simplicity.
294 The intended algorithm is:
295 <list>
296 <item>Choose a font with similar pixel sizes.
297 <item>If availavle, choose a font with similar weight and slant.
298 </list>
299 </footnote>
300 </P>
301
302 <sect1><heading>Font Preparation (continued)</heading>
303
304 <P>
305 After calling <tt>XCreateFontSet()</tt>, <tt>GetFont()</tt> builds
306 a few member variables of <tt>MyFont</tt>, i.e.,
307 <tt>font-&gt;height</tt>,
308 <tt>font-&gt;y</tt>,
309 <tt>font-&gt;ascent</tt>, and
310 <tt>font-&gt;descent</tt>. These parameters are easily get
311 from members of <tt>XFontStruct</tt> structure and are actually
312 often used in TWM. Thus I had to prepare substitutions for
313 <tt>XFontSet</tt> version. These variables also build
314 for <tt>XFontStruct</tt> version so that a united method can be
315 used to get these parameters.
316 </P>
317
318 <sect1><heading>Drawing Text using <tt>MyFont</tt></heading>
319
320 <P>
321 To draw a text, <tt>XDrawString()</tt> and <tt>XDrawImageString()</tt>
322 are used for conventional <tt>XFontStruct</tt>. On the other hand,
323 <tt>XmbDrawString()</tt>/<tt>XwcDrawString()</tt> and
324 <tt>XmbDrawImageString()</tt>/<tt>XwcDrawImageString()</tt> are
325 used for internationalized <tt>XFontSet</tt>.
326 The difference between <tt>mb</tt> and <tt>wc</tt> versions are
327 whether the text is given in <em>multibyte characters</em> or
328 in <em>wide characters</em>. Since TWM does not perform any
329 text processing, I didn't use wide characters and treat strings
330 as they are (in multibyte characters).
331 </P>
332
333 <P>
334 TWM has many calls of these functions. Thus I decided to write
335 wrappers which checks <tt>use_fontset</tt> and calls proper version
336 of X function. They are <tt>MyFont_DrawString()</tt> and
337 <tt>MyFont_DrawImageString()</tt>. Thus all calling of
338 <tt>XDrawString()</tt> and <tt>XDrawImageString()</tt> are replaced
339 with the wrappers. Since these two are almost
340 identical, I will explain one of them.
341 <example>
342 void
343 MyFont_DrawString(dpy, d, font, gc, x, y, string, len)
344 Display *dpy;
345 Drawable d;
346 MyFont *font;
347 GC gc;
348 int x,y;
349 char *string;
350 int len;
351 {
352 if (use_fontset) {
353 XmbDrawString(dpy, d, font-&gt;fontset, gc, x, y, string, len);
354 return;
355 }
356 XDrawString (dpy, d, gc, x, y, string, len);
357 }
358 </example>
359 Very simple function! However note that the required paramaters are
360 different in these two functions of conventional version and
361 internationalized version. Font is needed for internationalized version.
362 </P>
363
364 <P>
365 Then, is GC not used for specifying a font for internationalized version?
366 Right. This causes to increase the labor. The original version of
367 TWM use a macro of <tt>FBF</tt> to set up the GC. Fortunately,
368 font specification is always performed just before the drawing of
369 the texts. I wrote a function <tt>MyFont_ChangeGC()</tt> for substitution.
370 <example>
371 void
372 MyFont_ChangeGC(fix_fore, fix_back, fix_font)
373 unsigned long fix_fore, fix_back;
374 MyFont *fix_font;
375 {
376 Gcv.foreground = fix_fore;
377 Gcv.background = fix_back;
378 if (use_fontset) {
379 XChangeGC(dpy, Scr-&gt;NormalGC, GCForeground|GCBackground, &amp;Gcv);
380 return;
381 }
382 Gcv.font = fix_font-&gt;font-&gt;fid;
383 XChangeGC(dpy, Scr-&gt;NormalGC, GCFont|GCForeground|GCBackground,&amp;Gcv);
384 }
385 </example>
386 You may wonder why this is needed. You may think just do as
387 <tt>use_fontset</tt> is false and it will work well. No,
388 because <tt>fix_font-&gt;font</tt> is indefinite.
389 </P>
390
391 <P>
392 I had to modify one more part related to GC in <tt>gc.c</tt>.
393 </P>
394
395 <sect1><heading>Geting Size of Texts</heading>
396
397 <P>
398 TWM calls <tt>XTextWidth()</tt> many times. It returns
399 the width in pixels for a text. The internationalized version
400 of the function is <tt>XmbTextExtent()</tt> and <tt>XwcTextExtent()</tt>,
401 where the difference between <tt>mb</tt> version and <tt>wc</tt> version
402 is same as <tt>XmbDrawString()</tt> and so on.
403 </P>
404
405 <P>
406 I wrote a wrapper, as I did for other functions.
407 </P>
408
409 <sect1><heading>Getting Window Titles</heading>
410
411 <P>
412 General discussions have finished.
413 The following discussions are specific to window managers.
414 </P>
415
416 <P>
417 Window managers have to get the names for window titles
418 from X clients. <tt>XFetchName()</tt> is the function for this purpose.
419 </P>
420
421 <P>
422 Window title names are communicated using <strong>property</strong>
423 mechanism of X. <tt>XA_STRING</tt> and <tt>XA_COMPOUND_TEXT</tt>
424 are types to be used for this purpose. <tt>XA_STRING</tt> means
425 the text data is in ISO8859-1 encoding and <tt>XA_COMPOUND_TEXT</tt>
426 means the data is in compound text. Compound text is a subset of
427 ISO 2022 and can handle international text data.
428 </P>
429
430 <P>
431 Now, <tt>XFetchName()</tt> can handle <tt>XA_STRING</tt> type only.
432 Thus we should use <tt>XGetWMName()</tt>. Since handling of
433 compound text needs several lines of source codes, I wrote a
434 wrapper function.
435 <example>
436 /*
437 * The following functions are internationalized substitutions
438 * for XFetchName and XGetIconName using XGetWMName and
439 * XGetWMIconName.
440 *
441 * Please note that the third arguments have to be freed using free(),
442 * not XFree().
443 */
444 Status
445 I18N_FetchName(dpy, w, winname)
446 Display *dpy;
447 Window w;
448 char ** winname;
449 {
450 int status;
451 XTextProperty text_prop;
452 char **list;
453 int num;
454
455 status = XGetWMName(dpy, w, &amp;text_prop);
456 if (!status || !text_prop.value || !text_prop.nitems) return 0;
457 status = XmbTextPropertyToTextList(dpy, &amp;text_prop, &amp;list, &amp;num);
458 if (status &lt; Success || !num || !*list) return 0;
459 XFree(text_prop.value);
460 *winname = (char *)strdup(*list);
461 XFreeStringList(list);
462 return 1;
463 }
464 </example>
465 </P>
466
467 <sect1><heading>Getting Icon Names</heading>
468
469 <P>
470 Window managers need to get not only window titles but also icon names.
471 </P>
472
473 <P>
474 TWM used <tt>XGetWindowProperty()</tt> with <tt>XA_STRING</tt> to
475 get icon names. However, internationalized function
476 <tt>XGetWMIconName()</tt> is available for this purpose and
477 I rewrote using this function. Just like <tt>XGetWMName()</tt>,
478 I wrote a wrapper.
479 <example>
480 Status
481 I18N_GetIconName(dpy, w, iconname)
482 Display *dpy;
483 Window w;
484 char ** iconname;
485 {
486 int status;
487 XTextProperty text_prop;
488 char **list;
489 int num;
490
491 status = XGetWMIconName(dpy, w, &amp;text_prop);
492 if (!status || !text_prop.value || !text_prop.nitems) return 0;
493 status = XmbTextPropertyToTextList(dpy, &amp;text_prop, &amp;list, &amp;num);
494 if (status &lt; Success || !num || !*list) return 0;
495 XFree(text_prop.value);
496 *iconname = (char *)strdup(*list);
497 XFreeStringList(list);
498 return 1;
499 }
500 </example>
501 </P>
502
503 <sect1><heading>Configuration File Parser</heading>
504
505 <P>
506 The parser for configuration file was not 8bit clean.
507 I modified it. It was a very minor change.
508 In <tt>parse.c</tt>, global variables of <tt>buff[]</tt>,
509 <tt>overflowbuff[]</tt>, <tt>stringListSource</tt>,
510 and <tt>currentString</tt> and auto variable of <tt>sl</tt>
511 in <tt>ParseStringList()</tt> are changed from <tt>char</tt>
512 to <tt>unsigned char</tt>.
513 </P>
514

  ViewVC Help
Powered by ViewVC 1.1.5