| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650 |
- // Filename: tinyXGraphicsWindow.cxx
- // Created by: drose (03May08)
- //
- ////////////////////////////////////////////////////////////////////
- //
- // PANDA 3D SOFTWARE
- // Copyright (c) Carnegie Mellon University. All rights reserved.
- //
- // All use of this software is subject to the terms of the revised BSD
- // license. You should have received a copy of this license along
- // with this source code in a file named "LICENSE."
- //
- ////////////////////////////////////////////////////////////////////
- #include "pandabase.h"
- #ifdef HAVE_X11
- #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 "lightReMutexHolder.h"
- #include "nativeWindowHandle.h"
- TypeHandle TinyXGraphicsWindow::_type_handle;
- ////////////////////////////////////////////////////////////////////
- // Function: TinyXGraphicsWindow::Constructor
- // Access: Public
- // Description:
- ////////////////////////////////////////////////////////////////////
- TinyXGraphicsWindow::
- TinyXGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
- const string &name,
- const FrameBufferProperties &fb_prop,
- const WindowProperties &win_prop,
- int flags,
- GraphicsStateGuardian *gsg,
- GraphicsOutput *host) :
- x11GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
- {
- _gc = (GC)NULL;
- _reduced_frame_buffer = NULL;
- _full_frame_buffer = NULL;
- _ximage = NULL;
- update_pixel_factor();
- }
- ////////////////////////////////////////////////////////////////////
- // Function: TinyXGraphicsWindow::Destructor
- // Access: Public, Virtual
- // Description:
- ////////////////////////////////////////////////////////////////////
- TinyXGraphicsWindow::
- ~TinyXGraphicsWindow() {
- if (_gc != NULL && _display != NULL) {
- XFreeGC(_display, _gc);
- }
- if (_ximage != NULL) {
- PANDA_FREE_ARRAY(_ximage->data);
- _ximage->data = NULL;
- XDestroyImage(_ximage);
- }
- }
- ////////////////////////////////////////////////////////////////////
- // 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);
- if (_xwindow == (X11_Window)NULL) {
- return false;
- }
- 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);
- if (_reduced_frame_buffer != (ZBuffer *)NULL) {
- tinygsg->_current_frame_buffer = _reduced_frame_buffer;
- } else {
- tinygsg->_current_frame_buffer = _full_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();
- clear_cube_map_selection();
- }
- }
- ////////////////////////////////////////////////////////////////////
- // Function: TinyXGraphicsWindow::end_flip
- // Access: Public, Virtual
- // Description: This function will be called within the draw thread
- // after begin_flip() has been called on all windows, to
- // finish the exchange of the front and back buffers.
- //
- // This should cause the window to wait for the flip, if
- // necessary.
- ////////////////////////////////////////////////////////////////////
- void TinyXGraphicsWindow::
- end_flip() {
- if (_xwindow == (X11_Window)NULL || !_flip_ready) {
- GraphicsWindow::end_flip();
- return;
- }
- if (_reduced_frame_buffer != (ZBuffer *)NULL) {
- // Zoom the reduced buffer onto the full buffer.
- ZB_zoomFrameBuffer(_full_frame_buffer, 0, 0,
- _full_frame_buffer->xsize, _full_frame_buffer->ysize,
- _reduced_frame_buffer, 0, 0,
- _reduced_frame_buffer->xsize, _reduced_frame_buffer->ysize);
- }
- // We can't just point the XPutImage directly at our own framebuffer
- // data, even if the bytes_per_pixel matches, because some X
- // displays will respect the alpha channel and make the window
- // transparent there. We don't want transparent windows where the
- // alpha data happens to less than 1.0.
- ZB_copyFrameBufferNoAlpha(_full_frame_buffer, _ximage->data, _pitch);
- XPutImage(_display, _xwindow, _gc, _ximage, 0, 0, 0, 0,
- _full_frame_buffer->xsize, _full_frame_buffer->ysize);
- XFlush(_display);
- GraphicsWindow::end_flip();
- }
- ////////////////////////////////////////////////////////////////////
- // Function: TinyXGraphicsWindow::supports_pixel_zoom
- // Access: Published, Virtual
- // Description: Returns true if a call to set_pixel_zoom() will be
- // respected, false if it will be ignored. If this
- // returns false, then get_pixel_factor() will always
- // return 1.0, regardless of what value you specify for
- // set_pixel_zoom().
- //
- // This may return false if the underlying renderer
- // doesn't support pixel zooming, or if you have called
- // this on a DisplayRegion that doesn't have both
- // set_clear_color() and set_clear_depth() enabled.
- ////////////////////////////////////////////////////////////////////
- bool TinyXGraphicsWindow::
- supports_pixel_zoom() const {
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // 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() {
- LightReMutexHolder holder(TinyXGraphicsPipe::_x_mutex);
- GraphicsWindow::process_events();
- if (_xwindow == (X11_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);
- ZB_resize(_full_frame_buffer, NULL, _properties.get_x_size(), _properties.get_y_size());
- _pitch = (_full_frame_buffer->xsize * _bytes_per_pixel + 3) & ~3;
- create_reduced_frame_buffer();
- create_ximage();
- }
- 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:
- _input_devices[0].focus_lost();
- 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::close_window
- // Access: Protected, Virtual
- // Description: Closes the window right now. Called from the window
- // thread.
- ////////////////////////////////////////////////////////////////////
- void TinyXGraphicsWindow::
- close_window() {
- if (_gsg != (GraphicsStateGuardian *)NULL) {
- TinyGraphicsStateGuardian *tinygsg;
- DCAST_INTO_V(tinygsg, _gsg);
- tinygsg->_current_frame_buffer = NULL;
- _gsg.clear();
- }
-
- x11GraphicsWindow::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(_engine, _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;
- }
- _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;
- }
- setup_colormap(_visual_info);
- if (!x11GraphicsWindow::open_window()) {
- return false;
- }
- _gc = XCreateGC(_display, _xwindow, 0, NULL);
- create_full_frame_buffer();
- if (_full_frame_buffer == NULL) {
- tinydisplay_cat.error()
- << "Could not create frame buffer.\n";
- return false;
- }
- create_reduced_frame_buffer();
- create_ximage();
- nassertr(_ximage != NULL, false);
- tinygsg->_current_frame_buffer = _full_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";
- }
- }
- // Create a WindowHandle for ourselves
- _window_handle = NativeWindowHandle::make_x11(_xwindow);
- // And tell our parent window that we're now its child.
- if (_parent_window_handle != (WindowHandle *)NULL) {
- _parent_window_handle->attach_child(_window_handle);
- }
-
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: TinyXGraphicsWindow::pixel_factor_changed
- // Access: Protected, Virtual
- // Description: Called internally when the pixel factor changes.
- ////////////////////////////////////////////////////////////////////
- void TinyXGraphicsWindow::
- pixel_factor_changed() {
- x11GraphicsWindow::pixel_factor_changed();
- create_reduced_frame_buffer();
- }
- ////////////////////////////////////////////////////////////////////
- // Function: TinyXGraphicsWindow::create_full_frame_buffer
- // Access: Private
- // Description: Creates a suitable frame buffer for the current
- // window size.
- ////////////////////////////////////////////////////////////////////
- void TinyXGraphicsWindow::
- create_full_frame_buffer() {
- if (_full_frame_buffer != NULL) {
- ZB_close(_full_frame_buffer);
- _full_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;
- }
- _full_frame_buffer = ZB_open(_properties.get_x_size(), _properties.get_y_size(), mode, 0, 0, 0, 0);
- _pitch = (_full_frame_buffer->xsize * _bytes_per_pixel + 3) & ~3;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: TinyXGraphicsWindow::create_reduced_frame_buffer
- // Access: Private
- // Description: Creates a suitable frame buffer for the current
- // window size and pixel zoom.
- ////////////////////////////////////////////////////////////////////
- void TinyXGraphicsWindow::
- create_reduced_frame_buffer() {
- if (_reduced_frame_buffer != NULL) {
- ZB_close(_reduced_frame_buffer);
- _reduced_frame_buffer = NULL;
- }
- int x_size = get_fb_x_size();
- int y_size = get_fb_y_size();
- if (x_size == _full_frame_buffer->xsize) {
- // No zooming is necessary.
- } else {
- // The reduced size is different, so we need a separate buffer to
- // render into.
- _reduced_frame_buffer = ZB_open(x_size, y_size, _full_frame_buffer->mode, 0, 0, 0, 0);
- }
- }
- ////////////////////////////////////////////////////////////////////
- // Function: TinyXGraphicsWindow::create_ximage
- // Access: Private
- // Description: Creates a suitable XImage for the current
- // window size.
- ////////////////////////////////////////////////////////////////////
- void TinyXGraphicsWindow::
- create_ximage() {
- if (_ximage != NULL) {
- PANDA_FREE_ARRAY(_ximage->data);
- _ximage->data = NULL;
- XDestroyImage(_ximage);
- _ximage = NULL;
- }
- int image_size = _full_frame_buffer->ysize * _pitch;
- char *data = (char *)PANDA_MALLOC_ARRAY(image_size);
- _ximage = XCreateImage(_display, _visual, _depth, ZPixmap, 0, data,
- _full_frame_buffer->xsize, _full_frame_buffer->ysize,
- 32, 0);
- }
- #endif // HAVE_X11
|