Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TGCocoa.mm
Go to the documentation of this file.
1 // @(#)root/graf2d:$Id$
2 // Author: Timur Pocheptsov 22/11/2011
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2012, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 //#define NDEBUG
13 
14 #include "TGCocoa.h"
15 
16 // We want to pickup ROOT's glew and not the system OpenGL coming from:
17 // ROOTOpenGLView.h ->QuartzWindow.h->Cocoa.h
18 // Allowing TU's which include the system GL and then glew (from TGLIncludes)
19 // leads to gltypes.h redefinition errors.
20 #include "TGLIncludes.h"
21 
22 #include "ROOTOpenGLView.h"
23 #include "CocoaConstants.h"
24 #include "TMacOSXSystem.h"
25 #include "CocoaPrivate.h"
26 #include "QuartzWindow.h"
27 #include "QuartzPixmap.h"
28 #include "QuartzUtils.h"
29 #include "X11Drawable.h"
30 #include "QuartzText.h"
31 #include "CocoaUtils.h"
32 #include "MenuLoader.h"
33 #include "TVirtualGL.h"
34 #include "X11Events.h"
35 #include "X11Buffer.h"
36 #include "TGClient.h"
37 #include "TGWindow.h"
38 #include "TSystem.h"
39 #include "TGFrame.h"
40 #include "TGLIncludes.h"
41 #include "TError.h"
42 #include "TColor.h"
43 #include "TROOT.h"
44 #include "TEnv.h"
45 #include "TVirtualMutex.h"
46 
47 #include <ApplicationServices/ApplicationServices.h>
48 #include <OpenGL/OpenGL.h>
49 #include <Cocoa/Cocoa.h>
50 
51 #include <algorithm>
52 #include <stdexcept>
53 #include <cassert>
54 #include <cstring>
55 #include <cstddef>
56 #include <limits>
57 
58 
59 //Style notes: I'm using a lot of asserts to check pre-conditions - mainly function parameters.
60 //In asserts, expression always looks like 'p != 0' for "C++ pointer" (either object of built-in type
61 //or C++ class), and 'p != nil' for object from Objective-C. There is no difference, this is to make
62 //asserts more explicit. In conditional statement, it'll always be 'if (p)' or 'if (!p)' for both
63 //C++ and Objective-C pointers/code.
64 
65 //I never use const qualifier for pointers to Objective-C objects since they are useless:
66 //there are no cv-qualified methods (member-functions in C++) in Objective-C, and I do not use
67 //'->' operator to access instance variables (data-members in C++) of Objective-C's object.
68 //I also declare pointer as a const, if it's const:
69 //NSWindow * const topLevelWindow = ... (and note, not pointer to const - no use with Obj-C).
70 
71 //Asserts on drawables ids usually only check, that it's not a 'root' window id (unless operation
72 //is permitted on a 'root' window):
73 //a) assert(!fPimpl->IsRootWindow(windowID)) and later I also check that windowID != 0 (kNone).
74 //b) assert(drawableID > fPimpl->GetRootWindowID()) so drawableID can not be kNone and
75 // can not be a 'root' window.
76 
77 //ROOT window has id 1. So if id > 1 (id > fPimpl->GetRootWindowID())
78 //id is considered as valid (if it's out of range and > maximum valid id, this will be
79 //caught by CocoaPrivate.
80 
81 namespace Details = ROOT::MacOSX::Details;
82 namespace Util = ROOT::MacOSX::Util;
83 namespace X11 = ROOT::MacOSX::X11;
84 namespace Quartz = ROOT::Quartz;
85 namespace OpenGL = ROOT::MacOSX::OpenGL;
86 
87 namespace {
88 
89 #pragma mark - Display configuration management.
90 
91 //______________________________________________________________________________
92 void DisplayReconfigurationCallback(CGDirectDisplayID /*display*/, CGDisplayChangeSummaryFlags flags, void * /*userInfo*/)
93 {
94  if (flags & kCGDisplayBeginConfigurationFlag)
95  return;
96 
97  if (flags & kCGDisplayDesktopShapeChangedFlag) {
98  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 && "DisplayReconfigurationCallback, gVirtualX"
99  " is either null or has a wrong type");
100  TGCocoa * const gCocoa = static_cast<TGCocoa *>(gVirtualX);
101  gCocoa->ReconfigureDisplay();
102  }
103 }
104 
105 #pragma mark - Aux. functions called from GUI-rendering part.
106 
107 //______________________________________________________________________________
108 void SetStrokeForegroundColorFromX11Context(CGContextRef ctx, const GCValues_t &gcVals)
109 {
110  assert(ctx != 0 && "SetStrokeForegroundColorFromX11Context, parameter 'ctx' is null");
111 
112  CGFloat rgb[3] = {};
113  if (gcVals.fMask & kGCForeground)
114  X11::PixelToRGB(gcVals.fForeground, rgb);
115  else
116  ::Warning("SetStrokeForegroundColorFromX11Context",
117  "x11 context does not have line color information");
118 
119  CGContextSetRGBStrokeColor(ctx, rgb[0], rgb[1], rgb[2], 1.);
120 }
121 
122 //______________________________________________________________________________
123 void SetStrokeDashFromX11Context(CGContextRef ctx, const GCValues_t &gcVals)
124 {
125  //Set line dash pattern (X11's LineOnOffDash line style).
126  assert(ctx != 0 && "SetStrokeDashFromX11Context, ctx parameter is null");
127 
128  SetStrokeForegroundColorFromX11Context(ctx, gcVals);
129 
130  static const std::size_t maxLength = sizeof gcVals.fDashes / sizeof gcVals.fDashes[0];
131  assert(maxLength >= std::size_t(gcVals.fDashLen) &&
132  "SetStrokeDashFromX11Context, x11 context has bad dash length > sizeof(fDashes)");
133 
134  CGFloat dashes[maxLength] = {};
135  for (Int_t i = 0; i < gcVals.fDashLen; ++i)
136  dashes[i] = gcVals.fDashes[i];
137 
138  CGContextSetLineDash(ctx, gcVals.fDashOffset, dashes, gcVals.fDashLen);
139 }
140 
141 //______________________________________________________________________________
142 void SetStrokeDoubleDashFromX11Context(CGContextRef /*ctx*/, const GCValues_t & /*gcVals*/)
143 {
144  //assert(ctx != 0 && "SetStrokeDoubleDashFromX11Context, ctx parameter is null");
145  ::Warning("SetStrokeDoubleDashFromX11Context", "Not implemented yet, kick tpochep!");
146 }
147 
148 //______________________________________________________________________________
149 void SetStrokeParametersFromX11Context(CGContextRef ctx, const GCValues_t &gcVals)
150 {
151  //Set line width and color from GCValues_t object.
152  //(GUI rendering).
153  assert(ctx != 0 && "SetStrokeParametersFromX11Context, parameter 'ctx' is null");
154 
155  const Mask_t mask = gcVals.fMask;
156  if ((mask & kGCLineWidth) && gcVals.fLineWidth > 1)
157  CGContextSetLineWidth(ctx, gcVals.fLineWidth);
158  else
159  CGContextSetLineWidth(ctx, 1.);
160 
161  CGContextSetLineDash(ctx, 0., 0, 0);
162 
163  if (mask & kGCLineStyle) {
164  if (gcVals.fLineStyle == kLineSolid)
165  SetStrokeForegroundColorFromX11Context(ctx, gcVals);
166  else if (gcVals.fLineStyle == kLineOnOffDash)
167  SetStrokeDashFromX11Context(ctx, gcVals);
168  else if (gcVals.fLineStyle == kLineDoubleDash)
169  SetStrokeDoubleDashFromX11Context(ctx ,gcVals);
170  else {
171  ::Warning("SetStrokeParametersFromX11Context", "line style bit is set,"
172  " but line style is unknown");
173  SetStrokeForegroundColorFromX11Context(ctx, gcVals);
174  }
175  } else
176  SetStrokeForegroundColorFromX11Context(ctx, gcVals);
177 }
178 
179 //______________________________________________________________________________
180 void SetFilledAreaColorFromX11Context(CGContextRef ctx, const GCValues_t &gcVals)
181 {
182  //Set fill color from "foreground" pixel color.
183  //(GUI rendering).
184  assert(ctx != 0 && "SetFilledAreaColorFromX11Context, parameter 'ctx' is null");
185 
186  CGFloat rgb[3] = {};
187  if (gcVals.fMask & kGCForeground)
188  X11::PixelToRGB(gcVals.fForeground, rgb);
189  else
190  ::Warning("SetFilledAreaColorFromX11Context", "no fill color found in x11 context");
191 
192  CGContextSetRGBFillColor(ctx, rgb[0], rgb[1], rgb[2], 1.);
193 }
194 
195 struct PatternContext {
196  Mask_t fMask;
197  Int_t fFillStyle;
198  ULong_t fForeground;
199  ULong_t fBackground;
200  NSObject<X11Drawable> *fImage;//Either stipple or tile image.
201  CGSize fPhase;
202 };
203 
204 
205 //______________________________________________________________________________
206 bool HasFillTiledStyle(Mask_t mask, Int_t fillStyle)
207 {
208  return (mask & kGCFillStyle) && (fillStyle == kFillTiled);
209 }
210 
211 //______________________________________________________________________________
212 bool HasFillTiledStyle(const GCValues_t &gcVals)
213 {
214  return HasFillTiledStyle(gcVals.fMask, gcVals.fFillStyle);
215 }
216 
217 //______________________________________________________________________________
218 bool HasFillStippledStyle(Mask_t mask, Int_t fillStyle)
219 {
220  return (mask & kGCFillStyle) && (fillStyle == kFillStippled);
221 }
222 
223 //______________________________________________________________________________
224 bool HasFillStippledStyle(const GCValues_t &gcVals)
225 {
226  return HasFillStippledStyle(gcVals.fMask, gcVals.fFillStyle);
227 }
228 
229 //______________________________________________________________________________
230 bool HasFillOpaqueStippledStyle(Mask_t mask, Int_t fillStyle)
231 {
232  return (mask & kGCFillStyle) && (fillStyle == kFillOpaqueStippled);
233 }
234 
235 //______________________________________________________________________________
236 bool HasFillOpaqueStippledStyle(const GCValues_t &gcVals)
237 {
238  return HasFillOpaqueStippledStyle(gcVals.fMask, gcVals.fFillStyle);
239 }
240 
241 //______________________________________________________________________________
242 void DrawTile(NSObject<X11Drawable> *patternImage, CGContextRef ctx)
243 {
244  assert(patternImage != nil && "DrawTile, parameter 'patternImage' is nil");
245  assert(ctx != 0 && "DrawTile, ctx parameter is null");
246 
247  const CGRect patternRect = CGRectMake(0, 0, patternImage.fWidth, patternImage.fHeight);
248  if ([patternImage isKindOfClass : [QuartzImage class]]) {
249  CGContextDrawImage(ctx, patternRect, ((QuartzImage *)patternImage).fImage);
250  } else if ([patternImage isKindOfClass : [QuartzPixmap class]]){
251  const Util::CFScopeGuard<CGImageRef> imageFromPixmap([((QuartzPixmap *)patternImage) createImageFromPixmap]);
252  assert(imageFromPixmap.Get() != 0 && "DrawTile, createImageFromPixmap failed");
253  CGContextDrawImage(ctx, patternRect, imageFromPixmap.Get());
254  } else
255  assert(0 && "DrawTile, pattern is neither a QuartzImage, nor a QuartzPixmap");
256 }
257 
258 //______________________________________________________________________________
259 void DrawPattern(void *info, CGContextRef ctx)
260 {
261  //Pattern callback, either use foreground (and background, if any)
262  //color and stipple mask to draw a pattern, or use pixmap
263  //as a pattern image.
264  //(GUI rendering).
265  assert(info != 0 && "DrawPattern, parameter 'info' is null");
266  assert(ctx != 0 && "DrawPattern, parameter 'ctx' is null");
267 
268  const PatternContext * const patternContext = (PatternContext *)info;
269  const Mask_t mask = patternContext->fMask;
270  const Int_t fillStyle = patternContext->fFillStyle;
271 
272  NSObject<X11Drawable> * const patternImage = patternContext->fImage;
273  assert(patternImage != nil && "DrawPattern, pattern (stipple) image is nil");
274  const CGRect patternRect = CGRectMake(0, 0, patternImage.fWidth, patternImage.fHeight);
275 
276  if (HasFillTiledStyle(mask, fillStyle)) {
277  DrawTile(patternImage, ctx);
278  } else if (HasFillStippledStyle(mask, fillStyle) || HasFillOpaqueStippledStyle(mask, fillStyle)) {
279  assert([patternImage isKindOfClass : [QuartzImage class]] &&
280  "DrawPattern, stipple must be a QuartzImage object");
281  QuartzImage * const image = (QuartzImage *)patternImage;
282  assert(image.fIsStippleMask == YES && "DrawPattern, image is not a stipple mask");
283 
284  CGFloat rgb[3] = {};
285 
286  if (HasFillOpaqueStippledStyle(mask,fillStyle)) {
287  //Fill background first.
288  assert((mask & kGCBackground) &&
289  "DrawPattern, fill style is FillOpaqueStippled, but background color is not set in a context");
290  X11::PixelToRGB(patternContext->fBackground, rgb);
291  CGContextSetRGBFillColor(ctx, rgb[0], rgb[1], rgb[2], 1.);
292  CGContextFillRect(ctx, patternRect);
293  }
294 
295  //Fill rectangle with foreground colour, using stipple mask.
296  assert((mask & kGCForeground) && "DrawPattern, foreground color is not set");
297  X11::PixelToRGB(patternContext->fForeground, rgb);
298  CGContextSetRGBFillColor(ctx, rgb[0], rgb[1], rgb[2], 1.);
299  CGContextClipToMask(ctx, patternRect, image.fImage);
300  CGContextFillRect(ctx, patternRect);
301  } else {
302  //This can be a window background pixmap
303  DrawTile(patternImage, ctx);
304  }
305 }
306 
307 //______________________________________________________________________________
308 void SetFillPattern(CGContextRef ctx, const PatternContext *patternContext)
309 {
310  //Create CGPatternRef to fill GUI elements with pattern.
311  //Pattern is a QuartzImage object, it can be either a mask,
312  //or pattern image itself.
313  //(GUI-rendering).
314  assert(ctx != 0 && "SetFillPattern, parameter 'ctx' is null");
315  assert(patternContext != 0 && "SetFillPattern, parameter 'patternContext' is null");
316  assert(patternContext->fImage != nil && "SetFillPattern, pattern image is nil");
317 
318  const Util::CFScopeGuard<CGColorSpaceRef> patternColorSpace(CGColorSpaceCreatePattern(0));
319  CGContextSetFillColorSpace(ctx, patternColorSpace.Get());
320 
321  CGPatternCallbacks callbacks = {};
322  callbacks.drawPattern = DrawPattern;
323  const CGRect patternRect = CGRectMake(0, 0, patternContext->fImage.fWidth, patternContext->fImage.fHeight);
324  const Util::CFScopeGuard<CGPatternRef> pattern(CGPatternCreate((void *)patternContext, patternRect, CGAffineTransformIdentity,
325  patternContext->fImage.fWidth, patternContext->fImage.fHeight,
326  kCGPatternTilingNoDistortion, true, &callbacks));
327  const CGFloat alpha = 1.;
328  CGContextSetFillPattern(ctx, pattern.Get(), &alpha);
329  CGContextSetPatternPhase(ctx, patternContext->fPhase);
330 }
331 
332 //______________________________________________________________________________
333 bool ParentRendersToChild(NSView<X11Window> *child)
334 {
335  assert(child != nil && "ParentRendersToChild, parameter 'child' is nil");
336 
337  //Adovo poluchaetsia, tashhem-ta! ;)
338  return (X11::ViewIsTextViewFrame(child, true) || X11::ViewIsHtmlViewFrame(child, true)) && !child.fContext &&
339  child.fMapState == kIsViewable && child.fParentView.fContext &&
340  !child.fIsOverlapped;
341 }
342 
343 class ViewFixer final {
344 public:
345  ViewFixer(QuartzView *&viewToFix, Drawable_t &widToFix)
346  {
347  if (ParentRendersToChild(viewToFix) && [viewToFix.fParentView isKindOfClass:[QuartzView class]]) {
348  const auto origin = viewToFix.frame.origin;
349  viewToFix = viewToFix.fParentView;
350  widToFix = viewToFix.fID;
351  if ((context = viewToFix.fContext)) {
352  CGContextSaveGState(context);
353  CGContextTranslateCTM(context, origin.x, origin.y);
354  }
355  }
356  }
357  ~ViewFixer()
358  {
359  if (context)
360  CGContextRestoreGState(context);
361  }
362  ViewFixer(const ViewFixer &rhs) = delete;
363  ViewFixer &operator = (const ViewFixer &) = delete;
364 
365 private:
366  CGContextRef context = nullptr;
367 };
368 
369 //______________________________________________________________________________
370 bool IsNonPrintableAsciiCharacter(UniChar c)
371 {
372  if (c == 9 || (c >= 32 && c < 127))
373  return false;
374 
375  return true;
376 }
377 
378 //______________________________________________________________________________
379 void FixAscii(std::vector<UniChar> &text)
380 {
381  //GUI text is essentially ASCII. Our GUI
382  //calculates text metrix 'per-symbol', this means,
383  //it never asks about 'Text' metrics, but 'T', 'e', 'x', 't'.
384  //Obviously, text does not fit any widget because of
385  //this and I have to place all glyphs manually.
386  //And here I have another problem from our GUI - it
387  //can easily feed TGCocoa with non-printable symbols
388  //(this is a bug). Obviously, I do not have glyphs for, say, form feed
389  //or 'data link escape'. So I have to fix ascii text before
390  //manual glyph rendering: DLE symbol - replaced by space (this
391  //is done in TGText, but due to a bug it fails to replace them all)
392  //Other non-printable symbols simply removed (and thus ignored).
393 
394  //Replace remaining ^P symbols with whitespaces, I have not idea why
395  //TGTextView replaces only part of them and not all of them.
396  std::replace(text.begin(), text.end(), UniChar(16), UniChar(' '));
397 
398  //Now, remove remaining non-printable characters (no glyphs exist for them).
399  text.erase(std::remove_if(text.begin(), text.end(), IsNonPrintableAsciiCharacter), text.end());
400 }
401 
402 }
403 
404 ClassImp(TGCocoa)
405 
406 Atom_t TGCocoa::fgDeleteWindowAtom = 0;
407 
408 //______________________________________________________________________________
409 TGCocoa::TGCocoa()
410  : fSelectedDrawable(0),
411  fCocoaDraw(0),
412  fDrawMode(kCopy),
413  fDirectDraw(false),
414  fForegroundProcess(false),
415  fSetApp(true),
416  fDisplayShapeChanged(true)
417 {
418  assert(dynamic_cast<TMacOSXSystem *>(gSystem) != nullptr &&
419  "TGCocoa, gSystem is eihter null or has a wrong type");
420  TMacOSXSystem * const system = (TMacOSXSystem *)gSystem;
421 
422  if (!system->CocoaInitialized())
423  system->InitializeCocoa();
424 
425  fPimpl.reset(new Details::CocoaPrivate);
426 
427  X11::InitWithPredefinedAtoms(fNameToAtom, fAtomToName);
428  fgDeleteWindowAtom = FindAtom("WM_DELETE_WINDOW", true);
429 
430  CGDisplayRegisterReconfigurationCallback (DisplayReconfigurationCallback, 0);
431 }
432 
433 //______________________________________________________________________________
434 TGCocoa::TGCocoa(const char *name, const char *title)
435  : TVirtualX(name, title),
436  fSelectedDrawable(0),
437  fCocoaDraw(0),
438  fDrawMode(kCopy),
439  fDirectDraw(false),
440  fForegroundProcess(false),
441  fSetApp(true),
442  fDisplayShapeChanged(true)
443 {
444  assert(dynamic_cast<TMacOSXSystem *>(gSystem) != nullptr &&
445  "TGCocoa, gSystem is eihter null or has a wrong type");
446  TMacOSXSystem * const system = (TMacOSXSystem *)gSystem;
447 
448  if (!system->CocoaInitialized())
449  system->InitializeCocoa();
450 
451  fPimpl.reset(new Details::CocoaPrivate);
452 
453  X11::InitWithPredefinedAtoms(fNameToAtom, fAtomToName);
454  fgDeleteWindowAtom = FindAtom("WM_DELETE_WINDOW", true);
455 
456  CGDisplayRegisterReconfigurationCallback (DisplayReconfigurationCallback, 0);
457 }
458 
459 //______________________________________________________________________________
460 TGCocoa::~TGCocoa()
461 {
462  //
463  CGDisplayRemoveReconfigurationCallback (DisplayReconfigurationCallback, 0);
464 }
465 
466 //General part (empty, since it's not an X server.
467 
468 //______________________________________________________________________________
469 Bool_t TGCocoa::Init(void * /*display*/)
470 {
471  //Nothing to initialize here, return true to make
472  //a caller happy.
473  return kTRUE;
474 }
475 
476 
477 //______________________________________________________________________________
478 Int_t TGCocoa::OpenDisplay(const char * /*dpyName*/)
479 {
480  //Noop.
481  return 0;
482 }
483 
484 //______________________________________________________________________________
485 const char *TGCocoa::DisplayName(const char *)
486 {
487  //Noop.
488  return "dummy";
489 }
490 
491 //______________________________________________________________________________
492 Int_t TGCocoa::SupportsExtension(const char *) const
493 {
494  //No, thank you, I'm not supporting any of X11 extensions!
495  return -1;
496 }
497 
498 //______________________________________________________________________________
499 void TGCocoa::CloseDisplay()
500 {
501  //Noop.
502 }
503 
504 //______________________________________________________________________________
505 Display_t TGCocoa::GetDisplay() const
506 {
507  //Noop.
508  return 0;
509 }
510 
511 //______________________________________________________________________________
512 Visual_t TGCocoa::GetVisual() const
513 {
514  //Noop.
515  return 0;
516 }
517 
518 //______________________________________________________________________________
519 Int_t TGCocoa::GetScreen() const
520 {
521  //Noop.
522  return 0;
523 }
524 
525 //______________________________________________________________________________
526 UInt_t TGCocoa::ScreenWidthMM() const
527 {
528  //Comment from TVirtualX:
529  // Returns the width of the screen in millimeters.
530  //End of comment.
531 
532  return CGDisplayScreenSize(CGMainDisplayID()).width;
533 }
534 
535 //______________________________________________________________________________
536 Int_t TGCocoa::GetDepth() const
537 {
538  //Comment from TVirtualX:
539  // Returns depth of screen (number of bit planes).
540  // Equivalent to GetPlanes().
541  //End of comment.
542 
543  NSArray * const screens = [NSScreen screens];
544  assert(screens != nil && "screens array is nil");
545 
546  NSScreen * const mainScreen = [screens objectAtIndex : 0];
547  assert(mainScreen != nil && "screen with index 0 is nil");
548 
549  return NSBitsPerPixelFromDepth([mainScreen depth]);
550 }
551 
552 //______________________________________________________________________________
553 void TGCocoa::Update(Int_t mode)
554 {
555  R__LOCKGUARD(gROOTMutex);
556 
557  if (mode == 2) {
558  assert(gClient != 0 && "Update, gClient is null");
559  gClient->DoRedraw();//Call DoRedraw for all widgets, who need to be updated.
560  } else if (mode > 0) {
561  //Execute buffered commands.
562  fPimpl->fX11CommandBuffer.Flush(fPimpl.get());
563  }
564 
565  if (fDirectDraw && mode != 2)
566  fPimpl->fX11CommandBuffer.FlushXOROps(fPimpl.get());
567 }
568 
569 //______________________________________________________________________________
570 void TGCocoa::ReconfigureDisplay()
571 {
572  fDisplayShapeChanged = true;
573 }
574 
575 //______________________________________________________________________________
576 X11::Rectangle TGCocoa::GetDisplayGeometry()const
577 {
578  if (fDisplayShapeChanged) {
579  NSArray * const screens = [NSScreen screens];
580  assert(screens != nil && screens.count != 0 && "GetDisplayGeometry, no screens found");
581 
582  NSRect frame = [(NSScreen *)[screens objectAtIndex : 0] frame];
583  CGFloat xMin = frame.origin.x, xMax = xMin + frame.size.width;
584  CGFloat yMin = frame.origin.y, yMax = yMin + frame.size.height;
585 
586  for (NSUInteger i = 1, e = screens.count; i < e; ++i) {
587  frame = [(NSScreen *)[screens objectAtIndex : i] frame];
588  xMin = std::min(xMin, frame.origin.x);
589  xMax = std::max(xMax, frame.origin.x + frame.size.width);
590  yMin = std::min(yMin, frame.origin.y);
591  yMax = std::max(yMax, frame.origin.y + frame.size.height);
592  }
593 
594  fDisplayRect.fX = int(xMin);
595  fDisplayRect.fY = int(yMin);
596  fDisplayRect.fWidth = unsigned(xMax - xMin);
597  fDisplayRect.fHeight = unsigned(yMax - yMin);
598 
599  fDisplayShapeChanged = false;
600  }
601 
602  return fDisplayRect;
603 }
604 
605 #pragma mark - Window management part.
606 
607 //______________________________________________________________________________
608 Window_t TGCocoa::GetDefaultRootWindow() const
609 {
610  //Index, fixed and used only by 'root' window.
611  return fPimpl->GetRootWindowID();
612 }
613 
614 //______________________________________________________________________________
615 Int_t TGCocoa::InitWindow(ULong_t parentID)
616 {
617  //InitWindow is a bad name, since this function
618  //creates a window, but this name comes from the TVirtualX interface.
619  //Actually, there is no special need in this function,
620  //it's a kind of simplified CreateWindow (with only
621  //one parameter). This function is called by TRootCanvas,
622  //to create a special window inside TGCanvas (thus parentID must be a valid window ID).
623  //TGX11/TGWin32 have internal array of such special windows,
624  //they return index into this array, instead of drawable's ids.
625  //I simply re-use CreateWindow and return a drawable's id.
626 
627  assert(parentID != 0 && "InitWindow, parameter 'parentID' is 0");
628 
629  //Use parent's attributes (as it's done in TGX11).
630  WindowAttributes_t attr = {};
631  if (fPimpl->IsRootWindow(parentID))
632  ROOT::MacOSX::X11::GetRootWindowAttributes(&attr);
633  else
634  [fPimpl->GetWindow(parentID) getAttributes : &attr];
635 
636  return CreateWindow(parentID, 0, 0, attr.fWidth, attr.fHeight, 0, attr.fDepth, attr.fClass, 0, 0, 0);
637 }
638 
639 //______________________________________________________________________________
640 Window_t TGCocoa::GetWindowID(Int_t windowID)
641 {
642  //In case of TGX11/TGWin32, there is a mixture of
643  //casted X11 ids (Window_t) and indices in some internal array, which
644  //contains such an id. On Mac I always have indices. Yes, I'm smart.
645  return windowID;
646 }
647 
648 //______________________________________________________________________________
649 void TGCocoa::SelectWindow(Int_t windowID)
650 {
651  //This function can be called from pad/canvas, both for window and for pixmap.
652  fSelectedDrawable = windowID;
653 }
654 
655 //______________________________________________________________________________
656 void TGCocoa::ClearWindow()
657 {
658  //Clear the selected drawable OR pixmap (the name - from TVirtualX interface - is bad).
659  assert(fSelectedDrawable > fPimpl->GetRootWindowID() &&
660  "ClearWindow, fSelectedDrawable is invalid");
661 
662  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(fSelectedDrawable);
663  if (drawable.fIsPixmap) {
664  //Pixmaps are white by default.
665  //This is bad - we can not have transparent sub-pads (in TCanvas)
666  //because of this. But there is no way how gVirtualX can
667  //obtain real pad's color and check for its transparency.
668  CGContextRef pixmapCtx = drawable.fContext;
669  assert(pixmapCtx != 0 && "ClearWindow, pixmap's context is null");
670  //const Quartz::CGStateGuard ctxGuard(pixmapCtx);
671  //CGContextSetRGBFillColor(pixmapCtx, 1., 1., 1., 1.);
672  //CGContextFillRect(pixmapCtx, CGRectMake(0, 0, drawable.fWidth, drawable.fHeight));
673  //Now we really clear!
674  CGContextClearRect(pixmapCtx, CGRectMake(0, 0, drawable.fWidth, drawable.fHeight));
675  } else {
676  //For a window ClearArea with w == 0 and h == 0 means the whole window.
677  ClearArea(fSelectedDrawable, 0, 0, 0, 0);
678  }
679 }
680 
681 //______________________________________________________________________________
682 void TGCocoa::GetGeometry(Int_t windowID, Int_t & x, Int_t &y, UInt_t &w, UInt_t &h)
683 {
684  //In TGX11, GetGeometry works with special windows, created by InitWindow
685  //(thus this function is called from TCanvas/TGCanvas/TRootCanvas).
686 
687  //IMPORTANT: this function also translates x and y
688  //from parent's coordinates into screen coordinates - so, again, name "GetGeometry"
689  //from the TVirtualX interface is bad and misleading.
690 
691  if (windowID < 0 || fPimpl->IsRootWindow(windowID)) {
692  //Comment in TVirtualX suggests, that wid can be < 0.
693  //This will be a screen's geometry.
694  WindowAttributes_t attr = {};
695  ROOT::MacOSX::X11::GetRootWindowAttributes(&attr);
696  x = attr.fX;
697  y = attr.fY;
698  w = attr.fWidth;
699  h = attr.fHeight;
700  } else {
701  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(windowID);
702  x = drawable.fX;
703  y = drawable.fY;
704  w = drawable.fWidth;
705  h = drawable.fHeight;
706 
707  if (!drawable.fIsPixmap) {
708  NSObject<X11Window> * const window = (NSObject<X11Window> *)drawable;
709  NSPoint srcPoint = {};
710  srcPoint.x = x;
711  srcPoint.y = y;
712  NSView<X11Window> * const view = window.fContentView.fParentView ? window.fContentView.fParentView : window.fContentView;
713  //View parameter for TranslateToScreen call must
714  //be parent view, since x and y are in parent's
715  //coordinate system.
716  const NSPoint dstPoint = X11::TranslateToScreen(view, srcPoint);
717  x = dstPoint.x;
718  y = dstPoint.y;
719  }
720  }
721 }
722 
723 //______________________________________________________________________________
724 void TGCocoa::MoveWindow(Int_t windowID, Int_t x, Int_t y)
725 {
726  //windowID is either kNone or a valid window id.
727  //x and y are coordinates of a top-left corner relative to the parent's coordinate system.
728 
729  assert(!fPimpl->IsRootWindow(windowID) && "MoveWindow, called for root window");
730 
731  if (!windowID)//From TGX11.
732  return;
733 
734  [fPimpl->GetWindow(windowID) setX : x Y : y];
735 }
736 
737 //______________________________________________________________________________
738 void TGCocoa::RescaleWindow(Int_t /*wid*/, UInt_t /*w*/, UInt_t /*h*/)
739 {
740  //This function is for TRootCanvas and related stuff, never gets
741  //called/used from/by any our GUI class.
742  //Noop.
743 }
744 
745 //______________________________________________________________________________
746 void TGCocoa::ResizeWindow(Int_t windowID)
747 {
748  //This function does not resize window (it was done already by layout management?),
749  //it resizes "back buffer" if any.
750 
751  if (!windowID)//From TGX11.
752  return;
753 
754  assert(!fPimpl->IsRootWindow(windowID) &&
755  "ResizeWindow, parameter 'windowID' is a root window's id");
756 
757  const Util::AutoreleasePool pool;
758 
759  NSObject<X11Window> * const window = fPimpl->GetWindow(windowID);
760  if (window.fBackBuffer) {
761  const Drawable_t currentDrawable = fSelectedDrawable;
762  fSelectedDrawable = windowID;
763  SetDoubleBufferON();
764  fSelectedDrawable = currentDrawable;
765  }
766 }
767 
768 //______________________________________________________________________________
769 void TGCocoa::UpdateWindow(Int_t /*mode*/)
770 {
771  //This function is used by TCanvas/TPad:
772  //draw "back buffer" image into the view.
773  //fContentView (destination) MUST be a QuartzView.
774 
775  //Basic es-guarantee: X11Buffer::AddUpdateWindow modifies vector with commands,
776  //if the following call to TGCocoa::Update will produce an exception dusing X11Buffer::Flush,
777  //initial state of X11Buffer can not be restored, but it still must be in some valid state.
778 
779  assert(fSelectedDrawable > fPimpl->GetRootWindowID() &&
780  "UpdateWindow, fSelectedDrawable is not a valid window id");
781 
782  //Have no idea, why this can happen with ROOT - done by TGDNDManager :(
783  if (fPimpl->GetDrawable(fSelectedDrawable).fIsPixmap == YES)
784  return;
785 
786  NSObject<X11Window> * const window = fPimpl->GetWindow(fSelectedDrawable);
787 
788  if (QuartzPixmap * const pixmap = window.fBackBuffer) {
789  assert([window.fContentView isKindOfClass : [QuartzView class]] && "UpdateWindow, content view is not a QuartzView");
790  QuartzView *dstView = (QuartzView *)window.fContentView;
791 
792  if (dstView.fIsOverlapped)
793  return;
794 
795  if (dstView.fContext) {
796  //We can draw directly.
797  const X11::Rectangle copyArea(0, 0, pixmap.fWidth, pixmap.fHeight);
798  [dstView copy : pixmap area : copyArea withMask : nil clipOrigin : X11::Point() toPoint : X11::Point()];
799  } else {
800  //Have to wait.
801  fPimpl->fX11CommandBuffer.AddUpdateWindow(dstView);
802  Update(1);
803  }
804  }
805 }
806 
807 //______________________________________________________________________________
808 Window_t TGCocoa::GetCurrentWindow() const
809 {
810  //Window selected by SelectWindow.
811  return fSelectedDrawable;
812 }
813 
814 //______________________________________________________________________________
815 void TGCocoa::CloseWindow()
816 {
817  //Deletes selected window.
818 }
819 
820 //______________________________________________________________________________
821 Int_t TGCocoa::AddWindow(ULong_t /*qwid*/, UInt_t /*w*/, UInt_t /*h*/)
822 {
823  //Should register a window created by Qt as a ROOT window,
824  //but since Qt-ROOT does not work on Mac and will never work,
825  //especially with version 4.8 - this implementation will always
826  //be empty.
827  return 0;
828 }
829 
830 //______________________________________________________________________________
831 void TGCocoa::RemoveWindow(ULong_t /*qwid*/)
832 {
833  //Remove window, created by Qt.
834 }
835 
836 //______________________________________________________________________________
837 Window_t TGCocoa::CreateWindow(Window_t parentID, Int_t x, Int_t y, UInt_t w, UInt_t h, UInt_t border, Int_t depth,
838  UInt_t clss, void *visual, SetWindowAttributes_t *attr, UInt_t wtype)
839 {
840  //Create new window (top-level == QuartzWindow + QuartzView, or child == QuartzView)
841 
842  //Strong es-guarantee - exception can be only during registration, class state will remain
843  //unchanged, no leaks (scope guards).
844 
845  const Util::AutoreleasePool pool;
846 
847  if (fPimpl->IsRootWindow(parentID)) {//parent == root window.
848  //Can throw:
849  QuartzWindow * const newWindow = X11::CreateTopLevelWindow(x, y, w, h, border,
850  depth, clss, visual, attr, wtype);
851  //Something like unique_ptr would perfectly solve the problem with raw pointer + a separate
852  //guard for this pointer, but it requires move semantics.
853  const Util::NSScopeGuard<QuartzWindow> winGuard(newWindow);
854  const Window_t result = fPimpl->RegisterDrawable(newWindow);//Can throw.
855  newWindow.fID = result;
856  [newWindow setAcceptsMouseMovedEvents : YES];
857 
858  return result;
859  } else {
860  NSObject<X11Window> * const parentWin = fPimpl->GetWindow(parentID);
861  //OpenGL view can not have children.
862  assert([parentWin.fContentView isKindOfClass : [QuartzView class]] &&
863  "CreateWindow, parent view must be QuartzView");
864 
865  //Can throw:
866  QuartzView * const childView = X11::CreateChildView((QuartzView *)parentWin.fContentView,
867  x, y, w, h, border, depth, clss, visual, attr, wtype);
868  const Util::NSScopeGuard<QuartzView> viewGuard(childView);
869  const Window_t result = fPimpl->RegisterDrawable(childView);//Can throw.
870  childView.fID = result;
871  [parentWin addChild : childView];
872 
873  return result;
874  }
875 }
876 
877 //______________________________________________________________________________
878 void TGCocoa::DestroyWindow(Window_t wid)
879 {
880  //The XDestroyWindow function destroys the specified window as well as all of its subwindows
881  //and causes the X server to generate a DestroyNotify event for each window. The window
882  //should never be referenced again. If the window specified by the w argument is mapped,
883  //it is unmapped automatically. The ordering of the
884  //DestroyNotify events is such that for any given window being destroyed, DestroyNotify is generated
885  //on any inferiors of the window before being generated on the window itself. The ordering
886  //among siblings and across subhierarchies is not otherwise constrained.
887  //If the window you specified is a root window, no windows are destroyed. Destroying a mapped window
888  //will generate Expose events on other windows that were obscured by the window being destroyed.
889 
890  //No-throw guarantee???
891 
892  //I have NO idea why ROOT's GUI calls DestroyWindow with illegal
893  //window id, but it does.
894 
895  if (!wid)
896  return;
897 
898  if (fPimpl->IsRootWindow(wid))
899  return;
900 
901  BOOL needFocusChange = NO;
902 
903  {//Block to force autoreleasepool to drain.
904  const Util::AutoreleasePool pool;
905 
906  fPimpl->fX11EventTranslator.CheckUnmappedView(wid);
907 
908  assert(fPimpl->GetDrawable(wid).fIsPixmap == NO &&
909  "DestroyWindow, can not be called for QuartzPixmap or QuartzImage object");
910 
911  NSObject<X11Window> * const window = fPimpl->GetWindow(wid);
912  if (fPimpl->fX11CommandBuffer.BufferSize())
913  fPimpl->fX11CommandBuffer.RemoveOperationsForDrawable(wid);
914 
915  //TEST: "fix" a keyboard focus.
916  if ((needFocusChange = window == window.fQuartzWindow && window.fQuartzWindow.fHasFocus))
917  window.fHasFocus = NO;//If any.
918 
919  DestroySubwindows(wid);
920  if (window.fEventMask & kStructureNotifyMask)
921  fPimpl->fX11EventTranslator.GenerateDestroyNotify(wid);
922 
923  //Interrupt modal loop (TGClient::WaitFor).
924  if (gClient->GetWaitForEvent() == kDestroyNotify && wid == gClient->GetWaitForWindow())
925  gClient->SetWaitForWindow(kNone);
926 
927  fPimpl->DeleteDrawable(wid);
928  }
929 
930  //"Fix" a keyboard focus.
931  if (needFocusChange)
932  X11::WindowLostFocus(wid);
933 }
934 
935 //______________________________________________________________________________
936 void TGCocoa::DestroySubwindows(Window_t wid)
937 {
938  // The DestroySubwindows function destroys all inferior windows of the
939  // specified window, in bottom-to-top stacking order.
940 
941  //No-throw guarantee??
942 
943  //From TGX11:
944  if (!wid)
945  return;
946 
947  if (fPimpl->IsRootWindow(wid))
948  return;
949 
950  const Util::AutoreleasePool pool;
951 
952  assert(fPimpl->GetDrawable(wid).fIsPixmap == NO &&
953  "DestroySubwindows, can not be called for QuartzPixmap or QuartzImage object");
954 
955  NSObject<X11Window> *window = fPimpl->GetWindow(wid);
956 
957  //I can not iterate on subviews array directly, since it'll be modified
958  //during this iteration - create a copy (and it'll also increase references,
959  //which will be decreased by guard's dtor).
960  const Util::NSScopeGuard<NSArray> children([[window.fContentView subviews] copy]);
961 
962  for (NSView<X11Window> *child in children.Get())
963  DestroyWindow(child.fID);
964 }
965 
966 //______________________________________________________________________________
967 void TGCocoa::GetWindowAttributes(Window_t wid, WindowAttributes_t &attr)
968 {
969  //No-throw guarantee.
970 
971  if (!wid)//X11's None?
972  return;
973 
974  if (fPimpl->IsRootWindow(wid))
975  ROOT::MacOSX::X11::GetRootWindowAttributes(&attr);
976  else
977  [fPimpl->GetWindow(wid) getAttributes : &attr];
978 }
979 
980 //______________________________________________________________________________
981 void TGCocoa::ChangeWindowAttributes(Window_t wid, SetWindowAttributes_t *attr)
982 {
983  //No-throw guarantee.
984 
985  if (!wid)//From TGX11
986  return;
987 
988  const Util::AutoreleasePool pool;
989 
990  assert(!fPimpl->IsRootWindow(wid) && "ChangeWindowAttributes, called for root window");
991  assert(attr != 0 && "ChangeWindowAttributes, parameter 'attr' is null");
992 
993  [fPimpl->GetWindow(wid) setAttributes : attr];
994 }
995 
996 //______________________________________________________________________________
997 void TGCocoa::SelectInput(Window_t windowID, UInt_t eventMask)
998 {
999  //No-throw guarantee.
1000 
1001  // Defines which input events the window is interested in. By default
1002  // events are propageted up the window stack. This mask can also be
1003  // set at window creation time via the SetWindowAttributes_t::fEventMask
1004  // attribute.
1005 
1006  //TGuiBldDragManager selects input on a 'root' window.
1007  //TGWin32 has a check on windowID == 0.
1008  if (windowID <= fPimpl->GetRootWindowID())
1009  return;
1010 
1011  NSObject<X11Window> * const window = fPimpl->GetWindow(windowID);
1012  //XSelectInput overrides a previous mask.
1013  window.fEventMask = eventMask;
1014 }
1015 
1016 //______________________________________________________________________________
1017 void TGCocoa::ReparentChild(Window_t wid, Window_t pid, Int_t x, Int_t y)
1018 {
1019  //Reparent view.
1020  using namespace Details;
1021 
1022  assert(!fPimpl->IsRootWindow(wid) && "ReparentChild, can not re-parent root window");
1023 
1024  const Util::AutoreleasePool pool;
1025 
1026  NSView<X11Window> * const view = fPimpl->GetWindow(wid).fContentView;
1027  if (fPimpl->IsRootWindow(pid)) {
1028  //Make a top-level view from a child view.
1029  [view retain];
1030  [view removeFromSuperview];
1031  view.fParentView = nil;
1032 
1033  NSRect frame = view.frame;
1034  frame.origin = NSPoint();
1035 
1036  NSUInteger styleMask = kClosableWindowMask | kMiniaturizableWindowMask | kResizableWindowMask;
1037  if (!view.fOverrideRedirect)
1038  styleMask |= kTitledWindowMask;
1039 
1040  QuartzWindow * const newTopLevel = [[QuartzWindow alloc] initWithContentRect : frame
1041  styleMask : styleMask
1042  backing : NSBackingStoreBuffered
1043  defer : NO];
1044  [view setX : x Y : y];
1045  [newTopLevel addChild : view];
1046 
1047  fPimpl->ReplaceDrawable(wid, newTopLevel);
1048 
1049  [view release];
1050  [newTopLevel release];
1051  } else {
1052  [view retain];
1053  [view removeFromSuperview];
1054  //
1055  NSObject<X11Window> * const newParent = fPimpl->GetWindow(pid);
1056  assert(newParent.fIsPixmap == NO && "ReparentChild, pixmap can not be a new parent");
1057  [view setX : x Y : y];
1058  [newParent addChild : view];//It'll also update view's level, no need to call updateLevel.
1059  [view release];
1060  }
1061 }
1062 
1063 //______________________________________________________________________________
1064 void TGCocoa::ReparentTopLevel(Window_t wid, Window_t pid, Int_t x, Int_t y)
1065 {
1066  //Reparent top-level window.
1067  //I have to delete QuartzWindow here and place in its slot content view +
1068  //reparent this view into pid.
1069  if (fPimpl->IsRootWindow(pid))//Nothing to do, wid is already a top-level window.
1070  return;
1071 
1072  const Util::AutoreleasePool pool;
1073 
1074  NSView<X11Window> * const contentView = fPimpl->GetWindow(wid).fContentView;
1075  QuartzWindow * const topLevel = (QuartzWindow *)[contentView window];
1076  [contentView retain];
1077  [contentView removeFromSuperview];
1078  [topLevel setContentView : nil];
1079  fPimpl->ReplaceDrawable(wid, contentView);
1080  [contentView setX : x Y : y];
1081  [fPimpl->GetWindow(pid) addChild : contentView];//Will also replace view's level.
1082  [contentView release];
1083 }
1084 
1085 //______________________________________________________________________________
1086 void TGCocoa::ReparentWindow(Window_t wid, Window_t pid, Int_t x, Int_t y)
1087 {
1088  //Change window's parent (possibly creating new top-level window or destroying top-level window).
1089 
1090  if (!wid) //From TGX11.
1091  return;
1092 
1093  assert(!fPimpl->IsRootWindow(wid) && "ReparentWindow, can not re-parent root window");
1094 
1095  NSView<X11Window> * const view = fPimpl->GetWindow(wid).fContentView;
1096  if (view.fParentView)
1097  ReparentChild(wid, pid, x, y);
1098  else
1099  //wid is a top-level window (or content view of such a window).
1100  ReparentTopLevel(wid, pid, x, y);
1101 }
1102 
1103 //______________________________________________________________________________
1104 void TGCocoa::MapWindow(Window_t wid)
1105 {
1106  // Maps the window "wid" and all of its subwindows that have had map
1107  // requests. This function has no effect if the window is already mapped.
1108 
1109  assert(!fPimpl->IsRootWindow(wid) && "MapWindow, called for root window");
1110 
1111  const Util::AutoreleasePool pool;
1112 
1113  if (MakeProcessForeground())
1114  [fPimpl->GetWindow(wid) mapWindow];
1115 
1116  if (fSetApp) {
1117  SetApplicationIcon();
1118  Details::PopulateMainMenu();
1119  fSetApp = false;
1120  }
1121 }
1122 
1123 //______________________________________________________________________________
1124 void TGCocoa::MapSubwindows(Window_t wid)
1125 {
1126  // Maps all subwindows for the specified window "wid" in top-to-bottom
1127  // stacking order.
1128 
1129  assert(!fPimpl->IsRootWindow(wid) && "MapSubwindows, called for 'root' window");
1130 
1131  const Util::AutoreleasePool pool;
1132 
1133  if (MakeProcessForeground())
1134  [fPimpl->GetWindow(wid) mapSubwindows];
1135 }
1136 
1137 //______________________________________________________________________________
1138 void TGCocoa::MapRaised(Window_t wid)
1139 {
1140  // Maps the window "wid" and all of its subwindows that have had map
1141  // requests on the screen and put this window on the top of of the
1142  // stack of all windows.
1143 
1144  assert(!fPimpl->IsRootWindow(wid) && "MapRaised, called for root window");
1145 
1146  const Util::AutoreleasePool pool;
1147 
1148  if (MakeProcessForeground())
1149  [fPimpl->GetWindow(wid) mapRaised];
1150 
1151  if (fSetApp) {
1152  SetApplicationIcon();
1153  Details::PopulateMainMenu();
1154  fSetApp = false;
1155  }
1156 }
1157 
1158 //______________________________________________________________________________
1159 void TGCocoa::UnmapWindow(Window_t wid)
1160 {
1161  // Unmaps the specified window "wid". If the specified window is already
1162  // unmapped, this function has no effect. Any child window will no longer
1163  // be visible (but they are still mapped) until another map call is made
1164  // on the parent.
1165  assert(!fPimpl->IsRootWindow(wid) && "UnmapWindow, called for root window");
1166 
1167  const Util::AutoreleasePool pool;
1168 
1169  //If this window is a grab window or a parent of a grab window.
1170  fPimpl->fX11EventTranslator.CheckUnmappedView(wid);
1171 
1172  NSObject<X11Window> * const win = fPimpl->GetWindow(wid);
1173  [win unmapWindow];
1174 
1175  if (win == win.fQuartzWindow && win.fQuartzWindow.fHasFocus)
1176  X11::WindowLostFocus(win.fID);
1177 
1178  win.fHasFocus = NO;
1179 
1180  //if (window.fEventMask & kStructureNotifyMask)
1181  // fPimpl->fX11EventTranslator.GenerateUnmapNotify(wid);
1182 
1183  //Interrupt modal loop (TGClient::WaitForUnmap).
1184  if (gClient->GetWaitForEvent() == kUnmapNotify && gClient->GetWaitForWindow() == wid)
1185  gClient->SetWaitForWindow(kNone);
1186 }
1187 
1188 //______________________________________________________________________________
1189 void TGCocoa::RaiseWindow(Window_t wid)
1190 {
1191  // Raises the specified window to the top of the stack so that no
1192  // sibling window obscures it.
1193 
1194  if (!wid)//From TGX11.
1195  return;
1196 
1197  assert(!fPimpl->IsRootWindow(wid) && "RaiseWindow, called for root window");
1198 
1199  if (!fPimpl->GetWindow(wid).fParentView)
1200  return;
1201 
1202  [fPimpl->GetWindow(wid) raiseWindow];
1203 }
1204 
1205 //______________________________________________________________________________
1206 void TGCocoa::LowerWindow(Window_t wid)
1207 {
1208  // Lowers the specified window "wid" to the bottom of the stack so
1209  // that it does not obscure any sibling windows.
1210 
1211  if (!wid)//From TGX11.
1212  return;
1213 
1214  assert(!fPimpl->IsRootWindow(wid) && "LowerWindow, called for root window");
1215 
1216  if (!fPimpl->GetWindow(wid).fParentView)
1217  return;
1218 
1219  [fPimpl->GetWindow(wid) lowerWindow];
1220 }
1221 
1222 //______________________________________________________________________________
1223 void TGCocoa::MoveWindow(Window_t wid, Int_t x, Int_t y)
1224 {
1225  // Moves the specified window to the specified x and y coordinates.
1226  // It does not change the window's size, raise the window, or change
1227  // the mapping state of the window.
1228  //
1229  // x, y - coordinates, which define the new position of the window
1230  // relative to its parent.
1231 
1232  if (!wid)//From TGX11.
1233  return;
1234 
1235  assert(!fPimpl->IsRootWindow(wid) && "MoveWindow, called for root window");
1236  const Util::AutoreleasePool pool;
1237  [fPimpl->GetWindow(wid) setX : x Y : y];
1238 }
1239 
1240 //______________________________________________________________________________
1241 void TGCocoa::MoveResizeWindow(Window_t wid, Int_t x, Int_t y, UInt_t w, UInt_t h)
1242 {
1243  // Changes the size and location of the specified window "wid" without
1244  // raising it.
1245  //
1246  // x, y - coordinates, which define the new position of the window
1247  // relative to its parent.
1248  // w, h - the width and height, which define the interior size of
1249  // the window
1250 
1251  if (!wid)//From TGX11.
1252  return;
1253 
1254  assert(!fPimpl->IsRootWindow(wid) && "MoveResizeWindow, called for 'root' window");
1255 
1256  const Util::AutoreleasePool pool;
1257  [fPimpl->GetWindow(wid) setX : x Y : y width : w height : h];
1258 }
1259 
1260 //______________________________________________________________________________
1261 void TGCocoa::ResizeWindow(Window_t wid, UInt_t w, UInt_t h)
1262 {
1263  if (!wid)//From TGX11.
1264  return;
1265 
1266  assert(!fPimpl->IsRootWindow(wid) && "ResizeWindow, called for 'root' window");
1267 
1268  const Util::AutoreleasePool pool;
1269 
1270  //We can have this unfortunately.
1271  const UInt_t siMax = std::numeric_limits<Int_t>::max();
1272  if (w > siMax || h > siMax)
1273  return;
1274 
1275  NSSize newSize = {};
1276  newSize.width = w;
1277  newSize.height = h;
1278 
1279  [fPimpl->GetWindow(wid) setDrawableSize : newSize];
1280 }
1281 
1282 //______________________________________________________________________________
1283 void TGCocoa::IconifyWindow(Window_t wid)
1284 {
1285  // Iconifies the window "wid".
1286  if (!wid)
1287  return;
1288 
1289  assert(!fPimpl->IsRootWindow(wid) && "IconifyWindow, can not iconify the root window");
1290  assert(fPimpl->GetWindow(wid).fIsPixmap == NO && "IconifyWindow, invalid window id");
1291 
1292  NSObject<X11Window> * const win = fPimpl->GetWindow(wid);
1293  assert(win.fQuartzWindow == win && "IconifyWindow, can be called only for a top level window");
1294 
1295  fPimpl->fX11EventTranslator.CheckUnmappedView(wid);
1296 
1297  NSObject<X11Window> * const window = fPimpl->GetWindow(wid);
1298  if (fPimpl->fX11CommandBuffer.BufferSize())
1299  fPimpl->fX11CommandBuffer.RemoveOperationsForDrawable(wid);
1300 
1301  if (window.fQuartzWindow.fHasFocus) {
1302  X11::WindowLostFocus(win.fID);
1303  window.fQuartzWindow.fHasFocus = NO;
1304  }
1305 
1306  [win.fQuartzWindow miniaturize : win.fQuartzWindow];
1307 }
1308 
1309 //______________________________________________________________________________
1310 void TGCocoa::TranslateCoordinates(Window_t srcWin, Window_t dstWin, Int_t srcX, Int_t srcY, Int_t &dstX, Int_t &dstY, Window_t &child)
1311 {
1312  // Translates coordinates in one window to the coordinate space of another
1313  // window. It takes the "src_x" and "src_y" coordinates relative to the
1314  // source window's origin and returns these coordinates to "dest_x" and
1315  // "dest_y" relative to the destination window's origin.
1316 
1317  // child - returns the child of "dest" if the coordinates
1318  // are contained in a mapped child of the destination
1319  // window; otherwise, child is set to 0
1320  child = 0;
1321  if (!srcWin || !dstWin)//This is from TGX11, looks like this can happen.
1322  return;
1323 
1324  const bool srcIsRoot = fPimpl->IsRootWindow(srcWin);
1325  const bool dstIsRoot = fPimpl->IsRootWindow(dstWin);
1326 
1327  if (srcIsRoot && dstIsRoot) {
1328  //This can happen with ROOT's GUI. Set dstX/Y equal to srcX/Y.
1329  //From man for XTranslateCoordinates it's not clear, what should be in child.
1330  dstX = srcX;
1331  dstY = srcY;
1332 
1333  if (QuartzWindow * const qw = X11::FindWindowInPoint(srcX, srcY))
1334  child = qw.fID;
1335 
1336  return;
1337  }
1338 
1339  NSPoint srcPoint = {};
1340  srcPoint.x = srcX;
1341  srcPoint.y = srcY;
1342 
1343  NSPoint dstPoint = {};
1344 
1345 
1346  if (dstIsRoot) {
1347  NSView<X11Window> * const srcView = fPimpl->GetWindow(srcWin).fContentView;
1348  dstPoint = X11::TranslateToScreen(srcView, srcPoint);
1349  } else if (srcIsRoot) {
1350  NSView<X11Window> * const dstView = fPimpl->GetWindow(dstWin).fContentView;
1351  dstPoint = X11::TranslateFromScreen(srcPoint, dstView);
1352 
1353  if ([dstView superview]) {
1354  //hitTest requires a point in a superview's coordinate system.
1355  //Even contentView of QuartzWindow has a superview (NSThemeFrame),
1356  //so this should always work.
1357  dstPoint = [[dstView superview] convertPoint : dstPoint fromView : dstView];
1358  if (NSView<X11Window> * const view = (NSView<X11Window> *)[dstView hitTest : dstPoint]) {
1359  if (view != dstView && view.fMapState == kIsViewable)
1360  child = view.fID;
1361  }
1362  }
1363  } else {
1364  NSView<X11Window> * const srcView = fPimpl->GetWindow(srcWin).fContentView;
1365  NSView<X11Window> * const dstView = fPimpl->GetWindow(dstWin).fContentView;
1366 
1367  dstPoint = X11::TranslateCoordinates(srcView, dstView, srcPoint);
1368  if ([dstView superview]) {
1369  //hitTest requires a point in a view's superview coordinate system.
1370  //Even contentView of QuartzWindow has a superview (NSThemeFrame),
1371  //so this should always work.
1372  const NSPoint pt = [[dstView superview] convertPoint : dstPoint fromView : dstView];
1373  if (NSView<X11Window> * const view = (NSView<X11Window> *)[dstView hitTest : pt]) {
1374  if (view != dstView && view.fMapState == kIsViewable)
1375  child = view.fID;
1376  }
1377  }
1378  }
1379 
1380  dstX = dstPoint.x;
1381  dstY = dstPoint.y;
1382 }
1383 
1384 //______________________________________________________________________________
1385 void TGCocoa::GetWindowSize(Drawable_t wid, Int_t &x, Int_t &y, UInt_t &w, UInt_t &h)
1386 {
1387  // Returns the location and the size of window "wid"
1388  //
1389  // x, y - coordinates of the upper-left outer corner relative to the
1390  // parent window's origin
1391  // w, h - the size of the window, not including the border.
1392 
1393  //From GX11Gui.cxx:
1394  if (!wid)
1395  return;
1396 
1397  if (fPimpl->IsRootWindow(wid)) {
1398  WindowAttributes_t attr = {};
1399  ROOT::MacOSX::X11::GetRootWindowAttributes(&attr);
1400  x = attr.fX;
1401  y = attr.fY;
1402  w = attr.fWidth;
1403  h = attr.fHeight;
1404  } else {
1405  NSObject<X11Drawable> *window = fPimpl->GetDrawable(wid);
1406  //ROOT can ask window size for ... non-window drawable.
1407  if (!window.fIsPixmap) {
1408  x = window.fX;
1409  y = window.fY;
1410  } else {
1411  x = 0;
1412  y = 0;
1413  }
1414 
1415  w = window.fWidth;
1416  h = window.fHeight;
1417  }
1418 }
1419 
1420 //______________________________________________________________________________
1421 void TGCocoa::SetWindowBackground(Window_t wid, ULong_t color)
1422 {
1423  //From TGX11:
1424  if (!wid)
1425  return;
1426 
1427  assert(!fPimpl->IsRootWindow(wid) && "SetWindowBackground, can not set color for root window");
1428 
1429  fPimpl->GetWindow(wid).fBackgroundPixel = color;
1430 }
1431 
1432 //______________________________________________________________________________
1433 void TGCocoa::SetWindowBackgroundPixmap(Window_t windowID, Pixmap_t pixmapID)
1434 {
1435  // Sets the background pixmap of the window "wid" to the specified
1436  // pixmap "pxm".
1437 
1438  //From TGX11/TGWin32:
1439  if (!windowID)
1440  return;
1441 
1442  assert(!fPimpl->IsRootWindow(windowID) &&
1443  "SetWindowBackgroundPixmap, can not set background for a root window");
1444  assert(fPimpl->GetDrawable(windowID).fIsPixmap == NO &&
1445  "SetWindowBackgroundPixmap, invalid window id");
1446 
1447  NSObject<X11Window> * const window = fPimpl->GetWindow(windowID);
1448  if (pixmapID == kNone) {
1449  window.fBackgroundPixmap = nil;
1450  return;
1451  }
1452 
1453  assert(pixmapID > fPimpl->GetRootWindowID() &&
1454  "SetWindowBackgroundPixmap, parameter 'pixmapID' is not a valid pixmap id");
1455  assert(fPimpl->GetDrawable(pixmapID).fIsPixmap == YES &&
1456  "SetWindowBackgroundPixmap, bad drawable");
1457 
1458  NSObject<X11Drawable> * const pixmapOrImage = fPimpl->GetDrawable(pixmapID);
1459  //X11 doc says, that pixmap can be freed immediately after call
1460  //XSetWindowBackgroundPixmap, so I have to copy a pixmap.
1461  Util::NSScopeGuard<QuartzImage> backgroundImage;
1462 
1463  if ([pixmapOrImage isKindOfClass : [QuartzPixmap class]]) {
1464  backgroundImage.Reset([[QuartzImage alloc] initFromPixmap : (QuartzPixmap *)pixmapOrImage]);
1465  if (backgroundImage.Get())
1466  window.fBackgroundPixmap = backgroundImage.Get();//the window is retaining the image.
1467  } else {
1468  backgroundImage.Reset([[QuartzImage alloc] initFromImage : (QuartzImage *)pixmapOrImage]);
1469  if (backgroundImage.Get())
1470  window.fBackgroundPixmap = backgroundImage.Get();//the window is retaining the image.
1471  }
1472 
1473  if (!backgroundImage.Get())
1474  //Detailed error message was issued by QuartzImage at this point.
1475  Error("SetWindowBackgroundPixmap", "QuartzImage initialization failed");
1476 }
1477 
1478 //______________________________________________________________________________
1479 Window_t TGCocoa::GetParent(Window_t windowID) const
1480 {
1481  // Returns the parent of the window "windowID".
1482 
1483  //0 or root (checked in TGX11):
1484  if (windowID <= fPimpl->GetRootWindowID())
1485  return windowID;
1486 
1487  NSView<X11Window> *view = fPimpl->GetWindow(windowID).fContentView;
1488  return view.fParentView ? view.fParentView.fID : fPimpl->GetRootWindowID();
1489 }
1490 
1491 //______________________________________________________________________________
1492 void TGCocoa::SetWindowName(Window_t wid, char *name)
1493 {
1494  if (!wid || !name)//From TGX11.
1495  return;
1496 
1497  const Util::AutoreleasePool pool;
1498 
1499  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(wid);
1500 
1501  if ([(NSObject *)drawable isKindOfClass : [NSWindow class]]) {
1502  NSString * const windowTitle = [NSString stringWithCString : name encoding : NSASCIIStringEncoding];
1503  [(NSWindow *)drawable setTitle : windowTitle];
1504  }
1505 }
1506 
1507 //______________________________________________________________________________
1508 void TGCocoa::SetIconName(Window_t /*wid*/, char * /*name*/)
1509 {
1510  //Noop.
1511 }
1512 
1513 //______________________________________________________________________________
1514 void TGCocoa::SetIconPixmap(Window_t /*wid*/, Pixmap_t /*pix*/)
1515 {
1516  //Noop.
1517 }
1518 
1519 //______________________________________________________________________________
1520 void TGCocoa::SetClassHints(Window_t /*wid*/, char * /*className*/, char * /*resourceName*/)
1521 {
1522  //Noop.
1523 }
1524 
1525 //______________________________________________________________________________
1526 void TGCocoa::ShapeCombineMask(Window_t windowID, Int_t /*x*/, Int_t /*y*/, Pixmap_t pixmapID)
1527 {
1528  //Comment from TVirtualX:
1529  // The Nonrectangular Window Shape Extension adds nonrectangular
1530  // windows to the System.
1531  // This allows for making shaped (partially transparent) windows
1532 
1533  assert(!fPimpl->IsRootWindow(windowID) &&
1534  "ShapeCombineMask, windowID parameter is a 'root' window");
1535  assert(fPimpl->GetDrawable(windowID).fIsPixmap == NO &&
1536  "ShapeCombineMask, windowID parameter is a bad window id");
1537  assert([fPimpl->GetDrawable(pixmapID) isKindOfClass : [QuartzImage class]] &&
1538  "ShapeCombineMask, pixmapID parameter must point to QuartzImage object");
1539 
1540  if (fPimpl->GetWindow(windowID).fContentView.fParentView)
1541  return;
1542 
1543  QuartzImage * const srcImage = (QuartzImage *)fPimpl->GetDrawable(pixmapID);
1544  assert(srcImage.fIsStippleMask == YES && "ShapeCombineMask, source image is not a stipple mask");
1545 
1546  // There is some kind of problems with shape masks and
1547  // flipped views, I have to do an image flip here.
1548  const Util::NSScopeGuard<QuartzImage> image([[QuartzImage alloc] initFromImageFlipped : srcImage]);
1549  if (image.Get()) {
1550  QuartzWindow * const qw = fPimpl->GetWindow(windowID).fQuartzWindow;
1551  qw.fShapeCombineMask = image.Get();
1552  [qw setOpaque : NO];
1553  [qw setBackgroundColor : [NSColor clearColor]];
1554  }
1555 }
1556 
1557 #pragma mark - "Window manager hints" set of functions.
1558 
1559 //______________________________________________________________________________
1560 void TGCocoa::SetMWMHints(Window_t wid, UInt_t value, UInt_t funcs, UInt_t /*input*/)
1561 {
1562  // Sets decoration style.
1563  using namespace Details;
1564 
1565  assert(!fPimpl->IsRootWindow(wid) && "SetMWMHints, called for 'root' window");
1566 
1567  QuartzWindow * const qw = fPimpl->GetWindow(wid).fQuartzWindow;
1568  NSUInteger newMask = 0;
1569 
1570  if ([qw styleMask] & kTitledWindowMask) {//Do not modify this.
1571  newMask |= kTitledWindowMask;
1572  newMask |= kClosableWindowMask;
1573  }
1574 
1575  if (value & kMWMFuncAll) {
1576  newMask |= kMiniaturizableWindowMask | kResizableWindowMask;
1577  } else {
1578  if (value & kMWMDecorMinimize)
1579  newMask |= kMiniaturizableWindowMask;
1580  if (funcs & kMWMFuncResize)
1581  newMask |= kResizableWindowMask;
1582  }
1583 
1584  [qw setStyleMask : newMask];
1585 
1586  if (funcs & kMWMDecorAll) {
1587  if (!qw.fMainWindow) {//Do not touch buttons for transient window.
1588  [[qw standardWindowButton : NSWindowZoomButton] setEnabled : YES];
1589  [[qw standardWindowButton : NSWindowMiniaturizeButton] setEnabled : YES];
1590  }
1591  } else {
1592  if (!qw.fMainWindow) {//Do not touch transient window's titlebar.
1593  [[qw standardWindowButton : NSWindowZoomButton] setEnabled : funcs & kMWMDecorMaximize];
1594  [[qw standardWindowButton : NSWindowMiniaturizeButton] setEnabled : funcs & kMWMDecorMinimize];
1595  }
1596  }
1597 }
1598 
1599 //______________________________________________________________________________
1600 void TGCocoa::SetWMPosition(Window_t /*wid*/, Int_t /*x*/, Int_t /*y*/)
1601 {
1602  //Noop.
1603 }
1604 
1605 //______________________________________________________________________________
1606 void TGCocoa::SetWMSize(Window_t /*wid*/, UInt_t /*w*/, UInt_t /*h*/)
1607 {
1608  //Noop.
1609 }
1610 
1611 //______________________________________________________________________________
1612 void TGCocoa::SetWMSizeHints(Window_t wid, UInt_t wMin, UInt_t hMin, UInt_t wMax, UInt_t hMax, UInt_t /*wInc*/, UInt_t /*hInc*/)
1613 {
1614  using namespace Details;
1615 
1616  assert(!fPimpl->IsRootWindow(wid) && "SetWMSizeHints, called for root window");
1617 
1618  const NSUInteger styleMask = kTitledWindowMask | kClosableWindowMask | kMiniaturizableWindowMask | kResizableWindowMask;
1619  const NSRect minRect = [NSWindow frameRectForContentRect : NSMakeRect(0., 0., wMin, hMin) styleMask : styleMask];
1620  const NSRect maxRect = [NSWindow frameRectForContentRect : NSMakeRect(0., 0., wMax, hMax) styleMask : styleMask];
1621 
1622  QuartzWindow * const qw = fPimpl->GetWindow(wid).fQuartzWindow;
1623  [qw setMinSize : minRect.size];
1624  [qw setMaxSize : maxRect.size];
1625 }
1626 
1627 //______________________________________________________________________________
1628 void TGCocoa::SetWMState(Window_t /*wid*/, EInitialState /*state*/)
1629 {
1630  //Noop.
1631 }
1632 
1633 //______________________________________________________________________________
1634 void TGCocoa::SetWMTransientHint(Window_t wid, Window_t mainWid)
1635 {
1636  //Comment from TVirtualX:
1637  // Tells window manager that the window "wid" is a transient window
1638  // of the window "main_id". A window manager may decide not to decorate
1639  // a transient window or may treat it differently in other ways.
1640  //End of TVirtualX's comment.
1641 
1642  //TGTransientFrame uses this hint to attach a window to some "main" window,
1643  //so that transient window is alway above the main window. This is used for
1644  //dialogs and dockable panels.
1645  assert(wid > fPimpl->GetRootWindowID() && "SetWMTransientHint, wid parameter is not a valid window id");
1646 
1647  if (fPimpl->IsRootWindow(mainWid))
1648  return;
1649 
1650  QuartzWindow * const mainWindow = fPimpl->GetWindow(mainWid).fQuartzWindow;
1651 
1652  if (![mainWindow isVisible])
1653  return;
1654 
1655  QuartzWindow * const transientWindow = fPimpl->GetWindow(wid).fQuartzWindow;
1656 
1657  if (mainWindow != transientWindow) {
1658  if (transientWindow.fMainWindow) {
1659  if (transientWindow.fMainWindow != mainWindow)
1660  Error("SetWMTransientHint", "window is already transient for other window");
1661  } else {
1662  [[transientWindow standardWindowButton : NSWindowZoomButton] setEnabled : NO];
1663  [mainWindow addTransientWindow : transientWindow];
1664  }
1665  } else
1666  Warning("SetWMTransientHint", "transient and main windows are the same window");
1667 }
1668 
1669 #pragma mark - GUI-rendering part.
1670 
1671 //______________________________________________________________________________
1672 void TGCocoa::DrawLineAux(Drawable_t wid, const GCValues_t &gcVals, Int_t x1, Int_t y1, Int_t x2, Int_t y2)
1673 {
1674  //Can be called directly of when flushing command buffer.
1675  assert(!fPimpl->IsRootWindow(wid) && "DrawLineAux, called for root window");
1676 
1677  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(wid);
1678  CGContextRef ctx = drawable.fContext;
1679  assert(ctx != 0 && "DrawLineAux, context is null");
1680 
1681  const Quartz::CGStateGuard ctxGuard(ctx);//Will restore state back.
1682  //Draw a line.
1683  //This draw line is a special GUI method, it's used not by ROOT's graphics, but
1684  //widgets. The problem is:
1685  //-I have to switch off anti-aliasing, since if anti-aliasing is on,
1686  //the line is thick and has different color.
1687  //-As soon as I switch-off anti-aliasing, and line is precise, I can not
1688  //draw a line [0, 0, -> w, 0].
1689  //I use a small translation, after all, this is ONLY gui method and it
1690  //will not affect anything except GUI.
1691 
1692  CGContextSetAllowsAntialiasing(ctx, false);//Smoothed line is of wrong color and in a wrong position - this is bad for GUI.
1693 
1694  if (!drawable.fIsPixmap)
1695  CGContextTranslateCTM(ctx, 0.5, 0.5);
1696  else {
1697  //Pixmap uses native Cocoa's left-low-corner system.
1698  y1 = Int_t(X11::LocalYROOTToCocoa(drawable, y1));
1699  y2 = Int_t(X11::LocalYROOTToCocoa(drawable, y2));
1700  }
1701 
1702  SetStrokeParametersFromX11Context(ctx, gcVals);
1703  CGContextBeginPath(ctx);
1704  CGContextMoveToPoint(ctx, x1, y1);
1705  CGContextAddLineToPoint(ctx, x2, y2);
1706  CGContextStrokePath(ctx);
1707 
1708  CGContextSetAllowsAntialiasing(ctx, true);//Somehow, it's not saved/restored, this affects ... window's titlebar.
1709 }
1710 
1711 //______________________________________________________________________________
1712 void TGCocoa::DrawLine(Drawable_t wid, GContext_t gc, Int_t x1, Int_t y1, Int_t x2, Int_t y2)
1713 {
1714  //This function can be called:
1715  //a)'normal' way - from view's drawRect method.
1716  //b) for 'direct rendering' - operation was initiated by ROOT's GUI, not by
1717  // drawRect.
1718 
1719  //From TGX11:
1720  if (!wid)
1721  return;
1722 
1723  assert(!fPimpl->IsRootWindow(wid) && "DrawLine, called for root window");
1724  assert(gc > 0 && gc <= fX11Contexts.size() && "DrawLine, invalid context index");
1725 
1726  const GCValues_t &gcVals = fX11Contexts[gc - 1];
1727 
1728  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(wid);
1729  if (!drawable.fIsPixmap) {
1730  NSObject<X11Window> * const window = (NSObject<X11Window> *)drawable;
1731  QuartzView *view = (QuartzView *)window.fContentView;
1732  const ViewFixer fixer(view, wid);
1733  if (!view.fIsOverlapped && view.fMapState == kIsViewable) {
1734  if (!view.fContext)
1735  fPimpl->fX11CommandBuffer.AddDrawLine(wid, gcVals, x1, y1, x2, y2);
1736  else
1737  DrawLineAux(wid, gcVals, x1, y1, x2, y2);
1738  }
1739  } else {
1740  if (!IsCocoaDraw()) {
1741  fPimpl->fX11CommandBuffer.AddDrawLine(wid, gcVals, x1, y1, x2, y2);
1742  } else {
1743  DrawLineAux(wid, gcVals, x1, y1, x2, y2);
1744  }
1745  }
1746 }
1747 
1748 //______________________________________________________________________________
1749 void TGCocoa::DrawSegmentsAux(Drawable_t wid, const GCValues_t &gcVals, const Segment_t *segments, Int_t nSegments)
1750 {
1751  assert(!fPimpl->IsRootWindow(wid) && "DrawSegmentsAux, called for root window");
1752  assert(segments != 0 && "DrawSegmentsAux, segments parameter is null");
1753  assert(nSegments > 0 && "DrawSegmentsAux, nSegments <= 0");
1754 
1755  for (Int_t i = 0; i < nSegments; ++i)
1756  DrawLineAux(wid, gcVals, segments[i].fX1, segments[i].fY1 - 3, segments[i].fX2, segments[i].fY2 - 3);
1757 }
1758 
1759 //______________________________________________________________________________
1760 void TGCocoa::DrawSegments(Drawable_t wid, GContext_t gc, Segment_t *segments, Int_t nSegments)
1761 {
1762  //Draw multiple line segments. Each line is specified by a pair of points.
1763 
1764  //From TGX11:
1765  if (!wid)
1766  return;
1767 
1768  assert(!fPimpl->IsRootWindow(wid) && "DrawSegments, called for root window");
1769  assert(gc > 0 && gc <= fX11Contexts.size() && "DrawSegments, invalid context index");
1770  assert(segments != 0 && "DrawSegments, parameter 'segments' is null");
1771  assert(nSegments > 0 && "DrawSegments, number of segments <= 0");
1772 
1773  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(wid);
1774  const GCValues_t &gcVals = fX11Contexts[gc - 1];
1775 
1776  if (!drawable.fIsPixmap) {
1777  QuartzView *view = (QuartzView *)fPimpl->GetWindow(wid).fContentView;
1778  const ViewFixer fixer(view, wid);
1779 
1780  if (!view.fIsOverlapped && view.fMapState == kIsViewable) {
1781  if (!view.fContext)
1782  fPimpl->fX11CommandBuffer.AddDrawSegments(wid, gcVals, segments, nSegments);
1783  else
1784  DrawSegmentsAux(wid, gcVals, segments, nSegments);
1785  }
1786  } else {
1787  if (!IsCocoaDraw())
1788  fPimpl->fX11CommandBuffer.AddDrawSegments(wid, gcVals, segments, nSegments);
1789  else
1790  DrawSegmentsAux(wid, gcVals, segments, nSegments);
1791  }
1792 }
1793 
1794 //______________________________________________________________________________
1795 void TGCocoa::DrawRectangleAux(Drawable_t wid, const GCValues_t &gcVals, Int_t x, Int_t y, UInt_t w, UInt_t h)
1796 {
1797  //Can be called directly or during flushing command buffer.
1798  assert(!fPimpl->IsRootWindow(wid) && "DrawRectangleAux, called for root window");
1799 
1800  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(wid);
1801 
1802  if (!drawable.fIsPixmap) {
1803  //I can not draw a line at y == 0, shift the rectangle to 1 pixel (and reduce its height).
1804  if (!y) {
1805  y = 1;
1806  if (h)
1807  h -= 1;
1808  }
1809  } else {
1810  //Pixmap has native Cocoa's low-left-corner system.
1811  y = Int_t(X11::LocalYROOTToCocoa(drawable, y + h));
1812  }
1813 
1814  CGContextRef ctx = fPimpl->GetDrawable(wid).fContext;
1815  assert(ctx && "DrawRectangleAux, context is null");
1816  const Quartz::CGStateGuard ctxGuard(ctx);//Will restore context state.
1817 
1818  CGContextSetAllowsAntialiasing(ctx, false);
1819  //Line color from X11 context.
1820  SetStrokeParametersFromX11Context(ctx, gcVals);
1821 
1822  const CGRect rect = CGRectMake(x, y, w, h);
1823  CGContextStrokeRect(ctx, rect);
1824 
1825  CGContextSetAllowsAntialiasing(ctx, true);
1826 }
1827 
1828 //______________________________________________________________________________
1829 void TGCocoa::DrawRectangle(Drawable_t wid, GContext_t gc, Int_t x, Int_t y, UInt_t w, UInt_t h)
1830 {
1831  //Can be called in a 'normal way' - from drawRect method (QuartzView)
1832  //or directly by ROOT.
1833 
1834  if (!wid)//From TGX11.
1835  return;
1836 
1837  assert(!fPimpl->IsRootWindow(wid) && "DrawRectangle, called for root window");
1838  assert(gc > 0 && gc <= fX11Contexts.size() && "DrawRectangle, invalid context index");
1839 
1840  const GCValues_t &gcVals = fX11Contexts[gc - 1];
1841 
1842  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(wid);
1843 
1844  if (!drawable.fIsPixmap) {
1845  NSObject<X11Window> * const window = (NSObject<X11Window> *)drawable;
1846  QuartzView *view = (QuartzView *)window.fContentView;
1847  const ViewFixer fixer(view, wid);
1848 
1849  if (!view.fIsOverlapped && view.fMapState == kIsViewable) {
1850  if (!view.fContext)
1851  fPimpl->fX11CommandBuffer.AddDrawRectangle(wid, gcVals, x, y, w, h);
1852  else
1853  DrawRectangleAux(wid, gcVals, x, y, w, h);
1854  }
1855  } else {
1856  if (!IsCocoaDraw())
1857  fPimpl->fX11CommandBuffer.AddDrawRectangle(wid, gcVals, x, y, w, h);
1858  else
1859  DrawRectangleAux(wid, gcVals, x, y, w, h);
1860  }
1861 }
1862 
1863 //______________________________________________________________________________
1864 void TGCocoa::FillRectangleAux(Drawable_t wid, const GCValues_t &gcVals, Int_t x, Int_t y, UInt_t w, UInt_t h)
1865 {
1866  //Can be called directly or when flushing command buffer.
1867  //Can be called directly or when flushing command buffer.
1868 
1869  //From TGX11:
1870  if (!wid)
1871  return;
1872 
1873  assert(!fPimpl->IsRootWindow(wid) && "FillRectangleAux, called for root window");
1874 
1875  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(wid);
1876  CGContextRef ctx = drawable.fContext;
1877  CGSize patternPhase = {};
1878 
1879  if (drawable.fIsPixmap) {
1880  //Pixmap has low-left-corner based system.
1881  y = Int_t(X11::LocalYROOTToCocoa(drawable, y + h));
1882  }
1883 
1884  const CGRect fillRect = CGRectMake(x, y, w, h);
1885 
1886  if (!drawable.fIsPixmap) {
1887  QuartzView * const view = (QuartzView *)fPimpl->GetWindow(wid).fContentView;
1888  if (view.fParentView) {
1889  const NSPoint origin = [view.fParentView convertPoint : view.frame.origin toView : nil];
1890  patternPhase.width = origin.x;
1891  patternPhase.height = origin.y;
1892  }
1893  }
1894 
1895  const Quartz::CGStateGuard ctxGuard(ctx);//Will restore context state.
1896 
1897  if (HasFillStippledStyle(gcVals) || HasFillOpaqueStippledStyle(gcVals) || HasFillTiledStyle(gcVals)) {
1898  PatternContext patternContext = {gcVals.fMask, gcVals.fFillStyle, 0, 0, nil, patternPhase};
1899 
1900  if (HasFillStippledStyle(gcVals) || HasFillOpaqueStippledStyle(gcVals)) {
1901  assert(gcVals.fStipple != kNone &&
1902  "FillRectangleAux, fill_style is FillStippled/FillOpaqueStippled,"
1903  " but no stipple is set in a context");
1904 
1905  patternContext.fForeground = gcVals.fForeground;
1906  patternContext.fImage = fPimpl->GetDrawable(gcVals.fStipple);
1907 
1908  if (HasFillOpaqueStippledStyle(gcVals))
1909  patternContext.fBackground = gcVals.fBackground;
1910  } else {
1911  assert(gcVals.fTile != kNone &&
1912  "FillRectangleAux, fill_style is FillTiled, but not tile is set in a context");
1913 
1914  patternContext.fImage = fPimpl->GetDrawable(gcVals.fTile);
1915  }
1916 
1917  SetFillPattern(ctx, &patternContext);
1918  CGContextFillRect(ctx, fillRect);
1919 
1920  return;
1921  }
1922 
1923  SetFilledAreaColorFromX11Context(ctx, gcVals);
1924  CGContextFillRect(ctx, fillRect);
1925 }
1926 
1927 //______________________________________________________________________________
1928 void TGCocoa::FillRectangle(Drawable_t wid, GContext_t gc, Int_t x, Int_t y, UInt_t w, UInt_t h)
1929 {
1930  //Can be called in a 'normal way' - from drawRect method (QuartzView)
1931  //or directly by ROOT.
1932 
1933  //From TGX11:
1934  if (!wid)
1935  return;
1936 
1937  assert(!fPimpl->IsRootWindow(wid) && "FillRectangle, called for root window");
1938  assert(gc > 0 && gc <= fX11Contexts.size() && "FillRectangle, invalid context index");
1939 
1940  const GCValues_t &gcVals = fX11Contexts[gc - 1];
1941  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(wid);
1942 
1943  if (!drawable.fIsPixmap) {
1944  NSObject<X11Window> * const window = (NSObject<X11Window> *)drawable;
1945  QuartzView *view = (QuartzView *)window.fContentView;
1946  const ViewFixer fixer(view, wid);
1947  if (!view.fIsOverlapped && view.fMapState == kIsViewable) {
1948  if (!view.fContext)
1949  fPimpl->fX11CommandBuffer.AddFillRectangle(wid, gcVals, x, y, w, h);
1950  else
1951  FillRectangleAux(wid, gcVals, x, y, w, h);
1952  }
1953  } else
1954  FillRectangleAux(wid, gcVals, x, y, w, h);
1955 }
1956 
1957 //______________________________________________________________________________
1958 void TGCocoa::FillPolygonAux(Window_t wid, const GCValues_t &gcVals, const Point_t *polygon, Int_t nPoints)
1959 {
1960  //Can be called directly or when flushing command buffer.
1961 
1962  //From TGX11:
1963  if (!wid)
1964  return;
1965 
1966  assert(!fPimpl->IsRootWindow(wid) && "FillPolygonAux, called for root window");
1967  assert(polygon != 0 && "FillPolygonAux, parameter 'polygon' is null");
1968  assert(nPoints > 0 && "FillPolygonAux, number of points must be positive");
1969 
1970  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(wid);
1971  CGContextRef ctx = drawable.fContext;
1972 
1973  CGSize patternPhase = {};
1974 
1975  if (!drawable.fIsPixmap) {
1976  QuartzView * const view = (QuartzView *)fPimpl->GetWindow(wid).fContentView;
1977  const NSPoint origin = [view convertPoint : view.frame.origin toView : nil];
1978  patternPhase.width = origin.x;
1979  patternPhase.height = origin.y;
1980  }
1981 
1982  const Quartz::CGStateGuard ctxGuard(ctx);//Will restore context state.
1983 
1984  CGContextSetAllowsAntialiasing(ctx, false);
1985 
1986  if (HasFillStippledStyle(gcVals) || HasFillOpaqueStippledStyle(gcVals) || HasFillTiledStyle(gcVals)) {
1987  PatternContext patternContext = {gcVals.fMask, gcVals.fFillStyle, 0, 0, nil, patternPhase};
1988 
1989  if (HasFillStippledStyle(gcVals) || HasFillOpaqueStippledStyle(gcVals)) {
1990  assert(gcVals.fStipple != kNone &&
1991  "FillRectangleAux, fill style is FillStippled/FillOpaqueStippled,"
1992  " but no stipple is set in a context");
1993 
1994  patternContext.fForeground = gcVals.fForeground;
1995  patternContext.fImage = fPimpl->GetDrawable(gcVals.fStipple);
1996 
1997  if (HasFillOpaqueStippledStyle(gcVals))
1998  patternContext.fBackground = gcVals.fBackground;
1999  } else {
2000  assert(gcVals.fTile != kNone &&
2001  "FillRectangleAux, fill_style is FillTiled, but not tile is set in a context");
2002 
2003  patternContext.fImage = fPimpl->GetDrawable(gcVals.fTile);
2004  }
2005 
2006  SetFillPattern(ctx, &patternContext);
2007  } else
2008  SetFilledAreaColorFromX11Context(ctx, gcVals);
2009 
2010  //This +2 -2 shit is the result of ROOT's GUI producing strange coordinates out of ....
2011  // - first noticed on checkmarks in a menu - they were all shifted.
2012 
2013  CGContextBeginPath(ctx);
2014  if (!drawable.fIsPixmap) {
2015  CGContextMoveToPoint(ctx, polygon[0].fX, polygon[0].fY - 2);
2016  for (Int_t i = 1; i < nPoints; ++i)
2017  CGContextAddLineToPoint(ctx, polygon[i].fX, polygon[i].fY - 2);
2018  } else {
2019  CGContextMoveToPoint(ctx, polygon[0].fX, X11::LocalYROOTToCocoa(drawable, polygon[0].fY + 2));
2020  for (Int_t i = 1; i < nPoints; ++i)
2021  CGContextAddLineToPoint(ctx, polygon[i].fX, X11::LocalYROOTToCocoa(drawable, polygon[i].fY + 2));
2022  }
2023 
2024  CGContextFillPath(ctx);
2025  CGContextSetAllowsAntialiasing(ctx, true);
2026 }
2027 
2028 //______________________________________________________________________________
2029 void TGCocoa::FillPolygon(Window_t wid, GContext_t gc, Point_t *polygon, Int_t nPoints)
2030 {
2031  // Fills the region closed by the specified path. The path is closed
2032  // automatically if the last point in the list does not coincide with the
2033  // first point.
2034  //
2035  // Point_t *points - specifies an array of points
2036  // Int_t npnt - specifies the number of points in the array
2037  //
2038  // GC components in use: function, plane-mask, fill-style, fill-rule,
2039  // subwindow-mode, clip-x-origin, clip-y-origin, and clip-mask. GC
2040  // mode-dependent components: foreground, background, tile, stipple,
2041  // tile-stipple-x-origin, and tile-stipple-y-origin.
2042  // (see also the GCValues_t structure)
2043 
2044  //From TGX11:
2045  if (!wid)
2046  return;
2047 
2048  assert(polygon != 0 && "FillPolygon, parameter 'polygon' is null");
2049  assert(nPoints > 0 && "FillPolygon, number of points must be positive");
2050  assert(gc > 0 && gc <= fX11Contexts.size() && "FillPolygon, invalid context index");
2051 
2052  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(wid);
2053  const GCValues_t &gcVals = fX11Contexts[gc - 1];
2054 
2055  if (!drawable.fIsPixmap) {
2056  QuartzView *view = (QuartzView *)fPimpl->GetWindow(wid).fContentView;
2057  const ViewFixer fixer(view, wid);
2058 
2059  if (!view.fIsOverlapped && view.fMapState == kIsViewable) {
2060  if (!view.fContext)
2061  fPimpl->fX11CommandBuffer.AddFillPolygon(wid, gcVals, polygon, nPoints);
2062  else
2063  FillPolygonAux(wid, gcVals, polygon, nPoints);
2064  }
2065  } else {
2066  if (!IsCocoaDraw())
2067  fPimpl->fX11CommandBuffer.AddFillPolygon(wid, gcVals, polygon, nPoints);
2068  else
2069  FillPolygonAux(wid, gcVals, polygon, nPoints);
2070  }
2071 }
2072 
2073 //______________________________________________________________________________
2074 void TGCocoa::CopyAreaAux(Drawable_t src, Drawable_t dst, const GCValues_t &gcVals, Int_t srcX, Int_t srcY,
2075  UInt_t width, UInt_t height, Int_t dstX, Int_t dstY)
2076 {
2077  //Called directly or when flushing command buffer.
2078  if (!src || !dst)//Can this happen? From TGX11.
2079  return;
2080 
2081  assert(!fPimpl->IsRootWindow(src) && "CopyAreaAux, src parameter is root window");
2082  assert(!fPimpl->IsRootWindow(dst) && "CopyAreaAux, dst parameter is root window");
2083 
2084  //Some copy operations create autoreleased cocoa objects,
2085  //I do not want them to wait till run loop's iteration end to die.
2086  const Util::AutoreleasePool pool;
2087 
2088  NSObject<X11Drawable> * const srcDrawable = fPimpl->GetDrawable(src);
2089  NSObject<X11Drawable> * const dstDrawable = fPimpl->GetDrawable(dst);
2090 
2091  const X11::Point dstPoint(dstX, dstY);
2092  const X11::Rectangle copyArea(srcX, srcY, width, height);
2093 
2094  QuartzImage *mask = nil;
2095  if ((gcVals.fMask & kGCClipMask) && gcVals.fClipMask) {
2096  assert(fPimpl->GetDrawable(gcVals.fClipMask).fIsPixmap == YES &&
2097  "CopyArea, mask is not a pixmap");
2098  mask = (QuartzImage *)fPimpl->GetDrawable(gcVals.fClipMask);
2099  }
2100 
2101  X11::Point clipOrigin;
2102  if (gcVals.fMask & kGCClipXOrigin)
2103  clipOrigin.fX = gcVals.fClipXOrigin;
2104  if (gcVals.fMask & kGCClipYOrigin)
2105  clipOrigin.fY = gcVals.fClipYOrigin;
2106 
2107  [dstDrawable copy : srcDrawable area : copyArea withMask : mask clipOrigin : clipOrigin toPoint : dstPoint];
2108 }
2109 
2110 //______________________________________________________________________________
2111 void TGCocoa::CopyArea(Drawable_t src, Drawable_t dst, GContext_t gc, Int_t srcX, Int_t srcY,
2112  UInt_t width, UInt_t height, Int_t dstX, Int_t dstY)
2113 {
2114  if (!src || !dst)//Can this happen? From TGX11.
2115  return;
2116 
2117  assert(!fPimpl->IsRootWindow(src) && "CopyArea, src parameter is root window");
2118  assert(!fPimpl->IsRootWindow(dst) && "CopyArea, dst parameter is root window");
2119  assert(gc > 0 && gc <= fX11Contexts.size() && "CopyArea, invalid context index");
2120 
2121  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(dst);
2122  const GCValues_t &gcVals = fX11Contexts[gc - 1];
2123 
2124  if (!drawable.fIsPixmap) {
2125  QuartzView *view = (QuartzView *)fPimpl->GetWindow(dst).fContentView;
2126  const ViewFixer fixer(view, dst);
2127 
2128  if (!view.fIsOverlapped && view.fMapState == kIsViewable) {
2129  if (!view.fContext)
2130  fPimpl->fX11CommandBuffer.AddCopyArea(src, dst, gcVals, srcX, srcY, width, height, dstX, dstY);
2131  else
2132  CopyAreaAux(src, dst, gcVals, srcX, srcY, width, height, dstX, dstY);
2133  }
2134  } else {
2135  if (fPimpl->GetDrawable(src).fIsPixmap) {
2136  //Both are pixmaps, nothing is buffered for src (???).
2137  CopyAreaAux(src, dst, gcVals, srcX, srcY, width, height, dstX, dstY);
2138  } else {
2139  if (!IsCocoaDraw())
2140  fPimpl->fX11CommandBuffer.AddCopyArea(src, dst, gcVals, srcX, srcY, width, height, dstX, dstY);
2141  else
2142  CopyAreaAux(src, dst, gcVals, srcX, srcY, width, height, dstX, dstY);
2143  }
2144  }
2145 }
2146 
2147 //______________________________________________________________________________
2148 void TGCocoa::DrawStringAux(Drawable_t wid, const GCValues_t &gcVals, Int_t x, Int_t y, const char *text, Int_t len)
2149 {
2150  //Can be called by ROOT directly, or indirectly by AppKit.
2151  assert(!fPimpl->IsRootWindow(wid) && "DrawStringAux, called for root window");
2152 
2153  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(wid);
2154  CGContextRef ctx = drawable.fContext;
2155  assert(ctx != 0 && "DrawStringAux, context is null");
2156 
2157  const Quartz::CGStateGuard ctxGuard(ctx);//Will reset parameters back.
2158 
2159  CGContextSetTextMatrix(ctx, CGAffineTransformIdentity);
2160 
2161  //View is flipped, I have to transform for text to work.
2162  if (!drawable.fIsPixmap) {
2163  CGContextTranslateCTM(ctx, 0., drawable.fHeight);
2164  CGContextScaleCTM(ctx, 1., -1.);
2165  }
2166 
2167  //Text must be antialiased
2168  CGContextSetAllowsAntialiasing(ctx, true);
2169 
2170  assert(gcVals.fMask & kGCFont && "DrawString, font is not set in a context");
2171 
2172  if (len < 0)//Negative length can come from caller.
2173  len = std::strlen(text);
2174  //Text can be not black, for example, highlighted label.
2175  CGFloat textColor[4] = {0., 0., 0., 1.};//black by default.
2176  //I do not check the results here, it's ok to have a black text.
2177  if (gcVals.fMask & kGCForeground)
2178  X11::PixelToRGB(gcVals.fForeground, textColor);
2179 
2180  CGContextSetRGBFillColor(ctx, textColor[0], textColor[1], textColor[2], textColor[3]);
2181 
2182  //Do a simple text layout using CGGlyphs.
2183  //GUI uses non-ascii symbols, and does not care about signed/unsigned - just dump everything
2184  //into a char and be happy. I'm not.
2185  std::vector<UniChar> unichars((unsigned char *)text, (unsigned char *)text + len);
2186  FixAscii(unichars);
2187 
2188  Quartz::DrawTextLineNoKerning(ctx, (CTFontRef)gcVals.fFont, unichars, x, X11::LocalYROOTToCocoa(drawable, y));
2189 }
2190 
2191 //______________________________________________________________________________
2192 void TGCocoa::DrawString(Drawable_t wid, GContext_t gc, Int_t x, Int_t y, const char *text, Int_t len)
2193 {
2194  //Can be called by ROOT directly, or indirectly by AppKit.
2195  if (!wid)//from TGX11.
2196  return;
2197 
2198  assert(!fPimpl->IsRootWindow(wid) && "DrawString, called for root window");
2199  assert(gc > 0 && gc <= fX11Contexts.size() && "DrawString, invalid context index");
2200 
2201  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(wid);
2202  const GCValues_t &gcVals = fX11Contexts[gc - 1];
2203  assert(gcVals.fMask & kGCFont && "DrawString, font is not set in a context");
2204 
2205  if (!drawable.fIsPixmap) {
2206  QuartzView *view = (QuartzView *)fPimpl->GetWindow(wid).fContentView;
2207  const ViewFixer fixer(view, wid);
2208 
2209  if (!view.fIsOverlapped && view.fMapState == kIsViewable) {
2210  if (!view.fContext)
2211  fPimpl->fX11CommandBuffer.AddDrawString(wid, gcVals, x, y, text, len);
2212  else
2213  DrawStringAux(wid, gcVals, x, y, text, len);
2214  }
2215 
2216  } else {
2217  if (!IsCocoaDraw())
2218  fPimpl->fX11CommandBuffer.AddDrawString(wid, gcVals, x, y, text, len);
2219  else
2220  DrawStringAux(wid, gcVals, x, y, text, len);
2221  }
2222 }
2223 
2224 //______________________________________________________________________________
2225 void TGCocoa::ClearAreaAux(Window_t windowID, Int_t x, Int_t y, UInt_t w, UInt_t h)
2226 {
2227  assert(!fPimpl->IsRootWindow(windowID) && "ClearAreaAux, called for root window");
2228 
2229  QuartzView * const view = (QuartzView *)fPimpl->GetWindow(windowID).fContentView;
2230  assert(view.fContext != 0 && "ClearAreaAux, view.fContext is null");
2231 
2232  //w and h can be 0 (comment from TGX11) - clear the entire window.
2233  if (!w)
2234  w = view.fWidth;
2235  if (!h)
2236  h = view.fHeight;
2237 
2238  if (!view.fBackgroundPixmap) {
2239  //Simple solid fill.
2240  CGFloat rgb[3] = {};
2241  X11::PixelToRGB(view.fBackgroundPixel, rgb);
2242 
2243  const Quartz::CGStateGuard ctxGuard(view.fContext);
2244  CGContextSetRGBFillColor(view.fContext, rgb[0], rgb[1], rgb[2], 1.);//alpha can be also used.
2245  CGContextFillRect(view.fContext, CGRectMake(x, y, w, h));
2246  } else {
2247  const CGRect fillRect = CGRectMake(x, y, w, h);
2248 
2249  CGSize patternPhase = {};
2250  if (view.fParentView) {
2251  const NSPoint origin = [view.fParentView convertPoint : view.frame.origin toView : nil];
2252  patternPhase.width = origin.x;
2253  patternPhase.height = origin.y;
2254  }
2255  const Quartz::CGStateGuard ctxGuard(view.fContext);//Will restore context state.
2256 
2257  PatternContext patternContext = {Mask_t(), 0, 0, 0, view.fBackgroundPixmap, patternPhase};
2258  SetFillPattern(view.fContext, &patternContext);
2259  CGContextFillRect(view.fContext, fillRect);
2260  }
2261 }
2262 
2263 //______________________________________________________________________________
2264 void TGCocoa::ClearArea(Window_t wid, Int_t x, Int_t y, UInt_t w, UInt_t h)
2265 {
2266  //Can be called from drawRect method and also by ROOT's GUI directly.
2267  //Should not be called for pixmap?
2268 
2269  //From TGX11:
2270  if (!wid)
2271  return;
2272 
2273  assert(!fPimpl->IsRootWindow(wid) && "ClearArea, called for root window");
2274 
2275  //If wid is pixmap or image, this will crush.
2276  QuartzView *view = (QuartzView *)fPimpl->GetWindow(wid).fContentView;
2277  if (ParentRendersToChild(view))
2278  return;
2279 
2280  if (!view.fIsOverlapped && view.fMapState == kIsViewable) {
2281  if (!view.fContext)
2282  fPimpl->fX11CommandBuffer.AddClearArea(wid, x, y, w, h);
2283  else
2284  ClearAreaAux(wid, x, y, w, h);
2285  }
2286 }
2287 
2288 //______________________________________________________________________________
2289 void TGCocoa::ClearWindow(Window_t wid)
2290 {
2291  //Clears the entire area in the specified window (comment from TGX11).
2292 
2293  //From TGX11:
2294  if (!wid)
2295  return;
2296 
2297  ClearArea(wid, 0, 0, 0, 0);
2298 }
2299 
2300 #pragma mark - Pixmap management.
2301 
2302 //______________________________________________________________________________
2303 Int_t TGCocoa::OpenPixmap(UInt_t w, UInt_t h)
2304 {
2305  //Two stage creation.
2306  NSSize newSize = {};
2307  newSize.width = w;
2308  newSize.height = h;
2309 
2310  Util::NSScopeGuard<QuartzPixmap> pixmap([[QuartzPixmap alloc] initWithW : w H : h
2311  scaleFactor : [[NSScreen mainScreen] backingScaleFactor]]);
2312  if (pixmap.Get()) {
2313  pixmap.Get().fID = fPimpl->RegisterDrawable(pixmap.Get());//Can throw.
2314  return (Int_t)pixmap.Get().fID;
2315  } else {
2316  //Detailed error message was issued by QuartzPixmap by this point:
2317  Error("OpenPixmap", "QuartzPixmap initialization failed");
2318  return -1;
2319  }
2320 }
2321 
2322 //______________________________________________________________________________
2323 Int_t TGCocoa::ResizePixmap(Int_t wid, UInt_t w, UInt_t h)
2324 {
2325  assert(!fPimpl->IsRootWindow(wid) && "ResizePixmap, called for root window");
2326 
2327  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(wid);
2328  assert(drawable.fIsPixmap == YES && "ResizePixmap, invalid drawable");
2329 
2330  QuartzPixmap *pixmap = (QuartzPixmap *)drawable;
2331  if (w == pixmap.fWidth && h == pixmap.fHeight)
2332  return 1;
2333 
2334  if ([pixmap resizeW : w H : h scaleFactor : [[NSScreen mainScreen] backingScaleFactor]])
2335  return 1;
2336 
2337  return -1;
2338 }
2339 
2340 //______________________________________________________________________________
2341 void TGCocoa::SelectPixmap(Int_t pixmapID)
2342 {
2343  assert(pixmapID > (Int_t)fPimpl->GetRootWindowID() &&
2344  "SelectPixmap, parameter 'pixmapID' is not a valid id");
2345 
2346  fSelectedDrawable = pixmapID;
2347 }
2348 
2349 //______________________________________________________________________________
2350 void TGCocoa::CopyPixmap(Int_t pixmapID, Int_t x, Int_t y)
2351 {
2352  assert(pixmapID > (Int_t)fPimpl->GetRootWindowID() &&
2353  "CopyPixmap, parameter 'pixmapID' is not a valid id");
2354  assert(fSelectedDrawable > fPimpl->GetRootWindowID() &&
2355  "CopyPixmap, fSelectedDrawable is not a valid window id");
2356 
2357  NSObject<X11Drawable> * const source = fPimpl->GetDrawable(pixmapID);
2358  assert([source isKindOfClass : [QuartzPixmap class]] &&
2359  "CopyPixmap, source is not a pixmap");
2360  QuartzPixmap * const pixmap = (QuartzPixmap *)source;
2361 
2362  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(fSelectedDrawable);
2363  NSObject<X11Drawable> * destination = nil;
2364 
2365  if (drawable.fIsPixmap) {
2366  destination = drawable;
2367  } else {
2368  NSObject<X11Window> * const window = fPimpl->GetWindow(fSelectedDrawable);
2369  if (window.fBackBuffer) {
2370  destination = window.fBackBuffer;
2371  } else {
2372  Warning("CopyPixmap", "Operation skipped, since destination"
2373  " window is not double buffered");
2374  return;
2375  }
2376  }
2377 
2378  const X11::Rectangle copyArea(0, 0, pixmap.fWidth, pixmap.fHeight);
2379  const X11::Point dstPoint(x, y);
2380 
2381  [destination copy : pixmap area : copyArea withMask : nil clipOrigin : X11::Point() toPoint : dstPoint];
2382 }
2383 
2384 //______________________________________________________________________________
2385 void TGCocoa::ClosePixmap()
2386 {
2387  // Deletes current pixmap.
2388  assert(fSelectedDrawable > fPimpl->GetRootWindowID() && "ClosePixmap, no drawable selected");
2389  assert(fPimpl->GetDrawable(fSelectedDrawable).fIsPixmap == YES && "ClosePixmap, selected drawable is not a pixmap");
2390 
2391  DeletePixmap(fSelectedDrawable);
2392  fSelectedDrawable = 0;
2393 }
2394 
2395 #pragma mark - Different functions to create pixmap from different data sources. Used by GUI.
2396 #pragma mark - These functions implement TVirtualX interface, some of them dupilcate others.
2397 
2398 //______________________________________________________________________________
2399 Pixmap_t TGCocoa::CreatePixmap(Drawable_t /*wid*/, UInt_t w, UInt_t h)
2400 {
2401  //
2402  return OpenPixmap(w, h);
2403 }
2404 
2405 //______________________________________________________________________________
2406 Pixmap_t TGCocoa::CreatePixmap(Drawable_t /*wid*/, const char *bitmap, UInt_t width, UInt_t height,
2407  ULong_t foregroundPixel, ULong_t backgroundPixel, Int_t depth)
2408 {
2409  //Create QuartzImage, using bitmap and foregroundPixel/backgroundPixel,
2410  //if depth is one - create an image mask instead.
2411 
2412  assert(bitmap != 0 && "CreatePixmap, parameter 'bitmap' is null");
2413  assert(width > 0 && "CreatePixmap, parameter 'width' is 0");
2414  assert(height > 0 && "CreatePixmap, parameter 'height' is 0");
2415 
2416  std::vector<unsigned char> imageData (depth > 1 ? width * height * 4 : width * height);
2417 
2418  X11::FillPixmapBuffer((unsigned char*)bitmap, width, height, foregroundPixel,
2419  backgroundPixel, depth, &imageData[0]);
2420 
2421  //Now we can create CGImageRef.
2422  Util::NSScopeGuard<QuartzImage> image;
2423 
2424  if (depth > 1)
2425  image.Reset([[QuartzImage alloc] initWithW : width H : height data: &imageData[0]]);
2426  else
2427  image.Reset([[QuartzImage alloc] initMaskWithW : width H : height bitmapMask : &imageData[0]]);
2428 
2429  if (!image.Get()) {
2430  Error("CreatePixmap", "QuartzImage initialization failed");//More concrete message was issued by QuartzImage.
2431  return kNone;
2432  }
2433 
2434  image.Get().fID = fPimpl->RegisterDrawable(image.Get());//This can throw.
2435  return image.Get().fID;
2436 }
2437 
2438 //______________________________________________________________________________
2439 Pixmap_t TGCocoa::CreatePixmapFromData(unsigned char *bits, UInt_t width, UInt_t height)
2440 {
2441  //Create QuartzImage, using "bits" (data in bgra format).
2442  assert(bits != 0 && "CreatePixmapFromData, data parameter is null");
2443  assert(width != 0 && "CreatePixmapFromData, width parameter is 0");
2444  assert(height != 0 && "CreatePixmapFromData, height parameter is 0");
2445 
2446  //I'm not using vector here, since I have to pass this pointer to Obj-C code
2447  //(and Obj-C object will own this memory later).
2448  std::vector<unsigned char> imageData(bits, bits + width * height * 4);
2449 
2450  //Convert bgra to rgba.
2451  unsigned char *p = &imageData[0];
2452  for (unsigned i = 0, e = width * height; i < e; ++i, p += 4)
2453  std::swap(p[0], p[2]);
2454 
2455  //Now we can create CGImageRef.
2456  Util::NSScopeGuard<QuartzImage> image([[QuartzImage alloc] initWithW : width
2457  H : height data : &imageData[0]]);
2458 
2459  if (!image.Get()) {
2460  //Detailed error message was issued by QuartzImage.
2461  Error("CreatePixmapFromData", "QuartzImage initialziation failed");
2462  return kNone;
2463  }
2464 
2465  image.Get().fID = fPimpl->RegisterDrawable(image.Get());//This can throw.
2466  return image.Get().fID;
2467 }
2468 
2469 //______________________________________________________________________________
2470 Pixmap_t TGCocoa::CreateBitmap(Drawable_t /*wid*/, const char *bitmap, UInt_t width, UInt_t height)
2471 {
2472  //Create QuartzImage with image mask.
2473  assert(std::numeric_limits<unsigned char>::digits == 8 && "CreateBitmap, ASImage requires octets");
2474 
2475  //I'm not using vector here, since I have to pass this pointer to Obj-C code
2476  //(and Obj-C object will own this memory later).
2477 
2478  //TASImage has a bug, it calculates size in pixels (making a with to multiple-of eight and
2479  //allocates memory as each bit occupies one byte, and later packs bits into bytes.
2480 
2481  std::vector<unsigned char> imageData(width * height);
2482 
2483  //TASImage assumes 8-bit bytes and packs mask bits.
2484  for (unsigned i = 0, j = 0, e = width / 8 * height; i < e; ++i) {
2485  for(unsigned bit = 0; bit < 8; ++bit, ++j) {
2486  if (bitmap[i] & (1 << bit))
2487  imageData[j] = 0;//Opaque.
2488  else
2489  imageData[j] = 255;//Masked out bit.
2490  }
2491  }
2492 
2493  //Now we can create CGImageRef.
2494  Util::NSScopeGuard<QuartzImage> image([[QuartzImage alloc] initMaskWithW : width
2495  H : height bitmapMask : &imageData[0]]);
2496  if (!image.Get()) {
2497  //Detailed error message was issued by QuartzImage.
2498  Error("CreateBitmap", "QuartzImage initialization failed");
2499  return kNone;
2500  }
2501 
2502  image.Get().fID = fPimpl->RegisterDrawable(image.Get());//This can throw.
2503  return image.Get().fID;
2504 }
2505 
2506 //______________________________________________________________________________
2507 void TGCocoa::DeletePixmapAux(Pixmap_t pixmapID)
2508 {
2509  fPimpl->DeleteDrawable(pixmapID);
2510 }
2511 
2512 //______________________________________________________________________________
2513 void TGCocoa::DeletePixmap(Pixmap_t pixmapID)
2514 {
2515  // Explicitely deletes the pixmap resource "pmap".
2516  assert(fPimpl->GetDrawable(pixmapID).fIsPixmap == YES && "DeletePixmap, object is not a pixmap");
2517  fPimpl->fX11CommandBuffer.AddDeletePixmap(pixmapID);
2518 }
2519 
2520 //______________________________________________________________________________
2521 Int_t TGCocoa::AddPixmap(ULong_t /*pixind*/, UInt_t /*w*/, UInt_t /*h*/)
2522 {
2523  // Registers a pixmap created by TGLManager as a ROOT pixmap
2524  //
2525  // w, h - the width and height, which define the pixmap size
2526  return 0;
2527 }
2528 
2529 //______________________________________________________________________________
2530 unsigned char *TGCocoa::GetColorBits(Drawable_t wid, Int_t x, Int_t y, UInt_t w, UInt_t h)
2531 {
2532  //Can be also in a window management part, since window is also drawable.
2533  if (fPimpl->IsRootWindow(wid)) {
2534  Warning("GetColorBits", "Called for root window");
2535  } else {
2536  assert(x >= 0 && "GetColorBits, parameter 'x' is negative");
2537  assert(y >= 0 && "GetColorBits, parameter 'y' is negative");
2538  assert(w != 0 && "GetColorBits, parameter 'w' is 0");
2539  assert(h != 0 && "GetColorBits, parameter 'h' is 0");
2540 
2541  const X11::Rectangle area(x, y, w, h);
2542  return [fPimpl->GetDrawable(wid) readColorBits : area];//readColorBits can throw std::bad_alloc, no resource will leak.
2543  }
2544 
2545  return 0;
2546 }
2547 
2548 #pragma mark - XImage emulation.
2549 
2550 //______________________________________________________________________________
2551 Drawable_t TGCocoa::CreateImage(UInt_t width, UInt_t height)
2552 {
2553  // Allocates the memory needed for a drawable.
2554  //
2555  // width - the width of the image, in pixels
2556  // height - the height of the image, in pixels
2557  return OpenPixmap(width, height);
2558 }
2559 
2560 //______________________________________________________________________________
2561 void TGCocoa::GetImageSize(Drawable_t wid, UInt_t &width, UInt_t &height)
2562 {
2563  // Returns the width and height of the image wid
2564  assert(wid > fPimpl->GetRootWindowID() && "GetImageSize, parameter 'wid' is invalid");
2565 
2566  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(wid);
2567  width = drawable.fWidth;
2568  height = drawable.fHeight;
2569 }
2570 
2571 //______________________________________________________________________________
2572 void TGCocoa::PutPixel(Drawable_t imageID, Int_t x, Int_t y, ULong_t pixel)
2573 {
2574  // Overwrites the pixel in the image with the specified pixel value.
2575  // The image must contain the x and y coordinates.
2576  //
2577  // imageID - specifies the image
2578  // x, y - coordinates
2579  // pixel - the new pixel value
2580 
2581  assert([fPimpl->GetDrawable(imageID) isKindOfClass : [QuartzPixmap class]] &&
2582  "PutPixel, parameter 'imageID' is a bad pixmap id");
2583  assert(x >= 0 && "PutPixel, parameter 'x' is negative");
2584  assert(y >= 0 && "PutPixel, parameter 'y' is negative");
2585 
2586  QuartzPixmap * const pixmap = (QuartzPixmap *)fPimpl->GetDrawable(imageID);
2587 
2588  unsigned char rgb[3] = {};
2589  X11::PixelToRGB(pixel, rgb);
2590  [pixmap putPixel : rgb X : x Y : y];
2591 }
2592 
2593 //______________________________________________________________________________
2594 void TGCocoa::PutImage(Drawable_t drawableID, GContext_t gc, Drawable_t imageID, Int_t dstX, Int_t dstY,
2595  Int_t srcX, Int_t srcY, UInt_t width, UInt_t height)
2596 {
2597  //TGX11 uses ZPixmap in CreateImage ... so background/foreground
2598  //in gc can NEVER be used (and the depth is ALWAYS > 1).
2599  //This means .... I can call CopyArea!
2600 
2601  CopyArea(imageID, drawableID, gc, srcX, srcY, width, height, dstX, dstY);
2602 }
2603 
2604 //______________________________________________________________________________
2605 void TGCocoa::DeleteImage(Drawable_t imageID)
2606 {
2607  // Deallocates the memory associated with the image img
2608  assert([fPimpl->GetDrawable(imageID) isKindOfClass : [QuartzPixmap class]] &&
2609  "DeleteImage, imageID parameter is not a valid image id");
2610  DeletePixmap(imageID);
2611 }
2612 
2613 #pragma mark - Mouse related code.
2614 
2615 //______________________________________________________________________________
2616 void TGCocoa::GrabButton(Window_t wid, EMouseButton button, UInt_t keyModifiers, UInt_t eventMask,
2617  Window_t /*confine*/, Cursor_t /*cursor*/, Bool_t grab)
2618 {
2619  //Emulate "passive grab" feature of X11 (similar to "implicit grab" in Cocoa
2620  //and implicit grab on X11, the difference is that "implicit grab" works as
2621  //if owner_events parameter for XGrabButton was False, but in ROOT
2622  //owner_events for XGrabButton is _always_ True.
2623  //Confine will never be used - no such feature on MacOSX and
2624  //I'm not going to emulate it..
2625  //This function also does ungrab.
2626 
2627  //From TGWin32:
2628  if (!wid)
2629  return;
2630 
2631  assert(!fPimpl->IsRootWindow(wid) && "GrabButton, called for 'root' window");
2632 
2633  NSObject<X11Window> * const widget = fPimpl->GetWindow(wid);
2634 
2635  if (grab) {
2636  widget.fPassiveGrabOwnerEvents = YES; //This is how TGX11 works.
2637  widget.fPassiveGrabButton = button;
2638  widget.fPassiveGrabEventMask = eventMask;
2639  widget.fPassiveGrabKeyModifiers = keyModifiers;
2640  //Set the cursor.
2641  } else {
2642  widget.fPassiveGrabOwnerEvents = NO;
2643  widget.fPassiveGrabButton = -1;//0 is kAnyButton.
2644  widget.fPassiveGrabEventMask = 0;
2645  widget.fPassiveGrabKeyModifiers = 0;
2646  }
2647 }
2648 
2649 //______________________________________________________________________________
2650 void TGCocoa::GrabPointer(Window_t wid, UInt_t eventMask, Window_t /*confine*/, Cursor_t /*cursor*/, Bool_t grab, Bool_t ownerEvents)
2651 {
2652  //Emulate pointer grab from X11.
2653  //Confine will never be used - no such feature on MacOSX and
2654  //I'm not going to emulate it..
2655  //This function also does ungrab.
2656 
2657  if (grab) {
2658  NSView<X11Window> * const view = fPimpl->GetWindow(wid).fContentView;
2659  assert(!fPimpl->IsRootWindow(wid) && "GrabPointer, called for 'root' window");
2660  //set the cursor.
2661  //set active grab.
2662  fPimpl->fX11EventTranslator.SetPointerGrab(view, eventMask, ownerEvents);
2663  } else {
2664  //unset cursor?
2665  //cancel grab.
2666  fPimpl->fX11EventTranslator.CancelPointerGrab();
2667  }
2668 }
2669 
2670 //______________________________________________________________________________
2671 void TGCocoa::ChangeActivePointerGrab(Window_t, UInt_t, Cursor_t)
2672 {
2673  // Changes the specified dynamic parameters if the pointer is actively
2674  // grabbed by the client and if the specified time is no earlier than the
2675  // last-pointer-grab time and no later than the current X server time.
2676  //Noop.
2677 }
2678 
2679 //______________________________________________________________________________
2680 void TGCocoa::SetKeyAutoRepeat(Bool_t /*on*/)
2681 {
2682  // Turns key auto repeat on (kTRUE) or off (kFALSE).
2683  //Noop.
2684 }
2685 
2686 //______________________________________________________________________________
2687 void TGCocoa::GrabKey(Window_t wid, Int_t keyCode, UInt_t rootKeyModifiers, Bool_t grab)
2688 {
2689  //Comment from TVirtualX:
2690  // Establishes a passive grab on the keyboard. In the future, the
2691  // keyboard is actively grabbed, the last-keyboard-grab time is set
2692  // to the time at which the key was pressed (as transmitted in the
2693  // KeyPress event), and the KeyPress event is reported if all of the
2694  // following conditions are true:
2695  // - the keyboard is not grabbed and the specified key (which can
2696  // itself be a modifier key) is logically pressed when the
2697  // specified modifier keys are logically down, and no other
2698  // modifier keys are logically down;
2699  // - either the grab window "id" is an ancestor of (or is) the focus
2700  // window, or "id" is a descendant of the focus window and contains
2701  // the pointer;
2702  // - a passive grab on the same key combination does not exist on any
2703  // ancestor of grab_window
2704  //
2705  // id - window id
2706  // keycode - specifies the KeyCode or AnyKey
2707  // modifier - specifies the set of keymasks or AnyModifier; the mask is
2708  // the bitwise inclusive OR of the valid keymask bits
2709  // grab - a switch between grab/ungrab key
2710  // grab = kTRUE grab the key and modifier
2711  // grab = kFALSE ungrab the key and modifier
2712  //End of comment.
2713 
2714 
2715  //Key code already must be Cocoa's key code, this is done by GUI classes,
2716  //they call KeySymToKeyCode.
2717  assert(!fPimpl->IsRootWindow(wid) && "GrabKey, called for root window");
2718 
2719  NSView<X11Window> * const view = fPimpl->GetWindow(wid).fContentView;
2720  const NSUInteger cocoaKeyModifiers = X11::GetCocoaKeyModifiersFromROOTKeyModifiers(rootKeyModifiers);
2721 
2722  if (grab)
2723  [view addPassiveKeyGrab : keyCode modifiers : cocoaKeyModifiers];
2724  else
2725  [view removePassiveKeyGrab : keyCode modifiers : cocoaKeyModifiers];
2726 }
2727 
2728 //______________________________________________________________________________
2729 Int_t TGCocoa::KeysymToKeycode(UInt_t keySym)
2730 {
2731  // Converts the "keysym" to the appropriate keycode. For example,
2732  // keysym is a letter and keycode is the matching keyboard key (which
2733  // is dependend on the current keyboard mapping). If the specified
2734  // "keysym" is not defined for any keycode, returns zero.
2735 
2736  return X11::MapKeySymToKeyCode(keySym);
2737 }
2738 
2739 //______________________________________________________________________________
2740 Window_t TGCocoa::GetInputFocus()
2741 {
2742  // Returns the window id of the window having the input focus.
2743 
2744  return fPimpl->fX11EventTranslator.GetInputFocus();
2745 }
2746 
2747 //______________________________________________________________________________
2748 void TGCocoa::SetInputFocus(Window_t wid)
2749 {
2750  // Changes the input focus to specified window "wid".
2751  assert(!fPimpl->IsRootWindow(wid) && "SetInputFocus, called for root window");
2752 
2753  if (wid == kNone)
2754  fPimpl->fX11EventTranslator.SetInputFocus(nil);
2755  else
2756  fPimpl->fX11EventTranslator.SetInputFocus(fPimpl->GetWindow(wid).fContentView);
2757 }
2758 
2759 //______________________________________________________________________________
2760 void TGCocoa::LookupString(Event_t *event, char *buf, Int_t length, UInt_t &keysym)
2761 {
2762  // Converts the keycode from the event structure to a key symbol (according
2763  // to the modifiers specified in the event structure and the current
2764  // keyboard mapping). In "buf" a null terminated ASCII string is returned
2765  // representing the string that is currently mapped to the key code.
2766  //
2767  // event - specifies the event structure to be used
2768  // buf - returns the translated characters
2769  // buflen - the length of the buffer
2770  // keysym - returns the "keysym" computed from the event
2771  // if this argument is not NULL
2772  assert(buf != 0 && "LookupString, parameter 'buf' is null");
2773  assert(length >= 2 && "LookupString, parameter 'length' - not enough memory to return null-terminated ASCII string");
2774 
2775  X11::MapUnicharToKeySym(event->fCode, buf, length, keysym);
2776 }
2777 
2778 #pragma mark - Font management.
2779 
2780 //______________________________________________________________________________
2781 FontStruct_t TGCocoa::LoadQueryFont(const char *fontName)
2782 {
2783  //fontName is in XLFD format:
2784  //-foundry-family- ..... etc., some components can be omitted and replaced by *.
2785  assert(fontName != 0 && "LoadQueryFont, fontName is null");
2786 
2787  X11::XLFDName xlfd;
2788  if (ParseXLFDName(fontName, xlfd)) {
2789  //Make names more flexible: fFamilyName can be empty or '*'.
2790  if (!xlfd.fFamilyName.length() || xlfd.fFamilyName == "*")
2791  xlfd.fFamilyName = "Courier";//Up to me, right?
2792  if (!xlfd.fPixelSize)
2793  xlfd.fPixelSize = 11;//Again, up to me.
2794  return fPimpl->fFontManager.LoadFont(xlfd);
2795  }
2796 
2797  return FontStruct_t();
2798 }
2799 
2800 //______________________________________________________________________________
2801 FontH_t TGCocoa::GetFontHandle(FontStruct_t fs)
2802 {
2803  return (FontH_t)fs;
2804 }
2805 
2806 //______________________________________________________________________________
2807 void TGCocoa::DeleteFont(FontStruct_t fs)
2808 {
2809  fPimpl->fFontManager.UnloadFont(fs);
2810 }
2811 
2812 //______________________________________________________________________________
2813 Bool_t TGCocoa::HasTTFonts() const
2814 {
2815  // Returns True when TrueType fonts are used
2816  //No, we use Core Text and do not want TTF to calculate metrics.
2817  return kFALSE;
2818 }
2819 
2820 //______________________________________________________________________________
2821 Int_t TGCocoa::TextWidth(FontStruct_t font, const char *s, Int_t len)
2822 {
2823  // Return lenght of the string "s" in pixels. Size depends on font.
2824  return fPimpl->fFontManager.GetTextWidth(font, s, len);
2825 }
2826 
2827 //______________________________________________________________________________
2828 void TGCocoa::GetFontProperties(FontStruct_t font, Int_t &maxAscent, Int_t &maxDescent)
2829 {
2830  // Returns the font properties.
2831  fPimpl->fFontManager.GetFontProperties(font, maxAscent, maxDescent);
2832 }
2833 
2834 //______________________________________________________________________________
2835 FontStruct_t TGCocoa::GetFontStruct(FontH_t fh)
2836 {
2837  // Retrieves the associated font structure of the font specified font
2838  // handle "fh".
2839  //
2840  // Free returned FontStruct_t using FreeFontStruct().
2841 
2842  return (FontStruct_t)fh;
2843 }
2844 
2845 //______________________________________________________________________________
2846 void TGCocoa::FreeFontStruct(FontStruct_t /*fs*/)
2847 {
2848  // Frees the font structure "fs". The font itself will be freed when
2849  // no other resource references it.
2850  //Noop.
2851 }
2852 
2853 //______________________________________________________________________________
2854 char **TGCocoa::ListFonts(const char *fontName, Int_t maxNames, Int_t &count)
2855 {
2856  count = 0;
2857 
2858  if (fontName && fontName[0]) {
2859  X11::XLFDName xlfd;
2860  if (X11::ParseXLFDName(fontName, xlfd))
2861  return fPimpl->fFontManager.ListFonts(xlfd, maxNames, count);
2862  }
2863 
2864  return 0;
2865 }
2866 
2867 //______________________________________________________________________________
2868 void TGCocoa::FreeFontNames(char **fontList)
2869 {
2870  // Frees the specified the array of strings "fontlist".
2871  if (!fontList)
2872  return;
2873 
2874  fPimpl->fFontManager.FreeFontNames(fontList);
2875 }
2876 
2877 #pragma mark - Color management.
2878 
2879 //______________________________________________________________________________
2880 Bool_t TGCocoa::ParseColor(Colormap_t /*cmap*/, const char *colorName, ColorStruct_t &color)
2881 {
2882  //"Color" passed as colorName, can be one of the names, defined in X11/rgb.txt,
2883  //or rgb triplet, which looks like: #rgb #rrggbb #rrrgggbbb #rrrrggggbbbb,
2884  //where r, g, and b - are hex digits.
2885  return fPimpl->fX11ColorParser.ParseColor(colorName, color);
2886 }
2887 
2888 //______________________________________________________________________________
2889 Bool_t TGCocoa::AllocColor(Colormap_t /*cmap*/, ColorStruct_t &color)
2890 {
2891  const unsigned red = unsigned(double(color.fRed) / 0xFFFF * 0xFF);
2892  const unsigned green = unsigned(double(color.fGreen) / 0xFFFF * 0xFF);
2893  const unsigned blue = unsigned(double(color.fBlue) / 0xFFFF * 0xFF);
2894  color.fPixel = red << 16 | green << 8 | blue;
2895  return kTRUE;
2896 }
2897 
2898 //______________________________________________________________________________
2899 void TGCocoa::QueryColor(Colormap_t /*cmap*/, ColorStruct_t & color)
2900 {
2901  // Returns the current RGB value for the pixel in the "color" structure
2902  color.fRed = (color.fPixel >> 16 & 0xFF) * 0xFFFF / 0xFF;
2903  color.fGreen = (color.fPixel >> 8 & 0xFF) * 0xFFFF / 0xFF;
2904  color.fBlue = (color.fPixel & 0xFF) * 0xFFFF / 0xFF;
2905 }
2906 
2907 //______________________________________________________________________________
2908 void TGCocoa::FreeColor(Colormap_t /*cmap*/, ULong_t /*pixel*/)
2909 {
2910  // Frees color cell with specified pixel value.
2911 }
2912 
2913 //______________________________________________________________________________
2914 ULong_t TGCocoa::GetPixel(Color_t rootColorIndex)
2915 {
2916  ULong_t pixel = 0;
2917  if (const TColor * const color = gROOT->GetColor(rootColorIndex)) {
2918  Float_t red = 0.f, green = 0.f, blue = 0.f;
2919  color->GetRGB(red, green, blue);
2920  pixel = unsigned(red * 255) << 16;
2921  pixel |= unsigned(green * 255) << 8;
2922  pixel |= unsigned(blue * 255);
2923  }
2924 
2925  return pixel;
2926 }
2927 
2928 //______________________________________________________________________________
2929 void TGCocoa::GetPlanes(Int_t &nPlanes)
2930 {
2931  //Implemented as NSBitsPerPixelFromDepth([mainScreen depth]);
2932  nPlanes = GetDepth();
2933 }
2934 
2935 //______________________________________________________________________________
2936 void TGCocoa::GetRGB(Int_t /*index*/, Float_t &/*r*/, Float_t &/*g*/, Float_t &/*b*/)
2937 {
2938  // Returns RGB values for color "index".
2939 }
2940 
2941 //______________________________________________________________________________
2942 void TGCocoa::SetRGB(Int_t /*cindex*/, Float_t /*r*/, Float_t /*g*/, Float_t /*b*/)
2943 {
2944  // Sets color intensities the specified color index "cindex".
2945  //
2946  // cindex - color index
2947  // r, g, b - the red, green, blue intensities between 0.0 and 1.0
2948 }
2949 
2950 //______________________________________________________________________________
2951 Colormap_t TGCocoa::GetColormap() const
2952 {
2953  return Colormap_t();
2954 }
2955 
2956 #pragma mark - Graphical context management.
2957 
2958 //______________________________________________________________________________
2959 GContext_t TGCocoa::CreateGC(Drawable_t /*wid*/, GCValues_t *gval)
2960 {
2961  //Here I have to imitate graphics context that exists in X11.
2962  fX11Contexts.push_back(*gval);
2963  return fX11Contexts.size();
2964 }
2965 
2966 //______________________________________________________________________________
2967 void TGCocoa::SetForeground(GContext_t gc, ULong_t foreground)
2968 {
2969  // Sets the foreground color for the specified GC (shortcut for ChangeGC
2970  // with only foreground mask set).
2971  //
2972  // gc - specifies the GC
2973  // foreground - the foreground you want to set
2974  // (see also the GCValues_t structure)
2975 
2976  assert(gc <= fX11Contexts.size() && gc > 0 && "ChangeGC, invalid context id");
2977 
2978  GCValues_t &x11Context = fX11Contexts[gc - 1];
2979  x11Context.fMask |= kGCForeground;
2980  x11Context.fForeground = foreground;
2981 }
2982 
2983 //______________________________________________________________________________
2984 void TGCocoa::ChangeGC(GContext_t gc, GCValues_t *gval)
2985 {
2986  //
2987  assert(gc <= fX11Contexts.size() && gc > 0 && "ChangeGC, invalid context id");
2988  assert(gval != 0 && "ChangeGC, gval parameter is null");
2989 
2990  GCValues_t &x11Context = fX11Contexts[gc - 1];
2991  const Mask_t &mask = gval->fMask;
2992  x11Context.fMask |= mask;
2993 
2994  //Not all of GCValues_t members are used, but
2995  //all can be copied/set without any problem.
2996 
2997  if (mask & kGCFunction)
2998  x11Context.fFunction = gval->fFunction;
2999  if (mask & kGCPlaneMask)
3000  x11Context.fPlaneMask = gval->fPlaneMask;
3001  if (mask & kGCForeground)
3002  x11Context.fForeground = gval->fForeground;
3003  if (mask & kGCBackground)
3004  x11Context.fBackground = gval->fBackground;
3005  if (mask & kGCLineWidth)
3006  x11Context.fLineWidth = gval->fLineWidth;
3007  if (mask & kGCLineStyle)
3008  x11Context.fLineStyle = gval->fLineStyle;
3009  if (mask & kGCCapStyle)//nobody uses
3010  x11Context.fCapStyle = gval->fCapStyle;
3011  if (mask & kGCJoinStyle)//nobody uses
3012  x11Context.fJoinStyle = gval->fJoinStyle;
3013  if (mask & kGCFillRule)//nobody uses
3014  x11Context.fFillRule = gval->fFillRule;
3015  if (mask & kGCArcMode)//nobody uses
3016  x11Context.fArcMode = gval->fArcMode;
3017  if (mask & kGCFillStyle)
3018  x11Context.fFillStyle = gval->fFillStyle;
3019  if (mask & kGCTile)
3020  x11Context.fTile = gval->fTile;
3021  if (mask & kGCStipple)
3022  x11Context.fStipple = gval->fStipple;
3023  if (mask & kGCTileStipXOrigin)
3024  x11Context.fTsXOrigin = gval->fTsXOrigin;
3025  if (mask & kGCTileStipYOrigin)
3026  x11Context.fTsYOrigin = gval->fTsYOrigin;
3027  if (mask & kGCFont)
3028  x11Context.fFont = gval->fFont;
3029  if (mask & kGCSubwindowMode)
3030  x11Context.fSubwindowMode = gval->fSubwindowMode;
3031  if (mask & kGCGraphicsExposures)
3032  x11Context.fGraphicsExposures = gval->fGraphicsExposures;
3033  if (mask & kGCClipXOrigin)
3034  x11Context.fClipXOrigin = gval->fClipXOrigin;
3035  if (mask & kGCClipYOrigin)
3036  x11Context.fClipYOrigin = gval->fClipYOrigin;
3037  if (mask & kGCClipMask)
3038  x11Context.fClipMask = gval->fClipMask;
3039  if (mask & kGCDashOffset)
3040  x11Context.fDashOffset = gval->fDashOffset;
3041  if (mask & kGCDashList) {
3042  const unsigned nDashes = sizeof x11Context.fDashes / sizeof x11Context.fDashes[0];
3043  for (unsigned i = 0; i < nDashes; ++i)
3044  x11Context.fDashes[i] = gval->fDashes[i];
3045  x11Context.fDashLen = gval->fDashLen;
3046  }
3047 }
3048 
3049 //______________________________________________________________________________
3050 void TGCocoa::CopyGC(GContext_t src, GContext_t dst, Mask_t mask)
3051 {
3052  assert(src <= fX11Contexts.size() && src > 0 && "CopyGC, bad source context");
3053  assert(dst <= fX11Contexts.size() && dst > 0 && "CopyGC, bad destination context");
3054 
3055  GCValues_t srcContext = fX11Contexts[src - 1];
3056  srcContext.fMask = mask;
3057 
3058  ChangeGC(dst, &srcContext);
3059 }
3060 
3061 //______________________________________________________________________________
3062 void TGCocoa::GetGCValues(GContext_t gc, GCValues_t &gval)
3063 {
3064  // Returns the components specified by the mask in "gval" for the
3065  // specified GC "gc" (see also the GCValues_t structure)
3066  const GCValues_t &gcVal = fX11Contexts[gc - 1];
3067  gval = gcVal;
3068 }
3069 
3070 //______________________________________________________________________________
3071 void TGCocoa::DeleteGC(GContext_t /*gc*/)
3072 {
3073  // Deletes the specified GC "gc".
3074 }
3075 
3076 #pragma mark - Cursor management.
3077 
3078 //______________________________________________________________________________
3079 Cursor_t TGCocoa::CreateCursor(ECursor cursor)
3080 {
3081  // Creates the specified cursor. (just return cursor from cursor pool).
3082  // The cursor can be:
3083  //
3084  // kBottomLeft, kBottomRight, kTopLeft, kTopRight,
3085  // kBottomSide, kLeftSide, kTopSide, kRightSide,
3086  // kMove, kCross, kArrowHor, kArrowVer,
3087  // kHand, kRotate, kPointer, kArrowRight,
3088  // kCaret, kWatch
3089 
3090  return Cursor_t(cursor + 1);//HAHAHAHAHA!!! CREATED!!!
3091 }
3092 
3093 //______________________________________________________________________________
3094 void TGCocoa::SetCursor(Int_t wid, ECursor cursor)
3095 {
3096  // The cursor "cursor" will be used when the pointer is in the
3097  // window "wid".
3098  assert(!fPimpl->IsRootWindow(wid) && "SetCursor, called for root window");
3099 
3100  NSView<X11Window> * const view = fPimpl->GetWindow(wid).fContentView;
3101  view.fCurrentCursor = cursor;
3102 }
3103 
3104 //______________________________________________________________________________
3105 void TGCocoa::SetCursor(Window_t wid, Cursor_t cursorID)
3106 {
3107  // Sets the cursor "curid" to be used when the pointer is in the
3108  // window "wid".
3109  if (cursorID > 0)
3110  SetCursor(Int_t(wid), ECursor(cursorID - 1));
3111  else
3112  SetCursor(Int_t(wid), kPointer);
3113 }
3114 
3115 //______________________________________________________________________________
3116 void TGCocoa::QueryPointer(Int_t &x, Int_t &y)
3117 {
3118  // Returns the pointer position.
3119 
3120  //I ignore fSelectedDrawable here. If you have any problems with this, hehe, you can ask me :)
3121  const NSPoint screenPoint = [NSEvent mouseLocation];
3122  x = X11::GlobalXCocoaToROOT(screenPoint.x);
3123  y = X11::GlobalYCocoaToROOT(screenPoint.y);
3124 }
3125 
3126 //______________________________________________________________________________
3127 void TGCocoa::QueryPointer(Window_t winID, Window_t &rootWinID, Window_t &childWinID,
3128  Int_t &rootX, Int_t &rootY, Int_t &winX, Int_t &winY, UInt_t &mask)
3129 {
3130  //Emulate XQueryPointer.
3131 
3132  //From TGX11/TGWin32:
3133  if (!winID)
3134  return;//Neither TGX11, nor TGWin32 set any of out parameters.
3135 
3136  //We have only one root window.
3137  rootWinID = fPimpl->GetRootWindowID();
3138  //Find cursor position (screen coordinates).
3139  NSPoint screenPoint = [NSEvent mouseLocation];
3140  screenPoint.x = X11::GlobalXCocoaToROOT(screenPoint.x);
3141  screenPoint.y = X11::GlobalYCocoaToROOT(screenPoint.y);
3142  rootX = screenPoint.x;
3143  rootY = screenPoint.y;
3144 
3145  //Convert a screen point to winID's coordinate system.
3146  if (winID > fPimpl->GetRootWindowID()) {
3147  NSObject<X11Window> * const window = fPimpl->GetWindow(winID);
3148  const NSPoint winPoint = X11::TranslateFromScreen(screenPoint, window.fContentView);
3149  winX = winPoint.x;
3150  winY = winPoint.y;
3151  } else {
3152  winX = screenPoint.x;
3153  winY = screenPoint.y;
3154  }
3155 
3156  //Find child window in these coordinates (?).
3157  if (QuartzWindow * const childWin = X11::FindWindowInPoint(screenPoint.x, screenPoint.y)) {
3158  childWinID = childWin.fID;
3159  mask = X11::GetModifiers();
3160  } else {
3161  childWinID = 0;
3162  mask = 0;
3163  }
3164 }
3165 
3166 #pragma mark - OpenGL management.
3167 
3168 //______________________________________________________________________________
3169 Double_t TGCocoa::GetOpenGLScalingFactor()
3170 {
3171  //Scaling factor to let our OpenGL code know, that we probably
3172  //work on a retina display.
3173 
3174  return [[NSScreen mainScreen] backingScaleFactor];
3175 }
3176 
3177 //______________________________________________________________________________
3178 Window_t TGCocoa::CreateOpenGLWindow(Window_t parentID, UInt_t width, UInt_t height,
3179  const std::vector<std::pair<UInt_t, Int_t> > &formatComponents)
3180 {
3181  //ROOT never creates GL widgets with 'root' as a parent (so not top-level gl-windows).
3182  //If this change, assert must be deleted.
3183  typedef std::pair<UInt_t, Int_t> component_type;
3184  typedef std::vector<component_type>::size_type size_type;
3185 
3186  //Convert pairs into Cocoa's GL attributes.
3187  std::vector<NSOpenGLPixelFormatAttribute> attribs;
3188  for (size_type i = 0, e = formatComponents.size(); i < e; ++i) {
3189  const component_type &comp = formatComponents[i];
3190 
3191  if (comp.first == Rgl::kDoubleBuffer) {
3192  attribs.push_back(NSOpenGLPFADoubleBuffer);
3193  } else if (comp.first == Rgl::kDepth) {
3194  attribs.push_back(NSOpenGLPFADepthSize);
3195  attribs.push_back(comp.second > 0 ? comp.second : 32);
3196  } else if (comp.first == Rgl::kAccum) {
3197  attribs.push_back(NSOpenGLPFAAccumSize);
3198  attribs.push_back(comp.second > 0 ? comp.second : 1);
3199  } else if (comp.first == Rgl::kStencil) {
3200  attribs.push_back(NSOpenGLPFAStencilSize);
3201  attribs.push_back(comp.second > 0 ? comp.second : 8);
3202  } else if (comp.first == Rgl::kMultiSample) {
3203  attribs.push_back(NSOpenGLPFAMultisample);
3204  attribs.push_back(NSOpenGLPFASampleBuffers);
3205  attribs.push_back(1);
3206  attribs.push_back(NSOpenGLPFASamples);
3207  attribs.push_back(comp.second ? comp.second : 8);
3208  }
3209  }
3210 
3211  attribs.push_back(0);
3212 
3213  NSOpenGLPixelFormat * const pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes : &attribs[0]];
3214  const Util::NSScopeGuard<NSOpenGLPixelFormat> formatGuard(pixelFormat);
3215 
3216  NSView<X11Window> *parentView = nil;
3217  if (!fPimpl->IsRootWindow(parentID)) {
3218  parentView = fPimpl->GetWindow(parentID).fContentView;
3219  assert([parentView isKindOfClass : [QuartzView class]] &&
3220  "CreateOpenGLWindow, parent view must be QuartzView");
3221  }
3222 
3223  NSRect viewFrame = {};
3224  viewFrame.size.width = width;
3225  viewFrame.size.height = height;
3226 
3227  ROOTOpenGLView * const glView = [[ROOTOpenGLView alloc] initWithFrame : viewFrame pixelFormat : pixelFormat];
3228  const Util::NSScopeGuard<ROOTOpenGLView> viewGuard(glView);
3229 
3230  Window_t glID = kNone;
3231 
3232  if (parentView) {
3233  [parentView addChild : glView];
3234  glID = fPimpl->RegisterDrawable(glView);
3235  glView.fID = glID;
3236  } else {
3237  //"top-level glview".
3238  //Create a window to be parent of this gl-view.
3239  QuartzWindow *parent = [[QuartzWindow alloc] initWithGLView : glView];
3240  const Util::NSScopeGuard<QuartzWindow> winGuard(parent);
3241 
3242 
3243  if (!parent) {
3244  Error("CreateOpenGLWindow", "QuartzWindow allocation/initialization"
3245  " failed for a top-level GL widget");
3246  return kNone;
3247  }
3248 
3249  glID = fPimpl->RegisterDrawable(parent);
3250  parent.fID = glID;
3251  }
3252 
3253  return glID;
3254 }
3255 
3256 //______________________________________________________________________________
3257 Handle_t TGCocoa::CreateOpenGLContext(Window_t windowID, Handle_t sharedID)
3258 {
3259  assert(!fPimpl->IsRootWindow(windowID) &&
3260  "CreateOpenGLContext, parameter 'windowID' is a root window");
3261  assert([fPimpl->GetWindow(windowID).fContentView isKindOfClass : [ROOTOpenGLView class]] &&
3262  "CreateOpenGLContext, view is not an OpenGL view");
3263 
3264  NSOpenGLContext * const sharedContext = fPimpl->GetGLContextForHandle(sharedID);
3265  ROOTOpenGLView * const glView = (ROOTOpenGLView *)fPimpl->GetWindow(windowID);
3266 
3267  const Util::NSScopeGuard<NSOpenGLContext>
3268  newContext([[NSOpenGLContext alloc] initWithFormat : glView.pixelFormat shareContext : sharedContext]);
3269  glView.fOpenGLContext = newContext.Get();
3270  const Handle_t ctxID = fPimpl->RegisterGLContext(newContext.Get());
3271 
3272  return ctxID;
3273 }
3274 
3275 //______________________________________________________________________________
3276 void TGCocoa::CreateOpenGLContext(Int_t /*wid*/)
3277 {
3278  // Creates OpenGL context for window "wid"
3279 }
3280 
3281 //______________________________________________________________________________
3282 Bool_t TGCocoa::MakeOpenGLContextCurrent(Handle_t ctxID, Window_t windowID)
3283 {
3284  using namespace Details;
3285 
3286  assert(ctxID > 0 && "MakeOpenGLContextCurrent, invalid context id");
3287 
3288  NSOpenGLContext * const glContext = fPimpl->GetGLContextForHandle(ctxID);
3289  if (!glContext) {
3290  Error("MakeOpenGLContextCurrent", "No OpenGL context found for id %d", int(ctxID));
3291 
3292  return kFALSE;
3293  }
3294 
3295  ROOTOpenGLView * const glView = (ROOTOpenGLView *)fPimpl->GetWindow(windowID).fContentView;
3296 
3297  if (OpenGL::GLViewIsValidDrawable(glView)) {
3298  if ([glContext view] != glView)
3299  [glContext setView : glView];
3300 
3301  if (glView.fUpdateContext) {
3302  [glContext update];
3303  glView.fUpdateContext = NO;
3304  }
3305 
3306  glView.fOpenGLContext = glContext;
3307  [glContext makeCurrentContext];
3308 
3309  return kTRUE;
3310  } else {
3311  //Oh, here's the real black magic.
3312  //Our brilliant GL code is sure that MakeCurrent always succeeds.
3313  //But it does not: if view is not visible, context can not be attached,
3314  //gl operations will fail.
3315  //Funny enough, but if you have invisible window with visible view,
3316  //this trick works.
3317 
3318  NSView *fakeView = nil;
3319  QuartzWindow *fakeWindow = fPimpl->GetFakeGLWindow();
3320 
3321  if (!fakeWindow) {
3322  //We did not find any window. Create a new one.
3323  SetWindowAttributes_t attr = {};
3324  //100 - is just a stupid hardcoded value:
3325  const UInt_t width = std::max(glView.frame.size.width, CGFloat(100));
3326  const UInt_t height = std::max(glView.frame.size.height, CGFloat(100));
3327 
3328  NSRect viewFrame = {};
3329  viewFrame.size.width = width;
3330  viewFrame.size.height = height;
3331 
3332  const NSUInteger styleMask = kTitledWindowMask | kClosableWindowMask |
3333  kMiniaturizableWindowMask | kResizableWindowMask;
3334 
3335  //NOTE: defer parameter is 'NO', otherwise this trick will not help.
3336  fakeWindow = [[QuartzWindow alloc] initWithContentRect : viewFrame styleMask : styleMask
3337  backing : NSBackingStoreBuffered defer : NO windowAttributes : &attr];
3338  Util::NSScopeGuard<QuartzWindow> winGuard(fakeWindow);
3339 
3340  fakeView = fakeWindow.fContentView;
3341  [fakeView setHidden : NO];//!
3342 
3343  fPimpl->SetFakeGLWindow(fakeWindow);//Can throw.
3344  winGuard.Release();
3345  } else {
3346  fakeView = fakeWindow.fContentView;
3347  [fakeView setHidden : NO];
3348  }
3349 
3350  glView.fOpenGLContext = nil;
3351  [glContext setView : fakeView];
3352  [glContext makeCurrentContext];
3353  }
3354 
3355  return kTRUE;
3356 }
3357 
3358 //______________________________________________________________________________
3359 Handle_t TGCocoa::GetCurrentOpenGLContext()
3360 {
3361  NSOpenGLContext * const currentContext = [NSOpenGLContext currentContext];
3362  if (!currentContext) {
3363  Error("GetCurrentOpenGLContext", "The current OpenGL context is null");
3364  return kNone;
3365  }
3366 
3367  const Handle_t contextID = fPimpl->GetHandleForGLContext(currentContext);
3368  if (!contextID)
3369  Error("GetCurrentOpenGLContext", "The current OpenGL context was"
3370  " not created/registered by TGCocoa");
3371 
3372  return contextID;
3373 }
3374 
3375 //______________________________________________________________________________
3376 void TGCocoa::FlushOpenGLBuffer(Handle_t ctxID)
3377 {
3378  assert(ctxID > 0 && "FlushOpenGLBuffer, invalid context id");
3379 
3380  NSOpenGLContext * const glContext = fPimpl->GetGLContextForHandle(ctxID);
3381  assert(glContext != nil && "FlushOpenGLBuffer, bad context id");
3382 
3383  if (glContext != [NSOpenGLContext currentContext])//???
3384  return;
3385 
3386  glFlush();//???
3387  [glContext flushBuffer];
3388 }
3389 
3390 //______________________________________________________________________________
3391 void TGCocoa::DeleteOpenGLContext(Int_t ctxID)
3392 {
3393  //Historically, DeleteOpenGLContext was accepting window id,
3394  //now it's a context id. DeleteOpenGLContext is not used in ROOT,
3395  //only in TGLContext for Cocoa.
3396  NSOpenGLContext * const glContext = fPimpl->GetGLContextForHandle(ctxID);
3397  if (NSView * const v = [glContext view]) {
3398  if ([v isKindOfClass : [ROOTOpenGLView class]])
3399  ((ROOTOpenGLView *)v).fOpenGLContext = nil;
3400 
3401  [glContext clearDrawable];
3402  }
3403 
3404  if (glContext == [NSOpenGLContext currentContext])
3405  [NSOpenGLContext clearCurrentContext];
3406 
3407  fPimpl->DeleteGLContext(ctxID);
3408 }
3409 
3410 #pragma mark - Off-screen rendering for TPad/TCanvas.
3411 
3412 //______________________________________________________________________________
3413 void TGCocoa::SetDoubleBuffer(Int_t windowID, Int_t mode)
3414 {
3415  //In ROOT, canvas has a "double buffer" - pixmap attached to 'wid'.
3416  assert(windowID > (Int_t)fPimpl->GetRootWindowID() && "SetDoubleBuffer called for root window");
3417 
3418  if (windowID == 999) {//Comment in TVirtaulX suggests, that 999 means all windows.
3419  Warning("SetDoubleBuffer", "called with wid == 999");
3420  //Window with id 999 can not exists - this is checked in CocoaPrivate.
3421  } else {
3422  fSelectedDrawable = windowID;
3423  mode ? SetDoubleBufferON() : SetDoubleBufferOFF();
3424  }
3425 }
3426 
3427 //______________________________________________________________________________
3428 void TGCocoa::SetDoubleBufferOFF()
3429 {
3430  fDirectDraw = true;
3431 }
3432 
3433 //______________________________________________________________________________
3434 void TGCocoa::SetDoubleBufferON()
3435 {
3436  //Attach pixmap to the selected window (view).
3437  fDirectDraw = false;
3438 
3439  assert(fSelectedDrawable > fPimpl->GetRootWindowID() &&
3440  "SetDoubleBufferON, called, but no correct window was selected before");
3441 
3442  NSObject<X11Window> * const window = fPimpl->GetWindow(fSelectedDrawable);
3443 
3444  assert(window.fIsPixmap == NO &&
3445  "SetDoubleBufferON, selected drawable is a pixmap, can not attach pixmap to pixmap");
3446 
3447  const unsigned currW = window.fWidth;
3448  const unsigned currH = window.fHeight;
3449 
3450  if (QuartzPixmap *const currentPixmap = window.fBackBuffer) {
3451  if (currH == currentPixmap.fHeight && currW == currentPixmap.fWidth)
3452  return;
3453  }
3454 
3455  Util::NSScopeGuard<QuartzPixmap> pixmap([[QuartzPixmap alloc] initWithW : currW
3456  H : currH scaleFactor : [[NSScreen mainScreen] backingScaleFactor]]);
3457  if (pixmap.Get())
3458  window.fBackBuffer = pixmap.Get();
3459  else
3460  //Detailed error message was issued by QuartzPixmap.
3461  Error("SetDoubleBufferON", "QuartzPixmap initialization failed");
3462 }
3463 
3464 //______________________________________________________________________________
3465 void TGCocoa::SetDrawMode(EDrawMode mode)
3466 {
3467  // Sets the drawing mode.
3468  //
3469  //EDrawMode{kCopy, kXor};
3470  fDrawMode = mode;
3471 }
3472 
3473 #pragma mark - Event management part.
3474 
3475 //______________________________________________________________________________
3476 void TGCocoa::SendEvent(Window_t wid, Event_t *event)
3477 {
3478  if (fPimpl->IsRootWindow(wid))//ROOT's GUI can send events to root window.
3479  return;
3480 
3481  //From TGX11:
3482  if (!wid || !event)
3483  return;
3484 
3485  Event_t newEvent = *event;
3486  newEvent.fWindow = wid;
3487  fPimpl->fX11EventTranslator.fEventQueue.push_back(newEvent);
3488 }
3489 
3490 //______________________________________________________________________________
3491 void TGCocoa::NextEvent(Event_t &event)
3492 {
3493  assert(fPimpl->fX11EventTranslator.fEventQueue.size() > 0 && "NextEvent, event queue is empty");
3494 
3495  event = fPimpl->fX11EventTranslator.fEventQueue.front();
3496  fPimpl->fX11EventTranslator.fEventQueue.pop_front();
3497 }
3498 
3499 //______________________________________________________________________________
3500 Int_t TGCocoa::EventsPending()
3501 {
3502  return (Int_t)fPimpl->fX11EventTranslator.fEventQueue.size();
3503 }
3504 
3505 
3506 //______________________________________________________________________________
3507 Bool_t TGCocoa::CheckEvent(Window_t windowID, EGEventType type, Event_t &event)
3508 {
3509  typedef X11::EventQueue_t::iterator iterator_type;
3510 
3511  iterator_type it = fPimpl->fX11EventTranslator.fEventQueue.begin();
3512  iterator_type eIt = fPimpl->fX11EventTranslator.fEventQueue.end();
3513 
3514  for (; it != eIt; ++it) {
3515  const Event_t &queuedEvent = *it;
3516  if (queuedEvent.fWindow == windowID && queuedEvent.fType == type) {
3517  event = queuedEvent;
3518  fPimpl->fX11EventTranslator.fEventQueue.erase(it);
3519  return kTRUE;
3520  }
3521  }
3522 
3523  return kFALSE;
3524 }
3525 
3526 //______________________________________________________________________________
3527 Handle_t TGCocoa::GetNativeEvent() const
3528 {
3529  //I can not give an access to the native event,
3530  //it even, probably, does not exist already.
3531  return kNone;
3532 }
3533 
3534 #pragma mark - "Drag and drop", "Copy and paste", X11 properties.
3535 
3536 //______________________________________________________________________________
3537 Atom_t TGCocoa::InternAtom(const char *name, Bool_t onlyIfExist)
3538 {
3539  //X11 properties emulation.
3540 
3541  assert(name != 0 && "InternAtom, parameter 'name' is null");
3542  return FindAtom(name, !onlyIfExist);
3543 }
3544 
3545 //______________________________________________________________________________
3546 void TGCocoa::SetPrimarySelectionOwner(Window_t windowID)
3547 {
3548  //Comment from TVirtualX:
3549  // Makes the window "wid" the current owner of the primary selection.
3550  // That is the window in which, for example some text is selected.
3551  //End of comment.
3552 
3553  //It's not clear, why SetPrimarySelectionOwner and SetSelectionOwner have different return types.
3554 
3555  if (!windowID)//From TGWin32.
3556  return;
3557 
3558  assert(!fPimpl->IsRootWindow(windowID) &&
3559  "SetPrimarySelectionOwner, windowID parameter is a 'root' window");
3560  assert(fPimpl->GetDrawable(windowID).fIsPixmap == NO &&
3561  "SetPrimarySelectionOwner, windowID parameter is not a valid window");
3562 
3563  const Atom_t primarySelectionAtom = FindAtom("XA_PRIMARY", false);
3564  assert(primarySelectionAtom != kNone &&
3565  "SetPrimarySelectionOwner, predefined XA_PRIMARY atom was not found");
3566 
3567  fSelectionOwners[primarySelectionAtom] = windowID;
3568  //No events will be send - I do not have different clients, so nobody to send SelectionClear.
3569 }
3570 
3571 //______________________________________________________________________________
3572 Bool_t TGCocoa::SetSelectionOwner(Window_t windowID, Atom_t &selection)
3573 {
3574  //Comment from TVirtualX:
3575  // Changes the owner and last-change time for the specified selection.
3576  //End of comment.
3577 
3578  //It's not clear, why SetPrimarySelectionOwner and SetSelectionOwner have different return types.
3579 
3580  if (!windowID)
3581  return kFALSE;
3582 
3583  assert(!fPimpl->IsRootWindow(windowID) &&
3584  "SetSelectionOwner, windowID parameter is a 'root' window'");
3585  assert(fPimpl->GetDrawable(windowID).fIsPixmap == NO &&
3586  "SetSelectionOwner, windowID parameter is not a valid window");
3587 
3588  fSelectionOwners[selection] = windowID;
3589  //No messages, since I do not have different clients.
3590 
3591  return kTRUE;
3592 }
3593 
3594 //______________________________________________________________________________
3595 Window_t TGCocoa::GetPrimarySelectionOwner()
3596 {
3597  //Comment from TVirtualX:
3598  // Returns the window id of the current owner of the primary selection.
3599  // That is the window in which, for example some text is selected.
3600  //End of comment.
3601  const Atom_t primarySelectionAtom = FindAtom("XA_PRIMARY", false);
3602  assert(primarySelectionAtom != kNone &&
3603  "GetPrimarySelectionOwner, predefined XA_PRIMARY atom was not found");
3604 
3605  return fSelectionOwners[primarySelectionAtom];
3606 }
3607 
3608 //______________________________________________________________________________
3609 void TGCocoa::ConvertPrimarySelection(Window_t windowID, Atom_t clipboard, Time_t when)
3610 {
3611  //Comment from TVirtualX:
3612  // Causes a SelectionRequest event to be sent to the current primary
3613  // selection owner. This event specifies the selection property
3614  // (primary selection), the format into which to convert that data before
3615  // storing it (target = XA_STRING), the property in which the owner will
3616  // place the information (sel_property), the window that wants the
3617  // information (id), and the time of the conversion request (when).
3618  // The selection owner responds by sending a SelectionNotify event, which
3619  // confirms the selected atom and type.
3620  //End of comment.
3621 
3622  //From TGWin32:
3623  if (!windowID)
3624  return;
3625 
3626  assert(!fPimpl->IsRootWindow(windowID) &&
3627  "ConvertPrimarySelection, parameter 'windowID' is root window");
3628  assert(fPimpl->GetDrawable(windowID).fIsPixmap == NO &&
3629  "ConvertPrimarySelection, parameter windowID parameter is not a window id");
3630 
3631  Atom_t primarySelectionAtom = FindAtom("XA_PRIMARY", false);
3632  assert(primarySelectionAtom != kNone &&
3633  "ConvertPrimarySelection, XA_PRIMARY predefined atom not found");
3634 
3635  Atom_t stringAtom = FindAtom("XA_STRING", false);
3636  assert(stringAtom != kNone &&
3637  "ConvertPrimarySelection, XA_STRING predefined atom not found");
3638 
3639  ConvertSelection(windowID, primarySelectionAtom, stringAtom, clipboard, when);
3640 }
3641 
3642 //______________________________________________________________________________
3643 void TGCocoa::ConvertSelection(Window_t windowID, Atom_t &selection, Atom_t &target,
3644  Atom_t &property, Time_t &/*timeStamp*/)
3645 {
3646  // Requests that the specified selection be converted to the specified
3647  // target type.
3648 
3649  // Requests that the specified selection be converted to the specified
3650  // target type.
3651 
3652  if (!windowID)
3653  return;
3654 
3655  assert(!fPimpl->IsRootWindow(windowID) &&
3656  "ConvertSelection, parameter 'windowID' is root window'");
3657  assert(fPimpl->GetDrawable(windowID).fIsPixmap == NO &&
3658  "ConvertSelection, parameter 'windowID' is not a window id");
3659 
3660  Event_t newEvent = {};
3661  selection_iterator selIter = fSelectionOwners.find(selection);
3662 
3663  if (selIter != fSelectionOwners.end())
3664  newEvent.fType = kSelectionRequest;
3665  else
3666  newEvent.fType = kSelectionNotify;
3667 
3668  newEvent.fWindow = windowID;
3669  newEvent.fUser[0] = windowID;//requestor
3670  newEvent.fUser[1] = selection;
3671  newEvent.fUser[2] = target;
3672  newEvent.fUser[3] = property;
3673 
3674  SendEvent(windowID, &newEvent);
3675 }
3676 
3677 //______________________________________________________________________________
3678 Int_t TGCocoa::GetProperty(Window_t windowID, Atom_t propertyID, Long_t, Long_t, Bool_t, Atom_t,
3679  Atom_t *actualType, Int_t *actualFormat, ULong_t *nItems,
3680  ULong_t *bytesAfterReturn, unsigned char **propertyReturn)
3681 {
3682  //Comment from TVirtualX:
3683  // Returns the actual type of the property; the actual format of the property;
3684  // the number of 8-bit, 16-bit, or 32-bit items transferred; the number of
3685  // bytes remaining to be read in the property; and a pointer to the data
3686  // actually returned.
3687  //End of comment.
3688 
3689  if (fPimpl->IsRootWindow(windowID))
3690  return 0;
3691 
3692  assert(fPimpl->GetDrawable(windowID).fIsPixmap == NO &&
3693  "GetProperty, parameter 'windowID' is not a valid window id");
3694  assert(propertyID > 0 && propertyID <= fAtomToName.size() &&
3695  "GetProperty, parameter 'propertyID' is not a valid atom");
3696  assert(actualType != 0 && "GetProperty, parameter 'actualType' is null");
3697  assert(actualFormat != 0 && "GetProperty, parameter 'actualFormat' is null");
3698  assert(bytesAfterReturn != 0 && "GetProperty, parameter 'bytesAfterReturn' is null");
3699  assert(propertyReturn != 0 && "GetProperty, parameter 'propertyReturn' is null");
3700 
3701  const Util::AutoreleasePool pool;
3702 
3703  *bytesAfterReturn = 0;//In TGWin32 the value set to .. nItems?
3704  *propertyReturn = 0;
3705  *nItems = 0;
3706 
3707  const std::string &atomName = fAtomToName[propertyID - 1];
3708  NSObject<X11Window> *window = fPimpl->GetWindow(windowID);
3709 
3710  if (![window hasProperty : atomName.c_str()]) {
3711  Error("GetProperty", "Unknown property %s requested", atomName.c_str());
3712  return 0;//actually, 0 is ... Success (X11)?
3713  }
3714 
3715  unsigned tmpFormat = 0, tmpElements = 0;
3716  *propertyReturn = [window getProperty : atomName.c_str() returnType : actualType
3717  returnFormat : &tmpFormat nElements : &tmpElements];
3718  *actualFormat = (Int_t)tmpFormat;
3719  *nItems = tmpElements;
3720 
3721  return *nItems;//Success (X11) is 0?
3722 }
3723 
3724 //______________________________________________________________________________
3725 void TGCocoa::GetPasteBuffer(Window_t windowID, Atom_t propertyID, TString &text,
3726  Int_t &nChars, Bool_t clearBuffer)
3727 {
3728  //Comment from TVirtualX:
3729  // Gets contents of the paste buffer "atom" into the string "text".
3730  // (nchar = number of characters) If "del" is true deletes the paste
3731  // buffer afterwards.
3732  //End of comment.
3733 
3734  //From TGX11:
3735  if (!windowID)
3736  return;
3737 
3738  assert(!fPimpl->IsRootWindow(windowID) &&
3739  "GetPasteBuffer, parameter 'windowID' is root window");
3740  assert(fPimpl->GetDrawable(windowID).fIsPixmap == NO &&
3741  "GetPasteBuffer, parameter 'windowID' is not a valid window");
3742  assert(propertyID && propertyID <= fAtomToName.size() &&
3743  "GetPasteBuffer, parameter 'propertyID' is not a valid atom");
3744 
3745  const Util::AutoreleasePool pool;
3746 
3747  const std::string &atomString = fAtomToName[propertyID - 1];
3748  NSObject<X11Window> *window = fPimpl->GetWindow(windowID);
3749 
3750  if (![window hasProperty : atomString.c_str()]) {
3751  Error("GetPasteBuffer", "No property %s on a window", atomString.c_str());
3752  return;
3753  }
3754 
3755  Atom_t tmpType = 0;
3756  unsigned tmpFormat = 0, nElements = 0;
3757 
3758  const Util::ScopedArray<char>
3759  propertyData((char *)[window getProperty : atomString.c_str()
3760  returnType : &tmpType returnFormat : &tmpFormat
3761  nElements : &nElements]);
3762 
3763  assert(tmpFormat == 8 && "GetPasteBuffer, property has wrong format");
3764 
3765  text.Insert(0, propertyData.Get(), nElements);
3766  nChars = (Int_t)nElements;
3767 
3768  if (clearBuffer) {
3769  //For the moment - just remove the property
3770  //(anyway, ChangeProperty/ChangeProperties will re-create it).
3771  [window removeProperty : atomString.c_str()];
3772  }
3773 }
3774 
3775 //______________________________________________________________________________
3776 void TGCocoa::ChangeProperty(Window_t windowID, Atom_t propertyID, Atom_t type,
3777  UChar_t *data, Int_t len)
3778 {
3779  //Comment from TVirtualX:
3780  // Alters the property for the specified window and causes the X server
3781  // to generate a PropertyNotify event on that window.
3782  //
3783  // wid - the window whose property you want to change
3784  // property - specifies the property name
3785  // type - the type of the property; the X server does not
3786  // interpret the type but simply passes it back to
3787  // an application that might ask about the window
3788  // properties
3789  // data - the property data
3790  // len - the length of the specified data format
3791  //End of comment.
3792 
3793  //TGX11 always calls XChangeProperty with PropModeReplace.
3794  //I simply reset the property (or create a new one).
3795 
3796  if (!windowID) //From TGWin32.
3797  return;
3798 
3799  if (!data || !len) //From TGWin32.
3800  return;
3801 
3802  assert(!fPimpl->IsRootWindow(windowID) &&
3803  "ChangeProperty, parameter 'windowID' is root window");
3804  assert(fPimpl->GetDrawable(windowID).fIsPixmap == NO &&
3805  "ChangeProperty, parameter 'windowID' is not a valid window id");
3806  assert(propertyID && propertyID <= fAtomToName.size() &&
3807  "ChangeProperty, parameter 'propertyID' is not a valid atom");
3808 
3809  const Util::AutoreleasePool pool;
3810 
3811  const std::string &atomString = fAtomToName[propertyID - 1];
3812 
3813  NSObject<X11Window> * const window = fPimpl->GetWindow(windowID);
3814  [window setProperty : atomString.c_str() data : data size : len forType : type format : 8];
3815  //ROOT ignores PropertyNotify events.
3816 }
3817 
3818 //______________________________________________________________________________
3819 void TGCocoa::ChangeProperties(Window_t windowID, Atom_t propertyID, Atom_t type,
3820  Int_t format, UChar_t *data, Int_t len)
3821 {
3822  //Comment from TVirtualX:
3823  // Alters the property for the specified window and causes the X server
3824  // to generate a PropertyNotify event on that window.
3825  //End of comment.
3826 
3827  //TGX11 always calls XChangeProperty with PropModeReplace.
3828  //I simply reset the property (or create a new one).
3829 
3830  if (!windowID)//From TGWin32.
3831  return;
3832 
3833  if (!data || !len)//From TGWin32.
3834  return;
3835 
3836  assert(!fPimpl->IsRootWindow(windowID) &&
3837  "ChangeProperties, parameter 'windowID' is root window");
3838  assert(fPimpl->GetDrawable(windowID).fIsPixmap == NO &&
3839  "ChangeProperties, parameter 'windowID' is not a valid window id");
3840  assert(propertyID && propertyID <= fAtomToName.size() &&
3841  "ChangeProperties, parameter 'propertyID' is not a valid atom");
3842 
3843  const Util::AutoreleasePool pool;
3844 
3845  const std::string &atomName = fAtomToName[propertyID - 1];
3846 
3847  NSObject<X11Window> * const window = fPimpl->GetWindow(windowID);
3848  [window setProperty : atomName.c_str() data : data
3849  size : len forType : type format : format];
3850  //No property notify, ROOT does not know about this.
3851 }
3852 
3853 //______________________________________________________________________________
3854 void TGCocoa::DeleteProperty(Window_t windowID, Atom_t &propertyID)
3855 {
3856  //Comment from TVirtualX:
3857  // Deletes the specified property only if the property was defined on the
3858  // specified window and causes the X server to generate a PropertyNotify
3859  // event on the window unless the property does not exist.
3860  //End of comment.
3861 
3862  if (!windowID)//Can this happen?
3863  return;
3864 
3865  //Strange signature - why propertyID is a reference?
3866  assert(!fPimpl->IsRootWindow(windowID) &&
3867  "DeleteProperty, parameter 'windowID' is root window");
3868  assert(fPimpl->GetDrawable(windowID).fIsPixmap == NO &&
3869  "DeleteProperty, parameter 'windowID' is not a valid window");
3870  assert(propertyID && propertyID <= fAtomToName.size() &&
3871  "DeleteProperty, parameter 'propertyID' is not a valid atom");
3872 
3873  const std::string &atomString = fAtomToName[propertyID - 1];
3874  [fPimpl->GetWindow(windowID) removeProperty : atomString.c_str()];
3875 }
3876 
3877 //______________________________________________________________________________
3878 void TGCocoa::SetDNDAware(Window_t windowID, Atom_t *typeList)
3879 {
3880  //Comment from TVirtaulX:
3881  // Add XdndAware property and the list of drag and drop types to the
3882  // Window win.
3883  //End of comment.
3884 
3885 
3886  //TGX11 first replaces XdndAware property for a windowID, and then appends atoms from a typelist.
3887  //I simply put all data for a property into a vector and set the property (either creating
3888  //a new property or replacing the existing).
3889 
3890  assert(windowID > fPimpl->GetRootWindowID() &&
3891  "SetDNDAware, parameter 'windowID' is not a valid window id");
3892  assert(fPimpl->GetDrawable(windowID).fIsPixmap == NO &&
3893  "SetDNDAware, parameter 'windowID' is not a window");
3894 
3895  const Util::AutoreleasePool pool;
3896 
3897  QuartzView * const view = (QuartzView *)fPimpl->GetWindow(windowID).fContentView;
3898  NSArray * const supportedTypes = [NSArray arrayWithObjects : NSFilenamesPboardType, nil];//In a pool.
3899 
3900  //Do this for Cocoa - to make it possible to drag something to a
3901  //ROOT's window (also this will change cursor shape while dragging).
3902  [view registerForDraggedTypes : supportedTypes];
3903  //Declared property - for convenience, not to check atoms/shmatoms or X11 properties.
3904  view.fIsDNDAware = YES;
3905 
3906  FindAtom("XdndAware", true);//Add it, if not yet.
3907  const Atom_t xaAtomAtom = FindAtom("XA_ATOM", false);
3908 
3909  assert(xaAtomAtom == 4 && "SetDNDAware, XA_ATOM is not defined");//This is a predefined atom.
3910 
3911  //ROOT's GUI uses Atom_t, which is unsigned long, and it's 64-bit.
3912  //While calling XChangeProperty, it passes the address of this typelist
3913  //and format is ... 32. I have to pack data into unsigned and force the size:
3914  assert(sizeof(unsigned) == 4 && "SetDNDAware, sizeof(unsigned) must be 4");
3915 
3916  std::vector<unsigned> propertyData;
3917  propertyData.push_back(4);//This '4' is from TGX11 (is it XA_ATOM???)
3918 
3919  if (typeList) {
3920  for (unsigned i = 0; typeList[i]; ++i)
3921  propertyData.push_back(unsigned(typeList[i]));//hehe.
3922  }
3923 
3924  [view setProperty : "XdndAware" data : (unsigned char *)&propertyData[0]
3925  size : propertyData.size() forType : xaAtomAtom format : 32];
3926 }
3927 
3928 //______________________________________________________________________________
3929 Bool_t TGCocoa::IsDNDAware(Window_t windowID, Atom_t * /*typeList*/)
3930 {
3931  //Checks if the Window is DND aware. typeList is ignored.
3932 
3933  if (windowID <= fPimpl->GetRootWindowID())//kNone or root.
3934  return kFALSE;
3935 
3936  assert(fPimpl->GetDrawable(windowID).fIsPixmap == NO &&
3937  "IsDNDAware, windowID parameter is not a window");
3938 
3939  QuartzView * const view = (QuartzView *)fPimpl->GetWindow(windowID).fContentView;
3940  return view.fIsDNDAware;
3941 }
3942 
3943 //______________________________________________________________________________
3944 void TGCocoa::SetTypeList(Window_t, Atom_t, Atom_t *)
3945 {
3946  // Add the list of drag and drop types to the Window win.
3947  //It's never called from GUI.
3948  ::Warning("SetTypeList", "Not implemented");
3949 }
3950 
3951 //______________________________________________________________________________
3952 Window_t TGCocoa::FindRWindow(Window_t winID, Window_t dragWinID, Window_t inputWinID, int x, int y, int maxDepth)
3953 {
3954  //Comment from TVirtualX:
3955 
3956  // Recursively search in the children of Window for a Window which is at
3957  // location x, y and is DND aware, with a maximum depth of maxd.
3958  // Ignore dragwin and input (???)
3959  //End of comment from TVirtualX.
3960 
3961 
3962  //Now my comments. The name of this function, as usually, says nothing about what it does.
3963  //It's searching for some window, probably child of winID, or may be winID itself(?) and
3964  //window must be DND aware. So the name should be FindDNDAwareWindowRecursively or something like this.
3965 
3966  //This function is not documented, comments suck as soon as they are simply wrong - the
3967  //first return statement in X11 version contradicts with comments
3968  //about child. Since X11 version is more readable, I'm reproducing X11 version here,
3969  //and ... my code can't be wrong, since there is nothing right about this function.
3970 
3971  NSView<X11Window> * const testView = X11::FindDNDAwareViewInPoint(
3972  fPimpl->IsRootWindow(winID) ? nil : fPimpl->GetWindow(winID).fContentView,
3973  dragWinID, inputWinID, x, y, maxDepth);
3974  if (testView)
3975  return testView.fID;
3976 
3977  return kNone;
3978 }
3979 
3980 #pragma mark - Noops.
3981 
3982 //______________________________________________________________________________
3983 UInt_t TGCocoa::ExecCommand(TGWin32Command * /*code*/)
3984 {
3985  // Executes the command "code" coming from the other threads (Win32)
3986  return 0;
3987 }
3988 
3989 //______________________________________________________________________________
3990 Int_t TGCocoa::GetDoubleBuffer(Int_t /*wid*/)
3991 {
3992  // Queries the double buffer value for the window "wid".
3993  return 0;
3994 }
3995 
3996 //______________________________________________________________________________
3997 void TGCocoa::GetCharacterUp(Float_t &chupx, Float_t &chupy)
3998 {
3999  // Returns character up vector.
4000  chupx = chupy = 0.f;
4001 }
4002 
4003 //______________________________________________________________________________
4004 Pixmap_t TGCocoa::ReadGIF(Int_t /*x0*/, Int_t /*y0*/, const char * /*file*/, Window_t /*id*/)
4005 {
4006  // If id is NULL - loads the specified gif file at position [x0,y0] in the
4007  // current window. Otherwise creates pixmap from gif file
4008 
4009  return kNone;
4010 }
4011 
4012 //______________________________________________________________________________
4013 Int_t TGCocoa::RequestLocator(Int_t /*mode*/, Int_t /*ctyp*/, Int_t &/*x*/, Int_t &/*y*/)
4014 {
4015  // Requests Locator position.
4016  // x,y - cursor position at moment of button press (output)
4017  // ctyp - cursor type (input)
4018  // ctyp = 1 tracking cross
4019  // ctyp = 2 cross-hair
4020  // ctyp = 3 rubber circle
4021  // ctyp = 4 rubber band
4022  // ctyp = 5 rubber rectangle
4023  //
4024  // mode - input mode
4025  // mode = 0 request
4026  // mode = 1 sample
4027  //
4028  // The returned value is:
4029  // in request mode:
4030  // 1 = left is pressed
4031  // 2 = middle is pressed
4032  // 3 = right is pressed
4033  // in sample mode:
4034  // 11 = left is released
4035  // 12 = middle is released
4036  // 13 = right is released
4037  // -1 = nothing is pressed or released
4038  // -2 = leave the window
4039  // else = keycode (keyboard is pressed)
4040 
4041  return 0;
4042 }
4043 
4044 //______________________________________________________________________________
4045 Int_t TGCocoa::RequestString(Int_t /*x*/, Int_t /*y*/, char * /*text*/)
4046 {
4047  // Requests string: text is displayed and can be edited with Emacs-like
4048  // keybinding. Returns termination code (0 for ESC, 1 for RETURN)
4049  //
4050  // x,y - position where text is displayed
4051  // text - displayed text (as input), edited text (as output)
4052  return 0;
4053 }
4054 
4055 //______________________________________________________________________________
4056 void TGCocoa::SetCharacterUp(Float_t /*chupx*/, Float_t /*chupy*/)
4057 {
4058  // Sets character up vector.
4059 }
4060 
4061 //______________________________________________________________________________
4062 void TGCocoa::SetClipOFF(Int_t /*wid*/)
4063 {
4064  // Turns off the clipping for the window "wid".
4065 }
4066 
4067 //______________________________________________________________________________
4068 void TGCocoa::SetClipRegion(Int_t /*wid*/, Int_t /*x*/, Int_t /*y*/, UInt_t /*w*/, UInt_t /*h*/)
4069 {
4070  // Sets clipping region for the window "wid".
4071  //
4072  // wid - window indentifier
4073  // x, y - origin of clipping rectangle
4074  // w, h - the clipping rectangle dimensions
4075 
4076 }
4077 
4078 //______________________________________________________________________________
4079 void TGCocoa::SetTextMagnitude(Float_t /*mgn*/)
4080 {
4081  // Sets the current text magnification factor to "mgn"
4082 }
4083 
4084 //______________________________________________________________________________
4085 void TGCocoa::Sync(Int_t /*mode*/)
4086 {
4087  // Set synchronisation on or off.
4088  // mode : synchronisation on/off
4089  // mode=1 on
4090  // mode<>0 off
4091 }
4092 
4093 //______________________________________________________________________________
4094 void TGCocoa::Warp(Int_t ix, Int_t iy, Window_t winID)
4095 {
4096  // Sets the pointer position.
4097  // ix - new X coordinate of pointer
4098  // iy - new Y coordinate of pointer
4099  // Coordinates are relative to the origin of the window id
4100  // or to the origin of the current window if id == 0.
4101 
4102  if (!winID)
4103  return;
4104 
4105  NSPoint newCursorPosition = {};
4106  newCursorPosition.x = ix;
4107  newCursorPosition.y = iy;
4108 
4109  if (fPimpl->GetRootWindowID() == winID) {
4110  //Suddenly .... top-left - based!
4111  newCursorPosition.x = X11::GlobalXROOTToCocoa(newCursorPosition.x);
4112  } else {
4113  assert(fPimpl->GetDrawable(winID).fIsPixmap == NO &&
4114  "Warp, drawable is not a window");
4115  newCursorPosition = X11::TranslateToScreen(fPimpl->GetWindow(winID).fContentView,
4116  newCursorPosition);
4117  }
4118 
4119  CGWarpMouseCursorPosition(NSPointToCGPoint(newCursorPosition));
4120 }
4121 
4122 //______________________________________________________________________________
4123 Int_t TGCocoa::WriteGIF(char * /*name*/)
4124 {
4125  // Writes the current window into GIF file.
4126  // Returns 1 in case of success, 0 otherwise.
4127 
4128  return 0;
4129 }
4130 
4131 //______________________________________________________________________________
4132 void TGCocoa::WritePixmap(Int_t /*wid*/, UInt_t /*w*/, UInt_t /*h*/, char * /*pxname*/)
4133 {
4134  // Writes the pixmap "wid" in the bitmap file "pxname".
4135  //
4136  // wid - the pixmap address
4137  // w, h - the width and height of the pixmap.
4138  // pxname - the file name
4139 }
4140 
4141 //______________________________________________________________________________
4142 Bool_t TGCocoa::NeedRedraw(ULong_t /*tgwindow*/, Bool_t /*force*/)
4143 {
4144  // Notify the low level GUI layer ROOT requires "tgwindow" to be
4145  // updated
4146  //
4147  // Returns kTRUE if the notification was desirable and it was sent
4148  //
4149  // At the moment only Qt4 layer needs that
4150  //
4151  // One needs explicitly cast the first parameter to TGWindow to make
4152  // it working in the implementation.
4153  //
4154  // One needs to process the notification to confine
4155  // all paint operations within "expose" / "paint" like low level event
4156  // or equivalent
4157 
4158  return kFALSE;
4159 }
4160 
4161 //______________________________________________________________________________
4162 Bool_t TGCocoa::CreatePictureFromFile(Drawable_t /*wid*/,
4163  const char * /*filename*/,
4164  Pixmap_t &/*pict*/,
4165  Pixmap_t &/*pict_mask*/,
4166  PictureAttributes_t &/*attr*/)
4167 {
4168  // Creates a picture pict from data in file "filename". The picture
4169  // attributes "attr" are used for input and output. Returns kTRUE in
4170  // case of success, kFALSE otherwise. If the mask "pict_mask" does not
4171  // exist it is set to kNone.
4172 
4173  return kFALSE;
4174 }
4175 
4176 //______________________________________________________________________________
4177 Bool_t TGCocoa::CreatePictureFromData(Drawable_t /*wid*/, char ** /*data*/,
4178  Pixmap_t &/*pict*/,
4179  Pixmap_t &/*pict_mask*/,
4180  PictureAttributes_t & /*attr*/)
4181 {
4182  // Creates a picture pict from data in bitmap format. The picture
4183  // attributes "attr" are used for input and output. Returns kTRUE in
4184  // case of success, kFALSE otherwise. If the mask "pict_mask" does not
4185  // exist it is set to kNone.
4186 
4187  return kFALSE;
4188 }
4189 //______________________________________________________________________________
4190 Bool_t TGCocoa::ReadPictureDataFromFile(const char * /*filename*/, char *** /*ret_data*/)
4191 {
4192  // Reads picture data from file "filename" and store it in "ret_data".
4193  // Returns kTRUE in case of success, kFALSE otherwise.
4194 
4195  return kFALSE;
4196 }
4197 
4198 //______________________________________________________________________________
4199 void TGCocoa::DeletePictureData(void * /*data*/)
4200 {
4201  // Delete picture data created by the function ReadPictureDataFromFile.
4202 }
4203 
4204 //______________________________________________________________________________
4205 void TGCocoa::SetDashes(GContext_t /*gc*/, Int_t /*offset*/, const char * /*dash_list*/, Int_t /*n*/)
4206 {
4207  // Sets the dash-offset and dash-list attributes for dashed line styles
4208  // in the specified GC. There must be at least one element in the
4209  // specified dash_list. The initial and alternating elements (second,
4210  // fourth, and so on) of the dash_list are the even dashes, and the
4211  // others are the odd dashes. Each element in the "dash_list" array
4212  // specifies the length (in pixels) of a segment of the pattern.
4213  //
4214  // gc - specifies the GC (see GCValues_t structure)
4215  // offset - the phase of the pattern for the dashed line-style you
4216  // want to set for the specified GC.
4217  // dash_list - the dash-list for the dashed line-style you want to set
4218  // for the specified GC
4219  // n - the number of elements in dash_list
4220  // (see also the GCValues_t structure)
4221 }
4222 
4223 //______________________________________________________________________________
4224 void TGCocoa::Bell(Int_t /*percent*/)
4225 {
4226  // Sets the sound bell. Percent is loudness from -100% .. 100%.
4227 }
4228 
4229 //______________________________________________________________________________
4230 void TGCocoa::WMDeleteNotify(Window_t /*wid*/)
4231 {
4232  // Tells WM to send message when window is closed via WM.
4233 }
4234 
4235 //______________________________________________________________________________
4236 void TGCocoa::SetClipRectangles(GContext_t /*gc*/, Int_t /*x*/, Int_t /*y*/,
4237  Rectangle_t * /*recs*/, Int_t /*n*/)
4238 {
4239  // Sets clipping rectangles in graphics context. [x,y] specify the origin
4240  // of the rectangles. "recs" specifies an array of rectangles that define
4241  // the clipping mask and "n" is the number of rectangles.
4242  // (see also the GCValues_t structure)
4243 }
4244 
4245 //______________________________________________________________________________
4246 Region_t TGCocoa::CreateRegion()
4247 {
4248  // Creates a new empty region.
4249 
4250  return 0;
4251 }
4252 
4253 //______________________________________________________________________________
4254 void TGCocoa::DestroyRegion(Region_t /*reg*/)
4255 {
4256  // Destroys the region "reg".
4257 }
4258 
4259 //______________________________________________________________________________
4260 void TGCocoa::UnionRectWithRegion(Rectangle_t * /*rect*/, Region_t /*src*/, Region_t /*dest*/)
4261 {
4262  // Updates the destination region from a union of the specified rectangle
4263  // and the specified source region.
4264  //
4265  // rect - specifies the rectangle
4266  // src - specifies the source region to be used
4267  // dest - returns the destination region
4268 }
4269 
4270 //______________________________________________________________________________
4271 Region_t TGCocoa::PolygonRegion(Point_t * /*points*/, Int_t /*np*/, Bool_t /*winding*/)
4272 {
4273  // Returns a region for the polygon defined by the points array.
4274  //
4275  // points - specifies an array of points
4276  // np - specifies the number of points in the polygon
4277  // winding - specifies the winding-rule is set (kTRUE) or not(kFALSE)
4278 
4279  return 0;
4280 }
4281 
4282 //______________________________________________________________________________
4283 void TGCocoa::UnionRegion(Region_t /*rega*/, Region_t /*regb*/, Region_t /*result*/)
4284 {
4285  // Computes the union of two regions.
4286  //
4287  // rega, regb - specify the two regions with which you want to perform
4288  // the computation
4289  // result - returns the result of the computation
4290 
4291 }
4292 
4293 //______________________________________________________________________________
4294 void TGCocoa::IntersectRegion(Region_t /*rega*/, Region_t /*regb*/, Region_t /*result*/)
4295 {
4296  // Computes the intersection of two regions.
4297  //
4298  // rega, regb - specify the two regions with which you want to perform
4299  // the computation
4300  // result - returns the result of the computation
4301 }
4302 
4303 //______________________________________________________________________________
4304 void TGCocoa::SubtractRegion(Region_t /*rega*/, Region_t /*regb*/, Region_t /*result*/)
4305 {
4306  // Subtracts regb from rega and stores the results in result.
4307 }
4308 
4309 //______________________________________________________________________________
4310 void TGCocoa::XorRegion(Region_t /*rega*/, Region_t /*regb*/, Region_t /*result*/)
4311 {
4312  // Calculates the difference between the union and intersection of
4313  // two regions.
4314  //
4315  // rega, regb - specify the two regions with which you want to perform
4316  // the computation
4317  // result - returns the result of the computation
4318 
4319 }
4320 
4321 //______________________________________________________________________________
4322 Bool_t TGCocoa::EmptyRegion(Region_t /*reg*/)
4323 {
4324  // Returns kTRUE if the region reg is empty.
4325 
4326  return kFALSE;
4327 }
4328 
4329 //______________________________________________________________________________
4330 Bool_t TGCocoa::PointInRegion(Int_t /*x*/, Int_t /*y*/, Region_t /*reg*/)
4331 {
4332  // Returns kTRUE if the point [x, y] is contained in the region reg.
4333 
4334  return kFALSE;
4335 }
4336 
4337 //______________________________________________________________________________
4338 Bool_t TGCocoa::EqualRegion(Region_t /*rega*/, Region_t /*regb*/)
4339 {
4340  // Returns kTRUE if the two regions have the same offset, size, and shape.
4341 
4342  return kFALSE;
4343 }
4344 
4345 //______________________________________________________________________________
4346 void TGCocoa::GetRegionBox(Region_t /*reg*/, Rectangle_t * /*rect*/)
4347 {
4348  // Returns smallest enclosing rectangle.
4349 }
4350 
4351 #pragma mark - Details and aux. functions.
4352 
4353 //______________________________________________________________________________
4354 ROOT::MacOSX::X11::EventTranslator *TGCocoa::GetEventTranslator()const
4355 {
4356  return &fPimpl->fX11EventTranslator;
4357 }
4358 
4359 //______________________________________________________________________________
4360 ROOT::MacOSX::X11::CommandBuffer *TGCocoa::GetCommandBuffer()const
4361 {
4362  return &fPimpl->fX11CommandBuffer;
4363 }
4364 
4365 //______________________________________________________________________________
4366 void TGCocoa::CocoaDrawON()
4367 {
4368  ++fCocoaDraw;
4369 }
4370 
4371 //______________________________________________________________________________
4372 void TGCocoa::CocoaDrawOFF()
4373 {
4374  assert(fCocoaDraw > 0 && "CocoaDrawOFF, was already off");
4375  --fCocoaDraw;
4376 }
4377 
4378 //______________________________________________________________________________
4379 bool TGCocoa::IsCocoaDraw()const
4380 {
4381  return bool(fCocoaDraw);
4382 }
4383 
4384 //______________________________________________________________________________
4385 void *TGCocoa::GetCurrentContext()
4386 {
4387  NSObject<X11Drawable> * const drawable = fPimpl->GetDrawable(fSelectedDrawable);
4388  if (!drawable.fIsPixmap) {
4389  Error("GetCurrentContext", "TCanvas/TPad's internal error,"
4390  " selected drawable is not a pixmap!");
4391  return 0;
4392  }
4393 
4394  return drawable.fContext;
4395 }
4396 
4397 //______________________________________________________________________________
4398 bool TGCocoa::MakeProcessForeground()
4399 {
4400  //We start ROOT in a terminal window, so it's considered as a
4401  //background process. Background process has a lot of problems
4402  //if it tries to create and manage windows.
4403  //So, first time we convert process to foreground, next time
4404  //we make it front.
4405 
4406  if (!fForegroundProcess) {
4407  ProcessSerialNumber psn = {0, kCurrentProcess};
4408 
4409  const OSStatus res1 = TransformProcessType(&psn, kProcessTransformToForegroundApplication);
4410 
4411  //When TGCocoa's functions are called from the python (Apple's system version),
4412  //TransformProcessType fails with paramErr (looks like process is _already_ foreground),
4413  //why is it a paramErr - I've no idea.
4414  if (res1 != noErr && res1 != paramErr) {
4415  Error("MakeProcessForeground", "TransformProcessType failed with code %d", int(res1));
4416  return false;
4417  }
4418 #ifdef MAC_OS_X_VERSION_10_9
4419  //Instead of quite transparent Carbon calls we now have another black-box function.
4420  [[NSApplication sharedApplication] activateIgnoringOtherApps : YES];
4421 #else
4422  const OSErr res2 = SetFrontProcess(&psn);
4423  if (res2 != noErr) {
4424  Error("MakeProcessForeground", "SetFrontProcess failed with code %d", res2);
4425  return false;
4426  }
4427 #endif
4428 
4429  fForegroundProcess = true;
4430  } else {
4431 #ifdef MAC_OS_X_VERSION_10_9
4432  //Instead of quite transparent Carbon calls we now have another black-box function.
4433  [[NSApplication sharedApplication] activateIgnoringOtherApps : YES];
4434 #else
4435  ProcessSerialNumber psn = {};
4436 
4437  OSErr res = GetCurrentProcess(&psn);
4438  if (res != noErr) {
4439  Error("MakeProcessForeground", "GetCurrentProcess failed with code %d", res);
4440  return false;
4441  }
4442 
4443  res = SetFrontProcess(&psn);
4444  if (res != noErr) {
4445  Error("MapProcessForeground", "SetFrontProcess failed with code %d", res);
4446  return false;
4447  }
4448 #endif
4449  }
4450 
4451  return true;
4452 }
4453 
4454 //______________________________________________________________________________
4455 Atom_t TGCocoa::FindAtom(const std::string &atomName, bool addIfNotFound)
4456 {
4457  const std::map<std::string, Atom_t>::const_iterator it = fNameToAtom.find(atomName);
4458 
4459  if (it != fNameToAtom.end())
4460  return it->second;
4461  else if (addIfNotFound) {
4462  //Create a new atom.
4463  fAtomToName.push_back(atomName);
4464  fNameToAtom[atomName] = Atom_t(fAtomToName.size());
4465 
4466  return Atom_t(fAtomToName.size());
4467  }
4468 
4469  return kNone;
4470 }
4471 
4472 //______________________________________________________________________________
4473 void TGCocoa::SetApplicationIcon()
4474 {
4475  if (gEnv) {
4476  const char * const iconDirectoryPath = gEnv->GetValue("Gui.IconPath",TROOT::GetIconPath());
4477  if (iconDirectoryPath) {
4478  const Util::ScopedArray<char> fileName(gSystem->Which(iconDirectoryPath, "Root6Icon.png", kReadPermission));
4479  if (fileName.Get()) {
4480  const Util::AutoreleasePool pool;
4481  //Aha, ASCII ;) do not install ROOT in ...
4482  NSString *cocoaStr = [NSString stringWithCString : fileName.Get() encoding : NSASCIIStringEncoding];
4483  NSImage *image = [[[NSImage alloc] initWithContentsOfFile : cocoaStr] autorelease];
4484  [NSApp setApplicationIconImage : image];
4485  }
4486  }
4487  }
4488 }