Logo Search packages:      
Sourcecode: xdaliclock version File versions  Download package

daliclock.c

/* Dali Clock - a melting digital clock for PalmOS.
 * Copyright (c) 1991-2002 Jamie Zawinski <jwz@jwz.org>
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or
 * implied warranty.
 */


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#ifdef HACK_FRAMEBUFFER
  /* needed to access WinHandle->displayAddrV20 for ROMs earlier than 3.5 */
# define ALLOW_ACCESS_TO_INTERNALS_OF_WINDOWS
#endif

#include "daliclock.h"


/* Make sure Err*Display* are not no-ops. */
#define ERROR_CHECK_LEVEL ERROR_CHECK_FULL

#include <PalmOS.h>
/*#include <Hardware.h>*/
#include <FeatureMgr.h>       /* for FtrGet() */

#ifndef HAVE_APPLAUNCHWITHCOMMAND
# define sysErrRomIncompatible -1
#endif


#define ROM_20 0x02003000
#define ROM_30 0x03003000
#define ROM_35 0x03503000


#define SECONDS_ONLY_MODE

#undef countof
#define countof(x) (sizeof((x))/sizeof(*(x)))

typedef unsigned char POS;

#ifndef MAX_SEGS_PER_LINE
# define MAX_SEGS_PER_LINE 2
#endif

struct raw_number {
  const unsigned char *bits;
  POS width, height;
};

struct scanline {
  POS left[MAX_SEGS_PER_LINE], right[MAX_SEGS_PER_LINE];
};

typedef MemHandle FrameHandle;
struct frame {
  struct scanline scanlines [1]; /* scanlines are contiguous here */
};


/* The persistent preferences that aren't covered by system preferences.
 */
struct daliclock_prefs {
  Boolean inverted_p;
  Boolean seconds_only_p;
  int ticks;
};

/* The runtime settings (some initialized from system prefs, but changable.)
 */
struct state {
  Boolean inverted_p;
  Boolean twelve_hour_time_p;
  enum date_state { DTime, DDateIn, DDate, DDateOut, DDateOut2, DDash }
    display_date;
  char test_hack;
  int ticks;
  long interval;
  Boolean redraw_p;
  Boolean seconds_only_p;

  enum { MMDDYY, DDMMYY, YYMMDD, YYDDMM, MMYYDD, DDYYMM } date_format;

  int tick;

  int char_width, char_height, colon_width;

  FrameHandle base_frames [12];
  FrameHandle current_frames [6];
  FrameHandle target_frames [6];
  FrameHandle clear_frame;

  signed char current_digits [6];
  signed char target_digits [6];

  Int16 win_width, win_height;
};


# include "zero0.xbm"
# include "one0.xbm"
# include "two0.xbm"
# include "three0.xbm"
# include "four0.xbm"
# include "five0.xbm"
# include "six0.xbm"
# include "seven0.xbm"
# include "eight0.xbm"
# include "nine0.xbm"
# include "colon0.xbm"
# include "slash0.xbm"

static struct raw_number numbers_0 [] = {
  { zero0_bits, zero0_width, zero0_height },
  { one0_bits, one0_width, one0_height },
  { two0_bits, two0_width, two0_height },
  { three0_bits, three0_width, three0_height },
  { four0_bits, four0_width, four0_height },
  { five0_bits, five0_width, five0_height },
  { six0_bits, six0_width, six0_height },
  { seven0_bits, seven0_width, seven0_height },
  { eight0_bits, eight0_width, eight0_height },
  { nine0_bits, nine0_width, nine0_height },
  { colon0_bits, colon0_width, colon0_height },
  { slash0_bits, slash0_width, slash0_height },
  { 0, 0, 0 }
};


#ifdef SECONDS_ONLY_MODE
# include "zero2.xbm"
# include "one2.xbm"
# include "two2.xbm"
# include "three2.xbm"
# include "four2.xbm"
# include "five2.xbm"
# include "six2.xbm"
# include "seven2.xbm"
# include "eight2.xbm"
# include "nine2.xbm"
# include "colon2.xbm"
# include "slash2.xbm"

