1 #ifndef SWELL_PROVIDED_BY_APP
3 //#import <Carbon/Carbon.h>
4 #import <Cocoa/Cocoa.h>
6 #include "swell-internal.h"
10 HWND g_swell_only_timerhwnd;
12 @implementation SWELL_TimerFuncTarget
14 -(id) initWithId:(UINT_PTR)tid hwnd:(HWND)h callback:(TIMERPROC)cb
16 if ((self = [super init]))
24 -(void)SWELL_Timer:(id)sender
26 if (g_swell_only_timerhwnd && m_hwnd != g_swell_only_timerhwnd) return;
28 m_cb(m_hwnd,WM_TIMER,m_timerid,GetTickCount());
32 @implementation SWELL_DataHold
33 -(id) initWithVal:(void *)val
35 if ((self = [super init]))
47 void SWELL_CFStringToCString(const void *str, char *buf, int buflen)
49 NSString *s = (NSString *)str;
51 if (buflen <= 1 || !s || [s getCString:buf maxLength:buflen encoding:NSUTF8StringEncoding]) return; // should always work, I'd hope (ambiguous documentation ugh)
53 NSData *data = [s dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
56 [s getCString:buf maxLength:buflen encoding:NSASCIIStringEncoding];
59 int len = [data length];
60 if (len > buflen-1) len=buflen-1;
61 [data getBytes:buf length:len];
65 void *SWELL_CStringToCFString(const char *str)
70 ret=(void *)CFStringCreateWithCString(NULL,str,kCFStringEncodingUTF8);
72 ret=(void*)CFStringCreateWithCString(NULL,str,kCFStringEncodingASCII);
76 void SWELL_MakeProcessFront(HANDLE h)
78 SWELL_InternalObjectHeader_NSTask *buf = (SWELL_InternalObjectHeader_NSTask*)h;
79 if (buf && buf->hdr.type == INTERNAL_OBJECT_NSTASK && buf->task)
81 ProcessSerialNumber psn;
83 int pid=[(id)buf->task processIdentifier];
84 // try for 1sec to get the PSN, if it fails
86 while (GetProcessForPID(pid,&psn) != noErr && n-- > 0)
90 if (n>0) SetFrontProcess(&psn);
94 void SWELL_ReleaseNSTask(void *p)
96 NSTask *a =(NSTask*)p;
99 DWORD SWELL_WaitForNSTask(void *p, DWORD msTO)
101 NSTask *a =(NSTask*)p;
102 DWORD t = msTO ? GetTickCount()+msTO : 0;
105 if (![a isRunning]) return WAIT_OBJECT_0;
108 while (GetTickCount()<t);
110 return [a isRunning] ? WAIT_TIMEOUT : WAIT_OBJECT_0;
113 HANDLE SWELL_CreateProcessIO(const char *exe, int nparams, const char **params, bool redirectIO)
115 NSString *ex = (NSString *)SWELL_CStringToCFString(exe);
116 NSMutableArray *ar = [[NSMutableArray alloc] initWithCapacity:nparams];
119 for (x=0;x <nparams;x++)
121 NSString *s = (NSString *)SWELL_CStringToCFString(params[x]?params[x]:"");
129 tsk = [[NSTask alloc] init];
130 [tsk setLaunchPath:ex];
131 [tsk setArguments:ar];
134 [tsk setStandardInput:[NSPipe pipe]];
135 [tsk setStandardOutput:[NSPipe pipe]];
136 [tsk setStandardError:[NSPipe pipe]];
140 @catch (NSException *exception) {
147 if (tsk) [tsk retain];
150 if (!tsk) return NULL;
152 SWELL_InternalObjectHeader_NSTask *buf = (SWELL_InternalObjectHeader_NSTask*)malloc(sizeof(SWELL_InternalObjectHeader_NSTask));
153 buf->hdr.type = INTERNAL_OBJECT_NSTASK;
159 HANDLE SWELL_CreateProcess(const char *exe, int nparams, const char **params)
161 return SWELL_CreateProcessIO(exe,nparams,params,false);
165 int SWELL_GetProcessExitCode(HANDLE hand)
168 SWELL_InternalObjectHeader_NSTask *hdr=(SWELL_InternalObjectHeader_NSTask*)hand;
169 if (!hdr || hdr->hdr.type != INTERNAL_OBJECT_NSTASK || !hdr->task) return -1;
171 if ([(NSTask *)hdr->task isRunning]) rv=-3;
172 else rv = [(NSTask *)hdr->task terminationStatus];
180 int SWELL_TerminateProcess(HANDLE hand)
183 SWELL_InternalObjectHeader_NSTask *hdr=(SWELL_InternalObjectHeader_NSTask*)hand;
184 if (!hdr || hdr->hdr.type != INTERNAL_OBJECT_NSTASK || !hdr->task) return -1;
187 [(NSTask *)hdr->task terminate];
194 int SWELL_ReadWriteProcessIO(HANDLE hand, int w/*stdin,stdout,stderr*/, char *buf, int bufsz)
196 SWELL_InternalObjectHeader_NSTask *hdr=(SWELL_InternalObjectHeader_NSTask*)hand;
197 if (!hdr || hdr->hdr.type != INTERNAL_OBJECT_NSTASK || !hdr->task) return 0;
198 NSTask *tsk = (NSTask*)hdr->task;
202 case 0: pipe = [tsk standardInput]; break;
203 case 1: pipe = [tsk standardOutput]; break;
204 case 2: pipe = [tsk standardError]; break;
206 if (!pipe || ![pipe isKindOfClass:[NSPipe class]]) return 0;
208 NSFileHandle *fh = w!=0 ? [pipe fileHandleForReading] : [pipe fileHandleForWriting];
214 NSData *d = [NSData dataWithBytes:buf length:bufsz];
217 if (d) [fh writeData:d];
220 @catch (id ex) { bufsz=0; }
230 d = [fh readDataOfLength:(bufsz < 1 ? 32768 : bufsz)];
234 if (!d || bufsz < 1) return d ? [d length] : 0;
236 if (l > bufsz) l = bufsz;
237 [d getBytes:buf length:l];
245 @implementation SWELL_ThreadTmp
250 DWORD (*func)(void *);
251 *(void **)(&func) = a;
258 void SWELL_EnsureMultithreadedCocoa()
264 if (![NSThread isMultiThreaded]) // force cocoa into multithreaded mode
266 SWELL_ThreadTmp *t=[[SWELL_ThreadTmp alloc] init];
269 [NSThread detachNewThreadSelector:@selector(bla:) toTarget:t withObject:t];
275 void CreateThreadNS(void *TA, DWORD stackSize, DWORD (*ThreadProc)(LPVOID), LPVOID parm, DWORD cf, DWORD *tidOut)
277 SWELL_ThreadTmp *t=[[SWELL_ThreadTmp alloc] init];
278 t->a=(void*)ThreadProc;
280 return [NSThread detachNewThreadSelector:@selector(bla:) toTarget:t withObject:t];
284 // used by swell.cpp (lazy these should go elsewhere)
285 void *SWELL_InitAutoRelease()
287 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
290 void SWELL_QuitAutoRelease(void *p)
293 [(NSAutoreleasePool*)p release];
296 void SWELL_RunEvents()
298 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
302 NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate dateWithTimeIntervalSinceNow:0.001] inMode:NSDefaultRunLoopMode dequeue:YES];
304 [NSApp sendEvent:event];
310 typedef struct TimerInfoRec
315 struct TimerInfoRec *_next;
317 static TimerInfoRec *m_timer_list;
318 static WDL_Mutex m_timermutex;
319 static pthread_t m_pmq_mainthread;
320 static void SWELL_pmq_settimer(HWND h, UINT_PTR timerid, UINT rate, TIMERPROC tProc);
322 UINT_PTR SetTimer(HWND hwnd, UINT_PTR timerid, UINT rate, TIMERPROC tProc)
324 if (!hwnd && !tProc) return 0; // must have either callback or hwnd
326 if (hwnd && !timerid) return 0;
328 if (timerid != ~(UINT_PTR)0 && m_pmq_mainthread && pthread_self()!=m_pmq_mainthread)
330 SWELL_pmq_settimer(hwnd,timerid,(rate==(UINT)-1)?((UINT)-2):rate,tProc);
335 if (hwnd && ![(id)hwnd respondsToSelector:@selector(SWELL_Timer:)])
337 if (![(id)hwnd isKindOfClass:[NSWindow class]]) return 0;
338 hwnd=(HWND)[(id)hwnd contentView];
339 if (![(id)hwnd respondsToSelector:@selector(SWELL_Timer:)]) return 0;
342 WDL_MutexLock lock(&m_timermutex);
343 TimerInfoRec *rec=NULL;
349 if (rec->timerid == timerid && rec->hwnd == hwnd) // works for both kinds
358 rec=(TimerInfoRec*)malloc(sizeof(TimerInfoRec));
363 [rec->timer invalidate];
367 rec->timerid=timerid;
372 // set timer to this unique ptr
373 if (!hwnd) timerid = rec->timerid = (UINT_PTR)rec;
375 SWELL_TimerFuncTarget *t = [[SWELL_TimerFuncTarget alloc] initWithId:timerid hwnd:hwnd callback:tProc];
376 rec->timer = [NSTimer scheduledTimerWithTimeInterval:(wdl_max(rate,1)*0.001) target:t selector:@selector(SWELL_Timer:)
377 userInfo:t repeats:YES];
383 SWELL_DataHold *t=[[SWELL_DataHold alloc] initWithVal:(void *)timerid];
384 rec->timer = [NSTimer scheduledTimerWithTimeInterval:(wdl_max(rate,1)*0.001) target:(id)hwnd selector:@selector(SWELL_Timer:)
385 userInfo:t repeats:YES];
389 [[NSRunLoop currentRunLoop] addTimer:rec->timer forMode:(NSString*)kCFRunLoopCommonModes];
393 rec->_next=m_timer_list;
399 void SWELL_RunRunLoop(int ms)
401 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:ms*0.001]];
403 void SWELL_RunRunLoopEx(int ms, HWND hwndOnlyTimer)
405 HWND h=g_swell_only_timerhwnd;
406 g_swell_only_timerhwnd = hwndOnlyTimer;
407 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:ms*0.001]];
408 if (g_swell_only_timerhwnd == hwndOnlyTimer) g_swell_only_timerhwnd = h;
411 BOOL KillTimer(HWND hwnd, UINT_PTR timerid)
413 if (!hwnd && !timerid) return FALSE;
415 WDL_MutexLock lock(&m_timermutex);
416 if (timerid != ~(UINT_PTR)0 && m_pmq_mainthread && pthread_self()!=m_pmq_mainthread)
418 SWELL_pmq_settimer(hwnd,timerid,~(UINT)0,NULL);
423 // don't allow removing all global timers
424 if (timerid!=~(UINT_PTR)0 || hwnd)
426 TimerInfoRec *rec = m_timer_list, *lrec=NULL;
430 if (rec->hwnd == hwnd && (timerid==~(UINT_PTR)0 || rec->timerid == timerid))
432 TimerInfoRec *nrec = rec->_next;
434 // remove self from list
435 if (lrec) lrec->_next = nrec;
436 else m_timer_list = nrec;
438 [rec->timer invalidate];
442 if (timerid!=~(UINT_PTR)0) break;
458 ///////// PostMessage emulation
460 BOOL PostMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
462 id del=[NSApp delegate];
463 if (del && [del respondsToSelector:@selector(swellPostMessage:msg:wp:lp:)])
464 return !![(SWELL_DelegateExtensions*)del swellPostMessage:hwnd msg:message wp:wParam lp:lParam];
468 void SWELL_MessageQueue_Clear(HWND h)
470 id del=[NSApp delegate];
471 if (del && [del respondsToSelector:@selector(swellPostMessageClearQ:)])
472 [(SWELL_DelegateExtensions*)del swellPostMessageClearQ:h];
477 // implementation of postmessage stuff
481 typedef struct PMQ_rec
488 struct PMQ_rec *next;
489 bool is_special_timer; // if set, then msg=interval(-1 for kill),wParam=timer id, lParam = timerproc
492 static WDL_Mutex *m_pmq_mutex;
493 static PMQ_rec *m_pmq, *m_pmq_empty, *m_pmq_tail;
494 static int m_pmq_size;
495 static id m_pmq_timer;
496 #define MAX_POSTMESSAGE_SIZE 1024
498 void SWELL_Internal_PostMessage_Init()
500 if (m_pmq_mutex) return;
501 id del = [NSApp delegate];
502 if (!del || ![del respondsToSelector:@selector(swellPostMessageTick:)]) return;
504 m_pmq_mainthread=pthread_self();
505 m_pmq_mutex = new WDL_Mutex;
507 m_pmq_timer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:(id)del selector:@selector(swellPostMessageTick:) userInfo:nil repeats:YES];
508 [[NSRunLoop currentRunLoop] addTimer:m_pmq_timer forMode:(NSString*)kCFRunLoopCommonModes];
510 // set a timer to the delegate
513 #ifndef SWELL_JUST_FOR_THREADING
514 void SWELL_MessageQueue_Flush()
516 if (!m_pmq_mutex) return;
518 m_pmq_mutex->Enter();
519 int max_amt = m_pmq_size;
524 if (m_pmq_tail == p) m_pmq_tail=NULL;
527 m_pmq_mutex->Leave();
532 if (p->is_special_timer)
534 if (p->msg == ~(UINT)0) KillTimer(p->hwnd,p->wParam);
535 else SetTimer(p->hwnd,p->wParam,p->msg,(TIMERPROC)p->lParam);
539 SendMessage(p->hwnd,p->msg,p->wParam,p->lParam);
542 m_pmq_mutex->Enter();
543 // move this message to empty list
547 // get next queued message (if within limits)
548 p = (--max_amt > 0) ? m_pmq : NULL;
552 if (m_pmq_tail == p) m_pmq_tail=NULL;
555 m_pmq_mutex->Leave();
560 void SWELL_Internal_PMQ_ClearAllMessages(HWND hwnd)
562 if (!m_pmq_mutex) return;
564 m_pmq_mutex->Enter();
566 PMQ_rec *lastrec=NULL;
569 if (hwnd && p->hwnd != hwnd) { lastrec=p; p=p->next; }
572 PMQ_rec *next=p->next;
574 p->next=m_pmq_empty; // add p to empty list
579 if (p==m_pmq_tail) m_pmq_tail=lastrec; // update tail
581 if (lastrec) p = lastrec->next = next;
582 else p = m_pmq = next;
585 m_pmq_mutex->Leave();
588 static void SWELL_pmq_settimer(HWND h, UINT_PTR timerid, UINT rate, TIMERPROC tProc)
590 if (!h||!m_pmq_mutex) return;
591 WDL_MutexLock lock(m_pmq_mutex);
596 if (rec->is_special_timer && rec->hwnd == h && rec->wParam == timerid)
598 rec->msg = rate; // adjust to new rate
599 rec->lParam = (LPARAM)tProc;
606 if (rec) m_pmq_empty=rec->next;
607 else rec=(PMQ_rec*)malloc(sizeof(PMQ_rec));
612 rec->lParam=(LPARAM)tProc;
613 rec->is_special_timer=true;
615 if (m_pmq_tail) m_pmq_tail->next=rec;
619 while (p && p->next) p=p->next; // shouldnt happen unless m_pmq is NULL As well but why not for safety
627 BOOL SWELL_Internal_PostMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
629 if (!hwnd||!m_pmq_mutex) return FALSE;
630 if (![(id)hwnd respondsToSelector:@selector(swellCanPostMessage)]) return FALSE;
633 m_pmq_mutex->Enter();
635 if ((m_pmq_empty||m_pmq_size<MAX_POSTMESSAGE_SIZE) && [(id)hwnd swellCanPostMessage])
637 PMQ_rec *rec=m_pmq_empty;
638 if (rec) m_pmq_empty=rec->next;
639 else rec=(PMQ_rec*)malloc(sizeof(PMQ_rec));
645 rec->is_special_timer=false;
647 if (m_pmq_tail) m_pmq_tail->next=rec;
651 while (p && p->next) p=p->next; // shouldnt happen unless m_pmq is NULL As well but why not for safety
661 m_pmq_mutex->Leave();
667 static bool s_rightclickemulate=true;
669 bool IsRightClickEmulateEnabled()
671 return s_rightclickemulate;
674 void SWELL_EnableRightClickEmulate(BOOL enable)
676 s_rightclickemulate=enable;
679 int g_swell_terminating;
680 void SWELL_PostQuitMessage(void *sender)
682 g_swell_terminating=true;
684 [NSApp terminate:(id)sender];
688 #ifndef MAC_OS_X_VERSION_10_9
689 typedef uint64_t NSActivityOptions;
692 NSActivityIdleDisplaySleepDisabled = (1ULL << 40),
693 NSActivityIdleSystemSleepDisabled = (1ULL << 20),
694 NSActivitySuddenTerminationDisabled = (1ULL << 14),
695 NSActivityAutomaticTerminationDisabled = (1ULL << 15),
696 NSActivityUserInitiated = (0x00FFFFFFULL | NSActivityIdleSystemSleepDisabled),
697 NSActivityUserInitiatedAllowingIdleSystemSleep = (NSActivityUserInitiated & ~NSActivityIdleSystemSleepDisabled),
698 NSActivityBackground = 0x000000FFULL,
699 NSActivityLatencyCritical = 0xFF00000000ULL,
703 @interface NSProcessInfo (reaperhostadditions)
704 - (id<NSObject>)beginActivityWithOptions:(NSActivityOptions)options reason:(NSString *)reason;
705 - (void)endActivity:(id<NSObject>)activity;
711 void SWELL_DisableAppNap(int disable)
713 if (!g_swell_terminating && floor(NSFoundationVersionNumber) > 945.00) // 10.9+
726 const NSActivityOptions v = NSActivityLatencyCritical | NSActivityIdleSystemSleepDisabled;
728 // beginActivityWithOptions returns an autoreleased object
729 obj = [[NSProcessInfo processInfo] beginActivityWithOptions:v reason:@"SWELL_DisableAppNap"];
730 if (obj) [obj retain];
738 // in case we crash somehow, dont' want obj sticking around pointing to a stale object
740 [[NSProcessInfo processInfo] endActivity:a];
741 [a release]; // apparently releasing this is enough, without the endActivity call, but the docs are quite vague
745 @catch (NSException *exception) {
753 int SWELL_GetOSXVersion()
758 if (NSAppKitVersionNumber >= 1266.0)
760 if (NSAppKitVersionNumber >= 1404.0)
763 v = 0x10a0; // 10.10+ Gestalt(gsv) return 0x109x, so we bump this to 0x10a0
768 Gestalt(gestaltSystemVersion,&a);