[prev in list] [next in list] [prev in thread] [next in thread] 

List:       xine-cvslog
Subject:    [xine-cvs] CVS: xine-ui/src/xitk/oxine Makefile.am,NONE,1.1 event.h,NONE,1.1 list.c,NONE,1.1 list.h,
From:       Miguel Freitas <miguelfreitas () users ! sourceforge ! net>
Date:       2005-01-31 2:49:51
Message-ID: E1CvRdP-000145-BF () sc8-pr-cvs1 ! sourceforge ! net
[Download RAW message or body]

Update of /cvsroot/xine/xine-ui/src/xitk/oxine
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv2443/src/xitk/oxine

Added Files:
	Makefile.am event.h list.c list.h mediamarks.c mediamarks.h 
	odk.c odk.h otk.c otk.h oxine.c oxine.h utils.c utils.h 
Log Message:
xine-ui meets oxine ;-)
that is, my living room is now powered by xine!

minor xine-ui hooks are needed to get osd menus working, forwarding
navigation and mouse events as needed.

oxine code was greatly simplified since lots of features are already
provided by xine-ui itself with osd feedback (eg. navigation keys, 
volume adjust, brightness/contrast...)

not all oxine menu code has been ported, this is still work in progress but
it is already usable (specially by providing a ~/.xine/oxine_mediamarks
file).


--- NEW FILE: Makefile.am ---
AM_CFLAGS = -I../

if HAVE_X11
oxine_lib = liboxine.la
endif

noinst_LTLIBRARIES = $(oxine_lib)

liboxine_la_SOURCES = \
	list.c \
	mediamarks.c \
	odk.c \
	otk.c \
	oxine.c \
	utils.c

noinst_HEADERS = \
	list.h \
	mediamarks.h \
	odk.h \
	otk.h \
	oxine.h \
	utils.h 

debug:
	@$(MAKE) CFLAGS="$(DEBUG_CFLAGS) -I../"

install-debug: debug
	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am

mostlyclean-generic:
	-rm -f *~ \#* .*~ .\#*

maintainer-clean-generic:
	-@echo "This command is intended for maintainers to use;"
	-@echo "it deletes files that may require special tools to rebuild."
	-rm -f Makefile.in

--- NEW FILE: event.h ---
/*
 * Copyright (C) 2002 Stefan Holst
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * $Id: event.h,v 1.1 2005/01/31 02:49:47 miguelfreitas Exp $
 *
 * datatypes and defines for events
 */

#ifndef HAVE_EVENT_H
#define HAVE_EVENT_H

/*
 * oxine global events and keycodes
 */

#define OXINE_EVENT_KEY             1
#define OXINE_EVENT_BUTTON          2
#define OXINE_EVENT_MOTION          3
#define OXINE_EVENT_FINISH          4
#define OXINE_EVENT_FORMAT_CHANGED  5
#define OXINE_EVENT_START           6

#define OXINE_KEY_NULL      0
#define OXINE_KEY_UP        1
#define OXINE_KEY_DOWN      2
#define OXINE_KEY_LEFT      3
#define OXINE_KEY_RIGHT     4
#define OXINE_KEY_PRIOR     5
#define OXINE_KEY_NEXT      6
#define OXINE_KEY_SELECT    7
#define OXINE_KEY_PLAY      8
#define OXINE_KEY_PAUSE     9
#define OXINE_KEY_STOP     10
#define OXINE_KEY_FORWARD  11
#define OXINE_KEY_REWIND   12
#define OXINE_KEY_0        13
#define OXINE_KEY_1        14
#define OXINE_KEY_2        15
#define OXINE_KEY_3        16
#define OXINE_KEY_4        17
#define OXINE_KEY_5        18
#define OXINE_KEY_6        19
#define OXINE_KEY_7        20
#define OXINE_KEY_8        21
#define OXINE_KEY_9        22
#define OXINE_KEY_MENU1    23
#define OXINE_KEY_MENU2    24
#define OXINE_KEY_MENU3    25
#define OXINE_KEY_MENU4    26
#define OXINE_KEY_MENU5    27
#define OXINE_KEY_MENU6    28
#define OXINE_KEY_MENU7    29
#define OXINE_KEY_ESCAPE   30
#define OXINE_KEY_FULLSCREEN 31
#define OXINE_KEY_VOLUP    32
#define OXINE_KEY_VOLDOWN  33
#define OXINE_KEY_VOLMUTE  34
#define OXINE_KEY_SEEK     35
#define OXINE_KEY_OSD      36
#define OXINE_KEY_EJECT    37
#define OXINE_KEY_PPLAY    38
#define OXINE_KEY_PL_NEXT  39
#define OXINE_KEY_PL_PREV  40
#define OXINE_KEY_SATURATION  41
#define OXINE_KEY_BRIGHTNESS  42
#define OXINE_KEY_CONTRAST    43
#define OXINE_KEY_HUE         44  
#define OXINE_KEY_SPU_OFFSET  45  
#define OXINE_KEY_SPU_CHANNEL   46          
#define OXINE_KEY_AUDIO_CHANNEL_LOGICAL  47
#define OXINE_KEY_AV_OFFSET     48          
#define OXINE_KEY_SPEED     49
#define OXINE_KEY_TOGGLE_ASPECT_RATIO     50  
#define OXINE_KEY_VO_DEINTERLACE      51   
#define OXINE_KEY_STREAM_POSITION     52

#define OXINE_BUTTON1       1
#define OXINE_BUTTON2       2
#define OXINE_BUTTON3       3
#define OXINE_BUTTON4       4
#define OXINE_BUTTON5       5

typedef struct {

  int              type;
  int              key, x, y;
  int              repeat;
  char             *data;

} oxine_event_t;

#endif

--- NEW FILE: list.c ---
/* 
 * Copyright (C) 2003 the oxine project
 * 
 * xine 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.
 * 
 * xine 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
 *
 * $Id: list.c,v 1.1 2005/01/31 02:49:47 miguelfreitas Exp $
 *
 * doubly linked lists with builtin iterator,
 * based upon xine_list functions of the xine project
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <inttypes.h>

#ifdef DEBUG
#include <string.h>
#endif

#include "list.h"
#include "utils.h"

/*
 * private data structures
 */

typedef struct node_s {

  struct node_s    *next, *prev;
  void             *content;
  int              priority;
  
} node_t;


struct list_s {

  node_t    *first, *last, *cur;

#ifdef DEBUG
  char tag[256];
#endif
};


/*
 * create a new, empty list
 */

#ifdef DEBUG
list_t *_list_new_tagged(char *file, int line) {

  char tag[256];
  list_t *list;

  snprintf(tag, 255, "list @ %s:%i", file, line);
  list = ho_new_tagged(list_t, tag);
  strncpy(list->tag, tag, 255);

  list->first=NULL;
  list->last =NULL;
  list->cur  =NULL;

  return list;
}
#else
list_t *list_new (void) {
  list_t *list;

  list = ho_new(list_t);

  list->first=NULL;
  list->last =NULL;
  list->cur  =NULL;

  return list;
}
#endif

/*
 * dispose a list (and only the list, contents have to be managed separately)
 */

void list_free(list_t *l) {
  node_t *node;

  if (!l) {
    printf ("list: tried to free empty list\n");
    abort();
  }
 
  if (!l->first) {
  
    ho_free(l);

    return;
  }

  node = l->first;
  
  while(node) {
    node_t *n = node;
    
    node = n->next;
    ho_free(n);
  }
  
  ho_free(l);
}

void *list_first_content (list_t *l) {

  l->cur = l->first;

  if (l->first) 
    return l->first->content;
  else
    return NULL;
}

void *list_next_content (list_t *l) {
  if (l->cur) {

    if (l->cur->next) {
      l->cur = l->cur->next;
      return l->cur->content;
    } 
    else
      return NULL;
    
  } else {
    printf ("list: next_content - passed end of list\n");
    abort ();
  }    
}

void *list_current_content (list_t *l) {
  if (l->cur) {

    return l->cur->content;
    
  } else {
    printf ("list: next_content - passed end of list\n");
    abort ();
  }    
}

int list_is_empty (list_t *l) {

  if (l == NULL){
    printf ("list: list_is_empty : list is NULL\n");
    abort();
  }
  return (l->first != NULL);
}

void *list_last_content (list_t *l) {

  if (l->last) {
    l->cur = l->last;
    return l->last->content;
  } else 
    return NULL;
}

void *list_prev_content (list_t *l) {

  if (l->cur) {
    if (l->cur->prev) {
      l->cur = l->cur->prev;
      return l->cur->content;
    } 
    else
      return NULL;
  } else {
    printf ("list: passed begin of list\n");
    abort ();
  }    
}

void list_append_priority_content (list_t *l, void *content, int priority) {
  node_t *node;
  
#ifdef DEBUG
  char tag[256];

  snprintf(tag, 255, "pri node in %s", l->tag);
  node = ho_new_tagged(node_t, tag);
#else
  node = ho_new(node_t);
#endif
  
  node->content = content;
  node->priority = priority;

  if (l->first) {
    node_t *cur;

    cur = l->first;

    while(1) {
      if( priority >= cur->priority ) {
        node->next = cur;
        node->prev = cur->prev;

        if( node->prev )
          node->prev->next = node;
        else
          l->first = node;
        cur->prev = node;

        l->cur = node;
        break;
      }

      if( !cur->next ) {
        node->next = NULL;
        node->prev = cur;
        cur->next = node;

        l->cur = node;
        l->last = node;
        break;
      }
     
      cur = cur->next;
    }
  } 
  else {
    l->first = l->last = l->cur = node;
    node->prev = node->next = NULL;
  }
}