static struct raw_number numbers_2 [] = {
  { zero2_bits, zero2_width, zero2_height },
  { one2_bits, one2_width, one2_height },
  { two2_bits, two2_width, two2_height },
  { three2_bits, three2_width, three2_height },
  { four2_bits, four2_width, four2_height },
  { five2_bits, five2_width, five2_height },
  { six2_bits, six2_width, six2_height },
  { seven2_bits, seven2_width, seven2_height },
  { eight2_bits, eight2_width, eight2_height },
  { nine2_bits, nine2_width, nine2_height },
  { colon2_bits, colon2_width, colon2_height },
  { slash2_bits, slash2_width, slash2_height },
  { 0, 0, 0 }
};
#endif /* SECONDS_ONLY_MODE */


static FrameHandle
make_blank_frame (int width, int height)
{
  int size = sizeof (struct frame) + (sizeof (struct scanline) * (height - 1));
  FrameHandle fh = MemHandleNew (size);
  struct frame *frame;
  int x, y;

  if (!fh)
    {
      ErrDisplay ("Out of memory.");
      return 0;
    }

  frame = (struct frame *) MemHandleLock (fh);
  MemSet (frame, size, 0);
  for (y = 0; y < height; y++)
    for (x = 0; x < MAX_SEGS_PER_LINE; x++)
      frame->scanlines[y].left [x] = frame->scanlines[y].right [x] = width / 2;
  MemHandleUnlock (fh);
  return fh;
}


static FrameHandle
number_to_frame (const unsigned char *bits, int width, int height)
{
  int x, y;
  FrameHandle fh;
  struct frame *frame;
  POS *left, *right;

  fh = make_blank_frame (width, height);
  if (!fh) return 0;
  frame = (struct frame *) MemHandleLock (fh);

  for (y = 0; y < height; y++)
    {
      int seg, end;
      x = 0;
# define GETBIT(bits,x,y) \
         (!! ((bits) [((y) * ((width+7) >> 3)) + ((x) >> 3)] \
              & (1 << ((x) & 7))))

      left = frame->scanlines[y].left;
      right = frame->scanlines[y].right;

      for (seg = 0; seg < MAX_SEGS_PER_LINE; seg++)
        left [seg] = right [seg] = width / 2;

      for (seg = 0; seg < MAX_SEGS_PER_LINE; seg++)
        {
          for (; x < width; x++)
            if (GETBIT (bits, x, y)) break;
          if (x == width) break;
          left [seg] = x;
          for (; x < width; x++)
            if (! GETBIT (bits, x, y)) break;
          right [seg] = x;
        }

      for (; x < width; x++)
        if (GETBIT (bits, x, y))
          {
            /* This means the font is too curvy.  Increase MAX_SEGS_PER_LINE
               and recompile. */
            ErrDisplay ("Internal error: builtin font is bogus.");
          }

      /* If there were any segments on this line, then replicate the last
         one out to the end of the line.  If it's blank, leave it alone,
         meaning it will be a 0-pixel-wide line down the middle.
       */
      end = seg;
      if (end > 0)
        for (; seg < MAX_SEGS_PER_LINE; seg++)
          {
            left [seg] = left [end-1];
            right [seg] = right [end-1];
          }

# undef GETBIT
    }

  MemHandleUnlock (fh);
  return fh;
}


static void
copy_frame (struct state *state, FrameHandle from_h, FrameHandle to_h)
{
  struct frame *from = (struct frame *) MemHandleLock (from_h);
  struct frame *to   = (struct frame *) MemHandleLock (to_h);
  int y;

  for (y = 0; y < state->char_height; y++)
    to->scanlines[y] = from->scanlines[y];  /* copies the whole struct */

  MemHandleUnlock (to_h);
  MemHandleUnlock (from_h);
}


static Boolean
init_numbers (struct state *state)
{
  int i;
  struct raw_number *raw = numbers_0;

# ifdef SECONDS_ONLY_MODE
  if (state->seconds_only_p)
    raw = numbers_2;
# endif /* SECONDS_ONLY_MODE */

  state->char_width  = raw[0].width;
  state->char_height = raw[0].height;
  state->colon_width = raw[10].width;

  for (i = 0; i < countof(state->base_frames); i++)
    state->base_frames [i] =
      number_to_frame (raw[i].bits, raw[i].width, raw[i].height);

  for (i = 0; i < countof(state->current_frames); i++)
    {
      FrameHandle fh = make_blank_frame (state->char_width,
                                         state->char_height);
      if (!fh) return false;
      state->current_frames [i] = fh;
    }

  for (i = 0; i < countof(state->target_frames); i++)
    {
      FrameHandle fh = make_blank_frame (state->char_width,
                                         state->char_height);
      if (!fh) return false;
      state->target_frames [i] = fh;
    }

  state->clear_frame = make_blank_frame (state->char_width,
                                         state->char_height);
  if (!state->clear_frame) return false;

  for (i = 0; i < countof(state->target_digits); i++)
    state->target_digits[i] = state->current_digits[i] = -1;

  return true;
}


