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