#ifdef DEBUG
void _list_append_content(list_t *l, void *content, char *file, int line) {
  node_t *node;
  char tag[256];

  snprintf(tag, 255, "app node @ %s:%i", file, line);
  node = ho_new_tagged(node_t, tag);

#else
void list_append_content (list_t *l, void *content) {
  node_t *node;
  
  node = ho_new(node_t);
#endif
  node->content = content;

  if (l->last) {
    node->next = NULL;
    node->prev = l->last;
    l->last->next = node;
    l->last = node;
    l->cur = node;
  } 
  else {
    l->first = l->last = l->cur = node;
    node->prev = node->next = NULL;
  }
}

void list_insert_content (list_t *l, void *content) {
  node_t *nodecur, *nodenew, *nodeprev;
  
#ifdef DEBUG
  char tag[256];

  snprintf(tag, 255, "ins node in %s", l->tag);
  nodenew = ho_new_tagged(node_t, tag);
#else
  nodenew = ho_new(node_t);
#endif
  nodenew->content = content;
  nodecur = l->cur;

  if(!nodecur) {
    list_append_content(l, content);
    return;
  }
  
  nodeprev = nodecur->prev;
  nodecur->prev = nodenew;
  nodenew->next = nodecur;
  nodenew->prev = nodeprev;
  if(nodecur != l->first) {
    nodeprev->next = nodenew;
  } else {
    l->first = nodenew;
  }
  l->cur = nodenew;
}

void list_delete_current (list_t *l) {
  node_t *node_cur;

  node_cur = l->cur;

  if(node_cur->prev) {
    node_cur->prev->next = node_cur->next;
  } 
  else { /* First entry */
    l->first = node_cur->next;
  }
  
  if(node_cur->next) {
    node_cur->next->prev = node_cur->prev;
    l->cur = node_cur->next;
  }
  else { /* last entry in the list */
    l->last = node_cur->prev;
    l->cur = node_cur->prev;
  }
  ho_free(node_cur);
}

/*
 * glib's implementation of lists
 */

#define _g_list_new g_list_new
g_list_t* g_list_new (void) {
  g_list_t *list;
  
  list = ho_new(g_list_t);
  
  return list;
}

void g_list_free (g_list_t *list) {
  g_list_t *last;
  
  while (list)
  {
    last = list;
    list = list->next;
    ho_free (last);
  }
}

#define _g_list_free_1 g_list_free_1
void g_list_free_1 (g_list_t *list) {
  ho_free (list);
}

g_list_t* g_list_append (g_list_t *list, void* data) {
  g_list_t *new_list;
  g_list_t *last;
  
  new_list = _g_list_new ();
  new_list->data = data;
  
  if (list)
  {
    last = g_list_last (list);
    last->next = new_list;
    new_list->prev = last;

    return list;
  }
  else
    return new_list;
}

g_list_t* g_list_prepend (g_list_t *list, void* data) {
  g_list_t *new_list;
  
  new_list = _g_list_new ();
  new_list->data = data;
  
  if (list)
  {
    if (list->prev)
    {
      list->prev->next = new_list;
      new_list->prev = list->prev;
    }
    list->prev = new_list;
    new_list->next = list;
  }
  
  return new_list;
}

g_list_t* g_list_insert (g_list_t *list, void* data, int position) {
  g_list_t *new_list;
  g_list_t *tmp_list;
  
  if (position < 0)
    return g_list_append (list, data);
  else if (position == 0)
    return g_list_prepend (list, data);
  
  tmp_list = g_list_nth (list, position);
  if (!tmp_list)
    return g_list_append (list, data);
  
  new_list = _g_list_new ();
  new_list->data = data;
  
  if (tmp_list->prev)
  {
    tmp_list->prev->next = new_list;
    new_list->prev = tmp_list->prev;
  }
  new_list->next = tmp_list;
  tmp_list->prev = new_list;
  
  if (tmp_list == list)
    return new_list;
  else
    return list;
}

g_list_t* g_list_insert_before (g_list_t *list, g_list_t *sibling, void* data) {
  if (!list)
  {
    list = g_list_new ();
    list->data = data;
    return list;
  }
  else if (sibling)
  {
    g_list_t *node;

    node = g_list_new ();
    node->data = data;
    if (sibling->prev)
    {
      node->prev = sibling->prev;
      node->prev->next = node;
      node->next = sibling;
      sibling->prev = node;
      return list;
    }
    else
    {
      node->next = sibling;
      sibling->prev = node;
      return node;
    }
  }
  else
  {
    g_list_t *last;

    last = list;
    while (last->next)
      last = last->next;

    last->next = g_list_new ();
    last->next->data = data;
    last->next->prev = last;

    return list;
  }
}

g_list_t * g_list_concat (g_list_t *list1, g_list_t *list2) {
  g_list_t *tmp_list;
  
  if (list2)
  {
    tmp_list = g_list_last (list1);
    if (tmp_list)
      tmp_list->next = list2;
    else
      list1 = list2;
    list2->prev = tmp_list;
  }
  
  return list1;
}

g_list_t* g_list_remove (g_list_t *list, const void *data) {
  g_list_t *tmp;
  
  tmp = list;
  while (tmp)
  {
    if (tmp->data != data)
      tmp = tmp->next;
    else
    {
      if (tmp->prev)
	tmp->prev->next = tmp->next;
      if (tmp->next)
	tmp->next->prev = tmp->prev;
      
      if (list == tmp)
	list = list->next;
      
      _g_list_free_1 (tmp);
      
      break;
    }
  }
  return list;
}

g_list_t* g_list_remove_all (g_list_t *list, const void *data) {
  g_list_t *tmp = list;

  while (tmp)
  {
    if (tmp->data != data)
      tmp = tmp->next;
    else
    {
      g_list_t *next = tmp->next;

      if (tmp->prev)
	tmp->prev->next = next;
      else
	list = next;
      if (next)
	next->prev = tmp->prev;

      _g_list_free_1 (tmp);
      tmp = next;
    }
  }
  return list;
}

static inline g_list_t* _g_list_remove_link (g_list_t *list, g_list_t *link) {
  if (link)
  {
    if (link->prev)
      link->prev->next = link->next;
    if (link->next)
      link->next->prev = link->prev;
    
    if (link == list)
      list = list->next;
    
    link->next = NULL;
    link->prev = NULL;
  }
  
  return list;
}

g_list_t* g_list_remove_link (g_list_t *list, g_list_t *link) {
  return _g_list_remove_link (list, link);
}

g_list_t* g_list_delete_link (g_list_t *list, g_list_t *link) {
  list = _g_list_remove_link (list, link);
  _g_list_free_1 (link);

  return list;
}

g_list_t* g_list_copy (g_list_t *list) {
  g_list_t *new_list = NULL;

  if (list)
  {
    g_list_t *last;

    new_list = _g_list_new ();
    new_list->data = list->data;
    last = new_list;
    list = list->next;
    while (list)
    {
      last->next = _g_list_new ();
      last->next->prev = last;
      last = last->next;
      last->data = list->data;
      list = list->next;
    }
  }

  return new_list;
}

g_list_t* g_list_reverse (g_list_t *list) {
  g_list_t *last;
  
  last = NULL;
  while (list)
    {
      last = list;
      list = last->next;
      last->next = last->prev;
      last->prev = list;
    }
  
  return last;
}

g_list_t* g_list_nth (g_list_t *list, unsigned int  n) {
  while ((n-- > 0) && list)
    list = list->next;
  
  return list;
}

g_list_t* g_list_nth_prev (g_list_t *list, unsigned int n) {
  while ((n-- > 0) && list)
    list = list->prev;
  
  return list;
}

void* g_list_nth_data (g_list_t *list, unsigned int n) {
  while ((n-- > 0) && list)
    list = list->next;
  
  return list ? list->data : NULL;
}

g_list_t* g_list_find (g_list_t *list, const void *data) {
  while (list)
  {
    if (list->data == data)
      break;
    list = list->next;
  }
  
  return list;
}

g_list_t* g_list_find_custom (g_list_t *list, const void *data, list_compare func) {

  while (list)
  {
    if (! func (list->data, data))
      return list;
    list = list->next;
  }

  return NULL;
}


int g_list_position (g_list_t *list, g_list_t *link) {
  int i;

  i = 0;
  while (list)
  {
    if (list == link)
      return i;
    i++;
    list = list->next;
  }

  return -1;
}

int g_list_index (g_list_t *list, const void *data) {
  int i;

  i = 0;
  while (list)
  {
    if (list->data == data)
      return i;
    i++;
    list = list->next;
  }

  return -1;
}

g_list_t* g_list_last (g_list_t *list) {
  if (list)
  {
    while (list->next)
      list = list->next;
  }
  
  return list;
}

g_list_t* g_list_first (g_list_t *list) {
  if (list)
  {
    while (list->prev)
      list = list->prev;
  }
  
  return list;
}

unsigned int g_list_length (g_list_t *list) {
  unsigned int length;
  
  length = 0;
  while (list)
  {
    length++;
    list = list->next;
  }
  
  return length;
}

void g_list_foreach (g_list_t *list, list_func func, void *user_data) {
  while (list)
  {
    g_list_t *next = list->next;
    (*func) (list->data, user_data);
    list = next;
  }
}


g_list_t* g_list_insert_sorted (g_list_t *list, void *data, list_compare func) {
  g_list_t *tmp_list = list;
  g_list_t *new_list;
  int cmp;

  if (!func) return list;
  
  if (!list) 
  {
    new_list = _g_list_new ();
    new_list->data = data;
    return new_list;
  }
  
  cmp = (*func) (data, tmp_list->data);
  
  while ((tmp_list->next) && (cmp > 0))
  {
    tmp_list = tmp_list->next;
    cmp = (*func) (data, tmp_list->data);
  }

  new_list = _g_list_new ();
  new_list->data = data;

  if ((!tmp_list->next) && (cmp > 0))
  {
    tmp_list->next = new_list;
    new_list->prev = tmp_list;
    return list;
  }
   
  if (tmp_list->prev)
  {
    tmp_list->prev->next = new_list;
    new_list->prev = tmp_list->prev;
  }
  new_list->next = tmp_list;
  tmp_list->prev = new_list;
 
  if (tmp_list == list)
    return new_list;
  else
    return list;
}

static g_list_t *g_list_sort_merge (g_list_t *l1, g_list_t *l2, list_func \
compare_func,   int use_data, void *user_data) {
  
  g_list_t list, *l, *lprev;
  int cmp;

  l = &list; 
  lprev = NULL;

  while (l1 && l2)
  {
    if (use_data)
      cmp = ((list_compare_data) compare_func) (l1->data, l2->data, user_data);
    else
      cmp = ((list_compare) compare_func) (l1->data, l2->data);

    if (cmp <= 0)
    {
      l->next = l1;
      l = l->next;
      l->prev = lprev; 
      lprev = l;
      l1 = l1->next;
    } 
    else 
    {
      l->next = l2;
      l = l->next;
      l->prev = lprev; 
      lprev = l;
      l2 = l2->next;
    }
  }
  l->next = l1 ? l1 : l2;
  l->next->prev = l;

  return list.next;
}

static g_list_t* g_list_sort_real (g_list_t *list, list_func compare_func, 
    int use_data, void *user_data) {
  
  g_list_t *l1, *l2;
  
  if (!list) 
    return NULL;
  if (!list->next) 
    return list;
  
  l1 = list; 
  l2 = list->next;

  while ((l2 = l2->next) != NULL)
  {
    if ((l2 = l2->next) == NULL) 
      break;
    l1 = l1->next;
  }
  l2 = l1->next; 
  l1->next = NULL; 

  return g_list_sort_merge (g_list_sort_real (list, compare_func, use_data, \
user_data),  g_list_sort_real (l2, compare_func, use_data, user_data),
			    compare_func,
			    use_data,
			    user_data);
}

g_list_t *g_list_sort (g_list_t *list, list_compare compare_func) {
  return g_list_sort_real (list, (list_func) compare_func, 0, NULL);
}

g_list_t * g_list_sort_with_data (g_list_t *list, list_compare compare_func, void \
*user_data) {  return g_list_sort_real (list, (list_func) compare_func, 1, \
user_data); }

#if 0
static g_list_t* g_list_sort2 (g_list_t *list, list_compare compare_func) {
  g_list_t *runs = NULL;
  g_list_t *tmp;

  /* Degenerate case.  */
  if (!list) return NULL;

  /* Assume: list = [12,2,4,11,2,4,6,1,1,12].  */
  for (tmp = list; tmp; )
  {
    g_list_t *tmp2;
    for (tmp2 = tmp;
	 tmp2->next && compare_func (tmp2->data, tmp2->next->data) <= 0;
	 tmp2 = tmp2->next)
      /* Nothing */;
    runs = g_list_append (runs, tmp);
    tmp = tmp2->next;
    tmp2->next = NULL;
  }
  /* Now: runs = [[12],[2,4,11],[2,4,6],[1,1,12]].  */
  
  while (runs->next)
  {
    /* We have more than one run.  Merge pairwise.  */
    g_list_t *dst, *src, *dstprev = NULL;
    dst = src = runs;
    while (src && src->next)
    {
      dst->data = g_list_sort_merge (src->data,
				     src->next->data,
				     (list_func) compare_func,
				     0, NULL);
      dstprev = dst;
      dst = dst->next;
      src = src->next->next;
    }

    /* If number of runs was odd, just keep the last.  */
    if (src)
    {
      dst->data = src->data;
      dstprev = dst;
      dst = dst->next;
    }

    dstprev->next = NULL;
    g_list_free (dst);
  }

  /* After 1st loop: runs = [[2,4,11,12],[1,1,2,4,6,12]].  */
  /* After 2nd loop: runs = [[1,1,2,2,4,4,6,11,12,12]].  */

  list = runs->data;
  g_list_free (runs);
  return list;
}
#endif

--- NEW FILE: list.h ---
/* 
 * Copyright (C) 2003 the oxine project
 * 
 * xine 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.
 * 
 * xine 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
 *
 * $Id: list.h,v 1.1 2005/01/31 02:49:48 miguelfreitas Exp $
 *
 * doubly linked lists with builtin iterator,
 * based upon xine_list functions of the xine project
 */


#ifndef HAVE_LIST_H
#define HAVE_LIST_H

#include <inttypes.h>

typedef struct list_s list_t;

/*
 * create a new, empty list
 */

#ifdef DEBUG
list_t *_list_new_tagged(char*, int) ;
#define list_new() (_list_new_tagged(__FILE__, __LINE__))
#define xine_list_new() (_list_new_tagged(__FILE__, __LINE__))
#else
list_t *list_new (void) ;
#define xine_list_new list_new
#endif

/*
 * dispose a list (and only the list, contents have to be managed separately)
 */

void list_free (list_t *l) ;

/*
 * set cursor to the first element in list, return contents of first entry
 */

void *list_first_content (list_t *l) ;

/*
 * set cursor to the next element, returns its contents
 */

void *list_next_content (list_t *l) ;

/*
 * returns contents of the current element
 */

void *list_current_content (list_t *l) ;

/*
 * returns 1, if list is empty
 */

int list_is_empty (list_t *l) ;

/*
 * set cursor to the last element, returns its contents
 */

void *list_last_content (list_t *l) ;

/*
 * set cursor to the previous element, return its contents
 */

void *list_prev_content (list_t *l) ;

/*
 * sort element into list by priority. list_first_content returns then
 * the element with the highest priority.
 */

void list_append_priority_content (list_t *l, void *content, int priority) ;

/*
 * append element at the end of the list
 */

#ifdef DEBUG
void _list_append_content (list_t *l, void *content, char*, int) ;
#define list_append_content(list,cont) (_list_append_content(list, cont, __FILE__, \
__LINE__)) #define xine_list_append_content(list,cont) (_list_append_content(list, \
cont, __FILE__, __LINE__)) #else
void list_append_content (list_t *l, void *content) ;
#define xine_list_append_content list_append_content
#endif

/*
 * insert content just before cursor position
 */

void list_insert_content (list_t *l, void *content) ;

/*
 * delete current element (its contents have to be freen seperately!)
 */

void list_delete_current (list_t *l) ;

/*
 * compatibility macros
 */

#define xine_list_t list_t
#define xine_list_free list_free
#define xine_list_first_content list_first_content
#define xine_list_next_content list_next_content
#define xine_list_current_content list_current_content
#define xine_list_is_empty list_is_empty
#define xine_list_last_content list_last_content
#define xine_list_prev_content list_prev_content
#define xine_list_append_priority_content list_append_priority_content
#define xine_list_insert_content list_insert_content
#define xine_list_delete_current list_delete_current

/*
 * yet another list implementation
 * thanks to glib project.
 */

typedef struct _g_list_t g_list_t;

struct _g_list_t
{
  void* data;
  g_list_t *next;
  g_list_t *prev;
};

typedef int(*list_compare)(const void*, const void*);
typedef int(*list_compare_data)(const void*, const void*, void*);
typedef int(*list_func)(void*,void*);

/* Doubly linked lists
 */
//void   g_list_push_allocator (GAllocator *allocator);
//void   g_list_pop_allocator (void);
g_list_t* g_list_new (void);
void   g_list_free (g_list_t *list);
void   g_list_free_1 (g_list_t *list);
g_list_t* g_list_append (g_list_t *list, void *data);
g_list_t* g_list_prepend (g_list_t *list, void *data);
g_list_t* g_list_insert (g_list_t *list, void *data, int position);
g_list_t* g_list_insert_sorted (g_list_t *list, void* data, list_compare func);
g_list_t* g_list_insert_before (g_list_t *list, g_list_t *sibling, void* data);
g_list_t* g_list_concat (g_list_t *list1, g_list_t *list2);
g_list_t* g_list_remove (g_list_t *list, const void *data);
g_list_t* g_list_remove_all (g_list_t *list, const void *data);
g_list_t* g_list_remove_link (g_list_t *list, g_list_t *llink);
g_list_t* g_list_delete_link (g_list_t *list, g_list_t *link_);
g_list_t* g_list_reverse (g_list_t *list);
g_list_t* g_list_copy (g_list_t *list);
g_list_t* g_list_nth (g_list_t *list, unsigned int n);
g_list_t* g_list_nth_prev (g_list_t *list, unsigned int n);
g_list_t* g_list_find (g_list_t *list, const void *data);
g_list_t* g_list_find_custom (g_list_t *list, const void *data, list_compare func);
int   g_list_position (g_list_t *list, g_list_t *llink);
int   g_list_index (g_list_t *list, const void * data);
g_list_t* g_list_last (g_list_t *list);
g_list_t* g_list_first (g_list_t *list);
unsigned int  g_list_length (g_list_t *list);
void   g_list_foreach (g_list_t *list, list_func func, void* user_data);
g_list_t* g_list_sort (g_list_t *list, list_compare compare_func);
g_list_t* g_list_sort_with_data (g_list_t *list, list_compare compare_func, void* \
user_data); void* g_list_nth_data (g_list_t *list, unsigned int n);

#define g_list_previous(list)	((list) ? (((g_list_t *)(list))->prev) : NULL)
#define g_list_next(list)	((list) ? (((g_list_t *)(list))->next) : NULL)

