From 19e0b0824cf1e890b1ef2478679d0a751ced2a08 Mon Sep 17 00:00:00 2001 From: David Rose Date: Sun, 4 May 2008 08:00:12 +0000 Subject: [PATCH] X support --- panda/src/tinydisplay/Sources.pp | 6 +- panda/src/tinydisplay/config_tinydisplay.cxx | 45 +- panda/src/tinydisplay/config_tinydisplay.h | 8 + .../tinydisplay/tinyGraphicsStateGuardian.cxx | 24 +- .../tinydisplay/tinyGraphicsStateGuardian.h | 12 +- ...nyGraphicsPipe.I => tinySDLGraphicsPipe.I} | 2 +- ...aphicsPipe.cxx => tinySDLGraphicsPipe.cxx} | 46 +- ...nyGraphicsPipe.h => tinySDLGraphicsPipe.h} | 22 +- ...aphicsWindow.I => tinySDLGraphicsWindow.I} | 2 +- ...csWindow.cxx => tinySDLGraphicsWindow.cxx} | 89 +- ...aphicsWindow.h => tinySDLGraphicsWindow.h} | 35 +- panda/src/tinydisplay/tinyXGraphicsPipe.I | 76 + panda/src/tinydisplay/tinyXGraphicsPipe.cxx | 337 ++++ panda/src/tinydisplay/tinyXGraphicsPipe.h | 145 ++ panda/src/tinydisplay/tinyXGraphicsWindow.I | 18 + panda/src/tinydisplay/tinyXGraphicsWindow.cxx | 1620 +++++++++++++++++ panda/src/tinydisplay/tinyXGraphicsWindow.h | 136 ++ panda/src/tinydisplay/zbuffer.c | 34 +- 18 files changed, 2548 insertions(+), 109 deletions(-) rename panda/src/tinydisplay/{tinyGraphicsPipe.I => tinySDLGraphicsPipe.I} (94%) rename panda/src/tinydisplay/{tinyGraphicsPipe.cxx => tinySDLGraphicsPipe.cxx} (77%) rename panda/src/tinydisplay/{tinyGraphicsPipe.h => tinySDLGraphicsPipe.h} (81%) rename panda/src/tinydisplay/{tinyGraphicsWindow.I => tinySDLGraphicsWindow.I} (94%) rename panda/src/tinydisplay/{tinyGraphicsWindow.cxx => tinySDLGraphicsWindow.cxx} (90%) rename panda/src/tinydisplay/{tinyGraphicsWindow.h => tinySDLGraphicsWindow.h} (71%) create mode 100644 panda/src/tinydisplay/tinyXGraphicsPipe.I create mode 100644 panda/src/tinydisplay/tinyXGraphicsPipe.cxx create mode 100644 panda/src/tinydisplay/tinyXGraphicsPipe.h create mode 100644 panda/src/tinydisplay/tinyXGraphicsWindow.I create mode 100644 panda/src/tinydisplay/tinyXGraphicsWindow.cxx create mode 100644 panda/src/tinydisplay/tinyXGraphicsWindow.h diff --git a/panda/src/tinydisplay/Sources.pp b/panda/src/tinydisplay/Sources.pp index 130826ceea..e4501665b9 100644 --- a/panda/src/tinydisplay/Sources.pp +++ b/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 \ diff --git a/panda/src/tinydisplay/config_tinydisplay.cxx b/panda/src/tinydisplay/config_tinydisplay.cxx index 906b2b39d3..103fd50fbe 100644 --- a/panda/src/tinydisplay/config_tinydisplay.cxx +++ b/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"); diff --git a/panda/src/tinydisplay/config_tinydisplay.h b/panda/src/tinydisplay/config_tinydisplay.h index c2997fd64e..f32800bb8c 100644 --- a/panda/src/tinydisplay/config_tinydisplay.h +++ b/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 diff --git a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx index 57ba203fbb..43db911196 100644 --- a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx +++ b/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; diff --git a/panda/src/tinydisplay/tinyGraphicsStateGuardian.h b/panda/src/tinydisplay/tinyGraphicsStateGuardian.h index e1bd23974a..0949a5b2df 100644 --- a/panda/src/tinydisplay/tinyGraphicsStateGuardian.h +++ b/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; diff --git a/panda/src/tinydisplay/tinyGraphicsPipe.I b/panda/src/tinydisplay/tinySDLGraphicsPipe.I similarity index 94% rename from panda/src/tinydisplay/tinyGraphicsPipe.I rename to panda/src/tinydisplay/tinySDLGraphicsPipe.I index ae8b199173..1febe09e4f 100644 --- a/panda/src/tinydisplay/tinyGraphicsPipe.I +++ b/panda/src/tinydisplay/tinySDLGraphicsPipe.I @@ -1,4 +1,4 @@ -// Filename: tinyGraphicsPipe.I +// Filename: tinySDLGraphicsPipe.I // Created by: drose (24Apr08) // //////////////////////////////////////////////////////////////////// diff --git a/panda/src/tinydisplay/tinyGraphicsPipe.cxx b/panda/src/tinydisplay/tinySDLGraphicsPipe.cxx similarity index 77% rename from panda/src/tinydisplay/tinyGraphicsPipe.cxx rename to panda/src/tinydisplay/tinySDLGraphicsPipe.cxx index 964dd423b3..151606333c 100644 --- a/panda/src/tinydisplay/tinyGraphicsPipe.cxx +++ b/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. diff --git a/panda/src/tinydisplay/tinyGraphicsPipe.h b/panda/src/tinydisplay/tinySDLGraphicsPipe.h similarity index 81% rename from panda/src/tinydisplay/tinyGraphicsPipe.h rename to panda/src/tinydisplay/tinySDLGraphicsPipe.h index 89d03528c4..7612477bff 100644 --- a/panda/src/tinydisplay/tinyGraphicsPipe.h +++ b/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 diff --git a/panda/src/tinydisplay/tinyGraphicsWindow.I b/panda/src/tinydisplay/tinySDLGraphicsWindow.I similarity index 94% rename from panda/src/tinydisplay/tinyGraphicsWindow.I rename to panda/src/tinydisplay/tinySDLGraphicsWindow.I index 8ec94ffdb8..6c7e067ab8 100644 --- a/panda/src/tinydisplay/tinyGraphicsWindow.I +++ b/panda/src/tinydisplay/tinySDLGraphicsWindow.I @@ -1,4 +1,4 @@ -// Filename: tinyGraphicsWindow.I +// Filename: tinySDLGraphicsWindow.I // Created by: drose (24Apr08) // //////////////////////////////////////////////////////////////////// diff --git a/panda/src/tinydisplay/tinyGraphicsWindow.cxx b/panda/src/tinydisplay/tinySDLGraphicsWindow.cxx similarity index 90% rename from panda/src/tinydisplay/tinyGraphicsWindow.cxx rename to panda/src/tinydisplay/tinySDLGraphicsWindow.cxx index fd79d42d73..95da2dcda7 100644 --- a/panda/src/tinydisplay/tinyGraphicsWindow.cxx +++ b/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: diff --git a/panda/src/tinydisplay/tinyGraphicsWindow.h b/panda/src/tinydisplay/tinySDLGraphicsWindow.h similarity index 71% rename from panda/src/tinydisplay/tinyGraphicsWindow.h rename to panda/src/tinydisplay/tinySDLGraphicsWindow.h index e1c1d4cd50..6b05a7102f 100644 --- a/panda/src/tinydisplay/tinyGraphicsWindow.h +++ b/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 diff --git a/panda/src/tinydisplay/tinyXGraphicsPipe.I b/panda/src/tinydisplay/tinyXGraphicsPipe.I new file mode 100644 index 0000000000..10599e8880 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// 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; +} diff --git a/panda/src/tinydisplay/tinyXGraphicsPipe.cxx b/panda/src/tinydisplay/tinyXGraphicsPipe.cxx new file mode 100644 index 0000000000..acb0f26694 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/panda/src/tinydisplay/tinyXGraphicsPipe.h b/panda/src/tinydisplay/tinyXGraphicsPipe.h new file mode 100644 index 0000000000..d52b96f9ce --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 +#include + +#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 diff --git a/panda/src/tinydisplay/tinyXGraphicsWindow.I b/panda/src/tinydisplay/tinyXGraphicsWindow.I new file mode 100644 index 0000000000..a46d6ac9f1 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + diff --git a/panda/src/tinydisplay/tinyXGraphicsWindow.cxx b/panda/src/tinydisplay/tinyXGraphicsWindow.cxx new file mode 100644 index 0000000000..f5d7d8f7a3 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 +#include +#include +#include +#include + +#ifdef HAVE_LINUX_INPUT_H +#include +#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= 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 + diff --git a/panda/src/tinydisplay/tinyXGraphicsWindow.h b/panda/src/tinydisplay/tinyXGraphicsWindow.h new file mode 100644 index 0000000000..a303f75464 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 _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 + diff --git a/panda/src/tinydisplay/zbuffer.c b/panda/src/tinydisplay/zbuffer.c index ee9131022b..4f4542764b 100644 --- a/panda/src/tinydisplay/zbuffer.c +++ b/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 @@ -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);