summaryrefslogtreecommitdiffstats
path: root/o3d/plugin/mac/overlay_window_mac.mm
diff options
context:
space:
mode:
Diffstat (limited to 'o3d/plugin/mac/overlay_window_mac.mm')
-rw-r--r--o3d/plugin/mac/overlay_window_mac.mm286
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