Files
tlib/oversampling/WDL/swell/swell.cpp
2024-05-24 13:28:31 +02:00

1208 lines
31 KiB
C++

/* 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.
This file implements a few Windows calls using their posix equivilents
*/
#ifndef SWELL_PROVIDED_BY_APP
#include "swell.h"
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <sys/fcntl.h>
#include <sys/resource.h>
#include "swell-internal.h"
#ifdef SWELL_TARGET_OSX
#include <Carbon/Carbon.h>
#endif
#ifdef __APPLE__
#include <sched.h>
#include <sys/errno.h>
#else
#include <sys/wait.h>
#endif
#ifdef __linux__
#include <linux/sched.h>
#endif
#include <pthread.h>
#include "../wdlatomic.h"
#include "../mutex.h"
#include "../assocarray.h"
#include "../wdlcstring.h"
void (*SWELL_DDrop_onDragLeave)();
void (*SWELL_DDrop_onDragOver)(POINT pt);
void (*SWELL_DDrop_onDragEnter)(void *hGlobal, POINT pt);
const char* (*SWELL_DDrop_getDroppedFileTargetPath)(const char* extension);
void Sleep(int ms)
{
usleep(ms?ms*1000:100);
}
DWORD GetTickCount()
{
#ifdef __APPLE__
// could switch to mach_getabsolutetime() maybe
struct timeval tm={0,};
gettimeofday(&tm,NULL);
return (DWORD) (tm.tv_sec*1000 + tm.tv_usec/1000);
#else
struct timespec ts={0,};
clock_gettime(CLOCK_MONOTONIC,&ts);
return (DWORD) (ts.tv_sec*1000 + ts.tv_nsec/1000000);
#endif
}
static void intToFileTime(time_t t, FILETIME *out)
{
// see WDL_DirScan::GetCurrentLastWriteTime and similar
unsigned long long a=(unsigned long long)t; // seconds since january 1st, 1970
a += 11644473600ull; // 1601-1970
a *= 10000000; // seconds to 1/10th microseconds (100 nanoseconds)
out->dwLowDateTime=a & 0xffffffff;
out->dwHighDateTime=a>>32;
}
BOOL GetFileTime(int filedes, FILETIME *lpCreationTime, FILETIME *lpLastAccessTime, FILETIME *lpLastWriteTime)
{
if (WDL_NOT_NORMALLY(filedes<0)) return 0;
struct stat st;
if (fstat(filedes,&st)) return 0;
if (lpCreationTime) intToFileTime(st.st_ctime,lpCreationTime);
if (lpLastAccessTime) intToFileTime(st.st_atime,lpLastAccessTime);
if (lpLastWriteTime) intToFileTime(st.st_mtime,lpLastWriteTime);
return 1;
}
BOOL SWELL_PtInRect(const RECT *r, POINT p)
{
if (!r) return FALSE;
int tp=r->top;
int bt=r->bottom;
if (tp>bt)
{
bt=tp;
tp=r->bottom;
}
return p.x>=r->left && p.x<r->right && p.y >= tp && p.y < bt;
}
int MulDiv(int a, int b, int c)
{
if(c == 0) return 0;
return (int)((double)a*(double)b/c);
}
unsigned int _controlfp(unsigned int flag, unsigned int mask)
{
#if !defined(__ppc__) && !defined(__LP64__) && !defined(__arm__)
unsigned short ret;
mask &= _MCW_RC; // don't let the caller set anything other than round control for now
__asm__ __volatile__("fnstcw %0\n\t":"=m"(ret));
ret=(ret&~(mask<<2))|(flag<<2);
if (mask) __asm__ __volatile__(
"fldcw %0\n\t"::"m"(ret));
return (unsigned int) (ret>>2);
#else
return 0;
#endif
}
#ifndef SWELL_TARGET_OSX
static WDL_PtrList<void> s_zombie_handles;
void swell_cleanupZombies()
{
int x = s_zombie_handles.GetSize();
while (--x>=0)
{
HANDLE h = s_zombie_handles.Get(x);
if (WaitForSingleObject(h,0) != WAIT_TIMEOUT)
s_zombie_handles.Delete(x,free);
}
}
#endif
BOOL CloseHandle(HANDLE hand)
{
SWELL_InternalObjectHeader *hdr=(SWELL_InternalObjectHeader*)hand;
if (WDL_NOT_NORMALLY(!hdr)) return FALSE;
if (hdr->type <= INTERNAL_OBJECT_START || hdr->type >= INTERNAL_OBJECT_END) return FALSE;
if (!wdl_atomic_decr(&hdr->count))
{
switch (hdr->type)
{
case INTERNAL_OBJECT_FILE:
{
SWELL_InternalObjectHeader_File *file = (SWELL_InternalObjectHeader_File*)hdr;
if (file->fp) fclose(file->fp);
}
break;
case INTERNAL_OBJECT_EXTERNALSOCKET: return FALSE; // pure sockets are not to be closed this way;
case INTERNAL_OBJECT_SOCKETEVENT:
{
SWELL_InternalObjectHeader_SocketEvent *se= (SWELL_InternalObjectHeader_SocketEvent *)hdr;
if (se->socket[0]>=0) close(se->socket[0]);
if (se->socket[1]>=0) close(se->socket[1]);
}
break;
case INTERNAL_OBJECT_EVENT:
{
SWELL_InternalObjectHeader_Event *evt=(SWELL_InternalObjectHeader_Event*)hdr;
pthread_cond_destroy(&evt->cond);
pthread_mutex_destroy(&evt->mutex);
}
break;
case INTERNAL_OBJECT_THREAD:
{
SWELL_InternalObjectHeader_Thread *thr = (SWELL_InternalObjectHeader_Thread*)hdr;
pthread_detach(thr->pt);
}
break;
#ifdef SWELL_TARGET_OSX
case INTERNAL_OBJECT_NSTASK:
{
SWELL_InternalObjectHeader_NSTask *nst = (SWELL_InternalObjectHeader_NSTask*)hdr;
extern void SWELL_ReleaseNSTask(void *);
if (nst->task) SWELL_ReleaseNSTask(nst->task);
}
break;
#else
case INTERNAL_OBJECT_PID:
swell_cleanupZombies();
if (WaitForSingleObject(hand,0)==WAIT_TIMEOUT)
{
s_zombie_handles.Add(hand);
return TRUE;
}
break;
#endif
}
free(hdr);
}
return TRUE;
}
HANDLE CreateEventAsSocket(void *SA, BOOL manualReset, BOOL initialSig, const char *ignored)
{
SWELL_InternalObjectHeader_SocketEvent *buf = (SWELL_InternalObjectHeader_SocketEvent*)malloc(sizeof(SWELL_InternalObjectHeader_SocketEvent));
buf->hdr.type=INTERNAL_OBJECT_SOCKETEVENT;
buf->hdr.count=1;
buf->autoReset = !manualReset;
buf->socket[0]=buf->socket[1]=-1;
if (socketpair(AF_UNIX,SOCK_STREAM,0,buf->socket)<0)
{
free(buf);
return 0;
}
fcntl(buf->socket[0], F_SETFL, fcntl(buf->socket[0],F_GETFL) | O_NONBLOCK); // nonblocking
char c=0;
if (initialSig&&buf->socket[1]>=0)
{
if (write(buf->socket[1],&c,1) != 1)
{
WDL_ASSERT( false /* write to socket failed in CreateEventAsSocket() */ );
}
}
return buf;
}
DWORD WaitForAnySocketObject(int numObjs, HANDLE *objs, DWORD msTO) // only supports special (socket) handles at the moment
{
struct pollfd list1[128];
WDL_TypedBuf<struct pollfd> list2;
struct pollfd *fds = numObjs > 128 ? list2.ResizeOK(numObjs) : list1;
if (WDL_NOT_NORMALLY(!fds)) { numObjs = 128; fds = list1; }
int x, nfds = 0;
for (x = 0; x < numObjs; x ++)
{
SWELL_InternalObjectHeader_SocketEvent *se = (SWELL_InternalObjectHeader_SocketEvent *)objs[x];
if (WDL_NORMALLY(se) &&
WDL_NORMALLY(se->hdr.type == INTERNAL_OBJECT_EXTERNALSOCKET || se->hdr.type == INTERNAL_OBJECT_SOCKETEVENT) &&
WDL_NORMALLY(se->socket[0]>=0))
{
fds[nfds].fd = se->socket[0];
fds[nfds].events = POLLIN;
fds[nfds].revents = 0;
nfds++;
}
}
if (nfds>0)
{
again:
const int res = poll(fds,nfds,msTO == INFINITE ? -1 : msTO);
int pos = 0;
if (res>0) for (x = 0; x < numObjs; x ++)
{
SWELL_InternalObjectHeader_SocketEvent *se = (SWELL_InternalObjectHeader_SocketEvent *)objs[x];
if (WDL_NORMALLY(se) &&
WDL_NORMALLY(se->hdr.type == INTERNAL_OBJECT_EXTERNALSOCKET || se->hdr.type == INTERNAL_OBJECT_SOCKETEVENT) &&
WDL_NORMALLY(se->socket[0]>=0))
{
if (fds[pos].revents & POLLIN)
{
if (se->hdr.type == INTERNAL_OBJECT_SOCKETEVENT && se->autoReset)
{
char buf[128];
if (read(se->socket[0],buf,sizeof(buf))<1) goto again;
}
return WAIT_OBJECT_0 + x;
}
pos++;
}
}
if (res < 0) return WAIT_FAILED;
}
return WAIT_TIMEOUT;
}
DWORD WaitForSingleObject(HANDLE hand, DWORD msTO)
{
SWELL_InternalObjectHeader *hdr=(SWELL_InternalObjectHeader*)hand;
if (WDL_NOT_NORMALLY(!hdr)) return WAIT_FAILED;
switch (hdr->type)
{
#ifdef SWELL_TARGET_OSX
case INTERNAL_OBJECT_NSTASK:
{
SWELL_InternalObjectHeader_NSTask *nst = (SWELL_InternalObjectHeader_NSTask*)hdr;
extern DWORD SWELL_WaitForNSTask(void *,DWORD);
if (nst->task) return SWELL_WaitForNSTask(nst->task,msTO);
}
break;
#else
case INTERNAL_OBJECT_PID:
{
SWELL_InternalObjectHeader_PID *pb = (SWELL_InternalObjectHeader_PID*)hdr;
if (pb->pid)
{
if (pb->done) return WAIT_OBJECT_0;
int wstatus=0;
if (msTO == INFINITE || msTO == 0)
{
pid_t v = waitpid(pb->pid,&wstatus,msTO == INFINITE ? 0 : WNOHANG);
if (v == 0) return WAIT_TIMEOUT;
if (v < 0) return WAIT_FAILED;
}
else
{
const DWORD start_t = GetTickCount();
for (;;)
{
pid_t v = waitpid(pb->pid,&wstatus,WNOHANG);
if (v > 0) break;
if (v < 0) return WAIT_FAILED;
if ((GetTickCount()-start_t) > msTO) return WAIT_TIMEOUT;
Sleep(1);
}
}
if (!pb->done)
{
pb->done=1;
pb->result = WEXITSTATUS(wstatus);
}
return WAIT_OBJECT_0;
}
}
break;
#endif
case INTERNAL_OBJECT_THREAD:
{
SWELL_InternalObjectHeader_Thread *thr = (SWELL_InternalObjectHeader_Thread*)hdr;
if (thr->done) return WAIT_OBJECT_0;
if (!msTO) return WAIT_TIMEOUT;
const DWORD d=GetTickCount();
for (;;)
{
Sleep(1);
if (thr->done) return WAIT_OBJECT_0;
if (msTO != INFINITE && (GetTickCount()-d)>=msTO) return WAIT_TIMEOUT;
}
}
break;
case INTERNAL_OBJECT_EXTERNALSOCKET:
case INTERNAL_OBJECT_SOCKETEVENT:
{
SWELL_InternalObjectHeader_SocketEvent *se = (SWELL_InternalObjectHeader_SocketEvent *)hdr;
if (WDL_NOT_NORMALLY(se->socket[0]<0)) Sleep(msTO!=INFINITE?msTO:1);
else
{
again:
struct pollfd fd = { se->socket[0], POLLIN, 0 };
const int res = poll(&fd,1,msTO==INFINITE?-1 : msTO);
if (res < 0) return WAIT_FAILED;
if (res>0 && (fd.revents&POLLIN))
{
if (se->hdr.type == INTERNAL_OBJECT_SOCKETEVENT && se->autoReset)
{
char buf[128];
if (read(se->socket[0],buf,sizeof(buf))<1) goto again;
}
return WAIT_OBJECT_0;
}
return WAIT_TIMEOUT;
}
}
break;
case INTERNAL_OBJECT_EVENT:
{
SWELL_InternalObjectHeader_Event *evt = (SWELL_InternalObjectHeader_Event*)hdr;
int rv=WAIT_OBJECT_0;
pthread_mutex_lock(&evt->mutex);
if (msTO == 0)
{
if (!evt->isSignal) rv=WAIT_TIMEOUT;
}
else if (msTO == INFINITE)
{
while (!evt->isSignal) pthread_cond_wait(&evt->cond,&evt->mutex);
}
else
{
// timed wait
#ifdef __APPLE__
struct timespec ts;
ts.tv_sec = msTO/1000;
ts.tv_nsec = (msTO%1000)*1000000;
#endif
while (!evt->isSignal)
{
#ifdef __APPLE__
if (pthread_cond_timedwait_relative_np(&evt->cond,&evt->mutex,&ts)==ETIMEDOUT)
{
rv = WAIT_TIMEOUT;
break;
}
#else
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC,&ts);
ts.tv_sec += msTO/1000;
ts.tv_nsec += (msTO%1000)*1000000;
if (ts.tv_nsec>=1000000000)
{
int n = ts.tv_nsec/1000000000;
ts.tv_sec+=n;
ts.tv_nsec -= ((long long)n * (long long)1000000000);
}
if (pthread_cond_timedwait(&evt->cond,&evt->mutex,&ts))
{
rv = WAIT_TIMEOUT;
break;
}
#endif
// we should track/correct the timeout amount here since in theory we could end up waiting a bit longer!
}
}
if (!evt->isManualReset && rv==WAIT_OBJECT_0) evt->isSignal=false;
pthread_mutex_unlock(&evt->mutex);
return rv;
}
break;
}
return WAIT_FAILED;
}
static void *__threadproc(void *parm)
{
#ifdef SWELL_TARGET_OSX
void *arp=SWELL_InitAutoRelease();
#endif
SWELL_InternalObjectHeader_Thread *t=(SWELL_InternalObjectHeader_Thread*)parm;
t->retv=t->threadProc(t->threadParm);
t->done=1;
CloseHandle(parm);
#ifdef SWELL_TARGET_OSX
SWELL_QuitAutoRelease(arp);
#endif
pthread_exit(0);
return 0;
}
DWORD GetCurrentThreadId()
{
return (DWORD)(INT_PTR)pthread_self(); // this is incorrect on x64
}
HANDLE CreateEvent(void *SA, BOOL manualReset, BOOL initialSig, const char *ignored)
{
SWELL_InternalObjectHeader_Event *buf = (SWELL_InternalObjectHeader_Event*)malloc(sizeof(SWELL_InternalObjectHeader_Event));
buf->hdr.type=INTERNAL_OBJECT_EVENT;
buf->hdr.count=1;
buf->isSignal = !!initialSig;
buf->isManualReset = !!manualReset;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
#ifdef __linux__
pthread_mutexattr_setprotocol(&attr,PTHREAD_PRIO_INHERIT);
#endif
pthread_mutex_init(&buf->mutex,&attr);
pthread_mutexattr_destroy(&attr);
#ifndef __APPLE__
pthread_condattr_t cattr;
pthread_condattr_init(&cattr);
pthread_condattr_setclock(&cattr,CLOCK_MONOTONIC);
pthread_cond_init(&buf->cond,&cattr);
pthread_condattr_destroy(&cattr);
#else
pthread_cond_init(&buf->cond,NULL);
#endif
return (HANDLE)buf;
}
HANDLE CreateThread(void *TA, DWORD stackSize, DWORD (*ThreadProc)(LPVOID), LPVOID parm, DWORD cf, DWORD *tidOut)
{
#ifdef SWELL_TARGET_OSX
SWELL_EnsureMultithreadedCocoa();
#endif
SWELL_InternalObjectHeader_Thread *buf = (SWELL_InternalObjectHeader_Thread *)malloc(sizeof(SWELL_InternalObjectHeader_Thread));
buf->hdr.type=INTERNAL_OBJECT_THREAD;
buf->hdr.count=2;
buf->threadProc=ThreadProc;
buf->threadParm = parm;
buf->retv=0;
buf->pt=0;
buf->done=0;
pthread_create(&buf->pt,NULL,__threadproc,buf);
if (tidOut) *tidOut=(DWORD)(INT_PTR)buf->pt; // incorrect on x64
return (HANDLE)buf;
}
BOOL SetThreadPriority(HANDLE hand, int prio)
{
SWELL_InternalObjectHeader_Thread *evt=(SWELL_InternalObjectHeader_Thread*)hand;
#ifdef __linux__
static int s_rt_max;
if (!evt && prio >= 0x10000 && prio < 0x10000 + 100)
{
s_rt_max = prio - 0x10000;
return TRUE;
}
#endif
if (WDL_NOT_NORMALLY(!evt || evt->hdr.type != INTERNAL_OBJECT_THREAD)) return FALSE;
if (evt->done) return FALSE;
int pol;
struct sched_param param;
memset(&param,0,sizeof(param));
#ifdef __linux__
// linux only has meaningful priorities if using realtime threads,
// for this to be enabled the caller should use:
// #ifdef __linux__
// SetThreadPriority(NULL,0x10000 + max_thread_priority (0..99));
// #endif
if (s_rt_max < 1 || prio <= THREAD_PRIORITY_NORMAL)
{
pol = SCHED_NORMAL;
param.sched_priority=0;
}
else
{
int lb = s_rt_max;
if (prio < THREAD_PRIORITY_TIME_CRITICAL)
{
lb--;
if (prio < THREAD_PRIORITY_HIGHEST)
{
lb--;
if (prio < THREAD_PRIORITY_ABOVE_NORMAL) lb--;
if (lb > 40) lb = 40; // if not HIGHEST or higher, do not permit RT priority of more than 40
}
}
param.sched_priority = lb < 1 ? 1 : lb;
pol = SCHED_RR;
}
return !pthread_setschedparam(evt->pt,pol,&param);
#else
if (!pthread_getschedparam(evt->pt,&pol,&param))
{
// this is for darwin, but might work elsewhere
param.sched_priority = 31 + prio;
if (prio >= THREAD_PRIORITY_TIME_CRITICAL) // this could be _HIGHEST eventually
pol = SCHED_FIFO;
int mt=sched_get_priority_min(pol);
if (param.sched_priority<mt||param.sched_priority > (mt=sched_get_priority_max(pol)))param.sched_priority=mt;
if (!pthread_setschedparam(evt->pt,pol,&param))
{
return TRUE;
}
}
return FALSE;
#endif
}
BOOL SetEvent(HANDLE hand)
{
SWELL_InternalObjectHeader_Event *evt=(SWELL_InternalObjectHeader_Event*)hand;
if (WDL_NOT_NORMALLY(!evt)) return FALSE;
if (evt->hdr.type == INTERNAL_OBJECT_EVENT)
{
pthread_mutex_lock(&evt->mutex);
if (!evt->isSignal)
{
evt->isSignal = true;
if (evt->isManualReset) pthread_cond_broadcast(&evt->cond);
else pthread_cond_signal(&evt->cond);
}
pthread_mutex_unlock(&evt->mutex);
return TRUE;
}
if (evt->hdr.type == INTERNAL_OBJECT_SOCKETEVENT)
{
SWELL_InternalObjectHeader_SocketEvent *se=(SWELL_InternalObjectHeader_SocketEvent*)hand;
if (se->socket[1]>=0)
{
if (se->socket[0]>=0)
{
struct pollfd fd = { se->socket[0], POLLIN, 0 };
int res = poll(&fd,1,0);
if (res > 0 && (fd.revents&POLLIN)) return TRUE; // already set
}
char c=0;
if (write(se->socket[1],&c,1) != 1)
{
WDL_ASSERT( false /* write to socket failed in SetEvent() */ );
}
}
return TRUE;
}
WDL_ASSERT(false);
return FALSE;
}
BOOL ResetEvent(HANDLE hand)
{
SWELL_InternalObjectHeader_Event *evt=(SWELL_InternalObjectHeader_Event*)hand;
if (WDL_NOT_NORMALLY(!evt)) return FALSE;
if (evt->hdr.type == INTERNAL_OBJECT_EVENT)
{
evt->isSignal=false;
return TRUE;
}
if (evt->hdr.type == INTERNAL_OBJECT_SOCKETEVENT)
{
SWELL_InternalObjectHeader_SocketEvent *se=(SWELL_InternalObjectHeader_SocketEvent*)hand;
if (se->socket[0]>=0)
{
char buf[128];
if (read(se->socket[0],buf,sizeof(buf)) < 0)
{
WDL_ASSERT( false /* read from socket failed in ResetEvent() */ );
}
}
return TRUE;
}
WDL_ASSERT(false);
return FALSE;
}
BOOL WinOffsetRect(LPRECT lprc, int dx, int dy)
{
if(!lprc) return 0;
lprc->left+=dx;
lprc->top+=dy;
lprc->right+=dx;
lprc->bottom+=dy;
return TRUE;
}
BOOL WinSetRect(LPRECT lprc, int xLeft, int yTop, int xRight, int yBottom)
{
if(!lprc) return 0;
lprc->left = xLeft;
lprc->top = yTop;
lprc->right = xRight;
lprc->bottom = yBottom;
return TRUE;
}
int WinIntersectRect(RECT *out, const RECT *in1, const RECT *in2)
{
RECT tmp = *in1; in1 = &tmp;
memset(out,0,sizeof(RECT));
if (in1->right <= in1->left) return false;
if (in2->right <= in2->left) return false;
if (in1->bottom <= in1->top) return false;
if (in2->bottom <= in2->top) return false;
// left is maximum of minimum of right edges and max of left edges
out->left = wdl_max(in1->left,in2->left);
out->right = wdl_min(in1->right,in2->right);
out->top=wdl_max(in1->top,in2->top);
out->bottom = wdl_min(in1->bottom,in2->bottom);
return out->right>out->left && out->bottom>out->top;
}
void WinUnionRect(RECT *out, const RECT *in1, const RECT *in2)
{
if (in1->left == in1->right && in1->top == in1->bottom)
{
*out = *in2;
}
else if (in2->left == in2->right && in2->top == in2->bottom)
{
*out = *in1;
}
else
{
out->left = wdl_min(in1->left,in2->left);
out->top = wdl_min(in1->top,in2->top);
out->right=wdl_max(in1->right,in2->right);
out->bottom=wdl_max(in1->bottom,in2->bottom);
}
}
typedef struct
{
int sz;
int refcnt;
} GLOBAL_REC;
void *GlobalLock(HANDLE h)
{
if (!h) return 0;
GLOBAL_REC *rec=((GLOBAL_REC*)h)-1;
rec->refcnt++;
return h;
}
int GlobalSize(HANDLE h)
{
if (!h) return 0;
GLOBAL_REC *rec=((GLOBAL_REC*)h)-1;
return rec->sz;
}
void GlobalUnlock(HANDLE h)
{
if (!h) return;
GLOBAL_REC *rec=((GLOBAL_REC*)h)-1;
rec->refcnt--;
}
void GlobalFree(HANDLE h)
{
if (!h) return;
GLOBAL_REC *rec=((GLOBAL_REC*)h)-1;
if (rec->refcnt)
{
// note error freeing locked ram
}
free(rec);
}
HANDLE GlobalAlloc(int flags, int sz)
{
if (sz<0)sz=0;
GLOBAL_REC *rec=(GLOBAL_REC*)malloc(sizeof(GLOBAL_REC)+sz);
if (!rec) return 0;
rec->sz=sz;
rec->refcnt=0;
if (flags&GMEM_FIXED) memset(rec+1,0,sz);
return rec+1;
}
char *lstrcpyn(char *dest, const char *src, int l)
{
if (l<1) return dest;
char *dsrc=dest;
while (--l > 0)
{
char p=*src++;
if (!p) break;
*dest++=p;
}
*dest++=0;
return dsrc;
}
static WDL_Mutex s_libraryMutex;
static int libkeycomp(void **p1, void **p2)
{
INT_PTR a=(INT_PTR)(*p1) - (INT_PTR)(*p2);
if (a<0)return -1;
if (a>0) return 1;
return 0;
}
static WDL_AssocArray<void *, SWELL_HINSTANCE *> s_loadedLibs(libkeycomp); // index by OS-provided handle (rather than filename since filenames could be relative etc)
HINSTANCE LoadLibrary(const char *fn)
{
return LoadLibraryGlobals(fn,false);
}
#ifndef SWELL_TARGET_OSX
extern "C" {
void *SWELLAPI_GetFunc(const char *name);
};
#endif
HINSTANCE LoadLibraryGlobals(const char *fn, bool symbolsAsGlobals)
{
if (!fn || !*fn) return NULL;
void *inst = NULL, *bundleinst=NULL;
struct stat ss;
#ifdef SWELL_TARGET_OSX
if (stat(fn,&ss) || (ss.st_mode&S_IFMT) == S_IFDIR)
{
CFStringRef str=(CFStringRef)SWELL_CStringToCFString(fn);
CFURLRef r=CFURLCreateWithFileSystemPath(NULL,str,kCFURLPOSIXPathStyle,true);
CFRelease(str);
bundleinst=(void *)CFBundleCreate(NULL,r);
CFRelease(r);
if (bundleinst)
{
if (!CFBundleLoadExecutable((CFBundleRef)bundleinst))
{
CFRelease((CFBundleRef)bundleinst);
bundleinst=NULL;
}
}
}
#endif
#ifdef SWELL_TARGET_OSX
if (!bundleinst)
#endif
{
inst=dlopen(fn,RTLD_NOW|(symbolsAsGlobals?RTLD_GLOBAL:RTLD_LOCAL));
if (!inst)
{
if (fn[0] == '/' && !stat(fn,&ss) && (ss.st_mode&S_IFMT) != S_IFDIR)
{
const char *err = dlerror();
fprintf(stderr,"swell: dlopen() failed: %s\n",err ? err : fn);
}
return 0;
}
}
WDL_MutexLock lock(&s_libraryMutex);
SWELL_HINSTANCE *rec = s_loadedLibs.Get(bundleinst ? bundleinst : inst);
if (!rec)
{
rec = (SWELL_HINSTANCE *)calloc(sizeof(SWELL_HINSTANCE),1);
rec->instptr = inst;
#ifdef __APPLE__
rec->bundleinstptr = bundleinst;
#endif
rec->refcnt = 1;
s_loadedLibs.Insert(bundleinst ? bundleinst : inst,rec);
#ifndef SWELL_EXTRA_MINIMAL
int (*SWELL_dllMain)(HINSTANCE, DWORD, LPVOID) = 0;
BOOL (*dllMain)(HINSTANCE, DWORD, LPVOID) = 0;
*(void **)&SWELL_dllMain = GetProcAddress(rec,"SWELL_dllMain");
if (SWELL_dllMain)
{
if (!SWELL_dllMain(rec,DLL_PROCESS_ATTACH,
#ifdef SWELL_TARGET_OSX
NULL
#else
(void*)SWELLAPI_GetFunc
#endif
))
{
FreeLibrary(rec);
return 0;
}
*(void **)&dllMain = GetProcAddress(rec,"DllMain");
if (dllMain)
{
if (!dllMain(rec,DLL_PROCESS_ATTACH,NULL))
{
SWELL_dllMain(rec,DLL_PROCESS_DETACH,(void*)NULL);
FreeLibrary(rec);
return 0;
}
}
}
rec->SWELL_dllMain = SWELL_dllMain;
rec->dllMain = dllMain;
#endif
}
else rec->refcnt++;
return rec;
}
void *GetProcAddress(HINSTANCE hInst, const char *procName)
{
if (WDL_NOT_NORMALLY(!hInst)) return 0;
SWELL_HINSTANCE *rec=(SWELL_HINSTANCE*)hInst;
void *ret = NULL;
#ifdef SWELL_TARGET_OSX
if (rec->bundleinstptr)
{
CFStringRef str=(CFStringRef)SWELL_CStringToCFString(procName);
ret = (void *)CFBundleGetFunctionPointerForName((CFBundleRef)rec->bundleinstptr, str);
if (ret) rec->lastSymbolRequested=ret;
CFRelease(str);
return ret;
}
#endif
if (rec->instptr) ret=(void *)dlsym(rec->instptr, procName);
if (ret) rec->lastSymbolRequested=ret;
return ret;
}
BOOL FreeLibrary(HINSTANCE hInst)
{
if (WDL_NOT_NORMALLY(!hInst)) return FALSE;
WDL_MutexLock lock(&s_libraryMutex);
bool dofree=false;
SWELL_HINSTANCE *rec=(SWELL_HINSTANCE*)hInst;
if (--rec->refcnt<=0)
{
dofree=true;
#ifdef SWELL_TARGET_OSX
s_loadedLibs.Delete(rec->bundleinstptr ? rec->bundleinstptr : rec->instptr);
#else
s_loadedLibs.Delete(rec->instptr);
#endif
#ifndef SWELL_EXTRA_MINIMAL
if (rec->SWELL_dllMain)
{
rec->SWELL_dllMain(rec,DLL_PROCESS_DETACH,NULL);
if (rec->dllMain) rec->dllMain(rec,DLL_PROCESS_DETACH,NULL);
}
#endif
}
#ifdef SWELL_TARGET_OSX
if (rec->bundleinstptr)
{
CFRelease((CFBundleRef)rec->bundleinstptr);
}
#endif
if (rec->instptr) dlclose(rec->instptr);
if (dofree) free(rec);
return TRUE;
}
void* SWELL_GetBundle(HINSTANCE hInst)
{
SWELL_HINSTANCE* rec=(SWELL_HINSTANCE*)hInst;
WDL_ASSERT(rec!=NULL);
#ifdef SWELL_TARGET_OSX
if (rec) return rec->bundleinstptr;
#else
if (rec) return rec->instptr;
#endif
return NULL;
}
DWORD GetModuleFileName(HINSTANCE hInst, char *fn, DWORD nSize)
{
*fn=0;
void *instptr = NULL, *lastSymbolRequested=NULL;
#ifdef SWELL_TARGET_OSX
void *bundleinstptr=NULL;
#endif
if (hInst)
{
SWELL_HINSTANCE *p = (SWELL_HINSTANCE*)hInst;
instptr = p->instptr;
#ifdef SWELL_TARGET_OSX
bundleinstptr = p->bundleinstptr;
#endif
lastSymbolRequested=p->lastSymbolRequested;
}
#ifdef SWELL_TARGET_OSX
if (!instptr || bundleinstptr)
{
CFBundleRef bund=bundleinstptr ? (CFBundleRef)bundleinstptr : CFBundleGetMainBundle();
if (bund)
{
CFURLRef url=CFBundleCopyBundleURL(bund);
if (url)
{
char buf[8192];
if (CFURLGetFileSystemRepresentation(url,true,(UInt8*)buf,sizeof(buf))) lstrcpyn(fn,buf,nSize);
CFRelease(url);
}
}
return (DWORD)strlen(fn);
}
#elif defined(__linux__)
if (!instptr) // get exe file name
{
int sz=readlink("/proc/self/exe",fn,nSize);
if (sz<1)
{
static char tmp;
// this will likely not work if the program was launched with a relative path
// and the cwd has changed, but give it a try anyway
Dl_info inf={0,};
if (dladdr(&tmp,&inf) && inf.dli_fname)
sz = (int) strlen(inf.dli_fname);
else
sz=0;
}
if ((DWORD)sz>=nSize)sz=nSize-1;
fn[sz]=0;
return sz;
}
#endif
if (instptr && lastSymbolRequested)
{
Dl_info inf={0,};
dladdr(lastSymbolRequested,&inf);
if (inf.dli_fname)
{
lstrcpyn(fn,inf.dli_fname,nSize);
return (DWORD)strlen(fn);
}
}
return 0;
}
bool SWELL_GenerateGUID(void *g)
{
#ifdef SWELL_TARGET_OSX
CFUUIDRef r = CFUUIDCreate(NULL);
if (!r) return false;
CFUUIDBytes a = CFUUIDGetUUIDBytes(r);
if (g) memcpy(g,&a,16);
CFRelease(r);
return true;
#else
int f = open("/dev/urandom",O_RDONLY);
if (f<0) return false;
int v = read(f,g,sizeof(GUID));
close(f);
return v == sizeof(GUID);
#endif
}
void GetTempPath(int bufsz, char *buf)
{
if (bufsz<2)
{
if (bufsz>0) *buf=0;
return;
}
#ifdef __APPLE__
const char *p = getenv("TMPDIR");
#else
const char *p = getenv("TEMP");
#endif
if (!p || !*p) p="/tmp/";
lstrcpyn(buf, p, bufsz);
size_t len = strlen(buf);
if (!len || buf[len-1] != '/')
{
if (len > (size_t)bufsz-2) len = bufsz-2;
buf[len] = '/';
buf[len+1]=0;
}
}
const char *g_swell_appname;
char *g_swell_defini;
const char *g_swell_fontpangram;
#ifdef SWELL_TARGET_GDK
bool swell_gdk_set_fullscreen(HWND, int);
#endif
void *SWELL_ExtendedAPI(const char *key, void *v)
{
if (!strcmp(key,"APPNAME")) g_swell_appname = (const char *)v;
else if (!strcmp(key,"INIFILE"))
{
free(g_swell_defini);
g_swell_defini = v ? strdup((const char *)v) : NULL;
#ifndef SWELL_TARGET_OSX
char buf[1024];
GetPrivateProfileString(".swell","max_open_files","",buf,sizeof(buf),"");
if (!buf[0])
WritePrivateProfileString(".swell","max_open_files","auto // (default is min of default or 16384)","");
struct rlimit rl = {0,};
getrlimit(RLIMIT_NOFILE,&rl);
const int orig_n = atoi(buf);
rlim_t n = orig_n > 0 ? (rlim_t) orig_n : 16384;
if (n > rl.rlim_max) n = rl.rlim_max;
if (orig_n > 0 ? (n != rl.rlim_cur) : (n > rl.rlim_cur))
{
rl.rlim_cur = n;
setrlimit(RLIMIT_NOFILE,&rl);
#ifdef _DEBUG
getrlimit(RLIMIT_NOFILE,&rl);
printf("applied rlimit %d/%d\n",(int)rl.rlim_cur,(int)rl.rlim_max);
#endif
}
#endif
#ifdef SWELL_TARGET_GDK
if (g_swell_defini)
{
void swell_load_color_theme(const char *fn);
lstrcpyn_safe(buf,g_swell_defini,sizeof(buf));
WDL_remove_filepart(buf);
if (buf[0])
{
lstrcatn(buf,"/libSwell.colortheme",sizeof(buf));
swell_load_color_theme(buf);
WDL_remove_fileext(buf);
lstrcatn(buf,"-user.colortheme",sizeof(buf));
swell_load_color_theme(buf);
}
}
GetPrivateProfileString(".swell","ui_scale","",buf,sizeof(buf),"");
if (buf[0])
{
double sc = atof(buf);
if (sc > 0.01 && sc < 10.0 && sc != 1.0)
{
g_swell_ui_scale = (int) (256 * sc + 0.5);
}
}
else
{
WritePrivateProfileString(".swell","ui_scale","1.0 // scales the sizes in libSwell.colortheme","");
}
bool no_auto_hidpi=false;
GetPrivateProfileString(".swell","ui_scale_auto","",buf,sizeof(buf),"");
if (buf[0])
{
const char *p = buf;
while (*p == ' ') p++;
if (*p == '0' && atoi(p) == 0)
no_auto_hidpi=true;
}
else
{
WritePrivateProfileString(".swell","ui_scale_auto","1 // set to 0 to disable system DPI detection (only used when ui_scale=1)","");
}
swell_scaling_init(no_auto_hidpi);
if (g_swell_ui_scale != 256)
{
const double sc = g_swell_ui_scale * (1.0 / 256.0);
if (sc>0) g_swell_ctheme.default_font_size--;
#define __scale(x,c) g_swell_ctheme.x = (int) (g_swell_ctheme.x * sc + 0.5);
SWELL_GENERIC_THEMESIZEDEFS(__scale,__scale)
#undef __scale
if (sc>0) g_swell_ctheme.default_font_size++;
}
#endif
}
else if (!strcmp(key,"FONTPANGRAM"))
{
g_swell_fontpangram = (const char *)v;
}
#ifdef SWELL_TARGET_GDK
else if (!strcmp(key,"-FULLSCREEN"))
return v && swell_gdk_set_fullscreen((HWND)v,0) ? v : NULL;
else if (!strcmp(key,"FULLSCREEN"))
return v && swell_gdk_set_fullscreen((HWND)v,1) ? v : NULL;
else if (!strcmp(key,"oFULLSCREEN"))
return v && swell_gdk_set_fullscreen((HWND)v,2) ? v : NULL;
else if (!strcmp(key,"activate_app"))
{
void swell_gdk_reactivate_app(void);
swell_gdk_reactivate_app();
}
#endif
else if (!strcmp(key,"SWELL_DDrop_onDragLeave")) { *(void **)&SWELL_DDrop_onDragLeave = v; return v; }
else if (!strcmp(key,"SWELL_DDrop_onDragOver")) { *(void **)&SWELL_DDrop_onDragOver = v; return v; }
else if (!strcmp(key,"SWELL_DDrop_onDragEnter")) { *(void **)&SWELL_DDrop_onDragEnter = v; return v; }
else if (!strcmp(key,"SWELL_DDrop_getDroppedFileTargetPath")) { *(void **)&SWELL_DDrop_getDroppedFileTargetPath = v; return v; }
return NULL;
}
#endif