Ticket #3 (assigned task)

Opened 1 year ago

Last modified 1 year ago

Add support for Cocoa on Mac OS X

Reported by: patrick Assigned to: patrick (accepted)
Priority: minor Milestone: 3.0
Component: VR Juggler Version: HEAD
Keywords: mac cocoa threading Cc:
Completion:

Description

Since the VR Juggler 2.0 beta days, I have been planning to add Cocoa support to VR Juggler to drop the need for running an X server. Doing so means using Objective-C and Objective-C++. The build already supports these two languages, and I have had most of the Cocoa written for a while. The big hurdle has been working out the multi-threading issues in the context of Cocoa, of which there are many.

At this point, I think I have most of the big problems solved. The only remaining issue known to me at this point is how to prevent any windows from being opened from the kernel control loop thread before the primordial thread enters the Cocoa run loop. It may be possible to graft data (most likely an NSConditionLock) onto the global NSApp object using features of Objective-C.

Attachments

Change History

(in reply to: ↑ description ) 02/15/07 07:51:30 changed by patrick

Replying to patrick:

At this point, I think I have most of the big problems solved. The only remaining issue known to me at this point is how to prevent any windows from being opened from the kernel control loop thread before the primordial thread enters the Cocoa run loop. It may be possible to graft data (most likely an NSConditionLock) onto the global NSApp object using features of Objective-C.

Having a static data member of type NSConditionLock in the (uncommitted) class gadget::InputAreaCocoa should do the job. This would make the lock globally accessible to all subclasses of gadget::InputAreaCocoa, and that is exactly what is needed. The lock would have to be acquired as part of vrj::Kernel initialization (through a helper class that I still have to write) and would be released when the application gets initialized. This is accomplished by setting a delegate for the NSApp object that receives the message applicationDidFinishLaunching:.

02/19/07 11:03:39 changed by patrick

All the code for this is written and compiling, and I have been testing and debugging it. Currently, the major roadblock is that calling gluNewQuadric(3) causes a crash. I do not yet know why this happens, but I do know that the correct libGLU.dylib is being loaded.

An annoyance is that calculating the center point of an input window for mouse locking isn't behaving. The X value is correct, but the Y value is always wrong. The mouse locking itself works fine using a Carbon call, but it would be nice if the mouse would lock to the correct position. It almost seems as though Carbon and Cocoa use different coordinate frames, but the documentation suggests otherwise. (A nice benefit is that Mac OS X uses the same window system coordinate frame as VR Juggler—unlike Windows and X11.)

Aside from this, I am trying to refine the bundle handling. Doozer still needs to be taught how to build bundles, but I am pretty sure that I can do this with very little effort.

02/20/07 21:59:01 changed by patrick

Revision 19828 introduced the Gadgeteer part of this work. It is almost complete, but it needs to be tested with a multi-button mouse that has a scroll wheel.

02/21/07 20:22:00 changed by patrick

The OpenGL part of this work is nearly complete. gluNewQuadric() is still being a pain, but it seems as though the OpenGL framework needs to be initialized (or something) before gluNewQuadric() can be invoked. For example, the following program crashes:

#import <OpenGL/glu.h>

int main()
{
   gluNewQuadric();
   return 0;
}

This one does not:

#import <AppKit/NSOpenGLView.h>
#import <OpenGL/glu.h>

int main()
{
   [NSOpenGLView defaultPixelFormat];
   gluNewQuadric();
   return 0;
}

Aside from this, the subclass of NSOpenGLView that I have written may not be getting sized correctly, but viewports are working. It may well be that my approach of making the custom OpenGL view a subview of the InputViewCocoa view may not quite work out. It's close, though.

(follow-ups: ↓ 6 ↓ 9 ) 02/22/07 21:52:32 changed by patrick