#endif

--- NEW FILE: mediamarks.c ---
/*
 * Copyright (C) 2002-2003 Stefan Holst
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * $Id: mediamarks.c,v 1.1 2005/01/31 02:49:48 miguelfreitas Exp $
 *
 * parsing mediamarks
 */

#define _GNU_SOURCE   /* this is needed for getline(..) and strndup(..) */

#include "config.h"

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <libgen.h>

#include "common.h"

#include "oxine.h"
#include "otk.h"
#include "xine/xmlparser.h"
#include "utils.h"

extern gGui_t          *gGui;

#define TYPE_NONE (0)
#define TYPE_DIR  (1)
#define TYPE_REG  (2)
#define TYPE_RDIR (3)
#define TYPE_RREG (4)
#define TYPE_UP   (5)
#define TYPE_MULTIPLE (6)
#define TYPE_M3U  (7)

/* sort methods 0 is default*/
#define PLAYITEM_SORT_FILES (1)
#define PLAYITEM_SORT_DIRFILES (0)

#define MM_ACTION_PLAY (1)
#define MM_ACTION_ADD (2)

const char *mm_action_strings[] = { "Play", "Add to PL" };

typedef struct playitem_s playitem_t;

struct playitem_s {

  int   type;
  char *title;
  char *mrl;
  list_t *sub;
};

typedef struct mm_session_s mm_session_t;

struct mm_session_s {

  oxine_t            *oxine;
  otk_widget_t       *list;
  int                listpos;
  list_t             *backpath;
  int                action;
  char               ilabel[64];
};

/* private functions */
static int file_is_m3u(const char *mrl);


/* body of mediamarks functions */

void bpush (list_t *list, list_t *item) {

  list_first_content(list);
  list_insert_content(list, item);
}

list_t *bpop (list_t *list) {

  list_t *item;

  item = list_first_content(list);
  list_delete_current(list);

  return item;
}

/*
 * implements case insensitive lengthlexicographic order
 * 
 * returns 1  if a <= b
 *         0  otherwise.
 */

static int in_cill_order(playitem_t *ita, playitem_t *itb, int type) {

  char *a = ita->title;
  char *b = itb->title;

  char *ap, *bp; /* pointers to a, b */
  char ac, bc;

  int rtypea=0, rtypeb=0;

  ap = a; bp = b;

  /* printf("%s %s\n", a, b); 
  stat(ita->mrl, &filestat1);
  stat(itb->mrl, &filestat2);*/

  if(type == PLAYITEM_SORT_DIRFILES) {
    if((ita->type == TYPE_DIR)||(ita->type == TYPE_RDIR)) rtypea = 1;
    if((itb->type == TYPE_DIR)||(itb->type == TYPE_RDIR)) rtypeb = 1;

    if((rtypea)&&(!rtypeb)) return 1;
    if((!rtypea)&&(rtypeb)) return 0;

    /*if((S_ISDIR(filestat1.st_mode))&&(!S_ISDIR(filestat2.st_mode))) return 1;
      if((!S_ISDIR(filestat1.st_mode))&&(S_ISDIR(filestat2.st_mode))) return 0;*/
  }

  while (1) {
    ac = *ap; bc = *bp;
    if ((ac >= 'A') && (ac <= 'Z')) ac |= 0x20;
    if ((bc >= 'A') && (bc <= 'Z')) bc |= 0x20;

    if (ac < bc) return 1;
    if (ac > bc) return 0;
    if (!ac) return 1;

    ap++; bp++;
  }
  return 1;
}


static void playitem_sort_into(playitem_t *item, list_t *list, int type) {

  playitem_t *i;

  i = list_first_content(list);

  while(i) {
    if (in_cill_order(item, i, type)) {      
      list_insert_content(list, item);
      return;
    }
    i = list_next_content(list);
  }
  list_append_content(list, item);

}

static void playitem_append(playitem_t *item, list_t *list) {

  list_append_content(list, item);
}

static playitem_t *playitem_new(int type, char *title, char *mrl, list_t *sub) {

  playitem_t *item = ho_new(playitem_t);
      
  item->type = type;
  item->sub = sub;
  if (title) item->title = ho_strdup(title);
  if (mrl) item->mrl = ho_strdup(mrl);

  return item;
}

static void free_subtree(list_t *list, int i) {

  playitem_t *item;
 
  if (!list) return;
  
  item = list_first_content(list);
  while (item) {
    if (item->title) ho_free(item->title);
    if (item->mrl) ho_free(item->mrl);
    if(item->type != TYPE_UP) free_subtree(item->sub, 1);    
    list_delete_current(list);
    ho_free(item);
    item = list_first_content(list);
  }
  
  if(i) list_free(list);
}

/*static void playitem_free(playitem_t *item) {
  playitem_t *child;

  if(item->title) ho_free(item->title);
  if(item->mrl) ho_free(item->mrl);

  if(item->sub) {
    child = list_first_content(item->sub);

    while(child) {
      playitem_free(child);
      list_delete_current(item->sub);
      child = list_first_content(item->sub);
    }
    list_free(item->sub);
  }
      
  ho_free(item);
  }*/

static playitem_t *playitem_load (xml_node_t *node) {

  playitem_t *play_item;
  char *type;
  struct stat filestat;

  play_item = ho_new(playitem_t);

  while (node) {

    if (!strcasecmp (node->name, "title")) {
      play_item->title = ho_strdup (node->data);
#ifdef LOG
      printf ("mediamarks: title = %s\n", play_item->title);
#endif
    } else if (!strcasecmp (node->name, "ref")) {
      play_item->mrl = ho_strdup(xml_parser_get_property (node, "href"));
      type = xml_parser_get_property(node, "type");
      if(type) {
	if(!strcasecmp(type, "multiple")) {
	  play_item->type = TYPE_MULTIPLE;
	  play_item->sub = list_new();
	}
      } else {
	filestat.st_mode = 0;
	stat(play_item->mrl, &filestat);
	
	if(S_ISDIR(filestat.st_mode)) {
	  play_item->sub = list_new();
	  play_item->type = TYPE_RDIR;
	} else {
	  play_item->type = TYPE_REG;
	  if(file_is_m3u(play_item->mrl)) play_item->type = TYPE_M3U;
	}
      }
    
#ifdef LOG
      printf ("mediamarks: mrl   = %s\n", play_item->mrl);
#endif
      } else if (!strcasecmp (node->name, "time")) {
    } else 
      printf ("mediamarks: error while loading mediamarks file, unknown node '%s'\n", \
node->name);

    node = node->next;
  }

  return play_item;
}

static int parse_multiple(oxine_t *oxine, const char *mrl, list_t *list) {
  int i=0, num=0;
  playitem_t *item;
  char **str;

  str = xine_get_autoplay_mrls (oxine->xine, mrl, &num);
  if (num<=0) return 0;
 
  free_subtree(list, 0);

  while(i < num) {
    /* printf("%d %s\n", i, str[i]); */
    item = playitem_new(TYPE_REG, str[i], str[i], list_new());
    playitem_append(item, list);
    i++;
  }
  return 1;
}

static void read_directory(oxine_t *oxine, const char *dir, list_t *list) {
  
  DIR *dirp;
  struct dirent *entp;
  playitem_t *item;

  free_subtree(list, 0);

#if 0
  printf("Processing %s\n", dir);
#endif
  dirp = opendir (dir);
  if (dirp != NULL)
    {
      while ((entp = readdir(dirp))) {

        struct stat filestat;
	char mrl[1024];
	char title[256];
	int type = 0;

	if((!strcmp(entp->d_name, "."))||(!strcmp(entp->d_name, ".."))) continue;
	
	snprintf(mrl, 1023, "%s/%s", dir, entp->d_name);
	stat(mrl, &filestat);
	if(S_ISDIR(filestat.st_mode)) {
	  type = TYPE_RDIR;
	  snprintf(title, 255, "[%s]", entp->d_name);	  
	}else if (S_ISREG(filestat.st_mode)) {
	  strncpy(title, entp->d_name, 255);
	  type = TYPE_RREG;
	}
	if(file_is_m3u(mrl)) {
	  type = TYPE_M3U;
	  snprintf(title, 255, "[%s]", entp->d_name);
	}

	item = playitem_new(type, title, mrl, list_new());
#if 0
	printf("mrl   : %s\n", item->mrl);
	printf("title : %s\n", item->title);
	printf("type  : %d\n", item->type);
#endif
	/* printf("ei %d\n", oxine->mm_sort_type); */
	/* xine_config_lookup_entry (oxine->xine, "oxine.sort_type", &entry); */
 
	playitem_sort_into(item, list, oxine->mm_sort_type);
      }
      closedir (dirp);
    }
  else
    printf ("mediamarks: Couldn't open the directory.\n");
}

static char *read_entire_file (const char *mrl, int *file_size) {

  char        *buf;
  struct stat  statb;
  int          fd;

  if (stat (mrl, &statb) < 0) {
    printf ("mediamarks: cannot stat '%s'\n", mrl);
    return NULL;
  }

  *file_size = statb.st_size;

  fd = open (mrl, O_RDONLY);
  if (fd<0)
    return NULL;

  /*  buf = malloc (sizeof(char)*((*file_size)+1)); */
  buf = ho_newstring((*file_size)+1);

  if (!buf)
    return NULL;

  buf[*file_size]=0;

  *file_size = read (fd, buf, *file_size);

  close (fd);

  return buf;
}

static int file_is_m3u(const char *mrl) {

#ifdef M3U_AS_SUBDIR

  FILE *file;
  char **line;
  int *n;
  int a;

  file = fopen(mrl, "r");
  if(!file) return 0;

  n = ho_new(size_t);
  line = ho_new(char *);

  *line = NULL;
  *n = 0;
  a = getline(line, n, file);
  if(a<=0) {
    ho_free(n);
    ho_free(line);
    return 0;
  }
  if(!strncmp(*line, "#EXTM3U", 7)) { 
    ho_free(n);
    ho_free(line);
    return 1;
  }

  ho_free(n);
  ho_free(line);

  if( strstr(mrl,".m3u") != NULL || strstr(mrl,".M3U") )
    return 1;
  else
    return 0;

#else

  return 0; /* m3u is a normal file here, let xine-ui handle it as playlist */

#endif
}

static void parse_m3u(const char *mrl, list_t *items) {
  FILE *file;
  char **line;
  int *n;
  int a;

  file = fopen(mrl, "r");
  if(!file) return ;

  n = ho_new(size_t);
  line = ho_new(char *);

  *line = NULL;
  *n = 0;
  a = getline(line, n, file);
  if(a<=0) return;

  while((a = getline(line, n, file))>0) {
    char *str;
    playitem_t *item;

    if(*line[0] == '#') continue;
    str = strndup(*line, a-1);
    /* printf("%s\n", str); */
    item = playitem_new (TYPE_REG, basename(str), str, list_new());
    ho_free(str);
    playitem_append(item, items);
  }
  ho_free(line);
  ho_free(n);
  fclose(file);
}

static void read_subs(xml_node_t *node, list_t *items) {

  playitem_t *item = NULL;
  
  while (node) {

    if (!strcasecmp (node->name, "entry")) {
      
      item = playitem_load (node->child);
      
    } else if(!strcasecmp (node->name, "sub")) {
      char title[256];
      
      snprintf(title, 255, "[%s]", xml_parser_get_property (node, "name"));
      item = playitem_new (TYPE_DIR, title, NULL, list_new());
      read_subs(node->child, item->sub);
    } 
    playitem_append(item, items);
    node=node->next;
  }
}

static int read_mediamarks(list_t *list, const char *mrl) {

  int size;
  char *file = read_entire_file(mrl, &size);
  xml_node_t *node;

  if (!file) return 0;

  xml_parser_init (file, strlen (file), XML_PARSER_CASE_INSENSITIVE);

  if (xml_parser_build_tree (&node)<0) {
    printf("mediamarks: xml parsing of %s failed\n", mrl);
    return 0;
  }
  
  if (strcasecmp (node->name, "oxinemm")) {
    printf ("mediamarks: error, root node must be OXINEMM\n");
    return 0;
  }

  read_subs(node->child, list);
  xml_parser_free_tree(node);
  ho_free(file);

  return 1;
}

static void changelist (otk_widget_t *list, list_t *backpath) {

  playitem_t *item, *back;
  list_t *current = list_first_content(backpath);
  list_t *parent = list_next_content(backpath);

  otk_remove_listentries(list);

  item = list_first_content(current);

  if((item->type != TYPE_UP)&&(parent)) {
    back = playitem_new(TYPE_UP, "[..]", NULL, parent);
    list_insert_content(current, back);
    //otk_add_listentry(list, back->title, back, -1);
  } 
  item = list_first_content(current);
  while (item) {
    /*printf("item : %s\n" ,item->title);*/
    otk_add_listentry(list, item->title, item, -1);

    item = list_next_content(current);
  }
  otk_list_set_pos(list,0);
  otk_set_focus(list);
}

static void action_changed (void *data, int pos) {

  mm_session_t *session = (mm_session_t*) data;

  session->action = pos;
}

static void set_ilabel(mm_session_t *session) {
  int n;
  
  n = gGui->playlist.num;
  sprintf(session->ilabel, "Selected: %3d", n);
}

