Logo ROOT   6.12/07
Reference Guide
TMacOSXSystem.mm
Go to the documentation of this file.
1 // @(#)root/graf2d:$Id$
2 // Author: Timur Pocheptsov 5/12/2011
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2012, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 #include "TMacOSXSystem.h"
13 
14 #include <Cocoa/Cocoa.h>
15 
16 #include "CocoaUtils.h"
17 #include "TVirtualX.h"
18 #include "TError.h"
19 #include "TROOT.h"
20 
21 #include <stdexcept>
22 #include <vector>
23 #include <map>
24 #include <set>
25 
26 class TSeqCollection;
27 //Handle deprecated symbols
28 namespace ROOT {
29 namespace MacOSX {
30 namespace Details {
31 #ifdef MAC_OS_X_VERSION_10_12
32 const NSUInteger kEventMaskAny = NSEventMaskAny;
33 const NSEventType kApplicationDefined = NSEventTypeApplicationDefined;
34 #else
35 const NSUInteger kEventMaskAny = NSAnyEventMask;
36 const NSEventType kApplicationDefined = NSApplicationDefined;
37 #endif
38 }
39 }
40 }
41 
42 
43 //The special class to perform a selector to stop a -run: method.
44 @interface RunStopper : NSObject
45 @end
46 
47 @implementation RunStopper
48 
49 //We attach this delegate only once, when trying to initialize
50 //NSApplication (by calling its -run method).
51 //______________________________________________________________________________
52 - (void) stopRun
53 {
54  [NSApp stop : nil];
55  //This is not enough to stop, from docs:
56  //This method notifies the application that you want to exit the current run loop
57  //as soon as it finishes processing the current NSEvent object.
58  //This method does not forcibly exit the current run loop. Instead it sets a flag
59  //that the application checks only after it finishes dispatching an actual event object.
60 
61 
62  //I'm sending a fake event, to stop.
63  NSEvent* stopEvent = [NSEvent otherEventWithType : ROOT::MacOSX::Details::kApplicationDefined
64  location : NSMakePoint(0., 0.) modifierFlags : 0 timestamp : 0.
65  windowNumber : 0 context : nil subtype: 0 data1 : 0 data2 : 0];
66  [NSApp postEvent : stopEvent atStart : true];
67 }
68 
69 @end
70 
71 
72 //
73 //Stuff which I have to copy from TUnixSystem. Find a better way to organize code.
74 //Fortunately, this does not violate ODR, but still UGLY.
75 //
76 
77 //------------------- Unix TFdSet ----------------------------------------------
78 #ifndef HOWMANY
79 # define HOWMANY(x, y) (((x)+((y)-1))/(y))
80 #endif
81 
82 const Int_t kNFDBITS = (sizeof(Long_t) * 8); // 8 bits per byte
83 #ifdef FD_SETSIZE
84 const Int_t kFDSETSIZE = FD_SETSIZE; // Linux = 1024 file descriptors
85 #else
86 const Int_t kFDSETSIZE = 256; // upto 256 file descriptors
87 #endif
88 
89 
90 class TFdSet {
91 private:
92  ULong_t fds_bits[HOWMANY(kFDSETSIZE, kNFDBITS)];
93 public:
94  TFdSet() { memset(fds_bits, 0, sizeof(fds_bits)); }
95  TFdSet(const TFdSet &org) { memcpy(fds_bits, org.fds_bits, sizeof(org.fds_bits)); }
96  TFdSet &operator=(const TFdSet &rhs)
97  {
98  if (this != &rhs) {
99  memcpy(fds_bits, rhs.fds_bits, sizeof(rhs.fds_bits));
100  }
101  return *this;
102  }
103  void Zero() { memset(fds_bits, 0, sizeof(fds_bits)); }
104  void Set(Int_t n)
105  {
106  if (n >= 0 && n < kFDSETSIZE) {
107  fds_bits[n/kNFDBITS] |= (1UL << (n % kNFDBITS));
108  } else {
109  ::Fatal("TFdSet::Set","fd (%d) out of range [0..%d]", n, kFDSETSIZE-1);
110  }
111  }
112  void Clr(Int_t n)
113  {
114  if (n >= 0 && n < kFDSETSIZE) {
115  fds_bits[n/kNFDBITS] &= ~(1UL << (n % kNFDBITS));
116  } else {
117  ::Fatal("TFdSet::Clr","fd (%d) out of range [0..%d]", n, kFDSETSIZE-1);
118  }
119  }
120  Int_t IsSet(Int_t n)
121  {
122  if (n >= 0 && n < kFDSETSIZE) {
123  return (fds_bits[n/kNFDBITS] & (1UL << (n % kNFDBITS))) != 0;
124  } else {
125  ::Fatal("TFdSet::IsSet","fd (%d) out of range [0..%d]", n, kFDSETSIZE-1);
126  return 0;
127  }
128  }
129  ULong_t *GetBits() { return (ULong_t *)fds_bits; }
130 };
131 
132 namespace ROOT {
133 namespace MacOSX {
134 namespace Details {
135 
136 class MacOSXSystem {
137 public:
138  MacOSXSystem();
139  ~MacOSXSystem();
140 
141  void InitializeCocoa();
142 
143  bool SetFileDescriptors(const TSeqCollection *fileHandlers);
144  void UnregisterFileDescriptor(CFFileDescriptorRef fd);
145  void CloseFileDescriptors();
146 
147  enum DescriptorType {
148  kDTWrite,
149  kDTRead
150  };
151 
152  //Before I had C++11 and auto, now I have ugly typedefs.
153  void SetFileDescriptor(int fd, DescriptorType fdType);
154 
155  std::set<CFFileDescriptorRef> fCFFileDescriptors;
156 
158  bool fCocoaInitialized;
159 
160  static MacOSXSystem *fgInstance;
161 };
162 
163 MacOSXSystem *MacOSXSystem::fgInstance = 0;
164 
165 extern "C" {
166 
167 //______________________________________________________________________________
168 void TMacOSXSystem_ReadCallback(CFFileDescriptorRef fdref, CFOptionFlags /*callBackTypes*/, void * /*info*/)
169 {
170  //Native descriptor.
171  const int nativeFD = CFFileDescriptorGetNativeDescriptor(fdref);
172 
173  //We do not need this descriptor anymore.
174  assert(MacOSXSystem::fgInstance != 0 && "TMacOSXSystem_ReadCallback, MacOSXSystem's singleton is null");
175  MacOSXSystem::fgInstance->UnregisterFileDescriptor(fdref);
176 
177  CFFileDescriptorInvalidate(fdref);
178  CFRelease(fdref);
179 
180  NSEvent *fdEvent = [NSEvent otherEventWithType : kApplicationDefined
181  location : NSMakePoint(0, 0) modifierFlags : 0
182  timestamp: 0. windowNumber : 0 context : nil
183  subtype : 0 data1 : nativeFD data2 : 0];
184  [NSApp postEvent : fdEvent atStart : NO];
185 }
186 
187 //______________________________________________________________________________
188 void TMacOSXSystem_WriteCallback(CFFileDescriptorRef fdref, CFOptionFlags /*callBackTypes*/, void * /*info*/)
189 {
190  //Native descriptor.
191  const int nativeFD = CFFileDescriptorGetNativeDescriptor(fdref);
192 
193  //We do not need this descriptor anymore.
194  assert(MacOSXSystem::fgInstance != 0 && "TMacOSXSystem_WriteCallback, MacOSXSystem's singleton is null");
195  MacOSXSystem::fgInstance->UnregisterFileDescriptor(fdref);
196 
197  CFFileDescriptorInvalidate(fdref);
198  CFRelease(fdref);
199 
200  NSEvent *fdEvent = [NSEvent otherEventWithType : kApplicationDefined
201  location : NSMakePoint(0, 0) modifierFlags : 0
202  timestamp: 0. windowNumber : 0 context : nil
203  subtype : 0 data1 : nativeFD data2 : 0];
204  [NSApp postEvent : fdEvent atStart : NO];
205 }
206 
207 }
208 
209 //______________________________________________________________________________
210 MacOSXSystem::MacOSXSystem()
211  : fPool(true), //Delay the pool creation!
212  fCocoaInitialized(false)
213 {
214  assert(fgInstance == 0 && "MacOSXSystem, fgInstance was initialized already");
215  fgInstance = this;
216 }
217 
218 //______________________________________________________________________________
219 MacOSXSystem::~MacOSXSystem()
220 {
221  if (fCocoaInitialized)
222  CloseFileDescriptors();
223 }
224 
225 //______________________________________________________________________________
226 void MacOSXSystem::InitializeCocoa()
227 {
228  assert(fCocoaInitialized == false && "InitializeCocoa, Cocoa was initialized already");
229 
230  [NSApplication sharedApplication];
231  fPool.Reset();//TODO: test, should it be BEFORE shared application???
232  fCocoaInitialized = true;
233 }
234 
235 //______________________________________________________________________________
236 bool MacOSXSystem::SetFileDescriptors(const TSeqCollection *fileHandlers)
237 {
238  //Allocates some resources and can throw.
239  //So, make sure resources are freed correctly
240  //in case of exception (std::bad_alloc) and
241  //return false. Return true if everything is ok.
242 
243  assert(fileHandlers != 0 && "SetFileDescriptors, parameter 'fileHandlers' is null");
244 
245  try {
246  //This iteration is really stupid: add a null pointer into the middle of collection
247  //and it will stop in the middle! AddFileHandler has a check for this.
248 
249  TIter next(fileHandlers);
250  while (TFileHandler * const handler = static_cast<TFileHandler *>(next())) {
251  assert(handler->GetFd() != -1 && "SetFileDescriptors, invalid file descriptor");
252 
253  if (handler->HasReadInterest())
254  SetFileDescriptor(handler->GetFd(), kDTRead);
255 
256  if (handler->HasWriteInterest())
257  SetFileDescriptor(handler->GetFd(), kDTWrite);
258  }
259  } catch (const std::exception &) {
260  CloseFileDescriptors();
261  return false;
262  }
263 
264  return true;
265 }
266 
267 //______________________________________________________________________________
268 void MacOSXSystem::UnregisterFileDescriptor(CFFileDescriptorRef fd)
269 {
270  //This function does not touch file descriptor (it's invalidated and released externally),
271  //just remove pointer.
272 
273  std::set<CFFileDescriptorRef>::iterator fdIter = fCFFileDescriptors.find(fd);
274  assert(fdIter != fCFFileDescriptors.end() && "InvalidateFileDescriptor, file descriptor was not found");
275  fCFFileDescriptors.erase(fdIter);
276 }
277 
278 //______________________________________________________________________________
279 void MacOSXSystem::CloseFileDescriptors()
280 {
281  //While Core Foundation is not Cocoa, it still should not be used if we are not initializing Cocoa.
282  assert(fCocoaInitialized == true && "CloseFileDescriptors, Cocoa was not initialized");
283 
284  std::set<CFFileDescriptorRef>::iterator fdIter = fCFFileDescriptors.begin(), end = fCFFileDescriptors.end();
285 
286  for (; fdIter != end; ++fdIter) {
287  CFFileDescriptorInvalidate(*fdIter);
288  CFRelease(*fdIter);
289  }
290 
291  fCFFileDescriptors.clear();
292 }
293 
294 //______________________________________________________________________________
295 void MacOSXSystem::SetFileDescriptor(int fd, DescriptorType fdType)
296 {
297  //While CoreFoundation is not Cocoa, it still should not be used if we are not initializing Cocoa.
298  assert(fCocoaInitialized == true && "SetFileDescriptors, Cocoa was not initialized");
299  //-1 can come from TSysEvtHandler's ctor.
300  assert(fd != -1 && "SetFileDescriptor, invalid file descriptor");
301 
302  const bool read = fdType == kDTRead;
303  CFFileDescriptorRef fdref = CFFileDescriptorCreate(kCFAllocatorDefault, fd, false,
305 
306  if (!fdref)
307  throw std::runtime_error("MacOSXSystem::SetFileDescriptors: CFFileDescriptorCreate failed");
308 
309  CFFileDescriptorEnableCallBacks(fdref, read ? kCFFileDescriptorReadCallBack : kCFFileDescriptorWriteCallBack);
310  CFRunLoopSourceRef runLoopSource = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, fdref, 0);
311 
312  if (!runLoopSource) {
313  CFRelease(fdref);
314  throw std::runtime_error("MacOSXSystem::SetFileDescriptors: CFFileDescriptorCreateRunLoopSource failed");
315  }
316 
317  CFRunLoopAddSource(CFRunLoopGetMain(), runLoopSource, kCFRunLoopDefaultMode);
318  CFRelease(runLoopSource);
319 
320  fCFFileDescriptors.insert(fdref);
321 }
322 
323 }//Details
324 }//MacOSX
325 }//ROOT
326 
327 namespace Private = ROOT::MacOSX::Details;
328 
330 
331 //______________________________________________________________________________
333  : fPimpl(new Private::MacOSXSystem),
334  fCocoaInitialized(false),
335  fFirstDispatch(true)
336 {
337 }
338 
339 //______________________________________________________________________________
341 {
342 }
343 
344 //______________________________________________________________________________
346 {
347  //Here I try to emulate TUnixSystem's behavior, which is quite twisted.
348  //I'm not even sure, I need all this code :)
349 
350  if (fFirstDispatch) {
351  if (!fCocoaInitialized && !gROOT->IsBatch())
352  InitializeCocoa();
353 
354  fFirstDispatch = false;
355  }
356 
357  if (!fCocoaInitialized)//We are in a batch mode (or 'batch').
358  return TUnixSystem::DispatchOneEvent(pendingOnly);
359 
360  Bool_t pollOnce = pendingOnly;
361 
362  while (true) {
364 
365  if (ProcessPendingEvents()) {
366  //If we had some events in a system queue, probably,
367  //now we have some events in our own event-queue.
368  if (gXDisplay && gXDisplay->Notify()) {
369  gVirtualX->Update(2);
370  gVirtualX->Update(3);
371  if (!pendingOnly)
372  return;
373  }
374  }
375 
376  //Check for file descriptors ready for reading/writing.
377  if (fNfd > 0 && fFileHandler && fFileHandler->GetSize() > 0)
378  if (CheckDescriptors())
379  if (!pendingOnly)
380  return;
381 
382  fNfd = 0;
383  fReadready->Zero();
384  fWriteready->Zero();
385 
386  if (pendingOnly && !pollOnce)
387  return;
388 
389  // check synchronous signals
390  if (fSigcnt > 0 && fSignalHandler->GetSize() > 0)
391  if (CheckSignals(kTRUE))
392  if (!pendingOnly) return;
393 
394  fSigcnt = 0;
395  fSignals->Zero();
396 
397  // check synchronous timers
398  Long_t nextto = 0;
399  if (fTimers && fTimers->GetSize() > 0) {
400  if (DispatchTimers(kTRUE)) {
401  // prevent timers from blocking file descriptor monitoring
402  nextto = NextTimeOut(kTRUE);
403  if (nextto > kItimerResolution || nextto == -1)
404  return;
405  }
406  }
407 
408  // if in pendingOnly mode poll once file descriptor activity
409  nextto = NextTimeOut(kTRUE);
410 
411  if (pendingOnly) {
412  //if (fFileHandler && !fFileHandler->GetSize())
413  // return;
414  nextto = 0;
415  pollOnce = kFALSE;
416  }
417 
418  //Wait for GUI events and for something else, like read/write from stdin/stdout (?).
419  WaitEvents(nextto);
420 
421  if (gXDisplay && gXDisplay->Notify()) {
422  gVirtualX->Update(2);
423  gVirtualX->Update(3);
424  if (!pendingOnly)
425  return;
426  }
427 
428  if (pendingOnly)
429  return;
430  }
431 }
432 
433 //______________________________________________________________________________
435 {
436  return fCocoaInitialized;
437 }
438 
439 //______________________________________________________________________________
441 {
442  if (fCocoaInitialized)
443  return;
444 
445  //TODO: add error handling and results check.
446 
447  fPimpl->InitializeCocoa();
448 
450 
451  //[NSApplication sharedApplication];//TODO: clean-up this mess with pools and sharedApplication
452  //Documentation says, that +sharedApplication, initializes the app. But this is not true,
453  //it's still not really initialized, part of initialization is done by -run method.
454 
455  //If you call run, it never returns unless app is finished. I have to stop Cocoa's event loop
456  //processing, since we have our own event loop.
457 
458  const ROOT::MacOSX::Util::NSScopeGuard<RunStopper> stopper([[RunStopper alloc] init]);
459 
460  //Delay? What it should be?
461  [stopper.Get() performSelector : @selector(stopRun) withObject : nil afterDelay : 0.05];
462  [NSApp run];
463 
464  fCocoaInitialized = true;
465 }
466 
467 //______________________________________________________________________________
469 {
470  assert(fCocoaInitialized == true && "ProcessPendingEvents, called while Cocoa was not initialized");
471 
472  bool processed = false;
473  while (NSEvent *event = [NSApp nextEventMatchingMask : Private::kEventMaskAny
474  untilDate : nil inMode : NSDefaultRunLoopMode dequeue : YES]) {
475  [NSApp sendEvent : event];
476  processed = true;
477  }
478  return processed;
479 }
480 
481 //______________________________________________________________________________
483 {
484  //Wait for GUI/Non-GUI events.
485 
486  assert(fCocoaInitialized == true && "WaitEvents, called while Cocoa was not initialized");
487 
488  if (fFileHandler && !fPimpl->SetFileDescriptors(fFileHandler)) {
489  //I consider this error as fatal.
490  Fatal("WaitForAllEvents", "SetFileDesciptors failed");
491  }
492 
493  NSDate *untilDate = nil;
494  if (nextto >= 0)//0 also means non-blocking call.
495  untilDate = [NSDate dateWithTimeIntervalSinceNow : nextto / 1000.];
496  else
497  untilDate = [NSDate distantFuture];
498 
499  fReadready->Zero();
500  fWriteready->Zero();
501  fNfd = 0;
502 
503  NSEvent *event = [NSApp nextEventMatchingMask : Private::kEventMaskAny
504  untilDate : untilDate inMode : NSDefaultRunLoopMode dequeue : YES];
505  if (event) {
506  if (event.type == Private::kApplicationDefined)
507  ProcessApplicationDefinedEvent(event);
508  else
509  [NSApp sendEvent : event];
510  }
511 
512  while ((event = [NSApp nextEventMatchingMask : Private::kEventMaskAny
513  untilDate : nil inMode : NSDefaultRunLoopMode dequeue : YES]))
514  {
515  if (event.type == Private::kApplicationDefined)
516  ProcessApplicationDefinedEvent(event);
517  else
518  [NSApp sendEvent : event];
519  }
520 
521  fPimpl->CloseFileDescriptors();
522 
523  gVirtualX->Update(2);
524  gVirtualX->Update(3);
525 }
526 
527 //______________________________________________________________________________
529 {
530  if (fh) {
531  if (fh->GetFd() == -1)//I do not need this crap!
532  Error("AddFileHandler", "invalid file descriptor");
533  else
535  }
536 }
537 
538 //______________________________________________________________________________
540 {
541  if (fh)
543 
544  return 0;
545 }
546 
547 //______________________________________________________________________________
549 {
550  //Right now I have app. defined events only
551  //for file descriptors. This can change in a future.
552 
553  assert(fCocoaInitialized == true &&
554  "ProcessApplicationDefinedEvent, called while Cocoa was not initialized");
555 
556  NSEvent *event = (NSEvent *)e;
557  assert(event != nil &&
558  "ProcessApplicationDefinedEvent, event parameter is nil");
559  assert(event.type == Private::kApplicationDefined &&
560  "ProcessApplicationDefinedEvent, event parameter has wrong type");
561 
562  bool descriptorFound = false;
563 
564  if (fReadmask->IsSet(event.data1)) {
565  fReadready->Set(event.data1);
566  descriptorFound = true;
567  }
568 
569  if (fWritemask->IsSet(event.data1)) {
570  fWriteready->Set(event.data1);
571  descriptorFound = true;
572  }
573 
574  if (!descriptorFound) {
575  Error("ProcessApplicationDefinedEvent", "file descriptor %d was not found", int(event.data1));
576  return;
577  }
578 
579  ++fNfd;
580 }
581 
virtual Bool_t Notify()
Notify when event occurred on descriptor associated with this handler.
bool ProcessPendingEvents()
void TMacOSXSystem_ReadCallback(CFFileDescriptorRef fdref, CFOptionFlags, void *)
Namespace for new ROOT classes and functions.
Definition: StringConv.hxx:21
void Fatal(const char *location, const char *msgfmt,...)
const Int_t kFDSETSIZE
const Int_t kNFDBITS
#define gROOT
Definition: TROOT.h:402
TFileHandler * RemoveFileHandler(TFileHandler *fh)
Remove a file handler from the list of file handlers.
int Int_t
Definition: RtypesCore.h:41
bool Bool_t
Definition: RtypesCore.h:59
bool CocoaInitialized() const
void ProcessApplicationDefinedEvent(void *event)
void DispatchOneEvent(Bool_t pendingOnly)
Dispatch a single event.
Sequenceable collection abstract base class.
void DispatchOneEvent(Bool_t pendingOnly=kFALSE)
Dispatch a single event.
TFileHandler * RemoveFileHandler(TFileHandler *fh)
Remove a file handler from the list of file handlers.
R__EXTERN TFileHandler * gXDisplay
Definition: TSystem.h:541
void InitializeCocoa()
int GetFd() const
const NSEventType kApplicationDefined
void TMacOSXSystem_WriteCallback(CFFileDescriptorRef fdref, CFOptionFlags, void *)
#define gVirtualX
Definition: TVirtualX.h:350
const Bool_t kFALSE
Definition: RtypesCore.h:88
long Long_t
Definition: RtypesCore.h:50
const NSUInteger kEventMaskAny
#define ClassImp(name)
Definition: Rtypes.h:359
static Int_t init()
unsigned long ULong_t
Definition: RtypesCore.h:51
you should not use this method at all Int_t Int_t Double_t Double_t Double_t e
Definition: TRolke.cxx:630
void WaitEvents(Long_t nextto)
Binding & operator=(OUT(*fun)(void))
void AddFileHandler(TFileHandler *fh)
Add a file handler to the list of system file handlers.
#define org(otri, vertexptr)
Definition: triangle.c:1037
typedef void((*Func_t)())
void AddFileHandler(TFileHandler *fh)
Add a file handler to the list of system file handlers.
const Bool_t kTRUE
Definition: RtypesCore.h:87
const Int_t n
Definition: legend1.C:16
void Error(ErrorHandler_t func, int code, const char *va_(fmt),...)
Write error message and call a handler, if required.