Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Emulator/Screen/TIOSScreenManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,17 @@ class TIOSScreenManager
return mDataProviderRef;
}

///
/// Accessor to the image buffer.
///
/// \return a pointer to the image buffer.
///
KUInt32*
GetImageBuffer(void)
{
return mImageBuffer;
}
Comment on lines +165 to +174

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make GetImageBuffer() contract explicit (lifetime/thread-safety) and prefer read-only access by default.

Returning a raw mutable KUInt32* leaks internal state: callers can accidentally write into the buffer, hold onto it across resizes/power transitions, or race with emulator rendering. At minimum, document ownership/nullability and whether the pointer can change; ideally expose a const getter and add an explicit mutable accessor only if you truly need external writes.

-    KUInt32*
-    GetImageBuffer(void)
-    {
-        return mImageBuffer;
-    }
+    /// Read-only access; pointer is owned by TIOSScreenManager and may become
+    /// invalid after resolution/layout changes or PowerOff.
+    const KUInt32*
+    GetImageBuffer(void) const
+    {
+        return mImageBuffer;
+    }
+
+    /// Mutable access (only if required by the rendering pipeline).
+    KUInt32*
+    GetMutableImageBuffer(void)
+    {
+        return mImageBuffer;
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
///
/// Accessor to the image buffer.
///
/// \return a pointer to the image buffer.
///
KUInt32*
GetImageBuffer(void)
{
return mImageBuffer;
}
/// Read-only access; pointer is owned by TIOSScreenManager and may become
/// invalid after resolution/layout changes or PowerOff.
const KUInt32*
GetImageBuffer(void) const
{
return mImageBuffer;
}
/// Mutable access (only if required by the rendering pipeline).
KUInt32*
GetMutableImageBuffer(void)
{
return mImageBuffer;
}
🤖 Prompt for AI Agents
In Emulator/Screen/TIOSScreenManager.h around lines 165 to 174, the
GetImageBuffer() accessor returns a raw mutable KUInt32* exposing internal
state; change it to return a const KUInt32* by default, add a separate
clearly-named mutable accessor only if external mutation is required, and
document ownership, nullability, lifetime (e.g., “pointer valid until buffer
resize/power state change”) and thread-safety (e.g., caller must hold render
lock or perform copies) in the method comments; if you need to support safe
slices, consider returning a span-like view or a pair {pointer, size} instead of
a bare pointer.


///
/// Insert a sample with pen down.
///
Expand Down
4 changes: 2 additions & 2 deletions Resources/iOSEinstein Storyboards-Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@
<false/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
</dict>
</plist>
9 changes: 5 additions & 4 deletions app/iEinstein/Classes/iEinsteinAppDelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ @implementation iEinsteinAppDelegate
+ (void)initialize
{
NSDictionary* defaults = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:0], @"screen_resolution",
[NSNumber numberWithBool:NO], @"clear_flash_ram",
@"{8,9.88897}", @"printable_size",
nil];
[NSNumber numberWithInt:0], @"screen_resolution",
[NSNumber numberWithBool:NO], @"use_full_screen",
[NSNumber numberWithBool:NO], @"clear_flash_ram",
@"{8,9.88897}", @"printable_size",
nil];

[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
[[NSUserDefaults standardUserDefaults] synchronize];
Expand Down
3 changes: 2 additions & 1 deletion app/iEinstein/Classes/iEinsteinView.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ class TEmulator;
// EOrientation mOrientation;
// KUInt32 mPreviousMods;
bool applePencilMode;
bool useFullScreen;
}

- (void)reset;
- (void)setScreenManager:(TScreenManager*)sm;
- (void)setScreenManager:(TScreenManager*)sm useFullScreen:(BOOL)fullScreen;

- (void)setEmulator:(TEmulator*)em;

Expand Down
117 changes: 75 additions & 42 deletions app/iEinstein/Classes/iEinsteinView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,10 @@ - (void)didRotate:(NSNotification*)notification
[self setNeedsDisplay];
}