static void mediamarks_play_cb(void *data, void *entry) {

  mm_session_t *session = (mm_session_t*) data;
  oxine_t *oxine = session->oxine;
  playitem_t *item = (playitem_t*) entry;

  session->listpos = otk_list_get_pos(session->list);
#ifdef LOG
  printf("mediamarks: mediamarks_play_cb %s\n", item->mrl);
  fflush(NULL);
#endif

  if (item->type == TYPE_DIR) {
    bpush(session->backpath, item->sub);
    changelist(session->list, session->backpath);
    otk_draw_all(oxine->otk);
  } else if (item->type == TYPE_RDIR) {
    read_directory(oxine, item->mrl, item->sub);
    bpush(session->backpath, item->sub);
    changelist(session->list, session->backpath);
    otk_draw_all(oxine->otk);
  } else if (item->type == TYPE_UP) {
    bpop(session->backpath);
    changelist(session->list, session->backpath);
    otk_draw_all(oxine->otk);
  } else if (session->action == MM_ACTION_PLAY) {
    if ((item->type == TYPE_REG)||(item->type == TYPE_RREG)) {      
      printf("mediamarks: playing %s\n", item->mrl);
/*      if (is_movie(item->mrl)) {
        oxine->main_window = NULL;
        oxine->info_window = NULL;
        otk_clear(oxine->otk);
        if (oxine->filename)
          ho_free(oxine->filename);
        oxine->filename=ho_strdup(item->mrl);
        select_subtitle_cb(get_dirname(oxine->filename), oxine, 0);
      } else */ if(odk_open_and_play(oxine->odk, item->mrl)) {
        set_ilabel(session);
        oxine->main_window = NULL;
        oxine->info_window = NULL;
        oxine->mode = OXINE_MODE_NORMAL;
        otk_clear(oxine->otk);
      } /* else play_error(oxine); */
      set_ilabel(session);
    } else if (item->type == TYPE_M3U) {
      parse_m3u(item->mrl, item->sub);  
      bpush(session->backpath, item->sub);    
      changelist(session->list, session->backpath);
      otk_draw_all(oxine->otk);
    } else if (item->type == TYPE_MULTIPLE) {
      if (parse_multiple(oxine, item->mrl, item->sub)) {
        bpush(session->backpath, item->sub);
        changelist(session->list, session->backpath);
        otk_draw_all(oxine->otk);
      } else
        printf("mediamarks: error getting autoplay mrls\n");
    } else printf("mediamarks: %s is of unknown type\n", item->mrl);
  } else if (session->action == MM_ACTION_ADD) {
    if ((item->type == TYPE_REG)||(item->type == TYPE_RREG)) {
      odk_enqueue(session->oxine->odk, item->mrl); /* FIXME: item->title unused */
      set_ilabel(session);
      otk_draw_all(session->oxine->otk);
    }
  }
}

static void mediamarks_leavetoplaylist_cb (void *data) {
/*
  mm_session_t *session = (mm_session_t*) data;

  session->oxine->reentry = NULL;
  session->oxine->reentry_data = NULL;
  
  playlist_cb(session->oxine);
*/
}

static void mediamarks_freeall(void *data) {

  mm_session_t *session = (mm_session_t *) data;
  list_t *current = list_first_content(session->backpath);

  current = list_first_content(session->backpath);

  while(current) {
    free_subtree(current, 1);
    list_delete_current(session->backpath);
    current = list_first_content(session->backpath);
  }

  list_free(session->backpath);
}

static void mediamarks_leave_cb (void *data) {

  mm_session_t *session = (mm_session_t*) data;

  session->oxine->reentry = NULL;
  session->oxine->reentry_data = NULL;

  mediamarks_freeall(session);
  
  session->oxine->main_menu_cb(session->oxine);
  ho_free(session);
}

static void mediamarks_reentry_cb (void *data) {

  mm_session_t *session = (mm_session_t*) data;
  oxine_t      *oxine = session->oxine;
  otk_widget_t *b, *list_window;
  
  lock_job_mutex();
  if (oxine->info_window && oxine->media_info_close_cb) {
    oxine->media_info_close_cb(oxine);
  }
  unlock_job_mutex();

  otk_clear(oxine->otk);

  oxine->main_window = otk_window_new (oxine->otk, NULL, 20, 130, 225, 420);

  otk_label_new (oxine->main_window, 108, 60, OTK_ALIGN_CENTER|OTK_ALIGN_BOTTOM, \
"Action:");  b = otk_selector_new (oxine->main_window, 10, 80, 195, 60, \
mm_action_strings, 2, action_changed, session);  otk_set_focus(b);
  otk_selector_set(b, session->action);
  otk_button_new (oxine->main_window, 10, 170, 195, 60, "Playlist", \
mediamarks_leavetoplaylist_cb, session);  set_ilabel(session);
  otk_label_new (oxine->main_window, 108, 280, OTK_ALIGN_CENTER|OTK_ALIGN_BOTTOM, \
session->ilabel);  otk_button_new (oxine->main_window, 10, 340, 195, 60, "Back", \
mediamarks_leave_cb, session);

  list_window = otk_window_new (oxine->otk, NULL, 245, 130, 535, 420);

  session->list = otk_list_new(list_window, 10, 15, 523, 390, mediamarks_play_cb, \
session);   changelist(session->list, session->backpath);
  otk_list_set_pos(session->list, session->listpos);
  
  otk_draw_all(oxine->otk);
}


void mediamarks_cb (void *data) {

  oxine_t      *oxine = (oxine_t*) data;
  char         mmpath[1025];
  mm_session_t *session;
  list_t  *items = list_new();

  session = ho_new(mm_session_t);
  session->oxine = oxine;
  session->backpath = list_new();
  session->action = 1;
  session->listpos = 0;

  snprintf(mmpath,1024,"%s/.xine/oxine_mediamarks", getenv("HOME"));
  if (!read_mediamarks(items, mmpath)) {
    printf("mediamarks: trying to load system wide mediamarks\n");
    snprintf(mmpath,1024,"%s/oxine_mediamarks", XINE_SCRIPTDIR);
    if (read_mediamarks(items, mmpath)) {
      bpush(session->backpath, items);
      oxine->reentry_data = session;
      oxine->reentry = mediamarks_reentry_cb;
      mediamarks_reentry_cb(session);
    } else {
      list_free(items);
      list_free(session->backpath);
      ho_free(session);
    }
  } else {
    bpush(session->backpath, items);
    oxine->reentry_data = session;
    oxine->reentry = mediamarks_reentry_cb;
    mediamarks_reentry_cb(session);
  }
}

--- NEW FILE: mediamarks.h ---
/*
 * Copyright (C) 2002 Stefan Holst
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * $Id: mediamarks.h,v 1.1 2005/01/31 02:49:48 miguelfreitas Exp $
 *
 * parsing gxine's mediamarks
 */

#ifndef HAVE_MEDIAMARKS_H
#define HAVE_MEDIAMARKS_H

void mediamarks_cb (void *data);

#endif

--- NEW FILE: odk.c ---
/*
 * Copyright (C) 2002-2003 Stefan Holst
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * $Id: odk.c,v 1.1 2005/01/31 02:49:48 miguelfreitas Exp $
 *
 * odk abstracts from xine and other software / hardware interfaces
 */

/*
#define LOG
*/
 
#include <stdio.h>
#include <string.h>
#include <xine.h>

#include "common.h"
#include "odk.h"
#include "utils.h"

extern gGui_t          *gGui;

#define V_WIDTH 800
#define V_HEIGHT 600

/* determined by xine engine: */
#define NUM_COLORS 256

struct  odk_s {

  xine_t     *xine;
  xine_stream_t *stream;
  
  xine_osd_t *osd;
  int unscaled_osd;
  double vscale, hscale;

  int (*event_handler)(void *data, oxine_event_t *ev);
  void *event_handler_data;

  uint32_t color[NUM_COLORS];
  uint8_t trans[NUM_COLORS];

  int palette_fill;
  char *menu_bg;

  int is_freetype;
};


/* generates a gradient on palette out of given values */

static void palette_transition(odk_t *odk, int from_index, int to_index,
                               uint32_t from_color, uint8_t from_trans,
			       uint32_t to_color, uint8_t to_trans) {
  double cr_step, cb_step, y_step;
  uint8_t cr_from, cb_from, y_from;
  uint8_t cr_to, cb_to, y_to;
  
  double trans_step;
  int num;

  num = to_index - from_index;

  trans_step = (double)(to_trans - from_trans) / num;

  cb_from = from_color & 0xff;
  cr_from = (from_color >> 8) & 0xff;
  y_from  = (from_color >> 16) & 0xff;

  cb_to   = to_color & 0xff;
  cr_to   = (to_color >> 8) & 0xff;
  y_to    = (to_color >> 16) & 0xff;

  cb_step = (double)(cb_to - cb_from) / num;
  cr_step = (double)(cr_to - cr_from) / num;
  y_step  = (double)(y_to  - y_from) / num;

  for (num = from_index; num < to_index; num++) {
    uint8_t cb_cur = cb_from + (int8_t)(cb_step * (num-from_index));
    uint8_t cr_cur = cr_from + (int8_t)(cr_step * (num-from_index));
    uint8_t y_cur  =  y_from + (int8_t)( y_step * (num-from_index));
    
    odk->color[num] = cb_cur + (cr_cur<<8) + (y_cur<<16);
    odk->trans[num] = from_trans + trans_step * (num-from_index);
    /* printf("writing: color %x trans %u to %u\n", odk->color[num], odk->trans[num], \
num); */  }
  odk->color[to_index] = to_color;
  odk->trans[to_index] = to_trans;
}

int odk_get_color(odk_t *odk, uint32_t color, uint8_t trans) {

  int i;

  for(i=0; i<odk->palette_fill; i++) {
    if ((odk->color[i] == color) && (odk->trans[i] == trans))
      return i;
  }
  if (odk->palette_fill == NUM_COLORS) {
    printf("odk: too many colors: palette is full\n");
    return 0;
  }
  odk->color[odk->palette_fill]=color;
  odk->trans[odk->palette_fill]=trans;

  xine_osd_set_palette(odk->osd, odk->color, odk->trans);

  odk->palette_fill++;

  return odk->palette_fill - 1;
}

int odk_alloc_text_palette(odk_t *odk, uint32_t fg_color, uint8_t fg_trans,
                           uint32_t bg_color, uint8_t bg_trans,
                           uint32_t bo_color, uint8_t bo_trans) {

  odk->color[odk->palette_fill]=bg_color; /* not really used by fonts, set to bg */
  odk->trans[odk->palette_fill]=bg_trans;

  /* background (1) to border (6) transition */
  palette_transition(odk, odk->palette_fill+1, odk->palette_fill+6, 
      bg_color, bg_trans, bo_color, bo_trans);

  /* border (6) to foreground (10) transition */
  palette_transition(odk, odk->palette_fill+6, odk->palette_fill+10, 
      bo_color, bo_trans, fg_color, fg_trans);

  odk->palette_fill += 11;
  
  xine_osd_set_palette(odk->osd, odk->color, odk->trans);

  return odk->palette_fill-11;
}

/*
 * adapt osd to new stream size.
 * This only affects primitives drawn after
 * this call.
 */

static void odk_adapt(odk_t *odk) {

  int width, height;

  if (odk->osd) xine_osd_free(odk->osd);

  if( odk->unscaled_osd )
    video_window_get_output_size(&width, &height);
  else
    video_window_get_frame_size(&width, &height);

  lprintf("odk_adapt to: %i, %i\n", width, height);
  odk->vscale = height / (double)V_HEIGHT;
  odk->hscale = width / (double)V_WIDTH;

  odk->osd = xine_osd_new(odk->stream, 0, 0, width, height);

  xine_osd_set_palette(odk->osd, odk->color, odk->trans);
}


uint8_t *odk_get_pixmap(int type) {
  int i;
  uint8_t arrowup[] = {0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
		       0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
		       0, 0, 1, 0, 1, 1, 0, 1, 0, 0,
		       0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
		       0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
		       0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
		       0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
		       0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
		       0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
		       0, 0, 0, 0, 1, 1, 0, 0, 0, 0};
  uint8_t arrowdown[] = {0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
			 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
			 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
			 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
			 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
			 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
			 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
			 0, 0, 1, 0, 1, 1, 0, 1, 0, 0,
			 0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
			 0, 0, 1, 1, 1, 1, 1, 1, 0, 0};

  uint8_t *p = NULL;

  p = ho_newstring(10*10 + 10);

  switch(type) {
  case PIXMAP_SIMPLE_ARROW_UP:
    memcpy(p, arrowup, 100);
    break;
  case PIXMAP_SIMPLE_ARROW_DOWN:
    memcpy(p, arrowdown, 100);
    break;
  }

  /* FIXME: check this stuff "XINE_OSD_TEXT1 + 2" */
  for(i=0;i<100;i++) p[i] = p[i] * XINE_OSD_TEXT1 + 2;

  return p;
}


/*
 * initializes drawable, xine stream and osd
 */

odk_t *odk_init(void) {

  odk_t *odk = ho_new_tagged(odk_t, "odk object");

  lprintf("initializing\n");

  odk->palette_fill = 0;
  odk->hscale = 0;
  odk->vscale = 0;

  odk->xine = gGui->xine;
  odk->stream = gGui->stream;

  /* test unscaled osd support */
  odk->osd = xine_osd_new(odk->stream, 0, 0, 10, 10);
  odk->unscaled_osd =
    (xine_osd_get_capabilities(odk->osd) & XINE_OSD_CAP_UNSCALED );
  xine_osd_free(odk->osd);
  odk->osd = NULL;

  odk_adapt(odk);

  return odk;
}


/*
 * primitive drawing functions
 */

void odk_draw_bitmap(odk_t *odk, uint8_t *bitmap, int x1, int y1, int width, int \
height,  uint8_t *palette_map)
{
  /* uint8_t a[] = {0, 0, 0, 255, 255, 255}; */
  if (!(odk->hscale&&odk->vscale)) return;

  /* FIXME: scaling? */
  xine_osd_draw_bitmap(odk->osd, bitmap, x1, y1, width, height, NULL);
}


void odk_draw_line(odk_t *odk, int x1, int y1, int x2, int y2, int color) {

 int px1, py1, px2, py2;

  if (!(odk->hscale&&odk->vscale)) return;

 px1 = (int) ((double)x1*odk->hscale);
 py1 = (int) ((double)y1*odk->vscale);
 px2 = (int) ((double)x2*odk->hscale);
 py2 = (int) ((double)y2*odk->vscale);
 lprintf("drawing line %u %u %u %u col: %u\n", px1, py1, px2, py2, color);

  xine_osd_draw_line(odk->osd,
      px1,
      py1,
      px2,
      py2,
      color);
}


void odk_draw_rect(odk_t *odk, int x1, int y1, int x2, int y2, int filled, int color) \
{

 int px1, py1, px2, py2;

  if (!(odk->hscale&&odk->vscale)) return;

 px1 = (int) ((double)x1*odk->hscale);
 py1 = (int) ((double)y1*odk->vscale);
 px2 = (int) ((double)x2*odk->hscale);
 py2 = (int) ((double)y2*odk->vscale);
 lprintf("drawing rect %u %u %u %u col: %u\n", px1, py1, px2, py2, color);

  xine_osd_draw_rect(odk->osd,
      px1, py1, px2, py2, color, filled);
}


