Wacom的MacOS驱动器用于竹子,Graphire,Intuos 1、2和3和Cintiq 1st Gen Tablets的虫子中有错误,这使它们完全无法启动Macos 10.15 Catalina和更高版本(包括11个Big Sur和12个蒙特雷)。这不适用于Windows驱动程序,也不适用于较新平板电脑的驱动程序。
当您尝试使用竹平板电脑打开Wacom偏好窗格时,您会收到一条错误消息,说“等待同步”,最后“平板电脑驱动程序存在问题。或更新驱动程序”。对于Intuos 3或Cintiq 1st Gen Tablet,首选项窗格将打开,但是单击任何内容会导致其崩溃,并随消息“ Wacom Tablet Persions有错误”。对于Graphire和Intuos 1和2平板电脑,驾驶员的安装程序甚至无法在Catalina上运行。
值得庆幸的是,我能够跟踪这些问题,并修补了司机以修复它们!
我的固定竹驾驶员(v5.3.7-6)支持以下平板电脑:
我固定的Graphire 1和2和Intuos 1&2驱动程序(v6.1.6-4)支持以下平板电脑:
我固定的Graphire 3驱动程序(v5.2.6-5)支持以下平板电脑:
我固定的Graphire 4驱动程序(v5.3.0-3)支持以下平板电脑:
我的固定Intuos 3和Cintiq驱动程序(v6.3.15-3)支持以下平板电脑:
对于Intuos 4,不需要我的修复程序。您可以改用Wacom的官方驱动程序v6.3.41-2。
?简化的英语说明
? / ??仪器
? 日本语で表示
?函
? ESPAñol
? instrukcja po polsku
?指示enfrançais
在此处为平板电脑下载正确的安装程序,然后双击它运行它,这将安装我的固定版本Wacom的驱动程序:
如果您收到一条错误消息,即您的Mac仅允许从App Store安装应用程序,请右键单击它,然后单击“打开”。
安装后,请按照下一节中的说明进行修复平板电脑的权限。
将笔小费触摸到平板电脑,它应该提示您访问系统首选项>安全性和隐私选项卡以授予平板电脑权限。
在“可访问性”页面上,单击挂锁以解锁该页面,然后查找并勾选您在列表中看到的任何PenTabletDriver
, WacomTabletDriver
TabletDriver
或WacomTabletSpringboard
条目。在输入监视页面上执行相同的操作。
如果平板电脑支持触摸,请用手指触摸平板电脑,它应该再次提示您授予权限。在“可访问性”页面上,勾选ConsumerTouchDriver
或WacomTouchDriver
条目。
有了一些平板电脑,该驱动程序可能仅出现在输入监视页面上,您可能需要第二次重新启动它才能出现在可访问性页面上。
您可能有之前的平板电脑驾驶员剩下的权限,这些陈旧的条目都需要像这样删除:
在安全性和隐私的“可访问性”页面上,在列表中找到与Wacom相关的任何内容(例如PenTabletDriver
, WacomTabletDriver
, TabletDriver
, ConsumerTouchDriver
, WacomTabletSpringboard
, WacomTouchDriver
),选择它们,然后单击“ minus”按钮将其删除。转到“输入监视页面”,然后在此执行同样的操作。
现在,重新启动计算机,或在终端运行这两个命令,以重新加载平板电脑驱动程序。用于竹子和图形3和4片:
launchctl unload /Library/LaunchAgents/com.wacom.pentablet.plist
launchctl load -w /Library/LaunchAgents/com.wacom.pentablet.plist
对于Graphire 1和2,Intuos和Cintiq平板电脑:
launchctl unload /Library/LaunchAgents/com.wacom.wacomtablet.plist
launchctl load -w /Library/LaunchAgents/com.wacom.wacomtablet.plist
这应该还原提示,要求您添加权限,因此现在再次开始本节中的说明。
对于少数人来说,WACOM驱动程序从未出现在输入监视列表中。要解决此问题,请首先打开“终端”应用程序并粘贴此行并按Enter键,以确保启用WACOM服务:
用于竹子和图形3和4片:
launchctl load -w /Library/LaunchAgents/com.wacom.pentablet.plist
对于Graphire 1和2,Intuos和Cintiq平板电脑:
launchctl load -w /Library/LaunchAgents/com.wacom.wacomtablet.plist
如果没有触发它,以要求您在尝试使用平板电脑时添加输入监视权限,则可以手动添加它。
在Finder中,单击GO->转到文件夹,然后将此路径粘贴到其中,然后单击确定:
/Library/Application Support/Tablet/
在那里,您应该看到一个“ pentableTdriver”文件(竹),“ pentabletspringboard”(Graphire 3&4)或“ wacomtabletspringboard”(Graphire 1&2,Intuos,cintiq)。
解锁输入监视页面,然后将pentableTdriver / pentabletspringboard / wacomtabletspringboard文件拖动到列表中,以将其添加到列表中,并确保其被打勾。现在,重新启动计算机,当您尝试使用平板电脑时,它也应提示您在可访问性页面中勾选它,之后它应该开始工作。
确保您仍然没有安装Wacom的最新驱动程序。使用“ Wacom实用程序”/“平板电脑实用程序”来完全卸载Wacom的所有驱动程序(而不是将它们拖到垃圾桶中),然后再次安装我的驱动程序。
损坏的偏好可以阻止事情工作,尤其是如果您在试图使事情正常工作的同时安装了许多不同的驱动程序版本时。使用WACOM实用程序重置您的首选项,重新启动并尝试再次使用平板电脑。
如果您喜欢将平板电脑重新使用,请考虑向我发送小费!
这将有助于为我提供资金并进一步发展这些固定驱动因素。
PentableTdriver推出了两个子驾驶员,以为其完成这项工作,消费者和桌上驱动器。为了在其资源文件夹中找到这些驱动程序,它最终调用此功能从URL提取路径:
CFString * MacPaths::PathFromURL (CFURL *url)
{
CFString *path;
path = _objc_retainAutoreleasedReturnValue (url-> path ());
_objc_release (path); /* Whoops, path is destroyed here! */
return path; /* Now returning a free'd path */
}
请原谅我以C ++的原始目标C代码来解释我不会说OBJC!
当CFURL创建路径时,其参考计数从1开始。它排队以稍后通过在其上调用autorelease()
,然后将其返回到Wacom的驱动程序中。该autorelease
与Wacom的retainAutoreleasedReturnValue()
呼叫,并因此将Path的参考数数毫不动摇。
但是现在WACOM驱动程序在路径上调用_objc_release()
。这将其参考计数减少到0,而路径立即被释放!
在启动子驱动器时使用此释放路径,该路径可以触发ProcessUtils::LaunchApplicationWithBundleID()
中的segfault。这杀死了驾驶员。
修复程序是一个单字节更改,将PathFromURL
中_objc_release()
的调用替换为呼叫_objc_retain()
。这样可以防止在使用之前释放路径,从而固化segfault。
conviceTouchDriver还包含相同的错误,并且该补丁在那里相同。
笔驱动程序和触摸驱动程序都需要修复,以阻止他们在执行多点触摸手势时崩溃,或者使用滚动环来缩放。
执行手势时,函数CMacHIDGestureEventOSX1010::PostGesture
负责将该手势发送到操作系统:
void CMacHIDGestureEventOSX1010::PostGesture (EIOHIDEventType gestureType_I, int32_t eventDirAmount)
{
__CFDataOSX1010 *eventStructure;
if (gestureType_I == 61 /* kCGHIDEventTypeGestureStarted */ ) {
this -> eventPhase = 1 /* kCGSGesturePhaseBegan */ ;
} else {
this -> eventPhase = 4 /* kCGSGesturePhaseEnded */ ;
(**(code **)(*( long *) this + 0x18 ))( 0 , this ,( uint32_t ) eventDirAmount);
}
eventStructure = (__CFDataOSX1010 *) _CGEventCreate ( 0 ); // Dubious
_CGEventSetType (eventStructure, 29 /* kCGSEventGesture */ );
eventStructure-> eventSubType = gestureType_I; // Relies on the exact memory layout of CGEvent (!)
eventStructure-> eventDirAmount = eventDirAmount; // Ditto
_CGEventPost ( 0 , eventStructure);
_CFRelease (eventStructure);
}
请注意,CgeventCreate的结果是如何被投入到结构的? CGEVENT应该是不透明的类型,由于其结构从OS版本变为OS版本,因此不应该知道或依赖其布局,但是在这里它被铸造为结构,以便其eventSubType
和eventDirAmount
字段可以直接分配。这两个写作会导致卡塔琳娜(Catalina)造成堆腐败并引发撞车事故,因为自塞拉(Sierra)以来, CGEvent
的布局发生了变化!
将值存储到事件中的正确方法是使用CGEVENTETETEGERVALUEFIELD API,它允许您通过逻辑ID来引用CGEVENT的字段,而不是它们在内存中的位置。那么,WACOM驱动程序需要制作的两个写作的等效字段ID是什么?
我拆除了Macos Sierra的Skylight框架,其中包含CGEventSetIntegerValueField
的实现,以查看这些字段的ID应该是什么。看来eventSubType
可以用字段ID 110编写,并且eventDirAmount
可以用ID 115编写。但是这些字段ID在Apple的文档中找不到,这解释了为什么Wacom无法使用它们!
我进行了一些谷歌搜索,发现这些领域是没有证件的,因为它们是苹果私人API的一部分。此Webkit标头揭示了他们的名字:
kCGEventGestureHIDType = 110
kCGEventGestureSwipeValue = 115
kCGEventGesturePhase = 132
这些私人API领域从塞拉利昂一直稳定到大苏尔!既然我们知道这一点,可以用这些呼叫代替事件结构的两个作业,并且消除了驾驶员撞车事故:
_CGEventSetIntegerValueField (eventStructure, 110 /* kCGEventGestureHIDType */ , gestureType_I);
_CGEventSetIntegerValueField (eventStructure, 115 /* kCGEventGestureSwipeValue */ , eventDirAmount);
Postgesture的浮点版具有相同的问题:
void CMacHIDGestureEventOSX1010::PostGesture (EIOHIDEventType eventSubType, float dirAmount)
{
__CFDataOSX1010 *eventStructure;
eventStructure = (__CFDataOSX1010 *) _CGEventCreate ( 0 );
_CGEventSetType (eventStructure, 29 /* kCGSEventGesture */ );
eventStructure-> eventSubType = eventSubType; // !
eventStructure-> eventDirAmount = reinterpret_cast < int32_t &>(dirAmount); // !
eventStructure-> eventState = this -> eventPhase ; // !
if ( this -> eventPhase == 1 /* kCGSGesturePhaseBegan */ ) {
this -> eventPhase = 2 /* kCGSGesturePhaseChanged */ ;
}
_CGEventSetLocation (eventStructure, GetMouseLocationInScreenCoordinates ());
_CGEventPost ( 0 , eventStructure);
_CFRelease (eventStructure);
}
我们可以这样修补:
_CGEventSetIntegerValueField (eventStructure, 110 /* kCGEventGestureHIDType */ , gestureType_I);
_CGEventSetIntegerValueField (eventStructure, 115 /* kCGEventGestureSwipeValue */ , reinterpret_cast < int32_t &>(dirAmount));
_CGEventSetIntegerValueField (eventStructure, 132 /* kCGEventGesturePhase */ , this ->eventPhase);
在Wacom的偏好窗格中,Wacom成为Apple Bug 8746551的受害者。自Macos Catalina以来,偏好窗格有效地打破了NSApp->mainWindow
的操作,通过允许任何窗口类型成为活动窗口,甚至是以前版本的床单,这是不可能的MACOS和独立应用程序(在先前的版本中,只有实际的主首选项窗口才会成为mainWindow
)。
当模态打开时,用户想打开新的表,需要首先关闭原始表。但是苹果的错误导致Wacom的检查以查看当前是否有打开的表格失败,因为它检查是否将表格连接到mainWindow
上时,它最终检查了附加到当前工作表上的表格,这是莫名其妙的成为mainWindow
。这会触发崩溃(例如,在笔映射设置中)。
我对mainWindow
的访问进行了修补,以便以后将读取的第一个值被缓存,因此,只要最初的值是明智的, mainWindow
更新以指向首次开放的表格就不会被打破(请参阅PentableTET(请参阅PenTablet.prefpane.newcode.asm
)。
Intuos 3和Cintiq驱动程序的首选窗格中有一个错误,该错误在单击一项项目后立即崩溃。
优先窗格UI的主要功能之一是代表您可以配置的平板电脑,工具和应用程序的图标列表。图标列表控件使用这样的函数将事件(例如点击)派遣到其孩子:
void WTCCPLIconList::action:(ID event, SEL param_2, ID param_3) {
int selectedIndex;
ID target;
ID action;
target = _objc_retainAutoreleasedReturnValue (event-> target ());
action = event-> action ();
selectedIndex = event-> selectedIndex ();
event-> scrollIndexToVisible (selectedIndex);
if ((target != 0 ) && (action != 0 )) {
code* handler = target-> methodForSelector (action);
Object *result = handler (target, action, event);
result = _objc_retainAutoreleasedReturnValue (result); // (!)
_objc_release (result); // (!)
}
event-> updateButtonStates ();
_objc_release (target);
}
此代码的问题在于,它需要事件的handler()
函数返回一个对象,然后它毫无用处地保留()和版本()。但是,将被称为的处理程序函数之一是OEventDispatcher_Professional::listClickAction()
,该函数是一个void
函数(没有定义明确的返回值)!
因此, action:()
最终在垃圾指针上调用retain()
和release()
,这会导致Segfault。
这里的补丁很容易 - 删除了无用的retain()
和release()
调用。 ONumberTextField::textDidChange:
具有相同的修复程序。
这个驱动程序还有另一个问题。如果在试图使平板电脑工作时,您不小心安装了最新的WACOM驱动程序(不支持Intuos 3),它将写一个旧的Intuos 3驱动程序在尝试阅读时会崩溃的较新的格式优先文件。而且这种情况无法解决,用户必须手动使用WACOM实用程序来删除平板电脑的首选项以修复它。
这很奇怪,因为优先文件包含一个版本编号,该版本旨在避免这种情况。旧驱动程序使用版本5,最新的驱动程序使用版本6:
< ImportFileVersion type = " integer " >6</ ImportFileVersion >
并且驱动程序代码明确检查了此版本号,并且在太新的情况下应该中止:
CTabletDriver::ReadSettings (basic_string settingsFilename, ...)
{
basic_string settingsFileContent;
CSettingsMap settingsMap;
...
ReadFromXMLFile (settingsMap, settingsFilename, settingsFileContent);
SettingsMigration *migration = SettingsMigration::MigratePen (settingsMap);
if (migration != nullptr ) {
int fileVersion = settingsMap. IntegerForKeyWithDefault ( " ImportFileVersion " , - 1 )
if (fileVersion < 1 ) {
// Report error
} else if (fileVersion <= this -> supportedVersion ) {
// Accept the loaded settings
} else {
// Report error: Settings are too new
}
}
...
}
但是驱动程序永远不会中断,而是在尝试加载首选项时会崩溃。那么这里出了什么问题?秘密位于MigratePen()
函数中。此功能旨在将旧设置文件升级到当前版本,但不幸的是,在此过程中,它无条件地用当前版本(5)覆盖ImportFileVersion
!这使CTabletDriver::ReadSettings()
无法检测到设置太新而无法解析。
为了解决这个问题,我从MigratePen()
中加强了无效版本检测代码:
if (settingsVersion < 1 ) {
// Report invalid settings version and return NULL
}
做到这一点:
if (settingsVersion < 1 || settingsVersion > 5 ) {
// Report invalid settings version and return NULL
}
因此,如果偏好太新, MigratePen()
将不会尝试升级它们,并且ReadSettings()
将干净地跳过加载首选项。这会导致首选项保留在其默认设置中,如果用户使用偏好窗格编辑设置,则应将设置覆盖。
Graphire驱动程序的安装程序是Catalina不再支持的旧格式,因此我不得不完全重建它们。
Graphire的偏好窗格错误地依赖于Catalina中不再存在的MacOS标准库中的私人符号,因此无法再启动。
例如,Wacom的NSNIBWakingOverride :: awakefromnib()函数在GUI避免序列化期间调用,并将加载的GUI控件添加到地图中,以便稍后可以通过标签访问:
void NSNibWakingOverride::awakeFromNib (NSControl * this ) {
OMasterDictionaryPtr-> addObject :withTag:( this , this -> _tag ));
}
但这依赖于读取nscontrol._tag,这是一个私人字段。在MacOS 10.9中,由于Nscontrol的定义是这样的:
@interface NSControl : NSView
{
/* All instance variables are private */
NSInteger _tag;
id _cell;
struct __conFlags {
...
} _conFlags;
}
但是在MacOS 10.10中,NSCONTROL被重构以将_tag字段移动到一个新的_aux字段中,使其无法访问:
@interface NSControl : NSView
{
/* All instance variables are private */
NSControlAuxiliary *_aux;
id _cell;
struct __conFlags {
...
} _conFlags;
}
@property NSInteger tag;
我修补了Awakefromnib,以使其正确调用此字段的公共访问功能:
void NSNibWakingOverride::awakeFromNib (NSControl * this ) {
OMasterDictionaryPtr-> addObject :withTag:( this , this -> tag ()));
}
在Wacom的OpoPupoutlineView :: ReloDdata()函数中,私有nstableview._datasource字段被错误地直接读取:
void OPopupOutlineView::reloadData (OPopupOutlineView * this ) {
this -> _dataSource . willReloadDataForOutlineView ( this );
...
}
因此,我对此进行了修补以使用公共访问者:
void OPopupOutlineView::reloadData (OPopupOutlineView * this ) {
this -> dataSource ()-> willReloadDataForOutlineView ( this );
...
}
这些驱动程序与Graphire 3和4驱动程序具有相同的问题,并在其ORadialSubMenuTableView::reloadData() method
_dataSource
,还有相同的修复程序。