Panda3D

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 == (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     if (_one_shot) {
00139       prepare_for_deletion();
00140     }
00141     clear_cube_map_selection();
00142   }
00143 }
00144 
00145 ////////////////////////////////////////////////////////////////////
00146 //     Function: TinyXGraphicsWindow::begin_flip
00147 //       Access: Public, Virtual
00148 //  Description: This function will be called within the draw thread
00149 //               after end_frame() has been called on all windows, to
00150 //               initiate the exchange of the front and back buffers.
00151 //
00152 //               This should instruct the window to prepare for the
00153 //               flip at the next video sync, but it should not wait.
00154 //
00155 //               We have the two separate functions, begin_flip() and
00156 //               end_flip(), to make it easier to flip all of the
00157 //               windows at the same time.
00158 ////////////////////////////////////////////////////////////////////
00159 void TinyXGraphicsWindow::
00160 begin_flip() {
00161   if (_xwindow == (Window)NULL) {
00162     return;
00163   }
00164 
00165   if (_reduced_frame_buffer != (ZBuffer *)NULL) {
00166     // Zoom the reduced buffer onto the full buffer.
00167     ZB_zoomFrameBuffer(_full_frame_buffer, 0, 0, 
00168                        _full_frame_buffer->xsize, _full_frame_buffer->ysize,
00169                        _reduced_frame_buffer, 0, 0,
00170                        _reduced_frame_buffer->xsize, _reduced_frame_buffer->ysize);
00171   }
00172 
00173   // We can't just point the XPutImage directly at our own framebuffer
00174   // data, even if the bytes_per_pixel matches, because some X
00175   // displays will respect the alpha channel and make the window
00176   // transparent there.  We don't want transparent windows where the
00177   // alpha data happens to less than 1.0.
00178   ZB_copyFrameBufferNoAlpha(_full_frame_buffer, _ximage->data, _pitch);
00179 
00180   XPutImage(_display, _xwindow, _gc, _ximage, 0, 0, 0, 0,
00181             _full_frame_buffer->xsize, _full_frame_buffer->ysize);
00182   XFlush(_display);
00183 }
00184 
00185 ////////////////////////////////////////////////////////////////////
00186 //     Function: TinyXGraphicsWindow::supports_pixel_zoom
00187 //       Access: Published, Virtual
00188 //  Description: Returns true if a call to set_pixel_zoom() will be
00189 //               respected, false if it will be ignored.  If this
00190 //               returns false, then get_pixel_factor() will always
00191 //               return 1.0, regardless of what value you specify for
00192 //               set_pixel_zoom().
00193 //
00194 //               This may return false if the underlying renderer
00195 //               doesn't support pixel zooming, or if you have called
00196 //               this on a DisplayRegion that doesn't have both
00197 //               set_clear_color() and set_clear_depth() enabled.
00198 ////////////////////////////////////////////////////////////////////
00199 bool TinyXGraphicsWindow::
00200 supports_pixel_zoom() const {
00201   return true;
00202 }
00203 
00204 ////////////////////////////////////////////////////////////////////
00205 //     Function: TinyXGraphicsWindow::process_events
00206 //       Access: Public, Virtual
00207 //  Description: Do whatever processing is necessary to ensure that
00208 //               the window responds to user events.  Also, honor any
00209 //               requests recently made via request_properties()
00210 //
00211 //               This function is called only within the window
00212 //               thread.
00213 ////////////////////////////////////////////////////////////////////
00214 void TinyXGraphicsWindow::
00215 process_events() {
00216   LightReMutexHolder holder(TinyXGraphicsPipe::_x_mutex);
00217 
00218   GraphicsWindow::process_events();
00219 
00220   if (_xwindow == (Window)0) {
00221     return;
00222   }
00223   
00224   poll_raw_mice();
00225   
00226   XEvent event;
00227   XKeyEvent keyrelease_event;
00228   bool got_keyrelease_event = false;
00229 
00230   while (XCheckIfEvent(_display, &event, check_event, (char *)this)) {
00231     if (XFilterEvent(&event, None)) {
00232       continue;
00233     }
00234 
00235     if (got_keyrelease_event) {
00236       // If a keyrelease event is immediately followed by a matching
00237       // keypress event, that's just key repeat and we should treat
00238       // the two events accordingly.  It would be nice if X provided a
00239       // way to differentiate between keyrepeat and explicit
00240       // keypresses more generally.
00241       got_keyrelease_event = false;
00242 
00243       if (event.type == KeyPress &&
00244           event.xkey.keycode == keyrelease_event.keycode &&
00245           (event.xkey.time - keyrelease_event.time <= 1)) {
00246         // In particular, we only generate down messages for the
00247         // repeated keys, not down-and-up messages.
00248         handle_keystroke(event.xkey);
00249 
00250         // We thought about not generating the keypress event, but we
00251         // need that repeat for backspace.  Rethink later.
00252         handle_keypress(event.xkey);
00253         continue;
00254 
00255       } else {
00256         // This keyrelease event is not immediately followed by a
00257         // matching keypress event, so it's a genuine release.
00258         handle_keyrelease(keyrelease_event);
00259       }
00260     }
00261 
00262     WindowProperties properties;
00263     ButtonHandle button;
00264 
00265     switch (event.type) {
00266     case ReparentNotify:
00267       break;
00268 
00269     case ConfigureNotify:
00270       _awaiting_configure = false;
00271       if (_properties.get_fixed_size()) {
00272         // If the window properties indicate a fixed size only, undo
00273         // any attempt by the user to change them.  In X, there
00274         // doesn't appear to be a way to universally disallow this
00275         // directly (although we do set the min_size and max_size to
00276         // the same value, which seems to work for most window
00277         // managers.)
00278         WindowProperties current_props = get_properties();
00279         if (event.xconfigure.width != current_props.get_x_size() ||
00280             event.xconfigure.height != current_props.get_y_size()) {
00281           XWindowChanges changes;
00282           changes.width = current_props.get_x_size();
00283           changes.height = current_props.get_y_size();
00284           int value_mask = (CWWidth | CWHeight);
00285           XConfigureWindow(_display, _xwindow, value_mask, &changes);
00286         }
00287 
00288       } else {
00289         // A normal window may be resized by the user at will.
00290         properties.set_size(event.xconfigure.width, event.xconfigure.height);
00291         system_changed_properties(properties);
00292         ZB_resize(_full_frame_buffer, NULL, _properties.get_x_size(), _properties.get_y_size());
00293         _pitch = (_full_frame_buffer->xsize * _bytes_per_pixel + 3) & ~3;
00294         create_reduced_frame_buffer();
00295         create_ximage();
00296       }
00297       break;
00298 
00299     case ButtonPress:
00300       // This refers to the mouse buttons.
00301       button = get_mouse_button(event.xbutton);
00302       _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
00303       _input_devices[0].button_down(button);
00304       break;
00305       
00306     case ButtonRelease:
00307       button = get_mouse_button(event.xbutton);
00308       _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
00309       _input_devices[0].button_up(button);
00310       break;
00311 
00312     case MotionNotify:
00313       _input_devices[0].set_pointer_in_window(event.xmotion.x, event.xmotion.y);
00314       break;
00315 
00316     case KeyPress:
00317       handle_keystroke(event.xkey);
00318       handle_keypress(event.xkey);
00319       break;
00320 
00321     case KeyRelease:
00322       // The KeyRelease can't be processed immediately, because we
00323       // have to check first if it's immediately followed by a
00324       // matching KeyPress event.
00325       keyrelease_event = event.xkey;
00326       got_keyrelease_event = true;
00327       break;
00328 
00329     case EnterNotify:
00330       _input_devices[0].set_pointer_in_window(event.xcrossing.x, event.xcrossing.y);
00331       break;
00332 
00333     case LeaveNotify:
00334       _input_devices[0].set_pointer_out_of_window();
00335       break;
00336 
00337     case FocusIn:
00338       properties.set_foreground(true);
00339       system_changed_properties(properties);
00340       break;
00341 
00342     case FocusOut:
00343       properties.set_foreground(false);
00344       system_changed_properties(properties);
00345       break;
00346 
00347     case UnmapNotify:
00348       properties.set_minimized(true);
00349       system_changed_properties(properties);
00350       break;
00351 
00352     case MapNotify:
00353       properties.set_minimized(false);
00354       system_changed_properties(properties);
00355 
00356       // Auto-focus the window when it is mapped.
00357       XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
00358       break;
00359 
00360     case ClientMessage:
00361       if ((Atom)(event.xclient.data.l[0]) == _wm_delete_window) {
00362         // This is a message from the window manager indicating that
00363         // the user has requested to close the window.
00364         string close_request_event = get_close_request_event();
00365         if (!close_request_event.empty()) {
00366           // In this case, the app has indicated a desire to intercept
00367           // the request and process it directly.
00368           throw_event(close_request_event);
00369 
00370         } else {
00371           // In this case, the default case, the app does not intend
00372           // to service the request, so we do by closing the window.
00373 
00374           // TODO: don't release the gsg in the window thread.
00375           close_window();
00376           properties.set_open(false);
00377           system_changed_properties(properties);
00378         }
00379       }
00380       break;
00381 
00382     case DestroyNotify:
00383       // Apparently, we never get a DestroyNotify on a toplevel
00384       // window.  Instead, we rely on hints from the window manager
00385       // (see above).
00386       tinydisplay_cat.info()
00387         << "DestroyNotify\n";
00388       break;
00389 
00390     default:
00391       tinydisplay_cat.error()
00392         << "unhandled X event type " << event.type << "\n";
00393     }
00394   }
00395 
00396   if (got_keyrelease_event) {
00397     // This keyrelease event is not immediately followed by a
00398     // matching keypress event, so it's a genuine release.
00399     handle_keyrelease(keyrelease_event);
00400   }
00401 }
00402 
00403 ////////////////////////////////////////////////////////////////////
00404 //     Function: TinyXGraphicsWindow::close_window
00405 //       Access: Protected, Virtual
00406 //  Description: Closes the window right now.  Called from the window
00407 //               thread.
00408 ////////////////////////////////////////////////////////////////////
00409 void TinyXGraphicsWindow::
00410 close_window() {
00411   if (_gsg != (GraphicsStateGuardian *)NULL) {
00412     TinyGraphicsStateGuardian *tinygsg;
00413     DCAST_INTO_V(tinygsg, _gsg);
00414     tinygsg->_current_frame_buffer = NULL;
00415     _gsg.clear();
00416     _active = false;
00417   }
00418   
00419   x11GraphicsWindow::close_window();
00420 }
00421 
00422 ////////////////////////////////////////////////////////////////////
00423 //     Function: TinyXGraphicsWindow::open_window
00424 //       Access: Protected, Virtual
00425 //  Description: Opens the window right now.  Called from the window
00426 //               thread.  Returns true if the window is successfully
00427 //               opened, or false if there was a problem.
00428 ////////////////////////////////////////////////////////////////////
00429 bool TinyXGraphicsWindow::
00430 open_window() {
00431   TinyXGraphicsPipe *tinyx_pipe;
00432   DCAST_INTO_R(tinyx_pipe, _pipe, false);
00433 
00434   // GSG Creation/Initialization
00435   TinyGraphicsStateGuardian *tinygsg;
00436   if (_gsg == 0) {
00437     // There is no old gsg.  Create a new one.
00438     tinygsg = new TinyGraphicsStateGuardian(_engine, _pipe, NULL);
00439     _gsg = tinygsg;
00440   } else {
00441     DCAST_INTO_R(tinygsg, _gsg, false);
00442   }
00443 
00444   XVisualInfo vinfo_template;
00445   vinfo_template.screen = _screen;
00446   vinfo_template.depth = 32;
00447   vinfo_template.c_class = TrueColor;
00448 
00449   // Try to get each of these properties in turn.
00450   int try_masks[] = {
00451     VisualScreenMask | VisualDepthMask | VisualClassMask,
00452     VisualScreenMask | VisualClassMask,
00453     VisualScreenMask | VisualDepthMask,
00454     VisualScreenMask,
00455     0,
00456   };
00457 
00458   int i = 0;
00459   int num_vinfos = 0;
00460   XVisualInfo *vinfo_array;
00461   while (try_masks[i] != 0 && num_vinfos == 0) {
00462     vinfo_array = 
00463       XGetVisualInfo(_display, try_masks[i], &vinfo_template, &num_vinfos);
00464     ++i;
00465   }
00466 
00467   if (num_vinfos == 0) {
00468     // No suitable X visual.
00469     tinydisplay_cat.error()
00470       << "No suitable X Visual available; cannot open window.\n";
00471     return false;
00472   }
00473   _visual_info = &vinfo_array[0];
00474 
00475   _visual = _visual_info->visual;
00476   _depth = _visual_info->depth;
00477   _bytes_per_pixel = _depth / 8;
00478   if (_bytes_per_pixel == 3) {
00479     // Seems to be a special case.
00480     _bytes_per_pixel = 4;
00481   }
00482   tinydisplay_cat.info()
00483     << "Got X Visual with depth " << _depth << " (bpp " << _bytes_per_pixel << ") and class ";
00484   switch (_visual_info->c_class) {
00485   case TrueColor:
00486     tinydisplay_cat.info(false) << "TrueColor\n";
00487     break;
00488       
00489   case DirectColor:
00490     tinydisplay_cat.info(false) << "DirectColor\n";
00491     break;
00492 
00493   case StaticColor:
00494     tinydisplay_cat.info(false) << "StaticColor\n";
00495     break;
00496 
00497   case StaticGray:
00498     tinydisplay_cat.info(false) << "StaticGray\n";
00499     break;
00500 
00501   case GrayScale:
00502     tinydisplay_cat.info(false) << "GrayScale\n";
00503     break;
00504 
00505   case PseudoColor:
00506     tinydisplay_cat.info(false) << "PseudoColor\n";
00507     break;
00508   }
00509 
00510   setup_colormap(_visual_info);
00511 
00512   if (!x11GraphicsWindow::open_window()) {
00513     return false;
00514   }
00515 
00516   _gc = XCreateGC(_display, _xwindow, 0, NULL);
00517 
00518   create_full_frame_buffer();
00519   if (_full_frame_buffer == NULL) {
00520     tinydisplay_cat.error()
00521       << "Could not create frame buffer.\n";
00522     return false;
00523   }
00524   create_reduced_frame_buffer();
00525   create_ximage();
00526   nassertr(_ximage != NULL, false);
00527 
00528   tinygsg->_current_frame_buffer = _full_frame_buffer;
00529   
00530   tinygsg->reset_if_new();
00531   if (!tinygsg->is_valid()) {
00532     close_window();
00533     return false;
00534   }
00535   
00536   XMapWindow(_display, _xwindow);
00537 
00538   if (_properties.get_raw_mice()) {
00539     open_raw_mice();
00540   } else {
00541     if (tinydisplay_cat.is_debug()) {
00542       tinydisplay_cat.debug()
00543         << "Raw mice not requested.\n";
00544     }
00545   }
00546 
00547   // Create a WindowHandle for ourselves
00548   _window_handle = NativeWindowHandle::make_x11(_xwindow);
00549 
00550   // And tell our parent window that we're now its child.
00551   if (_parent_window_handle != (WindowHandle *)NULL) {
00552     _parent_window_handle->attach_child(_window_handle);
00553   }
00554   
00555   return true;
00556 }
00557 
00558 ////////////////////////////////////////////////////////////////////
00559 //     Function: TinyXGraphicsWindow::pixel_factor_changed
00560 //       Access: Protected, Virtual
00561 //  Description: Called internally when the pixel factor changes.
00562 ////////////////////////////////////////////////////////////////////
00563 void TinyXGraphicsWindow::
00564 pixel_factor_changed() {
00565   x11GraphicsWindow::pixel_factor_changed();
00566   create_reduced_frame_buffer();
00567 }
00568 
00569 ////////////////////////////////////////////////////////////////////
00570 //     Function: TinyXGraphicsWindow::create_full_frame_buffer
00571 //       Access: Private
00572 //  Description: Creates a suitable frame buffer for the current
00573 //               window size.
00574 ////////////////////////////////////////////////////////////////////
00575 void TinyXGraphicsWindow::
00576 create_full_frame_buffer() {
00577   if (_full_frame_buffer != NULL) {
00578     ZB_close(_full_frame_buffer);
00579     _full_frame_buffer = NULL;
00580   }
00581 
00582   int mode;
00583   switch (_bytes_per_pixel) {
00584   case  1:
00585     tinydisplay_cat.error()
00586       << "Palette images are currently not supported.\n";
00587     return;
00588 
00589   case 2:
00590     mode = ZB_MODE_5R6G5B;
00591     break;
00592   case 4:
00593     mode = ZB_MODE_RGBA;
00594     break;
00595 
00596   default:
00597     return;
00598   }
00599 
00600   _full_frame_buffer = ZB_open(_properties.get_x_size(), _properties.get_y_size(), mode, 0, 0, 0, 0);
00601   _pitch = (_full_frame_buffer->xsize * _bytes_per_pixel + 3) & ~3;
00602 }
00603 
00604 ////////////////////////////////////////////////////////////////////
00605 //     Function: TinyXGraphicsWindow::create_reduced_frame_buffer
00606 //       Access: Private
00607 //  Description: Creates a suitable frame buffer for the current
00608 //               window size and pixel zoom.
00609 ////////////////////////////////////////////////////////////////////
00610 void TinyXGraphicsWindow::
00611 create_reduced_frame_buffer() {
00612   if (_reduced_frame_buffer != NULL) {
00613     ZB_close(_reduced_frame_buffer);
00614     _reduced_frame_buffer = NULL;
00615   }
00616 
00617   int x_size = get_fb_x_size();
00618   int y_size = get_fb_y_size();
00619 
00620   if (x_size == _full_frame_buffer->xsize) {
00621     // No zooming is necessary.
00622 
00623   } else {
00624     // The reduced size is different, so we need a separate buffer to
00625     // render into.
00626     _reduced_frame_buffer = ZB_open(x_size, y_size, _full_frame_buffer->mode, 0, 0, 0, 0);
00627   }
00628 }
00629 
00630 
00631 ////////////////////////////////////////////////////////////////////
00632 //     Function: TinyXGraphicsWindow::create_ximage
00633 //       Access: Private
00634 //  Description: Creates a suitable XImage for the current
00635 //               window size.
00636 ////////////////////////////////////////////////////////////////////
00637 void TinyXGraphicsWindow::
00638 create_ximage() {
00639   if (_ximage != NULL) {
00640     PANDA_FREE_ARRAY(_ximage->data);
00641     _ximage->data = NULL;
00642     XDestroyImage(_ximage);
00643     _ximage = NULL;
00644   }
00645 
00646   int image_size = _full_frame_buffer->ysize * _pitch;
00647   char *data = (char *)PANDA_MALLOC_ARRAY(image_size);
00648 
00649   _ximage = XCreateImage(_display, _visual, _depth, ZPixmap, 0, data,
00650                          _full_frame_buffer->xsize, _full_frame_buffer->ysize,
00651                          32, 0);
00652 }
00653 
00654 #endif  // HAVE_X11
00655 
 All Classes Functions Variables Enumerations