static void
free_numbers (struct state *state)
{
  int i;
# define FREEIF(x) do { if ((x)) { MemHandleFree((x)); (x) = 0; } } while (0)
# define FREELOOP(x) do { \
    for (i = 0; i < countof ((x)); i++) FREEIF ((x)[i]); } while (0)

  FREELOOP (state->base_frames);
  FREELOOP (state->current_frames);
  FREELOOP (state->target_frames);
  FREEIF (state->clear_frame);

# undef FREELOOP
# undef FREEIF
}



#ifndef HAVE_SYSTICKSPERSECOND
 /* Documentation says 100 on real device, 60 on mac emulator.
    What about on other emulators?  Seems to be ~100 on Linux.
  */
# define SysTicksPerSecond() 100
#endif /* !HAVE_SYSTICKSPERSECOND */





static void
fill_target_digits (struct state *state)
{
  UInt32 seconds = TimGetSeconds ();  /* since 1/1/1904 */
  DateTimeType date;

  TimSecondsToDateTime (seconds, &date);

  if (state->test_hack)
    {
      state->target_digits [0] =
        state->target_digits [1] =
        state->target_digits [2] =
        state->target_digits [3] =
        state->target_digits [4] =
        state->target_digits [5] = (state->test_hack == '-' ? -1
                                    : state->test_hack - '0');
      state->test_hack = 0;
    }
  else if (state->display_date == DTime ||
           state->display_date == DDash)
    {
      Boolean twelve_hour_time_p = state->twelve_hour_time_p;

      if (state->display_date == DDash)
        state->display_date = DTime;

      if (twelve_hour_time_p && date.hour > 12) date.hour -= 12;
      if (twelve_hour_time_p && date.hour == 0) date.hour = 12;
      state->target_digits [0] = (date.hour - (date.hour % 10)) / 10;
      state->target_digits [1] = date.hour % 10;
      state->target_digits [2] = (date.minute - (date.minute % 10)) / 10;
      state->target_digits [3] = date.minute % 10;
      state->target_digits [4] = (date.second - (date.second % 10)) / 10;
      state->target_digits [5] = date.second % 10;

      if (twelve_hour_time_p && state->target_digits [0] == 0)
        state->target_digits [0] = -1;
    }
  else
    {
      int m0,m1,d0,d1,y0,y1;
      m0 = (date.month - (date.month % 10)) / 10;
      m1 = date.month % 10;
      d0 = (date.day - (date.day % 10)) / 10;
      d1 = date.day % 10;
      y0 = date.year % 100;
      y0 = (y0 - (y0 % 10)) / 10;
      y1 = date.year % 10;

      if (state->display_date == DDateIn)
        state->display_date = DDate;
      if (state->display_date == DDateOut)
        state->display_date = DDateOut2;
      else if (state->display_date == DDateOut2)
        state->display_date = DDash;

      switch (state->date_format)
        {
        case MMDDYY:
          state->target_digits [0] = m0; state->target_digits [1] = m1;
          state->target_digits [2] = d0; state->target_digits [3] = d1;
          state->target_digits [4] = y0; state->target_digits [5] = y1;
          break;
        case DDMMYY:
          state->target_digits [0] = d0; state->target_digits [1] = d1;
          state->target_digits [2] = m0; state->target_digits [3] = m1;
          state->target_digits [4] = y0; state->target_digits [5] = y1;
          break;
        case YYMMDD:
          state->target_digits [0] = y0; state->target_digits [1] = y1;
          state->target_digits [2] = m0; state->target_digits [3] = m1;
          state->target_digits [4] = d0; state->target_digits [5] = d1;
          break;
        case YYDDMM:
          state->target_digits [0] = y0; state->target_digits [1] = y1;
          state->target_digits [2] = d0; state->target_digits [3] = d1;
          state->target_digits [4] = m0; state->target_digits [5] = m1;
          break;
          /* These are silly, but are included for completeness... */
        case MMYYDD:
          state->target_digits [0] = m0; state->target_digits [1] = m1;
          state->target_digits [2] = y0; state->target_digits [3] = y1;
          state->target_digits [4] = d0; state->target_digits [5] = d1;
          break;
        case DDYYMM:
          state->target_digits [0] = d0; state->target_digits [1] = d1;
          state->target_digits [2] = y0; state->target_digits [3] = y1;
          state->target_digits [4] = m0; state->target_digits [5] = m1;
          break;
        }
    }
}


