--- glslideshow.c.orig	2020-11-04 14:38:08.523341853 -0600
+++ glslideshow.c	2020-11-04 14:47:34.063253574 -0600
@@ -71,11 +71,12 @@
 #define DEFAULTS  "*delay:           20000                \n" \
 		  "*wireframe:       False                \n" \
                   "*showFPS:         False                \n" \
 	          "*fpsSolid:        True                 \n" \
 	          "*useSHM:          True                 \n" \
-            "*titleFont: -*-helvetica-medium-r-normal-*-*-180-*-*-*-*-*-*\n" \
+            "*titleFont: -*-helvetica-medium-r-normal-*-*-240-*-*-*-*-*-*\n" \
+            "*commentFont: -*-helvetica-medium-r-normal-*-*-240-*-*-*-*-*-*\n" \
                   "*desktopGrabber:  xscreensaver-getimage -no-desktop %s\n" \
 		  "*grabDesktopImages:   False \n" \
 		  "*chooseRandomImages:  True  \n"
 
 # define release_slideshow 0
@@ -91,25 +92,34 @@
 # define DEF_PAN_DURATION   "6"
 # define DEF_IMAGE_DURATION "30"
 # define DEF_ZOOM           "75"
 # define DEF_FPS_CUTOFF     "5"
 # define DEF_TITLES         "False"
+# define DEF_COMMENTS       "True"
 # define DEF_LETTERBOX      "True"
 # define DEF_DEBUG          "False"
 # define DEF_MIPMAP         "True"
 
+# define COMMENT_INITIAL_Y_OFFSET 50
+# define COMMENT_LINE_Y_OFFSET 35
+# define TEXT_OPACITY 0.6
+
+
 #include "grab-ximage.h"
 #include "texfont.h"
+#include <stdio.h>
+#include <string.h>
 
 typedef struct {
   double x, y, w, h;
 } rect;
 
 typedef struct {
   ModeInfo *mi;
   int id;			   /* unique number for debugging */
   char *title;			   /* the filename of this image */
+  char *comment;			   /* the comment for this image */
   int w, h;			   /* size in pixels of the image */
   int tw, th;			   /* size in pixels of the texture */
   XRectangle geom;		   /* where in the image the bits are */
   Bool loaded_p;		   /* whether the image has finished loading */
   Bool used_p;			   /* whether the image has yet appeared
@@ -157,11 +167,12 @@
   GLfloat fps;                  /* approximate frame rate we're achieving */
   GLfloat theoretical_fps;      /* maximum frame rate that might be possible */
   Bool checked_fps_p;		/* Whether we have checked for a low
                                    frame rate. */
 
-  texture_font_data *font_data;	/* for printing image file names */
+  texture_font_data *font_data_title;	/* for printing image file names */
+  texture_font_data *font_data_comment;	/* for printing image comments */
 
   int sprite_id, image_id;      /* debugging id counters */
 
   double time_elapsed;
   int frames_elapsed;
@@ -187,20 +198,22 @@
                                ratio as the window, whether to display black
                                bars.
                              */
 static Bool mipmap_p;	    /* Use mipmaps instead of single textures. */
 static Bool do_titles;	    /* Display image titles. */
+static Bool do_comments;	    /* Display image comments. */
 static Bool debug_p;	    /* Be loud and do weird things. */
 
 
 static XrmOptionDescRec opts[] = {
   {"-fade",         ".fadeDuration",  XrmoptionSepArg, 0      },
   {"-pan",          ".panDuration",   XrmoptionSepArg, 0      },
   {"-duration",     ".imageDuration", XrmoptionSepArg, 0      },
   {"-zoom",         ".zoom",          XrmoptionSepArg, 0      },
   {"-cutoff",       ".FPScutoff",     XrmoptionSepArg, 0      },
   {"-titles",       ".titles",        XrmoptionNoArg, "True"  },
+  {"-comments",       ".comments",        XrmoptionNoArg, "True"  },
   {"-letterbox",    ".letterbox",     XrmoptionNoArg, "True"  },
   {"-no-letterbox", ".letterbox",     XrmoptionNoArg, "False" },
   {"-clip",         ".letterbox",     XrmoptionNoArg, "False" },
   {"-mipmaps",      ".mipmap",        XrmoptionNoArg, "True"  },
   {"-no-mipmaps",   ".mipmap",        XrmoptionNoArg, "False" },
@@ -215,10 +228,11 @@
   { &mipmap_p,      "mipmap",       "Mipmap",       DEF_MIPMAP,        t_Bool},
   { &letterbox_p,   "letterbox",    "Letterbox",    DEF_LETTERBOX,     t_Bool},
   { &fps_cutoff,    "FPScutoff",    "FPSCutoff",    DEF_FPS_CUTOFF,     t_Int},
   { &debug_p,       "debug",        "Debug",        DEF_DEBUG,         t_Bool},
   { &do_titles,     "titles",       "Titles",       DEF_TITLES,        t_Bool},
+  { &do_comments,     "comments",       "Comments",       DEF_COMMENTS,        t_Bool},
 };
 
 ENTRYPOINT ModeSpecOpt slideshow_opts = {countof(opts), opts, countof(vars), vars, NULL};
 
 
@@ -295,10 +309,114 @@
   if (ss->nimages >= countof(ss->images)) abort();
 
   return img;
 }
 
