]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/x11/app.cpp
Fixed typos
[wxWidgets.git] / src / x11 / app.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: app.cpp
3// Purpose: wxApp
4// Author: Julian Smart
5// Modified by:
6// Created: 17/09/98
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12#ifdef __GNUG__
13 #pragma implementation "app.h"
14#endif
15
16#include "wx/frame.h"
17#include "wx/app.h"
18#include "wx/utils.h"
19#include "wx/gdicmn.h"
20#include "wx/icon.h"
21#include "wx/dialog.h"
22#include "wx/log.h"
23#include "wx/module.h"
24#include "wx/memory.h"
25#include "wx/log.h"
26#include "wx/intl.h"
27#include "wx/evtloop.h"
28
29#if wxUSE_THREADS
30 #include "wx/thread.h"
31#endif
32
33#if wxUSE_WX_RESOURCES
34 #include "wx/resource.h"
35#endif
36
37#ifdef __VMS__
38#pragma message disable nosimpint
39#endif
40#include <X11/Xlib.h>
41#include <X11/Xutil.h>
42#include <X11/Xatom.h>
43
44#ifdef __VMS__
45#pragma message enable nosimpint
46#endif
47
48#include "wx/x11/private.h"
49
50#include <string.h>
51
52extern wxList wxPendingDelete;
53
54wxApp *wxTheApp = NULL;
55
56wxHashTable *wxWidgetHashTable = NULL;
57
58IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
59
60BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
61 EVT_IDLE(wxApp::OnIdle)
62END_EVENT_TABLE()
63
64#ifdef __WXDEBUG__
65typedef int (*XErrorHandlerFunc)(Display *, XErrorEvent *);
66
67XErrorHandlerFunc gs_pfnXErrorHandler = 0;
68
69static int wxXErrorHandler(Display *dpy, XErrorEvent *xevent)
70{
71 // just forward to the default handler for now
72 return gs_pfnXErrorHandler(dpy, xevent);
73}
74#endif // __WXDEBUG__
75
76long wxApp::sm_lastMessageTime = 0;
77WXDisplay *wxApp::ms_display = NULL;
78
79// This is set within wxEntryStart -- too early on
80// to put these in wxTheApp
81static int g_newArgc = 0;
82static wxChar** g_newArgv = NULL;
83static bool g_showIconic = FALSE;
84static wxSize g_initialSize = wxDefaultSize;
85
86bool wxApp::Initialize()
87{
88 wxClassInfo::InitializeClasses();
89
90 // GL: I'm annoyed ... I don't know where to put this and I don't want to
91 // create a module for that as it's part of the core.
92#if wxUSE_THREADS
93 wxPendingEventsLocker = new wxCriticalSection();
94#endif
95
96 wxTheColourDatabase = new wxColourDatabase(wxKEY_STRING);
97 wxTheColourDatabase->Initialize();
98
99 wxInitializeStockLists();
100 wxInitializeStockObjects();
101
102#if wxUSE_WX_RESOURCES
103 wxInitializeResourceSystem();
104#endif
105
106 wxWidgetHashTable = new wxHashTable(wxKEY_INTEGER);
107
108 wxModule::RegisterModules();
109 if (!wxModule::InitializeModules()) return FALSE;
110
111 return TRUE;
112}
113
114void wxApp::CleanUp()
115{
116 if (g_newArgv)
117 delete[] g_newArgv;
118 g_newArgv = NULL;
119
120 delete wxWidgetHashTable;
121 wxWidgetHashTable = NULL;
122
123 wxModule::CleanUpModules();
124
125#if wxUSE_WX_RESOURCES
126 wxCleanUpResourceSystem();
127#endif
128
129 delete wxTheColourDatabase;
130 wxTheColourDatabase = NULL;
131
132 wxDeleteStockObjects();
133
134 wxDeleteStockLists();
135
136 delete wxTheApp;
137 wxTheApp = NULL;
138
139 wxClassInfo::CleanUpClasses();
140
141#if wxUSE_THREADS
142 delete wxPendingEvents;
143 delete wxPendingEventsLocker;
144#endif
145
146#if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
147 // At this point we want to check if there are any memory
148 // blocks that aren't part of the wxDebugContext itself,
149 // as a special case. Then when dumping we need to ignore
150 // wxDebugContext, too.
151 if (wxDebugContext::CountObjectsLeft(TRUE) > 0)
152 {
153 wxLogDebug("There were memory leaks.");
154 wxDebugContext::Dump();
155 wxDebugContext::PrintStatistics();
156 }
157#endif
158
159 // do it as the very last thing because everything else can log messages
160 wxLog::DontCreateOnDemand();
161 // do it as the very last thing because everything else can log messages
162 delete wxLog::SetActiveTarget(NULL);
163}
164
165// NB: argc and argv may be changed here, pass by reference!
166int wxEntryStart( int& argc, char *argv[] )
167{
168#ifdef __WXDEBUG__
169 // install the X error handler
170 gs_pfnXErrorHandler = XSetErrorHandler( wxXErrorHandler );
171#endif // __WXDEBUG__
172
173 wxString displayName;
174 bool syncDisplay = FALSE;
175
176 // Parse the arguments.
177 // We can't use wxCmdLineParser or OnInitCmdLine and friends because
178 // we have to create the Display earlier. If we can find a way to
179 // use the wxAppBase API then I'll be quite happy to change it.
180 g_newArgv = new wxChar*[argc];
181 g_newArgc = 0;
182 int i;
183 for (i = 0; i < argc; i++)
184 {
185 wxString arg(argv[i]);
186 if (arg == wxT("-display"))
187 {
188 if (i < (argc - 1))
189 {
190 i ++;
191 displayName = argv[i];
192 continue;
193 }
194 }
195 else if (arg == wxT("-geometry"))
196 {
197 if (i < (argc - 1))
198 {
199 i ++;
200 wxString windowGeometry = argv[i];
201 int w, h;
202 if (wxSscanf(windowGeometry.c_str(), _T("%dx%d"), &w, &h) != 2)
203 {
204 wxLogError(_("Invalid geometry specification '%s'"), windowGeometry.c_str());
205 }
206 else
207 {
208 g_initialSize = wxSize(w, h);
209 }
210 continue;
211 }
212 }
213 else if (arg == wxT("-sync"))
214 {
215 syncDisplay = TRUE;
216 continue;
217 }
218 else if (arg == wxT("-iconic"))
219 {
220 g_showIconic = TRUE;
221
222 continue;
223 }
224
225 // Not eaten by wxWindows, so pass through
226 g_newArgv[g_newArgc] = argv[i];
227 g_newArgc ++;
228 }
229
230 Display* xdisplay;
231 if (displayName.IsEmpty())
232 xdisplay = XOpenDisplay(NULL);
233 else
234 xdisplay = XOpenDisplay(displayName);
235
236 if (!xdisplay)
237 {
238 wxLogError( _("wxWindows could not open display. Exiting.") );
239 return -1;
240 }
241
242 if (syncDisplay)
243 {
244 XSynchronize(xdisplay, True);
245 }
246
247 wxApp::ms_display = (WXDisplay*) xdisplay;
248
249 XSelectInput( xdisplay, XDefaultRootWindow(xdisplay), PropertyChangeMask);
250
251// wxSetDetectableAutoRepeat( TRUE );
252
253 if (!wxApp::Initialize())
254 return -1;
255
256 return 0;
257}
258
259int wxEntryInitGui()
260{
261 int retValue = 0;
262
263 if ( !wxTheApp->OnInitGui() )
264 retValue = -1;
265
266 return retValue;
267}
268
269
270int wxEntry( int argc, char *argv[] )
271{
272#if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
273 // This seems to be necessary since there are 'rogue'
274 // objects present at this point (perhaps global objects?)
275 // Setting a checkpoint will ignore them as far as the
276 // memory checking facility is concerned.
277 // Of course you may argue that memory allocated in globals should be
278 // checked, but this is a reasonable compromise.
279 wxDebugContext::SetCheckpoint();
280#endif
281 int err = wxEntryStart(argc, argv);
282 if (err)
283 return err;
284
285 if (!wxTheApp)
286 {
287 if (!wxApp::GetInitializerFunction())
288 {
289 printf( "wxWindows error: No initializer - use IMPLEMENT_APP macro.\n" );
290 return 0;
291 };
292
293 wxTheApp = (wxApp*) (* wxApp::GetInitializerFunction()) ();
294 };
295
296 if (!wxTheApp)
297 {
298 printf( "wxWindows error: wxTheApp == NULL\n" );
299 return 0;
300 };
301
302 wxTheApp->SetClassName(wxFileNameFromPath(argv[0]));
303 wxTheApp->SetAppName(wxFileNameFromPath(argv[0]));
304
305 // The command line may have been changed
306 // by stripping out -display etc.
307 if (g_newArgc > 0)
308 {
309 wxTheApp->argc = g_newArgc;
310 wxTheApp->argv = g_newArgv;
311 }
312 else
313 {
314 wxTheApp->argc = argc;
315 wxTheApp->argv = argv;
316 }
317 wxTheApp->m_showIconic = g_showIconic;
318 wxTheApp->m_initialSize = g_initialSize;
319
320 int retValue;
321 retValue = wxEntryInitGui();
322
323 // Here frames insert themselves automatically into wxTopLevelWindows by
324 // getting created in OnInit().
325 if ( retValue == 0 )
326 {
327 if ( !wxTheApp->OnInit() )
328 retValue = -1;
329 }
330
331 if ( retValue == 0 )
332 {
333 if (wxTheApp->Initialized()) retValue = wxTheApp->OnRun();
334 }
335
336 // flush the logged messages if any
337 wxLog *pLog = wxLog::GetActiveTarget();
338 if ( pLog != NULL && pLog->HasPendingMessages() )
339 pLog->Flush();
340
341 delete wxLog::SetActiveTarget(new wxLogStderr); // So dialog boxes aren't used
342 // for further messages
343
344 if (wxTheApp->GetTopWindow())
345 {
346 delete wxTheApp->GetTopWindow();
347 wxTheApp->SetTopWindow(NULL);
348 }
349
350 wxTheApp->DeletePendingObjects();
351
352 wxTheApp->OnExit();
353
354 wxApp::CleanUp();
355
356 return retValue;
357};
358
359// Static member initialization
360wxAppInitializerFunction wxAppBase::m_appInitFn = (wxAppInitializerFunction) NULL;
361
362wxApp::wxApp()
363{
364 m_topWindow = NULL;
365 wxTheApp = this;
366 m_className = "";
367 m_wantDebugOutput = TRUE ;
368 m_appName = "";
369 argc = 0;
370 argv = NULL;
371 m_exitOnFrameDelete = TRUE;
372 m_mainColormap = (WXColormap) NULL;
373 m_topLevelWidget = (WXWindow) NULL;
374 m_maxRequestSize = 0;
375 m_mainLoop = NULL;
376 m_showIconic = FALSE;
377 m_initialSize = wxDefaultSize;
378}
379
380bool wxApp::Initialized()
381{
382 if (GetTopWindow())
383 return TRUE;
384 else
385 return FALSE;
386}
387
388int wxApp::MainLoop()
389{
390 int rt;
391 m_mainLoop = new wxEventLoop;
392
393 rt = m_mainLoop->Run();
394
395 delete m_mainLoop;
396 m_mainLoop = NULL;
397 return rt;
398}
399
400// Processes an X event.
401void wxApp::ProcessXEvent(WXEvent* _event)
402{
403 XEvent* event = (XEvent*) _event;
404
405 wxWindow* win = NULL;
406 Window window = event->xany.window;
407 Window actualWindow = window;
408
409 // Find the first wxWindow that corresponds to this event window
410 // Because we're receiving events after a window
411 // has been destroyed, assume a 1:1 match between
412 // Window and wxWindow, so if it's not in the table,
413 // it must have been destroyed.
414
415 win = wxGetWindowFromTable(window);
416 if (!win)
417 return;
418
419 switch (event->type)
420 {
421 case KeyPress:
422 {
423 if (win && !win->IsEnabled())
424 return;
425
426 {
427 if (win)
428 {
429 wxKeyEvent keyEvent(wxEVT_KEY_DOWN);
430 wxTranslateKeyEvent(keyEvent, win, window, event);
431
432 wxLogDebug( "OnKey from %s", win->GetName().c_str() );
433
434 // We didn't process wxEVT_KEY_DOWN, so send
435 // wxEVT_CHAR
436 if (!win->GetEventHandler()->ProcessEvent( keyEvent ))
437 {
438 keyEvent.SetEventType(wxEVT_CHAR);
439 win->GetEventHandler()->ProcessEvent( keyEvent );
440 }
441
442 // We intercepted and processed the key down event
443 return;
444 }
445 }
446 return;
447 }
448 case KeyRelease:
449 {
450 if (win && !win->IsEnabled())
451 return;
452
453 if (win)
454 {
455 wxKeyEvent keyEvent(wxEVT_KEY_UP);
456 wxTranslateKeyEvent(keyEvent, win, window, event);
457
458 win->GetEventHandler()->ProcessEvent( keyEvent );
459 }
460 return;
461 }
462 case ConfigureNotify:
463 {
464 // Not clear if this is the same in NanoX
465 if (win)
466 {
467 wxSizeEvent sizeEvent( wxSize(event->xconfigure.width,event->xconfigure.height), win->GetId() );
468 sizeEvent.SetEventObject( win );
469
470 win->GetEventHandler()->ProcessEvent( sizeEvent );
471 }
472 }
473#if !wxUSE_NANOX
474 case PropertyNotify:
475 {
476 HandlePropertyChange(_event);
477 return;
478 }
479 case ClientMessage:
480 {
481 if (win && !win->IsEnabled())
482 return;
483
484 Atom wm_delete_window = XInternAtom(wxGlobalDisplay(), "WM_DELETE_WINDOW", True);;
485 Atom wm_protocols = XInternAtom(wxGlobalDisplay(), "WM_PROTOCOLS", True);;
486
487 if (event->xclient.message_type == wm_protocols)
488 {
489 if ((Atom) (event->xclient.data.l[0]) == wm_delete_window)
490 {
491 if (win)
492 {
493 win->Close(FALSE);
494 }
495 }
496 }
497 return;
498 }
499 case ResizeRequest:
500 {
501 /*
502 * If resize event, don't resize until the last resize event for this
503 * window is recieved. Prevents flicker as windows are resized.
504 */
505
506 Display *disp = (Display*) wxGetDisplay();
507 XEvent report;
508
509 // to avoid flicker
510 report = * event;
511 while( XCheckTypedWindowEvent (disp, actualWindow, ResizeRequest, &report));
512
513 if (win)
514 {
515 wxSize sz = win->GetSize();
516 wxSizeEvent sizeEvent(sz, win->GetId());
517 sizeEvent.SetEventObject(win);
518
519 win->GetEventHandler()->ProcessEvent( sizeEvent );
520 }
521
522 return;
523 }
524#endif
525#if wxUSE_NANOX
526 case GR_EVENT_TYPE_CLOSE_REQ:
527 {
528 if (win)
529 {
530 win->Close(FALSE);
531 }
532 break;
533 }
534#endif
535 case Expose:
536 {
537 if (win)
538 {
539 win->GetUpdateRegion().Union( event->xexpose.x, event->xexpose.y,
540 event->xexpose.width, event->xexpose.height);
541
542 win->GetClearRegion().Union( event->xexpose.x, event->xexpose.y,
543 event->xexpose.width, event->xexpose.height);
544
545 // if (event->xexpose.count == 0)
546 // win->Update();
547 }
548
549 return;
550 }
551 case EnterNotify:
552 case LeaveNotify:
553 case ButtonPress:
554 case ButtonRelease:
555 case MotionNotify:
556 {
557 if (win && !win->IsEnabled())
558 return;
559
560 if (win)
561 {
562 wxMouseEvent wxevent;
563 wxTranslateMouseEvent(wxevent, win, window, event);
564 win->GetEventHandler()->ProcessEvent( wxevent );
565 }
566 return;
567 }
568 case FocusIn:
569 {
570#if !wxUSE_NANOX
571 if (win && event->xfocus.detail != NotifyPointer)
572#endif
573 {
574 wxLogDebug( "FocusIn from %s", win->GetName().c_str() );
575
576 wxFocusEvent focusEvent(wxEVT_SET_FOCUS, win->GetId());
577 focusEvent.SetEventObject(win);
578 win->GetEventHandler()->ProcessEvent(focusEvent);
579 }
580 break;
581 }
582 case FocusOut:
583 {
584#if !wxUSE_NANOX
585 if (win && event->xfocus.detail != NotifyPointer)
586#endif
587 {
588 wxLogDebug( "FocusOut from %s\n", win->GetName().c_str() );
589
590 wxFocusEvent focusEvent(wxEVT_KILL_FOCUS, win->GetId());
591 focusEvent.SetEventObject(win);
592 win->GetEventHandler()->ProcessEvent(focusEvent);
593 }
594 break;
595 }
596 case DestroyNotify:
597 {
598 // Do we want to process this (for top-level windows)?
599 // But we want to be able to veto closes, anyway
600 break;
601 }
602 default:
603 {
604#ifdef __WXDEBUG__
605 //wxString eventName = wxGetXEventName(XEvent& event);
606 //wxLogDebug(wxT("Event %s not handled"), eventName.c_str());
607#endif
608 break;
609 }
610 }
611}
612
613// Returns TRUE if more time is needed.
614// Note that this duplicates wxEventLoopImpl::SendIdleEvent
615// but ProcessIdle may be needed by apps, so is kept.
616bool wxApp::ProcessIdle()
617{
618 wxIdleEvent event;
619 event.SetEventObject(this);
620 ProcessEvent(event);
621
622 return event.MoreRequested();
623}
624
625void wxApp::ExitMainLoop()
626{
627 if (m_mainLoop)
628 m_mainLoop->Exit(0);
629}
630
631// Is a message/event pending?
632bool wxApp::Pending()
633{
634 return wxEventLoop::GetActive()->Pending();
635}
636
637// Dispatch a message.
638void wxApp::Dispatch()
639{
640 wxEventLoop::GetActive()->Dispatch();
641}
642
643// This should be redefined in a derived class for
644// handling property change events for XAtom IPC.
645void wxApp::HandlePropertyChange(WXEvent *event)
646{
647 // by default do nothing special
648 // TODO: what to do for X11
649 // XtDispatchEvent((XEvent*) event);
650}
651
652void wxApp::OnIdle(wxIdleEvent& event)
653{
654 static bool s_inOnIdle = FALSE;
655
656 // Avoid recursion (via ProcessEvent default case)
657 if (s_inOnIdle)
658 return;
659
660 s_inOnIdle = TRUE;
661
662 // Resend in the main thread events which have been prepared in other
663 // threads
664 ProcessPendingEvents();
665
666 // 'Garbage' collection of windows deleted with Close()
667 DeletePendingObjects();
668
669 // Send OnIdle events to all windows
670 bool needMore = SendIdleEvents();
671
672 if (needMore)
673 event.RequestMore(TRUE);
674
675 s_inOnIdle = FALSE;
676}
677
678void wxWakeUpIdle()
679{
680 // **** please implement me! ****
681 // Wake up the idle handler processor, even if it is in another thread...
682}
683
684
685// Send idle event to all top-level windows
686bool wxApp::SendIdleEvents()
687{
688 bool needMore = FALSE;
689
690 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
691 while (node)
692 {
693 wxWindow* win = node->GetData();
694 if (SendIdleEvents(win))
695 needMore = TRUE;
696 node = node->GetNext();
697 }
698
699 return needMore;
700}
701
702// Send idle event to window and all subwindows
703bool wxApp::SendIdleEvents(wxWindow* win)
704{
705 bool needMore = FALSE;
706
707 wxIdleEvent event;
708 event.SetEventObject(win);
709
710 win->GetEventHandler()->ProcessEvent(event);
711
712 win->OnInternalIdle();
713
714 if (event.MoreRequested())
715 needMore = TRUE;
716
717 wxNode* node = win->GetChildren().First();
718 while (node)
719 {
720 wxWindow* win = (wxWindow*) node->Data();
721 if (SendIdleEvents(win))
722 needMore = TRUE;
723
724 node = node->Next();
725 }
726
727 return needMore;
728}
729
730void wxApp::DeletePendingObjects()
731{
732 wxNode *node = wxPendingDelete.First();
733 while (node)
734 {
735 wxObject *obj = (wxObject *)node->Data();
736
737 delete obj;
738
739 if (wxPendingDelete.Member(obj))
740 delete node;
741
742 // Deleting one object may have deleted other pending
743 // objects, so start from beginning of list again.
744 node = wxPendingDelete.First();
745 }
746}
747
748// Create display, and other initialization
749bool wxApp::OnInitGui()
750{
751 // Eventually this line will be removed, but for
752 // now we don't want to try popping up a dialog
753 // for error messages.
754 delete wxLog::SetActiveTarget(new wxLogStderr);
755
756 if (!wxAppBase::OnInitGui())
757 return FALSE;
758
759 GetMainColormap( wxApp::GetDisplay() );
760
761 m_maxRequestSize = XMaxRequestSize( (Display*) wxApp::GetDisplay() );
762
763 return TRUE;
764}
765
766WXColormap wxApp::GetMainColormap(WXDisplay* display)
767{
768 if (!display) /* Must be called first with non-NULL display */
769 return m_mainColormap;
770
771 int defaultScreen = DefaultScreen((Display*) display);
772 Screen* screen = XScreenOfDisplay((Display*) display, defaultScreen);
773
774 Colormap c = DefaultColormapOfScreen(screen);
775
776 if (!m_mainColormap)
777 m_mainColormap = (WXColormap) c;
778
779 return (WXColormap) c;
780}
781
782Window wxGetWindowParent(Window window)
783{
784 wxASSERT_MSG( window, "invalid window" );
785
786 return (Window) 0;
787
788 Window parent, root = 0;
789 unsigned int noChildren = 0;
790 Window* children = NULL;
791 int res = XQueryTree((Display*) wxGetDisplay(), window, & root, & parent,
792 & children, & noChildren);
793 if (children)
794 XFree(children);
795 if (res)
796 return parent;
797 else
798 return (Window) 0;
799}
800
801void wxExit()
802{
803 int retValue = 0;
804 if (wxTheApp)
805 retValue = wxTheApp->OnExit();
806
807 wxApp::CleanUp();
808 /*
809 * Exit in some platform-specific way. Not recommended that the app calls this:
810 * only for emergencies.
811 */
812 exit(retValue);
813}
814
815// Yield to other processes
816
817bool wxApp::Yield(bool onlyIfNeeded)
818{
819 bool s_inYield = FALSE;
820
821 if ( s_inYield )
822 {
823 if ( !onlyIfNeeded )
824 {
825 wxFAIL_MSG( wxT("wxYield called recursively" ) );
826 }
827
828 return FALSE;
829 }
830
831 s_inYield = TRUE;
832
833 while (wxTheApp && wxTheApp->Pending())
834 wxTheApp->Dispatch();
835
836 s_inYield = FALSE;
837
838 return TRUE;
839}
840
841// XPM hack: make the arrays const
842#define static static const
843
844#include "wx/generic/info.xpm"
845#include "wx/generic/error.xpm"
846#include "wx/generic/question.xpm"
847#include "wx/generic/warning.xpm"
848
849#undef static
850
851wxIcon
852wxApp::GetStdIcon(int which) const
853{
854 switch(which)
855 {
856 case wxICON_INFORMATION:
857 return wxIcon(info_xpm);
858
859 case wxICON_QUESTION:
860 return wxIcon(question_xpm);
861
862 case wxICON_EXCLAMATION:
863 return wxIcon(warning_xpm);
864
865 default:
866 wxFAIL_MSG("requested non existent standard icon");
867 // still fall through
868
869 case wxICON_HAND:
870 return wxIcon(error_xpm);
871 }
872}
873
874void wxApp::OnAssert(const wxChar *file, int line, const wxChar *msg)
875{
876 // While the GUI isn't working that well, just print out the
877 // message.
878#if 0
879 wxAppBase::OnAssert(file, line, msg);
880#else
881 wxString msg2;
882 msg2.Printf("At file %s:%d: %s", file, line, msg);
883 wxLogDebug(msg2);
884#endif
885}
886