[PATCH] winemac: Add support for a high-resolution ("Retina") rendering mode.

Ken Thomases ken at codeweavers.com
Thu May 5 13:53:36 CDT 2016


When this Retina mode is enabled and the primary display is in the user's
default configuration, Wine gets told that screen and window sizes and mouse
coordinates are twice what Cocoa reports them as in its virtual coordinate
system ("points").  The Windows apps then renders at that high resolution and
the Mac driver blits it to screen.  If the screen is actually a Retina display
in a high-DPI mode, then this extra detail will be preserved.  Otherwise, the
rendering will be downsampled and blurry.

This is intended to be combined with increasing the Windows DPI, as via winecfg.
If that is doubled to 192, then, in theory, graphical elements will remain the
same visual size on screen but be rendered with finer detail.  Unfortunately,
many Windows programs don't correctly handle non-standard DPI so the results
are not always perfect.

The registry setting to enable Retina mode is:

[HKEY_CURRENT_USER\Software\Wine\Mac Driver]
"RetinaMode"="y"

Note that this setting is not looked for in the AppDefaults\<exe name> key
because it doesn't make sense for only some processes in a Wine session to see
the high-resolution sizes and coordinates.

Signed-off-by: Ken Thomases <ken at codeweavers.com>
---
 dlls/winemac.drv/cocoa_app.m     |  52 +++++++++--
 dlls/winemac.drv/cocoa_display.m |   2 +
 dlls/winemac.drv/cocoa_event.m   |   2 +
 dlls/winemac.drv/cocoa_opengl.h  |   2 +
 dlls/winemac.drv/cocoa_opengl.m  | 109 +++++++++++++++++++---
 dlls/winemac.drv/cocoa_window.h  |   6 ++
 dlls/winemac.drv/cocoa_window.m  | 193 +++++++++++++++++++++++++++++++++------
 dlls/winemac.drv/display.c       |  66 ++++++++++++-
 dlls/winemac.drv/gdi.c           |  61 ++++++++++---
 dlls/winemac.drv/macdrv.h        |   1 +
 dlls/winemac.drv/macdrv_cocoa.h  |  77 +++++++++++++++-
 dlls/winemac.drv/macdrv_main.c   |   6 ++
 dlls/winemac.drv/opengl.c        |  82 ++++++++++++++++-
 dlls/winemac.drv/surface.c       |   2 +-
 14 files changed, 587 insertions(+), 74 deletions(-)

diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m
index 97d13e2..f64de31 100644
--- a/dlls/winemac.drv/cocoa_app.m
+++ b/dlls/winemac.drv/cocoa_app.m
@@ -1590,6 +1590,7 @@ - (void) handleMouseMove:(NSEvent*)anEvent
             {
                 if (clippingCursor)
                     [self clipCursorLocation:&point];
+                point = cgpoint_win_from_mac(point);
 
                 event = macdrv_create_event(MOUSE_MOVED_ABSOLUTE, targetWindow);
                 event->mouse_moved.x = floor(point.x);
@@ -1600,18 +1601,20 @@ - (void) handleMouseMove:(NSEvent*)anEvent
             }
             else
             {
+                double scale = retina_on ? 2 : 1;
+
                 /* Add event delta to accumulated delta error */
                 /* deltaY is already flipped */
                 mouseMoveDeltaX += [anEvent deltaX];
                 mouseMoveDeltaY += [anEvent deltaY];
 
                 event = macdrv_create_event(MOUSE_MOVED, targetWindow);
-                event->mouse_moved.x = mouseMoveDeltaX;
-                event->mouse_moved.y = mouseMoveDeltaY;
+                event->mouse_moved.x = mouseMoveDeltaX * scale;
+                event->mouse_moved.y = mouseMoveDeltaY * scale;
 
                 /* Keep the remainder after integer truncation. */
-                mouseMoveDeltaX -= event->mouse_moved.x;
-                mouseMoveDeltaY -= event->mouse_moved.y;
+                mouseMoveDeltaX -= event->mouse_moved.x / scale;
+                mouseMoveDeltaY -= event->mouse_moved.y / scale;
             }
 
             if (event->type == MOUSE_MOVED_ABSOLUTE || event->mouse_moved.x || event->mouse_moved.y)
@@ -1730,6 +1733,8 @@ - (void) handleMouseButton:(NSEvent*)theEvent
             {
                 macdrv_event* event;
 
+                pt = cgpoint_win_from_mac(pt);
+
                 event = macdrv_create_event(MOUSE_BUTTON, window);
                 event->mouse_button.button = [theEvent buttonNumber];
                 event->mouse_button.pressed = pressed;
@@ -1811,6 +1816,8 @@ - (void) handleScrollWheel:(NSEvent*)theEvent
                 double x, y;
                 BOOL continuous = FALSE;
 
+                pt = cgpoint_win_from_mac(pt);
+
                 event = macdrv_create_event(MOUSE_SCROLL, window);
                 event->mouse_scroll.x = floor(pt.x);
                 event->mouse_scroll.y = floor(pt.y);
@@ -2146,6 +2153,26 @@ - (void) unminimizeWindowIfNoneVisible
         }
     }
 
+    - (void) setRetinaMode:(int)mode
+    {
+        retina_on = mode;
+
+        if (clippingCursor)
+        {
+            double scale = mode ? 0.5 : 2.0;
+            cursorClipRect.origin.x *= scale;
+            cursorClipRect.origin.y *= scale;
+            cursorClipRect.size.width *= scale;
+            cursorClipRect.size.height *= scale;
+        }
+
+        for (WineWindow* window in [NSApp windows])
+        {
+            if ([window isKindOfClass:[WineWindow class]])
+                [window setRetinaMode:mode];
+        }
+    }
+
 
     /*
      * ---------- NSApplicationDelegate methods ----------
@@ -2496,7 +2523,7 @@ int macdrv_get_cursor_position(CGPoint *pos)
     OnMainThread(^{
         NSPoint location = [NSEvent mouseLocation];
         location = [[WineApplicationController sharedController] flippedMouseLocation:location];
-        *pos = NSPointToCGPoint(location);
+        *pos = cgpoint_win_from_mac(NSPointToCGPoint(location));
     });
 
     return TRUE;
@@ -2513,7 +2540,7 @@ int macdrv_set_cursor_position(CGPoint pos)
     __block int ret;
 
     OnMainThread(^{
-        ret = [[WineApplicationController sharedController] setCursorPosition:pos];
+        ret = [[WineApplicationController sharedController] setCursorPosition:cgpoint_mac_from_win(pos)];
     });
 
     return ret;
@@ -2526,13 +2553,17 @@ int macdrv_set_cursor_position(CGPoint pos)
  * to or larger than the whole desktop region, the cursor is unclipped.
  * Returns zero on failure, non-zero on success.
  */