+/* Gets the EXIF comment from a file. Returns NULL if can't find it. */
+static char*
+get_comment_from_file (const char *filename)
+{
+    char line_buffer[16384];
+    FILE* exif_output;
+    char* command_line;
+    char* comment = NULL;
+    if (debug_p)
+      fprintf(stderr, "about to look for comment, filename is %s\n", filename);
+    command_line = (char*) malloc(sizeof(char) * (sizeof("exiftool -UserComment \"") + strlen(filename) + strlen("\" 2> /dev/null") + 1));
+    if (debug_p)
+      fprintf(stderr, "looking for comment\n");
+    memset(line_buffer, 0, sizeof(line_buffer)/sizeof(char));
+    strcpy(command_line, "exiftool -UserComment \"");
+    strcat(command_line, filename);
+    strcat(command_line, "\" 2> /dev/null");
+    if (debug_p)
+      fprintf(stderr, "exiftool command line: %s\n", command_line);
+    exif_output = popen(command_line, "r");
+    while (fgets(line_buffer, sizeof(line_buffer)/sizeof(char), exif_output) != NULL)
+    {
+      /* Make sure the line begins with spaces and "User Comment " */
+      int i = 0;
+      int line_buffer_length = strlen(line_buffer);
+      int begin_pos = -1;
+      while (i < line_buffer_length)
+      {
+        if (line_buffer[i] == 'U')
+        {
+          if (strncmp(&line_buffer[i], "User Comment ", strlen("User Comment ")) == 0)
+          {
+            /* Found it! */
+            /* Skip past the colon */
+            begin_pos = i + strlen("User Comment ");
+            while (begin_pos < sizeof(line_buffer)/sizeof(char) && line_buffer[begin_pos] != ':')
+            {
+              ++begin_pos;
+            }
+            if (begin_pos >= sizeof(line_buffer)/sizeof(char) - 2)
+            {
+              comment = malloc(10);
+              comment[0] = '~';
+              comment[1] = '!';
+              comment[2] = '\0';
+              begin_pos = -1;
+            }
+            else
+            {
+              ++begin_pos;
+              ++begin_pos;
+            }
+            /* Either way, we can stop. */
+            break;
+          }
+        }
+        ++i;
+      }
+      if (begin_pos != -1)
+      {
+        comment = (char*)malloc(sizeof(char) * (strlen(&line_buffer[begin_pos]) + 1));
+        strcpy(comment, &line_buffer[begin_pos]);
+        /* For testing line wrapping
+        comment = (char*)malloc(sizeof(char) * (strlen(&line_buffer[begin_pos]) * 3 + 1));
+        strcpy(comment, &line_buffer[begin_pos]);
+        strcpy(&comment[strlen(&line_buffer[begin_pos]) - 1], &line_buffer[begin_pos]);
+        strcpy(&comment[strlen(&line_buffer[begin_pos]) * 2 - 2], &line_buffer[begin_pos]);
+        */
+        pclose(exif_output);
+        free(command_line);
+        break;
+      }
+    }
+    return comment;
+}
+
+/* Gets the EXIF comment from a file. Returns NULL if can't find it. */
+static char*
+get_comment_from_filename_prefix (const char *filename_without_extension)
+{
+    /* TODO - sigh, don't know how to get this from the fn params */
+    const char* prefix = "/home/gregstoll/images/allgallerypictures/";
+    size_t full_name_size = sizeof(char) * (strlen(prefix) + strlen(filename_without_extension) + sizeof(".jpg") + 1);
+    char* full_name = (char*) calloc(1, full_name_size);
+    char* comment;
+    /* TODO - this is terrible.  Somewhere along the way the extension
+     * gets stripped out (see get_name_from_xprops, I think?) */
+    strcpy(full_name, prefix);
+    strcat(full_name, filename_without_extension);
+    strcat(full_name, ".jpg");
+    comment = get_comment_from_file(full_name);
+    if (comment != NULL)
+    {
+        free(full_name);
+        return comment;
+    }
+    memset(full_name, 0, full_name_size);
+    strcpy(full_name, prefix);
+    strcat(full_name, filename_without_extension);
+    strcat(full_name, ".JPG");
+    comment = get_comment_from_file(full_name);
+    free(full_name);
+    return comment;
+}
 
 /* Callback that tells us that the texture has been loaded.
  */
 static void
 image_loaded_cb (const char *filename, XRectangle *geom,
@@ -335,10 +453,15 @@
   img->h  = image_height;
   img->tw = texture_width;
   img->th = texture_height;
   img->geom = *geom;
   img->title = (filename ? strdup (filename) : 0);
+  /* Only load the comment if requested, since it adds a little overhead. */
+  if (do_comments)
+  {
+    img->comment = get_comment_from_filename_prefix(filename);
+  } 
 
   /* If the image's width doesn't come back as the width of the screen,
      then the image must have been scaled down (due to insufficient
      texture memory.)  Scale up the coordinates to stretch the image
      to fill the window.
@@ -375,11 +498,10 @@
 
   img->loaded_p = True;
 }
 
 
-
 /* Free the image and texture, after nobody is referencing it.
  */
 static void
 destroy_image (ModeInfo *mi, image *img)
 {
@@ -410,10 +532,11 @@
   if (debug_p)
     fprintf (stderr, "%s: unloaded img %2d: \"%s\"\n",
              blurb(), img->id, (img->title ? img->title : "(null)"));
 
   if (img->title) free (img->title);
+  if (img->comment) free (img->comment);
   glDeleteTextures (1, &img->texid);
   free (img);
 }
 
 
@@ -806,15 +929,79 @@
 
     if (do_titles &&
         img->title && *img->title &&
         (sp->state == IN || sp->state == FULL))
       {
-        glColor4f (1, 1, 1, sp->opacity);
-        print_texture_label (mi->dpy, ss->font_data,
+        glColor4f (1, 1, 1, TEXT_OPACITY);
+        print_texture_label (mi->dpy, ss->font_data_title,
                              mi->xgwa.width, mi->xgwa.height,
                              1, img->title);
       }
+
+    if (do_comments &&
+        img->comment && *img->comment &&
+        (sp->state == IN || sp->state == FULL))
+    {
+      /* Break up comment by line */
+      /* It would be nice if we could measure the font and tell exactly
+       * how big it's going to be - maybe we could use string_to_texture()? */
+      int y = COMMENT_INITIAL_Y_OFFSET;
+      char temp_char = '\0';
+      int comment_pos = 0;
+      /* See where we have to break the line. */
+      while(comment_pos < strlen(img->comment))
+      {
+        int where_to_break, orig_where_to_break, len;
+        
+        where_to_break = mi->xgwa.width / 16; /* 12; */
+        len = strlen(&img->comment[comment_pos]);
+        if (where_to_break > len)
+        {
+          if (len > 0 && img->comment[comment_pos+len-1] == '\n')
+          {
+            img->comment[comment_pos+len-1] = '\0';
+          }
+          /* No need to break. */
+        }
+        else
+        {
+          /* Find a friendly spot to break, if possible. */
+          orig_where_to_break = where_to_break;
+          where_to_break = comment_pos + orig_where_to_break;
+          while (img->comment[where_to_break] != ' ' && img->comment[where_to_break] != '\t' && img->comment[where_to_break] != '\n' && where_to_break > comment_pos)
+          {
+            --where_to_break;
+          }
+          /* If we found nothing, too bad. */
+          if (where_to_break == comment_pos)
+          {
+            where_to_break = comment_pos + orig_where_to_break;
+          }
+          temp_char = img->comment[where_to_break];
+          img->comment[where_to_break] = '\0';
+          }
+        glColor4f (1, 1, 1, TEXT_OPACITY);
+        print_texture_label_with_offset (mi->dpy, ss->font_data_comment,
+                             mi->xgwa.width, mi->xgwa.height,
+                             1, &img->comment[comment_pos],
+                             0, -1 * y);
+
+        if (temp_char != '\0')
+        {
+          img->comment[where_to_break] = temp_char;
+          comment_pos = where_to_break + 1;
+          if (temp_char != ' ' && temp_char != '\t' && temp_char != '\n')
+          {
+            --comment_pos;
+          }
+          temp_char = '\0';
+          y += COMMENT_LINE_Y_OFFSET;
+        } else {
+          comment_pos = strlen(img->comment);
+        }
+      }
+    }
   }
   glPopMatrix();
 
   if (debug_p)
     {
@@ -1112,11 +1299,12 @@
       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     }
 
   if (debug_p) glLineWidth (3);
 
-  ss->font_data = load_texture_font (mi->dpy, "titleFont");
+  ss->font_data_title = load_texture_font (mi->dpy, "titleFont");
+  ss->font_data_comment = load_texture_font (mi->dpy, "commentFont");
 
   if (debug_p)
     hack_resources();
 
   ss->now = double_time();
@@ -1228,12 +1416,14 @@
   slideshow_state *ss = &sss[MI_SCREEN(mi)];
   /* int i; */
   if (!ss->glx_context) return;
   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *ss->glx_context);
 
-  if (ss->font_data) free_texture_font (ss->font_data);
-  ss->font_data = 0;
+  if (ss->font_data_title) free_texture_font (ss->font_data_title);
+  ss->font_data_title = 0;
+  if (ss->font_data_comment) free_texture_font (ss->font_data_comment);
+  ss->font_data_comment = 0;
 
 # if 0
   /* The lifetime of these objects is incomprehensible.
      Doing this causes free pointers to be run from the XtInput.
    */