#ifdef HACK_FRAMEBUFFER
static unsigned char *frame_buffer = 0;
static unsigned int frame_buffer_bytes_per_line = 0;

/* Call this after each trip through the event loop (at least.) */
static void
cache_frame_buffer (void)
{
  static int initted_p = 0;

  if (initted_p) return;
  initted_p = 1;

  {
    UInt32 rom_version = 0;
    FtrGet (sysFtrCreator, sysFtrNumROMVersion, &rom_version);

    frame_buffer_bytes_per_line = 0;
    frame_buffer = 0;

    if (rom_version < ROM_30)  /* ROM earlier than 3.0 -- assume. */
      {
#       ifndef hwrDisplayWidth
#         define hwrDisplayWidth 160
#       endif
        frame_buffer_bytes_per_line = hwrDisplayWidth/8;
      }
    else
      {
        UInt32 w = 0, h = 0, d = 0;
        Boolean color = false;
        WinScreenMode (winScreenModeGet, &w, &h, &d, &color);
        if (w > 0 && h > 0 && d == 1 && color == false)
          frame_buffer_bytes_per_line = (w+7) / 8;
      }

    if (frame_buffer_bytes_per_line > 0)
      {
        WinHandle w = WinGetDisplayWindow();

        if (!w)
          ;
        else if (rom_version < ROM_35)
          frame_buffer = (unsigned char *) w->displayAddrV20;
        else
          { /* functions from "3.5 New Feature Set" */
            BitmapPtr b = WinGetBitmap (w);
            frame_buffer = (unsigned char *) (b ? BmpGetBits (b) : 0);
          }
      }
  }
}
#endif /* HACK_FRAMEBUFFER */


static void
draw_horizontal_line (Int16 x1, Int16 x2, Int16 y, Boolean black_p)
{
  if (x1 == x2)
    return;
  else if (x1 > x2)
    {
      Int16 swap = x1;
      x1 = x2;
      x2 = swap;
    }

#ifdef HACK_FRAMEBUFFER
  if (frame_buffer)
    {
      unsigned char *scanline =
        frame_buffer + (y * frame_buffer_bytes_per_line);

      if (black_p)
        for (; x1 < x2; x1++)
          scanline[x1>>3] |= 1 << (7 - (x1 & 7));
      else
        for (; x1 < x2; x1++)
          scanline[x1>>3] &= ~(1 << (7 - (x1 & 7)));
    }
  else
#endif /* HACK_FRAMEBUFFER */

  if (black_p)
    WinDrawLine (x1, y, x2, y);
  else
    WinEraseLine (x1, y, x2, y);
}



static void
draw_frame (struct state *state, FrameHandle fh, int x, int y)
{
  struct frame *frame = (struct frame *) MemHandleLock (fh);
  int px, py;

  for (py = 0; py < state->char_height; py++)
    {
      struct scanline *line = &frame->scanlines [py];
      int last_right = 0;

      for (px = 0; px < MAX_SEGS_PER_LINE; px++)
        {
          if (px > 0 &&
              (line->left[px] == line->right[px] ||
               (line->left [px] == line->left [px-1] &&
                line->right[px] == line->right[px-1])))
            continue;

          /* Erase the line between the last segment and this segment.
           */
          draw_horizontal_line (x + last_right,
                                x + line->left [px],
                                y + py,
                                state->inverted_p);

          /* Draw the line of this segment.
           */
          draw_horizontal_line (x + line->left [px],
                                x + line->right[px],
                                y + py,
                                !state->inverted_p);

          last_right = line->right[px];
        }

      /* Erase the line between the last segment and the right edge.
       */
      draw_horizontal_line (x + last_right,
                            x + state->char_width,
                            y + py,
                            state->inverted_p);
    }

  MemHandleUnlock (fh);
}



