static int string_width (sws_configuration *sc, const char *s) { if (textures_p) return texture_string_width (sc->texfont, s, 0); else return glutStrokeLength (GLUT_FONT, (unsigned char *) s); }
static int char_width (fliptext_configuration *sc, char c) { char s[2]; s[0] = c; s[1] = 0; return texture_string_width (sc->texfont, s, 0); }
static void loading_msg (ModeInfo *mi, int n) { carousel_state *ss = &sss[MI_SCREEN(mi)]; int wire = MI_IS_WIREFRAME(mi); char text[100]; GLfloat scale; if (wire) return; if (n == 0) sprintf (text, "Loading images..."); else sprintf (text, "Loading images... (%d%%)", (int) (n * 100 / MI_COUNT(mi))); if (ss->loading_sw == 0) /* only do this once, so that the string doesn't move. */ ss->loading_sw = texture_string_width (ss->texfont, text, &ss->loading_sh); scale = ss->loading_sh / (GLfloat) MI_HEIGHT(mi); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0, MI_WIDTH(mi), 0, MI_HEIGHT(mi)); glTranslatef ((MI_WIDTH(mi) - ss->loading_sw) / 2, (MI_HEIGHT(mi) - ss->loading_sh) / 2, 0); glColor3f (1, 1, 0); glEnable (GL_TEXTURE_2D); glDisable (GL_DEPTH_TEST); print_texture_string (ss->texfont, text); glEnable (GL_DEPTH_TEST); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glFinish(); glXSwapBuffers (MI_DISPLAY (mi), MI_WINDOW(mi)); }
/* Reads some text from the subprocess, and creates and returns a `line' object. Adds that object to the lines list. Returns 0 if no text available yet. If skip_blanks_p, then keep trying for new lines of text until we get one that is not empty. */ static line * make_line (fliptext_configuration *sc, Bool skip_blanks_p) { line *ln; char *s; AGAIN: s = get_one_line (sc); if (s && skip_blanks_p && blank_p (s)) { free (s); goto AGAIN; } if (!s) return 0; ln = (line *) calloc (1, sizeof(*ln)); ln->text = s; ln->state = NEW; ln->width = sc->font_scale * texture_string_width (sc->texfont, s, 0); ln->height = sc->font_scale * sc->line_height; memcpy (ln->color, sc->color, sizeof(ln->color)); sc->nlines++; if (sc->lines_size <= sc->nlines) { sc->lines_size = (sc->lines_size * 1.2) + sc->nlines; sc->lines = (line **) realloc (sc->lines, sc->lines_size * sizeof(*sc->lines)); if (! sc->lines) { fprintf (stderr, "%s: out of memory (%d lines)\n", progname, sc->lines_size); exit (1); } } sc->lines[sc->nlines-1] = ln; return ln; }
static void draw_frame (ModeInfo *mi, image_frame *frame, time_t now, Bool body_p) { carousel_state *ss = &sss[MI_SCREEN(mi)]; int wire = MI_IS_WIREFRAME(mi); GLfloat texw = frame->current.geom.width / (GLfloat) frame->current.tw; GLfloat texh = frame->current.geom.height / (GLfloat) frame->current.th; GLfloat texx1 = frame->current.geom.x / (GLfloat) frame->current.tw; GLfloat texy1 = frame->current.geom.y / (GLfloat) frame->current.th; GLfloat texx2 = texx1 + texw; GLfloat texy2 = texy1 + texh; GLfloat aspect = ((GLfloat) frame->current.geom.height / (GLfloat) frame->current.geom.width); glBindTexture (GL_TEXTURE_2D, frame->current.texid); glPushMatrix(); /* Position this image on the wheel. */ glRotatef (frame->theta, 0, 1, 0); glTranslatef (0, 0, frame->r); /* Scale down the image so that all N frames fit on the wheel without bumping in to each other. */ { GLfloat t, s; switch (ss->nframes) { case 1: t = -1.0; s = 1.7; break; case 2: t = -0.8; s = 1.6; break; case 3: t = -0.4; s = 1.5; break; case 4: t = -0.2; s = 1.3; break; default: t = 0.0; s = 6.0 / ss->nframes; break; } glTranslatef (0, 0, t); glScalef (s, s, s); } /* Center this image on the wheel plane. */ glTranslatef (-0.5, -(aspect/2), 0); /* Move as per the "zoom in and out" setting. */ if (zoom_p) { double x, y, z; /* Only use the Z component of the rotator for in/out position. */ get_position (frame->rot, &x, &y, &z, !ss->button_down_p); glTranslatef (0, 0, z/2); } /* Compute the "drop in and out" state. */ switch (frame->mode) { case EARLY: abort(); break; case NORMAL: if (!ss->button_down_p && now >= frame->expires && ss->loads_in_progress == 0) /* only load one at a time */ load_image (mi, frame); break; case LOADING: break; case OUT: if (--frame->mode_tick <= 0) { image swap = frame->current; frame->current = frame->loading; frame->loading = swap; frame->mode = IN; frame->mode_tick = fade_ticks / speed; } break; case IN: if (--frame->mode_tick <= 0) frame->mode = NORMAL; break; default: abort(); } /* Now translate for current in/out state. */ if (frame->mode == OUT || frame->mode == IN) { GLfloat t = (frame->mode == OUT ? frame->mode_tick / (fade_ticks / speed) : (((fade_ticks / speed) - frame->mode_tick + 1) / (fade_ticks / speed))); t = 5 * (1 - t); if (frame->from_top_p) t = -t; glTranslatef (0, t, 0); } if (body_p) /* Draw the image quad. */ { if (! wire) { glColor3f (1, 1, 1); glNormal3f (0, 0, 1); glEnable (GL_TEXTURE_2D); glBegin (GL_QUADS); glNormal3f (0, 0, 1); glTexCoord2f (texx1, texy2); glVertex3f (0, 0, 0); glTexCoord2f (texx2, texy2); glVertex3f (1, 0, 0); glTexCoord2f (texx2, texy1); glVertex3f (1, aspect, 0); glTexCoord2f (texx1, texy1); glVertex3f (0, aspect, 0); glEnd(); } /* Draw a box around it. */ glLineWidth (2.0); glColor3f (0.5, 0.5, 0.5); glDisable (GL_TEXTURE_2D); glBegin (GL_LINE_LOOP); glVertex3f (0, 0, 0); glVertex3f (1, 0, 0); glVertex3f (1, aspect, 0); glVertex3f (0, aspect, 0); glEnd(); } else /* Draw a title under the image. */ { int sw, sh; GLfloat scale = 0.05; char *title = frame->current.title ? frame->current.title : "(untitled)"; sw = texture_string_width (ss->texfont, title, &sh); glTranslatef (0, -scale, 0); scale /= sh; glScalef (scale, scale, scale); glTranslatef (((1/scale) - sw) / 2, 0, 0); glColor3f (1, 1, 1); if (!wire) { glEnable (GL_TEXTURE_2D); print_texture_string (ss->texfont, title); } else { glBegin (GL_LINE_LOOP); glVertex3f (0, 0, 0); glVertex3f (sw, 0, 0); glVertex3f (sw, sh, 0); glVertex3f (0, sh, 0); glEnd(); } } glPopMatrix(); }
ENTRYPOINT void init_sws (ModeInfo *mi) { double font_height; sws_configuration *sc = 0; if (!scs) { scs = (sws_configuration *) calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration)); if (!scs) { fprintf(stderr, "%s: out of memory\n", progname); exit(1); } } sc = &scs[MI_SCREEN(mi)]; sc->dpy = MI_DISPLAY(mi); sc = &scs[MI_SCREEN(mi)]; sc->lines = (char **) calloc (max_lines+1, sizeof(char *)); if ((sc->glx_context = init_GL(mi)) != NULL) { gl_init(mi); reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */ init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); } if (textures_p) { int cw, lh; sc->texfont = load_texture_font (MI_DISPLAY(mi), "font"); cw = texture_string_width (sc->texfont, "n", &lh); sc->char_width = cw; font_height = lh; glEnable(GL_ALPHA_TEST); glEnable (GL_TEXTURE_2D); check_gl_error ("loading font"); /* "Anistropic filtering helps for quadrilateral-angled textures. A sharper image is accomplished by interpolating and filtering multiple samples from one or more mipmaps to better approximate very distorted textures. This is the next level of filtering after trilinear filtering." */ if (smooth_p && strstr ((char *) glGetString(GL_EXTENSIONS), "GL_EXT_texture_filter_anisotropic")) { GLfloat anisotropic = 0.0; glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic); if (anisotropic >= 1.0) glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropic); } } else { font_height = GLUT_FONT->top - GLUT_FONT->bottom; sc->char_width = glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems wide */ } sc->font_scale = 1.0 / sc->char_width; /* We consider a font that consumes 80 columns to be "18 points". If neither -size nor -columns was specified, default to 60 columns (which is 24 points.) If both were specified, -columns has priority. */ { int base_col = 80; int base_size = 18; if (target_columns <= 0 && font_size <= 0) target_columns = 60; if (target_columns > 0) font_size = base_size * (base_col / (double) target_columns); else if (font_size > 0) target_columns = base_col * (base_size / (double) font_size); } sc->line_pixel_width = target_columns * sc->char_width; sc->font_scale /= target_columns; sc->line_height = font_height * sc->font_scale; /* Buffer only two lines of text. If the buffer is too big, there's a significant delay between when the program launches and when the text appears, which can be irritating for time-sensitive output (clock, current music, etc.) */ sc->buf_size = target_columns * 2; if (sc->buf_size < 80) sc->buf_size = 80; sc->buf = (char *) calloc (1, sc->buf_size); sc->total_lines = max_lines-1; if (random() & 1) star_spin = -star_spin; if (!alignment_str || !*alignment_str || !strcasecmp(alignment_str, "left")) alignment = -1; else if (!strcasecmp(alignment_str, "center") || !strcasecmp(alignment_str, "middle")) alignment = 0; else if (!strcasecmp(alignment_str, "right")) alignment = 1; else { fprintf (stderr, "%s: alignment must be left, center, or right, not \"%s\"\n", progname, alignment_str); exit (1); } sc->tc = textclient_open (sc->dpy); /* one more reshape, after line_height has been computed */ reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); }
static int draw_text (ModeInfo *mi, const char *string, GLfloat r, GLfloat th, GLfloat ttl, GLfloat size) { sonar_configuration *sp = &sps[MI_SCREEN(mi)]; int wire = MI_IS_WIREFRAME(mi); int polys = 0; GLfloat font_scale = 0.001 * (size > 0 ? size : font_size) / 14.0; int lines = 0, max_w = 0, lh = 0; char *string2 = strdup (string); char *token = string2; char *line; GLfloat color[4]; if (size <= 0) /* if size not specified, draw in yellow with alpha */ { color[0] = 1; color[1] = 1; color[2] = 0; color[3] = (ttl / (M_PI * 2)) * 1.2; if (color[3] > 1) color[3] = 1; glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); if (wire) glColor3f (color[0]*color[3], color[1]*color[3], color[2]*color[3]); } while ((line = strtok (token, "\r\n"))) { int w = texture_string_width (sp->texfont, line, &lh); if (w > max_w) max_w = w; lines++; token = 0; } glPushMatrix(); glTranslatef (r * cos (th), r * sin(th), 0); glScalef (font_scale, font_scale, font_scale); if (size <= 0) /* Draw the dot */ { GLfloat s = font_size * 1.7; glDisable (GL_TEXTURE_2D); glFrontFace (GL_CW); glBegin (wire ? GL_LINE_LOOP : GL_QUADS); glVertex3f (0, s, 0); glVertex3f (s, s, 0); glVertex3f (s, 0, 0); glVertex3f (0, 0, 0); glEnd(); glTranslatef (-max_w/2, -lh, 0); } else glTranslatef (-max_w/2, -lh/2, 0); /* draw each line, centered */ if (! wire) glEnable (GL_TEXTURE_2D); free (string2); string2 = strdup (string); token = string2; while ((line = strtok (token, "\r\n"))) { int w = texture_string_width (sp->texfont, line, 0); glPushMatrix(); glTranslatef ((max_w-w)/2, 0, 0); if (wire) { glBegin (GL_LINE_LOOP); glVertex3f (0, 0, 0); glVertex3f (w, 0, 0); glVertex3f (w, lh, 0); glVertex3f (0, lh, 0); glEnd(); } else { glFrontFace (GL_CW); print_texture_string (sp->texfont, line); } glPopMatrix(); glTranslatef (0, -lh, 0); polys++; token = 0; } glPopMatrix(); free (string2); if (! wire) glEnable (GL_DEPTH_TEST); return polys; }
ENTRYPOINT void init_fliptext (ModeInfo *mi) { int wire = MI_IS_WIREFRAME(mi); fliptext_configuration *sc; if (!scs) { scs = (fliptext_configuration *) calloc (MI_NUM_SCREENS(mi), sizeof (fliptext_configuration)); if (!scs) { fprintf(stderr, "%s: out of memory\n", progname); exit(1); } sc = &scs[MI_SCREEN(mi)]; sc->lines = (line **) calloc (max_lines+1, sizeof(char *)); } sc = &scs[MI_SCREEN(mi)]; sc->dpy = MI_DISPLAY(mi); if ((sc->glx_context = init_GL(mi)) != NULL) { reshape_fliptext (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); } program = get_string_resource (mi->dpy, "program", "Program"); { int cw, lh; sc->texfont = load_texture_font (MI_DISPLAY(mi), "font"); check_gl_error ("loading font"); cw = texture_string_width (sc->texfont, "n", &lh); sc->char_width = cw; sc->line_height = lh; } if (!wire) { glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable (GL_BLEND); glEnable (GL_ALPHA_TEST); glEnable (GL_TEXTURE_2D); /* "Anistropic filtering helps for quadrilateral-angled textures. A sharper image is accomplished by interpolating and filtering multiple samples from one or more mipmaps to better approximate very distorted textures. This is the next level of filtering after trilinear filtering." */ if (strstr ((char *) glGetString(GL_EXTENSIONS), "GL_EXT_texture_filter_anisotropic")) { GLfloat anisotropic = 0.0; glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic); if (anisotropic >= 1.0) glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropic); } } /* The default font is (by fiat) "18 points". Interpret the user's font size request relative to that. */ sc->font_scale = 3 * (font_size / 18.0); if (target_columns <= 2) target_columns = 2; /* Figure out what the wrap column should be, in font-coordinate pixels. Compute it from the given -columns value, but don't let it be wider than the screen. */ { GLfloat maxw = 110 * sc->line_height / sc->font_scale; /* magic... */ sc->font_wrap_pixels = target_columns * sc->char_width; if (sc->font_wrap_pixels > maxw || sc->font_wrap_pixels <= 0) sc->font_wrap_pixels = maxw; } sc->buf_size = target_columns * max_lines; sc->buf = (char *) calloc (1, sc->buf_size); sc->subproc_relaunch_delay = 2 * 1000; /* 2 seconds */ alignment_random_p = False; if (!alignment_str || !*alignment_str || !strcasecmp(alignment_str, "left")) alignment = -1; else if (!strcasecmp(alignment_str, "center") || !strcasecmp(alignment_str, "middle")) alignment = 0; else if (!strcasecmp(alignment_str, "right")) alignment = 1; else if (!strcasecmp(alignment_str, "random")) alignment = -1, alignment_random_p = True; else { fprintf (stderr, "%s: alignment must be left/center/right/random, not \"%s\"\n", progname, alignment_str); exit (1); } launch_text_generator (sc); if (max_lines < 1) max_lines = 1; min_lines = max_lines * 0.66; if (min_lines > max_lines - 3) min_lines = max_lines - 4; if (min_lines < 1) min_lines = 1; parse_color (mi, "foreground", get_string_resource(mi->dpy, "foreground", "Foreground"), sc->color); sc->top_margin = (sc->char_width * 100); sc->bottom_margin = -sc->top_margin; reshape_fliptext (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); /* compute left/right */ }
static void draw_line (ModeInfo *mi, line *line) { int wire = MI_IS_WIREFRAME(mi); fliptext_configuration *sc = &scs[MI_SCREEN(mi)]; if (! line->text || !*line->text || line->state == NEW || line->state == HESITATE || line->state == DEAD) return; glPushMatrix(); glTranslatef (line->current.x, line->current.y, line->current.z); glRotatef (line->cth, 0, 1, 0); if (alignment == 1) glTranslatef (-line->width, 0, 0); else if (alignment == 0) glTranslatef (-line->width/2, 0, 0); glScalef (sc->font_scale, sc->font_scale, sc->font_scale); glColor4f (line->color[0], line->color[1], line->color[2], line->color[3]); if (!wire) print_texture_string (sc->texfont, line->text); else { int w, h; char *s = line->text; char c[2]; c[1]=0; glDisable (GL_TEXTURE_2D); glColor3f (0.4, 0.4, 0.4); while (*s) { *c = *s++; w = texture_string_width (sc->texfont, c, &h); glBegin (GL_LINE_LOOP); glVertex3f (0, 0, 0); glVertex3f (w, 0, 0); glVertex3f (w, h, 0); glVertex3f (0, h, 0); glEnd(); glTranslatef (w, 0, 0); } } #if 0 glDisable (GL_TEXTURE_2D); glColor3f (0.4, 0.4, 0.4); glBegin (GL_LINE_LOOP); glVertex3f (0, 0, 0); glVertex3f (line->width/sc->font_scale, 0, 0); glVertex3f (line->width/sc->font_scale, line->height/sc->font_scale, 0); glVertex3f (0, line->height/sc->font_scale, 0); glEnd(); if (!wire) glEnable (GL_TEXTURE_2D); #endif glPopMatrix(); mi->polygon_count += strlen (line->text); }