mirror of
https://github.com/dgis/emu48android
synced 2025-01-18 22:26:37 +01:00
- Add an optional menu button in the top left corner.
- Rewrite the timers engine (Hoping this fixes the issue with the stuck "busy" annunciator.) - Prevent to load/save object and copy/paste with HP39/40.
This commit is contained in:
parent
3e345c61fd
commit
f4a3ec6476
7 changed files with 294 additions and 66 deletions
17
ReadMe.txt
17
ReadMe.txt
|
@ -4,7 +4,7 @@ WARNING: WITH VERSION 1.3, THE STATUS FILE HAS BEEN MODIFIED AND IS NOW FULLY CO
|
|||
HOWEVER, BEFORE THE UPDATE, BACK UP YOUR DATA BECAUSE YOU COULD LOSE THEM.
|
||||
|
||||
This project ports the Windows application Emu48 written in C to Android.
|
||||
It uses the Android NDK. The former Emu48 source code remains untouched because of a thin win32 emulation layer above Linux/NDK!
|
||||
It uses the Android NDK. The former Emu48 source code (written by Sébastien Carlier and Christoph Giesselink) remains untouched because of a thin win32 emulation layer above Linux/NDK!
|
||||
This win32 layer will allow to easily update from the original Emu48 source code.
|
||||
It can open or save the exact same state files (state.e48/e49) than the original Windows application!
|
||||
|
||||
|
@ -20,12 +20,13 @@ QUICK START
|
|||
|
||||
1. From the left side, slide your finger to open the menu.
|
||||
2. Touch the "New..." menu item.
|
||||
3. Select a predefined faceplate (or select a custom KML script folder).
|
||||
3. Select a predefined faceplate (or select a custom KML script folder with Android >= 5.0).
|
||||
4. And the calculator should now be opened.
|
||||
|
||||
|
||||
NOTES
|
||||
|
||||
- The Help menu displays Emu48's original help HTML page and may not accurately reflect the behavior of this Android version.
|
||||
- When using a custom KML script by selecting a folder, you must take care of the case sensitivity of its dependency files.
|
||||
- Starting with the version 1.4, a RAM card generator for the port 2 of the HP48SX and HP48GX has been added.
|
||||
Like with the MKSHARED.EXE on Windows, you can generate the card in a file (i.e.: SHARED.BIN).
|
||||
|
@ -57,13 +58,16 @@ NOT WORKING YET
|
|||
|
||||
CHANGES
|
||||
|
||||
Version 1.4 (2019-05-xx)
|
||||
Version 1.4 (2019-06-xx)
|
||||
|
||||
- Add an optional menu button in the top left corner.
|
||||
- Add a RAM card generator for the port 2 of the HP48SX and HP48GX.
|
||||
- Add the possibility to hide the status and/or the navigation bars.
|
||||
- Rewrite the timers engine (Hoping this fixes the issue with the stuck "busy" annunciator.)
|
||||
- Update the Win32 layer from Emu42 dev.
|
||||
- Fix the authentic speed issue at the first start.
|
||||
- Fix the non working Restore/Delete backup.
|
||||
- Prevent to load/save object and copy/paste with HP39/40.
|
||||
|
||||
|
||||
Version 1.3 (2019-04-04)
|
||||
|
@ -131,17 +135,18 @@ The Eric's Real scripts ("real*.kml" and "real*.bmp") are embedded in this appli
|
|||
|
||||
TODO
|
||||
|
||||
- Warn the user about the KML folder selection if this is Android < 5.0
|
||||
- Disable the Restore after loading a new state file.
|
||||
- Improve the swipe gesture.
|
||||
- The clock seems unsynchronized sometimes
|
||||
- Sometimes the "busy" annunciator gets stuck
|
||||
- Add KML script loading dependencies fallback to the inner ROM (and may be KML include?)
|
||||
- Add a separation between the pixels (Suggestion from Jaime Meza)
|
||||
- Improve the access to the menu
|
||||
- Change the logo following the template
|
||||
|
||||
|
||||
BUILD
|
||||
|
||||
Emu48 for Android is built with Android Studio 3.3 (2019).
|
||||
Emu48 for Android is built with Android Studio 3.4 (2019).
|
||||
And to generate an installable APK file with a real Android device, it MUST be signed.
|
||||
|
||||
Either use Android Studio:
|
||||
|
|
|
@ -19,6 +19,7 @@ cmake_minimum_required(VERSION 3.4.1)
|
|||
#add_compile_options(-DDEBUG_ANDROID_FILE)
|
||||
|
||||
#add_compile_options(-DNEW_WIN32_SOUND_ENGINE)
|
||||
#add_compile_options(-DWIN32_TIMER_THREAD) # old timer algorithm which stop when no more thread!
|
||||
|
||||
add_compile_options(-DEMUXX=48)
|
||||
|
||||
|
|
|
@ -32,25 +32,13 @@ DWORD GetLastError(VOID) {
|
|||
}
|
||||
|
||||
|
||||
struct timerEvent {
|
||||
BOOL valid;
|
||||
int timerId;
|
||||
LPTIMECALLBACK fptc;
|
||||
DWORD_PTR dwUser;
|
||||
UINT fuEvent;
|
||||
timer_t timer;
|
||||
};
|
||||
|
||||
#define MAX_TIMER 10
|
||||
struct timerEvent timerEvents[MAX_TIMER];
|
||||
static void initTimer();
|
||||
|
||||
#define MAX_FILE_MAPPING_HANDLE 10
|
||||
static HANDLE fileMappingHandles[MAX_FILE_MAPPING_HANDLE];
|
||||
|
||||
void win32Init() {
|
||||
for (int i = 0; i < MAX_TIMER; ++i) {
|
||||
timerEvents[i].valid = FALSE;
|
||||
}
|
||||
initTimer();
|
||||
for (int i = 0; i < MAX_FILE_MAPPING_HANDLE; ++i) {
|
||||
fileMappingHandles[i] = NULL;
|
||||
}
|
||||
|
@ -850,9 +838,9 @@ LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
|
|||
}
|
||||
return NULL;
|
||||
}
|
||||
BOOL PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
|
||||
BOOL PostMessage(HWND handleWindow, UINT Msg, WPARAM wParam, LPARAM lParam) {
|
||||
//TODO
|
||||
if(hWnd == 0 && Msg == WM_COMMAND) {
|
||||
if(Msg == WM_COMMAND && hWnd == handleWindow) {
|
||||
int menuCommand = (int) ((wParam & 0xffff) - 40000);
|
||||
LOGD("Menu Item %d", menuCommand);
|
||||
sendMenuItemCommand(menuCommand);
|
||||
|
@ -1143,7 +1131,7 @@ MMRESULT waveOutOpen(LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWOR
|
|||
sev.sigev_notify_attributes = NULL;
|
||||
timer_t * timer = &(handle->playerDoneTimer);
|
||||
if (timer_create(CLOCK_MONOTONIC, &sev, timer) == -1) {
|
||||
TIMER_LOGD("timeSetEvent() ERROR in timer_create");
|
||||
WAVE_OUT_LOGD("waveOutOpen() ERROR in timer_create");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1282,7 +1270,7 @@ MMRESULT waveOutWrite(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh) {
|
|||
}
|
||||
|
||||
if (timer_settime(hwo->playerDoneTimer, 0, &(hwo->playerDoneTimerSetTimes), NULL) == -1) {
|
||||
TIMER_LOGD("timeSetEvent() ERROR in timer_settime (playerDoneTimerSetTimes)");
|
||||
WAVE_OUT_LOGD("waveOutWrite() ERROR in timer_settime (playerDoneTimerSetTimes)");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1611,7 +1599,7 @@ BOOL PatBlt(HDC hdcDest, int x, int y, int w, int h, DWORD rop) {
|
|||
destinationStride = androidBitmapInfo.stride;
|
||||
|
||||
if ((ret = AndroidBitmap_lockPixels(jniEnv, bitmapMainScreen, &pixelsDestination)) < 0) {
|
||||
LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
|
||||
LOGD("AndroidBitmap_lockPixels() failed ! error=%d", ret);
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
|
@ -1738,7 +1726,7 @@ BOOL StretchBlt(HDC hdcDest, int xDest, int yDest, int wDest, int hDest, HDC hdc
|
|||
destinationStride = androidBitmapInfo.stride;
|
||||
|
||||
if ((ret = AndroidBitmap_lockPixels(jniEnv, bitmapMainScreen, &pixelsDestination)) < 0) {
|
||||
LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
|
||||
LOGD("AndroidBitmap_lockPixels() failed ! error=%d", ret);
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
|
@ -1938,7 +1926,7 @@ BOOL StretchBlt(HDC hdcDest, int xDest, int yDest, int wDest, int hDest, HDC hdc
|
|||
}
|
||||
|
||||
if(jniEnv && (ret = AndroidBitmap_unlockPixels(jniEnv, bitmapMainScreen)) < 0) {
|
||||
LOGE("AndroidBitmap_unlockPixels() failed ! error=%d", ret);
|
||||
LOGD("AndroidBitmap_unlockPixels() failed ! error=%d", ret);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -2224,10 +2212,38 @@ HANDLE WINAPI GetClipboardData(UINT uFormat) {
|
|||
return szText;
|
||||
}
|
||||
|
||||
#if defined WIN32_TIMER_THREAD
|
||||
struct timerEvent {
|
||||
BOOL valid;
|
||||
int timerId;
|
||||
LPTIMECALLBACK fptc;
|
||||
DWORD_PTR dwUser;
|
||||
UINT fuEvent;
|
||||
timer_t timer;
|
||||
};
|
||||
|
||||
#define MAX_TIMER 10
|
||||
struct timerEvent timerEvents[MAX_TIMER];
|
||||
pthread_mutex_t timerEventsLock;
|
||||
static void initTimer() {
|
||||
for (int i = 0; i < MAX_TIMER; ++i) {
|
||||
timerEvents[i].valid = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void deleteTimeEvent(UINT uTimerID) {
|
||||
pthread_mutex_lock(&timerEventsLock);
|
||||
timer_delete(timerEvents[uTimerID - 1].timer);
|
||||
timerEvents[uTimerID - 1].valid = FALSE;
|
||||
pthread_mutex_unlock(&timerEventsLock);
|
||||
}
|
||||
|
||||
MMRESULT timeKillEvent(UINT uTimerID) {
|
||||
TIMER_LOGD("timeKillEvent(uTimerID: [%d])", uTimerID);
|
||||
deleteTimeEvent(uTimerID);
|
||||
return 0; //No error
|
||||
}
|
||||
|
||||
void timerCallback(int timerId) {
|
||||
if(timerId >= 0 && timerId < MAX_TIMER && timerEvents[timerId].valid) {
|
||||
timerEvents[timerId].fptc((UINT) (timerId + 1), 0, (DWORD) timerEvents[timerId].dwUser, 0, 0);
|
||||
|
@ -2243,6 +2259,8 @@ void timerCallback(int timerId) {
|
|||
MMRESULT timeSetEvent(UINT uDelay, UINT uResolution, LPTIMECALLBACK fptc, DWORD_PTR dwUser, UINT fuEvent) {
|
||||
TIMER_LOGD("timeSetEvent(uDelay: %d, fuEvent: %d)", uDelay, fuEvent);
|
||||
|
||||
pthread_mutex_lock(&timerEventsLock);
|
||||
|
||||
// Find a timer id
|
||||
int timerId = -1;
|
||||
for (int i = 0; i < MAX_TIMER; ++i) {
|
||||
|
@ -2252,7 +2270,8 @@ MMRESULT timeSetEvent(UINT uDelay, UINT uResolution, LPTIMECALLBACK fptc, DWORD_
|
|||
}
|
||||
}
|
||||
if(timerId == -1) {
|
||||
TIMER_LOGD("timeSetEvent() ERROR: No more timer available");
|
||||
LOGD("timeSetEvent() ERROR: No more timer available");
|
||||
pthread_mutex_unlock(&timerEventsLock);
|
||||
return NULL;
|
||||
}
|
||||
timerEvents[timerId].timerId = timerId;
|
||||
|
@ -2267,8 +2286,26 @@ MMRESULT timeSetEvent(UINT uDelay, UINT uResolution, LPTIMECALLBACK fptc, DWORD_
|
|||
sev.sigev_value.sival_int = timerEvents[timerId].timerId; //this argument will be passed to cbf
|
||||
sev.sigev_notify_attributes = NULL;
|
||||
timer_t * timer = &(timerEvents[timerId].timer);
|
||||
if (timer_create(CLOCK_MONOTONIC, &sev, timer) == -1) {
|
||||
TIMER_LOGD("timeSetEvent() ERROR in timer_create");
|
||||
|
||||
// CLOCK_REALTIME 0 OK but X intervals only
|
||||
// CLOCK_MONOTONIC 1 OK but X intervals only
|
||||
// CLOCK_PROCESS_CPUTIME_ID 2 NOTOK, not working with PERIODIC!
|
||||
// CLOCK_THREAD_CPUTIME_ID 3 NOTOK
|
||||
// CLOCK_MONOTONIC_RAW 4 NOTOK
|
||||
// CLOCK_REALTIME_COARSE 5 NOTOK
|
||||
// CLOCK_MONOTONIC_COARSE 6 NOTOK
|
||||
// CLOCK_BOOTTIME 7 OK but X intervals only
|
||||
// CLOCK_REALTIME_ALARM 8 NOTOK EPERM
|
||||
// CLOCK_BOOTTIME_ALARM 9 NOTOK EPERM
|
||||
// CLOCK_SGI_CYCLE 10 NOTOK EINVAL
|
||||
// CLOCK_TAI 11
|
||||
|
||||
if (timer_create(CLOCK_REALTIME, &sev, timer) == -1) {
|
||||
LOGD("timeSetEvent() ERROR in timer_create, errno: %d (EAGAIN 11 / EINVAL 22 / ENOMEM 12)", errno);
|
||||
// EAGAIN Temporary error during kernel allocation of timer structures.
|
||||
// EINVAL Clock ID, sigev_notify, sigev_signo, or sigev_notify_thread_id is invalid.
|
||||
// ENOMEM Could not allocate memory.
|
||||
pthread_mutex_unlock(&timerEventsLock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2284,20 +2321,198 @@ MMRESULT timeSetEvent(UINT uDelay, UINT uResolution, LPTIMECALLBACK fptc, DWORD_
|
|||
its.it_interval.tv_nsec = 0;
|
||||
}
|
||||
if (timer_settime(timerEvents[timerId].timer, 0, &its, NULL) == -1) {
|
||||
LOGD("timeSetEvent() ERROR in timer_settime, errno: %d (EFAULT 14 / EINVAL 22)", errno);
|
||||
// EFAULT new_value, old_value, or curr_value is not a valid pointer.
|
||||
// EINVAL timerid is invalid. Or new_value.it_value is negative; or new_value.it_value.tv_nsec is negative or greater than 999,999,999.
|
||||
timer_delete(timerEvents[timerId].timer);
|
||||
TIMER_LOGD("timeSetEvent() ERROR in timer_settime");
|
||||
pthread_mutex_unlock(&timerEventsLock);
|
||||
return NULL;
|
||||
}
|
||||
timerEvents[timerId].valid = TRUE;
|
||||
TIMER_LOGD("timeSetEvent() -> timerId+1: [%d]", timerId + 1);
|
||||
pthread_mutex_unlock(&timerEventsLock);
|
||||
return (MMRESULT) (timerId + 1); // No error
|
||||
}
|
||||
#else
|
||||
struct timerEvent {
|
||||
int timerId;
|
||||
UINT uDelay;
|
||||
UINT uResolution;
|
||||
LPTIMECALLBACK fptc;
|
||||
DWORD_PTR dwUser;
|
||||
UINT fuEvent;
|
||||
|
||||
ULONGLONG triggerTime;
|
||||
|
||||
struct timerEvent * next;
|
||||
struct timerEvent * prev;
|
||||
};
|
||||
static struct timerEvent timersList;
|
||||
static int lastTimerId;
|
||||
static pthread_mutex_t timerEventsLock;
|
||||
static pthread_t timerThreadId;
|
||||
static BOOL timerThreadToEnd;
|
||||
|
||||
static void initTimer() {
|
||||
timersList.next = &timersList;
|
||||
timersList.prev = &timersList;
|
||||
timerThreadId = 0;
|
||||
timerThreadToEnd = TRUE;
|
||||
lastTimerId = 0;
|
||||
}
|
||||
|
||||
MMRESULT timeKillEvent(UINT uTimerID) {
|
||||
TIMER_LOGD("timeKillEvent(uTimerID: [%d])", uTimerID);
|
||||
deleteTimeEvent(uTimerID);
|
||||
|
||||
pthread_mutex_lock(&timerEventsLock);
|
||||
|
||||
struct timerEvent * nextTimer, * timerToFree = NULL;
|
||||
// Search the timer by id
|
||||
for (nextTimer = timersList.next; nextTimer != &timersList; nextTimer = nextTimer->next) {
|
||||
if(uTimerID == nextTimer->timerId) {
|
||||
// Remove the timer
|
||||
nextTimer->next->prev = nextTimer->prev;
|
||||
nextTimer->prev->next = nextTimer->next;
|
||||
timerToFree = nextTimer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(timersList.next == &timersList) {
|
||||
// The list is empty
|
||||
// Leave the thread?
|
||||
timerThreadToEnd = TRUE;
|
||||
}
|
||||
pthread_mutex_unlock(&timerEventsLock);
|
||||
|
||||
if(timerToFree)
|
||||
free(timerToFree);
|
||||
|
||||
return 0; //No error
|
||||
}
|
||||
|
||||
static void insertTimer(struct timerEvent * newTimer) {
|
||||
struct timerEvent * nextTimer;
|
||||
// Search where to insert this new timer
|
||||
for (nextTimer = timersList.next; nextTimer != &timersList; nextTimer = nextTimer->next) {
|
||||
if(newTimer->triggerTime < nextTimer->triggerTime)
|
||||
break;
|
||||
}
|
||||
// Insert this new timer
|
||||
newTimer->next = nextTimer;
|
||||
newTimer->prev = nextTimer->prev;
|
||||
nextTimer->prev->next = newTimer;
|
||||
nextTimer->prev = newTimer;
|
||||
}
|
||||
|
||||
static void dumpTimers() {
|
||||
TIMER_LOGD("dumpTimers()");
|
||||
for (struct timerEvent * nextTimer = timersList.next; nextTimer != &timersList; nextTimer = nextTimer->next) {
|
||||
TIMER_LOGD("\ttimerId: %d (%x), uDelay: %d, dwUser: %d, fuEvent: %d, triggerTime: %ld)",
|
||||
nextTimer->timerId, nextTimer, nextTimer->uDelay, nextTimer->dwUser, nextTimer->fuEvent, nextTimer->triggerTime);
|
||||
}
|
||||
}
|
||||
|
||||
static void timerThreadStart(LPVOID lpThreadParameter) {
|
||||
pthread_mutex_lock(&timerEventsLock);
|
||||
while (!timerThreadToEnd) {
|
||||
TIMER_LOGD("timerThreadStart() %ld", GetTickCount64());
|
||||
|
||||
int sleep_time;
|
||||
for (;;) {
|
||||
struct timerEvent *timer = (timersList.next == &timersList ? NULL : timersList.next);
|
||||
if (!timer) {
|
||||
sleep_time = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
sleep_time = timer->triggerTime - GetTickCount64();
|
||||
if (sleep_time > 0)
|
||||
break;
|
||||
|
||||
// Remove this timer from the linked list
|
||||
timer->next->prev = timer->prev;
|
||||
timer->prev->next = timer->next;
|
||||
|
||||
struct timerEvent *timerToFree;
|
||||
if(timer->fuEvent == TIME_PERIODIC) {
|
||||
timer->triggerTime += timer->uDelay;
|
||||
/* Re-insert the timer */
|
||||
insertTimer(timer);
|
||||
timerToFree = NULL;
|
||||
} else
|
||||
timerToFree = timer;
|
||||
|
||||
// Copy the timer data because we unlock the mutex!
|
||||
int timerId = timer->timerId;
|
||||
LPTIMECALLBACK fptc = timer->fptc;
|
||||
DWORD_PTR dwUser = timer->dwUser;
|
||||
|
||||
pthread_mutex_unlock(&timerEventsLock);
|
||||
|
||||
// Call the timer callback now!
|
||||
fptc((UINT)timerId, 0, (DWORD) dwUser, 0, 0);
|
||||
|
||||
pthread_mutex_lock(&timerEventsLock);
|
||||
if(timerToFree)
|
||||
free(timerToFree);
|
||||
}
|
||||
|
||||
#if defined(TIMER_LOGD)
|
||||
dumpTimers();
|
||||
#endif
|
||||
|
||||
if (sleep_time < 0)
|
||||
break;
|
||||
if (sleep_time == 0)
|
||||
continue;
|
||||
|
||||
pthread_mutex_unlock(&timerEventsLock);
|
||||
Sleep(sleep_time);
|
||||
pthread_mutex_lock(&timerEventsLock);
|
||||
}
|
||||
timerThreadId = 0;
|
||||
pthread_mutex_unlock(&timerEventsLock);
|
||||
jniDetachCurrentThread();
|
||||
}
|
||||
|
||||
MMRESULT timeSetEvent(UINT uDelay, UINT uResolution, LPTIMECALLBACK fptc, DWORD_PTR dwUser, UINT fuEvent) {
|
||||
TIMER_LOGD("timeSetEvent(uDelay: %d, fuEvent: %d)", uDelay, fuEvent);
|
||||
|
||||
struct timerEvent * newTimer = (struct timerEvent *)malloc(sizeof(struct timerEvent));
|
||||
|
||||
pthread_mutex_lock(&timerEventsLock);
|
||||
|
||||
newTimer->timerId = ++lastTimerId;
|
||||
newTimer->uDelay = uDelay; // In ms
|
||||
newTimer->uResolution = uResolution;
|
||||
newTimer->fptc = fptc;
|
||||
newTimer->dwUser = dwUser;
|
||||
newTimer->fuEvent = fuEvent; // TIME_ONESHOT / TIME_PERIODIC
|
||||
|
||||
newTimer->triggerTime = GetTickCount64() + (ULONGLONG)uDelay;
|
||||
|
||||
insertTimer(newTimer);
|
||||
|
||||
timerThreadToEnd = FALSE;
|
||||
|
||||
if(!timerThreadId) {
|
||||
// If not yet created, create the thread which will handle all the timers
|
||||
if (pthread_create(&timerThreadId, NULL, (void *(*)(void *)) timerThreadStart, NULL) != 0) {
|
||||
LOGD("timeSetEvent() ERROR in pthread_create, errno: %d (EAGAIN 11 / EINVAL 22 / EPERM 1)", errno);
|
||||
// EAGAIN Insufficient resources to create another thread.
|
||||
// EINVAL Invalid settings in attr.
|
||||
// ENOMEM No permission to set the scheduling policy and parameters specified in attr.
|
||||
pthread_mutex_unlock(&timerEventsLock);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
TIMER_LOGD("timeSetEvent() -> timerId: [%d]", newTimer->timerId);
|
||||
pthread_mutex_unlock(&timerEventsLock);
|
||||
return (MMRESULT) newTimer->timerId; // No error
|
||||
}
|
||||
#endif
|
||||
|
||||
MMRESULT timeGetDevCaps(LPTIMECAPS ptc, UINT cbtc) {
|
||||
if(ptc) {
|
||||
ptc->wPeriodMin = 1; // ms
|
||||
|
@ -2320,21 +2535,25 @@ VOID GetLocalTime(LPSYSTEMTIME lpSystemTime) {
|
|||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
time_t tim = ts.tv_sec;
|
||||
localtime_r(&tim, &tm);
|
||||
lpSystemTime->wYear = 1900 + tm.tm_year;
|
||||
lpSystemTime->wMonth = 1 + tm.tm_mon;
|
||||
lpSystemTime->wDayOfWeek = tm.tm_wday;
|
||||
lpSystemTime->wDay = tm.tm_mday;
|
||||
lpSystemTime->wHour = tm.tm_hour;
|
||||
lpSystemTime->wMinute = tm.tm_min;
|
||||
lpSystemTime->wSecond = tm.tm_sec;
|
||||
lpSystemTime->wMilliseconds = ts.tv_nsec / 1e6;
|
||||
lpSystemTime->wYear = (WORD) (1900 + tm.tm_year);
|
||||
lpSystemTime->wMonth = (WORD) (1 + tm.tm_mon);
|
||||
lpSystemTime->wDayOfWeek = (WORD) (tm.tm_wday);
|
||||
lpSystemTime->wDay = (WORD) (tm.tm_mday);
|
||||
lpSystemTime->wHour = (WORD) (tm.tm_hour);
|
||||
lpSystemTime->wMinute = (WORD) (tm.tm_min);
|
||||
lpSystemTime->wSecond = (WORD) (tm.tm_sec);
|
||||
lpSystemTime->wMilliseconds = (WORD) (ts.tv_nsec / 1e6);
|
||||
}
|
||||
}
|
||||
WORD GetTickCount(VOID) {
|
||||
//TODO
|
||||
ULONGLONG GetTickCount64(VOID) {
|
||||
struct timespec now;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &now))
|
||||
return 0;
|
||||
return (ULONGLONG) ((ULONGLONG)now.tv_sec * 1000UL + (ULONGLONG)now.tv_nsec / 1000000UL);
|
||||
}
|
||||
DWORD GetTickCount(VOID) {
|
||||
return (DWORD)GetTickCount64();
|
||||
}
|
||||
|
||||
|
||||
BOOL EnableWindow(HWND hWnd, BOOL bEnable) {
|
||||
//TODO
|
||||
|
@ -2529,4 +2748,4 @@ void _makepath(char _Buffer, char const* _Drive, char const* _Dir, char const* _
|
|||
BOOL GetClientRect(HWND hWnd, LPRECT lpRect)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1034,7 +1034,8 @@ extern MMRESULT timeGetDevCaps(LPTIMECAPS ptc, UINT cbtc);
|
|||
extern MMRESULT timeBeginPeriod(UINT uPeriod);
|
||||
extern MMRESULT timeEndPeriod(UINT uPeriod);
|
||||
extern VOID GetLocalTime(LPSYSTEMTIME lpSystemTime);
|
||||
extern WORD GetTickCount(VOID);
|
||||
extern ULONGLONG GetTickCount64(VOID);
|
||||
extern DWORD GetTickCount(VOID);
|
||||
|
||||
extern BOOL EnableWindow(HWND hWnd, BOOL bEnable);
|
||||
extern HWND GetDlgItem(HWND hDlg, int nIDDlgItem);
|
||||
|
|
|
@ -361,22 +361,19 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
Menu menu = navigationView.getMenu();
|
||||
boolean isDocumentAvailable = NativeLib.isDocumentAvailable();
|
||||
boolean isBackup = NativeLib.isBackup();
|
||||
// disable stack loading items on HP38G, HP39/40G, HP39G+
|
||||
// BOOL bStackEnable = cCurrentRomType!='6' && cCurrentRomType!='A' && cCurrentRomType!='E' && cCurrentRomType!='P'; // CdB for HP: add apples
|
||||
// BOOL bRun = nState == SM_RUN || nState == SM_SLEEP;
|
||||
|
||||
// UINT uStackEnable = (bRun && bStackEnable) ? MF_ENABLED : MF_GRAYED;
|
||||
// UINT uRun = bRun ? MF_ENABLED : MF_GRAYED;
|
||||
// UINT uBackup = bBackup ? MF_ENABLED : MF_GRAYED;
|
||||
int cCurrentRomType = NativeLib.getCurrentModel();
|
||||
//int nState = NativeLib.getState();
|
||||
//boolean bRun = (nState == 0 /* SM_RUN */ || nState == 3 /* SM_SLEEP */);
|
||||
boolean bStackEnable = cCurrentRomType!='6' && cCurrentRomType!='A' && cCurrentRomType!='E' && cCurrentRomType!='P'; // CdB for HP: add apples
|
||||
|
||||
menu.findItem(R.id.nav_save).setEnabled(isDocumentAvailable);
|
||||
menu.findItem(R.id.nav_save_as).setEnabled(isDocumentAvailable);
|
||||
menu.findItem(R.id.nav_close).setEnabled(isDocumentAvailable);
|
||||
menu.findItem(R.id.nav_load_object).setEnabled(isDocumentAvailable);
|
||||
menu.findItem(R.id.nav_save_object).setEnabled(isDocumentAvailable);
|
||||
menu.findItem(R.id.nav_load_object).setEnabled(isDocumentAvailable && bStackEnable);
|
||||
menu.findItem(R.id.nav_save_object).setEnabled(isDocumentAvailable && bStackEnable);
|
||||
menu.findItem(R.id.nav_copy_screen).setEnabled(isDocumentAvailable);
|
||||
menu.findItem(R.id.nav_copy_stack).setEnabled(isDocumentAvailable);
|
||||
menu.findItem(R.id.nav_paste_stack).setEnabled(isDocumentAvailable);
|
||||
menu.findItem(R.id.nav_copy_stack).setEnabled(isDocumentAvailable && bStackEnable);
|
||||
menu.findItem(R.id.nav_paste_stack).setEnabled(isDocumentAvailable && bStackEnable);
|
||||
menu.findItem(R.id.nav_reset_calculator).setEnabled(isDocumentAvailable);
|
||||
menu.findItem(R.id.nav_backup_save).setEnabled(isDocumentAvailable);
|
||||
menu.findItem(R.id.nav_backup_restore).setEnabled(isDocumentAvailable && isBackup);
|
||||
|
@ -633,29 +630,35 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType("*/*");
|
||||
int model = NativeLib.getCurrentModel();
|
||||
String extension = "e48";
|
||||
String filename = "emu48-state.e48";
|
||||
switch (model) {
|
||||
case 'S': //HP48SX
|
||||
filename = "emu48-state-48sx.e48";
|
||||
break;
|
||||
case 'G': //HP48GX
|
||||
extension = "e48";
|
||||
filename = "emu48-state-48gx.e48";
|
||||
break;
|
||||
case '6': //HP38G 64K RAM
|
||||
case 'A': //HP38G 32K RAM
|
||||
extension = "e38";
|
||||
filename = "emu48-state.e38";
|
||||
break;
|
||||
case 'E': // HP39G/(HP39G+/HP39GS)/HP40G/HP40GS
|
||||
extension = "e39";
|
||||
filename = "emu48-state.e39";
|
||||
break;
|
||||
case 'P': // HP39G+/HP39GS
|
||||
extension = "e39";
|
||||
filename = "emu48-state.e39";
|
||||
break;
|
||||
case '2': // HP48GII
|
||||
filename = "emu48-state-48gii.e49";
|
||||
break;
|
||||
case 'Q': // HP49G+/HP50G
|
||||
filename = "emu48-state-50g.e49";
|
||||
break;
|
||||
case 'X': // HP49G
|
||||
extension = "e49";
|
||||
filename = "emu48-state-49g.e49";
|
||||
break;
|
||||
}
|
||||
intent.putExtra(Intent.EXTRA_TITLE, "emu48-state." + extension);
|
||||
intent.putExtra(Intent.EXTRA_TITLE, filename);
|
||||
startActivityForResult(intent, INTENT_GETSAVEFILENAME);
|
||||
}
|
||||
private void OnFileClose() {
|
||||
|
|
|
@ -6,7 +6,6 @@ import android.content.pm.ActivityInfo;
|
|||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
|
||||
import android.graphics.Paint;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
|
@ -251,7 +250,7 @@ public class MainScreenView extends PanAndScaleView {
|
|||
if(imageRatio > 1.0f)
|
||||
((Activity)getContext()).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
else
|
||||
((Activity)getContext()).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||
((Activity)getContext()).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
|
||||
} else if(autoZoom) {
|
||||
((Activity)getContext()).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
||||
if (imageRatio < 1.0f != viewRatio < 1.0f) {
|
||||
|
|
|
@ -7,7 +7,7 @@ buildscript {
|
|||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.4.0'
|
||||
classpath 'com.android.tools.build:gradle:3.4.1'
|
||||
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
|
Loading…
Reference in a new issue