static void
draw_background (struct state *state)
{
  RectangleType r;

  r.topLeft.x = 0;
  r.topLeft.y = 0;
  WinGetWindowExtent (&r.extent.x, &r.extent.y);

# ifdef HACK_FRAMEBUFFER
  if (frame_buffer)
    {
      unsigned long pixel = (state->inverted_p ? ~0 : 0);
      int n = frame_buffer_bytes_per_line * r.extent.y;
      if (n % sizeof(unsigned long))
        {
          unsigned char *bc = frame_buffer;
          while (--n >= 0) *bc++ = pixel;
        }
      else
        {
          unsigned long *bi = (unsigned long *) frame_buffer;
          while ((n -= sizeof(unsigned long)) >= 0) *bi++ = pixel;
        }
      }
  else
# endif /* HACK_FRAMEBUFFER */
  if (state->inverted_p)
    WinDrawRectangle (&r, 0);
  else
    WinEraseRectangle (&r, 0);
}


static void draw_clock (struct state *state, Boolean colonic_p);

static void
start_sequence (struct state *state)
{
  int i;

  for (i = 0; i < countof (state->current_frames); i++)
    {
      /* Copy the (old) target_frames into the (new) current_frames,
         since that's what's on the screen now.
       */
      copy_frame (state, state->target_frames[i], state->current_frames[i]);
    }

  draw_clock (state, true);
  fill_target_digits (state);

  for (i = 0; i < countof (state->current_frames); i++)
    {
      /* Now fill the (new) target_frames with the digits of the
         current time, since that's where we're going.
       */
      int j = state->target_digits[i];
      copy_frame (state,
                  (j < 0 ? state->clear_frame : state->base_frames [j]),
                  state->target_frames[i]);
    }
}


static void
one_step (struct state *state,
          FrameHandle current_h, FrameHandle target_h,
          int tick)
{
  struct frame *current_frame = (struct frame *) MemHandleLock (current_h);
  struct frame *target_frame  = (struct frame *) MemHandleLock (target_h);

  struct scanline *line  = &current_frame->scanlines [0];
  struct scanline *target = &target_frame->scanlines [0];
  int i = 0, x;
  for (i = 0; i < state->char_height; i++)
    {
# define STEP(field) \
         (line->field += ((int) (target->field - line->field)) / tick)

      for (x = 0; x < MAX_SEGS_PER_LINE; x++)
        {
          STEP (left [x]);
          STEP (right[x]);
        }
      line++;
      target++;
# undef STEP
    }
  MemHandleUnlock (target_h);
  MemHandleUnlock (current_h);
}


static void
tick_sequence (struct state *state)
{
  int i;

  /* Advance one step in the current animation sequence. */
  for (i = 0; i < countof (state->current_frames); i++)
    if (state->target_digits[i] != state->current_digits[i])
      one_step (state,
                state->current_frames[i],
                state->target_frames[i],
                state->tick);
  state->tick--;

  if (state->tick <= 0)
    {
      /* End of the animation sequence; fill target_frames with the
         digits of the current time, and restart the counter. */
      start_sequence (state);
      state->tick = state->ticks;
    }
}


static void
draw_clock (struct state *state, Boolean colonic_p)
{
  int x, y, i;
  int width = state->win_width;
  int height = state->win_height;

# ifdef HACK_FRAMEBUFFER
  cache_frame_buffer ();
# endif /* HACK_FRAMEBUFFER */

  x = (width - ((state->char_width * 6) + (state->colon_width * 2))) / 2;
  y = (height - state->char_height) / 2;

  if (state->seconds_only_p)
    {
      x = (width - (state->char_width * 2)) / 2;
      x += 5; /* #### kludge */
    }

# define DIGIT(n) \
  if (state->target_digits[n] != state->current_digits[n]) \
    draw_frame (state, state->current_frames [n], x, y); \
  i++; \
  x += state->char_width

# define COLON() \
  if (colonic_p) \
    draw_frame (state, state->base_frames \
                         [state->display_date == DTime ? 10 : 11], \
                x, y); \
  x += state->colon_width

  i = 0;

  if (state->seconds_only_p)
    {
      DIGIT(4);
      DIGIT(5);
    }
  else
    {
      DIGIT(0);
      DIGIT(1);
      COLON();
      DIGIT(2);
      DIGIT(3);
      COLON();
      DIGIT(4);
      DIGIT(5);
    }
# undef COLON
# undef DIGIT
}


