1208 lines
31 KiB
C++
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(¶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||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<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
|