mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-05-11 17:29:42 +08:00
236 lines
7.9 KiB
Go
236 lines
7.9 KiB
Go
//go:build darwin
|
||
|
||
package app
|
||
|
||
/*
|
||
#cgo CFLAGS: -x objective-c -fblocks
|
||
#cgo LDFLAGS: -framework Cocoa
|
||
#include <stdlib.h>
|
||
#import <Cocoa/Cocoa.h>
|
||
#import <dispatch/dispatch.h>
|
||
|
||
static inline BOOL gonaviBoolYES() { return YES; }
|
||
static inline BOOL gonaviBoolNO() { return NO; }
|
||
|
||
static char *gonaviNativeLogPath = NULL;
|
||
static BOOL gonaviNativeObserverInstalled = NO;
|
||
|
||
static void gonaviWriteNativeWindowLogLine(NSString *line) {
|
||
if (line == nil || gonaviNativeLogPath == NULL) {
|
||
return;
|
||
}
|
||
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:[NSString stringWithUTF8String:gonaviNativeLogPath]];
|
||
if (handle == nil) {
|
||
return;
|
||
}
|
||
@try {
|
||
[handle seekToEndOfFile];
|
||
NSData *data = [line dataUsingEncoding:NSUTF8StringEncoding];
|
||
[handle writeData:data];
|
||
} @catch (__unused NSException *exception) {
|
||
} @finally {
|
||
[handle closeFile];
|
||
}
|
||
}
|
||
|
||
static NSString *gonaviWindowDiagnosticLine(NSString *eventName, NSWindow *window) {
|
||
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
|
||
[formatter setDateFormat:@"yyyy/MM/dd HH:mm:ss.SSSSSS"];
|
||
NSString *timestamp = [formatter stringFromDate:[NSDate date]];
|
||
[formatter release];
|
||
if (window == nil) {
|
||
return [NSString stringWithFormat:@"%@ [WARN] 原生窗口诊断:event=%@ window=nil\n", timestamp, eventName ?: @"unknown"];
|
||
}
|
||
NSUInteger occlusionState = 0;
|
||
NSInteger windowNumber = [window windowNumber];
|
||
NSInteger level = [window level];
|
||
NSUInteger collectionBehavior = [window collectionBehavior];
|
||
NSString *className = NSStringFromClass([window class]);
|
||
NSString *delegateClassName = [window delegate] ? NSStringFromClass([[window delegate] class]) : @"nil";
|
||
if (@available(macOS 10.9, *)) {
|
||
occlusionState = [window occlusionState];
|
||
}
|
||
return [NSString stringWithFormat:@"%@ [WARN] 原生窗口诊断:event=%@ ptr=%p number=%ld class=%@ delegate=%@ visible=%@ miniaturized=%@ key=%@ main=%@ canHide=%@ level=%ld occlusion=%lu styleMask=%lu collectionBehavior=%lu frame=%@ screen=%@ title=%@\n",
|
||
timestamp,
|
||
eventName ?: @"unknown",
|
||
window,
|
||
(long)windowNumber,
|
||
className ?: @"nil",
|
||
delegateClassName,
|
||
[window isVisible] ? @"true" : @"false",
|
||
[window isMiniaturized] ? @"true" : @"false",
|
||
[window isKeyWindow] ? @"true" : @"false",
|
||
[window isMainWindow] ? @"true" : @"false",
|
||
[window canHide] ? @"true" : @"false",
|
||
(long)level,
|
||
(unsigned long)occlusionState,
|
||
(unsigned long)[window styleMask],
|
||
(unsigned long)collectionBehavior,
|
||
NSStringFromRect([window frame]),
|
||
[window screen] ? NSStringFromRect([[window screen] frame]) : @"nil",
|
||
[window title] ?: @""];
|
||
}
|
||
|
||
@interface GoNaviNativeWindowObserver : NSObject
|
||
@end
|
||
|
||
@implementation GoNaviNativeWindowObserver
|
||
|
||
- (void)logNotification:(NSNotification *)notification {
|
||
NSString *name = [notification name] ?: @"unknown";
|
||
NSWindow *window = nil;
|
||
if ([[notification object] isKindOfClass:[NSWindow class]]) {
|
||
window = (NSWindow *)[notification object];
|
||
} else {
|
||
window = [NSApp keyWindow] ?: [NSApp mainWindow];
|
||
}
|
||
gonaviWriteNativeWindowLogLine(gonaviWindowDiagnosticLine(name, window));
|
||
}
|
||
|
||
@end
|
||
|
||
static GoNaviNativeWindowObserver *gonaviNativeWindowObserver = nil;
|
||
|
||
static void gonaviInstallNativeWindowObserver(void) {
|
||
if (gonaviNativeObserverInstalled) {
|
||
return;
|
||
}
|
||
gonaviNativeObserverInstalled = YES;
|
||
gonaviNativeWindowObserver = [[GoNaviNativeWindowObserver alloc] init];
|
||
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
||
NSArray<NSString *> *windowNotifications = @[
|
||
NSWindowDidBecomeKeyNotification,
|
||
NSWindowDidResignKeyNotification,
|
||
NSWindowDidBecomeMainNotification,
|
||
NSWindowDidResignMainNotification,
|
||
NSWindowDidMiniaturizeNotification,
|
||
NSWindowDidDeminiaturizeNotification,
|
||
NSWindowDidEnterFullScreenNotification,
|
||
NSWindowDidExitFullScreenNotification,
|
||
NSWindowDidMoveNotification,
|
||
NSWindowDidResizeNotification,
|
||
NSWindowDidChangeOcclusionStateNotification,
|
||
];
|
||
for (NSString *notificationName in windowNotifications) {
|
||
[center addObserver:gonaviNativeWindowObserver selector:@selector(logNotification:) name:notificationName object:nil];
|
||
}
|
||
NSArray<NSString *> *appNotifications = @[
|
||
NSApplicationDidHideNotification,
|
||
NSApplicationDidUnhideNotification,
|
||
NSApplicationDidBecomeActiveNotification,
|
||
NSApplicationDidResignActiveNotification,
|
||
];
|
||
for (NSString *notificationName in appNotifications) {
|
||
[center addObserver:gonaviNativeWindowObserver selector:@selector(logNotification:) name:notificationName object:nil];
|
||
}
|
||
for (NSWindow *window in [NSApp windows]) {
|
||
gonaviWriteNativeWindowLogLine(gonaviWindowDiagnosticLine(@"observer:snapshot", window));
|
||
}
|
||
}
|
||
|
||
static void gonaviConfigureNativeWindowDiagnostics(const char *logPath) {
|
||
if (logPath == NULL || logPath[0] == '\0') {
|
||
return;
|
||
}
|
||
if (gonaviNativeLogPath != NULL) {
|
||
free(gonaviNativeLogPath);
|
||
gonaviNativeLogPath = NULL;
|
||
}
|
||
gonaviNativeLogPath = strdup(logPath);
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
gonaviInstallNativeWindowObserver();
|
||
});
|
||
}
|
||
|
||
static void gonaviSetWindowButtonsVisible(NSWindow *window, BOOL visible) {
|
||
if (window == nil) {
|
||
return;
|
||
}
|
||
for (NSWindowButton buttonType = NSWindowCloseButton; buttonType <= NSWindowZoomButton; buttonType++) {
|
||
NSButton *button = [window standardWindowButton:buttonType];
|
||
if (button != nil) {
|
||
[button setHidden:!visible];
|
||
[button setEnabled:visible];
|
||
}
|
||
}
|
||
}
|
||
|
||
static BOOL gonaviShouldApplyMacWindowStyle(NSWindow *window) {
|
||
if (window == nil) {
|
||
return NO;
|
||
}
|
||
NSString *className = NSStringFromClass([window class]) ?: @"";
|
||
NSString *delegateClassName = [window delegate] ? NSStringFromClass([[window delegate] class]) : @"";
|
||
NSString *title = [window title] ?: @"";
|
||
|
||
// 仅对主 WailsWindow 套用原生标题栏/全屏样式,避免误伤输入法候选窗、全屏过渡窗。
|
||
if ([className isEqualToString:@"WailsWindow"] || [delegateClassName isEqualToString:@"WindowDelegate"]) {
|
||
return YES;
|
||
}
|
||
return [title isEqualToString:@"GoNavi"];
|
||
}
|
||
|
||
static void gonaviApplyMacWindowStyle(BOOL enabled) {
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
for (NSWindow *window in [NSApp windows]) {
|
||
if (window == nil) {
|
||
continue;
|
||
}
|
||
if (!gonaviShouldApplyMacWindowStyle(window)) {
|
||
gonaviWriteNativeWindowLogLine(gonaviWindowDiagnosticLine(@"style:skip-non-app-window", window));
|
||
continue;
|
||
}
|
||
|
||
NSUInteger styleMask = [window styleMask];
|
||
styleMask |= NSWindowStyleMaskClosable;
|
||
styleMask |= NSWindowStyleMaskMiniaturizable;
|
||
styleMask |= NSWindowStyleMaskResizable;
|
||
|
||
if (enabled) {
|
||
styleMask |= NSWindowStyleMaskTitled;
|
||
styleMask |= NSWindowStyleMaskFullSizeContentView;
|
||
[window setStyleMask:styleMask];
|
||
[window setTitleVisibility:NSWindowTitleHidden];
|
||
[window setTitlebarAppearsTransparent:YES];
|
||
[window setMovableByWindowBackground:YES];
|
||
[window setCollectionBehavior:[window collectionBehavior] | NSWindowCollectionBehaviorFullScreenPrimary];
|
||
gonaviSetWindowButtonsVisible(window, YES);
|
||
} else {
|
||
styleMask &= ~NSWindowStyleMaskTitled;
|
||
styleMask &= ~NSWindowStyleMaskFullSizeContentView;
|
||
[window setStyleMask:styleMask];
|
||
[window setTitleVisibility:NSWindowTitleVisible];
|
||
[window setTitlebarAppearsTransparent:NO];
|
||
[window setMovableByWindowBackground:YES];
|
||
gonaviSetWindowButtonsVisible(window, NO);
|
||
}
|
||
|
||
[[window contentView] setNeedsDisplay:YES];
|
||
[window invalidateShadow];
|
||
gonaviWriteNativeWindowLogLine(gonaviWindowDiagnosticLine(enabled ? @"style:enable-native-controls" : @"style:disable-native-controls", window));
|
||
}
|
||
});
|
||
}
|
||
*/
|
||
import "C"
|
||
|
||
import "unsafe"
|
||
|
||
func installMacNativeWindowDiagnostics(logPath string) {
|
||
if logPath == "" {
|
||
return
|
||
}
|
||
cLogPath := C.CString(logPath)
|
||
defer C.free(unsafe.Pointer(cLogPath))
|
||
C.gonaviConfigureNativeWindowDiagnostics(cLogPath)
|
||
}
|
||
|
||
func setMacNativeWindowControls(enabled bool) {
|
||
state := resolveMacNativeWindowControlState(enabled)
|
||
if state.ShowNativeButtons {
|
||
C.gonaviApplyMacWindowStyle(C.gonaviBoolYES())
|
||
} else {
|
||
C.gonaviApplyMacWindowStyle(C.gonaviBoolNO())
|
||
}
|
||
}
|