void odk_draw_text(odk_t *odk, int x, int y, const char *text, int alignment, int \
color) {

  int px, py, w, h;

  if (!(odk->hscale&&odk->vscale)) return;

  odk_get_text_size(odk, text, &w, &h);

  if (odk->is_freetype) {
    if (alignment & ODK_ALIGN_VCENTER) y += h/2;
    if (alignment & ODK_ALIGN_TOP)  y += h;
  } else {
    if (alignment & ODK_ALIGN_VCENTER) y -= h/2;
    if (alignment & ODK_ALIGN_BOTTOM)  y -= h;
  }

  if (alignment & ODK_ALIGN_CENTER)  x -= w/2;
  if (alignment & ODK_ALIGN_RIGHT)   x -= w;

  px = (int) ((double)x*odk->hscale);
  py = (int) ((double)y*odk->vscale);

  lprintf("drawing text at %u %u (vs: %f hs: %f)\n", px, py, odk->vscale, \
odk->hscale);

  xine_osd_draw_text(odk->osd, px, py, text, color);
}


/*
 * overall osd control
 */

void odk_show(odk_t *odk) {
  if( odk->unscaled_osd )
    xine_osd_show_unscaled(odk->osd, 0);
  else
    xine_osd_show(odk->osd, 0);
}

void odk_hide(odk_t *odk) {
  xine_osd_hide(odk->osd, 0);
}

void odk_clear(odk_t *odk) {
  xine_osd_clear(odk->osd);
}


/*
 * font stuff
 */

void odk_get_text_size(odk_t *odk, const char *text, int *width, int *height) {

  int w, h;

  if (!(odk->hscale&&odk->vscale)) {
    *width = 0;
    *height = 0;
    return;
  }

  xine_osd_get_text_size(odk->osd, text, &w, &h);
  *width = (int) ((double)w/odk->hscale);
  *height = (int) ((double)h/odk->vscale);
}


void odk_set_font(odk_t *odk, const char *font, int size) {

  int psize;

  psize = (int)((double)size*(odk->hscale+odk->vscale)/2);

  /* smallest text size possible */
  if (psize<16) psize=16;

  lprintf("setting font to %s %u %u\n", font, size, psize);

  if (strchr(font, '.')||strchr(font, '/'))
    odk->is_freetype = 1;
  else
    odk->is_freetype = 0;

  xine_osd_set_font(odk->osd, font, psize);
}


/*
 * event stuff
 */

void odk_set_event_handler(odk_t *odk, int (*cb)(void *data, oxine_event_t *ev), void \
*data) {

  odk->event_handler = cb;
  odk->event_handler_data = data;
}

int odk_send_event(odk_t *odk, oxine_event_t *event) {

  switch (event->type) {
    case OXINE_EVENT_FORMAT_CHANGED:
      odk_adapt(odk);
      break;
    
    case OXINE_EVENT_BUTTON:
    case OXINE_EVENT_MOTION:
      if( !odk->unscaled_osd ) {
        x11_rectangle_t rect;

        rect.x = event->x;
        rect.y = event->y;
        rect.w = 0;
        rect.h = 0;

        if (xine_port_send_gui_data(gGui->vo_port,
            XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO, (void*)&rect) == -1) {
          return 0;
        }

        event->x = rect.x;
        event->y = rect.y;
      }
      event->x = (int) ((double)event->x/odk->hscale);
      event->y = (int) ((double)event->y/odk->vscale);
      if ((event->x < 0) || (event->x > V_WIDTH)) return 0;
      if ((event->y < 0) || (event->y > V_HEIGHT)) return 0;
  }
  
  if (odk->event_handler)
    return odk->event_handler(odk->event_handler_data, event);

  return 0;
}

/*
 * destructor
 */

void odk_free(odk_t *odk) {

  lprintf("finalizing\n");

  xine_osd_free(odk->osd);

  ho_free(odk);
}


/*
 * xine control
 */

void odk_enqueue(odk_t *odk, const char *mrl)
{
  int t = 0;
  int playlist_num = gGui->playlist.num;

  gui_dndcallback((char *)mrl);

  /* wait a bit to get it enqueued */
  while( t < 20 && playlist_num == gGui->playlist.num )
      usleep(10000);
}

int odk_open_and_play(odk_t *odk, const char *mrl) {

  odk_enqueue(odk, mrl);

  if((xine_get_status(gGui->stream) != XINE_STATUS_STOP)) {
    gGui->ignore_next = 1;
    xine_stop(gGui->stream);
    gGui->ignore_next = 0;
  }

  if( gGui->playlist.num ) {
    gui_set_current_mmk(gGui->playlist.mmk[gGui->playlist.num - 1]);

    return gui_xine_open_and_play(gGui->mmk.mrl, gGui->mmk.sub, 0,
           gGui->mmk.start, gGui->mmk.av_offset, gGui->mmk.spu_offset, 0);
  } else
    return 0;
}

void odk_play(odk_t *odk) {

  gui_xine_play(odk->stream, 0, 0, 1);
}

void odk_stop(odk_t *odk) {

  gui_stop(NULL, NULL);
}

static int get_pos_length(xine_stream_t *stream, int *pos, int *time, int *length) {
  int t = 0, ret = 0;

  if(stream && (xine_get_status(stream) == XINE_STATUS_PLAY)) {
    while(((ret = xine_get_pos_length(stream, pos, time, length)) == 0) && (++t < \
10))  /*printf("wait");*/
      usleep(100000); /* wait before trying again */
  }
  return ret;
}

int odk_get_pos_length_high(odk_t *odk, int *pos, int *time, int *length) {
  int ret = 0;
#if 0
  static int last_time = 0;
#endif
  xine_stream_t *stream = odk->stream;

  if(stream && (xine_get_status(stream) == XINE_STATUS_PLAY)) {
    ret = xine_get_pos_length(stream, pos, time, length);
  }

  /*
   * smoothen the times xine returns a bit. filtering out small backjumps here.
   */

#if 0
  if (ret) {
    if ((*time < last_time) && (*time+1000 > last_time)) {
      *time = last_time;
    } else {
      last_time = *time;
    }
  }
#endif

  return ret;
}

int odk_get_pos_length(odk_t *odk, int *pos, int *time, int *length) {
  return get_pos_length(odk->stream, pos, time, length);
}

/* Return stream position 1..100 */
int odk_get_seek(odk_t *odk) {
  int pos_time, length;
  int pos=1;
  if(!odk_get_pos_length_high(odk, NULL, &pos_time, &length)) {
    return -1;
  }
  if (length) pos = ((pos_time*100) / length);
  /* printf("seek pos : %d\n", pos); */
  return pos;
}


void odk_seek(odk_t *odk, int how) {

  gui_seek_relative(how);
}

void odk_set_speed(odk_t *odk, uint32_t speed) {

  uint32_t s = XINE_SPEED_NORMAL;

  switch(speed) {
    case ODK_SPEED_PAUSE:
      s = XINE_SPEED_PAUSE;
      break;
    case ODK_SPEED_NORMAL:
      s = XINE_SPEED_NORMAL;
      break;
    case ODK_SPEED_SLOW_4:
      s = XINE_SPEED_SLOW_4;
      break;
    case ODK_SPEED_SLOW_2:
      s = XINE_SPEED_SLOW_2;
      break;
    case ODK_SPEED_FAST_4:
      s = XINE_SPEED_FAST_4;
      break;
    case ODK_SPEED_FAST_2:
      s = XINE_SPEED_FAST_2;
      break;
    default:
      printf("odk: odk_set_speed: invalid speed\n");

  }
  xine_set_param(odk->stream, XINE_PARAM_SPEED, s);
}

uint32_t odk_get_speed(odk_t *odk) {

  int s=xine_get_param(odk->stream, XINE_PARAM_SPEED);

  switch(s) {
    case XINE_SPEED_PAUSE:
      return ODK_SPEED_PAUSE;
      break;
    case XINE_SPEED_NORMAL:
      return ODK_SPEED_NORMAL;
      break;
    case XINE_SPEED_SLOW_4:
      return ODK_SPEED_SLOW_4;
      break;
    case XINE_SPEED_SLOW_2:
      return ODK_SPEED_SLOW_2;
      break;
    case XINE_SPEED_FAST_4:
      return ODK_SPEED_FAST_4;
      break;
    case XINE_SPEED_FAST_2:
      return ODK_SPEED_FAST_2;
      break;
    default:
      printf("odk: odk_get_speed: invalid speed\n");
  }
  return -1;
}

void odk_toggle_pause(odk_t *odk) {

  gui_pause (NULL, (void*)(1), 0);
}

void odk_eject(odk_t *odk) {

  xine_eject(odk->stream);
}

char *odk_get_mrl(odk_t *odk) {
  return (char *)mediamark_get_current_mrl();
}

char *odk_get_meta_info(odk_t *odk, int info) {
  const char *str = xine_get_meta_info(odk->stream, info);

  if (!str) return NULL;
  return ho_strdup(str);
}

void odk_user_color(odk_t *odk, char *name, uint32_t *color, uint8_t *trans) {

  char id[512];
  char value[64];
  const char *v;
  unsigned int c, t;

  snprintf(id, 511, "gui.osdmenu.color_%s", name);
  snprintf(value, 63, "%06x-%01x", *color, *trans);
  v = xine_config_register_string (odk->xine, id, value,
				"color specification yuv-opacity",
				"color spec. format: [YUV color]-[opacity 0-f]",
				10, NULL, NULL);
  if (sscanf(v, "%06x-%01x", &c, &t) != 2) {
    printf("odk: bad formated color spec in entry '%s'\n", id);
    printf("odk: using standard color\n");
    return;
  }
  *color = c;
  *trans = t;
}

/*
 * error handling
 */

int odk_get_error(odk_t *odk) {
  return xine_get_error(odk->stream);
}

--- NEW FILE: odk.h ---
/*
 * Copyright (C) 2002-2003 Stefan Holst
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * $Id: odk.h,v 1.1 2005/01/31 02:49:48 miguelfreitas Exp $
 *
 * odk abstracts from xine and other software / hardware interfaces
 */

/*
 * the features of odk are:
 *
 * - framesize independent (virtual) grid (currently 800x600)
 * - color palette management and generation of text palettes
 * - text alignments
 */
 
#ifndef HAVE_ODK_H
#define HAVE_ODK_H

#include <xine.h>
#include <pthread.h>
#include "event.h"

/*
 * alignments for text
 */

#define ODK_ALIGN_LEFT     0x001
#define ODK_ALIGN_CENTER   0x002
#define ODK_ALIGN_RIGHT    0x004
#define ODK_ALIGN_TOP      0x008
#define ODK_ALIGN_VCENTER  0x010
#define ODK_ALIGN_BOTTOM   0x020

#define PIXMAP_SIMPLE_ARROW_UP 1
#define PIXMAP_SIMPLE_ARROW_DOWN 2

/*
 * stream speeds
 */

#define ODK_SPEED_PAUSE    0
#define ODK_SPEED_SLOW_4   1
#define ODK_SPEED_SLOW_2   2
#define ODK_SPEED_NORMAL   4
#define ODK_SPEED_FAST_2   8
#define ODK_SPEED_FAST_4   16

/*
 * opaque odk data type
 */

typedef struct odk_s odk_t;

/*
 * initializes xine streams, create a drawable and returns a new odk object.
 * the given xine struct must have been initialized.
 */

odk_t *odk_init(void);

/*
 * draw primitives
 */

void odk_draw_point(odk_t *odk, int x, int y, int color);
void odk_draw_line(odk_t *odk, int x1, int y1, int x2, int y2, int color);
void odk_draw_rect(odk_t *odk, int x1, int y1, int x2, int y2, int filled, int \
color); void odk_draw_text(odk_t *odk, int x, int y, const char *text, int alignment, \
int color);

/*
 * show and hide an osd
 */

void odk_show(odk_t *odk);
void odk_hide(odk_t *odk);

/*
 * clears osd
 */

void odk_clear(odk_t *odk);

/*
 * simple pixmaps
 */ 

uint8_t *odk_get_pixmap(int type);
void odk_draw_bitmap(odk_t *odk, uint8_t *bitmap, int x1, int y1, int width, int \
height,  uint8_t *palette_map);


/*
 * closes streams, osd and frees resources and odk object
 */

void odk_free(odk_t *odk);

/*
 * returns sizes of text if the text would have been drawn with current font
 */

void odk_get_text_size(odk_t *odk, const char *text, int *width, int *height);

/*
 * change current font
 */

void odk_set_font(odk_t *odk, const char *font, int size);

/*
 * allocates 10 colors from palette and returns the index to the first
 * color
 */

int odk_alloc_text_palette(odk_t *odk, uint32_t fg_color, uint8_t fg_trans,
                           uint32_t bg_color, uint8_t bg_trans,
                           uint32_t bo_color, uint8_t bo_trans);

/*
 * returns index to given color.
 */

int odk_get_color(odk_t *odk, uint32_t color, uint8_t trans);

/*
 * use these functions to register your callbacks for different events
 */

void odk_set_event_handler(odk_t *odk, int (*cb)(void *data, oxine_event_t *ev), void \
*data);

int odk_send_event(odk_t *odk, oxine_event_t *event);

/*
 * stops a running stream (if there is one),
 * opens a new stream, returns 0 on success.
 */

int odk_open_and_play(odk_t *odk, const char *mrl);
void odk_enqueue(odk_t *odk, const char *mrl);

/*
 * play, stop, pause, seek a stream
 */

void odk_play(odk_t *odk);
void odk_stop(odk_t *odk);
void odk_set_speed(odk_t *odk, uint32_t speed);
uint32_t odk_get_speed(odk_t *odk);
void odk_seek(odk_t *odk, int how);
void odk_eject(odk_t *odk);

/*
 * Stream info
 */ 

char *odk_get_meta_info(odk_t *odk, int info);
int odk_get_seek(odk_t *odk);    /* 1..100 */
int odk_get_pos_length(odk_t *odk, int *pos, int *time, int *length);
int odk_get_pos_length_high(odk_t *odk, int *pos, int *time, int *length);
char *odk_get_mrl(odk_t *odk);

/*
 * error handling
 */

int odk_get_error(odk_t *odk);

void odk_user_color(odk_t *odk, char *name, uint32_t *color, uint8_t *trans);

#endif