static Boolean
do_settings_form (struct state *state, FormPtr form)
{
  FieldPtr field;
  ControlPtr dark, light, c12, c24, secs, mdy, dmy, ymd;

  MemHandle otext, ntext;
  char buf[5];
  char *s;

  /* Write into the text field
   */

  field = FrmGetObjectPtr (form, FrmGetObjectIndex (form, SettingsFieldFPS));
  if (!field)
    {
      ErrDisplay ("FPS field missing");
      return false;
    }
 
  if (state->ticks >= 10)
    {
      buf[0] = ((state->ticks / 10) % 10) + '0';
      buf[1] = (state->ticks % 10) + '0' ;
      buf[2] = 0;
    }
  else
    {
      buf[0] = (state->ticks % 10) + '0';
      buf[1] = 0;
    }

  ntext = (MemHandle) MemHandleNew (StrLen(buf)+1);
  s = (char *) MemHandleLock ((MemHandle) ntext);
  StrCopy (s, buf);
  MemHandleUnlock ((MemHandle) ntext);

  otext = FldGetTextHandle (field);
  FldSetTextHandle (field, ntext);
  /* FldDrawField (field); */

  if (otext) 
    MemHandleFree ((MemHandle) otext);

  /* Write into the checkboxes.
   */

  dark  = FrmGetObjectPtr (form, FrmGetObjectIndex(form,SettingsButtonDark));
  light = FrmGetObjectPtr (form, FrmGetObjectIndex(form,SettingsButtonLight));
  c12   = FrmGetObjectPtr (form, FrmGetObjectIndex(form,SettingsButton12));
  c24   = FrmGetObjectPtr (form, FrmGetObjectIndex(form,SettingsButton24));
# ifdef SECONDS_ONLY_MODE
  secs  = FrmGetObjectPtr (form, FrmGetObjectIndex(form,SettingsSecondsOnly));
# endif /* SECONDS_ONLY_MODE */
  mdy   = FrmGetObjectPtr (form, FrmGetObjectIndex(form,SettingsMMDDYY));
  dmy   = FrmGetObjectPtr (form, FrmGetObjectIndex(form,SettingsDDMMYY));
  ymd   = FrmGetObjectPtr (form, FrmGetObjectIndex(form,SettingsYYMMDD));

  CtlSetValue (dark,   state->inverted_p);
  CtlSetValue (light, !state->inverted_p);
  CtlSetValue (secs,   state->seconds_only_p);
  CtlSetValue (c12,    state->twelve_hour_time_p && !state->seconds_only_p);
  CtlSetValue (c24,   !state->twelve_hour_time_p && !state->seconds_only_p);
  CtlSetValue (mdy,    state->date_format == MMDDYY);
  CtlSetValue (dmy,    state->date_format == DDMMYY);
  CtlSetValue (ymd,    state->date_format == YYMMDD);


  /* Pop up the dialog box and process events waiting for "OK".
   */
  FrmDoDialog (form);


  /* Read from the checkboxes.
   */

  state->inverted_p = CtlGetValue (dark);

# ifdef SECONDS_ONLY_MODE
  {
    Boolean old = state->seconds_only_p;
    state->seconds_only_p = CtlGetValue (secs);
    if (state->seconds_only_p != old)
      {
        free_numbers (state);
        if (!init_numbers (state))
          {
            ErrDisplay ("Out of memory.");
            state->seconds_only_p = false;
            init_numbers (state);
          }
      }
  }
# endif /* SECONDS_ONLY_MODE */

  if (!state->seconds_only_p)
    state->twelve_hour_time_p = CtlGetValue (c12);

  state->date_format = (CtlGetValue (dmy) ? DDMMYY :
                        CtlGetValue (ymd) ? YYMMDD :
                        MMDDYY);

  /* Parse the frames-per-second text.
   */
  s = (char *) MemHandleLock ((MemHandle) ntext);
  state->ticks = 0;
  while (*s)
    {
      state->ticks = (state->ticks * 10) + (*s - '0');
      if (state->ticks >= 100) break;
      s++;
    }
  MemHandleUnlock ((MemHandle) ntext);

  FrmDeleteForm (form);

  state->redraw_p = true;
  if (state->ticks <= 0) state->ticks = DEFAULT_FRAME_RATE;
  state->interval = SysTicksPerSecond() / state->ticks;
  state->tick = state->ticks;

  {
    struct daliclock_prefs prefs;
    prefs.inverted_p = state->inverted_p;
    prefs.seconds_only_p = state->seconds_only_p;
    prefs.ticks = state->ticks;

#ifdef HAVE_PREFSETAPPPREFERENCES_20
    PrefSetAppPreferences (CREATOR_ID, 0, 1, &prefs, sizeof(prefs), true);
#else  /* !HAVE_PREFSETAPPPREFERENCES_20 */
    PrefSetAppPreferences (CREATOR_ID, 1, &prefs, sizeof(prefs));
#endif /* !HAVE_PREFSETAPPPREFERENCES_20 */
  }

  return true;
}


