Panda3D
Loading...
Searching...
No Matches
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
24using std::string;
25
26TypeHandle SubprocessWindow::_type_handle;
27
28/**
29 * Normally, the SubprocessWindow constructor is not called directly; these
30 * are created instead via the GraphicsEngine::make_output() function.
31 */
32SubprocessWindow::
33SubprocessWindow(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 */
63SubprocessWindow::
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 */
76void SubprocessWindow::
77process_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 */
133bool SubprocessWindow::
134begin_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 */
148void SubprocessWindow::
149end_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 */
168void SubprocessWindow::
169begin_flip() {
170 nassertv(_buffer != nullptr);
171 if (_swbuffer == nullptr) {
172 return;
173 }
174
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 */
227void SubprocessWindow::
228set_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())) {
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 */
271void SubprocessWindow::
272close_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 */
285bool SubprocessWindow::
286open_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 */
303void SubprocessWindow::
304internal_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 */
336bool SubprocessWindow::
337internal_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())) {
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 */
422ButtonHandle SubprocessWindow::
423translate_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 */
555void SubprocessWindow::
556transition_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
A ButtonHandle represents a single button from any device, including keyboard buttons and mouse butto...
has_ascii_equivalent
Returns true if the button was created with an ASCII equivalent code (e.g.
A ClockObject keeps track of elapsed real time and discrete time.
Definition clockObject.h:58
get_real_time
Returns the actual number of seconds elapsed since the ClockObject was created, or since it was last ...
Definition clockObject.h:92
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
const Element * p() const
Function p() is similar to the function from ConstPointerTo.
static int get_renderbuffer_type(int plane)
Returns the RenderBuffer::Type that corresponds to a RenderTexturePlane.
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
An offscreen buffer for rendering into.
This class is the main interface to controlling the render process.
This is a base class for the various different classes that represent the result of a frame of render...
An object to create GraphicsOutputs that share a particular 3-D API.
Encapsulates all the communication with a particular instance of a given rendering backend.
A window, fullscreen or on a desktop, into which a graphics device sends its output for interactive d...
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
static ButtonHandle ascii_key(char ascii_equivalent)
Returns the ButtonHandle associated with the particular ASCII character, if there is one,...
static ButtonHandle button(int button_number)
Returns the ButtonHandle associated with the particular numbered mouse button (zero-based),...
A RenderBuffer is an arbitrary subset of the various layers (depth buffer, color buffer,...
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().
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.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition texture.h:72
A thread; that is, a lightweight process.
Definition thread.h:46
static void force_yield()
Suspends the current thread for the rest of the current epoch.
Definition thread.I:201
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
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 object represents a window on the desktop, not necessarily a Panda window.
get_os_handle
Returns the OS-specific handle stored internally to the WindowHandle wrapper.
A container for the various kinds of properties we might ask to have on a graphics window before we o...
static WindowProperties size(const LVecBase2i &size)
Returns a WindowProperties structure with only the size specified.
has_parent_window
Checks the S_parent_window specification from the properties.
clear_parent_window
Removes the S_parent_window specification from the properties.
void clear()
Unsets all properties that have been specified so far, and resets the WindowProperties structure to i...
bool is_any_specified() const
Returns true if any properties have been specified, false otherwise.
set_foreground
Specifies whether the window should be opened in the foreground (true), or left in the background (fa...
set_open
Specifies whether the window should be open.
get_parent_window
Returns the parent window specification, or NULL if there is no parent window specified.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.