--- NEW FILE: otk.c ---
/*
 * Copyright (C) 2002-2003 Stefan Holst
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * $Id: otk.c,v 1.1 2005/01/31 02:49:48 miguelfreitas Exp $
 *
[...1817 lines suppressed...]

void otk_free (otk_t *otk) {

  lock_job_mutex();
  if(otk->update_job)
    cancel_job(otk->update_job);
  unlock_job_mutex();
  
  otk_clear(otk);
  
  g_list_free(otk->windows);

  if (otk->title_font) free(otk->title_font);
  if (otk->button_font) free(otk->button_font);

  odk_set_event_handler(otk->odk, NULL, NULL);

  pthread_mutex_destroy(&otk->draw_mutex);
  ho_free (otk);
}

--- NEW FILE: otk.h ---
/*
 * Copyright (C) 2002 Stefan Holst
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * $Id: otk.h,v 1.1 2005/01/31 02:49:48 miguelfreitas Exp $
 *
 * otk - the osd toolkit
 */

#ifndef HAVE_OTK_H
#define HAVE_OTK_H

#include "odk.h"
#include "list.h"
#include "event.h"

/*
 * alignments for text
 */

#define OTK_ALIGN_LEFT     0x001
#define OTK_ALIGN_CENTER   0x002
#define OTK_ALIGN_RIGHT    0x004
#define OTK_ALIGN_TOP      0x008
#define OTK_ALIGN_VCENTER  0x010
#define OTK_ALIGN_BOTTOM   0x020

/*
 * opaque data types
 */

typedef struct otk_s otk_t;
typedef struct otk_widget_s otk_widget_t;

/*
 * user contributed callbacks
 */

typedef void (*otk_button_cb_t) (void *user_data);
typedef void (*otk_list_cb_t) (void *user_data, void *entry);
typedef void (*otk_selector_cb_t) (void *user_data, int entry);
typedef int  (*otk_slider_cb_t) (odk_t *odk);
typedef void (*otk_button_uc_t) (void *data);

/*
 * initialisation
 */

otk_t *otk_init (odk_t *odk);

/*
 * creating widgets
 */

otk_widget_t *otk_window_new (otk_t *this, const char *title, int x, int y,
			      int w, int h);

otk_widget_t *otk_button_new (otk_widget_t *win, int x, int y,
			      int w, int h, char *text,
			      otk_button_cb_t cb,
			      void *user_data);

otk_widget_t *otk_button_grid_new (char *text,
			      otk_button_cb_t cb,
			      void *user_data);

otk_widget_t *otk_label_new (otk_widget_t *win, int x, int y, int alignment, 
                             char *text);

otk_widget_t *otk_list_new (otk_widget_t *win, int x, int y, int w, int h, 
			  otk_list_cb_t cb,
			  void *user_data);
otk_widget_t *otk_slider_new (otk_widget_t *win, int x, int y, int w, int h, \
otk_slider_cb_t cb); otk_widget_t *otk_slider_grid_new (otk_slider_cb_t cb);

otk_widget_t *otk_selector_new(otk_widget_t *win, int x, int y, 
                           int w, int h, const char **items, int num,
			   otk_selector_cb_t cb,
			   void *cb_data);

otk_widget_t *otk_selector_grid_new (const char **items, int num,
			      otk_selector_cb_t cb,
			      void *cb_data);

otk_widget_t *otk_layout_new(otk_widget_t *win, int x, int y, int w, int h, int rows, \
int columns);

/*
 * selector widget functions
 */

void otk_selector_set(otk_widget_t *this, int pos);

/*
 * button widget functions
 */

void otk_button_uc_set(otk_widget_t *wid, otk_button_uc_t uc, void *data);

/*
 * layout widget functions
 */

/* layout widget use widget's x,y,w,h to set positions and sizes :
   x = start row : y = start column
   w = columns to be uses : h = rows to be used
*/

void otk_layout_add_widget(otk_widget_t *layout, otk_widget_t *widget, int srow, int \
scol, int frow, int fcol);

/*
 * list widget functions
 */

void otk_add_listentry(otk_widget_t *this, char *text, void *data, int pos);
void otk_remove_listentries(otk_widget_t *this);
int otk_list_get_pos(otk_widget_t *this);
void otk_list_set_pos(otk_widget_t *this, int newpos);

/* returns number of last selected entry (first entry in list is 1) */
int otk_list_get_entry_number(otk_widget_t *this);

/*
 * update drawing area
 */

void otk_draw_all (otk_t *otk);

/* on 1, update this widget continously (every 100 ms) */
void otk_set_update(otk_widget_t *this, int update);

/*
 * set focus to a specific widget (causes buttons lighting up)
 */

void otk_set_focus (otk_widget_t *widget);

/*
 * frees given widget and all its childs
 */

void otk_destroy_widget (otk_widget_t *widget);

/*
 * frees all widgets
 */

void otk_clear (otk_t *otk);

/*
 * frees otk struct
 */

void otk_free (otk_t *otk);

/*
 * event stuff
 */

void otk_set_event_handler(otk_t *otk, int (*cb)(void *data, oxine_event_t *ev), void \
*data);

int otk_send_event(otk_t *otk, oxine_event_t *event);

#endif


--- NEW FILE: oxine.c ---
/*
 * Copyright (C) 2002-2003 Stefan Holst
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * $Id: oxine.c,v 1.1 2005/01/31 02:49:48 miguelfreitas Exp $
 *
 * oxine main program
 */

#define LOG

#include "config.h"

#include <stdio.h>
#include <ctype.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <getopt.h>

#include <xine.h>
#include "common.h"
#include "oxine.h"
#include "odk.h"
#include "otk.h"
#include "event.h"
#include "utils.h"

#include "mediamarks.h"

extern gGui_t          *gGui;
static oxine_t         *oxine_instance = NULL;

static void main_menu_cb(void *data);
static void playing_menu_cb(void *data);
static void media_stop_cb(void *data);
static void media_info_cb(void *data);
//static void media_returnto_cb(void *data);
//static void media_freeandreturnto_cb(void *data);
static void media_pause_cb(void *data, int i);

//static void event_delay(void *data);

const char *playpause_strings[] = { "<", ">" };

static void media_pause_cb(void *data, int i) {
  oxine_t *oxine = (oxine_t*) data;
 
  switch(i) {
    case 1:
      odk_set_speed(oxine->odk, ODK_SPEED_NORMAL);
      break;
    case 2:
      odk_set_speed(oxine->odk, ODK_SPEED_PAUSE);
      break;
  }
}

static void format_time(char *buf, int sec) {
    sprintf(buf, "%d:%02d:%02d", sec/3600, (sec%3600)/60, ((sec%3600)%60));
}

static void media_stop_cb(void *data) {
  oxine_t *oxine = (oxine_t*) data;

  odk_stop(oxine->odk);
  /*
  odk_open_and_play(oxine->odk, oxine->menu_bg);
  */
  oxine->main_menu_cb(oxine);
}

static void media_tracks_list_cb(void *data, void *entry) {
  oxine_t *oxine = (oxine_t *) data;
  char *mrl = (char *) entry;

  odk_stop(oxine->odk);
  odk_open_and_play(oxine->odk, mrl);
}

/*
static void media_tracks_quit(void *data) {
  oxine_t *oxine = (oxine_t*) data;

  oxine->need_draw = 0;
  printf("%d\n", oxine->mode);
  oxine->mode = OXINE_MODE_NORMAL;
  printf("%d\n", oxine->mode);
  oxine->pauseplay = NULL;
  otk_clear(oxine->otk); 
}
*/

static void media_tracks_cb(void *data) {
  oxine_t *oxine = (oxine_t *) data;
  int num, i = 0;
  char **str;
  otk_widget_t *list;

  oxine->pauseplay = NULL;
  oxine->main_window = NULL;
  oxine->info_window = NULL;
  otk_clear(oxine->otk);
  oxine->main_window = otk_window_new (oxine->otk, NULL, 50, 50, 550, 500);
  list = otk_list_new(oxine->main_window, 10, 10, 500, 490, media_tracks_list_cb, \
oxine);

  str = xine_get_autoplay_mrls (oxine->xine, "CD", &num);
  while(i < num) {
    /* printf("%d %s\n", i, str[i]); */
    otk_add_listentry(list, str[i], str[i], -1);
    i++;
  } 
  otk_list_set_pos(list, 0);
  otk_set_focus(list);
  otk_draw_all(oxine->otk);
}

#if 0
static void info_button_time(void *data) {
  oxine_t *oxine = (oxine_t *)data;
  int ret, pos_time, length;
  
  ret = odk_get_pos_length_high(oxine->odk, NULL, &pos_time, &length);  
  /* oxine->time = malloc(sizeof(char)*255); */
  if(ret) {
    pos_time /= 1000;
    length /= 1000;
    sprintf(oxine->time, "(%d:%02d:%02d / %d:%02d:%02d)", pos_time/3600, \
(pos_time%3600)/60, ((pos_time%3600)%60),   length/3600, (length%3600)/60, \
((length%3600)%60));  }
  else sprintf(oxine->time, "N/A");
}
#endif

static void media_info_close_cb(void *data) {
  oxine_t *oxine = (oxine_t*) data;

  if (!oxine->info_window) return;
  
  otk_destroy_widget(oxine->info_window);
  oxine->info_window = NULL;
  otk_draw_all(oxine->otk);

  if (oxine->lines[0]) ho_free(oxine->lines[0]);
  if (oxine->lines[1]) ho_free(oxine->lines[1]);
  if (oxine->lines[2]) ho_free(oxine->lines[2]);
}

static void media_info_cb(void *data) {
  oxine_t *oxine = (oxine_t*) data;
  int pos_time, length, ret;
  int cline = 0;
  char *buf1, *buf2;
  /* otk_widget_t *b, *layout; */

  oxine->media_info_close_cb = media_info_close_cb;
  oxine->pauseplay = NULL;
  if (oxine->info_window) {
    otk_destroy_widget(oxine->info_window);
    oxine->info_window = NULL;
    otk_draw_all(oxine->otk);
    return;
  }
  /* otk_clear(oxine->otk); */

  buf2 = NULL;
  buf1 = odk_get_mrl(oxine->odk);
  if (buf1) {
    buf2 = strrchr(buf1,'/');
    if (buf2) buf2++; else buf2 = buf1;
  }

  if (oxine->lines[0]) ho_free(oxine->lines[0]);
  if (oxine->lines[1]) ho_free(oxine->lines[1]);
  if (oxine->lines[2]) ho_free(oxine->lines[2]);
  
  oxine->lines[0] = odk_get_meta_info(oxine->odk, XINE_META_INFO_TITLE);
  if(!oxine->lines[0] && buf2) oxine->lines[0] = ho_strdup(buf2);
  if(oxine->lines[0]) cline++;
  buf1 = odk_get_meta_info(oxine->odk, XINE_META_INFO_ARTIST);
  buf2 = odk_get_meta_info(oxine->odk, XINE_META_INFO_ALBUM);
  if (buf1 && buf2) {
    oxine->lines[cline] = ho_newstring(strlen(buf1)+strlen(buf2)+10);
    sprintf(oxine->lines[cline], "%s: %s", buf1, buf2);
    cline++;
  } else if (buf1 && (strlen(buf1)>0)) {
    oxine->lines[cline++] = ho_strdup(buf1);
  } else if (buf2 && (strlen(buf2)>0)) {
    oxine->lines[cline++] = ho_strdup(buf2);
  }
  /*
  oxine->genre = odk_get_meta_info(oxine->odk, XINE_META_INFO_GENRE);
  if(!oxine->genre) oxine->genre = ho_strdup("Genre N/A");
  oxine->year = odk_get_meta_info(oxine->odk, XINE_META_INFO_YEAR);
  if(!oxine->year) oxine->year = ho_strdup("Year N/A");
*/
  ret = odk_get_pos_length_high(oxine->odk, NULL, &pos_time, &length);  

  if(ret && (length > 0)) {
    oxine->lines[cline] = ho_newstring(255);
    pos_time /= 1000;
    length /= 1000;
    format_time(oxine->lines[cline], length);
  }

  oxine->info_window = otk_window_new (oxine->otk, NULL, 5, 5, 790, 240);

  if (oxine->lines[0])
  otk_label_new(oxine->info_window, 5, 40, OTK_ALIGN_LEFT|OTK_ALIGN_VCENTER, \
oxine->lines[0]);  
  if (oxine->lines[1])
  otk_label_new(oxine->info_window, 5, 110, OTK_ALIGN_LEFT|OTK_ALIGN_VCENTER, \
oxine->lines[1]);  
  if (oxine->lines[2])
  otk_label_new(oxine->info_window, 5, 180, OTK_ALIGN_LEFT|OTK_ALIGN_VCENTER, \
oxine->lines[2]);  
  /*
  layout = otk_layout_new(oxine->info_window, 10, 10, 680, 480, 6, 1);

  b = otk_button_grid_new(oxine->title, media_freeandreturnto_cb, oxine);  
  otk_layout_add_widget(layout, b, 0, 0, 1, 1);
  otk_set_focus(b);
 
  b = otk_button_grid_new(oxine->artist, media_freeandreturnto_cb, oxine);  
  otk_layout_add_widget(layout, b, 0, 1, 1, 1);

  b = otk_button_grid_new(oxine->genre, media_freeandreturnto_cb, oxine);  
  otk_layout_add_widget(layout, b, 0, 2, 1, 1);
   
  b = otk_button_grid_new(oxine->album, media_freeandreturnto_cb, oxine);  
  otk_layout_add_widget(layout, b, 0, 3, 1, 1);
 
  b = otk_button_grid_new(oxine->year, media_freeandreturnto_cb, oxine);  
  otk_layout_add_widget(layout, b, 0, 4, 1, 1);
  
  b = otk_button_grid_new(oxine->time, media_freeandreturnto_cb, oxine);  
  otk_layout_add_widget(layout, b, 0, 5, 1, 1);
  otk_button_uc_set(b, info_button_time, oxine);
 */
  
  otk_draw_all(oxine->otk);

  schedule_job(5000, media_info_close_cb, oxine);
}

/*
static void media_returnto_cb(void *data) {
  oxine_t *oxine = (oxine_t*) data;

  oxine->need_draw = 0;
  printf("%d\n", oxine->mode);
  oxine->mode = OXINE_MODE_NORMAL;
  printf("%d\n", oxine->mode);
  oxine->pauseplay = NULL;
  otk_clear(oxine->otk); 
}
*/

static void shutdown_cb (void *data) {
  /*
  oxine_t *oxine = (oxine_t*) data;
  */
}