/* Blocks until the exact moment that the wall-clock's seconds advance,
   so that we are animating in lock-step with the reported time.
 */
static void
sync_to_seconds (void)
{
  UInt32 sec1, sec2;
  sec1 = sec2 = TimGetSeconds ();
  while (sec1 == sec2)
    sec2 = TimGetSeconds ();
}


static Boolean
app_handle_event (struct state *state, EventPtr event)
{
  FormPtr form;

  switch (event->eType)
    {
    case menuEvent:
      switch (event->data.menu.itemID)
        {
        case MainMenuAbout:
          form = FrmInitForm (AboutForm);
          FrmDoDialog (form);
          FrmDeleteForm (form);
          state->redraw_p = true;
          return true;

        case MainMenuSettings:
          form = FrmInitForm (SettingsForm);
          return do_settings_form (state, form);

        default:
          return false;
        }

    case keyDownEvent:
      if (event->data.keyDown.chr == ' ')
        {
          state->twelve_hour_time_p = !state->twelve_hour_time_p;
          return true;
        }
      else if (event->data.keyDown.chr == '-' ||
               (event->data.keyDown.chr >= '0' &&
                event->data.keyDown.chr <= '9'))
        {
          state->test_hack = event->data.keyDown.chr;
          return true;
        }
      else
        return false;
      break;

    case penDownEvent:
      if (!state->seconds_only_p)
        state->display_date = DDateIn;
      return true;
      break;

    case penUpEvent:
      if (state->display_date == DDate) /* turn off faster if already up */
        state->display_date = DDash;
      else if (state->display_date != DTime)
        state->display_date = DDateOut;

      return true;
      break;

    case nilEvent:
      {
        Boolean redraw_p = state->redraw_p;

        tick_sequence (state);

        if (redraw_p)
          {
            int i;
            state->redraw_p = false;
            for (i = 0; i < countof(state->target_digits); i++)
              state->target_digits[i] = state->current_digits[i] = -2;
            draw_background (state);
          }

        draw_clock (state, false);

        if (redraw_p)
          sync_to_seconds ();

        return true;
      }

    default:
      return false;
    }
}

static Boolean
initialize (struct state *state)
{
  SystemPreferencesType sysPrefs;
  struct daliclock_prefs prefs;
  UInt16 prefs_size;
  Boolean got_prefs;

  MemSet (state, sizeof(*state), 0);

  PrefGetPreferences (&sysPrefs);

  state->twelve_hour_time_p = !Use24HourFormat (sysPrefs.timeFormat);

  switch (sysPrefs.dateFormat)
    {
    case dfMDYWithSlashes:          /* 12/31/95     */
    case dfMDYLongWithComma:        /* Dec 31, 1995 */
      state->date_format = MMDDYY;
      break;
    case dfDMYWithSlashes:          /* 31/12/95     */
    case dfDMYWithDots:             /* 31.12.95     */
    case dfDMYWithDashes:           /* 31-12-95     */
    case dfDMYLong:                 /* 31 Dec 1995  */
    case dfDMYLongWithDot:          /* 31. Dec 1995 */
    case dfDMYLongNoDay:            /* Dec 1995     */
    case dfDMYLongWithComma:        /* 31 Dec, 1995 */
    case dfMYMed:             /* Dec '95      */
      state->date_format = DDMMYY;
      break;
    case dfYMDWithSlashes:          /* 95/12/31     */
    case dfYMDWithDots:             /* 95.12.31     */
    case dfYMDWithDashes:           /* 95-12-31     */
    case dfYMDLongWithDot:          /* 1995.12.31   */
    case dfYMDLongWithSpace:        /* 1995 Dec 31  */
      state->date_format = YYMMDD;
      break;
    default:
      ErrNonFatalDisplayIf (true, "unregognised date format");
      state->date_format = MMDDYY;
      break;
    }


  prefs_size = sizeof(prefs);

#ifdef HAVE_PREFSETAPPPREFERENCES_20
  got_prefs = (PrefGetAppPreferences (CREATOR_ID, 0, &prefs, &prefs_size,
                                      true) != noPreferenceFound &&
               prefs_size >= sizeof(prefs));
#else  /* !HAVE_PREFSETAPPPREFERENCES_20 */
  got_prefs = (PrefGetAppPreferences (CREATOR_ID, 1, &prefs, prefs_size));
#endif /* !HAVE_PREFSETAPPPREFERENCES_20 */


  state->inverted_p = true;
  state->ticks = DEFAULT_FRAME_RATE;

  if (got_prefs)
    {
      state->inverted_p = prefs.inverted_p;
      state->seconds_only_p = prefs.seconds_only_p;
      if (prefs.ticks > 0)
        state->ticks = prefs.ticks;
    }

  state->redraw_p = true;
  state->display_date = DTime;
  state->test_hack = 0;
  state->interval = SysTicksPerSecond() / state->ticks;
  state->tick = state->ticks;

#ifndef SECONDS_ONLY_MODE
  state->seconds_only_p = 0;
#endif

  if (!init_numbers (state))
    return false;

  WinGetWindowExtent (&state->win_width, &state->win_height);
  return true;
}




