Skip to content

Commit 81408fb

Browse files
committed
Improve display example output formatting and display info
1 parent e998405 commit 81408fb

File tree

3 files changed

+282
-64
lines changed

3 files changed

+282
-64
lines changed

examples/display_example/main.cpp

Lines changed: 193 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,206 @@
11
#include <iostream>
2+
#include <iomanip>
3+
#include <thread>
4+
#include <chrono>
5+
#include <sstream>
26
#include "nativeapi.h"
37

48
using nativeapi::Display;
5-
using nativeapi::DisplayEventHandler;
69
using nativeapi::DisplayManager;
10+
using nativeapi::DisplayOrientation;
711
using nativeapi::Point;
812

13+
// Helper function to convert orientation enum to string
14+
std::string orientationToString(DisplayOrientation orientation) {
15+
switch (orientation) {
16+
case DisplayOrientation::kPortrait:
17+
return "Portrait (0 deg)";
18+
case DisplayOrientation::kLandscape:
19+
return "Landscape (90 deg)";
20+
case DisplayOrientation::kPortraitFlipped:
21+
return "Portrait Flipped (180 deg)";
22+
case DisplayOrientation::kLandscapeFlipped:
23+
return "Landscape Flipped (270 deg)";
24+
default:
25+
return "Unknown";
26+
}
27+
}
28+
29+
// Helper function to truncate string if it's too long
30+
std::string truncateString(const std::string& str, size_t maxLength) {
31+
if (str.length() <= maxLength) {
32+
return str;
33+
}
34+
return str.substr(0, maxLength - 3) + "...";
35+
}
36+
37+
// Helper function to format a table row with proper alignment
38+
std::string formatTableRow(const std::string& content, int totalWidth = 70) {
39+
std::string truncated = truncateString(content, totalWidth - 4); // Leave space for "│ " and " │"
40+
std::ostringstream oss;
41+
oss << "" << std::left << std::setw(totalWidth - 4) << truncated << "";
42+
return oss.str();
43+
}
44+
45+
// Helper function to create table border - using ASCII characters for better compatibility
46+
std::string createTableBorder(const std::string& leftChar, const std::string& rightChar, const std::string& fillChar, int width = 70) {
47+
std::string border;
48+
if (leftChar == "") border = "+";
49+
else if (leftChar == "") border = "+";
50+
else if (leftChar == "") border = "+";
51+
else border = leftChar;
52+
53+
border += std::string(width - 2, '-');
54+
55+
if (rightChar == "") border += "+";
56+
else if (rightChar == "") border += "+";
57+
else if (rightChar == "") border += "+";
58+
else border += rightChar;
59+
60+
return border;
61+
}
62+
63+
// Helper function to print display information in a formatted way
64+
void printDisplayInfo(const Display& display, bool isPrimary = false) {
65+
const int tableWidth = 70;
66+
67+
std::cout << createTableBorder("", "", "", tableWidth) << std::endl;
68+
std::cout << formatTableRow("Display: " + display.name, tableWidth) << std::endl;
69+
std::cout << createTableBorder("", "", "", tableWidth) << std::endl;
70+
std::cout << formatTableRow("ID: " + display.id, tableWidth) << std::endl;
71+
72+
// Format position string
73+
std::string positionStr = "Position: (" + std::to_string((int)display.position.x) + ", " + std::to_string((int)display.position.y) + ")";
74+
std::cout << formatTableRow(positionStr, tableWidth) << std::endl;
75+
76+
// Format size string with proper separator
77+
std::string sizeStr = "Size: " + std::to_string((int)display.size.width) + " x " + std::to_string((int)display.size.height);
78+
std::cout << formatTableRow(sizeStr, tableWidth) << std::endl;
79+
80+
// Format work area string with proper formatting
81+
std::string workAreaStr = "Work Area: (" + std::to_string((int)display.workArea.x) + ", " +
82+
std::to_string((int)display.workArea.y) + ") " +
83+
std::to_string((int)display.workArea.width) + " x " +
84+
std::to_string((int)display.workArea.height);
85+
std::cout << formatTableRow(workAreaStr, tableWidth) << std::endl;
86+
87+
// Format scale factor string
88+
std::stringstream scaleStream;
89+
scaleStream << "Scale Factor: " << std::fixed << std::setprecision(2) << display.scaleFactor;
90+
std::cout << formatTableRow(scaleStream.str(), tableWidth) << std::endl;
91+
92+
// Format primary status
93+
std::string primaryStr = "Primary: " + std::string(display.isPrimary ? "Yes" : "No");
94+
std::cout << formatTableRow(primaryStr, tableWidth) << std::endl;
95+
96+
// Format orientation
97+
std::string orientationStr = "Orientation: " + orientationToString(display.orientation);
98+
std::cout << formatTableRow(orientationStr, tableWidth) << std::endl;
99+
100+
// Format refresh rate
101+
std::string refreshStr = "Refresh Rate: " + std::to_string(display.refreshRate) + " Hz";
102+
std::cout << formatTableRow(refreshStr, tableWidth) << std::endl;
103+
104+
if (display.bitDepth > 0) {
105+
std::string bitDepthStr = "Bit Depth: " + std::to_string(display.bitDepth) + " bits";
106+
std::cout << formatTableRow(bitDepthStr, tableWidth) << std::endl;
107+
}
108+
109+
if (!display.manufacturer.empty()) {
110+
std::string manufacturerStr = "Manufacturer: " + display.manufacturer;
111+
std::cout << formatTableRow(manufacturerStr, tableWidth) << std::endl;
112+
}
113+
114+
if (!display.model.empty()) {
115+
std::string modelStr = "Model: " + display.model;
116+
std::cout << formatTableRow(modelStr, tableWidth) << std::endl;
117+
}
118+
119+
std::cout << createTableBorder("", "", "", tableWidth) << std::endl;
120+
}
121+
9122
int main() {
10-
DisplayManager displayManager = DisplayManager();
11-
12-
// DisplayEventHandler displayEventHandler = DisplayEventHandler(
13-
// [](const Display& display) {
14-
// std::cout << "Display added: " << display.id << std::endl;
15-
// },
16-
// [](const Display& display) {
17-
// std::cout << "Display removed: " << display.id << std::endl;
18-
// });
19-
// displayManager.AddListener(&displayEventHandler);
20-
21-
// Get primary display information
22-
Display primaryDisplay = displayManager.GetPrimary();
23-
std::cout << "Primary Display Information:" << std::endl;
24-
std::cout << "ID: " << primaryDisplay.id << std::endl;
25-
std::cout << "Name: " << primaryDisplay.name << std::endl;
26-
std::cout << "Resolution: " << primaryDisplay.width << "x"
27-
<< primaryDisplay.height << std::endl;
28-
std::cout << "Scale Factor: " << primaryDisplay.scaleFactor << std::endl;
29-
std::cout << "Visible Position: (" << primaryDisplay.visiblePositionX << ", "
30-
<< primaryDisplay.visiblePositionY << ")" << std::endl;
31-
std::cout << "Visible Size: " << primaryDisplay.visibleSizeWidth << "x"
32-
<< primaryDisplay.visibleSizeHeight << std::endl;
33-
std::cout << std::endl;
34-
35-
// Get all displays information
36-
std::vector<Display> allDisplays = displayManager.GetAll();
37-
std::cout << "All Displays Information:" << std::endl;
38-
for (int i = 0; i < allDisplays.size(); i++) {
39-
Display& display = allDisplays[i];
40-
std::cout << "Display " << (i + 1) << ":" << std::endl;
41-
std::cout << "ID: " << display.id << std::endl;
42-
std::cout << "Name: " << display.name << std::endl;
43-
std::cout << "Resolution: " << display.width << "x" << display.height
44-
<< std::endl;
45-
std::cout << "Scale Factor: " << display.scaleFactor << std::endl;
46-
std::cout << "Visible Position: (" << display.visiblePositionX << ", "
47-
<< display.visiblePositionY << ")" << std::endl;
48-
std::cout << "Visible Size: " << display.visibleSizeWidth << "x"
49-
<< display.visibleSizeHeight << std::endl;
123+
try {
124+
DisplayManager displayManager = DisplayManager();
125+
126+
std::cout << "=== Native API Display Manager Demo ===" << std::endl;
127+
std::cout << std::endl;
128+
129+
// Get and display all displays
130+
std::vector<Display> displays = displayManager.GetAll();
131+
132+
if (displays.empty()) {
133+
std::cerr << "❌ No displays found!" << std::endl;
134+
return 1;
135+
}
136+
137+
std::cout << "📺 Found " << displays.size() << " display(s):" << std::endl;
138+
std::cout << std::endl;
139+
140+
// Display primary display first
141+
Display primaryDisplay = displayManager.GetPrimary();
142+
std::cout << "🌟 PRIMARY DISPLAY:" << std::endl;
143+
printDisplayInfo(primaryDisplay, true);
144+
std::cout << std::endl;
145+
146+
// Display other displays
147+
if (displays.size() > 1) {
148+
std::cout << "📱 SECONDARY DISPLAYS:" << std::endl;
149+
for (const auto& display : displays) {
150+
if (!display.isPrimary) {
151+
printDisplayInfo(display);
152+
std::cout << std::endl;
153+
}
154+
}
155+
}
156+
157+
// Display cursor position
158+
Point cursorPos = displayManager.GetCursorPosition();
159+
std::cout << "🖱️ Current Cursor Position: (" << cursorPos.x << ", " << cursorPos.y << ")" << std::endl;
50160
std::cout << std::endl;
161+
162+
// Display summary statistics
163+
double totalWidth = 0, totalHeight = 0;
164+
double minScaleFactor = displays[0].scaleFactor;
165+
double maxScaleFactor = displays[0].scaleFactor;
166+
167+
for (const auto& display : displays) {
168+
totalWidth += display.size.width;
169+
totalHeight = std::max(totalHeight, display.size.height);
170+
minScaleFactor = std::min(minScaleFactor, display.scaleFactor);
171+
maxScaleFactor = std::max(maxScaleFactor, display.scaleFactor);
172+
}
173+
174+
const int summaryWidth = 60;
175+
std::cout << "📊 SUMMARY:" << std::endl;
176+
std::cout << createTableBorder("", "", "", summaryWidth) << std::endl;
177+
178+
// Format each summary line properly
179+
std::string totalDisplaysStr = "Total Displays: " + std::to_string(displays.size());
180+
std::cout << formatTableRow(totalDisplaysStr, summaryWidth) << std::endl;
181+
182+
std::string combinedWidthStr = "Combined Width: " + std::to_string((int)totalWidth);
183+
std::cout << formatTableRow(combinedWidthStr, summaryWidth) << std::endl;
184+
185+
std::string maxHeightStr = "Max Height: " + std::to_string((int)totalHeight);
186+
std::cout << formatTableRow(maxHeightStr, summaryWidth) << std::endl;
187+
188+
std::stringstream scaleRangeStream;
189+
scaleRangeStream << "Scale Range: " << std::fixed << std::setprecision(1)
190+
<< minScaleFactor << "x - " << maxScaleFactor << "x";
191+
std::string scaleRangeStr = scaleRangeStream.str();
192+
std::cout << formatTableRow(scaleRangeStr, summaryWidth) << std::endl;
193+
194+
std::cout << createTableBorder("", "", "", summaryWidth) << std::endl;
195+
196+
} catch (const std::exception& e) {
197+
std::cerr << "❌ Error: " << e.what() << std::endl;
198+
return 1;
199+
} catch (...) {
200+
std::cerr << "❌ Unknown error occurred" << std::endl;
201+
return 1;
51202
}
52203

53-
// Get cursor position
54-
Point cursorPosition = displayManager.GetCursorPosition();
55-
std::cout << "Current Cursor Position: (" << cursorPosition.x << ", "
56-
<< cursorPosition.y << ")" << std::endl;
204+
std::cout << "\n✅ Display information retrieved successfully!" << std::endl;
57205
return 0;
58206
}

src/display.h

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,47 @@
11
#pragma once
22
#include <string>
3+
#include "geometry.h"
34

45
namespace nativeapi {
56

6-
// Representation of a display
7+
/**
8+
* Display orientation enumeration
9+
*/
10+
enum class DisplayOrientation {
11+
kPortrait = 0,
12+
kLandscape = 90,
13+
kPortraitFlipped = 180,
14+
kLandscapeFlipped = 270
15+
};
16+
17+
/**
18+
* Representation of a display/monitor
19+
*/
720
struct Display {
8-
std::string id;
9-
std::string name;
10-
double width;
11-
double height;
12-
double visiblePositionX;
13-
double visiblePositionY;
14-
double visibleSizeWidth;
15-
double visibleSizeHeight;
16-
double scaleFactor;
21+
// Basic identification
22+
std::string id; // Unique identifier for the display
23+
std::string name; // Human-readable display name
24+
25+
// Physical properties
26+
Point position{0.0, 0.0}; // Display position in virtual desktop coordinates
27+
Size size{0.0, 0.0}; // Full display size in logical pixels
28+
Rectangle workArea{
29+
0.0, 0.0, 0.0,
30+
0.0}; // Available work area (excluding taskbars, docks, etc.)
31+
double scaleFactor =
32+
0.0; // Display scaling factor (1.0 = 100%, 2.0 = 200%, etc.)
33+
34+
// Additional properties
35+
bool isPrimary = false; // Whether this is the primary display
36+
DisplayOrientation orientation =
37+
DisplayOrientation::kPortrait; // Current display orientation
38+
int refreshRate = 0; // Refresh rate in Hz (0 if unknown)
39+
int bitDepth = 0; // Color bit depth (0 if unknown)
40+
41+
// Hardware information
42+
std::string manufacturer; // Display manufacturer
43+
std::string model; // Display model
44+
std::string serialNumber; // Display serial number (if available)
1745
};
1846

19-
} // namespace nativeapi
47+
} // namespace nativeapi