static void dvb_cb (void *data) {
  oxine_t *oxine = (oxine_t*) data;

  oxine->pauseplay = NULL;
  oxine->main_window = NULL;
  otk_clear(oxine->otk);
  oxine->mode = OXINE_MODE_NORMAL;
  odk_open_and_play(oxine->odk, "dvb://");
}

static void tv_cb (void *data) {
  oxine_t *oxine = (oxine_t*) data;

  oxine->pauseplay = NULL;
  oxine->main_window = NULL;
  otk_clear(oxine->otk);
  oxine->mode = OXINE_MODE_NORMAL;
  odk_open_and_play(oxine->odk, "v4l://");
}


static void playing_menu_update(void *data) {
  oxine_t *oxine = (oxine_t *) data;
  int pos_time, length;

  if (oxine->pos_str)
    if (odk_get_pos_length_high(oxine->odk, NULL, &pos_time, &length)) {
      pos_time /= 1000;
      format_time(oxine->pos_str, pos_time);
    }
}

static void playing_menu_cb(void *data) {
   oxine_t *oxine = (oxine_t *) data;
   otk_widget_t *b, *l;
   otk_widget_t *layout;
   int pos_time, length;
 
   oxine->pauseplay = NULL;
   if (oxine->main_window) {
     otk_destroy_widget(oxine->main_window);
     oxine->main_window = NULL;
     otk_draw_all(oxine->otk);
   }
   
   /* otk_clear(oxine->otk); */

   if(oxine->mode == OXINE_MODE_PLAY_MENU) {
     oxine->mode = OXINE_MODE_NORMAL;
     return ;
   }
   oxine->mode = OXINE_MODE_PLAY_MENU;

   oxine->main_window = otk_window_new (oxine->otk, NULL, 50, 400, 700, 150);
   layout = otk_layout_new(oxine->main_window, 10, 10, 680, 130, 2, 6); 
   oxine->pauseplay = otk_selector_grid_new (playpause_strings, 2, media_pause_cb, \
oxine);     otk_layout_add_widget(layout, oxine->pauseplay, 0, 0, 1, 1);
   otk_set_focus(oxine->pauseplay);
   if (odk_get_speed(oxine->odk) == ODK_SPEED_PAUSE) \
otk_selector_set(oxine->pauseplay, 2);  
   b = otk_button_grid_new ("}", media_stop_cb, oxine);  
   otk_layout_add_widget(layout, b, 1, 0, 1, 1);
   
/*   b = otk_button_grid_new ("Volume", NULL, NULL);
   otk_layout_add_widget(layout, b, 0, 1, 1, 1);
 
   b = otk_slider_grid_new (odk_get_volume);
   otk_layout_add_widget(layout, b, 1, 1, 1, 1);

   if(!oxine->cd_in_use) {
     b = otk_button_grid_new("Seek", NULL, NULL);
     otk_layout_add_widget(layout, b, 0, 2, 1, 1);
   }else {
     b = otk_button_grid_new ("T", media_tracks_cb, oxine);
     otk_layout_add_widget(layout, b, 0, 2, 1, 1);
   }*/
   
   if(oxine->cd_in_use) {
     b = otk_button_grid_new ("T", media_tracks_cb, oxine);
     otk_layout_add_widget(layout, b, 3, 0, 1, 1);
   }
   
   b = otk_slider_grid_new (odk_get_seek);
   otk_layout_add_widget(layout, b, 2, 1, 4, 1);
   otk_set_update(b,1);

   b = otk_button_grid_new ("i", media_info_cb, oxine);
   otk_layout_add_widget(layout, b, 2, 0, 1, 1);
   
   if (!oxine->pos_str) oxine->pos_str = ho_newstring(64);
   if (odk_get_pos_length_high(oxine->odk, NULL, &pos_time, &length)) {
     pos_time /= 1000;
     format_time(oxine->pos_str, pos_time);
   }
   
   l = otk_label_new (oxine->main_window,  110, 100, \
OTK_ALIGN_CENTER|OTK_ALIGN_VCENTER, oxine->pos_str);  otk_set_update(l,1);
   otk_button_uc_set(b, playing_menu_update, oxine);

   /* oxine->need_draw = 1; */

   otk_draw_all(oxine->otk);
}

static void main_menu_cb(void *data) {
  
  oxine_t *oxine = (oxine_t*) data;
  otk_widget_t *b;

  lock_job_mutex();
  if (oxine->info_window) {
    media_info_close_cb(oxine);
  }
  unlock_job_mutex();

  oxine->pauseplay = NULL;
  oxine->main_window = NULL;

  otk_clear(oxine->otk);

  oxine->main_window = otk_window_new (oxine->otk, NULL, 50, 130, 700, 420);

  /*
  b = otk_button_new (oxine->main_window, 50, 45, 290, 60, "Play Disc", disc_cb, \
oxine);  otk_set_focus(b);
  */
  
  b = otk_button_new (oxine->main_window, 360, 45, 290, 60, "Mediamarks", \
mediamarks_cb, oxine);  otk_set_focus(b);
    
  b = otk_button_new (oxine->main_window, 50, 150, 290, 60, "Analogue TV", tv_cb, \
oxine);  otk_set_focus(b);
  
  /*
  otk_button_new (oxine->main_window, 360, 150, 290, 60, "Playlist", playlist_cb, \
                oxine);
  */
  
  otk_button_new (oxine->main_window, 50, 255, 290, 60, "Digital TV", dvb_cb, oxine);
  /*
  otk_button_new (oxine->main_window, 360, 255, 290, 60, "Control", control_cb, \
                oxine);
  */

  otk_button_new (oxine->main_window, 205, 320, 290, 60, "Shutdown", shutdown_cb, \
oxine);

/*  otk_button_new (oxine->main_window, 50, 180, 290, 60, "File", file_cb, oxine);
  otk_button_new (oxine->main_window, 50, 260, 290, 60, "Streaming", streaming_cb, \
oxine);*/

  otk_draw_all(oxine->otk);
}

/*
 * initialisation
 */
 
oxine_t *create_oxine(void) {
  
  oxine_t *oxine;
  xine_cfg_entry_t centry;
  
  oxine = ho_new(oxine_t);
  oxine->main_menu_cb = main_menu_cb;

  oxine->xine = gGui->xine;
  
  oxine->cd_mountpoint = 
  xine_config_register_string (oxine->xine,
				"gui.osdmenu.dvd_mountpoint",
				"/dvd",
				"directory a media in dvd device will be mounted",
				"directory a media in dvd device will be mounted",
				10,
				NULL,
				NULL);
 
  xine_config_lookup_entry (oxine->xine, "input.dvd_device", &centry);
  oxine->cd_device = centry.str_value;
  
  start_scheduler();
  
  oxine->odk = odk_init();
    
  oxine->otk = otk_init(oxine->odk);
    
  oxine->mode = OXINE_MODE_NORMAL;
   
  return oxine;
}

void destroy_oxine(oxine_t *oxine) {
  
  if (oxine->otk) otk_free(oxine->otk);
  if (oxine->odk) odk_free(oxine->odk);

  ho_free(oxine);
  
  stop_scheduler();

#ifdef DEBUG
  heapstat();
#endif
}

void oxine_init(void)
{
  oxine_instance = create_oxine();
}

void oxine_exit(void)
{
  destroy_oxine(oxine_instance);
  oxine_instance = NULL;
}

void oxine_menu(void)
{
  oxine_t *oxine = oxine_instance;

  if( !oxine )
    return;

  oxine_adapt();
      
  if( oxine->mode != OXINE_MODE_MAINMENU ) {
    if( oxine->reentry )
      oxine->reentry(oxine->reentry_data);
    else
      main_menu_cb(oxine);
    oxine->mode = OXINE_MODE_MAINMENU;
  } else {
    oxine->mode = OXINE_MODE_NORMAL;
    otk_clear(oxine->otk);
  }
}

int oxine_action_event(int xine_event_type)
{
  oxine_t *oxine = oxine_instance;
  oxine_event_t ev;

  if( !oxine )
    return 0;

  if( oxine->mode == OXINE_MODE_NORMAL )
    return 0;

  ev.type = OXINE_EVENT_KEY;
  
  switch( xine_event_type ) {
  case XINE_EVENT_INPUT_UP:
    ev.key = OXINE_KEY_UP;
    otk_send_event(oxine->otk, &ev);
    return 1;
  case XINE_EVENT_INPUT_DOWN:
    ev.key = OXINE_KEY_DOWN;
    otk_send_event(oxine->otk, &ev);
    return 1;
  case XINE_EVENT_INPUT_LEFT:
    ev.key = OXINE_KEY_LEFT;
    otk_send_event(oxine->otk, &ev);
    return 1;
  case XINE_EVENT_INPUT_RIGHT:
    ev.key = OXINE_KEY_RIGHT;
    otk_send_event(oxine->otk, &ev);
    return 1;
  case XINE_EVENT_INPUT_SELECT:
    ev.key = OXINE_KEY_SELECT;
    otk_send_event(oxine->otk, &ev);
    return 1;
  }
  
  return 0;
}

int oxine_mouse_event(int xine_event_type, int x, int y) {
  oxine_t *oxine = oxine_instance;
  oxine_event_t ev;

  if( !oxine )
    return 0;

  if( oxine->mode == OXINE_MODE_NORMAL )
    return 0;

  ev.x = x;
  ev.y = y;
    
  switch( xine_event_type ) {
  case XINE_EVENT_INPUT_MOUSE_MOVE:
    ev.type = OXINE_EVENT_MOTION;
    return otk_send_event(oxine->otk, &ev);
  case XINE_EVENT_INPUT_MOUSE_BUTTON:
    ev.type = OXINE_EVENT_BUTTON;
    ev.key = OXINE_BUTTON1;
    return otk_send_event(oxine->otk, &ev);
  }

  return 0;
}

void oxine_adapt(void)
{
  oxine_t *oxine = oxine_instance;
  oxine_event_t ev;

  if( !oxine )
    return;
  
  ev.type = OXINE_EVENT_FORMAT_CHANGED;
  otk_send_event(oxine->otk, &ev);
}

--- NEW FILE: oxine.h ---
/*
 * Copyright (C) 2002 Stefan Holst
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * $Id: oxine.h,v 1.1 2005/01/31 02:49:48 miguelfreitas Exp $
 *
 * oxine data structures
 */

#ifndef HAVE_OXINE_H
#define HAVE_OXINE_H

#include <stdlib.h>
#include <sys/types.h>
#include <X11/Xlib.h>
#include <pthread.h>
#include <xine.h>

#include "otk.h"
#include "odk.h"

/* I assume that we can have only an osd window  *
 * displayed                                     */

#define OXINE_MODE_PLAY_MENU (1)
#define OXINE_MODE_INFO_MENU (2)
#define OXINE_MODE_NORMAL (3)
#define OXINE_MODE_MAINMENU (4)

typedef struct oxine_s oxine_t;
typedef struct oxine_window_s oxine_window_t;

struct oxine_s {

  xine_t             *xine;

  otk_t              *otk;
  odk_t              *odk;
  otk_widget_t       *main_window;
  otk_widget_t       *pauseplay;


  int                mode;
  int                cd_is_mounted;
  int                cd_in_use;
  /* int                need_draw; */

  const char         *cd_mountpoint;
  const char         *cd_device;

  int                mm_sort_type;

  void (*main_menu_cb) (void *data);

  void (*reentry) (void *data);
  void               *reentry_data;

  char               *pos_str;

  /*
   * stream info blending
   */
  char               *lines[3];
  otk_widget_t       *info_window;
  int                 info_window_job;
  void (*media_info_close_cb)(void *data);

      
};

void oxine_init(void);

void oxine_exit(void);

void oxine_menu(void);

int oxine_action_event(int xine_event_type);

int oxine_mouse_event(int xine_event_type, int x, int y);

void oxine_adapt(void);

#endif

--- NEW FILE: utils.c ---
/*
 * Copyright (C) 2002-2003 Stefan Holst
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * $Id: utils.c,v 1.1 2005/01/31 02:49:48 miguelfreitas Exp $
 *
 * utilities
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <errno.h>
#include <pthread.h>

#include "list.h"
#include "utils.h"

/*
 * multi purpose scheduler
 */

typedef struct {

  time_t start_time;
  xine_list_t *jobs;
  pthread_t scheduler_thread;
  pthread_mutex_t jobs_mutex;
  pthread_mutex_t job_execution_mutex;
  pthread_mutex_t wait_mutex;
  pthread_cond_t jobs_reorganize;
  int job_id;
  int keep_going;
  
} ox_scheduler_t;


/*
 * GLOBAL scheduler instance
 */
 
static ox_scheduler_t *ox_scheduler = NULL;


typedef struct {

  struct timespec ts; /* when to do */
  void (*cb)(void *data);
  void *data;
  int id;

} job_t;

static void *scheduler_thread(void *data) {

  job_t *job;
  int ret;
  void (*cb)(void *data);
  void *cb_data;

#ifdef LOG
  printf("utils: scheduler thread created\n");
#endif
 
  if (!ox_scheduler) {
    pthread_exit(NULL);
    return NULL;
  }
  pthread_mutex_lock(&ox_scheduler->wait_mutex);

  while (ox_scheduler->keep_going) {
    
    pthread_mutex_lock(&ox_scheduler->jobs_mutex);
    job = xine_list_last_content(ox_scheduler->jobs);
    
    if (!job) { /* no jobs for me */
      pthread_mutex_unlock(&ox_scheduler->jobs_mutex);
#ifdef LOG
      printf("utils: no jobs in queue\n");
#endif
      pthread_cond_wait(&ox_scheduler->jobs_reorganize, &ox_scheduler->wait_mutex);
      continue;
    } else pthread_mutex_unlock(&ox_scheduler->jobs_mutex);

#ifdef LOG
    printf("utils: sleeping until next job\n");
#endif
    ret = pthread_cond_timedwait(&ox_scheduler->jobs_reorganize, 
	                         &ox_scheduler->wait_mutex, &job->ts);

    if (ret == ETIMEDOUT) {
      /* lets do a job */
      pthread_mutex_lock(&ox_scheduler->job_execution_mutex);
      pthread_mutex_lock(&ox_scheduler->jobs_mutex);
      job = xine_list_last_content(ox_scheduler->jobs);
      cb = NULL;
      cb_data = NULL;
      if (job) {
#ifdef LOG
        printf("utils: executing job %i\n", job->id);
#endif
        cb = job->cb;
        cb_data = job->data;
      }
      xine_list_delete_current(ox_scheduler->jobs);
      pthread_mutex_unlock(&ox_scheduler->jobs_mutex);

      if (cb) cb(cb_data);

      pthread_mutex_unlock(&ox_scheduler->job_execution_mutex);
      
    } else {
#ifdef LOG
      printf("utils: reorganizing queue\n");
#endif
    }
  }
  pthread_mutex_unlock(&ox_scheduler->wait_mutex);
  pthread_exit(NULL);
}