static Boolean
start_app (struct state *state)
{
  FormPtr form;

#if 0
  MemSetDebugMode (memDebugModeCheckOnChange |
                   memDebugModeCheckOnAll |
                   memDebugModeScrambleOnChange |
                   memDebugModeScrambleOnAll |
                   memDebugModeFillFree |
                   memDebugModeAllHeaps |
                   memDebugModeRecordMinDynHeapFree);
#endif

  form = FrmInitForm (MainForm);
  FrmSetActiveForm (form);
  FrmDrawForm (form);
  return false;
}


static void
stop_app (struct state *state)
{
  state->display_date = DTime;
  free_numbers (state);
  FrmCloseAllForms ();
}


static void
event_loop (struct state *state)
{
  UInt16 error;
  EventType event;

  long delay = 0;

  do {
    UInt32 start, stop;
    long elapsed;

    EvtGetEvent (&event, delay);

    start = TimGetTicks();          /* start timing event execution */

    if (!SysHandleEvent (&event))
      if (!MenuHandleEvent (NULL, &event, &error))
        if (!app_handle_event (state, &event))
          /*FrmDispatchEvent (&event)*/; 

    stop = TimGetTicks();           /* stop timing event execution */


    elapsed = stop - start;
    delay = state->interval - elapsed;
    if (delay < 0) delay = 0;

  } while (event.eType != appStopEvent);
}


static Err
check_rom_version (UInt32 required_version, UInt16 launch_flags)
{
  UInt32 rom_version = 0;
  FtrGet (sysFtrCreator, sysFtrNumROMVersion, &rom_version);
  if (rom_version < required_version)
    {
      if ((launch_flags &
           (sysAppLaunchFlagNewGlobals | sysAppLaunchFlagUIApp)) 
          == (sysAppLaunchFlagNewGlobals | sysAppLaunchFlagUIApp))
        {
          FrmAlert (RomIncompatibleAlert);
      
# ifdef HAVE_APPLAUNCHWITHCOMMAND
          /* PalmOS 1.0 will continuously relaunch this app unless we switch
             to another safe one.  The sysFileCDefaultApp is considered "safe"
          */
          if (rom_version < ROM_20)
            AppLaunchWithCommand (sysFileCDefaultApp,
                                  sysAppLaunchCmdNormalLaunch, 0);
# endif /* HAVE_APPLAUNCHWITHCOMMAND */
        }

      return sysErrRomIncompatible;
    }
  return 0;
}



UInt32 
PilotMain (UInt16 cmd, MemPtr cmdPBP, UInt16 launchFlags)
{
  Err error = check_rom_version (ROM_20, launchFlags);
  if (error)
    return error;

  if (cmd == sysAppLaunchCmdNormalLaunch)
    {
      struct state state;

      if (!initialize (&state))
        return true;

      if (start_app (&state))
        return true;

      event_loop (&state);
      stop_app (&state);
    }
  return 0;
}

/* pose -load_apps daliclock.prc -run_app 'Dali Clock'
   remember to set "ReportScreenAccess=0" in ~/.poserrc
 */

Generated by  Doxygen 1.6.0   Back to index