1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2008-2017. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  */
20 
21 #include <stdio.h>
22 #include <signal.h>
23 
24 
25 #include <wx/wx.h>
26 
27 // Avoid including these in dcbuffer below
28 #include "wx/dcmemory.h"
29 #include "wx/dcclient.h"
30 #include "wx/window.h"
31 // Ok ugly but needed for wxBufferedDC crash workaround
32 #define private public
33 #include <wx/dcbuffer.h>
34 #undef private
35 
36 #include "wxe_impl.h"
37 #include "wxe_events.h"
38 #include "wxe_return.h"
39 #include "wxe_gl.h"
40 
41 IMPLEMENT_APP_NO_MAIN(WxeApp)
42 
43 DECLARE_APP(WxeApp)
44 
45 DEFINE_EVENT_TYPE(wxeEVT_META_COMMAND)
46 
47 #define WXE_NORMAL      0
48 #define WXE_CALLBACK    1
49 #define WXE_STORED      2
50 
51 // Globals initiated in wxe_init.cpp
52 extern ErlNifMutex *wxe_status_m;
53 extern ErlNifCond  *wxe_status_c;
54 extern ErlNifMutex * wxe_batch_locker_m;
55 extern ErlNifCond  * wxe_batch_locker_c;
56 extern ErlNifPid  init_caller;
57 extern int wxe_status;
58 
59 wxeFifo * wxe_queue = NULL;
60 
61 unsigned int wxe_idle_processed = 0;
62 unsigned int wxe_needs_signal = 0;  // inside batch if larger than 0
63 unsigned int wxe_needs_wakeup = 0;  // inside batch if larger than 0
64 
65 /* ************************************************************
66  *  Commands from erlang
67  *    Called by emulator thread
68  * ************************************************************/
push_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[],int op,wxe_me_ref * mp)69 void push_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[], int op, wxe_me_ref *mp)
70 {
71   ErlNifPid caller;
72 
73   if(!enif_self(env, &caller)) {
74     caller = ((wxeMemEnv *) mp->memenv)->owner;
75   }
76 
77   enif_mutex_lock(wxe_batch_locker_m);
78   int n = wxe_queue->Add(argc, argv, op, mp, caller);
79 
80   if(wxe_needs_signal) {
81     enif_cond_signal(wxe_batch_locker_c);
82     enif_mutex_unlock(wxe_batch_locker_m);
83   } else {
84     // wx-thread is waiting gui-events
85     int wakeup = wxe_needs_wakeup;
86     wxe_needs_wakeup = 0;
87     enif_mutex_unlock(wxe_batch_locker_m);
88     if(n < 2 || wakeup || WXE_DEBUG_PING) {
89       wxWakeUpIdle();
90     }
91   }
92 }
93 
meta_command(ErlNifEnv * env,int what,wxe_me_ref * mp)94 void meta_command(ErlNifEnv *env, int what, wxe_me_ref *mp) {
95   int status;
96   enif_mutex_lock(wxe_status_m);
97   status = wxe_status;
98   enif_cond_signal(wxe_status_c);
99   enif_mutex_unlock(wxe_status_m);
100 
101   if(status == WXE_INITIATED) {
102     ErlNifPid self;
103     enif_self(env, &self);
104     wxeMetaCommand Cmd(self, what, mp);
105     wxTheApp->AddPendingEvent(Cmd);
106   }
107 }
108 
send_msg(const char * type,const wxString * msg)109 void send_msg(const char * type, const wxString * msg) {
110   WxeApp * app = (WxeApp *) wxTheApp;
111   wxeReturn rt = wxeReturn(app->global_me, init_caller);
112   ERL_NIF_TERM emsg = enif_make_tuple3(rt.env,
113                                        rt.make_atom((char *) "wxe_driver"),
114                                        rt.make_atom((char *) type),
115                                        rt.make(msg));
116   rt.send(emsg);
117 }
118 
print_cmd(wxeCommand & event)119 void print_cmd(wxeCommand& event)
120 {
121   int i;
122   wxe_fns_t *func = &wxe_fns[event.op];
123   enif_fprintf(stderr, "  %T %d %s::%s(", event.caller, event.op, func->cname, func->fname);
124   for(i=0; i < event.argc-1; i++) {
125     enif_fprintf(stderr, "%T,", event.args[i]);
126   }
127   if(i > 0) {
128     enif_fprintf(stderr, "%T)\r\n", event.args[i]);
129   } else {
130     enif_fprintf(stderr, ")\r\n");
131   }
132 }
133 
134 /* ************************************************************
135  *  Init WxeApp the application emulator
136  * ************************************************************/
137 
OnInit()138 bool WxeApp::OnInit()
139 {
140 
141   global_me = new wxeMemEnv();
142   wxe_queue = new wxeFifo(2000);
143   cb_return = NULL;
144   recurse_level = 0;
145   delayed_delete = new wxeFifo(100);
146   delayed_cleanup  = new wxList;
147 
148   wxe_ps_init2();
149   wxIdleEvent::SetMode(wxIDLE_PROCESS_SPECIFIED);
150 
151   Connect(wxID_ANY, wxEVT_IDLE,	(wxObjectEventFunction) (wxEventFunction) &WxeApp::idle);
152   Connect(WXE_GET_CONSTS, wxeEVT_META_COMMAND,(wxObjectEventFunction) (wxEventFunction) &WxeApp::init_consts);
153   Connect(WXE_DELETE_ENV, wxeEVT_META_COMMAND,(wxObjectEventFunction) (wxEventFunction) &WxeApp::destroyMemEnv);
154   Connect(WXE_SHUTDOWN, wxeEVT_META_COMMAND,(wxObjectEventFunction) (wxEventFunction) &WxeApp::shutdown);
155 
156 //   fprintf(stderr, "Size void* %d: long %d long long %d int64 %d \r\n",
157 // 	  sizeof(void *), sizeof(long), sizeof(long long), sizeof(wxInt64));
158   initEventTable();
159   wxInitAllImageHandlers();
160 
161 #ifdef  _MACOSX
162   /* Create a default MenuBar so that we can intercept the quit command */
163   wxMenuBar *macMB = new wxMenuBar;
164   wxMenuBar::MacSetCommonMenuBar(macMB);
165   macMB->MacInstallMenuBar();
166   macMB->Connect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED,
167 		 (wxObjectEventFunction) (wxEventFunction) &WxeApp::dummy_close);
168 #endif
169 
170   SetExitOnFrameDelete(false);
171 
172   enif_mutex_lock(wxe_status_m);
173   wxe_status = WXE_INITIATED;
174   enif_cond_signal(wxe_status_c);
175   enif_mutex_unlock(wxe_status_m);
176   return TRUE;
177 }
178 
179 
180 #ifdef  _MACOSX
MacPrintFile(const wxString & filename)181 void WxeApp::MacPrintFile(const wxString &filename) {
182   send_msg("print_file", &filename);
183 }
184 
MacOpenFile(const wxString & filename)185 void WxeApp::MacOpenFile(const wxString &filename) {
186   send_msg("open_file", &filename);
187 }
188 
MacOpenURL(const wxString & url)189 void WxeApp::MacOpenURL(const wxString &url) {
190   send_msg("open_url", &url);
191 }
192 
MacNewFile()193 void WxeApp::MacNewFile() {
194   wxString empty;
195   send_msg("new_file", &empty);
196 }
197 
MacReopenApp()198 void WxeApp::MacReopenApp() {
199   wxString empty;
200   send_msg("reopen_app", &empty);
201 }
202 #endif
203 
shutdown(wxeMetaCommand & Ecmd)204 void WxeApp::shutdown(wxeMetaCommand& Ecmd) {
205   wxe_status = WXE_EXITING;
206   ExitMainLoop();
207   delete wxe_queue;
208 }
209 
dummy_close(wxEvent & Ev)210 void WxeApp::dummy_close(wxEvent& Ev) {
211   // fprintf(stderr, "Dummy Close invoked\r\n");
212   // wxMac really wants a top level window which command-q quits if there are no
213   // windows open, and this will kill the erlang, override default handling
214 }
215 
OnAssertFailure(const wxChar * file,int line,const wxChar * cfunc,const wxChar * cond,const wxChar * cmsgUser)216 void WxeApp::OnAssertFailure(const wxChar *file, int line, const wxChar *cfunc,
217 			    const wxChar *cond, const wxChar *cmsgUser) {
218   wxString msg;
219   wxString func(cfunc);
220   wxString msgUser(cmsgUser);
221 
222   msg.Printf(wxT("wxWidgets Assert failure: %s(%d): \"%s\""),
223 	     file, line, cond);
224   if ( !func.empty() ) {
225     msg << wxT(" in ") << func << wxT("()");
226   }
227   // and the message itself
228   if ( !msgUser.empty() ) {
229     msg << wxT(" : ") << msgUser;
230   }
231 
232   send_msg("error", &msg);
233 }
234 
235 // Called by wx thread
idle(wxIdleEvent & event)236 void WxeApp::idle(wxIdleEvent& event) {
237   event.Skip(true);
238   if(dispatch_cmds())
239     event.RequestMore();
240 }
241 
242 /* ************************************************************
243  *  Erlang Command execution  *
244  * ************************************************************/
245 
246 /* Callback from printer and event callbacks */
pre_callback()247 void pre_callback()
248 {
249   // no-op
250 }
251 
handle_event_callback(wxe_me_ref * mr,ErlNifPid process)252 void handle_event_callback(wxe_me_ref *mr, ErlNifPid process)
253 {
254   WxeApp * app = (WxeApp *) wxTheApp;
255   ErlNifMonitor monitor;
256 
257   if(wxe_status != WXE_INITIATED)
258     return;
259   // enif_fprintf(stderr, "CB EV start %T \r\n", process);
260   // Is thread safe if pdl have been incremented
261   if(mr->memenv && enif_monitor_process(NULL, mr, &process, &monitor) == 0) {
262     // Should we be able to handle commands when recursing? probably
263     app->cb_return = NULL;
264     app->recurse_level++;
265     app->dispatch_cb(wxe_queue, (wxeMemEnv *) mr->memenv, process);
266     app->recurse_level--;
267     enif_demonitor_process(NULL, mr, &monitor);
268   } else {
269     // enif_fprintf(stderr, "CB %T is not alive ignoring\r\n", process);
270     app->cb_return = NULL;
271   }
272   // enif_fprintf(stderr, "CB EV done %T \r\n", process);
273 }
274 
dispatch_cmds()275 int WxeApp::dispatch_cmds()
276 {
277   int more = 0;
278   if(wxe_status != WXE_INITIATED)
279     return more;
280   recurse_level++;
281   // fprintf(stderr, "\r\ndispatch_normal %d\r\n", recurse_level);fflush(stderr);
282   more = dispatch(wxe_queue);
283   // fprintf(stderr, "\r\ndispatch_done %d\r\n", recurse_level);fflush(stderr);
284   recurse_level--;
285 
286   // Cleanup old memenv's and deleted objects
287   if(recurse_level == 0) {
288     wxeCommand *curr;
289     while((curr = delayed_delete->Get()) != NULL) {
290       wxe_dispatch(*curr);
291       delayed_delete->DeleteCmd(curr);
292     }
293     // delayed_delete->Cleanup();
294     if(delayed_cleanup->size() > 0)
295       for( wxList::compatibility_iterator node = delayed_cleanup->GetFirst();
296 	   node;
297 	   node = delayed_cleanup->GetFirst()) {
298 	wxeMetaCommand *event = (wxeMetaCommand *)node->GetData();
299 	delayed_cleanup->Erase(node);
300 	destroyMemEnv(*event);
301 	delete event;
302       }
303   }
304   return more;
305 }
306 
307 #define CHECK_EVENTS 10000
308 
dispatch(wxeFifo * batch)309 int WxeApp::dispatch(wxeFifo * batch)
310 {
311   int ping = 0;
312   int blevel = 0;
313   int wait = 0; // Let event handling generate events sometime
314   wxeCommand *event;
315   enif_mutex_lock(wxe_batch_locker_m);
316   wxe_idle_processed = 1;
317   while(true) {
318     while((event = batch->Get()) != NULL) {
319       wait += 1;
320       switch(event->op) {
321       case WXE_BATCH_END:
322 	if(blevel>0) {
323           blevel--;
324           if(blevel==0)
325             wait += CHECK_EVENTS/4;
326         }
327 	break;
328       case WXE_BATCH_BEGIN:
329 	blevel++;
330 	break;
331       case WXE_DEBUG_PING:
332 	// When in debugger we don't want to hang waiting for a BATCH_END
333 	// that never comes, because a breakpoint have hit.
334 	ping++;
335 	if(ping > 2)
336 	  blevel = 0;
337 	break;
338       case WXE_CB_START:
339         // CB process died just ignore this
340         break;
341       case WXE_CB_RETURN:
342         if(enif_is_identical(event->args[0], WXE_ATOM_ok)) {
343           batch->DeleteCmd(event);
344         } else {
345           cb_return = event;   // must be deleted after taken care of
346         }
347         enif_mutex_unlock(wxe_batch_locker_m);
348 	return 1;
349       default:
350         enif_mutex_unlock(wxe_batch_locker_m);
351 	if(event->op < OPENGL_START) {
352 	  // fprintf(stderr, "  c %d (%d) \r\n", event->op, blevel);
353 	  wxe_dispatch(*event);
354 	} else {
355 	  gl_dispatch(event);
356 	}
357         enif_mutex_lock(wxe_batch_locker_m);
358 	break;
359       }
360       if(wait > CHECK_EVENTS) {
361         enif_mutex_unlock(wxe_batch_locker_m);
362         return 1; // Let wx check for events
363       }
364       batch->DeleteCmd(event);
365     }
366     if(blevel <= 0) {
367       enif_mutex_unlock(wxe_batch_locker_m);
368       return 0;
369     }
370     // sleep until something happens
371     // fprintf(stderr, "%s:%d sleep %d %d %d\r\n", __FILE__, __LINE__, batch->m_n, blevel, wait);fflush(stderr);
372     wxe_needs_signal = 1;
373     while(batch->m_q.empty()) {
374       enif_cond_wait(wxe_batch_locker_c, wxe_batch_locker_m);
375     }
376     wxe_needs_signal = 0;
377   }
378 }
379 
dispatch_cb(wxeFifo * batch,wxeMemEnv * memenv,ErlNifPid process)380 void WxeApp::dispatch_cb(wxeFifo * batch, wxeMemEnv * memenv, ErlNifPid process) {
381   wxeCommand *event;
382   unsigned int peek = 0;
383   enif_mutex_lock(wxe_batch_locker_m);
384   unsigned int i = 0;
385   unsigned int last = batch->m_q.size();
386   wxe_idle_processed = 0;
387   while(true) {
388 
389     while (i < last ) {
390       event = batch->m_q[i];
391       // enif_fprintf(stderr, "%d: CB %T owner %T  it %d %d (%d) \r\n",
392       //              recurse_level, event ? event->caller : process, process,
393       //              i, batch->Size(), batch->m_q.size());
394       if(event &&
395          (event->op == WXE_CB_START || // Event callback start change process
396           event->op == WXE_CB_DIED ||  // Event callback process died
397           event->op == WXE_DEBUG_PING ||
398           enif_compare_pids(&event->caller, &process) == 0 ||  // Callbacks from CB process only
399           // Allow connect_cb during CB i.e. msg from wxe_server.
400           (memenv && enif_compare_pids(&event->caller,&memenv->owner) == 0)
401           ))
402         {
403           // enif_fprintf(stderr, "Exec:"); print_cmd(*event);
404           batch->DelQueue(i);
405           switch(event->op) {
406           case WXE_BATCH_END:
407           case WXE_BATCH_BEGIN:
408           case WXE_DEBUG_PING:
409             break;
410           case WXE_CB_RETURN:
411             if(enif_is_identical(event->args[0], WXE_ATOM_ok)) {
412               batch->DeleteCmd(event);
413             } else {
414               cb_return = event;   // must be deleted after taken care of
415             }
416             wxe_needs_wakeup = 1;
417             enif_mutex_unlock(wxe_batch_locker_m);
418             return;
419           case WXE_CB_DIED:
420             cb_return = NULL;
421             batch->DeleteCmd(event);
422             wxe_needs_wakeup = 1;
423             enif_mutex_unlock(wxe_batch_locker_m);
424             return;
425           case WXE_CB_START:
426             // CB start from now accept message from CB process only
427             process = event->caller;
428             break;
429           default:
430             enif_mutex_unlock(wxe_batch_locker_m);
431             if(event->op < OPENGL_START) {
432               wxe_dispatch(*event);
433             } else {
434               gl_dispatch(event);
435             }
436             enif_mutex_lock(wxe_batch_locker_m);
437             last = batch->m_q.size();
438             if(wxe_idle_processed) {
439               // We have processed cmds inside dispatch()
440               // so the iterator may be wrong, restart from
441               // beginning of the queue
442               i = 0;
443             }
444             break;
445           }
446           batch->DeleteCmd(event);
447         } else {
448         // enif_fprintf(stderr, "Ignore:"); event ? print_cmd(*event) : fprintf(stderr, "NULL\r\n");
449       }
450       i++;
451     }
452     // sleep until something happens
453     // enif_fprintf(stderr, "\r\n%s:%d: %d: sleep sz %d (%d) it pos: %d\r\n", __FILE__, __LINE__, recurse_level,
454     //              batch->Size(), batch->m_q.size(), i); fflush(stderr);
455     wxe_needs_signal = 1;
456     peek = batch->Size();
457     while(peek >= batch->Size()) {
458       enif_cond_wait(wxe_batch_locker_c, wxe_batch_locker_m);
459     }
460     wxe_needs_signal = 0;
461     last = batch->m_q.size();
462   }
463 }
464 
465 
wxe_dispatch(wxeCommand & event)466 void WxeApp::wxe_dispatch(wxeCommand& event)
467 {
468   int op = event.op;
469   wxe_fns_t *func = &wxe_fns[op];
470   void (*nif_cb) (WxeApp *, wxeMemEnv *, wxeCommand& ) = func->nif_cb;
471   wxeMemEnv * memenv = (wxeMemEnv *) event.me_ref->memenv;
472   if(wxe_debug) {
473     print_cmd(event);
474   }
475   if (event.me_ref->memenv) {
476     if(nif_cb) {
477       try { nif_cb(this, memenv, event); }
478       catch (wxe_badarg badarg) {
479         wxeReturn rt = wxeReturn(memenv, event.caller, false);
480         ERL_NIF_TERM ba =
481           enif_make_tuple2(rt.env,
482                            WXE_ATOM_badarg,
483                            enif_make_string(rt.env, badarg.var, ERL_NIF_LATIN1));
484         ERL_NIF_TERM mfa = enif_make_tuple3(rt.env, enif_make_atom(rt.env, func->cname),
485                                             enif_make_atom(rt.env, func->fname),
486                                             rt.make_int(func->n));
487         rt.send(enif_make_tuple4(rt.env, WXE_ATOM_error, rt.make_int(op), mfa, ba));
488       }
489     } else {
490       wxeReturn rt = wxeReturn(memenv, event.caller, false);
491       ERL_NIF_TERM undef = enif_make_atom(rt.env, "undefined_function");
492       ERL_NIF_TERM mfa = enif_make_tuple3(rt.env, enif_make_atom(rt.env, func->cname),
493                                           enif_make_atom(rt.env, func->fname),
494                                           rt.make_int(func->n));
495       rt.send(enif_make_tuple4(rt.env, WXE_ATOM_error, rt.make_int(op), mfa, undef));
496     }
497   } else {
498     wxeReturn rt = wxeReturn(global_me, event.caller);
499     ERL_NIF_TERM unknown_env = enif_make_atom(rt.env, "unknown_env");
500     ERL_NIF_TERM mfa = enif_make_tuple3(rt.env, enif_make_atom(rt.env, func->cname),
501                                         enif_make_atom(rt.env, func->fname),
502                                         rt.make_int(func->n));
503     rt.send(enif_make_tuple4(rt.env, WXE_ATOM_error, rt.make_int(op), mfa, unknown_env));
504   }
505 }
506 
507 /* Memory handling */
508 
newMemEnv(ErlNifEnv * env,wxe_me_ref * mr)509 void * newMemEnv(ErlNifEnv* env, wxe_me_ref *mr)
510 {
511   WxeApp * app = (WxeApp *) wxTheApp;
512   wxeMemEnv* global_me = app->global_me;
513   wxeMemEnv* memenv = new wxeMemEnv();
514   memenv->create();
515 
516   for(int i = 0; i < global_me->next; i++) {
517     memenv->ref2ptr[i] = global_me->ref2ptr[i];
518   }
519   memenv->next = global_me->next;
520   enif_self(env, &memenv->owner);
521   memenv->me_ref = mr;
522   return memenv;
523 }
524 
destroyMemEnv(wxeMetaCommand & Ecmd)525 void WxeApp::destroyMemEnv(wxeMetaCommand &Ecmd)
526 {
527   // Clear incoming cmd queue first
528   dispatch_cmds();
529   enif_mutex_lock(wxe_batch_locker_m);
530   wxe_needs_wakeup = 1;
531   enif_mutex_unlock(wxe_batch_locker_m);
532 
533   wxWindow *parent = NULL;
534 
535   if(!Ecmd.me_ref || !Ecmd.me_ref->memenv) {
536     wxString msg;
537     msg.Printf(wxT("MemEnv already deleted"));
538     send_msg("debug", &msg);
539     return;
540   }
541   wxeMemEnv *memenv = (wxeMemEnv *) Ecmd.me_ref->memenv;
542 
543   if(wxe_debug) {
544     wxString msg;
545     msg.Printf(wxT("Destroying all memory "));
546     send_msg("debug", &msg);
547   }
548 
549   // pre-pass delete all dialogs first since they might crash erlang otherwise
550   for(int i=1; i < memenv->next; i++) {
551     wxObject * ptr = (wxObject *) memenv->ref2ptr[i];
552     if(ptr) {
553       ptrMap::iterator it = ptr2ref.find(ptr);
554       if(it != ptr2ref.end()) {
555 	wxeRefData *refd = it->second;
556 	if(refd->alloc_in_erl && refd->type == 2) {
557 	  wxDialog *win = (wxDialog *) ptr;
558 	  if(win->IsModal()) {
559 	    win->EndModal(-1);
560 	  }
561 	  parent = win->GetParent();
562 	  if(parent) {
563 	    ptrMap::iterator parentRef = ptr2ref.find(parent);
564 	    if(parentRef == ptr2ref.end()) {
565 	      // The parent is already dead delete the parent ref
566 	      win->SetParent(NULL);
567 	    }
568 	  }
569 	  if(recurse_level > 0) {
570 	    // Delay delete until we are out of dispatch*
571 	  } else {
572 	    delete win;
573 	  }
574 	}
575       }
576     }
577   }
578 
579   if(recurse_level > 0) {
580     delayed_cleanup->Append(Ecmd.Clone());
581     return;
582   }
583   // First pass, delete all top parents/windows of all linked objects
584   //   fprintf(stderr, "close port %x\r\n", Ecmd.port);fflush(stderr);
585 
586   for(int i=1; i < memenv->next; i++) {
587     void * ptr = memenv->ref2ptr[i];
588     if(ptr) {
589       ptrMap::iterator it = ptr2ref.find(ptr);
590       if(it != ptr2ref.end()) {
591 	wxeRefData *refd = it->second;
592 	if(refd->alloc_in_erl && refd->type == 0) {
593 	  parent = (wxWindow *) ptr;
594 	  // fprintf(stderr, "window %x %d\r\n", (int) parent, refd->ref);
595 	  while(parent->GetParent()) {
596 	    parent = parent->GetParent();
597 	    // fprintf(stderr, "  parent %x \r\n", (int) parent);
598 	  }
599 	  ptrMap::iterator pdata = ptr2ref.find(parent);
600 	  if(pdata != ptr2ref.end()) {
601 	    delete parent;
602 	  } // else parent is already deleted
603 	}
604       } else {
605 	// fprintf(stderr, "Error found no ref in %d => %x\r\n", i, ptr);
606       }
607     }
608   }
609   // Second pass delete everything else allocated
610   // everything linked from windows should now be deleted
611   for(int i=1; i < memenv->next; i++) {
612     void * ptr = memenv->ref2ptr[i];
613     if(ptr) {
614       ptrMap::iterator it = ptr2ref.find(ptr);
615       if(it != ptr2ref.end()) {
616 	wxeRefData *refd = it->second;
617 	if(refd->alloc_in_erl) {
618 	  if((refd->type == 8) && ((wxObject *)ptr)->IsKindOf(CLASSINFO(wxBufferedDC))) {
619 	    ((wxBufferedDC *)ptr)->m_dc = NULL; // Workaround
620 	  }
621 	  wxString msg;
622 	  bool cleanup_ref=true;
623 	  if(refd->type == 0) { // Maybe also class 1
624 	    wxClassInfo *cinfo = ((wxObject *)ptr)->GetClassInfo();
625 	    msg.Printf(wxT("Memory leak: {wx_ref, %d, %s}"),
626 		       refd->ref, cinfo->GetClassName());
627 	    send_msg("error", &msg);
628 	  } else if(refd->type != 4) {
629 	    cleanup_ref = delete_object(ptr, refd);
630 	  }
631 	  if(cleanup_ref) {
632 	    // Delete refs for leaks and non overridden allocs
633 	    delete refd;
634 	    ptr2ref.erase(it);
635 	  } // overridden allocs deletes meta-data in clearPtr
636 	} else { // Not alloced in erl just delete references
637 	  if(refd->ref >= global_me->next) { // if it is not part of global ptrs
638 	    delete refd;
639 	    ptr2ref.erase(it);
640 	  }
641 	}
642       }
643     }
644   }
645 // // Assert ?
646 // for(ptrMap::iterator it = ptr2ref.begin(); it != ptr2ref.end(); it++) {
647 //   wxeRefData *refd = it->second;
648 //   if(refd->ref >= global_me->next)
649 //     fprintf(stderr, "L %d %d %d\r\n", refd->ref, refd->type, refd->alloc_in_erl);
650 // }
651 //  fflush(stderr);
652   enif_free(memenv->ref2ptr);
653   enif_free_env(memenv->tmp_env);
654   if(wxe_debug) enif_fprintf(stderr, "Deleting memenv %d\r\n", memenv);
655   Ecmd.me_ref->memenv = NULL;
656   enif_release_resource(Ecmd.me_ref);
657 }
658 
659 
getRefData(void * ptr)660 wxeRefData * WxeApp::getRefData(void *ptr) {
661   ptrMap::iterator it = ptr2ref.find(ptr);
662   if(it != ptr2ref.end()) {
663     wxeRefData *refd = it->second;
664     return refd;
665   }
666   return NULL;
667 }
668 
669 
670 // wxeMemEnv * WxeApp::getMemEnv(ErlDrvTermData port) {
671 //   return refmap[port];
672 // }
673 
newPtr(void * ptr,int type,wxeMemEnv * memenv)674 int WxeApp::newPtr(void * ptr, int type, wxeMemEnv *memenv) {
675   int ref;
676   intList free = memenv->free;
677 
678   if(free.IsEmpty()) {
679     ref = memenv->next++;
680   } else {
681     ref = free.Pop();
682   };
683   if(ref >= memenv->max) {
684     memenv->max *= 2;
685     memenv->ref2ptr = (void **) enif_realloc(memenv->ref2ptr,memenv->max * sizeof(void*));
686   }
687   memenv->ref2ptr[ref] = ptr;
688 
689   if(wxe_debug) {
690     wxString msg;
691     const wxChar *class_info = wxT("unknown");
692     if(type < 10) {
693       wxClassInfo *cinfo = ((wxObject *)ptr)->GetClassInfo();
694       class_info = cinfo->GetClassName();
695     }
696     msg.Printf(wxT("Creating {wx_ref, %d, %s} at %p "), ref, class_info, ptr);
697     send_msg("debug", &msg);
698   }
699 
700   ptr2ref[ptr] = new wxeRefData(ref, type, true, memenv);
701   // fprintf(stderr, "ptr %x id %d\r\n", (int) ptr,ref);
702   return ref;
703 }
704 
getRef(void * ptr,wxeMemEnv * memenv,int type)705 int WxeApp::getRef(void * ptr, wxeMemEnv *memenv, int type) {
706   if(!ptr) return 0;  // NULL and zero is the same
707   ptrMap::iterator it = ptr2ref.find(ptr);
708   if(it != ptr2ref.end()) {
709     wxeRefData *refd = it->second;
710     if(refd->memenv == memenv || refd->memenv == global_me) {
711       // Found it return
712       return refd->ref;
713     } // else
714     // Old reference to deleted object, release old and recreate in current memenv.
715     ptr2ref.erase(it);
716   }
717   int ref;
718   intList free = memenv->free;
719 
720   if(free.IsEmpty()) {
721     ref = memenv->next++;
722   } else {
723     ref = free.Pop();
724   };
725   if(ref >= memenv->max) {
726     memenv->max *= 2;
727     memenv->ref2ptr = (void **) enif_realloc(memenv->ref2ptr,memenv->max * sizeof(void*));
728   }
729 
730   memenv->ref2ptr[ref] = ptr;
731   ptr2ref[ptr] = new wxeRefData(ref, type, false, memenv);
732   return ref;
733 }
734 
735 
clearPtr(void * ptr)736 void WxeApp::clearPtr(void * ptr) {
737   ptrMap::iterator it;
738   it = ptr2ref.find(ptr);
739 
740   if(it != ptr2ref.end()) {
741     wxeRefData *refd = it->second;
742     intList free = refd->memenv->free;
743     int ref = refd->ref;
744     refd->memenv->ref2ptr[ref] = NULL;
745     free.Append(ref);
746 
747     if(!enif_is_pid_undefined(&(refd->pid))) {
748       // Send terminate pid to owner
749       wxeReturn rt = wxeReturn(refd->memenv,refd->pid, false);
750       rt.send(enif_make_tuple2(rt.env,
751                                rt.make_atom("_wxe_destroy_"),
752                                enif_make_pid(rt.env, &refd->pid)));
753       enif_set_pid_undefined(&(refd->pid));
754     };
755     if(refd->type == 1 && ((wxObject*)ptr)->IsKindOf(CLASSINFO(wxSizer))) {
756       wxSizerItemList list = ((wxSizer*)ptr)->GetChildren();
757       for(wxSizerItemList::compatibility_iterator node = list.GetFirst();
758 	  node; node = node->GetNext()) {
759 	wxSizerItem *item = node->GetData();
760 	wxObject *content=NULL;
761 	if((content = item->GetWindow()))
762 	  if(ptr2ref.end() == ptr2ref.find(content)) {
763 	    wxString msg;
764 	    wxClassInfo *cinfo = ((wxObject *)ptr)->GetClassInfo();
765 	    msg.Printf(wxT("Double usage detected of window at %p in sizer {wx_ref, %d, %s}"),
766 		       content, ref, cinfo->GetClassName());
767 	    send_msg("error", &msg);
768 	    ((wxSizer*)ptr)->Detach((wxWindow*)content);
769 	  }
770 	if((content = item->GetSizer()))
771 	  if(ptr2ref.end() == ptr2ref.find(content)) {
772 	    wxString msg;
773 	    wxClassInfo *cinfo = ((wxObject *)ptr)->GetClassInfo();
774 	    msg.Printf(wxT("Double usage detected of sizer at %p in sizer {wx_ref, %d, %s}"),
775 		       content, ref, cinfo->GetClassName());
776 	    send_msg("error", &msg);
777 	    ((wxSizer*)ptr)->Detach((wxSizer*)content);
778 	  }
779       }
780     }
781 
782     delete refd;
783     ptr2ref.erase(it);
784   }
785 }
786 
787 
registerPid(int index,ErlNifPid pid,wxeMemEnv * memenv)788 int WxeApp::registerPid(int index, ErlNifPid pid, wxeMemEnv * memenv) {
789   void * temp = memenv->ref2ptr[index];
790   if((index < memenv->next) && ((index == 0) || (temp != (void *) NULL))) {
791     ptrMap::iterator it;
792     it = ptr2ref.find(temp);
793     if(it != ptr2ref.end()) {
794       wxeRefData *refd = it->second;
795       refd->pid = pid;
796       return 1;
797     }
798   };
799   return 0;
800 }
801