- (void)setScreenManager:(TScreenManager*)sm
- (void)setScreenManager:(TScreenManager*)sm useFullScreen:(BOOL)fullScreen
{
mScreenManager = sm;
useFullScreen = fullScreen;
[self setNeedsDisplay];
}

Expand Down Expand Up @@ -116,14 +117,14 @@ - (void)layoutSubviews
- (void)drawRect:(CGRect)rect
{
CGContextRef theContext = UIGraphicsGetCurrentContext();
CGRect bounds = [self bounds];

if (mScreenManager == NULL)
{
// Just fill black
// Fill with black when no screen manager
CGFloat black[] = { 0.0, 0.0, 0.0, 1.0 };
CGRect frame = [self frame];
CGContextSetFillColor(theContext, black);
CGContextFillRect(theContext, frame);
CGContextFillRect(theContext, bounds);
} else
Comment on lines 122 to 128

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Also set superview background when mScreenManager == NULL.
Right now you fill self black, but safe areas outside this view can keep the previous background until a later draw.

   if (mScreenManager == NULL)
   {
+    if (self.superview) self.superview.backgroundColor = [UIColor blackColor];
     // Fill with black when no screen manager
     CGFloat black[] = { 0.0, 0.0, 0.0, 1.0 };
     CGContextSetFillColor(theContext, black);
     CGContextFillRect(theContext, bounds);
   }
🤖 Prompt for AI Agents
In app/iEinstein/Classes/iEinsteinView.mm around lines 122 to 128, when
mScreenManager == NULL you only fill this view black but leave the superview’s
safe-area/background unchanged; update the code to also set the superview’s
background to black (check superview != nil) — e.g. set
superview.backgroundColor to black (or its equivalent NSColor/UIColor), then
fill this view as before and optionally call setNeedsDisplay on the superview so
the change renders immediately.

{
if (mScreenImage == NULL)
Expand All @@ -144,59 +145,91 @@ - (void)drawRect:(CGRect)rect

CGColorSpaceRelease(theColorSpace);

// CGRect screenBounds = [[UIScreen mainScreen] bounds]; //OLD
CGRect screenBounds = [self bounds];
CGRect r = [self frame];

if (screenBounds.size.width > newtonScreenWidth && screenBounds.size.height > newtonScreenHeight)
{
if (newtonScreenWidth == newtonScreenHeight)
if (useFullScreen) {
// Full screen mode: leave room for border on all sides
// On iPad with 2x scaling, double the padding to maintain proportions
CGFloat borderPadding = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) ? 30.0 : 15.0;
screenImageRect = CGRectInset(bounds, borderPadding, borderPadding);
} else {
// Classic mode: letterbox/center the Newton screen with integer scaling
CGRect screenBounds = bounds;
CGRect r = bounds;

if (screenBounds.size.width > newtonScreenWidth && screenBounds.size.height > newtonScreenHeight)
{
// Newton screen resolution is square (like 320x320)

int mod = (int) screenBounds.size.width % newtonScreenWidth;
r.size.width -= mod;
r.size.height = r.size.width;
} else
{
// Newton screen resolution is rectangular (like 320x480)

int wmod = (int) r.size.width % newtonScreenWidth;
int hmod = (int) r.size.height % newtonScreenHeight;

if (wmod > hmod)
if (newtonScreenWidth == newtonScreenHeight)
{
r.size.width -= wmod;

int scale = (int) r.size.width / newtonScreenWidth;
r.size.height = newtonScreenHeight * scale;
// Newton screen resolution is square (like 320x320)
int mod = (int) screenBounds.size.width % newtonScreenWidth;
r.size.width -= mod;
r.size.height = r.size.width;
} else
{
r.size.height -= hmod;

int scale = (int) r.size.height / newtonScreenHeight;
r.size.width = newtonScreenWidth * scale;
// Newton screen resolution is rectangular (like 320x480)
int wmod = (int) r.size.width % newtonScreenWidth;
int hmod = (int) r.size.height % newtonScreenHeight;

if (wmod > hmod)
{
r.size.width -= wmod;
int scale = (int) r.size.width / newtonScreenWidth;
r.size.height = newtonScreenHeight * scale;
} else
{
r.size.height -= hmod;
int scale = (int) r.size.height / newtonScreenHeight;
r.size.width = newtonScreenWidth * scale;
}
}
}

// Center image on screen
// Center image on screen
r.origin.x += (screenBounds.size.width - r.size.width) / 2;
r.origin.y += (screenBounds.size.height - r.size.height) / 2;
}

r.origin.x += (screenBounds.size.width - r.size.width) / 2;
r.origin.y += (screenBounds.size.height - r.size.height) / 2;
screenImageRect = r;
screenImageRect.origin.x = floor(screenImageRect.origin.x);
screenImageRect.origin.y = floor(screenImageRect.origin.y);
screenImageRect.size.width = floor(screenImageRect.size.width);
screenImageRect.size.height = floor(screenImageRect.size.height);
}
Comment on lines +148 to 195

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fullscreen rect should preserve Newton aspect ratio (avoid subtle stretching).
screenImageRect = CGRectInset(bounds, …) can distort if screenImageRect ratio doesn’t match newtonScreenWidth/newtonScreenHeight (padding changes ratio; and any future orientation/buffer change will too).

-      CGFloat borderPadding = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) ? 30.0 : 15.0;
-      screenImageRect = CGRectInset(bounds, borderPadding, borderPadding);
+      CGFloat borderPadding = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) ? 30.0 : 15.0;
+      CGRect avail = CGRectInset(bounds, borderPadding, borderPadding);
+      const CGFloat sx = avail.size.width  / (CGFloat)newtonScreenWidth;
+      const CGFloat sy = avail.size.height / (CGFloat)newtonScreenHeight;
+      const CGFloat s = MIN(sx, sy); // aspect-fit
+      CGSize sz = CGSizeMake(floor(newtonScreenWidth * s), floor(newtonScreenHeight * s));
+      screenImageRect = CGRectMake(
+        floor(avail.origin.x + (avail.size.width  - sz.width)  / 2.0),
+        floor(avail.origin.y + (avail.size.height - sz.height) / 2.0),
+        sz.width, sz.height
+      );
🤖 Prompt for AI Agents
In app/iEinstein/Classes/iEinsteinView.mm around lines 148 to 195, the
fullscreen branch currently uses CGRectInset(bounds, ...) which can change the
aspect ratio and subtly stretch the Newton image; instead compute the available
area as the inset bounds, then fit a rect into that area that preserves the
Newton aspect ratio (newtonScreenWidth/newtonScreenHeight): calculate target
width/height by comparing availableAspect to newtonAspect, choose width-limited
or height-limited scaling, floor the resulting origin/size to integers, center
the rect in the available area, and assign that to screenImageRect so fullscreen
never distorts the Newton aspect ratio.


