Panda3D
subprocessWindow.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file subprocessWindow.cxx
10  * @author drose
11  * @date 2009-07-11
12  */
13 
14 #include "subprocessWindow.h"
15 
16 #ifdef SUPPORT_SUBPROCESS_WINDOW
17 
18 #include "graphicsEngine.h"
19 #include "config_display.h"
20 #include "nativeWindowHandle.h"
21 #include "mouseButton.h"
22 #include "throw_event.h"
23 
24 using std::string;
25 
26 TypeHandle SubprocessWindow::_type_handle;
27 
28 /**
29  * Normally, the SubprocessWindow constructor is not called directly; these
30  * are created instead via the GraphicsEngine::make_window() function.
31  */
32 SubprocessWindow::
33 SubprocessWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
34  const string &name,
35  const FrameBufferProperties &fb_prop,
36  const WindowProperties &win_prop,
37  int flags,
39  GraphicsOutput *host) :
40  GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
41 {
42  _input = GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard/mouse");
43  _input_devices.push_back(_input.p());
44 
45  // This will be an offscreen buffer that we use to render the actual
46  // contents.
47  _buffer = nullptr;
48 
49  // Create a texture to receive the contents of the framebuffer from the
50  // offscreen buffer.
51  _texture = new Texture(name);
52 
53  _fd = -1;
54  _mmap_size = 0;
55  _filename = string();
56  _swbuffer = nullptr;
57  _last_event_flags = 0;
58 }
59 
60 /**
61  *
62  */
63 SubprocessWindow::
64 ~SubprocessWindow() {
65  nassertv(_buffer == nullptr);
66  nassertv(_swbuffer == nullptr);
67 }
68 
69 /**
70  * Do whatever processing is necessary to ensure that the window responds to
71  * user events. Also, honor any requests recently made via
72  * request_properties().
73  *
74  * This function is called only within the window thread.
75  */
76 void SubprocessWindow::
77 process_events() {
79 
80  if (_swbuffer != nullptr) {
82  while (_swbuffer->get_event(swb_event)) {
83  // Deal with this event.
84  if (swb_event._flags & SubprocessWindowBuffer::EF_mouse_position) {
85  _input->set_pointer_in_window(swb_event._x, swb_event._y);
86  } else if ((swb_event._flags & SubprocessWindowBuffer::EF_has_mouse) == 0) {
87  _input->set_pointer_out_of_window();
88  }
89 
90  unsigned int diff = swb_event._flags ^ _last_event_flags;
91  _last_event_flags = swb_event._flags;
92 
93  if (diff & SubprocessWindowBuffer::EF_shift_held) {
94  transition_button(swb_event._flags & SubprocessWindowBuffer::EF_shift_held, KeyboardButton::shift());
95  }
96  if (diff & SubprocessWindowBuffer::EF_control_held) {
97  transition_button(swb_event._flags & SubprocessWindowBuffer::EF_control_held, KeyboardButton::control());
98  }
99  if (diff & SubprocessWindowBuffer::EF_alt_held) {
100  transition_button(swb_event._flags & SubprocessWindowBuffer::EF_alt_held, KeyboardButton::alt());
101  }
102  if (diff & SubprocessWindowBuffer::EF_meta_held) {
103  transition_button(swb_event._flags & SubprocessWindowBuffer::EF_meta_held, KeyboardButton::meta());
104  }
105 
106  ButtonHandle button = ButtonHandle::none();
107  if (swb_event._source == SubprocessWindowBuffer::ES_mouse) {
108  button = MouseButton::button(swb_event._code);
109 
110  } else if (swb_event._source == SubprocessWindowBuffer::ES_keyboard) {
111  int keycode;
112  button = translate_key(keycode, swb_event._code, swb_event._flags);
113  if (keycode != 0 && swb_event._type != SubprocessWindowBuffer::ET_button_up) {
114  _input->keystroke(keycode);
115  }
116  }
117 
118  if (swb_event._type == SubprocessWindowBuffer::ET_button_up) {
119  _input->button_up(button);
120  } else if (swb_event._type == SubprocessWindowBuffer::ET_button_down) {
121  _input->button_down(button);
122  }
123  }
124  }
125 }
126 
127 /**
128  * This function will be called within the draw thread before beginning
129  * rendering for a given frame. It should do whatever setup is required, and
130  * return true if the frame should be rendered, or false if it should be
131  * skipped.
132  */
133 bool SubprocessWindow::
134 begin_frame(FrameMode mode, Thread *current_thread) {
135  if (_swbuffer == nullptr || _buffer == nullptr) {
136  return false;
137  }
138 
139  bool result = _buffer->begin_frame(mode, current_thread);
140  return result;
141 }
142 
143 /**
144  * This function will be called within the draw thread after rendering is
145  * completed for a given frame. It should do whatever finalization is
146  * required.
147  */
148 void SubprocessWindow::
149 end_frame(FrameMode mode, Thread *current_thread) {
150  _buffer->end_frame(mode, current_thread);
151 
152  if (mode == FM_render) {
153  _flip_ready = true;
154  }
155 }
156 
157 /**
158  * This function will be called within the draw thread after end_frame() has
159  * been called on all windows, to initiate the exchange of the front and back
160  * buffers.
161  *
162  * This should instruct the window to prepare for the flip at the next video
163  * sync, but it should not wait.
164  *
165  * We have the two separate functions, begin_flip() and end_flip(), to make it
166  * easier to flip all of the windows at the same time.
167  */
168 void SubprocessWindow::
169 begin_flip() {
170  nassertv(_buffer != nullptr);
171  if (_swbuffer == nullptr) {
172  return;
173  }
174 
175  RenderBuffer buffer(_gsg, DrawableRegion::get_renderbuffer_type(RTP_color));
176  buffer = _gsg->get_render_buffer(_buffer->get_draw_buffer_type(),
177  _buffer->get_fb_properties());
178 
179  bool copied =
180  _gsg->framebuffer_copy_to_ram(_texture, 0, -1,
181  _overlay_display_region, buffer);
182 
183  if (copied) {
184  CPTA_uchar image = _texture->get_ram_image();
185  size_t framebuffer_size = _swbuffer->get_framebuffer_size();
186  nassertv(image.size() == framebuffer_size);
187 
188  if (!_swbuffer->ready_for_write()) {
189  // We have to wait for the other end to remove the last frame we
190  // rendered. We only wait so long before we give up, so we don't
191  // completely starve the Python process just because the render window
192  // is offscreen or something.
193 
195  double start = clock->get_real_time();
196  while (!_swbuffer->ready_for_write()) {
198  double now = clock->get_real_time();
199  if (now - start > subprocess_window_max_wait) {
200  // Never mind.
201  return;
202  }
203  }
204  }
205 
206  // We're ready to go. Copy the image to our shared framebuffer.
207  void *target = _swbuffer->open_write_framebuffer();
208  memcpy(target, image.p(), framebuffer_size);
209  _swbuffer->close_write_framebuffer();
210  }
211 }
212 
213 /**
214  * Applies the requested set of properties to the window, if possible, for
215  * instance to request a change in size or minimization status.
216  *
217  * The window properties are applied immediately, rather than waiting until
218  * the next frame. This implies that this method may *only* be called from
219  * within the window thread.
220  *
221  * The properties that have been applied are cleared from the structure by
222  * this function; so on return, whatever remains in the properties structure
223  * are those that were unchanged for some reason (probably because the
224  * underlying interface does not support changing that property on an open
225  * window).
226  */
227 void SubprocessWindow::
228 set_properties_now(WindowProperties &properties) {
229  Filename filename;
230  WindowHandle *window_handle = properties.get_parent_window();
231  if (window_handle != nullptr) {
232  WindowHandle::OSHandle *os_handle = window_handle->get_os_handle();
233  if (os_handle != nullptr) {
234  if (os_handle->is_of_type(NativeWindowHandle::SubprocessHandle::get_class_type())) {
235  NativeWindowHandle::SubprocessHandle *subprocess_handle = DCAST(NativeWindowHandle::SubprocessHandle, os_handle);
236  filename = subprocess_handle->get_filename();
237  }
238  }
239  }
240 
241  if (!filename.empty() && filename != _filename) {
242  // We're changing the subprocess buffer filename; that means we might as
243  // well completely close and re-open the window.
244  display_cat.info() << "Re-opening SubprocessWindow\n";
245  internal_close_window();
246 
247  _properties.add_properties(properties);
248  properties.clear();
249 
250  internal_open_window();
251  set_size_and_recalc(_properties.get_x_size(), _properties.get_y_size());
252  throw_event(get_window_event(), this);
253  return;
254  }
255 
257  if (!properties.is_any_specified()) {
258  // The base class has already handled this case.
259  return;
260  }
261 
262  if (properties.has_parent_window()) {
263  // Redundant parent-window specification.
264  properties.clear_parent_window();
265  }
266 }
267 
268 /**
269  * Closes the window right now. Called from the window thread.
270  */
271 void SubprocessWindow::
272 close_window() {
273  internal_close_window();
274 
275  WindowProperties properties;
276  properties.set_open(false);
277  properties.set_foreground(false);
278  system_changed_properties(properties);
279 }
280 
281 /**
282  * Opens the window right now. Called from the window thread. Returns true
283  * if the window is successfully opened, or false if there was a problem.
284  */
285 bool SubprocessWindow::
286 open_window() {
287  if (!internal_open_window()) {
288  return false;
289  }
290 
291  WindowProperties properties;
292  properties.set_open(true);
293  properties.set_foreground(true);
294  system_changed_properties(properties);
295 
296  return true;
297 }
298 
299 /**
300  * Closes the "window" and resets the buffer, without changing the
301  * WindowProperties.
302  */
303 void SubprocessWindow::
304 internal_close_window() {
305  if (_swbuffer != nullptr) {
307  (_fd, _mmap_size, _filename.to_os_specific(), _swbuffer);
308  _fd = -1;
309  _filename = string();
310 
311  _swbuffer = nullptr;
312  }
313 
314  if (_buffer != nullptr) {
315  _buffer->request_close();
316  _buffer->process_events();
317  _engine->remove_window(_buffer);
318  _buffer = nullptr;
319  }
320 
321  // Tell our parent window (if any) that we're no longer its child.
322  if (_window_handle != nullptr &&
323  _parent_window_handle != nullptr) {
324  _parent_window_handle->detach_child(_window_handle);
325  }
326 
327  _window_handle = nullptr;
328  _parent_window_handle = nullptr;
329  _is_valid = false;
330 }
331 
332 /**
333  * Opens the "window" and the associated offscreen buffer, without changing
334  * the WindowProperties.
335  */
336 bool SubprocessWindow::
337 internal_open_window() {
338  nassertr(_buffer == nullptr, false);
339 
340  // Create a buffer with the same properties as the window.
341  int flags = _creation_flags;
342  flags = ((flags & ~GraphicsPipe::BF_require_window) | GraphicsPipe::BF_refuse_window);
343  WindowProperties win_props = WindowProperties::size(_properties.get_x_size(), _properties.get_y_size());
344 
345  GraphicsOutput *buffer =
346  _engine->make_output(_pipe, _name, 0, _fb_properties, win_props,
347  flags, _gsg, _host);
348  if (buffer != nullptr) {
349  _buffer = DCAST(GraphicsBuffer, buffer);
350  // However, the buffer is not itself intended to be rendered. We only
351  // render it indirectly, via callbacks in here.
352  _buffer->set_active(false);
353 
354  _buffer->request_open();
355  _buffer->process_events();
356 
357  _is_valid = _buffer->is_valid();
358  }
359 
360  if (!_is_valid) {
361  display_cat.error()
362  << "Failed to open SubprocessWindowBuffer's internal offscreen buffer.\n";
363  return false;
364  }
365 
366  _gsg = _buffer->get_gsg();
367 
368  WindowHandle *window_handle = _properties.get_parent_window();
369  if (window_handle != nullptr) {
370  WindowHandle::OSHandle *os_handle = window_handle->get_os_handle();
371  if (os_handle != nullptr) {
372  if (os_handle->is_of_type(NativeWindowHandle::SubprocessHandle::get_class_type())) {
373  NativeWindowHandle::SubprocessHandle *subprocess_handle = DCAST(NativeWindowHandle::SubprocessHandle, os_handle);
374  _filename = subprocess_handle->get_filename();
375  }
376  }
377  }
378  _parent_window_handle = window_handle;
379 
380  if (_filename.empty()) {
381  _is_valid = false;
382  display_cat.error()
383  << "No filename given to SubprocessWindow.\n";
384  return false;
385  }
386 
388  (_fd, _mmap_size, _filename.to_os_specific());
389 
390  if (_swbuffer == nullptr) {
391  close(_fd);
392  _fd = -1;
393  _filename = string();
394  _is_valid = false;
395  display_cat.error()
396  << "Failed to open SubprocessWindowBuffer's shared-memory buffer "
397  << _filename << "\n";
398  return false;
399  }
400 
401  if (display_cat.is_debug()) {
402  display_cat.debug()
403  << "SubprocessWindow reading " << _filename << "\n";
404  }
405 
406  // Create a WindowHandle for ourselves
407  _window_handle = NativeWindowHandle::make_subprocess(_filename);
408 
409  // And tell our parent window that we're now its child.
410  if (_parent_window_handle != nullptr) {
411  _parent_window_handle->attach_child(_window_handle);
412  }
413 
414  return true;
415 }
416 
417 /**
418  * Converts the os-specific keycode into the appropriate ButtonHandle object.
419  * Also stores the corresponding Unicode keycode in keycode, if any; or 0
420  * otherwise.
421  */
422 ButtonHandle SubprocessWindow::
423 translate_key(int &keycode, int os_code, unsigned int flags) const {
424  keycode = 0;
425  ButtonHandle nk = ButtonHandle::none();
426 
427 #ifdef __APPLE__
428  switch ((os_code >> 8) & 0xff) {
429  case 0: nk = KeyboardButton::ascii_key('a'); break;
430  case 11: nk = KeyboardButton::ascii_key('b'); break;
431  case 8: nk = KeyboardButton::ascii_key('c'); break;
432  case 2: nk = KeyboardButton::ascii_key('d'); break;
433  case 14: nk = KeyboardButton::ascii_key('e'); break;
434  case 3: nk = KeyboardButton::ascii_key('f'); break;
435  case 5: nk = KeyboardButton::ascii_key('g'); break;
436  case 4: nk = KeyboardButton::ascii_key('h'); break;
437  case 34: nk = KeyboardButton::ascii_key('i'); break;
438  case 38: nk = KeyboardButton::ascii_key('j'); break;
439  case 40: nk = KeyboardButton::ascii_key('k'); break;
440  case 37: nk = KeyboardButton::ascii_key('l'); break;
441  case 46: nk = KeyboardButton::ascii_key('m'); break;
442  case 45: nk = KeyboardButton::ascii_key('n'); break;
443  case 31: nk = KeyboardButton::ascii_key('o'); break;
444  case 35: nk = KeyboardButton::ascii_key('p'); break;
445  case 12: nk = KeyboardButton::ascii_key('q'); break;
446  case 15: nk = KeyboardButton::ascii_key('r'); break;
447  case 1: nk = KeyboardButton::ascii_key('s'); break;
448  case 17: nk = KeyboardButton::ascii_key('t'); break;
449  case 32: nk = KeyboardButton::ascii_key('u'); break;
450  case 9: nk = KeyboardButton::ascii_key('v'); break;
451  case 13: nk = KeyboardButton::ascii_key('w'); break;
452  case 7: nk = KeyboardButton::ascii_key('x'); break;
453  case 16: nk = KeyboardButton::ascii_key('y'); break;
454  case 6: nk = KeyboardButton::ascii_key('z'); break;
455 
456  // top row numbers
457  case 29: nk = KeyboardButton::ascii_key('0'); break;
458  case 18: nk = KeyboardButton::ascii_key('1'); break;
459  case 19: nk = KeyboardButton::ascii_key('2'); break;
460  case 20: nk = KeyboardButton::ascii_key('3'); break;
461  case 21: nk = KeyboardButton::ascii_key('4'); break;
462  case 23: nk = KeyboardButton::ascii_key('5'); break;
463  case 22: nk = KeyboardButton::ascii_key('6'); break;
464  case 26: nk = KeyboardButton::ascii_key('7'); break;
465  case 28: nk = KeyboardButton::ascii_key('8'); break;
466  case 25: nk = KeyboardButton::ascii_key('9'); break;
467 
468  // key pad ... do they really map to the top number in panda ?
469  case 82: nk = KeyboardButton::ascii_key('0'); break;
470  case 83: nk = KeyboardButton::ascii_key('1'); break;
471  case 84: nk = KeyboardButton::ascii_key('2'); break;
472  case 85: nk = KeyboardButton::ascii_key('3'); break;
473  case 86: nk = KeyboardButton::ascii_key('4'); break;
474  case 87: nk = KeyboardButton::ascii_key('5'); break;
475  case 88: nk = KeyboardButton::ascii_key('6'); break;
476  case 89: nk = KeyboardButton::ascii_key('7'); break;
477  case 91: nk = KeyboardButton::ascii_key('8'); break;
478  case 92: nk = KeyboardButton::ascii_key('9'); break;
479 
480  // case 36: nk = KeyboardButton::ret(); break; no return in panda ???
481  case 49: nk = KeyboardButton::space(); break;
482  case 51: nk = KeyboardButton::backspace(); break;
483  case 48: nk = KeyboardButton::tab(); break;
484  case 53: nk = KeyboardButton::escape(); break;
485  case 76: nk = KeyboardButton::enter(); break;
486  case 36: nk = KeyboardButton::enter(); break;
487 
488  case 123: nk = KeyboardButton::left(); break;
489  case 124: nk = KeyboardButton::right(); break;
490  case 125: nk = KeyboardButton::down(); break;
491  case 126: nk = KeyboardButton::up(); break;
492  case 116: nk = KeyboardButton::page_up(); break;
493  case 121: nk = KeyboardButton::page_down(); break;
494  case 115: nk = KeyboardButton::home(); break;
495  case 119: nk = KeyboardButton::end(); break;
496  case 114: nk = KeyboardButton::help(); break;
497  case 117: nk = KeyboardButton::del(); break;
498 
499  // case 71: nk = KeyboardButton::num_lock() break;
500 
501  case 122: nk = KeyboardButton::f1(); break;
502  case 120: nk = KeyboardButton::f2(); break;
503  case 99: nk = KeyboardButton::f3(); break;
504  case 118: nk = KeyboardButton::f4(); break;
505  case 96: nk = KeyboardButton::f5(); break;
506  case 97: nk = KeyboardButton::f6(); break;
507  case 98: nk = KeyboardButton::f7(); break;
508  case 100: nk = KeyboardButton::f8(); break;
509  case 101: nk = KeyboardButton::f9(); break;
510  case 109: nk = KeyboardButton::f10(); break;
511  case 103: nk = KeyboardButton::f11(); break;
512  case 111: nk = KeyboardButton::f12(); break;
513 
514  case 105: nk = KeyboardButton::f13(); break;
515  case 107: nk = KeyboardButton::f14(); break;
516  case 113: nk = KeyboardButton::f15(); break;
517  case 106: nk = KeyboardButton::f16(); break;
518 
519  // shiftable chartablet
520  case 50: nk = KeyboardButton::ascii_key('`'); break;
521  case 27: nk = KeyboardButton::ascii_key('-'); break;
522  case 24: nk = KeyboardButton::ascii_key('='); break;
523  case 33: nk = KeyboardButton::ascii_key('['); break;
524  case 30: nk = KeyboardButton::ascii_key(']'); break;
525  case 42: nk = KeyboardButton::ascii_key('\\'); break;
526  case 41: nk = KeyboardButton::ascii_key(';'); break;
527  case 39: nk = KeyboardButton::ascii_key('\''); break;
528  case 43: nk = KeyboardButton::ascii_key(','); break;
529  case 47: nk = KeyboardButton::ascii_key('.'); break;
530  case 44: nk = KeyboardButton::ascii_key('/'); break;
531 
532  default:
533  // Punt.
534  nk = KeyboardButton::ascii_key(os_code & 0xff);
535  }
536 
537  if (nk.has_ascii_equivalent()) {
538  // If we assigned an ASCII button, then get the original ASCII code from
539  // the event (it will include shift et al).
540 
541  // TODO: is it possible to get any international characters via this old
542  // EventRecord interface?
543  keycode = os_code & 0xff;
544  }
545 
546 #endif // __APPLE__
547 
548  return nk;
549 }
550 
551 /**
552  * Sends the appropriate up/down transition for the indicated modifier key, as
553  * determined implicitly from the flags.
554  */
555 void SubprocessWindow::
556 transition_button(unsigned int flags, ButtonHandle button) {
557  if (flags) {
558  _input->button_down(button);
559  } else {
560  _input->button_up(button);
561  }
562 }
563 
564 
565 #endif // SUPPORT_SUBPROCESS_WINDOW
bool is_any_specified() const
Returns true if any properties have been specified, false otherwise.
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
static int get_renderbuffer_type(int plane)
Returns the RenderBuffer::Type that corresponds to a RenderTexturePlane.
void clear()
Unsets all properties that have been specified so far, and resets the WindowProperties structure to i...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This object represents a window on the desktop, not necessarily a Panda window.
Definition: windowHandle.h:34
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_parent_window
Returns the parent window specification, or NULL if there is no parent window specified.
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:71
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
has_parent_window
Checks the S_parent_window specification from the properties.
get_os_handle
Returns the OS-specific handle stored internally to the WindowHandle wrapper.
Definition: windowHandle.h:44
static WindowProperties size(const LVecBase2i &size)
Returns a WindowProperties structure with only the size specified.
static void force_yield()
Suspends the current thread for the rest of the current epoch.
Definition: thread.I:201
A window, fullscreen or on a desktop, into which a graphics device sends its output for interactive d...
A ButtonHandle represents a single button from any device, including keyboard buttons and mouse butto...
Definition: buttonHandle.h:26
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A container for the various kinds of properties we might ask to have on a graphics window before we o...
static void close_buffer(int fd, size_t mmap_size, const std::string &filename, SubprocessWindowBuffer *buffer)
Closes a buffer object created via a previous call to open_buffer().
An offscreen buffer for rendering into.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
static ButtonHandle button(int button_number)
Returns the ButtonHandle associated with the particular numbered mouse button (zero-based),...
Definition: mouseButton.cxx:32
const Element * p() const
Function p() is similar to the function from ConstPointerTo.
A ClockObject keeps track of elapsed real time and discrete time.
Definition: clockObject.h:58
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
get_real_time
Returns the actual number of seconds elapsed since the ClockObject was created, or since it was last ...
Definition: clockObject.h:92
has_ascii_equivalent
Returns true if the button was created with an ASCII equivalent code (e.g.
Definition: buttonHandle.h:63
This is a base class for the various different classes that represent the result of a frame of render...
A thread; that is, a lightweight process.
Definition: thread.h:46
Encapsulates all the communication with a particular instance of a given rendering backend.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
This class is the main interface to controlling the render process.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
clear_parent_window
Removes the S_parent_window specification from the properties.
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
static ButtonHandle ascii_key(char ascii_equivalent)
Returns the ButtonHandle associated with the particular ASCII character, if there is one,...
set_open
Specifies whether the window should be open.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A RenderBuffer is an arbitrary subset of the various layers (depth buffer, color buffer,...
Definition: renderBuffer.h:27
Similar to PointerToArray, except that its contents may not be modified.
set_foreground
Specifies whether the window should be opened in the foreground (true), or left in the background (fa...
static SubprocessWindowBuffer * open_buffer(int &fd, size_t &mmap_size, const std::string &filename)
Call this method to open a reference to an existing buffer in shared memory space.