-int macdrv_clip_cursor(CGRect rect)
+int macdrv_clip_cursor(CGRect r)
 {
     __block int ret;
 
     OnMainThread(^{
         WineApplicationController* controller = [WineApplicationController sharedController];
         BOOL clipping = FALSE;
+        CGRect rect = r;
+
+        if (!CGRectIsInfinite(rect))
+            rect = cgrect_mac_from_win(rect);
 
         if (!CGRectIsInfinite(rect))
         {
@@ -2676,3 +2707,10 @@ int macdrv_select_input_source(TISInputSourceRef input_source)
 
     return ret;
 }
+
+void macdrv_set_cocoa_retina_mode(int new_mode)
+{
+    OnMainThread(^{
+        [[WineApplicationController sharedController] setRetinaMode:new_mode];
+    });
+}
diff --git a/dlls/winemac.drv/cocoa_display.m b/dlls/winemac.drv/cocoa_display.m
index 5e3c8f3..93a0fbc 100644
--- a/dlls/winemac.drv/cocoa_display.m
+++ b/dlls/winemac.drv/cocoa_display.m
@@ -78,6 +78,8 @@ int macdrv_get_displays(struct macdrv_display** displays, int* count)
                 convert_display_rect(&disps[i].frame, frame, primary_frame);
                 convert_display_rect(&disps[i].work_frame, visible_frame,
                                      primary_frame);
+                disps[i].frame = cgrect_win_from_mac(disps[i].frame);
+                disps[i].work_frame = cgrect_win_from_mac(disps[i].work_frame);
             }
 
             *displays = disps;
diff --git a/dlls/winemac.drv/cocoa_event.m b/dlls/winemac.drv/cocoa_event.m
index 9ce43fb..08a706f 100644
--- a/dlls/winemac.drv/cocoa_event.m
+++ b/dlls/winemac.drv/cocoa_event.m
@@ -323,6 +323,8 @@ - (void) resetMouseEventPositions:(CGPoint)pos
     {
         MacDrvEvent* event;
 
+        pos = cgpoint_win_from_mac(pos);
+
         [eventsLock lock];
 
         for (event in events)
diff --git a/dlls/winemac.drv/cocoa_opengl.h b/dlls/winemac.drv/cocoa_opengl.h
index 2cdcdbd..e7e1686 100644
--- a/dlls/winemac.drv/cocoa_opengl.h
+++ b/dlls/winemac.drv/cocoa_opengl.h
@@ -26,6 +26,8 @@ @interface WineOpenGLContext : NSOpenGLContext
     NSView* latentView;
     BOOL needsUpdate;
     BOOL shouldClearToBlack;
+
+    GLint backing_size[2];
 }
 
 @property BOOL needsUpdate;
diff --git a/dlls/winemac.drv/cocoa_opengl.m b/dlls/winemac.drv/cocoa_opengl.m
index ec2b191..debeb78 100644
--- a/dlls/winemac.drv/cocoa_opengl.m
+++ b/dlls/winemac.drv/cocoa_opengl.m
@@ -27,6 +27,10 @@
 
 @interface WineOpenGLContext ()
 @property (retain, nonatomic) NSView* latentView;
+
+    + (NSView*) dummyView;
+    - (void) wine_updateBackingSize:(const CGSize*)size;
+
 @end
 
 
@@ -40,6 +44,81 @@ - (void) dealloc
         [super dealloc];
     }
 
+    + (NSView*) dummyView
+    {
+        static NSWindow* dummyWindow;
+        static dispatch_once_t once;
+
+        dispatch_once(&once, ^{
+            OnMainThread(^{
+                dummyWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
+                                                          styleMask:NSBorderlessWindowMask
+                                                            backing:NSBackingStoreBuffered
+                                                              defer:NO];
+            });
+        });
+
+        return dummyWindow.contentView;
+    }
+
+    // Normally, we take care that disconnecting a context from a view doesn't
+    // destroy that view's GL surface (see -clearDrawableLeavingSurfaceOnScreen).
+    // However, if we're using a surface backing size and that size changes, we
+    // need to destroy and recreate the surface or we get weird behavior.
+    - (void) resetSurfaceIfBackingSizeChanged
+    {
+        if (!retina_enabled)
+            return;
+
+        int view_backing[2];
+        if (macdrv_get_view_backing_size((macdrv_view)self.view, view_backing) &&
+            (view_backing[0] != backing_size[0] || view_backing[1] != backing_size[1]))
+        {
+            view_backing[0] = backing_size[0];
+            view_backing[1] = backing_size[1];
+            macdrv_set_view_backing_size((macdrv_view)self.view, view_backing);
+
+            NSView* save = self.view;
+            [super clearDrawable];
+            [super setView:save];
+            shouldClearToBlack = TRUE;
+        }
+    }
+
+    - (void) wine_updateBackingSize:(const CGSize*)size
+    {
+        GLint enabled;
+
+        if (!retina_enabled)
+            return;
+
+        if (size)
+        {
+            if (CGLIsEnabled(self.CGLContextObj, kCGLCESurfaceBackingSize, &enabled) != kCGLNoError)
+                enabled = 0;
+
+            if (!enabled || backing_size[0] != size->width || backing_size[1] != size->height)
+            {
+                backing_size[0] = size->width;
+                backing_size[1] = size->height;
+                CGLSetParameter(self.CGLContextObj, kCGLCPSurfaceBackingSize, backing_size);
+            }
+
+            if (!enabled)
+                CGLEnable(self.CGLContextObj, kCGLCESurfaceBackingSize);
+
+            [self resetSurfaceIfBackingSizeChanged];
+        }
+        else
+        {
+            backing_size[0] = 0;
+            backing_size[1] = 0;
+
+            if (CGLIsEnabled(self.CGLContextObj, kCGLCESurfaceBackingSize, &enabled) == kCGLNoError && enabled)
+               CGLDisable(self.CGLContextObj, kCGLCESurfaceBackingSize);
+        }
+    }
+
     - (void) setView:(NSView*)newView
     {
         NSView* oldView = [self view];
@@ -53,6 +132,8 @@ - (void) clearDrawable
         NSView* oldView = [self view];
         [super clearDrawable];
         [oldView release];
+
+        [self wine_updateBackingSize:NULL];
     }
 
     /* On at least some versions of Mac OS X, -[NSOpenGLContext clearDrawable] has the
@@ -64,19 +145,7 @@ a different view (the content view of an off-screen window) and then letting the
        original implementation proceed. */
     - (void) clearDrawableLeavingSurfaceOnScreen
     {
-        static NSWindow* dummyWindow;
-        static dispatch_once_t once;
-
-        dispatch_once(&once, ^{
-            OnMainThread(^{
-                dummyWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
-                                                          styleMask:NSBorderlessWindowMask
-                                                            backing:NSBackingStoreBuffered
-                                                              defer:NO];
-            });
-        });
-
-        [self setView:[dummyWindow contentView]];
+        [self setView:[[self class] dummyView]];
         [self clearDrawable];
     }
 
@@ -189,7 +258,7 @@ void macdrv_dispose_opengl_context(macdrv_opengl_context c)
 /***********************************************************************
  *              macdrv_make_context_current
  */