The problems with gluNewQuadric() can be fixed by ensuring that that function is called only when an OpenGL context is active. Revision 19839 takes care of that so that the OpenGL simulator code can be used with Cocoa. Now, there are three known remaining major problems:

  1. Resizing GL windows crashes the window server.
  2. When the OpenGL view (an instance of GlViewCocoa) is added as a subview of the InputViewCocoa, the OpenGL view does not occupy the entire window.
  3. When opening a full screen window, the window is behind the menu bar and Dock.

(in reply to: ↑ 5 ; follow-up: ↓ 7 ) 02/28/07 07:20:00 changed by patrick

Replying to patrick:

Now, there are three known remaining major problems:

1. Resizing GL windows crashes the window server.
2. When the OpenGL view (an instance of GlViewCocoa) is added as a subview of the InputViewCocoa, the OpenGL view does not occupy the entire window.

This problem is most likely due to Cocoa not really supporting overlapping views. I need to come up with a different design that still allows reducing code duplication between Gadgeteer's InputViewCocoa and VR Juggler's GlViewCocoa. The most likely course is to push more code into gadget::InputAreaCocoa. What I would really like, however, is to use the NSWindow method -setDelegate: to take care of handling input. That was my original plan, but I could not figure out how to do it and thus started trying to make GlViewCocoa instances subviews of InputViewCocoa instances.

(in reply to: ↑ 6 ) 02/28/07 10:57:25 changed by patrick

Replying to patrick:

Replying to patrick:

2. When the OpenGL view (an instance of GlViewCocoa) is added as a subview of the InputViewCocoa, the OpenGL view does not occupy the entire window.

This problem is most likely due to Cocoa not really supporting overlapping views. I need to come up with a different design that still allows reducing code duplication between Gadgeteer's InputViewCocoa and VR Juggler's GlViewCocoa. The most likely course is to push more code into gadget::InputAreaCocoa. What I would really like, however, is to use the NSWindow method -setDelegate: to take care of handling input. That was my original plan, but I could not figure out how to do it and thus started trying to make GlViewCocoa instances subviews of InputViewCocoa instances.

A delegate for NSWindow is not the way to go. My understanding of this was incomplete. The "Delegate Methods" section of the NSWindow reference identifies what messages are delivered to the window delegate, and none relate to handling keyboard/mouse events. NSView does use a delegate, so the most reasonable approach seems to be refactoring the code to put as much complexity into gadget::InputAreaCocoa as is feasible.

02/28/07 20:45:15 changed by patrick

Revision 19844 takes care of refactoring InputViewCocoa and gadget::InputAreaCocoa so that GlViewCocoa can remain relatively simple. By having GlViewCocoa as the content view for the NSWindow instance fixes the problems with the OpenGL view not occupying the entire window when the window is configured to receive keyboard and mouse events.

(in reply to: ↑ 5 ) 02/28/07 21:31:57 changed by patrick

The problem with full screen windows being behind the application menu and the Dock is resolved. The window has to be set to be at the top-most level when it is to occupy the full screen. This also takes care of the always-on-top property for display windows.

Unfortunately, a new problem has arisen. When a window is full screen, mouse move events are not being registered. This, of course, causes problems with receiving input. Maybe the problem has to do with the tracking rectangle. Other keyboard and mouse events work, so it is probably not an issue with the GlViewCocoa instance not being the first responder.

03/03/07 23:32:54 changed by patrick

Revision 19854 officially connected the Cocoa input window code to the Gadgeteer build. The last known bug in that code was fixed in Revision 19853. At this point, the following big tasks remain:

  1. Fix window server crash when resizing OpenGL windows.
  2. Fix weird window server refresh problem when exiting an application that used a full screen window.
  3. Update Doozer 2.1 to support bundle creation (almost done).
  4. Deal with application launching issues, especially in the context of Maestro.
  5. Fix lingering crash-on-exit bugs (which should be helpful for all platforms).
  6. Fix document handling in CocoaWrapper. This may require changing how the application menu is put together and/or using NSDocument in some way for config file handling.

03/04/07 14:52:39 changed by patrick

The OpenGL Programming Guide for Mac OS X says that code wanting to do full screen rendering is supposed to capture the display. This is not working for me. I just end up with a black screen, and when releasing the captured display after closing the window, I still get the weird behavior from the window server that requires refreshing the display. I am not sure how critical capturing the diplay is, but it sounds as though it is important for correctness.

03/10/07 10:08:12 changed by patrick

Application launching seems not to be quite the big issue that I had feared. Using open to launch the application bundle works, of course, but it is also possible to execute the bundle's contained binary directly and pass it command line arguments while still getting the correct Cocoa event behavior. This needs to be tested more, but this is looking very promising.

(follow-up: ↓ 15 ) 03/13/07 18:46:19 changed by patrick

  • status changed from new to assigned.

03/13/07 18:46:32 changed by patrick

  • milestone set to 2.2.

(in reply to: ↑ 13 ; follow-up: ↓ 16 ) 03/13/07 22:00:47 changed by patrick

Replying to patrick:

[...] it is also possible to execute the bundle's contained binary directly and pass it command line arguments while still getting the correct Cocoa event behavior. This needs to be tested more, but this is looking very promising.

An interesting aspect of this is that something (Core Foundation, AppKit, NSApplication, whatever) looks at argv, and for each argument that ends up being a file, the application:openFiles: message is delivered to the NSApplication delegate. (The current incarnation of the NSApplication delegate responds to this message and to application:openFile: so that VR Juggler application bundles can be identified as .jconf file handlers. This may or may not be a useful feature.) If the VR Juggler application main() function loads configuration files, as one would expect, then the configuration files get loaded twice. This tends to be a problem since the VR Juggler run-time reconfiguration is designed around an add/remove/add paradigm rather than an add/add—where the second "add" would be a replacement operation—paradigm.

So far, I have identified three options for handling this case:

  1. One way that this could be handled is to have the VR Juggler preferences file control whether the NSApplication delegate will do anything when it receives the application:openFile: and application:openFiles: messages. However, that isn't a very fine-grained approach, and it wouldn't be very pleasant to deal with that.
  2. Another option is to have the user code set an attribute at run time in the Cocoa wrapper class via vrj::Kernel.
  3. Ultimately, what I think would work the best would be to have some setting in Info.plist for the application bundle, but I don't know if that is possible. I have yet more reading to do.

Regarding the VR Juggler preferences file, this is something that I have just been tinkering with. It is not necessarily something that will be included with the Cocoa support, and even if it is, it may not do much. After all, VR Juggler configuration files are where it's at. Application-specific preferences files are far outside the scope of VR Juggler, and therefore, a general-purpose preferences file would have limited utility.

(in reply to: ↑ 15 ) 03/14/07 20:45:29 changed by patrick

Replying to patrick:

Replying to patrick:

[...] it is also possible to execute the bundle's contained binary directly and pass it command line arguments while still getting the correct Cocoa event behavior. This needs to be tested more, but this is looking very promising.

An interesting aspect of this is that something (Core Foundation, AppKit, NSApplication, whatever) looks at argv, and for each argument that ends up being a file, the application:openFiles: message is delivered to the NSApplication delegate.

[...]

So far, I have identified three options for handling this case:

1. One way that this could be handled is to have the VR Juggler preferences file control whether the NSApplication delegate will do anything when it receives the application:openFile: and application:openFiles: messages. However, that isn't a very fine-grained approach, and it wouldn't be very pleasant to deal with that.
2. Another option is to have the user code set an attribute at run time in the Cocoa wrapper class via vrj::Kernel.
3. Ultimately, what I think would work the best would be to have some setting in Info.plist for the application bundle, but I don't know if that is possible. I have yet more reading to do.

The third option ended up working out, and it worked out quite nicely. Using custom property lists is a perfectly reasonable thing to do, so I have introduced an optional VR Juggler property list file vrjuggler.plist. Users can choose to set the boolean property VRJConfigHandling to control how the application delegate responds to application:openFile: and application:openFiles: messages.

03/14/07 21:06:47 changed by patrick

Doozer 2.1.4 now has working support for building Mac OS X application bundles, and vrj.appdefs.mk is taking advantage of it.

