Logo ROOT   6.18/05
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
26class TSeqCollection;
27//Handle deprecated symbols
28namespace ROOT {
29namespace MacOSX {
30namespace Details {
31#ifdef MAC_OS_X_VERSION_10_12
32const NSUInteger kEventMaskAny = NSEventMaskAny;
33const NSEventType kApplicationDefined = NSEventTypeApplicationDefined;
34#else
35const NSUInteger kEventMaskAny = NSAnyEventMask;
36const 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
82const Int_t kNFDBITS = (sizeof(Long_t) * 8); // 8 bits per byte
83#ifdef FD_SETSIZE
84const Int_t kFDSETSIZE = FD_SETSIZE; // Linux = 1024 file descriptors
85#else
86const Int_t kFDSETSIZE = 256; // upto 256 file descriptors
87#endif
88
89
90class TFdSet {
91private:
92 ULong_t fds_bits[HOWMANY(kFDSETSIZE, kNFDBITS)];
93public:
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
132namespace ROOT {
133namespace MacOSX {
134namespace Details {
135
136class MacOSXSystem {
137public:
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
163MacOSXSystem *MacOSXSystem::fgInstance = 0;
164
165extern "C" {
166
167//______________________________________________________________________________
168void 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//______________________________________________________________________________
188void 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//______________________________________________________________________________
210MacOSXSystem::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//______________________________________________________________________________
219MacOSXSystem::~MacOSXSystem()
220{
221 if (fCocoaInitialized)
222 CloseFileDescriptors();
223}
224
225//______________________________________________________________________________
226void 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//______________________________________________________________________________
236bool 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//______________________________________________________________________________
268void 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//______________________________________________________________________________
279void 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//______________________________________________________________________________
295void 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
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())
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{
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)
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)
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
#define e(i)
Definition: RSha256.hxx:103
static Int_t init()
int Int_t
Definition: RtypesCore.h:41
const Bool_t kFALSE
Definition: RtypesCore.h:88
unsigned long ULong_t
Definition: RtypesCore.h:51
long Long_t
Definition: RtypesCore.h:50
bool Bool_t
Definition: RtypesCore.h:59
const Bool_t kTRUE
Definition: RtypesCore.h:87
#define ClassImp(name)
Definition: Rtypes.h:365
@ kItimerResolution
Definition: Rtypes.h:60
void Fatal(const char *location, const char *msgfmt,...)
const Int_t kFDSETSIZE
const Int_t kNFDBITS
Binding & operator=(OUT(*fun)(void))
#define gROOT
Definition: TROOT.h:414
typedef void((*Func_t)())
R__EXTERN TFileHandler * gXDisplay
Definition: TSystem.h:561
#define gVirtualX
Definition: TVirtualX.h:345
virtual Int_t GetSize() const
Return the capacity of the collection, i.e.
Definition: TCollection.h:182
virtual Bool_t Notify()
Notify when event occurred on descriptor associated with this handler.
int GetFd() const
bool ProcessPendingEvents()
void AddFileHandler(TFileHandler *fh)
Add a file handler to the list of system file handlers.
void InitializeCocoa()
bool fCocoaInitialized
Definition: TMacOSXSystem.h:60
void WaitEvents(Long_t nextto)
bool CocoaInitialized() const
void DispatchOneEvent(Bool_t pendingOnly)
Dispatch a single event.
TFileHandler * RemoveFileHandler(TFileHandler *fh)
Remove a file handler from the list of file handlers.
void ProcessApplicationDefinedEvent(void *event)
std::unique_ptr< ROOT::MacOSX::Details::MacOSXSystem > fPimpl
Definition: TMacOSXSystem.h:59
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition: TObject.cxx:880
virtual void Fatal(const char *method, const char *msgfmt,...) const
Issue fatal error message.
Definition: TObject.cxx:908
Sequenceable collection abstract base class.
TSeqCollection * fFileHandler
Definition: TSystem.h:298
TFdSet * fReadmask
Definition: TSystem.h:277
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:503
TFdSet * fWritemask
Files that should be checked for read events.
Definition: TSystem.h:278
TFdSet * fSignals
Files with writes waiting.
Definition: TSystem.h:281
Int_t fSigcnt
Definition: TSystem.h:285
TFdSet * fWriteready
Files with reads waiting.
Definition: TSystem.h:280
TSeqCollection * fSignalHandler
Definition: TSystem.h:297
TFdSet * fReadready
Files that should be checked for write events.
Definition: TSystem.h:279
Int_t fNfd
Signals that were trapped.
Definition: TSystem.h:282
TSeqCollection * fTimers
Definition: TSystem.h:296
Bool_t CheckSignals(Bool_t sync)
Check if some signals were raised and call their Notify() member.
Bool_t DispatchTimers(Bool_t mode)
Handle and dispatch timers.
void AddFileHandler(TFileHandler *fh)
Add a file handler to the list of system file handlers.
void DispatchOneEvent(Bool_t pendingOnly=kFALSE)
Dispatch a single event.
Bool_t CheckDescriptors()
Check if there is activity on some file descriptors and call their Notify() member.
TFileHandler * RemoveFileHandler(TFileHandler *fh)
Remove a file handler from the list of file handlers.
const Int_t n
Definition: legend1.C:16
const NSEventType kApplicationDefined
void TMacOSXSystem_ReadCallback(CFFileDescriptorRef fdref, CFOptionFlags, void *)
const NSUInteger kEventMaskAny
void TMacOSXSystem_WriteCallback(CFFileDescriptorRef fdref, CFOptionFlags, void *)
Namespace for new ROOT classes and functions.
Definition: StringConv.hxx:21
#define org(otri, vertexptr)
Definition: triangle.c:1037