/* * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* Copyright (c) 1992 NeXT Computer, Inc. All rights reserved. * * EventDriver.m - Event System module, ObjC implementation. * * The EventDriver is a pseudo-device driver. * * HISTORY * 31-Mar-92 Mike Paquette at NeXT * Created. * 04-Aug-93 Erik Kay at NeXT * minor API cleanup * 12-Dec-00 bubba at Apple. * Handle eject key cases on Pro Keyboard. * 20-Nov-01 ryepez at Apple * Replaced use of command queue with command gate. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Per-machine configuration info */ #include "IOHIDUserClient.h" #include #ifdef __cplusplus extern "C" { #include } #endif bool displayWranglerUp( OSObject *, void *, IOService * ); static IOHIDSystem * evInstance = 0; MasterAudioFunctions *masterAudioFunctions = 0; #define xpr_ev_cursor(x, a, b, c, d, e) #define PtInRect(ptp,rp) \ ((ptp)->x >= (rp)->minx && (ptp)->x < (rp)->maxx && \ (ptp)->y >= (rp)->miny && (ptp)->y < (rp)->maxy) #ifndef kIOFBWaitCursorFramesKey #define kIOFBWaitCursorFramesKey "IOFBWaitCursorFrames" #endif #ifndef kIOFBWaitCursorPeriodKey #define kIOFBWaitCursorPeriodKey "IOFBWaitCursorPeriod" #endif #ifndef abs #define abs(_a) ((_a >= 0) ? _a : -_a) #endif static inline unsigned AbsoluteTimeToTick( AbsoluteTime * ts ) { UInt64 nano; absolutetime_to_nanoseconds(*ts, &nano); return( nano >> 24 ); } static inline void TickToAbsoluteTime( unsigned tick, AbsoluteTime * ts ) { UInt64 nano = ((UInt64) tick) << 24; nanoseconds_to_absolutetime(nano, ts); } #define EV_NS_TO_TICK(ns) AbsoluteTimeToTick(ns) #define EV_TICK_TO_NS(tick,ns) TickToAbsoluteTime(tick,ns) /* COMMAND GATE COMPATIBILITY TYPE DEFS */ typedef struct _IOHIDCmdGateActionArgs { void* arg0; void* arg1; void* arg2; void* arg3; void* arg4; void* arg5; void* arg6; void* arg7; void* arg8; void* arg9; void* arg10; } IOHIDCmdGateActionArgs; /* END COMMAND GATE COMPATIBILITY TYPE DEFS */ /* HID SYSTEM EVENT LOCK OUT SUPPORT */ static bool keySwitchLocked = false; static IONotifier * switchNotification = 0; // IONotificationHandler static bool keySwitchNotificationHandler(void *target, void *refCon, IOService *service) { keySwitchLocked = (service->getProperty("Keyswitch") == kOSBooleanTrue) ? true : false; return true; } /* END HID SYSTEM EVENT LOCK OUT SUPPORT */ // RY: Consume any keyboard events that come in before the // dealine after the system wakes up or if the keySwitch is locked #define SHOULD_CONSUME_EVENT(absTimeStamp) \ ( keySwitchLocked || \ (CMP_ABSOLUTETIME(&absTimeStamp, &stateChangeDeadline) <= 0)) #define TICKLE_DISPLAY \ { \ if (displayManager != NULL) \ displayManager->activityTickle(0,0); \ } enum { // Options for IOHIDPostEvent() kIOHIDSetGlobalEventFlags = 0x00000001, kIOHIDSetCursorPosition = 0x00000002, kIOHIDSetRelativeCursorPosition = 0x00000004 }; #define kIOHIDPowerOnThresholdNS 1000000000ULL #define kIOHIDRelativeTickleThresholdNS 50000000ULL #define kIOHIDRelativeTickleThresholdPixel 20 static AbsoluteTime gIOHIDPowerOnThresoldAbsoluteTime; static AbsoluteTime gIOHIDRelativeTickleThresholdAbsoluteTime; typedef struct _CachedMouseEventStruct { AbsoluteTime eventDeadline; UInt32 lastButtons; SInt32 accumX; SInt32 accumY; NXTabletPointDataPtr tabletPointDataPtr; NXTabletProximityDataPtr tabletProximityDataPtr; } CachedMouseEventStruct; static OSSymbol * GetKeyForService(OSObject * newService) { char key[9]; sprintf(key, "%x", (UInt32)newService); return OSSymbol::withCString(key); } static CachedMouseEventStruct * GetCachedMouseEventForKey(OSDictionary *events, OSSymbol * key) { OSData * data; CachedMouseEventStruct * retVal = 0; if (events && key && (data = events->getObject(key))) retVal = (CachedMouseEventStruct *)data->getBytesNoCopy(); return retVal; } static CachedMouseEventStruct * GetCachedMouseEventForService(OSDictionary *events, OSObject *service) { OSSymbol * key; CachedMouseEventStruct * retVal = 0; if (key = GetKeyForService(service)) { retVal = GetCachedMouseEventForKey(events, key); key->release(); } return retVal; } static void AppendNewCachedMouseEventForService(OSDictionary *events, OSObject *service) { OSSymbol * key; OSData * data; CachedMouseEventStruct temp; if (key = GetKeyForService(service)) { bzero(&temp, sizeof(CachedMouseEventStruct)); data = OSData::withBytes(&temp, sizeof(CachedMouseEventStruct)); events->setObject(key, data); data->release(); key->release(); } } static void RemoveCachedMouseEventForService(OSDictionary *events, OSObject *service) { OSSymbol * key; if (key = GetKeyForService(service)) { events->removeObject(key); key->release(); } } static UInt8 stickyKeysState = false; static void notifyHIDevices(IOService *service, OSArray *hiDevices, UInt32 type) { IOHIKeyboard *keyboard; if((type != kIOHIDSystem508MouseClickMessage) || !stickyKeysState || !hiDevices) return; for(int index=0; indexgetCount(); index++) { if (keyboard = OSDynamicCast(IOHIKeyboard, hiDevices->getObject(index))) keyboard->IOHIKeyboard::message(kIOHIDSystem508MouseClickMessage, service); } } #define super IOService OSDefineMetaClassAndStructors(IOHIDSystem, IOService); /* Return the current instance of the EventDriver, or 0 if none. */ IOHIDSystem * IOHIDSystem::instance() { return evInstance; } bool IOHIDSystem::init(OSDictionary * properties) { if (!super::init(properties)) return false; /* * Initialize minimal state. */ evScreen = NULL; timerES = 0; eventConsumerES = 0; cmdGate = 0; workLoop = 0; cachedEventFlags = 0; lastProximity = false; AbsoluteTime_to_scalar(&lastEventTime) = 0; AbsoluteTime_to_scalar(&stateChangeDeadline) = 0; ioHIDevices = OSArray::withCapacity(2); cachedButtonStates = OSDictionary::withCapacity(3); // RY: Populate cachedButtonStates key=0 with a button State // This will cover all pointing devices that don't support // the new private methods. AppendNewCachedMouseEventForService(cachedButtonStates, 0); nanoseconds_to_absolutetime(kIOHIDPowerOnThresholdNS, &gIOHIDPowerOnThresoldAbsoluteTime); nanoseconds_to_absolutetime(kIOHIDRelativeTickleThresholdNS, &gIOHIDRelativeTickleThresholdAbsoluteTime); return true; } IOHIDSystem * IOHIDSystem::probe(IOService * provider, SInt32 * score) { if (!super::probe(provider,score)) return 0; return this; } /* * Perform reusable initialization actions here. */ IOWorkLoop * IOHIDSystem::getWorkLoop() const { return workLoop; } bool IOHIDSystem::start(IOService * provider) { bool iWasStarted = false; do { if (!super::start(provider)) break; evInstance = this; /* A few details to be set up... */ pointerLoc.x = INIT_CURSOR_X; pointerLoc.y = INIT_CURSOR_Y; pointerDelta.x = 0; pointerDelta.y = 0; evScreenSize = sizeof(EvScreen) * 32; evScreen = (void *) IOMalloc(evScreenSize); savedParameters = OSDictionary::withCapacity(4); if (!evScreen || !savedParameters) break; bzero(evScreen, evScreenSize); firstWaitCursorFrame = EV_WAITCURSOR; maxWaitCursorFrame = EV_MAXCURSOR; createParameters(); /* * Start up the work loop */ workLoop = IOWorkLoop::workLoop(); cmdGate = IOCommandGate::commandGate (this); timerES = IOTimerEventSource::timerEventSource (this, (IOTimerEventSource::Action) &_periodicEvents ); eventConsumerES = IOInterruptEventSource::interruptEventSource (this, (IOInterruptEventSource::Action) &doKickEventConsumer); vblES = IOTimerEventSource::timerEventSource (this, &_vblEvent ); if (!workLoop || !cmdGate || !timerES || !eventConsumerES || !vblES) break; if ((workLoop->addEventSource(cmdGate) != kIOReturnSuccess) || (workLoop->addEventSource(timerES) != kIOReturnSuccess) || (workLoop->addEventSource(vblES) != kIOReturnSuccess) || (workLoop->addEventSource(eventConsumerES) != kIOReturnSuccess)) break; publishNotify = addNotification( gIOPublishNotification, serviceMatching("IOHIDevice"), &IOHIDSystem::publishNotificationHandler, this, 0 ); if (!publishNotify) break; terminateNotify = addNotification( gIOTerminatedNotification, serviceMatching("IOHIDevice"), &IOHIDSystem::terminateNotificationHandler, this, 0 ); if (!terminateNotify) break; // RY: Listen to the root domain rootDomain = (IOService *)getPMRootDomain(); if (rootDomain) rootDomain->registerInterestedDriver(this); /* * IOHIDSystem serves both as a service and a nub (we lead a double * life). Register ourselves as a nub to kick off matching. */ registerService(); addNotification( gIOPublishNotification, serviceMatching("IODisplayWrangler"), &IOHIDSystem::publishNotificationHandler, this, 0 ); // Get notified everytime AppleKeyswitch registers (each time keyswitch changes) switchNotification = addNotification(gIOPublishNotification, nameMatching("AppleKeyswitch"), (IOServiceNotificationHandler)keySwitchNotificationHandler, this, 0); iWasStarted = true; // Let's go ahead and cache our registry name. // This was added to remove a call to getName while // we are disabling preemption registryName = getName(); } while(false); if (!iWasStarted) evInstance = 0; return iWasStarted; } // powerStateDidChangeTo // // The display wrangler has changed state, so the displays have changed // state, too. We save the new state. IOReturn IOHIDSystem::powerStateDidChangeTo( IOPMPowerFlags theFlags, unsigned long, IOService * service) { AbsoluteTime delta; if (service == displayManager) { displayState = theFlags; } else if (service == rootDomain) { if (theFlags & kIOPMPowerOn) { clock_get_uptime(&stateChangeDeadline); nanoseconds_to_absolutetime(kIOHIDPowerOnThresholdNS, &delta); ADD_ABSOLUTETIME(&stateChangeDeadline, &delta); } } return IOPMNoErr; } bool IOHIDSystem::publishNotificationHandler( void * target, void * /* ref */, IOService * newService ) { IOHIDSystem * self = (IOHIDSystem *) target; // avoiding OSDynamicCast & dependency on graphics family if( newService->metaCast("IODisplayWrangler")) { if( !self->displayManager) { self->displayManager = newService; self->displayState = newService->registerInterestedDriver(self); } return true; } self->attach( newService ); if(OSDynamicCast(IOHIDevice, newService)) { if (self->ioHIDevices) { if (self->ioHIDevices->getNextIndexOfObject(newService, 0) == -1) self->ioHIDevices->setObject(newService); } if(self->eventsOpen) self->registerEventSource((IOHIDevice *) newService); } return true; } bool IOHIDSystem::terminateNotificationHandler( void * target, void * /* ref */, IOService * service ) { IOHIDSystem * self = (IOHIDSystem *) target; int index; if( self->eventsOpen && OSDynamicCast(IOHIDevice, service)) { service->close(self); } self->detach(service); if (self->ioHIDevices) { if ((index = self->ioHIDevices->getNextIndexOfObject(service, 0)) != -1) self->ioHIDevices->removeObject(index); } // RY: Remove this object from the cachedButtonState if (OSDynamicCast(IOHIPointing, service)) { RemoveCachedMouseEventForService(self->cachedButtonStates, service); } return true; } /* * Free locally allocated resources, and then ourselves. */ void IOHIDSystem::free() { if (evScreen) IOFree( (void *)evScreen, evScreenSize ); evScreen = (void *)0; evScreenSize = 0; if (timerES) timerES->release(); if (eventConsumerES) eventConsumerES->release(); if (cmdGate) {evClose(); cmdGate->release();} if (workLoop) workLoop->release(); if (publishNotify) publishNotify->release(); if (switchNotification) switchNotification->release(); if (ioHIDevices) ioHIDevices->release(); if (cachedButtonStates) cachedButtonStates->release(); super::free(); } /* * Open the driver for business. This call must be made before * any other calls to the Event driver. We can only be opened by * one user at a time. */ IOReturn IOHIDSystem::evOpen(void) { IOReturn r = kIOReturnSuccess; if ( evOpenCalled == true ) { r = kIOReturnBusy; goto done; } evOpenCalled = true; if (!evInitialized) { evInitialized = true; curBright = EV_SCREEN_MAX_BRIGHTNESS; // FIXME: Set from NVRAM? curVolume = EV_AUDIO_MAX_VOLUME / 2; // FIXME: Set from NVRAM? // Put code here that is to run on the first open ONLY. } done: return r; } IOReturn IOHIDSystem::evClose(void){ return cmdGate->runAction((IOCommandGate::Action)doEvClose); } IOReturn IOHIDSystem::doEvClose(IOHIDSystem *self) /* IOCommandGate::Action */ { return self->evCloseGated(); } IOReturn IOHIDSystem::evCloseGated(void) { if ( evOpenCalled == false ) return kIOReturnBadArgument; // Early close actions here forceAutoDimState(false); if( cursorEnabled) hideCursor(); cursorStarted = false; cursorEnabled = false; // Release the input devices. detachEventSources(); // Tear down the shared memory area if set up // if ( eventsOpen == true ) // unmapEventShmem(eventPort); // Clear screens registry and related data if ( evScreen != (void *)0 ) { screens = 0; lastShmemPtr = (void *)0; } // Remove port notification for the eventPort and clear the port out setEventPort(MACH_PORT_NULL); // ipc_port_release_send(event_port); // Clear local state to shutdown evOpenCalled = false; eventsOpen = false; return kIOReturnSuccess; } // // Dispatch state to screens registered with the Event Driver // Pending state changes for a device may be coalesced. // // // This should be run from a command gate action. // void IOHIDSystem::evDispatch( /* command */ EvCmd evcmd) { Point p; if( !eventsOpen) return; for( int i = 0; i < screens; i++ ) { EvScreen *esp = &((EvScreen*)evScreen)[i]; if ( esp->instance ) { p.x = evg->cursorLoc.x; // Copy from shmem. p.y = evg->cursorLoc.y; bool onscreen = (0 != (cursorScreens & (1 << i))); switch ( evcmd ) { case EVMOVE: if (onscreen) esp->instance->moveCursor(&p, evg->frame); break; case EVSHOW: if (onscreen) esp->instance->showCursor(&p, evg->frame); break; case EVHIDE: if (onscreen) esp->instance->hideCursor(); break; case EVLEVEL: case EVNOP: /* lets keep that compiler happy */ break; } } } } // // Dispatch mechanism for special key press. If a port has been registered, // a message is built to be sent out to that port notifying that the key has // changed state. A level in the range 0-64 is provided for convenience. // void IOHIDSystem::evSpecialKeyMsg(unsigned key, /* direction */ unsigned dir, /* flags */ unsigned f, /* level */ unsigned l) { mach_port_t dst_port; struct evioSpecialKeyMsg *msg; static const struct evioSpecialKeyMsg init_msg = { { MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, // mach3xxx, is the right? MACH_MSG_TYPE_MAKE_SEND), // mach_msg_bits_t msgh_bits; sizeof (struct evioSpecialKeyMsg), // mach_msg_size_t msgh_size; MACH_PORT_NULL, // mach_port_t msgh_remote_port; MACH_PORT_NULL, // mach_port_t msgh_local_port; 0, // mach_msg_size_t msgh_reserved; EV_SPECIAL_KEY_MSG_ID // mach_msg_id_t msgh_id; }, 0, /* key */ 0, /* direction */ 0, /* flags */ 0 /* level */ }; if ( (dst_port = specialKeyPort(key)) == MACH_PORT_NULL ) return; msg = (struct evioSpecialKeyMsg *) IOMalloc( sizeof (struct evioSpecialKeyMsg) ); if ( msg == NULL ) return; // Initialize the message. bcopy( &init_msg, msg, sizeof (struct evioSpecialKeyMsg) ); msg->Head.msgh_remote_port = dst_port; msg->key = key; msg->direction = dir; msg->flags = f; msg->level = l; // Send the message out from the command gate. cmdGate->runAction((IOCommandGate::Action)doSpecialKeyMsg,(void*)msg); } // // Reset instance variables to their default state for mice/pointers // void IOHIDSystem::_resetMouseParameters() { cmdGate->runAction((IOCommandGate::Action)doResetMouseParameters); } IOReturn IOHIDSystem::doResetMouseParameters(IOHIDSystem *self) /* IOCommandGate::Action */ { self->resetMouseParametersGated(); return kIOReturnSuccess; } void IOHIDSystem::resetMouseParametersGated(void) { if ( eventsOpen == false ) return; OSDictionary *tempDict = OSDictionary::withCapacity(3); UInt64 nano; nanoseconds_to_absolutetime( EV_DCLICKTIME, &clickTimeThresh); clickSpaceThresh.x = clickSpaceThresh.y = EV_DCLICKSPACE; AbsoluteTime_to_scalar( &clickTime) = 0; clickLoc.x = clickLoc.y = -EV_DCLICKSPACE; clickState = 1; nanoseconds_to_absolutetime( DAUTODIMPERIOD, &autoDimPeriod); clock_get_uptime( &autoDimTime); ADD_ABSOLUTETIME( &autoDimTime, &autoDimPeriod); dimmedBrightness = DDIMBRIGHTNESS; if (tempDict) { UInt32 tempClickSpace[] = {clickSpaceThresh.x, clickSpaceThresh.y}; makeInt32ArrayParamProperty( tempDict, kIOHIDClickSpaceKey, tempClickSpace, sizeof(tempClickSpace)/sizeof(UInt32) ); nano = EV_DCLICKTIME; makeNumberParamProperty( tempDict, kIOHIDClickTimeKey, nano, 64 ); nano = DAUTODIMPERIOD; makeNumberParamProperty( tempDict, kIOHIDAutoDimThresholdKey, nano, 64 ); makeNumberParamProperty( tempDict, kIOHIDAutoDimBrightnessKey, dimmedBrightness, 32 ); setParamPropertiesGated(tempDict); tempDict->release(); } } void IOHIDSystem::_resetKeyboardParameters() { } /* * Methods exported by the EventDriver. * * The screenRegister protocol is used by frame buffer drivers to register * themselves with the Event Driver. These methods are called in response * to a registerSelf or unregisterSelf message received from the Event * Driver. */ int IOHIDSystem::registerScreen(IOGraphicsDevice * instance, /* bounds */ Bounds * bp) { EvScreen *esp; OSNumber *num; if( (false == eventsOpen) || (0 == bp) ) { return -1; } if ( lastShmemPtr == (void *)0 ) lastShmemPtr = evs; /* shmemSize and bounds already set */ esp = &((EvScreen*)evScreen)[screens]; esp->instance = instance; esp->bounds = bp; // Update our idea of workSpace bounds if ( bp->minx < workSpace.minx ) workSpace.minx = bp->minx; if ( bp->miny < workSpace.miny ) workSpace.miny = bp->miny; if ( bp->maxx < workSpace.maxx ) workSpace.maxx = bp->maxx; if ( esp->bounds->maxy < workSpace.maxy ) workSpace.maxy = bp->maxy; if( (num = OSDynamicCast(OSNumber, instance->getProperty(kIOFBWaitCursorFramesKey))) && (num->unsigned32BitValue() > maxWaitCursorFrame)) { firstWaitCursorFrame = 0; maxWaitCursorFrame = num->unsigned32BitValue(); evg->lastFrame = maxWaitCursorFrame; } if( (num = OSDynamicCast(OSNumber, instance->getProperty(kIOFBWaitCursorPeriodKey)))) clock_interval_to_absolutetime_interval(num->unsigned32BitValue(), kNanosecondScale, &waitFrameRate); return(SCREENTOKEN + screens++); } void IOHIDSystem::unregisterScreen(int index) { cmdGate->runAction((IOCommandGate::Action)doUnregisterScreen, (void *)index); } IOReturn IOHIDSystem::doUnregisterScreen (IOHIDSystem *self, void * arg0) /* IOCommandGate::Action */ { int index = (int) arg0; self->unregisterScreenGated(index); return kIOReturnSuccess; } void IOHIDSystem::unregisterScreenGated(int index) { index -= SCREENTOKEN; if ( eventsOpen == false || index < 0 || index >= screens ) return; hideCursor(); // clear the state for the screen ((EvScreen*)evScreen)[index].instance = 0; // Put the cursor someplace reasonable if it was on the destroyed screen cursorScreens &= ~(1 << index); // This will jump the cursor back on screen setCursorPosition((Point *)&evg->cursorLoc, true); showCursor(); } /* Member of EventClient protocol * * Absolute position input devices and some specialized output devices * may need to know the bounding rectangle for all attached displays. * The following method returns a Bounds* for the workspace. Please note * that the bounds are kept as signed values, and that on a multi-display * system the minx and miny values may very well be negative. */ Bounds * IOHIDSystem::workspaceBounds() { return &workSpace; } IOReturn IOHIDSystem::createShmem(void* p1, void*, void*, void*, void*, void*) { // IOMethod return cmdGate->runAction((IOCommandGate::Action)doCreateShmem, p1); } IOReturn IOHIDSystem::doCreateShmem (IOHIDSystem *self, void * arg0) /* IOCommandGate::Action */ { return self->createShmemGated(arg0); } IOReturn IOHIDSystem::createShmemGated(void* p1) { int shmemVersion = (int)p1; IOByteCount size; if( shmemVersion != kIOHIDCurrentShmemVersion) return kIOReturnUnsupported; if( 0 == globalMemory) { size = sizeof(EvOffsets) + sizeof(EvGlobals); globalMemory = IOBufferMemoryDescriptor::withOptions( kIODirectionNone | kIOMemoryKernelUserShared, size ); if( !globalMemory) return kIOReturnNoMemory; shmem_addr = (vm_offset_t) globalMemory->getBytesNoCopy(); shmem_size = size; } initShmem(); return kIOReturnSuccess; } // Initialize the shared memory area. // // This should be run from a command gate action. void IOHIDSystem::initShmem() { int i; EvOffsets *eop; /* top of sharedMem is EvOffsets structure */ eop = (EvOffsets *) shmem_addr; bzero( (void*)shmem_addr, shmem_size); /* fill in EvOffsets structure */ eop->evGlobalsOffset = sizeof(EvOffsets); eop->evShmemOffset = eop->evGlobalsOffset + sizeof(EvGlobals); /* find pointers to start of globals and private shmem region */ evg = (EvGlobals *)((char *)shmem_addr + eop->evGlobalsOffset); evs = (void *)((char *)shmem_addr + eop->evShmemOffset); evg->version = kIOHIDCurrentShmemVersion; evg->structSize = sizeof( EvGlobals); /* Set default wait cursor parameters */ evg->waitCursorEnabled = TRUE; evg->globalWaitCursorEnabled = TRUE; evg->lastFrame = maxWaitCursorFrame; evg->waitThreshold = (12 * EV_TICKS_PER_SEC) / 10; clock_interval_to_absolutetime_interval(DefaultWCFrameRate, kNanosecondScale, &waitFrameRate); clock_interval_to_absolutetime_interval(DefaultWCSustain, kNanosecondScale, &waitSustain); AbsoluteTime_to_scalar(&waitSusTime) = 0; AbsoluteTime_to_scalar(&waitFrameTime) = 0; EV_TICK_TO_NS(10,&periodicEventDelta); /* Set up low-level queues */ lleqSize = LLEQSIZE; for (i=lleqSize; --i != -1; ) { evg->lleq[i].event.type = 0; AbsoluteTime_to_scalar(&evg->lleq[i].event.time) = 0; evg->lleq[i].event.flags = 0; ev_init_lock(&evg->lleq[i].sema); evg->lleq[i].next = i+1; } evg->LLELast = 0; evg->lleq[lleqSize-1].next = 0; evg->LLEHead = evg->lleq[evg->LLELast].next; evg->LLETail = evg->lleq[evg->LLELast].next; evg->buttons = 0; evg->eNum = INITEVENTNUM; evg->eventFlags = 0; AbsoluteTime ts; unsigned tick; clock_get_uptime( &ts); tick = EV_NS_TO_TICK(&ts); if ( tick == 0 ) tick = 1; // No zero values allowed! evg->VertRetraceClock = tick; evg->cursorLoc.x = pointerLoc.x; evg->cursorLoc.y = pointerLoc.y; evg->dontCoalesce = 0; evg->dontWantCoalesce = 0; evg->wantPressure = 0; evg->wantPrecision = 0; evg->mouseRectValid = 0; evg->movedMask = 0; ev_init_lock( &evg->cursorSema ); ev_init_lock( &evg->waitCursorSema ); // Set eventsOpen last to avoid race conditions. eventsOpen = true; } // // Set the event port. The event port is both an ownership token // and a live port we hold send rights on. The port is owned by our client, // the WindowServer. We arrange to be notified on a port death so that // we can tear down any active resources set up during this session. // An argument of PORT_NULL will cause us to forget any port death // notification that's set up. // // This should be run from a command gate action. // void IOHIDSystem::setEventPort(mach_port_t port) { static struct _eventMsg init_msg = { { // mach_msg_bits_t msgh_bits; MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,0), // mach_msg_size_t msgh_size; sizeof (struct _eventMsg), // mach_port_t msgh_remote_port; MACH_PORT_NULL, // mach_port_t msgh_local_port; MACH_PORT_NULL, // mach_msg_size_t msgh_reserved; 0, // mach_msg_id_t msgh_id; 0 } }; if ( eventMsg == NULL ) eventMsg = IOMalloc( sizeof (struct _eventMsg) ); eventPort = port; // Initialize the events available message. *((struct _eventMsg *)eventMsg) = init_msg; ((struct _eventMsg *)eventMsg)->h.msgh_remote_port = port; // RY: Added this check so that the event consumer // can get notified if the queue in not empty. // Otherwise the event consumer will never get a // notification. if (EventsInQueue()) kickEventConsumer(); } // // Set the port to be used for a special key notification. This could be more // robust about letting ports be set... // IOReturn IOHIDSystem::setSpecialKeyPort( /* keyFlavor */ int special_key, /* keyPort */ mach_port_t key_port) { if ( special_key >= 0 && special_key < NX_NUM_SCANNED_SPECIALKEYS ) _specialKeyPort[special_key] = key_port; return kIOReturnSuccess; } mach_port_t IOHIDSystem::specialKeyPort(int special_key) { if ( special_key >= 0 && special_key < NX_NUM_SCANNED_SPECIALKEYS ) return _specialKeyPort[special_key]; return MACH_PORT_NULL; } // // Helper functions for postEvent // static inline int myAbs(int a) { return(a > 0 ? a : -a); } short IOHIDSystem::getUniqueEventNum() { while (++evg->eNum == NULLEVENTNUM) ; /* sic */ return(evg->eNum); } // postEvent // // This routine actually places events in the event queue which is in // the EvGlobals structure. It is called from all parts of the ev // driver. // // This should be run from a command gate action. // void IOHIDSystem::postEvent(int what, /* at */ Point * location, /* atTime */ AbsoluteTime ts, /* withData */ NXEventData * myData) { NXEQElement * theHead = (NXEQElement *) &evg->lleq[evg->LLEHead]; NXEQElement * theLast = (NXEQElement *) &evg->lleq[evg->LLELast]; NXEQElement * theTail = (NXEQElement *) &evg->lleq[evg->LLETail]; int wereEvents; unsigned theClock = EV_NS_TO_TICK(&ts); if(CMP_ABSOLUTETIME(&ts, &lastEventTime) < 0) { ts = lastEventTime; } lastEventTime = ts; /* Some events affect screen dimming */ if (EventCodeMask(what) & NX_UNDIMMASK) { autoDimTime = ts; ADD_ABSOLUTETIME( &autoDimTime, &autoDimPeriod); if (autoDimmed) undoAutoDim(); } // Update the PS VertRetraceClock off of the timestamp if it looks sane if ( theClock > (unsigned)evg->VertRetraceClock && theClock < (unsigned)(evg->VertRetraceClock + (20 * EV_TICK_TIME)) ) evg->VertRetraceClock = theClock; wereEvents = EventsInQueue(); xpr_ev_post("postEvent: what %d, X %d Y %d Q %d, needKick %d\n", what,location->x,location->y, EventsInQueue(), needToKickEventConsumer); if ((!evg->dontCoalesce) /* Coalescing enabled */ && (theHead != theTail) && (theLast->event.type == what) && (EventCodeMask(what) & COALESCEEVENTMASK) && ev_try_lock(&theLast->sema)) { /* coalesce events */ theLast->event.location.x = location->x; theLast->event.location.y = location->y; absolutetime_to_nanoseconds(ts, &theLast->event.time); if (myData != NULL) theLast->event.data = *myData; ev_unlock(&theLast->sema); } else if (theTail->next != evg->LLEHead) { /* store event in tail */ theTail->event.type = what; theTail->event.location.x = location->x; theTail->event.location.y = location->y; theTail->event.flags = evg->eventFlags; absolutetime_to_nanoseconds(ts, &theLast->event.time); theTail->event.window = 0; if (myData != NULL) theTail->event.data = *myData; switch(what) { case NX_LMOUSEDOWN: theTail->event.data.mouse.eventNum = leftENum = getUniqueEventNum(); // Inform the devices that the mouse was clicked notifyHIDevices(this, ioHIDevices, kIOHIDSystem508MouseClickMessage); break; case NX_RMOUSEDOWN: theTail->event.data.mouse.eventNum = rightENum = getUniqueEventNum(); // Inform the devices that the mouse was clicked notifyHIDevices(this, ioHIDevices, kIOHIDSystem508MouseClickMessage); break; case NX_LMOUSEUP: theTail->event.data.mouse.eventNum = leftENum; leftENum = NULLEVENTNUM; break; case NX_RMOUSEUP: theTail->event.data.mouse.eventNum = rightENum; rightENum = NULLEVENTNUM; break; } if (EventCodeMask(what) & PRESSUREEVENTMASK) { // if this is a mouse event, then fill in the mouse pressure field if (EventCodeMask(what) & MOUSEEVENTMASK) theTail->event.data.mouse.pressure = lastPressure; // else if this is a mouse moved/dragged event, // then fill in the mousemove pressure field else if (EventCodeMask(what) & MOVEDEVENTMASK) theTail->event.data.mouseMove.reserved1 = lastPressure; // this case will not happen unless someone modifies PRESSUREEVENTMASK else IOLog("%s: postEvent unknown pressure event, cannot fill pressure.\n", registryName); } if (EventCodeMask(what) & MOUSEEVENTMASK) { /* Click state */ AbsoluteTime delta = ts; SUB_ABSOLUTETIME( &delta, &clickTime); if ((CMP_ABSOLUTETIME(&delta, &clickTimeThresh) <= 0) && (myAbs(location->x - clickLoc.x) <= clickSpaceThresh.x) && (myAbs(location->y - clickLoc.y) <= clickSpaceThresh.y)) { if ((what == NX_LMOUSEDOWN)||(what == NX_RMOUSEDOWN)) { clickTime = ts; theTail->event.data.mouse.click = ++clickState; } else { theTail->event.data.mouse.click = clickState; } } else if ((what == NX_LMOUSEDOWN)||(what == NX_RMOUSEDOWN)) { clickLoc = *location; clickTime = ts; clickState = 1; theTail->event.data.mouse.click = clickState; } else theTail->event.data.mouse.click = 0; } #if PMON pmon_log_event(PMON_SOURCE_EV, KP_EV_POST_EVENT, what, evg->eventFlags, theClock); #endif evg->LLETail = theTail->next; evg->LLELast = theLast->next; if ( ! wereEvents ) // Events available, so wake event consumer kickEventConsumer(); } else { /* * if queue is full, ignore event, too hard to take care of all cases */ IOLog("%s: postEvent LLEventQueue overflow.\n", registryName); kickEventConsumer(); #if PMON pmon_log_event( PMON_SOURCE_EV, KP_EV_QUEUE_FULL, what, evg->eventFlags, theClock); #endif } } /* * - kickEventConsumer * * Try to send a message out to let the event consumer know that * there are now events available for consumption. */ void IOHIDSystem::kickEventConsumer() { xpr_ev_post("kickEventConsumer (need == %d)\n", needToKickEventConsumer,2,3,4,5); if ( needToKickEventConsumer == true ) return; // Request is already pending needToKickEventConsumer = true; // Posting a request now // Trigger eventConsumerES, so that doKickEventConsumer // is run from the workloop thread. eventConsumerES->interruptOccurred(0, 0, 0); } /* * HID System no longer runs the following methods on the EventSource thread. * Instead all are run from the caller thread through the use of command gates. * This will limit the amount of context switching that takes place. */ /* * The following methods are executed from the caller thread only. */ /* * The method is now called from a command gate and is run on the caller thread */ void IOHIDSystem::doSpecialKeyMsg(IOHIDSystem * self, struct evioSpecialKeyMsg *msg) /* IOCommandGate::Action */ { kern_return_t r; xpr_ev_post("doSpecialKeyMsg 0x%x\n", msg,2,3,4,5); /* FIXME: Don't block */ r = mach_msg_send_from_kernel( &msg->Head, msg->Head.msgh_size); xpr_ev_post("doSpecialKeyMsg: msg_send() == %d\n",r,2,3,4,5); if ( r != MACH_MSG_SUCCESS ) { IOLog("%s: doSpecialKeyMsg msg_send returned %d\n", self->registryName, r); } if ( r == MACH_SEND_INVALID_DEST ) /* Invalidate the port */ { self->setSpecialKeyPort( /* keyFlavor */ msg->key, /* keyPort */ MACH_PORT_NULL); } IOFree( (void *)msg, sizeof (struct evioSpecialKeyMsg) ); } /* * This is now being run from the workloop via an IOInterruptEventSource. * Note that we perform a non-blocking send. The Event port in the event * consumer has a queue depth of 1 message. Once the consumer picks up that * message, it runs until the event queue is exhausted before trying to read * another message. If a message is pending,there is no need to enqueue a * second one. This also keeps us from blocking the I/O thread in a msg_send * which could result in a deadlock if the consumer were to make a call into * the event driver. */ void IOHIDSystem::doKickEventConsumer(IOHIDSystem * self) /*IOInterruptEventSource::Action */ { kern_return_t r; mach_msg_header_t *msgh xpr_ev_post("doKickEventConsumer\n", 1,2,3,4,5); self->needToKickEventConsumer = false; // Request received and processed msgh = (mach_msg_header_t *)self->eventMsg; if( msgh) { r = mach_msg_send_from_kernel( msgh, msgh->msgh_size); switch ( r ) { case MACH_SEND_TIMED_OUT:/* Already has a message posted */ case MACH_MSG_SUCCESS: /* Message is posted */ break; default: /* Log the error */ IOLog("%s: doKickEventConsumer msg_send returned %d\n", self->registryName, r); break; } } } // // Schedule the next periodic event to be run, based on the current state of // the event system. We have to consider things here such as when the last // periodic event pass ran, if there is currently any mouse delta accumulated, // and how long it has been since the last event was consumed by an app (for // driving the wait cursor). // // This code should only be run from the periodicEvents method or // _setCursorPosition. // void IOHIDSystem::scheduleNextPeriodicEvent() { if (CMP_ABSOLUTETIME( &waitFrameTime, &thisPeriodicRun) > 0) { AbsoluteTime time_for_next_run; clock_get_uptime(&time_for_next_run); ADD_ABSOLUTETIME( &time_for_next_run, &periodicEventDelta); if (CMP_ABSOLUTETIME( &waitFrameTime, &time_for_next_run) < 0) { timerES->wakeAtTime(waitFrameTime); return; } } timerES->setTimeout(periodicEventDelta); } // Periodic events are driven from this method. // After taking care of all pending work, the method // calls scheduleNextPeriodicEvent to compute and set the // next callout. // Modified this method to call a command Gate action. // This will hopefully insure that this is serialized // with other actions associated with the command gate. void IOHIDSystem::_periodicEvents(IOHIDSystem * self, IOTimerEventSource *timer) /* IOTimerEventSource::Action */ { self->periodicEvents(timer); } void IOHIDSystem::periodicEvents(IOTimerEventSource * /* timer */) { unsigned int tick; // If eventsOpen is false, then the driver shmem is // no longer valid, and it is in the process of shutting down. // We should give up without rescheduling. if ( eventsOpen == false ) return; // Increment event time stamp last clock_get_uptime(&thisPeriodicRun); // Temporary hack til we wean CGS off of VertRetraceClock tick = EV_NS_TO_TICK(&thisPeriodicRun); if ( tick == 0 ) tick = 1; evg->VertRetraceClock = tick; // Update cursor position if needed if ( needSetCursorPosition == true ) _setCursorPosition(&pointerLoc, false); // WAITCURSOR ACTION if ( ev_try_lock(&evg->waitCursorSema) ) { if ( ev_try_lock(&evg->cursorSema) ) { // See if the current context has timed out if ( (evg->AALastEventSent != evg->AALastEventConsumed) && ((evg->VertRetraceClock - evg->AALastEventSent > evg->waitThreshold))) evg->ctxtTimedOut = TRUE; // If wait cursor enabled and context timed out, do waitcursor if (evg->waitCursorEnabled && evg->globalWaitCursorEnabled && evg->ctxtTimedOut) { /* WAIT CURSOR SHOULD BE ON */ if (!evg->waitCursorUp) showWaitCursor(); } else { /* WAIT CURSOR SHOULD BE OFF */ if (evg->waitCursorUp && CMP_ABSOLUTETIME(&waitSusTime, &thisPeriodicRun) <= 0) hideWaitCursor(); } /* Animate cursor */ if (evg->waitCursorUp && CMP_ABSOLUTETIME(&waitFrameTime, &thisPeriodicRun) <= 0) animateWaitCursor(); ev_unlock(&evg->cursorSema); if ((CMP_ABSOLUTETIME(&thisPeriodicRun, &autoDimTime) > 0) && (!autoDimmed)) doAutoDim(); } ev_unlock(&evg->waitCursorSema); } scheduleNextPeriodicEvent(); return; } // // Start the cursor system running. // // At this point, the WindowServer is up, running, and ready to process events. // We will attach the keyboard and mouse, if none are available yet. // bool IOHIDSystem::resetCursor() { volatile Point * p; UInt32 newScreens = 0; SInt32 candidate = 0; SInt32 pinScreen = -1L; p = &evg->cursorLoc; /* Get mask of screens on which the cursor is present */ EvScreen *screen = (EvScreen *)evScreen; for (int i = 0; i < screens; i++ ) { if (!screen[i].instance) continue; if ((screen[i].bounds->maxx - screen[i].bounds->minx) < 128) continue; candidate = i; if ((screen[i].instance) && PtInRect(p, screen[i].bounds)) { pinScreen = i; newScreens |= (1 << i); } } if (newScreens == 0) pinScreen = candidate; if (!cursorPinned) { // reset pin rect cursorPin = *(((EvScreen*)evScreen)[pinScreen].bounds); cursorPin.maxx--; /* Make half-open rectangle */ cursorPin.maxy--; cursorPinScreen = pinScreen; } if (newScreens == 0) { /* Pin new cursor position to cursorPin rect */ p->x = (p->x < cursorPin.minx) ? cursorPin.minx : ((p->x > cursorPin.maxx) ? cursorPin.maxx : p->x); p->y = (p->y < cursorPin.miny) ? cursorPin.miny : ((p->y > cursorPin.maxy) ? cursorPin.maxy : p->y); /* regenerate mask for new position */ for (int i = 0; i < screens; i++ ) { if ((screen[i].instance) && PtInRect(p, screen[i].bounds)) newScreens |= (1 << i); } } cursorScreens = newScreens; pointerDelta.x += (evg->cursorLoc.x - pointerLoc.x); pointerDelta.y += (evg->cursorLoc.y - pointerLoc.y); pointerLoc.x = evg->cursorLoc.x; pointerLoc.y = evg->cursorLoc.y; return( true ); } bool IOHIDSystem::startCursor() { bool ok; if (0 == screens) // no screens, no cursor return( false ); cursorPinned = false; resetCursor(); setBrightness(); showCursor(); // Start the cursor control callouts ok = (kIOReturnSuccess == cmdGate->runAction((IOCommandGate::Action)_periodicEvents, timerES)); cursorStarted = ok; return( ok ); } // // Wait Cursor machinery. These methods should be run from a command // gate action and the shared memory area must be set up. // void IOHIDSystem::showWaitCursor() { xpr_ev_cursor("showWaitCursor\n",1,2,3,4,5); evg->waitCursorUp = true; hideCursor(); evg->frame = EV_WAITCURSOR; showCursor(); // Set animation and sustain absolute times. waitSusTime = waitFrameTime = thisPeriodicRun; ADD_ABSOLUTETIME( &waitFrameTime, &waitFrameRate); ADD_ABSOLUTETIME( &waitSusTime, &waitSustain); } void IOHIDSystem::hideWaitCursor() { xpr_ev_cursor("hideWaitCursor\n",1,2,3,4,5); evg->waitCursorUp = false; hideCursor(); evg->frame = EV_STD_CURSOR; showCursor(); AbsoluteTime_to_scalar(&waitFrameTime) = 0; AbsoluteTime_to_scalar(&waitSusTime ) = 0; } void IOHIDSystem::animateWaitCursor() { xpr_ev_cursor("animateWaitCursor\n",1,2,3,4,5); changeCursor(evg->frame + 1); // Set the next animation time. waitFrameTime = thisPeriodicRun; ADD_ABSOLUTETIME( &waitFrameTime, &waitFrameRate); } void IOHIDSystem::changeCursor(int frame) { evg->frame = ((frame > maxWaitCursorFrame) || (frame > evg->lastFrame)) ? firstWaitCursorFrame : frame; xpr_ev_cursor("changeCursor %d\n",evg->frame,2,3,4,5); moveCursor(); } // // Return the screen number in which point p lies. Return -1 if the point // lies outside of all registered screens. // int IOHIDSystem::pointToScreen(Point * p) { int i; EvScreen *screen = (EvScreen *)evScreen; for (i=screens; --i != -1; ) { if (screen[i].instance != 0 && (p->x >= screen[i].bounds->minx) && (p->x < screen[i].bounds->maxx) && (p->y >= screen[i].bounds->miny) && (p->y < screen[i].bounds->maxy)) return i; } return(-1); /* Cursor outside of known screen boundary */ } // // API used to manipulate screen brightness // // These should be run from a command gate action. // // Set the current brightness void IOHIDSystem::setBrightness(int b) { if ( b < EV_SCREEN_MIN_BRIGHTNESS ) b = EV_SCREEN_MIN_BRIGHTNESS; else if ( b > EV_SCREEN_MAX_BRIGHTNESS ) b = EV_SCREEN_MAX_BRIGHTNESS; if ( b != curBright ) { curBright = b; if ( autoDimmed == false ) setBrightness(); } } int IOHIDSystem::brightness() { return curBright; } // Set the current brightness void IOHIDSystem::setAutoDimBrightness(int b) { if ( b < EV_SCREEN_MIN_BRIGHTNESS ) b = EV_SCREEN_MIN_BRIGHTNESS; else if ( b > EV_SCREEN_MAX_BRIGHTNESS ) b = EV_SCREEN_MAX_BRIGHTNESS; if ( b != dimmedBrightness ) { dimmedBrightness = b; if ( autoDimmed == true ) setBrightness(); } } int IOHIDSystem::autoDimBrightness() { return dimmedBrightness; } int IOHIDSystem::currentBrightness() // Return the current brightness { if ( autoDimmed == true && dimmedBrightness < curBright ) return dimmedBrightness; else return curBright; } void IOHIDSystem::doAutoDim() { autoDimmed = true; setBrightness(); } // Return display brightness to normal void IOHIDSystem::undoAutoDim() { autoDimmed = false; setBrightness(); } void IOHIDSystem::forceAutoDimState(bool dim) { if ( dim == true ) { if ( autoDimmed == false ) { if ( eventsOpen == true ) clock_get_uptime( &autoDimTime); doAutoDim(); } } else { if ( autoDimmed == true ) { if ( eventsOpen == true ) { clock_get_uptime( &autoDimTime); ADD_ABSOLUTETIME( &autoDimTime, &autoDimPeriod); } undoAutoDim(); } } } // // API used to manipulate sound volume/attenuation // // Set the current brightness. void IOHIDSystem::setAudioVolume(int v) { if ( v < EV_AUDIO_MIN_VOLUME ) v = EV_AUDIO_MIN_VOLUME; else if ( v > EV_AUDIO_MAX_VOLUME ) v = EV_AUDIO_MAX_VOLUME; curVolume = v; } // // Volume set programatically, rather than from keyboard // void IOHIDSystem::setUserAudioVolume(int v) { setAudioVolume(v); // Let sound driver know about the change evSpecialKeyMsg( NX_KEYTYPE_SOUND_UP, /* direction */ NX_KEYDOWN, /* flags */ 0, /* level */ curVolume); } int IOHIDSystem::audioVolume() { return curVolume; } // // API used to drive event state out to attached screens // // These should be run from a command gate action. // inline void IOHIDSystem::setBrightness() // Propagate state out to screens { evDispatch(/* command */ EVLEVEL); } inline void IOHIDSystem::showCursor() { evDispatch(/* command */ EVSHOW); } inline void IOHIDSystem::hideCursor() { evDispatch(/* command */ EVHIDE); } inline void IOHIDSystem::moveCursor() { evDispatch(/* command */ EVMOVE); } // // - attachDefaultEventSources // Attach the default event sources. // void IOHIDSystem::attachDefaultEventSources() { OSObject * source; OSIterator * sources; sources = getProviderIterator(); if (!sources) return; while( (source = sources->getNextObject())) { if (OSDynamicCast(IOHIDevice, source)) { registerEventSource((IOHIDevice *)source); } } sources->release(); } // // - detachEventSources // Detach all event sources // void IOHIDSystem::detachEventSources() { OSIterator * iter; IOHIDevice * srcInstance; iter = getOpenProviderIterator(); if( iter) { while( (srcInstance = (IOHIDevice *) iter->getNextObject())) { #ifdef DEBUG kprintf("detachEventSource:%s\n", srcInstance->getName()); #endif srcInstance->close(this); } iter->release(); } } // // EventSrcClient implementation // // // A new device instance desires to be added to our list. // Try to get ownership of the device. If we get it, add it to // the list. // bool IOHIDSystem::registerEventSource(IOHIDevice * source) { bool success = false; #ifdef DEBUG kprintf("registerEventSource:%s\n", ((IOHIDevice*)source)->getName()); #endif if ( OSDynamicCast(IOHIKeyboard, source) ) { success = ((IOHIKeyboard*)source)->open(this, kIOServiceSeize,0, (KeyboardEventCallback) _keyboardEvent, (KeyboardSpecialEventCallback) _keyboardSpecialEvent, (UpdateEventFlagsCallback) _updateEventFlags); } else if ( OSDynamicCast(IOHIPointing, source) ) { AppendNewCachedMouseEventForService(cachedButtonStates, source); success = ((IOHIPointing*)source)->open(this, kIOServiceSeize,0, (RelativePointerEventCallback) _relativePointerEvent, (AbsolutePointerEventCallback) _absolutePointerEvent, (ScrollWheelEventCallback) _scrollWheelEvent); } if ( success ) // update with user settings source->setParamProperties( savedParameters ); else IOLog("%s: Seize of %s failed.\n", registryName, source->getName()); return success; } IOReturn IOHIDSystem::message(UInt32 type, IOService * provider, void * argument) { IOReturn status = kIOReturnSuccess; switch (type) { case kIOMessageServiceIsTerminated: #ifdef DEBUG kprintf("detachEventSource:%s\n", provider->getName()); #endif provider->close( this ); case kIOMessageServiceWasClosed: break; default: status = super::message(type, provider, argument); break; } return status; } // // This will scale the point at location in the coordinate system represented by bounds // to the coordinate system of the current screen. // This is needed for absolute pointer events that come from devices with different bounds. // void IOHIDSystem::_scaleLocationToCurrentScreen(Point *location, Bounds *bounds) { // We probably also need to look at current screen offsets as well // but that shouldn't matter until we provide tablets with a way to // switch screens... location->x = ((location->x - bounds->minx) * (cursorPin.maxx - cursorPin.minx + 1) / (bounds->maxx - bounds->minx)) + cursorPin.minx; location->y = ((location->y - bounds->miny) * (cursorPin.maxy - cursorPin.miny + 1) / (bounds->maxy - bounds->miny)) + cursorPin.miny; return; } // // Process a mouse status change. The driver should sign extend // it's deltas and perform any bit flipping needed there. // // We take the state as presented and turn it into events. // void IOHIDSystem::_relativePointerEvent(IOHIDSystem * self, int buttons, /* deltaX */ int dx, /* deltaY */ int dy, /* atTime */ AbsoluteTime ts, OSObject * sender, void * refcon) { self->relativePointerEvent(buttons, dx, dy, ts, sender); } void IOHIDSystem::relativePointerEvent(int buttons, /* deltaX */ int dx, /* deltaY */ int dy, /* atTime */ AbsoluteTime ts) { relativePointerEvent(buttons, dx, dy, ts, 0); } void IOHIDSystem::relativePointerEvent(int buttons, /* deltaX */ int dx, /* deltaY */ int dy, /* atTime */ AbsoluteTime ts, /* sender */ OSObject * sender) { IOHIDCmdGateActionArgs args; args.arg0 = &buttons; args.arg1 = &dx; args.arg2 = &dy; args.arg3 = &ts; args.arg4 = sender; cmdGate->runAction((IOCommandGate::Action)doRelativePointerEvent, &args); } IOReturn IOHIDSystem::doRelativePointerEvent(IOHIDSystem *self, void * args) /* IOCommandGate::Action */ { int buttons = *(int *)((IOHIDCmdGateActionArgs *)args)->arg0; int dx = *(int *)((IOHIDCmdGateActionArgs *)args)->arg1; int dy = *(int *)((IOHIDCmdGateActionArgs *)args)->arg2; AbsoluteTime ts = *(AbsoluteTime *)((IOHIDCmdGateActionArgs *)args)->arg3; OSObject * sender = (OSObject *)((IOHIDCmdGateActionArgs *)args)->arg4; self->relativePointerEventGated(buttons, dx, dy, ts, sender); return kIOReturnSuccess; } void IOHIDSystem::relativePointerEventGated(int buttons, int dx, int dy, AbsoluteTime ts, OSObject * sender) { AbsoluteTime nextVBL, vblDeltaTime, vblDeltaAdj, eventDeltaTime, moveDeltaTime; #if __i386__ static AbsoluteTime postedVBLTime; #endif bool haveVBL; if(SHOULD_CONSUME_EVENT(ts)) return; CachedMouseEventStruct *cachedMouseEvent; if (cachedMouseEvent = GetCachedMouseEventForService(cachedButtonStates, sender)) { do { // RY: If the display is dim and there is no change in buttonStates, // compare relative mouse movements against thresholds to determine // whether or not to wake the display. This will prevent premature // display wake upon chatty mice. if ((cachedMouseEvent->lastButtons == buttons) && (displayState == 0)) { if (CMP_ABSOLUTETIME(&ts, &(cachedMouseEvent->eventDeadline)) > 0) { cachedMouseEvent->eventDeadline = ts; ADD_ABSOLUTETIME(&(cachedMouseEvent->eventDeadline), &gIOHIDRelativeTickleThresholdAbsoluteTime); cachedMouseEvent->accumX = 0; cachedMouseEvent->accumY = 0; } cachedMouseEvent->accumX += dx; cachedMouseEvent->accumY += dy; if ((abs(cachedMouseEvent->accumX) < kIOHIDRelativeTickleThresholdPixel) && (abs(cachedMouseEvent->accumY) < kIOHIDRelativeTickleThresholdPixel)) { break; } } TICKLE_DISPLAY; cachedMouseEvent->lastButtons = buttons; cachedMouseEvent->eventDeadline = ts; } while (false); } if( eventsOpen == false ) return; // Fake up pressure changes from button state changes if( (buttons & EV_LB) != (evg->buttons & EV_LB) ) { if ( buttons & EV_LB ) lastPressure = MAXPRESSURE; else lastPressure = MINPRESSURE; } _setButtonState(buttons, /* atTime */ ts, sender); // figure cursor movement if( dx || dy ) { eventDeltaTime = ts; SUB_ABSOLUTETIME( &eventDeltaTime, &lastRelativeEventTime ); lastRelativeEventTime = ts; IOGraphicsDevice * instance = ((EvScreen*)evScreen)[cursorPinScreen].instance; if( instance) { instance->getVBLTime( &nextVBL, &vblDeltaTime ); } else nextVBL.hi = nextVBL.lo = vblDeltaTime.hi = vblDeltaTime.lo = 0; if( dx && ((dx ^ accumDX) < 0)) accumDX = 0; if( dy && ((dy ^ accumDY) < 0)) accumDY = 0; KERNEL_DEBUG(0x0c000060 | DBG_FUNC_NONE, nextVBL.hi, nextVBL.lo, lastRelativeEventTime.hi, lastRelativeEventTime.lo, 0); haveVBL = (nextVBL.lo || nextVBL.hi); #if __i386__ if (haveVBL && (nextVBL.lo == postedVBLTime.lo) && (nextVBL.hi == postedVBLTime.hi)) { #else if (haveVBL && (postDeltaX || postDeltaY)) { #endif accumDX += dx; accumDY += dy; } else { SInt32 num = 0, div = 0; dx += accumDX; dy += accumDY; moveDeltaTime = ts; SUB_ABSOLUTETIME( &moveDeltaTime, &lastRelativeMoveTime ); lastRelativeMoveTime = ts; if( (eventDeltaTime.lo < vblDeltaTime.lo) && (0 == eventDeltaTime.hi) && vblDeltaTime.lo && moveDeltaTime.lo) { num = vblDeltaTime.lo; div = moveDeltaTime.lo; dx = (num * dx) / div; dy = (num * dy) / div; } KERNEL_DEBUG(0x0c000000 | DBG_FUNC_NONE, dx, dy, num, div, 0); accumDX = accumDY = 0; #if __i386__ postedVBLTime = nextVBL; // we have posted for this vbl #endif if( dx || dy ) { #if !__i386__ if( haveVBL ) { postDeltaX = dx; postDeltaY = dy; vblDeltaTime.lo += (vblDeltaTime.lo >> 4); ADD_ABSOLUTETIME(&nextVBL, &vblDeltaTime); vblES->wakeAtTime(nextVBL); } else #endif { pointerLoc.x += dx; pointerLoc.y += dy; pointerDelta.x += dx; pointerDelta.y += dy; _setCursorPosition(&pointerLoc, false); } } } } } void IOHIDSystem::vblEvent(void) { if (postDeltaX || postDeltaY) { pointerLoc.x += postDeltaX; pointerLoc.y += postDeltaY; pointerDelta.x += postDeltaX; pointerDelta.y += postDeltaY; _setCursorPosition(&pointerLoc, false); postDeltaX = postDeltaY = 0; } } void IOHIDSystem::_vblEvent(OSObject *self, IOTimerEventSource *sender) { ((IOHIDSystem *)self)->vblEvent(); } void IOHIDSystem::_absolutePointerEvent(IOHIDSystem * self, int buttons, /* at */ Point * newLoc, /* withBounds */ Bounds * bounds, /* inProximity */ bool proximity, /* withPressure */ int pressure, /* withAngle */ int stylusAngle, /* atTime */ AbsoluteTime ts, OSObject * sender, void * refcon) { self->absolutePointerEvent(buttons, newLoc, bounds, proximity, pressure, stylusAngle, ts, sender); } // capabilities masks // Use these masks with the capabilities field of a proximity // event to determine what fields in a Tablet Event are valid // for this device. #define kTransducerDeviceIdBitMask 0x0001 #define kTransducerAbsXBitMask 0x0002 #define kTransducerAbsYBitMask 0x0004 #define kTransducerVendor1BitMask 0x0008 #define kTransducerVendor2BitMask 0x0010 #define kTransducerVendor3BitMask 0x0020 #define kTransducerButtonsBitMask 0x0040 #define kTransducerTiltXBitMask 0x0080 #define kTransducerTiltYBitMask 0x0100 #define kTransducerAbsZBitMask 0x0200 #define kTransducerPressureBitMask 0x0400 #define kTransducerTangentialPressureBitMask 0x0800 #define kTransducerOrientInfoBitMask 0x1000 #define kTransducerRotationBitMask 0x2000 void IOHIDSystem::absolutePointerEvent(int buttons, /* at */ Point * newLoc, /* withBounds */ Bounds * bounds, /* inProximity */ bool proximity, /* withPressure */ int pressure, /* withAngle */ int stylusAngle, /* atTime */ AbsoluteTime ts) { absolutePointerEvent(buttons, newLoc, bounds, proximity, pressure, stylusAngle, ts, 0); } void IOHIDSystem::absolutePointerEvent(int buttons, /* at */ Point * newLoc, /* withBounds */ Bounds * bounds, /* inProximity */ bool proximity, /* withPressure */ int pressure, /* withAngle */ int stylusAngle, /* atTime */ AbsoluteTime ts, /* sender */ OSObject * sender) { IOHIDCmdGateActionArgs args; args.arg0 = &buttons; args.arg1 = (void *)newLoc; args.arg2 = (void *)bounds; args.arg3 = &proximity; args.arg4 = &pressure; args.arg5 = &stylusAngle; args.arg6 = &ts; args.arg7 = sender; cmdGate->runAction((IOCommandGate::Action)doAbsolutePointerEvent, &args); } IOReturn IOHIDSystem::doAbsolutePointerEvent(IOHIDSystem *self, void * args) /* IOCommandGate::Action */ { int buttons = *(int *) ((IOHIDCmdGateActionArgs *)args)->arg0; Point * newLoc = (Point *) ((IOHIDCmdGateActionArgs *)args)->arg1; Bounds * bounds = (Bounds *) ((IOHIDCmdGateActionArgs *)args)->arg2; bool proximity = *(bool *) ((IOHIDCmdGateActionArgs *)args)->arg3; int pressure = *(int *) ((IOHIDCmdGateActionArgs *)args)->arg4; int stylusAngle = *(int *) ((IOHIDCmdGateActionArgs *)args)->arg5; AbsoluteTime ts = *(AbsoluteTime *) ((IOHIDCmdGateActionArgs *)args)->arg6; OSObject * sender = (OSObject *)((IOHIDCmdGateActionArgs *)args)->arg7; self->absolutePointerEventGated(buttons, newLoc, bounds, proximity, pressure, stylusAngle, ts, sender); return kIOReturnSuccess; } void IOHIDSystem::absolutePointerEventGated(int buttons, /* at */ Point * newLoc, /* withBounds */ Bounds * bounds, /* inProximity */ bool proximity, /* withPressure */ int pressure, /* withAngle */ int stylusAngle, /* atTime */ AbsoluteTime ts, /* sender */ OSObject * sender) { /* * If you don't know what to pass for the following fields, pass the * default values below: * pressure = MINPRESSURE or MAXPRESSURE * stylusAngle = 90 */ NXEventData outData; /* dummy data */ bool proximityChange = (lastProximity != proximity); NXTabletProximityData proximityData; NXTabletPointData tabletData; CachedMouseEventStruct *cachedMouseEvent = 0; if(SHOULD_CONSUME_EVENT(ts)) return; TICKLE_DISPLAY; if ( eventsOpen == false ) return; lastPressure = (pressure > EV_MAXPRESSURE) ? EV_MAXPRESSURE : pressure; _scaleLocationToCurrentScreen(newLoc, bounds); // RY: Attempt to add basic tablet support to absolute pointing devices // Basically, we will fill in the tablet support portions of both the // mouse and mouseMove of NXEventData. Pending tablet events are stored // in the CachedMouseEventStruct and then later picked off in // _setButtonState and _postMouseMoveEvent if (cachedMouseEvent = GetCachedMouseEventForService(cachedButtonStates, sender)) { if (proximityChange) { bzero(&proximityData, sizeof(NXTabletProximityData)); proximityData.enterProximity = proximity; proximityData.capabilityMask = ( kTransducerAbsXBitMask | kTransducerAbsYBitMask | kTransducerButtonsBitMask | kTransducerPressureBitMask); cachedMouseEvent->tabletProximityDataPtr = &proximityData; } else { bzero(&tabletData, sizeof(NXTabletPointData)); tabletData.x = newLoc->x; tabletData.y = newLoc->y; tabletData.buttons = buttons; tabletData.pressure = pressure; cachedMouseEvent->tabletPointDataPtr = &tabletData; } } if ( newLoc->x != pointerLoc.x || newLoc->y != pointerLoc.y || proximityChange) { pointerDelta.x += (newLoc->x - pointerLoc.x); pointerDelta.y += (newLoc->y - pointerLoc.y); pointerLoc = *newLoc; _setCursorPosition(&pointerLoc, false, sender); } if ( proximityChange && proximity == true ) { evg->eventFlags |= NX_STYLUSPROXIMITYMASK; bzero( (char *)&outData, sizeof outData ); postEvent( NX_FLAGSCHANGED, /* at */ (Point *)&pointerLoc, /* atTime */ ts, /* withData */ &outData); } if ( proximity == true ) _setButtonState(buttons, /* atTime */ ts, sender); if ( proximityChange && proximity == false ) { evg->eventFlags &= ~NX_STYLUSPROXIMITYMASK; bzero( (char *)&outData, sizeof outData ); postEvent( NX_FLAGSCHANGED, /* at */ (Point *)&pointerLoc, /* atTime */ ts, /* withData */ &outData); } lastProximity = proximity; // RY: Clean it off if (cachedMouseEvent) { cachedMouseEvent->tabletPointDataPtr = 0; cachedMouseEvent->tabletProximityDataPtr = 0; } } void IOHIDSystem::_scrollWheelEvent(IOHIDSystem * self, short deltaAxis1, short deltaAxis2, short deltaAxis3, /* atTime */ AbsoluteTime ts, OSObject * sender, void * refcon) { self->scrollWheelEvent(deltaAxis1, deltaAxis2, deltaAxis3, ts, sender); } void IOHIDSystem::scrollWheelEvent(short deltaAxis1, short deltaAxis2, short deltaAxis3, /* atTime */ AbsoluteTime ts) { scrollWheelEvent(deltaAxis1, deltaAxis2, deltaAxis3, ts, 0); } void IOHIDSystem::scrollWheelEvent(short deltaAxis1, short deltaAxis2, short deltaAxis3, /* atTime */ AbsoluteTime ts, OSObject * sender) { IOHIDCmdGateActionArgs args; args.arg0 = &deltaAxis1; args.arg1 = &deltaAxis2; args.arg2 = &deltaAxis3; args.arg3 = &ts; args.arg4 = sender; cmdGate->runAction((IOCommandGate::Action)doScrollWheelEvent, (void *)&args); } IOReturn IOHIDSystem::doScrollWheelEvent(IOHIDSystem *self, void * args) /* IOCommandGate::Action */ { short deltaAxis1 = *(short *)((IOHIDCmdGateActionArgs *)args)->arg0; short deltaAxis2 = *(short *)((IOHIDCmdGateActionArgs *)args)->arg1; short deltaAxis3 = *(short *)((IOHIDCmdGateActionArgs *)args)->arg2; AbsoluteTime ts = *(AbsoluteTime *)((IOHIDCmdGateActionArgs *)args)->arg3; OSObject * sender= (OSObject *)((IOHIDCmdGateActionArgs *)args)->arg4; self->scrollWheelEventGated(deltaAxis1, deltaAxis2, deltaAxis3, ts, sender); return kIOReturnSuccess; } void IOHIDSystem::scrollWheelEventGated(short deltaAxis1, short deltaAxis2, short deltaAxis3, /* atTime */ AbsoluteTime ts, OSObject * sender) { NXEventData wheelData; if(SHOULD_CONSUME_EVENT(ts)) return; TICKLE_DISPLAY; if ((deltaAxis1 == 0) && (deltaAxis2 == 0) && (deltaAxis3 == 0)) { return; } if (!eventsOpen) return; bzero((char *)&wheelData, sizeof wheelData); wheelData.scrollWheel.deltaAxis1 = deltaAxis1; wheelData.scrollWheel.deltaAxis2 = deltaAxis2; wheelData.scrollWheel.deltaAxis3 = deltaAxis3; postEvent( NX_SCROLLWHEELMOVED, /* at */ (Point *)&evg->cursorLoc, /* atTime */ ts, /* withData */ &wheelData); return; } void IOHIDSystem::_tabletEvent(IOHIDSystem *self, NXEventData *tabletData, AbsoluteTime ts, OSObject * sender, void * refcon) { self->tabletEvent(tabletData, ts, sender); } void IOHIDSystem::tabletEvent(NXEventData *tabletData, AbsoluteTime ts) { tabletEvent(tabletData, ts, 0); } void IOHIDSystem::tabletEvent(NXEventData *tabletData, AbsoluteTime ts, OSObject * sender) { cmdGate->runAction((IOCommandGate::Action)doTabletEvent, tabletData, &ts, sender); } IOReturn IOHIDSystem::doTabletEvent(IOHIDSystem *self, void * arg0, void * arg1, void * arg2) /* IOCommandGate::Action */ { NXEventData *tabletData = (NXEventData *) arg0; AbsoluteTime ts = *(AbsoluteTime *) arg1; OSObject * sender = (OSObject *) sender; self->tabletEventGated(tabletData, ts, sender); return kIOReturnSuccess; } void IOHIDSystem::tabletEventGated(NXEventData *tabletData, AbsoluteTime ts, OSObject * sender) { if(SHOULD_CONSUME_EVENT(ts)) return; #if 0 // FIXME - NX_TABLETPOINTER has been deprecated. if (eventsOpen) { postEvent(NX_TABLETPOINTER, (Point *)&evg->cursorLoc, ts, tabletData); } #endif /* 0 */ return; } void IOHIDSystem::_proximityEvent(IOHIDSystem *self, NXEventData *proximityData, AbsoluteTime ts, OSObject * sender, void * refcon) { self->proximityEvent(proximityData, ts, sender); } void IOHIDSystem::proximityEvent(NXEventData *proximityData, AbsoluteTime ts) { proximityEvent(proximityData, ts, 0); } void IOHIDSystem::proximityEvent(NXEventData *proximityData, AbsoluteTime ts, OSObject * sender) { cmdGate->runAction((IOCommandGate::Action)doProximityEvent, proximityData, &ts, sender); } IOReturn IOHIDSystem::doProximityEvent(IOHIDSystem *self, void * arg0, void *arg1, void * arg2) /* IOCommandGate::Action */ { NXEventData *proximityData = (NXEventData *)arg0; AbsoluteTime ts = *(AbsoluteTime *)arg1; OSObject * sender = (OSObject *)sender; self->proximityEventGated(proximityData, ts, sender); return kIOReturnSuccess; } void IOHIDSystem::proximityEventGated(NXEventData *proximityData, AbsoluteTime ts, OSObject * sender) { if(SHOULD_CONSUME_EVENT(ts)) return; #if 0 // FIXME - NX_TABLETPROXIMITY has been deprecated. if (eventsOpen) { postEvent(NX_TABLETPROXIMITY, (Point *)&evg->cursorLoc, ts, proximityData); } #endif /* 0 */ return; } // // Process a keyboard state change. // void IOHIDSystem::_keyboardEvent(IOHIDSystem * self, unsigned eventType, /* flags */ unsigned flags, /* keyCode */ unsigned key, /* charCode */ unsigned charCode, /* charSet */ unsigned charSet, /* originalCharCode */ unsigned origCharCode, /* originalCharSet */ unsigned origCharSet, /* keyboardType */ unsigned keyboardType, /* repeat */ bool repeat, /* atTime */ AbsoluteTime ts, OSObject * sender, void * refcon) { self->keyboardEvent(eventType, flags, key, charCode, charSet, origCharCode, origCharSet, keyboardType, repeat, ts, sender); } void IOHIDSystem::keyboardEvent(unsigned eventType, /* flags */ unsigned flags, /* keyCode */ unsigned key, /* charCode */ unsigned charCode, /* charSet */ unsigned charSet, /* originalCharCode */ unsigned origCharCode, /* originalCharSet */ unsigned origCharSet, /* keyboardType */ unsigned keyboardType, /* repeat */ bool repeat, /* atTime */ AbsoluteTime ts) { keyboardEvent(eventType, flags, key, charCode, charSet, origCharCode, origCharSet, keyboardType, repeat, ts, 0); } void IOHIDSystem::keyboardEvent(unsigned eventType, /* flags */ unsigned flags, /* keyCode */ unsigned key, /* charCode */ unsigned charCode, /* charSet */ unsigned charSet, /* originalCharCode */ unsigned origCharCode, /* originalCharSet */ unsigned origCharSet, /* keyboardType */ unsigned keyboardType, /* repeat */ bool repeat, /* atTime */ AbsoluteTime ts, /* sender */ OSObject * sender) { IOHIDCmdGateActionArgs args; args.arg0 = &eventType; args.arg1 = &flags; args.arg2 = &key; args.arg3 = &charCode; args.arg4 = &charSet; args.arg5 = &origCharCode; args.arg6 = &origCharSet; args.arg7 = &keyboardType; args.arg8 = &repeat; args.arg9 = &ts; args.arg10 = sender; cmdGate->runAction((IOCommandGate::Action)doKeyboardEvent, &args); } IOReturn IOHIDSystem::doKeyboardEvent(IOHIDSystem *self, void * args) /* IOCommandGate::Action */ { unsigned eventType = *(unsigned *) ((IOHIDCmdGateActionArgs *)args)->arg0; unsigned flags = *(unsigned *) ((IOHIDCmdGateActionArgs *)args)->arg1; unsigned key = *(unsigned *) ((IOHIDCmdGateActionArgs *)args)->arg2; unsigned charCode = *(unsigned *) ((IOHIDCmdGateActionArgs *)args)->arg3; unsigned charSet = *(unsigned *) ((IOHIDCmdGateActionArgs *)args)->arg4; unsigned origCharCode = *(unsigned *) ((IOHIDCmdGateActionArgs *)args)->arg5; unsigned origCharSet = *(unsigned *) ((IOHIDCmdGateActionArgs *)args)->arg6; unsigned keyboardType = *(unsigned *) ((IOHIDCmdGateActionArgs *)args)->arg7; bool repeat = *(bool *) ((IOHIDCmdGateActionArgs *)args)->arg8; AbsoluteTime ts = *(AbsoluteTime *) ((IOHIDCmdGateActionArgs *)args)->arg9; OSObject * sender = (OSObject *) ((IOHIDCmdGateActionArgs *)args)->arg10; self->keyboardEventGated(eventType, flags, key, charCode, charSet, origCharCode, origCharSet, keyboardType, repeat, ts, sender); return kIOReturnSuccess; } void IOHIDSystem::keyboardEventGated(unsigned eventType, /* flags */ unsigned flags, /* keyCode */ unsigned key, /* charCode */ unsigned charCode, /* charSet */ unsigned charSet, /* originalCharCode */ unsigned origCharCode, /* originalCharSet */ unsigned origCharSet, /* keyboardType */ unsigned keyboardType, /* repeat */ bool repeat, /* atTime */ AbsoluteTime ts, /* sender */ OSObject * sender) { NXEventData outData; if(SHOULD_CONSUME_EVENT(ts)) return; if ( ! (displayState & IOPMDeviceUsable) ) { // display is off, consume the keystroke if ( eventType == NX_KEYDOWN ) { return; } TICKLE_DISPLAY; return; } TICKLE_DISPLAY; outData.key.repeat = repeat; outData.key.keyCode = key; outData.key.charSet = charSet; outData.key.charCode = charCode; outData.key.origCharSet = origCharSet; outData.key.origCharCode = origCharCode; outData.key.keyboardType = keyboardType; if ( eventsOpen == false ) return; evg->eventFlags = (evg->eventFlags & ~KEYBOARD_FLAGSMASK) | (flags & KEYBOARD_FLAGSMASK); if (cachedEventFlags != evg->eventFlags) { cachedEventFlags = evg->eventFlags; // RY: Reset the clickTime as well on modifier // change to prevent double click from occuring nanoseconds_to_absolutetime(0, &clickTime); } postEvent( eventType, /* at */ (Point *)&pointerLoc, /* atTime */ ts, /* withData */ &outData); } void IOHIDSystem::_keyboardSpecialEvent( IOHIDSystem * self, unsigned eventType, /* flags */ unsigned flags, /* keyCode */ unsigned key, /* specialty */ unsigned flavor, /* guid */ UInt64 guid, /* repeat */ bool repeat, /* atTime */ AbsoluteTime ts, OSObject * sender, void * refcon) { self->keyboardSpecialEvent(eventType, flags, key, flavor, guid, repeat, ts, sender); } void IOHIDSystem::keyboardSpecialEvent( unsigned eventType, /* flags */ unsigned flags, /* keyCode */ unsigned key, /* specialty */ unsigned flavor, /* guid */ UInt64 guid, /* repeat */ bool repeat, /* atTime */ AbsoluteTime ts) { keyboardSpecialEvent(eventType, flags, key, flavor, guid, repeat, ts, 0); } void IOHIDSystem::keyboardSpecialEvent( unsigned eventType, /* flags */ unsigned flags, /* keyCode */ unsigned key, /* specialty */ unsigned flavor, /* guid */ UInt64 guid, /* repeat */ bool repeat, /* atTime */ AbsoluteTime ts, /* sender */ OSObject * sender) { IOHIDCmdGateActionArgs args; args.arg0 = &eventType; args.arg1 = &flags; args.arg2 = &key; args.arg3 = &flavor; args.arg4 = &guid; args.arg5 = &repeat; args.arg6 = &ts; args.arg7 = sender; cmdGate->runAction((IOCommandGate::Action)doKeyboardSpecialEvent, &args); } IOReturn IOHIDSystem::doKeyboardSpecialEvent(IOHIDSystem *self, void * args) /* IOCommandGate::Action */ { unsigned eventType= *(unsigned *) ((IOHIDCmdGateActionArgs *)args)->arg0; unsigned flags = *(unsigned *) ((IOHIDCmdGateActionArgs *)args)->arg1; unsigned key = *(unsigned *) ((IOHIDCmdGateActionArgs *)args)->arg2; unsigned flavor = *(unsigned *) ((IOHIDCmdGateActionArgs *)args)->arg3; UInt64 guid = *(UInt64 *) ((IOHIDCmdGateActionArgs *)args)->arg4; bool repeat = *(bool *) ((IOHIDCmdGateActionArgs *)args)->arg5; AbsoluteTime ts = *(AbsoluteTime *)((IOHIDCmdGateActionArgs *)args)->arg6; OSObject * sender = (OSObject *) ((IOHIDCmdGateActionArgs *)args)->arg7; self->keyboardSpecialEventGated(eventType, flags, key, flavor, guid, repeat, ts, sender); return kIOReturnSuccess; } void IOHIDSystem::keyboardSpecialEventGated( /* event */ unsigned eventType, /* flags */ unsigned flags, /* keyCode */ unsigned key, /* specialty */ unsigned flavor, /* guid */ UInt64 guid, /* repeat */ bool repeat, /* atTime */ AbsoluteTime ts, /* sender */ OSObject * sender) { NXEventData outData; int level = -1; if(SHOULD_CONSUME_EVENT(ts)) return; TICKLE_DISPLAY; // clear event record bzero( (void *)&outData, sizeof outData ); // if no one is listening, then dont bother to do anything if ( eventsOpen == false ) return; // Update flags. evg->eventFlags = (evg->eventFlags & ~KEYBOARD_FLAGSMASK) | (flags & KEYBOARD_FLAGSMASK); // was this a keydown event if ( eventType == NX_KEYDOWN ) { // which special key went down switch ( flavor ) { case NX_KEYTYPE_EJECT: // Special key handlers: // // Command = invoke macsbug // Command+option = sleep now // Command+option+control = shutdown now // Control = logout dialog if( (evg->eventFlags & NX_COMMANDMASK) && !(evg->eventFlags & NX_CONTROLMASK) && !(evg->eventFlags & NX_SHIFTMASK) && !(evg->eventFlags & NX_ALTERNATEMASK) ) { // Post a power key event, Classic should pick this up and // drop into MacsBug. // outData.compound.subType = NX_SUBTYPE_POWER_KEY; postEvent( NX_SYSDEFINED, /* at */ (Point *)&pointerLoc, /* atTime */ ts, /* withData */ &outData); } else if( (evg->eventFlags & NX_COMMANDMASK) && !(evg->eventFlags & NX_CONTROLMASK) && !(evg->eventFlags & NX_SHIFTMASK) && (evg->eventFlags & NX_ALTERNATEMASK) ) { //IOLog( "IOHIDSystem -- sleep now!\n" ); // Post the sleep now event. Someone else will handle the actual call. // outData.compound.subType = NX_SUBTYPE_SLEEP_EVENT; postEvent( NX_SYSDEFINED, /* at */ (Point *)&pointerLoc, /* atTime */ ts, /* withData */ &outData); } else if( (evg->eventFlags & NX_COMMANDMASK) && (evg->eventFlags & NX_CONTROLMASK) && !(evg->eventFlags & NX_SHIFTMASK) && (evg->eventFlags & NX_ALTERNATEMASK) ) { //IOLog( "IOHIDSystem -- shutdown now!\n" ); // Post the shutdown now event. Someone else will handle the actual call. // outData.compound.subType = NX_SUBTYPE_SHUTDOWN_EVENT; postEvent( NX_SYSDEFINED, /* at */ (Point *)&pointerLoc, /* atTime */ ts, /* withData */ &outData); } else if( (evg->eventFlags & NX_COMMANDMASK) && (evg->eventFlags & NX_CONTROLMASK) && !(evg->eventFlags & NX_SHIFTMASK) && !(evg->eventFlags & NX_ALTERNATEMASK) ) { // Restart now! //IOLog( "IOHIDSystem -- Restart now!\n" ); // Post the Restart now event. Someone else will handle the actual call. // outData.compound.subType = NX_SUBTYPE_RESTART_EVENT; postEvent( NX_SYSDEFINED, /* at */ (Point *)&pointerLoc, /* atTime */ ts, /* withData */ &outData); } else if( !(evg->eventFlags & NX_COMMANDMASK) && (evg->eventFlags & NX_CONTROLMASK) && !(evg->eventFlags & NX_SHIFTMASK) && !(evg->eventFlags & NX_ALTERNATEMASK) ) { // Looks like we should put up the normal 'Power Key' dialog. // // Set the event flags to zero, because the system will not do the right // thing if we don't zero this out (it will ignore the power key event // we post, thinking that some modifiers are down). // evg->eventFlags = 0; // Post the power keydown event. // outData.compound.subType = NX_SUBTYPE_POWER_KEY; postEvent( NX_SYSDEFINED, /* at */ (Point *)&pointerLoc, /* atTime */ ts, /* withData */ &outData); } else { // After all that checking, no modifiers are down, so let's pump up a // system defined eject event. This way we can have anyone who's watching // for this event (aka LoginWindow) route this event to the right target // (aka AutoDiskMounter). //IOLog( "IOHIDSystem--Normal Eject action!\n" ); // Post the eject keydown event. // outData.compound.subType = NX_SUBTYPE_EJECT_KEY; postEvent( NX_SYSDEFINED, /* at */ (Point *)&pointerLoc, /* atTime */ ts, /* withData */ &outData); } break; case NX_POWER_KEY: outData.compound.subType = NX_SUBTYPE_POWER_KEY; postEvent( NX_SYSDEFINED, /* at */ (Point *)&pointerLoc, /* atTime */ ts, /* withData */ &outData); break; } } #if 0 /* So far, nothing to do on keyup */ else if ( eventType == NX_KEYUP ) { switch ( flavor ) { case NX_KEYTYPE_SOUND_UP: break; case NX_KEYTYPE_SOUND_DOWN: break; case NX_KEYTYPE_MUTE: break; case NX_POWER_KEY: break; } } #endif // if someone pases a sysdefined type in, then its ready to go already else if ( eventType == NX_SYSDEFINED ) { outData.compound.subType = flavor; postEvent( NX_SYSDEFINED, /* at */ (Point *)&pointerLoc, /* atTime */ ts, /* withData */ &outData); } // post keydowns and key ups if this flavor should be posted // note, we dont want to do this for events of type NX_SYSDEFINED // we probably _should_ just check for keydowns and keyups here // but the smallest change was to just make sure we didnt do this for // the new type, which is NX_SYSDEFINED (used for sticky keys feature) if ( eventType != NX_SYSDEFINED ) { if( (flags & SPECIALKEYS_MODIFIER_MASK) && (flavor == NX_POWER_KEY)) { //Should modifier + POWER ever be dispatched? Command-power // is used to get into debugger and MacsBug } else { outData.compound.subType = NX_SUBTYPE_AUX_CONTROL_BUTTONS; outData.compound.misc.S[0] = flavor; outData.compound.misc.C[2] = eventType; outData.compound.misc.C[3] = repeat; outData.compound.misc.L[1] = guid & 0xffffffff; outData.compound.misc.L[2] = guid >> 32; postEvent( NX_SYSDEFINED, /* at */ (Point *)&pointerLoc, /* atTime */ ts, /* withData */ &outData); } } if ( level != -1 ) // An interesting special key event occurred { evSpecialKeyMsg( flavor, /* direction */ eventType, /* flags */ flags, /* level */ level); } } /* * Update current event flags. Restricted to keyboard flags only, this * method is used to silently update the flags state for keys which both * generate characters and flag changes. The specs say we don't generate * a flags-changed event for such keys. This method is also used to clear * the keyboard flags on a keyboard subsystem reset. */ void IOHIDSystem::_updateEventFlags(IOHIDSystem * self, unsigned flags, OSObject * sender, void * refcon) { self->updateEventFlags(flags, sender); } void IOHIDSystem::updateEventFlags(unsigned flags) { updateEventFlags(flags, 0); } void IOHIDSystem::updateEventFlags(unsigned flags, OSObject * sender) { cmdGate->runAction((IOCommandGate::Action)doUpdateEventFlags, (void *)flags, sender); } IOReturn IOHIDSystem::doUpdateEventFlags(IOHIDSystem *self, void * arg0, void * arg1) /* IOCommandGate::Action */ { unsigned flags = (unsigned)arg0; OSObject * sender = (OSObject *) arg1; self->updateEventFlagsGated(flags, sender); return kIOReturnSuccess; } void IOHIDSystem::updateEventFlagsGated(unsigned flags, OSObject * sender) { if ( eventsOpen ) { evg->eventFlags = (evg->eventFlags & ~KEYBOARD_FLAGSMASK) | (flags & KEYBOARD_FLAGSMASK); // RY: Reset the clickTime as well on modifier // change to prevent double click from occuring nanoseconds_to_absolutetime(0, &clickTime); } } // // - _setButtonState:(int)buttons atTime:(int)t // Update the button state. Generate button events as needed // void IOHIDSystem::_setButtonState(int buttons, /* atTime */ AbsoluteTime ts, OSObject * sender) { // еее HACK ALERT еее // Chache the sent button state and or it with the other button states. // This will prevent awkward behavior when two pointing devices are used // at the same time. CachedMouseEventStruct *cachedMouseEvent = GetCachedMouseEventForService(cachedButtonStates, sender); OSCollectionIterator * buttonIterator; OSSymbol * buttonKey; if (cachedMouseEvent) { cachedMouseEvent->lastButtons = buttons; } if(evg->buttons == buttons) return; if (buttonIterator = OSCollectionIterator::withCollection(cachedButtonStates)) { while (buttonKey = (OSSymbol*)buttonIterator->getNextObject()) { if (!(cachedMouseEvent = GetCachedMouseEventForKey(cachedButtonStates, buttonKey))) continue; buttons |= cachedMouseEvent->lastButtons; } buttonIterator->release(); } // еее END HACK еее // Once again check if new button state differs if(evg->buttons == buttons) return; // Magic uber-mouse buttons changed event so we can get all of the buttons... NXEventData evData; unsigned long hwButtons, hwDelta, temp; /* I'd like to keep the event button mapping linear, so I have to "undo" the LB/RB mouse bit numbering funkiness before I pass the information down to the app. */ /* Ideally this would all go away if we fixed EV_LB and EV_RB to be bits 0 and 1 */ hwButtons = buttons & ~7; /* Keep everything but bottom 3 bits. */ hwButtons |= (buttons & 3) << 1; /* Map bits 01 to 12 */ hwButtons |= (buttons & 4) >> 2; /* Map bit 2 back to bit 0 */ temp = evg->buttons ^ buttons; hwDelta = temp & ~7; hwDelta |= (temp & 3) << 1; /* Map bits 01 to 12 */ hwDelta |= (temp & 4) >> 2; /* Map bit 2 back to bit 0 */ evData.compound.reserved = 0; evData.compound.subType = NX_SUBTYPE_AUX_MOUSE_BUTTONS; evData.compound.misc.L[0] = hwDelta; evData.compound.misc.L[1] = hwButtons; postEvent( NX_SYSDEFINED, /* at */ (Point *)&evg->cursorLoc, /* atTime */ ts, /* withData */ &evData); // End Magic uber-mouse buttons changed event bzero(&evData, sizeof(NXEventData)); // RY: Roll in the tablet info we got from absolutePointerEventGated // into a button event. Prior to this we were sending null data to // post event. This won't be much different as the only non-zero // contents should be the tablet area of the event. if (cachedMouseEvent = GetCachedMouseEventForService(cachedButtonStates, sender)) { if (cachedMouseEvent->tabletPointDataPtr != 0) { bcopy(cachedMouseEvent->tabletPointDataPtr, &(evData.mouse.tablet.point), sizeof(NXTabletPointData)); evData.mouse.subType = NX_SUBTYPE_TABLET_POINT; } else if (cachedMouseEvent->tabletProximityDataPtr != 0) { bcopy(cachedMouseEvent->tabletProximityDataPtr, &(evData.mouse.tablet.proximity), sizeof(NXTabletProximityData)); evData.mouse.subType = NX_SUBTYPE_TABLET_PROXIMITY; } } if ((evg->buttons & EV_LB) != (buttons & EV_LB)) { if (buttons & EV_LB) { postEvent( NX_LMOUSEDOWN, /* at */ (Point *)&evg->cursorLoc, /* atTime */ ts, /* withData */ &evData); } else { postEvent( NX_LMOUSEUP, /* at */ (Point *)&evg->cursorLoc, /* atTime */ ts, /* withData */ &evData); } // After entering initial up/down event, set up // coalescing state so drags will behave correctly evg->dontCoalesce = evg->dontWantCoalesce; if (evg->dontCoalesce) evg->eventFlags |= NX_NONCOALSESCEDMASK; else evg->eventFlags &= ~NX_NONCOALSESCEDMASK; } if ((evg->buttons & EV_RB) != (buttons & EV_RB)) { if (buttons & EV_RB) { postEvent( NX_RMOUSEDOWN, /* at */ (Point *)&evg->cursorLoc, /* atTime */ ts, /* withData */ &evData); } else { postEvent( NX_RMOUSEUP, /* at */ (Point *)&evg->cursorLoc, /* atTime */ ts, /* withData */ &evData); } } evg->buttons = buttons; } // // Sets the cursor position (evg->cursorLoc) to the new // location. The location is clipped against the cursor pin rectangle, // mouse moved/dragged events are generated using the given event mask, // and a mouse-exited event may be generated. The cursor image is // moved. // This should be run from a command gate action. // void IOHIDSystem::setCursorPosition(Point * newLoc, bool external, OSObject * sender) { if ( eventsOpen == true ) { pointerDelta.x += (newLoc->x - pointerLoc.x); pointerDelta.y += (newLoc->y - pointerLoc.y); pointerLoc = *newLoc; _setCursorPosition(newLoc, external, sender); } } // // This mechanism is used to update the cursor position, possibly generating // messages to registered frame buffer devices and posting drag, tracking, and // mouse motion events. // // This should be run from a command gate action. // This can be called from setCursorPosition:(Point *)newLoc to set the // position by a _IOSetParameterFromIntArray() call, directly from the absolute or // relative pointer device routines, or on a timed event callback. // void IOHIDSystem::_setCursorPosition(Point * newLoc, bool external, OSObject * sender) { bool cursorMoved = true; if (!screens) return; OSSymbol * senderKey = GetKeyForService(sender); if( ev_try_lock(&evg->cursorSema) == 0 ) // host using shmem { needSetCursorPosition = true; // try again later // scheduleNextPeriodicEvent(); if (senderKey) senderKey->release(); return; } // Past here we hold the cursorSema lock. Make sure the lock is // cleared before returning or the system will be wedged. needSetCursorPosition = false; // We WILL succeed if (cursorCoupled || external) { UInt32 newScreens = 0; SInt32 pinScreen = -1L; EvScreen *screen = (EvScreen *)evScreen; if (!cursorPinned) { /* Get mask of screens on which the cursor is present */ for (int i = 0; i < screens; i++ ) { if ((screen[i].instance) && PtInRect(newLoc, screen[i].bounds)) { pinScreen = i; newScreens |= (1 << i); } } } if (newScreens == 0) { /* At this point cursor has gone off all screens, just clip it to one of the previous screens. */ newLoc->x = (newLoc->x < cursorPin.minx) ? cursorPin.minx : ((newLoc->x > cursorPin.maxx) ? cursorPin.maxx : newLoc->x); newLoc->y = (newLoc->y < cursorPin.miny) ? cursorPin.miny : ((newLoc->y > cursorPin.maxy) ? cursorPin.maxy : newLoc->y); /* regenerate mask for new position */ for (int i = 0; i < screens; i++ ) { if ((screen[i].instance) && PtInRect(newLoc, screen[i].bounds)) { pinScreen = i; newScreens |= (1 << i); } } } pointerLoc = *newLoc; // Sync up pointer with clipped cursor /* Catch the no-move case */ if ((evg->cursorLoc.x == newLoc->x) && (evg->cursorLoc.y == newLoc->y)) { if ((pointerDelta.x == 0) && (pointerDelta.y == 0)) { ev_unlock(&evg->cursorSema); if (senderKey) senderKey->release(); return; } cursorMoved = false; // mouse moved, but cursor didn't } else { evg->cursorLoc.x = newLoc->x; evg->cursorLoc.y = newLoc->y; /* If cursor changed screens */ if (newScreens != cursorScreens) { hideCursor(); /* hide cursor on old screens */ cursorScreens = newScreens; cursorPin = *(((EvScreen*)evScreen)[pinScreen].bounds); cursorPin.maxx--; /* Make half-open rectangle */ cursorPin.maxy--; cursorPinScreen = pinScreen; showCursor(); } else { /* cursor moved on same screens */ moveCursor(); } } } else { /* cursor uncoupled */ pointerLoc.x = evg->cursorLoc.x; pointerLoc.y = evg->cursorLoc.y; } AbsoluteTime ts; clock_get_uptime(&ts); /* See if anybody wants the mouse moved or dragged events */ // Note: extPostEvent clears evg->movedMask as a hack to prevent these events // so any change here should check to make sure it does not break that hack if (evg->movedMask) { if ((evg->movedMask&NX_LMOUSEDRAGGEDMASK)&&(evg->buttons& EV_LB)) { _postMouseMoveEvent(NX_LMOUSEDRAGGED, newLoc, ts, senderKey); } else if ((evg->movedMask&NX_RMOUSEDRAGGEDMASK) && (evg->buttons & EV_RB)) { _postMouseMoveEvent(NX_RMOUSEDRAGGED, newLoc, ts, senderKey); } else if (evg->movedMask & NX_MOUSEMOVEDMASK) { _postMouseMoveEvent(NX_MOUSEMOVED, newLoc, ts, senderKey); } } /* check new cursor position for leaving evg->mouseRect */ if (cursorMoved && evg->mouseRectValid && (!PtInRect(newLoc, &evg->mouseRect))) { if (evg->mouseRectValid) { postEvent( NX_MOUSEEXITED, /* at */ newLoc, /* atTime */ ts, /* withData */ NULL); evg->mouseRectValid = 0; } } ev_unlock(&evg->cursorSema); if (senderKey) senderKey->release(); } void IOHIDSystem::_postMouseMoveEvent(int what, Point * location, AbsoluteTime ts, OSSymbol * senderKey) { NXEventData data; CachedMouseEventStruct *cachedMouseEvent = 0; bzero( &data, sizeof(data) ); data.mouseMove.dx = pointerDelta.x; data.mouseMove.dy = pointerDelta.y; pointerDelta.x = 0; pointerDelta.y = 0; // RY: Roll in the tablet info we got from absolutePointerEventGated // into the mouseMove event. if (senderKey && (cachedMouseEvent = GetCachedMouseEventForKey(cachedButtonStates, senderKey))) { if (cachedMouseEvent->tabletProximityDataPtr != 0) { bcopy(cachedMouseEvent->tabletProximityDataPtr, &(data.mouseMove.tablet.proximity), sizeof(NXTabletProximityData)); data.mouseMove.subType = NX_SUBTYPE_TABLET_PROXIMITY; } else if (cachedMouseEvent->tabletPointDataPtr != 0) { bcopy(cachedMouseEvent->tabletPointDataPtr, &(data.mouseMove.tablet.point), sizeof(NXTabletPointData)); data.mouseMove.subType = NX_SUBTYPE_TABLET_POINT; } } postEvent(what, location, ts, &data); } /** ** IOUserClient methods **/ IOReturn IOHIDSystem::newUserClient(task_t owningTask, /* withToken */ void * security_id, /* ofType */ UInt32 type, /* client */ IOUserClient ** handler) { return cmdGate->runAction((IOCommandGate::Action)doNewUserClient, &owningTask, security_id, &type, handler); } IOReturn IOHIDSystem::doNewUserClient(IOHIDSystem *self, void * arg0, void * arg1, void * arg2, void * arg3) /* IOCommandGate::Action */ { task_t owningTask = *(task_t *) arg0; void * security_id = arg1; UInt32 type = *(UInt32 *) arg2; IOUserClient ** handler = (IOUserClient **) arg3; return self->newUserClientGated(owningTask, security_id, type, handler); } IOReturn IOHIDSystem::newUserClientGated(task_t owningTask, /* withToken */ void * security_id, /* ofType */ UInt32 type, /* client */ IOUserClient ** handler) { IOUserClient * newConnect = 0; IOReturn err = kIOReturnNoMemory; do { if( type == kIOHIDParamConnectType) { if( paramConnect) { newConnect = paramConnect; newConnect->retain(); } else if( eventsOpen) { newConnect = new IOHIDParamUserClient; } else { err = kIOReturnNotOpen; continue; } } else if( type == kIOHIDServerConnectType) { newConnect = new IOHIDUserClient; } else err = kIOReturnUnsupported; if( !newConnect) continue; // initialization is getting out of hand if( (newConnect != paramConnect) && ( (false == newConnect->init()) || (false == newConnect->attach( this )) || (false == newConnect->start( this )) || ((type == kIOHIDServerConnectType) && (err = evOpen())) )) { newConnect->detach( this ); newConnect->release(); newConnect = 0; continue; } if( type == kIOHIDParamConnectType) paramConnect = newConnect; err = kIOReturnSuccess; } while( false ); *handler = newConnect; return err; } IOReturn IOHIDSystem::setEventsEnable(void*p1,void*,void*,void*,void*,void*) { // IOMethod bool enable = (bool)p1; if( enable) { attachDefaultEventSources(); _resetMouseParameters(); _resetKeyboardParameters(); } return( kIOReturnSuccess); } IOReturn IOHIDSystem::setCursorEnable(void*p1,void*,void*,void*,void*,void*) { // IOMethod return cmdGate->runAction((IOCommandGate::Action)doSetCursorEnable, p1); } IOReturn IOHIDSystem::doSetCursorEnable(IOHIDSystem *self, void * arg0) /* IOCommandGate::Action */ { return self->setCursorEnableGated(arg0); } IOReturn IOHIDSystem::setCursorEnableGated(void* p1) { bool enable = (bool)p1; IOReturn err = kIOReturnSuccess; if ( eventsOpen == false ) return kIOReturnNotOpen; if( 0 == screens) // Should be at least 1! return kIOReturnNoDevice; if( enable) { if( cursorStarted) { hideCursor(); cursorEnabled = resetCursor(); showCursor(); } else cursorEnabled = startCursor(); } else cursorEnabled = enable; cursorCoupled = cursorEnabled; return err; } IOReturn IOHIDSystem::extSetBounds( IOGBounds * bounds ) { if( bounds->minx != bounds->maxx) { cursorPin = *bounds; cursorPin.maxx--; /* Make half-open rectangle */ cursorPin.maxy--; cursorPinned = true; } else cursorPinned = false; return( kIOReturnSuccess ); } IOReturn IOHIDSystem::extPostEvent(void*p1,void*,void*,void*,void*,void*) { // IOMethod return cmdGate->runAction((IOCommandGate::Action)doExtPostEvent, p1); } IOReturn IOHIDSystem::doExtPostEvent(IOHIDSystem *self, void * arg0) /* IOCommandGate::Action */ { return self->extPostEventGated(arg0); } IOReturn IOHIDSystem::extPostEventGated(void *p1) { struct evioLLEvent * event = (struct evioLLEvent *)p1; bool isMoveOrDragEvent = false; int oldMovedMask; TICKLE_DISPLAY; // used in set cursor below if (EventCodeMask(event->type) & MOVEDEVENTMASK) isMoveOrDragEvent = true; if( event->setCursor) { // hack: clear evg->movedMask so setCursorPosition will not post // mouse moved events if this event is itself a mouse move event // this will prevent double events if (isMoveOrDragEvent) { oldMovedMask = evg->movedMask; evg->movedMask = 0; } if (( event->type == NX_MOUSEMOVED ) && ( event->setCursor & kIOHIDSetRelativeCursorPosition )) { pointerLoc.x += event->data.mouseMove.dx; pointerLoc.y += event->data.mouseMove.dy; pointerDelta.x += event->data.mouseMove.dx; pointerDelta.y += event->data.mouseMove.dy; _setCursorPosition(&pointerLoc, false); } else if ( event->setCursor & kIOHIDSetCursorPosition ) { setCursorPosition(&event->location, false); } // other side of hack if (isMoveOrDragEvent) evg->movedMask = oldMovedMask; } // RY: This event is not moving the cursor, but we should // still determine if we need to click the location else { UInt32 newScreens = 0; EvScreen *screen = (EvScreen *)evScreen; if (!cursorPinned) { /* Get mask of screens on which the cursor is present */ for (int i = 0; i < screens; i++ ) { if ((screen[i].instance) && PtInRect(&event->location, screen[i].bounds)) { newScreens |= (1 << i); } } } if (newScreens == 0) { /* At this point cursor has gone off all screens, just clip it to one of the previous screens. */ event->location.x = (event->location.x < cursorPin.minx) ? cursorPin.minx : ((event->location.x > cursorPin.maxx) ? cursorPin.maxx : event->location.x); event->location.y = (event->location.y < cursorPin.miny) ? cursorPin.miny : ((event->location.y > cursorPin.maxy) ? cursorPin.maxy : event->location.y); } } if( event->setFlags) evg->eventFlags = (evg->eventFlags & ~KEYBOARD_FLAGSMASK) | (event->flags & KEYBOARD_FLAGSMASK); AbsoluteTime ts; clock_get_uptime(&ts); postEvent( event->type, /* at */ &event->location, /* atTime */ ts, /* withData */ &event->data); return kIOReturnSuccess; } IOReturn IOHIDSystem::extSetMouseLocation(void*p1,void*,void*,void*,void*,void*) { // IOMethod return cmdGate->runAction((IOCommandGate::Action)doExtSetMouseLocation, p1); } IOReturn IOHIDSystem::doExtSetMouseLocation(IOHIDSystem *self, void * arg0) /* IOCommandGate::Action */ { return self->extSetMouseLocationGated(arg0); } IOReturn IOHIDSystem::extSetMouseLocationGated(void *p1) { Point * loc = (Point *)p1; setCursorPosition(loc, true); return kIOReturnSuccess; } IOReturn IOHIDSystem::extGetButtonEventNum(void*p1,void*p2,void*,void*,void*,void*) { // IOMethod return cmdGate->runAction((IOCommandGate::Action)doExtGetButtonEventNum, p1, p2); } IOReturn IOHIDSystem::doExtGetButtonEventNum(IOHIDSystem *self, void * arg0, void * arg1) /* IOCommandGate::Action */ { return self->extGetButtonEventNumGated(arg0, arg1); } IOReturn IOHIDSystem::extGetButtonEventNumGated(void *p1, void* p2) { NXMouseButton button = (NXMouseButton)(int)p1; int * eventNum = (int *)p2; IOReturn err = kIOReturnSuccess; switch( button) { case NX_LeftButton: *eventNum = leftENum; break; case NX_RightButton: *eventNum = rightENum; break; default: err = kIOReturnBadArgument; } return err; } void IOHIDSystem::makeNumberParamProperty( OSDictionary * dict, const char * key, unsigned long long number, unsigned int bits ) { OSNumber * numberRef; numberRef = OSNumber::withNumber(number, bits); if( numberRef) { dict->setObject( key, numberRef); numberRef->release(); } } void IOHIDSystem::makeInt32ArrayParamProperty( OSDictionary * dict, const char * key, UInt32 * intArray, unsigned int count ) { OSArray * array; OSNumber * number; array = OSArray::withCapacity(count); if ( !array ) return; for (int i=0; array && isetObject(number); number->release(); } } dict->setObject( key, array); array->release(); } void IOHIDSystem::createParameters( void ) { UInt64 nano; IOFixed fixed; UInt32 int32; nano = EV_DCLICKTIME; makeNumberParamProperty( savedParameters, kIOHIDClickTimeKey, nano, 64 ); nano = DAUTODIMPERIOD; makeNumberParamProperty( savedParameters, kIOHIDAutoDimThresholdKey, nano, 64 ); makeNumberParamProperty( savedParameters, kIOHIDAutoDimBrightnessKey, dimmedBrightness, 32 ); UInt32 tempClickSpace[] = {clickSpaceThresh.x, clickSpaceThresh.y}; makeInt32ArrayParamProperty( savedParameters, kIOHIDClickSpaceKey, tempClickSpace, sizeof(tempClickSpace)/sizeof(UInt32) ); nano = EV_DEFAULTKEYREPEAT; makeNumberParamProperty( savedParameters, kIOHIDKeyRepeatKey, nano, 64 ); nano = EV_DEFAULTINITIALREPEAT; makeNumberParamProperty( savedParameters, kIOHIDInitialKeyRepeatKey, nano, 64 ); fixed = 0x10000; makeNumberParamProperty( savedParameters, kIOHIDMouseAccelerationType, fixed, sizeof(fixed) << 3); fixed = 0x8000; makeNumberParamProperty( savedParameters, kIOHIDTrackpadAccelerationType, fixed, sizeof(fixed) << 3); fixed = 0x8000; makeNumberParamProperty( savedParameters, kIOHIDPointerAccelerationKey, fixed, sizeof(fixed) << 3); fixed = 0x5000; makeNumberParamProperty( savedParameters, kIOHIDScrollAccelerationKey, fixed, sizeof(fixed) << 3); fixed = kIOHIDButtonMode_EnableRightClick; makeNumberParamProperty( savedParameters, kIOHIDPointerButtonMode, fixed, sizeof(fixed) << 3); // set eject delay property initially to 250 ms int32 = 250; makeNumberParamProperty( savedParameters, kIOHIDF12EjectDelayKey, int32, 32 ); // set slow keys delay property int32 = 0; makeNumberParamProperty( savedParameters, kIOHIDSlowKeysDelayKey, int32, 32 ); // set disabled property int32 = 0; // not disabled makeNumberParamProperty( savedParameters, kIOHIDStickyKeysDisabledKey, int32, 32 ); // set on/off property int32 = 0; // off makeNumberParamProperty( savedParameters, kIOHIDStickyKeysOnKey, int32, 32 ); // set shift toggles property int32 = 0; // off, shift does not toggle makeNumberParamProperty( savedParameters, kIOHIDStickyKeysShiftTogglesKey, int32, 32 ); // set option toggles property int32 = 0; // off, option does not toggle makeNumberParamProperty( savedParameters, kIOHIDMouseKeysOptionTogglesKey, int32, 32 ); int32 = 0; makeNumberParamProperty( savedParameters, kIOHIDFKeyModeKey, int32, 32 ); setProperty( kIOHIDParametersKey, savedParameters ); savedParameters->release(); } bool IOHIDSystem::updateProperties( void ) { return cmdGate->runAction((IOCommandGate::Action)doUpdateProperties); } bool IOHIDSystem::doUpdateProperties(IOHIDSystem *self) /* IOCommandGate::Action */ { return self->updatePropertiesGated(); } bool IOHIDSystem::updatePropertiesGated( void ) { UInt64 autoDimTimeNano; UInt64 idleTimeNano; AbsoluteTime time1, time2; if( eventsOpen) { clock_get_uptime( &time1); if( autoDimmed) { autoDimTimeNano = 0; // now - (autoDimTime - autoDimPeriod) SUB_ABSOLUTETIME( &time1, &autoDimTime); ADD_ABSOLUTETIME( &time1, &autoDimPeriod); absolutetime_to_nanoseconds( time1, &idleTimeNano); } else { // autoDimTime - now time2 = autoDimTime; SUB_ABSOLUTETIME( &time2, &time1); absolutetime_to_nanoseconds( time2, &autoDimTimeNano); // autoDimPeriod - (autoDimTime - evg->VertRetraceClock) time1 = autoDimPeriod; SUB_ABSOLUTETIME( &time1, &time2); absolutetime_to_nanoseconds( time1, &idleTimeNano); } } else { absolutetime_to_nanoseconds( autoDimPeriod, &autoDimTimeNano); idleTimeNano = 0; // user is active } setProperty( kIOHIDAutoDimTimeKey, autoDimTimeNano, 64); setProperty( kIOHIDIdleTimeKey, idleTimeNano, 64); setProperty( kIOHIDAutoDimStateKey, autoDimmed, 32); setProperty( kIOHIDBrightnessKey, curBright, 32); return true; } bool IOHIDSystem::serializeProperties( OSSerialize * s ) const { ((IOHIDSystem *) this)->updateProperties(); return( super::serializeProperties( s )); } IOReturn IOHIDSystem::setProperties( OSObject * properties ) { OSDictionary * dict; IOReturn err = kIOReturnSuccess; IOReturn ret; dict = OSDynamicCast( OSDictionary, properties ); if( dict) ret = setParamProperties( dict ); else err = kIOReturnBadArgument; return( err ); } IOReturn IOHIDSystem::setParamProperties( OSDictionary * dict ) { return cmdGate->runAction((IOCommandGate::Action)doSetParamProperties, dict); } IOReturn IOHIDSystem::doSetParamProperties(IOHIDSystem *self, void * arg0) /* IOCommandGate::Action */ { OSDictionary * dict = (OSDictionary *)arg0; return self->setParamPropertiesGated(dict); } IOReturn IOHIDSystem::setParamPropertiesGated( OSDictionary * dict ) { OSIterator * iter; IOHIDevice * eventSrc; OSArray * array; OSNumber * number; IOReturn ret; IOReturn err = kIOReturnSuccess; // check for null if (dict == NULL) return kIOReturnError; if( (number = OSDynamicCast( OSNumber, dict->getObject(kIOHIDClickTimeKey)))) { UInt64 nano = number->unsigned64BitValue(); nanoseconds_to_absolutetime(nano, &clickTimeThresh); } if( (array = OSDynamicCast( OSArray, dict->getObject(kIOHIDClickSpaceKey)))) { if ((number = OSDynamicCast( OSNumber, array->getObject(EVSIOSCS_X)))) { clickSpaceThresh.x = number->unsigned32BitValue(); } if ((number = OSDynamicCast( OSNumber, array->getObject(EVSIOSCS_Y)))) { clickSpaceThresh.y = number->unsigned32BitValue(); } } if( (number = OSDynamicCast( OSNumber, dict->getObject(kIOHIDAutoDimThresholdKey)))) { AbsoluteTime oldPeriod = autoDimPeriod; UInt64 nano = number->unsigned64BitValue(); nanoseconds_to_absolutetime(nano, &autoDimPeriod); // autoDimTime = autoDimTime - oldPeriod + autoDimPeriod; SUB_ABSOLUTETIME( &autoDimTime, &oldPeriod); ADD_ABSOLUTETIME( &autoDimTime, &autoDimPeriod); } if( (number = OSDynamicCast( OSNumber, dict->getObject(kIOHIDAutoDimStateKey)))) forceAutoDimState( 0 != number->unsigned32BitValue()); if( (number = OSDynamicCast( OSNumber, dict->getObject(kIOHIDBrightnessKey)))) setBrightness( number->unsigned32BitValue()); if( (number = OSDynamicCast( OSNumber, dict->getObject(kIOHIDAutoDimBrightnessKey)))) setAutoDimBrightness( number->unsigned32BitValue()); if( (number = OSDynamicCast( OSNumber, dict->getObject(kIOHIDWaitCursorFrameIntervalKey)))) clock_interval_to_absolutetime_interval(number->unsigned32BitValue(), kNanosecondScale, &waitFrameRate); if( (number = OSDynamicCast( OSNumber, dict->getObject(kIOHIDStickyKeysOnKey)))) { if (number->unsigned32BitValue()) stickyKeysState |= (1 << 0); else stickyKeysState &= ~(1 << 0); } if( (number = OSDynamicCast( OSNumber, dict->getObject(kIOHIDStickyKeysShiftTogglesKey)))) { if (number->unsigned32BitValue()) stickyKeysState |= (1 << 1); else stickyKeysState &= ~(1 << 1); } // update connected input devices iter = getOpenProviderIterator(); if( iter) { while( (eventSrc = (IOHIDevice *) iter->getNextObject())) { ret = eventSrc->setParamProperties( dict ); if( (ret != kIOReturnSuccess) && (ret != kIOReturnBadArgument)) err = ret; } iter->release(); } // save all params for new devices dict->removeObject(kIOHIDResetKeyboardKey); dict->removeObject(kIOHIDResetPointerKey); OSDictionary * newParams = OSDictionary::withDictionary( savedParameters ); if( newParams) { newParams->merge( dict ); setProperty( kIOHIDParametersKey, newParams ); newParams->release(); savedParameters = newParams; } // Send anevent notification that the properties changed. struct evioLLEvent event; AbsoluteTime ts; // clear event record bzero( (void *)&event, sizeof event); event.data.compound.subType = NX_SUBTYPE_HIDPARAMETER_MODIFIED; clock_get_uptime(&ts); postEvent(NX_SYSDEFINED, &(event.location), ts, &(event.data)); return err; }