Browse Source

X support

David Rose 17 years ago
parent
commit
19e0b0824c

+ 4 - 2
panda/src/tinydisplay/Sources.pp

@@ -14,11 +14,13 @@
   #define SOURCES \
     config_tinydisplay.cxx config_tinydisplay.h \
     tinyGeomMunger.I tinyGeomMunger.cxx tinyGeomMunger.h \
-    tinyGraphicsPipe.I tinyGraphicsPipe.cxx tinyGraphicsPipe.h \
-    tinyGraphicsWindow.h tinyGraphicsWindow.I tinyGraphicsWindow.cxx \
+    tinySDLGraphicsPipe.I tinySDLGraphicsPipe.cxx tinySDLGraphicsPipe.h \
+    tinySDLGraphicsWindow.h tinySDLGraphicsWindow.I tinySDLGraphicsWindow.cxx \
     tinyGraphicsStateGuardian.h tinyGraphicsStateGuardian.I \
     tinyGraphicsStateGuardian.cxx \
     tinyTextureContext.I tinyTextureContext.cxx tinyTextureContext.h \
+    tinyXGraphicsPipe.I tinyXGraphicsPipe.cxx tinyXGraphicsPipe.h \
+    tinyXGraphicsWindow.h tinyXGraphicsWindow.I tinyXGraphicsWindow.cxx \
     api.c arrays.c clear.c clip.c error.c get.c \
     glu.c image_util.c init.c light.c list.c \
     matrix.c memory.c misc.c msghandling.c msghandling.h \

+ 39 - 6
panda/src/tinydisplay/config_tinydisplay.cxx

@@ -17,8 +17,10 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "config_tinydisplay.h"
-#include "tinyGraphicsPipe.h"
-#include "tinyGraphicsWindow.h"
+#include "tinySDLGraphicsPipe.h"
+#include "tinySDLGraphicsWindow.h"
+#include "tinyXGraphicsPipe.h"
+#include "tinyXGraphicsWindow.h"
 #include "tinyGraphicsStateGuardian.h"
 #include "tinyGeomMunger.h"
 #include "tinyTextureContext.h"
@@ -33,6 +35,29 @@ ConfigureFn(config_tinydisplay) {
   init_libtinydisplay();
 }
 
+ConfigVariableString display_cfg
+("display", "",
+ PRC_DESC("Specify the X display string for the default display.  If this "
+          "is not specified, $DISPLAY is used."));
+
+ConfigVariableBool x_error_abort
+("x-error-abort", false,
+ PRC_DESC("Set this true to trigger and abort (and a stack trace) on receipt "
+          "of an error from the X window system.  This can make it easier "
+          "to discover where these errors are generated."));
+
+ConfigVariableInt x_wheel_up_button
+("x-wheel-up-button", 4,
+ PRC_DESC("This is the mouse button index of the wheel_up event: which "
+          "mouse button number does the system report when the mouse wheel "
+          "is rolled one notch up?"));
+
+ConfigVariableInt x_wheel_down_button
+("x-wheel-down-button", 5,
+ PRC_DESC("This is the mouse button index of the wheel_down event: which "
+          "mouse button number does the system report when the mouse wheel "
+          "is rolled one notch down?"));
+
 ////////////////////////////////////////////////////////////////////
 //     Function: init_libtinydisplay
 //  Description: Initializes the library.  This must be called at
@@ -49,15 +74,23 @@ init_libtinydisplay() {
   }
   initialized = true;
 
-  TinyGraphicsPipe::init_type();
-  TinyGraphicsWindow::init_type();
   TinyGraphicsStateGuardian::init_type();
   TinyGeomMunger::init_type();
   TinyTextureContext::init_type();
 
   GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr();
-  selection->add_pipe_type(TinyGraphicsPipe::get_class_type(),
-                           TinyGraphicsPipe::pipe_constructor);
+
+#ifdef IS_LINUX
+  TinyXGraphicsPipe::init_type();
+  TinyXGraphicsWindow::init_type();
+  selection->add_pipe_type(TinyXGraphicsPipe::get_class_type(),
+                           TinyXGraphicsPipe::pipe_constructor);
+#endif
+
+  TinySDLGraphicsPipe::init_type();
+  TinySDLGraphicsWindow::init_type();
+  selection->add_pipe_type(TinySDLGraphicsPipe::get_class_type(),
+                           TinySDLGraphicsPipe::pipe_constructor);
 
   PandaSystem *ps = PandaSystem::get_global_ptr();
   ps->add_system("TinyGL");

+ 8 - 0
panda/src/tinydisplay/config_tinydisplay.h

@@ -21,9 +21,17 @@
 
 #include "pandabase.h"
 #include "notifyCategoryProxy.h"
+#include "configVariableString.h"
+#include "configVariableBool.h"
+#include "configVariableInt.h"
 
 NotifyCategoryDecl(tinydisplay, EXPCL_PANDAGL, EXPTP_PANDAGL);
 
 extern EXPCL_PANDAGL void init_libtinydisplay();
 
+extern ConfigVariableString display_cfg;
+extern ConfigVariableBool x_error_abort;
+extern ConfigVariableInt x_wheel_up_button;
+extern ConfigVariableInt x_wheel_down_button;
+
 #endif

+ 14 - 10
panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx

@@ -37,9 +37,9 @@ static const ZB_fillTriangleFunc fill_tri_funcs
 [2 /* depth write: zon, zoff */]
 [3 /* color write: noblend, blend, nocolor */]
 [3 /* alpha test: anone, aless, amore */]
