Mac OS X窗口服务器与X11:疯狂的任务

5
致力于所有喜欢低级Window Server(CoreGraphicsPrivate.h等)、在Mac上使用X11、SIMBL和其他疯狂东西的人们 :)
有一个简单的在Mac上模拟X11的应用程序(如xterm、xeyes等),它只有一个窗口。当运行时,X11会以某种方式创建一个本地的Quartz窗口来表示这个模拟的应用程序,并通过Quartz Window Services使得我可以获取它的CSWindowID、标题、位置、大小和所有者的PID(X11.app的PID)。但是它不支持辅助功能API,所以没有办法控制它(除了可能来自同一进程的Core Graphics私有函数)。
现在,任务来了:
我需要在这样一个窗口上托管一个额外的NSView(或者只是绘制一些东西)。我的意思是一个本地的Quartz窗口,它出现是因为X11模拟了某个应用程序。我知道,在Mac上操作窗口必须在同一个进程中,即X11.app。
我写了一个SIMBL插件,侵入了X11.app进程。
在那里,我可以调用[NSApp windows],但每次都只能得到两个与真实应用程序窗口没有任何关系的NSWindows。它们甚至在屏幕上也看不见。
尽管如此,当我调用NSWindowList()时,我得到了任何我需要的东西(X11窗口的窗口ID),甚至更多(来自其他应用程序的窗口ID)。
当我得到了X11模拟窗口的CSWindowIDs时,我调用[Cocoa]的[NSApp windowWithWindowNumber: ]和[Carbon]的HIWindowFromCGWindowID(),但它们都返回nil!来自同一进程!
顺便说一下,当我侵入Safari进程和其他进程时,所有这些操作都能完美地工作...
那么,问题来了:
1. X11是如何创建这样的窗口,以至于从同一个进程中无法访问它们?
2. 我该如何获取指向X11窗口的指针(NSWindow*、CGContextRef或者至少是任何东西...)并将我的图形(我甚至不谈论NSViews)托管在它们上面?
非常感谢您的帮助!
2个回答

5
我理解X11使用自己的窗口服务器和通用堆栈,因此可以在没有特殊端口的情况下运行X11应用程序。
它只有一个响应层,模仿Cocoa窗口的响应方式,以便与通用界面通信。它不是伪装成Cocoa堆栈,而是外表上伪装成Cocoa的X11堆栈。因此,它仅响应与Cocoa相关的消息子集。
我认为,在X11中进行任何严肃的工作都必须从头开始使用X11 API。换句话说,编写时要像不打算在Mac OS上运行一样。

那么,创建一个真正的Quartz窗口但没有WindowRef是可能的吗?那么X11如何在获取NSEvents时移动它的窗口,折叠它们等等?你的意思是它在其代码中深处存储WindowsRefs吗? - shoumikhin
好的,请看:CGError CGSNewWindowWithOpaqueShape(CGSConnectionID cid, int always2, float x, float y, CGSRegionRef shape, CGSRegionRef opaqueShape, int unknown1, void *unknownPtr, int always32, CGSWindowID *outWID); - shoumikhin
我应该看什么?更确切地说,我应该从这段代码中推断出什么? - TechZen
使用IDA Pro打开/usr/lib/libXplugin.1.dylib并查看_xp_create_window子程序。您会发现它在那里使用CGSNewWindowWithOpaqueShape(),然后将其结果存储在自己的哈希表中,并返回一个不透明的ID,称为xp_window_id。 Xquartz(Mac的X服务器绑定)使用Xplugin创建窗口,并将x_window_id与XID关联起来。 - shoumikhin
所以,最终它基本上就像我想的那样。它具有自定义的内部窗口管理,并将其映射到Cocoa。 - TechZen
显示剩余2条评论

5
所有X11.app的源代码和其他内容(Xquartz)都可以在苹果官网上找到(当前版本2.3.5(服务器85.2)。窗口创建的核心位于xpr子目录中。
为操作窗口,Xquartz使用 Xplugin 库 (/usr/lib/libXplugin.dylib)。其头文件 /usr/include/Xplugin.h 定义了像 xp_create_surface() 等函数,这些函数使用私有的 CoreGraphics API,如 CGSNewWindowWithOpaqueShape() 来创建窗口。通过逆向工程得到的未公开的 CoreGraphicsPrivate.h 或 CSGPrivate.h 可以在 网络上 找到。Xplugin 在其自己的哈希表中记住了这些 Quartz 窗口的 ID,并返回一个不透明的整数 (即 xp_resource_id)。然后 Xquartz 将特定的 XID 与此 xp_resource_id 关联起来并将其返回给客户端。
Xplugin 是闭源的,没有 API 可以通过 xp_resource_id 或 XID 返回本地的 Quartz drawable。
为了在使用私有 CoreGraphics API 创建的窗口上绘制,必须使用那些私有 API。有一个名为 CGWindowContextCreate() 的函数,它通过其 Quartz id 返回特定本地窗口的 CGContextRef。可以使用此上下文在窗口上绘制。但是要接收真正的上下文而不是 NULL,必须在创建窗口的进程中。

如果这是你问题的答案,你应该点击复选标记,这样系统就知道已经回答了。 - TechZen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接