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