1 //
2 // System.Drawing.carbonFunctions.cs
3 //
4 // Authors:
5 //      Geoff Norton (gnorton@customerdna.com>
6 //
7 // Copyright (C) 2007 Novell, Inc. (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 #undef DEBUG_CLIPPING
29 
30 using System.Collections;
31 using System.Reflection;
32 using System.Runtime.InteropServices;
33 using System.Security;
34 
35 namespace System.Drawing {
36 
37 	internal static class MacSupport {
38 		internal static Hashtable contextReference = new Hashtable ();
39 		internal static object lockobj = new object ();
40 
41 		internal static Delegate hwnd_delegate;
42 
43 #if DEBUG_CLIPPING
44 		internal static float red = 1.0f;
45 		internal static float green = 0.0f;
46 		internal static float blue = 0.0f;
47 		internal static int debug_threshold = 1;
48 #endif
49 
MacSupport()50 		static MacSupport () {
51 #if !NETSTANDARD1_6
52 			foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies ()) {
53 				if (String.Equals (asm.GetName ().Name, "System.Windows.Forms")) {
54 					Type driver_type = asm.GetType ("System.Windows.Forms.XplatUICarbon");
55 					if (driver_type != null) {
56 						hwnd_delegate = (Delegate) driver_type.GetTypeInfo() .GetField ("HwndDelegate", BindingFlags.NonPublic | BindingFlags.Static).GetValue (null);
57 					}
58 				}
59 			}
60 #endif
61 		}
62 
GetCGContextForNSView(IntPtr handle)63 		internal static CocoaContext GetCGContextForNSView (IntPtr handle) {
64 			IntPtr graphicsContext = objc_msgSend (objc_getClass ("NSGraphicsContext"), sel_registerName ("currentContext"));
65 			IntPtr ctx = objc_msgSend (graphicsContext, sel_registerName ("graphicsPort"));
66 			Rect bounds = new Rect ();
67 
68 			CGContextSaveGState (ctx);
69 
70 			objc_msgSend_stret (ref bounds, handle, sel_registerName ("bounds"));
71 
72 			var isFlipped = bool_objc_msgSend (handle, sel_registerName ("isFlipped"));
73 			if (isFlipped) {
74 				CGContextTranslateCTM (ctx, bounds.origin.x, bounds.size.height);
75 				CGContextScaleCTM (ctx,1.0f,-1.0f);
76 			}
77 
78 			return new CocoaContext (ctx, (int) bounds.size.width, (int) bounds.size.height);
79 		}
80 
GetCGContextForView(IntPtr handle)81 		internal static CarbonContext GetCGContextForView (IntPtr handle) {
82 			IntPtr context = IntPtr.Zero;
83 			IntPtr port = IntPtr.Zero;
84 			IntPtr window = IntPtr.Zero;
85 
86 			window = GetControlOwner (handle);
87 
88 			if (handle == IntPtr.Zero || window == IntPtr.Zero) {
89 				// FIXME: Can we actually get a CGContextRef for the desktop?  this makes context IntPtr.Zero
90 				port = GetQDGlobalsThePort ();
91 				CreateCGContextForPort (port, ref context);
92 
93 				Rect desktop_bounds = CGDisplayBounds (CGMainDisplayID ());
94 
95 				return new CarbonContext (port, context, (int)desktop_bounds.size.width, (int)desktop_bounds.size.height);
96 			}
97 
98 			QDRect window_bounds = new QDRect ();
99 			Rect view_bounds = new Rect ();
100 
101 			port = GetWindowPort (window);
102 
103 			context = GetContext (port);
104 
105 			GetWindowBounds (window, 32, ref window_bounds);
106 
107 			HIViewGetBounds (handle, ref view_bounds);
108 
109 			HIViewConvertRect (ref view_bounds, handle, IntPtr.Zero);
110 
111 			if (view_bounds.size.height < 0) view_bounds.size.height = 0;
112 			if (view_bounds.size.width < 0) view_bounds.size.width = 0;
113 
114 			CGContextTranslateCTM (context, view_bounds.origin.x, (window_bounds.bottom - window_bounds.top) - (view_bounds.origin.y + view_bounds.size.height));
115 
116 			// Create the original rect path and clip to it
117 			Rect rc_clip = new Rect (0, 0, view_bounds.size.width, view_bounds.size.height);
118 
119 			CGContextSaveGState (context);
120 
121 			Rectangle [] clip_rectangles = (Rectangle []) hwnd_delegate.DynamicInvoke (new object [] {handle});
122 			if (clip_rectangles != null && clip_rectangles.Length > 0) {
123 				int length = clip_rectangles.Length;
124 
125 				CGContextBeginPath (context);
126 				CGContextAddRect (context, rc_clip);
127 
128 				for (int i = 0; i < length; i++) {
129 					CGContextAddRect (context, new Rect (clip_rectangles [i].X, view_bounds.size.height - clip_rectangles [i].Y - clip_rectangles [i].Height, clip_rectangles [i].Width, clip_rectangles [i].Height));
130 				}
131 				CGContextClosePath (context);
132 				CGContextEOClip (context);
133 #if DEBUG_CLIPPING
134 				if (clip_rectangles.Length >= debug_threshold) {
135 					CGContextSetRGBFillColor (context, red, green, blue, 0.5f);
136 					CGContextFillRect (context, rc_clip);
137 					CGContextFlush (context);
138 					System.Threading.Thread.Sleep (500);
139 					if (red == 1.0f) { red = 0.0f; blue = 1.0f; }
140 					else if (blue == 1.0f) { blue = 0.0f; green = 1.0f; }
141 					else if (green == 1.0f) { green = 0.0f; red = 1.0f; }
142 				}
143 #endif
144 			} else {
145 				CGContextBeginPath (context);
146 				CGContextAddRect (context, rc_clip);
147 				CGContextClosePath (context);
148 				CGContextClip (context);
149 			}
150 
151 			return new CarbonContext (port, context, (int)view_bounds.size.width, (int)view_bounds.size.height);
152 		}
153 
GetContext(IntPtr port)154 		internal static IntPtr GetContext (IntPtr port) {
155 			IntPtr context = IntPtr.Zero;
156 
157 			lock (lockobj) {
158 #if FALSE
159 				if (contextReference [port] != null) {
160 					CreateCGContextForPort (port, ref context);
161 				} else {
162 					QDBeginCGContext (port, ref context);
163 					contextReference [port] = context;
164 				}
165 #else
166 				CreateCGContextForPort (port, ref context);
167 #endif
168 			}
169 
170 			return context;
171 		}
172 
ReleaseContext(IntPtr port, IntPtr context)173 		internal static void ReleaseContext (IntPtr port, IntPtr context) {
174 			CGContextRestoreGState (context);
175 
176 			lock (lockobj) {
177 #if FALSE
178 				if (contextReference [port] != null && context == (IntPtr) contextReference [port]) {
179 					QDEndCGContext (port, ref context);
180 					contextReference [port] = null;
181 				} else {
182 					CFRelease (context);
183 				}
184 #else
185 				CFRelease (context);
186 #endif
187 			}
188 		}
189 
190 		#region Cocoa Methods
191 		[DllImport("libobjc.dylib")]
objc_getClass(string className)192 		public static extern IntPtr objc_getClass(string className);
193 		[DllImport("libobjc.dylib")]
objc_msgSend(IntPtr basePtr, IntPtr selector, string argument)194 		public static extern IntPtr objc_msgSend(IntPtr basePtr, IntPtr selector, string argument);
195 		[DllImport("libobjc.dylib")]
objc_msgSend(IntPtr basePtr, IntPtr selector)196 		public static extern IntPtr objc_msgSend(IntPtr basePtr, IntPtr selector);
197 		[DllImport("libobjc.dylib")]
objc_msgSend_stret(ref Rect arect, IntPtr basePtr, IntPtr selector)198 		public static extern void objc_msgSend_stret(ref Rect arect, IntPtr basePtr, IntPtr selector);
199 		[DllImport ("libobjc.dylib", EntryPoint = "objc_msgSend")]
bool_objc_msgSend(IntPtr handle, IntPtr selector)200 		public static extern bool bool_objc_msgSend (IntPtr handle, IntPtr selector);
201 		[DllImport("libobjc.dylib")]
sel_registerName(string selectorName)202 		public static extern IntPtr sel_registerName(string selectorName);
203 		#endregion
204 
205 		[DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGMainDisplayID()206 		internal static extern IntPtr CGMainDisplayID ();
207 		[DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGDisplayBounds(IntPtr display)208 		internal static extern Rect CGDisplayBounds (IntPtr display);
209 
210 		[DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
HIViewGetBounds(IntPtr vHnd, ref Rect r)211 		internal static extern int HIViewGetBounds (IntPtr vHnd, ref Rect r);
212 		[DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
HIViewConvertRect(ref Rect r, IntPtr a, IntPtr b)213 		internal static extern int HIViewConvertRect (ref Rect r, IntPtr a, IntPtr b);
214 
215 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
GetControlOwner(IntPtr aView)216 		internal static extern IntPtr GetControlOwner (IntPtr aView);
217 
218 		[DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
GetWindowBounds(IntPtr wHnd, uint reg, ref QDRect rect)219 		internal static extern int GetWindowBounds (IntPtr wHnd, uint reg, ref QDRect rect);
220 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
GetWindowPort(IntPtr hWnd)221 		internal static extern IntPtr GetWindowPort (IntPtr hWnd);
222 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
GetQDGlobalsThePort()223 		internal static extern IntPtr GetQDGlobalsThePort ();
224 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CreateCGContextForPort(IntPtr port, ref IntPtr context)225 		internal static extern void CreateCGContextForPort (IntPtr port, ref IntPtr context);
226 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CFRelease(IntPtr context)227 		internal static extern void CFRelease (IntPtr context);
228 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
QDBeginCGContext(IntPtr port, ref IntPtr context)229 		internal static extern void QDBeginCGContext (IntPtr port, ref IntPtr context);
230 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
QDEndCGContext(IntPtr port, ref IntPtr context)231 		internal static extern void QDEndCGContext (IntPtr port, ref IntPtr context);
232 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGContextClipToRect(IntPtr context, Rect clip)233 		internal static extern int CGContextClipToRect (IntPtr context, Rect clip);
234 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGContextClipToRects(IntPtr context, Rect [] clip_rects, int count)235 		internal static extern int CGContextClipToRects (IntPtr context, Rect [] clip_rects, int count);
236 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGContextTranslateCTM(IntPtr context, float tx, float ty)237 		internal static extern void CGContextTranslateCTM (IntPtr context, float tx, float ty);
238 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGContextScaleCTM(IntPtr context, float x, float y)239 		internal static extern void CGContextScaleCTM (IntPtr context, float x, float y);
240 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGContextFlush(IntPtr context)241 		internal static extern void CGContextFlush (IntPtr context);
242 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGContextSynchronize(IntPtr context)243 		internal static extern void CGContextSynchronize (IntPtr context);
244 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGPathCreateMutable()245 		internal static extern IntPtr CGPathCreateMutable ();
246 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGPathAddRects(IntPtr path, IntPtr _void, Rect [] rects, int count)247 		internal static extern void CGPathAddRects (IntPtr path, IntPtr _void, Rect [] rects, int count);
248 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGPathAddRect(IntPtr path, IntPtr _void, Rect rect)249 		internal static extern void CGPathAddRect (IntPtr path, IntPtr _void, Rect rect);
250 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGContextAddRects(IntPtr context, Rect [] rects, int count)251 		internal static extern void CGContextAddRects (IntPtr context, Rect [] rects, int count);
252 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGContextAddRect(IntPtr context, Rect rect)253 		internal static extern void CGContextAddRect (IntPtr context, Rect rect);
254 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGContextBeginPath(IntPtr context)255 		internal static extern void CGContextBeginPath (IntPtr context);
256 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGContextClosePath(IntPtr context)257 		internal static extern void CGContextClosePath (IntPtr context);
258 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGContextAddPath(IntPtr context, IntPtr path)259 		internal static extern void CGContextAddPath (IntPtr context, IntPtr path);
260 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGContextClip(IntPtr context)261 		internal static extern void CGContextClip (IntPtr context);
262 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGContextEOClip(IntPtr context)263 		internal static extern void CGContextEOClip (IntPtr context);
264 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGContextEOFillPath(IntPtr context)265 		internal static extern void CGContextEOFillPath (IntPtr context);
266 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGContextSaveGState(IntPtr context)267 		internal static extern void CGContextSaveGState (IntPtr context);
268 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGContextRestoreGState(IntPtr context)269 		internal static extern void CGContextRestoreGState (IntPtr context);
270 
271 #if DEBUG_CLIPPING
272 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGContextSetRGBFillColor(IntPtr context, float red, float green, float blue, float alpha)273 		internal static extern void CGContextSetRGBFillColor (IntPtr context, float red, float green, float blue, float alpha);
274 		[DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
CGContextFillRect(IntPtr context, Rect rect)275 		internal static extern void CGContextFillRect (IntPtr context, Rect rect);
276 #endif
277 	}
278 
279 	internal struct CGSize {
280 		public float width;
281 		public float height;
282 	}
283 
284 	internal struct CGPoint {
285 		public float x;
286 		public float y;
287 	}
288 
289 	internal struct Rect {
RectSystem.Drawing.Rect290 		public Rect (float x, float y, float width, float height) {
291 			this.origin.x = x;
292 			this.origin.y = y;
293 			this.size.width = width;
294 			this.size.height = height;
295 		}
296 
297 		public CGPoint origin;
298 		public CGSize size;
299 	}
300 
301 	internal struct QDRect
302 	{
303 		public short top;
304 		public short left;
305 		public short bottom;
306 		public short right;
307 	}
308 
309 	internal struct CarbonContext : IMacContext
310 	{
311 		public IntPtr port;
312 		public IntPtr ctx;
313 		public int width;
314 		public int height;
315 
CarbonContextSystem.Drawing.CarbonContext316 		public CarbonContext (IntPtr port, IntPtr ctx, int width, int height)
317 		{
318 			this.port = port;
319 			this.ctx = ctx;
320 			this.width = width;
321 			this.height = height;
322 		}
323 
SynchronizeSystem.Drawing.CarbonContext324 		public void Synchronize ()
325 		{
326 			MacSupport.CGContextSynchronize (ctx);
327 		}
328 
ReleaseSystem.Drawing.CarbonContext329 		public void Release ()
330 		{
331 			MacSupport.ReleaseContext (port, ctx);
332 		}
333 	}
334 
335 	internal struct CocoaContext : IMacContext
336 	{
337 		public IntPtr ctx;
338 		public int width;
339 		public int height;
340 
CocoaContextSystem.Drawing.CocoaContext341 		public CocoaContext (IntPtr ctx, int width, int height)
342 		{
343 			this.ctx = ctx;
344 			this.width = width;
345 			this.height = height;
346 		}
347 
SynchronizeSystem.Drawing.CocoaContext348 		public void Synchronize ()
349 		{
350 			MacSupport.CGContextSynchronize (ctx);
351 		}
352 
ReleaseSystem.Drawing.CocoaContext353 		public void Release ()
354 		{
355 			MacSupport.CGContextRestoreGState(ctx);
356 		}
357 	}
358 
359 	internal interface IMacContext
360 	{
Synchronize()361 		void Synchronize ();
Release()362 		void Release ();
363 	}
364 }
365