1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        gtk/glcanvas.cpp 
   3 // Purpose:     wxGLCanvas, for using OpenGL/Mesa with wxWidgets and GTK 
   4 // Author:      Robert Roebling 
   8 // Copyright:   (c) Robert Roebling 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // For compilers that support precompilation, includes "wx.h". 
  13 #include "wx/wxprec.h" 
  19 #include "wx/glcanvas.h" 
  22 #include "wx/colour.h" 
  23 #include "wx/module.h" 
  33 #include "wx/gtk/win_gtk.h" 
  35 // DLL options compatibility check: 
  37 WX_CHECK_BUILD_OPTIONS("wxGL") 
  40 //--------------------------------------------------------------------------- 
  42 //--------------------------------------------------------------------------- 
  43 int wxGLCanvas::m_glxVersion 
= 0; 
  45 //--------------------------------------------------------------------------- 
  47 //--------------------------------------------------------------------------- 
  49 XVisualInfo 
*g_vi 
= (XVisualInfo
*) NULL
; 
  50 //----------------------------------------------------------------------------- 
  52 //----------------------------------------------------------------------------- 
  54 extern void wxapp_install_idle_handler(); 
  57 //--------------------------------------------------------------------------- 
  59 //--------------------------------------------------------------------------- 
  61 IMPLEMENT_CLASS(wxGLContext
,wxObject
) 
  63 wxGLContext::wxGLContext( bool WXUNUSED(isRGB
), wxWindow 
*win
, const wxPalette
& WXUNUSED(palette
) ) 
  66     m_widget 
= win
->m_wxwindow
; 
  68     wxGLCanvas 
*gc 
= (wxGLCanvas
*) win
; 
  70     if (wxGLCanvas::GetGLXVersion() >= 13) 
  73         GLXFBConfig 
*fbc 
= gc
->m_fbc
; 
  74         wxCHECK_RET( fbc
, _T("invalid GLXFBConfig for OpenGl") ); 
  75         m_glContext 
= glXCreateNewContext( GDK_DISPLAY(), fbc
[0], GLX_RGBA_TYPE
, None
, GL_TRUE 
); 
  80         XVisualInfo 
*vi 
= (XVisualInfo 
*) gc
->m_vi
; 
  81         wxCHECK_RET( vi
, _T("invalid visual for OpenGl") ); 
  82         m_glContext 
