Skip to content

Commit

Permalink
remove blocking eventloop. (#1882)
Browse files Browse the repository at this point in the history
  • Loading branch information
GillesDuvert authored Sep 13, 2024
1 parent fb0caa0 commit 61cbc2a
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 101 deletions.
42 changes: 7 additions & 35 deletions src/gdlwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -633,8 +633,7 @@ WidgetListT GDLWidget::widgetList;
wxImageList *gdlDefaultTreeStateImages;
wxImageList *gdlDefaultTreeImages;

GDLEventQueue GDLWidget::BlockingEventQueue; // the event queue in which all widget events are versed in case of blocking (XMANAGER)
GDLEventQueue GDLWidget::InteractiveEventQueue; // event queue used when no blocking is made -- part of the main GDLEventHandler()
GDLEventQueue GDLWidget::widgetEventQueue; // event queue used by the main GDLEventHandler()
bool GDLWidget::wxIsOn=false;
bool GDLWidget::handlersOk=false;
wxFont GDLWidget::defaultFont=wxNullFont; //the font defined by widget_control,default_font.
Expand Down Expand Up @@ -1252,8 +1251,7 @@ void GDLWidget::SendWidgetTimerEvent(int millisecs) {
}

void GDLWidget::ClearEvents() {
InteractiveEventQueue.Purge(this->GetWidgetID());
BlockingEventQueue.Purge(this->GetWidgetID());
widgetEventQueue.Purge(this->GetWidgetID());
}

void GDLWidget::HandleUnblockedWidgetEvents()
Expand All @@ -1262,7 +1260,7 @@ void GDLWidget::HandleUnblockedWidgetEvents()
CallWXEventLoop();
//treat our GDL events...
DStructGDL* ev = NULL;
while( (ev = GDLWidget::InteractiveEventQueue.Pop()) != NULL)
while( (ev = GDLWidget::widgetEventQueue.Pop()) != NULL)
{
ev = CallEventHandler( ev );

Expand All @@ -1277,22 +1275,7 @@ void GDLWidget::HandleUnblockedWidgetEvents()
}

void GDLWidget::PushEvent( WidgetIDT baseWidgetID, DStructGDL* ev) {
// Get XmanagerActiveCommand status
GDLWidget *baseWidget = GDLWidget::GetWidget( baseWidgetID );
if ( baseWidget != NULL ) {
bool interactive = baseWidget->IsUsingInteractiveEventLoop( );
if ( interactive ) { //non-Blocking: events in InteractiveEventQueue.
#ifdef GDL_DEBUG_WIDGETS
wxMessageOutputStderr().Printf(_T("InteractiveEventQueue.PushEvent: %d\n"),baseWidgetID);
#endif
InteractiveEventQueue.PushBack( ev );
} else { //blocking: events in BlockingEventQueue.
#ifdef GDL_DEBUG_WIDGETS
wxMessageOutputStderr().Printf(_T("BlockingEventQueue.PushEvent: %d\n"),baseWidgetID);
#endif
BlockingEventQueue.PushBack( ev );
}
} else cerr << "NULL baseWidget (possibly Destroyed?) found in GDLWidget::PushEvent( WidgetIDT baseWidgetID=" << baseWidgetID << ", DStructGDL* ev=" << ev << "), please report!\n";
widgetEventQueue.PushBack( ev );
}

void GDLWidget::InformAuthorities(const std::string& message){
Expand All @@ -1302,7 +1285,7 @@ void GDLWidget::InformAuthorities(const std::string& message){
ev->InitTag( "TOP", DLongGDL( 0 ) );
ev->InitTag( "HANDLER", DLongGDL( 0 ) );
ev->InitTag( "MESSAGE", DStringGDL(message) );
InteractiveEventQueue.PushFront( ev ); // push front (will be handled next)
widgetEventQueue.PushFront( ev ); // push front (will be handled next)
}
//return false if already blocked by XManager (one managed realized top Widget is not marked as interactive).
bool GDLWidget::IsXmanagerBlocking()
Expand Down Expand Up @@ -1488,8 +1471,7 @@ void GDLWidget::UnInit() {
if (wxIsStarted()) {
ResetWidgets();
//clear all events --- otherwise baoum!)
InteractiveEventQueue.Purge();
BlockingEventQueue.Purge();
widgetEventQueue.Purge();
// the following cannot be done: once unitialized, the wxWidgets library cannot be safely initilized again.: wxUninitialize( );
UnsetWxStarted(); //reset handlersOk too.
}
Expand Down Expand Up @@ -2542,17 +2524,7 @@ GDLWidgetTopBase::~GDLWidgetTopBase() {
ev->InitTag("ID", DLongGDL(widgetID));
ev->InitTag("TOP", DLongGDL(widgetID));
ev->InitTag("HANDLER", DLongGDL(0));
if (this->IsUsingInteractiveEventLoop()) {
#ifdef GDL_DEBUG_WIDGETS
wxMessageOutputStderr().Printf(_T("~GDLWidgetTopBase InteractiveEventQueue.Push: %d\n"),widgetID);
#endif
InteractiveEventQueue.PushFront(ev); // push front (will be handled next)
} else {
#ifdef GDL_DEBUG_WIDGETS
wxMessageOutputStderr().Printf(_T("~GDLWidgetTopBase BlockingEventQueue.Push: %d\n"),widgetID);
#endif
BlockingEventQueue.PushFront(ev); // push front (will be handled next)
}
widgetEventQueue.PushFront(ev); // push front (will be handled next)
}
/*********************************************************/
// Context Menu pseudo-base
Expand Down
2 changes: 1 addition & 1 deletion src/gdlwidget.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ class GDLWidget
static wxFont defaultFont;
static wxFont systemFont;
static GDLEventQueue BlockingEventQueue;
static GDLEventQueue InteractiveEventQueue;
static GDLEventQueue widgetEventQueue;
static void PushEvent( WidgetIDT baseWidgetID, DStructGDL* ev);
static void InformAuthorities(const std::string& message);

Expand Down
102 changes: 43 additions & 59 deletions src/widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2501,74 +2501,58 @@ BaseGDL* widget_info( EnvT* e ) {
int infinity = (nowait) ? 0 : 1;
DStructGDL* ev;

do { // outer while loop, will run once if NOWAIT
do { // outer while loop, will run once if NOWAIT
while (1) { //inner loop, catch controlC, default return if no event trapped in nowait mode
GDLWidget::CallWXEventLoop();
if (!all) {
//specific widget(s)
// we cannot check only readlineEventQueue thinking our XMANAGER in blocking state looks to ALL widgets.
// because XMANAGER may have been called AFTER events are created.
while ((ev = GDLWidget::BlockingEventQueue.Pop()) != NULL) { // get event
static int idIx = ev->Desc()->TagIndex("ID");
id = (*static_cast<DLongGDL*> (ev->GetTag(idIx, 0)))[0]; // get its id
for (SizeT i = 0; i < widgetIDList.size(); i++) { //is ID corresponding to any widget in list?
if (widgetIDList.at(i) == id) { //if yes
ev = CallEventHandler(ev); //process it recursively (going up hierarchy) in eventHandler. Should block waiting for xmanager.
if (ev == NULL) return defaultRes;
else return ev;
}
}
// blocking eventqueue should drain (just Pop() ) when unconcerned interactive events must be preserved
// to be executed normally when widget_event returns (so, pusehd back in InteractiveEventQueue).
}
while ((ev = GDLWidget::InteractiveEventQueue.Pop()) != NULL) { // get event
static int idIx = ev->Desc()->TagIndex("ID");
id = (*static_cast<DLongGDL*> (ev->GetTag(idIx, 0)))[0]; // get its id
for (SizeT i = 0; i < widgetIDList.size(); i++) { //is ID corresponding to any widget in list?
if (widgetIDList.at(i) == id) { //if yes
GDLWidget::CallWXEventLoop();
if (!all) {
//specific widget(s)
while ((ev = GDLWidget::widgetEventQueue.Pop()) != NULL) { // get event
static int idIx = ev->Desc()->TagIndex("ID");
id = (*static_cast<DLongGDL*> (ev->GetTag(idIx, 0)))[0]; // get its id
for (SizeT i = 0; i < widgetIDList.size(); i++) { //is ID corresponding to any widget in list?
if (widgetIDList.at(i) == id) { //if yes
//IMPORTANT: return ev immediately. This is what permits #1685: an event trapped by WIDGET_EVENT does not behave
//like the same event processed in the evenloop.
return ev;
}
}
GDLWidget::InteractiveEventQueue.PushBack(ev);
return ev;
}
}
GDLWidget::widgetEventQueue.PushBack(ev);
GDLWidget::CallWXEventLoop();
// avoid looping like crazy
// avoid looping like crazy
#ifdef _WIN32
Sleep(10); // this just to quiet down the character input from readline. 2 was not enough. 20 was ok.
Sleep(10); // this just to quiet down the character input from readline. 2 was not enough. 20 was ok.
#else
const long SLEEP = 10000000; // 10ms
struct timespec delay;
delay.tv_sec = 0;
delay.tv_nsec = SLEEP; // 20ms
nanosleep(&delay, NULL);
const long SLEEP = 10000000; // 10ms
struct timespec delay;
delay.tv_sec = 0;
delay.tv_nsec = SLEEP; // 20ms
nanosleep(&delay, NULL);
#endif
}
} else {
//wait for ALL . This is the case of /XMANAGER_BLOCK for example. Both queues may be active, some widgets being managed other not.
if ((ev = GDLWidget::BlockingEventQueue.Pop()) != NULL) goto endwait;
if ((ev = GDLWidget::InteractiveEventQueue.Pop()) != NULL) goto endwait;
}
if (nowait) return defaultRes;
if (sigControlC) return defaultRes;
} //end inner loop
//here we got a real event, process it, walking back the hierachy (in CallEventHandler()) for modified ev in case of function handlers.
endwait:
if (blockedByXmanager && ev->Desc( )->Name( ) == "*TOPLEVEL_DESTROYED*" ) {
// deleted widgets list are hopefully handled internally by xmanager
GDLDelete(ev);
return defaultRes;
}
ev = CallEventHandler(ev); //process it recursively (going up hierarchy) in eventHandler. Should block waiting for xmanager.
// examine return:
if (ev == NULL) { //swallowed by a procedure or non-event-stucture returning function
if (nowait) return defaultRes; //else will loop again
} else { // untreated or modified by a function
return ev;
}
}
} else {
//wait for ALL . This is the case of /XMANAGER_BLOCK for example.
if ((ev = GDLWidget::widgetEventQueue.Pop()) != NULL) goto endwait;
}
if (nowait) return defaultRes;
if (sigControlC) return defaultRes;
} //end inner loop
//here we got a real event, process it, walking back the hierachy (in CallEventHandler()) for modified ev in case of function handlers.
endwait:
if (blockedByXmanager && ev->Desc()->Name() == "*TOPLEVEL_DESTROYED*") {
// deleted widgets list are hopefully handled internally by xmanager
GDLDelete(ev);
return defaultRes;
}
ev = CallEventHandler(ev); //process it recursively (going up hierarchy) in eventHandler. Should block waiting for xmanager.
// examine return:
if (ev == NULL) { //swallowed by a procedure or non-event-stucture returning function
if (nowait) return defaultRes; //else will loop again
} else { // untreated or modified by a function
return ev;
}
GDLWidget::CallWXEventLoop();

} while (infinity);
} while (infinity);
return NULL; //pacifier.
#endif //HAVE_LIBWXWIDGETS
}
Expand Down
37 changes: 31 additions & 6 deletions testsuite/interactive_tests/test_widgets.pro
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,16 @@ pro list_event,event
toto=["A","list","created","with","WIDGET_LIST","YSIZE=3"]
print,toto[event.index]
end

; called by XMANAGER : main eventloop
pro handle_Event,ev
common mycount,count
common pixmaps,green_bmp,red_bmp
help,ev,/str
common forprogressbar,progressbar,pbarid,pbarpos

; avoid to report timer events
if tag_names(ev, /structure_name) ne 'WIDGET_TIMER' then help,ev,/str

if tag_names(ev, /structure_name) eq 'WIDGET_KILL_REQUEST' then begin
acceptance=dialog_message(dialog_parent=ev.id,"I Do want to close the window", /CANCEL, /DEFAULT_NO,/QUESTION) ; +strtrim(ev.id,2))
if acceptance eq "Yes" then begin
Expand Down Expand Up @@ -230,7 +236,7 @@ pro handle_Event,ev
return
endif

print,"uvalue.type=",uv.type
if uv.type ne 'timer' then print,"uvalue.type=",uv.type
case uv.type of
'file': begin
widget_control,ev.id,get_value=value ;& print,value
Expand Down Expand Up @@ -262,6 +268,9 @@ pro handle_Event,ev
if val eq 'ON' then begin
widget_control,ev.id,set_value = 'OFF'
endif else begin
vv=widget_info(/xmanager)
widget_control,ev.id,set_value = "MANAGED: "+strtrim(vv,2)
wait,1
widget_control,ev.id,set_value = 'ON'
endelse
end
Expand All @@ -275,6 +284,14 @@ pro handle_Event,ev
widget_control,ev.id,set_value = green_bmp, set_uvalue= val
endelse
end
'timer': begin
wset,pbarid
pbarpos += 5
pbarpos MOD= 300
ERASE, 255
POLYFILL, pbarpos+[0,0,5,5], [0, 19, 19,0], /DEVICE, color=140
WIDGET_CONTROL, ev.id, TIMER=.2
end
'quit': widget_control,ev.top,/DESTROY
else: begin
print, "(other, not treated, event: ok)"
Expand Down Expand Up @@ -321,7 +338,8 @@ siz= widget_button(menu,VALUE="Resize (error)",EVENT_PRO="resize_gui") ; 5
pro test_widgets,table,help=help,nocanvas=nocanvas,notree=notree,block=block,fontname=fontname,present=present,select=select,_extra=extra
common mycount,count
common pixmaps,green_bmp,red_bmp

common forprogressbar,progressbar,pbarid,pbarpos

green_bmp= bytarr(7,7,3)& green_bmp[*,*,1] = 255& & green_bmp[0,0,1] = 254
red_bmp= bytarr(7,7,3)& red_bmp[*,*,0] = 255& & red_bmp[0,0,0] = 254
if (n_elements(select) gt 0) then present=select
Expand Down Expand Up @@ -364,7 +382,9 @@ ev = {vEv,type:'',pos:[0,0]}
base = WIDGET_BASE(/col,MBAR=mbar,title=title,event_pro='base_event_nok',kill_notify='cleanup',/tlb_kill_request_events,/tlb_size_events) ; ---> PROBLEM: ,/tlb_size_events) ;,/scroll)
doMbar,mbar,fontname
;mysize=widget_info(base,string_size='012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234')