-void macdrv_make_context_current(macdrv_opengl_context c, macdrv_view v)
+void macdrv_make_context_current(macdrv_opengl_context c, macdrv_view v, CGRect r)
 {
     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
     WineOpenGLContext *context = (WineOpenGLContext*)c;
@@ -198,7 +267,10 @@ void macdrv_make_context_current(macdrv_opengl_context c, macdrv_view v)
     if (context && view)
     {
         if (view == [context view] || view == [context latentView])
+        {
+            [context wine_updateBackingSize:&r.size];
             macdrv_update_opengl_context(c);
+        }
         else
         {
             [context removeFromViews:NO];
@@ -207,13 +279,18 @@ void macdrv_make_context_current(macdrv_opengl_context c, macdrv_view v)
             if (context.needsUpdate)
             {
                 context.needsUpdate = FALSE;
+                if (context.view)
+                    [context setView:[[context class] dummyView]];
+                [context wine_updateBackingSize:&r.size];
                 [context setView:view];
                 [context setLatentView:nil];
+                [context resetSurfaceIfBackingSizeChanged];
             }
             else
             {
                 if ([context view])
                     [context clearDrawableLeavingSurfaceOnScreen];
+                [context wine_updateBackingSize:&r.size];
                 [context setLatentView:view];
             }
         }
@@ -257,10 +334,14 @@ void macdrv_update_opengl_context(macdrv_opengl_context c)
             [context setView:context.latentView];
             context.latentView = nil;
 
+            [context resetSurfaceIfBackingSizeChanged];
             [context clearToBlackIfNeeded];
         }
         else
+        {
             [context update];
+            [context resetSurfaceIfBackingSizeChanged];
+        }
     }
 
     [pool release];
diff --git a/dlls/winemac.drv/cocoa_window.h b/dlls/winemac.drv/cocoa_window.h
index 19e5778..d52a9d2 100644
--- a/dlls/winemac.drv/cocoa_window.h
+++ b/dlls/winemac.drv/cocoa_window.h
@@ -46,6 +46,9 @@ @interface WineWindow : NSPanel <NSWindowDelegate>
     CGDirectDisplayID _lastDisplayID;
     NSTimeInterval _lastDisplayTime;
 
+    NSRect wineFrame;
+    NSRect roundedWineFrame;
+
     NSBezierPath* shape;
     NSData* shapeData;
     BOOL shapeChangedSinceLastDraw;
@@ -88,6 +91,7 @@ @interface WineWindow : NSPanel <NSWindowDelegate>
 @property (readonly, nonatomic) BOOL floating;
 @property (readonly, getter=isFullscreen, nonatomic) BOOL fullscreen;
 @property (readonly, getter=isFakingClose, nonatomic) BOOL fakingClose;
+ at property (readonly, nonatomic) NSRect wine_fractionalFrame;
 
     - (NSInteger) minimumLevelForActive:(BOOL)active;
     - (void) updateFullscreen;
@@ -99,4 +103,6 @@ - (WineWindow*) ancestorWineWindow;
 
     - (void) updateForCursorClipping;
 
+    - (void) setRetinaMode:(int)mode;
+
 @end
diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m
index 1b0b655..aee9981 100644
--- a/dlls/winemac.drv/cocoa_window.m
+++ b/dlls/winemac.drv/cocoa_window.m
@@ -294,12 +294,17 @@ @interface WineContentView : NSView <NSTextInputClient>
 
     NSMutableAttributedString* markedText;
     NSRange markedTextSelection;
+
+    int backingSize[2];
 }
 
     - (void) addGLContext:(WineOpenGLContext*)context;
     - (void) removeGLContext:(WineOpenGLContext*)context;
     - (void) updateGLContexts;
 
+    - (void) wine_getBackingSize:(int*)outBackingSize;
+    - (void) wine_setBackingSize:(const int*)newBackingSize;
+
 @end
 
 
@@ -393,6 +398,7 @@ - (void) drawRect:(NSRect)rect
 
             if (get_surface_blit_rects(window.surface, &rects, &count) && count)
             {
+                CGRect dirtyRect = cgrect_win_from_mac(NSRectToCGRect(rect));
                 CGContextRef context;
                 int i;
 
@@ -400,14 +406,14 @@ - (void) drawRect:(NSRect)rect
 
                 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
                 CGContextSetBlendMode(context, kCGBlendModeCopy);
-                CGContextSetInterpolationQuality(context, kCGInterpolationNone);
+                CGContextSetInterpolationQuality(context, retina_on ? kCGInterpolationHigh : kCGInterpolationNone);
 
                 for (i = 0; i < count; i++)
                 {
                     CGRect imageRect;
                     CGImageRef image;
 
-                    imageRect = CGRectIntersection(rects[i], NSRectToCGRect(rect));
+                    imageRect = CGRectIntersection(rects[i], dirtyRect);
                     image = create_surface_image(window.surface, &imageRect, FALSE);
 
                     if (image)
@@ -426,7 +432,7 @@ - (void) drawRect:(NSRect)rect
                             }
                         }
 
-                        CGContextDrawImage(context, imageRect, image);
+                        CGContextDrawImage(context, cgrect_mac_from_win(imageRect), image);
 
                         CGImageRelease(image);
                     }
@@ -490,6 +496,19 @@ - (BOOL) hasGLContext
         return [glContexts count] || [pendingGlContexts count];
     }
 
+    - (void) wine_getBackingSize:(int*)outBackingSize
+    {
+        @synchronized(self) {
+            memcpy(outBackingSize, backingSize, sizeof(backingSize));
+        }
+    }
+    - (void) wine_setBackingSize:(const int*)newBackingSize
+    {
+        @synchronized(self) {
+            memcpy(backingSize, newBackingSize, sizeof(backingSize));
+        }
+    }
+
     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
     {
         return YES;
@@ -652,7 +671,7 @@ - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointe
         if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
         {
             aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
-            ret = NSRectFromCGRect(query->ime_char_rect.rect);
+            ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
             [[WineApplicationController sharedController] flipRect:&ret];
         }
         else
@@ -772,6 +791,8 @@ + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)w
                                                                   name:NSWorkspaceActiveSpaceDidChangeNotification
                                                                 object:[NSWorkspace sharedWorkspace]];
 
+        [window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
+
         return window;
     }
 
@@ -821,7 +842,7 @@ - (void) adjustFeaturesForState
 
         if ([self preventResizing])
         {
-            NSSize size = [self contentRectForFrameRect:[self frame]].size;
+            NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
             [self setContentMinSize:size];
             [self setContentMaxSize:size];
         }
@@ -1433,7 +1454,7 @@ - (void) doOrderOut
 
     - (void) updateFullscreen
     {
-        NSRect contentRect = [self contentRectForFrameRect:[self frame]];
+        NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
         BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
 
         if (nowFullscreen != fullscreen)
@@ -1448,6 +1469,32 @@ - (void) updateFullscreen
         }
     }
 
