前言
笔者使用M芯片的mac电脑,在27寸的4k显示器,使用2048的缩放,可以开启hidpi,且显示大小合适,观感舒适,当然使用5k显示器效果最佳。毕竟mac自己出了5k的imac pro。笔者使用开源项目rdm:RDM,但是RDM的作者已经不维护了。目前分发版本只有x86的Intel版本,想着反正是C++代码,是不是自己在M芯片的电脑上编译一下就行了,来试试,开干。
准备
下载RDM源码:https://github.com/avibrazil/RDM

可以看到这个项目还是很火的,基本上大部分是C++写的
重点关注makefile和license

这个是无任何协议说明的,根据这段说明,可以使用,看看Makefile

这个要改,使用man uname看看

试试,为arm64

先改一下

笔者虽然懂C++,但是没有任何系统桌面软件开发经验,先make试试效果
使用AI
首次make,跳坑,说识别不了display这个定义
bash
./utils.h:26:51: error: use of undeclared identifier 'display'; did you mean 'eDisplay'?
26 | void CGSGetCurrentDisplayMode(CGDirectDisplayID display, int* modeNum);
| ^~~~~~~
| eDisplay
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/CoreServices.framework/Frameworks/AE.framework/Headers/AERegistry.h:949:3: note: 'eDisplay' declared here
949 | eDisplay = 'edds',
| ^
In file included from cmdline.mm:5:
./utils.h:26:8: error: variable has incomplete type 'void'
26 | void CGSGetCurrentDisplayMode(CGDirectDisplayID display, int* modeNum);
| ^
./utils.h:26:33: error: use of undeclared identifier 'CGDirectDisplayID'
26 | void CGSGetCurrentDisplayMode(CGDirectDisplayID display, int* modeNum);
| ^
./utils.h:26:63: error: expected '(' for function-style cast or type construction
26 | void CGSGetCurrentDisplayMode(CGDirectDisplayID display, int* modeNum);
| ~~~^
./utils.h:26:65: error: use of undeclared identifier 'modeNum'
26 | void CGSGetCurrentDisplayMode(CGDirectDisplayID display, int* modeNum);
| ^
./utils.h:27:76: error: use of undeclared identifier 'display'; did you mean 'eDisplay'?
27 | void CGSConfigureDisplayMode(CGDisplayConfigRef config, CGDirectDisplayID display, int modeNum);
| ^~~~~~~
| eDisplay
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/CoreServices.framework/Frameworks/AE.framework/Headers/AERegistry.h:949:3: note: 'eDisplay' declared here
949 | eDisplay = 'edds',
| ^
In file included from cmdline.mm:5:
./utils.h:27:7: error: variable has incomplete type 'void'
27 | void CGSConfigureDisplayMode(CGDisplayConfigRef config, CGDirectDisplayID display, int modeNum);
| ^
./utils.h:27:31: error: use of undeclared identifier 'CGDisplayConfigRef'
27 | void CGSConfigureDisplayMode(CGDisplayConfigRef config, CGDirectDisplayID display, int modeNum);
| ^
./utils.h:27:58: error: use of undeclared identifier 'CGDirectDisplayID'
27 | void CGSConfigureDisplayMode(CGDisplayConfigRef config, CGDirectDisplayID display, int modeNum);
| ^
./utils.h:27:89: error: expected '(' for function-style cast or type construction
27 | void CGSConfigureDisplayMode(CGDisplayConfigRef config, CGDirectDisplayID display, int modeNum);
| ~~~ ^
./utils.h:28:52: error: use of undeclared identifier 'display'; did you mean 'eDisplay'?
28 | void CGSGetNumberOfDisplayModes(CGDirectDisplayID display, int* nModes);
| ^~~~~~~
| eDisplay
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/CoreServices.framework/Frameworks/AE.framework/Headers/AERegistry.h:949:3: note: 'eDisplay' declared here
949 | eDisplay = 'edds',
| ^
In file included from cmdline.mm:5:
./utils.h:28:7: error: variable has incomplete type 'void'
28 | void CGSGetNumberOfDisplayModes(CGDirectDisplayID display, int* nModes);
| ^
./utils.h:28:34: error: use of undeclared identifier 'CGDirectDisplayID'
28 | void CGSGetNumberOfDisplayModes(CGDirectDisplayID display, int* nModes);
| ^
./utils.h:28:64: error: expected '(' for function-style cast or type construction
28 | void CGSGetNumberOfDisplayModes(CGDirectDisplayID display, int* nModes);
| ~~~^
./utils.h:28:66: error: use of undeclared identifier 'nModes'; did you mean 'eModem'?
28 | void CGSGetNumberOfDisplayModes(CGDirectDisplayID display, int* nModes);
| ^~~~~~
| eModem
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/CoreServices.framework/Frameworks/AE.framework/Headers/AERegistry.h:967:3: note: 'eModem' declared here
967 | eModem = 'edmm',
| ^
In file included from cmdline.mm:5:
./utils.h:29:62: error: use of undeclared identifier 'display'; did you mean 'eDisplay'?
29 | void CGSGetDisplayModeDescriptionOfLength(CGDirectDisplayID display, int idx, modes_D4* mode, int length);
| ^~~~~~~
| eDisplay
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/CoreServices.framework/Frameworks/AE.framework/Headers/AERegistry.h:949:3: note: 'eDisplay' declared here
949 | eDisplay = 'edds',
根据GLM AI诊断:这个是由于.h文件的定义缺失引用,另外定义的变量名称不一致,因为继承的C,AI还说这个是macOS display的私有API啥的
那么我们在utils.h加入引用,修改变量名
cpp
#ifndef UTILS_H
#define UTILS_H
#include <CoreGraphics/CGDirectDisplay.h>
#include <CoreGraphics/CoreGraphics.h>
#include <CoreGraphics/CGDisplayConfiguration.h>
typedef union
{
uint8_t rawData[0xDC];
struct
{
uint32_t mode;
uint32_t flags; // 0x4
uint32_t width; // 0x8
uint32_t height; // 0xC
uint32_t depth; // 0x10
uint32_t dc2[42];
uint16_t dc3;
uint16_t freq; // 0xBC
uint32_t dc4[4];
float density; // 0xD0
// freq = 0xBC
// density = 0xD0
// uint32_t freq;
} derived;
} modes_D4;
extern "C"
{
void CGSGetCurrentDisplayMode(CGDirectDisplayID displayID, int* modeNum);
void CGSConfigureDisplayMode(CGDisplayConfigRef config, CGDirectDisplayID displayID, int modeNum);
void CGSGetNumberOfDisplayModes(CGDirectDisplayID displayID, int* nModes);
void CGSGetDisplayModeDescriptionOfLength(CGDirectDisplayID displayID, int idx, modes_D4* mode, int length);
};
void CopyAllDisplayModes(CGDirectDisplayID displayID, modes_D4** modes, int* cnt);
void SetDisplayModeNum(CGDirectDisplayID displayID, int modeNum);
#endif
这里的#if 和#endif,是AI说防止定义冲突啥的,建议使用运行时代码选择编译,说的有道理就加上了
然后执行make clean清理刚刚的编译
再执行make,成功,双击试试效果

效果如下:可以使用,原生ARM应用

遗留问题
遗留问题:
bash
SRApplicationDelegate.mm:77:22: warning: 'NSOnState' is deprecated: first deprecated in macOS 10.14 [-Wdeprecated-declarations]
77 | [item setState: NSOnState];
| ^~~~~~~~~
| NSControlStateValueOn
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/AppKit.framework/Headers/NSCell.h:355:34: note: 'NSOnState' has been explicitly marked deprecated here
355 | static const NSControlStateValue NSOnState API_DEPRECATED_WITH_REPLACEMENT("NSControlStateValueOn", macos(10.0,10.14)) = NSControlStateValueOn;
| ^
SRApplicationDelegate.mm:161:22: warning: 'NSOnState' is deprecated: first deprecated in macOS 10.14 [-Wdeprecated-declarations]
161 | [item setState: NSOnState];
| ^~~~~~~~~
| NSControlStateValueOn
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/AppKit.framework/Headers/NSCell.h:355:34: note: 'NSOnState' has been explicitly marked deprecated here
355 | static const NSControlStateValue NSOnState API_DEPRECATED_WITH_REPLACEMENT("NSControlStateValueOn", macos(10.0,10.14)) = NSControlStateValueOn;
| ^
SRApplicationDelegate.mm:247:14: warning: 'setImage:' is deprecated: first deprecated in macOS 10.14 - Use the receiver's button.image instead [-Wdeprecated-declarations]
247 | [statusItem setImage: statusImage];
| ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/AppKit.framework/Headers/NSStatusItem.h:75:39: note: property 'image' is declared deprecated here
75 | @property (nullable, strong) NSImage *image API_DEPRECATED("Use the receiver's button.image instead", macos(10.0,10.14));
| ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/AppKit.framework/Headers/NSStatusItem.h:75:39: note: 'setImage:' has been explicitly marked deprecated here
SRApplicationDelegate.mm:248:14: warning: 'setHighlightMode:' is deprecated: first deprecated in macOS 10.14 - Use the receiver's button.cell.highlightsBy instead [-Wdeprecated-declarations]
248 | [statusItem setHighlightMode: YES];
| ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/AppKit.framework/Headers/NSStatusItem.h:78:16: note: property 'highlightMode' is declared deprecated here
78 | @property BOOL highlightMode API_DEPRECATED("Use the receiver's button.cell.highlightsBy instead", macos(10.0,10.14));
| ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/AppKit.framework/Headers/NSStatusItem.h:78:16: note: 'setHighlightMode:' has been explicitly marked deprecated here
SRApplicationDelegate.mm:252:18: warning: 'image' is deprecated: first deprecated in macOS 10.14 - Use the receiver's button.image instead [-Wdeprecated-declarations]
252 | [[statusItem image] setTemplate:YES];
| ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/AppKit.framework/Headers/NSStatusItem.h:75:39: note: property 'image' is declared deprecated here
75 | @property (nullable, strong) NSImage *image API_DEPRECATED("Use the receiver's button.image instead", macos(10.0,10.14));
| ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/AppKit.framework/Headers/NSStatusItem.h:75:39: note: 'image' has been explicitly marked deprecated here
5 warnings generated.
有些废弃类,笔者可以自己修改一下,比如可以完全替代的NSOnState使用NSControlStateValueOn,这个比较好改,而且完全兼容,直接替换即可。其他让AI来修改,代码如下:SRApplicationDelegate.mm
objectivec
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import "SRApplicationDelegate.h"
#import "utils.h"
#import "ResMenuItem.h"
@implementation SRApplicationDelegate
- (void) showAbout
{
[NSApp activateIgnoringOtherApps:YES];
[NSApp orderFrontStandardAboutPanel:self];
}
- (void) quit
{
[NSApp terminate: self];
}
- (void) refreshStatusMenu
{
if(statusMenu)
[statusMenu release];
statusMenu = [[NSMenu alloc] initWithTitle: @""];
uint32_t nDisplays;
CGDirectDisplayID displays[0x10];
CGGetOnlineDisplayList(0x10, displays, &nDisplays);
for(int i=0; i<nDisplays; i++)
{
CGDirectDisplayID display = displays[i];
{
NSMenuItem* item;
NSString* title = i ? [NSString stringWithFormat: @"Display %d", i+1] : @"Main Display";
item = [[NSMenuItem alloc] initWithTitle: title action: nil keyEquivalent: @""];
[item setEnabled: NO];
[statusMenu addItem: item];
}
int mainModeNum;
CGSGetCurrentDisplayMode(display, &mainModeNum);
//modes_D4 mainMode;
//CGSGetDisplayModeDescriptionOfLength(display, mainModeNum, &mainMode, 0xD4);
ResMenuItem* mainItem = nil;
int nModes;
modes_D4* modes;
CopyAllDisplayModes(display, &modes, &nModes);
{
NSMutableArray* displayMenuItems = [NSMutableArray new];
//ResMenuItem* mainItem = nil;
for(int j = 0; j <nModes; j++)
{
ResMenuItem* item = [[ResMenuItem alloc] initWithDisplay: display andMode: &modes[j]];
//[item autorelease];
if(mainModeNum == j)
{
mainItem = item;
[item setState: NSControlStateValueOn];
}
[displayMenuItems addObject: item];
[item release];
}
int idealColorDepth = 32;
double idealRefreshRate = 0.0f;
if(mainItem)
{
idealColorDepth = [mainItem colorDepth];
idealRefreshRate = [mainItem refreshRate];
}
[displayMenuItems sortUsingSelector: @selector(compareResMenuItem:)];
NSMenu* submenu = [[NSMenu alloc] initWithTitle: @""];
ResMenuItem* lastAddedItem = nil;
for(int j=0; j < [displayMenuItems count]; j++)
{
ResMenuItem* item = [displayMenuItems objectAtIndex: j];
if([item colorDepth] == idealColorDepth)
{
if([item refreshRate] == idealRefreshRate)
{
[item setTextFormat: 1];
}
if(lastAddedItem && [lastAddedItem width]==[item width] && [lastAddedItem height]==[item height] && [lastAddedItem scale]==[item scale])
{
double lastRefreshRate = lastAddedItem ? [lastAddedItem refreshRate] : 0;
double refreshRate = [item refreshRate];
if(!lastAddedItem || (lastRefreshRate != idealRefreshRate && (refreshRate == idealRefreshRate || refreshRate > lastRefreshRate)))
{
if(lastAddedItem)
{
[submenu removeItem: lastAddedItem];
lastAddedItem = nil;
}
[submenu addItem: item];
lastAddedItem = item;
}
}
else
{
[submenu addItem: item];
lastAddedItem = item;
}
}
}
NSString* title;
{
if([mainItem scale] == 2.0f)
{
title = [NSString stringWithFormat: @"%d × %d ⚡️️", [mainItem width], [mainItem height]];
}
else
{
title = [NSString stringWithFormat: @"%d × %d", [mainItem width], [mainItem height]];
}
}
NSMenuItem* resolution = [[NSMenuItem alloc] initWithTitle: title action: nil keyEquivalent: @""];
[resolution setSubmenu: submenu];
[submenu release];
[statusMenu addItem: resolution];
[resolution release];
[displayMenuItems release];
}
{
NSMutableArray* displayMenuItems = [NSMutableArray new];
ResMenuItem* mainItem = nil;
for(int j = 0; j < nModes; j++)
{
ResMenuItem* item = [[ResMenuItem alloc] initWithDisplay: display andMode: &modes[j]];
[item setTextFormat: 2];
//[item autorelease];
if(mainModeNum == j)
{
mainItem = item;
[item setState: NSControlStateValueOn];
}
[displayMenuItems addObject: item];
[item release];
}
int idealColorDepth = 32;
double idealRefreshRate = 0.0f;
if(mainItem)
{
idealColorDepth = [mainItem colorDepth];
idealRefreshRate = [mainItem refreshRate];
}
[displayMenuItems sortUsingSelector: @selector(compareResMenuItem:)];
NSMenu* submenu = [[NSMenu alloc] initWithTitle: @""];
for(int j=0; j< [displayMenuItems count]; j++)
{
ResMenuItem* item = [displayMenuItems objectAtIndex: j];
if([item colorDepth] == idealColorDepth)
{
if([mainItem width]==[item width] && [mainItem height]==[item height] && [mainItem scale]==[item scale])
{
[submenu addItem: item];
}
}
}
if(idealRefreshRate)
{
NSMenuItem* freq = [[NSMenuItem alloc] initWithTitle: [NSString stringWithFormat: @"%.0f Hz", [mainItem refreshRate]] action: nil keyEquivalent: @""];
if([submenu numberOfItems] > 1)
{
[freq setSubmenu: submenu];
}
else
{
[freq setEnabled: NO];
}
[statusMenu addItem: freq];
[freq release];
}
[submenu release];
[displayMenuItems release];
}
free(modes);
[statusMenu addItem: [NSMenuItem separatorItem]];
}
[statusMenu addItemWithTitle: @"About RDM" action: @selector(showAbout) keyEquivalent: @""];
[statusMenu addItemWithTitle: @"Quit" action: @selector(quit) keyEquivalent: @""];
[statusMenu setDelegate: self];
[statusItem setMenu: statusMenu];
}
- (void) setMode: (ResMenuItem*) item
{
CGDirectDisplayID display = [item display];
int modeNum = [item modeNum];
SetDisplayModeNum(display, modeNum);
/*
CGDisplayConfigRef config;
if (CGBeginDisplayConfiguration(&config) == kCGErrorSuccess) {
CGConfigureDisplayWithDisplayMode(config, display, mode, NULL);
CGCompleteDisplayConfiguration(config, kCGConfigureForSession);
}*/
[self refreshStatusMenu];
}
- (void) applicationDidFinishLaunching: (NSNotification*) notification
{
// NSLog(@"Finished launching");
statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength: NSSquareStatusItemLength] retain];
NSImage* statusImage = [NSImage imageNamed: @"StatusIcon"];
[statusItem.button setImage: statusImage];
[statusItem.button.cell setHighlightsBy: NSPushInCellMask];
BOOL supportsDarkMenu = !(floor(NSAppKitVersionNumber) < 1343); // NSAppKitVersionNumber10_10
if (supportsDarkMenu) {
[[statusItem.button image] setTemplate:YES];
}
[self refreshStatusMenu];
}
@end
试试效果:

完美,效果OK,其实看提示也能修改,但是笔者毕竟没写过object-c代码。
总结
这次使用AI把开源的RDM软件实现了ARM编译,借助AI修改了其中utils.h文件实现了原生M芯片应用,毕竟macOS 26之后要废弃转移x86的能力,证明了AI在小项目的强大之处,如果项目复杂,笔者即使使用AI也搞不定,毕竟不懂桌面APP的开发。
附上笔者编译的app附件,见文章附件