; add a progress bar to test timer
progressbar = WIDGET_DRAW(base, XSIZE=300, YSIZE=20, UVALUE={vEv,'timer',[0,0]})
pbarpos=0
; define a tabbed base that contains everything:
label=widget_label(base,value='to best mimic IDL`s widgets, call GDL ',/align_left)
label=widget_label(base,value='with option "--widget-compat" ',/align_left)
Expand Down Expand Up @@ -738,7 +758,9 @@ widget_control,base,notify_realize='i_am_realized'
;Realize the widgets.
WIDGET_CONTROL, /REALIZE, base

;;Obtain the window index.
;;Obtain the window index.
WIDGET_CONTROL, progressbar, GET_VALUE = pbarId

if ~keyword_set(nocanvas) and total(strcmp('DRAW',present,/fold)) then begin
print,"Draw widgets:",draw,draw2
WIDGET_CONTROL, draw, GET_VALUE = index
Expand Down Expand Up @@ -767,5 +789,8 @@ print,"Draw widgets:",draw,draw2
tv,image,10,10,/data; ,/true
endif

xmanager,"handle",base,cleanup="cleanup_xmanager",no_block=~block
; create a timer event --- otherwise will not be active and thus not catched in eventloop
widget_control,progressbar,timer=0

xmanager,"handle",base,cleanup="cleanup_xmanager";,no_block=~block
end

0 comments on commit 61cbc2a

Please sign in to comment.