+    - (void) setFrameAndWineFrame:(NSRect)frame
+    {
+        [self setFrame:frame display:YES];
+
+        wineFrame = frame;
+        roundedWineFrame = self.frame;
+        CGFloat junk;
+#if CGFLOAT_IS_DOUBLE
+        if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
+             !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
+            fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
+            fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
+            fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
+            fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
+            roundedWineFrame = wineFrame;
+#else
+        if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
+             !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
+            fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
+            fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
+            fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
+            fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
+            roundedWineFrame = wineFrame;
+#endif
+    }
+
     - (void) setFrameFromWine:(NSRect)contentRect
     {
         /* Origin is (left, top) in a top-down space.  Need to convert it to
@@ -1464,7 +1511,7 @@ - (void) setFrameFromWine:(NSRect)contentRect
         {
             NSRect frame, oldFrame;
 
-            oldFrame = [self frame];
+            oldFrame = self.wine_fractionalFrame;
             frame = [self frameRectForContentRect:contentRect];
             if (!NSEqualRects(frame, oldFrame))
             {
@@ -1498,7 +1545,7 @@ - (void) setFrameFromWine:(NSRect)contentRect
                     ignore_windowResize = FALSE;
                 }
 
-                [self setFrame:frame display:YES];
+                [self setFrameAndWineFrame:frame];
                 if ([self preventResizing])
                 {
                     [self setContentMinSize:contentRect.size];
@@ -1527,6 +1574,14 @@ - (void) setFrameFromWine:(NSRect)contentRect
         }
     }
 
+    - (NSRect) wine_fractionalFrame
+    {
+        NSRect frame = self.frame;
+        if (NSEqualRects(frame, roundedWineFrame))
+            frame = wineFrame;
+        return frame;
+    }
+
     - (void) setMacDrvParentWindow:(WineWindow*)parent
     {
         WineWindow* oldParent = (WineWindow*)[self parentWindow];
@@ -2052,7 +2107,7 @@ - (void) sendEvent:(NSEvent*)event
 
                     event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
                     event->window_restore_requested.keep_frame = TRUE;
-                    event->window_restore_requested.frame = NSRectToCGRect(frame);
+                    event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
                     [queue postEvent:event];
                     macdrv_release_event(event);
 
@@ -2165,6 +2220,57 @@ - (void) updateForGLSubviews
             [self checkTransparency];
     }
 
+    - (void) setRetinaMode:(int)mode
+    {
+        NSRect frame;
+        double scale = mode ? 0.5 : 2.0;
+        NSAffineTransform* transform = [NSAffineTransform transform];
+
+        [transform scaleBy:scale];
+
+        if (shape)
+            [shape transformUsingAffineTransform:transform];
+
+        for (WineContentView* subview in [self.contentView subviews])
+        {
+            if ([subview isKindOfClass:[WineContentView class]])
+            {
+                frame = subview.frame;
+                frame.origin.x *= scale;
+                frame.origin.y *= scale;
+                frame.size.width *= scale;
+                frame.size.height *= scale;
+                [subview setFrame:frame];
+                [subview updateGLContexts];
+            }
+        }
+
+        frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
+        frame.origin.x *= scale;
+        frame.origin.y *= scale;
+        frame.size.width *= scale;
+        frame.size.height *= scale;
+        frame = [self frameRectForContentRect:frame];
+
+        savedContentMinSize = [transform transformSize:savedContentMinSize];
+        if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
+            savedContentMaxSize.width *= scale;
+        if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
+            savedContentMaxSize.height *= scale;
+
+        self.contentMinSize = [transform transformSize:self.contentMinSize];
+        NSSize temp = self.contentMaxSize;
+        if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
+            temp.width *= scale;
+        if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
+            temp.height *= scale;
+        self.contentMaxSize = temp;
+
+        ignore_windowResize = TRUE;
+        [self setFrameAndWineFrame:frame];
+        ignore_windowResize = FALSE;
+    }
+
 
     /*
      * ---------- NSResponder method overrides ----------
@@ -2337,7 +2443,7 @@ - (void) windowDidEnterFullScreen:(NSNotification*)notification
     - (void) windowDidExitFullScreen:(NSNotification*)notification
     {
         exitingFullScreen = FALSE;
-        [self setFrame:nonFullscreenFrame display:YES animate:NO];
+        [self setFrameAndWineFrame:nonFullscreenFrame];
         [self windowDidResize:nil];
     }
 
@@ -2379,7 +2485,7 @@ - (void)windowDidResignKey:(NSNotification *)notification
     - (void)windowDidResize:(NSNotification *)notification
     {
         macdrv_event* event;
-        NSRect frame = [self frame];
+        NSRect frame = self.wine_fractionalFrame;
 
         if ([self inLiveResize])
         {
@@ -2406,7 +2512,7 @@ - (void)windowDidResize:(NSNotification *)notification
                                forWindow:self];
 
         event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
-        event->window_frame_changed.frame = NSRectToCGRect(frame);
+        event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
         event->window_frame_changed.fullscreen = ([self styleMask] & NSFullScreenWindowMask) != 0;
         event->window_frame_changed.in_resize = [self inLiveResize];
         [queue postEvent:event];
@@ -2466,7 +2572,7 @@ - (void) windowWillClose:(NSNotification*)notification
     - (void) windowWillEnterFullScreen:(NSNotification*)notification
     {
         enteringFullScreen = TRUE;
-        nonFullscreenFrame = [self frame];
+        nonFullscreenFrame = self.wine_fractionalFrame;
     }
 
     - (void) windowWillExitFullScreen:(NSNotification*)notification
@@ -2485,7 +2591,7 @@ - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
         if ([self inLiveResize])
         {
             if (maximized)
-                return self.frame.size;
+                return self.wine_fractionalFrame.size;
 
             NSRect rect;
             macdrv_query* query;
@@ -2502,13 +2608,13 @@ - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
             query = macdrv_create_query();
             query->type = QUERY_RESIZE_SIZE;
             query->window = (macdrv_window)[self retain];
-            query->resize_size.rect = NSRectToCGRect(rect);
+            query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
             query->resize_size.from_left = resizingFromLeft;
             query->resize_size.from_top = resizingFromTop;
 
             if ([self.queue query:query timeout:0.1])
             {
-                rect = NSRectFromCGRect(query->resize_size.rect);
+                rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
                 rect = [self frameRectForContentRect:rect];
                 frameSize = rect.size;
             }
@@ -2532,7 +2638,7 @@ - (void) windowWillStartLiveResize:(NSNotification *)notification
 
             event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
             event->window_restore_requested.keep_frame = TRUE;
-            event->window_restore_requested.frame = NSRectToCGRect(frame);
+            event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
             [queue postEvent:event];
             macdrv_release_event(event);
         }
@@ -2625,13 +2731,14 @@ - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
     {
         NSDragOperation ret;
         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
+        CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
         NSPasteboard* pb = [sender draggingPasteboard];
 
         macdrv_query* query = macdrv_create_query();
         query->type = QUERY_DRAG_OPERATION;
         query->window = (macdrv_window)[self retain];
-        query->drag_operation.x = floor(pt.x);
-        query->drag_operation.y = floor(pt.y);
+        query->drag_operation.x = floor(cgpt.x);
+        query->drag_operation.y = floor(cgpt.y);
         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
         query->drag_operation.accepted_op = NSDragOperationNone;
         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
@@ -2647,13 +2754,14 @@ - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
     {
         BOOL ret;
         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
+        CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
         NSPasteboard* pb = [sender draggingPasteboard];
 
         macdrv_query* query = macdrv_create_query();
         query->type = QUERY_DRAG_DROP;
         query->window = (macdrv_window)[self retain];
-        query->drag_drop.x = floor(pt.x);
-        query->drag_drop.y = floor(pt.y);
+        query->drag_drop.x = floor(cgpt.x);
+        query->drag_drop.y = floor(cgpt.y);
         query->drag_drop.op = [sender draggingSourceOperationMask];
         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
 
@@ -2685,7 +2793,7 @@ macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf
 
     OnMainThread(^{
         window = [[WineWindow createWindowWithFeatures:wf
-                                           windowFrame:NSRectFromCGRect(frame)
+                                           windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
                                                   hwnd:hwnd
                                                  queue:(WineEventQueue*)queue] retain];
     });
@@ -2829,7 +2937,7 @@ void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
     WineWindow* window = (WineWindow*)w;
 
     OnMainThread(^{
-        [window setFrameFromWine:NSRectFromCGRect(*new_frame)];
+        [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
     });
 }
 
@@ -2845,9 +2953,9 @@ void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
     OnMainThread(^{
         NSRect frame;
 
-        frame = [window contentRectForFrameRect:[window frame]];
+        frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
         [[WineApplicationController sharedController] flipRect:&frame];
-        *out_frame = NSRectToCGRect(frame);
+        *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
     });
 }
 
@@ -2894,7 +3002,7 @@ void macdrv_window_needs_display(macdrv_window w, CGRect rect)
     WineWindow* window = (WineWindow*)w;
 
     OnMainThreadAsync(^{
-        [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
+        [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
     });
 
     [pool release];
@@ -2928,7 +3036,7 @@ void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
 
                 path = [NSBezierPath bezierPath];
                 for (i = 0; i < count; i++)
-                    [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
+                    [path appendBezierPathWithRect:NSRectFromCGRect(cgrect_mac_from_win(rects[i]))];
                 window.shape = path;
                 window.shapeData = [NSData dataWithBytes:rects length:length];
                 [window checkEmptyShaped];
@@ -3030,7 +3138,7 @@ void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize ma
     WineWindow* window = (WineWindow*)w;
 
     OnMainThread(^{
-        [window setWineMinSize:NSSizeFromCGSize(min_size) maxSize:NSSizeFromCGSize(max_size)];
+        [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
     });
 }
 
@@ -3052,7 +3160,7 @@ macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
     OnMainThread(^{
         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
 
-        view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
+        view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
         [view setAutoresizesSubviews:NO];
         [nc addObserver:view
                selector:@selector(updateGLContexts)
@@ -3115,7 +3223,7 @@ void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rec
 
     OnMainThread(^{
         BOOL changedWindow = (window && window != [view window]);
-        NSRect newFrame = NSRectFromCGRect(rect);
+        NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
         NSRect oldFrame = [view frame];
         BOOL needUpdateWindowForGLSubviews = FALSE;
 
@@ -3140,6 +3248,12 @@ void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rec
                 [view setFrame:newFrame];
             [view setNeedsDisplay:YES];
             needUpdateWindowForGLSubviews = TRUE;
+
+            if (retina_enabled)
+            {
+                int backing_size[2] = { 0 };
+                [view wine_setBackingSize:backing_size];
+            }
         }
 
         if (needUpdateWindowForGLSubviews)
@@ -3185,6 +3299,25 @@ void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
     [pool release];
 }
 
+int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
+{
+    WineContentView* view = (WineContentView*)v;
+
+    if (![view isKindOfClass:[WineContentView class]])
+        return FALSE;
+
+    [view wine_getBackingSize:backing_size];
+    return TRUE;
+}
+
+void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
+{
+    WineContentView* view = (WineContentView*)v;
+
+    if ([view isKindOfClass:[WineContentView class]])
+        [view wine_setBackingSize:backing_size];
+}
+
 /***********************************************************************
  *              macdrv_window_background_color
  *
diff --git a/dlls/winemac.drv/display.c b/dlls/winemac.drv/display.c
index ad01191..49892ca 100644
--- a/dlls/winemac.drv/display.c
+++ b/dlls/winemac.drv/display.c
@@ -488,7 +488,7 @@ static int get_default_bpp(void)
 
 
 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
-static CFDictionaryRef create_mode_dict(CGDisplayModeRef display_mode)
+static CFDictionaryRef create_mode_dict(CGDisplayModeRef display_mode, BOOL is_original)
 {
     CFDictionaryRef ret;
     SInt32 io_flags = CGDisplayModeGetIOFlags(display_mode);
@@ -498,6 +498,12 @@ static CFDictionaryRef create_mode_dict(CGDisplayModeRef display_mode)
     CFStringRef pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
     CFNumberRef cf_io_flags, cf_width, cf_height, cf_refresh;
 
+    if (retina_enabled && is_original)
+    {
+        width *= 2;
+        height *= 2;
+    }
+
     io_flags &= kDisplayModeValidFlag | kDisplayModeSafeFlag | kDisplayModeInterlacedFlag |
                 kDisplayModeStretchedFlag | kDisplayModeTelevisionFlag;
     cf_io_flags = CFNumberCreate(NULL, kCFNumberSInt32Type, &io_flags);
@@ -581,7 +587,7 @@ static CFArrayRef copy_display_modes(CGDirectDisplayID display)
             BOOL better = TRUE;
             CGDisplayModeRef new_mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
             BOOL new_is_original = display_mode_matches_descriptor(new_mode, desc);
-            CFDictionaryRef key = create_mode_dict(new_mode);
+            CFDictionaryRef key = create_mode_dict(new_mode, new_is_original);
 
             /* If a given mode is the user's default, then always list it in preference to any similar
                modes that may exist. */
@@ -667,6 +673,23 @@ static CFArrayRef copy_display_modes(CGDirectDisplayID display)
 }
 
 
