LMMS
Loading...
Searching...
No Matches
juce_Displays.cpp
Go to the documentation of this file.
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
30{
31 init (desktop);
32}
33
34void Displays::init (Desktop& desktop)
35{
37}
38
39const Displays::Display* Displays::getDisplayForRect (Rectangle<int> rect, bool isPhysical) const noexcept
40{
41 int maxArea = -1;
42 const Display* foundDisplay = nullptr;
43
44 for (auto& display : displays)
45 {
46 auto displayArea = display.totalArea;
47
48 if (isPhysical)
49 displayArea = (displayArea.withZeroOrigin() * display.scale) + display.topLeftPhysical;
50
51 displayArea = displayArea.getIntersection (rect);
52 auto area = displayArea.getWidth() * displayArea.getHeight();
53
54 if (area >= maxArea)
55 {
56 maxArea = area;
57 foundDisplay = &display;
58 }
59 }
60
61 return foundDisplay;
62}
63
64const Displays::Display* Displays::getDisplayForPoint (Point<int> point, bool isPhysical) const noexcept
65{
66 auto minDistance = std::numeric_limits<int>::max();
67 const Display* foundDisplay = nullptr;
68
69 for (auto& display : displays)
70 {
71 auto displayArea = display.totalArea;
72
73 if (isPhysical)
74 displayArea = (displayArea.withZeroOrigin() * display.scale) + display.topLeftPhysical;
75
76 if (displayArea.contains (point))
77 return &display;
78
79 auto distance = displayArea.getCentre().getDistanceFrom (point);
80
81 if (distance <= minDistance)
82 {
83 minDistance = distance;
84 foundDisplay = &display;
85 }
86 }
87
88 return foundDisplay;
89}
90
91Rectangle<int> Displays::physicalToLogical (Rectangle<int> rect, const Display* useScaleFactorOfDisplay) const noexcept
92{
93 return physicalToLogical (rect.toFloat(), useScaleFactorOfDisplay).toNearestInt();
94}
95
96Rectangle<float> Displays::physicalToLogical (Rectangle<float> rect, const Display* useScaleFactorOfDisplay) const noexcept
97{
98 const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
99 : getDisplayForRect (rect.toNearestInt(), true);
100
101 if (display == nullptr)
102 return rect;
103
104 auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
105
106 return ((rect - display->topLeftPhysical.toFloat()) / (display->scale / globalScale))
107 + (display->totalArea.getTopLeft().toFloat() * globalScale);
108}
109
110Rectangle<int> Displays::logicalToPhysical (Rectangle<int> rect, const Display* useScaleFactorOfDisplay) const noexcept
111{
112 return logicalToPhysical (rect.toFloat(), useScaleFactorOfDisplay).toNearestInt();
113}
114
115Rectangle<float> Displays::logicalToPhysical (Rectangle<float> rect, const Display* useScaleFactorOfDisplay) const noexcept
116{
117 const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
118 : getDisplayForRect (rect.toNearestInt(), false);
119
120 if (display == nullptr)
121 return rect;
122
123 auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
124
125 return ((rect.toFloat() - (display->totalArea.getTopLeft().toFloat() * globalScale)) * (display->scale / globalScale))
126 + display->topLeftPhysical.toFloat();
127}
128
129template <typename ValueType>
130Point<ValueType> Displays::physicalToLogical (Point<ValueType> point, const Display* useScaleFactorOfDisplay) const noexcept
131{
132 const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
133 : getDisplayForPoint (point.roundToInt(), true);
134
135 if (display == nullptr)
136 return point;
137
138 auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
139
140 Point<ValueType> logicalTopLeft (static_cast<ValueType> (display->totalArea.getX()), static_cast<ValueType> (display->totalArea.getY()));
141 Point<ValueType> physicalTopLeft (static_cast<ValueType> (display->topLeftPhysical.getX()), static_cast<ValueType> (display->topLeftPhysical.getY()));
142
143 return ((point - physicalTopLeft) / (display->scale / globalScale)) + (logicalTopLeft * globalScale);
144}
145
146template <typename ValueType>
147Point<ValueType> Displays::logicalToPhysical (Point<ValueType> point, const Display* useScaleFactorOfDisplay) const noexcept
148{
149 const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
150 : getDisplayForPoint (point.roundToInt(), false);
151
152 if (display == nullptr)
153 return point;
154
155 auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
156
157 Point<ValueType> logicalTopLeft (static_cast<ValueType> (display->totalArea.getX()), static_cast<ValueType> (display->totalArea.getY()));
158 Point<ValueType> physicalTopLeft (static_cast<ValueType> (display->topLeftPhysical.getX()), static_cast<ValueType> (display->topLeftPhysical.getY()));
159
160 return ((point - (logicalTopLeft * globalScale)) * (display->scale / globalScale)) + physicalTopLeft;
161}
162
164{
166
167 for (auto& d : displays)
168 if (d.isMain)
169 return &d;
170
171 return nullptr;
172}
173
175{
178
179 for (auto& d : displays)
180 rl.addWithoutMerging (userAreasOnly ? d.userArea : d.totalArea);
181
182 return rl;
183}
184
185Rectangle<int> Displays::getTotalBounds (bool userAreasOnly) const
186{
187 return getRectangleList (userAreasOnly).getBounds();
188}
189
191{
192 Array<Display> oldDisplays;
193 oldDisplays.swapWith (displays);
194
196
197 if (oldDisplays != displays)
198 {
199 for (auto i = ComponentPeer::getNumPeers(); --i >= 0;)
200 if (auto* peer = ComponentPeer::getPeer (i))
201 peer->handleScreenSizeChange();
202 }
203}
204
205bool operator== (const Displays::Display& d1, const Displays::Display& d2) noexcept;
206bool operator== (const Displays::Display& d1, const Displays::Display& d2) noexcept
207{
208 return d1.isMain == d2.isMain
209 && d1.totalArea == d2.totalArea
210 && d1.userArea == d2.userArea
211 && d1.topLeftPhysical == d2.topLeftPhysical
212 && d1.scale == d2.scale
213 && d1.dpi == d2.dpi;
214}
215
216bool operator!= (const Displays::Display& d1, const Displays::Display& d2) noexcept;
217bool operator!= (const Displays::Display& d1, const Displays::Display& d2) noexcept { return ! (d1 == d2); }
218
219//==============================================================================
220// These methods are used for converting the totalArea and userArea Rectangles in Display from physical to logical
221// pixels. We do this by constructing a graph of connected displays where the root node has position (0, 0); this can be
222// safely converted to logical pixels using its scale factor and we can then traverse the graph and work out the logical pixels
223// for all the other connected displays. We need to do this as the logical bounds of a display depend not only on its scale
224// factor but also the scale factor of the displays connected to it.
225
245
247static void processDisplay (DisplayNode* currentNode, Array<DisplayNode>& allNodes)
248{
249 const auto physicalArea = currentNode->display->totalArea.toDouble();
250 const auto scale = currentNode->display->scale;
251
252 if (! currentNode->isRoot)
253 {
254 const auto logicalWidth = physicalArea.getWidth() / scale;
255 const auto logicalHeight = physicalArea.getHeight() / scale;
256
257 const auto physicalParentArea = currentNode->parent->display->totalArea.toDouble();
258 const auto logicalParentArea = currentNode->parent->logicalArea; // logical area of parent has already been calculated
259 const auto parentScale = currentNode->parent->display->scale;
260
261 Rectangle<double> logicalArea (0.0, 0.0, logicalWidth, logicalHeight);
262
263 if (physicalArea.getRight() == physicalParentArea.getX()) logicalArea.setPosition ({ logicalParentArea.getX() - logicalWidth, physicalArea.getY() / parentScale }); // on left
264 else if (physicalArea.getX() == physicalParentArea.getRight()) logicalArea.setPosition ({ logicalParentArea.getRight(), physicalArea.getY() / parentScale }); // on right
265 else if (physicalArea.getBottom() == physicalParentArea.getY()) logicalArea.setPosition ({ physicalArea.getX() / parentScale, logicalParentArea.getY() - logicalHeight }); // on top
266 else if (physicalArea.getY() == physicalParentArea.getBottom()) logicalArea.setPosition ({ physicalArea.getX() / parentScale, logicalParentArea.getBottom() }); // on bottom
267 else jassertfalse;
268
269 currentNode->logicalArea = logicalArea;
270 }
271 else
272 {
273 // If currentNode is the root (position (0, 0)) then we can just scale the physical area
274 currentNode->logicalArea = physicalArea / scale;
275 currentNode->parent = currentNode;
276 }
277
278 // Find child nodes
279 Array<DisplayNode*> children;
280 for (auto& node : allNodes)
281 {
282 // Already calculated
283 if (node.parent != nullptr)
284 continue;
285
286 const auto otherPhysicalArea = node.display->totalArea.toDouble();
287
288 // If the displays are touching on any side
289 if (otherPhysicalArea.getX() == physicalArea.getRight() || otherPhysicalArea.getRight() == physicalArea.getX()
290 || otherPhysicalArea.getY() == physicalArea.getBottom() || otherPhysicalArea.getBottom() == physicalArea.getY())
291 {
292 node.parent = currentNode;
293 children.add (&node);
294 }
295 }
296
297 // Recursively process all child nodes
298 for (auto child : children)
299 processDisplay (child, allNodes);
300}
301
306{
307 if (displays.size() == 1)
308 {
309 auto& display = displays.getReference (0);
310
311 display.totalArea = (display.totalArea.toDouble() / display.scale).toNearestInt();
312 display.userArea = (display.userArea.toDouble() / display.scale).toNearestInt();
313
314 return;
315 }
316
317 Array<DisplayNode> displayNodes;
318
319 for (auto& d : displays)
320 {
321 DisplayNode node;
322
323 node.display = &d;
324
325 if (d.totalArea.getTopLeft() == Point<int>())
326 node.isRoot = true;
327
328 displayNodes.add (node);
329 }
330
331 auto* root = [&displayNodes]() -> DisplayNode*
332 {
333 for (auto& node : displayNodes)
334 if (node.isRoot)
335 return &node;
336
337 auto minDistance = std::numeric_limits<int>::max();
338 DisplayNode* retVal = nullptr;
339
340 for (auto& node : displayNodes)
341 {
342 auto distance = node.display->totalArea.getTopLeft().getDistanceFrom ({});
343
344 if (distance < minDistance)
345 {
346 minDistance = distance;
347 retVal = &node;
348 }
349 }
350
351 if (retVal != nullptr)
352 retVal->isRoot = true;
353
354 return retVal;
355 }();
356
357 // Must have a root node!
358 jassert (root != nullptr);
359
360 // Recursively traverse the display graph from the root and work out logical bounds
361 processDisplay (root, displayNodes);
362
363 for (auto& node : displayNodes)
364 {
365 // All of the nodes should have a parent
366 jassert (node.parent != nullptr);
367
368 auto relativeUserArea = (node.display->userArea.toDouble() - node.display->totalArea.toDouble().getTopLeft()) / node.display->scale;
369
370 // Now set Display::totalArea and ::userArea using the logical area that we have calculated
371 node.display->topLeftPhysical = node.display->totalArea.getTopLeft();
372 node.display->totalArea = node.logicalArea.toNearestInt();
373 node.display->userArea = (relativeUserArea + node.logicalArea.getTopLeft()).toNearestInt();
374 }
375}
376
377#ifndef DOXYGEN
378 // explicit template instantiations
379 template Point<int> Displays::physicalToLogical (Point<int>, const Display*) const noexcept;
380 template Point<float> Displays::physicalToLogical (Point<float>, const Display*) const noexcept;
381
382 template Point<int> Displays::logicalToPhysical (Point<int>, const Display*) const noexcept;
383 template Point<float> Displays::logicalToPhysical (Point<float>, const Display*) const noexcept;
384#endif
385
386//==============================================================================
387// Deprecated methods
389{
391 const auto* best = &displays.getReference (0);
392 auto bestDistance = std::numeric_limits<int>::max();
393
394 for (auto& d : displays)
395 {
396 if (d.totalArea.contains (position))
397 {
398 best = &d;
399 break;
400 }
401
402 auto distance = d.totalArea.getCentre().getDistanceFrom (position);
403
404 if (distance < bestDistance)
405 {
406 bestDistance = distance;
407 best = &d;
408 }
409 }
410
411 return *best;
412}
413
414const Displays::Display& Displays::findDisplayForRect (Rectangle<int> rect, bool isPhysical) const noexcept
415{
416 if (auto* display = getDisplayForRect (rect, isPhysical))
417 return *display;
418
419 return emptyDisplay;
420}
421
422const Displays::Display& Displays::findDisplayForPoint (Point<int> point, bool isPhysical) const noexcept
423{
424 if (auto* display = getDisplayForPoint (point, isPhysical))
425 return *display;
426
427 return emptyDisplay;
428}
429
431{
432 if (auto* display = getPrimaryDisplay())
433 return *display;
434
435 return emptyDisplay;
436}
437
438} // namespace juce
#define noexcept
Definition DistrhoDefines.h:72
Definition juce_Array.h:56
void swapWith(OtherArrayType &otherArray) noexcept
Definition juce_Array.h:621
void add(const ElementType &newElement)
Definition juce_Array.h:418
static int getNumPeers() noexcept
Definition juce_ComponentPeer.cpp:51
static ComponentPeer * getPeer(int index) noexcept
Definition juce_ComponentPeer.cpp:56
float getGlobalScaleFactor() const noexcept
Definition juce_Desktop.h:391
static Desktop &JUCE_CALLTYPE getInstance()
Definition juce_Desktop.cpp:50
void refresh()
Definition juce_Displays.cpp:190
Display emptyDisplay
Definition juce_Displays.h:189
void updateToLogical()
Definition juce_Displays.cpp:305
Rectangle< int > logicalToPhysical(Rectangle< int > logicalRect, const Display *useScaleFactorOfDisplay=nullptr) const noexcept
Definition juce_Displays.cpp:110
const Display & getDisplayContaining(Point< int > position) const noexcept
Definition juce_Displays.cpp:388
friend class Desktop
Definition juce_Displays.h:182
const Display * getDisplayForPoint(Point< int > point, bool isPhysical=false) const noexcept
Definition juce_Displays.cpp:64
const Display * getDisplayForRect(Rectangle< int > rect, bool isPhysical=false) const noexcept
Definition juce_Displays.cpp:39
Array< Display > displays
Definition juce_Displays.h:164
const Display & findDisplayForPoint(Point< int >, bool isPhysical=false) const noexcept
Definition juce_Displays.cpp:422
Rectangle< int > physicalToLogical(Rectangle< int > physicalRect, const Display *useScaleFactorOfDisplay=nullptr) const noexcept
Definition juce_Displays.cpp:91
const Display & getMainDisplay() const noexcept
Definition juce_Displays.cpp:430
Displays(Desktop &)
Definition juce_Displays.cpp:29
void init(Desktop &)
Definition juce_Displays.cpp:34
Rectangle< int > getTotalBounds(bool userAreasOnly) const
Definition juce_Displays.cpp:185
const Display & findDisplayForRect(Rectangle< int >, bool isPhysical=false) const noexcept
Definition juce_Displays.cpp:414
const Display * getPrimaryDisplay() const noexcept
Definition juce_Displays.cpp:163
void findDisplays(float masterScale)
Definition juce_linux_Windowing.cpp:592
RectangleList< int > getRectangleList(bool userAreasOnly) const
Definition juce_Displays.cpp:174
Definition juce_Point.h:42
Definition juce_Rectangle.h:67
Rectangle< double > toDouble() const noexcept
Definition juce_Rectangle.h:882
Rectangle< int > toNearestInt() const noexcept
Definition juce_Rectangle.h:853
void setPosition(Point< ValueType > newPos) noexcept
Definition juce_Rectangle.h:164
Definition juce_RectangleList.h:43
void addWithoutMerging(RectangleType rect)
Definition juce_RectangleList.h:180
unsigned d
Definition inflate.c:940
register unsigned i
Definition inflate.c:1575
#define JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
Definition juce_MessageManager.h:465
#define jassert(expression)
#define jassertfalse
Definition carla_juce.cpp:31
static void processDisplay(DisplayNode *currentNode, Array< DisplayNode > &allNodes)
Definition juce_Displays.cpp:247
Definition juce_Displays.cpp:230
DisplayNode * parent
Definition juce_Displays.cpp:238
bool isRoot
Definition juce_Displays.cpp:235
Rectangle< double > logicalArea
Definition juce_Displays.cpp:243
Displays::Display * display
Definition juce_Displays.cpp:232
Definition juce_Displays.h:44
Rectangle< int > totalArea
Definition juce_Displays.h:51
double scale
Definition juce_Displays.h:77
ZCONST uch * init
Definition extract.c:2392
#define const
Definition zconf.h:137