├── 00readme.txt
├── DisplayX.xcodeproj
└── project.pbxproj
├── README.md
├── appicon.iconset
├── icon_128x128.png
├── icon_128x128@2x.png
├── icon_16x16.png
├── icon_16x16@2x.png
├── icon_256x256.png
├── icon_256x256@2x.png
├── icon_32x32.png
├── icon_32x32@2x.png
├── icon_512x512.png
└── icon_512x512@2x.png
└── source
├── displayxdemo
├── DXDemo-Info.plist
├── DXDemo-Prefix.pch
├── DXDemoAppDelegate.h
├── DXDemoAppDelegate.mm
├── DXDemoInstaller.h
├── DXDemoInstaller.mm
├── DXDemoOpenGLView.h
├── DXDemoOpenGLView.mm
├── com_tsoniq_dxdemo_installer.sh
├── en.lproj
│ ├── Credits.rtf
│ ├── InfoPlist.strings
│ └── MainMenu.xib
├── main.m
├── ts_privilegedscriptrunner.cc
└── ts_privilegedscriptrunner.h
├── displayxfb
├── DisplayXFB-Info.plist
├── DisplayXFB-Prefix.pch
├── DisplayXFBAccelerator.cc
├── DisplayXFBAccelerator.h
├── DisplayXFBDriver.cc
├── DisplayXFBDriver.h
├── DisplayXFBFramebuffer.cc
├── DisplayXFBFramebuffer.h
├── DisplayXFBNames.h
├── DisplayXFBPowerState.cc
├── DisplayXFBPowerState.h
├── DisplayXFBShared.h
├── DisplayXFBTiming.cc
├── DisplayXFBTiming.h
├── DisplayXFBUserClient.cc
├── DisplayXFBUserClient.h
└── en.lproj
│ └── InfoPlist.strings
├── displayxlib
├── DisplayXFBInterface.cc
└── DisplayXFBInterface.h
└── xcconfig
└── DisplayX.xcconfig
/00readme.txt:
--------------------------------------------------------------------------------
1 | DisplayX Virtual Display Software
2 | =================================
3 |
4 |
5 | Licence
6 | -------
7 |
8 | DisplayX may be used freely for research and academic work.
9 |
10 | However, the DisplayX software remains the Copyright of tSoniq. DisplayX may not be used in a commercial
11 | application without prior approval.
12 |
13 | For more information contact support@tsoniq.com or visit http://tsoniq.com.
14 |
15 |
16 |
17 |
18 | Description
19 | -----------
20 |
21 | DisplayX is a virtual display driver for MacOSX, offering the following capabilities:
22 |
23 | - dynamic display configuration and control from the application level
24 | - high performance data capture via Quartz display streaming
25 | - supports MacOSX 10.6 or later
26 | - integrated application installer/uninstaller
27 | - supports multiple display units (via build option)
28 |
29 | The sample application provides a window that shows the content of a virtual display in real-time. To test,
30 | run the application and click 'Connect' to connect the virtual display and shows its contents. Once connected,
31 | the display resolution and arrangement/mirroring can be configured via System Preferences exactly as for a
32 | conventional 2nd physical display.
33 |
34 | Please note that by default Quartz will select the highest permitted resolution when the display is connected.
35 | If this is too high to read successfully (for example, when trying to use System Preferences to set the display
36 | resolution), the resolution can be reset to a default level via the 'Reset Resolution' display command.
37 |
38 | The c++ class DisplayXFBInterface provides the application interface to the display driver. Please see the
39 | doxygen markup for a description of the available methods. Driver configuration and state structures are defined
40 | in DisplayXFBShared.h.
41 |
42 |
43 |
44 |
45 | Driver Installation and Removal
46 | -------------------------------
47 |
48 | The demonstration application includes an example installer and uninstaller. It will prompt to install
49 | and restart the computer if it contains a newer driver than is currently installed.
50 |
51 | To uninstall both driver and application, select "Uninstall..." from the application menu.
52 |
53 |
54 |
55 |
56 |
57 | Building and Code-signing
58 | -------------------------
59 |
60 | The supplied project requires Xcode 5.1 and can be used on MacOSX 10.8 or later.
61 |
62 | The "DXDemo" target in the project will build both the kernel extension and the demonstration application.
63 |
64 | For earlier OS support the Quartz DisplayStream API used by the demonstration application should be replaced with
65 | alternative code that uses direct access to the framebuffer memory. The driver and installation process support
66 | MacOSX 10.6 or later.
67 |
68 | The kernel extension can be code-signed by entering the SHA1 hash for the appropriate certificate
69 | in DisplayX.xcconfig. Without an appropriate certificate the extension will cause security warnings on
70 | installation and any system reboot. The application code-signing can be made in the standard way.
71 |
72 | The virtual display count and per-display VRAM can also be configured in DisplayX.xcconfig.
73 |
74 |
75 |
76 |
77 | About tSoniq
78 | ------------
79 |
80 | tSoniq is a Barcelona based software consultancy specialising in MacOS and iOS application and driver development.
81 |
82 | For more information on what we offer, please visit http://tsoniq.com.
83 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | DisplayX Virtual Display Software
2 | =================================
3 |
4 |
5 | Licence
6 | -------
7 |
8 | DisplayX may be used freely for research and academic work.
9 |
10 | However, the DisplayX software remains the Copyright of tSoniq. DisplayX may not be used in a commercial
11 | application without prior approval.
12 |
13 | For more information contact support@tsoniq.com or visit http://tsoniq.com.
14 |
15 |
16 |
17 |
18 | Description
19 | -----------
20 |
21 | DisplayX is a virtual display driver for MacOSX, offering the following capabilities:
22 |
23 | - dynamic display configuration and control from the application level
24 | - high performance data capture via Quartz display streaming
25 | - supports MacOSX 10.6 or later
26 | - integrated application installer/uninstaller
27 | - supports multiple display units (via build option)
28 |
29 | The sample application provides a window that shows the content of a virtual display in real-time. To test,
30 | run the application and click 'Connect' to connect the virtual display and shows its contents. Once connected,
31 | the display resolution and arrangement/mirroring can be configured via System Preferences exactly as for a
32 | conventional 2nd physical display.
33 |
34 | Please note that by default Quartz will select the highest permitted resolution when the display is connected.
35 | If this is too high to read successfully (for example, when trying to use System Preferences to set the display
36 | resolution), the resolution can be reset to a default level via the 'Reset Resolution' display command.
37 |
38 | The c++ class DisplayXFBInterface provides the application interface to the display driver. Please see the
39 | doxygen markup for a description of the available methods. Driver configuration and state structures are defined
40 | in DisplayXFBShared.h.
41 |
42 |
43 |
44 |
45 | Driver Installation and Removal
46 | -------------------------------
47 |
48 | The demonstration application includes an example installer and uninstaller. It will prompt to install
49 | and restart the computer if it contains a newer driver than is currently installed.
50 |
51 | To uninstall both driver and application, select "Uninstall..." from the application menu.
52 |
53 |
54 |
55 |
56 |
57 | Building and Code-signing
58 | -------------------------
59 |
60 | The supplied project requires Xcode 5.1 and can be used on MacOSX 10.8 or later.
61 |
62 | The "DXDemo" target in the project will build both the kernel extension and the demonstration application.
63 |
64 | For earlier OS support the Quartz DisplayStream API used by the demonstration application should be replaced with
65 | alternative code that uses direct access to the framebuffer memory. The driver and installation process support
66 | MacOSX 10.6 or later.
67 |
68 | The kernel extension can be code-signed by entering the SHA1 hash for the appropriate certificate
69 | in DisplayX.xcconfig. Without an appropriate certificate the extension will cause security warnings on
70 | installation and any system reboot. The application code-signing can be made in the standard way.
71 |
72 | The virtual display count and per-display VRAM can also be configured in DisplayX.xcconfig.
73 |
74 |
75 |
76 |
77 | About tSoniq
78 | ------------
79 |
80 | tSoniq is a Barcelona based consultancy specialising in wireless multimdia, hardware and semiconductor design, together with MacOS and iOS application and driver development.
81 |
82 | For more information on what we offer, please visit http://tsoniq.com.
83 |
--------------------------------------------------------------------------------
/appicon.iconset/icon_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tSoniq/displayx/c55eb85194a474bf4721889e5c50bcca4df61b3a/appicon.iconset/icon_128x128.png
--------------------------------------------------------------------------------
/appicon.iconset/icon_128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tSoniq/displayx/c55eb85194a474bf4721889e5c50bcca4df61b3a/appicon.iconset/icon_128x128@2x.png
--------------------------------------------------------------------------------
/appicon.iconset/icon_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tSoniq/displayx/c55eb85194a474bf4721889e5c50bcca4df61b3a/appicon.iconset/icon_16x16.png
--------------------------------------------------------------------------------
/appicon.iconset/icon_16x16@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tSoniq/displayx/c55eb85194a474bf4721889e5c50bcca4df61b3a/appicon.iconset/icon_16x16@2x.png
--------------------------------------------------------------------------------
/appicon.iconset/icon_256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tSoniq/displayx/c55eb85194a474bf4721889e5c50bcca4df61b3a/appicon.iconset/icon_256x256.png
--------------------------------------------------------------------------------
/appicon.iconset/icon_256x256@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tSoniq/displayx/c55eb85194a474bf4721889e5c50bcca4df61b3a/appicon.iconset/icon_256x256@2x.png
--------------------------------------------------------------------------------
/appicon.iconset/icon_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tSoniq/displayx/c55eb85194a474bf4721889e5c50bcca4df61b3a/appicon.iconset/icon_32x32.png
--------------------------------------------------------------------------------
/appicon.iconset/icon_32x32@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tSoniq/displayx/c55eb85194a474bf4721889e5c50bcca4df61b3a/appicon.iconset/icon_32x32@2x.png
--------------------------------------------------------------------------------
/appicon.iconset/icon_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tSoniq/displayx/c55eb85194a474bf4721889e5c50bcca4df61b3a/appicon.iconset/icon_512x512.png
--------------------------------------------------------------------------------
/appicon.iconset/icon_512x512@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tSoniq/displayx/c55eb85194a474bf4721889e5c50bcca4df61b3a/appicon.iconset/icon_512x512@2x.png
--------------------------------------------------------------------------------
/source/displayxdemo/DXDemo-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIconFile
10 | appicon
11 | CFBundleIdentifier
12 | com.tsoniq.application.${PRODUCT_NAME:rfc1034identifier}
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | ${PRODUCT_NAME}
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSMinimumSystemVersion
26 | ${MACOSX_DEPLOYMENT_TARGET}
27 | NSMainNibFile
28 | MainMenu
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/source/displayxdemo/DXDemo-Prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #endif
4 |
--------------------------------------------------------------------------------
/source/displayxdemo/DXDemoAppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // DXDemoAppDelegate.mm
3 | //
4 | // Copyright 2010 tSoniq. All rights reserved.
5 | //
6 |
7 | #import
8 | #import "DXDemoInstaller.h"
9 | #import "DXDemoOpenGLView.h"
10 | #import "DisplayXFBInterface.h"
11 |
12 | @interface DXDemoAppDelegate : NSObject
13 | {
14 | IBOutlet NSWindow* _window;
15 | IBOutlet DXDemoOpenGLView* _openGLView;
16 | IBOutlet NSButton* _connectButton;
17 | IBOutlet NSButton* _disconnectButton;
18 |
19 | ts::DisplayXFBInterface* _displayInterface; //!< The virtual display interface (framebuffer)
20 | unsigned _visibleDisplayIndex; //!< The number of the current display
21 | CGDisplayStreamRef _displayStream; //!< The display stream of null if stopped
22 | ts::DisplayXFBConfiguration _configuration[ts::kDisplayXFBMaxDisplays]; //!< The display configuration
23 | const uint8_t* _displayMemory[ts::kDisplayXFBMaxDisplays]; //!< Mapped display
24 | const ts::DisplayXFBCursor* _cursor[ts::kDisplayXFBMaxDisplays]; //!< Cursor data
25 | }
26 |
27 | - (IBAction)actionResetResolution:(id)sender;
28 | - (IBAction)actionRemove:(id)sender;
29 | - (IBAction)actionConnect:(id)sender;
30 | - (IBAction)actionDisconnect:(id)sender;
31 | - (IBAction)actionSelectDisplay:(id)sender;
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/source/displayxdemo/DXDemoAppDelegate.mm:
--------------------------------------------------------------------------------
1 | //
2 | // DXDemoAppDelegate.mm
3 | //
4 | // Copyright 2010 tSoniq. All rights reserved.
5 | //
6 |
7 | #include
8 | #include
9 | #import "DXDemoInstaller.h"
10 | #import "DXDemoAppDelegate.h"
11 |
12 | using namespace ts;
13 |
14 | @interface DXDemoAppDelegate (Private)
15 | - (void)debugLogCurrentDisplays;
16 | - (BOOL)isDisplayStreamEnabled;
17 | - (void)disableDisplayStream;
18 | - (BOOL)enableDisplayStream;
19 | - (void)updateControlState;
20 | - (void)notificationDisplayState:(unsigned)displayIndex;
21 | - (void)notificationCursorState:(unsigned)displayIndex;
22 | - (void)notificationCursorImage:(unsigned)displayIndex;
23 | - (void)notificationDisplayReconfiguration:(CGDirectDisplayID)displayID flags:(CGDisplayChangeSummaryFlags)flags;
24 | @end
25 |
26 |
27 |
28 |
29 | @implementation DXDemoAppDelegate
30 |
31 |
32 | /** The display event handler. This is the callback issued on any display event from the driver.
33 | *
34 | * These are notification issued by the display driver. Be aware that display state changes may be issued
35 | * the Quartz layer internally has processed these. Therefore use the Quartz display configuration callback
36 | * to detect events that require new Quartz objects to be constructed (for example, the display stream).
37 | */
38 | static void displayNotificationCallback(DisplayXFBInterface::Notification notification, unsigned displayIndex, void* context)
39 | {
40 | DXDemoAppDelegate* object = (__bridge DXDemoAppDelegate*)context;
41 |
42 | switch (notification)
43 | {
44 | case DisplayXFBInterface::kNotificationDisplayState: [object notificationDisplayState:displayIndex]; break;
45 | case DisplayXFBInterface::kNotificationCursorState: [object notificationCursorState:displayIndex]; break;
46 | case DisplayXFBInterface::kNotificationCursorImage: [object notificationCursorImage:displayIndex]; break;
47 | default: NSLog(@">>> Display %d unknown event %d", displayIndex, (int)notification); break;
48 | }
49 | }
50 |
51 |
52 | /** Display reconfiguration notification handler.
53 | *
54 | * This is used by Quartz to notify display events.
55 | */
56 | static void displayReconfigurationCallBack(CGDirectDisplayID displayID, CGDisplayChangeSummaryFlags flags, void* context)
57 | {
58 | DXDemoAppDelegate* object = (__bridge DXDemoAppDelegate*)context;
59 | [object notificationDisplayReconfiguration:displayID flags:flags];
60 | }
61 |
62 |
63 |
64 |
65 |
66 | - (id)init
67 | {
68 | if ((self = [super init]))
69 | {
70 | _displayInterface = new DisplayXFBInterface;
71 | _visibleDisplayIndex = 0;
72 | }
73 | return self;
74 | }
75 |
76 |
77 | - (void)dealloc
78 | {
79 | CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallBack, (__bridge void*)self);
80 |
81 | delete _displayInterface;
82 | _displayInterface = 0;
83 | }
84 |
85 |
86 |
87 | - (void)applicationDidFinishLaunching:(NSNotification*)aNotification
88 | {
89 | [self debugLogCurrentDisplays];
90 |
91 |
92 | // Check for an perform installation if reqiured.
93 | if ([DXDemoInstaller needsInstall])
94 | {
95 | if ([DXDemoInstaller doInstall]) [DXDemoInstaller doQuitAndRestart];
96 | else [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
97 | return;
98 | }
99 |
100 |
101 | // Set up the display(s).
102 | bool ok = _displayInterface->open();
103 | if (!ok)
104 | {
105 | NSLog(@"Unable to open the display interface - possible driver installation problem?");
106 | }
107 | else
108 | {
109 | // Map the display objects.
110 | for (unsigned i = 0; i != kDisplayXFBMaxDisplays; ++i)
111 | {
112 | DisplayXFBMap map;
113 | _displayInterface->displayMapFramebuffer(map, i, true);
114 | _displayMemory[i] = (const uint8_t*)map.address();
115 | _displayInterface->displayMapCursor(map, i, true);
116 | _cursor[i] = (const DisplayXFBCursor*)map.address();
117 | }
118 |
119 | // Setup the display configuration.
120 | // If the display is connected, we read whatever it was previously configured with.
121 | // If the display is not connected, we re-program it with a set of example resolutions.
122 | // This is not necessarily the best approach in a real application - it is shown here as
123 | // a demonstration of two possible ways of working with the display setup (use what you find
124 | // vs reprogramming the display modes).
125 | for (unsigned i = 0; i < _displayInterface->displayCount(); i++)
126 | {
127 | if (_displayInterface->displayIsConnected(i))
128 | {
129 | _displayInterface->displayGetConfiguration(_configuration[i], i);
130 | }
131 | else
132 | {
133 | char buffer[32];
134 | snprintf(buffer, sizeof buffer, "Display %u", i);
135 |
136 | // Construct a display.
137 | _configuration[i].appendMode(1280, 800, true);
138 | _configuration[i].appendMode(1440, 900);
139 | _configuration[i].appendMode(1600, 900);
140 | _configuration[i].appendMode(1920, 1080);
141 | _configuration[i].appendMode(1920, 1200);
142 | _configuration[i].appendMode(2560, 1440);
143 | _configuration[i].appendMode(2560, 1600);
144 | _configuration[i].appendMode(2880, 1800);
145 | _configuration[i].setRefreshRate(0.0);
146 | _configuration[i].setName(buffer);
147 |
148 | bool ok = _displayInterface->displaySetConfiguration(_configuration[i], i);
149 | if (!ok) NSLog(@"Warning: failed to set configuration for display %u", i);
150 | }
151 | }
152 |
153 | // Setup notification callbacks from the driver.
154 | _displayInterface->setNotificationHandler(displayNotificationCallback, (__bridge void*)self);
155 |
156 |
157 | // Setup reconfiguration callbacks from Quartz.
158 | CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallBack, (__bridge void*)self);
159 | }
160 |
161 |
162 | [self updateControlState];
163 |
164 | // If the display was connected at startup, start displaying the content.
165 | if (_displayInterface->displayIsConnected(_visibleDisplayIndex))
166 | {
167 | [self enableDisplayStream];
168 | }
169 | }
170 |
171 |
172 | - (IBAction)actionResetResolution:(id)sender
173 | {
174 | // Get the current list of displays.
175 | uint32_t activeDisplayCount = 0;
176 | CGDirectDisplayID activeDisplays[64];
177 |
178 | CGError result = CGGetActiveDisplayList(sizeof activeDisplays / sizeof activeDisplays[0],activeDisplays, &activeDisplayCount);
179 | if (result != kCGErrorSuccess)
180 | {
181 | NSLog(@"Unable to get active display list");
182 | return;
183 | }
184 |
185 | // Find the display ID for the current screen.
186 | for (unsigned i = 0; i != activeDisplayCount; i++)
187 | {
188 | unsigned targetWidth = _configuration[_visibleDisplayIndex].defaultMode().width();
189 | unsigned targetHeight = _configuration[_visibleDisplayIndex].defaultMode().height();
190 | CGDirectDisplayID did = activeDisplays[i];
191 | uint32_t manufacturer = CGDisplayVendorNumber(did);
192 | uint32_t serial = CGDisplaySerialNumber(did);
193 | NSLog(@"ID %08x: mfgr %08x serial %08x", (unsigned)did, (unsigned)manufacturer, (unsigned)serial);
194 | if (manufacturer == kDisplayXManufacturer)
195 | {
196 | CFArrayRef allModes = CGDisplayCopyAllDisplayModes(did, NULL);
197 | if (!allModes) continue;
198 | if (0 != CFArrayGetCount(allModes))
199 | {
200 | CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, 0);
201 | for (unsigned j = 0 ; j < CFArrayGetCount(allModes); j++)
202 | {
203 | CGDisplayModeRef thisMode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, j);
204 | if (CGDisplayModeGetWidth(thisMode) == targetWidth && CGDisplayModeGetHeight(thisMode) == targetHeight)
205 | {
206 | mode = thisMode;
207 | break;
208 | }
209 | }
210 | CGDisplayConfigRef config;
211 | CGError rc = CGBeginDisplayConfiguration(&config);
212 | if (rc == kCGErrorSuccess)
213 | {
214 | CGConfigureDisplayWithDisplayMode(config, did, mode, NULL);
215 | CGCompleteDisplayConfiguration(config, kCGConfigureForSession);
216 | }
217 | }
218 | CFRelease(allModes);
219 | }
220 | }
221 | }
222 |
223 |
224 | /** Connect the virtual display. Streaming updates will be started when the display is available.
225 | */
226 | - (IBAction)actionConnect:(id)sender
227 | {
228 | (void)sender;
229 |
230 | if (!_displayInterface->isOpen()) NSLog(@"Connect: framebuffer interface not open");
231 | else _displayInterface->displayConnect(_visibleDisplayIndex);
232 | // Do not call [self enableDisplayStream] here. The display connection takes some time for the system to
233 | // notice, and the display stream setup will be potentially broken if it is set up immediately after displayConnect().
234 | // Instead, streaming should be re-initialised in the Quartz reconfiguration callback.
235 | [self updateControlState];
236 | }
237 |
238 |
239 | /** Disconnect the virtual display.
240 | */
241 | - (IBAction)actionDisconnect:(id)sender
242 | {
243 | (void)sender;
244 |
245 |
246 | if (!_displayInterface->isOpen()) NSLog(@"Disconnect: framebuffer interface not open");
247 | else _displayInterface->displayDisconnect(_visibleDisplayIndex);
248 | [self disableDisplayStream];
249 | [self updateControlState];
250 | }
251 |
252 |
253 | /** Uninstall the driver and application.
254 | */
255 | - (IBAction)actionRemove:(id)sender
256 | {
257 | (void)sender;
258 | if ([DXDemoInstaller doRemove]) [DXDemoInstaller doQuitAndRestart];
259 | }
260 |
261 |
262 | /** Perform a single display capture.
263 | */
264 | - (IBAction)actionCapture:(id)sender
265 | {
266 | (void)sender;
267 |
268 | // Manually capture the raw data
269 | if (_displayInterface->isOpen())
270 | {
271 | DisplayXFBState state;
272 | _displayInterface->displayGetState(state, _visibleDisplayIndex);
273 |
274 | // Render the texture
275 | [_openGLView setDesktop:(uint32_t*)_displayMemory[_visibleDisplayIndex] width:state.width() height:state.height()];
276 |
277 | // Render the cursor
278 | const ts::DisplayXFBCursor* crsr = _cursor[_visibleDisplayIndex];
279 | [_openGLView setCursor:(uint32_t*)crsr->pixelData() width:crsr->width() height:crsr->height() x:crsr->x() y:crsr->y() isVisible:crsr->isVisible()];
280 | }
281 | [self updateControlState];
282 | }
283 |
284 |
285 | /** Select the display to display. The UI element's tag should contain the virtual display index.
286 | */
287 | - (IBAction)actionSelectDisplay:(id)sender
288 | {
289 | unsigned newDisplayIndex = (unsigned)[sender tag];
290 | if (newDisplayIndex != _visibleDisplayIndex)
291 | {
292 | [self disableDisplayStream];
293 |
294 | _visibleDisplayIndex = newDisplayIndex;
295 |
296 | if (_displayInterface->displayIsConnected(_visibleDisplayIndex))
297 | {
298 | [self enableDisplayStream];
299 | }
300 |
301 | [self updateControlState];
302 | }
303 | }
304 |
305 |
306 | @end
307 |
308 |
309 |
310 |
311 |
312 | @implementation DXDemoAppDelegate (Private)
313 |
314 |
315 | /** Display on the console information about the currently available displays.
316 | */
317 | - (void)debugLogCurrentDisplays
318 | {
319 | uint32_t allocDisplayCount = 0;
320 | CGError result = CGGetActiveDisplayList(0, NULL, &allocDisplayCount);
321 | if (result != kCGErrorSuccess && allocDisplayCount == 0)
322 | {
323 | NSLog(@"No displays currently active?");
324 | }
325 | else
326 | {
327 | uint32_t activeDisplayCount = 0;
328 | CGDirectDisplayID* activeDisplays = (CGDirectDisplayID*)calloc(sizeof (CGDirectDisplayID), allocDisplayCount);
329 | if (!activeDisplays)
330 | {
331 | NSLog(@"No memory for %u display IDs?", (unsigned)allocDisplayCount);
332 | }
333 | else
334 | {
335 | result = CGGetActiveDisplayList(allocDisplayCount, activeDisplays, &activeDisplayCount);
336 | if (result != kCGErrorSuccess)
337 | {
338 | NSLog(@"Error reading display IDs?");
339 | }
340 | else
341 | {
342 | // Find the display ID for the current screen.
343 | // The display exports EDID data with a custom manufacturer and the displayIndex value as the serial number.
344 | for (unsigned i = 0; i != activeDisplayCount; ++i)
345 | {
346 | CGDirectDisplayID displayID = activeDisplays[i];
347 |
348 | uint32_t manufacturer = CGDisplayVendorNumber(displayID);
349 | char mstring[4];
350 | mstring[0] = ((manufacturer >> 10) & 31) + 'A' - 1;
351 | mstring[1] = ((manufacturer >> 5) & 31) + 'A' - 1;
352 | mstring[2] = ((manufacturer >> 0) & 31) + 'A' - 1;
353 | mstring[3] = 0;
354 |
355 | uint32_t serial = CGDisplaySerialNumber(displayID);
356 |
357 | unsigned index = 0;
358 | if (DisplayXFBInterface::displayIDToIndex(index, displayID))
359 | {
360 | NSLog(@"Display #%u: ident %08x, manufacturer %s, virtual display index %u", i, (unsigned)displayID, mstring, index);
361 | }
362 | else
363 | {
364 | NSLog(@"Display #%u: ident %08x, manufacturer %s, serial %u", i, (unsigned)displayID, mstring, (unsigned)serial);
365 | }
366 | }
367 | }
368 | free(activeDisplays);
369 | }
370 | }
371 |
372 | }
373 |
374 |
375 |
376 | - (BOOL)isDisplayStreamEnabled
377 | {
378 | return (_displayStream != NULL);
379 | }
380 |
381 |
382 |
383 | - (void)disableDisplayStream
384 | {
385 | if (_displayStream)
386 | {
387 | CFRelease(_displayStream);
388 | _displayStream = 0;
389 | }
390 | }
391 |
392 |
393 |
394 |
395 | - (BOOL)enableDisplayStream
396 | {
397 | [self disableDisplayStream];
398 |
399 | // Set up the display stream notification callbacks.
400 | CGDirectDisplayID displayID;
401 | if (DisplayXFBInterface::displayIndexToID(displayID, _visibleDisplayIndex))
402 | {
403 | unsigned width = (unsigned)CGDisplayPixelsWide(displayID);
404 | unsigned height = (unsigned)CGDisplayPixelsHigh(displayID);
405 | const void* keys[1] = { kCGDisplayStreamSourceRect };
406 | const void* values[1] = { CGRectCreateDictionaryRepresentation(CGRectMake(0, 0, width, height)) };
407 |
408 | CFDictionaryRef properties = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
409 |
410 | _displayStream = CGDisplayStreamCreate(
411 | displayID,
412 | width,
413 | height,
414 | '420f',
415 | properties,
416 | ^(CGDisplayStreamFrameStatus status, uint64_t displayTime, IOSurfaceRef frameSurface, CGDisplayStreamUpdateRef updateRef)
417 | {
418 | // This is a very simplistic use of the display-stream API, refreshing the entire display content on
419 | // each update. In practise, it is possible to get significant performance improvements by peforming
420 | // selective updates.
421 | [self actionCapture:nil];
422 |
423 | /*
424 | size_t rectCount = 0;
425 | const CGRect* rect = CGDisplayStreamUpdateGetRects(updateRef, kCGDisplayStreamUpdateDirtyRects, &rectCount);
426 | NSLog(@"Notify: %16llx %u", (unsigned long long)displayTime, (unsigned)rectCount);
427 | while (rectCount != 0)
428 | {
429 | NSLog(@" %f %f : %f %f", rect->origin.x, rect->origin.y, rect->size.width, rect->size.height);
430 | ++ rect;
431 | -- rectCount;
432 | }
433 | */
434 | }
435 | );
436 | }
437 |
438 |
439 | if (_displayStream)
440 | {
441 | CFRunLoopSourceRef runLoopSource = CGDisplayStreamGetRunLoopSource(_displayStream);
442 | CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
443 |
444 | CGError err = CGDisplayStreamStart(_displayStream);
445 | if (err != CGDisplayNoErr)
446 | {
447 | NSLog(@"Error %u starting display stream", (unsigned)err);
448 | CFRelease(_displayStream);
449 | _displayStream = 0;
450 | }
451 | }
452 |
453 | return (0 != _displayStream);
454 | }
455 |
456 |
457 |
458 | - (void)updateControlState
459 | {
460 | // Set the button enables and status field(s) based on current state.
461 | BOOL enableConnect = FALSE;
462 | BOOL enableDisconnect = FALSE;
463 | NSString* statusText = @"Failed";
464 |
465 | DisplayXFBState state;
466 |
467 | if (![self isDisplayStreamEnabled])
468 | {
469 | [_openGLView setBlank];
470 | }
471 |
472 | if (!_displayInterface->displayGetState(state, _visibleDisplayIndex))
473 | {
474 | statusText = @"Failed to get display state";
475 | }
476 | else if (state.isConnected())
477 | {
478 | enableDisconnect = TRUE;
479 | statusText = [NSString stringWithFormat:@"%u x %u", (unsigned)state.width(), (unsigned)state.height()];
480 | }
481 | else
482 | {
483 | enableConnect = TRUE;
484 | statusText = @"Disconnected";
485 | }
486 |
487 | [_connectButton setEnabled:enableConnect];
488 | [_disconnectButton setEnabled:enableDisconnect];
489 |
490 | NSString* windowTitle = [NSString stringWithFormat:@"Display:%u -- %@ -- %@",
491 | (unsigned)_visibleDisplayIndex,
492 | statusText,
493 | (_displayStream) ? @"Running" : @"Stopped"];
494 |
495 | [_window setTitle:windowTitle];
496 | }
497 |
498 |
499 |
500 | - (void)notificationDisplayState:(unsigned)displayIndex
501 | {
502 | [self actionCapture:nil];
503 | [self updateControlState];
504 | }
505 |
506 |
507 | - (void)notificationCursorState:(unsigned)displayIndex
508 | {
509 | const DisplayXFBCursor* crsr = _cursor[_visibleDisplayIndex];
510 | [_openGLView setCursor:(uint32_t*)crsr->pixelData() width:crsr->width() height:crsr->height() x:crsr->x() y:crsr->y() isVisible:crsr->isVisible()];
511 | }
512 |
513 |
514 | - (void)notificationCursorImage:(unsigned)displayIndex
515 | {
516 | const DisplayXFBCursor* crsr = _cursor[_visibleDisplayIndex];
517 | [_openGLView setCursor:(uint32_t*)crsr->pixelData() width:crsr->width() height:crsr->height() x:crsr->x() y:crsr->y() isVisible:crsr->isVisible()];
518 | }
519 |
520 |
521 | - (void)notificationDisplayReconfiguration:(CGDirectDisplayID)displayID flags:(CGDisplayChangeSummaryFlags)flags
522 | {
523 | unsigned displayIndex;
524 | bool isVirtualDisplay = _displayInterface->displayIDToIndex(displayIndex, displayID);
525 | if (isVirtualDisplay && displayIndex == _visibleDisplayIndex)
526 | {
527 | // This is a reconfiguration notification for the display that we are currently showing.
528 | if (0 != (flags & kCGDisplayBeginConfigurationFlag))
529 | {
530 | // Starting a re-configure.
531 | [self disableDisplayStream];
532 | }
533 | else if (0 == (flags & kCGDisplayRemoveFlag) && 0 == (flags & kCGDisplayDisabledFlag))
534 | {
535 | // Not removed or disabled, so try to (re)start the stream.
536 | [self enableDisplayStream];
537 | }
538 | else
539 | {
540 | // Being removed or disabled.
541 | [self disableDisplayStream];
542 | }
543 |
544 |
545 | [self updateControlState];
546 | }
547 | }
548 |
549 |
550 | @end
551 |
552 |
553 |
554 |
555 |
--------------------------------------------------------------------------------
/source/displayxdemo/DXDemoInstaller.h:
--------------------------------------------------------------------------------
1 | /** @file
2 | * @brief Class used to manage driver and/or application installation and uninstallation.
3 | * @copyright Copyright (c) 2012 tSoniq. All rights reserved.
4 | */
5 | #import
6 |
7 | /** This is a Cocoa class used to assist application installation and removal.
8 | *
9 | * Although the application itself is installed by drag-and-drop, additional
10 | * steps are required to install the kernel driver or to uninstall the application.
11 | * This class provides a simple API to perform these tasks.
12 | *
13 | * To install:
14 | *
15 | * if ([DXDemoInstaller needsInstall])
16 | * {
17 | * if (![DXDemoInstaller doInstall]) { handle-failed-install; }
18 | * else if (![DXDemoInstaller waitInstall:nil]) { handle-driver-not-functional; }
19 | * }
20 | *
21 | * To uninstall:
22 | *
23 | * if ([DXDemoInstaller doRemove])
24 | * {
25 | * [NSApp terminate:self];
26 | * exit(0);
27 | * }
28 | *
29 | * Note that the installer is is split in to needsInstall, doInstall and waitInstall to allow a UI
30 | * status display updates (if desired). The installer methods will provide any necessary prompts
31 | * using modal dialogues where necessary.
32 | */
33 | @interface DXDemoInstaller : NSObject
34 |
35 |
36 | /** Quit the current application, requesting that the Finder perform a restart.
37 | */
38 | + (void)doQuitAndRestart;
39 |
40 |
41 | /** Test if a call to doInstall is required.
42 | *
43 | * @return Logical true if a new installation should be performed.
44 | *
45 | * This will test if the driver is currently installed. If not, use doInstall to install it.
46 | */
47 | + (BOOL)needsInstall;
48 |
49 |
50 | /** Install the kernel extension.
51 | *
52 | * @return Logical true for success, else the installation is not valid.
53 | *
54 | * This will test if the driver is currently installed. If not, it will automatically prompt the user and perform
55 | * a new installation. This method does nothing (and will not prompt the user) if the driver is already installed
56 | * and functional.
57 | *
58 | * It is recommended that application code call this method once each time it starts, once the UI is available.
59 | * If false is returned, either the driver is incompatible with the host computer, or the user did not authorise
60 | * its installation. In this case the application should exit.
61 | *
62 | * See the doInstall() method in the install script for the underlying implementation.
63 | *
64 | * After calling this method, the client should call waitInstall to delay until the driver is available.
65 | * Although this method returns as soon as the driver is installed and loaded, the OS appears to need
66 | * several seconds to make the driver available to applications, and attempting to use the driver immediately
67 | * after doInstall may fail.
68 | */
69 | + (BOOL)doInstall;
70 |
71 |
72 | /** Remove the kernel extension and move the current application to the trash.
73 | *
74 | * @return Logical true for success, false if uninstallation failed for any reason (such as the user saying "don't").
75 | *
76 | * This will test if the driver is currently installed. If it is, it will (may) automatically prompt the user before
77 | * removing the files and moving the application to the trash.
78 | *
79 | * This method may prompt the user if authorisation is required.
80 | *
81 | * See the doRemove() method in install script for the underlying implementation. Note that the
82 | * kextunload call in the script may fail if the client is still holding the driver open at the time that doRemove
83 | * is called. In this case, the driver will remain loaded until the next reboot (but the uninstall should otherwise
84 | * complete correctly).
85 | */
86 | + (BOOL)doRemove;
87 |
88 | @end
89 |
--------------------------------------------------------------------------------
/source/displayxdemo/DXDemoInstaller.mm:
--------------------------------------------------------------------------------
1 | /** @file
2 | * @brief Class used to manage driver and/or application installation and uninstallation.
3 | * @copyright Copyright (c) 2012 tSoniq. All rights reserved.
4 | */
5 |
6 | #import
7 | #import
8 | #import "ts_privilegedscriptrunner.h"
9 | #import "DisplayXFBInterface.h"
10 | #import "DXDemoInstaller.h"
11 |
12 |
13 |
14 | #define kInstallerScriptName "com_tsoniq_dxdemo_installer.sh" //!< The filename of the install script (in the app's "Resources" folder).
15 |
16 |
17 |
18 | @interface DXDemoInstaller (Private)
19 | + (NSInteger)modalAlert:(NSString*)title info:(NSString*)info style:(NSAlertStyle)style button0:(NSString*)bitle0 button1:(NSString*)bitle1;
20 | @end
21 |
22 |
23 |
24 | @implementation DXDemoInstaller
25 |
26 |
27 | /** Method called to present a modal alert.
28 | *
29 | * @private
30 | * @param title The alert title.
31 | * @param info The informational text.
32 | * @param style The alert style (NSWarningAlertStyle, NSInformationalAlertStyle or NSCriticalAlertStyle)
33 | * @param bitle0 The title for button #0
34 | * @param bitle1 The title for button #1 (or nil for none)
35 | * @return The index of the button that was clicked to dismiss the alert.
36 | */
37 | + (NSInteger)modalAlert:(NSString*)title info:(NSString*)info style:(NSAlertStyle)style button0:(NSString*)bitle0 button1:(NSString*)bitle1
38 | {
39 | NSAlert* alert = [[NSAlert alloc] init];
40 |
41 | [alert setMessageText:title];
42 | [alert setInformativeText:info];
43 | if (bitle0) [alert addButtonWithTitle:bitle0];
44 | if (bitle1) [alert addButtonWithTitle:bitle1];
45 | [alert setAlertStyle:style];
46 |
47 | return [alert runModal];
48 | }
49 |
50 |
51 |
52 | + (void)doQuitAndRestart
53 | {
54 | // Calling this method will ask the Finder to restart the computer and quite the current application.
55 | NSString *scriptAction = @"restart";
56 | NSString *scriptSource = [NSString stringWithFormat:@"tell application \"Finder\" to %@", scriptAction];
57 | NSAppleScript *appleScript = [[NSAppleScript alloc] initWithSource:scriptSource];
58 | NSDictionary *errDict = nil;
59 | if (![appleScript executeAndReturnError:&errDict])
60 | {
61 | NSLog(@"%@", errDict);
62 | }
63 | [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
64 | }
65 |
66 |
67 |
68 | + (BOOL)needsInstall
69 | {
70 | return !ts::DisplayXFBInterface::isInstalled();
71 | }
72 |
73 |
74 |
75 | + (BOOL)doInstall
76 | {
77 | ts::PrivilegedScriptRunner script(kInstallerScriptName);
78 |
79 | BOOL shouldQuit = NO;
80 | BOOL shouldInstall = NO;
81 |
82 | bool isInstalled = ts::DisplayXFBInterface::isInstalled();
83 | bool isCompatible = true;
84 | bool isOlder = false;
85 |
86 | if (!isInstalled)
87 | {
88 | NSInteger option = [DXDemoInstaller
89 | modalAlert:@"Driver installation required"
90 | info:@"The software needs authorisation to install a kernel driver in order to function.\n\n"
91 | "After a successful installation the computer will automatically restart.\n\n"
92 | "Select 'Install' to proceed or 'Quit' to exit the application without installing.\n\n"
93 | style:NSInformationalAlertStyle
94 | button0:@"Install"
95 | button1:@"Quit"];
96 | if (NSAlertFirstButtonReturn == option) shouldInstall = YES;
97 | else shouldQuit = YES;
98 | }
99 | else if (!isCompatible)
100 | {
101 | NSInteger option;
102 | if (isOlder)
103 | {
104 | option = [DXDemoInstaller
105 | modalAlert:@"Older driver installed"
106 | info:@"An older version of the kernel driver needed by this application is already installed.\n\n"
107 | "Click 'Upgrade' to replace it with the version from this application "
108 | "or 'Quit' to exit, leaving the currently installed files untouched."
109 | style:NSInformationalAlertStyle
110 | button0:@"Upgrade"
111 | button1:@"Quit"];
112 | }
113 | else
114 | {
115 | option = [DXDemoInstaller
116 | modalAlert:@"Newer driver installed"
117 | info:@"An newer version of the kernel driver needed by this application is already installed.\n\n"
118 | "Click 'Downgrade' to replace it with the version from this application "
119 | "or 'Quit' to exit, leaving the currently installed files untouched."
120 | style:NSInformationalAlertStyle
121 | button0:@"Downgrade"
122 | button1:@"Quit"];
123 | }
124 | if (NSAlertFirstButtonReturn == option) shouldInstall = YES;
125 | else shouldQuit = YES;
126 | }
127 | else if (isOlder)
128 | {
129 | NSInteger option = [DXDemoInstaller
130 | modalAlert:@"Older driver installed"
131 | info:@"An older version of the kernel driver needed by this application is already installed.\n\n"
132 | "Click 'Upgrade' to replace it with the version from this application "
133 | "or 'Continue' to continue using the existing installed files."
134 | style:NSInformationalAlertStyle
135 | button0:@"Upgrade"
136 | button1:@"Continue"];
137 | if (NSAlertFirstButtonReturn == option) shouldInstall = YES;
138 | else shouldQuit = NO;
139 | }
140 |
141 | if (shouldQuit) return FALSE;
142 |
143 | if (shouldInstall)
144 | {
145 | while (!script.authorise())
146 | {
147 | NSInteger option = [DXDemoInstaller
148 | modalAlert:@"Authorisation Failed"
149 | info:@"Select 'Try Again' to re-try the authorisation, or 'Quit' to close the application without installing."
150 | style:NSInformationalAlertStyle
151 | button0:@"Try Again"
152 | button1:@"Quit"];
153 | if (NSAlertSecondButtonReturn == option) break;
154 | }
155 |
156 | if (!script.isAuthorised()) return FALSE;
157 |
158 | // Uncomment for debug: script.setRedirect("/tmp/dfu_install_stdout.txt", "/tmp/dfu_install_stderr.txt");
159 | script.setRedirect("/tmp/dxdemo_install_stdout.txt", "/tmp/dxdemo_install_stderr.txt");
160 | if (script.start("install", "#$APPLICATION_PATH", "#$RESOURCE_PATH", "#$=USER", "#$=HOME"))
161 | {
162 | script.wait();
163 | }
164 | else
165 | {
166 | [DXDemoInstaller
167 | modalAlert:@"Installer Failure"
168 | info:@"The kernel extension could not be installed on this computer."
169 | "Please contact support for assistance."
170 | style:NSInformationalAlertStyle
171 | button0:@"Quit"
172 | button1:nil];
173 | return FALSE;
174 | }
175 | }
176 |
177 | // If we got here, the driver should be installed and working. If may take
178 | // a short time from the kextload performed by the installer before the
179 | // service is publically accessible.
180 | return TRUE;
181 | }
182 |
183 |
184 |
185 |
186 |
187 | + (BOOL)doRemove
188 | {
189 | ts::PrivilegedScriptRunner script(kInstallerScriptName);
190 |
191 | NSInteger option = [DXDemoInstaller
192 | modalAlert:@"Uninstall this software?"
193 | info:@"Select 'Cancel' to continue without uninstalling, or 'Uninstall' to remove any installed drivers and move this application to the trash.\n\n"
194 | "After a successful uninstall the computer will automatically restart.\n\n"
195 | style:NSInformationalAlertStyle
196 | button0:@"Cancel"
197 | button1:@"Uninstall"];
198 | if (NSAlertFirstButtonReturn == option) return FALSE;
199 |
200 | if (!script.isAuthorised())
201 | {
202 | while (!script.authorise())
203 | {
204 | NSInteger option = [DXDemoInstaller
205 | modalAlert:@"Authorisation for Uninstall Failed"
206 | info:@"Select 'Try Again' to re-try the authorisation, or 'Cancel' to continue without uninstalling."
207 | style:NSInformationalAlertStyle
208 | button0:@"Try Again"
209 | button1:@"Cancel"];
210 | if (NSAlertSecondButtonReturn == option) return FALSE;
211 | }
212 | }
213 |
214 | // Uncomment for debug: script.setRedirect("/tmp/dfu_install_stdout.txt", "/tmp/dfu_install_stderr.txt");
215 | script.setRedirect("/tmp/dfu_install_stdout.txt", "/tmp/dfu_install_stderr.txt");
216 | if (script.start("remove", "#$APPLICATION_PATH", "#$RESOURCE_PATH", "#$=USER", "#$=HOME"))
217 | {
218 | script.wait();
219 | }
220 | else
221 | {
222 | [DXDemoInstaller
223 | modalAlert:@"Uninstall Failed"
224 | info:@"The kernel extension could not be removed from this computer."
225 | "Please contact support for assistance."
226 | style:NSInformationalAlertStyle
227 | button0:@"Continue"
228 | button1:nil];
229 | return FALSE;
230 | }
231 |
232 |
233 | [DXDemoInstaller
234 | modalAlert:@"Uninstall Complete"
235 | info:@"The application was moved to the trash and the driver was uninstalled. The application will now quit."
236 | style:NSInformationalAlertStyle
237 | button0:@"Quit"
238 | button1:nil];
239 | return TRUE; // The application has been moved to the trash and should exit now.
240 | }
241 |
242 |
243 | @end
244 |
--------------------------------------------------------------------------------
/source/displayxdemo/DXDemoOpenGLView.h:
--------------------------------------------------------------------------------
1 | /** @file DXDemoOpenGLView.h
2 | * @brief Custom OpenGL view used to display raw image data.
3 | *
4 | * Copyright (c) 2010 tSoniq. All rights reserved.
5 | */
6 |
7 | #import
8 | #include
9 |
10 | @interface DXDemoOpenGLView : NSOpenGLView
11 | {
12 | class Texture* _desktopTexture;
13 | class Texture* _cursorTexture;
14 | unsigned _cursorX;
15 | unsigned _cursorY;
16 | bool _cursorIsVisible;
17 | }
18 |
19 | - (void)setBlank;
20 | - (void)setDesktop:(const uint32_t*)bitmap width:(unsigned)width height:(unsigned)height;
21 | - (void)updateDesktop:(const uint32_t*)bitmap regionX:(unsigned)x regionY:(unsigned)y regionWidth:(unsigned)width regionHeight:(unsigned)height;
22 | - (void)setCursor:(uint32_t*)bitmap width:(unsigned)width height:(unsigned)height x:(unsigned)x y:(unsigned)y isVisible:(bool)isVisible;
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/source/displayxdemo/DXDemoOpenGLView.mm:
--------------------------------------------------------------------------------
1 | /** @file DXDemoOpenGLView.mm
2 | * @brief
3 | *
4 | * Copyright (c) 2010 tSoniq. All rights reserved.
5 | */
6 |
7 | #import
8 | #import
9 | #import
10 | #import
11 | #import "DXDemoOpenGLView.h"
12 |
13 | /** Helper class used to manage an OpenGL texture.
14 | */
15 | class Texture
16 | {
17 | uint32_t* m_textureData;
18 | uint32_t m_textureWidth;
19 | uint32_t m_textureHeight;
20 | GLuint m_textureId;
21 |
22 | public:
23 |
24 | Texture() : m_textureData(0), m_textureWidth(0), m_textureHeight(0)
25 | {
26 | glEnable(GL_TEXTURE_RECTANGLE_ARB);
27 | glGenTextures(1, &m_textureId);
28 | }
29 |
30 | ~Texture()
31 | {
32 | initialise();
33 | glDeleteTextures(1, &m_textureId);
34 | }
35 |
36 | void initialise()
37 | {
38 | if (m_textureData) free(m_textureData);
39 | m_textureData = 0;
40 | m_textureWidth = 0;
41 | m_textureHeight = 0;
42 | }
43 |
44 |
45 | void initialise(const uint32_t* bitmap, unsigned w, unsigned h)
46 | {
47 | // Save the texture data.
48 | if (!m_textureData || m_textureWidth != w || m_textureHeight != h)
49 | {
50 | if (m_textureData) free(m_textureData);
51 | m_textureWidth = w;
52 | m_textureHeight = h;
53 | size_t size = m_textureWidth * m_textureHeight * sizeof (uint32_t);
54 | void* ptr = 0;
55 | posix_memalign(&ptr, 256, size);
56 | m_textureData = (uint32_t*)ptr;
57 |
58 | if (size >= 1024*1024)
59 | {
60 | // This should allow the last large (1Mbyte or more) texture assigned a small performance improvement.
61 | glTextureRangeAPPLE(GL_TEXTURE_RECTANGLE_ARB, (GLsizei)size, m_textureData);
62 | }
63 | }
64 |
65 | memcpy(m_textureData, bitmap, m_textureWidth * m_textureHeight * sizeof (uint32_t));
66 |
67 | glEnable(GL_TEXTURE_RECTANGLE_ARB);
68 | glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_textureId);
69 | glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, m_textureWidth, m_textureHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, m_textureData);
70 | }
71 |
72 |
73 | void update(const uint32_t* bitmap, unsigned x, unsigned y, unsigned w, unsigned h)
74 | {
75 | if (m_textureData && x < m_textureWidth && y < m_textureHeight)
76 | {
77 | if (x + w > m_textureWidth) w = m_textureWidth - x;
78 | if (y + h > m_textureHeight) h = m_textureHeight - y;
79 |
80 | unsigned index = (x + (y * m_textureWidth)); // Offset to first pixel
81 | for (unsigned i = 0; i < h; ++i)
82 | {
83 | memcpy(&m_textureData[index], &bitmap[index], w * sizeof (uint32_t));
84 | index += m_textureWidth;
85 | }
86 |
87 | glEnable(GL_TEXTURE_RECTANGLE_ARB);
88 | glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_textureId);
89 | glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_PRIORITY, 0.0f);
90 | glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
91 | glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, m_textureWidth, m_textureHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, m_textureData);
92 | }
93 | }
94 |
95 |
96 | // Draw the texture over the entire context
97 | void render()
98 | {
99 | if (m_textureId && m_textureData)
100 | {
101 | glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_textureId);
102 |
103 | glMatrixMode(GL_PROJECTION);
104 | glLoadIdentity();
105 | glMatrixMode(GL_MODELVIEW);
106 |
107 | glColor3f(1, 1, 1);
108 |
109 | glBegin(GL_QUADS);
110 | glTexCoord2f(0, 0); glVertex2f(-1, 1);
111 | glTexCoord2f(m_textureWidth, 0); glVertex2f( 1, 1);
112 | glTexCoord2f(m_textureWidth, m_textureHeight); glVertex2f( 1, -1);
113 | glTexCoord2f(0, m_textureHeight); glVertex2f(-1, -1);
114 | glEnd();
115 | }
116 | }
117 |
118 |
119 | // Draw the texture within another.
120 | void render(int x, int y, const Texture& other)
121 | {
122 | if (m_textureId && m_textureData)
123 | {
124 | glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_textureId);
125 | glPushMatrix();
126 | glMatrixMode(GL_PROJECTION);
127 | glLoadIdentity();
128 | glMatrixMode(GL_MODELVIEW);
129 | glScalef(1, -1, 1);
130 |
131 | glColor4f(1, 1, 1, 1);
132 |
133 | float x0 = (float(x) / float(other.m_textureWidth)) * 2 - 1;
134 | float x1 = (float(x+m_textureWidth) / float(other.m_textureWidth)) * 2 - 1;
135 |
136 | float y0 = (float(y) / float(other.m_textureHeight)) * 2 - 1;
137 | float y1 = (float(y+m_textureHeight) / float(other.m_textureHeight)) * 2 - 1;
138 |
139 | glBegin(GL_QUADS);
140 | glTexCoord2f(0, 0); glVertex2f(x0, y0);
141 | glTexCoord2f(m_textureWidth, 0); glVertex2f(x1, y0);
142 | glTexCoord2f(m_textureWidth, m_textureHeight); glVertex2f(x1, y1);
143 | glTexCoord2f(0, m_textureHeight); glVertex2f(x0, y1);
144 | glEnd();
145 | glPopMatrix();
146 | }
147 | }
148 | };
149 |
150 |
151 | @implementation DXDemoOpenGLView
152 |
153 | - (id)initWithFrame:(NSRect)frame
154 | {
155 | _desktopTexture = 0;
156 | _cursorTexture = 0;
157 |
158 | _cursorX = 0;
159 | _cursorY = 0;
160 | _cursorIsVisible = false;
161 |
162 | GLuint attribs[] =
163 | {
164 | NSOpenGLPFANoRecovery,
165 | NSOpenGLPFAWindow,
166 | NSOpenGLPFAAccelerated,
167 | NSOpenGLPFADoubleBuffer,
168 | NSOpenGLPFAColorSize, 24,
169 | NSOpenGLPFAAlphaSize, 8,
170 | 0
171 | };
172 |
173 | NSOpenGLPixelFormat* fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:(NSOpenGLPixelFormatAttribute*)attribs];
174 |
175 | if (!fmt) NSLog(@"No OpenGL pixel format");
176 |
177 | self = [super initWithFrame:frame pixelFormat:fmt];
178 | return self;
179 | }
180 |
181 |
182 | - (void)prepareOpenGL
183 | {
184 | [super prepareOpenGL];
185 |
186 | CGLContextObj ctx = CGLGetCurrentContext();
187 | CGLEnable( ctx, kCGLCEMPEngine);
188 |
189 | GLint swapInt = 1;
190 | [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
191 |
192 | glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_PRIORITY, 0.0f);
193 | glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_SHARED_APPLE);
194 | glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_PRIORITY, 0.0f);
195 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
196 | glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
197 |
198 | _cursorTexture = new Texture();
199 | _desktopTexture = new Texture();
200 | }
201 |
202 |
203 | - (void)drawRect:(NSRect)rect
204 | {
205 | GLsizei w = (GLsizei)rect.size.width;
206 | GLsizei h = (GLsizei)rect.size.height;
207 |
208 | glViewport(0, 0, (GLsizei)w, (GLsizei)h);
209 | glClearColor(0, 0, 0, 1);
210 | glClear(GL_COLOR_BUFFER_BIT+GL_DEPTH_BUFFER_BIT+GL_STENCIL_BUFFER_BIT);
211 |
212 | glDisable(GL_BLEND);
213 | _desktopTexture->render();
214 | if (_cursorIsVisible)
215 | {
216 | glEnable(GL_BLEND);
217 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
218 | _cursorTexture->render(_cursorX, _cursorY, *_desktopTexture);
219 | }
220 | [[self openGLContext] flushBuffer];
221 | }
222 |
223 |
224 | - (void)setBlank
225 | {
226 | _desktopTexture->initialise();
227 | }
228 |
229 |
230 | - (void)setDesktop:(const uint32_t*)bitmap width:(unsigned)width height:(unsigned)height
231 | {
232 | _desktopTexture->initialise(bitmap, width, height);
233 | [self setNeedsDisplay:YES];
234 | }
235 |
236 |
237 | - (void)updateDesktop:(const uint32_t*)bitmap regionX:(unsigned)x regionY:(unsigned)y regionWidth:(unsigned)width regionHeight:(unsigned)height
238 | {
239 | _desktopTexture->update(bitmap, x, y, width, height);
240 | [self setNeedsDisplay:YES];
241 | }
242 |
243 |
244 | - (void)setCursor:(uint32_t*)bitmap width:(unsigned)width height:(unsigned)height x:(unsigned)x y:(unsigned)y isVisible:(bool)isVisible
245 | {
246 | _cursorX = x;
247 | _cursorY = y;
248 | _cursorIsVisible = isVisible;
249 | if (_cursorIsVisible) _cursorTexture->initialise(bitmap, width, height);
250 | [self setNeedsDisplay:YES];
251 | }
252 |
253 |
254 | @end
255 |
--------------------------------------------------------------------------------
/source/displayxdemo/com_tsoniq_dxdemo_installer.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # Installation Script.
4 | #
5 | # Copyright (c) 2012 tSoniq. All rights reserved.
6 | #
7 | # $0 script name.
8 | # $1 the command (as passed to PrivilegedScriptRunner::startScript()).
9 | # $2 the path to the application requesting the script run.
10 | # $3 the path to the application's resources directory (containing any files to be installed).
11 | # $4 the user that originally invoked the script
12 | # $5 the home directory for the user that originally invoked the script
13 | #
14 | # WARNING: this script is run as root and may damage the user's computer.
15 | # Test any changes THOROUGHLY run running the script manually before testing with the end application.
16 | #
17 | # This script supports two commands:
18 | #
19 | # install Install the kernel extension and start it.
20 | # remove Stop and uninstall the kernel extension; optionally move the application to the trash
21 | #
22 | # Note that the commands are not symmetric: the assumption is that the initial installation is simply
23 | # by dragging the the application from a disk image to a folder somewhere on disk.
24 | #
25 | # This script - like seemingly most Mac applications - currently makes no attempt to uninstall any meta-files
26 | # stored in the user's ~/Library directory. There are two reasons for this: (i) we do not know exactly
27 | # what is generated by the OS and (ii) we have no knowledge of which users to clear the application
28 | # from.
29 | #
30 |
31 | # Common definitions.
32 | COMMAND=$1
33 | APPLICATION=$2
34 | RESOURCE_DIR=$3
35 | ORIGINAL_USER=$4
36 | ORIGINAL_HOME=$5
37 |
38 | # The paths to the kext(s).
39 | KEXT_BID="com.tsoniq.driver.DisplayXFB"
40 | KEXT_NAM="com_tsoniq_DisplayXFB.kext"
41 | PLUG_NAM="com_tsoniq_DisplayXGA.plugin"
42 |
43 | echo "Running script $0"
44 | echo " command: '$COMMAND'"
45 | echo " application: '$APPLICATION'"
46 | echo " resources: '$RESOURCE_DIR'"
47 | echo " original_user: '$ORIGINAL_USER'"
48 | echo " original_home: '$ORIGINAL_HOME'"
49 | echo " OS version: `sw_vers -productVersion`"
50 | echo " KEXT_BID: '$KEXT_BID'"
51 | echo " KEXT_NAM: '$KEXT_NAM'"
52 | echo " PLUG_NAM: '$PLUG_NAM'"
53 |
54 |
55 |
56 | # Method called to terminate execution with success status.
57 | doExitSuccess()
58 | {
59 | echo "@@@exit/success/${COMMAND}"
60 | exit 0
61 | }
62 |
63 |
64 | # Method called to terminate execution with failure status.
65 | doExitFailure()
66 | {
67 | echo "@@@exit/fail/${COMMAND}/$1"
68 | exit 0
69 | }
70 |
71 |
72 | # Move the supplied argument(s) to the trash
73 | doMoveToTrash()
74 | {
75 | local SOURCE
76 | for SOURCE in "$@"; do
77 |
78 | local FILENAME=`basename "${SOURCE}"`
79 | FILENAME=${FILENAME%/}
80 | local TARGET=$FILENAME
81 |
82 | while [ -e "${ORIGINAL_HOME}/.Trash/${TARGET}" ]; do
83 | local EXTENSION=`expr "${FILENAME}" : ".*\(\.[^\.]*\)$"`
84 | local STEM=${FILENAME%$EXTENSION}
85 | TARGET="/$STEM`date +%H-%M-%S` ${RANDOM}${EXTENSION}"
86 | done
87 | TARGET="${ORIGINAL_HOME}/.Trash/${TARGET}"
88 |
89 | mv "${SOURCE}" "${TARGET}"
90 | done
91 | }
92 |
93 |
94 | # Generic method used to uninstall a specific instance of the driver.
95 | #
96 | # $1 Specifies the installed KEXT path.
97 | #
98 | doRemoveKextInstance()
99 | {
100 | # The following checks for the Info.plist file in the kext, rather than the kext dir name
101 | # to reduce the risk of a script error damaging the system installation.
102 | if [ -f "${1}/Contents/Info.plist" ] ; then
103 | # kextunload "${1}"
104 | # sleep 1
105 | # kextunload "${1}"
106 | # sleep 1
107 | rm -rf "${1}"
108 | fi
109 | }
110 |
111 |
112 | # Method called to perform uninstallation of the driver.
113 | doRemoveDriver()
114 | {
115 | # clean up older installs
116 | rm -f /Library/LaunchDaemons/*tsoniq*dxdemo*
117 | rm -f /Library/PrivilegedHelperTools/*tsoniq*dxdemo*
118 | rm -f /Library/LaunchDaemons/*tsoniq*dxdemo*
119 | rm -f /Library/PrivilegedHelperTools/*tsoniq*dxdemo*
120 | rm -f /Library/LaunchDaemons/*tsoniq*dxdemo*
121 | rm -f /Library/PrivilegedHelperTools/*tsoniq*dxdemo*
122 |
123 | # sanity check (before sudo rm -rf destroys the user's computer)
124 | if [ -z "${KEXT_NAM}" ] ; then
125 | doExitFailure "Undefined kext name"
126 | else
127 | doRemoveKextInstance "/Library/Extensions/${KEXT_NAM}"
128 | doRemoveKextInstance "/Library/Extensions/${PLUG_NAM}"
129 | doRemoveKextInstance "/System/Library/Extensions/${KEXT_NAM}"
130 | doRemoveKextInstance "/System/Library/Extensions/${PLUG_NAM}"
131 | fi
132 | }
133 |
134 |
135 | # Generic method used to install a specific instance of the driver.
136 | #
137 | # $1 Specifies the KEXT name.
138 | # $2 Specifies the KEXT source dir.
139 | # $3 Specifies the KEXT target dir.
140 | #
141 | doInstallDriverFile()
142 | {
143 | KEXT_TMP="`mktemp -d -t tsoniq`/${1}"
144 | KEXT_SRC="${2}/${1}"
145 | KEXT_DST="${3}/${1}"
146 | echo "doInstallDriverFile: ${KEXT_SRC} --> ${KEXT_TMP} --> ${KEXT_DST}"
147 | if [ ! -z "${1}" ] && [ -d "${KEXT_SRC}" ] && [ -f "${KEXT_SRC}/Contents/Info.plist" ] && [ -d "${3}" ] ; then
148 | cp -rp "${KEXT_SRC}" "${KEXT_TMP}"
149 | chown -R root:wheel "${KEXT_TMP}"
150 | chmod -R 755 "${KEXT_TMP}"
151 | mv "${KEXT_TMP}" "${KEXT_DST}"
152 | fi
153 | }
154 |
155 |
156 | # Method called to perform installation.
157 | doInstallDriver()
158 | {
159 | # remove current install files
160 | doRemoveDriver
161 |
162 | # we always install to /Library/Extensions, but only /System/Library/Extensions if running a pre-10.9 OS.
163 | OS_PRODUCT_VERSION=`sw_vers -productVersion | cut -d . -f 2`
164 |
165 | # install the new driver and load it
166 | if [ -z "${KEXT_NAM}" ] ; then
167 |
168 | doExitFailure "Undefined kext name"
169 |
170 | elif [ ${OS_PRODUCT_VERSION} -lt 9 ] ; then
171 |
172 | # Running on 10.8 or older.
173 | # Install the unsigned driver extension in to /System/Library/Extensions (this is the driver that is used).
174 | echo "Installing for OS 10.8 or earlier"
175 | doInstallDriverFile "${KEXT_NAM}" "${RESOURCE_DIR}/k106" "/System/Library/Extensions"
176 | #doInstallDriverFile "${PLUG_NAM}" "${RESOURCE_DIR}/k106" "/System/Library/Extensions"
177 | #kextload "/System/Library/Extensions/${KEXT_NAM}"
178 | touch "/System/Library/Extensions"
179 |
180 | # Also install the signed driver to /Library/Extensions to cover future upgrades to 10.9.
181 | if [ -d "/Library/Extensions" ] ; then
182 | doInstallDriverFile "${KEXT_NAM}" "${RESOURCE_DIR}/k109" "/Library/Extensions"
183 | #doInstallDriverFile "${PLUG_NAM}" "${RESOURCE_DIR}/k109" "/Library/Extensions"
184 | fi
185 |
186 | else
187 |
188 | # Running on 10.9 or newer. Install the signed driver in to /Library/Extensions.
189 | echo "Installing for OS 10.9 or later"
190 | doInstallDriverFile "${KEXT_NAM}" "${RESOURCE_DIR}/k109" "/Library/Extensions"
191 | #doInstallDriverFile "${PLUG_NAM}" "${RESOURCE_DIR}/k109" "/Library/Extensions"
192 | #kextload "/Library/Extensions/${KEXT_NAM}"
193 | touch "/Library/Extensions"
194 | fi
195 | }
196 |
197 |
198 | # Method called to clean the audio installation.
199 | doResetAudioSettings()
200 | {
201 | # rebuild the binding directory (this should be unnecessary, but...)
202 | update_dyld_shared_cache -force
203 |
204 | # clean protools databases and preferences
205 | if [ -n "${ORIGINAL_HOME}" ] ; then
206 | rm -rf "/Library/Application\ Support/Digidesign/Databases"
207 | rm -f "${ORIGINAL_HOME}/Library/Preferences/com.digidesign.protools.plist"
208 | rm -f "${ORIGINAL_HOME}/Library/Preferences/com.digidesign.protoolsLE.plist"
209 | rm -rf "${ORIGINAL_HOME}/Library/Preferences/DAE Prefs"
210 | rm -rf "${ORIGINAL_HOME}/Library/Preferences/DigiSetup.OSX"
211 | fi
212 |
213 | # remove device specific preferences - this will clear any user-device configuration.
214 | rm -f "/Library/Preferences/com.apple.audio.DeviceSettings.plist"
215 | rm -f "/Library/Preferences/com.apple.audio.SystemSettings.plist"
216 | rm -f "/Library/Preferences/Audio/com.apple.audio.DeviceSettings.plist"
217 | rm -f "/Library/Preferences/Audio/com.apple.audio.SystemSettings.plist"
218 |
219 | # restart coreaudiod
220 | COREAUDIO_PID=`ps -Af | grep -v grep | grep coreaudiod | awk '{ print $1 }'`
221 | echo "got pid ${COREAUDIO_PID}"
222 | if [ -n "${COREAUDIO_PID}" ] ; then
223 | echo 'killing coreaudiod PID ${COREAUDIO_PID}'
224 | echo "kill -9 ${COREAUDIO_PID}"
225 | kill -9 "${COREAUDIO_PID}"
226 | fi
227 | }
228 |
229 |
230 |
231 | # Run the script.
232 | if [ ! -d "${RESOURCE_DIR}" ] ; then
233 | doExitFailure "Unable to access resource directory: ${RESOURCE_DIR}"
234 | elif [ ! -d "${RESOURCE_DIR}/k106/${KEXT_NAM}" ] && [ ! -d "${RESOURCE_DIR}/k109/${KEXT_NAM}" ] ; then
235 | doExitFailure "Missing install file: ${KEXT_NAM}"
236 | elif [ "${COMMAND}" = "install" ] ; then
237 | doInstallDriver
238 | doExitSuccess
239 | elif [ "${COMMAND}" = "remove" ] ; then
240 | doRemoveDriver
241 | doMoveToTrash "${APPLICATION}"
242 | doExitSuccess
243 | elif [ "${COMMAND}" = "resetaudiosettings" ] ; then
244 | doResetAudioSettings
245 | doExitSuccess
246 | else
247 | doExitFailure "unknown command: ${COMMAND}"
248 | fi
249 |
--------------------------------------------------------------------------------
/source/displayxdemo/en.lproj/Credits.rtf:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\ansicpg1252\cocoartf1265
2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;}
3 | {\colortbl;\red255\green255\blue255;}
4 | \paperw11900\paperh16840\vieww9600\viewh8400\viewkind0
5 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
6 |
7 | \f0\b\fs24 \cf0 \
8 | Copyright (c) 2010-2014 tSoniq.\
9 | All Rights Reserved.\
10 | \
11 | http://tsoniq.com\
12 | }
--------------------------------------------------------------------------------
/source/displayxdemo/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/source/displayxdemo/en.lproj/MainMenu.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
113 |
114 |
115 |
116 |
117 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/source/displayxdemo/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | //
4 |
5 | #import
6 |
7 | int main(int argc, char *argv[])
8 | {
9 | return NSApplicationMain(argc, (const char **)argv);
10 | }
11 |
--------------------------------------------------------------------------------
/source/displayxdemo/ts_privilegedscriptrunner.cc:
--------------------------------------------------------------------------------
1 | /** @file
2 | * @brief Class used to run a script with root privileges.
3 | * @copyright Copyright (c) 2012 tSoniq. All rights reserved.
4 | */
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include "ts_privilegedscriptrunner.h"
13 |
14 |
15 | #define kPrivilegedScriptRunnerProgramPath CFSTR("/bin/sh") //!< The program used to run the script
16 | #define kPrivilegedScriptRunnerDefaultJobPrefixId CFSTR("com.tsoniq.id") //!< Used if the bundle ID can not be accessed
17 |
18 | namespace ts
19 | {
20 | PrivilegedScriptRunner::PrivilegedScriptRunner(const char* script, const char* ident)
21 | :
22 | m_scriptName(0),
23 | m_scriptLabel(0),
24 | m_stdoutPath(0),
25 | m_stderrPath(0),
26 | m_auth(),
27 | m_haveAuth(false),
28 | m_timeoutSecs(kDefaultTimeout),
29 | m_scriptIsActive(false),
30 | m_scriptPid(0)
31 | {
32 | // Construct a label for the job.
33 | CFMutableStringRef label = 0;
34 | CFUUIDRef uuid = 0;
35 | CFStringRef postfix = 0;
36 |
37 | do
38 | {
39 | // Initialise the script name.
40 | m_scriptName = CFStringCreateWithCString(kCFAllocatorDefault, script, kCFStringEncodingUTF8);
41 | if (!m_scriptName) break;
42 |
43 | // Create the job label.
44 | label = CFStringCreateMutable(kCFAllocatorDefault, 0);
45 | if (!label) break;
46 |
47 | // Get the job prefix, applying a default if the bundle ID is not available.
48 | CFStringRef prefix = 0;
49 | CFBundleRef bundle = CFBundleGetMainBundle();
50 | if (bundle) prefix = CFBundleGetIdentifier(bundle);
51 | if (!prefix) prefix = kPrivilegedScriptRunnerDefaultJobPrefixId;
52 |
53 | // Create the postfix.
54 | if (ident)
55 | {
56 | postfix = CFStringCreateWithCString(kCFAllocatorDefault, ident, kCFStringEncodingUTF8);
57 | if (!postfix) break;
58 | }
59 | else
60 | {
61 | uuid = CFUUIDCreate(kCFAllocatorDefault);
62 | if (!uuid) break;
63 | postfix = CFUUIDCreateString(kCFAllocatorDefault, uuid);
64 | if (!postfix) break;
65 | }
66 |
67 | CFStringAppend(label, prefix);
68 | CFStringAppend(label, CFSTR(".script."));
69 | CFStringAppend(label, postfix);
70 | m_scriptLabel = label;
71 | CFRetain(m_scriptLabel);
72 |
73 | } while (false);
74 |
75 | if (postfix) CFRelease(postfix);
76 | if (uuid) CFRelease(uuid);
77 | if (label) CFRelease(label);
78 |
79 | if (!m_scriptName || !m_scriptLabel) throw std::bad_alloc();
80 | }
81 |
82 |
83 | PrivilegedScriptRunner::~PrivilegedScriptRunner()
84 | {
85 | deauthorise();
86 |
87 | if (m_scriptName) { CFRelease(m_scriptName); m_scriptName = 0; }
88 | if (m_scriptLabel) { CFRelease(m_scriptLabel); m_scriptLabel = 0; }
89 | if (m_stdoutPath) { CFRelease(m_stdoutPath); m_stdoutPath = 0; }
90 | if (m_stderrPath) { CFRelease(m_stderrPath); m_stderrPath = 0; }
91 | }
92 |
93 |
94 | void PrivilegedScriptRunner::setTimeout(int tmsecs)
95 | {
96 | if (tmsecs < 0) tmsecs = 0;
97 | m_timeoutSecs = tmsecs;
98 | }
99 |
100 |
101 | void PrivilegedScriptRunner::setRedirect(const char* stdoutPath, const char* stderrPath)
102 | {
103 | if (m_stdoutPath) { CFRelease(m_stdoutPath); m_stdoutPath = 0; }
104 | if (m_stderrPath) { CFRelease(m_stderrPath); m_stderrPath = 0; }
105 |
106 | if (stdoutPath) m_stdoutPath = CFStringCreateWithCString(kCFAllocatorDefault, stdoutPath, kCFStringEncodingUTF8);
107 | if (stderrPath) m_stderrPath = CFStringCreateWithCString(kCFAllocatorDefault, stdoutPath, kCFStringEncodingUTF8);
108 | }
109 |
110 |
111 | bool PrivilegedScriptRunner::authorise()
112 | {
113 | deauthorise();
114 |
115 | assert(!isAuthorised());
116 |
117 | AuthorizationItem authItem = { kSMRightModifySystemDaemons, 0, NULL, 0 };
118 | AuthorizationRights authRights = { 1, &authItem };
119 | AuthorizationFlags flags = kAuthorizationFlagInteractionAllowed | kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights;
120 |
121 | if (errAuthorizationSuccess == AuthorizationCreate(&authRights, kAuthorizationEmptyEnvironment, flags, &m_auth))
122 | {
123 | m_haveAuth = true;
124 | assert(isAuthorised());
125 | }
126 |
127 | return m_haveAuth;
128 | }
129 |
130 |
131 | void PrivilegedScriptRunner::deauthorise()
132 | {
133 | if (m_haveAuth)
134 | {
135 | AuthorizationFree(m_auth, 0);
136 | m_haveAuth = false;
137 | }
138 | }
139 |
140 |
141 | bool PrivilegedScriptRunner::isRunning()
142 | {
143 | bool result = false;
144 | if (m_scriptIsActive)
145 | {
146 | assert(0 != m_scriptPid);
147 | int n = kill(m_scriptPid, 0);
148 | result = (0 == n || EPERM == errno);
149 | }
150 | return result;
151 | }
152 |
153 |
154 | void PrivilegedScriptRunner::wait()
155 | {
156 | while (isRunning())
157 | {
158 | usleep(50000);
159 | }
160 | }
161 |
162 |
163 |
164 |
165 |
166 |
167 | #pragma mark -
168 | #pragma mark Private Methods
169 |
170 |
171 | /** Start a script running.
172 | *
173 | * See start() for a description of the arguments.
174 | */
175 | bool PrivilegedScriptRunner::startScript(const char* arg1, const char* arg2, const char* arg3, const char* arg4, const char* arg5, const char* arg6, const char* arg7, const char* arg8)
176 | {
177 | bool result = false;
178 | CFStringRef cfScriptPath = 0;
179 | CFMutableArrayRef cfProgramArguments = 0;
180 | CFNumberRef cfExitTimeoutNumber = 0;
181 | CFMutableDictionaryRef plist = 0;
182 | CFErrorRef error = 0;
183 |
184 | do
185 | {
186 | if (!isAuthorised()) break;
187 |
188 | stopScript(); // terminate/cleanup previously running script
189 |
190 | cfScriptPath = urlToAbsPath(CFBundleCopyResourceURL(CFBundleGetMainBundle(), m_scriptName, NULL, NULL));
191 | if (!cfScriptPath) break;
192 |
193 | cfExitTimeoutNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &m_timeoutSecs);
194 | if (!cfExitTimeoutNumber) break;
195 |
196 | cfProgramArguments = CFArrayCreateMutable(kCFAllocatorDefault, 4, &kCFTypeArrayCallBacks);
197 | if (!cfProgramArguments) break;
198 |
199 | CFArrayAppendValue(cfProgramArguments, kPrivilegedScriptRunnerProgramPath); // argv[0] is the shell program name
200 | CFArrayAppendValue(cfProgramArguments, cfScriptPath); // argv[1] is the script name
201 |
202 | // The remaing arguments are passed by the caller to this method (and massaged a bit).
203 | const char* argArray[8] = { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 };
204 | for (unsigned i = 0; i != sizeof argArray / sizeof argArray[0]; ++i)
205 | {
206 | const char* arg = argArray[i];
207 | CFStringRef cfArg = 0;
208 |
209 | if (!arg) continue;
210 |
211 | // We have an argument. Try to make sense of it. If we can't make sense of it, we
212 | // just pass an empty string as the argument (eg if undefined variables are used).
213 | if (0 == strncmp("#$=", arg, 3))
214 | {
215 | // getenv substitution handling
216 | const char* env = getenv(&arg[3]);
217 | if (env) cfArg = CFStringCreateWithCString(kCFAllocatorDefault, env, kCFStringEncodingUTF8);
218 | }
219 | else if (0 == strncmp("#$?", arg, 3))
220 | {
221 | // info.plist substitution handling
222 | CFStringRef cfKey = CFStringCreateWithCString(kCFAllocatorDefault, &arg[3], kCFStringEncodingUTF8);
223 | if (cfKey)
224 | {
225 | CFTypeRef object = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), cfKey);
226 | if (object && CFStringGetTypeID() == CFGetTypeID(object))
227 | {
228 | cfArg = CFStringCreateCopy(kCFAllocatorDefault, (CFStringRef)object);
229 | }
230 | else if (object && CFBooleanGetTypeID() == CFGetTypeID(object))
231 | {
232 | cfArg = CFBooleanGetValue((CFBooleanRef)object) ? CFSTR("1") : CFSTR("0");
233 | }
234 | else if (object && CFNumberGetTypeID() == CFGetTypeID(object))
235 | {
236 | CFNumberRef number = (CFNumberRef)object;
237 | if (CFNumberIsFloatType(number))
238 | {
239 | double n = 0;
240 | CFNumberGetValue(number, kCFNumberDoubleType, &n);
241 | cfArg = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%.15f"), n);
242 | }
243 | else
244 | {
245 | SInt64 n = 0;
246 | CFNumberGetValue(number, kCFNumberSInt64Type, &n);
247 | cfArg = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%lld"), (long long)n);
248 | }
249 | }
250 | else
251 | {
252 | // Other types are not be converted at present.
253 | }
254 | CFRelease(cfKey);
255 | }
256 | }
257 | else if (0 == strncmp("#$", arg, 2))
258 | {
259 | // Non-generic substitution handling
260 | if (0 == strcmp(&arg[2], "APPLICATION_PATH"))
261 | {
262 | cfArg = urlToAbsPath(CFBundleCopyBundleURL(CFBundleGetMainBundle()));
263 | }
264 | else if (0 == strcmp(&arg[2], "RESOURCE_PATH"))
265 | {
266 | cfArg = urlToAbsPath(CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle()));
267 | }
268 | }
269 | else
270 | {
271 | // Literals
272 | cfArg = CFStringCreateWithCString(kCFAllocatorDefault, arg, kCFStringEncodingUTF8);
273 | }
274 |
275 | if (!cfArg) cfArg = CFSTR("");
276 |
277 | CFArrayAppendValue(cfProgramArguments, cfArg); // append the new argument
278 | CFRelease(cfArg);
279 | }
280 |
281 |
282 | // Create the job definition.
283 | plist = CFDictionaryCreateMutable(kCFAllocatorDefault, 9, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
284 | if (!plist) break;
285 | CFDictionaryAddValue(plist, CFSTR("Label"), m_scriptLabel);
286 | CFDictionaryAddValue(plist, CFSTR("RunAtLoad"), kCFBooleanTrue);
287 | CFDictionaryAddValue(plist, CFSTR("KeepAlive"), kCFBooleanFalse);
288 | CFDictionaryAddValue(plist, CFSTR("EnableTransactions"), kCFBooleanFalse);
289 | CFDictionaryAddValue(plist, CFSTR("ExitTimeout"), cfExitTimeoutNumber);
290 | CFDictionaryAddValue(plist, CFSTR("Program"), kPrivilegedScriptRunnerProgramPath);
291 | CFDictionaryAddValue(plist, CFSTR("ProgramArguments"), cfProgramArguments);
292 | if (m_stdoutPath) CFDictionaryAddValue(plist, CFSTR("StandardOutPath"), m_stdoutPath);
293 | if (m_stderrPath) CFDictionaryAddValue(plist, CFSTR("StandardErrorPath"), m_stderrPath);
294 | #if DEBUG
295 | CFDictionaryAddValue(plist, CFSTR("Debug"), kCFBooleanTrue);
296 | #else
297 | CFDictionaryAddValue(plist, CFSTR("Debug"), kCFBooleanFalse);
298 | #endif
299 |
300 |
301 | // Submit the job.
302 | if (!SMJobSubmit(kSMDomainSystemLaunchd, (CFDictionaryRef)plist, m_auth, &error))
303 | {
304 | // Error.
305 | break;
306 | }
307 |
308 | // Script is now running. Get the PID. Currently, we use a key from the job dictionary,
309 | // but if Apple decides to remove this we could always write a wrapper script to export the
310 | // PID via a file or similar mechanism.
311 | m_scriptIsActive = true;
312 | usleep(10000); // paranoid short sleep to give SMJobSubmit() a chance to start. This *should* be unnecessary.
313 | pid_t pid = 0;
314 | CFDictionaryRef dict = SMJobCopyDictionary(kSMDomainSystemLaunchd, m_scriptLabel);
315 | if (dict)
316 | {
317 | CFNumberRef pidNumber = (CFNumberRef)CFDictionaryGetValue(dict, CFSTR("PID"));
318 | SInt64 i64pid = 0;
319 | if (pidNumber)
320 | {
321 | if (CFNumberGetValue(pidNumber, kCFNumberSInt64Type, &i64pid))
322 | {
323 | pid = (pid_t) i64pid;
324 | }
325 | }
326 | CFRelease(dict);
327 | }
328 |
329 | m_scriptPid = pid; // set the PID, or zero if not known (this means that the job already completed)
330 | if (!m_scriptPid) stopScript(); // if we already finished, take the chance to clean up
331 | result = true;
332 |
333 | } while (false);
334 |
335 |
336 | // Clean up.
337 | if (error) CFRelease(error);
338 | if (plist) CFRelease(plist);
339 | if (cfExitTimeoutNumber) CFRelease(cfExitTimeoutNumber);
340 | if (cfProgramArguments) CFRelease(cfProgramArguments);
341 | if (cfScriptPath) CFRelease(cfScriptPath);
342 |
343 | return result;
344 | }
345 |
346 |
347 | /** Cleanup after running a script. If necessary, the script will be terminated.
348 | */
349 | void PrivilegedScriptRunner::stopScript()
350 | {
351 | if (m_scriptIsActive)
352 | {
353 | assert(m_scriptLabel);
354 | SMJobRemove(kSMDomainSystemLaunchd, m_scriptLabel, m_auth, false, NULL);
355 | m_scriptPid = 0;
356 | m_scriptIsActive = false;
357 | }
358 | }
359 |
360 |
361 |
362 | /** Read a line from a file, with timeout.
363 | *
364 | * @param line Returns the resulting string.
365 | * @param fd The file descriptor.
366 | * @param timeoutSeconds The timeout.
367 | * @return True for success, false if a timeout or IO error occurred.
368 | *
369 | * This method waits for up to timeoutSeconds to try to read a single text line (terminated
370 | * via a newline character) from the specified file descriptor. Any data after the line
371 | * is ignored.
372 | */
373 | bool PrivilegedScriptRunner::readLine(std::string& line, int fd, unsigned timeoutSeconds)
374 | {
375 | // Set up the kernel queue to monitor the file (better than polling).
376 | int kq = kqueue();
377 | struct kevent kev;
378 | memset(&kev, 0, sizeof kev);
379 | kev.ident = fd;
380 | kev.flags = EV_ADD | EV_CLEAR;
381 | kev.filter = EVFILT_READ;
382 | kev.fflags = 0;
383 | kev.data = 0;
384 | kev.udata = 0;
385 | int kr = kevent(kq, &kev, 1, 0, 0, 0);
386 | assert(-1 != kr);
387 | if (-1 == kr) return false;
388 |
389 | // Initialise the timeout parameters.
390 | time_t timeNow = 0;
391 | time(&timeNow);
392 | const time_t timeoutEnd = timeNow + timeoutSeconds + 1;
393 |
394 | // Loop waiting to receive a complete line.
395 | bool result = false;
396 | line.clear();
397 | while (!result)
398 | {
399 | time(&timeNow);
400 | if (timeNow > timeoutEnd) break; // timeout expired
401 | long timeoutSecondsRemaining = (long)(timeoutEnd - timeNow);
402 | struct timespec keventTimeout = { timeoutSecondsRemaining, 1000000 * 500 };
403 |
404 | struct kevent keventResults[1];
405 | int kr = kevent(kq, 0, 0, keventResults, sizeof (keventResults) / sizeof (keventResults[0]), &keventTimeout);
406 | if (1 == kr)
407 | {
408 | // Something is available to read. We are expecting a 1-line result, terminated with a newline character or EOF.
409 | char ch = 0;
410 | while (1 == read(fd, &ch, 1))
411 | {
412 | if ('\n' == ch || 0 == ch) { result = true; break; }
413 | else { line += ch; };
414 | }
415 | }
416 | else if (0 == kr) break; // timeout
417 | else break; // some other error
418 | }
419 |
420 | return result;
421 | }
422 |
423 |
424 | /** Given a URL, return an absolute string reference to the path.
425 | *
426 | * @param url The URL reference. May be nil. A reference to the URL is consumed.
427 | * @return A string giving the absolute file system path, or zero if no possible resolution.
428 | *
429 | * The supplied URL is CFRelease()d. The caller must CFRelease() the returned string if non-nil.
430 | */
431 | CFStringRef PrivilegedScriptRunner::urlToAbsPath(CFURLRef url)
432 | {
433 | CFStringRef result = 0;
434 | if (url)
435 | {
436 | CFURLRef absUrl = CFURLCopyAbsoluteURL(url);
437 | if (absUrl)
438 | {
439 | result = CFURLCopyFileSystemPath(absUrl, kCFURLPOSIXPathStyle);
440 | CFRelease(absUrl);
441 | }
442 | CFRelease(url);
443 | }
444 | return result;
445 | }
446 |
447 | } // namespace
448 |
--------------------------------------------------------------------------------
/source/displayxdemo/ts_privilegedscriptrunner.h:
--------------------------------------------------------------------------------
1 | /** @file
2 | * @brief Class used to run a script with root privileges.
3 | * @copyright Copyright (c) 2012 tSoniq. All rights reserved.
4 | */
5 |
6 | #ifndef COM_TSONIQ_ts_drivershieldinstaller_H
7 | #define COM_TSONIQ_ts_drivershieldinstaller_H (1)
8 |
9 | #include
10 | #include
11 | #include
12 |
13 | namespace ts
14 | {
15 | /** Class used to manage driver installation and uninstallation.
16 | *
17 | * This class requires that the script kPrivilegedScriptRunnerScriptName exist in the current program's resources folder
18 | * and is an executable 'sh' script.
19 | *
20 | * The script is run with the following arguments:
21 | *
22 | * $0 the script name (as passed to the constructor)
23 | * $1 a 'command' string (as passed to start())
24 | * $2 a fully qualified path to the application launching the script
25 | * $3 a fully qualified path to a resource directory (usually, but not necessarily, the directory containing the script)
26 | *
27 | * The script should perform the requested action and then append a single line completion status to the specified file.
28 | * The completion line must be terminated by a newline and must be "success" for successful completion. Any other string
29 | * denotes a failure.
30 | *
31 | * If the script needs to locate other files (eg to install), it should do so relative to the script path.
32 | *
33 | * Be aware that the script may be terminated early via kill() if the client supplied timeout to install() or remove()
34 | * expires.
35 | */
36 | class PrivilegedScriptRunner
37 | {
38 | public:
39 |
40 | static const int kDefaultTimeout = 20; //!< The default timeout for scripts (seconds).
41 |
42 |
43 | /** Constructor.
44 | *
45 | * @param script The name of the script to run.
46 | * @param ident An identifier for the job. This will be appended to the application CFBundleID
47 | * to create the job identifier. If zero, a UUID is used. Default zero.
48 | *
49 | * The script name must contain any file extension but must not contain any path specifiers. The script must reside in
50 | * in the application's Resources directory directly and must be executable by root.
51 | *
52 | * You can use an explicit identifier if you are sure that there will only be once instance of
53 | * the script running concurrently. One advantage of this is that any left-over resources in the
54 | * avent of an application crash during execution are implicitly cleaned up the next time that
55 | * it is run.
56 | */
57 | explicit PrivilegedScriptRunner(const char* script, const char* ident=0);
58 |
59 |
60 | ~PrivilegedScriptRunner(); //!< Destructor.
61 |
62 | bool isAuthorised() const { return m_haveAuth; } //!< Test if the installer is authorised
63 |
64 |
65 | /** Set the script timeout.
66 | *
67 | * @param tmsecs The timeout, in seconds. Pass zero or negative for none. Default kDefaultTimeout.
68 | *
69 | * If the script has not completed by after @e timeout seconds from starting, the OS will
70 | * automatically kill it.
71 | */
72 | void setTimeout(int tmsecs);
73 |
74 |
75 | /** Configure IO redirection.
76 | *
77 | * @param stdoutPath The file path for stdout, or zero for none. Default zero.
78 | * @param stderrPath The file path for stderr, or zero for none. Default zero.
79 | *
80 | * If the script has not completed by after @e timeout seconds from starting, the OS will
81 | * automatically kill it.
82 | */
83 | void setRedirect(const char* stdoutPath=0, const char* stderrPath=0);
84 |
85 |
86 | /** Authorise the client.
87 | *
88 | * @return Logical true for success, false for failure.
89 | *
90 | * This method must be called prior to performing any other operation. It may prompt the
91 | * user for a password.
92 | */
93 | bool authorise();
94 |
95 |
96 | /** Deathorise the client.
97 | */
98 | void deauthorise();
99 |
100 |
101 | /** Run the script.
102 | *
103 | * @param arg1 String passed as $1 to the script, or zero for none. Default zero.
104 | * @param arg2 String passed as $2 to the script, or zero for none. Default zero.
105 | * @param arg3 String passed as $3 to the script, or zero for none. Default zero.
106 | * @param arg4 String passed as $4 to the script, or zero for none. Default zero.
107 | * @param arg5 String passed as $5 to the script, or zero for none. Default zero.
108 | * @param arg6 String passed as $6 to the script, or zero for none. Default zero.
109 | * @param arg7 String passed as $7 to the script, or zero for none. Default zero.
110 | * @param arg8 String passed as $8 to the script, or zero for none. Default zero.
111 | * @return Logical true for success, false for failure.
112 | *
113 | * This method starts the script running, but does not wait for completion. Only one script may be running
114 | * at a time (this call will return false otherwise).
115 | *
116 | * The optional arguments are passed to the script in order of definition. Any string beginning with
117 | * "#$" is reserved for automatic value substitution, with the following methods currently defined:
118 | *
119 | * | #$APPLICATION_PATH | Replaced with the path to the current application.
120 | * | #$RESOURCE_PATH | Replaced with the path to the resource folder for the current application.
121 | * | #$= | Replaced with the value of getenv("") from the app environment invoking the script.
122 | * | #$? | Replaced with the value for in the application info.plist file.
123 | *
124 | * This method will attempt to run the requested script as root (as if via sudo). To obtain information
125 | * about the environment launching the script, you can use the special '#$' prefixed substitution keys.
126 | * Note that plist queries can only access the top level entries, and then only string, number and bool
127 | * types. Any query that fails is passed to the script as an empty string rather than failing this call.
128 | *
129 | * If the script does not complete before the specified timeout elapses, it is terminated.
130 | *
131 | * An example script:
132 | *
133 | * # MyScript.sh
134 | * echo "Script $0 run with scriptCommand $1, resourcePath $2, user $3"
135 | * echo "script is exiting"
136 | *
137 | * This could be run using the following:
138 | *
139 | * PrivilegedScriptRunner isr("MyScript.sh"); // create an object to run the specified script
140 | * isr.authorise(); // get authorisation
141 | * isr.start("a command", "#$RESOURCE_PATH", "#$=USER"); // start the script running (in the background)
142 | * isr.wait(); // wait for it to complete
143 | * isr.deauthorise(); // tear down the authorisation rights
144 | *
145 | */
146 | bool start(const char* arg1=0, const char* arg2=0, const char* arg3=0, const char* arg4=0, const char* arg5=0, const char* arg6=0, const char* arg7=0, const char* arg8=0)
147 | {
148 | return startScript(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
149 | }
150 |
151 |
152 | /** Test if the script is running.
153 | *
154 | * @return Logical true if the script is active, false otherwise.
155 | */
156 | bool isRunning();
157 |
158 |
159 | /** Wait for the script to finish running (or return immediately if no script is active).
160 | */
161 | void wait();
162 |
163 |
164 | /** Stop the script running.
165 | *
166 | * This terminates the script via kill. It does nothing if no script is current active.
167 | */
168 | void stop()
169 | {
170 | return stopScript();
171 | }
172 |
173 |
174 | private:
175 |
176 | CFStringRef m_scriptName; //!< The script to run.
177 | CFStringRef m_scriptLabel; //!< The label associated with the current script.
178 | CFStringRef m_stdoutPath; //!< The stdout re-direct, or nil if none
179 | CFStringRef m_stderrPath; //!< The stderr re-direct, or nil if none
180 | AuthorizationRef m_auth; //!< The system authorisation.
181 | bool m_haveAuth; //!< Logical true if we have authorisation.
182 | int m_timeoutSecs; //!< The script timeout.
183 | bool m_scriptIsActive; //!< Logical true if a script is active.
184 | pid_t m_scriptPid; //!< The PID associated with the current script.
185 |
186 | bool startScript(const char* arg1, const char* arg2, const char* arg3, const char* arg4, const char* arg5, const char* arg6, const char* arg7, const char* arg8);
187 | void stopScript();
188 | void cleanupScript();
189 | bool readLine(std::string& str, int fd, unsigned timeoutSeconds);
190 | CFStringRef urlToAbsPath(CFURLRef url);
191 |
192 | PrivilegedScriptRunner(const PrivilegedScriptRunner&); // prevent use
193 | PrivilegedScriptRunner& operator=(const PrivilegedScriptRunner&); // prevent use
194 | };
195 |
196 | } // namespace
197 |
198 | #endif // COM_TSONIQ_ts_drivershieldinstaller_H
199 |
--------------------------------------------------------------------------------
/source/displayxfb/DisplayXFB-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleGetInfoString
10 | DisplayX FrameBuffer v1.1.0
11 | CFBundleIdentifier
12 | com.tsoniq.driver.DisplayXFB
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | ${PRODUCT_NAME}
17 | CFBundlePackageType
18 | KEXT
19 | CFBundleShortVersionString
20 | 1.1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | IOKitPersonalities
26 |
27 | com_tsoniq_driver_DisplayXFB
28 |
29 | CFBundleIdentifier
30 | com.tsoniq.driver.DisplayXFB
31 | DisplayXFB_DisplayCount
32 | 1
33 | DisplayXFB_DisplayName
34 | DisplayX
35 | DisplayXFB_VRAMSize
36 | 67108864
37 | IOClass
38 | com_tsoniq_driver_DisplayXFBDriver
39 | IOMatchCategory
40 | com_tsoniq_driver_DisplayXFBDriver
41 | IOProviderClass
42 | IOResources
43 | IOResourceMatch
44 | IOKit
45 | IOUserClientClass
46 | com_tsoniq_driver_DisplayXFBUserClient
47 |
48 |
49 | NSHumanReadableCopyright
50 | Copyright © 2011-2014 tSoniq. All rights reserved.
51 | OSBundleLibraries
52 |
53 | com.apple.iokit.IOGraphicsFamily
54 | 1.3
55 | com.apple.kpi.iokit
56 | 8.0.0
57 | com.apple.kpi.libkern
58 | 8.0.0
59 | com.apple.kpi.mach
60 | 8.0.0
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/source/displayxfb/DisplayXFB-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header for all source files of the 'com_tsoniq_driver_DisplayXFB' target in the 'com_tsoniq_driver_DisplayXFB' project
3 | //
4 |
5 |
--------------------------------------------------------------------------------
/source/displayxfb/DisplayXFBAccelerator.cc:
--------------------------------------------------------------------------------
1 | /** @file DisplayXFBAccelerator.cc
2 | * @brief IOAccelerator implementation.
3 | *
4 | * Copyright (c) 2011 tSoniq. All rights reserved.
5 | */
6 |
7 | #include "DisplayXFBAccelerator.h"
8 |
9 |
10 | #pragma mark -
11 | #pragma mark Miscellaneous Support
12 |
13 |
14 | // Console logging macro.
15 | #if 0
16 | #define TSLog(fmt, args...) do { IOLog("AC%p: %s: " fmt "\n", this, __PRETTY_FUNCTION__, ## args); } while (false)
17 | #define TSTrace() do { IOLog("AC%p: %s\n", this, __PRETTY_FUNCTION__); }while (false)
18 | #else
19 | #define TSLog(args...) do { } while (false)
20 | #define TSTrace() do { } while (false)
21 | #endif
22 |
23 |
24 |
25 |
26 | #pragma mark -
27 | #pragma mark DisplayXFBAccelerator (IOService methods)
28 |
29 |
30 |
31 |
32 | #define super IOAccelerator
33 | OSDefineMetaClassAndStructors(DisplayXFBAccelerator, IOAccelerator);
34 |
35 |
36 |
37 | /** Object initialisation.
38 | */
39 | bool DisplayXFBAccelerator::init(OSDictionary* dictionary)
40 | {
41 | TSTrace();
42 |
43 | // Perform superclass initialisation.
44 | if (!super::init(dictionary)) return false;
45 |
46 | // Do local initialisation here.
47 | return true;
48 | }
49 |
50 |
51 | /** Object release.
52 | */
53 | void DisplayXFBAccelerator::free()
54 | {
55 | TSTrace();
56 |
57 | // Do local teardown here.
58 | super::free();
59 | }
60 |
61 |
62 |
63 | /** Start the driver.
64 | */
65 | bool DisplayXFBAccelerator::start(IOService* provider)
66 | {
67 | TSTrace();
68 |
69 | // Initialise the provider.
70 | if (!super::start(provider)) return false;
71 |
72 | // Handle local start here.
73 | int pathLen = sizeof m_types - 1;
74 | if (!getPath(m_types, &pathLen, gIOServicePlane))
75 | {
76 | m_types[0] = 0;
77 | }
78 | else
79 | {
80 | if (pathLen < 0) pathLen = 0;
81 | else if ((size_t)pathLen >= sizeof m_types) pathLen = sizeof m_types - 1;
82 | m_types[pathLen] = 0;
83 | }
84 |
85 | /* Set up the registry keys. These are only vaguely documented by Apple (ie: only their existence is noted
86 | * and there is little or no information about usage).
87 | *
88 | * Registry keys for MacBook with GMA950 10.6.8:
89 | *
90 | * KEY SYSTEM VALUE
91 | * AccelCaps: MB 10.6.8 (GMA950) 0x03 QGL|MIPMAP
92 | * MBP 10.7.2 (9400,9600) 0x0b QEX|QGL|MIPMAP
93 | *
94 | * IOAccelRevision: 0x02
95 | *
96 | * IODVDBundleName: MB 10.6.8 (GMA950) AppleIntelGMA950VADriver
97 | * MBP 10.7.2 (9400,9600) GeForceVADriver
98 | *
99 | * IOGLBundleName: MB 10.6.8 (GMA950) eAppleIntelGMA950GLDriver
100 | * MBP 10.7.2 (9400,9600) GeForceGLDriver
101 | *
102 | * IOVABundleName: MB 10.6.8 (GMA950) (not present)
103 | * MBP 10.7.2 (9400,9600) GeForceVADriver (this is sometimes different from IODVDBundleName)
104 | *
105 | * IOVARendererID: MB 10.6.8 (GMA950) (not present)
106 | * MBP 10.7.2 (9400,9600) { 0x01040002, 0x01040004 } on each accelerator for { 9600MGT, 9400M } respectively
107 | *
108 | * Some drivers (GeForce variants) set IOVABundleName to "AppleVADriver" (seemingly a generic driver) and
109 | * IOGLBundleName to "Unknown". There was apparently a generic IODVDBundleName "AppleAltiVecDVDDriver", but this is obviously
110 | * not present in 10.7 and appears to have no direct Intel equivalent.
111 | *
112 | * Other keys that may be needed:
113 | *
114 | * IOClass: the driver class name
115 | *
116 | * For information on these, see the IOKIt/graphics source code release.
117 | *
118 | */
119 |
120 | // The follow properties are required on all OS tested.
121 | setProperty("IOClass", DisplayXFBAcceleratorClassName);
122 | setProperty("AccelCaps", (unsigned long long)getAccelCaps(), 32);
123 | setProperty("IOAccelRevision", (unsigned long long)getAccelRevision(), 32);
124 |
125 | // The following properties empirically fix a DVD player crash bug on 10.7.0 to 10.7.2 (this
126 | // appears to be a bug in the Apple GPU drivers, unrelated to this driver - the system gets
127 | // confused about which accelerators are attached to which displays).
128 | setProperty("IOGLBundleName", "");
129 | setProperty("IOVABundleName", "AppleVADriver");
130 | setProperty("IODVDBundleName", "AppleVADriver");
131 | setProperty("IOVARendererID", (unsigned long long)0xa5020001, 32);
132 |
133 | return true;
134 | }
135 |
136 |
137 | /** Stop the driver.
138 | */
139 | void DisplayXFBAccelerator::stop(IOService* provider)
140 | {
141 | TSTrace();
142 |
143 | // Handle local stop here.
144 |
145 | super::stop(provider);
146 | }
147 |
148 |
--------------------------------------------------------------------------------
/source/displayxfb/DisplayXFBAccelerator.h:
--------------------------------------------------------------------------------
1 | /** @file DisplayXFBAccelerator.h
2 | * @brief IOAccelerator implementation.
3 | *
4 | * Copyright (c) 2011 tSoniq. All rights reserved.
5 | */
6 |
7 | #ifndef COM_TSONIQ_DisplayXFBAccelerator_H
8 | #define COM_TSONIQ_DisplayXFBAccelerator_H (1)
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include "DisplayXFBNames.h"
15 | #include "DisplayXFBFramebuffer.h"
16 | #include "DisplayXFBShared.h"
17 |
18 | /** The accelerator object. A single accelerator is shared between all framebuffers and it
19 | * would normally provide kernel-level services to the user-mode GA plugin. In our case,
20 | * it really exists only to allow the plugin to be loaded - any subsequent attempt to use
21 | * the GA to create an accelerated surface (via a system user-client) will result in an
22 | * error being returned to the caller.
23 | */
24 | class DisplayXFBAccelerator : public IOAccelerator
25 | {
26 | OSDeclareDefaultStructors(DisplayXFBAccelerator);
27 |
28 | public:
29 |
30 | // IOService methods.
31 |
32 | virtual bool init(OSDictionary *dictionary = 0);
33 | virtual void free();
34 | virtual bool start(IOService* provider);
35 | virtual void stop(IOService* provider);
36 |
37 | // Local implementation.
38 | unsigned getAccelCaps() { return 0x03; } //! Return the value for IOAccelCaps
39 | unsigned getAccelRevision() { return kCurrentGraphicsInterfaceRevision; } //! Return the value for IOAccelRevision
40 | const char* getAccelTypes() { return m_types; } //! Return the accelerator types string
41 |
42 | private:
43 |
44 | char m_types[512];
45 |
46 | DisplayXFBAccelerator(const DisplayXFBAccelerator&); // Prevent copy constructor
47 | DisplayXFBAccelerator& operator=(const DisplayXFBAccelerator&); // Prevent assignment
48 | };
49 |
50 |
51 | #define DisplayXFBAcceleratorStringify0(str) #str
52 | #define DisplayXFBAcceleratorStringify1(str) DisplayXFBAcceleratorStringify0(str)
53 | #define DisplayXFBAcceleratorClassName DisplayXFBAcceleratorStringify1(DisplayXFBAccelerator) // The mangled class name as c-string
54 |
55 | #endif // COM_TSONIQ_DisplayXFBAccelerator_H
56 |
--------------------------------------------------------------------------------
/source/displayxfb/DisplayXFBDriver.cc:
--------------------------------------------------------------------------------
1 | /** @file DisplayXFBDriver.cc
2 | * @brief The driver object to which the user-client is bound.
3 | *
4 | * Copyright (c) 2010 tsoniq. All rights reserved.
5 | */
6 |
7 | #include "DisplayXFBDriver.h"
8 | #include "DisplayXFBFramebuffer.h"
9 | #include "DisplayXFBPowerState.h"
10 |
11 | using namespace ts;
12 |
13 | #pragma mark -
14 | #pragma mark Miscellaneous Support
15 |
16 |
17 | // Console logging macro.
18 | #if 0
19 | #define TSLog(fmt, args...) do { IOLog("DR%p: %s: " fmt "\n", this, __PRETTY_FUNCTION__, ## args); } while (false)
20 | #define TSTrace() do { IOLog("DR%p: %s\n", this, __PRETTY_FUNCTION__); }while (false)
21 | #else
22 | #define TSLog(args...) do { } while (false)
23 | #define TSTrace() do { } while (false)
24 | #endif
25 |
26 |
27 |
28 |
29 | #pragma mark -
30 | #pragma mark DisplayXFBDriver (IOService methods)
31 |
32 |
33 |
34 |
35 | #define super IOService
36 | OSDefineMetaClassAndStructors(DisplayXFBDriver, IOService);
37 |
38 |
39 | /** Demo expiry date (as seconds since 1st Jan 1970).
40 | * See http://unixtime-converter.com for an online converter.
41 | */
42 | #ifndef DISPLAYX_EXPIRY_EPOCH
43 | //#define DISPLAYX_EXPIRY_EPOCH (1320105599u + 273u) // 2011-10-31 23:59:59 + a small offset for mysteriosity
44 | #endif
45 |
46 |
47 | /** Object initialisation.
48 | */
49 | bool DisplayXFBDriver::init(OSDictionary* dictionary)
50 | {
51 | TSTrace();
52 |
53 | // Initialise local variables with default (constructor).
54 | m_displayCount = 0;
55 | m_vramSize = 0;
56 | memset(m_displayName, 0, sizeof m_displayName);
57 | m_accelerator = 0;
58 | for (unsigned i = 0; i < sizeof m_framebuffers / sizeof m_framebuffers[0]; i++) m_framebuffers[i] = 0;
59 | for (unsigned i = 0; i < sizeof m_clients / sizeof m_clients[0]; i++) m_clients[i] = 0;
60 |
61 |
62 | // Validate expiry date. Very crude protection against permanent use.
63 | #ifdef DISPLAYX_EXPIRY_EPOCH
64 | clock_sec_t secs = 0;
65 | clock_usec_t microsecs = 0;
66 | clock_get_calendar_microtime(&secs, µsecs);
67 | if (secs >= DISPLAYX_EXPIRY_EPOCH)
68 | {
69 | IOLog("The DisplayX driver %u.%u evaluation period has expired. Contact support@tsoniq for information.\n",
70 | (unsigned)DisplayXFBInfo::kVersionMajor, (unsigned)DisplayXFBInfo::kVersionMinor);
71 | return false;
72 | }
73 | else
74 | {
75 | const unsigned days = (unsigned)((DISPLAYX_EXPIRY_EPOCH - secs) / 86400);
76 | IOLog("DisplayX driver %u.%u evaluation. %u days remaining. Copyright (c) 2010-2014 tSoniq. All Rights Reserved.\n",
77 | (unsigned)DisplayXFBInfo::kVersionMajor, (unsigned)DisplayXFBInfo::kVersionMinor, days);
78 | }
79 | #endif
80 |
81 |
82 | // Perform superclass initialisation.
83 | if (!super::init(dictionary)) return false;
84 |
85 |
86 | // Read the configuration to use from the plist entries.
87 | getPropertyU32(&m_displayCount, kDisplayXFBKeyDisplayCount, 1, kDisplayXFBMaxDisplays, 1);
88 | getPropertyU32(&m_vramSize, kDisplayXFBKeyVRAMSize, kDisplayXFBMinVRAMSize, kDisplayXFBMaxVRAMSize, kDisplayXFBDefaultVRAMSize);
89 | getPropertyStr(m_displayName, sizeof m_displayName, kDisplayXFBKeyDisplayName, "DisplayX");
90 |
91 | // Round the VRAM size to the next higher number of MBytes.
92 | m_vramSize = ((m_vramSize + 0xfffff) >> 20) << 20;
93 |
94 | return true;
95 | }
96 |
97 |
98 | /** Object release.
99 | */
100 | void DisplayXFBDriver::free()
101 | {
102 | TSTrace();
103 |
104 | for (unsigned i = 0; i < sizeof m_clients / sizeof m_clients[0]; i++)
105 | {
106 | if (m_clients[i] != 0) { IOLog("Driver being freed with open client(s)\n"); break; }
107 | }
108 |
109 | super::free();
110 | }
111 |
112 |
113 | /** Probe request.
114 | */
115 | IOService* DisplayXFBDriver::probe(IOService* provider, SInt32* score)
116 | {
117 | TSTrace();
118 | IOService* service = super::probe(provider, score);
119 | return service;
120 | }
121 |
122 |
123 | /** Start the driver.
124 | *
125 | * The driver is loaded automatically at boot-time. At this point we can parse the info.plist
126 | * settings to determine our configuration and create any framebuffer objects as required.
127 | *
128 | * @param provider The provider service.
129 | * @return Logical true for success, false if we are unable to start.
130 | */
131 | bool DisplayXFBDriver::start(IOService* provider)
132 | {
133 | TSTrace();
134 |
135 | // Initialise the provider.
136 | if (!super::start(provider)) return false;
137 |
138 |
139 | if (m_displayCount > sizeof m_framebuffers / sizeof m_framebuffers[0])
140 | {
141 | TSLog("Broken display count - got %u but max %u", m_displayCount, (unsigned)(sizeof m_framebuffers / sizeof m_framebuffers[0]));
142 | return false;
143 | }
144 |
145 | // Create the accelerator.
146 | m_accelerator = OSTypeAlloc(com_tsoniq_driver_DisplayXFBAccelerator);
147 | if (!m_accelerator)
148 | {
149 | TSLog("Failed to create accelerator");
150 | }
151 | else
152 | {
153 | // Must attach before calling start() (so that start can see the registry definitions)
154 | if (!m_accelerator->init(0) || !m_accelerator->attach(this) || !m_accelerator->start(this))
155 | {
156 | TSLog("Failed to initialise/start accelerator");
157 | m_accelerator->release();
158 | m_accelerator = 0;
159 | }
160 | }
161 |
162 |
163 | // Create nubs for each requested display. Each nub is an instance of an IOFramebuffer object.
164 | for (unsigned index = 0; index < m_displayCount; index++)
165 | {
166 | com_tsoniq_driver_DisplayXFBFramebuffer* nub = OSTypeAlloc(com_tsoniq_driver_DisplayXFBFramebuffer);
167 | if (!nub) { TSLog("Failed to create driver nub"); continue; }
168 | TSLog("Create framebuffer nub @ %p", nub);
169 |
170 | if (!nub->init(0))
171 | {
172 | TSLog("Failed to initialise nub");
173 | nub->release();
174 | continue;
175 | }
176 |
177 | //TSLog("Attaching nub @ %p", nub);
178 | if (!nub->attach(this))
179 | {
180 | TSLog("Failed to attach nub");
181 | nub->release();
182 | continue;
183 | }
184 |
185 | // Remember the device.
186 | m_framebuffers[index] = nub;
187 |
188 | // Create a display name
189 | char name[sizeof m_displayName + 16];
190 | if (index == 0) strncpy(name, m_displayName, sizeof name); // First display - use base name only
191 | else snprintf(name, sizeof name, "%s %u", m_displayName, index+1); // Additional displays - add index qualifier
192 | nub->setProperty(kDisplayXFBKeyDisplayName, name);
193 |
194 | // Set some keys on the nub (really should be done in the framebuffer, but the API provides no other means to do this).
195 | nub->setProperty(kDisplayXFBKeyDisplayIndex, index, 32); // The display index number (as passed in interface methods)
196 | nub->setProperty(kDisplayXFBKeyVRAMSize, m_vramSize, 32); // The VRAM size
197 |
198 | // Set the nub location (for IOKit search disambiguation)
199 | char location[16];
200 | snprintf(location, sizeof location, "%u", index);
201 | nub->setLocation(location);
202 |
203 | // Start the nub.
204 | if (!nub->start(this))
205 | {
206 | TSLog("Failed to start nub");
207 | nub->release();
208 | continue;
209 | }
210 |
211 | TSLog("Completed set up for nub %s @ %p", name, nub);
212 | nub->registerService(); // Start service matching for the nub
213 |
214 | // Do we need to release the nub here, as it is retained following register service? In any event,
215 | // display card drivers are never unloaded...
216 | }
217 |
218 |
219 | // Configure power management.
220 | TSLog("Config PM");
221 | PMinit();
222 | getProvider()->joinPMtree(this);
223 | registerPowerDriver(this, DisplayXFBDriverPowerStates, kDisplayXFBNumPowerStates);
224 | changePowerStateTo(kDisplayXFBPowerStateWake);
225 |
226 |
227 | // Allow applications to find us now that everything is ready.
228 | registerService();
229 |
230 |
231 | //TSLog("success");
232 | return true;
233 | }
234 |
235 |
236 | /** Stop the driver.
237 | */
238 | void DisplayXFBDriver::stop(IOService* provider)
239 | {
240 | TSTrace();
241 |
242 | TSLog("Stop PM");
243 | PMstop();
244 |
245 | // Release the attached nubs, created in start().
246 | OSIterator* iter = getClientIterator();
247 | while (true)
248 | {
249 | IOService* client = (IOService*)iter->getNextObject();
250 | if (!client) break; // No more clients
251 |
252 | //TSLog("terminate client %p", client);
253 | client->terminate(kIOServiceRequired | kIOServiceTerminate | kIOServiceSynchronous);
254 | client->release();
255 | }
256 | iter->release();
257 |
258 | super::stop(provider);
259 | }
260 |
261 |
262 | /** Handle power state management.
263 | */
264 | IOReturn DisplayXFBDriver::setPowerState(unsigned long whichState, IOService* whatDriver)
265 | {
266 | (void)whichState;
267 | (void)whatDriver;
268 | TSLog("state %u", (unsigned)whichState);
269 | return IOPMAckImplied;
270 | }
271 |
272 |
273 | /** Handle open() requests. This is called in response to a new user-client invoking provider->open().
274 | */
275 | bool DisplayXFBDriver::handleOpen(IOService* forClient, IOOptionBits options, void* arg)
276 | {
277 | (void)options;
278 | (void)arg;
279 |
280 | if (0 == forClient)
281 | {
282 | TSLog("Open with null client denied");
283 | return false;
284 | }
285 | else if (handleIsOpen(forClient))
286 | {
287 | TSLog("Duplicate client-open request");
288 | return false;
289 | }
290 | else
291 | {
292 | TSLog("Open client");
293 | for (unsigned i = 0; i < sizeof m_clients / sizeof m_clients[0]; i++)
294 | {
295 | if (0 == m_clients[i])
296 | {
297 | m_clients[i] = forClient;
298 | return true;
299 | }
300 | }
301 | TSLog("Too many clients open");
302 | return false;
303 | }
304 | }
305 |
306 |
307 | /** Handle close() requests.
308 | */
309 | void DisplayXFBDriver::handleClose(IOService* forClient, IOOptionBits options)
310 | {
311 | TSTrace();
312 | (void)options;
313 | for (unsigned i = 0; i < sizeof m_clients / sizeof m_clients[0]; i++)
314 | {
315 | if (forClient == m_clients[i]) m_clients[i] = 0;
316 | }
317 | }
318 |
319 |
320 | /** Check if a session is open for a given client.
321 | */
322 | bool DisplayXFBDriver::handleIsOpen(const IOService* forClient) const
323 | {
324 | TSTrace();
325 | for (unsigned i = 0; i < sizeof m_clients / sizeof m_clients[0]; i++)
326 | {
327 | if (forClient == m_clients[i]) return true;
328 | }
329 | return false;
330 | }
331 |
332 |
333 |
334 |
335 | #pragma mark -
336 | #pragma mark Local Helper Methods
337 |
338 |
339 |
340 | /** Get the framebuffer for a specific display index number.
341 | *
342 | * @param displayIndex The display number.
343 | * @return A reference to the framebufer for the display, or zero if not found. The returned
344 | * nub is *not* retained and should not be released by the caller.
345 | */
346 | class com_tsoniq_driver_DisplayXFBFramebuffer* DisplayXFBDriver::indexToFramebuffer(unsigned displayIndex) const
347 | {
348 | return (displayIndex < sizeof m_framebuffers / sizeof m_framebuffers[0]) ? m_framebuffers[displayIndex] : 0;
349 | }
350 |
351 |
352 |
353 | /** Get the display index number for a given framebuffer.
354 | *
355 | * @param framebuffer The framebuffer instance.
356 | * @return The corresponding display index, or kDisplayXFBMaxClients if not found.
357 | */
358 | unsigned DisplayXFBDriver::framebufferToIndex(const com_tsoniq_driver_DisplayXFBFramebuffer* framebuffer) const
359 | {
360 | unsigned displayIndex = 0;
361 | while (displayIndex < kDisplayXFBMaxDisplays)
362 | {
363 | if (m_framebuffers[displayIndex] == framebuffer) break;
364 | displayIndex ++;
365 | }
366 | return displayIndex;
367 | }
368 |
369 |
370 |
371 |
372 | /** Helper function used to read property keys, checking for errors and applying range limiting.
373 | *
374 | * @param value The value to assign.
375 | * @param key The key.
376 | * @param minValue The minimum permitted value. Inclusive.
377 | * @param maxValue The maximum permitted value. Inclusive.
378 | * @param defValue The default value to use if the key is not found.
379 | * @return True for success, false if a default or limited value has been applied.
380 | */
381 | bool DisplayXFBDriver::getPropertyU32(unsigned* value, const char* key, unsigned minValue, unsigned maxValue, unsigned defValue) const
382 | {
383 | bool ok;
384 | unsigned result;
385 |
386 | OSNumber* number = OSDynamicCast(OSNumber, getProperty(key));
387 | if (number) { result = number->unsigned32BitValue(); ok = true; }
388 | else { result = defValue; ok = false; }
389 |
390 | if (result < minValue) { result = minValue; ok = false; }
391 | else if (result > maxValue) { result = maxValue; ok = false; }
392 |
393 | if (!ok) TSLog("Invalid property %s: applying value %u", key, result);
394 | *value = result;
395 | return ok;
396 | }
397 |
398 |
399 |
400 | /** Helper function used to read property keys, checking for errors and applying range limiting.
401 | *
402 | * @param str The cString to receive the result.
403 | * @param size The storage space for the string (including null terminator).
404 | * @param key The key.
405 | * @param defValue The default value to use if the key is not found.
406 | * @return True for success, false if a default or limited value has been applied.
407 | */
408 | bool DisplayXFBDriver::getPropertyStr(char* str, size_t size, const char* key, const char* defValue) const
409 | {
410 | bool ok = false;
411 |
412 | if (size == 0)
413 | {
414 | // No space for anything.
415 | }
416 | else if (size == 1)
417 | {
418 | // Space only for a terminator
419 | str[0] = 0;
420 | }
421 | else
422 | {
423 | if (!defValue) defValue = "";
424 |
425 | OSString* osString = OSDynamicCast(OSString, getProperty(key));
426 | const char* cString = (osString) ? (osString->getCStringNoCopy()) : 0;
427 | if (cString) ok = true;
428 | else cString = defValue;
429 |
430 | memset(str, 0, size);
431 | strncpy(str, cString, size);
432 | str[size - 1] = 0;
433 |
434 | ok = ok && (strlen(str) == strlen(cString));
435 | }
436 |
437 | if (!ok) TSLog("Invalid property %s: applying value \"%s\"", key, (size != 0) ? str : "");
438 | return ok;
439 | }
440 |
441 |
442 |
443 | #pragma mark -
444 | #pragma mark User Client Methods
445 |
446 |
447 |
448 |
449 | /** Open a new session.
450 | *
451 | * @param info Returns the driver info structure.
452 | * @return An IOReturn code.
453 | * @note The user client will first open() this driver, resulting in the handleOpen() methods being
454 | * being run before this one.
455 | */
456 | IOReturn DisplayXFBDriver::userClientOpen(DisplayXFBInfo* info)
457 | {
458 | TSTrace();
459 | if (!info) return kIOReturnBadArgument;
460 |
461 | info->initialise(m_displayCount);
462 | return kIOReturnSuccess;
463 | }
464 |
465 |
466 | /** Close an existing session.
467 | *
468 | * @return An IOReturn code.
469 | * @note The user client will call close() after this call, regardless of the return status.
470 | */
471 | IOReturn DisplayXFBDriver::userClientClose()
472 | {
473 | TSTrace();
474 | return kIOReturnSuccess;
475 | }
476 |
477 |
478 | /** Query the current display information.
479 | *
480 | * @param config Returns the display structure.
481 | * @param displayIndex The display index.
482 | * @return An IOReturn code.
483 | */
484 | IOReturn DisplayXFBDriver::userClientGetConfiguration(DisplayXFBConfiguration* config, unsigned displayIndex)
485 | {
486 | TSTrace();
487 | if (!config) return kIOReturnBadArgument;
488 |
489 | com_tsoniq_driver_DisplayXFBFramebuffer* device = indexToFramebuffer(displayIndex);
490 | if (!device) return kIOReturnNotFound;
491 |
492 | return device->userClientGetConfiguration(config);
493 | }
494 |
495 |
496 | /** Set the current display information.
497 | *
498 | * @param config Supplies the display structure. On return, contains updated data.
499 | * @param displayIndex The display index.
500 | * @return An IOReturn code.
501 | */
502 | IOReturn DisplayXFBDriver::userClientSetConfiguration(const DisplayXFBConfiguration* config, unsigned displayIndex)
503 | {
504 | TSTrace();
505 | if (!config) return kIOReturnBadArgument;
506 |
507 | com_tsoniq_driver_DisplayXFBFramebuffer* device = indexToFramebuffer(displayIndex);
508 | if (!device) return kIOReturnNotFound;
509 |
510 | return device->userClientSetConfiguration(config);
511 | }
512 |
513 |
514 | /** Query the current display information.
515 | *
516 | * @param state Returns the display structure.
517 | * @param displayIndex The display index.
518 | * @return An IOReturn code.
519 | */
520 | IOReturn DisplayXFBDriver::userClientGetState(DisplayXFBState* state, uint32_t displayIndex)
521 | {
522 | TSTrace();
523 | if (!state) return kIOReturnBadArgument;
524 |
525 | com_tsoniq_driver_DisplayXFBFramebuffer* device = indexToFramebuffer(displayIndex);
526 | if (!device) return kIOReturnNotFound;
527 |
528 | return device->userClientGetState(state);
529 | }
530 |
531 |
532 | /** Connect a display.
533 | *
534 | * @param displayIndex The display index.
535 | * @return An IOReturn code.
536 | */
537 | IOReturn DisplayXFBDriver::userClientConnect(unsigned displayIndex)
538 | {
539 | TSTrace();
540 | com_tsoniq_driver_DisplayXFBFramebuffer* device = indexToFramebuffer(displayIndex);
541 | if (!device) return kIOReturnNotFound;
542 |
543 | return device->userClientConnect();
544 | }
545 |
546 |
547 | /** Connect a display.
548 | *
549 | * @param displayIndex The display index.
550 | * @return An IOReturn code.
551 | */
552 | IOReturn DisplayXFBDriver::userClientDisconnect(unsigned displayIndex)
553 | {
554 | TSTrace();
555 | com_tsoniq_driver_DisplayXFBFramebuffer* device = indexToFramebuffer(displayIndex);
556 | if (!device) return kIOReturnNotFound;
557 |
558 | return device->userClientDisconnect();
559 | }
560 |
561 |
562 | /** Map the shared data in to a client's address space.
563 | *
564 | * @param readOnly Logical true to create a read-only mapping.
565 | * @param task The task to map the data to.
566 | * @param displayIndex The display index.
567 | * @param mapType The object to be mapped.
568 | * @return An IOMemoryMap for the data, or zero if failed. Ownership of the object
569 | * is passed to the caller (releasing it will remove the mapping).
570 | */
571 | IOMemoryMap* DisplayXFBDriver::userClientMapInTask(bool readOnly, task_t task, unsigned displayIndex, unsigned mapType)
572 | {
573 | TSTrace();
574 | com_tsoniq_driver_DisplayXFBFramebuffer* device = indexToFramebuffer(displayIndex);
575 | return (device) ? device->userClientMapInTask(readOnly, task, mapType) : 0;
576 | }
577 |
578 |
579 |
580 | /** Check if a display index references a display.
581 | *
582 | * @param displayIndex The display number.
583 | * @return Logical true if the index maps to a display, false if no display matches the index.
584 | */
585 | bool DisplayXFBDriver::validateDisplayIndex(unsigned displayIndex) const
586 | {
587 | return 0 != indexToFramebuffer(displayIndex);
588 | }
589 |
590 |
591 | #pragma mark -
592 | #pragma mark Framebuffer Services
593 |
594 | /** Send a notification.
595 | *
596 | * @param code The notification code (kDisplayXFBNotificationXyz).
597 | * @param fb The framebuffer issuing the notification.
598 | */
599 | void DisplayXFBDriver::sendNotification(uint32_t code, const com_tsoniq_driver_DisplayXFBFramebuffer* framebuffer)
600 | {
601 | unsigned displayIndex = framebufferToIndex(framebuffer);
602 | if (displayIndex >= kDisplayXFBMaxDisplays)
603 | {
604 | TSLog("Invalid framebuffer");
605 | }
606 | else
607 | {
608 | switch (code)
609 | {
610 | case kDisplayXFBNotificationDisplayState:
611 | case kDisplayXFBNotificationCursorState:
612 | case kDisplayXFBNotificationCursorImage:
613 | messageClients(code, (void*)(uintptr_t)displayIndex);
614 | break;
615 |
616 | default:
617 | // Ignore: invalid code.
618 | TSLog("Invalid code");
619 | break;
620 | }
621 | }
622 | }
623 |
--------------------------------------------------------------------------------
/source/displayxfb/DisplayXFBDriver.h:
--------------------------------------------------------------------------------
1 | /** @file DisplayXFBDriver.h
2 | * @brief The driver object to which the user-client is bound.
3 | *
4 | * Copyright (c) 2010 tsoniq. All rights reserved.
5 | */
6 |
7 | #ifndef COM_TSONIQ_DisplayXFBDriver_H
8 | #define COM_TSONIQ_DisplayXFBDriver_H (1)
9 |
10 | #include
11 | #include
12 | #include "DisplayXFBNames.h"
13 | #include "DisplayXFBFramebuffer.h"
14 | #include "DisplayXFBShared.h"
15 | #include "DisplayXFBAccelerator.h"
16 |
17 | class DisplayXFBDriver : public IOService
18 | {
19 | OSDeclareDefaultStructors(DisplayXFBDriver);
20 |
21 | public:
22 |
23 | // IOService methods.
24 |
25 | virtual bool init(OSDictionary *dictionary = 0);
26 | virtual void free();
27 | virtual IOService* probe(IOService* provider, SInt32* score);
28 | virtual bool start(IOService* provider);
29 | virtual void stop(IOService* provider);
30 | virtual IOReturn setPowerState(unsigned long whichState, IOService* whatDriver);
31 |
32 | virtual bool handleOpen(IOService* forClient, IOOptionBits options, void* arg); // IOService override needed for multiple clients
33 | virtual void handleClose(IOService* forClient, IOOptionBits options); // IOService override needed for multiple clients
34 | virtual bool handleIsOpen(const IOService* forClient) const; // IOService override needed for multiple clients
35 |
36 | // User client and support methods. Mostly these just re-direct to the frame buffer methods.
37 | IOReturn userClientOpen(ts::DisplayXFBInfo* info);
38 | IOReturn userClientClose();
39 | IOReturn userClientGetConfiguration(ts::DisplayXFBConfiguration* config, unsigned displayIndex);
40 | IOReturn userClientSetConfiguration(const ts::DisplayXFBConfiguration* config, unsigned displayIndex);
41 | IOReturn userClientGetState(ts::DisplayXFBState* state, uint32_t displayIndex);
42 | IOReturn userClientConnect(unsigned displayIndex);
43 | IOReturn userClientDisconnect(unsigned displayIndex);
44 | IOMemoryMap* userClientMapInTask(bool readOnly, task_t task, unsigned displayIndex, unsigned mapType);
45 | bool validateDisplayIndex(unsigned displayIndex) const;
46 |
47 | // Methods that are used from com_tsoniq_driver_DisplayXFBFramebuffer.
48 | unsigned vramSize() const { return m_vramSize; } //! Return the VRAM size (bytes)
49 | unsigned framebufferToIndex(const com_tsoniq_driver_DisplayXFBFramebuffer* framebuffer) const;
50 | void sendNotification(uint32_t code, const com_tsoniq_driver_DisplayXFBFramebuffer* framebuffer);
51 | com_tsoniq_driver_DisplayXFBAccelerator* getAccelerator() { return m_accelerator; } //! Return the accelerator handle, or zero if not available
52 |
53 | private:
54 |
55 | unsigned m_displayCount; //! The number of display nubs that were created
56 | unsigned m_vramSize; //! The VRAM size (bytes)
57 | char m_displayName[32]; //! The display name
58 | com_tsoniq_driver_DisplayXFBAccelerator* m_accelerator; //! The accelerator, or zero if not available
59 | com_tsoniq_driver_DisplayXFBFramebuffer* m_framebuffers[ts::kDisplayXFBMaxDisplays]; //! Array of one frame buffer per display
60 | IOService* m_clients[ts::kDisplayXFBMaxClients]; //! Array of currently attached user-clients
61 |
62 | com_tsoniq_driver_DisplayXFBFramebuffer* indexToFramebuffer(unsigned displayIndex) const;
63 | bool getPropertyU32(unsigned* value, const char* key, unsigned minValue, unsigned maxValue, unsigned defValue) const;
64 | bool getPropertyStr(char* str, size_t size, const char* key, const char* defValue) const;
65 |
66 | DisplayXFBDriver(const DisplayXFBDriver&); // Prevent copy constructor
67 | DisplayXFBDriver& operator=(const DisplayXFBDriver&); // Prevent assignment
68 | };
69 |
70 | #endif // COM_TSONIQ_DisplayXFBDriver_H
71 |
--------------------------------------------------------------------------------
/source/displayxfb/DisplayXFBFramebuffer.h:
--------------------------------------------------------------------------------
1 | /** @file DisplayXFBFramebuffer.h
2 | * @brief Virtual display driver for OSX.
3 | * @copyright Copyright (c) 2010 tsoniq. All Rights Reserved.
4 | */
5 |
6 | #ifndef COM_TSONIQ_DisplayXFBFramebuffer_H
7 | #define COM_TSONIQ_DisplayXFBFramebuffer_H
8 |
9 | #include "DisplayXFBNames.h"
10 | #include "DisplayXFBShared.h"
11 | #include "DisplayXFBTiming.h"
12 |
13 | #include
14 | #include
15 | //#include
16 |
17 |
18 |
19 | /** The IOKit driver object for a virtual display driver.
20 | */
21 | class DisplayXFBFramebuffer : public IOFramebuffer
22 | {
23 | OSDeclareDefaultStructors(DisplayXFBFramebuffer);
24 |
25 | public:
26 |
27 | // Methods for IOService.
28 |
29 | virtual bool start(IOService *provider);
30 | virtual void stop(IOService *provider);
31 |
32 | // Methods for IOFramebuffer.
33 |
34 | virtual IOReturn enableController();
35 | virtual IODeviceMemory* getVRAMRange();
36 | virtual UInt32 getApertureSize(IODisplayModeID displayMode, IOIndex depth);
37 | virtual IODeviceMemory* getApertureRange(IOPixelAperture aperture);
38 | virtual IOItemCount getConnectionCount();
39 |
40 | virtual IOReturn setAttribute (IOSelect attribute, uintptr_t value);
41 | virtual IOReturn getAttribute (IOSelect attribute, uintptr_t* value);
42 | virtual IOReturn setAttributeForConnection(IOIndex connectIndex, IOSelect attribute, uintptr_t value);
43 | virtual IOReturn getAttributeForConnection(IOIndex connectIndex, IOSelect attribute, uintptr_t* value);
44 | virtual const char* getPixelFormats();
45 | virtual IOItemCount getDisplayModeCount();
46 | virtual IOReturn getDisplayModes (IODisplayModeID* allDisplayModes);
47 | virtual IOReturn getInformationForDisplayMode (IODisplayModeID displayMode, IODisplayModeInformation* info);
48 | virtual UInt64 getPixelFormatsForDisplayMode (IODisplayModeID displayMode, IOIndex depth);
49 | virtual IOReturn getTimingInfoForDisplayMode(IODisplayModeID displayMode, IOTimingInformation* info);
50 | virtual IOReturn getPixelInformation(IODisplayModeID displayMode, IOIndex depth, IOPixelAperture aperture, IOPixelInformation* pixelInfo);
51 | virtual IOReturn getCurrentDisplayMode (IODisplayModeID* displayMode, IOIndex* depth);
52 | virtual IOReturn getStartupDisplayMode(IODisplayModeID* displayMode, IOIndex* depth);
53 | virtual IOReturn setDisplayMode(IODisplayModeID displayMode, IOIndex depth);
54 | virtual IOReturn setGammaTable(UInt32 channelCount, UInt32 dataCount, UInt32 dataWidth, void* data);
55 | virtual IOReturn registerForInterruptType(IOSelect interruptType, IOFBInterruptProc proc, OSObject* target, void* ref, void** interruptRef);
56 | virtual IOReturn unregisterInterrupt(void *interruptRef);
57 | virtual IOReturn setInterruptState(void* interruptRef, UInt32 state);
58 | virtual IOReturn setCursorImage(void* cursorImage);
59 | virtual IOReturn setCursorState(SInt32 x, SInt32 y, bool visible);
60 |
61 |
62 | virtual bool hasDDCConnect(IOIndex connectIndex);
63 | virtual IOReturn getDDCBlock(IOIndex connectIndex, UInt32 blockNumber, IOSelect blockType, IOOptionBits options, UInt8* data, IOByteCount* length);
64 |
65 | // Methods used for the user-client access.
66 | IOReturn userClientGetConfiguration(ts::DisplayXFBConfiguration* config);
67 | IOReturn userClientSetConfiguration(const ts::DisplayXFBConfiguration* config);
68 | IOReturn userClientGetState(ts::DisplayXFBState* state);
69 | IOReturn userClientConnect();
70 | IOReturn userClientDisconnect();
71 | IOMemoryMap* userClientMapInTask(bool readOnly, task_t task, unsigned mapType);
72 |
73 |
74 | private:
75 |
76 | static const unsigned kFramePadding = 1024; //! Rumour (incorrect?) has it that some padding bytes are needed at the end of the display
77 |
78 | struct InterruptHandler
79 | {
80 | IOFBInterruptProc m_handler; //! Interrupt callback on monitor connect/disconnect (or zero if none)
81 | OSObject* m_object; //! Parameter for connection interrupt handler
82 | void* m_ref; //! Parameter for connection interrupt handler
83 | bool m_enabled; //! Logical true if enabled, false otherwise
84 |
85 | void init()
86 | {
87 | m_handler = 0;
88 | m_object = 0;
89 | m_ref = 0;
90 | m_enabled = false;
91 | }
92 |
93 | void clear()
94 | {
95 | init();
96 | }
97 |
98 | void assign(IOFBInterruptProc proc, OSObject* object, void* ref)
99 | {
100 | m_handler = proc;
101 | m_object = object;
102 | m_ref = ref;
103 | m_enabled = true;
104 | }
105 |
106 | bool isAssigned() const { return 0 != m_handler; }
107 |
108 | void setEnabled(bool enable) { m_enabled = enable; }
109 | bool isEnabled() const { return m_enabled; }
110 |
111 | void fire() const
112 | {
113 | if (isAssigned() && isEnabled()) m_handler(m_object, m_ref);
114 | }
115 | };
116 |
117 | DisplayXFBFramebuffer(const DisplayXFBFramebuffer&); // Prevent copy constructor
118 | DisplayXFBFramebuffer& operator=(const DisplayXFBFramebuffer&); // Prevent assignment
119 |
120 |
121 | class com_tsoniq_driver_DisplayXFBDriver* m_provider; //! The provider class
122 | unsigned m_displayIndex; //! The display index number
123 | unsigned m_vramSize; //! The maximum display width
124 | IOWorkLoop* m_vblankWorkLoop; //! Work loop used for vblank events
125 | IOTimerEventSource* m_vblankTimerEventSource; //! Event source used to emulate vblank timing interrupts
126 | UInt32 m_vblankPeriodUS; //! The vblank interval, in microseconds
127 | com_tsoniq_driver_DisplayXFBTiming m_vblankTiming; //! Timing handler
128 | bool m_vblankTimerIsEnabled; //! Logical true if the timer is enabled
129 | IOBufferMemoryDescriptor* m_displayMemory; //! The framebuffer memory description (the raw RGBA32 pixel array)
130 | IOBufferMemoryDescriptor* m_cursorMemory; //! The cursor state (DisplayXFBCursor)
131 | ts::DisplayXFBCursor* m_cursor; //! The cursor referenced by m_cursorMemory
132 | InterruptHandler m_connectInterruptHandler; //! Handler for connect interrupts
133 | InterruptHandler m_vblankInterruptHandler; //! Handler for VBlank interrupts
134 | ts::DisplayXFBConfiguration m_configuration; //! The current configuration
135 | ts::DisplayXFBState m_state; //! The current state
136 |
137 | static void vblankEventHandler(OSObject* owner, IOTimerEventSource* sender);
138 | void vblankEventEnable(bool enable);
139 |
140 | IOReturn sharedMemoryAlloc(IOBufferMemoryDescriptor** buffer, unsigned size, bool contiguous);
141 | void sharedMemoryFree(IOBufferMemoryDescriptor** buffer);
142 | };
143 |
144 | #endif // COM_TSONIQ_DisplayXFBFramebuffer_H
145 |
--------------------------------------------------------------------------------
/source/displayxfb/DisplayXFBNames.h:
--------------------------------------------------------------------------------
1 | /** @file
2 | * @brief Symbol name remapping for the kernel extension.
3 | * @copyright Copyright (c) 2014 tSoniq. All rights reserved.
4 | */
5 |
6 | #ifndef COM_TSONIQ_DisplayXFBNames_H
7 | #define COM_TSONIQ_DisplayXFBNames_H (1)
8 |
9 | #define DisplayXFBDriver com_tsoniq_driver_DisplayXFBDriver
10 | #define DisplayXFBUserClient com_tsoniq_driver_DisplayXFBUserClient
11 | #define DisplayXFBTiming com_tsoniq_driver_DisplayXFBTiming
12 | #define DisplayXFBDriverPowerStates com_tsoniq_driver_DisplayXFBDriverPowerStates
13 | #define DisplayXFBFramebuffer com_tsoniq_driver_DisplayXFBFramebuffer
14 | #define DisplayXFBDriver com_tsoniq_driver_DisplayXFBDriver
15 | #define DisplayXFBAccelerator com_tsoniq_driver_DisplayXFBAccelerator
16 | #define DisplayXFBInfo com_tsoniq_driver_DisplayXFBInfo
17 | #define DisplayXFBMode com_tsoniq_driver_DisplayXFBMode
18 | #define DisplayXFBState com_tsoniq_driver_DisplayXFBState
19 | #define DisplayXFBConfiguration com_tsoniq_driver_DisplayXFBConfiguration
20 | #define DisplayXFBMap com_tsoniq_driver_DisplayXFBMap
21 | #define DisplayXFBCursor com_tsoniq_driver_DisplayXFBCursor
22 | #define DisplayXEDID com_tsoniq_driver_DisplayXEDID
23 |
24 | #endif // COM_TSONIQ_DisplayXFBNames_H
25 |
--------------------------------------------------------------------------------
/source/displayxfb/DisplayXFBPowerState.cc:
--------------------------------------------------------------------------------
1 | /** @file com_tsoniq_driver_DisplayXFBPowerState.cc
2 | * @brief Power management states.
3 | *
4 | * Copyright (c) 2010 tsoniq. All rights reserved.
5 | */
6 |
7 | #include "DisplayXFBPowerState.h"
8 |
9 | /** The power state structures for the driver.
10 | * See IONDRVFramebuffer for an example.
11 | */
12 | IOPMPowerState DisplayXFBDriverPowerStates[kDisplayXFBNumPowerStates] =
13 | {
14 | // version, capabilityFlags, outputPowerCharacter, inputPowerRequirement (remaining fields unused)
15 | { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
16 | { 1, 0, 0, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 },
17 | { 1, IOPMDeviceUsable, IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 }
18 | };
19 |
--------------------------------------------------------------------------------
/source/displayxfb/DisplayXFBPowerState.h:
--------------------------------------------------------------------------------
1 | /** @file com_tsoniq_driver_DisplayXFBPowerState.h
2 | * @brief Power management states.
3 | *
4 | * Copyright (c) 2010 tsoniq. All rights reserved.
5 | */
6 |
7 | #ifndef COM_TSONIQ_com_tsoniq_driver_DisplayXFBPowerState_H
8 | #define COM_TSONIQ_com_tsoniq_driver_DisplayXFBPowerState_H (1)
9 |
10 | #include
11 | #include "DisplayXFBNames.h"
12 |
13 | // Power modes. These are indexes in to the DisplayXFBDriverPowerStates[] array.
14 | static const unsigned kDisplayXFBPowerStateOff = 0; //! Device is off
15 | static const unsigned kDisplayXFBPowerStateSleep = 1; //! Device is dozy
16 | static const unsigned kDisplayXFBPowerStateWake = 2; //! Device at full power
17 | static const unsigned kDisplayXFBNumPowerStates = 3; //! The total number of power states
18 |
19 |
20 | extern IOPMPowerState DisplayXFBDriverPowerStates[kDisplayXFBNumPowerStates];
21 |
22 | #endif // COM_TSONIQ_com_tsoniq_driver_DisplayXFBPowerState_H
23 |
--------------------------------------------------------------------------------
/source/displayxfb/DisplayXFBShared.h:
--------------------------------------------------------------------------------
1 | /** @file DisplayXFBShared.h
2 | * @brief Frame buffer shared data definitions (between the kernel-mode user-client and the application library).
3 | * @warning Structures and definitions here must be suitable for sharing between user and kernel
4 | * space code and must allow for 64-32 bit issues.
5 | *
6 | * Copyright (c) 2010 tsoniq. All rights reserved.
7 | *
8 | * This file defines the structures shared between the framebuffer and user-land code. The following
9 | * types are used:
10 | *
11 | * DisplayXFBInfo Driver system info (supplied from the driver when first opened)
12 | * DisplayXFBMode Structure describing a display mode (supplied from the user-mode code to the driver)
13 | * DisplayXFBConfiguration Structure supplying a list of modes and additional shared data (from user to driver)
14 | * DisplayXFBState Structure describing the current display state (from driver to user)
15 | * DisplayXFBCursor Structure describing the cursor position and image.
16 | *
17 | * In use, the user opens the driver, returning the info structure. The user then creates a configuration specifying
18 | * a list of display modes and common parameters such as refresh rate and padding information. This is passed to the
19 | * driver in order to connect a virtual display. When the system is running, the user can read both the framebuffer
20 | * memory and the state object to describe the current operating mode. The state can be changed asynchronously by the
21 | * end user, via the monitors system preference pane.
22 | */
23 |
24 | #ifndef COM_TSONIQ_DisplayXFBShared_H
25 | #define COM_TSONIQ_DisplayXFBShared_H (1)
26 |
27 | #include
28 | #include
29 | #include "DisplayXFBNames.h"
30 |
31 | /** Macro used to verify at compile-time that a structure is suitable for kernel-user-mode exchange.
32 | */
33 | #define TSFBVDS_CHECK_STRUCTURE(SNAME) extern char temp_struct_size_check_ ## SNAME[(sizeof(SNAME) % 8) == 0 ? 1 : -1]
34 |
35 | namespace ts
36 | {
37 | // The driver version. This defines the format of structures exchanged between user and kernel land.
38 | // If any structural changes are made, adjust the version number accordingly.
39 | static const uint32_t kDisplayXFBVersionMajor = 2; //! The major version number (change for incompatible changes)
40 | static const uint32_t kDisplayXFBVersionMinor = 1; //! The minor version number (change for bug-fixes or compatible changes)
41 |
42 | // Global limits.
43 | static const unsigned kDisplayXFBMaxDisplays = 4; //! The maximum number of displays that can be used (note that there is also a hard limit of 16 displays due to the use of 16 bit integer bit-masks in some code)
44 | static const unsigned kDisplayXFBMaxClients = 8; //! The maximum number of concurrent user-clients (must be at least 2, for the GA and the client app)
45 | static const unsigned kDisplayXFBMinWidth = 320; //! The minimum display width considered valid
46 | static const unsigned kDisplayXFBMinHeight = 200; //! The minimum display height considered valid
47 | static const unsigned kDisplayXFBDefaultWidth = 1280; //! The default display width
48 | static const unsigned kDisplayXFBDefaultHeight = 800; //! The default display width
49 | static const unsigned kDisplayXFBMaxWidth = 8192; //! The maximum display width considered valid
50 | static const unsigned kDisplayXFBMaxHeight = 8192; //! The maximum display height considered valid
51 | static const unsigned kDisplayXFBWidthQuantise = 8; //! The width must be an integer multiple of this many pixels
52 | static const unsigned kDisplayXFBMinRefresh1616 = 0x00010000; //! The maximum refresh period (16.16 fixed point Hz)
53 | static const unsigned kDisplayXFBMaxRefresh1616 = 0x00800000; //! The maximum refresh period (16.16 fixed point Hz)
54 |
55 | // Limits on the configured VRAM size, to prevent silly allocations.
56 | // A 1280x900 display with 32 bit pixels requires ((1280*4+32)*900+128) ~= 4.5MBytes.
57 | // A 1920x1200 display with 32 bit pixels requires ((1920*4+32)*1200+128) ~= 8.8MBytes.
58 | // A 2560x1600 display with 32 bit pixels requires ((2560*4+32)*1600+128) ~= 15.7MBytes.
59 | // Hence we set a minimum buffer size of 8MBytes and an arbitrary maximum of 64Mbytes, chosen
60 | // to prevent configuration errors either stopping operation or hammering system memory.
61 | static const unsigned kDisplayXFBMinVRAMSize = 8 * 1024 * 1024; //! Smallest permitted VRAM size (bytes)
62 | static const unsigned kDisplayXFBMaxVRAMSize = 64 * 1024 * 1024; //! Largest permitted VRAM size (bytes)
63 | static const unsigned kDisplayXFBDefaultVRAMSize = 32 * 1024 * 1024; //! The default VRAM size (bytes) if no registry key is present
64 |
65 |
66 | // EDID data.
67 | static const uint16_t kDisplayXManufacturer = 0x5233u; // Manufacturer ID ("TQS" == 20/17/19 = %10100.10001.10011 = 0x5233
68 |
69 | /** Driver information. This is returned via the user-client when a new user-mode connection is established.
70 | *
71 | * @warning This structure must be a multiple of 64 bits in length (due to kernel-user-space crossing).
72 | */
73 | struct DisplayXFBInfo
74 | {
75 | static const uint32_t kMagic = 0x78464269; //! The value for m_magic ("xFBi")
76 |
77 | uint32_t m_magic; //! The value kMagic
78 | uint32_t m_versionMajor; //! The driver major version number (protocol compatibility)
79 | uint32_t m_versionMinor; //! The driver minor version number (revision)
80 | uint32_t m_displayCount; //! The number of supported displays (display index ranges from 0 to n-1)
81 |
82 | static const uint32_t kVersionMajor = kDisplayXFBVersionMajor; //! The current major version.
83 | static const uint32_t kVersionMinor = kDisplayXFBVersionMinor; //! The current minor version.
84 |
85 | DisplayXFBInfo()
86 | {
87 | invalidate();
88 | }
89 |
90 | void initialise(uint32_t initDisplayCount)
91 | {
92 | m_magic = kMagic;
93 | m_versionMajor = kVersionMajor;
94 | m_versionMinor = kVersionMinor;
95 | m_displayCount = initDisplayCount;
96 | }
97 |
98 | void invalidate()
99 | {
100 | m_magic = ~kMagic;
101 | m_versionMajor = 0;
102 | m_versionMinor = 0;
103 | m_displayCount = 0;
104 | }
105 |
106 | bool isValid() const
107 | {
108 | return m_magic == kMagic && kVersionMajor == m_versionMajor;
109 | }
110 |
111 | unsigned displayCount() const
112 | {
113 | return isValid() ? m_displayCount : 0;
114 | }
115 | };
116 | TSFBVDS_CHECK_STRUCTURE(DisplayXFBInfo);
117 |
118 |
119 |
120 | /** Display mode description.
121 | *
122 | * @warning This structure must be a multiple of 64 bits in length (due to kernel-user-space crossing).
123 | *
124 | * An instance of this structure describes a single display mode. It defines the display size and the
125 | * format of the data that is encoded in the framebuffer.
126 | */
127 | struct DisplayXFBMode
128 | {
129 | static const uint32_t kDefaultWidth = 1280; //!< The default width
130 | static const uint32_t kDefaultHeight = 900; //!< The default height
131 |
132 | uint32_t m_width; //!< The display width for the mode
133 | uint32_t m_height; //!< The display height for the mode
134 | uint32_t m_reserved0; //!< Reserved for future use
135 | uint32_t m_reserved1; //!< Reserved for future use
136 |
137 | DisplayXFBMode() { initialise(); }
138 | DisplayXFBMode(unsigned w, unsigned h) { initialise(w, h); }
139 |
140 | bool initialise() { return initialise(kDefaultWidth, kDefaultHeight); }
141 |
142 | bool initialise(unsigned w, unsigned h)
143 | {
144 | bool wok = setWidth(w);
145 | bool wol = setHeight(h);
146 | m_reserved0 = 0;
147 | m_reserved1 = 0;
148 | return wok && wol;
149 | }
150 |
151 | unsigned width() const { return m_width; } //!< Return the image width (pixels)
152 | unsigned height() const { return m_height; } //!< Return the image height (pixels)
153 |
154 | bool setWidth(unsigned w)
155 | {
156 | m_width = (uint32_t)(w - (w % kDisplayXFBWidthQuantise)); // Round down to the next lowest round width
157 | if (m_width < kDisplayXFBMinWidth) m_width = kDisplayXFBMinWidth; // Truncate at the minimum width
158 | else if (m_width > kDisplayXFBMaxWidth) m_width = kDisplayXFBMaxWidth; // Cap at the maximum width
159 | return m_width == w; // Return true if the requested value was used unmolested
160 | }
161 |
162 | bool setHeight(unsigned h)
163 | {
164 | m_height = (uint32_t)h;
165 | if (m_height < kDisplayXFBMinHeight) m_height = kDisplayXFBMinHeight; // Truncate at the minimum height
166 | else if (m_height > kDisplayXFBMaxHeight) m_height = kDisplayXFBMaxHeight; // Cap at the maximum height
167 | return m_height == h; // Return true if the requested value was used unmolested
168 | }
169 |
170 | };
171 | TSFBVDS_CHECK_STRUCTURE(DisplayXFBMode);
172 |
173 |
174 |
175 |
176 | /** Display buffer memory format description.
177 | *
178 | * @warning This structure must be a multiple of 64 bits in length (due to kernel-user-space crossing).
179 | */
180 | struct DisplayXFBState
181 | {
182 | static const uint32_t kMagic = 0x78464266; //! The value for m_magic ("xFBf")
183 | static const uint32_t kFlagConnected = (1u << 0); //! Flag bit that is set if the display is currently connected
184 |
185 | uint32_t m_magic; //! The value kMagic
186 | uint32_t m_offset; //! The byte offset to the first pixel in the frame buffer
187 | uint32_t m_pad; //! The number of padding bytes at the end of each row
188 | uint32_t m_flags; //! Bit flags (kFlagXxx)
189 | uint32_t m_modeIndex; //! The current mode index number
190 | uint32_t m_reserved[3]; //! Reserved for future use
191 | struct DisplayXFBMode m_mode; //! The mode description
192 |
193 | DisplayXFBState()
194 | {
195 | invalidate();
196 | }
197 |
198 | void initialise(const DisplayXFBMode& m, unsigned off, unsigned pd)
199 | {
200 | m_magic = kMagic;
201 | m_offset = off;
202 | m_pad = pd;
203 | m_flags = 0;
204 | for (unsigned i = 0; i < sizeof m_reserved / sizeof m_reserved[0]; i++) m_reserved[i] = 0;
205 | m_mode = m;
206 | }
207 |
208 | void invalidate()
209 | {
210 | m_magic = ~kMagic;
211 | m_offset = 0;
212 | m_pad = 0;
213 | m_flags = 0;
214 | for (unsigned i = 0; i < sizeof m_reserved / sizeof m_reserved[0]; i++) m_reserved[i] = 0;
215 | m_mode.initialise();
216 | }
217 |
218 | bool isValid() const
219 | {
220 | return (kMagic == m_magic);
221 | }
222 |
223 | DisplayXFBMode& mode() { return m_mode; } //! Return the display mode
224 | const DisplayXFBMode& mode() const { return m_mode; } //! Return the display mode
225 | unsigned modeIndex() const { return m_modeIndex; } //! Return the current mode index
226 |
227 | unsigned bytesPerPixel() const //! Return the number of bytes in a single pixel
228 | {
229 | return 4; // Only ARGB32 is supported at present
230 | }
231 |
232 | unsigned width() const { return m_mode.width(); } //! Return the image width (pixels)
233 | unsigned height() const { return m_mode.height(); } //! Return the image height (pixels)
234 | unsigned offset() const { return m_offset; } //! Return the byte offset to the first pixel in the framebuffer
235 | unsigned pad() const { return m_pad; } //! Return the byte padding at the end of each row
236 | unsigned bitsPerPixel() const { return bytesPerPixel() * 8; } //! Return the number of bits per-pixel
237 | unsigned bytesPerRow() const { return pad() + (bytesPerPixel()*width()); } //! Return the number of bytes in each row (the stride)
238 | unsigned bytesPerFrame() const { return bytesPerRow()*height(); } //! Return the total frame buffer data size, excluding offset
239 | bool isConnected() const { return 0 != (m_flags & kFlagConnected); } //! Return the current connection state for the display
240 |
241 | void setOffset(unsigned o) { m_offset = (uint32_t)o; } //! Set the offset
242 | void setPad(unsigned p) { m_pad = (uint32_t)p; } //! Set the pad
243 | void setIsConnected(bool con) { m_flags = (con) ? (m_flags | kFlagConnected) : ( m_flags & ~kFlagConnected); }; //! Set the connection state
244 |
245 | /** Set the current mode information.
246 | */
247 | void setMode(const DisplayXFBMode& m, unsigned mi)
248 | {
249 | m_modeIndex = (uint32_t) mi;
250 | m_mode = m;
251 | }
252 |
253 | };
254 | TSFBVDS_CHECK_STRUCTURE(DisplayXFBState);
255 |
256 |
257 | /** Display configuration. Instances of this structure are passed to the driver to define the supported
258 | * video modes, or read from the driver to find current information.
259 | *
260 | * @warning This structure must be a multiple of 64 bits in length (due to kernel-user-space crossing).
261 | *
262 | * A newly created configuration will have no modes defined and so can not be used directly. To construct a
263 | * usable configuration, the client should call appendMode() to define the supported display mode set. If
264 | * no default mode is specified to appendMode(), the first mode will be used as the default.
265 | */
266 | struct DisplayXFBConfiguration
267 | {
268 | static const uint32_t kMagic = 0x78464263; //! The value for m_magic ("xFBc")
269 | static const unsigned kMaxModes = 32; //! The maximum number of display modes that can be configured
270 | static const unsigned kDefaultRefresh = 0x003c0000; //! The default refresh rate (in 16.16 fixed point Hz)
271 | static const unsigned kDefaultRowPadding = 0; //! The default row padding, in bytes. Early OS (10.4) require that this is at least 32 bytes.
272 | static const unsigned kDefaultFramePadding = 1024; //! The default frame pading, in bytes
273 |
274 | uint32_t m_magic; //! DisplayXFBConfiguration::kMagic
275 | uint32_t m_defaultModeIndex; //! The default mode index
276 | uint32_t m_modeCount; //! The number of valid modes
277 | uint32_t m_refreshRate; //! The refresh rate (16.16 fixed point Hz)
278 | uint32_t m_rowPadding; //! The number of bytes of padding to use on each row
279 | uint32_t m_framePadding; //! THe number of bytes of padding to add to each frame
280 | uint32_t m_reserved0; //! Reserved
281 | uint32_t m_reserved1; //! Reserved
282 | uint8_t m_name[16]; //! The display name (zero terminated UTF8 string)
283 | DisplayXFBMode m_modes[kMaxModes]; //! Array of display mode definitions
284 |
285 | DisplayXFBConfiguration() { initialise(); }
286 | explicit DisplayXFBConfiguration(const char* n) { initialise(n); }
287 |
288 | void initialise(const char* n="") //! Create a configuration with a single mode at default resolution
289 | {
290 | m_magic = kMagic;
291 | m_defaultModeIndex = 0;
292 | m_modeCount = 0;
293 | m_refreshRate = kDefaultRefresh;
294 | m_rowPadding = kDefaultRowPadding;
295 | m_framePadding = kDefaultFramePadding;
296 | m_reserved0 = 0;
297 | m_reserved1 = 0;
298 | setName(n);
299 | for (unsigned i = 0; i < kMaxModes; i++) m_modes[i].initialise();
300 | }
301 |
302 | void invalidate()
303 | {
304 | initialise();
305 | m_magic = ~kMagic;
306 | }
307 |
308 | bool isValid() const //! Test if the structure is valid
309 | {
310 | return m_magic == kMagic && // Magic must be good
311 | m_refreshRate >= kDisplayXFBMinRefresh1616 && // Not too slow
312 | m_refreshRate <= kDisplayXFBMaxRefresh1616 && // Not too fast
313 | m_modeCount <= kMaxModes && // There is a hard limit to the maximum number of modes
314 | m_defaultModeIndex < kMaxModes && // Default mode must be plausible
315 | ((m_name[sizeof m_name / sizeof m_name[0] - 1]) == 0); // Null terminator must be intact in name string
316 | }
317 |
318 | const char* name() const { return (const char*)&m_name[0]; }
319 | void setName(const char* initName)
320 | {
321 | unsigned i = 0;
322 | while (i != sizeof m_name / sizeof m_name[0] - 1 && 0 != initName[i]) { m_name[i] = initName[i]; i++; }
323 | while (i != sizeof m_name / sizeof m_name[0]) { m_name[i] = 0; i++; }
324 | }
325 |
326 | double refreshRate() const { return m_refreshRate / 65536.0; } //! Return the refresh rate in Hz
327 | unsigned refreshRate1616() const { return m_refreshRate; } //! Return the refresh rate as 16.16 fixed point Hz
328 | uint32_t refreshPeriodUS() const { return (uint32_t) (65536000000ull / m_refreshRate); } // Return the refresh period, in us.
329 |
330 | void setRefreshRate(double r) //! Set the refresh rate in Hz
331 | {
332 | if (r <= 0) setRefreshRate1616(kDefaultRefresh);
333 | else setRefreshRate1616((unsigned)(r * 65536.0 + 0.5));
334 | }
335 |
336 | void setRefreshRate1616(unsigned r) //! Set the refresh rate as 16.16 fixed point Hz
337 | {
338 | if (r < kDisplayXFBMinRefresh1616) r = kDisplayXFBMinRefresh1616;
339 | else if (r > kDisplayXFBMaxRefresh1616) r = kDisplayXFBMaxRefresh1616;
340 | m_refreshRate = r;
341 | }
342 |
343 | unsigned rowPadding() const { return m_rowPadding; } //! Return the row padding, in bytes.
344 | void setRowPadding(unsigned n) { m_rowPadding = (uint32_t)n; } //! Set the row padding, in bytes.
345 |
346 | unsigned framePadding() const { return m_framePadding; } //! Return the row padding, in bytes.
347 | void setFramePadding(unsigned n) { m_framePadding = (uint32_t)n; } //! Set the row padding, in bytes.
348 |
349 | unsigned modeCount() const { return isValid() ? m_modeCount : 0; } //! Return the number of modes, or zero if the structure is invalid
350 |
351 | bool appendMode(unsigned w, unsigned h, bool setAsDefault=false)
352 | {
353 | uint32_t index = m_modeCount;
354 | if (index >= kMaxModes) return false;
355 | else
356 | {
357 | m_modeCount ++;
358 | if (setAsDefault) m_defaultModeIndex = index;
359 | return m_modes[index].initialise(w, h); // Note: this may modify the sizes to accommodate driver restrictions
360 | }
361 | }
362 |
363 | bool appendMode(const DisplayXFBMode& m, bool setAsDefault=false)
364 | {
365 | return appendMode(m.width(), m.height(), setAsDefault);
366 | }
367 |
368 | unsigned defaultModeIndex() const { return (m_defaultModeIndex < kMaxModes) ? m_defaultModeIndex : 0; }
369 |
370 | DisplayXFBMode& mode(unsigned index) { return (index < kMaxModes) ? m_modes[index] : m_modes[kMaxModes-1]; }
371 | const DisplayXFBMode& mode(unsigned index) const { return (index < kMaxModes) ? m_modes[index] : m_modes[kMaxModes-1]; }
372 |
373 | DisplayXFBMode& defaultMode() { return m_modes[defaultModeIndex()]; }
374 | const DisplayXFBMode& defaultMode() const { return m_modes[defaultModeIndex()]; }
375 |
376 | /** Given a mode index, create a display header to provide the format and size information.
377 | *
378 | * @param state Returns the display state information for the specified mode.
379 | * @param modeIndex The mode index.
380 | * @param offset The byte offset to the first pixel. Default zero.
381 | * @return Logical true for success.
382 | */
383 | bool makeState(DisplayXFBState& state, unsigned modeIndex, unsigned offset=0) const
384 | {
385 | if (modeIndex < modeCount()) state.initialise(mode(modeIndex), offset, rowPadding());
386 | else state.invalidate();
387 | return state.isValid();
388 | }
389 |
390 | };
391 | TSFBVDS_CHECK_STRUCTURE(DisplayXFBConfiguration);
392 |
393 |
394 |
395 |
396 | /** Memory map information. This is returned via the user-client in response to MapDisplay or MapUpdateBuffer messages.
397 | *
398 | * @warning This structure must be a multiple of 64 bits in length (due to kernel-user-space crossing).
399 | * @note A uint64_t is used to return the address mapping to avoid SDK & build dependent structure sizes.
400 | */
401 | struct DisplayXFBMap
402 | {
403 | static const uint32_t kMagic = 0x78464261; //! The value for m_magic ("xFBa")
404 |
405 | uint32_t m_magic; //! The value kMagic
406 | uint32_t m_reserved0; //! Reserved
407 | uint64_t m_address; //! The address of the data in the task's virtual address space, or zero if invalid.
408 | uint64_t m_size; //! The size of the mapping (bytes), or zero if invalid.
409 |
410 | DisplayXFBMap()
411 | {
412 | invalidate();
413 | }
414 |
415 | void initialise(uint64_t initAddress, uint64_t initSize)
416 | {
417 | m_magic = kMagic;
418 | m_reserved0 = 0;
419 | m_address = initAddress;
420 | m_size = initSize;
421 | }
422 |
423 | void invalidate()
424 | {
425 | m_magic = ~kMagic;
426 | m_reserved0 = 0;
427 | m_address = 0;
428 | m_size = 0;
429 | }
430 |
431 | bool isValid() const { return m_address != 0; }
432 | uint64_t address() const { return m_address; }
433 | uint64_t size() const { return m_size; }
434 |
435 | };
436 | TSFBVDS_CHECK_STRUCTURE(DisplayXFBMap);
437 |
438 |
439 | /** The cursor state. This is memory mapped as a read-only structure to a client.
440 | *
441 | * REVIEW: break this in to two structures - a cursor position and a cursor image object. These
442 | * should be read via user-client calls rather than memory mapping, to avoid asynchronous
443 | * changes.
444 | */
445 | struct DisplayXFBCursor
446 | {
447 | static const uint32_t kMagic = 0x43434246; //! Magic number ('FBCC' in little-endian form)
448 | static const uint32_t kMaxWidth = 128; //! The maximum supported cursor width
449 | static const uint32_t kMaxHeight = 128; //! The maximum supported cursor height
450 |
451 | uint32_t m_magic; //! The magic number DisplayXFBCursor::kMagic
452 | uint32_t m_isValid; //! Non-zero if the pixel data is valid
453 | uint32_t m_isVisible; //! Non-zero if the cursor is visible
454 | int32_t m_x; //! The cursor position (note: signed - negative values are legal)
455 | int32_t m_y; //! The cursor position (note: signed - negative values are legal)
456 | uint32_t m_width; //! The cursor width
457 | uint32_t m_height; //! The cursor height
458 | int32_t m_hotspotX; //! The cursor hostspot position
459 | int32_t m_hotspotY; //! The cursor hostspot position
460 | uint32_t m_sequenceState; //! Counter incremented on state updates
461 | uint32_t m_sequencePixel; //! Counter incremented on pixel data updates
462 | uint32_t m_reserved[5]; //! Reserved for future use
463 | uint32_t m_pixelData[kMaxWidth * kMaxHeight]; //! The RGBA32 pixel data
464 |
465 | void initialise()
466 | {
467 | m_magic = kMagic;
468 | m_isValid = 0;
469 | m_isVisible = 0;
470 | m_x = 0;
471 | m_y = 0;
472 | m_width = 0;
473 | m_height = 0;
474 | m_hotspotX = 0;
475 | m_hotspotY = 0;
476 | m_sequenceState = 0;
477 | m_sequencePixel = 0;
478 | bzero(m_reserved, sizeof m_reserved);
479 | bzero(m_pixelData, sizeof m_pixelData);
480 | }
481 |
482 | bool isValid() const { return m_magic == kMagic; }
483 | bool isVisible() const { return 0 != m_isVisible; }
484 | int x() const { return m_x; }
485 | int y() const { return m_y; }
486 | unsigned width() const { return m_width; }
487 | unsigned height() const { return m_height; }
488 | int hotX() const { return m_hotspotX; }
489 | int hotY() const { return m_hotspotY; }
490 | const uint32_t* pixelData() const { return &m_pixelData[0]; }
491 | };
492 | TSFBVDS_CHECK_STRUCTURE(DisplayXFBCursor);
493 |
494 |
495 |
496 | // Definitions for various property keys used by the driver classes.
497 | // Unless otherwise stated, the values are stored on both the com_tsoniq_driver_DisplayXFBDriver and com_tsoniq_driver_DisplayXFBFramebuffer
498 | // instances.
499 | #define kDisplayXFBKeyDisplayCount "DisplayXFB_DisplayCount" //! Property key for the number of displays (com_tsoniq_driver_DisplayXFBDriver only)
500 | #define kDisplayXFBKeyDisplayName "DisplayXFB_DisplayName" //! Property key for the display name
501 | #define kDisplayXFBKeyVRAMSize "DisplayXFB_VRAMSize" //! Property key for the video memory size (bytes)
502 | #define kDisplayXFBKeyDisplayIndex "DisplayXFB_DisplayIndex" //! Property key for the displayIndex
503 |
504 |
505 | // Notification codes (used to implement asynchronous notifications to a client).
506 | #define kDisplayXFBNotificationDisplayState iokit_vendor_specific_msg(0x01) //! Message sent on a display state change
507 | #define kDisplayXFBNotificationCursorState iokit_vendor_specific_msg(0x02) //! Message sent on a cursor change
508 | #define kDisplayXFBNotificationCursorImage iokit_vendor_specific_msg(0x03) //! Message sent on a cursor change
509 |
510 |
511 | /** Type codes for memory mapping. A single API call is used to establish shared memory mappings, indexed
512 | * by the display number and an integer that specifies what is being mapped.
513 | */
514 | #define kDisplayXFBMapTypeDisplay (0) //! Mapping is for the display VRAM
515 | #define kDisplayXFBMapTypeCursor (1) //! Mapping is for the mouse cursor
516 | #define kDisplayXFBMaxMapTypes (2) //! The highest permitted map type
517 |
518 |
519 | /** User client method dispatch selectors.
520 | *
521 | * @warning These *must* match the displach table ordering in DisplayXFBUserClient
522 | */
523 | enum
524 | {
525 | kDisplayXFBSelectorOpen = 0, //! Open a new user-client session
526 | kDisplayXFBSelectorClose = 1, //! Terminate a user-client session
527 | kDisplayXFBSelectorGetState = 2, //! Get the current configuration for a display
528 | kDisplayXFBSelectorGetConfiguration = 3, //! Get the current configuration for a display
529 | kDisplayXFBSelectorSetConfiguration = 4, //! Set the current configuration for a display
530 | kDisplayXFBSelectorConnect = 5, //! Connect a display
531 | kDisplayXFBSelectorDisconnect = 6, //! Disconnect a display
532 | kDisplayXFBSelectorMap = 7, //! Map shared memory in to application memory space
533 | kDisplayXFBNumberSelectors = 8
534 | };
535 |
536 | } // namespace
537 |
538 | #endif // COM_TSONIQ_DisplayXFBShared_H
539 |
--------------------------------------------------------------------------------
/source/displayxfb/DisplayXFBTiming.cc:
--------------------------------------------------------------------------------
1 | /** @file DisplayXFBTiming.cc
2 | * @brief Class used to provide timing services in the kernel.
3 | *
4 | * Copyright (c) 2011 tSoniq. All rights reserved.
5 | */
6 |
7 | #include "DisplayXFBTiming.h"
8 | #include
9 |
10 | /** Start the timer.
11 | *
12 | * @param period The tick interval, in us. Must be non-zero.
13 | */
14 | void DisplayXFBTiming::start(uint32_t period)
15 | {
16 | uint64_t nsecs = ((uint64_t)period) * 1000; // Convert period to ns
17 | nanoseconds_to_absolutetime(nsecs, &m_period); // Convert period to abstime units
18 | clock_get_uptime(&m_nextTick); // Get current uptime
19 | m_nextTick += m_period; // Time of next tick
20 | }
21 |
22 |
23 | /** Update the timer.
24 | *
25 | * @param ticks Returns the number of elapsed ticks since @e start() or the last @e update() call.
26 | * @param timeToNextTick Returns the number of microseconds until the next tick.
27 | */
28 | void DisplayXFBTiming::update(uint64_t& ticks, uint32_t& timeToNextTick)
29 | {
30 | uint64_t now = 0;
31 | clock_get_uptime(&now);
32 |
33 | if (now >= m_nextTick)
34 | {
35 | // Passed the current tick
36 | if (m_period != 0)
37 | {
38 | ticks = ((now - m_nextTick) / m_period) + 1;
39 | m_nextTick += (m_period * ticks);
40 | }
41 | else
42 | {
43 | // Paranoid handling to avoid division by zero kernel crash if the timer is misconfigured
44 | ticks = 0;
45 | m_nextTick = now;
46 | }
47 | }
48 | else
49 | {
50 | // Still got time to go.
51 | ticks = 0;
52 | if ((m_nextTick - now) > m_period)
53 | {
54 | // The system clock has gone backwards (should never happen - this is an uptime measure!)
55 | m_nextTick = now + 1;
56 | }
57 | }
58 |
59 | uint64_t nsecs = 0;
60 | absolutetime_to_nanoseconds(m_nextTick - now, &nsecs);
61 | timeToNextTick = (uint32_t)(nsecs / 1000);
62 | }
63 |
--------------------------------------------------------------------------------
/source/displayxfb/DisplayXFBTiming.h:
--------------------------------------------------------------------------------
1 | /** @file DisplayXFBTiming.h
2 | * @brief Class used to provide timing services in the kernel.
3 | *
4 | * Copyright (c) 2011 tSoniq. All rights reserved.
5 | */
6 |
7 | #ifndef COM_TSONIQ_DisplayXFBTiming_H
8 | #define COM_TSONIQ_DisplayXFBTiming_H (1)
9 |
10 | #include
11 | #include "DisplayXFBNames.h"
12 |
13 |
14 | /** Class used to manage timing services.
15 | */
16 | class DisplayXFBTiming
17 | {
18 | public:
19 |
20 | DisplayXFBTiming() : m_period(0), m_nextTick(0) { }
21 |
22 | void start(uint32_t period);
23 | void update(uint64_t& ticks, uint32_t& timeToNextTick);
24 |
25 | private:
26 |
27 | uint64_t m_period; //! Tick period, in abstime units
28 | uint64_t m_nextTick; //! Time for next tick, in abstime units
29 | };
30 |
31 | #endif // COM_TSONIQ_DisplayXFBTiming_H
32 |
--------------------------------------------------------------------------------
/source/displayxfb/DisplayXFBUserClient.h:
--------------------------------------------------------------------------------
1 | /** @file DisplayXFBUserClient.h
2 | * @brief IOUserClient implementation for the virtual display.
3 | *
4 | * Copyright (c) 2010 tsoniq. All rights reserved.
5 | */
6 |
7 | #ifndef COM_TSONIQ_DisplayXFBUserClient_H
8 | #define COM_TSONIQ_DisplayXFBUserClient_H (1)
9 |
10 | #include
11 | #include
12 | #include
13 | #include "DisplayXFBNames.h"
14 | #include "DisplayXFBDriver.h"
15 | #include "DisplayXFBShared.h"
16 |
17 | class DisplayXFBUserClient : public IOUserClient
18 | {
19 | OSDeclareDefaultStructors(DisplayXFBUserClient);
20 |
21 | public:
22 |
23 | virtual IOReturn externalMethod(uint32_t selector, IOExternalMethodArguments* arguments, IOExternalMethodDispatch* dispatch, OSObject* target, void* reference);
24 | virtual bool initWithTask(task_t owningTask, void* securityToken, UInt32 type, OSDictionary* properties);
25 | virtual bool start(IOService* provider);
26 | virtual void stop(IOService* provider);
27 | virtual IOReturn clientClose();
28 | virtual IOReturn clientDied();
29 | virtual bool willTerminate(IOService* provider, IOOptionBits options);
30 | virtual bool didTerminate(IOService* provider, IOOptionBits options, bool* defer);
31 | virtual bool finalize(IOOptionBits options);
32 |
33 | // Messaging
34 | virtual IOReturn message(UInt32 type, IOService* provider, void* argument);
35 |
36 |
37 | // User client methods
38 | IOReturn userClientOpen(ts::DisplayXFBInfo* info, uint32_t* infoSize);
39 | IOReturn userClientClose();
40 | IOReturn userClientGetConfiguration(uint32_t displayIndex, ts::DisplayXFBConfiguration* config, uint32_t* configSize);
41 | IOReturn userClientSetConfiguration(uint32_t displayIndex, ts::DisplayXFBConfiguration* config, uint32_t* configSize);
42 | IOReturn userClientGetState(uint32_t displayIndex, ts::DisplayXFBState* state, uint32_t* stateSize);
43 | IOReturn userClientConnect(unsigned displayIndex);
44 | IOReturn userClientDisconnect(unsigned displayIndex);
45 | IOReturn userClientMap(unsigned displayIndex, unsigned mapType, bool readOnly, ts::DisplayXFBMap* map, uint32_t* mapSize);
46 |
47 | static const IOExternalMethodDispatch selectorMethods[ts::kDisplayXFBNumberSelectors];
48 |
49 | static IOReturn selectorUserClientOpen(DisplayXFBUserClient* target, void* reference, IOExternalMethodArguments* arguments);
50 | static IOReturn selectorUserClientClose(DisplayXFBUserClient* target, void* reference, IOExternalMethodArguments* arguments);
51 | static IOReturn selectorUserClientGetConfiguration(DisplayXFBUserClient* target, void* reference, IOExternalMethodArguments* arguments);
52 | static IOReturn selectorUserClientSetConfiguration(DisplayXFBUserClient* target, void* reference, IOExternalMethodArguments* arguments);
53 | static IOReturn selectorUserClientGetState(DisplayXFBUserClient* target, void* reference, IOExternalMethodArguments* arguments);
54 | static IOReturn selectorUserClientConnect(DisplayXFBUserClient* target, void* reference, IOExternalMethodArguments* arguments);
55 | static IOReturn selectorUserClientDisconnect(DisplayXFBUserClient* target, void* reference, IOExternalMethodArguments* arguments);
56 | static IOReturn selectorUserClientMap(DisplayXFBUserClient* target, void* reference, IOExternalMethodArguments* arguments);
57 |
58 | private:
59 |
60 | com_tsoniq_driver_DisplayXFBDriver* m_provider; //! The providing service
61 | task_t m_owningTask; //! The client's task handle
62 | IOMemoryMap* m_memoryMaps[ts::kDisplayXFBMaxDisplays][kDisplayXFBMaxMapTypes]; //! Array of memory mappings
63 |
64 | DisplayXFBUserClient(const DisplayXFBUserClient&); // Prevent copy constructor
65 | DisplayXFBUserClient& operator=(const DisplayXFBUserClient&); // Prevent assignment
66 | };
67 |
68 |
69 | #endif // COM_TSONIQ_DisplayXFBUserClient_H
70 |
--------------------------------------------------------------------------------
/source/displayxfb/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/source/displayxlib/DisplayXFBInterface.cc:
--------------------------------------------------------------------------------
1 | /** @file DisplayXFBInterface.cc
2 | * @brief User-space client interface object.
3 | *
4 | * Copyright (c) 2010 tsoniq. All rights reserved.
5 | */
6 |
7 | #include "DisplayXFBInterface.h"
8 | #include
9 |
10 | #include
11 | #include
12 | #include
13 |
14 |
15 | #define kDisplayXFBServiceName0(str) #str
16 | #define kDisplayXFBServiceName1(str) kDisplayXFBServiceName0(str)
17 | #define kDisplayXFBServiceName kDisplayXFBServiceName1(DisplayXFBDriver)
18 |
19 |
20 | namespace ts
21 | {
22 | DisplayXFBInterface::DisplayXFBInterface()
23 | :
24 | m_isOpen(false),
25 | m_service(0),
26 | m_connect(0),
27 | m_info(),
28 | m_notificationHandler(0),
29 | m_notificationHandlerContext(0),
30 | m_notificationHandlerNotificationPort(0),
31 | m_notificationObject(0),
32 | m_notificationRunloop(0)
33 | {
34 | m_info.invalidate();
35 | }
36 |
37 |
38 | DisplayXFBInterface::~DisplayXFBInterface()
39 | {
40 | close();
41 | }
42 |
43 |
44 | bool DisplayXFBInterface::open()
45 | {
46 | if (!m_isOpen)
47 | {
48 | assert(!m_service);
49 | assert(!m_connect);
50 |
51 | m_service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kDisplayXFBServiceName));
52 | if (!m_service) fprintf(stderr, "%s:no virtual display service running\n", __PRETTY_FUNCTION__);
53 | else
54 | {
55 | IOReturn status = IOServiceOpen(m_service, mach_task_self(), 0, &m_connect);
56 | if (kIOReturnSuccess != status) fprintf(stderr, "%s: status %08x from IOServiceOpen\n", __PRETTY_FUNCTION__, (unsigned)status);
57 | else m_isOpen = userOpen(&m_info);
58 | }
59 |
60 | // Cleanup in the event of an error.
61 | if (!m_isOpen)
62 | {
63 | if (m_service) { IOObjectRelease(m_service); m_service = 0;}
64 | if (m_connect) { IOServiceClose(m_connect); m_connect = 0; }
65 | }
66 | }
67 |
68 | return m_isOpen;
69 | }
70 |
71 |
72 | void DisplayXFBInterface::close()
73 | {
74 | clearNotificationHandler();
75 | if (m_isOpen)
76 | {
77 | userClose();
78 | IOServiceClose(m_connect);
79 | m_connect = 0;
80 | IOObjectRelease(m_service);
81 | m_service = 0;
82 | m_info.invalidate();
83 | m_isOpen = false;
84 | }
85 | }
86 |
87 |
88 | bool DisplayXFBInterface::isOpen() const
89 | {
90 | return m_isOpen;
91 | }
92 |
93 |
94 | unsigned DisplayXFBInterface::displayCount() const
95 | {
96 | if (!isOpen()) return 0;
97 | else return m_info.displayCount();
98 | }
99 |
100 |
101 | bool DisplayXFBInterface::displayGetConfiguration(DisplayXFBConfiguration& configuration, unsigned displayIndex)
102 | {
103 | if (!isOpen() || !userDisplayGetConfiguration(&configuration, displayIndex)) { configuration.invalidate(); return false; }
104 | else return configuration.isValid();
105 | }
106 |
107 |
108 | bool DisplayXFBInterface::displaySetConfiguration(const DisplayXFBConfiguration& configuration, unsigned displayIndex)
109 | {
110 | if (!isOpen() || !userDisplaySetConfiguration(&configuration, displayIndex)) return false;
111 | else return true;
112 | }
113 |
114 |
115 | bool DisplayXFBInterface::displayGetState(DisplayXFBState& state, unsigned displayIndex)
116 | {
117 | if (!isOpen() || !userDisplayGetState(&state, displayIndex)) { state.invalidate(); return false; }
118 | else return state.isValid();
119 | }
120 |
121 |
122 | bool DisplayXFBInterface::displayIsConnected(unsigned displayIndex)
123 | {
124 | DisplayXFBState state;
125 | bool result = displayGetState(state, displayIndex);
126 | return result && (state.isConnected());
127 | }
128 |
129 |
130 | bool DisplayXFBInterface::displayConnect(unsigned displayIndex)
131 | {
132 | if (!isOpen() || !userDisplayConnect(displayIndex)) return false;
133 | else return true;
134 | }
135 |
136 |
137 | bool DisplayXFBInterface::displayDisconnect(unsigned displayIndex)
138 | {
139 | if (!isOpen() || !userDisplayDisconnect(displayIndex)) return false;
140 | else return true;
141 | }
142 |
143 |
144 | bool DisplayXFBInterface::displayMapFramebuffer(DisplayXFBMap& map, unsigned displayIndex, bool readOnly)
145 | {
146 | if (!isOpen() || !userMap(displayIndex, kDisplayXFBMapTypeDisplay, readOnly, &map)) { map.invalidate(); return false; }
147 | else return true;
148 | }
149 |
150 |
151 | bool DisplayXFBInterface::displayMapCursor(DisplayXFBMap& map, unsigned displayIndex, bool readOnly)
152 | {
153 | if (!isOpen() || !userMap(displayIndex, kDisplayXFBMapTypeCursor, readOnly, &map)) { map.invalidate(); return false; }
154 | else return true;
155 | }
156 |
157 |
158 | bool DisplayXFBInterface::setNotificationHandler(NotificationHandler handler, void* context, CFRunLoopRef runloop)
159 | {
160 | clearNotificationHandler();
161 |
162 | if (!handler) return false;
163 | if (!isOpen()) return false;
164 |
165 | assert(!m_notificationHandlerNotificationPort);
166 | assert(!m_notificationRunloop);
167 |
168 | m_notificationHandlerNotificationPort = IONotificationPortCreate(kIOMasterPortDefault);
169 | if (!m_notificationHandlerNotificationPort) return false;
170 |
171 | if (!runloop) m_notificationRunloop = CFRunLoopGetCurrent();
172 | else m_notificationRunloop = runloop;
173 |
174 | CFRunLoopAddSource(
175 | m_notificationRunloop,
176 | IONotificationPortGetRunLoopSource(m_notificationHandlerNotificationPort),
177 | kCFRunLoopCommonModes);
178 |
179 | m_notificationHandler = handler;
180 | m_notificationHandlerContext = context;
181 |
182 | IOReturn status = IOServiceAddInterestNotification(
183 | m_notificationHandlerNotificationPort,
184 | m_service,
185 | kIOGeneralInterest,
186 | &interestCallback, this,
187 | &m_notificationObject);
188 |
189 | if (kIOReturnSuccess != status)
190 | {
191 | clearNotificationHandler();
192 | return false;
193 | }
194 | else return true;
195 | }
196 |
197 |
198 | void DisplayXFBInterface::clearNotificationHandler()
199 | {
200 | // This method is also used to clean up in the event of some failure - don't make it conditional on everything being ok.
201 | if (m_notificationObject)
202 | {
203 | IOObjectRelease(m_notificationObject);
204 | m_notificationObject = 0;
205 | }
206 | if (m_notificationHandlerNotificationPort)
207 | {
208 | if (m_notificationRunloop)
209 | {
210 | CFRunLoopRemoveSource(
211 | m_notificationRunloop,
212 | IONotificationPortGetRunLoopSource(m_notificationHandlerNotificationPort),
213 | kCFRunLoopCommonModes);
214 | }
215 |
216 | IONotificationPortDestroy(m_notificationHandlerNotificationPort);
217 |
218 | m_notificationHandlerNotificationPort = 0;
219 | m_notificationRunloop = 0;
220 | }
221 |
222 | m_notificationHandler = 0;
223 | m_notificationHandlerContext = 0;
224 | }
225 |
226 |
227 | #pragma -
228 | #pragma RPC Methods
229 |
230 |
231 | bool DisplayXFBInterface::userOpen(DisplayXFBInfo* info)
232 | {
233 | bool result;
234 |
235 | assert(!isOpen());
236 | assert(m_connect);
237 |
238 | size_t structOutSize = sizeof *info;
239 |
240 | kern_return_t kr = IOConnectCallMethod(
241 | m_connect, // service handle
242 | kDisplayXFBSelectorOpen, // selector
243 | NULL, // array of input values
244 | 0, // number of input values (pass actual)
245 | NULL, // input structure
246 | 0, // input structure size (pass actual)
247 | NULL, // array of output values
248 | 0, // number of output values (pass max, return actual)
249 | info, // output structure
250 | &structOutSize // output structure size (pass max, return actual)
251 | );
252 |
253 | assert(structOutSize == sizeof *info);
254 |
255 | if (kr != KERN_SUCCESS)
256 | {
257 | fprintf(stderr, "%s: error %08x from open\n", __PRETTY_FUNCTION__, (unsigned)kr);
258 | result = false;
259 | }
260 | else if (structOutSize != sizeof *info || info->m_versionMajor != DisplayXFBInfo::kVersionMajor)
261 | {
262 | fprintf(stderr, "%s: incompatible version (want %08x, got %08x)\n", __PRETTY_FUNCTION__, (unsigned)info->m_versionMajor, (unsigned)DisplayXFBInfo::kVersionMajor);
263 | userClose();
264 | result = false;
265 | }
266 | else
267 | {
268 | // The open request succeeded and the FB version is compatible with what we are compiled with
269 | result = true;
270 | }
271 |
272 | return result;
273 | }
274 |
275 |
276 |
277 | void DisplayXFBInterface::userClose()
278 | {
279 | //assert(isOpen()); // This may be called from userOpen() before m_isOpen is set, to clean up an incompatible open() operation.
280 | assert(m_connect);
281 | IOConnectCallScalarMethod(m_connect, kDisplayXFBSelectorClose, NULL, 0, NULL, NULL);
282 | }
283 |
284 |
285 |
286 | bool DisplayXFBInterface::userDisplayGetConfiguration(DisplayXFBConfiguration* configuration, unsigned displayIndex)
287 | {
288 | assert(configuration);
289 | assert(isOpen());
290 | assert(m_connect);
291 |
292 | uint64_t scalarInData[1] = { displayIndex };
293 | uint32_t scalarInCount = (uint32_t) (sizeof scalarInData / sizeof scalarInData[0]);
294 | size_t structOutSize = sizeof *configuration;
295 |
296 | kern_return_t kr = IOConnectCallMethod(
297 | m_connect, // service handle
298 | kDisplayXFBSelectorGetConfiguration, // selector
299 | scalarInData, // array of input values
300 | scalarInCount, // number of input values (pass actual)
301 | NULL, // input structure
302 | 0, // input structure size (pass actual)
303 | NULL, // array of output values
304 | 0, // number of output values (pass max, return actual)
305 | (void*)configuration, // output structure
306 | &structOutSize // output structure size (pass max, return actual)
307 | );
308 |
309 | assert(structOutSize == sizeof *configuration);
310 |
311 | return (structOutSize == sizeof *configuration) && (KERN_SUCCESS == kr);
312 | }
313 |
314 |
315 |
316 | bool DisplayXFBInterface::userDisplaySetConfiguration(const DisplayXFBConfiguration* configuration, unsigned displayIndex)
317 | {
318 | assert(isOpen());
319 | assert(m_connect);
320 |
321 | uint64_t scalarInData[1] = { displayIndex };
322 | uint32_t scalarInCount = (uint32_t) (sizeof scalarInData / sizeof scalarInData[0]);
323 | uint32_t scalarOutCount = 0;
324 | size_t structOutSize = 0;
325 |
326 | kern_return_t kr = IOConnectCallMethod(
327 | m_connect, // service handle
328 | kDisplayXFBSelectorSetConfiguration, // selector
329 | scalarInData, // array of input values
330 | scalarInCount, // number of input values (pass actual)
331 | (void*)configuration, // input structure
332 | sizeof *configuration, // input structure size (pass actual)
333 | NULL, // array of output values
334 | &scalarOutCount, // number of output values (pass max, return actual)
335 | NULL, // output structure
336 | &structOutSize // output structure size (pass max, return actual)
337 | );
338 |
339 | return (kr == KERN_SUCCESS);
340 | }
341 |
342 |
343 | bool DisplayXFBInterface::userDisplayGetState(DisplayXFBState* state, unsigned displayIndex)
344 | {
345 | assert(state);
346 | assert(isOpen());
347 | assert(m_connect);
348 |
349 | uint64_t scalarInData[1] = { displayIndex };
350 | uint32_t scalarInCount = (uint32_t) (sizeof scalarInData / sizeof scalarInData[0]);
351 | size_t structOutSize = sizeof *state;
352 |
353 | kern_return_t kr = IOConnectCallMethod(
354 | m_connect, // service handle
355 | kDisplayXFBSelectorGetState, // selector
356 | scalarInData, // array of input values
357 | scalarInCount, // number of input values (pass actual)
358 | NULL, // input structure
359 | 0, // input structure size (pass actual)
360 | NULL, // array of output values
361 | 0, // number of output values (pass max, return actual)
362 | (void*)state, // output structure
363 | &structOutSize // output structure size (pass max, return actual)
364 | );
365 |
366 | assert(structOutSize == sizeof *state);
367 |
368 | return (structOutSize == sizeof *state) && (KERN_SUCCESS == kr);
369 | }
370 |
371 |
372 | bool DisplayXFBInterface::userDisplayConnect(unsigned displayIndex)
373 | {
374 | assert(isOpen());
375 | assert(m_connect);
376 |
377 | uint64_t scalarInData[1] = { displayIndex };
378 | uint32_t scalarInCount = (uint32_t) (sizeof scalarInData / sizeof scalarInData[0]);
379 | uint32_t scalarOutCount = 0;
380 | size_t structOutSize = 0;
381 |
382 | kern_return_t kr = IOConnectCallMethod(
383 | m_connect, // service handle
384 | kDisplayXFBSelectorConnect, // selector
385 | scalarInData, // array of input values
386 | scalarInCount, // number of input values (pass actual)
387 | NULL, // input structure
388 | 0, // input structure size (pass actual)
389 | NULL, // array of output values
390 | &scalarOutCount, // number of output values (pass max, return actual)
391 | NULL, // output structure
392 | &structOutSize // output structure size (pass max, return actual)
393 | );
394 |
395 | return (kr == KERN_SUCCESS);
396 | }
397 |
398 |
399 | bool DisplayXFBInterface::userDisplayDisconnect(unsigned displayIndex)
400 | {
401 | assert(isOpen());
402 | assert(m_connect);
403 |
404 | uint64_t scalarInData[1] = { displayIndex };
405 | uint32_t scalarInCount = (uint32_t) (sizeof scalarInData / sizeof scalarInData[0]);
406 | uint32_t scalarOutCount = 0;
407 | size_t structOutSize = 0;
408 |
409 | kern_return_t kr = IOConnectCallMethod(
410 | m_connect, // service handle
411 | kDisplayXFBSelectorDisconnect, // selector
412 | scalarInData, // array of input values
413 | scalarInCount, // number of input values (pass actual)
414 | NULL, // input structure
415 | 0, // input structure size (pass actual)
416 | NULL, // array of output values
417 | &scalarOutCount, // number of output values (pass max, return actual)
418 | NULL, // output structure
419 | &structOutSize // output structure size (pass max, return actual)
420 | );
421 |
422 | return (kr == KERN_SUCCESS);
423 | }
424 |
425 |
426 | bool DisplayXFBInterface::userMap(unsigned displayIndex, unsigned mapType, bool readOnly, DisplayXFBMap* map)
427 | {
428 | assert(map);
429 | assert(isOpen());
430 | assert(m_connect);
431 |
432 | uint64_t scalarInData[3] = { displayIndex, mapType, (readOnly) ? 1u : 0u };
433 | uint32_t scalarInCount = (uint32_t) (sizeof scalarInData / sizeof scalarInData[0]);
434 | uint32_t scalarOutCount = 0;
435 | size_t structOutSize = sizeof *map;
436 |
437 | kern_return_t kr = IOConnectCallMethod(
438 | m_connect, // service handle
439 | kDisplayXFBSelectorMap, // selector
440 | scalarInData, // array of input values
441 | scalarInCount, // number of input values (pass actual)
442 | NULL, // input structure
443 | 0, // input structure size (pass actual)
444 | NULL, // array of output values
445 | &scalarOutCount, // number of output values (pass max, return actual)
446 | map, // output structure
447 | &structOutSize // output structure size (pass max, return actual)
448 | );
449 |
450 | assert(structOutSize == sizeof *map);
451 |
452 | return (structOutSize == sizeof *map) && (KERN_SUCCESS == kr);
453 | }
454 |
455 |
456 | /** IOService callback on a notification from the driver.
457 | */
458 | void DisplayXFBInterface::interestCallback(void* refcon, io_service_t service, natural_t messageType, void* messageArgument)
459 | {
460 | DisplayXFBInterface& interface = *((DisplayXFBInterface*)refcon);
461 | unsigned arg = (unsigned)((uint64_t)messageArgument);
462 |
463 | if (!interface.m_notificationHandler)
464 | {
465 | /* No handler */
466 | }
467 | else if (kDisplayXFBNotificationCursorState == messageType)
468 | {
469 | interface.m_notificationHandler(kNotificationCursorState, arg, interface.m_notificationHandlerContext);
470 | }
471 | else if (kDisplayXFBNotificationCursorImage == messageType)
472 | {
473 | interface.m_notificationHandler(kNotificationCursorImage, arg, interface.m_notificationHandlerContext);
474 | }
475 | else if (kDisplayXFBNotificationDisplayState == messageType)
476 | {
477 | interface.m_notificationHandler(kNotificationDisplayState, arg, interface.m_notificationHandlerContext);
478 | }
479 | else
480 | {
481 | /* Unknown notification */
482 | }
483 | }
484 |
485 |
486 | #pragma mark -
487 | #pragma mark Class Methods
488 |
489 |
490 | bool DisplayXFBInterface::isInstalled()
491 | {
492 | io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kDisplayXFBServiceName));
493 | if (service)
494 | {
495 | IOObjectRelease(service);
496 | return true;
497 | }
498 | else
499 | {
500 | return false;
501 | }
502 | }
503 |
504 |
505 |
506 | bool DisplayXFBInterface::displayIndexToID(CGDirectDisplayID& displayID, unsigned displayIndex)
507 | {
508 | // Get the current list of displays.
509 | CGDirectDisplayID resultIdent = 0;
510 | bool resultStatus = false;
511 |
512 | uint32_t allocDisplayCount = 0;
513 | CGError result = CGGetActiveDisplayList(0, NULL, &allocDisplayCount);
514 | if (result == kCGErrorSuccess && allocDisplayCount != 0)
515 | {
516 | uint32_t activeDisplayCount = 0;
517 | CGDirectDisplayID* activeDisplays = (CGDirectDisplayID*)calloc(sizeof (CGDirectDisplayID), allocDisplayCount);
518 | if (activeDisplays)
519 | {
520 | result = CGGetActiveDisplayList(allocDisplayCount, activeDisplays, &activeDisplayCount);
521 | if (result == kCGErrorSuccess && activeDisplayCount != 0)
522 | {
523 | // Find the display ID for the current screen.
524 | // The display exports EDID data with a custom manufacturer and the displayIndex value as the serial number.
525 | for (unsigned i = 0; i != activeDisplayCount; ++i)
526 | {
527 | uint32_t manufacturer = CGDisplayVendorNumber(activeDisplays[i]);
528 | uint32_t serial = CGDisplaySerialNumber(activeDisplays[i]);
529 | if (manufacturer == kDisplayXManufacturer && serial == displayIndex)
530 | {
531 | resultIdent = activeDisplays[i];
532 | resultStatus = true;
533 | break;
534 | }
535 | }
536 | }
537 | free(activeDisplays);
538 | }
539 | }
540 |
541 | if (displayID) displayID = resultIdent;
542 | return resultStatus;
543 | }
544 |
545 |
546 | bool DisplayXFBInterface::displayIDToIndex(unsigned& displayIndex, CGDirectDisplayID displayID)
547 | {
548 | bool resultStatus = false;
549 | unsigned resultIndex = ~0u;
550 |
551 | if (kDisplayXManufacturer == CGDisplayVendorNumber(displayID))
552 | {
553 | resultIndex = CGDisplaySerialNumber(displayID);
554 | resultStatus = true;
555 | }
556 |
557 | if (displayIndex) displayIndex = resultIndex;
558 | return resultStatus;
559 | }
560 |
561 | } // namespace
562 |
--------------------------------------------------------------------------------
/source/displayxlib/DisplayXFBInterface.h:
--------------------------------------------------------------------------------
1 | /** @file DisplayXFBInterface.h
2 | * @brief User-space client interface to the frame buffer.
3 | *
4 | * Copyright (c) 2010 tsoniq. All rights reserved.
5 | */
6 |
7 | #ifndef COM_TSONIQ_DisplayXFBInterface_H
8 | #define COM_TSONIQ_DisplayXFBInterface_H (1)
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include "DisplayXFBShared.h"
15 |
16 | namespace ts
17 | {
18 | /** This is the application interface to the framebuffer driver.
19 | *
20 | * See DisplayXFBUserClient for a description of the messaging protocol. Note that the APIs
21 | * provided here are not necessarily a one-to-one mapping of the messages, but are provided
22 | * for client convenience.
23 | */
24 | class DisplayXFBInterface
25 | {
26 | public:
27 |
28 | /** Enumeration for notification callbacks.
29 | */
30 | enum Notification
31 | {
32 | kNotificationDisplayState = 1, //! A display's state has been changed by the user
33 | kNotificationCursorState = 2, //! The cursor has changed state
34 | kNotificationCursorImage = 3, //! The cursor image has changed
35 | };
36 |
37 |
38 | /** The prototype for a c-function callback signalling an update to the display.
39 | *
40 | * @param notification The notification type.
41 | * @param displayIndex The display for which the notification is being raised.
42 | * @param context The client specific context handle.
43 | */
44 | typedef void (*NotificationHandler)(enum Notification notification, unsigned displayIndex, void* context);
45 |
46 |
47 | /** Test if the driver is installed.
48 | *
49 | * @return true if a compatible driver is present, false otherwise.
50 | */
51 | static bool isInstalled();
52 |
53 |
54 | /** Find the Quartz display ID for a connected virtual display.
55 | *
56 | * @param displayID Returns the display ID.
57 | * @param displayIndex The virtual display number.
58 | * @return Logical true for success, false for failure.
59 | *
60 | * This method does not require an open driver connection to function. It will fail if the requested
61 | * display index is out of range or if the requested display is not currently connected.
62 | *
63 | * Application code can use this method to determine the CGDirectDisplayID to allow the use of normal
64 | * Quartz low-level calls for a specific display. Typically, the ID is used to connect to the IOSurface
65 | * stream methods.
66 | */
67 | static bool displayIndexToID(CGDirectDisplayID& displayID, unsigned displayIndex);
68 |
69 |
70 | /** Get the virtual display index given a Quartz display ID.
71 | *
72 | * @param displayIndex Returns the display index.
73 | * @param displayID The Quartz display ID to query.
74 | * @return true for success, false if the Quartz ID does not correspond to one of our displays.
75 | */
76 | static bool displayIDToIndex(unsigned& displayIndex, CGDirectDisplayID displayID);
77 |
78 |
79 | /** Constructor.
80 | */
81 | DisplayXFBInterface();
82 |
83 |
84 | /** Destructor.
85 | */
86 | ~DisplayXFBInterface();
87 |
88 |
89 | /** Open the driver.
90 | *
91 | * @return true for success, false for failure. This can fail if either no compatible driver is installed, or
92 | * if the driver is already open by another client.
93 | */
94 | bool open();
95 |
96 |
97 | /** Close the driver.
98 | */
99 | void close();
100 |
101 |
102 | /** Test if the driver is open.
103 | */
104 | bool isOpen() const;
105 |
106 |
107 | /** Return the number of available displays.
108 | */
109 | unsigned displayCount() const;
110 |
111 |
112 | /** Get a current display's configuration.
113 | *
114 | * @param configuration Returns the configuration.
115 | * @param displayIndex The display to query.
116 | * @return true for success, false for failure.
117 | */
118 | bool displayGetConfiguration(DisplayXFBConfiguration& configuration, unsigned displayIndex);
119 |
120 |
121 | /** Set a display configuration.
122 | *
123 | * @param configuration Specifies the configuration.
124 | * @param displayIndex The display to query.
125 | * @return true for success, false for failure.
126 | *
127 | * This method may fail if the configuration requires too much resource for the driver.
128 | */
129 | bool displaySetConfiguration(const DisplayXFBConfiguration& configuration, unsigned displayIndex);
130 |
131 |
132 | /** Get a display's current state.
133 | *
134 | * @param state Returns the state information.
135 | * @param displayIndex The display number.
136 | * @return Logical true for success, false for failure.
137 | */
138 | bool displayGetState(DisplayXFBState& state, unsigned displayIndex);
139 |
140 |
141 | /** Test if a display is connected. This is a shortcut alternative to using the
142 | * more capable displayGetState() method.
143 | */
144 | bool displayIsConnected(unsigned displayIndex);
145 |
146 |
147 | /** Connect a display (make it available).
148 | *
149 | * @param displayIndex The display number.
150 | * @return Logical true for success, false for failure.
151 | */
152 | bool displayConnect(unsigned displayIndex);
153 |
154 |
155 | /** Disconnect a display.
156 | *
157 | * @param displayIndex The display number.
158 | * @return Logical true for success, false for failure.
159 | */
160 | bool displayDisconnect(unsigned displayIndex);
161 |
162 |
163 |
164 | /** Map the framebuffer memory for a display in to the current task's address space.
165 | *
166 | * @param map Returns the address mapping information.
167 | * @param displayIndex The display number.
168 | * @param readOnly Logical true if the mapping should be read-only (recommended).
169 | * @return Logical true for success, false for failure.
170 | */
171 | bool displayMapFramebuffer(DisplayXFBMap& map, unsigned displayIndex, bool readOnly=true);
172 |
173 |
174 | /** Map the cursor memory for a display in to the current task's address space.
175 | *
176 | * @param map Returns the address mapping information.
177 | * @param displayIndex The display number.
178 | * @param readOnly Logical true if the mapping should be read-only (recommended).
179 | * @return Logical true for success, false for failure.
180 | */
181 | bool displayMapCursor(DisplayXFBMap& map, unsigned displayIndex, bool readOnly=true);
182 |
183 |
184 | /** Set the notification callback handler.
185 | *
186 | * @param handler The function to call with notifications.
187 | * @param context The client-specific context to pass on callbacks.
188 | * @param runloop The runloop for callbacks. If NULL, the current runloop will be used.
189 | * @return Logical true for success, false for failure.
190 | *
191 | * On completion, notification callbacks will be issued to the specificied function
192 | * using the target runloop.
193 | */
194 | bool setNotificationHandler(NotificationHandler handler, void* context, CFRunLoopRef runloop=0);
195 |
196 |
197 | /** Stop further notification callbacks.
198 | */
199 | void clearNotificationHandler();
200 |
201 | private:
202 |
203 | bool m_isOpen; //!< Logical true if the interface is bound
204 | io_service_t m_service; //!< The service handle
205 | io_connect_t m_connect; //!< The connection handle
206 | DisplayXFBInfo m_info; //!< The driver info structure (from open)
207 | NotificationHandler m_notificationHandler; //!< The registered notification handler, or zero if none
208 | void* m_notificationHandlerContext; //!< Client supplied context for notification callbacks
209 | IONotificationPortRef m_notificationHandlerNotificationPort; //!< What it says
210 | io_object_t m_notificationObject; //!< Notification context.
211 | CFRunLoopRef m_notificationRunloop; //!< The runloop where notifications are posted
212 |
213 | // Methods implementing the RPC. These ultimately map directly to the methods in com_tsoniq_driver_DisplayXFB via the user-client.
214 | bool userOpen(DisplayXFBInfo* info);
215 | void userClose();
216 | bool userDisplayGetConfiguration(DisplayXFBConfiguration* configuration, unsigned displayIndex);
217 | bool userDisplaySetConfiguration(const DisplayXFBConfiguration* configuration, unsigned displayIndex);
218 | bool userDisplayGetState(DisplayXFBState* state, unsigned displayIndex);
219 | bool userDisplayConnect(unsigned displayIndex);
220 | bool userDisplayDisconnect(unsigned displayIndex);
221 | bool userMap(unsigned displayIndex, unsigned mapType, bool readOnly, DisplayXFBMap* map);
222 |
223 | // Class methods
224 | static void interestCallback(void* refcon, io_service_t service, natural_t messageType, void* messageArgument);
225 | };
226 |
227 | } // namespace
228 |
229 | #endif // COM_TSONIQ_DisplayXFBInterface_H
230 |
--------------------------------------------------------------------------------
/source/xcconfig/DisplayX.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // DisplayX.xcconfig
3 | //
4 |
5 |
6 | // Code-signing identities for the kernel extenion.
7 | //
8 | // Leave blank to disable signing. Be aware that conventional Apple application signing certificates can
9 | // not be used to sign the kext. For more information see the 2013 WWDC presentations.
10 | //
11 | // Note that kext codesigning is performed in the kext post-build script as part of the process
12 | // of generating two versions (one suitable for 10.6 or later, the other for 10.9 or later).
13 | DISPLAYXFB_CODESIGN_KEXT=
14 |
15 |
16 | // Version information for the kexts.
17 | //
18 | // The kext should be identified by a version of form "x.y". The build script will then generate two
19 | // copies of each kext with versions "x.y.0" and "x.y.1" corresponding to the unsigned (10.8 or earlier)
20 | // and signed (10.9 or later) copies.
21 | //
22 | // Note that kext versioning is performed in the kext post-build script as part of the process
23 | // of generating two versions (one suitable for 10.6 or later, the other for 10.9 or later).
24 | DISPLAYXFB_VERSION = 1.1
25 |
26 |
27 | // The number of displays and VRAM size allocated by the kext.
28 | //
29 | // The driver can not dynamically allocate display buffer memory (well, not safely anyway). Each display
30 | // is allocated its own VRAM memory pool at boot, so these values must be reasonable even for systems
31 | // with limited memory.
32 | DISPLAYXFB_DISPLAYCOUNT = 1
33 | DISPLAYXFB_VRAMSIZE = 134217728
34 |
--------------------------------------------------------------------------------