1 /**************************************************************
3 * IOKit support for the Darwin X Server
6 * Original port to Mac OS X Server by John Carmack
7 * Port to Darwin 1.0 by Dave Zarzycki
8 * Significantly rewritten for XFree86 4.0.1 by Torrey Lyons
10 **************************************************************/
12 * Copyright (c) 2001-2004 Torrey T. Lyons. All Rights Reserved.
14 * Permission is hereby granted, free of charge, to any person obtaining a
15 * copy of this software and associated documentation files (the "Software"),
16 * to deal in the Software without restriction, including without limitation
17 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 * and/or sell copies of the Software, and to permit persons to whom the
19 * Software is furnished to do so, subject to the following conditions:
21 * The above copyright notice and this permission notice shall be included in
22 * all copies or substantial portions of the Software.
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
28 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
29 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 * DEALINGS IN THE SOFTWARE.
32 * Except as contained in this notice, the name(s) of the above copyright
33 * holders shall not be used in advertising or otherwise to promote the sale,
34 * use or other dealings in this Software without prior written authorization.
37 #if HAVE_XORG_CONFIG_H
38 #include <xorg-config.h>
42 #include <X11/Xproto.h>
46 #include "scrnintstr.h"
49 #include "mipointer.h"
53 #include <sys/types.h>
60 #include <mach/mach_interface.h>
63 #include <IOKit/IOKitLib.h>
64 #include <IOKit/hidsystem/IOHIDShared.h>
65 #include <IOKit/graphics/IOGraphicsLib.h>
66 #include <drivers/event_status_driver.h>
68 // Define this to work around bugs in the display drivers for
69 // older PowerBook G3's. If the X server starts without this
70 // #define, you don't need it.
71 #undef OLD_POWERBOOK_G3
77 int xfIOKitScreenIndex
= 0;
78 io_connect_t xfIOKitInputConnect
= 0;
80 static pthread_t inputThread
;
81 static EvGlobals
* evg
;
82 static mach_port_t masterPort
;
83 static mach_port_t notificationPort
;
84 static IONotificationPortRef NotificationPortRef
;
85 static mach_port_t pmNotificationPort
;
86 static io_iterator_t fbIter
;
91 * This is a callback from X to change the hardware colormap
92 * when using PsuedoColor.
94 static void XFIOKitStoreColors(
101 IOColorEntry
*newColors
;
102 ScreenPtr pScreen
= pmap
->pScreen
;
103 XFIOKitScreenPtr iokitScreen
= XFIOKIT_SCREEN_PRIV(pScreen
);
105 assert( newColors
= (IOColorEntry
*)
106 xalloc( numEntries
*sizeof(IOColorEntry
) ));
108 // Convert xColorItem values to IOColorEntry
109 // assume the colormap is PsuedoColor
110 // as we do not support DirectColor
111 for (i
= 0; i
< numEntries
; i
++) {
112 newColors
[i
].index
= pdefs
[i
].pixel
;
113 newColors
[i
].red
= pdefs
[i
].red
;
114 newColors
[i
].green
= pdefs
[i
].green
;
115 newColors
[i
].blue
= pdefs
[i
].blue
;
118 kr
= IOFBSetCLUT( iokitScreen
->fbService
, 0, numEntries
,
119 kSetCLUTByValue
, newColors
);
132 DeviceIntPtr pDevice
,
141 * Closes the connections to IOKit services
143 void DarwinModeGiveUp( void )
147 // we must close the HID System first
148 // because it is a client of the framebuffer
149 NXCloseEventStatus( darwinParamConnect
);
150 IOServiceClose( xfIOKitInputConnect
);
151 for (i
= 0; i
< screenInfo
.numScreens
; i
++) {
152 XFIOKitScreenPtr iokitScreen
=
153 XFIOKIT_SCREEN_PRIV(screenInfo
.screens
[i
]);
154 IOServiceClose( iokitScreen
->fbService
);
161 * Clear an event from the HID System event queue
163 static void ClearEvent(NXEvent
* ep
)
165 static NXEvent nullEvent
= {NX_NULLEVENT
, {0, 0 }, 0, -1, 0 };
168 ep
->data
.compound
.subType
= ep
->data
.compound
.misc
.L
[0] =
169 ep
->data
.compound
.misc
.L
[1] = 0;
175 * Read the HID System event queue, translate it to an X event,
176 * and queue it for processing.
178 static void *XFIOKitHIDThread(void *unused
)
181 NXEQElement
*oldHead
;
182 mach_msg_return_t kr
;
183 mach_msg_empty_rcv_t msg
;
185 kr
= mach_msg((mach_msg_header_t
*) &msg
, MACH_RCV_MSG
, 0,
186 sizeof(msg
), notificationPort
, 0, MACH_PORT_NULL
);
189 while (evg
->LLEHead
!= evg
->LLETail
) {
193 // Extract the next event from the kernel queue
194 oldHead
= (NXEQElement
*)&evg
->lleq
[evg
->LLEHead
];
195 ev_lock(&oldHead
->sema
);
197 ClearEvent(&oldHead
->event
);
198 evg
->LLEHead
= oldHead
->next
;
199 ev_unlock(&oldHead
->sema
);
201 memset(&xe
, 0, sizeof(xe
));
203 // These fields should be filled in for every event
204 xe
.u
.keyButtonPointer
.rootX
= ev
.location
.x
;
205 xe
.u
.keyButtonPointer
.rootY
= ev
.location
.y
;
206 xe
.u
.keyButtonPointer
.time
= GetTimeInMillis();
210 xe
.u
.u
.type
= MotionNotify
;
214 xe
.u
.u
.type
= ButtonPress
;
219 xe
.u
.u
.type
= ButtonRelease
;
223 // A newer kernel generates multi-button events with
224 // NX_SYSDEFINED. Button 2 isn't handled correctly by
225 // older kernels anyway. Just let NX_SYSDEFINED events
229 xe
.u
.u
.type
= ButtonPress
;
234 xe
.u
.u
.type
= ButtonRelease
;
240 xe
.u
.u
.type
= KeyPress
;
241 xe
.u
.u
.detail
= ev
.data
.key
.keyCode
;
245 xe
.u
.u
.type
= KeyRelease
;
246 xe
.u
.u
.detail
= ev
.data
.key
.keyCode
;
249 case NX_FLAGSCHANGED
:
250 xe
.u
.u
.type
= kXDarwinUpdateModifiers
;
251 xe
.u
.clientMessage
.u
.l
.longs0
= ev
.flags
;
255 if (ev
.data
.compound
.subType
== 7) {
256 xe
.u
.u
.type
= kXDarwinUpdateButtons
;
257 xe
.u
.clientMessage
.u
.l
.longs0
=
258 ev
.data
.compound
.misc
.L
[0];
259 xe
.u
.clientMessage
.u
.l
.longs1
=
260 ev
.data
.compound
.misc
.L
[1];
266 case NX_SCROLLWHEELMOVED
:
267 xe
.u
.u
.type
= kXDarwinScrollWheel
;
268 xe
.u
.clientMessage
.u
.s
.shorts0
=
269 ev
.data
.scrollWheel
.deltaAxis1
;
276 DarwinEQEnqueue(&xe
);
286 * Handle power state notifications
288 static void *XFIOKitPMThread(void *arg
)
290 ScreenPtr pScreen
= (ScreenPtr
)arg
;
291 XFIOKitScreenPtr iokitScreen
= XFIOKIT_SCREEN_PRIV(pScreen
);
294 mach_msg_return_t kr
;
295 mach_msg_empty_rcv_t msg
;
297 kr
= mach_msg((mach_msg_header_t
*) &msg
, MACH_RCV_MSG
, 0,
298 sizeof(msg
), pmNotificationPort
, 0, MACH_PORT_NULL
);
301 // display is powering down
302 if (msg
.header
.msgh_id
== 0) {
303 IOFBAcknowledgePM( iokitScreen
->fbService
);
304 xf86SetRootClip(pScreen
, FALSE
);
306 // display just woke up
307 else if (msg
.header
.msgh_id
== 1) {
308 xf86SetRootClip(pScreen
, TRUE
);
317 * Setup an IOFramebuffer service and connect the HID system to it.
319 static Bool
SetupFBandHID(
321 DarwinFramebufferPtr dfb
,
322 XFIOKitScreenPtr iokitScreen
)
325 io_service_t service
;
326 io_connect_t fbService
;
331 IODisplayModeInformation modeInfo
;
332 IODisplayModeID displayMode
, *allModes
;
333 IOIndex displayDepth
;
334 IOFramebufferInformation fbInfo
;
335 IOPixelInformation pixelInfo
;
336 StdFBShmem_t
*cshmem
;
338 // find and open the IOFrameBuffer service
339 service
= IOIteratorNext(fbIter
);
343 kr
= IOServiceOpen( service
, mach_task_self(),
344 kIOFBServerConnectType
, &iokitScreen
->fbService
);
345 IOObjectRelease( service
);
346 if (kr
!= KERN_SUCCESS
) {
347 ErrorF("Failed to connect as window server to screen %i.\n", index
);
350 fbService
= iokitScreen
->fbService
;
352 // create the slice of shared memory containing cursor state data
353 kr
= IOFBCreateSharedCursor( fbService
,
354 kIOFBCurrentShmemVersion
,
356 if (kr
!= KERN_SUCCESS
)
359 // Register for power management events for the framebuffer's device
360 kr
= IOCreateReceivePort(kOSNotificationMessageID
, &pmNotificationPort
);
362 kr
= IOConnectSetNotificationPort( fbService
, 0,
363 pmNotificationPort
, 0 );
364 if (kr
!= KERN_SUCCESS
) {
365 ErrorF("Power management registration failed.\n");
368 // SET THE SCREEN PARAMETERS
369 // get the current screen resolution, refresh rate and depth
370 kr
= IOFBGetCurrentDisplayModeAndDepth( fbService
,
373 if (kr
!= KERN_SUCCESS
)
376 // use the current screen resolution if the user
377 // only wants to change the refresh rate
378 if (darwinDesiredRefresh
!= -1 && darwinDesiredWidth
== 0) {
379 kr
= IOFBGetDisplayModeInformation( fbService
,
382 if (kr
!= KERN_SUCCESS
)
384 darwinDesiredWidth
= modeInfo
.nominalWidth
;
385 darwinDesiredHeight
= modeInfo
.nominalHeight
;
388 // use the current resolution and refresh rate
389 // if the user doesn't have a preference
390 if (darwinDesiredWidth
== 0) {
392 // change the pixel depth if desired
393 if (darwinDesiredDepth
!= -1) {
394 kr
= IOFBGetDisplayModeInformation( fbService
,
397 if (kr
!= KERN_SUCCESS
)
399 if (modeInfo
.maxDepthIndex
< darwinDesiredDepth
) {
400 ErrorF("Discarding screen %i:\n", index
);
401 ErrorF("Current screen resolution does not support desired pixel depth.\n");
405 displayDepth
= darwinDesiredDepth
;
406 kr
= IOFBSetDisplayModeAndDepth( fbService
, displayMode
,
408 if (kr
!= KERN_SUCCESS
)
412 // look for display mode with correct resolution and refresh rate
415 // get an array of all supported display modes
416 kr
= IOFBGetDisplayModeCount( fbService
, &numModes
);
417 if (kr
!= KERN_SUCCESS
)
419 assert(allModes
= (IODisplayModeID
*)
420 xalloc( numModes
* sizeof(IODisplayModeID
) ));
421 kr
= IOFBGetDisplayModes( fbService
, numModes
, allModes
);
422 if (kr
!= KERN_SUCCESS
)
425 for (i
= 0; i
< numModes
; i
++) {
426 kr
= IOFBGetDisplayModeInformation( fbService
, allModes
[i
],
428 if (kr
!= KERN_SUCCESS
)
431 if (modeInfo
.flags
& kDisplayModeValidFlag
&&
432 modeInfo
.nominalWidth
== darwinDesiredWidth
&&
433 modeInfo
.nominalHeight
== darwinDesiredHeight
) {
435 if (darwinDesiredDepth
== -1)
436 darwinDesiredDepth
= modeInfo
.maxDepthIndex
;
437 if (modeInfo
.maxDepthIndex
< darwinDesiredDepth
) {
438 ErrorF("Discarding screen %i:\n", index
);
439 ErrorF("Desired screen resolution does not support desired pixel depth.\n");
443 if ((darwinDesiredRefresh
== -1 ||
444 (darwinDesiredRefresh
<< 16) == modeInfo
.refreshRate
)) {
445 displayMode
= allModes
[i
];
446 displayDepth
= darwinDesiredDepth
;
447 kr
= IOFBSetDisplayModeAndDepth(fbService
,
450 if (kr
!= KERN_SUCCESS
)
459 ErrorF("Discarding screen %i:\n", index
);
460 ErrorF("Desired screen resolution or refresh rate is not supported.\n");
465 kr
= IOFBGetPixelInformation( fbService
, displayMode
, displayDepth
,
466 kIOFBSystemAperture
, &pixelInfo
);
467 if (kr
!= KERN_SUCCESS
)
471 /* x86 in 8bit mode currently needs fixed color map... */
472 if (pixelInfo
.bitsPerComponent
== 8 &&
473 pixelInfo
.componentCount
== 1)
475 pixelInfo
.pixelType
= kIOFixedCLUTPixels
;
479 #ifdef OLD_POWERBOOK_G3
480 if (pixelInfo
.pixelType
== kIOCLUTPixels
)
481 pixelInfo
.pixelType
= kIOFixedCLUTPixels
;
484 kr
= IOFBGetFramebufferInformationForAperture( fbService
,
487 if (kr
!= KERN_SUCCESS
)
490 // FIXME: 1x1 IOFramebuffers are sometimes used to indicate video
491 // outputs without a monitor connected to them. Since IOKit Xinerama
492 // does not really work, this often causes problems on PowerBooks.
493 // For now we explicitly check and ignore these screens.
494 if (fbInfo
.activeWidth
<= 1 || fbInfo
.activeHeight
<= 1) {
495 ErrorF("Discarding screen %i:\n", index
);
496 ErrorF("Invalid width or height.\n");
500 kr
= IOConnectMapMemory( fbService
, kIOFBCursorMemory
,
501 mach_task_self(), (vm_address_t
*) &cshmem
,
502 &shmemSize
, kIOMapAnywhere
);
503 if (kr
!= KERN_SUCCESS
)
505 iokitScreen
->cursorShmem
= cshmem
;
507 kr
= IOConnectMapMemory( fbService
, kIOFBSystemAperture
,
508 mach_task_self(), &vram
, &shmemSize
,
510 if (kr
!= KERN_SUCCESS
)
513 iokitScreen
->framebuffer
= (void*)vram
;
514 dfb
->x
= cshmem
->screenBounds
.minx
;
515 dfb
->y
= cshmem
->screenBounds
.miny
;
516 dfb
->width
= fbInfo
.activeWidth
;
517 dfb
->height
= fbInfo
.activeHeight
;
518 dfb
->pitch
= fbInfo
.bytesPerRow
;
519 dfb
->bitsPerPixel
= fbInfo
.bitsPerPixel
;
520 dfb
->colorBitsPerPixel
= pixelInfo
.componentCount
*
521 pixelInfo
.bitsPerComponent
;
522 dfb
->bitsPerComponent
= pixelInfo
.bitsPerComponent
;
524 // allocate shadow framebuffer
525 iokitScreen
->shadowPtr
= xalloc(dfb
->pitch
* dfb
->height
);
526 dfb
->framebuffer
= iokitScreen
->shadowPtr
;
528 // Note: Darwin kIORGBDirectPixels = X TrueColor, not DirectColor
529 if (pixelInfo
.pixelType
== kIORGBDirectPixels
) {
530 dfb
->colorType
= TrueColor
;
531 } else if (pixelInfo
.pixelType
== kIOCLUTPixels
) {
532 dfb
->colorType
= PseudoColor
;
533 } else if (pixelInfo
.pixelType
== kIOFixedCLUTPixels
) {
534 dfb
->colorType
= StaticColor
;
537 // Inform the HID system that the framebuffer is also connected to it.
538 kr
= IOConnectAddClient( xfIOKitInputConnect
, fbService
);
541 // We have to have added at least one screen
542 // before we can enable the cursor.
543 kr
= IOHIDSetCursorEnable(xfIOKitInputConnect
, TRUE
);
551 * DarwinModeAddScreen
552 * IOKit specific initialization for each screen.
554 Bool
DarwinModeAddScreen(
558 DarwinFramebufferPtr dfb
= SCREEN_PRIV(pScreen
);
559 XFIOKitScreenPtr iokitScreen
;
561 // allocate space for private per screen storage
562 iokitScreen
= xalloc(sizeof(XFIOKitScreenRec
));
563 XFIOKIT_SCREEN_PRIV(pScreen
) = iokitScreen
;
565 // setup hardware framebuffer
566 iokitScreen
->fbService
= 0;
567 if (! SetupFBandHID(index
, dfb
, iokitScreen
)) {
568 if (iokitScreen
->fbService
) {
569 IOServiceClose(iokitScreen
->fbService
);
579 * XFIOKitShadowUpdate
580 * Update the damaged regions of the shadow framebuffer on the screen.
582 static void XFIOKitShadowUpdate(ScreenPtr pScreen
,
585 DarwinFramebufferPtr dfb
= SCREEN_PRIV(pScreen
);
586 XFIOKitScreenPtr iokitScreen
= XFIOKIT_SCREEN_PRIV(pScreen
);
587 RegionPtr damage
= &pBuf
->damage
;
588 int numBox
= REGION_NUM_RECTS(damage
);
589 BoxPtr pBox
= REGION_RECTS(damage
);
590 int pitch
= dfb
->pitch
;
591 int bpp
= dfb
->bitsPerPixel
/8;
593 // Loop through all the damaged boxes
595 int width
, height
, offset
;
596 unsigned char *src
, *dst
;
598 width
= (pBox
->x2
- pBox
->x1
) * bpp
;
599 height
= pBox
->y2
- pBox
->y1
;
600 offset
= (pBox
->y1
* pitch
) + (pBox
->x1
* bpp
);
601 src
= iokitScreen
->shadowPtr
+ offset
;
602 dst
= iokitScreen
->framebuffer
+ offset
;
605 memcpy(dst
, src
, width
);
617 * DarwinModeSetupScreen
618 * Finalize IOKit specific initialization of each screen.
620 Bool
DarwinModeSetupScreen(
624 DarwinFramebufferPtr dfb
= SCREEN_PRIV(pScreen
);
627 // initalize cursor support
628 if (! XFIOKitInitCursor(pScreen
)) {
632 // initialize shadow framebuffer support
633 if (! shadowInit(pScreen
, XFIOKitShadowUpdate
, NULL
)) {
634 ErrorF("Failed to initalize shadow framebuffer for screen %i.\n",
639 // initialize colormap handling as needed
640 if (dfb
->colorType
== PseudoColor
) {
641 pScreen
->StoreColors
= XFIOKitStoreColors
;
644 // initialize power manager handling
645 pthread_create( &pmThread
, NULL
, XFIOKitPMThread
,
653 * DarwinModeInitOutput
654 * One-time initialization of IOKit output support.
656 void DarwinModeInitOutput(
660 static unsigned long generation
= 0;
663 io_service_t service
;
667 ErrorF("Display mode: IOKit\n");
669 // Allocate private storage for each screen's IOKit specific info
670 if (generation
!= serverGeneration
) {
671 xfIOKitScreenIndex
= AllocateScreenPrivateIndex();
672 generation
= serverGeneration
;
675 kr
= IOMasterPort(bootstrap_port
, &masterPort
);
678 // Find and open the HID System Service
679 // Do this now to be sure the Mac OS X window server is not running.
680 kr
= IOServiceGetMatchingServices( masterPort
,
681 IOServiceMatching( kIOHIDSystemClass
),
685 assert( service
= IOIteratorNext( iter
) );
687 kr
= IOServiceOpen( service
, mach_task_self(), kIOHIDServerConnectType
,
688 &xfIOKitInputConnect
);
689 if (kr
!= KERN_SUCCESS
) {
690 ErrorF("Failed to connect to the HID System as the window server!\n");
691 #ifdef DARWIN_WITH_QUARTZ
692 FatalError("Quit the Mac OS X window server or use the -quartz option.\n");
694 FatalError("Make sure you have quit the Mac OS X window server.\n");
698 IOObjectRelease( service
);
699 IOObjectRelease( iter
);
701 // Setup the event queue in memory shared by the kernel and X server
702 kr
= IOHIDCreateSharedMemory( xfIOKitInputConnect
,
703 kIOHIDCurrentShmemVersion
);
706 kr
= IOConnectMapMemory( xfIOKitInputConnect
, kIOHIDGlobalMemory
,
707 mach_task_self(), &shmem
, &shmemSize
,
711 evg
= (EvGlobals
*)(shmem
+ ((EvOffsets
*)shmem
)->evGlobalsOffset
);
713 assert(sizeof(EvGlobals
) == evg
->structSize
);
715 NotificationPortRef
= IONotificationPortCreate( masterPort
);
717 notificationPort
= IONotificationPortGetMachPort(NotificationPortRef
);
719 kr
= IOConnectSetNotificationPort( xfIOKitInputConnect
,
720 kIOHIDEventNotification
,
721 notificationPort
, 0 );
724 evg
->movedMask
|= NX_MOUSEMOVEDMASK
;
726 // find number of framebuffers
727 kr
= IOServiceGetMatchingServices( masterPort
,
728 IOServiceMatching( IOFRAMEBUFFER_CONFORMSTO
),
732 darwinScreensFound
= 0;
733 while ((service
= IOIteratorNext(fbIter
))) {
734 IOObjectRelease( service
);
735 darwinScreensFound
++;
737 IOIteratorReset(fbIter
);
742 * DarwinModeInitInput
743 * One-time initialization of IOKit input support.
745 void DarwinModeInitInput(
752 kr
= IOHIDSetEventsEnable(xfIOKitInputConnect
, TRUE
);
755 // Start event passing thread
756 assert( pipe(fd
) == 0 );
757 darwinEventReadFD
= fd
[0];
758 darwinEventWriteFD
= fd
[1];
759 fcntl(darwinEventReadFD
, F_SETFL
, O_NONBLOCK
);
760 pthread_create(&inputThread
, NULL
,
761 XFIOKitHIDThread
, NULL
);
767 * DarwinModeProcessEvent
768 * Process IOKit specific events.
770 void DarwinModeProcessEvent(
773 // No mode specific events
774 ErrorF("Unknown X event caught: %d\n", xe
->u
.u
.type
);