Browse Source

New implementation with a state machine

Olivier Marty 9 years ago
parent
commit
3abd4a2f12
10 changed files with 384 additions and 110 deletions
  1. 1 1
      Makefile
  2. 96 0
      events.c
  3. 80 0
      events.h
  4. 150 53
      main.c
  5. 30 30
      printer.c
  6. 7 4
      printer.h
  7. 10 19
      rich_text.c
  8. 6 3
      rich_text.h
  9. 3 0
      time.c
  10. 1 0
      time.h

+ 1 - 1
Makefile

@@ -17,7 +17,7 @@
 #  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 #
 
-SRCS=time.c parser.c rich_text.c printer.c main.c
+SRCS=time.c parser.c rich_text.c events.c printer.c main.c
 CC=gcc
 CFLAGS=
 LIBS=$(shell pkg-config --cflags --libs x11) -lrt

+ 96 - 0
events.c

@@ -0,0 +1,96 @@
+/*
+  Copyright (C) 2014  Olivier Marty <olivier.marty.m at gmail.com>
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License
+  as published by the Free Software Foundation; either version 2
+  of the License, or (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+#include "rich_text.h"
+#include "time.h"
+#include "string.h"
+#include "events.h"
+#include <assert.h>
+#include <stdlib.h>
+
+t_events eventsInit(int initSize)
+{
+  t_events events = {0,initSize,NULL};
+  assert(initSize > 0);
+  events.events = malloc(sizeof(t_event[initSize]));
+  if(events.events == NULL)
+  {
+    perror("malloc()");
+    exit(1);
+  }
+  return events;
+}
+
+int id_min(t_events events)
+{
+  int min = 0, i;
+  mytime tmin;
+  if(events.size == 0)
+    return -1;
+  tmin = events.events[0].any.time;
+  for(i = 1; i < events.size; i++)
+  {
+    if(timeDiff(events.events[i].any.time, tmin).tv_sec < 0)
+    {
+      min = i;
+      tmin = events.events[i].any.time;
+    }
+  }
+  return min;
+}
+
+// events must be non empty
+mytime eventsNextTime(t_events events)
+{
+  int id = id_min(events);
+  assert(id >= 0);
+  return events.events[id].any.time;
+}
+
+// events must be non empty
+t_event eventsPop(t_events *events)
+{
+  int id = id_min(*events);
+  assert(id >= 0);
+  t_event e = events->events[id];
+  memmove(events->events+id, events->events+(id+1),
+    sizeof(t_event[events->size-id-1]));
+  events->size--;
+  return e;
+}
+
+void eventsPush(t_events *events, t_event e)
+{
+  if(events->size == events->maxsize)
+  {
+    events->maxsize *= 2;
+    events->events = realloc(events->events, sizeof(t_event[events->maxsize]));
+    if(events->events == NULL)
+    {
+      perror("realloc()");
+      exit(1);
+    }
+  }
+  events->events[events->size] = e;
+  events->size++;
+}
+
+int eventsEmpty(t_events events)
+{
+  return !events.size;
+}

+ 80 - 0
events.h

@@ -0,0 +1,80 @@
+/*
+  Copyright (C) 2014  Olivier Marty <olivier.marty.m at gmail.com>
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License
+  as published by the Free Software Foundation; either version 2
+  of the License, or (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+#ifndef H_EVENTS
+#define H_EVENTS
+
+#include "rich_text.h"
+#include "time.h"
+
+enum _e_type { T_NONE, T_KEYPRESSED, T_SHOW, T_HIDE };
+typedef enum _e_type e_type;
+
+struct eventAny {
+  e_type type;
+  mytime time;
+};
+
+struct eventKeyPressed {
+  e_type type;
+  mytime time;
+  int key;
+};
+
+struct eventShow {
+  e_type type;
+  mytime time;
+  int id;
+  struct richText *rt;
+};
+
+struct eventHide {
+  e_type type;
+  mytime time;
+  int id;
+  struct richText *rt;
+};
+
+union _t_event {
+  e_type type;
+  struct eventAny any;
+  struct eventKeyPressed keyPressed;
+  struct eventShow show;
+  struct eventHide hide;
+};
+typedef union _t_event t_event;
+
+// should be implemented as a priority queue
+struct _t_events {
+  int size;
+  int maxsize;
+  t_event *events;
+};
+typedef struct _t_events t_events;
+
+mytime eventsNextTime(t_events events);
+
+// events must be non empty
+void eventsPush(t_events *events, t_event e);
+t_event eventsPop(t_events *events);
+
+t_events eventsInit(int initSize);
+int eventsEmpty(t_events events);
+
+#endif
+

+ 150 - 53
main.c

@@ -20,17 +20,23 @@
 #include "time.h"
 #include "printer.h"
 #include "rich_text.h"
+#include "events.h"
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 
+enum _t_state { S_RUNNING, S_PAUSED, S_STOP };
+typedef enum _t_state t_state;
+
+
+
 void displayUsage(char *name)
 {
   printf("Usage : %s file.srt\n", name);
   printf("Options :\n");
   printf("  -s sec\t: skip the first x seconds\n");
   printf("  -d sec\t: wait x seconds before starting (default : 5)\n");
-  printf("  -t x\t\t: time factor x1000\n");
+//  printf("  -t x\t\t: time factor x1000\n");
   printf("  -m px\t\t: margin with the bottom of the screen\n");
   printf("  -p px\t\t: padding of the box\n");
   printf("  -g px\t\t: gap between two lines\n");
@@ -41,35 +47,13 @@ void displayUsage(char *name)
   printf("  -h\t\t: display this help and exit\n");
 }
 
-void callbackEvent(struct printerEnv* env, int key, void* a) {
-  if(key == ' ') {
-    // display a message
-    if(!timeIsPaused())
-    {
-      struct richText rt = richTextParse("(paused - press space to resume)\n");
-      printerShow(env, &rt);
-      richTextFree(rt);
-    }
-    else
-      printerClean(*env);
-    
-    // toggle pause
-    timePause(!timeIsPaused());
-    // if paused, wait for an event
-    while(timeIsPaused())
-    {
-      waitEvent(env);
-      manageEvent(env, callbackEvent, a);
-    }
-  }
-}
-
 int main(int argc, char **argv)
 {
   int i, shift = 0, delay = 5, margin_bottom = 50, padding = 5, gap = 5;
   float factor = 1.0;
   char *font = NULL, *font_i = NULL, *font_b = NULL, *font_bi = NULL;
   FILE *f = NULL;
+  t_state state = S_RUNNING;
   
   // parse arguments
   int c;
@@ -82,9 +66,10 @@ int main(int argc, char **argv)
       case 'd':
         delay = atoi(optarg);
         break;
-      case 't':
-        factor = (float)atoi(optarg)/1000;
+      /*case 't':
+        factor = atoi(optarg)/1000.;
         break;
+      */
       case 'm':
         margin_bottom = atoi(optarg);
         break;