-[2 /* ztest: znone, zless */]
-[3 /* white, flat, smooth */]
-[3 /* untextured, textured, perspective textured */] = {
+[2 /* depth test: znone, zless */]
+[3 /* shading: white, flat, smooth */]
+[3 /* texturing: untextured, textured, perspective textured */] = {
   { // depth write zon
     { // color write noblend
       { // alpha test anone
@@ -586,6 +586,8 @@ reset() {
   free_pointers();
   GraphicsStateGuardian::reset();
 
+  glInit(_current_frame_buffer);
+
   _c = gl_get_context();
 
   _c->draw_triangle_front = gl_draw_triangle_fill;
@@ -811,6 +813,8 @@ begin_frame(Thread *current_thread) {
     return false;
   }
 
+  _c->zb = _current_frame_buffer;
+
 #ifdef DO_PSTATS
   _vertices_immediate_pcollector.clear_level();
 #endif
@@ -1151,26 +1155,26 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader,
     // as well use the flat shading model.
     shade_model = ShadeModelAttrib::M_flat;
   }
-  int color_state = 2;  // smooth
+  int shading_state = 2;  // smooth
   if (shade_model == ShadeModelAttrib::M_flat) {
-    color_state = 1;  // flat
+    shading_state = 1;  // flat
     if (_c->current_color.X == 1.0f &&
         _c->current_color.Y == 1.0f &&
         _c->current_color.Z == 1.0f &&
         _c->current_color.W == 1.0f) {
-      color_state = 0;  // white
+      shading_state = 0;  // white
     }
   }
 
-  int texture_state = 0;  // untextured
+  int texturing_state = 0;  // untextured
   if (_c->texture_2d_enabled) {
-    texture_state = 2;  // perspective-correct textures
+    texturing_state = 2;  // perspective-correct textures
     if (_c->matrix_model_projection_no_w_transform) {
-      texture_state = 1;  // non-perspective-correct textures
+      texturing_state = 1;  // non-perspective-correct textures
     }
   }
 
-  _c->zb_fill_tri = fill_tri_funcs[depth_write_state][color_write_state][alpha_test_state][depth_test_state][color_state][texture_state];
+  _c->zb_fill_tri = fill_tri_funcs[depth_write_state][color_write_state][alpha_test_state][depth_test_state][shading_state][texturing_state];
   //_c->zb_fill_tri = ZB_fillTriangleFlat_zless;
   
   return true;

+ 8 - 4
panda/src/tinydisplay/tinyGraphicsStateGuardian.h

@@ -22,11 +22,12 @@
 #include "pandabase.h"
 
 #include "graphicsStateGuardian.h"
-#include "tinyGraphicsPipe.h"
+#include "tinySDLGraphicsPipe.h"
 #include "tinygl.h"
 
 extern "C" {
   #include "zmath.h"
+  #include "zbuffer.h"
 }
 
 class TinyTextureContext;
@@ -34,11 +35,10 @@ struct GLContext;
 struct GLVertex;
 struct GLImage;
 
-
 ////////////////////////////////////////////////////////////////////
 //       Class : TinyGraphicsStateGuardian
-// Description : An interface to TinySDGL (an implementation of TinyGL
-//               over SDL).
+// Description : An interface to the TinyGL software rendering code
+//               within this module.
 ////////////////////////////////////////////////////////////////////
 class TinyGraphicsStateGuardian : public GraphicsStateGuardian {
 public:
@@ -118,6 +118,10 @@ private:
 
   INLINE static GLenum get_light_id(int index);
 
+public:
+  // Filled in by the Tiny*GraphicsWindow at begin_frame().
+  ZBuffer *_current_frame_buffer;
+
 private:
   GLContext *_c;
 

+ 1 - 1
panda/src/tinydisplay/tinyGraphicsPipe.I → panda/src/tinydisplay/tinySDLGraphicsPipe.I

@@ -1,4 +1,4 @@
-// Filename: tinyGraphicsPipe.I
+// Filename: tinySDLGraphicsPipe.I
 // Created by:  drose (24Apr08)
 //
 ////////////////////////////////////////////////////////////////////

+ 23 - 23
panda/src/tinydisplay/tinyGraphicsPipe.cxx → panda/src/tinydisplay/tinySDLGraphicsPipe.cxx

@@ -1,4 +1,4 @@
-// Filename: tinyGraphicsPipe.cxx
+// Filename: tinySDLGraphicsPipe.cxx
 // Created by:  drose (24Apr08)
 //
 ////////////////////////////////////////////////////////////////////
@@ -16,21 +16,21 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-#include "tinyGraphicsPipe.h"
-#include "tinyGraphicsWindow.h"
+#include "tinySDLGraphicsPipe.h"
+#include "tinySDLGraphicsWindow.h"
 #include "tinyGraphicsStateGuardian.h"
 #include "config_tinydisplay.h"
 #include "frameBufferProperties.h"
 
-TypeHandle TinyGraphicsPipe::_type_handle;
+TypeHandle TinySDLGraphicsPipe::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TinyGraphicsPipe::Constructor
+//     Function: TinySDLGraphicsPipe::Constructor
 //       Access: Public
 //  Description: 
 ////////////////////////////////////////////////////////////////////
-TinyGraphicsPipe::
-TinyGraphicsPipe() {
+TinySDLGraphicsPipe::
+TinySDLGraphicsPipe() {
   _is_valid = true;
 
   if (SDL_Init(SDL_INIT_VIDEO) < 0) {
@@ -41,12 +41,12 @@ TinyGraphicsPipe() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TinyGraphicsPipe::Destructor
+//     Function: TinySDLGraphicsPipe::Destructor
 //       Access: Public, Virtual
 //  Description: 
 ////////////////////////////////////////////////////////////////////
-TinyGraphicsPipe::
-~TinyGraphicsPipe() {
+TinySDLGraphicsPipe::
+~TinySDLGraphicsPipe() {
   if (SDL_WasInit(SDL_INIT_VIDEO)) {
     SDL_QuitSubSystem(SDL_INIT_VIDEO);
   }
@@ -55,7 +55,7 @@ TinyGraphicsPipe::
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TinyGraphicsPipe::get_interface_name
+//     Function: TinySDLGraphicsPipe::get_interface_name
 //       Access: Published, Virtual
 //  Description: Returns the name of the rendering interface
 //               associated with this GraphicsPipe.  This is used to
@@ -64,29 +64,29 @@ TinyGraphicsPipe::
 //               particular platform, so the name should be meaningful
 //               and unique for a given platform.
 ////////////////////////////////////////////////////////////////////
-string TinyGraphicsPipe::
+string TinySDLGraphicsPipe::
 get_interface_name() const {
   return "TinyGL";
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TinyGraphicsPipe::pipe_constructor
+//     Function: TinySDLGraphicsPipe::pipe_constructor
 //       Access: Public, Static
 //  Description: This function is passed to the GraphicsPipeSelection
 //               object to allow the user to make a default
-//               TinyGraphicsPipe.
+//               TinySDLGraphicsPipe.
 ////////////////////////////////////////////////////////////////////
-PT(GraphicsPipe) TinyGraphicsPipe::
+PT(GraphicsPipe) TinySDLGraphicsPipe::
 pipe_constructor() {
-  return new TinyGraphicsPipe;
+  return new TinySDLGraphicsPipe;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TinyGraphicsPipe::make_output
+//     Function: TinySDLGraphicsPipe::make_output
 //       Access: Protected, Virtual
 //  Description: Creates a new window on the pipe, if possible.
 ////////////////////////////////////////////////////////////////////
-PT(GraphicsOutput) TinyGraphicsPipe::
+PT(GraphicsOutput) TinySDLGraphicsPipe::
 make_output(const string &name,
             const FrameBufferProperties &fb_prop,
             const WindowProperties &win_prop,
@@ -99,12 +99,12 @@ make_output(const string &name,
     return NULL;
   }
 
-  TinyGraphicsStateGuardian *glxgsg = 0;
+  TinyGraphicsStateGuardian *tinygsg = 0;
   if (gsg != 0) {
-    DCAST_INTO_R(glxgsg, gsg, NULL);
+    DCAST_INTO_R(tinygsg, gsg, NULL);
   }
 
-  // First thing to try: a TinyGraphicsWindow
+  // First thing to try: a TinySDLGraphicsWindow
 
   if (retry == 0) {
     if (((flags&BF_require_parasite)!=0)||
@@ -116,8 +116,8 @@ make_output(const string &name,
         ((flags&BF_can_bind_every)!=0)) {
       return NULL;
     }
-    return new TinyGraphicsWindow(this, name, fb_prop, win_prop,
-                                  flags, gsg, host);
+    return new TinySDLGraphicsWindow(this, name, fb_prop, win_prop,
+                                     flags, gsg, host);
   }
   
   // Nothing else left to try.

+ 11 - 11
panda/src/tinydisplay/tinyGraphicsPipe.h → panda/src/tinydisplay/tinySDLGraphicsPipe.h

@@ -1,4 +1,4 @@
-// Filename: tinyGraphicsPipe.h
+// Filename: tinySDLGraphicsPipe.h
 // Created by:  drose (24Apr08)
 //
 ////////////////////////////////////////////////////////////////////
@@ -16,8 +16,8 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-#ifndef TINYGRAPHICSPIPE_H
-#define TINYGRAPHICSPIPE_H
+#ifndef TINYSDLGRAPHICSPIPE_H
+#define TINYSDLGRAPHICSPIPE_H
 
 #include "pandabase.h"
 #include "graphicsWindow.h"
@@ -26,14 +26,14 @@
 class FrameBufferProperties;
 
 ////////////////////////////////////////////////////////////////////
-//       Class : TinyGraphicsPipe
-// Description : This graphics pipe represents the interface for
-//               to TinySDGL (an implementation of TinyGL over SDL).
+//       Class : TinySDLGraphicsPipe
+// Description : This graphics pipe manages SDL windows for rendering
+//               TinyGL software buffers.
 ////////////////////////////////////////////////////////////////////
-class TinyGraphicsPipe : public GraphicsPipe {
+class TinySDLGraphicsPipe : public GraphicsPipe {
 public:
-  TinyGraphicsPipe();
-  virtual ~TinyGraphicsPipe();
+  TinySDLGraphicsPipe();
+  virtual ~TinySDLGraphicsPipe();
 
   virtual string get_interface_name() const;
   static PT(GraphicsPipe) pipe_constructor();
@@ -54,7 +54,7 @@ public:
   }
   static void init_type() {
     GraphicsPipe::init_type();
-    register_type(_type_handle, "TinyGraphicsPipe",
+    register_type(_type_handle, "TinySDLGraphicsPipe",
                   GraphicsPipe::get_class_type());
   }
   virtual TypeHandle get_type() const {
@@ -66,6 +66,6 @@ private:
   static TypeHandle _type_handle;
 };
 
-#include "tinyGraphicsPipe.I"
+#include "tinySDLGraphicsPipe.I"
 
 #endif

+ 1 - 1
panda/src/tinydisplay/tinyGraphicsWindow.I → panda/src/tinydisplay/tinySDLGraphicsWindow.I

@@ -1,4 +1,4 @@
-// Filename: tinyGraphicsWindow.I
+// Filename: tinySDLGraphicsWindow.I
 // Created by:  drose (24Apr08)
 //
 ////////////////////////////////////////////////////////////////////

+ 57 - 32
panda/src/tinydisplay/tinyGraphicsWindow.cxx → panda/src/tinydisplay/tinySDLGraphicsWindow.cxx

@@ -1,4 +1,4 @@
-// Filename: tinyGraphicsWindow.cxx
+// Filename: tinySDLGraphicsWindow.cxx
 // Created by:  drose (24Apr08)
 //
 ////////////////////////////////////////////////////////////////////
@@ -16,24 +16,24 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-#include "tinyGraphicsWindow.h"
+#include "tinySDLGraphicsWindow.h"
 #include "tinyGraphicsStateGuardian.h"
 #include "config_tinydisplay.h"
-#include "tinyGraphicsPipe.h"
+#include "tinySDLGraphicsPipe.h"
 #include "mouseButton.h"
 #include "keyboardButton.h"
 #include "graphicsPipe.h"
 #include "tinygl.h"
 
-TypeHandle TinyGraphicsWindow::_type_handle;
+TypeHandle TinySDLGraphicsWindow::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TinyGraphicsWindow::Constructor
+//     Function: TinySDLGraphicsWindow::Constructor
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
-TinyGraphicsWindow::
-TinyGraphicsWindow(GraphicsPipe *pipe, 
+TinySDLGraphicsWindow::
+TinySDLGraphicsWindow(GraphicsPipe *pipe, 
                    const string &name,
                    const FrameBufferProperties &fb_prop,
                    const WindowProperties &win_prop,
@@ -52,16 +52,16 @@ TinyGraphicsWindow(GraphicsPipe *pipe,
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TinyGraphicsWindow::Destructor
+//     Function: TinySDLGraphicsWindow::Destructor
 //       Access: Public, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
-TinyGraphicsWindow::
-~TinyGraphicsWindow() {
+TinySDLGraphicsWindow::
+~TinySDLGraphicsWindow() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TinyGraphicsWindow::begin_frame
+//     Function: TinySDLGraphicsWindow::begin_frame
 //       Access: Public, Virtual
 //  Description: This function will be called within the draw thread
 //               before beginning rendering for a given frame.  It
@@ -69,27 +69,52 @@ TinyGraphicsWindow::
 //               if the frame should be rendered, or false if it
 //               should be skipped.
 ////////////////////////////////////////////////////////////////////
-bool TinyGraphicsWindow::
+bool TinySDLGraphicsWindow::
 begin_frame(FrameMode mode, Thread *current_thread) {
-  return true;
+  begin_frame_spam(mode);
+  if (_gsg == (GraphicsStateGuardian *)NULL) {
+    return false;
+  }
+
+  TinyGraphicsStateGuardian *tinygsg;
+  DCAST_INTO_R(tinygsg, _gsg, false);
+
+  tinygsg->_current_frame_buffer = _frame_buffer;
+  tinygsg->reset_if_new();
+  
+  _gsg->set_current_properties(&get_fb_properties());
+  return _gsg->begin_frame(current_thread);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TinyGraphicsWindow::end_frame
+//     Function: TinySDLGraphicsWindow::end_frame
 //       Access: Public, Virtual
 //  Description: This function will be called within the draw thread
 //               after rendering is completed for a given frame.  It
 //               should do whatever finalization is required.
 ////////////////////////////////////////////////////////////////////
-void TinyGraphicsWindow::
+void TinySDLGraphicsWindow::
 end_frame(FrameMode mode, Thread *current_thread) {
+  end_frame_spam(mode);
+  nassertv(_gsg != (GraphicsStateGuardian *)NULL);
+
+  if (mode == FM_render) {
+    // end_render_texture();
+    copy_to_textures();
+  }
+
+  _gsg->end_frame(current_thread);
+
   if (mode == FM_render) {
     trigger_flip();
+    if (_one_shot) {
+      prepare_for_deletion();
+    }
   }
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TinyGraphicsWindow::begin_flip
+//     Function: TinySDLGraphicsWindow::begin_flip
 //       Access: Public, Virtual
 //  Description: This function will be called within the draw thread
 //               after end_frame() has been called on all windows, to
@@ -102,7 +127,7 @@ end_frame(FrameMode mode, Thread *current_thread) {
 //               end_flip(), to make it easier to flip all of the
 //               windows at the same time.
 ////////////////////////////////////////////////////////////////////
-void TinyGraphicsWindow::
+void TinySDLGraphicsWindow::
 begin_flip() {
   if (SDL_MUSTLOCK(_screen)) {
     if (SDL_LockSurface(_screen) < 0) {
@@ -120,7 +145,7 @@ begin_flip() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TinyGraphicsWindow::process_events
+//     Function: TinySDLGraphicsWindow::process_events
 //       Access: Public, Virtual
 //  Description: Do whatever processing is necessary to ensure that
 //               the window responds to user events.  Also, honor any
@@ -129,7 +154,7 @@ begin_flip() {
 //               This function is called only within the window
 //               thread.
 ////////////////////////////////////////////////////////////////////
-void TinyGraphicsWindow::
+void TinySDLGraphicsWindow::
 process_events() {
   GraphicsWindow::process_events();
 
@@ -195,7 +220,7 @@ process_events() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TinyGraphicsWindow::set_properties_now
+//     Function: TinySDLGraphicsWindow::set_properties_now
 //       Access: Public, Virtual
 //  Description: Applies the requested set of properties to the
 //               window, if possible, for instance to request a change
@@ -211,7 +236,7 @@ process_events() {
 //               derived classes to implement extensions to this
 //               function.
 ////////////////////////////////////////////////////////////////////
-void TinyGraphicsWindow::
+void TinySDLGraphicsWindow::
 set_properties_now(WindowProperties &properties) {
   GraphicsWindow::set_properties_now(properties);
   if (!properties.is_any_specified()) {
@@ -221,25 +246,25 @@ set_properties_now(WindowProperties &properties) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TinyGraphicsWindow::close_window
+//     Function: TinySDLGraphicsWindow::close_window
 //       Access: Protected, Virtual
 //  Description: Closes the window right now.  Called from the window
 //               thread.
 ////////////////////////////////////////////////////////////////////
-void TinyGraphicsWindow::
+void TinySDLGraphicsWindow::
 close_window() {
   glClose();
   GraphicsWindow::close_window();
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TinyGraphicsWindow::open_window
+//     Function: TinySDLGraphicsWindow::open_window
 //       Access: Protected, Virtual
 //  Description: Opens the window right now.  Called from the window
 //               thread.  Returns true if the window is successfully
 //               opened, or false if there was a problem.
 ////////////////////////////////////////////////////////////////////
-bool TinyGraphicsWindow::
+bool TinySDLGraphicsWindow::
 open_window() {
 
   // GSG Creation/Initialization
@@ -278,7 +303,7 @@ open_window() {
     return false;
   }
 
-  glInit(_frame_buffer);
+  tinygsg->_current_frame_buffer = _frame_buffer;
 
   // Now that we have made the context current to a window, we can
   // reset the GSG state if this is the first time it has been used.
@@ -290,12 +315,12 @@ open_window() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TinyGraphicsWindow::create_frame_buffer
+//     Function: TinySDLGraphicsWindow::create_frame_buffer
 //       Access: Private
 //  Description: Creates a suitable frame buffer for the current
 //               window size.
 ////////////////////////////////////////////////////////////////////
-void TinyGraphicsWindow::
+void TinySDLGraphicsWindow::
 create_frame_buffer() {
   if (_frame_buffer != NULL) {
     ZB_close(_frame_buffer);
@@ -329,12 +354,12 @@ create_frame_buffer() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TinyGraphicsWindow::get_keyboard_button
+//     Function: TinySDLGraphicsWindow::get_keyboard_button
 //       Access: Private, Static
 //  Description: Maps from an SDL keysym to the corresponding Panda
 //               ButtonHandle.
 ////////////////////////////////////////////////////////////////////
-ButtonHandle TinyGraphicsWindow::
+ButtonHandle TinySDLGraphicsWindow::
 get_keyboard_button(SDLKey sym) {
   switch (sym) {
   case SDLK_BACKSPACE: return KeyboardButton::backspace();
@@ -478,12 +503,12 @@ get_keyboard_button(SDLKey sym) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TinyGraphicsWindow::get_mouse_button
+//     Function: TinySDLGraphicsWindow::get_mouse_button
 //       Access: Private, Static
 //  Description: Maps from an SDL mouse button index to the
 //               corresponding Panda ButtonHandle.
 ////////////////////////////////////////////////////////////////////
-ButtonHandle TinyGraphicsWindow::
+ButtonHandle TinySDLGraphicsWindow::
 get_mouse_button(Uint8 button) {
   switch (button) {
   case SDL_BUTTON_LEFT:

+ 17 - 18
panda/src/tinydisplay/tinyGraphicsWindow.h → panda/src/tinydisplay/tinySDLGraphicsWindow.h

@@ -1,4 +1,4 @@
-// Filename: tinyGraphicsWindow.h
+// Filename: tinySDLGraphicsWindow.h
 // Created by:  drose (24Apr08)
 //
 ////////////////////////////////////////////////////////////////////
@@ -16,12 +16,12 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-#ifndef TINYGRAPHICSWINDOW_H
-#define TINYGRAPHICSWINDOW_H
+#ifndef TINYSDLGRAPHICSWINDOW_H
+#define TINYSDLGRAPHICSWINDOW_H
 
 #include "pandabase.h"
 
-#include "tinyGraphicsPipe.h"
+#include "tinySDLGraphicsPipe.h"
 #include "graphicsWindow.h"
 #include "buttonHandle.h"
 
@@ -31,20 +31,19 @@ extern "C" {
 }
 
 ////////////////////////////////////////////////////////////////////
-//       Class : TinyGraphicsWindow
-// Description : An interface to TinySDGL (an implementation of TinyGL
-//               over SDL).
+//       Class : TinySDLGraphicsWindow
+// Description : This graphics window class is implemented via SDL.
 ////////////////////////////////////////////////////////////////////
-class TinyGraphicsWindow : public GraphicsWindow {
+class TinySDLGraphicsWindow : public GraphicsWindow {
 public:
-  TinyGraphicsWindow(GraphicsPipe *pipe, 
-                     const string &name,
-                     const FrameBufferProperties &fb_prop,
-                     const WindowProperties &win_prop,
-                     int flags,
-                     GraphicsStateGuardian *gsg,
-                     GraphicsOutput *host);
-  virtual ~TinyGraphicsWindow();
+  TinySDLGraphicsWindow(GraphicsPipe *pipe, 
+                        const string &name,
+                        const FrameBufferProperties &fb_prop,
+                        const WindowProperties &win_prop,
+                        int flags,
+                        GraphicsStateGuardian *gsg,
+                        GraphicsOutput *host);
+  virtual ~TinySDLGraphicsWindow();
 
   virtual bool begin_frame(FrameMode mode, Thread *current_thread);
   virtual void end_frame(FrameMode mode, Thread *current_thread);
@@ -74,7 +73,7 @@ public:
   }
   static void init_type() {
     GraphicsWindow::init_type();
-    register_type(_type_handle, "TinyGraphicsWindow",
+    register_type(_type_handle, "TinySDLGraphicsWindow",
                   GraphicsWindow::get_class_type());
   }
   virtual TypeHandle get_type() const {
@@ -86,6 +85,6 @@ private:
   static TypeHandle _type_handle;
 };
 
-#include "tinyGraphicsWindow.I"
+#include "tinySDLGraphicsWindow.I"
 
 #endif

+ 76 - 0
panda/src/tinydisplay/tinyXGraphicsPipe.I

@@ -0,0 +1,76 @@
+// Filename: tinyXGraphicsPipe.I
+// Created by:  drose (03May08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsPipe::get_display
+//       Access: Public
+//  Description: Returns a pointer to the X display associated with
+//               the pipe: the display on which to create the windows.
+////////////////////////////////////////////////////////////////////
+INLINE Display *TinyXGraphicsPipe::
+get_display() const {
+  return _display;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsPipe::get_screen
+//       Access: Public
+//  Description: Returns the X screen number associated with the pipe.
+////////////////////////////////////////////////////////////////////
+INLINE int TinyXGraphicsPipe::
+get_screen() const {
+  return _screen;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsPipe::get_root
+//       Access: Public
+//  Description: Returns the handle to the root window on the pipe's
+//               display.
+////////////////////////////////////////////////////////////////////
+INLINE Window TinyXGraphicsPipe::
+get_root() const {
+  return _root;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsPipe::get_im
+//       Access: Public
+//  Description: Returns the input method opened for the pipe, or NULL
+//               if the input method could not be opened for some
+//               reason.
+////////////////////////////////////////////////////////////////////
+INLINE XIM TinyXGraphicsPipe::
+get_im() const {
+  return _im;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsPipe::get_hidden_cursor
+//       Access: Public
+//  Description: Returns an invisible Cursor suitable for assigning to
+//               windows that have the cursor_hidden property set.
+////////////////////////////////////////////////////////////////////
+INLINE Cursor TinyXGraphicsPipe::
+get_hidden_cursor() {
+  if (_hidden_cursor == None) {
+    make_hidden_cursor();
+  }
+  return _hidden_cursor;
+}

+ 337 - 0
panda/src/tinydisplay/tinyXGraphicsPipe.cxx

@@ -0,0 +1,337 @@
+// Filename: tinyXGraphicsPipe.cxx
+// Created by:  drose (03May08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pandabase.h"
+#ifdef IS_LINUX
+
+#include "tinyXGraphicsPipe.h"
+#include "tinyXGraphicsWindow.h"
+#include "tinyGraphicsStateGuardian.h"
+#include "config_tinydisplay.h"
+#include "frameBufferProperties.h"
+
+TypeHandle TinyXGraphicsPipe::_type_handle;
+
+bool TinyXGraphicsPipe::_error_handlers_installed = false;
+TinyXGraphicsPipe::ErrorHandlerFunc *TinyXGraphicsPipe::_prev_error_handler;
+TinyXGraphicsPipe::IOErrorHandlerFunc *TinyXGraphicsPipe::_prev_io_error_handler;
+
+ReMutex TinyXGraphicsPipe::_x_mutex;
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsPipe::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+TinyXGraphicsPipe::
+TinyXGraphicsPipe(const string &display) {
+  string display_spec = display;
+  if (display_spec.empty()) {
+    display_spec = display_cfg;
+  }
+  if (display_spec.empty()) {
+    display_spec = ExecutionEnvironment::get_environment_variable("DISPLAY");
+  }
+  if (display_spec.empty()) {
+    display_spec = ":0.0";
+  }
+
+  // The X docs say we should do this to get international character
+  // support from the keyboard.
+  setlocale(LC_ALL, "");
+
+  // But it's important that we use the "C" locale for numeric
+  // formatting, since all of the internal Panda code assumes this--we
+  // need a decimal point to mean a decimal point.
+  setlocale(LC_NUMERIC, "C");
+
+  _is_valid = false;
+  _supported_types = OT_window | OT_buffer | OT_texture_buffer;
+  _display = NULL;
+  _screen = 0;
+  _root = (Window)NULL;
+  _im = (XIM)NULL;
+  _hidden_cursor = None;
+
+  install_error_handlers();
+
+  _display = XOpenDisplay(display_spec.c_str());
+  if (!_display) {
+    tinydisplay_cat.error()
+      << "Could not open display \"" << display_spec << "\".\n";
+    return;
+  }
+
+  if (!XSupportsLocale()) {
+    tinydisplay_cat.warning()
+      << "X does not support locale " << setlocale(LC_ALL, NULL) << "\n";
+  }
+  XSetLocaleModifiers("");
+
+  _screen = DefaultScreen(_display);
+  _root = RootWindow(_display, _screen);
+  _display_width = DisplayWidth(_display, _screen);
+  _display_height = DisplayHeight(_display, _screen);
+  _is_valid = true;
+
+  // Connect to an input method for supporting international text
+  // entry.
+  _im = XOpenIM(_display, NULL, NULL, NULL);
+  if (_im == (XIM)NULL) {
+    tinydisplay_cat.warning()
+      << "Couldn't open input method.\n";
+  }
+
+  // What styles does the current input method support?
+  /*
+  XIMStyles *im_supported_styles;
+  XGetIMValues(_im, XNQueryInputStyle, &im_supported_styles, NULL);
+
+  for (int i = 0; i < im_supported_styles->count_styles; i++) {
+    XIMStyle style = im_supported_styles->supported_styles[i];
+    cerr << "style " << i << ". " << hex << style << dec << "\n";
+  }
+
+  XFree(im_supported_styles);
+  */
+
+  // Get some X atom numbers.
+  _wm_delete_window = XInternAtom(_display, "WM_DELETE_WINDOW", false);
+  _net_wm_window_type = XInternAtom(_display, "_NET_WM_WINDOW_TYPE", false);
+  _net_wm_window_type_splash = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_SPLASH", false);
+  _net_wm_window_type_fullscreen = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_FULLSCREEN", false);
+  _net_wm_state = XInternAtom(_display, "_NET_WM_STATE", false);
+  _net_wm_state_fullscreen = XInternAtom(_display, "_NET_WM_STATE_FULLSCREEN", false);
+  _net_wm_state_above = XInternAtom(_display, "_NET_WM_STATE_ABOVE", false);
+  _net_wm_state_below = XInternAtom(_display, "_NET_WM_STATE_BELOW", false);
+  _net_wm_state_add = XInternAtom(_display, "_NET_WM_STATE_ADD", false);
+  _net_wm_state_remove = XInternAtom(_display, "_NET_WM_STATE_REMOVE", false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsPipe::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+TinyXGraphicsPipe::
+~TinyXGraphicsPipe() {
+  release_hidden_cursor();
+  if (_im) {
+    XCloseIM(_im);
+  }
+  if (_display) {
+    XCloseDisplay(_display);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsPipe::get_interface_name
+//       Access: Published, Virtual
+//  Description: Returns the name of the rendering interface
+//               associated with this GraphicsPipe.  This is used to
+//               present to the user to allow him/her to choose
+//               between several possible GraphicsPipes available on a
+//               particular platform, so the name should be meaningful
+//               and unique for a given platform.
+////////////////////////////////////////////////////////////////////
+string TinyXGraphicsPipe::
+get_interface_name() const {
+  return "OpenGL";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsPipe::pipe_constructor
+//       Access: Public, Static
+//  Description: This function is passed to the GraphicsPipeSelection
+//               object to allow the user to make a default
+//               TinyXGraphicsPipe.
+////////////////////////////////////////////////////////////////////
+PT(GraphicsPipe) TinyXGraphicsPipe::
+pipe_constructor() {
+  return new TinyXGraphicsPipe;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsPipe::get_preferred_window_thread
+//       Access: Public, Virtual
+//  Description: Returns an indication of the thread in which this
+//               GraphicsPipe requires its window processing to be
+//               performed: typically either the app thread (e.g. X)
+//               or the draw thread (Windows).
+////////////////////////////////////////////////////////////////////
+GraphicsPipe::PreferredWindowThread 
+TinyXGraphicsPipe::get_preferred_window_thread() const {
+  // Actually, since we're creating the graphics context in
+  // open_window() now, it appears we need to ensure the open_window()
+  // call is performed in the draw thread for now, even though X wants
+  // all of its calls to be single-threaded.
+
+  // This means that all X windows may have to be handled by the same
+  // draw thread, which we didn't intend (though the global _x_mutex
+  // may allow them to be technically served by different threads,
+  // even though the actual X calls will be serialized).  There might
+  // be a better way.
+
+  return PWT_draw;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsPipe::make_output
+//       Access: Protected, Virtual
+//  Description: Creates a new window on the pipe, if possible.
+////////////////////////////////////////////////////////////////////
+PT(GraphicsOutput) TinyXGraphicsPipe::
+make_output(const string &name,
+            const FrameBufferProperties &fb_prop,
+            const WindowProperties &win_prop,
+            int flags,
+            GraphicsStateGuardian *gsg,
+            GraphicsOutput *host,
+            int retry,
+            bool &precertify) {
+  
+  if (!_is_valid) {
+    return NULL;
+  }
+
+  TinyGraphicsStateGuardian *tinygsg = 0;
+  if (gsg != 0) {
+    DCAST_INTO_R(tinygsg, gsg, NULL);
+  }
+
+  // First thing to try: a TinyXGraphicsWindow
+
+  if (retry == 0) {
+    if (((flags&BF_require_parasite)!=0)||
+        ((flags&BF_refuse_window)!=0)||
+        ((flags&BF_resizeable)!=0)||
+        ((flags&BF_size_track_host)!=0)||
+        ((flags&BF_rtt_cumulative)!=0)||
+        ((flags&BF_can_bind_color)!=0)||
+        ((flags&BF_can_bind_every)!=0)) {
+      return NULL;
+    }
+    return new TinyXGraphicsWindow(this, name, fb_prop, win_prop,
+                                 flags, gsg, host);
+  }
+  
+  // Nothing else left to try.
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsPipe::make_hidden_cursor
+//       Access: Private
+//  Description: Called once to make an invisible Cursor for return
+//               from get_hidden_cursor().
+////////////////////////////////////////////////////////////////////
+void TinyXGraphicsPipe::
+make_hidden_cursor() {
+  nassertv(_hidden_cursor == None);
+
+  unsigned int x_size, y_size;
+  XQueryBestCursor(_display, _root, 1, 1, &x_size, &y_size);
+
+  Pixmap empty = XCreatePixmap(_display, _root, x_size, y_size, 1);
+
+  XColor black;
+  memset(&black, 0, sizeof(black));
+
+  _hidden_cursor = XCreatePixmapCursor(_display, empty, empty, 
+                                       &black, &black, x_size, y_size);
+  XFreePixmap(_display, empty);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsPipe::release_hidden_cursor
+//       Access: Private
+//  Description: Called once to release the invisible cursor created
+//               by make_hidden_cursor().
+////////////////////////////////////////////////////////////////////
+void TinyXGraphicsPipe::
+release_hidden_cursor() {
+  if (_hidden_cursor != None) {
+    XFreeCursor(_display, _hidden_cursor);
+    _hidden_cursor = None;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsPipe::install_error_handlers
+//       Access: Private, Static
+//  Description: Installs new Xlib error handler functions if this is
+//               the first time this function has been called.  These
+//               error handler functions will attempt to reduce Xlib's
+//               annoying tendency to shut down the client at the
+//               first error.  Unfortunately, it is difficult to play
+//               nice with the client if it has already installed its
+//               own error handlers.
+////////////////////////////////////////////////////////////////////
+void TinyXGraphicsPipe::
+install_error_handlers() {
+  if (_error_handlers_installed) {
+    return;
+  }
+
+  _prev_error_handler = (ErrorHandlerFunc *)XSetErrorHandler(error_handler);
+  _prev_io_error_handler = (IOErrorHandlerFunc *)XSetIOErrorHandler(io_error_handler);
+  _error_handlers_installed = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsPipe::error_handler
+//       Access: Private, Static
+//  Description: This function is installed as the error handler for a
+//               non-fatal Xlib error.
+////////////////////////////////////////////////////////////////////
+int TinyXGraphicsPipe::
+error_handler(Display *display, XErrorEvent *error) {
+  static const int msg_len = 80;
+  char msg[msg_len];
+  XGetErrorText(display, error->error_code, msg, msg_len);
+  tinydisplay_cat.error()
+    << msg << "\n";
+
+  if (x_error_abort) {
+    abort();
+  }
+
+  // We return to allow the application to continue running, unlike
+  // the default X error handler which exits.
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsPipe::io_error_handler
+//       Access: Private, Static
+//  Description: This function is installed as the error handler for a
+//               fatal Xlib error.
+////////////////////////////////////////////////////////////////////
+int TinyXGraphicsPipe::
+io_error_handler(Display *display) {
+  tinydisplay_cat.fatal()
+    << "X fatal error on display " << (void *)display << "\n";
+
+  // Unfortunately, we can't continue from this function, even if we
+  // promise never to use X again.  We're supposed to terminate
+  // without returning, and if we do return, the caller will exit
+  // anyway.  Sigh.  Very poor design on X's part.
+  return 0;
+}
+
+#endif  // IS_LINUX

+ 145 - 0
panda/src/tinydisplay/tinyXGraphicsPipe.h

@@ -0,0 +1,145 @@
+// Filename: tinyXGraphicsPipe.h
+// Created by:  drose (03May08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef TINYXGRAPHICSPIPE_H
+#define TINYXGRAPHICSPIPE_H
+
+#include "pandabase.h"
+
+#ifdef IS_LINUX
+
+#include "graphicsWindow.h"
+#include "graphicsPipe.h"
+#include "tinyGraphicsStateGuardian.h"
+#include "pmutex.h"
+#include "reMutex.h"
+
+class FrameBufferProperties;
+
+#ifdef CPPPARSER
+// A simple hack so interrogate can parse this file.
+typedef int Display;
+typedef int Window;
+typedef int XErrorEvent;
+typedef int XVisualInfo;
+typedef int Atom;
+typedef int Cursor;
+typedef int XIM;
+typedef int XIC;
+#else
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#endif  // CPPPARSER
+
+////////////////////////////////////////////////////////////////////
+//       Class : TinyXGraphicsPipe
+// Description : This graphics pipe represents the interface for
+//               creating OpenGL graphics windows on an X-based
+//               (e.g. Unix) client.
+////////////////////////////////////////////////////////////////////
+class TinyXGraphicsPipe : public GraphicsPipe {
+public:
+  TinyXGraphicsPipe(const string &display = string());
+  virtual ~TinyXGraphicsPipe();
+
+  virtual string get_interface_name() const;
+  static PT(GraphicsPipe) pipe_constructor();
+
+  INLINE Display *get_display() const;
+  INLINE int get_screen() const;
+  INLINE Window get_root() const;
+  INLINE XIM get_im() const;
+
+  INLINE Cursor get_hidden_cursor();
+
+public:
+  virtual PreferredWindowThread get_preferred_window_thread() const;
+
+public:
+  // Atom specifications.
+  Atom _wm_delete_window;
+  Atom _net_wm_window_type;
+  Atom _net_wm_window_type_splash;
+  Atom _net_wm_window_type_fullscreen;
+  Atom _net_wm_state;
+  Atom _net_wm_state_fullscreen;
+  Atom _net_wm_state_above;
+  Atom _net_wm_state_below;
+  Atom _net_wm_state_add;
+  Atom _net_wm_state_remove;
+
+protected:
+  virtual PT(GraphicsOutput) make_output(const string &name,
+                                         const FrameBufferProperties &fb_prop,
+                                         const WindowProperties &win_prop,
+                                         int flags,
+                                         GraphicsStateGuardian *gsg,
+                                         GraphicsOutput *host,
+                                         int retry,
+                                         bool &precertify);
+
+private:
+  void make_hidden_cursor();
+  void release_hidden_cursor();
+
+  static void install_error_handlers();
+  static int error_handler(Display *display, XErrorEvent *error);
+  static int io_error_handler(Display *display);
+
+  Display *_display;
+  int _screen;
+  Window _root;
+  XIM _im;
+
+  Cursor _hidden_cursor;
+
+  typedef int ErrorHandlerFunc(Display *, XErrorEvent *);
+  typedef int IOErrorHandlerFunc(Display *);
+  static bool _error_handlers_installed;
+  static ErrorHandlerFunc *_prev_error_handler;
+  static IOErrorHandlerFunc *_prev_io_error_handler;
+
+public:
+  // This Mutex protects any X library calls, which all have to be
+  // single-threaded.
+  static ReMutex _x_mutex;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GraphicsPipe::init_type();
+    register_type(_type_handle, "TinyXGraphicsPipe",
+                  GraphicsPipe::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "tinyXGraphicsPipe.I"
+
+#endif  // IS_LINUX
+
+#endif

+ 18 - 0
panda/src/tinydisplay/tinyXGraphicsWindow.I

@@ -0,0 +1,18 @@
+// Filename: tinyXGraphicsWindow.I
+// Created by:  drose (03May08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 1620 - 0
panda/src/tinydisplay/tinyXGraphicsWindow.cxx

@@ -0,0 +1,1620 @@
+// Filename: tinyXGraphicsWindow.cxx
+// Created by:  drose (03May08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pandabase.h"
+
+#ifdef IS_LINUX
+
+#include "tinyXGraphicsWindow.h"
+#include "tinyGraphicsStateGuardian.h"
+#include "tinyXGraphicsPipe.h"
+#include "config_tinydisplay.h"
+
+#include "graphicsPipe.h"
+#include "keyboardButton.h"
+#include "mouseButton.h"
+#include "clockObject.h"
+#include "pStatTimer.h"
+#include "textEncoder.h"
+#include "throw_event.h"
+#include "reMutexHolder.h"
+
+#include <errno.h>
+#include <sys/time.h>
+#include <X11/keysym.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#ifdef HAVE_LINUX_INPUT_H
+#include <linux/input.h>
+#endif
+
+TypeHandle TinyXGraphicsWindow::_type_handle;
+
+#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)&7)))
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+TinyXGraphicsWindow::
+TinyXGraphicsWindow(GraphicsPipe *pipe, 
+                  const string &name,
+                  const FrameBufferProperties &fb_prop,
+                  const WindowProperties &win_prop,
+                  int flags,
+                  GraphicsStateGuardian *gsg,
+                  GraphicsOutput *host) :
+  GraphicsWindow(pipe, name, fb_prop, win_prop, flags, gsg, host)
+{
+  TinyXGraphicsPipe *tinyx_pipe;
+  DCAST_INTO_V(tinyx_pipe, _pipe);
+  _display = tinyx_pipe->get_display();
+  _screen = tinyx_pipe->get_screen();
+  _xwindow = (Window)NULL;
+  _gc = (GC)NULL;
+  _ic = (XIC)NULL;
+  _awaiting_configure = false;
+  _wm_delete_window = tinyx_pipe->_wm_delete_window;
+  _net_wm_window_type = tinyx_pipe->_net_wm_window_type;
+  _net_wm_window_type_splash = tinyx_pipe->_net_wm_window_type_splash;
+  _net_wm_window_type_fullscreen = tinyx_pipe->_net_wm_window_type_fullscreen;
+  _net_wm_state = tinyx_pipe->_net_wm_state;
+  _net_wm_state_fullscreen = tinyx_pipe->_net_wm_state_fullscreen;
+  _net_wm_state_above = tinyx_pipe->_net_wm_state_above;
+  _net_wm_state_below = tinyx_pipe->_net_wm_state_below;
+  _net_wm_state_add = tinyx_pipe->_net_wm_state_add;
+  _net_wm_state_remove = tinyx_pipe->_net_wm_state_remove;
+
+  _frame_buffer = NULL;
+  _ximage = NULL;
+
+  GraphicsWindowInputDevice device =
+    GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard/mouse");
+  add_input_device(device);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+TinyXGraphicsWindow::
+~TinyXGraphicsWindow() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::move_pointer
+//       Access: Published, Virtual
+//  Description: Forces the pointer to the indicated position within
+//               the window, if possible.  
+//
+//               Returns true if successful, false on failure.  This
+//               may fail if the mouse is not currently within the
+//               window, or if the API doesn't support this operation.
+////////////////////////////////////////////////////////////////////
+bool TinyXGraphicsWindow::
+move_pointer(int device, int x, int y) {
+  // Note: this is not thread-safe; it should be called only from App.
+  // Probably not an issue.
+  if (device == 0) {
+    // Move the system mouse pointer.
+    if (!_properties.get_foreground() ||
+        !_input_devices[0].get_pointer().get_in_window()) {
+      // If the window doesn't have input focus, or the mouse isn't
+      // currently within the window, forget it.
+      return false;
+    }
+
+    XWarpPointer(_display, None, _xwindow, 0, 0, 0, 0, x, y);
+    _input_devices[0].set_pointer_in_window(x, y);
+    return true;
+  } else {
+    // Move a raw mouse.
+    if ((device < 1)||(device >= _input_devices.size())) {
+      return false;
+    }
+    _input_devices[device].set_pointer_in_window(x, y);
+    return true;
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::begin_frame
+//       Access: Public, Virtual
+//  Description: This function will be called within the draw thread
+//               before beginning rendering for a given frame.  It
+//               should do whatever setup is required, and return true
+//               if the frame should be rendered, or false if it
+//               should be skipped.
+////////////////////////////////////////////////////////////////////
+bool TinyXGraphicsWindow::
+begin_frame(FrameMode mode, Thread *current_thread) {
+  PStatTimer timer(_make_current_pcollector, current_thread);
+
+  begin_frame_spam(mode);
+  if (_gsg == (GraphicsStateGuardian *)NULL) {
+    return false;
+  }
+  if (_awaiting_configure) {
+    // Don't attempt to draw while we have just reconfigured the
+    // window and we haven't got the notification back yet.
+    return false;
+  }
+
+  TinyGraphicsStateGuardian *tinygsg;
+  DCAST_INTO_R(tinygsg, _gsg, false);
+
+  tinygsg->_current_frame_buffer = _frame_buffer;
+  tinygsg->reset_if_new();
+  
+  _gsg->set_current_properties(&get_fb_properties());
+  return _gsg->begin_frame(current_thread);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::end_frame
+//       Access: Public, Virtual
+//  Description: This function will be called within the draw thread
+//               after rendering is completed for a given frame.  It
+//               should do whatever finalization is required.
+////////////////////////////////////////////////////////////////////
+void TinyXGraphicsWindow::
+end_frame(FrameMode mode, Thread *current_thread) {
+  end_frame_spam(mode);
+  nassertv(_gsg != (GraphicsStateGuardian *)NULL);
+
+  if (mode == FM_render) {
+    // end_render_texture();
+    copy_to_textures();
+  }
+
+  _gsg->end_frame(current_thread);
+
+  if (mode == FM_render) {
+    trigger_flip();
+    if (_one_shot) {
+      prepare_for_deletion();
+    }
+    clear_cube_map_selection();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::begin_flip
+//       Access: Public, Virtual
+//  Description: This function will be called within the draw thread
+//               after end_frame() has been called on all windows, to
+//               initiate the exchange of the front and back buffers.
+//
+//               This should instruct the window to prepare for the
+//               flip at the next video sync, but it should not wait.
+//
+//               We have the two separate functions, begin_flip() and
+//               end_flip(), to make it easier to flip all of the
+//               windows at the same time.
+////////////////////////////////////////////////////////////////////
+void TinyXGraphicsWindow::
+begin_flip() {
+  if (_bytes_per_pixel == 4) {
+    // If we match the expected bpp, we don't need an intervening copy
+    // operation.  Just point the XImage directly at the framebuffer
+    // data.
+    _ximage->data = (char *)_frame_buffer->pbuf;
+  } else {
+    ZB_copyFrameBuffer(_frame_buffer, _ximage->data, _pitch);
+  }
+
+  XPutImage(_display, _xwindow, _gc, _ximage, 0, 0, 0, 0,
+            _properties.get_x_size(), _properties.get_y_size());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::process_events
+//       Access: Public, Virtual
+//  Description: Do whatever processing is necessary to ensure that
+//               the window responds to user events.  Also, honor any
+//               requests recently made via request_properties()
+//
+//               This function is called only within the window
+//               thread.
+////////////////////////////////////////////////////////////////////
+void TinyXGraphicsWindow::
+process_events() {
+  ReMutexHolder holder(TinyXGraphicsPipe::_x_mutex);
+
+  GraphicsWindow::process_events();
+
+  if (_xwindow == (Window)0) {
+    return;
+  }
+  
+  poll_raw_mice();
+  
+  XEvent event;
+  XKeyEvent keyrelease_event;
+  bool got_keyrelease_event = false;
+
+  while (XCheckIfEvent(_display, &event, check_event, (char *)this)) {
+    if (XFilterEvent(&event, None)) {
+      continue;
+    }
+
+    if (got_keyrelease_event) {
+      // If a keyrelease event is immediately followed by a matching
+      // keypress event, that's just key repeat and we should treat
+      // the two events accordingly.  It would be nice if X provided a
+      // way to differentiate between keyrepeat and explicit
+      // keypresses more generally.
+      got_keyrelease_event = false;
+
+      if (event.type == KeyPress &&
+          event.xkey.keycode == keyrelease_event.keycode &&
+          (event.xkey.time - keyrelease_event.time <= 1)) {
+        // In particular, we only generate down messages for the
+        // repeated keys, not down-and-up messages.
+        handle_keystroke(event.xkey);
+
+        // We thought about not generating the keypress event, but we
+        // need that repeat for backspace.  Rethink later.
+        handle_keypress(event.xkey);
+        continue;
+
+      } else {
+        // This keyrelease event is not immediately followed by a
+        // matching keypress event, so it's a genuine release.
+        handle_keyrelease(keyrelease_event);
+      }
+    }
+
+    WindowProperties properties;
+    ButtonHandle button;
+
+    switch (event.type) {
+    case ReparentNotify:
+      break;
+
+    case ConfigureNotify:
+      _awaiting_configure = false;
+      if (_properties.get_fixed_size()) {
+        // If the window properties indicate a fixed size only, undo
+        // any attempt by the user to change them.  In X, there
+        // doesn't appear to be a way to universally disallow this
+        // directly (although we do set the min_size and max_size to
+        // the same value, which seems to work for most window
+        // managers.)
+        WindowProperties current_props = get_properties();
+        if (event.xconfigure.width != current_props.get_x_size() ||
+            event.xconfigure.height != current_props.get_y_size()) {
+          XWindowChanges changes;
+          changes.width = current_props.get_x_size();
+          changes.height = current_props.get_y_size();
+          int value_mask = (CWWidth | CWHeight);
+          XConfigureWindow(_display, _xwindow, value_mask, &changes);
+        }
+
+      } else {
+        // A normal window may be resized by the user at will.
+        properties.set_size(event.xconfigure.width, event.xconfigure.height);
+        system_changed_properties(properties);
+      }
+      break;
+
+    case ButtonPress:
+      // This refers to the mouse buttons.
+      button = get_mouse_button(event.xbutton);
+      _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
+      _input_devices[0].button_down(button);
+      break;
+      
+    case ButtonRelease:
+      button = get_mouse_button(event.xbutton);
+      _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
+      _input_devices[0].button_up(button);
+      break;
+
+    case MotionNotify:
+      _input_devices[0].set_pointer_in_window(event.xmotion.x, event.xmotion.y);
+      break;
+
+    case KeyPress:
+      handle_keystroke(event.xkey);
+      handle_keypress(event.xkey);
+      break;
+
+    case KeyRelease:
+      // The KeyRelease can't be processed immediately, because we
+      // have to check first if it's immediately followed by a
+      // matching KeyPress event.
+      keyrelease_event = event.xkey;
+      got_keyrelease_event = true;
+      break;
+
+    case EnterNotify:
+      _input_devices[0].set_pointer_in_window(event.xcrossing.x, event.xcrossing.y);
+      break;
+
+    case LeaveNotify:
+      _input_devices[0].set_pointer_out_of_window();
+      break;
+
+    case FocusIn:
+      properties.set_foreground(true);
+      system_changed_properties(properties);
+      break;
+
+    case FocusOut:
+      properties.set_foreground(false);
+      system_changed_properties(properties);
+      break;
+
+    case UnmapNotify:
+      properties.set_minimized(true);
+      system_changed_properties(properties);
+      break;
+
+    case MapNotify:
+      properties.set_minimized(false);
+      system_changed_properties(properties);
+
+      // Auto-focus the window when it is mapped.
+      XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
+      break;
+
+    case ClientMessage:
+      if ((Atom)(event.xclient.data.l[0]) == _wm_delete_window) {
+        // This is a message from the window manager indicating that
+        // the user has requested to close the window.
+        string close_request_event = get_close_request_event();
+        if (!close_request_event.empty()) {
+          // In this case, the app has indicated a desire to intercept
+          // the request and process it directly.
+          throw_event(close_request_event);
+
+        } else {
+          // In this case, the default case, the app does not intend
+          // to service the request, so we do by closing the window.
+
+          // TODO: don't release the gsg in the window thread.
+          close_window();
+          properties.set_open(false);
+          system_changed_properties(properties);
+        }
+      }
+      break;
+
+    case DestroyNotify:
+      // Apparently, we never get a DestroyNotify on a toplevel
+      // window.  Instead, we rely on hints from the window manager
+      // (see above).
+      tinydisplay_cat.info()
+        << "DestroyNotify\n";
+      break;
+
+    default:
+      tinydisplay_cat.error()
+        << "unhandled X event type " << event.type << "\n";
+    }
+  }
+
+  if (got_keyrelease_event) {
+    // This keyrelease event is not immediately followed by a
+    // matching keypress event, so it's a genuine release.
+    handle_keyrelease(keyrelease_event);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::set_properties_now
+//       Access: Public, Virtual
+//  Description: Applies the requested set of properties to the
+//               window, if possible, for instance to request a change
+//               in size or minimization status.
+//
+//               The window properties are applied immediately, rather
+//               than waiting until the next frame.  This implies that
+//               this method may *only* be called from within the
+//               window thread.
+//
+//               The return value is true if the properties are set,
+//               false if they are ignored.  This is mainly useful for
+//               derived classes to implement extensions to this
+//               function.
+////////////////////////////////////////////////////////////////////
+void TinyXGraphicsWindow::
+set_properties_now(WindowProperties &properties) {
+  if (_pipe == (GraphicsPipe *)NULL) {
+    // If the pipe is null, we're probably closing down.
+    GraphicsWindow::set_properties_now(properties);
+    return;
+  }
+
+  TinyXGraphicsPipe *tinyx_pipe;
+  DCAST_INTO_V(tinyx_pipe, _pipe);
+
+  // Fullscreen mode is implemented with a hint to the window manager.
+  // However, we also implicitly set the origin to (0, 0) and the size
+  // to the desktop size, and request undecorated mode, in case the
+  // user has a less-capable window manager (or no window manager at
+  // all).
+  if (properties.get_fullscreen()) {
+    properties.set_undecorated(true);
+    properties.set_origin(0, 0);
+    properties.set_size(tinyx_pipe->get_display_width(),
+                        tinyx_pipe->get_display_height());
+  }
+
+  GraphicsWindow::set_properties_now(properties);
+  if (!properties.is_any_specified()) {
+    // The base class has already handled this case.
+    return;
+  }
+
+  // The window is already open; we are limited to what we can change
+  // on the fly.
+
+  // We'll pass some property requests on as a window manager hint.
+  WindowProperties wm_properties = _properties;
+  wm_properties.add_properties(properties);
+
+  // The window title may be changed by issuing another hint request.
+  // Assume this will be honored.
+  if (properties.has_title()) {
+    _properties.set_title(properties.get_title());
+    properties.clear_title();
+  }
+
+  // Ditto for fullscreen mode.
+  if (properties.has_fullscreen()) {
+    _properties.set_fullscreen(properties.get_fullscreen());
+    properties.clear_fullscreen();
+  }
+
+  // The size and position of an already-open window are changed via
+  // explicit X calls.  These may still get intercepted by the window
+  // manager.  Rather than changing _properties immediately, we'll
+  // wait for the ConfigureNotify message to come back.
+  XWindowChanges changes;
+  int value_mask = 0;
+
+  if (properties.has_origin()) {
+    changes.x = properties.get_x_origin();
+    changes.y = properties.get_y_origin();
+    value_mask |= (CWX | CWY);
+    properties.clear_origin();
+  }
+  if (properties.has_size()) {
+    changes.width = properties.get_x_size();
+    changes.height = properties.get_y_size();
+    value_mask |= (CWWidth | CWHeight);
+    properties.clear_size();
+  }
+  if (properties.has_z_order()) {
+    // We'll send the classic stacking request through the standard
+    // interface, for users of primitive window managers; but we'll
+    // also send it as a window manager hint, for users of modern
+    // window managers.
+    _properties.set_z_order(properties.get_z_order());
+    switch (properties.get_z_order()) {
+    case WindowProperties::Z_bottom:
+      changes.stack_mode = Below;
+      break;
+
+    case WindowProperties::Z_normal:
+      changes.stack_mode = TopIf;
+      break;
+
+    case WindowProperties::Z_top:
+      changes.stack_mode = Above;
+      break;
+    }
+
+    value_mask |= (CWStackMode);
+    properties.clear_z_order();
+  }
+
+  if (value_mask != 0) {
+    XReconfigureWMWindow(_display, _xwindow, _screen, value_mask, &changes);
+
+    // Don't draw anything until this is done reconfiguring.
+    _awaiting_configure = true;
+  }
+
+  // We hide the cursor by setting it to an invisible pixmap.
+  if (properties.has_cursor_hidden()) {
+    _properties.set_cursor_hidden(properties.get_cursor_hidden());
+    if (properties.get_cursor_hidden()) {
+      XDefineCursor(_display, _xwindow, tinyx_pipe->get_hidden_cursor());
+    } else {
+      XDefineCursor(_display, _xwindow, None);
+    }
+    properties.clear_cursor_hidden();
+  }
+
+  if (properties.has_foreground()) {
+    if (properties.get_foreground()) {
+      XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
+    } else {
+      XSetInputFocus(_display, PointerRoot, RevertToPointerRoot, CurrentTime);
+    }
+    properties.clear_foreground();
+  }
+
+  set_wm_properties(wm_properties, true);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::close_window
+//       Access: Protected, Virtual
+//  Description: Closes the window right now.  Called from the window
+//               thread.
+////////////////////////////////////////////////////////////////////
+void TinyXGraphicsWindow::
+close_window() {
+  if (_gsg != (GraphicsStateGuardian *)NULL) {
+    //    glXMakeCurrent(_display, None, NULL);
+    _gsg.clear();
+    _active = false;
+  }
+  
+  if (_ic != (XIC)NULL) {
+    XDestroyIC(_ic);
+    _ic = (XIC)NULL;
+  }
+
+  if (_gc != (GC)NULL) {
+    XFreeGC(_display, _gc);
+    _gc = (GC)NULL;
+  }
+
+  if (_xwindow != (Window)NULL) {
+    XDestroyWindow(_display, _xwindow);
+    _xwindow = (Window)NULL;
+
+    // This may be necessary if we just closed the last X window in an
+    // application, so the server hears the close request.
+    XFlush(_display);
+  }
+  GraphicsWindow::close_window();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::open_window
+//       Access: Protected, Virtual
+//  Description: Opens the window right now.  Called from the window
+//               thread.  Returns true if the window is successfully
+//               opened, or false if there was a problem.
+////////////////////////////////////////////////////////////////////
+bool TinyXGraphicsWindow::
+open_window() {
+  TinyXGraphicsPipe *tinyx_pipe;
+  DCAST_INTO_R(tinyx_pipe, _pipe, false);
+
+  // GSG Creation/Initialization
+  TinyGraphicsStateGuardian *tinygsg;
+  if (_gsg == 0) {
+    // There is no old gsg.  Create a new one.
+    tinygsg = new TinyGraphicsStateGuardian(_pipe, NULL);
+    _gsg = tinygsg;
+  } else {
+    DCAST_INTO_R(tinygsg, _gsg, false);
+  }
+
+  XVisualInfo vinfo_template;
+  vinfo_template.screen = _screen;
+  vinfo_template.depth = 32;
+  vinfo_template.c_class = TrueColor;
+
+  // Try to get each of these properties in turn.
+  int try_masks[] = {
+    VisualScreenMask | VisualDepthMask | VisualClassMask,
+    VisualScreenMask | VisualClassMask,
+    VisualScreenMask | VisualDepthMask,
+    VisualScreenMask,
+    0,
+  };
+
+  int i = 0;
+  int num_vinfos = 0;
+  XVisualInfo *vinfo_array;
+  while (try_masks[i] != 0 && num_vinfos == 0) {
+    vinfo_array = 
+      XGetVisualInfo(_display, try_masks[i], &vinfo_template, &num_vinfos);
+    ++i;
+  }
+
+  if (num_vinfos == 0) {
+    // No suitable X visual.
+    tinydisplay_cat.error()
+      << "No suitable X Visual available; cannot open window.\n";
+    return false;
+  }
+  XVisualInfo *visual_info = &vinfo_array[0];
+
+  _visual = visual_info->visual;
+  _depth = visual_info->depth;
+  _bytes_per_pixel = _depth / 8;
+  if (_bytes_per_pixel == 3) {
+    // Seems to be a special case.
+    _bytes_per_pixel = 4;
+  }
+  tinydisplay_cat.info()
+    << "Got X Visual with depth " << _depth << " (bpp " << _bytes_per_pixel << ") and class ";
+  switch (visual_info->c_class) {
+  case TrueColor:
+    tinydisplay_cat.info(false) << "TrueColor\n";
+    break;
+      
+  case DirectColor:
+    tinydisplay_cat.info(false) << "DirectColor\n";
+    break;
+
+  case StaticColor:
+    tinydisplay_cat.info(false) << "StaticColor\n";
+    break;
+
+  case StaticGray:
+    tinydisplay_cat.info(false) << "StaticGray\n";
+    break;
+
+  case GrayScale:
+    tinydisplay_cat.info(false) << "GrayScale\n";
+    break;
+
+  case PseudoColor:
+    tinydisplay_cat.info(false) << "PseudoColor\n";
+    break;
+  }
+
+  if (!_properties.has_origin()) {
+    _properties.set_origin(0, 0);
+  }
+  if (!_properties.has_size()) {
+    _properties.set_size(100, 100);
+  }
+
+  Window root_window = tinyx_pipe->get_root();
+  setup_colormap(visual_info);
+
+  _event_mask =
+    ButtonPressMask | ButtonReleaseMask |
+    KeyPressMask | KeyReleaseMask |
+    EnterWindowMask | LeaveWindowMask |
+    PointerMotionMask |
+    FocusChangeMask |
+    StructureNotifyMask;
+
+  // Initialize window attributes
+  XSetWindowAttributes wa;
+  wa.background_pixel = XBlackPixel(_display, _screen);
+  wa.border_pixel = 0;
+  wa.colormap = _colormap;
+  wa.event_mask = _event_mask;
+
+  unsigned long attrib_mask = 
+    CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
+
+  _xwindow = XCreateWindow
+    (_display, root_window,
+     _properties.get_x_origin(), _properties.get_y_origin(),
+     _properties.get_x_size(), _properties.get_y_size(),
+     0, _depth, InputOutput, _visual, attrib_mask, &wa);
+
+  if (_xwindow == (Window)0) {
+    tinydisplay_cat.error()
+      << "failed to create X window.\n";
+    return false;
+  }
+  set_wm_properties(_properties, false);
+
+  // We don't specify any fancy properties of the XIC.  It would be
+  // nicer if we could support fancy IM's that want preedit callbacks,
+  // etc., but that can wait until we have an X server that actually
+  // supports these to test it on.
+  XIM im = tinyx_pipe->get_im();
+  _ic = NULL;
+  if (im) {
+    _ic = XCreateIC
+      (im,
+       XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
+       NULL);
+    if (_ic == (XIC)NULL) {
+      tinydisplay_cat.warning()
+        << "Couldn't create input context.\n";
+    }
+  }
+
+  if (_properties.get_cursor_hidden()) {
+    XDefineCursor(_display, _xwindow, tinyx_pipe->get_hidden_cursor());
+  }
+
+  _gc = XCreateGC(_display, _xwindow, 0, NULL);
+
+  create_frame_buffer();
+  if (_frame_buffer == NULL) {
+    tinydisplay_cat.error()
+      << "Could not create frame buffer.\n";
+    return false;
+  }
+  create_ximage();
+  nassertr(_ximage != NULL, false);
+
+  tinygsg->_current_frame_buffer = _frame_buffer;
+  
+  tinygsg->reset_if_new();
+  if (!tinygsg->is_valid()) {
+    close_window();
+    return false;
+  }
+  
+  XMapWindow(_display, _xwindow);
+
+  if (_properties.get_raw_mice()) {
+    open_raw_mice();
+  } else {
+    if (tinydisplay_cat.is_debug()) {
+      tinydisplay_cat.debug()
+        << "Raw mice not requested.\n";
+    }
+  }
+  
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::set_wm_properties
+//       Access: Private
+//  Description: Asks the window manager to set the appropriate
+//               properties.  In X, these properties cannot be
+//               specified directly by the application; they must be
+//               requested via the window manager, which may or may
+//               not choose to honor the request.
+//
+//               If already_mapped is true, the window has already
+//               been mapped (manifested) on the display.  This means
+//               we may need to use a different action in some cases.
+////////////////////////////////////////////////////////////////////
+void TinyXGraphicsWindow::
+set_wm_properties(const WindowProperties &properties, bool already_mapped) {
+  // Name the window if there is a name
+  XTextProperty window_name;
+  XTextProperty *window_name_p = (XTextProperty *)NULL;
+  if (properties.has_title()) {
+    char *name = (char *)properties.get_title().c_str();
+    if (XStringListToTextProperty(&name, 1, &window_name) != 0) {
+      window_name_p = &window_name;
+    }
+  }
+
+  // The size hints request a window of a particular size and/or a
+  // particular placement onscreen.
+  XSizeHints *size_hints_p = NULL;
+  if (properties.has_origin() || properties.has_size()) {
+    size_hints_p = XAllocSizeHints();
+    if (size_hints_p != (XSizeHints *)NULL) {
+      if (properties.has_origin()) {
+        size_hints_p->x = properties.get_x_origin();
+        size_hints_p->y = properties.get_y_origin();
+        size_hints_p->flags |= USPosition;
+      }
+      if (properties.has_size()) {
+        size_hints_p->width = properties.get_x_size();
+        size_hints_p->height = properties.get_y_size();
+        size_hints_p->flags |= USSize;
+
+        if (properties.has_fixed_size()) {
+          size_hints_p->min_width = properties.get_x_size();
+          size_hints_p->min_height = properties.get_y_size();
+          size_hints_p->max_width = properties.get_x_size();
+          size_hints_p->max_height = properties.get_y_size();
+          size_hints_p->flags |= (PMinSize | PMaxSize);
+        }
+      }
+    }
+  }
+
+  // The window manager hints include requests to the window manager
+  // other than those specific to window geometry.
+  XWMHints *wm_hints_p = NULL;
+  wm_hints_p = XAllocWMHints();
+  if (wm_hints_p != (XWMHints *)NULL) {
+    if (properties.has_minimized() && properties.get_minimized()) {
+      wm_hints_p->initial_state = IconicState;
+    } else {
+      wm_hints_p->initial_state = NormalState;
+    }
+    wm_hints_p->flags = StateHint;
+  }
+
+  // Two competing window manager interfaces have evolved.  One of
+  // them allows to set certain properties as a "type"; the other one
+  // as a "state".  We'll try to honor both.
+  static const int max_type_data = 32;
+  PN_int32 type_data[max_type_data];
+  int next_type_data = 0;
+
+  static const int max_state_data = 32;
+  PN_int32 state_data[max_state_data];
+  int next_state_data = 0;
+
+  static const int max_set_data = 32;
+  class SetAction {
+  public:
+    inline SetAction() { }
+    inline SetAction(Atom state, Atom action) : _state(state), _action(action) { }
+    Atom _state;
+    Atom _action;
+  };
+  SetAction set_data[max_set_data];
+  int next_set_data = 0;
+
+  if (properties.get_fullscreen()) {
+    // For a "fullscreen" request, we pass this through, hoping the
+    // window manager will support EWMH.
+    type_data[next_type_data++] = _net_wm_window_type_fullscreen;
+
+    // We also request it as a state.
+    state_data[next_state_data++] = _net_wm_state_fullscreen;
+    set_data[next_set_data++] = SetAction(_net_wm_state_fullscreen, _net_wm_state_add);
+  } else {
+    set_data[next_set_data++] = SetAction(_net_wm_state_fullscreen, _net_wm_state_remove);
+  }
+
+  // If we asked for a window without a border, there's no excellent
+  // way to arrange that.  For users whose window managers follow the
+  // EWMH specification, we can ask for a "splash" screen, which is
+  // usually undecorated.  It's not exactly right, but the spec
+  // doesn't give us an exactly-right option.
+
+  // For other users, we'll totally punt and just set the window's
+  // Class to "Undecorated", and let the user configure his/her window
+  // manager not to put a border around windows of this class.
+  XClassHint *class_hints_p = NULL;
+  if (properties.get_undecorated()) {
+    class_hints_p = XAllocClassHint();
+    class_hints_p->res_class = "Undecorated";
+
+    if (!properties.get_fullscreen()) {
+      type_data[next_type_data++] = _net_wm_window_type_splash;
+    }
+  }
+
+  if (properties.has_z_order()) {
+    switch (properties.get_z_order()) {
+    case WindowProperties::Z_bottom:
+      state_data[next_state_data++] = _net_wm_state_below;
+      set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_add);
+      set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_remove);
+      break;
+
+    case WindowProperties::Z_normal:
+      set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_remove);
+      set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_remove);
+      break;
+
+    case WindowProperties::Z_top:
+      state_data[next_state_data++] = _net_wm_state_above;
+      set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_remove);
+      set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_add);
+      break;
+    }
+  }
+
+  nassertv(next_type_data < max_type_data);
+  nassertv(next_state_data < max_state_data);
+  nassertv(next_set_data < max_set_data);
+
+  XChangeProperty(_display, _xwindow, _net_wm_window_type,
+                  XA_ATOM, 32, PropModeReplace,
+                  (unsigned char *)type_data, next_type_data);
+
+  // Request the state properties all at once.
+  XChangeProperty(_display, _xwindow, _net_wm_state,
+                  XA_ATOM, 32, PropModeReplace,
+                  (unsigned char *)state_data, next_state_data);
+
+  if (already_mapped) {
+    // We have to request state changes differently when the window
+    // has been mapped.  To do this, we need to send a client message
+    // to the root window for each change.
+
+    TinyXGraphicsPipe *tinyx_pipe;
+    DCAST_INTO_V(tinyx_pipe, _pipe);
+  
+    for (int i = 0; i < next_set_data; ++i) {
+      XClientMessageEvent event;
+      memset(&event, 0, sizeof(event));
+
+      event.type = ClientMessage;
+      event.send_event = True;
+      event.display = _display;
+      event.window = _xwindow;
+      event.message_type = _net_wm_state;
+      event.format = 32;
+      event.data.l[0] = set_data[i]._action;
+      event.data.l[1] = set_data[i]._state;
+      event.data.l[2] = 0;
+      event.data.l[3] = 1;
+
+      XSendEvent(_display, tinyx_pipe->get_root(), True, 0, (XEvent *)&event);
+    }
+  }
+
+  XSetWMProperties(_display, _xwindow, window_name_p, window_name_p,
+                   NULL, 0, size_hints_p, wm_hints_p, class_hints_p);
+
+  if (size_hints_p != (XSizeHints *)NULL) {
+    XFree(size_hints_p);
+  }
+  if (wm_hints_p != (XWMHints *)NULL) {
+    XFree(wm_hints_p);
+  }
+  if (class_hints_p != (XClassHint *)NULL) {
+    XFree(class_hints_p);
+  }
+
+  // Also, indicate to the window manager that we'd like to get a
+  // chance to close our windows cleanly, rather than being rudely
+  // disconnected from the X server if the user requests a window
+  // close.
+  Atom protocols[] = {
+    _wm_delete_window,
+  };
+
+  XSetWMProtocols(_display, _xwindow, protocols, 
+                  sizeof(protocols) / sizeof(Atom));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::setup_colormap
+//       Access: Private
+//  Description: Allocates a colormap appropriate to the visual and
+//               stores in in the _colormap method.
+////////////////////////////////////////////////////////////////////
+void TinyXGraphicsWindow::
+setup_colormap(XVisualInfo *visual_info) {
+  TinyXGraphicsPipe *tinyx_pipe;
+  DCAST_INTO_V(tinyx_pipe, _pipe);
+  Window root_window = tinyx_pipe->get_root();
+
+  int visual_class = visual_info->c_class;
+  int rc, is_rgb;
+
+  switch (visual_class) {
+    case TrueColor:
+    case DirectColor:
+      _colormap = XCreateColormap(_display, root_window,
+                                  visual_info->visual, AllocNone);
+      break;
+    case StaticColor:
+    case StaticGray:
+    case GrayScale:
+      _colormap = XCreateColormap(_display, root_window,
+                                  visual_info->visual, AllocNone);
+      break;
+
+    default:
+      tinydisplay_cat.error()
+        << "Could not allocate a colormap for visual class "
+        << visual_class << ".\n";
+      break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::open_raw_mice
+//       Access: Private
+//  Description: Adds raw mice to the _input_devices list.
+////////////////////////////////////////////////////////////////////
+void TinyXGraphicsWindow::
+open_raw_mice()
+{
+#ifdef HAVE_LINUX_INPUT_H
+  bool any_present = false;
+  bool any_mice = false;
+  
+  for (int i=0; i<64; i++) {
+    uint8_t evtypes[EV_MAX/8 + 1];
+    ostringstream fnb;
+    fnb << "/dev/input/event" << i;
+    string fn = fnb.str();
+    int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0);
+    if (fd >= 0) {
+      any_present = true;
+      char name[256];
+      char phys[256];
+      char uniq[256];
+      if ((ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0)||
+	  (ioctl(fd, EVIOCGPHYS(sizeof(phys)), phys) < 0)||
+	  (ioctl(fd, EVIOCGPHYS(sizeof(uniq)), uniq) < 0)||
+	  (ioctl(fd, EVIOCGBIT(0, EV_MAX), &evtypes) < 0)) {
+	close(fd);
+	tinydisplay_cat.error() <<
+	  "Opening raw mice: ioctl failed on " << fn << "\n";
+      } else {
+	if (test_bit(EV_REL, evtypes) || test_bit(EV_ABS, evtypes)) {
+          for (char *p=name; *p; p++) {
+            if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) {
+              *p = '_';
+            }
+          }
+          for (char *p=uniq; *p; p++) {
+            if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) {
+              *p = '_';
+            }
+          }
+	  string full_id = ((string)name) + "." + uniq;
+	  MouseDeviceInfo inf;
+	  inf._fd = fd;
+	  inf._input_device_index = _input_devices.size();
+	  inf._io_buffer = "";
+	  _mouse_device_info.push_back(inf);
+	  GraphicsWindowInputDevice device =
+	    GraphicsWindowInputDevice::pointer_only(this, full_id);
+          add_input_device(device);
+	  tinydisplay_cat.info() << "Raw mouse " <<
+	    inf._input_device_index << " detected: " << full_id << "\n";
+	  any_mice = true;
+	} else {
+	  close(fd);
+	}
+      }
+    } else {
+      if ((errno == ENOENT)||(errno == ENOTDIR)) {
+	break;
+      } else {
+	any_present = true;
+	tinydisplay_cat.error() << 
+	  "Opening raw mice: " << strerror(errno) << " " << fn << "\n";
+      }
+    }
+  }
+  
+  if (!any_present) {
+    tinydisplay_cat.error() << 
+      "Opening raw mice: files not found: /dev/input/event*\n";
+  } else if (!any_mice) {
+    tinydisplay_cat.error() << 
+      "Opening raw mice: no mouse devices detected in /dev/input/event*\n";
+  }
+#else
+  tinydisplay_cat.error() <<
+    "Opening raw mice: panda not compiled with raw mouse support.\n";
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::poll_raw_mice
+//       Access: Private
+//  Description: Reads events from the raw mouse device files.
+////////////////////////////////////////////////////////////////////
+void TinyXGraphicsWindow::
+poll_raw_mice()
+{
+#ifdef HAVE_LINUX_INPUT_H
+  for (int dev=0; dev<_mouse_device_info.size(); dev++) {
+    MouseDeviceInfo &inf = _mouse_device_info[dev];
+
+    // Read all bytes into buffer.
+    if (inf._fd >= 0) {
+      while (1) {
+	char tbuf[1024];
+	int nread = read(inf._fd, tbuf, sizeof(tbuf));
+	if (nread > 0) {
+	  inf._io_buffer += string(tbuf, nread);
+	} else {
+	  if ((nread < 0)&&((errno == EWOULDBLOCK) || (errno==EAGAIN))) {
+	    break;
+	  }
+	  close(inf._fd);
+	  inf._fd = -1;
+	  break;
+	}
+      }
+    }
+
+    // Process events.
+    int nevents = inf._io_buffer.size() / sizeof(struct input_event);
+    if (nevents == 0) {
+      continue;
+    }
+    const input_event *events = (const input_event *)(inf._io_buffer.c_str());
+    GraphicsWindowInputDevice &dev = _input_devices[inf._input_device_index];
+    int x = dev.get_raw_pointer().get_x();
+    int y = dev.get_raw_pointer().get_y();
+    for (int i=0; i<nevents; i++) {
+      if (events[i].type == EV_REL) {
+	if (events[i].code == REL_X) x += events[i].value;
+	if (events[i].code == REL_Y) y += events[i].value;
+      } else if (events[i].type == EV_ABS) {
+	if (events[i].code == ABS_X) x = events[i].value;
+	if (events[i].code == ABS_Y) y = events[i].value;
+      } else if (events[i].type == EV_KEY) {
+	if ((events[i].code >= BTN_MOUSE)&&(events[i].code < BTN_MOUSE+8)) {
+	  int btn = events[i].code - BTN_MOUSE;
+	  dev.set_pointer_in_window(x,y);
+	  if (events[i].value) {
+	    dev.button_down(MouseButton::button(btn));
+	  } else {
+	    dev.button_up(MouseButton::button(btn));
+	  }
+	}
+      }
+    }
+    inf._io_buffer.erase(0,nevents*sizeof(struct input_event));
+    dev.set_pointer_in_window(x,y);
+  }
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::handle_keystroke
+//       Access: Private
+//  Description: Generates a keystroke corresponding to the indicated
+//               X KeyPress event.
+////////////////////////////////////////////////////////////////////
+void TinyXGraphicsWindow::
+handle_keystroke(XKeyEvent &event) {
+  _input_devices[0].set_pointer_in_window(event.x, event.y);
+
+  if (_ic) {
+    // First, get the keystroke as a wide-character sequence.
+    static const int buffer_size = 256;
+    wchar_t buffer[buffer_size];
+    Status status;
+    int len = XwcLookupString(_ic, &event, buffer, buffer_size, NULL,
+                              &status);
+    if (status == XBufferOverflow) {
+      tinydisplay_cat.error()
+        << "Overflowed input buffer.\n";
+    }
+    
+    // Now each of the returned wide characters represents a
+    // keystroke.
+    for (int i = 0; i < len; i++) {
+      _input_devices[0].keystroke(buffer[i]);
+    }
+
+  } else {
+    // Without an input context, just get the ascii keypress.
+    ButtonHandle button = get_button(event);
+    if (button.has_ascii_equivalent()) {
+      _input_devices[0].keystroke(button.get_ascii_equivalent());
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::handle_keypress
+//       Access: Private
+//  Description: Generates a keypress corresponding to the indicated
+//               X KeyPress event.
+////////////////////////////////////////////////////////////////////
+void TinyXGraphicsWindow::
+handle_keypress(XKeyEvent &event) {
+  _input_devices[0].set_pointer_in_window(event.x, event.y);
+
+  // Now get the raw unshifted button.
+  ButtonHandle button = get_button(event);
+  if (button != ButtonHandle::none()) {
+    _input_devices[0].button_down(button);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::handle_keyrelease
+//       Access: Private
+//  Description: Generates a keyrelease corresponding to the indicated
+//               X KeyRelease event.
+////////////////////////////////////////////////////////////////////
+void TinyXGraphicsWindow::
+handle_keyrelease(XKeyEvent &event) {
+  _input_devices[0].set_pointer_in_window(event.x, event.y);
+
+  // Now get the raw unshifted button.
+  ButtonHandle button = get_button(event);
+  if (button != ButtonHandle::none()) {
+    _input_devices[0].button_up(button);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::get_button
+//       Access: Private
+//  Description: Returns the Panda ButtonHandle corresponding to the
+//               keyboard button indicated by the given key event.
+////////////////////////////////////////////////////////////////////
+ButtonHandle TinyXGraphicsWindow::
+get_button(XKeyEvent &key_event) {
+  KeySym key = XLookupKeysym(&key_event, 0);
+
+  switch (key) {
+  case XK_BackSpace:
+    return KeyboardButton::backspace();
+  case XK_Tab:
+    return KeyboardButton::tab();
+  case XK_Return:
+    return KeyboardButton::enter();
+  case XK_Escape:
+    return KeyboardButton::escape();
+  case XK_space:
+    return KeyboardButton::space();
+  case XK_exclam:
+    return KeyboardButton::ascii_key('!');
+  case XK_quotedbl:
+    return KeyboardButton::ascii_key('"');
+  case XK_numbersign:
+    return KeyboardButton::ascii_key('#');
+  case XK_dollar:
+    return KeyboardButton::ascii_key('$');
+  case XK_percent:
+    return KeyboardButton::ascii_key('%');
+  case XK_ampersand:
+    return KeyboardButton::ascii_key('&');
+  case XK_apostrophe: // == XK_quoteright
+    return KeyboardButton::ascii_key('\'');
+  case XK_parenleft:
+    return KeyboardButton::ascii_key('(');
+  case XK_parenright:
+    return KeyboardButton::ascii_key(')');
+  case XK_asterisk:
+    return KeyboardButton::ascii_key('*');
+  case XK_plus:
+    return KeyboardButton::ascii_key('+');
+  case XK_comma:
+    return KeyboardButton::ascii_key(',');
+  case XK_minus:
+    return KeyboardButton::ascii_key('-');
+  case XK_period:
+    return KeyboardButton::ascii_key('.');
+  case XK_slash:
+    return KeyboardButton::ascii_key('/');
+  case XK_0:
+    return KeyboardButton::ascii_key('0');
+  case XK_1:
+    return KeyboardButton::ascii_key('1');
+  case XK_2:
+    return KeyboardButton::ascii_key('2');
+  case XK_3:
+    return KeyboardButton::ascii_key('3');
+  case XK_4:
+    return KeyboardButton::ascii_key('4');
+  case XK_5:
+    return KeyboardButton::ascii_key('5');
+  case XK_6:
+    return KeyboardButton::ascii_key('6');
+  case XK_7:
+    return KeyboardButton::ascii_key('7');
+  case XK_8:
+    return KeyboardButton::ascii_key('8');
+  case XK_9:
+    return KeyboardButton::ascii_key('9');
+  case XK_colon:
+    return KeyboardButton::ascii_key(':');
+  case XK_semicolon:
+    return KeyboardButton::ascii_key(';');
+  case XK_less:
+    return KeyboardButton::ascii_key('<');
+  case XK_equal:
+    return KeyboardButton::ascii_key('=');
+  case XK_greater:
+    return KeyboardButton::ascii_key('>');
+  case XK_question:
+    return KeyboardButton::ascii_key('?');
+  case XK_at:
+    return KeyboardButton::ascii_key('@');
+  case XK_A:
+    return KeyboardButton::ascii_key('A');
+  case XK_B:
+    return KeyboardButton::ascii_key('B');
+  case XK_C:
+    return KeyboardButton::ascii_key('C');
+  case XK_D:
+    return KeyboardButton::ascii_key('D');
+  case XK_E:
+    return KeyboardButton::ascii_key('E');
+  case XK_F:
+    return KeyboardButton::ascii_key('F');
+  case XK_G:
+    return KeyboardButton::ascii_key('G');
+  case XK_H:
+    return KeyboardButton::ascii_key('H');
+  case XK_I:
+    return KeyboardButton::ascii_key('I');
+  case XK_J:
+    return KeyboardButton::ascii_key('J');
+  case XK_K:
+    return KeyboardButton::ascii_key('K');
+  case XK_L:
+    return KeyboardButton::ascii_key('L');
+  case XK_M:
+    return KeyboardButton::ascii_key('M');
+  case XK_N:
+    return KeyboardButton::ascii_key('N');
+  case XK_O:
+    return KeyboardButton::ascii_key('O');
+  case XK_P:
+    return KeyboardButton::ascii_key('P');
+  case XK_Q:
+    return KeyboardButton::ascii_key('Q');
+  case XK_R:
+    return KeyboardButton::ascii_key('R');
+  case XK_S:
+    return KeyboardButton::ascii_key('S');
+  case XK_T:
+    return KeyboardButton::ascii_key('T');
+  case XK_U:
+    return KeyboardButton::ascii_key('U');
+  case XK_V:
+    return KeyboardButton::ascii_key('V');
+  case XK_W:
+    return KeyboardButton::ascii_key('W');
+  case XK_X:
+    return KeyboardButton::ascii_key('X');
+  case XK_Y:
+    return KeyboardButton::ascii_key('Y');
+  case XK_Z:
+    return KeyboardButton::ascii_key('Z');
+  case XK_bracketleft:
+    return KeyboardButton::ascii_key('[');
+  case XK_backslash:
+    return KeyboardButton::ascii_key('\\');
+  case XK_bracketright:
+    return KeyboardButton::ascii_key(']');
+  case XK_asciicircum:
+    return KeyboardButton::ascii_key('^');
+  case XK_underscore:
+    return KeyboardButton::ascii_key('_');
+  case XK_grave: // == XK_quoteleft
+    return KeyboardButton::ascii_key('`');
+  case XK_a:
+    return KeyboardButton::ascii_key('a');
+  case XK_b:
+    return KeyboardButton::ascii_key('b');
+  case XK_c:
+    return KeyboardButton::ascii_key('c');
+  case XK_d:
+    return KeyboardButton::ascii_key('d');
+  case XK_e:
+    return KeyboardButton::ascii_key('e');
+  case XK_f:
+    return KeyboardButton::ascii_key('f');
+  case XK_g:
+    return KeyboardButton::ascii_key('g');
+  case XK_h:
+    return KeyboardButton::ascii_key('h');
+  case XK_i:
+    return KeyboardButton::ascii_key('i');
+  case XK_j:
+    return KeyboardButton::ascii_key('j');
+  case XK_k:
+    return KeyboardButton::ascii_key('k');
+  case XK_l:
+    return KeyboardButton::ascii_key('l');
+  case XK_m:
+    return KeyboardButton::ascii_key('m');
+  case XK_n:
+    return KeyboardButton::ascii_key('n');
+  case XK_o:
+    return KeyboardButton::ascii_key('o');
+  case XK_p:
+    return KeyboardButton::ascii_key('p');
+  case XK_q:
+    return KeyboardButton::ascii_key('q');
+  case XK_r:
+    return KeyboardButton::ascii_key('r');
+  case XK_s:
+    return KeyboardButton::ascii_key('s');
+  case XK_t:
+    return KeyboardButton::ascii_key('t');
+  case XK_u:
+    return KeyboardButton::ascii_key('u');
+  case XK_v:
+    return KeyboardButton::ascii_key('v');
+  case XK_w:
+    return KeyboardButton::ascii_key('w');
+  case XK_x:
+    return KeyboardButton::ascii_key('x');
+  case XK_y:
+    return KeyboardButton::ascii_key('y');
+  case XK_z:
+    return KeyboardButton::ascii_key('z');
+  case XK_braceleft:
+    return KeyboardButton::ascii_key('{');
+  case XK_bar:
+    return KeyboardButton::ascii_key('|');
+  case XK_braceright:
+    return KeyboardButton::ascii_key('}');
+  case XK_asciitilde:
+    return KeyboardButton::ascii_key('~');
+  case XK_F1:
+    return KeyboardButton::f1();
+  case XK_F2:
+    return KeyboardButton::f2();
+  case XK_F3:
+    return KeyboardButton::f3();
+  case XK_F4:
+    return KeyboardButton::f4();
+  case XK_F5:
+    return KeyboardButton::f5();
+  case XK_F6:
+    return KeyboardButton::f6();
+  case XK_F7:
+    return KeyboardButton::f7();
+  case XK_F8:
+    return KeyboardButton::f8();
+  case XK_F9:
+    return KeyboardButton::f9();
+  case XK_F10:
+    return KeyboardButton::f10();
+  case XK_F11:
+    return KeyboardButton::f11();
+  case XK_F12:
+    return KeyboardButton::f12();
+  case XK_KP_Left:
+  case XK_Left:
+    return KeyboardButton::left();
+  case XK_KP_Up:
+  case XK_Up:
+    return KeyboardButton::up();
+  case XK_KP_Right:
+  case XK_Right:
+    return KeyboardButton::right();
+  case XK_KP_Down:
+  case XK_Down:
+    return KeyboardButton::down();
+  case XK_KP_Prior:
+  case XK_Prior:
+    return KeyboardButton::page_up();
+  case XK_KP_Next:
+  case XK_Next:
+    return KeyboardButton::page_down();
+  case XK_KP_Home:
+  case XK_Home:
+    return KeyboardButton::home();
+  case XK_KP_End:
+  case XK_End:
+    return KeyboardButton::end();
+  case XK_KP_Insert:
+  case XK_Insert:
+    return KeyboardButton::insert();
+  case XK_KP_Delete:
+  case XK_Delete:
+    return KeyboardButton::del();
+  case XK_Shift_L:
+  case XK_Shift_R:
+    return KeyboardButton::shift();
+  case XK_Control_L:
+  case XK_Control_R:
+    return KeyboardButton::control();
+  case XK_Alt_L:
+  case XK_Alt_R:
+    return KeyboardButton::alt();
+  case XK_Meta_L:
+  case XK_Meta_R:
+    return KeyboardButton::meta();
+  case XK_Caps_Lock:
+    return KeyboardButton::caps_lock();
+  case XK_Shift_Lock:
+    return KeyboardButton::shift_lock();
+  }
+
+  return ButtonHandle::none();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::get_mouse_button
+//       Access: Private
+//  Description: Returns the Panda ButtonHandle corresponding to the
+//               mouse button indicated by the given button event.
+////////////////////////////////////////////////////////////////////
+ButtonHandle TinyXGraphicsWindow::
+get_mouse_button(XButtonEvent &button_event) {
+  int index = button_event.button;
+  if (index == x_wheel_up_button) {
+    return MouseButton::wheel_up();
+  } else if (index == x_wheel_down_button) {
+    return MouseButton::wheel_down();
+  } else  {
+    return MouseButton::button(index - 1);
+  }
+}
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::check_event
+//       Access: Private, Static
+//  Description: This function is used as a predicate to
+//               XCheckIfEvent() to determine if the indicated queued
+//               X event is relevant and should be returned to this
+//               window.
+////////////////////////////////////////////////////////////////////
+Bool TinyXGraphicsWindow::
+check_event(Display *display, XEvent *event, char *arg) {
+  const TinyXGraphicsWindow *self = (TinyXGraphicsWindow *)arg;
+
+  // We accept any event that is sent to our window.
+  return (event->xany.window == self->_xwindow);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::create_frame_buffer
+//       Access: Private
+//  Description: Creates a suitable frame buffer for the current
+//               window size.
+////////////////////////////////////////////////////////////////////
+void TinyXGraphicsWindow::
+create_frame_buffer() {
+  if (_frame_buffer != NULL) {
+    ZB_close(_frame_buffer);
+    _frame_buffer = NULL;
+  }
+
+  int mode;
+  switch (_bytes_per_pixel) {
+  case  1:
+    tinydisplay_cat.error()
+      << "Palette images are currently not supported.\n";
+    return;
+
+  case 2:
+    mode = ZB_MODE_5R6G5B;
+    break;
+  case 4:
+    mode = ZB_MODE_RGBA;
+    break;
+
+  default:
+    return;
+  }
+
+  _frame_buffer = ZB_open(_properties.get_x_size(), _properties.get_y_size(), mode, 0, 0, 0, 0);
+  _pitch = _properties.get_x_size() * _bytes_per_pixel;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyXGraphicsWindow::create_ximage
+//       Access: Private
+//  Description: Creates a suitable XImage for the current
+//               window size.
+////////////////////////////////////////////////////////////////////
+void TinyXGraphicsWindow::
+create_ximage() {
+  if (_ximage != NULL) {
+    if (_bytes_per_pixel != 4) {
+      PANDA_FREE_ARRAY(_ximage->data);
+    }
+    _ximage->data = NULL;
+    XDestroyImage(_ximage);
+    _ximage = NULL;
+  }
+
+  int image_size = _properties.get_x_size() * _properties.get_y_size() * _bytes_per_pixel;
+  char *data = NULL;
+  if (_bytes_per_pixel != 4) {
+    data = (char *)PANDA_MALLOC_ARRAY(image_size);
+  }
+
+  _ximage = XCreateImage(_display, _visual, _depth, ZPixmap, 0, data,
+                         _properties.get_x_size(), _properties.get_y_size(),
+                         8, 0);
+}
+
+#endif  // IS_LINUX
+

+ 136 - 0
panda/src/tinydisplay/tinyXGraphicsWindow.h

@@ -0,0 +1,136 @@
+// Filename: tinyXGraphicsWindow.h
+// Created by:  drose (03May08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef TINYXGRAPHICSWINDOW_H
+#define TINYXGRAPHICSWINDOW_H
+
+#include "pandabase.h"
+
+#ifdef IS_LINUX
+
+#include "tinyXGraphicsPipe.h"
+#include "graphicsWindow.h"
+#include "buttonHandle.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : TinyXGraphicsWindow
+// Description : Opens a window on X11 to display the TinyGL software
+//               rendering.
+////////////////////////////////////////////////////////////////////
+class TinyXGraphicsWindow : public GraphicsWindow {
+public:
+  TinyXGraphicsWindow(GraphicsPipe *pipe, 
+                    const string &name,
+                    const FrameBufferProperties &fb_prop,
+                    const WindowProperties &win_prop,
+                    int flags,
+                    GraphicsStateGuardian *gsg,
+                    GraphicsOutput *host);
+  virtual ~TinyXGraphicsWindow();
+
+  virtual bool move_pointer(int device, int x, int y);
+  virtual bool begin_frame(FrameMode mode, Thread *current_thread);
+  virtual void end_frame(FrameMode mode, Thread *current_thread);
+  virtual void begin_flip();
+
+  virtual void process_events();
+  virtual void set_properties_now(WindowProperties &properties);
+
+protected:
+  virtual void close_window();
+  virtual bool open_window();
+
+private:
+  void set_wm_properties(const WindowProperties &properties,
+                         bool already_mapped);
+
+  void setup_colormap(XVisualInfo *visual);
+  void handle_keystroke(XKeyEvent &event);
+  void handle_keypress(XKeyEvent &event);
+  void handle_keyrelease(XKeyEvent &event);
+
+  ButtonHandle get_button(XKeyEvent &key_event);
+  ButtonHandle get_mouse_button(XButtonEvent &button_event);
+
+  static Bool check_event(Display *display, XEvent *event, char *arg);
+
+  void open_raw_mice();
+  void poll_raw_mice();
+
+  void create_frame_buffer();
+  void create_ximage();
+  
+private:
+  ZBuffer *_frame_buffer;
+  int _pitch;
+  XImage *_ximage;
+
+  Display *_display;
+  int _screen;
+  Visual *_visual;
+  int _depth;
+  int _bytes_per_pixel;
+  Window _xwindow;
+  Colormap _colormap;
+  XIC _ic;
+  GC _gc;
+
+  long _event_mask;
+  bool _awaiting_configure;
+  Atom _wm_delete_window;
+  Atom _net_wm_window_type;
+  Atom _net_wm_window_type_splash;
+  Atom _net_wm_window_type_fullscreen;
+  Atom _net_wm_state;
+  Atom _net_wm_state_fullscreen;
+  Atom _net_wm_state_above;
+  Atom _net_wm_state_below;
+  Atom _net_wm_state_add;
+  Atom _net_wm_state_remove;
+
+  struct MouseDeviceInfo {
+    int    _fd;
+    int    _input_device_index;
+    string _io_buffer;
+  };
+  pvector<MouseDeviceInfo> _mouse_device_info;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GraphicsWindow::init_type();
+    register_type(_type_handle, "TinyXGraphicsWindow",
+                  GraphicsWindow::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "tinyXGraphicsWindow.I"
+
+#endif  // IS_LINUX
+
+#endif
+

+ 33 - 1
panda/src/tinydisplay/zbuffer.c

@@ -1,6 +1,6 @@
 /*
 
- * Z buffer: 16 bits Z / 16 bits color
+ * Z buffer: 16 bits Z / 32 bits color
  * 
  */
 #include <stdlib.h>
@@ -161,6 +161,33 @@ static void ZB_copyFrameBuffer5R6G5B(ZBuffer * zb,
     }
 }
 
+/* XXX: not optimized */
+static void ZB_copyFrameBufferRGB24(ZBuffer * zb, 
+                                    void *buf, int linesize) 
+{
+    PIXEL *q;
+    unsigned char *p, *p1;
+    int y, n;
+
+    fprintf(stderr, "copyFrameBufferRGB24\n");
+    
+    q = zb->pbuf;
+    p1 = (unsigned char *) buf;
+
+    for (y = 0; y < zb->ysize; y++) {
+	p = p1;
+	n = zb->xsize;
+	do {
+          p[0] = q[0];
+          p[1] = q[1];
+          p[2] = q[2];
+          q += 4;
+          p += 3;
+	} while (--n > 0);
+	p1 += linesize;
+    }
+}
+
 void ZB_copyFrameBuffer(ZBuffer * zb, void *buf,
 			int linesize)
 {
@@ -170,6 +197,11 @@ void ZB_copyFrameBuffer(ZBuffer * zb, void *buf,
 	ZB_copyFrameBuffer5R6G5B(zb, buf, linesize);
 	break;
 #endif
+#ifdef TGL_FEATURE_24_BITS
+    case ZB_MODE_RGB24:
+	ZB_copyFrameBufferRGB24(zb, buf, linesize);
+	break;
+#endif
 #ifdef TGL_FEATURE_32_BITS
     case ZB_MODE_RGBA:
 	ZB_copyBuffer(zb, buf, linesize);