Panda3D
 All Classes Functions Variables Enumerations
tinyXGraphicsWindow.cxx
00001 // Filename: tinyXGraphicsWindow.cxx
00002 // Created by:  drose (03May08)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "pandabase.h"
00016 
00017 #ifdef HAVE_X11
00018 
00019 #include "tinyXGraphicsWindow.h"
00020 #include "tinyGraphicsStateGuardian.h"
00021 #include "tinyXGraphicsPipe.h"
00022 #include "config_tinydisplay.h"
00023 
00024 #include "graphicsPipe.h"
00025 #include "keyboardButton.h"
00026 #include "mouseButton.h"
00027 #include "clockObject.h"
00028 #include "pStatTimer.h"
00029 #include "textEncoder.h"
00030 #include "throw_event.h"
00031 #include "lightReMutexHolder.h"
00032 #include "nativeWindowHandle.h"
00033 
00034 TypeHandle TinyXGraphicsWindow::_type_handle;
00035 
00036 ////////////////////////////////////////////////////////////////////
00037 //     Function: TinyXGraphicsWindow::Constructor
00038 //       Access: Public
00039 //  Description:
00040 ////////////////////////////////////////////////////////////////////
00041 TinyXGraphicsWindow::
00042 TinyXGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, 
00043                     const string &name,
00044                     const FrameBufferProperties &fb_prop,
00045                     const WindowProperties &win_prop,
00046                     int flags,
00047                     GraphicsStateGuardian *gsg,
00048                     GraphicsOutput *host) :
00049   x11GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
00050 {
00051   _gc = (GC)NULL;
00052 
00053   _reduced_frame_buffer = NULL;
00054   _full_frame_buffer = NULL;
00055   _ximage = NULL;
00056   update_pixel_factor();
00057 }
00058 
00059 ////////////////////////////////////////////////////////////////////
00060 //     Function: TinyXGraphicsWindow::Destructor
00061 //       Access: Public, Virtual
00062 //  Description:
00063 ////////////////////////////////////////////////////////////////////
00064 TinyXGraphicsWindow::
00065 ~TinyXGraphicsWindow() {
00066   if (_gc != NULL && _display != NULL) {
00067     XFreeGC(_display, _gc);
00068   }
00069   if (_ximage != NULL) {
00070     PANDA_FREE_ARRAY(_ximage->data);
00071     _ximage->data = NULL;
00072     XDestroyImage(_ximage);
00073   }
00074 }
00075 
00076 ////////////////////////////////////////////////////////////////////
00077 //     Function: TinyXGraphicsWindow::begin_frame
00078 //       Access: Public, Virtual
00079 //  Description: This function will be called within the draw thread
00080 //               before beginning rendering for a given frame.  It
00081 //               should do whatever setup is required, and return true
00082 //               if the frame should be rendered, or false if it
00083 //               should be skipped.
00084 ////////////////////////////////////////////////////////////////////
00085 bool TinyXGraphicsWindow::
00086 begin_frame(FrameMode mode, Thread *current_thread) {
00087   PStatTimer timer(_make_current_pcollector, current_thread);
00088 
00089   if (_xwindow == (X11_Window)NULL) {
00090     return false;
00091   }
00092 
00093   begin_frame_spam(mode);
00094   if (_gsg == (GraphicsStateGuardian *)NULL) {
00095     return false;
00096   }
00097   if (_awaiting_configure) {
00098     // Don't attempt to draw while we have just reconfigured the
00099     // window and we haven't got the notification back yet.
00100     return false;
00101   }
00102 
00103   TinyGraphicsStateGuardian *tinygsg;
00104   DCAST_INTO_R(tinygsg, _gsg, false);
00105 
00106   if (_reduced_frame_buffer != (ZBuffer *)NULL) {
00107     tinygsg->_current_frame_buffer = _reduced_frame_buffer;
00108   } else {
00109     tinygsg->_current_frame_buffer = _full_frame_buffer;
00110   }
00111   tinygsg->reset_if_new();
00112   
00113   _gsg->set_current_properties(&get_fb_properties());
00114   return _gsg->begin_frame(current_thread);
00115 }
00116 
00117 ////////////////////////////////////////////////////////////////////
00118 //     Function: TinyXGraphicsWindow::end_frame
00119 //       Access: Public, Virtual
00120 //  Description: This function will be called within the draw thread
00121 //               after rendering is completed for a given frame.  It
00122 //               should do whatever finalization is required.
00123 ////////////////////////////////////////////////////////////////////
00124 void TinyXGraphicsWindow::
00125 end_frame(FrameMode mode, Thread *current_thread) {
00126   end_frame_spam(mode);
00127   nassertv(_gsg != (GraphicsStateGuardian *)NULL);
00128 
00129   if (mode == FM_render) {
00130     // end_render_texture();
00131     copy_to_textures();
00132   }
00133 
00134   _gsg->end_frame(current_thread);
00135 
00136   if (mode == FM_render) {
00137     trigger_flip();
00138     clear_cube_map_selection();
00139   }
00140 }
00141 
00142 ////////////////////////////////////////////////////////////////////
00143 //     Function: TinyXGraphicsWindow::end_flip
00144 //       Access: Public, Virtual
00145 //  Description: This function will be called within the draw thread
00146 //               after begin_flip() has been called on all windows, to
00147 //               finish the exchange of the front and back buffers.
00148 //
00149 //               This should cause the window to wait for the flip, if
00150 //               necessary.
00151 ////////////////////////////////////////////////////////////////////
00152 void TinyXGraphicsWindow::
00153 end_flip() {
00154   if (_xwindow == (X11_Window)NULL || !_flip_ready) {
00155     GraphicsWindow::end_flip();
00156     return;
00157   }
00158 
00159   if (_reduced_frame_buffer != (ZBuffer *)NULL) {
00160     // Zoom the reduced buffer onto the full buffer.
00161     ZB_zoomFrameBuffer(_full_frame_buffer, 0, 0, 
00162                        _full_frame_buffer->xsize, _full_frame_buffer->ysize,
00163                        _reduced_frame_buffer, 0, 0,
00164                        _reduced_frame_buffer->xsize, _reduced_frame_buffer->ysize);
00165   }
00166 
00167   // We can't just point the XPutImage directly at our own framebuffer
00168   // data, even if the bytes_per_pixel matches, because some X
00169   // displays will respect the alpha channel and make the window
00170   // transparent there.  We don't want transparent windows where the
00171   // alpha data happens to less than 1.0.
00172   ZB_copyFrameBufferNoAlpha(_full_frame_buffer, _ximage->data, _pitch);
00173 
00174   XPutImage(_display, _xwindow, _gc, _ximage, 0, 0, 0, 0,
00175             _full_frame_buffer->xsize, _full_frame_buffer->ysize);
00176   XFlush(_display);
00177   GraphicsWindow::end_flip();
00178 }
00179 
00180 ////////////////////////////////////////////////////////////////////
00181 //     Function: TinyXGraphicsWindow::supports_pixel_zoom
00182 //       Access: Published, Virtual
00183 //  Description: Returns true if a call to set_pixel_zoom() will be
00184 //               respected, false if it will be ignored.  If this
00185 //               returns false, then get_pixel_factor() will always
00186 //               return 1.0, regardless of what value you specify for
00187 //               set_pixel_zoom().
00188 //
00189 //               This may return false if the underlying renderer
00190 //               doesn't support pixel zooming, or if you have called
00191 //               this on a DisplayRegion that doesn't have both
00192 //               set_clear_color() and set_clear_depth() enabled.
00193 ////////////////////////////////////////////////////////////////////
00194 bool TinyXGraphicsWindow::
00195 supports_pixel_zoom() const {
00196   return true;
00197 }
00198 
00199 ////////////////////////////////////////////////////////////////////
00200 //     Function: TinyXGraphicsWindow::process_events
00201 //       Access: Public, Virtual
00202 //  Description: Do whatever processing is necessary to ensure that
00203 //               the window responds to user events.  Also, honor any
00204 //               requests recently made via request_properties()
00205 //
00206 //               This function is called only within the window
00207 //               thread.
00208 ////////////////////////////////////////////////////////////////////
00209 void TinyXGraphicsWindow::
00210 process_events() {
00211   LightReMutexHolder holder(TinyXGraphicsPipe::_x_mutex);
00212 
00213   GraphicsWindow::process_events();
00214 
00215   if (_xwindow == (X11_Window)0) {
00216     return;
00217   }
00218   
00219   poll_raw_mice();
00220   
00221   XEvent event;
00222   XKeyEvent keyrelease_event;
00223   bool got_keyrelease_event = false;
00224 
00225   while (XCheckIfEvent(_display, &event, check_event, (char *)this)) {
00226     if (XFilterEvent(&event, None)) {
00227       continue;
00228     }
00229 
00230     if (got_keyrelease_event) {
00231       // If a keyrelease event is immediately followed by a matching
00232       // keypress event, that's just key repeat and we should treat
00233       // the two events accordingly.  It would be nice if X provided a
00234       // way to differentiate between keyrepeat and explicit
00235       // keypresses more generally.
00236       got_keyrelease_event = false;
00237 
00238       if (event.type == KeyPress &&
00239           event.xkey.keycode == keyrelease_event.keycode &&
00240           (event.xkey.time - keyrelease_event.time <= 1)) {
00241         // In particular, we only generate down messages for the
00242         // repeated keys, not down-and-up messages.
00243         handle_keystroke(event.xkey);
00244 
00245         // We thought about not generating the keypress event, but we
00246         // need that repeat for backspace.  Rethink later.
00247         handle_keypress(event.xkey);
00248         continue;
00249 
00250       } else {
00251         // This keyrelease event is not immediately followed by a
00252         // matching keypress event, so it's a genuine release.
00253         handle_keyrelease(keyrelease_event);
00254       }
00255     }
00256 
00257     WindowProperties properties;
00258     ButtonHandle button;
00259 
00260     switch (event.type) {
00261     case ReparentNotify:
00262       break;
00263 
00264     case ConfigureNotify:
00265       _awaiting_configure = false;
00266       if (_properties.get_fixed_size()) {
00267         // If the window properties indicate a fixed size only, undo
00268         // any attempt by the user to change them.  In X, there
00269         // doesn't appear to be a way to universally disallow this
00270         // directly (although we do set the min_size and max_size to
00271         // the same value, which seems to work for most window
00272         // managers.)
00273         WindowProperties current_props = get_properties();
00274         if (event.xconfigure.width != current_props.get_x_size() ||
00275             event.xconfigure.height != current_props.get_y_size()) {
00276           XWindowChanges changes;
00277           changes.width = current_props.get_x_size();
00278           changes.height = current_props.get_y_size();
00279           int value_mask = (CWWidth | CWHeight);
00280           XConfigureWindow(_display, _xwindow, value_mask, &changes);
00281         }
00282 
00283       } else {
00284         // A normal window may be resized by the user at will.
00285         properties.set_size(event.xconfigure.width, event.xconfigure.height);
00286         system_changed_properties(properties);
00287         ZB_resize(_full_frame_buffer, NULL, _properties.get_x_size(), _properties.get_y_size());
00288         _pitch = (_full_frame_buffer->xsize * _bytes_per_pixel + 3) & ~3;
00289         create_reduced_frame_buffer();
00290         create_ximage();
00291       }
00292       break;
00293 
00294     case ButtonPress:
00295       // This refers to the mouse buttons.
00296       button = get_mouse_button(event.xbutton);
00297       _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
00298       _input_devices[0].button_down(button);
00299       break;
00300       
00301     case ButtonRelease:
00302       button = get_mouse_button(event.xbutton);
00303       _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
00304       _input_devices[0].button_up(button);
00305       break;
00306 
00307     case MotionNotify:
00308       _input_devices[0].set_pointer_in_window(event.xmotion.x, event.xmotion.y);
00309       break;
00310 
00311     case KeyPress:
00312       handle_keystroke(event.xkey);
00313       handle_keypress(event.xkey);
00314       break;
00315 
00316     case KeyRelease:
00317       // The KeyRelease can't be processed immediately, because we
00318       // have to check first if it's immediately followed by a
00319       // matching KeyPress event.
00320       keyrelease_event = event.xkey;
00321       got_keyrelease_event = true;
00322       break;
00323 
00324     case EnterNotify:
00325       _input_devices[0].set_pointer_in_window(event.xcrossing.x, event.xcrossing.y);
00326       break;
00327 
00328     case LeaveNotify:
00329       _input_devices[0].set_pointer_out_of_window();
00330       break;
00331 
00332     case FocusIn:
00333       properties.set_foreground(true);
00334       system_changed_properties(properties);
00335       break;
00336 
00337     case FocusOut:
00338       _input_devices[0].focus_lost();
00339       properties.set_foreground(false);
00340       system_changed_properties(properties);
00341       break;
00342 
00343     case UnmapNotify:
00344       properties.set_minimized(true);
00345       system_changed_properties(properties);
00346       break;
00347 
00348     case MapNotify:
00349       properties.set_minimized(false);
00350       system_changed_properties(properties);
00351 
00352       // Auto-focus the window when it is mapped.
00353       XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
00354       break;
00355 
00356     case ClientMessage:
00357       if ((Atom)(event.xclient.data.l[0]) == _wm_delete_window) {
00358         // This is a message from the window manager indicating that
00359         // the user has requested to close the window.
00360         string close_request_event = get_close_request_event();
00361         if (!close_request_event.empty()) {
00362           // In this case, the app has indicated a desire to intercept
00363           // the request and process it directly.
00364           throw_event(close_request_event);
00365 
00366         } else {
00367           // In this case, the default case, the app does not intend
00368           // to service the request, so we do by closing the window.
00369 
00370           // TODO: don't release the gsg in the window thread.
00371           close_window();
00372           properties.set_open(false);
00373           system_changed_properties(properties);
00374         }
00375       }
00376       break;
00377 
00378     case DestroyNotify:
00379       // Apparently, we never get a DestroyNotify on a toplevel
00380       // window.  Instead, we rely on hints from the window manager
00381       // (see above).
00382       tinydisplay_cat.info()
00383         << "DestroyNotify\n";
00384       break;
00385 
00386     default:
00387       tinydisplay_cat.error()
00388         << "unhandled X event type " << event.type << "\n";
00389     }
00390   }
00391 
00392   if (got_keyrelease_event) {
00393     // This keyrelease event is not immediately followed by a
00394     // matching keypress event, so it's a genuine release.
00395     handle_keyrelease(keyrelease_event);
00396   }
00397 }
00398 
00399 ////////////////////////////////////////////////////////////////////
00400 //     Function: TinyXGraphicsWindow::close_window
00401 //       Access: Protected, Virtual
00402 //  Description: Closes the window right now.  Called from the window
00403 //               thread.
00404 ////////////////////////////////////////////////////////////////////
00405 void TinyXGraphicsWindow::
00406 close_window() {
00407   if (_gsg != (GraphicsStateGuardian *)NULL) {
00408     TinyGraphicsStateGuardian *tinygsg;
00409     DCAST_INTO_V(tinygsg, _gsg);
00410     tinygsg->_current_frame_buffer = NULL;
00411     _gsg.clear();
00412   }
00413   
00414   x11GraphicsWindow::close_window();
00415 }
00416 
00417 ////////////////////////////////////////////////////////////////////
00418 //     Function: TinyXGraphicsWindow::open_window
00419 //       Access: Protected, Virtual
00420 //  Description: Opens the window right now.  Called from the window
00421 //               thread.  Returns true if the window is successfully
00422 //               opened, or false if there was a problem.
00423 ////////////////////////////////////////////////////////////////////
00424 bool TinyXGraphicsWindow::
00425 open_window() {
00426   TinyXGraphicsPipe *tinyx_pipe;
00427   DCAST_INTO_R(tinyx_pipe, _pipe, false);
00428 
00429   // GSG Creation/Initialization
00430   TinyGraphicsStateGuardian *tinygsg;
00431   if (_gsg == 0) {
00432     // There is no old gsg.  Create a new one.
00433     tinygsg = new TinyGraphicsStateGuardian(_engine, _pipe, NULL);
00434     _gsg = tinygsg;
00435   } else {
00436     DCAST_INTO_R(tinygsg, _gsg, false);
00437   }
00438 
00439   XVisualInfo vinfo_template;
00440   vinfo_template.screen = _screen;
00441   vinfo_template.depth = 32;
00442   vinfo_template.c_class = TrueColor;
00443 
00444   // Try to get each of these properties in turn.
00445   int try_masks[] = {
00446     VisualScreenMask | VisualDepthMask | VisualClassMask,
00447     VisualScreenMask | VisualClassMask,
00448     VisualScreenMask | VisualDepthMask,
00449     VisualScreenMask,
00450     0,
00451   };
00452 
00453   int i = 0;
00454   int num_vinfos = 0;
00455   XVisualInfo *vinfo_array;
00456   while (try_masks[i] != 0 && num_vinfos == 0) {
00457     vinfo_array = 
00458       XGetVisualInfo(_display, try_masks[i], &vinfo_template, &num_vinfos);
00459     ++i;
00460   }
00461 
00462   if (num_vinfos == 0) {
00463     // No suitable X visual.
00464     tinydisplay_cat.error()
00465       << "No suitable X Visual available; cannot open window.\n";
00466     return false;
00467   }
00468   _visual_info = &vinfo_array[0];
00469 
00470   _visual = _visual_info->visual;
00471   _depth = _visual_info->depth;
00472   _bytes_per_pixel = _depth / 8;
00473   if (_bytes_per_pixel == 3) {
00474     // Seems to be a special case.
00475     _bytes_per_pixel = 4;
00476   }
00477   tinydisplay_cat.info()
00478     << "Got X Visual with depth " << _depth << " (bpp " << _bytes_per_pixel << ") and class ";
00479   switch (_visual_info->c_class) {
00480   case TrueColor:
00481     tinydisplay_cat.info(false) << "TrueColor\n";
00482     break;
00483       
00484   case DirectColor:
00485     tinydisplay_cat.info(false) << "DirectColor\n";
00486     break;
00487 
00488   case StaticColor:
00489     tinydisplay_cat.info(false) << "StaticColor\n";
00490     break;
00491 
00492   case StaticGray:
00493     tinydisplay_cat.info(false) << "StaticGray\n";
00494     break;
00495 
00496   case GrayScale:
00497     tinydisplay_cat.info(false) << "GrayScale\n";
00498     break;
00499 
00500   case PseudoColor:
00501     tinydisplay_cat.info(false) << "PseudoColor\n";
00502     break;
00503   }
00504 
00505   setup_colormap(_visual_info);
00506 
00507   if (!x11GraphicsWindow::open_window()) {
00508     return false;
00509   }
00510 
00511   _gc = XCreateGC(_display, _xwindow, 0, NULL);
00512 
00513   create_full_frame_buffer();
00514   if (_full_frame_buffer == NULL) {
00515     tinydisplay_cat.error()
00516       << "Could not create frame buffer.\n";
00517     return false;
00518   }
00519   create_reduced_frame_buffer();
00520   create_ximage();
00521   nassertr(_ximage != NULL, false);
00522 
00523   tinygsg->_current_frame_buffer = _full_frame_buffer;
00524   
00525   tinygsg->reset_if_new();
00526   if (!tinygsg->is_valid()) {
00527     close_window();
00528     return false;
00529   }
00530   
00531   XMapWindow(_display, _xwindow);
00532 
00533   if (_properties.get_raw_mice()) {
00534     open_raw_mice();
00535   } else {
00536     if (tinydisplay_cat.is_debug()) {
00537       tinydisplay_cat.debug()
00538         << "Raw mice not requested.\n";
00539     }
00540   }
00541 
00542   // Create a WindowHandle for ourselves
00543   _window_handle = NativeWindowHandle::make_x11(_xwindow);
00544 
00545   // And tell our parent window that we're now its child.
00546   if (_parent_window_handle != (WindowHandle *)NULL) {
00547     _parent_window_handle->attach_child(_window_handle);
00548   }
00549   
00550   return true;
00551 }
00552 
00553 ////////////////////////////////////////////////////////////////////
00554 //     Function: TinyXGraphicsWindow::pixel_factor_changed
00555 //       Access: Protected, Virtual
00556 //  Description: Called internally when the pixel factor changes.
00557 ////////////////////////////////////////////////////////////////////
00558 void TinyXGraphicsWindow::
00559 pixel_factor_changed() {
00560   x11GraphicsWindow::pixel_factor_changed();
00561   create_reduced_frame_buffer();
00562 }
00563 
00564 ////////////////////////////////////////////////////////////////////
00565 //     Function: TinyXGraphicsWindow::create_full_frame_buffer
00566 //       Access: Private
00567 //  Description: Creates a suitable frame buffer for the current
00568 //               window size.
00569 ////////////////////////////////////////////////////////////////////
00570 void TinyXGraphicsWindow::
00571 create_full_frame_buffer() {
00572   if (_full_frame_buffer != NULL) {
00573     ZB_close(_full_frame_buffer);
00574     _full_frame_buffer = NULL;
00575   }
00576 
00577   int mode;
00578   switch (_bytes_per_pixel) {
00579   case  1:
00580     tinydisplay_cat.error()
00581       << "Palette images are currently not supported.\n";
00582     return;
00583 
00584   case 2:
00585     mode = ZB_MODE_5R6G5B;
00586     break;
00587   case 4:
00588     mode = ZB_MODE_RGBA;
00589     break;
00590 
00591   default:
00592     return;
00593   }
00594 
00595   _full_frame_buffer = ZB_open(_properties.get_x_size(), _properties.get_y_size(), mode, 0, 0, 0, 0);
00596   _pitch = (_full_frame_buffer->xsize * _bytes_per_pixel + 3) & ~3;
00597 }
00598 
00599 ////////////////////////////////////////////////////////////////////
00600 //     Function: TinyXGraphicsWindow::create_reduced_frame_buffer
00601 //       Access: Private
00602 //  Description: Creates a suitable frame buffer for the current
00603 //               window size and pixel zoom.
00604 ////////////////////////////////////////////////////////////////////
00605 void TinyXGraphicsWindow::
00606 create_reduced_frame_buffer() {
00607   if (_reduced_frame_buffer != NULL) {
00608     ZB_close(_reduced_frame_buffer);
00609     _reduced_frame_buffer = NULL;
00610   }
00611 
00612   int x_size = get_fb_x_size();
00613   int y_size = get_fb_y_size();
00614 
00615   if (x_size == _full_frame_buffer->xsize) {
00616     // No zooming is necessary.
00617 
00618   } else {
00619     // The reduced size is different, so we need a separate buffer to
00620     // render into.
00621     _reduced_frame_buffer = ZB_open(x_size, y_size, _full_frame_buffer->mode, 0, 0, 0, 0);
00622   }
00623 }
00624 
00625 
00626 ////////////////////////////////////////////////////////////////////
00627 //     Function: TinyXGraphicsWindow::create_ximage
00628 //       Access: Private
00629 //  Description: Creates a suitable XImage for the current
00630 //               window size.
00631 ////////////////////////////////////////////////////////////////////
00632 void TinyXGraphicsWindow::
00633 create_ximage() {
00634   if (_ximage != NULL) {
00635     PANDA_FREE_ARRAY(_ximage->data);
00636     _ximage->data = NULL;
00637     XDestroyImage(_ximage);
00638     _ximage = NULL;
00639   }
00640 
00641   int image_size = _full_frame_buffer->ysize * _pitch;
00642   char *data = (char *)PANDA_MALLOC_ARRAY(image_size);
00643 
00644   _ximage = XCreateImage(_display, _visual, _depth, ZPixmap, 0, data,
00645                          _full_frame_buffer->xsize, _full_frame_buffer->ysize,
00646                          32, 0);
00647 }
00648 
00649 #endif  // HAVE_X11
00650 
 All Classes Functions Variables Enumerations