献给所有喜欢低级 Window Server(CoreGraphicsPrivate.h 等)、Mac 上的 X11、SIMBL 和其他疯狂东西的人 :)
Mac 上有一个简单的 X11 模拟应用程序(如 xterm、xeyes 等),只有一个窗口。运行时,X11 以某种方式创建一个本机 Quartz 窗口来表示这个模拟应用程序,并且可以通过 Quartz Window Services 访问该窗口,以便我可以获得它的 CSWindowID、标题、位置、大小和所有者的 PID(X11.app 的 PID)。但它不支持 Accessibility API,因此无法控制它(除了,可能来自同一进程的 Core Graphichs 私有函数)。
现在,任务是:
我需要在这样的窗口上托管一个额外的 NSView (或者只是画一些东西)。我指的是原生 Quartz 窗口,它是 X11 模拟某些应用程序的结果。我知道,要在 Mac 上操作窗口,我必须处于同一进程中,即 X11.app。
我编写了一个侵入X11.app进程的SIMBL插件。
在那里我可以调用 [NSApp windows],但我一直得到恰好 2 个 NSWindows,它们与真实应用程序的窗口没有任何共同点。它们甚至在屏幕上都看不到。
尽管如此,当我调用 NSWindowList() 时,我得到了我需要的任何东西(X11 窗口的窗口 ID),甚至更多(来自其他应用程序的窗口 ID)。
当我获得 X11 模拟窗口的 CSWindowID 时,我调用 [NSApp windowWithWindowNumber: ] (Cocoa) 和 HIWindowFromCGWindowID() (Carbon),但它们都返回 nil!来自完全相同的过程!
顺便说一句,当我侵入 Safari 进程和其他进程时,所有这些操作都可以完美地工作...
所以,问题是:
-
X11 如何创建此类无法从同一进程访问的窗口?
- 才能获得指向 X11 窗口(NSWindow *、CGContextRef,或者至少是任何东西......)的指针并在它们上托管我的图形(我什至不谈论 NSViews)?
预先非常感谢!
Dedicated to all who likes low-level Window Server (CoreGraphicsPrivate.h, etc), X11 on Mac, SIMBL and other crazy stuff :)
There's a simple X11-emulated application on Mac (like xterm, xeyes and so on) with one window. While running, X11 somehow creates a native Quartz window to represent this emulated application, and this window is accessible via Quartz Window Services so that I can get its CSWindowID, title, position, size and owner's PID (PID of X11.app). But it does not support Accessibility API, so there's no way to control it (except, maybe Core Graphichs private functions from the same process).
Now, here's the task:
I need to host an additional NSView (or just draw something) upon such a window. I mean a native Quartz window, which appeared as a result of X11 emulating some application. I know, to manipulate windows on Mac I must be in the same process, i.e. X11.app.
I wrote a SIMBL plug-in which intrudes into X11.app process.
There I can call [NSApp windows], but all the time I'm getting exactly 2 NSWindows which have nothing in common with real applications' windows. They are not even visible on the screen.
Nevertheless, when I call NSWindowList(), I get anything I need (window IDs for X11 windows) and even more (window IDs from other applications).
When I've got CSWindowIDs for X11-emulated windows, I call [NSApp windowWithWindowNumber: ] (Cocoa) and HIWindowFromCGWindowID() (Carbon), but they both return nil! From the very same process!
BTW, all this actions work perfectly when I intrude into Safari process and others...
So, the questions are:
-
How did X11 create such windows which are not accessible from the very same process?
-
How can I get pointers to X11 windows (NSWindow *, CGContextRef, or, at least, anything...) and host my graphics (I don't even speak about NSViews) upon them?
Thanks a lot in advance!
发布评论
评论(2)
据我了解,X11 使用自己的 Windows 服务器和通用堆栈。这就是为什么它无需特殊端口即可运行 X11 应用程序。
它只有一层模仿 Cocoa windows 的响应层,以便它可以与通用接口进行通信。它不是伪装的 Cocoa 堆栈,而是表面上伪装成 Cocoa 的 X11 堆栈。因此,它只响应 Cocoa 相关消息的子集。
我认为要在 X11 中做任何严肃的事情,你必须从一开始就使用 X11 API。换句话说,编写时就好像它不打算在 Mac 操作系统之上运行一样。
It is my understanding that X11 uses its own windows server and general stack. That is why it can run X11 apps without special ports.
It only has a layer of responses that mimics that of Cocoa windows such that it can communicate with the general interface. Its not a Cocoa stack in disguise, its an X11 stack superficially disguised as Cocoa. As such, it only responds to only a subset of the Cocoa related messages.
I think to do anything serious in X11 you have to use the X11 API from the start. In other words, write as if it was not intended to run on top of the Mac OS.
所有 X11.app 源代码和其他内容 (Xquartz) 均可在 Apple 官方网站(当前版本 2.3.5(服务器 85.2))。 windows创建的核心在于xpr 子目录。
为了操作 Windows,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 可绘制对象。
为了在使用私有 CoreGraphics API 创建的窗口上进行绘制,您必须使用这些私有 API。有一个名为 CGWindowContextCreate() 的函数,它通过 Quartz id 返回特定本机窗口的 CGContextRef。可以使用此上下文在窗口上绘图。但是要接收真实的上下文而不是 NULL,您必须位于创建窗口的进程中。
All the X11.app sources and other stuff (Xquartz) are available at Apple's official site (current version 2.3.5 (server 85.2)). The core of windows creation lies in xpr subdirectory.
To manipulate windows Xquartz uses Xplugin library (/usr/lib/libXplugin.dylib). Its header, /usr/include/Xplugin.h, defines functions like xp_create_surface() and others, which create windows using private CoreGraphics API, like CGSNewWindowWithOpaqueShape(). Undocumented CoreGraphicsPrivate.h or CSGPrivate.h, the result of reverse engineering, can be found over the Web. Xplugin remembers ids of such Quartz windows in its own hash and returns an opaque integer (i.e., xp_resource_id) for them. Then Xquartz associates a particular XID with this xp_resource_id and returns it to a client.
Xplugin is closed source and has no API to return native Quartz drawable by xp_resource_id or XID.
In order to draw upon a window which was created with private CoreGraphics API you have to use those private API. There's a function, named CGWindowContextCreate(), which returns CGContextRef for a particular native window by its Quartz id. It is possible to draw on the window using this context. But to receive the real context instead of NULL, you must be in a process, which created the window.