void start_scheduler() {

  struct timeval tv;
  struct timezone tz;

  ox_scheduler = malloc(sizeof(ox_scheduler_t));
  
  gettimeofday(&tv, &tz);
  ox_scheduler->start_time = tv.tv_sec;

  ox_scheduler->jobs = xine_list_new();
  pthread_mutex_init(&ox_scheduler->wait_mutex, NULL);
  pthread_mutex_init(&ox_scheduler->jobs_mutex, NULL);
  pthread_mutex_init(&ox_scheduler->job_execution_mutex, NULL);
  pthread_cond_init(&ox_scheduler->jobs_reorganize, NULL);
  ox_scheduler->job_id = 0;
  ox_scheduler->keep_going = 1;

  if (pthread_create(&ox_scheduler->scheduler_thread, NULL, scheduler_thread, \
ox_scheduler)) {  printf("utils: error creating scheduler thread\n");
    abort();
  }
}

void stop_scheduler() {

  job_t *job;
  void *ret = NULL;
  
  if (!ox_scheduler) return;
  
  ox_scheduler->keep_going = 0;
  pthread_cond_signal(&ox_scheduler->jobs_reorganize);

  pthread_join(ox_scheduler->scheduler_thread, ret);
  
  job = xine_list_first_content(ox_scheduler->jobs);

  while(job) {
#ifdef LOG
    printf("utils: cancelling pending job %i\n", job->id);
#endif
    free(job);
    job = xine_list_next_content(ox_scheduler->jobs);
  }
  xine_list_free(ox_scheduler->jobs);
  pthread_mutex_destroy(&ox_scheduler->wait_mutex);
  pthread_mutex_destroy(&ox_scheduler->jobs_mutex);
  pthread_cond_destroy(&ox_scheduler->jobs_reorganize);

  free(ox_scheduler);
  ox_scheduler = NULL;
}

int schedule_job(int delay, void (*cb)(void *data), void *data) {
  
  struct timeval tv;
  struct timezone tz;
  job_t *job = malloc(sizeof(job_t));
  int msec;
  int priority;
  
  if (!ox_scheduler) return -1;

  gettimeofday(&tv, &tz);
  
  job->ts.tv_sec = (delay / 1000) + tv.tv_sec;
  msec = delay % 1000;
  if ((msec + tv.tv_usec/1000)>=1000) job->ts.tv_sec++;
  msec = (msec + tv.tv_usec/1000) % 1000;
  job->ts.tv_nsec = msec * 1000000;

  job->cb = cb;
  job->data = data;
  job->id = ++ox_scheduler->job_id;
  
  priority = (job->ts.tv_sec - ox_scheduler->start_time) * 1000 + msec;

  pthread_mutex_lock(&ox_scheduler->jobs_mutex);
  xine_list_append_priority_content(ox_scheduler->jobs, job, priority);
  pthread_mutex_unlock(&ox_scheduler->jobs_mutex);
  
  pthread_cond_signal(&ox_scheduler->jobs_reorganize);

  return ox_scheduler->job_id;
}

void cancel_job(int job_id) {

  job_t *job;
  
  if (!ox_scheduler) return;

  pthread_mutex_lock(&ox_scheduler->jobs_mutex);

  job = xine_list_first_content(ox_scheduler->jobs);

  while(job) {
    if (job->id == job_id) break;
    job = xine_list_next_content(ox_scheduler->jobs);
  }
  if (job) {
    xine_list_delete_current(ox_scheduler->jobs);
#ifdef LOG
    printf("utils: job %i cancelled\n", job->id);
#endif
    free(job);
  }
  pthread_mutex_unlock(&ox_scheduler->jobs_mutex);

  pthread_cond_signal(&ox_scheduler->jobs_reorganize);
}

void lock_job_mutex() {
  if (!ox_scheduler) return;
  pthread_mutex_lock(&ox_scheduler->job_execution_mutex);
}

void unlock_job_mutex() {
  if (!ox_scheduler) return;
  pthread_mutex_unlock(&ox_scheduler->job_execution_mutex);
}

/*
 * heap management
 */

/*
 * Heap objects are aligned on sizeof(int) boundaries
 */

#define ALIGNMENT (sizeof(int))
#define DOALIGN(num) (((num)+ALIGNMENT-1)&~(ALIGNMENT-1))

/*
 * tag datastructures
 */

typedef struct prefix_tag_s  prefix_tag_t;
typedef struct postfix_tag_s postfix_tag_t;

struct prefix_tag_s {
  prefix_tag_t *prev;         /* previous object in heap      */
  prefix_tag_t* next;         /* next object in heap          */
  postfix_tag_t* postfix;     /* ptr to postfix object        */
  char* filename;             /* filename ptr or NULL         */
  long line;                  /* line number or 0             */
  size_t size;                /* size of allocated block      */
  void* content;              /* _gen_malloc() ptr of object  */
  char* tag;                  /* description string or NULL   */
};

struct postfix_tag_s {
  prefix_tag_t* prefix;
};

/*
 * GLOBAL: Points to first object in linked list of heap objects
 */

static prefix_tag_t* heap_head=NULL;


void static AddToLinkedList      ( prefix_tag_t* );
void static RemoveFromLinkedList ( prefix_tag_t* );
void static RenderDesc           ( prefix_tag_t*, char* );


void *_gen_malloc(size_t wSize, const char* tag, char* lpFile, int nLine) {
  
  prefix_tag_t* prefix;
  
  wSize = DOALIGN(wSize);
  prefix=(prefix_tag_t*)malloc(sizeof(prefix_tag_t)+wSize+sizeof(postfix_tag_t));
  if (prefix) {
    AddToLinkedList( prefix );
    prefix->postfix = (postfix_tag_t*)((char*)(prefix+1)+wSize);
    prefix->postfix->prefix = prefix;
    prefix->filename = lpFile;
    prefix->line = nLine;
    prefix->content = prefix+1;
    prefix->size = wSize;
    prefix->tag = NULL;
    if (tag) prefix->tag = strdup(tag);
    memset( prefix->content, 0, wSize );
    }
  else {
    printf("utils: failed to alloc memory\n");
    abort();
  }
  
  return(prefix ? prefix+1 : NULL);
}


void *_gen_free(void* content) {
  
  if (ho_verify(content)) {
    
    prefix_tag_t* prefix=(prefix_tag_t*)content-1;
    size_t        wSize=(char*)(prefix->postfix+1)-(char*)prefix;
    
    RemoveFromLinkedList( prefix );
    memset( prefix, 0, wSize );
    if (prefix->tag) free(prefix->tag);
    free(prefix);
  }

  return (NULL);
}


void *_gen_strdup(const char* lpS, char* lpFile, int nLine) {
  
  void* lpReturn=NULL;

  if (lpS) {
    size_t wSize = (size_t)(strlen(lpS)+1);
    
    lpReturn = _gen_malloc( wSize, "strdup'ed string", lpFile, nLine );
    if (lpReturn) {
      memcpy( lpReturn, lpS, wSize );
    }
  }
  return(lpReturn);

}


void *_gen_realloc(void* lpOld, size_t wSize, char* lpFile, int nLine) {

  void* lpNew=NULL;

  /*--- Try to realloc ---*/
  if (lpOld) {
    if (ho_verify(lpOld)) {
      prefix_tag_t* prefix=(prefix_tag_t*)lpOld-1;
      prefix_tag_t* lpNewPrefix;
      prefix_tag_t* lpPre;

      /*--- Try to reallocate block ---*/
      RemoveFromLinkedList( prefix );
      memset( prefix->postfix, 0, sizeof(postfix_tag_t) );
      wSize = DOALIGN(wSize);
      lpNewPrefix=(prefix_tag_t*)realloc(prefix,
          sizeof(prefix_tag_t)+wSize+sizeof(postfix_tag_t));

      /*--- Add new (or failed old) back in ---*/
      lpPre=(lpNewPrefix?lpNewPrefix:prefix);
      AddToLinkedList( lpPre );
      lpPre->postfix = (postfix_tag_t*)((char*)(lpPre+1)+wSize);
      lpPre->postfix->prefix = lpPre;
      lpPre->content = lpPre+1;
      lpPre->size = wSize;

      /*--- Finish ---*/
      lpNew = (lpNewPrefix ? &lpNewPrefix[1] : NULL);
      if (!lpNew) {
	printf("utils: failed to alloc memory\n");
	abort();
      }
    }
  }

  /*--- Else try new allocation ---*/
  else {
    lpNew = _gen_malloc( wSize, NULL, lpFile, nLine );
    }

  /*--- Return address to object ---*/
  return(lpNew);

}


void heapstat(void) {
 
  unsigned long total = 0;
  unsigned long chunks = 0;
  if (heap_head) {
    prefix_tag_t* lpCur=heap_head;
    
    while (ho_verify(&lpCur[1])) {
      char buffer[100];
      
      RenderDesc( lpCur, buffer );
      /*--- print out buffer ---*/
      printf( "heapstat: %s\n", buffer );
      total+=lpCur->size;
      chunks++;
      lpCur = lpCur->next;
      if (lpCur==heap_head) {
        break;
      }
    }
    if (total) 
      printf("heapstat: memory usage: %li words in %li chunks\n", total, chunks);
  }
}


void static AddToLinkedList(prefix_tag_t* lpAdd) {
    /*--- Add before current head of list ---*/
    if (heap_head) {
        lpAdd->prev = heap_head->prev;
        (lpAdd->prev)->next = lpAdd;
        lpAdd->next = heap_head;
        (lpAdd->next)->prev = lpAdd;
        }

    /*--- Else first node ---*/
    else {
        lpAdd->prev = lpAdd;
        lpAdd->next = lpAdd;
        }

    /*--- Make new item head of list ---*/
    heap_head = lpAdd;
}

void static RemoveFromLinkedList(prefix_tag_t* lpRemove) {

    /*--- Remove from doubly linked list ---*/
    (lpRemove->prev)->next = lpRemove->next;
    (lpRemove->next)->prev = lpRemove->prev;

    /*--- Possibly correct head pointer ---*/
    if (lpRemove==heap_head) {
        heap_head = ((lpRemove->next==lpRemove) ? NULL : 
            lpRemove->next);
        }
}

int static ho_is_ok(void* content)
{
    return ((content) && (!((long)content&(ALIGNMENT-1))));
}


int ho_verify(void *content) {
  int bOk=0;

  if (content) {
    if (ho_is_ok(content)) {
      prefix_tag_t* prefix=(prefix_tag_t*)content-1;
      if(prefix->content==content) {
        if(prefix->postfix->prefix==prefix) {
          bOk = 1;
	}
      }
    }
  }
  return (bOk);
}


void static RenderDesc(prefix_tag_t* prefix, char* lpBuffer) {
  if (prefix->content==&prefix[1]) {
    sprintf( lpBuffer, "%i words @ 0x%08lx:", prefix->size, (unsigned \
long)prefix->content );  if (prefix->filename) {
      sprintf( lpBuffer+strlen(lpBuffer), "%s:%ld ",
	  prefix->filename, prefix->line );
    }
    if (prefix->tag) {
      sprintf( lpBuffer+strlen(lpBuffer), "%s",
	  prefix->tag );
    }
  }
  else {
    strcpy( lpBuffer, "(bad pointer given)" );
  }
}

--- NEW FILE: utils.h ---
/*
 * Copyright (C) 2002-2003 Stefan Holst
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * $Id: utils.h,v 1.1 2005/01/31 02:49:48 miguelfreitas Exp $
 *
 * utilities
 */

#ifndef HAVE_UTILS_H
#define HAVE_UTILS_H

/*
 * multi purpose scheduler
 */

void start_scheduler();
void stop_scheduler();

/*
 * will execute cb(data) after <delay> ms.
 * returns job id.
 */

int schedule_job(int delay, void (*cb)(void *data), void *data);

/*
 * cancel pending job width given job id.
 */

void cancel_job(int job_id);

void lock_job_mutex();
void unlock_job_mutex();

/*
 * heap management
 */

/* semipublic functions*/
void *_gen_malloc   ( size_t, const char * , char *, int );
void *_gen_free     ( void * );
void *_gen_realloc  ( void *, size_t, char *, int );
void *_gen_strdup   ( const char *, char *, int );

/*
 * allocs a new heap object of given type
 */

#define ho_new(type)  (_gen_malloc(sizeof(type),NULL,__FILE__,__LINE__))
#define ho_new_tagged(type,text)  (_gen_malloc(sizeof(type),text,__FILE__,__LINE__))

/*
 * resizes a heap object
 */

#define ho_resize(hObj, size) (hObj=_gen_realloc((hObj), \
(size_t)(size),__FILE__,__LINE__))

/*
 * frees a heap object
 */

#define ho_free(hObj) (hObj=_gen_free(hObj))

/*
 * allocs a new string of given size
 */

#define ho_newstring(wSize) (_gen_malloc((size_t)(wSize),"string",__FILE__,__LINE__))

/*
 * like strdup, but returns a valid heap object
 */

#define ho_strdup(lpSrc) (_gen_strdup(lpSrc,__FILE__,__LINE__))

/*
 * test wether a heap pointer is valid
 */

int ho_verify(void *ho);

/*
 * prints info about all allocated data chunks to stdout
 */

void heapstat();


#if 0
#define _LPV(hObj) *(void*)&hObj
/*--- Array interface macros ---*/
#define NEWARRAY(lpArray, wSize) \
  (_LPV(lpArray)=FmNew((SIZET)(sizeof(*(lpArray))*(wSize)), \
  NULL,szSRCFILE,__LINE__))
#endif

#endif



-------------------------------------------------------
This SF.Net email is sponsored by: IntelliVIEW -- Interactive Reporting
Tool for open source databases. Create drag-&-drop reports. Save time
by over 75%! Publish reports on the web. Export to DOC, XLS, RTF, etc.
Download a FREE copy at http://www.intelliview.com/go/osdn_nl
_______________________________________________
Xine-cvslog mailing list
Xine-cvslog@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/xine-cvslog


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic