diff --git a/panda/src/cocoadisplay/Sources.pp b/panda/src/cocoadisplay/Sources.pp new file mode 100644 index 0000000000..ac9b119e46 --- /dev/null +++ b/panda/src/cocoadisplay/Sources.pp @@ -0,0 +1,34 @@ +#define BUILD_DIRECTORY $[and $[IS_OSX],$[HAVE_GL]] + +#define OTHER_LIBS p3interrogatedb:c p3dconfig:c p3dtoolconfig:m \ + p3dtoolutil:c p3dtoolbase:c p3dtool:m + +#define OSX_SYS_FRAMEWORKS ApplicationServices AppKit + +#begin lib_target + #define TARGET p3cocoadisplay + #define LOCAL_LIBS \ + p3display p3putil p3glgsg + + #define COMBINED_SOURCES $[TARGET]_composite1.mm + + #define INSTALL_HEADERS \ + config_cocoadisplay.h \ + cocoaGraphicsPipe.h cocoaGraphicsPipe.I \ + cocoaGraphicsWindow.h cocoaGraphicsWindow.I \ + cocoaGraphicsStateGuardian.h cocoaGraphicsStateGuardian.I \ + cocoaPandaView.h cocoaPandaWindowDelegate.h + + #define INCLUDED_SOURCES \ + config_cocoadisplay.mm \ + cocoaGraphicsPipe.mm \ + cocoaGraphicsStateGuardian.mm \ + cocoaGraphicsWindow.mm \ + cocoaPandaView.mm \ + cocoaPandaWindow.mm \ + cocoaPandaWindowDelegate.mm + + #define SOURCES \ + $[INSTALL_HEADERS] + +#end lib_target diff --git a/panda/src/cocoadisplay/cocoaGraphicsBuffer.cxx b/panda/src/cocoadisplay/cocoaGraphicsBuffer.cxx new file mode 100644 index 0000000000..b34ca33e07 --- /dev/null +++ b/panda/src/cocoadisplay/cocoaGraphicsBuffer.cxx @@ -0,0 +1,230 @@ +// Filename: eglGraphicsBuffer.cxx +// Created by: pro-rsoft (13Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "eglGraphicsBuffer.h" +#include "eglGraphicsStateGuardian.h" +#include "config_egldisplay.h" +#include "eglGraphicsPipe.h" + +#include "graphicsPipe.h" +#include "pStatTimer.h" + +TypeHandle eglGraphicsBuffer::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsBuffer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +eglGraphicsBuffer:: +eglGraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe, + const string &name, + const FrameBufferProperties &fb_prop, + const WindowProperties &win_prop, + int flags, + GraphicsStateGuardian *gsg, + GraphicsOutput *host) : + GraphicsBuffer(engine, pipe, name, fb_prop, win_prop, flags, gsg, host) +{ + eglGraphicsPipe *egl_pipe; + DCAST_INTO_V(egl_pipe, _pipe); + _pbuffer = EGL_NO_SURFACE; + + // Since the pbuffer never gets flipped, we get screenshots from the + // same buffer we draw into. + _screenshot_buffer_type = _draw_buffer_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsBuffer::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +eglGraphicsBuffer:: +~eglGraphicsBuffer() { + nassertv(_pbuffer == EGL_NO_SURFACE); +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsBuffer::begin_frame +// Access: Public, Virtual +// Description: This function will be called within the draw thread +// before beginning rendering for a given frame. It +// should do whatever setup is required, and return true +// if the frame should be rendered, or false if it +// should be skipped. +//////////////////////////////////////////////////////////////////// +bool eglGraphicsBuffer:: +begin_frame(FrameMode mode, Thread *current_thread) { + PStatTimer timer(_make_current_pcollector, current_thread); + + begin_frame_spam(mode); + if (_gsg == (GraphicsStateGuardian *)NULL) { + return false; + } + + eglGraphicsStateGuardian *eglgsg; + DCAST_INTO_R(eglgsg, _gsg, false); + if (!eglMakeCurrent(eglgsg->_egl_display, _pbuffer, _pbuffer, eglgsg->_context)) { + egldisplay_cat.error() << "Failed to call eglMakeCurrent: " + << get_egl_error_string(eglGetError()) << "\n"; + } + + // Now that we have made the context current to a window, we can + // reset the GSG state if this is the first time it has been used. + // (We can't just call reset() when we construct the GSG, because + // reset() requires having a current context.) + eglgsg->reset_if_new(); + + if (mode == FM_render) { + CDLockedReader cdata(_cycler); + for (size_t i = 0; i != cdata->_textures.size(); ++i) { + const RenderTexture &rt = cdata->_textures[i]; + RenderTextureMode rtm_mode = rt._rtm_mode; + if (rtm_mode == RTM_bind_or_copy) { + CDWriter cdataw(_cycler, cdata, false); + nassertr(cdata->_textures.size() == cdataw->_textures.size(), false); + cdataw->_textures[i]._rtm_mode = RTM_copy_texture; + } + } + clear_cube_map_selection(); + } + + _gsg->set_current_properties(&get_fb_properties()); + return _gsg->begin_frame(current_thread); +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsBuffer::end_frame +// Access: Public, Virtual +// Description: This function will be called within the draw thread +// after rendering is completed for a given frame. It +// should do whatever finalization is required. +//////////////////////////////////////////////////////////////////// +void eglGraphicsBuffer:: +end_frame(FrameMode mode, Thread *current_thread) { + end_frame_spam(mode); + nassertv(_gsg != (GraphicsStateGuardian *)NULL); + + if (mode == FM_render) { + copy_to_textures(); + } + + _gsg->end_frame(current_thread); + + if (mode == FM_render) { + trigger_flip(); + clear_cube_map_selection(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsBuffer::close_buffer +// Access: Protected, Virtual +// Description: Closes the buffer right now. Called from the window +// thread. +//////////////////////////////////////////////////////////////////// +void eglGraphicsBuffer:: +close_buffer() { + if (_gsg != (GraphicsStateGuardian *)NULL) { + eglGraphicsStateGuardian *eglgsg; + DCAST_INTO_V(eglgsg, _gsg); + if (!eglMakeCurrent(eglgsg->_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { + egldisplay_cat.error() << "Failed to call eglMakeCurrent: " + << get_egl_error_string(eglGetError()) << "\n"; + } + _gsg.clear(); + + if (_pbuffer != EGL_NO_SURFACE) { + if (!eglDestroySurface(_egl_display, _pbuffer)) { + egldisplay_cat.error() << "Failed to destroy surface: " + << get_egl_error_string(eglGetError()) << "\n"; + } + _pbuffer = EGL_NO_SURFACE; + } + } + + _is_valid = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: eglGraphicsBuffer::open_buffer +// Access: Protected, Virtual +// Description: Opens the buffer right now. Called from the window +// thread. Returns true if the buffer is successfully +// opened, or false if there was a problem. +//////////////////////////////////////////////////////////////////// +bool eglGraphicsBuffer:: +open_buffer() { + eglGraphicsPipe *egl_pipe; + DCAST_INTO_R(egl_pipe, _pipe, false); + + // GSG Creation/Initialization + eglGraphicsStateGuardian *eglgsg; + if (_gsg == 0) { + // There is no old gsg. Create a new one. + eglgsg = new eglGraphicsStateGuardian(_engine, _pipe, NULL); + eglgsg->choose_pixel_format(_fb_properties, egl_pipe->get_display(), egl_pipe->get_screen(), true, false); + _gsg = eglgsg; + } else { + // If the old gsg has the wrong pixel format, create a + // new one that shares with the old gsg. + DCAST_INTO_R(eglgsg, _gsg, false); + if (!eglgsg->get_fb_properties().subsumes(_fb_properties)) { + eglgsg = new eglGraphicsStateGuardian(_engine, _pipe, eglgsg); + eglgsg->choose_pixel_format(_fb_properties, egl_pipe->get_display(), egl_pipe->get_screen(), true, false); + _gsg = eglgsg; + } + } + + if (eglgsg->_fbconfig == None) { + // If we didn't use an fbconfig to create the GSG, we can't create + // a PBuffer. + return false; + } + + int attrib_list[] = { + EGL_WIDTH, _x_size, + EGL_HEIGHT, _y_size, + EGL_NONE + }; + + _pbuffer = eglCreatePbufferSurface(eglgsg->_egl_display, eglgsg->_fbconfig, attrib_list); + + if (_pbuffer == EGL_NO_SURFACE) { + egldisplay_cat.error() + << "Failed to create EGL pbuffer surface: " + << get_egl_error_string(eglGetError()) << "\n"; + return false; + } + + if (!eglMakeCurrent(eglgsg->_egl_display, _pbuffer, _pbuffer, eglgsg->_context)) { + egldisplay_cat.error() << "Failed to call eglMakeCurrent: " + << get_egl_error_string(eglGetError()) << "\n"; + } + eglgsg->reset_if_new(); + if (!eglgsg->is_valid()) { + close_buffer(); + return false; + } + if (!eglgsg->get_fb_properties().verify_hardware_software + (_fb_properties, eglgsg->get_gl_renderer())) { + close_buffer(); + return false; + } + _fb_properties = eglgsg->get_fb_properties(); + + _is_valid = true; + return true; +} diff --git a/panda/src/cocoadisplay/cocoaGraphicsBuffer.h b/panda/src/cocoadisplay/cocoaGraphicsBuffer.h new file mode 100644 index 0000000000..155a9aba2f --- /dev/null +++ b/panda/src/cocoadisplay/cocoaGraphicsBuffer.h @@ -0,0 +1,69 @@ +// Filename: eglGraphicsBuffer.h +// Created by: pro-rsoft (13Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGLGRAPHICSBUFFER_H +#define EGLGRAPHICSBUFFER_H + +#include "pandabase.h" + +#include "eglGraphicsPipe.h" +#include "graphicsBuffer.h" + +//////////////////////////////////////////////////////////////////// +// Class : eglGraphicsBuffer +// Description : An offscreen buffer in the EGL environment. This +// creates an EGL pbuffer. +//////////////////////////////////////////////////////////////////// +class eglGraphicsBuffer : public GraphicsBuffer { +public: + eglGraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe, + const string &name, + const FrameBufferProperties &fb_prop, + const WindowProperties &win_prop, + int flags, + GraphicsStateGuardian *gsg, + GraphicsOutput *host); + virtual ~eglGraphicsBuffer(); + + virtual bool begin_frame(FrameMode mode, Thread *current_thread); + virtual void end_frame(FrameMode mode, Thread *current_thread); + +protected: + virtual void close_buffer(); + virtual bool open_buffer(); + +private: + X11_Display *_display; + EGLSurface _pbuffer; + EGLDisplay _egl_display; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GraphicsBuffer::init_type(); + register_type(_type_handle, "eglGraphicsBuffer", + GraphicsBuffer::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/cocoadisplay/cocoaGraphicsPipe.I b/panda/src/cocoadisplay/cocoaGraphicsPipe.I new file mode 100644 index 0000000000..4d7ca9175c --- /dev/null +++ b/panda/src/cocoadisplay/cocoaGraphicsPipe.I @@ -0,0 +1,36 @@ +// Filename: cocoaGraphicsPipe.I +// Created by: rdb (14May12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + + +/*//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsPipe::get_display +// Access: Public +// Description: Returns a pointer to the X display associated with +// the pipe: the display on which to create the windows. +//////////////////////////////////////////////////////////////////// +INLINE X11_Display *CocoaGraphicsPipe:: +get_display() const { + return _display; +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsPipe::get_screen +// Access: Public +// Description: Returns the X screen number associated with the pipe. +//////////////////////////////////////////////////////////////////// +INLINE int CocoaGraphicsPipe:: +get_screen() const { + return _screen; +} +*/ \ No newline at end of file diff --git a/panda/src/cocoadisplay/cocoaGraphicsPipe.h b/panda/src/cocoadisplay/cocoaGraphicsPipe.h new file mode 100644 index 0000000000..b223e4fe06 --- /dev/null +++ b/panda/src/cocoadisplay/cocoaGraphicsPipe.h @@ -0,0 +1,91 @@ +// Filename: cocoaGraphicsPipe.h +// Created by: rdb (14May12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef COCOAGRAPHICSPIPE_H +#define COCOAGRAPHICSPIPE_H + +#include "pandabase.h" +#include "graphicsWindow.h" +#include "graphicsPipe.h" +#include "lightMutex.h" +#include "lightReMutex.h" + +#import +#import + +class FrameBufferProperties; + +//////////////////////////////////////////////////////////////////// +// Class : CocoaGraphicsPipe +// Description : This graphics pipe represents the interface for +// creating OpenGL graphics windows on a Cocoa-based +// (e.g. Mac OS X) client. +//////////////////////////////////////////////////////////////////// +class CocoaGraphicsPipe : public GraphicsPipe { +public: + CocoaGraphicsPipe(); + CocoaGraphicsPipe(CGDirectDisplayID display); + CocoaGraphicsPipe(NSScreen *screen); + virtual ~CocoaGraphicsPipe(); + + virtual string get_interface_name() const; + static PT(GraphicsPipe) pipe_constructor(); + +public: + virtual PreferredWindowThread get_preferred_window_thread() const; + +protected: + virtual PT(GraphicsOutput) make_output(const string &name, + const FrameBufferProperties &fb_prop, + const WindowProperties &win_prop, + int flags, + GraphicsEngine *engine, + GraphicsStateGuardian *gsg, + GraphicsOutput *host, + int retry, + bool &precertify); + +private: + void load_display_information(); + + // _display and _screen refer to the same thing, + // NSScreen being the tiny Cocoa wrapper around the Quartz + // display ID. NSScreen isn't generally useful, but we need + // it when creating the window. + CGDirectDisplayID _display; + NSScreen *_screen; + + friend class CocoaGraphicsWindow; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GraphicsPipe::init_type(); + register_type(_type_handle, "CocoaGraphicsPipe", + GraphicsPipe::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "cocoaGraphicsPipe.I" + +#endif diff --git a/panda/src/cocoadisplay/cocoaGraphicsPipe.mm b/panda/src/cocoadisplay/cocoaGraphicsPipe.mm new file mode 100644 index 0000000000..a0e17c31fa --- /dev/null +++ b/panda/src/cocoadisplay/cocoaGraphicsPipe.mm @@ -0,0 +1,362 @@ +// Filename: cocoaGraphicsPipe.mm +// Created by: rdb (14May12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "cocoaGraphicsPipe.h" +//#include "cocoaGraphicsBuffer.h" +#include "cocoaGraphicsWindow.h" +#include "cocoaGraphicsStateGuardian.h" +#include "config_cocoadisplay.h" +#include "frameBufferProperties.h" + +#import +#import + +TypeHandle CocoaGraphicsPipe::_type_handle; + +static void init_app() { + if (NSApp == nil) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [NSApplication sharedApplication]; + + [NSApp setActivationPolicy: nil]; + [NSApp finishLaunching]; + [NSApp activateIgnoringOtherApps: YES]; + + //[pool release]; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsPipe::Constructor +// Access: Public +// Description: Uses the main screen (the one the user is most +// likely to be working in at the moment). +//////////////////////////////////////////////////////////////////// +CocoaGraphicsPipe:: +CocoaGraphicsPipe() { + _supported_types = OT_window | OT_buffer | OT_texture_buffer; + _is_valid = true; + + init_app(); + + _screen = [NSScreen mainScreen]; + NSNumber *num = [[_screen deviceDescription] objectForKey: @"NSScreenNumber"]; + _display = (CGDirectDisplayID) [num pointerValue]; + + _display_width = CGDisplayPixelsWide(_display); + _display_height = CGDisplayPixelsHigh(_display); + load_display_information(); + + cocoadisplay_cat.debug() + << "Creating CocoaGraphicsPipe for main screen " + << _screen << " with display ID " << _display << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsPipe::Constructor +// Access: Public +// Description: Takes a CoreGraphics display ID. +//////////////////////////////////////////////////////////////////// +CocoaGraphicsPipe:: +CocoaGraphicsPipe(CGDirectDisplayID display) { + _supported_types = OT_window | OT_buffer | OT_texture_buffer; + _is_valid = true; + _display = display; + + init_app(); + + // Iterate over the screens to find the one with our display ID. + NSEnumerator *e = [[NSScreen screens] objectEnumerator]; + while (NSScreen *screen = (NSScreen *) [e nextObject]) { + NSNumber *num = [[screen deviceDescription] objectForKey: @"NSScreenNumber"]; + if (display == (CGDirectDisplayID) [num pointerValue]) { + _screen = screen; + break; + } + } + + _display_width = CGDisplayPixelsWide(_display); + _display_height = CGDisplayPixelsHigh(_display); + load_display_information(); + + cocoadisplay_cat.debug() + << "Creating CocoaGraphicsPipe for screen " + << _screen << " with display ID " << _display << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsPipe::Constructor +// Access: Public +// Description: Takes an NSScreen pointer. +//////////////////////////////////////////////////////////////////// +CocoaGraphicsPipe:: +CocoaGraphicsPipe(NSScreen *screen) { + _supported_types = OT_window | OT_buffer | OT_texture_buffer; + _is_valid = true; + + init_app(); + + if (screen == nil) { + _screen = [NSScreen mainScreen]; + } else { + _screen = screen; + } + NSNumber *num = [[_screen deviceDescription] objectForKey: @"NSScreenNumber"]; + _display = (CGDirectDisplayID) [num pointerValue]; + + _display_width = CGDisplayPixelsWide(_display); + _display_height = CGDisplayPixelsHigh(_display); + load_display_information(); + + cocoadisplay_cat.debug() + << "Creating CocoaGraphicsPipe for screen " + << _screen << " with display ID " << _display << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsPipe::load_display_information +// Access: Private +// Description: Fills in _display_information. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsPipe:: +load_display_information() { + //TODO: read display modes and display information. + _display_information->_vendor_id = CGDisplayVendorNumber(_display); + //_display_information->_device_id = CGDisplayUnitNumber(_display); + //_display_information->_device_id = CGDisplaySerialNumber(_display); + + // Display modes +#if defined(MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + CFArrayRef modes = CGDisplayCopyAllDisplayModes(_display, NULL); + size_t num_modes = CFArrayGetCount(modes); + _display_information->_total_display_modes = num_modes; + _display_information->_display_mode_array = new DisplayMode[num_modes]; + + for (size_t i = 0; i < num_modes; ++i) { + CGDisplayModeRef mode = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); + + _display_information->_display_mode_array[i].width = CGDisplayModeGetWidth(mode); + _display_information->_display_mode_array[i].height = CGDisplayModeGetHeight(mode); + _display_information->_display_mode_array[i].refresh_rate = CGDisplayModeGetRefreshRate(mode); + _display_information->_display_mode_array[i].fullscreen_only = false; + + // Read number of bits per pixels from the pixel encoding + CFStringRef encoding = CGDisplayModeCopyPixelEncoding(mode); + if (CFStringCompare(encoding, CFSTR(kIO64BitDirectPixels), + kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + _display_information->_display_mode_array[i].bits_per_pixel = 64; + + } else if (CFStringCompare(encoding, CFSTR(kIO32BitFloatPixels), + kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + _display_information->_display_mode_array[i].bits_per_pixel = 32; + + } else if (CFStringCompare(encoding, CFSTR(kIO16BitFloatPixels), + kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + _display_information->_display_mode_array[i].bits_per_pixel = 16; + + } else if (CFStringCompare(encoding, CFSTR(IOYUV422Pixels), + kCFCompareCaseInsensitive) == kCFCompareEqualTo || + CFStringCompare(encoding, CFSTR(IO8BitOverlayPixels), + kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + _display_information->_display_mode_array[i].bits_per_pixel = 8; + } else { + // The other possible pixel formats in IOKit/IOGraphicsTypes.h + // have strings like "PPPP" or "-RRRRRGGGGGBBBBB", so the number + // of bits per pixel can be deduced from the string length. Nifty! + _display_information->_display_mode_array[i].bits_per_pixel = CFStringGetLength(encoding); + } + CFRelease(encoding); + } + CFRelease(modes); + +#else + CFArrayRef modes = CGDisplayAvailableModes(_display); + size_t num_modes = CFArrayGetCount(modes); + _display_information->_total_display_modes = num_modes; + _display_information->_display_mode_array = new DisplayMode[num_modes]; + + for (size_t i = 0; i < num_modes; ++i) { + CFDictionaryRef mode = (CFDictionaryRef) CFArrayGetValueAtIndex(modes, i); + + CFNumberGetValue((CFNumberRef) CFDictionaryGetValue(mode, kCGDisplayWidth), + kCFNumberIntType, &_display_information->_display_mode_array[i].width); + + CFNumberGetValue((CFNumberRef) CFDictionaryGetValue(mode, kCGDisplayHeight), + kCFNumberIntType, &_display_information->_display_mode_array[i].height); + + CFNumberGetValue((CFNumberRef) CFDictionaryGetValue(mode, kCGDisplayBitsPerPixel), + kCFNumberIntType, &_display_information->_display_mode_array[i].bits_per_pixel); + + CFNumberGetValue((CFNumberRef) CFDictionaryGetValue(mode, kCGDisplayRefreshRate), + kCFNumberIntType, &_display_information->_display_mode_array[i].refresh_rate); + + _display_information->_display_mode_array[i].fullscreen_only = false; + } +#endif + + //XXX remove when done + cerr << *_display_information << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsPipe::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CocoaGraphicsPipe:: +~CocoaGraphicsPipe() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsPipe::get_interface_name +// Access: Published, Virtual +// Description: Returns the name of the rendering interface +// associated with this GraphicsPipe. This is used to +// present to the user to allow him/her to choose +// between several possible GraphicsPipes available on a +// particular platform, so the name should be meaningful +// and unique for a given platform. +//////////////////////////////////////////////////////////////////// +string CocoaGraphicsPipe:: +get_interface_name() const { + return "OpenGL"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsPipe::pipe_constructor +// Access: Public, Static +// Description: This function is passed to the GraphicsPipeSelection +// object to allow the user to make a default +// CocoaGraphicsPipe. +//////////////////////////////////////////////////////////////////// +PT(GraphicsPipe) CocoaGraphicsPipe:: +pipe_constructor() { + return new CocoaGraphicsPipe; +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsPipe::get_preferred_window_thread +// Access: Public, Virtual +// Description: Returns an indication of the thread in which this +// GraphicsPipe requires its window processing to be +// performed: typically either the app thread (e.g. X) +// or the draw thread (Windows). +//////////////////////////////////////////////////////////////////// +GraphicsPipe::PreferredWindowThread +CocoaGraphicsPipe::get_preferred_window_thread() const { + // The NSView and NSWindow classes are not thread-safe, + // they can only be called from the main thread! + return PWT_app; +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsPipe::make_output +// Access: Protected, Virtual +// Description: Creates a new window on the pipe, if possible. +//////////////////////////////////////////////////////////////////// +PT(GraphicsOutput) CocoaGraphicsPipe:: +make_output(const string &name, + const FrameBufferProperties &fb_prop, + const WindowProperties &win_prop, + int flags, + GraphicsEngine *engine, + GraphicsStateGuardian *gsg, + GraphicsOutput *host, + int retry, + bool &precertify) { + + if (!_is_valid) { + return NULL; + } + + CocoaGraphicsStateGuardian *cocoagsg = 0; + if (gsg != 0) { + DCAST_INTO_R(cocoagsg, gsg, NULL); + } + + // First thing to try: a CocoaGraphicsWindow + + if (retry == 0) { + if (((flags&BF_require_parasite)!=0)|| + ((flags&BF_refuse_window)!=0)|| + ((flags&BF_resizeable)!=0)|| + ((flags&BF_size_track_host)!=0)|| + ((flags&BF_rtt_cumulative)!=0)|| + ((flags&BF_can_bind_color)!=0)|| + ((flags&BF_can_bind_every)!=0)) { + return NULL; + } + return new CocoaGraphicsWindow(engine, this, name, fb_prop, win_prop, + flags, gsg, host); + } +/* + // Second thing to try: a GLES(2)GraphicsBuffer + if (retry == 1) { + if ((host==0)|| + // (!gl_support_fbo)|| + ((flags&BF_require_parasite)!=0)|| + ((flags&BF_require_window)!=0)) { + return NULL; + } + // Early failure - if we are sure that this buffer WONT + // meet specs, we can bail out early. + if ((flags & BF_fb_props_optional)==0) { + if ((fb_prop.get_indexed_color() > 0)|| + (fb_prop.get_back_buffers() > 0)|| + (fb_prop.get_accum_bits() > 0)|| + (fb_prop.get_multisamples() > 0)) { + return NULL; + } + } + // Early success - if we are sure that this buffer WILL + // meet specs, we can precertify it. + if ((cocoagsg != 0) && + (cocoagsg->is_valid()) && + (!cocoagsg->needs_reset()) && + (cocoagsg->_supports_framebuffer_object) && + (cocoagsg->_glDrawBuffers != 0)&& + (fb_prop.is_basic())) { + precertify = true; + } + + return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop, + flags, gsg, host); + } + + // Third thing to try: a CocoaGraphicsBuffer + if (retry == 2) { + if (((flags&BF_require_parasite)!=0)|| + ((flags&BF_require_window)!=0)|| + ((flags&BF_resizeable)!=0)|| + ((flags&BF_size_track_host)!=0)) { + return NULL; + } + + if (!support_rtt) { + if (((flags&BF_rtt_cumulative)!=0)|| + ((flags&BF_can_bind_every)!=0)) { + // If we require Render-to-Texture, but can't be sure we + // support it, bail. + return NULL; + } + } + + return new CocoaGraphicsBuffer(engine, this, name, fb_prop, win_prop, + flags, gsg, host); + } +*/ + + // Nothing else left to try. + return NULL; +} diff --git a/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.I b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.I new file mode 100644 index 0000000000..69311ef37c --- /dev/null +++ b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.I @@ -0,0 +1,26 @@ +// Filename: cocoaGraphicsStateGuardian.I +// Created by: rdb (14May12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsStateGuardian::get_fb_properties +// Access: Private +// Description: Gets the FrameBufferProperties for all windows and +// buffers that use this GSG. +//////////////////////////////////////////////////////////////////// +INLINE const FrameBufferProperties &CocoaGraphicsStateGuardian:: +get_fb_properties() const { + return _fbprops; +} + + diff --git a/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.h b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.h new file mode 100644 index 0000000000..c8fd5a6f92 --- /dev/null +++ b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.h @@ -0,0 +1,70 @@ +// Filename: cocoaGraphicsStateGuardian.h +// Created by: rdb (14May12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef COCOAGRAPHICSSTATEGUARDIAN_H +#define COCOAGRAPHICSSTATEGUARDIAN_H + +#include "pandabase.h" +#include "cocoaGraphicsPipe.h" +#include "glgsg.h" + +#import + +//////////////////////////////////////////////////////////////////// +// Class : CocoaGraphicsStateGuardian +// Description : A tiny specialization on GLGraphicsStateGuardian +// to add some Cocoa-specific information. +//////////////////////////////////////////////////////////////////// +class CocoaGraphicsStateGuardian : public GLGraphicsStateGuardian { +public: + INLINE const FrameBufferProperties &get_fb_properties() const; + void get_properties(FrameBufferProperties &properties, + NSOpenGLPixelFormat *pixel_format, int virtual_screen); + void choose_pixel_format(const FrameBufferProperties &properties, + CGDirectDisplayID display, + bool need_window, bool need_pbuffer); + + CocoaGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe, + CocoaGraphicsStateGuardian *share_with); + + virtual ~CocoaGraphicsStateGuardian(); + + NSOpenGLContext *_share_context; + NSOpenGLContext *_context; + FrameBufferProperties _fbprops; + +protected: + virtual void *do_get_extension_func(const char *prefix, const char *name); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GLGraphicsStateGuardian::init_type(); + register_type(_type_handle, "CocoaGraphicsStateGuardian", + GLGraphicsStateGuardian::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "cocoaGraphicsStateGuardian.I" + +#endif diff --git a/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm new file mode 100644 index 0000000000..e4385798a2 --- /dev/null +++ b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm @@ -0,0 +1,251 @@ +// Filename: cocoaGraphicsStateGuardian.mm +// Created by: rdb (14May12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "cocoaGraphicsStateGuardian.h" +#include "config_cocoadisplay.h" +#include "lightReMutexHolder.h" + +#include +#import + +TypeHandle CocoaGraphicsStateGuardian::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsStateGuardian::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CocoaGraphicsStateGuardian:: +CocoaGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe, + CocoaGraphicsStateGuardian *share_with) : + GLGraphicsStateGuardian(engine, pipe) +{ + _share_context = nil; + _context = nil; + + if (share_with != (CocoaGraphicsStateGuardian *)NULL) { + _prepared_objects = share_with->get_prepared_objects(); + _share_context = share_with->_context; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsStateGuardian::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CocoaGraphicsStateGuardian:: +~CocoaGraphicsStateGuardian() { + if (_context != nil) { + [_context clearDrawable]; + [_context release]; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsStateGuardian::get_properties +// Access: Private +// Description: Gets the FrameBufferProperties to match the +// indicated config. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsStateGuardian:: +get_properties(FrameBufferProperties &properties, NSOpenGLPixelFormat* pixel_format, int screen) { + + properties.clear(); + + // Now update our framebuffer_mode and bit depth appropriately. + GLint double_buffer, stereo, aux_buffers, color_size, alpha_size, + depth_size, stencil_size, accum_size, sample_buffers, samples, + renderer_id, accelerated, window, pbuffer; + + [pixel_format getValues: &double_buffer forAttribute: NSOpenGLPFADoubleBuffer forVirtualScreen: screen]; + [pixel_format getValues: &stereo forAttribute: NSOpenGLPFAStereo forVirtualScreen: screen]; + [pixel_format getValues: &aux_buffers forAttribute: NSOpenGLPFAAuxBuffers forVirtualScreen: screen]; + [pixel_format getValues: &color_size forAttribute: NSOpenGLPFAColorSize forVirtualScreen: screen]; + [pixel_format getValues: &alpha_size forAttribute: NSOpenGLPFAAlphaSize forVirtualScreen: screen]; + [pixel_format getValues: &depth_size forAttribute: NSOpenGLPFADepthSize forVirtualScreen: screen]; + [pixel_format getValues: &stencil_size forAttribute: NSOpenGLPFAStencilSize forVirtualScreen: screen]; + [pixel_format getValues: &accum_size forAttribute: NSOpenGLPFAAccumSize forVirtualScreen: screen]; + [pixel_format getValues: &sample_buffers forAttribute: NSOpenGLPFASampleBuffers forVirtualScreen: screen]; + [pixel_format getValues: &samples forAttribute: NSOpenGLPFASamples forVirtualScreen: screen]; + [pixel_format getValues: &renderer_id forAttribute: NSOpenGLPFARendererID forVirtualScreen: screen]; + [pixel_format getValues: &accelerated forAttribute: NSOpenGLPFAAccelerated forVirtualScreen: screen]; + [pixel_format getValues: &window forAttribute: NSOpenGLPFAWindow forVirtualScreen: screen]; + [pixel_format getValues: &pbuffer forAttribute: NSOpenGLPFAPixelBuffer forVirtualScreen: screen]; + + properties.set_back_buffers(double_buffer); + properties.set_stereo(stereo); + properties.set_rgb_color(1); + properties.set_color_bits(color_size); + properties.set_stencil_bits(stencil_size); + properties.set_depth_bits(depth_size); + properties.set_alpha_bits(alpha_size); + properties.set_accum_bits(accum_size); + if (sample_buffers > 0) { + properties.set_multisamples(samples); + } + //TODO: add aux buffers + + if (renderer_id == kCGLRendererGenericID || + renderer_id == kCGLRendererGenericFloatID || + renderer_id == kCGLRendererAppleSWID) { + properties.set_force_software(1); + } + if (accelerated) { + properties.set_force_hardware(1); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsStateGuardian::choose_pixel_format +// Access: Private +// Description: Selects a visual or fbconfig for all the windows +// and buffers that use this gsg. Also creates the GL +// context and obtains the visual. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsStateGuardian:: +choose_pixel_format(const FrameBufferProperties &properties, + CGDirectDisplayID display, + bool need_window, bool need_pbuffer) { + + _context = nil; + _fbprops.clear(); + + // Neither Cocoa nor CGL seem to have a mechanism to query the available + // pixel formats, unfortunately, so the only thing we can do is ask for one + // with the properties we have requested. + pvector attribs; + attribs.reserve(13); + + // Picked this up from the pyglet source - seems + // to be necessary to support RAGE-II, which is not compliant. + attribs.push_back(NSOpenGLPFAAllRenderers); + + // Don't let it fall back to a different renderer. + attribs.push_back(NSOpenGLPFANoRecovery); + + // Selection policy. + //attribs.push_back(NSOpenGLPFAMinimumPolicy); + + if (!properties.is_single_buffered()) { + attribs.push_back(NSOpenGLPFADoubleBuffer); + } + + if (properties.is_stereo()) { + attribs.push_back(NSOpenGLPFAStereo); + } + + int aux_buffers = properties.get_aux_rgba() + properties.get_aux_hrgba() + properties.get_aux_float(); + attribs.push_back(NSOpenGLPFAAuxBuffers); + attribs.push_back(aux_buffers); + attribs.push_back(NSOpenGLPFAColorSize); + attribs.push_back(properties.get_color_bits()); + attribs.push_back(NSOpenGLPFAAlphaSize); + attribs.push_back(properties.get_alpha_bits()); + attribs.push_back(NSOpenGLPFADepthSize); + attribs.push_back(properties.get_depth_bits()); + attribs.push_back(NSOpenGLPFAStencilSize); + attribs.push_back(properties.get_stencil_bits()); + + if (properties.get_multisamples() > 0) { + attribs.push_back(NSOpenGLPFASampleBuffers); + attribs.push_back(1); + attribs.push_back(NSOpenGLPFASamples); + attribs.push_back(properties.get_multisamples()); + attribs.push_back(NSOpenGLPFAMultisample); + } + + if (properties.get_force_software()) { + attribs.push_back(NSOpenGLPFARendererID); + attribs.push_back(kCGLRendererAppleSWID); + } + + if (properties.get_force_hardware()) { + attribs.push_back(NSOpenGLPFAAccelerated); + } + + if (need_window) { + //TODO: fullscreen != window on OSX + attribs.push_back(NSOpenGLPFAWindow); + //attribs.push_back(NSOpenGLPFAFullScreen); + } + + if (need_pbuffer) { + attribs.push_back(NSOpenGLPFAPixelBuffer); + } + + // Required when going fullscreen, optional when windowed + attribs.push_back(NSOpenGLPFAScreenMask); + attribs.push_back(CGDisplayIDToOpenGLDisplayMask(display)); + + attribs.push_back((NSOpenGLPixelFormatAttribute) nil); + + // Create the format. + NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc] initWithAttributes: &attribs[0]]; + if (format == nil) { + cocoadisplay_cat.error() << + "Could not find a usable pixel format.\n"; + return; + } + + //XXX not sure what to do with virtual_screen, let's just set it to 0. + get_properties(_fbprops, format, 0); + _context = [[NSOpenGLContext alloc] initWithFormat: format shareContext: _share_context]; + [format release]; + if (_context == nil) { + cocoadisplay_cat.error() << + "Failed to create OpenGL context!\n"; + return; + } + + // Set vsync setting on the context + GLint swap = sync_video ? 1 : 0; + [_context setValues:&swap forParameter:NSOpenGLCPSwapInterval]; + + cocoadisplay_cat.debug() + << "Created context " << _context << ": " << _fbprops << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsStateGuardian::do_get_extension_func +// Access: Public, Virtual +// Description: Returns the pointer to the GL extension function with +// the indicated name. It is the responsibility of the +// caller to ensure that the required extension is +// defined in the OpenGL runtime prior to calling this; +// it is an error to call this for a function that is +// not defined. +//////////////////////////////////////////////////////////////////// +void *CocoaGraphicsStateGuardian:: +do_get_extension_func(const char *prefix, const char *name) { + char* fullname = (char*) malloc(strlen(prefix) + strlen(name) + 2); + strcpy(fullname + 1, prefix); + strcpy(fullname + 1 + strlen(prefix), name); + fullname[0] = '_'; + + // Believe it or not, but this is actually the + // Apple-recommended way to do it. I know, right? + + if (NSIsSymbolNameDefined(fullname)) { + NSSymbol symbol = NSLookupAndBindSymbol(fullname); + free(fullname); + return (void *) NSAddressOfSymbol(symbol); + } + + cocoadisplay_cat.error() << + "do_get_extension_func failed for " << prefix << name << "!\n"; + + free(fullname); + return NULL; +} diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.I b/panda/src/cocoadisplay/cocoaGraphicsWindow.I new file mode 100644 index 0000000000..012c65013d --- /dev/null +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.I @@ -0,0 +1,34 @@ +// Filename: cocoaGraphicsWindow.I +// Created by: rdb (14May12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::get_nswindow +// Access: Public +// Description: Returns a pointer to the underlying NSWindow. +//////////////////////////////////////////////////////////////////// +INLINE NSWindow *CocoaGraphicsWindow:: +get_nswindow() const { + return _window; +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::get_nsview +// Access: Public +// Description: Returns a pointer to the underlying NSView. +//////////////////////////////////////////////////////////////////// +INLINE NSView *CocoaGraphicsWindow:: +get_nsview() const { + return _view; +} diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.h b/panda/src/cocoadisplay/cocoaGraphicsWindow.h new file mode 100644 index 0000000000..e25702cb34 --- /dev/null +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.h @@ -0,0 +1,105 @@ +// Filename: cocoaGraphicsWindow.h +// Created by: rdb (14May12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef COCOAGRAPHICSWINDOW_H +#define COCOAGRAPHICSWINDOW_H + +#include "pandabase.h" + +#include "cocoaGraphicsPipe.h" +#include "graphicsWindow.h" + +#import +#import +#import + +//////////////////////////////////////////////////////////////////// +// Class : CocoaGraphicsWindow +// Description : An interface to the Cocoa system for managing +// OpenGL windows under Mac OS X. +//////////////////////////////////////////////////////////////////// +class CocoaGraphicsWindow : public GraphicsWindow { +public: + CocoaGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, + const string &name, + const FrameBufferProperties &fb_prop, + const WindowProperties &win_prop, + int flags, + GraphicsStateGuardian *gsg, + GraphicsOutput *host); + virtual ~CocoaGraphicsWindow(); + + virtual bool move_pointer(int device, int x, int y); + virtual bool begin_frame(FrameMode mode, Thread *current_thread); + virtual void end_frame(FrameMode mode, Thread *current_thread); + virtual void end_flip(); + + virtual void process_events(); + virtual void set_properties_now(WindowProperties &properties); + + void handle_move_event(); + void handle_resize_event(); + void handle_minimize_event(bool minimized); + void handle_foreground_event(bool foreground); + bool handle_close_request(); + void handle_close_event(); + void handle_key_event(NSEvent *event); + void handle_mouse_button_event(int button, bool down); + void handle_mouse_moved_event(bool in_window, int x, int y, bool absolute); + void handle_wheel_event(double x, double y); + + INLINE NSWindow *get_nswindow() const; + INLINE NSView *get_nsview() const; + +protected: + virtual void close_window(); + virtual bool open_window(); + + virtual void mouse_mode_absolute(); + virtual void mouse_mode_relative(); + +private: + void set_wm_properties(const WindowProperties &properties, + bool already_mapped); + + ButtonHandle map_function_key(unsigned short keycode); + +private: + NSWindow *_window; + NSView *_view; + NSUInteger _modifier_keys; + CGDirectDisplayID _display; + bool _mouse_hidden; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GraphicsWindow::init_type(); + register_type(_type_handle, "CocoaGraphicsWindow", + GraphicsWindow::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "cocoaGraphicsWindow.I" + +#endif diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm new file mode 100644 index 0000000000..64fb02436e --- /dev/null +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm @@ -0,0 +1,1239 @@ +// Filename: cocoaGraphicsWindow.mm +// Created by: rdb (14May12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "cocoaGraphicsWindow.h" +#include "cocoaGraphicsStateGuardian.h" +#include "config_cocoadisplay.h" +#include "cocoaGraphicsPipe.h" + +#include "graphicsPipe.h" +#include "keyboardButton.h" +#include "mouseButton.h" +#include "clockObject.h" +#include "pStatTimer.h" +#include "textEncoder.h" +#include "throw_event.h" +#include "lightReMutexHolder.h" +#include "nativeWindowHandle.h" + +#import "cocoaPandaView.h" +#import "cocoaPandaWindow.h" + +#import +#import +#import +#import +#import + +TypeHandle CocoaGraphicsWindow::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CocoaGraphicsWindow:: +CocoaGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, + const string &name, + const FrameBufferProperties &fb_prop, + const WindowProperties &win_prop, + int flags, + GraphicsStateGuardian *gsg, + GraphicsOutput *host) : + GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host) +{ + _window = nil; + _view = nil; + _modifier_keys = 0; + _mouse_hidden = false; + + GraphicsWindowInputDevice device = + GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse"); + add_input_device(device); + + CocoaGraphicsPipe *cocoa_pipe; + DCAST_INTO_V(cocoa_pipe, _pipe); + _display = cocoa_pipe->_display; +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CocoaGraphicsWindow:: +~CocoaGraphicsWindow() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::move_pointer +// Access: Published, Virtual +// Description: Forces the pointer to the indicated position within +// the window, if possible. +// +// Returns true if successful, false on failure. This +// may fail if the mouse is not currently within the +// window, or if the API doesn't support this operation. +//////////////////////////////////////////////////////////////////// +bool CocoaGraphicsWindow:: +move_pointer(int device, int x, int y) { + if (device == 0) { + CGPoint point; + if (_properties.get_fullscreen()) { + point = CGPointMake(x, y); + } else { + point = CGPointMake(x + _properties.get_x_origin(), + y + _properties.get_y_origin()); + } + + return (CGDisplayMoveCursorToPoint(_display, point) == kCGErrorSuccess); + } else { + // No support for raw mice at the moment. + return false; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::begin_frame +// Access: Public, Virtual +// Description: This function will be called within the draw thread +// before beginning rendering for a given frame. It +// should do whatever setup is required, and return true +// if the frame should be rendered, or false if it +// should be skipped. +//////////////////////////////////////////////////////////////////// +bool CocoaGraphicsWindow:: +begin_frame(FrameMode mode, Thread *current_thread) { + PStatTimer timer(_make_current_pcollector, current_thread); + + begin_frame_spam(mode); + if (_gsg == (GraphicsStateGuardian *)NULL) { + return false; + } + + CocoaGraphicsStateGuardian *cocoagsg; + DCAST_INTO_R(cocoagsg, _gsg, false); + nassertr(cocoagsg->_context != nil, false); + + // Let's not bother drawing to ta miniaturized window, eh? + if (_properties.get_minimized()) { + return false; + } + + // Set the drawable. + if (_properties.get_fullscreen()) { + // Fullscreen. + [cocoagsg->_context setFullScreen]; + } else { + nassertr([_view lockFocusIfCanDraw], false); + [cocoagsg->_context setView: _view]; + } + + // Make the context current. + [cocoagsg->_context makeCurrentContext]; + + // Now that we have made the context current to a window, we can + // reset the GSG state if this is the first time it has been used. + // (We can't just call reset() when we construct the GSG, because + // reset() requires having a current context.) + cocoagsg->reset_if_new(); + + if (mode == FM_render) { + // begin_render_texture(); + clear_cube_map_selection(); + } + + _gsg->set_current_properties(&get_fb_properties()); + return _gsg->begin_frame(current_thread); +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::end_frame +// Access: Public, Virtual +// Description: This function will be called within the draw thread +// after rendering is completed for a given frame. It +// should do whatever finalization is required. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsWindow:: +end_frame(FrameMode mode, Thread *current_thread) { + end_frame_spam(mode); + nassertv(_gsg != (GraphicsStateGuardian *)NULL); + + if (mode == FM_render) { + // end_render_texture(); + copy_to_textures(); + } + + _gsg->end_frame(current_thread); + + if (mode == FM_render) { + trigger_flip(); + clear_cube_map_selection(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::end_flip +// Access: Public, Virtual +// Description: This function will be called within the draw thread +// after begin_flip() has been called on all windows, to +// finish the exchange of the front and back buffers. +// +// This should cause the window to wait for the flip, if +// necessary. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsWindow:: +end_flip() { + if (_gsg != (GraphicsStateGuardian *)NULL && _flip_ready) { + + // It doesn't appear to be necessary to ensure the graphics + // context is current before flipping the windows, and insisting + // on doing so can be a significant performance hit. + + //make_current(); + + CocoaGraphicsStateGuardian *cocoagsg; + DCAST_INTO_V(cocoagsg, _gsg); + + [cocoagsg->_context flushBuffer]; + if (!_properties.get_fullscreen()) { + [_view unlockFocus]; + } + } + GraphicsWindow::end_flip(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::process_events +// Access: Public, Virtual +// Description: Do whatever processing is necessary to ensure that +// the window responds to user events. Also, honor any +// requests recently made via request_properties() +// +// This function is called only within the window +// thread. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsWindow:: +process_events() { + GraphicsWindow::process_events(); + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSEvent *event = nil; + + while (true) { + event = [NSApp + nextEventMatchingMask: NSAnyEventMask + untilDate: nil + inMode: NSDefaultRunLoopMode + dequeue: YES]; + + if (event == nil) { + break; + } + + [NSApp sendEvent: event]; + } + + if (_window != nil) { + [_window update]; + } + + [pool release]; +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::open_window +// Access: Protected, Virtual +// Description: Opens the window right now. Called from the window +// thread. Returns true if the window is successfully +// opened, or false if there was a problem. +//////////////////////////////////////////////////////////////////// +bool CocoaGraphicsWindow:: +open_window() { + CocoaGraphicsPipe *cocoa_pipe; + DCAST_INTO_R(cocoa_pipe, _pipe, false); + + // GSG Creation/Initialization + CocoaGraphicsStateGuardian *cocoagsg; + if (_gsg == 0) { + // There is no old gsg. Create a new one. + cocoagsg = new CocoaGraphicsStateGuardian(_engine, _pipe, NULL); + cocoagsg->choose_pixel_format(_fb_properties, cocoa_pipe->_display, true, false); + _gsg = cocoagsg; + } else { + // If the old gsg has the wrong pixel format, create a + // new one that shares with the old gsg. + DCAST_INTO_R(cocoagsg, _gsg, false); + if (!cocoagsg->get_fb_properties().subsumes(_fb_properties)) { + cocoagsg = new CocoaGraphicsStateGuardian(_engine, _pipe, cocoagsg); + cocoagsg->choose_pixel_format(_fb_properties, cocoa_pipe->_display, true, false); + _gsg = cocoagsg; + } + } + + // Fill in the blanks. + if (!_properties.has_origin()) { + _properties.set_origin(-2, -2); + } + if (!_properties.has_size()) { + _properties.set_size(100, 100); + } + if (!_properties.has_fullscreen()) { + _properties.set_fullscreen(false); + } + if (!_properties.has_foreground()) { + _properties.set_foreground(true); + } + if (!_properties.has_undecorated()) { + _properties.set_undecorated(false); + } + if (!_properties.has_fixed_size()) { + _properties.set_fixed_size(false); + } + if (!_properties.has_minimized()) { + _properties.set_minimized(false); + } + if (!_properties.has_z_order()) { + _properties.set_z_order(WindowProperties::Z_normal); + } + if (!_properties.has_cursor_hidden()) { + _properties.set_cursor_hidden(false); + } + + // Configure the window decorations + NSUInteger windowStyle; + if (_properties.get_undecorated()) { + windowStyle = NSBorderlessWindowMask; + } else if (_properties.get_fixed_size()) { + // Fixed size windows should not show the resize button. + windowStyle = NSTitledWindowMask | NSClosableWindowMask | + NSMiniaturizableWindowMask; + } else { + windowStyle = NSTitledWindowMask | NSClosableWindowMask | + NSMiniaturizableWindowMask | NSResizableWindowMask; + } + + // Center the window if coordinates were set to -1 or -2 + NSRect screenFrame = [cocoa_pipe->_screen frame]; + int x = _properties.get_x_origin(); + int y = _properties.get_y_origin(); + if (x < 0) { + x = floor(screenFrame.size.width / 2 - _properties.get_x_size() / 2); + } + if (y < 0) { + y = floor(screenFrame.size.height / 2 - _properties.get_y_size() / 2); + } + _properties.set_origin(x, y); + + // Content rectangle + NSRect rect = NSMakeRect(_properties.get_x_origin(), + screenFrame.size.height - _properties.get_y_origin(), + _properties.get_x_size(), _properties.get_y_size()); + + // Create the window. + if (cocoadisplay_cat.is_debug()) { + NSString *str = NSStringFromRect(rect); + cocoadisplay_cat.debug() + << "Creating NSWindow with content rect " << [str UTF8String] << "\n"; + } + _window = [[CocoaPandaWindow alloc] + initWithContentRect: rect + styleMask:windowStyle + screen:cocoa_pipe->_screen + window:this]; + + if (_window == nil) { + cocoadisplay_cat.error() + << "Failed to create Cocoa window.\n"; + return false; + } + + // Create the NSView to render to. + _view = [[CocoaPandaView alloc] initWithFrame:rect context:cocoagsg->_context window:this]; + [_window setContentView:_view]; + [_window setReleasedWhenClosed:YES]; + [_window makeFirstResponder:_view]; + + // Create a WindowHandle for ourselves + // I'd rather store a window number than an NSWindow pointer, + // but wxWidgets seems to use the NSWindow/NSView pointer approach, + // so let's do the same here. + _window_handle = NativeWindowHandle::make_int((size_t) _window); + + // Set the properties + if (_properties.has_title()) { + [_window setTitle: [NSString stringWithUTF8String: _properties.get_title().c_str()]]; + } + + [_window setShowsResizeIndicator: !_properties.get_fixed_size()]; + + if (_properties.get_minimized()) { + [_window makeKeyAndOrderFront: nil]; + [_window miniaturize: nil]; + } else if (_properties.get_foreground()) { + [_window makeKeyAndOrderFront: nil]; + } else { + [_window orderBack: nil]; + } + + switch (_properties.get_z_order()) { + case WindowProperties::Z_bottom: + [_window setLevel: NSNormalWindowLevel - 1]; + break; + + case WindowProperties::Z_normal: + [_window setLevel: NSNormalWindowLevel]; + break; + + case WindowProperties::Z_top: + [_window setLevel: NSPopUpMenuWindowLevel]; + break; + } + + // Make the context current. + [cocoagsg->_context update]; + [cocoagsg->_context makeCurrentContext]; + + cocoagsg->reset_if_new(); + if (!cocoagsg->is_valid()) { + close_window(); + return false; + } + + if (!cocoagsg->get_fb_properties().verify_hardware_software + (_fb_properties, cocoagsg->get_gl_renderer())) { + close_window(); + return false; + } + _fb_properties = cocoagsg->get_fb_properties(); + + // And tell our parent window that we're now its child. + //if (_parent_window_handle != (WindowHandle *)NULL) { + // _parent_window_handle->attach_child(_window_handle); + // + + //TODO: update initial mouse position in the case that + // the NSWindow delegate doesn't send the make key event, ie + // if setParentWindow was used. + + //TODO: cursor image, app icon + + // Enable relative mouse mode, if this was requested. + if (_properties.has_mouse_mode() && + _properties.get_mouse_mode() == WindowProperties::M_relative) { + mouse_mode_relative(); + } + + if (_properties.get_fullscreen() && !_properties.get_minimized()) { + CGDisplayCapture(_display); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::close_window +// Access: Protected, Virtual +// Description: Closes the window right now. Called from the window +// thread. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsWindow:: +close_window() { + if (_mouse_hidden) { + [NSCursor unhide]; + _mouse_hidden = false; + } + + if (_gsg != (GraphicsStateGuardian *)NULL) { + CocoaGraphicsStateGuardian *cocoagsg; + cocoagsg = DCAST(CocoaGraphicsStateGuardian, _gsg); + + if (cocoagsg != NULL && cocoagsg->_context != nil) { + [cocoagsg->_context clearDrawable]; + } + _gsg.clear(); + } + + if (_window != nil) { + [_window setReleasedWhenClosed: YES]; + [_window close]; + _window = nil; + } + + if (_view != nil) { + [_view release]; + _view = nil; + } + + GraphicsWindow::close_window(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::mouse_mode_relative +// Access: Protected, Virtual +// Description: Overridden from GraphicsWindow. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsWindow:: +mouse_mode_absolute() { + CGAssociateMouseAndMouseCursorPosition(YES); +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::mouse_mode_relative +// Access: Protected, Virtual +// Description: Overridden from GraphicsWindow. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsWindow:: +mouse_mode_relative() { + CGAssociateMouseAndMouseCursorPosition(NO); +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::set_properties_now +// Access: Public, Virtual +// Description: Applies the requested set of properties to the +// window, if possible, for instance to request a change +// in size or minimization status. +// +// The window properties are applied immediately, rather +// than waiting until the next frame. This implies that +// this method may *only* be called from within the +// window thread. +// +// The return value is true if the properties are set, +// false if they are ignored. This is mainly useful for +// derived classes to implement extensions to this +// function. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsWindow:: +set_properties_now(WindowProperties &properties) { + if (_pipe == (GraphicsPipe *)NULL) { + // If the pipe is null, we're probably closing down. + GraphicsWindow::set_properties_now(properties); + return; + } + + GraphicsWindow::set_properties_now(properties); + if (!properties.is_any_specified()) { + // The base class has already handled this case. + return; + } + + // The window is already open; we are limited to what we can change + // on the fly. + + if (properties.has_minimized()) { + _properties.set_minimized(properties.get_minimized()); + if (properties.get_minimized()) { + [_window miniaturize: nil]; + } else { + [_window deminiaturize: nil]; + } + if (_properties.get_fullscreen()) { + if (properties.get_minimized()) { + CGDisplayRelease(_display); + } else { + CGDisplayCapture(_display); + } + } + properties.clear_minimized(); + } + + if (properties.has_fullscreen()) { + if (_properties.get_fullscreen() != properties.get_fullscreen()) { + if (properties.get_fullscreen()) { + // Capture the display + if (!_properties.get_minimized()) { + CGDisplayCapture(_display); + } + + } else { + // Release the display + CGDisplayRelease(_display); + } + _properties.set_fullscreen(properties.get_fullscreen()); + } + properties.clear_fullscreen(); + } + + if (properties.has_origin()) { + int x = properties.get_x_origin(); + int y = properties.get_y_origin(); + + // Get the frame for the screen + NSRect frame = [_window frame]; + NSScreen *screen = [_window screen]; + nassertv(screen != nil); + NSRect screenFrame = [screen frame]; + + if (x < 0) { + x = floor(screenFrame.size.width / 2 - _properties.get_x_size() / 2); + } + if (y < 0) { + y = floor(screenFrame.size.height / 2 - _properties.get_y_size() / 2); + } + _properties.set_origin(x, y); + + if (!_properties.get_fullscreen()) { + // Convert to content rect, change pos, convert back. + // Remember, Mac OS X coordinates are flipped in the vertical axis. + NSRect content = [_window contentRectForFrameRect: frame]; + content.origin.x = screenFrame.origin.x + x; + content.origin.y = screenFrame.origin.y + screenFrame.size.height - y - content.size.height; + frame = [_window frameRectForContentRect: content]; + + cocoadisplay_cat.debug() + << "Setting frame origin to " << screenFrame.origin.x << ", " << screenFrame.origin.y << "\n"; + + [_window setFrame: frame display: NO]; + } + properties.clear_origin(); + } + + if (properties.has_size()) { + _properties.set_size(properties.get_x_size(), properties.get_y_size()); + if (!_properties.get_fullscreen()) { + [_window setContentSize: NSMakeSize(properties.get_x_size(), properties.get_y_size())]; + } + properties.clear_size(); + } + + //TODO: mouse mode + + if (properties.has_title()) { + _properties.set_title(properties.get_title()); + [_window setTitle: [NSString stringWithUTF8String: properties.get_title().c_str()]]; + properties.clear_title(); + } + + if (properties.has_fixed_size()) { + _properties.set_fixed_size(properties.get_fixed_size()); + [_window setShowsResizeIndicator: !properties.get_fixed_size()]; + + if (!_properties.get_fullscreen()) { + // If our window is decorated, change the style mask + // to show or hide the resize button appropriately. + // However, if we're specifying the 'undecorated' property also, + // then we'll be setting the style mask about 20 LOC further down, + // so we won't need to bother setting it here. + if (!properties.has_undecorated() && !_properties.get_undecorated()) { + if (properties.get_fixed_size()) { + [_window setStyleMask: NSTitledWindowMask | NSClosableWindowMask | + NSMiniaturizableWindowMask ]; + } else { + [_window setStyleMask: NSTitledWindowMask | NSClosableWindowMask | + NSMiniaturizableWindowMask | NSResizableWindowMask ]; + } + } + } + + properties.clear_fixed_size(); + } + + if (properties.has_undecorated()) { + _properties.set_undecorated(properties.get_undecorated()); + + if (!_properties.get_fullscreen()) { + if (properties.get_undecorated()) { + [_window setStyleMask: NSBorderlessWindowMask]; + } else if (_properties.get_fixed_size()) { + // Fixed size windows should not show the resize button. + [_window setStyleMask: NSTitledWindowMask | NSClosableWindowMask | + NSMiniaturizableWindowMask ]; + } else { + [_window setStyleMask: NSTitledWindowMask | NSClosableWindowMask | + NSMiniaturizableWindowMask | NSResizableWindowMask ]; + } + } + + properties.clear_undecorated(); + } + + if (properties.has_foreground()) { + _properties.set_foreground(properties.get_foreground()); + if (!_properties.get_minimized()) { + if (properties.get_foreground()) { + [_window makeKeyAndOrderFront: nil]; + } else { + [_window orderBack: nil]; + } + } + properties.clear_foreground(); + } + + //TODO: support raw mice. + + if (properties.has_cursor_hidden()) { + if (properties.get_cursor_hidden() != _properties.get_cursor_hidden()) { + if (properties.get_cursor_hidden() && _input_devices[0].get_pointer().get_in_window()) { + [NSCursor hide]; + _mouse_hidden = true; + } else if (_mouse_hidden) { + [NSCursor unhide]; + _mouse_hidden = false; + } + _properties.set_cursor_hidden(properties.get_cursor_hidden()); + } + properties.clear_cursor_hidden(); + } + + if (properties.has_icon_filename()) { + //_properties.set_icon_filename(properties.get_icon_filename()); + //properties.clear_icon_filename(); + //TODO: setMiniwindowImage + //You can also call this method as needed to change the minimized window image. Typically, you would specify a custom image immediately prior to a window being minimized—when the system posts an NSWindowWillMiniaturizeNotification. You can call this method while the window is minimized to update the current image in the Dock. However, this method is not recommended for creating complex animations in the Dock. + //Support for custom images is disabled by default. To enable support, set the AppleDockIconEnabled key to YES when first registering your application’s user defaults. You must set this key prior to calling the init method of NSApplication, which reads the current value of the key. + } + + //XXX cursor filename + + if (properties.has_z_order()) { + _properties.set_z_order(properties.get_z_order()); + + if (!_properties.get_fullscreen()) { + switch (properties.get_z_order()) { + case WindowProperties::Z_bottom: + [_window setLevel: NSNormalWindowLevel - 1]; + break; + + case WindowProperties::Z_normal: + [_window setLevel: NSNormalWindowLevel]; + break; + + case WindowProperties::Z_top: + [_window setLevel: NSPopUpMenuWindowLevel]; + break; + } + } + properties.clear_z_order(); + } + + //TODO: parent window + if (properties.has_parent_window()) { + _properties.set_parent_window(properties.get_parent_window()); + properties.clear_parent_window(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::handle_move_event +// Access: Public +// Description: Called by CocoaPandaView or the window delegate +// when the frame rect changes. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsWindow:: +handle_move_event() { + NSRect frame = [_window frame]; + NSRect screenFrame = [[_window screen] frame]; + NSRect content = [_window contentRectForFrameRect: frame]; + + // Remember, Mac OS X uses flipped coordinates + WindowProperties properties; + properties.set_origin(content.origin.x - screenFrame.origin.x, + screenFrame.size.height - content.size.height - (content.origin.y - screenFrame.origin.y)); + + if (properties.get_x_origin() != _properties.get_x_origin() || + properties.get_y_origin() != _properties.get_y_origin()) { + + if (cocoadisplay_cat.is_spam()) { + cocoadisplay_cat.spam() + << "Window changed origin to (" << properties.get_x_origin() + << ", " << properties.get_y_origin() << ")\n"; + } + system_changed_properties(properties); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::handle_resize_event +// Access: Public +// Description: Called by CocoaPandaView or the window delegate +// when the frame rect changes. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsWindow:: +handle_resize_event() { + NSRect frame = [_window frame]; + NSRect screenFrame = [[_window screen] frame]; + NSRect content = [_window contentRectForFrameRect: frame]; + + WindowProperties properties; + properties.set_size(content.size.width, content.size.height); + + if (properties.get_x_size() != _properties.get_x_size() || + properties.get_y_size() != _properties.get_y_size()) { + + if (cocoadisplay_cat.is_spam()) { + cocoadisplay_cat.spam() + << "Window changed size to (" << properties.get_x_size() + << ", " << properties.get_y_size() << ")\n"; + } + system_changed_properties(properties); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::handle_minimize_event +// Access: Public +// Description: Called by the window delegate when the window is +// miniaturized or deminiaturized. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsWindow:: +handle_minimize_event(bool minimized) { + if (minimized == _properties.get_minimized()) { + return; + } + + WindowProperties properties; + properties.set_minimized(minimized); + system_changed_properties(properties); + + if (_properties.get_fullscreen()) { + if (minimized) { + CGDisplayRelease(_display); + } else { + CGDisplayCapture(_display); + } + } + + if (cocoadisplay_cat.is_debug()) { + if (minimized) { + cocoadisplay_cat.debug() << "Window was miniaturized\n"; + } else { + cocoadisplay_cat.debug() << "Window was deminiaturized\n"; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::handle_foreground_event +// Access: Public +// Description: Called by the window delegate when the window has +// become the key window or resigned that status. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsWindow:: +handle_foreground_event(bool foreground) { + WindowProperties properties; + properties.set_foreground(foreground); + system_changed_properties(properties); + + if (cocoadisplay_cat.is_debug()) { + if (foreground) { + cocoadisplay_cat.debug() << "Window became key\n"; + } else { + cocoadisplay_cat.debug() << "Window resigned key\n"; + } + } + + if (foreground && _properties.get_mouse_mode() != WindowProperties::M_relative) { + // The mouse position may have changed during + // the time that we were not the key window. + NSPoint pos = [NSEvent mouseLocation]; + NSRect frame = [_window frame]; + pos.x -= frame.origin.x; + pos.y -= frame.origin.y; + + NSPoint loc = [_view convertPoint: pos fromView: nil]; + BOOL inside = [_view mouse: loc inRect: [_view bounds]]; + + handle_mouse_moved_event(inside, loc.x, [_view bounds].size.height - loc.y, true); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::handle_close_request +// Access: Public +// Description: Called by the window delegate when the user +// requests to close the window. This may not always +// be called, which is why there is also a +// handle_close_event. +// Returns false if the user indicated that he wants +// to handle the close request himself, true if the +// operating system should continue closing the window. +//////////////////////////////////////////////////////////////////// +bool CocoaGraphicsWindow:: +handle_close_request() { + string close_request_event = get_close_request_event(); + if (!close_request_event.empty()) { + // In this case, the app has indicated a desire to intercept + // the request and process it directly. + throw_event(close_request_event); + + cocoadisplay_cat.debug() + << "Window requested close. Rejecting, throwing event " + << close_request_event << " instead\n"; + + // Prevent the operating system from closing the window. + return false; + } + + cocoadisplay_cat.debug() + << "Window requested close, accepting\n"; + + // Let the operating system close the window normally. + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::handle_close_event +// Access: Public +// Description: Called by the window delegate when the window closes. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsWindow:: +handle_close_event() { + cocoadisplay_cat.debug() << "Window is about to close\n"; + + // Make sure that the window gets released + [_window setReleasedWhenClosed: YES]; + _window = nil; + + // Get rid of the GSG + if (_gsg != (GraphicsStateGuardian *)NULL) { + CocoaGraphicsStateGuardian *cocoagsg; + cocoagsg = DCAST(CocoaGraphicsStateGuardian, _gsg); + + if (cocoagsg != NULL && cocoagsg->_context != nil) { + [cocoagsg->_context clearDrawable]; + } + _gsg.clear(); + } + + // Dump the view, too + if (_view != nil) { + [_view release]; + _view = nil; + } + + // Unhide the mouse cursor + if (_mouse_hidden) { + [NSCursor unhide]; + _mouse_hidden = false; + } + + WindowProperties properties; + properties.set_open(false); + properties.set_cursor_hidden(false); + system_changed_properties(properties); + + GraphicsWindow::close_window(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::handle_key_event +// Access: Public +// Description: This method processes the NSEvent of type NSKeyUp, +// NSKeyDown or NSFlagsChanged and passes the +// information on to Panda. +// Should only be called by CocoaPandaView. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsWindow:: +handle_key_event(NSEvent *event) { + NSUInteger modifierFlags = [event modifierFlags]; + + if ((modifierFlags ^ _modifier_keys) & NSAlphaShiftKeyMask) { + if (modifierFlags & NSAlphaShiftKeyMask) { + _input_devices[0].button_down(KeyboardButton::caps_lock()); + } else { + _input_devices[0].button_up(KeyboardButton::caps_lock()); + } + } + + if ((modifierFlags ^ _modifier_keys) & NSShiftKeyMask) { + if (modifierFlags & NSShiftKeyMask) { + _input_devices[0].button_down(KeyboardButton::shift()); + } else { + _input_devices[0].button_up(KeyboardButton::shift()); + } + } + + if ((modifierFlags ^ _modifier_keys) & NSControlKeyMask) { + if (modifierFlags & NSControlKeyMask) { + _input_devices[0].button_down(KeyboardButton::control()); + } else { + _input_devices[0].button_up(KeyboardButton::control()); + } + } + + if ((modifierFlags ^ _modifier_keys) & NSAlternateKeyMask) { + if (modifierFlags & NSAlternateKeyMask) { + _input_devices[0].button_down(KeyboardButton::alt()); + } else { + _input_devices[0].button_up(KeyboardButton::alt()); + } + } + + if ((modifierFlags ^ _modifier_keys) & NSCommandKeyMask) { + if (modifierFlags & NSCommandKeyMask) { + _input_devices[0].button_down(KeyboardButton::meta()); + } else { + _input_devices[0].button_up(KeyboardButton::meta()); + } + } + + // I'd add the help key too, but something else in Cocoa messes + // around with it. The up event is registered fine below, but + // the down event isn't, and the modifier flag gets stuck after 1 press. + // More testing is needed, but I don't think it's worth it until + // we encounter someone who requires support for the help key. + + _modifier_keys = modifierFlags; + + // FlagsChanged events only carry modifier key information. + if ([event type] == NSFlagsChanged) { + return; + } + + NSString *str = [event charactersIgnoringModifiers]; + if (str == nil || [str length] == 0) { + return; + } + nassertv([str length] == 1); + unichar c = [str characterAtIndex: 0]; + + ButtonHandle button; + + if (c >= 0xF700 && c < 0xF900) { + // Special function keys. + button = map_function_key(c); + + } else if (c == 0x3) { + button = KeyboardButton::enter(); + + } else { + // If a down event, process as keystroke too. + if ([event type] == NSKeyDown) { + NSString *origstr = [event characters]; + c = [str characterAtIndex: 0]; + _input_devices[0].keystroke(c); + } + + // That done, continue trying to find out the button handle. + if ([str canBeConvertedToEncoding: NSASCIIStringEncoding]) { + // Nhm, ascii character perhaps? + button = KeyboardButton::ascii_key([str cStringUsingEncoding: NSASCIIStringEncoding]); + + } else { + button = ButtonHandle::none(); + } + } + + if (button == ButtonHandle::none()) { + cocoadisplay_cat.warning() + << "Unhandled keypress, character " << (int) c << ", keyCode " << [event keyCode] << "\n"; + return; + } + + // Let's get it off our chest. + if ([event type] == NSKeyUp) { + _input_devices[0].button_up(button); + } else { + _input_devices[0].button_down(button); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::handle_mouse_button_event +// Access: Public +// Description: This method processes the NSEvents related to +// mouse button presses. +// Should only be called by CocoaPandaView. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsWindow:: +handle_mouse_button_event(int button, bool down) { + if (down) { + _input_devices[0].button_down(MouseButton::button(button)); + +#ifndef NDEBUG + cocoadisplay_cat.spam() + << "Mouse button " << button << " down\n"; +#endif + } else { + _input_devices[0].button_up(MouseButton::button(button)); + +#ifndef NDEBUG + cocoadisplay_cat.spam() + << "Mouse button " << button << " up\n"; +#endif + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::handle_mouse_moved_event +// Access: Public +// Description: This method processes the NSEvents of the +// mouseMoved and mouseDragged types. +// Should only be called by CocoaPandaView. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsWindow:: +handle_mouse_moved_event(bool in_window, int x, int y, bool absolute) { + if (cocoadisplay_cat.is_spam()) { + if (in_window != _input_devices[0].get_pointer().get_in_window()) { + if (in_window) { + cocoadisplay_cat.spam() << "Mouse pointer entered window\n"; + } else { + cocoadisplay_cat.spam() << "Mouse pointer exited window\n"; + } + } + } + + if (absolute) { + _input_devices[0].set_pointer(in_window, x, y, + ClockObject::get_global_clock()->get_frame_time()); + } else { + //TODO: also get initial mouse position + MouseData md = _input_devices[0].get_pointer(); + _input_devices[0].set_pointer_in_window(md.get_x() + x, md.get_y() + y); + } + + if (in_window != _mouse_hidden && _properties.get_cursor_hidden()) { + // Hide the cursor if the mouse enters the window, + // and unhide it when the mouse leaves the window. + if (in_window) { + [NSCursor hide]; + } else { + [NSCursor unhide]; + } + _mouse_hidden = in_window; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::handle_wheel_event +// Access: Public +// Description: Called by CocoaPandaView to inform that the scroll +// wheel has been used. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsWindow:: +handle_wheel_event(double x, double y) { + //TODO +} + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::map_function_key +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +ButtonHandle CocoaGraphicsWindow:: +map_function_key(unsigned short keycode) { + switch (keycode) { + case NSUpArrowFunctionKey: + return KeyboardButton::up(); + case NSDownArrowFunctionKey: + return KeyboardButton::down(); + case NSLeftArrowFunctionKey: + return KeyboardButton::left(); + case NSRightArrowFunctionKey: + return KeyboardButton::right(); + case NSF1FunctionKey: + return KeyboardButton::f1(); + case NSF2FunctionKey: + return KeyboardButton::f2(); + case NSF3FunctionKey: + return KeyboardButton::f3(); + case NSF4FunctionKey: + return KeyboardButton::f4(); + case NSF5FunctionKey: + return KeyboardButton::f5(); + case NSF6FunctionKey: + return KeyboardButton::f6(); + case NSF7FunctionKey: + return KeyboardButton::f7(); + case NSF8FunctionKey: + return KeyboardButton::f8(); + case NSF9FunctionKey: + return KeyboardButton::f9(); + case NSF10FunctionKey: + return KeyboardButton::f10(); + case NSF11FunctionKey: + return KeyboardButton::f11(); + case NSF12FunctionKey: + return KeyboardButton::f12(); + case NSF13FunctionKey: + return KeyboardButton::f13(); + case NSF14FunctionKey: + return KeyboardButton::f14(); + case NSF15FunctionKey: + return KeyboardButton::f15(); + case NSF16FunctionKey: + return KeyboardButton::f16(); + case NSF17FunctionKey: + case NSF18FunctionKey: + case NSF19FunctionKey: + case NSF20FunctionKey: + case NSF21FunctionKey: + case NSF22FunctionKey: + case NSF23FunctionKey: + case NSF24FunctionKey: + case NSF25FunctionKey: + case NSF26FunctionKey: + case NSF27FunctionKey: + case NSF28FunctionKey: + case NSF29FunctionKey: + case NSF30FunctionKey: + case NSF31FunctionKey: + case NSF32FunctionKey: + case NSF33FunctionKey: + case NSF34FunctionKey: + case NSF35FunctionKey: + break; + case NSInsertFunctionKey: + return KeyboardButton::insert(); + case NSDeleteFunctionKey: + return KeyboardButton::del(); + case NSHomeFunctionKey: + return KeyboardButton::home(); + case NSBeginFunctionKey: + break; + case NSEndFunctionKey: + return KeyboardButton::end(); + case NSPageUpFunctionKey: + return KeyboardButton::page_up(); + case NSPageDownFunctionKey: + return KeyboardButton::page_down(); + case NSPrintScreenFunctionKey: + return KeyboardButton::print_screen(); + case NSScrollLockFunctionKey: + return KeyboardButton::scroll_lock(); + case NSPauseFunctionKey: + return KeyboardButton::pause(); + case NSSysReqFunctionKey: + case NSBreakFunctionKey: + case NSResetFunctionKey: + case NSStopFunctionKey: + case NSMenuFunctionKey: + case NSUserFunctionKey: + case NSSystemFunctionKey: + case NSPrintFunctionKey: + case NSClearLineFunctionKey: + return KeyboardButton::num_lock(); + case NSClearDisplayFunctionKey: + case NSInsertLineFunctionKey: + case NSDeleteLineFunctionKey: + case NSInsertCharFunctionKey: + case NSDeleteCharFunctionKey: + case NSPrevFunctionKey: + case NSNextFunctionKey: + case NSSelectFunctionKey: + case NSExecuteFunctionKey: + case NSUndoFunctionKey: + case NSRedoFunctionKey: + case NSFindFunctionKey: + case NSHelpFunctionKey: + return KeyboardButton::help(); + case NSModeSwitchFunctionKey: + break; + } + return ButtonHandle::none(); +} diff --git a/panda/src/cocoadisplay/cocoaPandaView.h b/panda/src/cocoadisplay/cocoaPandaView.h new file mode 100644 index 0000000000..20f10253b6 --- /dev/null +++ b/panda/src/cocoadisplay/cocoaPandaView.h @@ -0,0 +1,53 @@ +// Filename: cocoaPandaView.h +// Created by: rdb (17May12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#import + +class CocoaGraphicsWindow; + +@interface CocoaPandaView : NSView { + @private + NSOpenGLContext *_context; + CocoaGraphicsWindow *_graphicsWindow; +} + +- (id) initWithFrame:(NSRect)frameRect context:(NSOpenGLContext*)context window:(CocoaGraphicsWindow*)window; +- (BOOL) acceptsFirstResponder; +- (BOOL) becomeFirstResponder; +- (BOOL) resignFirstResponder; + +- (void) setFrame: (NSRect) frame; + +// Keyboard events +- (void) keyDown: (NSEvent *) event; +- (void) keyUp: (NSEvent *) event; +- (void) flagsChanged: (NSEvent *) event; + +// Mouse events +- (void) mouseDown: (NSEvent *) event; +- (void) mouseDragged: (NSEvent *) event; +- (void) mouseUp: (NSEvent *) event; +- (void) mouseMoved: (NSEvent *) event; +- (void) rightMouseDown: (NSEvent *) event; +- (void) rightMouseDragged: (NSEvent *) event; +- (void) rightMouseUp: (NSEvent *) event; +- (void) otherMouseDown: (NSEvent *) event; +- (void) otherMouseDragged: (NSEvent *) event; +- (void) otherMouseUp: (NSEvent *) event; + +- (void) scrollWheel: (NSEvent *) event; + +- (BOOL) isOpaque; + +@end diff --git a/panda/src/cocoadisplay/cocoaPandaView.mm b/panda/src/cocoadisplay/cocoaPandaView.mm new file mode 100644 index 0000000000..9c803a1574 --- /dev/null +++ b/panda/src/cocoadisplay/cocoaPandaView.mm @@ -0,0 +1,129 @@ +// Filename: cocoaPandaView.mm +// Created by: rdb (17May12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#import "cocoaPandaView.h" +#import "cocoaGraphicsWindow.h" + +#include + +@implementation CocoaPandaView +- (id) initWithFrame:(NSRect)frameRect context:(NSOpenGLContext*)context window:(CocoaGraphicsWindow*)window { + self = [super initWithFrame: frameRect]; + + cocoadisplay_cat.debug() + << "Created CocoaPandaView " << self << " for GraphicsWindow " << window << "\n"; + _graphicsWindow = window; + + // Make sure that the view automatically resizes with the window + //[self setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable]; + + return self; +} + +- (BOOL) acceptsFirstResponder { + return YES; +} + +- (BOOL) becomeFirstResponder { + return YES; +} + +- (BOOL) resignFirstResponder { + return YES; +} + +- (void) setFrame: (NSRect) frame { + [super setFrame: frame]; + [_context update]; + + _graphicsWindow->handle_resize_event(); +} + +- (void) keyDown: (NSEvent *) event { + _graphicsWindow->handle_key_event(event); +} + +- (void) keyUp: (NSEvent *) event { + _graphicsWindow->handle_key_event(event); +} + +- (void) flagsChanged: (NSEvent *) event { + _graphicsWindow->handle_key_event(event); +} + +- (void) mouseDown: (NSEvent *) event { + _graphicsWindow->handle_mouse_button_event(0, true); +} + +- (void) mouseDragged: (NSEvent *) event { + [self mouseMoved: event]; +} + +- (void) mouseUp: (NSEvent *) event { + _graphicsWindow->handle_mouse_button_event(0, false); +} + +- (void) mouseMoved: (NSEvent *) event { + NSPoint loc = [self convertPoint: [event locationInWindow] fromView: nil]; + BOOL inside = [self mouse: loc inRect: [self bounds]]; + + if (_graphicsWindow->get_properties().get_mouse_mode() == WindowProperties::M_relative) { + _graphicsWindow->handle_mouse_moved_event(inside, [event deltaX], [event deltaY], false); + } else { + _graphicsWindow->handle_mouse_moved_event(inside, loc.x, [self bounds].size.height - loc.y, true); + } +} + +- (void) rightMouseDown: (NSEvent *) event { + _graphicsWindow->handle_mouse_button_event(2, true); +} + +- (void) rightMouseDragged: (NSEvent *) event { + [self mouseMoved: event]; +} + +- (void) rightMouseUp: (NSEvent *) event { + _graphicsWindow->handle_mouse_button_event(2, false); +} + +- (void) otherMouseDown: (NSEvent *) event { + // 2 and 3 are swapped, for consistency with X11 implementation + if ([event buttonNumber] == 2) { + _graphicsWindow->handle_mouse_button_event(1, true); + } else { + _graphicsWindow->handle_mouse_button_event([event buttonNumber], true); + } +} + +- (void) otherMouseDragged: (NSEvent *) event { + [self mouseMoved: event]; +} + +- (void) otherMouseUp: (NSEvent *) event { + // 2 and 3 are swapped, for consistency with X11 implementation + if ([event buttonNumber] == 2) { + _graphicsWindow->handle_mouse_button_event(1, false); + } else { + _graphicsWindow->handle_mouse_button_event([event buttonNumber], false); + } +} + +- (void) scrollWheel: (NSEvent *) event { + _graphicsWindow->handle_wheel_event([event deltaX], [event deltaY]); +} + +- (BOOL) isOpaque { + return YES; +} +@end diff --git a/panda/src/cocoadisplay/cocoaPandaWindow.h b/panda/src/cocoadisplay/cocoaPandaWindow.h new file mode 100644 index 0000000000..c7bd6bcbe5 --- /dev/null +++ b/panda/src/cocoadisplay/cocoaPandaWindow.h @@ -0,0 +1,27 @@ +// Filename: cocoaPandaWindow.h +// Created by: rdb (25May12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#import + +class CocoaGraphicsWindow; + +@interface CocoaPandaWindow : NSWindow { + @private + CocoaGraphicsWindow *_graphicsWindow; +} + +- (id) initWithContentRect:(NSRect)rect styleMask:(NSUInteger)styleMask screen:(NSScreen*)screen window:(CocoaGraphicsWindow*)window; +- (BOOL)canBecomeKeyWindow; + +@end diff --git a/panda/src/cocoadisplay/cocoaPandaWindow.mm b/panda/src/cocoadisplay/cocoaPandaWindow.mm new file mode 100644 index 0000000000..68fc64cb64 --- /dev/null +++ b/panda/src/cocoadisplay/cocoaPandaWindow.mm @@ -0,0 +1,44 @@ +// Filename: cocoaPandaWindow.mm +// Created by: rdb (25May12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#import "cocoaPandaWindow.h" +#import "cocoaPandaWindowDelegate.h" + +@implementation CocoaPandaWindow +- (id) initWithContentRect:(NSRect)rect styleMask:(NSUInteger)styleMask screen:(NSScreen*)screen window:(CocoaGraphicsWindow*)window { + + if (self = [super initWithContentRect:rect + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:YES + screen:screen]) { + _graphicsWindow = window; + + CocoaPandaWindowDelegate *delegate = [[CocoaPandaWindowDelegate alloc] initWithGraphicsWindow:window]; + [self setDelegate:delegate]; + [self setOpaque:YES]; + + // Necessary to be able to accept mouseMoved in the NSView + [self setAcceptsMouseMovedEvents:YES]; + } + + return self; +} + +- (BOOL) canBecomeKeyWindow { + // Otherwise borderless windows won't be able to get keyboard events. + return YES; +} + +@end diff --git a/panda/src/cocoadisplay/cocoaPandaWindowDelegate.h b/panda/src/cocoadisplay/cocoaPandaWindowDelegate.h new file mode 100644 index 0000000000..b7aee05e11 --- /dev/null +++ b/panda/src/cocoadisplay/cocoaPandaWindowDelegate.h @@ -0,0 +1,36 @@ +// Filename: cocoaPandaWindowDelegate.h +// Created by: rdb (24May12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#import + +class CocoaGraphicsWindow; + +@interface CocoaPandaWindowDelegate : NSObject { + @private + CocoaGraphicsWindow *_graphicsWindow; +} + +- (id) initWithGraphicsWindow:(CocoaGraphicsWindow*)window; +- (void)windowDidMove:(NSNotification *)notification; +- (void)windowDidResize:(NSNotification *)notification; +- (void)windowDidMiniaturize:(NSNotification *)notification; +- (void)windowDidDeminiaturize:(NSNotification *)notification; +- (void)windowDidBecomeKey:(NSNotification *)notification; +- (void)windowDidResignKey:(NSNotification *)notification; +- (BOOL)windowShouldClose:(id)sender; +- (void)windowWillClose:(NSNotification *)notification; + +//TODO: handle fullscreen on Lion. + +@end diff --git a/panda/src/cocoadisplay/cocoaPandaWindowDelegate.mm b/panda/src/cocoadisplay/cocoaPandaWindowDelegate.mm new file mode 100644 index 0000000000..505c2f2a5a --- /dev/null +++ b/panda/src/cocoadisplay/cocoaPandaWindowDelegate.mm @@ -0,0 +1,61 @@ +// Filename: cocoaPandaWindowDelegate.mm +// Created by: rdb (24May12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#import "cocoaPandaWindowDelegate.h" + +@implementation CocoaPandaWindowDelegate +- (id) initWithGraphicsWindow:(CocoaGraphicsWindow*)window { + + if (self = [super init]) { + _graphicsWindow = window; + } + + return self; +} + +- (void) windowDidMove:(NSNotification *)notification { + _graphicsWindow->handle_move_event(); +} + +- (void) windowDidResize:(NSNotification *)notification { + // Forcing a move event is unfortunately necessary because + // Cocoa does not call windowDidMove in case of window zooms. + _graphicsWindow->handle_resize_event(); +} + +- (void) windowDidMiniaturize:(NSNotification *)notification { + _graphicsWindow->handle_minimize_event(true); +} + +- (void) windowDidDeminiaturize:(NSNotification *)notification { + _graphicsWindow->handle_minimize_event(false); +} + +- (void) windowDidBecomeKey:(NSNotification *)notification { + _graphicsWindow->handle_foreground_event(true); +} + +- (void) windowDidResignKey:(NSNotification *)notification { + _graphicsWindow->handle_foreground_event(false); +} + +- (BOOL) windowShouldClose:(id)sender { + return _graphicsWindow->handle_close_request(); +} + +- (void) windowWillClose:(NSNotification *)notification { + _graphicsWindow->handle_close_event(); +} + +@end diff --git a/panda/src/cocoadisplay/config_cocoadisplay.h b/panda/src/cocoadisplay/config_cocoadisplay.h new file mode 100644 index 0000000000..04754bf635 --- /dev/null +++ b/panda/src/cocoadisplay/config_cocoadisplay.h @@ -0,0 +1,25 @@ +// Filename: config_cocoadisplay.h +// Created by: rdb (17May12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_COCOADISPLAY_H +#define CONFIG_COCOADISPLAY_H + +#include "pandabase.h" +#include "notifyCategoryProxy.h" + +NotifyCategoryDecl(cocoadisplay, EXPCL_PANDAGL, EXPTP_PANDAGL); + +extern EXPCL_PANDAGL void init_libcocoadisplay(); + +#endif diff --git a/panda/src/cocoadisplay/config_cocoadisplay.mm b/panda/src/cocoadisplay/config_cocoadisplay.mm new file mode 100644 index 0000000000..f3a59a242b --- /dev/null +++ b/panda/src/cocoadisplay/config_cocoadisplay.mm @@ -0,0 +1,56 @@ +// Filename: config_cocoadisplay.cxx +// Created by: rdb (17May12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "config_cocoadisplay.h" +#include "cocoaGraphicsPipe.h" +#include "cocoaGraphicsStateGuardian.h" +#include "cocoaGraphicsWindow.h" +#include "graphicsPipeSelection.h" +#include "dconfig.h" +#include "pandaSystem.h" + +Configure(config_cocoadisplay); +NotifyCategoryDef(cocoadisplay, "display"); + +ConfigureFn(config_cocoadisplay) { + init_libcocoadisplay(); +} + +//////////////////////////////////////////////////////////////////// +// Function: init_libcocoadisplay +// Description: Initializes the library. This must be called at +// least once before any of the functions or classes in +// this library can be used. Normally it will be +// called by the static initializers and need not be +// called explicitly, but special cases exist. +//////////////////////////////////////////////////////////////////// +void +init_libcocoadisplay() { + static bool initialized = false; + if (initialized) { + return; + } + initialized = true; + + CocoaGraphicsPipe::init_type(); + CocoaGraphicsStateGuardian::init_type(); + CocoaGraphicsWindow::init_type(); + + GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr(); + selection->add_pipe_type(CocoaGraphicsPipe::get_class_type(), + CocoaGraphicsPipe::pipe_constructor); + + PandaSystem *ps = PandaSystem::get_global_ptr(); + ps->set_system_tag("OpenGL", "window_system", "Cocoa"); +} diff --git a/panda/src/cocoadisplay/p3cocoadisplay_composite1.mm b/panda/src/cocoadisplay/p3cocoadisplay_composite1.mm new file mode 100644 index 0000000000..fe8d7d03de --- /dev/null +++ b/panda/src/cocoadisplay/p3cocoadisplay_composite1.mm @@ -0,0 +1,7 @@ +#include "config_cocoadisplay.mm" +#include "cocoaGraphicsPipe.mm" +#include "cocoaGraphicsStateGuardian.mm" +#include "cocoaGraphicsWindow.mm" +#include "cocoaPandaView.mm" +#include "cocoaPandaWindow.mm" +#include "cocoaPandaWindowDelegate.mm" \ No newline at end of file