src/display_manager_macos.mm

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
#include "display.h"
88
#include "display_manager.h"
99

10-
// Import Cocoa headers
10+
// Import Cocoa and Core Graphics headers
1111
#import <Cocoa/Cocoa.h>
12+
#import <CoreGraphics/CoreGraphics.h>
1213

1314
namespace nativeapi {
1415

@@ -36,14 +37,55 @@ static Display CreateDisplayFromNSScreen(NSScreen* screen, bool isPrimary) {
3637
}
3738
display.name = [displayName UTF8String];
3839

39-
// Set size and position properties
40-
display.width = frame.size.width;
41-
display.height = frame.size.height;
42-
display.visiblePositionX = visibleFrame.origin.x;
43-
display.visiblePositionY = visibleFrame.origin.y;
44-
display.visibleSizeWidth = visibleFrame.size.width;
45-
display.visibleSizeHeight = visibleFrame.size.height;
40+
// Set position and size using geometry types
41+
display.position = {frame.origin.x, frame.origin.y};
42+
display.size = {frame.size.width, frame.size.height};
43+
display.workArea = {visibleFrame.origin.x, visibleFrame.origin.y,
44+
visibleFrame.size.height, visibleFrame.size.width};
4645
display.scaleFactor = scaleFactor;
46+
display.isPrimary = isPrimary;
47+
48+
// Determine orientation based on dimensions
49+
if (frame.size.width > frame.size.height) {
50+
display.orientation = DisplayOrientation::kLandscape;
51+
} else {
52+
display.orientation = DisplayOrientation::kPortrait;
53+
}
54+
55+
// Try to get refresh rate using Core Graphics
56+
CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode(displayID);
57+
if (displayMode) {
58+
double refreshRate = CGDisplayModeGetRefreshRate(displayMode);
59+
display.refreshRate = refreshRate > 0 ? (int)refreshRate : 60; // Default to 60Hz if unknown
60+
CGDisplayModeRelease(displayMode);
61+
}
62+
63+
// Set default bit depth for modern displays
64+
display.bitDepth = 32;
65+
66+
// Try to get hardware info using newer APIs
67+
// For now, use display name parsing and default values since the old API is deprecated
68+
// This could be enhanced with newer IOKit approaches if needed
69+
NSString* displayNameStr = [NSString stringWithUTF8String:display.name.c_str()];
70+
if ([displayNameStr containsString:@"Apple"]) {
71+
display.manufacturer = "Apple";
72+
} else if ([displayNameStr containsString:@"Dell"]) {
73+
display.manufacturer = "Dell";
74+
} else if ([displayNameStr containsString:@"Samsung"]) {
75+
display.manufacturer = "Samsung";
76+
} else if ([displayNameStr containsString:@"LG"]) {
77+
display.manufacturer = "LG";
78+
} else {
79+
display.manufacturer = "Unknown";
80+
}
81+
82+
// Use display name as model for now
83+
display.model = display.name;
84+
display.serialNumber = ""; // Not easily available without deprecated APIs
85+
86+
// Set default values if hardware info couldn't be retrieved
87+
if (display.manufacturer.empty()) display.manufacturer = "Unknown";
88+
if (display.model.empty()) display.model = "Unknown";
4789

4890
return display;
4991
}

0 commit comments

Comments
 (0)