/* 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 #include #include #include #include #include #include #include "swell-internal.h" #ifdef SWELL_TARGET_OSX #include #endif #ifdef __APPLE__ #include #include #else #include #endif #ifdef __linux__ #include #endif #include #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.xright && 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 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 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(¶m,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,¶m); #else if (!pthread_getschedparam(evt->pt,&pol,¶m)) { // 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=sched_get_priority_max(pol)))param.sched_priority=mt; if (!pthread_setschedparam(evt->pt,pol,¶m)) { 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 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