#include <stdio.h> #include "oswald.h" #include "oswald_main.h" #include "oswald_watch_faces.h" #include "oswald_graphics.h" #include "oswald_hal.h" #include "dayslib.h" #include "mymem.h" #include "strutils.h" #include "oswald_screens.h" #if defined(__GNUC__) && (__MSP430X__ > 0) __attribute__((__far__)) #endif #include "bitmaps/startstopbutton_icon.xbm" #if defined(__GNUC__) && (__MSP430X__ > 0) __attribute__((__far__)) #endif #include "bitmaps/lapsebutton_icon.xbm" #if defined(__GNUC__) && (__MSP430X__ > 0) __attribute__((__far__)) #endif #include "bitmaps/upbutton_icon.xbm" #if defined(__GNUC__) && (__MSP430X__ > 0) __attribute__((__far__)) #endif #include "bitmaps/downbutton_icon.xbm" #if defined(__GNUC__) && (__MSP430X__ > 0) __attribute__((__far__)) #endif #include "bitmaps/leftbutton_icon.xbm" #if defined(__GNUC__) && (__MSP430X__ > 0) __attribute__((__far__)) #endif #include "bitmaps/rightbutton_icon.xbm" #if defined(__GNUC__) && (__MSP430X__ > 0) __attribute__((__far__)) #endif #include "bitmaps/enterbutton_icon.xbm" #if defined(__GNUC__) && (__MSP430X__ > 0) __attribute__((__far__)) #endif #include "bitmaps/checked_icon.xbm" #if defined(__GNUC__) && (__MSP430X__ > 0) __attribute__((__far__)) #endif #include "bitmaps/unchecked_icon.xbm" #if defined(__GNUC__) && (__MSP430X__ > 0) __attribute__((__far__)) #endif #include "bitmaps/main_menu_icon.xbm" #if defined(__GNUC__) && (__MSP430X__ > 0) __attribute__((__far__)) #endif #include "bitmaps/timesetup_icon.xbm" #if defined(__GNUC__) && (__MSP430X__ > 0) __attribute__((__far__)) #endif #include "bitmaps/stopwatch_icon.xbm" #if defined(__GNUC__) && (__MSP430X__ > 0) __attribute__((__far__)) #endif #include "bitmaps/alarm_icon.xbm" #if defined(__GNUC__) && (__MSP430X__ > 0) __attribute__((__far__)) #endif #include "bitmaps/bluetooth_icon.xbm" #if defined(__GNUC__) && (__MSP430X__ > 0) __attribute__((__far__)) #endif #include "bitmaps/info_icon.xbm" #if defined(__GNUC__) && (__MSP430X__ > 0) __attribute__((__far__)) #endif #include "bitmaps/acc_icon.xbm" #if defined(__GNUC__) && (__MSP430X__ > 0) __attribute__((__far__)) #endif #include "bitmaps/message_icon.xbm" #if defined(__GNUC__) && (__MSP430X__ > 0) __attribute__((__far__)) #endif #include "bitmaps/exit_icon.xbm" /* * Common event handler part of the watch faces */ typedef struct { void (*screendraw_func)(boolean show_seconds); boolean show_seconds; int curclock; } idle_data_t; static idle_data_t idle_screen = { DrawLcdDigitalClock, TRUE, 0, }; static struct { void (*screendraw_func)(boolean show_seconds); } clocks[]={ {DrawLcdDigitalClock}, {DrawLcdDigitalClock2}, {DrawLcdAnaClock}, {DrawCalendarClock}, {DrawHelpClock} }; event_ret_t idle_handle_user_buttons(watch_button button) { switch (button) { case BUTTON_A: case BUTTON_C: idle_screen.curclock+=(button==BUTTON_C)?1:((sizeof(clocks)/sizeof(clocks[0]))-1); idle_screen.curclock%=(sizeof(clocks)/sizeof(clocks[0])); idle_screen.screendraw_func = clocks[idle_screen.curclock].screendraw_func; break; case BUTTON_B: OswaldState.screen_id = CALENDAR_OVERVIEW_SCREEN; OswaldState.screen = &OswaldScreens[OswaldState.screen_id]; OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL); return EVENT_RET_HANDLED; break; case BUTTON_E: OswaldState.screen_id = MAIN_MENU_SCREEN; OswaldState.screen = &OswaldScreens[OswaldState.screen_id]; OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL); return EVENT_RET_HANDLED; break; case BUTTON_F: OswaldState.screen_id = DATETIME_SETTING_SCREEN; OswaldState.screen = &OswaldScreens[OswaldState.screen_id]; OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL); return EVENT_RET_HANDLED; break; default: return EVENT_RET_UNHANDLED; break; }; idle_screen.screendraw_func(idle_screen.show_seconds); return EVENT_RET_HANDLED; } event_ret_t idle_handle_events(uint16_t event, void *data) { switch (event) { case EVENT_ONE_SEC_TIMER: case EVENT_SCREEN_VISIBLE: idle_screen.screendraw_func(idle_screen.show_seconds); return EVENT_RET_HANDLED; break; case EVENT_USER_BUTTONS: dbg_out("button event %d\n", *(int *)data); return idle_handle_user_buttons(*(watch_button *)data); break; default: return EVENT_RET_UNHANDLED; break; }; return EVENT_RET_UNHANDLED; } /* * Main Menu Screen */ typedef struct { int8_t pos; uint8_t tmo; } main_menu_data_t; static main_menu_data_t main_menu_screen = { ALARM_SETUP_SCREEN, 0, }; #define MAIN_MENU_GRID_PIXEL 84 #define MAIN_MENU_GRID_X 3 // GRID_Y is +1 since there is one empty row for title icon #define MAIN_MENU_GRID_Y 3 #define MAIN_MENU_GRID_SPACING 0 #define MAIN_MENU_OFFSET_X 6 #define MAIN_MENU_OFFSET_Y 8 #define oswald_draw_bitmap_i(a,b,c,d,e,f) if(f) oswald_draw_bitmap(a,b,c,d,1,e) static struct struct_menu_items { int screen_num; char *name; unsigned char *bits; int width; int height; } menu_items[] = { {ALARM_SETUP_SCREEN,"Alarms",alarm_icon_bits,alarm_icon_width,alarm_icon_height}, {STOP_WATCH_SCREEN,"Stopwatch",stopwatch_icon_bits,stopwatch_icon_width,stopwatch_icon_height}, {BLUETOOTH_SCREEN,"Bluetooth",bluetooth_icon_bits,bluetooth_icon_width,bluetooth_icon_height}, {ACCEL_DISPLAY_SCREEN,"Accel.",acc_icon_bits,acc_icon_width,acc_icon_height}, {MESSAGES_SCREEN,"Messages",message_icon_bits,message_icon_width,message_icon_height}, {INFO_SCREEN,"About",info_icon_bits,info_icon_width,info_icon_height}, {LAST_SCREEN,"Close menu",exit_icon_bits,exit_icon_width,exit_icon_height} }; void draw_main_menu_screen(main_menu_data_t *sdata) { int x,y; int i,l; int w; char *t; hal_lcd_clear_display(); i=sdata->pos-2; i=(i<0)?0:i; if(i>(sizeof(menu_items)/sizeof(menu_items[0]))-3) { i=(sizeof(menu_items)/sizeof(menu_items[0]))-3; } for(l=i+3,y=5;i<(sizeof(menu_items)/sizeof(menu_items[0])) && i<l;i++,y+=32) { x=15; if(i==sdata->pos) oswald_draw_rect(0,y-5,95,y+32-5,1); oswald_draw_bitmap(x,y,menu_items[i].width,menu_items[i].height,(i==sdata->pos)?0:1,menu_items[i].bits); oswald_writestr_font58_scaled(x+30, y+6,menu_items[i].name,&w,(i==sdata->pos)?0:1,(i==sdata->pos)?1:0,1,2); } hal_lcd_update_display(); } event_ret_t handle_main_menu_buttons(watch_button button, main_menu_data_t *sdata) { switch (button) { case BUTTON_C: sdata->pos++; if (sdata->pos >= (sizeof(menu_items)/sizeof(menu_items[0]))) sdata->pos = 0; draw_main_menu_screen(&main_menu_screen); return EVENT_RET_HANDLED; break; case BUTTON_A: sdata->pos--; if (sdata->pos < 0) sdata->pos = (sizeof(menu_items)/sizeof(menu_items[0]))-1; draw_main_menu_screen(&main_menu_screen); return EVENT_RET_HANDLED; break; case BUTTON_B: if (menu_items[sdata->pos].screen_num!=LAST_SCREEN) { OswaldState.screen_id = menu_items[sdata->pos].screen_num; OswaldState.screen = &OswaldScreens[OswaldState.screen_id]; OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL); } else { OswaldState.screen_id = IDLE_SCREEN; OswaldState.screen = &OswaldScreens[OswaldState.screen_id]; OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL); } return EVENT_RET_HANDLED; break; default: break; } return EVENT_RET_UNHANDLED; } /* after MAIN_MENU_TIMEOUT seconds return to IDLE_SCREEN */ #define MAIN_MENU_TIMEOUT 10 event_ret_t main_menu_handle_events(uint16_t event, void *data) { switch (event) { case EVENT_SCREEN_VISIBLE: main_menu_screen.tmo = 0; main_menu_screen.pos = 0; draw_main_menu_screen(&main_menu_screen); return EVENT_RET_HANDLED; break; case EVENT_USER_BUTTONS: dbg_out("button event %d\n", *(int *)data); main_menu_screen.tmo = 0; return handle_main_menu_buttons(*(watch_button *)data, &main_menu_screen); break; case EVENT_ONE_SEC_TIMER: main_menu_screen.tmo++; if (main_menu_screen.tmo > MAIN_MENU_TIMEOUT) { OswaldState.screen_id = IDLE_SCREEN; OswaldState.screen = &OswaldScreens[OswaldState.screen_id]; OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL); } return EVENT_RET_HANDLED; default: break; }; return EVENT_RET_UNHANDLED; } /* * Accelerometer and sensor display screen */ typedef struct { accel_data_t accdata; } accelscreen_data_t; static accelscreen_data_t accel_screen = { { 0, 0, 0}, }; void draw_accel_screen(accel_data_t *accel_data) { uint8_t x,y; hal_lcd_clear_display(); oswald_draw_bitmap(36, 0, acc_icon_width, acc_icon_height, 1, acc_icon_bits); oswald_write_string(1, 40, FONT_6x9, FALSE, "X:"); oswald_write_number(15, 40, FONT_6x9, FALSE, accel_data->x); oswald_write_string(1, 52, FONT_6x9, FALSE, "Y:"); oswald_write_number(15, 52, FONT_6x9, FALSE, accel_data->y); oswald_write_string(1, 64, FONT_6x9, FALSE, "Z:"); oswald_write_number(15, 64, FONT_6x9, FALSE, accel_data->z); oswald_write_string(1, 85, FONT_6x9, FALSE, "Light:"); oswald_write_number(50, 85, FONT_6x9, FALSE, 0); oswald_draw_line(40, 30, 92, 30, 1); oswald_draw_line(92, 30, 92, 82, 1); oswald_draw_line(40, 82, 92, 82, 1); oswald_draw_line(40, 82, 40, 30, 1); x = 41+25+((accel_data->x * 50) / (254)); y = 31+25-((accel_data->y * 50) / (254)); oswald_draw_pixel(x, y, TRUE); oswald_draw_pixel(x+1, y, TRUE); oswald_draw_pixel(x-1, y, TRUE); oswald_draw_pixel(x, y+1, TRUE); oswald_draw_pixel(x, y-1, TRUE); hal_lcd_update_display(); } event_ret_t accel_handle_events(uint16_t event, void *data) { switch (event) { case EVENT_SCREEN_VISIBLE: draw_accel_screen(&accel_screen.accdata); hal_accelerometer_enable(); return EVENT_RET_HANDLED; break; case EVENT_SCREEN_DESTROY: hal_accelerometer_disable(); return EVENT_RET_HANDLED; break; case EVENT_ACCEL_UPDATE: { accel_data_t *accel_data = (accel_data_t *)data; accel_screen.accdata.x = accel_data->x; accel_screen.accdata.y = accel_data->y; accel_screen.accdata.z = accel_data->z; draw_accel_screen(&accel_screen.accdata); }; return EVENT_RET_HANDLED; break; case EVENT_USER_BUTTONS: dbg_out("button event %d\n", *(int *)data); break; default: return EVENT_RET_UNHANDLED; break; }; return EVENT_RET_UNHANDLED; } /* * Date / time setup screen */ typedef struct { uint8_t pos; boolean set_mode; boolean on; } datetime_setup_data_t; static datetime_setup_data_t dt_setup_screen = { 0, FALSE, TRUE }; void draw_datetime_setup_screen(datetime_setup_data_t *sdata) { int w,sx=2,sy=3; hal_lcd_clear_display(); oswald_draw_grid(1,1,94,94,100,100); oswald_draw_bitmap(36, 4, timesetup_icon_width, timesetup_icon_height, 1, timesetup_icon_bits); if ((sdata->pos == 0 && sdata->on) || sdata->pos != 0) { if(OswaldClk.hour>=10) oswald_draw_font58_scaled(12, 34, (OswaldClk.hour / 10)+'0',&w,1,0,sx,sy); oswald_draw_font58_scaled(25, 34, (OswaldClk.hour % 10)+'0',&w,1,0,sx,sy); } oswald_draw_font58_scaled(37, 38, ':',&w,1,0,1,2); if ((sdata->pos == 1 && sdata->on) || sdata->pos != 1) { oswald_draw_font58_scaled(44, 34, (OswaldClk.minute / 10)+'0',&w,1,0,sx,sy); oswald_draw_font58_scaled(57, 34, (OswaldClk.minute % 10)+'0',&w,1,0,sx,sy); } if ((sdata->pos == 2 && sdata->on) || sdata->pos != 2) { oswald_draw_font58_scaled(71, 40, (OswaldClk.second / 10)+'0',&w,1,0,sx-1,sy-1); oswald_draw_font58_scaled(79, 40, (OswaldClk.second % 10)+'0',&w,1,0,sx-1,sy-1); } if ((sdata->pos == 3 && sdata->on) || sdata->pos != 3) { oswald_write_number(18, 57, FONT_5x8, FALSE, OswaldClk.day); } oswald_write_character(31, 57, FONT_5x8, FALSE, '.'); if ((sdata->pos == 4 && sdata->on) || sdata->pos != 4) { oswald_write_number(37, 57, FONT_5x8, FALSE, OswaldClk.month); } oswald_write_character(51, 57, FONT_5x8, FALSE, '.'); if ((sdata->pos == 5 && sdata->on) || sdata->pos != 5) { oswald_write_number(59, 57, FONT_5x8, FALSE, OswaldClk.year); } if ((sdata->pos == 6 && sdata->on) || sdata->pos != 6) { if (OswaldClk.clk24hr) { oswald_draw_bitmap(12, 73, checked_icon_width, checked_icon_height, 1, checked_icon_bits); } else { oswald_draw_bitmap(12, 73, unchecked_icon_width, unchecked_icon_height, 1, unchecked_icon_bits); } } oswald_write_string(25, 73, FONT_5x8, FALSE, "24hr"); if ((sdata->pos == 7 && sdata->on) || sdata->pos != 7) { if (OswaldClk.day_first) { oswald_draw_bitmap(12, 83, checked_icon_width, checked_icon_height, 1, checked_icon_bits); } else { oswald_draw_bitmap(12, 83, unchecked_icon_width, unchecked_icon_height, 1, unchecked_icon_bits); } } oswald_write_string(25, 83, FONT_5x8, FALSE, "dd.mm. mm/dd"); hal_lcd_update_display(); } void datetime_handle_updown(uint8_t pos, int8_t incr) { switch (pos) { case 0: // hour if (OswaldClk.hour == 0 && incr == -1) { OswaldClk.hour = 23; break; }; OswaldClk.hour += incr; if (OswaldClk.hour > 23) OswaldClk.hour = 0; break; case 1: // minute if (OswaldClk.minute == 0 && incr == -1) { OswaldClk.minute = 59; break; }; OswaldClk.minute += incr; if (OswaldClk.minute > 59) OswaldClk.minute = 0; break; case 2: // second OswaldClk.second = 0; break; case 3: // day if (OswaldClk.day == 1 && incr == -1) { OswaldClk.day = 31; break; }; OswaldClk.day += incr; if (OswaldClk.day > 31) OswaldClk.day = 1; break; case 4: // month if (OswaldClk.month == 1 && incr == -1) { OswaldClk.month = 12; break; }; OswaldClk.month += incr; if (OswaldClk.month > 12) OswaldClk.month = 1; break; case 5: // year OswaldClk.year += incr; break; case 6: // 24hr / 12hr if (OswaldClk.clk24hr) OswaldClk.clk24hr = FALSE; else OswaldClk.clk24hr = TRUE; break; case 7: // dd.mm. / mm/dd if (OswaldClk.day_first) OswaldClk.day_first = FALSE; else OswaldClk.day_first = TRUE; break; default: break; }; if (pos == 2) hal_set_rtc(&OswaldClk, TRUE); else hal_set_rtc(&OswaldClk, FALSE); } event_ret_t handle_setup_datetime_buttons(watch_button button, datetime_setup_data_t *sdata) { switch (button) { case BUTTON_A: datetime_handle_updown(sdata->pos, 1); break; case BUTTON_C: datetime_handle_updown(sdata->pos, -1); break; case BUTTON_B: sdata->pos++; sdata->pos %= 8; break; case BUTTON_E: sdata->pos=(sdata->pos+8-1)%8; break; case BUTTON_F: OswaldState.screen->event_func(EVENT_SCREEN_DESTROY, NULL); OswaldState.screen_id = IDLE_SCREEN; OswaldState.screen = &OswaldScreens[OswaldState.screen_id]; OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL); return EVENT_RET_HANDLED; break; default: return EVENT_RET_UNHANDLED; break; } draw_datetime_setup_screen(sdata); return EVENT_RET_HANDLED; } event_ret_t datetime_setup_events(uint16_t event, void *data) { switch (event) { case EVENT_SCREEN_VISIBLE: dt_setup_screen.pos = 0; draw_datetime_setup_screen(&dt_setup_screen); hal_enable_halfsecond_timer(); return EVENT_RET_HANDLED; break; case EVENT_SCREEN_DESTROY: hal_disable_halfsecond_timer(); return EVENT_RET_HANDLED; break; case EVENT_USER_BUTTONS: dbg_out("button event %d\n", *(int *)data); return handle_setup_datetime_buttons(*(watch_button *)data, &dt_setup_screen); break; case EVENT_HALF_SEC_TIMER: if (dt_setup_screen.on) dt_setup_screen.on = FALSE; else dt_setup_screen.on = TRUE; draw_datetime_setup_screen(&dt_setup_screen); return EVENT_RET_HANDLED; break; default: return EVENT_RET_UNHANDLED; break; }; return EVENT_RET_UNHANDLED; } /* * Alarm setup screen */ typedef struct { int num; int pos; boolean set_mode; boolean on; } alarm_setup_data_t; static alarm_setup_data_t alarm_setup_screen = { 0, 0, FALSE, TRUE }; void draw_alarm_setup_screen(alarm_setup_data_t *sdata) { static char initials[]={"SMTWTFS"}; int w,i; hal_lcd_clear_display(); if (sdata->set_mode) oswald_draw_grid(1,1,94,94,100,100); oswald_draw_bitmap(36, 3, alarm_icon_width, alarm_icon_height, 1, alarm_icon_bits); oswald_draw_rect(43,8,52,20,0); oswald_draw_font58_scaled(44,9,sdata->num+'1',&w,1,0,2,2); if ((sdata->pos == 0 && sdata->on) || sdata->pos != 0) { oswald_draw_font58_scaled(18, 34, (OswaldAlarm[sdata->num].hour / 10)+'0',&w,1,0,2,3); oswald_draw_font58_scaled(31, 34, (OswaldAlarm[sdata->num].hour % 10)+'0',&w,1,0,2,3); } oswald_draw_font58_scaled(43, 38, ':',&w,1,0,1,2); if ((sdata->pos == 1 && sdata->on) || sdata->pos != 1) { oswald_draw_font58_scaled(50, 34, (OswaldAlarm[sdata->num].minute / 10)+'0',&w,1,0,2,3); oswald_draw_font58_scaled(63, 34, (OswaldAlarm[sdata->num].minute % 10)+'0',&w,1,0,2,3); } for(i=0;i<7;i++) { oswald_write_character(8+i*12, 65, FONT_6x9, FALSE, initials[i]); if ((sdata->pos == (i+2) && sdata->on) || sdata->pos != (i+2)) { if ((OswaldAlarm[sdata->num].wday & (1<<i))) oswald_draw_bitmap(7+i*12, 76, checked_icon_width, checked_icon_height, 1, checked_icon_bits); else oswald_draw_bitmap(7+i*12, 76, unchecked_icon_width, unchecked_icon_height, 1, unchecked_icon_bits); } } hal_lcd_update_display(); } void alarm_handle_updown(int num, int pos, int incr) { switch (pos) { case 0: // hour if (OswaldAlarm[num].hour == 0 && incr == -1) { OswaldAlarm[num].hour = 23; break; }; OswaldAlarm[num].hour += incr; if (OswaldAlarm[num].hour > 23) OswaldAlarm[num].hour = 0; break; case 1: // minute if (OswaldAlarm[num].minute == 0 && incr == -1) { OswaldAlarm[num].minute = 59; break; }; OswaldAlarm[num].minute += incr; if (OswaldAlarm[num].minute > 59) OswaldAlarm[num].minute = 0; break; case 2: // sunday OswaldAlarm[num].wday ^= WDAY_SUNDAY; break; case 3: // monday OswaldAlarm[num].wday ^= WDAY_MONDAY; break; case 4: // tuesday OswaldAlarm[num].wday ^= WDAY_TUESDAY; break; case 5: // wednesday OswaldAlarm[num].wday ^= WDAY_WEDNESDAY; break; case 6: // thursday OswaldAlarm[num].wday ^= WDAY_THURSDAY; break; case 7: // friday OswaldAlarm[num].wday ^= WDAY_FRIDAY; break; case 8: // saturday OswaldAlarm[num].wday ^= WDAY_SATURDAY; break; default: break; }; } event_ret_t handle_setup_alarm_buttons(watch_button button, alarm_setup_data_t *sdata) { if (alarm_setup_screen.set_mode) { switch (button) { case BUTTON_A: alarm_handle_updown(sdata->num, sdata->pos, 1); break; case BUTTON_C: alarm_handle_updown(sdata->num, sdata->pos, -1); break; case BUTTON_E: sdata->pos+=9-1; sdata->pos %= 9; break; case BUTTON_B: sdata->pos++; sdata->pos %= 9; break; case BUTTON_F: alarm_setup_screen.set_mode = FALSE; alarm_setup_screen.pos = 0; break; default: return EVENT_RET_UNHANDLED; break; } } else { switch (button) { case BUTTON_A: sdata->num+=MAX_ALARM+((button==BUTTON_A)?-1:1); sdata->num%=MAX_ALARM; break; case BUTTON_C: sdata->num+=MAX_ALARM+((button==BUTTON_A)?-1:1); sdata->num%=MAX_ALARM; break; case BUTTON_F: alarm_setup_screen.set_mode = TRUE; break; default: return EVENT_RET_UNHANDLED; break; } } draw_alarm_setup_screen(sdata); return EVENT_RET_HANDLED; } event_ret_t alarm_setup_events(uint16_t event, void *data) { switch (event) { case EVENT_SCREEN_VISIBLE: alarm_setup_screen.num = 0; alarm_setup_screen.pos = 0; alarm_setup_screen.on = TRUE; alarm_setup_screen.set_mode = FALSE; draw_alarm_setup_screen(&alarm_setup_screen); hal_enable_halfsecond_timer(); return EVENT_RET_HANDLED; break; case EVENT_SCREEN_DESTROY: hal_disable_halfsecond_timer(); return EVENT_RET_HANDLED; break; case EVENT_USER_BUTTONS: dbg_out("button event %d\n", *(int *)data); return handle_setup_alarm_buttons(*(watch_button *)data, &alarm_setup_screen); break; case EVENT_HALF_SEC_TIMER: if (alarm_setup_screen.set_mode) { if (alarm_setup_screen.on) alarm_setup_screen.on = FALSE; else alarm_setup_screen.on = TRUE; } else alarm_setup_screen.on = TRUE; draw_alarm_setup_screen(&alarm_setup_screen); return EVENT_RET_HANDLED; break; default: return EVENT_RET_UNHANDLED; break; }; return EVENT_RET_UNHANDLED; } /* * Test menu */ typedef struct { uint8_t menu_pos; } test_menu_t; static test_menu_t test_menu = { 0 }; void draw_menu_test_screen(void) { hal_lcd_clear_display(); #if 0 SetFont(MetaWatch16); WriteLcdString(2, 2, "Menu"); SetFont(MetaWatch7); WriteLcdString(2, 20, "Item 1"); WriteLcdString(2, 29, "Item 2"); WriteLcdString(2, 38, "Item 3"); WriteLcdString(2, 47, "Item 4"); WriteLcdString(2, 56, "Item 5"); WriteLcdString(50, 20+(9*test_menu.menu_pos), "*"); #endif oswald_write_string(2, 2, FONT_DROID8x12, FALSE, "Menu"); oswald_write_string(2, 20, FONT_DROID8x12, FALSE, "Item 1"); oswald_write_string(2, 29, FONT_DROID8x12, FALSE, "Item 2"); oswald_write_string(2, 38, FONT_DROID8x12, FALSE, "Item 3"); oswald_write_string(2, 47, FONT_DROID8x12, FALSE, "Item 4"); oswald_write_string(2, 56, FONT_DROID8x12, FALSE, "Item 5"); oswald_write_character(50, 18+(9*test_menu.menu_pos), FONT_6x9, FALSE, 0x11); hal_lcd_update_display(); } event_ret_t handle_menu_user_buttons(watch_button button) { switch (button) { case BUTTON_A: test_menu.menu_pos--; test_menu.menu_pos %= 5; break; case BUTTON_B: test_menu.menu_pos++; test_menu.menu_pos %= 5; break; default: return EVENT_RET_UNHANDLED; break; } draw_menu_test_screen(); return EVENT_RET_HANDLED; } event_ret_t test_menu_handle_events(uint16_t event, void *data) { switch (event) { case EVENT_USER_BUTTONS: dbg_out("button event %d\n", *(int *)data); return handle_menu_user_buttons(*(watch_button *)data); break; case EVENT_SCREEN_VISIBLE: test_menu.menu_pos = 0; draw_menu_test_screen(); break; default: return EVENT_RET_UNHANDLED; break; }; return EVENT_RET_HANDLED; } /* * Stop Watch */ typedef enum estopw_item { stopw_startstop, stopw_split, stopw_reset, stopw_end } stopw_item; typedef struct { uint8_t hr; uint8_t min; uint8_t sec; uint8_t csec; } timerecord; #define MAX_LAPS 2 typedef struct { timerecord current; int laps; timerecord lapsdata[MAX_LAPS]; timerecord avg; timerecord max; timerecord min; timerecord lastlap; boolean running; stopw_item item; boolean showstats; } stopwatch_data_t; static stopwatch_data_t stopwatch_screen = { {0, 0, 0, 0},0,{{0, 0, 0, 0}} , FALSE,0}; static void timerecord2str(timerecord *tr, char *timebuf /* sizeof(timebuf)>=9 */, char *csecbuf /*sizeof(csecbuf)>=3*/) { num2str(tr->hr,timebuf,2,2); if(tr->hr<10) timebuf[0]=' '; timebuf[2]=':'; num2str(tr->min,timebuf+3,2,2); timebuf[5]=':'; num2str(tr->sec,timebuf+6,2,2); num2str(tr->csec,csecbuf,2,2); } static void timerecord_diff(timerecord *new, timerecord *old, timerecord *res) { int d,od; mymemset(res,0,sizeof(timerecord)); d=(old->csec>new->csec)?1:0; res->csec=(new->csec+d*100)-old->csec; od=d,d=(old->sec>(new->sec-od))?1:0; res->sec=(new->sec-od+d*60)-old->sec; od=d,d=(old->min>(new->min-od))?1:0; res->min=(new->min-od+d*60)-old->min; res->hr=(new->hr-d)-old->hr; } static void timerecord_div(timerecord *total, int div, timerecord *res) { int n; mymemset(res,0,sizeof(timerecord)); if(div<0) return; res->hr=total->hr/div; n=(((int)total->min)+(total->hr%div)*60); res->min=n/div; n=(((int)total->sec)+(n%div)*60); res->sec=n/div; n=(((int)total->csec)+(n%div)*100); res->csec=n/div; } static int timerecord_cmp(timerecord *a, timerecord *b) { if(a->hr!=b->hr) return(a->hr-b->hr); if(a->min!=b->min) return(a->min-b->min); if(a->sec!=b->sec) return(a->sec-b->sec); return(a->csec-b->csec); } typedef enum estopwmask { stopwm_icon=0x1, stopwm_laps=0x2, stopwm_split=0x4, stopwm_current=0x8, stopwm_menu=0x10, stopwm_all=0x1f } stopwmask; static void draw_stop_watch_screen(stopwatch_data_t *sdata, int mask) { int w; char *hint; int x,y; char lapbuf[4]; char timebuf[9]; char csecbuf[3]; int i; if(sdata->showstats) { hal_lcd_clear_display(); oswald_draw_grid(1,1,94,94,100,100); oswald_draw_bitmap(36, 3, stopwatch_icon_width, stopwatch_icon_height, 1, stopwatch_icon_bits); /* laps */ num2str(sdata->laps,lapbuf,3,3); oswald_writestr_font58_scaled(3,3,lapbuf,&w,1,0,2,2); /* stats */ for(i=0;i<3;i++) { y=20+i*24; hint=((i==0)?"Average":(i==1)?"Min":"Max"); oswald_writestr_font58(4,y+3,hint,&w,1,0); timerecord2str(i==0?&(sdata->avg):i==1?&(sdata->min):&(sdata->max),timebuf,csecbuf); x=8+((timebuf[0]==' ')?oswald_getwidth_font58_scaled("0",2,3):0); oswald_writestr_font58_scaled(x,y+12,timebuf+((timebuf[0]==' ')?1:0),&w,1,0,2,2); oswald_writestr_font58_scaled(x+w+1,y+12+6,csecbuf,&w,1,0,2,1); } hal_lcd_update_display(); return; } if(mask==stopwm_all) hal_lcd_clear_display(); if(mask&stopwm_icon) { if(mask!=stopwm_all) oswald_draw_rect(36,3,36-1+stopwatch_icon_width,3-1+stopwatch_icon_height,0); oswald_draw_bitmap(36, 3, stopwatch_icon_width, stopwatch_icon_height, 1, stopwatch_icon_bits); } /* Current menu option */ if(mask&stopwm_menu) { oswald_draw_rect(0,95-12,95,95,1); hint=(sdata->item==stopw_startstop)?"Start-Stop": (sdata->item==stopw_split)?"Start-Split": (sdata->item==stopw_reset)?"Reset": "Unknown"; w=oswald_getwidth_font58_scaled(hint,2,1); oswald_writestr_font58_scaled((96-w)/2,95-8,hint,&w,0,1,2,1); } /* Lap number */ if(mask&stopwm_laps) { if(mask!=stopwm_all) oswald_draw_rect(3,3,3+9*3,3+12,0); num2str(sdata->laps,lapbuf,3,3); oswald_writestr_font58_scaled(3,3,lapbuf,&w,1,0,2,2); } /* Split diff /Split diff */ if(mask&stopwm_split) { if(mask!=stopwm_all) oswald_draw_rect(0,28,95,60,0); /* diff */ timerecord2str(&(sdata->lastlap),timebuf,csecbuf); x=8+((timebuf[0]==' ')?oswald_getwidth_font58_scaled("0",2,3):0); oswald_writestr_font58_scaled(x,28,timebuf+((timebuf[0]==' ')?1:0),&w,1,0,2,2); oswald_writestr_font58_scaled(x+w+1,34,csecbuf,&w,1,0,2,1); /* Split */ if(sdata->laps>0) timerecord2str(sdata->lapsdata+((sdata->laps<MAX_LAPS)?sdata->laps-1:MAX_LAPS-1),timebuf,csecbuf); else { mymemcpy(timebuf," 0:00:00",9); mymemcpy(csecbuf,"00",3); } x=8+((timebuf[0]==' ')?oswald_getwidth_font58_scaled("0",2,3):0); oswald_writestr_font58_scaled(x,42,timebuf+((timebuf[0]==' ')?1:0),&w,1,0,2,2); oswald_writestr_font58_scaled(x+w+1,48,csecbuf,&w,1,0,2,1); } /* main display */ if(mask&stopwm_current) { if(mask!=stopwm_all) oswald_draw_rect(0,61,95,95-13,0); timerecord2str(&(sdata->current),timebuf,csecbuf); x=8+((timebuf[0]==' ')?oswald_getwidth_font58_scaled("0",2,3):0); oswald_writestr_font58_scaled(x,61,timebuf+((timebuf[0]==' ')?1:0),&w,1,0,2,3); x+=w; if(sdata->running) csecbuf[1]='\0'; oswald_writestr_font58_scaled(x+1,67,csecbuf,&w,1,0,2,2); } hal_lcd_update_display(); } event_ret_t handle_stop_watch_buttons(watch_button button) { if(stopwatch_screen.showstats) { if(button==BUTTON_F) stopwatch_screen.showstats=0; else if(button==BUTTON_D) return(EVENT_RET_UNHANDLED); draw_stop_watch_screen(&stopwatch_screen,stopwm_all); return(EVENT_RET_HANDLED); } switch (button) { case BUTTON_B: // start/stop if(stopwatch_screen.item==stopw_startstop) { if (stopwatch_screen.running) { hal_disable_centisecond_timer(); stopwatch_screen.running = FALSE; } else { hal_enable_centisecond_timer(); stopwatch_screen.running = TRUE; } draw_stop_watch_screen(&stopwatch_screen,stopwm_current); } else if(stopwatch_screen.item==stopw_split) { if (stopwatch_screen.running) { int l; if(stopwatch_screen.laps>=MAX_LAPS) { mymemmove(stopwatch_screen.lapsdata,stopwatch_screen.lapsdata+1,(MAX_LAPS-1)*sizeof(timerecord)); l=MAX_LAPS-1; } else { l=stopwatch_screen.laps; } stopwatch_screen.laps++; mymemcpy(stopwatch_screen.lapsdata+l,&(stopwatch_screen.current),sizeof(timerecord)); /* update statistics */ if(l>0) timerecord_diff(stopwatch_screen.lapsdata+l,stopwatch_screen.lapsdata+l-1,&(stopwatch_screen.lastlap)); else mymemcpy(&(stopwatch_screen.lastlap),stopwatch_screen.lapsdata+l,sizeof(timerecord)); timerecord_div(stopwatch_screen.lapsdata+l,stopwatch_screen.laps,&(stopwatch_screen.avg)); if(stopwatch_screen.laps==1 || timerecord_cmp(&(stopwatch_screen.lastlap),&(stopwatch_screen.min))<0) mymemcpy(&(stopwatch_screen.min),&(stopwatch_screen.lastlap),sizeof(timerecord)); if(stopwatch_screen.laps==1 || timerecord_cmp(&(stopwatch_screen.lastlap),&(stopwatch_screen.max))>0) mymemcpy(&(stopwatch_screen.max),&(stopwatch_screen.lastlap),sizeof(timerecord)); draw_stop_watch_screen(&stopwatch_screen,stopwm_split|stopwm_laps); } else { hal_enable_centisecond_timer(); stopwatch_screen.running = TRUE; } } else if(stopwatch_screen.item==stopw_reset) { if(stopwatch_screen.running) hal_disable_centisecond_timer(); mymemset(&(stopwatch_screen),0,sizeof(stopwatch_screen)); draw_stop_watch_screen(&stopwatch_screen,stopwm_all); } else { return EVENT_RET_UNHANDLED; } return EVENT_RET_HANDLED; break; case BUTTON_A: stopwatch_screen.item+=stopw_end-1; stopwatch_screen.item%=stopw_end; draw_stop_watch_screen(&stopwatch_screen,stopwm_menu); return EVENT_RET_HANDLED; break; case BUTTON_C: stopwatch_screen.item++; stopwatch_screen.item%=stopw_end; draw_stop_watch_screen(&stopwatch_screen,stopwm_menu); return EVENT_RET_HANDLED; break; case BUTTON_F: stopwatch_screen.showstats=1-stopwatch_screen.showstats; draw_stop_watch_screen(&stopwatch_screen,stopwm_all); return EVENT_RET_HANDLED; break; default: return EVENT_RET_UNHANDLED; break; } return EVENT_RET_UNHANDLED; } event_ret_t stop_watch_handle_events(uint16_t event, void *data) { event_ret_t ret; switch (event) { case EVENT_USER_BUTTONS: dbg_out("button event %d\n", *(int *)data); ret=handle_stop_watch_buttons(*(watch_button *)data); return(ret); break; case EVENT_SCREEN_VISIBLE: draw_stop_watch_screen(&stopwatch_screen,stopwm_all); return EVENT_RET_HANDLED; break; case EVENT_SCREEN_DESTROY: hal_disable_centisecond_timer(); stopwatch_screen.running = FALSE; return EVENT_RET_HANDLED; break; case EVENT_CS_TIMER: stopwatch_screen.current.csec++; if (stopwatch_screen.current.csec > 99) { stopwatch_screen.current.csec = 0; stopwatch_screen.current.sec++; }; if (stopwatch_screen.current.sec > 59) { stopwatch_screen.current.sec = 0; stopwatch_screen.current.min++; }; if (stopwatch_screen.current.min > 59) { stopwatch_screen.current.min = 0; stopwatch_screen.current.hr++; }; if (stopwatch_screen.current.hr > 59) { stopwatch_screen.current.hr = 0; }; if ((stopwatch_screen.current.csec % 10)==0 || stopwatch_screen.running!=TRUE) draw_stop_watch_screen(&stopwatch_screen,stopwm_current); return EVENT_RET_HANDLED; break; default: return EVENT_RET_UNHANDLED; break; }; return EVENT_RET_HANDLED; } /* * Alarm screen, shown when alarm is fired */ void draw_alarm_screen(void) { static char txt[]={"Alarm"}; int w; hal_lcd_clear_display(); oswald_draw_bitmap(36, 20, alarm_icon_width, alarm_icon_height, 1, alarm_icon_bits); w=oswald_getwidth_font58_scaled(txt,1,2); oswald_writestr_font58_scaled((96-1-w)/2+1,49,txt,&w,1,0,1,2); hal_lcd_update_display(); } event_ret_t alarm_handle_events(uint16_t event, void *data) { switch (event) { case EVENT_SCREEN_VISIBLE: draw_alarm_screen(); hal_enable_halfsecond_timer(); hal_vibration_set_state(TRUE); return EVENT_RET_HANDLED; break; case EVENT_SCREEN_DESTROY: hal_disable_halfsecond_timer(); hal_lcd_set_backlight(FALSE); hal_vibration_set_state(FALSE); return EVENT_RET_HANDLED; break; case EVENT_USER_BUTTONS: dbg_out("button event %d\n", *(int *)data); return EVENT_RET_UNHANDLED; break; case EVENT_HALF_SEC_TIMER: hal_lcd_set_backlight(!hal_lcd_get_backlight()); hal_vibration_set_state(!hal_vibration_get_state()); dbg_out("timer\n"); return EVENT_RET_HANDLED; break; default: return EVENT_RET_UNHANDLED; break; }; return EVENT_RET_HANDLED; } /* * Bluetooth setup screen */ typedef struct { uint8_t pos; boolean bt_en; boolean set_mode; boolean on; } bluetooth_data_t; static bluetooth_data_t bluetooth_screen = { 0, FALSE, FALSE, TRUE }; void draw_bluetooth_screen(bluetooth_data_t *sdata) { char bstr[20]; uint8_t *bd_addr; hal_lcd_clear_display(); oswald_draw_bitmap(36, 3, bluetooth_icon_width, bluetooth_icon_height, 1, bluetooth_icon_bits); if (sdata->set_mode) oswald_draw_grid(1,1,94,94,100,100); oswald_write_string(25, 39, FONT_DROID8x12, FALSE, "State"); switch (hal_bluetooth_get_state()) { case BLUETOOTH_OFF: oswald_write_string(53, 39, FONT_DROID8x12, FALSE, "off"); break; case BLUETOOTH_ON: oswald_write_string(53, 39, FONT_DROID8x12, FALSE, "on"); break; case BLUETOOTH_CONNECTED: oswald_write_string(53, 39, FONT_DROID8x12, FALSE, "conn."); break; default: break; }; oswald_write_string(16, 54, FONT_DROID8x12, FALSE, "Enabled"); if ((sdata->pos == 0 && sdata->on) || sdata->pos != 0) { if (bluetooth_screen.bt_en) oswald_draw_bitmap(53, 53, checked_icon_width, checked_icon_height, 1, checked_icon_bits); else oswald_draw_bitmap(53, 53, unchecked_icon_width, unchecked_icon_height, 1, unchecked_icon_bits); } oswald_write_string(18, 69, FONT_DROID8x12, FALSE, "Visible"); if ((sdata->pos == 1 && sdata->on) || sdata->pos != 1) { // oswald_write_character(53, 50, FONT_DROID8x12, hal_bluetooth_get_visible() ? 'x' : '_'); if (hal_bluetooth_get_visible()) oswald_draw_bitmap(53, 68, checked_icon_width, checked_icon_height, 1, checked_icon_bits); else oswald_draw_bitmap(53, 68, unchecked_icon_width, unchecked_icon_height, 1, unchecked_icon_bits); } if (hal_bluetooth_get_state() >= BLUETOOTH_ON) { int i,off,c; bd_addr = hal_bluetooth_get_local_bdaddr(); for(i=5,off=0;i>=0;i--) { if(i!=5) bstr[off++]=':'; c=(bd_addr[i]>>4)&0xf; bstr[off++]=((c<10)?(c+'0'):(c+'a'-10)); c=bd_addr[i]&0xf; bstr[off++]=((c<10)?(c+'0'):(c+'a'-10)); } bstr[off++]='\0'; oswald_write_string(11, 85, FONT_5x7, FALSE, bstr); } hal_lcd_update_display(); } void bluetooth_handle_updown(uint8_t pos, int8_t incr) { switch (pos) { case 0: if (hal_bluetooth_get_state() >= BLUETOOTH_ON) { hal_bluetooth_set_state(BLUETOOTH_OFF); bluetooth_screen.bt_en = FALSE; } else { hal_bluetooth_set_state(BLUETOOTH_ON); bluetooth_screen.bt_en = TRUE; } break; case 1: if (hal_bluetooth_get_state() >= BLUETOOTH_ON && !hal_bluetooth_get_visible()) { hal_bluetooth_set_visible(TRUE); } else { hal_bluetooth_set_visible(FALSE); } break; default: break; }; } event_ret_t handle_bluetooth_buttons(watch_button button, bluetooth_data_t *sdata) { if (bluetooth_screen.set_mode) { switch (button) { case BUTTON_B: bluetooth_handle_updown(sdata->pos, 1); break; case BUTTON_E: bluetooth_handle_updown(sdata->pos, -1); break; case BUTTON_A: sdata->pos=1-sdata->pos; break; case BUTTON_C: sdata->pos=1-sdata->pos; break; case BUTTON_F: bluetooth_screen.set_mode = FALSE; break; default: return EVENT_RET_UNHANDLED; break; } } else { switch (button) { case BUTTON_F: bluetooth_screen.set_mode = TRUE; break; default: return EVENT_RET_UNHANDLED; break; } } draw_bluetooth_screen(sdata); return EVENT_RET_HANDLED; } event_ret_t bluetooth_screen_events(uint16_t event, void *data) { switch (event) { case EVENT_SCREEN_VISIBLE: bluetooth_screen.pos = 0; bluetooth_screen.bt_en = (hal_bluetooth_get_state() > 0); draw_bluetooth_screen(&bluetooth_screen); hal_enable_halfsecond_timer(); break; case EVENT_SCREEN_DESTROY: hal_disable_halfsecond_timer(); break; case EVENT_USER_BUTTONS: dbg_out("button event %d\n", *(int *)data); return handle_bluetooth_buttons(*(watch_button *)data, &bluetooth_screen); break; case EVENT_HALF_SEC_TIMER: if (bluetooth_screen.set_mode) { if (bluetooth_screen.on) bluetooth_screen.on = FALSE; else bluetooth_screen.on = TRUE; } else bluetooth_screen.on = TRUE; draw_bluetooth_screen(&bluetooth_screen); break; default: return EVENT_RET_UNHANDLED; break; }; return EVENT_RET_HANDLED; } /* * Info Screen */ void draw_info_screen(void) { static char OsName[]={"Oswald"}; char *ptr; int w,x; hal_lcd_clear_display(); oswald_draw_bitmap(36, 0, info_icon_width, info_icon_height, 1, info_icon_bits); w=oswald_getwidth_font58_scaled(OsName,1,2); oswald_writestr_font58_scaled((96-w-1)/2,29,OsName,&w,1,0,1,2); w=oswald_getwidth_font58(OSWALD_VERSION); oswald_writestr_font58((96-w-1)/2, 44, OSWALD_VERSION, &w, 1, 0); oswald_write_string(2, 61, FONT_DROID8x12, FALSE, "HAL"); oswald_write_string(35, 61, FONT_DROID8x12, FALSE, (char *)hal_get_version_string()); oswald_write_string(2, 73, FONT_DROID8x12, FALSE, "Build"); oswald_write_string(35, 73, FONT_DROID8x12, FALSE, (char *)hal_get_buildno_string()); oswald_write_string(2, 85, FONT_DROID8x12, FALSE, "Radio"); oswald_write_string(35, 85, FONT_DROID8x12, FALSE, (char *)hal_get_radio_version_string()); hal_lcd_update_display(); } event_ret_t info_screen_handle_events(uint16_t event, void *data) { switch (event) { case EVENT_SCREEN_VISIBLE: draw_info_screen(); return EVENT_RET_HANDLED; break; case EVENT_USER_BUTTONS: dbg_out("button event %d\n", *(int *)data); break; default: return EVENT_RET_UNHANDLED; break; }; return EVENT_RET_UNHANDLED; } /* * Messages Screens */ typedef struct { int8_t pos; // marker position int8_t offset; // offset in msg list } messages_data_t; static messages_data_t messages_screen = { 0, 0, }; typedef struct { uint8_t day; uint8_t month; uint8_t year; // without century, add +1900 uint8_t hour; uint8_t minute; } msg_timedate_t; #define MSG_TYPE_READ 0 #define MSG_TYPE_NEW 1 #define MSG_TYPE_END 127 typedef struct { uint8_t type; msg_timedate_t td; char *msg; } message_t; uint8_t Msgs = 15; message_t Msg[] = { { MSG_TYPE_READ, {9,5,113,0,38}, "Testmessage with more text than fits into the menu." }, { MSG_TYPE_NEW, {9,5,113,0,39}, "Sitting in the train waiting to arrive." }, { MSG_TYPE_READ, {9,5,113,0,40}, "People in the train are annoying!" }, { MSG_TYPE_READ, {9,5,113,0,40}, "Auch auf Deutsch geht das hier und Text können lang sein." }, { MSG_TYPE_NEW, {8,5,113,0,40}, "Und hier noch eine neue Nachricht, die nun wirklich lang ist, laenger als die anderen." }, { MSG_TYPE_READ, {9,5,113,0,38}, "Testmessage with more text than fits into the menu." }, { MSG_TYPE_NEW, {9,5,113,0,39}, "Sitting in the train waiting to arrive." }, { MSG_TYPE_READ, {9,5,113,0,40}, "People in the train are annoying!" }, { MSG_TYPE_READ, {9,5,113,0,40}, "Auch auf Deutsch geht das hier und Text können lang sein." }, { MSG_TYPE_NEW, {8,5,113,0,40}, "Und hier noch eine neue Nachricht, die nun wirklich lang ist, laenger als die anderen." }, { MSG_TYPE_READ, {9,5,113,0,38}, "Testmessage with more text than fits into the menu." }, { MSG_TYPE_NEW, {9,5,113,0,39}, "Sitting in the train waiting to arrive." }, { MSG_TYPE_READ, {9,5,113,0,40}, "People in the train are annoying!" }, { MSG_TYPE_READ, {9,5,113,0,40}, "Auch auf Deutsch geht das hier und Text können lang sein." }, { MSG_TYPE_NEW, {8,5,113,0,40}, "Und hier noch eine neue Nachricht, die nun wirklich lang ist, laenger als die anderen." }, { MSG_TYPE_END, {0,0,0,0,0}, "Exit" }, }; static void msginfo2str(message_t *msginfo,char *dstr, int dstrsize) { int off=0; dstr[off++]=(msginfo->type == MSG_TYPE_NEW) ? '*':' '; dstr[off++]=' '; off+=num2str(msginfo->td.day,dstr+off,2,2); dstr[off++]='.'; off+=num2str(msginfo->td.month,dstr+off,2,2); dstr[off++]='.'; off+=num2str(msginfo->td.year+1900,dstr+off,4,4); dstr[off++]=' '; off+=num2str(msginfo->td.hour,dstr+off,2,2); dstr[off++]=':'; off+=num2str(msginfo->td.minute,dstr+off,2,2); dstr[off++]='\0'; } void draw_message_screen(messages_data_t *sdata) { char dstr[32]; int off; uint8_t strpos, msglen; uint8_t line; hal_lcd_clear_display(); #if 0 oswald_draw_bitmap(81, 6, upbutton_icon_width, upbutton_icon_height, upbutton_icon_bits); oswald_draw_bitmap(81, 38, downbutton_icon_width, downbutton_icon_height, downbutton_icon_bits); #endif oswald_draw_bitmap(81, 70, enterbutton_icon_width, enterbutton_icon_height, 1, enterbutton_icon_bits); Msg[sdata->offset + sdata->pos].type = MSG_TYPE_READ; off=0; dstr[off++]='#'; off+=num2str(sdata->pos + sdata->offset + 1,dstr+off,1,sizeof(dstr)-2-off); dstr[off++]='/'; off+=num2str(Msgs,dstr+off,1,sizeof(dstr)-1-off); dstr[off++]='\0'; oswald_write_string(30, 0, FONT_5x7, FALSE, dstr); oswald_draw_line(0,7,95,7, 1); // here goes the text msglen = strlen(Msg[sdata->pos+sdata->offset].msg); strpos=0; line=0; while ((strpos < msglen) && (line < 6)) { strpos += oswald_write_string_length(4, 9+(line*12), 84, FONT_DROID8x12, FALSE, &Msg[sdata->pos+sdata->offset].msg[strpos]); line++; } oswald_draw_line(0,87,95,87, 1); if (Msg[sdata->offset + sdata->pos].type != MSG_TYPE_END) { msginfo2str(Msg+sdata->pos+sdata->offset,dstr,sizeof(dstr)); oswald_write_string(2, 89, FONT_5x7, FALSE, dstr); } hal_lcd_update_display(); } event_ret_t handle_message_screen_buttons(watch_button button, messages_data_t *sdata) { switch (button) { case BUTTON_A: break; case BUTTON_B: return EVENT_RET_HANDLED; break; case BUTTON_C: // OswaldState.screen->event_func(EVENT_SCREEN_DESTROY, NULL); OswaldState.screen_id = MESSAGES_SCREEN; OswaldState.screen = &OswaldScreens[OswaldState.screen_id]; OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL); return EVENT_RET_HANDLED; break; default: break; } return EVENT_RET_UNHANDLED; } event_ret_t message_screen_handle_events(uint16_t event, void *data) { switch (event) { case EVENT_SCREEN_VISIBLE: draw_message_screen(&messages_screen); return EVENT_RET_HANDLED; break; case EVENT_USER_BUTTONS: dbg_out("button event %d\n", *(int *)data); return handle_message_screen_buttons(*(watch_button *)data, &messages_screen); break; default: break; }; return EVENT_RET_UNHANDLED; } void draw_messages_screen(messages_data_t *sdata) { int w; static char msg[]={"(disabled)"}; hal_lcd_clear_display(); oswald_draw_bitmap(36, 3, message_icon_width, message_icon_height, 1, message_icon_bits); w=oswald_getwidth_font58(msg); oswald_write_string((96-w-1)/2, 44, FONT_5x8, FALSE, msg); hal_lcd_update_display(); #if 0 char dstr[32]; int off; int i; hal_lcd_clear_display(); // oswald_draw_bitmap(36, 0, message_icon_width, message_icon_height, message_icon_bits); oswald_draw_bitmap(81, 6, upbutton_icon_width, upbutton_icon_height, upbutton_icon_bits); oswald_draw_bitmap(81, 38, downbutton_icon_width, downbutton_icon_height, downbutton_icon_bits); oswald_draw_bitmap(81, 70, enterbutton_icon_width, enterbutton_icon_height, enterbutton_icon_bits); if (Msg[sdata->offset + sdata->pos].type != MSG_TYPE_END) { off=0; dstr[off++]='#'; off+=num2str(sdata->pos + sdata->offset + 1,dstr+off,2,2); dstr[off++]='/'; off+=num2str(Msgs,dstr+off,2,2); dstr[off++]='\0'; oswald_write_string(30, 0, FONT_5x7, FALSE, dstr); } oswald_draw_line(0,7,95,7); for (i=0; i<6; i++) { if (Msg[i+sdata->offset].type != MSG_TYPE_END) { //oswald_write_string_length(4, 9+(i*12), 84, FONT_DROID8x12, (Msg[i+sdata->offset].type == MSG_TYPE_NEW), Msg[i+sdata->offset].msg); if (Msg[i+sdata->offset].type == MSG_TYPE_NEW) oswald_write_string_length(4, 9+(i*12), 84, FONT_DROID8x12b, (i+sdata->offset) == (sdata->offset + sdata->pos), Msg[i+sdata->offset].msg); else oswald_write_string_length(4, 9+(i*12), 84, FONT_DROID8x12, (i+sdata->offset) == (sdata->offset + sdata->pos), Msg[i+sdata->offset].msg); } else { oswald_draw_bitmap(0, 66, leftbutton_icon_width, leftbutton_icon_height, leftbutton_icon_bits); } } // marker selected msg oswald_draw_line(1,9,1,81); oswald_draw_line_ww(1,10+(sdata->pos*12),1,20+(sdata->pos*12),2); oswald_draw_line(0,87,95,87); if (Msg[sdata->offset + sdata->pos].type != MSG_TYPE_END) { msginfo2str(Msg+sdata->pos+sdata->offset,dstr,sizeof(dstr)); oswald_write_string(2, 89, FONT_5x7, FALSE, dstr); } hal_lcd_update_display(); #endif } event_ret_t handle_messages_screen_buttons(watch_button button, messages_data_t *sdata) { #if 0 switch (button) { case BUTTON_A: sdata->pos--; if (sdata->pos < 0) { if (sdata->offset > 0) { sdata->pos = 0; sdata->offset--; if (sdata->offset < 0) sdata->offset = 0; } else { sdata->pos = 5; sdata->offset = (Msgs - 5); if (sdata->offset < 0) sdata->offset = 0; } } draw_messages_screen(&messages_screen); return EVENT_RET_HANDLED; break; case BUTTON_B: sdata->pos++; if (sdata->pos > 5) { sdata->pos = 5; sdata->offset++; if ((sdata->offset + 5) > Msgs) { sdata->offset = 0; sdata->pos = 0; } } draw_messages_screen(&messages_screen); return EVENT_RET_HANDLED; break; case BUTTON_C: if (Msg[sdata->offset + sdata->pos].type == MSG_TYPE_END) return EVENT_RET_UNHANDLED; else // OswaldState.screen->event_func(EVENT_SCREEN_DESTROY, NULL); OswaldState.screen_id = MESSAGE_SCREEN; OswaldState.screen = &OswaldScreens[OswaldState.screen_id]; OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL); return EVENT_RET_HANDLED; break; default: break; } #endif return EVENT_RET_UNHANDLED; } event_ret_t messages_screen_handle_events(uint16_t event, void *data) { switch (event) { case EVENT_SCREEN_VISIBLE: // messages_screen.pos = 0; // messages_screen.offset = 0; draw_messages_screen(&messages_screen); return EVENT_RET_HANDLED; break; case EVENT_USER_BUTTONS: dbg_out("button event %d\n", *(int *)data); return handle_messages_screen_buttons(*(watch_button *)data, &messages_screen); break; default: break; }; return EVENT_RET_UNHANDLED; } /* calendar overview */ typedef struct { int16_t off; } calendar_overview_data_t; static calendar_overview_data_t calendar_overview_screen = { 0 }; void draw_calendar_overview_screen(int off) { static const char wnames[]={"MTWTFSS"}; static const char *mnames[]={"January","February","March","April","May","June","July","August","September","October","November","December"}; int i,w,val,x,y; int yday,k; int *yeardays; int l; int month,year; unsigned char time[6]; /* shift the month by the amount on "off" */ month=OswaldClk.month+off; year=OswaldClk.year; if(month<=0) { while(month<=0) { year--; month+=12; } } else { year+=((month-1)/12); month=((month-1)%12)+1; } /* do the actual drawing */ hal_lcd_clear_display(); oswald_draw_rect(2,0,93,17,1); oswald_draw_grid(2,17,93,95,13,13); for(i=0;i<7;i++) oswald_draw_font58(6+13*i, 30-9-13+2, wnames[i],&w,0,1); oswald_write_number(4,0, FONT_6x9, 1, year); w=4*5; oswald_write_string(4+w+5,0, FONT_6x9, 1, (char *) mnames[(month+11)%12]); val = OswaldClk.hour; if(!OswaldClk.clk24hr) val=(val>12)?val-12:val; time[0]=(val/10)+'0'; time[1]=(val%10)+'0'; time[2]=':'; time[3]=(OswaldClk.minute/10)+'0'; time[4]=(OswaldClk.minute%10)+'0'; time[5]='\0'; oswald_write_string(93-23,0, FONT_6x9, 1, (char *) time); mday2yday(year,month,1,&yday); yday2wday(year,yday,&k); k=(k+6)%7; /* wday for Monday is 1, we want it to be 0 */ yeardays=GetYearDays(year); l=yeardays[month]-yeardays[month-1]; for(i=1;i<=l;i++) { x=2+2+((i+k-1)%7)*13; y=17+2+((i+k-1)/7)*13; if(off==0 && i==OswaldClk.day) { oswald_draw_grid(x-3,y-3,x+13+1-3+1,y+13+1-3+1,15,15); oswald_draw_grid(x-3+1,y-3+1,x+13+1-3,y+13+1-3,15,15); } if(i<10) { oswald_draw_tinydigit(x,y,i+'0',&w,1,0); } else { oswald_draw_tinydigit(x,y,i/10+'0',&w,1,0); oswald_draw_tinydigit(x+w,y,i%10+'0',&w,1,0); } } hal_lcd_update_display(); } event_ret_t handle_calendar_overview_buttons(watch_button button, calendar_overview_data_t *calendar_overview_screen) { switch (button) { case BUTTON_A: case BUTTON_C: calendar_overview_screen->off+=((button==BUTTON_C)?1:-1); draw_calendar_overview_screen(calendar_overview_screen->off); return EVENT_RET_HANDLED; break; case BUTTON_B: OswaldState.screen_id = IDLE_SCREEN; OswaldState.screen = &OswaldScreens[OswaldState.screen_id]; OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL); return EVENT_RET_HANDLED; break; default: break; } return EVENT_RET_UNHANDLED; } event_ret_t calendar_overview_handle_events(uint16_t event, void *data) { switch (event) { case EVENT_SCREEN_VISIBLE: calendar_overview_screen.off=0; draw_calendar_overview_screen(0); return EVENT_RET_HANDLED; break; case EVENT_USER_BUTTONS: dbg_out("button event %d\n", *(int *)data); return handle_calendar_overview_buttons(*(watch_button *)data, &calendar_overview_screen); break; case EVENT_ONE_SEC_TIMER: return EVENT_RET_HANDLED; default: break; }; return EVENT_RET_UNHANDLED; }