diff options
Diffstat (limited to 'o3d/plugin/mac/overlay_window_mac.mm')
-rw-r--r-- | o3d/plugin/mac/overlay_window_mac.mm | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/o3d/plugin/mac/overlay_window_mac.mm b/o3d/plugin/mac/overlay_window_mac.mm new file mode 100644 index 0000000..ea3af06 --- /dev/null +++ b/o3d/plugin/mac/overlay_window_mac.mm @@ -0,0 +1,286 @@ +/* + * Copyright 2010, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "overlay_window_mac.h" + +#import "plugin/mac/graphics_utils_mac.h" + +#ifdef O3D_PLUGIN_ENABLE_FULLSCREEN_MSG + +#define kTransitionTime 1.0 + +namespace o3d { + +// A little wrapper for ATSUSetAttributes to make calling it with one attribute +// less annoying. +static void MySetAttribute(ATSUStyle style, + ATSUAttributeTag tag, + ByteCount size, + ATSUAttributeValuePtr value) { + + ATSUAttributeTag tags[2] = {tag, 0}; + ByteCount sizes[2] = {size, 0}; + ATSUAttributeValuePtr values[2] = {value, 0}; + + ATSUSetAttributes(style, 1, tags, sizes, values); +} + +// A little wrapper for ATSUSetLayoutControls to make calling it with one +// attribute less annoying. +static void MySetLayoutControl(ATSUTextLayout layout, + ATSUAttributeTag tag, + ByteCount size, + ATSUAttributeValuePtr value) { + + ATSUAttributeTag tags[2] = {tag, 0}; + ByteCount sizes[2] = {size, 0}; + ATSUAttributeValuePtr values[2] = {value, 0}; + + ATSUSetLayoutControls(layout, 1, tags, sizes, values); +} + +// Returns the unicode 16 chars that we need to display as the fullscreen +// message. Should be disposed with free() after use. +static UniChar * GetFullscreenDisplayText(int *returned_length) { + // TODO this will need to be a localized string. + NSString* ns_display_text = @"Press ESC to exit fullscreen"; + int count = [ns_display_text length]; + UniChar* display_text_16 = (UniChar*) calloc(count, sizeof(UniChar)); + + [ns_display_text getCharacters:display_text_16]; + *returned_length = count; + return display_text_16; +} + + +static void DrawToOverlayWindow(WindowRef overlayWindow) { + CGContextRef overlayContext = NULL; + CGFloat kWhiteOpaque[] = {1.0, 1.0, 1.0, 1.0}; + CGFloat kBlackNotOpaque[] = {0.0, 0.0, 0.0, 0.5}; + Rect bounds = {0, 0, 0, 0}; + const char* kOverlayWindowFontName = "Arial"; + const int kPointSize = 22; + const float kShadowRadius = 5.0; + const float kRoundRectRadius = 9.0; + const float kTextLeftMargin = 15.0; + const float kTextBottomMargin = 22.0; + + QDBeginCGContext(GetWindowPort(overlayWindow), &overlayContext); + GetWindowBounds(overlayWindow, kWindowContentRgn, &bounds); + + // Make the global rect local. + bounds.right -= bounds.left; + bounds.left = 0; + bounds.bottom -= bounds.top; + bounds.top = 0; + + CGRect cgTotalRect = Rect2CGRect(bounds); + CGContextSetShouldSmoothFonts(overlayContext, true); + CGContextClearRect(overlayContext, cgTotalRect); + + CGColorSpaceRef myColorSpace = CGColorSpaceCreateDeviceRGB(); + CGColorRef shadow = CGColorCreate(myColorSpace, kBlackNotOpaque); + CGColorRef roundRectBackColor = CGColorCreate(myColorSpace, kBlackNotOpaque); + CGSize shadowOffset = {0.0,0.0}; + + CGContextSetFillColor(overlayContext, kWhiteOpaque); + CGContextSetStrokeColor(overlayContext, kWhiteOpaque); + + // Draw the round rect background. + CGContextSaveGState(overlayContext); + CGContextSetFillColorWithColor(overlayContext, roundRectBackColor); + CGRect cg_rounded_area = + CGRectMake(// Offset from left and bottom to give shadow its space. + kShadowRadius, kShadowRadius, + // Increase width and height so rounded corners + // will be clipped out, except at bottom left. + (bounds.right - bounds.left) + 30, + (bounds.bottom - bounds.top) + 30); + // Save state before applying shadow. + CGContextSetShadowWithColor(overlayContext, shadowOffset, + kShadowRadius, shadow); + PaintRoundedCGRect(overlayContext, cg_rounded_area, kRoundRectRadius, true); + // Restore graphics state to remove shadow. + CGContextRestoreGState(overlayContext); + + // Draw the text. + int text_length = 0; + UniChar* display_text = GetFullscreenDisplayText(&text_length); + + if ((text_length > 0) && (display_text != NULL)) { + ATSUStyle style; + ATSUTextLayout layout; + ATSUFontID font; + Fixed pointSize = Long2Fix(kPointSize); + Boolean is_bold = true; + + ATSUCreateStyle(&style); + ATSUFindFontFromName(kOverlayWindowFontName, strlen(kOverlayWindowFontName), + kFontFullName, kFontNoPlatformCode, kFontNoScriptCode, + kFontNoLanguageCode, &font); + + MySetAttribute(style, kATSUFontTag, sizeof(font), &font); + MySetAttribute(style, kATSUSizeTag, sizeof(pointSize), &pointSize); + MySetAttribute(style, kATSUQDBoldfaceTag, sizeof(Boolean), &is_bold); + + + ATSUCreateTextLayout(&layout); + ATSUSetTextPointerLocation(layout, display_text, + kATSUFromTextBeginning, kATSUToTextEnd, + text_length); + ATSUSetRunStyle(layout, style, kATSUFromTextBeginning, kATSUToTextEnd); + + MySetLayoutControl(layout, kATSUCGContextTag, + sizeof(CGContextRef), &overlayContext); + + // Need to enable this for languages like Japanese to draw as something + // other than a series of squares. + ATSUSetTransientFontMatching(layout, true); + + + CGContextSetFillColor(overlayContext, kWhiteOpaque); + ATSUDrawText(layout, kATSUFromTextBeginning, kATSUToTextEnd, + X2Fix(kShadowRadius + kTextLeftMargin), + X2Fix(kShadowRadius + kTextBottomMargin)); + ATSUDisposeStyle(style); + ATSUDisposeTextLayout(layout); + free(display_text); + } + + CGColorRelease(roundRectBackColor); + CGColorRelease(shadow); + CGColorSpaceRelease (myColorSpace); + + QDEndCGContext(GetWindowPort(overlayWindow), &overlayContext); +} + +static OSStatus HandleOverlayWindow(EventHandlerCallRef inHandlerCallRef, + EventRef inEvent, + void *inUserData) { + OSType event_class = GetEventClass(inEvent); + OSType event_kind = GetEventKind(inEvent); + + if (event_class == kEventClassWindow && + event_kind == kEventWindowPaint) { + WindowRef theWindow = NULL; + GetEventParameter(inEvent, kEventParamDirectObject, + typeWindowRef, NULL, + sizeof(theWindow), NULL, + &theWindow); + if (theWindow) { + CallNextEventHandler(inHandlerCallRef, inEvent); + DrawToOverlayWindow(theWindow); + } + } + + return noErr; +} + + + +static Rect GetOverlayWindowRect(bool visible) { +#define kOverlayHeight 60 +#define kOverlayWidth 340 + Rect screen_bounds = CGRect2Rect(CGDisplayBounds(CGMainDisplayID())); + Rect hidden_window_bounds = {screen_bounds.top - kOverlayHeight, + screen_bounds.right - kOverlayWidth, + screen_bounds.top, + screen_bounds.right}; + Rect visible_window_bounds = {screen_bounds.top, + screen_bounds.right - kOverlayWidth, + screen_bounds.top + kOverlayHeight, + screen_bounds.right}; + + return (visible) ? visible_window_bounds : hidden_window_bounds; +} + + +static WindowRef CreateOverlayWindow(void) { + Rect window_bounds = GetOverlayWindowRect(false); + WindowClass wClass = kOverlayWindowClass; + WindowRef window = NULL; + OSStatus err = noErr; + WindowAttributes overlayAttributes = kWindowNoShadowAttribute | + kWindowIgnoreClicksAttribute | + kWindowNoActivatesAttribute | + kWindowStandardHandlerAttribute; + EventTypeSpec eventTypes[] = { + {kEventClassWindow, kEventWindowPaint}, + {kEventClassWindow, kEventWindowShown} + }; + + err = CreateNewWindow(wClass, + overlayAttributes, + &window_bounds, + &window); + if (err) + return NULL; + + SetWindowLevel(window, CGShieldingWindowLevel() + 1); + InstallEventHandler(GetWindowEventTarget(window), HandleOverlayWindow, + sizeof(eventTypes)/sizeof(eventTypes[0]), eventTypes, + NULL, NULL); + return window; +} + +OverlayWindowMac::OverlayWindowMac() + : overlay_window_(NULL), + time_to_hide_overlay_(0.0) { + overlay_window_ = CreateOverlayWindow(); + ShowWindow(overlay_window_); + o3d::SlideWindowToRect(overlay_window_, + Rect2CGRect(GetOverlayWindowRect(true)), + kTransitionTime); + + // Hide the overlay text 4 seconds from now. + time_to_hide_overlay_ = [NSDate timeIntervalSinceReferenceDate] + 4.0; +} + +OverlayWindowMac::~OverlayWindowMac() { + HideWindow(overlay_window_); + ReleaseWindowGroup(GetWindowGroup(overlay_window_)); + DisposeWindow(overlay_window_); +} + +void OverlayWindowMac::IdleCallback() { + if ((overlay_window_ != NULL) && + (time_to_hide_overlay_ != 0.0) && + (time_to_hide_overlay_ < [NSDate timeIntervalSinceReferenceDate])) { + time_to_hide_overlay_ = 0.0; + SlideWindowToRect(overlay_window_, + Rect2CGRect(GetOverlayWindowRect(false)), + kTransitionTime); + } +} + +} // namespace o3d + +#endif // O3D_PLUGIN_ENABLE_FULLSCREEN_MSG |