+void check_retina_status(void)
+{
+    if (retina_enabled)
+    {
+        struct display_mode_descriptor* desc = create_original_display_mode_descriptor(kCGDirectMainDisplay);
+        CGDisplayModeRef mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay);
+        BOOL new_value = display_mode_matches_descriptor(mode, desc);
+
+        CGDisplayModeRelease(mode);
+        free_display_mode_descriptor(desc);
+
+        if (new_value != retina_on)
+            macdrv_set_cocoa_retina_mode(new_value);
+    }
+}
+
+
 /***********************************************************************
  *              ChangeDisplaySettingsEx  (MACDRV.@)
  *
@@ -681,9 +704,11 @@ LONG CDECL macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode,
     struct macdrv_display *displays;
     int num_displays;
     CFArrayRef display_modes;
+    struct display_mode_descriptor* desc;
     CFIndex count, i, safe, best;
     CGDisplayModeRef best_display_mode;
     uint32_t best_io_flags;
+    BOOL best_is_original;
 
     TRACE("%s %p %p 0x%08x %p\n", debugstr_w(devname), devmode, hwnd, flags, lpvoid);
 
@@ -748,17 +773,26 @@ LONG CDECL macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode,
         TRACE(" %sinterlaced", devmode->dmDisplayFlags & DM_INTERLACED ? "" : "non-");
     TRACE("\n");
 
+    desc = create_original_display_mode_descriptor(displays[0].displayID);
+
     safe = -1;
     best_display_mode = NULL;
     count = CFArrayGetCount(display_modes);
     for (i = 0; i < count; i++)
     {
         CGDisplayModeRef display_mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(display_modes, i);
+        BOOL is_original = display_mode_matches_descriptor(display_mode, desc);
         uint32_t io_flags = CGDisplayModeGetIOFlags(display_mode);
         int mode_bpp = display_mode_bits_per_pixel(display_mode);
         size_t width = CGDisplayModeGetWidth(display_mode);
         size_t height = CGDisplayModeGetHeight(display_mode);
 
+        if (is_original && retina_enabled)
+        {
+            width *= 2;
+            height *= 2;
+        }
+
         if (!(io_flags & kDisplayModeValidFlag) || !(io_flags & kDisplayModeSafeFlag))
             continue;
 
@@ -817,6 +851,7 @@ better:
         best_display_mode = display_mode;
         best = safe;
         best_io_flags = io_flags;
+        best_is_original = is_original;
     }
 
     if (best_display_mode)
@@ -837,6 +872,12 @@ better:
             size_t width = CGDisplayModeGetWidth(best_display_mode);
             size_t height = CGDisplayModeGetHeight(best_display_mode);
 
+            if (best_is_original && retina_enabled)
+            {
+                width *= 2;
+                height *= 2;
+            }
+
             SendMessageW(GetDesktopWindow(), WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
                          MAKELPARAM(width, height));
             ret = DISP_CHANGE_SUCCESSFUL;
@@ -854,6 +895,7 @@ better:
             bpp, devmode->dmDisplayFrequency);
     }
 
+    free_display_mode_descriptor(desc);
     CFRelease(display_modes);
     macdrv_free_displays(displays);
 
@@ -1082,6 +1124,16 @@ BOOL CDECL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode,
 
     devmode->dmPelsWidth = CGDisplayModeGetWidth(display_mode);
     devmode->dmPelsHeight = CGDisplayModeGetHeight(display_mode);
+    if (retina_enabled)
+    {
+        struct display_mode_descriptor* desc = create_original_display_mode_descriptor(displays[0].displayID);
+        if (display_mode_matches_descriptor(display_mode, desc))
+        {
+            devmode->dmPelsWidth *= 2;
+            devmode->dmPelsHeight *= 2;
+        }
+        free_display_mode_descriptor(desc);
+    }
     devmode->dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT;
 
     devmode->dmDisplayFlags = 0;
@@ -1329,8 +1381,18 @@ void macdrv_displays_changed(const macdrv_event *event)
         size_t width = CGDisplayModeGetWidth(mode);
         size_t height = CGDisplayModeGetHeight(mode);
         int mode_bpp = display_mode_bits_per_pixel(mode);
+        struct display_mode_descriptor* desc = create_original_display_mode_descriptor(mainDisplay);
+        BOOL is_original = display_mode_matches_descriptor(mode, desc);
 
+        free_display_mode_descriptor(desc);
         CGDisplayModeRelease(mode);
+
+        if (is_original && retina_enabled)
+        {
+            width *= 2;
+            height *= 2;
+        }
+
         SendMessageW(hwnd, WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
                      MAKELPARAM(width, height));
     }
diff --git a/dlls/winemac.drv/gdi.c b/dlls/winemac.drv/gdi.c
index 9f18ac4..e110227 100644
--- a/dlls/winemac.drv/gdi.c
+++ b/dlls/winemac.drv/gdi.c
@@ -51,6 +51,8 @@ static int desktop_vert_res;    /* height in pixels of virtual desktop */
 static int bits_per_pixel;      /* pixel depth of screen */
 static int device_data_valid;   /* do the above variables have up-to-date values? */
 