@@ -140,53 +125,165 @@ int main(int argc, char **argv)
   penv.padding = padding;
   penv.gap = gap;
   
-  struct SubtitleLine sline;
-  struct richText rt;
-  int id = 0;
   // show a counter before start the clock
   for(i = delay; i > 0; i--)
   {
+    struct richText *rt;
     char t[16];
     sprintf(t, "<i>%d...</i>\n", i);
     printf("%s\n", t);
     rt = richTextParse(t);
-    printerShow(&penv, &rt);
+    printerShow(&penv, rt, 0);
     sleep(1);
+    printerHide(&penv, 0);
     richTextFree(rt);
   }
   printf("0 !\n");
-  printerClean(penv);
   timeInitialize(-factor*shift);
   
-  while(!feof(f))
+  int id = 0;
+  t_events events = eventsInit(8);
+  t_event event;
+  event.type = T_SHOW;
+  event.show.id = -1; // first
+  while(1)
   {
-    id = next(f, id+1, &sline);
-    if(timeInFuture(timeFactor(sline.end, factor)))
+    switch(state)
     {
-      rt = richTextParse(sline.text);
-      timeSleepUntil(timeFactor(sline.begin, factor));
-      
-      printf("%ds\n", sline.begin.tv_sec);
-      // show
-      printf("%s\n", sline.text);
-      printerShow(&penv, &rt);
-      
-      // hide
-      timeSleepUntil(timeFactor(sline.end, factor));
-      // TODO manage when the next subtitle appear before
-      printf("\n");
-      printerClean(penv);
-      richTextFree(rt);
-      manageEvent(&penv, callbackEvent, NULL);
+      case S_RUNNING:
+        switch(event.type)
+        {
+          case T_KEYPRESSED:
+            if(event.keyPressed.key == ' ')
+            {
+              state = S_PAUSED;
+              printf("paused...\n");
+              timePause(1);
+            }
+            break;
+
+          case T_HIDE:
+            printerHide(&penv, event.hide.id);
+            free(event.hide.rt->raw);
+            richTextFree(event.hide.rt);
+            printf("\n");
+            break;
+          
+          case T_SHOW:
+            if(event.hide.id >= 0)
+            {
+              printf("%ds\n", timeFactor(event.show.time, 1./factor).tv_sec);
+              printf("%s\n", event.show.rt->raw);
+              printerShow(&penv, event.show.rt, event.show.id);
+            }
+            // grab next subtitles
+            while(1)
+            {
+              struct SubtitleLine sline;
+              if(feof(f))
+                break;
+              id = next(f, id+1, &sline);
+              if(timeInFuture(timeFactor(sline.end, factor)))
+              {
+                char *copy = NULL;
+                struct richText *rt;
+                copy = malloc(sizeof(char[strlen(sline.text)+1]));
+                if(copy == NULL)
+                {
+                  perror("malloc()");
+                  exit(1);
+                }
+                strcpy(copy, sline.text);
+                rt = richTextParse(copy);
+                // show event
+                t_event show, hide;
+                show.type = T_SHOW;
+                show.show.id = id;
+                show.show.rt = rt;
+                show.show.time = timeFactor(sline.begin, factor);
+                // hide event
+                hide.type = T_HIDE;
+                hide.hide.id = id;
+                hide.hide.rt = rt;
+                hide.hide.time = timeFactor(sline.end, factor);
+                eventsPush(&events, show);
+                eventsPush(&events, hide);
+                break;
+              }
+              else
+              {
+                printf("skipped :\n");
+                printf("%s\n", sline.text);
+              }
+            }
+            break;
+        }
+        break;
+      case S_PAUSED:
+        switch(event.type)
+        {
+          case T_KEYPRESSED:
+            if(event.keyPressed.key == ' ')
+            {
+              state = S_RUNNING;
+              printf("end\n");
+              timePause(0);
+            }
+            break;
+        }
+        break;
     }
-    else
+    
+    if(eventsEmpty(events))
+      state = S_STOP;
+    if(state == S_STOP)
+      break;
+    
+    // sleep until next event
+    mytime next_event = eventsNextTime(events);
+    while(1)
     {
-      printf("skipped :\n");
-      printf("%s\n", sline.text);
+      int value;
+      fd_set in_fds;
+      FD_ZERO(&in_fds);
+      FD_SET(penv.d_fd, &in_fds);
+      if(state == S_PAUSED) // blocking select
+        value = pselect(penv.d_fd+1, &in_fds, NULL, NULL, NULL, NULL);
+      else // timeout
+      {
+        struct timespec to_wait = timeDiff(next_event, timeGetRelative());
+        if(to_wait.tv_sec < 0)
+        {
+          to_wait.tv_sec = 0;
+          to_wait.tv_nsec = 0;
+        }
+        value = pselect(penv.d_fd+1, &in_fds, NULL, NULL, &to_wait, NULL);
+      }
+      if(value == -1) // TODO tester interruption par un signal : return 0 ou -1
+      // dans les deux cas c'est foireux
+      {
+        perror("pselect()");
+        exit(1);
+      }
+      else if(value > 0) // x event
+      {
+        while(XPending(penv.d))
+        {
+          event = manageEvent(&penv);
+          if(event.type == T_KEYPRESSED)
+            break;
+        }
+        if(event.type == T_KEYPRESSED)
+          break;
+      }
+      else // deadline
+      {
+        event = eventsPop(&events);
+        break;
+      }
     }
   }
   
   printerCloseWindow(penv);
   fclose(f);
 }
