Skip to content
This repository was archived by the owner on Aug 31, 2021. It is now read-only.

Commit 61a6903

Browse files
Add the ability to exit modal loops on script errors
Currently only implemented for the Linux drag-and-drop loop.
1 parent 32be954 commit 61a6903

File tree

5 files changed

+87
-2
lines changed

5 files changed

+87
-2
lines changed

engine/src/debug.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,14 +299,20 @@ void MCB_trace(MCExecContext &ctxt, uint2 line, uint2 pos)
299299

300300
void MCB_break(MCExecContext &ctxt, uint2 line, uint2 pos)
301301
{
302-
MCB_prepmessage(ctxt, MCM_trace_break, line, pos, 0);
302+
// We hit a breakpoint - end all modal loops
303+
MCscreen->breakModalLoops();
304+
305+
MCB_prepmessage(ctxt, MCM_trace_break, line, pos, 0);
303306
}
304307

305308
bool s_in_trace_error = false;
306309

307310
bool MCB_error(MCExecContext &ctxt, uint2 line, uint2 pos, uint2 id)
308311
{
309-
// OK-2009-03-25: [[Bug 7517]] - The crash described in this bug report is probably caused by a stack overflow. This overflow is due to
312+
// An unhandled error has been thrown - end all modal loops
313+
MCscreen->breakModalLoops();
314+
315+
// OK-2009-03-25: [[Bug 7517]] - The crash described in this bug report is probably caused by a stack overflow. This overflow is due to
310316
// errors being thrown in the IDE (or in this case GLX2) component of the debugger. This should prevent traceError from recursing.
311317
if (s_in_trace_error)
312318
return false;

engine/src/linux.stubs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ gdk libgdk-x11-2.0.so.0
239239
gdk_drag_context_list_targets: (pointer) -> (pointer)
240240
gdk_drag_status: (pointer, integer, integer) -> ()
241241
gdk_drop_finish: (pointer, integer, integer) -> ()
242+
gdk_drag_abort: (pointer, integer) -> ()
242243
gdk_window_set_cursor: (pointer, pointer) -> ()
243244
gdk_drag_find_window_for_screen: (pointer, pointer, pointer, integer, integer, pointer, pointer) -> ()
244245
gdk_drag_motion: (pointer, pointer, integer, integer, integer, integer, integer, integer) -> (integer)

engine/src/lnxdnd.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,19 @@ void set_dnd_cursor(GdkWindow *w, bool p_okay, MCImage *p_image)
7676
}
7777

7878

