3
\$\begingroup\$

I'm seeking style pointers, bugs, memory leaks, incorrect usage etc.

#include <pebble.h>

static Window *s_main_window;
static AppTimer *app_timer;
time_t now;
struct tm disp_time; // Time to display.

// Slot on-screen layout:
//     0 1
//     2 3
#define TOTAL_IMAGE_SLOTS 4
#define NUMBER_OF_IMAGES 10

// These images are 72 x 84 pixels (i.e. a quarter of the Aplite display),
// black and white with the digit character centered in the image.
// (As generated by the `fonttools/font2png.py` script.)
const int IMAGE_RESOURCE_IDS[NUMBER_OF_IMAGES] = {
  RESOURCE_ID_IMAGE_NUM_0, RESOURCE_ID_IMAGE_NUM_1, RESOURCE_ID_IMAGE_NUM_2,
  RESOURCE_ID_IMAGE_NUM_3, RESOURCE_ID_IMAGE_NUM_4, RESOURCE_ID_IMAGE_NUM_5,
  RESOURCE_ID_IMAGE_NUM_6, RESOURCE_ID_IMAGE_NUM_7, RESOURCE_ID_IMAGE_NUM_8,
  RESOURCE_ID_IMAGE_NUM_9
};

static GBitmap *s_images[TOTAL_IMAGE_SLOTS + 1];
static BitmapLayer *s_image_layers[TOTAL_IMAGE_SLOTS];

#define EMPTY_SLOT -1

// The state is either "empty" or the digit of the image currently in the slot.
static int s_image_slot_state[TOTAL_IMAGE_SLOTS] = {
  EMPTY_SLOT, EMPTY_SLOT, EMPTY_SLOT, EMPTY_SLOT
};

// Loads the digit image from the application's resources and
// displays it on-screen in the correct location.
// Each slot is a quarter of the screen.
static void load_digit_image_into_slot(int slot_number, int digit_value) {
  GRect bounds = layer_get_bounds(window_get_root_layer(s_main_window));

  s_image_slot_state[slot_number] = digit_value;
  s_images[slot_number] = gbitmap_create_with_resource(IMAGE_RESOURCE_IDS[digit_value]);

  GRect tile_bounds = gbitmap_get_bounds(s_images[slot_number]);

  const int x_offset = (bounds.size.w - (2 * tile_bounds.size.w)) / 2;
  const int y_offset = (bounds.size.h - (2 * tile_bounds.size.h)) / 2;
  BitmapLayer *bitmap_layer = bitmap_layer_create(
    GRect(x_offset + ((slot_number % 2) * tile_bounds.size.w),
      y_offset + ((slot_number / 2) * tile_bounds.size.h),
      tile_bounds.size.w, tile_bounds.size.h));
  s_image_layers[slot_number] = bitmap_layer;
  bitmap_layer_set_bitmap(bitmap_layer, s_images[slot_number]);

  Layer *window_layer = window_get_root_layer(s_main_window);
  layer_add_child(window_layer, bitmap_layer_get_layer(bitmap_layer));
}

static void unload_digit_image_from_slot(int slot_number) {
  if (s_image_slot_state[slot_number] != EMPTY_SLOT) {
    layer_remove_from_parent(bitmap_layer_get_layer(s_image_layers[slot_number]));
    bitmap_layer_destroy(s_image_layers[slot_number]);
    gbitmap_destroy(s_images[slot_number]);

    // This is now an empty slot
    s_image_slot_state[slot_number] = EMPTY_SLOT;
  }
}

static void display_value(unsigned short value, unsigned short row_number, bool show_first_leading_zero) {
  value = value % 100; // Maximum of two digits per row.

  // Column order is: | Column 0 | Column 1 |
  // (We process the columns in reverse order because that makes
  // extracting the digits from the value easier.)
  for (int column_number = 1; column_number >= 0; column_number--) {
    int slot_number = (row_number * 2) + column_number;
    unload_digit_image_from_slot(slot_number);
    if (!((value == 0) && (column_number == 0) && !show_first_leading_zero)) {
      load_digit_image_into_slot(slot_number, value % 10);
    }
    value = value / 10;
  }
}

static unsigned short get_display_hour(unsigned short hour) {
  if (clock_is_24h_style()) {
    return hour;
  }
  // Converts "0" to "12"
  unsigned short display_hour = hour % 12;
  return display_hour ? display_hour : 12;
}

static void display_time() {
  display_value(get_display_hour(disp_time.tm_hour), 0, false);
  display_value(disp_time.tm_min, 1, true);
}

static void tick_handler(struct tm *tick_time, TimeUnits units_changed) {
  disp_time = *tick_time; // Set display time value supplied to tick_handler.
  display_time();
}

static void auto_redisplay_time(void *data) {
  app_timer = NULL;
  display_time();
}

static void display_date() {
  display_value(disp_time.tm_mday, 0, true);
  display_value(disp_time.tm_mon+1, 1, true);
  if (app_timer != NULL) {
    app_timer_cancel(app_timer);
  }
  app_timer = app_timer_register(1000, auto_redisplay_time, NULL);
}

static void tap_handler(AccelAxisType axis, int32_t direction) {
  display_date();
}

static void main_window_load(Window *window) {
  now = time(NULL);
  disp_time = *localtime(&now);  // First run through so set display_time.
  display_time();
}

static void main_window_unload(Window *window) {
  for (int i = 0; i < TOTAL_IMAGE_SLOTS; i++) {
    unload_digit_image_from_slot(i);
  }
}

static void init() {
  s_main_window = window_create();
  window_set_background_color(s_main_window, GColorBlack);
  window_set_window_handlers(s_main_window, (WindowHandlers) {
    .load = main_window_load,
    .unload = main_window_unload,
  });
  window_stack_push(s_main_window, true);
  tick_timer_service_subscribe(MINUTE_UNIT, tick_handler);
  accel_tap_service_subscribe(tap_handler);
}

static void deinit() {
  accel_tap_service_unsubscribe();
  tick_timer_service_unsubscribe();
  window_destroy(s_main_window);
}

int main(void) {
  init();
  app_event_loop();
  deinit();
}
\$\endgroup\$
  • 2
    \$\begingroup\$ I think you should add some documentation. \$\endgroup\$ – edmz Dec 1 '15 at 18:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.