891 lines
22 KiB
Plaintext
891 lines
22 KiB
Plaintext
/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX)
|
|
Copyright (C) 2006 and later, Cockos, Inc.
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
*/
|
|
|
|
#ifndef SWELL_PROVIDED_BY_APP
|
|
|
|
//#import <Carbon/Carbon.h>
|
|
#import <Cocoa/Cocoa.h>
|
|
#include <IOKit/graphics/IOGraphicsLib.h>
|
|
#include <sys/poll.h>
|
|
#include "swell.h"
|
|
#define SWELL_IMPLEMENT_GETOSXVERSION
|
|
#include "swell-internal.h"
|
|
|
|
#include "../mutex.h"
|
|
|
|
HWND g_swell_only_timerhwnd;
|
|
|
|
@implementation SWELL_TimerFuncTarget
|
|
|
|
-(id) initWithId:(UINT_PTR)tid hwnd:(HWND)h callback:(TIMERPROC)cb
|
|
{
|
|
if ((self = [super init]))
|
|
{
|
|
m_hwnd=h;
|
|
m_cb=cb;
|
|
m_timerid = tid;
|
|
}
|
|
return self;
|
|
}
|
|
-(void)SWELL_Timer:(id)sender
|
|
{
|
|
if (g_swell_only_timerhwnd && m_hwnd != g_swell_only_timerhwnd) return;
|
|
|
|
m_cb(m_hwnd,WM_TIMER,m_timerid,GetTickCount());
|
|
}
|
|
@end
|
|
|
|
@implementation SWELL_DataHold
|
|
-(id) initWithVal:(void *)val
|
|
{
|
|
if ((self = [super init]))
|
|
{
|
|
m_data=val;
|
|
}
|
|
return self;
|
|
}
|
|
-(void *) getValue
|
|
{
|
|
return m_data;
|
|
}
|
|
@end
|
|
|
|
void SWELL_CFStringToCString(const void *str, char *buf, int buflen)
|
|
{
|
|
NSString *s = (NSString *)str;
|
|
if (buflen>0) *buf=0;
|
|
if (buflen <= 1 || !s || [s getCString:buf maxLength:buflen encoding:NSUTF8StringEncoding]) return; // should always work, I'd hope (ambiguous documentation ugh)
|
|
|
|
NSData *data = [s dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
|
|
if (!data)
|
|
{
|
|
[s getCString:buf maxLength:buflen encoding:NSASCIIStringEncoding];
|
|
return;
|
|
}
|
|
int len = (int)[data length];
|
|
if (len > buflen-1) len=buflen-1;
|
|
[data getBytes:buf length:len];
|
|
buf[len]=0;
|
|
}
|
|
|
|
void *SWELL_CStringToCFString(const char *str)
|
|
{
|
|
if (!str) str="";
|
|
void *ret;
|
|
|
|
ret=(void *)CFStringCreateWithCString(NULL,str,kCFStringEncodingUTF8);
|
|
if (ret) return ret;
|
|
ret=(void*)CFStringCreateWithCString(NULL,str,kCFStringEncodingASCII);
|
|
return ret;
|
|
}
|
|
|
|
void SWELL_MakeProcessFront(HANDLE h)
|
|
{
|
|
SWELL_InternalObjectHeader_NSTask *buf = (SWELL_InternalObjectHeader_NSTask*)h;
|
|
if (buf && buf->hdr.type == INTERNAL_OBJECT_NSTASK && buf->task)
|
|
{
|
|
ProcessSerialNumber psn;
|
|
|
|
int pid=[(id)buf->task processIdentifier];
|
|
// try for 1sec to get the PSN, if it fails
|
|
int n = 20;
|
|
while (GetProcessForPID(pid,&psn) != noErr && n-- > 0)
|
|
{
|
|
Sleep(50);
|
|
}
|
|
if (n>0) SetFrontProcess(&psn);
|
|
}
|
|
}
|
|
|
|
void SWELL_ReleaseNSTask(void *p)
|
|
{
|
|
NSTask *a =(NSTask*)p;
|
|
if (a)
|
|
{
|
|
if (![a isRunning]) [a waitUntilExit]; // workaround for the system keeping a reference held on macOS 12+ at least
|
|
[a release];
|
|
}
|
|
}
|
|
DWORD SWELL_WaitForNSTask(void *p, DWORD msTO)
|
|
{
|
|
NSTask *a =(NSTask*)p;
|
|
const DWORD t = GetTickCount();
|
|
do
|
|
{
|
|
if (![a isRunning]) return WAIT_OBJECT_0;
|
|
if (msTO) Sleep(1);
|
|
}
|
|
while (msTO && (GetTickCount()-t) < msTO);
|
|
|
|
return [a isRunning] ? WAIT_TIMEOUT : WAIT_OBJECT_0;
|
|
}
|
|
|
|
HANDLE SWELL_CreateProcessIO(const char *exe, int nparams, const char **params, bool redirectIO)
|
|
{
|
|
NSString *ex = (NSString *)SWELL_CStringToCFString(exe);
|
|
NSMutableArray *ar = [[NSMutableArray alloc] initWithCapacity:nparams];
|
|
|
|
int x;
|
|
for (x=0;x <nparams;x++)
|
|
{
|
|
NSString *s = (NSString *)SWELL_CStringToCFString(params[x]?params[x]:"");
|
|
[ar addObject:s];
|
|
[s release];
|
|
}
|
|
|
|
NSTask *tsk = NULL;
|
|
|
|
@try {
|
|
tsk = [[NSTask alloc] init];
|
|
[tsk setLaunchPath:ex];
|
|
[tsk setArguments:ar];
|
|
if (redirectIO)
|
|
{
|
|
[tsk setStandardInput:[NSPipe pipe]];
|
|
[tsk setStandardOutput:[NSPipe pipe]];
|
|
[tsk setStandardError:[NSPipe pipe]];
|
|
}
|
|
[tsk launch];
|
|
}
|
|
@catch (NSException *exception) {
|
|
[tsk release];
|
|
tsk=0;
|
|
}
|
|
@catch (id ex) {
|
|
}
|
|
|
|
[ex release];
|
|
[ar release];
|
|
if (!tsk) return NULL;
|
|
|
|
SWELL_InternalObjectHeader_NSTask *buf = (SWELL_InternalObjectHeader_NSTask*)malloc(sizeof(SWELL_InternalObjectHeader_NSTask));
|
|
buf->hdr.type = INTERNAL_OBJECT_NSTASK;
|
|
buf->hdr.count=1;
|
|
buf->task = tsk;
|
|
return buf;
|
|
}
|
|
|
|
HANDLE SWELL_CreateProcess(const char *exe, int nparams, const char **params)
|
|
{
|
|
return SWELL_CreateProcessIO(exe,nparams,params,false);
|
|
}
|
|
|
|
|
|
int SWELL_GetProcessExitCode(HANDLE hand)
|
|
{
|
|
int rv=0;
|
|
SWELL_InternalObjectHeader_NSTask *hdr=(SWELL_InternalObjectHeader_NSTask*)hand;
|
|
if (!hdr || hdr->hdr.type != INTERNAL_OBJECT_NSTASK || !hdr->task) return -1;
|
|
@try {
|
|
if ([(NSTask *)hdr->task isRunning]) rv=-3;
|
|
else rv = [(NSTask *)hdr->task terminationStatus];
|
|
}
|
|
@catch (id ex) {
|
|
rv=-2;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
int SWELL_TerminateProcess(HANDLE hand)
|
|
{
|
|
int rv=0;
|
|
SWELL_InternalObjectHeader_NSTask *hdr=(SWELL_InternalObjectHeader_NSTask*)hand;
|
|
if (!hdr || hdr->hdr.type != INTERNAL_OBJECT_NSTASK || !hdr->task) return -1;
|
|
@try
|
|
{
|
|
[(NSTask *)hdr->task terminate];
|
|
}
|
|
@catch (id ex) {
|
|
rv=-2;
|
|
}
|
|
return rv;
|
|
}
|
|
int SWELL_ReadWriteProcessIO(HANDLE hand, int w/*stdin,stdout,stderr*/, char *buf, int bufsz)
|
|
{
|
|
SWELL_InternalObjectHeader_NSTask *hdr=(SWELL_InternalObjectHeader_NSTask*)hand;
|
|
if (!hdr || hdr->hdr.type != INTERNAL_OBJECT_NSTASK || !hdr->task) return 0;
|
|
NSTask *tsk = (NSTask*)hdr->task;
|
|
NSPipe *pipe = NULL;
|
|
bool async_mode = false;
|
|
if (w & (1<<24))
|
|
{
|
|
async_mode = true;
|
|
w &= ~ (1<<24);
|
|
}
|
|
switch (w)
|
|
{
|
|
case 0: pipe = [tsk standardInput]; break;
|
|
case 1: pipe = [tsk standardOutput]; break;
|
|
case 2: pipe = [tsk standardError]; break;
|
|
}
|
|
if (!pipe || ![pipe isKindOfClass:[NSPipe class]]) return 0;
|
|
|
|
NSFileHandle *fh = w!=0 ? [pipe fileHandleForReading] : [pipe fileHandleForWriting];
|
|
if (!fh) return 0;
|
|
if (w==0)
|
|
{
|
|
if (bufsz>0)
|
|
{
|
|
NSData *d = [NSData dataWithBytes:buf length:bufsz];
|
|
@try
|
|
{
|
|
if (d) [fh writeData:d];
|
|
else bufsz=0;
|
|
}
|
|
@catch (id ex) { bufsz=0; }
|
|
|
|
return bufsz;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (async_mode)
|
|
{
|
|
int handle = [fh fileDescriptor];
|
|
if (handle >= 0)
|
|
{
|
|
struct pollfd pl = { handle, POLLIN };
|
|
if (poll(&pl,1,0)<1) return 0;
|
|
|
|
return (int)read(handle,buf,bufsz);
|
|
}
|
|
}
|
|
NSData *d = NULL;
|
|
@try
|
|
{
|
|
d = [fh readDataOfLength:(bufsz < 1 ? 32768 : bufsz)];
|
|
}
|
|
@catch (id ex) { }
|
|
|
|
if (!d || bufsz < 1) return d ? (int)[d length] : 0;
|
|
int l = (int)[d length];
|
|
if (l > bufsz) l = bufsz;
|
|
[d getBytes:buf length:l];
|
|
return l;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
@implementation SWELL_ThreadTmp
|
|
-(void)bla:(id)obj
|
|
{
|
|
if (a)
|
|
{
|
|
DWORD (*func)(void *);
|
|
*(void **)(&func) = a;
|
|
func(b);
|
|
}
|
|
[NSThread exit];
|
|
}
|
|
@end
|
|
|
|
void SWELL_EnsureMultithreadedCocoa()
|
|
{
|
|
static int a;
|
|
if (!a)
|
|
{
|
|
a++;
|
|
if (![NSThread isMultiThreaded]) // force cocoa into multithreaded mode
|
|
{
|
|
SWELL_ThreadTmp *t=[[SWELL_ThreadTmp alloc] init];
|
|
t->a=0;
|
|
t->b=0;
|
|
[NSThread detachNewThreadSelector:@selector(bla:) toTarget:t withObject:t];
|
|
/// [t release];
|
|
}
|
|
}
|
|
}
|
|
|
|
void CreateThreadNS(void *TA, DWORD stackSize, DWORD (*ThreadProc)(LPVOID), LPVOID parm, DWORD cf, DWORD *tidOut)
|
|
{
|
|
SWELL_ThreadTmp *t=[[SWELL_ThreadTmp alloc] init];
|
|
t->a=(void*)ThreadProc;
|
|
t->b=parm;
|
|
return [NSThread detachNewThreadSelector:@selector(bla:) toTarget:t withObject:t];
|
|
}
|
|
|
|
|
|
// used by swell.cpp (lazy these should go elsewhere)
|
|
void *SWELL_InitAutoRelease()
|
|
{
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
return (void *)pool;
|
|
}
|
|
void SWELL_QuitAutoRelease(void *p)
|
|
{
|
|
if (p)
|
|
[(NSAutoreleasePool*)p release];
|
|
}
|
|
|
|
void SWELL_RunEvents()
|
|
{
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
int x=100;
|
|
while (x-- > 0)
|
|
{
|
|
NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate dateWithTimeIntervalSinceNow:0.001] inMode:NSDefaultRunLoopMode dequeue:YES];
|
|
if (!event) break;
|
|
[NSApp sendEvent:event];
|
|
}
|
|
[pool release];
|
|
}
|
|
|
|
// timer stuff
|
|
typedef struct TimerInfoRec
|
|
{
|
|
UINT_PTR timerid;
|
|
HWND hwnd;
|
|
NSTimer *timer;
|
|
struct TimerInfoRec *_next;
|
|
} TimerInfoRec;
|
|
static TimerInfoRec *m_timer_list;
|
|
static WDL_Mutex m_timermutex;
|
|
#ifndef SWELL_NO_POSTMESSAGE
|
|
static pthread_t m_pmq_mainthread;
|
|
static void SWELL_pmq_settimer(HWND h, UINT_PTR timerid, UINT rate, TIMERPROC tProc);
|
|
#endif
|
|
|
|
UINT_PTR SetTimer(HWND hwnd, UINT_PTR timerid, UINT rate, TIMERPROC tProc)
|
|
{
|
|
if (!hwnd && !tProc) return 0; // must have either callback or hwnd
|
|
|
|
if (hwnd && !timerid) return 0;
|
|
|
|
#ifndef SWELL_NO_POSTMESSAGE
|
|
if (timerid != ~(UINT_PTR)0 && m_pmq_mainthread && pthread_self()!=m_pmq_mainthread)
|
|
{
|
|
SWELL_pmq_settimer(hwnd,timerid,(rate==(UINT)-1)?((UINT)-2):rate,tProc);
|
|
return timerid;
|
|
}
|
|
#endif
|
|
|
|
|
|
if (hwnd && ![(id)hwnd respondsToSelector:@selector(SWELL_Timer:)])
|
|
{
|
|
if (![(id)hwnd isKindOfClass:[NSWindow class]]) return 0;
|
|
hwnd=(HWND)[(id)hwnd contentView];
|
|
if (![(id)hwnd respondsToSelector:@selector(SWELL_Timer:)]) return 0;
|
|
}
|
|
|
|
WDL_MutexLock lock(&m_timermutex);
|
|
TimerInfoRec *rec=NULL;
|
|
if (hwnd||timerid)
|
|
{
|
|
rec = m_timer_list;
|
|
while (rec)
|
|
{
|
|
if (rec->timerid == timerid && rec->hwnd == hwnd) // works for both kinds
|
|
break;
|
|
rec=rec->_next;
|
|
}
|
|
}
|
|
|
|
bool recAdd=false;
|
|
if (!rec)
|
|
{
|
|
rec=(TimerInfoRec*)malloc(sizeof(TimerInfoRec));
|
|
recAdd=true;
|
|
}
|
|
else
|
|
{
|
|
[rec->timer invalidate];
|
|
rec->timer=0;
|
|
}
|
|
|
|
rec->timerid=timerid;
|
|
rec->hwnd=hwnd;
|
|
|
|
if (!hwnd || tProc)
|
|
{
|
|
// set timer to this unique ptr
|
|
if (!hwnd) timerid = rec->timerid = (UINT_PTR)rec;
|
|
|
|
SWELL_TimerFuncTarget *t = [[SWELL_TimerFuncTarget alloc] initWithId:timerid hwnd:hwnd callback:tProc];
|
|
rec->timer = [NSTimer scheduledTimerWithTimeInterval:(wdl_max(rate,1)*0.001) target:t selector:@selector(SWELL_Timer:)
|
|
userInfo:t repeats:YES];
|
|
[t release];
|
|
|
|
}
|
|
else
|
|
{
|
|
SWELL_DataHold *t=[[SWELL_DataHold alloc] initWithVal:(void *)timerid];
|
|
rec->timer = [NSTimer scheduledTimerWithTimeInterval:(wdl_max(rate,1)*0.001) target:(id)hwnd selector:@selector(SWELL_Timer:)
|
|
userInfo:t repeats:YES];
|
|
|
|
[t release];
|
|
}
|
|
[[NSRunLoop currentRunLoop] addTimer:rec->timer forMode:(NSString*)kCFRunLoopCommonModes];
|
|
|
|
if (recAdd)
|
|
{
|
|
rec->_next=m_timer_list;
|
|
m_timer_list=rec;
|
|
}
|
|
|
|
return timerid;
|
|
}
|
|
void SWELL_RunRunLoop(int ms)
|
|
{
|
|
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:ms*0.001]];
|
|
}
|
|
void SWELL_RunRunLoopEx(int ms, HWND hwndOnlyTimer)
|
|
{
|
|
HWND h=g_swell_only_timerhwnd;
|
|
g_swell_only_timerhwnd = hwndOnlyTimer;
|
|
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:ms*0.001]];
|
|
if (g_swell_only_timerhwnd == hwndOnlyTimer) g_swell_only_timerhwnd = h;
|
|
}
|
|
|
|
BOOL KillTimer(HWND hwnd, UINT_PTR timerid)
|
|
{
|
|
if (!hwnd && !timerid) return FALSE;
|
|
|
|
WDL_MutexLock lock(&m_timermutex);
|
|
#ifndef SWELL_NO_POSTMESSAGE
|
|
if (timerid != ~(UINT_PTR)0 && m_pmq_mainthread && pthread_self()!=m_pmq_mainthread)
|
|
{
|
|
SWELL_pmq_settimer(hwnd,timerid,~(UINT)0,NULL);
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
BOOL rv=FALSE;
|
|
|
|
// don't allow removing all global timers
|
|
if (timerid!=~(UINT_PTR)0 || hwnd)
|
|
{
|
|
TimerInfoRec *rec = m_timer_list, *lrec=NULL;
|
|
while (rec)
|
|
{
|
|
|
|
if (rec->hwnd == hwnd && (timerid==~(UINT_PTR)0 || rec->timerid == timerid))
|
|
{
|
|
TimerInfoRec *nrec = rec->_next;
|
|
|
|
// remove self from list
|
|
if (lrec) lrec->_next = nrec;
|
|
else m_timer_list = nrec;
|
|
|
|
[rec->timer invalidate];
|
|
free(rec);
|
|
|
|
rv=TRUE;
|
|
if (timerid!=~(UINT_PTR)0) break;
|
|
|
|
rec=nrec;
|
|
}
|
|
else
|
|
{
|
|
lrec=rec;
|
|
rec=rec->_next;
|
|
}
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
#ifndef SWELL_NO_POSTMESSAGE
|
|
|
|
///////// PostMessage emulation
|
|
|
|
// implementation of postmessage stuff
|
|
|
|
|
|
|
|
typedef struct PMQ_rec
|
|
{
|
|
HWND hwnd;
|
|
UINT msg;
|
|
WPARAM wParam;
|
|
LPARAM lParam;
|
|
|
|
struct PMQ_rec *next;
|
|
bool is_special_timer; // if set, then msg=interval(-1 for kill),wParam=timer id, lParam = timerproc
|
|
} PMQ_rec;
|
|
|
|
static WDL_Mutex *m_pmq_mutex;
|
|
static PMQ_rec *m_pmq, *m_pmq_empty, *m_pmq_tail;
|
|
static int m_pmq_size;
|
|
static id m_pmq_timer;
|
|
#define MAX_POSTMESSAGE_SIZE 1024
|
|
|
|
void SWELL_Internal_PostMessage_Init()
|
|
{
|
|
if (m_pmq_mutex) return;
|
|
id del = [NSApp delegate];
|
|
if (!del || ![del respondsToSelector:@selector(swellPostMessageTick:)]) return;
|
|
|
|
m_pmq_mainthread=pthread_self();
|
|
m_pmq_mutex = new WDL_Mutex;
|
|
|
|
m_pmq_timer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:(id)del selector:@selector(swellPostMessageTick:) userInfo:nil repeats:YES];
|
|
[[NSRunLoop currentRunLoop] addTimer:m_pmq_timer forMode:(NSString*)kCFRunLoopCommonModes];
|
|
// [ release];
|
|
// set a timer to the delegate
|
|
}
|
|
|
|
|
|
void SWELL_MessageQueue_Flush()
|
|
{
|
|
if (!m_pmq_mutex) return;
|
|
|
|
m_pmq_mutex->Enter();
|
|
int max_amt = m_pmq_size;
|
|
PMQ_rec *p=m_pmq;
|
|
if (p)
|
|
{
|
|
m_pmq = p->next;
|
|
if (m_pmq_tail == p) m_pmq_tail=NULL;
|
|
m_pmq_size--;
|
|
}
|
|
m_pmq_mutex->Leave();
|
|
|
|
// process out queue
|
|
while (p)
|
|
{
|
|
if (p->is_special_timer)
|
|
{
|
|
if (p->msg == ~(UINT)0) KillTimer(p->hwnd,p->wParam);
|
|
else SetTimer(p->hwnd,p->wParam,p->msg,(TIMERPROC)p->lParam);
|
|
}
|
|
else
|
|
{
|
|
if ([(id)p->hwnd respondsToSelector:@selector(swellCanPostMessage)] && [(id)p->hwnd swellCanPostMessage])
|
|
SendMessage(p->hwnd,p->msg,p->wParam,p->lParam);
|
|
}
|
|
|
|
m_pmq_mutex->Enter();
|
|
// move this message to empty list
|
|
p->next=m_pmq_empty;
|
|
m_pmq_empty = p;
|
|
|
|
// get next queued message (if within limits)
|
|
p = (--max_amt > 0) ? m_pmq : NULL;
|
|
if (p)
|
|
{
|
|
m_pmq = p->next;
|
|
if (m_pmq_tail == p) m_pmq_tail=NULL;
|
|
m_pmq_size--;
|
|
}
|
|
m_pmq_mutex->Leave();
|
|
}
|
|
}
|
|
|
|
void SWELL_Internal_PMQ_ClearAllMessages(HWND hwnd)
|
|
{
|
|
if (!m_pmq_mutex) return;
|
|
|
|
m_pmq_mutex->Enter();
|
|
PMQ_rec *p=m_pmq;
|
|
PMQ_rec *lastrec=NULL;
|
|
while (p)
|
|
{
|
|
if (hwnd && p->hwnd != hwnd) { lastrec=p; p=p->next; }
|
|
else
|
|
{
|
|
PMQ_rec *next=p->next;
|
|
|
|
p->next=m_pmq_empty; // add p to empty list
|
|
m_pmq_empty=p;
|
|
m_pmq_size--;
|
|
|
|
|
|
if (p==m_pmq_tail) m_pmq_tail=lastrec; // update tail
|
|
|
|
if (lastrec) p = lastrec->next = next;
|
|
else p = m_pmq = next;
|
|
}
|
|
}
|
|
m_pmq_mutex->Leave();
|
|
}
|
|
|
|
static void SWELL_pmq_settimer(HWND h, UINT_PTR timerid, UINT rate, TIMERPROC tProc)
|
|
{
|
|
if (!h||!m_pmq_mutex) return;
|
|
WDL_MutexLock lock(m_pmq_mutex);
|
|
|
|
PMQ_rec *rec=m_pmq;
|
|
while (rec)
|
|
{
|
|
if (rec->is_special_timer && rec->hwnd == h && rec->wParam == timerid)
|
|
{
|
|
rec->msg = rate; // adjust to new rate
|
|
rec->lParam = (LPARAM)tProc;
|
|
return;
|
|
}
|
|
rec=rec->next;
|
|
}
|
|
|
|
rec=m_pmq_empty;
|
|
if (rec) m_pmq_empty=rec->next;
|
|
else rec=(PMQ_rec*)malloc(sizeof(PMQ_rec));
|
|
rec->next=0;
|
|
rec->hwnd=h;
|
|
rec->msg=rate;
|
|
rec->wParam=timerid;
|
|
rec->lParam=(LPARAM)tProc;
|
|
rec->is_special_timer=true;
|
|
|
|
if (m_pmq_tail) m_pmq_tail->next=rec;
|
|
else
|
|
{
|
|
PMQ_rec *p=m_pmq;
|
|
while (p && p->next) p=p->next; // shouldnt happen unless m_pmq is NULL As well but why not for safety
|
|
if (p) p->next=rec;
|
|
else m_pmq=rec;
|
|
}
|
|
m_pmq_tail=rec;
|
|
m_pmq_size++;
|
|
}
|
|
|
|
BOOL PostMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
if (WDL_NORMALLY(m_pmq_mutex != NULL))
|
|
{
|
|
return SWELL_Internal_PostMessage(hwnd,message,wParam,lParam);
|
|
}
|
|
|
|
// legacy passthrough to delegate if caller is using its own swell impl, not threadsafe
|
|
id del=[NSApp delegate];
|
|
if (del && [del respondsToSelector:@selector(swellPostMessage:msg:wp:lp:)])
|
|
return !![(SWELL_DelegateExtensions*)del swellPostMessage:hwnd msg:message wp:wParam lp:lParam];
|
|
return FALSE;
|
|
}
|
|
|
|
void SWELL_MessageQueue_Clear(HWND h)
|
|
{
|
|
if (WDL_NORMALLY(m_pmq_mutex != NULL))
|
|
{
|
|
SWELL_Internal_PMQ_ClearAllMessages(h);
|
|
}
|
|
else
|
|
{
|
|
id del=[NSApp delegate];
|
|
if (del && [del respondsToSelector:@selector(swellPostMessageClearQ:)])
|
|
[(SWELL_DelegateExtensions*)del swellPostMessageClearQ:h];
|
|
}
|
|
}
|
|
|
|
BOOL SWELL_Internal_PostMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
if (!hwnd||!m_pmq_mutex) return FALSE;
|
|
|
|
BOOL ret=FALSE;
|
|
m_pmq_mutex->Enter();
|
|
|
|
if (m_pmq_empty||m_pmq_size<MAX_POSTMESSAGE_SIZE)
|
|
{
|
|
PMQ_rec *rec=m_pmq_empty;
|
|
if (rec) m_pmq_empty=rec->next;
|
|
else rec=(PMQ_rec*)malloc(sizeof(PMQ_rec));
|
|
rec->next=0;
|
|
rec->hwnd=hwnd;
|
|
rec->msg=msg;
|
|
rec->wParam=wParam;
|
|
rec->lParam=lParam;
|
|
rec->is_special_timer=false;
|
|
|
|
if (m_pmq_tail) m_pmq_tail->next=rec;
|
|
else
|
|
{
|
|
PMQ_rec *p=m_pmq;
|
|
while (p && p->next) p=p->next; // shouldnt happen unless m_pmq is NULL As well but why not for safety
|
|
if (p) p->next=rec;
|
|
else m_pmq=rec;
|
|
}
|
|
m_pmq_tail=rec;
|
|
m_pmq_size++;
|
|
|
|
ret=TRUE;
|
|
}
|
|
|
|
m_pmq_mutex->Leave();
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static bool s_rightclickemulate=true;
|
|
|
|
bool IsRightClickEmulateEnabled()
|
|
{
|
|
return s_rightclickemulate;
|
|
}
|
|
|
|
void SWELL_EnableRightClickEmulate(BOOL enable)
|
|
{
|
|
s_rightclickemulate=enable;
|
|
}
|
|
|
|
int g_swell_terminating;
|
|
void SWELL_PostQuitMessage(void *sender)
|
|
{
|
|
g_swell_terminating=true;
|
|
|
|
[NSApp terminate:(id)sender];
|
|
}
|
|
|
|
|
|
#ifndef MAC_OS_X_VERSION_10_9
|
|
typedef uint64_t NSActivityOptions;
|
|
enum
|
|
{
|
|
NSActivityIdleDisplaySleepDisabled = (1ULL << 40),
|
|
NSActivityIdleSystemSleepDisabled = (1ULL << 20),
|
|
NSActivitySuddenTerminationDisabled = (1ULL << 14),
|
|
NSActivityAutomaticTerminationDisabled = (1ULL << 15),
|
|
NSActivityUserInitiated = (0x00FFFFFFULL | NSActivityIdleSystemSleepDisabled),
|
|
NSActivityUserInitiatedAllowingIdleSystemSleep = (NSActivityUserInitiated & ~NSActivityIdleSystemSleepDisabled),
|
|
NSActivityBackground = 0x000000FFULL,
|
|
NSActivityLatencyCritical = 0xFF00000000ULL,
|
|
};
|
|
|
|
|
|
@interface NSProcessInfo (reaperhostadditions)
|
|
- (id<NSObject>)beginActivityWithOptions:(NSActivityOptions)options reason:(NSString *)reason;
|
|
- (void)endActivity:(id<NSObject>)activity;
|
|
|
|
@end
|
|
|
|
#endif
|
|
|
|
void SWELL_DisableAppNap(int disable)
|
|
{
|
|
if (!g_swell_terminating && floor(NSFoundationVersionNumber) > 945.00) // 10.9+
|
|
{
|
|
static int cnt;
|
|
static id obj;
|
|
|
|
cnt += disable;
|
|
|
|
@try
|
|
{
|
|
if (cnt > 0)
|
|
{
|
|
if (!obj)
|
|
{
|
|
const NSActivityOptions v = NSActivityLatencyCritical | NSActivityIdleSystemSleepDisabled;
|
|
|
|
// beginActivityWithOptions returns an autoreleased object
|
|
obj = [[NSProcessInfo processInfo] beginActivityWithOptions:v reason:@"SWELL_DisableAppNap"];
|
|
if (obj) [obj retain];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
id a = obj;
|
|
if (a)
|
|
{
|
|
// in case we crash somehow, dont' want obj sticking around pointing to a stale object
|
|
obj = NULL;
|
|
[[NSProcessInfo processInfo] endActivity:a];
|
|
[a release]; // apparently releasing this is enough, without the endActivity call, but the docs are quite vague
|
|
}
|
|
}
|
|
}
|
|
@catch (NSException *exception) {
|
|
}
|
|
@catch (id ex) {
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOL EnumDisplayMonitors(HDC hdc, const LPRECT r, MONITORENUMPROC proc,LPARAM lParam)
|
|
{
|
|
// ignores hdc
|
|
NSArray *screens = [NSScreen screens];
|
|
const unsigned int ns = (unsigned int) [screens count];
|
|
for (unsigned int x = 0; x < ns; x ++)
|
|
{
|
|
NSScreen *mon = [screens objectAtIndex:x];
|
|
if (mon)
|
|
{
|
|
NSRect tr=[mon frame];
|
|
RECT screen_rect,tmp;
|
|
NSRECT_TO_RECT(&tmp,tr);
|
|
if (r)
|
|
{
|
|
if (!IntersectRect(&screen_rect,r,&tmp))
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
screen_rect = tmp;
|
|
}
|
|
if (!proc((HMONITOR)mon,hdc,&screen_rect,lParam)) break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GetMonitorInfo(HMONITOR hmon, void *inf)
|
|
{
|
|
if (!hmon) return FALSE;
|
|
MONITORINFOEX *a = (MONITORINFOEX*)inf;
|
|
if (a->cbSize < sizeof(MONITORINFO)) return FALSE;
|
|
|
|
NSScreen *mon = (NSScreen *)hmon;
|
|
NSRect tr=[mon frame];
|
|
NSRECT_TO_RECT(&a->rcMonitor,tr);
|
|
tr = [mon visibleFrame];
|
|
NSRECT_TO_RECT(&a->rcWork,tr);
|
|
a->dwFlags = 0;
|
|
|
|
if (a->cbSize > sizeof(MONITORINFO))
|
|
{
|
|
const int maxlen = (int) (a->cbSize - sizeof(MONITORINFO));
|
|
const int displayID = [[[mon deviceDescription] valueForKey:@"NSScreenNumber"] intValue];
|
|
snprintf(a->szDevice,maxlen,"DisplayID %d",displayID);
|
|
|
|
|
|
static bool init;
|
|
static CFDictionaryRef (*_IODisplayCreateInfoDictionary)(io_service_t framebuffer, IOOptionBits options);
|
|
|
|
if (!init)
|
|
{
|
|
init = true;
|
|
void *lib = dlopen("/System/Library/Frameworks/IOKit.framework/Versions/Current/IOKit",RTLD_LAZY);
|
|
if (lib)
|
|
*(void **)&_IODisplayCreateInfoDictionary = dlsym(lib,"IODisplayCreateInfoDictionary");
|
|
}
|
|
|
|
if (_IODisplayCreateInfoDictionary)
|
|
{
|
|
NSDictionary *deviceInfo = (NSDictionary *)_IODisplayCreateInfoDictionary(CGDisplayIOServicePort(displayID), kIODisplayOnlyPreferredName);
|
|
NSDictionary *names = [deviceInfo objectForKey:@"DisplayProductName"];
|
|
if ([names count] > 0)
|
|
{
|
|
NSString *s = [names objectForKey:[[names allKeys] objectAtIndex:0]];
|
|
if (s) SWELL_CFStringToCString(s,a->szDevice,maxlen);
|
|
}
|
|
[deviceInfo release];
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#endif
|