├── 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 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 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 | --------------------------------------------------------------------------------