Logo ROOT   master
Reference Guide
QuartzWindow.mm
Go to the documentation of this file.
1 // @(#)root/graf2d:$Id$
2 // Author: Timur Pocheptsov 16/02/2012
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 DEBUG_ROOT_COCOA
13 
14 //#define NDEBUG
15 
16 #ifdef DEBUG_ROOT_COCOA
17 #include <iostream>
18 #include <fstream>
19 
20 #include "TClass.h"
21 #endif
22 
23 #include <algorithm>
24 #include <stdexcept>
25 #include <cassert>
26 #include <vector>
27 
28 #include <Availability.h>
29 
30 #include "ROOTOpenGLView.h"
31 #include "CocoaConstants.h"
32 #include "QuartzWindow.h"
33 #include "QuartzPixmap.h"
34 #include "QuartzUtils.h"
35 #include "CocoaUtils.h"
36 #include "RConfigure.h"
37 #include "X11Colors.h"
38 #include "X11Buffer.h"
39 #include "TGWindow.h"
40 #include "TGClient.h"
41 #include "TSystem.h"
42 #include "TGCocoa.h"
43 #include "TROOT.h"
44 #include "TGTextView.h"
45 #include "TGView.h"
46 #include "TGCanvas.h"
47 
48 
49 namespace ROOT {
50 namespace MacOSX {
51 namespace X11 {
52 
53 #pragma mark - Create a window or a view.
54 
55 //______________________________________________________________________________
57  UInt_t clss, void */*visual*/, SetWindowAttributes_t *attr, UInt_t)
58 {
59  using namespace Details;
60 
61  NSRect winRect = {};
62  winRect.origin.x = GlobalXROOTToCocoa(x);
63  winRect.origin.y = GlobalYROOTToCocoa(y + h);
64  winRect.size.width = w;
65  winRect.size.height = h;
66 
67  const NSUInteger styleMask = kTitledWindowMask | kClosableWindowMask |
69 
70  QuartzWindow * const newWindow = [[QuartzWindow alloc] initWithContentRect : winRect
71  styleMask : styleMask
72  backing : NSBackingStoreBuffered
73  defer : YES
74  windowAttributes : attr];
75  if (!newWindow)
76  throw std::runtime_error("CreateTopLevelWindow failed");
77 
78  newWindow.fDepth = depth;
79  newWindow.fClass = clss;
80 
81  return newWindow;
82 }
83 
84 //______________________________________________________________________________
85 QuartzView *CreateChildView(QuartzView * /*parent*/, Int_t x, Int_t y, UInt_t w, UInt_t h, UInt_t /*border*/, Int_t /*depth*/,
86  UInt_t /*clss*/, void * /*visual*/, SetWindowAttributes_t *attr, UInt_t /*wtype*/)
87 {
88  NSRect viewRect = {};
89  viewRect.origin.x = x;
90  viewRect.origin.y = y;
91  viewRect.size.width = w;
92  viewRect.size.height = h;
93 
94  QuartzView * const view = [[QuartzView alloc] initWithFrame : viewRect windowAttributes : attr];
95  if (!view)
96  throw std::runtime_error("CreateChildView failed");
97 
98  return view;
99 }
100 
101 #pragma mark - root window (does not really exist, it's our desktop built of all screens).
102 
103 //______________________________________________________________________________
105 {
106  //'root' window does not exist, but we can request its attributes.
107  assert(attr != 0 && "GetRootWindowAttributes, parameter 'attr' is null");
108 
109 
110  NSArray * const screens = [NSScreen screens];
111  assert(screens != nil && "screens array is nil");
112  NSScreen * const mainScreen = [screens objectAtIndex : 0];
113  assert(mainScreen != nil && "screen with index 0 is nil");
114 
115  *attr = WindowAttributes_t();
116 
117  assert(dynamic_cast<TGCocoa *>(gVirtualX) &&
118  "GetRootWindowAttributes, gVirtualX is either null or has a wrong type");
119 
120  TGCocoa * const gCocoa = static_cast<TGCocoa *>(gVirtualX);
121 
122  const Rectangle &frame = gCocoa->GetDisplayGeometry();
123 
124  attr->fX = 0;
125  attr->fY = 0;
126  attr->fWidth = frame.fWidth;
127  attr->fHeight = frame.fHeight;
128  attr->fBorderWidth = 0;
129  attr->fYourEventMask = 0;
130  attr->fAllEventMasks = 0;//???
131 
132  attr->fDepth = NSBitsPerPixelFromDepth([mainScreen depth]);
133  attr->fVisual = 0;
134  attr->fRoot = 0;
135 }
136 
137 
138 #pragma mark - Coordinate conversions.
139 
140 //______________________________________________________________________________
141 NSPoint ConvertPointFromBaseToScreen(NSWindow *window, NSPoint windowPoint)
142 {
143  assert(window != nil && "ConvertPointFromBaseToScreen, parameter 'window' is nil");
144 
145  //I have no idea why apple deprecated function for a point conversion and requires rect conversion,
146  //point conversion seems to produce wrong results with HiDPI.
147 
148  NSRect tmpRect = {};
149  tmpRect.origin = windowPoint;
150  tmpRect.size = NSMakeSize(1., 1.);//This is strange size :) But if they require rect, 0,0 - will not work?
151  tmpRect = [window convertRectToScreen : tmpRect];
152 
153  return tmpRect.origin;
154 }
155 
156 //______________________________________________________________________________
157 NSPoint ConvertPointFromScreenToBase(NSPoint screenPoint, NSWindow *window)
158 {
159  assert(window != nil && "ConvertPointFromScreenToBase, parameter 'window' is nil");
160 
161  //I have no idea why apple deprecated function for a point conversion and requires rect conversion,
162  //point conversion seems to produce wrong results with HiDPI.
163 
164  NSRect tmpRect = {};
165  tmpRect.origin = screenPoint;
166  tmpRect.size = NSMakeSize(1., 1.);
167  tmpRect = [window convertRectFromScreen : tmpRect];
168 
169  return tmpRect.origin;
170 }
171 
172 //______________________________________________________________________________
173 int GlobalYCocoaToROOT(CGFloat yCocoa)
174 {
175  //We can have several physical displays and thus several NSScreens in some arbitrary order.
176  //With Cocoa, some screens can have negative coordinates - to the left ro down to the primary
177  //screen (whatever it means). With X11 (XQuartz) though it's always 0,0.
178 
179  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
180  "GlobalYCocoaToROOT, gVirtualX is either nul or has a wrong type");
181 
182  const Rectangle frame = ((TGCocoa *)gVirtualX)->GetDisplayGeometry();
183 
184  return int(frame.fHeight - (yCocoa - frame.fY));
185 }
186 
187 //______________________________________________________________________________
188 int GlobalXCocoaToROOT(CGFloat xCocoa)
189 {
190  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
191  "GlobalXCocoaToROOT, gVirtualX is either nul or has a wrong type");
192  const Rectangle frame = ((TGCocoa *)gVirtualX)->GetDisplayGeometry();
193  //With X11 coordinate space always starts from 0, 0
194  return int(xCocoa - frame.fX);
195 }
196 
197 //______________________________________________________________________________
198 int GlobalYROOTToCocoa(CGFloat yROOT)
199 {
200  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
201  "GlobalYROOTToCocoa, gVirtualX is either nul or has a wrong type");
202  const Rectangle frame = ((TGCocoa *)gVirtualX)->GetDisplayGeometry();
203 
204  return int(frame.fY + (frame.fHeight - yROOT));
205 }
206 
207 //______________________________________________________________________________
208 int GlobalXROOTToCocoa(CGFloat xROOT)
209 {
210  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
211  "GlobalXROOTToCocoa, gVirtualX is either nul or has a wrong type");
212  const Rectangle frame = ((TGCocoa *)gVirtualX)->GetDisplayGeometry();
213  //With X11 coordinate space always starts from 0, 0
214  return int(frame.fX + xROOT);
215 }
216 
217 //______________________________________________________________________________
218 int LocalYCocoaToROOT(NSView<X11Window> *parentView, CGFloat yCocoa)
219 {
220  assert(parentView != nil && "LocalYCocoaToROOT, parent view is nil");
221 
222  return int(parentView.frame.size.height - yCocoa);
223 }
224 
225 //______________________________________________________________________________
226 int LocalYROOTToCocoa(NSView<X11Window> *parentView, CGFloat yROOT)
227 {
228  //:)
229  assert(parentView != nil && "LocalYROOTToCocoa, parent view is nil");
230 
231  return int(parentView.frame.size.height - yROOT);
232 }
233 
234 
235 //______________________________________________________________________________
236 int LocalYROOTToCocoa(NSObject<X11Drawable> *drawable, CGFloat yROOT)
237 {
238  //:)
239  assert(drawable != nil && "LocalYROOTToCocoa, drawable is nil");
240 
241  return int(drawable.fHeight - yROOT);
242 }
243 
244 //______________________________________________________________________________
245 NSPoint TranslateToScreen(NSView<X11Window> *from, NSPoint point)
246 {
247  assert(from != nil && "TranslateToScreen, parameter 'from' is nil");
248 
249  const NSPoint winPoint = [from convertPoint : point toView : nil];
250 
251  NSPoint screenPoint = ConvertPointFromBaseToScreen([from window], winPoint);
252  screenPoint.x = GlobalXCocoaToROOT(screenPoint.x);
253  screenPoint.y = GlobalYCocoaToROOT(screenPoint.y);
254 
255  return screenPoint;
256 }
257 
258 //______________________________________________________________________________
259 NSPoint TranslateFromScreen(NSPoint point, NSView<X11Window> *to)
260 {
261  assert(to != nil && "TranslateFromScreen, parameter 'to' is nil");
262 
263  point.x = GlobalXROOTToCocoa(point.x);
264  point.y = GlobalYROOTToCocoa(point.y);
265  point = ConvertPointFromScreenToBase(point, [to window]);
266 
267  return [to convertPoint : point fromView : nil];
268 }
269 
270 //______________________________________________________________________________
271 NSPoint TranslateCoordinates(NSView<X11Window> *from, NSView<X11Window> *to, NSPoint sourcePoint)
272 {
273  //Both views are valid.
274  assert(from != nil && "TranslateCoordinates, parameter 'from' is nil");
275  assert(to != nil && "TranslateCoordinates, parameter 'to' is nil");
276 
277  if ([from window] == [to window]) {
278  //Both views are in the same window.
279  return [to convertPoint : sourcePoint fromView : from];
280  } else {
281  //May be, I can do it in one call, but it's not obvious for me
282  //what is 'pixel aligned backing store coordinates' and
283  //if they are the same as screen coordinates.
284 
285  //Many thanks to Apple for deprecated functions!!!
286 
287  const NSPoint win1Point = [from convertPoint : sourcePoint toView : nil];
288  const NSPoint screenPoint = ConvertPointFromBaseToScreen([from window], win1Point);
289  const NSPoint win2Point = ConvertPointFromScreenToBase(screenPoint, [to window]);
290 
291  return [to convertPoint : win2Point fromView : nil];
292  }
293 }
294 
295 //______________________________________________________________________________
297 {
298  assert(view != nil && "ScreenPointIsInView, parameter 'view' is nil");
299 
300  NSPoint point = {};
301  point.x = x, point.y = y;
302  point = TranslateFromScreen(point, view);
303  const NSRect viewFrame = view.frame;
304 
305  if (point.x < 0 || point.x > viewFrame.size.width)
306  return false;
307  if (point.y < 0 || point.y > viewFrame.size.height)
308  return false;
309 
310  return true;
311 }
312 
313 #pragma mark - Different FindView/Window functions iterating on the ROOT's windows/views.
314 
315 //______________________________________________________________________________
317 {
318  //array's counter is increased, all object in array are also retained.
319  const Util::AutoreleasePool pool;
320 
321  NSArray * const orderedWindows = [NSApp orderedWindows];
322  for (NSWindow *window in orderedWindows) {
323  if (![window isKindOfClass : [QuartzWindow class]])
324  continue;
325  QuartzWindow * const qw = (QuartzWindow *)window;
326  if (qw.fIsDeleted)//Because of reference counting this can happen.
327  continue;
328  //Check if point is inside.
330  return qw;
331  }
332 
333  return nil;
334 }
335 
336 //______________________________________________________________________________
337 NSView<X11Window> *FindDNDAwareViewInPoint(NSArray *children, Window_t dragWinID,
338  Window_t inputWinID, Int_t x, Int_t y, Int_t maxDepth)
339 {
340  assert(children != nil && "FindDNDAwareViewInPoint, parameter 'children' is nil");
341 
342  if (maxDepth <= 0)
343  return nil;
344 
345  NSEnumerator * const reverseEnumerator = [children reverseObjectEnumerator];
346  for (NSView<X11Window> *child in reverseEnumerator) {
347  if (!ScreenPointIsInView(child, x, y))
348  continue;
349  if (child.fIsDNDAware && child.fID != dragWinID && child.fID != inputWinID)
350  return child;//got it!
351 
352  NSView<X11Window> * const testView = FindDNDAwareViewInPoint([child subviews], dragWinID,
353  inputWinID, x, y, maxDepth - 1);
354  if (testView)
355  return testView;
356  }
357 
358  return nil;
359 }
360 
361 //______________________________________________________________________________
363  Int_t x, Int_t y, Int_t maxDepth)
364 {
365  //X and Y are ROOT's screen coordinates (Y is inverted).
366  if (maxDepth <= 0)
367  return nil;
368 
369  const Util::AutoreleasePool pool;
370 
371  if (!parentView) {//Start from the screen as a 'root' window.
372  NSArray * const orderedWindows = [NSApp orderedWindows];
373  for (NSWindow *window in orderedWindows) {
374  if (![window isKindOfClass : [QuartzWindow class]])
375  continue;
376  QuartzWindow * const qw = (QuartzWindow *)window;
377 
378  if (qw.fIsDeleted)//Because of reference counting this can happen.
379  continue;
380 
381  if (qw.fMapState != kIsViewable)
382  continue;
383 
384  //First, check this view itself, my be we found what we need already.
385  NSView<X11Window> *testView = qw.fContentView;
386  if (!ScreenPointIsInView(testView, x, y))
387  continue;
388 
389  if (testView.fIsDNDAware && testView.fID != dragWinID && testView.fID != inputWinID)
390  return testView;
391 
392  //Recursive part, check children.
393  NSArray * const children = [testView subviews];
394  testView = FindDNDAwareViewInPoint(children, dragWinID, inputWinID, x, y, maxDepth - 1);
395  if (testView)
396  return testView;
397  }
398 
399  //We did not find anything for 'root' window as parent.
400  return nil;
401  } else {
402  //Parent view is tested already (or should not be tested at all, check children.
403  return FindDNDAwareViewInPoint([parentView subviews], dragWinID, inputWinID, x, y, maxDepth);
404  }
405 }
406 
407 //______________________________________________________________________________
409 {
410  const Util::AutoreleasePool pool;
411 
412  NSArray * const orderedWindows = [NSApp orderedWindows];
413  for (NSWindow *nsWindow in orderedWindows) {
414  if (![nsWindow isKindOfClass : [QuartzWindow class]])
415  continue;
416 
417  QuartzWindow * const qWindow = (QuartzWindow *)nsWindow;
418 
419  if (qWindow.fIsDeleted)//Because of reference counting this can happen.
420  continue;
421 
422  if (qWindow.fMapState != kIsViewable)//Can it be false and still in this array???
423  continue;
424 
425  const NSPoint mousePosition = [qWindow mouseLocationOutsideOfEventStream];
426  const NSSize windowSize = qWindow.frame.size;
427  if (mousePosition.x >= 0 && mousePosition.x <= windowSize.width &&
428  mousePosition.y >= 0 && mousePosition.y <= windowSize.height)
429  return qWindow;
430  }
431 
432  return nil;
433 }
434 
435 //______________________________________________________________________________
437 {
438  const Util::AutoreleasePool pool;
439 
440  if (QuartzWindow *topLevel = FindWindowUnderPointer()) {
441  const NSPoint mousePosition = [topLevel mouseLocationOutsideOfEventStream];
442  return (NSView<X11Window> *)[[topLevel contentView] hitTest : mousePosition];
443  }
444 
445  return nil;
446 }
447 
448 //______________________________________________________________________________
450 {
451  //FindWindowForPointerEvent is required because due to grabs
452  //the receiver of the event can be different from the actual
453  //window under cursor.
454 
455  assert(pointerEvent != nil &&
456  "FindWindowForPointerEvent, parameter 'pointerEvent' is nil");
457 
458  const Util::AutoreleasePool pool;
459 
460  NSArray * const orderedWindows = [NSApp orderedWindows];
461  for (NSWindow *nsWindow in orderedWindows) {
462  if (![nsWindow isKindOfClass : [QuartzWindow class]])
463  continue;
464 
465  QuartzWindow * const qWindow = (QuartzWindow *)nsWindow;
466 
467  if (qWindow.fIsDeleted)//Because of reference counting this can happen.
468  continue;
469 
470  //Can it be false and still in this array???
471  if (qWindow.fMapState != kIsViewable)
472  continue;
473 
474  NSPoint mousePosition = [pointerEvent locationInWindow];
475  //The event has a window, so position is in this window's coordinate system,
476  //convert it into screen point first.
477  if ([pointerEvent window]) {
478  //convertBaseToScreen is deprecated.
479  //mousePosition = [[pointerEvent window] convertBaseToScreen : mousePosition];
480  mousePosition = ConvertPointFromBaseToScreen([pointerEvent window], mousePosition);
481  }
482 
483  //convertScreenToBase is deprecated.
484  //mousePosition = [qWindow convertScreenToBase : mousePosition];
485  mousePosition = ConvertPointFromScreenToBase(mousePosition, qWindow);
486 
487  const NSSize windowSize = qWindow.frame.size;
488  if (mousePosition.x >= 0 && mousePosition.x <= windowSize.width &&
489  mousePosition.y >= 0 && mousePosition.y <= windowSize.height)
490  return qWindow;
491  }
492 
493  return nil;
494 }
495 
496 //______________________________________________________________________________
498 {
499  //FindViewForPointerEvent is required because of grabs - the receiver of the
500  //event can be different from the actual window under cursor.
501 
502  assert(pointerEvent != nil &&
503  "FindViewForPointerEvent, parameter 'pointerEvent' is nil");
504 
505  const Util::AutoreleasePool pool;
506 
507  if (QuartzWindow *topLevel = FindWindowForPointerEvent(pointerEvent)) {
508  NSPoint mousePosition = [pointerEvent locationInWindow];
509  if ([pointerEvent window])
510  mousePosition = ConvertPointFromBaseToScreen([pointerEvent window], mousePosition);
511 
512  //convertScreenToBase is deprecated.
513  //mousePosition = [topLevel convertScreenToBase : mousePosition];
514  mousePosition = ConvertPointFromScreenToBase(mousePosition, topLevel);
515 
516  return (NSView<X11Window> *)[[topLevel contentView] hitTest : mousePosition];
517  }
518 
519  return nil;
520 }
521 
522 #pragma mark - Downscale image ("reading color bits" on retina macs).
523 
524 //Hoho, we support C++11?? Let's return by value then!!!
525 std::vector<unsigned char> DownscaledImageData(unsigned w, unsigned h, CGImageRef image)
526 {
527  assert(w != 0 && h != 0 && "DownscaledImageData, invalid geometry");
528  assert(image != nullptr && "DonwscaledImageData, invalid parameter 'image'");
529 
530  std::vector<unsigned char> result;
531  try {
532  result.resize(w * h * 4);
533  } catch (const std::bad_alloc &) {
534  NSLog(@"DownscaledImageData, memory allocation failed");
535  return result;
536  }
537 
538  const Util::CFScopeGuard<CGColorSpaceRef> colorSpace(CGColorSpaceCreateDeviceRGB());//[1]
539  if (!colorSpace.Get()) {
540  NSLog(@"DownscaledImageData, CGColorSpaceCreateDeviceRGB failed");
541  return result;
542  }
543 
544  Util::CFScopeGuard<CGContextRef> ctx(CGBitmapContextCreateWithData(&result[0], w, h, 8,
545  w * 4, colorSpace.Get(),
546  kCGImageAlphaPremultipliedLast, NULL, 0));
547  if (!ctx.Get()) {
548  NSLog(@"DownscaledImageData, CGBitmapContextCreateWithData failed");
549  return result;
550  }
551 
552  CGContextDrawImage(ctx.Get(), CGRectMake(0, 0, w, h), image);
553 
554  return result;
555 }
556 
557 #pragma mark - "Focus management" - just make another window key window.
558 
559 //______________________________________________________________________________
561 {
562  //XQuartz (and other X11
563  if (![NSApp isActive])
564  return;
565 
566  const Util::AutoreleasePool pool;
567 
568  NSArray * const orderedWindows = [NSApp orderedWindows];
569  for (NSWindow *nsWindow in orderedWindows) {
570  if (![nsWindow isKindOfClass : [QuartzWindow class]])
571  continue;
572 
573  QuartzWindow * const qWindow = (QuartzWindow *)nsWindow;
574 
575  if (qWindow.fIsDeleted || qWindow.fMapState != kIsViewable || qWindow.fID == winID)
576  continue;
577 
578  if (qWindow.fContentView.fOverrideRedirect)
579  continue;
580 
581  [qWindow makeKeyAndOrderFront : qWindow];
582  break;
583  }
584 }
585 
586 #pragma mark - 'shape mask' - to create a window with arbitrary (probably non-rectangle) shape.
587 
588 //______________________________________________________________________________
589 void ClipToShapeMask(NSView<X11Window> *view, CGContextRef ctx)
590 {
591  assert(view != nil && "ClipToShapeMask, parameter 'view' is nil");
592  assert(ctx != 0 && "ClipToShapeMask, parameter 'ctx' is null");
593 
594  QuartzWindow * const topLevelParent = view.fQuartzWindow;
595  assert(topLevelParent.fShapeCombineMask != nil &&
596  "ClipToShapeMask, fShapeCombineMask is nil on a top-level window");
597  assert(topLevelParent.fShapeCombineMask.fImage != 0 &&
598  "ClipToShapeMask, shape mask is null");
599 
600  //Important: shape mask should have the same width and height as
601  //a top-level window. In ROOT it does not :( Say hello to visual artifacts.
602 
603  //Attach clip mask to the context.
604  if (!view.fParentView) {
605  //'view' is a top-level view.
606  const CGRect clipRect = CGRectMake(0, 0, topLevelParent.fShapeCombineMask.fWidth,
607  topLevelParent.fShapeCombineMask.fHeight);
608  CGContextClipToMask(ctx, clipRect, topLevelParent.fShapeCombineMask.fImage);
609  } else {
610  NSRect clipRect = view.frame;
611  //More complex case: 'self' is a child view, we have to create a subimage from shape mask.
612  clipRect.origin = [view.fParentView convertPoint : clipRect.origin
613  toView : [view window].contentView];
614  clipRect.origin.y = X11::LocalYROOTToCocoa((NSView<X11Window> *)[view window].contentView,
615  clipRect.origin.y + clipRect.size.height);
616 
617  if (AdjustCropArea(topLevelParent.fShapeCombineMask, clipRect)) {
619  clipImageGuard(CGImageCreateWithImageInRect(topLevelParent.fShapeCombineMask.fImage,
620  NSRectToCGRect(clipRect)));
621  clipRect.origin = NSPoint();
622  CGContextClipToMask(ctx, NSRectToCGRect(clipRect), clipImageGuard.Get());
623  } else {
624  //View is invisible.
625  CGRect rect = {};
626  CGContextClipToRect(ctx, rect);
627  }
628  }
629 }
630 
631 #pragma mark - Window's geometry and attributes.
632 
633 //______________________________________________________________________________
634 void SetWindowAttributes(const SetWindowAttributes_t *attr, NSObject<X11Window> *window)
635 {
636  assert(attr != 0 && "SetWindowAttributes, parameter 'attr' is null");
637  assert(window != nil && "SetWindowAttributes, parameter 'window' is nil");
638 
639  const Mask_t mask = attr->fMask;
640 
641  if (mask & kWABackPixel)
642  window.fBackgroundPixel = attr->fBackgroundPixel;
643 
644  if (mask & kWAEventMask)
645  window.fEventMask = attr->fEventMask;
646 
647  if (mask & kWABitGravity)
648  window.fBitGravity = attr->fBitGravity;
649 
650  if (mask & kWAWinGravity)
651  window.fWinGravity = attr->fWinGravity;
652 
653  if (mask & kWAOverrideRedirect) {
654  if ([(NSObject *)window isKindOfClass : [QuartzWindow class]]) {
655  QuartzWindow * const qw = (QuartzWindow *)window;
656  [qw setStyleMask : Details::kBorderlessWindowMask];
657  [qw setAlphaValue : 0.95];
658  }
659 
660  window.fOverrideRedirect = YES;
661  }
662 }
663 
664 //______________________________________________________________________________
665 void GetWindowGeometry(NSObject<X11Window> *win, WindowAttributes_t *dst)
666 {
667  assert(win != nil && "GetWindowGeometry, parameter 'win' is nil");
668  assert(dst != 0 && "GetWindowGeometry, parameter 'dst' is null");
669 
670  dst->fX = win.fX;
671  dst->fY = win.fY;
672 
673  dst->fWidth = win.fWidth;
674  dst->fHeight = win.fHeight;
675 }
676 
677 //______________________________________________________________________________
678 void GetWindowAttributes(NSObject<X11Window> *window, WindowAttributes_t *dst)
679 {
680  assert(window != nil && "GetWindowAttributes, parameter 'window' is nil");
681  assert(dst != 0 && "GetWindowAttributes, parameter 'attr' is null");
682 
683  *dst = WindowAttributes_t();
684 
685  //fX, fY, fWidth, fHeight.
686  GetWindowGeometry(window, dst);
687 
688  //Actually, most of them are not used by GUI.
689  dst->fBorderWidth = 0;
690  dst->fDepth = window.fDepth;
691  //Dummy value.
692  dst->fVisual = 0;
693  //Dummy value.
694  dst->fRoot = 0;
695  dst->fClass = window.fClass;
696  dst->fBitGravity = window.fBitGravity;
697  dst->fWinGravity = window.fWinGravity;
698  //Dummy value.
699  dst->fBackingStore = kAlways;//??? CHECK
700  dst->fBackingPlanes = 0;
701 
702  //Dummy value.
703  dst->fBackingPixel = 0;
704 
705  dst->fSaveUnder = 0;
706 
707  //Dummy value.
708  dst->fColormap = 0;
709  //Dummy value.
710  dst->fMapInstalled = kTRUE;
711 
712  dst->fMapState = window.fMapState;
713 
714  dst->fAllEventMasks = window.fEventMask;
715  dst->fYourEventMask = window.fEventMask;
716 
717  //Not used by GUI.
718  //dst->fDoNotPropagateMask
719 
720  dst->fOverrideRedirect = window.fOverrideRedirect;
721  //Dummy value.
722  dst->fScreen = 0;
723 }
724 
725 //With Apple's poor man's objective C/C++ + "brilliant" Cocoa you never know, what should be
726 //the linkage of callback functions, API + language dialects == MESS. I declare/define this comparators here
727 //as having "C++" linkage. If one good day clang will start to complane, I'll have to change this.
728 
729 #pragma mark - Comparators (I need them when changing a window's z-order).
730 
731 //______________________________________________________________________________
732 // SDK 10.11 and above ...
733 #ifdef MAC_OS_X_VERSION_10_11
734 NSComparisonResult CompareViewsToLower(__kindof NSView *view1, __kindof NSView *view2, void *context)
735 #else
736 NSComparisonResult CompareViewsToLower(id view1, id view2, void *context)
737 #endif
738 {
739  id topView = (id)context;
740  if (view1 == topView)
741  return NSOrderedAscending;
742  if (view2 == topView)
743  return NSOrderedDescending;
744 
745  return NSOrderedSame;
746 }
747 
748 //______________________________________________________________________________
749 // SDK 10.11 and above ...
750 #ifdef MAC_OS_X_VERSION_10_11
751 NSComparisonResult CompareViewsToRaise(__kindof NSView *view1, __kindof NSView *view2, void *context)
752 #else
753 NSComparisonResult CompareViewsToRaise(id view1, id view2, void *context)
754 #endif
755 {
756  id topView = (id)context;
757  if (view1 == topView)
758  return NSOrderedDescending;
759  if (view2 == topView)
760  return NSOrderedAscending;
761 
762  return NSOrderedSame;
763 }
764 
765 #pragma mark - Cursor's area.
766 
767 //______________________________________________________________________________
768 NSPoint GetCursorHotStop(NSImage *image, ECursor cursor)
769 {
770  assert(image != nil && "CursroHotSpot, parameter 'image' is nil");
771 
772  const NSSize imageSize = image.size;
773 
774  if (cursor == kArrowRight)
775  return NSMakePoint(imageSize.width, imageSize.height / 2);
776 
777  return NSMakePoint(imageSize.width / 2, imageSize.height / 2);
778 }
779 
780 //______________________________________________________________________________
781 NSCursor *CreateCustomCursor(ECursor currentCursor)
782 {
783  // Returns auto-released cursor object.
784  const char *pngFileName = 0;
785 
786  switch (currentCursor) {
787  case kMove:
788  pngFileName = "move_cursor.png";
789  break;
790  case kArrowHor:
791  pngFileName = "hor_arrow_cursor.png";
792  break;
793  case kArrowVer:
794  pngFileName = "ver_arrow_cursor.png";
795  break;
796  case kArrowRight:
797  pngFileName = "right_arrow_cursor.png";
798  break;
799  case kRotate:
800  pngFileName = "rotate.png";
801  break;
802  case kBottomLeft:
803  case kTopRight:
804  pngFileName = "top_right_cursor.png";
805  break;
806  case kTopLeft:
807  case kBottomRight:
808  pngFileName = "top_left_cursor.png";
809  break;
810  default:;
811  }
812 
813  if (pngFileName) {
814  const char * const path = gSystem->Which(TROOT::GetIconPath(), pngFileName, kReadPermission);
815  const Util::ScopedArray<const char> arrayGuard(path);
816 
817  if (!path || path[0] == 0) {
818  //File was not found.
819  return nil;
820  }
821 
822  NSString *nsPath = [NSString stringWithFormat : @"%s", path];//in autorelease pool.
823  NSImage * const cursorImage = [[NSImage alloc] initWithContentsOfFile : nsPath];
824 
825  if (!cursorImage)
826  return nil;
827 
828  const NSPoint hotSpot(X11::GetCursorHotStop(cursorImage, currentCursor));
829  NSCursor * const customCursor = [[[NSCursor alloc] initWithImage : cursorImage
830  hotSpot : hotSpot] autorelease];
831 
832  [cursorImage release];
833 
834  return customCursor;
835  }
836 
837  return nil;
838 }
839 
840 //______________________________________________________________________________
841 NSCursor *CreateCursor(ECursor currentCursor)
842 {
843  // Returns auto-released cursor object.
844 
845  //Cursors from TVirtaulX:
846  // kBottomLeft, kBottomRight, kTopLeft, kTopRight,
847  // kBottomSide, kLeftSide, kTopSide, kRightSide,
848  // kMove, kCross, kArrowHor, kArrowVer,
849  // kHand, kRotate, kPointer, kArrowRight,
850  // kCaret, kWatch
851 
852  NSCursor *cursor = nil;
853  switch (currentCursor) {
854  case kCross:
855  cursor = [NSCursor crosshairCursor];
856  break;
857  case kPointer:
858  cursor = [NSCursor arrowCursor];
859  break;
860  case kHand:
861  cursor = [NSCursor openHandCursor];
862  break;
863  case kLeftSide:
864  cursor = [NSCursor resizeLeftCursor];
865  break;
866  case kRightSide:
867  cursor = [NSCursor resizeRightCursor];
868  break;
869  case kTopSide:
870  cursor = [NSCursor resizeUpCursor];
871  break;
872  case kBottomSide:
873  cursor = [NSCursor resizeDownCursor];
874  break;
875  case kCaret:
876  cursor = [NSCursor IBeamCursor];
877  break;
878  case kRotate:
879  case kWatch:
880  default:
881  cursor = CreateCustomCursor(currentCursor);
882  }
883 
884  return cursor;
885 }
886 
887 //TGTextView/TGHtml is a very special window: it's a TGCompositeFrame,
888 //which has TGCompositeFrame inside (TGViewFrame). This TGViewFrame
889 //delegates Expose events to its parent, and parent tries to draw
890 //inside a TGViewFrame. This does not work with default
891 //QuartzView -drawRect/TGCocoa. So I need a trick to identify
892 //this special window.
893 
894 #pragma mark - Workarounds for a text view and its descendants.
895 
896 //______________________________________________________________________________
897 bool ViewIsTextView(unsigned viewID)
898 {
899  const TGWindow * const window = gClient->GetWindowById(viewID);
900  if (!window)
901  return false;
902  // This code used to use TObject::InheritsFrom, however since this is
903  // run under the AppKit, we can not call core/meta functions, otherwise
904  // we will run into deadlocks.
905  return dynamic_cast<const TGTextView*>(window);
906 }
907 
908 //______________________________________________________________________________
910 {
911  assert(view != nil && "ViewIsTextView, parameter 'view' is nil");
912 
913  return ViewIsTextView(view.fID);
914 }
915 
916 //______________________________________________________________________________
917 bool ViewIsTextViewFrame(NSView<X11Window> *view, bool checkParent)
918 {
919  assert(view != nil && "ViewIsTextViewFrame, parameter 'view' is nil");
920 
921  const TGWindow * const window = gClient->GetWindowById(view.fID);
922  if (!window)
923  return false;
924 
925  // This code used to use TObject::InheritsFrom, however since this is
926  // run under the AppKit, we can not call core/meta functions, otherwise
927  // we will run into deadlocks.
928  if (!dynamic_cast<const TGViewFrame*>(window))
929  return false;
930 
931  if (!checkParent)
932  return true;
933 
934  if (!view.fParentView)
935  return false;
936 
937  return ViewIsTextView(view.fParentView);
938 }
939 
940 //______________________________________________________________________________
941 bool ViewIsHtmlView(unsigned viewID)
942 {
943  const TGWindow * const window = gClient->GetWindowById(viewID);
944  if (!window)
945  return false;
946  // This code used to use TObject::InheritsFrom, however since this is
947  // run under the AppKit, we can not call core/meta functions, otherwise
948  // we will run into deadlocks.
949  return window->TestBit(TGWindow::kIsHtmlView);
950 }
951 
952 //______________________________________________________________________________
954 {
955  assert(view != nil && "ViewIsHtmlView, parameter 'view' is nil");
956 
957  return ViewIsHtmlView(view.fID);
958 }
959 
960 //______________________________________________________________________________
961 bool ViewIsHtmlViewFrame(NSView<X11Window> *view, bool checkParent)
962 {
963  //
964  assert(view != nil && "ViewIsHtmlViewFrame, parameter 'view' is nil");
965 
966  const TGWindow * const window = gClient->GetWindowById(view.fID);
967  if (!window)
968  return false;
969 
970  // This code used to use TObject::InheritsFrom, however since this is
971  // run under the AppKit, we can not call core/meta functions, otherwise
972  // we will run into deadlocks.
973  if (!dynamic_cast<const TGViewFrame*>(window))
974  return false;
975 
976  if (!checkParent)
977  return true;
978 
979  if (!view.fParentView)
980  return false;
981 
982  return ViewIsHtmlView(view.fParentView);
983 }
984 
985 //______________________________________________________________________________
987 {
988  assert(textView != nil && "FrameForTextView, parameter 'textView' is nil");
989 
990  for (NSView<X11Window> *child in [textView subviews]) {
991  if (ViewIsTextViewFrame(child, false))
992  return child;
993  }
994 
995  return nil;
996 }
997 
998 //______________________________________________________________________________
1000 {
1001  assert(htmlView != nil && "FrameForHtmlView, parameter 'htmlView' is nil");
1002 
1003  for (NSView<X11Window> *child in [htmlView subviews]) {
1004  if (ViewIsHtmlViewFrame(child, false))
1005  return child;
1006  }
1007 
1008  return nil;
1009 }
1010 
1011 #pragma mark - Workarounds for 'paint out of paint events'.
1012 
1013 //______________________________________________________________________________
1015 {
1016  assert(view != nil && "LockFocus, parameter 'view' is nil");
1017  assert([view isKindOfClass : [QuartzView class]] &&
1018  "LockFocus, QuartzView is expected");
1019 
1020  if ([view lockFocusIfCanDraw]) {
1021  NSGraphicsContext *nsContext = [NSGraphicsContext currentContext];
1022  assert(nsContext != nil && "LockFocus, currentContext is nil");
1023  CGContextRef currContext = (CGContextRef)[nsContext graphicsPort];
1024  assert(currContext != 0 && "LockFocus, graphicsPort is null");//remove this assert?
1025 
1026  ((QuartzView *)view).fContext = currContext;
1027 
1028  return true;
1029  }
1030 
1031  return false;
1032 }
1033 
1034 //______________________________________________________________________________
1036 {
1037  assert(view != nil && "UnlockFocus, parameter 'view' is nil");
1038  assert([view isKindOfClass : [QuartzView class]] &&
1039  "UnlockFocus, QuartzView is expected");
1040 
1041  [view unlockFocus];
1042  ((QuartzView *)view).fContext = 0;
1043 }
1044 
1045 }//X11
1046 }//MacOSX
1047 }//ROOT
1048 
1049 namespace Quartz = ROOT::Quartz;
1050 namespace Util = ROOT::MacOSX::Util;
1051 namespace X11 = ROOT::MacOSX::X11;
1052 namespace Details = ROOT::MacOSX::Details;
1053 
1054 #ifdef DEBUG_ROOT_COCOA
1055 
1056 #pragma mark - 'loggers'.
1057 
1058 namespace {
1059 
1060 //______________________________________________________________________________
1061 void log_attributes(const SetWindowAttributes_t *attr, unsigned winID)
1062 {
1063  //This function is loggin requests, at the moment I can not set all
1064  //of these attributes, so I first have to check, what is actually
1065  //requested by ROOT.
1066  static std::ofstream logfile("win_attr.txt");
1067 
1068  const Mask_t mask = attr->fMask;
1069  if (mask & kWABackPixmap)
1070  logfile<<"win "<<winID<<": BackPixmap\n";
1071  if (mask & kWABackPixel)
1072  logfile<<"win "<<winID<<": BackPixel\n";
1073  if (mask & kWABorderPixmap)
1074  logfile<<"win "<<winID<<": BorderPixmap\n";
1075  if (mask & kWABorderPixel)
1076  logfile<<"win "<<winID<<": BorderPixel\n";
1077  if (mask & kWABorderWidth)
1078  logfile<<"win "<<winID<<": BorderWidth\n";
1079  if (mask & kWABitGravity)
1080  logfile<<"win "<<winID<<": BitGravity\n";
1081  if (mask & kWAWinGravity)
1082  logfile<<"win "<<winID<<": WinGravity\n";
1083  if (mask & kWABackingStore)
1084  logfile<<"win "<<winID<<": BackingStore\n";
1085  if (mask & kWABackingPlanes)
1086  logfile<<"win "<<winID<<": BackingPlanes\n";
1087  if (mask & kWABackingPixel)
1088  logfile<<"win "<<winID<<": BackingPixel\n";
1089  if (mask & kWAOverrideRedirect)
1090  logfile<<"win "<<winID<<": OverrideRedirect\n";
1091  if (mask & kWASaveUnder)
1092  logfile<<"win "<<winID<<": SaveUnder\n";
1093  if (mask & kWAEventMask)
1094  logfile<<"win "<<winID<<": EventMask\n";
1095  if (mask & kWADontPropagate)
1096  logfile<<"win "<<winID<<": DontPropagate\n";
1097  if (mask & kWAColormap)
1098  logfile<<"win "<<winID<<": Colormap\n";
1099  if (mask & kWACursor)
1100  logfile<<"win "<<winID<<": Cursor\n";
1101 }
1102 
1103 //______________________________________________________________________________
1104 void print_mask_info(ULong_t mask)
1105 {
1106  if (mask & kButtonPressMask)
1107  NSLog(@"button press mask");
1108  if (mask & kButtonReleaseMask)
1109  NSLog(@"button release mask");
1110  if (mask & kExposureMask)
1111  NSLog(@"exposure mask");
1112  if (mask & kPointerMotionMask)
1113  NSLog(@"pointer motion mask");
1114  if (mask & kButtonMotionMask)
1115  NSLog(@"button motion mask");
1116  if (mask & kEnterWindowMask)
1117  NSLog(@"enter notify mask");
1118  if (mask & kLeaveWindowMask)
1119  NSLog(@"leave notify mask");
1120 }
1121 
1122 }
1123 #endif
1124 
1125 @implementation CrosshairView
1126 
1127 - (void) drawRect : (NSRect) dirtyRect
1128 {
1129  [super drawRect:dirtyRect];
1130 
1131  NSGraphicsContext *nsContext = [NSGraphicsContext currentContext];
1132  if (!nsContext)
1133  return;
1134 
1135  CGContextRef cgContext = nsContext.CGContext;
1136  if (!cgContext)
1137  return;
1138 
1139  const Quartz::CGStateGuard ctxGuard(cgContext);
1140 
1141  CGContextSetRGBStrokeColor(cgContext, 0., 0., 0., 1.);
1142  CGContextSetLineWidth(cgContext, 1.);
1143  // Horizontal line:
1144  CGContextBeginPath(cgContext);
1145  CGContextMoveToPoint(cgContext, self.start1.x, self.start1.y);
1146  CGContextAddLineToPoint(cgContext, self.end1.x, self.end1.y);
1147  CGContextStrokePath(cgContext);
1148  // Vertical line:
1149  CGContextBeginPath(cgContext);
1150  CGContextMoveToPoint(cgContext, self.start2.x, self.start2.y);
1151  CGContextAddLineToPoint(cgContext, self.end2.x, self.end2.y);
1152  CGContextStrokePath(cgContext);
1153 }
1154 
1155 @end
1156 
1157 @implementation CrosshairWindow
1158 
1159 - (instancetype) init
1160 {
1161  if (self = [super init])
1162  {
1163  self.styleMask = NSWindowStyleMaskBorderless; // No titlebar, buttons, etc.
1164  self.opaque = NO;
1165  self.hasShadow = NO;
1166  self.backgroundColor = NSColor.clearColor; // No background.
1167  self.ignoresMouseEvents = YES; // Lets mouse events pass through.
1168  self.contentView = [[CrosshairView alloc] init];
1169  }
1170  return self;
1171 }
1172 
1173 #pragma mark - suppress the normal window behavior.
1174 - (BOOL)canBecomeKeyWindow
1175 {
1176  return NO;
1177 }
1178 - (BOOL)canBecomeMainWindow
1179 {
1180  return NO;
1181 }
1182 
1183 @end
1184 
1185 
1186 @implementation QuartzWindow
1187 
1188 @synthesize fMainWindow;
1189 @synthesize fHasFocus;
1190 
1191 #pragma mark - QuartzWindow's life cycle.
1192 
1193 //______________________________________________________________________________
1194 - (id) initWithContentRect : (NSRect) contentRect styleMask : (NSUInteger) windowStyle
1195  backing : (NSBackingStoreType) bufferingType defer : (BOOL) deferCreation
1196  windowAttributes : (const SetWindowAttributes_t *) attr
1197 {
1198  self = [super initWithContentRect : contentRect styleMask : windowStyle
1199  backing : bufferingType defer : deferCreation];
1200 
1201  if (self) {
1202  //ROOT's not able to draw GUI concurrently, thanks to global variables and gVirtualX itself.
1203  [self setAllowsConcurrentViewDrawing : NO];
1204 
1205  self.delegate = self;
1206  //create content view here.
1207  NSRect contentViewRect = contentRect;
1208  contentViewRect.origin.x = 0.f;
1209  contentViewRect.origin.y = 0.f;
1210 
1211  fContentView = [[QuartzView alloc] initWithFrame : contentViewRect windowAttributes : 0];
1212 
1213  [self setContentView : fContentView];
1214 
1215  [fContentView release];
1216  fDelayedTransient = NO;
1217 
1218  if (attr)
1219  X11::SetWindowAttributes(attr, self);
1220 
1221  fIsDeleted = NO;
1222  fHasFocus = NO;
1223  }
1224 
1225  return self;
1226 }
1227 
1228 //______________________________________________________________________________
1229 - (id) initWithGLView : (ROOTOpenGLView *) glView
1230 {
1231  using namespace Details;
1232 
1233  assert(glView != nil && "-initWithGLView, parameter 'glView' is nil");
1234 
1235  const NSUInteger styleMask = kTitledWindowMask | kClosableWindowMask |
1237 
1238  NSRect contentRect = glView.frame;
1239  contentRect.origin = NSPoint();
1240 
1241  self = [super initWithContentRect : contentRect styleMask : styleMask
1242  backing : NSBackingStoreBuffered defer : NO];
1243 
1244  if (self) {
1245  //ROOT's not able to draw GUI concurrently, thanks to global variables and gVirtualX itself.
1246  [self setAllowsConcurrentViewDrawing : NO];
1247  self.delegate = self;
1248  fContentView = glView;
1249  [self setContentView : fContentView];
1250  fDelayedTransient = NO;
1251  fIsDeleted = NO;
1252  fHasFocus = NO;
1253  }
1254 
1255  return self;
1256 }
1257 
1258 //______________________________________________________________________________
1260 {
1261  [fShapeCombineMask release];
1262  [super dealloc];
1263 }
1264 
1265 //______________________________________________________________________________
1266 - (BOOL) fIsDeleted
1267 {
1268  return fIsDeleted;
1269 }
1270 
1271 //______________________________________________________________________________
1272 - (void) setContentView:(NSView *)cv
1273 {
1274  [super setContentView:cv];
1275  if ([cv isKindOfClass:[QuartzView class]])
1276  fContentView = (QuartzView *)cv;
1277  else
1278  fContentView = nil;
1279 }
1280 
1281 //______________________________________________________________________________
1282 - (void) setFIsDeleted : (BOOL) deleted
1283 {
1284  fIsDeleted = deleted;
1285 }
1286 
1287 #pragma mark - Forwaring: I want to forward a lot of property setters/getters to the content view.
1288 
1289 //______________________________________________________________________________
1290 - (void) forwardInvocation : (NSInvocation *) anInvocation
1291 {
1292  if (!fContentView)
1293  return;
1294 
1295  if ([fContentView respondsToSelector : [anInvocation selector]]) {
1296  [anInvocation invokeWithTarget : fContentView];
1297  } else {
1298  [super forwardInvocation : anInvocation];
1299  }
1300 }
1301 
1302 //______________________________________________________________________________
1303 - (NSMethodSignature*) methodSignatureForSelector : (SEL) selector
1304 {
1305  NSMethodSignature *signature = [super methodSignatureForSelector : selector];
1306 
1307  if (!signature) {
1308  if (fContentView)
1309  signature = [fContentView methodSignatureForSelector : selector];
1310  }
1311 
1312  return signature;
1313 }
1314 
1315 //______________________________________________________________________________
1316 - (void) addTransientWindow : (QuartzWindow *) window
1317 {
1318  //Transient window: all the popups (menus, dialogs, popups, comboboxes, etc.)
1319  //always should be on the top of its 'parent' window.
1320  //To enforce this ordering, I have to connect such windows with parent/child
1321  //relation (it's not the same as a view hierarchy - both child and parent
1322  //windows are top-level windows).
1323 
1324  assert(window != nil && "-addTransientWindow:, parameter 'window' is nil");
1325 
1326  window.fMainWindow = self;
1327 
1328  if (window.fMapState != kIsViewable) {
1329  //If I add it as child, it'll immediately make a window visible
1330  //and this thing sucks.
1331  window.fDelayedTransient = YES;
1332  } else {
1333  [self addChildWindow : window ordered : NSWindowAbove];
1334  window.fDelayedTransient = NO;
1335  }
1336 }
1337 
1338 //______________________________________________________________________________
1339 - (void) makeKeyAndOrderFront : (id) sender
1340 {
1341 #pragma unused(sender)
1342 
1343  //The more I know Cocoa, the less I like it.
1344  //Window behavior between spaces is a total mess.
1345  //Set the window to join all spaces.
1346 #ifdef MAC_OS_X_VERSION_10_9
1347  [self setCollectionBehavior : NSWindowCollectionBehaviorMoveToActiveSpace];
1348 #else
1349  [self setCollectionBehavior : NSWindowCollectionBehaviorCanJoinAllSpaces];
1350 #endif
1351  //now bring it to the front, it will appear on the active space.
1352  [super makeKeyAndOrderFront : self];
1353  //then reset the collection behavior to default, so the window
1354  [self setCollectionBehavior : NSWindowCollectionBehaviorDefault];
1355 }
1356 
1357 //______________________________________________________________________________
1358 - (void) setFDelayedTransient : (BOOL) d
1359 {
1360  fDelayedTransient = d;
1361 }
1362 
1363 //______________________________________________________________________________
1365 {
1366  return fShapeCombineMask;
1367 }
1368 
1369 //______________________________________________________________________________
1370 - (void) setFShapeCombineMask : (QuartzImage *) mask
1371 {
1372  if (mask != fShapeCombineMask) {
1373  [fShapeCombineMask release];
1374  if (mask) {
1375  fShapeCombineMask = [mask retain];
1376  //TODO: Check window's shadow???
1377  }
1378  }
1379 }
1380 
1381 #pragma mark - X11Drawable's protocol.
1382 
1383 //______________________________________________________________________________
1384 - (BOOL) fIsPixmap
1385 {
1386  //Never.
1387  return NO;
1388 }
1389 
1390 //______________________________________________________________________________
1392 {
1393  //Never.
1394  return NO;
1395 }
1396 
1397 //______________________________________________________________________________
1398 - (CGFloat) fScaleFactor
1399 {
1400  if (!self.screen)
1401  return 1.;
1402  return self.screen.backingScaleFactor;
1403 }
1404 
1405 //______________________________________________________________________________
1406 - (int) fX
1407 {
1408  return X11::GlobalXCocoaToROOT(self.frame.origin.x);
1409 }
1410 
1411 //______________________________________________________________________________
1412 - (int) fY
1413 {
1414  return X11::GlobalYCocoaToROOT(self.frame.origin.y + self.frame.size.height);
1415 }
1416 
1417 //______________________________________________________________________________
1418 - (unsigned) fWidth
1419 {
1420  return self.frame.size.width;
1421 }
1422 
1423 //______________________________________________________________________________
1424 - (unsigned) fHeight
1425 {
1426  //NSWindow's frame (height component) also includes title-bar.
1427  //So I have to use content view's height.
1428  //Obviously, there is a "hole" == 22 pixels.
1429  assert(fContentView != nil && "-fHeight:, content view is nil");
1430 
1431  return fContentView.frame.size.height;
1432 }
1433 
1434 //______________________________________________________________________________
1435 - (void) setDrawableSize : (NSSize) newSize
1436 {
1437  //Can not simply do self.frame.size = newSize.
1438  assert(!(newSize.width < 0) && "-setDrawableSize:, width is negative");
1439  assert(!(newSize.height < 0) && "-setDrawableSize:, height is negative");
1440 
1441  NSRect frame = self.frame;
1442  //dY is potentially a titlebar height.
1443  const CGFloat dY = fContentView ? frame.size.height - fContentView.frame.size.height : 0.;
1444  //Adjust the frame.
1445  frame.origin.y = frame.origin.y + frame.size.height - newSize.height - dY;
1446  frame.size = newSize;
1447  frame.size.height += dY;
1448  [self setFrame : frame display : YES];
1449 }
1450 
1451 //______________________________________________________________________________
1452 - (void) setX : (int) x Y : (int) y width : (unsigned) w height : (unsigned) h
1453 {
1454  NSSize newSize = {};
1455  newSize.width = w;
1456  newSize.height = h;
1457  [self setContentSize : newSize];
1458 
1459  //Check how this is affected by title bar's height.
1460  NSPoint topLeft = {};
1461  topLeft.x = X11::GlobalXROOTToCocoa(x);
1462  topLeft.y = X11::GlobalYROOTToCocoa(y);
1463 
1464  [self setFrameTopLeftPoint : topLeft];
1465 }
1466 
1467 //______________________________________________________________________________
1468 - (void) setX : (int) x Y : (int) y
1469 {
1470  NSPoint topLeft = {};
1471  topLeft.x = X11::GlobalXROOTToCocoa(x);
1472  topLeft.y = X11::GlobalYROOTToCocoa(y);
1473 
1474  [self setFrameTopLeftPoint : topLeft];
1475 }
1476 
1477 //______________________________________________________________________________
1478 - (void) copy : (NSObject<X11Drawable> *) src area : (X11::Rectangle) area withMask : (QuartzImage *) mask
1479  clipOrigin : (X11::Point) clipXY toPoint : (X11::Point) dstPoint
1480 {
1481  if (!fContentView)
1482  return;
1483 
1484  [fContentView copy : src area : area withMask : mask clipOrigin : clipXY toPoint : dstPoint];
1485 }
1486 
1487 //______________________________________________________________________________
1488 - (unsigned char *) readColorBits : (X11::Rectangle) area
1489 {
1490  if (!fContentView)
1491  return nullptr;
1492 
1493  return [fContentView readColorBits : area];
1494 }
1495 
1496 #pragma mark - CrosshairWindow/View
1497 
1498 //______________________________________________________________________________
1500 {
1501  if ([self findCrosshairWindow])
1502  return;
1503 
1504  CrosshairWindow *special = [[CrosshairWindow alloc] init];
1505  [self adjustCrosshairWindowGeometry:special];
1506  [self addChildWindow : special ordered : NSWindowAbove];
1507  [special release];
1508 }
1509 
1510 //______________________________________________________________________________
1512 {
1513  if (auto win = [self findCrosshairWindow])
1514  [self adjustCrosshairWindowGeometry:win];
1515 }
1516 
1517 //______________________________________________________________________________
1519 {
1520  assert(win && "invalid (nil) parameter 'win'");
1521  auto frame = self.contentView.frame;
1522  frame = [self convertRectToScreen:frame];
1523  [win setFrame:frame display:NO];
1524 }
1525 
1526 //______________________________________________________________________________
1528 {
1529  if (auto win = [self findCrosshairWindow]) {
1530  // For some reason, without ordeing out, the crosshair window's content stays
1531  // in the parent's window. Thus we first have to order out the crosshair window.
1532  [win orderOut:nil];
1533  [self removeChildWindow : win];
1534  }
1535 }
1536 
1537 //______________________________________________________________________________
1539 {
1540  auto children = [self childWindows];
1541  for (NSWindow *child in children) {
1542  if ([child isKindOfClass : CrosshairWindow.class])
1543  return (CrosshairWindow *)child;
1544  }
1545  return nil;
1546 }
1547 
1548 
1549 #pragma mark - X11Window protocol's implementation.
1550 
1551 //______________________________________________________________________________
1552 - (QuartzView *) fParentView
1553 {
1554  return nil;
1555 }
1556 
1557 //______________________________________________________________________________
1558 - (void) setFParentView : (QuartzView *) parent
1559 {
1560 #pragma unused(parent)
1561 }
1562 
1563 //______________________________________________________________________________
1565 {
1566  return fContentView;
1567 }
1568 
1569 //______________________________________________________________________________
1571 {
1572  return self;
1573 }
1574 
1575 //... many forwards to fContentView.
1576 
1577 //______________________________________________________________________________
1578 - (void) setFBackgroundPixel : (unsigned long) backgroundColor
1579 {
1580  if (!fContentView)
1581  return;
1582 
1583  if (!fShapeCombineMask) {
1584  CGFloat rgba[] = {0., 0., 0., 1.};
1585  X11::PixelToRGB(backgroundColor, rgba);
1586 
1587  [self setBackgroundColor : [NSColor colorWithColorSpace : [NSColorSpace deviceRGBColorSpace] components : rgba count : 4]];
1588  }
1589 
1590  fContentView.fBackgroundPixel = backgroundColor;
1591 }
1592 
1593 //______________________________________________________________________________
1594 - (unsigned long) fBackgroundPixel
1595 {
1596  if (!fContentView)
1597  return 0;
1598 
1600 }
1601 
1602 //______________________________________________________________________________
1603 - (int) fMapState
1604 {
1605  //Top-level window can be only kIsViewable or kIsUnmapped (not unviewable).
1606  if (!fContentView)
1607  return kIsUnmapped;
1608 
1609  if ([fContentView isHidden])
1610  return kIsUnmapped;
1611 
1612  return kIsViewable;
1613 }
1614 
1615 //______________________________________________________________________________
1616 - (void) addChild : (NSView<X11Window> *) child
1617 {
1618  assert(child != nil && "-addChild:, parameter 'child' is nil");
1619 
1620  if (!fContentView) {
1621  //This can happen only in case of re-parent operation.
1622  assert([child isKindOfClass : [QuartzView class]] &&
1623  "-addChild: gl view in a top-level window as content view is not supported");
1624 
1625  fContentView = (QuartzView *)child;
1626  [self setContentView : child];
1627  fContentView.fParentView = nil;
1628  } else
1629  [fContentView addChild : child];
1630 }
1631 
1632 //______________________________________________________________________________
1633 - (void) getAttributes : (WindowAttributes_t *) attr
1634 {
1635  if (!fContentView)
1636  return;
1637 
1638  assert(attr && "-getAttributes:, parameter 'attr' is nil");
1639 
1640  X11::GetWindowAttributes(self, attr);
1641 }
1642 
1643 //______________________________________________________________________________
1644 - (void) setAttributes : (const SetWindowAttributes_t *) attr
1645 {
1646  assert(attr != 0 && "-setAttributes:, parameter 'attr' is null");
1647 
1648 #ifdef DEBUG_ROOT_COCOA
1649  log_attributes(attr, self.fID);
1650 #endif
1651 
1652  X11::SetWindowAttributes(attr, self);
1653 }
1654 
1655 //______________________________________________________________________________
1657 {
1658  if (!fContentView)
1659  return;
1660 
1661  const Util::AutoreleasePool pool;
1662 
1663  [fContentView setHidden : NO];
1664  [self makeKeyAndOrderFront : self];
1666 
1667  if (fDelayedTransient) {
1668  fDelayedTransient = NO;
1669  [fMainWindow addChildWindow : self ordered : NSWindowAbove];
1670  }
1671 }
1672 
1673 //______________________________________________________________________________
1675 {
1676  if (!fContentView)
1677  return;
1678 
1679  const Util::AutoreleasePool pool;
1680 
1681  [fContentView setHidden : NO];
1682  [self makeKeyAndOrderFront : self];
1684 
1685  if (fDelayedTransient) {
1686  fDelayedTransient = NO;
1687  [fMainWindow addChildWindow : self ordered : NSWindowAbove];
1688  }
1689 }
1690 
1691 //______________________________________________________________________________
1693 {
1694  if (!fContentView)
1695  return;
1696 
1697  const Util::AutoreleasePool pool;
1698 
1701 }
1702 
1703 //______________________________________________________________________________
1705 {
1706  if (!fContentView)
1707  return;
1708 
1709  [fContentView setHidden : YES];
1710  [self orderOut : self];
1711 
1712  if (fMainWindow && !fDelayedTransient) {
1713  [fMainWindow removeChildWindow : self];
1714  fMainWindow = nil;
1715  }
1716 }
1717 
1718 #pragma mark - Events.
1719 
1720 //______________________________________________________________________________
1721 - (void) sendEvent : (NSEvent *) theEvent
1722 {
1723  //With XQuartz, if you open a menu and try to move a window without closing this menu,
1724  //window does not move, menu closes, and after that you can start draggin a window again.
1725  //With Cocoa I can not do such a thing (window WILL move), but still can report button release event
1726  //to close a menu.
1727  if (!fContentView)
1728  return;
1729 
1730  if (theEvent.type == Details::kLeftMouseDown || theEvent.type == Details::kRightMouseDown) {
1731  bool generateFakeRelease = false;
1732 
1733  const NSPoint windowPoint = [theEvent locationInWindow];
1734 
1735  if (windowPoint.x <= 4 || windowPoint.x >= self.fWidth - 4)
1736  generateFakeRelease = true;
1737 
1738  if (windowPoint.y <= 4 || windowPoint.y >= self.fHeight - 4)
1739  generateFakeRelease = true;
1740 
1741  const NSPoint viewPoint = [fContentView convertPoint : windowPoint fromView : nil];
1742 
1743  if (viewPoint.y <= 0 && windowPoint.y >= 0)
1744  generateFakeRelease = true;
1745 
1746  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
1747  "-sendEvent:, gVirtualX is either null or not of TGCocoa type");
1748 
1749  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
1750  if (vx->GetEventTranslator()->HasPointerGrab() && generateFakeRelease) {
1752  theEvent.type == Details::kLeftMouseDown ?
1753  kButton1 : kButton3);
1754  //Yes, ignore this event completely (this means, you are not able to immediately start
1755  //resizing a window, if some popup is open. Actually, this is more or less
1756  //the same as with XQuartz and X11 version.
1757  return;
1758  }
1759  }
1760 
1761  [super sendEvent : theEvent];
1762 }
1763 
1764 #pragma mark - NSWindowDelegate's methods.
1765 
1766 //______________________________________________________________________________
1767 - (BOOL) windowShouldClose : (id) sender
1768 {
1769 #pragma unused(sender)
1770  if (!fContentView)
1771  return NO;
1772 
1773  if ([[self childWindows] count])
1774  return NO;
1775 
1776  //Prepare client message for a window.
1777  Event_t closeEvent = {};
1778  closeEvent.fWindow = fContentView.fID;
1779  closeEvent.fType = kClientMessage;
1780  closeEvent.fFormat = 32;//Taken from GUI classes.
1781  closeEvent.fHandle = TGCocoa::fgDeleteWindowAtom;
1782  closeEvent.fUser[0] = TGCocoa::fgDeleteWindowAtom;
1783  //Place it into the queue.
1784  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
1785  "-windowShouldClose:, gVirtualX is either null or has a type different from TGCocoa");
1786  ((TGCocoa *)gVirtualX)->SendEvent(fContentView.fID, &closeEvent);
1787 
1788  //Do not let AppKit to close a window,
1789  //ROOT will do.
1790  return NO;
1791 }
1792 
1793 //______________________________________________________________________________
1794 - (void) windowDidBecomeKey : (NSNotification *) aNotification
1795 {
1796 #pragma unused(aNotification)
1797 
1798  if (!fContentView)
1799  return;
1800 
1802  fHasFocus = YES;
1803  //
1804  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
1805  "-windowDidBecomeKey:, gVirtualX is null or not of TGCocoa type");
1806  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
1808  }
1809 }
1810 
1811 
1812 //______________________________________________________________________________
1813 - (void) windowDidResignKey : (NSNotification *) aNotification
1814 {
1815 #pragma unused(aNotification)
1816  fHasFocus = NO;
1817 }
1818 
1819 @end
1820 
1821 #pragma mark - Passive key grab info.
1822 
1823 @implementation PassiveKeyGrab
1824 
1825 //______________________________________________________________________________
1826 - (id) initWithKey : (unichar) keyCode modifiers : (NSUInteger) modifiers
1827 {
1828  if (self = [super init]) {
1829  fKeyCode = keyCode;
1830  fModifiers = modifiers;
1831  }
1832 
1833  return self;
1834 }
1835 
1836 //______________________________________________________________________________
1837 - (BOOL) matchKey : (unichar) keyCode modifiers : (NSUInteger) modifiers
1838 {
1839  return keyCode == fKeyCode && modifiers == fModifiers;
1840 }
1841 
1842 //______________________________________________________________________________
1843 - (BOOL) matchKey : (unichar) keyCode
1844 {
1845  return keyCode == fKeyCode;
1846 }
1847 
1848 //______________________________________________________________________________
1849 - (unichar) fKeyCode
1850 {
1851  return fKeyCode;
1852 }
1853 
1854 //______________________________________________________________________________
1855 - (NSUInteger) fModifiers
1856 {
1857  return fModifiers;
1858 }
1859 
1860 @end
1861 
1862 #pragma mark - X11 property emulation.
1863 
1864 @interface QuartzWindowProperty : NSObject {
1865  NSData *fPropertyData;
1866  Atom_t fType;
1867  unsigned fFormat;
1868 }
1869 
1870 @property (nonatomic, readonly) Atom_t fType;
1871 
1872 @end
1873 
1874 @implementation QuartzWindowProperty
1875 
1876 @synthesize fType;
1877 
1878 //______________________________________________________________________________
1879 - (id) initWithData : (unsigned char *) data size : (unsigned) dataSize type : (Atom_t) type format : (unsigned) format
1880 {
1881  if (self = [super init]) {
1882  //Memory is zero-initialized, but just to make it explicit:
1883  fPropertyData = nil;
1884  fType = 0;
1885  fFormat = 0;
1886 
1887  [self resetPropertyData : data size : dataSize type : type format : format];
1888  }
1889 
1890  return self;
1891 }
1892 
1893 //______________________________________________________________________________
1894 - (void) dealloc
1895 {
1896  [fPropertyData release];
1897 
1898  [super dealloc];
1899 }
1900 
1901 //______________________________________________________________________________
1902 - (void) resetPropertyData : (unsigned char *) data size : (unsigned) dataSize
1903  type : (Atom_t) type format : (unsigned) format
1904 {
1905  [fPropertyData release];
1906 
1907  fFormat = format;
1908  if (format == 16)
1909  dataSize *= 2;
1910  else if (format == 32)
1911  dataSize *= 4;
1912 
1913  fPropertyData = [[NSData dataWithBytes : data length : dataSize] retain];
1914 
1915  fType = type;
1916 }
1917 
1918 //______________________________________________________________________________
1919 - (NSData *) fPropertyData
1920 {
1921  return fPropertyData;
1922 }
1923 
1924 //______________________________________________________________________________
1925 - (unsigned) fFormat
1926 {
1927  return fFormat;
1928 }
1929 
1930 @end
1931 
1932 #pragma mark - QuartzView.
1933 
1934 //
1935 //QuartzView is a children view (also is a content view for a top-level QuartzWindow).
1936 //
1937 
1938 @implementation QuartzView
1939 
1940 @synthesize fID;
1941 @synthesize fContext;
1942 /////////////////////
1943 //SetWindowAttributes_t/WindowAttributes_t
1944 @synthesize fEventMask;
1945 @synthesize fClass;
1946 @synthesize fDepth;
1947 @synthesize fBitGravity;
1948 @synthesize fWinGravity;
1949 @synthesize fBackgroundPixel;
1950 @synthesize fOverrideRedirect;
1951 //SetWindowAttributes_t/WindowAttributes_t
1952 /////////////////////
1953 @synthesize fHasFocus;
1954 @synthesize fParentView;
1955 
1956 @synthesize fPassiveGrabButton;
1957 @synthesize fPassiveGrabEventMask;
1958 @synthesize fPassiveGrabKeyModifiers;
1959 @synthesize fActiveGrabEventMask;
1960 @synthesize fPassiveGrabOwnerEvents;
1961 @synthesize fSnapshotDraw;
1962 @synthesize fCurrentCursor;
1963 @synthesize fIsDNDAware;
1964 
1965 #pragma mark - Lifetime.
1966 
1967 //______________________________________________________________________________
1968 - (id) initWithFrame : (NSRect) frame windowAttributes : (const SetWindowAttributes_t *)attr
1969 {
1970  if (self = [super initWithFrame : frame]) {
1971  //Make this explicit (though memory is zero initialized).
1972  fBackBuffer = nil;
1973  fID = 0;
1974 
1975  //Passive grab parameters.
1976  fPassiveGrabButton = -1;//0 is kAnyButton.
1979 
1980  fPassiveKeyGrabs = [[NSMutableArray alloc] init];
1981 
1982  [self setCanDrawConcurrently : NO];
1983 
1984  [self setHidden : YES];
1985  //Actually, check if view need this.
1986  //
1987  if (attr)
1988  X11::SetWindowAttributes(attr, self);
1989 
1991  fX11Properties = [[NSMutableDictionary alloc] init];
1992 
1995  fActiveGrabOwnerEvents = YES;
1996  }
1997 
1998  return self;
1999 }
2000 
2001 //______________________________________________________________________________
2002 - (void) dealloc
2003 {
2004  [fBackBuffer release];
2005  [fPassiveKeyGrabs release];
2006  [fX11Properties release];
2007  [fBackgroundPixmap release];
2008  [super dealloc];
2009 }
2010 
2011 #pragma mark - Tracking area.
2012 
2013 //Tracking area is required to ... track mouse motion events inside a view.
2014 
2015 //______________________________________________________________________________
2016 - (void) updateTrackingAreas
2017 {
2018  [super updateTrackingAreas];
2019 
2020  if (!fID)
2021  return;
2022 
2023  const Util::AutoreleasePool pool;
2024 
2025  if (NSArray *trackingArray = [self trackingAreas]) {
2026  const NSUInteger size = [trackingArray count];
2027  for (NSUInteger i = 0; i < size; ++i) {
2028  NSTrackingArea * const t = [trackingArray objectAtIndex : i];
2029  [self removeTrackingArea : t];
2030  }
2031  }
2032 
2033  const NSUInteger trackerOptions = NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited |
2034  NSTrackingActiveInActiveApp | NSTrackingInVisibleRect |
2035  NSTrackingEnabledDuringMouseDrag | NSTrackingCursorUpdate;
2036 
2037  NSRect frame = {};
2038  frame.size.width = self.fWidth;
2039  frame.size.height = self.fHeight;
2040 
2041  NSTrackingArea * const tracker = [[NSTrackingArea alloc] initWithRect : frame
2042  options : trackerOptions owner : self userInfo : nil];
2043  [self addTrackingArea : tracker];
2044  [tracker release];
2045 }
2046 
2047 //______________________________________________________________________________
2048 - (void) updateTrackingAreasAfterRaise
2049 {
2050  [self updateTrackingAreas];
2051 
2052  for (QuartzView *childView in [self subviews])
2053  [childView updateTrackingAreasAfterRaise];
2054 }
2055 
2056 #pragma mark - X11Drawable protocol.
2057 
2058 //______________________________________________________________________________
2059 - (BOOL) fIsPixmap
2060 {
2061  return NO;
2062 }
2063 
2064 //______________________________________________________________________________
2066 {
2067  return NO;
2068 }
2069 
2070 //______________________________________________________________________________
2071 - (CGFloat) fScaleFactor
2072 {
2073  return self.fQuartzWindow.fScaleFactor;
2074 }
2075 
2076 //______________________________________________________________________________
2077 - (int) fX
2078 {
2079  return self.frame.origin.x;
2080 }
2081 
2082 //______________________________________________________________________________
2083 - (int) fY
2084 {
2085  return self.frame.origin.y;
2086 }
2087 
2088 //______________________________________________________________________________
2089 - (unsigned) fWidth
2090 {
2091  return self.frame.size.width;
2092 }
2093 
2094 //______________________________________________________________________________
2095 - (unsigned) fHeight
2096 {
2097  return self.frame.size.height;
2098 }
2099 
2100 //______________________________________________________________________________
2101 - (void) setDrawableSize : (NSSize) newSize
2102 {
2103  assert(!(newSize.width < 0) && "-setDrawableSize, width is negative");
2104  assert(!(newSize.height < 0) && "-setDrawableSize, height is negative");
2105 
2106  //This will cause redraw(?)
2107 
2108  //In X11, resize changes the size, but upper-left corner is not changed.
2109  //In Cocoa, bottom-left is fixed.
2110  NSRect frame = self.frame;
2111  frame.size = newSize;
2112 
2113  self.frame = frame;
2114 }
2115 
2116 //______________________________________________________________________________
2117 - (void) setX : (int) x Y : (int) y width : (unsigned) w height : (unsigned) h
2118 {
2119  NSRect newFrame = {};
2120  newFrame.origin.x = x;
2121  newFrame.origin.y = y;
2122  newFrame.size.width = w;
2123  newFrame.size.height = h;
2124 
2125  self.frame = newFrame;
2126 }
2127 
2128 //______________________________________________________________________________
2129 - (void) setX : (int) x Y : (int) y
2130 {
2131  NSRect newFrame = self.frame;
2132  newFrame.origin.x = x;
2133  newFrame.origin.y = y;
2134 
2135  self.frame = newFrame;
2136 }
2137 
2138 //______________________________________________________________________________
2139 - (void) copyImage : (QuartzImage *) srcImage area : (X11::Rectangle) area
2140  withMask : (QuartzImage *) mask clipOrigin : (X11::Point) clipXY
2141  toPoint : (X11::Point) dstPoint
2142 {
2143  //Check parameters.
2144  assert(srcImage != nil &&
2145  "-copyImage:area:withMask:clipOrigin:toPoint:, parameter 'srcImage' is nil");
2146  assert(srcImage.fImage != nil &&
2147  "-copyImage:area:withMask:clipOrigin:toPoint:, srcImage.fImage is nil");
2148 
2149  //Check self.
2150  assert(self.fContext != 0 &&
2151  "-copyImage:area:withMask:clipOrigin:toPoint:, self.fContext is null");
2152 
2153  if (!X11::AdjustCropArea(srcImage, area)) {
2154  NSLog(@"QuartzView: -copyImage:area:withMask:clipOrigin:toPoint:,"
2155  " srcRect and copyRect do not intersect");
2156  return;
2157  }
2158 
2159  //No RAII for subImage, since it can be really subimage or image itself and
2160  //in these cases there is no need to release image.
2161  CGImageRef subImage = 0;
2162  bool needSubImage = false;
2163  if (area.fX || area.fY || area.fWidth != srcImage.fWidth || area.fHeight != srcImage.fHeight) {
2164  needSubImage = true;
2165  subImage = X11::CreateSubImage(srcImage, area);
2166  if (!subImage) {
2167  NSLog(@"QuartzView: -copyImage:area:withMask:clipOrigin:toPoint:,"
2168  " subimage creation failed");
2169  return;
2170  }
2171  } else
2172  subImage = srcImage.fImage;
2173 
2174  //Save context state.
2175  const Quartz::CGStateGuard ctxGuard(self.fContext);
2176 
2177  //Scale and translate to undo isFlipped.
2178  CGContextTranslateCTM(self.fContext, 0., self.fHeight);
2179  CGContextScaleCTM(self.fContext, 1., -1.);
2180  //Set clip mask on a context.
2181 
2182  if (mask) {
2183  assert(mask.fImage != nil &&
2184  "-copyImage:area:withMask:clipOrigin:toPoint:, mask.fImage is nil");
2185  assert(CGImageIsMask(mask.fImage) == true &&
2186  "-copyImage:area:withMask:clipOrigin:toPoint:, mask.fImage is not a mask");
2187  //clipXY.fY = X11::LocalYROOTToCocoa(self, clipXY.fY + mask.fHeight);
2188  const CGFloat clipY = X11::LocalYROOTToCocoa(self, CGFloat(clipXY.fY) + mask.fHeight);
2189  //const CGRect clipRect = CGRectMake(clipXY.fX, clipXY.fY, mask.fWidth, mask.fHeight);
2190  const CGRect clipRect = CGRectMake(clipXY.fX, clipY, mask.fWidth, mask.fHeight);
2191  CGContextClipToMask(self.fContext, clipRect, mask.fImage);
2192  }
2193 
2194  //Convert from X11 to Cocoa (as soon as we scaled y * -1).
2195  //dstPoint.fY = X11::LocalYROOTToCocoa(self, dstPoint.fY + area.fHeight);
2196  const CGFloat dstY = X11::LocalYROOTToCocoa(self, CGFloat(dstPoint.fY) + area.fHeight);
2197  //const CGRect imageRect = CGRectMake(dstPoint.fX, dstPoint.fY, area.fWidth, area.fHeight);
2198  const CGRect imageRect = CGRectMake(dstPoint.fX, dstY, area.fWidth, area.fHeight);
2199  CGContextDrawImage(self.fContext, imageRect, subImage);
2200 
2201  if (needSubImage)
2202  CGImageRelease(subImage);
2203 }
2204 
2205 //______________________________________________________________________________
2206 - (void) copyView : (QuartzView *) srcView area : (X11::Rectangle) area toPoint : (X11::Point) dstPoint
2207 {
2208  //To copy one "window" to another "window", I have to ask source QuartzView to draw intself into
2209  //bitmap, and copy this bitmap into the destination view.
2210 
2211  assert(srcView != nil && "-copyView:area:toPoint:, parameter 'srcView' is nil");
2212 
2213  const NSRect frame = [srcView frame];
2214  //imageRep is in autorelease pool now.
2215  NSBitmapImageRep * const imageRep = [srcView bitmapImageRepForCachingDisplayInRect : frame];
2216  if (!imageRep) {
2217  NSLog(@"QuartzView: -copyView:area:toPoint failed");
2218  return;
2219  }
2220 
2221  assert(srcView != nil && "-copyView:area:toPoint:, parameter 'srcView' is nil");
2222  assert(self.fContext != 0 && "-copyView:area:toPoint, self.fContext is null");
2223 
2224  //It can happen, that src and self are the same.
2225  //cacheDisplayInRect calls drawRect with bitmap context
2226  //(and this will reset self.fContext: I have to save/restore it.
2227  CGContextRef ctx = srcView.fContext;
2228  srcView.fSnapshotDraw = YES;
2229  [srcView cacheDisplayInRect : frame toBitmapImageRep : imageRep];
2230  srcView.fSnapshotDraw = NO;
2231  srcView.fContext = ctx;
2232 
2233  const CGRect subImageRect = CGRectMake(area.fX, area.fY, area.fWidth, area.fHeight);
2234  const Util::CFScopeGuard<CGImageRef> subImage(CGImageCreateWithImageInRect(imageRep.CGImage, subImageRect));
2235 
2236  if (!subImage.Get()) {
2237  NSLog(@"QuartzView: -copyView:area:toPoint, CGImageCreateWithImageInRect failed");
2238  return;
2239  }
2240 
2241  const Quartz::CGStateGuard ctxGuard(self.fContext);
2242  const CGRect imageRect = CGRectMake(dstPoint.fX,
2243  [self visibleRect].size.height - (CGFloat(dstPoint.fY) + area.fHeight),
2244  area.fWidth, area.fHeight);
2245 
2246  CGContextTranslateCTM(self.fContext, 0., [self visibleRect].size.height);
2247  CGContextScaleCTM(self.fContext, 1., -1.);
2248 
2249  CGContextDrawImage(self.fContext, imageRect, subImage.Get());
2250 }
2251 
2252 //______________________________________________________________________________
2253 - (void) copyPixmap : (QuartzPixmap *) srcPixmap area : (X11::Rectangle) area
2254  withMask : (QuartzImage *) mask clipOrigin : (X11::Point) clipXY toPoint : (X11::Point) dstPoint
2255 {
2256  //Check parameters.
2257  assert(srcPixmap != nil && "-copyPixmap:area:withMask:clipOrigin:toPoint:, parameter 'srcPixmap' is nil");
2258 
2259  if (!X11::AdjustCropArea(srcPixmap, area)) {
2260  NSLog(@"QuartzView: -copyPixmap:area:withMask:clipOrigin:toPoint,"
2261  " no intersection between pixmap rectangle and cropArea");
2262  return;
2263  }
2264 
2265  //Check self.
2266  assert(self.fContext != 0 &&
2267  "-copyPixmap:area:withMask:clipOrigin:toPoint:, self.fContext is null");
2268 
2269  //Save context state.
2270  const Quartz::CGStateGuard ctxGuard(self.fContext);
2271 
2272  CGContextTranslateCTM(self.fContext, 0., self.frame.size.height);//???
2273  CGContextScaleCTM(self.fContext, 1., -1.);
2274 
2275  const Util::CFScopeGuard<CGImageRef> imageFromPixmap([srcPixmap createImageFromPixmap]);
2276  assert(imageFromPixmap.Get() != 0 &&
2277  "-copyPixmap:area:withMask:clipOrigin:toPoint:, createImageFromPixmap failed");
2278 
2279  CGImageRef subImage = 0;
2280  bool needSubImage = false;
2281  if (area.fX || area.fY || area.fWidth != srcPixmap.fWidth || area.fHeight != srcPixmap.fHeight) {
2282  needSubImage = true;
2283  const CGRect subImageRect = CGRectMake(area.fX, area.fY, area.fHeight, area.fWidth);
2284  subImage = CGImageCreateWithImageInRect(imageFromPixmap.Get(), subImageRect);
2285  if (!subImage) {
2286  NSLog(@"QuartzView: -copyImage:area:withMask:clipOrigin:toPoint:,"
2287  " subimage creation failed");
2288  return;
2289  }
2290  } else
2291  subImage = imageFromPixmap.Get();
2292 
2293  if (mask) {
2294  assert(mask.fImage != nil &&
2295  "-copyPixmap:area:withMask:clipOrigin:toPoint:, mask.fImage is nil");
2296  assert(CGImageIsMask(mask.fImage) == true &&
2297  "-copyPixmap:area:withMask:clipOrigin:toPoint:, mask.fImage is not a mask");
2298 
2299  //clipXY.fY = X11::LocalYROOTToCocoa(self, clipXY.fY + mask.fHeight);
2300  const CGFloat clipY = X11::LocalYROOTToCocoa(self, CGFloat(clipXY.fY) + mask.fHeight);
2301  //const CGRect clipRect = CGRectMake(clipXY.fX, clipXY.fY, mask.fWidth, mask.fHeight);
2302  const CGRect clipRect = CGRectMake(clipXY.fX, clipY, mask.fWidth, mask.fHeight);
2303  CGContextClipToMask(self.fContext, clipRect, mask.fImage);
2304  }
2305 
2306  //dstPoint.fY = X11::LocalYCocoaToROOT(self, dstPoint.fY + area.fHeight);
2307  const CGFloat dstY = X11::LocalYCocoaToROOT(self, CGFloat(dstPoint.fY) + area.fHeight);
2308  const CGRect imageRect = CGRectMake(dstPoint.fX, dstY, area.fWidth, area.fHeight);
2309  CGContextDrawImage(self.fContext, imageRect, imageFromPixmap.Get());
2310 
2311  if (needSubImage)
2312  CGImageRelease(subImage);
2313 }
2314 
2315 
2316 //______________________________________________________________________________
2317 - (void) copyImage : (QuartzImage *) srcImage area : (X11::Rectangle) area
2318  toPoint : (X11::Point) dstPoint
2319 {
2320  assert(srcImage != nil && "-copyImage:area:toPoint:, parameter 'srcImage' is nil");
2321  assert(srcImage.fImage != nil && "-copyImage:area:toPoint:, srcImage.fImage is nil");
2322  assert(self.fContext != 0 && "-copyImage:area:toPoint:, fContext is null");
2323 
2324  if (!X11::AdjustCropArea(srcImage, area)) {
2325  NSLog(@"QuartzView: -copyImage:area:toPoint, image and copy area do not intersect");
2326  return;
2327  }
2328 
2329  CGImageRef subImage = 0;
2330  bool needSubImage = false;
2331  if (area.fX || area.fY || area.fWidth != srcImage.fWidth || area.fHeight != srcImage.fHeight) {
2332  needSubImage = true;
2333  subImage = X11::CreateSubImage(srcImage, area);
2334  if (!subImage) {
2335  NSLog(@"QuartzView: -copyImage:area:toPoint:, subimage creation failed");
2336  return;
2337  }
2338  } else
2339  subImage = srcImage.fImage;
2340 
2341  const Quartz::CGStateGuard ctxGuard(self.fContext);
2342 
2343  CGContextTranslateCTM(self.fContext, 0., self.fHeight);
2344  CGContextScaleCTM(self.fContext, 1., -1.);
2345 
2346  //dstPoint.fY = X11::LocalYCocoaToROOT(self, dstPoint.fY + area.fHeight);
2347  const CGFloat dstY = X11::LocalYCocoaToROOT(self, CGFloat(dstPoint.fY) + area.fHeight);
2348  //const CGRect imageRect = CGRectMake(dstPoint.fX, dstPoint.fY, area.fWidth, area.fHeight);
2349  const CGRect imageRect = CGRectMake(dstPoint.fX, dstY, area.fWidth, area.fHeight);
2350  CGContextDrawImage(self.fContext, imageRect, subImage);
2351 
2352  if (needSubImage)
2353  CGImageRelease(subImage);
2354 }
2355 
2356 //______________________________________________________________________________
2357 - (void) copy : (NSObject<X11Drawable> *) src area : (X11::Rectangle) area
2358  withMask : (QuartzImage *)mask clipOrigin : (X11::Point) clipXY toPoint : (X11::Point) dstPoint
2359 {
2360  assert(src != nil && "-copy:area:withMask:clipOrigin:toPoint:, parameter 'src' is nil");
2361  assert(area.fWidth && area.fHeight && "-copy:area:withMask:clipOrigin:toPoint:, area to copy is empty");
2362 
2363  if ([src isKindOfClass : [QuartzWindow class]]) {
2364  //Forget about mask (can I have it???)
2365  QuartzWindow * const qw = (QuartzWindow *)src;
2366  //Will not work with OpenGL.
2367  [self copyView : (QuartzView *)qw.fContentView area : area toPoint : dstPoint];
2368  } else if ([src isKindOfClass : [QuartzView class]]) {
2369  //Forget about mask (can I have it???)
2370  [self copyView : (QuartzView *)src area : area toPoint : dstPoint];
2371  } else if ([src isKindOfClass : [QuartzPixmap class]]) {
2372  [self copyPixmap : (QuartzPixmap *)src area : area withMask : mask clipOrigin : clipXY toPoint : dstPoint];
2373  } else if ([src isKindOfClass : [QuartzImage class]]) {
2374  [self copyImage : (QuartzImage *)src area : area withMask : mask clipOrigin : clipXY toPoint : dstPoint];
2375  } else {
2376  assert(0 && "-copy:area:withMask:clipOrigin:toPoint:, src is of unknown type");
2377  }
2378 }
2379 
2380 //______________________________________________________________________________
2381 - (unsigned char *) readColorBits : (X11::Rectangle) area
2382 {
2383  //This is quite a bad idea - to read pixels back from a view,
2384  //but our GUI does exactly this. In case of Cocoa it's expensive
2385  //and not guaranteed to work.
2386 
2387  assert(area.fWidth && area.fHeight && "-readColorBits:, area to copy is empty");
2388 
2389  //int, not unsigned or something - to keep it simple.
2390  const NSRect visRect = [self visibleRect];
2391  const X11::Rectangle srcRect(int(visRect.origin.x), int(visRect.origin.y),
2392  unsigned(visRect.size.width), unsigned(visRect.size.height));
2393 
2394  if (!X11::AdjustCropArea(srcRect, area)) {
2395  NSLog(@"QuartzView: -readColorBits:, visible rect of view and copy area do not intersect");
2396  return nullptr;
2397  }
2398 
2399  //imageRep is autoreleased.
2400  NSBitmapImageRep * const imageRep = [self bitmapImageRepForCachingDisplayInRect : visRect];
2401  if (!imageRep) {
2402  NSLog(@"QuartzView: -readColorBits:, bitmapImageRepForCachingDisplayInRect failed");
2403  return nullptr;
2404  }
2405 
2406  CGContextRef ctx = self.fContext; //Save old context if any.
2407  [self cacheDisplayInRect : visRect toBitmapImageRep : imageRep];
2408  self.fContext = ctx; //Restore old context.
2409  //
2410  const NSInteger bitsPerPixel = [imageRep bitsPerPixel];
2411 
2412  assert(bitsPerPixel == 32 && "-readColorBits:, no alpha channel???");
2413  const NSInteger bytesPerRow = [imageRep bytesPerRow];
2414  unsigned dataWidth = bytesPerRow / (bitsPerPixel / 8);//assume an octet :(
2415 
2416  unsigned char *srcData = nullptr;
2417  std::vector<unsigned char> downscaled;
2418  if ([self.window.screen backingScaleFactor] > 1 && imageRep.CGImage) {
2419  downscaled = X11::DownscaledImageData(area.fWidth, area.fHeight, imageRep.CGImage);
2420  if (downscaled.size())
2421  srcData = &downscaled[0];
2422  dataWidth = area.fWidth;
2423  } else
2424  srcData = [imageRep bitmapData];
2425 
2426  if (!srcData) {
2427  NSLog(@"QuartzView: -readColorBits:, failed to obtain backing store contents");
2428  return nullptr;
2429  }
2430 
2431  //We have a source data now. Let's allocate buffer for ROOT's GUI and convert source data.
2432  unsigned char *data = nullptr;
2433 
2434  try {
2435  data = new unsigned char[area.fWidth * area.fHeight * 4];//bgra?
2436  } catch (const std::bad_alloc &) {
2437  NSLog(@"QuartzView: -readColorBits:, memory allocation failed");
2438  return nullptr;
2439  }
2440 
2441  unsigned char *dstPixel = data;
2442  const unsigned char *line = srcData + area.fY * dataWidth * 4;
2443  const unsigned char *srcPixel = line + area.fX * 4;
2444 
2445  for (unsigned i = 0; i < area.fHeight; ++i) {
2446  for (unsigned j = 0; j < area.fWidth; ++j, srcPixel += 4, dstPixel += 4) {
2447  dstPixel[0] = srcPixel[2];
2448  dstPixel[1] = srcPixel[1];
2449  dstPixel[2] = srcPixel[0];
2450  dstPixel[3] = srcPixel[3];
2451  }
2452 
2453  line += dataWidth * 4;
2454  srcPixel = line + area.fX * 4;
2455  }
2456 
2457  return data;
2458 }
2459 
2460 //______________________________________________________________________________
2461 - (void) setFBackgroundPixmap : (QuartzImage *) pixmap
2462 {
2463  if (fBackgroundPixmap != pixmap) {
2464  [fBackgroundPixmap release];
2465  if (pixmap)
2466  fBackgroundPixmap = [pixmap retain];
2467  else
2468  fBackgroundPixmap = nil;
2469  }
2470 }
2471 
2472 //______________________________________________________________________________
2474 {
2475  //I do not autorelease, screw this idiom!
2476 
2477  return fBackgroundPixmap;
2478 }
2479 
2480 //______________________________________________________________________________
2481 - (int) fMapState
2482 {
2483  if ([self isHidden])
2484  return kIsUnmapped;
2485 
2486  for (QuartzView *parent = fParentView; parent; parent = parent.fParentView) {
2487  if ([parent isHidden])
2488  return kIsUnviewable;
2489  }
2490 
2491  return kIsViewable;
2492 }
2493 
2494 //______________________________________________________________________________
2495 - (BOOL) fHasFocus
2496 {
2497  //With the latest update clang became a bit more stupid.
2498  //Let's write a stupid useless cargo cult code
2499  //to make IT SHUT THE F... UP.
2500  (void)fHasFocus;
2501  return NO;
2502 }
2503 
2504 //______________________________________________________________________________
2505 - (void) setFHasFocus : (BOOL) focus
2506 {
2507 #pragma unused(focus)
2508  //With the latest update clang became a bit more stupid.
2509  //Let's write a stupid useless cargo cult code
2510  //to make IT SHUT THE F... UP.
2511  (void)fHasFocus;
2512 }
2513 
2514 //______________________________________________________________________________
2516 {
2517  return fBackBuffer;//No autorelease, I know the object's lifetime myself.
2518 }
2519 
2520 //______________________________________________________________________________
2521 - (void) setFBackBuffer : (QuartzPixmap *) backBuffer
2522 {
2523  if (fBackBuffer != backBuffer) {
2524  [fBackBuffer release];
2525 
2526  if (backBuffer)
2527  fBackBuffer = [backBuffer retain];
2528  else
2529  fBackBuffer = nil;
2530  }
2531 }
2532 
2533 //______________________________________________________________________________
2535 {
2536  return self;
2537 }
2538 
2539 //______________________________________________________________________________
2541 {
2542  return (QuartzWindow *)[self window];
2543 }
2544 
2545 //______________________________________________________________________________
2547 {
2549 }
2550 
2551 //______________________________________________________________________________
2553 {
2555 }
2556 
2557 //______________________________________________________________________________
2558 - (void) activateGrab : (unsigned) eventMask ownerEvents : (BOOL) ownerEvents
2559 {
2561  fActiveGrabEventMask = eventMask;
2562  fActiveGrabOwnerEvents = ownerEvents;
2563 }
2564 
2565 //______________________________________________________________________________
2567 {
2570  fActiveGrabOwnerEvents = YES;
2571 }
2572 
2573 //______________________________________________________________________________
2574 - (BOOL) acceptsCrossingEvents : (unsigned) eventMask
2575 {
2576  bool accepts = fEventMask & eventMask;
2577 
2578  //In ROOT passive grabs are always with owner_events == true.
2580  accepts = accepts || (fPassiveGrabEventMask & eventMask);
2581 
2584  accepts = accepts || (fActiveGrabOwnerEvents & eventMask);
2585  else
2586  accepts = fActiveGrabOwnerEvents & eventMask;
2587  }
2588 
2589  return accepts;
2590 }
2591 
2592 //______________________________________________________________________________
2593 - (void) addChild : (NSView<X11Window> *) child
2594 {
2595  assert(child != nil && "-addChild:, parameter 'child' is nil");
2596 
2597  [self addSubview : child];
2598  child.fParentView = self;
2599 }
2600 
2601 //______________________________________________________________________________
2602 - (void) getAttributes : (WindowAttributes_t *) attr
2603 {
2604  assert(attr != 0 && "-getAttributes:, parameter 'attr' is null");
2605 
2606  X11::GetWindowAttributes(self, attr);
2607 }
2608 
2609 //______________________________________________________________________________
2610 - (void) setAttributes : (const SetWindowAttributes_t *)attr
2611 {
2612  assert(attr != 0 && "-setAttributes:, parameter 'attr' is null");
2613 
2614 #ifdef DEBUG_ROOT_COCOA
2615  log_attributes(attr, fID);
2616 #endif
2617 
2618  X11::SetWindowAttributes(attr, self);
2619 }
2620 
2621 //______________________________________________________________________________
2623 {
2624  //Move view to the top of subviews.
2625  QuartzView * const parent = fParentView;
2626  [self removeFromSuperview];
2627  [parent addSubview : self];
2628  [self setHidden : NO];
2629 }
2630 
2631 //______________________________________________________________________________
2633 {
2634  [self setHidden : NO];
2635 }
2636 
2637 //______________________________________________________________________________
2639 {
2640  for (QuartzView * v in [self subviews])
2641  [v setHidden : NO];
2642 }
2643 
2644 //______________________________________________________________________________
2646 {
2647  [self setHidden : YES];
2648 }
2649 
2650 //______________________________________________________________________________
2652 {
2653  return fIsOverlapped;
2654 }
2655 
2656 //______________________________________________________________________________
2657 - (void) setOverlapped : (BOOL) overlap
2658 {
2659  fIsOverlapped = overlap;
2660  for (NSView<X11Window> *child in [self subviews])
2661  [child setOverlapped : overlap];
2662 }
2663 
2664 //______________________________________________________________________________
2666 {
2667  //Now, I can not remove window and add it ...
2668  //For example, if you click on a tab, this:
2669  //1. Creates (potentially) a passive button grab
2670  //2. Raises this tab - changes the window order.
2671  //3. On a button release - grab is release.
2672  //The tough problem is, if I remove a view from subviews
2673  //and add it ... it will never receve the
2674  //release event thus a grab will 'hang' on
2675  //view leading to bugs and artifacts.
2676  //So instead I have to ... SORT!!!!!
2677 
2678  using namespace X11;//Comparators.
2679 
2680  for (QuartzView *sibling in [fParentView subviews]) {
2681  if (self == sibling)
2682  continue;
2683  if ([sibling isHidden])
2684  continue;
2685 
2686  if (NSEqualRects(sibling.frame, self.frame)) {
2687  [sibling setOverlapped : YES];
2688  [sibling setHidden : YES];
2689  }
2690  }
2691 
2692  [self setOverlapped : NO];
2693  //
2694  [self setHidden : NO];
2695  //
2696  [fParentView sortSubviewsUsingFunction : CompareViewsToRaise context : (void *)self];
2697  //
2698  [self updateTrackingAreasAfterRaise];
2699  //
2700  [self setNeedsDisplay : YES];
2701 }
2702 
2703 //______________________________________________________________________________
2705 {
2706  //See comment about sorting in -raiseWindow.
2707 
2708  using namespace X11;
2709 
2710  NSEnumerator * const reverseEnumerator = [[fParentView subviews] reverseObjectEnumerator];
2711  for (QuartzView *sibling in reverseEnumerator) {
2712  if (sibling == self)
2713  continue;
2714 
2715  if (NSEqualRects(sibling.frame, self.frame)) {
2716  [sibling setOverlapped : NO];
2717  //
2718  [sibling setHidden : NO];
2719  //
2720  [sibling setNeedsDisplay : YES];
2721  [self setOverlapped : YES];
2722  //
2723  [self setHidden : YES];
2724  //
2725  break;
2726  }
2727  }
2728 
2729  [fParentView sortSubviewsUsingFunction : CompareViewsToLower context : (void*)self];
2730 }
2731 
2732 //______________________________________________________________________________
2733 - (BOOL) isFlipped
2734 {
2735  //Now view's placement, geometry, moving and resizing can be
2736  //done with ROOT's (X11) coordinates without conversion - we're are 'flipped'.
2737  return YES;
2738 }
2739 
2740 //______________________________________________________________________________
2742 {
2743  if (self.fMapState == kIsViewable || fIsOverlapped == YES) {
2745  assert(dynamic_cast<TGCocoa *>(gVirtualX) &&
2746  "-configureNotifyTree, gVirtualX is either null or has type different from TGCocoa");
2747  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2748  vx->GetEventTranslator()->GenerateConfigureNotifyEvent(self, self.frame);
2749  }
2750 
2751  for (NSView<X11Window> *v in [self subviews])
2752  [v configureNotifyTree];
2753  }
2754 }
2755 
2756 #pragma mark - Key grabs.
2757 
2758 //______________________________________________________________________________
2759 - (void) addPassiveKeyGrab : (unichar) keyCode modifiers : (NSUInteger) modifiers
2760 {
2761  [self removePassiveKeyGrab : keyCode modifiers : modifiers];
2762  PassiveKeyGrab * const newGrab = [[PassiveKeyGrab alloc] initWithKey : keyCode
2763  modifiers : modifiers];
2764  [fPassiveKeyGrabs addObject : newGrab];
2765  [newGrab release];
2766 }
2767 
2768 //______________________________________________________________________________
2769 - (void) removePassiveKeyGrab : (unichar) keyCode modifiers : (NSUInteger) modifiers
2770 {
2771  const NSUInteger count = [fPassiveKeyGrabs count];
2772  for (NSUInteger i = 0; i < count; ++i) {
2773  PassiveKeyGrab *grab = [fPassiveKeyGrabs objectAtIndex : i];
2774  if ([grab matchKey : keyCode modifiers : modifiers]) {
2775  [fPassiveKeyGrabs removeObjectAtIndex : i];
2776  break;
2777  }
2778  }
2779 }
2780 
2781 //______________________________________________________________________________
2782 - (PassiveKeyGrab *) findPassiveKeyGrab : (unichar) keyCode modifiers : (NSUInteger) modifiers
2783 {
2784  NSEnumerator * const enumerator = [fPassiveKeyGrabs objectEnumerator];
2785  while (PassiveKeyGrab *grab = (PassiveKeyGrab *)[enumerator nextObject]) {
2786  if ([grab matchKey : keyCode modifiers : modifiers])
2787  return grab;
2788  }
2789 
2790  return nil;
2791 }
2792 
2793 //______________________________________________________________________________
2794 - (PassiveKeyGrab *) findPassiveKeyGrab : (unichar) keyCode
2795 {
2796  //Do not check modifiers.
2797  NSEnumerator * const enumerator = [fPassiveKeyGrabs objectEnumerator];
2798  while (PassiveKeyGrab *grab = (PassiveKeyGrab *)[enumerator nextObject]) {
2799  if ([grab matchKey : keyCode])
2800  return grab;
2801  }
2802 
2803  return nil;
2804 }
2805 
2806 #pragma mark - Painting mechanics.
2807 
2808 //______________________________________________________________________________
2809 - (void) drawRect : (NSRect) dirtyRect
2810 {
2811 #pragma unused(dirtyRect)
2812 
2813  using namespace X11;
2814 
2815  if (fID) {
2816  if (TGWindow * const window = gClient->GetWindowById(fID)) {
2817  //It's never painted, parent renders child. true == check the parent also.
2818  if (ViewIsTextViewFrame(self, true) ||ViewIsHtmlViewFrame(self, true))
2819  return;
2820 
2821  NSGraphicsContext * const nsContext = [NSGraphicsContext currentContext];
2822  assert(nsContext != nil && "-drawRect:, currentContext returned nil");
2823 
2824  TGCocoa * const vx = (TGCocoa *)gVirtualX;
2825  vx->CocoaDrawON();
2826 
2827  fContext = (CGContextRef)[nsContext graphicsPort];
2828  assert(fContext != 0 && "-drawRect:, graphicsPort returned null");
2829 
2830  const Quartz::CGStateGuard ctxGuard(fContext);
2831 
2832  //Non-rectangular windows.
2835 
2836  // This code used to use TObject::InheritsFrom, however since this is
2837  // run under the AppKit, we can not call core/meta functions, otherwise
2838  // we will run into deadlocks.
2839  if (dynamic_cast<const TGContainer*>(window))//It always has an ExposureMask.
2840  vx->GetEventTranslator()->GenerateExposeEvent(self, [self visibleRect]);
2841 
2842  if (fEventMask & kExposureMask) {
2843  if (ViewIsTextView(self)) {
2844  //Send Expose event, using child view (this is how it's done in GUI :( ).
2845  [NSColor.whiteColor setFill];
2846  NSRectFill(dirtyRect);
2847  NSView<X11Window> * const viewFrame = FrameForTextView(self);
2848  if (viewFrame)//Now we set fExposedRegion for TGView.
2849  vx->GetEventTranslator()->GenerateExposeEvent(viewFrame, viewFrame.visibleRect);
2850  }
2851 
2852  if (ViewIsHtmlView(self)) {
2853  NSView<X11Window> *const viewFrame = FrameForHtmlView(self);
2854  if (viewFrame)
2855  vx->GetEventTranslator()->GenerateExposeEvent(viewFrame, viewFrame.visibleRect);
2856  }
2857 
2858  //Ask ROOT's widget/window to draw itself.
2859  gClient->NeedRedraw(window, kTRUE);
2860 
2861  if (!fSnapshotDraw) {
2862  //If Cocoa repaints widget, cancel all ROOT's "outside of paint event"
2863  //rendering into this widget.
2864  gClient->CancelRedraw(window);
2866  }
2867  }
2868 
2869  if (fBackBuffer) {
2870  //Very "special" window.
2871  const X11::Rectangle copyArea(0, 0, fBackBuffer.fWidth, fBackBuffer.fHeight);
2872  [self copy : fBackBuffer area : copyArea withMask : nil
2873  clipOrigin : X11::Point() toPoint : X11::Point()];
2874  }
2875 
2876  vx->CocoaDrawOFF();
2877 #ifdef DEBUG_ROOT_COCOA
2878  CGContextSetRGBStrokeColor(fContext, 1., 0., 0., 1.);
2879  CGContextStrokeRect(fContext, dirtyRect);
2880 #endif
2881 
2882  fContext = 0;
2883  } else {
2884 #ifdef DEBUG_ROOT_COCOA
2885  NSLog(@"QuartzView: -drawRect: method, no window for id %u was found", fID);
2886 #endif
2887  }
2888  }
2889 }
2890 
2891 #pragma mark - Geometry.
2892 
2893 //______________________________________________________________________________
2894 - (void) setFrame : (NSRect) newFrame
2895 {
2896  //In case of TBrowser, setFrame started infinite recursion:
2897  //HandleConfigure for embedded main frame emits signal, slot
2898  //calls layout, layout calls setFrame -> HandleConfigure and etc. etc.
2899  if (NSEqualRects(newFrame, self.frame))
2900  return;
2901 
2902  [super setFrame : newFrame];
2903 }
2904 
2905 //______________________________________________________________________________
2906 - (void) setFrameSize : (NSSize) newSize
2907 {
2908  //Check, if setFrameSize calls setFrame.
2909 
2910  [super setFrameSize : newSize];
2911 
2912  if ((fEventMask & kStructureNotifyMask) && (self.fMapState == kIsViewable || fIsOverlapped == YES)) {
2913  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
2914  "setFrameSize:, gVirtualX is either null or has a type, different from TGCocoa");
2915  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2916  vx->GetEventTranslator()->GenerateConfigureNotifyEvent(self, self.frame);
2917  }
2918 
2919  [self setNeedsDisplay : YES];//?
2920 }
2921 
2922 #pragma mark - Event handling.
2923 
2924 //______________________________________________________________________________
2925 - (void) mouseDown : (NSEvent *) theEvent
2926 {
2927  assert(fID != 0 && "-mouseDown:, fID is 0");
2928 
2929  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
2930  "-mouseDown:, gVirtualX is either null or has a type, different from TGCocoa");
2931  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2932  vx->GetEventTranslator()->GenerateButtonPressEvent(self, theEvent, kButton1);
2933 }
2934 
2935 //______________________________________________________________________________
2936 - (void) scrollWheel : (NSEvent*) theEvent
2937 {
2938  assert(fID != 0 && "-scrollWheel:, fID is 0");
2939 
2940 
2941  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
2942  "-scrollWheel:, gVirtualX is either null or has a type, different from TGCocoa");
2943 
2944  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2945  const CGFloat deltaY = [theEvent deltaY];
2946  if (deltaY < 0) {
2947  vx->GetEventTranslator()->GenerateButtonPressEvent(self, theEvent, kButton5);
2948  vx->GetEventTranslator()->GenerateButtonReleaseEvent(self, theEvent, kButton5);
2949  } else if (deltaY > 0) {
2950  vx->GetEventTranslator()->GenerateButtonPressEvent(self, theEvent, kButton4);
2951  vx->GetEventTranslator()->GenerateButtonReleaseEvent(self, theEvent, kButton4);
2952  }
2953 }
2954 
2955 #ifdef DEBUG_ROOT_COCOA
2956 //______________________________________________________________________________
2957 - (void) printViewInformation
2958 {
2959  assert(fID != 0 && "-printWindowInformation, fID is 0");
2960  const TGWindow * const window = gClient->GetWindowById(fID);
2961  assert(window != 0 && "printWindowInformation, window not found");
2962 
2963  NSLog(@"-----------------View %u info:---------------------", fID);
2964  NSLog(@"ROOT's window class is %s", window->IsA()->GetName());
2965  NSLog(@"event mask is:");
2966  print_mask_info(fEventMask);
2967  NSLog(@"grab mask is:");
2968  print_mask_info(fPassiveGrabEventMask);
2969  NSLog(@"view's geometry: x == %g, y == %g, w == %g, h == %g", self.frame.origin.x,
2970  self.frame.origin.y, self.frame.size.width, self.frame.size.height);
2971  NSLog(@"----------------End of view info------------------");
2972 }
2973 #endif
2974 
2975 //______________________________________________________________________________
2976 - (void) rightMouseDown : (NSEvent *) theEvent
2977 {
2978  assert(fID != 0 && "-rightMouseDown:, fID is 0");
2979 
2980 #ifdef DEBUG_ROOT_COCOA
2981  [self printViewInformation];
2982 #endif
2983 
2984  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
2985  "-rightMouseDown:, gVirtualX is either null or has type different from TGCocoa");
2986  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
2987  vx->GetEventTranslator()->GenerateButtonPressEvent(self, theEvent, kButton3);
2988 }
2989 
2990 //______________________________________________________________________________
2991 - (void) otherMouseDown : (NSEvent *) theEvent
2992 {
2993  assert(fID != 0 && "-otherMouseDown:, fID is 0");
2994 
2995  //Funny enough, [theEvent buttonNumber] is not the same thing as button masked in [NSEvent pressedMouseButtons],
2996  //button number actually is a kind of right operand for bitshift for pressedMouseButtons.
2997  if ([theEvent buttonNumber] == 2) {//this '2' will correspond to '4' in pressedMouseButtons.
2998  //I do not care about mouse buttons after left/right/wheel - ROOT does not have
2999  //any code for this.
3000  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
3001  "-otherMouseDown:, gVirtualX is either null or has type different from TGCocoa");
3002  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
3003  vx->GetEventTranslator()->GenerateButtonPressEvent(self, theEvent, kButton2);
3004  }
3005 }
3006 
3007 //______________________________________________________________________________
3008 - (void) mouseUp : (NSEvent *) theEvent
3009 {
3010  assert(fID != 0 && "-mouseUp:, fID is 0");
3011 
3012  assert(dynamic_cast<TGCocoa *>(gVirtualX) &&
3013  "-mouseUp:, gVirtualX is either null or has type different from TGCocoa");
3014  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
3015  vx->GetEventTranslator()->GenerateButtonReleaseEvent(self, theEvent, kButton1);
3016 }
3017 
3018 //______________________________________________________________________________
3019 - (void) rightMouseUp : (NSEvent *) theEvent
3020 {
3021 
3022  assert(fID != 0 && "-rightMouseUp:, fID is 0");
3023 
3024  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
3025  "-rightMouseUp:, gVirtualX is either null or has type different from TGCocoa");
3026 
3027  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
3028  vx->GetEventTranslator()->GenerateButtonReleaseEvent(self, theEvent, kButton3);
3029 }
3030 
3031 //______________________________________________________________________________
3032 - (void) otherMouseUp : (NSEvent *) theEvent
3033 {
3034  assert(fID != 0 && "-otherMouseUp:, fID is 0");
3035 
3036  //Here I assume it's always kButton2.
3037  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
3038  "-otherMouseUp:, gVirtualX is either null or has type different from TGCocoa");
3039  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
3040  vx->GetEventTranslator()->GenerateButtonReleaseEvent(self, theEvent, kButton2);
3041 }
3042 
3043 //______________________________________________________________________________
3044 - (void) mouseEntered : (NSEvent *) theEvent
3045 {
3046  assert(fID != 0 && "-mouseEntered:, fID is 0");
3047  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
3048  "-mouseEntered:, gVirtualX is null or not of TGCocoa type");
3049 
3050  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
3051  vx->GetEventTranslator()->GenerateCrossingEvent(theEvent);
3052 }
3053 
3054 //______________________________________________________________________________
3055 - (void) mouseExited : (NSEvent *) theEvent
3056 {
3057  assert(fID != 0 && "-mouseExited:, fID is 0");
3058 
3059  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
3060  "-mouseExited:, gVirtualX is null or not of TGCocoa type");
3061 
3062  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
3063  vx->GetEventTranslator()->GenerateCrossingEvent(theEvent);
3064 }
3065 
3066 //______________________________________________________________________________
3067 - (void) mouseMoved : (NSEvent *) theEvent
3068 {
3069  assert(fID != 0 && "-mouseMoved:, fID is 0");
3070 
3071  if (fParentView)//Suppress events in all views, except the top-level one.
3072  return;
3073 
3074  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
3075  "-mouseMoved:, gVirtualX is null or not of TGCocoa type");
3076 
3077  TGCocoa *vx = static_cast<TGCocoa *>(gVirtualX);
3079 }
3080 
3081 //______________________________________________________________________________
3082 - (void) mouseDragged : (NSEvent *) theEvent
3083 {
3084  assert(fID != 0 && "-mouseDragged:, fID is 0");
3085 
3086  TGCocoa * const vx = dynamic_cast<TGCocoa *>(gVirtualX);
3087  assert(vx != 0 && "-mouseDragged:, gVirtualX is null or not of TGCocoa type");
3088 
3090 }
3091 
3092 //______________________________________________________________________________
3093 - (void) rightMouseDragged : (NSEvent *) theEvent
3094 {
3095  assert(fID != 0 && "-rightMouseDragged:, fID is 0");
3096 
3097  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
3098  "-rightMouseDragged:, gVirtualX is null or not of TGCocoa type");
3099 
3100  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
3102 }
3103 
3104 //______________________________________________________________________________
3105 - (void) otherMouseDragged : (NSEvent *) theEvent
3106 {
3107  assert(fID != 0 && "-otherMouseDragged:, fID is 0");
3108 
3109  if ([theEvent buttonNumber] == 2) {
3110  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
3111  "-otherMouseDragged:, gVirtualX is null or not of TGCocoa type");
3112  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
3114  }
3115 }
3116 
3117 //______________________________________________________________________________
3118 - (void) keyDown : (NSEvent *) theEvent
3119 {
3120  assert(fID != 0 && "-keyDown:, fID is 0");
3121 
3122  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
3123  "-keyDown:, gVirtualX is null or not of TGCocoa type");
3124 
3125  NSView<X11Window> *eventView = self;
3126  if (NSView<X11Window> *pointerView = X11::FindViewUnderPointer())
3127  eventView = pointerView;
3128 
3129  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
3130  vx->GetEventTranslator()->GenerateKeyPressEvent(eventView, theEvent);
3131 }
3132 
3133 //______________________________________________________________________________
3134 - (void) keyUp : (NSEvent *) theEvent
3135 {
3136  assert(fID != 0 && "-keyUp:, fID is 0");
3137 
3138  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
3139  "-keyUp:, gVirtualX is null or not of TGCocoa type");
3140 
3141  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
3142  NSView<X11Window> *eventView = self;
3143  if (NSView<X11Window> *pointerView = X11::FindViewUnderPointer())
3144  eventView = pointerView;
3145 
3146  vx->GetEventTranslator()->GenerateKeyReleaseEvent(eventView, theEvent);
3147 }
3148 
3149 #pragma mark - First responder stuff.
3150 
3151 //______________________________________________________________________________
3152 - (BOOL) acceptsFirstMouse : (NSEvent *) theEvent
3153 {
3154 #pragma unused(theEvent)
3155  return YES;
3156 }
3157 
3158 //______________________________________________________________________________
3159 - (BOOL) acceptsFirstResponder
3160 {
3161  return YES;
3162 }
3163 
3164 #pragma mark - Cursors.
3165 
3166 //______________________________________________________________________________
3167 - (void) setFCurrentCursor : (ECursor) cursor
3168 {
3169  if (cursor != fCurrentCursor) {
3170  fCurrentCursor = cursor;
3171  [self.fQuartzWindow invalidateCursorRectsForView : self];
3172  }
3173 }
3174 
3175 //______________________________________________________________________________
3176 - (NSCursor *) createCustomCursor
3177 {
3178  const char *pngFileName = 0;
3179 
3180  switch (fCurrentCursor) {
3181  case kMove:
3182  pngFileName = "move_cursor.png";
3183  break;
3184  case kArrowHor:
3185  pngFileName = "hor_arrow_cursor.png";
3186  break;
3187  case kArrowVer:
3188  pngFileName = "ver_arrow_cursor.png";
3189  break;
3190  case kArrowRight:
3191  pngFileName = "right_arrow_cursor.png";
3192  break;
3193  case kRotate:
3194  pngFileName = "rotate.png";
3195  break;
3196  case kBottomLeft:
3197  case kTopRight:
3198  pngFileName = "top_right_cursor.png";
3199  break;
3200  case kTopLeft:
3201  case kBottomRight:
3202  pngFileName = "top_left_cursor.png";
3203  break;
3204  default:;
3205  }
3206 
3207  if (pngFileName) {
3208  const char * const path = gSystem->Which(TROOT::GetIconPath(), pngFileName, kReadPermission);
3209  const Util::ScopedArray<const char> arrayGuard(path);
3210 
3211  if (!path || path[0] == 0) {
3212  //File was not found.
3213  return nil;
3214  }
3215 
3216  NSString *nsPath = [NSString stringWithFormat : @"%s", path];//in autorelease pool.
3217  NSImage * const cursorImage = [[NSImage alloc] initWithContentsOfFile : nsPath];
3218 
3219  if (!cursorImage)
3220  return nil;
3221 
3222  NSPoint hotSpot = X11::GetCursorHotStop(cursorImage, fCurrentCursor);
3223  NSCursor * const customCursor = [[[NSCursor alloc] initWithImage : cursorImage
3224  hotSpot : hotSpot] autorelease];
3225 
3226  [cursorImage release];
3227 
3228  return customCursor;
3229  }
3230 
3231  return nil;
3232 }
3233 
3234 //______________________________________________________________________________
3235 - (void) resetCursorRects
3236 {
3237  if (NSCursor * const cursor = X11::CreateCursor(fCurrentCursor))
3238  [self addCursorRect : self.visibleRect cursor : cursor];
3239 }
3240 
3241 //______________________________________________________________________________
3242 - (void) cursorUpdate
3243 {
3244  if (NSCursor * const cursor = X11::CreateCursor(fCurrentCursor)) {
3245  // NB: [window invalidateCursorRectsForView] called here has the
3246  // same problem as commented below in -cursorUpdate:.
3247  [cursor set];
3248  }
3249 }
3250 
3251 //______________________________________________________________________________
3252 - (void) cursorUpdate : (NSEvent *) event
3253 {
3254 #pragma unused(event)
3255  // It looks like [NSCursor set] method does not work properly when called from
3256  // cursorUpdate:, having, say, a parent frame with 'arrow' cursor and a child (completely
3257  // filling its parent's area) with 'cross', it happens the 'cross' cursor is not always
3258  // set correctly, for example:
3259  // if we have a TCanvas and resize it, cursor is 'arrow' inside this canvas,
3260  // though it must be 'cross'. This all, as it always happesn with "thinking different"
3261  // Apple is somehow related to run loop or something. As always, it's not documented,
3262  // so Apple can continue to think different. The idea with performSelector comes from:
3263  // http://stackoverflow.com/questions/8430236/nscursor-set-method-has-no-effect
3264  // Or may be it's just a bug:
3265  // http://stackoverflow.com/questions/13901232/nscursor-set-not-working-on-unfocused-window
3266  [self performSelector : @selector(cursorUpdate) withObject : nil afterDelay : 0.05f];
3267 }
3268 
3269 #pragma mark - Emulated X11 properties.
3270 
3271 //______________________________________________________________________________
3272 - (void) setProperty : (const char *) propName data : (unsigned char *) propData
3273  size : (unsigned) dataSize forType : (Atom_t) dataType format : (unsigned) format
3274 {
3275  assert(propName != 0 && "-setProperty:data:size:forType:, parameter 'propName' is null");
3276  assert(propData != 0 && "-setProperty:data:size:forType:, parameter 'propData' is null");
3277  assert(dataSize != 0 && "-setProperty:data:size:forType:, parameter 'dataSize' is 0");
3278 
3279  NSString * const key = [NSString stringWithCString : propName encoding : NSASCIIStringEncoding];
3280  QuartzWindowProperty * property = (QuartzWindowProperty *)[fX11Properties valueForKey : key];
3281 
3282  //At the moment (and I think this will never change) TGX11 always calls XChangeProperty with PropModeReplace.
3283  if (property)
3284  [property resetPropertyData : propData size : dataSize type : dataType format : format];
3285  else {
3286  //No property found, add a new one.
3287  property = [[QuartzWindowProperty alloc] initWithData : propData size : dataSize
3288  type : dataType format : format];
3289  [fX11Properties setObject : property forKey : key];
3290  [property release];
3291  }
3292 }
3293 
3294 //______________________________________________________________________________
3295 - (BOOL) hasProperty : (const char *) propName
3296 {
3297  assert(propName != 0 && "-hasProperty:, propName parameter is null");
3298 
3299  NSString * const key = [NSString stringWithCString : propName encoding : NSASCIIStringEncoding];
3300  QuartzWindowProperty * const property = (QuartzWindowProperty *)[fX11Properties valueForKey : key];
3301 
3302  return property != nil;
3303 }
3304 
3305 //______________________________________________________________________________
3306 - (unsigned char *) getProperty : (const char *) propName returnType : (Atom_t *) type
3307  returnFormat : (unsigned *) format nElements : (unsigned *) nElements
3308 {
3309  assert(propName != 0 &&
3310  "-getProperty:returnType:returnFormat:nElements:, parameter 'propName' is null");
3311  assert(type != 0 &&
3312  "-getProperty:returnType:returnFormat:nElements:, parameter 'type' is null");
3313  assert(format != 0 &&
3314  "-getProperty:returnType:returnFormat:nElements:, parameter 'format' is null");
3315  assert(nElements != 0 &&
3316  "-getProperty:returnType:returnFormat:nElements:, parameter 'nElements' is null");
3317 
3318  NSString * const key = [NSString stringWithCString : propName encoding : NSASCIIStringEncoding];
3319  QuartzWindowProperty * const property = (QuartzWindowProperty *)[fX11Properties valueForKey : key];
3320  assert(property != 0 &&
3321  "-getProperty:returnType:returnFormat:nElements, property not found");
3322 
3323  NSData * const propData = property.fPropertyData;
3324 
3325  const NSUInteger dataSize = [propData length];
3326  unsigned char *buff = 0;
3327  try {
3328  buff = new unsigned char[dataSize]();
3329  } catch (const std::bad_alloc &) {
3330  //Hmm, can I log, if new failed? :)
3331  NSLog(@"QuartzWindow: -getProperty:returnType:returnFormat:nElements:,"
3332  " memory allocation failed");
3333  return 0;
3334  }
3335 
3336  [propData getBytes : buff length : dataSize];
3337  *format = property.fFormat;
3338 
3339  *nElements = dataSize;
3340 
3341  if (*format == 16)
3342  *nElements= dataSize / 2;
3343  else if (*format == 32)
3344  *nElements = dataSize / 4;
3345 
3346  *type = property.fType;
3347 
3348  return buff;
3349 }
3350 
3351 //______________________________________________________________________________
3352 - (void) removeProperty : (const char *) propName
3353 {
3354  assert(propName != 0 && "-removeProperty:, parameter 'propName' is null");
3355 
3356  NSString * const key = [NSString stringWithCString : propName
3357  encoding : NSASCIIStringEncoding];
3358  [fX11Properties removeObjectForKey : key];
3359 }
3360 
3361 //DND
3362 //______________________________________________________________________________
3363 - (NSDragOperation) draggingEntered : (id<NSDraggingInfo>) sender
3364 {
3365  NSPasteboard * const pasteBoard = [sender draggingPasteboard];
3366  const NSDragOperation sourceDragMask = [sender draggingSourceOperationMask];
3367 
3368  if ([[pasteBoard types] containsObject : NSFilenamesPboardType] && (sourceDragMask & NSDragOperationCopy))
3369  return NSDragOperationCopy;
3370 
3371  return NSDragOperationNone;
3372 }
3373 
3374 //______________________________________________________________________________
3375 - (BOOL) performDragOperation : (id<NSDraggingInfo>) sender
3376 {
3377  //We can drag some files (images, pdfs, source code files) from
3378  //finder to ROOT's window (mainly TCanvas or text editor).
3379  //The logic is totally screwed here :((( - ROOT will try to
3380  //read a property of some window (not 'self', unfortunately) -
3381  //this works since on Window all data is in a global clipboard
3382  //(on X11 it simply does not work at all).
3383  //I'm attaching the file name as a property for the top level window,
3384  //there is no other way to make this data accessible for ROOT.
3385 
3386  NSPasteboard * const pasteBoard = [sender draggingPasteboard];
3387  const NSDragOperation sourceDragMask = [sender draggingSourceOperationMask];
3388 
3389  if ([[pasteBoard types] containsObject : NSFilenamesPboardType] && (sourceDragMask & NSDragOperationCopy)) {
3390 
3391  //Here I try to put string ("file://....") into window's property to make
3392  //it accesible from ROOT's GUI.
3393  const Atom_t textUriAtom = gVirtualX->InternAtom("text/uri-list", kFALSE);
3394 
3395  NSArray * const files = [pasteBoard propertyListForType : NSFilenamesPboardType];
3396  for (NSString *path in files) {
3397  //ROOT can not process several files, use the first one.
3398  NSString * const item = [@"file://" stringByAppendingString : path];
3399  //Yes, only ASCII encoding, but after all, ROOT's not able to work with NON-ASCII strings.
3400  const NSUInteger len = [item lengthOfBytesUsingEncoding : NSASCIIStringEncoding] + 1;
3401  try {
3402  std::vector<unsigned char> propertyData(len);
3403  [item getCString : (char *)&propertyData[0] maxLength : propertyData.size()
3404  encoding : NSASCIIStringEncoding];
3405  //There is no any guarantee, that this will ever work, logic in TGDNDManager is totally crazy.
3406  NSView<X11Window> * const targetView = self.fQuartzWindow.fContentView;
3407  [targetView setProperty : "_XC_DND_DATA" data : &propertyData[0]
3408  size : propertyData.size() forType : textUriAtom format : 8];
3409  } catch (const std::bad_alloc &) {
3410  //Hehe, can I log something in case of bad_alloc??? ;)
3411  NSLog(@"QuartzView: -performDragOperation:, memory allocation failed");
3412  return NO;
3413  }
3414 
3415  break;
3416  }
3417 
3418  //Property is attached now.
3419 
3420  //Gdk on windows creates three events on file drop (WM_DROPFILES): XdndEnter, XdndPosition, XdndDrop.
3421  //1. Dnd enter.
3422  Event_t event1 = {};
3423  event1.fType = kClientMessage;
3424  event1.fWindow = fID;
3425  event1.fHandle = gVirtualX->InternAtom("XdndEnter", kFALSE);
3426  event1.fUser[0] = long(fID);
3427  event1.fUser[2] = textUriAtom;//gVirtualX->InternAtom("text/uri-list", kFALSE);
3428  //
3429  gVirtualX->SendEvent(fID, &event1);
3430 
3431  //2. Dnd position.
3432  Event_t event2 = {};
3433  event2.fType = kClientMessage;
3434  event2.fWindow = fID;
3435  event2.fHandle = gVirtualX->InternAtom("XdndPosition", kFALSE);
3436  event2.fUser[0] = long(fID);
3437  event2.fUser[2] = 0;//Here I have to pack x and y for drop coordinates, shifting by 16 bits.
3438  NSPoint dropPoint = [sender draggingLocation];
3439  //convertPointFromBase is deprecated.
3440  //dropPoint = [self convertPointFromBase : dropPoint];
3441  dropPoint = [self convertPoint : dropPoint fromView : nil];
3442  //
3443  dropPoint = X11::TranslateToScreen(self, dropPoint);
3444  event2.fUser[2] = UShort_t(dropPoint.y) | (UShort_t(dropPoint.x) << 16);
3445 
3446  gVirtualX->SendEvent(fID, &event2);
3447 
3448  Event_t event3 = {};
3449  event3.fType = kClientMessage;
3450  event3.fWindow = fID;
3451  event3.fHandle = gVirtualX->InternAtom("XdndDrop", kFALSE);
3452 
3453  gVirtualX->SendEvent(fID, &event3);
3454  }
3455 
3456  return YES;//Always ok, even if file type is not supported - no need in "animation".
3457 }
3458 
3459 @end
std::vector< unsigned char > DownscaledImageData(unsigned w, unsigned h, CGImageRef image)
int GlobalXCocoaToROOT(CGFloat xCocoa)
BOOL fOverrideRedirect
Definition: QuartzWindow.h:193
void GenerateConfigureNotifyEvent(NSView< X11Window > *view, const NSRect &newFrame)
Definition: X11Events.mm:1149
QuartzWindow * CreateTopLevelWindow(Int_t x, Int_t y, UInt_t w, UInt_t h, UInt_t border, Int_t depth, UInt_t clss, void *visual, SetWindowAttributes_t *attr, UInt_t)
Definition: QuartzWindow.mm:56
NSView< X11Window > * FindViewForPointerEvent(NSEvent *pointerEvent)
void GetRootWindowAttributes(WindowAttributes_t *attr)
unsigned long fBackgroundPixel
Definition: QuartzWindow.h:129
CGFloat fScaleFactor()
void GeneratePointerMotionEvent(NSEvent *theEvent)
Definition: X11Events.mm:1244
void configureNotifyTree()
unsigned fPassiveGrabEventMask
Definition: QuartzWindow.h:199
bool LockFocus(NSView< X11Window > *view)
ROOT::MacOSX::Util::CFScopeGuard< CGImageRef > fImage
Definition: QuartzPixmap.h:96
Returns the available number of logical cores.
Definition: StringConv.hxx:21
void GetWindowAttributes(NSObject< X11Window > *window, WindowAttributes_t *dst)
TLine * line
QuartzWindow * fQuartzWindow
Definition: QuartzWindow.h:140
UInt_t Mask_t
Definition: GuiTypes.h:40
ECursor fCurrentCursor
Definition: QuartzWindow.h:204
ULong_t fBackingPixel
Definition: GuiTypes.h:125
QuartzWindow * FindWindowInPoint(Int_t x, Int_t y)
void mapSubwindows()
void GenerateKeyReleaseEvent(NSView< X11Window > *eventView, NSEvent *theEvent)
Definition: X11Events.mm:1301
static const TString & GetIconPath()
Get the icon path in the installation. Static utility function.
Definition: TROOT.cxx:3065
const Mask_t kButtonMotionMask
Definition: GuiTypes.h:163
const Mask_t kWACursor
Definition: GuiTypes.h:153
unsigned short UShort_t
Definition: RtypesCore.h:38
NSView< X11Window > * FindDNDAwareViewInPoint(NSView *parentView, Window_t dragWinID, Window_t inputWinID, Int_t x, Int_t y, Int_t maxDepth)
bool AdjustCropArea(const Rectangle &srcRect, Rectangle &cropArea)
const Mask_t kLeaveWindowMask
Definition: GuiTypes.h:167
const Mask_t kWABackPixmap
Definition: GuiTypes.h:138
BOOL fIsDNDAware
Definition: QuartzWindow.h:205
const Mask_t kWABorderPixel
Definition: GuiTypes.h:141
CrosshairWindow * findCrosshairWindow()
const Mask_t kWABitGravity
Definition: GuiTypes.h:143
bool ViewIsTextView(NSView< X11Window > *view)
int LocalYCocoaToROOT(NSView< X11Window > *parentView, CGFloat yCocoa)
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition: TObject.h:172
QuartzView * fContentView
Definition: QuartzWindow.h:61
QuartzImage * fShapeCombineMask
Definition: QuartzWindow.h:63
void unmapWindow()
#define gClient
Definition: TGClient.h:166
int Int_t
Definition: RtypesCore.h:43
const Mask_t kWABackingStore
Definition: GuiTypes.h:145
BOOL fIsOpenGLWidget()
virtual char * Which(const char *search, const char *file, EAccessMode mode=kFileExists)
Find location of file in a search path.
Definition: TSystem.cxx:1543
NSView< X11Window > * fContentView
Definition: QuartzWindow.h:268
NSUInteger fModifiers()
unsigned fWidth()
unsigned char * readColorBits:(ROOT::MacOSX::X11::Rectangle area)
QuartzPixmap * fBackBuffer
Definition: QuartzWindow.h:207
NSCursor * CreateCustomCursor(ECursor currentCursor)
Window_t fWindow
Definition: GuiTypes.h:175
const NSUInteger kTitledWindowMask
NSPoint start1
Definition: QuartzWindow.h:30
void addCrosshairWindow()
BOOL fIsOpenGLWidget()
const NSUInteger kResizableWindowMask
int GlobalYROOTToCocoa(CGFloat yROOT)
void GenerateButtonPressEvent(NSView< X11Window > *eventView, NSEvent *theEvent, EMouseButton btn)
Definition: X11Events.mm:1257
const Mask_t kWABorderPixmap
Definition: GuiTypes.h:140
unsigned fHeight
Definition: QuartzPixmap.h:94
ULong_t fBackgroundPixel
Definition: GuiTypes.h:94
const Mask_t kPointerMotionMask
Definition: GuiTypes.h:162
void addChild:(NSView< X11Window > *child)
Bool_t fMapInstalled
Definition: GuiTypes.h:128
void SetWindowAttributes(const SetWindowAttributes_t *attr, NSObject< X11Window > *window)
QuartzView * fParentView
Definition: QuartzWindow.h:196
NSMutableDictionary * fX11Properties
Definition: QuartzWindow.h:211
Double_t x[n]
Definition: legend1.C:17
NSPoint GetCursorHotStop(NSImage *image, ECursor cursor)
NSPoint TranslateCoordinates(NSView< X11Window > *fromView, NSView< X11Window > *toView, NSPoint sourcePoint)
QuartzImage * fBackgroundPixmap
Definition: QuartzWindow.h:212
NSPoint ConvertPointFromBaseToScreen(NSWindow *window, NSPoint windowPoint)
ULong_t fBackingPlanes
Definition: GuiTypes.h:124
Handle_t fHandle
Definition: GuiTypes.h:184
QuartzWindow * FindWindowForPointerEvent(NSEvent *pointerEvent)
void adjustCrosshairWindowGeometry:(CrosshairWindow *win)
Handle_t Atom_t
Definition: GuiTypes.h:36
Colormap_t fColormap
Definition: GuiTypes.h:127
CGFloat fScaleFactor()
unsigned fHeight
Definition: QuartzPixmap.h:38
ROOT::MacOSX::X11::PointerGrab fCurrentGrabType
Definition: QuartzWindow.h:214
QuartzWindow * fQuartzWindow
Definition: QuartzWindow.h:269
CGImageRef CreateSubImage(QuartzImage *image, const Rectangle &area)
ROOT::MacOSX::X11::Rectangle GetDisplayGeometry() const
Definition: TGCocoa.mm:576
const Mask_t kWAColormap
Definition: GuiTypes.h:152
void lowerWindow()
XFontStruct * id
Definition: TGX11.cxx:108
NSPoint TranslateFromScreen(NSPoint point, NSView< X11Window > *to)
void CocoaDrawOFF()
Definition: TGCocoa.mm:4382
NSPoint ConvertPointFromScreenToBase(NSPoint screenPoint, NSWindow *window)
const NSUInteger kClosableWindowMask
const Mask_t kWABackingPlanes
Definition: GuiTypes.h:146
const NSUInteger kMiniaturizableWindowMask
int GlobalXROOTToCocoa(CGFloat xROOT)
const Mask_t kButtonPressMask
Definition: GuiTypes.h:160
NSView< X11Window > * FindViewUnderPointer()
BOOL fHasFocus
Definition: QuartzWindow.h:195
NSPoint TranslateToScreen(NSView< X11Window > *from, NSPoint point)
NSView< X11Window > * FrameForTextView(NSView< X11Window > *textView)
const NSEventType kLeftMouseDown
Long_t fAllEventMasks
Definition: GuiTypes.h:130
int GlobalYCocoaToROOT(CGFloat yCocoa)
bool ViewIsHtmlViewFrame(NSView< X11Window > *view, bool checkParent)
void activatePassiveGrab()
void GenerateExposeEvent(NSView< X11Window > *view, const NSRect &exposedRect)
Definition: X11Events.mm:1174
void setOverlapped:(BOOL overlap)
const Mask_t kWAEventMask
Definition: GuiTypes.h:150
R__EXTERN TSystem * gSystem
Definition: TSystem.h:558
const Mask_t kWASaveUnder
Definition: GuiTypes.h:149
static Atom_t fgDeleteWindowAtom
Definition: TGCocoa.h:469
EGEventType fType
Definition: GuiTypes.h:174
QuartzWindow * FindWindowUnderPointer()
unsigned fHeight()
NSPoint end1
Definition: QuartzWindow.h:31
virtual const char * GetName() const
Return unique name, used in SavePrimitive methods.
Definition: TGWindow.cxx:318
unsigned fWidth
Definition: QuartzPixmap.h:93
const Mask_t kExposureMask
Definition: GuiTypes.h:164
unsigned int UInt_t
Definition: RtypesCore.h:44
EvaluateInfo init(std::vector< RooRealProxy > parameters, std::vector< ArrayWrapper * > wrappers, std::vector< double *> arrays, size_t begin, size_t batchSize)
QuartzView * fParentView
Definition: QuartzWindow.h:138
QuartzView * CreateChildView(QuartzView *parent, Int_t x, Int_t y, UInt_t w, UInt_t h, UInt_t border, Int_t depth, UInt_t clss, void *visual, SetWindowAttributes_t *attr, UInt_t wtype)
Definition: QuartzWindow.mm:85
BOOL fIsOverlapped()
void UnlockFocus(NSView< X11Window > *view)
void PixelToRGB(Pixel_t pixelColor, CGFloat *rgb)
Definition: X11Colors.mm:920
long fEventMask
Definition: QuartzWindow.h:187
void RemoveGraphicsOperationsForWindow(Window_t wid)
Definition: X11Buffer.mm:674
unsigned fID
Definition: QuartzWindow.h:185
#define gVirtualX
Definition: TVirtualX.h:338
#define h(i)
Definition: RSha256.hxx:106
int LocalYROOTToCocoa(NSView< X11Window > *parentView, CGFloat yROOT)
bool ViewIsTextView(unsigned viewID)
const Bool_t kFALSE
Definition: RtypesCore.h:90
unsigned fWidth
Definition: QuartzPixmap.h:37
PyObject * fType
const Mask_t kEnterWindowMask
Definition: GuiTypes.h:166
BOOL fPassiveGrabOwnerEvents
Definition: QuartzWindow.h:202
#define d(i)
Definition: RSha256.hxx:102
CGContextRef fContext
Definition: QuartzWindow.h:186
unsigned fWidth()
QuartzWindow * fMainWindow
Definition: QuartzWindow.h:58
BOOL fSnapshotDraw
Definition: QuartzWindow.h:203
const Mask_t kStructureNotifyMask
Definition: GuiTypes.h:165
bool ViewIsHtmlView(unsigned viewID)
NSView< X11Window > * FrameForHtmlView(NSView< X11Window > *htmlView)
bool ScreenPointIsInView(NSView< X11Window > *view, Int_t x, Int_t y)
const Mask_t kButtonReleaseMask
Definition: GuiTypes.h:161
const NSEventType kRightMouseDown
int type
Definition: TGX11.cxx:120
const Mask_t kWAOverrideRedirect
Definition: GuiTypes.h:148
unsigned long ULong_t
Definition: RtypesCore.h:53
void GenerateCrossingEvent(NSEvent *theEvent)
Definition: X11Events.mm:1192
Window_t fRoot
Definition: GuiTypes.h:119
Double_t y[n]
Definition: legend1.C:17
const Mask_t kWAWinGravity
Definition: GuiTypes.h:144
void WindowLostFocus(Window_t winID)
void ClipToShapeMask(NSView< X11Window > *view, CGContextRef ctx)
const NSUInteger kBorderlessWindowMask
NSCursor * CreateCursor(ECursor currentCursor)
Long_t fUser[5]
Definition: GuiTypes.h:186
bool ViewIsHtmlView(NSView< X11Window > *view)
unsigned long fBackgroundPixel
Definition: QuartzWindow.h:192
void adjustCrosshairWindowGeometry()
unsigned fPassiveGrabKeyModifiers
Definition: QuartzWindow.h:200
ROOT::MacOSX::X11::CommandBuffer * GetCommandBuffer() const
Definition: TGCocoa.mm:4370
BOOL fDelayedTransient
Definition: QuartzWindow.h:62
typedef void((*Func_t)())
Bool_t fOverrideRedirect
Definition: GuiTypes.h:133
Handle_t Window_t
Definition: GuiTypes.h:28
Int_t fFormat
Definition: GuiTypes.h:185
This class implements TVirtualX interface for MacOS X, using Cocoa and Quartz 2D. ...
Definition: TGCocoa.h:58
NSComparisonResult CompareViewsToLower(id view1, id view2, void *context)
const Mask_t kWABorderWidth
Definition: GuiTypes.h:142
NSPoint start2
Definition: QuartzWindow.h:33
void GenerateKeyPressEvent(NSView< X11Window > *eventView, NSEvent *theEvent)
Definition: X11Events.mm:1285
NSComparisonResult CompareViewsToRaise(id view1, id view2, void *context)
BOOL fActiveGrabOwnerEvents
Definition: QuartzWindow.h:216
const Mask_t kWABackPixel
Definition: GuiTypes.h:139
const Mask_t kWABackingPixel
Definition: GuiTypes.h:147
void removeCrosshairWindow()
NSPoint end2
Definition: QuartzWindow.h:34
const Mask_t kWADontPropagate
Definition: GuiTypes.h:151
ECursor
Definition: GuiTypes.h:371
bool ViewIsTextViewFrame(NSView< X11Window > *view, bool checkParent)
void raiseWindow()
const Bool_t kTRUE
Definition: RtypesCore.h:89
void CocoaDrawON()
Definition: TGCocoa.mm:4376
Long_t fYourEventMask
Definition: GuiTypes.h:131
unsigned fActiveGrabEventMask
Definition: QuartzWindow.h:201
ROOT::MacOSX::X11::EventTranslator * GetEventTranslator() const
Definition: TGCocoa.mm:4364
void GenerateFocusChangeEvent(NSView< X11Window > *eventView)
Definition: X11Events.mm:1321
unsigned fHeight()
int fPassiveGrabButton
Definition: QuartzWindow.h:198
void GetWindowGeometry(NSObject< X11Window > *win, WindowAttributes_t *dst)
NSMutableArray * fPassiveKeyGrabs
Definition: QuartzWindow.h:208
void activateImplicitGrab()
void GenerateButtonReleaseEvent(NSView< X11Window > *eventView, NSEvent *theEvent, EMouseButton btn)
Definition: X11Events.mm:1270