1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* text3d2 --- Shows moving 3D texts */
3 
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)text3d.2cc	5.12 2004/03/09 xlockmore";
6 
7 #endif
8 
9 /* Copyright (c) E. Lassauge, 2004. */
10 
11 /*
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * This module is based on a demo of the FTGL graphics library
25  * Copyright Henry Maddocks <henryj AT paradise.net.nz>
26  * 	http://homepages.paradise.net.nz/henryj/
27  *
28  * My e-mail address is <lassauge AT users.sourceforge.net> (NOSPAM please)
29  * Web site at http://lassauge.free.fr/
30  *
31  * Eric Lassauge  (March-09-2004)
32  *
33  * Revision History:
34  *
35  * Eric Lassauge  (March-09-2004) Created based on xscreensaver's gltext and
36  * 				the FTGL library demo:
37  * 				it uses freetype2 which is more common now.
38  *
39  */
40 
41 #define DEF_TEXT        "(default)"
42 #define DEF_SPIN        "XYZ"
43 #define DEF_WANDER      "True"
44 #define DEF_NOSPLIT	"False"
45 
46 #ifdef STANDALONE
47 #define MODE_text3d2
48 #define DEFAULTS	"*delay:	10000       \n" \
49 			"*showFPS:      False       \n" \
50 			"*wireframe:    False       \n" \
51 			"*nosplit:    " DEF_NOSPLIT "\n" \
52 			"*message:    " DEF_TEXT   "\n"
53 
54 #define free_text3d2 0
55 #define text3d2_handle_event 0
56 extern "C"
57 {
58   #include "xlockmore.h"	/* from the xscreensaver distribution */
59   /*#include "iostuff.h"*/
60 }
61 #else				/* !STANDALONE */
62   #include "xlock.h"		/* from the xlockmore distribution */
63   #include "visgl.h"
64   #ifdef HAS_MMOV
65     #undef error
66   #endif
67   #include "iostuff.h"
68 #endif				/* !STANDALONE */
69 
70 #ifdef MODE_text3d2
71 
72 #ifdef FTGL213
73 #include "FTGL/ftgl.h"
74 #else
75 #ifdef __CYGWIN__
76   #include "FTGL/FTGLExtrdFont.h"
77   #include "FTGL/FTGLOutlineFont.h"
78 #else
79   #include "FTGLExtrdFont.h"
80   #include "FTGLOutlineFont.h"
81 #endif
82 #endif
83 
84 extern "C"
85 {
86 #include "rotator.h"
87 }
88 #include "text3d2.h"
89 #include <GL/glu.h>
90 
91 #include <X11/Xcms.h>
92 
93 /* Yes, it's an ugly mix of 'C' and 'C++' functions */
94 #ifndef STANDALONE
95 extern "C" { void init_text3d2(ModeInfo * mi); }
96 extern "C" { void draw_text3d2(ModeInfo * mi); }
97 extern "C" { void change_text3d2(ModeInfo * mi); }
98 extern "C" { void release_text3d2(ModeInfo * mi); }
99 extern "C" { void refresh_text3d2(ModeInfo * mi); }
100 #endif
101 
102 /* arial.ttf is not supplied for legal reasons. */
103 /* NT and Windows 3.1 in C:\WINDOWS\SYSTEM\ARIAL.TTF */
104 /* Windows95 in C:\Windows\Fonts\arial.ttf */
105 #ifndef DEF_TTFONT
106 /* Directory of only *.ttf */
107 /* symbol.ttf and wingding.ttf should be excluded or it may core dump */
108 /* can be excluded if gltt is modified see README */
109 #ifdef __CYGWIN__
110 #define DEF_TTFONT "C:\\Windows\\Fonts\\"
111 #else
112 #define DEF_TTFONT "/usr/lib/X11/xlock/fonts/"
113 #endif
114 #endif
115 
116 static char *mode_font;
117 static int nosplit;
118 static char *do_spin;
119 static Bool do_wander;
120 static char *fontfile = (char *) NULL;
121 
122 /* Manage Option vars */
123 
124 static XrmOptionDescRec opts[] =
125 {
126     {(char *) "-ttfont", (char *) ".text3d2.ttfont", XrmoptionSepArg, (caddr_t) NULL},
127     {(char *) "-no_split", (char *) ".text3d2.no_split", XrmoptionNoArg, (caddr_t) "on"},
128     {(char *) "+no_split", (char *) ".text3d2.no_split", XrmoptionNoArg, (caddr_t) "off"},
129     {(char *) "-wander", (char *) ".text3d2.wander", XrmoptionNoArg, (caddr_t) "on"},
130     {(char *) "+wander", (char *) ".text3d2.wander", XrmoptionNoArg, (caddr_t) "off"},
131     {(char *) "-spin", (char *) ".text3d2.spin", XrmoptionSepArg, (caddr_t) NULL},
132     {(char *) "+spin", (char *) ".text3d2.spin", XrmoptionNoArg, (caddr_t) ""},
133 };
134 
135 static argtype vars[] =
136 {
137     {(void *) & mode_font, (char *) "ttfont", (char *) "TTFont", (char *) DEF_TTFONT, t_String},
138     {(void *) & nosplit, (char *) "no_split", (char *) "NoSplit", (char *) DEF_NOSPLIT, t_Bool},
139     {(void *) & do_wander, (char *) "wander", (char *) "Wander", (char *) DEF_WANDER, t_Bool},
140     {(void *) & do_spin, (char *) "spin", (char *) "Spin", (char *) DEF_SPIN, t_String},
141 };
142 
143 static OptionStruct desc[] =
144 {
145     {(char *) "-ttfont filename", (char *) "Text3d2 TrueType font file name"},
146     {(char *) "-/+no_split", (char *) "Text3d2 words splitting off/on"},
147     {(char *) "-/+wander", (char *) "Text3d2 wander off/on"},
148     {(char *) "-spin name/+spin", (char *) "Text3d2 spin mode"},
149 };
150 
151 ENTRYPOINT ModeSpecOpt text3d2_opts =
152 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
153 
154 #ifdef USE_MODULES
155 ModStruct text3d2_description =
156 {"text3d2", "init_text3d2", "draw_text3d2", "release_text3d2",
157  "refresh_text3d2", "change_text3d2", (char *) NULL, &text3d2_opts,
158  100000, 1, 10, 1, 64, 1.0, "",
159  "Shows 3D text", 0, NULL};
160 #endif
161 
162 static text3d2struct *text3d2 = (text3d2struct *) NULL;
163 
164 static GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
165 static GLfloat light1_ambient[4]  = { 1.0, 1.0, 1.0, 1.0 };
166 static GLfloat light2_ambient[4]  = { 0.2, 0.2, 0.2, 1.0 };
167 
168 /*
169  *-----------------------------------------------------------------------------
170  *    Utils.
171  *-----------------------------------------------------------------------------
172  */
173 
174 static void
setup_lighting()175 setup_lighting()
176 {
177    // Set up lighting.
178    float light1_diffuse[4]  = { 1.0, 0.9, 0.9, 1.0 };
179    float light1_specular[4] = { 1.0, 0.7, 0.7, 1.0 };
180    float light1_position[4] = { -1.0, 1.0, 1.0, 0.0 };
181    glLightfv(GL_LIGHT1, GL_AMBIENT,  light1_ambient);
182    glLightfv(GL_LIGHT1, GL_DIFFUSE,  light1_diffuse);
183    glLightfv(GL_LIGHT1, GL_SPECULAR, light1_specular);
184    glLightfv(GL_LIGHT1, GL_POSITION, light1_position);
185    glEnable(GL_LIGHT1);
186 
187    float light2_diffuse[4]  = { 0.9, 0.9, 0.9, 1.0 };
188    float light2_specular[4] = { 0.7, 0.7, 0.7, 1.0 };
189    float light2_position[4] = { 1.0, -1.0, -1.0, 0.0 };
190    glLightfv(GL_LIGHT2, GL_AMBIENT,  light2_ambient);
191    glLightfv(GL_LIGHT2, GL_DIFFUSE,  light2_diffuse);
192    glLightfv(GL_LIGHT2, GL_SPECULAR, light2_specular);
193    glLightfv(GL_LIGHT2, GL_POSITION, light2_position);
194    glEnable(GL_LIGHT2);
195 
196    float front_emission[4] = { 0.3, 0.2, 0.1, 0.0 };
197    float front_ambient[4]  = { 0.2, 0.2, 0.2, 0.0 };
198    float front_diffuse[4]  = { 0.95, 0.95, 0.8, 0.0 };
199    float front_specular[4] = { 0.6, 0.6, 0.6, 0.0 };
200    glMaterialfv(GL_FRONT, GL_EMISSION, front_emission);
201    glMaterialfv(GL_FRONT, GL_AMBIENT, front_ambient);
202    glMaterialfv(GL_FRONT, GL_DIFFUSE, front_diffuse);
203    glMaterialfv(GL_FRONT, GL_SPECULAR, front_specular);
204    glMaterialf(GL_FRONT, GL_SHININESS, 16.0);
205    glColor4fv(front_diffuse);
206 
207    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
208    glEnable(GL_CULL_FACE);
209    glColorMaterial(GL_FRONT, GL_DIFFUSE);
210    glEnable(GL_COLOR_MATERIAL);
211 
212    glEnable(GL_LIGHTING);
213    glShadeModel(GL_SMOOTH);
214 }
215 
216 static int
setup_font(text3d2struct * tp,const char * fontfile)217 setup_font( text3d2struct * tp,
218 		const char* fontfile)
219 {
220 	if (!tp->wire)
221 		tp->font = new FTGLExtrdFont(fontfile);
222 	else
223 		tp->font = new FTGLOutlineFont(fontfile);
224 
225 	if( tp->font->Error())
226 	{
227 		fprintf( stderr, "Failed to open font %s", fontfile);
228 		return 0;
229 	}
230 
231 	if( !tp->font->FaceSize( 192))
232 	{
233 		fprintf( stderr, "Failed to set size");
234 		return(0);
235 	}
236 
237 	tp->font->Depth(24);
238 
239 	tp->font->CharMap(ft_encoding_none);
240 
241 	return 1;
242 }
243 
244 /*
245  *-----------------------------------------------------------------------------
246  *-----------------------------------------------------------------------------
247  *    Mode funcs.
248  *-----------------------------------------------------------------------------
249  *-----------------------------------------------------------------------------
250  */
251 
252 void
reshape_text3d2(ModeInfo * mi,int width,int height)253  reshape_text3d2(ModeInfo * mi, int width, int height)
254 {
255     text3d2struct *tp = &text3d2[MI_SCREEN(mi)];
256     GLfloat h = (GLfloat) height / (GLfloat) width;
257 
258     glViewport(0, 0, tp->WinW = (GLint) width, tp->WinH = (GLint) height);
259 
260     glMatrixMode(GL_PROJECTION);
261     glLoadIdentity();
262     gluPerspective (30.0, 1/h, 1.0, 100.0);
263 
264     glMatrixMode(GL_MODELVIEW);
265     glMatrixMode(GL_MODELVIEW);
266     glLoadIdentity();
267     gluLookAt( 0.0, 0.0, 30.0,
268                0.0, 0.0, 0.0,
269                0.0, 1.0, 0.0);
270     glClear(GL_COLOR_BUFFER_BIT);
271 }
272 
273 /*-------------------------------------------------------------*/
274 static void
gl_init(text3d2struct * tp)275 gl_init (text3d2struct * tp)
276 {
277   glEnable( GL_CULL_FACE);
278   glFrontFace( GL_CCW);
279   glEnable( GL_DEPTH_TEST);
280 
281   // Color is used for wire mode only
282   color[0] = ((float) (NRAND(70)) / 100.0) + 0.30;
283   color[1] = ((float) (NRAND(30)) / 100.0) + 0.70;
284   color[2] = color[1] * 0.56;
285 
286 }
287 
288 /*-------------------------------------------------------------*/
289 static void
draw_text(text3d2struct * tp,Display * display,Window window)290  draw_text(text3d2struct * tp,
291       Display * display,
292       Window window)
293 {
294     int text_length;
295     char *c_text;
296 
297 #ifndef STANDALONE
298     if (!nosplit)
299     {
300 	text_length = index_dir(tp->words, (char *) " ");
301 	if (text_length == 0)
302 	    text_length = strlen(tp->words);
303 	if ((c_text = (char *) malloc(text_length+2)) != NULL)
304 	strncpy(c_text, tp->words, text_length);
305 	// +2 is because of a bug in FTGL !!
306 	c_text[text_length] = 0;
307 	c_text[text_length+1] = 0;
308     }
309     else
310 #endif
311     {
312 	text_length = strlen(tp->words_start);
313 	if ((c_text = (char *) malloc(text_length+2)) != NULL)
314 	strncpy(c_text, tp->words_start, text_length);
315 	c_text[text_length] = 0;
316 	c_text[text_length+1] = 0;
317     }
318 
319     // Render text
320     tp->font->Render(c_text);
321 
322     //if (!nosplit)
323 	free(c_text);
324 }
325 
326 /*
327  *-----------------------------------------------------------------------------
328  *-----------------------------------------------------------------------------
329  *    Xlock hooks.
330  *-----------------------------------------------------------------------------
331  *-----------------------------------------------------------------------------
332  */
333 
334 /*
335  *-----------------------------------------------------------------------------
336  *    The display is being taken away from us.  Free up malloc'ed
337  *      memory and X resources that we've alloc'ed.  Only called
338  *      once, we must zap everything for every screen.
339  *-----------------------------------------------------------------------------
340  */
341 
342 ENTRYPOINT void
release_text3d2(ModeInfo * mi)343  release_text3d2(ModeInfo * mi)
344 {
345     if (text3d2 != NULL)
346     {
347 	for (int screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
348 	{
349 	    text3d2struct *tp = &text3d2[screen];
350 
351 	    if (tp->font)
352 		delete tp->font;
353 	    if (tp->rot)
354 		free_rotator(tp->rot);
355 	}
356 	free(text3d2);
357 	text3d2 = (text3d2struct *) NULL;
358     }
359     FreeAllGL(mi);
360 }
361 
362 /*
363  *-----------------------------------------------------------------------------
364  *    Initialize text3d2.  Called each time the window changes.
365  *-----------------------------------------------------------------------------
366  */
367 
368 ENTRYPOINT void
init_text3d2(ModeInfo * mi)369  init_text3d2(ModeInfo * mi)
370 {
371     text3d2struct *tp;
372 
373     if (text3d2 == NULL)
374     {
375 	/*MI_INIT(mi, text3d2);*/
376 	if ((text3d2 = (text3d2struct *) calloc(MI_NUM_SCREENS(mi),
377 					      sizeof(text3d2struct))) == NULL)
378 	    return;
379     }
380     tp = &text3d2[MI_SCREEN(mi)];
381     tp->wire = MI_IS_WIREFRAME(mi);
382     tp->counter = 0;
383 
384 #ifdef STANDALONE
385     if ((fontfile = (char *) malloc(strlen(mode_font) + 10)) != NULL)
386     	sprintf(fontfile, "%s%s", mode_font, "arial.ttf");
387 #else
388     fontfile = getModeFont(mode_font);
389 #endif
390     if (!fontfile) {
391 		MI_CLEARWINDOW(mi);
392 		release_text3d2(mi);
393 		return;
394     }
395     if (!setup_font(tp,fontfile)) {
396 		(void) fprintf(stderr, "%s: unable to open True Type font %s!\n", MI_NAME(mi), fontfile);
397 		MI_CLEARWINDOW(mi);
398 		release_text3d2(mi);
399 		return;
400     }
401     if (MI_IS_DEBUG(mi)) {
402 		(void) fprintf(stderr,
403 			"%s:\n\tfontfile=%s .\n", MI_NAME(mi), fontfile);
404     }
405 
406     /* Do not free fontfile getModeFont handles potential leak */
407     if ((tp->glx_context = init_GL(mi)) != NULL)
408     {
409 	if (MI_IS_USE3D(mi)) {
410 		// Find out the RGB values out of the left/right colors !
411 		XcmsColor search;
412 		search.pixel = MI_RIGHT_COLOR(mi);
413 		XcmsQueryColor(MI_DISPLAY(mi), MI_COLORMAP(mi), &search, XcmsRGBiFormat);
414 		light1_ambient[0] = search.spec.RGBi.red;
415 		light1_ambient[1] = search.spec.RGBi.green;
416 		light1_ambient[2] = search.spec.RGBi.blue;
417 		search.pixel = MI_LEFT_COLOR(mi);
418 		XcmsQueryColor(MI_DISPLAY(mi), MI_COLORMAP(mi), &search, XcmsRGBiFormat);
419 		light2_ambient[0] = search.spec.RGBi.red;
420 		light2_ambient[1] = search.spec.RGBi.green;
421 		light2_ambient[2] = search.spec.RGBi.blue;
422 	}
423 	gl_init(tp);
424 	reshape_text3d2(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
425     }
426     else
427     {
428 	MI_CLEARWINDOW(mi);
429     }
430 
431    { // Manage spin
432     Bool spinx=False, spiny=False, spinz=False;
433     double spin_speed   = 1.0;
434     double wander_speed = 0.05;
435     double spin_accel   = 1.0;
436 
437     char *s = do_spin;
438     while (*s)
439       {
440         if      (*s == 'x' || *s == 'X') spinx = True;
441         else if (*s == 'y' || *s == 'Y') spiny = True;
442         else if (*s == 'z' || *s == 'Z') spinz = True;
443         else
444           {
445             (void)fprintf (stderr,
446          "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
447                      MI_NAME(mi), do_spin);
448 		MI_CLEARWINDOW(mi);
449 		release_text3d2(mi);
450 		return;
451           }
452         s++;
453       }
454 
455     tp->rot = make_rotator (spinx ? spin_speed : 0,
456                             spiny ? spin_speed : 0,
457                             spinz ? spin_speed : 0,
458                             spin_accel,
459                             do_wander ? wander_speed : 0,
460                             False);
461     }
462 
463     /* Initialize displayed string */
464     tp->words_start = tp->words = getWords(MI_SCREEN(mi), MI_NUM_SCREENS(mi));
465     if (MI_IS_DEBUG(mi))
466     {
467 	(void) fprintf(stderr,
468 		   "%s words:\n%s\n",
469 		   MI_NAME(mi), tp->words);
470     }
471 }
472 
473 /*
474  *-----------------------------------------------------------------------------
475  *    Called by the mainline code periodically to update the display.
476  *-----------------------------------------------------------------------------
477  */
478 ENTRYPOINT void
draw_text3d2(ModeInfo * mi)479  draw_text3d2(ModeInfo * mi)
480 {
481     Display *display = MI_DISPLAY(mi);
482     Window window = MI_WINDOW(mi);
483     text3d2struct *tp;
484 
485 
486     if (text3d2 == NULL) {
487 	  return;
488 	}
489     tp = &text3d2[MI_SCREEN(mi)];
490 
491     if (!tp->glx_context)
492 	return;
493 
494     MI_IS_DRAWN(mi) = True;
495     tp->counter = tp->counter + 1;
496 
497 #ifndef STANDALONE
498     if (tp->counter > (MI_CYCLES(mi) & !nosplit))
499     {
500 	int text_length = index_dir(tp->words, (char *) " ");
501 
502 	/* Every now and then, get a new word */
503 	if (text_length == 0)
504 	    text_length = strlen(tp->words);
505 	tp->counter = 0;
506 	tp->words += text_length;
507 	text_length = strlen(tp->words);
508 	if (text_length == 0)
509 	{
510 	    tp->words_start = tp->words =
511 		getWords(MI_SCREEN(mi), MI_NUM_SCREENS(mi));
512 	}
513     }
514 #endif
515   glShadeModel(GL_SMOOTH);
516 
517   glEnable(GL_DEPTH_TEST);
518   glEnable(GL_NORMALIZE);
519   glEnable(GL_CULL_FACE);
520 
521   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
522 
523   glPushMatrix ();
524 
525   glScalef(1.1, 1.1, 1.1);
526 
527   {
528     double x, y, z;
529     get_position (tp->rot, &x, &y, &z, 1);
530     glTranslatef((x - 1.0) * 8,
531                  (y - 0.5) * 8,
532                  (z - 0.5) * 8);
533 
534     get_rotation (tp->rot, &x, &y, &z, 1);
535     glRotatef (x * 360, 1.0, 0.0, 0.0);
536     glRotatef (y * 360, 0.0, 1.0, 0.0);
537     glRotatef (z * 360, 0.0, 0.0, 1.0);
538   }
539 
540 
541   glScalef(0.01, 0.01, 0.01);
542 
543   if (!tp->wire)
544   {
545 	glDisable( GL_BLEND);
546     	setup_lighting();
547   }
548   else
549   	glColor4fv (color);
550 
551   draw_text(tp, display, window);
552   glPopMatrix ();
553 
554   if (MI_IS_FPS(mi)) do_fps (mi);
555 
556   glXSwapBuffers(display, window);
557 
558 }
559 
560 #ifndef STANDALONE
561 ENTRYPOINT void
refresh_text3d2(ModeInfo * mi)562  refresh_text3d2(ModeInfo * mi)
563 {
564     /* Do nothing, it will refresh by itself :) */
565 }
566 
567 ENTRYPOINT void
change_text3d2(ModeInfo * mi)568  change_text3d2(ModeInfo * mi)
569 {
570     text3d2struct *tp;
571 
572     if (text3d2 == NULL) {
573 	  return;
574 	}
575     tp = &text3d2[MI_SCREEN(mi)];
576 
577     if (!tp->glx_context)
578 	return;
579     glDrawBuffer(GL_BACK);
580 #ifdef WIN32
581     wglMakeCurrent(hdc, tp->glx_context);
582 #else
583     glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tp->glx_context));
584 #endif
585 }
586 #endif
587 
588 XSCREENSAVER_MODULE ("Text3d2", text3d2)
589 
590 #endif				/* MODE_text3d2 */
591