/*
* %CopyrightBegin%
*
* Copyright Ericsson AB 2008-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* %CopyrightEnd%
*/
#include <stdio.h>
#include <signal.h>
#include <wx/wx.h>
// Avoid including these in dcbuffer below
#include "wx/dcmemory.h"
#include "wx/dcclient.h"
#include "wx/window.h"
// Ok ugly but needed for wxBufferedDC crash workaround
#define private public
#include <wx/dcbuffer.h>
#undef private
#include "wxe_impl.h"
#include "wxe_events.h"
#include "wxe_return.h"
IMPLEMENT_APP_NO_MAIN(WxeApp)
DECLARE_APP(WxeApp)
DEFINE_EVENT_TYPE(wxeEVT_META_COMMAND)
#define WXE_NOT_INITIATED 0
#define WXE_INITIATED 1
#define WXE_EXITED 2
#define WXE_ERROR -1
#define WXE_NORMAL 0
#define WXE_CALLBACK 1
#define WXE_STORED 2
ErlDrvTid wxe_thread;
ErlDrvMutex *wxe_status_m;
ErlDrvCond *wxe_status_c;
ErlDrvMutex * wxe_batch_locker_m;
ErlDrvCond * wxe_batch_locker_c;
static int wxe_status = WXE_NOT_INITIATED;
wxList * wxe_batch = NULL;
wxList * wxe_batch_cb_saved = NULL;
ErlDrvTermData wxe_batch_caller = 0;
ErlDrvTermData init_caller = 0;
// extern opengl
void gl_dispatch(int op, char *bp, ErlDrvTermData caller, WXEBinRef *bins[]);
// Until fixed in emulator
#ifndef _WIN32
extern "C" {
extern void erts_thread_disable_fpe(void);
}
#endif
#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
#define __DARWIN__ 1
#endif
#ifdef __DARWIN__
extern "C" {
int erl_drv_stolen_main_thread_join(ErlDrvTid tid, void **respp);
int erl_drv_steal_main_thread(char *name,
ErlDrvTid *dtid,
void* (*func)(void*),
void* arg,
ErlDrvThreadOpts *opts);
}
#endif
void *wxe_main_loop(void * );
/* ************************************************************
* START AND STOP of driver thread
* ************************************************************/
int load_native_gui()
{
return 1;
}
int start_native_gui(wxe_data *sd)
{
int res;
wxe_status_m = erl_drv_mutex_create((char *) "wxe_status_m");
wxe_status_c = erl_drv_cond_create((char *)"wxe_status_c");
wxe_batch_locker_m = erl_drv_mutex_create((char *)"wxe_batch_locker_m");
wxe_batch_locker_c = erl_drv_cond_create((char *)"wxe_batch_locker_c");
init_caller = driver_connected(sd->port);
#ifdef __DARWIN__
res = erl_drv_steal_main_thread((char *)"wxwidgets",
&wxe_thread,wxe_main_loop,(void *) sd->pdl,NULL);
#else
res = erl_drv_thread_create((char *)"wxwidgets",
&wxe_thread,wxe_main_loop,(void *) sd->pdl,NULL);
#endif
if(res == 0) {
erl_drv_mutex_lock(wxe_status_m);
for(;wxe_status == WXE_NOT_INITIATED;) {
erl_drv_cond_wait(wxe_status_c, wxe_status_m);
}
erl_drv_mutex_unlock(wxe_status_m);
return wxe_status;
} else {
wxString msg;
msg.Printf(wxT("Erlang failed to create wxe-thread %d\r\n"), res);
send_msg("error", &msg);
return -1;
}
}
void stop_native_gui(wxe_data *sd)
{
if(wxe_status == WXE_INITIATED) {
meta_command(WXE_SHUTDOWN, sd);
}
#ifdef __DARWIN__
erl_drv_stolen_main_thread_join(wxe_thread, NULL);
#else
erl_drv_thread_join(wxe_thread, NULL);
#endif
erl_drv_mutex_destroy(wxe_status_m);
erl_drv_cond_destroy(wxe_status_c);
erl_drv_mutex_destroy(wxe_batch_locker_m);
erl_drv_cond_destroy(wxe_batch_locker_c);
}
void unload_native_gui()
{
}
/* ************************************************************
* Commands from erlang
* Called by emulator thread
* ************************************************************/
void push_command(int op,char * buf,int len, wxe_data *sd)
{
// fprintf(stderr, "Op %d %d\r\n", op, (int) driver_caller(sd->port)),fflush(stderr);
wxeCommand *Cmd = new wxeCommand(op, buf, len, sd);
erl_drv_mutex_lock(wxe_batch_locker_m);
wxe_batch->Append(Cmd);
if(wxe_batch_caller > 0) {
// wx-thread is waiting on batch end in cond_wait
erl_drv_cond_signal(wxe_batch_locker_c);
} else {
// wx-thread is waiting gui-events
if(op == WXE_BATCH_BEGIN) {
wxe_batch_caller = 1;
}
erl_drv_cond_signal(wxe_batch_locker_c);
wxWakeUpIdle();
}
erl_drv_mutex_unlock(wxe_batch_locker_m);
}
void meta_command(int what, wxe_data *sd) {
if(what == PING_PORT) {
erl_drv_mutex_lock(wxe_batch_locker_m);
if(wxe_batch_caller > 0) {
wxeCommand *Cmd = new wxeCommand(WXE_DEBUG_PING, NULL, 0, sd);
wxe_batch->Append(Cmd);
erl_drv_cond_signal(wxe_batch_locker_c);
} else {
wxWakeUpIdle();
}
erl_drv_mutex_unlock(wxe_batch_locker_m);
} else {
if(sd) {
wxeMetaCommand Cmd(sd, what);
wxTheApp->AddPendingEvent(Cmd);
}
}
}
/* ************************************************************
* wxWidgets Thread
* ************************************************************/
void *wxe_main_loop(void *vpdl)
{
int result;
int argc = 1;
char * temp = (char *) "Erlang";
char * argv[] = {temp,NULL};
ErlDrvPDL pdl = (ErlDrvPDL) vpdl;
driver_pdl_inc_refc(pdl);
// ErlDrvSysInfo einfo;
// driver_system_info(&einfo, sizeof(ErlDrvSysInfo));
// Disable floating point execption if they are on.
// This should be done in emulator but it's not in yet.
#ifndef _WIN32
erts_thread_disable_fpe();
#endif
result = wxEntry(argc, argv);
// fprintf(stderr, "WXWidgets quits main loop %d \r\n", result);
if(result >= 0 && wxe_status == WXE_INITIATED) {
/* We are done try to make a clean exit */
wxe_status = WXE_EXITED;
driver_pdl_dec_refc(pdl);
#ifndef __DARWIN__
erl_drv_thread_exit(NULL);
#endif
return NULL;
} else {
erl_drv_mutex_lock(wxe_status_m);
wxe_status = WXE_ERROR;
erl_drv_cond_signal(wxe_status_c);
erl_drv_mutex_unlock(wxe_status_m);
driver_pdl_dec_refc(pdl);
return NULL;
}
}
wxFrame * dummy_window;
void create_dummy_window() {
dummy_window = new wxFrame(NULL,-1, wxT("wx driver"),
wxDefaultPosition, wxSize(5,5),
wxFRAME_NO_TASKBAR);
dummy_window->Connect(wxID_ANY, wxEVT_CLOSE_WINDOW,
(wxObjectEventFunction) (wxEventFunction) &WxeApp::dummy_close);
}
// wxMac really wants a top level window which command-q quits if there are no
// windows open, and this will kill the thread, so restart the dummy_window each
// time a we receive a close.
void WxeApp::dummy_close(wxEvent& Ev) {
// fprintf(stderr, "Tried to close dummy window\r\n"); fflush(stderr);
create_dummy_window();
}
// Init wx-widgets thread
bool WxeApp::OnInit()
{
wxe_ps_init();
global_me = new wxeMemEnv();
wxe_batch = new wxList;
wxe_batch_cb_saved = new wxList;
cb_buff = NULL;
// wxIdleEvent::SetMode(wxIDLE_PROCESS_SPECIFIED); Hmm printpreview doesn't work in 2.9 with this
this->Connect(wxID_ANY, wxEVT_IDLE,
(wxObjectEventFunction) (wxEventFunction) &WxeApp::idle);
this->Connect(CREATE_PORT, wxeEVT_META_COMMAND,
(wxObjectEventFunction) (wxEventFunction) &WxeApp::newMemEnv);
this->Connect(DELETE_PORT, wxeEVT_META_COMMAND,
(wxObjectEventFunction) (wxEventFunction) &WxeApp::destroyMemEnv);
this->Connect(WXE_SHUTDOWN, wxeEVT_META_COMMAND,
(wxObjectEventFunction) (wxEventFunction) &WxeApp::shutdown);
// fprintf(stderr, "Size void* %d: long %d long long %d int64 %d \r\n",
// sizeof(void *), sizeof(long), sizeof(long long), sizeof(wxInt64));
initEventTable();
wxInitAllImageHandlers();
/* Create a dummy window so wxWidgets don't automagicly quits the main loop
after the last window */
create_dummy_window();
init_nonconsts(global_me, init_caller);
erl_drv_mutex_lock(wxe_status_m);
wxe_status = WXE_INITIATED;
erl_drv_cond_signal(wxe_status_c);
erl_drv_mutex_unlock(wxe_status_m);
return TRUE;
}
void WxeApp::shutdown(wxeMetaCommand& Ecmd) {
delete dummy_window;
ExitMainLoop();
}
void send_msg(const char * type, wxString * msg) {
wxeReturn rt = wxeReturn(WXE_DRV_PORT, init_caller);
rt.addAtom((char *) "wxe_driver");
rt.addAtom((char *) type);
rt.add(msg);
rt.addTupleCount(3);
rt.send();
}
/* ************************************************************
* Erlang Command execution *
* ************************************************************/
/* Callback from printer and event callbacks */
void pre_callback()
{
// no-op
}
void handle_event_callback(ErlDrvPort port, ErlDrvTermData process)
{
WxeApp * app = (WxeApp *) wxTheApp;
ErlDrvMonitor monitor;
driver_monitor_process(port, process, &monitor);
// Should we be able to handle commands when recursing? probably
erl_drv_mutex_lock(wxe_batch_locker_m);
//fprintf(stderr, "\r\nCB EV Start %lu \r\n", process);fflush(stderr);
app->dispatch_cb(wxe_batch, wxe_batch_cb_saved, process);
//fprintf(stderr, "CB EV done %lu \r\n", process);fflush(stderr);
wxe_batch_caller = 0;
erl_drv_mutex_unlock(wxe_batch_locker_m);
driver_demonitor_process(port, &monitor);
}
// Called by wx thread
void WxeApp::idle(wxIdleEvent& event) {
dispatch_cmds();
}
void WxeApp::dispatch_cmds() {
erl_drv_mutex_lock(wxe_batch_locker_m);
int level = dispatch(wxe_batch_cb_saved, 0, WXE_STORED);
dispatch(wxe_batch, level, WXE_NORMAL);
wxe_batch_caller = 0;
erl_drv_mutex_unlock(wxe_batch_locker_m);
}
// Should have erl_drv_mutex_lock(wxe_batch_locker_m);
// when entering this function and it should be released
// afterwards
int WxeApp::dispatch(wxList * batch, int blevel, int list_type)
{
int ping = 0;
// erl_drv_mutex_lock(wxe_batch_locker_m); must be locked already
while(true)
{
if (batch->size() > 0) {
for( wxList::compatibility_iterator node = batch->GetFirst();
node;
node = batch->GetFirst())
{
wxeCommand *event = (wxeCommand *)node->GetData();
batch->Erase(node);
switch(event->op) {
case WXE_BATCH_END:
{--blevel; }
break;
case WXE_BATCH_BEGIN:
{blevel++; }
break;
case WXE_DEBUG_PING:
// When in debugger we don't want to hang waiting for a BATCH_END
// that never comes, because a breakpoint have hit.
ping++;
if(ping > 2)
blevel = 0;
break;
case WXE_CB_RETURN:
// erl_drv_mutex_unlock(wxe_batch_locker_m); should be called after
// whatever cleaning is necessary
if(event->len > 0) {
cb_buff = (char *) driver_alloc(event->len);
memcpy(cb_buff, event->buffer, event->len);
}
return blevel;
default:
erl_drv_mutex_unlock(wxe_batch_locker_m);
if(event->op < OPENGL_START) {
// fprintf(stderr, " c %d (%d) \r\n", event->op, blevel);
wxe_dispatch(*event);
} else {
gl_dispatch(event->op,event->buffer,event->caller,event->bin);
}
erl_drv_mutex_lock(wxe_batch_locker_m);
break;
}
delete event;
}
} else {
if((list_type == WXE_STORED) || (blevel <= 0 && list_type == WXE_NORMAL)) {
// erl_drv_mutex_unlock(wxe_batch_locker_m); should be called after
// whatever cleaning is necessary
return blevel;
}
// sleep until something happens
//fprintf(stderr, "%s:%d sleep %d %d %d %d \r\n", __FILE__, __LINE__, batch->size(), callback_returned, blevel, is_callback);fflush(stderr);
wxe_batch_caller++;
while(batch->size() == 0) {
erl_drv_cond_wait(wxe_batch_locker_c, wxe_batch_locker_m);
}
}
}
}
void WxeApp::dispatch_cb(wxList * batch, wxList * temp, ErlDrvTermData process) {
int callback_returned = 0;
while(true) {
if (batch->size() > 0) {
for( wxList::compatibility_iterator node = batch->GetFirst();
node;
node = batch->GetFirst())
{
wxeCommand *event = (wxeCommand *)node->GetData();
wxeMemEnv *memenv = getMemEnv(event->port);
batch->Erase(node);
// fprintf(stderr, " Ev %d %lu\r\n", event->op, event->caller);
if(event->caller == process || // Callbacks from CB process only
event->op == WXE_CB_START || // Event callback start change process
// Allow connect_cb during CB i.e. msg from wxe_server.
(memenv && event->caller == memenv->owner))
{
switch(event->op) {
case WXE_BATCH_END:
case WXE_BATCH_BEGIN:
case WXE_DEBUG_PING:
break;
case WXE_CB_RETURN:
if(event->len > 0) {
cb_buff = (char *) driver_alloc(event->len);
memcpy(cb_buff, event->buffer, event->len);
}
callback_returned = 1;
return;
case WXE_CB_START:
// CB start from now accept message from CB process only
process = event->caller;
break;
default:
erl_drv_mutex_unlock(wxe_batch_locker_m);
size_t start=temp->GetCount();
if(event->op < OPENGL_START) {
// fprintf(stderr, " cb %d \r\n", event->op);
wxe_dispatch(*event);
} else {
gl_dispatch(event->op,event->buffer,event->caller,event->bin);
}
erl_drv_mutex_lock(wxe_batch_locker_m);
if(temp->GetCount() > start) {
// We have recursed dispatch_cb and messages for this
// callback may be saved on temp list move them
// to orig list
for(wxList::compatibility_iterator node = temp->Item(start);
node;
node = node->GetNext()) {
wxeCommand *ev = (wxeCommand *)node->GetData();
if(ev->caller == process) {
batch->Append(ev);
temp->Erase(node);
}
}
}
if(callback_returned)
return;
break;
}
delete event;
} else {
// fprintf(stderr, " save %d \r\n", event->op);
temp->Append(event);
}
}
} else {
if(callback_returned) {
return;
}
// sleep until something happens
//fprintf(stderr, "%s:%d sleep %d %d %d %d \r\n", __FILE__, __LINE__, batch->size(), callback_returned, blevel, is_callback);fflush(stderr);
while(batch->size() == 0) {
erl_drv_cond_wait(wxe_batch_locker_c, wxe_batch_locker_m);
}
}
}
}
/* Memory handling */
void WxeApp::newMemEnv(wxeMetaCommand& Ecmd) {
wxeMemEnv * memenv = new wxeMemEnv();
driver_pdl_inc_refc(Ecmd.pdl);
for(int i = 0; i < global_me->next; i++) {
memenv->ref2ptr[i] = global_me->ref2ptr[i];
}
memenv->next = global_me->next;
refmap[(ErlDrvTermData) Ecmd.port] = memenv;
memenv->owner = Ecmd.caller;
ErlDrvTermData rt[] = {ERL_DRV_ATOM, driver_mk_atom((char *)"wx_port_initiated")};
driver_send_term(WXE_DRV_PORT,Ecmd.caller,rt,2);
}
void WxeApp::destroyMemEnv(wxeMetaCommand& Ecmd) {
// Clear incoming cmd queue first
// dispatch_cmds();
wxWindow *parent = NULL;
wxeMemEnv * memenv = refmap[(ErlDrvTermData) Ecmd.port];
if(wxe_debug) {
wxString msg;
msg.Printf(wxT("Destroying all memory "));
send_msg("debug", &msg);
}
// pre-pass delete all dialogs first since they might crash erlang otherwise
for(int i=1; i < memenv->next; i++) {
wxObject * ptr = (wxObject *) memenv->ref2ptr[i];
if(ptr) {
ptrMap::iterator it = ptr2ref.find(ptr);
if(it != ptr2ref.end()) {
wxeRefData *refd = it->second;
if(refd->alloc_in_erl) {
if(refd->type == 2) {
wxDialog *win = (wxDialog *) ptr;
if(win->IsModal()) {
win->EndModal(-1);
}
parent = win->GetParent();
if(parent) {
ptrMap::iterator parentRef = ptr2ref.find(parent);
if(parentRef == ptr2ref.end()) {
// The parent is already dead delete the parent ref
win->SetParent(NULL);
}
}
delete win;
}
}
}
}
}
// First pass, delete all top parents/windows of all linked objects
// fprintf(stderr, "close port %x\r\n", Ecmd.port);fflush(stderr);
for(int i=1; i < memenv->next; i++) {
void * ptr = memenv->ref2ptr[i];
if(ptr) {
ptrMap::iterator it = ptr2ref.find(ptr);
if(it != ptr2ref.end()) {
wxeRefData *refd = it->second;
if(refd->alloc_in_erl && refd->type == 0) {
parent = (wxWindow *) ptr;
// fprintf(stderr, "window %x %d\r\n", (int) parent, refd->ref);
while(parent->GetParent()) {
parent = parent->GetParent();
// fprintf(stderr, " parent %x \r\n", (int) parent);
}
ptrMap::iterator pdata = ptr2ref.find(parent);
if(pdata != ptr2ref.end()) {
delete parent;
} // else parent is already deleted
}
} else {
// fprintf(stderr, "Error found no ref in %d => %x\r\n", i, ptr);
}
}
}
// Second pass delete everything else allocated
// everything linked from windows should now be deleted
for(int i=1; i < memenv->next; i++) {
void * ptr = memenv->ref2ptr[i];
if(ptr) {
ptrMap::iterator it = ptr2ref.find(ptr);
if(it != ptr2ref.end()) {
wxeRefData *refd = it->second;
if(refd->alloc_in_erl) {
int type = refd->type;
if((refd->type == 1) && ((wxObject *)ptr)->IsKindOf(CLASSINFO(wxBufferedDC))) {
((wxBufferedDC *)ptr)->m_dc = NULL; // Workaround
}
wxString msg;
if((refd->type == 0)) { // Maybe also class 1
wxClassInfo *cinfo = ((wxObject *)ptr)->GetClassInfo();
msg.Printf(wxT("Memory leak: {wx_ref, %d, %s}"),
refd->ref, cinfo->GetClassName());
send_msg("error", &msg);
} else {
delete_object(ptr, refd);
}
if(type == 0 || type > 2) {
// Delete refs for leaks and non overridden allocs
delete refd;
ptr2ref.erase(it);
} // overridden allocs deletes meta-data in clearPtr
} else { // Not alloced in erl just delete references
if(refd->ref >= global_me->next) { // if it is not part of global ptrs
delete refd;
ptr2ref.erase(it);
}
}
}
}
}
// // Assert ?
// for(ptrMap::iterator it = ptr2ref.begin(); it != ptr2ref.end(); it++) {
// wxeRefData *refd = it->second;
// if(refd->ref >= global_me->next)
// fprintf(stderr, "L %d %d %d\r\n", refd->ref, refd->type, refd->alloc_in_erl);
// }
// fflush(stderr);
delete memenv;
driver_pdl_dec_refc(Ecmd.pdl);
refmap.erase((ErlDrvTermData) Ecmd.port);
}
wxeMemEnv * WxeApp::getMemEnv(ErlDrvPort port) {
return refmap[(ErlDrvTermData) port];
}
int WxeApp::newPtr(void * ptr, int type, wxeMemEnv *memenv) {
int ref;
intList free = memenv->free;
if(free.IsEmpty()) {
ref = memenv->next++;
} else {
ref = free.Pop();
};
if(ref >= memenv->max) {
memenv->max *= 2;
memenv->ref2ptr =
(void **) driver_realloc(memenv->ref2ptr,memenv->max * sizeof(void*));
}
memenv->ref2ptr[ref] = ptr;
if(wxe_debug) {
wxString msg;
msg.Printf(wxT("Creating {wx_ref, %d, unknown} at %p "), ref, ptr);
send_msg("debug", &msg);
}
ptr2ref[ptr] = new wxeRefData(ref, type, true, memenv);
// fprintf(stderr, "ptr %x id %d\r\n", (int) ptr,ref);
return ref;
}
int WxeApp::getRef(void * ptr, wxeMemEnv *memenv) {
if(!ptr) return 0; // NULL and zero is the same
ptrMap::iterator it = ptr2ref.find(ptr);
if(it != ptr2ref.end()) {
wxeRefData *refd = it->second;
if(refd->memenv == memenv || refd->memenv == global_me) {
// Found it return
return refd->ref;
} // else
// Old reference to deleted object, release old and recreate in current memenv.
ptr2ref.erase(it);
}
int ref;
intList free = memenv->free;
if(free.IsEmpty()) {
ref = memenv->next++;
} else {
ref = free.Pop();
};
if(ref >= memenv->max) {
memenv->max *= 2;
memenv->ref2ptr =
(void **) driver_realloc(memenv->ref2ptr,memenv->max * sizeof(void*));
}
memenv->ref2ptr[ref] = ptr;
ptr2ref[ptr] = new wxeRefData(ref, 0, false, memenv);
return ref;
}
void WxeApp::clearPtr(void * ptr) {
ptrMap::iterator it;
it = ptr2ref.find(ptr);
if(it != ptr2ref.end()) {
wxeRefData *refd = it->second;
intList free = refd->memenv->free;
int ref = refd->ref;
refd->memenv->ref2ptr[ref] = NULL;
free.Append(ref);
if(wxe_debug) {
wxString msg;
msg.Printf(wxT("Deleting {wx_ref, %d, unknown} at %p "), ref, ptr);
send_msg("debug", &msg);
}
if(((int) refd->pid) != -1) {
// Send terminate pid to owner
wxeReturn rt = wxeReturn(WXE_DRV_PORT,refd->memenv->owner, false);
rt.addAtom("_wxe_destroy_");
rt.add(ERL_DRV_PID, refd->pid);
rt.addTupleCount(2);
rt.send();
refd->pid = -1;
};
if(refd->type == 1 && ((wxObject*)ptr)->IsKindOf(CLASSINFO(wxSizer))) {
wxSizerItemList list = ((wxSizer*)ptr)->GetChildren();
for(wxSizerItemList::compatibility_iterator node = list.GetFirst();
node; node = node->GetNext()) {
wxSizerItem *item = node->GetData();
wxObject *content=NULL;
if((content = item->GetWindow()))
if(ptr2ref.end() == ptr2ref.find(content)) {
wxString msg;
wxClassInfo *cinfo = ((wxObject *)ptr)->GetClassInfo();
msg.Printf(wxT("Double usage detected of window at %p in sizer {wx_ref, %d, %s}"),
content, ref, cinfo->GetClassName());
send_msg("error", &msg);
((wxSizer*)ptr)->Detach((wxWindow*)content);
}
if((content = item->GetSizer()))
if(ptr2ref.end() == ptr2ref.find(content)) {
wxString msg;
wxClassInfo *cinfo = ((wxObject *)ptr)->GetClassInfo();
msg.Printf(wxT("Double usage detected of sizer at %p in sizer {wx_ref, %d, %s}"),
content, ref, cinfo->GetClassName());
send_msg("error", &msg);
((wxSizer*)ptr)->Detach((wxSizer*)content);
}
}
}
delete refd;
ptr2ref.erase(it);
}
}
void * WxeApp::getPtr(char * bp, wxeMemEnv *memenv) {
int index = *(int *) bp;
if(!memenv) {
throw wxe_badarg(index);
}
void * temp = memenv->ref2ptr[index];
if((index < memenv->next) && ((index == 0) || (temp > NULL)))
return temp;
else {
throw wxe_badarg(index);
}
}
void WxeApp::registerPid(char * bp, ErlDrvTermData pid, wxeMemEnv * memenv) {
int index = *(int *) bp;
if(!memenv)
throw wxe_badarg(index);
void * temp = memenv->ref2ptr[index];
if((index < memenv->next) && ((index == 0) || (temp > NULL))) {
ptrMap::iterator it;
it = ptr2ref.find(temp);
if(it != ptr2ref.end()) {
wxeRefData *refd = it->second;
refd->pid = pid;
return ;
}
};
throw wxe_badarg(index);
}
/* ************************************************************
* Misc utility classes
* ************************************************************/
/* ****************************************************************************
* Memory handling
* ****************************************************************************/
wxeMemEnv::wxeMemEnv() {
ref2ptr = (void **) driver_alloc(128*sizeof(void *));
ref2ptr[0] = NULL;
next = 1;
max = 128;
}
wxeMemEnv::~wxeMemEnv() {
driver_free(ref2ptr);
}
/* ****************************************************************************
* Erlang Commands (don't need to be derived of wxEvent anymore should
* be re-written to own class struct)
* ****************************************************************************/
wxeCommand::wxeCommand(int fc,char * cbuf,int buflen, wxe_data *sd)
: wxObject()
{
WXEBinRef *temp, *start, *prev;
int n = 0;
caller = driver_caller(sd->port);
port = sd->port;
op = fc;
len = buflen;
bin[0] = NULL;
bin[1] = NULL;
bin[2] = NULL;
if(cbuf) {
buffer = (char *) driver_alloc(len);
memcpy((void *) buffer, (void *) cbuf, len);;
temp = sd->bin;
prev = NULL;
start = temp;
while(temp) {
if(caller == temp->from) {
bin[n++] = temp;
if(prev) {
prev->next = temp->next;
} else {
start = temp->next;
}
temp = temp->next;
} else {
prev = temp;
temp = temp->next;
}
}
sd->bin = start;
} else { // No-op only PING currently
buffer = NULL;
}
}
wxeCommand::~wxeCommand() {
int n = 0;
if(buffer) {
while(bin[n]) {
if(bin[n]->bin)
driver_free_binary(bin[n]->bin);
driver_free(bin[n++]);
}
driver_free(buffer);
}
}
/* ****************************************************************************
* TreeItemData
* ****************************************************************************/
wxETreeItemData::wxETreeItemData(int sz, char * data) {
size = sz;
bin = (char *) driver_alloc(sz);
memcpy(bin, data, sz);
}
wxETreeItemData::~wxETreeItemData()
{
driver_free(bin);
}
/* ****************************************************************************
* CallbackData *
* ****************************************************************************/
wxeCallbackData::wxeCallbackData(ErlDrvTermData caller,void * req, char *req_type,
int funcb, int skip_ev, wxeErlTerm * userData)
: wxObject()
{
listener = caller;
obj = req;
fun_id = funcb;
strcpy(class_name, req_type);
skip = skip_ev;
user_data = userData;
}
wxeCallbackData::~wxeCallbackData() {
// fprintf(stderr, "CBD Deleteing %x %s\r\n", (unsigned int) this, class_name); fflush(stderr);
if(user_data) {
delete user_data;
}
}
/* ****************************************************************************
* wxListCtrlCompare wrapper
* ****************************************************************************/
int wxCALLBACK wxEListCtrlCompare(long item1, long item2, long callbackInfoPtr)
{
callbackInfo * cb = (callbackInfo *)callbackInfoPtr;
wxeMemEnv * memenv = ((WxeApp *) wxTheApp)->getMemEnv(cb->port);
wxeReturn rt = wxeReturn(WXE_DRV_PORT, memenv->owner, false);
rt.addInt(cb->callbackID);
rt.addInt(item1);
rt.addInt(item2);
rt.endList(2);
rt.addAtom("_wx_invoke_cb_");
rt.addTupleCount(3);
rt.send();
handle_event_callback(cb->port, memenv->owner);
if(((WxeApp *) wxTheApp)->cb_buff) {
int res = * (int*) ((WxeApp *) wxTheApp)->cb_buff;
driver_free(((WxeApp *) wxTheApp)->cb_buff);
((WxeApp *) wxTheApp)->cb_buff = NULL;
return res;
}
return 0;
}