File 1172-wx-Fix-callback-problems.patch of Package erlang

From 7604209d02278b5547ae42fb328bfbc7d9043963 Mon Sep 17 00:00:00 2001
From: Dan Gudmundsson <dgud@erlang.org>
Date: Fri, 15 Apr 2016 10:19:59 +0200
Subject: [PATCH 1/2] wx: Fix callback problems

Commands could be executed twice, if the command was dispatched
from a callback and caused a recursive invocation of command loop.
Solution is to mark op as -1 before calling wxWidgets.

Also commands could be missed when idle processing was done inside
while executing a recursive callback, solved be always resetting peak
index after idle processing is done.
---
 lib/wx/api_gen/wx_extra/wxEvtHandler.c_src |  2 +-
 lib/wx/api_gen/wx_gen_cpp.erl              | 12 +++---
 lib/wx/c_src/gen/wxe_funcs.cpp             | 14 ++++---
 lib/wx/c_src/wxe_helpers.cpp               |  4 +-
 lib/wx/c_src/wxe_helpers.h                 |  2 +-
 lib/wx/c_src/wxe_impl.cpp                  |  5 ++-
 lib/wx/test/wx_class_SUITE.erl             | 66 +++++++++++++++++++++++++++++-
 7 files changed, 87 insertions(+), 18 deletions(-)

diff --git a/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src b/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src
index 08fef1c..b9cb4f0 100644
--- a/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src
+++ b/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src
@@ -43,7 +43,7 @@ case 101: { // wxEvtHandler::Disconnect
   int eventType = wxeEventTypeFromAtom(bp); bp += *eventTypeLen;
   if(eventType > 0) {
     if(recurse_level > 1) {
-      delayed_delete->Append(Ecmd.Save());
+      delayed_delete->Append(Ecmd.Save(op));
     } else {
      bool Result = This->Disconnect((int) *winid,(int) *lastId,eventType,
                                     (wxObjectEventFunction)(wxEventFunction)
diff --git a/lib/wx/api_gen/wx_gen_cpp.erl b/lib/wx/api_gen/wx_gen_cpp.erl
index 07486e8..55c1791 100644
--- a/lib/wx/api_gen/wx_gen_cpp.erl
+++ b/lib/wx/api_gen/wx_gen_cpp.erl
@@ -195,11 +195,13 @@ gen_funcs(Defs) ->
 
     w("void WxeApp::wxe_dispatch(wxeCommand& Ecmd)~n{~n"),
     w(" char * bp = Ecmd.buffer;~n"),
+    w(" int op = Ecmd.op;~n"),
+    w(" Ecmd.op = -1;~n"),
     w(" wxeMemEnv *memenv = getMemEnv(Ecmd.port);~n"),
 %%    w(" wxMBConvUTF32 UTFconverter;~n"),
-    w("  wxeReturn rt = wxeReturn(WXE_DRV_PORT, Ecmd.caller, true);~n"),
+    w(" wxeReturn rt = wxeReturn(WXE_DRV_PORT, Ecmd.caller, true);~n"),
     w(" try {~n"),
-    w(" switch (Ecmd.op)~n{~n"),
+    w(" switch (op)~n{~n"),
 %%     w("  case WXE_CREATE_PORT:~n", []),
 %%     w("   { newMemEnv(Ecmd.port); } break;~n", []),
 %%     w("  case WXE_REMOVE_PORT:~n", []),
@@ -209,7 +211,7 @@ gen_funcs(Defs) ->
     w("     wxeRefData *refd = getRefData(This);~n"),
     w("     if(This && refd) {~n"),
     w("       if(recurse_level > 1 && refd->type != 4) {~n"),
-    w("          delayed_delete->Append(Ecmd.Save());~n"),
+    w("          delayed_delete->Append(Ecmd.Save(op));~n"),
     w("       } else {~n"),
     w("          delete_object(This, refd);~n"),
     w("          ((WxeApp *) wxTheApp)->clearPtr(This);}~n"),
@@ -228,7 +230,7 @@ gen_funcs(Defs) ->
     w("  default: {~n"),
     w("    wxeReturn error = wxeReturn(WXE_DRV_PORT, Ecmd.caller, false);"),
     w("    error.addAtom(\"_wxe_error_\");~n"),
-    w("    error.addInt((int) Ecmd.op);~n"),
+    w("    error.addInt((int) op);~n"),
     w("    error.addAtom(\"not_supported\");~n"),
     w("    error.addTupleCount(3);~n"),
     w("    error.send();~n"),
@@ -239,7 +241,7 @@ gen_funcs(Defs) ->
     w("} catch (wxe_badarg badarg) {  // try~n"),
     w("    wxeReturn error = wxeReturn(WXE_DRV_PORT, Ecmd.caller, false);"),
     w("    error.addAtom(\"_wxe_error_\");~n"),
-    w("    error.addInt((int) Ecmd.op);~n"),
+    w("    error.addInt((int) op);~n"),
     w("    error.addAtom(\"badarg\");~n"),
     w("    error.addInt((int) badarg.ref);~n"),
     w("    error.addTupleCount(2);~n"),
diff --git a/lib/wx/c_src/gen/wxe_funcs.cpp b/lib/wx/c_src/gen/wxe_funcs.cpp
index 059cee5..283e97f 100644
--- a/lib/wx/c_src/gen/wxe_funcs.cpp
+++ b/lib/wx/c_src/gen/wxe_funcs.cpp
@@ -40,17 +40,19 @@
 void WxeApp::wxe_dispatch(wxeCommand& Ecmd)
 {
  char * bp = Ecmd.buffer;
+ int op = Ecmd.op;
+ Ecmd.op = -1;
  wxeMemEnv *memenv = getMemEnv(Ecmd.port);
-  wxeReturn rt = wxeReturn(WXE_DRV_PORT, Ecmd.caller, true);
+ wxeReturn rt = wxeReturn(WXE_DRV_PORT, Ecmd.caller, true);
  try {
- switch (Ecmd.op)
+ switch (op)
 {
   case DESTROY_OBJECT: {
      void *This = getPtr(bp,memenv);
      wxeRefData *refd = getRefData(This);
      if(This && refd) {
        if(recurse_level > 1 && refd->type != 4) {
-          delayed_delete->Append(Ecmd.Save());
+          delayed_delete->Append(Ecmd.Save(op));
        } else {
           delete_object(This, refd);
           ((WxeApp *) wxTheApp)->clearPtr(This);}
@@ -114,7 +116,7 @@ case 101: { // wxEvtHandler::Disconnect
   int eventType = wxeEventTypeFromAtom(bp); bp += *eventTypeLen;
   if(eventType > 0) {
     if(recurse_level > 1) {
-      delayed_delete->Append(Ecmd.Save());
+      delayed_delete->Append(Ecmd.Save(op));
     } else {
      bool Result = This->Disconnect((int) *winid,(int) *lastId,eventType,
                                     (wxObjectEventFunction)(wxEventFunction)
@@ -32077,7 +32079,7 @@ case wxDCOverlay_Clear: { // wxDCOverlay::Clear
 }
   default: {
     wxeReturn error = wxeReturn(WXE_DRV_PORT, Ecmd.caller, false);    error.addAtom("_wxe_error_");
-    error.addInt((int) Ecmd.op);
+    error.addInt((int) op);
     error.addAtom("not_supported");
     error.addTupleCount(3);
     error.send();
@@ -32087,7 +32089,7 @@ case wxDCOverlay_Clear: { // wxDCOverlay::Clear
  rt.send();
 } catch (wxe_badarg badarg) {  // try
     wxeReturn error = wxeReturn(WXE_DRV_PORT, Ecmd.caller, false);    error.addAtom("_wxe_error_");
-    error.addInt((int) Ecmd.op);
+    error.addInt((int) op);
     error.addAtom("badarg");
     error.addInt((int) badarg.ref);
     error.addTupleCount(2);
diff --git a/lib/wx/c_src/wxe_helpers.cpp b/lib/wx/c_src/wxe_helpers.cpp
index 76958a3..4798e60 100644
--- a/lib/wx/c_src/wxe_helpers.cpp
+++ b/lib/wx/c_src/wxe_helpers.cpp
@@ -47,8 +47,8 @@ void wxeCommand::Delete()
     if(len > 64)
       driver_free(buffer);
     buffer = NULL;
-    op = -1;
   }
+  op = -1;
 }
 
 /* ****************************************************************************
@@ -226,7 +226,7 @@ unsigned int wxeFifo::Cleanup(unsigned int def)
     // Realloced we need to start from the beginning
     return 0;
   } else {
-    return def;
+    return def < cb_start? def : cb_start;
   }
 }
 
diff --git a/lib/wx/c_src/wxe_helpers.h b/lib/wx/c_src/wxe_helpers.h
index 3f66b6d..70ffccd 100644
--- a/lib/wx/c_src/wxe_helpers.h
+++ b/lib/wx/c_src/wxe_helpers.h
@@ -46,7 +46,7 @@ class wxeCommand
     wxeCommand();
     virtual ~wxeCommand(); // Use Delete()
 
-    wxeCommand * Save() { return this; };
+    wxeCommand * Save(int Op) { op = Op; return this; };
     void Delete();
 
     ErlDrvTermData   caller;
diff --git a/lib/wx/c_src/wxe_impl.cpp b/lib/wx/c_src/wxe_impl.cpp
index f899839..175bcfc 100644
--- a/lib/wx/c_src/wxe_impl.cpp
+++ b/lib/wx/c_src/wxe_impl.cpp
@@ -238,9 +238,10 @@ void WxeApp::dispatch_cmds()
   if(wxe_status != WXE_INITIATED)
     return;
   recurse_level++;
-  // fprintf(stderr, "\r\ndispatch_normal %d\r\n", level);fflush(stderr);
+  // fprintf(stderr, "\r\ndispatch_normal %d\r\n", recurse_level);fflush(stderr);
+  wxe_queue->cb_start = 0;
   dispatch(wxe_queue);
-  // fprintf(stderr, "\r\ndispatch_done \r\n");fflush(stderr);
+  // fprintf(stderr, "\r\ndispatch_done %d\r\n", recurse_level);fflush(stderr);
   recurse_level--;
 
   // Cleanup old memenv's and deleted objects
diff --git a/lib/wx/test/wx_class_SUITE.erl b/lib/wx/test/wx_class_SUITE.erl
index e23fd90..0e8e9f8 100644
--- a/lib/wx/test/wx_class_SUITE.erl
+++ b/lib/wx/test/wx_class_SUITE.erl
@@ -51,7 +51,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
 all() ->
     [calendarCtrl, treeCtrl, notebook, staticBoxSizer,
      clipboard, helpFrame, htmlWindow, listCtrlSort, listCtrlVirtual,
-     radioBox, systemSettings, taskBarIcon, toolbar, popup].
+     radioBox, systemSettings, taskBarIcon, toolbar, popup, modal].
 
 groups() ->
     [].
@@ -621,3 +621,67 @@ lang_env() ->
 format_env({match, List}) ->
     [io:format("  ~ts~n",[L]) || L <- List];
 format_env(nomatch) -> ok.
+
+%%  Add a testcase that tests that we can recurse in showModal
+%%  because it hangs in observer if object are not destroyed correctly
+%%  when popping the stack
+
+modal(Config) ->
+    Wx = wx:new(),
+    case {?wxMAJOR_VERSION, ?wxMINOR_VERSION, ?wxRELEASE_NUMBER} of
+	{2, Min, Rel} when Min < 8 orelse (Min =:= 8 andalso Rel < 11) ->
+	    {skip, "old wxWidgets version"};
+	_ ->
+	    Frame = wxFrame:new(Wx, -1, "Test Modal windows"),
+	    wxFrame:show(Frame),
+	    Env = wx:get_env(),
+	    Tester = self(),
+	    ets:new(test_state, [named_table, public]),
+	    Upd = wxUpdateUIEvent:getUpdateInterval(),
+	    wxUpdateUIEvent:setUpdateInterval(500),
+	    _Pid = spawn(fun() ->
+				 wx:set_env(Env),
+				 modal_dialog(Frame, 1, Tester)
+			 end),
+	    receive {dialog, M1, 1} -> timer:sleep(200), ets:insert(test_state, {M1, ready}) end,
+	    receive {dialog, M2, 2} -> timer:sleep(200), ets:insert(test_state, {M2, ready}) end,
+
+	    receive done -> ok end,
+	    receive {dialog_done, M2, 2} -> M2 end,
+	    receive {dialog_done, M1, 1} -> M1 end,
+
+	    wxUpdateUIEvent:setUpdateInterval(Upd),
+	    wx_test_lib:wx_destroy(Frame,Config)
+    end.
+
+modal_dialog(Parent, Level, Tester) when Level < 3 ->
+    M1 = wxTextEntryDialog:new(Parent, "Dialog " ++ integer_to_list(Level)),
+    io:format("Creating dialog ~p ~p~n",[Level, M1]),
+    wxDialog:connect(M1, show, [{callback, fun(#wx{event=Ev},_) ->
+						   case Ev of
+						       #wxShow{show=true} ->
+							   Tester ! {dialog, M1, Level};
+						       _ -> ignore
+						   end
+					   end}]),
+    DoOnce = fun(_,_) ->
+		     case ets:take(test_state, M1) of
+			 [] -> ignore;
+			 [_] -> modal_dialog(M1, Level+1, Tester)
+		     end
+	     end,
+    wxDialog:connect(M1, update_ui, [{callback, DoOnce}]),
+    ?wxID_OK = wxDialog:showModal(M1),
+    wxDialog:destroy(M1),
+    case Level > 1 of
+	true ->
+	    io:format("~p: End dialog ~p ~p~n",[?LINE, Level-1, Parent]),
+	    wxDialog:endModal(Parent, ?wxID_OK);
+	false -> ok
+    end,
+    Tester ! {dialog_done, M1, Level},
+    ok;
+modal_dialog(Parent, Level, Tester) ->
+    io:format("~p: End dialog ~p ~p~n",[?LINE, Level-1, Parent]),
+    wxDialog:endModal(Parent, ?wxID_OK),
+    Tester ! done.
-- 
2.1.4

openSUSE Build Service is sponsored by