/*
nClock for TI-Nspire
Copyright (C) 2014 Levak
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 3 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, see .
*/
#include
#include "charmap_8x12.h"
typedef unsigned long int uint32_t;
#define HOOK_PATH "/nclock.hook"
#define CONTRAST_MIN 0x6B
#define CONTRASTOFF_MIN 0x60
unsigned char* SCREEN_BASE_ADDRESS = 0;
#define SCREEN_BYTES_SIZE (has_colors?SCREEN_WIDTH*SCREEN_HEIGHT*2:SCREEN_WIDTH*SCREEN_HEIGHT/2)
static char * config_path = "/documents/config.nclock";
static char * title = "nClock says";
static int revertedScreen = 0;
unsigned char screen_type = SCR_320x240_565;
static const unsigned read_nand_31_addrs[] = { 0x10071F5C, 0x10071EC4, 0x10071658, 0X100715E8, 0x1006E0AC, 0x1006E03C};
#define read_nand_31 SYSCALL_CUSTOM(read_nand_31_addrs ,void, void* dest, int size, int offset, int, int percent_max, void *progress_cb )
void bc_read_nand(void* dest, int size, int offset, int unknown, int percent_max, void *progress_cb) {
if (nl_ndless_rev() < 989) // Ndless 3.1
read_nand_31(dest, size, offset, unknown, percent_max, progress_cb);
else
read_nand(dest, size, offset, unknown, percent_max, progress_cb);
}
uint8_t getScreenType() {
if(has_colors) {
if(nl_ndless_rev() >= 2004) { // Ndless 4.2+
if(lcd_type()==SCR_240x320_565)
return SCR_240x320_565;
}
else { // Ndless < 4.2
char f=0;
bc_read_nand(&f, 1, 0x81d, 0, 0, 0);
if(f==1)
return SCR_240x320_565;
}
}
return SCR_320x240_565;
}
inline void setPixel(int x, int y, int r, int g, int b) {
if(revertedScreen)
x = 319-x, y = 239-y;
if(has_colors) { /* Inverted color for CX since it is ugly and sharpen */
unsigned short* p;
if(screen_type==SCR_320x240_565)
p = (unsigned short*)(SCREEN_BASE_ADDRESS + (x << 1) + (y << 9) + (y << 7));
else
p = (unsigned short*)(SCREEN_BASE_ADDRESS + (y << 1) + (x << 8) + (x << 7) + (x << 6) + (x << 5));
*p = (((255-r) >> 3) << 11) | (((255-g) >> 2) << 5) | ((255-b) >> 3);
}
else {
unsigned char* p = (unsigned char*)(SCREEN_BASE_ADDRESS + ((x >> 1) + (y << 7) + (y << 5)));
char c = (r>>4)/3 + (g>>4)/3 + (b>>4)/3;
*p = (x & 1) ? ((*p & 0xF0) | c) : ((*p & 0x0F) | (c << 4));
}
}
void clrscr(void) {
if(lcd_isincolor())
memset(SCREEN_BASE_ADDRESS, 0x00, SCREEN_BYTES_SIZE);
else
memset(SCREEN_BASE_ADDRESS, 0xFF, SCREEN_BYTES_SIZE);
}
void putChar(int x, int y, char ch, int c, int size)
{
int i, j, k1, k2;
for(i = 0; i < CHAR_HEIGHT; ++i) {
unsigned char code = charMap_ascii[(unsigned char)ch][i];
for(j = CHAR_WIDTH; j >= 0; --j, code >>= 1)
if (code & 1)
for(k1 = 0; k1 < size; ++k1)
for(k2 = 0; k2 < size; ++k2)
setPixel(x+j*size + k1, y+i*size + k2, 0, c, c);
}
}
void fillRect(int x1, int y1, int x2, int y2, int c) {
for(; x1 < x2; ++x1)
for(; y1 < y2; ++y1)
setPixel(x1, y1, c, c, c);
}
void drawRect(int x, int y, int w, int h, int s, int c) {
int i, j;
for(i = x; i < x+w; ++i)
for(j = y; j < y+s; ++j)
setPixel(i, j, c, c, c);
for(i = x; i < x+w; ++i)
for(j = y+h; j > y+h-s; --j)
setPixel(i, j, c, c, c);
for(j = y; j < y+h; ++j)
for(i = x; i < x+s; ++i)
setPixel(i, j, c, c, c);
for(j = y; j < y+h; ++j)
for(i = x+w; i > x+w-s; --i)
setPixel(i, j, c, c, c);
}
void drawString(char * string, int xpos, int ypos, int c, int size) {
int i;
for(i = 0; string[i]; ++i)
putChar(xpos+(CHAR_WIDTH+2)*size*i, ypos, string[i], c, size);
}
void drawBase(int x, int y) {
int c = 0;
setPixel(x, y, c, c, c);
setPixel(x+1, y, c, c, c);
setPixel(x+2, y, c, c, c);
setPixel(x, y+1, c, c, c);
setPixel(x+2, y+1, c, c, c);
setPixel(x, y+2, c, c, c);
setPixel(x+2, y+2, c, c, c);
setPixel(x, y+3, c, c, c);
}
void drawM(int x, int y) {
drawBase(x, y);
setPixel(x+2, y+3, 0, 0, 0);
}
void drawP(int x, int y) {
drawBase(x, y);
setPixel(x+1, y+2, 0, 0, 0);
}
void drawA(int x, int y) {
drawP(x, y);
setPixel(x+2, y+3, 0, 0, 0);
}
void drawSegment(int x, int y, int x1, int y1, int x2, int y2, int size, int c[3]) {
int i, j;
for(i = x+x1*size; i < x+(x2+1)*size; ++i)
for(j = y+y1*size; j < y+(y2+1)*size; ++j)
setPixel(i, j, c[0], c[1], c[2]);
}
char segments[] = {119, 18, 61, 59, 90, 107, 111, 50, 127, 123};
void sevenSegments(int x, int y, int n, int size, int c) {
char code = segments[n];
int color[] = {0, c, c};
int grey[] = {200, 200, 200};
drawSegment(x, y, 1, 9, 3, 9, size, (code&1)?color:grey);
code >>= 1;
drawSegment(x, y, 4, 5, 4, 8, size, (code&1)?color:grey);
code >>= 1;
drawSegment(x, y, 0, 5, 0, 8, size, (code&1)?color:grey);
code >>= 1;
drawSegment(x, y, 1, 4, 3, 4, size, (code&1)?color:grey);
code >>= 1;
drawSegment(x, y, 4, 1, 4, 3, size, (code&1)?color:grey);
code >>= 1;
drawSegment(x, y, 1, 0, 3, 0, size, (code&1)?color:grey);
code >>= 1;
drawSegment(x, y, 0, 1, 0, 3, size, (code&1)?color:grey);
}
static unsigned int* p_contrast = (unsigned int*) 0x900F0020;
static unsigned int contrast;
inline int isScreenOn() {
return *p_contrast > CONTRASTOFF_MIN;
}
int fadeStep(int step, int to) {
int sens = step < 0;
if((sens && (int)*p_contrast + step > to)
|| (!sens && (int)*p_contrast + step < to)) {
*p_contrast = *p_contrast + step;
return step;
}
else {
*p_contrast = to;
return 0;
}
}
/* Non-bloking fadeDown of the backlight */
int fadeDown(unsigned step) {
printf("%x fadeDown\n", *p_contrast);
int to = CONTRAST_MIN;
return fadeStep(-step, to);
}
/* Non-bloking fadeUp of the backlight */
int fadeUp(unsigned step) {
printf("%x fadeUp\n", *p_contrast);
return fadeStep(step, contrast);
}
void fadeFunc(int (*fade)(unsigned), int ms) {
int div = 4;
int minimum = CONTRAST_MIN;
int diff = (contrast - minimum);
printf("contrast: %x\nmin: %x\ndiff: %d\n", contrast, minimum, diff);
if(diff) {
int inc = (diff >> div) ? (diff >> div) : (diff > 0) ? 1 : -1;
int waittime = ms / (1 << div);
printf("ms: %d\nwait : %d\n", ms, waittime);
while(fade(inc))
sleep(waittime);
}
}
/* Blocking fadeOut of the backlight */
inline void fadeOut(int ms) {
fadeFunc(fadeDown, ms);
}
/* Blocking fadeIn of the backlight */
inline void fadeIn(int ms) {
fadeFunc(fadeUp, ms);
}
inline void resetCalc(void) {
*((int*)0x900A0008) = 2; /* reboot the calc */
}
inline int fwrite_long(unsigned long n, FILE * f) {
return fprintf(f, "%ld ", n);
}
inline int fwrite_long_ptr(unsigned long * p, FILE * f) {
fwrite_long((unsigned long)p, f);
return fwrite_long(*p, f);
}
unsigned long fread_long(FILE * f) {
if(f) {
char c[1];
unsigned long n = 0;
while(fread(c, 1, sizeof(char), f) && *c != ' ' && *c != '\n' && *c != '\t') {
n = n*10 + (*c - '0');
}
return n;
}
else
return 0;
}
void fread_long_ptr(unsigned long ** pp, FILE * f) {
unsigned long * p = (unsigned long *) fread_long(f);
unsigned long v = fread_long(f);
if(!*pp) *pp = p;
else **pp = v;
//printf("%x %x %d\n", pp, *pp, **pp);
}
unsigned long * const p_RTC = (unsigned long*)0x90090000;
/* Shared data between hook and nclock */
static unsigned long * display12hrs = 0;
static unsigned long * displaySecs = 0;
static unsigned long * countdown = 0;
static unsigned long * mini_clock_posx = 0;
static unsigned long * mini_clock_posy = 0;
static unsigned long * resetCalcOnCountdown = 0;
static unsigned long * checkTimeOnStartup = 0;
static unsigned long * noMiniClock = 0;
static unsigned long timeout = 180;
int writeConfig(char * config_path) {
FILE * config = fopen(config_path, "w");
if(!config)
return 0;
fwrite_long(*p_RTC, config);
fwrite_long_ptr(display12hrs, config);
fwrite_long_ptr(displaySecs, config);
fwrite_long_ptr(countdown, config);
fwrite_long_ptr(mini_clock_posx, config);
fwrite_long_ptr(mini_clock_posy, config);
fwrite_long_ptr(resetCalcOnCountdown, config);
fwrite_long_ptr(checkTimeOnStartup, config);
fwrite_long_ptr(noMiniClock, config);
fwrite_long(timeout, config);
//printf("wrote %d %x(%d) %x(%d) %x(%d) %x(%d)\n", *p_RTC, display12hrs, *display12hrs, displaySecs, *displaySecs, countdown, *countdown, resetCalcOnCountdown, *resetCalcOnCountdown);
fclose(config);
return 1;
}
int readConfig(unsigned long * ts, char * config_path) {
FILE * config = fopen(config_path, "r");
if(!config)
return 0;
unsigned long t = fread_long(config);
if(ts) *ts = t;
fread_long_ptr(&display12hrs, config);
fread_long_ptr(&displaySecs, config);
fread_long_ptr(&countdown, config);
if(*countdown < *p_RTC)
*countdown = 0;
fread_long_ptr(&mini_clock_posx, config);
fread_long_ptr(&mini_clock_posy, config);
*mini_clock_posx%=320;
*mini_clock_posy%=240;
fread_long_ptr(&resetCalcOnCountdown, config);
fread_long_ptr(&checkTimeOnStartup, config);
fread_long_ptr(&noMiniClock, config);
t = fread_long(config);
if(t) timeout = t;
//printf("read %d %x(%d) %x(%d) %x(%d) %x(%d)\n", *p_RTC, display12hrs, *display12hrs, displaySecs, *displaySecs, countdown, *countdown, resetCalcOnCountdown, *resetCalcOnCountdown);
fclose(config);
return 1;
}
void malloc_shared_data(void) {
display12hrs = malloc(sizeof(unsigned long));
*display12hrs = 0;
displaySecs = malloc(sizeof(unsigned long));
*displaySecs = 0;
countdown = malloc(sizeof(unsigned long));
*countdown = 0;
mini_clock_posx = malloc(sizeof(unsigned long));
*mini_clock_posx = 210;
mini_clock_posy = malloc(sizeof(unsigned long));
*mini_clock_posy = 0;
resetCalcOnCountdown = malloc(sizeof(unsigned long));
*resetCalcOnCountdown = 0;
checkTimeOnStartup = malloc(sizeof(unsigned long));
*checkTimeOnStartup = 0;
noMiniClock = malloc(sizeof(unsigned long));
*noMiniClock = 0;
}
void free_shared_data(void) {
free(display12hrs);
free(displaySecs);
free(countdown);
free(mini_clock_posx);
free(mini_clock_posy);
free(resetCalcOnCountdown);
free(checkTimeOnStartup);
free(noMiniClock);
}
int maxDayInMonth(unsigned month, int leap) {
if(month == 4 || month == 6 || month == 9 || month == 11)
return 30;
else
if(month == 2)
return (leap) ? 29 : 28;
else
return 31;
}
/* Optimized LeapYear Detection (with optimized modulos) */
int isLeapYear(unsigned year) {
if(!(year&3)) { // mod 4 = 0
int r2= (year>>2)/25; // century-1
int r3= (r2>>1)/5; // millennium-1
r2= year - ((r2*25) << 2); // 2-digits year
r3= year - ((r3*125) << 3); // 3-digits year
if(!r3 || (!(r2&3) && r2))
return 1;
else
return 0;
}
else
return 0;
}
static const double nb_days_in_year = 5113/14.;
unsigned long time2timestamp(int hr, int min, int sec) {
return sec + min*60 + hr*3600;
}
unsigned long date2timestamp(int year, int month, int day, int hr, int min, int sec) {
int cyear;
sec = time2timestamp(hr, min, sec);
int ly = isLeapYear(year);
for(--month; month > 0; --month)
day += maxDayInMonth(month, ly);
day--;
day+=365*(year-1970);
for(cyear=1972;cyear> 2) / 15;
*sec = t - ((tt * 15) << 2);
t = tt;
tt = (tt >> 2) / 15;
*min = t - ((tt * 15) << 2);
t = tt;
tt = (tt >> 3) / 3;
*hr = t - ((tt * 3) << 3);
return tt;
}
void timestamp2day(unsigned long t, int * year, int * month, int * day) {
*year=1970;
int leap_year=0;
while(t>=365+leap_year) {
(*year)++;
t-=365+leap_year;
leap_year=isLeapYear(*year);
}
*month = 1;
int max_day = maxDayInMonth(*month, leap_year);
while(t >= max_day) {
t -= max_day;
++*month;
max_day = maxDayInMonth(*month, leap_year);
}
*day=t+1;
}
void timestamp2date(unsigned long t, int * year, int * month, int * day, int * hr, int * min, int * sec) {
t = timestamp2time(t, hr, min, sec);
timestamp2day(t, year, month, day);
}
/* Fully optimized clock that only displays current time
* It runs only when the calc is ON (notice that the hook remains
* even if the calc is OFF) */
void mini_nclock(int clearBg) {
/* if calc is ON (screen not OFF) */
int blink = 0;
int blink2 = 0;
int screen_on = isScreenOn();
unsigned long t = 0;
if (*countdown || screen_on)
{
t = *p_RTC;
if(*countdown > t)
t = *countdown - t;
}
/* if we are in countdown mode */
if(*countdown) {
if(*countdown > t) {
blink = (t < 60) ? (t&1)*255 : 0;
blink2 = (t > 60 && t < 300) ? (t&1)*255 : 0;
}
else {
//*p_contrast = contrast;
//*((int*)0x900B0008) = 0xFFFFFFFF;
//*((short*)0x900B0004) = 0x2000;
//fadeIn(0);
*countdown = 0;
char * s = "Time's over !";
int i, j;
for(i = 60; i < 260; ++i)
for(j = 60; j < 180; ++j)
setPixel(i, j, 255, 255, 255);
for(i = 0; i < 30 && s[i]; ++i)
putChar(100+(CHAR_WIDTH+2)*i, 115, s[i], 0, 1);
if(*resetCalcOnCountdown)
resetCalc();
}
}
if(screen_on) {
int hr, min, sec;
timestamp2time(t, &hr, &min, &sec);
int offset = (*display12hrs && !*countdown) ? 5: 0;
int xpos = *mini_clock_posx - offset;
int xend = xpos + offset + ((*displaySecs) ? 49 : 31);
int ypos = *mini_clock_posy;
int yend = ypos + 12;
int i, j;
if(clearBg&1)
for(i = xpos; i < xend; ++i)
for(j = ypos; j < yend; ++j)
setPixel(i, j, 255, 255, 255);
else if(clearBg&2) {
int bx = (xpos > 2) ? xpos-2 : (xpos > 1) ? xpos-1 : xpos;
int by = (ypos > 2) ? ypos-2 : (ypos > 1) ? ypos-1 : ypos;
for(i = bx; i < xend+2 && i < 320; ++i)
for(j = by; j < yend+2 && j < 240; ++j)
setPixel(i, j, 255, 255, 255);
}
if(ypos-1 > 0)
for(i = xpos; i < xend; ++i)
setPixel(i, ypos-1, 50, 50, 50);
if(yend < 240)
for(i = xpos; i < xend; ++i)
setPixel(i, yend, 50, 50, 50);
if(xpos - 1 > 0)
for(j = ypos; j < yend; ++j)
setPixel(xpos - 1, j, 50, 50, 50);
if(xend < 320)
for(j = ypos; j < yend; ++j)
setPixel(xend, j, 50, 50, 50);
if(*display12hrs && !*countdown) {
if(hr >= 12) {
if(hr > 12)
hr -= 12;
drawP(xpos + 1, ypos + 1);
}
else
drawA(xpos + 1, ypos + 1);
drawM(xpos + 1, ypos + 6);
}
xpos += offset;
ypos++;
xpos++;
sevenSegments(xpos, ypos, hr/10, 1, blink);
sevenSegments(xpos+7, ypos, hr%10, 1, blink);
putChar(xpos+8, ypos, ':', blink2, 1);
sevenSegments(xpos+17, ypos, min/10, 1, blink);
sevenSegments(xpos+24, ypos, min%10, 1, blink);
if(*displaySecs) {
putChar(xpos+26, ypos, ':', blink2, 1);
sevenSegments(xpos+34, ypos, sec/10, 1, blink);
sevenSegments(xpos+41, ypos, sec%10, 1, blink);
}
}
}
// CX/CM :
// search : 30 10 9F E5 04 E0 2D E5 34 D0 4D E2 2C 30 8D E2 00 00 A0 E3 04 10 8D E5 28 30 8D E5 08 00 8D E5 70 FE FF EB 04 10 8D E2
// mask : FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF F0 FF FF FF FF FF FF FF
static const int hook_addrs[] = {0x100B66C8, 0x100B6988, // Clickpad / Touchpad 3.1
0x100EAAAC, 0x100EADC4, // CX 3.1
0x100E72CC, 0x100E75E4, // CM 3.1
0x101122b8, 0x100eb288, // Clickpad / Touchpad 3.6
0x10111cfc, 0x1011201C, // CX 3.6
0x101184C0, 0, // ClickPad / Touchpad 3.9.0
0, 0, // ClickPad / Touchpad 3.9.1
0x10117EF8, 0x10117D24, // CX 3.9.0
0x10118684, 0x101184B0, // CX 3.9.1 - low miniclock refresh rate issue, new hook location needed
0, 0, // CX 4.0.0
0x1011EF68, 0x1011EDB8, // CX 4.0.3 - low miniclock refresh rate
0x10122E04, 0x10122C5C, // CX 4.2 - non working hook anymore
0x10127CD0, 0x10127B28, // CX 4.3 - non working hook anymore
0x1012AB14, 0x1012A960, // CX 4.4 - non working hook anymore
0x1012C4BC, 0x1012C354, // CX 4.5 - non working hook anymore
0x1012C9A4, 0x1012C8C8, // CX 4.5.1 - non working hook anymore
};
static uint32_t lcd_mirror_ptr[] = {
0, 0, 0, 0, 0, 0, // classic 3.1 + CX 3.1 + CM 3.1
0, 0, 0, 0, // classic 3.6 + CX 3.6
0, 0, 0, 0, // classic 3.9.0 + classic 3.9.1
0, 0, 0, 0, // CX 3.9.0 + CX 3.9.1
0, 0, // CX 4.0.0
0, 0, // CX 4.0.3
0x110ED6D4, 0x111516D4, // CX 4.2
0x110FD6DC, 0x111616DC, // CX 4.3
0x113356DC, 0x113996DC, // CX 4.4
0x113496E4, 0x113B16E4, // CX 4.5
0, 0, // CX 4.5.1
};
#define HOOK_ADDR (nl_osvalue((int*)hook_addrs, sizeof(hook_addrs)/sizeof(hook_addrs[0])))
HOOK_DEFINE(hook_nclock) {
if(!*noMiniClock)
mini_nclock(1);
HOOK_RESTORE_RETURN(hook_nclock);
}
static short year_codes[] = {6, 4, 2, 0};
static short month_codes[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
static char months[][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
static char weekdays[][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
// for example, see http://blog.artofmemory.com/how-to-calculate-the-day-of-the-week-4203.html
char * getWeekDay(int year, int month, int day) {
int d = (year>>2)/25; // century
int r = year - (d<<2)*25; // year with 2 digits
// (Century Code + Year Code + Month Code + Date Number – Leap Year Code) mod 7
return weekdays[(year_codes[d&3] // century code
+ r + (r>>2) // year code
+ month_codes[month-1] // month code
+ day // date number (day of month)
- (((month<=2)&&isLeapYear(year))?1:0) // leap year code
) % 7];
}
void display(int xpos, int ypos, int size, int blink1, int blink2, int hr, int min, int sec) {
int sizeSeg = size * 7;
sevenSegments(xpos, ypos, hr/10, size, blink1);
sevenSegments(xpos+sizeSeg, ypos, hr%10, size, blink1);
putChar(xpos+sizeSeg*2-size*2, ypos+sizeSeg/2, ':', blink2, 1);
sevenSegments(xpos+sizeSeg*3-size*4, ypos, min/10, size, blink1);
sevenSegments(xpos+sizeSeg*4-size*4, ypos, min%10, size, blink1);
putChar(xpos+sizeSeg*5-size*6, ypos+sizeSeg/2, ':', blink2, 1);
sevenSegments(xpos+sizeSeg*6-size*8, ypos, sec/10, size, blink1);
sevenSegments(xpos+sizeSeg*7-size*8, ypos, sec%10, size, blink1);
}
int nclock(int forcedDisplay) {
int dayChanged = 0;
int xpos = 45;
int ypos = 100;
int size = 4;
if(*countdown) {
unsigned long t = *p_RTC;
if(*countdown > t) {
t = *countdown - t;
int hr, min, sec;
if(t > 86400 && forcedDisplay) {
int day, month, year;
timestamp2date(t, &year, &month, &day, &hr, &min, &sec);
char s_CD[50];
year -= 1970; /* timestamp2date adds 1970 */
--month; /* 1 indexed mounths */
if(year > 0)
sprintf(s_CD, "%d year(s) %d month(s) %d day(s)", year, month, day);
else if(month > 0)
sprintf(s_CD, "%d month(s) %d day(s)", month, day);
else
sprintf(s_CD, "%d day(s)", day);
drawString(s_CD, (320-strlen(s_CD)*(CHAR_WIDTH+2))>>1, ypos-13, 0, 1);
}
timestamp2time(t, &hr, &min, &sec);
if(isScreenOn()) {
char * str_desc = (*resetCalcOnCountdown) ? "Reset in:" : "Countdown:";
drawString(str_desc, xpos+65, ypos-60, 0, 1);
int blink = (t < 60) ? (t&1)*255 : 0;
int blink2 = (t > 60 && t < 300) ? (t&1)*255 : 0;
display(xpos+45, ypos-45, 3, blink, blink2, hr, min, sec);
}
if(t > 297 && t < 300) {
fadeIn(100);
show_msgbox(title, "5 minutes left !");
wait_no_key_pressed();
dayChanged = 1;
}
}
else {
fadeIn(100);
*countdown = 0;
show_msgbox(title, "Time's up !");
wait_no_key_pressed();
dayChanged = 1;
if(*resetCalcOnCountdown)
resetCalc();
}
}
if(isScreenOn() || forcedDisplay) {
int hr, min, sec;
unsigned long t = timestamp2time(*p_RTC, &hr, &min, &sec);
dayChanged |= (hr == 0 || hr == 24 || ((*display12hrs) && hr == 12)) && min == 0 && sec == 0;
if(*display12hrs) {
if(hr >= 12) {
if(hr > 12)
hr -= 12;
putChar(xpos - 15, ypos + 2*size, 'P', 0, 1);
}
else
putChar(xpos - 15, ypos + 2*size, 'A', 0, 1);
putChar(xpos - 15, ypos + 6*size, 'M', 0, 1);
}
display(xpos, ypos, 5, 0, 0, hr, min, sec);
if(dayChanged || forcedDisplay) {
int day, month, year;
char s_RTC[30];
timestamp2day(t, &year, &month, &day);
char * weekday = getWeekDay(year, month, day);
sprintf(s_RTC, "%s %02d %s %04d", weekday, day, months[month-1], year);
drawString(s_RTC, xpos+40, 100 + size*18, 0, 1);
}
}
return dayChanged;
}
int askTime(int * hour, int * min, int * sec) {
char * subtitle = "Set the time";
return (show_1numeric_input(title, subtitle, "Hour (0-23)", hour, 0, 23)
&& show_1numeric_input(title, subtitle, "Min (0-59)", min, 0, 59)
&& show_1numeric_input(title, subtitle, "Sec (0-59)", sec, 0, 59));
}
int askDay(int * year, int * month, int * day) {
char * subtitle = "Set the day";
return (show_1numeric_input(title, subtitle, "Year (1970-?)", year, 1970, 0x9001)
&& show_1numeric_input(title, subtitle, "Month (1-12)", month, 1, 12)
&& show_1numeric_input(title, subtitle, "Day (1-31)", day, 1, maxDayInMonth(*month, isLeapYear(*year))));
}
int askDate(int * year, int * month, int * day, int * hour, int * min, int * sec) {
return askDay(year, month, day) && askTime(hour, min, sec);
}
int askTimeout(void) {
char * subtitle = "Delay before the screen fades out :";
return (show_1numeric_input(title, subtitle, "Seconds (5-?)", (int*)&timeout, 5, 0x9001));
}
int askDisplay12hrs(void) {
int result = show_msgbox_3b(title, "Select hour format", "12hrs", "24hrs", "Cancel");
*display12hrs = (result == 1) ? 1 : (result == 2) ? 0 : *display12hrs;
return result != 3;
}
int askDisplaySecs(void) {
int result = show_msgbox_3b(title, "Do you want to display seconds ?", "Yes", "No", "Cancel");
*displaySecs = (result == 1) ? 1 : (result == 2) ? 0 : *displaySecs;
return result != 3;
}
int askResetCalcOnCountdown(void) {
int result = show_msgbox_3b(title, "Do you want the calc to reboot when the countdown reaches 0 ?", "Yes", "No", "Cancel");
*resetCalcOnCountdown = (result == 1) ? 1 : (result == 2) ? 0 : *resetCalcOnCountdown;
return result;
}
int askNoMiniClock(void) {
int result = show_msgbox_3b(title, "You feel that the mini-clock is\ntoo mainstream ?", "Yes", "No", "Cancel");
*noMiniClock = (result == 1) ? 1 : (result == 2) ? 0 : *noMiniClock;
if(result == 1) refresh_osscr();
return result;
}
static char * selfName;
static char * selfFolder;
static char * selfPath;
int askStartup(void) {
char * startupFolder = "/documents/ndless/startup/";
if(strcmp(selfFolder, startupFolder) == 0) {
show_msgbox(title, "nClock is already a start-up program !\nIf you want to remove this option, move nClock in a \\1keyword different folder than /documents/ndless/startup/");
}
else {
if(show_msgbox_2b(title, "Do you want nClock to start when the calc boots ?", "Yes", "Cancel") == 1) {
char newPath[strlen(startupFolder) + strlen(selfName) + 1];
strcpy(newPath, startupFolder);
strcat(newPath, selfName);
if(!rename(selfPath, newPath) || (!mkdir(startupFolder, 0) && !rename(selfPath, newPath))) {
char * caption = "nClock was moved to :\n";
char msg[strlen(caption) + strlen(newPath) + 1];
strcpy(msg, caption);
strcat(msg, newPath);
show_msgbox(title, msg);
refresh_osscr();
}
else
show_msgbox(title, "Something went wrong, sorry =(");
}
}
return 0;
}
int askCheckTimeOnStartup(void) {
int result = show_msgbox_3b(title, "Do you want to check if the time is correct when you launch nClock after a reboot ?", "Yes", "No", "Cancel");
*checkTimeOnStartup = (result == 1) ? 1 : (result == 2) ? 0 : *checkTimeOnStartup;
return result;
}
int getEvt() {
/*ON Left Right Down Up Enter Esc
x x x x x x x x
80 40 20 10 8 4 2 1*/
int evt = 0;
if(isKeyPressed(KEY_NSPIRE_ESC)) evt |= 0x1;
if(isKeyPressed(KEY_NSPIRE_ENTER)) evt |= 0x2;
if(isKeyPressed(KEY_NSPIRE_CLICK)) evt |= 0x2;
if(isKeyPressed(KEY_NSPIRE_UP)) evt |= 0x4;
if(isKeyPressed(KEY_NSPIRE_LEFTUP)) evt |= 0x4;
if(isKeyPressed(KEY_NSPIRE_UPRIGHT)) evt |= 0x4;
if(isKeyPressed(KEY_NSPIRE_DOWN)) evt |= 0x8;
if(isKeyPressed(KEY_NSPIRE_DOWNLEFT)) evt |= 0x8;
if(isKeyPressed(KEY_NSPIRE_RIGHTDOWN)) evt |= 0x8;
if(isKeyPressed(KEY_NSPIRE_RIGHT)) evt |= 0x10;
if(isKeyPressed(KEY_NSPIRE_LEFT)) evt |= 0x20;
if(on_key_pressed()) evt |= 0x80;
//if(evt) printf("%x\n", evt);
return evt;
}
void wait_no_key_pressed_repeat(int * repeatKey) {
unsigned int counter = 0xFFF;
unsigned int counter2 = (*repeatKey) ? 0x7FFF : 0x1FFFF;
while(any_key_pressed() && (--counter > 0 || (counter = 0xFFF && --counter2 > 0)));
*repeatKey = counter2 == 0;
}
void ScreenBG(char * Title) {
clrscr();
int xlen = strlen(Title)*(CHAR_WIDTH+2)*2;
int xpos = (320 - xlen) / 2;
drawString(Title, xpos, 5, 0, 2);
}
void Screen(char table[][24], unsigned tablelength, char * Title,
int (*handleEnter)(unsigned),
void (*handlePaint)(char [][24], unsigned, unsigned),
int (*personalInvalidate)(int)) {
int evt = 0;
unsigned option = 0;
int repeatKey = 0;
int invalidate = 1;
int idle_mode = 0;
unsigned long last_timer = *p_RTC; /* reset timer */
wait_no_key_pressed();
fadeOut(100);
while(!(evt&1)) {
if(invalidate) {
ScreenBG(Title);
handlePaint(table, tablelength, option);
if(personalInvalidate)
invalidate = personalInvalidate(1);
else
invalidate = 0;
fadeIn(100);
}
else if(personalInvalidate)
invalidate |= personalInvalidate(0);
int prev_evt = evt;
evt = getEvt();
if(prev_evt != evt) repeatKey = 0;
if(evt) {
last_timer = *p_RTC; /* reset timer */
if(idle_mode) {
fadeIn(100);
idle_mode = 0;
wait_no_key_pressed();
evt = 0; /* Cancel the event*/
}
} else
idle();
/* if timer has expired, bright down the screen */
if(!idle_mode && *p_RTC >= last_timer + timeout) {
idle_mode = !fadeDown(1);
sleep(100);
}
else if(idle_mode) {
sleep(500);
}
if(evt & 0x2) {
wait_no_key_pressed();
evt = handleEnter(option);
invalidate = 1;
wait_no_key_pressed();
last_timer = *p_RTC; /* reset timer */
fadeOut(100);
}
if(evt & 0x24) {
if(option > 0)
--option;
else
option = tablelength - 1;
invalidate = 1;
wait_no_key_pressed_repeat(&repeatKey);
}
if(evt & 0x18) {
if(option < tablelength - 1)
++option;
else
option = 0;
invalidate = 1;
wait_no_key_pressed_repeat(&repeatKey);
}
}
}
void handlePaintV(char table[][24], unsigned tablelength, unsigned option) {
int marginy = 50;
int yinc = (240 - marginy) / tablelength;
int ypos = marginy;
unsigned i;
for(i = 0; i < tablelength; ++i, ypos += yinc) {
int xlen = strlen(table[i])*(CHAR_WIDTH+2);
int xpos = (320 - xlen) >> 1;
drawString(table[i], xpos, ypos, 0, 1);
if(i == option)
drawRect(xpos-5, ypos-5, xlen+10, CHAR_HEIGHT+10, 2, 0);
}
}
void handlePaintH(char table[][24], unsigned tablelength, unsigned option) {
int xinc = 320 / tablelength;
int xpos = 0;
int ypos = 220;
unsigned i;
for(i = 0; i < tablelength; ++i, xpos += xinc) {
int xlen = strlen(table[i])*(CHAR_WIDTH+2);
int xpos2 = xpos + (xinc - xlen) / 2;
drawString(table[i], xpos2, ypos, 0, 1);
if(i == option)
drawRect(xpos2-5, ypos-5, xlen+10, CHAR_HEIGHT+10, 2, 0);
}
}
int handleEnterQuickCountdowns(unsigned option) {
switch(option) {
case 0:
*countdown = *p_RTC + 180;
return 1;
case 1:
*countdown = *p_RTC + 300;
return 1;
case 2:
*countdown = *p_RTC + 900;
return 1;
case 3:
*countdown = *p_RTC + 1800;
return 1;
case 4:
*countdown = *p_RTC + 3600;
return 1;
case 5:
*countdown = *p_RTC + 7200;
return 1;
case 6:
*countdown = *p_RTC + 9600;
return 1;
default:
return 0;
}
}
void quickCountdownsScreen(void) {
char table[][24] = {"3 min (eggs)", "5 min", "15 min (rice)", "30 min", "1 hour (essay)", "2 hours (test)", "3 hours (biiig test)", "Back"};
unsigned tablelength = sizeof(table) / sizeof(table[0]);
Screen(table, tablelength, "Quick Countdowns", handleEnterQuickCountdowns, handlePaintV, 0);
}
int handleEnterCountdown(unsigned option) {
int year = 0, month = 0, day = 0, hr = 0, min = 0, sec = 0;
switch(option) {
case 0:
if(askTime(&hr, &min, &sec)) {
*countdown = *p_RTC + time2timestamp(hr, min, sec);
}
return 1;
case 1:
timestamp2date(*p_RTC, &year, &month, &day, &hr, &min, &sec);
if(askDate(&year, &month, &day, &hr, &min, &sec)) {
*countdown = date2timestamp(year, month, day, hr, min, sec);
timestamp2date(*countdown, &year, &month, &day, &hr, &min, &sec);
}
return 1;
case 2:
quickCountdownsScreen();
return 1;
case 3:
*countdown = 0;
show_msgbox(title, "Countdown removed.");
return 0;
default:
return 1;
}
}
void countdownScreen(void) {
char table[][24] = {"...delay", "...from date", "Quick countdowns...", "Stop current countdown", "Back"};
unsigned tablelength = sizeof(table) / sizeof(table[0]);
Screen(table, tablelength, "Countdown", handleEnterCountdown, handlePaintV, 0);
}
void askMiniClockPosition(void) {
int evt = 0;
int invalidate = 1;
wait_no_key_pressed();
clrscr();
int saved_x = *mini_clock_posx;
int saved_y = *mini_clock_posy;
int offset = (*display12hrs && !*countdown) ? 5: 0;
int xend = ((*displaySecs) ? 49 : 31);
int yend = 12;
while(!(evt&1)) {
if(invalidate) {
mini_nclock(2);
invalidate = 0;
}
sleep(10);
evt = getEvt();
if(evt & 0x2) {
wait_no_key_pressed();
break;
}
if(evt & 0x4) {
if(*mini_clock_posy > 0)
(*mini_clock_posy)--;
invalidate = 1;
}
if(evt & 0x8) {
if(*mini_clock_posy < 240 - yend)
(*mini_clock_posy)++;
invalidate = 1;
}
if(evt & 0x10) {
if(*mini_clock_posx < 320 - xend)
(*mini_clock_posx)++;
invalidate = 1;
}
if(evt & 0x20) {
if(*mini_clock_posx > offset)
(*mini_clock_posx)--;
invalidate = 1;
}
}
if(evt&1) {
*mini_clock_posx = saved_x;
*mini_clock_posy = saved_y;
}
else
refresh_osscr();
}
int handleEnterOptions(unsigned option) {
int year, month, day, hr, min, sec;
switch(option) {
case 0:
timestamp2date(*p_RTC, &year, &month, &day, &hr, &min, &sec);
if(askTime(&hr, &min, &sec)) {
*(p_RTC+2) = date2timestamp(year, month, day, hr, min, sec);
}
break;
case 1:
timestamp2date(*p_RTC, &year, &month, &day, &hr, &min, &sec);
if(askDay(&year, &month, &day)) {
timestamp2time(*p_RTC, &hr, &min, &sec); /* We lost some seconds on the popup */
*(p_RTC+2) = date2timestamp(year, month, day, hr, min, sec);
}
break;
case 2:
askDisplay12hrs();
break;
case 3:
askDisplaySecs();
break;
case 4:
askMiniClockPosition();
break;
case 5:
askNoMiniClock();
break;
case 6:
askTimeout();
break;
case 7:
askCheckTimeOnStartup();
break;
default:
return 1;
break;
}
return 0;
}
void optionScreen(void) {
char table[][24] = {"Change Time", "Change Date", "12/24 hrs", "h:m:s / h:m", "Mini clock position", "Don't use the miniclock", "Fade Out delay", "Ask time on first load", "Back"};
unsigned tablelength = sizeof(table) / sizeof(table[0]);
Screen(table, tablelength, "Options", handleEnterOptions, handlePaintV, 0);
}
int handleEnterMoar(unsigned option) {
switch(option) {
case 0:
revertedScreen = !revertedScreen;
return 1;
break;
case 1:
askResetCalcOnCountdown();
break;
case 2:
askStartup();
break;
case 3:
show_msgbox(title, " nClock\nby Levak & Critor - 18/2/2016\nDisplay the time everywhere !\nnClock comes with two clocks : One is a tiny little clock hooked in the Real Time Clock of the TI-Nspire, and the other one let's you see more things like the actual date. nClock is a whole and options are synchronised in real time and saved in a config file.\nEverything (especially the mini-clock) has been designed to consume the minimal power and don't be energy intensive.\n\nAlso, many many thanks to Lionel Debroux and ExtendeD who helped me a lot in Nspire C programming.\n\nMore TI-Nspire programs on http://tiplanet.org !");
break;
default:
break;
}
return 0;
}
void moarScreen(void) {
char table[][24] = {"Reverse the screen", "Reset Calc on countdown", "Load nClock on startup", "About"};
unsigned tablelength = sizeof(table) / sizeof(table[0]);
Screen(table, tablelength, "Moar!", handleEnterMoar, handlePaintV, 0);
}
int handleEnterMenu(unsigned option) {
switch(option) {
case 0:
optionScreen();
break;
case 1:
countdownScreen();
break;
case 2:
moarScreen();
default:
break;
}
return 0;
}
void menuScreen(void) {
char table[][24] = {"Options", "Countdown", "Moar !"};
unsigned tablelength = sizeof(table) / sizeof(table[0]);
Screen(table, tablelength, "nClock", handleEnterMenu, handlePaintH, nclock);
}
void saveHook(uint32_t hook) {
FILE* h=fopen(HOOK_PATH,"wb");
if(h) {
fwrite(&hook,sizeof(uint32_t),1,h);
fclose(h);
}
}
int loadHook(uint32_t* hook) {
FILE* h=fopen(HOOK_PATH,"rb");
if(h) {
fread(hook,sizeof(uint32_t),1,h);
fclose(h);
return 1;
}
return 0;
}
int main(int argc, char* argv[]) {
if(argc < 1) return 0;
SCREEN_BASE_ADDRESS =*(unsigned char**)0xC0000010 ;
screen_type = getScreenType();
/* Save user contrast */
contrast = *p_contrast;
int len = strlen(argv[0]);
char path[len];
strcpy(path, argv[0]);
char *p = strrchr(path, '/');
char self_name[len - ((p) ? p - path + 1: 0) + 1];
strcpy(self_name, (p) ? p + 1 : path);
if(p) *(p + 1) = 0;
selfName = self_name;
selfFolder = path;
selfPath = argv[0];
if(strcmp(self_name, "uninstall.tns") == 0) {
if(!nl_isstartup()) {
if (show_msgbox_2b(title, "Do you want to unsinstall nClock ?", "Yes", "No") == 2)
return 0;
if(remove(config_path) == 0) {
show_msgbox(title, "nClock has successfuly deleted the config file.");
free_shared_data();
if(show_msgbox_2b(title, "To completly remove nclock, you need to reboot.\nReboot now ?", "Yes", "No") == 1) {
resetCalc();
}
else
return 0;
}
else
show_msgbox(title, "nClock is not installed.");
return 0;
}
else {
char newName[] = "nclock.tns";
char newPath[strlen(selfFolder)+strlen(newName)+1];
strcpy(newPath, selfFolder);
strcat(newPath, newName);
if(rename(selfPath, newPath) != 0)
return 0;
}
}
uint32_t current_hook = *(volatile uint32_t*)HOOK_ADDR;
uint32_t orig_hook;
int r = loadHook(&orig_hook);
if(!r || nl_isstartup()) {
if(!r || orig_hook!=current_hook)
saveHook(current_hook);
orig_hook=current_hook;
}
/* If hook isn't already installed */
if(orig_hook==current_hook) {
/* if(screen_type==SCR_240x320_565) {
SCREEN_BASE_ADDRESS=*(unsigned char**)nl_osvalue((int*)lcd_mirror_ptr, sizeof(lcd_mirror_ptr)/sizeof(lcd_mirror_ptr[0]));
screen_type==SCR_320x240_565;
}*/
malloc_shared_data();
/* Install hook */
HOOK_INSTALL(HOOK_ADDR, hook_nclock);
nl_set_resident();
}
if(!nl_isstartup()) {
/* Extra screen buffer */
char *sbuffer = malloc(SCREEN_BYTES_SIZE);
memcpy(sbuffer, SCREEN_BASE_ADDRESS, SCREEN_BYTES_SIZE);
/* Event loop */
readConfig(0, config_path);
menuScreen();
fadeOut(100);
memcpy(SCREEN_BASE_ADDRESS, sbuffer, SCREEN_BYTES_SIZE);
fadeIn(100);
free(sbuffer);
//printf("%d %x(%d) %x(%d) %x(%d)\n", *p_RTC, display12hrs, *display12hrs, displaySecs, *displaySecs, countdown, *countdown);
}
/* Restore user defined contrast */
*p_contrast = contrast;
writeConfig(config_path);
return 0;
}