aboutsummaryrefslogblamecommitdiffstats
path: root/lib/wx/c_src/wxe_impl.cpp
blob: e3dc4862f2352c0fdbe604c28ffa3a4597981359 (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                   
  
                                                        
  




                                                                      
  



                                                                         
  
                 



















                                                       
                   






                                      

                         
                         
 






                                        


                                   
                                                           





                                                                

                                                          

                                                                                       


                                                     
 









                                                     
   


                                           
                                           





                                                                    
     
                   








                                             
                                                        





                                                      

 


                                                                
 

                     



                                  
                 

                               
                               
 

                                                                                                      
 



                                                                                                             





                                                                            






                                                                                 
      
 

                              


                                         




                                     






                                                    
                                             


                 









                                                                                


                                                               
                               

                                                                
                                               

                   
          





                                                                   
                                                











                                                                          

 

                            
                                         
                  

                                                          
                  

                                           



















                                                                             
   

 

                                                        
             
                                                               


                                                                    
             















                                                                     
                                                                               

                                                                 
                          




                                                                                 



                                                            

                            
                                                       








                                                                              
                            

























                                                                                                                                                    
                                                     
                             
                                                                          
                                                                           
                                                                                
                                                                        
                                                                    
                                                        






                                   


                                                              

                               







                                                                        
                                              






                                                                                













                                                                              
                                     
                         
                      
               
                              
                    
                                                            



















                                                                                                                                                  


                                
                                            
                                               

                                 
                             

                              
                                                                                    
                                                   

 

                                                


                                   
                                         
 






                                              








                                                                              
             


                                              










                                                                 
             


                                                         
                  
                       
           



         
 

                                          
           
   
                                                                     



                                                                      
             



                                                   
                                    


















                                                                          
             








                                                                                         
                                
                                                     
                                                                   
                                                            


                                                         
                                                   
           
                           
                                                              




                                                                                
                        




                              
   






                                                                                    
                
                                


                                           

                                                    




                                                             
 






                          

                                                                            


















                                                                       
                                                             



                                                                                   
                      


                              
 








                                                                            
   



                                                       

 
 






                                      
                        







                                                                         
 
                                 










                                                                           
                                         

                                            
                                         





                                                                                              
                                                        
           
                                        





                                                                                             
                                                       


           
 






                                                     
               
                            
   


                                                               
        
                            
   



                                                                             
             












                                                                 
/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 2008-2014. 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"
#include "wxe_gl.h"

IMPLEMENT_APP_NO_MAIN(WxeApp)

DECLARE_APP(WxeApp)

DEFINE_EVENT_TYPE(wxeEVT_META_COMMAND)

#define WXE_NORMAL      0
#define WXE_CALLBACK    1
#define WXE_STORED      2

// Globals initiated in wxe_init.cpp
extern ErlDrvMutex *wxe_status_m;
extern ErlDrvCond  *wxe_status_c;
extern ErlDrvMutex * wxe_batch_locker_m;
extern ErlDrvCond  * wxe_batch_locker_c;
extern ErlDrvTermData  init_caller;
extern int wxe_status;

wxList * wxe_batch = NULL;
wxList * wxe_batch_cb_saved = NULL;
int wxe_batch_caller = 0;  // inside batch if larger than 0

/* ************************************************************
 *  Commands from erlang
 *    Called by emulator thread
 * ************************************************************/

void push_command(int op,char * buf,int len, wxe_data *sd)
{
  /* fprintf(stderr, "Op %d %d [%ld] %d\r\n", op, (int) driver_caller(sd->port_handle),
     wxe_batch->size(), wxe_batch_caller),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);
    }
    wxWakeUpIdle();
    erl_drv_mutex_unlock(wxe_batch_locker_m);
  } else {
    if(sd) {
      wxeMetaCommand Cmd(sd, what);
      wxTheApp->AddPendingEvent(Cmd);
    }
  }
}

void send_msg(const char * type, const 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();
}

/* ************************************************************
 *  Init WxeApp the application emulator
 * ************************************************************/

bool WxeApp::OnInit()
{

  global_me = new wxeMemEnv();
  wxe_batch = new wxList;
  wxe_batch_cb_saved = new wxList;
  cb_buff = NULL;
  recurse_level = 0;
  delayed_cleanup = new wxList;
  delayed_delete  = new wxList;

  wxe_ps_init2();
  // wxIdleEvent::SetMode(wxIDLE_PROCESS_SPECIFIED); // Hmm printpreview doesn't work in 2.9 with this

  Connect(wxID_ANY, wxEVT_IDLE,	(wxObjectEventFunction) (wxEventFunction) &WxeApp::idle);
  Connect(CREATE_PORT, wxeEVT_META_COMMAND,(wxObjectEventFunction) (wxEventFunction) &WxeApp::newMemEnv);
  Connect(DELETE_PORT, wxeEVT_META_COMMAND,(wxObjectEventFunction) (wxEventFunction) &WxeApp::destroyMemEnv);
  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();

#ifdef  _MACOSX
  /* Create a default MenuBar so that we can intercept the quit command */
  wxMenuBar *macMB = new wxMenuBar;
  wxMenuBar::MacSetCommonMenuBar(macMB);
  macMB->MacInstallMenuBar();
  macMB->Connect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED,
		 (wxObjectEventFunction) (wxEventFunction) &WxeApp::dummy_close);
#endif

  SetExitOnFrameDelete(false);

  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;
}


#ifdef  _MACOSX
void WxeApp::MacOpenFile(const wxString &filename) {
  send_msg("open_file", &filename);
}
#endif

void WxeApp::shutdown(wxeMetaCommand& Ecmd) {
  ExitMainLoop();
}

void WxeApp::dummy_close(wxEvent& Ev) {
  // fprintf(stderr, "Dummy Close invoked\r\n");
  // wxMac really wants a top level window which command-q quits if there are no
  // windows open, and this will kill the erlang, override default handling
}

// Called by wx thread
void WxeApp::idle(wxIdleEvent& event) {
  event.Skip(true);
  dispatch_cmds();
}

/* ************************************************************
 *  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;
  // Is thread safe if pdl have been incremented
  if(driver_monitor_process(port, process, &monitor) == 0) {
    // 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->recurse_level++;
    app->dispatch_cb(wxe_batch, wxe_batch_cb_saved, process);
    app->recurse_level--;
    //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);
  }
}

void WxeApp::dispatch_cmds()
{
  erl_drv_mutex_lock(wxe_batch_locker_m);
  recurse_level++;
  int level = dispatch(wxe_batch_cb_saved, 0, WXE_STORED);
  dispatch(wxe_batch, level, WXE_NORMAL);
  recurse_level--;
  wxe_batch_caller = 0;
  erl_drv_mutex_unlock(wxe_batch_locker_m);
  // Cleanup old memenv's and deleted objects
  if(recurse_level == 0) {
    if(delayed_delete->size() > 0)
      for( wxList::compatibility_iterator node = delayed_delete->GetFirst();
	   node;
	   node = delayed_delete->GetFirst()) {
	wxeCommand *event = (wxeCommand *)node->GetData();
	delayed_delete->Erase(node);
	wxe_dispatch(*event);
	event->Delete();
      }
    if(delayed_cleanup->size() > 0)
      for( wxList::compatibility_iterator node = delayed_cleanup->GetFirst();
	   node;
	   node = delayed_cleanup->GetFirst()) {
	wxeMetaCommand *event = (wxeMetaCommand *)node->GetData();
	delayed_cleanup->Erase(node);
	destroyMemEnv(*event);
	delete event;
      }
  }
}

// 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;
	    }
	    event->Delete();
	  }
      } 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
	     event->op == WXE_CB_DIED ||  // Event callback process died
	     // 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);
		}  // continue
	      case WXE_CB_DIED:
		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;
	      }
	      event->Delete();
	    } 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[Ecmd.port] = memenv;
  memenv->owner = Ecmd.caller;

  ErlDrvTermData rt[] = {ERL_DRV_ATOM, driver_mk_atom((char *)"wx_port_initiated")};
  erl_drv_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[Ecmd.port];

  if(!memenv) {
    wxString msg;
    msg.Printf(wxT("MemEnv already deleted"));
    send_msg("debug", &msg);
    return;
  }

  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 && 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);
	    }
	  }
	  if(recurse_level > 0) {
	    // Delay delete until we are out of dispatch*
	  } else {
	    delete win;
	  }
	}
      }
    }
  }

  if(recurse_level > 0) {
    delayed_cleanup->Append(Ecmd.Clone());
    return;
  }
  // 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;
	  bool cleanup_ref=true;
	  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 {
	    cleanup_ref = delete_object(ptr, refd);
	  }
	  if(cleanup_ref) {
	    // 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(ErlDrvTermData port) {
  return refmap[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);
}