]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/gtk1/app.cpp
applied patch for compilation with gcc 3.0
[wxWidgets.git] / src / gtk1 / app.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: app.cpp
3// Purpose:
4// Author: Robert Roebling
5// Id: $Id$
6// Copyright: (c) 1998 Robert Roebling, Julian Smart
7// Licence: wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10#ifdef __GNUG__
11 #pragma implementation "app.h"
12#endif
13
14#include "wx/app.h"
15#include "wx/gdicmn.h"
16#include "wx/utils.h"
17#include "wx/intl.h"
18#include "wx/log.h"
19#include "wx/memory.h"
20#include "wx/font.h"
21#include "wx/settings.h"
22#include "wx/dialog.h"
23
24#if wxUSE_WX_RESOURCES
25 #include "wx/resource.h"
26#endif
27
28#include "wx/module.h"
29#include "wx/image.h"
30
31#if wxUSE_THREADS
32 #include "wx/thread.h"
33#endif
34
35#include <unistd.h>
36
37#include <glib.h>
38#include <gdk/gdk.h>
39#include <gtk/gtk.h>
40
41#include "wx/gtk/win_gtk.h"
42
43//-----------------------------------------------------------------------------
44// global data
45//-----------------------------------------------------------------------------
46
47wxApp *wxTheApp = (wxApp *) NULL;
48wxAppInitializerFunction wxAppBase::m_appInitFn = (wxAppInitializerFunction) NULL;
49
50extern bool g_isIdle;
51
52bool g_mainThreadLocked = FALSE;
53gint g_pendingTag = 0;
54
55static GtkWidget *gs_RootWindow = (GtkWidget*) NULL;
56
57//-----------------------------------------------------------------------------
58// local functions
59//-----------------------------------------------------------------------------
60
61/* forward declaration */
62gint wxapp_idle_callback( gpointer WXUNUSED(data) );
63gint wxapp_pending_callback( gpointer WXUNUSED(data) );
64void wxapp_install_idle_handler();
65
66#if wxUSE_THREADS
67gint wxapp_wakeup_timerout_callback( gpointer WXUNUSED(data) );
68#endif
69
70//-----------------------------------------------------------------------------
71// wxExit
72//-----------------------------------------------------------------------------
73
74void wxExit()
75{
76 gtk_main_quit();
77}
78
79//-----------------------------------------------------------------------------
80// wxYield
81//-----------------------------------------------------------------------------
82
83static bool gs_inYield = FALSE;
84
85bool wxYield()
86{
87#if wxUSE_THREADS
88 if ( !wxThread::IsMain() )
89 {
90 // can't call gtk_main_iteration() from other threads like this
91 return TRUE;
92 }
93#endif // wxUSE_THREADS
94
95#ifdef __WXDEBUG__
96 if (gs_inYield)
97 wxFAIL_MSG( wxT("wxYield called recursively" ) );
98#endif
99
100 gs_inYield = TRUE;
101
102 if (!g_isIdle)
103 {
104 // We need to remove idle callbacks or the loop will
105 // never finish.
106 gtk_idle_remove( wxTheApp->m_idleTag );
107 wxTheApp->m_idleTag = 0;
108 g_isIdle = TRUE;
109 }
110
111 while (gtk_events_pending())
112 gtk_main_iteration();
113
114 // disable log flushing from here because a call to wxYield() shouldn't
115 // normally result in message boxes popping up &c
116 wxLog::Suspend();
117
118 /* it's necessary to call ProcessIdle() to update the frames sizes which
119 might have been changed (it also will update other things set from
120 OnUpdateUI() which is a nice (and desired) side effect) */
121 while (wxTheApp->ProcessIdle()) { }
122
123 // let the logs be flashed again
124 wxLog::Resume();
125
126 gs_inYield = FALSE;
127
128 return TRUE;
129}
130
131//-----------------------------------------------------------------------------
132// wxYieldIfNeeded
133// Like wxYield, but fails silently if the yield is recursive.
134//-----------------------------------------------------------------------------
135
136bool wxYieldIfNeeded()
137{
138 if (gs_inYield)
139 return FALSE;
140
141 return wxYield();
142}
143
144//-----------------------------------------------------------------------------
145// wxWakeUpIdle
146//-----------------------------------------------------------------------------
147
148void wxWakeUpIdle()
149{
150#if wxUSE_THREADS
151 if (!wxThread::IsMain())
152 wxMutexGuiEnter();
153#endif
154
155 if (g_isIdle)
156 wxapp_install_idle_handler();
157
158#if wxUSE_THREADS
159 if (!wxThread::IsMain())
160 wxMutexGuiLeave();
161#endif
162}
163
164//-----------------------------------------------------------------------------
165// local functions
166//-----------------------------------------------------------------------------
167
168gint wxapp_pending_callback( gpointer WXUNUSED(data) )
169{
170 if (!wxTheApp) return TRUE;
171
172 // when getting called from GDK's time-out handler
173 // we are no longer within GDK's grab on the GUI
174 // thread so we must lock it here ourselves
175 gdk_threads_enter();
176
177 // Sent idle event to all who request them
178 wxTheApp->ProcessPendingEvents();
179
180 g_pendingTag = 0;
181
182 /* flush the logged messages if any */
183#if wxUSE_LOG
184 wxLog::FlushActive();
185#endif // wxUSE_LOG
186
187 // Release lock again
188 gdk_threads_leave();
189
190 // Return FALSE to indicate that no more idle events are
191 // to be sent (single shot instead of continuous stream)
192 return FALSE;
193}
194
195gint wxapp_idle_callback( gpointer WXUNUSED(data) )
196{
197 if (!wxTheApp) return TRUE;
198
199 // when getting called from GDK's time-out handler
200 // we are no longer within GDK's grab on the GUI
201 // thread so we must lock it here ourselves
202 gdk_threads_enter();
203
204 /* Indicate that we are now in idle mode - even so deeply
205 in idle mode that we don't get any idle events anymore.
206 this is like wxMSW where an idle event is sent only
207 once each time after the event queue has been completely
208 emptied */
209 g_isIdle = TRUE;
210 wxTheApp->m_idleTag = 0;
211
212 // Sent idle event to all who request them
213 while (wxTheApp->ProcessIdle()) { }
214
215 // Release lock again
216 gdk_threads_leave();
217
218 // Return FALSE to indicate that no more idle events are
219 // to be sent (single shot instead of continuous stream)
220 return FALSE;
221}
222
223void wxapp_install_idle_handler()
224{
225 wxASSERT_MSG( wxTheApp->m_idleTag == 0, wxT("attempt to install idle handler twice") );
226
227 g_isIdle = FALSE;
228
229 if (g_pendingTag == 0)
230 g_pendingTag = gtk_idle_add_priority( 900, wxapp_pending_callback, (gpointer) NULL );
231
232 /* This routine gets called by all event handlers
233 indicating that the idle is over. It may also
234 get called from other thread for sending events
235 to the main thread (and processing these in
236 idle time). Very low priority. */
237
238 wxTheApp->m_idleTag = gtk_idle_add_priority( 1000, wxapp_idle_callback, (gpointer) NULL );
239}
240
241#if wxUSE_THREADS
242
243static int g_threadUninstallLevel = 0;
244
245void wxapp_install_thread_wakeup()
246{
247 g_threadUninstallLevel++;
248
249 if (g_threadUninstallLevel != 1) return;
250
251 if (wxTheApp->m_wakeUpTimerTag) return;
252
253 wxTheApp->m_wakeUpTimerTag = gtk_timeout_add( 50, wxapp_wakeup_timerout_callback, (gpointer) NULL );
254}
255
256void wxapp_uninstall_thread_wakeup()
257{
258 g_threadUninstallLevel--;
259
260 if (g_threadUninstallLevel != 0) return;
261
262 if (!wxTheApp->m_wakeUpTimerTag) return;
263
264 gtk_timeout_remove( wxTheApp->m_wakeUpTimerTag );
265 wxTheApp->m_wakeUpTimerTag = 0;
266}
267
268gint wxapp_wakeup_timerout_callback( gpointer WXUNUSED(data) )
269{
270 // when getting called from GDK's time-out handler
271 // we are no longer within GDK's grab on the GUI
272 // thread so we must lock it here ourselves
273 gdk_threads_enter();
274
275 wxapp_uninstall_thread_wakeup();
276
277 // unblock other threads wishing to do some GUI things
278 wxMutexGuiLeave();
279
280 g_mainThreadLocked = TRUE;
281
282 // wake up other threads
283 wxUsleep( 1 );
284
285 // block other thread again
286 wxMutexGuiEnter();
287
288 g_mainThreadLocked = FALSE;
289
290 wxapp_install_thread_wakeup();
291
292 // release lock again
293 gdk_threads_leave();
294
295 return TRUE;
296}
297
298#endif // wxUSE_THREADS
299
300//-----------------------------------------------------------------------------
301// wxApp
302//-----------------------------------------------------------------------------
303
304IMPLEMENT_DYNAMIC_CLASS(wxApp,wxEvtHandler)
305
306BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
307 EVT_IDLE(wxApp::OnIdle)
308END_EVENT_TABLE()
309
310wxApp::wxApp()
311{
312 wxTheApp = this;
313
314 m_topWindow = (wxWindow *) NULL;
315 m_exitOnFrameDelete = TRUE;
316
317 m_idleTag = 0;
318 wxapp_install_idle_handler();
319
320#if wxUSE_THREADS
321 m_wakeUpTimerTag = 0;
322 wxapp_install_thread_wakeup();
323#endif
324
325 m_colorCube = (unsigned char*) NULL;
326
327 m_useBestVisual = FALSE;
328}
329
330wxApp::~wxApp()
331{
332 if (m_idleTag) gtk_idle_remove( m_idleTag );
333
334#if wxUSE_THREADS
335 wxapp_uninstall_thread_wakeup();
336#endif
337
338 if (m_colorCube) free(m_colorCube);
339}
340
341bool wxApp::OnInitGui()
342{
343 GdkVisual *visual = gdk_visual_get_system();
344
345 /* on some machines, the default visual is just 256 colours, so
346 we make sure we get the best. this can sometimes be wasteful,
347 of course, but what do these guys pay $30.000 for? */
348
349 if ((gdk_visual_get_best() != gdk_visual_get_system()) &&
350 (m_useBestVisual))
351 {
352#ifdef __WXGTK20__
353 /* seems gtk_widget_set_default_visual no longer exists? */
354 GdkVisual* vis = gtk_widget_get_default_visual();
355#else
356 GdkVisual* vis = gdk_visual_get_best();
357 gtk_widget_set_default_visual( vis );
358#endif
359
360 GdkColormap *colormap = gdk_colormap_new( vis, FALSE );
361 gtk_widget_set_default_colormap( colormap );
362
363 visual = vis;
364 }
365
366 /* Nothing to do for 15, 16, 24, 32 bit displays */
367 if (visual->depth > 8) return TRUE;
368
369 /* initialize color cube for 8-bit color reduction dithering */
370
371 GdkColormap *cmap = gtk_widget_get_default_colormap();
372
373 m_colorCube = (unsigned char*)malloc(32 * 32 * 32);
374
375 for (int r = 0; r < 32; r++)
376 {
377 for (int g = 0; g < 32; g++)
378 {
379 for (int b = 0; b < 32; b++)
380 {
381 int rr = (r << 3) | (r >> 2);
382 int gg = (g << 3) | (g >> 2);
383 int bb = (b << 3) | (b >> 2);
384
385 int index = -1;
386
387 GdkColor *colors = cmap->colors;
388 if (colors)
389 {
390 int max = 3 * 65536;
391
392 for (int i = 0; i < cmap->size; i++)
393 {
394 int rdiff = ((rr << 8) - colors[i].red);
395 int gdiff = ((gg << 8) - colors[i].green);
396 int bdiff = ((bb << 8) - colors[i].blue);
397 int sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
398 if (sum < max)
399 {
400 index = i; max = sum;
401 }
402 }
403 }
404 else
405 {
406#if (GTK_MINOR_VERSION > 0)
407 /* assume 8-bit true or static colors. this really
408 exists. */
409 GdkVisual* vis = gdk_colormap_get_visual( cmap );
410 index = (r >> (5 - vis->red_prec)) << vis->red_shift;
411 index |= (g >> (5 - vis->green_prec)) << vis->green_shift;
412 index |= (b >> (5 - vis->blue_prec)) << vis->blue_shift;
413#else
414 wxFAIL_MSG( wxT("Unsupported graphics hardware") );
415#endif
416 }
417 m_colorCube[ (r*1024) + (g*32) + b ] = index;
418 }
419 }
420 }
421
422 return TRUE;
423}
424
425bool wxApp::ProcessIdle()
426{
427 wxIdleEvent event;
428 event.SetEventObject( this );
429 ProcessEvent( event );
430
431 return event.MoreRequested();
432}
433
434void wxApp::OnIdle( wxIdleEvent &event )
435{
436 static bool s_inOnIdle = FALSE;
437
438 /* Avoid recursion (via ProcessEvent default case) */
439 if (s_inOnIdle)
440 return;
441
442 s_inOnIdle = TRUE;
443
444 /* Resend in the main thread events which have been prepared in other
445 threads */
446 ProcessPendingEvents();
447
448 /* 'Garbage' collection of windows deleted with Close(). */
449 DeletePendingObjects();
450
451 /* Send OnIdle events to all windows */
452 bool needMore = SendIdleEvents();
453
454 if (needMore)
455 event.RequestMore(TRUE);
456
457 s_inOnIdle = FALSE;
458}
459
460bool wxApp::SendIdleEvents()
461{
462 bool needMore = FALSE;
463
464 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
465 while (node)
466 {
467 wxWindow* win = node->GetData();
468 if (SendIdleEvents(win))
469 needMore = TRUE;
470 node = node->GetNext();
471 }
472
473 return needMore;
474}
475
476bool wxApp::SendIdleEvents( wxWindow* win )
477{
478 bool needMore = FALSE;
479
480 wxIdleEvent event;
481 event.SetEventObject(win);
482
483 win->GetEventHandler()->ProcessEvent(event);
484
485 win->OnInternalIdle();
486
487 if (event.MoreRequested())
488 needMore = TRUE;
489
490 wxNode* node = win->GetChildren().First();
491 while (node)
492 {
493 wxWindow* win = (wxWindow*) node->Data();
494 if (SendIdleEvents(win))
495 needMore = TRUE;
496
497 node = node->Next();
498 }
499 return needMore ;
500}
501
502int wxApp::MainLoop()
503{
504 gtk_main();
505 return 0;
506}
507
508void wxApp::ExitMainLoop()
509{
510 if (gtk_main_level() > 0)
511 gtk_main_quit();
512}
513
514bool wxApp::Initialized()
515{
516 return m_initialized;
517}
518
519bool wxApp::Pending()
520{
521 return (gtk_events_pending() > 0);
522}
523
524void wxApp::Dispatch()
525{
526 gtk_main_iteration();
527}
528
529void wxApp::DeletePendingObjects()
530{
531 wxNode *node = wxPendingDelete.First();
532 while (node)
533 {
534 wxObject *obj = (wxObject *)node->Data();
535
536 delete obj;
537
538 if (wxPendingDelete.Find(obj))
539 delete node;
540
541 node = wxPendingDelete.First();
542 }
543}
544
545bool wxApp::Initialize()
546{
547 wxBuffer = new wxChar[BUFSIZ + 512];
548
549 wxClassInfo::InitializeClasses();
550
551 wxSystemSettings::Init();
552
553#if wxUSE_INTL
554 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
555#endif
556
557 // GL: I'm annoyed ... I don't know where to put this and I don't want to
558 // create a module for that as it's part of the core.
559#if wxUSE_THREADS
560 wxPendingEvents = new wxList();
561 wxPendingEventsLocker = new wxCriticalSection();
562#endif
563
564 wxTheColourDatabase = new wxColourDatabase( wxKEY_STRING );
565 wxTheColourDatabase->Initialize();
566
567 wxInitializeStockLists();
568 wxInitializeStockObjects();
569
570#if wxUSE_WX_RESOURCES
571 wxInitializeResourceSystem();
572#endif
573
574 wxModule::RegisterModules();
575 if (!wxModule::InitializeModules()) return FALSE;
576
577 return TRUE;
578}
579
580void wxApp::CleanUp()
581{
582 wxModule::CleanUpModules();
583
584#if wxUSE_WX_RESOURCES
585 wxCleanUpResourceSystem();
586#endif
587
588 if (wxTheColourDatabase)
589 delete wxTheColourDatabase;
590
591 wxTheColourDatabase = (wxColourDatabase*) NULL;
592
593 wxDeleteStockObjects();
594
595 wxDeleteStockLists();
596
597 delete wxTheApp;
598 wxTheApp = (wxApp*) NULL;
599
600 // GL: I'm annoyed ... I don't know where to put this and I don't want to
601 // create a module for that as it's part of the core.
602#if wxUSE_THREADS
603 delete wxPendingEvents;
604 delete wxPendingEventsLocker;
605#endif
606
607 wxSystemSettings::Done();
608
609 delete[] wxBuffer;
610
611 wxClassInfo::CleanUpClasses();
612
613 // check for memory leaks
614#if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
615 if (wxDebugContext::CountObjectsLeft(TRUE) > 0)
616 {
617 wxLogDebug(wxT("There were memory leaks.\n"));
618 wxDebugContext::Dump();
619 wxDebugContext::PrintStatistics();
620 }
621#endif // Debug
622
623#if wxUSE_LOG
624 // do this as the very last thing because everything else can log messages
625 wxLog::DontCreateOnDemand();
626
627 wxLog *oldLog = wxLog::SetActiveTarget( (wxLog*) NULL );
628 if (oldLog)
629 delete oldLog;
630#endif // wxUSE_LOG
631}
632
633//-----------------------------------------------------------------------------
634// Access to the root window global
635//-----------------------------------------------------------------------------
636
637GtkWidget* wxGetRootWindow()
638{
639 if (gs_RootWindow == NULL) {
640 gs_RootWindow = gtk_window_new( GTK_WINDOW_TOPLEVEL );
641 gtk_widget_realize( gs_RootWindow );
642 }
643 return gs_RootWindow;
644}
645
646//-----------------------------------------------------------------------------
647// wxEntry
648//-----------------------------------------------------------------------------
649
650
651int wxEntryStart( int argc, char *argv[] )
652{
653#if wxUSE_THREADS
654 /* GTK 1.2 up to version 1.2.3 has broken threads */
655 if ((gtk_major_version == 1) &&
656 (gtk_minor_version == 2) &&
657 (gtk_micro_version < 4))
658 {
659 printf( "wxWindows warning: GUI threading disabled due to outdated GTK version\n" );
660 }
661 else
662 {
663 g_thread_init(NULL);
664 }
665#endif
666
667 gtk_set_locale();
668
669 // We should have the wxUSE_WCHAR_T test on the _outside_
670#if wxUSE_WCHAR_T
671#if defined(__WXGTK20__)
672 // gtk+ 2.0 supports Unicode through UTF-8 strings
673 wxConvCurrent = &wxConvUTF8;
674#else
675 if (!wxOKlibc()) wxConvCurrent = &wxConvLocal;
676#endif
677#else
678 if (!wxOKlibc()) wxConvCurrent = (wxMBConv*) NULL;
679#endif
680
681 gdk_threads_enter();
682
683 gtk_init( &argc, &argv );
684
685 wxSetDetectableAutoRepeat( TRUE );
686
687 if (!wxApp::Initialize())
688 {
689 gdk_threads_leave();
690 return -1;
691 }
692
693 return 0;
694}
695
696
697int wxEntryInitGui()
698{
699 int retValue = 0;
700
701 if ( !wxTheApp->OnInitGui() )
702 retValue = -1;
703
704 wxGetRootWindow();
705
706 return retValue;
707}
708
709
710void wxEntryCleanup()
711{
712#if wxUSE_LOG
713 // flush the logged messages if any
714 wxLog *log = wxLog::GetActiveTarget();
715 if (log != NULL && log->HasPendingMessages())
716 log->Flush();
717
718 // continuing to use user defined log target is unsafe from now on because
719 // some resources may be already unavailable, so replace it by something
720 // more safe
721 wxLog *oldlog = wxLog::SetActiveTarget(new wxLogStderr);
722 if ( oldlog )
723 delete oldlog;
724#endif // wxUSE_LOG
725
726 wxApp::CleanUp();
727
728 gdk_threads_leave();
729}
730
731
732
733int wxEntry( int argc, char *argv[] )
734{
735#if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
736 // This seems to be necessary since there are 'rogue'
737 // objects present at this point (perhaps global objects?)
738 // Setting a checkpoint will ignore them as far as the
739 // memory checking facility is concerned.
740 // Of course you may argue that memory allocated in globals should be
741 // checked, but this is a reasonable compromise.
742 wxDebugContext::SetCheckpoint();
743#endif
744 int err = wxEntryStart(argc, argv);
745 if (err)
746 return err;
747
748 if (!wxTheApp)
749 {
750 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
751 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
752
753 wxAppInitializerFunction app_ini = wxApp::GetInitializerFunction();
754
755 wxObject *test_app = app_ini();
756
757 wxTheApp = (wxApp*) test_app;
758 }
759
760 wxCHECK_MSG( wxTheApp, -1, wxT("wxWindows error: no application object") );
761
762 wxTheApp->argc = argc;
763#if wxUSE_UNICODE
764 wxTheApp->argv = new wxChar*[argc+1];
765 int mb_argc = 0;
766 while (mb_argc < argc)
767 {
768 wxTheApp->argv[mb_argc] = wxStrdup(wxConvLibc.cMB2WX(argv[mb_argc]));
769 mb_argc++;
770 }
771 wxTheApp->argv[mb_argc] = (wxChar *)NULL;
772#else
773 wxTheApp->argv = argv;
774#endif
775
776 wxString name(wxFileNameFromPath(argv[0]));
777 wxStripExtension( name );
778 wxTheApp->SetAppName( name );
779
780 int retValue;
781 retValue = wxEntryInitGui();
782
783 // Here frames insert themselves automatically into wxTopLevelWindows by
784 // getting created in OnInit().
785 if ( retValue == 0 )
786 {
787 if ( !wxTheApp->OnInit() )
788 retValue = -1;
789 }
790
791 if ( retValue == 0 )
792 {
793 /* delete pending toplevel windows (typically a single
794 dialog) so that, if there isn't any left, we don't
795 call OnRun() */
796 wxTheApp->DeletePendingObjects();
797
798 wxTheApp->m_initialized = wxTopLevelWindows.GetCount() != 0;
799
800 if (wxTheApp->Initialized())
801 {
802 wxTheApp->OnRun();
803
804 wxWindow *topWindow = wxTheApp->GetTopWindow();
805 if (topWindow)
806 {
807 /* Forcibly delete the window. */
808 if (topWindow->IsKindOf(CLASSINFO(wxFrame)) ||
809 topWindow->IsKindOf(CLASSINFO(wxDialog)) )
810 {
811 topWindow->Close( TRUE );
812 wxTheApp->DeletePendingObjects();
813 }
814 else
815 {
816 delete topWindow;
817 wxTheApp->SetTopWindow( (wxWindow*) NULL );
818 }
819 }
820
821 retValue = wxTheApp->OnExit();
822 }
823 }
824
825 wxEntryCleanup();
826
827 return retValue;
828}
829
830#include "wx/gtk/info.xpm"
831#include "wx/gtk/error.xpm"
832#include "wx/gtk/question.xpm"
833#include "wx/gtk/warning.xpm"
834
835wxIcon
836wxApp::GetStdIcon(int which) const
837{
838 switch(which)
839 {
840 case wxICON_INFORMATION:
841 return wxIcon(info_xpm);
842
843 case wxICON_QUESTION:
844 return wxIcon(question_xpm);
845
846 case wxICON_EXCLAMATION:
847 return wxIcon(warning_xpm);
848
849 default:
850 wxFAIL_MSG(wxT("requested non existent standard icon"));
851 // still fall through
852
853 case wxICON_HAND:
854 return wxIcon(error_xpm);
855 }
856}