Clock Application


This page will tell you enough to go away and build the clock application. It describes the build and running of the clock application, and also gives an annotation of the code to explain how the application works.


Build and Run

Build

Run

The clock is a dive application so if you run it will happily sit there and work away in the background. You won't see anything for two reasons:

Remember that vishnu is just an application as well, just it is more sophisicated and it renders a view of the database.

So to see your new clock in action:


Annotated Code

I've taken some of the superfluous code out in this annotation.

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <sys/time.h>
#include <time.h>
#include <math.h>

#include "dive/dive.h"
#include "dive/lammopt.h"
#include "dive/ai_util.h"
#include "dive/vrx.h"
#include "dive/menu.h"
#include "dive/statistics.h"


#ifndef DEFAULT_STARTING_WORLD
#define DEFAULT_STARTING_WORLD "local"
#endif

#define CLOCK_URL "http://www.sics.se/dive/manual/examples/clock.vr"
static int distr_mode;
static int distr_debug;
static int ttl = 2;
static char *nameserver_address;
static int nameserver_port;
static char *world_name;
static divebool printversion;
static struct {
  int hour;
  int min;
  int sec;
  objid_t clock_id;
  objid_t sec_id;
  objid_t min_id;
  objid_t hour_id;
} ClockData = {
  0,
  0,
  0,
};
#define update_hour(id, hours) \
  distr_abs_fixedXYZ(id, 0.0, 0.0, ((hours)%12)*(M_PI/6), LOCAL_C, NULL, NULL)

#define update_hand(id, seconds) \
  distr_abs_fixedXYZ(id, 0.0, 0.0, seconds*(M_PI/30), LOCAL_C, NULL, NULL)
static void
command_line(int argc, char *argv[])
{
  LambOption opts[] = {
  {"debug", REQ_ARG, &dive_debug, setint, "0", ""},
  {"distr_debug", REQ_ARG, &distr_debug, setint, "0", ""},
  {"singleuser" ,NO_ARG, &distr_mode, setbool, True_val, False_val},
  {"version" , NO_ARG, &printversion, setbool, False_val, True_val},
  {"nameserver_addr", REQ_ARG, &nameserver_address, setstr, DIVESERVER_ADDRESS, ""},
  {"nameserver_port", REQ_ARG, &nameserver_port, setint, DIVESERVER_STRPORT, ""},
  {"ttl", REQ_ARG, &ttl, setint, "2", ""},
  {"world", REQ_ARG, &world_name, setstr, DEFAULT_STARTING_WORLD, ""}
  };

  if( !lammopt(argc, argv, opts, sizeof(opts) / sizeof(*opts), TRUE,
  NULL, NULL, NULL) )
  exit(0);
}

static void
on_timer(void *arg)
{
  if (!find_entity(&ClockData.clock_id))
  dive_exit(0);
  ClockData.sec ++;
  if (ClockData.sec == 60) {
  ClockData.sec = 0;
  ClockData.min ++;
  if (ClockData.min == 60) {
  ClockData.min = 0;
  ClockData.hour ++;
  if (ClockData.hour == 24) {
  ClockData.hour = 0;
  }
  update_hour(&ClockData.hour_id, ClockData.hour);
  }
  update_hand(&ClockData.min_id, ClockData.min);
  }
  update_hand(&ClockData.sec_id, ClockData.sec);
}

static void
set_current_time()
{
  time_t t;
  struct tm *tm;

  /* Get current system time and update our own structure */
  t = time(NULL);
  tm = localtime(&t);
  ClockData.hour = tm->tm_hour;
  ClockData.min = tm->tm_min;
  ClockData.sec = tm->tm_sec;

  /* Update the position of the clock hands */
  update_hour(&ClockData.hour_id, ClockData.hour);
  update_hand(&ClockData.min_id, ClockData.min);
  update_hand(&ClockData.sec_id, ClockData.sec);
}
static void interaction_cb(objid_t *dest_id, interaction_type_t type, objid_t *pid,
  objid_t *src_id, point_t *pt, objid_t *viewid,void *arg)
{
  /* No source or no destination! */
  if (!src_id || !dest_id)
  return ;

  /* If the destination identifier is not the board of the clock */
  if (!objid_eq(dest_id, &ClockData.clock_id))
  return ;

  switch (type) {
  case DIVE_IA_SELECT:
  /* Setup clock to current time when button is pressed */
  set_current_time();
  break;
  default:
  break;
  }
}
static void clock_exit(void *arg)
{
  distr_entity_remove(&ClockData.clock_id, NULL);
}
static void clock_init(objid_t *wid)
{
  entity *obj;
  if (distr_readURL(wid, CLOCK_URL, NULL, &ClockData.clock_id) < 0)
  dive_error("clock_init: Could not read clock data from %s.", CLOCK_URL);
  if (obj = find_sub_byname(&ClockData.clock_id, "Hour Hand"))
  ClockData.hour_id = obj->id;
  else
  dive_error("clock_init: Hour hand not found in %s.", CLOCK_URL);
  if (obj = find_sub_byname(&ClockData.clock_id, "Minute Hand"))
  ClockData.min_id = obj->id;
  else
  dive_error("clock_init: Minute hand not found in %s.", CLOCK_URL);
  if (obj = find_sub_byname(&ClockData.clock_id, "Second Hand"))
  ClockData.sec_id = obj->id;
  else
  dive_error("clock_init: Second hand not found in %s.", CLOCK_URL);
  set_current_time();
  callback_register_filter(INTERACTION_SIGNAL, interaction_cb, 0,
  &ClockData.clock_id, NULL, 0x0, NULL);
  dive_add_exit_fn(clock_exit, NULL);

}
void main(int argc, char *argv[])
{
  objid_t wid;
  command_line(argc, argv);
  /* Printout the DIVE version number */
  if (printversion){
  fprintf(stdout,"%s\n", DIVE_VERSION);
  exit(0);
  }
  /* Set multicast TTL level: Must be set before nameserver lookup */
  dive_distr_ttl(ttl);
  /* Initialize DIVE */
  dive_init(NULL);
  dive_distr_debug(distr_debug);
  if (distr_mode){
  if (ds_init(nameserver_address, nameserver_port) < 0)
  set_distr_mode(SOFT_SINGLEUSER);
  }
  objid_zero(&wid);
  distr_world_connect(world_name, &wid);
  clock_init(&wid);
  alarm_init(1000); alarm_add(on_timer, NULL);
  threads_main_loop();
}


Anthony Steed, A.Steed@cs.ucl.ac.uk