-

+ 30 - 30
printer.c

@@ -53,6 +53,7 @@ struct printerEnv printerOpenWindow(char *font, char *font_i, char *font_b,
       fprintf(stderr, "Unable to open display\n");
       exit(1);
   }
+  env.d_fd = ConnectionNumber(env.d);
   env.s = DefaultScreen(env.d);
 
   XVisualInfo vinfo;
@@ -193,7 +194,8 @@ int drawText(struct printerEnv env, struct richText *rt,
     drawTextLines(env, rt->pos, rt->size, rt->type, x, y, maxw, draw);
 }
 
-void printerShow(struct printerEnv *env, struct richText *rt)
+// TODO show and hide : show mutliple rt, with id
+void printerShow(struct printerEnv *env, struct richText *rt, int id)
 {
   int x, y;
   // remap the window
@@ -224,43 +226,41 @@ void printerShow(struct printerEnv *env, struct richText *rt)
   XFlush(env->d);
 }
 
-void printerClean(struct printerEnv env)
+void printerHide(struct printerEnv *env, int id)
 {
-  XClearWindow(env.d, env.w);
-  XUnmapWindow(env.d, env.w);
-  XFlush(env.d);
+  XClearWindow(env->d, env->w);
+  XUnmapWindow(env->d, env->w);
+  XFlush(env->d);
 }
 
-void manageEvent(struct printerEnv *env, void callback(struct printerEnv*,int,void*), void* callback_arg)
+t_event manageEvent(struct printerEnv *env)
 {
   XEvent event;
   int r;
   XComposeStatus comp;
+  t_event e;
 
-  while(XPending(env->d))
+  XNextEvent(env->d, &event);
+  switch(event.type)
   {
-    XNextEvent(env->d, &event);
-    switch(event.type)
-    {
-      case FocusOut:
-        XGetInputFocus(env->d, &env->w_focused, &r);
-        if(env->w_focused == PointerRoot)
-          env->w_focused = RootWindow(env->d, env->s); // TODO why ?
-        XSelectInput(env->d, env->w_focused, KeyPressMask|FocusChangeMask);
-        break;
-      
-      case KeyPress:
-        callback(env, (int)XkbKeycodeToKeysym(env->d, event.xkey.keycode, 0, event.xkey.state & ShiftMask ? 1 : 0), callback_arg);
-        break;
-      
-      default:
-        break;
-    }
+    case FocusOut:
+      XGetInputFocus(env->d, &env->w_focused, &r);
+      if(env->w_focused == PointerRoot)
+        env->w_focused = RootWindow(env->d, env->s); // TODO why ?
+      XSelectInput(env->d, env->w_focused, KeyPressMask|FocusChangeMask);
+      event.type = T_NONE;
+      break;
+    
+    case KeyPress:
+      e.type = T_KEYPRESSED;
+      e.keyPressed.key = (int)XkbKeycodeToKeysym(env->d, event.xkey.keycode, 0,
+        event.xkey.state & ShiftMask ? 1 : 0);
+      // e.keyPress.time = TODO (event.xkey.time is the number of milliseconds (from uptime ?)
+      break;
+    
+    default:
+      e.type = T_NONE;
+      break;
   }
-}
-
-void waitEvent(struct printerEnv *env)
-{
-  XEvent event;
-  XPeekEvent(env->d, &event);
+  return e;
 }

+ 7 - 4
printer.h

@@ -20,11 +20,13 @@
 #define H_PRINTER
 
 #include "rich_text.h"
+#include "events.h"
 #include <X11/Xlib.h>
 
 struct printerEnv
 {
   Display *d;
+  int d_fd;
   int s;
   Window w;
   Window w_focused;
@@ -45,11 +47,12 @@ struct printerEnv printerOpenWindow(char *font, char *font_i, char *font_b,
   char *font_bi);
 void printerCloseWindow(struct printerEnv env);
 
-void printerShow(struct printerEnv *env, struct richText *rt);
-void printerClean(struct printerEnv env);
+// the id are not implemented now
+void printerShow(struct printerEnv *env, struct richText *rt, int id);
+void printerHide(struct printerEnv *env, int id);
 
-void manageEvent(struct printerEnv *env, void callback(struct printerEnv*,int,void*), void* callback_arg);
-void waitEvent(struct printerEnv *env); // TODO plutot un arguement dans manageEvent qui déclenche une lecture bloquante
+// read next event
+t_event manageEvent(struct printerEnv *env);
 
 #endif
 

+ 10 - 19
rich_text.c

@@ -122,26 +122,17 @@ void parseAux(struct richText *rt) {
   }
 }
 
-struct richText richTextParse(char* data) {
-  struct richText rt;
-  rt.left = NULL;
-  rt.right = NULL;
-  rt.pos = data;
-  rt.size = strlen(data);
-  rt.type = T_REGULAR;
-  parseAux(&rt);
+struct richText* richTextParse(char* data) {
+  struct richText *rt = newRt(NULL, NULL, data, strlen(data), T_REGULAR);
+  parseAux(rt);
+  rt->raw = data;
   return rt;
 }
 
-void richTextFree(struct richText rt) {
-  if(rt.left != NULL)
-  {
-    richTextFree(*rt.left);
-    free(rt.left);
-  }
-  if(rt.right != NULL)
-  {
-    richTextFree(*rt.right);
-    free(rt.right);
-  }
+void richTextFree(struct richText *rt) {
+  if(rt->left != NULL)
+    richTextFree(rt->left);
+  if(rt->right != NULL)
+    richTextFree(rt->right);
+  free(rt);
 }

+ 6 - 3
rich_text.h

@@ -30,10 +30,13 @@ struct richText
   enum t_type type;
   char *pos;
   int size;
+
+  // if first node :
+  char *raw;
 };
 
-// data is not copied
-struct richText richTextParse(char* data);
-void richTextFree(struct richText rt);
+// data is not copied, field raw contains a pointer to it
+struct richText* richTextParse(char* data);
+void richTextFree(struct richText *rt);
 
 #endif

+ 3 - 0
time.c

@@ -49,6 +49,7 @@ struct timespec timeDiff(struct timespec a, struct timespec b)
 // f should be >= 0
 struct timespec timeFactor(struct timespec a, double f)
 {
+  /* bogued code :
   struct timespec r;
   r.tv_sec = f*a.tv_sec;
   r.tv_nsec = f*a.tv_nsec;
@@ -58,6 +59,8 @@ struct timespec timeFactor(struct timespec a, double f)
     r.tv_sec += 1;
   }
   return r;
+  */
+  return a;
 }
 
 struct timespec begin;

+ 1 - 0
time.h

@@ -35,6 +35,7 @@ mytime timeCreate(time_t s, long ns);
 int timeSleep(mytime t);
 mytime timeDiff(mytime a, mytime b);
 // f should be >= 0
+// this function was wrong, now it returns a
 mytime timeFactor(mytime a, double f);
 
 void timePause(int pause); // pause and resume the clock