03/14/07 21:30:38 changed by patrick

For anyone who is interested, here are some screen shots of cubes running on Mac OS X using Cocoa with no X server running (check the Dock):

03/25/07 20:09:52 changed by patrick

Revision 19884 includes nearly all the code for the VR Juggler module to be able to use Cocoa for its OpenGL windows. It is still not quite ready for testing, though it will be soon. See the commit log for the details about what remains (aside from the pending bugs noted here).

03/27/07 15:17:16 changed by patrick

With some recent changes that I made to vrj::CocoaWrapper (included with Revision 19844, the problem with the desktop turning black after exiting an application using full screen window is sort of fixed. If exiting the application with Command-Q, things are fine. Using the application shutdown key causes the black screen, though. There must be some clean-up step that gets missed when using the shutdown key.

Related to this, I posted a message to the Apple cocoa-dev list, but I have not received any responsye et. Previous posts have gotten responses quite quickly, and at this point, I do not expect a response at all.

03/27/07 15:29:18 changed by patrick

I posted a message to the vrjuggler-devel mailing list proposing a change to vpr::Thread that is needed for the Cocoa support to work. This change would add start and exit notifications for all threads. For Cocoa, this is used to create and destroy auto-release pools for the threads that Juggler creates on its own. I am hopeful that the proposal will be received well. So far, Dan is in favor. :)

(follow-up: ↓ 23 ) 03/27/07 15:45:14 changed by patrick

The issue with crashes on exit seems to be solved if the application controller waits on the VR Juggler kernel to shut down. These problems were most prevalent when using Command-Q to exit the running application without using the Juggler shutdown key. In that case, I am pretty sure that there was a race condition wherein the kernel was in the process of shutting itself down when the NSApplication method run called exit(3). That had the effect of deleting all the singletons, which were still in the process of cleaning up due to the kernel shutting down.

Additionally, vrj::Kernel::stop() is telling the main application run loop to stop, and this seems beneficial. Right now, it is using the stop: message, but I am wondering if using terminate: would give better results. In this case, "better results" would be better emulation of the shutdown behavior seen when using Command-Q.

(in reply to: ↑ 22 ) 03/28/07 07:41:00 changed by patrick

Replying to patrick:

Additionally, vrj::Kernel::stop() is telling the main application run loop to stop, and this seems beneficial. Right now, it is using the stop: message, but I am wondering if using terminate: would give better results. In this case, "better results" would be better emulation of the shutdown behavior seen when using Command-Q.

Making the above change (see Revision 19887) did work. Now, the desktop does not turn black after exiting applications using full screen windows. This is true for both clean shutdown methods: using the kernel shutdown key and using Command-Q.

04/05/07 16:57:48 changed by patrick

Revision 19897 adds thread start/exit notification to vpr::Thread. This was implemted using Boost.Signals according to the design that I proposed on the vrjuggler-devel mailing list. The full Cocoa support is now ready to be attached to the build and made the default windowing interface on Mac OS X.

04/05/07 17:33:12 changed by patrick

Revision 19906 makes Cocoa the default windowing system on Mac OS X.

05/11/07 08:19:59 changed by patrick

Revision 20201 fixes the "Open Recent" menu on the trunk, and this change will eventually be merged to the 2.2 branch. However, selecting a configuration file from this menu may have no effect depending on the settings in the application bundle's Contents/Resources/vrjuggler.plist file. It may be necessary to make a subclass of NSDocument to get things working better, but I do not yet understand the full implications of document-based applications in the context of VR Juggler. It may well be that the Cocoa usage could be made even more flexible than it already is, and that would certainly be a plus. It is also my hope that by addressing this issue, I will be able to find a fix for the PyJuggler problems with Cocoa.

08/13/07 11:14:22 changed by patrick

  • milestone changed from 2.2 to 3.0.

Any further refinements to the Cocoa support will be targeted for VR Juggler 3.0.


Add/Change #3 (Add support for Cocoa on Mac OS X)




Change Properties
Action