+int retina_on = FALSE;
+
 static CRITICAL_SECTION device_data_section;
 static CRITICAL_SECTION_DEBUG critsect_debug =
 {
@@ -94,6 +96,29 @@ static DWORD get_dpi(void)
 
 
 /***********************************************************************
+ *              compute_desktop_rect
+ */
+static void compute_desktop_rect(void)
+{
+    CGDirectDisplayID displayIDs[32];
+    uint32_t count, i;
+
+    desktop_rect = CGRectNull;
+    if (CGGetActiveDisplayList(sizeof(displayIDs)/sizeof(displayIDs[0]),
+                               displayIDs, &count) != kCGErrorSuccess ||
+        !count)
+    {
+        displayIDs[0] = CGMainDisplayID();
+        count = 1;
+    }
+
+    for (i = 0; i < count; i++)
+        desktop_rect = CGRectUnion(desktop_rect, CGDisplayBounds(displayIDs[i]));
+    desktop_rect = cgrect_win_from_mac(desktop_rect);
+}
+
+
+/***********************************************************************
  *              macdrv_get_desktop_rect
  *
  * Returns the rectangle encompassing all the screens.
@@ -101,27 +126,16 @@ static DWORD get_dpi(void)
 CGRect macdrv_get_desktop_rect(void)
 {
     CGRect ret;
-    CGDirectDisplayID displayIDs[32];
-    uint32_t count, i;
 
     EnterCriticalSection(&device_data_section);
 
     if (!device_data_valid)
     {
-        desktop_rect = CGRectNull;
-        if (CGGetActiveDisplayList(sizeof(displayIDs)/sizeof(displayIDs[0]),
-                                   displayIDs, &count) != kCGErrorSuccess ||
-            !count)
-        {
-            displayIDs[0] = CGMainDisplayID();
-            count = 1;
-        }
-
-        for (i = 0; i < count; i++)
-            desktop_rect = CGRectUnion(desktop_rect, CGDisplayBounds(displayIDs[i]));
+        check_retina_status();
+        compute_desktop_rect();
     }
-
     ret = desktop_rect;
+
     LeaveCriticalSection(&device_data_section);
 
     TRACE("%s\n", wine_dbgstr_cgrect(ret));
@@ -141,12 +155,23 @@ static void device_init(void)
     CGSize size_mm = CGDisplayScreenSize(mainDisplay);
     CGDisplayModeRef mode = CGDisplayCopyDisplayMode(mainDisplay);
 
+    check_retina_status();
+
     /* Initialize device caps */
     log_pixels_x = log_pixels_y = get_dpi();
     if (!log_pixels_x)
     {
         size_t width = CGDisplayPixelsWide(mainDisplay);
         size_t height = CGDisplayPixelsHigh(mainDisplay);
+
+        if (retina_on)
+        {
+            /* Although CGDisplayPixelsWide/High() claim to report in pixels, they
+               actually report in points. */
+            width *= 2;
+            height *= 2;
+        }
+
         log_pixels_x = MulDiv(width, 254, size_mm.width * 10);
         log_pixels_y = MulDiv(height, 254, size_mm.height * 10);
     }
@@ -181,7 +206,13 @@ static void device_init(void)
         vert_res = CGDisplayPixelsHigh(mainDisplay);
     }
 
-    macdrv_get_desktop_rect();
+    if (retina_on)
+    {
+        horz_res *= 2;
+        vert_res *= 2;
+    }
+
+    compute_desktop_rect();
     desktop_horz_res = desktop_rect.size.width;
     desktop_vert_res = desktop_rect.size.height;
 
diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h
index f3b5e65..b6b4819 100644
--- a/dlls/winemac.drv/macdrv.h
+++ b/dlls/winemac.drv/macdrv.h
@@ -219,6 +219,7 @@ extern CGImageRef create_cgimage_from_icon_bitmaps(HDC hdc, HANDLE icon, HBITMAP
 extern void macdrv_status_item_mouse_button(const macdrv_event *event) DECLSPEC_HIDDEN;
 extern void macdrv_status_item_mouse_move(const macdrv_event *event) DECLSPEC_HIDDEN;
 
+extern void check_retina_status(void) DECLSPEC_HIDDEN;
 
 /**************************************************************************
  * Mac IME driver
diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h
index 5dc84e2..2c06bea 100644
--- a/dlls/winemac.drv/macdrv_cocoa.h
+++ b/dlls/winemac.drv/macdrv_cocoa.h
@@ -160,6 +160,78 @@
 extern int use_precise_scrolling DECLSPEC_HIDDEN;
 extern int gl_surface_mode DECLSPEC_HIDDEN;
 extern CFDictionaryRef localized_strings DECLSPEC_HIDDEN;
+extern int retina_enabled DECLSPEC_HIDDEN;  /* Whether Retina mode is enabled via registry setting. */
+extern int retina_on DECLSPEC_HIDDEN;       /* Whether Retina mode is currently active (enabled and display is in default mode). */
+
+static inline CGRect cgrect_mac_from_win(CGRect rect)
+{
+    if (retina_on)
+    {
+        rect.origin.x /= 2;
+        rect.origin.y /= 2;
+        rect.size.width /= 2;
+        rect.size.height /= 2;
+    }
+
+    return rect;
+}
+
+static inline CGRect cgrect_win_from_mac(CGRect rect)
+{
+    if (retina_on)
+    {
+        rect.origin.x *= 2;
+        rect.origin.y *= 2;
+        rect.size.width *= 2;
+        rect.size.height *= 2;
+    }
+
+    return rect;
+}
+
+static inline CGSize cgsize_mac_from_win(CGSize size)
+{
+    if (retina_on)
+    {
+        size.width /= 2;
+        size.height /= 2;
+    }
+
+    return size;
+}
+
+static inline CGSize cgsize_win_from_mac(CGSize size)
+{
+    if (retina_on)
+    {
+        size.width *= 2;
+        size.height *= 2;
+    }
+
+    return size;
+}
+
+static inline CGPoint cgpoint_mac_from_win(CGPoint point)
+{
+    if (retina_on)
+    {
+        point.x /= 2;
+        point.y /= 2;
+    }
+
+    return point;
+}
+
+static inline CGPoint cgpoint_win_from_mac(CGPoint point)
+{
+    if (retina_on)
+    {
+        point.x *= 2;
+        point.y *= 2;
+    }
+
+    return point;
+}
 
 extern int macdrv_start_cocoa_app(unsigned long long tickcount) DECLSPEC_HIDDEN;
 extern void macdrv_window_rejected_focus(const struct macdrv_event *event) DECLSPEC_HIDDEN;
@@ -168,6 +240,7 @@
 extern void macdrv_quit_reply(int reply) DECLSPEC_HIDDEN;
 extern int macdrv_using_input_method(void) DECLSPEC_HIDDEN;
 extern void macdrv_set_mouse_capture_window(macdrv_window window) DECLSPEC_HIDDEN;
+extern void macdrv_set_cocoa_retina_mode(int new_mode) DECLSPEC_HIDDEN;
 
 
 /* cursor */
@@ -443,6 +516,8 @@ extern void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat
 extern void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect) DECLSPEC_HIDDEN;
 extern void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c) DECLSPEC_HIDDEN;
 extern void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c) DECLSPEC_HIDDEN;
+extern int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2]) DECLSPEC_HIDDEN;
+extern void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2]) DECLSPEC_HIDDEN;
 extern uint32_t macdrv_window_background_color(void) DECLSPEC_HIDDEN;
 extern void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc,
                                          void* data, int* done) DECLSPEC_HIDDEN;
@@ -470,7 +545,7 @@ extern void macdrv_get_input_source_info(CFDataRef* uchr,CGEventSourceKeyboardTy
 /* opengl */
 extern macdrv_opengl_context macdrv_create_opengl_context(void* cglctx) DECLSPEC_HIDDEN;
 extern void macdrv_dispose_opengl_context(macdrv_opengl_context c) DECLSPEC_HIDDEN;
-extern void macdrv_make_context_current(macdrv_opengl_context c, macdrv_view v) DECLSPEC_HIDDEN;
+extern void macdrv_make_context_current(macdrv_opengl_context c, macdrv_view v, CGRect r) DECLSPEC_HIDDEN;
 extern void macdrv_update_opengl_context(macdrv_opengl_context c) DECLSPEC_HIDDEN;
 extern void macdrv_flush_opengl_context(macdrv_opengl_context c) DECLSPEC_HIDDEN;
 
diff --git a/dlls/winemac.drv/macdrv_main.c b/dlls/winemac.drv/macdrv_main.c
index 5af0f41..2ce78d1 100644
--- a/dlls/winemac.drv/macdrv_main.c
+++ b/dlls/winemac.drv/macdrv_main.c
@@ -58,6 +58,7 @@ int allow_immovable_windows = TRUE;
 int cursor_clipping_locks_windows = TRUE;
 int use_precise_scrolling = TRUE;
 int gl_surface_mode = GL_SURFACE_IN_FRONT_OPAQUE;
+int retina_enabled = FALSE;
 HMODULE macdrv_module = 0;
 
 CFDictionaryRef localized_strings;
@@ -196,6 +197,11 @@ static void setup_options(void)
             gl_surface_mode = GL_SURFACE_IN_FRONT_OPAQUE;
     }
 
+    /* Don't use appkey.  The DPI and monitor sizes should be consistent for all
+       processes in the prefix. */
+    if (!get_config_key(hkey, NULL, "RetinaMode", buffer, sizeof(buffer)))
+        retina_enabled = IS_OPTION_TRUE(buffer[0]);
+
     if (appkey) RegCloseKey(appkey);
     if (hkey) RegCloseKey(hkey);
 }
diff --git a/dlls/winemac.drv/opengl.c b/dlls/winemac.drv/opengl.c
index 728866b..fbdc645 100644
--- a/dlls/winemac.drv/opengl.c
+++ b/dlls/winemac.drv/opengl.c
@@ -65,12 +65,15 @@ struct wgl_context
     CGLContextObj           cglcontext;
     HWND                    draw_hwnd;
     macdrv_view             draw_view;
+    RECT                    draw_rect;
     struct wgl_pbuffer     *draw_pbuffer;
     macdrv_view             read_view;
+    RECT                    read_rect;
     struct wgl_pbuffer     *read_pbuffer;
     BOOL                    has_been_current;
     BOOL                    sharing;
     LONG                    update_swap_interval;
+    LONG                    view_moved;
     DWORD                   last_flush_time;
     UINT                    major;
 };
@@ -1691,6 +1694,23 @@ done:
 
 
 /**********************************************************************
+ *              mark_contexts_for_moved_view
+ */
+static void mark_contexts_for_moved_view(macdrv_view view)
+{
+    struct wgl_context *context;
+
+    EnterCriticalSection(&context_section);
+    LIST_FOR_EACH_ENTRY(context, &context_list, struct wgl_context, entry)
+    {
+        if (context->draw_view == view)
+            InterlockedExchange(&context->view_moved, TRUE);
+    }
+    LeaveCriticalSection(&context_section);
+}
+
+
+/**********************************************************************
  *              set_gl_view_parent
  */
 void set_gl_view_parent(HWND hwnd, HWND parent)
