Mercurial > hg > pub > prymula > com
comparison DPF-Prymula-audioplugins/dpf/dgl/src/WindowPrivateData.cpp @ 3:84e66ea83026
DPF-Prymula-audioplugins-0.231015-2
author | prymula <prymula76@outlook.com> |
---|---|
date | Mon, 16 Oct 2023 21:53:34 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2:cf2cb71d31dd | 3:84e66ea83026 |
---|---|
1 /* | |
2 * DISTRHO Plugin Framework (DPF) | |
3 * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |
4 * | |
5 * Permission to use, copy, modify, and/or distribute this software for any purpose with | |
6 * or without fee is hereby granted, provided that the above copyright notice and this | |
7 * permission notice appear in all copies. | |
8 * | |
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |
10 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |
11 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |
12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |
13 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 */ | |
16 | |
17 #include "WindowPrivateData.hpp" | |
18 #include "TopLevelWidgetPrivateData.hpp" | |
19 | |
20 #include "pugl.hpp" | |
21 | |
22 // #define DGL_DEBUG_EVENTS | |
23 | |
24 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) | |
25 # ifdef DISTRHO_PROPER_CPP11_SUPPORT | |
26 # include <cinttypes> | |
27 # else | |
28 # include <inttypes.h> | |
29 # endif | |
30 #endif | |
31 | |
32 START_NAMESPACE_DGL | |
33 | |
34 #ifdef DGL_DEBUG_EVENTS | |
35 # define DGL_DBG(msg) std::fprintf(stderr, "%s", msg); | |
36 # define DGL_DBGp(...) std::fprintf(stderr, __VA_ARGS__); | |
37 # define DGL_DBGF std::fflush(stderr); | |
38 #else | |
39 # define DGL_DBG(msg) | |
40 # define DGL_DBGp(...) | |
41 # define DGL_DBGF | |
42 #endif | |
43 | |
44 #define DEFAULT_WIDTH 640 | |
45 #define DEFAULT_HEIGHT 480 | |
46 | |
47 #define FOR_EACH_TOP_LEVEL_WIDGET(it) \ | |
48 for (std::list<TopLevelWidget*>::iterator it = topLevelWidgets.begin(); it != topLevelWidgets.end(); ++it) | |
49 | |
50 #define FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) \ | |
51 for (std::list<TopLevelWidget*>::reverse_iterator rit = topLevelWidgets.rbegin(); rit != topLevelWidgets.rend(); ++rit) | |
52 | |
53 // ----------------------------------------------------------------------- | |
54 | |
55 static double getScaleFactorFromParent(const PuglView* const view) | |
56 { | |
57 // allow custom scale for testing | |
58 if (const char* const scale = getenv("DPF_SCALE_FACTOR")) | |
59 return std::max(1.0, std::atof(scale)); | |
60 | |
61 if (view != nullptr) | |
62 return puglGetScaleFactorFromParent(view); | |
63 | |
64 return 1.0; | |
65 } | |
66 | |
67 static PuglView* puglNewViewWithTransientParent(PuglWorld* const world, PuglView* const transientParentView) | |
68 { | |
69 DISTRHO_SAFE_ASSERT_RETURN(world != nullptr, nullptr); | |
70 | |
71 if (PuglView* const view = puglNewView(world)) | |
72 { | |
73 puglSetTransientParent(view, puglGetNativeView(transientParentView)); | |
74 return view; | |
75 } | |
76 | |
77 return nullptr; | |
78 } | |
79 | |
80 static PuglView* puglNewViewWithParentWindow(PuglWorld* const world, const uintptr_t parentWindowHandle) | |
81 { | |
82 DISTRHO_SAFE_ASSERT_RETURN(world != nullptr, nullptr); | |
83 | |
84 if (PuglView* const view = puglNewView(world)) | |
85 { | |
86 puglSetParentWindow(view, parentWindowHandle); | |
87 return view; | |
88 } | |
89 | |
90 return nullptr; | |
91 } | |
92 | |
93 // ----------------------------------------------------------------------- | |
94 | |
95 Window::PrivateData::PrivateData(Application& a, Window* const s) | |
96 : app(a), | |
97 appData(a.pData), | |
98 self(s), | |
99 view(appData->world != nullptr ? puglNewView(appData->world) : nullptr), | |
100 topLevelWidgets(), | |
101 isClosed(true), | |
102 isVisible(false), | |
103 isEmbed(false), | |
104 usesSizeRequest(false), | |
105 scaleFactor(getScaleFactorFromParent(view)), | |
106 autoScaling(false), | |
107 autoScaleFactor(1.0), | |
108 minWidth(0), | |
109 minHeight(0), | |
110 keepAspectRatio(false), | |
111 ignoreIdleCallbacks(false), | |
112 waitingForClipboardData(false), | |
113 waitingForClipboardEvents(false), | |
114 clipboardTypeId(0), | |
115 filenameToRenderInto(nullptr), | |
116 #ifndef DGL_FILE_BROWSER_DISABLED | |
117 fileBrowserHandle(nullptr), | |
118 #endif | |
119 modal() | |
120 { | |
121 initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); | |
122 } | |
123 | |
124 Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* const ppData) | |
125 : app(a), | |
126 appData(a.pData), | |
127 self(s), | |
128 view(puglNewViewWithTransientParent(appData->world, ppData->view)), | |
129 topLevelWidgets(), | |
130 isClosed(true), | |
131 isVisible(false), | |
132 isEmbed(false), | |
133 usesSizeRequest(false), | |
134 scaleFactor(ppData->scaleFactor), | |
135 autoScaling(false), | |
136 autoScaleFactor(1.0), | |
137 minWidth(0), | |
138 minHeight(0), | |
139 keepAspectRatio(false), | |
140 ignoreIdleCallbacks(false), | |
141 waitingForClipboardData(false), | |
142 waitingForClipboardEvents(false), | |
143 clipboardTypeId(0), | |
144 filenameToRenderInto(nullptr), | |
145 #ifndef DGL_FILE_BROWSER_DISABLED | |
146 fileBrowserHandle(nullptr), | |
147 #endif | |
148 modal(ppData) | |
149 { | |
150 initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); | |
151 } | |
152 | |
153 Window::PrivateData::PrivateData(Application& a, Window* const s, | |
154 const uintptr_t parentWindowHandle, | |
155 const double scale, const bool resizable) | |
156 : app(a), | |
157 appData(a.pData), | |
158 self(s), | |
159 view(puglNewViewWithParentWindow(appData->world, parentWindowHandle)), | |
160 topLevelWidgets(), | |
161 isClosed(parentWindowHandle == 0), | |
162 isVisible(parentWindowHandle != 0), | |
163 isEmbed(parentWindowHandle != 0), | |
164 usesSizeRequest(false), | |
165 scaleFactor(scale != 0.0 ? scale : getScaleFactorFromParent(view)), | |
166 autoScaling(false), | |
167 autoScaleFactor(1.0), | |
168 minWidth(0), | |
169 minHeight(0), | |
170 keepAspectRatio(false), | |
171 ignoreIdleCallbacks(false), | |
172 waitingForClipboardData(false), | |
173 waitingForClipboardEvents(false), | |
174 clipboardTypeId(0), | |
175 filenameToRenderInto(nullptr), | |
176 #ifndef DGL_FILE_BROWSER_DISABLED | |
177 fileBrowserHandle(nullptr), | |
178 #endif | |
179 modal() | |
180 { | |
181 initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, resizable); | |
182 } | |
183 | |
184 Window::PrivateData::PrivateData(Application& a, Window* const s, | |
185 const uintptr_t parentWindowHandle, | |
186 const uint width, const uint height, | |
187 const double scale, const bool resizable, const bool usesSizeRequest_) | |
188 : app(a), | |
189 appData(a.pData), | |
190 self(s), | |
191 view(puglNewViewWithParentWindow(appData->world, parentWindowHandle)), | |
192 topLevelWidgets(), | |
193 isClosed(parentWindowHandle == 0), | |
194 isVisible(parentWindowHandle != 0 && view != nullptr), | |
195 isEmbed(parentWindowHandle != 0), | |
196 usesSizeRequest(usesSizeRequest_), | |
197 scaleFactor(scale != 0.0 ? scale : getScaleFactorFromParent(view)), | |
198 autoScaling(false), | |
199 autoScaleFactor(1.0), | |
200 minWidth(0), | |
201 minHeight(0), | |
202 keepAspectRatio(false), | |
203 ignoreIdleCallbacks(false), | |
204 waitingForClipboardData(false), | |
205 waitingForClipboardEvents(false), | |
206 clipboardTypeId(0), | |
207 filenameToRenderInto(nullptr), | |
208 #ifndef DGL_FILE_BROWSER_DISABLED | |
209 fileBrowserHandle(nullptr), | |
210 #endif | |
211 modal() | |
212 { | |
213 if (isEmbed) | |
214 puglSetParentWindow(view, parentWindowHandle); | |
215 | |
216 initPre(width != 0 ? width : DEFAULT_WIDTH, height != 0 ? height : DEFAULT_HEIGHT, resizable); | |
217 } | |
218 | |
219 Window::PrivateData::~PrivateData() | |
220 { | |
221 appData->idleCallbacks.remove(this); | |
222 appData->windows.remove(self); | |
223 std::free(filenameToRenderInto); | |
224 | |
225 if (view == nullptr) | |
226 return; | |
227 | |
228 if (isEmbed) | |
229 { | |
230 #ifndef DGL_FILE_BROWSER_DISABLED | |
231 if (fileBrowserHandle != nullptr) | |
232 fileBrowserClose(fileBrowserHandle); | |
233 #endif | |
234 puglHide(view); | |
235 appData->oneWindowClosed(); | |
236 isClosed = true; | |
237 isVisible = false; | |
238 } | |
239 | |
240 puglFreeView(view); | |
241 } | |
242 | |
243 // ----------------------------------------------------------------------- | |
244 | |
245 void Window::PrivateData::initPre(const uint width, const uint height, const bool resizable) | |
246 { | |
247 appData->windows.push_back(self); | |
248 appData->idleCallbacks.push_back(this); | |
249 memset(graphicsContext, 0, sizeof(graphicsContext)); | |
250 | |
251 if (view == nullptr) | |
252 { | |
253 d_stderr2("Failed to create Pugl view, everything will fail!"); | |
254 return; | |
255 } | |
256 | |
257 puglSetMatchingBackendForCurrentBuild(view); | |
258 puglSetHandle(view, this); | |
259 | |
260 puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); | |
261 puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE); | |
262 #if DGL_USE_RGBA | |
263 puglSetViewHint(view, PUGL_DEPTH_BITS, 24); | |
264 #else | |
265 puglSetViewHint(view, PUGL_DEPTH_BITS, 16); | |
266 #endif | |
267 puglSetViewHint(view, PUGL_STENCIL_BITS, 8); | |
268 | |
269 // PUGL_SAMPLES ?? | |
270 puglSetEventFunc(view, puglEventCallback); | |
271 | |
272 // setting default size triggers system-level calls, do it last | |
273 puglSetSizeHint(view, PUGL_DEFAULT_SIZE, width, height); | |
274 } | |
275 | |
276 bool Window::PrivateData::initPost() | |
277 { | |
278 if (view == nullptr) | |
279 return false; | |
280 | |
281 // create view now, as a few methods we allow devs to use require it | |
282 if (puglRealize(view) != PUGL_SUCCESS) | |
283 { | |
284 view = nullptr; | |
285 d_stderr2("Failed to realize Pugl view, everything will fail!"); | |
286 return false; | |
287 } | |
288 | |
289 if (isEmbed) | |
290 { | |
291 appData->oneWindowShown(); | |
292 puglShow(view); | |
293 } | |
294 | |
295 return true; | |
296 } | |
297 | |
298 // ----------------------------------------------------------------------- | |
299 | |
300 void Window::PrivateData::close() | |
301 { | |
302 DGL_DBG("Window close\n"); | |
303 // DGL_DBGp("Window close DBG %i %i %p\n", isEmbed, isClosed, appData); | |
304 | |
305 if (isEmbed || isClosed) | |
306 return; | |
307 | |
308 isClosed = true; | |
309 hide(); | |
310 appData->oneWindowClosed(); | |
311 } | |
312 | |
313 // ----------------------------------------------------------------------- | |
314 | |
315 void Window::PrivateData::show() | |
316 { | |
317 if (isVisible) | |
318 { | |
319 DGL_DBG("Window show matches current visible state, ignoring request\n"); | |
320 return; | |
321 } | |
322 if (isEmbed) | |
323 { | |
324 DGL_DBG("Window show cannot be called when embedded\n"); | |
325 return; | |
326 } | |
327 | |
328 DGL_DBG("Window show called\n"); | |
329 | |
330 if (view == nullptr) | |
331 return; | |
332 | |
333 if (isClosed) | |
334 { | |
335 isClosed = false; | |
336 appData->oneWindowShown(); | |
337 | |
338 // FIXME | |
339 // PuglRect rect = puglGetFrame(view); | |
340 // puglSetWindowSize(view, static_cast<uint>(rect.width), static_cast<uint>(rect.height)); | |
341 | |
342 #if defined(DISTRHO_OS_WINDOWS) | |
343 puglWin32ShowCentered(view); | |
344 #elif defined(DISTRHO_OS_MAC) | |
345 puglMacOSShowCentered(view); | |
346 #else | |
347 puglShow(view); | |
348 #endif | |
349 } | |
350 else | |
351 { | |
352 #ifdef DISTRHO_OS_WINDOWS | |
353 puglWin32RestoreWindow(view); | |
354 #else | |
355 puglShow(view); | |
356 #endif | |
357 } | |
358 | |
359 isVisible = true; | |
360 } | |
361 | |
362 void Window::PrivateData::hide() | |
363 { | |
364 if (isEmbed) | |
365 { | |
366 DGL_DBG("Window hide cannot be called when embedded\n"); | |
367 return; | |
368 } | |
369 if (! isVisible) | |
370 { | |
371 DGL_DBG("Window hide matches current visible state, ignoring request\n"); | |
372 return; | |
373 } | |
374 | |
375 DGL_DBG("Window hide called\n"); | |
376 | |
377 if (modal.enabled) | |
378 stopModal(); | |
379 | |
380 #ifndef DGL_FILE_BROWSER_DISABLED | |
381 if (fileBrowserHandle != nullptr) | |
382 { | |
383 fileBrowserClose(fileBrowserHandle); | |
384 fileBrowserHandle = nullptr; | |
385 } | |
386 #endif | |
387 | |
388 puglHide(view); | |
389 | |
390 isVisible = false; | |
391 } | |
392 | |
393 // ----------------------------------------------------------------------- | |
394 | |
395 void Window::PrivateData::focus() | |
396 { | |
397 if (view == nullptr) | |
398 return; | |
399 | |
400 if (! isEmbed) | |
401 puglRaiseWindow(view); | |
402 | |
403 puglGrabFocus(view); | |
404 } | |
405 | |
406 // ----------------------------------------------------------------------- | |
407 | |
408 void Window::PrivateData::setResizable(const bool resizable) | |
409 { | |
410 DISTRHO_SAFE_ASSERT_RETURN(! isEmbed,); | |
411 | |
412 DGL_DBG("Window setResizable called\n"); | |
413 | |
414 puglSetResizable(view, resizable); | |
415 } | |
416 | |
417 // ----------------------------------------------------------------------- | |
418 | |
419 void Window::PrivateData::idleCallback() | |
420 { | |
421 #ifndef DGL_FILE_BROWSER_DISABLED | |
422 if (fileBrowserHandle != nullptr && fileBrowserIdle(fileBrowserHandle)) | |
423 { | |
424 self->onFileSelected(fileBrowserGetPath(fileBrowserHandle)); | |
425 fileBrowserClose(fileBrowserHandle); | |
426 fileBrowserHandle = nullptr; | |
427 } | |
428 #endif | |
429 } | |
430 | |
431 // ----------------------------------------------------------------------- | |
432 // idle callback stuff | |
433 | |
434 bool Window::PrivateData::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs) | |
435 { | |
436 if (ignoreIdleCallbacks) | |
437 return false; | |
438 | |
439 if (timerFrequencyInMs == 0) | |
440 { | |
441 appData->idleCallbacks.push_back(callback); | |
442 return true; | |
443 } | |
444 | |
445 return puglStartTimer(view, (uintptr_t)callback, static_cast<double>(timerFrequencyInMs) / 1000.0) == PUGL_SUCCESS; | |
446 } | |
447 | |
448 bool Window::PrivateData::removeIdleCallback(IdleCallback* const callback) | |
449 { | |
450 if (ignoreIdleCallbacks) | |
451 return false; | |
452 | |
453 if (std::find(appData->idleCallbacks.begin(), | |
454 appData->idleCallbacks.end(), callback) != appData->idleCallbacks.end()) | |
455 { | |
456 appData->idleCallbacks.remove(callback); | |
457 return true; | |
458 } | |
459 | |
460 return puglStopTimer(view, (uintptr_t)callback) == PUGL_SUCCESS; | |
461 } | |
462 | |
463 #ifndef DGL_FILE_BROWSER_DISABLED | |
464 // ----------------------------------------------------------------------- | |
465 // file handling | |
466 | |
467 bool Window::PrivateData::openFileBrowser(const FileBrowserOptions& options) | |
468 { | |
469 if (fileBrowserHandle != nullptr) | |
470 fileBrowserClose(fileBrowserHandle); | |
471 | |
472 FileBrowserOptions options2 = options; | |
473 | |
474 if (options2.title == nullptr) | |
475 options2.title = puglGetWindowTitle(view); | |
476 | |
477 fileBrowserHandle = fileBrowserCreate(isEmbed, | |
478 puglGetNativeView(view), | |
479 autoScaling ? autoScaleFactor : scaleFactor, | |
480 options2); | |
481 | |
482 return fileBrowserHandle != nullptr; | |
483 } | |
484 #endif // ! DGL_FILE_BROWSER_DISABLED | |
485 | |
486 // ----------------------------------------------------------------------- | |
487 // modal handling | |
488 | |
489 void Window::PrivateData::startModal() | |
490 { | |
491 DGL_DBG("Window modal loop starting..."); DGL_DBGF; | |
492 DISTRHO_SAFE_ASSERT_RETURN(modal.parent != nullptr, show()); | |
493 | |
494 // activate modal mode for this window | |
495 modal.enabled = true; | |
496 | |
497 // make parent give focus to us | |
498 modal.parent->modal.child = this; | |
499 | |
500 // make sure both parent and ourselves are visible | |
501 modal.parent->show(); | |
502 show(); | |
503 | |
504 #ifdef DISTRHO_OS_MAC | |
505 puglMacOSAddChildWindow(modal.parent->view, view); | |
506 #endif | |
507 | |
508 DGL_DBG("Ok\n"); | |
509 } | |
510 | |
511 void Window::PrivateData::stopModal() | |
512 { | |
513 DGL_DBG("Window modal loop stopping..."); DGL_DBGF; | |
514 | |
515 // deactivate modal mode | |
516 modal.enabled = false; | |
517 | |
518 // safety checks, make sure we have a parent and we are currently active as the child to give focus to | |
519 if (modal.parent == nullptr) | |
520 return; | |
521 if (modal.parent->modal.child != this) | |
522 return; | |
523 | |
524 #ifdef DISTRHO_OS_MAC | |
525 puglMacOSRemoveChildWindow(modal.parent->view, view); | |
526 #endif | |
527 | |
528 // stop parent from giving focus to us, so it behaves like normal | |
529 modal.parent->modal.child = nullptr; | |
530 | |
531 // refocus main window after closing child | |
532 if (! modal.parent->isClosed) | |
533 { | |
534 const Widget::MotionEvent ev; | |
535 modal.parent->onPuglMotion(ev); | |
536 modal.parent->focus(); | |
537 } | |
538 | |
539 DGL_DBG("Ok\n"); | |
540 } | |
541 | |
542 void Window::PrivateData::runAsModal(const bool blockWait) | |
543 { | |
544 DGL_DBGp("Window::PrivateData::runAsModal %i\n", blockWait); | |
545 startModal(); | |
546 | |
547 if (blockWait) | |
548 { | |
549 DISTRHO_SAFE_ASSERT_RETURN(appData->isStandalone,); | |
550 | |
551 while (isVisible && modal.enabled) | |
552 appData->idle(10); | |
553 | |
554 stopModal(); | |
555 } | |
556 else | |
557 { | |
558 appData->idle(0); | |
559 } | |
560 } | |
561 | |
562 // ----------------------------------------------------------------------- | |
563 // pugl events | |
564 | |
565 void Window::PrivateData::onPuglConfigure(const double width, const double height) | |
566 { | |
567 DISTRHO_SAFE_ASSERT_INT2_RETURN(width > 1 && height > 1, width, height,); | |
568 | |
569 DGL_DBGp("PUGL: onReshape : %f %f\n", width, height); | |
570 | |
571 if (autoScaling) | |
572 { | |
573 const double scaleHorizontal = width / static_cast<double>(minWidth); | |
574 const double scaleVertical = height / static_cast<double>(minHeight); | |
575 autoScaleFactor = scaleHorizontal < scaleVertical ? scaleHorizontal : scaleVertical; | |
576 } | |
577 | |
578 const uint uwidth = static_cast<uint>(width + 0.5); | |
579 const uint uheight = static_cast<uint>(height + 0.5); | |
580 | |
581 self->onReshape(uwidth, uheight); | |
582 | |
583 #ifndef DPF_TEST_WINDOW_CPP | |
584 FOR_EACH_TOP_LEVEL_WIDGET(it) | |
585 { | |
586 TopLevelWidget* const widget(*it); | |
587 | |
588 /* Some special care here, we call Widget::setSize instead of the TopLevelWidget one. | |
589 * This is because we want TopLevelWidget::setSize to handle both window and widget size, | |
590 * but we dont want to change window size here, because we are the window.. | |
591 * So we just call the Widget specific method manually. | |
592 * | |
593 * Alternatively, we could expose a resize function on the pData, like done with the display function. | |
594 * But there is nothing extra we need to do in there, so this works fine. | |
595 */ | |
596 ((Widget*)widget)->setSize(uwidth, uheight); | |
597 } | |
598 #endif | |
599 | |
600 // always repaint after a resize | |
601 puglPostRedisplay(view); | |
602 } | |
603 | |
604 void Window::PrivateData::onPuglExpose() | |
605 { | |
606 // DGL_DBG("PUGL: onPuglExpose\n"); | |
607 | |
608 puglOnDisplayPrepare(view); | |
609 | |
610 #ifndef DPF_TEST_WINDOW_CPP | |
611 FOR_EACH_TOP_LEVEL_WIDGET(it) | |
612 { | |
613 TopLevelWidget* const widget(*it); | |
614 | |
615 if (widget->isVisible()) | |
616 widget->pData->display(); | |
617 } | |
618 | |
619 if (char* const filename = filenameToRenderInto) | |
620 { | |
621 const PuglRect rect = puglGetFrame(view); | |
622 filenameToRenderInto = nullptr; | |
623 renderToPicture(filename, getGraphicsContext(), static_cast<uint>(rect.width), static_cast<uint>(rect.height)); | |
624 std::free(filename); | |
625 } | |
626 #endif | |
627 } | |
628 | |
629 void Window::PrivateData::onPuglClose() | |
630 { | |
631 DGL_DBG("PUGL: onClose\n"); | |
632 | |
633 #ifndef DISTRHO_OS_MAC | |
634 // if we are running as standalone we can prevent closing in certain conditions | |
635 if (appData->isStandalone) | |
636 { | |
637 // a child window is active, gives focus to it | |
638 if (modal.child != nullptr) | |
639 return modal.child->focus(); | |
640 | |
641 // ask window if we should close | |
642 if (! self->onClose()) | |
643 return; | |
644 } | |
645 #endif | |
646 | |
647 if (modal.enabled) | |
648 stopModal(); | |
649 | |
650 if (modal.child != nullptr) | |
651 { | |
652 modal.child->close(); | |
653 modal.child = nullptr; | |
654 } | |
655 | |
656 close(); | |
657 } | |
658 | |
659 void Window::PrivateData::onPuglFocus(const bool focus, const CrossingMode mode) | |
660 { | |
661 DGL_DBGp("onPuglFocus : %i %i | %i\n", focus, mode, isClosed); | |
662 | |
663 if (isClosed) | |
664 return; | |
665 | |
666 if (modal.child != nullptr) | |
667 return modal.child->focus(); | |
668 | |
669 self->onFocus(focus, mode); | |
670 } | |
671 | |
672 void Window::PrivateData::onPuglKey(const Widget::KeyboardEvent& ev) | |
673 { | |
674 DGL_DBGp("onPuglKey : %i %u %u\n", ev.press, ev.key, ev.keycode); | |
675 | |
676 if (modal.child != nullptr) | |
677 return modal.child->focus(); | |
678 | |
679 #ifndef DPF_TEST_WINDOW_CPP | |
680 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) | |
681 { | |
682 TopLevelWidget* const widget(*rit); | |
683 | |
684 if (widget->isVisible() && widget->onKeyboard(ev)) | |
685 break; | |
686 } | |
687 #endif | |
688 } | |
689 | |
690 void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent& ev) | |
691 { | |
692 DGL_DBGp("onPuglText : %u %u %s\n", ev.keycode, ev.character, ev.string); | |
693 | |
694 if (modal.child != nullptr) | |
695 return modal.child->focus(); | |
696 | |
697 #ifndef DPF_TEST_WINDOW_CPP | |
698 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) | |
699 { | |
700 TopLevelWidget* const widget(*rit); | |
701 | |
702 if (widget->isVisible() && widget->onCharacterInput(ev)) | |
703 break; | |
704 } | |
705 #endif | |
706 } | |
707 | |
708 void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev) | |
709 { | |
710 DGL_DBGp("onPuglMouse : %i %i %f %f\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY()); | |
711 | |
712 if (modal.child != nullptr) | |
713 return modal.child->focus(); | |
714 | |
715 #ifndef DPF_TEST_WINDOW_CPP | |
716 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) | |
717 { | |
718 TopLevelWidget* const widget(*rit); | |
719 | |
720 if (widget->isVisible() && widget->onMouse(ev)) | |
721 break; | |
722 } | |
723 #endif | |
724 } | |
725 | |
726 void Window::PrivateData::onPuglMotion(const Widget::MotionEvent& ev) | |
727 { | |
728 DGL_DBGp("onPuglMotion : %f %f\n", ev.pos.getX(), ev.pos.getY()); | |
729 | |
730 if (modal.child != nullptr) | |
731 return modal.child->focus(); | |
732 | |
733 #ifndef DPF_TEST_WINDOW_CPP | |
734 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) | |
735 { | |
736 TopLevelWidget* const widget(*rit); | |
737 | |
738 if (widget->isVisible() && widget->onMotion(ev)) | |
739 break; | |
740 } | |
741 #endif | |
742 } | |
743 | |
744 void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev) | |
745 { | |
746 DGL_DBGp("onPuglScroll : %f %f %f %f\n", ev.pos.getX(), ev.pos.getY(), ev.delta.getX(), ev.delta.getY()); | |
747 | |
748 if (modal.child != nullptr) | |
749 return modal.child->focus(); | |
750 | |
751 #ifndef DPF_TEST_WINDOW_CPP | |
752 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) | |
753 { | |
754 TopLevelWidget* const widget(*rit); | |
755 | |
756 if (widget->isVisible() && widget->onScroll(ev)) | |
757 break; | |
758 } | |
759 #endif | |
760 } | |
761 | |
762 const void* Window::PrivateData::getClipboard(size_t& dataSize) | |
763 { | |
764 clipboardTypeId = 0; | |
765 waitingForClipboardData = true, | |
766 waitingForClipboardEvents = true; | |
767 | |
768 // begin clipboard dance here | |
769 if (puglPaste(view) != PUGL_SUCCESS) | |
770 { | |
771 dataSize = 0; | |
772 waitingForClipboardEvents = false; | |
773 return nullptr; | |
774 } | |
775 | |
776 #ifdef DGL_USING_X11 | |
777 // wait for type request, clipboardTypeId must be != 0 to be valid | |
778 int retry = static_cast<int>(2 / 0.03); | |
779 while (clipboardTypeId == 0 && waitingForClipboardData && --retry >= 0) | |
780 { | |
781 if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS) | |
782 break; | |
783 } | |
784 #endif | |
785 | |
786 if (clipboardTypeId == 0) | |
787 { | |
788 dataSize = 0; | |
789 waitingForClipboardEvents = false; | |
790 return nullptr; | |
791 } | |
792 | |
793 #ifdef DGL_USING_X11 | |
794 // wait for actual data (assumes offer was accepted) | |
795 retry = static_cast<int>(2 / 0.03); | |
796 while (waitingForClipboardData && --retry >= 0) | |
797 { | |
798 if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS) | |
799 break; | |
800 } | |
801 #endif | |
802 | |
803 if (clipboardTypeId == 0) | |
804 { | |
805 dataSize = 0; | |
806 waitingForClipboardEvents = false; | |
807 return nullptr; | |
808 } | |
809 | |
810 waitingForClipboardEvents = false; | |
811 return puglGetClipboard(view, clipboardTypeId - 1, &dataSize); | |
812 } | |
813 | |
814 uint32_t Window::PrivateData::onClipboardDataOffer() | |
815 { | |
816 DGL_DBG("onClipboardDataOffer\n"); | |
817 | |
818 if ((clipboardTypeId = self->onClipboardDataOffer()) != 0) | |
819 return clipboardTypeId; | |
820 | |
821 // stop waiting for data, it was rejected | |
822 waitingForClipboardData = false; | |
823 return 0; | |
824 } | |
825 | |
826 void Window::PrivateData::onClipboardData(const uint32_t typeId) | |
827 { | |
828 if (clipboardTypeId != typeId) | |
829 clipboardTypeId = 0; | |
830 | |
831 waitingForClipboardData = false; | |
832 } | |
833 | |
834 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) | |
835 static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose); | |
836 #endif | |
837 | |
838 PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event) | |
839 { | |
840 Window::PrivateData* const pData = (Window::PrivateData*)puglGetHandle(view); | |
841 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) | |
842 if (event->type != PUGL_TIMER) { | |
843 printEvent(event, "pugl event: ", true); | |
844 } | |
845 #endif | |
846 | |
847 if (pData->waitingForClipboardEvents) | |
848 { | |
849 switch (event->type) | |
850 { | |
851 case PUGL_UPDATE: | |
852 case PUGL_EXPOSE: | |
853 case PUGL_FOCUS_IN: | |
854 case PUGL_FOCUS_OUT: | |
855 case PUGL_KEY_PRESS: | |
856 case PUGL_KEY_RELEASE: | |
857 case PUGL_TEXT: | |
858 case PUGL_POINTER_IN: | |
859 case PUGL_POINTER_OUT: | |
860 case PUGL_BUTTON_PRESS: | |
861 case PUGL_BUTTON_RELEASE: | |
862 case PUGL_MOTION: | |
863 case PUGL_SCROLL: | |
864 case PUGL_TIMER: | |
865 case PUGL_LOOP_ENTER: | |
866 case PUGL_LOOP_LEAVE: | |
867 return PUGL_SUCCESS; | |
868 case PUGL_DATA_OFFER: | |
869 case PUGL_DATA: | |
870 break; | |
871 default: | |
872 d_stdout("Got event %d while waitingForClipboardEvents", event->type); | |
873 break; | |
874 } | |
875 } | |
876 | |
877 switch (event->type) | |
878 { | |
879 ///< No event | |
880 case PUGL_NOTHING: | |
881 break; | |
882 | |
883 ///< View created, a #PuglEventCreate | |
884 case PUGL_CREATE: | |
885 #ifdef DGL_USING_X11 | |
886 if (! pData->isEmbed) | |
887 puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone); | |
888 #endif | |
889 break; | |
890 | |
891 ///< View destroyed, a #PuglEventDestroy | |
892 case PUGL_DESTROY: | |
893 break; | |
894 | |
895 ///< View moved/resized, a #PuglEventConfigure | |
896 case PUGL_CONFIGURE: | |
897 // unused x, y (double) | |
898 pData->onPuglConfigure(event->configure.width, event->configure.height); | |
899 break; | |
900 | |
901 ///< View made visible, a #PuglEventMap | |
902 case PUGL_MAP: | |
903 break; | |
904 | |
905 ///< View made invisible, a #PuglEventUnmap | |
906 case PUGL_UNMAP: | |
907 break; | |
908 | |
909 ///< View ready to draw, a #PuglEventUpdate | |
910 case PUGL_UPDATE: | |
911 break; | |
912 | |
913 ///< View must be drawn, a #PuglEventExpose | |
914 case PUGL_EXPOSE: | |
915 // unused x, y, width, height (double) | |
916 pData->onPuglExpose(); | |
917 break; | |
918 | |
919 ///< View will be closed, a #PuglEventClose | |
920 case PUGL_CLOSE: | |
921 pData->onPuglClose(); | |
922 break; | |
923 | |
924 ///< Keyboard focus entered view, a #PuglEventFocus | |
925 case PUGL_FOCUS_IN: | |
926 ///< Keyboard focus left view, a #PuglEventFocus | |
927 case PUGL_FOCUS_OUT: | |
928 pData->onPuglFocus(event->type == PUGL_FOCUS_IN, | |
929 static_cast<CrossingMode>(event->focus.mode)); | |
930 break; | |
931 | |
932 ///< Key pressed, a #PuglEventKey | |
933 case PUGL_KEY_PRESS: | |
934 ///< Key released, a #PuglEventKey | |
935 case PUGL_KEY_RELEASE: | |
936 { | |
937 // unused x, y, xRoot, yRoot (double) | |
938 Widget::KeyboardEvent ev; | |
939 ev.mod = event->key.state; | |
940 ev.flags = event->key.flags; | |
941 ev.time = static_cast<uint>(event->key.time * 1000.0 + 0.5); | |
942 ev.press = event->type == PUGL_KEY_PRESS; | |
943 ev.key = event->key.key; | |
944 ev.keycode = event->key.keycode; | |
945 | |
946 // keyboard events must always be lowercase | |
947 if (ev.key >= 'A' && ev.key <= 'Z') | |
948 { | |
949 ev.key += 'a' - 'A'; // A-Z -> a-z | |
950 ev.mod |= kModifierShift; | |
951 } | |
952 | |
953 pData->onPuglKey(ev); | |
954 break; | |
955 } | |
956 | |
957 ///< Character entered, a #PuglEventText | |
958 case PUGL_TEXT: | |
959 { | |
960 // unused x, y, xRoot, yRoot (double) | |
961 Widget::CharacterInputEvent ev; | |
962 ev.mod = event->text.state; | |
963 ev.flags = event->text.flags; | |
964 ev.time = static_cast<uint>(event->text.time * 1000.0 + 0.5); | |
965 ev.keycode = event->text.keycode; | |
966 ev.character = event->text.character; | |
967 std::strncpy(ev.string, event->text.string, sizeof(ev.string)); | |
968 pData->onPuglText(ev); | |
969 break; | |
970 } | |
971 | |
972 ///< Pointer entered view, a #PuglEventCrossing | |
973 case PUGL_POINTER_IN: | |
974 break; | |
975 ///< Pointer left view, a #PuglEventCrossing | |
976 case PUGL_POINTER_OUT: | |
977 break; | |
978 | |
979 ///< Mouse button pressed, a #PuglEventButton | |
980 case PUGL_BUTTON_PRESS: | |
981 ///< Mouse button released, a #PuglEventButton | |
982 case PUGL_BUTTON_RELEASE: | |
983 { | |
984 Widget::MouseEvent ev; | |
985 ev.mod = event->button.state; | |
986 ev.flags = event->button.flags; | |
987 ev.time = static_cast<uint>(event->button.time * 1000.0 + 0.5); | |
988 ev.button = event->button.button + 1; | |
989 ev.press = event->type == PUGL_BUTTON_PRESS; | |
990 ev.pos = Point<double>(event->button.x, event->button.y); | |
991 ev.absolutePos = ev.pos; | |
992 pData->onPuglMouse(ev); | |
993 break; | |
994 } | |
995 | |
996 ///< Pointer moved, a #PuglEventMotion | |
997 case PUGL_MOTION: | |
998 { | |
999 Widget::MotionEvent ev; | |
1000 ev.mod = event->motion.state; | |
1001 ev.flags = event->motion.flags; | |
1002 ev.time = static_cast<uint>(event->motion.time * 1000.0 + 0.5); | |
1003 ev.pos = Point<double>(event->motion.x, event->motion.y); | |
1004 ev.absolutePos = ev.pos; | |
1005 pData->onPuglMotion(ev); | |
1006 break; | |
1007 } | |
1008 | |
1009 ///< Scrolled, a #PuglEventScroll | |
1010 case PUGL_SCROLL: | |
1011 { | |
1012 Widget::ScrollEvent ev; | |
1013 ev.mod = event->scroll.state; | |
1014 ev.flags = event->scroll.flags; | |
1015 ev.time = static_cast<uint>(event->scroll.time * 1000.0 + 0.5); | |
1016 ev.pos = Point<double>(event->scroll.x, event->scroll.y); | |
1017 ev.delta = Point<double>(event->scroll.dx, event->scroll.dy); | |
1018 ev.direction = static_cast<ScrollDirection>(event->scroll.direction); | |
1019 ev.absolutePos = ev.pos; | |
1020 pData->onPuglScroll(ev); | |
1021 break; | |
1022 } | |
1023 | |
1024 ///< Custom client message, a #PuglEventClient | |
1025 case PUGL_CLIENT: | |
1026 break; | |
1027 | |
1028 ///< Timer triggered, a #PuglEventTimer | |
1029 case PUGL_TIMER: | |
1030 if (IdleCallback* const idleCallback = reinterpret_cast<IdleCallback*>(event->timer.id)) | |
1031 idleCallback->idleCallback(); | |
1032 break; | |
1033 | |
1034 ///< Recursive loop entered, a #PuglEventLoopEnter | |
1035 case PUGL_LOOP_ENTER: | |
1036 break; | |
1037 | |
1038 ///< Recursive loop left, a #PuglEventLoopLeave | |
1039 case PUGL_LOOP_LEAVE: | |
1040 break; | |
1041 | |
1042 ///< Data offered from clipboard, a #PuglDataOfferEvent | |
1043 case PUGL_DATA_OFFER: | |
1044 if (const uint32_t offerTypeId = pData->onClipboardDataOffer()) | |
1045 puglAcceptOffer(view, &event->offer, offerTypeId - 1); | |
1046 break; | |
1047 | |
1048 ///< Data available from clipboard, a #PuglDataEvent | |
1049 case PUGL_DATA: | |
1050 pData->onClipboardData(event->data.typeIndex + 1); | |
1051 break; | |
1052 } | |
1053 | |
1054 return PUGL_SUCCESS; | |
1055 } | |
1056 | |
1057 // ----------------------------------------------------------------------- | |
1058 | |
1059 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) | |
1060 static int printModifiers(const uint32_t mods) | |
1061 { | |
1062 return fprintf(stderr, "Modifiers:%s%s%s%s\n", | |
1063 (mods & PUGL_MOD_SHIFT) ? " Shift" : "", | |
1064 (mods & PUGL_MOD_CTRL) ? " Ctrl" : "", | |
1065 (mods & PUGL_MOD_ALT) ? " Alt" : "", | |
1066 (mods & PUGL_MOD_SUPER) ? " Super" : ""); | |
1067 } | |
1068 | |
1069 static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose) | |
1070 { | |
1071 #define FFMT "%6.1f" | |
1072 #define PFMT FFMT " " FFMT | |
1073 #define PRINT(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) | |
1074 | |
1075 switch (event->type) { | |
1076 case PUGL_NOTHING: | |
1077 return 0; | |
1078 case PUGL_KEY_PRESS: | |
1079 return PRINT("%sKey press code %3u key U+%04X\n", | |
1080 prefix, | |
1081 event->key.keycode, | |
1082 event->key.key); | |
1083 case PUGL_KEY_RELEASE: | |
1084 return PRINT("%sKey release code %3u key U+%04X\n", | |
1085 prefix, | |
1086 event->key.keycode, | |
1087 event->key.key); | |
1088 case PUGL_TEXT: | |
1089 return PRINT("%sText entry code %3u char U+%04X (%s)\n", | |
1090 prefix, | |
1091 event->text.keycode, | |
1092 event->text.character, | |
1093 event->text.string); | |
1094 case PUGL_BUTTON_PRESS: | |
1095 case PUGL_BUTTON_RELEASE: | |
1096 return (PRINT("%sMouse %u %s at " PFMT " ", | |
1097 prefix, | |
1098 event->button.button, | |
1099 (event->type == PUGL_BUTTON_PRESS) ? "down" : "up ", | |
1100 event->button.x, | |
1101 event->button.y) + | |
1102 printModifiers(event->scroll.state)); | |
1103 case PUGL_SCROLL: | |
1104 return (PRINT("%sScroll %5.1f %5.1f at " PFMT " ", | |
1105 prefix, | |
1106 event->scroll.dx, | |
1107 event->scroll.dy, | |
1108 event->scroll.x, | |
1109 event->scroll.y) + | |
1110 printModifiers(event->scroll.state)); | |
1111 case PUGL_POINTER_IN: | |
1112 return PRINT("%sMouse enter at " PFMT "\n", | |
1113 prefix, | |
1114 event->crossing.x, | |
1115 event->crossing.y); | |
1116 case PUGL_POINTER_OUT: | |
1117 return PRINT("%sMouse leave at " PFMT "\n", | |
1118 prefix, | |
1119 event->crossing.x, | |
1120 event->crossing.y); | |
1121 case PUGL_FOCUS_IN: | |
1122 return PRINT("%sFocus in %i\n", | |
1123 prefix, | |
1124 event->focus.mode); | |
1125 case PUGL_FOCUS_OUT: | |
1126 return PRINT("%sFocus out %i\n", | |
1127 prefix, | |
1128 event->focus.mode); | |
1129 case PUGL_CLIENT: | |
1130 return PRINT("%sClient %" PRIXPTR " %" PRIXPTR "\n", | |
1131 prefix, | |
1132 event->client.data1, | |
1133 event->client.data2); | |
1134 case PUGL_TIMER: | |
1135 return PRINT("%sTimer %" PRIuPTR "\n", prefix, event->timer.id); | |
1136 default: | |
1137 break; | |
1138 } | |
1139 | |
1140 if (verbose) { | |
1141 switch (event->type) { | |
1142 case PUGL_CREATE: | |
1143 return fprintf(stderr, "%sCreate\n", prefix); | |
1144 case PUGL_DESTROY: | |
1145 return fprintf(stderr, "%sDestroy\n", prefix); | |
1146 case PUGL_MAP: | |
1147 return fprintf(stderr, "%sMap\n", prefix); | |
1148 case PUGL_UNMAP: | |
1149 return fprintf(stderr, "%sUnmap\n", prefix); | |
1150 case PUGL_UPDATE: | |
1151 return 0; // fprintf(stderr, "%sUpdate\n", prefix); | |
1152 case PUGL_CONFIGURE: | |
1153 return PRINT("%sConfigure " PFMT " " PFMT "\n", | |
1154 prefix, | |
1155 event->configure.x, | |
1156 event->configure.y, | |
1157 event->configure.width, | |
1158 event->configure.height); | |
1159 case PUGL_EXPOSE: | |
1160 return PRINT("%sExpose " PFMT " " PFMT "\n", | |
1161 prefix, | |
1162 event->expose.x, | |
1163 event->expose.y, | |
1164 event->expose.width, | |
1165 event->expose.height); | |
1166 case PUGL_CLOSE: | |
1167 return PRINT("%sClose\n", prefix); | |
1168 case PUGL_MOTION: | |
1169 return PRINT("%sMouse motion at " PFMT "\n", | |
1170 prefix, | |
1171 event->motion.x, | |
1172 event->motion.y); | |
1173 default: | |
1174 return PRINT("%sUnknown event type %d\n", prefix, (int)event->type); | |
1175 } | |
1176 } | |
1177 | |
1178 #undef PRINT | |
1179 #undef PFMT | |
1180 #undef FFMT | |
1181 | |
1182 return 0; | |
1183 } | |
1184 #endif | |
1185 | |
1186 #undef DGL_DBG | |
1187 #undef DGL_DBGF | |
1188 | |
1189 // ----------------------------------------------------------------------- | |
1190 | |
1191 END_NAMESPACE_DGL |