screenImageRect = r;
screenImageRect.origin.x = floor(screenImageRect.origin.x);
screenImageRect.origin.y = floor(screenImageRect.origin.y);
screenImageRect.size.width = floor(screenImageRect.size.width);
screenImageRect.size.height = floor(screenImageRect.size.height);
}

// Draw the background of this view and the superview
[self drawBackgroundInContext:theContext withBounds:bounds];

CGContextSetInterpolationQuality(theContext, kCGInterpolationNone);
CGContextDrawImage(theContext, screenImageRect, mScreenImage);
}
}

- (void)drawBackgroundInContext:(CGContextRef)context withBounds:(CGRect)bounds
{
static UIColor *defaultBgColor = [UIColor colorWithRed:(176/255.0) green:(191/255.0) blue:(169/255.0) alpha:1.0];
static UIColor *fullScreenBgColor = [UIColor blackColor];

// In classic mode, we use the storyboard's color (a muted green).
// In full-screen mode, we fill background with black.
UIColor* bgColor = useFullScreen ? fullScreenBgColor : defaultBgColor;

if (mScreenManager == NULL || newtonScreenWidth == 0 || newtonScreenHeight == 0) {
if (self.superview) {
self.superview.backgroundColor = bgColor;
}
return;
}

// Set superview background to match (fills safe areas on notched devices)
if (self.superview) {
self.superview.backgroundColor = bgColor;
}

// Fill the entire view bounds with the background color
// The Newton screen image will be drawn on top
CGContextSetFillColorWithColor(context, bgColor.CGColor);
CGContextFillRect(context, bounds);
}

- (void)reset
{
mEmulator = NULL;
Expand Down
2 changes: 2 additions & 0 deletions app/iEinstein/Classes/iEinsteinViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ class TLog;
TEmulator* mEmulator;
TPlatformManager* mPlatformManager;
TLog* mLog;
BOOL mEmulatorInitialized;
int lastKnownScreenResolution;
BOOL lastKnownFullScreenMode;
}

@property (retain, nonatomic) IBOutlet iEinsteinView* einsteinView;
Expand Down
101 changes: 77 additions & 24 deletions app/iEinstein/Classes/iEinsteinViewController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,26 @@ @implementation iEinsteinViewController
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
#ifdef USE_STORYBOARDS
[self initEmulator];
#endif
// Initialization moved to viewDidLayoutSubviews to ensure correct bounds
}

- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}

- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
#ifdef USE_STORYBOARDS
// Only initialize once, after layout is complete so we have correct bounds
if (!mEmulatorInitialized) {
mEmulatorInitialized = YES;
[self initEmulator];
}
#endif
}
Comment on lines 44 to +65

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix init regression for non-storyboard builds (emulator may never initialize).
-viewDidLayoutSubviews only calls initEmulator under #ifdef USE_STORYBOARDS, while -viewWillAppear: no longer initializes. If USE_STORYBOARDS is off, startup can skip initialization entirely.

- (void)viewDidLayoutSubviews
+ (void)viewDidLayoutSubviews
 {
   [super viewDidLayoutSubviews];
-#ifdef USE_STORYBOARDS
   // Only initialize once, after layout is complete so we have correct bounds
   if (!mEmulatorInitialized) {
     mEmulatorInitialized = YES;
     [self initEmulator];
   }
-#endif
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
#ifdef USE_STORYBOARDS
[self initEmulator];
#endif
// Initialization moved to viewDidLayoutSubviews to ensure correct bounds
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
#ifdef USE_STORYBOARDS
// Only initialize once, after layout is complete so we have correct bounds
if (!mEmulatorInitialized) {
mEmulatorInitialized = YES;
[self initEmulator];
}
#endif
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
// Only initialize once, after layout is complete so we have correct bounds
if (!mEmulatorInitialized) {
mEmulatorInitialized = YES;
[self initEmulator];
}
}
🤖 Prompt for AI Agents
In app/iEinstein/Classes/iEinsteinViewController.mm around lines 44 to 65, the
emulator initialization was moved to viewDidLayoutSubviews but is wrapped in
#ifdef USE_STORYBOARDS so non-storyboard builds never call initEmulator; restore
a one-time initialization path for non-storyboard builds by calling initEmulator
(guarded by the same mEmulatorInitialized flag) from viewWillAppear when
USE_STORYBOARDS is not defined, or remove the #ifdef so the existing
viewDidLayoutSubviews initialization runs for all builds—ensure the init is only
performed once.


// Action sheet delegate method.
- (void)actionSheet:(UIActionSheet*)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
Expand Down Expand Up @@ -326,25 +336,60 @@ - (int)initEmulator

mSoundManager = new TCoreAudioSoundManager(mLog);

// iPad is 1024x768. This size, and some appropriate scaling factors, should be selectable from
// the 'Settings' panel.

static int widthLUT[] = { 320, 640, 384, 786, 640, 320, 750, 375, 1080, 540 };
static int heightLUT[] = { 480, 960, 512, 1024, 1136, 568, 1134, 567, 1920, 960 };

NSUserDefaults* prefs = [NSUserDefaults standardUserDefaults];
int index = [(NSNumber*) [prefs objectForKey:@"screen_resolution"] intValue];
int newtonScreenWidth = widthLUT[index];
int newtonScreenHeight = heightLUT[index];

#ifdef USE_STORYBOARDS
// When using storyboards as configured, the einsteinView is a subview of self.view
// This facilitates other subviews in the future.
iEinsteinView* einsteinView = self.einsteinView;
#else
// When using NIBs, the einsteinView is self.view
iEinsteinView* einsteinView = (iEinsteinView*) [self view];
#endif

// Determine screen resolution based on use_full_screen setting
NSUserDefaults* prefs = [NSUserDefaults standardUserDefaults];
BOOL useFullScreen = [(NSNumber*) [prefs objectForKey:@"use_full_screen"] boolValue];
int newtonScreenWidth, newtonScreenHeight;

if (useFullScreen) {
// Full screen mode: use native view bounds for resolution
CGRect viewBounds = [einsteinView bounds];

// Use point dimensions (not pixels) for the Newton resolution.
// The Newton is always in portrait orientation internally (width < height).
int viewWidth = (int)viewBounds.size.width;
int viewHeight = (int)viewBounds.size.height;

// Ensure width is the smaller dimension (portrait mode for Newton)
if (viewWidth < viewHeight) {
newtonScreenWidth = viewWidth;
newtonScreenHeight = viewHeight;
} else {
newtonScreenWidth = viewHeight;
newtonScreenHeight = viewWidth;
}

// On iPad, use 2x scaling (halve resolution, scale up when drawing)
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
newtonScreenWidth /= 2.0;
newtonScreenHeight /= 2.0;
} else {
newtonScreenWidth /= 1.25;
newtonScreenHeight /= 1.25;
}

// Round width to an even number (required for pixel operations)
newtonScreenWidth = (newtonScreenWidth + 1) & ~1;
} else {
// Classic mode: use LUT-based resolution from Settings
static int widthLUT[] = { 320, 640, 384, 786, 640, 320, 750, 375, 1080, 540 };
static int heightLUT[] = { 480, 960, 512, 1024, 1136, 568, 1134, 567, 1920, 960 };

int index = [(NSNumber*) [prefs objectForKey:@"screen_resolution"] intValue];
newtonScreenWidth = widthLUT[index];
newtonScreenHeight = heightLUT[index];
}

// Store current settings for change detection
lastKnownFullScreenMode = useFullScreen;
lastKnownScreenResolution = [(NSNumber*) [prefs objectForKey:@"screen_resolution"] intValue];

Comment on lines +345 to +392

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Clamp screen_resolution index before LUT access to avoid OOB.
If Settings ever stores an unexpected value, widthLUT[index] / heightLUT[index] can read out of bounds.

-    int index = [(NSNumber*) [prefs objectForKey:@"screen_resolution"] intValue];
-    newtonScreenWidth = widthLUT[index];
-    newtonScreenHeight = heightLUT[index];
+    int index = [(NSNumber*) [prefs objectForKey:@"screen_resolution"] intValue];
+    const int lutSize = (int)(sizeof(widthLUT) / sizeof(widthLUT[0]));
+    if (index < 0) index = 0;
+    if (index >= lutSize) index = lutSize - 1;
+    newtonScreenWidth = widthLUT[index];
+    newtonScreenHeight = heightLUT[index];
🤖 Prompt for AI Agents
In app/iEinstein/Classes/iEinsteinViewController.mm around lines 345 to 392, the
code reads screen_resolution into an index and directly indexes
widthLUT/heightLUT which can go out-of-bounds; clamp the index to the valid
range before LUT access by computing maxIndex =
(sizeof(widthLUT)/sizeof(widthLUT[0])) - 1, read the stored value into an
NSInteger, then set index = MAX(0, MIN(index, maxIndex)); use that clamped index
for widthLUT[index] and heightLUT[index], and store the clamped value in
lastKnownScreenResolution.

Boolean isLandscape = (newtonScreenWidth > newtonScreenHeight);

mScreenManager = new TIOSScreenManager(
Expand All @@ -355,7 +400,7 @@ - (int)initEmulator
true,
isLandscape);

[einsteinView setScreenManager:mScreenManager];
[einsteinView setScreenManager:mScreenManager useFullScreen:useFullScreen];

// Create the printer manager.
mPrinterManager = new TIOSPrinterManager(mLog);
Expand Down Expand Up @@ -386,23 +431,31 @@ - (int)initEmulator

- (void)startEmulator
{
// See if screen resolution has changed since last time

// Check if screen settings have changed since last time
NSUserDefaults* prefs = [NSUserDefaults standardUserDefaults];
[prefs synchronize];

Comment on lines +434 to +437

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

NSUserDefaults synchronize deprecated iOS modern alternative

💡 Result:

Short answer: don't call synchronize — it's unnecessary and deprecated. Use UserDefaults (UserDefaults.standard or UserDefaults(suiteName:)) normally; changes are written asynchronously by the system. For cross-device sync use NSUbiquitousKeyValueStore; for shared-app-group storage use UserDefaults(suiteName:). There is no supported API to forcibly flush writes on iOS — design so you don't depend on immediate disk writes. [1][2][3]

Sources:

  • Apple UserDefaults documentation (synchronize: “unnecessary and shouldn't be used”). [1]
  • iOS 12 release notes: “Removed synchronization requirements… these methods will be deprecated”. [2]
  • Guidance / practical details and migration notes. [3]

Remove deprecated [NSUserDefaults synchronize] call.

The synchronize method is deprecated and unnecessary on modern iOS. The system automatically writes changes to NSUserDefaults asynchronously. If you need cross-device synchronization, use NSUbiquitousKeyValueStore; for app-group shared storage, use UserDefaults(suiteName:). Simply remove the synchronize call and rely on normal NSUserDefaults reads.

🤖 Prompt for AI Agents
In app/iEinstein/Classes/iEinsteinViewController.mm around lines 434 to 437,
remove the deprecated call to [NSUserDefaults synchronize]; it is
unnecessary—just read from [NSUserDefaults standardUserDefaults] (or migrate to
NSUbiquitousKeyValueStore or UserDefaults(suiteName:) if cross-device or
app-group sync is required) and delete the synchronize line so the code relies
on the system-managed asynchronous persistence.

BOOL currentFullScreenMode = [(NSNumber*) [prefs objectForKey:@"use_full_screen"] boolValue];
int currentScreenResolution = [(NSNumber*) [prefs objectForKey:@"screen_resolution"] intValue];

if (currentScreenResolution != lastKnownScreenResolution)
{
// Reboot emulator
BOOL needsReset = NO;

if (currentFullScreenMode != lastKnownFullScreenMode) {
mLog->LogLine("Full screen mode changed by Settings.");
needsReset = YES;
} else if (!currentFullScreenMode && currentScreenResolution != lastKnownScreenResolution) {
// Only check resolution change in classic mode (not full screen)
mLog->LogLine("Newton screen resolution changed by Settings.");
needsReset = YES;
}

if (needsReset) {
lastKnownFullScreenMode = currentFullScreenMode;
lastKnownScreenResolution = currentScreenResolution;
[self resetEmulator];
}

// Start the thread.

mLog->LogLine("Detaching emulator thread");
[NSThread detachNewThreadSelector:@selector(emulatorThread) toTarget:self withObject:nil];
}
Expand Down
Loading