79+
struct dnd_modal_loop_context
80+
{
81+
GdkDragContext* drag_context;
82+
GdkDisplay* display;
83+
};
84+
85+
static void break_dnd_modal_loop(void* context)
86+
{
87+
dnd_modal_loop_context* t_context = (dnd_modal_loop_context*)context;
88+
gdk_drag_abort(t_context->drag_context, GDK_CURRENT_TIME);
89+
gdk_display_pointer_ungrab(t_context->display, GDK_CURRENT_TIME);
90+
}
91+
7992
// SN-2014-07-11: [[ Bug 12769 ]] Update the signature - the non-implemented UIDC dodragdrop was called otherwise
8093
MCDragAction MCScreenDC::dodragdrop(Window w, MCPasteboard *p_pasteboard, MCDragActionSet p_allowed_actions, MCImage *p_image, const MCPoint* p_image_offset)
8194
{
@@ -141,10 +154,22 @@ MCDragAction MCScreenDC::dodragdrop(Window w, MCPasteboard *p_pasteboard, MCDrag
141154
// Whether the target accepted the drop or not
142155
bool t_accepted = true;
143156

157+
// Context for breaking out of the modal loop, if required
158+
dnd_modal_loop_context t_loop_context;
159+
modal_loop t_modal_loop;
160+
t_loop_context.drag_context = t_context;
161+
t_loop_context.display = dpy;
162+
t_modal_loop.break_function = break_dnd_modal_loop;
163+
t_modal_loop.context = &t_loop_context;
164+
modalLoopStart(t_modal_loop);
165+
144166
// The drag-and-drop loop
145167
bool t_dnd_done = false;
146168
while (!t_dnd_done)
147169
{
170+
if (t_modal_loop.broken)
171+
break;
172+
148173
// Run the GLib event loop to exhaustion
149174
while (g_main_context_iteration(NULL, FALSE))
150175
;
@@ -350,6 +375,8 @@ MCDragAction MCScreenDC::dodragdrop(Window w, MCPasteboard *p_pasteboard, MCDrag
350375
siguser();
351376
}
352377

378+
modalLoopEnd();
379+
353380
// Other people can now use the pointer
354381
g_object_unref(t_context);
355382
gdk_display_pointer_ungrab(dpy, GDK_CURRENT_TIME);

engine/src/uidc.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ MCUIDC::MCUIDC()
247247

248248
// IM-2014-03-06: [[ revBrowserCEF ]] List of callback functions to call during wait()
249249
m_runloop_actions = nil;
250+
251+
m_modal_loops = NULL;
250252
}
251253

252254
MCUIDC::~MCUIDC()
@@ -1937,3 +1939,26 @@ void MCUIDC::hidecursoruntilmousemoves(void)
19371939
// Default action is to do nothing - Mac overrides and performs the
19381940
// appropriate function.
19391941
}
1942+
1943+
void MCUIDC::breakModalLoops()
1944+
{
1945+
modal_loop* loop = m_modal_loops;
1946+
while (loop != NULL)
1947+
{
1948+
loop->break_function(loop->context);
1949+
loop->broken = true;
1950+
loop = loop->chain;
1951+
}
1952+
}
1953+
1954+
void MCUIDC::modalLoopStart(modal_loop& info)
1955+
{
1956+
info.chain = m_modal_loops;
1957+
info.broken = false;
1958+
m_modal_loops = &info;
1959+
}
1960+
1961+
void MCUIDC::modalLoopEnd()
1962+
{
1963+
m_modal_loops = m_modal_loops->chain;
1964+
}

engine/src/uidc.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,17 @@ enum MCPlatformFeature
255255

256256
class MCUIDC
257257
{
258+
public:
259+
260+
typedef void (*modal_break_callback_fn)(void *);
261+
struct modal_loop
262+
{
263+
modal_break_callback_fn break_function;
264+
void* context;
265+
modal_loop* chain;
266+
bool broken;
267+
};
268+
258269
protected:
259270
MCMessageList *messages;
260271
MCMovingList *moving;
@@ -278,6 +289,8 @@ class MCUIDC
278289
uint2 bluebits;
279290
const char * m_sound_internal ;
280291

292+
modal_loop* m_modal_loops;
293+
281294
// IM-2014-01-24: [[ HiDPI ]] Cache displays array returned from platform-specific methods
282295
static MCDisplay *s_displays;
283296
static uint4 s_display_count;
@@ -459,6 +472,19 @@ class MCUIDC
459472
virtual void addmessage(MCObject *optr, MCNameRef name, real8 time, MCParameter *params);
460473
virtual void delaymessage(MCObject *optr, MCNameRef name, MCStringRef p1 = nil, MCStringRef p2 = nil);
461474

475+
// When called, all modal loops should be exited and control should return
476+
// to the main event loop. The intended use of this method is to prevent UI
477+
// lockups when a script error occurs during a modal loop (e.g during
478+
// the drag-and-drop loop)
479+
void breakModalLoops();
480+
481+
// Indicates that a modal event loop is being entered. A callback function
482+
// should be passed in order to allow the breaking of the loop.
483+
void modalLoopStart(modal_loop& info);
484+
485+
// Indicates that the innermost modal loop is being exited
486+
void modalLoopEnd();
487+
462488
// Wait for at most 'duration' seconds. If 'dispatch' is true then event
463489
// dispatch will occur. If 'anyevent' is true then the call will return
464490
// as soon as something notable happens. If an abort/quit occurs while the

0 commit comments

Comments
 (0)