= glXCreateContext( GDK_DISPLAY(), vi
, None
, GL_TRUE 
); 
  85     wxCHECK_RET( m_glContext
, _T("Couldn't create OpenGl context") ); 
  88 wxGLContext::wxGLContext( 
  89                bool WXUNUSED(isRGB
), wxWindow 
*win
, 
  90                const wxPalette
& WXUNUSED(palette
), 
  91                const wxGLContext 
*other        
/* for sharing display lists */ 
  95     m_widget 
= win
->m_wxwindow
; 
  97     wxGLCanvas 
*gc 
= (wxGLCanvas
*) win
; 
  99     if (wxGLCanvas::GetGLXVersion() >= 13) 
 102         GLXFBConfig 
*fbc 
= gc
->m_fbc
; 
 103         wxCHECK_RET( fbc
, _T("invalid GLXFBConfig for OpenGl") ); 
 104         m_glContext 
= glXCreateNewContext( GDK_DISPLAY(), fbc
[0], GLX_RGBA_TYPE
, 
 105                                            other 
? other
->m_glContext 
: None
, 
 111         XVisualInfo 
*vi 
= (XVisualInfo 
*) gc
->m_vi
; 
 112         wxCHECK_RET( vi
, _T("invalid visual for OpenGl") ); 
 113         m_glContext 
= glXCreateContext( GDK_DISPLAY(), vi
, 
 114                                         other 
? other
->m_glContext 
: None
, 
 120         wxFAIL_MSG( _T("Couldn't create OpenGl context") ); 
 124 wxGLContext::~wxGLContext() 
 126     if (!m_glContext
) return; 
 128     if (m_glContext 
== glXGetCurrentContext()) 
 130         if (wxGLCanvas::GetGLXVersion() >= 13) 
 132             glXMakeContextCurrent( GDK_DISPLAY(), None
, None
, NULL
); 
 135             glXMakeCurrent( GDK_DISPLAY(), None
, NULL
); 
 138     glXDestroyContext( GDK_DISPLAY(), m_glContext 
); 
 141 void wxGLContext::SwapBuffers() 
 145         GdkWindow 
*window 
= GTK_PIZZA(m_widget
)->bin_window
; 
 146         glXSwapBuffers( GDK_DISPLAY(), GDK_WINDOW_XWINDOW( window 
) ); 
 150 void wxGLContext::SetCurrent() 
 154         GdkWindow 
*window 
= GTK_PIZZA(m_widget
)->bin_window
; 
 156         if (wxGLCanvas::GetGLXVersion() >= 13) 
 158             glXMakeContextCurrent( GDK_DISPLAY(), GDK_WINDOW_XWINDOW(window
), 
 159                                    GDK_WINDOW_XWINDOW(window
), m_glContext 
); 
 162             glXMakeCurrent( GDK_DISPLAY(), GDK_WINDOW_XWINDOW(window
), m_glContext 
); 
 166 void wxGLContext::SetColour(const wxChar 
*colour
) 
 168     wxColour col 
= wxTheColourDatabase
->Find(colour
); 
 171         float r 
= (float)(col
.Red()/256.0); 
 172         float g 
= (float)(col
.Green()/256.0); 
 173         float b 
= (float)(col
.Blue()/256.0); 
 178 void wxGLContext::SetupPixelFormat() 
 182 void wxGLContext::SetupPalette( const wxPalette
& WXUNUSED(palette
) ) 
 186 wxPalette 
wxGLContext::CreateDefaultPalette() 
 188     return wxNullPalette
; 
 191 //----------------------------------------------------------------------------- 
 192 // "realize" from m_wxwindow 
 193 //----------------------------------------------------------------------------- 
 197 gtk_glwindow_realized_callback( GtkWidget 
*WXUNUSED(widget
), wxGLCanvas 
*win 
) 
 199     if ( !win
->m_glContext 
) 
 201         wxGLContext 
*share 
= win
->m_sharedContext
; 
 202         if ( !share 
&& win
->m_sharedContextOf 
) 
 203             share 
= win
->m_sharedContextOf
->GetContext(); 
 205         win
->m_glContext 
= new wxGLContext( TRUE
, win
, wxNullPalette
, share 
); 
 212 //----------------------------------------------------------------------------- 
 213 // "map" from m_wxwindow 
 214 //----------------------------------------------------------------------------- 
 218 gtk_glwindow_map_callback( GtkWidget 
* WXUNUSED(widget
), wxGLCanvas 
*win 
) 
 220     if (win
->m_glContext
/* && win->m_exposed*/) 
 222         wxPaintEvent 
event( win
->GetId() ); 
 223         event
.SetEventObject( win 
); 
 224         win
->GetEventHandler()->ProcessEvent( event 
); 
 226         win
->m_exposed 
= FALSE
; 
 227         win
->GetUpdateRegion().Clear(); 
 234 //----------------------------------------------------------------------------- 
 235 // "expose_event" of m_wxwindow 
 236 //----------------------------------------------------------------------------- 
 240 gtk_glwindow_expose_callback( GtkWidget 
*WXUNUSED(widget
), GdkEventExpose 
*gdk_event
, wxGLCanvas 
*win 
) 
 243         wxapp_install_idle_handler(); 
 245     win
->m_exposed 
= TRUE
; 
 247     win
->GetUpdateRegion().Union( gdk_event
->area
.x
, 
 249                                   gdk_event
->area
.width
, 
 250                                   gdk_event
->area
.height 
); 
 254 //----------------------------------------------------------------------------- 
 255 // "draw" of m_wxwindow 
 256 //----------------------------------------------------------------------------- 
 261 gtk_glwindow_draw_callback( GtkWidget 
*WXUNUSED(widget
), GdkRectangle 
*rect
, wxGLCanvas 
*win 
) 
 264         wxapp_install_idle_handler(); 
 266     win
->m_exposed 
= TRUE
; 
 268     win
->GetUpdateRegion().Union( rect
->x
, rect
->y
, 
 269                                   rect
->width
, rect
->height 
); 
 274 //----------------------------------------------------------------------------- 
 275 // "size_allocate" of m_wxwindow 
 276 //----------------------------------------------------------------------------- 
 280 gtk_glcanvas_size_callback( GtkWidget 
*WXUNUSED(widget
), GtkAllocation
* alloc
, wxGLCanvas 
*win 
) 
 283         wxapp_install_idle_handler(); 
 288     wxSizeEvent 
event( wxSize(win
->m_width
,win
->m_height
), win
->GetId() ); 
 289     event
.SetEventObject( win 
); 
 290     win
->GetEventHandler()->ProcessEvent( event 
); 
 294 //--------------------------------------------------------------------------- 
 296 //--------------------------------------------------------------------------- 
 298 IMPLEMENT_CLASS(wxGLCanvas
, wxWindow
) 
 300 BEGIN_EVENT_TABLE(wxGLCanvas
, wxWindow
) 
 301     EVT_SIZE(wxGLCanvas::OnSize
) 
 304 wxGLCanvas::wxGLCanvas( wxWindow 
*parent
, wxWindowID id
, 
 305                         const wxPoint
& pos
, const wxSize
& size
, 
 306                         long style
, const wxString
& name
, 
 308                         const wxPalette
& palette 
) 
 310     Create( parent
, NULL
, NULL
, id
, pos
, size
, style
, name
, attribList
, palette 
); 
 313 wxGLCanvas::wxGLCanvas( wxWindow 
*parent
, 
 314                         const wxGLContext 
*shared
, 
 316                         const wxPoint
& pos
, const wxSize
& size
, 
 317                         long style
, const wxString
& name
, 
 319                         const wxPalette
& palette 
) 
 321     Create( parent
, shared
, NULL
, id
, pos
, size
, style
, name
, attribList
, palette 
); 
 324 wxGLCanvas::wxGLCanvas( wxWindow 
*parent
, 
 325                         const wxGLCanvas 
*shared
, 
 327                         const wxPoint
& pos
, const wxSize
& size
, 
 328                         long style
, const wxString
& name
, 
 330                         const wxPalette
& palette 
) 
 332     Create( parent
, NULL
, shared
, id
, pos
, size
, style
, name
, attribList
, palette 
); 
 335 bool wxGLCanvas::Create( wxWindow 
*parent
, 
 336                          const wxGLContext 
*shared
, 
 337                          const wxGLCanvas 
*shared_context_of
, 
 339                          const wxPoint
& pos
, const wxSize
& size
, 
 340                          long style
, const wxString
& name
, 
 342                          const wxPalette
& palette
) 
 344     m_sharedContext 
= (wxGLContext
*)shared
;  // const_cast 
 345     m_sharedContextOf 
= (wxGLCanvas
*)shared_context_of
;  // const_cast 
 346     m_glContext 
= (wxGLContext
*) NULL
; 
 350     m_nativeSizeEvent 
= TRUE
; 
 354     // to be sure the glx version is known 
 355     wxGLCanvas::QueryGLXVersion(); 
 357     if (wxGLCanvas::GetGLXVersion() >= 13) 
 359         // GLX >= 1.3 uses a GLXFBConfig 
 360         GLXFBConfig 
* fbc 
= NULL
; 
 361         if (wxTheApp
->m_glFBCInfo 
!= NULL
) 
 363             fbc 
= (GLXFBConfig 
*) wxTheApp
->m_glFBCInfo
; 
 364             m_canFreeFBC 
= FALSE
; // owned by wxTheApp - don't free upon destruction 
 368             fbc 
= (GLXFBConfig 
*) wxGLCanvas::ChooseGLFBC(attribList
); 
 371         m_fbc 
= fbc
;  // save for later use 
 372         wxCHECK_MSG( m_fbc
, FALSE
, _T("required FBConfig couldn't be found") ); 
 375     XVisualInfo 
*vi 
= NULL
; 
 376     if (wxTheApp
->m_glVisualInfo 
!= NULL
) 
 378         vi 
= (XVisualInfo 
*)wxTheApp
->m_glVisualInfo
; 
 379         m_canFreeVi 
= FALSE
; // owned by wxTheApp - don't free upon destruction 
 383         if (wxGLCanvas::GetGLXVersion() >= 13) 
 385             vi 
= glXGetVisualFromFBConfig(GDK_DISPLAY(), m_fbc
[0]); 
 388             vi 
= (XVisualInfo 
*) ChooseGLVisual(attribList
); 
 393     m_vi 
= vi
;  // save for later use 
 395     wxCHECK_MSG( m_vi
, FALSE
, _T("required visual couldn't be found") ); 
 397     GdkColormap 
*colormap
; 
 399     // MR: This needs a fix for lower gtk+ versions too. Might need to rethink logic (FIXME) 
 400 #if defined(__WXGTK20__) && GTK_CHECK_VERSION(2,2,0) 
 401     if (!gtk_check_version(2,2,0)) 
 403         wxWindow::Create( parent
, id
, pos
, size
, style
, name 
); 
 405         m_glWidget 
= m_wxwindow
; 
 407         GdkScreen 
*screen 
= gtk_widget_get_screen( m_glWidget 
); 
 408         colormap 
= gdk_screen_get_default_colormap(screen
); 
 409         visual 
= gdk_colormap_get_visual(colormap
); 
 411         if (GDK_VISUAL_XVISUAL(visual
)->visualid 
!= vi
->visualid
) 
 413             visual 
= gdk_x11_screen_lookup_visual( screen
, vi
->visualid 
); 
 414             colormap 
= gdk_colormap_new(visual
, FALSE
); 
 417         gtk_widget_set_colormap( m_glWidget
, colormap 
); 
 422         visual 
= gdkx_visual_get( vi
->visualid 
); 
 423         colormap 
= gdk_colormap_new( visual
, TRUE 
); 
 425         gtk_widget_push_colormap( colormap 
); 
 426         gtk_widget_push_visual( visual 
); 
 428         wxWindow::Create( parent
, id
, pos
, size
, style
, name 
); 
 429         m_glWidget 
= m_wxwindow
; 
 433     gtk_widget_set_double_buffered( m_glWidget
, FALSE 
); 
 436     gtk_pizza_set_clear( GTK_PIZZA(m_wxwindow
), FALSE 
); 
 438     gtk_signal_connect( GTK_OBJECT(m_wxwindow
), "realize", 
 439                             GTK_SIGNAL_FUNC(gtk_glwindow_realized_callback
), (gpointer
) this ); 
 441     gtk_signal_connect( GTK_OBJECT(m_wxwindow
), "map", 
 442                             GTK_SIGNAL_FUNC(gtk_glwindow_map_callback
), (gpointer
) this ); 
 444     gtk_signal_connect( GTK_OBJECT(m_wxwindow
), "expose_event", 
 445         GTK_SIGNAL_FUNC(gtk_glwindow_expose_callback
), (gpointer
)this ); 
 448     gtk_signal_connect( GTK_OBJECT(m_wxwindow
), "draw", 
 449         GTK_SIGNAL_FUNC(gtk_glwindow_draw_callback
), (gpointer
)this ); 
 452     gtk_signal_connect( GTK_OBJECT(m_widget
), "size_allocate", 
 453         GTK_SIGNAL_FUNC(gtk_glcanvas_size_callback
), (gpointer
)this ); 
 456     if (gtk_check_version(2,2,0) != NULL
) 
 459         gtk_widget_pop_visual(); 
 460         gtk_widget_pop_colormap(); 
 463     // if our parent window is already visible, we had been realized before we 
 464     // connected to the "realize" signal and hence our m_glContext hasn't been 
 465     // initialized yet and we have to do it now 
 466     if (GTK_WIDGET_REALIZED(m_wxwindow
)) 
 467         gtk_glwindow_realized_callback( m_wxwindow
, this ); 
 469     if (GTK_WIDGET_MAPPED(m_wxwindow
)) 
 470         gtk_glwindow_map_callback( m_wxwindow
, this ); 
 475 wxGLCanvas::~wxGLCanvas() 
 477     GLXFBConfig 
* fbc 
= (GLXFBConfig 
*) m_fbc
; 
 478     if (fbc 
&& m_canFreeFBC
) 
 481     XVisualInfo 
*vi 
= (XVisualInfo 
*) m_vi
; 
 482     if (vi 
&& m_canFreeVi
) 
 488 void* wxGLCanvas::ChooseGLVisual(int *attribList
) 
 491     GetGLAttribListFromWX( attribList
, data 
); 
 492     attribList 
= (int*) data
; 
 494     Display 
*dpy 
= GDK_DISPLAY(); 
 496     return glXChooseVisual( dpy
, DefaultScreen(dpy
), attribList 
); 
 499 void* wxGLCanvas::ChooseGLFBC(int *attribList
) 
 502     GetGLAttribListFromWX( attribList
, data 
); 
 503     attribList 
= (int*) data
; 
 506     return glXChooseFBConfig( GDK_DISPLAY(), DefaultScreen(GDK_DISPLAY()), 
 507                               attribList
, &returned 
); 
 511 void wxGLCanvas::GetGLAttribListFromWX(int *wx_attribList
, int *gl_attribList 
) 
 515         if (wxGLCanvas::GetGLXVersion() >= 13) 
 516         // leave GLX >= 1.3 choose the default attributes 
 517             gl_attribList
[0] = 0; 
 521             // default settings if attriblist = 0 
 522             gl_attribList
[i
++] = GLX_RGBA
; 
 523             gl_attribList
[i
++] = GLX_DOUBLEBUFFER
; 
 524             gl_attribList
[i
++] = GLX_DEPTH_SIZE
;   gl_attribList
[i
++] = 1; 
 525             gl_attribList
[i
++] = GLX_RED_SIZE
;     gl_attribList
[i
++] = 1; 
 526             gl_attribList
[i
++] = GLX_GREEN_SIZE
;   gl_attribList
[i
++] = 1; 
 527             gl_attribList
[i
++] = GLX_BLUE_SIZE
;    gl_attribList
[i
++] = 1; 
 528             gl_attribList
[i
++] = GLX_ALPHA_SIZE
;   gl_attribList
[i
++] = 0; 
 529             gl_attribList
[i
++] = None
; 
 535         while( (wx_attribList
[arg
]!=0) && (p
<510) ) 
 537             switch( wx_attribList
[arg
++] ) 
 540                     if (wxGLCanvas::GetGLXVersion() <= 12) 
 541                         // for GLX >= 1.3, GLX_RGBA is useless (setting this flags will crash on most opengl implm) 
 542                         gl_attribList
[p
++] = GLX_RGBA
; 
 544                 case WX_GL_BUFFER_SIZE
: 
 545                     gl_attribList
[p
++] = GLX_BUFFER_SIZE
; 
 546                     gl_attribList
[p
++] = wx_attribList
[arg
++]; 
 549                     gl_attribList
[p
++] = GLX_LEVEL
; 
 550                     gl_attribList
[p
++] = wx_attribList
[arg
++]; 
 552                 case WX_GL_DOUBLEBUFFER
: 
 553                     if (wxGLCanvas::GetGLXVersion() <= 12) 
 554                         gl_attribList
[p
++] = GLX_DOUBLEBUFFER
; 
 556                         // for GLX >= 1.3, GLX_DOUBLEBUFFER format is different (1 <=> True) 
 557                         // it seems this flag is useless for some hardware opengl implementation. 
 558                         // but for Mesa 6.2.1, this flag is used so don't ignore it. 
 559                         gl_attribList
[p
++] = GLX_DOUBLEBUFFER
; 
 560                     gl_attribList
[p
++] = 1; 
 563                     gl_attribList
[p
++] = GLX_STEREO
; 
 565                 case WX_GL_AUX_BUFFERS
: 
 566                     gl_attribList
[p
++] = GLX_AUX_BUFFERS
; 
 567                     gl_attribList
[p
++] = wx_attribList
[arg
++]; 
 570                     gl_attribList
[p
++] = GLX_RED_SIZE
; 
 571                     gl_attribList
[p
++] = wx_attribList
[arg
++]; 
 573                 case WX_GL_MIN_GREEN
: 
 574                     gl_attribList
[p
++] = GLX_GREEN_SIZE
; 
 575                     gl_attribList
[p
++] = wx_attribList
[arg
++]; 
 578                     gl_attribList
[p
++] = GLX_BLUE_SIZE
; 
 579                     gl_attribList
[p
++] = wx_attribList
[arg
++]; 
 581                 case WX_GL_MIN_ALPHA
: 
 582                     gl_attribList
[p
++] = GLX_ALPHA_SIZE
; 
 583                     gl_attribList
[p
++] = wx_attribList
[arg
++]; 
 585                 case WX_GL_DEPTH_SIZE
: 
 586                     gl_attribList
[p
++] = GLX_DEPTH_SIZE
; 
 587                     gl_attribList
[p
++] = wx_attribList
[arg
++]; 
 589                 case WX_GL_STENCIL_SIZE
: 
 590                     gl_attribList
[p
++] = GLX_STENCIL_SIZE
; 
 591                     gl_attribList
[p
++] = wx_attribList
[arg
++]; 
 593                 case WX_GL_MIN_ACCUM_RED
: 
 594                     gl_attribList
[p
++] = GLX_ACCUM_RED_SIZE
; 
 595                     gl_attribList
[p
++] = wx_attribList
[arg
++]; 
 597                 case WX_GL_MIN_ACCUM_GREEN
: 
 598                     gl_attribList
[p
++] = GLX_ACCUM_GREEN_SIZE
; 
 599                     gl_attribList
[p
++] = wx_attribList
[arg
++]; 
 601                 case WX_GL_MIN_ACCUM_BLUE
: 
 602                     gl_attribList
[p
++] = GLX_ACCUM_BLUE_SIZE
; 
 603                     gl_attribList
[p
++] = wx_attribList
[arg
++]; 
 605                 case WX_GL_MIN_ACCUM_ALPHA
: 
 606                     gl_attribList
[p
++] = GLX_ACCUM_ALPHA_SIZE
; 
 607                     gl_attribList
[p
++] = wx_attribList
[arg
++]; 
 614         gl_attribList
[p
] = 0; 
 618 void wxGLCanvas::QueryGLXVersion() 
 620     if (m_glxVersion 
== 0) 
 622         // check the GLX version 
 623         int glxMajorVer
, glxMinorVer
; 
 624         bool ok 
= glXQueryVersion(GDK_DISPLAY(), &glxMajorVer
, &glxMinorVer
); 
 625         wxASSERT_MSG( ok
, _T("GLX version not found") ); 
 627             m_glxVersion 
= 10; // 1.0 by default 
 629             m_glxVersion 
= glxMajorVer
*10 + glxMinorVer
; 
 633 int wxGLCanvas::GetGLXVersion() 
 635     wxASSERT_MSG( m_glxVersion
>0, _T("GLX version has not been initialized with wxGLCanvas::QueryGLXVersion()") ); 
 640 void wxGLCanvas::SwapBuffers() 
 643         m_glContext
->SwapBuffers(); 
 646 void wxGLCanvas::OnSize(wxSizeEvent
& WXUNUSED(event
)) 
 650 void wxGLCanvas::SetCurrent() 
 653         m_glContext
->SetCurrent(); 
 656 void wxGLCanvas::SetColour( const wxChar 
*colour 
) 
 659         m_glContext
->SetColour( colour 
); 
 662 void wxGLCanvas::OnInternalIdle() 
 664     if (m_glContext 
&& m_exposed
) 
 666         wxPaintEvent 
event( GetId() ); 
 667         event
.SetEventObject( this ); 
 668         GetEventHandler()->ProcessEvent( event 
); 
 671         GetUpdateRegion().Clear(); 
 674     wxWindow::OnInternalIdle(); 
 679 //--------------------------------------------------------------------------- 
 681 //--------------------------------------------------------------------------- 
 683 IMPLEMENT_CLASS(wxGLApp
, wxApp
) 
 690         XFree(m_glVisualInfo
); 
 693 bool wxGLApp::InitGLVisual(int *attribList
) 
 695     wxGLCanvas::QueryGLXVersion(); 
 697     if (wxGLCanvas::GetGLXVersion() >= 13) 
 702         m_glFBCInfo 
= wxGLCanvas::ChooseGLFBC(attribList
); 
 707                 XFree(m_glVisualInfo
); 
 708             m_glVisualInfo 
= glXGetVisualFromFBConfig(GDK_DISPLAY(), ((GLXFBConfig 
*)m_glFBCInfo
)[0]); 
 710         return (m_glFBCInfo 
!= NULL
) && (m_glVisualInfo 
!= NULL
); 
 716             XFree(m_glVisualInfo
); 
 717         m_glVisualInfo 
= wxGLCanvas::ChooseGLVisual(attribList
); 
 718         return m_glVisualInfo 
!= NULL
;