Version 2.5 (2022-03-03)

- Allow to load RLE4, RLE8 and monochrome BMP images.
- Optimize the number of draw calls when displaying the LCD pixel borders.
This commit is contained in:
dgis 2022-03-03 08:11:19 +01:00
parent 8a54fb0b33
commit d27d777153
12 changed files with 310 additions and 48 deletions

View file

@ -58,6 +58,12 @@ LINKS
CHANGES CHANGES
Version 2.5 (2022-03-03)
- Allow to load RLE4, RLE8 and monochrome BMP images.
- Optimize the number of draw calls when displaying the LCD pixel borders.
Version 2.4 (2021-12-08) Version 2.4 (2021-12-08)
- Updated source code from Eric Rechlin's Emu48 version 1.63+ that was merged from Christoph Gießelink's Emu48 version 1.64. - Updated source code from Eric Rechlin's Emu48 version 1.63+ that was merged from Christoph Gießelink's Emu48 version 1.64.

View file

@ -28,13 +28,13 @@ if (keystorePropertiesFile.exists()) {
} }
android { android {
compileSdkVersion 30 compileSdkVersion 31
defaultConfig { defaultConfig {
applicationId "org.emulator.forty.eight" applicationId "org.emulator.forty.eight"
minSdkVersion 19 minSdkVersion 19
targetSdkVersion 30 targetSdkVersion 31
versionCode 23 versionCode 24
versionName "2.4" versionName "2.5"
setProperty("archivesBaseName", "Emu48-v$versionName") setProperty("archivesBaseName", "Emu48-v$versionName")
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild { externalNativeBuild {
@ -84,12 +84,12 @@ android {
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'androidx.preference:preference:1.1.1' implementation 'androidx.preference:preference:1.2.0'
implementation 'com.google.android.material:material:1.4.0' implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.documentfile:documentfile:1.0.1' implementation 'androidx.documentfile:documentfile:1.0.1'
testImplementation 'junit:junit:4.13.1' testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test:runner:1.4.0' androidTestImplementation 'androidx.test:runner:1.4.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
} }

View file

@ -21,7 +21,7 @@
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/AppTheme.NoActionBar" android:theme="@style/AppTheme.NoActionBar"
> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />

View file

@ -58,6 +58,12 @@ LINKS
CHANGES CHANGES
Version 2.5 (2022-03-03)
- Allow to load RLE4, RLE8 and monochrome BMP images.
- Optimize the number of draw calls when displaying the LCD pixel borders.
Version 2.4 (2021-12-08) Version 2.4 (2021-12-08)
- Updated source code from Eric Rechlin's Emu48 version 1.63+ that was merged from Christoph Gießelink's Emu48 version 1.64. - Updated source code from Eric Rechlin's Emu48 version 1.63+ that was merged from Christoph Gießelink's Emu48 version 1.64.

View file

@ -2237,7 +2237,7 @@ void StretchBltInternal(int xDest, int yDest, int wDest, int hDest,
// and the color white in the source turns into the destinations background // and the color white in the source turns into the destinations background
// color. // color.
BYTE * sourcePixel = sourcePixelBase + ((UINT)src_curx >> (UINT)3); BYTE * sourcePixel = sourcePixelBase + ((UINT)src_curx >> (UINT)3);
UINT bitNumber = (UINT) (src_curx % 8); UINT bitNumber = (UINT) (7 - (src_curx % 8));
if(*sourcePixel & ((UINT)1 << bitNumber)) { if(*sourcePixel & ((UINT)1 << bitNumber)) {
// Monochrome 1=White // Monochrome 1=White
sourceColorPointer[0] = 255; sourceColorPointer[0] = 255;
@ -2311,7 +2311,7 @@ void StretchBltInternal(int xDest, int yDest, int wDest, int hDest,
// In other words, GDI considers a monochrome bitmap to be // In other words, GDI considers a monochrome bitmap to be
// black pixels on a white background. // black pixels on a white background.
BYTE * destinationPixel = destinationPixelBase + (x >> 3); BYTE * destinationPixel = destinationPixelBase + (x >> 3);
UINT bitNumber = x % 8; UINT bitNumber = (UINT) (7 - (x % 8));
if(backgroundColor == sourceColor) { if(backgroundColor == sourceColor) {
*destinationPixel |= (1 << bitNumber); // 1 White *destinationPixel |= (1 << bitNumber); // 1 White
} else { } else {
@ -2397,6 +2397,180 @@ HBITMAP CreateBitmap( int nWidth, int nHeight, UINT nPlanes, UINT nBitCount, CON
newHBITMAP->bitmapBits = bitmapBits; newHBITMAP->bitmapBits = bitmapBits;
return newHBITMAP; return newHBITMAP;
} }
// RLE decode from Christoph Giesselink in FILES.C from Emu48forPocketPC v125
#define WIDTHBYTES(bits) ((((bits) + 31) / 32) * 4)
typedef struct _BmpFile
{
DWORD dwPos; // actual reading pos
DWORD dwFileSize; // file size
LPBYTE pbyFile; // buffer
} BMPFILE, FAR *LPBMPFILE, *PBMPFILE;
static BOOL ReadRleBmpByte(LPBMPFILE pBmp, BYTE *n)
{
// outside BMP file
if (pBmp->dwPos >= pBmp->dwFileSize)
return TRUE;
*n = pBmp->pbyFile[pBmp->dwPos++];
return FALSE;
}
static BOOL DecodeRleBmp(LPBYTE ppvBits,BITMAPINFOHEADER CONST *lpbi, LPBMPFILE pBmp)
{
BYTE byLength,byColorIndex;
DWORD dwPos,dwRow,dwSizeImage,dwPixelPerLine;
BOOL bDecoding;
_ASSERT(ppvBits != NULL); // destination
_ASSERT(lpbi != NULL); // BITMAPINFOHEADER
_ASSERT(pBmp != NULL); // bitmap data
// valid bit count for RLE bitmaps
_ASSERT(lpbi->biBitCount == 4 || lpbi->biBitCount == 8);
// wrong bit count for compressed bitmap or top-down bitmap
if ((lpbi->biBitCount != 4 && lpbi->biBitCount != 8) || lpbi->biHeight < 0)
return TRUE;
bDecoding = TRUE; // RLE decoder running
dwPos = dwRow = 0; // reset absolute position and row counter
// image size
_ASSERT(lpbi->biHeight >= 0);
dwSizeImage = WIDTHBYTES((DWORD)lpbi->biWidth * lpbi->biBitCount) * lpbi->biHeight;
ZeroMemory(ppvBits,dwSizeImage); // clear bitmap
// image size in pixel
dwSizeImage *= (8 / lpbi->biBitCount);
// no. of pixels per line
dwPixelPerLine = dwSizeImage / lpbi->biHeight;
do
{
// length information is WORD aligned
_ASSERT(((DWORD) &pBmp->pbyFile[pBmp->dwPos] % sizeof(WORD)) == 0);
if (ReadRleBmpByte(pBmp,&byLength)) return TRUE;
if (ReadRleBmpByte(pBmp,&byColorIndex)) return TRUE;
if (byLength) // length information
{
// check for buffer overflow
if (dwPos + byLength > dwSizeImage)
{
// write rest of data until buffer full
byLength = (dwPos > dwSizeImage) ? 0 : (BYTE) (dwSizeImage - dwPos);
bDecoding = FALSE; // abort
}
if (lpbi->biBitCount == 4) // RLE4
{
BYTE byColor[2];
UINT s,d;
// split into upper/lower nibble
byColor[0] = byColorIndex >> 4;
byColor[1] = byColorIndex & 0x0F;
s = 0; // source nibble selector [0/1]
d = (~dwPos & 1) * 4; // destination shift [0/4]
while (byLength-- > 0)
{
// write nibble to memory
_ASSERT((byColor[s] & 0xF0) == 0);
ppvBits[dwPos++/2] |= (byColor[s] << d);
s ^= 1; // next source nibble
d ^= 4; // next destination shift
}
}
else // RLE8
{
while (byLength-- > 0)
ppvBits[dwPos++] = byColorIndex;
}
}
else // escape sequence
{
switch (byColorIndex)
{
case 0: // End of Line
dwPos = ++dwRow * dwPixelPerLine;
break;
case 1: // End of Bitmap
bDecoding = FALSE;
break;
case 2: // Delta
// column offset
if (ReadRleBmpByte(pBmp,&byColorIndex)) return TRUE;
dwPos += byColorIndex;
// row offset
if (ReadRleBmpByte(pBmp,&byColorIndex)) return TRUE;
dwRow += byColorIndex;
dwPos += dwPixelPerLine * byColorIndex;
break;
default: // absolute mode
// check for buffer overflow
if (dwPos + byColorIndex > dwSizeImage)
{
// write rest of data until buffer full
byColorIndex = (dwPos > dwSizeImage) ? 0 : (BYTE) (dwSizeImage - dwPos);
bDecoding = FALSE; // abort
}
if (lpbi->biBitCount == 4) // RLE4
{
BYTE byColor,byColorPair;
UINT s,d;
d = (~dwPos & 1) * 4; // destination shift [0/4]
for (s = 0; s < byColorIndex; ++s)
{
if ((s & 1) == 0) // upper nibble
{
// fetch color pair
if (ReadRleBmpByte(pBmp,&byColorPair)) return TRUE;
// get upper nibble
byColor = (byColorPair >> 4);
}
else // lower nibble
{
// get lower nibble
byColor = (byColorPair & 0x0F);
}
// write nibble to memory
_ASSERT((byColor & 0xF0) == 0);
ppvBits[dwPos++/2] |= (byColor << d);
d ^= 4; // next destination shift
}
// for odd byte length detection
byColorIndex = (++byColorIndex) >> 1;
}
else // RLE8
{
if (pBmp->dwPos + byColorIndex > pBmp->dwFileSize) return TRUE;
CopyMemory(ppvBits+dwPos,&pBmp->pbyFile[pBmp->dwPos],byColorIndex);
dwPos += byColorIndex;
pBmp->dwPos += byColorIndex;
}
// word align on odd byte length
if (byColorIndex & 1) ++pBmp->dwPos;
break;
}
}
}
while (bDecoding);
return FALSE;
}
HBITMAP CreateDIBitmap( HDC hdc, CONST BITMAPINFOHEADER *pbmih, DWORD flInit, CONST VOID *pjBits, CONST BITMAPINFO *pbmi, UINT iUsage) { HBITMAP CreateDIBitmap( HDC hdc, CONST BITMAPINFOHEADER *pbmih, DWORD flInit, CONST VOID *pjBits, CONST BITMAPINFO *pbmi, UINT iUsage) {
PAINT_LOGD("PAINT CreateDIBitmap()"); PAINT_LOGD("PAINT CreateDIBitmap()");
@ -2409,17 +2583,51 @@ HBITMAP CreateDIBitmap( HDC hdc, CONST BITMAPINFOHEADER *pbmih, DWORD flInit, CO
newHBITMAP->bitmapInfo = newBitmapInfo; newHBITMAP->bitmapInfo = newBitmapInfo;
newHBITMAP->bitmapInfoHeader = (BITMAPINFOHEADER *)newBitmapInfo; newHBITMAP->bitmapInfoHeader = (BITMAPINFOHEADER *)newBitmapInfo;
if(flInit == CBM_INIT && pjBits) {
VOID * bitmapBits = NULL;
if(iUsage == DIB_RGB_COLORS) {
switch (pbmi->bmiHeader.biCompression) {
case BI_RLE4:
case BI_RLE8: {
// Destination
BOOL bErr;
newBitmapInfo->bmiHeader.biCompression = BI_RGB;
size_t stride = (size_t)(4 * ((newBitmapInfo->bmiHeader.biWidth * newBitmapInfo->bmiHeader.biBitCount + 31) / 32));
size_t size = abs(newBitmapInfo->bmiHeader.biHeight) * stride;
bitmapBits = malloc(size);
BMPFILE Bmp;
int bitOffset = sizeof(BITMAPINFOHEADER) + (pbmi->bmiHeader.biCompression == BI_BITFIELDS ? 3 * sizeof(DWORD) : DibNumColors(&pbmi->bmiHeader) * sizeof(RGBQUAD));
Bmp.pbyFile = ((LPBYTE)&pbmi->bmiHeader) + bitOffset;
Bmp.dwPos = 0;
Bmp.dwFileSize = -1; //size;
bErr = DecodeRleBmp(
/* Destination */ bitmapBits,
/* Destination header */ &newBitmapInfo->bmiHeader,
/* Source */ &Bmp);
break;
}
case BI_RGB:
case BI_BITFIELDS: {
// We consider the source and destination dib with the same format
size_t stride = (size_t)(4 * ((newBitmapInfo->bmiHeader.biWidth * newBitmapInfo->bmiHeader.biBitCount + 31) / 32)); size_t stride = (size_t)(4 * ((newBitmapInfo->bmiHeader.biWidth * newBitmapInfo->bmiHeader.biBitCount + 31) / 32));
size_t size = newBitmapInfo->bmiHeader.biSizeImage ? size_t size = newBitmapInfo->bmiHeader.biSizeImage ?
newBitmapInfo->bmiHeader.biSizeImage : newBitmapInfo->bmiHeader.biSizeImage :
abs(newBitmapInfo->bmiHeader.biHeight) * stride; abs(newBitmapInfo->bmiHeader.biHeight) * stride;
VOID * bitmapBits = malloc(size); bitmapBits = malloc(size);
memcpy(bitmapBits, pjBits, size); memcpy(bitmapBits, pjBits, size);
break;
}
}
}
newHBITMAP->bitmapBits = bitmapBits; newHBITMAP->bitmapBits = bitmapBits;
}
return newHBITMAP; return newHBITMAP;
} }
HBITMAP CreateDIBSection(HDC hdc, CONST BITMAPINFO *pbmi, UINT usage, VOID **ppvBits, HANDLE hSection, DWORD offset) { HBITMAP CreateDIBSection(HDC hdc, CONST BITMAPINFO *pbmi, UINT usage, VOID **ppvBits, HANDLE hSection, DWORD offset) {
PAINT_LOGD("PAINT CreateDIBitmap()"); PAINT_LOGD("PAINT CreateDIBSection()");
HGDIOBJ newHBITMAP = (HGDIOBJ)malloc(sizeof(_HGDIOBJ)); HGDIOBJ newHBITMAP = (HGDIOBJ)malloc(sizeof(_HGDIOBJ));
memset(newHBITMAP, 0, sizeof(_HGDIOBJ)); memset(newHBITMAP, 0, sizeof(_HGDIOBJ));
@ -2445,7 +2653,7 @@ HBITMAP CreateDIBSection(HDC hdc, CONST BITMAPINFO *pbmi, UINT usage, VOID **ppv
return newHBITMAP; return newHBITMAP;
} }
HBITMAP CreateCompatibleBitmap( HDC hdc, int cx, int cy) { HBITMAP CreateCompatibleBitmap( HDC hdc, int cx, int cy) {
PAINT_LOGD("PAINT CreateDIBitmap()"); PAINT_LOGD("PAINT CreateCompatibleBitmap()");
HGDIOBJ newHBITMAP = (HGDIOBJ)malloc(sizeof(_HGDIOBJ)); HGDIOBJ newHBITMAP = (HGDIOBJ)malloc(sizeof(_HGDIOBJ));
memset(newHBITMAP, 0, sizeof(_HGDIOBJ)); memset(newHBITMAP, 0, sizeof(_HGDIOBJ));
@ -2469,6 +2677,7 @@ HBITMAP CreateCompatibleBitmap( HDC hdc, int cx, int cy) {
return newHBITMAP; return newHBITMAP;
} }
int GetDIBits(HDC hdc, HBITMAP hbm, UINT start, UINT cLines, LPVOID lpvBits, LPBITMAPINFO lpbmi, UINT usage) { int GetDIBits(HDC hdc, HBITMAP hbm, UINT start, UINT cLines, LPVOID lpvBits, LPBITMAPINFO lpbmi, UINT usage) {
PAINT_LOGD("PAINT GetDIBits()");
//TODO Not sure at all for this function //TODO Not sure at all for this function
if(hbm && lpbmi) { if(hbm && lpbmi) {
CONST BITMAPINFO *pbmi = hbm->bitmapInfo; CONST BITMAPINFO *pbmi = hbm->bitmapInfo;

View file

@ -811,6 +811,7 @@ typedef struct _RGNDATA {
char Buffer[1]; char Buffer[1];
} RGNDATA, *PRGNDATA, NEAR *NPRGNDATA, FAR *LPRGNDATA; } RGNDATA, *PRGNDATA, NEAR *NPRGNDATA, FAR *LPRGNDATA;
extern int GetDIBits(HDC hdc, HBITMAP hbm, UINT start, UINT cLines, LPVOID lpvBits, LPBITMAPINFO lpbmi, UINT usage); extern int GetDIBits(HDC hdc, HBITMAP hbm, UINT start, UINT cLines, LPVOID lpvBits, LPBITMAPINFO lpbmi, UINT usage);
extern int SetDIBits(HDC hdc, HBITMAP hbm, UINT start, UINT cLines, const VOID *lpBits, const BITMAPINFO *lpbmi, UINT ColorUse);
extern COLORREF GetPixel(HDC hdc, int x ,int y); extern COLORREF GetPixel(HDC hdc, int x ,int y);
extern HPALETTE SelectPalette(HDC hdc, HPALETTE hPal, BOOL bForceBkgd); extern HPALETTE SelectPalette(HDC hdc, HPALETTE hPal, BOOL bForceBkgd);
extern UINT RealizePalette(HDC hdc); extern UINT RealizePalette(HDC hdc);

View file

@ -256,7 +256,7 @@ public class MainScreenView extends PanAndScaleView {
@Override @Override
public boolean onKeyDown(int keyCode, KeyEvent event) { public boolean onKeyDown(int keyCode, KeyEvent event) {
if((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) == 0 if((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) == 0
&& (event.getSource() & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) { && ((event.getSource() & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) || event.getSource() == 0) {
if(!event.isNumLockOn() && numpadKey.indexOf(keyCode) != -1) if(!event.isNumLockOn() && numpadKey.indexOf(keyCode) != -1)
return false; return false;
char pressedKey = (char) event.getUnicodeChar(); char pressedKey = (char) event.getUnicodeChar();
@ -277,7 +277,7 @@ public class MainScreenView extends PanAndScaleView {
@Override @Override
public boolean onKeyUp(int keyCode, KeyEvent event) { public boolean onKeyUp(int keyCode, KeyEvent event) {
if((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) == 0 if((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) == 0
&& (event.getSource() & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) { && ((event.getSource() & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) || event.getSource() == 0) {
if(!event.isNumLockOn() && numpadKey.indexOf(keyCode) != -1) if(!event.isNumLockOn() && numpadKey.indexOf(keyCode) != -1)
return false; return false;
char pressedKey = (char) event.getUnicodeChar(); char pressedKey = (char) event.getUnicodeChar();
@ -451,20 +451,36 @@ public class MainScreenView extends PanAndScaleView {
} }
} }
static float[] pointsBuffer = new float[0];
static void drawPixelBorder(Canvas canvas, int lcdWidthNative, int lcdHeightNative, float screenPositionX, float screenPositionY, float screenWidth, float screenHeight, Paint paintLCD) { static void drawPixelBorder(Canvas canvas, int lcdWidthNative, int lcdHeightNative, float screenPositionX, float screenPositionY, float screenWidth, float screenHeight, Paint paintLCD) {
// Draw the LCD grid lines without antialiasing to emulate the genuine pixels borders // Draw the LCD grid lines without antialiasing to emulate the genuine pixels borders
int lcdBackgroundColor = 0xFF000000 | NativeLib.getLCDBackgroundColor(); int lcdBackgroundColor = 0xFF000000 | NativeLib.getLCDBackgroundColor();
paintLCD.setColor(lcdBackgroundColor); paintLCD.setColor(lcdBackgroundColor);
float stepX = screenWidth / lcdWidthNative; float stepX = screenWidth / lcdWidthNative;
// Optimized drawcalls
int pointBufferLength = (lcdWidthNative + lcdHeightNative) << 2;
if(pointsBuffer.length != pointBufferLength)
pointsBuffer = new float[pointBufferLength]; // Adjust the buffer of points
int pointsIndex = 0;
for (int x = 0; x < lcdWidthNative; x++) { for (int x = 0; x < lcdWidthNative; x++) {
float screenX = screenPositionX + x * stepX; float screenX = screenPositionX + x * stepX;
canvas.drawLine(screenX, screenPositionY, screenX, screenPositionY + screenHeight, paintLCD); pointsBuffer[pointsIndex++] = screenX;
pointsBuffer[pointsIndex++] = screenPositionY;
pointsBuffer[pointsIndex++] = screenX;
pointsBuffer[pointsIndex++] = screenPositionY + screenHeight;
} }
float stepY = screenHeight / lcdHeightNative; float stepY = screenHeight / lcdHeightNative;
for (int y = 0; y < lcdHeightNative; y++) { for (int y = 0; y < lcdHeightNative; y++) {
float screenY = screenPositionY + y * stepY; float screenY = screenPositionY + y * stepY;
canvas.drawLine(screenPositionX, screenY, screenPositionX + screenWidth, screenY, paintLCD); pointsBuffer[pointsIndex++] = screenPositionX;
pointsBuffer[pointsIndex++] = screenY;
pointsBuffer[pointsIndex++] = screenPositionX + screenWidth;
pointsBuffer[pointsIndex++] = screenY;
} }
canvas.drawLines(pointsBuffer, paintLCD);
} }
public void updateCallback(int type, int param1, int param2, String param3, String param4) { public void updateCallback(int type, int param1, int param2, String param3, String param4) {

View file

@ -169,6 +169,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
ViewGroup mainScreenContainer = findViewById(R.id.main_screen_container); ViewGroup mainScreenContainer = findViewById(R.id.main_screen_container);
mainScreenView = new MainScreenView(this); mainScreenView = new MainScreenView(this);
mainScreenView.setId(R.id.main_screen_view);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
mainScreenView.setStatusBarColor(getWindow().getStatusBarColor()); mainScreenView.setStatusBarColor(getWindow().getStatusBarColor());
mainScreenContainer.addView(mainScreenView, 0, new ViewGroup.LayoutParams( mainScreenContainer.addView(mainScreenView, 0, new ViewGroup.LayoutParams(
@ -437,6 +438,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
boolean bStackCEnable = bObjectEnable; boolean bStackCEnable = bObjectEnable;
boolean bStackPEnable = bObjectEnable; boolean bStackPEnable = bObjectEnable;
menu.findItem(R.id.nav_manage_flash_rom).setEnabled(uRun && (cCurrentRomType == 'X' || cCurrentRomType == 'Q'));
menu.findItem(R.id.nav_save).setEnabled(uRun); menu.findItem(R.id.nav_save).setEnabled(uRun);
menu.findItem(R.id.nav_save_as).setEnabled(uRun); menu.findItem(R.id.nav_save_as).setEnabled(uRun);
menu.findItem(R.id.nav_close).setEnabled(uRun); menu.findItem(R.id.nav_close).setEnabled(uRun);
@ -451,7 +454,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
menu.findItem(R.id.nav_backup_delete).setEnabled(uRun && isBackup); menu.findItem(R.id.nav_backup_delete).setEnabled(uRun && isBackup);
menu.findItem(R.id.nav_change_kml_script).setEnabled(uRun); menu.findItem(R.id.nav_change_kml_script).setEnabled(uRun);
menu.findItem(R.id.nav_show_kml_script_compilation_result).setEnabled(uRun); menu.findItem(R.id.nav_show_kml_script_compilation_result).setEnabled(uRun);
menu.findItem(R.id.nav_manage_flash_rom).setEnabled(uRun && (cCurrentRomType == 'X' || cCurrentRomType == 'Q'));
menu.findItem(R.id.nav_macro_record).setEnabled(uRun && nMacroState == 0 /* MACRO_OFF */); menu.findItem(R.id.nav_macro_record).setEnabled(uRun && nMacroState == 0 /* MACRO_OFF */);
menu.findItem(R.id.nav_macro_play).setEnabled(uRun && nMacroState == 0 /* MACRO_OFF */); menu.findItem(R.id.nav_macro_play).setEnabled(uRun && nMacroState == 0 /* MACRO_OFF */);
menu.findItem(R.id.nav_macro_stop).setEnabled(uRun && nMacroState != 0 /* MACRO_OFF */); menu.findItem(R.id.nav_macro_stop).setEnabled(uRun && nMacroState != 0 /* MACRO_OFF */);
@ -844,7 +846,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
} else } else
openDocument(); openDocument();
} }
private void OnObjectSave() { private void SaveObject() {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE); intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*"); intent.setType("*/*");
@ -852,6 +854,23 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
saveWhenLaunchingActivity = false; saveWhenLaunchingActivity = false;
startActivityForResult(intent, INTENT_OBJECT_SAVE); startActivityForResult(intent, INTENT_OBJECT_SAVE);
} }
private void OnObjectSave() {
int model = NativeLib.getCurrentModel();
if(model == 'L' // HP32S # Leonardo
|| model == 'N' // HP32SII # Nardo
|| model == 'D') { // HP42S # Davinci
final String[] objectsToSave = NativeLib.getObjectsToSave();
objectsToSaveItemChecked = new boolean[objectsToSave.length];
new AlertDialog.Builder(MainActivity.this)
.setTitle(getResources().getString(R.string.message_object_save_program))
.setMultiChoiceItems(objectsToSave, null, (dialog, which, isChecked) -> objectsToSaveItemChecked[which] = isChecked).setPositiveButton("OK", (dialog, id) -> {
//NativeLib.onObjectSave(url);
SaveObject();
}).setNegativeButton("Cancel", (dialog, id) -> objectsToSaveItemChecked = null).show();
} else
SaveObject(); // Others calculators
}
private void OnViewCopyFullscreen() { private void OnViewCopyFullscreen() {
Bitmap bitmapScreen = mainScreenView.getBitmap(); Bitmap bitmapScreen = mainScreenView.getBitmap();
if(bitmapScreen == null) if(bitmapScreen == null)
@ -1222,7 +1241,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
} }
case INTENT_OBJECT_SAVE: { case INTENT_OBJECT_SAVE: {
if(debug) Log.d(TAG, "onActivityResult INTENT_OBJECT_SAVE " + url); if(debug) Log.d(TAG, "onActivityResult INTENT_OBJECT_SAVE " + url);
NativeLib.onObjectSave(url, null); NativeLib.onObjectSave(url, objectsToSaveItemChecked);
objectsToSaveItemChecked = null;
break; break;
} }
case INTENT_PORT2LOAD: { case INTENT_PORT2LOAD: {
@ -1544,7 +1564,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
} }
} }
if(settings.getBoolean("settings_port2en", false)) { if(getPackageName().contains("org.emulator.forty.eight") && settings.getBoolean("settings_port2en", false)) {
// Check if the access to the port2 shared file is still possible. // Check if the access to the port2 shared file is still possible.
String port2Url = settings.getString("settings_port2load", ""); String port2Url = settings.getString("settings_port2load", "");
if(port2Url != null && port2Url.length() > 0) { if(port2Url != null && port2Url.length() > 0) {
@ -2201,9 +2221,9 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
mainScreenView.setRotationMode(rotationMode, isDynamic); mainScreenView.setRotationMode(rotationMode, isDynamic);
break; break;
case "settings_auto_layout": case "settings_auto_layout":
int autoLayoutMode = 1; int autoLayoutMode = getPackageName().contains("org.emulator.forty.eight") ? 1 : 2;
try { try {
autoLayoutMode = Integer.parseInt(settings.getString("settings_auto_layout", "1")); autoLayoutMode = Integer.parseInt(settings.getString("settings_auto_layout", String.valueOf(autoLayoutMode)));
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
// Catch bad number format // Catch bad number format
} }

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="main_screen_view"/>
</resources>

View file

@ -7,7 +7,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.0.4' classpath 'com.android.tools.build:gradle:7.1.2'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong

View file

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip