Skip to content

Commit 9990767

Browse files
committed
Improve macOS menu and tray cleanup logic
- Fix modifier mapping for keyboard accelerators - Add exception safety to registry cleanup - Ensure delegates and UI references are cleared before destruction - Prevent callbacks during shutdown and avoid circular references
1 parent f7fd47a commit 9990767

File tree

3 files changed

+67
-27
lines changed

3 files changed

+67
-27
lines changed

src/platform/macos/menu_macos.mm

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ @interface MenuDelegate : NSObject <NSMenuDelegate>
7171

7272
// Convert modifiers
7373
if (accelerator.modifiers & KeyboardAccelerator::Ctrl) {
74-
modifierMask |= NSEventModifierFlagCommand; // On macOS, Ctrl maps to Cmd
74+
modifierMask |= NSEventModifierFlagControl;
7575
}
7676
if (accelerator.modifiers & KeyboardAccelerator::Alt) {
7777
modifierMask |= NSEventModifierFlagOption;
@@ -80,7 +80,7 @@ @interface MenuDelegate : NSObject <NSMenuDelegate>
8080
modifierMask |= NSEventModifierFlagShift;
8181
}
8282
if (accelerator.modifiers & KeyboardAccelerator::Meta) {
83-
modifierMask |= NSEventModifierFlagControl; // On macOS, Meta maps to Ctrl
83+
modifierMask |= NSEventModifierFlagCommand;
8484
}
8585

8686
return std::make_pair(keyEquivalent, modifierMask);
@@ -168,7 +168,7 @@ - (void)menuDidClose:(NSMenu *)menu {
168168
if (submenu_) {
169169
submenu_.reset();
170170
}
171-
171+
172172
if (target_) {
173173
// Remove target and action to prevent callbacks after destruction
174174
[ns_menu_item_ setTarget:nil];
@@ -227,9 +227,13 @@ - (void)menuDidClose:(NSMenu *)menu {
227227

228228
MenuItem::~MenuItem() {
229229
// Safely remove from global registry
230-
auto it = g_menu_item_registry.find(id);
231-
if (it != g_menu_item_registry.end()) {
232-
g_menu_item_registry.erase(it);
230+
try {
231+
auto it = g_menu_item_registry.find(id);
232+
if (it != g_menu_item_registry.end()) {
233+
g_menu_item_registry.erase(it);
234+
}
235+
} catch (...) {
236+
// Ignore exceptions during cleanup - the registry may be destroyed during shutdown
233237
}
234238
}
235239

@@ -453,14 +457,14 @@ - (void)menuDidClose:(NSMenu *)menu {
453457
}
454458

455459
~Impl() {
456-
// First, clear all menu item references
457-
items_.clear();
458-
460+
// First, remove delegate to prevent callbacks during cleanup
459461
if (delegate_) {
460-
// Remove delegate to prevent callbacks after destruction
461462
[ns_menu_ setDelegate:nil];
462463
delegate_ = nil;
463464
}
465+
466+
// Then clear all menu item references
467+
items_.clear();
464468
}
465469
};
466470

@@ -492,9 +496,13 @@ - (void)menuDidClose:(NSMenu *)menu {
492496

493497
Menu::~Menu() {
494498
// Safely remove from global registry
495-
auto it = g_menu_registry.find(id);
496-
if (it != g_menu_registry.end()) {
497-
g_menu_registry.erase(it);
499+
try {
500+
auto it = g_menu_registry.find(id);
501+
if (it != g_menu_registry.end()) {
502+
g_menu_registry.erase(it);
503+
}
504+
} catch (...) {
505+
// Ignore exceptions during cleanup - the registry may be destroyed during shutdown
498506
}
499507
}
500508

src/platform/macos/tray_icon_macos.mm

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,28 @@ - (void)statusItemRightClicked:(id)sender;
3939
}
4040

4141
~Impl() {
42-
// First, safely clean up context_menu_
43-
if (context_menu_) {
44-
context_menu_.reset(); // Explicitly reset shared_ptr
42+
// First, clean up delegate to prevent callbacks
43+
if (delegate_) {
44+
delegate_.trayIcon = nullptr; // Clear the raw pointer first
45+
delegate_ = nil;
4546
}
4647

48+
// Then clean up the status item
4749
if (ns_status_item_) {
4850
// Remove target and action to prevent callbacks after destruction
4951
if (ns_status_item_.button) {
5052
[ns_status_item_.button setTarget:nil];
5153
[ns_status_item_.button setAction:nil];
5254
}
55+
// Clear the menu to break potential circular references
56+
[ns_status_item_ setMenu:nil];
5357
[[NSStatusBar systemStatusBar] removeStatusItem:ns_status_item_];
5458
ns_status_item_ = nil;
5559
}
56-
if (delegate_) {
57-
delegate_ = nil;
60+
61+
// Finally, safely clean up context_menu_ after all UI references are cleared
62+
if (context_menu_) {
63+
context_menu_.reset(); // Explicitly reset shared_ptr
5864
}
5965
}
6066

@@ -81,6 +87,10 @@ - (void)statusItemRightClicked:(id)sender;
8187
}
8288

8389
TrayIcon::~TrayIcon() {
90+
// Clear the delegate's reference to this object before destruction
91+
if (pimpl_ && pimpl_->delegate_) {
92+
pimpl_->delegate_.trayIcon = nullptr;
93+
}
8494
delete pimpl_;
8595
}
8696

@@ -289,7 +299,12 @@ - (void)statusItemRightClicked:(id)sender;
289299
@implementation TrayIconDelegate
290300

291301
- (void)statusItemClicked:(id)sender {
302+
// Check if trayIcon is still valid before proceeding
292303
if (!_trayIcon) return;
304+
305+
// Create a local reference to prevent race conditions
306+
nativeapi::TrayIcon* trayIcon = _trayIcon;
307+
if (!trayIcon) return;
293308

294309
NSEvent* event = [NSApp currentEvent];
295310
if (!event) return;
@@ -298,23 +313,24 @@ - (void)statusItemClicked:(id)sender {
298313
if (event.type == NSEventTypeRightMouseUp ||
299314
(event.type == NSEventTypeLeftMouseUp && (event.modifierFlags & NSEventModifierFlagControl))) {
300315
// Right click or Ctrl+Left click
301-
_trayIcon->HandleRightClick();
316+
trayIcon->HandleRightClick();
302317

303-
// Show context menu if available
304-
if (_trayIcon->GetContextMenu()) {
318+
// Show context menu if available (check again for safety)
319+
if (_trayIcon && _trayIcon->GetContextMenu()) {
305320
_trayIcon->ShowContextMenu();
306321
}
307322
} else if (event.type == NSEventTypeLeftMouseUp) {
308323
// Check for double click
309324
if (event.clickCount == 2) {
310-
_trayIcon->HandleDoubleClick();
325+
trayIcon->HandleDoubleClick();
311326
} else {
312-
_trayIcon->HandleLeftClick();
327+
trayIcon->HandleLeftClick();
313328
}
314329
}
315330
}
316331

317332
- (void)statusItemRightClicked:(id)sender {
333+
// Check if trayIcon is still valid before proceeding
318334
if (_trayIcon) {
319335
_trayIcon->HandleRightClick();
320336
}

src/platform/macos/tray_manager_macos.mm

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,32 @@
2323
TrayManager::~TrayManager() {
2424
std::lock_guard<std::mutex> lock(mutex_);
2525

26-
// First, clean up all tray icon menu references to prevent circular references
26+
// First, hide all tray icons to prevent further UI interactions
2727
for (auto& pair : trays_) {
2828
auto tray = pair.second;
2929
if (tray) {
30-
// Explicitly clear menu references
31-
tray->SetContextMenu(nullptr);
30+
try {
31+
tray->Hide();
32+
} catch (...) {
33+
// Ignore exceptions during cleanup
34+
}
3235
}
3336
}
3437

35-
// Then clear the container
38+
// Then, clean up all tray icon menu references to prevent circular references
39+
for (auto& pair : trays_) {
40+
auto tray = pair.second;
41+
if (tray) {
42+
try {
43+
// Explicitly clear menu references
44+
tray->SetContextMenu(nullptr);
45+
} catch (...) {
46+
// Ignore exceptions during cleanup
47+
}
48+
}
49+
}
50+
51+
// Finally, clear the container
3652
trays_.clear();
3753
}
3854

0 commit comments

Comments
 (0)