@@ -1716,6 +1736,7 @@ void set_gl_view_parent(HWND hwnd, HWND parent)
         }
 
         macdrv_set_view_window_and_frame(data->gl_view, cocoa_window, cgrect_from_rect(data->gl_rect));
+        mark_contexts_for_moved_view(data->gl_view);
     }
 
     release_win_data(data);
@@ -1723,28 +1744,59 @@ void set_gl_view_parent(HWND hwnd, HWND parent)
 
 
 /**********************************************************************
+ *              sync_context_rect
+ */
+static BOOL sync_context_rect(struct wgl_context *context)
+{
+    BOOL ret = FALSE;
+    if (InterlockedCompareExchange(&context->view_moved, FALSE, TRUE))
+    {
+        struct macdrv_win_data *data = get_win_data(context->draw_hwnd);
+
+        if (data && data->gl_view && data->gl_view == context->draw_view &&
+            memcmp(&context->draw_rect, &data->gl_rect, sizeof(context->draw_rect)))
+        {
+            context->draw_rect = data->gl_rect;
+            ret = TRUE;
+        }
+        release_win_data(data);
+    }
+    return ret;
+}
+
+
+/**********************************************************************
  *              make_context_current
  */
 static void make_context_current(struct wgl_context *context, BOOL read)
 {
     macdrv_view view;
+    RECT view_rect;
     struct wgl_pbuffer *pbuffer;
 
     if (read)
     {
         view = context->read_view;
+        view_rect = context->read_rect;
         pbuffer = context->read_pbuffer;
     }
     else
     {
+        sync_context_rect(context);
+
         view = context->draw_view;
+        view_rect = context->draw_rect;
         pbuffer = context->draw_pbuffer;
     }
 
     if (view || !pbuffer)
-        macdrv_make_context_current(context->context, view);
+        macdrv_make_context_current(context->context, view, cgrect_from_rect(view_rect));
     else
     {
+        GLint enabled;
+
+        if (CGLIsEnabled(context->cglcontext, kCGLCESurfaceBackingSize, &enabled) == kCGLNoError && enabled)
+            CGLDisable(context->cglcontext, kCGLCESurfaceBackingSize);
         CGLSetPBuffer(context->cglcontext, pbuffer->pbuffer, pbuffer->face,
                       pbuffer->level, 0);
         CGLSetCurrentContext(context->cglcontext);
@@ -1753,6 +1805,16 @@ static void make_context_current(struct wgl_context *context, BOOL read)
 
 
 /**********************************************************************
+ *              sync_context
+ */
+static void sync_context(struct wgl_context *context)
+{
+    if (sync_context_rect(context))
+        make_context_current(context, FALSE);
+}
+
+
+/**********************************************************************
  *              set_swap_interval
  */
 static BOOL set_swap_interval(struct wgl_context *context, long interval)
@@ -2275,6 +2337,7 @@ static void macdrv_glFinish(void)
     struct wgl_context *context = NtCurrentTeb()->glContext;
 
     sync_swap_interval(context);
+    sync_context(context);
     pglFinish();
 }
 
@@ -2287,6 +2350,7 @@ static void macdrv_glFlush(void)
     struct wgl_context *context = NtCurrentTeb()->glContext;
 
     sync_swap_interval(context);
+    sync_context(context);
 
     if (skip_single_buffer_flushes)
     {
@@ -2367,6 +2431,7 @@ static void macdrv_glViewport(GLint x, GLint y, GLsizei width, GLsizei height)
 {
     struct wgl_context *context = NtCurrentTeb()->glContext;
 
+    sync_context(context);
     macdrv_update_opengl_context(context->context);
     pglViewport(x, y, width, height);
 }
@@ -3552,7 +3617,7 @@ static BOOL macdrv_wglMakeContextCurrentARB(HDC draw_hdc, HDC read_hdc, struct w
 
     if (!context)
     {
-        macdrv_make_context_current(NULL, NULL);
+        macdrv_make_context_current(NULL, NULL, CGRectNull);
         NtCurrentTeb()->glContext = NULL;
         return TRUE;
     }
@@ -3585,6 +3650,7 @@ static BOOL macdrv_wglMakeContextCurrentARB(HDC draw_hdc, HDC read_hdc, struct w
 
         context->draw_hwnd = hwnd;
         context->draw_view = data->gl_view;
+        context->draw_rect = data->gl_rect;
         context->draw_pbuffer = NULL;
         release_win_data(data);
     }
@@ -3631,7 +3697,10 @@ static BOOL macdrv_wglMakeContextCurrentARB(HDC draw_hdc, HDC read_hdc, struct w
             if ((data = get_win_data(hwnd)))
             {
                 if (data->gl_view != context->draw_view)
+                {
                     context->read_view = data->gl_view;
+                    context->read_rect = data->gl_rect;
+                }
                 release_win_data(data);
             }
         }
@@ -3643,8 +3712,9 @@ static BOOL macdrv_wglMakeContextCurrentARB(HDC draw_hdc, HDC read_hdc, struct w
         }
     }
 
-    TRACE("making context current with draw_view %p draw_pbuffer %p read_view %p read_pbuffer %p format %u\n",
-          context->draw_view, context->draw_pbuffer, context->read_view, context->read_pbuffer, context->format);
+    TRACE("making context current with draw_view %p %s draw_pbuffer %p read_view %p %s read_pbuffer %p format %u\n",
+          context->draw_view, wine_dbgstr_rect(&context->draw_rect), context->draw_pbuffer,
+          context->read_view, wine_dbgstr_rect(&context->read_rect), context->read_pbuffer, context->format);
 
     make_context_current(context, FALSE);
     context->has_been_current = TRUE;
@@ -4361,6 +4431,7 @@ void sync_gl_view(struct macdrv_win_data *data)
         TRACE("Setting GL view %p frame to %s\n", data->gl_view, wine_dbgstr_rect(&rect));
         macdrv_set_view_window_and_frame(data->gl_view, NULL, cgrect_from_rect(rect));
         data->gl_rect = rect;
+        mark_contexts_for_moved_view(data->gl_view);
     }
 }
 
@@ -4607,7 +4678,10 @@ static BOOL macdrv_wglSwapBuffers(HDC hdc)
           (context ? context->cglcontext : NULL));
 
     if (context)
+    {
         sync_swap_interval(context);
+        sync_context(context);
+    }
 
     if ((hwnd = WindowFromDC(hdc)))
     {
diff --git a/dlls/winemac.drv/surface.c b/dlls/winemac.drv/surface.c
index a79ec47..1230bbe 100644
--- a/dlls/winemac.drv/surface.c
+++ b/dlls/winemac.drv/surface.c
@@ -403,7 +403,7 @@ CGImageRef create_surface_image(void *window_surface, CGRect *rect, int copy_dat
         cgimage = CGImageCreate(CGRectGetWidth(visrect), CGRectGetHeight(visrect),
                                 8, 32, bytes_per_row, colorspace,
                                 alphaInfo | kCGBitmapByteOrder32Little,
-                                provider, NULL, FALSE, kCGRenderingIntentDefault);
+                                provider, NULL, retina_on, kCGRenderingIntentDefault);
         CGDataProviderRelease(provider);
         CGColorSpaceRelease(colorspace);
     }
-- 
2.6.0




More information about the wine-patches mailing list