/* gltext, Copyright (c) 2001-2017 Jamie Zawinski #ifdef USE_GL /* whole file */ #define DEF_TEXT "(default)" #define DEF_PROGRAM "(default)" #define DEF_SCALE_FACTOR "0.01" #define DEF_WANDER_SPEED "0.02" #define DEF_MAX_LINES "8" #define DEF_SPIN "XYZ" #define DEF_WANDER "True" #define DEF_FACE_FRONT "True" #define DEF_USE_MONOSPACE "False" #ifdef HAVE_UNAME # include #endif /* HAVE_UNAME */ #include "glutstroke.h" #include "glut_roman.h" #include "glut_mroman.h" #define GLUT_VARI_FONT (&glutStrokeRoman) #define GLUT_MONO_FONT (&glutStrokeMonoRoman) #define GLUT_FONT ((use_monospace) ? GLUT_MONO_FONT : GLUT_VARI_FONT) typedef struct { GLXContext *glx_context; rotator *rot, *rot2; trackball_state *trackball; Bool button_down_p; Bool spinx, spiny, spinz; GLuint text_list; int ncolors; XColor *colors; int ccolor; char *text; int reload; time_t last_update; text_data *tc; } text_configuration; static text_configuration *tps = NULL; static char *text_fmt; static char *program_str; static float scale_factor; static int max_no_lines; static float wander_speed; static char *do_spin; static Bool do_wander; static Bool face_front_p; static Bool use_monospace; static XrmOptionDescRec opts[] = { { "-text", ".text", XrmoptionSepArg, 0 }, { "-program", ".program", XrmoptionSepArg, 0 }, { "-scale", ".scaleFactor", XrmoptionSepArg, 0 }, { "-maxlines", ".maxLines", XrmoptionSepArg, 0 }, { "-wander-speed", ".wanderSpeed", XrmoptionSepArg, 0 }, { "-spin", ".spin", XrmoptionSepArg, 0 }, { "+spin", ".spin", XrmoptionNoArg, "" }, { "-wander", ".wander", XrmoptionNoArg, "True" }, { "+wander", ".wander", XrmoptionNoArg, "False" }, { "-front", ".faceFront", XrmoptionNoArg, "True" }, { "+front", ".faceFront", XrmoptionNoArg, "False" }, { "-mono", ".useMonoSpace", XrmoptionNoArg, "True" }, { "+mono", ".useMonoSpace", XrmoptionNoArg, "False" } }; static argtype vars[] = { {&text_fmt, "text", "Text", DEF_TEXT, t_String}, {&program_str, "program", "Program", DEF_PROGRAM, t_String}, {&do_spin, "spin", "Spin", DEF_SPIN, t_String}, {&scale_factor, "scaleFactor", "ScaleFactor", DEF_SCALE_FACTOR, t_Float}, {&max_no_lines, "maxLines", "MaxLines", DEF_MAX_LINES, t_Int}, {&wander_speed, "wanderSpeed", "WanderSpeed", DEF_WANDER_SPEED, t_Float}, {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool}, {&face_front_p, "faceFront", "FaceFront", DEF_FACE_FRONT, t_Bool}, {&use_monospace, "useMonoSpace", "UseMonoSpace", DEF_USE_MONOSPACE, t_Bool}, }; ENTRYPOINT ModeSpecOpt text_opts = {countof(opts), opts, countof(vars), vars, NULL}; /* Window management, etc */ ENTRYPOINT void reshape_text (ModeInfo *mi, int width, int height) { GLfloat h = (GLfloat) height / (GLfloat) width; int y = 0; if (width > height * 5) { /* tiny window: show middle */ height = width * 9/16; y = -height/2; h = height / (GLfloat) width; } glViewport (0, y, (GLint) width, (GLint) height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective (30.0, 1/h, 1.0, 100.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt( 0.0, 0.0, 30.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */ { int o = (int) current_device_rotation(); if (o != 0 && o != 180 && o != -180) glScalef (1/h, 1/h, 1/h); } # endif glClear(GL_COLOR_BUFFER_BIT); } static void gl_init (ModeInfo *mi) { text_configuration *tp = &tps[MI_SCREEN(mi)]; int wire = MI_IS_WIREFRAME(mi); static const GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0}; if (!wire) { glLightfv(GL_LIGHT0, GL_POSITION, pos); glEnable(GL_CULL_FACE); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); } tp->text_list = glGenLists (1); glNewList (tp->text_list, GL_COMPILE); glEndList (); } static void parse_text (ModeInfo *mi) { text_configuration *tp = &tps[MI_SCREEN(mi)]; char *old = tp->text; if (program_str && *program_str && !!strcmp(program_str, "(default)")) { int max_lines = max_no_lines; char buf[4096]; char *p = buf; int lines = 0; if (! tp->tc) tp->tc = textclient_open (mi->dpy); while (p < buf + sizeof(buf) - 1 && lines < max_lines) { int c = textclient_getc (tp->tc); if (c == '\n') lines++; if (c > 0) *p++ = (char) c; else break; } *p = 0; if (lines == 0 && buf[0]) lines++; tp->text = strdup (buf); tp->reload = 7; /* Let this one linger for a few seconds */ if (!*tp->text) tp->reload = 1; } else if (!text_fmt || !*text_fmt || !strcmp(text_fmt, "(default)")) { # ifdef HAVE_UNAME struct utsname uts; if (uname (&uts) < 0) { tp->text = strdup("uname() failed"); } else { char *s; if ((s = strchr(uts.nodename, '.'))) *s = 0; tp->text = (char *) malloc(strlen(uts.nodename) + strlen(uts.sysname) + strlen(uts.version) + strlen(uts.release) + 10); # if defined(_AIX) sprintf(tp->text, "%s\n%s %s.%s", uts.nodename, uts.sysname, uts.version, uts.release); # elif defined(USE_IPHONE) /* "My iPhone\n iPhone4,1\n Darwin 11.0.0" */ sprintf(tp->text, "%s\n%s\n%s %s", uts.nodename, uts.machine, uts.sysname, uts.release); # elif defined(__APPLE__) /* MacOS X + XDarwin */ { const char *file = "/System/Library/CoreServices/SystemVersion.plist"; FILE *f = fopen (file, "r"); char *pbv = 0, *pn = 0, *puvv = 0; if (f) { char *s, buf[255]; while (fgets (buf, sizeof(buf)-1, f)) { # define GRAB(S,V) \ if (strstr(buf, S)) { \ fgets (buf, sizeof(buf)-1, f); \ if ((s = strchr (buf, '>'))) V = strdup(s+1); \ if ((s = strchr (V, '<'))) *s = 0; \ } GRAB ("ProductName", pn) GRAB ("ProductBuildVersion", pbv) GRAB ("ProductUserVisibleVersion", puvv) # undef GRAB } } if (pbv) sprintf (tp->text, "%s\n%s\n%s\n%s", uts.nodename, pn, puvv, uts.machine); else sprintf(tp->text, "%s\n%s %s\n%s", uts.nodename, uts.sysname, uts.release, uts.machine); } # else sprintf(tp->text, "%s\n%s %s", uts.nodename, uts.sysname, uts.release); # endif /* special system types */ } # else /* !HAVE_UNAME */ # ifdef VMS tp->text = strdup(getenv("SYS$NODE")); # else tp->text = strdup("* *\n* * *\nxscreensaver\n* * *\n* *"); # endif # endif /* !HAVE_UNAME */ } else if (!strchr (text_fmt, '%')) { tp->text = strdup (text_fmt); } else { time_t now = time ((time_t *) 0); struct tm *tm = localtime (&now); int L = strlen(text_fmt) + 100; tp->text = (char *) malloc (L); *tp->text = 0; strftime (tp->text, L-1, text_fmt, tm); if (!*tp->text) sprintf (tp->text, "strftime error:\n%s", text_fmt); tp->reload = 1; } { /* The GLUT font only has ASCII characters. */ char *s1 = utf8_to_latin1 (tp->text, True); free (tp->text); tp->text = s1; } /* If we had text before but got no text this time, hold on to the old one, to avoid flickering. */ if (old && *old && !*tp->text) { free (tp->text); tp->text = old; } else if (old) free (old); } ENTRYPOINT Bool text_handle_event (ModeInfo *mi, XEvent *event) { text_configuration *tp = &tps[MI_SCREEN(mi)]; if (gltrackball_event_handler (event, tp->trackball, MI_WIDTH (mi), MI_HEIGHT (mi), &tp->button_down_p)) return True; return False; } ENTRYPOINT void init_text (ModeInfo *mi) { text_configuration *tp; int i; MI_INIT (mi, tps); tp = &tps[MI_SCREEN(mi)]; if ((tp->glx_context = init_GL(mi)) != NULL) { gl_init(mi); reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); } { double spin_speed = 0.5; double wander_speed = 0.02; double tilt_speed = 0.03; double spin_accel = 0.5; char *s = do_spin; while (*s) { if (*s == 'x' || *s == 'X') tp->spinx = True; else if (*s == 'y' || *s == 'Y') tp->spiny = True; else if (*s == 'z' || *s == 'Z') tp->spinz = True; else if (*s == '0') ; else { fprintf (stderr, "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n", progname, do_spin); exit (1); } s++; } tp->rot = make_rotator (tp->spinx ? spin_speed : 0, tp->spiny ? spin_speed : 0, tp->spinz ? spin_speed : 0, spin_accel, do_wander ? wander_speed : 0, False); tp->rot2 = (face_front_p ? make_rotator (0, 0, 0, 0, tilt_speed, True) : 0); tp->trackball = gltrackball_init (False); } tp->ncolors = 255; tp->colors = (XColor *) calloc(tp->ncolors, sizeof(XColor)); make_smooth_colormap (0, 0, 0, tp->colors, &tp->ncolors, False, 0, False); /* brighter colors, please... */ for (i = 0; i < tp->ncolors; i++) { tp->colors[i].red = (tp->colors[i].red / 2) + 32767; tp->colors[i].green = (tp->colors[i].green / 2) + 32767; tp->colors[i].blue = (tp->colors[i].blue / 2) + 32767; } parse_text (mi); } static int fill_character (GLUTstrokeFont font, int c, Bool wire, int *polysP) { GLfloat tube_width = 10; const StrokeCharRec *ch; const StrokeRec *stroke; const CoordRec *coord; StrokeFontPtr fontinfo; int i, j; fontinfo = (StrokeFontPtr) font; if (c < 0 || c >= fontinfo->num_chars) return 0; ch = &(fontinfo->ch[c]); if (ch) { GLfloat lx=0, ly=0; for (i = ch->num_strokes, stroke = ch->stroke; i > 0; i--, stroke++) { for (j = stroke->num_coords, coord = stroke->coord; j > 0; j--, coord++) { # ifdef SMOOTH_TUBE int smooth = True; # else int smooth = False; # endif if (j != stroke->num_coords) *polysP += tube (lx, ly, 0, coord->x, coord->y, 0, tube_width, tube_width * 0.15, TUBE_FACES, smooth, False, wire); lx = coord->x; ly = coord->y; /* Put a sphere at the endpoint of every line segment. Wasteful on curves like "0" but necessary on corners like "4". */ if (! wire) { glPushMatrix(); glTranslatef (lx, ly, 0); glScalef (tube_width, tube_width, tube_width); *polysP += unit_sphere (TUBE_FACES, TUBE_FACES, wire); glPopMatrix(); } } } return (int) (ch->right + tube_width); } return 0; } static int text_extents (const char *string, int *wP, int *hP) { const char *s, *start; int line_height = GLUT_FONT->top - GLUT_FONT->bottom; int lines = 0; *wP = 0; *hP = 0; start = string; s = start; while (1) if (*s == '\n' || *s == 0) { int w = 0; while (start < s) { w += glutStrokeWidth(GLUT_FONT, *start); start++; } start = s+1; if (w > *wP) *wP = w; *hP += line_height; lines++; if (*s == 0) break; s++; } else s++; return lines; } static unsigned long fill_string (const char *string, Bool wire) { int polys = 0; const char *s, *start; int line_height = GLUT_FONT->top - GLUT_FONT->bottom; int off; GLfloat x = 0, y = 0; int ow, oh; text_extents (string, &ow, &oh); y = oh / 2 - line_height; start = string; s = start; while (1) if (*s == '\n' || *s == 0) { int line_w = 0; const char *s2; const char *lstart = start; const char *lend = s; /* strip off whitespace at beginning and end of line (since we're centering.) */ while (lend > lstart && isspace(lend[-1])) lend--; while (lstart < lend && isspace(*lstart)) lstart++; for (s2 = lstart; s2 < lend; s2++) line_w += glutStrokeWidth (GLUT_FONT, *s2); x = (-ow/2) + ((ow-line_w)/2); for (s2 = lstart; s2 < lend; s2++) { glPushMatrix(); glTranslatef(x, y, 0); off = fill_character (GLUT_FONT, *s2, wire, &polys); x += off; glPopMatrix(); } start = s+1; y -= line_height; if (*s == 0) break; s++; } else s++; return polys; } ENTRYPOINT void draw_text (ModeInfo *mi) { text_configuration *tp = &tps[MI_SCREEN(mi)]; Display *dpy = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); int wire = MI_IS_WIREFRAME(mi); GLfloat white[4] = {1.0, 1.0, 1.0, 1.0}; GLfloat color[4] = {0.0, 0.0, 0.0, 1.0}; if (!tp->glx_context) return; glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tp->glx_context)); if (tp->reload) { if (time ((time_t *) 0) >= tp->last_update + tp->reload) { parse_text (mi); tp->last_update = time ((time_t *) 0); } } glShadeModel(GL_SMOOTH); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); glEnable(GL_CULL_FACE); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix (); glScalef(1.1, 1.1, 1.1); glRotatef(current_device_rotation(), 0, 0, 1); { double x, y, z; get_position (tp->rot, &x, &y, &z, !tp->button_down_p); glTranslatef((x - 0.5) * 8, (y - 0.5) * 8, (z - 0.5) * 8); gltrackball_rotate (tp->trackball); if (face_front_p) { double max = 90; get_position (tp->rot2, &x, &y, &z, !tp->button_down_p); if (tp->spinx) glRotatef (max/2 - x*max, 1, 0, 0); if (tp->spiny) glRotatef (max/2 - y*max, 0, 1, 0); if (tp->spinz) glRotatef (max/2 - z*max, 0, 0, 1); } else { get_rotation (tp->rot, &x, &y, &z, !tp->button_down_p); glRotatef (x * 360, 1, 0, 0); glRotatef (y * 360, 0, 1, 0); glRotatef (z * 360, 0, 0, 1); } } glColor4fv (white); color[0] = tp->colors[tp->ccolor].red / 65536.0; color[1] = tp->colors[tp->ccolor].green / 65536.0; color[2] = tp->colors[tp->ccolor].blue / 65536.0; tp->ccolor++; if (tp->ccolor >= tp->ncolors) tp->ccolor = 0; glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); glScalef(scale_factor, scale_factor, scale_factor); mi->polygon_count = fill_string(tp->text, wire); glPopMatrix (); if (mi->fps_p) do_fps (mi); glFinish(); glXSwapBuffers(dpy, window); } ENTRYPOINT void free_text(ModeInfo * mi) { text_configuration *tp = &tps[MI_SCREEN(mi)]; if (tp->tc) textclient_close (tp->tc); } XSCREENSAVER_MODULE_2 ("GLText", gltext, text) #endif /* USE_GL */