GtkEvent.cpp

Zbigniew Rebacz, 01/20/2014 08:31 PM

Download (15.3 KB)

 
1
#include <CtrlCore/CtrlCore.h>
2

    
3
#ifdef GUI_GTK
4

    
5
NAMESPACE_UPP
6

    
7
#define LLOG(x)    // DLOG(rmsecs() << ' ' << x)
8
//_DBG_ #define LOG_EVENTS
9

    
10
bool  Ctrl::EventMouseValid;
11
Point Ctrl::EventMousePos;
12
guint Ctrl::EventState;
13

    
14
BiVector<Ctrl::Event> Ctrl::Events;
15

    
16
Point         Ctrl::CurrentMousePos;
17
guint         Ctrl::CurrentState;
18
guint32       Ctrl::CurrentTime;
19
Ctrl::Event   Ctrl::CurrentEvent;
20

    
21
bool  GetShift() { return Ctrl::CurrentState & GDK_SHIFT_MASK; }
22
bool  GetCtrl() { return Ctrl::CurrentState & GDK_CONTROL_MASK ; }
23
bool  GetAlt() { return Ctrl::CurrentState & GDK_MOD1_MASK; }
24
bool  GetCapsLock() { return Ctrl::CurrentState & GDK_LOCK_MASK; }
25
bool  GetMouseLeft() { return Ctrl::CurrentState & GDK_BUTTON1_MASK; }
26
bool  GetMouseRight() { return Ctrl::CurrentState & GDK_BUTTON3_MASK; }
27
bool  GetMouseMiddle() { return Ctrl::CurrentState & GDK_BUTTON2_MASK; }
28
Point GetMousePos() { return Ctrl::CurrentMousePos; }
29

    
30
#ifdef LOG_EVENTS
31
Tuple2<int, const char *> xEvent[] = {
32
        { GDK_NOTHING, "GDK_NOTHING" },
33
        { GDK_DELETE, "GDK_DELETE" },
34
        { GDK_DESTROY, "GDK_DESTROY" },
35
        { GDK_EXPOSE, "GDK_EXPOSE" },
36
        { GDK_MOTION_NOTIFY, "GDK_MOTION_NOTIFY" },
37
        { GDK_BUTTON_PRESS, "GDK_BUTTON_PRESS" },
38
        { GDK_2BUTTON_PRESS, "GDK_2BUTTON_PRESS" },
39
        { GDK_3BUTTON_PRESS, "GDK_3BUTTON_PRESS" },
40
        { GDK_BUTTON_RELEASE, "GDK_BUTTON_RELEASE" },
41
        { GDK_KEY_PRESS, "GDK_KEY_PRESS" },
42
        { GDK_KEY_RELEASE, "GDK_KEY_RELEASE" },
43
        { GDK_ENTER_NOTIFY, "GDK_ENTER_NOTIFY" },
44
        { GDK_LEAVE_NOTIFY, "GDK_LEAVE_NOTIFY" },
45
        { GDK_FOCUS_CHANGE, "GDK_FOCUS_CHANGE" },
46
        { GDK_CONFIGURE, "GDK_CONFIGURE" },
47
        { GDK_MAP, "GDK_MAP" },
48
        { GDK_UNMAP, "GDK_UNMAP" },
49
        { GDK_PROPERTY_NOTIFY, "GDK_PROPERTY_NOTIFY" },
50
        { GDK_SELECTION_CLEAR, "GDK_SELECTION_CLEAR" },
51
        { GDK_SELECTION_REQUEST, "GDK_SELECTION_REQUEST" },
52
        { GDK_SELECTION_NOTIFY, "GDK_SELECTION_NOTIFY" },
53
        { GDK_PROXIMITY_IN, "GDK_PROXIMITY_IN" },
54
        { GDK_PROXIMITY_OUT, "GDK_PROXIMITY_OUT" },
55
        { GDK_DRAG_ENTER, "GDK_DRAG_ENTER" },
56
        { GDK_DRAG_LEAVE, "GDK_DRAG_LEAVE" },
57
        { GDK_DRAG_MOTION, "GDK_DRAG_MOTION" },
58
        { GDK_DRAG_STATUS, "GDK_DRAG_STATUS" },
59
        { GDK_DROP_START, "GDK_DROP_START" },
60
        { GDK_DROP_FINISHED, "GDK_DROP_FINISHED" },
61
        { GDK_CLIENT_EVENT, "GDK_CLIENT_EVENT" },
62
        { GDK_VISIBILITY_NOTIFY, "GDK_VISIBILITY_NOTIFY" },
63
        { GDK_NO_EXPOSE, "GDK_NO_EXPOSE" },
64
        { GDK_SCROLL, "GDK_SCROLL" },
65
        { GDK_WINDOW_STATE, "GDK_WINDOW_STATE" },
66
        { GDK_SETTING, "GDK_SETTING" },
67
        { GDK_OWNER_CHANGE, "GDK_OWNER_CHANGE" },
68
        { GDK_GRAB_BROKEN, "GDK_GRAB_BROKEN" },
69
        { GDK_DAMAGE, "GDK_DAMAGE" },
70
        { GDK_EVENT_LAST, "GDK_EVENT_LAST" },
71
};
72
#endif
73

    
74
Ctrl *Ctrl::GetTopCtrlFromId(int id)
75
{
76
        int q = FindId(id);
77
        if(q >= 0) {
78
                Ctrl *p = wins[q].ctrl;
79
                if(p && p->top)
80
                        return p;
81
        }
82
        return NULL;
83
}
84

    
85
gboolean Ctrl::GtkEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data)
86
{
87
        GuiLock __;
88
        GdkEventKey *key;
89
        bool pressed = false;
90
        bool  retval = true;
91
        Value value;
92
        Ctrl *p = GetTopCtrlFromId(user_data);
93
#ifdef LOG_EVENTS
94
        String ev = "?";
95
        Tuple2<int, const char *> *f = FindTuple(xEvent, __countof(xEvent), event->type);
96
        if(f)
97
                ev = f->b;
98
        LOG(rmsecs() << " FETCH EVENT " << ev << " ctrl: " << Name(p));
99
#endif
100

    
101
        switch(event->type) {
102
        case GDK_EXPOSE:
103
        case GDK_DAMAGE:
104
                if(p) {
105
#ifdef LOG_EVENTS
106
                        TimeStop tm;
107
#endif
108
                        p->fullrefresh = false;
109
                        GdkEventExpose *e = (GdkEventExpose *)event;
110
                        SystemDraw w(gdk_cairo_create(p->gdk()));
111
                        painting = true;
112
                        Rect r = RectC(e->area.x, e->area.y, e->area.width, e->area.height);
113
                        w.Clip(r);
114
                        p->UpdateArea(w, r);
115
                        w.End();
116
                        cairo_destroy(w);
117
                        if(p->top->dr)
118
                                DrawDragRect(*p, *p->top->dr);
119
                        painting = false;
120
#ifdef LOG_EVENTS
121
                        LOG("* " << ev << " elapsed " << tm);
122
#endif                        
123
                }
124
                return true;
125
        case GDK_DELETE:
126
                break;
127
        case GDK_FOCUS_CHANGE:
128
                if(p) {
129
                        if(((GdkEventFocus *)event)->in)
130
                                gtk_im_context_focus_in(p->top->im_context);
131
                        else
132
                                gtk_im_context_focus_in(p->top->im_context);
133
                        AddEvent(user_data, EVENT_NONE, value);
134
                }
135
                return false;
136
        case GDK_LEAVE_NOTIFY:
137
                EventMouseValid = false;
138
        case GDK_MOTION_NOTIFY: {
139
                GdkEventMotion *e = (GdkEventMotion *)event;
140
                DoMouseEvent(e->state, Point((int)e->x_root, (int)e->y_root));
141
                break;
142
        }
143
        case GDK_BUTTON_PRESS:
144
                value = DoButtonEvent(event, true);
145
                if(IsNull(value))
146
                        return false;
147
                break;
148
        case GDK_2BUTTON_PRESS:
149
                value = DoButtonEvent(event, true);
150
                if(IsNull(value))
151
                        return false;
152
                break;
153
        case GDK_BUTTON_RELEASE:
154
                value = DoButtonEvent(event, false);
155
                if(IsNull(value))
156
                        return false;
157
                break;
158
        case GDK_SCROLL: {
159
                GdkEventScroll *e = (GdkEventScroll *)event;
160
                value = findarg(e->direction, GDK_SCROLL_UP, GDK_SCROLL_LEFT) < 0 ? -120 : 120;
161
                DoMouseEvent(e->state, Point((int)e->x_root, (int)e->y_root));
162
                break;
163
        }
164
        case GDK_KEY_PRESS:
165
                pressed = true;
166
        case GDK_KEY_RELEASE:
167
                key = (GdkEventKey *)event;
168
                EventState = key->state;
169
                value = (int) key->keyval;
170
                if(pressed) {
171
                        p = GetTopCtrlFromId(user_data);
172
                        if(p && gtk_im_context_filter_keypress(p->top->im_context, key))
173
                                return true;
174
                }
175
                break;
176
        case GDK_CONFIGURE: {
177
                retval = false;
178
                GdkEventConfigure *e = (GdkEventConfigure *)event;
179
                value = RectC(e->x, e->y, e->width, e->height);
180
                LLOG("GDK_CONFIGURE " << value);
181
                break;
182
        }
183
        default:
184
                return false;
185
        }
186
        AddEvent(user_data, event->type, value);
187
        return retval;
188
}
189

    
190
void Ctrl::DoMouseEvent(int state, Point pos)
191
{
192
        EventMousePos = pos;
193
        EventState = state;
194
        EventMouseValid = true;
195
}
196

    
197
int Ctrl::DoButtonEvent(GdkEvent *event, bool press)
198
{
199
        GdkEventButton *e = (GdkEventButton *)event;
200
        static int mask[] = { GDK_BUTTON1_MASK, GDK_BUTTON2_MASK, GDK_BUTTON3_MASK };
201
        if(e->button >= 1 && e->button <= 3) {
202
                int m = mask[e->button - 1];
203
                int state = e->state;
204
                if(press) // gtk seems to provide state "before" the event, so buttons are wrong
205
                        state |= m;
206
                else
207
                        state &= ~m;
208
                DoMouseEvent(state, Point((int)e->x_root, (int)e->y_root));
209
                return e->button;
210
        }
211
        return Null;
212
}
213

    
214
Ctrl::Event::Event()
215
{
216
        event = NULL;
217
}
218

    
219
void Ctrl::Event::Free()
220
{
221
        if(event) {
222
                gdk_event_free(event);
223
                event = NULL;
224
        }
225
}
226

    
227
void Ctrl::Event::Set(const Event& e)
228
{
229
        *(Event0 *)this = e;
230
        event = gdk_event_copy(e.event);
231
}
232

    
233
Ctrl::Event::~Event()
234
{
235
        Free();
236
}
237

    
238
Ctrl::Event::Event(const Event& e)
239
{
240
        Set(e);
241
}
242

    
243
void Ctrl::Event::operator=(const Event& e)
244
{
245
        if(this == &e)
246
                return;
247
        Free();
248
        Set(e);
249
}
250

    
251
void Ctrl::AddEvent(gpointer user_data, int type, const Value& value)
252
{
253
        if(Events.GetCount() > 50000)
254
                return;
255
        Event& e = Events.AddTail();
256
        e.time = gtk_get_current_event_time();
257
        e.windowid = (uint32)(uintptr_t)user_data;
258
        e.type = type;
259
        e.value = value;
260
        if(!EventMouseValid) {
261
                gint x, y;
262
                GdkModifierType mod;
263
                gdk_window_get_pointer(gdk_get_default_root_window(), &x, &y, &mod);
264
                EventState = mod;
265
                EventMousePos = Point(x, y);
266
                EventMouseValid = true;
267
        }
268
        e.mousepos = EventMousePos;
269
        e.state = EventState;
270
        e.count = 1;
271
        e.event = gtk_get_current_event();
272
}
273

    
274
void Ctrl::IMCommit(GtkIMContext *context, gchar *str, gpointer user_data)
275
{
276
        GuiLock __;
277
        AddEvent(user_data, EVENT_TEXT, FromUtf8(str));
278
}
279

    
280
void Ctrl::FetchEvents(bool may_block)
281
{
282
        LLOG("FetchEvents " << may_block);
283
        int level = LeaveGuiMutexAll();
284
        while(g_main_context_iteration(NULL, may_block))
285
                may_block = false;
286
        EnterGuiMutex(level);
287
}
288

    
289
bool Ctrl::IsWaitingEvent0(bool fetch)
290
{
291
        if(fetch)
292
                FetchEvents(FALSE);
293
        return Events.GetCount();
294
}
295

    
296
bool Ctrl::IsWaitingEvent()
297
{
298
        return IsWaitingEvent0(true);
299
}
300

    
301
struct ProcStop {
302
        TimeStop tm;
303
        String   ev;
304
        
305
        ~ProcStop() { LOG("* " << ev << " elapsed " << tm); }
306
};
307

    
308
bool Ctrl::DispatchMouseIn(int act, int zd)
309
{
310
        Point p = GetMousePos();
311
        Rect r = GetScreenRect();
312
        if(r.Contains(p)) {
313
                p -= r.TopLeft();
314
                DispatchMouse(act, p, zd);
315
                return true;
316
        }
317
        return false;
318
}
319

    
320
void Ctrl::GtkMouseEvent(int action, int act, int zd)
321
{
322
        if(grabpopup && activePopup.GetCount()) {
323
                for(int i = activePopup.GetCount(); --i >= 0;)
324
                        if(activePopup[i] && activePopup[i]->DispatchMouseIn(act, zd))
325
                                return;
326
                if(action == DOWN) { // Deactivate active popup(s) if clicked outside of active popups
327
                        IgnoreMouseUp();
328
                        if(activePopup.Top())
329
                                activePopup.Top()->GetTopWindow()->ActivateWnd();
330
                }
331
                else
332
                if(activePopup[0]) { // Redirect other events to TopWindow that spawned first popup
333
                        Ptr<TopWindow> w = activePopup[0]->GetTopWindow();
334
                        if(w)
335
                                w->DispatchMouseIn(act, zd);
336
                }
337
                return;
338
        }
339
        DispatchMouse(act, GetMousePos() - GetScreenRect().TopLeft(), zd);
340
}
341

    
342
void Ctrl::GtkButtonEvent(int action)
343
{
344
        int act = action;
345
        int button = CurrentEvent.value;
346
        if(action != MOUSEMOVE)
347
                act |= button == 2 ? MIDDLE : button == 3 ? RIGHT : LEFT;
348
        GtkMouseEvent(action, act, 0);
349
}
350

    
351
void Ctrl::Proc()
352
{
353
#ifdef LOG_EVENTS
354
        String ev = "?";
355
        Tuple2<int, const char *> *f = FindTuple(xEvent, __countof(xEvent), CurrentEvent.type);
356
        if(f)
357
                ev = f->b;
358
        LOG(rmsecs() << " PROCESS EVENT " << Upp::Name(this) << " " << ev);
359
        ProcStop tm;
360
        tm.ev = ev;
361
#endif
362
        if(!top)
363
                return;
364
        Ptr<Ctrl> _this = this;
365
        bool pressed = false;
366
        int  kv;
367
        static int clicktime = msecs() - 100000;
368
        switch(CurrentEvent.type) {
369
        case GDK_MOTION_NOTIFY: {
370
                GtkMouseEvent(MOUSEMOVE, MOUSEMOVE, 0);
371
                DoCursorShape();
372
                break;
373
        }
374
        case GDK_BUTTON_PRESS:
375
                if(!HasWndFocus() && !popup)
376
                        SetWndFocus();
377
                ClickActivateWnd();
378
                if(ignoremouseup) {
379
                        KillRepeat();
380
                        ignoreclick = false;
381
                        ignoremouseup = false;
382
                }
383
                if(!ignoreclick)
384
                        GtkButtonEvent(msecs(clicktime) < 250 ? DOUBLE : DOWN);
385
                clicktime = msecs();
386
                break;
387
/*        case GDK_2BUTTON_PRESS:
388
                if(!ignoreclick)
389
                        GtkButtonEvent(DOUBLE);
390
                break;
391
*/        case GDK_BUTTON_RELEASE:
392
                if(ignoreclick)
393
                        EndIgnore();
394
                else
395
                        GtkButtonEvent(UP);
396
                break;
397
        case GDK_SCROLL: {
398
                GtkMouseEvent(MOUSEWHEEL, MOUSEWHEEL, CurrentEvent.value);
399
                break;
400
        }
401
        case GDK_KEY_PRESS:
402
                pressed = true;
403
        case GDK_KEY_RELEASE:
404
                kv = CurrentEvent.value;
405
                if(kv >= 0 && kv < 65536) {
406
                        LLOG("keyval " << FormatIntHex(kv) << ' ' << (char)kv);
407
                        if(kv >= 'a' && kv <= 'z')
408
                                kv = kv - 'a' + 'A';
409
                        static Tuple2<int, int> cv[] = {
410
                                { GDKEY(BackSpace), K_BACKSPACE },
411
                                { GDKEY(Tab), K_TAB },
412
                                { GDKEY(ISO_Left_Tab), K_TAB },
413
                                { GDKEY(Return), K_ENTER },
414
                                { GDKEY(Escape), K_ESCAPE },
415
                                { GDKEY(space), K_SPACE },
416
                                { GDKEY(Control_L), K_CTRL_KEY },
417
                                { GDKEY(Control_R), K_CTRL_KEY },
418
                                { GDKEY(Shift_L), K_SHIFT_KEY },
419
                                { GDKEY(Shift_R), K_SHIFT_KEY },
420
                                { GDKEY(Alt_L), K_ALT_KEY },
421
                                { GDKEY(Alt_R), K_ALT_KEY },
422

    
423
                                { GDKEY(KP_Space), K_SPACE },
424
                                { GDKEY(KP_Tab), K_TAB },
425
                                { GDKEY(KP_Enter), K_ENTER },
426
                                { GDKEY(KP_F1), K_F1 },
427
                                { GDKEY(KP_F2), K_F2 },
428
                                { GDKEY(KP_F3), K_F3 },
429
                                { GDKEY(KP_F4), K_F4 },
430
                                { GDKEY(KP_Home), K_HOME },
431
                                { GDKEY(KP_Left), K_LEFT },
432
                                { GDKEY(KP_Up), K_UP },
433
                                { GDKEY(KP_Right), K_RIGHT },
434
                                { GDKEY(KP_Down), K_DOWN },
435
                                { GDKEY(KP_Page_Up), K_PAGEUP },
436
                                { GDKEY(KP_Page_Down), K_PAGEDOWN },
437
                                { GDKEY(KP_End), K_END },
438
                                { GDKEY(KP_Begin), K_HOME },
439
                                { GDKEY(KP_Insert), K_INSERT },
440
                                { GDKEY(KP_Delete), K_DELETE },
441
                        };
442
                        Tuple2<int, int> *x = FindTuple(cv, __countof(cv), kv);
443
                        if(x)
444
                                kv = x->b;
445
                        else
446
                                kv += K_DELTA;
447
                        if(GetShift() && kv != K_SHIFT_KEY)
448
                                kv |= K_SHIFT;
449
                        if(GetCtrl() && kv != K_CTRL_KEY)
450
                                kv |= K_CTRL;
451
                        if(GetAlt() && kv != K_ALT_KEY)
452
                                kv |= K_ALT;
453
                        LLOG(GetKeyDesc(kv) << ", pressed: " << pressed << ", count: " << CurrentEvent.count);
454
#ifdef GDK_WINDOWING_X11
455
                        if(pressed)
456
                                for(int i = 0; i < hotkey.GetCount(); i++) {
457
                                        if(hotkey[i] && keyhot[i] == (dword)kv) {
458
                                                hotkey[i]();
459
                                                return;
460
                                        }
461
                                }
462
#endif
463
                        DispatchKey(!pressed * K_KEYUP + kv, CurrentEvent.count);
464
                }
465
                break;
466
        case EVENT_TEXT: {
467
                WString h = CurrentEvent.value;
468
                for(int i = 0; i < h.GetCount(); i++) // TODO: Add compression
469
                        DispatchKey(h[i], 1);
470
                break;
471
        }
472
        case GDK_DELETE: {
473
                TopWindow *w = dynamic_cast<TopWindow *>(this);
474
                if(w) {
475
                        if(IsEnabled()) {
476
                                IgnoreMouseUp();
477
                                w->WhenClose();
478
                        }
479
                }
480
                return;
481
        }
482
        case GDK_CONFIGURE: {
483
                        Rect rect = CurrentEvent.value;
484
                        if(GetRect() != rect)
485
                                SetWndRect(rect);
486
                }
487
                break;
488
        default:
489
                return;
490
        }
491
        if(_this)
492
                _this->PostInput();
493
}
494

    
495

    
496
bool Ctrl::ProcessEvent0(bool *quit, bool fetch)
497
{
498
        ASSERT(IsMainThread());
499
        bool r = false;
500
        if(IsWaitingEvent0(fetch)) {
501
                while(Events.GetCount() > 1) { // Event compression (coalesce autorepeat, mouse moves/wheel, configure)
502
                        Event& a = Events[0];
503
                        Event& b = Events[1];
504
                        if(b.type == a.type && a.windowid == b.windowid && a.state == b.state) {
505
                                if(a.type == GDK_KEY_PRESS && a.value == b.value)
506
                                        b.count += a.count;
507
                            else
508
                            if(a.type == GDK_SCROLL)
509
                                b.value = (int)b.value + (int)a.value;
510
                            else
511
                            if(findarg(a.type, GDK_MOTION_NOTIFY, GDK_CONFIGURE) < 0)
512
                                break;
513
                                Events.DropHead();
514
                        }
515
                        else
516
                                break;
517
                }
518
                Event& e = Events.Head();
519
                CurrentTime = e.time;
520
                CurrentMousePos = e.mousepos;
521
                CurrentState = e.state;
522
                CurrentEvent = e;
523
                Value val = e.value;
524
                Events.DropHead();
525
                Ctrl *w = GetTopCtrlFromId(e.windowid);
526
                FocusSync();
527
                CaptureSync();
528
                if(w)
529
                        w->Proc();
530
                r = true;
531
        }
532
        if(quit)
533
                *quit = IsEndSession();
534
        FocusSync();
535
        SyncCaret();
536
        return r;
537
}
538

    
539
bool Ctrl::ProcessEvent(bool *quit)
540
{
541
        return ProcessEvent0(quit, true);
542
}
543

    
544
gboolean Ctrl::TimeHandler(GtkWidget *)
545
{ // we only need timer to periodically break blocking g_main_context_iteration
546
        return true;
547
}
548

    
549
void SweepMkImageCache();
550

    
551
bool Ctrl::ProcessEvents0(bool *quit, bool fetch)
552
{
553
        bool r = false;
554
        while(IsWaitingEvent0(fetch) && (!LoopCtrl || LoopCtrl->InLoop()))
555
                r = ProcessEvent0(quit, fetch) || r;
556
        TimerProc(GetTickCount());
557
        AnimateCaret();
558
        if(quit)
559
                *quit = IsEndSession();
560
        for(int i = 0; i < wins.GetCount(); i++)
561
                if(wins[i].ctrl)
562
                        wins[i].ctrl->SyncScroll();
563
        gdk_window_process_all_updates();
564
        FetchEvents(FALSE); // To perform any pending GDK_EXPOSE
565
        gdk_flush();
566
        return r;
567
}
568

    
569
bool Ctrl::ProcessEvents(bool *quit)
570
{
571
        return ProcessEvents0(quit, true);
572
}
573

    
574
void Ctrl::SysEndLoop()
575
{
576
}
577

    
578
void WakeUpGuiThread()
579
{
580
        g_main_context_wakeup(g_main_context_default());
581
}
582

    
583
void Ctrl::EventLoop(Ctrl *ctrl)
584
{
585
        GuiLock __;
586
        ASSERT_(IsMainThread(), "Event loop can only run in the main thread");
587
        ASSERT(LoopLevel == 0 || ctrl);
588
        LoopLevel++;
589
        LLOG("Entering event loop at level " << LoopLevel << LOG_BEGIN);
590
        if(!GetMouseRight() && !GetMouseMiddle() && !GetMouseLeft())
591
                ReleaseCtrlCapture();
592
        Ptr<Ctrl> ploop;
593
        if(ctrl) {
594
                ploop = LoopCtrl;
595
                LoopCtrl = ctrl;
596
                ctrl->inloop = true;
597
        }
598

    
599
        while(!IsEndSession() &&
600
              (ctrl ? ctrl->IsOpen() && ctrl->InLoop() : GetTopCtrls().GetCount())) {
601
                FetchEvents(TRUE);
602
                ProcessEvents();
603
        }
604

    
605
        if(ctrl)
606
                LoopCtrl = ploop;
607
        LoopLevel--;
608
        LLOG(LOG_END << "Leaving event loop ");
609
}
610

    
611
gboolean sOnce(GtkWidget *)
612
{
613
        return false;
614
}
615

    
616
void Ctrl::GuiSleep(int ms)
617
{
618
        GuiLock __;
619
        ASSERT_(IsMainThread(), "Only the main thread can perform GuiSleep");
620
        if(ms < 20) // Periodic timer is each 20ms, so that is the longest possible wait
621
                g_timeout_add(ms, (GSourceFunc) sOnce, NULL); // otherwise setup shorter timer
622
        FetchEvents(TRUE);
623
}
624

    
625
END_UPP_NAMESPACE
626

    
627
#endif